From 03d576717bdebfea008778aac84b4efc1937c50d Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Tue, 30 Jun 2026 16:08:57 +0100 Subject: [PATCH 01/17] chore: upgrade to zod v4 --- .changeset/zod-4-support.md | 9 ++++ apps/supervisor/package.json | 2 +- apps/webapp/app/components/Feedback.tsx | 26 +++-------- .../errors/ConfigureErrorAlerts.tsx | 2 +- .../app/components/metrics/QueryWidget.tsx | 2 +- .../components/runs/v3/ReplayRunDialog.tsx | 2 +- .../schedules/PurchaseSchedulesModal.tsx | 2 +- .../app/models/orgIntegration.server.ts | 2 +- .../app/models/vercelIntegration.server.ts | 2 +- .../app/models/vercelSdkRecovery.server.ts | 4 +- .../v3/MetricDashboardPresenter.server.ts | 2 +- .../app/presenters/v3/SpanPresenter.server.ts | 3 ++ .../route.tsx | 2 +- .../route.tsx | 9 ++-- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 4 +- .../route.tsx | 43 ++++++++----------- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 2 +- .../route.tsx | 14 +++--- .../route.tsx | 2 +- .../webapp/app/routes/_app.orgs.new/route.tsx | 2 +- .../app/routes/account._index/route.tsx | 4 +- .../app/routes/account.tokens/route.tsx | 4 +- .../webapp/app/routes/admin.feature-flags.tsx | 2 +- .../app/routes/api.v1.plain.customer-cards.ts | 2 +- .../webapp/app/routes/api.v1.prompts.$slug.ts | 2 +- apps/webapp/app/routes/api.v1.query.ts | 2 +- apps/webapp/app/routes/api.v1.schedules.ts | 1 + .../app/routes/confirm-basic-details.tsx | 2 +- apps/webapp/app/routes/invite-resend.tsx | 2 +- apps/webapp/app/routes/invite-revoke.tsx | 2 +- apps/webapp/app/routes/invites.tsx | 2 +- ...deployments.$deploymentShortCode.cancel.ts | 2 +- ...eployments.$deploymentShortCode.promote.ts | 2 +- ...ployments.$deploymentShortCode.rollback.ts | 2 +- ...urces.batches.$batchId.check-completion.ts | 2 +- .../app/routes/resources.branches.archive.tsx | 8 ++-- apps/webapp/app/routes/resources.feedback.ts | 5 +-- ...cts.$projectParam.env.$envParam.github.tsx | 2 +- ...ectParam.env.$envParam.runs.bulkaction.tsx | 2 +- .../route.tsx | 4 +- ...cts.$projectParam.env.$envParam.vercel.tsx | 2 +- .../route.tsx | 2 +- ....orgs.$organizationSlug.schedules-addon.ts | 2 +- .../resources.sessions.$sessionParam.close.ts | 2 +- .../resources.taskruns.$runParam.cancel.ts | 2 +- .../resources.taskruns.$runParam.replay.ts | 2 +- apps/webapp/app/services/apiAuth.server.ts | 4 +- ...authorizationRateLimitMiddleware.server.ts | 2 +- .../app/services/queryService.server.ts | 2 +- .../routeBuilders/apiBuilder.server.ts | 2 +- .../routeBuilders/dashboardBuilder.server.ts | 2 +- .../routeBuilders/dashboardBuilder.ts | 2 +- apps/webapp/app/utils/json.ts | 4 +- apps/webapp/app/utils/timeGranularity.ts | 16 ++++--- apps/webapp/app/v3/featureFlags.server.ts | 4 +- apps/webapp/app/v3/featureFlags.ts | 33 ++++++++------ apps/webapp/app/v3/schedules.ts | 37 ++++++++-------- .../v3/services/aiRunFilterService.server.ts | 2 +- .../app/v3/services/replayTaskRun.server.ts | 4 +- apps/webapp/app/v3/utils/zodPubSub.server.ts | 2 +- .../vercel/vercelProjectIntegrationSchema.ts | 2 +- apps/webapp/package.json | 4 +- internal-packages/clickhouse/package.json | 2 +- .../clickhouse/src/client/tsql.ts | 4 +- .../clickhouse/src/tsqlFunctions.test.ts | 2 +- internal-packages/compute/package.json | 2 +- internal-packages/compute/src/types.ts | 18 ++++---- internal-packages/emails/package.json | 2 +- internal-packages/run-engine/package.json | 2 +- .../run-engine/src/batch-queue/types.ts | 6 +-- .../schedule-engine/package.json | 2 +- internal-packages/tsql/package.json | 2 +- packages/cli-v3/package.json | 9 ++-- packages/cli-v3/src/cli/common.ts | 4 +- packages/cli-v3/src/commands/analyze.ts | 4 +- packages/cli-v3/src/mcp/tools/agentChat.ts | 2 +- packages/cli-v3/src/utilities/configFiles.ts | 4 +- packages/core/package.json | 11 +++-- packages/core/src/schemas/eventFilter.ts | 2 +- packages/core/src/schemas/json.ts | 4 +- packages/core/src/v3/apiClient/index.ts | 2 +- .../v3/runEngineWorker/supervisor/schemas.ts | 4 +- packages/core/src/v3/schemas/api.ts | 42 +++++++++--------- packages/core/src/v3/schemas/build.ts | 17 ++++---- packages/core/src/v3/schemas/common.ts | 12 +++--- packages/core/src/v3/schemas/eventFilter.ts | 2 +- packages/core/src/v3/schemas/messages.ts | 4 +- packages/core/src/v3/schemas/openTelemetry.ts | 2 +- packages/core/src/v3/schemas/query.ts | 2 +- packages/core/src/v3/schemas/resources.ts | 2 +- packages/core/src/v3/schemas/runEngine.ts | 2 +- packages/core/src/v3/schemas/schemas.ts | 14 +++--- packages/core/src/v3/serverOnly/httpServer.ts | 5 ++- packages/core/src/v3/types/tools.ts | 2 +- packages/core/src/v3/zodIpc.ts | 5 ++- packages/core/src/v3/zodMessageHandler.ts | 9 ++-- packages/core/src/v3/zodSocket.ts | 5 ++- packages/redis-worker/package.json | 7 ++- .../redis-worker/src/mollifier/schemas.ts | 6 +-- packages/redis-worker/src/queue.ts | 5 ++- packages/redis-worker/src/worker.ts | 4 +- packages/trigger-sdk/package.json | 4 +- 113 files changed, 300 insertions(+), 285 deletions(-) create mode 100644 .changeset/zod-4-support.md diff --git a/.changeset/zod-4-support.md b/.changeset/zod-4-support.md new file mode 100644 index 00000000000..c0578513828 --- /dev/null +++ b/.changeset/zod-4-support.md @@ -0,0 +1,9 @@ +--- +"@trigger.dev/core": minor +"@trigger.dev/sdk": minor +"trigger.dev": minor +"@trigger.dev/redis-worker": minor +"@trigger.dev/schema-to-json": minor +--- + +Add zod v4 compatibility. The `zod` peer dependency is widened to `^3.25.0 || ^4.0.0`, so projects can use zod 3.25+ or zod 4. Internal code was updated for zod v4 API changes (`ZodError.errors` → `.issues`, single-arg `z.record` → keyed, unified `error` option, `z.ZodSchema`/`z.AnyZodObject` → `z.ZodType`/`z.ZodObject`, `z.any()` object fields made `.optional()` to preserve v3 inference). No runtime behavior change for existing zod 3 users. diff --git a/apps/supervisor/package.json b/apps/supervisor/package.json index 2725fe2b729..d4555061708 100644 --- a/apps/supervisor/package.json +++ b/apps/supervisor/package.json @@ -23,7 +23,7 @@ "prom-client": "^15.1.0", "socket.io": "4.7.4", "std-env": "^3.8.0", - "zod": "3.25.76" + "zod": "4.4.3" }, "devDependencies": { "@internal/testcontainers": "workspace:*", diff --git a/apps/webapp/app/components/Feedback.tsx b/apps/webapp/app/components/Feedback.tsx index b679b042da1..7ceaa05fb53 100644 --- a/apps/webapp/app/components/Feedback.tsx +++ b/apps/webapp/app/components/Feedback.tsx @@ -1,11 +1,5 @@ -import { - getFormProps, - getSelectProps, - getInputProps, - getTextareaProps, - useForm, -} from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { getFormProps, getSelectProps, getInputProps, getTextareaProps, useForm } from "@conform-to/react"; +import { parseWithZod } from "@conform-to/zod/v4"; import { InformationCircleIcon, ArrowUpCircleIcon } from "@heroicons/react/20/solid"; import { EnvelopeIcon, ShieldCheckIcon } from "@heroicons/react/24/solid"; import { Form, useActionData, useLocation, useNavigation, useSearchParams } from "@remix-run/react"; @@ -66,11 +60,11 @@ export function Feedback({ if ( navigation.formAction === "/resources/feedback" && navigation.state === "loading" && - Object.keys(form.allErrors).length === 0 + (form.errors === undefined || form.errors.length === 0) ) { setOpen(false); } - }, [navigation.formAction, navigation.state, form.allErrors]); + }, [navigation, form]); // Handle URL param functionality useEffect(() => { @@ -120,17 +114,9 @@ export function Feedback({ type === "concurrency" || type === "hipaa" ) &&
} -
+
- + {type === "feature" && ( ; diff --git a/apps/webapp/app/models/vercelIntegration.server.ts b/apps/webapp/app/models/vercelIntegration.server.ts index 3a1aaf4b8ea..324a1651270 100644 --- a/apps/webapp/app/models/vercelIntegration.server.ts +++ b/apps/webapp/app/models/vercelIntegration.server.ts @@ -129,7 +129,7 @@ export const VercelSecretSchema = z.object({ teamId: z.string().nullable().optional(), userId: z.string().optional(), installationId: z.string().optional(), - raw: z.record(z.any()).optional(), + raw: z.record(z.string(), z.any()).optional(), }); export type VercelSecret = z.infer; diff --git a/apps/webapp/app/models/vercelSdkRecovery.server.ts b/apps/webapp/app/models/vercelSdkRecovery.server.ts index d3e1bfd6961..31253fe430e 100644 --- a/apps/webapp/app/models/vercelSdkRecovery.server.ts +++ b/apps/webapp/app/models/vercelSdkRecovery.server.ts @@ -145,11 +145,11 @@ export const VercelSchemas = { .union([ z .object({ - envs: z.array(z.record(z.unknown())), + envs: z.array(z.record(z.string(), z.unknown())), pagination: z.unknown().optional(), }) .passthrough(), - z.array(z.record(z.unknown())), + z.array(z.record(z.string(), z.unknown())), ]) .transform((val) => (Array.isArray(val) ? { envs: val } : val)), diff --git a/apps/webapp/app/presenters/v3/MetricDashboardPresenter.server.ts b/apps/webapp/app/presenters/v3/MetricDashboardPresenter.server.ts index 0b84e971b2f..5c43a7ff63c 100644 --- a/apps/webapp/app/presenters/v3/MetricDashboardPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/MetricDashboardPresenter.server.ts @@ -48,7 +48,7 @@ export const DashboardLayout = z.discriminatedUnion("version", [ z.object({ version: z.literal("1"), layout: z.array(LayoutItem), - widgets: z.record(Widget), + widgets: z.record(z.string(), Widget), }), ]); diff --git a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts index b680a1c6a8e..209e906e50f 100644 --- a/apps/webapp/app/presenters/v3/SpanPresenter.server.ts +++ b/apps/webapp/app/presenters/v3/SpanPresenter.server.ts @@ -1024,6 +1024,9 @@ export class SpanPresenter extends BasePresenter { filePath: lockedWorker?.lockedBy?.filePath ?? "", }, run: { + // zod v4 types the run-context `context` (z.any) field as required; it was + // never populated here (optional under v3), so undefined preserves behavior. + context: undefined, id: run.friendlyId, createdAt: run.createdAt, tags: run.runTags, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsx index b6fe0b09c68..b51a6d5ae1a 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.invite/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { ArrowUpCircleIcon, EnvelopeIcon, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx index f121900e901..275f6556eb3 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.alerts.new/route.tsx @@ -1,11 +1,11 @@ import { getFormProps, getInputProps, getSelectProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { HashtagIcon, LockClosedIcon } from "@heroicons/react/20/solid"; import { Form, useActionData, useNavigate, useNavigation } from "@remix-run/react"; import { type LoaderFunctionArgs } from "@remix-run/router"; import { type ActionFunctionArgs, json } from "@remix-run/server-runtime"; import { SlackIcon } from "@trigger.dev/companyicons"; -import { useEffect, useRef, useState } from "react"; +import { useEffect, useState } from "react"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; import { z } from "zod"; import { InlineCode } from "~/components/code/InlineCode"; @@ -233,7 +233,6 @@ export default function Page() { const project = useProject(); const environment = useEnvironment(); const [currentAlertChannel, setCurrentAlertChannel] = useState(option ?? "EMAIL"); - const formRef = useRef(null); const [selectedSlackChannelValue, setSelectedSlackChannelValue] = useState(); @@ -267,7 +266,7 @@ export default function Page() { if (navigation.state !== "idle") return; if (lastSubmission !== undefined) return; - formRef.current?.reset(); + form.reset(); }, [navigation.state, lastSubmission]); return ( @@ -281,7 +280,7 @@ export default function Page() { > New alert - +
- {formEnvironments.errors} + {formEnvironments.errors} diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx index 02d6ef00c4d..5409b8ab217 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, useForm, type FieldMetadata, type FormMetadata } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { LockClosedIcon, LockOpenIcon, @@ -74,22 +74,19 @@ const schema = z.object({ if (i === "true") return true; return false; }, z.boolean()), - environmentIds: z.preprocess( - (i) => { - if (typeof i === "string") return [i]; - - if (Array.isArray(i)) { - const ids = i.filter((v) => typeof v === "string" && v !== ""); - if (ids.length === 0) { - return; - } - return ids; + environmentIds: z.preprocess((i) => { + if (typeof i === "string") return [i]; + + if (Array.isArray(i)) { + const ids = i.filter((v) => typeof v === "string" && v !== ""); + if (ids.length === 0) { + return; } + return ids; + } - return; - }, - z.array(z.string(), { required_error: "At least one environment is required" }) - ), + return; + }, z.array(z.string(), { error: "At least one environment is required" })), variables: z.preprocess((i) => { if (!Array.isArray(i)) { return []; @@ -247,14 +244,12 @@ export default function Page() { const [selectedEnvironmentIds, setSelectedEnvironmentIds] = useState>(new Set()); const [selectedBranchId, setSelectedBranchId] = useState(undefined); - // TODO for no we only support branch-specific env vars for Preview environments - // Mostly to keep the UX for setting consistent env-vars across Dev/Staging/Prod easier - const previewBranches = environments.filter( - (env) => env.type === "PREVIEW" && env.parentEnvironmentId !== null - ); - const nonBranchEnvironments = environments.filter((env) => env.parentEnvironmentId === null); + const branchEnvironments = environments.filter((env) => env.branchName); + const nonBranchEnvironments = environments.filter((env) => !env.branchName); const selectedEnvironments = environments.filter((env) => selectedEnvironmentIds.has(env.id)); - const previewIsSelected = selectedEnvironments.some((env) => env.type === "PREVIEW"); + const previewIsSelected = selectedEnvironments.some( + (env) => env.branchName !== null || env.type === "PREVIEW" + ); const isLoading = navigation.state !== "idle" && navigation.formMethod === "post"; @@ -436,7 +431,7 @@ export default function Page() { value={selectedBranchId ?? "all"} setValue={handleBranchChange} placeholder="All branches" - items={[{ id: "all", branchName: "All branches" }, ...previewBranches]} + items={[{ id: "all", branchName: "All branches" }, ...branchEnvironments]} className="w-fit min-w-52" filter={{ keys: [ @@ -444,7 +439,7 @@ export default function Page() { ], }} text={(val) => - val ? previewBranches.find((b) => b.id === val)?.branchName : null + val ? branchEnvironments.find((b) => b.id === val)?.branchName : null } dropdownIcon > diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx index bd152e58033..d53c28df3cb 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { BookOpenIcon, InformationCircleIcon, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint/route.tsx index 6ec1aa7fce1..1b23b80390b 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors.$fingerprint/route.tsx @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { BellAlertIcon } from "@heroicons/react/20/solid"; import { type MetaFunction, useFetcher, useRevalidator } from "@remix-run/react"; import { type ActionFunctionArgs, json, type LoaderFunctionArgs } from "@remix-run/server-runtime"; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx index 769a4868266..7ed9502b375 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.errors/route.tsx @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { Outlet, useNavigate } from "@remix-run/react"; import { type ActionFunctionArgs, type LoaderFunctionArgs, json } from "@remix-run/server-runtime"; import { useCallback } from "react"; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx index 4f5a2c1e52d..5c8a7b77fd7 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.query/route.tsx @@ -160,7 +160,7 @@ export const action = async ({ request, params }: ActionFunctionArgs) => { if (!parsed.success) { return typedjson( { - error: parsed.error.errors.map((e) => e.message).join(", "), + error: parsed.error.issues.map((e) => e.message).join(", "), rows: null, columns: null, stats: null, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx index a3a0ef05470..3d154e26289 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.$scheduleParam/route.tsx @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { useLocation } from "@remix-run/react"; import { type ActionFunctionArgs, type LoaderFunctionArgs, json } from "@remix-run/server-runtime"; import { typedjson, useTypedLoaderData } from "remix-typedjson"; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.general/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.general/route.tsx index ccc61fc5ac9..c2f3ec2aa22 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.general/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.general/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { conformZodMessage, parseWithZod } from "@conform-to/zod"; +import { conformZodMessage, parseWithZod } from "@conform-to/zod/v4"; import { ExclamationTriangleIcon, FolderIcon, TrashIcon } from "@heroicons/react/20/solid"; import { Form, useActionData, useNavigation } from "@remix-run/react"; import { json } from "@remix-run/server-runtime"; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.integrations/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.integrations/route.tsx index 9bc54b19bf1..706aad4d860 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.integrations/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.settings.integrations/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { Form, useActionData, useNavigation, useSearchParams } from "@remix-run/react"; import { json } from "@remix-run/server-runtime"; import React, { useCallback, useEffect, useRef, useState } from "react"; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx index ffe6dbcd038..a7cdf1e189a 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, getSelectProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { CheckCircleIcon, RectangleStackIcon, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index/route.tsx index 348658ba9ef..29c1e0c26f8 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings._index/route.tsx @@ -1,6 +1,6 @@ import colorWheelIcon from "../../assets/images/color-wheel.png"; import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { conformZodMessage, parseWithZod } from "@conform-to/zod"; +import { conformZodMessage, parseWithZod } from "@conform-to/zod/v4"; import { CheckIcon, ExclamationTriangleIcon, diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx index 1dc9753ac13..888d30c681a 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.private-connections.new/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, getSelectProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { Form, useActionData, useParams, type MetaFunction } from "@remix-run/react"; import { json, type ActionFunction, type LoaderFunctionArgs } from "@remix-run/server-runtime"; import { tryCatch } from "@trigger.dev/core/utils"; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx index cc2a95571d0..afb0bacd37e 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { EnvelopeIcon, NoSymbolIcon, UserPlusIcon } from "@heroicons/react/20/solid"; import { DialogClose } from "@radix-ui/react-dialog"; import { @@ -1020,11 +1020,11 @@ export function PurchaseSeatsModal({ // when the role can't manage billing. The action enforces it independently. const noBillingTooltip = "You don't have permission to manage billing"; const trigger = canManageBilling ? ( - (triggerButton ?? ( + triggerButton ?? ( - )) + ) ) : triggerButton ? ( cloneElement(triggerButton, { disabled: true, tooltip: noBillingTooltip }) ) : ( @@ -1042,11 +1042,7 @@ export function PurchaseSeatsModal({ {trigger} {title} - +
@@ -1062,7 +1058,7 @@ export function PurchaseSeatsModal({ Total extra seats [], result.columns); return json({ format: "csv", diff --git a/apps/webapp/app/routes/api.v1.schedules.ts b/apps/webapp/app/routes/api.v1.schedules.ts index b5fd2fd968c..c7eb6f87281 100644 --- a/apps/webapp/app/routes/api.v1.schedules.ts +++ b/apps/webapp/app/routes/api.v1.schedules.ts @@ -108,6 +108,7 @@ export async function loader({ request }: LoaderFunctionArgs) { environmentId: authenticationResult.environment.id, page: params.data.page ?? 1, pageSize: params.data.perPage, + tasks: undefined, }); return { diff --git a/apps/webapp/app/routes/confirm-basic-details.tsx b/apps/webapp/app/routes/confirm-basic-details.tsx index 4e370913240..913f88df80f 100644 --- a/apps/webapp/app/routes/confirm-basic-details.tsx +++ b/apps/webapp/app/routes/confirm-basic-details.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { conformZodMessage, parseWithZod } from "@conform-to/zod"; +import { conformZodMessage, parseWithZod } from "@conform-to/zod/v4"; import { ArrowRightIcon, EnvelopeIcon, UserIcon } from "@heroicons/react/20/solid"; import { HandRaisedIcon } from "@heroicons/react/24/solid"; import { RadioGroup } from "@radix-ui/react-radio-group"; diff --git a/apps/webapp/app/routes/invite-resend.tsx b/apps/webapp/app/routes/invite-resend.tsx index 3cba88bd20a..d64cc2d2aaf 100644 --- a/apps/webapp/app/routes/invite-resend.tsx +++ b/apps/webapp/app/routes/invite-resend.tsx @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { json } from "@remix-run/server-runtime"; import { env } from "process"; import { z } from "zod"; diff --git a/apps/webapp/app/routes/invite-revoke.tsx b/apps/webapp/app/routes/invite-revoke.tsx index fc1e4d0b79e..d4ab69f9c83 100644 --- a/apps/webapp/app/routes/invite-revoke.tsx +++ b/apps/webapp/app/routes/invite-revoke.tsx @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { json } from "@remix-run/server-runtime"; import { z } from "zod"; import { $replica } from "~/db.server"; diff --git a/apps/webapp/app/routes/invites.tsx b/apps/webapp/app/routes/invites.tsx index 6cf667c8677..acc0a09f4a3 100644 --- a/apps/webapp/app/routes/invites.tsx +++ b/apps/webapp/app/routes/invites.tsx @@ -1,5 +1,5 @@ import { getFormProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { EnvelopeIcon } from "@heroicons/react/20/solid"; import { type ActionFunction, type LoaderFunctionArgs, json, redirect } from "@remix-run/node"; import { Form, useActionData } from "@remix-run/react"; diff --git a/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.cancel.ts b/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.cancel.ts index 239d0982f9e..6acf14777c0 100644 --- a/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.cancel.ts +++ b/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.cancel.ts @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { json } from "@remix-run/node"; import { errAsync, fromPromise, okAsync } from "neverthrow"; import { z } from "zod"; diff --git a/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.promote.ts b/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.promote.ts index 979264516d1..529f627e5fb 100644 --- a/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.promote.ts +++ b/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.promote.ts @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { json } from "@remix-run/node"; import { z } from "zod"; import { $replica, prisma } from "~/db.server"; diff --git a/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.rollback.ts b/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.rollback.ts index d109e97c9d5..3caed9013b3 100644 --- a/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.rollback.ts +++ b/apps/webapp/app/routes/resources.$projectId.deployments.$deploymentShortCode.rollback.ts @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { json } from "@remix-run/node"; import { z } from "zod"; import { $replica, prisma } from "~/db.server"; diff --git a/apps/webapp/app/routes/resources.batches.$batchId.check-completion.ts b/apps/webapp/app/routes/resources.batches.$batchId.check-completion.ts index 2d1c6bc702b..3c7cd64523b 100644 --- a/apps/webapp/app/routes/resources.batches.$batchId.check-completion.ts +++ b/apps/webapp/app/routes/resources.batches.$batchId.check-completion.ts @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import type { ActionFunction } from "@remix-run/node"; import { json } from "@remix-run/node"; import { z } from "zod"; diff --git a/apps/webapp/app/routes/resources.branches.archive.tsx b/apps/webapp/app/routes/resources.branches.archive.tsx index bf9e05a0d64..490cb77fc03 100644 --- a/apps/webapp/app/routes/resources.branches.archive.tsx +++ b/apps/webapp/app/routes/resources.branches.archive.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { DialogClose } from "@radix-ui/react-dialog"; import { Form, useActionData, useLocation } from "@remix-run/react"; import { type ActionFunctionArgs } from "@remix-run/server-runtime"; @@ -100,10 +100,10 @@ export function ArchiveButton({ {...getFormProps(form)} className="w-full" > - + This will permanently make this branch{" "} @@ -116,7 +116,7 @@ export function ArchiveButton({ Once archived you can create a new branch with the same name. - {form.errors} + {form.errors?.join(", ")} diff --git a/apps/webapp/app/routes/resources.feedback.ts b/apps/webapp/app/routes/resources.feedback.ts index 4ace262f0cb..41f0f24249d 100644 --- a/apps/webapp/app/routes/resources.feedback.ts +++ b/apps/webapp/app/routes/resources.feedback.ts @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { type ActionFunctionArgs, json } from "@remix-run/server-runtime"; import { uiComponent } from "@team-plain/ui-components"; import { z } from "zod"; @@ -56,8 +56,7 @@ const feedbackTypeLiterals = Object.keys(feedbackTypes).map((key) => z.literal(k const feedbackType = z.union( [feedbackTypeLiterals[0], feedbackTypeLiterals[1], ...feedbackTypeLiterals.slice(2)], { - required_error: "Must be either 'bug' or 'feature'", - invalid_type_error: "Must be either 'bug' or 'feature'", + error: "Must be a valid feedback type", } ); diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx index 4840a424d8c..e5dfd81a97e 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.github.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { ArrowUpCircleIcon, ArrowUpRightIcon, diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.bulkaction.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.bulkaction.tsx index 5bef068b994..66c5d92297a 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.bulkaction.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.bulkaction.tsx @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { ArrowPathIcon, InformationCircleIcon } from "@heroicons/react/20/solid"; import { XCircleIcon } from "@heroicons/react/24/outline"; import { Form } from "@remix-run/react"; diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.new/route.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.new/route.tsx index 61ce814dbd5..32de08aca6d 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.new/route.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.schedules.new/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, getSelectProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { CheckIcon, XMarkIcon } from "@heroicons/react/20/solid"; import { type FetcherWithComponents, @@ -196,7 +196,7 @@ export function UpsertScheduleForm({ if (!result.success) { cronPatternResult = { isValid: false, - error: result.error.errors[0].message, + error: result.error.issues[0].message, }; } else { try { diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx index 792275a917d..019a28db063 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.vercel.tsx @@ -1,5 +1,5 @@ import { getFormProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { CheckCircleIcon, ExclamationTriangleIcon } from "@heroicons/react/20/solid"; import { DialogClose } from "@radix-ui/react-dialog"; import { Form, useActionData, useFetcher, useLocation, useNavigation } from "@remix-run/react"; diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route.tsx b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route.tsx index e1ed1b2cc70..cf3940fdfe5 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route.tsx +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.waitpoints.$waitpointFriendlyId.complete/route.tsx @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { Form, useLocation, useNavigation, useSubmit } from "@remix-run/react"; import { type ActionFunctionArgs, json } from "@remix-run/server-runtime"; import type { WaitpointTokenStatus } from "@trigger.dev/core/v3"; diff --git a/apps/webapp/app/routes/resources.orgs.$organizationSlug.schedules-addon.ts b/apps/webapp/app/routes/resources.orgs.$organizationSlug.schedules-addon.ts index d61bea07045..88f27b4d47c 100644 --- a/apps/webapp/app/routes/resources.orgs.$organizationSlug.schedules-addon.ts +++ b/apps/webapp/app/routes/resources.orgs.$organizationSlug.schedules-addon.ts @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { type ActionFunctionArgs, json } from "@remix-run/server-runtime"; import { tryCatch } from "@trigger.dev/core/v3"; import { z } from "zod"; diff --git a/apps/webapp/app/routes/resources.sessions.$sessionParam.close.ts b/apps/webapp/app/routes/resources.sessions.$sessionParam.close.ts index 26f573399c9..b0f820fccc7 100644 --- a/apps/webapp/app/routes/resources.sessions.$sessionParam.close.ts +++ b/apps/webapp/app/routes/resources.sessions.$sessionParam.close.ts @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { type ActionFunction, json } from "@remix-run/node"; import { z } from "zod"; import { $replica, prisma } from "~/db.server"; diff --git a/apps/webapp/app/routes/resources.taskruns.$runParam.cancel.ts b/apps/webapp/app/routes/resources.taskruns.$runParam.cancel.ts index cf6b0317780..7610f2c099c 100644 --- a/apps/webapp/app/routes/resources.taskruns.$runParam.cancel.ts +++ b/apps/webapp/app/routes/resources.taskruns.$runParam.cancel.ts @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { json } from "@remix-run/node"; import { z } from "zod"; import { prisma } from "~/db.server"; diff --git a/apps/webapp/app/routes/resources.taskruns.$runParam.replay.ts b/apps/webapp/app/routes/resources.taskruns.$runParam.replay.ts index e91ac2c5451..73e45b94d5d 100644 --- a/apps/webapp/app/routes/resources.taskruns.$runParam.replay.ts +++ b/apps/webapp/app/routes/resources.taskruns.$runParam.replay.ts @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { json, type LoaderFunctionArgs } from "@remix-run/node"; import { type EnvironmentType, prettyPrintPacket } from "@trigger.dev/core/v3"; import { type Prisma } from "@trigger.dev/database"; diff --git a/apps/webapp/app/services/apiAuth.server.ts b/apps/webapp/app/services/apiAuth.server.ts index 90fc9d2d3b7..8f2d4abad8e 100644 --- a/apps/webapp/app/services/apiAuth.server.ts +++ b/apps/webapp/app/services/apiAuth.server.ts @@ -768,7 +768,7 @@ export async function validateJWTTokenAndRenew( return; } - const renewedJwt = await renewJWTToken(payload.data); + const renewedJwt = await renewJWTToken(payload.data as Record); return { payload: payload.data, @@ -807,7 +807,7 @@ export async function validateJWTTokenAndRenew( } const renewedJwt = await generateJWTTokenForEnvironment(authenticatedEnv.environment, { - ...payload.data, + ...(payload.data as Record), }); // The environment carries secret material; log only non-secret fields. diff --git a/apps/webapp/app/services/authorizationRateLimitMiddleware.server.ts b/apps/webapp/app/services/authorizationRateLimitMiddleware.server.ts index 5fcd6eb5450..349c8cf0d7e 100644 --- a/apps/webapp/app/services/authorizationRateLimitMiddleware.server.ts +++ b/apps/webapp/app/services/authorizationRateLimitMiddleware.server.ts @@ -118,7 +118,7 @@ async function resolveLimitConfig( if (!parsedOverride.success) { logger.error("Error parsing rate limiter override", { override, - errors: parsedOverride.error.errors, + errors: parsedOverride.error.issues, }); return defaultLimiter; diff --git a/apps/webapp/app/services/queryService.server.ts b/apps/webapp/app/services/queryService.server.ts index 755da8ec3a3..33eb60e3cc8 100644 --- a/apps/webapp/app/services/queryService.server.ts +++ b/apps/webapp/app/services/queryService.server.ts @@ -381,7 +381,7 @@ export async function executeQuery( const result = await executeTSQL(queryClickhouse.reader, { ...baseOptions, - schema: z.record(z.any()), + schema: z.record(z.string(), z.any()), tableSchema: effectiveSchemas, transformValues: true, enforcedWhereClause, diff --git a/apps/webapp/app/services/routeBuilders/apiBuilder.server.ts b/apps/webapp/app/services/routeBuilders/apiBuilder.server.ts index 83212a6e8b2..fe01c3504e6 100644 --- a/apps/webapp/app/services/routeBuilders/apiBuilder.server.ts +++ b/apps/webapp/app/services/routeBuilders/apiBuilder.server.ts @@ -127,7 +127,7 @@ async function rejectRestrictedKeyWithoutAuthorization({ ); } -type AnyZodSchema = z.ZodFirstPartySchemaTypes | z.ZodDiscriminatedUnion; +type AnyZodSchema = z.ZodType; // A multi-resource auth check has two possible directions, and route authors // have to pick one explicitly: diff --git a/apps/webapp/app/services/routeBuilders/dashboardBuilder.server.ts b/apps/webapp/app/services/routeBuilders/dashboardBuilder.server.ts index 01bdb6d9b53..42d8acd4bf2 100644 --- a/apps/webapp/app/services/routeBuilders/dashboardBuilder.server.ts +++ b/apps/webapp/app/services/routeBuilders/dashboardBuilder.server.ts @@ -12,7 +12,7 @@ import type { AuthorizationOption, DashboardLoaderOptions, SessionUser } from ". import { fromZodError } from "zod-validation-error"; import type { z } from "zod"; -type AnyZodSchema = z.ZodFirstPartySchemaTypes | z.ZodDiscriminatedUnion; +type AnyZodSchema = z.ZodType; function loginRedirectFor(request: Request, override?: string): Response { if (override) return redirect(override); diff --git a/apps/webapp/app/services/routeBuilders/dashboardBuilder.ts b/apps/webapp/app/services/routeBuilders/dashboardBuilder.ts index a4603895915..b2b8d72f10b 100644 --- a/apps/webapp/app/services/routeBuilders/dashboardBuilder.ts +++ b/apps/webapp/app/services/routeBuilders/dashboardBuilder.ts @@ -17,7 +17,7 @@ import type { ActionFunctionArgs, LoaderFunctionArgs } from "@remix-run/server-r import type { RbacAbility, RbacResource } from "@trigger.dev/rbac"; import type { z } from "zod"; -type AnyZodSchema = z.ZodFirstPartySchemaTypes | z.ZodDiscriminatedUnion; +type AnyZodSchema = z.ZodType; type InferZod = T extends z.ZodFirstPartySchemaTypes | z.ZodDiscriminatedUnion ? z.infer diff --git a/apps/webapp/app/utils/json.ts b/apps/webapp/app/utils/json.ts index b3b44055958..e32960a08af 100644 --- a/apps/webapp/app/utils/json.ts +++ b/apps/webapp/app/utils/json.ts @@ -15,7 +15,7 @@ export function safeJsonParse(json?: string): unknown { export function safeJsonZodParse( schema: z.Schema, json: string -): z.SafeParseReturnType | undefined { +): z.ZodSafeParseResult | undefined { const parsed = safeJsonParse(json); if (parsed === null) { @@ -51,7 +51,7 @@ export async function safeBodyFromResponse( export async function safeParseBodyFromResponse( response: Response, schema: z.Schema -): Promise | undefined> { +): Promise | undefined> { try { const unknownJson = await response.json(); diff --git a/apps/webapp/app/utils/timeGranularity.ts b/apps/webapp/app/utils/timeGranularity.ts index 0c8ecc41ac8..1cfc6f80361 100644 --- a/apps/webapp/app/utils/timeGranularity.ts +++ b/apps/webapp/app/utils/timeGranularity.ts @@ -1,13 +1,15 @@ import { z } from "zod"; import parseDuration from "parse-duration"; -const DurationString = z.string().refine( - (val) => { - const ms = parseDuration(val); - return ms !== null && ms > 0; - }, - (val) => ({ message: `Invalid or non-positive duration string: "${val}"` }) -); +const DurationString = z + .string() + .refine( + (val) => { + const ms = parseDuration(val); + return ms !== null && ms > 0; + }, + { error: (issue) => `Invalid or non-positive duration string: "${issue.input}"` } + ); const BracketSchema = z.object({ max: z.union([z.literal("Infinity"), DurationString]), diff --git a/apps/webapp/app/v3/featureFlags.server.ts b/apps/webapp/app/v3/featureFlags.server.ts index c79bd8c01d7..1a435a48e52 100644 --- a/apps/webapp/app/v3/featureFlags.server.ts +++ b/apps/webapp/app/v3/featureFlags.server.ts @@ -37,7 +37,7 @@ export function makeFlag(_prisma: PrismaClientOrTransaction = prisma) { const parsed = flagSchema.safeParse(opts.overrides[opts.key]); if (parsed.success) { - return parsed.data; + return parsed.data as z.infer<(typeof FeatureFlagCatalog)[T]>; } } @@ -45,7 +45,7 @@ export function makeFlag(_prisma: PrismaClientOrTransaction = prisma) { const parsed = flagSchema.safeParse(value.value); if (parsed.success) { - return parsed.data; + return parsed.data as z.infer<(typeof FeatureFlagCatalog)[T]>; } } diff --git a/apps/webapp/app/v3/featureFlags.ts b/apps/webapp/app/v3/featureFlags.ts index f617cab16f3..d6ba1fb191f 100644 --- a/apps/webapp/app/v3/featureFlags.ts +++ b/apps/webapp/app/v3/featureFlags.ts @@ -112,8 +112,10 @@ export type FeatureFlagCatalog = z.infer; export function validateFeatureFlagValue( key: T, value: unknown -): z.SafeParseReturnType> { - return FeatureFlagCatalog[key].safeParse(value); +): ReturnType<(typeof FeatureFlagCatalog)[T]["safeParse"]> { + return FeatureFlagCatalog[key].safeParse(value) as ReturnType< + (typeof FeatureFlagCatalog)[T]["safeParse"] + >; } // Utility function to validate all feature flags at once @@ -162,23 +164,28 @@ export type FlagControlType = | { type: "number"; min?: number; max?: number } | { type: "string" }; -export function getFlagControlType(schema: z.ZodTypeAny): FlagControlType { - const typeName = schema._def.typeName; +export function getFlagControlType(schema: z.ZodType): FlagControlType { + // zod v4: schema.def.type is a lowercase tag ("boolean"/"enum"/"number"/...). + const def = schema.def as { + type: string; + checks?: Array<{ _zod?: { def?: { check?: string; value?: number } } }>; + }; - if (typeName === "ZodBoolean") { + if (def.type === "boolean") { return { type: "boolean" }; } - if (typeName === "ZodEnum") { - return { type: "enum", options: schema._def.values as string[] }; + if (def.type === "enum") { + return { type: "enum", options: (schema as z.ZodEnum).options as string[] }; } - // z.coerce.number() reports as ZodNumber; pull min/max out of its checks - // so the UI can render a constrained number input instead of free text. - if (typeName === "ZodNumber") { - const checks = (schema._def.checks ?? []) as Array<{ kind: string; value?: number }>; - const min = checks.find((c) => c.kind === "min")?.value; - const max = checks.find((c) => c.kind === "max")?.value; + // z.coerce.number() reports as "number"; pull min/max out of its v4 checks + // (each check carries `_zod.def.{check,value}`) so the UI can render a + // constrained number input instead of free text. + if (def.type === "number") { + const checks = def.checks ?? []; + const min = checks.find((c) => c._zod?.def?.check === "greater_than")?._zod?.def?.value; + const max = checks.find((c) => c._zod?.def?.check === "less_than")?._zod?.def?.value; return { type: "number", min, max }; } diff --git a/apps/webapp/app/v3/schedules.ts b/apps/webapp/app/v3/schedules.ts index 1653e053544..e0d33c77654 100644 --- a/apps/webapp/app/v3/schedules.ts +++ b/apps/webapp/app/v3/schedules.ts @@ -20,28 +20,25 @@ export const CronPattern = z.string().refine( return false; } }, - (val) => { - const parts = val.split(" "); - if (parts.length > 5) { - return { - message: "CRON expressions with seconds are not allowed", - }; - } + { + error: (issue) => { + const val = String(issue.input); + const parts = val.split(" "); + if (parts.length > 5) { + return "CRON expressions with seconds are not allowed"; + } - if (val === "") { - return { - message: "CRON expression is required", - }; - } + if (val === "") { + return "CRON expression is required"; + } - try { - parseExpression(val); - return { - message: "Unknown problem", - }; - } catch (e) { - return { message: e instanceof Error ? e.message : JSON.stringify(e) }; - } + try { + parseExpression(val); + return "Unknown problem"; + } catch (e) { + return e instanceof Error ? e.message : JSON.stringify(e); + } + }, } ); diff --git a/apps/webapp/app/v3/services/aiRunFilterService.server.ts b/apps/webapp/app/v3/services/aiRunFilterService.server.ts index 96b74ab3314..c3bc2b10e8b 100644 --- a/apps/webapp/app/v3/services/aiRunFilterService.server.ts +++ b/apps/webapp/app/v3/services/aiRunFilterService.server.ts @@ -258,7 +258,7 @@ export class AIRunFilterService { const validationResult = AIFilters.safeParse(output.filters); if (!validationResult.success) { logger.error("AI filter validation failed", { - errors: validationResult.error.errors, + errors: validationResult.error.issues, filters: output.filters, }); diff --git a/apps/webapp/app/v3/services/replayTaskRun.server.ts b/apps/webapp/app/v3/services/replayTaskRun.server.ts index d626ec2ae8c..8e71026252c 100644 --- a/apps/webapp/app/v3/services/replayTaskRun.server.ts +++ b/apps/webapp/app/v3/services/replayTaskRun.server.ts @@ -20,7 +20,9 @@ type OverrideOptions = { metadata?: unknown; bulkActionId?: string; triggerSource?: string; -} & RunOptionsData; + // zod v4 types `.optional().transform()` fields as required-with-undefined in the + // output; Partial keeps them omittable for callers passing a subset of overrides. +} & Partial; export class ReplayTaskRunService extends BaseService { public async call(existingTaskRun: TaskRun, overrideOptions: OverrideOptions = {}) { diff --git a/apps/webapp/app/v3/utils/zodPubSub.server.ts b/apps/webapp/app/v3/utils/zodPubSub.server.ts index c473443d169..6b1132a8715 100644 --- a/apps/webapp/app/v3/utils/zodPubSub.server.ts +++ b/apps/webapp/app/v3/utils/zodPubSub.server.ts @@ -54,7 +54,7 @@ class RedisZodSubscriber< eventName: K, listener: (payload: z.infer) => Promise ): void { - this._listeners.set(eventName as string, listener); + this._listeners.set(eventName as string, listener as Parameters[1]); } public async stopListening(): Promise { diff --git a/apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts b/apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts index 1399b87fc2b..cb55583c518 100644 --- a/apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts +++ b/apps/webapp/app/v3/vercel/vercelProjectIntegrationSchema.ts @@ -71,7 +71,7 @@ export type TriggerEnvironmentType = z.infer; * Only explicitly `false` entries disable sync. */ export const SyncEnvVarsMappingSchema = z - .record(EnvSlugSchema, z.record(z.string(), z.boolean())) + .partialRecord(EnvSlugSchema, z.record(z.string(), z.boolean())) .default({}); export type SyncEnvVarsMapping = z.infer; diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 90dc46ac088..21a28f7a54e 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -216,9 +216,9 @@ "util": "^0.12.5", "uuid": "^14.0.0", "ws": "^8.11.0", - "zod": "3.25.76", + "zod": "4.4.3", "zod-error": "1.5.0", - "zod-validation-error": "^1.5.0" + "zod-validation-error": "^5.0.0" }, "devDependencies": { "@internal/clickhouse": "workspace:*", diff --git a/internal-packages/clickhouse/package.json b/internal-packages/clickhouse/package.json index ec5ec25e667..c3f87cf62dc 100644 --- a/internal-packages/clickhouse/package.json +++ b/internal-packages/clickhouse/package.json @@ -10,7 +10,7 @@ "@internal/tracing": "workspace:*", "@internal/tsql": "workspace:*", "@trigger.dev/core": "workspace:*", - "zod": "3.25.76", + "zod": "4.4.3", "zod-error": "1.5.0" }, "devDependencies": { diff --git a/internal-packages/clickhouse/src/client/tsql.ts b/internal-packages/clickhouse/src/client/tsql.ts index f61009237e4..99e6e402bc3 100644 --- a/internal-packages/clickhouse/src/client/tsql.ts +++ b/internal-packages/clickhouse/src/client/tsql.ts @@ -221,7 +221,7 @@ export async function executeTSQL( const queryFn = reader.queryWithStats({ name: isExplain ? `${options.name}-explain` : options.name, query: queryToExecute, - params: z.record(z.any()), + params: z.record(z.string(), z.any()), // EXPLAIN returns rows with an 'explain' column schema: isExplain ? z.object({ explain: z.string() }) : options.schema, settings: options.clickhouseSettings, @@ -257,7 +257,7 @@ export async function executeTSQL( const additionalQueryFn = reader.queryWithStats({ name: `${options.name}-explain-${explainType.name.toLowerCase()}`, query: explainType.query, - params: z.record(z.any()), + params: z.record(z.string(), z.any()), schema: z.object({ explain: z.string() }), settings: options.clickhouseSettings, logFields: { tsql: options.query }, diff --git a/internal-packages/clickhouse/src/tsqlFunctions.test.ts b/internal-packages/clickhouse/src/tsqlFunctions.test.ts index 8c1f9ec4221..188fd58e875 100644 --- a/internal-packages/clickhouse/src/tsqlFunctions.test.ts +++ b/internal-packages/clickhouse/src/tsqlFunctions.test.ts @@ -101,7 +101,7 @@ async function assertQueryExecutes(client: ClickhouseClient, tsqlQuery: string): const [error] = await executeTSQL(client, { name: "func-test", query: tsqlQuery, - schema: z.record(z.any()), + schema: z.record(z.string(), z.any()), enforcedWhereClause, tableSchema: [taskRunsSchema], }); diff --git a/internal-packages/compute/package.json b/internal-packages/compute/package.json index 4e221879382..c5045878f54 100644 --- a/internal-packages/compute/package.json +++ b/internal-packages/compute/package.json @@ -14,7 +14,7 @@ } }, "dependencies": { - "zod": "3.25.76" + "zod": "4.4.3" }, "devDependencies": { "rimraf": "6.0.1" diff --git a/internal-packages/compute/src/types.ts b/internal-packages/compute/src/types.ts index 5d8d5e26af9..fd878334efa 100644 --- a/internal-packages/compute/src/types.ts +++ b/internal-packages/compute/src/types.ts @@ -15,7 +15,7 @@ export const TemplateCreateRequestSchema = z.object({ callback: z .object({ url: z.string(), - metadata: z.record(z.string()).optional(), + metadata: z.record(z.string(), z.string()).optional(), }) .optional(), }); @@ -38,14 +38,14 @@ export type TemplateCreateResponse = z.infer; @@ -58,7 +58,7 @@ export type InstanceCreateResponse = z.infer; @@ -67,13 +67,13 @@ export type InstanceSnapshotRequest = z.infer; @@ -82,14 +82,14 @@ export const SnapshotCallbackPayloadSchema = z.discriminatedUnion("status", [ status: z.literal("completed"), snapshot_id: z.string(), instance_id: z.string(), - metadata: z.record(z.string()).optional(), + metadata: z.record(z.string(), z.string()).optional(), duration_ms: z.number().optional(), }), z.object({ status: z.literal("failed"), instance_id: z.string(), error: z.string().optional(), - metadata: z.record(z.string()).optional(), + metadata: z.record(z.string(), z.string()).optional(), duration_ms: z.number().optional(), }), ]); diff --git a/internal-packages/emails/package.json b/internal-packages/emails/package.json index 33dfe4c1d71..9f61faab457 100644 --- a/internal-packages/emails/package.json +++ b/internal-packages/emails/package.json @@ -18,7 +18,7 @@ "react-dom": "^18.2.0", "resend": "^3.2.0", "tiny-invariant": "^1.2.0", - "zod": "3.25.76" + "zod": "4.4.3" }, "devDependencies": { "@types/nodemailer": "^8.0.0", diff --git a/internal-packages/run-engine/package.json b/internal-packages/run-engine/package.json index 516e6a18696..026463ce0aa 100644 --- a/internal-packages/run-engine/package.json +++ b/internal-packages/run-engine/package.json @@ -32,7 +32,7 @@ "nanoid": "3.3.8", "redlock": "5.0.0-beta.2", "seedrandom": "^3.0.5", - "zod": "3.25.76", + "zod": "4.4.3", "p-map": "^6.0.0" }, "devDependencies": { diff --git a/internal-packages/run-engine/src/batch-queue/types.ts b/internal-packages/run-engine/src/batch-queue/types.ts index 9aa085e286a..0a8494abd00 100644 --- a/internal-packages/run-engine/src/batch-queue/types.ts +++ b/internal-packages/run-engine/src/batch-queue/types.ts @@ -37,7 +37,7 @@ export const BatchItem = z.object({ */ payloadType: z.string().optional(), /** Options for this specific item - stored as JSON */ - options: z.record(z.unknown()).optional(), + options: z.record(z.string(), z.unknown()).optional(), }); export type BatchItem = z.infer; @@ -68,7 +68,7 @@ export const BatchMeta = z.object({ /** Trigger version */ triggerVersion: z.string().optional(), /** Trace context */ - traceContext: z.record(z.unknown()).optional(), + traceContext: z.record(z.string(), z.unknown()).optional(), /** Whether span parent should be a link */ spanParentAsLink: z.boolean().optional(), /** Realtime streams version */ @@ -103,7 +103,7 @@ export const BatchItemFailure = z.object({ */ payload: z.string().optional(), /** The options that were used */ - options: z.record(z.unknown()).optional(), + options: z.record(z.string(), z.unknown()).optional(), /** Error message */ error: z.string(), /** Error code if available */ diff --git a/internal-packages/schedule-engine/package.json b/internal-packages/schedule-engine/package.json index 52a27a11e27..b8a53a75f6c 100644 --- a/internal-packages/schedule-engine/package.json +++ b/internal-packages/schedule-engine/package.json @@ -22,7 +22,7 @@ "cron-parser": "^4.9.0", "cronstrue": "^2.50.0", "nanoid": "3.3.8", - "zod": "3.25.76" + "zod": "4.4.3" }, "devDependencies": { "@internal/testcontainers": "workspace:*", diff --git a/internal-packages/tsql/package.json b/internal-packages/tsql/package.json index 0cac36e7b27..3c86630738f 100644 --- a/internal-packages/tsql/package.json +++ b/internal-packages/tsql/package.json @@ -8,7 +8,7 @@ "dependencies": { "@trigger.dev/core": "workspace:*", "antlr4ts": "0.5.0-alpha.4", - "zod": "3.25.76" + "zod": "4.4.3" }, "scripts": { "typecheck": "tsc --noEmit", diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index 6d0d90da98f..a486a90013e 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -68,7 +68,8 @@ "rimraf": "^6.0.1", "ts-essentials": "10.0.1", "tshy": "^3.0.2", - "tsx": "4.17.0" + "tsx": "4.17.0", + "zod": "4.4.3" }, "scripts": { "clean": "rimraf dist .tshy .tshy-build .turbo", @@ -146,8 +147,7 @@ "tinyglobby": "^0.2.10", "ws": "^8.18.0", "xdg-app-paths": "^8.3.0", - "zod": "3.25.76", - "zod-validation-error": "^1.5.0" + "zod-validation-error": "^5.0.0" }, "engines": { "node": ">=18.20.0" @@ -160,5 +160,8 @@ "default": "./dist/esm/index.js" } } + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" } } diff --git a/packages/cli-v3/src/cli/common.ts b/packages/cli-v3/src/cli/common.ts index 18d61a05803..dbb706e06c6 100644 --- a/packages/cli-v3/src/cli/common.ts +++ b/packages/cli-v3/src/cli/common.ts @@ -42,7 +42,7 @@ export async function handleTelemetry(action: () => Promise) { } } -export async function wrapCommandAction( +export async function wrapCommandAction, TResult>( name: string, schema: T, options: unknown, @@ -55,7 +55,7 @@ export async function wrapCommandAction( throw new Error(fromZodError(parsedOptions.error).toString()); } - logger.loggerLevel = parsedOptions.data.logLevel; + logger.loggerLevel = parsedOptions.data.logLevel as typeof logger.loggerLevel; logger.debug(`Running "${name}" with the following options`, { options: options, diff --git a/packages/cli-v3/src/commands/analyze.ts b/packages/cli-v3/src/commands/analyze.ts index dc03d36e2e5..e52112a53bb 100644 --- a/packages/cli-v3/src/commands/analyze.ts +++ b/packages/cli-v3/src/commands/analyze.ts @@ -113,14 +113,14 @@ const ImportSchema = z.object({ kind: ImportKind, external: z.boolean().optional(), original: z.string().optional(), - with: z.record(z.string()).optional(), + with: z.record(z.string(), z.string()).optional(), }); const InputSchema = z.object({ bytes: z.number(), imports: z.array(ImportSchema), format: z.enum(["cjs", "esm"]).optional(), - with: z.record(z.string()).optional(), + with: z.record(z.string(), z.string()).optional(), }); const OutputImportSchema = z.object({ diff --git a/packages/cli-v3/src/mcp/tools/agentChat.ts b/packages/cli-v3/src/mcp/tools/agentChat.ts index 992e3d436a1..3f5b390c046 100644 --- a/packages/cli-v3/src/mcp/tools/agentChat.ts +++ b/packages/cli-v3/src/mcp/tools/agentChat.ts @@ -70,7 +70,7 @@ const StartAgentChatInput = CommonProjectsInput.extend({ .describe("A unique conversation ID. Reuse to resume a conversation.") .optional(), clientData: z - .record(z.unknown()) + .record(z.string(), z.unknown()) .describe("Client data to include with every message (e.g. userId, model).") .optional(), preload: z diff --git a/packages/cli-v3/src/utilities/configFiles.ts b/packages/cli-v3/src/utilities/configFiles.ts index 4fee41d9cc4..541a58a68ad 100644 --- a/packages/cli-v3/src/utilities/configFiles.ts +++ b/packages/cli-v3/src/utilities/configFiles.ts @@ -22,13 +22,13 @@ const CliConfigProfileSettings = z.object({ }); type CliConfigProfileSettings = z.infer; -const OldCliConfigFile = z.record(CliConfigProfileSettings); +const OldCliConfigFile = z.record(z.string(), CliConfigProfileSettings); type OldCliConfigFile = z.infer; const CliConfigFile = z.object({ version: z.literal(2), currentProfile: z.string().default(DEFFAULT_PROFILE), - profiles: z.record(CliConfigProfileSettings), + profiles: z.record(z.string(), CliConfigProfileSettings), settings: z .object({ hasSeenMCPInstallPrompt: z.boolean().optional(), diff --git a/packages/core/package.json b/packages/core/package.json index 292d4ff421d..1003f7514b8 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -225,9 +225,8 @@ "std-env": "^3.8.1", "tinyexec": "^0.3.2", "uncrypto": "^0.1.3", - "zod": "3.25.76", "zod-error": "1.5.0", - "zod-validation-error": "^1.5.0" + "zod-validation-error": "^5.0.0" }, "devDependencies": { "@ai-sdk/provider-utils": "^1.0.22", @@ -245,7 +244,8 @@ "superjson": "^2.2.1", "ts-essentials": "10.0.1", "tshy": "^3.0.2", - "tsx": "4.17.0" + "tsx": "4.17.0", + "zod": "4.4.3" }, "engines": { "node": ">=18.20.0" @@ -663,5 +663,8 @@ "type": "module", "main": "./dist/commonjs/index.js", "types": "./dist/commonjs/index.d.ts", - "module": "./dist/esm/index.js" + "module": "./dist/esm/index.js", + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" + } } diff --git a/packages/core/src/schemas/eventFilter.ts b/packages/core/src/schemas/eventFilter.ts index 66b4d645466..25cf03b45e7 100644 --- a/packages/core/src/schemas/eventFilter.ts +++ b/packages/core/src/schemas/eventFilter.ts @@ -65,7 +65,7 @@ type EventMatcher = z.infer; export type EventFilter = { [key: string]: EventMatcher | EventFilter }; export const EventFilterSchema: z.ZodType = z.lazy(() => - z.record(z.union([EventMatcherSchema, EventFilterSchema])) + z.record(z.string(), z.union([EventMatcherSchema, EventFilterSchema])) ); export const EventRuleSchema = z.object({ diff --git a/packages/core/src/schemas/json.ts b/packages/core/src/schemas/json.ts index 79384db156a..32dd506c8ff 100644 --- a/packages/core/src/schemas/json.ts +++ b/packages/core/src/schemas/json.ts @@ -6,7 +6,7 @@ type Literal = z.infer; export type DeserializedJson = Literal | { [key: string]: DeserializedJson } | DeserializedJson[]; export const DeserializedJsonSchema: z.ZodType = z.lazy(() => - z.union([LiteralSchema, z.array(DeserializedJsonSchema), z.record(DeserializedJsonSchema)]) + z.union([LiteralSchema, z.array(DeserializedJsonSchema), z.record(z.string(), DeserializedJsonSchema)]) ); const SerializableSchema = z.union([ @@ -26,5 +26,5 @@ export type SerializableJson = | SerializableJson[]; export const SerializableJsonSchema: z.ZodType = z.lazy(() => - z.union([SerializableSchema, z.array(SerializableJsonSchema), z.record(SerializableJsonSchema)]) + z.union([SerializableSchema, z.array(SerializableJsonSchema), z.record(z.string(), SerializableJsonSchema)]) ); diff --git a/packages/core/src/v3/apiClient/index.ts b/packages/core/src/v3/apiClient/index.ts index 23f13f37573..759f6943971 100644 --- a/packages/core/src/v3/apiClient/index.ts +++ b/packages/core/src/v3/apiClient/index.ts @@ -1903,7 +1903,7 @@ export class ApiClient { async generateJWTClaims(requestOptions?: ZodFetchOptions): Promise> { return zodfetch( - z.record(z.any()), + z.record(z.string(), z.any()), `${this.baseUrl}/api/v1/auth/jwt/claims`, { method: "POST", diff --git a/packages/core/src/v3/runEngineWorker/supervisor/schemas.ts b/packages/core/src/v3/runEngineWorker/supervisor/schemas.ts index e5360c6ed6b..778bdb395b7 100644 --- a/packages/core/src/v3/runEngineWorker/supervisor/schemas.ts +++ b/packages/core/src/v3/runEngineWorker/supervisor/schemas.ts @@ -51,7 +51,7 @@ export type WorkerApiContinueRunExecutionRequestBody = z.infer< >; export const WorkerApiConnectRequestBody = z.object({ - metadata: z.record(z.any()), + metadata: z.record(z.string(), z.any()), }); export type WorkerApiConnectRequestBody = z.infer; @@ -104,7 +104,7 @@ export type WorkerApiRunAttemptStartRequestBody = z.infer< export const WorkerApiRunAttemptStartResponseBody = StartRunAttemptResult.and( z.object({ - envVars: z.record(z.string()), + envVars: z.record(z.string(), z.string()), }) ); export type WorkerApiRunAttemptStartResponseBody = z.infer< diff --git a/packages/core/src/v3/schemas/api.ts b/packages/core/src/v3/schemas/api.ts index 3f6ba0aeced..245351bf0c5 100644 --- a/packages/core/src/v3/schemas/api.ts +++ b/packages/core/src/v3/schemas/api.ts @@ -203,7 +203,7 @@ const ConcurrencyKeySchema = z.union([z.string(), z.number()]).transform((value) export const TriggerTaskRequestBody = z .object({ payload: z.any(), - context: z.any(), + context: z.any().optional(), options: z .object({ /** @deprecated engine v1 only */ @@ -254,7 +254,7 @@ export const TriggerTaskRequestBody = z machine: MachinePresetName.optional(), maxAttempts: z.number().int().optional(), maxDuration: z.number().optional(), - metadata: z.any(), + metadata: z.any().optional(), metadataType: z.string().optional(), payloadType: z.string().optional(), /** @@ -313,7 +313,7 @@ export type BatchTriggerTaskRequestBody = z.infer; * * `options` reuses the strict shape from BatchTriggerTaskItem so that the * Phase-2 streaming path validates option fields identically to the V2/V3 - * batch trigger endpoints — historically this used z.record(z.unknown()) and + * batch trigger endpoints — historically this used z.record(z.string(), z.unknown()) and * let invalid values (e.g. numeric concurrencyKey) reach Prisma. */ export const BatchItemNDJSON = z.object({ @@ -544,7 +544,7 @@ export const RescheduleRunRequestBody = z.object({ export type RescheduleRunRequestBody = z.infer; export const GetEnvironmentVariablesResponseBody = z.object({ - variables: z.record(z.string()), + variables: z.record(z.string(), z.string()), }); export type GetEnvironmentVariablesResponseBody = z.infer< @@ -658,7 +658,7 @@ export type CreateArtifactRequestBody = z.infer; export const ImportEnvironmentVariablesRequestBody = z.object({ - variables: z.record(z.string()), - parentVariables: z.record(z.string()).optional(), + variables: z.record(z.string(), z.string()), + parentVariables: z.record(z.string(), z.string()).optional(), override: z.boolean().optional(), // When omitted, variables default to non-secret (the DB default is false). isSecret: z.boolean().optional(), @@ -1443,7 +1443,7 @@ export const UpdateMetadataRequestBody = FlushedRunMetadata; export type UpdateMetadataRequestBody = z.infer; export const UpdateMetadataResponseBody = z.object({ - metadata: z.record(DeserializedJsonSchema), + metadata: z.record(z.string(), DeserializedJsonSchema), }); export type UpdateMetadataResponseBody = z.infer; @@ -1715,7 +1715,7 @@ export type CompleteWaitpointTokenRequestBody = z.infer; export const ResolvePromptRequestBody = z.object({ - variables: z.record(z.unknown()).default({}), + variables: z.record(z.string(), z.unknown()).default({}), label: z.string().optional(), version: z.number().optional(), }); @@ -2225,7 +2225,7 @@ export const ResolvePromptResponseBody = z.object({ template: z.string().optional(), text: z.string().optional(), model: z.string().optional().nullable(), - config: z.record(z.unknown()).optional().nullable(), + config: z.record(z.string(), z.unknown()).optional().nullable(), }), }); export type ResolvePromptResponseBody = z.infer; diff --git a/packages/core/src/v3/schemas/build.ts b/packages/core/src/v3/schemas/build.ts index 52cae0b2385..366db9ab48f 100644 --- a/packages/core/src/v3/schemas/build.ts +++ b/packages/core/src/v3/schemas/build.ts @@ -41,6 +41,7 @@ export const BuildManifest = z.object({ config: ConfigManifest, files: z.array(TaskFile), sources: z.record( + z.string(), z.object({ contents: z.string(), contentHash: z.string(), @@ -56,18 +57,18 @@ export const BuildManifest = z.object({ configPath: z.string(), externals: BuildExternal.array().optional(), build: z.object({ - env: z.record(z.string()).optional(), + env: z.record(z.string(), z.string()).optional(), commands: z.array(z.string()).optional(), }), customConditions: z.array(z.string()).optional(), deploy: z.object({ - env: z.record(z.string()).optional(), + env: z.record(z.string(), z.string()).optional(), sync: z .object({ - env: z.record(z.string()).optional(), - parentEnv: z.record(z.string()).optional(), - secretEnv: z.record(z.string()).optional(), - secretParentEnv: z.record(z.string()).optional(), + env: z.record(z.string(), z.string()).optional(), + parentEnv: z.record(z.string(), z.string()).optional(), + secretEnv: z.record(z.string(), z.string()).optional(), + secretParentEnv: z.record(z.string(), z.string()).optional(), }) .optional(), }), @@ -84,7 +85,7 @@ export const BuildManifest = z.object({ }) .optional(), /** Maps output file paths to their content hashes for deduplication during dev */ - outputHashes: z.record(z.string()).optional(), + outputHashes: z.record(z.string(), z.string()).optional(), /** Skills discovered and bundled into `.trigger/skills/{id}/` under `outputPath`. */ skills: SkillManifest.array().optional(), }); @@ -113,7 +114,7 @@ export const WorkerManifest = z.object({ runtime: BuildRuntime, runtimeVersion: z.string().optional(), customConditions: z.array(z.string()).optional(), - timings: z.record(z.number()).optional(), + timings: z.record(z.string(), z.number()).optional(), processKeepAlive: z .object({ enabled: z.boolean(), diff --git a/packages/core/src/v3/schemas/common.ts b/packages/core/src/v3/schemas/common.ts index a6ec4b1002d..d5ec88883dc 100644 --- a/packages/core/src/v3/schemas/common.ts +++ b/packages/core/src/v3/schemas/common.ts @@ -25,7 +25,7 @@ const MetadataOperationKey = z.string().refine(isSafeMetadataKey, { export const RunMetadataUpdateOperation = z.object({ type: z.literal("update"), - value: z.record(z.unknown()), + value: z.record(z.string(), z.unknown()), }); export type RunMetadataUpdateOperation = z.infer; @@ -81,7 +81,7 @@ export const RunMetadataChangeOperation = z.discriminatedUnion("type", [ export type RunMetadataChangeOperation = z.infer; export const FlushedRunMetadata = z.object({ - metadata: z.record(DeserializedJsonSchema).optional(), + metadata: z.record(z.string(), DeserializedJsonSchema).optional(), operations: z.array(RunMetadataChangeOperation).optional(), parentOperations: z.array(RunMetadataChangeOperation).optional(), rootOperations: z.array(RunMetadataChangeOperation).optional(), @@ -244,7 +244,7 @@ export const TaskRun = z.object({ idempotencyKeyScope: z.enum(["run", "attempt", "global"]).optional(), maxAttempts: z.number().optional(), version: z.string().optional(), - metadata: z.record(DeserializedJsonSchema).optional(), + metadata: z.record(z.string(), DeserializedJsonSchema).optional(), maxDuration: z.number().optional(), /** The priority of the run. Wih a value of 10 it will be dequeued before runs that were triggered 9 seconds before it (assuming they had no priority set). */ priority: z.number().optional(), @@ -365,7 +365,7 @@ export const TaskRunExecution = z.object({ attempt: TaskRunExecutionAttempt.passthrough(), run: TaskRun.and( z.object({ - traceContext: z.record(z.unknown()).optional(), + traceContext: z.record(z.string(), z.unknown()).optional(), realtimeStreamsVersion: z.string().optional(), }) ), @@ -408,7 +408,7 @@ export const V3TaskRun = z.object({ idempotencyKeyScope: z.enum(["run", "attempt", "global"]).optional(), maxAttempts: z.number().optional(), version: z.string().optional(), - metadata: z.record(DeserializedJsonSchema).optional(), + metadata: z.record(z.string(), DeserializedJsonSchema).optional(), maxDuration: z.number().optional(), context: z.unknown(), durationMs: z.number(), @@ -423,7 +423,7 @@ export const V3TaskRunExecution = z.object({ attempt: V3TaskRunExecutionAttempt, run: V3TaskRun.and( z.object({ - traceContext: z.record(z.unknown()).optional(), + traceContext: z.record(z.string(), z.unknown()).optional(), }) ), queue: TaskRunExecutionQueue, diff --git a/packages/core/src/v3/schemas/eventFilter.ts b/packages/core/src/v3/schemas/eventFilter.ts index 6ddda362108..698ae49184f 100644 --- a/packages/core/src/v3/schemas/eventFilter.ts +++ b/packages/core/src/v3/schemas/eventFilter.ts @@ -65,5 +65,5 @@ type EventMatcher = z.infer; export type EventFilter = { [key: string]: EventMatcher | EventFilter }; export const EventFilter: z.ZodType = z.lazy(() => - z.record(z.union([EventMatcher, EventFilter])) + z.record(z.string(), z.union([EventMatcher, EventFilter])) ); diff --git a/packages/core/src/v3/schemas/messages.ts b/packages/core/src/v3/schemas/messages.ts index 1389fcddc40..65784ce4a6a 100644 --- a/packages/core/src/v3/schemas/messages.ts +++ b/packages/core/src/v3/schemas/messages.ts @@ -90,10 +90,10 @@ export const WorkerToExecutorMessageCatalog = { message: z.object({ version: z.literal("v1").default("v1"), execution: TaskRunExecution, - traceContext: z.record(z.unknown()), + traceContext: z.record(z.string(), z.unknown()), metadata: ServerBackgroundWorker, metrics: TaskRunExecutionMetrics.optional(), - env: z.record(z.string()).optional(), + env: z.record(z.string(), z.string()).optional(), isWarmStart: z.boolean().optional(), }), }, diff --git a/packages/core/src/v3/schemas/openTelemetry.ts b/packages/core/src/v3/schemas/openTelemetry.ts index 47d9d9a50d9..785805b94e5 100644 --- a/packages/core/src/v3/schemas/openTelemetry.ts +++ b/packages/core/src/v3/schemas/openTelemetry.ts @@ -43,7 +43,7 @@ export type AttemptFailedSpanEvent = z.infer; export const OtherSpanEvent = z.object({ name: z.string(), time: z.coerce.date(), - properties: z.record(z.unknown()), + properties: z.record(z.string(), z.unknown()), }); export type OtherSpanEvent = z.infer; diff --git a/packages/core/src/v3/schemas/query.ts b/packages/core/src/v3/schemas/query.ts index 027259e8b74..f5a6d8be902 100644 --- a/packages/core/src/v3/schemas/query.ts +++ b/packages/core/src/v3/schemas/query.ts @@ -21,7 +21,7 @@ export type QueryExecuteRequestBody = z.infer; */ export const QueryExecuteJSONResponseBody = z.object({ format: z.literal("json"), - results: z.array(z.record(z.any())), + results: z.array(z.record(z.string(), z.any())), }); export type QueryExecuteJSONResponseBody = z.infer; diff --git a/packages/core/src/v3/schemas/resources.ts b/packages/core/src/v3/schemas/resources.ts index 753324d1257..923c29355be 100644 --- a/packages/core/src/v3/schemas/resources.ts +++ b/packages/core/src/v3/schemas/resources.ts @@ -46,7 +46,7 @@ export const PromptResource = z.object({ /** Default model identifier */ model: z.string().optional(), /** Default model config (temperature, maxTokens, etc.) */ - config: z.record(z.unknown()).optional(), + config: z.record(z.string(), z.unknown()).optional(), /** JSONSchema7 for template variables */ variableSchema: z.unknown().optional(), }); diff --git a/packages/core/src/v3/schemas/runEngine.ts b/packages/core/src/v3/schemas/runEngine.ts index 92144364494..5d9be4b1aa8 100644 --- a/packages/core/src/v3/schemas/runEngine.ts +++ b/packages/core/src/v3/schemas/runEngine.ts @@ -288,7 +288,7 @@ export const DequeuedMessage = z.object({ machine: MachinePreset, attemptNumber: z.number(), masterQueue: z.string(), - traceContext: z.record(z.unknown()), + traceContext: z.record(z.string(), z.unknown()), annotations: RunAnnotations.optional(), }), environment: z.object({ diff --git a/packages/core/src/v3/schemas/schemas.ts b/packages/core/src/v3/schemas/schemas.ts index 0b12e7ae3db..9fd4470cc2d 100644 --- a/packages/core/src/v3/schemas/schemas.ts +++ b/packages/core/src/v3/schemas/schemas.ts @@ -31,8 +31,8 @@ export type TaskRunExecutionMetrics = z.infer; export const TaskRunExecutionPayload = z.object({ execution: TaskRunExecution, - traceContext: z.record(z.unknown()), - environment: z.record(z.string()).optional(), + traceContext: z.record(z.string(), z.unknown()), + environment: z.record(z.string(), z.string()).optional(), metrics: TaskRunExecutionMetrics.optional(), }); @@ -56,8 +56,8 @@ export type V3ProdTaskRunExecution = z.infer; export const V3ProdTaskRunExecutionPayload = z.object({ execution: V3ProdTaskRunExecution, - traceContext: z.record(z.unknown()), - environment: z.record(z.string()).optional(), + traceContext: z.record(z.string(), z.unknown()), + environment: z.record(z.string(), z.string()).optional(), metrics: TaskRunExecutionMetrics.optional(), }); @@ -231,7 +231,7 @@ const promptMetadata = { description: z.string().optional(), content: z.string().optional(), model: z.string().optional(), - config: z.record(z.unknown()).optional(), + config: z.record(z.string(), z.unknown()).optional(), variableSchema: z.unknown().optional(), }; @@ -320,8 +320,8 @@ export const TaskRunExecutionLazyAttemptPayload = z.object({ messageId: z.string(), isTest: z.boolean(), isReplay: z.boolean().default(false), - traceContext: z.record(z.unknown()), - environment: z.record(z.string()).optional(), + traceContext: z.record(z.string(), z.unknown()), + environment: z.record(z.string(), z.string()).optional(), metrics: TaskRunExecutionMetrics.optional(), }); diff --git a/packages/core/src/v3/serverOnly/httpServer.ts b/packages/core/src/v3/serverOnly/httpServer.ts index 8360067d583..1ada1c37ac8 100644 --- a/packages/core/src/v3/serverOnly/httpServer.ts +++ b/packages/core/src/v3/serverOnly/httpServer.ts @@ -302,13 +302,14 @@ export class HttpServer { return { success: true, data }; } - const parsed = schema.safeParse(data); + // zod v4 narrows the generic schema to $ZodTypes here; cast to the public ZodType (has safeParse in v3 & v4) + const parsed = (schema as unknown as z.ZodType).safeParse(data); if (!parsed.success) { return { success: false, error: parsed.error.message }; } - return { success: true, data: parsed.data }; + return { success: true, data: parsed.data as TSchema extends z.ZodFirstPartySchemaTypes ? z.infer : TData }; } private parseQueryParams(url: string): Record { diff --git a/packages/core/src/v3/types/tools.ts b/packages/core/src/v3/types/tools.ts index 9df741dc0ad..abf5646f9da 100644 --- a/packages/core/src/v3/types/tools.ts +++ b/packages/core/src/v3/types/tools.ts @@ -14,7 +14,7 @@ export type inferToolParameters = export function convertToolParametersToSchema( toolParameters: TToolParameters ): Schema { - return toolParameters instanceof z.ZodSchema + return toolParameters instanceof z.ZodType ? toolParameters : convertAISchemaToTaskSchema(toolParameters); } diff --git a/packages/core/src/v3/zodIpc.ts b/packages/core/src/v3/zodIpc.ts index d810d255d7f..7ee56ea654b 100644 --- a/packages/core/src/v3/zodIpc.ts +++ b/packages/core/src/v3/zodIpc.ts @@ -84,7 +84,8 @@ class ZodIpcMessageHandler< return; } - const ack = await handler(parsedMessage.payload, this.#sender); + // parsedMessage.payload is parsed output at runtime; zod v4 distinguishes input/output + const ack = await handler(parsedMessage.payload as Parameters[0], this.#sender); return ack; } @@ -320,7 +321,7 @@ export class ZodIpcConnection< ); }, timeoutInMs ?? defaultTimeoutInMs); - this.#acks.set(currentId, { resolve, reject, timeout }); + this.#acks.set(currentId, { resolve: resolve as (value: unknown) => void, reject, timeout }); const schema = this.opts.emitSchema[type]?.["message"]; diff --git a/packages/core/src/v3/zodMessageHandler.ts b/packages/core/src/v3/zodMessageHandler.ts index 2836822faf5..645e290edf3 100644 --- a/packages/core/src/v3/zodMessageHandler.ts +++ b/packages/core/src/v3/zodMessageHandler.ts @@ -109,7 +109,8 @@ export class ZodMessageHandler }; } - const ack = await handler(parsedMessage.data.payload); + // parsedMessage.data.payload is parsed output at runtime; zod v4 distinguishes input/output + const ack = await handler(parsedMessage.data.payload as Parameters[0]); return { success: true, @@ -277,7 +278,8 @@ export class ZodMessageSender { } try { - await this.#sender({ type, payload, version: "v1" }); + // send the validated/normalized output so zod transforms, defaults, and coercions are applied + await this.#sender({ type, payload: parsedPayload.data, version: "v1" }); } catch (error) { console.error("[ZodMessageSender] Failed to send message", error); } @@ -332,7 +334,8 @@ export async function sendMessageInCatalog = { diff --git a/packages/core/src/v3/zodSocket.ts b/packages/core/src/v3/zodSocket.ts index 32488dd3551..86fa0c1f197 100644 --- a/packages/core/src/v3/zodSocket.ts +++ b/packages/core/src/v3/zodSocket.ts @@ -128,7 +128,8 @@ export class ZodSocketMessageHandler[0]); return ack; } @@ -314,7 +315,7 @@ export class ZodSocketMessageSender>; } } diff --git a/packages/redis-worker/package.json b/packages/redis-worker/package.json index 7c014c23436..90e1ac36bdb 100644 --- a/packages/redis-worker/package.json +++ b/packages/redis-worker/package.json @@ -28,7 +28,6 @@ "nanoid": "^5.0.7", "p-limit": "^6.2.0", "seedrandom": "^3.0.5", - "zod": "3.25.76", "cron-parser": "^4.9.0" }, "devDependencies": { @@ -39,7 +38,8 @@ "@types/seedrandom": "^3.0.8", "rimraf": "6.0.1", "tsup": "^8.4.0", - "tsx": "4.17.0" + "tsx": "4.17.0", + "zod": "4.4.3" }, "engines": { "node": ">=18.20.0" @@ -53,5 +53,8 @@ "import": "./dist/index.js", "require": "./dist/index.cjs" } + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" } } diff --git a/packages/redis-worker/src/mollifier/schemas.ts b/packages/redis-worker/src/mollifier/schemas.ts index db75ed6e452..f395bad5888 100644 --- a/packages/redis-worker/src/mollifier/schemas.ts +++ b/packages/redis-worker/src/mollifier/schemas.ts @@ -56,12 +56,12 @@ export const BufferEntrySchema = z.object({ // so an entry written by an accept Lua predating this field — or one // surviving across the deploy that introduced it — still parses instead // of being silently dropped on pop. - createdAtMicros: stringToInt.default("0"), + createdAtMicros: stringToInt.default(0), // Drainer-ack flag: `true` once the drainer has materialised this run // into PG. The hash persists for a short grace TTL after ack so direct // reads (retrieve, trace, etc.) still resolve while PG replica lag // settles. Absent on pre-ack entries. - materialised: stringToBool.default("false"), + materialised: stringToBool.default(false), // Denormalised pointer to the Redis idempotency lookup key (set when // the run was accepted with an idempotency key, empty otherwise). The // ack Lua reads this to DEL the lookup atomically with marking the @@ -71,7 +71,7 @@ export const BufferEntrySchema = z.object({ // Incremented atomically by the CAS metadata Lua. Matches the // semantic of `TaskRun.metadataVersion` on the PG side (which the // UpdateMetadataService uses for the same retry-on-conflict pattern). - metadataVersion: stringToInt.default("0"), + metadataVersion: stringToInt.default(0), lastError: stringToError.optional(), }); diff --git a/packages/redis-worker/src/queue.ts b/packages/redis-worker/src/queue.ts index becfb3813ea..63e53b334e4 100644 --- a/packages/redis-worker/src/queue.ts +++ b/packages/redis-worker/src/queue.ts @@ -217,7 +217,8 @@ export class SimpleQueue { continue; } - const validatedItem = schema.safeParse(parsedItem.item); + // zod v4 narrows the generic schema union here; cast to public ZodType (has safeParse in v3 & v4) + const validatedItem = (schema as z.ZodType).safeParse(parsedItem.item); if (!validatedItem.success) { this.logger.error("Invalid item in queue", { @@ -236,7 +237,7 @@ export class SimpleQueue { dequeuedItems.push({ id, job: parsedItem.job, - item: validatedItem.data, + item: validatedItem.data as QueueItem["item"], visibilityTimeoutMs, attempt: parsedItem.attempt ?? 0, timestamp, diff --git a/packages/redis-worker/src/worker.ts b/packages/redis-worker/src/worker.ts index b9240acaa63..d7e9e098eac 100644 --- a/packages/redis-worker/src/worker.ts +++ b/packages/redis-worker/src/worker.ts @@ -1088,11 +1088,13 @@ class Worker { const enqueued = await this.enqueueOnce({ id: identifier, job, + // job is a runtime string here, so K widens and the payload can't match a + // single catalog entry statically; the shape is correct for the cron job. payload: { timestamp: scheduledAt.getTime(), lastTimestamp: lastTimestamp?.getTime(), cron, - }, + } as z.infer, availableAt, }); diff --git a/packages/trigger-sdk/package.json b/packages/trigger-sdk/package.json index 64de5ea6d96..65237e95480 100644 --- a/packages/trigger-sdk/package.json +++ b/packages/trigger-sdk/package.json @@ -101,13 +101,13 @@ "tshy": "^3.0.2", "tsx": "4.17.0", "typed-emitter": "^2.1.0", - "zod": "3.25.76" + "zod": "4.4.3" }, "peerDependencies": { "@ai-sdk/otel": ">=1.0.0-0 <2", "ai": "^5.0.0 || ^6.0.0 || >=7.0.0-canary <8", "react": "^18.0 || ^19.0", - "zod": "^3.0.0 || ^4.0.0" + "zod": "^3.25.0 || ^4.0.0" }, "peerDependenciesMeta": { "@ai-sdk/otel": { From a13357ca6196c7c2cec0c4ba7b510fae99f42f01 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Tue, 30 Jun 2026 16:17:39 +0100 Subject: [PATCH 02/17] format, typecheck, fix build --- apps/webapp/app/components/Feedback.tsx | 20 ++++++++++++--- .../billing/BillingAlertsSection.tsx | 2 +- .../billing/BillingLimitConfigSection.tsx | 4 +-- .../billing/BillingLimitRecoveryPanel.tsx | 6 ++--- .../route.tsx | 25 +++++++++++-------- .../route.tsx | 2 +- .../route.tsx | 10 +++++--- .../route.tsx | 4 +-- .../app/routes/confirm-basic-details.tsx | 2 +- .../app/routes/resources.branches.archive.tsx | 5 +++- .../app/routes/resources.branches.create.tsx | 2 +- apps/webapp/app/utils/timeGranularity.ts | 16 ++++++------ packages/core/src/schemas/json.ts | 12 +++++++-- packages/core/src/v3/serverOnly/httpServer.ts | 5 +++- 14 files changed, 74 insertions(+), 41 deletions(-) diff --git a/apps/webapp/app/components/Feedback.tsx b/apps/webapp/app/components/Feedback.tsx index 7ceaa05fb53..fc60bdad1b7 100644 --- a/apps/webapp/app/components/Feedback.tsx +++ b/apps/webapp/app/components/Feedback.tsx @@ -1,4 +1,10 @@ -import { getFormProps, getSelectProps, getInputProps, getTextareaProps, useForm } from "@conform-to/react"; +import { + getFormProps, + getSelectProps, + getInputProps, + getTextareaProps, + useForm, +} from "@conform-to/react"; import { parseWithZod } from "@conform-to/zod/v4"; import { InformationCircleIcon, ArrowUpCircleIcon } from "@heroicons/react/20/solid"; import { EnvelopeIcon, ShieldCheckIcon } from "@heroicons/react/24/solid"; @@ -114,9 +120,17 @@ export function Feedback({ type === "concurrency" || type === "hipaa" ) &&
} - +
- + {type === "feature" && ( v === "on" || v === true || v === "true", z.boolean()) diff --git a/apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx b/apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx index 1dfcdd73707..c0ecbc402c2 100644 --- a/apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx +++ b/apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx @@ -1,5 +1,5 @@ import { getFormProps, useForm, type SubmissionResult } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { Form, useActionData, useNavigation } from "@remix-run/react"; import { useEffect, useMemo, useRef, useState } from "react"; import { z } from "zod"; @@ -21,7 +21,7 @@ export const billingLimitRecoveryFormSchema = z .object({ action: z.enum(["increase", "remove"]), newAmount: z.coerce - .number({ invalid_type_error: "Not a valid amount" }) + .number({ error: "Not a valid amount" }) .positive("Amount must be greater than 0") .optional(), resumeMode: z.enum(["queue", "new_only"]), @@ -87,7 +87,7 @@ export function BillingLimitRecoveryPanel({ }, defaultValue: { action: "increase", - newAmount: suggestedNewLimitDollars, + newAmount: String(suggestedNewLimitDollars), resumeMode: "queue", }, }); diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx index 5409b8ab217..d3402165808 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx @@ -74,19 +74,22 @@ const schema = z.object({ if (i === "true") return true; return false; }, z.boolean()), - environmentIds: z.preprocess((i) => { - if (typeof i === "string") return [i]; - - if (Array.isArray(i)) { - const ids = i.filter((v) => typeof v === "string" && v !== ""); - if (ids.length === 0) { - return; + environmentIds: z.preprocess( + (i) => { + if (typeof i === "string") return [i]; + + if (Array.isArray(i)) { + const ids = i.filter((v) => typeof v === "string" && v !== ""); + if (ids.length === 0) { + return; + } + return ids; } - return ids; - } - return; - }, z.array(z.string(), { error: "At least one environment is required" })), + return; + }, + z.array(z.string(), { error: "At least one environment is required" }) + ), variables: z.preprocess((i) => { if (!Array.isArray(i)) { return []; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx index a7790c4ee48..c61bbe72b8f 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.billing-limits/route.tsx @@ -1,4 +1,4 @@ -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import type { MetaFunction } from "@remix-run/react"; import { json, redirect } from "@remix-run/server-runtime"; import { tryCatch } from "@trigger.dev/core"; diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx index afb0bacd37e..2212a69828d 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.settings.team/route.tsx @@ -1020,11 +1020,11 @@ export function PurchaseSeatsModal({ // when the role can't manage billing. The action enforces it independently. const noBillingTooltip = "You don't have permission to manage billing"; const trigger = canManageBilling ? ( - triggerButton ?? ( + (triggerButton ?? ( - ) + )) ) : triggerButton ? ( cloneElement(triggerButton, { disabled: true, tooltip: noBillingTooltip }) ) : ( @@ -1042,7 +1042,11 @@ export function PurchaseSeatsModal({ {trigger} {title} - +
diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx index ddc21398a33..b58ab15b768 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug_.projects.new/route.tsx @@ -388,12 +388,12 @@ export default function Page() { {canCreateV3Projects ? ( ) : ( )} diff --git a/apps/webapp/app/routes/confirm-basic-details.tsx b/apps/webapp/app/routes/confirm-basic-details.tsx index 913f88df80f..c15d5991705 100644 --- a/apps/webapp/app/routes/confirm-basic-details.tsx +++ b/apps/webapp/app/routes/confirm-basic-details.tsx @@ -316,7 +316,7 @@ export default function Page() { ) : ( <> - + )} diff --git a/apps/webapp/app/routes/resources.branches.archive.tsx b/apps/webapp/app/routes/resources.branches.archive.tsx index 490cb77fc03..dfc542f793f 100644 --- a/apps/webapp/app/routes/resources.branches.archive.tsx +++ b/apps/webapp/app/routes/resources.branches.archive.tsx @@ -100,7 +100,10 @@ export function ArchiveButton({ {...getFormProps(form)} className="w-full" > - + { - const ms = parseDuration(val); - return ms !== null && ms > 0; - }, - { error: (issue) => `Invalid or non-positive duration string: "${issue.input}"` } - ); +const DurationString = z.string().refine( + (val) => { + const ms = parseDuration(val); + return ms !== null && ms > 0; + }, + { error: (issue) => `Invalid or non-positive duration string: "${issue.input}"` } +); const BracketSchema = z.object({ max: z.union([z.literal("Infinity"), DurationString]), diff --git a/packages/core/src/schemas/json.ts b/packages/core/src/schemas/json.ts index 32dd506c8ff..985c3073a39 100644 --- a/packages/core/src/schemas/json.ts +++ b/packages/core/src/schemas/json.ts @@ -6,7 +6,11 @@ type Literal = z.infer; export type DeserializedJson = Literal | { [key: string]: DeserializedJson } | DeserializedJson[]; export const DeserializedJsonSchema: z.ZodType = z.lazy(() => - z.union([LiteralSchema, z.array(DeserializedJsonSchema), z.record(z.string(), DeserializedJsonSchema)]) + z.union([ + LiteralSchema, + z.array(DeserializedJsonSchema), + z.record(z.string(), DeserializedJsonSchema), + ]) ); const SerializableSchema = z.union([ @@ -26,5 +30,9 @@ export type SerializableJson = | SerializableJson[]; export const SerializableJsonSchema: z.ZodType = z.lazy(() => - z.union([SerializableSchema, z.array(SerializableJsonSchema), z.record(z.string(), SerializableJsonSchema)]) + z.union([ + SerializableSchema, + z.array(SerializableJsonSchema), + z.record(z.string(), SerializableJsonSchema), + ]) ); diff --git a/packages/core/src/v3/serverOnly/httpServer.ts b/packages/core/src/v3/serverOnly/httpServer.ts index 1ada1c37ac8..28bf0af9ac7 100644 --- a/packages/core/src/v3/serverOnly/httpServer.ts +++ b/packages/core/src/v3/serverOnly/httpServer.ts @@ -309,7 +309,10 @@ export class HttpServer { return { success: false, error: parsed.error.message }; } - return { success: true, data: parsed.data as TSchema extends z.ZodFirstPartySchemaTypes ? z.infer : TData }; + return { + success: true, + data: parsed.data as TSchema extends z.ZodFirstPartySchemaTypes ? z.infer : TData, + }; } private parseQueryParams(url: string): Record { From 61327b61b62c84e98bb964d54fda0f3462d6238e Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Tue, 30 Jun 2026 16:23:01 +0100 Subject: [PATCH 03/17] fix failing test --- apps/webapp/test/billingLimitsRoute.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/test/billingLimitsRoute.test.ts b/apps/webapp/test/billingLimitsRoute.test.ts index 66f1ac5a7b5..278ee90d791 100644 --- a/apps/webapp/test/billingLimitsRoute.test.ts +++ b/apps/webapp/test/billingLimitsRoute.test.ts @@ -1,5 +1,5 @@ import { describe, expect, it } from "vitest"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { billingAlertsSchema } from "~/components/billing/BillingAlertsSection"; import { billingLimitFormSchema, From 5ec053b17683bbac80749bd46db4af89d99a4095 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Fri, 3 Jul 2026 16:07:44 +0100 Subject: [PATCH 04/17] format, lint --- .../route.tsx | 46 +++++++++---------- 1 file changed, 21 insertions(+), 25 deletions(-) diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx index a7cdf1e189a..87ddb127205 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.test.tasks.$taskParam/route.tsx @@ -433,37 +433,15 @@ function StandardTaskForm({ const [showTemplateCreatedSuccessMessage, setShowTemplateCreatedSuccessMessage] = useState(false); - const [ - form, - { - environmentId, - payload, - metadata, - taskIdentifier, - delaySeconds, - ttlSeconds, - idempotencyKey, - idempotencyKeyTTLSeconds, - queue, - concurrencyKey, - maxAttempts, - maxDurationSeconds, - triggerSource, - tags, - version, - machine, - region, - prioritySeconds, - }, - ] = useForm({ + const [form, fields] = useForm({ id: "test-task", // TODO: type this lastResult: lastSubmission as any, onSubmit(event, { formData }) { event.preventDefault(); - formData.set(payload.name, currentPayloadJson.current); - formData.set(metadata.name, currentMetadataJson.current); + formData.set(fields.payload.name, currentPayloadJson.current); + formData.set(fields.metadata.name, currentMetadataJson.current); submit(formData, { method: "POST" }); }, @@ -471,6 +449,24 @@ function StandardTaskForm({ return parseWithZod(formData, { schema: TestTaskData }); }, }); + const { + environmentId, + taskIdentifier, + delaySeconds, + ttlSeconds, + idempotencyKey, + idempotencyKeyTTLSeconds, + queue, + concurrencyKey, + maxAttempts, + maxDurationSeconds, + triggerSource, + tags, + version, + machine, + region, + prioritySeconds, + } = fields; return ( From f1603b28cb99cd0710afb55879d1658be43dee1e Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Fri, 3 Jul 2026 16:25:45 +0100 Subject: [PATCH 05/17] cli dep zod4, update changelog --- .changeset/zod-4-support.md | 4 +++- packages/cli-v3/package.json | 7 ++----- 2 files changed, 5 insertions(+), 6 deletions(-) diff --git a/.changeset/zod-4-support.md b/.changeset/zod-4-support.md index c0578513828..fd739c5be76 100644 --- a/.changeset/zod-4-support.md +++ b/.changeset/zod-4-support.md @@ -6,4 +6,6 @@ "@trigger.dev/schema-to-json": minor --- -Add zod v4 compatibility. The `zod` peer dependency is widened to `^3.25.0 || ^4.0.0`, so projects can use zod 3.25+ or zod 4. Internal code was updated for zod v4 API changes (`ZodError.errors` → `.issues`, single-arg `z.record` → keyed, unified `error` option, `z.ZodSchema`/`z.AnyZodObject` → `z.ZodType`/`z.ZodObject`, `z.any()` object fields made `.optional()` to preserve v3 inference). No runtime behavior change for existing zod 3 users. +Add zod v4 support. You can now use zod 3.25+ or zod 4 (the minimum zod 3 version is now 3.25.0). `zod` is now a peer dependency of `@trigger.dev/core`, `@trigger.dev/sdk`, `@trigger.dev/redis-worker`, and `@trigger.dev/schema-to-json`, so it shares a single copy with your project; if you depend on `@trigger.dev/core` directly, add `zod` to your own dependencies. + +The published type declarations are built against zod 4. If you are still on zod 3, keep `skipLibCheck: true` in your tsconfig (the default in Trigger.dev's templates). diff --git a/packages/cli-v3/package.json b/packages/cli-v3/package.json index a486a90013e..1051d4666c8 100644 --- a/packages/cli-v3/package.json +++ b/packages/cli-v3/package.json @@ -68,8 +68,7 @@ "rimraf": "^6.0.1", "ts-essentials": "10.0.1", "tshy": "^3.0.2", - "tsx": "4.17.0", - "zod": "4.4.3" + "tsx": "4.17.0" }, "scripts": { "clean": "rimraf dist .tshy .tshy-build .turbo", @@ -147,6 +146,7 @@ "tinyglobby": "^0.2.10", "ws": "^8.18.0", "xdg-app-paths": "^8.3.0", + "zod": "4.4.3", "zod-validation-error": "^5.0.0" }, "engines": { @@ -160,8 +160,5 @@ "default": "./dist/esm/index.js" } } - }, - "peerDependencies": { - "zod": "^3.25.0 || ^4.0.0" } } From 526b89210640c1a1d04a7e46ed617d12b8e24b29 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Fri, 3 Jul 2026 16:54:19 +0100 Subject: [PATCH 06/17] bump to platform@1.2.0 --- package.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package.json b/package.json index 2708b06be73..3f7acafe0a6 100644 --- a/package.json +++ b/package.json @@ -143,6 +143,7 @@ "brace-expansion@>=5 <5.0.6": "5.0.6", "@jsonhero/json-infer-types>ip-address": "^10.2.0", "@modelcontextprotocol/sdk@>=1.26.0>express-rate-limit": "^8.6.0" + "zod": "4.4.3" }, "onlyBuiltDependencies": [ "@depot/cli", From 80da777793b65b0fe42b88b5ae78406128c93223 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Mon, 3 Aug 2026 15:35:42 +0100 Subject: [PATCH 07/17] updates --- apps/webapp/app/routes/login._index/route.tsx | 4 +- apps/webapp/app/routes/login.sso/route.tsx | 2 +- .../app/services/queryService.server.ts | 2 +- package.json | 2 +- pnpm-lock.yaml | 312 +++++++++--------- 5 files changed, 158 insertions(+), 164 deletions(-) diff --git a/apps/webapp/app/routes/login._index/route.tsx b/apps/webapp/app/routes/login._index/route.tsx index ce186c73461..81c5d26d30b 100644 --- a/apps/webapp/app/routes/login._index/route.tsx +++ b/apps/webapp/app/routes/login._index/route.tsx @@ -38,9 +38,7 @@ import { cn } from "~/utils/cn"; // format validation in the browser and renders the styled inline error. // Server-side errors still surface via the session-backed authError below. const emailSchema = z.object({ - email: z - .string({ required_error: "Enter your email address" }) - .email("Enter a valid email address"), + email: z.string({ error: "Enter your email address" }).email("Enter a valid email address"), }); function LastUsedBadge({ className }: { className?: string }) { diff --git a/apps/webapp/app/routes/login.sso/route.tsx b/apps/webapp/app/routes/login.sso/route.tsx index 1ed85497353..d03b7de2893 100644 --- a/apps/webapp/app/routes/login.sso/route.tsx +++ b/apps/webapp/app/routes/login.sso/route.tsx @@ -67,7 +67,7 @@ const ERROR_MESSAGES: Record = { // inline error before submit. Server-side errors keep flowing via ?error=. const ssoEmailSchema = z.object({ email: z - .string({ required_error: "Enter your enterprise email address" }) + .string({ error: "Enter your enterprise email address" }) .email("Enter a valid email address"), }); diff --git a/apps/webapp/app/services/queryService.server.ts b/apps/webapp/app/services/queryService.server.ts index 33eb60e3cc8..77f2b3f4178 100644 --- a/apps/webapp/app/services/queryService.server.ts +++ b/apps/webapp/app/services/queryService.server.ts @@ -381,7 +381,7 @@ export async function executeQuery( const result = await executeTSQL(queryClickhouse.reader, { ...baseOptions, - schema: z.record(z.string(), z.any()), + schema: z.record(z.string(), z.any()) as unknown as TOut, tableSchema: effectiveSchemas, transformValues: true, enforcedWhereClause, diff --git a/package.json b/package.json index 3f7acafe0a6..0718cb0178f 100644 --- a/package.json +++ b/package.json @@ -142,7 +142,7 @@ "brace-expansion@>=2 <2.0.3": "2.0.3", "brace-expansion@>=5 <5.0.6": "5.0.6", "@jsonhero/json-infer-types>ip-address": "^10.2.0", - "@modelcontextprotocol/sdk@>=1.26.0>express-rate-limit": "^8.6.0" + "@modelcontextprotocol/sdk@>=1.26.0>express-rate-limit": "^8.6.0", "zod": "4.4.3" }, "onlyBuiltDependencies": [ diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 45163c161bb..4ed4f2765d1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -53,6 +53,7 @@ overrides: brace-expansion@>=5 <5.0.6: 5.0.6 '@jsonhero/json-infer-types>ip-address': ^10.2.0 '@modelcontextprotocol/sdk@>=1.26.0>express-rate-limit': ^8.6.0 + zod: 4.4.3 patchedDependencies: '@changesets/assemble-release-plan@5.2.4': @@ -191,8 +192,8 @@ importers: specifier: ^3.8.0 version: 3.8.1 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 devDependencies: '@internal/testcontainers': specifier: workspace:* @@ -205,13 +206,13 @@ importers: dependencies: '@ai-sdk/anthropic': specifier: ^3.0.0 - version: 3.0.84(zod@3.25.76) + version: 3.0.84(zod@4.4.3) '@ai-sdk/openai': specifier: ^3.0.0 - version: 3.0.41(zod@3.25.76) + version: 3.0.41(zod@4.4.3) '@ai-sdk/react': specifier: ^3.0.0 - version: 3.0.170(react@18.3.1)(zod@3.25.76) + version: 3.0.170(react@18.3.1)(zod@4.4.3) '@ariakit/react': specifier: ^0.4.6 version: 0.4.6(react-dom@18.3.1(react@18.3.1))(react@18.3.1) @@ -268,7 +269,7 @@ importers: version: 1.19.4(react-dom@18.3.1(react@18.3.1))(react@18.3.1) '@conform-to/zod': specifier: ^1.2.2 - version: 1.19.4(zod@3.25.76) + version: 1.19.4(zod@4.4.3) '@depot/cli': specifier: 0.0.1-cli.2.80.0 version: 0.0.1-cli.2.80.0 @@ -490,7 +491,7 @@ importers: version: link:../../internal-packages/otlp-importer '@trigger.dev/platform': specifier: 1.3.0 - version: 1.3.0(zod@3.25.76) + version: 1.3.0(zod@4.4.3) '@trigger.dev/plugins': specifier: workspace:* version: link:../../packages/plugins @@ -526,7 +527,7 @@ importers: version: 1.1.3(react-dom@18.3.1(react@18.3.1))(react@18.3.1) ai: specifier: 6.0.116 - version: 6.0.116(zod@3.25.76) + version: 6.0.116(zod@4.4.3) assert: specifier: ^2.1.0 version: 2.1.0 @@ -727,7 +728,7 @@ importers: version: 0.3.1(@remix-run/react@2.17.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@6.0.3))(@remix-run/server-runtime@2.17.5(typescript@6.0.3))(react@18.3.1) remix-utils: specifier: ^7.7.0 - version: 7.7.0(@remix-run/node@2.17.5(typescript@6.0.3))(@remix-run/react@2.17.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@6.0.3))(@remix-run/router@1.23.3(patch_hash=33e2966e9ef36aa09955cec9922c5063227b83429157a71ba226369a08627ac9))(crypto-js@4.2.0)(intl-parse-accept-language@1.0.0)(react@18.3.1)(zod@3.25.76) + version: 7.7.0(@remix-run/node@2.17.5(typescript@6.0.3))(@remix-run/react@2.17.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@6.0.3))(@remix-run/router@1.23.3(patch_hash=33e2966e9ef36aa09955cec9922c5063227b83429157a71ba226369a08627ac9))(crypto-js@4.2.0)(intl-parse-accept-language@1.0.0)(react@18.3.1)(zod@4.4.3) semver: specifier: ^7.5.0 version: 7.8.1 @@ -777,14 +778,14 @@ importers: specifier: 8.21.0 version: 8.21.0(bufferutil@4.0.9) zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 zod-error: specifier: 1.5.0 version: 1.5.0 zod-validation-error: - specifier: ^1.5.0 - version: 1.5.0(zod@3.25.76) + specifier: ^5.0.0 + version: 5.0.0(zod@4.4.3) devDependencies: '@internal/clickhouse': specifier: workspace:* @@ -899,7 +900,7 @@ importers: version: 0.15.18 evalite: specifier: 1.0.0-beta.16 - version: 1.0.0-beta.16(ai@6.0.116(zod@3.25.76))(better-sqlite3@11.10.0)(bufferutil@4.0.9) + version: 1.0.0-beta.16(ai@6.0.116(zod@4.4.3))(better-sqlite3@11.10.0)(bufferutil@4.0.9) npm-run-all: specifier: ^4.1.5 version: 4.1.5 @@ -975,8 +976,8 @@ importers: specifier: workspace:* version: link:../../packages/core zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 zod-error: specifier: 1.5.0 version: 1.5.0 @@ -991,8 +992,8 @@ importers: internal-packages/compute: dependencies: zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 devDependencies: rimraf: specifier: 6.0.1 @@ -1002,7 +1003,7 @@ importers: dependencies: '@ai-sdk/anthropic': specifier: ^3.0.0 - version: 3.0.84(zod@3.25.76) + version: 3.0.84(zod@4.4.3) '@internal/dashboard-agent-db': specifier: workspace:* version: link:../dashboard-agent-db @@ -1011,10 +1012,10 @@ importers: version: link:../../packages/trigger-sdk ai: specifier: 6.0.116 - version: 6.0.116(zod@3.25.76) + version: 6.0.116(zod@4.4.3) zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 devDependencies: '@ai-sdk/provider': specifier: 3.0.8 @@ -1088,8 +1089,8 @@ importers: specifier: ^1.2.0 version: 1.3.1 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 devDependencies: '@types/nodemailer': specifier: ^8.0.0 @@ -1256,8 +1257,8 @@ importers: specifier: ^3.0.5 version: 3.0.5 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 devDependencies: '@internal/testcontainers': specifier: workspace:* @@ -1334,8 +1335,8 @@ importers: specifier: 3.3.8 version: 3.3.8 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 devDependencies: '@internal/testcontainers': specifier: workspace:* @@ -1446,8 +1447,8 @@ importers: specifier: 0.5.0-alpha.4 version: 0.5.0-alpha.4(patch_hash=b5d41129ddbf7c4cbb0288244b2c8d041ee0803c5102c1181c180408d3b579f4) zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 devDependencies: antlr4ts-cli: specifier: 0.5.0-alpha.4 @@ -1506,7 +1507,7 @@ importers: version: 0.0.1-cli.2.80.0 '@modelcontextprotocol/sdk': specifier: ^1.25.2 - version: 1.25.2(hono@4.12.25)(supports-color@10.0.0)(zod@3.25.76) + version: 1.25.2(hono@4.12.25)(supports-color@10.0.0)(zod@4.4.3) '@opentelemetry/api': specifier: 1.9.1 version: 1.9.1 @@ -1685,11 +1686,11 @@ importers: specifier: ^8.3.0 version: 8.3.0 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 zod-validation-error: - specifier: ^1.5.0 - version: 1.5.0(zod@3.25.76) + specifier: ^5.0.0 + version: 5.0.0(zod@4.4.3) devDependencies: '@epic-web/test-server': specifier: ^0.1.0 @@ -1844,19 +1845,16 @@ importers: uncrypto: specifier: ^0.1.3 version: 0.1.3 - zod: - specifier: 3.25.76 - version: 3.25.76 zod-error: specifier: 1.5.0 version: 1.5.0 zod-validation-error: - specifier: ^1.5.0 - version: 1.5.0(zod@3.25.76) + specifier: ^5.0.0 + version: 5.0.0(zod@4.4.3) devDependencies: '@ai-sdk/provider-utils': specifier: ^1.0.22 - version: 1.0.22(zod@3.25.76) + version: 1.0.22(zod@4.4.3) '@arethetypeswrong/cli': specifier: ^0.18.5 version: 0.18.5 @@ -1877,10 +1875,10 @@ importers: version: 4.0.14 ai: specifier: 6.0.116 - version: 6.0.116(zod@3.25.76) + version: 6.0.116(zod@4.4.3) ai-v7: specifier: npm:ai@7.0.0-canary.159 - version: ai@7.0.0-canary.159(zod@3.25.76) + version: ai@7.0.0-canary.159(zod@4.4.3) defu: specifier: ^6.1.5 version: 6.1.7 @@ -1902,6 +1900,9 @@ importers: tsx: specifier: 4.17.0 version: 4.17.0 + zod: + specifier: 4.4.3 + version: 4.4.3 packages/plugins: dependencies: @@ -2016,9 +2017,6 @@ importers: seedrandom: specifier: ^3.0.5 version: 3.0.5 - zod: - specifier: 3.25.76 - version: 3.25.76 devDependencies: '@internal/redis': specifier: workspace:* @@ -2044,6 +2042,9 @@ importers: tsx: specifier: 4.17.0 version: 4.17.0 + zod: + specifier: 4.4.3 + version: 4.4.3 packages/rsc: dependencies: @@ -2097,11 +2098,11 @@ importers: specifier: ^3 version: 3.17.1 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 zod-to-json-schema: specifier: ^3.24.0 - version: 3.24.6(zod@3.25.76) + version: 3.24.6(zod@4.4.3) devDependencies: '@arethetypeswrong/cli': specifier: ^0.18.5 @@ -2135,7 +2136,7 @@ importers: dependencies: '@ai-sdk/otel': specifier: '>=1.0.0-0 <2' - version: 1.0.0-beta.6(zod@3.25.76) + version: 1.0.0-beta.6(zod@4.4.3) '@opentelemetry/api': specifier: 1.9.1 version: 1.9.1 @@ -2193,10 +2194,10 @@ importers: version: 8.5.4 ai: specifier: 6.0.116 - version: 6.0.116(zod@3.25.76) + version: 6.0.116(zod@4.4.3) ai-v7: specifier: npm:ai@7.0.0-canary.159 - version: ai@7.0.0-canary.159(zod@3.25.76) + version: ai@7.0.0-canary.159(zod@4.4.3) encoding: specifier: ^0.1.13 version: 0.1.13 @@ -2213,8 +2214,8 @@ importers: specifier: ^2.1.0 version: 2.1.0 zod: - specifier: 3.25.76 - version: 3.25.76 + specifier: 4.4.3 + version: 4.4.3 packages: @@ -2222,31 +2223,31 @@ packages: resolution: {integrity: sha512-BIDaHmCHs6Sr5VUsEkTbbVlAN4GWjg97X9x/IfXyviLtzsXvffui9XIcZugkAi1Ri6FnvI5T5qDGh5YLnSuzRg==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 '@ai-sdk/gateway@3.0.66': resolution: {integrity: sha512-SIQ0YY0iMuv+07HLsZ+bB990zUJ6S4ujORAh+Jv1V2KGNn73qQKnGO0JBk+w+Res8YqOFSycwDoWcFlQrVxS4A==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 '@ai-sdk/gateway@4.0.0-beta.30': resolution: {integrity: sha512-05tWxCb+3SRj5LBY37nRQ8eUdolO5QvKAfsIAUY3zAxg0jc1cBkQNSpZEVwQUR4uFm/Y5fLJ+MIa+6hkol1//A==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 '@ai-sdk/gateway@4.0.0-canary.93': resolution: {integrity: sha512-/d0d/DfJRmdMJJww/ihrYezgKedJhrOCsREuAuSHmd/kVzFB5p2bBuCZH3EBxKFQ8NLtPJpOR+QJxOUmDLD8PA==} engines: {node: '>=22'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 '@ai-sdk/openai@3.0.41': resolution: {integrity: sha512-IZ42A+FO+vuEQCVNqlnAPYQnnUpUfdJIwn1BEDOBywiEHa23fw7PahxVtlX9zm3/zMvTW4JKPzWyvAgDu+SQ2A==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 '@ai-sdk/otel@1.0.0-beta.6': resolution: {integrity: sha512-K5VikyO3EKQkNk77ew9oMjM8FInKF+WWar599LmP8rQ0x0iB+P/DVS+h6zQvmecxMNPtQOOyt0uDQFx/AA0DGw==} @@ -2256,7 +2257,7 @@ packages: resolution: {integrity: sha512-YHK2rpj++wnLVc9vPGzGFP3Pjeld2MwhKinetA0zKXOoHAT/Jit5O8kZsxcSlJPu9wvcGT1UGZEjZrtO7PfFOQ==} engines: {node: '>=18'} peerDependencies: - zod: ^3.0.0 + zod: 4.4.3 peerDependenciesMeta: zod: optional: true @@ -2265,19 +2266,19 @@ packages: resolution: {integrity: sha512-uhukHaCBvqkwBHkT8C2PrnqKTCoLn3pdHXqtcR9I8ErH+flbzgW4o7VHSNIup9LRu+WBvZIZDQLsx6rwl2tiOA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 '@ai-sdk/provider-utils@5.0.0-beta.9': resolution: {integrity: sha512-Ku/S1TV8ibqlweKQHa22eWT9QvKqR4hkl7KMJ0k7kw64Q7XObh12fJUnK4Mh8q+IR5ZeQGm95SLtx++d7FC6XA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 '@ai-sdk/provider-utils@5.0.0-canary.44': resolution: {integrity: sha512-3h7gp8MEwXACWsW8ZPjzio53ynKq0jTNp4Hwr0BWdychMFzmkRRWyvijhg56JWxye16FU92SqQnfBoe2PN4I2A==} engines: {node: '>=22'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 '@ai-sdk/provider@0.0.26': resolution: {integrity: sha512-dQkfBDs2lTYpKM8389oopPdQgIU007GQyCbuPPrV+K6MtSII3HBfE0stUIMXUb44L+LK1t6GXPP7wjSzjO6uKg==} @@ -3149,7 +3150,7 @@ packages: '@conform-to/zod@1.19.4': resolution: {integrity: sha512-g80QtotXLcXWCF86Xou/VpI6i+NBOQxYgLS9YCQYrgV27yilZFJYpgWWBsA8YwodmJVBIYm9RawJba7WnUoddA==} peerDependencies: - zod: ^3.21.0 || ^4.0.0 + zod: 4.4.3 '@connectrpc/connect-node@1.4.0': resolution: {integrity: sha512-0ANnrr6SvsjevsWEgdzHy7BaHkluZyS6s4xNoVt7RBHFR5V/kT9lPokoIbYUOU9JHzdRgTaS3x5595mwUsu15g==} @@ -4607,7 +4608,7 @@ packages: engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 + zod: 4.4.3 peerDependenciesMeta: '@cfworker/json-schema': optional: true @@ -4617,7 +4618,7 @@ packages: engines: {node: '>=18'} peerDependencies: '@cfworker/json-schema': ^4.1.1 - zod: ^3.25 || ^4.0 + zod: 4.4.3 peerDependenciesMeta: '@cfworker/json-schema': optional: true @@ -7730,7 +7731,7 @@ packages: '@trigger.dev/platform@1.3.0': resolution: {integrity: sha512-DIJ5Sy4X5XKSHQdYA3GImkb0jdE9I/HTRRRB3c9CFuXjGzhK3rJkEzjIEbaNRjk6yAev67EVTWY8P+D+khorUw==} peerDependencies: - zod: ^3.25.0 || ^4.0.0 + zod: 4.4.3 '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} @@ -8406,19 +8407,19 @@ packages: resolution: {integrity: sha512-7yM+cTmyRLeNIXwt4Vj+mrrJgVQ9RMIW5WO0ydoLoYkewIvsMcvUmqS4j2RJTUXaF1HphwmSKUMQ/HypNRGOmA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 ai@7.0.0-beta.60: resolution: {integrity: sha512-grSKE/9kQtElLCnrhwriTLwXlV+5W8Ljco2KzVc5cY9egK1zwSwVyFQo3L3Al+WVxwvVT6fQjLyEdQpqJHLTrA==} engines: {node: '>=18'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 ai@7.0.0-canary.159: resolution: {integrity: sha512-9z8OkZdrISDjS3bguQ/mCYsthmr7mNbB1hTa8cRg8aAp31r1HCxVFqmc+ZXTA0FqhGb8aW46UUcjUuJcwxg5Mw==} engines: {node: '>=22'} peerDependencies: - zod: ^3.25.76 || ^4.1.8 + zod: 4.4.3 ajv-formats@2.1.1: resolution: {integrity: sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==} @@ -12613,7 +12614,7 @@ packages: hasBin: true peerDependencies: ws: 8.21.0 - zod: ^3.23.8 + zod: 4.4.3 peerDependenciesMeta: ws: optional: true @@ -13769,7 +13770,7 @@ packages: intl-parse-accept-language: ^1.0.0 is-ip: ^5.0.1 react: 18.3.1 - zod: ^3.22.4 + zod: 4.4.3 peerDependenciesMeta: '@remix-run/cloudflare': optional: true @@ -15603,31 +15604,28 @@ packages: zod-to-json-schema@3.24.5: resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} peerDependencies: - zod: ^3.24.1 + zod: 4.4.3 zod-to-json-schema@3.24.6: resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} peerDependencies: - zod: ^3.24.1 + zod: 4.4.3 zod-to-json-schema@3.25.0: resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} peerDependencies: - zod: ^3.25 || ^4 + zod: 4.4.3 zod-to-json-schema@3.25.1: resolution: {integrity: sha512-pM/SU9d3YAggzi6MtR4h7ruuQlqKtad8e9S0fmxcMi+ueAK5Korys/aWcV9LIIHTVbj01NdzxcnXSN+O74ZIVA==} peerDependencies: - zod: ^3.25 || ^4 + zod: 4.4.3 - zod-validation-error@1.5.0: - resolution: {integrity: sha512-/7eFkAI4qV0tcxMBB/3+d2c1P6jzzZYdYSlBuAklzMuCrJu5bzJfHS0yVAS87dRHVlhftd6RFJDIvv03JgkSbw==} - engines: {node: '>=16.0.0'} + zod-validation-error@5.0.0: + resolution: {integrity: sha512-hmk+pkyKq7Q71PiWVSDUc3VfpzpvcRHZ3QPw9yEMVvmtCekaMeOHnbr3WbxfrgEnQTv6haGP4cmv0Ojmihzsxw==} + engines: {node: '>=18.0.0'} peerDependencies: - zod: ^3.18.0 - - zod@3.25.76: - resolution: {integrity: sha512-gzUt/qt81nXsFGKIFcC3YnfEAx5NkunCfnDlvuBSSFS02bcXu4Lmea0AFIUwbLWxWPx3d9p8S5QoaujKcNQxcQ==} + zod: 4.4.3 zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} @@ -15637,77 +15635,77 @@ packages: snapshots: - '@ai-sdk/anthropic@3.0.84(zod@3.25.76)': + '@ai-sdk/anthropic@3.0.84(zod@4.4.3)': dependencies: '@ai-sdk/provider': 3.0.10 - '@ai-sdk/provider-utils': 4.0.29(zod@3.25.76) - zod: 3.25.76 + '@ai-sdk/provider-utils': 4.0.29(zod@4.4.3) + zod: 4.4.3 - '@ai-sdk/gateway@3.0.66(zod@3.25.76)': + '@ai-sdk/gateway@3.0.66(zod@4.4.3)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.29(zod@3.25.76) + '@ai-sdk/provider-utils': 4.0.29(zod@4.4.3) '@vercel/oidc': 3.1.0 - zod: 3.25.76 + zod: 4.4.3 - '@ai-sdk/gateway@4.0.0-beta.30(zod@3.25.76)': + '@ai-sdk/gateway@4.0.0-beta.30(zod@4.4.3)': dependencies: '@ai-sdk/provider': 4.0.0-beta.5 - '@ai-sdk/provider-utils': 5.0.0-beta.9(zod@3.25.76) + '@ai-sdk/provider-utils': 5.0.0-beta.9(zod@4.4.3) '@vercel/oidc': 3.2.0 - zod: 3.25.76 + zod: 4.4.3 - '@ai-sdk/gateway@4.0.0-canary.93(zod@3.25.76)': + '@ai-sdk/gateway@4.0.0-canary.93(zod@4.4.3)': dependencies: '@ai-sdk/provider': 4.0.0-canary.17 - '@ai-sdk/provider-utils': 5.0.0-canary.44(zod@3.25.76) + '@ai-sdk/provider-utils': 5.0.0-canary.44(zod@4.4.3) '@vercel/oidc': 3.2.0 - zod: 3.25.76 + zod: 4.4.3 - '@ai-sdk/openai@3.0.41(zod@3.25.76)': + '@ai-sdk/openai@3.0.41(zod@4.4.3)': dependencies: '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.29(zod@3.25.76) - zod: 3.25.76 + '@ai-sdk/provider-utils': 4.0.29(zod@4.4.3) + zod: 4.4.3 - '@ai-sdk/otel@1.0.0-beta.6(zod@3.25.76)': + '@ai-sdk/otel@1.0.0-beta.6(zod@4.4.3)': dependencies: '@ai-sdk/provider': 4.0.0-beta.5 '@opentelemetry/api': 1.9.0 - ai: 7.0.0-beta.60(zod@3.25.76) + ai: 7.0.0-beta.60(zod@4.4.3) transitivePeerDependencies: - zod - '@ai-sdk/provider-utils@1.0.22(zod@3.25.76)': + '@ai-sdk/provider-utils@1.0.22(zod@4.4.3)': dependencies: '@ai-sdk/provider': 0.0.26 eventsource-parser: 1.1.2 nanoid: 3.3.8 secure-json-parse: 2.7.0 optionalDependencies: - zod: 3.25.76 + zod: 4.4.3 - '@ai-sdk/provider-utils@4.0.29(zod@3.25.76)': + '@ai-sdk/provider-utils@4.0.29(zod@4.4.3)': dependencies: '@ai-sdk/provider': 3.0.10 '@standard-schema/spec': 1.1.0 eventsource-parser: 3.1.0 - zod: 3.25.76 + zod: 4.4.3 - '@ai-sdk/provider-utils@5.0.0-beta.9(zod@3.25.76)': + '@ai-sdk/provider-utils@5.0.0-beta.9(zod@4.4.3)': dependencies: '@ai-sdk/provider': 4.0.0-beta.5 '@standard-schema/spec': 1.1.0 eventsource-parser: 3.1.0 - zod: 3.25.76 + zod: 4.4.3 - '@ai-sdk/provider-utils@5.0.0-canary.44(zod@3.25.76)': + '@ai-sdk/provider-utils@5.0.0-canary.44(zod@4.4.3)': dependencies: '@ai-sdk/provider': 4.0.0-canary.17 '@standard-schema/spec': 1.1.0 '@workflow/serde': 4.1.0 eventsource-parser: 3.1.0 - zod: 3.25.76 + zod: 4.4.3 '@ai-sdk/provider@0.0.26': dependencies: @@ -15729,10 +15727,10 @@ snapshots: dependencies: json-schema: 0.4.0 - '@ai-sdk/react@3.0.170(react@18.3.1)(zod@3.25.76)': + '@ai-sdk/react@3.0.170(react@18.3.1)(zod@4.4.3)': dependencies: - '@ai-sdk/provider-utils': 4.0.29(zod@3.25.76) - ai: 6.0.116(zod@3.25.76) + '@ai-sdk/provider-utils': 4.0.29(zod@4.4.3) + ai: 6.0.116(zod@4.4.3) react: 18.3.1 swr: 2.2.5(react@18.3.1) throttleit: 2.1.0 @@ -17821,10 +17819,10 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@conform-to/zod@1.19.4(zod@3.25.76)': + '@conform-to/zod@1.19.4(zod@4.4.3)': dependencies: '@conform-to/dom': 1.19.4 - zod: 3.25.76 + zod: 4.4.3 '@connectrpc/connect-node@1.4.0(@bufbuild/protobuf@1.10.0)(@connectrpc/connect@1.4.0(@bufbuild/protobuf@1.10.0))': dependencies: @@ -18899,7 +18897,7 @@ snapshots: '@microsoft/fetch-event-source@2.0.1': {} - '@modelcontextprotocol/sdk@1.25.2(hono@4.12.25)(supports-color@10.0.0)(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.25.2(hono@4.12.25)(supports-color@10.0.0)(zod@4.4.3)': dependencies: '@hono/node-server': 1.19.9(hono@4.12.25) ajv: 8.18.0 @@ -18915,13 +18913,13 @@ snapshots: json-schema-typed: 8.0.2 pkce-challenge: 5.0.0 raw-body: 3.0.0 - zod: 3.25.76 - zod-to-json-schema: 3.25.0(zod@3.25.76) + zod: 4.4.3 + zod-to-json-schema: 3.25.0(zod@4.4.3) transitivePeerDependencies: - hono - supports-color - '@modelcontextprotocol/sdk@1.26.0(zod@3.25.76)': + '@modelcontextprotocol/sdk@1.26.0(zod@4.4.3)': dependencies: '@hono/node-server': 1.19.9(hono@4.12.25) ajv: 8.18.0 @@ -18938,8 +18936,8 @@ snapshots: json-schema-typed: 8.0.2 pkce-challenge: 5.0.0 raw-body: 3.0.0 - zod: 3.25.76 - zod-to-json-schema: 3.25.1(zod@3.25.76) + zod: 4.4.3 + zod-to-json-schema: 3.25.1(zod@4.4.3) transitivePeerDependencies: - supports-color @@ -22368,9 +22366,9 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@trigger.dev/platform@1.3.0(zod@3.25.76)': + '@trigger.dev/platform@1.3.0(zod@4.4.3)': dependencies: - zod: 3.25.76 + zod: 4.4.3 '@tybys/wasm-util@0.10.3': dependencies: @@ -22889,7 +22887,7 @@ snapshots: '@unkey/error@0.2.0': dependencies: - zod: 3.25.76 + zod: 4.4.3 '@upsetjs/venn.js@2.0.0': optionalDependencies: @@ -22961,8 +22959,8 @@ snapshots: '@vercel/sdk@1.19.1': dependencies: - '@modelcontextprotocol/sdk': 1.26.0(zod@3.25.76) - zod: 3.25.76 + '@modelcontextprotocol/sdk': 1.26.0(zod@4.4.3) + zod: 4.4.3 transitivePeerDependencies: - '@cfworker/json-schema' - supports-color @@ -23197,27 +23195,27 @@ snapshots: ahocorasick@1.0.2: {} - ai@6.0.116(zod@3.25.76): + ai@6.0.116(zod@4.4.3): dependencies: - '@ai-sdk/gateway': 3.0.66(zod@3.25.76) + '@ai-sdk/gateway': 3.0.66(zod@4.4.3) '@ai-sdk/provider': 3.0.8 - '@ai-sdk/provider-utils': 4.0.29(zod@3.25.76) + '@ai-sdk/provider-utils': 4.0.29(zod@4.4.3) '@opentelemetry/api': 1.9.0 - zod: 3.25.76 + zod: 4.4.3 - ai@7.0.0-beta.60(zod@3.25.76): + ai@7.0.0-beta.60(zod@4.4.3): dependencies: - '@ai-sdk/gateway': 4.0.0-beta.30(zod@3.25.76) + '@ai-sdk/gateway': 4.0.0-beta.30(zod@4.4.3) '@ai-sdk/provider': 4.0.0-beta.5 - '@ai-sdk/provider-utils': 5.0.0-beta.9(zod@3.25.76) - zod: 3.25.76 + '@ai-sdk/provider-utils': 5.0.0-beta.9(zod@4.4.3) + zod: 4.4.3 - ai@7.0.0-canary.159(zod@3.25.76): + ai@7.0.0-canary.159(zod@4.4.3): dependencies: - '@ai-sdk/gateway': 4.0.0-canary.93(zod@3.25.76) + '@ai-sdk/gateway': 4.0.0-canary.93(zod@4.4.3) '@ai-sdk/provider': 4.0.0-canary.17 - '@ai-sdk/provider-utils': 5.0.0-canary.44(zod@3.25.76) - zod: 3.25.76 + '@ai-sdk/provider-utils': 5.0.0-canary.44(zod@4.4.3) + zod: 4.4.3 ajv-formats@2.1.1(ajv@8.20.0): optionalDependencies: @@ -23404,9 +23402,9 @@ snapshots: js-yaml: 4.1.1 linear-sum-assignment: 1.0.7 mustache: 4.2.0 - openai: 4.97.0(encoding@0.1.13)(ws@8.21.0(bufferutil@4.0.9))(zod@3.25.76) - zod: 3.25.76 - zod-to-json-schema: 3.24.5(zod@3.25.76) + openai: 4.97.0(encoding@0.1.13)(ws@8.21.0(bufferutil@4.0.9))(zod@4.4.3) + zod: 4.4.3 + zod-to-json-schema: 3.24.5(zod@4.4.3) transitivePeerDependencies: - encoding - ws @@ -25307,7 +25305,7 @@ snapshots: dependencies: require-like: 0.1.2 - evalite@1.0.0-beta.16(ai@6.0.116(zod@3.25.76))(better-sqlite3@11.10.0)(bufferutil@4.0.9): + evalite@1.0.0-beta.16(ai@6.0.116(zod@4.4.3))(better-sqlite3@11.10.0)(bufferutil@4.0.9): dependencies: '@fastify/static': 8.2.0 '@fastify/websocket': 11.2.0(bufferutil@4.0.9) @@ -25324,7 +25322,7 @@ snapshots: table: 6.9.0 tinyrainbow: 3.1.0 optionalDependencies: - ai: 6.0.116(zod@3.25.76) + ai: 6.0.116(zod@4.4.3) better-sqlite3: 11.10.0 transitivePeerDependencies: - bufferutil @@ -28219,7 +28217,7 @@ snapshots: transitivePeerDependencies: - encoding - openai@4.97.0(encoding@0.1.13)(ws@8.21.0(bufferutil@4.0.9))(zod@3.25.76): + openai@4.97.0(encoding@0.1.13)(ws@8.21.0(bufferutil@4.0.9))(zod@4.4.3): dependencies: '@types/node': 24.13.3 '@types/node-fetch': 2.6.12 @@ -28230,7 +28228,7 @@ snapshots: node-fetch: 2.6.12(encoding@0.1.13) optionalDependencies: ws: 8.21.0(bufferutil@4.0.9) - zod: 3.25.76 + zod: 4.4.3 transitivePeerDependencies: - encoding @@ -29561,7 +29559,7 @@ snapshots: '@remix-run/server-runtime': 2.17.5(typescript@6.0.3) react: 18.3.1 - remix-utils@7.7.0(@remix-run/node@2.17.5(typescript@6.0.3))(@remix-run/react@2.17.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@6.0.3))(@remix-run/router@1.23.3(patch_hash=33e2966e9ef36aa09955cec9922c5063227b83429157a71ba226369a08627ac9))(crypto-js@4.2.0)(intl-parse-accept-language@1.0.0)(react@18.3.1)(zod@3.25.76): + remix-utils@7.7.0(@remix-run/node@2.17.5(typescript@6.0.3))(@remix-run/react@2.17.5(react-dom@18.3.1(react@18.3.1))(react@18.3.1)(typescript@6.0.3))(@remix-run/router@1.23.3(patch_hash=33e2966e9ef36aa09955cec9922c5063227b83429157a71ba226369a08627ac9))(crypto-js@4.2.0)(intl-parse-accept-language@1.0.0)(react@18.3.1)(zod@4.4.3): dependencies: type-fest: 4.33.0 optionalDependencies: @@ -29571,7 +29569,7 @@ snapshots: crypto-js: 4.2.0 intl-parse-accept-language: 1.0.0 react: 18.3.1 - zod: 3.25.76 + zod: 4.4.3 remove-accents@0.5.0: {} @@ -31688,29 +31686,27 @@ snapshots: zod-error@1.5.0: dependencies: - zod: 3.25.76 + zod: 4.4.3 - zod-to-json-schema@3.24.5(zod@3.25.76): + zod-to-json-schema@3.24.5(zod@4.4.3): dependencies: - zod: 3.25.76 + zod: 4.4.3 - zod-to-json-schema@3.24.6(zod@3.25.76): + zod-to-json-schema@3.24.6(zod@4.4.3): dependencies: - zod: 3.25.76 + zod: 4.4.3 - zod-to-json-schema@3.25.0(zod@3.25.76): + zod-to-json-schema@3.25.0(zod@4.4.3): dependencies: - zod: 3.25.76 + zod: 4.4.3 - zod-to-json-schema@3.25.1(zod@3.25.76): + zod-to-json-schema@3.25.1(zod@4.4.3): dependencies: - zod: 3.25.76 + zod: 4.4.3 - zod-validation-error@1.5.0(zod@3.25.76): + zod-validation-error@5.0.0(zod@4.4.3): dependencies: - zod: 3.25.76 - - zod@3.25.76: {} + zod: 4.4.3 zod@4.4.3: {} From 750070aecb8766216ec61d878bab4805a7ab4924 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Mon, 3 Aug 2026 15:46:11 +0100 Subject: [PATCH 08/17] fixes --- .../app/routes/confirm-basic-details.tsx | 5 ++++- apps/webapp/app/routes/login._index/route.tsx | 2 +- apps/webapp/app/routes/login.sso/route.tsx | 2 +- packages/schema-to-json/package.json | 4 ++-- packages/schema-to-json/src/index.ts | 22 +++++++++---------- pnpm-lock.yaml | 13 ++--------- 6 files changed, 21 insertions(+), 27 deletions(-) diff --git a/apps/webapp/app/routes/confirm-basic-details.tsx b/apps/webapp/app/routes/confirm-basic-details.tsx index c15d5991705..efea8ac4ad0 100644 --- a/apps/webapp/app/routes/confirm-basic-details.tsx +++ b/apps/webapp/app/routes/confirm-basic-details.tsx @@ -316,7 +316,10 @@ export default function Page() { ) : ( <> - + )} diff --git a/apps/webapp/app/routes/login._index/route.tsx b/apps/webapp/app/routes/login._index/route.tsx index 81c5d26d30b..a3d402078e8 100644 --- a/apps/webapp/app/routes/login._index/route.tsx +++ b/apps/webapp/app/routes/login._index/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { EnvelopeIcon, LockClosedIcon } from "@heroicons/react/20/solid"; import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node"; import { Form, useNavigation } from "@remix-run/react"; diff --git a/apps/webapp/app/routes/login.sso/route.tsx b/apps/webapp/app/routes/login.sso/route.tsx index d03b7de2893..f3c3b0a4188 100644 --- a/apps/webapp/app/routes/login.sso/route.tsx +++ b/apps/webapp/app/routes/login.sso/route.tsx @@ -1,5 +1,5 @@ import { getFormProps, getInputProps, useForm } from "@conform-to/react"; -import { parseWithZod } from "@conform-to/zod"; +import { parseWithZod } from "@conform-to/zod/v4"; import { ArrowLeftIcon, ArrowUpRightIcon } from "@heroicons/react/20/solid"; import type { LoaderFunctionArgs, MetaFunction } from "@remix-run/node"; import { Form, useNavigation } from "@remix-run/react"; diff --git a/packages/schema-to-json/package.json b/packages/schema-to-json/package.json index 0a590345abc..cc014e30706 100644 --- a/packages/schema-to-json/package.json +++ b/packages/schema-to-json/package.json @@ -42,9 +42,9 @@ }, "dependencies": { "@trigger.dev/core": "workspace:*", - "zod-to-json-schema": "^3.24.0", + "zod-to-json-schema": "^3.25.0", "@sodaru/yup-to-json-schema": "^2", - "zod": "3.25.76", + "zod": "4.4.3", "effect": "^3" }, "devDependencies": { diff --git a/packages/schema-to-json/src/index.ts b/packages/schema-to-json/src/index.ts index a02eef07aa3..1cb42ef2d79 100644 --- a/packages/schema-to-json/src/index.ts +++ b/packages/schema-to-json/src/index.ts @@ -43,26 +43,26 @@ export function schemaToJsonSchema( ): ConversionResult | undefined { const parser = schema as any; - // Check if schema has a built-in toJsonSchema method (e.g., ArkType, Zod 4) - if (typeof parser.toJsonSchema === "function") { - try { - const jsonSchema = parser.toJsonSchema(); + if (isZodSchema(parser)) { + const jsonSchema = convertZodSchema(parser, options); + if (jsonSchema) { return { - jsonSchema, + jsonSchema: jsonSchema, }; - } catch (_error) { - // If toJsonSchema fails, continue to other checks } } - if (isZodSchema(parser)) { - const jsonSchema = convertZodSchema(parser, options); + // Check if schema has a built-in toJsonSchema method (e.g., ArkType) + if (typeof parser.toJsonSchema === "function") { + try { + const jsonSchema = parser.toJsonSchema(); - if (jsonSchema) { return { - jsonSchema: jsonSchema, + jsonSchema, }; + } catch (_error) { + // If toJsonSchema fails, continue to other checks } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 4ed4f2765d1..93bc82c994f 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2101,8 +2101,8 @@ importers: specifier: 4.4.3 version: 4.4.3 zod-to-json-schema: - specifier: ^3.24.0 - version: 3.24.6(zod@4.4.3) + specifier: ^3.25.0 + version: 3.25.1(zod@4.4.3) devDependencies: '@arethetypeswrong/cli': specifier: ^0.18.5 @@ -15606,11 +15606,6 @@ packages: peerDependencies: zod: 4.4.3 - zod-to-json-schema@3.24.6: - resolution: {integrity: sha512-h/z3PKvcTcTetyjl1fkj79MHNEjm+HpD6NXheWjzOekY7kV+lwDYnHw+ivHkijnCSMz1yJaWBD9vu/Fcmk+vEg==} - peerDependencies: - zod: 4.4.3 - zod-to-json-schema@3.25.0: resolution: {integrity: sha512-HvWtU2UG41LALjajJrML6uQejQhNJx+JBO9IflpSja4R03iNWfKXrj6W2h7ljuLyc1nKS+9yDyL/9tD1U/yBnQ==} peerDependencies: @@ -31692,10 +31687,6 @@ snapshots: dependencies: zod: 4.4.3 - zod-to-json-schema@3.24.6(zod@4.4.3): - dependencies: - zod: 4.4.3 - zod-to-json-schema@3.25.0(zod@4.4.3): dependencies: zod: 4.4.3 From fbf3bdc20deb19de2ac80f6572259f66af662463 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Mon, 3 Aug 2026 16:17:24 +0100 Subject: [PATCH 09/17] fix test --- apps/webapp/test/routesBatchGetReplicaLag.guard.test.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/webapp/test/routesBatchGetReplicaLag.guard.test.ts b/apps/webapp/test/routesBatchGetReplicaLag.guard.test.ts index 6fe7a646708..e851dea2053 100644 --- a/apps/webapp/test/routesBatchGetReplicaLag.guard.test.ts +++ b/apps/webapp/test/routesBatchGetReplicaLag.guard.test.ts @@ -331,7 +331,7 @@ describe("routes-batch-get callers under a lagging replica", () => { holder.environment = authEnv(seed); holder.cachedRequest = { id: batchId }; - const body = JSON.stringify({ items: [{ task: "my-task" }] }); + const body = JSON.stringify({ items: [{ task: "my-task", payload: null }] }); const res = (await actionTasksBatch({ request: new Request("http://localhost/api/v2/tasks/batch", { method: "POST", From b017f3f156d75b5cb571290b8c77316f8e17942b Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Mon, 3 Aug 2026 16:43:09 +0100 Subject: [PATCH 10/17] bump platform dep --- apps/webapp/package.json | 2 +- pnpm-lock.yaml | 10 +++++----- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 21a28f7a54e..44e78de653a 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -120,7 +120,7 @@ "@trigger.dev/core": "workspace:*", "@trigger.dev/database": "workspace:*", "@trigger.dev/otlp-importer": "workspace:*", - "@trigger.dev/platform": "1.3.0", + "@trigger.dev/platform": "1.3.1", "@trigger.dev/plugins": "workspace:*", "@trigger.dev/rbac": "workspace:*", "@trigger.dev/redis-worker": "workspace:*", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 93bc82c994f..758dd3cd2d0 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -490,8 +490,8 @@ importers: specifier: workspace:* version: link:../../internal-packages/otlp-importer '@trigger.dev/platform': - specifier: 1.3.0 - version: 1.3.0(zod@4.4.3) + specifier: 1.3.1 + version: 1.3.1(zod@4.4.3) '@trigger.dev/plugins': specifier: workspace:* version: link:../../packages/plugins @@ -7728,8 +7728,8 @@ packages: react: 18.3.1 react-dom: 18.3.1 - '@trigger.dev/platform@1.3.0': - resolution: {integrity: sha512-DIJ5Sy4X5XKSHQdYA3GImkb0jdE9I/HTRRRB3c9CFuXjGzhK3rJkEzjIEbaNRjk6yAev67EVTWY8P+D+khorUw==} + '@trigger.dev/platform@1.3.1': + resolution: {integrity: sha512-eCmoeilJyat6APofjHnyiJzOYobHbukbmexDpZVbQlqAkJaXKsK/f07Ijuw8Cyx3kTj8Hc24fj2vMHFPwcE9VA==} peerDependencies: zod: 4.4.3 @@ -22361,7 +22361,7 @@ snapshots: react: 18.3.1 react-dom: 18.3.1(react@18.3.1) - '@trigger.dev/platform@1.3.0(zod@4.4.3)': + '@trigger.dev/platform@1.3.1(zod@4.4.3)': dependencies: zod: 4.4.3 From 55475955414334fcaa224aef316404889ca9b446 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Mon, 3 Aug 2026 16:47:26 +0100 Subject: [PATCH 11/17] fix(webapp): keep feedback dialog open for field errors --- apps/webapp/app/components/Feedback.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/apps/webapp/app/components/Feedback.tsx b/apps/webapp/app/components/Feedback.tsx index fc60bdad1b7..d7204dde41a 100644 --- a/apps/webapp/app/components/Feedback.tsx +++ b/apps/webapp/app/components/Feedback.tsx @@ -66,11 +66,11 @@ export function Feedback({ if ( navigation.formAction === "/resources/feedback" && navigation.state === "loading" && - (form.errors === undefined || form.errors.length === 0) + Object.keys(form.allErrors).length === 0 ) { setOpen(false); } - }, [navigation, form]); + }, [navigation.formAction, navigation.state, form.allErrors]); // Handle URL param functionality useEffect(() => { From f1edc1ccc18cef9ac9d6f8d193d394fb28cc38b5 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Mon, 3 Aug 2026 16:54:05 +0100 Subject: [PATCH 12/17] fix schema --- apps/webapp/app/services/billingLimit.schemas.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/apps/webapp/app/services/billingLimit.schemas.ts b/apps/webapp/app/services/billingLimit.schemas.ts index 6628e5cb22c..9153b1fce85 100644 --- a/apps/webapp/app/services/billingLimit.schemas.ts +++ b/apps/webapp/app/services/billingLimit.schemas.ts @@ -175,7 +175,7 @@ export type BillingLimitPageData = BillingLimitResult & { currentSpendCents: number; }; -/** Bridge webapp Zod schemas to BillingClient.fetch (separate Zod type instances). */ -export function asPlatformSchema(schema: z.ZodTypeAny) { - return schema as unknown as Parameters[1]; +/** Preserve a concrete schema type when passing it to BillingClient.fetch. */ +export function asPlatformSchema(schema: TSchema): TSchema { + return schema; } From e1e977299b7fa81098f3a2fe0f90410b7bfe7681 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Mon, 3 Aug 2026 16:59:56 +0100 Subject: [PATCH 13/17] lint --- apps/webapp/app/services/billingLimit.schemas.ts | 1 - 1 file changed, 1 deletion(-) diff --git a/apps/webapp/app/services/billingLimit.schemas.ts b/apps/webapp/app/services/billingLimit.schemas.ts index 9153b1fce85..8ffdca3fd57 100644 --- a/apps/webapp/app/services/billingLimit.schemas.ts +++ b/apps/webapp/app/services/billingLimit.schemas.ts @@ -1,4 +1,3 @@ -import type { BillingClient } from "@trigger.dev/platform"; import { z } from "zod"; /** From 88ac58892583a8898b13d05569ce33a87d523e02 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Mon, 3 Aug 2026 22:28:42 +0100 Subject: [PATCH 14/17] fix: preserve zod v4 configuration behavior --- AGENTS.md | 2 +- .../route.tsx | 12 +++++++----- apps/webapp/app/services/platform.v3.server.ts | 2 +- internal-packages/dashboard-agent/package.json | 2 +- 4 files changed, 10 insertions(+), 8 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index cc947c7a57d..e459a74c9c6 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -108,7 +108,7 @@ When modifying only server components (`apps/webapp/`, `apps/supervisor/`, etc.) ## Dependency Pinning -Zod is pinned to a single version across the entire monorepo (currently `3.25.76`). When adding zod to a new or existing package, use the **exact same version** as the rest of the repo - never a different version or a range. Mismatched zod versions cause runtime type incompatibilities (e.g., schemas from one package can't be used as body validators in another). +Zod is pinned to a single version across the entire monorepo (currently `4.4.3`). When adding zod to a new or existing package, use the **exact same version** as the rest of the repo - never a different version or a range. Mismatched zod versions cause runtime type incompatibilities (e.g., schemas from one package can't be used as body validators in another). ## Architecture Overview diff --git a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx index d3402165808..1c397e3eddf 100644 --- a/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx +++ b/apps/webapp/app/routes/_app.orgs.$organizationSlug.projects.$projectParam.env.$envParam.environment-variables.new/route.tsx @@ -247,12 +247,14 @@ export default function Page() { const [selectedEnvironmentIds, setSelectedEnvironmentIds] = useState>(new Set()); const [selectedBranchId, setSelectedBranchId] = useState(undefined); - const branchEnvironments = environments.filter((env) => env.branchName); - const nonBranchEnvironments = environments.filter((env) => !env.branchName); - const selectedEnvironments = environments.filter((env) => selectedEnvironmentIds.has(env.id)); - const previewIsSelected = selectedEnvironments.some( - (env) => env.branchName !== null || env.type === "PREVIEW" + // TODO: we only support branch-specific env vars for Preview environments. + // This keeps setting consistent env vars across Dev/Staging/Prod easier. + const branchEnvironments = environments.filter( + (env) => env.type === "PREVIEW" && env.parentEnvironmentId !== null ); + const nonBranchEnvironments = environments.filter((env) => env.parentEnvironmentId === null); + const selectedEnvironments = environments.filter((env) => selectedEnvironmentIds.has(env.id)); + const previewIsSelected = selectedEnvironments.some((env) => env.type === "PREVIEW"); const isLoading = navigation.state !== "idle" && navigation.formMethod === "post"; diff --git a/apps/webapp/app/services/platform.v3.server.ts b/apps/webapp/app/services/platform.v3.server.ts index f8b524e3cdd..a533cafaf42 100644 --- a/apps/webapp/app/services/platform.v3.server.ts +++ b/apps/webapp/app/services/platform.v3.server.ts @@ -242,7 +242,7 @@ const MachineOverrideValues = z.object({ }); type MachineOverrideValues = z.infer; -const MachineOverrides = z.record(MachinePresetName, MachineOverrideValues.partial()); +const MachineOverrides = z.partialRecord(MachinePresetName, MachineOverrideValues.partial()); type MachineOverrides = z.infer; const MachinePresetOverrides = z.object({ diff --git a/internal-packages/dashboard-agent/package.json b/internal-packages/dashboard-agent/package.json index c0776e25f11..3bfe5f3f4a6 100644 --- a/internal-packages/dashboard-agent/package.json +++ b/internal-packages/dashboard-agent/package.json @@ -14,7 +14,7 @@ "@internal/dashboard-agent-db": "workspace:*", "@trigger.dev/sdk": "workspace:*", "ai": "^6.0.116", - "zod": "3.25.76" + "zod": "4.4.3" }, "devDependencies": { "@ai-sdk/provider": "3.0.8", From db3da91edb06226eef4234fdedf3e0b4c053a77c Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Tue, 4 Aug 2026 07:05:16 +0100 Subject: [PATCH 15/17] fix: declare zod peer dependencies --- .changeset/zod-4-support.md | 3 +++ packages/build/package.json | 6 +++++- packages/react-hooks/package.json | 6 ++++-- packages/rsc/package.json | 6 +++++- pnpm-lock.yaml | 9 +++++++++ 5 files changed, 26 insertions(+), 4 deletions(-) diff --git a/.changeset/zod-4-support.md b/.changeset/zod-4-support.md index fd739c5be76..4849b8624b4 100644 --- a/.changeset/zod-4-support.md +++ b/.changeset/zod-4-support.md @@ -1,5 +1,8 @@ --- +"@trigger.dev/build": minor "@trigger.dev/core": minor +"@trigger.dev/react-hooks": minor +"@trigger.dev/rsc": minor "@trigger.dev/sdk": minor "trigger.dev": minor "@trigger.dev/redis-worker": minor diff --git a/packages/build/package.json b/packages/build/package.json index c55ffbb687e..6d051d4741a 100644 --- a/packages/build/package.json +++ b/packages/build/package.json @@ -92,7 +92,11 @@ "esbuild": "^0.23.0", "rimraf": "6.0.1", "tshy": "^3.0.2", - "tsx": "4.17.0" + "tsx": "4.17.0", + "zod": "4.4.3" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" }, "engines": { "node": ">=18.20.0" diff --git a/packages/react-hooks/package.json b/packages/react-hooks/package.json index cc8bc3dd300..b7039450a88 100644 --- a/packages/react-hooks/package.json +++ b/packages/react-hooks/package.json @@ -46,11 +46,13 @@ "@types/react-dom": "*", "rimraf": "^6.0.1", "tshy": "^3.0.2", - "tsx": "4.17.0" + "tsx": "4.17.0", + "zod": "4.4.3" }, "peerDependencies": { "react": "^18.0 || ^19.0 || ^19.0.0-rc", - "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc" + "react-dom": "^18.0 || ^19.0 || ^19.0.0-rc", + "zod": "^3.25.0 || ^4.0.0" }, "engines": { "node": ">=18.20.0" diff --git a/packages/rsc/package.json b/packages/rsc/package.json index eb6396223dc..c0232aeefae 100644 --- a/packages/rsc/package.json +++ b/packages/rsc/package.json @@ -50,7 +50,11 @@ "@types/react-dom": "*", "rimraf": "^6.0.1", "tshy": "^3.0.2", - "tsx": "4.17.0" + "tsx": "4.17.0", + "zod": "4.4.3" + }, + "peerDependencies": { + "zod": "^3.25.0 || ^4.0.0" }, "engines": { "node": ">=18.20.0" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 758dd3cd2d0..5be7a7cf6bd 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1496,6 +1496,9 @@ importers: tsx: specifier: 4.17.0 version: 4.17.0 + zod: + specifier: 4.4.3 + version: 4.4.3 packages/cli-v3: dependencies: @@ -1996,6 +1999,9 @@ importers: tsx: specifier: 4.17.0 version: 4.17.0 + zod: + specifier: 4.4.3 + version: 4.4.3 packages/redis-worker: dependencies: @@ -2085,6 +2091,9 @@ importers: tsx: specifier: 4.17.0 version: 4.17.0 + zod: + specifier: 4.4.3 + version: 4.4.3 packages/schema-to-json: dependencies: From c05c5ca93402eef679f63449a6711942976d0d33 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Tue, 4 Aug 2026 08:01:11 +0100 Subject: [PATCH 16/17] fix: use zod-error v2 with zod 4 --- apps/webapp/package.json | 2 +- internal-packages/clickhouse/package.json | 2 +- packages/core/package.json | 2 +- pnpm-lock.yaml | 20 +++++++++++--------- 4 files changed, 14 insertions(+), 12 deletions(-) diff --git a/apps/webapp/package.json b/apps/webapp/package.json index 44e78de653a..0a43858a722 100644 --- a/apps/webapp/package.json +++ b/apps/webapp/package.json @@ -217,7 +217,7 @@ "uuid": "^14.0.0", "ws": "^8.11.0", "zod": "4.4.3", - "zod-error": "1.5.0", + "zod-error": "2.0.0", "zod-validation-error": "^5.0.0" }, "devDependencies": { diff --git a/internal-packages/clickhouse/package.json b/internal-packages/clickhouse/package.json index c3f87cf62dc..719b979ba90 100644 --- a/internal-packages/clickhouse/package.json +++ b/internal-packages/clickhouse/package.json @@ -11,7 +11,7 @@ "@internal/tsql": "workspace:*", "@trigger.dev/core": "workspace:*", "zod": "4.4.3", - "zod-error": "1.5.0" + "zod-error": "2.0.0" }, "devDependencies": { "@internal/testcontainers": "workspace:*", diff --git a/packages/core/package.json b/packages/core/package.json index 1003f7514b8..43b7188352a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -225,7 +225,7 @@ "std-env": "^3.8.1", "tinyexec": "^0.3.2", "uncrypto": "^0.1.3", - "zod-error": "1.5.0", + "zod-error": "2.0.0", "zod-validation-error": "^5.0.0" }, "devDependencies": { diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 5be7a7cf6bd..ff054afc53e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -781,8 +781,8 @@ importers: specifier: 4.4.3 version: 4.4.3 zod-error: - specifier: 1.5.0 - version: 1.5.0 + specifier: 2.0.0 + version: 2.0.0(zod@4.4.3) zod-validation-error: specifier: ^5.0.0 version: 5.0.0(zod@4.4.3) @@ -979,8 +979,8 @@ importers: specifier: 4.4.3 version: 4.4.3 zod-error: - specifier: 1.5.0 - version: 1.5.0 + specifier: 2.0.0 + version: 2.0.0(zod@4.4.3) devDependencies: '@internal/testcontainers': specifier: workspace:* @@ -1849,8 +1849,8 @@ importers: specifier: ^0.1.3 version: 0.1.3 zod-error: - specifier: 1.5.0 - version: 1.5.0 + specifier: 2.0.0 + version: 2.0.0(zod@4.4.3) zod-validation-error: specifier: ^5.0.0 version: 5.0.0(zod@4.4.3) @@ -15607,8 +15607,10 @@ packages: resolution: {integrity: sha512-zK7YHHz4ZXpW89AHXUPbQVGKI7uvkd3hzusTdotCg1UxyaVtg0zFJSTfW/Dq5f7OBBVnq6cZIaC8Ti4hb6dtCA==} engines: {node: '>= 14'} - zod-error@1.5.0: - resolution: {integrity: sha512-zzopKZ/skI9iXpqCEPj+iLCKl9b88E43ehcU+sbRoHuwGd9F1IDVGQ70TyO6kmfiRL1g4IXkjsXK+g1gLYl4WQ==} + zod-error@2.0.0: + resolution: {integrity: sha512-ivSoPm7h5SEGG8GUvQ7YTwrnEx36qNmUIU5hSIGcqcdl7+ZFoTrYXAdQEs3KP/rkihrrR9REEdOeGU+fWdMoNw==} + peerDependencies: + zod: 4.4.3 zod-to-json-schema@3.24.5: resolution: {integrity: sha512-/AuWwMP+YqiPbsJx5D6TfgRTc4kTLjsh5SOcd4bLsfUg2RcEXrFMJl1DGgdHy2aCfsIA/cr/1JM0xcB2GZji8g==} @@ -31688,7 +31690,7 @@ snapshots: compress-commons: 6.0.2 readable-stream: 4.7.0 - zod-error@1.5.0: + zod-error@2.0.0(zod@4.4.3): dependencies: zod: 4.4.3 From d00af71bdd100295420199461887db6192be0686 Mon Sep 17 00:00:00 2001 From: Chris Arderne Date: Tue, 4 Aug 2026 10:46:39 +0100 Subject: [PATCH 17/17] fix: preserve empty task payloads and validation messages --- apps/webapp/app/routes/account._index/route.tsx | 2 +- apps/webapp/app/routes/account.tokens/route.tsx | 2 +- packages/core/src/v3/schemas/api-type.test.ts | 8 ++++++++ packages/core/src/v3/schemas/api.ts | 4 ++-- packages/core/src/v3/schemas/batchItemNDJSON.test.ts | 9 +++++++++ 5 files changed, 21 insertions(+), 4 deletions(-) diff --git a/apps/webapp/app/routes/account._index/route.tsx b/apps/webapp/app/routes/account._index/route.tsx index 03da2f9987a..94969a30239 100644 --- a/apps/webapp/app/routes/account._index/route.tsx +++ b/apps/webapp/app/routes/account._index/route.tsx @@ -92,7 +92,7 @@ function createSchema( name: z .string({ error: "You must enter a name" }) .min(2, "Your name must be at least 2 characters long") - .max(50), + .max(50, "Your name must be 50 characters or less"), email: emailSchema.pipe( z.string().superRefine((email, ctx) => { if (constraints.isEmailUnique === undefined) { diff --git a/apps/webapp/app/routes/account.tokens/route.tsx b/apps/webapp/app/routes/account.tokens/route.tsx index 3889cbdf592..63baccf8f2b 100644 --- a/apps/webapp/app/routes/account.tokens/route.tsx +++ b/apps/webapp/app/routes/account.tokens/route.tsx @@ -170,7 +170,7 @@ const CreateTokenSchema = z.discriminatedUnion("action", [ tokenName: z .string({ error: "You must enter a name" }) .min(2, "Your name must be at least 2 characters long") - .max(50), + .max(50, "Your name must be 50 characters or less"), // Optional — when no RBAC plugin is installed the UI hides the // dropdown and submits no roleId; the action passes that through // and createPersonalAccessToken just doesn't write a TokenRole. diff --git a/packages/core/src/v3/schemas/api-type.test.ts b/packages/core/src/v3/schemas/api-type.test.ts index 39fb3131ce1..73c697969cd 100644 --- a/packages/core/src/v3/schemas/api-type.test.ts +++ b/packages/core/src/v3/schemas/api-type.test.ts @@ -141,6 +141,14 @@ describe("InitializeDeploymentRequestBody", () => { }); describe("TriggerTaskRequestBody", () => { + it("accepts a missing payload", () => { + const result = TriggerTaskRequestBody.safeParse({ + options: {}, + }); + + expect(result.success).toBe(true); + }); + it("accepts application/store payload as a non-empty string", () => { const result = TriggerTaskRequestBody.safeParse({ payload: "packets/payloads/file.json", diff --git a/packages/core/src/v3/schemas/api.ts b/packages/core/src/v3/schemas/api.ts index 245351bf0c5..c16521270cc 100644 --- a/packages/core/src/v3/schemas/api.ts +++ b/packages/core/src/v3/schemas/api.ts @@ -202,7 +202,7 @@ const ConcurrencyKeySchema = z.union([z.string(), z.number()]).transform((value) export const TriggerTaskRequestBody = z .object({ - payload: z.any(), + payload: z.any().optional(), context: z.any().optional(), options: z .object({ @@ -312,7 +312,7 @@ export type BatchTriggerTaskRequestBody = z.infer { }); describe("BatchTriggerTaskItem", () => { + it("accepts a missing payload", () => { + const result = BatchTriggerTaskItem.safeParse({ + task: "user-workflow-tick", + options: {}, + }); + + expect(result.success).toBe(true); + }); + it("coerces a numeric concurrencyKey to a string", () => { const result = BatchTriggerTaskItem.safeParse({ task: "user-workflow-tick",