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
package/README.md
CHANGED
|
@@ -155,13 +155,13 @@ try {
|
|
|
155
155
|
expected: 'string',
|
|
156
156
|
code: 'invalid_type',
|
|
157
157
|
path: [ 'username' ],
|
|
158
|
-
message: 'Invalid input: expected string'
|
|
158
|
+
message: 'Invalid input: expected string, received number'
|
|
159
159
|
},
|
|
160
160
|
{
|
|
161
161
|
expected: 'number',
|
|
162
162
|
code: 'invalid_type',
|
|
163
163
|
path: [ 'xp' ],
|
|
164
|
-
message: 'Invalid input: expected number'
|
|
164
|
+
message: 'Invalid input: expected number, received string'
|
|
165
165
|
}
|
|
166
166
|
] */
|
|
167
167
|
}
|
package/package.json
CHANGED
package/src/v4/classic/checks.ts
CHANGED
|
@@ -2,6 +2,7 @@ import type { $ZodBigIntFormats } from "../core/checks.js";
|
|
|
2
2
|
import * as core from "../core/index.js";
|
|
3
3
|
import { util, type $ZodNumberFormats } from "../core/index.js";
|
|
4
4
|
import * as processors from "../core/json-schema-processors.js";
|
|
5
|
+
import * as regexes from "../core/regexes.js";
|
|
5
6
|
import type { StandardSchemaWithJSONProps } from "../core/standard-schema.js";
|
|
6
7
|
import { createStandardJSONSchemaMethod, createToJSONSchemaMethod } from "../core/to-json-schema.js";
|
|
7
8
|
import en from "../locales/en.js";
|
|
@@ -770,8 +771,8 @@ export function url(params?: string | core.$ZodURLParams): ZodURL {
|
|
|
770
771
|
|
|
771
772
|
export function httpUrl(params?: string | Omit<core.$ZodURLParams, "protocol" | "hostname">): ZodURL {
|
|
772
773
|
return core._url(ZodURL, {
|
|
773
|
-
protocol:
|
|
774
|
-
hostname:
|
|
774
|
+
protocol: regexes.httpProtocol,
|
|
775
|
+
hostname: regexes.domain,
|
|
775
776
|
...util.normalizeParams(params),
|
|
776
777
|
});
|
|
777
778
|
}
|
|
@@ -1081,11 +1082,15 @@ export function stringFormat<Format extends string>(
|
|
|
1081
1082
|
}
|
|
1082
1083
|
|
|
1083
1084
|
export function hostname(_params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat<"hostname"> {
|
|
1084
|
-
return core._stringFormat(ZodCustomStringFormat, "hostname",
|
|
1085
|
+
return core._stringFormat(ZodCustomStringFormat, "hostname", regexes.hostname, _params) as any;
|
|
1085
1086
|
}
|
|
1086
1087
|
|
|
1087
1088
|
export function hex(_params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat<"hex"> {
|
|
1088
|
-
return core._stringFormat(ZodCustomStringFormat, "hex",
|
|
1089
|
+
return core._stringFormat(ZodCustomStringFormat, "hex", regexes.hex, _params) as any;
|
|
1090
|
+
}
|
|
1091
|
+
|
|
1092
|
+
export function currencyCode(_params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat<"currency_code"> {
|
|
1093
|
+
return core._stringFormat(ZodCustomStringFormat, "currency_code", regexes.currencyCode, _params) as any;
|
|
1089
1094
|
}
|
|
1090
1095
|
|
|
1091
1096
|
export function hash<Alg extends util.HashAlgorithm, Enc extends util.HashEncoding = "hex">(
|
|
@@ -2791,28 +2796,6 @@ export const ZodCustom: core.$constructor<ZodCustom> = /*@__PURE__*/ core.$const
|
|
|
2791
2796
|
inst._zod.processJSONSchema = (ctx, json, params) => processors.customProcessor(inst, ctx, json, params);
|
|
2792
2797
|
});
|
|
2793
2798
|
|
|
2794
|
-
// ZodProperties
|
|
2795
|
-
export interface ZodProperties<Shape extends core.$ZodShape = core.$ZodShape>
|
|
2796
|
-
extends _ZodType<core.$ZodPropertiesInternals<Shape>>,
|
|
2797
|
-
core.$ZodProperties<Shape> {
|
|
2798
|
-
"~standard": ZodStandardSchemaWithJSON<this>;
|
|
2799
|
-
}
|
|
2800
|
-
export const ZodProperties: core.$constructor<ZodProperties> = /*@__PURE__*/ core.$constructor(
|
|
2801
|
-
"ZodProperties",
|
|
2802
|
-
(inst, def) => {
|
|
2803
|
-
_ensureDefaultMemoizer();
|
|
2804
|
-
core.$ZodProperties.init(inst, def);
|
|
2805
|
-
ZodType.init(inst, def);
|
|
2806
|
-
}
|
|
2807
|
-
);
|
|
2808
|
-
|
|
2809
|
-
export function properties<Shape extends core.$ZodShape>(
|
|
2810
|
-
shape: Shape,
|
|
2811
|
-
params?: string | core.$ZodPropertiesParams
|
|
2812
|
-
): ZodProperties<Shape> {
|
|
2813
|
-
return core._properties(ZodProperties, shape, params) as any;
|
|
2814
|
-
}
|
|
2815
|
-
|
|
2816
2799
|
// custom checks
|
|
2817
2800
|
export function check<O = unknown>(fn: core.CheckFn<O>): core.$ZodCheck<O> {
|
|
2818
2801
|
const ch = new core.$ZodCheck({
|
|
@@ -2860,7 +2843,7 @@ type ZodInstanceOfParams = core.Params<
|
|
|
2860
2843
|
export interface ZodInstanceOf<T = unknown> extends ZodCustom<T, T> {
|
|
2861
2844
|
properties<Shape extends core.$ZodShape>(
|
|
2862
2845
|
shape: Shape,
|
|
2863
|
-
params?: string | core.$
|
|
2846
|
+
params?: string | core.$ZodCheckPropertiesParams
|
|
2864
2847
|
): ZodInstanceOf<T & core.$InferObjectInput<Shape, {}>>;
|
|
2865
2848
|
}
|
|
2866
2849
|
export const ZodInstanceOf: core.$constructor<ZodInstanceOf> = /*@__PURE__*/ core.$constructor(
|
|
@@ -2869,9 +2852,9 @@ export const ZodInstanceOf: core.$constructor<ZodInstanceOf> = /*@__PURE__*/ cor
|
|
|
2869
2852
|
ZodCustom.init(inst, def);
|
|
2870
2853
|
},
|
|
2871
2854
|
{
|
|
2872
|
-
properties(shape: core.$ZodShape, params?: string | core.$
|
|
2855
|
+
properties(shape: core.$ZodShape, params?: string | core.$ZodCheckPropertiesParams) {
|
|
2873
2856
|
// asserts in place, so the narrowed output type is truthful without a wrapper
|
|
2874
|
-
return this.check(
|
|
2857
|
+
return this.check(core._properties(shape, params)) as any;
|
|
2875
2858
|
},
|
|
2876
2859
|
}
|
|
2877
2860
|
);
|
|
@@ -352,6 +352,7 @@ test("checks", () => {
|
|
|
352
352
|
const _b: z.core.$ZodCheck = {} as any as z.core.$ZodStringFormatChecks;
|
|
353
353
|
const _c: z.core.$ZodType = {} as any as z.core.$ZodTypes;
|
|
354
354
|
const _d: z.core.$ZodType = {} as any as z.core.$ZodStringFormatTypes;
|
|
355
|
+
z.currencyCode() satisfies z.core.$ZodStringFormatTypes;
|
|
355
356
|
});
|
|
356
357
|
|
|
357
358
|
test("assignability to $ZodType", () => {
|
|
@@ -924,17 +924,8 @@ test("a deferred edge stops being assumed a cycle once the graph resolves", () =
|
|
|
924
924
|
expect(z.core.isRecursiveSchema(Cyclic)).toBe(true);
|
|
925
925
|
});
|
|
926
926
|
|
|
927
|
-
// `z.object` resolves its shape by spread, so a non-enumerable key is no part of it
|
|
928
|
-
test("a non-enumerable key
|
|
929
|
-
const props = (enumerable: boolean) => {
|
|
930
|
-
const shape: Record<string, any> = { id: z.number() };
|
|
931
|
-
const P = z.properties(shape);
|
|
932
|
-
Object.defineProperty(shape, "next", { value: z.lazy(() => z.optional(P)), enumerable, configurable: true });
|
|
933
|
-
return P;
|
|
934
|
-
};
|
|
935
|
-
expect(z.core.isRecursiveSchema(props(true) as any)).toBe(true);
|
|
936
|
-
expect(z.core.isRecursiveSchema(props(false) as any)).toBe(true);
|
|
937
|
-
|
|
927
|
+
// `z.object` resolves its shape by spread, so a non-enumerable key is no part of it
|
|
928
|
+
test("a non-enumerable key does not count for z.object", () => {
|
|
938
929
|
const object = (enumerable: boolean) => {
|
|
939
930
|
const shape: Record<string, any> = { id: z.number() };
|
|
940
931
|
const O: any = z.object(shape);
|
|
@@ -80,8 +80,6 @@ test("first party switch", () => {
|
|
|
80
80
|
break;
|
|
81
81
|
case "file":
|
|
82
82
|
break;
|
|
83
|
-
case "properties":
|
|
84
|
-
break;
|
|
85
83
|
case "lazy":
|
|
86
84
|
break;
|
|
87
85
|
case "function":
|
|
@@ -172,8 +170,6 @@ test("$ZodSchemaTypes", () => {
|
|
|
172
170
|
break;
|
|
173
171
|
case "lazy":
|
|
174
172
|
break;
|
|
175
|
-
case "properties":
|
|
176
|
-
break;
|
|
177
173
|
case "function":
|
|
178
174
|
break;
|
|
179
175
|
|
|
@@ -105,6 +105,28 @@ test("deferred initializers are released after construction", () => {
|
|
|
105
105
|
expect(z.object({ a: z.string() })._zod.deferred).toEqual(undefined);
|
|
106
106
|
});
|
|
107
107
|
|
|
108
|
+
test("trait initializers run once per instance across repeated entry", () => {
|
|
109
|
+
const calls: string[] = [];
|
|
110
|
+
const Base: core.$constructor<any> = core.$constructor<any>("CountedBase", () => calls.push("base"));
|
|
111
|
+
const Child: core.$constructor<any> = core.$constructor<any>("CountedChild", (inst, def) => {
|
|
112
|
+
calls.push("child");
|
|
113
|
+
Base.init(inst, def);
|
|
114
|
+
Base.init(inst, def);
|
|
115
|
+
});
|
|
116
|
+
const constructed = new Child({});
|
|
117
|
+
Child.init(constructed, {});
|
|
118
|
+
const direct = Object.create({});
|
|
119
|
+
Child.init(direct, {});
|
|
120
|
+
Child.init(direct, {});
|
|
121
|
+
expect(calls).toEqual(["child", "base", "child", "base"]);
|
|
122
|
+
for (const inst of [constructed, direct]) {
|
|
123
|
+
expect([...inst._zod.traits]).toEqual(["CountedChild", "CountedBase"]);
|
|
124
|
+
expect(inst instanceof Base).toBe(true);
|
|
125
|
+
expect(inst instanceof Child).toBe(true);
|
|
126
|
+
}
|
|
127
|
+
expect(constructed._zod.traits).not.toBe(direct._zod.traits);
|
|
128
|
+
});
|
|
129
|
+
|
|
108
130
|
test("a trait initializer called directly still installs its members", () => {
|
|
109
131
|
// a direct `init` installs onto the receiver's own prototype, since it is not below the constructor's
|
|
110
132
|
z.string();
|
|
@@ -90,7 +90,7 @@ test("z.properties", () => {
|
|
|
90
90
|
expect(obj.safeParse(input).error!.issues.map((i) => [i.code, i.path])).toEqual([["invalid_type", []]]);
|
|
91
91
|
}
|
|
92
92
|
|
|
93
|
-
// Known looseness versus the longhand, pinned so it stays deliberate: the
|
|
93
|
+
// Known looseness versus the longhand, pinned so it stays deliberate: the check is typed over the whole shape, and `$ZodCheckInternals.check()` is a method, so TypeScript compares it bivariantly and accepts a check type that is a subtype of the target. Naming a key the target lacks therefore compiles here and fails at parse time, where the equivalent chain of `z.property()` calls rejects it outright.
|
|
94
94
|
z.object({ a: z.string() }).check(...z.properties({ a: z.literal("x"), b: z.literal("y") }));
|
|
95
95
|
expect(
|
|
96
96
|
z
|
|
@@ -2,6 +2,45 @@ import { expect, expectTypeOf, test } from "vitest";
|
|
|
2
2
|
|
|
3
3
|
import * as z from "zod/v4";
|
|
4
4
|
|
|
5
|
+
test("number factory checks", () => {
|
|
6
|
+
const schema = z.number({ checks: [z.gte(1), z.lte(3)] as const });
|
|
7
|
+
const chained = z.number().min(1).max(3);
|
|
8
|
+
expectTypeOf<z.output<typeof schema>>().toEqualTypeOf<number>();
|
|
9
|
+
for (const value of [0, 1, 3, 4, Number.NaN, "2", undefined]) {
|
|
10
|
+
expect(z.validate(schema, value)).toBe(z.validate(chained, value));
|
|
11
|
+
expect(schema.safeParse(value)).toEqual(chained.safeParse(value));
|
|
12
|
+
}
|
|
13
|
+
expect(z.toJSONSchema(schema)).toEqual(z.toJSONSchema(chained));
|
|
14
|
+
expect(schema.minValue).toBe(1);
|
|
15
|
+
expect(schema.maxValue).toBe(3);
|
|
16
|
+
expect(z.coerce.number({ checks: [z.gte(1)] }).parse("2")).toBe(2);
|
|
17
|
+
const overwritten = z.number({ checks: [z.overwrite<number>((value) => value + 1), z.gte(2)] });
|
|
18
|
+
expect(overwritten.parse(1)).toBe(2);
|
|
19
|
+
expect(z.validate(overwritten, 0)).toBe(false);
|
|
20
|
+
// @ts-expect-error length checks cannot validate numbers
|
|
21
|
+
z.number({ checks: [z.minLength(1)] });
|
|
22
|
+
});
|
|
23
|
+
|
|
24
|
+
test("number factory checks snapshot caller arrays", () => {
|
|
25
|
+
for (const factory of [z.number, z.coerce.number]) {
|
|
26
|
+
const checks = [z.gte(1)];
|
|
27
|
+
const schema = factory({ checks });
|
|
28
|
+
let reads = 0;
|
|
29
|
+
factory({
|
|
30
|
+
get checks() {
|
|
31
|
+
if (++reads > 1) throw new Error("checks read twice");
|
|
32
|
+
return checks;
|
|
33
|
+
},
|
|
34
|
+
});
|
|
35
|
+
expect(reads).toBe(1);
|
|
36
|
+
const before = z.toJSONSchema(schema);
|
|
37
|
+
checks.push(z.gte(10));
|
|
38
|
+
expect(z.validate(schema, 3)).toBe(true);
|
|
39
|
+
expect(z.validate(z.compile(schema), 3)).toBe(true);
|
|
40
|
+
expect(z.toJSONSchema(schema)).toEqual(before);
|
|
41
|
+
}
|
|
42
|
+
});
|
|
43
|
+
|
|
5
44
|
test("z.number() basic validation", () => {
|
|
6
45
|
const schema = z.number();
|
|
7
46
|
expect(schema.parse(1234)).toEqual(1234);
|
|
@@ -1,25 +1,15 @@
|
|
|
1
1
|
import { expect, expectTypeOf, test } from "vitest";
|
|
2
2
|
import * as z from "zod/v4";
|
|
3
3
|
|
|
4
|
-
test("z.properties is a
|
|
5
|
-
const
|
|
4
|
+
test("z.properties is a check", () => {
|
|
5
|
+
const s = z.object({ a: z.string(), b: z.string() }).check(z.properties({ a: z.literal("x"), b: z.literal("y") }));
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
// asserts in place: extra keys pass, and every failing key reports
|
|
8
|
+
expect(s.safeParse({ a: "x", b: "y", extra: 1 }).success).toBe(true);
|
|
9
|
+
expect(s.safeParse({ a: "!", b: "!" }).error!.issues.map((i) => i.path)).toEqual([["a"], ["b"]]);
|
|
8
10
|
|
|
9
|
-
//
|
|
10
|
-
const
|
|
11
|
-
expect(p.parse(ok)).toBe(ok);
|
|
12
|
-
|
|
13
|
-
// every failing key reports
|
|
14
|
-
expect(p.safeParse({ a: "!", b: "!" }).error!.issues.map((i) => i.path)).toEqual([["a"], ["b"]]);
|
|
15
|
-
|
|
16
|
-
// as a schema it is the type gate and infers an object shape, so a primitive is a type error
|
|
17
|
-
for (const input of [null, undefined, 5, "abc"]) {
|
|
18
|
-
expect(p.safeParse(input).error!.issues.map((i) => [i.code, i.path])).toEqual([["invalid_type", []]]);
|
|
19
|
-
}
|
|
20
|
-
|
|
21
|
-
// as a check the base already typed the value, so the properties are asserted on whatever it produced — a string's length, as z.property() does
|
|
22
|
-
const long = z.string().check(...z.properties({ length: z.number().min(3) }));
|
|
11
|
+
// the base already typed the value, so the properties are asserted on whatever it produced — a string's length, as z.property() does
|
|
12
|
+
const long = z.string().check(z.properties({ length: z.number().min(3) }));
|
|
23
13
|
expect(long.parse("abc")).toBe("abc");
|
|
24
14
|
expect(long.safeParse("ab").error!.issues.map((i) => i.path)).toEqual([["length"]]);
|
|
25
15
|
expect(
|
|
@@ -29,29 +19,19 @@ test("z.properties is a schema", () => {
|
|
|
29
19
|
.error!.issues.map((i) => i.path)
|
|
30
20
|
).toEqual([["length"]]);
|
|
31
21
|
|
|
32
|
-
//
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
expectTypeOf<z.infer<typeof opt>>().toEqualTypeOf<{ a?: string | undefined }>();
|
|
22
|
+
// not a schema
|
|
23
|
+
// @ts-expect-error a check has no parse of its own
|
|
24
|
+
z.properties({ a: z.string() }).parse;
|
|
36
25
|
});
|
|
37
26
|
|
|
38
27
|
test("z.properties discards transformed output", () => {
|
|
39
28
|
// asserts and never writes back, exactly as z.property() behaves; a nested object schema rebuilds its output even without transforms, so identity cannot distinguish the two
|
|
40
|
-
const
|
|
29
|
+
const s = z.any().check(z.properties({ a: z.string().transform((s) => s.toUpperCase()) }));
|
|
41
30
|
const input = { a: "hi" };
|
|
42
|
-
expect(
|
|
31
|
+
expect(s.parse(input)).toBe(input);
|
|
43
32
|
expect(input.a).toBe("hi");
|
|
44
33
|
});
|
|
45
34
|
|
|
46
|
-
test("z.properties narrows through intersection", () => {
|
|
47
|
-
const httpsUrl = z.instanceof(URL).and(z.properties({ protocol: z.literal("https:") }));
|
|
48
|
-
expectTypeOf<z.infer<typeof httpsUrl>>().toEqualTypeOf<URL & { protocol: "https:" }>();
|
|
49
|
-
|
|
50
|
-
const u = new URL("https://example.com");
|
|
51
|
-
expect(httpsUrl.parse(u)).toBe(u);
|
|
52
|
-
expect(httpsUrl.safeParse(new URL("http://example.com")).success).toBe(false);
|
|
53
|
-
});
|
|
54
|
-
|
|
55
35
|
test("z.instanceof().properties()", () => {
|
|
56
36
|
const httpsUrl = z.instanceof(URL).properties({ protocol: z.literal("https:") });
|
|
57
37
|
expectTypeOf<z.infer<typeof httpsUrl>>().toEqualTypeOf<URL & { protocol: "https:" }>();
|
|
@@ -65,38 +45,23 @@ test("z.instanceof().properties()", () => {
|
|
|
65
45
|
expectTypeOf<z.infer<typeof chained>>().toEqualTypeOf<URL & { protocol: "https:" } & { port: "" }>();
|
|
66
46
|
expect(chained.safeParse(u).success).toBe(true);
|
|
67
47
|
expect(chained.safeParse(new URL("https://example.com:8443")).error!.issues.map((i) => i.path)).toEqual([["port"]]);
|
|
48
|
+
|
|
49
|
+
// nothing is written back, so the narrowing is over the shape's input side
|
|
50
|
+
const W = z.instanceof(URL).properties({ search: z.string().transform((s) => s.length) });
|
|
51
|
+
expectTypeOf<z.infer<typeof W>>().toEqualTypeOf<URL & { search: string }>();
|
|
68
52
|
});
|
|
69
53
|
|
|
70
54
|
test("z.properties spreads into .check()", () => {
|
|
71
|
-
// the
|
|
55
|
+
// the 4.5 array call sites: the check yields itself from Symbol.iterator
|
|
72
56
|
const p = z.properties({ a: z.literal("x") });
|
|
73
|
-
|
|
74
|
-
expect(spread).toEqual([p]);
|
|
57
|
+
expect([...p]).toEqual([p]);
|
|
75
58
|
|
|
76
59
|
const s = z.object({ a: z.string() }).check(...z.properties({ a: z.literal("x") }));
|
|
77
60
|
expect(s.safeParse({ a: "x" }).success).toBe(true);
|
|
78
61
|
expect(s.safeParse({ a: "!" }).error!.issues.map((i) => i.path)).toEqual([["a"]]);
|
|
79
62
|
});
|
|
80
63
|
|
|
81
|
-
test("z.properties
|
|
82
|
-
const p = z.properties({ a: z.string(), b: z.number().optional() });
|
|
83
|
-
expect(z.toJSONSchema(p)).toEqual({
|
|
84
|
-
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
85
|
-
type: "object",
|
|
86
|
-
properties: { a: { type: "string" }, b: { type: "number" } },
|
|
87
|
-
required: ["a"],
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
|
|
91
|
-
test("z.properties compiles", () => {
|
|
92
|
-
const p = z.properties({ a: z.literal("x"), b: z.literal("y") });
|
|
93
|
-
const compiled = z.compile(p);
|
|
94
|
-
const ok = { a: "x", b: "y", extra: 1 };
|
|
95
|
-
expect(compiled.parse(ok)).toBe(ok);
|
|
96
|
-
expect(compiled.safeParse({ a: "!", b: "!" }).error!.issues.map((i) => i.path)).toEqual([["a"], ["b"]]);
|
|
97
|
-
expect(compiled.safeParse(null).error!.issues.map((i) => [i.code, i.path])).toEqual([["invalid_type", []]]);
|
|
98
|
-
|
|
99
|
-
// nested in a container
|
|
64
|
+
test("z.properties compiles inside a container", () => {
|
|
100
65
|
const outer = z.compile(z.object({ url: z.instanceof(URL).properties({ protocol: z.literal("https:") }) }));
|
|
101
66
|
expect(outer.safeParse({ url: new URL("http://example.com") }).error!.issues.map((i) => i.path)).toEqual([
|
|
102
67
|
["url", "protocol"],
|
|
@@ -104,43 +69,28 @@ test("z.properties compiles", () => {
|
|
|
104
69
|
});
|
|
105
70
|
|
|
106
71
|
test("z.properties async", async () => {
|
|
107
|
-
const
|
|
72
|
+
const s = z.any().check(z.properties({ a: z.string().refine(async (s) => s.length > 1) }));
|
|
108
73
|
const ok = { a: "hi" };
|
|
109
|
-
await expect(
|
|
110
|
-
const bad = await
|
|
74
|
+
await expect(s.parseAsync(ok)).resolves.toBe(ok);
|
|
75
|
+
const bad = await s.safeParseAsync({ a: "!" });
|
|
111
76
|
expect(bad.error!.issues.map((i) => i.path)).toEqual([["a"]]);
|
|
112
77
|
});
|
|
113
78
|
|
|
114
|
-
test("z.properties infers the input side", () => {
|
|
115
|
-
// nothing is written back, so an output-typed inference would lie for every shape entry whose output differs from its input
|
|
116
|
-
const withDefault = z.properties({ a: z.string().default("x") });
|
|
117
|
-
expectTypeOf<z.infer<typeof withDefault>>().toEqualTypeOf<{ a?: string | undefined }>();
|
|
118
|
-
expect(withDefault.parse({})).toEqual({});
|
|
119
|
-
|
|
120
|
-
const withTransform = z.properties({ a: z.string().transform((s) => s.length) });
|
|
121
|
-
expectTypeOf<z.infer<typeof withTransform>>().toEqualTypeOf<{ a: string }>();
|
|
122
|
-
expect(withTransform.parse({ a: "hi" })).toEqual({ a: "hi" });
|
|
123
|
-
|
|
124
|
-
// a plain schema has the same input and output, so the documented narrowing is unchanged
|
|
125
|
-
const W = z.instanceof(URL).properties({ protocol: z.literal("https:") });
|
|
126
|
-
expectTypeOf<z.infer<typeof W>>().toEqualTypeOf<URL & { protocol: "https:" }>();
|
|
127
|
-
});
|
|
128
|
-
|
|
129
79
|
test("z.properties validates symbol keys", () => {
|
|
130
80
|
const sym = Symbol("tag");
|
|
131
|
-
const
|
|
132
|
-
expect(
|
|
133
|
-
expect(
|
|
81
|
+
const s = z.any().check(z.properties({ [sym]: z.string() }));
|
|
82
|
+
expect(s.safeParse({ [sym]: "ok" }).success).toBe(true);
|
|
83
|
+
expect(s.safeParse({ [sym]: 123 }).error!.issues.map((i) => i.path)).toEqual([[sym]]);
|
|
134
84
|
expect(
|
|
135
85
|
z
|
|
136
|
-
.compile(
|
|
86
|
+
.compile(s)
|
|
137
87
|
.safeParse({ [sym]: 123 })
|
|
138
88
|
.error!.issues.map((i) => i.path)
|
|
139
89
|
).toEqual([[sym]]);
|
|
140
90
|
});
|
|
141
91
|
|
|
142
92
|
test("z.properties compiled and interpreted agree on a nullish value", () => {
|
|
143
|
-
// a base that permits null reaches the check
|
|
93
|
+
// a base that permits null reaches the check with one; the compiled property read must not throw where the runtime reports an issue
|
|
144
94
|
const s = z.any().check(...z.properties({ a: z.string() }));
|
|
145
95
|
const expected = [["invalid_type", []]];
|
|
146
96
|
expect(s.safeParse(null).error!.issues.map((i) => [i.code, i.path])).toEqual(expected);
|
|
@@ -153,73 +103,20 @@ test("z.properties compiled and interpreted agree on a nullish value", () => {
|
|
|
153
103
|
});
|
|
154
104
|
|
|
155
105
|
test("z.properties with a custom when refuses to compile", () => {
|
|
156
|
-
// `when` is not publicly settable, since it gates
|
|
157
|
-
const p = new z.core.$
|
|
158
|
-
|
|
159
|
-
check: "properties",
|
|
160
|
-
shape: { a: z.literal("x") },
|
|
161
|
-
when: () => false,
|
|
162
|
-
});
|
|
163
|
-
const s = z.union([p as unknown as z.ZodType, z.object({ b: z.string() })]);
|
|
106
|
+
// `when` is not publicly settable, since it gates the assertion inside the run loop. A hand-built def can still carry one, and compiling it must refuse rather than run the shape unconditionally: a union branch compiles to its own IIFE, so a wrong rejection is absorbed as a branch failure with no fallback.
|
|
107
|
+
const p = new z.core.$ZodCheckProperties({ check: "properties", shape: { a: z.literal("x") }, when: () => false });
|
|
108
|
+
const s = z.union([z.any().check(p), z.object({ b: z.string() })]);
|
|
164
109
|
const input = { a: "wrong", b: "hello" };
|
|
165
110
|
expect(z.compile(s).safeParse(input)).toEqual(s.safeParse(input));
|
|
166
111
|
});
|
|
167
112
|
|
|
168
|
-
test("z.properties parses cyclic input", () => {
|
|
169
|
-
// the input is its own output, so it registers as its own memo entry; without that a cycle re-enters forever
|
|
170
|
-
const Node: z.ZodType<{ id: string; next?: unknown }> = z.lazy(() =>
|
|
171
|
-
z.properties({ id: z.string(), next: Node.optional() })
|
|
172
|
-
);
|
|
173
|
-
const cyclic: Record<string, unknown> = { id: "a" };
|
|
174
|
-
cyclic.next = cyclic;
|
|
175
|
-
expect(Node.parse(cyclic)).toBe(cyclic);
|
|
176
|
-
expect(z.compile(Node).parse(cyclic)).toBe(cyclic);
|
|
177
|
-
|
|
178
|
-
// the cycle guard must not swallow a real failure further down
|
|
179
|
-
expect(Node.safeParse({ id: "a", next: { id: 1 } }).error!.issues.map((i) => i.path)).toEqual([["next", "id"]]);
|
|
180
|
-
});
|
|
181
|
-
|
|
182
|
-
test("z.properties asserts forward while encoding", () => {
|
|
183
|
-
// both sides declare the shape's input type, so encoding must still check the value against it; running the children backward would encode them and reject that type
|
|
184
|
-
const codec = z.codec(z.string(), z.number(), { decode: (s) => Number(s), encode: (n) => String(n) });
|
|
185
|
-
const p = z.properties({ a: codec, b: z.string().transform((s) => s.length) });
|
|
186
|
-
const value = { a: "1", b: "hi" };
|
|
187
|
-
expect(z.decode(p, value)).toBe(value);
|
|
188
|
-
expect(z.encode(p, value)).toBe(value);
|
|
189
|
-
expect(z.safeEncode(p, { a: 1, b: "hi" } as never).error!.issues.map((i) => i.path)).toEqual([["a"]]);
|
|
190
|
-
});
|
|
191
|
-
|
|
192
|
-
test("z.properties parses a cyclic callable", () => {
|
|
193
|
-
// property reads work on a function too, so the cycle guard has to recognize one as a reference
|
|
194
|
-
const Fn: z.ZodType<{ self?: unknown }> = z.lazy(() => z.properties({ self: Fn.optional() }));
|
|
195
|
-
const fn = (() => {}) as unknown as Record<string, unknown>;
|
|
196
|
-
fn.self = fn;
|
|
197
|
-
expect(Fn.parse(fn)).toBe(fn);
|
|
198
|
-
});
|
|
199
|
-
|
|
200
113
|
test("z.properties survives shape mutation", () => {
|
|
201
114
|
// key and schema are snapshotted together; caching one and reading the other live paired a stale key with a missing schema
|
|
202
115
|
const shape: Record<string, z.ZodType> = { a: z.string() };
|
|
203
|
-
const
|
|
204
|
-
expect(
|
|
116
|
+
const s = z.any().check(z.properties(shape));
|
|
117
|
+
expect(s.safeParse({ a: "x" }).success).toBe(true);
|
|
205
118
|
delete shape.a;
|
|
206
|
-
expect(
|
|
119
|
+
expect(s.safeParse({ a: "x" }).success).toBe(true);
|
|
207
120
|
shape.b = z.string();
|
|
208
|
-
expect(
|
|
209
|
-
});
|
|
210
|
-
|
|
211
|
-
test("z.properties JSON Schema describes the input side", () => {
|
|
212
|
-
// the parsed value is the input, so a defaulted key that stays absent must not be required of the output either
|
|
213
|
-
const p = z.properties({ a: z.string().default("x") });
|
|
214
|
-
expect(p.parse({})).toEqual({});
|
|
215
|
-
expect(z.toJSONSchema(p, { io: "output" })).toEqual({
|
|
216
|
-
$schema: "https://json-schema.org/draft/2020-12/schema",
|
|
217
|
-
type: "object",
|
|
218
|
-
properties: { a: { default: "x", type: "string" } },
|
|
219
|
-
});
|
|
220
|
-
|
|
221
|
-
// an output mode emission for a transforming child would describe a value this schema never returns
|
|
222
|
-
const t = z.properties({ a: z.string().transform((s) => s.length) });
|
|
223
|
-
expect(() => z.toJSONSchema(t, { io: "output" })).toThrow(/cannot be represented/);
|
|
224
|
-
expect(z.toJSONSchema(t, { io: "input" })).toMatchObject({ properties: { a: { type: "string" } }, required: ["a"] });
|
|
121
|
+
expect(s.safeParse({ a: "x" }).success).toBe(true);
|
|
225
122
|
});
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { randomBytes } from "node:crypto";
|
|
2
|
-
import { expect, test } from "vitest";
|
|
2
|
+
import { expect, expectTypeOf, test } from "vitest";
|
|
3
3
|
|
|
4
4
|
import * as z from "zod/v4";
|
|
5
5
|
|
|
@@ -12,6 +12,63 @@ const includesFromIndex2 = z.string().includes("includes", { position: 2 });
|
|
|
12
12
|
const startsWith = z.string().startsWith("startsWith");
|
|
13
13
|
const endsWith = z.string().endsWith("endsWith");
|
|
14
14
|
|
|
15
|
+
test("string factory checks", async () => {
|
|
16
|
+
const schema = z.string({ checks: [z.minLength(1), z.maxLength(3)] as const });
|
|
17
|
+
const chained = z.string().min(1).max(3);
|
|
18
|
+
expectTypeOf<z.output<typeof schema>>().toEqualTypeOf<string>();
|
|
19
|
+
for (const value of ["", "ab", "abcd", 1, undefined]) {
|
|
20
|
+
expect(z.validate(schema, value)).toBe(z.validate(chained, value));
|
|
21
|
+
expect(schema.safeParse(value)).toEqual(chained.safeParse(value));
|
|
22
|
+
}
|
|
23
|
+
expect(z.toJSONSchema(schema)).toEqual(z.toJSONSchema(chained));
|
|
24
|
+
expect(schema.minLength).toBe(1);
|
|
25
|
+
expect(schema.maxLength).toBe(3);
|
|
26
|
+
expect(schema._zod.parent).toBeUndefined();
|
|
27
|
+
expect([...schema._zod.traits]).toEqual([...chained._zod.traits]);
|
|
28
|
+
const extended = schema.max(2);
|
|
29
|
+
expect(extended._zod.parent).toBe(schema);
|
|
30
|
+
expect(z.validate(schema, "abc")).toBe(true);
|
|
31
|
+
expect(z.validate(extended, "abc")).toBe(false);
|
|
32
|
+
const coerce = z.coerce.string({ checks: [z.minLength(2)] });
|
|
33
|
+
expect(coerce.parse(12)).toBe("12");
|
|
34
|
+
expect(z.validate(coerce, 1)).toBe(false);
|
|
35
|
+
const asyncSchema = z.string({
|
|
36
|
+
checks: [
|
|
37
|
+
z.check<string>(async (ctx) => {
|
|
38
|
+
if (ctx.value !== "ok") ctx.issues.push({ code: "custom", input: ctx.value });
|
|
39
|
+
}),
|
|
40
|
+
],
|
|
41
|
+
});
|
|
42
|
+
expect(await asyncSchema.safeParseAsync("ok")).toEqual({ success: true, data: "ok" });
|
|
43
|
+
expect((await asyncSchema.safeParseAsync("bad")).success).toBe(false);
|
|
44
|
+
// @ts-expect-error numeric checks cannot validate strings
|
|
45
|
+
z.string({ checks: [z.gte(1)] });
|
|
46
|
+
});
|
|
47
|
+
|
|
48
|
+
test("factory checks snapshot caller arrays", () => {
|
|
49
|
+
for (const factory of [z.string, z.coerce.string]) {
|
|
50
|
+
for (const initial of [[], [z.minLength(1)]]) {
|
|
51
|
+
const schema = factory({ checks: initial });
|
|
52
|
+
let reads = 0;
|
|
53
|
+
const getterSchema = factory({
|
|
54
|
+
get checks() {
|
|
55
|
+
if (++reads > 1) throw new Error("checks read twice");
|
|
56
|
+
return initial;
|
|
57
|
+
},
|
|
58
|
+
});
|
|
59
|
+
expect(reads).toBe(1);
|
|
60
|
+
expect(z.validate(getterSchema, "long")).toBe(true);
|
|
61
|
+
const before = z.toJSONSchema(schema);
|
|
62
|
+
initial.push(z.minLength(10));
|
|
63
|
+
expect(schema._zod.def.checks).not.toBe(initial);
|
|
64
|
+
expect(z.validate(schema, "long")).toBe(true);
|
|
65
|
+
expect(z.validate(z.compile(schema), "long")).toBe(true);
|
|
66
|
+
expect(z.toJSONSchema(schema)).toEqual(before);
|
|
67
|
+
expect(z.validate(schema.check(z.minLength(2)), "long")).toBe(true);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
});
|
|
71
|
+
|
|
15
72
|
test("length checks", () => {
|
|
16
73
|
minFive.parse("12345");
|
|
17
74
|
minFive.parse("123456");
|
|
@@ -1289,6 +1346,18 @@ test("hostname", () => {
|
|
|
1289
1346
|
expect(() => hostname.parse("example..com")).toThrow();
|
|
1290
1347
|
});
|
|
1291
1348
|
|
|
1349
|
+
test("currency code", () => {
|
|
1350
|
+
const currency = z.currencyCode();
|
|
1351
|
+
for (const code of ["USD", "EUR", "XXX", "XCG", "XAD"]) expect(currency.parse(code)).toBe(code);
|
|
1352
|
+
for (const input of ["", "usd", "US", "USDX", " USD", "ANG", "BGN"])
|
|
1353
|
+
expect(currency.safeParse(input).success).toBe(false);
|
|
1354
|
+
expect(currency.safeParse("usd").error!.issues[0]).toMatchObject({
|
|
1355
|
+
code: "invalid_format",
|
|
1356
|
+
format: "currency_code",
|
|
1357
|
+
message: "Invalid currency code",
|
|
1358
|
+
});
|
|
1359
|
+
});
|
|
1360
|
+
|
|
1292
1361
|
test("hash validation", () => {
|
|
1293
1362
|
// MD5 tests
|
|
1294
1363
|
const md5hex = z.hash("md5");
|