zod 3.26.0-canary.20250703T215303 → 3.26.0-canary.20250708T090717

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 (48) hide show
  1. package/index.cjs +2 -2
  2. package/index.d.cts +2 -2
  3. package/index.d.ts +2 -2
  4. package/index.js +2 -2
  5. package/package.json +1 -1
  6. package/src/index.ts +2 -2
  7. package/src/v4/classic/schemas.ts +8 -9
  8. package/src/v4/classic/tests/brand.test.ts +1 -3
  9. package/src/v4/classic/tests/discriminated-unions.test.ts +27 -0
  10. package/src/v4/classic/tests/index.test.ts +1 -1
  11. package/src/v4/classic/tests/object.test.ts +11 -1
  12. package/src/v4/classic/tests/record.test.ts +11 -1
  13. package/src/v4/classic/tests/recursive-types.test.ts +33 -2
  14. package/src/v4/classic/tests/refine.test.ts +109 -0
  15. package/src/v4/classic/tests/registries.test.ts +9 -0
  16. package/src/v4/classic/tests/to-json-schema.test.ts +78 -17
  17. package/src/v4/core/api.ts +2 -0
  18. package/src/v4/core/checks.ts +8 -10
  19. package/src/v4/core/errors.ts +4 -0
  20. package/src/v4/core/registries.ts +11 -1
  21. package/src/v4/core/schemas.ts +80 -34
  22. package/src/v4/core/to-json-schema.ts +67 -30
  23. package/src/v4/mini/schemas.ts +2 -2
  24. package/src/v4/mini/tests/string.test.ts +6 -0
  25. package/v4/classic/schemas.cjs +4 -4
  26. package/v4/classic/schemas.d.cts +4 -3
  27. package/v4/classic/schemas.d.ts +4 -3
  28. package/v4/classic/schemas.js +4 -4
  29. package/v4/core/checks.cjs +18 -12
  30. package/v4/core/checks.d.cts +2 -1
  31. package/v4/core/checks.d.ts +2 -1
  32. package/v4/core/checks.js +18 -12
  33. package/v4/core/errors.cjs +4 -0
  34. package/v4/core/errors.js +4 -0
  35. package/v4/core/registries.cjs +10 -1
  36. package/v4/core/registries.d.cts +2 -1
  37. package/v4/core/registries.d.ts +2 -1
  38. package/v4/core/registries.js +10 -1
  39. package/v4/core/schemas.cjs +15 -6
  40. package/v4/core/schemas.d.cts +31 -9
  41. package/v4/core/schemas.d.ts +31 -9
  42. package/v4/core/schemas.js +15 -6
  43. package/v4/core/to-json-schema.cjs +51 -26
  44. package/v4/core/to-json-schema.d.cts +6 -2
  45. package/v4/core/to-json-schema.d.ts +6 -2
  46. package/v4/core/to-json-schema.js +51 -26
  47. package/v4/mini/schemas.d.cts +1 -1
  48. package/v4/mini/schemas.d.ts +1 -1
package/index.cjs CHANGED
@@ -27,7 +27,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
27
27
  };
28
28
  Object.defineProperty(exports, "__esModule", { value: true });
29
29
  exports.z = void 0;
30
- const z = __importStar(require("./v3/index.cjs"));
30
+ const z = __importStar(require("./v3/external.cjs"));
31
31
  exports.z = z;
32
- __exportStar(require("./v3/index.cjs"), exports);
32
+ __exportStar(require("./v3/external.cjs"), exports);
33
33
  exports.default = z;
package/index.d.cts CHANGED
@@ -1,4 +1,4 @@
1
- import * as z from "./v3/index.cjs";
2
- export * from "./v3/index.cjs";
1
+ import * as z from "./v3/external.cjs";
2
+ export * from "./v3/external.cjs";
3
3
  export { z };
4
4
  export default z;
package/index.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as z from "./v3/index.js";
2
- export * from "./v3/index.js";
1
+ import * as z from "./v3/external.js";
2
+ export * from "./v3/external.js";
3
3
  export { z };
4
4
  export default z;
