mirror of
https://github.com/colinhacks/zod.git
synced 2026-10-28 08:35:41 +08:00
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:
1 parent
c5b9bcb39b
commit
22bed61328
74 files changed
+280
-1
No files matched your search
@@ -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 ---
|
||||
|
||||
@@ -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") {
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
});
|
||||
});
|
||||
@@ -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">;
|
||||
|
||||
@@ -711,6 +711,7 @@ export type $ZodStringFormats =
|
||||
| "json_string"
|
||||
| "e164"
|
||||
| "credit_card"
|
||||
| "iban"
|
||||
| "lowercase"
|
||||
| "uppercase"
|
||||
| "regex"
|
||||
|
||||
@@ -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" ||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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>
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "نَص على هيئة JSON",
|
||||
e164: "رقم هاتف بمعيار E.164",
|
||||
credit_card: "رقم بطاقة الائتمان",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "مدخل",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -109,6 +109,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON радок",
|
||||
e164: "нумар E.164",
|
||||
credit_card: "нумар крэдытнай карты",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "увод",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON низ",
|
||||
e164: "E.164 номер",
|
||||
credit_card: "номер на кредитна карта",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "вход",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON স্ট্রিং",
|
||||
e164: "E.164 নম্বর",
|
||||
credit_card: "ক্রেডিট কার্ড নম্বর",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "ইনপুট",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "دەقی JSON",
|
||||
e164: "ژمارەی E.164",
|
||||
credit_card: "ژمارەی کارتی کرێدیت",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "تێکردە",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "συμβολοσειρά JSON",
|
||||
e164: "αριθμός E.164",
|
||||
credit_card: "αριθμός πιστωτικής κάρτας",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "είσοδος",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON رشته",
|
||||
e164: "E.164 عدد",
|
||||
credit_card: "شماره کارت اعتباری",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "ورودی",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON સ્ટ્રિંગ",
|
||||
e164: "E.164 નંબર",
|
||||
credit_card: "ક્રેડિટ કાર્ડ નંબર",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "ઇનપુટ",
|
||||
};
|
||||
|
||||
@@ -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" },
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON स्ट्रिंग",
|
||||
e164: "E.164 संख्या",
|
||||
credit_card: "क्रेडिट कार्ड संख्या",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "इनपुट",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -95,6 +95,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON տող",
|
||||
e164: "E.164 համար",
|
||||
credit_card: "կրեդիտ քարտի համար",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "մուտք",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON文字列",
|
||||
e164: "E.164番号",
|
||||
credit_card: "クレジットカード番号",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "入力値",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON ველი",
|
||||
e164: "E.164 ნომერი",
|
||||
credit_card: "საკრედიტო ბარათის ნომერი",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "შეყვანა",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "ខ្សែអក្សរ JSON",
|
||||
e164: "លេខ E.164",
|
||||
credit_card: "លេខប័ណ្ណឥណទាន",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "ទិន្នន័យបញ្ចូល",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSONಸ್ಟ್ರಿಂಗ್",
|
||||
e164: "E.164 ಸಂಖ್ಯೆ",
|
||||
credit_card: "ಕ್ರೆಡಿಟ್ ಕಾರ್ಡ್ ಸಂಖ್ಯೆ",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "ಇನ್ಪುಟ್",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON 문자열",
|
||||
e164: "E.164 번호",
|
||||
credit_card: "신용카드 번호",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "입력",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON низа",
|
||||
e164: "E.164 број",
|
||||
credit_card: "број на кредитна картичка",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "внес",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON स्ट्रिङ",
|
||||
e164: "E.164 नम्बर",
|
||||
credit_card: "क्रेडिट कार्ड नम्बर",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "इनपुट",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON متن",
|
||||
e164: "د E.164 شمېره",
|
||||
credit_card: "د کریډیټ کارت شمیره",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "ورودي",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -109,6 +109,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON строка",
|
||||
e164: "номер E.164",
|
||||
credit_card: "номер кредитной карты",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "ввод",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON சரம்",
|
||||
e164: "E.164 எண்",
|
||||
credit_card: "கடன் அட்டை எண்",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "input",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "ข้อความแบบ JSON",
|
||||
e164: "เบอร์โทรศัพท์ระหว่างประเทศ (E.164)",
|
||||
credit_card: "หมายเลขบัตรเครดิต",
|
||||
iban: "IBAN",
|
||||
jwt: "โทเคน JWT",
|
||||
template_literal: "ข้อมูลที่ป้อน",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "рядок JSON",
|
||||
e164: "номер E.164",
|
||||
credit_card: "номер кредитної картки",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "вхідні дані",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "جے ایس او این سٹرنگ",
|
||||
e164: "ای 164 نمبر",
|
||||
credit_card: "کریڈٹ کارڈ نمبر",
|
||||
iban: "IBAN",
|
||||
jwt: "جے ڈبلیو ٹی",
|
||||
template_literal: "ان پٹ",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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",
|
||||
};
|
||||
|
||||
@@ -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é",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON字符串",
|
||||
e164: "E.164号码",
|
||||
credit_card: "信用卡号",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "输入",
|
||||
};
|
||||
|
||||
@@ -46,6 +46,7 @@ const error: () => errors.$ZodErrorMap = () => {
|
||||
json_string: "JSON 字串",
|
||||
e164: "E.164 數值",
|
||||
credit_card: "信用卡號",
|
||||
iban: "IBAN",
|
||||
jwt: "JWT",
|
||||
template_literal: "輸入",
|
||||
};
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
Reference in new issue
Block a user