zod 3.26.0-canary.20250703T011142 → 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 +4 -4
  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,2182 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import * as z from "zod/v4";
3
+ // import * as zCore from "zod/v4/core";
4
+
5
+ describe("toJSONSchema", () => {
6
+ test("primitive types", () => {
7
+ expect(z.toJSONSchema(z.string())).toMatchInlineSnapshot(`
8
+ {
9
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
10
+ "type": "string",
11
+ }
12
+ `);
13
+ expect(z.toJSONSchema(z.number())).toMatchInlineSnapshot(`
14
+ {
15
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
16
+ "type": "number",
17
+ }
18
+ `);
19
+ expect(z.toJSONSchema(z.boolean())).toMatchInlineSnapshot(`
20
+ {
21
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
22
+ "type": "boolean",
23
+ }
24
+ `);
25
+ expect(z.toJSONSchema(z.null())).toMatchInlineSnapshot(`
26
+ {
27
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
28
+ "type": "null",
29
+ }
30
+ `);
31
+ expect(z.toJSONSchema(z.undefined())).toMatchInlineSnapshot(`
32
+ {
33
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
34
+ "type": "null",
35
+ }
36
+ `);
37
+ expect(z.toJSONSchema(z.any())).toMatchInlineSnapshot(`
38
+ {
39
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
40
+ }
41
+ `);
42
+ expect(z.toJSONSchema(z.unknown())).toMatchInlineSnapshot(`
43
+ {
44
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
45
+ }
46
+ `);
47
+ expect(z.toJSONSchema(z.never())).toMatchInlineSnapshot(`
48
+ {
49
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
50
+ "not": {},
51
+ }
52
+ `);
53
+ expect(z.toJSONSchema(z.email())).toMatchInlineSnapshot(`
54
+ {
55
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
56
+ "format": "email",
57
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
58
+ "type": "string",
59
+ }
60
+ `);
61
+ expect(z.toJSONSchema(z.iso.datetime())).toMatchInlineSnapshot(`
62
+ {
63
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
64
+ "format": "date-time",
65
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
66
+ "type": "string",
67
+ }
68
+ `);
69
+ expect(z.toJSONSchema(z.iso.date())).toMatchInlineSnapshot(`
70
+ {
71
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
72
+ "format": "date",
73
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
74
+ "type": "string",
75
+ }
76
+ `);
77
+ expect(z.toJSONSchema(z.iso.time())).toMatchInlineSnapshot(`
78
+ {
79
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
80
+ "format": "time",
81
+ "pattern": "^(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?$",
82
+ "type": "string",
83
+ }
84
+ `);
85
+ expect(z.toJSONSchema(z.iso.duration())).toMatchInlineSnapshot(`
86
+ {
87
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
88
+ "format": "duration",
89
+ "pattern": "^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$",
90
+ "type": "string",
91
+ }
92
+ `);
93
+ expect(z.toJSONSchema(z.ipv4())).toMatchInlineSnapshot(`
94
+ {
95
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
96
+ "format": "ipv4",
97
+ "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$",
98
+ "type": "string",
99
+ }
100
+ `);
101
+ expect(z.toJSONSchema(z.ipv6())).toMatchInlineSnapshot(`
102
+ {
103
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
104
+ "format": "ipv6",
105
+ "pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})$",
106
+ "type": "string",
107
+ }
108
+ `);
109
+ expect(z.toJSONSchema(z.uuid())).toMatchInlineSnapshot(`
110
+ {
111
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
112
+ "format": "uuid",
113
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$",
114
+ "type": "string",
115
+ }
116
+ `);
117
+ expect(z.toJSONSchema(z.guid())).toMatchInlineSnapshot(`
118
+ {
119
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
120
+ "format": "uuid",
121
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$",
122
+ "type": "string",
123
+ }
124
+ `);
125
+ expect(z.toJSONSchema(z.url())).toMatchInlineSnapshot(`
126
+ {
127
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
128
+ "format": "uri",
129
+ "type": "string",
130
+ }
131
+ `);
132
+ expect(z.toJSONSchema(z.base64())).toMatchInlineSnapshot(`
133
+ {
134
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
135
+ "contentEncoding": "base64",
136
+ "format": "base64",
137
+ "pattern": "^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$",
138
+ "type": "string",
139
+ }
140
+ `);
141
+ expect(z.toJSONSchema(z.cuid())).toMatchInlineSnapshot(`
142
+ {
143
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
144
+ "format": "cuid",
145
+ "pattern": "^[cC][^\\s-]{8,}$",
146
+ "type": "string",
147
+ }
148
+ `);
149
+ // expect(z.toJSONSchema(z.regex(/asdf/))).toMatchInlineSnapshot();
150
+ expect(z.toJSONSchema(z.emoji())).toMatchInlineSnapshot(`
151
+ {
152
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
153
+ "format": "emoji",
154
+ "pattern": "^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$",
155
+ "type": "string",
156
+ }
157
+ `);
158
+ expect(z.toJSONSchema(z.nanoid())).toMatchInlineSnapshot(`
159
+ {
160
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
161
+ "format": "nanoid",
162
+ "pattern": "^[a-zA-Z0-9_-]{21}$",
163
+ "type": "string",
164
+ }
165
+ `);
166
+ expect(z.toJSONSchema(z.cuid2())).toMatchInlineSnapshot(`
167
+ {
168
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
169
+ "format": "cuid2",
170
+ "pattern": "^[0-9a-z]+$",
171
+ "type": "string",
172
+ }
173
+ `);
174
+ expect(z.toJSONSchema(z.ulid())).toMatchInlineSnapshot(`
175
+ {
176
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
177
+ "format": "ulid",
178
+ "pattern": "^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$",
179
+ "type": "string",
180
+ }
181
+ `);
182
+ // expect(z.toJSONSchema(z.cidr())).toMatchInlineSnapshot();
183
+ expect(z.toJSONSchema(z.number())).toMatchInlineSnapshot(`
184
+ {
185
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
186
+ "type": "number",
187
+ }
188
+ `);
189
+ expect(z.toJSONSchema(z.int())).toMatchInlineSnapshot(`
190
+ {
191
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
192
+ "maximum": 9007199254740991,
193
+ "minimum": -9007199254740991,
194
+ "type": "integer",
195
+ }
196
+ `);
197
+ expect(z.toJSONSchema(z.int32())).toMatchInlineSnapshot(`
198
+ {
199
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
200
+ "maximum": 2147483647,
201
+ "minimum": -2147483648,
202
+ "type": "integer",
203
+ }
204
+ `);
205
+ expect(z.toJSONSchema(z.float32())).toMatchInlineSnapshot(`
206
+ {
207
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
208
+ "maximum": 3.4028234663852886e+38,
209
+ "minimum": -3.4028234663852886e+38,
210
+ "type": "number",
211
+ }
212
+ `);
213
+ expect(z.toJSONSchema(z.float64())).toMatchInlineSnapshot(`
214
+ {
215
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
216
+ "maximum": 1.7976931348623157e+308,
217
+ "minimum": -1.7976931348623157e+308,
218
+ "type": "number",
219
+ }
220
+ `);
221
+ expect(z.toJSONSchema(z.jwt())).toMatchInlineSnapshot(`
222
+ {
223
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
224
+ "format": "jwt",
225
+ "type": "string",
226
+ }
227
+ `);
228
+ });
229
+
230
+ test("unsupported schema types", () => {
231
+ expect(() => z.toJSONSchema(z.bigint())).toThrow("BigInt cannot be represented in JSON Schema");
232
+ expect(() => z.toJSONSchema(z.int64())).toThrow("BigInt cannot be represented in JSON Schema");
233
+ expect(() => z.toJSONSchema(z.symbol())).toThrow("Symbols cannot be represented in JSON Schema");
234
+ expect(() => z.toJSONSchema(z.void())).toThrow("Void cannot be represented in JSON Schema");
235
+ expect(() => z.toJSONSchema(z.date())).toThrow("Date cannot be represented in JSON Schema");
236
+ expect(() => z.toJSONSchema(z.map(z.string(), z.number()))).toThrow("Map cannot be represented in JSON Schema");
237
+ expect(() => z.toJSONSchema(z.set(z.string()))).toThrow("Set cannot be represented in JSON Schema");
238
+ expect(() => z.toJSONSchema(z.custom(() => true))).toThrow("Custom types cannot be represented in JSON Schema");
239
+
240
+ // Transform
241
+ const transformSchema = z.string().transform((val) => Number.parseInt(val));
242
+ expect(() => z.toJSONSchema(transformSchema)).toThrow("Transforms cannot be represented in JSON Schema");
243
+
244
+ // Static catch values
245
+ const staticCatchSchema = z.string().catch(() => "sup");
246
+ expect(z.toJSONSchema(staticCatchSchema)).toMatchInlineSnapshot(`
247
+ {
248
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
249
+ "default": "sup",
250
+ "type": "string",
251
+ }
252
+ `);
253
+
254
+ // Dynamic catch values
255
+ const dynamicCatchSchema = z.string().catch((ctx) => `${ctx.issues.length}`);
256
+ expect(() => z.toJSONSchema(dynamicCatchSchema)).toThrow("Dynamic catch values are not supported in JSON Schema");
257
+ });
258
+
259
+ test("string formats", () => {
260
+ expect(z.toJSONSchema(z.string().email())).toMatchInlineSnapshot(`
261
+ {
262
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
263
+ "format": "email",
264
+ "pattern": "^(?!\\.)(?!.*\\.\\.)([A-Za-z0-9_'+\\-\\.]*)[A-Za-z0-9_+-]@([A-Za-z0-9][A-Za-z0-9\\-]*\\.)+[A-Za-z]{2,}$",
265
+ "type": "string",
266
+ }
267
+ `);
268
+ expect(z.toJSONSchema(z.string().uuid())).toMatchInlineSnapshot(`
269
+ {
270
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
271
+ "format": "uuid",
272
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$",
273
+ "type": "string",
274
+ }
275
+ `);
276
+ expect(z.toJSONSchema(z.iso.datetime())).toMatchInlineSnapshot(`
277
+ {
278
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
279
+ "format": "date-time",
280
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))T(?:(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?(?:Z))$",
281
+ "type": "string",
282
+ }
283
+ `);
284
+
285
+ expect(z.toJSONSchema(z.iso.date())).toMatchInlineSnapshot(`
286
+ {
287
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
288
+ "format": "date",
289
+ "pattern": "^(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))$",
290
+ "type": "string",
291
+ }
292
+ `);
293
+ expect(z.toJSONSchema(z.iso.time())).toMatchInlineSnapshot(`
294
+ {
295
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
296
+ "format": "time",
297
+ "pattern": "^(?:[01]\\d|2[0-3]):[0-5]\\d(?::[0-5]\\d(?:\\.\\d+)?)?$",
298
+ "type": "string",
299
+ }
300
+ `);
301
+ expect(z.toJSONSchema(z.iso.duration())).toMatchInlineSnapshot(`
302
+ {
303
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
304
+ "format": "duration",
305
+ "pattern": "^P(?:(\\d+W)|(?!.*W)(?=\\d|T\\d)(\\d+Y)?(\\d+M)?(\\d+D)?(T(?=\\d)(\\d+H)?(\\d+M)?(\\d+([.,]\\d+)?S)?)?)$",
306
+ "type": "string",
307
+ }
308
+ `);
309
+ // expect(z.toJSONSchema(z.string().ip())).toMatchInlineSnapshot(`
310
+ // {
311
+ // "pattern": /\\(\\^\\(\\?:\\(\\?:25\\[0-5\\]\\|2\\[0-4\\]\\[0-9\\]\\|1\\[0-9\\]\\[0-9\\]\\|\\[1-9\\]\\[0-9\\]\\|\\[0-9\\]\\)\\\\\\.\\)\\{3\\}\\(\\?:25\\[0-5\\]\\|2\\[0-4\\]\\[0-9\\]\\|1\\[0-9\\]\\[0-9\\]\\|\\[1-9\\]\\[0-9\\]\\|\\[0-9\\]\\)\\$\\)\\|\\(\\^\\(\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{7\\}\\|::\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,6\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{1\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,5\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{2\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,4\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{3\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,3\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{4\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,2\\}\\|\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{5\\}:\\(\\[a-fA-F0-9\\]\\{1,4\\}:\\)\\{0,1\\}\\)\\(\\[a-fA-F0-9\\]\\{1,4\\}\\|\\(\\(\\(25\\[0-5\\]\\)\\|\\(2\\[0-4\\]\\[0-9\\]\\)\\|\\(1\\[0-9\\]\\{2\\}\\)\\|\\(\\[0-9\\]\\{1,2\\}\\)\\)\\\\\\.\\)\\{3\\}\\(\\(25\\[0-5\\]\\)\\|\\(2\\[0-4\\]\\[0-9\\]\\)\\|\\(1\\[0-9\\]\\{2\\}\\)\\|\\(\\[0-9\\]\\{1,2\\}\\)\\)\\)\\$\\)/,
312
+ // "type": "string",
313
+ // }
314
+ // `);
315
+ expect(z.toJSONSchema(z.ipv4())).toMatchInlineSnapshot(`
316
+ {
317
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
318
+ "format": "ipv4",
319
+ "pattern": "^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$",
320
+ "type": "string",
321
+ }
322
+ `);
323
+
324
+ expect(z.toJSONSchema(z.ipv6())).toMatchInlineSnapshot(`
325
+ {
326
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
327
+ "format": "ipv6",
328
+ "pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})$",
329
+ "type": "string",
330
+ }
331
+ `);
332
+
333
+ expect(z.toJSONSchema(z.base64())).toMatchInlineSnapshot(`
334
+ {
335
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
336
+ "contentEncoding": "base64",
337
+ "format": "base64",
338
+ "pattern": "^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==)|(?:[0-9a-zA-Z+/]{3}=))?$",
339
+ "type": "string",
340
+ }
341
+ `);
342
+ expect(z.toJSONSchema(z.url())).toMatchInlineSnapshot(`
343
+ {
344
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
345
+ "format": "uri",
346
+ "type": "string",
347
+ }
348
+ `);
349
+ expect(z.toJSONSchema(z.guid())).toMatchInlineSnapshot(`
350
+ {
351
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
352
+ "format": "uuid",
353
+ "pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$",
354
+ "type": "string",
355
+ }
356
+ `);
357
+ expect(z.toJSONSchema(z.string().regex(/asdf/))).toMatchInlineSnapshot(`
358
+ {
359
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
360
+ "pattern": "asdf",
361
+ "type": "string",
362
+ }
363
+ `);
364
+ });
365
+
366
+ test("string patterns", () => {
367
+ expect(
368
+ z.toJSONSchema(
369
+ z
370
+ .string()
371
+ .startsWith("hello")
372
+ .includes("cruel")
373
+ .includes("dark", { position: 10 })
374
+ .endsWith("world")
375
+ .regex(/stuff/)
376
+ )
377
+ ).toMatchInlineSnapshot(`
378
+ {
379
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
380
+ "allOf": [
381
+ {
382
+ "pattern": "^hello.*",
383
+ },
384
+ {
385
+ "pattern": "cruel",
386
+ },
387
+ {
388
+ "pattern": "^.{10}dark",
389
+ },
390
+ {
391
+ "pattern": ".*world$",
392
+ },
393
+ {
394
+ "pattern": "stuff",
395
+ },
396
+ ],
397
+ "type": "string",
398
+ }
399
+ `);
400
+
401
+ expect(
402
+ z.toJSONSchema(
403
+ z
404
+ .string()
405
+ .startsWith("hello")
406
+ .includes("cruel")
407
+ .includes("dark", { position: 10 })
408
+ .endsWith("world")
409
+ .regex(/stuff/),
410
+ {
411
+ target: "draft-7",
412
+ }
413
+ )
414
+ ).toMatchInlineSnapshot(`
415
+ {
416
+ "$schema": "http://json-schema.org/draft-07/schema#",
417
+ "allOf": [
418
+ {
419
+ "pattern": "^hello.*",
420
+ "type": "string",
421
+ },
422
+ {
423
+ "pattern": "cruel",
424
+ "type": "string",
425
+ },
426
+ {
427
+ "pattern": "^.{10}dark",
428
+ "type": "string",
429
+ },
430
+ {
431
+ "pattern": ".*world$",
432
+ "type": "string",
433
+ },
434
+ {
435
+ "pattern": "stuff",
436
+ "type": "string",
437
+ },
438
+ ],
439
+ "type": "string",
440
+ }
441
+ `);
442
+ });
443
+
444
+ test("number constraints", () => {
445
+ expect(z.toJSONSchema(z.number().min(5).max(10))).toMatchInlineSnapshot(
446
+ `
447
+ {
448
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
449
+ "maximum": 10,
450
+ "minimum": 5,
451
+ "type": "number",
452
+ }
453
+ `
454
+ );
455
+
456
+ expect(z.toJSONSchema(z.number().gt(5).gt(10))).toMatchInlineSnapshot(`
457
+ {
458
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
459
+ "exclusiveMinimum": 10,
460
+ "type": "number",
461
+ }
462
+ `);
463
+
464
+ expect(z.toJSONSchema(z.number().gt(5).gte(10))).toMatchInlineSnapshot(`
465
+ {
466
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
467
+ "minimum": 10,
468
+ "type": "number",
469
+ }
470
+ `);
471
+
472
+ expect(z.toJSONSchema(z.number().lt(5).lt(3))).toMatchInlineSnapshot(`
473
+ {
474
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
475
+ "exclusiveMaximum": 3,
476
+ "type": "number",
477
+ }
478
+ `);
479
+
480
+ expect(z.toJSONSchema(z.number().lt(5).lt(3).lte(2))).toMatchInlineSnapshot(`
481
+ {
482
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
483
+ "maximum": 2,
484
+ "type": "number",
485
+ }
486
+ `);
487
+
488
+ expect(z.toJSONSchema(z.number().lt(5).lte(3))).toMatchInlineSnapshot(`
489
+ {
490
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
491
+ "maximum": 3,
492
+ "type": "number",
493
+ }
494
+ `);
495
+
496
+ expect(z.toJSONSchema(z.number().gt(5).lt(10))).toMatchInlineSnapshot(`
497
+ {
498
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
499
+ "exclusiveMaximum": 10,
500
+ "exclusiveMinimum": 5,
501
+ "type": "number",
502
+ }
503
+ `);
504
+ expect(z.toJSONSchema(z.number().gte(5).lte(10))).toMatchInlineSnapshot(`
505
+ {
506
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
507
+ "maximum": 10,
508
+ "minimum": 5,
509
+ "type": "number",
510
+ }
511
+ `);
512
+ expect(z.toJSONSchema(z.number().positive())).toMatchInlineSnapshot(`
513
+ {
514
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
515
+ "exclusiveMinimum": 0,
516
+ "type": "number",
517
+ }
518
+ `);
519
+ expect(z.toJSONSchema(z.number().negative())).toMatchInlineSnapshot(`
520
+ {
521
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
522
+ "exclusiveMaximum": 0,
523
+ "type": "number",
524
+ }
525
+ `);
526
+ expect(z.toJSONSchema(z.number().nonpositive())).toMatchInlineSnapshot(`
527
+ {
528
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
529
+ "maximum": 0,
530
+ "type": "number",
531
+ }
532
+ `);
533
+ expect(z.toJSONSchema(z.number().nonnegative())).toMatchInlineSnapshot(`
534
+ {
535
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
536
+ "minimum": 0,
537
+ "type": "number",
538
+ }
539
+ `);
540
+ });
541
+
542
+ test("arrays", () => {
543
+ expect(z.toJSONSchema(z.array(z.string()))).toMatchInlineSnapshot(`
544
+ {
545
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
546
+ "items": {
547
+ "type": "string",
548
+ },
549
+ "type": "array",
550
+ }
551
+ `);
552
+ });
553
+
554
+ test("unions", () => {
555
+ const schema = z.union([z.string(), z.number()]);
556
+ expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
557
+ {
558
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
559
+ "anyOf": [
560
+ {
561
+ "type": "string",
562
+ },
563
+ {
564
+ "type": "number",
565
+ },
566
+ ],
567
+ }
568
+ `);
569
+ });
570
+
571
+ test("intersections", () => {
572
+ const schema = z.intersection(z.object({ name: z.string() }), z.object({ age: z.number() }));
573
+
574
+ expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
575
+ {
576
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
577
+ "allOf": [
578
+ {
579
+ "additionalProperties": false,
580
+ "properties": {
581
+ "name": {
582
+ "type": "string",
583
+ },
584
+ },
585
+ "required": [
586
+ "name",
587
+ ],
588
+ "type": "object",
589
+ },
590
+ {
591
+ "additionalProperties": false,
592
+ "properties": {
593
+ "age": {
594
+ "type": "number",
595
+ },
596
+ },
597
+ "required": [
598
+ "age",
599
+ ],
600
+ "type": "object",
601
+ },
602
+ ],
603
+ }
604
+ `);
605
+ });
606
+
607
+ test("record", () => {
608
+ const schema = z.record(z.string(), z.boolean());
609
+ expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
610
+ {
611
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
612
+ "additionalProperties": {
613
+ "type": "boolean",
614
+ },
615
+ "propertyNames": {
616
+ "type": "string",
617
+ },
618
+ "type": "object",
619
+ }
620
+ `);
621
+ });
622
+
623
+ test("tuple", () => {
624
+ const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
625
+ expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
626
+ {
627
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
628
+ "items": {
629
+ "type": "boolean",
630
+ },
631
+ "prefixItems": [
632
+ {
633
+ "type": "string",
634
+ },
635
+ {
636
+ "type": "number",
637
+ },
638
+ ],
639
+ "type": "array",
640
+ }
641
+ `);
642
+ });
643
+
644
+ test("promise", () => {
645
+ const schema = z.promise(z.string());
646
+ expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
647
+ {
648
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
649
+ "type": "string",
650
+ }
651
+ `);
652
+ });
653
+
654
+ test("lazy", () => {
655
+ const schema = z.lazy(() => z.string());
656
+ expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
657
+ {
658
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
659
+ "type": "string",
660
+ }
661
+ `);
662
+ });
663
+
664
+ // enum
665
+ test("enum", () => {
666
+ const a = z.enum(["a", "b", "c"]);
667
+ expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
668
+ {
669
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
670
+ "enum": [
671
+ "a",
672
+ "b",
673
+ "c",
674
+ ],
675
+ "type": "string",
676
+ }
677
+ `);
678
+
679
+ enum B {
680
+ A = 0,
681
+ B = 1,
682
+ C = 2,
683
+ }
684
+
685
+ const b = z.enum(B);
686
+ expect(z.toJSONSchema(b)).toMatchInlineSnapshot(`
687
+ {
688
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
689
+ "enum": [
690
+ 0,
691
+ 1,
692
+ 2,
693
+ ],
694
+ "type": "number",
695
+ }
696
+ `);
697
+ });
698
+
699
+ // literal
700
+ test("literal", () => {
701
+ const a = z.literal("hello");
702
+ expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
703
+ {
704
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
705
+ "const": "hello",
706
+ "type": "string",
707
+ }
708
+ `);
709
+
710
+ const b = z.literal(7);
711
+ expect(z.toJSONSchema(b)).toMatchInlineSnapshot(`
712
+ {
713
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
714
+ "const": 7,
715
+ "type": "number",
716
+ }
717
+ `);
718
+
719
+ const c = z.literal(["hello", undefined, null, 5, BigInt(1324)]);
720
+ expect(() => z.toJSONSchema(c)).toThrow();
721
+
722
+ const d = z.literal(["hello", null, 5]);
723
+ expect(z.toJSONSchema(d)).toMatchInlineSnapshot(`
724
+ {
725
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
726
+ "enum": [
727
+ "hello",
728
+ null,
729
+ 5,
730
+ ],
731
+ }
732
+ `);
733
+
734
+ const e = z.literal(["hello", "zod", "v4"]);
735
+ expect(z.toJSONSchema(e)).toMatchInlineSnapshot(`
736
+ {
737
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
738
+ "enum": [
739
+ "hello",
740
+ "zod",
741
+ "v4",
742
+ ],
743
+ "type": "string",
744
+ }
745
+ `);
746
+ });
747
+
748
+ // pipe
749
+ test("pipe", () => {
750
+ const schema = z
751
+ .string()
752
+ .transform((val) => Number.parseInt(val))
753
+ .pipe(z.number());
754
+ expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
755
+ {
756
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
757
+ "type": "number",
758
+ }
759
+ `);
760
+ });
761
+
762
+ test("simple objects", () => {
763
+ const schema = z.object({
764
+ name: z.string(),
765
+ age: z.number(),
766
+ });
767
+
768
+ expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(
769
+ `
770
+ {
771
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
772
+ "additionalProperties": false,
773
+ "properties": {
774
+ "age": {
775
+ "type": "number",
776
+ },
777
+ "name": {
778
+ "type": "string",
779
+ },
780
+ },
781
+ "required": [
782
+ "name",
783
+ "age",
784
+ ],
785
+ "type": "object",
786
+ }
787
+ `
788
+ );
789
+ });
790
+
791
+ test("additionalproperties in z.object", () => {
792
+ const a = z.object({
793
+ name: z.string(),
794
+ });
795
+ expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
796
+ {
797
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
798
+ "additionalProperties": false,
799
+ "properties": {
800
+ "name": {
801
+ "type": "string",
802
+ },
803
+ },
804
+ "required": [
805
+ "name",
806
+ ],
807
+ "type": "object",
808
+ }
809
+ `);
810
+ expect(z.toJSONSchema(a, { io: "input" })).toMatchInlineSnapshot(`
811
+ {
812
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
813
+ "properties": {
814
+ "name": {
815
+ "type": "string",
816
+ },
817
+ },
818
+ "required": [
819
+ "name",
820
+ ],
821
+ "type": "object",
822
+ }
823
+ `);
824
+ expect(
825
+ z.toJSONSchema(a, {
826
+ io: "input",
827
+ override(ctx) {
828
+ const def = ctx.zodSchema._zod.def;
829
+ if (def.type === "object" && !def.catchall) {
830
+ (ctx.jsonSchema as z.core.JSONSchema.ObjectSchema).additionalProperties = false;
831
+ }
832
+ },
833
+ })
834
+ ).toMatchInlineSnapshot(`
835
+ {
836
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
837
+ "additionalProperties": false,
838
+ "properties": {
839
+ "name": {
840
+ "type": "string",
841
+ },
842
+ },
843
+ "required": [
844
+ "name",
845
+ ],
846
+ "type": "object",
847
+ }
848
+ `);
849
+ });
850
+
851
+ test("catchall objects", () => {
852
+ const a = z.strictObject({
853
+ name: z.string(),
854
+ age: z.number(),
855
+ });
856
+
857
+ expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
858
+ {
859
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
860
+ "additionalProperties": false,
861
+ "properties": {
862
+ "age": {
863
+ "type": "number",
864
+ },
865
+ "name": {
866
+ "type": "string",
867
+ },
868
+ },
869
+ "required": [
870
+ "name",
871
+ "age",
872
+ ],
873
+ "type": "object",
874
+ }
875
+ `);
876
+
877
+ const b = z
878
+ .object({
879
+ name: z.string(),
880
+ })
881
+ .catchall(z.string());
882
+
883
+ expect(z.toJSONSchema(b)).toMatchInlineSnapshot(`
884
+ {
885
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
886
+ "additionalProperties": {
887
+ "type": "string",
888
+ },
889
+ "properties": {
890
+ "name": {
891
+ "type": "string",
892
+ },
893
+ },
894
+ "required": [
895
+ "name",
896
+ ],
897
+ "type": "object",
898
+ }
899
+ `);
900
+
901
+ const c = z.looseObject({
902
+ name: z.string(),
903
+ });
904
+
905
+ expect(z.toJSONSchema(c)).toMatchInlineSnapshot(`
906
+ {
907
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
908
+ "additionalProperties": {},
909
+ "properties": {
910
+ "name": {
911
+ "type": "string",
912
+ },
913
+ },
914
+ "required": [
915
+ "name",
916
+ ],
917
+ "type": "object",
918
+ }
919
+ `);
920
+ });
921
+
922
+ test("optional fields - object", () => {
923
+ const schema = z.object({
924
+ required: z.string(),
925
+ optional: z.string().optional(),
926
+ nonoptional: z.string().optional().nonoptional(),
927
+ });
928
+
929
+ const result = z.toJSONSchema(schema);
930
+
931
+ expect(result).toMatchInlineSnapshot(`
932
+ {
933
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
934
+ "additionalProperties": false,
935
+ "properties": {
936
+ "nonoptional": {
937
+ "type": "string",
938
+ },
939
+ "optional": {
940
+ "type": "string",
941
+ },
942
+ "required": {
943
+ "type": "string",
944
+ },
945
+ },
946
+ "required": [
947
+ "required",
948
+ "nonoptional",
949
+ ],
950
+ "type": "object",
951
+ }
952
+ `);
953
+ });
954
+
955
+ test("recursive object", () => {
956
+ interface Category {
957
+ name: string;
958
+ subcategories: Category[];
959
+ }
960
+
961
+ const categorySchema: z.ZodType<Category> = z.object({
962
+ name: z.string(),
963
+ subcategories: z.array(z.lazy(() => categorySchema)),
964
+ });
965
+
966
+ const result = z.toJSONSchema(categorySchema);
967
+ expect(result).toMatchInlineSnapshot(`
968
+ {
969
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
970
+ "additionalProperties": false,
971
+ "properties": {
972
+ "name": {
973
+ "type": "string",
974
+ },
975
+ "subcategories": {
976
+ "items": {
977
+ "$ref": "#",
978
+ },
979
+ "type": "array",
980
+ },
981
+ },
982
+ "required": [
983
+ "name",
984
+ "subcategories",
985
+ ],
986
+ "type": "object",
987
+ }
988
+ `);
989
+ });
990
+
991
+ test("simple interface", () => {
992
+ const userSchema = z.object({
993
+ name: z.string(),
994
+ age: z.number().optional(),
995
+ });
996
+
997
+ const result = z.toJSONSchema(userSchema);
998
+ expect(result).toMatchInlineSnapshot(`
999
+ {
1000
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1001
+ "additionalProperties": false,
1002
+ "properties": {
1003
+ "age": {
1004
+ "type": "number",
1005
+ },
1006
+ "name": {
1007
+ "type": "string",
1008
+ },
1009
+ },
1010
+ "required": [
1011
+ "name",
1012
+ ],
1013
+ "type": "object",
1014
+ }
1015
+ `);
1016
+ });
1017
+
1018
+ test("catchall interface", () => {
1019
+ const a = z.strictObject({
1020
+ name: z.string(),
1021
+ age: z.number(),
1022
+ });
1023
+
1024
+ expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
1025
+ {
1026
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1027
+ "additionalProperties": false,
1028
+ "properties": {
1029
+ "age": {
1030
+ "type": "number",
1031
+ },
1032
+ "name": {
1033
+ "type": "string",
1034
+ },
1035
+ },
1036
+ "required": [
1037
+ "name",
1038
+ "age",
1039
+ ],
1040
+ "type": "object",
1041
+ }
1042
+ `);
1043
+
1044
+ const b = z
1045
+ .object({
1046
+ name: z.string(),
1047
+ })
1048
+ .catchall(z.string());
1049
+
1050
+ expect(z.toJSONSchema(b)).toMatchInlineSnapshot(`
1051
+ {
1052
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1053
+ "additionalProperties": {
1054
+ "type": "string",
1055
+ },
1056
+ "properties": {
1057
+ "name": {
1058
+ "type": "string",
1059
+ },
1060
+ },
1061
+ "required": [
1062
+ "name",
1063
+ ],
1064
+ "type": "object",
1065
+ }
1066
+ `);
1067
+
1068
+ const c = z.looseObject({
1069
+ name: z.string(),
1070
+ });
1071
+
1072
+ expect(z.toJSONSchema(c)).toMatchInlineSnapshot(`
1073
+ {
1074
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1075
+ "additionalProperties": {},
1076
+ "properties": {
1077
+ "name": {
1078
+ "type": "string",
1079
+ },
1080
+ },
1081
+ "required": [
1082
+ "name",
1083
+ ],
1084
+ "type": "object",
1085
+ }
1086
+ `);
1087
+ });
1088
+
1089
+ test("recursive interface schemas", () => {
1090
+ const TreeNodeSchema = z.object({
1091
+ id: z.string(),
1092
+ get children() {
1093
+ return TreeNodeSchema;
1094
+ },
1095
+ });
1096
+
1097
+ const result = z.toJSONSchema(TreeNodeSchema);
1098
+
1099
+ // Should have definitions for recursive schema
1100
+ expect(JSON.stringify(result, null, 2)).toMatchInlineSnapshot(
1101
+ `
1102
+ "{
1103
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1104
+ "type": "object",
1105
+ "properties": {
1106
+ "id": {
1107
+ "type": "string"
1108
+ },
1109
+ "children": {
1110
+ "$ref": "#"
1111
+ }
1112
+ },
1113
+ "required": [
1114
+ "id",
1115
+ "children"
1116
+ ],
1117
+ "additionalProperties": false
1118
+ }"
1119
+ `
1120
+ );
1121
+ });
1122
+
1123
+ test("mutually recursive interface schemas", () => {
1124
+ const FolderSchema = z.object({
1125
+ name: z.string(),
1126
+ get files() {
1127
+ return z.array(FileSchema);
1128
+ },
1129
+ });
1130
+
1131
+ const FileSchema = z.object({
1132
+ name: z.string(),
1133
+ get parent() {
1134
+ return FolderSchema;
1135
+ },
1136
+ });
1137
+
1138
+ const result = z.toJSONSchema(FolderSchema);
1139
+
1140
+ // Should have definitions for both schemas
1141
+ expect(JSON.stringify(result, null, 2)).toMatchInlineSnapshot(
1142
+ `
1143
+ "{
1144
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1145
+ "type": "object",
1146
+ "properties": {
1147
+ "name": {
1148
+ "type": "string"
1149
+ },
1150
+ "files": {
1151
+ "type": "array",
1152
+ "items": {
1153
+ "type": "object",
1154
+ "properties": {
1155
+ "name": {
1156
+ "type": "string"
1157
+ },
1158
+ "parent": {
1159
+ "$ref": "#"
1160
+ }
1161
+ },
1162
+ "required": [
1163
+ "name",
1164
+ "parent"
1165
+ ],
1166
+ "additionalProperties": false
1167
+ }
1168
+ }
1169
+ },
1170
+ "required": [
1171
+ "name",
1172
+ "files"
1173
+ ],
1174
+ "additionalProperties": false
1175
+ }"
1176
+ `
1177
+ );
1178
+ });
1179
+ });
1180
+
1181
+ test("override", () => {
1182
+ const schema = z.z.toJSONSchema(z.string(), {
1183
+ override: (ctx) => {
1184
+ ctx.zodSchema;
1185
+ ctx.jsonSchema;
1186
+ ctx.jsonSchema.whatever = "sup";
1187
+ },
1188
+ });
1189
+ expect(schema).toMatchInlineSnapshot(`
1190
+ {
1191
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1192
+ "type": "string",
1193
+ "whatever": "sup",
1194
+ }
1195
+ `);
1196
+ });
1197
+
1198
+ test("override: do not run on references", () => {
1199
+ let overrideCount = 0;
1200
+ const schema = z
1201
+ .union([z.string().date(), z.string().datetime(), z.string().datetime({ local: true })])
1202
+ .meta({ a: true })
1203
+ .transform((str) => new Date(str))
1204
+ .meta({ b: true })
1205
+ .pipe(z.date())
1206
+ .meta({ c: true })
1207
+ .brand("dateIn");
1208
+ z.z.toJSONSchema(schema, {
1209
+ unrepresentable: "any",
1210
+ io: "input",
1211
+ override(_) {
1212
+ overrideCount++;
1213
+ },
1214
+ });
1215
+
1216
+ expect(overrideCount).toBe(6);
1217
+ });
1218
+
1219
+ test("override with refs", () => {
1220
+ const a = z.string().optional();
1221
+ const result = z.z.toJSONSchema(a, {
1222
+ override(ctx) {
1223
+ if (ctx.zodSchema._zod.def.type === "string") {
1224
+ ctx.jsonSchema.type = "STRING" as "string";
1225
+ }
1226
+ },
1227
+ });
1228
+
1229
+ expect(result).toMatchInlineSnapshot(`
1230
+ {
1231
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1232
+ "type": "STRING",
1233
+ }
1234
+ `);
1235
+ });
1236
+
1237
+ test("override execution order", () => {
1238
+ const schema = z.union([z.string(), z.number()]);
1239
+ let unionSchema!: any;
1240
+ z.z.toJSONSchema(schema, {
1241
+ override(ctx) {
1242
+ if (ctx.zodSchema._zod.def.type === "union") {
1243
+ unionSchema = ctx.jsonSchema;
1244
+ }
1245
+ },
1246
+ });
1247
+
1248
+ expect(unionSchema).toMatchInlineSnapshot(`
1249
+ {
1250
+ "anyOf": [
1251
+ {
1252
+ "type": "string",
1253
+ },
1254
+ {
1255
+ "type": "number",
1256
+ },
1257
+ ],
1258
+ }
1259
+ `);
1260
+ });
1261
+
1262
+ test("pipe", () => {
1263
+ const mySchema = z
1264
+ .string()
1265
+ .transform((val) => val.length)
1266
+ .pipe(z.number());
1267
+ // ZodPipe
1268
+
1269
+ const a = z.z.toJSONSchema(mySchema);
1270
+ expect(a).toMatchInlineSnapshot(`
1271
+ {
1272
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1273
+ "type": "number",
1274
+ }
1275
+ `);
1276
+ // => { type: "number" }
1277
+
1278
+ const b = z.z.toJSONSchema(mySchema, { io: "input" });
1279
+ expect(b).toMatchInlineSnapshot(`
1280
+ {
1281
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1282
+ "type": "string",
1283
+ }
1284
+ `);
1285
+ // => { type: "string" }
1286
+ });
1287
+
1288
+ test("passthrough schemas", () => {
1289
+ const Internal = z.object({
1290
+ num: z.number(),
1291
+ str: z.string(),
1292
+ });
1293
+ //.meta({ id: "Internal" });
1294
+
1295
+ const External = z.object({
1296
+ a: Internal,
1297
+ b: Internal.optional(),
1298
+ c: z.lazy(() => Internal),
1299
+ d: z.promise(Internal),
1300
+ e: z.pipe(Internal, Internal),
1301
+ });
1302
+
1303
+ const result = z.z.toJSONSchema(External, {
1304
+ reused: "ref",
1305
+ });
1306
+ expect(result).toMatchInlineSnapshot(`
1307
+ {
1308
+ "$defs": {
1309
+ "__schema0": {
1310
+ "additionalProperties": false,
1311
+ "properties": {
1312
+ "num": {
1313
+ "type": "number",
1314
+ },
1315
+ "str": {
1316
+ "type": "string",
1317
+ },
1318
+ },
1319
+ "required": [
1320
+ "num",
1321
+ "str",
1322
+ ],
1323
+ "type": "object",
1324
+ },
1325
+ },
1326
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1327
+ "additionalProperties": false,
1328
+ "properties": {
1329
+ "a": {
1330
+ "$ref": "#/$defs/__schema0",
1331
+ },
1332
+ "b": {
1333
+ "$ref": "#/$defs/__schema0",
1334
+ },
1335
+ "c": {
1336
+ "$ref": "#/$defs/__schema0",
1337
+ },
1338
+ "d": {
1339
+ "$ref": "#/$defs/__schema0",
1340
+ },
1341
+ "e": {
1342
+ "$ref": "#/$defs/__schema0",
1343
+ },
1344
+ },
1345
+ "required": [
1346
+ "a",
1347
+ "c",
1348
+ "d",
1349
+ "e",
1350
+ ],
1351
+ "type": "object",
1352
+ }
1353
+ `);
1354
+ });
1355
+
1356
+ test("extract schemas with id", () => {
1357
+ const name = z.string().meta({ id: "name" });
1358
+ const result = z.z.toJSONSchema(
1359
+ z.object({
1360
+ first_name: name,
1361
+ last_name: name.nullable(),
1362
+ middle_name: name.optional(),
1363
+ age: z.number().meta({ id: "age" }),
1364
+ })
1365
+ );
1366
+ expect(result).toMatchInlineSnapshot(`
1367
+ {
1368
+ "$defs": {
1369
+ "age": {
1370
+ "id": "age",
1371
+ "type": "number",
1372
+ },
1373
+ "name": {
1374
+ "id": "name",
1375
+ "type": "string",
1376
+ },
1377
+ },
1378
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1379
+ "additionalProperties": false,
1380
+ "properties": {
1381
+ "age": {
1382
+ "$ref": "#/$defs/age",
1383
+ },
1384
+ "first_name": {
1385
+ "$ref": "#/$defs/name",
1386
+ },
1387
+ "last_name": {
1388
+ "anyOf": [
1389
+ {
1390
+ "$ref": "#/$defs/name",
1391
+ },
1392
+ {
1393
+ "type": "null",
1394
+ },
1395
+ ],
1396
+ },
1397
+ "middle_name": {
1398
+ "$ref": "#/$defs/name",
1399
+ },
1400
+ },
1401
+ "required": [
1402
+ "first_name",
1403
+ "last_name",
1404
+ "age",
1405
+ ],
1406
+ "type": "object",
1407
+ }
1408
+ `);
1409
+ });
1410
+
1411
+ test("unrepresentable literal values are ignored", () => {
1412
+ const a = z.z.toJSONSchema(z.literal(["hello", null, 5, BigInt(1324), undefined]), { unrepresentable: "any" });
1413
+ expect(a).toMatchInlineSnapshot(`
1414
+ {
1415
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1416
+ "enum": [
1417
+ "hello",
1418
+ null,
1419
+ 5,
1420
+ 1324,
1421
+ ],
1422
+ }
1423
+ `);
1424
+
1425
+ const b = z.z.toJSONSchema(z.literal([undefined, null, 5, BigInt(1324)]), { unrepresentable: "any" });
1426
+ expect(b).toMatchInlineSnapshot(`
1427
+ {
1428
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1429
+ "enum": [
1430
+ null,
1431
+ 5,
1432
+ 1324,
1433
+ ],
1434
+ }
1435
+ `);
1436
+
1437
+ const c = z.z.toJSONSchema(z.literal([undefined]), { unrepresentable: "any" });
1438
+ expect(c).toMatchInlineSnapshot(`
1439
+ {
1440
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1441
+ }
1442
+ `);
1443
+ });
1444
+
1445
+ test("describe with id", () => {
1446
+ const jobId = z.string().meta({ id: "jobId" });
1447
+
1448
+ const a = z.z.toJSONSchema(
1449
+ z.object({
1450
+ current: jobId.describe("Current job"),
1451
+ previous: jobId.describe("Previous job"),
1452
+ })
1453
+ );
1454
+ expect(a).toMatchInlineSnapshot(`
1455
+ {
1456
+ "$defs": {
1457
+ "jobId": {
1458
+ "id": "jobId",
1459
+ "type": "string",
1460
+ },
1461
+ },
1462
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1463
+ "additionalProperties": false,
1464
+ "properties": {
1465
+ "current": {
1466
+ "$ref": "#/$defs/jobId",
1467
+ "description": "Current job",
1468
+ },
1469
+ "previous": {
1470
+ "$ref": "#/$defs/jobId",
1471
+ "description": "Previous job",
1472
+ },
1473
+ },
1474
+ "required": [
1475
+ "current",
1476
+ "previous",
1477
+ ],
1478
+ "type": "object",
1479
+ }
1480
+ `);
1481
+ });
1482
+
1483
+ test("overwrite id", () => {
1484
+ const jobId = z.string().meta({ id: "aaa" });
1485
+
1486
+ const a = z.z.toJSONSchema(
1487
+ z.object({
1488
+ current: jobId,
1489
+ previous: jobId.meta({ id: "bbb" }),
1490
+ })
1491
+ );
1492
+ expect(a).toMatchInlineSnapshot(`
1493
+ {
1494
+ "$defs": {
1495
+ "aaa": {
1496
+ "id": "aaa",
1497
+ "type": "string",
1498
+ },
1499
+ "bbb": {
1500
+ "$ref": "#/$defs/aaa",
1501
+ "id": "bbb",
1502
+ },
1503
+ },
1504
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1505
+ "additionalProperties": false,
1506
+ "properties": {
1507
+ "current": {
1508
+ "$ref": "#/$defs/aaa",
1509
+ },
1510
+ "previous": {
1511
+ "$ref": "#/$defs/bbb",
1512
+ },
1513
+ },
1514
+ "required": [
1515
+ "current",
1516
+ "previous",
1517
+ ],
1518
+ "type": "object",
1519
+ }
1520
+ `);
1521
+
1522
+ const b = z.z.toJSONSchema(
1523
+ z.object({
1524
+ current: jobId,
1525
+ previous: jobId.meta({ id: "ccc" }),
1526
+ }),
1527
+ {
1528
+ reused: "ref",
1529
+ }
1530
+ );
1531
+ expect(b).toMatchInlineSnapshot(`
1532
+ {
1533
+ "$defs": {
1534
+ "aaa": {
1535
+ "id": "aaa",
1536
+ "type": "string",
1537
+ },
1538
+ "ccc": {
1539
+ "$ref": "#/$defs/aaa",
1540
+ "id": "ccc",
1541
+ },
1542
+ },
1543
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1544
+ "additionalProperties": false,
1545
+ "properties": {
1546
+ "current": {
1547
+ "$ref": "#/$defs/aaa",
1548
+ },
1549
+ "previous": {
1550
+ "$ref": "#/$defs/ccc",
1551
+ },
1552
+ },
1553
+ "required": [
1554
+ "current",
1555
+ "previous",
1556
+ ],
1557
+ "type": "object",
1558
+ }
1559
+ `);
1560
+ });
1561
+
1562
+ test("overwrite descriptions", () => {
1563
+ const field = z.string().describe("a").describe("b").describe("c");
1564
+
1565
+ const a = z.z.toJSONSchema(
1566
+ z.object({
1567
+ d: field.describe("d"),
1568
+ e: field.describe("e"),
1569
+ })
1570
+ );
1571
+ expect(a).toMatchInlineSnapshot(`
1572
+ {
1573
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1574
+ "additionalProperties": false,
1575
+ "properties": {
1576
+ "d": {
1577
+ "description": "d",
1578
+ "type": "string",
1579
+ },
1580
+ "e": {
1581
+ "description": "e",
1582
+ "type": "string",
1583
+ },
1584
+ },
1585
+ "required": [
1586
+ "d",
1587
+ "e",
1588
+ ],
1589
+ "type": "object",
1590
+ }
1591
+ `);
1592
+
1593
+ const b = z.z.toJSONSchema(
1594
+ z.object({
1595
+ d: field.describe("d"),
1596
+ e: field.describe("e"),
1597
+ }),
1598
+ {
1599
+ reused: "ref",
1600
+ }
1601
+ );
1602
+ expect(b).toMatchInlineSnapshot(`
1603
+ {
1604
+ "$defs": {
1605
+ "__schema0": {
1606
+ "description": "c",
1607
+ "type": "string",
1608
+ },
1609
+ },
1610
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1611
+ "additionalProperties": false,
1612
+ "properties": {
1613
+ "d": {
1614
+ "$ref": "#/$defs/__schema0",
1615
+ "description": "d",
1616
+ },
1617
+ "e": {
1618
+ "$ref": "#/$defs/__schema0",
1619
+ "description": "e",
1620
+ },
1621
+ },
1622
+ "required": [
1623
+ "d",
1624
+ "e",
1625
+ ],
1626
+ "type": "object",
1627
+ }
1628
+ `);
1629
+ });
1630
+
1631
+ test("top-level readonly", () => {
1632
+ const A = z
1633
+ .object({
1634
+ name: z.string(),
1635
+ get b() {
1636
+ return B;
1637
+ },
1638
+ })
1639
+ .readonly()
1640
+ .meta({ id: "A" });
1641
+ // z.globalRegistry.add(A, { id: "A" });
1642
+ // .meta({ id: "A" });
1643
+
1644
+ const B = z
1645
+ .object({
1646
+ name: z.string(),
1647
+ get a() {
1648
+ return A;
1649
+ },
1650
+ })
1651
+ .readonly()
1652
+ .meta({ id: "B" });
1653
+ // z.globalRegistry.add(B, { id: "B" });
1654
+ // .meta({ id: "B" });
1655
+
1656
+ const result = z.z.toJSONSchema(A);
1657
+ expect(result).toMatchInlineSnapshot(`
1658
+ {
1659
+ "$defs": {
1660
+ "B": {
1661
+ "additionalProperties": false,
1662
+ "id": "B",
1663
+ "properties": {
1664
+ "a": {
1665
+ "$ref": "#",
1666
+ },
1667
+ "name": {
1668
+ "type": "string",
1669
+ },
1670
+ },
1671
+ "readOnly": true,
1672
+ "required": [
1673
+ "name",
1674
+ "a",
1675
+ ],
1676
+ "type": "object",
1677
+ },
1678
+ },
1679
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1680
+ "additionalProperties": false,
1681
+ "id": "A",
1682
+ "properties": {
1683
+ "b": {
1684
+ "$ref": "#/$defs/B",
1685
+ },
1686
+ "name": {
1687
+ "type": "string",
1688
+ },
1689
+ },
1690
+ "readOnly": true,
1691
+ "required": [
1692
+ "name",
1693
+ "b",
1694
+ ],
1695
+ "type": "object",
1696
+ }
1697
+ `);
1698
+ });
1699
+
1700
+ test("basic registry", () => {
1701
+ const myRegistry = z.registry<{ id: string }>();
1702
+ const User = z.object({
1703
+ name: z.string(),
1704
+ get posts() {
1705
+ return z.array(Post);
1706
+ },
1707
+ });
1708
+
1709
+ const Post = z.object({
1710
+ title: z.string(),
1711
+ content: z.string(),
1712
+ get author() {
1713
+ return User;
1714
+ },
1715
+ });
1716
+
1717
+ myRegistry.add(User, { id: "User" });
1718
+ myRegistry.add(Post, { id: "Post" });
1719
+
1720
+ const result = z.z.toJSONSchema(myRegistry);
1721
+ expect(result).toMatchInlineSnapshot(`
1722
+ {
1723
+ "schemas": {
1724
+ "Post": {
1725
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1726
+ "additionalProperties": false,
1727
+ "properties": {
1728
+ "author": {
1729
+ "$ref": "User",
1730
+ },
1731
+ "content": {
1732
+ "type": "string",
1733
+ },
1734
+ "title": {
1735
+ "type": "string",
1736
+ },
1737
+ },
1738
+ "required": [
1739
+ "title",
1740
+ "content",
1741
+ "author",
1742
+ ],
1743
+ "type": "object",
1744
+ },
1745
+ "User": {
1746
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1747
+ "additionalProperties": false,
1748
+ "properties": {
1749
+ "name": {
1750
+ "type": "string",
1751
+ },
1752
+ "posts": {
1753
+ "items": {
1754
+ "$ref": "Post",
1755
+ },
1756
+ "type": "array",
1757
+ },
1758
+ },
1759
+ "required": [
1760
+ "name",
1761
+ "posts",
1762
+ ],
1763
+ "type": "object",
1764
+ },
1765
+ },
1766
+ }
1767
+ `);
1768
+ });
1769
+
1770
+ test("_ref", () => {
1771
+ // const a = z.promise(z.string().describe("a"));
1772
+ const a = z.z.toJSONSchema(z.promise(z.string().describe("a")));
1773
+ expect(a).toMatchInlineSnapshot(`
1774
+ {
1775
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1776
+ "description": "a",
1777
+ "type": "string",
1778
+ }
1779
+ `);
1780
+
1781
+ const b = z.z.toJSONSchema(z.lazy(() => z.string().describe("a")));
1782
+ expect(b).toMatchInlineSnapshot(`
1783
+ {
1784
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1785
+ "description": "a",
1786
+ "type": "string",
1787
+ }
1788
+ `);
1789
+
1790
+ const c = z.z.toJSONSchema(z.optional(z.string().describe("a")));
1791
+ expect(c).toMatchInlineSnapshot(`
1792
+ {
1793
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1794
+ "description": "a",
1795
+ "type": "string",
1796
+ }
1797
+ `);
1798
+ });
1799
+
1800
+ test("defaults/prefaults", () => {
1801
+ const a = z
1802
+ .string()
1803
+ .transform((val) => val.length)
1804
+ .pipe(z.number());
1805
+ const b = a.prefault("hello");
1806
+ const c = a.default(1234);
1807
+
1808
+ // a
1809
+ expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
1810
+ {
1811
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1812
+ "type": "number",
1813
+ }
1814
+ `);
1815
+ expect(z.toJSONSchema(a, { io: "input" })).toMatchInlineSnapshot(`
1816
+ {
1817
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1818
+ "type": "string",
1819
+ }
1820
+ `);
1821
+
1822
+ // b
1823
+ expect(z.toJSONSchema(b)).toMatchInlineSnapshot(`
1824
+ {
1825
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1826
+ "type": "number",
1827
+ }
1828
+ `);
1829
+ expect(z.toJSONSchema(b, { io: "input" })).toMatchInlineSnapshot(`
1830
+ {
1831
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1832
+ "default": "hello",
1833
+ "type": "string",
1834
+ }
1835
+ `);
1836
+ // c
1837
+ expect(z.toJSONSchema(c)).toMatchInlineSnapshot(`
1838
+ {
1839
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1840
+ "default": 1234,
1841
+ "type": "number",
1842
+ }
1843
+ `);
1844
+ expect(z.toJSONSchema(c, { io: "input" })).toMatchInlineSnapshot(`
1845
+ {
1846
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1847
+ "type": "string",
1848
+ }
1849
+ `);
1850
+ });
1851
+
1852
+ test("input type", () => {
1853
+ const schema = z.object({
1854
+ a: z.string(),
1855
+ b: z.string().optional(),
1856
+ c: z.string().default("hello"),
1857
+ d: z.string().nullable(),
1858
+ e: z.string().prefault("hello"),
1859
+ });
1860
+ expect(z.toJSONSchema(schema, { io: "input" })).toMatchInlineSnapshot(`
1861
+ {
1862
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1863
+ "properties": {
1864
+ "a": {
1865
+ "type": "string",
1866
+ },
1867
+ "b": {
1868
+ "type": "string",
1869
+ },
1870
+ "c": {
1871
+ "default": "hello",
1872
+ "type": "string",
1873
+ },
1874
+ "d": {
1875
+ "anyOf": [
1876
+ {
1877
+ "type": "string",
1878
+ },
1879
+ {
1880
+ "type": "null",
1881
+ },
1882
+ ],
1883
+ },
1884
+ "e": {
1885
+ "default": "hello",
1886
+ "type": "string",
1887
+ },
1888
+ },
1889
+ "required": [
1890
+ "a",
1891
+ "d",
1892
+ ],
1893
+ "type": "object",
1894
+ }
1895
+ `);
1896
+ expect(z.toJSONSchema(schema, { io: "output" })).toMatchInlineSnapshot(`
1897
+ {
1898
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1899
+ "additionalProperties": false,
1900
+ "properties": {
1901
+ "a": {
1902
+ "type": "string",
1903
+ },
1904
+ "b": {
1905
+ "type": "string",
1906
+ },
1907
+ "c": {
1908
+ "default": "hello",
1909
+ "type": "string",
1910
+ },
1911
+ "d": {
1912
+ "anyOf": [
1913
+ {
1914
+ "type": "string",
1915
+ },
1916
+ {
1917
+ "type": "null",
1918
+ },
1919
+ ],
1920
+ },
1921
+ "e": {
1922
+ "type": "string",
1923
+ },
1924
+ },
1925
+ "required": [
1926
+ "a",
1927
+ "c",
1928
+ "d",
1929
+ "e",
1930
+ ],
1931
+ "type": "object",
1932
+ }
1933
+ `);
1934
+ });
1935
+
1936
+ test("examples on pipe", () => {
1937
+ const schema = z
1938
+ .string()
1939
+ .meta({ examples: ["test"] })
1940
+ .transform(Number)
1941
+ // .pipe(z.transform(Number).meta({ examples: [4] }))
1942
+ .meta({ examples: [4] });
1943
+
1944
+ const i = z.z.toJSONSchema(schema, { io: "input", unrepresentable: "any" });
1945
+ expect(i).toMatchInlineSnapshot(`
1946
+ {
1947
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1948
+ "examples": [
1949
+ "test",
1950
+ ],
1951
+ "type": "string",
1952
+ }
1953
+ `);
1954
+ const o = z.z.toJSONSchema(schema, { io: "output", unrepresentable: "any" });
1955
+ expect(o).toMatchInlineSnapshot(`
1956
+ {
1957
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
1958
+ "examples": [
1959
+ 4,
1960
+ ],
1961
+ }
1962
+ `);
1963
+ });
1964
+
1965
+ // test("number checks", () => {
1966
+ // expect(z.z.toJSONSchema(z.number().int())).toMatchInlineSnapshot(`
1967
+ // {
1968
+ // "maximum": 9007199254740991,
1969
+ // "minimum": -9007199254740991,
1970
+ // "type": "integer",
1971
+ // }
1972
+ // `);
1973
+ // expect(z.z.toJSONSchema(z.int())).toMatchInlineSnapshot(`
1974
+ // {
1975
+ // "maximum": 9007199254740991,
1976
+ // "minimum": -9007199254740991,
1977
+ // "type": "integer",
1978
+ // }
1979
+ // `);
1980
+ // expect(z.z.toJSONSchema(z.int().positive())).toMatchInlineSnapshot(`
1981
+ // {
1982
+ // "exclusiveMinimum": 0,
1983
+ // "maximum": 9007199254740991,
1984
+ // "minimum": -9007199254740991,
1985
+ // "type": "integer",
1986
+ // }
1987
+ // `);
1988
+ // expect(z.z.toJSONSchema(z.int().nonnegative())).toMatchInlineSnapshot(`
1989
+ // {
1990
+ // "maximum": 9007199254740991,
1991
+ // "minimum": 0,
1992
+ // "type": "integer",
1993
+ // }
1994
+ // `);
1995
+ // expect(z.z.toJSONSchema(z.int().gt(0))).toMatchInlineSnapshot(`
1996
+ // {
1997
+ // "exclusiveMinimum": 0,
1998
+ // "maximum": 9007199254740991,
1999
+ // "minimum": -9007199254740991,
2000
+ // "type": "integer",
2001
+ // }
2002
+ // `);
2003
+ // expect(z.z.toJSONSchema(z.int().gte(0))).toMatchInlineSnapshot(`
2004
+ // {
2005
+ // "maximum": 9007199254740991,
2006
+ // "minimum": 0,
2007
+ // "type": "integer",
2008
+ // }
2009
+ // `);
2010
+
2011
+ // });
2012
+
2013
+ test("use output type for preprocess", () => {
2014
+ const a = z.preprocess((val) => String(val), z.string());
2015
+
2016
+ expect(z.toJSONSchema(a, { io: "input" })).toMatchInlineSnapshot(`
2017
+ {
2018
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
2019
+ "type": "string",
2020
+ }
2021
+ `);
2022
+ });
2023
+
2024
+ // test("isTransforming", () => {
2025
+ // const tx = z.core.isTransforming;
2026
+ // expect(tx(z.string())).toEqual(false);
2027
+ // expect(tx(z.string().transform((val) => val))).toEqual(true);
2028
+ // expect(tx(z.string().pipe(z.string()))).toEqual(false);
2029
+ // expect(
2030
+ // tx(
2031
+ // z
2032
+ // .string()
2033
+ // .transform((val) => val)
2034
+ // .pipe(z.string())
2035
+ // )
2036
+ // ).toEqual(true);
2037
+
2038
+ // const a = z.transform((val) => val);
2039
+ // expect(tx(z.transform((val) => val))).toEqual(true);
2040
+ // expect(tx(a.optional())).toEqual(true);
2041
+
2042
+ // const b = z.string().optional();
2043
+ // expect(tx(b)).toEqual(false);
2044
+
2045
+ // const c = z.string().prefault("hello");
2046
+ // expect(tx(c)).toEqual(false);
2047
+
2048
+ // const d = z.string().default("hello");
2049
+ // expect(tx(d)).toEqual(false);
2050
+ // });
2051
+
2052
+ test("flatten simple intersections", () => {
2053
+ const FirstSchema = z.object({
2054
+ testNum: z.number(),
2055
+ });
2056
+
2057
+ const SecondSchema = z.object({
2058
+ testStr: z.string(),
2059
+ });
2060
+
2061
+ const ThirdSchema = z.object({
2062
+ testBool: z.boolean(),
2063
+ });
2064
+
2065
+ const HelloSchema = FirstSchema.and(SecondSchema).and(ThirdSchema).describe("123");
2066
+
2067
+ // Zod 3
2068
+ // console.log(JSON.stringify(zodToJsonSchema(HelloSchema), null, 2));
2069
+
2070
+ // Zod 4
2071
+ const result = z.toJSONSchema(HelloSchema, { target: "draft-7" });
2072
+ expect(result).toMatchInlineSnapshot(`
2073
+ {
2074
+ "$schema": "http://json-schema.org/draft-07/schema#",
2075
+ "allOf": [
2076
+ {
2077
+ "additionalProperties": false,
2078
+ "properties": {
2079
+ "testNum": {
2080
+ "type": "number",
2081
+ },
2082
+ },
2083
+ "required": [
2084
+ "testNum",
2085
+ ],
2086
+ "type": "object",
2087
+ },
2088
+ {
2089
+ "additionalProperties": false,
2090
+ "properties": {
2091
+ "testStr": {
2092
+ "type": "string",
2093
+ },
2094
+ },
2095
+ "required": [
2096
+ "testStr",
2097
+ ],
2098
+ "type": "object",
2099
+ },
2100
+ {
2101
+ "additionalProperties": false,
2102
+ "properties": {
2103
+ "testBool": {
2104
+ "type": "boolean",
2105
+ },
2106
+ },
2107
+ "required": [
2108
+ "testBool",
2109
+ ],
2110
+ "type": "object",
2111
+ },
2112
+ ],
2113
+ "description": "123",
2114
+ }
2115
+ `);
2116
+ });
2117
+
2118
+ test("z.file()", () => {
2119
+ const a = z.file();
2120
+ expect(z.toJSONSchema(a)).toMatchInlineSnapshot(`
2121
+ {
2122
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
2123
+ "contentEncoding": "binary",
2124
+ "format": "binary",
2125
+ "type": "string",
2126
+ }
2127
+ `);
2128
+
2129
+ const b = z.file().mime("image/png").min(1000).max(10000);
2130
+ expect(z.toJSONSchema(b)).toMatchInlineSnapshot(`
2131
+ {
2132
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
2133
+ "contentEncoding": "binary",
2134
+ "contentMediaType": "image/png",
2135
+ "format": "binary",
2136
+ "maxLength": 10000,
2137
+ "minLength": 1000,
2138
+ "type": "string",
2139
+ }
2140
+ `);
2141
+
2142
+ const c = z.file().mime(["image/png", "image/jpg"]).min(1000).max(10000);
2143
+ expect(z.toJSONSchema(c)).toMatchInlineSnapshot(`
2144
+ {
2145
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
2146
+ "anyOf": [
2147
+ {
2148
+ "contentEncoding": "binary",
2149
+ "contentMediaType": "image/png",
2150
+ "format": "binary",
2151
+ "maxLength": 10000,
2152
+ "minLength": 1000,
2153
+ "type": "string",
2154
+ },
2155
+ {
2156
+ "contentEncoding": "binary",
2157
+ "contentMediaType": "image/jpg",
2158
+ "format": "binary",
2159
+ "maxLength": 10000,
2160
+ "minLength": 1000,
2161
+ "type": "string",
2162
+ },
2163
+ ],
2164
+ }
2165
+ `);
2166
+ });
2167
+
2168
+ test("custom toJSONSchema", () => {
2169
+ const schema = z.instanceof(Date);
2170
+ schema._zod.toJSONSchema = () => ({
2171
+ type: "string",
2172
+ format: "date-time",
2173
+ });
2174
+
2175
+ expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
2176
+ {
2177
+ "$schema": "https://json-schema.org/draft/2020-12/schema",
2178
+ "format": "date-time",
2179
+ "type": "string",
2180
+ }
2181
+ `);
2182
+ });