Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion .npmrc
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
link-workspace-packages=true
prefer-workspace-packages=true
# prefer-workspace-packages=true
hoist-workspace-packages=false
save-workspace-protocol=false
3 changes: 1 addition & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,7 @@
"tsx": "^4.19.2",
"typescript": "~5.5.0",
"vitest": "^2.0.0",
"zod": "workspace:*",
"zod3": "npm:zod@^3.24.0"
"zod": "workspace:*"
},
"lint-staged": {
"src/*.ts": ["biome format --write", "biome lint --write"],
Expand Down
40 changes: 36 additions & 4 deletions packages/core/src/api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -301,6 +301,38 @@ export function _ipv6<T extends schemas.$ZodIPv6>(
});
}

// CIDRv4
export type $ZodCIDRv4Params = util.StringFormatParams<schemas.$ZodCIDRv4, "pattern">;
export type $ZodCheckCIDRv4Params = util.CheckStringFormatParams<schemas.$ZodCIDRv4, "pattern">;
export function _cidrv4<T extends schemas.$ZodCIDRv4>(
Class: util.SchemaClass<T>,
params?: string | $ZodCIDRv4Params | $ZodCheckCIDRv4Params
): T {
return new Class({
type: "string",
format: "cidrv4",
check: "string_format",
abort: false,
...util.normalizeParams(params),
});
}

// CIDRv6
export type $ZodCIDRv6Params = util.StringFormatParams<schemas.$ZodCIDRv6, "pattern">;
export type $ZodCheckCIDRv6Params = util.CheckStringFormatParams<schemas.$ZodCIDRv6, "pattern">;
export function _cidrv6<T extends schemas.$ZodCIDRv6>(
Class: util.SchemaClass<T>,
params?: string | $ZodCIDRv6Params | $ZodCheckCIDRv6Params
): T {
return new Class({
type: "string",
format: "cidrv6",
check: "string_format",
abort: false,
...util.normalizeParams(params),
});
}

