zod 4.2.0-canary.20251207T223211 → 4.2.0-canary.20251213T203150

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 (39) hide show
  1. package/package.json +1 -1
  2. package/src/v4/classic/schemas.ts +97 -5
  3. package/src/v4/classic/tests/json.test.ts +4 -3
  4. package/src/v4/classic/tests/standard-schema.test.ts +77 -0
  5. package/src/v4/classic/tests/to-json-schema-methods.test.ts +438 -0
  6. package/src/v4/classic/tests/to-json-schema.test.ts +66 -30
  7. package/src/v4/core/index.ts +2 -0
  8. package/src/v4/core/json-schema-generator.ts +124 -0
  9. package/src/v4/core/json-schema-processors.ts +630 -0
  10. package/src/v4/core/schemas.ts +8 -13
  11. package/src/v4/core/standard-schema.ts +114 -19
  12. package/src/v4/core/to-json-schema.ts +373 -827
  13. package/src/v4/mini/tests/standard-schema.test.ts +17 -0
  14. package/v4/classic/schemas.cjs +48 -0
  15. package/v4/classic/schemas.d.cts +35 -0
  16. package/v4/classic/schemas.d.ts +35 -0
  17. package/v4/classic/schemas.js +48 -0
  18. package/v4/core/index.cjs +5 -1
  19. package/v4/core/index.d.cts +2 -0
  20. package/v4/core/index.d.ts +2 -0
  21. package/v4/core/index.js +2 -0
  22. package/v4/core/json-schema-generator.cjs +99 -0
  23. package/v4/core/json-schema-generator.d.cts +64 -0
  24. package/v4/core/json-schema-generator.d.ts +64 -0
  25. package/v4/core/json-schema-generator.js +95 -0
  26. package/v4/core/json-schema-processors.cjs +617 -0
  27. package/v4/core/json-schema-processors.d.cts +49 -0
  28. package/v4/core/json-schema-processors.d.ts +49 -0
  29. package/v4/core/json-schema-processors.js +574 -0
  30. package/v4/core/schemas.cjs +0 -10
  31. package/v4/core/schemas.d.cts +4 -1
  32. package/v4/core/schemas.d.ts +4 -1
  33. package/v4/core/schemas.js +0 -10
  34. package/v4/core/standard-schema.d.cts +90 -19
  35. package/v4/core/standard-schema.d.ts +90 -19
  36. package/v4/core/to-json-schema.cjs +302 -793
  37. package/v4/core/to-json-schema.d.cts +56 -33
  38. package/v4/core/to-json-schema.d.ts +56 -33
  39. package/v4/core/to-json-schema.js +296 -791
