feat(v4): add z.iban() string format with mod-97 checksum (#6571)

Adds `z.iban()` to Zod and Zod Mini. Closes #6565.

Validation is shape plus [ISO 7064 MOD 97-10](https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN): a two-letter country code, two check digits, and 11 to 30 alphanumeric characters, with the checksum reduced in chunks so no `BigInt` is needed. The country registry is not consulted — any country code passes, and no country's own account length is enforced. Same reasoning that removed the issuer allowlist from `z.creditCard()` in #5931. The registry has its own update cadence, every entry is bytes in every bundle including `zod/mini`, and a mod-97-valid IBAN from an unlisted country would be turned away for nothing.

Check digits are bounded to 02–98 in the shape regex, which is the one change on top of the original branch. The generator computes them as `98 - remainder` with the remainder in 0–96, so 00, 01 and 99 are values no bank can issue, and a bare `mod 97 === 1` test accepts all three — roughly one account number in 97 lands on each. `ibantools` and `validator.js` both miss it. Keeping the bound in the pattern rather than the checksum routine means `toJSONSchema()` and template literals carry it too.

Electronic format only: no spaces, no hyphens, uppercase. `z.creditCard()` accepts single spaces and hyphens and banks do print IBANs grouped in fours, so the two formats disagree. Widening a format later is non-breaking and narrowing is not, and the spaced form still has to be stripped before a payment API will take it.

`zod/mini` pays nothing unless it calls `z.iban()`. Classic pays 12 raw bytes for the locale label, and a mini bundle that does call it pays 43 gzipped bytes. A `z.iban()` instance is shape-identical to a `z.creditCard()` one, and nothing is added to any existing parse path.

Thanks to @ismetcanbyk for the implementation, including all 60 locales with the Hebrew gendered form and the Portuguese article, and the compile-matrix and fuzz entries.

Co-authored-by: İsmet Can Bıyık <ismetcanbyk@gmail.com>
This commit is contained in:
Colin McDonnellandİsmet Can Bıyık authored and GitHub committed 2026-09-08 21:57:49 -07:00
1 parent c5b9bcb39b
commit 22bed61328
74 files changed
+280 -1

No files matched your search

+1
View File
@@ -43,6 +43,7 @@ add("string", "url", z.url(), "https://example.com/a/b?c=1");
add("string", "iso.datetime", z.iso.datetime(), "2026-08-16T12:00:00Z");
add("string", "base64", z.base64(), "aGVsbG8gd29ybGQ=");
add("string", "creditCard", z.creditCard(), "4111111111111111");
add("string", "iban", z.iban(), "DE89370400440532013000");
add("string", "trim + toLowerCase", z.string().trim().toLowerCase(), " MiXeD Case ");
// --- numbers ---
+15
View File
@@ -274,6 +274,7 @@ z.mac();
z.cidrv4(); // ipv4 CIDR block
z.cidrv6(); // ipv6 CIDR block
z.creditCard(); // credit card number (Luhn checksum)
z.iban(); // IBAN (ISO 7064 mod-97 checksum)
z.hash("sha256"); // or "sha1", "sha384", "sha512", "md5"
z.iso.date();
z.iso.time();
@@ -586,6 +587,20 @@ card.parse("4111.1111.1111.1111"); // ❌ spaces and hyphens only
card.parse("4111111111111112"); // ❌ failed checksum
```
### IBANs
Validate an International Bank Account Number (IBAN): 2-letter country code, 2 check digits, and 11 to 30 alphanumeric characters with a valid [ISO 7064 MOD 97-10](https://en.wikipedia.org/wiki/International_Bank_Account_Number#Validating_the_IBAN) checksum. The country registry is not consulted, so any country code is accepted and no country's own account length is enforced. Electronic format only.
```ts
const iban = z.iban();
iban.parse("DE89370400440532013000"); // ✅
iban.parse("NO9386011117947"); // ✅
iban.parse("DE89 3704 0044 0532 0130 00"); // ❌ no spaces
iban.parse("de89370400440532013000"); // ❌ uppercase only
iban.parse("DE89370400440532013001"); // ❌ failed checksum
iban.parse("DE00000000000000000066"); // ❌ check digits are always 02–98
```
### JWTs
Validate [JSON Web Tokens](https://jwt.io/).
@@ -542,6 +542,8 @@ function convertBaseSchema(schema: JSONSchema.JSONSchema, ctx: ConversionContext
stringSchema = stringSchema.check(z.e164());
} else if (format === "credit_card") {
stringSchema = stringSchema.check(z.creditCard());
} else if (format === "iban") {
stringSchema = stringSchema.check(z.iban());
} else if (format === "jwt") {
stringSchema = stringSchema.check(z.jwt());
} else if (format === "emoji") {
+13
View File
@@ -1030,6 +1030,19 @@ export function creditCard(params?: string | core.$ZodCreditCardParams): ZodCred
return core._creditCard(ZodCreditCard, params);
}
// ZodIBAN
export interface ZodIBAN extends ZodStringFormat<"iban"> {
_zod: core.$ZodIBANInternals;
}
export const ZodIBAN: core.$constructor<ZodIBAN> = /*@__PURE__*/ core.$constructor("ZodIBAN", (inst, def) => {
core.$ZodIBAN.init(inst, def);
ZodStringFormat.init(inst, def);
});
export function iban(params?: string | core.$ZodIBANParams): ZodIBAN {
return core._iban(ZodIBAN, params);
}
// ZodJWT
export interface ZodJWT extends ZodStringFormat<"jwt"> {
_zod: core.$ZodJWTInternals;
@@ -0,0 +1,81 @@
import { describe, expect, test } from "vitest";
import * as z from "zod";
describe("z.iban", () => {
const valid: [string, string][] = [
["Norway (15, minimum)", "NO9386011117947"],
["Belgium (16)", "BE68539007547034"],
["Netherlands (18)", "NL91ABNA0417164300"],
["Germany (22)", "DE89370400440532013000"],
["Great Britain (22)", "GB29NWBK60161331926819"],
["Turkey (26)", "TR610000100000000000000001"],
["France (27)", "FR1420041010050500013M02606"],
["Cyprus (28)", "CY17002001280000001200527600"],
["Malta (31)", "MT84MALT011000012345MTLCAST001S"],
["Maximum length (34)", "AA73AAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"],
["Maximum length mixed (34)", "ZZ560123456789ABCDEFGHIJKLMNOPQRST"],
];
test.each(valid)("accepts %s", (_label, n) => {
expect(z.iban().parse(n)).toBe(n);
});
test("rejects lowercase characters", () => {
expect(() => z.iban().parse("no9386011117947")).toThrow();
expect(() => z.iban().parse("de89370400440532013000")).toThrow();
expect(() => z.iban().parse("FR1420041010050500013m02606")).toThrow();
});
test("rejects spaces and hyphens", () => {
expect(() => z.iban().parse("NO93 8601 1117 947")).toThrow();
expect(() => z.iban().parse(" DE89370400440532013000")).toThrow();
expect(() => z.iban().parse("DE89370400440532013000 ")).toThrow();
expect(() => z.iban().parse("NO93-8601-1117-947")).toThrow();
expect(() => z.iban().parse("DE89-3704-0044-0532-0130-00")).toThrow();
});
test("rejects special characters", () => {
expect(() => z.iban().parse("NO938601111794!")).toThrow();
expect(() => z.iban().parse("DE8937040044053201300@")).toThrow();
expect(() => z.iban().parse("")).toThrow();
});
test("rejects inputs outside length bounds (15-34)", () => {
expect(() => z.iban().parse("NO938601111794")).toThrow();
expect(() => z.iban().parse("AA73AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA")).toThrow();
});
test("rejects invalid check digits (checksum failure)", () => {
expect(() => z.iban().parse("NO9386011117948")).toThrow();
expect(() => z.iban().parse("DE89370400440532013001")).toThrow();
expect(() => z.iban().parse("FR1420041010050500013M02607")).toThrow();
});
test("rejects check digits outside 02-98", () => {
// mod 97 alone accepts all three; `98 - remainder` can only ever emit 02-98
expect(() => z.iban().parse("DE00000000000000000066")).toThrow();
expect(() => z.iban().parse("DE01000000000000000048")).toThrow();
expect(() => z.iban().parse("DE99000000000000000030")).toThrow();
});
test("carries the shape regex as its JSON Schema pattern", () => {
expect(z.toJSONSchema(z.iban())).toMatchObject({
format: "iban",
pattern: z.regexes.iban.source,
});
});
test("round-trips through JSON Schema with the checksum intact", () => {
const schema = z.fromJSONSchema(z.toJSONSchema(z.iban()));
expect(schema.safeParse("DE89370400440532013000").success).toBe(true);
expect(schema.safeParse("DE89370400440532013001").success).toBe(false);
});
test("error message references IBAN", () => {
const result = z.iban().safeParse("not-an-iban");
expect(result.success).toBe(false);
if (!result.success) {
expect(result.error.issues[0]?.message).toMatch(/IBAN/i);
}
});
});
+17
View File
@@ -498,6 +498,23 @@ export function _creditCard<T extends schemas.$ZodCreditCard>(
});
}
// IBAN
export type $ZodIBANParams = StringFormatParams<schemas.$ZodIBAN, "pattern" | "when">;
export type $ZodCheckIBANParams = CheckStringFormatParams<schemas.$ZodIBAN, "pattern" | "when">;
// @__NO_SIDE_EFFECTS__
export function _iban<T extends schemas.$ZodIBAN>(
Class: util.SchemaClass<T>,
params?: string | $ZodIBANParams | $ZodCheckIBANParams
): T {
return new Class({
type: "string",
format: "iban",
check: "string_format",
abort: false,
...util.normalizeParams(params),
});
}
// JWT
export type $ZodJWTParams = StringFormatParams<schemas.$ZodJWT, "pattern" | "when">;
export type $ZodCheckJWTParams = CheckStringFormatParams<schemas.$ZodJWT, "pattern" | "when">;
+1
View File
@@ -711,6 +711,7 @@ export type $ZodStringFormats =
| "json_string"
| "e164"
| "credit_card"
| "iban"
| "lowercase"
| "uppercase"
| "regex"
+6
View File
@@ -9,6 +9,7 @@ import {
isValidBase64URL,
isValidCIDRv6,
isValidCreditCard,
isValidIBAN,
isValidIPv6,
isValidJWT,
mergeValues,
@@ -792,6 +793,11 @@ function generateStringFormatCheck(doc: Doc, ctx: CompileContext, def: StringFor
doc.write(`if (!${validator}(${accessor})) return INVALID;`);
return accessor;
}
if (fmt === "iban") {
const validator = addConstant(ctx, isValidIBAN);
doc.write(`if (!${validator}(${accessor})) return INVALID;`);
return accessor;
}
const formatDef = def as unknown as { normalize?: boolean; hostname?: unknown; protocol?: unknown };
if (
fmt === "url" ||
+3
View File
@@ -98,6 +98,9 @@ export const e164: RegExp = /^\+[1-9]\d{6,14}$/;
// Credit card shape: 12–19 digits, optionally separated by single spaces or single hyphens. ISO/IEC 7812 caps the PAN at 19 digits; 12 is the shortest issued length (Maestro).
export const creditCard: RegExp = /^\d(?:[ -]?\d){11,18}$/;
// iban electronic format: 2-letter country, check digits 02-98 (the only values `98 - remainder` can produce), 11-30 bban characters
export const iban: RegExp = /^[A-Z]{2}(?!00|01|99)\d{2}[A-Z0-9]{11,30}$/;
const dateSource = `(?:(?:\\d\\d[2468][048]|\\d\\d[13579][26]|\\d\\d0[48]|[02468][048]00|[13579][26]00)-02-29|\\d{4}-(?:(?:0[13578]|1[02])-(?:0[1-9]|[12]\\d|3[01])|(?:0[469]|11)-(?:0[1-9]|[12]\\d|30)|(?:02)-(?:0[1-9]|1\\d|2[0-8])))`;
/** Anchors a pattern source. The interpolation lives here rather than at the call site because
+48
View File
@@ -1144,6 +1144,53 @@ export const $ZodCreditCard: core.$constructor<$ZodCreditCard> = /*@__PURE__*/ c
}
);
////////////////////////////// ZodIBAN //////////////////////////////
// iso 7064 mod 97-10 checksum without BigInt
function isIso7064Mod97(iban: string): boolean {
let remainder = 0;
const len = iban.length;
for (let i = 4; i < len; i++) {
const code = iban.charCodeAt(i);
remainder = (code >= 65 ? remainder * 100 + (code - 55) : remainder * 10 + (code - 48)) % 97;
}
for (let i = 0; i < 4; i++) {
const code = iban.charCodeAt(i);
remainder = (code >= 65 ? remainder * 100 + (code - 55) : remainder * 10 + (code - 48)) % 97;
}
return remainder === 1;
}
export function isValidIBAN(input: string): boolean {
if (!regexes.iban.test(input)) return false;
return isIso7064Mod97(input);
}
export interface $ZodIBANDef extends $ZodStringFormatDef<"iban"> {}
export interface $ZodIBANInternals extends $ZodStringFormatInternals<"iban"> {
def: $ZodIBANDef;
}
export interface $ZodIBAN extends $ZodType {
_zod: $ZodIBANInternals;
}
export const $ZodIBAN: core.$constructor<$ZodIBAN> = /*@__PURE__*/ core.$constructor("$ZodIBAN", (inst, def): void => {
// shape only — checksum is not expressible as a pattern
def.pattern ??= regexes.iban;
$ZodStringFormat.init(inst, def);
inst._zod.check = (payload) => {
if (isValidIBAN(payload.value)) return;
payload.issues.push({
code: "invalid_format",
format: "iban",
input: payload.value,
inst,
continue: !def.abort,
});
};
});
////////////////////////////// ZodJWT //////////////////////////////
export function isValidJWT(token: string, algorithm: util.JWTAlgorithm | null = null): boolean {
@@ -5205,6 +5252,7 @@ export type $ZodStringFormatTypes =
| $ZodBase64URL
| $ZodE164
| $ZodCreditCard
| $ZodIBAN
| $ZodJWT
| $ZodCustomStringFormat<"hex">
| $ZodCustomStringFormat<util.HashFormat>
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "نَص على هيئة JSON",
e164: "رقم هاتف بمعيار E.164",
credit_card: "رقم بطاقة الائتمان",
iban: "IBAN",
jwt: "JWT",
template_literal: "مدخل",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON string",
e164: "E.164 number",
credit_card: "kredit kartı nömrəsi",
iban: "IBAN",
jwt: "JWT",
template_literal: "input",
};
+1
View File
@@ -109,6 +109,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON радок",
e164: "нумар E.164",
credit_card: "нумар крэдытнай карты",
iban: "IBAN",
jwt: "JWT",
template_literal: "увод",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON низ",
e164: "E.164 номер",
credit_card: "номер на кредитна карта",
iban: "IBAN",
jwt: "JWT",
template_literal: "вход",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON স্ট্রিং",
e164: "E.164 নম্বর",
credit_card: "ক্রেডিট কার্ড নম্বর",
iban: "IBAN",
jwt: "JWT",
template_literal: "ইনপুট",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "cadena JSON",
e164: "número E.164",
credit_card: "número de targeta de crèdit",
iban: "IBAN",
jwt: "JWT",
template_literal: "entrada",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "دەقی JSON",
e164: "ژمارەی E.164",
credit_card: "ژمارەی کارتی کرێدیت",
iban: "IBAN",
jwt: "JWT",
template_literal: "تێکردە",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "řetězec ve formátu JSON",
e164: "číslo E.164",
credit_card: "číslo kreditní karty",
iban: "IBAN",
jwt: "JWT",
template_literal: "vstup",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON-streng",
e164: "E.164-nummer",
credit_card: "kreditkortnummer",
iban: "IBAN",
jwt: "JWT",
template_literal: "input",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON-String",
e164: "E.164-Nummer",
credit_card: "Kreditkartennummer",
iban: "IBAN",
jwt: "JWT",
template_literal: "Eingabe",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "συμβολοσειρά JSON",
e164: "αριθμός E.164",
credit_card: "αριθμός πιστωτικής κάρτας",
iban: "IBAN",
jwt: "JWT",
template_literal: "είσοδος",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON string",
e164: "E.164 number",
credit_card: "credit card number",
iban: "IBAN",
jwt: "JWT",
template_literal: "input",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON-karaktraro",
e164: "E.164-nombro",
credit_card: "kreditkarta numero",
iban: "IBAN",
jwt: "JWT",
template_literal: "enigo",
};
+1
View File
@@ -45,6 +45,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "cadena JSON",
e164: "número E.164",
credit_card: "número de tarjeta de crédito",
iban: "IBAN",
jwt: "JWT",
template_literal: "entrada",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON رشته",
e164: "E.164 عدد",
credit_card: "شماره کارت اعتباری",
iban: "IBAN",
jwt: "JWT",
template_literal: "ورودی",
};
+1
View File
@@ -50,6 +50,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON-merkkijono",
e164: "E.164-luku",
credit_card: "luottokortin numero",
iban: "IBAN",
jwt: "JWT",
template_literal: "templaattimerkkijono",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "chaîne JSON",
e164: "numéro E.164",
credit_card: "numéro de carte de crédit",
iban: "IBAN",
jwt: "JWT",
template_literal: "entrée",
};
+1
View File
@@ -45,6 +45,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "chaîne de caractères JSON",
e164: "numéro au format E.164",
credit_card: "numéro de carte de crédit",
iban: "IBAN",
jwt: "JWT",
template_literal: "entrée",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON સ્ટ્રિંગ",
e164: "E.164 નંબર",
credit_card: "ક્રેડિટ કાર્ડ નંબર",
iban: "IBAN",
jwt: "JWT",
template_literal: "ઇનપુટ",
};
+1
View File
@@ -88,6 +88,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: { label: "מחרוזת JSON", gender: "f" },
e164: { label: "מספר E.164", gender: "m" },
credit_card: { label: "מספר כרטיס אשראי", gender: "m" },
iban: { label: "IBAN", gender: "m" },
jwt: { label: "JWT", gender: "m" },
template_literal: { label: "קלט", gender: "m" },
ends_with: { label: "קלט", gender: "m" },
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON स्ट्रिंग",
e164: "E.164 संख्या",
credit_card: "क्रेडिट कार्ड संख्या",
iban: "IBAN",
jwt: "JWT",
template_literal: "इनपुट",
};
+1
View File
@@ -45,6 +45,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON tekst",
e164: "E.164 broj",
credit_card: "broj kreditne kartice",
iban: "IBAN",
jwt: "JWT",
template_literal: "unos",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON string",
e164: "E.164 szám",
credit_card: "hitelkártyaszám",
iban: "IBAN",
jwt: "JWT",
template_literal: "bemenet",
};
+1
View File
@@ -95,6 +95,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON տող",
e164: "E.164 համար",
credit_card: "կրեդիտ քարտի համար",
iban: "IBAN",
jwt: "JWT",
template_literal: "մուտք",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "string JSON",
e164: "angka E.164",
credit_card: "nomor kartu kredit",
iban: "IBAN",
jwt: "JWT",
template_literal: "input",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON strengur",
e164: "E.164 tölugildi",
credit_card: "kreditkortanúmer",
iban: "IBAN",
jwt: "JWT",
template_literal: "gildi",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "stringa JSON",
e164: "numero E.164",
credit_card: "numero di carta di credito",
iban: "IBAN",
jwt: "JWT",
template_literal: "input",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON文字列",
e164: "E.164番号",
credit_card: "クレジットカード番号",
iban: "IBAN",
jwt: "JWT",
template_literal: "入力値",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON ველი",
e164: "E.164 ნომერი",
credit_card: "საკრედიტო ბარათის ნომერი",
iban: "IBAN",
jwt: "JWT",
template_literal: "შეყვანა",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "ខ្សែអក្សរ JSON",
e164: "លេខ E.164",
credit_card: "លេខប័ណ្ណឥណទាន",
iban: "IBAN",
jwt: "JWT",
template_literal: "ទិន្នន័យបញ្ចូល",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSONಸ್ಟ್ರಿಂಗ್",
e164: "E.164 ಸಂಖ್ಯೆ",
credit_card: "ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆ",
iban: "IBAN",
jwt: "JWT",
template_literal: "ಇನ್ಪುಟ್",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON 문자열",
e164: "E.164 번호",
credit_card: "신용카드 번호",
iban: "IBAN",
jwt: "JWT",
template_literal: "입력",
};
+1
View File
@@ -146,6 +146,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON eilutė",
e164: "E.164 numeris",
credit_card: "kredito kortelės numeris",
iban: "IBAN",
jwt: "JWT",
template_literal: "įvestis",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON низа",
e164: "E.164 број",
credit_card: "број на кредитна картичка",
iban: "IBAN",
jwt: "JWT",
template_literal: "внес",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "string JSON",
e164: "nombor E.164",
credit_card: "nombor kad kredit",
iban: "IBAN",
jwt: "JWT",
template_literal: "input",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON स्ट्रिङ",
e164: "E.164 नम्बर",
credit_card: "क्रेडिट कार्ड नम्बर",
iban: "IBAN",
jwt: "JWT",
template_literal: "इनपुट",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON string",
e164: "E.164-nummer",
credit_card: "creditcardnummer",
iban: "IBAN",
jwt: "JWT",
template_literal: "invoer",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON-streng",
e164: "E.164-nummer",
credit_card: "kredittkortnummer",
iban: "IBAN",
jwt: "JWT",
template_literal: "input",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON-streng",
e164: "E.164-nummer",
credit_card: "kredittkortnummer",
iban: "IBAN",
jwt: "JWT",
template_literal: "input",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON metin",
e164: "E.164 sayısı",
credit_card: "i'tibâr kartı numarası",
iban: "IBAN",
jwt: "JWT",
template_literal: "giren",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "ciąg znaków w formacie JSON",
e164: "liczba E.164",
credit_card: "numer karty kredytowej",
iban: "IBAN",
jwt: "JWT",
template_literal: "wejście",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON متن",
e164: "د E.164 شمېره",
credit_card: "د کریډیټ کارت شمیره",
iban: "IBAN",
jwt: "JWT",
template_literal: "ورودي",
};
+1
View File
@@ -47,6 +47,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "o texto JSON",
e164: "o número E.164",
credit_card: "o número de cartão de crédito",
iban: "o IBAN",
jwt: "o JWT",
template_literal: "a entrada",
};
+1
View File
@@ -47,6 +47,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "o texto JSON",
e164: "o número E.164",
credit_card: "o número de cartão de crédito",
iban: "o IBAN",
jwt: "o JWT",
template_literal: "a entrada",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "șir JSON",
e164: "număr E.164",
credit_card: "număr de card de credit",
iban: "IBAN",
jwt: "JWT",
template_literal: "intrare",
};
+1
View File
@@ -109,6 +109,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON строка",
e164: "номер E.164",
credit_card: "номер кредитной карты",
iban: "IBAN",
jwt: "JWT",
template_literal: "ввод",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "reťazec vo formáte JSON",
e164: "číslo E.164",
credit_card: "číslo kreditnej karty",
iban: "IBAN",
jwt: "JWT",
template_literal: "vstup",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON niz",
e164: "E.164 številka",
credit_card: "številka kreditne kartice",
iban: "IBAN",
jwt: "JWT",
template_literal: "vnos",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON-sträng",
e164: "E.164-nummer",
credit_card: "kreditkortsnummer",
iban: "IBAN",
jwt: "JWT",
template_literal: "mall-literal",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON சரம்",
e164: "E.164 எண்",
credit_card: "கடன் அட்டை எண்",
iban: "IBAN",
jwt: "JWT",
template_literal: "input",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "ข้อความแบบ JSON",
e164: "เบอร์โทรศัพท์ระหว่างประเทศ (E.164)",
credit_card: "หมายเลขบัตรเครดิต",
iban: "IBAN",
jwt: "โทเคน JWT",
template_literal: "ข้อมูลที่ป้อน",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON setiri",
e164: "E.164 nomeri",
credit_card: "kredit kartynyň nomeri",
iban: "IBAN",
jwt: "JWT",
template_literal: "şablon",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON dizesi",
e164: "E.164 sayısı",
credit_card: "kredi kartı numarası",
iban: "IBAN",
jwt: "JWT",
template_literal: "Şablon dizesi",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "рядок JSON",
e164: "номер E.164",
credit_card: "номер кредитної картки",
iban: "IBAN",
jwt: "JWT",
template_literal: "вхідні дані",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "جے ایس او این سٹرنگ",
e164: "ای 164 نمبر",
credit_card: "کریڈٹ کارڈ نمبر",
iban: "IBAN",
jwt: "جے ڈبلیو ٹی",
template_literal: "ان پٹ",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON satr",
e164: "E.164 raqam",
credit_card: "kredit karta raqami",
iban: "IBAN",
jwt: "JWT",
template_literal: "kirish",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "chuỗi JSON",
e164: "số E.164",
credit_card: "số thẻ tín dụng",
iban: "IBAN",
jwt: "JWT",
template_literal: "đầu vào",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "ọ̀rọ̀ JSON",
e164: "nọ́mbà E.164",
credit_card: "nọmba kaadi gbese",
iban: "IBAN",
jwt: "JWT",
template_literal: "ẹ̀rọ ìbáwọlé",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON字符串",
e164: "E.164号码",
credit_card: "信用卡号",
iban: "IBAN",
jwt: "JWT",
template_literal: "输入",
};
+1
View File
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
json_string: "JSON 字串",
e164: "E.164 數值",
credit_card: "信用卡號",
iban: "IBAN",
jwt: "JWT",
template_literal: "輸入",
};
+15
View File
@@ -509,6 +509,21 @@ export function creditCard(params?: string | core.$ZodCreditCardParams): ZodMini
return core._creditCard(ZodMiniCreditCard, params);
}
// ZodMiniIBAN
export interface ZodMiniIBAN extends _ZodMiniString<core.$ZodIBANInternals> {}
export const ZodMiniIBAN: core.$constructor<ZodMiniIBAN> = /*@__PURE__*/ core.$constructor(
"ZodMiniIBAN",
(inst, def) => {
core.$ZodIBAN.init(inst, def);
ZodMiniStringFormat.init(inst, def);
}
);
// @__NO_SIDE_EFFECTS__
export function iban(params?: string | core.$ZodIBANParams): ZodMiniIBAN {
return core._iban(ZodMiniIBAN, params);
}
// ZodMiniJWT
export interface ZodMiniJWT extends _ZodMiniString<core.$ZodJWTInternals> {
// _zod: core.$ZodJWTInternals;
@@ -338,6 +338,22 @@ test("z.creditCard", () => {
expect(() => z.parse(a, 123)).toThrow();
});
test("z.iban", () => {
const a = z.iban();
// valid iban
expect(z.parse(a, "DE89370400440532013000")).toEqual("DE89370400440532013000");
expect(z.parse(a, "NO9386011117947")).toEqual("NO9386011117947");
// failed mod 97 checksum
expect(() => z.parse(a, "DE89370400440532013001")).toThrow();
// formatting violations
expect(() => z.parse(a, "de89370400440532013000")).toThrow();
expect(() => z.parse(a, "DE89 3704 0044 0532 0130 00")).toThrow();
expect(() => z.parse(a, "NO938601111794")).toThrow();
expect(() => z.parse(a, "hello")).toThrow();
// wrong type
expect(() => z.parse(a, 123)).toThrow();
});
test("z.jwt", () => {
const a = z.jwt();
// valid jwt
+1
View File
@@ -26,6 +26,7 @@ const leaves: (() => Built)[] = [
() => ({ schema: z.email(), gen: () => pick(["a@b.com", "nope"]) }),
() => ({ schema: z.uuid(), gen: () => pick(["550e8400-e29b-41d4-a716-446655440000", "no"]) }),
() => ({ schema: z.creditCard(), gen: () => pick(["4111111111111111", "4111111111111112"]) }),
() => ({ schema: z.iban(), gen: () => pick(["DE89370400440532013000", "DE89370400440532013001"]) }),
() => ({ schema: z.number(), gen: () => pick([0, -1, 3.5, Number.NaN, Number.POSITIVE_INFINITY]) }),
() => ({ schema: z.number().int().min(0).max(10), gen: () => pick([0, 5, 11, -1, 2.5]) }),
() => ({ schema: z.boolean(), gen: () => pick([true, false, "true"]) }),
+1 -1
View File
@@ -29,7 +29,7 @@ Consequences:
- User-supplied `.refine` / `.transform` / `.superRefine` callbacks run **at most twice** on invalid input — once in the fast path, once in the runtime fallback. This matches Zod's existing Standard Schema sync-then-async behavior. The bound is enforced, including under global mode where every nested schema carries its own compiled wrapper: when a wrapper falls back it flags the parse ctx, and downstream compiled wrappers skip their fast paths for the rest of that parse.
- Success-path *value* parity is the compiler's responsibility, verified via differential tests against the runtime (key order, `undefined`-valued vs absent keys, array holes, frozenness, and NaN/-0 included — plus a per-fixture assertion that the fast path actually produced the value rather than silently falling back). It is not free; it has to be earned per schema type and per check.
- Anything the fast path can't model *exactly* raises `ZodCompileUnsupportedError` at codegen time, which `compile()` absorbs by returning the schema uncompiled unless `strict` is set — there are no silently-dead always-fallback fast paths and no plain `Error` escapes, and a `new Function` failure (malformed codegen, CSP rejection) is converted to the same type. Containers island unsupported children; unions don't (a falsely-rejecting branch would corrupt match semantics), and `z.xor` always falls back for the same reason. Custom `when`-gated checks, NaN/Invalid-Date comparison bounds, and `__proto__` shape/record keys also force fallback.
- String formats are classified by an **allowlist** of formats whose `def.pattern` is the complete check, not by whether a pattern exists. Several formats advertise a shape-only pattern and validate the rest separately — `credit_card` (Luhn digit), `base64`, `base64url`, `jwt`, `ipv6`, `cidrv6`, `url` — and those hoist the runtime validator instead. A custom format compiles `def.fn`, the predicate the runtime itself calls, rather than trusting the pattern it was built from. A format that appears in neither list loses its fast path rather than compiling to a regex that accepts more than the runtime does.
- String formats are classified by an **allowlist** of formats whose `def.pattern` is the complete check, not by whether a pattern exists. Several formats advertise a shape-only pattern and validate the rest separately — `credit_card` (Luhn digit), `iban` (mod-97 checksum), `base64`, `base64url`, `jwt`, `ipv6`, `cidrv6`, `url` — and those hoist the runtime validator instead. A custom format compiles `def.fn`, the predicate the runtime itself calls, rather than trusting the pattern it was built from. A format that appears in neither list loses its fast path rather than compiling to a regex that accepts more than the runtime does.
## Scope cuts