zod 3.26.0-canary.20250703T013930 → 3.26.0-canary.20250703T025502

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 (269) hide show
  1. package/package.json +20 -20
  2. package/src/index.ts +4 -0
  3. package/src/v3/ZodError.ts +330 -0
  4. package/src/v3/benchmarks/datetime.ts +58 -0
  5. package/src/v3/benchmarks/discriminatedUnion.ts +80 -0
  6. package/src/v3/benchmarks/index.ts +59 -0
  7. package/src/v3/benchmarks/ipv4.ts +57 -0
  8. package/src/v3/benchmarks/object.ts +69 -0
  9. package/src/v3/benchmarks/primitives.ts +162 -0
  10. package/src/v3/benchmarks/realworld.ts +63 -0
  11. package/src/v3/benchmarks/string.ts +55 -0
  12. package/src/v3/benchmarks/union.ts +80 -0
  13. package/src/v3/errors.ts +13 -0
  14. package/src/v3/external.ts +6 -0
  15. package/src/v3/helpers/enumUtil.ts +17 -0
  16. package/src/v3/helpers/errorUtil.ts +8 -0
  17. package/src/v3/helpers/parseUtil.ts +176 -0
  18. package/src/v3/helpers/partialUtil.ts +34 -0
  19. package/src/v3/helpers/typeAliases.ts +2 -0
  20. package/src/v3/helpers/util.ts +224 -0
  21. package/src/v3/index.ts +4 -0
  22. package/src/v3/locales/en.ts +124 -0
  23. package/src/v3/standard-schema.ts +113 -0
  24. package/src/v3/tests/Mocker.ts +54 -0
  25. package/src/v3/tests/all-errors.test.ts +157 -0
  26. package/src/v3/tests/anyunknown.test.ts +28 -0
  27. package/src/v3/tests/array.test.ts +71 -0
  28. package/src/v3/tests/async-parsing.test.ts +388 -0
  29. package/src/v3/tests/async-refinements.test.ts +46 -0
  30. package/src/v3/tests/base.test.ts +29 -0
  31. package/src/v3/tests/bigint.test.ts +55 -0
  32. package/src/v3/tests/branded.test.ts +53 -0
  33. package/src/v3/tests/catch.test.ts +220 -0
  34. package/src/v3/tests/coerce.test.ts +133 -0
  35. package/src/v3/tests/complex.test.ts +56 -0
  36. package/src/v3/tests/custom.test.ts +31 -0
  37. package/src/v3/tests/date.test.ts +32 -0
  38. package/src/v3/tests/deepmasking.test.ts +186 -0
  39. package/src/v3/tests/default.test.ts +112 -0
  40. package/src/v3/tests/description.test.ts +33 -0
  41. package/src/v3/tests/discriminated-unions.test.ts +315 -0
  42. package/src/v3/tests/enum.test.ts +80 -0
  43. package/src/v3/tests/error.test.ts +551 -0
  44. package/src/v3/tests/firstparty.test.ts +87 -0
  45. package/src/v3/tests/firstpartyschematypes.test.ts +21 -0
  46. package/src/v3/tests/function.test.ts +257 -0
  47. package/src/v3/tests/generics.test.ts +48 -0
  48. package/src/v3/tests/instanceof.test.ts +37 -0
  49. package/src/v3/tests/intersection.test.ts +110 -0
  50. package/src/v3/tests/language-server.source.ts +76 -0
  51. package/src/v3/tests/language-server.test.ts +207 -0
  52. package/src/v3/tests/literal.test.ts +36 -0
  53. package/src/v3/tests/map.test.ts +110 -0
  54. package/src/v3/tests/masking.test.ts +4 -0
  55. package/src/v3/tests/mocker.test.ts +19 -0
  56. package/src/v3/tests/nan.test.ts +21 -0
  57. package/src/v3/tests/nativeEnum.test.ts +87 -0
  58. package/src/v3/tests/nullable.test.ts +42 -0
  59. package/src/v3/tests/number.test.ts +176 -0
  60. package/src/v3/tests/object-augmentation.test.ts +29 -0
  61. package/src/v3/tests/object-in-es5-env.test.ts +29 -0
  62. package/src/v3/tests/object.test.ts +434 -0
  63. package/src/v3/tests/optional.test.ts +42 -0
  64. package/src/v3/tests/parseUtil.test.ts +23 -0
  65. package/src/v3/tests/parser.test.ts +41 -0
  66. package/src/v3/tests/partials.test.ts +243 -0
  67. package/src/v3/tests/pickomit.test.ts +111 -0
  68. package/src/v3/tests/pipeline.test.ts +29 -0
  69. package/src/v3/tests/preprocess.test.ts +186 -0
  70. package/src/v3/tests/primitive.test.ts +440 -0
  71. package/src/v3/tests/promise.test.ts +90 -0
  72. package/src/v3/tests/readonly.test.ts +194 -0
  73. package/src/v3/tests/record.test.ts +171 -0
  74. package/src/v3/tests/recursive.test.ts +197 -0
  75. package/src/v3/tests/refine.test.ts +313 -0
  76. package/src/v3/tests/safeparse.test.ts +27 -0
  77. package/src/v3/tests/set.test.ts +142 -0
  78. package/src/v3/tests/standard-schema.test.ts +83 -0
  79. package/src/v3/tests/string.test.ts +916 -0
  80. package/src/v3/tests/transformer.test.ts +233 -0
  81. package/src/v3/tests/tuple.test.ts +90 -0
  82. package/src/v3/tests/unions.test.ts +57 -0
  83. package/src/v3/tests/validations.test.ts +133 -0
  84. package/src/v3/tests/void.test.ts +15 -0
  85. package/src/v3/types.ts +5136 -0
  86. package/src/v4/classic/checks.ts +30 -0
  87. package/src/v4/classic/coerce.ts +27 -0
  88. package/src/v4/classic/compat.ts +66 -0
  89. package/src/v4/classic/errors.ts +75 -0
  90. package/src/v4/classic/external.ts +50 -0
  91. package/src/v4/classic/index.ts +5 -0
  92. package/src/v4/classic/iso.ts +90 -0
  93. package/src/v4/classic/parse.ts +33 -0
  94. package/src/v4/classic/schemas.ts +2055 -0
  95. package/src/v4/classic/tests/anyunknown.test.ts +26 -0
  96. package/src/v4/classic/tests/array.test.ts +264 -0
  97. package/src/v4/classic/tests/assignability.test.ts +210 -0
  98. package/src/v4/classic/tests/async-parsing.test.ts +381 -0
  99. package/src/v4/classic/tests/async-refinements.test.ts +68 -0
  100. package/src/v4/classic/tests/base.test.ts +7 -0
  101. package/src/v4/classic/tests/bigint.test.ts +54 -0
  102. package/src/v4/classic/tests/brand.test.ts +65 -0
  103. package/src/v4/classic/tests/catch.test.ts +252 -0
  104. package/src/v4/classic/tests/coalesce.test.ts +20 -0
  105. package/src/v4/classic/tests/coerce.test.ts +160 -0
  106. package/src/v4/classic/tests/continuability.test.ts +352 -0
  107. package/src/v4/classic/tests/custom.test.ts +40 -0
  108. package/src/v4/classic/tests/date.test.ts +31 -0
  109. package/src/v4/classic/tests/datetime.test.ts +296 -0
  110. package/src/v4/classic/tests/default.test.ts +313 -0
  111. package/src/v4/classic/tests/description.test.ts +32 -0
  112. package/src/v4/classic/tests/discriminated-unions.test.ts +592 -0
  113. package/src/v4/classic/tests/enum.test.ts +285 -0
  114. package/src/v4/classic/tests/error-utils.test.ts +527 -0
  115. package/src/v4/classic/tests/error.test.ts +711 -0
  116. package/src/v4/classic/tests/file.test.ts +91 -0
  117. package/src/v4/classic/tests/firstparty.test.ts +175 -0
  118. package/src/v4/classic/tests/function.test.ts +268 -0
  119. package/src/v4/classic/tests/generics.test.ts +72 -0
  120. package/src/v4/classic/tests/index.test.ts +829 -0
  121. package/src/v4/classic/tests/instanceof.test.ts +34 -0
  122. package/src/v4/classic/tests/intersection.test.ts +171 -0
  123. package/src/v4/classic/tests/json.test.ts +108 -0
  124. package/src/v4/classic/tests/lazy.test.ts +227 -0
  125. package/src/v4/classic/tests/literal.test.ts +92 -0
  126. package/src/v4/classic/tests/map.test.ts +196 -0
  127. package/src/v4/classic/tests/nan.test.ts +21 -0
  128. package/src/v4/classic/tests/nested-refine.test.ts +168 -0
  129. package/src/v4/classic/tests/nonoptional.test.ts +86 -0
  130. package/src/v4/classic/tests/nullable.test.ts +22 -0
  131. package/src/v4/classic/tests/number.test.ts +247 -0
  132. package/src/v4/classic/tests/object.test.ts +553 -0
  133. package/src/v4/classic/tests/optional.test.ts +103 -0
  134. package/src/v4/classic/tests/partial.test.ts +147 -0
  135. package/src/v4/classic/tests/pickomit.test.ts +127 -0
  136. package/src/v4/classic/tests/pipe.test.ts +81 -0
  137. package/src/v4/classic/tests/prefault.test.ts +37 -0
  138. package/src/v4/classic/tests/preprocess.test.ts +298 -0
  139. package/src/v4/classic/tests/primitive.test.ts +175 -0
  140. package/src/v4/classic/tests/promise.test.ts +81 -0
  141. package/src/v4/classic/tests/prototypes.test.ts +23 -0
  142. package/src/v4/classic/tests/readonly.test.ts +252 -0
  143. package/src/v4/classic/tests/record.test.ts +332 -0
  144. package/src/v4/classic/tests/recursive-types.test.ts +325 -0
  145. package/src/v4/classic/tests/refine.test.ts +423 -0
  146. package/src/v4/classic/tests/registries.test.ts +195 -0
  147. package/src/v4/classic/tests/set.test.ts +179 -0
  148. package/src/v4/classic/tests/standard-schema.test.ts +57 -0
  149. package/src/v4/classic/tests/string-formats.test.ts +109 -0
  150. package/src/v4/classic/tests/string.test.ts +881 -0
  151. package/src/v4/classic/tests/stringbool.test.ts +66 -0
  152. package/src/v4/classic/tests/template-literal.test.ts +758 -0
  153. package/src/v4/classic/tests/to-json-schema.test.ts +2182 -0
  154. package/src/v4/classic/tests/transform.test.ts +250 -0
  155. package/src/v4/classic/tests/tuple.test.ts +163 -0
  156. package/src/v4/classic/tests/union.test.ts +94 -0
  157. package/src/v4/classic/tests/validations.test.ts +283 -0
  158. package/src/v4/classic/tests/void.test.ts +12 -0
  159. package/src/v4/core/api.ts +1592 -0
  160. package/src/v4/core/checks.ts +1285 -0
  161. package/src/v4/core/config.ts +15 -0
  162. package/src/v4/core/core.ts +134 -0
  163. package/src/v4/core/doc.ts +44 -0
  164. package/src/v4/core/errors.ts +420 -0
  165. package/src/v4/core/function.ts +176 -0
  166. package/src/v4/core/index.ts +15 -0
  167. package/src/v4/core/json-schema.ts +143 -0
  168. package/src/v4/core/parse.ts +94 -0
  169. package/src/v4/core/regexes.ts +135 -0
  170. package/src/v4/core/registries.ts +86 -0
  171. package/src/v4/core/schemas.ts +3781 -0
  172. package/src/v4/core/standard-schema.ts +64 -0
  173. package/src/v4/core/tests/index.test.ts +46 -0
  174. package/src/v4/core/tests/locales/be.test.ts +124 -0
  175. package/src/v4/core/tests/locales/en.test.ts +22 -0
  176. package/src/v4/core/tests/locales/ru.test.ts +128 -0
  177. package/src/v4/core/tests/locales/tr.test.ts +69 -0
  178. package/src/v4/core/to-json-schema.ts +944 -0
  179. package/src/v4/core/util.ts +775 -0
  180. package/src/v4/core/versions.ts +5 -0
  181. package/src/v4/core/zsf.ts +323 -0
  182. package/src/v4/index.ts +4 -0
  183. package/src/v4/locales/ar.ts +125 -0
  184. package/src/v4/locales/az.ts +121 -0
  185. package/src/v4/locales/be.ts +184 -0
  186. package/src/v4/locales/ca.ts +127 -0
  187. package/src/v4/locales/cs.ts +142 -0
  188. package/src/v4/locales/de.ts +124 -0
  189. package/src/v4/locales/en.ts +127 -0
  190. package/src/v4/locales/es.ts +125 -0
  191. package/src/v4/locales/fa.ts +134 -0
  192. package/src/v4/locales/fi.ts +131 -0
  193. package/src/v4/locales/fr-CA.ts +126 -0
  194. package/src/v4/locales/fr.ts +124 -0
  195. package/src/v4/locales/he.ts +125 -0
  196. package/src/v4/locales/hu.ts +126 -0
  197. package/src/v4/locales/id.ts +125 -0
  198. package/src/v4/locales/index.ts +38 -0
  199. package/src/v4/locales/it.ts +125 -0
  200. package/src/v4/locales/ja.ts +122 -0
  201. package/src/v4/locales/kh.ts +126 -0
  202. package/src/v4/locales/ko.ts +131 -0
  203. package/src/v4/locales/mk.ts +127 -0
  204. package/src/v4/locales/ms.ts +124 -0
  205. package/src/v4/locales/nl.ts +126 -0
  206. package/src/v4/locales/no.ts +124 -0
  207. package/src/v4/locales/ota.ts +125 -0
  208. package/src/v4/locales/pl.ts +126 -0
  209. package/src/v4/locales/ps.ts +133 -0
  210. package/src/v4/locales/pt.ts +123 -0
  211. package/src/v4/locales/ru.ts +184 -0
  212. package/src/v4/locales/sl.ts +126 -0
  213. package/src/v4/locales/sv.ts +127 -0
  214. package/src/v4/locales/ta.ts +125 -0
  215. package/src/v4/locales/th.ts +126 -0
  216. package/src/v4/locales/tr.ts +121 -0
  217. package/src/v4/locales/ua.ts +126 -0
  218. package/src/v4/locales/ur.ts +126 -0
  219. package/src/v4/locales/vi.ts +125 -0
  220. package/src/v4/locales/zh-CN.ts +123 -0
  221. package/src/v4/locales/zh-TW.ts +125 -0
  222. package/src/v4/mini/checks.ts +32 -0
  223. package/src/v4/mini/coerce.ts +22 -0
  224. package/src/v4/mini/external.ts +40 -0
  225. package/src/v4/mini/index.ts +3 -0
  226. package/src/v4/mini/iso.ts +62 -0
  227. package/src/v4/mini/parse.ts +1 -0
  228. package/src/v4/mini/schemas.ts +1579 -0
  229. package/src/v4/mini/tests/assignability.test.ts +129 -0
  230. package/src/v4/mini/tests/brand.test.ts +51 -0
  231. package/src/v4/mini/tests/checks.test.ts +144 -0
  232. package/src/v4/mini/tests/computed.test.ts +36 -0
  233. package/src/v4/mini/tests/error.test.ts +22 -0
  234. package/src/v4/mini/tests/functions.test.ts +43 -0
  235. package/src/v4/mini/tests/index.test.ts +871 -0
  236. package/src/v4/mini/tests/number.test.ts +95 -0
  237. package/src/v4/mini/tests/object.test.ts +185 -0
  238. package/src/v4/mini/tests/prototypes.test.ts +43 -0
  239. package/src/v4/mini/tests/recursive-types.test.ts +275 -0
  240. package/src/v4/mini/tests/string.test.ts +293 -0
  241. package/src/v4-mini/index.ts +1 -0
  242. package/v4/classic/compat.cjs +1 -7
  243. package/v4/classic/compat.d.cts +0 -2
  244. package/v4/classic/compat.d.ts +0 -2
  245. package/v4/classic/compat.js +0 -6
  246. package/v4/classic/external.cjs +2 -1
  247. package/v4/classic/external.d.cts +1 -1
  248. package/v4/classic/external.d.ts +1 -1
  249. package/v4/classic/external.js +1 -1
  250. package/v4/classic/schemas.d.cts +4 -4
  251. package/v4/classic/schemas.d.ts +4 -4
  252. package/v4/core/api.cjs +11 -10
  253. package/v4/core/api.js +11 -10
  254. package/v4/core/checks.cjs +6 -4
  255. package/v4/core/checks.js +6 -4
  256. package/v4/core/core.cjs +5 -1
  257. package/v4/core/core.d.cts +2 -0
  258. package/v4/core/core.d.ts +2 -0
  259. package/v4/core/core.js +4 -0
  260. package/v4/core/schemas.cjs +12 -13
  261. package/v4/core/schemas.js +12 -13
  262. package/v4/core/util.cjs +3 -0
  263. package/v4/core/util.d.cts +1 -1
  264. package/v4/core/util.d.ts +1 -1
  265. package/v4/core/util.js +3 -0
  266. package/v4/mini/external.cjs +2 -1
  267. package/v4/mini/external.d.cts +1 -1
  268. package/v4/mini/external.d.ts +1 -1
  269. package/v4/mini/external.js +1 -1