package/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import * as z from "./v3/index.js";
2
- export * from "./v3/index.js";
1
+ import * as z from "./v3/external.js";
2
+ export * from "./v3/external.js";
3
3
  export { z };
4
4
  export default z;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "zod",
3
- "version": "3.26.0-canary.20250703T215303",
3
+ "version": "3.26.0-canary.20250708T090717",
4
4
  "type": "module",
5
5
  "author": "Colin McDonnell <zod@colinhacks.com>",
6
6
  "description": "TypeScript-first schema declaration and validation library with static type inference",
package/src/index.ts CHANGED
@@ -1,4 +1,4 @@
1
- import * as z from "./v3/index.js";
2
- export * from "./v3/index.js";
1
+ import * as z from "./v3/external.js";
2
+ export * from "./v3/external.js";
3
3
  export { z };
4
4
  export default z;
@@ -365,6 +365,8 @@ export const ZodString: core.$constructor<ZodString> = /*@__PURE__*/ core.$const
365
365
  inst.duration = (params) => inst.check(iso.duration(params as any));
366
366
  });
367
367
 
368
+ export function string(params?: string | core.$ZodStringParams): ZodString;
369
+ export function string<T extends string>(params?: string | core.$ZodStringParams): core.$ZodType<T, T>;
368
370
  export function string(params?: string | core.$ZodStringParams): ZodString {
369
371
  return core._string(ZodString, params) as any;
370
372
  }
@@ -1357,11 +1359,11 @@ export function partialRecord<Key extends core.$ZodRecordKey, Value extends core
1357
1359
  keyType: Key,
1358
1360
  valueType: Value,
1359
1361
  params?: string | core.$ZodRecordParams
1360
- ): ZodRecord<ZodUnion<[Key, ZodNever]>, Value> {
1362
+ ): ZodRecord<Key & core.$partial, Value> {
1361
1363
  return new ZodRecord({
1362
1364
  type: "record",
1363
1365
  keyType: union([keyType, never()]),
1364
- valueType: valueType as any as core.$ZodType,
1366
+ valueType: valueType as any,
1365
1367
  ...util.normalizeParams(params),
1366
1368
  }) as any;
1367
1369
  }
@@ -1935,10 +1937,10 @@ export const ZodCustom: core.$constructor<ZodCustom> = /*@__PURE__*/ core.$const
1935
1937
  });
1936
1938
 
1937
1939
  // custom checks