// Base64
export type $ZodBase64Params = util.StringFormatParams<schemas.$ZodBase64, "pattern">;
export type $ZodCheckBase64Params = util.CheckStringFormatParams<schemas.$ZodBase64, "pattern">;
Expand Down Expand Up @@ -358,7 +390,7 @@ export function _isoDateTime<T extends schemas.$ZodISODateTime>(
): T {
return new Class({
type: "string",
format: "iso_datetime",
format: "datetime",
check: "string_format",
offset: false,
local: false,
Expand All @@ -376,7 +408,7 @@ export function _isoDate<T extends schemas.$ZodISODate>(
): T {
return new Class({
type: "string",
format: "iso_date",
format: "date",
check: "string_format",
...util.normalizeParams(params),
});
Expand All @@ -391,7 +423,7 @@ export function _isoTime<T extends schemas.$ZodISOTime>(
): T {
return new Class({
type: "string",
format: "iso_time",
format: "time",
check: "string_format",
precision: null,
...util.normalizeParams(params),
Expand All @@ -407,7 +439,7 @@ export function _isoDuration<T extends schemas.$ZodISODuration>(
): T {
return new Class({
type: "string",
format: "iso_duration",
format: "duration",
check: "string_format",
...util.normalizeParams(params),
});
Expand Down
10 changes: 6 additions & 4 deletions packages/core/src/checks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -731,12 +731,14 @@ export type $ZodStringFormats =
| "ulid"
| "xid"
| "ksuid"
| "iso_datetime"
| "iso_date"
| "iso_time"
| "iso_duration"
| "datetime"
| "date"
| "time"
| "duration"
| "ipv4"
| "ipv6"
| "cidrv4"
| "cidrv6"
| "base64"
| "json_string"
| "e164"
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/locales/en-GB.ts
Original file line number Diff line number Diff line change
Expand Up @@ -30,10 +30,10 @@ const Nouns: {
ulid: "ULID",
xid: "XID",
ksuid: "KSUID",
iso_datetime: "ISO datetime",
iso_date: "ISO date",
iso_time: "ISO time",
iso_duration: "ISO duration",
datetime: "ISO datetime",
date: "ISO date",
time: "ISO time",
duration: "ISO duration",
ipv4: "IPv4 address",
ipv6: "IPv6 address",
base64: "base64-encoded string",
Expand Down
8 changes: 4 additions & 4 deletions packages/core/src/locales/en.ts
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ const Nouns: {
ulid: "ULID",
xid: "XID",
ksuid: "KSUID",
iso_datetime: "ISO datetime",
iso_date: "ISO date",
iso_time: "ISO time",
iso_duration: "ISO duration",
datetime: "ISO datetime",
date: "ISO date",
time: "ISO time",
duration: "ISO duration",
ipv4: "IPv4 address",
ipv6: "IPv6 address",
base64: "base64-encoded string",
Expand Down
9 changes: 7 additions & 2 deletions packages/core/src/regexes.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,7 @@ export const unicodeEmail = /^[^\s@"]{1,64}@[^\s@]{1,255}$/u;
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})+$`;
export function emoji(): RegExp {
return new RegExp(_emoji, "u");
Expand All @@ -56,8 +57,12 @@ 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 =
/^(([a-fA-F0-9]{1,4}:){1,7}[a-fA-F0-9]{1,4}|::|([a-fA-F0-9]{1,4})?::([a-fA-F0-9]{1,4}:?){0,6})$/;
// /^(([a-fA-F0-9]{1,4}:){7}|::([a-fA-F0-9]{1,4}:){0,6}|([a-fA-F0-9]{1,4}:){1}:([a-fA-F0-9]{1,4}:){0,5}|([a-fA-F0-9]{1,4}:){2}:([a-fA-F0-9]{1,4}:){0,4}|([a-fA-F0-9]{1,4}:){3}:([a-fA-F0-9]{1,4}:){0,3}|([a-fA-F0-9]{1,4}:){4}:([a-fA-F0-9]{1,4}:){0,2}|([a-fA-F0-9]{1,4}:){5}:([a-fA-F0-9]{1,4}:){0,1})([a-fA-F0-9]{1,4}|(((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2}))\.){3}((25[0-5])|(2[0-4][0-9])|(1[0-9]{2})|([0-9]{1,2})))$/
/^(([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})$/;

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 =
/^(([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})\/(12[0-8]|1[01][0-9]|[1-9]?[0-9])$/;

export const ip: RegExp = new RegExp(`(${ipv4.source})|(${ipv6.source})`);

Expand Down
95 changes: 84 additions & 11 deletions packages/core/src/schemas.ts
Original file line number Diff line number Diff line change
Expand Up @@ -228,7 +228,7 @@ export const $ZodType: core.$constructor<$ZodType> = /*@__PURE__*/ core.$constru
};
}

util.defineLazy(inst, "~standard", () => ({
inst["~standard"] = {
validate: (value: unknown) => {
const result = inst._zod.run({ value, issues: [] }, {});
if (result instanceof Promise) {
Expand All @@ -242,7 +242,7 @@ export const $ZodType: core.$constructor<$ZodType> = /*@__PURE__*/ core.$constru
},
vendor: "zod",
version: 1 as const,
}));
};
});

export { clone } from "./util.js";
Expand Down Expand Up @@ -520,7 +520,7 @@ export const $ZodKSUID: core.$constructor<$ZodKSUID> = /*@__PURE__*/ core.$const

////////////////////////////// ZodISODateTime //////////////////////////////

export interface $ZodISODateTimeDef extends $ZodStringFormatDef<"iso_datetime"> {
export interface $ZodISODateTimeDef extends $ZodStringFormatDef<"datetime"> {
precision: number | null;
offset: boolean;
local: boolean;
Expand All @@ -544,8 +544,8 @@ export const $ZodISODateTime: core.$constructor<$ZodISODateTime> = /*@__PURE__*/

////////////////////////////// ZodISODate //////////////////////////////

export interface $ZodISODateDef extends $ZodStringFormatDef<"iso_date"> {}
export interface $ZodISODateInternals extends $ZodStringFormatInternals<"iso_date"> {}
export interface $ZodISODateDef extends $ZodStringFormatDef<"date"> {}
export interface $ZodISODateInternals extends $ZodStringFormatInternals<"date"> {}

export interface $ZodISODate extends $ZodType {
_zod: $ZodISODateInternals;
Expand All @@ -561,13 +561,13 @@ export const $ZodISODate: core.$constructor<$ZodISODate> = /*@__PURE__*/ core.$c

////////////////////////////// ZodISOTime //////////////////////////////

export interface $ZodISOTimeDef extends $ZodStringFormatDef<"iso_time"> {
export interface $ZodISOTimeDef extends $ZodStringFormatDef<"time"> {
precision?: number | null;
// offset?: boolean;
// local?: boolean;
}

export interface $ZodISOTimeInternals extends $ZodStringFormatInternals<"iso_time"> {
export interface $ZodISOTimeInternals extends $ZodStringFormatInternals<"time"> {
def: $ZodISOTimeDef;
}

Expand All @@ -585,8 +585,8 @@ export const $ZodISOTime: core.$constructor<$ZodISOTime> = /*@__PURE__*/ core.$c

////////////////////////////// ZodISODuration //////////////////////////////

export interface $ZodISODurationDef extends $ZodStringFormatDef<"iso_duration"> {}
export interface $ZodISODurationInternals extends $ZodStringFormatInternals<"iso_duration"> {}
export interface $ZodISODurationDef extends $ZodStringFormatDef<"duration"> {}
export interface $ZodISODurationInternals extends $ZodStringFormatInternals<"duration"> {}

export interface $ZodISODuration extends $ZodType {
_zod: $ZodISODurationInternals;
Expand Down Expand Up @@ -623,6 +623,7 @@ export const $ZodIPv4: core.$constructor<$ZodIPv4> = /*@__PURE__*/ core.$constru
inst._zod.computed.format = `ipv4`;
};
});

////////////////////////////// ZodIPv6 //////////////////////////////

export interface $ZodIPv6Def extends $ZodStringFormatDef<"ipv6"> {
Expand Down Expand Up @@ -661,6 +662,78 @@ export const $ZodIPv6: core.$constructor<$ZodIPv6> = /*@__PURE__*/ core.$constru
};
});

////////////////////////////// ZodCIDRv4 //////////////////////////////

export interface $ZodCIDRv4Def extends $ZodStringFormatDef<"cidrv4"> {
version?: "v4";
}

export interface $ZodCIDRv4Internals extends $ZodStringFormatInternals<"cidrv4"> {
def: $ZodCIDRv4Def;
}

export interface $ZodCIDRv4 extends $ZodType {
_zod: $ZodCIDRv4Internals;
}

export const $ZodCIDRv4: core.$constructor<$ZodCIDRv4> = /*@__PURE__*/ core.$constructor(
"$ZodCIDRv4",
(inst, def): void => {
def.pattern ??= regexes.cidrv4;
$ZodStringFormat.init(inst, def);
const superAttach = inst._zod.onattach;
inst._zod.onattach = (inst) => {
superAttach?.(inst);
inst._zod.computed.format = `cidrv4`;
};
}
);

////////////////////////////// ZodCIDRv6 //////////////////////////////

export interface $ZodCIDRv6Def extends $ZodStringFormatDef<"cidrv6"> {
version?: "v6";
}

export interface $ZodCIDRv6Internals extends $ZodStringFormatInternals<"cidrv6"> {
def: $ZodCIDRv6Def;
}

export interface $ZodCIDRv6 extends $ZodType {
_zod: $ZodCIDRv6Internals;
}

export const $ZodCIDRv6: core.$constructor<$ZodCIDRv6> = /*@__PURE__*/ core.$constructor(
"$ZodCIDRv6",
(inst, def): void => {
def.pattern ??= regexes.cidrv6; // not used for validation
$ZodStringFormat.init(inst, def);
const superAttach = inst._zod.onattach;
inst._zod.onattach = (inst) => {
superAttach?.(inst);
inst._zod.computed.format = `cidrv6`;
};

inst._zod.check = (payload) => {
const [address, prefix] = payload.value.split("/");
try {
if (!prefix) throw new Error();
const prefixNum = Number(prefix);
if (`${prefixNum}` !== prefix) throw new Error();
if (prefixNum < 0 || prefixNum > 128) throw new Error();
new URL(`http://[${address}]`);
} catch {
payload.issues.push({
code: "invalid_format",
format: "cidrv6",
input: payload.value,
inst,
});
}
};
}
);

////////////////////////////// ZodIP //////////////////////////////

// export interface $ZodIPDef extends $ZodStringFormatDef<"ip"> {
Expand Down Expand Up @@ -2086,8 +2159,8 @@ function mergeValues(
return { valid: true, data: a };
}
if (util.isPlainObject(a) && util.isPlainObject(b)) {
const bKeys = util.objectKeys(b);
const sharedKeys = util.objectKeys(a).filter((key) => bKeys.indexOf(key) !== -1);
const bKeys = Object.keys(b);
const sharedKeys = Object.keys(a).filter((key) => bKeys.indexOf(key) !== -1);

const newObj: any = { ...a, ...b };
for (const key of sharedKeys) {
Expand Down
15 changes: 4 additions & 11 deletions packages/core/src/to-json-schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -54,17 +54,10 @@ interface EmitParams {
}

const formatMap: Partial<Record<checks.$ZodStringFormats, string>> = {
email: "email",
iso_datetime: "date-time",
iso_date: "date",
iso_time: "time",
iso_duration: "duration",
// hostname: "hostname",
ipv4: "ipv4",
ipv6: "ipv6",
uuid: "uuid",
guid: "uuid",
url: "uri",
datetime: "date-time",
json_string: "json-string",
};

interface Seen {
Expand Down Expand Up @@ -173,8 +166,8 @@ export class JSONSchemaGenerator {
if (minimum) json.minLength = minimum;
if (maximum) json.maxLength = maximum;
// custom pattern overrides format
if (format && formatMap[format]) {
json.format = formatMap[format];
if (format) {
json.format = formatMap[format] ?? format;
} else if (pattern) {
json.pattern = pattern.source;
}
Expand Down
Loading