zod 4.1.1 → 4.1.3
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 +185 -3
- package/src/v4/core/to-json-schema.ts +34 -22
- 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 +31 -21
- package/v4/core/to-json-schema.js +31 -21
- 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(`
|
|
@@ -571,6 +571,19 @@ describe("toJSONSchema", () => {
|
|
|
571
571
|
`);
|
|
572
572
|
});
|
|
573
573
|
|
|
574
|
+
test("number with exclusive min-max openapi", () => {
|
|
575
|
+
const schema = z.number().lt(100).gt(1);
|
|
576
|
+
expect(z.toJSONSchema(schema, { target: "openapi-3.0" })).toMatchInlineSnapshot(`
|
|
577
|
+
{
|
|
578
|
+
"exclusiveMaximum": true,
|
|
579
|
+
"exclusiveMinimum": true,
|
|
580
|
+
"maximum": 100,
|
|
581
|
+
"minimum": 1,
|
|
582
|
+
"type": "number",
|
|
583
|
+
}
|
|
584
|
+
`);
|
|
585
|
+
});
|
|
586
|
+
|
|
574
587
|
test("arrays", () => {
|
|
575
588
|
expect(z.toJSONSchema(z.array(z.string()))).toMatchInlineSnapshot(`
|
|
576
589
|
{
|
|
@@ -652,7 +665,37 @@ describe("toJSONSchema", () => {
|
|
|
652
665
|
`);
|
|
653
666
|
});
|
|
654
667
|
|
|
668
|
+
test("record openapi", () => {
|
|
669
|
+
const schema = z.record(z.string(), z.boolean());
|
|
670
|
+
expect(z.toJSONSchema(schema, { target: "openapi-3.0" })).toMatchInlineSnapshot(`
|
|
671
|
+
{
|
|
672
|
+
"additionalProperties": {
|
|
673
|
+
"type": "boolean",
|
|
674
|
+
},
|
|
675
|
+
"type": "object",
|
|
676
|
+
}
|
|
677
|
+
`);
|
|
678
|
+
});
|
|
679
|
+
|
|
655
680
|
test("tuple", () => {
|
|
681
|
+
const schema = z.tuple([z.string(), z.number()]);
|
|
682
|
+
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
|
|
683
|
+
{
|
|
684
|
+
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
685
|
+
"prefixItems": [
|
|
686
|
+
{
|
|
687
|
+
"type": "string",
|
|
688
|
+
},
|
|
689
|
+
{
|
|
690
|
+
"type": "number",
|
|
691
|
+
},
|
|
692
|
+
],
|
|
693
|
+
"type": "array",
|
|
694
|
+
}
|
|
695
|
+
`);
|
|
696
|
+
});
|
|
697
|
+
|
|
698
|
+
test("tuple with rest", () => {
|
|
656
699
|
const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
|
|
657
700
|
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
|
|
658
701
|
{
|
|
@@ -673,6 +716,139 @@ describe("toJSONSchema", () => {
|
|
|
673
716
|
`);
|
|
674
717
|
});
|
|
675
718
|
|
|
719
|
+
test("tuple openapi", () => {
|
|
720
|
+
const schema = z.tuple([z.string(), z.number()]);
|
|
721
|
+
expect(z.toJSONSchema(schema, { target: "openapi-3.0" })).toMatchInlineSnapshot(`
|
|
722
|
+
{
|
|
723
|
+
"items": {
|
|
724
|
+
"anyOf": [
|
|
725
|
+
{
|
|
726
|
+
"type": "string",
|
|
727
|
+
},
|
|
728
|
+
{
|
|
729
|
+
"type": "number",
|
|
730
|
+
},
|
|
731
|
+
],
|
|
732
|
+
},
|
|
733
|
+
"maxItems": 2,
|
|
734
|
+
"minItems": 2,
|
|
735
|
+
"type": "array",
|
|
736
|
+
}
|
|
737
|
+
`);
|
|
738
|
+
});
|
|
739
|
+
|
|
740
|
+
test("tuple with rest openapi", () => {
|
|
741
|
+
const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
|
|
742
|
+
expect(z.toJSONSchema(schema, { target: "openapi-3.0" })).toMatchInlineSnapshot(`
|
|
743
|
+
{
|
|
744
|
+
"items": {
|
|
745
|
+
"anyOf": [
|
|
746
|
+
{
|
|
747
|
+
"type": "string",
|
|
748
|
+
},
|
|
749
|
+
{
|
|
750
|
+
"type": "number",
|
|
751
|
+
},
|
|
752
|
+
{
|
|
753
|
+
"type": "boolean",
|
|
754
|
+
},
|
|
755
|
+
],
|
|
756
|
+
},
|
|
757
|
+
"minItems": 2,
|
|
758
|
+
"type": "array",
|
|
759
|
+
}
|
|
760
|
+
`);
|
|
761
|
+
});
|
|
762
|
+
|
|
763
|
+
test("tuple draft-7", () => {
|
|
764
|
+
const schema = z.tuple([z.string(), z.number()]);
|
|
765
|
+
expect(z.toJSONSchema(schema, { target: "draft-7", io: "input" })).toMatchInlineSnapshot(`
|
|
766
|
+
{
|
|
767
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
768
|
+
"items": [
|
|
769
|
+
{
|
|
770
|
+
"type": "string",
|
|
771
|
+
},
|
|
772
|
+
{
|
|
773
|
+
"type": "number",
|
|
774
|
+
},
|
|
775
|
+
],
|
|
776
|
+
"type": "array",
|
|
777
|
+
}
|
|
778
|
+
`);
|
|
779
|
+
});
|
|
780
|
+
|
|
781
|
+
test("tuple with rest draft-7", () => {
|
|
782
|
+
const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
|
|
783
|
+
expect(z.toJSONSchema(schema, { target: "draft-7", io: "input" })).toMatchInlineSnapshot(`
|
|
784
|
+
{
|
|
785
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
786
|
+
"additionalItems": {
|
|
787
|
+
"type": "boolean",
|
|
788
|
+
},
|
|
789
|
+
"items": [
|
|
790
|
+
{
|
|
791
|
+
"type": "string",
|
|
792
|
+
},
|
|
793
|
+
{
|
|
794
|
+
"type": "number",
|
|
795
|
+
},
|
|
796
|
+
],
|
|
797
|
+
"type": "array",
|
|
798
|
+
}
|
|
799
|
+
`);
|
|
800
|
+
});
|
|
801
|
+
|
|
802
|
+
test("tuple with rest draft-7 - issue #5151 regression test", () => {
|
|
803
|
+
// This test addresses issue #5151: tuple with rest elements and ids
|
|
804
|
+
// in draft-7 had incorrect internal path handling affecting complex scenarios
|
|
805
|
+
const primarySchema = z.string().meta({ id: "primary" });
|
|
806
|
+
const restSchema = z.number().meta({ id: "rest" });
|
|
807
|
+
const testSchema = z.tuple([primarySchema], restSchema);
|
|
808
|
+
|
|
809
|
+
// Test both final output structure AND internal path handling
|
|
810
|
+
const capturedPaths: string[] = [];
|
|
811
|
+
const result = z.toJSONSchema(testSchema, {
|
|
812
|
+
target: "draft-7",
|
|
813
|
+
override: (ctx) => capturedPaths.push(ctx.path.join("/")),
|
|
814
|
+
});
|
|
815
|
+
|
|
816
|
+
// Verify correct draft-7 structure with metadata extraction
|
|
817
|
+
expect(result).toMatchInlineSnapshot(`
|
|
818
|
+
{
|
|
819
|
+
"$schema": "http://json-schema.org/draft-07/schema#",
|
|
820
|
+
"additionalItems": {
|
|
821
|
+
"$ref": "#/definitions/rest",
|
|
822
|
+
},
|
|
823
|
+
"definitions": {
|
|
824
|
+
"primary": {
|
|
825
|
+
"id": "primary",
|
|
826
|
+
"type": "string",
|
|
827
|
+
},
|
|
828
|
+
"rest": {
|
|
829
|
+
"id": "rest",
|
|
830
|
+
"type": "number",
|
|
831
|
+
},
|
|
832
|
+
},
|
|
833
|
+
"items": [
|
|
834
|
+
{
|
|
835
|
+
"$ref": "#/definitions/primary",
|
|
836
|
+
},
|
|
837
|
+
],
|
|
838
|
+
"type": "array",
|
|
839
|
+
}
|
|
840
|
+
`);
|
|
841
|
+
|
|
842
|
+
// Verify internal paths are correct (this was the actual bug)
|
|
843
|
+
expect(capturedPaths).toContain("items/0"); // prefix items should use "items" path
|
|
844
|
+
expect(capturedPaths).toContain("additionalItems"); // rest should use "additionalItems" path
|
|
845
|
+
expect(capturedPaths).not.toContain("prefixItems/0"); // should not use draft-2020-12 paths
|
|
846
|
+
|
|
847
|
+
// Structural validations
|
|
848
|
+
expect(Array.isArray(result.items)).toBe(true);
|
|
849
|
+
expect(result.additionalItems).toBeDefined();
|
|
850
|
+
});
|
|
851
|
+
|
|
676
852
|
test("promise", () => {
|
|
677
853
|
const schema = z.promise(z.string());
|
|
678
854
|
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
|
|
@@ -1496,7 +1672,9 @@ test("unrepresentable literal values are ignored", () => {
|
|
|
1496
1672
|
}
|
|
1497
1673
|
`);
|
|
1498
1674
|
|
|
1499
|
-
const b = z.z.toJSONSchema(z.literal([undefined, null, 5, BigInt(1324)]), {
|
|
1675
|
+
const b = z.z.toJSONSchema(z.literal([undefined, null, 5, BigInt(1324)]), {
|
|
1676
|
+
unrepresentable: "any",
|
|
1677
|
+
});
|
|
1500
1678
|
expect(b).toMatchInlineSnapshot(`
|
|
1501
1679
|
{
|
|
1502
1680
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -1508,7 +1686,9 @@ test("unrepresentable literal values are ignored", () => {
|
|
|
1508
1686
|
}
|
|
1509
1687
|
`);
|
|
1510
1688
|
|
|
1511
|
-
const c = z.z.toJSONSchema(z.literal([undefined]), {
|
|
1689
|
+
const c = z.z.toJSONSchema(z.literal([undefined]), {
|
|
1690
|
+
unrepresentable: "any",
|
|
1691
|
+
});
|
|
1512
1692
|
expect(c).toMatchInlineSnapshot(`
|
|
1513
1693
|
{
|
|
1514
1694
|
"$schema": "https://json-schema.org/draft/2020-12/schema",
|
|
@@ -1791,7 +1971,9 @@ test("basic registry", () => {
|
|
|
1791
1971
|
myRegistry.add(User, { id: "User" });
|
|
1792
1972
|
myRegistry.add(Post, { id: "Post" });
|
|
1793
1973
|
|
|
1794
|
-
const result = z.z.toJSONSchema(myRegistry, {
|
|
1974
|
+
const result = z.z.toJSONSchema(myRegistry, {
|
|
1975
|
+
uri: (id) => `https://example.com/${id}.json`,
|
|
1976
|
+
});
|
|
1795
1977
|
expect(result).toMatchInlineSnapshot(`
|
|
1796
1978
|
{
|
|
1797
1979
|
"schemas": {
|
|
@@ -183,7 +183,7 @@ export class JSONSchemaGenerator {
|
|
|
183
183
|
else json.type = "number";
|
|
184
184
|
|
|
185
185
|
if (typeof exclusiveMinimum === "number") {
|
|
186
|
-
if (this.target === "draft-4") {
|
|
186
|
+
if (this.target === "draft-4" || this.target === "openapi-3.0") {
|
|
187
187
|
json.minimum = exclusiveMinimum;
|
|
188
188
|
json.exclusiveMinimum = true;
|
|
189
189
|
} else {
|
|
@@ -199,7 +199,7 @@ export class JSONSchemaGenerator {
|
|
|
199
199
|
}
|
|
200
200
|
|
|
201
201
|
if (typeof exclusiveMaximum === "number") {
|
|
202
|
-
if (this.target === "draft-4") {
|
|
202
|
+
if (this.target === "draft-4" || this.target === "openapi-3.0") {
|
|
203
203
|
json.maximum = exclusiveMaximum;
|
|
204
204
|
json.exclusiveMaximum = true;
|
|
205
205
|
} else {
|
|
@@ -368,35 +368,47 @@ export class JSONSchemaGenerator {
|
|
|
368
368
|
case "tuple": {
|
|
369
369
|
const json: JSONSchema.ArraySchema = _json as any;
|
|
370
370
|
json.type = "array";
|
|
371
|
+
|
|
372
|
+
const prefixPath = this.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
373
|
+
const restPath =
|
|
374
|
+
this.target === "draft-2020-12" ? "items" : this.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
375
|
+
|
|
371
376
|
const prefixItems = def.items.map((x, i) =>
|
|
372
|
-
this.process(x, {
|
|
377
|
+
this.process(x, {
|
|
378
|
+
...params,
|
|
379
|
+
path: [...params.path, prefixPath, i],
|
|
380
|
+
})
|
|
373
381
|
);
|
|
382
|
+
const rest = def.rest
|
|
383
|
+
? this.process(def.rest, {
|
|
384
|
+
...params,
|
|
385
|
+
path: [...params.path, restPath, ...(this.target === "openapi-3.0" ? [def.items.length] : [])],
|
|
386
|
+
})
|
|
387
|
+
: null;
|
|
388
|
+
|
|
374
389
|
if (this.target === "draft-2020-12") {
|
|
375
390
|
json.prefixItems = prefixItems;
|
|
391
|
+
if (rest) {
|
|
392
|
+
json.items = rest;
|
|
393
|
+
}
|
|
394
|
+
} else if (this.target === "openapi-3.0") {
|
|
395
|
+
json.items = {
|
|
396
|
+
anyOf: [...prefixItems],
|
|
397
|
+
};
|
|
398
|
+
if (rest) {
|
|
399
|
+
json.items.anyOf!.push(rest);
|
|
400
|
+
}
|
|
401
|
+
json.minItems = prefixItems.length;
|
|
402
|
+
if (!rest) {
|
|
403
|
+
json.maxItems = prefixItems.length;
|
|
404
|
+
}
|
|
376
405
|
} else {
|
|
377
406
|
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 {
|
|
407
|
+
if (rest) {
|
|
388
408
|
json.additionalItems = rest;
|
|
389
409
|
}
|
|
390
410
|
}
|
|
391
411
|
|
|
392
|
-
// additionalItems
|
|
393
|
-
if (def.rest) {
|
|
394
|
-
json.items = this.process(def.rest, {
|
|
395
|
-
...params,
|
|
396
|
-
path: [...params.path, "items"],
|
|
397
|
-
});
|
|
398
|
-
}
|
|
399
|
-
|
|
400
412
|
// length
|
|
401
413
|
const { minimum, maximum } = schema._zod.bag as {
|
|
402
414
|
minimum?: number;
|
|
@@ -409,7 +421,7 @@ export class JSONSchemaGenerator {
|
|
|
409
421
|
case "record": {
|
|
410
422
|
const json: JSONSchema.ObjectSchema = _json as any;
|
|
411
423
|
json.type = "object";
|
|
412
|
-
if (this.target
|
|
424
|
+
if (this.target === "draft-7" || this.target === "draft-2020-12") {
|
|
413
425
|
json.propertyNames = this.process(def.keyType, {
|
|
414
426
|
...params,
|
|
415
427
|
path: [...params.path, "propertyNames"],
|
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
|
|
@@ -101,7 +101,7 @@ class JSONSchemaGenerator {
|
|
|
101
101
|
else
|
|
102
102
|
json.type = "number";
|
|
103
103
|
if (typeof exclusiveMinimum === "number") {
|
|
104
|
-
if (this.target === "draft-4") {
|
|
104
|
+
if (this.target === "draft-4" || this.target === "openapi-3.0") {
|
|
105
105
|
json.minimum = exclusiveMinimum;
|
|
106
106
|
json.exclusiveMinimum = true;
|
|
107
107
|
}
|
|
@@ -119,7 +119,7 @@ class JSONSchemaGenerator {
|
|
|
119
119
|
}
|
|
120
120
|
}
|
|
121
121
|
if (typeof exclusiveMaximum === "number") {
|
|
122
|
-
if (this.target === "draft-4") {
|
|
122
|
+
if (this.target === "draft-4" || this.target === "openapi-3.0") {
|
|
123
123
|
json.maximum = exclusiveMaximum;
|
|
124
124
|
json.exclusiveMaximum = true;
|
|
125
125
|
}
|
|
@@ -288,32 +288,42 @@ class JSONSchemaGenerator {
|
|
|
288
288
|
case "tuple": {
|
|
289
289
|
const json = _json;
|
|
290
290
|
json.type = "array";
|
|
291
|
-
const
|
|
291
|
+
const prefixPath = this.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
292
|
+
const restPath = this.target === "draft-2020-12" ? "items" : this.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
293
|
+
const prefixItems = def.items.map((x, i) => this.process(x, {
|
|
294
|
+
...params,
|
|
295
|
+
path: [...params.path, prefixPath, i],
|
|
296
|
+
}));
|
|
297
|
+
const rest = def.rest
|
|
298
|
+
? this.process(def.rest, {
|
|
299
|
+
...params,
|
|
300
|
+
path: [...params.path, restPath, ...(this.target === "openapi-3.0" ? [def.items.length] : [])],
|
|
301
|
+
})
|
|
302
|
+
: null;
|
|
292
303
|
if (this.target === "draft-2020-12") {
|
|
293
304
|
json.prefixItems = prefixItems;
|
|
305
|
+
if (rest) {
|
|
306
|
+
json.items = rest;
|
|
307
|
+
}
|
|
308
|
+
}
|
|
309
|
+
else if (this.target === "openapi-3.0") {
|
|
310
|
+
json.items = {
|
|
311
|
+
anyOf: [...prefixItems],
|
|
312
|
+
};
|
|
313
|
+
if (rest) {
|
|
314
|
+
json.items.anyOf.push(rest);
|
|
315
|
+
}
|
|
316
|
+
json.minItems = prefixItems.length;
|
|
317
|
+
if (!rest) {
|
|
318
|
+
json.maxItems = prefixItems.length;
|
|
319
|
+
}
|
|
294
320
|
}
|
|
295
321
|
else {
|
|
296
322
|
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 {
|
|
323
|
+
if (rest) {
|
|
307
324
|
json.additionalItems = rest;
|
|
308
325
|
}
|
|
309
326
|
}
|
|
310
|
-
// additionalItems
|
|
311
|
-
if (def.rest) {
|
|
312
|
-
json.items = this.process(def.rest, {
|
|
313
|
-
...params,
|
|
314
|
-
path: [...params.path, "items"],
|
|
315
|
-
});
|
|
316
|
-
}
|
|
317
327
|
// length
|
|
318
328
|
const { minimum, maximum } = schema._zod.bag;
|
|
319
329
|
if (typeof minimum === "number")
|
|
@@ -325,7 +335,7 @@ class JSONSchemaGenerator {
|
|
|
325
335
|
case "record": {
|
|
326
336
|
const json = _json;
|
|
327
337
|
json.type = "object";
|
|
328
|
-
if (this.target
|
|
338
|
+
if (this.target === "draft-7" || this.target === "draft-2020-12") {
|
|
329
339
|
json.propertyNames = this.process(def.keyType, {
|
|
330
340
|
...params,
|
|
331
341
|
path: [...params.path, "propertyNames"],
|
|
@@ -97,7 +97,7 @@ export class JSONSchemaGenerator {
|
|
|
97
97
|
else
|
|
98
98
|
json.type = "number";
|
|
99
99
|
if (typeof exclusiveMinimum === "number") {
|
|
100
|
-
if (this.target === "draft-4") {
|
|
100
|
+
if (this.target === "draft-4" || this.target === "openapi-3.0") {
|
|
101
101
|
json.minimum = exclusiveMinimum;
|
|
102
102
|
json.exclusiveMinimum = true;
|
|
103
103
|
}
|
|
@@ -115,7 +115,7 @@ export class JSONSchemaGenerator {
|
|
|
115
115
|
}
|
|
116
116
|
}
|
|
117
117
|
if (typeof exclusiveMaximum === "number") {
|
|
118
|
-
if (this.target === "draft-4") {
|
|
118
|
+
if (this.target === "draft-4" || this.target === "openapi-3.0") {
|
|
119
119
|
json.maximum = exclusiveMaximum;
|
|
120
120
|
json.exclusiveMaximum = true;
|
|
121
121
|
}
|
|
@@ -284,32 +284,42 @@ export class JSONSchemaGenerator {
|
|
|
284
284
|
case "tuple": {
|
|
285
285
|
const json = _json;
|
|
286
286
|
json.type = "array";
|
|
287
|
-
const
|
|
287
|
+
const prefixPath = this.target === "draft-2020-12" ? "prefixItems" : "items";
|
|
288
|
+
const restPath = this.target === "draft-2020-12" ? "items" : this.target === "openapi-3.0" ? "items" : "additionalItems";
|
|
289
|
+
const prefixItems = def.items.map((x, i) => this.process(x, {
|
|
290
|
+
...params,
|
|
291
|
+
path: [...params.path, prefixPath, i],
|
|
292
|
+
}));
|
|
293
|
+
const rest = def.rest
|
|
294
|
+
? this.process(def.rest, {
|
|
295
|
+
...params,
|
|
296
|
+
path: [...params.path, restPath, ...(this.target === "openapi-3.0" ? [def.items.length] : [])],
|
|
297
|
+
})
|
|
298
|
+
: null;
|
|
288
299
|
if (this.target === "draft-2020-12") {
|
|
289
300
|
json.prefixItems = prefixItems;
|
|
301
|
+
if (rest) {
|
|
302
|
+
json.items = rest;
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
else if (this.target === "openapi-3.0") {
|
|
306
|
+
json.items = {
|
|
307
|
+
anyOf: [...prefixItems],
|
|
308
|
+
};
|
|
309
|
+
if (rest) {
|
|
310
|
+
json.items.anyOf.push(rest);
|
|
311
|
+
}
|
|
312
|
+
json.minItems = prefixItems.length;
|
|
313
|
+
if (!rest) {
|
|
314
|
+
json.maxItems = prefixItems.length;
|
|
315
|
+
}
|
|
290
316
|
}
|
|
291
317
|
else {
|
|
292
318
|
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 {
|
|
319
|
+
if (rest) {
|
|
303
320
|
json.additionalItems = rest;
|
|
304
321
|
}
|
|
305
322
|
}
|
|
306
|
-
// additionalItems
|
|
307
|
-
if (def.rest) {
|
|
308
|
-
json.items = this.process(def.rest, {
|
|
309
|
-
...params,
|
|
310
|
-
path: [...params.path, "items"],
|
|
311
|
-
});
|
|
312
|
-
}
|
|
313
323
|
// length
|
|
314
324
|
const { minimum, maximum } = schema._zod.bag;
|
|
315
325
|
if (typeof minimum === "number")
|
|
@@ -321,7 +331,7 @@ export class JSONSchemaGenerator {
|
|
|
321
331
|
case "record": {
|
|
322
332
|
const json = _json;
|
|
323
333
|
json.type = "object";
|
|
324
|
-
if (this.target
|
|
334
|
+
if (this.target === "draft-7" || this.target === "draft-2020-12") {
|
|
325
335
|
json.propertyNames = this.process(def.keyType, {
|
|
326
336
|
...params,
|
|
327
337
|
path: [...params.path, "propertyNames"],
|
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;
|