zod 4.1.12 → 4.1.13

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 (82) hide show
  1. package/package.json +1 -1
  2. package/src/v4/classic/checks.ts +1 -0
  3. package/src/v4/classic/schemas.ts +20 -0
  4. package/src/v4/classic/tests/continuability.test.ts +22 -0
  5. package/src/v4/classic/tests/describe-meta-checks.test.ts +27 -0
  6. package/src/v4/classic/tests/index.test.ts +55 -1
  7. package/src/v4/classic/tests/promise.test.ts +1 -1
  8. package/src/v4/classic/tests/readonly.test.ts +1 -1
  9. package/src/v4/classic/tests/record.test.ts +141 -9
  10. package/src/v4/classic/tests/registries.test.ts +5 -1
  11. package/src/v4/classic/tests/string.test.ts +72 -0
  12. package/src/v4/classic/tests/template-literal.test.ts +8 -0
  13. package/src/v4/classic/tests/to-json-schema.test.ts +97 -0
  14. package/src/v4/classic/tests/tuple.test.ts +18 -0
  15. package/src/v4/classic/tests/url.test.ts +13 -0
  16. package/src/v4/core/api.ts +45 -0
  17. package/src/v4/core/core.ts +22 -9
  18. package/src/v4/core/regexes.ts +6 -1
  19. package/src/v4/core/registries.ts +12 -1
  20. package/src/v4/core/schemas.ts +50 -33
  21. package/src/v4/core/tests/extend.test.ts +42 -1
  22. package/src/v4/core/tests/locales/he.test.ts +379 -0
  23. package/src/v4/core/tests/locales/nl.test.ts +46 -0
  24. package/src/v4/core/tests/record-constructor.test.ts +67 -0
  25. package/src/v4/core/tests/recursive-tuples.test.ts +45 -0
  26. package/src/v4/core/to-json-schema.ts +55 -91
  27. package/src/v4/core/util.ts +11 -0
  28. package/src/v4/core/versions.ts +1 -1
  29. package/src/v4/locales/en.ts +1 -0
  30. package/src/v4/locales/he.ts +202 -71
  31. package/src/v4/locales/nl.ts +10 -10
  32. package/src/v4/mini/iso.ts +4 -4
  33. package/src/v4/mini/schemas.ts +17 -0
  34. package/src/v4/mini/tests/functions.test.ts +0 -38
  35. package/src/v4/mini/tests/index.test.ts +24 -1
  36. package/src/v4/mini/tests/string.test.ts +32 -0
  37. package/v3/ZodError.d.cts +1 -1
  38. package/v3/ZodError.d.ts +1 -1
  39. package/v4/classic/checks.cjs +2 -1
  40. package/v4/classic/checks.d.cts +1 -1
  41. package/v4/classic/checks.d.ts +1 -1
  42. package/v4/classic/checks.js +1 -1
  43. package/v4/classic/schemas.cjs +15 -2
  44. package/v4/classic/schemas.d.cts +8 -0
  45. package/v4/classic/schemas.d.ts +8 -0
  46. package/v4/classic/schemas.js +12 -0
  47. package/v4/core/api.cjs +40 -0
  48. package/v4/core/api.d.cts +7 -0
  49. package/v4/core/api.d.ts +7 -0
  50. package/v4/core/api.js +36 -0
  51. package/v4/core/core.cjs +20 -11
  52. package/v4/core/core.js +20 -11
  53. package/v4/core/regexes.cjs +31 -2
  54. package/v4/core/regexes.d.cts +1 -0
  55. package/v4/core/regexes.d.ts +1 -0
  56. package/v4/core/regexes.js +5 -0
  57. package/v4/core/registries.cjs +3 -1
  58. package/v4/core/registries.js +3 -1
  59. package/v4/core/schemas.cjs +32 -33
  60. package/v4/core/schemas.d.cts +12 -2
  61. package/v4/core/schemas.d.ts +12 -2
  62. package/v4/core/schemas.js +30 -31
  63. package/v4/core/to-json-schema.cjs +55 -92
  64. package/v4/core/to-json-schema.js +55 -92
  65. package/v4/core/util.cjs +11 -0
  66. package/v4/core/util.d.cts +1 -0
  67. package/v4/core/util.d.ts +1 -0
  68. package/v4/core/util.js +10 -0
  69. package/v4/core/versions.cjs +1 -1
  70. package/v4/core/versions.js +1 -1
  71. package/v4/locales/en.cjs +1 -0
  72. package/v4/locales/en.js +1 -0
  73. package/v4/locales/he.cjs +177 -66
  74. package/v4/locales/he.js +177 -66
  75. package/v4/locales/nl.cjs +8 -8
  76. package/v4/locales/nl.js +8 -8
  77. package/v4/mini/iso.cjs +4 -4
  78. package/v4/mini/iso.js +4 -4
  79. package/v4/mini/schemas.cjs +13 -2
  80. package/v4/mini/schemas.d.cts +6 -0
  81. package/v4/mini/schemas.d.ts +6 -0
  82. package/v4/mini/schemas.js +10 -0
