Skip to content

feat: add examples and format patterns to validators (#108) - #258

Merged
koblas merged 1 commit into
mainfrom
feat/validator-examples
Nov 11, 2026
Merged

koblas merged 1 commit into
mainfrom
feat/validator-examples

Conversation

@koblas

@koblas koblas commented Nov 11, 2026

Copy link
Copy Markdown
Owner

Adds two optional fields to Validator, filled in for every validator, so apps can show a hint ("Enter your TFN, e.g. 123 456 782") or build an input mask:

  • examples: valid numbers in compact form.
  • formats: the shapes of the format() output, in IBAN registry notation: n is a digit, a a letter A-Z, c a digit or letter, anything else literal. For example br/cpf is nnn.nnn.nnn-nn.

Fixes #108

Changes

  • src/types.ts: the two fields, optional so third-party Validator objects still type-check.
  • All 360 validators (359 reachable from src/index.ts, plus gen/ean): 353 set the data on their impl, and 7 aliases (cl/run, sk/rc, pt/nipc, me/mk/rs/si jmbg) inherit it through their existing spread. Each export const { ... } = impl also exports examples and formats, so stdnum.XX.y.examples works.
  • src/examples.spec.ts: for every validator, checks that examples and formats are non-empty, that each example passes validate() and is unchanged by compact(), and that format(example) matches a declared pattern. Breaking an example, a pattern or a missing entry makes it fail.
  • README.md: an "Examples and formats" section.

How the data was made

  • Examples: up to 3 per validator, taken from the spec's valid fixtures, one per distinct shape. az/pin has no valid fixture, so it uses a made-up 1234567890.
  • Formats: generated from what each validator accepts (the format() output of the fixtures, a mutation search from them, and random strings of length 1–30 with the check digit repaired), then merged by separators and length. Literal letters are set by hand only where the validator requires that exact text (ch/uid, ch/vat, at/uid, ca/pst, no/mva, nl/btw, in/gstin, gb/vat, and a few passports); elsewhere every letter position is a, a deliberate superset.

Existing bugs found (not fixed here)

  • co/nit: format('12345678') returns 123.456.78-undefined (8- and 9-digit numbers). Those shapes are left out of formats.
  • isValidDateCompactYYYYMMDD / isValidDateCompactYYMMDD accept some letters, so tw/tax_code validates 4KGW8O4GGW and de/pwnr accepts a birth date of 6Y08125. Their formats describe the intended shape.
  • il/idnr: validate('0') is valid. That shape is left out of formats.
  • ec/ruc: the compact returned by validate() can differ from compact() (0101016905001 validates to 0101016905).

Not verified

  • The examples were not checked against the rule on real people's numbers. They are existing spec fixtures, but now that apps may display them, they should be reviewed before merging.
  • The notation can't express mx/rfc's Ñ and &; fi/hetu's + century sign and old ie/vat +/* forms weren't checked.
  • Validators that accept any length (at/businessid, li/peid, gt/nit, ro/cif, ro/cui, sm/coe, py/ruc) list only the lengths found from 1 to 30.
  • Prettier also reformatted a few existing lines in touched files (src/types.ts, src/gb/nino.ts, src/lk/nic.ts).
  • npx prettier --check README.md fails because of the passport table added in feat(passport): add passport number validators #175, which was already unformatted.
  • This conflicts with the kr/rrn ARN PR in src/kr/rrn.ts (different hunks); whichever merges second needs a small conflict fix.

Add optional `examples` (valid numbers in compact form) and `formats`
(patterns for the format() output, IBAN registry style: n digit, a letter
A-Z, c digit or letter, anything else literal) to the Validator interface,
and fill them in for every validator, including gen/ean.

Examples come from each validator's existing spec fixtures, except az/pin,
which has no valid fixture and uses a made-up 10-digit number. Formats were
derived from the format() output of those fixtures, of numbers found by
mutating them, and of random numbers of every length from 1 to 30 (with the
check character repaired), keeping only the ones validate() accepts. Literal
text (CHE, MWST, NO ... MVA, PST, GD/HA, ...) is set by hand where the
validator requires it. Aliases (cl/run, sk/rc, pt/nipc, me/mk/rs/si jmbg)
inherit from the module they spread.

src/examples.spec.ts checks, for every validator reachable from index.ts
and gen/ean, that it has examples and formats, that each example passes
validate(), that compact(example) is the example, and that format(example)
matches a declared pattern.

Fixes #108
@koblas
koblas merged commit 3598e84 into main Nov 11, 2026
10 checks passed
@koblas
koblas deleted the feat/validator-examples branch November 11, 2026 15:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Enhance each stdnum type with an example property

1 participant