Skip to content

Commit 9944f44

Browse files
authored
chore: upgrade to zod v4 (#4039)
## Zod v4 compatibility Upgrades the monorepo's internal zod version from 3.x to 4.4.3 and widens the peer dependency on all published packages to `"^3.25.56 || ^4.0.0"`. ### For library consumers No breaking change if you are on zod 3.25+. The peer dependency range is widened, not narrowed — your existing zod 3.25+ install continues to satisfy it and the library code is runtime-compatible with both versions. Zod 4.x is now also supported. Bumped as minor across all affected packages. Minimum zod 3 version is **3.25.56** (not 3.0.0). ### API compatibility Zod types appear in the public API of `@trigger.dev/core` and `@trigger.dev/redis-worker` (generic constraints like `T extends z.ZodTypeAny`, exported schema values typed as `z.ZodType<T>`, and `WorkerCatalog`'s schema field). These types exist in both zod 3 and zod 4, so TypeScript consumers on either version should resolve them without errors — the shapes are structurally compatible across versions. If you pass your own zod schemas into library APIs (e.g. `zodfetch`, `zodShapeStream`, `WorkerCatalog`), schemas from zod 3.25+ and zod 4.x are both accepted. `WorkerCatalog` uses `z.ZodFirstPartySchemaTypes`, which is present in zod 4 via a compatibility alias but marked `@deprecated`. This is intentional — the type is still fully functional, and we've left it in place to avoid a breaking API change. ## TODO list before merging - [x] bump @trigger.dev/platform dep to 1.2.1 when that becomes available
1 parent 0350e00 commit 9944f44

181 files changed

Lines changed: 1418 additions & 760 deletions

File tree

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.changeset/zod-4-support.md

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
"@trigger.dev/build": minor
3+
"@trigger.dev/core": minor
4+
"@trigger.dev/react-hooks": minor
5+
"@trigger.dev/rsc": minor
6+
"@trigger.dev/sdk": minor
7+
"trigger.dev": minor
8+
"@trigger.dev/redis-worker": minor
9+
"@trigger.dev/schema-to-json": minor
10+
---
11+
12+
Trigger.dev now uses Zod 4 by default. Projects using Zod 3.25.56 or later 3.x releases remain supported.
13+
14+
Zod remains a runtime dependency of packages that execute schemas, so existing and new installations continue to receive it automatically. The matching peer dependency range allows package managers to reuse either a compatible Zod 3 or Zod 4 installation from your project.

AGENTS.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -134,7 +134,7 @@ When a **server-only** change (`apps/webapp/`, `apps/supervisor/`, etc., with no
134134

135135
## Dependency Pinning
136136

137-
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).
137+
Zod is pinned to a single version across the entire monorepo. 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).
138138

139139
## Architecture Overview
140140

