zod 4.6.2 → 4.6.4
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/README.md +2 -2
- package/package.json +1 -1
- package/src/v4/classic/checks.ts +1 -0
- package/src/v4/classic/schemas.ts +12 -29
- package/src/v4/classic/tests/assignability.test.ts +1 -0
- package/src/v4/classic/tests/cyclic-data.test.ts +2 -11
- package/src/v4/classic/tests/firstparty.test.ts +0 -4
- package/src/v4/classic/tests/instance-footprint.test.ts +22 -0
- package/src/v4/classic/tests/instanceof.test.ts +1 -1
- package/src/v4/classic/tests/number.test.ts +39 -0
- package/src/v4/classic/tests/properties.test.ts +34 -137
- package/src/v4/classic/tests/string.test.ts +70 -1
- package/src/v4/classic/tests/to-json-schema.test.ts +87 -35
- package/src/v4/core/api.ts +21 -29
- package/src/v4/core/checks.ts +61 -1
- package/src/v4/core/compile.ts +35 -24
- package/src/v4/core/core.ts +1 -3
- package/src/v4/core/json-schema-processors.ts +9 -63
- package/src/v4/core/memoizer.ts +2 -5
- package/src/v4/core/regexes.ts +4 -0
- package/src/v4/core/schemas.ts +36 -109
- package/src/v4/core/tests/compile-differential.test.ts +35 -0
- package/src/v4/core/tests/compile.test.ts +177 -0
- package/src/v4/core/tests/url-no-canparse.test.ts +118 -16
- package/src/v4/core/to-json-schema.ts +1 -1
- package/src/v4/core/util.ts +0 -4
- package/src/v4/core/versions.ts +1 -1
- package/src/v4/core/visit.ts +0 -11
- package/src/v4/locales/ar.ts +1 -0
- package/src/v4/locales/az.ts +1 -0
- package/src/v4/locales/be.ts +1 -0
- package/src/v4/locales/bg.ts +1 -0
- package/src/v4/locales/bn.ts +1 -0
- package/src/v4/locales/ca.ts +1 -0
- package/src/v4/locales/ckb.ts +1 -0
- package/src/v4/locales/cs.ts +1 -0
- package/src/v4/locales/da.ts +1 -0
- package/src/v4/locales/de.ts +1 -0
- package/src/v4/locales/el.ts +1 -0
- package/src/v4/locales/en.ts +1 -0
- package/src/v4/locales/eo.ts +1 -0
- package/src/v4/locales/es.ts +1 -0
- package/src/v4/locales/fa.ts +1 -0
- package/src/v4/locales/fi.ts +1 -0
- package/src/v4/locales/fr-CA.ts +1 -0
- package/src/v4/locales/fr.ts +1 -0
- package/src/v4/locales/gu.ts +1 -0
- package/src/v4/locales/he.ts +1 -0
- package/src/v4/locales/hi.ts +1 -0
- package/src/v4/locales/hr.ts +1 -0
- package/src/v4/locales/hu.ts +1 -0
- package/src/v4/locales/hy.ts +1 -0
- package/src/v4/locales/id.ts +1 -0
- package/src/v4/locales/is.ts +1 -0
- package/src/v4/locales/it.ts +1 -0
- package/src/v4/locales/ja.ts +1 -0
- package/src/v4/locales/ka.ts +1 -0
- package/src/v4/locales/km.ts +1 -0
- package/src/v4/locales/kn.ts +1 -0
- package/src/v4/locales/ko.ts +1 -0
- package/src/v4/locales/lt.ts +1 -0
- package/src/v4/locales/mk.ts +1 -0
- package/src/v4/locales/ms.ts +1 -0
- package/src/v4/locales/ne.ts +1 -0
- package/src/v4/locales/nl.ts +1 -0
- package/src/v4/locales/nn.ts +1 -0
- package/src/v4/locales/no.ts +1 -0
- package/src/v4/locales/ota.ts +1 -0
- package/src/v4/locales/pl.ts +1 -0
- package/src/v4/locales/ps.ts +1 -0
- package/src/v4/locales/pt-BR.ts +1 -0
- package/src/v4/locales/pt.ts +1 -0
- package/src/v4/locales/ro.ts +1 -0
- package/src/v4/locales/ru.ts +1 -0
- package/src/v4/locales/sk.ts +1 -0
- package/src/v4/locales/sl.ts +1 -0
- package/src/v4/locales/sv.ts +1 -0
- package/src/v4/locales/ta.ts +1 -0
- package/src/v4/locales/tg.ts +1 -0
- package/src/v4/locales/th.ts +1 -0
- package/src/v4/locales/tk.ts +1 -0
- package/src/v4/locales/tr.ts +1 -0
- package/src/v4/locales/uk.ts +1 -0
- package/src/v4/locales/ur.ts +1 -0
- package/src/v4/locales/uz.ts +1 -0
- package/src/v4/locales/vi.ts +1 -0
- package/src/v4/locales/yo.ts +1 -0
- package/src/v4/locales/zh-CN.ts +1 -0
- package/src/v4/locales/zh-TW.ts +1 -0
- package/src/v4/mini/checks.ts +1 -0
- package/src/v4/mini/schemas.ts +12 -23
- package/src/v4/mini/tests/index.test.ts +56 -0
- package/src/v4/mini/tests/string.test.ts +7 -0
- package/v4/classic/checks.cjs +2 -1
- package/v4/classic/checks.d.cts +1 -1
- package/v4/classic/checks.d.ts +1 -1
- package/v4/classic/checks.js +1 -1
- package/v4/classic/schemas.cjs +11 -15
- package/v4/classic/schemas.d.cts +2 -6
- package/v4/classic/schemas.d.ts +2 -6
- package/v4/classic/schemas.js +9 -13
- package/v4/core/api.cjs +11 -23
- package/v4/core/api.d.cts +8 -4
- package/v4/core/api.d.ts +8 -4
- package/v4/core/api.js +11 -23
- package/v4/core/checks.cjs +32 -1
- package/v4/core/checks.d.cts +16 -0
- package/v4/core/checks.d.ts +16 -0
- package/v4/core/checks.js +31 -0
- package/v4/core/compile.cjs +12 -17
- package/v4/core/compile.js +13 -18
- package/v4/core/core.cjs +1 -1
- package/v4/core/core.js +1 -1
- package/v4/core/json-schema-processors.cjs +8 -44
- package/v4/core/json-schema-processors.d.cts +0 -1
- package/v4/core/json-schema-processors.d.ts +0 -1
- package/v4/core/json-schema-processors.js +8 -43
- package/v4/core/memoizer.cjs +2 -5
- package/v4/core/memoizer.js +2 -5
- package/v4/core/regexes.cjs +4 -2
- package/v4/core/regexes.d.cts +1 -0
- package/v4/core/regexes.d.ts +1 -0
- package/v4/core/regexes.js +2 -0
- package/v4/core/schemas.cjs +33 -73
- package/v4/core/schemas.d.cts +7 -22
- package/v4/core/schemas.d.ts +7 -22
- package/v4/core/schemas.js +30 -72
- package/v4/core/to-json-schema.cjs +0 -1
- package/v4/core/to-json-schema.d.cts +0 -3
- package/v4/core/to-json-schema.d.ts +0 -3
- package/v4/core/to-json-schema.js +1 -1
- package/v4/core/util.cjs +0 -4
- package/v4/core/util.js +0 -4
- package/v4/core/versions.cjs +1 -1
- package/v4/core/versions.js +1 -1
- package/v4/core/visit.cjs +0 -12
- package/v4/core/visit.js +0 -12
- package/v4/locales/ar.cjs +1 -0
- package/v4/locales/ar.js +1 -0
- package/v4/locales/az.cjs +1 -0
- package/v4/locales/az.js +1 -0
- package/v4/locales/be.cjs +1 -0
- package/v4/locales/be.js +1 -0
- package/v4/locales/bg.cjs +1 -0
- package/v4/locales/bg.js +1 -0
- package/v4/locales/bn.cjs +1 -0
- package/v4/locales/bn.js +1 -0
- package/v4/locales/ca.cjs +1 -0
- package/v4/locales/ca.js +1 -0
- package/v4/locales/ckb.cjs +1 -0
- package/v4/locales/ckb.js +1 -0
- package/v4/locales/cs.cjs +1 -0
- package/v4/locales/cs.js +1 -0
- package/v4/locales/da.cjs +1 -0
- package/v4/locales/da.js +1 -0
- package/v4/locales/de.cjs +1 -0
- package/v4/locales/de.js +1 -0
- package/v4/locales/el.cjs +1 -0
- package/v4/locales/el.js +1 -0
- package/v4/locales/en.cjs +1 -0
- package/v4/locales/en.js +1 -0
- package/v4/locales/eo.cjs +1 -0
- package/v4/locales/eo.js +1 -0
- package/v4/locales/es.cjs +1 -0
- package/v4/locales/es.js +1 -0
- package/v4/locales/fa.cjs +1 -0
- package/v4/locales/fa.js +1 -0
- package/v4/locales/fi.cjs +1 -0
- package/v4/locales/fi.js +1 -0
- package/v4/locales/fr-CA.cjs +1 -0
- package/v4/locales/fr-CA.js +1 -0
- package/v4/locales/fr.cjs +1 -0
- package/v4/locales/fr.js +1 -0
- package/v4/locales/gu.cjs +1 -0
- package/v4/locales/gu.js +1 -0
- package/v4/locales/he.cjs +1 -0
- package/v4/locales/he.js +1 -0
- package/v4/locales/hi.cjs +1 -0
- package/v4/locales/hi.js +1 -0
- package/v4/locales/hr.cjs +1 -0
- package/v4/locales/hr.js +1 -0
- package/v4/locales/hu.cjs +1 -0
- package/v4/locales/hu.js +1 -0
- package/v4/locales/hy.cjs +1 -0
- package/v4/locales/hy.js +1 -0
- package/v4/locales/id.cjs +1 -0
- package/v4/locales/id.js +1 -0
- package/v4/locales/is.cjs +1 -0
- package/v4/locales/is.js +1 -0
- package/v4/locales/it.cjs +1 -0
- package/v4/locales/it.js +1 -0
- package/v4/locales/ja.cjs +1 -0
- package/v4/locales/ja.js +1 -0
- package/v4/locales/ka.cjs +1 -0
- package/v4/locales/ka.js +1 -0
- package/v4/locales/km.cjs +1 -0
- package/v4/locales/km.js +1 -0
- package/v4/locales/kn.cjs +1 -0
- package/v4/locales/kn.js +1 -0
- package/v4/locales/ko.cjs +1 -0
- package/v4/locales/ko.js +1 -0
- package/v4/locales/lt.cjs +1 -0
- package/v4/locales/lt.js +1 -0
- package/v4/locales/mk.cjs +1 -0
- package/v4/locales/mk.js +1 -0
- package/v4/locales/ms.cjs +1 -0
- package/v4/locales/ms.js +1 -0
- package/v4/locales/ne.cjs +1 -0
- package/v4/locales/ne.js +1 -0
- package/v4/locales/nl.cjs +1 -0
- package/v4/locales/nl.js +1 -0
- package/v4/locales/nn.cjs +1 -0
- package/v4/locales/nn.js +1 -0
- package/v4/locales/no.cjs +1 -0
- package/v4/locales/no.js +1 -0
- package/v4/locales/ota.cjs +1 -0
- package/v4/locales/ota.js +1 -0
- package/v4/locales/pl.cjs +1 -0
- package/v4/locales/pl.js +1 -0
- package/v4/locales/ps.cjs +1 -0
- package/v4/locales/ps.js +1 -0
- package/v4/locales/pt-BR.cjs +1 -0
- package/v4/locales/pt-BR.js +1 -0
- package/v4/locales/pt.cjs +1 -0
- package/v4/locales/pt.js +1 -0
- package/v4/locales/ro.cjs +1 -0
- package/v4/locales/ro.js +1 -0
- package/v4/locales/ru.cjs +1 -0
- package/v4/locales/ru.js +1 -0
- package/v4/locales/sk.cjs +1 -0
- package/v4/locales/sk.js +1 -0
- package/v4/locales/sl.cjs +1 -0
- package/v4/locales/sl.js +1 -0
- package/v4/locales/sv.cjs +1 -0
- package/v4/locales/sv.js +1 -0
- package/v4/locales/ta.cjs +1 -0
- package/v4/locales/ta.js +1 -0
- package/v4/locales/tg.cjs +1 -0
- package/v4/locales/tg.js +1 -0
- package/v4/locales/th.cjs +1 -0
- package/v4/locales/th.js +1 -0
- package/v4/locales/tk.cjs +1 -0
- package/v4/locales/tk.js +1 -0
- package/v4/locales/tr.cjs +1 -0
- package/v4/locales/tr.js +1 -0
- package/v4/locales/uk.cjs +1 -0
- package/v4/locales/uk.js +1 -0
- package/v4/locales/ur.cjs +1 -0
- package/v4/locales/ur.js +1 -0
- package/v4/locales/uz.cjs +1 -0
- package/v4/locales/uz.js +1 -0
- package/v4/locales/vi.cjs +1 -0
- package/v4/locales/vi.js +1 -0
- package/v4/locales/yo.cjs +1 -0
- package/v4/locales/yo.js +1 -0
- package/v4/locales/zh-CN.cjs +1 -0
- package/v4/locales/zh-CN.js +1 -0
- package/v4/locales/zh-TW.cjs +1 -0
- package/v4/locales/zh-TW.js +1 -0
- package/v4/mini/checks.cjs +2 -1
- package/v4/mini/checks.d.cts +1 -1
- package/v4/mini/checks.d.ts +1 -1
- package/v4/mini/checks.js +1 -1
- package/v4/mini/schemas.cjs +11 -13
- package/v4/mini/schemas.d.cts +1 -4
- package/v4/mini/schemas.d.ts +1 -4
- package/v4/mini/schemas.js +9 -11
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { Validator } from "@seriousme/openapi-schema-validator";
|
|
2
|
-
import { describe, expect, test } from "vitest";
|
|
2
|
+
import { describe, expect, test, vi } from "vitest";
|
|
3
3
|
import * as z from "zod";
|
|
4
4
|
// import * as zCore from "zod/v4/core";
|
|
5
5
|
|
|
@@ -3235,11 +3235,31 @@ test("basic registry", () => {
|
|
|
3235
3235
|
`);
|
|
3236
3236
|
});
|
|
3237
3237
|
|
|
3238
|
-
|
|
3239
|
-
const
|
|
3240
|
-
const
|
|
3238
|
+
function countMapScanWork<T>(convert: () => T): { result: T; work: number } {
|
|
3239
|
+
const entries = Map.prototype.entries;
|
|
3240
|
+
const values = Map.prototype.values;
|
|
3241
|
+
let work = 0;
|
|
3242
|
+
const entriesSpy = vi.spyOn(Map.prototype, "entries").mockImplementation(function (this: Map<unknown, unknown>) {
|
|
3243
|
+
work += this.size;
|
|
3244
|
+
return entries.call(this);
|
|
3245
|
+
} as typeof Map.prototype.entries);
|
|
3246
|
+
const valuesSpy = vi.spyOn(Map.prototype, "values").mockImplementation(function (this: Map<unknown, unknown>) {
|
|
3247
|
+
work += this.size;
|
|
3248
|
+
return values.call(this);
|
|
3249
|
+
} as typeof Map.prototype.values);
|
|
3250
|
+
try {
|
|
3251
|
+
return { result: convert(), work };
|
|
3252
|
+
} finally {
|
|
3253
|
+
entriesSpy.mockRestore();
|
|
3254
|
+
valuesSpy.mockRestore();
|
|
3255
|
+
}
|
|
3256
|
+
}
|
|
3257
|
+
|
|
3258
|
+
test("large registry conversion performs linear map-scan work", () => {
|
|
3259
|
+
const count = 64;
|
|
3260
|
+
const convert = (size: number, withIntersection: boolean) => {
|
|
3241
3261
|
const registry = z.registry<{ id: string }>();
|
|
3242
|
-
for (let i = 0; i <
|
|
3262
|
+
for (let i = 0; i < size; i++) {
|
|
3243
3263
|
registry.add(
|
|
3244
3264
|
z.object({ id: z.string(), name: z.string(), count: z.number(), nested: z.object({ a: z.boolean() }) }),
|
|
3245
3265
|
{ id: `Type${i}` }
|
|
@@ -3247,55 +3267,53 @@ test("large registry converts in linear time", () => {
|
|
|
3247
3267
|
}
|
|
3248
3268
|
if (withIntersection) registry.add(z.object({ a: z.string() }).and(z.object({ b: z.string() })), { id: "Inter" });
|
|
3249
3269
|
|
|
3250
|
-
|
|
3251
|
-
const { schemas } = z.toJSONSchema(registry, { uri: (id) => `https://example.com/${id}.json` });
|
|
3252
|
-
return { schemas, elapsed: performance.now() - start };
|
|
3270
|
+
return countMapScanWork(() => z.toJSONSchema(registry, { uri: (id) => `https://example.com/${id}.json` }));
|
|
3253
3271
|
};
|
|
3254
3272
|
|
|
3255
|
-
const plain = convert(false);
|
|
3256
|
-
|
|
3257
|
-
expect(plain.schemas
|
|
3273
|
+
const plain = convert(count, false);
|
|
3274
|
+
const plainDouble = convert(count * 2, false);
|
|
3275
|
+
expect(Object.keys(plain.result.schemas)).toHaveLength(count);
|
|
3276
|
+
expect(plain.result.schemas.Type0).toMatchObject({
|
|
3258
3277
|
$id: "https://example.com/Type0.json",
|
|
3259
3278
|
type: "object",
|
|
3260
3279
|
properties: { nested: { type: "object" } },
|
|
3261
3280
|
});
|
|
3262
|
-
expect(plain.schemas[`Type${count - 1}`]!.$id).toBe(`https://example.com/Type${count - 1}.json`);
|
|
3263
|
-
|
|
3264
|
-
|
|
3265
|
-
expect(plain.elapsed).toBeLessThan(5000);
|
|
3281
|
+
expect(plain.result.schemas[`Type${count - 1}`]!.$id).toBe(`https://example.com/Type${count - 1}.json`);
|
|
3282
|
+
expect(plain.work).toBeGreaterThan(0);
|
|
3283
|
+
expect(plainDouble.work).toBeLessThanOrEqual(plain.work * 2);
|
|
3266
3284
|
|
|
3267
|
-
|
|
3268
|
-
const
|
|
3269
|
-
expect(folded.schemas.Inter).toMatchObject({ type: "object", properties: { a: {}, b: {} } });
|
|
3270
|
-
expect(folded.
|
|
3285
|
+
const folded = convert(count, true);
|
|
3286
|
+
const foldedDouble = convert(count * 2, true);
|
|
3287
|
+
expect(folded.result.schemas.Inter).toMatchObject({ type: "object", properties: { a: {}, b: {} } });
|
|
3288
|
+
expect(folded.work).toBeGreaterThan(0);
|
|
3289
|
+
expect(foldedDouble.work).toBeLessThanOrEqual(folded.work * 2);
|
|
3271
3290
|
});
|
|
3272
3291
|
|
|
3273
|
-
test("a registry of records with numeric keys
|
|
3274
|
-
const count =
|
|
3275
|
-
const convert = (key: (i: number) => z.core.$ZodType) => {
|
|
3292
|
+
test("a registry of records with numeric keys performs linear map-scan work", () => {
|
|
3293
|
+
const count = 64;
|
|
3294
|
+
const convert = (size: number, key: (i: number) => z.core.$ZodType) => {
|
|
3276
3295
|
const registry = z.registry<{ id: string }>();
|
|
3277
|
-
for (let i = 0; i <
|
|
3296
|
+
for (let i = 0; i < size; i++) {
|
|
3278
3297
|
registry.add(z.object({ m: z.record(key(i) as z.core.$ZodRecordKey, z.boolean()) }), { id: `Type${i}` });
|
|
3279
3298
|
}
|
|
3280
|
-
|
|
3281
|
-
const { schemas } = z.toJSONSchema(registry, { uri: (id) => `https://example.com/${id}.json` });
|
|
3282
|
-
return { schemas, elapsed: performance.now() - start };
|
|
3299
|
+
return countMapScanWork(() => z.toJSONSchema(registry, { uri: (id) => `https://example.com/${id}.json` }));
|
|
3283
3300
|
};
|
|
3284
3301
|
|
|
3285
|
-
const
|
|
3286
|
-
const
|
|
3287
|
-
expect(numeric.schemas.Type0).toMatchObject({
|
|
3302
|
+
const numeric = convert(count, () => z.number());
|
|
3303
|
+
const numericDouble = convert(count * 2, () => z.number());
|
|
3304
|
+
expect(numeric.result.schemas.Type0).toMatchObject({
|
|
3288
3305
|
properties: { m: { propertyNames: { type: "string", pattern: "^-?\\d+(?:\\.\\d+)?$" } } },
|
|
3289
3306
|
});
|
|
3307
|
+
expect(numeric.work).toBeGreaterThan(0);
|
|
3308
|
+
expect(numericDouble.work).toBeLessThanOrEqual(numeric.work * 2);
|
|
3290
3309
|
|
|
3291
|
-
|
|
3292
|
-
|
|
3293
|
-
|
|
3294
|
-
// An extracted key resolves through a map built once per conversion rather than a search per reference. There is no stable timing control for that — extraction has its own $defs cost, which swamps the difference — so this only pins the shape.
|
|
3295
|
-
const extracted = convert((i) => z.number().meta({ id: `Key${i}` }));
|
|
3296
|
-
expect(extracted.schemas.Type0).toMatchObject({
|
|
3310
|
+
const extracted = convert(count, (i) => z.number().meta({ id: `Key${i}` }));
|
|
3311
|
+
const extractedDouble = convert(count * 2, (i) => z.number().meta({ id: `Key${i}` }));
|
|
3312
|
+
expect(extracted.result.schemas.Type0).toMatchObject({
|
|
3297
3313
|
properties: { m: { propertyNames: { type: "string", pattern: "^-?\\d+(?:\\.\\d+)?$" } } },
|
|
3298
3314
|
});
|
|
3315
|
+
expect(extracted.work).toBeGreaterThan(0);
|
|
3316
|
+
expect(extractedDouble.work).toBeLessThanOrEqual(extracted.work * 2);
|
|
3299
3317
|
});
|
|
3300
3318
|
|
|
3301
3319
|
test("registry extracts unregistered subschemas into __shared", () => {
|
|
@@ -3849,6 +3867,31 @@ test("input JSON schema resolves requiredness past transform and catch wrappers"
|
|
|
3849
3867
|
expect(required(z.object({ a: z.transform((v: unknown) => String(v)).pipe(z.string()) }))).toEqual(["a"]);
|
|
3850
3868
|
});
|
|
3851
3869
|
|
|
3870
|
+
test("object required keys preserve shape order and direction", () => {
|
|
3871
|
+
const symbol = Symbol("symbol");
|
|
3872
|
+
const shape: z.ZodRawShape = {
|
|
3873
|
+
10: z.string(),
|
|
3874
|
+
2: z.string().optional(),
|
|
3875
|
+
plain: z.string(),
|
|
3876
|
+
optional: z.string().optional(),
|
|
3877
|
+
defaulted: z.string().default("x"),
|
|
3878
|
+
caught: z.string().catch("x"),
|
|
3879
|
+
[symbol]: z.string(),
|
|
3880
|
+
};
|
|
3881
|
+
Object.defineProperty(shape, "__proto__", { value: z.string(), enumerable: true });
|
|
3882
|
+
Object.defineProperty(shape, "hidden", { value: z.string(), enumerable: false });
|
|
3883
|
+
|
|
3884
|
+
const input = z.toJSONSchema(z.object(shape), { io: "input", unrepresentable: "any" });
|
|
3885
|
+
const output = z.toJSONSchema(z.object(shape), { io: "output", unrepresentable: "any" });
|
|
3886
|
+
|
|
3887
|
+
expect(Object.keys(input.properties!)).toEqual(["2", "10", "plain", "optional", "defaulted", "caught", "__proto__"]);
|
|
3888
|
+
expect(input.required).toEqual(["10", "plain", "caught", "__proto__"]);
|
|
3889
|
+
expect(output.required).toEqual(["10", "plain", "defaulted", "caught", "__proto__"]);
|
|
3890
|
+
expect(Object.prototype.hasOwnProperty.call(input.properties!, "__proto__")).toBe(true);
|
|
3891
|
+
expect(input.properties).not.toHaveProperty("hidden");
|
|
3892
|
+
expect(Object.getOwnPropertySymbols(input.properties!)).toEqual([]);
|
|
3893
|
+
});
|
|
3894
|
+
|
|
3852
3895
|
test("strip output-side examples from input JSON schema for codec", () => {
|
|
3853
3896
|
const codec = z
|
|
3854
3897
|
.codec(z.string(), z.number(), { decode: (s) => Number(s), encode: (n) => String(n) })
|
|
@@ -4679,3 +4722,12 @@ describe("intersection folding declines", () => {
|
|
|
4679
4722
|
expect(allOf(schema)).toHaveLength(3);
|
|
4680
4723
|
});
|
|
4681
4724
|
});
|
|
4725
|
+
|
|
4726
|
+
test("currency code", () => {
|
|
4727
|
+
expect(z.toJSONSchema(z.currencyCode())).toEqual({
|
|
4728
|
+
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
4729
|
+
type: "string",
|
|
4730
|
+
format: "currency_code",
|
|
4731
|
+
pattern: z.regexes.currencyCode.source,
|
|
4732
|
+
});
|
|
4733
|
+
});
|
package/src/v4/core/api.ts
CHANGED
|
@@ -58,16 +58,20 @@ export type CheckTypeParams<
|
|
|
58
58
|
> = Params<T, NonNullable<T["_zod"]["isst"] | T["_zod"]["issc"]>, "type" | "checks" | "error" | "check" | AlsoOmit>;
|
|
59
59
|
|
|
60
60
|
// String
|
|
61
|
-
export type $ZodStringParams = TypeParams<schemas.$ZodString<string>, "coerce"
|
|
61
|
+
export type $ZodStringParams = TypeParams<schemas.$ZodString<string>, "coerce"> & {
|
|
62
|
+
checks?: readonly checks.$ZodCheck<string>[];
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
function snapshotChecks<T extends { checks?: readonly checks.$ZodCheck<never>[] }>(def: T) {
|
|
66
|
+
if (def.checks) def.checks = [...def.checks];
|
|
67
|
+
return def as T & { checks?: NonNullable<T["checks"]>[number][] };
|
|
68
|
+
}
|
|
62
69
|
// @__NO_SIDE_EFFECTS__
|
|
63
70
|
export function _string<T extends schemas.$ZodString>(
|
|
64
71
|
Class: util.SchemaClass<T>,
|
|
65
72
|
params?: string | $ZodStringParams
|
|
66
73
|
): T {
|
|
67
|
-
return new Class({
|
|
68
|
-
type: "string",
|
|
69
|
-
...util.normalizeParams(params),
|
|
70
|
-
});
|
|
74
|
+
return new Class(snapshotChecks({ type: "string" as const, ...util.normalizeParams(params) }));
|
|
71
75
|
}
|
|
72
76
|
|
|
73
77
|
// @__NO_SIDE_EFFECTS__
|
|
@@ -75,11 +79,7 @@ export function _coercedString<T extends schemas.$ZodString>(
|
|
|
75
79
|
Class: util.SchemaClass<T>,
|
|
76
80
|
params?: string | $ZodStringParams
|
|
77
81
|
): T {
|
|
78
|
-
return new Class({
|
|
79
|
-
type: "string",
|
|
80
|
-
coerce: true,
|
|
81
|
-
...util.normalizeParams(params),
|
|
82
|
-
});
|
|
82
|
+
return new Class(snapshotChecks({ type: "string" as const, coerce: true, ...util.normalizeParams(params) }));
|
|
83
83
|
}
|
|
84
84
|
|
|
85
85
|
export type $ZodStringFormatParams = CheckTypeParams<
|
|
@@ -608,7 +608,9 @@ export function _isoDuration<T extends schemas.$ZodISODuration>(
|
|
|
608
608
|
}
|
|
609
609
|
|
|
610
610
|
// Number
|
|
611
|
-
export type $ZodNumberParams = TypeParams<schemas.$ZodNumber<number>, "coerce"
|
|
611
|
+
export type $ZodNumberParams = TypeParams<schemas.$ZodNumber<number>, "coerce"> & {
|
|
612
|
+
checks?: readonly checks.$ZodCheck<number>[];
|
|
613
|
+
};
|
|
612
614
|
export type $ZodNumberFormatParams = CheckTypeParams<schemas.$ZodNumberFormat, "format" | "coerce">;
|
|
613
615
|
export type $ZodCheckNumberFormatParams = CheckParams<checks.$ZodCheckNumberFormat, "format" | "when">;
|
|
614
616
|
// @__NO_SIDE_EFFECTS__
|
|
@@ -616,11 +618,7 @@ export function _number<T extends schemas.$ZodNumber>(
|
|
|
616
618
|
Class: util.SchemaClass<T>,
|
|
617
619
|
params?: string | $ZodNumberParams
|
|
618
620
|
): T {
|
|
619
|
-
return new Class({
|
|
620
|
-
type: "number",
|
|
621
|
-
checks: [],
|
|
622
|
-
...util.normalizeParams(params),
|
|
623
|
-
});
|
|
621
|
+
return new Class(snapshotChecks({ type: "number" as const, checks: [], ...util.normalizeParams(params) }));
|
|
624
622
|
}
|
|
625
623
|
|
|
626
624
|
// @__NO_SIDE_EFFECTS__
|
|
@@ -628,12 +626,9 @@ export function _coercedNumber<T extends schemas.$ZodNumber>(
|
|
|
628
626
|
Class: util.SchemaClass<T>,
|
|
629
627
|
params?: string | $ZodNumberParams
|
|
630
628
|
): T {
|
|
631
|
-
return new Class(
|
|
632
|
-
type: "number",
|
|
633
|
-
|
|
634
|
-
checks: [],
|
|
635
|
-
...util.normalizeParams(params),
|
|
636
|
-
});
|
|
629
|
+
return new Class(
|
|
630
|
+
snapshotChecks({ type: "number" as const, coerce: true, checks: [], ...util.normalizeParams(params) })
|
|
631
|
+
);
|
|
637
632
|
}
|
|
638
633
|
|
|
639
634
|
// @__NO_SIDE_EFFECTS__
|
|
@@ -1144,16 +1139,13 @@ export function _property<K extends string, T extends schemas.$ZodType>(
|
|
|
1144
1139
|
});
|
|
1145
1140
|
}
|
|
1146
1141
|
|
|
1147
|
-
|
|
1148
|
-
export type $ZodPropertiesParams = CheckTypeParams<schemas.$ZodProperties, "shape" | "when">;
|
|
1142
|
+
export type $ZodCheckPropertiesParams = CheckParams<checks.$ZodCheckProperties, "shape" | "when">;
|
|
1149
1143
|
// @__NO_SIDE_EFFECTS__
|
|
1150
1144
|
export function _properties<Shape extends schemas.$ZodShape>(
|
|
1151
|
-
Class: util.SchemaClass<schemas.$ZodProperties>,
|
|
1152
1145
|
shape: Shape,
|
|
1153
|
-
params?: string | $
|
|
1154
|
-
):
|
|
1155
|
-
return new
|
|
1156
|
-
type: "properties",
|
|
1146
|
+
params?: string | $ZodCheckPropertiesParams
|
|
1147
|
+
): checks.$ZodCheckProperties<Shape> {
|
|
1148
|
+
return new checks.$ZodCheckProperties({
|
|
1157
1149
|
check: "properties",
|
|
1158
1150
|
shape,
|
|
1159
1151
|
...util.normalizeParams(params),
|
package/src/v4/core/checks.ts
CHANGED
|
@@ -1000,7 +1000,7 @@ export const $ZodCheckEndsWith: core.$constructor<$ZodCheckEndsWith> = /*@__PURE
|
|
|
1000
1000
|
function handleCheckPropertyResult(
|
|
1001
1001
|
result: schemas.ParsePayload<unknown>,
|
|
1002
1002
|
payload: schemas.ParsePayload<unknown>,
|
|
1003
|
-
property: string
|
|
1003
|
+
property: string | symbol
|
|
1004
1004
|
) {
|
|
1005
1005
|
if (result.issues.length) {
|
|
1006
1006
|
payload.issues.push(...util.prefixIssues(property, result.issues));
|
|
@@ -1045,6 +1045,66 @@ export const $ZodCheckProperty: core.$constructor<$ZodCheckProperty> = /*@__PURE
|
|
|
1045
1045
|
}
|
|
1046
1046
|
);
|
|
1047
1047
|
|
|
1048
|
+
/////////////////////////////////////
|
|
1049
|
+
///// $ZodCheckProperties /////
|
|
1050
|
+
/////////////////////////////////////
|
|
1051
|
+
export interface $ZodCheckPropertiesDef<Shape extends schemas.$ZodShape = schemas.$ZodShape> extends $ZodCheckDef {
|
|
1052
|
+
check: "properties";
|
|
1053
|
+
shape: Shape;
|
|
1054
|
+
}
|
|
1055
|
+
|
|
1056
|
+
// typed over the input side, since each child's result is discarded; a literal widens to its primitive so spreading over a `string` property still type-checks (#6520)
|
|
1057
|
+
export type $ZodCheckPropertiesInput<Shape extends schemas.$ZodShape = schemas.$ZodShape> = {
|
|
1058
|
+
-readonly [k in keyof Shape]: util.Widen<core.input<Shape[k]>>;
|
|
1059
|
+
};
|
|
1060
|
+
|
|
1061
|
+
export interface $ZodCheckPropertiesInternals<Shape extends schemas.$ZodShape = schemas.$ZodShape>
|
|
1062
|
+
extends $ZodCheckInternals<$ZodCheckPropertiesInput<Shape>> {
|
|
1063
|
+
def: $ZodCheckPropertiesDef<Shape>;
|
|
1064
|
+
issc: errors.$ZodIssue;
|
|
1065
|
+
}
|
|
1066
|
+
|
|
1067
|
+
export interface $ZodCheckProperties<Shape extends schemas.$ZodShape = schemas.$ZodShape>
|
|
1068
|
+
extends $ZodCheck<$ZodCheckPropertiesInput<Shape>> {
|
|
1069
|
+
_zod: $ZodCheckPropertiesInternals<Shape>;
|
|
1070
|
+
// yields the check itself, so `.check(...z.properties(shape))` spread call sites keep working
|
|
1071
|
+
[Symbol.iterator](): Iterator<this>;
|
|
1072
|
+
}
|
|
1073
|
+
|
|
1074
|
+
export const $ZodCheckProperties: core.$constructor<$ZodCheckProperties> = /*@__PURE__*/ core.$constructor(
|
|
1075
|
+
"$ZodCheckProperties",
|
|
1076
|
+
(inst, def) => {
|
|
1077
|
+
$ZodCheck.init(inst, def);
|
|
1078
|
+
util.hide(inst, Symbol.iterator, function* () {
|
|
1079
|
+
yield inst;
|
|
1080
|
+
});
|
|
1081
|
+
|
|
1082
|
+
// key and schema snapshotted together: reading one live and the other cached lets a later mutation of the caller's shape object pair a stale key with a missing schema
|
|
1083
|
+
let entries!: [string | symbol, schemas.$ZodType][];
|
|
1084
|
+
inst._zod.check = (payload) => {
|
|
1085
|
+
// the base schema already typed the value, so only a nullish one is rejected here: the properties read on a primitive too, matching z.property() on a string's length
|
|
1086
|
+
if (payload.value == null) {
|
|
1087
|
+
payload.issues.push({ expected: "object", code: "invalid_type", input: payload.value, inst });
|
|
1088
|
+
return undefined;
|
|
1089
|
+
}
|
|
1090
|
+
entries ??= Reflect.ownKeys(def.shape).map((key) => [key, (def.shape as any)[key] as schemas.$ZodType]);
|
|
1091
|
+
const input = payload.value as any;
|
|
1092
|
+
let proms: Promise<any>[] | undefined;
|
|
1093
|
+
for (const [key, schema] of entries) {
|
|
1094
|
+
const result = schema._zod.run({ value: input[key], issues: [] }, {});
|
|
1095
|
+
if (result instanceof Promise) {
|
|
1096
|
+
proms ??= [];
|
|
1097
|
+
proms.push(result.then((result) => handleCheckPropertyResult(result, payload, key)));
|
|
1098
|
+
} else {
|
|
1099
|
+
handleCheckPropertyResult(result, payload, key);
|
|
1100
|
+
}
|
|
1101
|
+
}
|
|
1102
|
+
if (proms) return Promise.all(proms).then(() => undefined);
|
|
1103
|
+
return undefined;
|
|
1104
|
+
};
|
|
1105
|
+
}
|
|
1106
|
+
);
|
|
1107
|
+
|
|
1048
1108
|
///////////////////////////////////
|
|
1049
1109
|
///// $ZodCheckMimeType /////
|
|
1050
1110
|
///////////////////////////////////
|
package/src/v4/core/compile.ts
CHANGED
|
@@ -13,12 +13,12 @@ import {
|
|
|
13
13
|
isValidIPv6,
|
|
14
14
|
isValidJWT,
|
|
15
15
|
mergeValues,
|
|
16
|
-
parseURLObject,
|
|
17
16
|
stripTabAndNewline,
|
|
18
17
|
urlHostnameOk,
|
|
19
18
|
urlProtocolOk,
|
|
19
|
+
validateURL,
|
|
20
20
|
} from "./schemas.js";
|
|
21
|
-
import type {
|
|
21
|
+
import type { ParseContextInternal, ParsePayload, SomeType } from "./schemas.js";
|
|
22
22
|
import * as util from "./util.js";
|
|
23
23
|
|
|
24
24
|
/** @internal Sentinel the compiled fast path returns when validation fails. */
|
|
@@ -89,7 +89,7 @@ type SupportedCheck =
|
|
|
89
89
|
| checks.$ZodCheckLengthEquals
|
|
90
90
|
| checks.$ZodCheckStringFormat
|
|
91
91
|
| checks.$ZodCheckProperty
|
|
92
|
-
|
|
|
92
|
+
| checks.$ZodCheckProperties
|
|
93
93
|
| checks.$ZodCheckMimeType
|
|
94
94
|
| checks.$ZodCheckOverwrite
|
|
95
95
|
| { _zod: { def: { check: "custom"; fn?: (value: unknown) => boolean }; check?: (payload: unknown) => unknown } };
|
|
@@ -455,7 +455,7 @@ function generateChecks(doc: Doc, ctx: CompileContext, schema: SomeType, accesso
|
|
|
455
455
|
generatePropertyCheck(doc, ctx, def, currentAccessor);
|
|
456
456
|
break;
|
|
457
457
|
case "properties":
|
|
458
|
-
generatePropertiesChecks(doc, ctx, def, currentAccessor
|
|
458
|
+
generatePropertiesChecks(doc, ctx, def, currentAccessor);
|
|
459
459
|
break;
|
|
460
460
|
case "overwrite": {
|
|
461
461
|
// Overwrite transforms the value - create new variable for transformed result
|
|
@@ -616,20 +616,15 @@ function generateMimeTypeCheck(
|
|
|
616
616
|
function generatePropertiesChecks(
|
|
617
617
|
doc: Doc,
|
|
618
618
|
ctx: CompileContext,
|
|
619
|
-
def:
|
|
620
|
-
accessor: string
|
|
621
|
-
schemaRole: boolean
|
|
619
|
+
def: checks.$ZodCheckPropertiesDef,
|
|
620
|
+
accessor: string
|
|
622
621
|
): void {
|
|
623
|
-
// a custom `when` gates the assertion at runtime; inside a union a wrongly-run branch is absorbed as a branch failure rather than falling back, so refuse at codegen
|
|
622
|
+
// a custom `when` gates the assertion at runtime; inside a union a wrongly-run branch is absorbed as a branch failure rather than falling back, so refuse at codegen
|
|
624
623
|
if (def.when) {
|
|
625
624
|
throw new ZodCompileUnsupportedError(`check with a custom "when" condition`);
|
|
626
625
|
}
|
|
627
|
-
// matches the runtime gate
|
|
628
|
-
doc.write(
|
|
629
|
-
schemaRole
|
|
630
|
-
? `if (${accessor} === null || (typeof ${accessor} !== "object" && typeof ${accessor} !== "function")) return INVALID;`
|
|
631
|
-
: `if (${accessor} == null) return INVALID;`
|
|
632
|
-
);
|
|
626
|
+
// matches the runtime gate: the base schema already typed the value, so only a nullish one is rejected
|
|
627
|
+
doc.write(`if (${accessor} == null) return INVALID;`);
|
|
633
628
|
const shape = def.shape as Record<string | symbol, SomeType>;
|
|
634
629
|
for (const key of Reflect.ownKeys(shape)) {
|
|
635
630
|
// a symbol has no source literal, so it is hoisted as a constant
|
|
@@ -777,7 +772,21 @@ const PATTERN_IS_COMPLETE: Set<string> = new Set([
|
|
|
777
772
|
]);
|
|
778
773
|
|
|
779
774
|
// Returns the accessor holding the (possibly normalized) value after the check — url/normalize formats produce a new value like overwrite does. Never assigns to the incoming accessor: it may be a `const` or a property expression on user input.
|
|
780
|
-
function generateStringFormatCheck(doc: Doc, ctx: CompileContext, def: StringFormatDef, accessor: string): string
|
|
775
|
+
function generateStringFormatCheck(doc: Doc, ctx: CompileContext, def: StringFormatDef, accessor: string): string;
|
|
776
|
+
function generateStringFormatCheck(
|
|
777
|
+
doc: Doc,
|
|
778
|
+
ctx: CompileContext,
|
|
779
|
+
def: StringFormatDef,
|
|
780
|
+
accessor: string,
|
|
781
|
+
needsValue: boolean
|
|
782
|
+
): string | null;
|
|
783
|
+
function generateStringFormatCheck(
|
|
784
|
+
doc: Doc,
|
|
785
|
+
ctx: CompileContext,
|
|
786
|
+
def: StringFormatDef,
|
|
787
|
+
accessor: string,
|
|
788
|
+
needsValue = true
|
|
789
|
+
): string | null {
|
|
781
790
|
// Some string formats do runtime validation beyond their advertised pattern. For cheap pure utility checks, hoist the runtime function and call it so the fast path stays correct without cloning the utility logic into codegen.
|
|
782
791
|
const fmt = def.format;
|
|
783
792
|
if (fmt === "base64") {
|
|
@@ -825,7 +834,7 @@ function generateStringFormatCheck(doc: Doc, ctx: CompileContext, def: StringFor
|
|
|
825
834
|
formatDef.protocol !== undefined
|
|
826
835
|
) {
|
|
827
836
|
// Same three predicates the runtime calls, in the same order, so there is no second URL implementation to drift. Which options exist is known now, so the calls the runtime makes conditionally are emitted conditionally instead.
|
|
828
|
-
const parseConst = addConstant(ctx,
|
|
837
|
+
const parseConst = addConstant(ctx, validateURL);
|
|
829
838
|
const defConst = addConstant(ctx, def);
|
|
830
839
|
const trimVar = newVar(ctx);
|
|
831
840
|
const urlVar = newVar(ctx);
|
|
@@ -840,6 +849,7 @@ function generateStringFormatCheck(doc: Doc, ctx: CompileContext, def: StringFor
|
|
|
840
849
|
const protocolConst = addConstant(ctx, urlProtocolOk);
|
|
841
850
|
doc.write(`if (!${protocolConst}(${urlVar}, ${defConst}.protocol)) return INVALID;`);
|
|
842
851
|
}
|
|
852
|
+
if (!needsValue) return null;
|
|
843
853
|
const outputVar = newVar(ctx);
|
|
844
854
|
const outputExpr = formatDef.normalize ? `${urlVar}.href` : `${addConstant(ctx, stripTabAndNewline)}(${trimVar})`;
|
|
845
855
|
doc.write(`const ${outputVar} = ${outputExpr};`);
|
|
@@ -934,7 +944,6 @@ type SupportedSchemaType =
|
|
|
934
944
|
| "lazy"
|
|
935
945
|
| "pipe"
|
|
936
946
|
| "custom"
|
|
937
|
-
| "properties"
|
|
938
947
|
| "transform"
|
|
939
948
|
| "catch";
|
|
940
949
|
|
|
@@ -967,7 +976,7 @@ function generateCheck(
|
|
|
967
976
|
|
|
968
977
|
switch (type) {
|
|
969
978
|
case "string":
|
|
970
|
-
typeAccessor = generateStringCheck(doc, ctx, schema, accessor);
|
|
979
|
+
typeAccessor = generateStringCheck(doc, ctx, schema, accessor, buildsValue);
|
|
971
980
|
break;
|
|
972
981
|
case "number":
|
|
973
982
|
typeAccessor = generateNumberCheck(doc, schema, accessor);
|
|
@@ -1079,10 +1088,6 @@ function generateCheck(
|
|
|
1079
1088
|
case "custom":
|
|
1080
1089
|
typeAccessor = generateCustomCheck(doc, ctx, schema, accessor);
|
|
1081
1090
|
break;
|
|
1082
|
-
case "properties":
|
|
1083
|
-
generatePropertiesChecks(doc, ctx, (schema as $ZodProperties)._zod.def, accessor, true);
|
|
1084
|
-
typeAccessor = accessor;
|
|
1085
|
-
break;
|
|
1086
1091
|
case "transform":
|
|
1087
1092
|
typeAccessor = generateTransformCheck(doc, ctx, schema, accessor);
|
|
1088
1093
|
break;
|
|
@@ -1102,13 +1107,19 @@ function generateCheck(
|
|
|
1102
1107
|
return generateChecks(doc, ctx, schema, typeAccessor);
|
|
1103
1108
|
}
|
|
1104
1109
|
|
|
1105
|
-
function generateStringCheck(
|
|
1110
|
+
function generateStringCheck(
|
|
1111
|
+
doc: Doc,
|
|
1112
|
+
ctx: CompileContext,
|
|
1113
|
+
schema: SomeType,
|
|
1114
|
+
accessor: string,
|
|
1115
|
+
needsValue = true
|
|
1116
|
+
): string | null {
|
|
1106
1117
|
doc.write(`if (typeof ${accessor} !== "string") return INVALID;`);
|
|
1107
1118
|
|
|
1108
1119
|
// z.email() carries its format on the def, z.string().email() in def.checks; both route here so the format table has no second copy to drift from.
|
|
1109
1120
|
const def = schema._zod.def as unknown as StringFormatDef & { format?: string };
|
|
1110
1121
|
if (def.format === undefined) return accessor;
|
|
1111
|
-
return generateStringFormatCheck(doc, ctx, def, accessor);
|
|
1122
|
+
return generateStringFormatCheck(doc, ctx, def, accessor, needsValue);
|
|
1112
1123
|
}
|
|
1113
1124
|
|
|
1114
1125
|
function generateNumberCheck(doc: Doc, schema: SomeType, accessor: string): string {
|
package/src/v4/core/core.ts
CHANGED
|
@@ -79,9 +79,7 @@ export /*@__NO_SIDE_EFFECTS__*/ function $constructor<T extends ZodTrait, D = T[
|
|
|
79
79
|
// Cleared even on throw, so the shared descriptor never leaks one instance's internals into the next.
|
|
80
80
|
_zodDesc.value = undefined;
|
|
81
81
|
}
|
|
82
|
-
}
|
|
83
|
-
|
|
84
|
-
if (inst._zod.traits.has(name)) {
|
|
82
|
+
} else if (inst._zod.traits.has(name)) {
|
|
85
83
|
return;
|
|
86
84
|
}
|
|
87
85
|
|
|
@@ -16,7 +16,6 @@ import {
|
|
|
16
16
|
finalize,
|
|
17
17
|
handleUnrepresentable,
|
|
18
18
|
initializeContext,
|
|
19
|
-
isTransforming,
|
|
20
19
|
processSchema,
|
|
21
20
|
} from "./to-json-schema.js";
|
|
22
21
|
import { BIGINT_FORMAT_RANGES, NUMBER_FORMAT_RANGES, assignProp, getEnumValues } from "./util.js";
|
|
@@ -450,20 +449,16 @@ export const objectProcessor: Processor<schemas.$ZodObject> = (schema, ctx, _jso
|
|
|
450
449
|
}
|
|
451
450
|
|
|
452
451
|
// required keys
|
|
453
|
-
const
|
|
454
|
-
const
|
|
455
|
-
[
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
return field._zod.optout === undefined;
|
|
461
|
-
}
|
|
462
|
-
})
|
|
463
|
-
);
|
|
452
|
+
const requiredKeys: string[] = [];
|
|
453
|
+
for (const key of Object.keys(shape)) {
|
|
454
|
+
const field = def.shape[key]!;
|
|
455
|
+
if (ctx.io === "input" ? inputOptin(field) === undefined : field._zod.optout === undefined) {
|
|
456
|
+
requiredKeys.push(key);
|
|
457
|
+
}
|
|
458
|
+
}
|
|
464
459
|
|
|
465
|
-
if (requiredKeys.
|
|
466
|
-
json.required =
|
|
460
|
+
if (requiredKeys.length > 0) {
|
|
461
|
+
json.required = requiredKeys;
|
|
467
462
|
}
|
|
468
463
|
|
|
469
464
|
// catchall
|
|
@@ -481,54 +476,6 @@ export const objectProcessor: Processor<schemas.$ZodObject> = (schema, ctx, _jso
|
|
|
481
476
|
}
|
|
482
477
|
};
|
|
483
478
|
|
|
484
|
-
// asserts named properties in place and passes everything else through, so no additionalProperties constraint is emitted
|
|
485
|
-
export const propertiesProcessor: Processor<schemas.$ZodProperties> = (schema, ctx, _json, params) => {
|
|
486
|
-
const json = _json as JSONSchema.ObjectSchema;
|
|
487
|
-
const def = schema._zod.def;
|
|
488
|
-
|
|
489
|
-
// dropping a symbol key silently would emit a schema that asserts less than this one does
|
|
490
|
-
if (
|
|
491
|
-
Object.getOwnPropertySymbols(def.shape).length &&
|
|
492
|
-
handleUnrepresentable(schema, ctx, json, params, "Symbol keys cannot be represented in JSON Schema")
|
|
493
|
-
) {
|
|
494
|
-
return;
|
|
495
|
-
}
|
|
496
|
-
|
|
497
|
-
// the parsed value is the input, so an output-mode emission would describe a transformed value this schema never returns
|
|
498
|
-
if (ctx.io === "output") {
|
|
499
|
-
for (const key in def.shape) {
|
|
500
|
-
if (
|
|
501
|
-
isTransforming(def.shape[key]!) &&
|
|
502
|
-
handleUnrepresentable(
|
|
503
|
-
schema,
|
|
504
|
-
ctx,
|
|
505
|
-
json,
|
|
506
|
-
params,
|
|
507
|
-
`z.properties() returns its input, so the output of a transforming schema at key "${key}" cannot be represented in JSON Schema`
|
|
508
|
-
)
|
|
509
|
-
) {
|
|
510
|
-
return;
|
|
511
|
-
}
|
|
512
|
-
}
|
|
513
|
-
}
|
|
514
|
-
|
|
515
|
-
json.type = "object";
|
|
516
|
-
json.properties = {};
|
|
517
|
-
for (const key in def.shape) {
|
|
518
|
-
assignProp(
|
|
519
|
-
json.properties,
|
|
520
|
-
key,
|
|
521
|
-
processSchema(def.shape[key]!, ctx as any, {
|
|
522
|
-
...params,
|
|
523
|
-
path: [...params.path, "properties", key],
|
|
524
|
-
})
|
|
525
|
-
);
|
|
526
|
-
}
|
|
527
|
-
// input-side optionality in both modes: the parsed value is the input, so a defaulted key that stays absent must not be required of the output either
|
|
528
|
-
const required = Object.keys(def.shape).filter((key) => inputOptin(def.shape[key]!) === undefined);
|
|
529
|
-
if (required.length > 0) json.required = required;
|
|
530
|
-
};
|
|
531
|
-
|
|
532
479
|
export const unionProcessor: Processor<schemas.$ZodUnion> = (schema, ctx, json, params) => {
|
|
533
480
|
const def = schema._zod.def as schemas.$ZodUnionDef;
|
|
534
481
|
// Exclusive unions (inclusive === false) use oneOf (exactly one match) instead of anyOf (one or more matches). This includes both z.xor() and discriminated unions
|
|
@@ -904,7 +851,6 @@ export const allProcessors: Record<string, Processor<any>> = {
|
|
|
904
851
|
file: fileProcessor,
|
|
905
852
|
success: successProcessor,
|
|
906
853
|
custom: customProcessor,
|
|
907
|
-
properties: propertiesProcessor,
|
|
908
854
|
function: functionProcessor,
|
|
909
855
|
transform: transformProcessor,
|
|
910
856
|
map: mapProcessor,
|
package/src/v4/core/memoizer.ts
CHANGED
|
@@ -28,9 +28,9 @@ type WithState = { [STATE]?: State };
|
|
|
28
28
|
|
|
29
29
|
const NO_ISSUES: errors.$ZodRawIssue[] = [];
|
|
30
30
|
|
|
31
|
-
// a value a cycle can close through
|
|
31
|
+
// a value a cycle can close through
|
|
32
32
|
function isRef(value: unknown): value is object {
|
|
33
|
-
return value !== null &&
|
|
33
|
+
return value !== null && typeof value === "object";
|
|
34
34
|
}
|
|
35
35
|
|
|
36
36
|
// Receivers prefix paths in place, so the cache and every hand-out need their own copies.
|
|
@@ -90,9 +90,6 @@ function isRecursive(inst: $ZodType, stack: Set<object>, resolve: boolean): Answ
|
|
|
90
90
|
check(def.catchall);
|
|
91
91
|
break;
|
|
92
92
|
}
|
|
93
|
-
case "properties":
|
|
94
|
-
merge(shape(def.shape, false));
|
|
95
|
-
break;
|
|
96
93
|
case "array":
|
|
97
94
|
check(def.element);
|
|
98
95
|
break;
|
package/src/v4/core/regexes.ts
CHANGED
|
@@ -98,6 +98,10 @@ export const e164: RegExp = /^\+[1-9]\d{6,14}$/;
|
|
|
98
98
|
// Credit card shape: 12–19 digits, optionally separated by single spaces or single hyphens. ISO/IEC 7812 caps the PAN at 19 digits; 12 is the shortest issued length (Maestro).
|
|
99
99
|
export const creditCard: RegExp = /^\d(?:[ -]?\d){11,18}$/;
|
|
100
100
|
|
|
101
|
+
// ISO 4217 alpha codes from the SIX list, regenerated by scripts/update-iso-4217.ts
|
|
102
|
+
export const currencyCode: RegExp =
|
|
103
|
+
/^(?:AED|AFN|ALL|AMD|AOA|ARS|AUD|AWG|AZN|BAM|BBD|BDT|BHD|BIF|BMD|BND|BOB|BOV|BRL|BSD|BTN|BWP|BYN|BZD|CAD|CDF|CHE|CHF|CHW|CLF|CLP|CNY|COP|COU|CRC|CUP|CVE|CZK|DJF|DKK|DOP|DZD|EGP|ERN|ETB|EUR|FJD|FKP|GBP|GEL|GHS|GIP|GMD|GNF|GTQ|GYD|HKD|HNL|HTG|HUF|IDR|ILS|INR|IQD|IRR|ISK|JMD|JOD|JPY|KES|KGS|KHR|KMF|KPW|KRW|KWD|KYD|KZT|LAK|LBP|LKR|LRD|LSL|LYD|MAD|MDL|MGA|MKD|MMK|MNT|MOP|MRU|MUR|MVR|MWK|MXN|MXV|MYR|MZN|NAD|NGN|NIO|NOK|NPR|NZD|OMR|PAB|PEN|PGK|PHP|PKR|PLN|PYG|QAR|RON|RSD|RUB|RWF|SAR|SBD|SCR|SDG|SEK|SGD|SHP|SLE|SOS|SRD|SSP|STN|SVC|SYP|SZL|THB|TJS|TMT|TND|TOP|TRY|TTD|TWD|TZS|UAH|UGX|USD|USN|UYI|UYU|UYW|UZS|VED|VES|VND|VUV|WST|XAD|XAF|XAG|XAU|XBA|XBB|XBC|XBD|XCD|XCG|XDR|XOF|XPD|XPF|XPT|XSU|XTS|XUA|XXX|YER|ZAR|ZMW|ZWG)$/;
|
|
104
|
+
|
|
101
105
|
// iban electronic format: 2-letter country, check digits 02-98 (the only values `98 - remainder` can produce), 11-30 bban characters
|
|
102
106
|
export const iban: RegExp = /^[A-Z]{2}(?!00|01|99)\d{2}[A-Z0-9]{11,30}$/;
|
|
103
107
|
|