@@ -252,11 +252,19 @@ class JSONSchemaGenerator {
252
252
  }
253
253
  case "union": {
254
254
  const json = _json;
255
+ // Discriminated unions use oneOf (exactly one match) instead of anyOf (one or more matches)
256
+ // because the discriminator field ensures mutual exclusivity between options in JSON Schema
257
+ const isDiscriminated = def.discriminator !== undefined;
255
258
  const options = def.options.map((x, i) => this.process(x, {
256
259
  ...params,
257
- path: [...params.path, "anyOf", i],
260
+ path: [...params.path, isDiscriminated ? "oneOf" : "anyOf", i],
258
261
  }));
259
- json.anyOf = options;
262
+ if (isDiscriminated) {
263
+ json.oneOf = options;
264
+ }
265
+ else {
266
+ json.anyOf = options;
267
+ }
260
268
  break;
261
269
  }
262
270
  case "intersection": {
@@ -821,100 +829,55 @@ function isTransforming(_schema, _ctx) {
821
829
  if (ctx.seen.has(_schema))
822
830
  return false;
823
831
  ctx.seen.add(_schema);
824
- const schema = _schema;
825
- const def = schema._zod.def;
826
- switch (def.type) {
827
- case "string":
828
- case "number":
829
- case "bigint":
830
- case "boolean":
831
- case "date":
832
- case "symbol":
833
- case "undefined":
834
- case "null":
835
- case "any":
836
- case "unknown":
837
- case "never":
838
- case "void":
839
- case "literal":
840
- case "enum":
841
- case "nan":
842
- case "file":
843
- case "template_literal":
844
- return false;
845
- case "array": {
846
- return isTransforming(def.element, ctx);
847
- }
848
- case "object": {
849
- for (const key in def.shape) {
850
- if (isTransforming(def.shape[key], ctx))
851
- return true;
852
- }
853
- return false;
854
- }
855
- case "union": {
856
- for (const option of def.options) {
857
- if (isTransforming(option, ctx))
858
- return true;
859
- }
860
- return false;
861
- }
862
- case "intersection": {
863
- return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);
864
- }
865
- case "tuple": {
866
- for (const item of def.items) {
867
- if (isTransforming(item, ctx))
868
- return true;
869
- }
870
- if (def.rest && isTransforming(def.rest, ctx))
832
+ const def = _schema._zod.def;
833
+ if (def.type === "transform")
834
+ return true;
835
+ if (def.type === "array")
836
+ return isTransforming(def.element, ctx);
837
+ if (def.type === "set")
838
+ return isTransforming(def.valueType, ctx);
839
+ if (def.type === "lazy")
840
+ return isTransforming(def.getter(), ctx);
841
+ if (def.type === "promise" ||
842
+ def.type === "optional" ||
843
+ def.type === "nonoptional" ||
844
+ def.type === "nullable" ||
845
+ def.type === "readonly" ||
846
+ def.type === "default" ||
847
+ def.type === "prefault") {
848
+ return isTransforming(def.innerType, ctx);
849
+ }
850
+ if (def.type === "intersection") {
851
+ return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);
852
+ }
853
+ if (def.type === "record" || def.type === "map") {
854
+ return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
855
+ }
856
+ if (def.type === "pipe") {
857
+ return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
858
+ }
859
+ if (def.type === "object") {
860
+ for (const key in def.shape) {
861
+ if (isTransforming(def.shape[key], ctx))
871
862
  return true;
872
- return false;
873
- }
874
- case "record": {
875
- return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
876
863
  }
877
- case "map": {
878
- return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
879
- }
880
- case "set": {
881
- return isTransforming(def.valueType, ctx);
882
- }
883
- // inner types
884
- case "promise":
885
- case "optional":
886
- case "nonoptional":
887
- case "nullable":
888
- case "readonly":
889
- return isTransforming(def.innerType, ctx);
890
- case "lazy":
891
- return isTransforming(def.getter(), ctx);
892
- case "default": {
893
- return isTransforming(def.innerType, ctx);
894
- }
895
- case "prefault": {
896
- return isTransforming(def.innerType, ctx);
864
+ return false;
865
+ }
866
+ if (def.type === "union") {
867
+ for (const option of def.options) {
868
+ if (isTransforming(option, ctx))
869
+ return true;
897
870
  }
898
- case "custom": {
899
- return false;
871
+ return false;
872
+ }
873
+ if (def.type === "tuple") {
874
+ for (const item of def.items) {
875
+ if (isTransforming(item, ctx))
876
+ return true;
900
877
  }
901
- case "transform": {
878
+ if (def.rest && isTransforming(def.rest, ctx))
902
879
  return true;
903
- }
904
- case "pipe": {
905
- return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
906
- }
907
- case "success": {
908
- return false;
909
- }
910
- case "catch": {
911
- return false;
912
- }
913
- case "function": {
914
- return false;
915
- }
916
- default:
917
- def;
880
+ return false;
918
881
  }
919
- throw new Error(`Unknown schema type: ${def.type}`);
882
+ return false;
920
883
  }
@@ -248,11 +248,19 @@ export class JSONSchemaGenerator {
248
248
  }
249
249
  case "union": {
250
250
  const json = _json;
251
+ // Discriminated unions use oneOf (exactly one match) instead of anyOf (one or more matches)
252
+ // because the discriminator field ensures mutual exclusivity between options in JSON Schema
253
+ const isDiscriminated = def.discriminator !== undefined;
251
254
  const options = def.options.map((x, i) => this.process(x, {
252
255
  ...params,
253
- path: [...params.path, "anyOf", i],
256
+ path: [...params.path, isDiscriminated ? "oneOf" : "anyOf", i],
254
257
  }));
255
- json.anyOf = options;
258
+ if (isDiscriminated) {
259
+ json.oneOf = options;
260
+ }
261
+ else {
262
+ json.anyOf = options;
263
+ }
256
264
  break;
257
265
  }
258
266
  case "intersection": {
@@ -816,100 +824,55 @@ function isTransforming(_schema, _ctx) {
816
824
  if (ctx.seen.has(_schema))
817
825
  return false;
818
826
  ctx.seen.add(_schema);
819
- const schema = _schema;
820
- const def = schema._zod.def;
821
- switch (def.type) {
822
- case "string":
823
- case "number":
824
- case "bigint":
825
- case "boolean":
826
- case "date":
827
- case "symbol":
828
- case "undefined":
829
- case "null":
830
- case "any":
831
- case "unknown":
832
- case "never":
833
- case "void":
834
- case "literal":
835
- case "enum":
836
- case "nan":
837
- case "file":
838
- case "template_literal":
839
- return false;
840
- case "array": {
841
- return isTransforming(def.element, ctx);
842
- }
843
- case "object": {
844
- for (const key in def.shape) {
845
- if (isTransforming(def.shape[key], ctx))
846
- return true;
847
- }
848
- return false;
849
- }
850
- case "union": {
851
- for (const option of def.options) {
852
- if (isTransforming(option, ctx))
853
- return true;
854
- }
855
- return false;
856
- }
857
- case "intersection": {
858
- return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);
859
- }
860
- case "tuple": {
861
- for (const item of def.items) {
862
- if (isTransforming(item, ctx))
863
- return true;
864
- }
865
- if (def.rest && isTransforming(def.rest, ctx))
827
+ const def = _schema._zod.def;
828
+ if (def.type === "transform")
829
+ return true;
830
+ if (def.type === "array")
831
+ return isTransforming(def.element, ctx);
832
+ if (def.type === "set")
833
+ return isTransforming(def.valueType, ctx);
834
+ if (def.type === "lazy")
835
+ return isTransforming(def.getter(), ctx);
836
+ if (def.type === "promise" ||
837
+ def.type === "optional" ||
838
+ def.type === "nonoptional" ||
839
+ def.type === "nullable" ||
840
+ def.type === "readonly" ||
841
+ def.type === "default" ||
842
+ def.type === "prefault") {
843
+ return isTransforming(def.innerType, ctx);
844
+ }
845
+ if (def.type === "intersection") {
846
+ return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);
847
+ }
848
+ if (def.type === "record" || def.type === "map") {
849
+ return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
850
+ }
851
+ if (def.type === "pipe") {
852
+ return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
853
+ }
854
+ if (def.type === "object") {
855
+ for (const key in def.shape) {
856
+ if (isTransforming(def.shape[key], ctx))
866
857
  return true;
867
- return false;
868
- }
869
- case "record": {
870
- return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
871
858
  }
872
- case "map": {
873
- return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
874
- }
875
- case "set": {
876
- return isTransforming(def.valueType, ctx);
877
- }
878
- // inner types
879
- case "promise":
880
- case "optional":
881
- case "nonoptional":
882
- case "nullable":
883
- case "readonly":
884
- return isTransforming(def.innerType, ctx);
885
- case "lazy":
886
- return isTransforming(def.getter(), ctx);
887
- case "default": {
888
- return isTransforming(def.innerType, ctx);
889
- }
890
- case "prefault": {
891
- return isTransforming(def.innerType, ctx);
859
+ return false;
860
+ }
861
+ if (def.type === "union") {
862
+ for (const option of def.options) {
863
+ if (isTransforming(option, ctx))
864
+ return true;
892
865
  }
893
- case "custom": {
894
- return false;
866
+ return false;
867
+ }
868
+ if (def.type === "tuple") {
869
+ for (const item of def.items) {
870
+ if (isTransforming(item, ctx))
871
+ return true;
895
872
  }
896
- case "transform": {
873
+ if (def.rest && isTransforming(def.rest, ctx))
897
874
  return true;
898
- }
899
- case "pipe": {
900
- return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
901
- }
902
- case "success": {
903
- return false;
904
- }
905
- case "catch": {
906
- return false;
907
- }
908
- case "function": {
909
- return false;
910
- }
911
- default:
912
- def;
875
+ return false;
913
876
  }
914
- throw new Error(`Unknown schema type: ${def.type}`);
877
+ return false;
915
878
  }
package/v4/core/util.cjs CHANGED
@@ -22,6 +22,7 @@ exports.getElementAtPath = getElementAtPath;
22
22
  exports.promiseAllObject = promiseAllObject;
23
23
  exports.randomString = randomString;
24
24
  exports.esc = esc;
25
+ exports.slugify = slugify;
25
26
  exports.isObject = isObject;
26
27
  exports.isPlainObject = isPlainObject;
27
28
  exports.shallowClone = shallowClone;
@@ -190,6 +191,14 @@ function randomString(length = 10) {
190
191
  function esc(str) {
191
192
  return JSON.stringify(str);
192
193
  }
194
+ function slugify(input) {
195
+ return input
196
+ .toLowerCase()
197
+ .trim()
198
+ .replace(/[^\w\s-]/g, "")
199
+ .replace(/[\s_-]+/g, "-")
200
+ .replace(/^-+|-+$/g, "");
201
+ }
193
202
  exports.captureStackTrace = ("captureStackTrace" in Error ? Error.captureStackTrace : (..._args) => { });
194
203
  function isObject(data) {
195
204
  return typeof data === "object" && data !== null && !Array.isArray(data);
@@ -215,6 +224,8 @@ function isPlainObject(o) {
215
224
  const ctor = o.constructor;
216
225
  if (ctor === undefined)
217
226
  return true;
227
+ if (typeof ctor !== "function")
228
+ return true;
218
229
  // modified prototype
219
230
  const prot = ctor.prototype;
220
231
  if (isObject(prot) === false)
@@ -133,6 +133,7 @@ export declare function promiseAllObject<T extends object>(promisesObj: T): Prom
133
133
  }>;
134
134
  export declare function randomString(length?: number): string;
135
135
  export declare function esc(str: string): string;
136
+ export declare function slugify(input: string): string;
136
137
  export declare const captureStackTrace: (targetObject: object, constructorOpt?: Function) => void;
137
138
  export declare function isObject(data: any): data is Record<PropertyKey, unknown>;
138
139
  export declare const allowsEval: {
package/v4/core/util.d.ts CHANGED
@@ -133,6 +133,7 @@ export declare function promiseAllObject<T extends object>(promisesObj: T): Prom
133
133
  }>;
134
134
  export declare function randomString(length?: number): string;
135
135
  export declare function esc(str: string): string;
136
+ export declare function slugify(input: string): string;
136
137
  export declare const captureStackTrace: (targetObject: object, constructorOpt?: Function) => void;
137
138
  export declare function isObject(data: any): data is Record<PropertyKey, unknown>;
138
139
  export declare const allowsEval: {
package/v4/core/util.js CHANGED
@@ -135,6 +135,14 @@ export function randomString(length = 10) {
135
135
  export function esc(str) {
136
136
  return JSON.stringify(str);
137
137
  }
138
+ export function slugify(input) {
139
+ return input
140
+ .toLowerCase()
141
+ .trim()
142
+ .replace(/[^\w\s-]/g, "")
143
+ .replace(/[\s_-]+/g, "-")
144
+ .replace(/^-+|-+$/g, "");
145
+ }
138
146
  export const captureStackTrace = ("captureStackTrace" in Error ? Error.captureStackTrace : (..._args) => { });
139
147
  export function isObject(data) {
140
148
  return typeof data === "object" && data !== null && !Array.isArray(data);
@@ -160,6 +168,8 @@ export function isPlainObject(o) {
160
168
  const ctor = o.constructor;
161
169
  if (ctor === undefined)
162
170
  return true;
171
+ if (typeof ctor !== "function")
172
+ return true;
163
173
  // modified prototype
164
174
  const prot = ctor.prototype;
165
175
  if (isObject(prot) === false)
@@ -4,5 +4,5 @@ exports.version = void 0;
4
4
  exports.version = {
5
5
  major: 4,
6
6
  minor: 1,
7
- patch: 12,
7
+ patch: 13,
8
8
  };
@@ -1,5 +1,5 @@
1
1
  export const version = {
2
2
  major: 4,
3
3
  minor: 1,
4
- patch: 12,
4
+ patch: 13,
5
5
  };
package/v4/locales/en.cjs CHANGED
@@ -78,6 +78,7 @@ const error = () => {
78
78
  duration: "ISO duration",
79
79
  ipv4: "IPv4 address",
80
80
  ipv6: "IPv6 address",
81
+ mac: "MAC address",
81
82
  cidrv4: "IPv4 range",
82
83
  cidrv6: "IPv6 range",
83
84
  base64: "base64-encoded string",
package/v4/locales/en.js CHANGED
@@ -50,6 +50,7 @@ const error = () => {
50
50
  duration: "ISO duration",
51
51
  ipv4: "IPv4 address",
52
52
  ipv6: "IPv6 address",
53
+ mac: "MAC address",
53
54
  cidrv4: "IPv4 range",
54
55
  cidrv6: "IPv6 range",
55
56
  base64: "base64-encoded string",