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
@@ -32,7 +32,6 @@ export interface _$ZodTypeInternals {
32
32
  /** Schema definition. */
33
33
  def: $ZodTypeDef;
34
34
  /** @internal Randomly generated ID for this schema. */
35
- id: string;
36
35
  /** @internal List of deferred initializers. */
37
36
  deferred: util.AnyFunc[] | undefined;
38
37
  /** @internal Parses input and runs all checks (refinements). */
@@ -516,11 +515,13 @@ export interface $ZodArrayDef<T extends SomeType = $ZodType> extends $ZodTypeDef
516
515
  type: "array";
517
516
  element: T;
518
517
  }
519
- export interface $ZodArrayInternals<T extends SomeType = $ZodType> extends $ZodTypeInternals<core.output<T>[], core.input<T>[]> {
518
+ export interface $ZodArrayInternals<T extends SomeType = $ZodType> extends _$ZodTypeInternals {
520
519
  def: $ZodArrayDef<T>;
521
520
  isst: errors.$ZodIssueInvalidType;
521
+ output: core.output<T>[];
522
+ input: core.input<T>[];
522
523
  }
523
- export interface $ZodArray<T extends SomeType = $ZodType> extends $ZodType<core.output<T>[], core.input<T>[], $ZodArrayInternals<T>> {
524
+ export interface $ZodArray<T extends SomeType = $ZodType> extends $ZodType<any, any, $ZodArrayInternals<T>> {
524
525
  }
525
526
  export declare const $ZodArray: core.$constructor<$ZodArray>;
526
527
  type OptionalOutSchema = {
@@ -533,12 +534,12 @@ type OptionalInSchema = {
533
534
  optin: "optional";
534
535
  };
535
536
  };
536
- export type $InferObjectOutput<T extends $ZodLooseShape, Extra extends Record<string, unknown>> = string extends keyof T ? Record<string, unknown> : keyof (T & Extra) extends never ? Record<string, never> : util.Prettify<{
537
+ export type $InferObjectOutput<T extends $ZodLooseShape, Extra extends Record<string, unknown>> = string extends keyof T ? util.IsAny<T[keyof T]> extends true ? Record<string, unknown> : Record<string, core.output<T[keyof T]>> : keyof (T & Extra) extends never ? Record<string, never> : util.Prettify<{
537
538
  -readonly [k in keyof T as T[k] extends OptionalOutSchema ? never : k]: T[k]["_zod"]["output"];
538
539
  } & {
539
540
  -readonly [k in keyof T as T[k] extends OptionalOutSchema ? k : never]?: T[k]["_zod"]["output"];
540
541
  } & Extra>;
541
- export type $InferObjectInput<T extends $ZodLooseShape, Extra extends Record<string, unknown>> = string extends keyof T ? Record<string, unknown> : keyof (T & Extra) extends never ? Record<string, never> : util.Prettify<{
542
+ export type $InferObjectInput<T extends $ZodLooseShape, Extra extends Record<string, unknown>> = string extends keyof T ? util.IsAny<T[keyof T]> extends true ? Record<string, unknown> : Record<string, core.input<T[keyof T]>> : keyof (T & Extra) extends never ? Record<string, never> : util.Prettify<{
542
543
  -readonly [k in keyof T as T[k] extends OptionalInSchema ? never : k]: T[k]["_zod"]["input"];
543
544
  } & {
544
545
  -readonly [k in keyof T as T[k] extends OptionalInSchema ? k : never]?: T[k]["_zod"]["input"];
@@ -600,14 +601,17 @@ export interface $ZodUnionDef<Options extends readonly SomeType[] = readonly $Zo
600
601
  }
601
602
  type IsOptionalIn<T extends SomeType> = T extends OptionalInSchema ? true : false;
602
603
  type IsOptionalOut<T extends SomeType> = T extends OptionalOutSchema ? true : false;
603
- export interface $ZodUnionInternals<T extends readonly SomeType[] = readonly $ZodType[]> extends $ZodTypeInternals<$InferUnionOutput<T[number]>, $InferUnionInput<T[number]>> {
604
+ export interface $ZodUnionInternals<T extends readonly SomeType[] = readonly $ZodType[]> extends _$ZodTypeInternals {
604
605
  def: $ZodUnionDef<T>;
605
606
  isst: errors.$ZodIssueInvalidUnion;
606
607
  pattern: T[number]["_zod"]["pattern"];
608
+ values: T[number]["_zod"]["values"];
609
+ output: $InferUnionOutput<T[number]>;
610
+ input: $InferUnionInput<T[number]>;
607
611
  optin: IsOptionalIn<T[number]> extends false ? "optional" | undefined : "optional";
608
612
  optout: IsOptionalOut<T[number]> extends false ? "optional" | undefined : "optional";
609
613
  }
610
- export interface $ZodUnion<T extends readonly SomeType[] = readonly $ZodType[]> extends $ZodType {
614
+ export interface $ZodUnion<T extends readonly SomeType[] = readonly $ZodType[]> extends $ZodType<any, any, $ZodUnionInternals<T>> {
611
615
  _zod: $ZodUnionInternals<T>;
612
616
  }
613
617
  export declare const $ZodUnion: core.$constructor<$ZodUnion>;
@@ -631,6 +635,8 @@ export interface $ZodIntersectionDef<Left extends SomeType = $ZodType, Right ext
631
635
  export interface $ZodIntersectionInternals<A extends SomeType = $ZodType, B extends SomeType = $ZodType> extends $ZodTypeInternals<core.output<A> & core.output<B>, core.input<A> & core.input<B>> {
632
636
  def: $ZodIntersectionDef<A, B>;
633
637
  isst: never;
638
+ optin: A["_zod"]["optin"] | B["_zod"]["optin"];
639
+ optout: A["_zod"]["optout"] | B["_zod"]["optout"];
634
640
  }
635
641
  export interface $ZodIntersection<A extends SomeType = $ZodType, B extends SomeType = $ZodType> extends $ZodType {
636
642
  _zod: $ZodIntersectionInternals<A, B>;
@@ -677,12 +683,17 @@ export interface $ZodRecordDef<Key extends $ZodRecordKey = $ZodRecordKey, Value
677
683
  keyType: Key;
678
684
  valueType: Value;
679
685
  }
680
- export type $InferZodRecordOutput<Key extends $ZodRecordKey = $ZodRecordKey, Value extends SomeType = $ZodType> = undefined extends Key["_zod"]["values"] ? string extends core.output<Key> ? Record<core.output<Key>, core.output<Value>> : number extends core.output<Key> ? Record<core.output<Key>, core.output<Value>> : symbol extends core.output<Key> ? Record<core.output<Key>, core.output<Value>> : Partial<Record<core.output<Key>, core.output<Value>>> : Record<core.output<Key>, core.output<Value>>;
681
- export type $InferZodRecordInput<Key extends $ZodRecordKey = $ZodRecordKey, Value extends SomeType = $ZodType> = undefined extends Key["_zod"]["values"] ? string extends core.input<Key> ? Record<core.input<Key>, core.input<Value>> : number extends core.input<Key> ? Record<core.input<Key>, core.input<Value>> : symbol extends core.input<Key> ? Record<core.input<Key>, core.input<Value>> : Partial<Record<core.input<Key>, core.input<Value>>> : Record<core.input<Key>, core.input<Value>>;
686
+ export type $InferZodRecordOutput<Key extends $ZodRecordKey = $ZodRecordKey, Value extends SomeType = $ZodType> = Key extends $partial ? Partial<Record<core.output<Key>, core.output<Value>>> : Record<core.output<Key>, core.output<Value>>;
687
+ export type $InferZodRecordInput<Key extends $ZodRecordKey = $ZodRecordKey, Value extends SomeType = $ZodType> = Key extends $partial ? Partial<Record<core.input<Key>, core.input<Value>>> : Record<core.input<Key>, core.input<Value>>;
682
688
  export interface $ZodRecordInternals<Key extends $ZodRecordKey = $ZodRecordKey, Value extends SomeType = $ZodType> extends $ZodTypeInternals<$InferZodRecordOutput<Key, Value>, $InferZodRecordInput<Key, Value>> {
683
689
  def: $ZodRecordDef<Key, Value>;
684
690
  isst: errors.$ZodIssueInvalidType | errors.$ZodIssueInvalidKey<Record<PropertyKey, unknown>>;
691
+ optin?: "optional" | undefined;
692
+ optout?: "optional" | undefined;
685
693
  }
694
+ export type $partial = {
695
+ "~~partial": true;
696
+ };
686
697
  export interface $ZodRecord<Key extends $ZodRecordKey = $ZodRecordKey, Value extends SomeType = $ZodType> extends $ZodType {
687
698
  _zod: $ZodRecordInternals<Key, Value>;
688
699
  }
@@ -695,6 +706,8 @@ export interface $ZodMapDef<Key extends SomeType = $ZodType, Value extends SomeT
695
706
  export interface $ZodMapInternals<Key extends SomeType = $ZodType, Value extends SomeType = $ZodType> extends $ZodTypeInternals<Map<core.output<Key>, core.output<Value>>, Map<core.input<Key>, core.input<Value>>> {
696
707
  def: $ZodMapDef<Key, Value>;
697
708
  isst: errors.$ZodIssueInvalidType | errors.$ZodIssueInvalidKey | errors.$ZodIssueInvalidElement<unknown>;
709
+ optin?: "optional" | undefined;
710
+ optout?: "optional" | undefined;
698
711
  }
699
712
  export interface $ZodMap<Key extends SomeType = $ZodType, Value extends SomeType = $ZodType> extends $ZodType {
700
713
  _zod: $ZodMapInternals<Key, Value>;
@@ -707,6 +720,8 @@ export interface $ZodSetDef<T extends SomeType = $ZodType> extends $ZodTypeDef {
707
720
  export interface $ZodSetInternals<T extends SomeType = $ZodType> extends $ZodTypeInternals<Set<core.output<T>>, Set<core.input<T>>> {
708
721
  def: $ZodSetDef<T>;
709
722
  isst: errors.$ZodIssueInvalidType;
723
+ optin?: "optional" | undefined;
724
+ optout?: "optional" | undefined;
710
725
  }
711
726
  export interface $ZodSet<T extends SomeType = $ZodType> extends $ZodType {
712
727
  _zod: $ZodSetInternals<T>;
@@ -819,6 +834,7 @@ export interface $ZodDefaultDef<T extends SomeType = $ZodType> extends $ZodTypeD
819
834
  export interface $ZodDefaultInternals<T extends SomeType = $ZodType> extends $ZodTypeInternals<util.NoUndefined<core.output<T>>, core.input<T> | undefined> {
820
835
  def: $ZodDefaultDef<T>;
821
836
  optin: "optional";
837
+ optout?: "optional" | undefined;
822
838
  isst: never;
823
839
  values: T["_zod"]["values"];
824
840
  }
@@ -835,6 +851,7 @@ export interface $ZodPrefaultDef<T extends SomeType = $ZodType> extends $ZodType
835
851
  export interface $ZodPrefaultInternals<T extends SomeType = $ZodType> extends $ZodTypeInternals<util.NoUndefined<core.output<T>>, core.input<T> | undefined> {
836
852
  def: $ZodPrefaultDef<T>;
837
853
  optin: "optional";
854
+ optout?: "optional" | undefined;
838
855
  isst: never;
839
856
  values: T["_zod"]["values"];
840
857
  }
@@ -850,6 +867,8 @@ export interface $ZodNonOptionalInternals<T extends SomeType = $ZodType> extends
850
867
  def: $ZodNonOptionalDef<T>;
851
868
  isst: errors.$ZodIssueInvalidType;
852
869
  values: T["_zod"]["values"];
870
+ optin: "optional" | undefined;
871
+ optout: "optional" | undefined;
853
872
  }
854
873
  export interface $ZodNonOptional<T extends SomeType = $ZodType> extends $ZodType {
855
874
  _zod: $ZodNonOptionalInternals<T>;
@@ -862,6 +881,8 @@ export interface $ZodSuccessDef<T extends SomeType = $ZodType> extends $ZodTypeD
862
881
  export interface $ZodSuccessInternals<T extends SomeType = $ZodType> extends $ZodTypeInternals<boolean, core.input<T>> {
863
882
  def: $ZodSuccessDef<T>;
864
883
  isst: never;
884
+ optin: T["_zod"]["optin"];
885
+ optout: "optional" | undefined;
865
886
  }
866
887
  export interface $ZodSuccess<T extends SomeType = $ZodType> extends $ZodType {
867
888
  _zod: $ZodSuccessInternals<T>;
@@ -928,6 +949,7 @@ export interface $ZodReadonlyInternals<T extends SomeType = $ZodType> extends $Z
928
949
  optout: T["_zod"]["optout"];
929
950
  isst: never;
930
951
  propValues: T["_zod"]["propValues"];
952
+ values: T["_zod"]["values"];
931
953
  }
932
954
  export interface $ZodReadonly<T extends SomeType = $ZodType> extends $ZodType {
933
955
  _zod: $ZodReadonlyInternals<T>;
@@ -8,8 +8,6 @@ import { version } from "./versions.js";
8
8
  export const $ZodType = /*@__PURE__*/ core.$constructor("$ZodType", (inst, def) => {
9
9
  var _a;
10
10
  inst ?? (inst = {});
11
- // avoids issues with using Math.random() in Next.js caching
12
- util.defineLazy(inst._zod, "id", () => def.type + "_" + util.randomString(10));
13
11
  inst._zod.def = def; // set _def property
14
12
  inst._zod.bag = inst._zod.bag || {}; // initialize _bag object
15
13
  inst._zod.version = version;
@@ -37,8 +35,8 @@ export const $ZodType = /*@__PURE__*/ core.$constructor("$ZodType", (inst, def)
37
35
  let isAborted = util.aborted(payload);
38
36
  let asyncResult;
39
37
  for (const ch of checks) {
40
- if (ch._zod.when) {
41
- const shouldRun = ch._zod.when(payload);
38
+ if (ch._zod.def.when) {
39
+ const shouldRun = ch._zod.def.when(payload);
42
40
  if (!shouldRun)
43
41
  continue;
44
42
  }
@@ -158,7 +156,9 @@ export const $ZodURL = /*@__PURE__*/ core.$constructor("$ZodURL", (inst, def) =>
158
156
  $ZodStringFormat.init(inst, def);
159
157
  inst._zod.check = (payload) => {
160
158
  try {
161
- const url = new URL(payload.value);
159
+ const orig = payload.value;
160
+ const url = new URL(orig);
161
+ const href = url.href;
162
162
  if (def.hostname) {
163
163
  def.hostname.lastIndex = 0;
164
164
  if (!def.hostname.test(url.hostname)) {
@@ -187,6 +187,13 @@ export const $ZodURL = /*@__PURE__*/ core.$constructor("$ZodURL", (inst, def) =>
187
187
  });
188
188
  }
189
189
  }
190
+ // payload.value = url.href;
191
+ if (!orig.endsWith("/") && href.endsWith("/")) {
192
+ payload.value = href.slice(0, -1);
193
+ }
194
+ else {
195
+ payload.value = href;
196
+ }
190
197
  return;
191
198
  }
192
199
  catch (_) {
@@ -720,8 +727,9 @@ export const $ZodObject = /*@__PURE__*/ core.$constructor("$ZodObject", (inst, d
720
727
  };
721
728
  doc.write(`const input = payload.value;`);
722
729
  const ids = Object.create(null);
730
+ let counter = 0;
723
731
  for (const key of normalized.keys) {
724
- ids[key] = util.randomString(15);
732
+ ids[key] = `key_${counter++}`;
725
733
  }
726
734
  // A: preserve key order {
727
735
  doc.write(`const newResult = {}`);
@@ -1595,6 +1603,7 @@ function handlePipeResult(left, def, ctx) {
1595
1603
  export const $ZodReadonly = /*@__PURE__*/ core.$constructor("$ZodReadonly", (inst, def) => {
1596
1604
  $ZodType.init(inst, def);
1597
1605
  util.defineLazy(inst._zod, "propValues", () => def.innerType._zod.propValues);
1606
+ util.defineLazy(inst._zod, "values", () => def.innerType._zod.values);
1598
1607
  util.defineLazy(inst._zod, "optin", () => def.innerType._zod.optin);
1599
1608
  util.defineLazy(inst._zod, "optout", () => def.innerType._zod.optout);
1600
1609
  inst._zod.parse = (payload, ctx) => {
@@ -36,7 +36,7 @@ class JSONSchemaGenerator {
36
36
  return seen.schema;
37
37
  }
38
38
  // initialize
39
- const result = { schema: {}, count: 1, cycle: undefined };
39
+ const result = { schema: {}, count: 1, cycle: undefined, path: _params.path };
40
40
  this.seen.set(schema, result);
41
41
  // custom method overrides default behavior
42
42
  const overrideSchema = schema._zod.toJSONSchema?.();
@@ -151,9 +151,10 @@ class JSONSchemaGenerator {
151
151
  case "unknown": {
152
152
  break;
153
153
  }
154
- case "undefined":
155
- case "never": {
156
- _json.not = {};
154
+ case "undefined": {
155
+ if (this.unrepresentable === "throw") {
156
+ throw new Error("Undefined cannot be represented in JSON Schema");
157
+ }
157
158
  break;
158
159
  }
159
160
  case "void": {
@@ -162,6 +163,10 @@ class JSONSchemaGenerator {
162
163
  }
163
164
  break;
164
165
  }
166
+ case "never": {
167
+ _json.not = {};
168
+ break;
169
+ }
165
170
  case "date": {
166
171
  if (this.unrepresentable === "throw") {
167
172
  throw new Error("Date cannot be represented in JSON Schema");
@@ -531,6 +536,8 @@ class JSONSchemaGenerator {
531
536
  throw new Error("Unprocessed schema. This is a bug in Zod.");
532
537
  // initialize result with root schema fields
533
538
  // Object.assign(result, seen.cached);
539
+ // returns a ref to the schema
540
+ // defId will be empty if the ref points to an external schema (or #)
534
541
  const makeURI = (entry) => {
535
542
  // comparing the seen objects because sometimes
536
543
  // multiple schemas map to the same seen object.
@@ -540,12 +547,14 @@ class JSONSchemaGenerator {
540
547
  if (params.external) {
541
548
  const externalId = params.external.registry.get(entry[0])?.id; // ?? "__shared";// `__schema${this.counter++}`;
542
549
  // check if schema is in the external registry
543
- if (externalId)
544
- return { ref: params.external.uri(externalId) };
550
+ const uriGenerator = params.external.uri ?? ((id) => id);
551
+ if (externalId) {
552
+ return { ref: uriGenerator(externalId) };
553
+ }
545
554
  // otherwise, add to __shared
546
555
  const id = entry[1].defId ?? entry[1].schema.id ?? `schema${this.counter++}`;
547
- entry[1].defId = id;
548
- return { defId: id, ref: `${params.external.uri("__shared")}#/${defsSegment}/${id}` };
556
+ entry[1].defId = id; // set defId so it will be reused if needed
557
+ return { defId: id, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id}` };
549
558
  }
550
559
  if (entry[1] === root) {
551
560
  return { ref: "#" };
@@ -559,6 +568,7 @@ class JSONSchemaGenerator {
559
568
  // stored cached version in `def` property
560
569
  // remove all properties, set $ref
561
570
  const extractToDef = (entry) => {
571
+ // if the schema is already a reference, do not extract it
562
572
  if (entry[1].schema.$ref) {
563
573
  return;
564
574
  }
@@ -575,14 +585,24 @@ class JSONSchemaGenerator {
575
585
  }
576
586
  schema.$ref = ref;
577
587
  };
588
+ // throw on cycles
589
+ // break cycles
590
+ if (params.cycles === "throw") {
591
+ for (const entry of this.seen.entries()) {
592
+ const seen = entry[1];
593
+ if (seen.cycle) {
594
+ throw new Error("Cycle detected: " +
595
+ `#/${seen.cycle?.join("/")}/<root>` +
596
+ '\n\nSet the `cycles` parameter to `"ref"` to resolve cyclical schemas with defs.');
597
+ }
598
+ }
599
+ }
578
600
  // extract schemas into $defs
579
601
  for (const entry of this.seen.entries()) {
580
602
  const seen = entry[1];
581
603
  // convert root schema to # $ref
582
- // also prevents root schema from being extracted
583
604
  if (schema === entry[0]) {
584
- // do not copy to defs...this is the root schema
585
- extractToDef(entry);
605
+ extractToDef(entry); // this has special handling for the root schema
586
606
  continue;
587
607
  }
588
608
  // extract schemas that are in the external registry
@@ -601,14 +621,8 @@ class JSONSchemaGenerator {
601
621
  }
602
622
  // break cycles
603
623
  if (seen.cycle) {
604
- if (params.cycles === "throw") {
605
- throw new Error("Cycle detected: " +
606
- `#/${seen.cycle?.join("/")}/<root>` +
607
- '\n\nSet the `cycles` parameter to `"ref"` to resolve cyclical schemas with defs.');
608
- }
609
- else if (params.cycles === "ref") {
610
- extractToDef(entry);
611
- }
624
+ // any
625
+ extractToDef(entry);
612
626
  continue;
613
627
  }
614
628
  // extract reused schemas
@@ -650,6 +664,7 @@ class JSONSchemaGenerator {
650
664
  this.override({
651
665
  zodSchema: zodSchema,
652
666
  jsonSchema: schema,
667
+ path: seen.path ?? [],
653
668
  });
654
669
  };
655
670
  for (const entry of [...this.seen.entries()].reverse()) {
@@ -665,6 +680,12 @@ class JSONSchemaGenerator {
665
680
  else {
666
681
  console.warn(`Invalid target: ${this.target}`);
667
682
  }
683
+ if (params.external?.uri) {
684
+ const id = params.external.registry.get(schema)?.id;
685
+ if (!id)
686
+ throw new Error("Schema is missing an `id` property");
687
+ result.$id = params.external.uri(id);
688
+ }
668
689
  Object.assign(result, root.def);
669
690
  // build defs object
670
691
  const defs = params.external?.defs ?? {};
@@ -675,12 +696,16 @@ class JSONSchemaGenerator {
675
696
  }
676
697
  }
677
698
  // set definitions in result
678
- if (!params.external && Object.keys(defs).length > 0) {
679
- if (this.target === "draft-2020-12") {
680
- result.$defs = defs;
681
- }
682
- else {
683
- result.definitions = defs;
699
+ if (params.external) {
700
+ }
701
+ else {
702
+ if (Object.keys(defs).length > 0) {
703
+ if (this.target === "draft-2020-12") {
704
+ result.$defs = defs;
705
+ }
706
+ else {
707
+ result.definitions = defs;
708
+ }
684
709
  }
685
710
  }
686
711
  try {
@@ -706,7 +731,7 @@ function toJSONSchema(input, _params) {
706
731
  const schemas = {};
707
732
  const external = {
708
733
  registry: input,
709
- uri: _params?.uri || ((id) => id),
734
+ uri: _params?.uri,
710
735
  defs,
711
736
  };
712
737
  for (const entry of input._idmap.entries()) {
@@ -17,9 +17,10 @@ interface JSONSchemaGeneratorParams {
17
17
  override?: (ctx: {
18
18
  zodSchema: schemas.$ZodTypes;
19
19
  jsonSchema: JSONSchema.BaseSchema;
20
+ path: (string | number)[];
20
21
  }) => void;
21
22
  /** Whether to extract the `"input"` or `"output"` type. Relevant to transforms, Error converting schema to JSONz, defaults, coerced primitives, etc.
22
- * - `"output" — Default. Convert the output schema.
23
+ * - `"output"` Default. Convert the output schema.
23
24
  * - `"input"` — Convert the input schema. */
24
25
  io?: "input" | "output";
25
26
  }
@@ -38,7 +39,7 @@ interface EmitParams {
38
39
  registry: $ZodRegistry<{
39
40
  id?: string | undefined;
40
41
  }>;
41
- uri: (id: string) => string;
42
+ uri?: ((id: string) => string) | undefined;
42
43
  defs: Record<string, JSONSchema.BaseSchema>;
43
44
  } | undefined;
44
45
  }
@@ -54,6 +55,8 @@ interface Seen {
54
55
  cycle?: (string | number)[] | undefined;
55
56
  isParent?: boolean | undefined;
56
57
  ref?: schemas.$ZodType | undefined | null;
58
+ /** JSON Schema property path for this schema */
59
+ path?: (string | number)[] | undefined;
57
60
  }
58
61
  export declare class JSONSchemaGenerator {
59
62
  metadataRegistry: $ZodRegistry<Record<string, any>>;
@@ -62,6 +65,7 @@ export declare class JSONSchemaGenerator {
62
65
  override: (ctx: {
63
66
  zodSchema: schemas.$ZodTypes;
64
67
  jsonSchema: JSONSchema.BaseSchema;
68
+ path: (string | number)[];
65
69
  }) => void;
66
70
  io: "input" | "output";
67
71
  counter: number;
@@ -17,9 +17,10 @@ interface JSONSchemaGeneratorParams {
17
17
  override?: (ctx: {
18
18
  zodSchema: schemas.$ZodTypes;
19
19
  jsonSchema: JSONSchema.BaseSchema;
20
+ path: (string | number)[];
20
21
  }) => void;
21
22
  /** Whether to extract the `"input"` or `"output"` type. Relevant to transforms, Error converting schema to JSONz, defaults, coerced primitives, etc.
22
- * - `"output" — Default. Convert the output schema.
23
+ * - `"output"` Default. Convert the output schema.
23
24
  * - `"input"` — Convert the input schema. */
24
25
  io?: "input" | "output";
25
26
  }
@@ -38,7 +39,7 @@ interface EmitParams {
38
39
  registry: $ZodRegistry<{
39
40
  id?: string | undefined;
40
41
  }>;
41
- uri: (id: string) => string;
42
+ uri?: ((id: string) => string) | undefined;
42
43
  defs: Record<string, JSONSchema.BaseSchema>;
43
44
  } | undefined;
44
45
  }
@@ -54,6 +55,8 @@ interface Seen {
54
55
  cycle?: (string | number)[] | undefined;
55
56
  isParent?: boolean | undefined;
56
57
  ref?: schemas.$ZodType | undefined | null;
58
+ /** JSON Schema property path for this schema */
59
+ path?: (string | number)[] | undefined;
57
60
  }
58
61
  export declare class JSONSchemaGenerator {
59
62
  metadataRegistry: $ZodRegistry<Record<string, any>>;
@@ -62,6 +65,7 @@ export declare class JSONSchemaGenerator {
62
65
  override: (ctx: {
63
66
  zodSchema: schemas.$ZodTypes;
64
67
  jsonSchema: JSONSchema.BaseSchema;
68
+ path: (string | number)[];
65
69
  }) => void;
66
70
  io: "input" | "output";
67
71
  counter: number;
@@ -32,7 +32,7 @@ export class JSONSchemaGenerator {
32
32
  return seen.schema;
33
33
  }
34
34
  // initialize
35
- const result = { schema: {}, count: 1, cycle: undefined };
35
+ const result = { schema: {}, count: 1, cycle: undefined, path: _params.path };
36
36
  this.seen.set(schema, result);
37
37
  // custom method overrides default behavior
38
38
  const overrideSchema = schema._zod.toJSONSchema?.();
@@ -147,9 +147,10 @@ export class JSONSchemaGenerator {
147
147
  case "unknown": {
148
148
  break;
149
149
  }
150
- case "undefined":
151
- case "never": {
152
- _json.not = {};
150
+ case "undefined": {
151
+ if (this.unrepresentable === "throw") {
152
+ throw new Error("Undefined cannot be represented in JSON Schema");
153
+ }
153
154
  break;
154
155
  }
155
156
  case "void": {
@@ -158,6 +159,10 @@ export class JSONSchemaGenerator {
158
159
  }
159
160
  break;
160
161
  }
162
+ case "never": {
163
+ _json.not = {};
164
+ break;
165
+ }
161
166
  case "date": {
162
167
  if (this.unrepresentable === "throw") {
163
168
  throw new Error("Date cannot be represented in JSON Schema");
@@ -527,6 +532,8 @@ export class JSONSchemaGenerator {
527
532
  throw new Error("Unprocessed schema. This is a bug in Zod.");
528
533
  // initialize result with root schema fields
529
534
  // Object.assign(result, seen.cached);
535
+ // returns a ref to the schema
536
+ // defId will be empty if the ref points to an external schema (or #)
530
537
  const makeURI = (entry) => {
531
538
  // comparing the seen objects because sometimes
532
539
  // multiple schemas map to the same seen object.
@@ -536,12 +543,14 @@ export class JSONSchemaGenerator {
536
543
  if (params.external) {
537
544
  const externalId = params.external.registry.get(entry[0])?.id; // ?? "__shared";// `__schema${this.counter++}`;
538
545
  // check if schema is in the external registry
539
- if (externalId)
540
- return { ref: params.external.uri(externalId) };
546
+ const uriGenerator = params.external.uri ?? ((id) => id);
547
+ if (externalId) {
548
+ return { ref: uriGenerator(externalId) };
549
+ }
541
550
  // otherwise, add to __shared
542
551
  const id = entry[1].defId ?? entry[1].schema.id ?? `schema${this.counter++}`;
543
- entry[1].defId = id;
544
- return { defId: id, ref: `${params.external.uri("__shared")}#/${defsSegment}/${id}` };
552
+ entry[1].defId = id; // set defId so it will be reused if needed
553
+ return { defId: id, ref: `${uriGenerator("__shared")}#/${defsSegment}/${id}` };
545
554
  }
546
555
  if (entry[1] === root) {
547
556
  return { ref: "#" };
@@ -555,6 +564,7 @@ export class JSONSchemaGenerator {
555
564
  // stored cached version in `def` property
556
565
  // remove all properties, set $ref
557
566
  const extractToDef = (entry) => {
567
+ // if the schema is already a reference, do not extract it
558
568
  if (entry[1].schema.$ref) {
559
569
  return;
560
570
  }
@@ -571,14 +581,24 @@ export class JSONSchemaGenerator {
571
581
  }
572
582
  schema.$ref = ref;
573
583
  };
584
+ // throw on cycles
585
+ // break cycles
586
+ if (params.cycles === "throw") {
587
+ for (const entry of this.seen.entries()) {
588
+ const seen = entry[1];
589
+ if (seen.cycle) {
590
+ throw new Error("Cycle detected: " +
591
+ `#/${seen.cycle?.join("/")}/<root>` +
592
+ '\n\nSet the `cycles` parameter to `"ref"` to resolve cyclical schemas with defs.');
593
+ }
594
+ }
595
+ }
574
596
  // extract schemas into $defs
575
597
  for (const entry of this.seen.entries()) {
576
598
  const seen = entry[1];
577
599
  // convert root schema to # $ref
578
- // also prevents root schema from being extracted
579
600
  if (schema === entry[0]) {
580
- // do not copy to defs...this is the root schema
581
- extractToDef(entry);
601
+ extractToDef(entry); // this has special handling for the root schema
582
602
  continue;
583
603
  }
584
604
  // extract schemas that are in the external registry
@@ -597,14 +617,8 @@ export class JSONSchemaGenerator {
597
617
  }
598
618
  // break cycles
599
619
  if (seen.cycle) {
600
- if (params.cycles === "throw") {
601
- throw new Error("Cycle detected: " +
602
- `#/${seen.cycle?.join("/")}/<root>` +
603
- '\n\nSet the `cycles` parameter to `"ref"` to resolve cyclical schemas with defs.');
604
- }
605
- else if (params.cycles === "ref") {
606
- extractToDef(entry);
607
- }
620
+ // any
621
+ extractToDef(entry);
608
622
  continue;
609
623
  }
610
624
  // extract reused schemas
@@ -646,6 +660,7 @@ export class JSONSchemaGenerator {
646
660
  this.override({
647
661
  zodSchema: zodSchema,
648
662
  jsonSchema: schema,
663
+ path: seen.path ?? [],
649
664
  });
650
665
  };
651
666
  for (const entry of [...this.seen.entries()].reverse()) {
@@ -661,6 +676,12 @@ export class JSONSchemaGenerator {
661
676
  else {
662
677
  console.warn(`Invalid target: ${this.target}`);
663
678
  }
679
+ if (params.external?.uri) {
680
+ const id = params.external.registry.get(schema)?.id;
681
+ if (!id)
682
+ throw new Error("Schema is missing an `id` property");
683
+ result.$id = params.external.uri(id);
684
+ }
664
685
  Object.assign(result, root.def);
665
686
  // build defs object
666
687
  const defs = params.external?.defs ?? {};
@@ -671,12 +692,16 @@ export class JSONSchemaGenerator {
671
692
  }
672
693
  }
673
694
  // set definitions in result
674
- if (!params.external && Object.keys(defs).length > 0) {
675
- if (this.target === "draft-2020-12") {
676
- result.$defs = defs;
677
- }
678
- else {
679
- result.definitions = defs;
695
+ if (params.external) {
696
+ }
697
+ else {
698
+ if (Object.keys(defs).length > 0) {
699
+ if (this.target === "draft-2020-12") {
700
+ result.$defs = defs;
701
+ }
702
+ else {
703
+ result.definitions = defs;
704
+ }
680
705
  }
681
706
  }
682
707
  try {
@@ -701,7 +726,7 @@ export function toJSONSchema(input, _params) {
701
726
  const schemas = {};
702
727
  const external = {
703
728
  registry: input,
704
- uri: _params?.uri || ((id) => id),
729
+ uri: _params?.uri,
705
730
  defs,
706
731
  };
707
732
  for (const entry of input._idmap.entries()) {
@@ -234,7 +234,7 @@ export interface ZodMiniRecord<Key extends core.$ZodRecordKey = core.$ZodRecordK
234
234
  }
235
235
  export declare const ZodMiniRecord: core.$constructor<ZodMiniRecord>;
236
236
  export declare function record<Key extends core.$ZodRecordKey, Value extends SomeType>(keyType: Key, valueType: Value, params?: string | core.$ZodRecordParams): ZodMiniRecord<Key, Value>;
237
- export declare function partialRecord<Key extends core.$ZodRecordKey, Value extends SomeType>(keyType: Key, valueType: Value, params?: string | core.$ZodRecordParams): ZodMiniRecord<ZodMiniUnion<[Key, ZodMiniNever]>, Value>;
237
+ export declare function partialRecord<Key extends core.$ZodRecordKey, Value extends SomeType>(keyType: Key, valueType: Value, params?: string | core.$ZodRecordParams): ZodMiniRecord<Key & core.$partial, Value>;
238
238
  export interface ZodMiniMap<Key extends SomeType = core.$ZodType, Value extends SomeType = core.$ZodType> extends _ZodMiniType<core.$ZodMapInternals<Key, Value>> {
239
239
  }
240
240
  export declare const ZodMiniMap: core.$constructor<ZodMiniMap>;
@@ -234,7 +234,7 @@ export interface ZodMiniRecord<Key extends core.$ZodRecordKey = core.$ZodRecordK
234
234
  }
235
235
  export declare const ZodMiniRecord: core.$constructor<ZodMiniRecord>;
236
236
  export declare function record<Key extends core.$ZodRecordKey, Value extends SomeType>(keyType: Key, valueType: Value, params?: string | core.$ZodRecordParams): ZodMiniRecord<Key, Value>;
237
- export declare function partialRecord<Key extends core.$ZodRecordKey, Value extends SomeType>(keyType: Key, valueType: Value, params?: string | core.$ZodRecordParams): ZodMiniRecord<ZodMiniUnion<[Key, ZodMiniNever]>, Value>;
237
+ export declare function partialRecord<Key extends core.$ZodRecordKey, Value extends SomeType>(keyType: Key, valueType: Value, params?: string | core.$ZodRecordParams): ZodMiniRecord<Key & core.$partial, Value>;
238
238
  export interface ZodMiniMap<Key extends SomeType = core.$ZodType, Value extends SomeType = core.$ZodType> extends _ZodMiniType<core.$ZodMapInternals<Key, Value>> {
239
239
  }
240
240
  export declare const ZodMiniMap: core.$constructor<ZodMiniMap>;