From 92cb7a3a760ef1a663eb2acd330603b8b0544577 Mon Sep 17 00:00:00 2001 From: Eric Allam Date: Mon, 8 Sep 2025 13:59:45 +0100 Subject: [PATCH] fix(core): Improves our schema to JSON Schema conversion (fix for zod 4) --- .changeset/angry-files-yawn.md | 5 + .../src/entryPoints/dev-index-worker.ts | 4 +- .../src/entryPoints/managed-index-worker.ts | 4 +- packages/schema-to-json/package.json | 10 +- packages/schema-to-json/src/index.ts | 251 +++++++----------- packages/schema-to-json/tests/index.test.ts | 151 +++-------- pnpm-lock.yaml | 84 +++--- .../hello-world/src/trigger/jsonSchema.ts | 35 ++- 8 files changed, 215 insertions(+), 329 deletions(-) create mode 100644 .changeset/angry-files-yawn.md diff --git a/.changeset/angry-files-yawn.md b/.changeset/angry-files-yawn.md new file mode 100644 index 0000000000..cd8c76fbde --- /dev/null +++ b/.changeset/angry-files-yawn.md @@ -0,0 +1,5 @@ +--- +"trigger.dev": patch +--- + +Improves our schema to JSON Schema conversion, fixes zod 4 and a few other schema libraries, also correctly sets the dependencies diff --git a/packages/cli-v3/src/entryPoints/dev-index-worker.ts b/packages/cli-v3/src/entryPoints/dev-index-worker.ts index 7b40ecf9d4..da5c6ee750 100644 --- a/packages/cli-v3/src/entryPoints/dev-index-worker.ts +++ b/packages/cli-v3/src/entryPoints/dev-index-worker.ts @@ -18,7 +18,7 @@ import { registerResources } from "../indexing/registerResources.js"; import { env } from "std-env"; import { normalizeImportPath } from "../utilities/normalizeImportPath.js"; import { detectRuntimeVersion } from "@trigger.dev/core/v3/build"; -import { schemaToJsonSchema, initializeSchemaConverters } from "@trigger.dev/schema-to-json"; +import { schemaToJsonSchema } from "@trigger.dev/schema-to-json"; sourceMapSupport.install({ handleUncaughtExceptions: false, @@ -193,8 +193,6 @@ await new Promise((resolve) => { }); async function convertSchemasToJsonSchemas(tasks: TaskManifest[]): Promise { - await initializeSchemaConverters(); - const convertedTasks = tasks.map((task) => { const schema = resourceCatalog.getTaskSchema(task.id); diff --git a/packages/cli-v3/src/entryPoints/managed-index-worker.ts b/packages/cli-v3/src/entryPoints/managed-index-worker.ts index 98593cfc6e..5ff9f1b62e 100644 --- a/packages/cli-v3/src/entryPoints/managed-index-worker.ts +++ b/packages/cli-v3/src/entryPoints/managed-index-worker.ts @@ -18,7 +18,7 @@ import { registerResources } from "../indexing/registerResources.js"; import { env } from "std-env"; import { normalizeImportPath } from "../utilities/normalizeImportPath.js"; import { detectRuntimeVersion } from "@trigger.dev/core/v3/build"; -import { schemaToJsonSchema, initializeSchemaConverters } from "@trigger.dev/schema-to-json"; +import { schemaToJsonSchema } from "@trigger.dev/schema-to-json"; sourceMapSupport.install({ handleUncaughtExceptions: false, @@ -201,8 +201,6 @@ await new Promise((resolve) => { }); async function convertSchemasToJsonSchemas(tasks: TaskManifest[]): Promise { - await initializeSchemaConverters(); - const convertedTasks = tasks.map((task) => { const schema = resourceCatalog.getTaskSchema(task.id); diff --git a/packages/schema-to-json/package.json b/packages/schema-to-json/package.json index ecda598986..f4c7044e74 100644 --- a/packages/schema-to-json/package.json +++ b/packages/schema-to-json/package.json @@ -42,19 +42,19 @@ }, "dependencies": { "@trigger.dev/core": "workspace:*", - "zod-to-json-schema": "^3.24.5", - "@sodaru/yup-to-json-schema": "^2.0.1" + "zod-to-json-schema": "^3.24.0", + "@sodaru/yup-to-json-schema": "^2", + "zod": "3.25.76", + "effect": "^3" }, "devDependencies": { "arktype": "^2.0.0", - "effect": "^3.11.11", "runtypes": "^6.7.0", "superstruct": "^2.0.2", "tshy": "^3.0.2", "@sinclair/typebox": "^0.34.3", "valibot": "^1.1.0", "yup": "^1.7.0", - "zod": "^3.24.1 || ^4.0.0", "rimraf": "6.0.1", "@arethetypeswrong/cli": "^0.15.4" }, @@ -66,7 +66,7 @@ "@sinclair/typebox": ">=0.34.30", "valibot": ">=0.41.0", "yup": ">=1.0.0", - "zod": "^3.24.1 || ^4.0.0" + "zod": "^3.25.76 || ^4" }, "peerDependenciesMeta": { "arktype": { diff --git a/packages/schema-to-json/src/index.ts b/packages/schema-to-json/src/index.ts index 604ec8d78c..8631060db3 100644 --- a/packages/schema-to-json/src/index.ts +++ b/packages/schema-to-json/src/index.ts @@ -1,18 +1,21 @@ // Import JSONSchema from core to ensure compatibility import type { JSONSchema } from "@trigger.dev/core/v3"; +import { zodToJsonSchema } from "zod-to-json-schema"; +import * as z4 from "zod/v4"; +import { convertSchema } from "@sodaru/yup-to-json-schema"; +import { JSONSchema as EffectJSONSchema } from "effect"; export type Schema = unknown; export type { JSONSchema }; export interface ConversionOptions { /** - * The name to use for the schema in the JSON Schema + * Enables support for references in the schema. + * This is required for recursive schemas, e.g. with `z.lazy`. + * However, not all language models and providers support such references. + * Defaults to `false`. */ - name?: string; - /** - * Additional JSON Schema properties to merge - */ - additionalProperties?: Record; + useReferences?: boolean; } export interface ConversionResult { @@ -20,19 +23,6 @@ export interface ConversionResult { * The JSON Schema representation (JSON Schema Draft 7) */ jsonSchema: JSONSchema; - /** - * The detected schema type - */ - schemaType: - | "zod" - | "yup" - | "arktype" - | "effect" - | "valibot" - | "superstruct" - | "runtypes" - | "typebox" - | "unknown"; } /** @@ -57,107 +47,48 @@ export function schemaToJsonSchema( if (typeof parser.toJsonSchema === "function") { try { const jsonSchema = parser.toJsonSchema(); - // Determine if it's Zod or ArkType based on other methods - const schemaType = - typeof parser.parseAsync === "function" || typeof parser.parse === "function" - ? "zod" - : "arktype"; + return { - jsonSchema: options?.additionalProperties - ? { ...jsonSchema, ...options.additionalProperties } - : jsonSchema, - schemaType, + jsonSchema, }; } catch (error) { // If toJsonSchema fails, continue to other checks } } + if (isZodSchema(parser)) { + const jsonSchema = convertZodSchema(parser, options); + + if (jsonSchema) { + return { + jsonSchema: jsonSchema, + }; + } + } + // Check if it's a TypeBox schema (has Static and Kind symbols) if (parser[Symbol.for("TypeBox.Kind")] !== undefined) { // TypeBox schemas are already JSON Schema compliant return { - jsonSchema: options?.additionalProperties - ? { ...parser, ...options.additionalProperties } - : parser, - schemaType: "typebox", + jsonSchema: parser, }; } - // For schemas that need external libraries, we need to check if they're available - // This approach avoids bundling the dependencies while still allowing runtime usage - - // Check if it's a Zod schema (without built-in toJsonSchema) - if (typeof parser.parseAsync === "function" || typeof parser.parse === "function") { - try { - // Try to access zod-to-json-schema if it's available - // @ts-ignore - This is intentionally dynamic - if (typeof globalThis.__zodToJsonSchema !== "undefined") { - // @ts-ignore - const { zodToJsonSchema } = globalThis.__zodToJsonSchema; - const jsonSchema = options?.name - ? zodToJsonSchema(parser, options.name) - : zodToJsonSchema(parser); - - if (jsonSchema && typeof jsonSchema === "object" && "$schema" in jsonSchema) { - const { $schema, ...rest } = jsonSchema as any; - return { - jsonSchema: options?.additionalProperties - ? { ...rest, ...options.additionalProperties } - : rest, - schemaType: "zod", - }; - } - - return { - jsonSchema: options?.additionalProperties - ? { ...jsonSchema, ...options.additionalProperties } - : jsonSchema, - schemaType: "zod", - }; - } - } catch (error) { - // Library not available - } - } - - // Check if it's a Yup schema - if (typeof parser.validateSync === "function" && typeof parser.describe === "function") { - try { - // @ts-ignore - if (typeof globalThis.__yupToJsonSchema !== "undefined") { - // @ts-ignore - const { convertSchema } = globalThis.__yupToJsonSchema; - const jsonSchema = convertSchema(parser); - return { - jsonSchema: options?.additionalProperties - ? { ...jsonSchema, ...options.additionalProperties } - : jsonSchema, - schemaType: "yup", - }; - } - } catch (error) { - // Library not available + if (isYupSchema(parser)) { + const jsonSchema = convertYupSchema(parser); + if (jsonSchema) { + return { + jsonSchema: jsonSchema, + }; } } - // Check if it's an Effect schema - if (typeof parser.ast === "object" && typeof parser.ast._tag === "string") { - try { - // @ts-ignore - if (typeof globalThis.__effectJsonSchema !== "undefined") { - // @ts-ignore - const { JSONSchema } = globalThis.__effectJsonSchema; - const jsonSchema = JSONSchema.make(parser); - return { - jsonSchema: options?.additionalProperties - ? { ...jsonSchema, ...options.additionalProperties } - : jsonSchema, - schemaType: "effect", - }; - } - } catch (error) { - // Library not available + if (isEffectSchema(parser)) { + const jsonSchema = convertEffectSchema(parser); + if (jsonSchema) { + return { + jsonSchema: jsonSchema, + }; } } @@ -168,71 +99,75 @@ export function schemaToJsonSchema( } /** - * Initialize the schema conversion libraries - * This should be called by the consuming application if they want to enable - * conversion for schemas that don't have built-in JSON Schema support + * Check if a schema can be converted to JSON Schema */ -export async function initializeSchemaConverters(): Promise { - try { - // @ts-ignore - globalThis.__zodToJsonSchema = await import("zod-to-json-schema"); - } catch { - // Zod conversion not available +export function canConvertSchema(schema: Schema): boolean { + const result = schemaToJsonSchema(schema); + return result !== undefined; +} + +export function isZodSchema(schema: any): boolean { + return isZod3Schema(schema) || isZod4Schema(schema); +} + +function isZod3Schema(schema: any): boolean { + return "_def" in schema && "parse" in schema && "parseAsync" in schema && "safeParse" in schema; +} + +function isZod4Schema(schema: any): boolean { + return "_zod" in schema; +} + +function convertZodSchema(schema: any, options?: ConversionOptions): JSONSchema | undefined { + if (isZod4Schema(schema)) { + return convertZod4Schema(schema, options); } - try { - // @ts-ignore - globalThis.__yupToJsonSchema = await import("@sodaru/yup-to-json-schema"); - } catch { - // Yup conversion not available + if (isZod3Schema(schema)) { + return convertZod3Schema(schema, options); } - try { - // Try Effect first, then @effect/schema - let module; - try { - module = await import("effect"); - } catch {} + return undefined; +} - if (module?.JSONSchema) { - // @ts-ignore - globalThis.__effectJsonSchema = { JSONSchema: module.JSONSchema }; - } - } catch { - // Effect conversion not available - } +function convertZod3Schema(schema: any, options?: ConversionOptions): JSONSchema | undefined { + const useReferences = options?.useReferences ?? false; + + return zodToJsonSchema(schema, { + $refStrategy: useReferences ? "root" : "none", + }) as JSONSchema; } -/** - * Check if a schema can be converted to JSON Schema - */ -export function canConvertSchema(schema: Schema): boolean { - const result = schemaToJsonSchema(schema); - return result !== undefined; +function convertZod4Schema(schema: any, options?: ConversionOptions): JSONSchema | undefined { + const useReferences = options?.useReferences ?? false; + + return z4.toJSONSchema(schema, { + target: "draft-7", + io: "output", + reused: useReferences ? "ref" : "inline", + }) as JSONSchema; } -/** - * Get the detected schema type - */ -export function detectSchemaType(schema: Schema): ConversionResult["schemaType"] { - const result = schemaToJsonSchema(schema); - return result?.schemaType ?? "unknown"; +function isYupSchema(schema: any): boolean { + return "spec" in schema && "_typeCheck" in schema; } -/** - * Check if the conversion libraries are initialized - */ -export function areConvertersInitialized(): { - zod: boolean; - yup: boolean; - effect: boolean; -} { - return { - // @ts-ignore - zod: typeof globalThis.__zodToJsonSchema !== "undefined", - // @ts-ignore - yup: typeof globalThis.__yupToJsonSchema !== "undefined", - // @ts-ignore - effect: typeof globalThis.__effectJsonSchema !== "undefined", - }; +function convertYupSchema(schema: any): JSONSchema | undefined { + try { + return convertSchema(schema) as JSONSchema; + } catch { + return undefined; + } +} + +function isEffectSchema(schema: any): boolean { + return "ast" in schema && typeof schema.ast === "object" && typeof schema.ast._tag === "string"; +} + +function convertEffectSchema(schema: any): JSONSchema | undefined { + try { + return EffectJSONSchema.make(schema) as JSONSchema; + } catch { + return undefined; + } } diff --git a/packages/schema-to-json/tests/index.test.ts b/packages/schema-to-json/tests/index.test.ts index e64ddc96a5..b266a42b3b 100644 --- a/packages/schema-to-json/tests/index.test.ts +++ b/packages/schema-to-json/tests/index.test.ts @@ -1,44 +1,45 @@ -import { z } from "zod"; +import * as z3 from "zod/v3"; +import * as z4 from "zod/v4"; import * as y from "yup"; // @ts-ignore import { type } from "arktype"; import { Schema } from "effect"; import { Type } from "@sinclair/typebox"; -import { - schemaToJsonSchema, - canConvertSchema, - detectSchemaType, - initializeSchemaConverters, - areConvertersInitialized, -} from "../src/index.js"; - -// Initialize converters before running tests -beforeAll(async () => { - await initializeSchemaConverters(); -}); +import { schemaToJsonSchema, canConvertSchema } from "../src/index.js"; describe("schemaToJsonSchema", () => { - describe("Initialization", () => { - it("should have converters initialized", () => { - const status = areConvertersInitialized(); - expect(status.zod).toBe(true); - expect(status.yup).toBe(true); - expect(status.effect).toBe(true); - }); - }); - describe("Zod schemas", () => { it("should convert a simple Zod object schema", () => { - const schema = z.object({ - name: z.string(), - age: z.number(), - email: z.string().email(), + const schema = z3.object({ + name: z3.string(), + age: z3.number(), + email: z3.string().email(), + }); + + const result = schemaToJsonSchema(schema); + + expect(result).toBeDefined(); + expect(result?.jsonSchema).toMatchObject({ + type: "object", + properties: { + name: { type: "string" }, + age: { type: "number" }, + email: { type: "string", format: "email" }, + }, + required: ["name", "age", "email"], + }); + }); + + it("should convert a simple Zod 4 object schema", () => { + const schema = z4.object({ + name: z4.string(), + age: z4.number(), + email: z4.email(), }); const result = schemaToJsonSchema(schema); expect(result).toBeDefined(); - expect(result?.schemaType).toBe("zod"); expect(result?.jsonSchema).toMatchObject({ type: "object", properties: { @@ -51,10 +52,10 @@ describe("schemaToJsonSchema", () => { }); it("should convert a Zod schema with optional fields", () => { - const schema = z.object({ - id: z.string(), - description: z.string().optional(), - tags: z.array(z.string()).optional(), + const schema = z3.object({ + id: z3.string(), + description: z3.string().optional(), + tags: z3.array(z3.string()).optional(), }); const result = schemaToJsonSchema(schema); @@ -72,45 +73,16 @@ describe("schemaToJsonSchema", () => { }); it("should handle Zod schema with name option", () => { - const schema = z.object({ - value: z.number(), + const schema = z3.object({ + value: z3.number(), }); - const result = schemaToJsonSchema(schema, { name: "MySchema" }); + const result = schemaToJsonSchema(schema, { useReferences: true }); expect(result).toBeDefined(); expect(result?.jsonSchema).toBeDefined(); // The exact structure depends on zod-to-json-schema implementation }); - - it("should handle Zod 4 schema with built-in toJsonSchema method", () => { - // Mock a Zod 4 schema with toJsonSchema method - const mockZod4Schema = { - parse: (val: unknown) => val, - parseAsync: async (val: unknown) => val, - toJsonSchema: () => ({ - type: "object", - properties: { - id: { type: "string" }, - count: { type: "number" }, - }, - required: ["id", "count"], - }), - }; - - const result = schemaToJsonSchema(mockZod4Schema); - - expect(result).toBeDefined(); - expect(result?.schemaType).toBe("zod"); - expect(result?.jsonSchema).toEqual({ - type: "object", - properties: { - id: { type: "string" }, - count: { type: "number" }, - }, - required: ["id", "count"], - }); - }); }); describe("Yup schemas", () => { @@ -124,7 +96,6 @@ describe("schemaToJsonSchema", () => { const result = schemaToJsonSchema(schema); expect(result).toBeDefined(); - expect(result?.schemaType).toBe("yup"); expect(result?.jsonSchema).toMatchObject({ type: "object", properties: { @@ -169,7 +140,6 @@ describe("schemaToJsonSchema", () => { const result = schemaToJsonSchema(schema); expect(result).toBeDefined(); - expect(result?.schemaType).toBe("arktype"); expect(result?.jsonSchema).toBeDefined(); expect(result?.jsonSchema.type).toBe("object"); }); @@ -200,7 +170,6 @@ describe("schemaToJsonSchema", () => { const result = schemaToJsonSchema(schema); expect(result).toBeDefined(); - expect(result?.schemaType).toBe("effect"); expect(result?.jsonSchema).toMatchObject({ type: "object", properties: { @@ -238,7 +207,6 @@ describe("schemaToJsonSchema", () => { const result = schemaToJsonSchema(schema); expect(result).toBeDefined(); - expect(result?.schemaType).toBe("typebox"); expect(result?.jsonSchema).toMatchObject({ type: "object", properties: { @@ -272,27 +240,6 @@ describe("schemaToJsonSchema", () => { }); }); - describe("Additional options", () => { - it("should merge additional properties", () => { - const schema = z.object({ - value: z.number(), - }); - - const result = schemaToJsonSchema(schema, { - additionalProperties: { - title: "My Schema", - description: "A test schema", - "x-custom": "custom value", - }, - }); - - expect(result).toBeDefined(); - expect(result?.jsonSchema.title).toBe("My Schema"); - expect(result?.jsonSchema.description).toBe("A test schema"); - expect(result?.jsonSchema["x-custom"]).toBe("custom value"); - }); - }); - describe("Unsupported schemas", () => { it("should return undefined for unsupported schema types", () => { const invalidSchema = { notASchema: true }; @@ -310,7 +257,7 @@ describe("schemaToJsonSchema", () => { describe("canConvertSchema", () => { it("should return true for supported schemas", () => { - expect(canConvertSchema(z.string())).toBe(true); + expect(canConvertSchema(z3.string())).toBe(true); expect(canConvertSchema(y.string())).toBe(true); expect(canConvertSchema(type("string"))).toBe(true); expect(canConvertSchema(Schema.String)).toBe(true); @@ -322,29 +269,3 @@ describe("canConvertSchema", () => { expect(canConvertSchema(() => true)).toBe(false); }); }); - -describe("detectSchemaType", () => { - it("should detect Zod schemas", () => { - expect(detectSchemaType(z.string())).toBe("zod"); - }); - - it("should detect Yup schemas", () => { - expect(detectSchemaType(y.string())).toBe("yup"); - }); - - it("should detect ArkType schemas", () => { - expect(detectSchemaType(type("string"))).toBe("arktype"); - }); - - it("should detect Effect schemas", () => { - expect(detectSchemaType(Schema.String)).toBe("effect"); - }); - - it("should detect TypeBox schemas", () => { - expect(detectSchemaType(Type.String())).toBe("typebox"); - }); - - it("should return unknown for unsupported schemas", () => { - expect(detectSchemaType({ notASchema: true })).toBe("unknown"); - }); -}); diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c5111371c0..537d085716 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1837,14 +1837,20 @@ importers: packages/schema-to-json: dependencies: '@sodaru/yup-to-json-schema': - specifier: ^2.0.1 + specifier: ^2 version: 2.0.1 '@trigger.dev/core': specifier: workspace:* version: link:../core + effect: + specifier: ^3 + version: 3.17.1 + zod: + specifier: 3.25.76 + version: 3.25.76 zod-to-json-schema: - specifier: ^3.24.5 - version: 3.24.5(zod@3.25.76) + specifier: ^3.24.0 + version: 3.24.6(zod@3.25.76) devDependencies: '@arethetypeswrong/cli': specifier: ^0.15.4 @@ -1855,9 +1861,6 @@ importers: arktype: specifier: ^2.0.0 version: 2.1.20 - effect: - specifier: ^3.11.11 - version: 3.17.1 rimraf: specifier: 6.0.1 version: 6.0.1 @@ -1876,9 +1879,6 @@ importers: yup: specifier: ^1.7.0 version: 1.7.0 - zod: - specifier: ^3.24.1 || ^4.0.0 - version: 3.25.76 packages/trigger-sdk: dependencies: @@ -2530,7 +2530,7 @@ packages: '@standard-schema/spec': 1.0.0 eventsource-parser: 3.0.3 zod: 3.25.76 - zod-to-json-schema: 3.24.5(zod@3.25.76) + zod-to-json-schema: 3.24.6(zod@3.25.76) /@ai-sdk/provider@0.0.26: resolution: {integrity: sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==} @@ -2688,7 +2688,7 @@ packages: json-schema: 0.4.0 secure-json-parse: 2.7.0 zod: 3.25.76 - zod-to-json-schema: 3.24.5(zod@3.25.76) + zod-to-json-schema: 3.24.6(zod@3.25.76) dev: true /@ai-sdk/ui-utils@1.0.0(zod@3.25.76): @@ -2703,7 +2703,7 @@ packages: '@ai-sdk/provider': 1.0.0 '@ai-sdk/provider-utils': 2.0.0(zod@3.25.76) zod: 3.25.76 - zod-to-json-schema: 3.24.5(zod@3.25.76) + zod-to-json-schema: 3.24.6(zod@3.25.76) dev: false /@ai-sdk/ui-utils@1.2.1(zod@3.25.76): @@ -2715,7 +2715,7 @@ packages: '@ai-sdk/provider': 1.1.0 '@ai-sdk/provider-utils': 2.2.1(zod@3.25.76) zod: 3.25.76 - zod-to-json-schema: 3.24.5(zod@3.25.76) + zod-to-json-schema: 3.24.6(zod@3.25.76) dev: false /@ai-sdk/ui-utils@1.2.11(zod@3.25.76): @@ -2727,7 +2727,7 @@ packages: '@ai-sdk/provider': 1.1.3 '@ai-sdk/provider-utils': 2.2.8(zod@3.25.76) zod: 3.25.76 - zod-to-json-schema: 3.24.5(zod@3.25.76) + zod-to-json-schema: 3.24.6(zod@3.25.76) dev: false /@ai-sdk/vue@0.0.59(vue@3.5.16)(zod@3.25.76): @@ -8871,16 +8871,6 @@ packages: '@opentelemetry/api': 1.9.0 dev: false - /@opentelemetry/core@1.25.1(@opentelemetry/api@1.9.0): - resolution: {integrity: sha512-GeT/l6rBYWVQ4XArluLVB6WWQ8flHbdb6r2FCHC3smtdOAbrJBIv35tpV/yp9bmYUJf+xmZpu9DRTIeJVhFbEQ==} - engines: {node: '>=14'} - peerDependencies: - '@opentelemetry/api': '>=1.0.0 <1.10.0' - dependencies: - '@opentelemetry/api': 1.9.0 - '@opentelemetry/semantic-conventions': 1.25.1 - dev: false - /@opentelemetry/core@1.30.0(@opentelemetry/api@1.9.0): resolution: {integrity: sha512-Q/3u/K73KUjTCnFUP97ZY+pBjQ1kPEgjOfXj/bJl8zW7GbXdkw6cwuyZk6ZTXkVgCBsYRYUzx4fvYK1jxdb9MA==} engines: {node: '>=14'} @@ -9102,7 +9092,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: @@ -9131,7 +9121,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 '@types/connect': 3.4.38 @@ -9158,7 +9148,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: @@ -9201,7 +9191,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -9238,7 +9228,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: @@ -9323,7 +9313,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: @@ -9362,7 +9352,7 @@ packages: '@opentelemetry/api': ^1.3.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) '@opentelemetry/semantic-conventions': 1.36.0 transitivePeerDependencies: @@ -9449,7 +9439,7 @@ packages: '@opentelemetry/api': ^1.7.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color @@ -9800,11 +9790,6 @@ packages: '@opentelemetry/sdk-trace-base': 2.0.1(@opentelemetry/api@1.9.0) dev: false - /@opentelemetry/semantic-conventions@1.25.1: - resolution: {integrity: sha512-ZDjMJJQRlyk8A1KZFCc+bCbsyrn1wTwdNt56F7twdfUfnHUZUq77/WfONCj8p72NZOyP7pNTdUWSTYC3GTbuuQ==} - engines: {node: '>=14'} - dev: false - /@opentelemetry/semantic-conventions@1.28.0: resolution: {integrity: sha512-lp4qAiMTD4sNWW4DbKLBkfiMZ4jbAboJIGOQr5DvciMRI494OapieI9qiODpOt0XBr1LjIDy1xAGAnVs5supTA==} engines: {node: '>=14'} @@ -9822,7 +9807,7 @@ packages: '@opentelemetry/api': ^1.1.0 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/core': 1.25.1(@opentelemetry/api@1.9.0) + '@opentelemetry/core': 1.30.1(@opentelemetry/api@1.9.0) dev: false /@pkgjs/parseargs@0.11.0: @@ -9925,7 +9910,7 @@ packages: '@opentelemetry/api': ^1.8 dependencies: '@opentelemetry/api': 1.9.0 - '@opentelemetry/instrumentation': 0.52.1(@opentelemetry/api@1.9.0) + '@opentelemetry/instrumentation': 0.57.2(@opentelemetry/api@1.9.0) transitivePeerDependencies: - supports-color dev: false @@ -18068,10 +18053,10 @@ packages: /@types/json-schema@7.0.13: resolution: {integrity: sha512-RbSSoHliUbnXj3ny0CNFOoxrIDV6SUGyStHsvDqosw6CkdPV8TtWGlfecuK4ToyMEAql6pzNxgCFKanovUzlgQ==} + dev: true /@types/json-schema@7.0.15: resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} - dev: true /@types/json5@0.0.29: resolution: {integrity: sha512-dRLjCWHYg4oaA77cxO64oO+7JwCwnIzkZPdrrC71jQmQtlhM556pwKo5bUzqvZndkVbeFLIIi+9TC40JNF5hNQ==} @@ -18202,6 +18187,7 @@ packages: resolution: {integrity: sha512-acBjXdRJ3A6Pb3tqnw9HZmyR3Fiol3aGxRCK1x3d+6CDAMjl7I649wpSd+yNURCjbOUGu9tqtLKnTGxmK6CyGw==} dependencies: undici-types: 6.20.0 + dev: false /@types/nodemailer@6.4.17: resolution: {integrity: sha512-I9CCaIp6DTldEg7vyUTZi8+9Vo0hi1/T8gv3C89yk1rSAAzoKQ8H8ki/jBYJSFoH/BisgLP8tkZMlQ91CIquww==} @@ -19536,7 +19522,7 @@ packages: jsondiffpatch: 0.6.0 react: 18.3.1 zod: 3.25.76 - zod-to-json-schema: 3.24.5(zod@3.25.76) + zod-to-json-schema: 3.24.6(zod@3.25.76) dev: false /ai@4.2.5(react@19.0.0)(zod@3.25.76): @@ -22037,6 +22023,7 @@ packages: dependencies: '@standard-schema/spec': 1.0.0 fast-check: 3.23.2 + dev: false /effect@3.7.2: resolution: {integrity: sha512-pV7l1+LSZFvVObj4zuy4nYiBaC7qZOfrKV6s/Ef4p3KueiQwZFgamazklwyZ+x7Nyj2etRDFvHE/xkThTfQD1w==} @@ -23750,6 +23737,7 @@ packages: optional: true dependencies: picomatch: 4.0.2 + dev: false /fdir@6.4.4(picomatch@4.0.2): resolution: {integrity: sha512-1NZP+GK4GfuAv3PqKvxQRDMjdSRZjnkq7KfhlNrCNNlZ0ygQFpebfrnfnq/W7fpUnAv9aGWmY1zKx7FYL3gwhg==} @@ -25428,7 +25416,7 @@ packages: resolution: {integrity: sha512-7vuh85V5cdDofPyxn58nrPjBktZo0u9x1g8WtjQol+jZDaE+fhN+cIvTj11GndBnMnyfrUOG1sZQxCdjKh+DKg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/node': 22.13.9 + '@types/node': 20.14.14 merge-stream: 2.0.0 supports-color: 8.1.1 @@ -31044,7 +31032,7 @@ packages: resolution: {integrity: sha512-pN/yOAvcC+5rQ5nERGuwrjLlYvLTbCibnZ1I7B1LaiAz9BRBlE9GMgE/eqV30P7aJQUf7Ddimy/RsbYO/GrVGg==} engines: {node: '>= 10.13.0'} dependencies: - '@types/json-schema': 7.0.13 + '@types/json-schema': 7.0.15 ajv: 6.12.6 ajv-keywords: 3.5.2(ajv@6.12.6) dev: false @@ -32686,7 +32674,7 @@ packages: resolution: {integrity: sha512-qkf4trmKSIiMTs/E63cxH+ojC2unam7rJ0WrauAzpT3ECNTxGRMlaXxVbfxMUC/w0LaYk6jQ4y/nGR9uBO3tww==} engines: {node: '>=12.0.0'} dependencies: - fdir: 6.4.3(picomatch@4.0.2) + fdir: 6.4.4(picomatch@4.0.2) picomatch: 4.0.2 dev: true @@ -33381,6 +33369,7 @@ packages: /undici-types@6.20.0: resolution: {integrity: sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==} + dev: false /undici@5.28.4: resolution: {integrity: sha512-72RFADWFqKmUb2hmmvNODKL3p9hcB6Gt2DOQMis1SEBaV6a4MH8soBvzg+95CYhCKPFedut2JY9bMfrDl9D23g==} @@ -34698,6 +34687,13 @@ packages: dependencies: zod: 3.25.76 + /zod-to-json-schema@3.24.6(zod@3.25.76): + resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} + peerDependencies: + zod: ^3.24.1 + dependencies: + zod: 3.25.76 + /zod-validation-error@1.5.0(zod@3.25.76): resolution: {integrity: sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw==} engines: {node: '>=16.0.0'} diff --git a/references/hello-world/src/trigger/jsonSchema.ts b/references/hello-world/src/trigger/jsonSchema.ts index f347f9d389..d4da2c783c 100644 --- a/references/hello-world/src/trigger/jsonSchema.ts +++ b/references/hello-world/src/trigger/jsonSchema.ts @@ -1,5 +1,6 @@ import { task, schemaTask, logger, type JSONSchema } from "@trigger.dev/sdk/v3"; -import { z } from "zod"; +import { z } from "zod/v3"; +import * as z4 from "zod/v4"; import * as y from "yup"; import { type } from "arktype"; import { Type, Static } from "@sinclair/typebox"; @@ -39,6 +40,38 @@ export const processUserWithZod = schemaTask({ }, }); +const userSchema4 = z4.object({ + id: z4.uuid(), + name: z4.string().min(1), + email: z4.email(), + age: z4.number().int().min(0).max(150), + preferences: z4 + .object({ + newsletter: z4.boolean().default(false), + theme: z4.enum(["light", "dark"]).default("light"), + }) + .optional(), +}); + +export const processUserWithZod4 = schemaTask({ + id: "json-schema-zod-example-4", + schema: userSchema4, + run: async (payload, { ctx }) => { + // payload is fully typed based on the Zod schema + logger.info("Processing user with Zod schema", { + userId: payload.id, + userName: payload.name, + }); + + // The schema is automatically converted to JSON Schema and synced + return { + processed: true, + userId: payload.id, + welcomeMessage: `Welcome ${payload.name}!`, + }; + }, +}); + // =========================================== // Example 2: Using plain task with manual JSON Schema // ===========================================