@es-joy/jsoe 0.26.1 → 0.27.0

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 (124) hide show
  1. package/.gitignore +10 -0
  2. package/.npmignore +9 -0
  3. package/CHANGES.md +43 -0
  4. package/README.md +3 -3
  5. package/badges/coverage-badge.svg +1 -1
  6. package/dist/formats/schema.d.ts.map +1 -1
  7. package/dist/fundamentalTypes/arrayType.d.ts.map +1 -1
  8. package/dist/fundamentalTypes/stringType.d.ts.map +1 -1
  9. package/dist/index.js +2 -2
  10. package/dist/index.js.map +1 -1
  11. package/dist/typeChoices.d.ts.map +1 -1
  12. package/dist/types.d.ts.map +1 -1
  13. package/eslint.config.js +36 -27
  14. package/package.json +12 -11
  15. package/pnpm-workspace.yaml +3 -2
  16. package/src/formats/schema.js +35 -6
  17. package/src/fundamentalTypes/arrayType.js +30 -6
  18. package/src/fundamentalTypes/stringType.js +5 -0
  19. package/src/typeChoices.js +3 -1
  20. package/src/types.js +7 -0
  21. package/vendor/typeson-registry/dist/index.js +83 -10
  22. package/vendor/zod/classic/checks.d.ts +1 -1
  23. package/vendor/zod/classic/checks.js +1 -1
  24. package/vendor/zod/classic/external.d.ts +1 -1
  25. package/vendor/zod/classic/external.js +1 -1
  26. package/vendor/zod/classic/from-json-schema.js +245 -24
  27. package/vendor/zod/classic/schemas.d.ts +17 -1
  28. package/vendor/zod/classic/schemas.js +77 -29
  29. package/vendor/zod/core/api.d.ts +6 -4
  30. package/vendor/zod/core/api.js +17 -2
  31. package/vendor/zod/core/checks.d.ts +1 -1
  32. package/vendor/zod/core/checks.js +0 -96
  33. package/vendor/zod/core/compile.d.ts +18 -4
  34. package/vendor/zod/core/compile.js +146 -67
  35. package/vendor/zod/core/doc.js +7 -2
  36. package/vendor/zod/core/errors.js +2 -5
  37. package/vendor/zod/core/json-schema-generator.js +2 -2
  38. package/vendor/zod/core/json-schema-processors.d.ts +17 -0
  39. package/vendor/zod/core/json-schema-processors.js +195 -64
  40. package/vendor/zod/core/memoizer.js +72 -23
  41. package/vendor/zod/core/parse.js +37 -16
  42. package/vendor/zod/core/regexes.d.ts +2 -0
  43. package/vendor/zod/core/regexes.js +8 -4
  44. package/vendor/zod/core/schemas.d.ts +42 -7
  45. package/vendor/zod/core/schemas.js +301 -94
  46. package/vendor/zod/core/to-json-schema.d.ts +7 -3
  47. package/vendor/zod/core/to-json-schema.js +8 -7
  48. package/vendor/zod/core/util.d.ts +25 -4
  49. package/vendor/zod/core/util.js +160 -143
  50. package/vendor/zod/core/versions.d.ts +1 -1
  51. package/vendor/zod/core/versions.js +2 -2
  52. package/vendor/zod/core/visit.js +12 -0
  53. package/vendor/zod/locales/ar.js +1 -0
  54. package/vendor/zod/locales/az.js +1 -0
  55. package/vendor/zod/locales/be.js +1 -0
  56. package/vendor/zod/locales/bg.js +1 -0
  57. package/vendor/zod/locales/bn.js +1 -0
  58. package/vendor/zod/locales/ca.js +1 -0
  59. package/vendor/zod/locales/ckb.js +1 -0
  60. package/vendor/zod/locales/cs.js +1 -0
  61. package/vendor/zod/locales/da.js +1 -0
  62. package/vendor/zod/locales/de.js +1 -0
  63. package/vendor/zod/locales/el.js +1 -0
  64. package/vendor/zod/locales/en.js +1 -0
  65. package/vendor/zod/locales/eo.js +1 -0
  66. package/vendor/zod/locales/es.js +1 -0
  67. package/vendor/zod/locales/fa.js +1 -0
  68. package/vendor/zod/locales/fi.js +1 -0
  69. package/vendor/zod/locales/fr-CA.js +1 -0
  70. package/vendor/zod/locales/fr.js +1 -0
  71. package/vendor/zod/locales/gu.js +1 -0
  72. package/vendor/zod/locales/he.js +1 -0
  73. package/vendor/zod/locales/hi.js +1 -0
  74. package/vendor/zod/locales/hr.js +1 -0
  75. package/vendor/zod/locales/hu.js +1 -0
  76. package/vendor/zod/locales/hy.js +1 -0
  77. package/vendor/zod/locales/id.js +1 -0
  78. package/vendor/zod/locales/index.d.ts +1 -0
  79. package/vendor/zod/locales/index.js +1 -0
  80. package/vendor/zod/locales/is.js +1 -0
  81. package/vendor/zod/locales/it.js +1 -0
  82. package/vendor/zod/locales/ja.js +1 -0
  83. package/vendor/zod/locales/ka.js +1 -0
  84. package/vendor/zod/locales/km.js +1 -0
  85. package/vendor/zod/locales/kn.js +1 -0
  86. package/vendor/zod/locales/ko.js +1 -0
  87. package/vendor/zod/locales/lt.js +1 -0
  88. package/vendor/zod/locales/mk.js +1 -0
  89. package/vendor/zod/locales/ms.js +1 -0
  90. package/vendor/zod/locales/ne.js +1 -0
  91. package/vendor/zod/locales/nl.js +1 -0
  92. package/vendor/zod/locales/nn.js +1 -0
  93. package/vendor/zod/locales/no.js +1 -0
  94. package/vendor/zod/locales/ota.js +1 -0
  95. package/vendor/zod/locales/pl.js +1 -0
  96. package/vendor/zod/locales/ps.js +1 -0
  97. package/vendor/zod/locales/pt-BR.js +1 -0
  98. package/vendor/zod/locales/pt.js +1 -0
  99. package/vendor/zod/locales/ro.js +1 -0
  100. package/vendor/zod/locales/ru.js +1 -0
  101. package/vendor/zod/locales/sk.js +1 -0
  102. package/vendor/zod/locales/sl.js +1 -0
  103. package/vendor/zod/locales/sv.js +1 -0
  104. package/vendor/zod/locales/ta.js +1 -0
  105. package/vendor/zod/locales/tg.d.ts +4 -0
  106. package/vendor/zod/locales/tg.js +116 -0
  107. package/vendor/zod/locales/th.js +1 -0
  108. package/vendor/zod/locales/tk.js +1 -0
  109. package/vendor/zod/locales/tr.js +1 -0
  110. package/vendor/zod/locales/uk.js +1 -0
  111. package/vendor/zod/locales/ur.js +1 -0
  112. package/vendor/zod/locales/uz.js +1 -0
  113. package/vendor/zod/locales/vi.js +1 -0
  114. package/vendor/zod/locales/yo.js +1 -0
  115. package/vendor/zod/locales/zh-CN.js +1 -0
  116. package/vendor/zod/locales/zh-TW.js +1 -0
  117. package/vendor/zod/mini/checks.d.ts +1 -1
  118. package/vendor/zod/mini/checks.js +1 -1
  119. package/vendor/zod/mini/external.d.ts +1 -1
  120. package/vendor/zod/mini/external.js +1 -1
  121. package/vendor/zod/mini/schemas.d.ts +8 -0
  122. package/vendor/zod/mini/schemas.js +19 -2
  123. package/vendor/zodexy/dist/esm/index.js +34 -1
  124. package/vendor/zodexy/dist/schema.zodexy.json +18 -0