@@ -0,0 +1,711 @@
1
+ import { inspect } from "node:util";
2
+ import { expect, test } from "vitest";
3
+ import * as z from "zod/v4";
4
+
5
+ test("error creation", () => {
6
+ const err1 = new z.ZodError([]);
7
+
8
+ err1.issues.push({
9
+ code: "invalid_type",
10
+ expected: "object",
11
+ path: [],
12
+ message: "",
13
+ input: "adf",
14
+ });
15
+ err1.isEmpty;
16
+
17
+ const err2 = new z.ZodError(err1.issues);
18
+ const err3 = new z.ZodError([]);
19
+ err3.addIssues(err1.issues);
20
+ err3.addIssue(err1.issues[0]);
21
+ err1.message;
22
+ err2.message;
23
+ err3.message;
24
+ });
25
+
26
+ test("do not allow error and message together", () => {
27
+ expect(() =>
28
+ z.string().refine((_) => true, {
29
+ message: "override",
30
+ error: (iss) => (iss.input === undefined ? "asdf" : null),
31
+ })
32
+ ).toThrow();
33
+ });
34
+
35
+ const errorMap: z.ZodErrorMap = (issue) => {
36
+ if (issue.code === "invalid_type") {
37
+ if (issue.expected === "string") {
38
+ return { message: "bad type!" };
39
+ }
40
+ }
41
+ if (issue.code === "custom") {
42
+ return { message: `less-than-${issue.params?.minimum}` };
43
+ }
44
+ return undefined;
45
+ };
46
+
47
+ test("type error with custom error map", () => {
48
+ const result = z.string().safeParse(234, { error: errorMap });
49
+ expect(result.success).toBe(false);
50
+ expect(result.error).toMatchInlineSnapshot(`
51
+ [ZodError: [
52
+ {
53
+ "expected": "string",
54
+ "code": "invalid_type",
55
+ "path": [],
56
+ "message": "bad type!"
57
+ }
58
+ ]]
59
+ `);
60
+ });
61
+
62
+ test("refinement fail with params", () => {
63
+ const result = z
64
+ .number()
65
+ .refine((val) => val >= 3, {
66
+ params: { minimum: 3 },
67
+ })
68
+ .safeParse(2, { error: errorMap });
69
+ expect(result.success).toBe(false);
70
+ expect(result.error).toMatchInlineSnapshot(`
71
+ [ZodError: [
72
+ {
73
+ "code": "custom",
74
+ "path": [],
75
+ "params": {
76
+ "minimum": 3
77
+ },
78
+ "message": "less-than-3"
79
+ }
80
+ ]]
81
+ `);
82
+ });
83
+
84
+ test("hard coded error with custom errormap", () => {
85
+ const result = z
86
+ .string()
87
+ .refine((val) => val.length > 12, {
88
+ params: { minimum: 13 },
89
+ message: "override",
90
+ })
91
+ .safeParse("asdf", { error: () => "contextual" });
92
+
93
+ expect(result.success).toBe(false);
94
+ expect(result.error).toMatchInlineSnapshot(`
95
+ [ZodError: [
96
+ {
97
+ "code": "custom",
98
+ "path": [],
99
+ "params": {
100
+ "minimum": 13
101
+ },
102
+ "message": "override"
103
+ }
104
+ ]]
105
+ `);
106
+ });
107
+
108
+ test("default error message", () => {
109
+ const result = z
110
+ .number()
111
+ .refine((x) => x > 3)
112
+ .safeParse(2);
113
+
114
+ expect(result.success).toBe(false);
115
+ expect(result.error).toMatchInlineSnapshot(`
116
+ [ZodError: [
117
+ {
118
+ "code": "custom",
119
+ "path": [],
120
+ "message": "Invalid input"
121
+ }
122
+ ]]
123
+ `);
124
+ });
125
+
126
+ test("override error in refine", () => {
127
+ const result = z
128
+ .number()
129
+ .refine((x) => x > 3, "override")
130
+ .safeParse(2);
131
+ expect(result.success).toBe(false);
132
+ expect(result.error!.issues.length).toEqual(1);
133
+ expect(result.error).toMatchInlineSnapshot(`
134
+ [ZodError: [
135
+ {
136
+ "code": "custom",
137
+ "path": [],
138
+ "message": "override"
139
+ }
140
+ ]]
141
+ `);
142
+ });
143
+
144
+ test("override error in refinement", () => {
145
+ const result = z
146
+ .number()
147
+ .refine((x) => x > 3, {
148
+ message: "override",
149
+ })
150
+ .safeParse(2);
151
+ expect(result.success).toBe(false);
152
+ expect(result.error!.issues.length).toEqual(1);
153
+ expect(result.error).toMatchInlineSnapshot(`
154
+ [ZodError: [
155
+ {
156
+ "code": "custom",
157
+ "path": [],
158
+ "message": "override"
159
+ }
160
+ ]]
161
+ `);
162
+ });
163
+
164
+ test("array minimum", () => {
165
+ let result = z.array(z.string()).min(3, "tooshort").safeParse(["asdf", "qwer"]);
166
+ expect(result.success).toBe(false);
167
+ expect(result.error!.issues[0].code).toEqual("too_small");
168
+ expect(result.error!.issues[0].message).toEqual("tooshort");
169
+
170
+ result = z.array(z.string()).min(3).safeParse(["asdf", "qwer"]);
171
+ expect(result.success).toBe(false);
172
+ expect(result.error!.issues[0].code).toEqual("too_small");
173
+ expect(result.error).toMatchInlineSnapshot(`
174
+ [ZodError: [
175
+ {
176
+ "origin": "array",
177
+ "code": "too_small",
178
+ "minimum": 3,
179
+ "inclusive": true,
180
+ "path": [],
181
+ "message": "Too small: expected array to have >=3 items"
182
+ }
183
+ ]]
184
+ `);
185
+ });
186
+
187
+ test("literal bigint default error message", () => {
188
+ const result = z.literal(BigInt(12)).safeParse(BigInt(13));
189
+ expect(result.success).toBe(false);
190
+ expect(result.error!.issues.length).toEqual(1);
191
+ expect(result.error).toMatchInlineSnapshot(`
192
+ [ZodError: [
193
+ {
194
+ "code": "invalid_value",
195
+ "values": [
196
+ "12"
197
+ ],
198
+ "path": [],
199
+ "message": "Invalid input: expected 12n"
200
+ }
201
+ ]]
202
+ `);
203
+ });
204
+
205
+ test("custom path in custom error map", () => {
206
+ const schema = z.object({
207
+ items: z.array(z.string()).refine((data) => data.length > 3, {
208
+ path: ["items-too-few"],
209
+ }),
210
+ });
211
+
212
+ const errorMap: z.ZodErrorMap = (issue) => {
213
+ expect((issue.path ?? []).length).toBe(2);
214
+ return { message: "doesnt matter" };
215
+ };
216
+ const result = schema.safeParse({ items: ["first"] }, { error: errorMap });
217
+ expect(result.success).toBe(false);
218
+ expect(result.error).toMatchInlineSnapshot(`
219
+ [ZodError: [
220
+ {
221
+ "code": "custom",
222
+ "path": [
223
+ "items",
224
+ "items-too-few"
225
+ ],
226
+ "message": "doesnt matter"
227
+ }
228
+ ]]
229
+ `);
230
+ });
231
+
232
+ // test("error metadata from value", () => {
233
+ // const dynamicRefine = z.string().refine(
234
+ // (val) => val === val.toUpperCase(),
235
+ // (val) => ({ params: { val } })
236
+ // );
237
+
238
+ // const result = dynamicRefine.safeParse("asdf");
239
+ // expect(result.success).toEqual(false);
240
+ // if (!result.success) {
241
+ // const sub = result.error.issues[0];
242
+ // expect(result.error.issues[0].code).toEqual("custom");
243
+ // if (sub.code === "custom") {
244
+ // expect(sub.params?.val).toEqual("asdf");
245
+ // }
246
+ // }
247
+ // });
248
+
249
+ // test("don't call refine after validation failed", () => {
250
+ // const asdf = z
251
+ // .union([
252
+ // z.number(),
253
+ // z.string().transform(z.number(), (val) => {
254
+ // return parseFloat(val);
255
+ // }),
256
+ // ])
257
+ // .refine((v) => v >= 1);
258
+
259
+ // expect(() => asdf.safeParse("foo")).not.toThrow();
260
+ // });
261
+
262
+ test("root level formatting", () => {
263
+ const schema = z.string().email();
264
+ const result = schema.safeParse("asdfsdf");
265
+ expect(result.success).toBe(false);
266
+
267
+ expect(result.error!.format()).toMatchInlineSnapshot(`
268
+ {
269
+ "_errors": [
270
+ "Invalid email address",
271
+ ],
272
+ }
273
+ `);
274
+ });
275
+
276
+ test("custom path", () => {
277
+ const schema = z
278
+ .object({
279
+ password: z.string(),
280
+ confirm: z.string(),
281
+ })
282
+ .refine((val) => val.confirm === val.password, { path: ["confirm"] });
283
+
284
+ const result = schema.safeParse({
285
+ password: "peanuts",
286
+ confirm: "qeanuts",
287
+ });
288
+
289
+ expect(result.success).toBe(false);
290
+ const error = result.error!.format();
291
+ expect(error._errors).toEqual([]);
292
+ expect(error.password?._errors).toEqual(undefined);
293
+ expect(error.confirm?._errors).toEqual(["Invalid input"]);
294
+ });
295
+
296
+ test("custom path", () => {
297
+ const schema = z
298
+ .object({
299
+ password: z.string().min(6),
300
+ confirm: z.string().min(6),
301
+ })
302
+ .refine((val) => val.confirm === val.password);
303
+
304
+ const result = schema.safeParse({
305
+ password: "qwer",
306
+ confirm: "asdf",
307
+ });
308
+
309
+ expect(result.success).toBe(false);
310
+ expect(result.error!.issues.length).toEqual(3);
311
+ });
312
+
313
+ const schema = z.object({
314
+ inner: z.object({
315
+ name: z
316
+ .string()
317
+ .refine((val) => val.length > 5)
318
+ .array()
319
+ .refine((val) => val.length <= 1),
320
+ }),
321
+ });
322
+
323
+ test("no abort early on refinements", () => {
324
+ const invalidItem = {
325
+ inner: { name: ["aasd", "asdfasdfasfd"] },
326
+ };
327
+
328
+ const result1 = schema.safeParse(invalidItem);
329
+ expect(result1.success).toBe(false);
330
+ expect(result1.error!.issues.length).toEqual(2);
331
+ });
332
+
333
+ test("detect issue with input fallback", () => {
334
+ const schema = z
335
+ .string()
336
+ .transform((val) => val.length)
337
+ .refine(() => false, { message: "always fails" })
338
+ .refine(
339
+ (val) => {
340
+ if (typeof val !== "number") throw new Error();
341
+ return (val ^ 2) > 10;
342
+ } // should be number but it's a string
343
+ );
344
+ expect(() => schema.parse("hello")).toThrow(z.ZodError);
345
+ });
346
+
347
+ test("formatting", () => {
348
+ const invalidItem = {
349
+ inner: { name: ["aasd", "asdfasdfasfd"] },
350
+ };
351
+ const invalidArray = {
352
+ inner: { name: ["asdfasdf", "asdfasdfasfd"] },
353
+ };
354
+ const result1 = schema.safeParse(invalidItem);
355
+ const result2 = schema.safeParse(invalidArray);
356
+
357
+ expect(result1.success).toBe(false);
358
+ expect(result2.success).toBe(false);
359
+ const error1 = result1.error!.format();
360
+ expect(error1._errors).toEqual([]);
361
+ expect(error1.inner?._errors).toEqual([]);
362
+ expect(error1.inner?.name?.[1]).toEqual(undefined);
363
+
364
+ type FormattedError = z.inferFormattedError<typeof schema>;
365
+ const error2: FormattedError = result2.error!.format();
366
+ expect(error2._errors).toEqual([]);
367
+ expect(error2.inner?._errors).toEqual([]);
368
+ expect(error2.inner?.name?._errors).toEqual(["Invalid input"]);
369
+ expect(error2.inner?.name?.[0]).toEqual(undefined);
370
+ expect(error2.inner?.name?.[1]).toEqual(undefined);
371
+ expect(error2.inner?.name?.[2]).toEqual(undefined);
372
+
373
+ // test custom mapper
374
+ type FormattedErrorWithNumber = z.inferFormattedError<typeof schema, number>;
375
+ const errorWithNumber: FormattedErrorWithNumber = result2.error!.format(() => 5);
376
+ expect(errorWithNumber._errors).toEqual([]);
377
+ expect(errorWithNumber.inner?._errors).toEqual([]);
378
+ expect(errorWithNumber.inner?.name?._errors).toEqual([5]);
379
+ });
380
+
381
+ test("formatting with nullable and optional fields", () => {
382
+ const nameSchema = z.string().refine((val) => val.length > 5);
383
+ const schema = z.object({
384
+ nullableObject: z.object({ name: nameSchema }).nullable(),
385
+ nullableArray: z.array(nameSchema).nullable(),
386
+ nullableTuple: z.tuple([nameSchema, nameSchema, z.number()]).nullable(),
387
+ optionalObject: z.object({ name: nameSchema }).optional(),
388
+ optionalArray: z.array(nameSchema).optional(),
389
+ optionalTuple: z.tuple([nameSchema, nameSchema, z.number()]).optional(),
390
+ });
391
+ const invalidItem = {
392
+ nullableObject: { name: "abcd" },
393
+ nullableArray: ["abcd"],
394
+ nullableTuple: ["abcd", "abcd", 1],
395
+ optionalObject: { name: "abcd" },
396
+ optionalArray: ["abcd"],
397
+ optionalTuple: ["abcd", "abcd", 1],
398
+ };
399
+ const result = schema.safeParse(invalidItem);
400
+ expect(result.success).toBe(false);
401
+ const error: z.inferFormattedError<typeof schema> = result.error!.format();
402
+ expect(error._errors).toEqual([]);
403
+ expect(error.nullableObject?._errors).toEqual([]);
404
+ expect(error.nullableObject?.name?._errors).toEqual(["Invalid input"]);
405
+ expect(error.nullableArray?._errors).toEqual([]);
406
+ expect(error.nullableArray?.[0]?._errors).toEqual(["Invalid input"]);
407
+ expect(error.nullableTuple?._errors).toEqual([]);
408
+ expect(error.nullableTuple?.[0]?._errors).toEqual(["Invalid input"]);
409
+ expect(error.nullableTuple?.[1]?._errors).toEqual(["Invalid input"]);
410
+ expect(error.optionalObject?._errors).toEqual([]);
411
+ expect(error.optionalObject?.name?._errors).toEqual(["Invalid input"]);
412
+ expect(error.optionalArray?._errors).toEqual([]);
413
+ expect(error.optionalArray?.[0]?._errors).toEqual(["Invalid input"]);
414
+ expect(error.optionalTuple?._errors).toEqual([]);
415
+ expect(error.optionalTuple?.[0]?._errors).toEqual(["Invalid input"]);
416
+ expect(error.optionalTuple?.[1]?._errors).toEqual(["Invalid input"]);
417
+
418
+ expect(error).toMatchInlineSnapshot(`
419
+ {
420
+ "_errors": [],
421
+ "nullableArray": {
422
+ "0": {
423
+ "_errors": [
424
+ "Invalid input",
425
+ ],
426
+ },
427
+ "_errors": [],
428
+ },
429
+ "nullableObject": {
430
+ "_errors": [],
431
+ "name": {
432
+ "_errors": [
433
+ "Invalid input",
434
+ ],
435
+ },
436
+ },
437
+ "nullableTuple": {
438
+ "0": {
439
+ "_errors": [
440
+ "Invalid input",
441
+ ],
442
+ },
443
+ "1": {
444
+ "_errors": [
445
+ "Invalid input",
446
+ ],
447
+ },
448
+ "_errors": [],
449
+ },
450
+ "optionalArray": {
451
+ "0": {
452
+ "_errors": [
453
+ "Invalid input",
454
+ ],
455
+ },
456
+ "_errors": [],
457
+ },
458
+ "optionalObject": {
459
+ "_errors": [],
460
+ "name": {
461
+ "_errors": [
462
+ "Invalid input",
463
+ ],
464
+ },
465
+ },
466
+ "optionalTuple": {
467
+ "0": {
468
+ "_errors": [
469
+ "Invalid input",
470
+ ],
471
+ },
472
+ "1": {
473
+ "_errors": [
474
+ "Invalid input",
475
+ ],
476
+ },
477
+ "_errors": [],
478
+ },
479
+ }
480
+ `);
481
+ });
482
+
483
+ test("inferFlattenedErrors", () => {
484
+ const schemaWithTransform = z.object({ foo: z.string() }).transform((o) => ({ bar: o.foo }));
485
+
486
+ const result = schemaWithTransform.safeParse({});
487
+
488
+ expect(result.success).toBe(false);
489
+ type ValidationErrors = z.inferFlattenedErrors<typeof schemaWithTransform>;
490
+ const error: ValidationErrors = result.error!.flatten();
491
+ expect(error).toMatchInlineSnapshot(`
492
+ {
493
+ "fieldErrors": {
494
+ "foo": [
495
+ "Invalid input: expected string, received undefined",
496
+ ],
497
+ },
498
+ "formErrors": [],
499
+ }
500
+ `);
501
+ });
502
+
503
+ const stringWithCustomError = z.string({
504
+ error: () => "bound",
505
+ });
506
+
507
+ test("schema-bound error map", () => {
508
+ const result = stringWithCustomError.safeParse(1234);
509
+ expect(result.success).toBe(false);
510
+ expect(result.error!.issues[0].message).toEqual("bound");
511
+ });
512
+
513
+ test("bound error map overrides contextual", () => {
514
+ // support contextual override
515
+ const result = stringWithCustomError.safeParse(undefined, {
516
+ error: () => ({ message: "override" }),
517
+ });
518
+ expect(result.success).toBe(false);
519
+ expect(result.error!.issues[0].message).toEqual("bound");
520
+ });
521
+
522
+ test("z.config customError ", () => {
523
+ // support overrideErrorMap
524
+
525
+ z.config({ customError: () => ({ message: "override" }) });
526
+ const result = stringWithCustomError.min(10).safeParse("tooshort");
527
+ expect(result.success).toBe(false);
528
+ expect(result.error).toMatchInlineSnapshot(`
529
+ [ZodError: [
530
+ {
531
+ "origin": "string",
532
+ "code": "too_small",
533
+ "minimum": 10,
534
+ "inclusive": true,
535
+ "path": [],
536
+ "message": "override"
537
+ }
538
+ ]]
539
+ `);
540
+ expect(result.error!.issues[0].message).toEqual("override");
541
+ z.config({ customError: undefined });
542
+ });
543
+
544
+ // test("invalid and required", () => {
545
+ // const str = z.string({
546
+ // invalid_type_error: "Invalid name",
547
+ // required_error: "Name is required",
548
+ // });
549
+ // const result1 = str.safeParse(1234);
550
+ // expect(result1.success).toBe(false);
551
+ // if (!result1.success) {
552
+ // expect(result1.error.issues[0].message).toEqual("Invalid name");
553
+ // }
554
+ // const result2 = str.safeParse(undefined);
555
+ // expect(result2.success).toBe(false);
556
+ // if (!result2.success) {
557
+ // expect(result2.error.issues[0].message).toEqual("Name is required");
558
+ // }
559
+ // });
560
+
561
+ // test("Fallback to default required error", () => {
562
+ // const str = z.string({
563
+ // invalid_type_error: "Invalid name",
564
+ // // required_error: "Name is required",
565
+ // });
566
+
567
+ // const result2 = str.safeParse(undefined);
568
+ // expect(result2.success).toBe(false);
569
+ // if (!result2.success) {
570
+ // expect(result2.error.issues[0].message).toEqual("Required");
571
+ // }
572
+ // });
573
+
574
+ // test("invalid and required and errorMap", () => {
575
+ // expect(() => {
576
+ // return z.string({
577
+ // invalid_type_error: "Invalid name",
578
+ // required_error: "Name is required",
579
+ // errorMap: () => ({ message: "override" }),
580
+ // });
581
+ // }).toThrow();
582
+ // });
583
+
584
+ // test("strict error message", () => {
585
+ // const errorMsg = "Invalid object";
586
+ // const obj = z.object({ x: z.string() }).strict(errorMsg);
587
+ // const result = obj.safeParse({ x: "a", y: "b" });
588
+ // expect(result.success).toBe(false);
589
+ // if (!result.success) {
590
+ // expect(result.error.issues[0].message).toEqual(errorMsg);
591
+ // }
592
+ // });
593
+
594
+ test("empty string error message", () => {
595
+ const schema = z.string().max(1, { message: "" });
596
+ const result = schema.safeParse("asdf");
597
+ expect(result.success).toBe(false);
598
+ expect(result.error!.issues[0].message).toEqual("");
599
+ });
600
+
601
+ test("dont short circuit on continuable errors", () => {
602
+ const user = z
603
+ .object({
604
+ password: z.string().min(6),
605
+ confirm: z.string(),
606
+ })
607
+ .refine((data) => data.password === data.confirm, {
608
+ message: "Passwords don't match",
609
+ path: ["confirm"],
610
+ });
611
+ const result = user.safeParse({ password: "asdf", confirm: "qwer" });
612
+ expect(result.success).toBe(false);
613
+ expect(result.error).toMatchInlineSnapshot(`
614
+ [ZodError: [
615
+ {
616
+ "origin": "string",
617
+ "code": "too_small",
618
+ "minimum": 6,
619
+ "inclusive": true,
620
+ "path": [
621
+ "password"
622
+ ],
623
+ "message": "Too small: expected string to have >=6 characters"
624
+ },
625
+ {
626
+ "code": "custom",
627
+ "path": [
628
+ "confirm"
629
+ ],
630
+ "message": "Passwords don't match"
631
+ }
632
+ ]]
633
+ `);
634
+ // expect(result.error!.issues.length).toEqual(2);
635
+ });
636
+
637
+ test("string error params", () => {
638
+ const a = z.string("Bad!");
639
+ expect(a.safeParse(123).error!.issues[0].message).toBe("Bad!");
640
+
641
+ const b = z.string().min(5, "Too short!");
642
+ expect(b.safeParse("abc").error!.issues[0].message).toBe("Too short!");
643
+
644
+ const c = z.uuid("Bad UUID!");
645
+ expect(c.safeParse("not-a-uuid").error!.issues[0].message).toBe("Bad UUID!");
646
+
647
+ const d = z.string().datetime({ message: "Bad date!" });
648
+ expect(d.safeParse("not-a-date").error!.issues[0].message).toBe("Bad date!");
649
+
650
+ const e = z.array(z.string(), "Bad array!");
651
+ expect(e.safeParse("not-an-array").error!.issues[0].message).toBe("Bad array!");
652
+
653
+ const f = z.array(z.string()).min(5, "Too few items!");
654
+ expect(f.safeParse(["a", "b"]).error!.issues[0].message).toBe("Too few items!");
655
+
656
+ const g = z.set(z.string(), "Bad set!");
657
+ expect(g.safeParse("not-a-set").error!.issues[0].message).toBe("Bad set!");
658
+
659
+ const h = z.array(z.string(), "Bad array!");
660
+ expect(h.safeParse(123).error!.issues[0].message).toBe("Bad array!");
661
+
662
+ const i = z.set(z.string(), "Bad set!");
663
+ expect(i.safeParse(123).error!.issues[0].message).toBe("Bad set!");
664
+
665
+ const j = z.array(z.string(), "Bad array!");
666
+ expect(j.safeParse(null).error!.issues[0].message).toBe("Bad array!");
667
+ });
668
+
669
+ test("error inheritance", () => {
670
+ const e1 = z.string().safeParse(123).error!;
671
+ expect(e1).toBeInstanceOf(z.core.$ZodError);
672
+ expect(e1).toBeInstanceOf(z.ZodError);
673
+ expect(e1).toBeInstanceOf(z.ZodRealError);
674
+ // expect(e1).not.toBeInstanceOf(Error);
675
+
676
+ try {
677
+ z.string().parse(123);
678
+ } catch (e2) {
679
+ expect(e1).toBeInstanceOf(z.core.$ZodError);
680
+ expect(e2).toBeInstanceOf(z.ZodError);
681
+ expect(e2).toBeInstanceOf(z.ZodRealError);
682
+ // expect(e2).toBeInstanceOf(Error);
683
+ }
684
+ });
685
+
686
+ test("error serialization", () => {
687
+ try {
688
+ z.string().parse(123);
689
+ } catch (e) {
690
+ expect(e).toMatchInlineSnapshot(`
691
+ [ZodError: [
692
+ {
693
+ "expected": "string",
694
+ "code": "invalid_type",
695
+ "path": [],
696
+ "message": "Invalid input: expected string, received number"
697
+ }
698
+ ]]
699
+ `);
700
+ expect(inspect(e).split("\n").slice(0, 8).join("\n")).toMatchInlineSnapshot(`
701
+ "ZodError: [
702
+ {
703
+ "expected": "string",
704
+ "code": "invalid_type",
705
+ "path": [],
706
+ "message": "Invalid input: expected string, received number"
707
+ }
708
+ ]"
709
+ `);
710
+ }
711
+ });