zod 4.5.4 → 4.6.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 (347) hide show
  1. package/compile.cjs +19 -0
  2. package/index.cjs +19 -0
  3. package/locales/index.cjs +19 -0
  4. package/mini/index.cjs +19 -0
  5. package/package.json +8 -7
  6. package/src/v3/helpers/errorUtil.ts +1 -1
  7. package/src/v4/classic/checks.ts +0 -1
  8. package/src/v4/classic/external.ts +2 -0
  9. package/src/v4/classic/from-json-schema.ts +248 -25
  10. package/src/v4/classic/schemas.ts +255 -164
  11. package/src/v4/classic/tests/assignability.test.ts +59 -9
  12. package/src/v4/classic/tests/bigint.test.ts +6 -0
  13. package/src/v4/classic/tests/continuability.test.ts +2 -2
  14. package/src/v4/classic/tests/cyclic-data.test.ts +274 -0
  15. package/src/v4/classic/tests/enum.test.ts +9 -0
  16. package/src/v4/classic/tests/error.test.ts +130 -1
  17. package/src/v4/classic/tests/firstparty.test.ts +4 -0
  18. package/src/v4/classic/tests/from-json-schema.test.ts +157 -0
  19. package/src/v4/classic/tests/iban.test.ts +81 -0
  20. package/src/v4/classic/tests/instance-footprint.test.ts +18 -2
  21. package/src/v4/classic/tests/instanceof.test.ts +27 -7
  22. package/src/v4/classic/tests/issue-schema.test.ts +88 -56
  23. package/src/v4/classic/tests/object.test.ts +56 -0
  24. package/src/v4/classic/tests/optin-ladder.test.ts +3 -2
  25. package/src/v4/classic/tests/properties.test.ts +225 -0
  26. package/src/v4/classic/tests/recursive-types.test.ts +1 -1
  27. package/src/v4/classic/tests/standard-schema.test.ts +19 -0
  28. package/src/v4/classic/tests/string.test.ts +25 -0
  29. package/src/v4/classic/tests/template-literal.test.ts +33 -1
  30. package/src/v4/classic/tests/to-json-schema.test.ts +147 -3
  31. package/src/v4/classic/tests/validate.test.ts +569 -0
  32. package/src/v4/core/api.ts +30 -6
  33. package/src/v4/core/checks.ts +1 -101
  34. package/src/v4/core/compile.ts +193 -81
  35. package/src/v4/core/doc.ts +6 -2
  36. package/src/v4/core/errors.ts +3 -6
  37. package/src/v4/core/json-schema-generator.ts +2 -2
  38. package/src/v4/core/json-schema-processors.ts +226 -68
  39. package/src/v4/core/memoizer.ts +78 -28
  40. package/src/v4/core/parse.ts +37 -16
  41. package/src/v4/core/regexes.ts +10 -4
  42. package/src/v4/core/schemas.ts +314 -83
  43. package/src/v4/core/tests/cached.test.ts +54 -0
  44. package/src/v4/core/tests/compile-generative.test.ts +445 -0
  45. package/src/v4/core/tests/compile.test.ts +222 -2
  46. package/src/v4/core/tests/email-regex.test.ts +140 -0
  47. package/src/v4/core/tests/polyfill-collision.test.ts +46 -0
  48. package/src/v4/core/to-json-schema.ts +10 -8
  49. package/src/v4/core/util.ts +222 -147
  50. package/src/v4/core/versions.ts +2 -2
  51. package/src/v4/core/visit.ts +11 -0
  52. package/src/v4/locales/ar.ts +1 -0
  53. package/src/v4/locales/az.ts +1 -0
  54. package/src/v4/locales/be.ts +1 -0
  55. package/src/v4/locales/bg.ts +1 -0
  56. package/src/v4/locales/bn.ts +1 -0
  57. package/src/v4/locales/ca.ts +1 -0
  58. package/src/v4/locales/ckb.ts +1 -0
  59. package/src/v4/locales/cs.ts +1 -0
  60. package/src/v4/locales/da.ts +1 -0
  61. package/src/v4/locales/de.ts +1 -0
  62. package/src/v4/locales/el.ts +1 -0
  63. package/src/v4/locales/en.ts +1 -0
  64. package/src/v4/locales/eo.ts +1 -0
  65. package/src/v4/locales/es.ts +1 -0
  66. package/src/v4/locales/fa.ts +1 -0
  67. package/src/v4/locales/fi.ts +1 -0
  68. package/src/v4/locales/fr-CA.ts +1 -0
  69. package/src/v4/locales/fr.ts +1 -0
  70. package/src/v4/locales/gu.ts +1 -0
  71. package/src/v4/locales/he.ts +1 -0
  72. package/src/v4/locales/hi.ts +1 -0
  73. package/src/v4/locales/hr.ts +1 -0
  74. package/src/v4/locales/hu.ts +1 -0
  75. package/src/v4/locales/hy.ts +1 -0
  76. package/src/v4/locales/id.ts +1 -0
  77. package/src/v4/locales/is.ts +1 -0
  78. package/src/v4/locales/it.ts +1 -0
  79. package/src/v4/locales/ja.ts +1 -0
  80. package/src/v4/locales/ka.ts +1 -0
  81. package/src/v4/locales/km.ts +1 -0
  82. package/src/v4/locales/kn.ts +1 -0
  83. package/src/v4/locales/ko.ts +1 -0
  84. package/src/v4/locales/lt.ts +1 -0
  85. package/src/v4/locales/mk.ts +1 -0
  86. package/src/v4/locales/ms.ts +1 -0
  87. package/src/v4/locales/ne.ts +1 -0
  88. package/src/v4/locales/nl.ts +1 -0
  89. package/src/v4/locales/nn.ts +1 -0
  90. package/src/v4/locales/no.ts +1 -0
  91. package/src/v4/locales/ota.ts +1 -0
  92. package/src/v4/locales/pl.ts +1 -0
  93. package/src/v4/locales/ps.ts +1 -0
  94. package/src/v4/locales/pt-BR.ts +1 -0
  95. package/src/v4/locales/pt.ts +1 -0
  96. package/src/v4/locales/ro.ts +1 -0
  97. package/src/v4/locales/ru.ts +1 -0
  98. package/src/v4/locales/sk.ts +1 -0
  99. package/src/v4/locales/sl.ts +1 -0
  100. package/src/v4/locales/sv.ts +1 -0
  101. package/src/v4/locales/ta.ts +1 -0
  102. package/src/v4/locales/th.ts +1 -0
  103. package/src/v4/locales/tk.ts +1 -0
  104. package/src/v4/locales/tr.ts +1 -0
  105. package/src/v4/locales/uk.ts +1 -0
  106. package/src/v4/locales/ur.ts +1 -0
  107. package/src/v4/locales/uz.ts +1 -0
  108. package/src/v4/locales/vi.ts +1 -0
  109. package/src/v4/locales/yo.ts +1 -0
  110. package/src/v4/locales/zh-CN.ts +1 -0
  111. package/src/v4/locales/zh-TW.ts +1 -0
  112. package/src/v4/mini/checks.ts +0 -1
  113. package/src/v4/mini/external.ts +2 -0
  114. package/src/v4/mini/schemas.ts +38 -2
  115. package/src/v4/mini/tests/checks.test.ts +1 -1
  116. package/src/v4/mini/tests/computed.test.ts +11 -17
  117. package/src/v4/mini/tests/index.test.ts +8 -0
  118. package/src/v4/mini/tests/string.test.ts +16 -0
  119. package/v3/ZodError.cjs +19 -0
  120. package/v3/errors.cjs +19 -0
  121. package/v3/external.cjs +19 -0
  122. package/v3/helpers/enumUtil.cjs +19 -0
  123. package/v3/helpers/errorUtil.cjs +20 -1
  124. package/v3/helpers/errorUtil.js +1 -1
  125. package/v3/helpers/parseUtil.cjs +19 -0
  126. package/v3/helpers/partialUtil.cjs +19 -0
  127. package/v3/helpers/typeAliases.cjs +19 -0
  128. package/v3/helpers/util.cjs +19 -0
  129. package/v3/index.cjs +19 -0
  130. package/v3/standard-schema.cjs +19 -0
  131. package/v3/types.cjs +19 -0
  132. package/v4/classic/checks.cjs +20 -2
  133. package/v4/classic/checks.d.cts +1 -1
  134. package/v4/classic/checks.d.ts +1 -1
  135. package/v4/classic/checks.js +1 -1
  136. package/v4/classic/coerce.cjs +19 -0
  137. package/v4/classic/compat.cjs +19 -0
  138. package/v4/classic/deep-partial.cjs +19 -0
  139. package/v4/classic/errors.cjs +19 -0
  140. package/v4/classic/external.cjs +22 -1
  141. package/v4/classic/external.d.cts +1 -1
  142. package/v4/classic/external.d.ts +1 -1
  143. package/v4/classic/external.js +1 -1
  144. package/v4/classic/from-json-schema.cjs +264 -24
  145. package/v4/classic/from-json-schema.js +245 -24
  146. package/v4/classic/in-out.cjs +19 -0
  147. package/v4/classic/index.cjs +19 -0
  148. package/v4/classic/iso.cjs +19 -0
  149. package/v4/classic/parse.cjs +19 -0
  150. package/v4/classic/schemas.cjs +100 -31
  151. package/v4/classic/schemas.d.cts +17 -1
  152. package/v4/classic/schemas.d.ts +17 -1
  153. package/v4/classic/schemas.js +77 -29
  154. package/v4/core/api.cjs +37 -2
  155. package/v4/core/api.d.cts +6 -4
  156. package/v4/core/api.d.ts +6 -4
  157. package/v4/core/api.js +17 -2
  158. package/v4/core/checks.cjs +19 -96
  159. package/v4/core/checks.d.cts +1 -1
  160. package/v4/core/checks.d.ts +1 -1
  161. package/v4/core/checks.js +0 -96
  162. package/v4/core/compile.cjs +164 -65
  163. package/v4/core/compile.d.cts +18 -4
  164. package/v4/core/compile.d.ts +18 -4
  165. package/v4/core/compile.js +145 -66
  166. package/v4/core/core.cjs +19 -0
  167. package/v4/core/doc.cjs +26 -2
  168. package/v4/core/doc.js +7 -2
  169. package/v4/core/errors.cjs +21 -5
  170. package/v4/core/errors.js +2 -5
  171. package/v4/core/index.cjs +19 -0
  172. package/v4/core/json-schema-generator.cjs +20 -1
  173. package/v4/core/json-schema-generator.js +2 -2
  174. package/v4/core/json-schema-processors.cjs +215 -63
  175. package/v4/core/json-schema-processors.d.cts +17 -0
  176. package/v4/core/json-schema-processors.d.ts +17 -0
  177. package/v4/core/json-schema-processors.js +195 -64
  178. package/v4/core/json-schema.cjs +19 -0
  179. package/v4/core/memoizer.cjs +114 -23
  180. package/v4/core/memoizer.js +72 -23
  181. package/v4/core/parse.cjs +56 -16
  182. package/v4/core/parse.js +37 -16
  183. package/v4/core/regexes.cjs +29 -6
  184. package/v4/core/regexes.d.cts +2 -0
  185. package/v4/core/regexes.d.ts +2 -0
  186. package/v4/core/regexes.js +8 -4
  187. package/v4/core/registries.cjs +19 -0
  188. package/v4/core/schemas.cjs +285 -66
  189. package/v4/core/schemas.d.cts +34 -3
  190. package/v4/core/schemas.d.ts +34 -3
  191. package/v4/core/schemas.js +264 -65
  192. package/v4/core/standard-schema.cjs +19 -0
  193. package/v4/core/to-json-schema.cjs +29 -7
  194. package/v4/core/to-json-schema.d.cts +7 -3
  195. package/v4/core/to-json-schema.d.ts +7 -3
  196. package/v4/core/to-json-schema.js +8 -7
  197. package/v4/core/util.cjs +181 -143
  198. package/v4/core/util.d.cts +25 -4
  199. package/v4/core/util.d.ts +25 -4
  200. package/v4/core/util.js +160 -143
  201. package/v4/core/versions.cjs +21 -2
  202. package/v4/core/versions.d.cts +1 -1
  203. package/v4/core/versions.d.ts +1 -1
  204. package/v4/core/versions.js +2 -2
  205. package/v4/core/visit.cjs +31 -0
  206. package/v4/core/visit.js +12 -0
  207. package/v4/index.cjs +19 -0
  208. package/v4/locales/ar.cjs +1 -0
  209. package/v4/locales/ar.js +1 -0
  210. package/v4/locales/az.cjs +1 -0
  211. package/v4/locales/az.js +1 -0
  212. package/v4/locales/be.cjs +1 -0
  213. package/v4/locales/be.js +1 -0
  214. package/v4/locales/bg.cjs +1 -0
  215. package/v4/locales/bg.js +1 -0
  216. package/v4/locales/bn.cjs +1 -0
  217. package/v4/locales/bn.js +1 -0
  218. package/v4/locales/ca.cjs +1 -0
  219. package/v4/locales/ca.js +1 -0
  220. package/v4/locales/ckb.cjs +1 -0
  221. package/v4/locales/ckb.js +1 -0
  222. package/v4/locales/cs.cjs +1 -0
  223. package/v4/locales/cs.js +1 -0
  224. package/v4/locales/da.cjs +1 -0
  225. package/v4/locales/da.js +1 -0
  226. package/v4/locales/de.cjs +1 -0
  227. package/v4/locales/de.js +1 -0
  228. package/v4/locales/el.cjs +1 -0
  229. package/v4/locales/el.js +1 -0
  230. package/v4/locales/en.cjs +1 -0
  231. package/v4/locales/en.js +1 -0
  232. package/v4/locales/eo.cjs +1 -0
  233. package/v4/locales/eo.js +1 -0
  234. package/v4/locales/es.cjs +1 -0
  235. package/v4/locales/es.js +1 -0
  236. package/v4/locales/fa.cjs +1 -0
  237. package/v4/locales/fa.js +1 -0
  238. package/v4/locales/fi.cjs +1 -0
  239. package/v4/locales/fi.js +1 -0
  240. package/v4/locales/fr-CA.cjs +1 -0
  241. package/v4/locales/fr-CA.js +1 -0
  242. package/v4/locales/fr.cjs +1 -0
  243. package/v4/locales/fr.js +1 -0
  244. package/v4/locales/gu.cjs +1 -0
  245. package/v4/locales/gu.js +1 -0
  246. package/v4/locales/he.cjs +1 -0
  247. package/v4/locales/he.js +1 -0
  248. package/v4/locales/hi.cjs +1 -0
  249. package/v4/locales/hi.js +1 -0
  250. package/v4/locales/hr.cjs +1 -0
  251. package/v4/locales/hr.js +1 -0
  252. package/v4/locales/hu.cjs +1 -0
  253. package/v4/locales/hu.js +1 -0
  254. package/v4/locales/hy.cjs +1 -0
  255. package/v4/locales/hy.js +1 -0
  256. package/v4/locales/id.cjs +1 -0
  257. package/v4/locales/id.js +1 -0
  258. package/v4/locales/index.cjs +19 -0
  259. package/v4/locales/is.cjs +1 -0
  260. package/v4/locales/is.js +1 -0
  261. package/v4/locales/it.cjs +1 -0
  262. package/v4/locales/it.js +1 -0
  263. package/v4/locales/ja.cjs +1 -0
  264. package/v4/locales/ja.js +1 -0
  265. package/v4/locales/ka.cjs +1 -0
  266. package/v4/locales/ka.js +1 -0
  267. package/v4/locales/km.cjs +1 -0
  268. package/v4/locales/km.js +1 -0
  269. package/v4/locales/kn.cjs +1 -0
  270. package/v4/locales/kn.js +1 -0
  271. package/v4/locales/ko.cjs +1 -0
  272. package/v4/locales/ko.js +1 -0
  273. package/v4/locales/lt.cjs +1 -0
  274. package/v4/locales/lt.js +1 -0
  275. package/v4/locales/mk.cjs +1 -0
  276. package/v4/locales/mk.js +1 -0
  277. package/v4/locales/ms.cjs +1 -0
  278. package/v4/locales/ms.js +1 -0
  279. package/v4/locales/ne.cjs +1 -0
  280. package/v4/locales/ne.js +1 -0
  281. package/v4/locales/nl.cjs +1 -0
  282. package/v4/locales/nl.js +1 -0
  283. package/v4/locales/nn.cjs +1 -0
  284. package/v4/locales/nn.js +1 -0
  285. package/v4/locales/no.cjs +1 -0
  286. package/v4/locales/no.js +1 -0
  287. package/v4/locales/ota.cjs +1 -0
  288. package/v4/locales/ota.js +1 -0
  289. package/v4/locales/pl.cjs +1 -0
  290. package/v4/locales/pl.js +1 -0
  291. package/v4/locales/ps.cjs +1 -0
  292. package/v4/locales/ps.js +1 -0
  293. package/v4/locales/pt-BR.cjs +1 -0
  294. package/v4/locales/pt-BR.js +1 -0
  295. package/v4/locales/pt.cjs +1 -0
  296. package/v4/locales/pt.js +1 -0
  297. package/v4/locales/ro.cjs +1 -0
  298. package/v4/locales/ro.js +1 -0
  299. package/v4/locales/ru.cjs +1 -0
  300. package/v4/locales/ru.js +1 -0
  301. package/v4/locales/sk.cjs +1 -0
  302. package/v4/locales/sk.js +1 -0
  303. package/v4/locales/sl.cjs +1 -0
  304. package/v4/locales/sl.js +1 -0
  305. package/v4/locales/sv.cjs +1 -0
  306. package/v4/locales/sv.js +1 -0
  307. package/v4/locales/ta.cjs +1 -0
  308. package/v4/locales/ta.js +1 -0
  309. package/v4/locales/th.cjs +1 -0
  310. package/v4/locales/th.js +1 -0
  311. package/v4/locales/tk.cjs +1 -0
  312. package/v4/locales/tk.js +1 -0
  313. package/v4/locales/tr.cjs +1 -0
  314. package/v4/locales/tr.js +1 -0
  315. package/v4/locales/uk.cjs +1 -0
  316. package/v4/locales/uk.js +1 -0
  317. package/v4/locales/ur.cjs +1 -0
  318. package/v4/locales/ur.js +1 -0
  319. package/v4/locales/uz.cjs +1 -0
  320. package/v4/locales/uz.js +1 -0
  321. package/v4/locales/vi.cjs +1 -0
  322. package/v4/locales/vi.js +1 -0
  323. package/v4/locales/yo.cjs +1 -0
  324. package/v4/locales/yo.js +1 -0
  325. package/v4/locales/zh-CN.cjs +1 -0
  326. package/v4/locales/zh-CN.js +1 -0
  327. package/v4/locales/zh-TW.cjs +1 -0
  328. package/v4/locales/zh-TW.js +1 -0
  329. package/v4/mini/checks.cjs +20 -2
  330. package/v4/mini/checks.d.cts +1 -1
  331. package/v4/mini/checks.d.ts +1 -1
  332. package/v4/mini/checks.js +1 -1
  333. package/v4/mini/coerce.cjs +19 -0
  334. package/v4/mini/deep-partial.cjs +19 -0
  335. package/v4/mini/external.cjs +22 -1
  336. package/v4/mini/external.d.cts +1 -1
  337. package/v4/mini/external.d.ts +1 -1
  338. package/v4/mini/external.js +1 -1
  339. package/v4/mini/in-out.cjs +19 -0
  340. package/v4/mini/index.cjs +19 -0
  341. package/v4/mini/iso.cjs +19 -0
  342. package/v4/mini/parse.cjs +19 -0
  343. package/v4/mini/schemas.cjs +42 -4
  344. package/v4/mini/schemas.d.cts +8 -0
  345. package/v4/mini/schemas.d.ts +8 -0
  346. package/v4/mini/schemas.js +19 -2
  347. package/v4-mini/index.cjs +19 -0
