kreiseck_validator 0.13.0
kreiseck_validator: ^0.13.0 copied to clipboard
Zero-dependency validation, normalization and formatting for email, phone, URL, host, IBAN, BIC, VAT-ID, GTIN, credit-card, license-plate, IMEI, ICCID, MAC, VIN, postal-code and Austrian social-securi [...]
Changelog #
0.13.0 #
Company register — the Austrian courts. CompanyRegister.courts lists the
sixteen Firmenbuchgerichte (Handelsgericht Wien, Landesgericht für
Zivilrechtssachen Graz and the fourteen Landesgerichte incl. Krems an der
Donau, Steyr, Wels and Ried im Innkreis; § 120 JN, justiz.gv.at, checked
2026-08) with code, name, city, bundesland (ISO 3166-2:AT) and
bundeslandName. CompanyRegister.court(input) resolves free text — „LG
Salzburg", „Landesgericht Salzburg", „Salzburg", LG_SALZBURG — to one entry
and returns null for anything else (a federal state, a foreign court). Dart
and JS identical, vectors in test/vectors/company_register_courts.json.
Company register — the Austrian courts. CompanyRegister.courts lists the
sixteen Firmenbuchgerichte (Handelsgericht Wien, Landesgericht für
Zivilrechtssachen Graz and the fourteen Landesgerichte incl. Krems an der
Donau, Steyr, Wels and Ried im Innkreis; § 120 JN, justiz.gv.at, checked
2026-08) with code, name, city, bundesland (ISO 3166-2:AT) and
bundeslandName. CompanyRegister.court(input) resolves free text — „LG
Salzburg", „Landesgericht Salzburg", „Salzburg", LG_SALZBURG — to one entry
and returns null for anything else (a federal state, a foreign court). Dart
and JS identical, vectors in test/vectors/company_register_courts.json.
URL — strict mode for addresses a machine will call. Url.validate keeps its
lenient defaults (scheme optional, http and https both accepted) and gains
four options: requireProtocol (a missing scheme:// is now an error,
urlProtocolMissing, instead of being silently completed), protocols
(anything outside the list is urlProtocolNotAllowed), allowCredentials
(user:pass@ is urlCredentials when false) and allowLocalhost (localhost,
IP literals and private ranges are urlHostNotPublic when false). The shortcut
Url.webhook(input) switches all four on. Whitespace inside a URL is reported
as urlWhitespace rather than the generic urlBadHost. Scheme and host are
still lower-cased on normalization; path and query keep their case. Same
behaviour in Dart and TypeScript, pinned by 20 new shared vectors.
Both packages move to 0.13.0 (the TypeScript package skips 0.12.1, which was a Dart-only packaging release).
0.12.1 #
Packaging — the published archive was four times larger than it needed to be.
.pubignore excluded the design notes and the generator scripts and nothing
else, so every download also carried the TypeScript package's sources and
lockfile, the test suite with all its vectors, the phone-metadata JSON that
only tool/gen_phone_metadata.py ever reads, and a tarball left behind by
npm pack. That last one is why 0.12.0 shipped at 922 KB rather than the
498 KB its own dry run had reported: pub reads .pubignore instead of
.gitignore, so the root *.tgz rule never applied. 232 KB now.
No library code changed, and nothing was removed that any of it imports. This release is Dart-only — the npm package was never affected and stays at 0.12.0, so the two changelogs diverge from here.
0.12.0 #
Breaking — social-security numbers with a serial below 100 are now rejected.
The Austrian Laufnummer is only ever issued in the range 100-999, so its first
digit is never zero. Validation checked the mod-11 check digit alone, which let
through 90.909.091 numbers that cannot have been issued, 0000000000 among
them — a tenth of everything it accepted. 0000TTMMJJ in particular is the
form written on Austrian paperwork to mean "insurance number unknown, birth
date follows", so this was the likeliest wrong answer in practice, not a
theoretical one. The new ssnBadSerial code is reported before the checksum,
so a number wrong on both counts reports the serial it cannot have rather than
a check-digit failure that would send you looking in the wrong place.
SocialSecurityInfo.birthDate is documented for what it is. No behaviour
changed. The field was described as null whenever the digits do not form a real
calendar date, which is accurate but reads as a promise that a non-null value is
the person's date of birth. It is not: someone whose birthday is unknown is
registered as 1 January or 1 July of their birth year, and that is an ordinary
calendar date no inspection can tell from a real one. § 358 ASVG is explicit
that the date carried in the number has no civil-status quality. Collect a date
of birth separately if you need one.
0.11.2 #
Fix — Irish VAT IDs could not be typed. The historical Irish form carries a
+ or * in second position, which validate accepts but the field
descriptor's allowedChars did not list — so formatPartial deleted the
character again on every keystroke and the number was impossible to enter into
a field built from the descriptor.
Swiss numbers with their usual suffix are accepted.
CHE-116.281.710 MWST is how a Swiss UID is normally written (the marker says
the holder is VAT-registered) and is exactly what gets copied out of an
imprint; the suffix is now dropped instead of rejected. HR/MWST, TVA and
IVA likewise.
Three more countries, each with its real check digit: Norway (NO,
Organisasjonsnummer + MVA), Serbia (RS, ISO 7064 MOD 11,10) and Türkiye
(TR, Vergi Kimlik Numarası). 33 prefixes in total.
UK government and health-authority numbers in their long form
(GD8888nnn plus two check digits) are recognised; previously only the short
GD001/HA599 spelling was.
Still rejected, and deliberately so for now: Liechtenstein, Iceland and the
EU one-stop-shop numbers (EU…/IM…). None of them carries a check digit, so
accepting them would mean introducing a structure-only tier — a design
decision rather than a patch, since this package promises that a valid result
means the arithmetic was verified.
0.11.1 #
VatId.prefixFor(country)— the VAT prefix a country writes in front of its number (ATU,CHE,EL,XIand otherwise the ISO code), or null when the country has no VAT ID here. A form that shows the prefix beside the input, or re-prefixes a number when the user switches country, could not get at this; deriving it from the ISO code is wrong for four countries.
0.11.0 #
Six new types and, for the TypeScript port, a dom subpath.
VatId— VAT identification numbers for the 27 EU member states plus Switzerland, the United Kingdom and Northern Ireland. Every one of them is verified with its real check-digit algorithm; there is no structure-only tier, and the dispatch throws rather than silently degrading.parsereports the ISO country, the tax-side prefix (ELfor Greece,XIfor Northern Ireland) and asubtypefor the countries that pack several identifiers into one field.viesRequest/parseViesResponseare an offline seam around the EU's VIES service — the package still makes no network call.Bic— ISO 9362, with the country segment checked against the bundled country table and the location code's second character parsed intoBicInfo.kind, so a test-and-training BIC cannot slip into a payment file unnoticed.Bic.matchesIbancross-checks a BIC against an IBAN.Gtin— GS1 mod-10 for EAN-8, UPC-A, EAN-13 and ITF-14, plus the zero-paddedgtin14form andGtin.checkDigitfor completing a scan.SocialSecurityNumber— Austria. The checksum alone decides validity: months 13-15 and placeholder birthdays are genuinely issued, sobirthDateis simply null for them. The century is never inferred.CompanyRegister— Austria. The Firmenbuchnummer check letter, verified against twelve published numbers. The widely repeatedmod 26rule turns out to be wrong; seedoc/algorithms.md.TaxNumber— Austria. The Abgabenkontonummer's Luhn check digit and12-345/6789formatting. The Finanzamt number is reported but never used to reject.- Every new type ships
fieldDescriptorandformatPartiallike the rest.
Breaking-ish: IssueCode gained 27 members. Adding enum values is
source-breaking for an exhaustive Dart switch over it — add a default branch
if you have one.
0.10.0 #
- Added
FieldDescriptor(keyboard,autofill,capitalization,maxLength,example,allowedChars) and afieldDescriptor(...)method on every type, taking the same options asvalidate. - Added
formatPartial(...)on every type: as-you-type formatting that never throws and, for the nine grouping types, agrees withformaton valid input. PostalPatterngainedexample,charsetandlength; the postal metadata table was regenerated.length(the canonical formatted length, separators included) is mechanically derived from each country's pattern, soPostalCode.fieldDescriptor(country:).maxLengthandformatPartial's truncation are now set for all 51 countries instead of only the 23 with a curatedexample.Phone.fieldDescriptor(...).exampleis now alwaysnull: the previous synthetic construction (+{callingCode}1234567) produced a plausible- looking but fabricated example for most countries andnullfor the rest, contradicting the never-invented-example principle applied to postal codes.Host.formatPartialno longer drops non-ASCII characters (e.g.münchen.depreviously became the different, plausible-lookingmnchen.de); it now leaves them in place since the module is ASCII-only and would reject the value either way.
0.9.0 #
Imeigains an opt-inallowSvoption (defaultfalse, backward-compatible) accepting a 16-digit IMEISV (the 15-digit IMEI structure plus a 2-digit software version number) alongside the existing 15-digit Luhn IMEI. A 16-digit value is never Luhn-checked — IMEISV has no check digit.ImeiInfogainssoftwareVersion(set for a 16-digit IMEISV,nullotherwise) andcheckDigitbecomes nullable (nullfor a 16-digit IMEISV, unchanged for a 15-digit IMEI). With the defaultallowSv == false, every existing IMEI vector and behavior is unchanged.- New
Hostmodule: validation, normalization, formatting andparsefor a bare host — hostname (RFC 1123), IPv4 or IPv6 address — with an optional port. Classification tries IPv4, then IPv6, then hostname. A port is only recognized for IPv6 in the bracketed form ([::1]:8080); for hostname/IPv4 a single trailing:portis split off.Host.parsereturns aHostInfo(host,type,port,hasPort). More lenient thanUrl— no scheme required, acceptslocalhostand IP literals — and independent of it. - Three new
IssueCodes:hostEmpty,hostBadFormat,hostBadPort.
0.8.0 #
- Five new modules:
Imei,Iccid,MacAddress,VinandPostalCode, each with the standardisValid/validate/normalize/format(+tryFormat) API plusparsefor structural extraction. Imei: 15-digit Luhn checksum,parseinto anImeiInfo(TAC, serial number, check digit, reporting-body identifier). IMEISV (16-digit) is out of scope.Iccid: 19- or 20-digit SIM identifiers (ITU-T E.118) starting with the telecom MII89; 20-digit ICCIDs carry a Luhn check digit, 19-digit ones don't.parsereturns anIccidInforesolving the issuing country from the embedded E.164 calling code.MacAddress: EUI-48/64 hardware addresses across colon, hyphen, Cisco-dot and bare notation, withformat(..., notation:)conversion between them andparseexposing the OUI/NIC split plus the unicast/multicast and universal/local bits.Vin: ISO 3779 structure validation (17 chars,I/O/Qforbidden).validateis structure-only — the check digit is mandatory only for North American VINs — soparse'sVinInfoexposescheckDigitValid(ISO 3779 mod-11 weighted checksum) and the decodedmodelYearfrom character 10, disambiguated by whether character 7 is a letter (2010-2039 cycle) or a digit (1980-2009 cycle).PostalCode: a curated per-country pattern table covering Europe plus Turkey (51 countries), with canonical per-country spacing (e.g. NL1234ab→1234 AB, PL00950→00-950, GBsw1a1aa→SW1A 1AA) andparseinto aPostalInfo.country(ISO2) is required on every operation since a bare code is ambiguous across countries.- Internal refactor:
CreditCard's Luhn checksum is now a small shared helper (lib/src/common/luhn.dart,js/src/common/luhn.ts) reused byImeiandIccid;CreditCard's own validation behavior is unchanged. - Sixteen new
IssueCodes:imeiEmpty,imeiBadChars,imeiBadLength,imeiBadChecksum;iccidEmpty,iccidBadChars,iccidBadLength,iccidBadChecksum;macEmpty,macBadFormat;vinEmpty,vinBadChars,vinBadLength;postalEmpty,postalBadFormat,postalUnknownCountry.
0.7.0 #
- New
LicensePlatemodule: validation, normalization, formatting andparsefor vehicle registration plates ("Kennzeichen") across Austria, Germany, Switzerland, Croatia and Turkey (country: 'AT' | 'DE' | 'CH' | 'HR' | 'TR'). LicensePlate.parsereturns aPlateInfo— district/canton/province code, the resolved region name (from a curated code → region table;nullwhen the code is unrecognized), the serial part, aPlateTypeclassification and the canonicalformatteddisplay form.- Plates have no checksum:
validateis a per-country grammar plus the region table. AT/DE accept a structurally valid but unlisted code (regionnull, still valid); CH/HR/TR require the code to be one of their known cantons/cities/provinces. PlateTypeclassification (diplomatic, authority, military, temporary, seasonal, historic, electric) is rule-based per country and best-effort — it never blocks validation and defaults tostandardwhen a country's special forms aren't identifiable from the plate text alone.- Five new
IssueCodes:plateEmpty,plateBadChars,plateBadFormat,plateUnknownCountry,plateAmbiguousCountry. - A TypeScript port,
@kreiseck/validator, now lives underjs/; parity with this package is enforced by the shared vectors intest/vectors/.
0.6.0 #
IbanCountry.of(code)/IbanCountry.valuesexpose each country's IBAN format — total length, bank / branch / account field lengths, whether a branch code exists, and a valid example IBAN (canonical for AT/DE/CH, deterministically generated for the rest).
0.5.0 #
Iban.parsenow resolvesbankNameandbicfor German and Swiss IBANs, in addition to Austrian ones, from bundled snapshots of the Deutsche Bundesbank Bankleitzahlen directory and the SIX Bank Master.- The internal bank-enrichment table is now country-keyed (
AT/DE/CH).
0.4.0 #
Iban.parsereturns anIbanInfowith the country, check digits and bank / branch / account codes for every country whose BBAN layout is known (SWIFT IBAN Registry). Austrian IBANs additionally resolve the bank name and BIC from a bundled snapshot of the OeNB SEPA directory.- IBAN length validation now covers all registry countries, not only DE/AT/CH.
0.3.0 #
- Global phone support: validation, normalization and formatting for every country, derived from libphonenumber (Apache-2.0; see NOTICE).
Countrynow covers all countries with uniform metadata:iso2,callingCode,displayName,flag, and syntheticexamplenumbers. Look up viaCountry.fromIso2/Country.fromCallingCode; enumerate viaCountry.values. Breaking:Countrychanged from a 3-value enum to a class; onlyCountry.de/at/chremain as named constants.- Uniform, strict validation (
possibleLengths+ national pattern) with a newIssueCode.phoneInvalidfor structural mismatches. - AT/DE/CH display formatting re-baselined to libphonenumber grouping.
- Austrian type classification unchanged; other countries report
PhoneNumberType.unknown.
0.2.0 #
Phone.typeandPhone.parse(PhoneInfo): Austrian number-type classification (mobile, landline, VoIP, freephone, shared-cost, premium, corporate) from the public RTR numbering plan.- Type-aware Austrian formatting:
Phone.formatnow uses the geographic area-code length for landlines (e.g.01 …Vienna,0316 …Graz); mobile output is unchanged.
0.1.1 #
- Add a runnable
example/covering all five types.
0.1.0 #
- Initial release, zero runtime dependencies:
Email: syntax validation, trim/lowercase normalization, offline typo-domain suggestions for popular providers.Phone: E.164 and DACH (DE/AT/CH) national validation, normalization to E.164, national/international display formatting.Url: http/https scheme, host and TLD plausibility checks, normalization (scheme, lower-case host, trailing slash) and a compact display format.Iban: Mod-97 checksum validation with DACH (DE/AT/CH) length checks, upper-case normalization and 4-block formatting.CreditCard: Luhn checksum and per-network length validation, network detection (Visa/Mastercard/Amex/Discover), digits-only normalization and network-typical block formatting.- Shared
ValidationResult/Valid/Invalid/ValidationIssue/IssueCoderesult model across all five types.