avancement planning

This commit is contained in:
2026-05-26 11:58:39 +02:00
parent 619a2b240a
commit 150b97cd2e
4892 changed files with 99214 additions and 429382 deletions
+2 -2
View File
@@ -1,4 +1,4 @@
import * as z from "./v3/external.js";
export * from "./v3/external.js";
import * as z from "./v4/classic/external.js";
export * from "./v4/classic/external.js";
export { z };
export default z;
+1 -1
View File
@@ -296,7 +296,7 @@ export class ZodError<T = any> extends Error {
flatten(): typeToFlattenedError<T>;
flatten<U>(mapper?: (issue: ZodIssue) => U): typeToFlattenedError<T, U>;
flatten<U = string>(mapper: (issue: ZodIssue) => U = (issue: ZodIssue) => issue.message as any): any {
const fieldErrors: any = {};
const fieldErrors: any = Object.create(null);
const formErrors: U[] = [];
for (const sub of this.issues) {
if (sub.path.length > 0) {
+2 -2
View File
@@ -152,13 +152,13 @@ test("catchall overrides strict", () => {
});
});
test("test that optional keys are unset", async () => {
test("test that optional keys are unset", () => {
const SNamedEntity = z.object({
id: z.string(),
set: z.string().optional(),
unset: z.string().optional(),
});
const result = await SNamedEntity.parse({
const result = SNamedEntity.parse({
id: "asdf",
set: undefined,
});
+2 -2
View File
@@ -342,8 +342,8 @@ test("uuid", () => {
uuid.parse("9491d710-3185-4e06-bea0-6a2f275345e0");
uuid.parse("d89e7b01-7598-ed11-9d7a-0022489382fd"); // new sequential id
uuid.parse("00000000-0000-0000-0000-000000000000");
uuid.parse("b3ce60f8-e8b9-40f5-1150-172ede56ff74"); // Variant 0 - RFC 4122: Reserved, NCS backward compatibility
uuid.parse("92e76bf9-28b3-4730-cd7f-cb6bc51f8c09"); // Variant 2 - RFC 4122: Reserved, Microsoft Corporation backward compatibility
uuid.parse("b3ce60f8-e8b9-40f5-1150-172ede56ff74"); // Variant 0 - RFC 9562/4122: Reserved, NCS backward compatibility
uuid.parse("92e76bf9-28b3-4730-cd7f-cb6bc51f8c09"); // Variant 2 - RFC 9562/4122: Reserved, Microsoft Corporation backward compatibility
const result = uuid.safeParse("9491d710-3185-4e06-bea0-6a2f275345e0X");
expect(result.success).toEqual(false);
if (!result.success) {
+3 -1
View File
@@ -705,6 +705,7 @@ function isValidJWT(jwt: string, alg?: string): boolean {
.replace(/-/g, "+")
.replace(/_/g, "/")
.padEnd(header.length + ((4 - (header.length % 4)) % 4), "=");
// @ts-ignore
const decoded = JSON.parse(atob(base64));
if (typeof decoded !== "object" || decoded === null) return false;
if ("typ" in decoded && decoded?.typ !== "JWT") return false;
@@ -875,6 +876,7 @@ export class ZodString extends ZodType<string, ZodStringDef, string> {
}
} else if (check.kind === "url") {
try {
// @ts-ignore
new URL(input.data);
} catch {
ctx = this._getOrReturnCtx(input, ctx);
@@ -2454,7 +2456,7 @@ export class ZodObject<
Output = objectOutputType<T, Catchall, UnknownKeys>,
Input = objectInputType<T, Catchall, UnknownKeys>,
> extends ZodType<Output, ZodObjectDef<T, UnknownKeys, Catchall>, Input> {
private _cached: { shape: T; keys: string[] } | null = null;
_cached: { shape: T; keys: string[] } | null = null;
_getCached(): { shape: T; keys: string[] } {
if (this._cached !== null) return this._cached;
+2
View File
@@ -27,4 +27,6 @@ export {
_trim as trim,
_toLowerCase as toLowerCase,
_toUpperCase as toUpperCase,
_slugify as slugify,
type $RefinementCtx as RefinementCtx,
} from "../core/index.js";
+4
View File
@@ -63,4 +63,8 @@ export type {
ZodType as Schema,
};
/** Included for Zod 3 compatibility */
export type ZodRawShape = core.$ZodShape;
/** @deprecated Do not use. Stub definition, only included for zod-to-json-schema compatibility. */
export enum ZodFirstPartyTypeKind {}
+9 -2
View File
@@ -1,5 +1,6 @@
import * as core from "../core/index.js";
import { $ZodError } from "../core/index.js";
import * as util from "../core/util.js";
/** @deprecated Use `z.core.$ZodIssue` from `@zod/core` instead, especially if you are building a library on top of Zod. */
export type ZodIssue = core.$ZodIssue;
@@ -34,11 +35,17 @@ const initializer = (inst: ZodError, issues: core.$ZodIssue[]) => {
// enumerable: false,
},
addIssue: {
value: (issue: any) => inst.issues.push(issue),
value: (issue: any) => {
inst.issues.push(issue);
inst.message = JSON.stringify(inst.issues, util.jsonStringifyReplacer, 2);
},
// enumerable: false,
},
addIssues: {
value: (issues: any) => inst.issues.push(...issues),
value: (issues: any) => {
inst.issues.push(...issues);
inst.message = JSON.stringify(inst.issues, util.jsonStringifyReplacer, 2);
},
// enumerable: false,
},
isEmpty: {
+1 -1
View File
@@ -16,7 +16,6 @@ export {
type GlobalMeta,
registry,
config,
function,
$output,
$input,
$brand,
@@ -28,6 +27,7 @@ export {
flattenError,
toJSONSchema,
TimePrecision,
util,
NEVER,
} from "../core/index.js";
+49
View File
@@ -31,3 +31,52 @@ export const safeParseAsync: <T extends core.$ZodType>(
value: unknown,
_ctx?: core.ParseContext<core.$ZodIssue>
) => Promise<ZodSafeParseResult<core.output<T>>> = /* @__PURE__ */ core._safeParseAsync(ZodRealError) as any;
// Codec functions
export const encode: <T extends core.$ZodType>(
schema: T,
value: core.output<T>,
_ctx?: core.ParseContext<core.$ZodIssue>
) => core.input<T> = /* @__PURE__ */ core._encode(ZodRealError) as any;
export const decode: <T extends core.$ZodType>(
schema: T,
value: core.input<T>,
_ctx?: core.ParseContext<core.$ZodIssue>
) => core.output<T> = /* @__PURE__ */ core._decode(ZodRealError) as any;
export const encodeAsync: <T extends core.$ZodType>(
schema: T,
value: core.output<T>,
_ctx?: core.ParseContext<core.$ZodIssue>
) => Promise<core.input<T>> = /* @__PURE__ */ core._encodeAsync(ZodRealError) as any;
export const decodeAsync: <T extends core.$ZodType>(
schema: T,
value: core.input<T>,
_ctx?: core.ParseContext<core.$ZodIssue>
) => Promise<core.output<T>> = /* @__PURE__ */ core._decodeAsync(ZodRealError) as any;
export const safeEncode: <T extends core.$ZodType>(
schema: T,
value: core.output<T>,
_ctx?: core.ParseContext<core.$ZodIssue>
) => ZodSafeParseResult<core.input<T>> = /* @__PURE__ */ core._safeEncode(ZodRealError) as any;
export const safeDecode: <T extends core.$ZodType>(
schema: T,
value: core.input<T>,
_ctx?: core.ParseContext<core.$ZodIssue>
) => ZodSafeParseResult<core.output<T>> = /* @__PURE__ */ core._safeDecode(ZodRealError) as any;
export const safeEncodeAsync: <T extends core.$ZodType>(
schema: T,
value: core.output<T>,
_ctx?: core.ParseContext<core.$ZodIssue>
) => Promise<ZodSafeParseResult<core.input<T>>> = /* @__PURE__ */ core._safeEncodeAsync(ZodRealError) as any;
export const safeDecodeAsync: <T extends core.$ZodType>(
schema: T,
value: core.input<T>,
_ctx?: core.ParseContext<core.$ZodIssue>
) => Promise<ZodSafeParseResult<core.output<T>>> = /* @__PURE__ */ core._safeDecodeAsync(ZodRealError) as any;
+244 -81
View File
@@ -13,10 +13,6 @@ import * as parse from "./parse.js";
///////////////////////////////////////////
///////////////////////////////////////////
export interface RefinementCtx<T = unknown> extends core.ParsePayload<T> {
addIssue(arg: string | core.$ZodRawIssue | Partial<core.$ZodIssueCustom>): void;
}
export interface ZodType<
out Output = unknown,
out Input = unknown,
@@ -59,11 +55,32 @@ export interface ZodType<
params?: core.ParseContext<core.$ZodIssue>
) => Promise<parse.ZodSafeParseResult<core.output<this>>>;
// encoding/decoding
encode(data: core.output<this>, params?: core.ParseContext<core.$ZodIssue>): core.input<this>;
decode(data: core.input<this>, params?: core.ParseContext<core.$ZodIssue>): core.output<this>;
encodeAsync(data: core.output<this>, params?: core.ParseContext<core.$ZodIssue>): Promise<core.input<this>>;
decodeAsync(data: core.input<this>, params?: core.ParseContext<core.$ZodIssue>): Promise<core.output<this>>;
safeEncode(
data: core.output<this>,
params?: core.ParseContext<core.$ZodIssue>
): parse.ZodSafeParseResult<core.input<this>>;
safeDecode(
data: core.input<this>,
params?: core.ParseContext<core.$ZodIssue>
): parse.ZodSafeParseResult<core.output<this>>;
safeEncodeAsync(
data: core.output<this>,
params?: core.ParseContext<core.$ZodIssue>
): Promise<parse.ZodSafeParseResult<core.input<this>>>;
safeDecodeAsync(
data: core.input<this>,
params?: core.ParseContext<core.$ZodIssue>
): Promise<parse.ZodSafeParseResult<core.output<this>>>;
// refinements
refine(check: (arg: core.output<this>) => unknown | Promise<unknown>, params?: string | core.$ZodCustomParams): this;
/** @deprecated Use `.check()` instead. */
superRefine(
refinement: (arg: core.output<this>, ctx: RefinementCtx<core.output<this>>) => void | Promise<void>
refinement: (arg: core.output<this>, ctx: core.$RefinementCtx<core.output<this>>) => void | Promise<void>
): this;
overwrite(fn: (x: core.output<this>) => core.output<this>): this;
@@ -72,7 +89,7 @@ export interface ZodType<
nonoptional(params?: string | core.$ZodNonOptionalParams): ZodNonOptional<this>;
nullable(): ZodNullable<this>;
nullish(): ZodOptional<ZodNullable<this>>;
default(def: core.output<this>): ZodDefault<this>;
default(def: util.NoUndefined<core.output<this>>): ZodDefault<this>;
default(def: () => util.NoUndefined<core.output<this>>): ZodDefault<this>;
prefault(def: () => core.input<this>): ZodPrefault<this>;
prefault(def: core.input<this>): ZodPrefault<this>;
@@ -80,7 +97,7 @@ export interface ZodType<
or<T extends core.SomeType>(option: T): ZodUnion<[this, T]>;
and<T extends core.SomeType>(incoming: T): ZodIntersection<this, T>;
transform<NewOut>(
transform: (arg: core.output<this>, ctx: RefinementCtx<core.output<this>>) => NewOut | Promise<NewOut>
transform: (arg: core.output<this>, ctx: core.$RefinementCtx<core.output<this>>) => NewOut | Promise<NewOut>
): ZodPipe<this, ZodTransform<Awaited<NewOut>, core.output<this>>>;
catch(def: core.output<this>): ZodCatch<this>;
catch(def: (ctx: core.$ZodCatchCtx) => core.output<this>): ZodCatch<this>;
@@ -123,21 +140,20 @@ export interface _ZodType<out Internals extends core.$ZodTypeInternals = core.$Z
export const ZodType: core.$constructor<ZodType> = /*@__PURE__*/ core.$constructor("ZodType", (inst, def) => {
core.$ZodType.init(inst, def);
inst.def = def;
inst.type = def.type;
Object.defineProperty(inst, "_def", { value: def });
// base methods
inst.check = (...checks) => {
return inst.clone(
{
...def,
util.mergeDefs(def, {
checks: [
...(def.checks ?? []),
...checks.map((ch) =>
typeof ch === "function" ? { _zod: { check: ch, def: { check: "custom" }, onattach: [] } } : ch
),
],
}
// { parent: true }
})
);
};
inst.clone = (def, params) => core.clone(inst, def, params);
@@ -154,6 +170,16 @@ export const ZodType: core.$constructor<ZodType> = /*@__PURE__*/ core.$construct
inst.safeParseAsync = async (data, params) => parse.safeParseAsync(inst, data, params);
inst.spa = inst.safeParseAsync;
// encoding/decoding
inst.encode = (data, params) => parse.encode(inst, data, params);
inst.decode = (data, params) => parse.decode(inst, data, params);
inst.encodeAsync = async (data, params) => parse.encodeAsync(inst, data, params);
inst.decodeAsync = async (data, params) => parse.decodeAsync(inst, data, params);
inst.safeEncode = (data, params) => parse.safeEncode(inst, data, params);
inst.safeDecode = (data, params) => parse.safeDecode(inst, data, params);
inst.safeEncodeAsync = async (data, params) => parse.safeEncodeAsync(inst, data, params);
inst.safeDecodeAsync = async (data, params) => parse.safeDecodeAsync(inst, data, params);
// refinements
inst.refine = (check, params) => inst.check(refine(check, params));
inst.superRefine = (refinement) => inst.check(superRefine(refinement));
@@ -226,6 +252,7 @@ export interface _ZodString<T extends core.$ZodStringInternals<unknown> = core.$
normalize(form?: "NFC" | "NFD" | "NFKC" | "NFKD" | (string & {})): this;
toLowerCase(): this;
toUpperCase(): this;
slugify(): this;
}
/** @internal */
@@ -255,6 +282,7 @@ export const _ZodString: core.$constructor<_ZodString> = /*@__PURE__*/ core.$con
inst.normalize = (...args) => inst.check(checks.normalize(...args));
inst.toLowerCase = () => inst.check(checks.toLowerCase());
inst.toUpperCase = () => inst.check(checks.toUpperCase());
inst.slugify = () => inst.check(checks.slugify());
});
export interface ZodString extends _ZodString<core.$ZodStringInternals<string>> {
@@ -454,6 +482,14 @@ export function url(params?: string | core.$ZodURLParams): ZodURL {
return core._url(ZodURL, params);
}
export function httpUrl(params?: string | Omit<core.$ZodURLParams, "protocol" | "hostname">): ZodURL {
return core._url(ZodURL, {
protocol: /^https?$/,
hostname: core.regexes.domain,
...util.normalizeParams(params),
});
}
// ZodEmoji
export interface ZodEmoji extends ZodStringFormat<"emoji"> {
_zod: core.$ZodEmojiInternals;
@@ -580,6 +616,19 @@ export function ipv4(params?: string | core.$ZodIPv4Params): ZodIPv4 {
return core._ipv4(ZodIPv4, params);
}
// ZodMAC
export interface ZodMAC extends ZodStringFormat<"mac"> {
_zod: core.$ZodMACInternals;
}
export const ZodMAC: core.$constructor<ZodMAC> = /*@__PURE__*/ core.$constructor("ZodMAC", (inst, def) => {
// ZodStringFormat.init(inst, def);
core.$ZodMAC.init(inst, def);
ZodStringFormat.init(inst, def);
});
export function mac(params?: string | core.$ZodMACParams): ZodMAC {
return core._mac(ZodMAC, params);
}
// ZodIPv6
export interface ZodIPv6 extends ZodStringFormat<"ipv6"> {
_zod: core.$ZodIPv6Internals;
@@ -698,6 +747,27 @@ export function stringFormat<Format extends string>(
return core._stringFormat(ZodCustomStringFormat, format, fnOrRegex, _params) as any;
}
export function hostname(_params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat<"hostname"> {
return core._stringFormat(ZodCustomStringFormat, "hostname", core.regexes.hostname, _params) as any;
}
export function hex(_params?: string | core.$ZodStringFormatParams): ZodCustomStringFormat<"hex"> {
return core._stringFormat(ZodCustomStringFormat, "hex", core.regexes.hex, _params) as any;
}
export function hash<Alg extends util.HashAlgorithm, Enc extends util.HashEncoding = "hex">(
alg: Alg,
params?: {
enc?: Enc;
} & core.$ZodStringFormatParams
): ZodCustomStringFormat<`${Alg}_${Enc}`> {
const enc = params?.enc ?? "hex";
const format = `${alg}_${enc}` as const;
const regex = core.regexes[format as keyof typeof core.regexes] as RegExp;
if (!regex) throw new Error(`Unrecognized hash format: ${format}`);
return core._stringFormat(ZodCustomStringFormat, format, regex, params) as any;
}
// ZodNumber
export interface _ZodNumber<Internals extends core.$ZodNumberInternals = core.$ZodNumberInternals>
extends _ZodType<Internals> {
@@ -737,6 +807,7 @@ export interface ZodNumber extends _ZodNumber<core.$ZodNumberInternals<number>>
export const ZodNumber: core.$constructor<ZodNumber> = /*@__PURE__*/ core.$constructor("ZodNumber", (inst, def) => {
core.$ZodNumber.init(inst, def);
ZodType.init(inst, def);
inst.gt = (value, params) => inst.check(checks.gt(value, params));
@@ -1039,13 +1110,23 @@ export function array<T extends core.SomeType>(element: T, params?: string | cor
}
// .keyof
export function keyof<T extends ZodObject>(schema: T): ZodLiteral<Exclude<keyof T["_zod"]["output"], symbol>> {
export function keyof<T extends ZodObject>(schema: T): ZodEnum<util.KeysEnum<T["_zod"]["output"]>> {
const shape = schema._zod.def.shape;
return literal(Object.keys(shape)) as any;
return _enum(Object.keys(shape)) as any;
}
// ZodObject
export type SafeExtendShape<Base extends core.$ZodShape, Ext extends core.$ZodLooseShape> = {
[K in keyof Ext]: K extends keyof Base
? core.output<Ext[K]> extends core.output<Base[K]>
? core.input<Ext[K]> extends core.input<Base[K]>
? Ext[K]
: never
: never
: Ext[K];
};
export interface ZodObject<
/** @ts-ignore Cast variance */
out Shape extends core.$ZodShape = core.$ZodLooseShape,
@@ -1069,22 +1150,14 @@ export interface ZodObject<
/** This is the default behavior. This method call is likely unnecessary. */
strip(): ZodObject<Shape, core.$strip>;
extend<U extends core.$ZodLooseShape & Partial<Record<keyof Shape, core.SomeType>>>(
shape: U
extend<U extends core.$ZodLooseShape>(shape: U): ZodObject<util.Extend<Shape, U>, Config>;
safeExtend<U extends core.$ZodLooseShape>(
shape: SafeExtendShape<Shape, U> & Partial<Record<keyof Shape, core.SomeType>>
): ZodObject<util.Extend<Shape, U>, Config>;
/**
* @deprecated Use spread syntax and the `.shape` property to combine two object schemas:
*
* ```ts
* const A = z.object({ a: z.string() });
* const B = z.object({ b: z.number() });
*
* const C = z.object({
* ...A.shape,
* ...B.shape
* });
* ```
* @deprecated Use [`A.extend(B.shape)`](https://zod.dev/api?id=extend) instead.
*/
merge<U extends ZodObject>(other: U): ZodObject<util.Extend<Shape, U["shape"]>, U["_zod"]["config"]>;
@@ -1134,14 +1207,16 @@ export interface ZodObject<
}
export const ZodObject: core.$constructor<ZodObject> = /*@__PURE__*/ core.$constructor("ZodObject", (inst, def) => {
core.$ZodObject.init(inst, def);
core.$ZodObjectJIT.init(inst, def);
ZodType.init(inst, def);
util.defineLazy(inst, "shape", () => def.shape);
util.defineLazy(inst, "shape", () => {
return def.shape;
});
inst.keyof = () => _enum(Object.keys(inst._zod.def.shape)) as any;
inst.catchall = (catchall) => inst.clone({ ...inst._zod.def, catchall: catchall as any as core.$ZodType }) as any;
inst.passthrough = () => inst.clone({ ...inst._zod.def, catchall: unknown() });
// inst.nonstrict = () => inst.clone({ ...inst._zod.def, catchall: api.unknown() });
inst.loose = () => inst.clone({ ...inst._zod.def, catchall: unknown() });
inst.strict = () => inst.clone({ ...inst._zod.def, catchall: never() });
inst.strip = () => inst.clone({ ...inst._zod.def, catchall: undefined });
@@ -1149,6 +1224,9 @@ export const ZodObject: core.$constructor<ZodObject> = /*@__PURE__*/ core.$const
inst.extend = (incoming: any) => {
return util.extend(inst, incoming);
};
inst.safeExtend = (incoming: any) => {
return util.safeExtend(inst, incoming);
};
inst.merge = (other) => util.merge(inst, other);
inst.pick = (mask) => util.pick(inst, mask);
inst.omit = (mask) => util.omit(inst, mask);
@@ -1162,10 +1240,7 @@ export function object<T extends core.$ZodLooseShape = Partial<Record<never, cor
): ZodObject<util.Writeable<T>, core.$strip> {
const def: core.$ZodObjectDef = {
type: "object",
get shape() {
util.assignProp(this, "shape", { ...shape });
return this.shape;
},
shape: shape ?? {},
...util.normalizeParams(params),
};
return new ZodObject(def) as any;
@@ -1179,10 +1254,7 @@ export function strictObject<T extends core.$ZodLooseShape>(
): ZodObject<T, core.$strict> {
return new ZodObject({
type: "object",
get shape() {
util.assignProp(this, "shape", { ...shape });
return this.shape;
},
shape,
catchall: never(),
...util.normalizeParams(params),
}) as any;
@@ -1196,10 +1268,7 @@ export function looseObject<T extends core.$ZodLooseShape>(
): ZodObject<T, core.$loose> {
return new ZodObject({
type: "object",
get shape() {
util.assignProp(this, "shape", { ...shape });
return this.shape;
},
shape,
catchall: unknown(),
...util.normalizeParams(params),
}) as any;
@@ -1229,10 +1298,13 @@ export function union<const T extends readonly core.SomeType[]>(
}
// ZodDiscriminatedUnion
export interface ZodDiscriminatedUnion<Options extends readonly core.SomeType[] = readonly core.$ZodType[]>
extends ZodUnion<Options>,
core.$ZodDiscriminatedUnion<Options> {
_zod: core.$ZodDiscriminatedUnionInternals<Options>;
export interface ZodDiscriminatedUnion<
Options extends readonly core.SomeType[] = readonly core.$ZodType[],
Disc extends string = string,
> extends ZodUnion<Options>,
core.$ZodDiscriminatedUnion<Options, Disc> {
_zod: core.$ZodDiscriminatedUnionInternals<Options, Disc>;
def: core.$ZodDiscriminatedUnionDef<Options, Disc>;
}
export const ZodDiscriminatedUnion: core.$constructor<ZodDiscriminatedUnion> = /*@__PURE__*/ core.$constructor(
"ZodDiscriminatedUnion",
@@ -1244,11 +1316,12 @@ export const ZodDiscriminatedUnion: core.$constructor<ZodDiscriminatedUnion> = /
export function discriminatedUnion<
Types extends readonly [core.$ZodTypeDiscriminable, ...core.$ZodTypeDiscriminable[]],
Disc extends string,
>(
discriminator: string,
discriminator: Disc,
options: Types,
params?: string | core.$ZodDiscriminatedUnionParams
): ZodDiscriminatedUnion<Types> {
): ZodDiscriminatedUnion<Types, Disc> {
// const [options, params] = args;
return new ZodDiscriminatedUnion({
type: "union",
@@ -1360,9 +1433,11 @@ export function partialRecord<Key extends core.$ZodRecordKey, Value extends core
valueType: Value,
params?: string | core.$ZodRecordParams
): ZodRecord<Key & core.$partial, Value> {
const k = core.clone(keyType);
k._zod.values = undefined;
return new ZodRecord({
type: "record",
keyType: union([keyType, never()]),
keyType: k,
valueType: valueType as any,
...util.normalizeParams(params),
}) as any;
@@ -1400,7 +1475,6 @@ export interface ZodSet<T extends core.SomeType = core.$ZodType>
extends _ZodType<core.$ZodSetInternals<T>>,
core.$ZodSet<T> {
min(minSize: number, params?: string | core.$ZodCheckMinSizeParams): this;
/** */
nonempty(params?: string | core.$ZodCheckMinSizeParams): this;
max(maxSize: number, params?: string | core.$ZodCheckMaxSizeParams): this;
size(size: number, params?: string | core.$ZodCheckSizeEqualsParams): this;
@@ -1583,7 +1657,11 @@ export const ZodTransform: core.$constructor<ZodTransform> = /*@__PURE__*/ core.
ZodType.init(inst, def);
inst._zod.parse = (payload, _ctx) => {
(payload as RefinementCtx).addIssue = (issue) => {
if (_ctx.direction === "backward") {
throw new core.$ZodEncodeError(inst.constructor.name);
}
(payload as core.$RefinementCtx).addIssue = (issue) => {
if (typeof issue === "string") {
payload.issues.push(util.issue(issue, payload.value, def));
} else {
@@ -1594,7 +1672,7 @@ export const ZodTransform: core.$constructor<ZodTransform> = /*@__PURE__*/ core.
_issue.code ??= "custom";
_issue.input ??= payload.value;
_issue.inst ??= inst;
_issue.continue ??= true;
// _issue.continue ??= true;
payload.issues.push(util.issue(_issue));
}
};
@@ -1696,7 +1774,7 @@ export function _default<T extends core.SomeType>(
type: "default",
innerType: innerType as any as core.$ZodType,
get defaultValue() {
return typeof defaultValue === "function" ? (defaultValue as Function)() : defaultValue;
return typeof defaultValue === "function" ? (defaultValue as Function)() : util.shallowClone(defaultValue);
},
}) as any;
}
@@ -1724,7 +1802,7 @@ export function prefault<T extends core.SomeType>(
type: "prefault",
innerType: innerType as any as core.$ZodType,
get defaultValue() {
return typeof defaultValue === "function" ? (defaultValue as Function)() : defaultValue;
return typeof defaultValue === "function" ? (defaultValue as Function)() : util.shallowClone(defaultValue);
},
}) as any;
}
@@ -1845,15 +1923,48 @@ export function pipe(in_: core.SomeType, out: core.SomeType) {
});
}
// ZodCodec
export interface ZodCodec<A extends core.SomeType = core.$ZodType, B extends core.SomeType = core.$ZodType>
extends ZodPipe<A, B>,
core.$ZodCodec<A, B> {
_zod: core.$ZodCodecInternals<A, B>;
def: core.$ZodCodecDef<A, B>;
}
export const ZodCodec: core.$constructor<ZodCodec> = /*@__PURE__*/ core.$constructor("ZodCodec", (inst, def) => {
ZodPipe.init(inst, def);
core.$ZodCodec.init(inst, def);
});
export function codec<const A extends core.SomeType, B extends core.SomeType = core.$ZodType>(
in_: A,
out: B,
params: {
decode: (value: core.output<A>, payload: core.ParsePayload<core.output<A>>) => core.util.MaybeAsync<core.input<B>>;
encode: (value: core.input<B>, payload: core.ParsePayload<core.input<B>>) => core.util.MaybeAsync<core.output<A>>;
}
): ZodCodec<A, B> {
return new ZodCodec({
type: "pipe",
in: in_ as any as core.$ZodType,
out: out as any as core.$ZodType,
transform: params.decode as any,
reverseTransform: params.encode as any,
}) as any;
}
// ZodReadonly
export interface ZodReadonly<T extends core.SomeType = core.$ZodType>
extends _ZodType<core.$ZodReadonlyInternals<T>>,
core.$ZodReadonly<T> {}
core.$ZodReadonly<T> {
unwrap(): T;
}
export const ZodReadonly: core.$constructor<ZodReadonly> = /*@__PURE__*/ core.$constructor(
"ZodReadonly",
(inst, def) => {
core.$ZodReadonly.init(inst, def);
ZodType.init(inst, def);
inst.unwrap = () => inst._zod.def.innerType;
}
);
@@ -1927,6 +2038,71 @@ export function promise<T extends core.SomeType>(innerType: T): ZodPromise<T> {
}) as any;
}
// ZodFunction
export interface ZodFunction<
Args extends core.$ZodFunctionIn = core.$ZodFunctionIn,
Returns extends core.$ZodFunctionOut = core.$ZodFunctionOut,
> extends _ZodType<core.$ZodFunctionInternals<Args, Returns>>,
core.$ZodFunction<Args, Returns> {
_def: core.$ZodFunctionDef<Args, Returns>;
_input: core.$InferInnerFunctionType<Args, Returns>;
_output: core.$InferOuterFunctionType<Args, Returns>;
input<const Items extends util.TupleItems, const Rest extends core.$ZodFunctionOut = core.$ZodFunctionOut>(
args: Items,
rest?: Rest
): ZodFunction<core.$ZodTuple<Items, Rest>, Returns>;
input<NewArgs extends core.$ZodFunctionIn>(args: NewArgs): ZodFunction<NewArgs, Returns>;
input(...args: any[]): ZodFunction<any, Returns>;
output<NewReturns extends core.$ZodType>(output: NewReturns): ZodFunction<Args, NewReturns>;
}
export const ZodFunction: core.$constructor<ZodFunction> = /*@__PURE__*/ core.$constructor(
"ZodFunction",
(inst, def) => {
core.$ZodFunction.init(inst, def);
ZodType.init(inst, def);
}
);
export function _function(): ZodFunction;
export function _function<const In extends ReadonlyArray<core.$ZodType>>(params: {
input: In;
}): ZodFunction<ZodTuple<In, null>, core.$ZodFunctionOut>;
export function _function<
const In extends ReadonlyArray<core.$ZodType>,
const Out extends core.$ZodFunctionOut = core.$ZodFunctionOut,
>(params: {
input: In;
output: Out;
}): ZodFunction<ZodTuple<In, null>, Out>;
export function _function<const In extends core.$ZodFunctionIn = core.$ZodFunctionIn>(params: {
input: In;
}): ZodFunction<In, core.$ZodFunctionOut>;
export function _function<const Out extends core.$ZodFunctionOut = core.$ZodFunctionOut>(params: {
output: Out;
}): ZodFunction<core.$ZodFunctionIn, Out>;
export function _function<
In extends core.$ZodFunctionIn = core.$ZodFunctionIn,
Out extends core.$ZodType = core.$ZodType,
>(params?: {
input: In;
output: Out;
}): ZodFunction<In, Out>;
export function _function(params?: {
output?: core.$ZodType;
input?: core.$ZodFunctionArgs | Array<core.$ZodType>;
}): ZodFunction {
return new ZodFunction({
type: "function",
input: Array.isArray(params?.input) ? tuple(params?.input as any) : (params?.input ?? array(unknown())),
output: params?.output ?? unknown(),
});
}
export { _function as function };
// ZodCustom
export interface ZodCustom<O = unknown, I = unknown>
extends _ZodType<core.$ZodCustomInternals<O, I>>,
@@ -1962,28 +2138,16 @@ export function refine<T>(
}
// superRefine
export function superRefine<T>(fn: (arg: T, payload: RefinementCtx<T>) => void | Promise<void>): core.$ZodCheck<T> {
const ch = check<T>((payload) => {
(payload as RefinementCtx).addIssue = (issue) => {
if (typeof issue === "string") {
payload.issues.push(util.issue(issue, payload.value, ch._zod.def));
} else {
// for Zod 3 backwards compatibility
const _issue: any = issue;
if (_issue.fatal) _issue.continue = false;
_issue.code ??= "custom";
_issue.input ??= payload.value;
_issue.inst ??= ch;
_issue.continue ??= !ch._zod.def.abort;
payload.issues.push(util.issue(_issue));
}
};
return fn(payload.value, payload as RefinementCtx<T>);
});
return ch;
export function superRefine<T>(
fn: (arg: T, payload: core.$RefinementCtx<T>) => void | Promise<void>
): core.$ZodCheck<T> {
return core._superRefine(fn);
}
// Re-export describe and meta from core
export const describe = core.describe;
export const meta = core.meta;
type ZodInstanceOfParams = core.Params<
ZodCustom,
core.$ZodIssueCustom,
@@ -2008,15 +2172,14 @@ function _instanceof<T extends typeof util.Class>(
export { _instanceof as instanceof };
// stringbool
export const stringbool: (
_params?: string | core.$ZodStringBoolParams
) => ZodPipe<ZodPipe<ZodString, ZodTransform<boolean, string>>, ZodBoolean> = (...args) =>
export const stringbool: (_params?: string | core.$ZodStringBoolParams) => ZodCodec<ZodString, ZodBoolean> = (
...args
) =>
core._stringbool(
{
Pipe: ZodPipe,
Codec: ZodCodec,
Boolean: ZodBoolean,
String: ZodString,
Transform: ZodTransform,
},
...args
) as any;
@@ -2047,7 +2210,7 @@ export function json(params?: string | core.$ZodCustomParams): ZodJSONSchema {
// /** @deprecated Use `z.pipe()` and `z.transform()` instead. */
export function preprocess<A, U extends core.SomeType, B = unknown>(
fn: (arg: B, ctx: RefinementCtx) => A,
fn: (arg: B, ctx: core.$RefinementCtx) => A,
schema: U
): ZodPipe<ZodTransform<A, B>, U> {
return pipe(transform(fn as any), schema as any) as any;
+6 -6
View File
@@ -6,9 +6,9 @@ test("type inference", () => {
expectTypeOf<z.infer<typeof schema>>().toEqualTypeOf<string[]>();
});
test("array min/max", async () => {
test("array min/max", () => {
const schema = z.array(z.string()).min(2).max(2);
const r1 = await schema.safeParse(["asdf"]);
const r1 = schema.safeParse(["asdf"]);
expect(r1.success).toEqual(false);
expect(r1.error!.issues).toMatchInlineSnapshot(`
[
@@ -23,7 +23,7 @@ test("array min/max", async () => {
]
`);
const r2 = await schema.safeParse(["asdf", "asdf", "asdf"]);
const r2 = schema.safeParse(["asdf", "asdf", "asdf"]);
expect(r2.success).toEqual(false);
expect(r2.error!.issues).toMatchInlineSnapshot(`
[
@@ -39,11 +39,11 @@ test("array min/max", async () => {
`);
});
test("array length", async () => {
test("array length", () => {
const schema = z.array(z.string()).length(2);
schema.parse(["asdf", "asdf"]);
const r1 = await schema.safeParse(["asdf"]);
const r1 = schema.safeParse(["asdf"]);
expect(r1.success).toEqual(false);
expect(r1.error!.issues).toMatchInlineSnapshot(`
[
@@ -59,7 +59,7 @@ test("array length", async () => {
]
`);
const r2 = await schema.safeParse(["asdf", "asdf", "asdf"]);
const r2 = schema.safeParse(["asdf", "asdf", "asdf"]);
expect(r2.success).toEqual(false);
expect(r2.error!.issues).toMatchInlineSnapshot(`
[
+29 -5
View File
@@ -1,6 +1,5 @@
import { expect, expectTypeOf, test } from "vitest";
import { z } from "zod/v4";
import type { util } from "zod/v4/core";
test("basic catch", () => {
expect(z.string().catch("default").parse(undefined)).toBe("default");
@@ -45,7 +44,7 @@ test("catch with transform", () => {
expect(stringWithDefault.unwrap().out).toBeInstanceOf(z.ZodTransform);
type inp = z.input<typeof stringWithDefault>;
expectTypeOf<inp>().toEqualTypeOf<string | util.Whatever>();
expectTypeOf<inp>().toEqualTypeOf<string>();
type out = z.output<typeof stringWithDefault>;
expectTypeOf<out>().toEqualTypeOf<string>();
});
@@ -59,7 +58,7 @@ test("catch on existing optional", () => {
expect(stringWithDefault.unwrap().unwrap()).toBeInstanceOf(z.ZodString);
type inp = z.input<typeof stringWithDefault>;
expectTypeOf<inp>().toEqualTypeOf<string | undefined | util.Whatever>();
expectTypeOf<inp>().toEqualTypeOf<string | undefined>();
type out = z.output<typeof stringWithDefault>;
expectTypeOf<out>().toEqualTypeOf<string | undefined>();
});
@@ -68,7 +67,7 @@ test("optional on catch", () => {
const stringWithDefault = z.string().catch("asdf").optional();
type inp = z.input<typeof stringWithDefault>;
expectTypeOf<inp>().toEqualTypeOf<string | util.Whatever>();
expectTypeOf<inp>().toEqualTypeOf<string | undefined>();
type out = z.output<typeof stringWithDefault>;
expectTypeOf<out>().toEqualTypeOf<string | undefined>();
});
@@ -102,7 +101,7 @@ test("nested", () => {
inner: "asdf",
});
type input = z.input<typeof outer>;
expectTypeOf<input>().toEqualTypeOf<{ inner: string | util.Whatever } | util.Whatever>();
expectTypeOf<input>().toEqualTypeOf<{ inner: string }>();
type out = z.output<typeof outer>;
expectTypeOf<out>().toEqualTypeOf<{ inner: string }>();
@@ -250,3 +249,28 @@ test("ctx.input", () => {
expect(schema.parse(123)).toEqual("123");
});
test("direction-aware catch", () => {
const schema = z.string().catch("fallback");
// Forward direction (regular parse): catch should be applied
expect(schema.parse(123)).toBe("fallback");
// Reverse direction (encode): catch should NOT be applied, invalid value should fail validation
expect(z.safeEncode(schema, 123 as any)).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"expected": "string",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected string, received number"
}
]],
"success": false,
}
`);
// But valid values should still work in reverse
expect(z.encode(schema, "world")).toBe("world");
});
+23 -1
View File
@@ -62,7 +62,7 @@ test("continuability", () => {
"message": "Invalid UUID",
"origin": "string",
"path": [],
"pattern": "/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$/",
"pattern": "/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/",
},
{
"code": "custom",
@@ -195,6 +195,28 @@ test("continuability", () => {
},
]
`);
expect(
z
.mac()
.refine(() => false)
.safeParse("invalid_value").error!.issues
).toMatchInlineSnapshot(`
[
{
"code": "invalid_format",
"format": "mac",
"message": "Invalid MAC address",
"origin": "string",
"path": [],
"pattern": "/^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$/",
},
{
"code": "custom",
"message": "Invalid input",
"path": [],
},
]
`);
expect(
z
.emoji()
+6
View File
@@ -62,6 +62,12 @@ test("datetime parsing with offset", () => {
expect(() => datetimeOffset.parse("2020-10-14T17:42:29+03")).toThrow();
expect(() => datetimeOffset.parse("tuna")).toThrow();
expect(() => datetimeOffset.parse("2022-10-13T09:52:31.Z")).toThrow();
// Invalid offset tests
expect(() => datetimeOffset.parse("2020-10-14T17:42:29+24:00")).toThrow(); // out of range hours
expect(() => datetimeOffset.parse("2020-10-14T17:42:29+00:60")).toThrow(); // out of range minutes
expect(() => datetimeOffset.parse("2020-10-14T17:42:29+1:30")).toThrow(); // single digit hours
expect(() => datetimeOffset.parse("2020-10-14T17:42:29+00:")).toThrow(); // incomplete offset
});
test("datetime parsing with offset and precision 0", () => {
+52
View File
@@ -311,3 +311,55 @@ test("partial should not clobber defaults", () => {
}
`);
});
test("defaulted object schema returns shallow clone", () => {
const schema = z
.object({
a: z.string(),
})
.default({ a: "x" });
const result1 = schema.parse(undefined);
const result2 = schema.parse(undefined);
expect(result1).not.toBe(result2);
expect(result1).toEqual(result2);
});
test("defaulted array schema returns shallow clone", () => {
const schema = z.array(z.string()).default(["x"]);
const result1 = schema.parse(undefined);
const result2 = schema.parse(undefined);
expect(result1).not.toBe(result2);
expect(result1).toEqual(result2);
});
test("direction-aware defaults", () => {
const schema = z.string().default("hello");
// Forward direction (regular parse): defaults should be applied
expect(schema.parse(undefined)).toBe("hello");
expect(schema.parse("hello")).toBe("hello");
// Reverse direction (encode): defaults should NOT be applied, undefined should fail validation
expect(() => z.encode(schema, undefined as any)).toThrow();
// But valid values should still work in reverse
expect(z.safeEncode(schema, "world")).toMatchInlineSnapshot(`
{
"data": "world",
"success": true,
}
`);
expect(z.safeEncode(schema, undefined as any)).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"expected": "string",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected string, received undefined"
}
]],
"success": false,
}
`);
});
+42
View File
@@ -157,6 +157,7 @@ test("invalid discriminator value", () => {
"code": "invalid_union",
"errors": [],
"note": "No matching discriminator",
"discriminator": "type",
"path": [
"type"
],
@@ -617,3 +618,44 @@ test("readonly literal discriminator", () => {
discUnion.parse({ type: "c", a: "hello" });
}).toThrow();
});
test("pipes", () => {
const schema = z
.object({
type: z.literal("foo"),
})
.transform((s) => ({ ...s, v: 2 }));
expect(schema._zod.propValues).toMatchInlineSnapshot(`
{
"type": Set {
"foo",
},
}
`);
const schema2 = z.object({
type: z.literal("bar"),
});
const combinedSchema = z.discriminatedUnion("type", [schema, schema2], {
unionFallback: false,
});
combinedSchema.parse({
type: "foo",
v: 2,
});
});
test("def", () => {
const schema = z.discriminatedUnion(
"type",
[z.object({ type: z.literal("play") }), z.object({ type: z.literal("pause") })],
{ unionFallback: true }
);
expect(schema.def).toBeDefined();
expect(schema.def.discriminator).toEqual("type");
expect(schema.def.unionFallback).toEqual(true);
});
+70 -2
View File
@@ -321,7 +321,7 @@ test("all errors", () => {
});
const schema = z.strictObject({
username: z.string(),
username: z.string().brand<"username">(),
favoriteNumbers: z.array(z.number()),
nesting: z.object({
a: z.string(),
@@ -336,8 +336,33 @@ const result = schema.safeParse({
extra: 1234,
});
const tree = z.treeifyError(result.error!);
expectTypeOf(tree).toEqualTypeOf<{
errors: string[];
properties?: {
username?: {
errors: string[];
};
favoriteNumbers?: {
errors: string[];
items?: {
errors: string[];
}[];
};
nesting?: {
errors: string[];
properties?: {
a?: {
errors: string[];
};
};
};
};
}>();
test("z.treeifyError", () => {
expect(z.treeifyError(result.error!)).toMatchInlineSnapshot(`
expect(tree).toMatchInlineSnapshot(`
{
"errors": [
"Unrecognized key: "extra"",
@@ -525,3 +550,46 @@ test("disc union treeify/format", () => {
}
`);
});
test("update message after adding issues", () => {
const e = new z.ZodError([]);
e.addIssue({
code: "custom",
message: "message",
input: "asdf",
path: [],
});
expect(e.message).toMatchInlineSnapshot(`
"[
{
"code": "custom",
"message": "message",
"input": "asdf",
"path": []
}
]"
`);
e.addIssue({
code: "custom",
message: "message",
input: "asdf",
path: [],
});
expect(e.message).toMatchInlineSnapshot(`
"[
{
"code": "custom",
"message": "message",
"input": "asdf",
"path": []
},
{
"code": "custom",
"message": "message",
"input": "asdf",
"path": []
}
]"
`);
});
+9 -4
View File
@@ -1,7 +1,6 @@
// @ts-ignore
import { File as WebFile } from "@web-std/file";
import { afterEach, beforeEach, expect, test } from "vitest";
import { afterEach, beforeEach, expect, expectTypeOf, test } from "vitest";
import * as z from "zod/v4";
@@ -27,6 +26,10 @@ test("passing validations", () => {
expect(() => mimeCheck.parse(new File([""], "test.txt", { type: "text/csv" }))).toThrow();
});
test("types", () => {
expectTypeOf(z.file().parse(new File([], "test.txt"))).toEqualTypeOf(new File([], "test.txt"));
});
test("failing validations", () => {
expect(minCheck.safeParse(new File(["1234"], "test.txt"))).toMatchInlineSnapshot(`
{
@@ -35,8 +38,9 @@ test("failing validations", () => {
"origin": "file",
"code": "too_small",
"minimum": 5,
"inclusive": true,
"path": [],
"message": "Too small: expected file to have >5 bytes"
"message": "Too small: expected file to have >=5 bytes"
}
]],
"success": false,
@@ -49,8 +53,9 @@ test("failing validations", () => {
"origin": "file",
"code": "too_big",
"maximum": 8,
"inclusive": true,
"path": [],
"message": "Too big: expected file to have <8 bytes"
"message": "Too big: expected file to have <=8 bytes"
}
]],
"success": false,
+4
View File
@@ -82,6 +82,8 @@ test("first party switch", () => {
break;
case "lazy":
break;
case "function":
break;
default:
expectTypeOf(def).toEqualTypeOf<never>();
}
@@ -168,6 +170,8 @@ test("$ZodSchemaTypes", () => {
break;
case "lazy":
break;
case "function":
break;
default:
expectTypeOf(type).toEqualTypeOf<never>();
+71 -31
View File
@@ -32,38 +32,38 @@ test("function inference 1", () => {
expectTypeOf<func1>().toEqualTypeOf<(k: string) => number>();
});
// test("method parsing", () => {
// const methodObject = z.object({
// property: z.number(),
// method: z
// .function()
// .input(z.tuple([z.string()]))
// .output(z.number()),
// });
// const methodInstance = {
// property: 3,
// method: function (s: string) {
// return s.length + this.property;
// },
// };
// const parsed = methodObject.parse(methodInstance);
// expect(parsed.method("length=8")).toBe(11); // 8 length + 3 property
// });
test("method parsing", () => {
const methodObject = z.object({
property: z.number(),
method: z
.function()
.input(z.tuple([z.string()]))
.output(z.number()),
});
const methodInstance = {
property: 3,
method: function (s: string) {
return s.length + this.property;
},
};
const parsed = methodObject.parse(methodInstance);
expect(parsed.method("length=8")).toBe(11); // 8 length + 3 property
});
// test("async method parsing", async () => {
// const methodObject = z.object({
// property: z.number(),
// method: z.function().input(z.string()).output(z.promise(z.number())),
// });
// const methodInstance = {
// property: 3,
// method: async function (s: string) {
// return s.length + this.property;
// },
// };
// const parsed = methodObject.parse(methodInstance);
// expect(await parsed.method("length=8")).toBe(11); // 8 length + 3 property
// });
test("async method parsing", async () => {
const methodObject = z.object({
property: z.number(),
method: z.function().input([z.string()]).output(z.promise(z.number())),
});
const methodInstance = {
property: 3,
method: async function (s: string) {
return s.length + this.property;
},
};
const parsed = methodObject.parse(methodInstance);
expect(await parsed.method("length=8")).toBe(11); // 8 length + 3 property
});
test("args method", () => {
const t1 = z.function();
@@ -130,6 +130,46 @@ test("valid function run", () => {
});
});
const args3 = [
z.object({
f1: z.number(),
f2: z.string().nullable(),
f3: z.array(z.boolean().optional()).optional(),
}),
] as const;
const returns3 = z.union([z.string(), z.number()]);
const func3 = z.function({
input: args3,
output: returns3,
});
test("function inference 3", () => {
type func3 = (typeof func3)["_input"];
expectTypeOf<func3>().toEqualTypeOf<
(arg: {
f3?: (boolean | undefined)[] | undefined;
f1: number;
f2: string | null;
}) => string | number
>();
});
test("valid function run", () => {
const validFunc3Instance = func3.implement((_x) => {
_x.f2;
_x.f3![0];
return "adf" as any;
});
validFunc3Instance({
f1: 21,
f2: "asdf",
f3: [true, false],
});
});
test("input validation error", () => {
const schema = z.function({
input: z.tuple([z.string()]),
+70 -1
View File
@@ -302,6 +302,29 @@ test("z.record", () => {
const d = z.record(z.enum(["a", "b"]).or(z.never()), z.string());
type d = z.output<typeof d>;
expectTypeOf<d>().toEqualTypeOf<Record<"a" | "b", string>>();
// literal union keys
const e = z.record(z.union([z.literal("a"), z.literal(0)]), z.string());
type e = z.output<typeof e>;
expectTypeOf<e>().toEqualTypeOf<Record<"a" | 0, string>>();
expect(z.parse(e, { a: "hello", 0: "world" })).toEqual({
a: "hello",
0: "world",
});
// TypeScript enum keys
enum Enum {
A = 0,
B = "hi",
}
const f = z.record(z.enum(Enum), z.string());
type f = z.output<typeof f>;
expectTypeOf<f>().toEqualTypeOf<Record<Enum, string>>();
expect(z.parse(f, { [Enum.A]: "hello", [Enum.B]: "world" })).toEqual({
[Enum.A]: "hello",
[Enum.B]: "world",
});
});
test("z.map", () => {
@@ -749,7 +772,7 @@ test("z.json", () => {
test("z.promise", async () => {
const a = z.promise(z.string());
type a = z.output<typeof a>;
expectTypeOf<a>().toEqualTypeOf<string>();
expectTypeOf<a>().toEqualTypeOf<Promise<string>>();
expect(await z.safeParseAsync(a, Promise.resolve("hello"))).toMatchObject({
success: true,
@@ -786,6 +809,37 @@ test("isPlainObject", () => {
expect(z.core.util.isPlainObject("string")).toEqual(false);
expect(z.core.util.isPlainObject(123)).toEqual(false);
expect(z.core.util.isPlainObject(Symbol())).toEqual(false);
expect(z.core.util.isPlainObject({ constructor: "string" })).toEqual(true);
expect(z.core.util.isPlainObject({ constructor: 123 })).toEqual(true);
expect(z.core.util.isPlainObject({ constructor: null })).toEqual(true);
expect(z.core.util.isPlainObject({ constructor: undefined })).toEqual(true);
expect(z.core.util.isPlainObject({ constructor: true })).toEqual(true);
expect(z.core.util.isPlainObject({ constructor: {} })).toEqual(true);
expect(z.core.util.isPlainObject({ constructor: [] })).toEqual(true);
});
test("shallowClone with constructor field", () => {
const objWithConstructor = { constructor: "string", key: "value" };
const cloned = z.core.util.shallowClone(objWithConstructor);
expect(cloned).toEqual(objWithConstructor);
expect(cloned).not.toBe(objWithConstructor);
expect(cloned.constructor).toBe("string");
expect(cloned.key).toBe("value");
const testCases = [
{ constructor: 123, data: "test" },
{ constructor: null, data: "test" },
{ constructor: true, data: "test" },
{ constructor: {}, data: "test" },
{ constructor: [], data: "test" },
];
for (const testCase of testCases) {
const clonedCase = z.core.util.shallowClone(testCase);
expect(clonedCase).toEqual(testCase);
expect(clonedCase).not.toBe(testCase);
}
});
test("def typing", () => {
@@ -827,3 +881,18 @@ test("def typing", () => {
z.string().catch("fallback").def.type satisfies "catch";
z.file().def.type satisfies "file";
});
test("runtime type property exists and returns correct values", () => {
const stringSchema = z.string();
expect(stringSchema.type).toBe("string");
});
test("type narrowing works with type property", () => {
type ArrayOrRecord = z.ZodArray<z.ZodString> | z.ZodRecord<z.ZodString, z.ZodAny>;
const arraySchema = z.array(z.string()) as ArrayOrRecord;
if (arraySchema.type === "array") {
expectTypeOf(arraySchema).toEqualTypeOf<z.ZodArray<z.ZodString>>();
expect(arraySchema.element).toBeDefined();
}
});
+25
View File
@@ -90,3 +90,28 @@ test("readonly", () => {
const a = ["asdf"] as const;
z.literal(a);
});
test("literal pattern", () => {
expect(z.literal(1.1)._zod.pattern).toMatchInlineSnapshot(`/\\^\\(1\\\\\\.1\\)\\$/`);
expect(z.templateLiteral([z.literal(1.1)]).safeParse("1.1")).toMatchInlineSnapshot(`
{
"data": "1.1",
"success": true,
}
`);
expect(z.templateLiteral([z.literal(1.1)]).safeParse("1n1")).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "invalid_format",
"format": "template_literal",
"pattern": "^(1\\\\.1)$",
"path": [],
"message": "Invalid input"
}
]],
"success": false,
}
`);
});
+15
View File
@@ -84,3 +84,18 @@ test("nonoptional in object", () => {
]]
`);
});
test("encoding", () => {
const schema = z.string().optional().nonoptional();
expect(z.encode(schema, "hello")).toEqual("hello");
expect(() => z.encode(schema, undefined as any)).toThrowErrorMatchingInlineSnapshot(`
[ZodError: [
{
"code": "invalid_type",
"expected": "nonoptional",
"path": [],
"message": "Invalid input: expected nonoptional, received undefined"
}
]]
`);
});
+23
View File
@@ -114,6 +114,29 @@ test(".nonnegative() validation", () => {
expect(() => schema.parse(-1)).toThrow();
});
test("multipleOf", () => {
const numbers = {
number3: 5.123,
number6: 5.123456,
number7: 5.1234567,
number8: 5.12345678,
};
const schemas = {
schema6: z.number().multipleOf(0.000001),
schema7: z.number().multipleOf(0.0000001),
};
expect(() => schemas.schema6.parse(numbers.number3)).not.toThrow();
expect(() => schemas.schema6.parse(numbers.number6)).not.toThrow();
expect(() => schemas.schema6.parse(numbers.number7)).toThrow();
expect(() => schemas.schema6.parse(numbers.number8)).toThrow();
expect(() => schemas.schema7.parse(numbers.number3)).not.toThrow();
expect(() => schemas.schema7.parse(numbers.number6)).not.toThrow();
expect(() => schemas.schema7.parse(numbers.number7)).not.toThrow();
expect(() => schemas.schema7.parse(numbers.number8)).toThrow();
});
test(".multipleOf() with positive divisor", () => {
const schema = z.number().multipleOf(5);
expect(schema.parse(15)).toEqual(15);
+48 -2
View File
@@ -162,13 +162,13 @@ test("catchall overrides strict", () => {
});
});
test("optional keys are unset", async () => {
test("optional keys are unset", () => {
const SNamedEntity = z.object({
id: z.string(),
set: z.string().optional(),
unset: z.string().optional(),
});
const result = await SNamedEntity.parse({
const result = SNamedEntity.parse({
id: "asdf",
set: undefined,
});
@@ -261,6 +261,21 @@ test("inferred enum type", async () => {
expectTypeOf<Enum>().toEqualTypeOf<"a" | "b">();
});
test("z.keyof returns enum", () => {
const User = z.object({ name: z.string(), age: z.number() });
const keysSchema = z.keyof(User);
expect(keysSchema.enum).toEqual({
name: "name",
age: "age",
});
expect(keysSchema._zod.def.entries).toEqual({
name: "name",
age: "age",
});
type Keys = z.infer<typeof keysSchema>;
expectTypeOf<Keys>().toEqualTypeOf<"name" | "age">();
});
test("inferred partial object type with optional properties", async () => {
const Partial = z.object({ a: z.string(), b: z.string().optional() }).partial();
type Partial = z.infer<typeof Partial>;
@@ -420,6 +435,17 @@ test("extend() should have power to override existing key", () => {
expectTypeOf<PersonWithNumberAsLastName>().toEqualTypeOf<{ firstName: string; lastName: number }>();
});
test("safeExtend() maintains refinements", () => {
const schema = z.object({ name: z.string().min(1) });
const extended = schema.safeExtend({ name: z.string().min(2) });
expect(() => extended.parse({ name: "" })).toThrow();
expect(extended.parse({ name: "ab" })).toEqual({ name: "ab" });
type Extended = z.infer<typeof extended>;
expectTypeOf<Extended>().toEqualTypeOf<{ name: string }>();
// @ts-expect-error
schema.safeExtend({ name: z.number() });
});
test("passthrough index signature", () => {
const a = z.object({ a: z.string() });
type a = z.infer<typeof a>;
@@ -561,3 +587,23 @@ test("index signature in shape", () => {
expectTypeOf<schema>().toEqualTypeOf<Record<string, string>>();
});
test("extent() on object with refinements should throw", () => {
const schema = z
.object({
a: z.string(),
})
.refine(() => true);
expect(() => schema.extend({ b: z.string() })).toThrow();
});
test("safeExtend() on object with refinements should not throw", () => {
const schema = z
.object({
a: z.string(),
})
.refine(() => true);
expect(() => schema.safeExtend({ b: z.string() })).not.toThrow();
});
+13
View File
@@ -121,3 +121,16 @@ test("pipe optionality inside objects", () => {
e: string;
}>();
});
test("optional prop with pipe", () => {
const schema = z.object({
id: z
.union([z.number(), z.string().nullish()])
.transform((val) => (val === null || val === undefined ? val : Number(val)))
.pipe(z.number())
.optional(),
});
schema.parse({});
schema.parse({}, { jitless: true });
});
+193
View File
@@ -145,3 +145,196 @@ test("partial with mask -- ignore falsy values", async () => {
masked.parse({ country: "US" });
await masked.parseAsync({ country: "US" });
});
test("catch/prefault/default", () => {
const mySchema = z.object({
a: z.string().catch("catch value").optional(),
b: z.string().default("default value").optional(),
c: z.string().prefault("prefault value").optional(),
d: z.string().catch("catch value"),
e: z.string().default("default value"),
f: z.string().prefault("prefault value"),
});
expect(mySchema.parse({})).toMatchInlineSnapshot(`
{
"b": "default value",
"c": "prefault value",
"d": "catch value",
"e": "default value",
"f": "prefault value",
}
`);
expect(mySchema.parse({}, { jitless: true })).toMatchInlineSnapshot(`
{
"b": "default value",
"c": "prefault value",
"d": "catch value",
"e": "default value",
"f": "prefault value",
}
`);
});
test("handleOptionalObjectResult branches", () => {
const mySchema = z.object({
// Branch: input[key] === undefined, key not in input, caught error
caughtMissing: z.string().catch("caught").optional(),
// Branch: input[key] === undefined, key in input, caught error
caughtUndefined: z.string().catch("caught").optional(),
// Branch: input[key] === undefined, key not in input, validation issues
issueMissing: z.string().min(5).optional(),
// Branch: input[key] === undefined, key in input, validation issues
issueUndefined: z.string().min(5).optional(),
// Branch: input[key] === undefined, validation returns undefined
validUndefined: z.string().optional(),
// Branch: input[key] === undefined, non-undefined result (default/transform)
defaultValue: z.string().default("default").optional(),
// Branch: input[key] defined, caught error
caughtDefined: z.string().catch("caught").optional(),
// Branch: input[key] defined, validation issues
issueDefined: z.string().min(5).optional(),
// Branch: input[key] defined, validation returns undefined
validDefinedUndefined: z
.string()
.transform(() => undefined)
.optional(),
// Branch: input[key] defined, non-undefined value
validDefined: z.string().optional(),
});
// Test input[key] === undefined cases
const result1 = mySchema.parse(
{
// caughtMissing: not present (key not in input)
caughtUndefined: undefined, // key in input
// issueMissing: not present (key not in input)
issueUndefined: undefined, // key in input
validUndefined: undefined,
// defaultValue: not present, will get default
},
{ jitless: true }
);
expect(result1).toEqual({
caughtUndefined: undefined,
issueUndefined: undefined,
validUndefined: undefined,
defaultValue: "default",
});
// Test input[key] defined cases (successful)
const result2 = mySchema.parse(
{
caughtDefined: 123, // invalid type, should catch
validDefinedUndefined: "test", // transforms to undefined
validDefined: "valid", // valid value
},
{ jitless: true }
);
expect(result2).toEqual({
caughtDefined: "caught",
validDefinedUndefined: undefined,
validDefined: "valid",
defaultValue: "default",
});
// Test validation issues are properly reported (input[key] defined, validation fails)
expect(() =>
mySchema.parse(
{
issueDefined: "abc", // too short
},
{ jitless: true }
)
).toThrow();
});
test("fastpass vs non-fastpass consistency", () => {
const mySchema = z.object({
caughtMissing: z.string().catch("caught").optional(),
caughtUndefined: z.string().catch("caught").optional(),
issueMissing: z.string().min(5).optional(),
issueUndefined: z.string().min(5).optional(),
validUndefined: z.string().optional(),
defaultValue: z.string().default("default").optional(),
caughtDefined: z.string().catch("caught").optional(),
validDefinedUndefined: z
.string()
.transform(() => undefined)
.optional(),
validDefined: z.string().optional(),
});
const input = {
caughtUndefined: undefined,
issueUndefined: undefined,
validUndefined: undefined,
caughtDefined: 123,
validDefinedUndefined: "test",
validDefined: "valid",
};
// Test both paths produce identical results
const jitlessResult = mySchema.parse(input, { jitless: true });
const fastpassResult = mySchema.parse(input);
expect(jitlessResult).toEqual(fastpassResult);
expect(jitlessResult).toEqual({
caughtUndefined: undefined,
issueUndefined: undefined,
validUndefined: undefined,
defaultValue: "default",
caughtDefined: "caught",
validDefinedUndefined: undefined,
validDefined: "valid",
});
});
test("optional with check", () => {
const baseSchema = z
.string()
.optional()
.check(({ value, ...ctx }) => {
ctx.issues.push({
code: "custom",
input: value,
message: "message",
});
});
// this correctly fails
expect(baseSchema.safeParse(undefined)).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "custom",
"message": "message",
"path": []
}
]],
"success": false,
}
`);
const schemaObject = z.object({
date: baseSchema,
});
expect(schemaObject.safeParse({ date: undefined })).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "custom",
"message": "message",
"path": [
"date"
]
}
]],
"success": false,
}
`);
});
+5 -5
View File
@@ -114,14 +114,14 @@ test("pick/omit/required/partial - do not allow unknown keys", () => {
age: z.number(),
});
expect(() => schema.pick({ name: true, asdf: true })).toThrow();
expect(() => schema.pick({ name: true, asdf: true }).safeParse({})).toThrow();
// @ts-expect-error
expect(() => schema.pick({ $unknown: true })).toThrow();
expect(() => schema.pick({ $unknown: true }).safeParse({})).toThrow();
// @ts-expect-error
expect(() => schema.omit({ $unknown: true })).toThrow();
expect(() => schema.omit({ $unknown: true }).safeParse({})).toThrow();
// @ts-expect-error
expect(() => schema.required({ $unknown: true })).toThrow();
expect(() => schema.required({ $unknown: true }).safeParse({})).toThrow();
// @ts-expect-error
expect(() => schema.partial({ $unknown: true })).toThrow();
expect(() => schema.partial({ $unknown: true }).safeParse({})).toThrow();
});
+25 -5
View File
@@ -45,11 +45,6 @@ test("continue on non-fatal errors", () => {
"code": "custom",
"path": [],
"message": "A"
},
{
"code": "custom",
"path": [],
"message": "B"
}
]],
"success": false,
@@ -79,3 +74,28 @@ test("break on fatal errors", () => {
}
`);
});
test("reverse parsing with pipe", () => {
const schema = z.string().pipe(z.string());
// Reverse direction: default should NOT be applied
expect(z.safeDecode(schema, "asdf")).toMatchInlineSnapshot(`
{
"data": "asdf",
"success": true,
}
`);
expect(z.safeEncode(schema, "asdf")).toMatchInlineSnapshot(`
{
"data": "asdf",
"success": true,
}
`);
});
test("reverse parsing with pipe", () => {
const schema = z.string().transform((val) => val.length);
// should throw
expect(() => z.encode(schema, 1234)).toThrow();
});
+37
View File
@@ -35,3 +35,40 @@ test("prefault inside object", () => {
email: string;
}>();
});
test("object schema with prefault should return shallow clone", () => {
const schema = z
.object({
a: z.string(),
})
.default({ a: "x" });
const result1 = schema.parse(undefined);
const result2 = schema.parse(undefined);
expect(result1).not.toBe(result2);
expect(result1).toEqual(result2);
});
test("direction-aware prefault", () => {
const schema = z.string().prefault("hello");
// Forward direction (regular parse): prefault should be applied
expect(schema.parse(undefined)).toBe("hello");
// Reverse direction (encode): prefault should NOT be applied, undefined should fail validation
expect(z.safeEncode(schema, undefined as any)).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"expected": "string",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected string, received undefined"
}
]],
"success": false,
}
`);
// But valid values should still work in reverse
expect(z.encode(schema, "world")).toBe("world");
});
+5 -21
View File
@@ -73,17 +73,18 @@ test("preprocess ctx.addIssue with parse", () => {
`);
});
test("preprocess ctx.addIssue non-fatal by default", () => {
test("preprocess ctx.addIssue fatal by default", () => {
const schema = z.preprocess((data, ctx) => {
ctx.addIssue({
code: "custom",
message: `custom error`,
});
return data;
}, z.string());
const result = schema.safeParse(1234);
expect(result.error!.issues).toHaveLength(2);
expect(result.error!.issues).toHaveLength(1);
expect(result).toMatchInlineSnapshot(`
{
"error": [ZodError: [
@@ -91,12 +92,6 @@ test("preprocess ctx.addIssue non-fatal by default", () => {
"code": "custom",
"message": "custom error",
"path": []
},
{
"expected": "string",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected string, received number"
}
]],
"success": false,
@@ -175,7 +170,7 @@ test("z.NEVER in preprocess", () => {
expectTypeOf<foo>().toEqualTypeOf<number>();
const result = foo.safeParse(undefined);
expect(result.error!.issues).toHaveLength(2);
expect(result.error!.issues).toHaveLength(1);
expect(result).toMatchInlineSnapshot(`
{
"error": [ZodError: [
@@ -183,12 +178,6 @@ test("z.NEVER in preprocess", () => {
"code": "custom",
"message": "bad",
"path": []
},
{
"expected": "number",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected number, received object"
}
]],
"success": false,
@@ -280,14 +269,9 @@ test("perform transform with non-fatal issues", () => {
.transform((val) => val.length)
.pipe(z.number())
.refine((_) => false);
expect(A.safeParse("asdfasdf").error!.issues).toHaveLength(2);
expect(A.safeParse("asdfasdf").error!.issues).toHaveLength(1);
expect(A.safeParse("asdfasdf").error).toMatchInlineSnapshot(`
[ZodError: [
{
"code": "custom",
"path": [],
"message": "Invalid input"
},
{
"code": "custom",
"path": [],
+1 -1
View File
@@ -10,7 +10,7 @@ const promSchema = z.promise(
test("promise inference", () => {
type promSchemaType = z.infer<typeof promSchema>;
expectTypeOf<promSchemaType>().toEqualTypeOf<{ name: string; age: number }>();
expectTypeOf<promSchemaType>().toEqualTypeOf<Promise<{ name: string; age: number }>>();
});
test("promise parsing success", async () => {
+1 -1
View File
@@ -47,7 +47,7 @@ test("flat inference", () => {
expectTypeOf<typeof readonlyNumberRecord._output>().toEqualTypeOf<Readonly<Record<string, number>>>();
expectTypeOf<typeof readonlyObject._output>().toEqualTypeOf<{ readonly a: string; readonly 1: number }>();
expectTypeOf<typeof readonlyEnum._output>().toEqualTypeOf<Readonly<testEnum>>();
expectTypeOf<typeof readonlyPromise._output>().toEqualTypeOf<string>();
expectTypeOf<typeof readonlyPromise._output>().toEqualTypeOf<Promise<string>>();
});
// test("deep inference", () => {
+156 -10
View File
@@ -8,16 +8,28 @@ test("type inference", () => {
const recordWithEnumKeys = z.record(z.enum(["Tuna", "Salmon"]), z.string());
type recordWithEnumKeys = z.infer<typeof recordWithEnumKeys>;
const recordWithLiteralKey = z.record(z.literal(["Tuna", "Salmon"]), z.string());
const recordWithLiteralKey = z.record(z.literal(["Tuna", "Salmon", 21]), z.string());
type recordWithLiteralKey = z.infer<typeof recordWithLiteralKey>;
const recordWithLiteralUnionKeys = z.record(z.union([z.literal("Tuna"), z.literal("Salmon")]), z.string());
const recordWithLiteralUnionKeys = z.record(
z.union([z.literal("Tuna"), z.literal("Salmon"), z.literal(21)]),
z.string()
);
type recordWithLiteralUnionKeys = z.infer<typeof recordWithLiteralUnionKeys>;
enum Enum {
Tuna = 0,
Salmon = "Shark",
}
const recordWithTypescriptEnum = z.record(z.enum(Enum), z.string());
type recordWithTypescriptEnum = z.infer<typeof recordWithTypescriptEnum>;
expectTypeOf<booleanRecord>().toEqualTypeOf<Record<string, boolean>>();
expectTypeOf<recordWithEnumKeys>().toEqualTypeOf<Record<"Tuna" | "Salmon", string>>();
expectTypeOf<recordWithLiteralKey>().toEqualTypeOf<Record<"Tuna" | "Salmon", string>>();
expectTypeOf<recordWithLiteralUnionKeys>().toEqualTypeOf<Record<"Tuna" | "Salmon", string>>();
expectTypeOf<recordWithLiteralKey>().toEqualTypeOf<Record<"Tuna" | "Salmon" | 21, string>>();
expectTypeOf<recordWithLiteralUnionKeys>().toEqualTypeOf<Record<"Tuna" | "Salmon" | 21, string>>();
expectTypeOf<recordWithTypescriptEnum>().toEqualTypeOf<Record<Enum, string>>();
});
test("enum exhaustiveness", () => {
@@ -64,14 +76,76 @@ test("enum exhaustiveness", () => {
`);
});
test("typescript enum exhaustiveness", () => {
enum BigFish {
Tuna = 0,
Salmon = "Shark",
}
const schema = z.record(z.enum(BigFish), z.string());
const value = {
[BigFish.Tuna]: "asdf",
[BigFish.Salmon]: "asdf",
};
expect(schema.parse(value)).toEqual(value);
expect(schema.safeParse({ [BigFish.Tuna]: "asdf", [BigFish.Salmon]: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "unrecognized_keys",
"keys": [
"Trout"
],
"path": [],
"message": "Unrecognized key: \\"Trout\\""
}
]],
"success": false,
}
`);
expect(schema.safeParse({ [BigFish.Tuna]: "asdf" })).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"expected": "string",
"code": "invalid_type",
"path": [
"Shark"
],
"message": "Invalid input: expected string, received undefined"
}
]],
"success": false,
}
`);
expect(schema.safeParse({ [BigFish.Salmon]: "asdf" })).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"expected": "string",
"code": "invalid_type",
"path": [
0
],
"message": "Invalid input: expected string, received undefined"
}
]],
"success": false,
}
`);
});
test("literal exhaustiveness", () => {
const schema = z.record(z.literal(["Tuna", "Salmon"]), z.string());
const schema = z.record(z.literal(["Tuna", "Salmon", 21]), z.string());
schema.parse({
Tuna: "asdf",
Salmon: "asdf",
21: "asdf",
});
expect(schema.safeParse({ Tuna: "asdf", Salmon: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
expect(schema.safeParse({ Tuna: "asdf", Salmon: "asdf", 21: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
@@ -96,6 +170,14 @@ test("literal exhaustiveness", () => {
"Salmon"
],
"message": "Invalid input: expected string, received undefined"
},
{
"expected": "string",
"code": "invalid_type",
"path": [
21
],
"message": "Invalid input: expected string, received undefined"
}
]],
"success": false,
@@ -143,13 +225,14 @@ test("pipe exhaustiveness", () => {
});
test("union exhaustiveness", () => {
const schema = z.record(z.union([z.literal("Tuna"), z.literal("Salmon")]), z.string());
expect(schema.parse({ Tuna: "asdf", Salmon: "asdf" })).toEqual({
const schema = z.record(z.union([z.literal("Tuna"), z.literal("Salmon"), z.literal(21)]), z.string());
expect(schema.parse({ Tuna: "asdf", Salmon: "asdf", 21: "asdf" })).toEqual({
Tuna: "asdf",
Salmon: "asdf",
21: "asdf",
});
expect(schema.safeParse({ Tuna: "asdf", Salmon: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
expect(schema.safeParse({ Tuna: "asdf", Salmon: "asdf", 21: "asdf", Trout: "asdf" })).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
@@ -174,6 +257,14 @@ test("union exhaustiveness", () => {
"Salmon"
],
"message": "Invalid input: expected string, received undefined"
},
{
"expected": "string",
"code": "invalid_type",
"path": [
21
],
"message": "Invalid input: expected string, received undefined"
}
]],
"success": false,
@@ -336,7 +427,62 @@ test("partial record", () => {
type schema = z.infer<typeof schema>;
expectTypeOf<schema>().toEqualTypeOf<Partial<Record<string, string>>>();
const Keys = z.enum(["id", "name", "email"]).or(z.never());
const Keys = z.enum(["id", "name", "email"]); //.or(z.never());
const Person = z.partialRecord(Keys, z.string());
expectTypeOf<z.infer<typeof Person>>().toEqualTypeOf<Partial<Record<"id" | "name" | "email", string>>>();
Person.parse({
id: "123",
// name: "John",
// email: "john@example.com",
});
Person.parse({
// id: "123",
// name: "John",
email: "john@example.com",
});
expect(Person.def.keyType._zod.def.type).toEqual("enum");
});
test("partialRecord with z.literal([key, ...])", () => {
const Keys = z.literal(["id", "name", "email"]);
const schema = z.partialRecord(Keys, z.string());
type Schema = z.infer<typeof schema>;
expectTypeOf<Schema>().toEqualTypeOf<Partial<Record<"id" | "name" | "email", string>>>();
// Should parse valid partials
expect(schema.parse({})).toEqual({});
expect(schema.parse({ id: "1" })).toEqual({ id: "1" });
expect(schema.parse({ name: "n", email: "e@example.com" })).toEqual({ name: "n", email: "e@example.com" });
// Should fail with unrecognized key, error checked via inline snapshot
expect(schema.safeParse({ foo: "bar" })).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "invalid_key",
"origin": "record",
"issues": [
{
"code": "invalid_value",
"values": [
"id",
"name",
"email"
],
"path": [],
"message": "Invalid option: expected one of \\"id\\"|\\"name\\"|\\"email\\""
}
],
"path": [
"foo"
],
"message": "Invalid key in record"
}
]],
"success": false,
}
`);
});
+226
View File
@@ -260,6 +260,127 @@ test("mutual recursion with meta", () => {
expectTypeOf<B>().toEqualTypeOf<_B>();
});
test("intersection with recursive types", () => {
const A = z.discriminatedUnion("type", [
z.object({
type: z.literal("CONTAINER"),
}),
z.object({
type: z.literal("SCREEN"),
config: z.object({ x: z.number(), y: z.number() }),
}),
]);
// type A = z.infer<typeof A>;
const B = z.object({
get children() {
return z.array(C).optional();
},
});
// type B = z.infer<typeof B>;
const C = z.intersection(A, B);
type C = z.infer<typeof C>;
type _C = (
| {
type: "CONTAINER";
}
| {
type: "SCREEN";
config: {
x: number;
y: number;
};
}
) & {
children?: _C[] | undefined;
};
expectTypeOf<C>().toEqualTypeOf<_C>();
});
test("object utilities with recursive types", () => {
const NodeBase = z.object({
id: z.string(),
name: z.string(),
get children() {
return z.array(Node).optional();
},
});
// Test extend
const NodeOne = NodeBase.extend({
name: z.literal("nodeOne"),
get children() {
return z.array(Node);
},
});
const NodeTwo = NodeBase.extend({
name: z.literal("nodeTwo"),
get children() {
return z.array(Node);
},
});
// Test pick
const PickedNode = NodeBase.pick({ id: true, name: true });
// Test omit
const OmittedNode = NodeBase.omit({ children: true });
// Test merge
const ExtraProps = {
metadata: z.string(),
get parent() {
return Node.optional();
},
};
const MergedNode = NodeBase.extend(ExtraProps);
// Test partial
const PartialNode = NodeBase.partial();
const PartialMaskedNode = NodeBase.partial({ name: true });
// Test required (assuming NodeBase has optional fields)
const OptionalNodeBase = z.object({
id: z.string().optional(),
name: z.string().optional(),
get children() {
return z.array(Node).optional();
},
});
const RequiredNode = OptionalNodeBase.required();
const RequiredMaskedNode = OptionalNodeBase.required({ id: true });
const Node = z.union([
NodeOne,
NodeTwo,
PickedNode,
OmittedNode,
MergedNode,
PartialNode,
PartialMaskedNode,
RequiredNode,
RequiredMaskedNode,
]);
});
test("tuple with recursive types", () => {
const TaskListNodeSchema = z.strictObject({
type: z.literal("taskList"),
get content() {
return z.array(z.tuple([TaskListNodeSchema, z.union([TaskListNodeSchema])])).min(1);
},
});
type TaskListNodeSchema = z.infer<typeof TaskListNodeSchema>;
type _TaskListNodeSchema = {
type: "taskList";
content: [_TaskListNodeSchema, _TaskListNodeSchema][];
};
expectTypeOf<TaskListNodeSchema>().toEqualTypeOf<_TaskListNodeSchema>();
});
test("recursion compatibility", () => {
// array
const A = z.object({
@@ -325,6 +446,68 @@ test("recursion compatibility", () => {
});
});
test("recursive object with .check()", () => {
const Category = z
.object({
id: z.string(),
name: z.string(),
get subcategories() {
return z.array(Category).optional();
},
})
.check((ctx) => {
// Check for duplicate IDs among direct subcategories
if (ctx.value.subcategories) {
const siblingIds = new Set<string>();
ctx.value.subcategories.forEach((sub, index) => {
if (siblingIds.has(sub.id)) {
ctx.issues.push({
code: "custom",
message: `Duplicate sibling ID found: ${sub.id}`,
path: ["subcategories", index, "id"],
input: ctx.value,
});
}
siblingIds.add(sub.id);
});
}
});
// Valid - siblings have unique IDs
const validData = {
id: "electronics",
name: "Electronics",
subcategories: [
{
id: "computers",
name: "Computers",
subcategories: [
{ id: "laptops", name: "Laptops" },
{ id: "desktops", name: "Desktops" },
],
},
{
id: "phones",
name: "Phones",
},
],
};
// Invalid - duplicate sibling IDs
const invalidData = {
id: "electronics",
name: "Electronics",
subcategories: [
{ id: "computers", name: "Computers" },
{ id: "phones", name: "Phones" },
{ id: "computers", name: "Computers Again" }, // Duplicate at index 2
],
};
expect(() => Category.parse(validData)).not.toThrow();
expect(() => Category.parse(invalidData)).toThrow();
});
// biome-ignore lint: sadf
export type RecursiveA = z.ZodUnion<
[
@@ -354,3 +537,46 @@ export type RecursiveA = z.ZodUnion<
}>,
]
>;
test("recursive type with `id` meta", () => {
const AType = z.object({
type: z.literal("a"),
name: z.string(),
});
const BType = z.object({
type: z.literal("b"),
name: z.string(),
});
const CType = z.object({
type: z.literal("c"),
name: z.string(),
});
const Schema = z.object({
type: z.literal("special").meta({ description: "Type" }),
config: z.object({
title: z.string().meta({ description: "Title" }),
get elements() {
return z.array(z.discriminatedUnion("type", [AType, BType, CType])).meta({
id: "SpecialElements",
title: "SpecialElements",
description: "Array of elements",
});
},
}),
});
Schema.parse({
type: "special",
config: {
title: "Special",
elements: [
{ type: "a", name: "John" },
{ type: "b", name: "Jane" },
{ type: "c", name: "Jim" },
],
},
});
});
+78 -5
View File
@@ -167,8 +167,8 @@ describe("early termination options", () => {
});
describe("custom error paths", () => {
test("should use custom path in error message", async () => {
const result = await z
test("should use custom path in error message", () => {
const result = z
.object({ password: z.string(), confirm: z.string() })
.refine((data) => data.confirm === data.password, { path: ["confirm"] })
.safeParse({ password: "asdf", confirm: "qewr" });
@@ -252,6 +252,79 @@ describe("superRefine functionality", () => {
await expect(Strings.parseAsync(validArray)).resolves.toEqual(validArray);
});
test("should test continuability of custom issues", () => {
// Default continue behavior - allows subsequent refinements
const defaultContinue = z
.string()
.superRefine((_, ctx) => {
ctx.addIssue({ code: "custom", message: "First issue" });
})
.refine(() => false, "Second issue");
expect(defaultContinue.safeParse("test")).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "custom",
"message": "First issue",
"path": []
},
{
"code": "custom",
"path": [],
"message": "Second issue"
}
]],
"success": false,
}
`);
// Explicit continue: false - prevents subsequent refinements
const explicitContinueFalse = z
.string()
.superRefine((_, ctx) => {
ctx.addIssue({ code: "custom", message: "First issue", continue: false });
})
.refine(() => false, "Second issue");
expect(explicitContinueFalse.safeParse("test")).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "custom",
"message": "First issue",
"path": []
}
]],
"success": false,
}
`);
// Multiple issues in same refinement - both always added regardless of continue
const multipleInSame = z.string().superRefine((_, ctx) => {
ctx.addIssue({ code: "custom", message: "First", continue: false });
ctx.addIssue({ code: "custom", message: "Second" });
});
expect(multipleInSame.safeParse("test")).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "custom",
"message": "First",
"path": []
},
{
"code": "custom",
"message": "Second",
"path": []
}
]],
"success": false,
}
`);
});
test("should accept string as shorthand for custom error message", () => {
const schema = z.string().superRefine((_, ctx) => {
ctx.addIssue("bad stuff");
@@ -431,9 +504,9 @@ test("when", () => {
})
.refine(
(data) => {
console.log("running check...");
console.log(data);
console.log(data.password);
// console.log("running check...");
// console.log(data);
// console.log(data.password);
return data.password === data.confirmPassword;
},
{
+5 -1
View File
@@ -19,6 +19,10 @@ test("globalRegistry", () => {
expect(z.globalRegistry.has(a)).toEqual(false);
});
test("globalRegistry is singleton and attached to globalThis", () => {
expect(z.globalRegistry).toBe((globalThis as any).__zod_globalRegistry);
});
test("z.registry", () => {
const fieldRegistry = z.registry<{ name: string; description: string }>();
@@ -164,7 +168,7 @@ test("loose examples", () => {
});
});
test("function meta witout replacement", () => {
test("function meta without replacement", () => {
const myReg = z.registry<{
defaulter: (arg: string, test: boolean) => number;
}>();
+5 -3
View File
@@ -39,7 +39,7 @@ test("valid parse async", async () => {
expect(result.data!.has("second")).toEqual(true);
expect(result.data!.has("third")).toEqual(false);
const asyncResult = await stringSet.safeParse(new Set(["first", "second"]));
const asyncResult = stringSet.safeParse(new Set(["first", "second"]));
expect(asyncResult.success).toEqual(true);
expect(asyncResult.data!.has("first")).toEqual(true);
expect(asyncResult.data!.has("second")).toEqual(true);
@@ -155,7 +155,8 @@ test("min/max", async () => {
[
{
"code": "too_small",
"message": "Too small: expected set to have >4 items",
"inclusive": true,
"message": "Too small: expected set to have >=4 items",
"minimum": 4,
"origin": "set",
"path": [],
@@ -169,8 +170,9 @@ test("min/max", async () => {
[
{
"code": "too_big",
"inclusive": true,
"maximum": 5,
"message": "Too big: expected set to have <5 items",
"message": "Too big: expected set to have <=5 items",
"origin": "set",
"path": [],
},
+16
View File
@@ -107,3 +107,19 @@ test("z.stringFormat", () => {
`/\\^\\(\\?:\\\\d\\{14,19\\}\\|\\\\d\\{4\\}\\(\\?: \\\\d\\{3,6\\}\\)\\{2,4\\}\\|\\\\d\\{4\\}\\(\\?:-\\\\d\\{3,6\\}\\)\\{2,4\\}\\)\\$/u`
);
});
test("z.hex", () => {
const hexSchema = z.hex();
// Valid hex strings
expect(hexSchema.safeParse("").success).toBe(true); // Empty string is valid hex
expect(hexSchema.safeParse("123abc").success).toBe(true);
expect(hexSchema.safeParse("DEADBEEF").success).toBe(true);
expect(hexSchema.safeParse("0123456789abcdefABCDEF").success).toBe(true);
// Invalid hex strings
expect(hexSchema.safeParse("xyz").success).toBe(false);
expect(hexSchema.safeParse("123g").success).toBe(false);
expect(hexSchema.safeParse("hello world").success).toBe(false);
expect(hexSchema.safeParse("123-abc").success).toBe(false);
});
+276 -5
View File
@@ -318,6 +318,83 @@ test("url validations", () => {
expect(() => url.parse("https://")).toThrow();
});
test("url preserves original input", () => {
const url = z.string().url();
// Test the specific case from the user report
const input = "https://example.com?key=NUXOmHqWNVTapJkJJHw8BfD155AuqhH_qju_5fNmQ4ZHV7u8";
const output = url.parse(input);
expect(output).toBe(input); // Should preserve the original input exactly
// Test other cases where URL constructor would normalize
expect(url.parse("https://example.com?foo=bar")).toBe("https://example.com?foo=bar");
expect(url.parse("http://example.com?test=123")).toBe("http://example.com?test=123");
expect(url.parse("https://sub.example.com?param=value&other=data")).toBe(
"https://sub.example.com?param=value&other=data"
);
// Test cases with trailing slashes are preserved
expect(url.parse("https://example.com/")).toBe("https://example.com/");
expect(url.parse("https://example.com/path/")).toBe("https://example.com/path/");
// Test cases with paths and query parameters
expect(url.parse("https://example.com/path?query=param")).toBe("https://example.com/path?query=param");
});
test("url trims whitespace", () => {
const url = z.string().url();
// Test trimming whitespace from URLs
expect(url.parse(" https://example.com ")).toBe("https://example.com");
expect(url.parse(" https://example.com/path?query=param ")).toBe("https://example.com/path?query=param");
expect(url.parse("\t\nhttps://example.com\t\n")).toBe("https://example.com");
expect(url.parse(" https://example.com?key=value ")).toBe("https://example.com?key=value");
// Test that URLs without extra whitespace are unchanged
expect(url.parse("https://example.com")).toBe("https://example.com");
expect(url.parse("https://example.com/path")).toBe("https://example.com/path");
});
test("url normalize flag", () => {
const normalizeUrl = z.url({ normalize: true });
const preserveUrl = z.url(); // normalize: false/undefined by default
// Test that normalize flag causes URL normalization
expect(normalizeUrl.parse("https://example.com?key=value")).toBe("https://example.com/?key=value");
expect(normalizeUrl.parse("http://example.com?test=123")).toBe("http://example.com/?test=123");
// Test with already normalized URLs
expect(normalizeUrl.parse("https://example.com/")).toBe("https://example.com/");
expect(normalizeUrl.parse("https://example.com/path?query=param")).toBe("https://example.com/path?query=param");
// Test complex URLs with normalization
expect(normalizeUrl.parse("https://example.com/../?key=value")).toBe("https://example.com/?key=value");
expect(normalizeUrl.parse("https://example.com/./path?key=value")).toBe("https://example.com/path?key=value");
// Compare with non-normalize behavior
expect(preserveUrl.parse("https://example.com?key=value")).toBe("https://example.com?key=value");
expect(preserveUrl.parse("http://example.com?test=123")).toBe("http://example.com?test=123");
// Test trimming with normalize
expect(normalizeUrl.parse(" https://example.com?key=value ")).toBe("https://example.com/?key=value");
expect(preserveUrl.parse(" https://example.com?key=value ")).toBe("https://example.com?key=value");
});
test("url normalize with hostname and protocol constraints", () => {
const constrainedNormalizeUrl = z.url({
normalize: true,
protocol: /^https$/,
hostname: /^example\.com$/,
});
// Test that normalization works with constraints
expect(constrainedNormalizeUrl.parse("https://example.com?key=value")).toBe("https://example.com/?key=value");
// Test that constraints are still enforced
expect(() => constrainedNormalizeUrl.parse("http://example.com?key=value")).toThrow();
expect(() => constrainedNormalizeUrl.parse("https://other.com?key=value")).toThrow();
});
test("httpurl", () => {
const httpUrl = z.url({
protocol: /^https?$/,
@@ -450,6 +527,7 @@ test("good uuid", () => {
"9491d710-3185-5e06-8ea0-6a2f275345e0",
"9491d710-3185-5e06-9ea0-6a2f275345e0",
"00000000-0000-0000-0000-000000000000",
"ffffffff-ffff-ffff-ffff-ffffffffffff",
];
for (const goodUuid of goodUuids) {
@@ -464,11 +542,10 @@ test(`bad uuid`, () => {
"9491d710-3185-0e06-bea0-6a2f275345e0",
"9491d710-3185-5e06-0ea0-6a2f275345e0",
"d89e7b01-7598-ed11-9d7a-0022489382fd", // new sequential id
"b3ce60f8-e8b9-40f5-1150-172ede56ff74", // Variant 0 - RFC 4122: Reserved, NCS backward compatibility
"92e76bf9-28b3-4730-cd7f-cb6bc51f8c09", // Variant 2 - RFC 4122: Reserved, Microsoft Corporation backward compatibility
"b3ce60f8-e8b9-40f5-1150-172ede56ff74", // Variant 0 - RFC 9562/4122: Reserved, NCS backward compatibility
"92e76bf9-28b3-4730-cd7f-cb6bc51f8c09", // Variant 2 - RFC 9562/4122: Reserved, Microsoft Corporation backward compatibility
"invalid uuid",
"9491d710-3185-4e06-bea0-6a2f275345e0X",
"ffffffff-ffff-ffff-ffff-ffffffffffff",
]) {
const result = uuid.safeParse(badUuid);
expect(result).toMatchObject({ success: false });
@@ -481,8 +558,8 @@ test("good guid", () => {
for (const goodGuid of [
"9491d710-3185-4e06-bea0-6a2f275345e0",
"d89e7b01-7598-ed11-9d7a-0022489382fd", // new sequential id
"b3ce60f8-e8b9-40f5-1150-172ede56ff74", // Variant 0 - RFC 4122: Reserved, NCS backward compatibility
"92e76bf9-28b3-4730-cd7f-cb6bc51f8c09", // Variant 2 - RFC 4122: Reserved, Microsoft Corporation backward compatibility
"b3ce60f8-e8b9-40f5-1150-172ede56ff74", // Variant 0 - RFC 9562/4122: Reserved, NCS backward compatibility
"92e76bf9-28b3-4730-cd7f-cb6bc51f8c09", // Variant 2 - RFC 9562/4122: Reserved, Microsoft Corporation backward compatibility
"00000000-0000-0000-0000-000000000000",
"ffffffff-ffff-ffff-ffff-ffffffffffff",
]) {
@@ -698,6 +775,8 @@ test("format", () => {
expect(z.string().date().format).toEqual("date");
expect(z.string().time().format).toEqual("time");
expect(z.string().duration().format).toEqual("duration");
expect(z.mac().format).toEqual("mac");
});
test("min max getters", () => {
@@ -724,6 +803,24 @@ test("lowerCase", () => {
expect(z.string().toUpperCase().parse("asdf")).toEqual("ASDF");
});
test("slugify", () => {
expect(z.string().slugify().parse("Hello World")).toEqual("hello-world");
expect(z.string().slugify().parse(" Hello World ")).toEqual("hello-world");
expect(z.string().slugify().parse("Hello@World#123")).toEqual("helloworld123");
expect(z.string().slugify().parse("Hello-World")).toEqual("hello-world");
expect(z.string().slugify().parse("Hello_World")).toEqual("hello-world");
expect(z.string().slugify().parse("---Hello---World---")).toEqual("hello-world");
expect(z.string().slugify().parse("Hello World")).toEqual("hello-world");
expect(z.string().slugify().parse("Hello!@#$%^&*()World")).toEqual("helloworld");
// can be used with check
expect(z.string().check(z.slugify()).parse("Hello World")).toEqual("hello-world");
// can be chained with other methods
expect(z.string().slugify().min(5).parse("Hello World")).toEqual("hello-world");
expect(() => z.string().slugify().min(20).parse("Hello World")).toThrow();
});
// test("IP validation", () => {
// const ipSchema = z.string().ip();
@@ -808,6 +905,58 @@ test("IPv6 validation", () => {
expect(() => ipv6.parse("254.164.77.1")).toThrow();
});
test("MAC validation", () => {
const mac = z.mac();
// Valid MAC addresses
expect(mac.safeParse("00:1A:2B:3C:4D:5E").success).toBe(true);
expect(mac.safeParse("FF:FF:FF:FF:FF:FF").success).toBe(true);
expect(mac.safeParse("00:11:22:33:44:55").success).toBe(true);
expect(mac.safeParse("A1:B2:C3:D4:E5:F6").success).toBe(true);
expect(mac.safeParse("10:20:30:40:50:60").success).toBe(true);
expect(mac.safeParse("0a:1b:2c:3d:4e:5f").success).toBe(true);
expect(mac.safeParse("12:34:56:78:9A:BC").success).toBe(true);
// Invalid MAC addresses
expect(mac.safeParse("00:1A-2B:3C-4D:5E").success).toBe(false);
expect(mac.safeParse("00:1A:2B:3C:4D").success).toBe(false);
expect(mac.safeParse("00:1A:2B:3C:4D").success).toBe(false);
expect(mac.safeParse("00-1A-2B-3C-4D").success).toBe(false);
expect(mac.safeParse("01-23-45-67-89-AB").success).toBe(false); // Dash delimiter not accepted by default
expect(mac.safeParse("AA-BB-CC-DD-EE-FF").success).toBe(false); // Dash delimiter not accepted by default
expect(mac.safeParse("DE-AD-BE-EF-00-01").success).toBe(false); // Dash delimiter not accepted by default
expect(mac.safeParse("98-76-54-32-10-FF").success).toBe(false); // Dash delimiter not accepted by default
expect(mac.safeParse("00:1A:2B:3C:4D:GZ").success).toBe(false);
expect(mac.safeParse("00:1A:2B:3C:4D:5E:GG").success).toBe(false);
expect(mac.safeParse("123:45:67:89:AB:CD").success).toBe(false);
expect(mac.safeParse("00--1A:2B:3C:4D:5E").success).toBe(false);
expect(mac.safeParse("00:1A::2B:3C:4D:5E").success).toBe(false);
expect(mac.safeParse("00:1A:2B:3C:3C:2B:1A:00").success).toBe(false); // Disallow EUI-64
expect(mac.safeParse("00:1a:2B:3c:4D:5e").success).toBe(false); // Disallow mixed-case
// MAC formats that are nonstandard but occassionally referenced, ex. https://www.postgresql.org/docs/17/datatype-net-types.html#DATATYPE-MACADDR
expect(mac.safeParse("00:1A:2B:3C:4D:5E:FF").success).toBe(false);
expect(mac.safeParse("001A2B:3C4D5E").success).toBe(false);
expect(mac.safeParse("001A:2B3C:4D5E").success).toBe(false);
expect(mac.safeParse("001A.2B3C.4D5E").success).toBe(false);
expect(mac.safeParse("001A2B3C4D5E").success).toBe(false);
expect(mac.safeParse("00.1A.2B.3C.4D.5E").success).toBe(false);
});
test("MAC validation with custom delimiter", () => {
const colonMac = z.mac({ delimiter: ":" });
expect(colonMac.safeParse("00:1A:2B:3C:4D:5E").success).toBe(true);
expect(colonMac.safeParse("00-1A-2B-3C-4D-5E").success).toBe(false);
const dashMac = z.mac({ delimiter: "-" });
expect(dashMac.safeParse("00-1A-2B-3C-4D-5E").success).toBe(true);
expect(dashMac.safeParse("00:1A:2B:3C:4D:5E").success).toBe(false);
const colonOnlyMac = z.mac({ delimiter: ":" });
expect(colonOnlyMac.safeParse("00:1A:2B:3C:4D:5E").success).toBe(true);
expect(colonOnlyMac.safeParse("00-1A-2B-3C-4D-5E").success).toBe(false);
});
test("CIDR v4 validation", () => {
const cidrV4 = z.string().cidrv4();
@@ -842,6 +991,9 @@ test("CIDR v6 validation", () => {
expect(cidrV6.safeParse("2001:db8::/abc").success).toBe(false); // Invalid prefix format
expect(cidrV6.safeParse("not a cidr").success).toBe(false); // Invalid format
expect(cidrV6.safeParse("192.168.0.0/24").success).toBe(false); // IPv4 CIDR in v6 validation
expect(cidrV6.safeParse("2001:0db8:85a3::/64/whatever-after").success).toBe(false);
expect(cidrV6.safeParse("22d9:f4a8:6a90:f3bf:dcaa:2beb:5fba:0000/112").success).toBe(true);
expect(cidrV6.safeParse("22d9:f4a8:6a90:f3bf:dcaa:2beb:5fba:0000/112/268").success).toBe(false);
});
test("E.164 validation", () => {
@@ -879,3 +1031,122 @@ test("E.164 validation", () => {
expect(validE164Numbers.every((number) => e164Number.safeParse(number).success)).toBe(true);
expect(invalidE164Numbers.every((number) => e164Number.safeParse(number).success === false)).toBe(true);
});
test("hostname", () => {
const hostname = z.hostname();
// Valid hostnames
hostname.parse("localhost");
hostname.parse("example.com");
hostname.parse("sub.example.com");
hostname.parse("a-b-c.example.com");
hostname.parse("123.example.com");
hostname.parse("example-123.com");
hostname.parse("example-123.1234");
hostname.parse("developer.mozilla.org");
hostname.parse("hello.world.example.com");
hostname.parse("www.google.com");
hostname.parse("192.168.1.1");
hostname.parse("xn--d1acj3b.com");
hostname.parse("xn--d1acj3b.org");
hostname.parse("xn--d1acj3b");
// Invalid hostnames
expect(() => hostname.parse("")).toThrow();
expect(() => hostname.parse("example..com")).toThrow();
expect(() => hostname.parse("example-.com")).toThrow();
expect(() => hostname.parse("-example.com")).toThrow();
expect(() => hostname.parse("example.com-")).toThrow();
expect(() => hostname.parse("example_com")).toThrow();
expect(() => hostname.parse("example.com:8080")).toThrow();
expect(() => hostname.parse("http://example.com")).toThrow();
expect(() => hostname.parse("ht!tp://invalid.com")).toThrow();
expect(() => hostname.parse("xn--d1acj3b..com")).toThrow();
expect(() => hostname.parse("ex@mple.com")).toThrow();
expect(() => hostname.parse("[2001:db8::zzzz]")).toThrow();
expect(() => hostname.parse("exa mple.com")).toThrow();
expect(() => hostname.parse("-example.com")).toThrow();
expect(() => hostname.parse("example..com")).toThrow();
});
test("hash validation", () => {
// MD5 tests
const md5hex = z.hash("md5");
const md5base64 = z.hash("md5", { enc: "base64" });
const md5base64url = z.hash("md5", { enc: "base64url" });
// Valid MD5 hashes
expect(md5hex.parse("5d41402abc4b2a76b9719d911017c592")).toBe("5d41402abc4b2a76b9719d911017c592");
expect(md5hex.parse("5D41402ABC4B2A76B9719D911017C592")).toBe("5D41402ABC4B2A76B9719D911017C592"); // uppercase
expect(md5base64.parse("XUFAKrxLKna5cZ2REBfFkg==")).toBe("XUFAKrxLKna5cZ2REBfFkg==");
expect(md5base64url.parse("XUFAKrxLKna5cZ2REBfFkg")).toBe("XUFAKrxLKna5cZ2REBfFkg");
// Invalid MD5 hashes
expect(() => md5hex.parse("5d41402abc4b2a76b9719d911017c59")).toThrow(); // too short
expect(() => md5hex.parse("5d41402abc4b2a76b9719d911017c592x")).toThrow(); // too long
expect(() => md5base64.parse("XUFAKrxLKna5cZ2REBfFkg=")).toThrow(); // wrong padding
expect(() => md5base64url.parse("XUFAKrxLKna5cZ2REBfFkg=")).toThrow(); // has padding
// SHA1 tests
const sha1hex = z.hash("sha1");
const sha1base64 = z.hash("sha1", { enc: "base64" });
const sha1base64url = z.hash("sha1", { enc: "base64url" });
// Valid SHA1 hashes
expect(sha1hex.parse("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d")).toBe("aaf4c61ddcc5e8a2dabede0f3b482cd9aea9434d");
expect(sha1base64.parse("qvTGHdzF6KLavt4PO0gs2a6pQ00=")).toBe("qvTGHdzF6KLavt4PO0gs2a6pQ00=");
expect(sha1base64url.parse("qvTGHdzF6KLavt4PO0gs2a6pQ00")).toBe("qvTGHdzF6KLavt4PO0gs2a6pQ00");
// SHA256 tests
const sha256hex = z.hash("sha256");
const sha256base64 = z.hash("sha256", { enc: "base64" });
const sha256base64url = z.hash("sha256", { enc: "base64url" });
// Valid SHA256 hashes
expect(sha256hex.parse("2cf24dba4f21d4288094c4a2e2c2d6c6b0c3e0c8f0e0c8f0e0c8f0e0c8f0e0c8")).toBe(
"2cf24dba4f21d4288094c4a2e2c2d6c6b0c3e0c8f0e0c8f0e0c8f0e0c8f0e0c8"
);
expect(sha256base64.parse("LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ=")).toBe(
"LPJNul+wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ="
);
expect(sha256base64url.parse("LPJNul-wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ")).toBe(
"LPJNul-wow4m6DsqxbninhsWHlwfp0JecwQzYpOLmCQ"
);
// SHA384 tests (no padding in base64)
const sha384hex = z.hash("sha384");
const sha384base64 = z.hash("sha384", { enc: "base64" });
expect(
sha384hex.parse("59e1748777448c69de6b800d7a33bbfb9ff1b463e44354c3553bcdb9c666fa90125a3c79f90397bdf5f6a13de828684f")
).toBe("59e1748777448c69de6b800d7a33bbfb9ff1b463e44354c3553bcdb9c666fa90125a3c79f90397bdf5f6a13de828684f");
expect(sha384base64.parse("WeF0h3dEjGneawDXozO7+5/xtGPkQ1TDVTvNucZm+pASWjx5+QOXvfX2oT3oKGhP")).toBe(
"WeF0h3dEjGneawDXozO7+5/xtGPkQ1TDVTvNucZm+pASWjx5+QOXvfX2oT3oKGhP"
);
// SHA512 tests
const sha512hex = z.hash("sha512");
const sha512base64 = z.hash("sha512", { enc: "base64" });
expect(
sha512hex.parse(
"9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043"
)
).toBe(
"9b71d224bd62f3785d96d46ad3ea3d73319bfbc2890caadae2dff72519673ca72323c3d99ba5c11d7c7acc6e14b8c5da0c4663475c2e5c3adef46f73bcdec043"
);
expect(
sha512base64.parse("m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==")
).toBe("m3HSJL1i83hdltRq0+o9czGb+8KJDKra4t/3JRlnPKcjI8PZm6XBHXx6zG4UuMXaDEZjR1wuXDre9G9zvN7AQw==");
// Test default encoding (hex)
const defaultHash = z.hash("sha256");
expect(defaultHash.parse("2cf24dba4f21d4288094c4a2e2c2d6c6b0c3e0c8f0e0c8f0e0c8f0e0c8f0e0c8")).toBe(
"2cf24dba4f21d4288094c4a2e2c2d6c6b0c3e0c8f0e0c8f0e0c8f0e0c8f0e0c8"
);
// Test with custom error message
const hashWithMessage = z.hash("md5", { message: "Invalid MD5 hash" });
expect(() => hashWithMessage.parse("invalid")).toThrow("Invalid MD5 hash");
});
+40
View File
@@ -64,3 +64,43 @@ test("z.stringbool with custom error messages", () => {
expect(() => a.parse("")).toThrowError("wrong!");
});
test("z.stringbool codec encoding", () => {
const schema = z.stringbool();
// Test encoding with default values
expect(z.encode(schema, true)).toEqual("true");
expect(z.encode(schema, false)).toEqual("false");
});
test("z.stringbool codec encoding with custom values", () => {
const schema = z.stringbool({
truthy: ["yes", "on", "1"],
falsy: ["no", "off", "0"],
});
// Should return first element of custom arrays
expect(z.encode(schema, true)).toEqual("yes");
expect(z.encode(schema, false)).toEqual("no");
});
test("z.stringbool codec round trip", () => {
const schema = z.stringbool({
truthy: ["enabled", "active"],
falsy: ["disabled", "inactive"],
});
// Test round trip: string -> boolean -> string
const decoded = z.decode(schema, "enabled");
expect(decoded).toEqual(true);
const encoded = z.encode(schema, decoded);
expect(encoded).toEqual("enabled"); // First element of truthy array
// Test with falsy value
const decodedFalse = z.decode(schema, "inactive");
expect(decodedFalse).toEqual(false);
const encodedFalse = z.encode(schema, decodedFalse);
expect(encodedFalse).toEqual("disabled"); // First element of falsy array
});
+22 -9
View File
@@ -6,6 +6,7 @@ const hello = z.templateLiteral(["hello"]);
const world = z.templateLiteral(["", z.literal("world")]);
const one = z.templateLiteral([1]);
const two = z.templateLiteral(["", z.literal(2)]);
const onePointOne = z.templateLiteral([z.literal(1.1)]);
const truee = z.templateLiteral([true]);
const anotherTrue = z.templateLiteral(["", z.literal(true)]);
const falsee = z.templateLiteral([false]);
@@ -42,6 +43,7 @@ const email = z.templateLiteral(["", z.string().email()]);
// const ip = z.templateLiteral(["", z.string().ip()]);
const ipv4 = z.templateLiteral(["", z.string().ipv4()]);
const ipv6 = z.templateLiteral(["", z.string().ipv6()]);
const mac = z.templateLiteral(["", z.mac()]);
const ulid = z.templateLiteral(["", z.string().ulid()]);
const uuid = z.templateLiteral(["", z.string().uuid()]);
const stringAToZ = z.templateLiteral(["", z.string().regex(/^[a-z]+$/)]);
@@ -136,6 +138,7 @@ test("template literal type inference", () => {
// expectTypeOf<z.infer<typeof ip>>().toEqualTypeOf<string>();
expectTypeOf<z.infer<typeof ipv4>>().toEqualTypeOf<string>();
expectTypeOf<z.infer<typeof ipv6>>().toEqualTypeOf<string>();
expectTypeOf<z.infer<typeof mac>>().toEqualTypeOf<string>();
expectTypeOf<z.infer<typeof ulid>>().toEqualTypeOf<string>();
expectTypeOf<z.infer<typeof uuid>>().toEqualTypeOf<string>();
expectTypeOf<z.infer<typeof stringAToZ>>().toEqualTypeOf<string>();
@@ -289,6 +292,7 @@ test("template literal parsing - success - basic cases", () => {
world.parse("world");
one.parse("1");
two.parse("2");
onePointOne.parse("1.1");
truee.parse("true");
anotherTrue.parse("true");
falsee.parse("false");
@@ -359,6 +363,7 @@ test("template literal parsing - success - basic cases", () => {
// ip.parse("c359:f57c:21e5:39eb:1187:e501:f936:b452");
ipv4.parse("213.174.246.205");
ipv6.parse("c359:f57c:21e5:39eb:1187:e501:f936:b452");
mac.parse("00:1A:2B:3C:4D:5E");
ulid.parse("01GW3D2QZJBYB6P1Z1AE997VPW");
uuid.parse("808989fd-3a6e-4af2-b607-737323a176f6");
stringAToZ.parse("asudgaskhdgashd");
@@ -381,6 +386,7 @@ test("template literal parsing - failure - basic cases", () => {
expect(() => one.parse("2")).toThrow();
expect(() => one.parse("12")).toThrow();
expect(() => one.parse("21")).toThrow();
expect(() => onePointOne.parse("1s1")).toThrow();
expect(() => two.parse("1")).toThrow();
expect(() => two.parse("21")).toThrow();
expect(() => two.parse("12")).toThrow();
@@ -494,6 +500,8 @@ test("template literal parsing - failure - basic cases", () => {
expect(() => ipv4.parse("c359:f57c:21e5:39eb:1187:e501:f936:b452")).toThrow();
expect(() => ipv6.parse("c359:f57c:21e5:39eb:1187:e501:f936:b4521")).toThrow();
expect(() => ipv6.parse("213.174.246.205")).toThrow();
expect(() => mac.parse("00:1A:2B:3C:4D:5E:6A:7B")).toThrow();
expect(() => mac.parse("00:1A:2B:3C")).toThrow();
expect(() => ulid.parse("01GW3D2QZJBYB6P1Z1AE997VPW!")).toThrow();
expect(() => uuid.parse("808989fd-3a6e-4af2-b607-737323a176f6Z")).toThrow();
expect(() => uuid.parse("Z808989fd-3a6e-4af2-b607-737323a176f6")).toThrow();
@@ -531,15 +539,15 @@ test("regexes", () => {
expect(anyString._zod.pattern.source).toMatchInlineSnapshot(`"^[\\s\\S]{0,}$"`);
expect(lazyString._zod.pattern.source).toMatchInlineSnapshot(`"^[\\s\\S]{0,}$"`);
expect(anyNumber._zod.pattern.source).toMatchInlineSnapshot(`"^-?\\d+(?:\\.\\d+)?$"`);
expect(anyInt._zod.pattern.source).toMatchInlineSnapshot(`"^\\d+$"`);
expect(anyInt._zod.pattern.source).toMatchInlineSnapshot(`"^-?\\d+$"`);
// expect(anyFiniteNumber._zod.pattern.source).toMatchInlineSnapshot(`"^-?\\d+(?:\\.\\d+)?$"`);
// expect(anyNegativeNumber._zod.pattern.source).toMatchInlineSnapshot(`"^-?\\d+(?:\\.\\d+)?$"`);
// expect(anyPositiveNumber._zod.pattern.source).toMatchInlineSnapshot(`"^-?\\d+(?:\\.\\d+)?$"`);
// expect(zeroButInADumbWay._zod.pattern.source).toMatchInlineSnapshot(`"^-?\\d+(?:\\.\\d+)?$"`);
// expect(finiteButInADumbWay._zod.pattern.source).toMatchInlineSnapshot(`"^-?\\d+(?:\\.\\d+)?$"`);
expect(bool._zod.pattern.source).toMatchInlineSnapshot(`"^true|false$"`);
expect(bool._zod.pattern.source).toMatchInlineSnapshot(`"^(?:true|false)$"`);
expect(bigone._zod.pattern.source).toMatchInlineSnapshot(`"^(1)$"`);
expect(anyBigint._zod.pattern.source).toMatchInlineSnapshot(`"^\\d+n?$"`);
expect(anyBigint._zod.pattern.source).toMatchInlineSnapshot(`"^-?\\d+n?$"`);
expect(nullableYo._zod.pattern.source).toMatchInlineSnapshot(`"^((yo)|null)$"`);
expect(nullableString._zod.pattern.source).toMatchInlineSnapshot(`"^([\\s\\S]{0,}|null)$"`);
expect(optionalYeah._zod.pattern.source).toMatchInlineSnapshot(`"^((yeah))?$"`);
@@ -563,11 +571,14 @@ test("regexes", () => {
`"^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$"`
);
expect(ipv6._zod.pattern.source).toMatchInlineSnapshot(
`"^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})$"`
`"^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$"`
);
expect(mac._zod.pattern.source).toMatchInlineSnapshot(
`"^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$"`
);
expect(ulid._zod.pattern.source).toMatchInlineSnapshot(`"^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$"`);
expect(uuid._zod.pattern.source).toMatchInlineSnapshot(
`"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$"`
`"^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$"`
);
expect(stringAToZ._zod.pattern.source).toMatchInlineSnapshot(`"^[a-z]+$"`);
expect(stringStartsWith._zod.pattern.source).toMatchInlineSnapshot(`"^hello.*$"`);
@@ -580,7 +591,7 @@ test("regexes", () => {
expect(url._zod.pattern.source).toMatchInlineSnapshot(`"^https:\\/\\/\\w+\\.(com|net)$"`);
expect(measurement._zod.pattern.source).toMatchInlineSnapshot(`"^-?\\d+(?:\\.\\d+)?((px|em|rem|vh|vw|vmin|vmax))?$"`);
expect(connectionString._zod.pattern.source).toMatchInlineSnapshot(
`"^mongodb:\\/\\/(\\w+:\\w+@)?\\w+:\\d+(\\/(\\w+)?(\\?(\\w+=\\w+(&\\w+=\\w+)*)?)?)?$"`
`"^mongodb:\\/\\/(\\w+:\\w+@)?\\w+:-?\\d+(\\/(\\w+)?(\\?(\\w+=\\w+(&\\w+=\\w+)*)?)?)?$"`
);
});
@@ -670,8 +681,10 @@ test("template literal parsing - failure - complex cases", () => {
expect(() => connectionString.parse("mongodb://host1234")).toThrow();
expect(() => connectionString.parse("mongodb://host:d234")).toThrow();
expect(() => connectionString.parse("mongodb://host:12.34")).toThrow();
expect(() => connectionString.parse("mongodb://host:-1234")).toThrow();
expect(() => connectionString.parse("mongodb://host:-12.34")).toThrow();
// Note: template literal regex currently allows negative numbers despite .positive() constraint
// This is a known limitation where template literals use regex patterns directly
// expect(() => connectionString.parse("mongodb://host:-1234")).toThrow();
// expect(() => connectionString.parse("mongodb://host:-12.34")).toThrow();
expect(() => connectionString.parse("mongodb://host:")).toThrow();
expect(() => connectionString.parse("mongodb://:password@host:1234")).toThrow();
expect(() => connectionString.parse("mongodb://usernamepassword@host:1234")).toThrow();
@@ -732,7 +745,7 @@ test("template literal parsing - failure - issue format", () => {
{
"code": "invalid_format",
"format": "template_literal",
"pattern": "^mongodb:\\\\/\\\\/(\\\\w+:\\\\w+@)?\\\\w+:\\\\d+(\\\\/(\\\\w+)?(\\\\?(\\\\w+=\\\\w+(&\\\\w+=\\\\w+)*)?)?)?$",
"pattern": "^mongodb:\\\\/\\\\/(\\\\w+:\\\\w+@)?\\\\w+:-?\\\\d+(\\\\/(\\\\w+)?(\\\\?(\\\\w+=\\\\w+(&\\\\w+=\\\\w+)*)?)?)?$",
"path": [],
"message": "Invalid input"
}
+412 -7
View File
@@ -1,7 +1,34 @@
import { Validator } from "@seriousme/openapi-schema-validator";
import { describe, expect, test } from "vitest";
import * as z from "zod/v4";
// import * as zCore from "zod/v4/core";
const openAPI30Validator = new Validator();
/** @see https://github.com/colinhacks/zod/issues/5147 */
const validateOpenAPI30Schema = async (zodJSONSchema: Record<string, unknown>): Promise<true> => {
const res = await openAPI30Validator.validate({
openapi: "3.0.0",
info: {
title: "SampleApi",
description: "Sample backend service",
version: "1.0.0",
},
components: { schemas: { test: zodJSONSchema } },
paths: {},
});
if (!res.valid) {
// `console.error` should make `vitest` trow an unhandled error
// printing the validation messages in consoles
console.error(
`OpenAPI schema is not valid against ${openAPI30Validator.version}`,
JSON.stringify(res.errors, null, 2)
);
}
return true;
};
describe("toJSONSchema", () => {
test("primitive types", () => {
expect(z.toJSONSchema(z.string())).toMatchInlineSnapshot(`
@@ -101,7 +128,31 @@ describe("toJSONSchema", () => {
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "ipv6",
"pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})$",
"pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$",
"type": "string",
}
`);
expect(z.toJSONSchema(z.mac())).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "mac",
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
"type": "string",
}
`);
expect(z.toJSONSchema(z.mac({ delimiter: ":" }))).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "mac",
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
"type": "string",
}
`);
expect(z.toJSONSchema(z.mac({ delimiter: "-" }))).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "mac",
"pattern": "^(?:[0-9A-F]{2}-){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}-){5}[0-9a-f]{2}$",
"type": "string",
}
`);
@@ -109,7 +160,7 @@ describe("toJSONSchema", () => {
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"type": "string",
}
`);
@@ -269,7 +320,7 @@ describe("toJSONSchema", () => {
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "uuid",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$",
"pattern": "^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$",
"type": "string",
}
`);
@@ -325,7 +376,32 @@ describe("toJSONSchema", () => {
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "ipv6",
"pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})$",
"pattern": "^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$",
"type": "string",
}
`);
expect(z.toJSONSchema(z.mac())).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "mac",
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
"type": "string",
}
`);
expect(z.toJSONSchema(z.mac({ delimiter: ":" }))).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "mac",
"pattern": "^(?:[0-9A-F]{2}:){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}:){5}[0-9a-f]{2}$",
"type": "string",
}
`);
expect(z.toJSONSchema(z.mac({ delimiter: "-" }))).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"format": "mac",
"pattern": "^(?:[0-9A-F]{2}-){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}-){5}[0-9a-f]{2}$",
"type": "string",
}
`);
@@ -539,6 +615,68 @@ describe("toJSONSchema", () => {
`);
});
test("number constraints draft-4", () => {
expect(z.toJSONSchema(z.number().gt(5).lt(10), { target: "draft-4" })).toMatchInlineSnapshot(`
{
"$schema": "http://json-schema.org/draft-04/schema#",
"exclusiveMaximum": true,
"exclusiveMinimum": true,
"maximum": 10,
"minimum": 5,
"type": "number",
}
`);
});
test("nullable openapi-3.0", () => {
const schema = z.string().nullable();
const jsonSchema = z.toJSONSchema(schema, { target: "openapi-3.0" });
validateOpenAPI30Schema(jsonSchema);
expect(jsonSchema).toMatchInlineSnapshot(`
{
"nullable": true,
"type": "string",
}
`);
});
test("union with null openapi-3.0", () => {
const schema = z.union([z.string(), z.null()]);
const jsonSchema = z.toJSONSchema(schema, { target: "openapi-3.0" });
validateOpenAPI30Schema(jsonSchema);
expect(jsonSchema).toMatchInlineSnapshot(`
{
"anyOf": [
{
"type": "string",
},
{
"enum": [
null,
],
"nullable": true,
"type": "string",
},
],
}
`);
});
test("number with exclusive min-max openapi-3.0", () => {
const schema = z.number().lt(100).gt(1);
const jsonSchema = z.toJSONSchema(schema, { target: "openapi-3.0" });
validateOpenAPI30Schema(jsonSchema);
expect(jsonSchema).toMatchInlineSnapshot(`
{
"exclusiveMaximum": true,
"exclusiveMinimum": true,
"maximum": 100,
"minimum": 1,
"type": "number",
}
`);
});
test("arrays", () => {
expect(z.toJSONSchema(z.array(z.string()))).toMatchInlineSnapshot(`
{
@@ -568,6 +706,54 @@ describe("toJSONSchema", () => {
`);
});
test("discriminated unions", () => {
const schema = z.discriminatedUnion("type", [
z.object({ type: z.literal("success"), data: z.string() }),
z.object({ type: z.literal("error"), message: z.string() }),
]);
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"oneOf": [
{
"additionalProperties": false,
"properties": {
"data": {
"type": "string",
},
"type": {
"const": "success",
"type": "string",
},
},
"required": [
"type",
"data",
],
"type": "object",
},
{
"additionalProperties": false,
"properties": {
"message": {
"type": "string",
},
"type": {
"const": "error",
"type": "string",
},
},
"required": [
"type",
"message",
],
"type": "object",
},
],
}
`);
});
test("intersections", () => {
const schema = z.intersection(z.object({ name: z.string() }), z.object({ age: z.number() }));
@@ -620,7 +806,39 @@ describe("toJSONSchema", () => {
`);
});
test("record openapi-3.0", () => {
const schema = z.record(z.string(), z.boolean());
const jsonSchema = z.toJSONSchema(schema, { target: "openapi-3.0" });
validateOpenAPI30Schema(jsonSchema);
expect(jsonSchema).toMatchInlineSnapshot(`
{
"additionalProperties": {
"type": "boolean",
},
"type": "object",
}
`);
});
test("tuple", () => {
const schema = z.tuple([z.string(), z.number()]);
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
"prefixItems": [
{
"type": "string",
},
{
"type": "number",
},
],
"type": "array",
}
`);
});
test("tuple with rest", () => {
const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
{
@@ -641,6 +859,173 @@ describe("toJSONSchema", () => {
`);
});
test("tuple openapi-3.0", () => {
const schema = z.tuple([z.string(), z.number()]);
const jsonSchema = z.toJSONSchema(schema, { target: "openapi-3.0" });
validateOpenAPI30Schema(jsonSchema);
expect(jsonSchema).toMatchInlineSnapshot(`
{
"items": {
"anyOf": [
{
"type": "string",
},
{
"type": "number",
},
],
},
"maxItems": 2,
"minItems": 2,
"type": "array",
}
`);
});
test("tuple with rest openapi-3.0", () => {
const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
const jsonSchema = z.toJSONSchema(schema, { target: "openapi-3.0" });
validateOpenAPI30Schema(jsonSchema);
expect(jsonSchema).toMatchInlineSnapshot(`
{
"items": {
"anyOf": [
{
"type": "string",
},
{
"type": "number",
},
{
"type": "boolean",
},
],
},
"minItems": 3,
"type": "array",
}
`);
});
test("tuple with null openapi-3.0", () => {
const schema = z.tuple([z.string(), z.number(), z.null()]);
const jsonSchema = z.toJSONSchema(schema, { target: "openapi-3.0" });
validateOpenAPI30Schema(jsonSchema);
expect(jsonSchema).toMatchInlineSnapshot(`
{
"items": {
"anyOf": [
{
"type": "string",
},
{
"type": "number",
},
{
"enum": [
null,
],
"nullable": true,
"type": "string",
},
],
},
"maxItems": 3,
"minItems": 3,
"type": "array",
}
`);
});
test("tuple draft-7", () => {
const schema = z.tuple([z.string(), z.number()]);
expect(z.toJSONSchema(schema, { target: "draft-7", io: "input" })).toMatchInlineSnapshot(`
{
"$schema": "http://json-schema.org/draft-07/schema#",
"items": [
{
"type": "string",
},
{
"type": "number",
},
],
"type": "array",
}
`);
});
test("tuple with rest draft-7", () => {
const schema = z.tuple([z.string(), z.number()]).rest(z.boolean());
expect(z.toJSONSchema(schema, { target: "draft-7", io: "input" })).toMatchInlineSnapshot(`
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalItems": {
"type": "boolean",
},
"items": [
{
"type": "string",
},
{
"type": "number",
},
],
"type": "array",
}
`);
});
test("tuple with rest draft-7 - issue #5151 regression test", () => {
// This test addresses issue #5151: tuple with rest elements and ids
// in draft-7 had incorrect internal path handling affecting complex scenarios
const primarySchema = z.string().meta({ id: "primary" });
const restSchema = z.number().meta({ id: "rest" });
const testSchema = z.tuple([primarySchema], restSchema);
// Test both final output structure AND internal path handling
const capturedPaths: string[] = [];
const result = z.toJSONSchema(testSchema, {
target: "draft-7",
override: (ctx) => capturedPaths.push(ctx.path.join("/")),
});
// Verify correct draft-7 structure with metadata extraction
expect(result).toMatchInlineSnapshot(`
{
"$schema": "http://json-schema.org/draft-07/schema#",
"additionalItems": {
"$ref": "#/definitions/rest",
},
"definitions": {
"primary": {
"id": "primary",
"type": "string",
},
"rest": {
"id": "rest",
"type": "number",
},
},
"items": [
{
"$ref": "#/definitions/primary",
},
],
"type": "array",
}
`);
// Verify internal paths are correct (this was the actual bug)
expect(capturedPaths).toContain("items/0"); // prefix items should use "items" path
expect(capturedPaths).toContain("additionalItems"); // rest should use "additionalItems" path
expect(capturedPaths).not.toContain("prefixItems/0"); // should not use draft-2020-12 paths
// Structural validations
expect(Array.isArray(result.items)).toBe(true);
expect(result.additionalItems).toBeDefined();
});
test("promise", () => {
const schema = z.promise(z.string());
expect(z.toJSONSchema(schema)).toMatchInlineSnapshot(`
@@ -745,6 +1130,19 @@ describe("toJSONSchema", () => {
`);
});
test("literal draft-4", () => {
const a = z.literal("hello");
expect(z.toJSONSchema(a, { target: "draft-4" })).toMatchInlineSnapshot(`
{
"$schema": "http://json-schema.org/draft-04/schema#",
"enum": [
"hello",
],
"type": "string",
}
`);
});
// pipe
test("pipe", () => {
const schema = z
@@ -1451,7 +1849,9 @@ test("unrepresentable literal values are ignored", () => {
}
`);
const b = z.z.toJSONSchema(z.literal([undefined, null, 5, BigInt(1324)]), { unrepresentable: "any" });
const b = z.z.toJSONSchema(z.literal([undefined, null, 5, BigInt(1324)]), {
unrepresentable: "any",
});
expect(b).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -1463,7 +1863,9 @@ test("unrepresentable literal values are ignored", () => {
}
`);
const c = z.z.toJSONSchema(z.literal([undefined]), { unrepresentable: "any" });
const c = z.z.toJSONSchema(z.literal([undefined]), {
unrepresentable: "any",
});
expect(c).toMatchInlineSnapshot(`
{
"$schema": "https://json-schema.org/draft/2020-12/schema",
@@ -1746,7 +2148,9 @@ test("basic registry", () => {
myRegistry.add(User, { id: "User" });
myRegistry.add(Post, { id: "Post" });
const result = z.z.toJSONSchema(myRegistry, { uri: (id) => `https://example.com/${id}.json` });
const result = z.z.toJSONSchema(myRegistry, {
uri: (id) => `https://example.com/${id}.json`,
});
expect(result).toMatchInlineSnapshot(`
{
"schemas": {
@@ -1952,6 +2356,7 @@ test("input type", () => {
"required": [
"a",
"d",
"f",
"g",
],
"type": "object",
+111
View File
@@ -248,3 +248,114 @@ test("async short circuit on dirty", async () => {
]]
`);
});
test("do not continue by default", () => {
const A = z
.string()
.transform((val, ctx) => {
ctx.addIssue({
code: "custom",
message: `custom error`,
});
ctx.addIssue({
code: "custom",
message: `custom error`,
});
return val;
})
.pipe(z.number() as any);
expect(A.safeParse("asdf")).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "custom",
"message": "custom error",
"path": []
},
{
"code": "custom",
"message": "custom error",
"path": []
}
]],
"success": false,
}
`);
const B = z
.string()
.transform((val, ctx) => {
ctx.issues.push({
code: "custom",
message: `custom error`,
input: val,
});
ctx.issues.push({
code: "custom",
message: `custom error`,
input: val,
});
return val;
})
.pipe(z.number() as any);
expect(B.safeParse("asdf")).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "custom",
"message": "custom error",
"path": []
},
{
"code": "custom",
"message": "custom error",
"path": []
}
]],
"success": false,
}
`);
const C = z
.string()
.transform((val, ctx) => {
ctx.issues.push({
code: "custom",
message: `custom error`,
input: val,
continue: true,
});
ctx.issues.push({
code: "custom",
message: `custom error`,
input: val,
continue: true,
});
return val;
})
.pipe(z.number() as any);
expect(C.safeParse("asdf")).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "custom",
"message": "custom error",
"path": []
},
{
"code": "custom",
"message": "custom error",
"path": []
}
]],
"success": false,
}
`);
});
test("encode error", () => {
const schema = z.string().transform((val) => val.length);
expect(() => z.encode(schema, 1234)).toThrowErrorMatchingInlineSnapshot(
`[ZodEncodeError: Encountered unidirectional transform during encode: ZodTransform]`
);
});
+20 -2
View File
@@ -28,9 +28,9 @@ test("successful validation", () => {
expect(r2.error!).toMatchInlineSnapshot(`
[ZodError: [
{
"origin": "array",
"code": "too_big",
"maximum": 2,
"origin": "array",
"path": [],
"message": "Too big: expected array to have <2 items"
}
@@ -80,9 +80,9 @@ test("async validation", async () => {
expect(r2.error!).toMatchInlineSnapshot(`
[ZodError: [
{
"origin": "array",
"code": "too_big",
"maximum": 2,
"origin": "array",
"path": [],
"message": "Too big: expected array to have <2 items"
}
@@ -145,6 +145,24 @@ test("tuple with optional elements followed by required", () => {
}
});
test("tuple with all optional elements", () => {
const allOptionalTuple = z.tuple([z.string().optional(), z.number().optional(), z.boolean().optional()]);
expectTypeOf<typeof allOptionalTuple._output>().toEqualTypeOf<[string?, number?, boolean?]>();
// Empty array should be valid (all items optional)
expect(allOptionalTuple.parse([])).toEqual([]);
// Partial arrays should be valid
expect(allOptionalTuple.parse(["hello"])).toEqual(["hello"]);
expect(allOptionalTuple.parse(["hello", 42])).toEqual(["hello", 42]);
// Full array should be valid
expect(allOptionalTuple.parse(["hello", 42, true])).toEqual(["hello", 42, true]);
// Array that's too long should fail
expect(() => allOptionalTuple.parse(["hello", 42, true, "extra"])).toThrow();
});
test("tuple with rest schema", () => {
const myTuple = z.tuple([z.string(), z.number()]).rest(z.boolean());
expect(myTuple.parse(["asdf", 1234, true, false, true])).toEqual(["asdf", 1234, true, false, true]);
+90 -3
View File
@@ -27,7 +27,7 @@ test("return valid over invalid", () => {
});
test("return errors from both union arms", () => {
const result = z.union([z.number(), z.string().refine(() => false)]).safeParse("a");
const result = z.union([z.number(), z.boolean()]).safeParse("a");
expect(result.success).toEqual(false);
if (!result.success) {
expect(result.error.issues).toMatchInlineSnapshot(`
@@ -45,8 +45,9 @@ test("return errors from both union arms", () => {
],
[
{
"code": "custom",
"message": "Invalid input",
"code": "invalid_type",
"expected": "boolean",
"message": "Invalid input: expected boolean, received string",
"path": [],
},
],
@@ -92,3 +93,89 @@ test("union values", () => {
}
`);
});
test("non-aborted errors", () => {
const zItemTest = z.union([
z.object({
date: z.number(),
startDate: z.optional(z.null()),
endDate: z.optional(z.null()),
}),
z
.object({
date: z.optional(z.null()),
startDate: z.number(),
endDate: z.number(),
})
.refine((data) => data.startDate !== data.endDate, {
error: "startDate and endDate must be different",
path: ["endDate"],
}),
]);
const res = zItemTest.safeParse({
date: null,
startDate: 1,
endDate: 1,
});
expect(res).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "custom",
"path": [
"endDate"
],
"message": "startDate and endDate must be different"
}
]],
"success": false,
}
`);
});
test("surface continuable errors only if they exist", () => {
const schema = z.union([z.boolean(), z.uuid(), z.jwt()]);
expect(schema.safeParse("asdf")).toMatchInlineSnapshot(`
{
"error": [ZodError: [
{
"code": "invalid_union",
"errors": [
[
{
"expected": "boolean",
"code": "invalid_type",
"path": [],
"message": "Invalid input: expected boolean, received string"
}
],
[
{
"origin": "string",
"code": "invalid_format",
"format": "uuid",
"pattern": "/^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/",
"path": [],
"message": "Invalid UUID"
}
],
[
{
"code": "invalid_format",
"format": "jwt",
"path": [],
"message": "Invalid JWT"
}
]
],
"path": [],
"message": "Invalid input"
}
]],
"success": false,
}
`);
});
+205 -133
View File
@@ -1,6 +1,7 @@
import * as checks from "./checks.js";
import type * as core from "./core.js";
import type * as errors from "./errors.js";
import * as registries from "./registries.js";
import * as schemas from "./schemas.js";
import * as util from "./util.js";
@@ -79,13 +80,16 @@ export function _coercedString<T extends schemas.$ZodString>(
});
}
export type $ZodStringFormatParams = CheckTypeParams<schemas.$ZodStringFormat, "format" | "coerce">;
export type $ZodStringFormatParams = CheckTypeParams<
schemas.$ZodStringFormat,
"format" | "coerce" | "when" | "pattern"
>;
export type $ZodCheckStringFormatParams = CheckParams<checks.$ZodCheckStringFormat, "format">;
// custom format
// Email
export type $ZodEmailParams = StringFormatParams<schemas.$ZodEmail>;
export type $ZodCheckEmailParams = CheckStringFormatParams<schemas.$ZodEmail>;
export type $ZodEmailParams = StringFormatParams<schemas.$ZodEmail, "when">;
export type $ZodCheckEmailParams = CheckStringFormatParams<schemas.$ZodEmail, "when">;
export function _email<T extends schemas.$ZodEmail>(
Class: util.SchemaClass<T>,
params?: string | $ZodEmailParams | $ZodCheckEmailParams
@@ -100,8 +104,8 @@ export function _email<T extends schemas.$ZodEmail>(
}
// GUID
export type $ZodGUIDParams = StringFormatParams<schemas.$ZodGUID, "pattern">;
export type $ZodCheckGUIDParams = CheckStringFormatParams<schemas.$ZodGUID, "pattern">;
export type $ZodGUIDParams = StringFormatParams<schemas.$ZodGUID, "pattern" | "when">;
export type $ZodCheckGUIDParams = CheckStringFormatParams<schemas.$ZodGUID, "pattern" | "when">;
export function _guid<T extends schemas.$ZodGUID>(
Class: util.SchemaClass<T>,
params?: string | $ZodGUIDParams | $ZodCheckGUIDParams
@@ -116,8 +120,8 @@ export function _guid<T extends schemas.$ZodGUID>(
}
// UUID
export type $ZodUUIDParams = StringFormatParams<schemas.$ZodUUID, "pattern">;
export type $ZodCheckUUIDParams = CheckStringFormatParams<schemas.$ZodUUID, "pattern">;
export type $ZodUUIDParams = StringFormatParams<schemas.$ZodUUID, "pattern" | "when">;
export type $ZodCheckUUIDParams = CheckStringFormatParams<schemas.$ZodUUID, "pattern" | "when">;
export function _uuid<T extends schemas.$ZodUUID>(
Class: util.SchemaClass<T>,
params?: string | $ZodUUIDParams | $ZodCheckUUIDParams
@@ -132,8 +136,8 @@ export function _uuid<T extends schemas.$ZodUUID>(
}
// UUIDv4
export type $ZodUUIDv4Params = StringFormatParams<schemas.$ZodUUID, "pattern">;
export type $ZodCheckUUIDv4Params = CheckStringFormatParams<schemas.$ZodUUID, "pattern">;
export type $ZodUUIDv4Params = StringFormatParams<schemas.$ZodUUID, "pattern" | "when">;
export type $ZodCheckUUIDv4Params = CheckStringFormatParams<schemas.$ZodUUID, "pattern" | "when">;
export function _uuidv4<T extends schemas.$ZodUUID>(
Class: util.SchemaClass<T>,
params?: string | $ZodUUIDv4Params | $ZodCheckUUIDv4Params
@@ -149,8 +153,8 @@ export function _uuidv4<T extends schemas.$ZodUUID>(
}
// UUIDv6
export type $ZodUUIDv6Params = StringFormatParams<schemas.$ZodUUID, "pattern">;
export type $ZodCheckUUIDv6Params = CheckStringFormatParams<schemas.$ZodUUID, "pattern">;
export type $ZodUUIDv6Params = StringFormatParams<schemas.$ZodUUID, "pattern" | "when">;
export type $ZodCheckUUIDv6Params = CheckStringFormatParams<schemas.$ZodUUID, "pattern" | "when">;
export function _uuidv6<T extends schemas.$ZodUUID>(
Class: util.SchemaClass<T>,
params?: string | $ZodUUIDv6Params | $ZodCheckUUIDv6Params
@@ -166,8 +170,8 @@ export function _uuidv6<T extends schemas.$ZodUUID>(
}
// UUIDv7
export type $ZodUUIDv7Params = StringFormatParams<schemas.$ZodUUID, "pattern">;
export type $ZodCheckUUIDv7Params = CheckStringFormatParams<schemas.$ZodUUID, "pattern">;
export type $ZodUUIDv7Params = StringFormatParams<schemas.$ZodUUID, "pattern" | "when">;
export type $ZodCheckUUIDv7Params = CheckStringFormatParams<schemas.$ZodUUID, "pattern" | "when">;
export function _uuidv7<T extends schemas.$ZodUUID>(
Class: util.SchemaClass<T>,
params?: string | $ZodUUIDv7Params | $ZodCheckUUIDv7Params
@@ -183,8 +187,8 @@ export function _uuidv7<T extends schemas.$ZodUUID>(
}
// URL
export type $ZodURLParams = StringFormatParams<schemas.$ZodURL>;
export type $ZodCheckURLParams = CheckStringFormatParams<schemas.$ZodURL>;
export type $ZodURLParams = StringFormatParams<schemas.$ZodURL, "when">;
export type $ZodCheckURLParams = CheckStringFormatParams<schemas.$ZodURL, "when">;
export function _url<T extends schemas.$ZodURL>(
Class: util.SchemaClass<T>,
params?: string | $ZodURLParams | $ZodCheckURLParams
@@ -199,8 +203,8 @@ export function _url<T extends schemas.$ZodURL>(
}
// Emoji
export type $ZodEmojiParams = StringFormatParams<schemas.$ZodEmoji>;
export type $ZodCheckEmojiParams = CheckStringFormatParams<schemas.$ZodEmoji>;
export type $ZodEmojiParams = StringFormatParams<schemas.$ZodEmoji, "when">;
export type $ZodCheckEmojiParams = CheckStringFormatParams<schemas.$ZodEmoji, "when">;
export function _emoji<T extends schemas.$ZodEmoji>(
Class: util.SchemaClass<T>,
params?: string | $ZodEmojiParams | $ZodCheckEmojiParams
@@ -215,8 +219,8 @@ export function _emoji<T extends schemas.$ZodEmoji>(
}
// NanoID
export type $ZodNanoIDParams = StringFormatParams<schemas.$ZodNanoID>;
export type $ZodCheckNanoIDParams = CheckStringFormatParams<schemas.$ZodNanoID>;
export type $ZodNanoIDParams = StringFormatParams<schemas.$ZodNanoID, "when">;
export type $ZodCheckNanoIDParams = CheckStringFormatParams<schemas.$ZodNanoID, "when">;
export function _nanoid<T extends schemas.$ZodNanoID>(
Class: util.SchemaClass<T>,
params?: string | $ZodNanoIDParams | $ZodCheckNanoIDParams
@@ -231,8 +235,8 @@ export function _nanoid<T extends schemas.$ZodNanoID>(
}
// CUID
export type $ZodCUIDParams = StringFormatParams<schemas.$ZodCUID>;
export type $ZodCheckCUIDParams = CheckStringFormatParams<schemas.$ZodCUID>;
export type $ZodCUIDParams = StringFormatParams<schemas.$ZodCUID, "when">;
export type $ZodCheckCUIDParams = CheckStringFormatParams<schemas.$ZodCUID, "when">;
export function _cuid<T extends schemas.$ZodCUID>(
Class: util.SchemaClass<T>,
params?: string | $ZodCUIDParams | $ZodCheckCUIDParams
@@ -247,8 +251,8 @@ export function _cuid<T extends schemas.$ZodCUID>(
}
// CUID2
export type $ZodCUID2Params = StringFormatParams<schemas.$ZodCUID2>;
export type $ZodCheckCUID2Params = CheckStringFormatParams<schemas.$ZodCUID2>;
export type $ZodCUID2Params = StringFormatParams<schemas.$ZodCUID2, "when">;
export type $ZodCheckCUID2Params = CheckStringFormatParams<schemas.$ZodCUID2, "when">;
export function _cuid2<T extends schemas.$ZodCUID2>(
Class: util.SchemaClass<T>,
params?: string | $ZodCUID2Params | $ZodCheckCUID2Params
@@ -263,8 +267,8 @@ export function _cuid2<T extends schemas.$ZodCUID2>(
}
// ULID
export type $ZodULIDParams = StringFormatParams<schemas.$ZodULID>;
export type $ZodCheckULIDParams = CheckStringFormatParams<schemas.$ZodULID>;
export type $ZodULIDParams = StringFormatParams<schemas.$ZodULID, "when">;
export type $ZodCheckULIDParams = CheckStringFormatParams<schemas.$ZodULID, "when">;
export function _ulid<T extends schemas.$ZodULID>(
Class: util.SchemaClass<T>,
params?: string | $ZodULIDParams | $ZodCheckULIDParams
@@ -279,8 +283,8 @@ export function _ulid<T extends schemas.$ZodULID>(
}
// XID
export type $ZodXIDParams = StringFormatParams<schemas.$ZodXID>;
export type $ZodCheckXIDParams = CheckStringFormatParams<schemas.$ZodXID>;
export type $ZodXIDParams = StringFormatParams<schemas.$ZodXID, "when">;
export type $ZodCheckXIDParams = CheckStringFormatParams<schemas.$ZodXID, "when">;
export function _xid<T extends schemas.$ZodXID>(
Class: util.SchemaClass<T>,
params?: string | $ZodXIDParams | $ZodCheckXIDParams
@@ -295,8 +299,8 @@ export function _xid<T extends schemas.$ZodXID>(
}
// KSUID
export type $ZodKSUIDParams = StringFormatParams<schemas.$ZodKSUID>;
export type $ZodCheckKSUIDParams = CheckStringFormatParams<schemas.$ZodKSUID>;
export type $ZodKSUIDParams = StringFormatParams<schemas.$ZodKSUID, "when">;
export type $ZodCheckKSUIDParams = CheckStringFormatParams<schemas.$ZodKSUID, "when">;
export function _ksuid<T extends schemas.$ZodKSUID>(
Class: util.SchemaClass<T>,
params?: string | $ZodKSUIDParams | $ZodCheckKSUIDParams
@@ -310,25 +314,9 @@ export function _ksuid<T extends schemas.$ZodKSUID>(
});
}
// IP
// export type $ZodIPParams = StringFormatParams<schemas.$ZodIP, "pattern">;
// export type $ZodCheckIPParams = CheckStringFormatParams<schemas.$ZodIP, "pattern">;
// export function _ip<T extends schemas.$ZodIP>(
// Class: util.SchemaClass<T>,
// params?: string | $ZodIPParams | $ZodCheckIPParams
// ): T {
// return new Class({
// type: "string",
// format: "ip",
// check: "string_format",
// abort: false,
// ...util.normalizeParams(params),
// });
// }
// IPv4
export type $ZodIPv4Params = StringFormatParams<schemas.$ZodIPv4, "pattern">;
export type $ZodCheckIPv4Params = CheckStringFormatParams<schemas.$ZodIPv4, "pattern">;
export type $ZodIPv4Params = StringFormatParams<schemas.$ZodIPv4, "pattern" | "when" | "version">;
export type $ZodCheckIPv4Params = CheckStringFormatParams<schemas.$ZodIPv4, "pattern" | "when" | "version">;
export function _ipv4<T extends schemas.$ZodIPv4>(
Class: util.SchemaClass<T>,
params?: string | $ZodIPv4Params | $ZodCheckIPv4Params
@@ -343,8 +331,8 @@ export function _ipv4<T extends schemas.$ZodIPv4>(
}
// IPv6
export type $ZodIPv6Params = StringFormatParams<schemas.$ZodIPv6, "pattern">;
export type $ZodCheckIPv6Params = CheckStringFormatParams<schemas.$ZodIPv6, "pattern">;
export type $ZodIPv6Params = StringFormatParams<schemas.$ZodIPv6, "pattern" | "when" | "version">;
export type $ZodCheckIPv6Params = CheckStringFormatParams<schemas.$ZodIPv6, "pattern" | "when" | "version">;
export function _ipv6<T extends schemas.$ZodIPv6>(
Class: util.SchemaClass<T>,
params?: string | $ZodIPv6Params | $ZodCheckIPv6Params
@@ -358,9 +346,25 @@ export function _ipv6<T extends schemas.$ZodIPv6>(
});
}
// MAC
export type $ZodMACParams = StringFormatParams<schemas.$ZodMAC, "pattern" | "when">;
export type $ZodCheckMACParams = CheckStringFormatParams<schemas.$ZodMAC, "pattern" | "when">;
export function _mac<T extends schemas.$ZodMAC>(
Class: util.SchemaClass<T>,
params?: string | $ZodMACParams | $ZodCheckMACParams
): T {
return new Class({
type: "string",
format: "mac",
check: "string_format",
abort: false,
...util.normalizeParams(params),
});
}
// CIDRv4
export type $ZodCIDRv4Params = StringFormatParams<schemas.$ZodCIDRv4, "pattern">;
export type $ZodCheckCIDRv4Params = CheckStringFormatParams<schemas.$ZodCIDRv4, "pattern">;
export type $ZodCIDRv4Params = StringFormatParams<schemas.$ZodCIDRv4, "pattern" | "when">;
export type $ZodCheckCIDRv4Params = CheckStringFormatParams<schemas.$ZodCIDRv4, "pattern" | "when">;
export function _cidrv4<T extends schemas.$ZodCIDRv4>(
Class: util.SchemaClass<T>,
params?: string | $ZodCIDRv4Params | $ZodCheckCIDRv4Params
@@ -375,8 +379,8 @@ export function _cidrv4<T extends schemas.$ZodCIDRv4>(
}
// CIDRv6
export type $ZodCIDRv6Params = StringFormatParams<schemas.$ZodCIDRv6, "pattern">;
export type $ZodCheckCIDRv6Params = CheckStringFormatParams<schemas.$ZodCIDRv6, "pattern">;
export type $ZodCIDRv6Params = StringFormatParams<schemas.$ZodCIDRv6, "pattern" | "when">;
export type $ZodCheckCIDRv6Params = CheckStringFormatParams<schemas.$ZodCIDRv6, "pattern" | "when">;
export function _cidrv6<T extends schemas.$ZodCIDRv6>(
Class: util.SchemaClass<T>,
params?: string | $ZodCIDRv6Params | $ZodCheckCIDRv6Params
@@ -391,8 +395,8 @@ export function _cidrv6<T extends schemas.$ZodCIDRv6>(
}
// Base64
export type $ZodBase64Params = StringFormatParams<schemas.$ZodBase64, "pattern">;
export type $ZodCheckBase64Params = CheckStringFormatParams<schemas.$ZodBase64, "pattern">;
export type $ZodBase64Params = StringFormatParams<schemas.$ZodBase64, "pattern" | "when">;
export type $ZodCheckBase64Params = CheckStringFormatParams<schemas.$ZodBase64, "pattern" | "when">;
export function _base64<T extends schemas.$ZodBase64>(
Class: util.SchemaClass<T>,
params?: string | $ZodBase64Params | $ZodCheckBase64Params
@@ -407,8 +411,8 @@ export function _base64<T extends schemas.$ZodBase64>(
}
// base64url
export type $ZodBase64URLParams = StringFormatParams<schemas.$ZodBase64URL, "pattern">;
export type $ZodCheckBase64URLParams = CheckStringFormatParams<schemas.$ZodBase64URL, "pattern">;
export type $ZodBase64URLParams = StringFormatParams<schemas.$ZodBase64URL, "pattern" | "when">;
export type $ZodCheckBase64URLParams = CheckStringFormatParams<schemas.$ZodBase64URL, "pattern" | "when">;
export function _base64url<T extends schemas.$ZodBase64URL>(
Class: util.SchemaClass<T>,
params?: string | $ZodBase64URLParams | $ZodCheckBase64URLParams
@@ -423,8 +427,8 @@ export function _base64url<T extends schemas.$ZodBase64URL>(
}
// E164
export type $ZodE164Params = StringFormatParams<schemas.$ZodE164>;
export type $ZodCheckE164Params = CheckStringFormatParams<schemas.$ZodE164>;
export type $ZodE164Params = StringFormatParams<schemas.$ZodE164, "when">;
export type $ZodCheckE164Params = CheckStringFormatParams<schemas.$ZodE164, "when">;
export function _e164<T extends schemas.$ZodE164>(
Class: util.SchemaClass<T>,
params?: string | $ZodE164Params | $ZodCheckE164Params
@@ -439,8 +443,8 @@ export function _e164<T extends schemas.$ZodE164>(
}
// JWT
export type $ZodJWTParams = StringFormatParams<schemas.$ZodJWT, "pattern">;
export type $ZodCheckJWTParams = CheckStringFormatParams<schemas.$ZodJWT, "pattern">;
export type $ZodJWTParams = StringFormatParams<schemas.$ZodJWT, "pattern" | "when">;
export type $ZodCheckJWTParams = CheckStringFormatParams<schemas.$ZodJWT, "pattern" | "when">;
export function _jwt<T extends schemas.$ZodJWT>(
Class: util.SchemaClass<T>,
params?: string | $ZodJWTParams | $ZodCheckJWTParams
@@ -462,8 +466,8 @@ export const TimePrecision = {
Microsecond: 6,
} as const;
// ISODateTime
export type $ZodISODateTimeParams = StringFormatParams<schemas.$ZodISODateTime, "pattern">;
export type $ZodCheckISODateTimeParams = CheckStringFormatParams<schemas.$ZodISODateTime, "pattern">;
export type $ZodISODateTimeParams = StringFormatParams<schemas.$ZodISODateTime, "pattern" | "when">;
export type $ZodCheckISODateTimeParams = CheckStringFormatParams<schemas.$ZodISODateTime, "pattern" | "when">;
export function _isoDateTime<T extends schemas.$ZodISODateTime>(
Class: util.SchemaClass<T>,
params?: string | $ZodISODateTimeParams | $ZodCheckISODateTimeParams
@@ -480,8 +484,8 @@ export function _isoDateTime<T extends schemas.$ZodISODateTime>(
}
// ISODate
export type $ZodISODateParams = StringFormatParams<schemas.$ZodISODate, "pattern">;
export type $ZodCheckISODateParams = CheckStringFormatParams<schemas.$ZodISODate, "pattern">;
export type $ZodISODateParams = StringFormatParams<schemas.$ZodISODate, "pattern" | "when">;
export type $ZodCheckISODateParams = CheckStringFormatParams<schemas.$ZodISODate, "pattern" | "when">;
export function _isoDate<T extends schemas.$ZodISODate>(
Class: util.SchemaClass<T>,
params?: string | $ZodISODateParams | $ZodCheckISODateParams
@@ -495,8 +499,8 @@ export function _isoDate<T extends schemas.$ZodISODate>(
}
// ISOTime
export type $ZodISOTimeParams = StringFormatParams<schemas.$ZodISOTime, "pattern">;
export type $ZodCheckISOTimeParams = CheckStringFormatParams<schemas.$ZodISOTime, "pattern">;
export type $ZodISOTimeParams = StringFormatParams<schemas.$ZodISOTime, "pattern" | "when">;
export type $ZodCheckISOTimeParams = CheckStringFormatParams<schemas.$ZodISOTime, "pattern" | "when">;
export function _isoTime<T extends schemas.$ZodISOTime>(
Class: util.SchemaClass<T>,
params?: string | $ZodISOTimeParams | $ZodCheckISOTimeParams
@@ -511,8 +515,8 @@ export function _isoTime<T extends schemas.$ZodISOTime>(
}
// ISODuration
export type $ZodISODurationParams = StringFormatParams<schemas.$ZodISODuration>;
export type $ZodCheckISODurationParams = CheckStringFormatParams<schemas.$ZodISODuration>;
export type $ZodISODurationParams = StringFormatParams<schemas.$ZodISODuration, "when">;
export type $ZodCheckISODurationParams = CheckStringFormatParams<schemas.$ZodISODuration, "when">;
export function _isoDuration<T extends schemas.$ZodISODuration>(
Class: util.SchemaClass<T>,
params?: string | $ZodISODurationParams | $ZodCheckISODurationParams
@@ -528,7 +532,7 @@ export function _isoDuration<T extends schemas.$ZodISODuration>(
// Number
export type $ZodNumberParams = TypeParams<schemas.$ZodNumber<number>, "coerce">;
export type $ZodNumberFormatParams = CheckTypeParams<schemas.$ZodNumberFormat, "format" | "coerce">;
export type $ZodCheckNumberFormatParams = CheckParams<checks.$ZodCheckNumberFormat, "format">;
export type $ZodCheckNumberFormatParams = CheckParams<checks.$ZodCheckNumberFormat, "format" | "when">;
export function _number<T extends schemas.$ZodNumber>(
Class: util.SchemaClass<T>,
params?: string | $ZodNumberParams
@@ -638,7 +642,7 @@ export function _coercedBoolean<T extends schemas.$ZodBoolean>(
// BigInt
export type $ZodBigIntParams = TypeParams<schemas.$ZodBigInt<bigint>>;
export type $ZodBigIntFormatParams = CheckTypeParams<schemas.$ZodBigIntFormat, "format" | "coerce">;
export type $ZodCheckBigIntFormatParams = CheckParams<checks.$ZodCheckBigIntFormat, "format">;
export type $ZodCheckBigIntFormatParams = CheckParams<checks.$ZodCheckBigIntFormat, "format" | "when">;
export function _bigint<T extends schemas.$ZodBigInt>(
Class: util.SchemaClass<T>,
params?: string | $ZodBigIntParams
@@ -779,9 +783,9 @@ export function _nan<T extends schemas.$ZodNaN>(Class: util.SchemaClass<T>, para
});
}
// export type $ZodCheckParams = CheckParams<checks.$ZodCheck, "abort">;
// export type $ZodCheckParams = CheckParams<checks.$ZodCheck, "abort" | "when">;
export type $ZodCheckLessThanParams = CheckParams<checks.$ZodCheckLessThan, "inclusive" | "value">;
export type $ZodCheckLessThanParams = CheckParams<checks.$ZodCheckLessThan, "inclusive" | "value" | "when">;
export function _lt(
value: util.Numeric,
params?: string | $ZodCheckLessThanParams
@@ -812,7 +816,7 @@ export {
};
// ZodCheckGreaterThan
export type $ZodCheckGreaterThanParams = CheckParams<checks.$ZodCheckGreaterThan, "inclusive" | "value">;
export type $ZodCheckGreaterThanParams = CheckParams<checks.$ZodCheckGreaterThan, "inclusive" | "value" | "when">;
export function _gt(value: util.Numeric, params?: string | $ZodCheckGreaterThanParams): checks.$ZodCheckGreaterThan {
return new checks.$ZodCheckGreaterThan({
check: "greater_than",
@@ -856,7 +860,7 @@ export function _nonnegative(params?: string | $ZodCheckGreaterThanParams): chec
return _gte(0, params);
}
export type $ZodCheckMultipleOfParams = CheckParams<checks.$ZodCheckMultipleOf, "value">;
export type $ZodCheckMultipleOfParams = CheckParams<checks.$ZodCheckMultipleOf, "value" | "when">;
export function _multipleOf(
value: number | bigint,
params?: string | $ZodCheckMultipleOfParams
@@ -868,7 +872,7 @@ export function _multipleOf(
});
}
export type $ZodCheckMaxSizeParams = CheckParams<checks.$ZodCheckMaxSize, "maximum">;
export type $ZodCheckMaxSizeParams = CheckParams<checks.$ZodCheckMaxSize, "maximum" | "when">;
export function _maxSize(
maximum: number,
params?: string | $ZodCheckMaxSizeParams
@@ -880,7 +884,7 @@ export function _maxSize(
});
}
export type $ZodCheckMinSizeParams = CheckParams<checks.$ZodCheckMinSize, "minimum">;
export type $ZodCheckMinSizeParams = CheckParams<checks.$ZodCheckMinSize, "minimum" | "when">;
export function _minSize(
minimum: number,
params?: string | $ZodCheckMinSizeParams
@@ -892,7 +896,7 @@ export function _minSize(
});
}
export type $ZodCheckSizeEqualsParams = CheckParams<checks.$ZodCheckSizeEquals, "size">;
export type $ZodCheckSizeEqualsParams = CheckParams<checks.$ZodCheckSizeEquals, "size" | "when">;
export function _size(
size: number,
params?: string | $ZodCheckSizeEqualsParams
@@ -904,7 +908,7 @@ export function _size(
});
}
export type $ZodCheckMaxLengthParams = CheckParams<checks.$ZodCheckMaxLength, "maximum">;
export type $ZodCheckMaxLengthParams = CheckParams<checks.$ZodCheckMaxLength, "maximum" | "when">;
export function _maxLength(
maximum: number,
params?: string | $ZodCheckMaxLengthParams
@@ -917,7 +921,7 @@ export function _maxLength(
return ch;
}
export type $ZodCheckMinLengthParams = CheckParams<checks.$ZodCheckMinLength, "minimum">;
export type $ZodCheckMinLengthParams = CheckParams<checks.$ZodCheckMinLength, "minimum" | "when">;
export function _minLength(
minimum: number,
params?: string | $ZodCheckMinLengthParams
@@ -929,7 +933,7 @@ export function _minLength(
});
}
export type $ZodCheckLengthEqualsParams = CheckParams<checks.$ZodCheckLengthEquals, "length">;
export type $ZodCheckLengthEqualsParams = CheckParams<checks.$ZodCheckLengthEquals, "length" | "when">;
export function _length(
length: number,
params?: string | $ZodCheckLengthEqualsParams
@@ -941,7 +945,7 @@ export function _length(
});
}
export type $ZodCheckRegexParams = CheckParams<checks.$ZodCheckRegex, "format" | "pattern">;
export type $ZodCheckRegexParams = CheckParams<checks.$ZodCheckRegex, "format" | "pattern" | "when">;
export function _regex(pattern: RegExp, params?: string | $ZodCheckRegexParams): checks.$ZodCheckRegex {
return new checks.$ZodCheckRegex({
check: "string_format",
@@ -951,7 +955,7 @@ export function _regex(pattern: RegExp, params?: string | $ZodCheckRegexParams):
});
}
export type $ZodCheckLowerCaseParams = CheckParams<checks.$ZodCheckLowerCase, "format">;
export type $ZodCheckLowerCaseParams = CheckParams<checks.$ZodCheckLowerCase, "format" | "when">;
export function _lowercase(params?: string | $ZodCheckLowerCaseParams): checks.$ZodCheckLowerCase {
return new checks.$ZodCheckLowerCase({
check: "string_format",
@@ -960,7 +964,7 @@ export function _lowercase(params?: string | $ZodCheckLowerCaseParams): checks.$
});
}
export type $ZodCheckUpperCaseParams = CheckParams<checks.$ZodCheckUpperCase, "format">;
export type $ZodCheckUpperCaseParams = CheckParams<checks.$ZodCheckUpperCase, "format" | "when">;
export function _uppercase(params?: string | $ZodCheckUpperCaseParams): checks.$ZodCheckUpperCase {
return new checks.$ZodCheckUpperCase({
@@ -970,7 +974,7 @@ export function _uppercase(params?: string | $ZodCheckUpperCaseParams): checks.$
});
}
export type $ZodCheckIncludesParams = CheckParams<checks.$ZodCheckIncludes, "includes" | "format" | "pattern">;
export type $ZodCheckIncludesParams = CheckParams<checks.$ZodCheckIncludes, "includes" | "format" | "when" | "pattern">;
export function _includes(includes: string, params?: string | $ZodCheckIncludesParams): checks.$ZodCheckIncludes {
return new checks.$ZodCheckIncludes({
check: "string_format",
@@ -979,7 +983,10 @@ export function _includes(includes: string, params?: string | $ZodCheckIncludesP
includes,
});
}
export type $ZodCheckStartsWithParams = CheckParams<checks.$ZodCheckStartsWith, "prefix" | "format" | "pattern">;
export type $ZodCheckStartsWithParams = CheckParams<
checks.$ZodCheckStartsWith,
"prefix" | "format" | "when" | "pattern"
>;
export function _startsWith(prefix: string, params?: string | $ZodCheckStartsWithParams): checks.$ZodCheckStartsWith {
return new checks.$ZodCheckStartsWith({
check: "string_format",
@@ -989,7 +996,7 @@ export function _startsWith(prefix: string, params?: string | $ZodCheckStartsWit
});
}
export type $ZodCheckEndsWithParams = CheckParams<checks.$ZodCheckEndsWith, "suffix" | "format" | "pattern">;
export type $ZodCheckEndsWithParams = CheckParams<checks.$ZodCheckEndsWith, "suffix" | "format" | "pattern" | "when">;
export function _endsWith(suffix: string, params?: string | $ZodCheckEndsWithParams): checks.$ZodCheckEndsWith {
return new checks.$ZodCheckEndsWith({
@@ -1000,7 +1007,7 @@ export function _endsWith(suffix: string, params?: string | $ZodCheckEndsWithPar
});
}
export type $ZodCheckPropertyParams = CheckParams<checks.$ZodCheckProperty, "property" | "schema">;
export type $ZodCheckPropertyParams = CheckParams<checks.$ZodCheckProperty, "property" | "schema" | "when">;
export function _property<K extends string, T extends schemas.$ZodType>(
property: K,
schema: T,
@@ -1014,7 +1021,7 @@ export function _property<K extends string, T extends schemas.$ZodType>(
});
}
export type $ZodCheckMimeTypeParams = CheckParams<checks.$ZodCheckMimeType, "mime">;
export type $ZodCheckMimeTypeParams = CheckParams<checks.$ZodCheckMimeType, "mime" | "when">;
export function _mime(types: util.MimeTypes[], params?: string | $ZodCheckMimeTypeParams): checks.$ZodCheckMimeType {
return new checks.$ZodCheckMimeType({
check: "mime_type",
@@ -1046,6 +1053,10 @@ export function _toLowerCase(): checks.$ZodCheckOverwrite<string> {
export function _toUpperCase(): checks.$ZodCheckOverwrite<string> {
return _overwrite((input) => input.toUpperCase());
}
// slugify
export function _slugify(): checks.$ZodCheckOverwrite<string> {
return _overwrite((input) => util.slugify(input));
}
/////// collections ///////
@@ -1091,12 +1102,15 @@ export interface $ZodTypeDiscriminable extends schemas.$ZodType {
_zod: $ZodTypeDiscriminableInternals;
}
export type $ZodDiscriminatedUnionParams = TypeParams<schemas.$ZodDiscriminatedUnion, "options" | "discriminator">;
export function _discriminatedUnion<Types extends [$ZodTypeDiscriminable, ...$ZodTypeDiscriminable[]]>(
export function _discriminatedUnion<
Types extends [$ZodTypeDiscriminable, ...$ZodTypeDiscriminable[]],
Disc extends string,
>(
Class: util.SchemaClass<schemas.$ZodDiscriminatedUnion>,
discriminator: string,
discriminator: Disc,
options: Types,
params?: string | $ZodDiscriminatedUnionParams
): schemas.$ZodDiscriminatedUnion<Types> {
): schemas.$ZodDiscriminatedUnion<Types, Disc> {
return new Class({
type: "union",
options,
@@ -1328,7 +1342,7 @@ export function _default<T extends schemas.$ZodObject>(
type: "default",
innerType,
get defaultValue() {
return typeof defaultValue === "function" ? (defaultValue as Function)() : defaultValue;
return typeof defaultValue === "function" ? (defaultValue as Function)() : util.shallowClone(defaultValue);
},
}) as any;
}
@@ -1459,13 +1473,6 @@ export function _custom<O = unknown, I = O>(
return schema as any;
}
// export function _refine<T>(
// Class: util.SchemaClass<schemas.$ZodCustom>,
// fn: (arg: NoInfer<T>) => util.MaybeAsync<unknown>,
// _params: string | $ZodCustomParams = {}
// ): checks.$ZodCheck<T> {
// return _custom(Class, fn, _params);
// }
// same as _custom but defaults to abort:false
export function _refine<O = unknown, I = O>(
Class: util.SchemaClass<schemas.$ZodCustom>,
@@ -1482,6 +1489,80 @@ export function _refine<O = unknown, I = O>(
return schema as any;
}
export type $ZodSuperRefineIssue<T extends errors.$ZodIssueBase = errors.$ZodIssue> = T extends any
? RawIssue<T>
: never;
type RawIssue<T extends errors.$ZodIssueBase> = T extends any
? util.Flatten<
util.MakePartial<T, "message" | "path"> & {
/** The schema or check that originated this issue. */
readonly inst?: schemas.$ZodType | checks.$ZodCheck;
/** If `true`, Zod will execute subsequent checks/refinements instead of immediately aborting */
readonly continue?: boolean | undefined;
} & Record<string, unknown>
>
: never;
export interface $RefinementCtx<T = unknown> extends schemas.ParsePayload<T> {
addIssue(arg: string | $ZodSuperRefineIssue): void;
}
export function _superRefine<T>(fn: (arg: T, payload: $RefinementCtx<T>) => void | Promise<void>): checks.$ZodCheck<T> {
const ch = _check<T>((payload) => {
(payload as $RefinementCtx).addIssue = (issue) => {
if (typeof issue === "string") {
payload.issues.push(util.issue(issue, payload.value, ch._zod.def));
} else {
// for Zod 3 backwards compatibility
const _issue: any = issue;
if (_issue.fatal) _issue.continue = false;
_issue.code ??= "custom";
_issue.input ??= payload.value;
_issue.inst ??= ch;
_issue.continue ??= !ch._zod.def.abort; // abort is always undefined, so this is always true...
payload.issues.push(util.issue(_issue));
}
};
return fn(payload.value, payload as $RefinementCtx<T>);
});
return ch;
}
export function _check<O = unknown>(fn: schemas.CheckFn<O>, params?: string | $ZodCustomParams): checks.$ZodCheck<O> {
const ch = new checks.$ZodCheck({
check: "custom",
...util.normalizeParams(params),
});
ch._zod.check = fn;
return ch;
}
export function describe<T>(description: string): checks.$ZodCheck<T> {
const ch = new checks.$ZodCheck({ check: "describe" });
ch._zod.onattach = [
(inst) => {
const existing = registries.globalRegistry.get(inst) ?? {};
registries.globalRegistry.add(inst, { ...existing, description });
},
];
ch._zod.check = () => {}; // no-op check
return ch;
}
export function meta<T>(metadata: registries.GlobalMeta): checks.$ZodCheck<T> {
const ch = new checks.$ZodCheck({ check: "meta" });
ch._zod.onattach = [
(inst) => {
const existing = registries.globalRegistry.get(inst) ?? {};
registries.globalRegistry.add(inst, { ...existing, ...metadata });
},
];
ch._zod.check = () => {}; // no-op check
return ch;
}
// export type $ZodCustomParams = CheckTypeParams<schemas.$ZodCustom, "fn">
///////// STRINGBOOL /////////
@@ -1500,16 +1581,12 @@ export interface $ZodStringBoolParams extends TypeParams {
export function _stringbool(
Classes: {
Pipe?: typeof schemas.$ZodPipe;
Codec?: typeof schemas.$ZodCodec;
Boolean?: typeof schemas.$ZodBoolean;
Transform?: typeof schemas.$ZodTransform;
String?: typeof schemas.$ZodString;
},
_params?: string | $ZodStringBoolParams
): schemas.$ZodPipe<
schemas.$ZodPipe<schemas.$ZodString, schemas.$ZodTransform<boolean, string>>,
schemas.$ZodBoolean<boolean>
> {
): schemas.$ZodCodec<schemas.$ZodString, schemas.$ZodBoolean> {
const params = util.normalizeParams(_params);
let truthyArray = params.truthy ?? ["true", "1", "yes", "on", "y", "enabled"];
@@ -1522,15 +1599,19 @@ export function _stringbool(
const truthySet = new Set(truthyArray);
const falsySet = new Set(falsyArray);
const _Pipe = Classes.Pipe ?? schemas.$ZodPipe;
const _Codec = Classes.Codec ?? schemas.$ZodCodec;
const _Boolean = Classes.Boolean ?? schemas.$ZodBoolean;
const _String = Classes.String ?? schemas.$ZodString;
const _Transform = Classes.Transform ?? schemas.$ZodTransform;
const tx = new _Transform({
type: "transform",
transform: (input, payload: schemas.ParsePayload<unknown>) => {
let data: string = input as string;
const stringSchema = new _String({ type: "string", error: params.error });
const booleanSchema = new _Boolean({ type: "boolean", error: params.error });
const codec = new _Codec({
type: "pipe",
in: stringSchema as any,
out: booleanSchema as any,
transform: ((input: string, payload: schemas.ParsePayload<string>) => {
let data: string = input;
if (params.case !== "sensitive") data = data.toLowerCase();
if (truthySet.has(data)) {
return true;
@@ -1542,32 +1623,23 @@ export function _stringbool(
expected: "stringbool",
values: [...truthySet, ...falsySet],
input: payload.value,
inst: tx,
inst: codec,
continue: false,
});
return {} as never;
}
},
}) as any,
reverseTransform: ((input: boolean, _payload: schemas.ParsePayload<boolean>) => {
if (input === true) {
return truthyArray[0] || "true";
} else {
return falsyArray[0] || "false";
}
}) as any,
error: params.error,
});
// params.error;
}) as any;
const innerPipe = new _Pipe({
type: "pipe",
in: new _String({ type: "string", error: params.error }),
out: tx,
error: params.error,
});
const outerPipe = new _Pipe({
type: "pipe",
in: innerPipe,
out: new _Boolean({
type: "boolean",
error: params.error,
}),
error: params.error,
});
return outerPipe as any;
return codec;
}
export function _stringFormat<Format extends string>(
+6 -2
View File
@@ -297,6 +297,7 @@ export const $ZodCheckNumberFormat: core.$constructor<$ZodCheckNumberFormat> = /
expected: origin,
format: def.format,
code: "invalid_type",
continue: false,
input,
inst,
});
@@ -469,6 +470,7 @@ export const $ZodCheckMaxSize: core.$constructor<$ZodCheckMaxSize> = /*@__PURE__
origin: util.getSizableOrigin(input),
code: "too_big",
maximum: def.maximum,
inclusive: true,
input,
inst,
continue: !def.abort,
@@ -518,6 +520,7 @@ export const $ZodCheckMinSize: core.$constructor<$ZodCheckMinSize> = /*@__PURE__
origin: util.getSizableOrigin(input),
code: "too_small",
minimum: def.minimum,
inclusive: true,
input,
inst,
continue: !def.abort,
@@ -1128,12 +1131,12 @@ export interface $ZodCheckMimeTypeDef extends $ZodCheckDef {
mime: util.MimeTypes[];
}
export interface $ZodCheckMimeTypeInternals<T extends File = File> extends $ZodCheckInternals<T> {
export interface $ZodCheckMimeTypeInternals<T extends schemas.File = schemas.File> extends $ZodCheckInternals<T> {
def: $ZodCheckMimeTypeDef;
issc: errors.$ZodIssueInvalidValue;
}
export interface $ZodCheckMimeType<T extends File = File> extends $ZodCheck<T> {
export interface $ZodCheckMimeType<T extends schemas.File = schemas.File> extends $ZodCheck<T> {
_zod: $ZodCheckMimeTypeInternals<T>;
}
@@ -1152,6 +1155,7 @@ export const $ZodCheckMimeType: core.$constructor<$ZodCheckMimeType> = /*@__PURE
values: def.mime,
input: payload.value.type,
inst,
continue: !def.abort,
});
};
}
+31 -37
View File
@@ -20,21 +20,34 @@ export /*@__NO_SIDE_EFFECTS__*/ function $constructor<T extends ZodTrait, D = T[
params?: { Parent?: typeof Class }
): $constructor<T, D> {
function init(inst: T, def: D) {
Object.defineProperty(inst, "_zod", {
value: inst._zod ?? {},
enumerable: false,
});
if (!inst._zod) {
Object.defineProperty(inst, "_zod", {
value: {
def,
constr: _,
traits: new Set(),
},
enumerable: false,
});
}
inst._zod.traits ??= new Set();
if (inst._zod.traits.has(name)) {
return;
}
inst._zod.traits.add(name);
initializer(inst, def);
// support prototype modifications
for (const k in _.prototype) {
if (!(k in inst)) Object.defineProperty(inst, k, { value: _.prototype[k].bind(inst) });
const proto = _.prototype;
const keys = Object.keys(proto);
for (let i = 0; i < keys.length; i++) {
const k = keys[i]!;
if (!(k in inst)) {
(inst as any)[k] = proto[k].bind(inst);
}
}
inst._zod.constr = _;
inst._zod.def = def;
}
// doesn't work if Parent has a constructor with arguments
@@ -78,40 +91,21 @@ export class $ZodAsyncError extends Error {
}
}
export class $ZodEncodeError extends Error {
constructor(name: string) {
super(`Encountered unidirectional transform during encode: ${name}`);
this.name = "ZodEncodeError";
}
}
//////////////////////////// TYPE HELPERS ///////////////////////////////////
// export type input<T extends schemas.$ZodType> = T["_zod"]["input"];
// export type output<T extends schemas.$ZodType> = T["_zod"]["output"];
// export type input<T extends schemas.$ZodType> = T["_zod"]["input"];
// export type output<T extends schemas.$ZodType> = T["_zod"]["output"];
export type input<T> = T extends { _zod: { input: any } } ? Required<T["_zod"]>["input"] : unknown;
export type output<T> = T extends { _zod: { output: any } } ? Required<T["_zod"]>["output"] : unknown;
// Mk2
// export type input<T> = T extends { _zod: { "~input": any } }
// ? T["_zod"]["~input"]
// : T extends { _zod: { input: any } }
// ? T["_zod"]["input"]
// : never;
// export type output<T> = T extends { _zod: { "~output": any } }
// ? T["_zod"]["~output"]
// : T extends { _zod: { output: any } }
// ? T["_zod"]["output"]
// : never;
// Mk 3
// export type input<T extends schemas.$ZodType> = T["_zod"]["input"];
// export type output<T extends schemas.$ZodType> = T["_zod"]["output"];
// Mk 4
// export type input<T extends schemas.$ZodType> = T[] extends { _zod: { "~input": any } }
// ? T["_zod"]["~input"]
// : T extends { _zod: { input: any } }
// ? T["_zod"]["input"]
// : never;
// export type output<T extends schemas.$ZodType> = T extends { _zod: { "~output": any } }
// ? T["_zod"]["~output"]
// : T extends { _zod: { output: any } }
// ? T["_zod"]["output"]
// : never;
export type input<T> = T extends { _zod: { input: any } } ? T["_zod"]["input"] : unknown;
export type output<T> = T extends { _zod: { output: any } } ? T["_zod"]["output"] : unknown;
export type { output as infer };
+2 -2
View File
@@ -33,12 +33,12 @@ export class Doc {
}
}
compile() {
compile(): any {
const F = Function;
const args = this?.args;
const content = this?.content ?? [``];
const lines = [...content.map((x) => ` ${x}`)];
// console.log(lines.join("\n"));
return new F(...args, lines.join("\n"));
return new F(...args, lines.join("\n")) as any;
}
}
+49 -60
View File
@@ -1,6 +1,7 @@
import type { $ZodCheck, $ZodStringFormats } from "./checks.js";
import { $constructor } from "./core.js";
import type { $ZodType } from "./schemas.js";
import type { StandardSchemaV1 } from "./standard-schema.js";
import * as util from "./util.js";
///////////////////////////
@@ -11,7 +12,6 @@ export interface $ZodIssueBase {
readonly input?: unknown;
readonly path: PropertyKey[];
readonly message: string;
// [k: string]: unknown;
}
////////////////////////////////
@@ -20,7 +20,7 @@ export interface $ZodIssueBase {
export interface $ZodIssueInvalidType<Input = unknown> extends $ZodIssueBase {
readonly code: "invalid_type";
readonly expected: $ZodType["_zod"]["def"]["type"];
readonly input: Input;
readonly input?: Input;
}
export interface $ZodIssueTooBig<Input = unknown> extends $ZodIssueBase {
@@ -29,7 +29,7 @@ export interface $ZodIssueTooBig<Input = unknown> extends $ZodIssueBase {
readonly maximum: number | bigint;
readonly inclusive?: boolean;
readonly exact?: boolean;
readonly input: Input;
readonly input?: Input;
}
export interface $ZodIssueTooSmall<Input = unknown> extends $ZodIssueBase {
@@ -40,39 +40,40 @@ export interface $ZodIssueTooSmall<Input = unknown> extends $ZodIssueBase {
readonly inclusive?: boolean;
/** True if the allowed value is fixed (e.g.` z.length(5)`), not a range (`z.minLength(5)`) */
readonly exact?: boolean;
readonly input: Input;
readonly input?: Input;
}
export interface $ZodIssueInvalidStringFormat extends $ZodIssueBase {
readonly code: "invalid_format";
readonly format: $ZodStringFormats | (string & {});
readonly pattern?: string;
readonly input: string;
readonly input?: string;
}
export interface $ZodIssueNotMultipleOf<Input extends number | bigint = number | bigint> extends $ZodIssueBase {
readonly code: "not_multiple_of";
readonly divisor: number;
readonly input: Input;
readonly input?: Input;
}
export interface $ZodIssueUnrecognizedKeys extends $ZodIssueBase {
readonly code: "unrecognized_keys";
readonly keys: string[];
readonly input: Record<string, unknown>;
readonly input?: Record<string, unknown>;
}
export interface $ZodIssueInvalidUnion extends $ZodIssueBase {
readonly code: "invalid_union";
readonly errors: $ZodIssue[][];
readonly input: unknown;
readonly input?: unknown;
readonly discriminator?: string | undefined;
}
export interface $ZodIssueInvalidKey<Input = unknown> extends $ZodIssueBase {
readonly code: "invalid_key";
readonly origin: "map" | "record";
readonly issues: $ZodIssue[];
readonly input: Input;
readonly input?: Input;
}
export interface $ZodIssueInvalidElement<Input = unknown> extends $ZodIssueBase {
@@ -80,19 +81,19 @@ export interface $ZodIssueInvalidElement<Input = unknown> extends $ZodIssueBase
readonly origin: "map" | "set";
readonly key: unknown;
readonly issues: $ZodIssue[];
readonly input: Input;
readonly input?: Input;
}
export interface $ZodIssueInvalidValue<Input = unknown> extends $ZodIssueBase {
readonly code: "invalid_value";
readonly values: util.Primitive[];
readonly input: Input;
readonly input?: Input;
}
export interface $ZodIssueCustom extends $ZodIssueBase {
readonly code: "custom";
readonly params?: Record<string, any> | undefined;
readonly input: unknown;
readonly input?: unknown;
}
////////////////////////////////////////////
@@ -155,17 +156,21 @@ export type $ZodIssue =
export type $ZodIssueCode = $ZodIssue["code"];
export type $ZodRawIssue<T extends $ZodIssueBase = $ZodIssue> = T extends any ? RawIssue<T> : never;
type RawIssue<T extends $ZodIssueBase> = util.Flatten<
util.MakePartial<T, "message" | "path"> & {
/** The input data */
readonly input?: unknown;
/** The schema or check that originated this issue. */
readonly inst?: $ZodType | $ZodCheck;
/** @deprecated Internal use only. If `true`, Zod will continue executing validation despite this issue. */
readonly continue?: boolean | undefined;
} & Record<string, any>
>;
export type $ZodInternalIssue<T extends $ZodIssueBase = $ZodIssue> = T extends any ? RawIssue<T> : never;
type RawIssue<T extends $ZodIssueBase> = T extends any
? util.Flatten<
util.MakePartial<T, "message" | "path"> & {
/** The input data */
readonly input: unknown;
/** The schema or check that originated this issue. */
readonly inst?: $ZodType | $ZodCheck;
/** If `true`, Zod will continue executing checks/refinements after this issue. */
readonly continue?: boolean | undefined;
} & Record<string, unknown>
>
: never;
export type $ZodRawIssue<T extends $ZodIssueBase = $ZodIssue> = $ZodInternalIssue<T>;
export interface $ZodErrorMap<T extends $ZodIssueBase = $ZodIssue> {
// biome-ignore lint:
@@ -196,13 +201,8 @@ const initializer = (inst: $ZodError, def: $ZodIssue[]): void => {
value: def,
enumerable: false,
});
Object.defineProperty(inst, "message", {
get() {
return JSON.stringify(def, util.jsonStringifyReplacer, 2);
},
enumerable: true,
// configurable: false,
});
inst.message = JSON.stringify(def, util.jsonStringifyReplacer, 2);
Object.defineProperty(inst, "toString", {
value: () => inst.message,
enumerable: false,
@@ -226,9 +226,9 @@ type _FlattenedError<T, U = string> = {
export function flattenError<T>(error: $ZodError<T>): _FlattenedError<T>;
export function flattenError<T, U>(error: $ZodError<T>, mapper?: (issue: $ZodIssue) => U): _FlattenedError<T, U>;
export function flattenError(error: $ZodError, mapper = (issue: $ZodIssue) => issue.message): any {
const fieldErrors: any = {};
const formErrors: any[] = [];
export function flattenError<T, U>(error: $ZodError<T>, mapper = (issue: $ZodIssue) => issue.message as U) {
const fieldErrors: Record<PropertyKey, any> = {};
const formErrors: U[] = [];
for (const sub of error.issues) {
if (sub.path.length > 0) {
fieldErrors[sub.path[0]!] = fieldErrors[sub.path[0]!] || [];
@@ -254,12 +254,7 @@ export type $ZodFormattedError<T, U = string> = {
export function formatError<T>(error: $ZodError<T>): $ZodFormattedError<T>;
export function formatError<T, U>(error: $ZodError<T>, mapper?: (issue: $ZodIssue) => U): $ZodFormattedError<T, U>;
export function formatError<T>(error: $ZodError, _mapper?: any) {
const mapper: (issue: $ZodIssue) => any =
_mapper ||
function (issue: $ZodIssue) {
return issue.message;
};
export function formatError<T, U>(error: $ZodError<T>, mapper = (issue: $ZodIssue) => issue.message as U) {
const fieldErrors: $ZodFormattedError<T> = { _errors: [] } as any;
const processError = (error: { issues: $ZodIssue[] }) => {
for (const issue of error.issues) {
@@ -295,23 +290,20 @@ export function formatError<T>(error: $ZodError, _mapper?: any) {
return fieldErrors;
}
export type $ZodErrorTree<T, U = string> = T extends [any, ...any[]]
? { errors: U[]; items?: { [K in keyof T]?: $ZodErrorTree<T[K], U> } }
: T extends any[]
? { errors: U[]; items?: Array<$ZodErrorTree<T[number], U>> }
: T extends object
? { errors: U[]; properties?: { [K in keyof T]?: $ZodErrorTree<T[K], U> } }
: { errors: U[] };
export type $ZodErrorTree<T, U = string> = T extends util.Primitive
? { errors: U[] }
: T extends [any, ...any[]]
? { errors: U[]; items?: { [K in keyof T]?: $ZodErrorTree<T[K], U> } }
: T extends any[]
? { errors: U[]; items?: Array<$ZodErrorTree<T[number], U>> }
: T extends object
? { errors: U[]; properties?: { [K in keyof T]?: $ZodErrorTree<T[K], U> } }
: { errors: U[] };
export function treeifyError<T>(error: $ZodError<T>): $ZodErrorTree<T>;
export function treeifyError<T, U>(error: $ZodError<T>, mapper?: (issue: $ZodIssue) => U): $ZodErrorTree<T, U>;
export function treeifyError<T>(error: $ZodError, _mapper?: any) {
const mapper: (issue: $ZodIssue) => any =
_mapper ||
function (issue: $ZodIssue) {
return issue.message;
};
const result: $ZodErrorTree<T> = { errors: [] } as any;
export function treeifyError<T, U>(error: $ZodError<T>, mapper = (issue: $ZodIssue) => issue.message as U) {
const result: $ZodErrorTree<T, U> = { errors: [] } as any;
const processError = (error: { issues: $ZodIssue[] }, path: PropertyKey[] = []) => {
for (const issue of error.issues) {
if (issue.code === "invalid_union" && issue.errors.length) {
@@ -389,8 +381,9 @@ export function treeifyError<T>(error: $ZodError, _mapper?: any) {
* Invalid input: expected number
* ```
*/
export function toDotPath(path: (string | number | symbol)[]): string {
export function toDotPath(_path: readonly (string | number | symbol | StandardSchemaV1.PathSegment)[]): string {
const segs: string[] = [];
const path: PropertyKey[] = _path.map((seg: any) => (typeof seg === "object" ? seg.key : seg));
for (const seg of path) {
if (typeof seg === "number") segs.push(`[${seg}]`);
else if (typeof seg === "symbol") segs.push(`[${JSON.stringify(String(seg))}]`);
@@ -404,14 +397,10 @@ export function toDotPath(path: (string | number | symbol)[]): string {
return segs.join("");
}
interface BaseError {
issues: $ZodIssueBase[];
}
export function prettifyError(error: BaseError): string {
export function prettifyError(error: StandardSchemaV1.FailureResult): string {
const lines: string[] = [];
// sort by path length
const issues = [...error.issues].sort((a, b) => a.path.length - b.path.length);
const issues = [...error.issues].sort((a, b) => (a.path ?? []).length - (b.path ?? []).length);
// Process each issue
for (const issue of issues) {
-176
View File
@@ -1,176 +0,0 @@
import { _array, _tuple, _unknown } from "./api.js";
import type * as core from "./core.js";
import { parse, parseAsync } from "./parse.js";
import * as schemas from "./schemas.js";
import { $ZodTuple } from "./schemas.js";
import type * as util from "./util.js";
//////////////////////////////////////////
//////////////////////////////////////////
////////// //////////
////////// $ZodFunction //////////
////////// //////////
//////////////////////////////////////////
//////////////////////////////////////////
export interface $ZodFunctionDef<
In extends $ZodFunctionIn = $ZodFunctionIn,
Out extends $ZodFunctionOut = $ZodFunctionOut,
> {
type: "function";
input: In;
output: Out;
}
export type $ZodFunctionArgs = schemas.$ZodType<unknown[], unknown[]>;
export type $ZodFunctionIn = $ZodFunctionArgs;
export type $ZodFunctionOut = schemas.$ZodType;
export type $InferInnerFunctionType<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
...args: $ZodFunctionIn extends Args ? never[] : core.output<Args>
) => core.input<Returns>;
export type $InferInnerFunctionTypeAsync<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
...args: $ZodFunctionIn extends Args ? never[] : core.output<Args>
) => util.MaybeAsync<core.input<Returns>>;
export type $InferOuterFunctionType<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
...args: $ZodFunctionIn extends Args ? never[] : core.input<Args>
) => core.output<Returns>;
export type $InferOuterFunctionTypeAsync<Args extends $ZodFunctionIn, Returns extends $ZodFunctionOut> = (
...args: $ZodFunctionIn extends Args ? never[] : core.input<Args>
) => util.MaybeAsync<core.output<Returns>>;
export class $ZodFunction<
Args extends $ZodFunctionIn = $ZodFunctionIn,
Returns extends $ZodFunctionOut = $ZodFunctionOut,
> {
def: $ZodFunctionDef<Args, Returns>;
/** @deprecated */
_def!: $ZodFunctionDef<Args, Returns>;
_input!: $InferInnerFunctionType<Args, Returns>;
_output!: $InferOuterFunctionType<Args, Returns>;
constructor(def: $ZodFunctionDef<Args, Returns>) {
this._def = def;
this.def = def;
}
implement<F extends $InferInnerFunctionType<Args, Returns>>(
func: F
): // allow for return type inference
(
...args: Parameters<this["_output"]>
) => ReturnType<F> extends ReturnType<this["_output"]> ? ReturnType<F> : ReturnType<this["_output"]> {
if (typeof func !== "function") {
throw new Error("implement() must be called with a function");
}
const impl = ((...args: any[]) => {
const parsedArgs = this._def.input ? parse(this._def.input, args, undefined, { callee: impl }) : args;
if (!Array.isArray(parsedArgs)) {
throw new Error("Invalid arguments schema: not an array or tuple schema.");
}
const output = func(...(parsedArgs as any));
return this._def.output ? parse(this._def.output, output, undefined, { callee: impl }) : output;
}) as any;
return impl;
}
implementAsync<F extends $InferInnerFunctionTypeAsync<Args, Returns>>(
func: F
): F extends $InferOuterFunctionTypeAsync<Args, Returns> ? F : $InferOuterFunctionTypeAsync<Args, Returns> {
if (typeof func !== "function") {
throw new Error("implement() must be called with a function");
}
const impl = (async (...args: any[]) => {
const parsedArgs = this._def.input ? await parseAsync(this._def.input, args, undefined, { callee: impl }) : args;
if (!Array.isArray(parsedArgs)) {
throw new Error("Invalid arguments schema: not an array or tuple schema.");
}
const output = await func(...(parsedArgs as any));
return this._def.output ? parseAsync(this._def.output, output, undefined, { callee: impl }) : output;
}) as any;
return impl;
}
input<const Items extends util.TupleItems, const Rest extends $ZodFunctionOut = $ZodFunctionOut>(
args: Items,
rest?: Rest
): $ZodFunction<schemas.$ZodTuple<Items, Rest>, Returns>;
input<NewArgs extends $ZodFunctionIn>(args: NewArgs): $ZodFunction<NewArgs, Returns>;
input(...args: any[]): $ZodFunction<any, Returns> {
const F: any = this.constructor;
if (Array.isArray(args[0])) {
return new F({
type: "function",
input: new $ZodTuple({
type: "tuple",
items: args[0],
rest: args[1],
}),
output: this._def.output,
});
}
return new F({
type: "function",
input: args[0],
output: this._def.output,
});
}
output<NewReturns extends schemas.$ZodType>(output: NewReturns): $ZodFunction<Args, NewReturns> {
const F: any = this.constructor;
return new F({
type: "function",
input: this._def.input,
output,
});
}
}
export interface $ZodFunctionParams<I extends $ZodFunctionIn, O extends schemas.$ZodType> {
input?: I;
output?: O;
}
function _function(): $ZodFunction;
function _function<const In extends Array<schemas.$ZodType> = Array<schemas.$ZodType>>(params: {
input: In;
}): $ZodFunction<$ZodTuple<In, null>, $ZodFunctionOut>;
function _function<
const In extends Array<schemas.$ZodType> = Array<schemas.$ZodType>,
const Out extends $ZodFunctionOut = $ZodFunctionOut,
>(params: {
input: In;
output: Out;
}): $ZodFunction<$ZodTuple<In, null>, Out>;
function _function<const In extends $ZodFunctionIn = $ZodFunctionIn>(params: {
input: In;
}): $ZodFunction<In, $ZodFunctionOut>;
function _function<const Out extends $ZodFunctionOut = $ZodFunctionOut>(params: {
output: Out;
}): $ZodFunction<$ZodFunctionIn, Out>;
function _function<
In extends $ZodFunctionIn = $ZodFunctionIn,
Out extends schemas.$ZodType = schemas.$ZodType,
>(params?: {
input: In;
output: Out;
}): $ZodFunction<In, Out>;
function _function(params?: {
output?: schemas.$ZodType;
input?: $ZodFunctionArgs | Array<schemas.$ZodType>;
}): any {
return new $ZodFunction({
type: "function",
input: Array.isArray(params?.input)
? _tuple(schemas.$ZodTuple, params?.input as any)
: (params?.input ?? _array(schemas.$ZodArray, _unknown(schemas.$ZodUnknown))),
output: params?.output ?? _unknown(schemas.$ZodUnknown),
});
}
export { _function as function };
-1
View File
@@ -9,7 +9,6 @@ export * as regexes from "./regexes.js";
export * as locales from "../locales/index.js";
export * from "./registries.js";
export * from "./doc.js";
export * from "./function.js";
export * from "./api.js";
export * from "./to-json-schema.js";
export * as JSONSchema from "./json-schema.js";
+7 -3
View File
@@ -45,7 +45,10 @@ export type Schema =
export type _JSONSchema = boolean | JSONSchema;
export type JSONSchema = {
[k: string]: unknown;
$schema?: "https://json-schema.org/draft/2020-12/schema" | "http://json-schema.org/draft-07/schema#";
$schema?:
| "https://json-schema.org/draft/2020-12/schema"
| "http://json-schema.org/draft-07/schema#"
| "http://json-schema.org/draft-04/schema#";
$id?: string;
$anchor?: string;
$ref?: string;
@@ -75,9 +78,9 @@ export type JSONSchema = {
not?: _JSONSchema;
multipleOf?: number;
maximum?: number;
exclusiveMaximum?: number;
exclusiveMaximum?: number | boolean;
minimum?: number;
exclusiveMinimum?: number;
exclusiveMinimum?: number | boolean;
maxLength?: number;
minLength?: number;
pattern?: string;
@@ -101,6 +104,7 @@ export type JSONSchema = {
deprecated?: boolean;
readOnly?: boolean;
writeOnly?: boolean;
nullable?: boolean;
examples?: unknown[];
format?: string;
contentMediaType?: string;
+101
View File
@@ -92,3 +92,104 @@ export const _safeParseAsync: (_Err: $ZodErrorClass) => $SafeParseAsync = (_Err)
};
export const safeParseAsync: $SafeParseAsync = /* @__PURE__*/ _safeParseAsync(errors.$ZodRealError);
// Codec functions
export type $Encode = <T extends schemas.$ZodType>(
schema: T,
value: core.output<T>,
_ctx?: schemas.ParseContext<errors.$ZodIssue>
) => core.input<T>;
export const _encode: (_Err: $ZodErrorClass) => $Encode = (_Err) => (schema, value, _ctx) => {
const ctx = _ctx ? Object.assign(_ctx, { direction: "backward" as const }) : { direction: "backward" as const };
return _parse(_Err)(schema, value, ctx as any) as any;
};
export const encode: $Encode = /* @__PURE__*/ _encode(errors.$ZodRealError);
export type $Decode = <T extends schemas.$ZodType>(
schema: T,
value: core.input<T>,
_ctx?: schemas.ParseContext<errors.$ZodIssue>
) => core.output<T>;
export const _decode: (_Err: $ZodErrorClass) => $Decode = (_Err) => (schema, value, _ctx) => {
return _parse(_Err)(schema, value, _ctx);
};
export const decode: $Decode = /* @__PURE__*/ _decode(errors.$ZodRealError);
export type $EncodeAsync = <T extends schemas.$ZodType>(
schema: T,
value: core.output<T>,
_ctx?: schemas.ParseContext<errors.$ZodIssue>
) => Promise<core.input<T>>;
export const _encodeAsync: (_Err: $ZodErrorClass) => $EncodeAsync = (_Err) => async (schema, value, _ctx) => {
const ctx = _ctx ? Object.assign(_ctx, { direction: "backward" as const }) : { direction: "backward" as const };
return _parseAsync(_Err)(schema, value, ctx as any) as any;
};
export const encodeAsync: $EncodeAsync = /* @__PURE__*/ _encodeAsync(errors.$ZodRealError);
export type $DecodeAsync = <T extends schemas.$ZodType>(
schema: T,
value: core.input<T>,
_ctx?: schemas.ParseContext<errors.$ZodIssue>
) => Promise<core.output<T>>;
export const _decodeAsync: (_Err: $ZodErrorClass) => $DecodeAsync = (_Err) => async (schema, value, _ctx) => {
return _parseAsync(_Err)(schema, value, _ctx);
};
export const decodeAsync: $DecodeAsync = /* @__PURE__*/ _decodeAsync(errors.$ZodRealError);
export type $SafeEncode = <T extends schemas.$ZodType>(
schema: T,
value: core.output<T>,
_ctx?: schemas.ParseContext<errors.$ZodIssue>
) => util.SafeParseResult<core.input<T>>;
export const _safeEncode: (_Err: $ZodErrorClass) => $SafeEncode = (_Err) => (schema, value, _ctx) => {
const ctx = _ctx ? Object.assign(_ctx, { direction: "backward" as const }) : { direction: "backward" as const };
return _safeParse(_Err)(schema, value, ctx as any) as any;
};
export const safeEncode: $SafeEncode = /* @__PURE__*/ _safeEncode(errors.$ZodRealError);
export type $SafeDecode = <T extends schemas.$ZodType>(
schema: T,
value: core.input<T>,
_ctx?: schemas.ParseContext<errors.$ZodIssue>
) => util.SafeParseResult<core.output<T>>;
export const _safeDecode: (_Err: $ZodErrorClass) => $SafeDecode = (_Err) => (schema, value, _ctx) => {
return _safeParse(_Err)(schema, value, _ctx);
};
export const safeDecode: $SafeDecode = /* @__PURE__*/ _safeDecode(errors.$ZodRealError);
export type $SafeEncodeAsync = <T extends schemas.$ZodType>(
schema: T,
value: core.output<T>,
_ctx?: schemas.ParseContext<errors.$ZodIssue>
) => Promise<util.SafeParseResult<core.input<T>>>;
export const _safeEncodeAsync: (_Err: $ZodErrorClass) => $SafeEncodeAsync = (_Err) => async (schema, value, _ctx) => {
const ctx = _ctx ? Object.assign(_ctx, { direction: "backward" as const }) : { direction: "backward" as const };
return _safeParseAsync(_Err)(schema, value, ctx as any) as any;
};
export const safeEncodeAsync: $SafeEncodeAsync = /* @__PURE__*/ _safeEncodeAsync(errors.$ZodRealError);
export type $SafeDecodeAsync = <T extends schemas.$ZodType>(
schema: T,
value: core.input<T>,
_ctx?: schemas.ParseContext<errors.$ZodIssue>
) => Promise<util.SafeParseResult<core.output<T>>>;
export const _safeDecodeAsync: (_Err: $ZodErrorClass) => $SafeDecodeAsync = (_Err) => async (schema, value, _ctx) => {
return _safeParseAsync(_Err)(schema, value, _ctx);
};
export const safeDecodeAsync: $SafeDecodeAsync = /* @__PURE__*/ _safeDecodeAsync(errors.$ZodRealError);
+62 -15
View File
@@ -1,3 +1,5 @@
import * as util from "./util.js";
export const cuid: RegExp = /^[cC][^\s-]{8,}$/;
export const cuid2: RegExp = /^[0-9a-z]+$/;
export const ulid: RegExp = /^[0-9A-HJKMNP-TV-Za-hjkmnp-tv-z]{26}$/;
@@ -16,12 +18,12 @@ export const extendedDuration: RegExp =
/** A regex for any UUID-like identifier: 8-4-4-4-12 hex pattern */
export const guid: RegExp = /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12})$/;
/** Returns a regex for validating an RFC 4122 UUID.
/** Returns a regex for validating an RFC 9562/4122 UUID.
*
* @param version Optionally specify a version 1-8. If no version is specified, all versions are supported. */
export const uuid = (version?: number | undefined): RegExp => {
if (!version)
return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000)$/;
return /^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[1-8][0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12}|00000000-0000-0000-0000-000000000000|ffffffff-ffff-ffff-ffff-ffffffffffff)$/;
return new RegExp(
`^([0-9a-fA-F]{8}-[0-9a-fA-F]{4}-${version}[0-9a-fA-F]{3}-[89abAB][0-9a-fA-F]{3}-[0-9a-fA-F]{12})$`
);
@@ -44,12 +46,13 @@ export const rfc5322Email =
/** A loose regex that allows Unicode characters, enforces length limits, and that's about it. */
export const unicodeEmail = /^[^\s@"]{1,64}@[^\s@]{1,255}$/u;
export const idnEmail = unicodeEmail;
export const browserEmail: RegExp =
/^[a-zA-Z0-9.!#$%&'*+/=?^_`{|}~-]+@[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?)*$/;
// from https://thekevinscott.com/emojis-in-javascript/#writing-a-regular-expression
export const _emoji = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;
const _emoji: string = `^(\\p{Extended_Pictographic}|\\p{Emoji_Component})+$`;
export function emoji(): RegExp {
return new RegExp(_emoji, "u");
}
@@ -57,8 +60,11 @@ export function emoji(): RegExp {
export const ipv4: RegExp =
/^(?:(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(?:25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])$/;
export const ipv6: RegExp =
/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|::|([0-9a-fA-F]{1,4})?::([0-9a-fA-F]{1,4}:?){0,6})$/;
/^(([0-9a-fA-F]{1,4}:){7}[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,7}:|([0-9a-fA-F]{1,4}:){1,6}:[0-9a-fA-F]{1,4}|([0-9a-fA-F]{1,4}:){1,5}(:[0-9a-fA-F]{1,4}){1,2}|([0-9a-fA-F]{1,4}:){1,4}(:[0-9a-fA-F]{1,4}){1,3}|([0-9a-fA-F]{1,4}:){1,3}(:[0-9a-fA-F]{1,4}){1,4}|([0-9a-fA-F]{1,4}:){1,2}(:[0-9a-fA-F]{1,4}){1,5}|[0-9a-fA-F]{1,4}:((:[0-9a-fA-F]{1,4}){1,6})|:((:[0-9a-fA-F]{1,4}){1,7}|:))$/;
export const mac = (delimiter?: string): RegExp => {
const escapedDelim = util.escapeRegex(delimiter ?? ":");
return new RegExp(`^(?:[0-9A-F]{2}${escapedDelim}){5}[0-9A-F]{2}$|^(?:[0-9a-f]{2}${escapedDelim}){5}[0-9a-f]{2}$`);
};
export const cidrv4: RegExp =
/^((25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\.){3}(25[0-5]|2[0-4][0-9]|1[0-9][0-9]|[1-9][0-9]|[0-9])\/([0-9]|[1-2][0-9]|3[0-2])$/;
export const cidrv6: RegExp =
@@ -69,9 +75,10 @@ export const base64: RegExp = /^$|^(?:[0-9a-zA-Z+/]{4})*(?:(?:[0-9a-zA-Z+/]{2}==
export const base64url: RegExp = /^[A-Za-z0-9_-]*$/;
// based on https://stackoverflow.com/questions/106179/regular-expression-to-match-dns-hostname-or-ip-address
// export const hostname: RegExp =
// /^(([a-zA-Z0-9]|[a-zA-Z0-9][a-zA-Z0-9\-]*[a-zA-Z0-9])\.)+([A-Za-z0-9]|[A-Za-z0-9][A-Za-z0-9\-]*[A-Za-z0-9])$/;
export const hostname: RegExp = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+$/;
// export const hostname: RegExp = /^([a-zA-Z0-9-]+\.)*[a-zA-Z0-9-]+$/;
export const hostname: RegExp =
/^(?=.{1,253}\.?$)[a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?(?:\.[a-zA-Z0-9](?:[-0-9a-zA-Z]{0,61}[0-9a-zA-Z])?)*\.?$/;
export const domain: RegExp = /^([a-zA-Z0-9](?:[a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?\.)+[a-zA-Z]{2,}$/;
// https://blog.stevenlevithan.com/archives/validate-phone-number#r4-3 (regex sans spaces)
@@ -109,7 +116,8 @@ export function datetime(args: {
const time = timeSource({ precision: args.precision });
const opts = ["Z"];
if (args.local) opts.push("");
if (args.offset) opts.push(`([+-]\\d{2}:\\d{2})`);
// if (args.offset) opts.push(`([+-]\\d{2}:\\d{2})`);
if (args.offset) opts.push(`([+-](?:[01]\\d|2[0-3]):[0-5]\\d)`);
const timeRegex = `${time}(?:${opts.join("|")})`;
return new RegExp(`^${dateSource}T(?:${timeRegex})$`);
@@ -120,16 +128,55 @@ export const string = (params?: { minimum?: number | undefined; maximum?: number
return new RegExp(`^${regex}$`);
};
export const bigint: RegExp = /^\d+n?$/;
export const integer: RegExp = /^\d+$/;
export const number: RegExp = /^-?\d+(?:\.\d+)?/i;
export const boolean: RegExp = /true|false/i;
const _null: RegExp = /null/i;
export const bigint: RegExp = /^-?\d+n?$/;
export const integer: RegExp = /^-?\d+$/;
export const number: RegExp = /^-?\d+(?:\.\d+)?/;
export const boolean: RegExp = /^(?:true|false)$/i;
const _null: RegExp = /^null$/i;
export { _null as null };
const _undefined: RegExp = /undefined/i;
const _undefined: RegExp = /^undefined$/i;
export { _undefined as undefined };
// regex for string with no uppercase letters
export const lowercase: RegExp = /^[^A-Z]*$/;
// regex for string with no lowercase letters
export const uppercase: RegExp = /^[^a-z]*$/;
// regex for hexadecimal strings (any length)
export const hex: RegExp = /^[0-9a-fA-F]*$/;
// Hash regexes for different algorithms and encodings
// Helper function to create base64 regex with exact length and padding
function fixedBase64(bodyLength: number, padding: "" | "=" | "=="): RegExp {
return new RegExp(`^[A-Za-z0-9+/]{${bodyLength}}${padding}$`);
}
// Helper function to create base64url regex with exact length (no padding)
function fixedBase64url(length: number): RegExp {
return new RegExp(`^[A-Za-z0-9_-]{${length}}$`);
}
// MD5 (16 bytes): base64 = 24 chars total (22 + "==")
export const md5_hex: RegExp = /^[0-9a-fA-F]{32}$/;
export const md5_base64: RegExp = /*@__PURE__*/ fixedBase64(22, "==");
export const md5_base64url: RegExp = /*@__PURE__*/ fixedBase64url(22);
// SHA1 (20 bytes): base64 = 28 chars total (27 + "=")
export const sha1_hex: RegExp = /^[0-9a-fA-F]{40}$/;
export const sha1_base64: RegExp = /*@__PURE__*/ fixedBase64(27, "=");
export const sha1_base64url: RegExp = /*@__PURE__*/ fixedBase64url(27);
// SHA256 (32 bytes): base64 = 44 chars total (43 + "=")
export const sha256_hex: RegExp = /^[0-9a-fA-F]{64}$/;
export const sha256_base64: RegExp = /*@__PURE__*/ fixedBase64(43, "=");
export const sha256_base64url: RegExp = /*@__PURE__*/ fixedBase64url(43);
// SHA384 (48 bytes): base64 = 64 chars total (no padding)
export const sha384_hex: RegExp = /^[0-9a-fA-F]{96}$/;
export const sha384_base64: RegExp = /*@__PURE__*/ fixedBase64(64, "");
export const sha384_base64url: RegExp = /*@__PURE__*/ fixedBase64url(64);
// SHA512 (64 bytes): base64 = 88 chars total (86 + "==")
export const sha512_hex: RegExp = /^[0-9a-fA-F]{128}$/;
export const sha512_base64: RegExp = /*@__PURE__*/ fixedBase64(86, "==");
export const sha512_base64url: RegExp = /*@__PURE__*/ fixedBase64url(86);
+17 -5
View File
@@ -23,11 +23,11 @@ export type $replace<Meta, S extends $ZodType> = Meta extends $output
? { [K in keyof Meta]: $replace<Meta[K], S> }
: Meta;
type MetadataType = Record<string, unknown> | undefined;
type MetadataType = object | undefined;
export class $ZodRegistry<Meta extends MetadataType = MetadataType, Schema extends $ZodType = $ZodType> {
_meta!: Meta;
_schema!: Schema;
_map: Map<Schema, $replace<Meta, Schema>> = new Map();
_map: WeakMap<Schema, $replace<Meta, Schema>> = new WeakMap();
_idmap: Map<string, Schema> = new Map();
add<S extends Schema>(
@@ -46,7 +46,7 @@ export class $ZodRegistry<Meta extends MetadataType = MetadataType, Schema exten
}
clear(): this {
this._map = new Map();
this._map = new WeakMap();
this._idmap = new Map();
return this;
}
@@ -68,7 +68,8 @@ export class $ZodRegistry<Meta extends MetadataType = MetadataType, Schema exten
if (p) {
const pm: any = { ...(this.get(p) ?? {}) };
delete pm.id; // do not inherit id
return { ...pm, ...this._map.get(schema) } as any;
const f = { ...pm, ...this._map.get(schema) } as any;
return Object.keys(f).length ? f : undefined;
}
return this._map.get(schema) as any;
}
@@ -93,4 +94,15 @@ export function registry<T extends MetadataType = MetadataType, S extends $ZodTy
return new $ZodRegistry<T, S>();
}
export const globalRegistry: $ZodRegistry<GlobalMeta> = /*@__PURE__*/ registry<GlobalMeta>();
interface GlobalThisWithRegistry {
/**
* The globalRegistry instance shared across both CommonJS and ESM builds.
* By attaching the registry to `globalThis`, this property ensures a single, deduplicated instance
* is used regardless of whether the package is loaded via `require` (CJS) or `import` (ESM).
* This prevents dual package hazards and keeps registry state consistent.
*/
__zod_globalRegistry?: $ZodRegistry<GlobalMeta>;
}
(globalThis as GlobalThisWithRegistry).__zod_globalRegistry ??= registry<GlobalMeta>();
export const globalRegistry: $ZodRegistry<GlobalMeta> = (globalThis as GlobalThisWithRegistry).__zod_globalRegistry!;
+754 -262
View File
File diff suppressed because it is too large Load Diff
+153 -122
View File
@@ -10,8 +10,10 @@ interface JSONSchemaGeneratorParams {
metadata?: $ZodRegistry<Record<string, any>>;
/** The JSON Schema version to target.
* - `"draft-2020-12"` Default. JSON Schema Draft 2020-12
* - `"draft-7"` JSON Schema Draft 7 */
target?: "draft-7" | "draft-2020-12";
* - `"draft-7"` JSON Schema Draft 7
* - `"draft-4"` JSON Schema Draft 4
* - `"openapi-3.0"` OpenAPI 3.0 Schema Object */
target?: "draft-4" | "draft-7" | "draft-2020-12" | "openapi-3.0";
/** How to handle unrepresentable types.
* - `"throw"` Default. Unrepresentable types throw an error
* - `"any"` Unrepresentable types become `{}` */
@@ -71,7 +73,7 @@ interface Seen {
export class JSONSchemaGenerator {
metadataRegistry: $ZodRegistry<Record<string, any>>;
target: "draft-7" | "draft-2020-12";
target: "draft-4" | "draft-7" | "draft-2020-12" | "openapi-3.0";
unrepresentable: "throw" | "any";
override: (ctx: {
zodSchema: schemas.$ZodTypes;
@@ -163,7 +165,9 @@ export class JSONSchemaGenerator {
else if (regexes.length > 1) {
result.schema.allOf = [
...regexes.map((regex) => ({
...(this.target === "draft-7" ? ({ type: "string" } as const) : {}),
...(this.target === "draft-7" || this.target === "draft-4" || this.target === "openapi-3.0"
? ({ type: "string" } as const)
: {}),
pattern: regex.source,
})),
];
@@ -178,19 +182,33 @@ export class JSONSchemaGenerator {
if (typeof format === "string" && format.includes("int")) json.type = "integer";
else json.type = "number";
if (typeof exclusiveMinimum === "number") json.exclusiveMinimum = exclusiveMinimum;
if (typeof exclusiveMinimum === "number") {
if (this.target === "draft-4" || this.target === "openapi-3.0") {
json.minimum = exclusiveMinimum;
json.exclusiveMinimum = true;
} else {
json.exclusiveMinimum = exclusiveMinimum;
}
}
if (typeof minimum === "number") {
json.minimum = minimum;
if (typeof exclusiveMinimum === "number") {
if (typeof exclusiveMinimum === "number" && this.target !== "draft-4") {
if (exclusiveMinimum >= minimum) delete json.minimum;
else delete json.exclusiveMinimum;
}
}
if (typeof exclusiveMaximum === "number") json.exclusiveMaximum = exclusiveMaximum;
if (typeof exclusiveMaximum === "number") {
if (this.target === "draft-4" || this.target === "openapi-3.0") {
json.maximum = exclusiveMaximum;
json.exclusiveMaximum = true;
} else {
json.exclusiveMaximum = exclusiveMaximum;
}
}
if (typeof maximum === "number") {
json.maximum = maximum;
if (typeof exclusiveMaximum === "number") {
if (typeof exclusiveMaximum === "number" && this.target !== "draft-4") {
if (exclusiveMaximum <= maximum) delete json.maximum;
else delete json.exclusiveMaximum;
}
@@ -218,7 +236,11 @@ export class JSONSchemaGenerator {
break;
}
case "null": {
_json.type = "null";
if (this.target === "openapi-3.0") {
_json.type = "string";
_json.nullable = true;
_json.enum = [null];
} else _json.type = "null";
break;
}
case "any": {
@@ -308,12 +330,20 @@ export class JSONSchemaGenerator {
}
case "union": {
const json: JSONSchema.BaseSchema = _json as any;
json.anyOf = def.options.map((x, i) =>
// Discriminated unions use oneOf (exactly one match) instead of anyOf (one or more matches)
// because the discriminator field ensures mutual exclusivity between options in JSON Schema
const isDiscriminated = (def as any).discriminator !== undefined;
const options = def.options.map((x, i) =>
this.process(x, {
...params,
path: [...params.path, "anyOf", i],
path: [...params.path, isDiscriminated ? "oneOf" : "anyOf", i],
})
);
if (isDiscriminated) {
json.oneOf = options;
} else {
json.anyOf = options;
}
break;
}
case "intersection": {
@@ -338,35 +368,48 @@ export class JSONSchemaGenerator {
case "tuple": {
const json: JSONSchema.ArraySchema = _json as any;
json.type = "array";
const prefixPath = this.target === "draft-2020-12" ? "prefixItems" : "items";
const restPath =
this.target === "draft-2020-12" ? "items" : this.target === "openapi-3.0" ? "items" : "additionalItems";
const prefixItems = def.items.map((x, i) =>
this.process(x, { ...params, path: [...params.path, "prefixItems", i] })
this.process(x, {
...params,
path: [...params.path, prefixPath, i],
})
);
const rest = def.rest
? this.process(def.rest, {
...params,
path: [...params.path, restPath, ...(this.target === "openapi-3.0" ? [def.items.length] : [])],
})
: null;
if (this.target === "draft-2020-12") {
json.prefixItems = prefixItems;
if (rest) {
json.items = rest;
}
} else if (this.target === "openapi-3.0") {
json.items = {
anyOf: prefixItems,
};
if (rest) {
json.items.anyOf!.push(rest);
}
json.minItems = prefixItems.length;
if (!rest) {
json.maxItems = prefixItems.length;
}
} else {
json.items = prefixItems;
}
if (def.rest) {
const rest = this.process(def.rest, {
...params,
path: [...params.path, "items"],
});
if (this.target === "draft-2020-12") {
json.items = rest;
} else {
if (rest) {
json.additionalItems = rest;
}
}
// additionalItems
if (def.rest) {
json.items = this.process(def.rest, {
...params,
path: [...params.path, "items"],
});
}
// length
const { minimum, maximum } = schema._zod.bag as {
minimum?: number;
@@ -379,7 +422,12 @@ export class JSONSchemaGenerator {
case "record": {
const json: JSONSchema.ObjectSchema = _json as any;
json.type = "object";
json.propertyNames = this.process(def.keyType, { ...params, path: [...params.path, "propertyNames"] });
if (this.target === "draft-7" || this.target === "draft-2020-12") {
json.propertyNames = this.process(def.keyType, {
...params,
path: [...params.path, "propertyNames"],
});
}
json.additionalProperties = this.process(def.valueType, {
...params,
path: [...params.path, "additionalProperties"],
@@ -432,7 +480,11 @@ export class JSONSchemaGenerator {
} else if (vals.length === 1) {
const val = vals[0]!;
json.type = val === null ? ("null" as const) : (typeof val as any);
json.const = val;
if (this.target === "draft-4" || this.target === "openapi-3.0") {
json.enum = [val];
} else {
json.const = val;
}
} else {
if (vals.every((v) => typeof v === "number")) json.type = "number";
if (vals.every((v) => typeof v === "string")) json.type = "string";
@@ -482,7 +534,12 @@ export class JSONSchemaGenerator {
case "nullable": {
const inner = this.process(def.innerType, params);
_json.anyOf = [inner, { type: "null" }];
if (this.target === "openapi-3.0") {
result.ref = def.innerType;
_json.nullable = true;
} else {
_json.anyOf = [inner, { type: "null" }];
}
break;
}
case "nonoptional": {
@@ -570,6 +627,12 @@ export class JSONSchemaGenerator {
}
break;
}
case "function": {
if (this.unrepresentable === "throw") {
throw new Error("Function types cannot be represented in JSON Schema");
}
break;
}
default: {
def satisfies never;
}
@@ -749,7 +812,10 @@ export class JSONSchemaGenerator {
// merge referenced schema into current
const refSchema = this.seen.get(ref)!.schema;
if (refSchema.$ref && params.target === "draft-7") {
if (
refSchema.$ref &&
(params.target === "draft-7" || params.target === "draft-4" || params.target === "openapi-3.0")
) {
schema.allOf = schema.allOf ?? [];
schema.allOf.push(refSchema);
} else {
@@ -776,7 +842,12 @@ export class JSONSchemaGenerator {
result.$schema = "https://json-schema.org/draft/2020-12/schema";
} else if (this.target === "draft-7") {
result.$schema = "http://json-schema.org/draft-07/schema#";
} else if (this.target === "draft-4") {
result.$schema = "http://json-schema.org/draft-04/schema#";
} else if (this.target === "openapi-3.0") {
// OpenAPI 3.0 schema objects should not include a $schema property
} else {
// @ts-ignore
console.warn(`Invalid target: ${this.target}`);
}
@@ -883,95 +954,55 @@ function isTransforming(
if (ctx.seen.has(_schema)) return false;
ctx.seen.add(_schema);
const schema = _schema as schemas.$ZodTypes;
const def = schema._zod.def;
switch (def.type) {
case "string":
case "number":
case "bigint":
case "boolean":
case "date":
case "symbol":
case "undefined":
case "null":
case "any":
case "unknown":
case "never":
case "void":
case "literal":
case "enum":
case "nan":
case "file":
case "template_literal":
return false;
case "array": {
return isTransforming(def.element, ctx);
}
case "object": {
for (const key in def.shape) {
if (isTransforming(def.shape[key]!, ctx)) return true;
}
return false;
}
case "union": {
for (const option of def.options) {
if (isTransforming(option, ctx)) return true;
}
return false;
}
case "intersection": {
return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);
}
case "tuple": {
for (const item of def.items) {
if (isTransforming(item, ctx)) return true;
}
if (def.rest && isTransforming(def.rest, ctx)) return true;
return false;
}
case "record": {
return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
}
case "map": {
return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
}
case "set": {
return isTransforming(def.valueType, ctx);
}
const def = (_schema as schemas.$ZodTypes)._zod.def;
// inner types
case "promise":
case "optional":
case "nonoptional":
case "nullable":
case "readonly":
return isTransforming(def.innerType, ctx);
case "lazy":
return isTransforming(def.getter(), ctx);
case "default": {
return isTransforming(def.innerType, ctx);
}
case "prefault": {
return isTransforming(def.innerType, ctx);
}
case "custom": {
return false;
}
case "transform": {
return true;
}
case "pipe": {
return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
}
case "success": {
return false;
}
case "catch": {
return false;
}
if (def.type === "transform") return true;
default:
def satisfies never;
if (def.type === "array") return isTransforming(def.element, ctx);
if (def.type === "set") return isTransforming(def.valueType, ctx);
if (def.type === "lazy") return isTransforming(def.getter(), ctx);
if (
def.type === "promise" ||
def.type === "optional" ||
def.type === "nonoptional" ||
def.type === "nullable" ||
def.type === "readonly" ||
def.type === "default" ||
def.type === "prefault"
) {
return isTransforming(def.innerType, ctx);
}
throw new Error(`Unknown schema type: ${(def as any).type}`);
if (def.type === "intersection") {
return isTransforming(def.left, ctx) || isTransforming(def.right, ctx);
}
if (def.type === "record" || def.type === "map") {
return isTransforming(def.keyType, ctx) || isTransforming(def.valueType, ctx);
}
if (def.type === "pipe") {
return isTransforming(def.in, ctx) || isTransforming(def.out, ctx);
}
if (def.type === "object") {
for (const key in def.shape) {
if (isTransforming(def.shape[key]!, ctx)) return true;
}
return false;
}
if (def.type === "union") {
for (const option of def.options) {
if (isTransforming(option, ctx)) return true;
}
return false;
}
if (def.type === "tuple") {
for (const item of def.items) {
if (isTransforming(item, ctx)) return true;
}
if (def.rest && isTransforming(def.rest, ctx)) return true;
return false;
}
return false;
}
+246 -100
View File
@@ -20,6 +20,10 @@ export type JWTAlgorithm =
| "PS512"
| "EdDSA"
| (string & {});
export type HashAlgorithm = "md5" | "sha1" | "sha256" | "sha384" | "sha512";
export type HashEncoding = "hex" | "base64" | "base64url";
export type HashFormat = `${HashAlgorithm}_${HashEncoding}`;
export type IPVersion = "v4" | "v6";
export type MimeTypes =
| "application/json"
@@ -123,6 +127,7 @@ export type Identity<T> = T;
export type Flatten<T> = Identity<{ [k in keyof T]: T[k] }>;
export type Mapped<T> = { [k in keyof T]: T[k] };
export type Prettify<T> = {
// @ts-ignore
[K in keyof T]: T[K];
} & {};
@@ -241,23 +246,36 @@ export function cleanRegex(source: string): string {
export function floatSafeRemainder(val: number, step: number): number {
const valDecCount = (val.toString().split(".")[1] || "").length;
const stepDecCount = (step.toString().split(".")[1] || "").length;
const stepString = step.toString();
let stepDecCount = (stepString.split(".")[1] || "").length;
if (stepDecCount === 0 && /\d?e-\d?/.test(stepString)) {
const match = stepString.match(/\d?e-(\d?)/);
if (match?.[1]) {
stepDecCount = Number.parseInt(match[1]);
}
}
const decCount = valDecCount > stepDecCount ? valDecCount : stepDecCount;
const valInt = Number.parseInt(val.toFixed(decCount).replace(".", ""));
const stepInt = Number.parseInt(step.toFixed(decCount).replace(".", ""));
return (valInt % stepInt) / 10 ** decCount;
}
const EVALUATING = Symbol("evaluating");
export function defineLazy<T, K extends keyof T>(object: T, key: K, getter: () => T[K]): void {
const set = false;
let value: T[K] | typeof EVALUATING | undefined = undefined;
Object.defineProperty(object, key, {
get() {
if (!set) {
const value = getter();
object[key] = value;
return value;
if (value === EVALUATING) {
// Circular reference detected, return undefined to break the cycle
return undefined as T[K];
}
throw new Error("cached value already set");
if (value === undefined) {
value = EVALUATING;
value = getter();
}
return value;
},
set(v) {
Object.defineProperty(object, key, {
@@ -270,6 +288,10 @@ export function defineLazy<T, K extends keyof T>(object: T, key: K, getter: () =
});
}
export function objectClone(obj: object) {
return Object.create(Object.getPrototypeOf(obj), Object.getOwnPropertyDescriptors(obj));
}
export function assignProp<T extends object, K extends PropertyKey>(
target: T,
prop: K,
@@ -283,6 +305,21 @@ export function assignProp<T extends object, K extends PropertyKey>(
});
}
export function mergeDefs(...defs: Record<string, any>[]): any {
const mergedDescriptors: Record<string, PropertyDescriptor> = {};
for (const def of defs) {
const descriptors = Object.getOwnPropertyDescriptors(def);
Object.assign(mergedDescriptors, descriptors);
}
return Object.defineProperties({}, mergedDescriptors);
}
export function cloneDef(schema: schemas.$ZodType): any {
return mergeDefs(schema._zod.def);
}
export function getElementAtPath(obj: any, path: (string | number)[] | null | undefined): any {
if (!path) return obj;
return path.reduce((acc, key) => acc?.[key], obj);
@@ -314,15 +351,25 @@ export function esc(str: string): string {
return JSON.stringify(str);
}
export const captureStackTrace: (targetObject: object, constructorOpt?: Function) => void = Error.captureStackTrace
? Error.captureStackTrace
: (..._args) => {};
export function slugify(input: string): string {
return input
.toLowerCase()
.trim()
.replace(/[^\w\s-]/g, "")
.replace(/[\s_-]+/g, "-")
.replace(/^-+|-+$/g, "");
}
export const captureStackTrace: (targetObject: object, constructorOpt?: Function) => void = (
"captureStackTrace" in Error ? Error.captureStackTrace : (..._args: any[]) => {}
) as any;
export function isObject(data: any): data is Record<PropertyKey, unknown> {
return typeof data === "object" && data !== null && !Array.isArray(data);
}
export const allowsEval: { value: boolean } = cached(() => {
// @ts-ignore
if (typeof navigator !== "undefined" && navigator?.userAgent?.includes("Cloudflare")) {
return false;
}
@@ -343,6 +390,8 @@ export function isPlainObject(o: any): o is Record<PropertyKey, unknown> {
const ctor = o.constructor;
if (ctor === undefined) return true;
if (typeof ctor !== "function") return true;
// modified prototype
const prot = ctor.prototype;
if (isObject(prot) === false) return false;
@@ -355,6 +404,12 @@ export function isPlainObject(o: any): o is Record<PropertyKey, unknown> {
return true;
}
export function shallowClone(o: any): any {
if (isPlainObject(o)) return { ...o };
if (Array.isArray(o)) return [...o];
return o;
}
export function numKeys(data: any): number {
let keyCount = 0;
for (const key in data) {
@@ -409,6 +464,7 @@ export const getParsedType = (data: any): ParsedTypes => {
if (typeof Date !== "undefined" && data instanceof Date) {
return "date";
}
// @ts-ignore
if (typeof File !== "undefined" && data instanceof File) {
return "file";
}
@@ -535,48 +591,77 @@ export const BIGINT_FORMAT_RANGES: Record<checks.$ZodBigIntFormats, [bigint, big
};
export function pick(schema: schemas.$ZodObject, mask: Record<string, unknown>): any {
const newShape: Writeable<schemas.$ZodShape> = {};
const currDef = schema._zod.def; //.shape;
const currDef = schema._zod.def;
for (const key in mask) {
if (!(key in currDef.shape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!mask[key]) continue;
const def = mergeDefs(schema._zod.def, {
get shape() {
const newShape: Writeable<schemas.$ZodShape> = {};
for (const key in mask) {
if (!(key in currDef.shape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!mask[key]) continue;
newShape[key] = currDef.shape[key]!;
}
// pick key
newShape[key] = currDef.shape[key]!;
}
return clone(schema, {
...schema._zod.def,
shape: newShape,
assignProp(this, "shape", newShape); // self-caching
return newShape;
},
checks: [],
}) as any;
});
return clone(schema, def) as any;
}
export function omit(schema: schemas.$ZodObject, mask: object): any {
const newShape: Writeable<schemas.$ZodShape> = { ...schema._zod.def.shape };
const currDef = schema._zod.def; //.shape;
for (const key in mask) {
if (!(key in currDef.shape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!(mask as any)[key]) continue;
const currDef = schema._zod.def;
delete newShape[key];
}
return clone(schema, {
...schema._zod.def,
shape: newShape,
const def = mergeDefs(schema._zod.def, {
get shape() {
const newShape: Writeable<schemas.$ZodShape> = { ...schema._zod.def.shape };
for (const key in mask) {
if (!(key in currDef.shape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!(mask as any)[key]) continue;
delete newShape[key];
}
assignProp(this, "shape", newShape); // self-caching
return newShape;
},
checks: [],
});
return clone(schema, def);
}
export function extend(schema: schemas.$ZodObject, shape: schemas.$ZodShape): any {
if (!isPlainObject(shape)) {
throw new Error("Invalid input to extend: expected a plain object");
}
const checks = schema._zod.def.checks;
const hasChecks = checks && checks.length > 0;
if (hasChecks) {
throw new Error("Object schemas containing refinements cannot be extended. Use `.safeExtend()` instead.");
}
const def = mergeDefs(schema._zod.def, {
get shape() {
const _shape = { ...schema._zod.def.shape, ...shape };
assignProp(this, "shape", _shape); // self-caching
return _shape;
},
checks: [],
});
return clone(schema, def) as any;
}
export function safeExtend(schema: schemas.$ZodObject, shape: schemas.$ZodShape): any {
if (!isPlainObject(shape)) {
throw new Error("Invalid input to safeExtend: expected a plain object");
}
const def = {
...schema._zod.def,
get shape() {
@@ -584,22 +669,25 @@ export function extend(schema: schemas.$ZodObject, shape: schemas.$ZodShape): an
assignProp(this, "shape", _shape); // self-caching
return _shape;
},
checks: [], // delete existing checks
checks: schema._zod.def.checks,
} as any;
return clone(schema, def) as any;
}
export function merge(a: schemas.$ZodObject, b: schemas.$ZodObject): any {
return clone(a, {
...a._zod.def,
const def = mergeDefs(a._zod.def, {
get shape() {
const _shape = { ...a._zod.def.shape, ...b._zod.def.shape };
assignProp(this, "shape", _shape); // self-caching
return _shape;
},
catchall: b._zod.def.catchall,
get catchall() {
return b._zod.def.catchall;
},
checks: [], // delete existing checks
}) as any;
});
return clone(a, def) as any;
}
export function partial(
@@ -607,40 +695,44 @@ export function partial(
schema: schemas.$ZodObject,
mask: object | undefined
): any {
const oldShape = schema._zod.def.shape;
const shape: Writeable<schemas.$ZodShape> = { ...oldShape };
const def = mergeDefs(schema._zod.def, {
get shape() {
const oldShape = schema._zod.def.shape;
const shape: Writeable<schemas.$ZodShape> = { ...oldShape };
if (mask) {
for (const key in mask) {
if (!(key in oldShape)) {
throw new Error(`Unrecognized key: "${key}"`);
if (mask) {
for (const key in mask) {
if (!(key in oldShape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!(mask as any)[key]) continue;
// if (oldShape[key]!._zod.optin === "optional") continue;
shape[key] = Class
? new Class({
type: "optional",
innerType: oldShape[key]!,
})
: oldShape[key]!;
}
} else {
for (const key in oldShape) {
// if (oldShape[key]!._zod.optin === "optional") continue;
shape[key] = Class
? new Class({
type: "optional",
innerType: oldShape[key]!,
})
: oldShape[key]!;
}
}
if (!(mask as any)[key]) continue;
// if (oldShape[key]!._zod.optin === "optional") continue;
shape[key] = Class
? new Class({
type: "optional",
innerType: oldShape[key]!,
})
: oldShape[key]!;
}
} else {
for (const key in oldShape) {
// if (oldShape[key]!._zod.optin === "optional") continue;
shape[key] = Class
? new Class({
type: "optional",
innerType: oldShape[key]!,
})
: oldShape[key]!;
}
}
return clone(schema, {
...schema._zod.def,
shape,
assignProp(this, "shape", shape); // self-caching
return shape;
},
checks: [],
}) as any;
});
return clone(schema, def) as any;
}
export function required(
@@ -648,44 +740,51 @@ export function required(
schema: schemas.$ZodObject,
mask: object | undefined
): any {
const oldShape = schema._zod.def.shape;
const shape: Writeable<schemas.$ZodShape> = { ...oldShape };
const def = mergeDefs(schema._zod.def, {
get shape() {
const oldShape = schema._zod.def.shape;
const shape: Writeable<schemas.$ZodShape> = { ...oldShape };
if (mask) {
for (const key in mask) {
if (!(key in shape)) {
throw new Error(`Unrecognized key: "${key}"`);
if (mask) {
for (const key in mask) {
if (!(key in shape)) {
throw new Error(`Unrecognized key: "${key}"`);
}
if (!(mask as any)[key]) continue;
// overwrite with non-optional
shape[key] = new Class({
type: "nonoptional",
innerType: oldShape[key]!,
});
}
} else {
for (const key in oldShape) {
// overwrite with non-optional
shape[key] = new Class({
type: "nonoptional",
innerType: oldShape[key]!,
});
}
}
if (!(mask as any)[key]) continue;
// overwrite with non-optional
shape[key] = new Class({
type: "nonoptional",
innerType: oldShape[key]!,
});
}
} else {
for (const key in oldShape) {
// overwrite with non-optional
shape[key] = new Class({
type: "nonoptional",
innerType: oldShape[key]!,
});
}
}
return clone(schema, {
...schema._zod.def,
shape,
// optional: [],
assignProp(this, "shape", shape); // self-caching
return shape;
},
checks: [],
}) as any;
});
return clone(schema, def) as any;
}
export type Constructor<T, Def extends any[] = any[]> = new (...args: Def) => T;
// invalid_type | too_big | too_small | invalid_format | not_multiple_of | unrecognized_keys | invalid_union | invalid_key | invalid_element | invalid_value | custom
export function aborted(x: schemas.ParsePayload, startIndex = 0): boolean {
if (x.aborted === true) return true;
for (let i = startIndex; i < x.issues.length; i++) {
if (x.issues[i]?.continue !== true) return true;
if (x.issues[i]?.continue !== true) {
return true;
}
}
return false;
}
@@ -733,6 +832,7 @@ export function finalizeIssue(
export function getSizableOrigin(input: any): "set" | "map" | "file" | "unknown" {
if (input instanceof Set) return "set";
if (input instanceof Map) return "map";
// @ts-ignore
if (input instanceof File) return "file";
return "unknown";
}
@@ -769,6 +869,52 @@ export function cleanEnum(obj: Record<string, EnumValue>): EnumValue[] {
.map((el) => el[1]);
}
// Codec utility functions
export function base64ToUint8Array(base64: string): InstanceType<typeof Uint8Array> {
const binaryString = atob(base64);
const bytes = new Uint8Array(binaryString.length);
for (let i = 0; i < binaryString.length; i++) {
bytes[i] = binaryString.charCodeAt(i);
}
return bytes;
}
export function uint8ArrayToBase64(bytes: Uint8Array): string {
let binaryString = "";
for (let i = 0; i < bytes.length; i++) {
binaryString += String.fromCharCode(bytes[i]);
}
return btoa(binaryString);
}
export function base64urlToUint8Array(base64url: string): InstanceType<typeof Uint8Array> {
const base64 = base64url.replace(/-/g, "+").replace(/_/g, "/");
const padding = "=".repeat((4 - (base64.length % 4)) % 4);
return base64ToUint8Array(base64 + padding);
}
export function uint8ArrayToBase64url(bytes: Uint8Array): string {
return uint8ArrayToBase64(bytes).replace(/\+/g, "-").replace(/\//g, "_").replace(/=/g, "");
}
export function hexToUint8Array(hex: string): InstanceType<typeof Uint8Array> {
const cleanHex = hex.replace(/^0x/, "");
if (cleanHex.length % 2 !== 0) {
throw new Error("Invalid hex string length");
}
const bytes = new Uint8Array(cleanHex.length / 2);
for (let i = 0; i < cleanHex.length; i += 2) {
bytes[i / 2] = Number.parseInt(cleanHex.slice(i, i + 2), 16);
}
return bytes;
}
export function uint8ArrayToHex(bytes: Uint8Array): string {
return Array.from(bytes)
.map((b) => b.toString(16).padStart(2, "0"))
.join("");
}
// instanceof
export abstract class Class {
constructor(..._args: any[]) {}
+2 -2
View File
@@ -1,5 +1,5 @@
export const version = {
major: 4,
minor: 0,
patch: 0 as number,
minor: 1,
patch: 13 as number,
} as const;
+1
View File
@@ -60,6 +60,7 @@ const error: () => errors.$ZodErrorMap = () => {
duration: "ISO duration",
ipv4: "IPv4 address",
ipv6: "IPv6 address",
mac: "MAC address",
cidrv4: "IPv4 range",
cidrv6: "IPv6 range",
base64: "base64-encoded string",
+44 -10
View File
@@ -10,27 +10,59 @@ const error: () => errors.$ZodErrorMap = () => {
set: { unit: "elementos", verb: "tener" },
};
const TypeNames: Record<string, string> = {
string: "texto",
number: "número",
boolean: "booleano",
array: "arreglo",
object: "objeto",
set: "conjunto",
file: "archivo",
date: "fecha",
bigint: "número grande",
symbol: "símbolo",
undefined: "indefinido",
null: "nulo",
function: "función",
map: "mapa",
record: "registro",
tuple: "tupla",
enum: "enumeración",
union: "unión",
literal: "literal",
promise: "promesa",
void: "vacío",
never: "nunca",
unknown: "desconocido",
any: "cualquiera",
};
function getSizing(origin: string): { unit: string; verb: string } | null {
return Sizable[origin] ?? null;
}
function getTypeName(type: string): string {
return TypeNames[type] ?? type;
}
const parsedType = (data: any): string => {
const t = typeof data;
switch (t) {
case "number": {
return Number.isNaN(data) ? "NaN" : "número";
return Number.isNaN(data) ? "NaN" : "number";
}
case "object": {
if (Array.isArray(data)) {
return "arreglo";
return "array";
}
if (data === null) {
return "nulo";
return "null";
}
if (Object.getPrototypeOf(data) !== Object.prototype) {
return data.constructor.name;
}
return "object";
}
}
return t;
@@ -72,7 +104,7 @@ const error: () => errors.$ZodErrorMap = () => {
return (issue) => {
switch (issue.code) {
case "invalid_type":
return `Entrada inválida: se esperaba ${issue.expected}, recibido ${parsedType(issue.input)}`;
return `Entrada inválida: se esperaba ${getTypeName(issue.expected)}, recibido ${getTypeName(parsedType(issue.input))}`;
// return `Entrada inválida: se esperaba ${issue.expected}, recibido ${util.getParsedType(issue.input)}`;
case "invalid_value":
if (issue.values.length === 1)
@@ -81,18 +113,20 @@ const error: () => errors.$ZodErrorMap = () => {
case "too_big": {
const adj = issue.inclusive ? "<=" : "<";
const sizing = getSizing(issue.origin);
const origin = getTypeName(issue.origin);
if (sizing)
return `Demasiado grande: se esperaba que ${issue.origin ?? "valor"} tuviera ${adj}${issue.maximum.toString()} ${sizing.unit ?? "elementos"}`;
return `Demasiado grande: se esperaba que ${issue.origin ?? "valor"} fuera ${adj}${issue.maximum.toString()}`;
return `Demasiado grande: se esperaba que ${origin ?? "valor"} tuviera ${adj}${issue.maximum.toString()} ${sizing.unit ?? "elementos"}`;
return `Demasiado grande: se esperaba que ${origin ?? "valor"} fuera ${adj}${issue.maximum.toString()}`;
}
case "too_small": {
const adj = issue.inclusive ? ">=" : ">";
const sizing = getSizing(issue.origin);
const origin = getTypeName(issue.origin);
if (sizing) {
return `Demasiado pequeño: se esperaba que ${issue.origin} tuviera ${adj}${issue.minimum.toString()} ${sizing.unit}`;
return `Demasiado pequeño: se esperaba que ${origin} tuviera ${adj}${issue.minimum.toString()} ${sizing.unit}`;
}
return `Demasiado pequeño: se esperaba que ${issue.origin} fuera ${adj}${issue.minimum.toString()}`;
return `Demasiado pequeño: se esperaba que ${origin} fuera ${adj}${issue.minimum.toString()}`;
}
case "invalid_format": {
const _issue = issue as errors.$ZodStringFormatIssues;
@@ -107,11 +141,11 @@ const error: () => errors.$ZodErrorMap = () => {
case "unrecognized_keys":
return `Llave${issue.keys.length > 1 ? "s" : ""} desconocida${issue.keys.length > 1 ? "s" : ""}: ${util.joinValues(issue.keys, ", ")}`;
case "invalid_key":
return `Llave inválida en ${issue.origin}`;
return `Llave inválida en ${getTypeName(issue.origin)}`;
case "invalid_union":
return "Entrada inválida";
case "invalid_element":
return `Valor inválido en ${issue.origin}`;
return `Valor inválido en ${getTypeName(issue.origin)}`;
default:
return `Entrada inválida`;
}
+206 -75
View File
@@ -3,115 +3,246 @@ import type * as errors from "../core/errors.js";
import * as util from "../core/util.js";
const error: () => errors.$ZodErrorMap = () => {
const Sizable: Record<string, { unit: string; verb: string }> = {
string: { unit: "אותיות", verb: "לכלול" },
file: { unit: "בייטים", verb: "לכלול" },
array: { unit: "פריטים", verb: "לכלול" },
set: { unit: "פריטים", verb: "לכלול" },
// Hebrew labels + grammatical gender
const TypeNames: Record<string, { label: string; gender: "m" | "f" }> = {
string: { label: "מחרוזת", gender: "f" },
number: { label: "מספר", gender: "m" },
boolean: { label: "ערך בוליאני", gender: "m" },
bigint: { label: "BigInt", gender: "m" },
date: { label: "תאריך", gender: "m" },
array: { label: "מערך", gender: "m" },
object: { label: "אובייקט", gender: "m" },
null: { label: "ערך ריק (null)", gender: "m" },
undefined: { label: "ערך לא מוגדר (undefined)", gender: "m" },
symbol: { label: "סימבול (Symbol)", gender: "m" },
function: { label: "פונקציה", gender: "f" },
map: { label: "מפה (Map)", gender: "f" },
set: { label: "קבוצה (Set)", gender: "f" },
file: { label: "קובץ", gender: "m" },
promise: { label: "Promise", gender: "m" },
NaN: { label: "NaN", gender: "m" },
unknown: { label: "ערך לא ידוע", gender: "m" },
value: { label: "ערך", gender: "m" },
};
function getSizing(origin: string): { unit: string; verb: string } | null {
return Sizable[origin] ?? null;
}
// Sizing units for size-related messages + localized origin labels
const Sizable: Record<string, { unit: string; shortLabel?: string; longLabel?: string }> = {
string: { unit: "תווים", shortLabel: "קצר", longLabel: "ארוך" },
file: { unit: "בייטים", shortLabel: "קטן", longLabel: "גדול" },
array: { unit: "פריטים", shortLabel: "קטן", longLabel: "גדול" },
set: { unit: "פריטים", shortLabel: "קטן", longLabel: "גדול" },
number: { unit: "", shortLabel: "קטן", longLabel: "גדול" }, // no unit
};
// Helpers — labels, articles, and verbs
const typeEntry = (t?: string | null) => (t ? TypeNames[t] : undefined);
const typeLabel = (t?: string | null): string => {
const e = typeEntry(t);
if (e) return e.label;
// fallback: show raw string if unknown
return t ?? TypeNames.unknown.label;
};
const withDefinite = (t?: string | null): string => `ה${typeLabel(t)}`;
const verbFor = (t?: string | null): string => {
const e = typeEntry(t);
const gender = e?.gender ?? "m";
return gender === "f" ? "צריכה להיות" : "צריך להיות";
};
const getSizing = (origin?: string | null) => {
if (!origin) return null;
return Sizable[origin] ?? null;
};
// Robust type parser for "received" — returns a key we understand or a constructor name
const parsedType = (data: any): string => {
const t = typeof data;
switch (t) {
case "number": {
case "number":
return Number.isNaN(data) ? "NaN" : "number";
}
case "object": {
if (Array.isArray(data)) {
return "array";
}
if (data === null) {
return "null";
}
if (Array.isArray(data)) return "array";
if (data === null) return "null";
if (Object.getPrototypeOf(data) !== Object.prototype && data.constructor) {
return data.constructor.name;
return data.constructor.name; // keep as-is (e.g., "Date")
}
return "object";
}
default:
return t;
}
return t;
};
const Nouns: {
[k in $ZodStringFormats | (string & {})]?: string;
[k in $ZodStringFormats]: { label: string; gender: "m" | "f" };
} = {
regex: "קלט",
email: "כתובת אימייל",
url: "כתובת רשת",
emoji: "אימוג'י",
uuid: "UUID",
uuidv4: "UUIDv4",
uuidv6: "UUIDv6",
nanoid: "nanoid",
guid: "GUID",
cuid: "cuid",
cuid2: "cuid2",
ulid: "ULID",
xid: "XID",
ksuid: "KSUID",
datetime: "תאריך וזמן ISO",
date: "תאריך ISO",
time: "זמן ISO",
duration: "משך זמן ISO",
ipv4: "כתובת IPv4",
ipv6: "כתובת IPv6",
cidrv4: "טווח IPv4",
cidrv6: "טווח IPv6",
base64: "מחרוזת בבסיס 64",
base64url: "מחרוזת בבסיס 64 לכתובות רשת",
json_string: "מחרוזת JSON",
e164: "מספר E.164",
jwt: "JWT",
template_literal: "קלט",
regex: { label: "קלט", gender: "m" },
email: { label: "כתובת אימייל", gender: "f" },
url: { label: "כתובת רשת", gender: "f" },
emoji: { label: "אימוג'י", gender: "m" },
uuid: { label: "UUID", gender: "m" },
nanoid: { label: "nanoid", gender: "m" },
guid: { label: "GUID", gender: "m" },
cuid: { label: "cuid", gender: "m" },
cuid2: { label: "cuid2", gender: "m" },
ulid: { label: "ULID", gender: "m" },
xid: { label: "XID", gender: "m" },
ksuid: { label: "KSUID", gender: "m" },
datetime: { label: "תאריך וזמן ISO", gender: "m" },
date: { label: "תאריך ISO", gender: "m" },
time: { label: "זמן ISO", gender: "m" },
duration: { label: "משך זמן ISO", gender: "m" },
ipv4: { label: "כתובת IPv4", gender: "f" },
ipv6: { label: "כתובת IPv6", gender: "f" },
cidrv4: { label: "טווח IPv4", gender: "m" },
cidrv6: { label: "טווח IPv6", gender: "m" },
base64: { label: "מחרוזת בבסיס 64", gender: "f" },
base64url: { label: "מחרוזת בבסיס 64 לכתובות רשת", gender: "f" },
json_string: { label: "מחרוזת JSON", gender: "f" },
e164: { label: "מספר E.164", gender: "m" },
jwt: { label: "JWT", gender: "m" },
ends_with: { label: "קלט", gender: "m" },
includes: { label: "קלט", gender: "m" },
lowercase: { label: "קלט", gender: "m" },
starts_with: { label: "קלט", gender: "m" },
uppercase: { label: "קלט", gender: "m" },
};
return (issue) => {
switch (issue.code) {
case "invalid_type":
return `קלט לא תקין: צריך ${issue.expected}, התקבל ${parsedType(issue.input)}`;
// return `Invalid input: expected ${issue.expected}, received ${util.getParsedType(issue.input)}`;
case "invalid_value":
if (issue.values.length === 1) return `קלט לא תקין: צריך ${util.stringifyPrimitive(issue.values[0])}`;
return `קלט לא תקין: צריך אחת מהאפשרויות ${util.joinValues(issue.values, "|")}`;
case "too_big": {
const adj = issue.inclusive ? "<=" : "<";
const sizing = getSizing(issue.origin);
if (sizing)
return `גדול מדי: ${issue.origin ?? "value"} צריך להיות ${adj}${issue.maximum.toString()} ${sizing.unit ?? "elements"}`;
return `גדול מדי: ${issue.origin ?? "value"} צריך להיות ${adj}${issue.maximum.toString()}`;
case "invalid_type": {
// Expected type: show without definite article for clearer Hebrew
const expectedKey = issue.expected as string | undefined;
const expected = typeLabel(expectedKey);
// Received: show localized label if known, otherwise constructor/raw
const receivedKey = parsedType(issue.input);
const received = TypeNames[receivedKey]?.label ?? receivedKey;
return `קלט לא תקין: צריך להיות ${expected}, התקבל ${received}`;
}
case "too_small": {
const adj = issue.inclusive ? ">=" : ">";
case "invalid_value": {
if (issue.values.length === 1) {
return `ערך לא תקין: הערך חייב להיות ${util.stringifyPrimitive(issue.values[0])}`;
}
// Join values with proper Hebrew formatting
const stringified = issue.values.map((v) => util.stringifyPrimitive(v));
if (issue.values.length === 2) {
return `ערך לא תקין: האפשרויות המתאימות הן ${stringified[0]} או ${stringified[1]}`;
}
// For 3+ values: "a", "b" או "c"
const lastValue = stringified[stringified.length - 1];
const restValues = stringified.slice(0, -1).join(", ");
return `ערך לא תקין: האפשרויות המתאימות הן ${restValues} או ${lastValue}`;
}
case "too_big": {
const sizing = getSizing(issue.origin);
if (sizing) {
return `קטן מדי: ${issue.origin} צריך להיות ${adj}${issue.minimum.toString()} ${sizing.unit}`;
const subject = withDefinite(issue.origin ?? "value");
if (issue.origin === "string") {
// Special handling for strings - more natural Hebrew
return `${sizing?.longLabel ?? "ארוך"} מדי: ${subject} צריכה להכיל ${issue.maximum.toString()} ${sizing?.unit ?? ""} ${issue.inclusive ? "או פחות" : "לכל היותר"}`.trim();
}
return `קטן מדי: ${issue.origin} צריך להיות ${adj}${issue.minimum.toString()}`;
if (issue.origin === "number") {
// Natural Hebrew for numbers
const comparison = issue.inclusive ? `קטן או שווה ל-${issue.maximum}` : `קטן מ-${issue.maximum}`;
return `גדול מדי: ${subject} צריך להיות ${comparison}`;
}
if (issue.origin === "array" || issue.origin === "set") {
// Natural Hebrew for arrays and sets
const verb = issue.origin === "set" ? "צריכה" : "צריך";
const comparison = issue.inclusive
? `${issue.maximum} ${sizing?.unit ?? ""} או פחות`
: `פחות מ-${issue.maximum} ${sizing?.unit ?? ""}`;
return `גדול מדי: ${subject} ${verb} להכיל ${comparison}`.trim();
}
const adj = issue.inclusive ? "<=" : "<";
const be = verbFor(issue.origin ?? "value");
if (sizing?.unit) {
return `${sizing.longLabel} מדי: ${subject} ${be} ${adj}${issue.maximum.toString()} ${sizing.unit}`;
}
return `${sizing?.longLabel ?? "גדול"} מדי: ${subject} ${be} ${adj}${issue.maximum.toString()}`;
}
case "too_small": {
const sizing = getSizing(issue.origin);
const subject = withDefinite(issue.origin ?? "value");
if (issue.origin === "string") {
// Special handling for strings - more natural Hebrew
return `${sizing?.shortLabel ?? "קצר"} מדי: ${subject} צריכה להכיל ${issue.minimum.toString()} ${sizing?.unit ?? ""} ${issue.inclusive ? "או יותר" : "לפחות"}`.trim();
}
if (issue.origin === "number") {
// Natural Hebrew for numbers
const comparison = issue.inclusive ? `גדול או שווה ל-${issue.minimum}` : `גדול מ-${issue.minimum}`;
return `קטן מדי: ${subject} צריך להיות ${comparison}`;
}
if (issue.origin === "array" || issue.origin === "set") {
// Natural Hebrew for arrays and sets
const verb = issue.origin === "set" ? "צריכה" : "צריך";
// Special case for singular (minimum === 1)
if (issue.minimum === 1 && issue.inclusive) {
const singularPhrase = issue.origin === "set" ? "לפחות פריט אחד" : "לפחות פריט אחד";
return `קטן מדי: ${subject} ${verb} להכיל ${singularPhrase}`;
}
const comparison = issue.inclusive
? `${issue.minimum} ${sizing?.unit ?? ""} או יותר`
: `יותר מ-${issue.minimum} ${sizing?.unit ?? ""}`;
return `קטן מדי: ${subject} ${verb} להכיל ${comparison}`.trim();
}
const adj = issue.inclusive ? ">=" : ">";
const be = verbFor(issue.origin ?? "value");
if (sizing?.unit) {
return `${sizing.shortLabel} מדי: ${subject} ${be} ${adj}${issue.minimum.toString()} ${sizing.unit}`;
}
return `${sizing?.shortLabel ?? "קטן"} מדי: ${subject} ${be} ${adj}${issue.minimum.toString()}`;
}
case "invalid_format": {
const _issue = issue as errors.$ZodStringFormatIssues;
if (_issue.format === "starts_with") return `מחרוזת לא תקינה: חייבת להתחיל ב"${_issue.prefix}"`;
if (_issue.format === "ends_with") return `מחרוזת לא תקינה: חייבת להסתיים ב "${_issue.suffix}"`;
if (_issue.format === "includes") return `מחרוזת לא תקינה: חייבת לכלול "${_issue.includes}"`;
if (_issue.format === "regex") return `מחרוזת לא תקינה: חייבת להתאים לתבנית ${_issue.pattern}`;
return `${Nouns[_issue.format] ?? issue.format} לא תקין`;
// These apply to strings — use feminine grammar + ה׳ הידיעה
if (_issue.format === "starts_with") return `המחרוזת חייבת להתחיל ב "${_issue.prefix}"`;
if (_issue.format === "ends_with") return `המחרוזת חייבת להסתיים ב "${_issue.suffix}"`;
if (_issue.format === "includes") return `המחרוזת חייבת לכלול "${_issue.includes}"`;
if (_issue.format === "regex") return `המחרוזת חייבת להתאים לתבנית ${_issue.pattern}`;
// Handle gender agreement for formats
const nounEntry = Nouns[_issue.format];
const noun = nounEntry?.label ?? _issue.format;
const gender = nounEntry?.gender ?? "m";
const adjective = gender === "f" ? "תקינה" : "תקין";
return `${noun} לא ${adjective}`;
}
case "not_multiple_of":
return `מספר לא תקין: חייב להיות מכפלה של ${issue.divisor}`;
case "unrecognized_keys":
return `מפתח${issue.keys.length > 1 ? "ות" : ""} לא מזוה${issue.keys.length > 1 ? "ים" : "ה"}: ${util.joinValues(issue.keys, ", ")}`;
case "invalid_key":
return `מפתח לא תקין ב${issue.origin}`;
case "invalid_key": {
return `שדה לא תקין באובייקט`;
}
case "invalid_union":
return "קלט לא תקין";
case "invalid_element":
return `ערך לא תקין ב${issue.origin}`;
case "invalid_element": {
const place = withDefinite(issue.origin ?? "array");
return `ערך לא תקין ב${place}`;
}
default:
return `קלט לא תקין`;
}
+8
View File
@@ -1,8 +1,10 @@
export { default as ar } from "./ar.js";
export { default as az } from "./az.js";
export { default as be } from "./be.js";
export { default as bg } from "./bg.js";
export { default as ca } from "./ca.js";
export { default as cs } from "./cs.js";
export { default as da } from "./da.js";
export { default as de } from "./de.js";
export { default as en } from "./en.js";
export { default as eo } from "./eo.js";
@@ -14,10 +16,14 @@ export { default as frCA } from "./fr-CA.js";
export { default as he } from "./he.js";
export { default as hu } from "./hu.js";
export { default as id } from "./id.js";
export { default as is } from "./is.js";
export { default as it } from "./it.js";
export { default as ja } from "./ja.js";
export { default as ka } from "./ka.js";
export { default as kh } from "./kh.js";
export { default as km } from "./km.js";
export { default as ko } from "./ko.js";
export { default as lt } from "./lt.js";
export { default as mk } from "./mk.js";
export { default as ms } from "./ms.js";
export { default as nl } from "./nl.js";
@@ -33,7 +39,9 @@ export { default as ta } from "./ta.js";
export { default as th } from "./th.js";
export { default as tr } from "./tr.js";
export { default as ua } from "./ua.js";
export { default as uk } from "./uk.js";
export { default as ur } from "./ur.js";
export { default as vi } from "./vi.js";
export { default as zhCN } from "./zh-CN.js";
export { default as zhTW } from "./zh-TW.js";
export { default as yo } from "./yo.js";
+3 -122
View File
@@ -1,126 +1,7 @@
import type { $ZodStringFormats } from "../core/checks.js";
import type * as errors from "../core/errors.js";
import * as util from "../core/util.js";
const error: () => errors.$ZodErrorMap = () => {
const Sizable: Record<string, { unit: string; verb: string }> = {
string: { unit: "តួអក្សរ", verb: "គួរមាន" },
file: { unit: "បៃ", verb: "គួរមាន" },
array: { unit: "ធាតុ", verb: "គួរមាន" },
set: { unit: "ធាតុ", verb: "គួរមាន" },
};
function getSizing(origin: string): { unit: string; verb: string } | null {
return Sizable[origin] ?? null;
}
const parsedType = (data: any): string => {
const t = typeof data;
switch (t) {
case "number": {
return Number.isNaN(data) ? "មិនមែនជាលេខ (NaN)" : "លេខ";
}
case "object": {
if (Array.isArray(data)) {
return "អារេ (Array)";
}
if (data === null) {
return "គ្មានតម្លៃ (null)";
}
if (Object.getPrototypeOf(data) !== Object.prototype && data.constructor) {
return data.constructor.name;
}
}
}
return t;
};
const Nouns: {
[k in $ZodStringFormats | (string & {})]?: string;
} = {
regex: "ទិន្នន័យបញ្ចូល",
email: "អាសយដ្ឋានអ៊ីមែល",
url: "URL",
emoji: "សញ្ញាអារម្មណ៍",
uuid: "UUID",
uuidv4: "UUIDv4",
uuidv6: "UUIDv6",
nanoid: "nanoid",
guid: "GUID",
cuid: "cuid",
cuid2: "cuid2",
ulid: "ULID",
xid: "XID",
ksuid: "KSUID",
datetime: "កាលបរិច្ឆេទ និងម៉ោង ISO",
date: "កាលបរិច្ឆេទ ISO",
time: "ម៉ោង ISO",
duration: "រយៈពេល ISO",
ipv4: "អាសយដ្ឋាន IPv4",
ipv6: "អាសយដ្ឋាន IPv6",
cidrv4: "ដែនអាសយដ្ឋាន IPv4",
cidrv6: "ដែនអាសយដ្ឋាន IPv6",
base64: "ខ្សែអក្សរអ៊ិកូដ base64",
base64url: "ខ្សែអក្សរអ៊ិកូដ base64url",
json_string: "ខ្សែអក្សរ JSON",
e164: "លេខ E.164",
jwt: "JWT",
template_literal: "ទិន្នន័យបញ្ចូល",
};
return (issue) => {
switch (issue.code) {
case "invalid_type":
return `ទិន្នន័យបញ្ចូលមិនត្រឹមត្រូវ៖ ត្រូវការ ${issue.expected} ប៉ុន្តែទទួលបាន ${parsedType(issue.input)}`;
case "invalid_value":
if (issue.values.length === 1) return `ទិន្នន័យបញ្ចូលមិនត្រឹមត្រូវ៖ ត្រូវការ ${util.stringifyPrimitive(issue.values[0])}`;
return `ជម្រើសមិនត្រឹមត្រូវ៖ ត្រូវជាមួយក្នុងចំណោម ${util.joinValues(issue.values, "|")}`;
case "too_big": {
const adj = issue.inclusive ? "<=" : "<";
const sizing = getSizing(issue.origin);
if (sizing)
return `ធំពេក៖ ត្រូវការ ${issue.origin ?? "តម្លៃ"} ${adj} ${issue.maximum.toString()} ${sizing.unit ?? "ធាតុ"}`;
return `ធំពេក៖ ត្រូវការ ${issue.origin ?? "តម្លៃ"} ${adj} ${issue.maximum.toString()}`;
}
case "too_small": {
const adj = issue.inclusive ? ">=" : ">";
const sizing = getSizing(issue.origin);
if (sizing) {
return `តូចពេក៖ ត្រូវការ ${issue.origin} ${adj} ${issue.minimum.toString()} ${sizing.unit}`;
}
return `តូចពេក៖ ត្រូវការ ${issue.origin} ${adj} ${issue.minimum.toString()}`;
}
case "invalid_format": {
const _issue = issue as errors.$ZodStringFormatIssues;
if (_issue.format === "starts_with") {
return `ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវចាប់ផ្តើមដោយ "${_issue.prefix}"`;
}
if (_issue.format === "ends_with") return `ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវបញ្ចប់ដោយ "${_issue.suffix}"`;
if (_issue.format === "includes") return `ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវមាន "${_issue.includes}"`;
if (_issue.format === "regex") return `ខ្សែអក្សរមិនត្រឹមត្រូវ៖ ត្រូវតែផ្គូផ្គងនឹងទម្រង់ដែលបានកំណត់ ${_issue.pattern}`;
return `មិនត្រឹមត្រូវ៖ ${Nouns[_issue.format] ?? issue.format}`;
}
case "not_multiple_of":
return `លេខមិនត្រឹមត្រូវ៖ ត្រូវតែជាពហុគុណនៃ ${issue.divisor}`;
case "unrecognized_keys":
return `រកឃើញសោមិនស្គាល់៖ ${util.joinValues(issue.keys, ", ")}`;
case "invalid_key":
return `សោមិនត្រឹមត្រូវនៅក្នុង ${issue.origin}`;
case "invalid_union":
return `ទិន្នន័យមិនត្រឹមត្រូវ`;
case "invalid_element":
return `ទិន្នន័យមិនត្រឹមត្រូវនៅក្នុង ${issue.origin}`;
default:
return `ទិន្នន័យមិនត្រឹមត្រូវ`;
}
};
};
import km from "./km.js";
/** @deprecated Use `km` instead. */
export default function (): { localeError: errors.$ZodErrorMap } {
return {
localeError: error(),
};
return km();
}
+10 -10
View File
@@ -3,14 +3,14 @@ import type * as errors from "../core/errors.js";
import * as util from "../core/util.js";
const error: () => errors.$ZodErrorMap = () => {
const Sizable: Record<string, { unit: string }> = {
string: { unit: "tekens" },
file: { unit: "bytes" },
array: { unit: "elementen" },
set: { unit: "elementen" },
const Sizable: Record<string, { unit: string; verb: string }> = {
string: { unit: "tekens", verb: "te hebben" },
file: { unit: "bytes", verb: "te hebben" },
array: { unit: "elementen", verb: "te hebben" },
set: { unit: "elementen", verb: "te hebben" },
};
function getSizing(origin: string): { unit: string } | null {
function getSizing(origin: string): { unit: string; verb: string } | null {
return Sizable[origin] ?? null;
}
@@ -81,17 +81,17 @@ const error: () => errors.$ZodErrorMap = () => {
const adj = issue.inclusive ? "<=" : "<";
const sizing = getSizing(issue.origin);
if (sizing)
return `Te lang: verwacht dat ${issue.origin ?? "waarde"} ${adj}${issue.maximum.toString()} ${sizing.unit ?? "elementen"} bevat`;
return `Te lang: verwacht dat ${issue.origin ?? "waarde"} ${adj}${issue.maximum.toString()} is`;
return `Te groot: verwacht dat ${issue.origin ?? "waarde"} ${sizing.verb} ${adj}${issue.maximum.toString()} ${sizing.unit ?? "elementen"}`;
return `Te groot: verwacht dat ${issue.origin ?? "waarde"} ${adj}${issue.maximum.toString()} is`;
}
case "too_small": {
const adj = issue.inclusive ? ">=" : ">";
const sizing = getSizing(issue.origin);
if (sizing) {
return `Te kort: verwacht dat ${issue.origin} ${adj}${issue.minimum.toString()} ${sizing.unit} bevat`;
return `Te klein: verwacht dat ${issue.origin} ${sizing.verb} ${adj}${issue.minimum.toString()} ${sizing.unit}`;
}
return `Te kort: verwacht dat ${issue.origin} ${adj}${issue.minimum.toString()} is`;
return `Te klein: verwacht dat ${issue.origin} ${adj}${issue.minimum.toString()} is`;
}
case "invalid_format": {
const _issue = issue as errors.$ZodStringFormatIssues;
+3 -122
View File
@@ -1,126 +1,7 @@
import type { $ZodStringFormats } from "../core/checks.js";
import type * as errors from "../core/errors.js";
import * as util from "../core/util.js";
const error: () => errors.$ZodErrorMap = () => {
const Sizable: Record<string, { unit: string; verb: string }> = {
string: { unit: "символів", verb: "матиме" },
file: { unit: "байтів", verb: "матиме" },
array: { unit: "елементів", verb: "матиме" },
set: { unit: "елементів", verb: "матиме" },
};
function getSizing(origin: string): { unit: string; verb: string } | null {
return Sizable[origin] ?? null;
}
const parsedType = (data: any): string => {
const t = typeof data;
switch (t) {
case "number": {
return Number.isNaN(data) ? "NaN" : "число";
}
case "object": {
if (Array.isArray(data)) {
return "масив";
}
if (data === null) {
return "null";
}
if (Object.getPrototypeOf(data) !== Object.prototype && data.constructor) {
return data.constructor.name;
}
}
}
return t;
};
const Nouns: {
[k in $ZodStringFormats | (string & {})]?: string;
} = {
regex: "вхідні дані",
email: "адреса електронної пошти",
url: "URL",
emoji: "емодзі",
uuid: "UUID",
uuidv4: "UUIDv4",
uuidv6: "UUIDv6",
nanoid: "nanoid",
guid: "GUID",
cuid: "cuid",
cuid2: "cuid2",
ulid: "ULID",
xid: "XID",
ksuid: "KSUID",
datetime: "дата та час ISO",
date: "дата ISO",
time: "час ISO",
duration: "тривалість ISO",
ipv4: "адреса IPv4",
ipv6: "адреса IPv6",
cidrv4: "діапазон IPv4",
cidrv6: "діапазон IPv6",
base64: "рядок у кодуванні base64",
base64url: "рядок у кодуванні base64url",
json_string: "рядок JSON",
e164: "номер E.164",
jwt: "JWT",
template_literal: "вхідні дані",
};
return (issue) => {
switch (issue.code) {
case "invalid_type":
return `Неправильні вхідні дані: очікується ${issue.expected}, отримано ${parsedType(issue.input)}`;
// return `Неправильні вхідні дані: очікується ${issue.expected}, отримано ${util.getParsedType(issue.input)}`;
case "invalid_value":
if (issue.values.length === 1)
return `Неправильні вхідні дані: очікується ${util.stringifyPrimitive(issue.values[0])}`;
return `Неправильна опція: очікується одне з ${util.joinValues(issue.values, "|")}`;
case "too_big": {
const adj = issue.inclusive ? "<=" : "<";
const sizing = getSizing(issue.origin);
if (sizing)
return `Занадто велике: очікується, що ${issue.origin ?? "значення"} ${sizing.verb} ${adj}${issue.maximum.toString()} ${sizing.unit ?? "елементів"}`;
return `Занадто велике: очікується, що ${issue.origin ?? "значення"} буде ${adj}${issue.maximum.toString()}`;
}
case "too_small": {
const adj = issue.inclusive ? ">=" : ">";
const sizing = getSizing(issue.origin);
if (sizing) {
return `Занадто мале: очікується, що ${issue.origin} ${sizing.verb} ${adj}${issue.minimum.toString()} ${sizing.unit}`;
}
return `Занадто мале: очікується, що ${issue.origin} буде ${adj}${issue.minimum.toString()}`;
}
case "invalid_format": {
const _issue = issue as errors.$ZodStringFormatIssues;
if (_issue.format === "starts_with") return `Неправильний рядок: повинен починатися з "${_issue.prefix}"`;
if (_issue.format === "ends_with") return `Неправильний рядок: повинен закінчуватися на "${_issue.suffix}"`;
if (_issue.format === "includes") return `Неправильний рядок: повинен містити "${_issue.includes}"`;
if (_issue.format === "regex") return `Неправильний рядок: повинен відповідати шаблону ${_issue.pattern}`;
return `Неправильний ${Nouns[_issue.format] ?? issue.format}`;
}
case "not_multiple_of":
return `Неправильне число: повинно бути кратним ${issue.divisor}`;
case "unrecognized_keys":
return `Нерозпізнаний ключ${issue.keys.length > 1 ? "і" : ""}: ${util.joinValues(issue.keys, ", ")}`;
case "invalid_key":
return `Неправильний ключ у ${issue.origin}`;
case "invalid_union":
return "Неправильні вхідні дані";
case "invalid_element":
return `Неправильне значення у ${issue.origin}`;
default:
return `Неправильні вхідні дані`;
}
};
};
import uk from "./uk.js";
/** @deprecated Use `uk` instead. */
export default function (): { localeError: errors.$ZodErrorMap } {
return {
localeError: error(),
};
return uk();
}
+1 -1
View File
@@ -11,7 +11,6 @@ export {
$output,
$input,
$brand,
function,
clone,
regexes,
treeifyError,
@@ -20,6 +19,7 @@ export {
flattenError,
toJSONSchema,
TimePrecision,
util,
NEVER,
} from "../core/index.js";
+4 -4
View File
@@ -6,7 +6,7 @@ export interface ZodMiniISODateTime extends schemas.ZodMiniStringFormat<"datetim
_zod: core.$ZodISODateTimeInternals;
}
export const ZodMiniISODateTime: core.$constructor<ZodMiniISODateTime> = /*@__PURE__*/ core.$constructor(
"$ZodISODateTime",
"ZodMiniISODateTime",
(inst, def) => {
core.$ZodISODateTime.init(inst, def);
schemas.ZodMiniStringFormat.init(inst, def);
@@ -21,7 +21,7 @@ export interface ZodMiniISODate extends schemas.ZodMiniStringFormat<"date"> {
_zod: core.$ZodISODateInternals;
}
export const ZodMiniISODate: core.$constructor<ZodMiniISODate> = /*@__PURE__*/ core.$constructor(
"$ZodISODate",
"ZodMiniISODate",
(inst, def) => {
core.$ZodISODate.init(inst, def);
schemas.ZodMiniStringFormat.init(inst, def);
@@ -36,7 +36,7 @@ export interface ZodMiniISOTime extends schemas.ZodMiniStringFormat<"time"> {
_zod: core.$ZodISOTimeInternals;
}
export const ZodMiniISOTime: core.$constructor<ZodMiniISOTime> = /*@__PURE__*/ core.$constructor(
"$ZodISOTime",
"ZodMiniISOTime",
(inst, def) => {
core.$ZodISOTime.init(inst, def);
schemas.ZodMiniStringFormat.init(inst, def);
@@ -51,7 +51,7 @@ export interface ZodMiniISODuration extends schemas.ZodMiniStringFormat<"duratio
_zod: core.$ZodISODurationInternals;
}
export const ZodMiniISODuration: core.$constructor<ZodMiniISODuration> = /*@__PURE__*/ core.$constructor(
"$ZodISODuration",
"ZodMiniISODuration",
(inst, def) => {
core.$ZodISODuration.init(inst, def);
schemas.ZodMiniStringFormat.init(inst, def);
+14 -1
View File
@@ -1 +1,14 @@
export { parse, safeParse, parseAsync, safeParseAsync } from "../core/index.js";
export {
parse,
safeParse,
parseAsync,
safeParseAsync,
encode,
decode,
encodeAsync,
decodeAsync,
safeEncode,
safeDecode,
safeEncodeAsync,
safeDecodeAsync,
} from "../core/index.js";
+203 -40
View File
@@ -9,6 +9,7 @@ export interface ZodMiniType<
out Input = unknown,
out Internals extends core.$ZodTypeInternals<Output, Input> = core.$ZodTypeInternals<Output, Input>,
> extends core.$ZodType<Output, Input, Internals> {
type: Internals["def"]["type"];
check(...checks: (core.CheckFn<core.output<this>> | core.$ZodCheck<core.output<this>>)[]): this;
clone(def?: Internals["def"], params?: { parent: boolean }): this;
register<R extends core.$ZodRegistry>(
@@ -43,7 +44,9 @@ export const ZodMiniType: core.$constructor<ZodMiniType> = /*@__PURE__*/ core.$c
if (!inst._zod) throw new Error("Uninitialized schema in ZodMiniType.");
core.$ZodType.init(inst, def);
inst.def = def;
inst.type = def.type;
inst.parse = (data, params) => parse.parse(inst, data, params, { callee: inst.parse });
inst.safeParse = (data, params) => parse.safeParse(inst, data, params);
inst.parseAsync = async (data, params) => parse.parseAsync(inst, data, params, { callee: inst.parseAsync });
@@ -182,6 +185,14 @@ export function url(params?: string | core.$ZodURLParams): ZodMiniURL {
return core._url(ZodMiniURL, params);
}
export function httpUrl(params?: string | Omit<core.$ZodURLParams, "protocol" | "hostname">): ZodMiniURL {
return core._url(ZodMiniURL, {
protocol: /^https?$/,
hostname: core.regexes.domain,
...util.normalizeParams(params),
});
}
// ZodMiniEmoji
export interface ZodMiniEmoji extends _ZodMiniString<core.$ZodEmojiInternals> {
// _zod: core.$ZodEmojiInternals;
@@ -355,6 +366,19 @@ export function cidrv6(params?: string | core.$ZodCIDRv6Params): ZodMiniCIDRv6 {
return core._cidrv6(ZodMiniCIDRv6, params);
}
// ZodMiniMAC
export interface ZodMiniMAC extends _ZodMiniString<core.$ZodMACInternals> {
// _zod: core.$ZodMACInternals;
}
export const ZodMiniMAC: core.$constructor<ZodMiniMAC> = /*@__PURE__*/ core.$constructor("ZodMiniMAC", (inst, def) => {
core.$ZodMAC.init(inst, def);
ZodMiniStringFormat.init(inst, def);
});
export function mac(params?: string | core.$ZodMACParams): ZodMiniMAC {
return core._mac(ZodMiniMAC, params);
}
// ZodMiniBase64
export interface ZodMiniBase64 extends _ZodMiniString<core.$ZodBase64Internals> {
// _zod: core.$ZodBase64Internals;
@@ -436,6 +460,28 @@ export function stringFormat<Format extends string>(
return core._stringFormat(ZodMiniCustomStringFormat, format, fnOrRegex, _params) as any;
}
export function hostname(_params?: string | core.$ZodStringFormatParams): ZodMiniCustomStringFormat<"hostname"> {
return core._stringFormat(ZodMiniCustomStringFormat, "hostname", core.regexes.hostname, _params) as any;
}
export function hex(_params?: string | core.$ZodStringFormatParams): ZodMiniCustomStringFormat<"hex"> {
return core._stringFormat(ZodMiniCustomStringFormat, "hex", core.regexes.hex, _params) as any;
}
export function hash<Alg extends util.HashAlgorithm, Enc extends util.HashEncoding = "hex">(
alg: Alg,
params?: {
enc?: Enc;
} & core.$ZodStringFormatParams
): ZodMiniCustomStringFormat<`${Alg}_${Enc}`> {
const enc = params?.enc ?? "hex";
const format = `${alg}_${enc}` as const;
const regex = core.regexes[format as keyof typeof core.regexes] as RegExp;
// check for unrecognized format
if (!regex) throw new Error(`Unrecognized hash format: ${format}`);
return core._stringFormat(ZodMiniCustomStringFormat, format, regex, params) as any;
}
// ZodMiniNumber
interface _ZodMiniNumber<T extends core.$ZodNumberInternals<unknown> = core.$ZodNumberInternals<unknown>>
extends _ZodMiniType<T>,
@@ -708,9 +754,9 @@ export function array<T extends SomeType>(element: SomeType, params?: any): ZodM
}
// .keyof
export function keyof<T extends ZodMiniObject>(schema: T): ZodMiniLiteral<Exclude<keyof T["shape"], symbol>> {
export function keyof<T extends ZodMiniObject>(schema: T): ZodMiniEnum<util.KeysEnum<T["shape"]>> {
const shape = schema._zod.def.shape;
return literal(Object.keys(shape)) as any;
return _enum(Object.keys(shape)) as any;
}
// ZodMiniObject
@@ -736,10 +782,7 @@ export function object<T extends core.$ZodLooseShape = Record<never, SomeType>>(
): ZodMiniObject<T, core.$strip> {
const def: core.$ZodObjectDef = {
type: "object",
get shape() {
util.assignProp(this, "shape", { ...shape });
return this.shape;
},
shape: shape ?? {},
...util.normalizeParams(params),
};
return new ZodMiniObject(def) as any;
@@ -752,11 +795,7 @@ export function strictObject<T extends core.$ZodLooseShape>(
): ZodMiniObject<T, core.$strict> {
return new ZodMiniObject({
type: "object",
// shape: shape as core.$ZodLooseShape,
get shape() {
util.assignProp(this, "shape", { ...shape });
return this.shape;
},
shape,
catchall: never(),
...util.normalizeParams(params),
}) as any;
@@ -769,14 +808,7 @@ export function looseObject<T extends core.$ZodLooseShape>(
): ZodMiniObject<T, core.$loose> {
return new ZodMiniObject({
type: "object",
// shape: shape as core.$ZodLooseShape,
get shape() {
util.assignProp(this, "shape", { ...shape });
return this.shape;
},
// get optional() {
// return util.optionalKeys(shape);
// },
shape,
catchall: unknown(),
...util.normalizeParams(params),
}) as any;
@@ -790,6 +822,23 @@ export function extend<T extends ZodMiniObject, U extends core.$ZodLooseShape>(
return util.extend(schema, shape);
}
export type SafeExtendShape<Base extends core.$ZodShape, Ext extends core.$ZodLooseShape> = {
[K in keyof Ext]: K extends keyof Base
? core.output<Ext[K]> extends core.output<Base[K]>
? core.input<Ext[K]> extends core.input<Base[K]>
? Ext[K]
: never
: never
: Ext[K];
};
export function safeExtend<T extends ZodMiniObject, U extends core.$ZodLooseShape>(
schema: T,
shape: SafeExtendShape<T["shape"], U>
): ZodMiniObject<util.Extend<T["shape"], U>, T["_zod"]["config"]> {
return util.safeExtend(schema, shape as any);
}
/** @deprecated Identical to `z.extend(A, B)` */
export function merge<T extends ZodMiniObject, U extends ZodMiniObject>(
a: T,
@@ -903,9 +952,11 @@ export function union<const T extends readonly SomeType[]>(
}
// ZodMiniDiscriminatedUnion
export interface ZodMiniDiscriminatedUnion<Options extends readonly SomeType[] = readonly core.$ZodType[]>
extends ZodMiniUnion<Options> {
_zod: core.$ZodDiscriminatedUnionInternals<Options>;
export interface ZodMiniDiscriminatedUnion<
Options extends readonly SomeType[] = readonly core.$ZodType[],
Disc extends string = string,
> extends ZodMiniUnion<Options> {
_zod: core.$ZodDiscriminatedUnionInternals<Options, Disc>;
}
export const ZodMiniDiscriminatedUnion: core.$constructor<ZodMiniDiscriminatedUnion> = /*@__PURE__*/ core.$constructor(
"ZodMiniDiscriminatedUnion",
@@ -917,17 +968,18 @@ export const ZodMiniDiscriminatedUnion: core.$constructor<ZodMiniDiscriminatedUn
export function discriminatedUnion<
Types extends readonly [core.$ZodTypeDiscriminable, ...core.$ZodTypeDiscriminable[]],
Disc extends string,
>(
discriminator: string,
discriminator: Disc,
options: Types,
params?: string | core.$ZodDiscriminatedUnionParams
): ZodMiniDiscriminatedUnion<Types> {
): ZodMiniDiscriminatedUnion<Types, Disc> {
return new ZodMiniDiscriminatedUnion({
type: "union",
options,
discriminator,
...util.normalizeParams(params),
}) as ZodMiniDiscriminatedUnion<Types>;
}) as ZodMiniDiscriminatedUnion<Types, Disc>;
}
// ZodMiniIntersection
@@ -1024,9 +1076,11 @@ export function partialRecord<Key extends core.$ZodRecordKey, Value extends Some
valueType: Value,
params?: string | core.$ZodRecordParams
): ZodMiniRecord<Key & core.$partial, Value> {
const k = core.clone(keyType);
k._zod.values = undefined;
return new ZodMiniRecord({
type: "record",
keyType: union([keyType, never()]),
keyType: k,
valueType: valueType as any,
...util.normalizeParams(params),
}) as any;
@@ -1075,12 +1129,15 @@ export function set<Value extends SomeType>(valueType: Value, params?: string |
// ZodMiniEnum
export interface ZodMiniEnum<T extends util.EnumLike = util.EnumLike> extends _ZodMiniType<core.$ZodEnumInternals<T>> {
// _zod: core.$ZodEnumInternals<T>;
options: Array<T[keyof T]>;
}
export const ZodMiniEnum: core.$constructor<ZodMiniEnum> = /*@__PURE__*/ core.$constructor(
"ZodMiniEnum",
(inst, def) => {
core.$ZodEnum.init(inst, def);
ZodMiniType.init(inst, def);
inst.options = Object.values(def.entries);
}
);
@@ -1247,7 +1304,7 @@ export function _default<T extends SomeType>(
type: "default",
innerType: innerType as any as core.$ZodType,
get defaultValue() {
return typeof defaultValue === "function" ? (defaultValue as Function)() : defaultValue;
return typeof defaultValue === "function" ? (defaultValue as Function)() : util.shallowClone(defaultValue);
},
}) as any;
}
@@ -1272,7 +1329,7 @@ export function prefault<T extends SomeType>(
type: "prefault",
innerType: innerType as any as core.$ZodType,
get defaultValue() {
return typeof defaultValue === "function" ? (defaultValue as Function)() : defaultValue;
return typeof defaultValue === "function" ? (defaultValue as Function)() : util.shallowClone(defaultValue);
},
}) as any;
}
@@ -1383,6 +1440,38 @@ export function pipe<
}) as any;
}
// ZodMiniCodec
export interface ZodMiniCodec<A extends SomeType = core.$ZodType, B extends SomeType = core.$ZodType>
extends ZodMiniPipe<A, B>,
core.$ZodCodec<A, B> {
_zod: core.$ZodCodecInternals<A, B>;
def: core.$ZodCodecDef<A, B>;
}
export const ZodMiniCodec: core.$constructor<ZodMiniCodec> = /*@__PURE__*/ core.$constructor(
"ZodMiniCodec",
(inst, def) => {
ZodMiniPipe.init(inst, def);
core.$ZodCodec.init(inst, def);
}
);
export function codec<const A extends SomeType, B extends core.SomeType = core.$ZodType>(
in_: A,
out: B,
params: {
decode: (value: core.output<A>, payload: core.ParsePayload<core.output<A>>) => core.util.MaybeAsync<core.input<B>>;
encode: (value: core.input<B>, payload: core.ParsePayload<core.input<B>>) => core.util.MaybeAsync<core.output<A>>;
}
): ZodMiniCodec<A, B> {
return new ZodMiniCodec({
type: "pipe",
in: in_ as any as core.$ZodType,
out: out as any as core.$ZodType,
transform: params.decode as any,
reverseTransform: params.encode as any,
}) as any;
}
// /** @deprecated Use `z.pipe()` and `z.transform()` instead. */
// export function preprocess<A, U extends core.$ZodType>(
// fn: (arg: unknown, ctx: core.ParsePayload) => A,
@@ -1518,6 +1607,17 @@ export function refine<T>(
return core._refine(ZodMiniCustom, fn, _params);
}
// superRefine
export function superRefine<T>(
fn: (arg: T, payload: core.$RefinementCtx<T>) => void | Promise<void>
): core.$ZodCheck<T> {
return core._superRefine(fn);
}
// Re-export describe and meta from core
export const describe = core.describe;
export const meta = core.meta;
// instanceof
abstract class Class {
constructor(..._args: any[]) {}
@@ -1535,18 +1635,16 @@ function _instanceof<T extends typeof Class>(
export { _instanceof as instanceof };
// stringbool
export const stringbool: (
_params?: string | core.$ZodStringBoolParams
) => ZodMiniPipe<ZodMiniPipe<ZodMiniString, ZodMiniTransform<boolean, string>>, ZodMiniBoolean> = (...args) =>
core._stringbool(
{
Pipe: ZodMiniPipe,
Boolean: ZodMiniBoolean,
String: ZodMiniString,
Transform: ZodMiniTransform,
},
...args
) as any;
export const stringbool: (_params?: string | core.$ZodStringBoolParams) => ZodMiniCodec<ZodMiniString, ZodMiniBoolean> =
(...args) =>
core._stringbool(
{
Codec: ZodMiniCodec,
Boolean: ZodMiniBoolean,
String: ZodMiniString,
},
...args
) as any;
// json
@@ -1577,3 +1675,68 @@ export function json(): ZodMiniJSONSchema {
});
return jsonSchema;
}
// ZodMiniFunction
export interface ZodMiniFunction<
Args extends core.$ZodFunctionIn = core.$ZodFunctionIn,
Returns extends core.$ZodFunctionOut = core.$ZodFunctionOut,
> extends _ZodMiniType<core.$ZodFunctionInternals<Args, Returns>>,
core.$ZodFunction<Args, Returns> {
_def: core.$ZodFunctionDef<Args, Returns>;
_input: core.$InferInnerFunctionType<Args, Returns>;
_output: core.$InferOuterFunctionType<Args, Returns>;
input<const Items extends util.TupleItems, const Rest extends core.$ZodFunctionOut = core.$ZodFunctionOut>(
args: Items,
rest?: Rest
): ZodMiniFunction<ZodMiniTuple<Items, Rest>, Returns>;
input<NewArgs extends core.$ZodFunctionIn>(args: NewArgs): ZodMiniFunction<NewArgs, Returns>;
input(...args: any[]): ZodMiniFunction<any, Returns>;
output<NewReturns extends core.$ZodFunctionOut>(output: NewReturns): ZodMiniFunction<Args, NewReturns>;
}
export const ZodMiniFunction: core.$constructor<ZodMiniFunction> = /*@__PURE__*/ core.$constructor(
"ZodMiniFunction",
(inst, def) => {
core.$ZodFunction.init(inst, def);
ZodMiniType.init(inst, def);
}
);
export function _function(): ZodMiniFunction;
export function _function<const In extends Array<SomeType> = Array<SomeType>>(params: {
input: In;
}): ZodMiniFunction<ZodMiniTuple<In, null>, core.$ZodFunctionOut>;
export function _function<
const In extends Array<SomeType> = Array<SomeType>,
const Out extends core.$ZodFunctionOut = core.$ZodFunctionOut,
>(params: {
input: In;
output: Out;
}): ZodMiniFunction<ZodMiniTuple<In, null>, Out>;
export function _function<const In extends core.$ZodFunctionIn = core.$ZodFunctionIn>(params: {
input: In;
}): ZodMiniFunction<In, core.$ZodFunctionOut>;
export function _function<const Out extends core.$ZodFunctionOut = core.$ZodFunctionOut>(params: {
output: Out;
}): ZodMiniFunction<core.$ZodFunctionIn, Out>;
export function _function<
In extends core.$ZodFunctionIn = core.$ZodFunctionIn,
Out extends core.$ZodFunctionOut = core.$ZodFunctionOut,
>(params?: {
input: In;
output: Out;
}): ZodMiniFunction<In, Out>;
export function _function(params?: {
output?: core.$ZodFunctionOut;
input?: core.$ZodFunctionArgs | Array<SomeType>;
}): ZodMiniFunction {
return new ZodMiniFunction({
type: "function",
input: Array.isArray(params?.input) ? tuple(params?.input as any) : (params?.input ?? array(unknown())),
output: params?.output ?? unknown(),
});
}
export { _function as function };
+1 -1
View File
@@ -1,6 +1,6 @@
import { test } from "vitest";
import * as z from "zod/v4-mini";
import * as z from "zod/mini";
test("assignability", () => {
// $ZodString
+1 -1
View File
@@ -1,5 +1,5 @@
import { expect, test } from "vitest";
import * as z from "zod/v4-mini";
import * as z from "zod/mini";
import { util as zc } from "zod/v4/core";
test("min/max", () => {
+1 -1
View File
@@ -1,5 +1,5 @@
import { expect, test } from "vitest";
import * as z from "zod/v4-mini";
import * as z from "zod/mini";
test("no locale by default", () => {
const result = z.safeParse(z.string(), 12);
-38
View File
@@ -1,43 +1,5 @@
import { expect, test } from "vitest";
// import * as z from "zod/v4/core";
test("z.function", () => {
expect(true).toEqual(true);
});
// test("z.function", () => {
// const a = z.function({
// args: z.tuple([z.string()]),
// returns: z.string(),
// });
// const myFunc = a.implement((name: string | number) => `Hello, ${name}!`);
// expect(myFunc("world")).toEqual("Hello, world!");
// expect(() => myFunc(123 as any)).toThrow();
// // this won't run
// () => {
// // @ts-expect-error
// const r = myFunc(123);
// expectTypeOf(r).toEqualTypeOf<string>();
// };
// });
// test("z.function async", async () => {
// const b = z.function({
// args: z.tuple([z.string()]).$check(async (_) => {}),
// returns: z.string().$check(async (_) => {}),
// });
// const myFuncAsync = b.implementAsync(async (name) => `Hello, ${name}!`);
// expect(await myFuncAsync("world")).toEqual("Hello, world!");
// expect(myFuncAsync(123 as any)).rejects.toThrow();
// // this won't run
// () => {
// // @ts-expect-error
// const r = myFuncAsync(123);
// expectTypeOf(r).toEqualTypeOf<Promise<string>>();
// };
// });
+53 -2
View File
@@ -1,5 +1,5 @@
import { expect, expectTypeOf, test } from "vitest";
import * as z from "zod/v4-mini";
import * as z from "zod/mini";
import type { util } from "zod/v4/core";
test("z.boolean", () => {
@@ -296,6 +296,29 @@ test("z.record", () => {
expect(() => z.parse(c, { a: "hello", b: "world" })).toThrow();
// extra keys
expect(() => z.parse(c, { a: "hello", b: "world", c: "world", d: "world" })).toThrow();
// literal union keys
const d = z.record(z.union([z.literal("a"), z.literal(0)]), z.string());
type d = z.output<typeof d>;
expectTypeOf<d>().toEqualTypeOf<Record<"a" | 0, string>>();
expect(z.parse(d, { a: "hello", 0: "world" })).toEqual({
a: "hello",
0: "world",
});
// TypeScript enum keys
enum Enum {
A = 0,
B = "hi",
}
const e = z.record(z.enum(Enum), z.string());
type e = z.output<typeof e>;
expectTypeOf<e>().toEqualTypeOf<Record<Enum, string>>();
expect(z.parse(e, { [Enum.A]: "hello", [Enum.B]: "world" })).toEqual({
[Enum.A]: "hello",
[Enum.B]: "world",
});
});
test("z.map", () => {
@@ -789,7 +812,7 @@ test("z.stringbool", () => {
test("z.promise", async () => {
const a = z.promise(z.string());
type a = z.output<typeof a>;
expectTypeOf<a>().toEqualTypeOf<string>();
expectTypeOf<a>().toEqualTypeOf<Promise<string>>();
expect(await z.safeParseAsync(a, Promise.resolve("hello"))).toMatchObject({
success: true,
@@ -869,3 +892,31 @@ test("def typing", () => {
z.catch(z.string(), "fallback").def.type satisfies "catch";
z.file().def.type satisfies "file";
});
test("defaulted object schema returns shallow clone", () => {
const schema = z._default(
z.object({
a: z.string(),
}),
{ a: "x" }
);
const result1 = schema.parse(undefined);
const result2 = schema.parse(undefined);
expect(result1).not.toBe(result2);
expect(result1).toEqual(result2);
});
test("runtime type property exists and returns correct values", () => {
const stringSchema = z.string();
expect(stringSchema.type).toBe("string");
});
test("type narrowing works with type property", () => {
type ArrayOrRecord = z.ZodMiniArray<z.ZodMiniString> | z.ZodMiniRecord<z.ZodMiniString<string>, z.ZodMiniAny>;
const arraySchema = z.array(z.string()) as ArrayOrRecord;
if (arraySchema.type === "array") {
expectTypeOf(arraySchema).toEqualTypeOf<z.ZodMiniArray<z.ZodMiniString<unknown>>>();
expect(arraySchema.def.element).toBeDefined();
}
});
+1 -1
View File
@@ -1,5 +1,5 @@
import { expect, expectTypeOf, test } from "vitest";
import * as z from "zod/v4-mini";
import * as z from "zod/mini";
test("z.number", () => {
const a = z.number();
+16 -1
View File
@@ -1,5 +1,5 @@
import { expect, expectTypeOf, test } from "vitest";
import * as z from "zod/v4-mini";
import * as z from "zod/mini";
test("z.object", () => {
const a = z.object({
@@ -89,6 +89,12 @@ test("z.keyof", () => {
type UserKeys = z.infer<typeof userKeysSchema>;
expectTypeOf<UserKeys>().toEqualTypeOf<"name" | "age" | "email">();
expect(userKeysSchema).toBeDefined();
expect(userKeysSchema._zod.def.type).toBe("enum");
expect(userKeysSchema._zod.def.entries).toEqual({
name: "name",
age: "age",
email: "email",
});
expect(z.safeParse(userKeysSchema, "name").success).toBe(true);
expect(z.safeParse(userKeysSchema, "age").success).toBe(true);
expect(z.safeParse(userKeysSchema, "email").success).toBe(true);
@@ -110,6 +116,15 @@ test("z.extend", () => {
expect(z.safeParse(extendedSchema, { name: "John", age: 30, isAdmin: true }).success).toBe(true);
});
test("z.safeExtend", () => {
const extended = z.safeExtend(userSchema, { name: z.string() });
expect(z.safeParse(extended, { name: "John", age: 30 }).success).toBe(true);
type Extended = z.infer<typeof extended>;
expectTypeOf<Extended>().toEqualTypeOf<{ name: string; age: number; email?: string }>();
// @ts-expect-error
z.safeExtend(userSchema, { name: z.number() });
});
test("z.pick", () => {
const pickedSchema = z.pick(userSchema, { name: true, email: true });
type PickedUser = z.infer<typeof pickedSchema>;
+1 -1
View File
@@ -1,5 +1,5 @@
import { expect, test } from "vitest";
import * as z from "zod/v4-mini";
import * as z from "zod/mini";
declare module "zod/v4/core" {
interface $ZodType {
+1 -1
View File
@@ -1,5 +1,5 @@
import { expect, expectTypeOf, test } from "vitest";
import { z } from "zod/v4-mini";
import { z } from "zod/mini";
test("recursion with z.lazy", () => {
const data = {
+49 -1
View File
@@ -1,5 +1,5 @@
import { expect, expectTypeOf, test } from "vitest";
import * as z from "zod/v4-mini";
import * as z from "zod/mini";
const FAIL = { success: false };
@@ -241,6 +241,38 @@ test("z.ipv6", () => {
expect(() => z.parse(a, 123)).toThrow();
});
test("z.mac", () => {
const a = z.mac();
// valid mac
expect(z.parse(a, "00:1A:2B:3C:4D:5E")).toEqual("00:1A:2B:3C:4D:5E");
// invalid mac (dash delimiter not accepted by default)
expect(() => z.parse(a, "01-23-45-67-89-AB")).toThrow();
expect(() => z.parse(a, "00:1A:2B::4D:5E")).toThrow();
expect(() => z.parse(a, "00:1a-2B:3c-4D:5e")).toThrow();
expect(() => z.parse(a, "hello")).toThrow();
// wrong type
expect(() => z.parse(a, 123)).toThrow();
});
test("z.mac with custom delimiter", () => {
const a = z.mac({ delimiter: ":" });
// valid mac with colon
expect(z.parse(a, "00:1A:2B:3C:4D:5E")).toEqual("00:1A:2B:3C:4D:5E");
// invalid mac with dash
expect(() => z.parse(a, "00-1A-2B-3C-4D-5E")).toThrow();
const b = z.mac({ delimiter: "-" });
// valid mac with dash
expect(z.parse(b, "00-1A-2B-3C-4D-5E")).toEqual("00-1A-2B-3C-4D-5E");
// invalid mac with colon
expect(() => z.parse(b, "00:1A:2B:3C:4D:5E")).toThrow();
const c = z.mac({ delimiter: ":" });
// colon-only mac
expect(z.parse(c, "00:1A:2B:3C:4D:5E")).toEqual("00:1A:2B:3C:4D:5E");
expect(() => z.parse(c, "00-1A-2B-3C-4D-5E")).toThrow();
});
test("z.base64", () => {
const a = z.base64();
// valid base64
@@ -297,3 +329,19 @@ test("z.jwt", () => {
// wrong type
expect(() => z.parse(a, 123)).toThrow();
});
test("z.hash generic format", () => {
expect(z.hash("sha256").parse("a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3")).toBe(
"a665a45920422f9d417e4867efdc4fb8a04a1f3fff1fa07e998e86f7f7a27ae3"
);
// --- Type-level checks (ensure the literal format string is encoded in the return type)
expectTypeOf(z.hash("md5")).toEqualTypeOf<z.ZodMiniCustomStringFormat<"md5_hex">>();
expectTypeOf(z.hash("sha1")).toEqualTypeOf<z.ZodMiniCustomStringFormat<"sha1_hex">>();
expectTypeOf(z.hash("sha256", { enc: "base64" as const })).toEqualTypeOf<
z.ZodMiniCustomStringFormat<"sha256_base64">
>();
expectTypeOf(z.hash("sha384", { enc: "base64url" as const })).toEqualTypeOf<
z.ZodMiniCustomStringFormat<"sha384_base64url">
>();
});