@@ -0,0 +1,438 @@
1
+ import { describe, expect, test } from "vitest";
2
+ import * as z from "zod/v4";
3
+
4
+ // Utility functions
5
+ function expectMethodMatch(schema: z.ZodType, params?: z.core.ToJSONSchemaParams): void {
6
+ const staticResult = z.toJSONSchema(schema, params);
7
+ const methodResult = schema.toJSONSchema(params);
8
+ expect(methodResult).toEqual(staticResult);
9
+ }
10
+
11
+ describe("toJSONSchema method", () => {
12
+ describe("primitive types", () => {
13
+ test("string", () => {
14
+ expectMethodMatch(z.string());
15
+ });
16
+
17
+ test("number", () => {
18
+ expectMethodMatch(z.number());
19
+ });
20
+
21
+ test("boolean", () => {
22
+ expectMethodMatch(z.boolean());
23
+ });
24
+
25
+ test("bigint", () => {
26
+ expectMethodMatch(z.bigint(), { unrepresentable: "any" });
27
+ });
28
+
29
+ test("symbol", () => {
30
+ expectMethodMatch(z.symbol(), { unrepresentable: "any" });
31
+ });
32
+
33
+ test("null", () => {
34
+ expectMethodMatch(z.null());
35
+ });
36
+
37
+ test("undefined", () => {
38
+ expectMethodMatch(z.undefined(), { unrepresentable: "any" });
39
+ });
40
+
41
+ test("void", () => {
42
+ expectMethodMatch(z.void(), { unrepresentable: "any" });
43
+ });
44
+
45
+ test("never", () => {
46
+ expectMethodMatch(z.never());
47
+ });
48
+
49
+ test("any", () => {
50
+ expectMethodMatch(z.any());
51
+ });
52
+
53
+ test("unknown", () => {
54
+ expectMethodMatch(z.unknown());
55
+ });
56
+
57
+ test("date", () => {
58
+ expectMethodMatch(z.date(), { unrepresentable: "any" });
59
+ });
60
+
61
+ test("nan", () => {
62
+ expectMethodMatch(z.nan(), { unrepresentable: "any" });
63
+ });
64
+ });
65
+
66
+ describe("string formats", () => {
67
+ test("email", () => {
68
+ expectMethodMatch(z.email());
69
+ });
70
+
71
+ test("url", () => {
72
+ expectMethodMatch(z.url());
73
+ });
74
+
75
+ test("uuid", () => {
76
+ expectMethodMatch(z.uuid());
77
+ });
78
+
79
+ test("datetime", () => {
80
+ expectMethodMatch(z.iso.datetime());
81
+ });
82
+
83
+ test("date", () => {
84
+ expectMethodMatch(z.iso.date());
85
+ });
86
+
87
+ test("guid", () => {
88
+ expectMethodMatch(z.guid());
89
+ });
90
+
91
+ test("cuid", () => {
92
+ expectMethodMatch(z.cuid());
93
+ });
94
+
95
+ test("cuid2", () => {
96
+ expectMethodMatch(z.cuid2());
97
+ });
98
+
99
+ test("ulid", () => {
100
+ expectMethodMatch(z.ulid());
101
+ });
102
+
103
+ test("base64", () => {
104
+ expectMethodMatch(z.base64());
105
+ });
106
+
107
+ test("ipv4", () => {
108
+ expectMethodMatch(z.ipv4());
109
+ });
110
+
111
+ test("ipv6", () => {
112
+ expectMethodMatch(z.ipv6());
113
+ });
114
+ });
115
+
116
+ describe("string validations", () => {
117
+ test("min length", () => {
118
+ expectMethodMatch(z.string().min(5));
119
+ });
120
+
121
+ test("max length", () => {
122
+ expectMethodMatch(z.string().max(10));
123
+ });
124
+
125
+ test("length", () => {
126
+ expectMethodMatch(z.string().length(5));
127
+ });
128
+
129
+ test("regex", () => {
130
+ expectMethodMatch(z.string().regex(/^[A-Z]+$/));
131
+ });
132
+
133
+ test("multiple patterns", () => {
134
+ expectMethodMatch(
135
+ z
136
+ .string()
137
+ .regex(/^[A-Z]+$/)
138
+ .regex(/^[0-9]+$/)
139
+ );
140
+ });
141
+
142
+ test("startsWith", () => {
143
+ expectMethodMatch(z.string().startsWith("hello"));
144
+ });
145
+
146
+ test("endsWith", () => {
147
+ expectMethodMatch(z.string().endsWith("world"));
148
+ });
149
+
150
+ test("includes", () => {
151
+ expectMethodMatch(z.string().includes("test"));
152
+ });
153
+ });
154
+
155
+ describe("number validations", () => {
156
+ test("min", () => {
157
+ expectMethodMatch(z.number().min(5));
158
+ });
159
+
160
+ test("max", () => {
161
+ expectMethodMatch(z.number().max(10));
162
+ });
163
+
164
+ test("int", () => {
165
+ expectMethodMatch(z.int());
166
+ });
167
+
168
+ test("positive", () => {
169
+ expectMethodMatch(z.number().positive());
170
+ });
171
+
172
+ test("negative", () => {
173
+ expectMethodMatch(z.number().negative());
174
+ });
175
+
176
+ test("multipleOf", () => {
177
+ expectMethodMatch(z.number().multipleOf(2));
178
+ });
179
+
180
+ test("gte", () => {
181
+ expectMethodMatch(z.number().gte(5));
182
+ });
183
+
184
+ test("lte", () => {
185
+ expectMethodMatch(z.number().lte(10));
186
+ });
187
+
188
+ test("gt", () => {
189
+ expectMethodMatch(z.number().gt(5));
190
+ });
191
+
192
+ test("lt", () => {
193
+ expectMethodMatch(z.number().lt(10));
194
+ });
195
+ });
196
+
197
+ describe("literals and enums", () => {
198
+ test("literal string", () => {
199
+ expectMethodMatch(z.literal("hello"));
200
+ });
201
+
202
+ test("literal number", () => {
203
+ expectMethodMatch(z.literal(42));
204
+ });
205
+
206
+ test("literal boolean", () => {
207
+ expectMethodMatch(z.literal(true));
208
+ });
209
+
210
+ test("literal null", () => {
211
+ expectMethodMatch(z.literal(null));
212
+ });
213
+
214
+ test("multiple literals", () => {
215
+ expectMethodMatch(z.literal(["a", "b", "c"]));
216
+ });
217
+
218
+ test("enum", () => {
219
+ expectMethodMatch(z.enum(["red", "green", "blue"]));
220
+ });
221
+
222
+ test("nativeEnum", () => {
223
+ enum Colors {
224
+ Red = "red",
225
+ Green = "green",
226
+ Blue = "blue",
227
+ }
228
+ expectMethodMatch(z.nativeEnum(Colors));
229
+ });
230
+ });
231
+
232
+ describe("composite types", () => {
233
+ test("array", () => {
234
+ expectMethodMatch(z.array(z.string()));
235
+ });
236
+
237
+ test("array with min", () => {
238
+ expectMethodMatch(z.array(z.string()).min(2));
239
+ });
240
+
241
+ test("array with max", () => {
242
+ expectMethodMatch(z.array(z.string()).max(10));
243
+ });
244
+
245
+ test("object", () => {
246
+ expectMethodMatch(z.object({ name: z.string(), age: z.number() }));
247
+ });
248
+
249
+ test("object with optional", () => {
250
+ expectMethodMatch(z.object({ name: z.string(), age: z.number().optional() }));
251
+ });
252
+
253
+ test("strict object", () => {
254
+ expectMethodMatch(z.strictObject({ name: z.string() }));
255
+ });
256
+
257
+ test("loose object", () => {
258
+ expectMethodMatch(z.looseObject({ name: z.string() }));
259
+ });
260
+
261
+ test("object with catchall", () => {
262
+ expectMethodMatch(z.object({ name: z.string() }).catchall(z.string()));
263
+ });
264
+
265
+ test("tuple", () => {
266
+ expectMethodMatch(z.tuple([z.string(), z.number()]));
267
+ });
268
+
269
+ test("tuple with rest", () => {
270
+ expectMethodMatch(z.tuple([z.string()], z.number()));
271
+ });
272
+
273
+ test("record", () => {
274
+ expectMethodMatch(z.record(z.string(), z.number()));
275
+ });
276
+
277
+ test("union", () => {
278
+ expectMethodMatch(z.union([z.string(), z.number()]));
279
+ });
280
+
281
+ test("discriminated union", () => {
282
+ expectMethodMatch(
283
+ z.discriminatedUnion("type", [
284
+ z.object({ type: z.literal("a"), value: z.string() }),
285
+ z.object({ type: z.literal("b"), value: z.number() }),
286
+ ])
287
+ );
288
+ });
289
+
290
+ test("intersection", () => {
291
+ expectMethodMatch(z.intersection(z.object({ a: z.string() }), z.object({ b: z.number() })));
292
+ });
293
+ });
294
+
295
+ describe("wrapper types", () => {
296
+ test("optional", () => {
297
+ expectMethodMatch(z.string().optional());
298
+ });
299
+
300
+ test("nullable", () => {
301
+ expectMethodMatch(z.string().nullable());
302
+ });
303
+
304
+ test("nullish", () => {
305
+ expectMethodMatch(z.string().nullish());
306
+ });
307
+
308
+ test("default", () => {
309
+ expectMethodMatch(z.string().default("hello"));
310
+ });
311
+
312
+ test("default function", () => {
313
+ expectMethodMatch(z.string().default(() => "hello"));
314
+ });
315
+
316
+ test("prefault", () => {
317
+ expectMethodMatch(z.string().prefault("hello"));
318
+ });
319
+
320
+ test("prefault function", () => {
321
+ expectMethodMatch(z.string().prefault(() => "hello"));
322
+ });
323
+
324
+ test("catch", () => {
325
+ expectMethodMatch(z.string().catch("hello"));
326
+ });
327
+
328
+ test("readonly", () => {
329
+ expectMethodMatch(z.string().readonly());
330
+ });
331
+
332
+ test("nonoptional", () => {
333
+ expectMethodMatch(z.string().optional().nonoptional());
334
+ });
335
+ });
336
+
337
+ describe("special types", () => {
338
+ test("lazy", () => {
339
+ type Node = {
340
+ value: string;
341
+ children?: Node[] | undefined;
342
+ };
343
+ const Node: z.ZodType<Node> = z.lazy(() =>
344
+ z.object({
345
+ value: z.string(),
346
+ children: z.array(Node).optional(),
347
+ })
348
+ ) as z.ZodType<Node>;
349
+ expectMethodMatch(Node);
350
+ });
351
+
352
+ test("promise", () => {
353
+ expectMethodMatch(z.promise(z.string()));
354
+ });
355
+
356
+ test("pipe", () => {
357
+ expectMethodMatch(
358
+ z
359
+ .string()
360
+ .transform((val) => val.length)
361
+ .pipe(z.number())
362
+ );
363
+ });
364
+
365
+ test("transform", () => {
366
+ expectMethodMatch(
367
+ z.string().transform((val) => val.length),
368
+ { unrepresentable: "any" }
369
+ );
370
+ });
371
+
372
+ test("file", () => {
373
+ expectMethodMatch(z.file());
374
+ });
375
+
376
+ test("file with mime", () => {
377
+ expectMethodMatch(z.file().mime("image/png"));
378
+ });
379
+ });
380
+
381
+ describe("parameters", () => {
382
+ test("target draft-7", () => {
383
+ expectMethodMatch(z.string(), { target: "draft-7" });
384
+ });
385
+
386
+ test("target draft-4", () => {
387
+ expectMethodMatch(z.string(), { target: "draft-4" });
388
+ });
389
+
390
+ test("target openapi-3.0", () => {
391
+ expectMethodMatch(z.string(), { target: "openapi-3.0" });
392
+ });
393
+
394
+ test("io input", () => {
395
+ expectMethodMatch(z.string().default("hello"), { io: "input" });
396
+ });
397
+
398
+ test("cycles throw", () => {
399
+ const schema = z.object({
400
+ name: z.string(),
401
+ get subcategories() {
402
+ return z.array(schema);
403
+ },
404
+ });
405
+ // Both should throw the same error
406
+ expect(() => z.toJSONSchema(schema, { cycles: "throw" })).toThrow();
407
+ expect(() => schema.toJSONSchema({ cycles: "throw" })).toThrow();
408
+ });
409
+
410
+ test("reused ref", () => {
411
+ const shared = z.string();
412
+ const schema = z.object({
413
+ a: shared,
414
+ b: shared,
415
+ });
416
+ expectMethodMatch(schema, { reused: "ref" });
417
+ });
418
+ });
419
+
420
+ describe("edge cases with metadata", () => {
421
+ test("schema with id metadata", () => {
422
+ const a = z.string().meta({ id: "hi" });
423
+ expectMethodMatch(a);
424
+ });
425
+
426
+ test("schema with id then additional metadata", () => {
427
+ const a = z.string().meta({ id: "hi2" });
428
+ const b = a.meta({ name: "asdf" });
429
+ expectMethodMatch(b);
430
+ });
431
+
432
+ test("nested schema with id", () => {
433
+ const inner = z.string().meta({ id: "inner" });
434
+ const outer = z.object({ value: inner });
435
+ expectMethodMatch(outer);
436
+ });
437
+ });
438
+ });