@@ -1,9 +1,33 @@
1
1
  "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || function (mod) {
19
+ if (mod && mod.__esModule) return mod;
20
+ var result = {};
21
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
22
+ __setModuleDefault(result, mod);
23
+ return result;
24
+ };
2
25
  Object.defineProperty(exports, "__esModule", { value: true });
3
26
  exports.$ZodCyclicError = void 0;
4
27
  exports.isRecursiveSchema = isRecursiveSchema;
5
28
  exports.memoizer = memoizer;
6
29
  exports.isBackEdge = isBackEdge;
30
+ const util = __importStar(require("./util.cjs"));
7
31
  class $ZodCyclicError extends Error {
8
32
  constructor() {
9
33
  super(`Cannot parse a reference cycle that closes through a transform`);
@@ -14,35 +38,68 @@ exports.$ZodCyclicError = $ZodCyclicError;
14
38
  /** Keyed off the context object every schema in one parse call already shares. */
15
39
  const STATE = "~memo";
16
40
  const NO_ISSUES = [];
41
+ // a value a cycle can close through; callables count, since z.properties asserts on one
42
+ function isRef(value) {
43
+ return value !== null && (typeof value === "object" || typeof value === "function");
44
+ }
17
45
  // Receivers prefix paths in place, so the cache and every hand-out need their own copies.
18
46
  function cloneIssues(issues) {
19
47
  return issues.map((iss) => (iss.path ? { ...iss, path: iss.path.slice() } : { ...iss }));
20
48
  }
21
49
  const recursive = /*@__PURE__*/ new WeakMap();
50
+ /** What the walk established, in order of certainty: ordered so the strongest answer among children wins. */
51
+ const NONE = 0;
52
+ const ASSUMED = 1;
53
+ const PROVEN = 2;
22
54
  /** Whether this schema's subtree contains a cycle, so one parse can re-enter it. */
23
- function isRecursive(inst, stack) {
55
+ function isRecursive(inst, stack, resolve) {
24
56
  const cached = recursive.get(inst);
25
57
  if (cached !== undefined)
26
- return cached;
58
+ return cached ? PROVEN : NONE;
27
59
  // Relative to the walk in progress, so not cached.
28
60
  if (stack.has(inst))
29
- return true;
61
+ return PROVEN;
30
62
  stack.add(inst);
31
- let result = false;
63
+ let result = NONE;
32
64
  const check = (child) => {
33
- if (!result && child?._zod && isRecursive(child, stack))
34
- result = true;
65
+ if (result !== PROVEN && child?._zod) {
66
+ const answer = isRecursive(child, stack, resolve);
67
+ if (answer > result)
68
+ result = answer;
69
+ }
70
+ };
71
+ // `Reflect.ownKeys` rather than `Object.keys`, so a cycle through a declared symbol key is still seen
72
+ const shape = (sh, spread) => {
73
+ let answer = NONE;
74
+ for (const key of Reflect.ownKeys(sh)) {
75
+ const desc = Object.getOwnPropertyDescriptor(sh, key);
76
+ // an object resolves its shape by spread, so a key it does not enumerate is never parsed; `z.properties` reads every own key and so keeps them all
77
+ if (spread && !desc.enumerable)
78
+ continue;
79
+ // resolving runs user code, and a factory mints a fresh subtree per read, so an edge the walk can't follow counts as a cycle
80
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve) : NONE;
81
+ if (child > answer)
82
+ answer = child;
83
+ }
84
+ return answer;
85
+ };
86
+ const merge = (answer) => {
87
+ if (answer > result)
88
+ result = answer;
35
89
  };
36
90
  const def = inst._zod.def;
37
91
  const kind = def.type;
38
92
  switch (kind) {
39
93
  case "object": {
40
- // `Reflect.ownKeys` rather than `Object.keys`, so a cycle through a declared symbol key is still seen
41
- for (const key of Reflect.ownKeys(def.shape))
42
- check(def.shape[key]);
94
+ const raw = util.rawShape(def);
95
+ // a def with no raw shape answers `shape` from an accessor of its own, and running that can mint a whole fresh subtree
96
+ merge(raw ? shape(raw, true) : ASSUMED);
43
97
  check(def.catchall);
44
98
  break;
45
99
  }
100
+ case "properties":
101
+ merge(shape(def.shape, false));
102
+ break;
46
103
  case "array":
47
104
  check(def.element);
48
105
  break;
@@ -86,10 +143,13 @@ function isRecursive(inst, stack) {
86
143
  check(def.input);
87
144
  check(def.output);
88
145
  break;
89
- // reading `_zod.innerType` resolves the getter once and caches it
90
- case "lazy":
91
- check(inst._zod.innerType);
146
+ // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
147
+ case "lazy": {
148
+ const inner = def._cachedInner ?? (resolve ? inst._zod.innerType : undefined);
149
+ // walked with resolution off: one hop sees past the deferral, and a lazy that yields only another unresolved lazy is generative, so it stops there
150
+ merge(inner ? isRecursive(inner, stack, false) : ASSUMED);
92
151
  break;
152
+ }
93
153
  // a leaf by choice: `parts` are regex fragments, not data positions
94
154
  case "template_literal":
95
155
  // leaves
@@ -133,8 +193,13 @@ function isRecursive(inst, stack) {
133
193
  }
134
194
  }
135
195
  stack.delete(inst);
136
- recursive.set(inst, result);
137
- return result;
196
+ return settle(inst, result);
197
+ }
198
+ /** An assumed answer must not outlive the resolution that settles it, so only a certain one is cached. */
199
+ function settle(inst, answer) {
200
+ if (answer !== ASSUMED)
201
+ recursive.set(inst, answer === PROVEN);
202
+ return answer;
138
203
  }
139
204
  /**
140
205
  * Whether one parse can re-enter this schema, i.e. its subtree contains a cycle.
@@ -143,12 +208,13 @@ function isRecursive(inst, stack) {
143
208
  * generated fast path has no context to key on.
144
209
  */
145
210
  function isRecursiveSchema(inst) {
146
- return isRecursive(inst, new Set());
211
+ // z.compile never parses, so nothing would ever resolve a lazy for it; it runs once and already treats a throw here as recursive
212
+ return isRecursive(inst, new Set(), true) !== NONE;
147
213
  }
148
214
  function bucketFor(state, inst) {
149
215
  let bucket = state.buckets.get(inst);
150
216
  if (!bucket) {
151
- bucket = new Map();
217
+ bucket = new WeakMap();
152
218
  state.buckets.set(inst, bucket);
153
219
  }
154
220
  return bucket;
@@ -187,7 +253,8 @@ const memo = {
187
253
  attach(inst) {
188
254
  var _a;
189
255
  let isRecursiveInst;
190
- // `bucket` memoized for one parse; a recursive schema is re-entered many times and its bucket never changes
256
+ let rechecked = false;
257
+ // a recursive schema is re-entered many times per parse and its bucket never changes
191
258
  let lastCtx;
192
259
  let lastBucket;
193
260
  // Wraps `parse` in a deferred so it sees the container's final parse. Core's own deferred copies `parse` into `run` when there are no checks, and it ran first, so `run` is patched to match; with checks, `run` reads `parse` dynamically.
@@ -196,21 +263,26 @@ const memo = {
196
263
  const base = inst._zod.parse;
197
264
  const wrapped = (payload, ctx) => {
198
265
  if (isRecursiveInst === undefined) {
199
- isRecursiveInst = isRecursive(inst, new Set());
200
- if (!isRecursiveInst) {
266
+ const walked = isRecursive(inst, new Set(), false);
267
+ if (walked === NONE) {
201
268
  // Nothing here can ever fire, so take it back out.
202
269
  inst._zod.parse = base;
203
270
  if (inst._zod.run === wrapped)
204
271
  inst._zod.run = base;
205
272
  return base(payload, ctx);
206
273
  }
274
+ // this parse resolves the deferred edges on its own path, so ask once more before latching
275
+ if (walked === PROVEN || rechecked)
276
+ isRecursiveInst = true;
277
+ else
278
+ rechecked = true;
207
279
  }
208
280
  const input = payload.value;
209
- if (input === null || typeof input !== "object")
281
+ if (!isRef(input))
210
282
  return base(payload, ctx);
211
283
  let state = ctx[STATE];
212
284
  if (!state) {
213
- state = { buckets: new Map(), backEdges: undefined };
285
+ state = { buckets: new WeakMap(), backEdges: undefined };
214
286
  ctx[STATE] = state;
215
287
  }
216
288
  let bucket;
@@ -232,7 +304,7 @@ const memo = {
232
304
  else {
233
305
  // Still being parsed: its own checks cover it, so skip them here.
234
306
  payload.memo = true;
235
- state.backEdges ?? (state.backEdges = new Set());
307
+ state.backEdges ?? (state.backEdges = new WeakSet());
236
308
  state.backEdges.add(hit.value);
237
309
  }
238
310
  return payload;
@@ -268,5 +340,24 @@ function memoizer() {
268
340
  /** Whether this value is a node a back-edge resolved to before it finished. */
269
341
  function isBackEdge(ctx, value) {
270
342
  const backEdges = ctx[STATE]?.backEdges;
271
- return backEdges !== undefined && value !== null && typeof value === "object" && backEdges.has(value);
343
+ return backEdges !== undefined && isRef(value) && backEdges.has(value);
272
344
  }
345
+
346
+ // seal-cjs-exports
347
+ (function () {
348
+ var keys = Object.getOwnPropertyNames(exports);
349
+ for (var i = 0; i < keys.length; i++) {
350
+ var desc = Object.getOwnPropertyDescriptor(exports, keys[i]);
351
+ if (!desc || !desc.get || !desc.configurable) continue;
352
+ var value;
353
+ try {
354
+ value = desc.get();
355
+ } catch (e) {
356
+ continue;
357
+ }
358
+ // a circular require may not have settled this one yet, so leave it live
359
+ if (value === undefined) continue;
360
+ Object.defineProperty(exports, keys[i], { value: value, writable: false, enumerable: desc.enumerable, configurable: false });
361
+ }
362
+ Object.freeze(exports);
363
+ })();
@@ -1,3 +1,4 @@
1
+ import * as util from "./util.js";
1
2
  export class $ZodCyclicError extends Error {
2
3
  constructor() {
3
4
  super(`Cannot parse a reference cycle that closes through a transform`);
@@ -7,35 +8,68 @@ export class $ZodCyclicError extends Error {
7
8
  /** Keyed off the context object every schema in one parse call already shares. */
8
9
  const STATE = "~memo";
9
10
  const NO_ISSUES = [];
11
+ // a value a cycle can close through; callables count, since z.properties asserts on one
12
+ function isRef(value) {
13
+ return value !== null && (typeof value === "object" || typeof value === "function");
14
+ }
10
15
  // Receivers prefix paths in place, so the cache and every hand-out need their own copies.
11
16
  function cloneIssues(issues) {
12
17
  return issues.map((iss) => (iss.path ? { ...iss, path: iss.path.slice() } : { ...iss }));
13
18
  }
14
19
  const recursive = /*@__PURE__*/ new WeakMap();
20
+ /** What the walk established, in order of certainty: ordered so the strongest answer among children wins. */
21
+ const NONE = 0;
22
+ const ASSUMED = 1;
23
+ const PROVEN = 2;
15
24
  /** Whether this schema's subtree contains a cycle, so one parse can re-enter it. */
16
- function isRecursive(inst, stack) {
25
+ function isRecursive(inst, stack, resolve) {
17
26
  const cached = recursive.get(inst);
18
27
  if (cached !== undefined)
19
- return cached;
28
+ return cached ? PROVEN : NONE;
20
29
  // Relative to the walk in progress, so not cached.
21
30
  if (stack.has(inst))
22
- return true;
31
+ return PROVEN;
23
32
  stack.add(inst);
24
- let result = false;
33
+ let result = NONE;
25
34
  const check = (child) => {
26
- if (!result && child?._zod && isRecursive(child, stack))
27
- result = true;
35
+ if (result !== PROVEN && child?._zod) {
36
+ const answer = isRecursive(child, stack, resolve);
37
+ if (answer > result)
38
+ result = answer;
39
+ }
40
+ };
41
+ // `Reflect.ownKeys` rather than `Object.keys`, so a cycle through a declared symbol key is still seen
42
+ const shape = (sh, spread) => {
43
+ let answer = NONE;
44
+ for (const key of Reflect.ownKeys(sh)) {
45
+ const desc = Object.getOwnPropertyDescriptor(sh, key);
46
+ // an object resolves its shape by spread, so a key it does not enumerate is never parsed; `z.properties` reads every own key and so keeps them all
47
+ if (spread && !desc.enumerable)
48
+ continue;
49
+ // resolving runs user code, and a factory mints a fresh subtree per read, so an edge the walk can't follow counts as a cycle
50
+ const child = desc.get ? ASSUMED : desc.value?._zod ? isRecursive(desc.value, stack, resolve) : NONE;
51
+ if (child > answer)
52
+ answer = child;
53
+ }
54
+ return answer;
55
+ };
56
+ const merge = (answer) => {
57
+ if (answer > result)
58
+ result = answer;
28
59
  };
29
60
  const def = inst._zod.def;
30
61
  const kind = def.type;
31
62
  switch (kind) {
32
63
  case "object": {
33
- // `Reflect.ownKeys` rather than `Object.keys`, so a cycle through a declared symbol key is still seen
34
- for (const key of Reflect.ownKeys(def.shape))
35
- check(def.shape[key]);
64
+ const raw = util.rawShape(def);
65
+ // a def with no raw shape answers `shape` from an accessor of its own, and running that can mint a whole fresh subtree
66
+ merge(raw ? shape(raw, true) : ASSUMED);
36
67
  check(def.catchall);
37
68
  break;
38
69
  }
70
+ case "properties":
71
+ merge(shape(def.shape, false));
72
+ break;
39
73
  case "array":
40
74
  check(def.element);
41
75
  break;
@@ -79,10 +113,13 @@ function isRecursive(inst, stack) {
79
113
  check(def.input);
80
114
  check(def.output);
81
115
  break;
82
- // reading `_zod.innerType` resolves the getter once and caches it
83
- case "lazy":
84
- check(inst._zod.innerType);
116
+ // `$ZodLazy` caches its inner on the def, so a resolved edge is followed exactly
117
+ case "lazy": {
118
+ const inner = def._cachedInner ?? (resolve ? inst._zod.innerType : undefined);
119
+ // walked with resolution off: one hop sees past the deferral, and a lazy that yields only another unresolved lazy is generative, so it stops there
120
+ merge(inner ? isRecursive(inner, stack, false) : ASSUMED);
85
121
  break;
122
+ }
86
123
  // a leaf by choice: `parts` are regex fragments, not data positions
87
124
  case "template_literal":
88
125
  // leaves
@@ -126,8 +163,13 @@ function isRecursive(inst, stack) {
126
163
  }
127
164
  }
128
165
  stack.delete(inst);
129
- recursive.set(inst, result);
130
- return result;
166
+ return settle(inst, result);
167
+ }
168
+ /** An assumed answer must not outlive the resolution that settles it, so only a certain one is cached. */
169
+ function settle(inst, answer) {
170
+ if (answer !== ASSUMED)
171
+ recursive.set(inst, answer === PROVEN);
172
+ return answer;
131
173
  }
132
174
  /**
133
175
  * Whether one parse can re-enter this schema, i.e. its subtree contains a cycle.
@@ -136,12 +178,13 @@ function isRecursive(inst, stack) {
136
178
  * generated fast path has no context to key on.
137
179
  */
138
180
  export function isRecursiveSchema(inst) {
139
- return isRecursive(inst, new Set());
181
+ // z.compile never parses, so nothing would ever resolve a lazy for it; it runs once and already treats a throw here as recursive
182
+ return isRecursive(inst, new Set(), true) !== NONE;
140
183
  }
141
184
  function bucketFor(state, inst) {
142
185
  let bucket = state.buckets.get(inst);
143
186
  if (!bucket) {
144
- bucket = new Map();
187
+ bucket = new WeakMap();
145
188
  state.buckets.set(inst, bucket);
146
189
  }
147
190
  return bucket;
@@ -180,7 +223,8 @@ const memo = {
180
223
  attach(inst) {
181
224
  var _a;
182
225
  let isRecursiveInst;
183
- // `bucket` memoized for one parse; a recursive schema is re-entered many times and its bucket never changes
226
+ let rechecked = false;
227
+ // a recursive schema is re-entered many times per parse and its bucket never changes
184
228
  let lastCtx;
185
229
  let lastBucket;
186
230
  // Wraps `parse` in a deferred so it sees the container's final parse. Core's own deferred copies `parse` into `run` when there are no checks, and it ran first, so `run` is patched to match; with checks, `run` reads `parse` dynamically.
@@ -189,21 +233,26 @@ const memo = {
189
233
  const base = inst._zod.parse;
190
234
  const wrapped = (payload, ctx) => {
191
235
  if (isRecursiveInst === undefined) {
192
- isRecursiveInst = isRecursive(inst, new Set());
193
- if (!isRecursiveInst) {
236
+ const walked = isRecursive(inst, new Set(), false);
237
+ if (walked === NONE) {
194
238
  // Nothing here can ever fire, so take it back out.
195
239
  inst._zod.parse = base;
196
240
  if (inst._zod.run === wrapped)
197
241
  inst._zod.run = base;
198
242
  return base(payload, ctx);
199
243
  }
244
+ // this parse resolves the deferred edges on its own path, so ask once more before latching
245
+ if (walked === PROVEN || rechecked)
246
+ isRecursiveInst = true;
247
+ else
248
+ rechecked = true;
200
249
  }
201
250
  const input = payload.value;
202
- if (input === null || typeof input !== "object")
251
+ if (!isRef(input))
203
252
  return base(payload, ctx);
204
253
  let state = ctx[STATE];
205
254
  if (!state) {
206
- state = { buckets: new Map(), backEdges: undefined };
255
+ state = { buckets: new WeakMap(), backEdges: undefined };
207
256
  ctx[STATE] = state;
208
257
  }
209
258
  let bucket;
@@ -225,7 +274,7 @@ const memo = {
225
274
  else {
226
275
  // Still being parsed: its own checks cover it, so skip them here.
227
276
  payload.memo = true;
228
- state.backEdges ?? (state.backEdges = new Set());
277
+ state.backEdges ?? (state.backEdges = new WeakSet());
229
278
  state.backEdges.add(hit.value);
230
279
  }
231
280
  return payload;
@@ -261,5 +310,5 @@ export function memoizer() {
261
310
  /** Whether this value is a node a back-edge resolved to before it finished. */
262
311
  export function isBackEdge(ctx, value) {
263
312
  const backEdges = ctx[STATE]?.backEdges;
264
- return backEdges !== undefined && value !== null && typeof value === "object" && backEdges.has(value);
313
+ return backEdges !== undefined && isRef(value) && backEdges.has(value);
265
314
  }
package/v4/core/parse.cjs CHANGED
@@ -72,26 +72,38 @@ const _safeParse = (_Err) => (schema, value, _ctx) => {
72
72
  if (result instanceof Promise) {
73
73
  throw new core.$ZodAsyncError();
74
74
  }
75
- return result.issues.length
76
- ? {
77
- success: false,
78
- error: new (_Err ?? errors.$ZodError)(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),
79
- }
80
- : { success: true, data: result.value };
75
+ return result.issues.length ? failure(_Err, result.issues, ctx) : { success: true, data: result.value };
81
76
  };
82
77
  exports._safeParse = _safeParse;
83
78
  exports.safeParse = (0, exports._safeParse)(errors.$ZodRealError);
79
+ // the error is built on the first read of `error`: finalizing the issues and constructing the instance is most of a failing parse, and a caller that only branches on `success` never pays it. a getter in the literal keeps this small; the alternative, one shared accessor descriptor plus a hidden state slot, reads ~15% faster but costs ~75 B gzipped in every bundle
80
+ function failure(Err, issues, ctx) {
81
+ let error;
82
+ return {
83
+ success: false,
84
+ get error() {
85
+ if (!error) {
86
+ error = new Err(issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())));
87
+ // finalizeIssue drops `input`, so the built error holds nothing; keeping the raw issues past this point pins the parsed value for the life of the result
88
+ issues = undefined;
89
+ ctx = undefined;
90
+ }
91
+ return error;
92
+ },
93
+ set error(e) {
94
+ error = e;
95
+ // a replacement makes the getter's branch unreachable, so the captures have to go here too
96
+ issues = undefined;
97
+ ctx = undefined;
98
+ },
99
+ };
100
+ }
84
101
  const _safeParseAsync = (_Err) => async (schema, value, _ctx) => {
85
102
  const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
86
103
  let result = schema._zod.run({ value, issues: [] }, ctx);
87
104
  if (result instanceof Promise)
88
105
  result = await result;
89
- return result.issues.length
90
- ? {
91
- success: false,
92
- error: new _Err(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),
93
- }
94
- : { success: true, data: result.value };
106
+ return result.issues.length ? failure(_Err, result.issues, ctx) : { success: true, data: result.value };
95
107
  };
96
108
  exports._safeParseAsync = _safeParseAsync;
97
109
  exports.safeParseAsync = (0, exports._safeParseAsync)(errors.$ZodRealError);
@@ -101,12 +113,19 @@ const COMPILE_FALLBACK = /* @__PURE__ */ Symbol.for("zod.compile.fallback");
101
113
  // Deliberately tiny, because v8 will not inline a body carrying the fallback's object literals and throw. Everything that is not the compiled happy path lives in validateFallback, and that split is worth ~35% on a compiled schema.
102
114
  exports.validate = ((schema, value, _ctx) => {
103
115
  const validator = schema._zod.bag.validator;
104
- if (validator !== undefined && validator(value) !== COMPILE_INVALID)
105
- return true;
116
+ if (validator !== undefined) {
117
+ if (validator(value) !== COMPILE_INVALID)
118
+ return true;
119
+ // a definite sentinel means the runtime would reject, so skip the re-parse; a ctx can still change the answer
120
+ if (validator.definite === true && _ctx === undefined)
121
+ return false;
122
+ }
106
123
  return validateFallback(schema, value, _ctx);
107
124
  });
108
125
  function validateFallback(schema, value, _ctx) {
109
- const ctx = _ctx ? { ..._ctx, async: false } : { async: false };
126
+ const ctx = _ctx
127
+ ? { ..._ctx, async: false, abortEarly: true }
128
+ : { async: false, abortEarly: true };
110
129
  const fallbackRun = schema._zod.bag.fallbackRun;
111
130
  let result;
112
131
  if (fallbackRun) {
@@ -124,7 +143,9 @@ function validateFallback(schema, value, _ctx) {
124
143
  }
125
144
  // no fast path: the compiler keeps async parses on the runtime, because a promise-returning callback that is not declared async compiles to a throw
126
145
  const validateAsync = async (schema, value, _ctx) => {
127
- const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
146
+ const ctx = _ctx
147
+ ? { ..._ctx, async: true, abortEarly: true }
148
+ : { async: true, abortEarly: true };
128
149
  let result = schema._zod.run({ value, issues: [] }, ctx);
129
150
  if (result instanceof Promise)
130
151
  result = await result;
@@ -191,3 +212,22 @@ const _safeDecodeAsync = (_Err) => async (schema, value, _ctx) => {
191
212
  };
192
213
  exports._safeDecodeAsync = _safeDecodeAsync;
193
214
  exports.safeDecodeAsync = (0, exports._safeDecodeAsync)(errors.$ZodRealError);
215
+
216
+ // seal-cjs-exports
217
+ (function () {
218
+ var keys = Object.getOwnPropertyNames(exports);
219
+ for (var i = 0; i < keys.length; i++) {
220
+ var desc = Object.getOwnPropertyDescriptor(exports, keys[i]);
221
+ if (!desc || !desc.get || !desc.configurable) continue;
222
+ var value;
223
+ try {
224
+ value = desc.get();
225
+ } catch (e) {
226
+ continue;
227
+ }
228
+ // a circular require may not have settled this one yet, so leave it live
229
+ if (value === undefined) continue;
230
+ Object.defineProperty(exports, keys[i], { value: value, writable: false, enumerable: desc.enumerable, configurable: false });
231
+ }
232
+ Object.freeze(exports);
233
+ })();
package/v4/core/parse.js CHANGED
@@ -44,25 +44,37 @@ export const _safeParse = (_Err) => (schema, value, _ctx) => {
44
44
  if (result instanceof Promise) {
45
45
  throw new core.$ZodAsyncError();
46
46
  }
47
- return result.issues.length
48
- ? {
49
- success: false,
50
- error: new (_Err ?? errors.$ZodError)(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),
51
- }
52
- : { success: true, data: result.value };
47
+ return result.issues.length ? failure(_Err, result.issues, ctx) : { success: true, data: result.value };
53
48
  };
54
49
  export const safeParse = /* @__PURE__*/ _safeParse(errors.$ZodRealError);
50
+ // the error is built on the first read of `error`: finalizing the issues and constructing the instance is most of a failing parse, and a caller that only branches on `success` never pays it. a getter in the literal keeps this small; the alternative, one shared accessor descriptor plus a hidden state slot, reads ~15% faster but costs ~75 B gzipped in every bundle
51
+ function failure(Err, issues, ctx) {
52
+ let error;
53
+ return {
54
+ success: false,
55
+ get error() {
56
+ if (!error) {
57
+ error = new Err(issues.map((iss) => util.finalizeIssue(iss, ctx, core.config())));
58
+ // finalizeIssue drops `input`, so the built error holds nothing; keeping the raw issues past this point pins the parsed value for the life of the result
59
+ issues = undefined;
60
+ ctx = undefined;
61
+ }
62
+ return error;
63
+ },
64
+ set error(e) {
65
+ error = e;
66
+ // a replacement makes the getter's branch unreachable, so the captures have to go here too
67
+ issues = undefined;
68
+ ctx = undefined;
69
+ },
70
+ };
71
+ }
55
72
  export const _safeParseAsync = (_Err) => async (schema, value, _ctx) => {
56
73
  const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
57
74
  let result = schema._zod.run({ value, issues: [] }, ctx);
58
75
  if (result instanceof Promise)
59
76
  result = await result;
60
- return result.issues.length
61
- ? {
62
- success: false,
63
- error: new _Err(result.issues.map((iss) => util.finalizeIssue(iss, ctx, core.config()))),
64
- }
65
- : { success: true, data: result.value };
77
+ return result.issues.length ? failure(_Err, result.issues, ctx) : { success: true, data: result.value };
66
78
  };
67
79
  export const safeParseAsync = /* @__PURE__*/ _safeParseAsync(errors.$ZodRealError);
68
80
  // registry mirrors of the compiler's sentinels, so this module never imports the compiler
@@ -71,12 +83,19 @@ const COMPILE_FALLBACK = /* @__PURE__ */ Symbol.for("zod.compile.fallback");
71
83
  // Deliberately tiny, because v8 will not inline a body carrying the fallback's object literals and throw. Everything that is not the compiled happy path lives in validateFallback, and that split is worth ~35% on a compiled schema.
72
84
  export const validate = ((schema, value, _ctx) => {
73
85
  const validator = schema._zod.bag.validator;
74
- if (validator !== undefined && validator(value) !== COMPILE_INVALID)
75
- return true;
86
+ if (validator !== undefined) {
87
+ if (validator(value) !== COMPILE_INVALID)
88
+ return true;
89
+ // a definite sentinel means the runtime would reject, so skip the re-parse; a ctx can still change the answer
90
+ if (validator.definite === true && _ctx === undefined)
91
+ return false;
92
+ }
76
93
  return validateFallback(schema, value, _ctx);
77
94
  });
78
95
  function validateFallback(schema, value, _ctx) {
79
- const ctx = _ctx ? { ..._ctx, async: false } : { async: false };
96
+ const ctx = _ctx
97
+ ? { ..._ctx, async: false, abortEarly: true }
98
+ : { async: false, abortEarly: true };
80
99
  const fallbackRun = schema._zod.bag.fallbackRun;
81
100
  let result;
82
101
  if (fallbackRun) {
@@ -94,7 +113,9 @@ function validateFallback(schema, value, _ctx) {
94
113
  }
95
114
  // no fast path: the compiler keeps async parses on the runtime, because a promise-returning callback that is not declared async compiles to a throw
96
115
  export const validateAsync = async (schema, value, _ctx) => {
97
- const ctx = _ctx ? { ..._ctx, async: true } : { async: true };
116
+ const ctx = _ctx
117
+ ? { ..._ctx, async: true, abortEarly: true }
118
+ : { async: true, abortEarly: true };
98
119
  let result = schema._zod.run({ value, issues: [] }, ctx);
99
120
  if (result instanceof Promise)
100
121
  result = await result;