1938
- export function check<O = unknown>(fn: core.CheckFn<O>, params?: string | core.$ZodCustomParams): core.$ZodCheck<O> {
1940
+ export function check<O = unknown>(fn: core.CheckFn<O>): core.$ZodCheck<O> {
1939
1941
  const ch = new core.$ZodCheck({
1940
1942
  check: "custom",
1941
- ...util.normalizeParams(params),
1943
+ // ...util.normalizeParams(params),
1942
1944
  });
1943
1945
 
1944
1946
  ch._zod.check = fn;
@@ -1960,10 +1962,7 @@ export function refine<T>(
1960
1962
  }
1961
1963
 
1962
1964
  // superRefine
1963
- export function superRefine<T>(
1964
- fn: (arg: T, payload: RefinementCtx<T>) => void | Promise<void>,
1965
- params?: string | core.$ZodCustomParams
1966
- ): core.$ZodCheck<T> {
1965
+ export function superRefine<T>(fn: (arg: T, payload: RefinementCtx<T>) => void | Promise<void>): core.$ZodCheck<T> {
1967
1966
  const ch = check<T>((payload) => {
1968
1967
  (payload as RefinementCtx).addIssue = (issue) => {
1969
1968
  if (typeof issue === "string") {
@@ -1981,7 +1980,7 @@ export function superRefine<T>(
1981
1980
  };
1982
1981
 
1983
1982
  return fn(payload.value, payload as RefinementCtx<T>);
1984
- }, params);
1983
+ });
1985
1984
  return ch;
1986
1985
  }
1987
1986
 
@@ -59,7 +59,5 @@ test("$branded", () => {
59
59
  test("branded record", () => {
60
60
  const recordWithBrandedNumberKeys = z.record(z.string().brand("SomeBrand"), z.number());
61
61
  type recordWithBrandedNumberKeys = z.infer<typeof recordWithBrandedNumberKeys>;
62
- expectTypeOf<recordWithBrandedNumberKeys>().toEqualTypeOf<
63
- Record<string & z.core.$brand<"SomeBrand">, number | undefined>
64
- >();
62
+ expectTypeOf<recordWithBrandedNumberKeys>().toEqualTypeOf<Record<string & z.core.$brand<"SomeBrand">, number>>();
65
63
  });
@@ -24,6 +24,9 @@ test("_values", () => {
24
24
 
25
25
  const post = z.literal("test").transform((_) => Math.random());
26
26
  expect(post._zod.values).toEqual(new Set(["test"]));
27
+
28
+ // Test that readonly literals pass through their values property
29
+ expect(z.literal("test").readonly()._zod.values).toEqual(new Set(["test"]));
27
30
  });
28
31
 
29
32
  test("valid parse - object", () => {
@@ -590,3 +593,27 @@ test("nested discriminated unions", () => {
590
593
  }
591
594
  `);
592
595
  });
596
+
597
+ test("readonly literal discriminator", () => {
598
+ const discUnion = z.discriminatedUnion("type", [
599
+ z.object({ type: z.literal("a").readonly(), a: z.string() }),
600
+ z.object({ type: z.literal("b"), b: z.number() }),
601
+ ]);
602
+
603
+ // Test that both discriminator values are correctly included in propValues
604
+ const propValues = discUnion._zod.propValues;
605
+ expect(propValues?.type?.has("a")).toBe(true);
606
+ expect(propValues?.type?.has("b")).toBe(true);
607
+
608
+ // Test that the discriminated union works correctly
609
+ const result1 = discUnion.parse({ type: "a", a: "hello" });
610
+ expect(result1).toEqual({ type: "a", a: "hello" });
611
+
612
+ const result2 = discUnion.parse({ type: "b", b: 42 });
613
+ expect(result2).toEqual({ type: "b", b: 42 });
614
+
615
+ // Test that invalid discriminator values are rejected
616
+ expect(() => {
617
+ discUnion.parse({ type: "c", a: "hello" });
618
+ }).toThrow();
619
+ });
@@ -301,7 +301,7 @@ test("z.record", () => {
301
301
  // partial enum
302
302
  const d = z.record(z.enum(["a", "b"]).or(z.never()), z.string());
303
303
  type d = z.output<typeof d>;
304
- expectTypeOf<d>().toEqualTypeOf<Partial<Record<"a" | "b", string>>>();
304
+ expectTypeOf<d>().toEqualTypeOf<Record<"a" | "b", string>>();
305
305
  });
306
306
 
307
307
  test("z.map", () => {
@@ -217,6 +217,16 @@ test("test inferred merged type", async () => {
217
217
  expectTypeOf<asdf>().toEqualTypeOf<{ a: number }>();
218
218
  });
219
219
 
220
+ test("inferred type with Record shape", () => {
221
+ type A = z.ZodObject<Record<string, z.ZodType<string, number>>>;
222
+ expectTypeOf<z.infer<A>>().toEqualTypeOf<Record<string, string>>();
223
+ expectTypeOf<z.input<A>>().toEqualTypeOf<Record<string, number>>();
224
+
225
+ type B = z.ZodObject;
226
+ expectTypeOf<z.infer<B>>().toEqualTypeOf<Record<string, unknown>>();
227
+ expectTypeOf<z.input<B>>().toEqualTypeOf<Record<string, unknown>>();
228
+ });
229
+
220
230
  test("inferred merged object type with optional properties", async () => {
221
231
  const Merged = z
222
232
  .object({ a: z.string(), b: z.string().optional() })
@@ -549,5 +559,5 @@ test("index signature in shape", () => {
549
559
  const schema = makeZodObj("foo");
550
560
  type schema = z.infer<typeof schema>;
551
561
 
552
- expectTypeOf<schema>().toEqualTypeOf<Record<string, unknown>>();
562
+ expectTypeOf<schema>().toEqualTypeOf<Record<string, string>>();
553
563
  });
@@ -17,7 +17,7 @@ test("type inference", () => {
17
17
  expectTypeOf<booleanRecord>().toEqualTypeOf<Record<string, boolean>>();
18
18
  expectTypeOf<recordWithEnumKeys>().toEqualTypeOf<Record<"Tuna" | "Salmon", string>>();
19
19
  expectTypeOf<recordWithLiteralKey>().toEqualTypeOf<Record<"Tuna" | "Salmon", string>>();
20
- expectTypeOf<recordWithLiteralUnionKeys>().toEqualTypeOf<Partial<Record<"Tuna" | "Salmon", string>>>();
20
+ expectTypeOf<recordWithLiteralUnionKeys>().toEqualTypeOf<Record<"Tuna" | "Salmon", string>>();
21
21
  });
22
22
 
23
23
  test("enum exhaustiveness", () => {
@@ -330,3 +330,13 @@ test("async parsing", async () => {
330
330
  ]]
331
331
  `);
332
332
  });
333
+
334
+ test("partial record", () => {
335
+ const schema = z.partialRecord(z.string(), z.string());
336
+ type schema = z.infer<typeof schema>;
337
+ expectTypeOf<schema>().toEqualTypeOf<Partial<Record<string, string>>>();
338
+
339
+ const Keys = z.enum(["id", "name", "email"]).or(z.never());
340
+ const Person = z.partialRecord(Keys, z.string());
341
+ expectTypeOf<z.infer<typeof Person>>().toEqualTypeOf<Partial<Record<"id" | "name" | "email", string>>>();
342
+ });
@@ -160,9 +160,10 @@ test("deferred self-recursion", () => {
160
160
  const Output = z.object({
161
161
  id: z.int(), //.nonnegative(),
162
162
  name: z.string(),
163
- features: z.array(Feature), //.array(), // <—
163
+ get features(): z.ZodArray<typeof Feature> {
164
+ return Feature.array();
165
+ },
164
166
  });
165
-
166
167
  type Output = z.output<typeof Output>;
167
168
 
168
169
  type _Feature = {
@@ -323,3 +324,33 @@ test("recursion compatibility", () => {
323
324
  },
324
325
  });
325
326
  });
327
+
328
+ // biome-ignore lint: sadf
329
+ export type RecursiveA = z.ZodUnion<
330
+ [
331
+ z.ZodObject<{
332
+ a: z.ZodDefault<RecursiveA>;
333
+ b: z.ZodPrefault<RecursiveA>;
334
+ c: z.ZodNonOptional<RecursiveA>;
335
+ d: z.ZodOptional<RecursiveA>;
336
+ e: z.ZodNullable<RecursiveA>;
337
+ g: z.ZodReadonly<RecursiveA>;
338
+ h: z.ZodPipe<RecursiveA, z.ZodString>;
339
+ i: z.ZodArray<RecursiveA>;
340
+ j: z.ZodSet<RecursiveA>;
341
+ k: z.ZodMap<RecursiveA, RecursiveA>;
342
+ l: z.ZodRecord<z.ZodString, RecursiveA>;
343
+ m: z.ZodUnion<[RecursiveA, RecursiveA]>;
344
+ n: z.ZodIntersection<RecursiveA, RecursiveA>;
345
+ o: z.ZodLazy<RecursiveA>;
346
+ p: z.ZodPromise<RecursiveA>;
347
+ q: z.ZodCatch<RecursiveA>;
348
+ r: z.ZodSuccess<RecursiveA>;
349
+ s: z.ZodTransform<RecursiveA, string>;
350
+ t: z.ZodTuple<[RecursiveA, RecursiveA]>;
351
+ u: z.ZodObject<{
352
+ a: RecursiveA;
353
+ }>;
354
+ }>,
355
+ ]
356
+ >;
@@ -421,3 +421,112 @@ describe("type refinement", () => {
421
421
  });
422
422
  });
423
423
  */
424
+
425
+ test("when", () => {
426
+ const schema = z
427
+ .strictObject({
428
+ password: z.string().min(8),
429
+ confirmPassword: z.string(),
430
+ other: z.string(),
431
+ })
432
+ .refine(
433
+ (data) => {
434
+ console.log("running check...");
435
+ console.log(data);
436
+ console.log(data.password);
437
+ return data.password === data.confirmPassword;
438
+ },
439
+ {
440
+ message: "Passwords do not match",
441
+ path: ["confirmPassword"],
442
+ when(payload) {
443
+ if (payload.value === undefined) return false;
444
+ if (payload.value === null) return false;
445
+ // no issues with confirmPassword or password
446
+ return payload.issues.every((iss) => iss.path?.[0] !== "confirmPassword" && iss.path?.[0] !== "password");
447
+ },
448
+ }
449
+ );
450
+
451
+ expect(schema.safeParse(undefined)).toMatchInlineSnapshot(`
452
+ {
453
+ "error": [ZodError: [
454
+ {
455
+ "expected": "object",
456
+ "code": "invalid_type",
457
+ "path": [],
458
+ "message": "Invalid input: expected object, received undefined"
459
+ }
460
+ ]],
461
+ "success": false,
462
+ }
463
+ `);
464
+ expect(schema.safeParse(null)).toMatchInlineSnapshot(`
465
+ {
466
+ "error": [ZodError: [
467
+ {
468
+ "expected": "object",
469
+ "code": "invalid_type",
470
+ "path": [],
471
+ "message": "Invalid input: expected object, received null"
472
+ }
473
+ ]],
474
+ "success": false,
475
+ }
476
+ `);
477
+ expect(
478
+ schema.safeParse({
479
+ password: "asdf",
480
+ confirmPassword: "asdfg",
481
+ other: "qwer",
482
+ })
483
+ ).toMatchInlineSnapshot(`
484
+ {
485
+ "error": [ZodError: [
486
+ {
487
+ "origin": "string",
488
+ "code": "too_small",
489
+ "minimum": 8,
490
+ "inclusive": true,
491
+ "path": [
492
+ "password"
493
+ ],
494
+ "message": "Too small: expected string to have >=8 characters"
495
+ }
496
+ ]],
497
+ "success": false,
498
+ }
499
+ `);
500
+
501
+ expect(
502
+ schema.safeParse({
503
+ password: "asdf",
504
+ confirmPassword: "asdfg",
505
+ other: 1234,
506
+ })
507
+ ).toMatchInlineSnapshot(`
508
+ {
509
+ "error": [ZodError: [
510
+ {
511
+ "origin": "string",
512
+ "code": "too_small",
513
+ "minimum": 8,
514
+ "inclusive": true,
515
+ "path": [
516
+ "password"
517
+ ],
518
+ "message": "Too small: expected string to have >=8 characters"
519
+ },
520
+ {
521
+ "expected": "string",
522
+ "code": "invalid_type",
523
+ "path": [
524
+ "other"
525
+ ],
526
+ "message": "Invalid input: expected string, received number"
527
+ }
528
+ ]],
529
+ "success": false,
530
+ }
531
+ `);
532
+ });
@@ -193,3 +193,12 @@ test("function meta with replacement", () => {
193
193
 
194
194
  expect(myReg.get(mySchema)!.defaulter("hello", true)).toEqual(5);
195
195
  });
196
+
197
+ test("test .clear()", () => {
198
+ const reg = z.registry();
199
+ const a = z.string();
200
+ reg.add(a);
201
+ expect(reg.has(a)).toEqual(true);
202
+ reg.clear();
203
+ expect(reg.has(a)).toEqual(false);
204
+ });
@@ -28,10 +28,9 @@ describe("toJSONSchema", () => {
28
28
  "type": "null",
29
29
  }
30
30
  `);
31
- expect(z.toJSONSchema(z.undefined())).toMatchInlineSnapshot(`
31
+ expect(z.toJSONSchema(z.undefined(), { unrepresentable: "any" })).toMatchInlineSnapshot(`
32
32
  {
33
33
  "$schema": "https://json-schema.org/draft/2020-12/schema",
34
- "not": {},
35
34
  }
36
35
  `);
37
36
  expect(z.toJSONSchema(z.any())).toMatchInlineSnapshot(`
@@ -232,6 +231,7 @@ describe("toJSONSchema", () => {
232
231
  expect(() => z.toJSONSchema(z.int64())).toThrow("BigInt cannot be represented in JSON Schema");
233
232
  expect(() => z.toJSONSchema(z.symbol())).toThrow("Symbols cannot be represented in JSON Schema");
234
233
  expect(() => z.toJSONSchema(z.void())).toThrow("Void cannot be represented in JSON Schema");
234
+ expect(() => z.toJSONSchema(z.undefined())).toThrow("Undefined cannot be represented in JSON Schema");
235
235
  expect(() => z.toJSONSchema(z.date())).toThrow("Date cannot be represented in JSON Schema");
236
236
  expect(() => z.toJSONSchema(z.map(z.string(), z.number()))).toThrow("Map cannot be represented in JSON Schema");
237
237
  expect(() => z.toJSONSchema(z.set(z.string()))).toThrow("Set cannot be represented in JSON Schema");
@@ -1259,6 +1259,35 @@ test("override execution order", () => {
1259
1259
  `);
1260
1260
  });
1261
1261
 
1262
+ test("override with path", () => {
1263
+ const userSchema = z.object({
1264
+ name: z.string(),
1265
+ age: z.number(),
1266
+ });
1267
+
1268
+ const capturedPaths: (string | number)[][] = [];
1269
+
1270
+ z.toJSONSchema(userSchema, {
1271
+ override(ctx) {
1272
+ capturedPaths.push(ctx.path);
1273
+ },
1274
+ });
1275
+
1276
+ expect(capturedPaths).toMatchInlineSnapshot(`
1277
+ [
1278
+ [
1279
+ "properties",
1280
+ "age",
1281
+ ],
1282
+ [
1283
+ "properties",
1284
+ "name",
1285
+ ],
1286
+ [],
1287
+ ]
1288
+ `);
1289
+ });
1290
+
1262
1291
  test("pipe", () => {
1263
1292
  const mySchema = z
1264
1293
  .string()
@@ -1717,16 +1746,17 @@ test("basic registry", () => {
1717
1746
  myRegistry.add(User, { id: "User" });
1718
1747
  myRegistry.add(Post, { id: "Post" });
1719
1748
 
1720
- const result = z.z.toJSONSchema(myRegistry);
1749
+ const result = z.z.toJSONSchema(myRegistry, { uri: (id) => `https://example.com/${id}.json` });
1721
1750
  expect(result).toMatchInlineSnapshot(`
1722
1751
  {
1723
1752
  "schemas": {
1724
1753
  "Post": {
1754
+ "$id": "https://example.com/Post.json",
1725
1755
  "$schema": "https://json-schema.org/draft/2020-12/schema",
1726
1756
  "additionalProperties": false,
1727
1757
  "properties": {
1728
1758
  "author": {
1729
- "$ref": "User",
1759
+ "$ref": "https://example.com/User.json",
1730
1760
  },
1731
1761
  "content": {
1732
1762
  "type": "string",
@@ -1743,6 +1773,7 @@ test("basic registry", () => {
1743
1773
  "type": "object",
1744
1774
  },
1745
1775
  "User": {
1776
+ "$id": "https://example.com/User.json",
1746
1777
  "$schema": "https://json-schema.org/draft/2020-12/schema",
1747
1778
  "additionalProperties": false,
1748
1779
  "properties": {
@@ -1751,7 +1782,7 @@ test("basic registry", () => {
1751
1782
  },
1752
1783
  "posts": {
1753
1784
  "items": {
1754
- "$ref": "Post",
1785
+ "$ref": "https://example.com/Post.json",
1755
1786
  },
1756
1787
  "type": "array",
1757
1788
  },
@@ -1858,9 +1889,8 @@ test("input type", () => {
1858
1889
  e: z.string().prefault("hello"),
1859
1890
  f: z.string().catch("hello"),
1860
1891
  g: z.never(),
1861
- h: z.undefined(),
1862
- i: z.union([z.string(), z.number().default(2)]),
1863
- j: z.union([z.string(), z.string().optional()]),
1892
+ h: z.union([z.string(), z.number().default(2)]),
1893
+ i: z.union([z.string(), z.string().optional()]),
1864
1894
  });
1865
1895
  expect(z.toJSONSchema(schema, { io: "input" })).toMatchInlineSnapshot(`
1866
1896
  {
@@ -1898,9 +1928,6 @@ test("input type", () => {
1898
1928
  "not": {},
1899
1929
  },
1900
1930
  "h": {
1901
- "not": {},
1902
- },
1903
- "i": {
1904
1931
  "anyOf": [
1905
1932
  {
1906
1933
  "type": "string",
@@ -1911,7 +1938,7 @@ test("input type", () => {
1911
1938
  },
1912
1939
  ],
1913
1940
  },
1914
- "j": {
1941
+ "i": {
1915
1942
  "anyOf": [
1916
1943
  {
1917
1944
  "type": "string",
@@ -1966,9 +1993,6 @@ test("input type", () => {
1966
1993
  "not": {},
1967
1994
  },
1968
1995
  "h": {
1969
- "not": {},
1970
- },
1971
- "i": {
1972
1996
  "anyOf": [
1973
1997
  {
1974
1998
  "type": "string",
@@ -1979,7 +2003,7 @@ test("input type", () => {
1979
2003
  },
1980
2004
  ],
1981
2005
  },
1982
- "j": {
2006
+ "i": {
1983
2007
  "anyOf": [
1984
2008
  {
1985
2009
  "type": "string",
@@ -1997,7 +2021,7 @@ test("input type", () => {
1997
2021
  "e",
1998
2022
  "f",
1999
2023
  "g",
2000
- "i",
2024
+ "h",
2001
2025
  ],
2002
2026
  "type": "object",
2003
2027
  }
@@ -2251,3 +2275,40 @@ test("custom toJSONSchema", () => {
2251
2275
  }
2252
2276
  `);
2253
2277
  });
2278
+
2279
+ test("cycle detection - root", () => {
2280
+ const schema = z.object({
2281
+ name: z.string(),
2282
+ get subcategories() {
2283
+ return z.array(schema);
2284
+ },
2285
+ });
2286
+
2287
+ expect(() => z.toJSONSchema(schema, { cycles: "throw" })).toThrowErrorMatchingInlineSnapshot(`
2288
+ [Error: Cycle detected: #/properties/subcategories/items/<root>
2289
+
2290
+ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.]
2291
+ `);
2292
+ });
2293
+
2294
+ test("cycle detection - mutual recursion", () => {
2295
+ const A = z.object({
2296
+ name: z.string(),
2297
+ get subcategories() {
2298
+ return z.array(B);
2299
+ },
2300
+ });
2301
+
2302
+ const B = z.object({
2303
+ name: z.string(),
2304
+ get subcategories() {
2305
+ return z.array(A);
2306
+ },
2307
+ });
2308
+
2309
+ expect(() => z.toJSONSchema(A, { cycles: "throw" })).toThrowErrorMatchingInlineSnapshot(`
2310
+ [Error: Cycle detected: #/properties/subcategories/items/properties/subcategories/items/<root>
2311
+
2312
+ Set the \`cycles\` parameter to \`"ref"\` to resolve cyclical schemas with defs.]
2313
+ `);
2314
+ });
@@ -779,6 +779,8 @@ export function _nan<T extends schemas.$ZodNaN>(Class: util.SchemaClass<T>, para
779
779
  });
780
780
  }
781
781
 
782
+ // export type $ZodCheckParams = CheckParams<checks.$ZodCheck, "abort">;
783
+
782
784
  export type $ZodCheckLessThanParams = CheckParams<checks.$ZodCheckLessThan, "inclusive" | "value">;
783
785
  export function _lt(
784
786
  value: util.Numeric,
@@ -13,18 +13,16 @@ export interface $ZodCheckDef {
13
13
  error?: errors.$ZodErrorMap<never> | undefined;
14
14
  /** If true, no later checks will be executed if this check fails. Default `false`. */
15
15
  abort?: boolean | undefined;
16
+ /** If provided, this check will only be executed if the function returns `true`. Defaults to `payload => z.util.isAborted(payload)`. */
17
+ when?: ((payload: schemas.ParsePayload) => boolean) | undefined;
16
18
  }
17
19
 
18
20
  export interface $ZodCheckInternals<T> {
19
21
  def: $ZodCheckDef;
20
22
  /** The set of issues this check might throw. */
21
23
  issc?: errors.$ZodIssueBase;
22
- // "_check"(input: $ZodResult<T>): util.MaybeAsync<void>;
23
24
  check(payload: schemas.ParsePayload<T>): util.MaybeAsync<void>;
24
- // _parseB(payload: ParsePayload<any>, ctx: ParseContext): util.MaybeAsync<ParsePayload>;
25
25
  onattach: ((schema: schemas.$ZodType) => void)[];
26
- // "_async": boolean;
27
- when?: ((payload: schemas.ParsePayload) => boolean) | undefined;
28
26
  }
29
27
 
30
28
  export interface $ZodCheck<in T = never> {
@@ -452,7 +450,7 @@ export const $ZodCheckMaxSize: core.$constructor<$ZodCheckMaxSize> = /*@__PURE__
452
450
  (inst, def) => {
453
451
  $ZodCheck.init(inst, def);
454
452
 
455
- inst._zod.when = (payload) => {
453
+ inst._zod.def.when ??= (payload) => {
456
454
  const val = payload.value;
457
455
  return !util.nullish(val) && (val as any).size !== undefined;
458
456
  };
@@ -501,7 +499,7 @@ export const $ZodCheckMinSize: core.$constructor<$ZodCheckMinSize> = /*@__PURE__
501
499
  (inst, def) => {
502
500
  $ZodCheck.init(inst, def);
503
501
 
504
- inst._zod.when = (payload) => {
502
+ inst._zod.def.when ??= (payload) => {
505
503
  const val = payload.value;
506
504
  return !util.nullish(val) && (val as any).size !== undefined;
507
505
  };
@@ -550,7 +548,7 @@ export const $ZodCheckSizeEquals: core.$constructor<$ZodCheckSizeEquals> = /*@__
550
548
  (inst, def) => {
551
549
  $ZodCheck.init(inst, def);
552
550
 
553
- inst._zod.when = (payload) => {
551
+ inst._zod.def.when ??= (payload) => {
554
552
  const val = payload.value;
555
553
  return !util.nullish(val) && (val as any).size !== undefined;
556
554
  };
@@ -604,7 +602,7 @@ export const $ZodCheckMaxLength: core.$constructor<$ZodCheckMaxLength> = /*@__PU
604
602
  (inst, def) => {
605
603
  $ZodCheck.init(inst, def);
606
604
 
607
- inst._zod.when = (payload) => {
605
+ inst._zod.def.when ??= (payload) => {
608
606
  const val = payload.value;
609
607
  return !util.nullish(val) && (val as any).length !== undefined;
610
608
  };
@@ -655,7 +653,7 @@ export const $ZodCheckMinLength: core.$constructor<$ZodCheckMinLength> = /*@__PU
655
653
  (inst, def) => {
656
654
  $ZodCheck.init(inst, def);
657
655
 
658
- inst._zod.when = (payload) => {
656
+ inst._zod.def.when ??= (payload) => {
659
657
  const val = payload.value;
660
658
  return !util.nullish(val) && (val as any).length !== undefined;
661
659
  };
@@ -707,7 +705,7 @@ export const $ZodCheckLengthEquals: core.$constructor<$ZodCheckLengthEquals> = /
707
705
  (inst, def) => {
708
706
  $ZodCheck.init(inst, def);
709
707
 
710
- inst._zod.when = (payload) => {
708
+ inst._zod.def.when ??= (payload) => {
711
709
  const val = payload.value;
712
710
  return !util.nullish(val) && (val as any).length !== undefined;
713
711
  };
@@ -203,6 +203,10 @@ const initializer = (inst: $ZodError, def: $ZodIssue[]): void => {
203
203
  enumerable: true,
204
204
  // configurable: false,
205
205
  });
206
+ Object.defineProperty(inst, "toString", {
207
+ value: () => inst.message,
208
+ enumerable: false,
209
+ });
206
210
  };
207
211
 
208
212
  export const $ZodError: $constructor<$ZodError> = $constructor("$ZodError", initializer);