zod 4.4.0 → 4.4.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (37) hide show
  1. package/README.md +0 -17
  2. package/package.json +1 -1
  3. package/src/v3/tests/all-errors.test.ts +3 -3
  4. package/src/v3/tests/object.test.ts +5 -5
  5. package/src/v3/tests/partials.test.ts +3 -3
  6. package/src/v4/classic/schemas.ts +23 -3
  7. package/src/v4/classic/tests/assignability.test.ts +6 -0
  8. package/src/v4/classic/tests/discriminated-unions.test.ts +3 -0
  9. package/src/v4/classic/tests/preprocess-types.test.ts +26 -0
  10. package/src/v4/classic/tests/preprocess.test.ts +44 -0
  11. package/src/v4/classic/tests/to-json-schema.test.ts +22 -0
  12. package/src/v4/classic/tests/tuple.test.ts +30 -13
  13. package/src/v4/core/api.ts +6 -4
  14. package/src/v4/core/json-schema-processors.ts +2 -1
  15. package/src/v4/core/schemas.ts +53 -23
  16. package/src/v4/core/to-json-schema.ts +1 -0
  17. package/src/v4/core/versions.ts +1 -1
  18. package/src/v4/mini/schemas.ts +1 -1
  19. package/src/v4/mini/tests/index.test.ts +5 -0
  20. package/v4/classic/schemas.cjs +10 -2
  21. package/v4/classic/schemas.d.cts +8 -2
  22. package/v4/classic/schemas.d.ts +8 -2
  23. package/v4/classic/schemas.js +9 -1
  24. package/v4/core/api.d.cts +6 -4
  25. package/v4/core/api.d.ts +6 -4
  26. package/v4/core/json-schema-processors.cjs +2 -1
  27. package/v4/core/json-schema-processors.js +2 -1
  28. package/v4/core/schemas.cjs +27 -25
  29. package/v4/core/schemas.d.cts +13 -0
  30. package/v4/core/schemas.d.ts +13 -0
  31. package/v4/core/schemas.js +26 -24
  32. package/v4/core/to-json-schema.cjs +2 -0
  33. package/v4/core/to-json-schema.js +2 -0
  34. package/v4/core/versions.cjs +1 -1
  35. package/v4/core/versions.js +1 -1
  36. package/v4/mini/schemas.d.cts +1 -1
  37. package/v4/mini/schemas.d.ts +1 -1
package/README.md CHANGED
@@ -31,23 +31,6 @@
31
31
  <br/>
32
32
  <br/>
33
33
 
34
- <h2 align="center">Featured sponsor: Jazz</h2>
35
-
36
- <div align="center">
37
- <a href="https://jazz.tools/?utm_source=zod">
38
- <picture width="85%" >
39
- <source media="(prefers-color-scheme: dark)" srcset="https://raw.githubusercontent.com/garden-co/jazz/938f6767e46cdfded60e50d99bf3b533f4809c68/homepage/homepage/public/Zod%20sponsor%20message.png">
40
- <img alt="jazz logo" src="https://raw.githubusercontent.com/garden-co/jazz/938f6767e46cdfded60e50d99bf3b533f4809c68/homepage/homepage/public/Zod%20sponsor%20message.png" width="85%">
41
- </picture>
42
- </a>
43
- <br/>
44
- <p><sub>Learn more about <a target="_blank" rel="noopener noreferrer" href="mailto:sponsorship@colinhacks.com">featured sponsorships</a></sub></p>
45
- </div>
46
-
47
- <br/>
48
- <br/>
49
- <br/>
50
-
51
34
  ### [Read the docs →](https://zod.dev/api)
52
35
 
53
36
  <br/>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod",
3
- "version": "4.4.0",
3
+ "version": "4.4.2",
4
4
  "type": "module",
5
5
  "license": "MIT",
6
6
  "author": "Colin McDonnell <zod@colinhacks.com>",