@@ -1,7 +1,7 @@
1
1
  import * as checks from "./checks.js";
2
2
  import * as core from "./core.js";
3
3
  import { Doc } from "./doc.js";
4
- import { parse, parseAsync, safeParse, safeParseAsync } from "./parse.js";
4
+ import { parse, parseAsync } from "./parse.js";
5
5
  import * as regexes from "./regexes.js";
6
6
  import * as util from "./util.js";
7
7
  import { version } from "./versions.js";
@@ -128,16 +128,24 @@ export const $ZodType = /*@__PURE__*/ core.$constructor("$ZodType", (inst, def)
128
128
  },
129
129
  });
130
130
  /** The Standard Schema surface for `inst`. Shared so wrappers can extend it without forcing it. */
131
- const toStandardResult = (r) => r.success ? { value: r.data } : { issues: r.error?.issues };
131
+ // a Standard Schema result only reports issues, so a failure finalizes them straight off the raw payload: no ZodError, and no lazy result to read through
132
+ const toStandardResult = (r, ctx) => r.issues.length ? { issues: r.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())) } : { value: r.value };
133
+ async function validateAsync(inst, value) {
134
+ const ctx = { async: true };
135
+ return toStandardResult((await inst._zod.run({ value, issues: [] }, ctx)), ctx);
136
+ }
132
137
  export function standardProps(inst) {
133
138
  return {
134
139
  validate: (value) => {
140
+ const ctx = { async: false };
135
141
  try {
136
- return toStandardResult(safeParse(inst, value));
137
- }
138
- catch (_) {
139
- return safeParseAsync(inst, value).then(toStandardResult);
142
+ const r = inst._zod.run({ value, issues: [] }, ctx);
143
+ if (!(r instanceof Promise))
144
+ return toStandardResult(r, ctx);
140
145
  }
146
+ catch (_) { }
147
+ // async function so a synchronously throwing check rejects instead of escaping validate
148
+ return validateAsync(inst, value);
141
149
  },
142
150
  vendor: "zod",
143
151
  version: 1,
@@ -146,7 +154,8 @@ export function standardProps(inst) {
146
154
  export { clone } from "./util.js";
147
155
  export const $ZodString = /*@__PURE__*/ core.$constructor("$ZodString", (inst, def) => {
148
156
  $ZodType.init(inst, def);
149
- inst._zod.pattern = [...(inst?._zod.bag?.patterns ?? [])].pop() ?? regexes.string(inst._zod.bag);
157
+ // a format's own pattern, else unbounded; a template literal derives the check-aware form itself
158
+ inst._zod.pattern = def.pattern ?? regexes.anyString;
150
159
  inst._zod.parse = (payload, _) => {
151
160
  if (def.coerce)
152
161
  try {
@@ -332,13 +341,6 @@ export const $ZodKSUID = /*@__PURE__*/ core.$constructor("$ZodKSUID", (inst, def
332
341
  export const $ZodISODateTime = /*@__PURE__*/ core.$constructor("$ZodISODateTime", (inst, def) => {
333
342
  def.pattern ?? (def.pattern = regexes.datetime(def));
334
343
  $ZodStringFormat.init(inst, def);
335
- // these two drop the offset or seconds `date-time` requires — on the bag not the def, since `z.string().check(...)` lands the format on a different schema
336
- if (def.local || def.precision === -1) {
337
- inst._zod.bag.laxFormat = true;
338
- inst._zod.onattach.push((s) => {
339
- s._zod.bag.laxFormat = true;
340
- });
341
- }
342
344
  });
343
345
  export const $ZodISODate = /*@__PURE__*/ core.$constructor("$ZodISODate", (inst, def) => {
344
346
  def.pattern ?? (def.pattern = regexes.date);
@@ -355,7 +357,6 @@ export const $ZodISODuration = /*@__PURE__*/ core.$constructor("$ZodISODuration"
355
357
  export const $ZodIPv4 = /*@__PURE__*/ core.$constructor("$ZodIPv4", (inst, def) => {
356
358
  def.pattern ?? (def.pattern = regexes.ipv4);
357
359
  $ZodStringFormat.init(inst, def);
358
- inst._zod.bag.format = `ipv4`;
359
360
  });
360
361
  /** An IPv6 address is written with hex digits, colons and dots, and nothing else. The guard is what makes the check below an IPv6 check: `new URL("http://[...]")` parses an authority, not an address, so `@` and `\` re-delimit it and `"::@1\\"` validates against the host `0.0.0.1`. The URL parser also deletes ASCII tab, LF and CR rather than failing, which is how `"::1\n"` validated as `::1`. */
361
362
  const ipv6Alphabet = /^[0-9a-fA-F:.]+$/;
@@ -374,7 +375,6 @@ export function isValidIPv6(value) {
374
375
  export const $ZodIPv6 = /*@__PURE__*/ core.$constructor("$ZodIPv6", (inst, def) => {
375
376
  def.pattern ?? (def.pattern = regexes.ipv6);
376
377
  $ZodStringFormat.init(inst, def);
377
- inst._zod.bag.format = `ipv6`;
378
378
  inst._zod.check = (payload) => {
379
379
  if (!isValidIPv6(payload.value)) {
380
380
  payload.issues.push({
@@ -390,7 +390,6 @@ export const $ZodIPv6 = /*@__PURE__*/ core.$constructor("$ZodIPv6", (inst, def)
390
390
  export const $ZodMAC = /*@__PURE__*/ core.$constructor("$ZodMAC", (inst, def) => {
391
391
  def.pattern ?? (def.pattern = regexes.mac(def.delimiter));
392
392
  $ZodStringFormat.init(inst, def);
393
- inst._zod.bag.format = `mac`;
394
393
  });
395
394
  export const $ZodCIDRv4 = /*@__PURE__*/ core.$constructor("$ZodCIDRv4", (inst, def) => {
396
395
  def.pattern ?? (def.pattern = regexes.cidrv4);
@@ -443,10 +442,11 @@ export function isValidBase64(data) {
443
442
  return false;
444
443
  }
445
444
  }
445
+ // lax on purpose: the quantified regexes.base64 overflows the regex stack on multi-MB input and its leading ^$| alternation leaks through template-literal composition; isValidBase64 enforces length and padding
446
+ export const base64Charset = /^[0-9a-zA-Z+/]*={0,2}$/;
446
447
  export const $ZodBase64 = /*@__PURE__*/ core.$constructor("$ZodBase64", (inst, def) => {
447
- def.pattern ?? (def.pattern = regexes.base64);
448
+ def.pattern ?? (def.pattern = base64Charset);
448
449
  $ZodStringFormat.init(inst, def);
449
- inst._zod.bag.contentEncoding = "base64";
450
450
  inst._zod.check = (payload) => {
451
451
  if (isValidBase64(payload.value))
452
452
  return;
@@ -459,18 +459,19 @@ export const $ZodBase64 = /*@__PURE__*/ core.$constructor("$ZodBase64", (inst, d
459
459
  });
460
460
  };
461
461
  });
462
- ////////////////////////////// ZodBase64 //////////////////////////////
462
+ ////////////////////////////// ZodBase64URL //////////////////////////////
463
+ // lax on purpose: the quantified regexes.base64url overflows the regex stack on multi-MB input; isValidBase64 enforces length on the padded string
464
+ export const base64urlCharset = /^[A-Za-z0-9_-]*$/;
463
465
  export function isValidBase64URL(data) {
464
- if (!regexes.base64url.test(data))
466
+ if (!base64urlCharset.test(data))
465
467
  return false;
466
468
  const base64 = data.replace(/[-_]/g, (c) => (c === "-" ? "+" : "/"));
467
469
  const padded = base64.padEnd(Math.ceil(base64.length / 4) * 4, "=");
468
470
  return isValidBase64(padded);
469
471
  }
470
472
  export const $ZodBase64URL = /*@__PURE__*/ core.$constructor("$ZodBase64URL", (inst, def) => {
471
- def.pattern ?? (def.pattern = regexes.base64url);
473
+ def.pattern ?? (def.pattern = base64urlCharset);
472
474
  $ZodStringFormat.init(inst, def);
473
- inst._zod.bag.contentEncoding = "base64url";
474
475
  inst._zod.check = (payload) => {
475
476
  if (isValidBase64URL(payload.value))
476
477
  return;
@@ -495,7 +496,7 @@ function isLuhnAlgo(digits) {
495
496
  let bit = 1;
496
497
  let sum = 0;
497
498
  while (length) {
498
- const value = +digits[--length];
499
+ const value = digits.charCodeAt(--length) - 48;
499
500
  bit ^= 1;
500
501
  sum += bit ? [0, 2, 4, 6, 8, 1, 3, 5, 7, 9][value] : value;
501
502
  }
@@ -522,6 +523,42 @@ export const $ZodCreditCard = /*@__PURE__*/ core.$constructor("$ZodCreditCard",
522
523
  });
523
524
  };
524
525
  });
526
+ ////////////////////////////// ZodIBAN //////////////////////////////
527
+ // iso 7064 mod 97-10 checksum without BigInt
528
+ function isIso7064Mod97(iban) {
529
+ let remainder = 0;
530
+ const len = iban.length;
531
+ for (let i = 4; i < len; i++) {
532
+ const code = iban.charCodeAt(i);
533
+ remainder = (code >= 65 ? remainder * 100 + (code - 55) : remainder * 10 + (code - 48)) % 97;
534
+ }
535
+ for (let i = 0; i < 4; i++) {
536
+ const code = iban.charCodeAt(i);
537
+ remainder = (code >= 65 ? remainder * 100 + (code - 55) : remainder * 10 + (code - 48)) % 97;
538
+ }
539
+ return remainder === 1;
540
+ }
541
+ export function isValidIBAN(input) {
542
+ if (!regexes.iban.test(input))
543
+ return false;
544
+ return isIso7064Mod97(input);
545
+ }
546
+ export const $ZodIBAN = /*@__PURE__*/ core.$constructor("$ZodIBAN", (inst, def) => {
547
+ // shape only — checksum is not expressible as a pattern
548
+ def.pattern ?? (def.pattern = regexes.iban);
549
+ $ZodStringFormat.init(inst, def);
550
+ inst._zod.check = (payload) => {
551
+ if (isValidIBAN(payload.value))
552
+ return;
553
+ payload.issues.push({
554
+ code: "invalid_format",
555
+ format: "iban",
556
+ input: payload.value,
557
+ inst,
558
+ continue: !def.abort,
559
+ });
560
+ };
561
+ });
525
562
  ////////////////////////////// ZodJWT //////////////////////////////
526
563
  export function isValidJWT(token, algorithm = null) {
527
564
  try {
@@ -575,7 +612,7 @@ export const $ZodCustomStringFormat = /*@__PURE__*/ core.$constructor("$ZodCusto
575
612
  });
576
613
  export const $ZodNumber = /*@__PURE__*/ core.$constructor("$ZodNumber", (inst, def) => {
577
614
  $ZodType.init(inst, def);
578
- inst._zod.pattern = inst._zod.bag.pattern ?? regexes.number;
615
+ inst._zod.pattern = regexes.number;
579
616
  inst._zod.parse = (payload, _ctx) => {
580
617
  if (def.coerce)
581
618
  try {
@@ -783,6 +820,7 @@ export const $ZodArray = /*@__PURE__*/ core.$constructor("$ZodArray", (inst, def
783
820
  }
784
821
  payload.value = memo ? memo.alloc(inst, payload, Array(input.length), ctx) : Array(input.length);
785
822
  const proms = [];
823
+ const abortEarly = ctx?.abortEarly;
786
824
  for (let i = 0; i < input.length; i++) {
787
825
  const item = input[i];
788
826
  const result = def.element._zod.run({
@@ -794,6 +832,9 @@ export const $ZodArray = /*@__PURE__*/ core.$constructor("$ZodArray", (inst, def
794
832
  }
795
833
  else {
796
834
  handleArrayResult(result, payload, i);
835
+ // the element's payload is authoritative here, since handleArrayResult forwards every issue; an object's is not, because it drops a failed absent optional
836
+ if (abortEarly && result.issues.length !== 0 && util.aborted(result))
837
+ break;
797
838
  }
798
839
  }
799
840
  if (proms.length) {
@@ -860,14 +901,21 @@ function normalizeDef(def) {
860
901
  optionalKeys: new Set(okeys),
861
902
  };
862
903
  }
863
- function handleCatchall(proms, input, payload, ctx, def, inst) {
904
+ function handleCatchall(proms, input, payload, ctx, def, inst, abortEarly) {
864
905
  const unrecognized = [];
865
906
  const keySet = def.keySet;
866
907
  const _catchall = def.catchall._zod;
867
908
  const t = _catchall.def.type;
868
909
  const optin = _catchall.optin;
869
910
  const optout = _catchall.optout;
911
+ // starts at 0, not the current length: the shape phase already ran and may have aborted
912
+ let seen = 0;
870
913
  for (const key in input) {
914
+ if (abortEarly && payload.issues.length !== seen) {
915
+ if (util.aborted(payload, seen))
916
+ break;
917
+ seen = payload.issues.length;
918
+ }
871
919
  // Must precede the __proto__ branch: a declared key is not unrecognized, even though the shape loop deliberately strips __proto__ from the parsed output.
872
920
  if (keySet.has(key))
873
921
  continue;
@@ -905,26 +953,22 @@ function handleCatchall(proms, input, payload, ctx, def, inst) {
905
953
  return payload;
906
954
  });
907
955
  }
908
- // Whichever object a def's `shape` currently answers from: the one the caller passed until the first read, the frozen copy after it. Keyed by def, so a def rebuilt by a builder is simply absent rather than inheriting the source's. Read its keys with `Object.keys`, which does not invoke them — that is what lets a discriminated union check its discriminator without resolving an option whose getters reference the union being constructed.
909
- const propShapes = new WeakMap();
910
956
  export const $ZodObject = /*@__PURE__*/ core.$constructor("$ZodObject", (inst, def) => {
911
957
  // requires cast because technically $ZodObject doesn't extend
912
958
  $ZodType.init(inst, def);
913
- // const sh = def.shape;
914
959
  const desc = Object.getOwnPropertyDescriptor(def, "shape");
915
- if (!desc?.get) {
916
- const sh = def.shape;
917
- propShapes.set(def, sh);
918
- Object.defineProperty(def, "shape", {
919
- get: () => {
920
- const newSh = { ...sh };
921
- Object.defineProperty(def, "shape", {
922
- value: newSh,
923
- });
924
- propShapes.set(def, newSh);
925
- return newSh;
926
- },
927
- });
960
+ // a cloned def carries its source's accessor, which knows the shape it answers from; adopting that keeps the clone's keys readable without running it
961
+ const sh = desc?.get ? desc.get.raw : (def.shape ?? {});
962
+ if (sh) {
963
+ // Freezes the shape on first read, so its getters resolve once and every later read sees the same schemas.
964
+ const get = () => {
965
+ const newSh = { ...sh };
966
+ Object.defineProperty(def, "shape", { value: newSh });
967
+ get.raw = newSh;
968
+ return newSh;
969
+ };
970
+ get.raw = sh;
971
+ Object.defineProperty(def, "shape", { get });
928
972
  }
929
973
  const _normalized = util.cached(() => normalizeDef(def));
930
974
  util.defineLazyInternal(inst, "propValues", (zod) => {
@@ -965,7 +1009,14 @@ export const $ZodObject = /*@__PURE__*/ core.$constructor("$ZodObject", (inst, d
965
1009
  payload.value = memo ? memo.alloc(inst, payload, {}, ctx) : {};
966
1010
  const proms = [];
967
1011
  const shape = value.shape;
1012
+ const abortEarly = ctx?.abortEarly;
1013
+ let seen = payload.issues.length;
968
1014
  for (const key of value.allKeys) {
1015
+ if (abortEarly && payload.issues.length !== seen) {
1016
+ if (util.aborted(payload, seen))
1017
+ break;
1018
+ seen = payload.issues.length;
1019
+ }
969
1020
  if (key === "__proto__")
970
1021
  continue;
971
1022
  const el = shape[key];
@@ -982,7 +1033,7 @@ export const $ZodObject = /*@__PURE__*/ core.$constructor("$ZodObject", (inst, d
982
1033
  if (!catchall) {
983
1034
  return proms.length ? Promise.all(proms).then(() => payload) : payload;
984
1035
  }
985
- return handleCatchall(proms, input, payload, ctx, _normalized.value, inst);
1036
+ return handleCatchall(proms, input, payload, ctx, _normalized.value, inst, abortEarly === true);
986
1037
  };
987
1038
  });
988
1039
  export const $ZodObjectJIT = /*@__PURE__*/ core.$constructor("$ZodObjectJIT", (inst, def) => {
@@ -997,12 +1048,18 @@ export const $ZodObjectJIT = /*@__PURE__*/ core.$constructor("$ZodObjectJIT", (i
997
1048
  // a symbol has no source literal, so it is read as `syms[i]` off the closed-over scope
998
1049
  const doc = new Doc(["payload", "ctx"], { shape, inst, memo, syms });
999
1050
  const parseStr = (k) => `shape[${k}]._zod.run({ value: input[${k}], issues: [] }, ctx)`;
1000
- // Prefixes in place, like util.prefixIssues does for every interpreted path.
1051
+ // prefixes in place, like util.prefixIssues. newResult must land before the early return: a catchall runs after this and would otherwise write onto the caller's input
1001
1052
  const prefixStr = (id, k) => `
1053
+ let ${id}_ab = false;
1002
1054
  for (let i = 0; i < ${id}.issues.length; i++) {
1003
1055
  const iss = ${id}.issues[i];
1004
1056
  iss.path = iss.path ? [${k}, ...iss.path] : [${k}];
1005
1057
  payload.issues.push(iss);
1058
+ if (iss.continue !== true) ${id}_ab = true;
1059
+ }
1060
+ if (${id}_ab && ctx && ctx.abortEarly) {
1061
+ payload.value = newResult;
1062
+ return payload;
1006
1063
  }`;
1007
1064
  doc.write(`const input = payload.value;`);
1008
1065
  const ids = Object.create(null);
@@ -1051,6 +1108,10 @@ export const $ZodObjectJIT = /*@__PURE__*/ core.$constructor("$ZodObjectJIT", (i
1051
1108
  input: undefined,
1052
1109
  path: [${k}]
1053
1110
  });
1111
+ if (ctx && ctx.abortEarly) {
1112
+ payload.value = newResult;
1113
+ return payload;
1114
+ }
1054
1115
  }
1055
1116
 
1056
1117
  if (${id}_present) {
@@ -1106,7 +1167,7 @@ export const $ZodObjectJIT = /*@__PURE__*/ core.$constructor("$ZodObjectJIT", (i
1106
1167
  payload = fastpass(payload, ctx);
1107
1168
  if (!catchall)
1108
1169
  return payload;
1109
- return handleCatchall([], input, payload, ctx, value, inst);
1170
+ return handleCatchall([], input, payload, ctx, value, inst, ctx?.abortEarly === true);
1110
1171
  }
1111
1172
  return superParse(payload, ctx);
1112
1173
  };
@@ -1243,22 +1304,38 @@ export const $ZodXor = /*@__PURE__*/ core.$constructor("$ZodXor", (inst, def) =>
1243
1304
  });
1244
1305
  };
1245
1306
  });
1246
- /** Returns the option of `union` whose discriminator claims `value`. */
1307
+ /** Returns the option whose discriminator claims `value`, or throws if ambiguous. */
1247
1308
  export function getDiscriminatedOption(union, value) {
1248
1309
  const internals = union._zod;
1249
1310
  let map = internals.bag.optionsMap;
1250
1311
  if (!map) {
1251
- map = new Map();
1252
- const { options, discriminator } = internals.def;
1253
- for (const option of options) {
1254
- // First declaration wins, matching the order the parse path resolves a duplicate in.
1255
- for (const v of option._zod.propValues?.[discriminator] ?? [])
1256
- if (!map.has(v))
1257
- map.set(v, option);
1258
- }
1312
+ map = discriminatorMap(internals.def);
1259
1313
  internals.bag.optionsMap = map;
1260
1314
  }
1261
- return map.get(value);
1315
+ const option = map.get(value);
1316
+ if (option === null)
1317
+ throw new Error(`Ambiguous discriminator value "${String(value)}"`);
1318
+ return option;
1319
+ }
1320
+ function discriminatorMap(def) {
1321
+ const map = new Map();
1322
+ for (const option of def.options) {
1323
+ const values = option._zod.propValues?.[def.discriminator];
1324
+ if (!values || values.size === 0)
1325
+ throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(option)}"`);
1326
+ for (const value of values) {
1327
+ if (map.has(value)) {
1328
+ if (value !== undefined)
1329
+ throw new Error(`Duplicate discriminator value "${String(value)}"`);
1330
+ // keep the collision marked so a later member cannot reclaim it
1331
+ map.set(value, null);
1332
+ }
1333
+ else {
1334
+ map.set(value, option);
1335
+ }
1336
+ }
1337
+ }
1338
+ return map;
1262
1339
  }
1263
1340
  export const $ZodDiscriminatedUnion =
1264
1341
  /*@__PURE__*/
@@ -1268,10 +1345,13 @@ core.$constructor("$ZodDiscriminatedUnion", (inst, def) => {
1268
1345
  const _super = inst._zod.parse;
1269
1346
  util.defineLazyInternal(inst, "propValues", (zod) => {
1270
1347
  const propValues = {};
1348
+ let undefinedCount = 0;
1271
1349
  for (const option of zod.def.options) {
1272
1350
  const pv = option._zod.propValues;
1273
1351
  if (!pv || Object.keys(pv).length === 0)
1274
1352
  throw new Error(`Invalid discriminated union option at index "${zod.def.options.indexOf(option)}"`);
1353
+ if (pv[zod.def.discriminator]?.has(undefined))
1354
+ undefinedCount++;
1275
1355
  for (const [k, v] of Object.entries(pv)) {
1276
1356
  if (!Object.prototype.hasOwnProperty.call(propValues, k)) {
1277
1357
  util.assignProp(propValues, k, new Set());
@@ -1281,31 +1361,18 @@ core.$constructor("$ZodDiscriminatedUnion", (inst, def) => {
1281
1361
  }
1282
1362
  }
1283
1363
  }
1364
+ if (!zod.def.unionFallback && undefinedCount > 1)
1365
+ propValues[zod.def.discriminator]?.delete(undefined);
1284
1366
  return propValues;
1285
1367
  });
1286
- // Checked now rather than in the lookup map below, so an option that lacks the discriminator fails at the `discriminatedUnion` call instead of on the first object parsed. Options whose shape cannot be enumerated without resolving it — pipes, lazies, and objects rebuilt by a builder such as `.extend()` — are left to the map.
1368
+ // Checked now rather than in the lookup map below, so an option that lacks the discriminator fails at the `discriminatedUnion` call instead of on the first object parsed. Options whose shape cannot be enumerated without resolving it — pipes and lazies — are left to the map.
1287
1369
  def.options.forEach((option, i) => {
1288
- const propShape = propShapes.get(option._zod.def);
1370
+ const propShape = util.rawShape(option._zod.def);
1289
1371
  if (propShape && !Object.prototype.hasOwnProperty.call(propShape, def.discriminator)) {
1290
1372
  throw new Error(`Invalid discriminated union option at index "${i}"`);
1291
1373
  }
1292
1374
  });
1293
- const disc = util.cached(() => {
1294
- const opts = def.options;
1295
- const map = new Map();
1296
- for (const o of opts) {
1297
- const values = o._zod.propValues?.[def.discriminator];
1298
- if (!values || values.size === 0)
1299
- throw new Error(`Invalid discriminated union option at index "${def.options.indexOf(o)}"`);
1300
- for (const v of values) {
1301
- if (map.has(v)) {
1302
- throw new Error(`Duplicate discriminator value "${String(v)}"`);
1303
- }
1304
- map.set(v, o);
1305
- }
1306
- }
1307
- return map;
1308
- });
1375
+ const disc = util.cached(() => discriminatorMap(def));
1309
1376
  inst._zod.parse = (payload, ctx) => {
1310
1377
  const input = payload.value;
1311
1378
  if (!util.isObject(input)) {
@@ -1317,8 +1384,10 @@ core.$constructor("$ZodDiscriminatedUnion", (inst, def) => {
1317
1384
  });
1318
1385
  return payload;
1319
1386
  }
1320
- const opt = disc.value.get(input?.[def.discriminator]);
1321
- if (opt) {
1387
+ const value = input?.[def.discriminator];
1388
+ const opt = disc.value.get(value);
1389
+ // forward metadata cannot choose an encoder for an absent tag
1390
+ if (opt && (value !== undefined || ctx.direction !== "backward")) {
1322
1391
  return opt._zod.run(payload, ctx);
1323
1392
  }
1324
1393
  // Fall back to union matching when the fast discriminator path fails:
@@ -1333,7 +1402,7 @@ core.$constructor("$ZodDiscriminatedUnion", (inst, def) => {
1333
1402
  errors: [],
1334
1403
  note: "No matching discriminator",
1335
1404
  discriminator: def.discriminator,
1336
- options: Array.from(disc.value.keys()),
1405
+ options: Array.from(disc.value.keys()).filter((value) => disc.value.get(value) !== null),
1337
1406
  input,
1338
1407
  path: [def.discriminator],
1339
1408
  inst,
@@ -1506,6 +1575,9 @@ export const $ZodTuple = /*@__PURE__*/ core.$constructor("$ZodTuple", (inst, def
1506
1575
  }
1507
1576
  // Run every item in parallel, collecting results into an indexed array. The post-processing in `handleTupleResults` walks them in order so it can decide whether an absent optional-output error can truncate the tail or must be reported to preserve required output.
1508
1577
  const itemResults = new Array(items.length);
1578
+ // only tracked when there is a rest loop to skip
1579
+ const abortEarly = def.rest ? ctx?.abortEarly : undefined;
1580
+ let itemAborted = false;
1509
1581
  for (let i = 0; i < items.length; i++) {
1510
1582
  const r = items[i]._zod.run({ value: input[i], issues: [] }, ctx);
1511
1583
  if (r instanceof Promise) {
@@ -1515,12 +1587,21 @@ export const $ZodTuple = /*@__PURE__*/ core.$constructor("$ZodTuple", (inst, def
1515
1587
  }
1516
1588
  else {
1517
1589
  itemResults[i] = r;
1590
+ if (abortEarly && !itemAborted && r.issues.length)
1591
+ itemAborted = util.aborted(r);
1518
1592
  }
1519
1593
  }
1520
- if (def.rest) {
1594
+ // sound because rest is non-empty exactly when every fixed index is present, the one case handleTupleResults cannot discard an item's issues
1595
+ if (def.rest && !itemAborted) {
1521
1596
  let i = items.length - 1;
1522
1597
  const rest = input.slice(items.length);
1598
+ let seen = payload.issues.length;
1523
1599
  for (const el of rest) {
1600
+ if (abortEarly && payload.issues.length !== seen) {
1601
+ if (util.aborted(payload, seen))
1602
+ break;
1603
+ seen = payload.issues.length;
1604
+ }
1524
1605
  i++;
1525
1606
  const result = def.rest._zod.run({ value: el, issues: [] }, ctx);
1526
1607
  if (result instanceof Promise) {
@@ -1602,6 +1683,7 @@ export const $ZodRecord = /*@__PURE__*/ core.$constructor("$ZodRecord", (inst, d
1602
1683
  });
1603
1684
  return payload;
1604
1685
  }
1686
+ // no guard in either loop below: a record's invalid_key aborts but an enclosing intersection can reconcile it, so a stopped loop hides keys the sibling does not own and the intersection then rejects nothing
1605
1687
  const proms = [];
1606
1688
  const values = def.keyType._zod.values;
1607
1689
  if (values && !def.partial) {
@@ -1773,7 +1855,14 @@ export const $ZodMap = /*@__PURE__*/ core.$constructor("$ZodMap", (inst, def) =>
1773
1855
  }
1774
1856
  const proms = [];
1775
1857
  payload.value = memo ? memo.alloc(inst, payload, new Map(), ctx) : new Map();
1858
+ const abortEarly = ctx?.abortEarly;
1859
+ let seen = payload.issues.length;
1776
1860
  for (const [key, value] of input) {
1861
+ if (abortEarly && payload.issues.length !== seen) {
1862
+ if (util.aborted(payload, seen))
1863
+ break;
1864
+ seen = payload.issues.length;
1865
+ }
1777
1866
  const keyResult = def.keyType._zod.run({ value: key, issues: [] }, ctx);
1778
1867
  const valueResult = def.valueType._zod.run({ value: value, issues: [] }, ctx);
1779
1868
  if (keyResult instanceof Promise || valueResult instanceof Promise) {
@@ -1839,7 +1928,14 @@ export const $ZodSet = /*@__PURE__*/ core.$constructor("$ZodSet", (inst, def) =>
1839
1928
  }
1840
1929
  const proms = [];
1841
1930
  payload.value = memo ? memo.alloc(inst, payload, new Set(), ctx) : new Set();
1931
+ const abortEarly = ctx?.abortEarly;
1932
+ let seen = payload.issues.length;
1842
1933
  for (const item of input) {
1934
+ if (abortEarly && payload.issues.length !== seen) {
1935
+ if (util.aborted(payload, seen))
1936
+ break;
1937
+ seen = payload.issues.length;
1938
+ }
1843
1939
  const result = def.valueType._zod.run({ value: item, issues: [] }, ctx);
1844
1940
  if (result instanceof Promise) {
1845
1941
  proms.push(result.then((result) => handleSetResult(result, payload)));
@@ -1863,9 +1959,11 @@ export const $ZodEnum = /*@__PURE__*/ core.$constructor("$ZodEnum", (inst, def)
1863
1959
  const values = util.getEnumValues(def.entries);
1864
1960
  const valuesSet = new Set(values);
1865
1961
  inst._zod.values = valuesSet;
1866
- const patternValues = values.filter((k) => util.propertyKeyTypes.has(typeof k));
1867
- // unmatchable fallback, RE2-safe: an empty alternation would compile to /^()$/, which matches ""
1868
- inst._zod.pattern = new RegExp(patternValues.length ? `^(${patternValues.map((o) => util.escapeRegex(o.toString())).join("|")})$` : "^[^\\s\\S]$");
1962
+ util.defineLazyInternal(inst, "pattern", (zod) => {
1963
+ const patternValues = util.getEnumValues(zod.def.entries).filter((k) => util.propertyKeyTypes.has(typeof k));
1964
+ // unmatchable fallback, RE2-safe: an empty alternation would compile to /^()$/, which matches ""
1965
+ return new RegExp(patternValues.length ? `^(${patternValues.map((o) => util.escapeRegex(o.toString())).join("|")})$` : "^[^\\s\\S]$");
1966
+ });
1869
1967
  inst._zod.parse = (payload, _ctx) => {
1870
1968
  const input = payload.value;
1871
1969
  if (valuesSet.has(input)) {
@@ -1884,12 +1982,15 @@ export const $ZodLiteral = /*@__PURE__*/ core.$constructor("$ZodLiteral", (inst,
1884
1982
  $ZodType.init(inst, def);
1885
1983
  const values = new Set(def.values);
1886
1984
  inst._zod.values = values;
1887
- // unmatchable fallback, RE2-safe: an empty alternation would compile to /^()$/, which matches ""
1888
- inst._zod.pattern = new RegExp(def.values.length
1889
- ? `^(${def.values
1890
- .map((o) => (typeof o === "string" ? util.escapeRegex(o) : o ? util.escapeRegex(o.toString()) : String(o)))
1891
- .join("|")})$`
1892
- : "^[^\\s\\S]$");
1985
+ util.defineLazyInternal(inst, "pattern", (zod) => {
1986
+ const vals = zod.def.values;
1987
+ // unmatchable fallback, RE2-safe: an empty alternation would compile to /^()$/, which matches ""
1988
+ return new RegExp(vals.length
1989
+ ? `^(${vals
1990
+ .map((o) => typeof o === "string" ? util.escapeRegex(o) : o ? util.escapeRegex(o.toString()) : String(o))
1991
+ .join("|")})$`
1992
+ : "^[^\\s\\S]$");
1993
+ });
1893
1994
  inst._zod.parse = (payload, _ctx) => {
1894
1995
  const input = payload.value;
1895
1996
  if (values.has(input)) {
@@ -2253,22 +2354,67 @@ function handleReadonlyResult(payload) {
2253
2354
  payload.value = Object.freeze(payload.value);
2254
2355
  return payload;
2255
2356
  }
2357
+ // a leaf's pattern source with its own checks folded in: the last pattern-carrying check wins, else length bounds narrow the catch-all, else an integer format narrows the number form. the fold lives here instead of on `_zod.pattern` so a bundle without template literals never pays for it
2358
+ function leafPattern(schema) {
2359
+ const def = schema._zod.def;
2360
+ let pattern = def.pattern;
2361
+ let isInt = !!def.format?.includes("int");
2362
+ let minimum;
2363
+ let maximum;
2364
+ for (const ch of def.checks ?? []) {
2365
+ const d = ch._zod.def;
2366
+ if (d.pattern)
2367
+ pattern = d.pattern;
2368
+ isInt || (isInt = !!d.format?.includes("int"));
2369
+ const lo = d.minimum ?? d.length;
2370
+ const hi = d.maximum ?? d.length;
2371
+ if (lo !== undefined && (minimum === undefined || lo > minimum))
2372
+ minimum = lo;
2373
+ if (hi !== undefined && (maximum === undefined || hi < maximum))
2374
+ maximum = hi;
2375
+ }
2376
+ if (pattern)
2377
+ return pattern.source;
2378
+ // an empty range matches nothing at runtime, and `{8,5}` is not a legal quantifier
2379
+ if (minimum !== undefined && maximum !== undefined && minimum > maximum)
2380
+ return "(?!)";
2381
+ if (minimum !== undefined || maximum !== undefined)
2382
+ return regexes.string({ minimum, maximum }).source;
2383
+ const own = schema._zod.pattern;
2384
+ return (isInt && own === regexes.number ? regexes.integer : own)?.source;
2385
+ }
2386
+ // a part's pattern source. a wrapper's pattern embeds its inner pattern's source verbatim, so the folded form is substituted in place without knowing the wrapper's own composition; a union's options are joined the way the union builds its own pattern
2387
+ function partPattern(schema) {
2388
+ const def = schema._zod.def;
2389
+ const own = schema._zod.pattern?.source;
2390
+ // lazy resolves its inner on the internals, not the def
2391
+ const inner = def.innerType ?? schema._zod.innerType;
2392
+ if (inner) {
2393
+ const before = inner._zod.pattern?.source;
2394
+ const after = partPattern(inner);
2395
+ if (own && before && after && after !== before) {
2396
+ return own.replace(util.cleanRegex(before), () => util.cleanRegex(after));
2397
+ }
2398
+ return own;
2399
+ }
2400
+ if (def.options) {
2401
+ const sources = def.options.map(partPattern);
2402
+ if (sources.every(Boolean))
2403
+ return `^(${sources.map((s) => util.cleanRegex(s)).join("|")})$`;
2404
+ }
2405
+ return leafPattern(schema);
2406
+ }
2256
2407
  export const $ZodTemplateLiteral = /*@__PURE__*/ core.$constructor("$ZodTemplateLiteral", (inst, def) => {
2257
2408
  $ZodType.init(inst, def);
2258
2409
  const regexParts = [];
2259
2410
  for (const part of def.parts) {
2260
2411
  if (typeof part === "object" && part !== null) {
2261
2412
  // is Zod schema
2262
- if (!part._zod.pattern) {
2263
- // if (!source)
2413
+ const source = partPattern(part);
2414
+ if (!source) {
2264
2415
  throw new Error(`Invalid template literal part, no pattern found: ${[...part._zod.traits].shift()}`);
2265
2416
  }
2266
- const source = part._zod.pattern instanceof RegExp ? part._zod.pattern.source : part._zod.pattern;
2267
- if (!source)
2268
- throw new Error(`Invalid template literal part: ${part._zod.traits}`);
2269
- const start = source.startsWith("^") ? 1 : 0;
2270
- const end = source.endsWith("$") ? source.length - 1 : source.length;
2271
- regexParts.push(source.slice(start, end));
2417
+ regexParts.push(util.cleanRegex(source));
2272
2418
  }
2273
2419
  else if (part === null || util.primitiveTypes.has(typeof part)) {
2274
2420
  regexParts.push(util.escapeRegex(`${part}`));
@@ -2438,3 +2584,64 @@ function handleRefineResult(result, payload, input, inst) {
2438
2584
  payload.issues.push(util.issue(_iss));
2439
2585
  }
2440
2586
  }
2587
+ // asserts in place: the child result's value is discarded, matching z.property(), because a nested object or array schema rebuilds its output even when nothing transformed
2588
+ function handlePropertiesResult(result, payload, key) {
2589
+ if (result.issues.length) {
2590
+ payload.issues.push(...util.prefixIssues(key, result.issues));
2591
+ }
2592
+ }
2593
+ export const $ZodProperties = /*@__PURE__*/ core.$constructor("$ZodProperties", (inst, def) => {
2594
+ // $ZodType.init prepends an instance that already carries the $ZodCheck trait to its own `checks`, which would run the shape a second time and cost the parse context. Initializing the check trait after it keeps the schema role in `parse`, where the context arrives.
2595
+ $ZodType.init(inst, def);
2596
+ checks.$ZodCheck.init(inst, def);
2597
+ const memo = core.globalConfig.memoizer;
2598
+ memo?.attach(inst);
2599
+ // key and schema snapshotted together: reading one live and the other cached lets a later mutation of the caller's shape object pair a stale key with a missing schema
2600
+ let entries;
2601
+ const runShape = (payload, ctx) => {
2602
+ entries ?? (entries = Reflect.ownKeys(def.shape).map((key) => [key, def.shape[key]]));
2603
+ const input = payload.value;
2604
+ let proms;
2605
+ for (const [key, schema] of entries) {
2606
+ const result = schema._zod.run({ value: input[key], issues: [] }, ctx);
2607
+ if (result instanceof Promise) {
2608
+ proms ?? (proms = []);
2609
+ proms.push(result.then((result) => handlePropertiesResult(result, payload, key)));
2610
+ }
2611
+ else {
2612
+ handlePropertiesResult(result, payload, key);
2613
+ }
2614
+ }
2615
+ if (proms)
2616
+ return Promise.all(proms).then(() => undefined);
2617
+ return undefined;
2618
+ };
2619
+ inst._zod.parse = (payload, ctx) => {
2620
+ const input = payload.value;
2621
+ // as a schema this is the type gate, and it infers an object shape, so a primitive is a type error. A function passes: its properties read like any other object's, and z.instanceof allows one.
2622
+ if (input === null || (typeof input !== "object" && typeof input !== "function")) {
2623
+ payload.issues.push({ expected: "object", code: "invalid_type", input, inst });
2624
+ return payload;
2625
+ }
2626
+ // both sides declare the shape's input type, so the assertion runs forward in either direction; encoding the children backward would reject the very type this schema claims to take
2627
+ if (ctx.direction === "backward")
2628
+ ctx = { ...ctx, direction: "forward" };
2629
+ // the input is its own output here, so it registers as its own memo entry: a cycle re-entering this node hits the bucket instead of recursing forever
2630
+ if (memo)
2631
+ memo.alloc(inst, payload, input, ctx);
2632
+ const result = runShape(payload, ctx);
2633
+ return result instanceof Promise ? result.then(() => payload) : payload;
2634
+ };
2635
+ // as a check the base schema already typed the value, so this only asserts the properties — which read on a primitive too, matching z.property() on a string's length. It gets no context of its own, so a cycle through a spread schema is not tracked.
2636
+ inst._zod.check = (payload) => {
2637
+ if (payload.value == null) {
2638
+ payload.issues.push({ expected: "object", code: "invalid_type", input: payload.value, inst });
2639
+ return undefined;
2640
+ }
2641
+ return runShape(payload, {});
2642
+ };
2643
+ }, {
2644
+ *[Symbol.iterator]() {
2645
+ yield this;
2646
+ },
2647
+ });