apps/supervisor/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,7 @@
2323
"prom-client": "^15.1.0",
2424
"socket.io": "4.8.3",
2525
"std-env": "^3.8.0",
26-
"zod": "3.25.76"
26+
"zod": "4.5.4"
2727
},
2828
"devDependencies": {
2929
"@internal/testcontainers": "workspace:*",

apps/webapp/app/components/Feedback.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import {
55
getTextareaProps,
66
useForm,
77
} from "@conform-to/react";
8-
import { parseWithZod } from "@conform-to/zod";
8+
import { parseWithZod } from "@conform-to/zod/v4";
99
import { InformationCircleIcon, ArrowUpCircleIcon } from "@heroicons/react/20/solid";
1010
import { EnvelopeIcon, ShieldCheckIcon } from "@heroicons/react/24/solid";
1111
import { Form, useActionData, useLocation, useNavigation, useSearchParams } from "@remix-run/react";
@@ -130,7 +130,7 @@ export function Feedback({
130130
<Fieldset className="max-w-full gap-y-3">
131131
<input
132132
value={location.pathname}
133-
{...getInputProps(fields.path, { type: "hidden" })}
133+
{...getInputProps(fields.path, { type: "hidden", value: false })}
134134
/>
135135
<InputGroup className="max-w-full">
136136
{type === "feature" && (

apps/webapp/app/components/billing/BillingAlertsSection.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getFormProps, getInputProps, useForm, type SubmissionResult } from "@conform-to/react";
2-
import { parseWithZod } from "@conform-to/zod";
2+
import { parseWithZod } from "@conform-to/zod/v4";
33
import { PlusIcon, TrashIcon } from "@heroicons/react/20/solid";
44
import { Form, useActionData, useSearchParams } from "@remix-run/react";
55
import { Fragment, useEffect, useMemo, useRef, useState } from "react";

apps/webapp/app/components/billing/BillingLimitConfigSection.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getFormProps, useForm, type SubmissionResult } from "@conform-to/react";
22

3-
import { parseWithZod } from "@conform-to/zod";
3+
import { parseWithZod } from "@conform-to/zod/v4";
44
import { Form, useActionData } from "@remix-run/react";
55
import { useEffect, useMemo, useRef, useState } from "react";
66
import { z } from "zod";
@@ -38,7 +38,7 @@ export const billingLimitFormSchema = z.discriminatedUnion("mode", [
3838
z.object({
3939
mode: z.literal("custom"),
4040
amount: z.coerce
41-
.number({ invalid_type_error: "Not a valid amount" })
41+
.number({ error: "Not a valid amount" })
4242
.positive("Amount must be greater than 0"),
4343
cancelInProgressRuns: z
4444
.preprocess((v) => v === "on" || v === true || v === "true", z.boolean())

apps/webapp/app/components/billing/BillingLimitRecoveryPanel.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getFormProps, useForm, type SubmissionResult } from "@conform-to/react";
2-
import { parseWithZod } from "@conform-to/zod";
2+
import { parseWithZod } from "@conform-to/zod/v4";
33
import { Form, useActionData, useNavigation } from "@remix-run/react";
44
import { useEffect, useMemo, useRef, useState } from "react";
55
import { z } from "zod";
@@ -21,7 +21,7 @@ export const billingLimitRecoveryFormSchema = z
2121
.object({
2222
action: z.enum(["increase", "remove"]),
2323
newAmount: z.coerce
24-
.number({ invalid_type_error: "Not a valid amount" })
24+
.number({ error: "Not a valid amount" })
2525
.positive("Amount must be greater than 0")
2626
.optional(),
2727
resumeMode: z.enum(["queue", "new_only"]),
@@ -88,7 +88,7 @@ export function BillingLimitRecoveryPanel({
8888
},
8989
defaultValue: {
9090
action: "increase",
91-
newAmount: suggestedNewLimitDollars,
91+
newAmount: String(suggestedNewLimitDollars),
9292
resumeMode: "queue",
9393
},
9494
});

apps/webapp/app/components/errors/ConfigureErrorAlerts.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { getFormProps, getInputProps, useForm } from "@conform-to/react";
22
import { GlobeLinesIcon } from "~/assets/icons/GlobeLinesIcon";
3-
import { parseWithZod } from "@conform-to/zod";
3+
import { parseWithZod } from "@conform-to/zod/v4";
44
import { EnvelopeIcon, HashtagIcon, LockClosedIcon, XMarkIcon } from "@heroicons/react/20/solid";
55
import { BellAlertIcon } from "@heroicons/react/24/solid";
66
import { useFetcher, useNavigate } from "@remix-run/react";

apps/webapp/app/components/metrics/QueryWidget.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -55,7 +55,7 @@ const chartConfigOptions = {
5555
sortByColumn: z.string().nullable(),
5656
sortDirection: SortDirection,
5757
aggregation: AggregationType,
58-
seriesColors: z.record(z.string()).optional(),
58+
seriesColors: z.record(z.string(), z.string()).optional(),
5959
};
6060

6161
const ChartConfiguration = z.object({ ...chartConfigOptions });

apps/webapp/app/components/runs/v3/ReplayRunDialog.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { getFormProps, getInputProps, getSelectProps, useForm } from "@conform-to/react";
2-
import { parseWithZod } from "@conform-to/zod";
2+
import { parseWithZod } from "@conform-to/zod/v4";
33
import { RectangleStackIcon } from "@heroicons/react/20/solid";
44
import { DialogClose } from "@radix-ui/react-dialog";
55
import { Form, useActionData, useNavigation, useParams, useSubmit } from "@remix-run/react";

0 commit comments

Comments
 (0)