Skip to content
Merged
Show file tree
Hide file tree
Changes from 17 commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
f24423e
Add payload schema handling for task indexing
ericallam Jul 27, 2025
4b840a2
Refactor: Remove getSchemaToJsonSchema in favor of schemaToJsonSchema
ericallam Jul 27, 2025
a86dd5e
Add support for Zod 4 in schema-to-json
ericallam Jul 27, 2025
9350900
Revise schema-to-json for bundle safety and tests
ericallam Jul 27, 2025
3935c1e
Refine JSON Schema typing across packages
ericallam Jul 27, 2025
bfd3645
Add JSON Schema examples using various libraries
ericallam Jul 27, 2025
7d7a3c4
Refactor SDK to encapsulate schema-to-json package
ericallam Jul 27, 2025
6b1ff95
Add JSONSchema type for payloadSchema in tasks
ericallam Jul 27, 2025
7b224f4
Add JSON schema testing and revert package dependencies
ericallam Jul 28, 2025
05ad00f
Refactor JSON Schema test files for clarity
ericallam Jul 28, 2025
9cb8e24
Fixed some stuff
ericallam Jul 28, 2025
66f136d
WIP
ericallam Jul 30, 2025
6472427
we now convert schema to jsonSchema on the CLI side via the indexing
ericallam Jul 30, 2025
5371af6
Remove the json-schema-test reference project
ericallam Aug 6, 2025
4bdc59b
Improve schema-to-json peer deps and fix effect schema
ericallam Aug 6, 2025
1b4d0e1
Explain the casting and match the version numbers
ericallam Aug 6, 2025
e1dd635
Fixed a bunch more schema stuff
ericallam Aug 6, 2025
56e5d26
Don't clean files that might be written to
ericallam Aug 6, 2025
6dfb3dd
Don't use a custom version of vitest in the new package
ericallam Aug 6, 2025
002755d
fix attw in schema-to-json
ericallam Aug 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 3 additions & 1 deletion apps/webapp/app/v3/services/createBackgroundWorker.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,8 @@ export class CreateBackgroundWorkerService extends BaseService {
version: nextVersion,
runtimeEnvironmentId: environment.id,
projectId: project.id,
metadata: body.metadata,
// body.metadata has an index signature that Prisma doesn't like (from the JSONSchema type) so we are safe to just cast it
metadata: body.metadata as Prisma.InputJsonValue,
contentHash: body.metadata.contentHash,
cliVersion: body.metadata.cliPackageVersion,
sdkVersion: body.metadata.packageVersion,
Expand Down Expand Up @@ -280,6 +281,7 @@ async function createWorkerTask(
fileId: tasksToBackgroundFiles?.get(task.id) ?? null,
maxDurationInSeconds: task.maxDuration ? clampMaxDuration(task.maxDuration) : null,
queueId: queue.id,
payloadSchema: task.payloadSchema as any,
},
});
} catch (error) {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { CreateBackgroundWorkerRequestBody } from "@trigger.dev/core/v3";
import type { BackgroundWorker } from "@trigger.dev/database";
import type { BackgroundWorker, Prisma } from "@trigger.dev/database";
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { logger } from "~/services/logger.server";
import { socketIo } from "../handleSocketIo.server";
Expand Down Expand Up @@ -48,7 +48,8 @@ export class CreateDeploymentBackgroundWorkerServiceV3 extends BaseService {
version: deployment.version,
runtimeEnvironmentId: environment.id,
projectId: environment.projectId,
metadata: body.metadata,
// body.metadata has an index signature that Prisma doesn't like (from the JSONSchema type) so we are safe to just cast it
metadata: body.metadata as Prisma.InputJsonValue,
contentHash: body.metadata.contentHash,
cliVersion: body.metadata.cliPackageVersion,
sdkVersion: body.metadata.packageVersion,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import { CreateBackgroundWorkerRequestBody, logger, tryCatch } from "@trigger.dev/core/v3";
import { BackgroundWorkerId } from "@trigger.dev/core/v3/isomorphic";
import type { BackgroundWorker, WorkerDeployment } from "@trigger.dev/database";
import type { BackgroundWorker, Prisma, WorkerDeployment } from "@trigger.dev/database";
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { BaseService, ServiceValidationError } from "./baseService.server";
import {
Expand Down Expand Up @@ -65,7 +65,8 @@ export class CreateDeploymentBackgroundWorkerServiceV4 extends BaseService {
version: deployment.version,
runtimeEnvironmentId: environment.id,
projectId: environment.projectId,
metadata: body.metadata,
// body.metadata has an index signature that Prisma doesn't like (from the JSONSchema type) so we are safe to just cast it
metadata: body.metadata as Prisma.InputJsonValue,
contentHash: body.metadata.contentHash,
cliVersion: body.metadata.cliPackageVersion,
sdkVersion: body.metadata.packageVersion,
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
-- AlterTable
ALTER TABLE "BackgroundWorkerTask" ADD COLUMN "payloadSchema" JSONB;
2 changes: 2 additions & 0 deletions internal-packages/database/prisma/schema.prisma
Original file line number Diff line number Diff line change
Expand Up @@ -510,6 +510,8 @@ model BackgroundWorkerTask {

triggerSource TaskTriggerSource @default(STANDARD)

payloadSchema Json?

@@unique([workerId, slug])
// Quick lookup of task identifiers
@@index([projectId, slug])
Expand Down
1 change: 1 addition & 0 deletions packages/cli-v3/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@
"@opentelemetry/semantic-conventions": "1.36.0",
"@trigger.dev/build": "workspace:4.0.0-v4-beta.26",
"@trigger.dev/core": "workspace:4.0.0-v4-beta.26",
"@trigger.dev/schema-to-json": "workspace:4.0.0-v4-beta.26",
"ansi-escapes": "^7.0.0",
"braces": "^3.0.3",
"c12": "^1.11.1",
Expand Down
24 changes: 23 additions & 1 deletion packages/cli-v3/src/entryPoints/dev-index-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +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";

sourceMapSupport.install({
handleUncaughtExceptions: false,
Expand Down Expand Up @@ -100,7 +101,7 @@ async function bootstrap() {

const { buildManifest, importErrors, config, timings } = await bootstrap();

let tasks = resourceCatalog.listTaskManifests();
let tasks = await convertSchemasToJsonSchemas(resourceCatalog.listTaskManifests());

// If the config has retry defaults, we need to apply them to all tasks that don't have any retry settings
if (config.retries?.default) {
Expand Down Expand Up @@ -190,3 +191,24 @@ await new Promise<void>((resolve) => {
resolve();
}, 10);
});

async function convertSchemasToJsonSchemas(tasks: TaskManifest[]): Promise<TaskManifest[]> {
await initializeSchemaConverters();

const convertedTasks = tasks.map((task) => {
const schema = resourceCatalog.getTaskSchema(task.id);

if (schema) {
try {
const result = schemaToJsonSchema(schema);
return { ...task, payloadSchema: result?.jsonSchema };
} catch {
return task;
}
}

return task;
});

return convertedTasks;
}
24 changes: 23 additions & 1 deletion packages/cli-v3/src/entryPoints/managed-index-worker.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +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";

sourceMapSupport.install({
handleUncaughtExceptions: false,
Expand Down Expand Up @@ -100,7 +101,7 @@ async function bootstrap() {

const { buildManifest, importErrors, config, timings } = await bootstrap();

let tasks = resourceCatalog.listTaskManifests();
let tasks = await convertSchemasToJsonSchemas(resourceCatalog.listTaskManifests());

// If the config has retry defaults, we need to apply them to all tasks that don't have any retry settings
if (config.retries?.default) {
Expand Down Expand Up @@ -196,3 +197,24 @@ await new Promise<void>((resolve) => {
resolve();
}, 10);
});

async function convertSchemasToJsonSchemas(tasks: TaskManifest[]): Promise<TaskManifest[]> {
await initializeSchemaConverters();

const convertedTasks = tasks.map((task) => {
const schema = resourceCatalog.getTaskSchema(task.id);

if (schema) {
try {
const result = schemaToJsonSchema(schema);
return { ...task, payloadSchema: result?.jsonSchema };
} catch {
return task;
}
}

return task;
});

return convertedTasks;
}
3 changes: 2 additions & 1 deletion packages/core/src/v3/resource-catalog/catalog.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { QueueManifest, TaskManifest, WorkerManifest } from "../schemas/index.js";
import { TaskMetadataWithFunctions } from "../types/index.js";
import { TaskMetadataWithFunctions, TaskSchema } from "../types/index.js";

export interface ResourceCatalog {
setCurrentFileContext(filePath: string, entryPoint: string): void;
Expand All @@ -13,4 +13,5 @@ export interface ResourceCatalog {
registerWorkerManifest(workerManifest: WorkerManifest): void;
registerQueueMetadata(queue: QueueManifest): void;
listQueueManifests(): Array<QueueManifest>;
getTaskSchema(id: string): TaskSchema | undefined;
}
6 changes: 5 additions & 1 deletion packages/core/src/v3/resource-catalog/index.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
const API_NAME = "resource-catalog";

import { QueueManifest, TaskManifest, WorkerManifest } from "../schemas/index.js";
import { TaskMetadataWithFunctions } from "../types/index.js";
import { TaskMetadataWithFunctions, TaskSchema } from "../types/index.js";
import { getGlobal, registerGlobal, unregisterGlobal } from "../utils/globals.js";
import { type ResourceCatalog } from "./catalog.js";
import { NoopResourceCatalog } from "./noopResourceCatalog.js";
Expand Down Expand Up @@ -65,6 +65,10 @@ export class ResourceCatalogAPI {
return this.#getCatalog().getTask(id);
}

public getTaskSchema(id: string): TaskSchema | undefined {
return this.#getCatalog().getTaskSchema(id);
}

public taskExists(id: string): boolean {
return this.#getCatalog().taskExists(id);
}
Expand Down
6 changes: 5 additions & 1 deletion packages/core/src/v3/resource-catalog/noopResourceCatalog.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { QueueManifest, TaskManifest, WorkerManifest } from "../schemas/index.js";
import { TaskMetadataWithFunctions } from "../types/index.js";
import { TaskMetadataWithFunctions, TaskSchema } from "../types/index.js";
import { ResourceCatalog } from "./catalog.js";

export class NoopResourceCatalog implements ResourceCatalog {
Expand Down Expand Up @@ -31,6 +31,10 @@ export class NoopResourceCatalog implements ResourceCatalog {
return undefined;
}

getTaskSchema(id: string): TaskSchema | undefined {
return undefined;
}

taskExists(id: string): boolean {
return false;
}
Expand Down
17 changes: 14 additions & 3 deletions packages/core/src/v3/resource-catalog/standardResourceCatalog.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,11 @@ import {
WorkerManifest,
QueueManifest,
} from "../schemas/index.js";
import { TaskMetadataWithFunctions } from "../types/index.js";
import { TaskMetadataWithFunctions, TaskSchema } from "../types/index.js";
import { ResourceCatalog } from "./catalog.js";

export class StandardResourceCatalog implements ResourceCatalog {
private _taskSchemas: Map<string, TaskSchema> = new Map();
private _taskMetadata: Map<string, TaskMetadata> = new Map();
private _taskFunctions: Map<string, TaskMetadataWithFunctions["fns"]> = new Map();
private _taskFileMetadata: Map<string, TaskFileMetadata> = new Map();
Expand Down Expand Up @@ -72,6 +73,10 @@ export class StandardResourceCatalog implements ResourceCatalog {

this._taskMetadata.set(task.id, metadata);
this._taskFunctions.set(task.id, fns);

if (task.schema) {
this._taskSchemas.set(task.id, task.schema);
}
}

updateTaskMetadata(id: string, updates: Partial<TaskMetadataWithFunctions>): void {
Expand Down Expand Up @@ -107,15 +112,21 @@ export class StandardResourceCatalog implements ResourceCatalog {
continue;
}

result.push({
const taskManifest = {
...metadata,
...fileMetadata,
});
};

result.push(taskManifest);
}

return result;
}

getTaskSchema(id: string): TaskSchema | undefined {
return this._taskSchemas.get(id);
}

listQueueManifests(): Array<QueueManifest> {
return Array.from(this._queueMetadata.values());
}
Expand Down
2 changes: 2 additions & 0 deletions packages/core/src/v3/schemas/resources.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ export const TaskResource = z.object({
triggerSource: z.string().optional(),
schedule: ScheduleMetadata.optional(),
maxDuration: z.number().optional(),
// JSONSchema type - using z.unknown() for runtime validation to accept JSONSchema7
payloadSchema: z.unknown().optional(),
});

export type TaskResource = z.infer<typeof TaskResource>;
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/v3/schemas/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -189,6 +189,7 @@ const taskMetadata = {
triggerSource: z.string().optional(),
schedule: ScheduleMetadata.optional(),
maxDuration: z.number().optional(),
payloadSchema: z.unknown().optional(),
};

export const TaskMetadata = z.object(taskMetadata);
Expand Down
1 change: 1 addition & 0 deletions packages/core/src/v3/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ export * from "./tasks.js";
export * from "./idempotencyKeys.js";
export * from "./tools.js";
export * from "./queues.js";
export * from "./jsonSchema.js";

type ResolveEnvironmentVariablesOptions = {
variables: Record<string, string> | Array<{ name: string; value: string }>;
Expand Down
76 changes: 76 additions & 0 deletions packages/core/src/v3/types/jsonSchema.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
/**
* JSON Schema type definition - compatible with JSON Schema Draft 7
* Based on the JSONSchema7 type from @types/json-schema but defined inline to avoid import issues
*/
export interface JSONSchema {
$id?: string;
$ref?: string;
$schema?: string;
$comment?: string;

type?: JSONSchemaType | JSONSchemaType[];
enum?: any[];
const?: any;

// Number/Integer validations
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: number;
minimum?: number;
exclusiveMinimum?: number;

// String validations
maxLength?: number;
minLength?: number;
pattern?: string;
format?: string;

// Array validations
items?: JSONSchema | JSONSchema[];
additionalItems?: JSONSchema | boolean;
maxItems?: number;
minItems?: number;
uniqueItems?: boolean;
contains?: JSONSchema;

// Object validations
maxProperties?: number;
minProperties?: number;
required?: string[];
properties?: Record<string, JSONSchema>;
patternProperties?: Record<string, JSONSchema>;
additionalProperties?: JSONSchema | boolean;
dependencies?: Record<string, JSONSchema | string[]>;
propertyNames?: JSONSchema;

// Conditional schemas
if?: JSONSchema;
then?: JSONSchema;
else?: JSONSchema;

// Boolean logic
allOf?: JSONSchema[];
anyOf?: JSONSchema[];
oneOf?: JSONSchema[];
not?: JSONSchema;

// Metadata
title?: string;
description?: string;
default?: any;
readOnly?: boolean;
writeOnly?: boolean;
examples?: any[];

// Additional properties for extensibility
[key: string]: any;
}

export type JSONSchemaType =
| "string"
| "number"
| "integer"
| "boolean"
| "object"
| "array"
| "null";
Loading
Loading