zod 4.1.1 → 4.1.2
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.
- package/package.json +1 -1
- package/src/v4/classic/external.ts +1 -0
- package/src/v4/classic/tests/codec-examples.test.ts +177 -142
- package/src/v4/classic/tests/to-json-schema.test.ts +58 -0
- package/src/v4/core/to-json-schema.ts +20 -18
- package/src/v4/core/versions.ts +1 -1
- package/src/v4/mini/external.ts +1 -0
- package/v4/classic/external.cjs +2 -1
- package/v4/classic/external.d.cts +1 -1
- package/v4/classic/external.d.ts +1 -1
- package/v4/classic/external.js +1 -1
- package/v4/core/to-json-schema.cjs +20 -17
- package/v4/core/to-json-schema.js +20 -17
- package/v4/core/versions.cjs +1 -1
- package/v4/core/versions.js +1 -1
- package/v4/mini/external.cjs +2 -1
- package/v4/mini/external.d.cts +1 -1
- package/v4/mini/external.d.ts +1 -1
- package/v4/mini/external.js +1 -1
package/package.json
CHANGED
|
@@ -2,7 +2,7 @@ import { expect, test } from "vitest";
|
|
|
2
2
|
import * as z from "zod/v4";
|
|
3
3
|
|
|
4
4
|
// ============================================================================
|
|
5
|
-
//
|
|
5
|
+
// stringToNumber
|
|
6
6
|
// ============================================================================
|
|
7
7
|
|
|
8
8
|
const stringToNumber = () =>
|
|
@@ -11,128 +11,6 @@ const stringToNumber = () =>
|
|
|
11
11
|
encode: (num) => num.toString(),
|
|
12
12
|
});
|
|
13
13
|
|
|
14
|
-
const stringToInt = () =>
|
|
15
|
-
z.codec(z.string(), z.int(), {
|
|
16
|
-
decode: (str) => Number.parseInt(str, 10),
|
|
17
|
-
encode: (num) => num.toString(),
|
|
18
|
-
});
|
|
19
|
-
|
|
20
|
-
const stringToBigInt = () =>
|
|
21
|
-
z.codec(z.string(), z.bigint(), {
|
|
22
|
-
decode: (str) => BigInt(str),
|
|
23
|
-
encode: (bigint) => bigint.toString(),
|
|
24
|
-
});
|
|
25
|
-
|
|
26
|
-
const numberToBigInt = () =>
|
|
27
|
-
z.codec(z.int(), z.bigint(), {
|
|
28
|
-
decode: (num) => BigInt(num),
|
|
29
|
-
encode: (bigint) => Number(bigint),
|
|
30
|
-
});
|
|
31
|
-
|
|
32
|
-
// ============================================================================
|
|
33
|
-
// Date/Duration Codecs
|
|
34
|
-
// ============================================================================
|
|
35
|
-
|
|
36
|
-
const isoDatetimeToDate = () =>
|
|
37
|
-
z.codec(z.iso.datetime(), z.date(), {
|
|
38
|
-
decode: (isoString) => new Date(isoString),
|
|
39
|
-
encode: (date) => date.toISOString(),
|
|
40
|
-
});
|
|
41
|
-
|
|
42
|
-
const epochSecondsToDate = () =>
|
|
43
|
-
z.codec(z.int().min(0), z.date(), {
|
|
44
|
-
decode: (seconds) => new Date(seconds * 1000),
|
|
45
|
-
encode: (date) => Math.floor(date.getTime() / 1000),
|
|
46
|
-
});
|
|
47
|
-
|
|
48
|
-
const epochMillisToDate = () =>
|
|
49
|
-
z.codec(z.int().min(0), z.date(), {
|
|
50
|
-
decode: (millis) => new Date(millis),
|
|
51
|
-
encode: (date) => date.getTime(),
|
|
52
|
-
});
|
|
53
|
-
|
|
54
|
-
// ============================================================================
|
|
55
|
-
// JSON Codec
|
|
56
|
-
// ============================================================================
|
|
57
|
-
|
|
58
|
-
const json = <T extends z.ZodTypeAny>(schema: T) =>
|
|
59
|
-
z.codec(z.string(), schema, {
|
|
60
|
-
decode: (jsonString) => JSON.parse(jsonString),
|
|
61
|
-
encode: (value) => JSON.stringify(value),
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
// ============================================================================
|
|
65
|
-
// Text/Bytes Codecs
|
|
66
|
-
// ============================================================================
|
|
67
|
-
|
|
68
|
-
const utf8ToBytes = () =>
|
|
69
|
-
z.codec(z.string(), z.instanceof(Uint8Array), {
|
|
70
|
-
decode: (str) => new TextEncoder().encode(str),
|
|
71
|
-
encode: (bytes) => new TextDecoder().decode(bytes),
|
|
72
|
-
});
|
|
73
|
-
|
|
74
|
-
const bytesToUtf8 = () =>
|
|
75
|
-
z.codec(z.instanceof(Uint8Array), z.string(), {
|
|
76
|
-
decode: (bytes) => new TextDecoder().decode(bytes),
|
|
77
|
-
encode: (str) => new TextEncoder().encode(str),
|
|
78
|
-
});
|
|
79
|
-
|
|
80
|
-
// ============================================================================
|
|
81
|
-
// Binary-to-text Codecs
|
|
82
|
-
// ============================================================================
|
|
83
|
-
|
|
84
|
-
// Using utility functions from z.core.util
|
|
85
|
-
|
|
86
|
-
const base64 = () =>
|
|
87
|
-
z.codec(z.base64(), z.instanceof(Uint8Array), {
|
|
88
|
-
decode: (base64String) => z.core.util.base64ToUint8Array(base64String),
|
|
89
|
-
encode: (bytes) => z.core.util.uint8ArrayToBase64(bytes),
|
|
90
|
-
});
|
|
91
|
-
|
|
92
|
-
const base64urlToBytes = () =>
|
|
93
|
-
z.codec(z.base64url(), z.instanceof(Uint8Array), {
|
|
94
|
-
decode: (base64urlString) => z.core.util.base64urlToUint8Array(base64urlString),
|
|
95
|
-
encode: (bytes) => z.core.util.uint8ArrayToBase64url(bytes),
|
|
96
|
-
});
|
|
97
|
-
|
|
98
|
-
const hexToBytes = () =>
|
|
99
|
-
z.codec(z.hex(), z.instanceof(Uint8Array), {
|
|
100
|
-
decode: (hexString) => z.core.util.hexToUint8Array(hexString),
|
|
101
|
-
encode: (bytes) => z.core.util.uint8ArrayToHex(bytes),
|
|
102
|
-
});
|
|
103
|
-
|
|
104
|
-
// ============================================================================
|
|
105
|
-
// URL Codecs
|
|
106
|
-
// ============================================================================
|
|
107
|
-
|
|
108
|
-
const stringToURL = () =>
|
|
109
|
-
z.codec(z.url(), z.instanceof(URL), {
|
|
110
|
-
decode: (urlString) => new URL(urlString),
|
|
111
|
-
encode: (url) => url.href,
|
|
112
|
-
});
|
|
113
|
-
|
|
114
|
-
const stringToHttpURL = () =>
|
|
115
|
-
z.codec(z.httpUrl(), z.instanceof(URL), {
|
|
116
|
-
decode: (urlString) => new URL(urlString),
|
|
117
|
-
encode: (url) => url.href,
|
|
118
|
-
});
|
|
119
|
-
|
|
120
|
-
const uriComponent = () =>
|
|
121
|
-
z.codec(z.string(), z.string(), {
|
|
122
|
-
decode: (encodedString) => decodeURIComponent(encodedString),
|
|
123
|
-
encode: (decodedString) => encodeURIComponent(decodedString),
|
|
124
|
-
});
|
|
125
|
-
|
|
126
|
-
// ============================================================================
|
|
127
|
-
// Boolean Codec
|
|
128
|
-
// ============================================================================
|
|
129
|
-
|
|
130
|
-
const stringToBoolean = (options?: { truthy?: string[]; falsy?: string[] }) => z.stringbool(options);
|
|
131
|
-
|
|
132
|
-
// ============================================================================
|
|
133
|
-
// Tests
|
|
134
|
-
// ============================================================================
|
|
135
|
-
|
|
136
14
|
test("stringToNumber codec", () => {
|
|
137
15
|
const codec = stringToNumber();
|
|
138
16
|
|
|
@@ -152,6 +30,16 @@ test("stringToNumber codec", () => {
|
|
|
152
30
|
expect(roundTrip).toBe("3.14159");
|
|
153
31
|
});
|
|
154
32
|
|
|
33
|
+
// ============================================================================
|
|
34
|
+
// stringToInt
|
|
35
|
+
// ============================================================================
|
|
36
|
+
|
|
37
|
+
const stringToInt = () =>
|
|
38
|
+
z.codec(z.string(), z.int(), {
|
|
39
|
+
decode: (str) => Number.parseInt(str, 10),
|
|
40
|
+
encode: (num) => num.toString(),
|
|
41
|
+
});
|
|
42
|
+
|
|
155
43
|
test("stringToInt codec", () => {
|
|
156
44
|
const codec = stringToInt();
|
|
157
45
|
|
|
@@ -171,6 +59,16 @@ test("stringToInt codec", () => {
|
|
|
171
59
|
expect(roundTrip).toBe("999");
|
|
172
60
|
});
|
|
173
61
|
|
|
62
|
+
// ============================================================================
|
|
63
|
+
// stringToBigInt
|
|
64
|
+
// ============================================================================
|
|
65
|
+
|
|
66
|
+
const stringToBigInt = () =>
|
|
67
|
+
z.codec(z.string(), z.bigint(), {
|
|
68
|
+
decode: (str) => BigInt(str),
|
|
69
|
+
encode: (bigint) => bigint.toString(),
|
|
70
|
+
});
|
|
71
|
+
|
|
174
72
|
test("stringToBigInt codec", () => {
|
|
175
73
|
const codec = stringToBigInt();
|
|
176
74
|
|
|
@@ -190,6 +88,16 @@ test("stringToBigInt codec", () => {
|
|
|
190
88
|
expect(roundTrip).toBe("987654321098765432109876543210");
|
|
191
89
|
});
|
|
192
90
|
|
|
91
|
+
// ============================================================================
|
|
92
|
+
// numberToBigInt
|
|
93
|
+
// ============================================================================
|
|
94
|
+
|
|
95
|
+
const numberToBigInt = () =>
|
|
96
|
+
z.codec(z.int(), z.bigint(), {
|
|
97
|
+
decode: (num) => BigInt(num),
|
|
98
|
+
encode: (bigint) => Number(bigint),
|
|
99
|
+
});
|
|
100
|
+
|
|
193
101
|
test("numberToBigInt codec", () => {
|
|
194
102
|
const codec = numberToBigInt();
|
|
195
103
|
|
|
@@ -209,6 +117,16 @@ test("numberToBigInt codec", () => {
|
|
|
209
117
|
expect(roundTrip).toBe(999);
|
|
210
118
|
});
|
|
211
119
|
|
|
120
|
+
// ============================================================================
|
|
121
|
+
// isoDatetimeToDate
|
|
122
|
+
// ============================================================================
|
|
123
|
+
|
|
124
|
+
const isoDatetimeToDate = () =>
|
|
125
|
+
z.codec(z.iso.datetime(), z.date(), {
|
|
126
|
+
decode: (isoString) => new Date(isoString),
|
|
127
|
+
encode: (date) => date.toISOString(),
|
|
128
|
+
});
|
|
129
|
+
|
|
212
130
|
test("isoDatetimeToDate codec", () => {
|
|
213
131
|
const codec = isoDatetimeToDate();
|
|
214
132
|
|
|
@@ -227,6 +145,16 @@ test("isoDatetimeToDate codec", () => {
|
|
|
227
145
|
expect(roundTrip).toBe("2024-12-25T15:45:30.123Z");
|
|
228
146
|
});
|
|
229
147
|
|
|
148
|
+
// ============================================================================
|
|
149
|
+
// epochSecondsToDate
|
|
150
|
+
// ============================================================================
|
|
151
|
+
|
|
152
|
+
const epochSecondsToDate = () =>
|
|
153
|
+
z.codec(z.int().min(0), z.date(), {
|
|
154
|
+
decode: (seconds) => new Date(seconds * 1000),
|
|
155
|
+
encode: (date) => Math.floor(date.getTime() / 1000),
|
|
156
|
+
});
|
|
157
|
+
|
|
230
158
|
test("epochSecondsToDate codec", () => {
|
|
231
159
|
const codec = epochSecondsToDate();
|
|
232
160
|
|
|
@@ -245,6 +173,16 @@ test("epochSecondsToDate codec", () => {
|
|
|
245
173
|
expect(roundTrip).toBe(1640995200);
|
|
246
174
|
});
|
|
247
175
|
|
|
176
|
+
// ============================================================================
|
|
177
|
+
// epochMillisToDate
|
|
178
|
+
// ============================================================================
|
|
179
|
+
|
|
180
|
+
const epochMillisToDate = () =>
|
|
181
|
+
z.codec(z.int().min(0), z.date(), {
|
|
182
|
+
decode: (millis) => new Date(millis),
|
|
183
|
+
encode: (date) => date.getTime(),
|
|
184
|
+
});
|
|
185
|
+
|
|
248
186
|
test("epochMillisToDate codec", () => {
|
|
249
187
|
const codec = epochMillisToDate();
|
|
250
188
|
|
|
@@ -263,8 +201,30 @@ test("epochMillisToDate codec", () => {
|
|
|
263
201
|
expect(roundTrip).toBe(1640995200123);
|
|
264
202
|
});
|
|
265
203
|
|
|
204
|
+
// ============================================================================
|
|
205
|
+
// json
|
|
206
|
+
// ============================================================================
|
|
207
|
+
|
|
208
|
+
const jsonCodec = <T extends z.core.$ZodType>(schema: T) =>
|
|
209
|
+
z.codec(z.string(), schema, {
|
|
210
|
+
decode: (jsonString, ctx) => {
|
|
211
|
+
try {
|
|
212
|
+
return JSON.parse(jsonString);
|
|
213
|
+
} catch (err: any) {
|
|
214
|
+
ctx.issues.push({
|
|
215
|
+
code: "invalid_format",
|
|
216
|
+
format: "json",
|
|
217
|
+
input: jsonString,
|
|
218
|
+
message: err.message,
|
|
219
|
+
});
|
|
220
|
+
return z.NEVER;
|
|
221
|
+
}
|
|
222
|
+
},
|
|
223
|
+
encode: (value) => JSON.stringify(value),
|
|
224
|
+
});
|
|
225
|
+
|
|
266
226
|
test("json codec", () => {
|
|
267
|
-
const codec =
|
|
227
|
+
const codec = jsonCodec(z.object({ name: z.string(), age: z.number() }));
|
|
268
228
|
|
|
269
229
|
// Test decode
|
|
270
230
|
const decoded = z.decode(codec, '{"name":"Alice","age":30}');
|
|
@@ -281,6 +241,16 @@ test("json codec", () => {
|
|
|
281
241
|
expect(JSON.parse(roundTrip)).toEqual(JSON.parse(original));
|
|
282
242
|
});
|
|
283
243
|
|
|
244
|
+
// ============================================================================
|
|
245
|
+
// utf8ToBytes
|
|
246
|
+
// ============================================================================
|
|
247
|
+
|
|
248
|
+
const utf8ToBytes = () =>
|
|
249
|
+
z.codec(z.string(), z.instanceof(Uint8Array), {
|
|
250
|
+
decode: (str) => new TextEncoder().encode(str),
|
|
251
|
+
encode: (bytes) => new TextDecoder().decode(bytes),
|
|
252
|
+
});
|
|
253
|
+
|
|
284
254
|
test("utf8ToBytes codec", () => {
|
|
285
255
|
const codec = utf8ToBytes();
|
|
286
256
|
|
|
@@ -299,6 +269,16 @@ test("utf8ToBytes codec", () => {
|
|
|
299
269
|
expect(roundTrip).toBe(original);
|
|
300
270
|
});
|
|
301
271
|
|
|
272
|
+
// ============================================================================
|
|
273
|
+
// bytesToUtf8
|
|
274
|
+
// ============================================================================
|
|
275
|
+
|
|
276
|
+
const bytesToUtf8 = () =>
|
|
277
|
+
z.codec(z.instanceof(Uint8Array), z.string(), {
|
|
278
|
+
decode: (bytes) => new TextDecoder().decode(bytes),
|
|
279
|
+
encode: (str) => new TextEncoder().encode(str),
|
|
280
|
+
});
|
|
281
|
+
|
|
302
282
|
test("bytesToUtf8 codec", () => {
|
|
303
283
|
const codec = bytesToUtf8();
|
|
304
284
|
|
|
@@ -318,6 +298,16 @@ test("bytesToUtf8 codec", () => {
|
|
|
318
298
|
expect(roundTrip).toEqual(original);
|
|
319
299
|
});
|
|
320
300
|
|
|
301
|
+
// ============================================================================
|
|
302
|
+
// base64
|
|
303
|
+
// ============================================================================
|
|
304
|
+
|
|
305
|
+
const base64 = () =>
|
|
306
|
+
z.codec(z.base64(), z.instanceof(Uint8Array), {
|
|
307
|
+
decode: (base64String) => z.util.base64ToUint8Array(base64String),
|
|
308
|
+
encode: (bytes) => z.util.uint8ArrayToBase64(bytes),
|
|
309
|
+
});
|
|
310
|
+
|
|
321
311
|
test("base64 codec", () => {
|
|
322
312
|
const codec = base64();
|
|
323
313
|
|
|
@@ -336,6 +326,16 @@ test("base64 codec", () => {
|
|
|
336
326
|
expect(roundTrip).toBe(original);
|
|
337
327
|
});
|
|
338
328
|
|
|
329
|
+
// ============================================================================
|
|
330
|
+
// base64urlToBytes
|
|
331
|
+
// ============================================================================
|
|
332
|
+
|
|
333
|
+
const base64urlToBytes = () =>
|
|
334
|
+
z.codec(z.base64url(), z.instanceof(Uint8Array), {
|
|
335
|
+
decode: (base64urlString) => z.util.base64urlToUint8Array(base64urlString),
|
|
336
|
+
encode: (bytes) => z.util.uint8ArrayToBase64url(bytes),
|
|
337
|
+
});
|
|
338
|
+
|
|
339
339
|
test("base64urlToBytes codec", () => {
|
|
340
340
|
const codec = base64urlToBytes();
|
|
341
341
|
|
|
@@ -354,6 +354,16 @@ test("base64urlToBytes codec", () => {
|
|
|
354
354
|
expect(roundTrip).toBe(original);
|
|
355
355
|
});
|
|
356
356
|
|
|
357
|
+
// ============================================================================
|
|
358
|
+
// hexToBytes
|
|
359
|
+
// ============================================================================
|
|
360
|
+
|
|
361
|
+
const hexToBytes = () =>
|
|
362
|
+
z.codec(z.hex(), z.instanceof(Uint8Array), {
|
|
363
|
+
decode: (hexString) => z.util.hexToUint8Array(hexString),
|
|
364
|
+
encode: (bytes) => z.util.uint8ArrayToHex(bytes),
|
|
365
|
+
});
|
|
366
|
+
|
|
357
367
|
test("hexToBytes codec", () => {
|
|
358
368
|
const codec = hexToBytes();
|
|
359
369
|
|
|
@@ -376,6 +386,16 @@ test("hexToBytes codec", () => {
|
|
|
376
386
|
expect(roundTrip).toBe("deadbeef");
|
|
377
387
|
});
|
|
378
388
|
|
|
389
|
+
// ============================================================================
|
|
390
|
+
// stringToURL
|
|
391
|
+
// ============================================================================
|
|
392
|
+
|
|
393
|
+
const stringToURL = () =>
|
|
394
|
+
z.codec(z.url(), z.instanceof(URL), {
|
|
395
|
+
decode: (urlString) => new URL(urlString),
|
|
396
|
+
encode: (url) => url.href,
|
|
397
|
+
});
|
|
398
|
+
|
|
379
399
|
test("stringToURL codec", () => {
|
|
380
400
|
const codec = stringToURL();
|
|
381
401
|
|
|
@@ -396,6 +416,16 @@ test("stringToURL codec", () => {
|
|
|
396
416
|
expect(roundTrip).toBe(original);
|
|
397
417
|
});
|
|
398
418
|
|
|
419
|
+
// ============================================================================
|
|
420
|
+
// stringToHttpURL
|
|
421
|
+
// ============================================================================
|
|
422
|
+
|
|
423
|
+
const stringToHttpURL = () =>
|
|
424
|
+
z.codec(z.httpUrl(), z.instanceof(URL), {
|
|
425
|
+
decode: (urlString) => new URL(urlString),
|
|
426
|
+
encode: (url) => url.href,
|
|
427
|
+
});
|
|
428
|
+
|
|
399
429
|
test("stringToHttpURL codec", () => {
|
|
400
430
|
const codec = stringToHttpURL();
|
|
401
431
|
|
|
@@ -419,6 +449,16 @@ test("stringToHttpURL codec", () => {
|
|
|
419
449
|
expect(roundTrip).toBe(original);
|
|
420
450
|
});
|
|
421
451
|
|
|
452
|
+
// ============================================================================
|
|
453
|
+
// uriComponent
|
|
454
|
+
// ============================================================================
|
|
455
|
+
|
|
456
|
+
const uriComponent = () =>
|
|
457
|
+
z.codec(z.string(), z.string(), {
|
|
458
|
+
decode: (encodedString) => decodeURIComponent(encodedString),
|
|
459
|
+
encode: (decodedString) => encodeURIComponent(decodedString),
|
|
460
|
+
});
|
|
461
|
+
|
|
422
462
|
test("uriComponent codec", () => {
|
|
423
463
|
const codec = uriComponent();
|
|
424
464
|
|
|
@@ -442,6 +482,12 @@ test("uriComponent codec", () => {
|
|
|
442
482
|
expect(decodedComplex).toBe(complex);
|
|
443
483
|
});
|
|
444
484
|
|
|
485
|
+
// ============================================================================
|
|
486
|
+
// stringToBoolean
|
|
487
|
+
// ============================================================================
|
|
488
|
+
|
|
489
|
+
const stringToBoolean = (options?: { truthy?: string[]; falsy?: string[] }) => z.stringbool(options);
|
|
490
|
+
|
|
445
491
|
test("stringToBoolean codec", () => {
|
|
446
492
|
const codec = stringToBoolean();
|
|
447
493
|
|
|
@@ -469,6 +515,10 @@ test("stringToBoolean codec", () => {
|
|
|
469
515
|
expect(z.encode(customCodec, false)).toBe("no");
|
|
470
516
|
});
|
|
471
517
|
|
|
518
|
+
// ============================================================================
|
|
519
|
+
// Error Handling Tests
|
|
520
|
+
// ============================================================================
|
|
521
|
+
|
|
472
522
|
// Test error cases - these test input validation, not transform errors
|
|
473
523
|
test("codec input validation", () => {
|
|
474
524
|
// Test invalid base64 format
|
|
@@ -495,25 +545,10 @@ test("codec input validation", () => {
|
|
|
495
545
|
// Test transform errors - these test errors added by transform functions
|
|
496
546
|
test("codec transform error handling", () => {
|
|
497
547
|
// JSON codec that can fail during transform
|
|
498
|
-
const
|
|
499
|
-
decode: (jsonString, ctx) => {
|
|
500
|
-
try {
|
|
501
|
-
return JSON.parse(jsonString);
|
|
502
|
-
} catch (err: any) {
|
|
503
|
-
ctx.issues.push({
|
|
504
|
-
code: "invalid_format",
|
|
505
|
-
format: "json",
|
|
506
|
-
input: jsonString,
|
|
507
|
-
message: err.message,
|
|
508
|
-
});
|
|
509
|
-
return z.NEVER;
|
|
510
|
-
}
|
|
511
|
-
},
|
|
512
|
-
encode: (value) => JSON.stringify(value),
|
|
513
|
-
});
|
|
548
|
+
const anyJSON = jsonCodec(z.json());
|
|
514
549
|
|
|
515
550
|
// Test successful JSON parsing
|
|
516
|
-
const validResult = z.safeDecode(
|
|
551
|
+
const validResult = z.safeDecode(anyJSON, '{"valid": "json"}');
|
|
517
552
|
expect(validResult.success).toBe(true);
|
|
518
553
|
if (validResult.success) {
|
|
519
554
|
expect(validResult.data).toEqual({ valid: "json" });
|
|
@@ -521,7 +556,7 @@ test("codec transform error handling", () => {
|
|
|
521
556
|
|
|
522
557
|
// Test invalid JSON that should create a single "invalid_format" issue
|
|
523
558
|
// Verifies that the transform error aborts before reaching the output schema
|
|
524
|
-
const invalidResult = z.safeDecode(
|
|
559
|
+
const invalidResult = z.safeDecode(anyJSON, '{"invalid":,}');
|
|
525
560
|
expect(invalidResult.success).toBe(false);
|
|
526
561
|
if (!invalidResult.success) {
|
|
527
562
|
expect(invalidResult.error.issues).toMatchInlineSnapshot(`
|
|
@@ -653,6 +653,24 @@ describe("toJSONSchema", () => {
|
|
|
653
653
|
});
|
|
654
654
|
|
|
655
655
|
test("tuple", () => {
|
|
656
|
+
const schema = z.tuple([z.string(), z.number()]);
|
|
657
|
+
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
|
|
658
|
+
{
|
|
659
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
660
|
+
"prefixItems": [
|
|
661
|
+
{
|
|
662
|
+
"type": "string",
|
|
663
|
+
},
|
|
664
|
+
{
|
|
665
|
+
"type": "number",
|
|
666
|
+
},
|
|
667
|
+
],
|
|
668
|
+
"type": "array",
|
|
669
|
+
}
|
|
670
|
+
`);
|
|
671
|
+
});
|
|
672
|
+
|
|
673
|
+
test("tuple with rest", () => {
|
|
656
674
|
const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
|
|
657
675
|
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
|
|
658
676
|
{
|
|
@@ -673,6 +691,46 @@ describe("toJSONSchema", () => {
|
|
|
673
691
|
`);
|
|
674
692
|
});
|
|
675
693
|
|
|
694
|
+
test("tuple openapi", () => {
|
|
695
|
+
const schema = z.tuple([z.string(), z.number()]);
|
|
696
|
+
expect(z.toJSONSchema(schema, { target: "openapi-3.0" })).toMatchInlineSnapshot(`
|
|
697
|
+
{
|
|
698
|
+
"items": [
|
|
699
|
+
{
|
|
700
|
+
"type": "string",
|
|
701
|
+
},
|
|
702
|
+
{
|
|
703
|
+
"type": "number",
|
|
704
|
+
},
|
|
705
|
+
],
|
|
706
|
+
"maxItems": 2,
|
|
707
|
+
"minItems": 2,
|
|
708
|
+
"type": "array",
|
|
709
|
+
}
|
|
710
|
+
`);
|
|
711
|
+
});
|
|
712
|
+
|
|
713
|
+
test("tuple with rest openapi", () => {
|
|
714
|
+
const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
|
|
715
|
+
expect(z.toJSONSchema(schema, { target: "openapi-3.0" })).toMatchInlineSnapshot(`
|
|
716
|
+
{
|
|
717
|
+
"items": [
|
|
718
|
+
{
|
|
719
|
+
"type": "string",
|
|
720
|
+
},
|
|
721
|
+
{
|
|
722
|
+
"type": "number",
|
|
723
|
+
},
|
|
724
|
+
{
|
|
725
|
+
"type": "boolean",
|
|
726
|
+
},
|
|
727
|
+
],
|
|
728
|
+
"minItems": 2,
|
|
729
|
+
"type": "array",
|
|
730
|
+
}
|
|
731
|
+
`);
|
|
732
|
+
});
|
|
733
|
+
|
|
676
734
|
test("promise", () => {
|
|
677
735
|
const schema = z.promise(z.string());
|
|
678
736
|
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
|
|
@@ -371,32 +371,34 @@ export class JSONSchemaGenerator {
|
|
|
371
371
|
const prefixItems = def.items.map((x, i) =>
|
|
372
372
|
this.process(x, { ...params, path: [...params.path, "prefixItems", i] })
|
|
373
373
|
);
|
|
374
|
+
const rest = def.rest
|
|
375
|
+
? this.process(def.rest, {
|
|
376
|
+
...params,
|
|
377
|
+
path: [...params.path, "items"],
|
|
378
|
+
})
|
|
379
|
+
: null;
|
|
380
|
+
|
|
374
381
|
if (this.target === "draft-2020-12") {
|
|
375
382
|
json.prefixItems = prefixItems;
|
|
383
|
+
if (rest) {
|
|
384
|
+
json.items = rest;
|
|
385
|
+
}
|
|
386
|
+
} else if (this.target === "openapi-3.0") {
|
|
387
|
+
json.items = [...prefixItems];
|
|
388
|
+
if (rest) {
|
|
389
|
+
json.items.push(rest);
|
|
390
|
+
}
|
|
391
|
+
json.minItems = prefixItems.length;
|
|
392
|
+
if (!rest) {
|
|
393
|
+
json.maxItems = prefixItems.length;
|
|
394
|
+
}
|
|
376
395
|
} else {
|
|
377
396
|
json.items = prefixItems;
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
if (def.rest) {
|
|
381
|
-
const rest = this.process(def.rest, {
|
|
382
|
-
...params,
|
|
383
|
-
path: [...params.path, "items"],
|
|
384
|
-
});
|
|
385
|
-
if (this.target === "draft-2020-12") {
|
|
386
|
-
json.items = rest;
|
|
387
|
-
} else {
|
|
397
|
+
if (rest) {
|
|
388
398
|
json.additionalItems = rest;
|
|
389
399
|
}
|
|
390
400
|
}
|
|
391
401
|
|
|
392
|
-
// additionalItems
|
|
393
|
-
if (def.rest) {
|
|
394
|
-
json.items = this.process(def.rest, {
|
|
395
|
-
...params,
|
|
396
|
-
path: [...params.path, "items"],
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
402
|
// length
|
|
401
403
|
const { minimum, maximum } = schema._zod.bag as {
|
|
402
404
|
minimum?: number;
|
package/src/v4/core/versions.ts
CHANGED
package/src/v4/mini/external.ts
CHANGED
package/v4/classic/external.cjs
CHANGED
|
@@ -29,7 +29,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
29
29
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
30
30
|
};
|
|
31
31
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
32
|
-
exports.coerce = exports.iso = exports.ZodISODuration = exports.ZodISOTime = exports.ZodISODate = exports.ZodISODateTime = exports.locales = exports.NEVER = exports.TimePrecision = exports.toJSONSchema = exports.flattenError = exports.formatError = exports.prettifyError = exports.treeifyError = exports.regexes = exports.clone = exports.$brand = exports.$input = exports.$output = exports.config = exports.registry = exports.globalRegistry = exports.core = void 0;
|
|
32
|
+
exports.coerce = exports.iso = exports.ZodISODuration = exports.ZodISOTime = exports.ZodISODate = exports.ZodISODateTime = exports.locales = exports.NEVER = exports.util = exports.TimePrecision = exports.toJSONSchema = exports.flattenError = exports.formatError = exports.prettifyError = exports.treeifyError = exports.regexes = exports.clone = exports.$brand = exports.$input = exports.$output = exports.config = exports.registry = exports.globalRegistry = exports.core = void 0;
|
|
33
33
|
exports.core = __importStar(require("../core/index.cjs"));
|
|
34
34
|
__exportStar(require("./schemas.cjs"), exports);
|
|
35
35
|
__exportStar(require("./checks.cjs"), exports);
|
|
@@ -55,6 +55,7 @@ Object.defineProperty(exports, "formatError", { enumerable: true, get: function
|
|
|
55
55
|
Object.defineProperty(exports, "flattenError", { enumerable: true, get: function () { return index_js_2.flattenError; } });
|
|
56
56
|
Object.defineProperty(exports, "toJSONSchema", { enumerable: true, get: function () { return index_js_2.toJSONSchema; } });
|
|
57
57
|
Object.defineProperty(exports, "TimePrecision", { enumerable: true, get: function () { return index_js_2.TimePrecision; } });
|
|
58
|
+
Object.defineProperty(exports, "util", { enumerable: true, get: function () { return index_js_2.util; } });
|
|
58
59
|
Object.defineProperty(exports, "NEVER", { enumerable: true, get: function () { return index_js_2.NEVER; } });
|
|
59
60
|
exports.locales = __importStar(require("../locales/index.cjs"));
|
|
60
61
|
// iso
|
|
@@ -5,7 +5,7 @@ export * from "./errors.cjs";
|
|
|
5
5
|
export * from "./parse.cjs";
|
|
6
6
|
export * from "./compat.cjs";
|
|
7
7
|
export type { infer, output, input } from "../core/index.cjs";
|
|
8
|
-
export { globalRegistry, type GlobalMeta, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, NEVER, } from "../core/index.cjs";
|
|
8
|
+
export { globalRegistry, type GlobalMeta, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, util, NEVER, } from "../core/index.cjs";
|
|
9
9
|
export * as locales from "../locales/index.cjs";
|
|
10
10
|
export { ZodISODateTime, ZodISODate, ZodISOTime, ZodISODuration } from "./iso.cjs";
|
|
11
11
|
export * as iso from "./iso.cjs";
|
package/v4/classic/external.d.ts
CHANGED
|
@@ -5,7 +5,7 @@ export * from "./errors.js";
|
|
|
5
5
|
export * from "./parse.js";
|
|
6
6
|
export * from "./compat.js";
|
|
7
7
|
export type { infer, output, input } from "../core/index.js";
|
|
8
|
-
export { globalRegistry, type GlobalMeta, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, NEVER, } from "../core/index.js";
|
|
8
|
+
export { globalRegistry, type GlobalMeta, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, util, NEVER, } from "../core/index.js";
|
|
9
9
|
export * as locales from "../locales/index.js";
|
|
10
10
|
export { ZodISODateTime, ZodISODate, ZodISOTime, ZodISODuration } from "./iso.js";
|
|
11
11
|
export * as iso from "./iso.js";
|
package/v4/classic/external.js
CHANGED
|
@@ -8,7 +8,7 @@ export * from "./compat.js";
|
|
|
8
8
|
import { config } from "../core/index.js";
|
|
9
9
|
import en from "../locales/en.js";
|
|
10
10
|
config(en());
|
|
11
|
-
export { globalRegistry, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, NEVER, } from "../core/index.js";
|
|
11
|
+
export { globalRegistry, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, util, NEVER, } from "../core/index.js";
|
|
12
12
|
export * as locales from "../locales/index.js";
|
|
13
13
|
// iso
|
|
14
14
|
// must be exported from top-level
|
|
@@ -289,31 +289,34 @@ class JSONSchemaGenerator {
|
|
|
289
289
|
const json = _json;
|
|
290
290
|
json.type = "array";
|
|
291
291
|
const prefixItems = def.items.map((x, i) => this.process(x, { ...params, path: [...params.path, "prefixItems", i] }));
|
|
292
|
+
const rest = def.rest
|
|
293
|
+
? this.process(def.rest, {
|
|
294
|
+
...params,
|
|
295
|
+
path: [...params.path, "items"],
|
|
296
|
+
})
|
|
297
|
+
: null;
|
|
292
298
|
if (this.target === "draft-2020-12") {
|
|
293
299
|
json.prefixItems = prefixItems;
|
|
300
|
+
if (rest) {
|
|
301
|
+
json.items = rest;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
else if (this.target === "openapi-3.0") {
|
|
305
|
+
json.items = [...prefixItems];
|
|
306
|
+
if (rest) {
|
|
307
|
+
json.items.push(rest);
|
|
308
|
+
}
|
|
309
|
+
json.minItems = prefixItems.length;
|
|
310
|
+
if (!rest) {
|
|
311
|
+
json.maxItems = prefixItems.length;
|
|
312
|
+
}
|
|
294
313
|
}
|
|
295
314
|
else {
|
|
296
315
|
json.items = prefixItems;
|
|
297
|
-
|
|
298
|
-
if (def.rest) {
|
|
299
|
-
const rest = this.process(def.rest, {
|
|
300
|
-
...params,
|
|
301
|
-
path: [...params.path, "items"],
|
|
302
|
-
});
|
|
303
|
-
if (this.target === "draft-2020-12") {
|
|
304
|
-
json.items = rest;
|
|
305
|
-
}
|
|
306
|
-
else {
|
|
316
|
+
if (rest) {
|
|
307
317
|
json.additionalItems = rest;
|
|
308
318
|
}
|
|
309
319
|
}
|
|
310
|
-
// additionalItems
|
|
311
|
-
if (def.rest) {
|
|
312
|
-
json.items = this.process(def.rest, {
|
|
313
|
-
...params,
|
|
314
|
-
path: [...params.path, "items"],
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
320
|
// length
|
|
318
321
|
const { minimum, maximum } = schema._zod.bag;
|
|
319
322
|
if (typeof minimum === "number")
|
|
@@ -285,31 +285,34 @@ export class JSONSchemaGenerator {
|
|
|
285
285
|
const json = _json;
|
|
286
286
|
json.type = "array";
|
|
287
287
|
const prefixItems = def.items.map((x, i) => this.process(x, { ...params, path: [...params.path, "prefixItems", i] }));
|
|
288
|
+
const rest = def.rest
|
|
289
|
+
? this.process(def.rest, {
|
|
290
|
+
...params,
|
|
291
|
+
path: [...params.path, "items"],
|
|
292
|
+
})
|
|
293
|
+
: null;
|
|
288
294
|
if (this.target === "draft-2020-12") {
|
|
289
295
|
json.prefixItems = prefixItems;
|
|
296
|
+
if (rest) {
|
|
297
|
+
json.items = rest;
|
|
298
|
+
}
|
|
299
|
+
}
|
|
300
|
+
else if (this.target === "openapi-3.0") {
|
|
301
|
+
json.items = [...prefixItems];
|
|
302
|
+
if (rest) {
|
|
303
|
+
json.items.push(rest);
|
|
304
|
+
}
|
|
305
|
+
json.minItems = prefixItems.length;
|
|
306
|
+
if (!rest) {
|
|
307
|
+
json.maxItems = prefixItems.length;
|
|
308
|
+
}
|
|
290
309
|
}
|
|
291
310
|
else {
|
|
292
311
|
json.items = prefixItems;
|
|
293
|
-
|
|
294
|
-
if (def.rest) {
|
|
295
|
-
const rest = this.process(def.rest, {
|
|
296
|
-
...params,
|
|
297
|
-
path: [...params.path, "items"],
|
|
298
|
-
});
|
|
299
|
-
if (this.target === "draft-2020-12") {
|
|
300
|
-
json.items = rest;
|
|
301
|
-
}
|
|
302
|
-
else {
|
|
312
|
+
if (rest) {
|
|
303
313
|
json.additionalItems = rest;
|
|
304
314
|
}
|
|
305
315
|
}
|
|
306
|
-
// additionalItems
|
|
307
|
-
if (def.rest) {
|
|
308
|
-
json.items = this.process(def.rest, {
|
|
309
|
-
...params,
|
|
310
|
-
path: [...params.path, "items"],
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
316
|
// length
|
|
314
317
|
const { minimum, maximum } = schema._zod.bag;
|
|
315
318
|
if (typeof minimum === "number")
|
package/v4/core/versions.cjs
CHANGED
package/v4/core/versions.js
CHANGED
package/v4/mini/external.cjs
CHANGED
|
@@ -26,7 +26,7 @@ var __exportStar = (this && this.__exportStar) || function(m, exports) {
|
|
|
26
26
|
for (var p in m) if (p !== "default" && !Object.prototype.hasOwnProperty.call(exports, p)) __createBinding(exports, m, p);
|
|
27
27
|
};
|
|
28
28
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
29
|
-
exports.coerce = exports.ZodMiniISODuration = exports.ZodMiniISOTime = exports.ZodMiniISODate = exports.ZodMiniISODateTime = exports.iso = exports.locales = exports.NEVER = exports.TimePrecision = exports.toJSONSchema = exports.flattenError = exports.formatError = exports.prettifyError = exports.treeifyError = exports.regexes = exports.clone = exports.$brand = exports.$input = exports.$output = exports.config = exports.registry = exports.globalRegistry = exports.core = void 0;
|
|
29
|
+
exports.coerce = exports.ZodMiniISODuration = exports.ZodMiniISOTime = exports.ZodMiniISODate = exports.ZodMiniISODateTime = exports.iso = exports.locales = exports.NEVER = exports.util = exports.TimePrecision = exports.toJSONSchema = exports.flattenError = exports.formatError = exports.prettifyError = exports.treeifyError = exports.regexes = exports.clone = exports.$brand = exports.$input = exports.$output = exports.config = exports.registry = exports.globalRegistry = exports.core = void 0;
|
|
30
30
|
exports.core = __importStar(require("../core/index.cjs"));
|
|
31
31
|
__exportStar(require("./parse.cjs"), exports);
|
|
32
32
|
__exportStar(require("./schemas.cjs"), exports);
|
|
@@ -46,6 +46,7 @@ Object.defineProperty(exports, "formatError", { enumerable: true, get: function
|
|
|
46
46
|
Object.defineProperty(exports, "flattenError", { enumerable: true, get: function () { return index_js_1.flattenError; } });
|
|
47
47
|
Object.defineProperty(exports, "toJSONSchema", { enumerable: true, get: function () { return index_js_1.toJSONSchema; } });
|
|
48
48
|
Object.defineProperty(exports, "TimePrecision", { enumerable: true, get: function () { return index_js_1.TimePrecision; } });
|
|
49
|
+
Object.defineProperty(exports, "util", { enumerable: true, get: function () { return index_js_1.util; } });
|
|
49
50
|
Object.defineProperty(exports, "NEVER", { enumerable: true, get: function () { return index_js_1.NEVER; } });
|
|
50
51
|
exports.locales = __importStar(require("../locales/index.cjs"));
|
|
51
52
|
/** A special constant with type `never` */
|
package/v4/mini/external.d.cts
CHANGED
|
@@ -3,7 +3,7 @@ export * from "./parse.cjs";
|
|
|
3
3
|
export * from "./schemas.cjs";
|
|
4
4
|
export * from "./checks.cjs";
|
|
5
5
|
export type { infer, output, input } from "../core/index.cjs";
|
|
6
|
-
export { globalRegistry, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, NEVER, } from "../core/index.cjs";
|
|
6
|
+
export { globalRegistry, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, util, NEVER, } from "../core/index.cjs";
|
|
7
7
|
export * as locales from "../locales/index.cjs";
|
|
8
8
|
/** A special constant with type `never` */
|
|
9
9
|
export * as iso from "./iso.cjs";
|
package/v4/mini/external.d.ts
CHANGED
|
@@ -3,7 +3,7 @@ export * from "./parse.js";
|
|
|
3
3
|
export * from "./schemas.js";
|
|
4
4
|
export * from "./checks.js";
|
|
5
5
|
export type { infer, output, input } from "../core/index.js";
|
|
6
|
-
export { globalRegistry, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, NEVER, } from "../core/index.js";
|
|
6
|
+
export { globalRegistry, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, util, NEVER, } from "../core/index.js";
|
|
7
7
|
export * as locales from "../locales/index.js";
|
|
8
8
|
/** A special constant with type `never` */
|
|
9
9
|
export * as iso from "./iso.js";
|
package/v4/mini/external.js
CHANGED
|
@@ -2,7 +2,7 @@ export * as core from "../core/index.js";
|
|
|
2
2
|
export * from "./parse.js";
|
|
3
3
|
export * from "./schemas.js";
|
|
4
4
|
export * from "./checks.js";
|
|
5
|
-
export { globalRegistry, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, NEVER, } from "../core/index.js";
|
|
5
|
+
export { globalRegistry, registry, config, $output, $input, $brand, clone, regexes, treeifyError, prettifyError, formatError, flattenError, toJSONSchema, TimePrecision, util, NEVER, } from "../core/index.js";
|
|
6
6
|
export * as locales from "../locales/index.js";
|
|
7
7
|
/** A special constant with type `never` */
|
|
8
8
|
// export const NEVER = {} as never;
|