@@ -16,7 +16,7 @@ type TestFormErrors = z.inferFlattenedErrors<typeof Test>;
16
16
  test("default flattened errors type inference", () => {
17
17
  type TestTypeErrors = {
18
18
  formErrors: string[];
19
- fieldErrors: { [P in keyof z.TypeOf<typeof Test>]?: string[] | undefined };
19
+ fieldErrors: { [P in keyof z.TypeOf<typeof Test>]?: string[] };
20
20
  };
21
21
 
22
22
  util.assertEqual<z.inferFlattenedErrors<typeof Test>, TestTypeErrors>(true);
@@ -28,7 +28,7 @@ test("custom flattened errors type inference", () => {
28
28
  type TestTypeErrors = {
29
29
  formErrors: ErrorType[];
30
30
  fieldErrors: {
31
- [P in keyof z.TypeOf<typeof Test>]?: ErrorType[] | undefined;
31
+ [P in keyof z.TypeOf<typeof Test>]?: ErrorType[];
32
32
  };
33
33
  };
34
34
 
@@ -40,7 +40,7 @@ test("custom flattened errors type inference", () => {
40
40
  test("form errors type inference", () => {
41
41
  type TestTypeErrors = {
42
42
  formErrors: string[];
43
- fieldErrors: { [P in keyof z.TypeOf<typeof Test>]?: string[] | undefined };
43
+ fieldErrors: { [P in keyof z.TypeOf<typeof Test>]?: string[] };
44
44
  };
45
45
 
46
46
  util.assertEqual<z.inferFlattenedErrors<typeof Test>, TestTypeErrors>(true);
@@ -212,9 +212,9 @@ test("inferred merged object type with optional properties", async () => {
212
212
  .object({ a: z.string(), b: z.string().optional() })
213
213
  .merge(z.object({ a: z.string().optional(), b: z.string() }));
214
214
  type Merged = z.infer<typeof Merged>;
215
- util.assertEqual<Merged, { a?: string; b: string }>(true);
215
+ util.assertEqual<Merged, { a?: string | undefined; b: string }>(true);
216
216
  // todo
217
- // util.assertEqual<Merged, { a?: string; b: string }>(true);
217
+ // util.assertEqual<Merged, { a?: string | undefined; b: string }>(true);
218
218
  });
219
219
 
220
220
  test("inferred unioned object type with optional properties", async () => {
@@ -223,7 +223,7 @@ test("inferred unioned object type with optional properties", async () => {
223
223
  z.object({ a: z.string().optional(), b: z.string() }),
224
224
  ]);
225
225
  type Unioned = z.infer<typeof Unioned>;
226
- util.assertEqual<Unioned, { a: string; b?: string } | { a?: string; b: string }>(true);
226
+ util.assertEqual<Unioned, { a: string; b?: string | undefined } | { a?: string | undefined; b: string }>(true);
227
227
  });
228
228
 
229
229
  test("inferred enum type", async () => {
@@ -245,13 +245,13 @@ test("inferred enum type", async () => {
245
245
  test("inferred partial object type with optional properties", async () => {
246
246
  const Partial = z.object({ a: z.string(), b: z.string().optional() }).partial();
247
247
  type Partial = z.infer<typeof Partial>;
248
- util.assertEqual<Partial, { a?: string; b?: string }>(true);
248
+ util.assertEqual<Partial, { a?: string | undefined; b?: string | undefined }>(true);
249
249
  });
250
250
 
251
251
  test("inferred picked object type with optional properties", async () => {
252
252
  const Picked = z.object({ a: z.string(), b: z.string().optional() }).pick({ b: true });
253
253
  type Picked = z.infer<typeof Picked>;
254
- util.assertEqual<Picked, { b?: string }>(true);
254
+ util.assertEqual<Picked, { b?: string | undefined }>(true);
255
255
  });
256
256
 
257
257
  test("inferred type for unknown/any keys", () => {
@@ -21,7 +21,7 @@ test("shallow inference", () => {
21
21
  name?: string | undefined;
22
22
  age?: number | undefined;
23
23
  outer?: { inner: string } | undefined;
24
- array?: { asdf: string }[];
24
+ array?: { asdf: string }[] | undefined;
25
25
  };
26
26
  util.assertEqual<shallow, correct>(true);
27
27
  });
@@ -41,7 +41,7 @@ test("deep partial inference", () => {
41
41
  asdf.parse("asdf");
42
42
  type deep = z.infer<typeof deep>;
43
43
  type correct = {
44
- array?: { asdf?: string }[];
44
+ array?: { asdf?: string | undefined }[] | undefined;
45
45
  name?: string | undefined;
46
46
  age?: number | undefined;
47
47
  outer?: { inner?: string | undefined } | undefined;
@@ -118,7 +118,7 @@ test("deep partial inference", () => {
118
118
  asdf?: string | undefined;
119
119
  }[]
120
120
  | undefined;
121
- tuple?: [{ value?: string }] | undefined;
121
+ tuple?: [{ value?: string | undefined }] | undefined;
122
122
  };
123
123
  util.assertEqual<expected, partialed>(true);
124
124
  });
@@ -1617,7 +1617,7 @@ export const ZodDiscriminatedUnion: core.$constructor<ZodDiscriminatedUnion> = /
1617
1617
  );
1618
1618
 
1619
1619
  export function discriminatedUnion<
1620
- Types extends readonly [core.$ZodTypeDiscriminable, ...core.$ZodTypeDiscriminable[]],
1620
+ Types extends readonly [core.$ZodTypeDiscriminable<Disc>, ...core.$ZodTypeDiscriminable<Disc>[]],
1621
1621
  Disc extends string,
1622
1622
  >(
1623
1623
  discriminator: Disc,
@@ -2361,6 +2361,22 @@ export function invertCodec<A extends core.SomeType, B extends core.SomeType>(co
2361
2361
  }) as any;
2362
2362
  }
2363
2363
 
2364
+ // ZodPreprocess
2365
+ export interface ZodPreprocess<B extends core.SomeType = core.$ZodType>
2366
+ extends ZodPipe<core.$ZodTransform, B>,
2367
+ core.$ZodPreprocess<B> {
2368
+ "~standard": ZodStandardSchemaWithJSON<this>;
2369
+ _zod: core.$ZodPreprocessInternals<B>;
2370
+ def: core.$ZodPreprocessDef<B>;
2371
+ }
2372
+ export const ZodPreprocess: core.$constructor<ZodPreprocess> = /*@__PURE__*/ core.$constructor(
2373
+ "ZodPreprocess",
2374
+ (inst, def) => {
2375
+ ZodPipe.init(inst, def);
2376
+ core.$ZodPreprocess.init(inst, def);
2377
+ }
2378
+ );
2379
+
2364
2380
  // ZodReadonly
2365
2381
  export interface ZodReadonly<T extends core.SomeType = core.$ZodType>
2366
2382
  extends _ZodType<core.$ZodReadonlyInternals<T>>,
@@ -2645,6 +2661,10 @@ export function json(params?: string | core.$ZodCustomParams): ZodJSONSchema {
2645
2661
  export function preprocess<A, U extends core.SomeType, B = unknown>(
2646
2662
  fn: (arg: B, ctx: core.$RefinementCtx) => A,
2647
2663
  schema: U
2648
- ): ZodPipe<ZodTransform<A, B>, U> {
2649
- return pipe(transform(fn as any), schema as any) as any;
2664
+ ): ZodPreprocess<U> {
2665
+ return new ZodPreprocess({
2666
+ type: "pipe",
2667
+ in: transform(fn as any) as any as core.$ZodTransform,
2668
+ out: schema as any as core.$ZodType,
2669
+ }) as any;
2650
2670
  }
@@ -143,6 +143,12 @@ test("assignability", () => {
143
143
  z.unknown().pipe(z.number()) satisfies z.core.$ZodPipe;
144
144
  z.unknown().pipe(z.number()) satisfies z.ZodPipe;
145
145
 
146
+ // $ZodPreprocess
147
+ z.preprocess((v) => v, z.number()) satisfies z.core.$ZodPreprocess;
148
+ z.preprocess((v) => v, z.number()) satisfies z.ZodPreprocess;
149
+ z.preprocess((v) => v, z.number()) satisfies z.core.$ZodPipe<z.core.$ZodTransform, z.ZodNumber>;
150
+ z.preprocess((v) => v, z.number()) satisfies z.ZodPipe<z.core.$ZodTransform, z.ZodNumber>;
151
+
146
152
  // $ZodSuccess
147
153
  z.success(z.string()) satisfies z.core.$ZodSuccess;
148
154
  z.success(z.string()) satisfies z.ZodSuccess;
@@ -264,6 +264,9 @@ test("valid discriminator value, invalid data", () => {
264
264
  });
265
265
 
266
266
  test("wrong schema - missing discriminator", () => {
267
+ // @ts-expect-error missing discriminator property
268
+ z.discriminatedUnion("type", [z.object({ value: z.string() })]);
269
+
267
270
  try {
268
271
  z.discriminatedUnion("type", [
269
272
  z.object({ type: z.literal("a"), a: z.string() }),
@@ -0,0 +1,26 @@
1
+ import { expectTypeOf, test } from "vitest";
2
+ import * as z from "zod/v4";
3
+
4
+ test("ZodPreprocess<B> assignable to ZodPipe<$ZodTransform, B>", () => {
5
+ const pre = z.preprocess((v) => v, z.string().optional());
6
+ const _asPipe: z.ZodPipe<z.core.$ZodTransform, z.ZodOptional<z.ZodString>> = pre;
7
+ const _asCorePipe: z.core.$ZodPipe<z.core.$ZodTransform, z.ZodOptional<z.ZodString>> = pre;
8
+ expectTypeOf(_asPipe).toMatchTypeOf<z.ZodPipe>();
9
+ expectTypeOf(_asCorePipe).toMatchTypeOf<z.core.$ZodPipe>();
10
+ });
11
+
12
+ test("ZodPreprocess optin/optout defer to B", () => {
13
+ const optionalInside = z.preprocess((v) => v, z.string().optional());
14
+ expectTypeOf<(typeof optionalInside)["_zod"]["optin"]>().toEqualTypeOf<"optional">();
15
+ expectTypeOf<(typeof optionalInside)["_zod"]["optout"]>().toEqualTypeOf<"optional">();
16
+
17
+ const required = z.preprocess((v) => v, z.string());
18
+ expectTypeOf<(typeof required)["_zod"]["optin"]>().toEqualTypeOf<"optional" | undefined>();
19
+ expectTypeOf<(typeof required)["_zod"]["optout"]>().toEqualTypeOf<"optional" | undefined>();
20
+ });
21
+
22
+ test("ZodPreprocess input/output inference", () => {
23
+ const pre = z.preprocess((v) => v, z.number().optional());
24
+ expectTypeOf<z.output<typeof pre>>().toEqualTypeOf<number | undefined>();
25
+ expectTypeOf<z.input<typeof pre>>().toEqualTypeOf<unknown>();
26
+ });
@@ -280,3 +280,47 @@ test("perform transform with non-fatal issues", () => {
280
280
  ]]
281
281
  `);
282
282
  });
283
+
284
+ // https://github.com/colinhacks/zod/issues/5917
285
+ test("optional propagates through preprocess inside object", () => {
286
+ const outer = z.object({ x: z.preprocess((v) => v, z.number()).optional() });
287
+ const inner = z.object({ x: z.preprocess((v) => v, z.number().optional()) });
288
+
289
+ expect(outer.safeParse({}).success).toBe(true);
290
+ expect(inner.safeParse({}).success).toBe(true);
291
+
292
+ expect(outer.safeParse({ x: 1 })).toEqual({ success: true, data: { x: 1 } });
293
+ expect(inner.safeParse({ x: 1 })).toEqual({ success: true, data: { x: 1 } });
294
+
295
+ expect(inner._zod.def.shape.x._zod.optin).toBe("optional");
296
+ expect(inner._zod.def.shape.x._zod.optout).toBe("optional");
297
+ });
298
+
299
+ test("preprocess is a structural subtype of ZodPipe", () => {
300
+ const schema = z.preprocess((v) => v, z.string());
301
+ expect(schema).toBeInstanceOf(z.ZodPipe);
302
+ expect(schema).toBeInstanceOf(z.ZodPreprocess);
303
+ expect(schema._zod.def.type).toBe("pipe");
304
+ });
305
+
306
+ test("preprocess does not propagate values/propValues from inner schema", () => {
307
+ const inner = z.preprocess((v) => v, z.literal("test"));
308
+ expect(inner._zod.values).toBeUndefined();
309
+ expect(inner._zod.propValues).toBeUndefined();
310
+ });
311
+
312
+ test("preprocess as discriminator throws at construction (no propValues to inherit)", () => {
313
+ const schema = z.discriminatedUnion("kind", [
314
+ z.object({ kind: z.preprocess((v: any) => String(v).toUpperCase(), z.literal("A")), a: z.string() }),
315
+ z.object({ kind: z.preprocess((v: any) => String(v).toUpperCase(), z.literal("B")), b: z.number() }),
316
+ ]);
317
+ expect(() => schema.parse({ kind: "a", a: "x" })).toThrow(/Invalid discriminated union option/);
318
+ });
319
+
320
+ test("preprocess as record key does not restrict accepted keys", () => {
321
+ const schema = z.record(
322
+ z.preprocess((v: any) => String(v).toLowerCase(), z.enum(["a", "b"])),
323
+ z.string()
324
+ );
325
+ expect(schema.safeParse({ A: "x", B: "y" })).toEqual({ success: true, data: { a: "x", b: "y" } });
326
+ });
@@ -2896,6 +2896,28 @@ test("use output type for preprocess", () => {
2896
2896
  `);
2897
2897
  });
2898
2898
 
2899
+ test("strip output-side examples from input JSON schema for codec", () => {
2900
+ const codec = z
2901
+ .codec(z.string(), z.number(), { decode: (s) => Number(s), encode: (n) => String(n) })
2902
+ .meta({ examples: [42] });
2903
+
2904
+ expect(z.toJSONSchema(codec, { io: "input" })).toMatchInlineSnapshot(`
2905
+ {
2906
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
2907
+ "type": "string",
2908
+ }
2909
+ `);
2910
+ expect(z.toJSONSchema(codec, { io: "output" })).toMatchInlineSnapshot(`
2911
+ {
2912
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
2913
+ "examples": [
2914
+ 42,
2915
+ ],
2916
+ "type": "number",
2917
+ }
2918
+ `);
2919
+ });
2920
+
2899
2921
  // test("isTransforming", () => {
2900
2922
  // const tx = z.core.isTransforming;
2901
2923
  // expect(tx(z.string())).toEqual(false);
@@ -282,11 +282,10 @@ test("tuple result is dense when optional precedes a default", () => {
282
282
  expect(1 in out && 3 in out).toEqual(true);
283
283
  });
284
284
 
285
- test("tuple breaks and truncates on first absent-optional rejection", () => {
286
- // An `.optional()` slot that rejects `undefined` (e.g. via a refine) past
287
- // optStart must (a) swallow the issue, (b) truncate the result there, and
288
- // (c) NOT materialize any later defaults otherwise the parser would
289
- // happily fill in slots after a slot it just decided was missing/invalid.
285
+ test("tuple truncates absent optional rejections only when the output tail is optional", () => {
286
+ // An absent optional-output slot can only be swallowed when every later
287
+ // output slot is optional too. If a later default would make the output tail
288
+ // required, truncating would violate the tuple's output type.
290
289
  const refusesUndefined = z
291
290
  .string()
292
291
  .optional()
@@ -294,13 +293,15 @@ test("tuple breaks and truncates on first absent-optional rejection", () => {
294
293
 
295
294
  const trailingDefault = z.tuple([z.string(), refusesUndefined, z.string().default("d")]);
296
295
  const r1 = trailingDefault.safeParse(["alpha"]);
297
- expect(r1.success).toBe(true);
298
- expect(r1.data).toEqual(["alpha"]);
296
+ expect(r1.success).toBe(false);
297
+ expect(r1.error!.issues[0].path).toEqual([1]);
299
298
 
300
- // Optional slots BEFORE the rejected one collapse away with the truncate
301
- // (mirrors the trailing-trim behaviour for absent optionals).
299
+ // Optional slots BEFORE the rejected one still cannot hide a later required
300
+ // output slot.
302
301
  const beforeReject = z.tuple([z.string(), z.string().optional(), refusesUndefined, z.string().default("d")]);
303
- expect(beforeReject.safeParse(["alpha"]).data).toEqual(["alpha"]);
302
+ const r2 = beforeReject.safeParse(["alpha"]);
303
+ expect(r2.success).toBe(false);
304
+ expect(r2.error!.issues[0].path).toEqual([2]);
304
305
 
305
306
  // No default after — truncate still applies, no spurious issue surfaces.
306
307
  const noTrailingDefault = z.tuple([z.string(), refusesUndefined]);
@@ -309,7 +310,7 @@ test("tuple breaks and truncates on first absent-optional rejection", () => {
309
310
  expect(r3.data).toEqual(["alpha"]);
310
311
  });
311
312
 
312
- test("tuple breaks on absent-optional rejection under async parse", async () => {
313
+ test("tuple rejects absent optional before required output under async parse", async () => {
313
314
  const refusesUndefined = z
314
315
  .string()
315
316
  .optional()
@@ -317,8 +318,24 @@ test("tuple breaks on absent-optional rejection under async parse", async () =>
317
318
 
318
319
  const schema = z.tuple([z.string(), refusesUndefined, z.string().default("d")]);
319
320
  const r = await schema.safeParseAsync(["alpha"]);
320
- expect(r.success).toBe(true);
321
- expect(r.data).toEqual(["alpha"]);
321
+ expect(r.success).toBe(false);
322
+ expect(r.error!.issues[0].path).toEqual([1]);
323
+ });
324
+
325
+ test("tuple rejects absent exact optional before defaulted output", () => {
326
+ const schema = z.tuple([z.string(), z.string().exactOptional(), z.string().default("fallback")]);
327
+ expectTypeOf<typeof schema._output>().toEqualTypeOf<[string, string, string]>();
328
+
329
+ const missingExact = schema.safeParse(["alpha"]);
330
+ expect(missingExact.success).toBe(false);
331
+ expect(missingExact.error!.issues[0].path).toEqual([1]);
332
+
333
+ expect(schema.parse(["alpha", "bravo"])).toEqual(["alpha", "bravo", "fallback"]);
334
+ expect(schema.safeParse(["alpha", undefined]).success).toBe(false);
335
+
336
+ // With no later required output slot, exact optional still behaves like an
337
+ // omitted tuple tail and truncates cleanly.
338
+ expect(z.tuple([z.string(), z.string().exactOptional(), z.string().optional()]).parse(["alpha"])).toEqual(["alpha"]);
322
339
  });
323
340
 
324
341
  test("tuple preserves explicit undefined inside input even for optional-out schemas", () => {
@@ -1207,17 +1207,19 @@ export function _xor<const T extends readonly schemas.$ZodObject[]>(
1207
1207
  }
1208
1208
 
1209
1209
  // ZodDiscriminatedUnion
1210
- export interface $ZodTypeDiscriminableInternals extends schemas.$ZodTypeInternals {
1210
+ export interface $ZodTypeDiscriminableInternals<Disc extends string = string>
1211
+ extends schemas.$ZodTypeInternals<unknown, { [K in Disc]?: unknown }> {
1211
1212
  propValues: util.PropValues;
1212
1213
  }
1213
1214
 
1214
- export interface $ZodTypeDiscriminable extends schemas.$ZodType {
1215
- _zod: $ZodTypeDiscriminableInternals;
1215
+ export interface $ZodTypeDiscriminable<Disc extends string = string> extends schemas.$ZodType {
1216
+ _zod: $ZodTypeDiscriminableInternals<Disc>;
1216
1217
  }
1218
+
1217
1219
  export type $ZodDiscriminatedUnionParams = TypeParams<schemas.$ZodDiscriminatedUnion, "options" | "discriminator">;
1218
1220
  // @__NO_SIDE_EFFECTS__
1219
1221
  export function _discriminatedUnion<
1220
- Types extends [$ZodTypeDiscriminable, ...$ZodTypeDiscriminable[]],
1222
+ Types extends [$ZodTypeDiscriminable<Disc>, ...$ZodTypeDiscriminable<Disc>[]],
1221
1223
  Disc extends string,
1222
1224
  >(
1223
1225
  Class: util.SchemaClass<schemas.$ZodDiscriminatedUnion>,
@@ -525,7 +525,8 @@ export const catchProcessor: Processor<schemas.$ZodCatch> = (schema, ctx, json,
525
525
 
526
526
  export const pipeProcessor: Processor<schemas.$ZodPipe> = (schema, ctx, _json, params) => {
527
527
  const def = schema._zod.def as schemas.$ZodPipeDef;
528
- const innerType = ctx.io === "input" ? (def.in._zod.def.type === "transform" ? def.out : def.in) : def.out;
528
+ const inIsTransform = def.in._zod.traits.has("$ZodTransform");
529
+ const innerType = ctx.io === "input" ? (inIsTransform ? def.out : def.in) : def.out;
529
530
  process(innerType, ctx as any, params);
530
531
  const seen = ctx.seen.get(schema)!;
531
532
  seen.ref = innerType;
@@ -2677,14 +2677,14 @@ export const $ZodTuple: core.$constructor<$ZodTuple> = /*@__PURE__*/ core.$const
2677
2677
  payload.value = [];
2678
2678
  const proms: Promise<any>[] = [];
2679
2679
 
2680
- const reversedIndex = [...items].reverse().findIndex((item) => item._zod.optin !== "optional");
2681
- const optStart = reversedIndex === -1 ? 0 : items.length - reversedIndex;
2680
+ const optinStart = getTupleOptStart(items, "optin");
2681
+ const optoutStart = getTupleOptStart(items, "optout");
2682
2682
 
2683
2683
  if (!def.rest) {
2684
- if (input.length < optStart) {
2684
+ if (input.length < optinStart) {
2685
2685
  payload.issues.push({
2686
2686
  code: "too_small",
2687
- minimum: optStart,
2687
+ minimum: optinStart,
2688
2688
  inclusive: true,
2689
2689
  input,
2690
2690
  inst,
@@ -2706,9 +2706,8 @@ export const $ZodTuple: core.$constructor<$ZodTuple> = /*@__PURE__*/ core.$const
2706
2706
 
2707
2707
  // Run every item in parallel, collecting results into an indexed
2708
2708
  // array. The post-processing in `handleTupleResults` walks them in
2709
- // order so it can break on the first absent-optional error: once a
2710
- // slot rejects `undefined`, the tuple is malformed at that index and
2711
- // any later defaults must NOT fire.
2709
+ // order so it can decide whether an absent optional-output error can
2710
+ // truncate the tail or must be reported to preserve required output.
2712
2711
  const itemResults: ParsePayload[] = new Array(items.length);
2713
2712
  for (let i = 0; i < items.length; i++) {
2714
2713
  const r = items[i]._zod.run({ value: input[i], issues: [] }, ctx);
@@ -2737,11 +2736,20 @@ export const $ZodTuple: core.$constructor<$ZodTuple> = /*@__PURE__*/ core.$const
2737
2736
  }
2738
2737
  }
2739
2738
 
2740
- if (proms.length) return Promise.all(proms).then(() => handleTupleResults(itemResults, payload, items, input));
2741
- return handleTupleResults(itemResults, payload, items, input);
2739
+ if (proms.length) {
2740
+ return Promise.all(proms).then(() => handleTupleResults(itemResults, payload, items, input, optoutStart));
2741
+ }
2742
+ return handleTupleResults(itemResults, payload, items, input, optoutStart);
2742
2743
  };
2743
2744
  });
2744
2745
 
2746
+ function getTupleOptStart(items: readonly $ZodType[], key: "optin" | "optout") {
2747
+ for (let i = items.length - 1; i >= 0; i--) {
2748
+ if (items[i]._zod[key] !== "optional") return i + 1;
2749
+ }
2750
+ return 0;
2751
+ }
2752
+
2745
2753
  function handleTupleResult(result: ParsePayload, final: ParsePayload<any[]>, index: number) {
2746
2754
  if (result.issues.length) {
2747
2755
  final.issues.push(...util.prefixIssues(index, result.issues));
@@ -2749,30 +2757,21 @@ function handleTupleResult(result: ParsePayload, final: ParsePayload<any[]>, ind
2749
2757
  final.value[index] = result.value;
2750
2758
  }
2751
2759
 
2752
- // Post-processes the per-item results collected by the tuple parser.
2753
- // `optStart` is intentionally NOT consulted here — it's an input-length
2754
- // concern handled by the `too_small` precheck at the top of parse. This
2755
- // step is purely about output shaping, which is governed by `optout`:
2756
- // a `.default()` tail item sits inside the optStart region (its `optin`
2757
- // is optional), but it must NOT be dropped or have its errors swallowed
2758
- // because it materializes a defined value (`optout !== "optional"`).
2759
2760
  function handleTupleResults(
2760
2761
  itemResults: ParsePayload[],
2761
2762
  final: ParsePayload<any[]>,
2762
2763
  items: readonly $ZodType[],
2763
- input: unknown[]
2764
+ input: unknown[],
2765
+ optoutStart: number
2764
2766
  ) {
2765
2767
  // Walk results in order. Mirror $ZodObject's swallow-on-absent-optional
2766
- // rule, but for a tuple "absent" is a positional concept: once we
2767
- // swallow at index k, every later index is also absent-or-corrupted,
2768
- // so we truncate the result there and stop processing — including
2769
- // skipping any later defaults.
2768
+ // rule, but only after `optoutStart`: the first index where the output
2769
+ // tuple tail can be absent.
2770
2770
  for (let i = 0; i < items.length; i++) {
2771
2771
  const r = itemResults[i];
2772
- const isOptionalOut = items[i]._zod.optout === "optional";
2773
2772
  const isPresent = i < input.length;
2774
2773
  if (r.issues.length) {
2775
- if (isOptionalOut && !isPresent) {
2774
+ if (!isPresent && i >= optoutStart) {
2776
2775
  final.value.length = i;
2777
2776
  break;
2778
2777
  }
@@ -4119,6 +4118,37 @@ function handleCodecTxResult(left: ParsePayload, value: any, nextSchema: SomeTyp
4119
4118
  return nextSchema._zod.run({ value, issues: left.issues }, ctx);
4120
4119
  }
4121
4120
 
4121
+ /////////////////////////////////////////////////
4122
+ /////////////////////////////////////////////////
4123
+ ////////// //////////
4124
+ ////////// $ZodPreprocess //////////
4125
+ ////////// //////////
4126
+ /////////////////////////////////////////////////
4127
+ /////////////////////////////////////////////////
4128
+ export interface $ZodPreprocessDef<B extends SomeType = $ZodType> extends $ZodPipeDef<$ZodTransform, B> {
4129
+ in: $ZodTransform;
4130
+ out: B;
4131
+ }
4132
+
4133
+ export interface $ZodPreprocessInternals<B extends SomeType = $ZodType> extends $ZodPipeInternals<$ZodTransform, B> {
4134
+ def: $ZodPreprocessDef<B>;
4135
+ optin: B["_zod"]["optin"];
4136
+ optout: B["_zod"]["optout"];
4137
+ }
4138
+
4139
+ export interface $ZodPreprocess<B extends SomeType = $ZodType> extends $ZodPipe<$ZodTransform, B> {
4140
+ _zod: $ZodPreprocessInternals<B>;
4141
+ }
4142
+
4143
+ export const $ZodPreprocess: core.$constructor<$ZodPreprocess> = /*@__PURE__*/ core.$constructor(
4144
+ "$ZodPreprocess",
4145
+ (inst, def) => {
4146
+ $ZodPipe.init(inst, def);
4147
+ util.defineLazy(inst._zod, "optin", () => def.out._zod.optin);
4148
+ util.defineLazy(inst._zod, "optout", () => def.out._zod.optout);
4149
+ }
4150
+ );
4151
+
4122
4152
  ////////////////////////////////////////////
4123
4153
  ////////////////////////////////////////////
4124
4154
  ////////// //////////
@@ -561,6 +561,7 @@ function isTransforming(
561
561
  return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
562
562
  }
563
563
  if (def.type === "pipe") {
564
+ if (_schema._zod.traits.has("$ZodCodec")) return true;
564
565
  return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
565
566
  }
566
567
 
@@ -1,5 +1,5 @@
1
1
  export const version = {
2
2
  major: 4,
3
3
  minor: 4,
4
- patch: 0 as number,
4
+ patch: 2 as number,
5
5
  } as const;
@@ -1083,7 +1083,7 @@ export const ZodMiniDiscriminatedUnion: core.$constructor<ZodMiniDiscriminatedUn
1083
1083
 
1084
1084
  // @__NO_SIDE_EFFECTS__
1085
1085
  export function discriminatedUnion<
1086
- Types extends readonly [core.$ZodTypeDiscriminable, ...core.$ZodTypeDiscriminable[]],
1086
+ Types extends readonly [core.$ZodTypeDiscriminable<Disc>, ...core.$ZodTypeDiscriminable<Disc>[]],
1087
1087
  Disc extends string,
1088
1088
  >(
1089
1089
  discriminator: Disc,
@@ -247,6 +247,11 @@ test("z.union([]) / z.xor([]) / z.discriminatedUnion(_, []) construct and reject
247
247
  }
248
248
  });
249
249
 
250
+ test("z.discriminatedUnion rejects object options missing the discriminator at type level", () => {
251
+ // @ts-expect-error missing discriminator property
252
+ z.discriminatedUnion("type", [z.object({ value: z.string() })]);
253
+ });
254
+
250
255
  test("z.intersection", () => {
251
256
  const a = z.intersection(z.object({ a: z.string() }), z.object({ b: z.number() }));
252
257
  expect(z.parse(a, { a: "hello", b: 123 })).toEqual({ a: "hello", b: 123 });
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.ZodLiteral = exports.ZodEnum = exports.ZodSet = exports.ZodMap = exports.ZodRecord = exports.ZodTuple = exports.ZodIntersection = exports.ZodDiscriminatedUnion = exports.ZodXor = exports.ZodUnion = exports.ZodObject = exports.ZodArray = exports.ZodDate = exports.ZodVoid = exports.ZodNever = exports.ZodUnknown = exports.ZodAny = exports.ZodNull = exports.ZodUndefined = exports.ZodSymbol = exports.ZodBigIntFormat = exports.ZodBigInt = exports.ZodBoolean = exports.ZodNumberFormat = exports.ZodNumber = exports.ZodCustomStringFormat = exports.ZodJWT = exports.ZodE164 = exports.ZodBase64URL = exports.ZodBase64 = exports.ZodCIDRv6 = exports.ZodCIDRv4 = exports.ZodIPv6 = exports.ZodMAC = exports.ZodIPv4 = exports.ZodKSUID = exports.ZodXID = exports.ZodULID = exports.ZodCUID2 = exports.ZodCUID = exports.ZodNanoID = exports.ZodEmoji = exports.ZodURL = exports.ZodUUID = exports.ZodGUID = exports.ZodEmail = exports.ZodStringFormat = exports.ZodString = exports._ZodString = exports.ZodType = void 0;
27
- exports.stringbool = exports.meta = exports.describe = exports.ZodCustom = exports.ZodFunction = exports.ZodPromise = exports.ZodLazy = exports.ZodTemplateLiteral = exports.ZodReadonly = exports.ZodCodec = exports.ZodPipe = exports.ZodNaN = exports.ZodCatch = exports.ZodSuccess = exports.ZodNonOptional = exports.ZodPrefault = exports.ZodDefault = exports.ZodNullable = exports.ZodExactOptional = exports.ZodOptional = exports.ZodTransform = exports.ZodFile = void 0;
27
+ exports.stringbool = exports.meta = exports.describe = exports.ZodCustom = exports.ZodFunction = exports.ZodPromise = exports.ZodLazy = exports.ZodTemplateLiteral = exports.ZodReadonly = exports.ZodPreprocess = exports.ZodCodec = exports.ZodPipe = exports.ZodNaN = exports.ZodCatch = exports.ZodSuccess = exports.ZodNonOptional = exports.ZodPrefault = exports.ZodDefault = exports.ZodNullable = exports.ZodExactOptional = exports.ZodOptional = exports.ZodTransform = exports.ZodFile = void 0;
28
28
  exports.string = string;
29
29
  exports.email = email;
30
30
  exports.guid = guid;
@@ -1372,6 +1372,10 @@ function invertCodec(codec) {
1372
1372
  reverseTransform: def.transform,
1373
1373
  });
1374
1374
  }
1375
+ exports.ZodPreprocess = core.$constructor("ZodPreprocess", (inst, def) => {
1376
+ exports.ZodPipe.init(inst, def);
1377
+ core.$ZodPreprocess.init(inst, def);
1378
+ });
1375
1379
  exports.ZodReadonly = core.$constructor("ZodReadonly", (inst, def) => {
1376
1380
  core.$ZodReadonly.init(inst, def);
1377
1381
  exports.ZodType.init(inst, def);
@@ -1497,5 +1501,9 @@ function json(params) {
1497
1501
  }
1498
1502
  // preprocess
1499
1503
  function preprocess(fn, schema) {
1500
- return pipe(transform(fn), schema);
1504
+ return new exports.ZodPreprocess({
1505
+ type: "pipe",
1506
+ in: transform(fn),
1507
+ out: schema,
1508
+ });
1501
1509
  }
@@ -511,7 +511,7 @@ export interface ZodDiscriminatedUnion<Options extends readonly core.SomeType[]
511
511
  def: core.$ZodDiscriminatedUnionDef<Options, Disc>;
512
512
  }
513
513
  export declare const ZodDiscriminatedUnion: core.$constructor<ZodDiscriminatedUnion>;
514
- export declare function discriminatedUnion<Types extends readonly [core.$ZodTypeDiscriminable, ...core.$ZodTypeDiscriminable[]], Disc extends string>(discriminator: Disc, options: Types, params?: string | core.$ZodDiscriminatedUnionParams): ZodDiscriminatedUnion<Types, Disc>;
514
+ export declare function discriminatedUnion<Types extends readonly [core.$ZodTypeDiscriminable<Disc>, ...core.$ZodTypeDiscriminable<Disc>[]], Disc extends string>(discriminator: Disc, options: Types, params?: string | core.$ZodDiscriminatedUnionParams): ZodDiscriminatedUnion<Types, Disc>;
515
515
  export interface ZodIntersection<A extends core.SomeType = core.$ZodType, B extends core.SomeType = core.$ZodType> extends _ZodType<core.$ZodIntersectionInternals<A, B>>, core.$ZodIntersection<A, B> {
516
516
  "~standard": ZodStandardSchemaWithJSON<this>;
517
517
  }
@@ -674,6 +674,12 @@ export declare function codec<const A extends core.SomeType, B extends core.Some
674
674
  encode: (value: core.input<B>, payload: core.ParsePayload<core.input<B>>) => core.util.MaybeAsync<core.output<A>>;
675
675
  }): ZodCodec<A, B>;
676
676
  export declare function invertCodec<A extends core.SomeType, B extends core.SomeType>(codec: ZodCodec<A, B>): ZodCodec<B, A>;
677
+ export interface ZodPreprocess<B extends core.SomeType = core.$ZodType> extends ZodPipe<core.$ZodTransform, B>, core.$ZodPreprocess<B> {
678
+ "~standard": ZodStandardSchemaWithJSON<this>;
679
+ _zod: core.$ZodPreprocessInternals<B>;
680
+ def: core.$ZodPreprocessDef<B>;
681
+ }
682
+ export declare const ZodPreprocess: core.$constructor<ZodPreprocess>;
677
683
  export interface ZodReadonly<T extends core.SomeType = core.$ZodType> extends _ZodType<core.$ZodReadonlyInternals<T>>, core.$ZodReadonly<T> {
678
684
  "~standard": ZodStandardSchemaWithJSON<this>;
679
685
  unwrap(): T;
@@ -758,4 +764,4 @@ export interface ZodJSONSchema extends _ZodJSONSchema {
758
764
  _zod: ZodJSONSchemaInternals;
759
765
  }
760
766
  export declare function json(params?: string | core.$ZodCustomParams): ZodJSONSchema;
761
- export declare function preprocess<A, U extends core.SomeType, B = unknown>(fn: (arg: B, ctx: core.$RefinementCtx) => A, schema: U): ZodPipe<ZodTransform<A, B>, U>;
767
+ export declare function preprocess<A, U extends core.SomeType, B = unknown>(fn: (arg: B, ctx: core.$RefinementCtx) => A, schema: U): ZodPreprocess<U>;
@@ -511,7 +511,7 @@ export interface ZodDiscriminatedUnion<Options extends readonly core.SomeType[]
511
511
  def: core.$ZodDiscriminatedUnionDef<Options, Disc>;
512
512
  }
513
513
  export declare const ZodDiscriminatedUnion: core.$constructor<ZodDiscriminatedUnion>;
514
- export declare function discriminatedUnion<Types extends readonly [core.$ZodTypeDiscriminable, ...core.$ZodTypeDiscriminable[]], Disc extends string>(discriminator: Disc, options: Types, params?: string | core.$ZodDiscriminatedUnionParams): ZodDiscriminatedUnion<Types, Disc>;
514
+ export declare function discriminatedUnion<Types extends readonly [core.$ZodTypeDiscriminable<Disc>, ...core.$ZodTypeDiscriminable<Disc>[]], Disc extends string>(discriminator: Disc, options: Types, params?: string | core.$ZodDiscriminatedUnionParams): ZodDiscriminatedUnion<Types, Disc>;
515
515
  export interface ZodIntersection<A extends core.SomeType = core.$ZodType, B extends core.SomeType = core.$ZodType> extends _ZodType<core.$ZodIntersectionInternals<A, B>>, core.$ZodIntersection<A, B> {
516
516
  "~standard": ZodStandardSchemaWithJSON<this>;
517
517
  }
@@ -674,6 +674,12 @@ export declare function codec<const A extends core.SomeType, B extends core.Some
674
674
  encode: (value: core.input<B>, payload: core.ParsePayload<core.input<B>>) => core.util.MaybeAsync<core.output<A>>;
675
675
  }): ZodCodec<A, B>;
676
676
  export declare function invertCodec<A extends core.SomeType, B extends core.SomeType>(codec: ZodCodec<A, B>): ZodCodec<B, A>;
677
+ export interface ZodPreprocess<B extends core.SomeType = core.$ZodType> extends ZodPipe<core.$ZodTransform, B>, core.$ZodPreprocess<B> {
678
+ "~standard": ZodStandardSchemaWithJSON<this>;
679
+ _zod: core.$ZodPreprocessInternals<B>;
680
+ def: core.$ZodPreprocessDef<B>;
681
+ }
682
+ export declare const ZodPreprocess: core.$constructor<ZodPreprocess>;
677
683
  export interface ZodReadonly<T extends core.SomeType = core.$ZodType> extends _ZodType<core.$ZodReadonlyInternals<T>>, core.$ZodReadonly<T> {
678
684
  "~standard": ZodStandardSchemaWithJSON<this>;
679
685
  unwrap(): T;
@@ -758,4 +764,4 @@ export interface ZodJSONSchema extends _ZodJSONSchema {
758
764
  _zod: ZodJSONSchemaInternals;
759
765
  }
760
766
  export declare function json(params?: string | core.$ZodCustomParams): ZodJSONSchema;
761
- export declare function preprocess<A, U extends core.SomeType, B = unknown>(fn: (arg: B, ctx: core.$RefinementCtx) => A, schema: U): ZodPipe<ZodTransform<A, B>, U>;
767
+ export declare function preprocess<A, U extends core.SomeType, B = unknown>(fn: (arg: B, ctx: core.$RefinementCtx) => A, schema: U): ZodPreprocess<U>;
@@ -1255,6 +1255,10 @@ export function invertCodec(codec) {
1255
1255
  reverseTransform: def.transform,
1256
1256
  });
1257
1257
  }
1258
+ export const ZodPreprocess = /*@__PURE__*/ core.$constructor("ZodPreprocess", (inst, def) => {
1259
+ ZodPipe.init(inst, def);
1260
+ core.$ZodPreprocess.init(inst, def);
1261
+ });
1258
1262
  export const ZodReadonly = /*@__PURE__*/ core.$constructor("ZodReadonly", (inst, def) => {
1259
1263
  core.$ZodReadonly.init(inst, def);
1260
1264
  ZodType.init(inst, def);
@@ -1381,5 +1385,9 @@ export function json(params) {
1381
1385
  }
1382
1386
  // preprocess
1383
1387
  export function preprocess(fn, schema) {
1384
- return pipe(transform(fn), schema);
1388
+ return new ZodPreprocess({
1389
+ type: "pipe",
1390
+ in: transform(fn),
1391
+ out: schema,
1392
+ });
1385
1393
  }
package/v4/core/api.d.cts CHANGED
@@ -225,14 +225,16 @@ export type $ZodUnionParams = TypeParams<schemas.$ZodUnion, "options">;
225
225
  export declare function _union<const T extends readonly schemas.$ZodObject[]>(Class: util.SchemaClass<schemas.$ZodUnion>, options: T, params?: string | $ZodUnionParams): schemas.$ZodUnion<T>;
226
226
  export type $ZodXorParams = TypeParams<schemas.$ZodXor, "options">;
227
227
  export declare function _xor<const T extends readonly schemas.$ZodObject[]>(Class: util.SchemaClass<schemas.$ZodXor>, options: T, params?: string | $ZodXorParams): schemas.$ZodXor<T>;
228
- export interface $ZodTypeDiscriminableInternals extends schemas.$ZodTypeInternals {
228
+ export interface $ZodTypeDiscriminableInternals<Disc extends string = string> extends schemas.$ZodTypeInternals<unknown, {
229
+ [K in Disc]?: unknown;
230
+ }> {
229
231
  propValues: util.PropValues;
230
232
  }
231
- export interface $ZodTypeDiscriminable extends schemas.$ZodType {
232
- _zod: $ZodTypeDiscriminableInternals;
233
+ export interface $ZodTypeDiscriminable<Disc extends string = string> extends schemas.$ZodType {
234
+ _zod: $ZodTypeDiscriminableInternals<Disc>;
233
235
  }
234
236
  export type $ZodDiscriminatedUnionParams = TypeParams<schemas.$ZodDiscriminatedUnion, "options" | "discriminator">;
235
- export declare function _discriminatedUnion<Types extends [$ZodTypeDiscriminable, ...$ZodTypeDiscriminable[]], Disc extends string>(Class: util.SchemaClass<schemas.$ZodDiscriminatedUnion>, discriminator: Disc, options: Types, params?: string | $ZodDiscriminatedUnionParams): schemas.$ZodDiscriminatedUnion<Types, Disc>;
237
+ export declare function _discriminatedUnion<Types extends [$ZodTypeDiscriminable<Disc>, ...$ZodTypeDiscriminable<Disc>[]], Disc extends string>(Class: util.SchemaClass<schemas.$ZodDiscriminatedUnion>, discriminator: Disc, options: Types, params?: string | $ZodDiscriminatedUnionParams): schemas.$ZodDiscriminatedUnion<Types, Disc>;
236
238
  export type $ZodIntersectionParams = TypeParams<schemas.$ZodIntersection, "left" | "right">;
237
239
  export declare function _intersection<T extends schemas.$ZodObject, U extends schemas.$ZodObject>(Class: util.SchemaClass<schemas.$ZodIntersection>, left: T, right: U): schemas.$ZodIntersection<T, U>;
238
240
  export type $ZodTupleParams = TypeParams<schemas.$ZodTuple, "items" | "rest">;
package/v4/core/api.d.ts CHANGED
@@ -225,14 +225,16 @@ export type $ZodUnionParams = TypeParams<schemas.$ZodUnion, "options">;
225
225
  export declare function _union<const T extends readonly schemas.$ZodObject[]>(Class: util.SchemaClass<schemas.$ZodUnion>, options: T, params?: string | $ZodUnionParams): schemas.$ZodUnion<T>;
226
226
  export type $ZodXorParams = TypeParams<schemas.$ZodXor, "options">;
227
227
  export declare function _xor<const T extends readonly schemas.$ZodObject[]>(Class: util.SchemaClass<schemas.$ZodXor>, options: T, params?: string | $ZodXorParams): schemas.$ZodXor<T>;
228
- export interface $ZodTypeDiscriminableInternals extends schemas.$ZodTypeInternals {
228
+ export interface $ZodTypeDiscriminableInternals<Disc extends string = string> extends schemas.$ZodTypeInternals<unknown, {
229
+ [K in Disc]?: unknown;
230
+ }> {
229
231
  propValues: util.PropValues;
230
232
  }
231
- export interface $ZodTypeDiscriminable extends schemas.$ZodType {
232
- _zod: $ZodTypeDiscriminableInternals;
233
+ export interface $ZodTypeDiscriminable<Disc extends string = string> extends schemas.$ZodType {
234
+ _zod: $ZodTypeDiscriminableInternals<Disc>;
233
235
  }
234
236
  export type $ZodDiscriminatedUnionParams = TypeParams<schemas.$ZodDiscriminatedUnion, "options" | "discriminator">;
235
- export declare function _discriminatedUnion<Types extends [$ZodTypeDiscriminable, ...$ZodTypeDiscriminable[]], Disc extends string>(Class: util.SchemaClass<schemas.$ZodDiscriminatedUnion>, discriminator: Disc, options: Types, params?: string | $ZodDiscriminatedUnionParams): schemas.$ZodDiscriminatedUnion<Types, Disc>;
237
+ export declare function _discriminatedUnion<Types extends [$ZodTypeDiscriminable<Disc>, ...$ZodTypeDiscriminable<Disc>[]], Disc extends string>(Class: util.SchemaClass<schemas.$ZodDiscriminatedUnion>, discriminator: Disc, options: Types, params?: string | $ZodDiscriminatedUnionParams): schemas.$ZodDiscriminatedUnion<Types, Disc>;
236
238
  export type $ZodIntersectionParams = TypeParams<schemas.$ZodIntersection, "left" | "right">;
237
239
  export declare function _intersection<T extends schemas.$ZodObject, U extends schemas.$ZodObject>(Class: util.SchemaClass<schemas.$ZodIntersection>, left: T, right: U): schemas.$ZodIntersection<T, U>;
238
240
  export type $ZodTupleParams = TypeParams<schemas.$ZodTuple, "items" | "rest">;
@@ -525,7 +525,8 @@ const catchProcessor = (schema, ctx, json, params) => {
525
525
  exports.catchProcessor = catchProcessor;
526
526
  const pipeProcessor = (schema, ctx, _json, params) => {
527
527
  const def = schema._zod.def;
528
- const innerType = ctx.io === "input" ? (def.in._zod.def.type === "transform" ? def.out : def.in) : def.out;
528
+ const inIsTransform = def.in._zod.traits.has("$ZodTransform");
529
+ const innerType = ctx.io === "input" ? (inIsTransform ? def.out : def.in) : def.out;
529
530
  (0, to_json_schema_js_1.process)(innerType, ctx, params);
530
531
  const seen = ctx.seen.get(schema);
531
532
  seen.ref = innerType;
@@ -487,7 +487,8 @@ export const catchProcessor = (schema, ctx, json, params) => {
487
487
  };
488
488
  export const pipeProcessor = (schema, ctx, _json, params) => {
489
489
  const def = schema._zod.def;
490
- const innerType = ctx.io === "input" ? (def.in._zod.def.type === "transform" ? def.out : def.in) : def.out;
490
+ const inIsTransform = def.in._zod.traits.has("$ZodTransform");
491
+ const innerType = ctx.io === "input" ? (inIsTransform ? def.out : def.in) : def.out;
491
492
  process(innerType, ctx, params);
492
493
  const seen = ctx.seen.get(schema);
493
494
  seen.ref = innerType;
@@ -24,7 +24,7 @@ var __importStar = (this && this.__importStar) || function (mod) {
24
24
  };
25
25
  Object.defineProperty(exports, "__esModule", { value: true });
26
26
  exports.$ZodTuple = exports.$ZodIntersection = exports.$ZodDiscriminatedUnion = exports.$ZodXor = exports.$ZodUnion = exports.$ZodObjectJIT = exports.$ZodObject = exports.$ZodArray = exports.$ZodDate = exports.$ZodVoid = exports.$ZodNever = exports.$ZodUnknown = exports.$ZodAny = exports.$ZodNull = exports.$ZodUndefined = exports.$ZodSymbol = exports.$ZodBigIntFormat = exports.$ZodBigInt = exports.$ZodBoolean = exports.$ZodNumberFormat = exports.$ZodNumber = exports.$ZodCustomStringFormat = exports.$ZodJWT = exports.$ZodE164 = exports.$ZodBase64URL = exports.$ZodBase64 = exports.$ZodCIDRv6 = exports.$ZodCIDRv4 = exports.$ZodMAC = exports.$ZodIPv6 = exports.$ZodIPv4 = exports.$ZodISODuration = exports.$ZodISOTime = exports.$ZodISODate = exports.$ZodISODateTime = exports.$ZodKSUID = exports.$ZodXID = exports.$ZodULID = exports.$ZodCUID2 = exports.$ZodCUID = exports.$ZodNanoID = exports.$ZodEmoji = exports.$ZodURL = exports.$ZodEmail = exports.$ZodUUID = exports.$ZodGUID = exports.$ZodStringFormat = exports.$ZodString = exports.clone = exports.$ZodType = void 0;
27
- exports.$ZodCustom = exports.$ZodLazy = exports.$ZodPromise = exports.$ZodFunction = exports.$ZodTemplateLiteral = exports.$ZodReadonly = exports.$ZodCodec = exports.$ZodPipe = exports.$ZodNaN = exports.$ZodCatch = exports.$ZodSuccess = exports.$ZodNonOptional = exports.$ZodPrefault = exports.$ZodDefault = exports.$ZodNullable = exports.$ZodExactOptional = exports.$ZodOptional = exports.$ZodTransform = exports.$ZodFile = exports.$ZodLiteral = exports.$ZodEnum = exports.$ZodSet = exports.$ZodMap = exports.$ZodRecord = void 0;
27
+ exports.$ZodCustom = exports.$ZodLazy = exports.$ZodPromise = exports.$ZodFunction = exports.$ZodTemplateLiteral = exports.$ZodReadonly = exports.$ZodPreprocess = exports.$ZodCodec = exports.$ZodPipe = exports.$ZodNaN = exports.$ZodCatch = exports.$ZodSuccess = exports.$ZodNonOptional = exports.$ZodPrefault = exports.$ZodDefault = exports.$ZodNullable = exports.$ZodExactOptional = exports.$ZodOptional = exports.$ZodTransform = exports.$ZodFile = exports.$ZodLiteral = exports.$ZodEnum = exports.$ZodSet = exports.$ZodMap = exports.$ZodRecord = void 0;
28
28
  exports.isValidBase64 = isValidBase64;
29
29
  exports.isValidBase64URL = isValidBase64URL;
30
30
  exports.isValidJWT = isValidJWT;
@@ -1344,13 +1344,13 @@ exports.$ZodTuple = core.$constructor("$ZodTuple", (inst, def) => {
1344
1344
  }
1345
1345
  payload.value = [];
1346
1346
  const proms = [];
1347
- const reversedIndex = [...items].reverse().findIndex((item) => item._zod.optin !== "optional");
1348
- const optStart = reversedIndex === -1 ? 0 : items.length - reversedIndex;
1347
+ const optinStart = getTupleOptStart(items, "optin");
1348
+ const optoutStart = getTupleOptStart(items, "optout");
1349
1349
  if (!def.rest) {
1350
- if (input.length < optStart) {
1350
+ if (input.length < optinStart) {
1351
1351
  payload.issues.push({
1352
1352
  code: "too_small",
1353
- minimum: optStart,
1353
+ minimum: optinStart,
1354
1354
  inclusive: true,
1355
1355
  input,
1356
1356
  inst,
@@ -1371,9 +1371,8 @@ exports.$ZodTuple = core.$constructor("$ZodTuple", (inst, def) => {
1371
1371
  }
1372
1372
  // Run every item in parallel, collecting results into an indexed
1373
1373
  // array. The post-processing in `handleTupleResults` walks them in
1374
- // order so it can break on the first absent-optional error: once a
1375
- // slot rejects `undefined`, the tuple is malformed at that index and
1376
- // any later defaults must NOT fire.
1374
+ // order so it can decide whether an absent optional-output error can
1375
+ // truncate the tail or must be reported to preserve required output.
1377
1376
  const itemResults = new Array(items.length);
1378
1377
  for (let i = 0; i < items.length; i++) {
1379
1378
  const r = items[i]._zod.run({ value: input[i], issues: [] }, ctx);
@@ -1400,36 +1399,34 @@ exports.$ZodTuple = core.$constructor("$ZodTuple", (inst, def) => {
1400
1399
  }
1401
1400
  }
1402
1401
  }
1403
- if (proms.length)
1404
- return Promise.all(proms).then(() => handleTupleResults(itemResults, payload, items, input));
1405
- return handleTupleResults(itemResults, payload, items, input);
1402
+ if (proms.length) {
1403
+ return Promise.all(proms).then(() => handleTupleResults(itemResults, payload, items, input, optoutStart));
1404
+ }
1405
+ return handleTupleResults(itemResults, payload, items, input, optoutStart);
1406
1406
  };
1407
1407
  });
1408
+ function getTupleOptStart(items, key) {
1409
+ for (let i = items.length - 1; i >= 0; i--) {
1410
+ if (items[i]._zod[key] !== "optional")
1411
+ return i + 1;
1412
+ }
1413
+ return 0;
1414
+ }
1408
1415
  function handleTupleResult(result, final, index) {
1409
1416
  if (result.issues.length) {
1410
1417
  final.issues.push(...util.prefixIssues(index, result.issues));
1411
1418
  }
1412
1419
  final.value[index] = result.value;
1413
1420
  }
1414
- // Post-processes the per-item results collected by the tuple parser.
1415
- // `optStart` is intentionally NOT consulted here — it's an input-length
1416
- // concern handled by the `too_small` precheck at the top of parse. This
1417
- // step is purely about output shaping, which is governed by `optout`:
1418
- // a `.default()` tail item sits inside the optStart region (its `optin`
1419
- // is optional), but it must NOT be dropped or have its errors swallowed
1420
- // because it materializes a defined value (`optout !== "optional"`).
1421
- function handleTupleResults(itemResults, final, items, input) {
1421
+ function handleTupleResults(itemResults, final, items, input, optoutStart) {
1422
1422
  // Walk results in order. Mirror $ZodObject's swallow-on-absent-optional
1423
- // rule, but for a tuple "absent" is a positional concept: once we
1424
- // swallow at index k, every later index is also absent-or-corrupted,
1425
- // so we truncate the result there and stop processing — including
1426
- // skipping any later defaults.
1423
+ // rule, but only after `optoutStart`: the first index where the output
1424
+ // tuple tail can be absent.
1427
1425
  for (let i = 0; i < items.length; i++) {
1428
1426
  const r = itemResults[i];
1429
- const isOptionalOut = items[i]._zod.optout === "optional";
1430
1427
  const isPresent = i < input.length;
1431
1428
  if (r.issues.length) {
1432
- if (isOptionalOut && !isPresent) {
1429
+ if (!isPresent && i >= optoutStart) {
1433
1430
  final.value.length = i;
1434
1431
  break;
1435
1432
  }
@@ -2056,6 +2053,11 @@ function handleCodecTxResult(left, value, nextSchema, ctx) {
2056
2053
  }
2057
2054
  return nextSchema._zod.run({ value, issues: left.issues }, ctx);
2058
2055
  }
2056
+ exports.$ZodPreprocess = core.$constructor("$ZodPreprocess", (inst, def) => {
2057
+ exports.$ZodPipe.init(inst, def);
2058
+ util.defineLazy(inst._zod, "optin", () => def.out._zod.optin);
2059
+ util.defineLazy(inst._zod, "optout", () => def.out._zod.optout);
2060
+ });
2059
2061
  exports.$ZodReadonly = core.$constructor("$ZodReadonly", (inst, def) => {
2060
2062
  exports.$ZodType.init(inst, def);
2061
2063
  util.defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
@@ -1030,6 +1030,19 @@ export interface $ZodCodec<A extends SomeType = $ZodType, B extends SomeType = $
1030
1030
  _zod: $ZodCodecInternals<A, B>;
1031
1031
  }
1032
1032
  export declare const $ZodCodec: core.$constructor<$ZodCodec>;
1033
+ export interface $ZodPreprocessDef<B extends SomeType = $ZodType> extends $ZodPipeDef<$ZodTransform, B> {
1034
+ in: $ZodTransform;
1035
+ out: B;
1036
+ }
1037
+ export interface $ZodPreprocessInternals<B extends SomeType = $ZodType> extends $ZodPipeInternals<$ZodTransform, B> {
1038
+ def: $ZodPreprocessDef<B>;
1039
+ optin: B["_zod"]["optin"];
1040
+ optout: B["_zod"]["optout"];
1041
+ }
1042
+ export interface $ZodPreprocess<B extends SomeType = $ZodType> extends $ZodPipe<$ZodTransform, B> {
1043
+ _zod: $ZodPreprocessInternals<B>;
1044
+ }
1045
+ export declare const $ZodPreprocess: core.$constructor<$ZodPreprocess>;
1033
1046
  export interface $ZodReadonlyDef<T extends SomeType = $ZodType> extends $ZodTypeDef {
1034
1047
  type: "readonly";
1035
1048
  innerType: T;
@@ -1030,6 +1030,19 @@ export interface $ZodCodec<A extends SomeType = $ZodType, B extends SomeType = $
1030
1030
  _zod: $ZodCodecInternals<A, B>;
1031
1031
  }
1032
1032
  export declare const $ZodCodec: core.$constructor<$ZodCodec>;
1033
+ export interface $ZodPreprocessDef<B extends SomeType = $ZodType> extends $ZodPipeDef<$ZodTransform, B> {
1034
+ in: $ZodTransform;
1035
+ out: B;
1036
+ }
1037
+ export interface $ZodPreprocessInternals<B extends SomeType = $ZodType> extends $ZodPipeInternals<$ZodTransform, B> {
1038
+ def: $ZodPreprocessDef<B>;
1039
+ optin: B["_zod"]["optin"];
1040
+ optout: B["_zod"]["optout"];
1041
+ }
1042
+ export interface $ZodPreprocess<B extends SomeType = $ZodType> extends $ZodPipe<$ZodTransform, B> {
1043
+ _zod: $ZodPreprocessInternals<B>;
1044
+ }
1045
+ export declare const $ZodPreprocess: core.$constructor<$ZodPreprocess>;
1033
1046
  export interface $ZodReadonlyDef<T extends SomeType = $ZodType> extends $ZodTypeDef {
1034
1047
  type: "readonly";
1035
1048
  innerType: T;
@@ -1313,13 +1313,13 @@ export const $ZodTuple = /*@__PURE__*/ core.$constructor("$ZodTuple", (inst, def
1313
1313
  }
1314
1314
  payload.value = [];
1315
1315
  const proms = [];
1316
- const reversedIndex = [...items].reverse().findIndex((item) => item._zod.optin !== "optional");
1317
- const optStart = reversedIndex === -1 ? 0 : items.length - reversedIndex;
1316
+ const optinStart = getTupleOptStart(items, "optin");
1317
+ const optoutStart = getTupleOptStart(items, "optout");
1318
1318
  if (!def.rest) {
1319
- if (input.length < optStart) {
1319
+ if (input.length < optinStart) {
1320
1320
  payload.issues.push({
1321
1321
  code: "too_small",
1322
- minimum: optStart,
1322
+ minimum: optinStart,
1323
1323
  inclusive: true,
1324
1324
  input,
1325
1325
  inst,
@@ -1340,9 +1340,8 @@ export const $ZodTuple = /*@__PURE__*/ core.$constructor("$ZodTuple", (inst, def
1340
1340
  }
1341
1341
  // Run every item in parallel, collecting results into an indexed
1342
1342
  // array. The post-processing in `handleTupleResults` walks them in
1343
- // order so it can break on the first absent-optional error: once a
1344
- // slot rejects `undefined`, the tuple is malformed at that index and
1345
- // any later defaults must NOT fire.
1343
+ // order so it can decide whether an absent optional-output error can
1344
+ // truncate the tail or must be reported to preserve required output.
1346
1345
  const itemResults = new Array(items.length);
1347
1346
  for (let i = 0; i < items.length; i++) {
1348
1347
  const r = items[i]._zod.run({ value: input[i], issues: [] }, ctx);
@@ -1369,36 +1368,34 @@ export const $ZodTuple = /*@__PURE__*/ core.$constructor("$ZodTuple", (inst, def
1369
1368
  }
1370
1369
  }
1371
1370
  }
1372
- if (proms.length)
1373
- return Promise.all(proms).then(() => handleTupleResults(itemResults, payload, items, input));
1374
- return handleTupleResults(itemResults, payload, items, input);
1371
+ if (proms.length) {
1372
+ return Promise.all(proms).then(() => handleTupleResults(itemResults, payload, items, input, optoutStart));
1373
+ }
1374
+ return handleTupleResults(itemResults, payload, items, input, optoutStart);
1375
1375
  };
1376
1376
  });
1377
+ function getTupleOptStart(items, key) {
1378
+ for (let i = items.length - 1; i >= 0; i--) {
1379
+ if (items[i]._zod[key] !== "optional")
1380
+ return i + 1;
1381
+ }
1382
+ return 0;
1383
+ }
1377
1384
  function handleTupleResult(result, final, index) {
1378
1385
  if (result.issues.length) {
1379
1386
  final.issues.push(...util.prefixIssues(index, result.issues));
1380
1387
  }
1381
1388
  final.value[index] = result.value;
1382
1389
  }
1383
- // Post-processes the per-item results collected by the tuple parser.
1384
- // `optStart` is intentionally NOT consulted here — it's an input-length
1385
- // concern handled by the `too_small` precheck at the top of parse. This
1386
- // step is purely about output shaping, which is governed by `optout`:
1387
- // a `.default()` tail item sits inside the optStart region (its `optin`
1388
- // is optional), but it must NOT be dropped or have its errors swallowed
1389
- // because it materializes a defined value (`optout !== "optional"`).
1390
- function handleTupleResults(itemResults, final, items, input) {
1390
+ function handleTupleResults(itemResults, final, items, input, optoutStart) {
1391
1391
  // Walk results in order. Mirror $ZodObject's swallow-on-absent-optional
1392
- // rule, but for a tuple "absent" is a positional concept: once we
1393
- // swallow at index k, every later index is also absent-or-corrupted,
1394
- // so we truncate the result there and stop processing — including
1395
- // skipping any later defaults.
1392
+ // rule, but only after `optoutStart`: the first index where the output
1393
+ // tuple tail can be absent.
1396
1394
  for (let i = 0; i < items.length; i++) {
1397
1395
  const r = itemResults[i];
1398
- const isOptionalOut = items[i]._zod.optout === "optional";
1399
1396
  const isPresent = i < input.length;
1400
1397
  if (r.issues.length) {
1401
- if (isOptionalOut && !isPresent) {
1398
+ if (!isPresent && i >= optoutStart) {
1402
1399
  final.value.length = i;
1403
1400
  break;
1404
1401
  }
@@ -2025,6 +2022,11 @@ function handleCodecTxResult(left, value, nextSchema, ctx) {
2025
2022
  }
2026
2023
  return nextSchema._zod.run({ value, issues: left.issues }, ctx);
2027
2024
  }
2025
+ export const $ZodPreprocess = /*@__PURE__*/ core.$constructor("$ZodPreprocess", (inst, def) => {
2026
+ $ZodPipe.init(inst, def);
2027
+ util.defineLazy(inst._zod, "optin", () => def.out._zod.optin);
2028
+ util.defineLazy(inst._zod, "optout", () => def.out._zod.optout);
2029
+ });
2028
2030
  export const $ZodReadonly = /*@__PURE__*/ core.$constructor("$ZodReadonly", (inst, def) => {
2029
2031
  $ZodType.init(inst, def);
2030
2032
  util.defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
@@ -407,6 +407,8 @@ function isTransforming(_schema, _ctx) {
407
407
  return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
408
408
  }
409
409
  if (def.type === "pipe") {
410
+ if (_schema._zod.traits.has("$ZodCodec"))
411
+ return true;
410
412
  return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
411
413
  }
412
414
  if (def.type === "object") {
@@ -400,6 +400,8 @@ function isTransforming(_schema, _ctx) {
400
400
  return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
401
401
  }
402
402
  if (def.type === "pipe") {
403
+ if (_schema._zod.traits.has("$ZodCodec"))
404
+ return true;
403
405
  return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
404
406
  }
405
407
  if (def.type === "object") {
@@ -4,5 +4,5 @@ exports.version = void 0;
4
4
  exports.version = {
5
5
  major: 4,
6
6
  minor: 4,
7
- patch: 0,
7
+ patch: 2,
8
8
  };
@@ -1,5 +1,5 @@
1
1
  export const version = {
2
2
  major: 4,
3
3
  minor: 4,
4
- patch: 0,
4
+ patch: 2,
5
5
  };
@@ -260,7 +260,7 @@ export interface ZodMiniDiscriminatedUnion<Options extends readonly SomeType[] =
260
260
  _zod: core.$ZodDiscriminatedUnionInternals<Options, Disc>;
261
261
  }
262
262
  export declare const ZodMiniDiscriminatedUnion: core.$constructor<ZodMiniDiscriminatedUnion>;
263
- export declare function discriminatedUnion<Types extends readonly [core.$ZodTypeDiscriminable, ...core.$ZodTypeDiscriminable[]], Disc extends string>(discriminator: Disc, options: Types, params?: string | core.$ZodDiscriminatedUnionParams): ZodMiniDiscriminatedUnion<Types, Disc>;
263
+ export declare function discriminatedUnion<Types extends readonly [core.$ZodTypeDiscriminable<Disc>, ...core.$ZodTypeDiscriminable<Disc>[]], Disc extends string>(discriminator: Disc, options: Types, params?: string | core.$ZodDiscriminatedUnionParams): ZodMiniDiscriminatedUnion<Types, Disc>;
264
264
  export interface ZodMiniIntersection<A extends SomeType = core.$ZodType, B extends SomeType = core.$ZodType> extends _ZodMiniType<core.$ZodIntersectionInternals<A, B>> {
265
265
  }
266
266
  export declare const ZodMiniIntersection: core.$constructor<ZodMiniIntersection>;
@@ -260,7 +260,7 @@ export interface ZodMiniDiscriminatedUnion<Options extends readonly SomeType[] =
260
260
  _zod: core.$ZodDiscriminatedUnionInternals<Options, Disc>;
261
261
  }
262
262
  export declare const ZodMiniDiscriminatedUnion: core.$constructor<ZodMiniDiscriminatedUnion>;
263
- export declare function discriminatedUnion<Types extends readonly [core.$ZodTypeDiscriminable, ...core.$ZodTypeDiscriminable[]], Disc extends string>(discriminator: Disc, options: Types, params?: string | core.$ZodDiscriminatedUnionParams): ZodMiniDiscriminatedUnion<Types, Disc>;
263
+ export declare function discriminatedUnion<Types extends readonly [core.$ZodTypeDiscriminable<Disc>, ...core.$ZodTypeDiscriminable<Disc>[]], Disc extends string>(discriminator: Disc, options: Types, params?: string | core.$ZodDiscriminatedUnionParams): ZodMiniDiscriminatedUnion<Types, Disc>;
264
264
  export interface ZodMiniIntersection<A extends SomeType = core.$ZodType, B extends SomeType = core.$ZodType> extends _ZodMiniType<core.$ZodIntersectionInternals<A, B>> {
265
265
  }
266
266
  export declare const ZodMiniIntersection: core.$constructor<ZodMiniIntersection>;