-
-
Notifications
You must be signed in to change notification settings - Fork 1.1k
fix(core): define RunEvent schema and update ApiClient validation #3220
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Changes from all commits
d763cb8
0fba878
6bc752c
ca30dbd
0d361c8
fd2f823
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,5 @@ | ||
| --- | ||
| "@trigger.dev/core": patch | ||
| --- | ||
|
|
||
| Define RunEvent schema and update ApiClient to use it |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -9,6 +9,8 @@ import { | |
| } from "./common.js"; | ||
| import { BackgroundWorkerMetadata } from "./resources.js"; | ||
| import { DequeuedMessage, MachineResources } from "./runEngine.js"; | ||
| import { TaskEventStyle } from "./style.js"; | ||
| import { SpanEvents } from "./openTelemetry.js"; | ||
|
|
||
| export const RunEngineVersion = z.union([z.literal("V1"), z.literal("V2")]); | ||
|
|
||
|
|
@@ -1639,3 +1641,52 @@ export const SendInputStreamResponseBody = z.object({ | |
| ok: z.boolean(), | ||
| }); | ||
| export type SendInputStreamResponseBody = z.infer<typeof SendInputStreamResponseBody>; | ||
| export const TaskEventLevel = z.enum(["TRACE", "DEBUG", "INFO", "LOG", "WARN", "ERROR"]); | ||
| export type TaskEventLevel = z.infer<typeof TaskEventLevel>; | ||
| export const NanosecondTimestampSchema = z | ||
| .union([z.string(), z.number(), z.bigint()]) | ||
| .transform((val) => { | ||
| // If it's already a Date, return it (though union doesn't include it) | ||
| if (typeof val === "object" && val instanceof Date) return val; | ||
|
|
||
| const str = val.toString(); | ||
|
|
||
| // 19 digits is nanoseconds (e.g., 1710374400000000000) | ||
| if (str.length === 19) { | ||
| return new Date(Number(BigInt(str) / 1000000n)); | ||
| } | ||
|
|
||
| // 13 digits is milliseconds | ||
| if (str.length === 13) { | ||
| return new Date(Number(val)); | ||
| } | ||
|
|
||
| // Default to standard date coercion | ||
| return new Date(val as any); | ||
| }); | ||
|
|
||
| export const RunEvent = z.object({ | ||
| spanId: z.string(), | ||
| parentId: z.string().nullish(), | ||
| runId: z.string(), | ||
| message: z.string(), | ||
| style: TaskEventStyle, | ||
| startTime: NanosecondTimestampSchema, | ||
| duration: z.number(), | ||
| isError: z.boolean(), | ||
| isPartial: z.boolean(), | ||
| isCancelled: z.boolean(), | ||
| level: TaskEventLevel, | ||
| events: SpanEvents.optional(), | ||
| kind: z.string(), | ||
|
Contributor
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 📝 Info: ClickHouse getRunEvents hardcodes kind to 'UNSPECIFIED' and omits taskSlug The ClickHouse Was this helpful? React with 👍 or 👎 to provide feedback. |
||
| attemptNumber: z.number().nullish(), | ||
| taskSlug: z.string().optional(), | ||
| }); | ||
devin-ai-integration[bot] marked this conversation as resolved.
Show resolved
Hide resolved
|
||
|
|
||
| export type RunEvent = z.infer<typeof RunEvent>; | ||
|
|
||
| export const ListRunEventsResponse = z.object({ | ||
| events: z.array(RunEvent), | ||
| }); | ||
|
|
||
| export type ListRunEventsResponse = z.infer<typeof ListRunEventsResponse>; | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🧩 Analysis chain
🌐 Web query:
In Zod v3, does.transform()automatically validate that a returnedDateis valid, and will a transform onz.union([z.string(), z.number(), z.bigint()])run when the input is aDateobject?💡 Result:
No. In Zod v3,
.transform()runs after the input schema has parsed; Zod does not automatically re-validate the returned value (e.g., it won’t automatically reject anInvalid Date). If you need that, you must either add a check inside the transform viactx(add an issue) or “pipe” into an output schema (e.g. az.date()schema) to validate the post-transform value. [1], [2]No. A
.transform()attached toz.union([z.string(), z.number(), z.bigint()])will only run if the union successfully parses the input. If the input is aDateobject, none of those union branches match, so parsing fails and the transform is not executed. [3], [2]Sources:
[1] Zod docs – Transforms API (
z.transform, usingctx.issuesfor validation) https://zod.dev/api?id=preprocess[2] Zod v3 README (npm) – ZodEffects runs transforms post-parsing https://www.npmjs.com/package/zod/v/3.0.0-alpha.19
[3] Zod docs – unions return the first option that validates successfully (i.e., must validate to proceed) https://zod.dev/?id=dates-1
Nanosecond timestamp transform does not validate returned
Dateand misses literalDateinputs.In Zod v3,
.transform()does not automatically validate its returned value, so invalid timestamps can produceInvalid Dateobjects that pass through. Additionally, the unreachableDatebranch check is correct—since the union excludesz.date(), a literalDateinput fails parsing before the transform runs.🔧 Proposed fix
📝 Committable suggestion
🤖 Prompt for AI Agents