Latest commit

History

81 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

stella

@stll/stdnum

Validate, compact, format, generate, and parse standard identifiers through a single Rust implementation. The npm package uses a native N-API binding on Node.js and Bun; @stll/stdnum-wasm serves browsers and edge runtimes, and the same registry is available from Python as stella-stdnum.

This package covers 11 global identifiers and 96 countries through 176 per-module entry points.

Install

npm install @stll/stdnum
# or
bun add @stll/stdnum

For browser or edge runtimes, install both packages:

npm install @stll/stdnum @stll/stdnum-wasm

For Python 3.11 or newer:

pip install stella-stdnum

Usage

import{cz}from"@stll/stdnum";cz.ico.validate("25596641");// { valid: true, compact: "25596641" }cz.ico.validate("12345678");// { valid: false, error: { code: "INVALID_CHECKSUM", ... }}cz.rc.format("7103192745");// "710319/2745"cz.ico.validateMany(["25596641","12345678"]);// [{ valid: true, compact: "25596641" }, { valid: false, error: ... }]

Tree-shaking

Import only what you need:

importicofrom"@stll/stdnum/cz/ico";importibanfrom"@stll/stdnum/iban";ico.validate("25596641");iban.validate("CZ65 0800 0000 1920 0014 5399");

Browser and edge runtimes

Initialize the WebAssembly runtime once before calling the synchronous validator subpaths:

import{initialize}from"@stll/stdnum/browser";importicofrom"@stll/stdnum/cz/ico";awaitinitialize();ico.validate("25596641");

Concurrent calls to initialize() share the same promise. Calling a validator before initialization throws StdnumNotInitializedError from @stll/stdnum/browser.

The WASM package also exposes the registry API asynchronously:

import{validate}from"@stll/stdnum-wasm";awaitvalidate("cz.ico","25596641");

Python

importstella_stdnumresult=stella_stdnum.validate("cz.ico", "25596641")
assertresult.validassertresult.compact=="25596641"results=stella_stdnum.validate_many("cz.ico", ["25596641", "12345678"])
assert [result.validforresultinresults] == [True, False]

The Rust registry owns validator behavior and metadata. TypeScript subpaths, catalog files, and exact TypeScript/Python registry types are generated from that registry. One committed fixture set checks the Rust, Node.js, WASM, and Python surfaces, and ty checks the installed Python wheel as a real consumer.

Oracle Validation

The repo ships two oracle modes:

  • bun run oracle Runs the strict gate. It keeps only high-confidence cross-checks that are stable enough for CI.
  • bun run oracle:survey Runs the broader ecosystem survey. It includes noisier third-party validators and mutation probes to surface drift without treating every disagreement as a release blocker.

Both commands accept ORACLE_SAMPLES=<n> to trade off runtime against coverage.

bun run performance:oracles is an optional local diagnostic for the handful of specialized JavaScript oracles it covers. It is deliberately not a CI gate: those country-specific comparisons are not representative of the library as a whole. The benchmark alternates execution order and compares paired medians to reduce host noise.

Dependabot watches every JavaScript, Python, Ruby, PHP, and Rust oracle manifest. Its weekly update PRs rerun the strict correctness gates against the new upstream versions before they can merge.

Supported Identifiers

International

IdentifierModuleType
Business Identifier Codebiccompany
Credit Card Numbercreditcardany
Bitcoin Base58Check Addresscrypto/btcbase58any
Bitcoin Bech32 Addresscrypto/btcbech32any
Ethereum Addresscrypto/ethany
Cryptocurrency Wallet Addresscrypto/walletany
EU VAT Numbereu/vatcompany
IBANibanany
International Securities Identification Numberisinany
Legal Entity Identifierleicompany
Luhnluhnany

Countries

96 countries supported (click to expand)
CountryModuleIdentifier
ADad/nrtNRT
AEae/eidEID
AIai/tinTIN
ALal/niptNIPT
AMam/tinTIN
ARar/cbuCBU
ar/cuitCUIT
ar/dniDNI
ATat/businessidFN
at/tinTIN
at/uidUID
at/vnrVNR
AUau/abnABN
au/acnACN
au/tfnTFN
AZaz/voenVÖEN
BAba/jmbgJMBG
BDbd/nidNID
BEbe/bisBIS
be/nnNN
be/vatBTW
BGbg/egnЕГН
bg/pnfЛНЧ
bg/vatИН по ДДС
BHbh/cprCPR
BRbr/cnpjCNPJ
br/cpfCPF
BYby/unpУНП
BZbz/tinTIN
CAca/bnBN
ca/sinSIN
CHch/ssnAHV
ch/uidUID
ch/vatMWST
CLcl/rutRUT
CNcn/ricRIC
cn/usccUSCC
COco/nitNIT
CRcr/cpfCPF
CUcu/niNI
CYcy/vatΦΠΑ
CZcz/bankaccountČíslo účtu
cz/dicDIČ
cz/icoIČO
cz/rc
DEde/handelsregHReg
de/idnrIdNr
de/stnrStNr
de/svnrSVNR
de/vatUSt-IdNr.
DKdk/cprCPR
dk/cvrCVR
dk/vatCVR
DOdo/rncRNC
ECec/rucRUC
EEee/ikIK
ee/registrikoodRegistrikood
ee/vatKMKR
EGeg/tnTN
ESes/cifCIF
es/dniDNI
es/nieNIE
es/nssNSS
es/vatNIF
FIfi/hetuHETU
fi/vatALV nro
fi/ytunnusY-tunnus
FRfr/nifNIF
fr/nirNIR
fr/sirenSIREN
fr/siretSIRET
fr/tvaTVA
GBgb/nhsNHS
gb/ninoNINO
gb/sedolSEDOL
gb/utrUTR
gb/vatVAT
GEge/pinPIN
GHgh/tinTIN
GRgr/amkaΑΜΚΑ
gr/vatΑΦΜ
GTgt/nitNIT
HKhk/hkidHKID
HRhr/vatOIB
HUhu/vatANUM
IDid/npwpNPWP
IEie/ppsPPS
ie/vatVAT
ILil/idnrת.ז.
INin/aadhaarAadhaar
in/gstinGSTIN
in/panPAN
IQiq/nidNID
IRir/nidNID
ISis/kennitalakt.
is/vskVSK
ITit/codicefiscaleCF
it/ivaP.IVA
JPjp/cnCN
jp/mynumberMy Number
KRkr/brnBRN
kr/rrnRRN
KWkw/civilCivil ID
KZkz/iinIIN
LIli/peidPEID
LKlk/nicNIC
LTlt/asmensAK
lt/vatPVM kodas
LUlu/vatTVA
LVlv/vatPVN
MAma/iceICE
MCmc/tvaTVA
MDmd/idnoIDNO
MEme/pibPIB
MKmk/edbEDB
MTmt/vatVAT
MUmu/brnBRN
MXmx/clabeCLABE
mx/curpCURP
mx/rfcRFC
MYmy/nricNRIC
NGng/ninNIN
NIni/rucRUC
NLnl/bsnBSN
nl/kvkKvK
nl/vatBTW
NOno/fodselsnummerFødselsnr
no/mvaMVA
no/orgnrOrgnr
NZnz/irdIRD
PApa/rucRUC
PEpe/rucRUC
PHph/philidPhilID
PKpk/cnicCNIC
PLpl/nipNIP
pl/peselPESEL
pl/regonREGON
PTpt/ccCC
pt/vatNIF
ROro/cnpCNP
ro/vatCIF
RSrs/pibPIB
RUru/innИНН
SEse/orgnrOrgnr
se/personnummerPN
se/vatMomsnr.
SGsg/uenUEN
SIsi/emsoEMŠO
si/vatDDV
SKsk/dicIČ DPH
sk/icoIČO
sk/rc
THth/tinTIN
TRtr/tckimlikT.C. Kimlik
tr/vknVKN
TWtw/ubnUBN
UAua/edrpouЄДРПОУ
USus/einEIN
us/itinITIN
us/rtnRTN
us/ssnSSN
UYuy/rutRUT
VEve/rifRIF
VNvn/mstMST
ZAza/idnrSA ID

API

Every identifier exports the same interface:

typeValidator={name: string;localName: string;abbreviation: string;country?: CountryCode;entityType: "person"|"company"|"any";compact: (value: string)=>string;format: (value: string)=>string;validate: (value: string)=>ValidateResult;// Optional metadatadescription?: string;sourceUrl?: string;lengths?: readonlynumber[];examples?: readonlystring[];aliases?: readonlystring[];candidatePattern?: string;// Available on most validatorsgenerate?: ()=>string;};typeValidateResult=|{valid: true;compact: string}|{valid: false;error: {code:
|"INVALID_FORMAT"|"INVALID_LENGTH"|"INVALID_CHECKSUM"|"INVALID_COMPONENT";message: string;};};

Additional exports

Many person-type validators export parse(), returning structured data extracted from the identifier:

import{cz}from"@stll/stdnum";cz.rc.parse("7103192745");// { birthDate: Date, gender: "male" }

Validators with parse(): BA JMBG, BG ЕГН, CN RIC, CU NI, CZ RČ, DK CPR, EE IK, FI HETU, FR NIR, IT CF, KR RRN, KW Civil ID, KZ IIN, LK NIC, MX CURP, MY NRIC, NO Fødselsnr, PL PESEL, RO CNP, SE PN, SI EMŠO, SK RČ, ZA SA ID.

detectNetwork() and the CardNetwork type are top-level named exports:

import{detectNetwork}from"@stll/stdnum";importtype{CardNetwork}from"@stll/stdnum";detectNetwork("4111111111111111");// "visa"

Unicode handling

All validators normalize Unicode artifacts before validation: fullwidth digits, various dashes, non-breaking spaces, zero-width characters, and other common OCR/PDF copy-paste issues are handled automatically.

Development

bun install
bun run rust:check
bun run codegen:check
bun run typecheck
bun test
bun run lint # oxlint
bun run format # oxfmt

License

MIT

About

Validate, compact, and format standard identifiers in TypeScript

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Latest commit

History

81 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

stella

@stll/stdnum

Validate, compact, format, generate, and parse standard identifiers through a single Rust implementation. The npm package uses a native N-API binding on Node.js and Bun; @stll/stdnum-wasm serves browsers and edge runtimes, and the same registry is available from Python as stella-stdnum.

This package covers 11 global identifiers and 96 countries through 176 per-module entry points.

Install

npm install @stll/stdnum
# or
bun add @stll/stdnum

For browser or edge runtimes, install both packages:

npm install @stll/stdnum @stll/stdnum-wasm

For Python 3.11 or newer:

pip install stella-stdnum

Usage

import{cz}from"@stll/stdnum";cz.ico.validate("25596641");// { valid: true, compact: "25596641" }cz.ico.validate("12345678");// { valid: false, error: { code: "INVALID_CHECKSUM", ... }}cz.rc.format("7103192745");// "710319/2745"cz.ico.validateMany(["25596641","12345678"]);// [{ valid: true, compact: "25596641" }, { valid: false, error: ... }]

Tree-shaking

Import only what you need:

importicofrom"@stll/stdnum/cz/ico";importibanfrom"@stll/stdnum/iban";ico.validate("25596641");iban.validate("CZ65 0800 0000 1920 0014 5399");

Browser and edge runtimes

Initialize the WebAssembly runtime once before calling the synchronous validator subpaths:

import{initialize}from"@stll/stdnum/browser";importicofrom"@stll/stdnum/cz/ico";awaitinitialize();ico.validate("25596641");

Concurrent calls to initialize() share the same promise. Calling a validator before initialization throws StdnumNotInitializedError from @stll/stdnum/browser.

The WASM package also exposes the registry API asynchronously:

import{validate}from"@stll/stdnum-wasm";awaitvalidate("cz.ico","25596641");

Python

importstella_stdnumresult=stella_stdnum.validate("cz.ico", "25596641")
assertresult.validassertresult.compact=="25596641"results=stella_stdnum.validate_many("cz.ico", ["25596641", "12345678"])
assert [result.validforresultinresults] == [True, False]

The Rust registry owns validator behavior and metadata. TypeScript subpaths, catalog files, and exact TypeScript/Python registry types are generated from that registry. One committed fixture set checks the Rust, Node.js, WASM, and Python surfaces, and ty checks the installed Python wheel as a real consumer.

Oracle Validation

The repo ships two oracle modes:

  • bun run oracle Runs the strict gate. It keeps only high-confidence cross-checks that are stable enough for CI.
  • bun run oracle:survey Runs the broader ecosystem survey. It includes noisier third-party validators and mutation probes to surface drift without treating every disagreement as a release blocker.

Both commands accept ORACLE_SAMPLES=<n> to trade off runtime against coverage.

bun run performance:oracles is an optional local diagnostic for the handful of specialized JavaScript oracles it covers. It is deliberately not a CI gate: those country-specific comparisons are not representative of the library as a whole. The benchmark alternates execution order and compares paired medians to reduce host noise.

Dependabot watches every JavaScript, Python, Ruby, PHP, and Rust oracle manifest. Its weekly update PRs rerun the strict correctness gates against the new upstream versions before they can merge.

Supported Identifiers

International

IdentifierModuleType
Business Identifier Codebiccompany
Credit Card Numbercreditcardany
Bitcoin Base58Check Addresscrypto/btcbase58any
Bitcoin Bech32 Addresscrypto/btcbech32any
Ethereum Addresscrypto/ethany
Cryptocurrency Wallet Addresscrypto/walletany
EU VAT Numbereu/vatcompany
IBANibanany
International Securities Identification Numberisinany
Legal Entity Identifierleicompany
Luhnluhnany

Countries

96 countries supported (click to expand)
CountryModuleIdentifier
ADad/nrtNRT
AEae/eidEID
AIai/tinTIN
ALal/niptNIPT
AMam/tinTIN
ARar/cbuCBU
ar/cuitCUIT
ar/dniDNI
ATat/businessidFN
at/tinTIN
at/uidUID
at/vnrVNR
AUau/abnABN
au/acnACN
au/tfnTFN
AZaz/voenVÖEN
BAba/jmbgJMBG
BDbd/nidNID
BEbe/bisBIS
be/nnNN
be/vatBTW
BGbg/egnЕГН
bg/pnfЛНЧ
bg/vatИН по ДДС
BHbh/cprCPR
BRbr/cnpjCNPJ
br/cpfCPF
BYby/unpУНП
BZbz/tinTIN
CAca/bnBN
ca/sinSIN
CHch/ssnAHV
ch/uidUID
ch/vatMWST
CLcl/rutRUT
CNcn/ricRIC
cn/usccUSCC
COco/nitNIT
CRcr/cpfCPF
CUcu/niNI
CYcy/vatΦΠΑ
CZcz/bankaccountČíslo účtu
cz/dicDIČ
cz/icoIČO
cz/rc
DEde/handelsregHReg
de/idnrIdNr
de/stnrStNr
de/svnrSVNR
de/vatUSt-IdNr.
DKdk/cprCPR
dk/cvrCVR
dk/vatCVR
DOdo/rncRNC
ECec/rucRUC
EEee/ikIK
ee/registrikoodRegistrikood
ee/vatKMKR
EGeg/tnTN
ESes/cifCIF
es/dniDNI
es/nieNIE
es/nssNSS
es/vatNIF
FIfi/hetuHETU
fi/vatALV nro
fi/ytunnusY-tunnus
FRfr/nifNIF
fr/nirNIR
fr/sirenSIREN
fr/siretSIRET
fr/tvaTVA
GBgb/nhsNHS
gb/ninoNINO
gb/sedolSEDOL
gb/utrUTR
gb/vatVAT
GEge/pinPIN
GHgh/tinTIN
GRgr/amkaΑΜΚΑ
gr/vatΑΦΜ
GTgt/nitNIT
HKhk/hkidHKID
HRhr/vatOIB
HUhu/vatANUM
IDid/npwpNPWP
IEie/ppsPPS
ie/vatVAT
ILil/idnrת.ז.
INin/aadhaarAadhaar
in/gstinGSTIN
in/panPAN
IQiq/nidNID
IRir/nidNID
ISis/kennitalakt.
is/vskVSK
ITit/codicefiscaleCF
it/ivaP.IVA
JPjp/cnCN
jp/mynumberMy Number
KRkr/brnBRN
kr/rrnRRN
KWkw/civilCivil ID
KZkz/iinIIN
LIli/peidPEID
LKlk/nicNIC
LTlt/asmensAK
lt/vatPVM kodas
LUlu/vatTVA
LVlv/vatPVN
MAma/iceICE
MCmc/tvaTVA
MDmd/idnoIDNO
MEme/pibPIB
MKmk/edbEDB
MTmt/vatVAT
MUmu/brnBRN
MXmx/clabeCLABE
mx/curpCURP
mx/rfcRFC
MYmy/nricNRIC
NGng/ninNIN
NIni/rucRUC
NLnl/bsnBSN
nl/kvkKvK
nl/vatBTW
NOno/fodselsnummerFødselsnr
no/mvaMVA
no/orgnrOrgnr
NZnz/irdIRD
PApa/rucRUC
PEpe/rucRUC
PHph/philidPhilID
PKpk/cnicCNIC
PLpl/nipNIP
pl/peselPESEL
pl/regonREGON
PTpt/ccCC
pt/vatNIF
ROro/cnpCNP
ro/vatCIF
RSrs/pibPIB
RUru/innИНН
SEse/orgnrOrgnr
se/personnummerPN
se/vatMomsnr.
SGsg/uenUEN
SIsi/emsoEMŠO
si/vatDDV
SKsk/dicIČ DPH
sk/icoIČO
sk/rc
THth/tinTIN
TRtr/tckimlikT.C. Kimlik
tr/vknVKN
TWtw/ubnUBN
UAua/edrpouЄДРПОУ
USus/einEIN
us/itinITIN
us/rtnRTN
us/ssnSSN
UYuy/rutRUT
VEve/rifRIF
VNvn/mstMST
ZAza/idnrSA ID

API

Every identifier exports the same interface:

typeValidator={name: string;localName: string;abbreviation: string;country?: CountryCode;entityType: "person"|"company"|"any";compact: (value: string)=>string;format: (value: string)=>string;validate: (value: string)=>ValidateResult;// Optional metadatadescription?: string;sourceUrl?: string;lengths?: readonlynumber[];examples?: readonlystring[];aliases?: readonlystring[];candidatePattern?: string;// Available on most validatorsgenerate?: ()=>string;};typeValidateResult=|{valid: true;compact: string}|{valid: false;error: {code:
|"INVALID_FORMAT"|"INVALID_LENGTH"|"INVALID_CHECKSUM"|"INVALID_COMPONENT";message: string;};};

Additional exports

Many person-type validators export parse(), returning structured data extracted from the identifier:

import{cz}from"@stll/stdnum";cz.rc.parse("7103192745");// { birthDate: Date, gender: "male" }

Validators with parse(): BA JMBG, BG ЕГН, CN RIC, CU NI, CZ RČ, DK CPR, EE IK, FI HETU, FR NIR, IT CF, KR RRN, KW Civil ID, KZ IIN, LK NIC, MX CURP, MY NRIC, NO Fødselsnr, PL PESEL, RO CNP, SE PN, SI EMŠO, SK RČ, ZA SA ID.

detectNetwork() and the CardNetwork type are top-level named exports:

import{detectNetwork}from"@stll/stdnum";importtype{CardNetwork}from"@stll/stdnum";detectNetwork("4111111111111111");// "visa"

Unicode handling

All validators normalize Unicode artifacts before validation: fullwidth digits, various dashes, non-breaking spaces, zero-width characters, and other common OCR/PDF copy-paste issues are handled automatically.

Development

bun install
bun run rust:check
bun run codegen:check
bun run typecheck
bun test
bun run lint # oxlint
bun run format # oxfmt

License

MIT

About

Validate, compact, and format standard identifiers in TypeScript

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

81 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

stella

@stll/stdnum

Validate, compact, format, generate, and parse standard identifiers through a single Rust implementation. The npm package uses a native N-API binding on Node.js and Bun; @stll/stdnum-wasm serves browsers and edge runtimes, and the same registry is available from Python as stella-stdnum.

This package covers 11 global identifiers and 96 countries through 176 per-module entry points.

Install

npm install @stll/stdnum
# or
bun add @stll/stdnum

For browser or edge runtimes, install both packages:

npm install @stll/stdnum @stll/stdnum-wasm

For Python 3.11 or newer:

pip install stella-stdnum

Usage

import{cz}from"@stll/stdnum";cz.ico.validate("25596641");// { valid: true, compact: "25596641" }cz.ico.validate("12345678");// { valid: false, error: { code: "INVALID_CHECKSUM", ... }}cz.rc.format("7103192745");// "710319/2745"cz.ico.validateMany(["25596641","12345678"]);// [{ valid: true, compact: "25596641" }, { valid: false, error: ... }]

Tree-shaking

Import only what you need:

importicofrom"@stll/stdnum/cz/ico";importibanfrom"@stll/stdnum/iban";ico.validate("25596641");iban.validate("CZ65 0800 0000 1920 0014 5399");

Browser and edge runtimes

Initialize the WebAssembly runtime once before calling the synchronous validator subpaths:

import{initialize}from"@stll/stdnum/browser";importicofrom"@stll/stdnum/cz/ico";awaitinitialize();ico.validate("25596641");

Concurrent calls to initialize() share the same promise. Calling a validator before initialization throws StdnumNotInitializedError from @stll/stdnum/browser.

The WASM package also exposes the registry API asynchronously:

import{validate}from"@stll/stdnum-wasm";awaitvalidate("cz.ico","25596641");

Python

importstella_stdnumresult=stella_stdnum.validate("cz.ico", "25596641")
assertresult.validassertresult.compact=="25596641"results=stella_stdnum.validate_many("cz.ico", ["25596641", "12345678"])
assert [result.validforresultinresults] == [True, False]

The Rust registry owns validator behavior and metadata. TypeScript subpaths, catalog files, and exact TypeScript/Python registry types are generated from that registry. One committed fixture set checks the Rust, Node.js, WASM, and Python surfaces, and ty checks the installed Python wheel as a real consumer.

Oracle Validation

The repo ships two oracle modes:

  • bun run oracle Runs the strict gate. It keeps only high-confidence cross-checks that are stable enough for CI.
  • bun run oracle:survey Runs the broader ecosystem survey. It includes noisier third-party validators and mutation probes to surface drift without treating every disagreement as a release blocker.

Both commands accept ORACLE_SAMPLES=<n> to trade off runtime against coverage.

bun run performance:oracles is an optional local diagnostic for the handful of specialized JavaScript oracles it covers. It is deliberately not a CI gate: those country-specific comparisons are not representative of the library as a whole. The benchmark alternates execution order and compares paired medians to reduce host noise.

Dependabot watches every JavaScript, Python, Ruby, PHP, and Rust oracle manifest. Its weekly update PRs rerun the strict correctness gates against the new upstream versions before they can merge.

Supported Identifiers

International

IdentifierModuleType
Business Identifier Codebiccompany
Credit Card Numbercreditcardany
Bitcoin Base58Check Addresscrypto/btcbase58any
Bitcoin Bech32 Addresscrypto/btcbech32any
Ethereum Addresscrypto/ethany
Cryptocurrency Wallet Addresscrypto/walletany
EU VAT Numbereu/vatcompany
IBANibanany
International Securities Identification Numberisinany
Legal Entity Identifierleicompany
Luhnluhnany

Countries

96 countries supported (click to expand)
CountryModuleIdentifier
ADad/nrtNRT
AEae/eidEID
AIai/tinTIN
ALal/niptNIPT
AMam/tinTIN
ARar/cbuCBU
ar/cuitCUIT
ar/dniDNI
ATat/businessidFN
at/tinTIN
at/uidUID
at/vnrVNR
AUau/abnABN
au/acnACN
au/tfnTFN
AZaz/voenVÖEN
BAba/jmbgJMBG
BDbd/nidNID
BEbe/bisBIS
be/nnNN
be/vatBTW
BGbg/egnЕГН
bg/pnfЛНЧ
bg/vatИН по ДДС
BHbh/cprCPR
BRbr/cnpjCNPJ
br/cpfCPF
BYby/unpУНП
BZbz/tinTIN
CAca/bnBN
ca/sinSIN
CHch/ssnAHV
ch/uidUID
ch/vatMWST
CLcl/rutRUT
CNcn/ricRIC
cn/usccUSCC
COco/nitNIT
CRcr/cpfCPF
CUcu/niNI
CYcy/vatΦΠΑ
CZcz/bankaccountČíslo účtu
cz/dicDIČ
cz/icoIČO
cz/rc
DEde/handelsregHReg
de/idnrIdNr
de/stnrStNr
de/svnrSVNR
de/vatUSt-IdNr.
DKdk/cprCPR
dk/cvrCVR
dk/vatCVR
DOdo/rncRNC
ECec/rucRUC
EEee/ikIK
ee/registrikoodRegistrikood
ee/vatKMKR
EGeg/tnTN
ESes/cifCIF
es/dniDNI
es/nieNIE
es/nssNSS
es/vatNIF
FIfi/hetuHETU
fi/vatALV nro
fi/ytunnusY-tunnus
FRfr/nifNIF
fr/nirNIR
fr/sirenSIREN
fr/siretSIRET
fr/tvaTVA
GBgb/nhsNHS
gb/ninoNINO
gb/sedolSEDOL
gb/utrUTR
gb/vatVAT
GEge/pinPIN
GHgh/tinTIN
GRgr/amkaΑΜΚΑ
gr/vatΑΦΜ
GTgt/nitNIT
HKhk/hkidHKID
HRhr/vatOIB
HUhu/vatANUM
IDid/npwpNPWP
IEie/ppsPPS
ie/vatVAT
ILil/idnrת.ז.
INin/aadhaarAadhaar
in/gstinGSTIN
in/panPAN
IQiq/nidNID
IRir/nidNID
ISis/kennitalakt.
is/vskVSK
ITit/codicefiscaleCF
it/ivaP.IVA
JPjp/cnCN
jp/mynumberMy Number
KRkr/brnBRN
kr/rrnRRN
KWkw/civilCivil ID
KZkz/iinIIN
LIli/peidPEID
LKlk/nicNIC
LTlt/asmensAK
lt/vatPVM kodas
LUlu/vatTVA
LVlv/vatPVN
MAma/iceICE
MCmc/tvaTVA
MDmd/idnoIDNO
MEme/pibPIB
MKmk/edbEDB
MTmt/vatVAT
MUmu/brnBRN
MXmx/clabeCLABE
mx/curpCURP
mx/rfcRFC
MYmy/nricNRIC
NGng/ninNIN
NIni/rucRUC
NLnl/bsnBSN
nl/kvkKvK
nl/vatBTW
NOno/fodselsnummerFødselsnr
no/mvaMVA
no/orgnrOrgnr
NZnz/irdIRD
PApa/rucRUC
PEpe/rucRUC
PHph/philidPhilID
PKpk/cnicCNIC
PLpl/nipNIP
pl/peselPESEL
pl/regonREGON
PTpt/ccCC
pt/vatNIF
ROro/cnpCNP
ro/vatCIF
RSrs/pibPIB
RUru/innИНН
SEse/orgnrOrgnr
se/personnummerPN
se/vatMomsnr.
SGsg/uenUEN
SIsi/emsoEMŠO
si/vatDDV
SKsk/dicIČ DPH
sk/icoIČO
sk/rc
THth/tinTIN
TRtr/tckimlikT.C. Kimlik
tr/vknVKN
TWtw/ubnUBN
UAua/edrpouЄДРПОУ
USus/einEIN
us/itinITIN
us/rtnRTN
us/ssnSSN
UYuy/rutRUT
VEve/rifRIF
VNvn/mstMST
ZAza/idnrSA ID

API

Every identifier exports the same interface:

typeValidator={name: string;localName: string;abbreviation: string;country?: CountryCode;entityType: "person"|"company"|"any";compact: (value: string)=>string;format: (value: string)=>string;validate: (value: string)=>ValidateResult;// Optional metadatadescription?: string;sourceUrl?: string;lengths?: readonlynumber[];examples?: readonlystring[];aliases?: readonlystring[];candidatePattern?: string;// Available on most validatorsgenerate?: ()=>string;};typeValidateResult=|{valid: true;compact: string}|{valid: false;error: {code:
|"INVALID_FORMAT"|"INVALID_LENGTH"|"INVALID_CHECKSUM"|"INVALID_COMPONENT";message: string;};};

Additional exports

Many person-type validators export parse(), returning structured data extracted from the identifier:

import{cz}from"@stll/stdnum";cz.rc.parse("7103192745");// { birthDate: Date, gender: "male" }

Validators with parse(): BA JMBG, BG ЕГН, CN RIC, CU NI, CZ RČ, DK CPR, EE IK, FI HETU, FR NIR, IT CF, KR RRN, KW Civil ID, KZ IIN, LK NIC, MX CURP, MY NRIC, NO Fødselsnr, PL PESEL, RO CNP, SE PN, SI EMŠO, SK RČ, ZA SA ID.

detectNetwork() and the CardNetwork type are top-level named exports:

import{detectNetwork}from"@stll/stdnum";importtype{CardNetwork}from"@stll/stdnum";detectNetwork("4111111111111111");// "visa"

Unicode handling

All validators normalize Unicode artifacts before validation: fullwidth digits, various dashes, non-breaking spaces, zero-width characters, and other common OCR/PDF copy-paste issues are handled automatically.

Development

bun install
bun run rust:check
bun run codegen:check
bun run typecheck
bun test
bun run lint # oxlint
bun run format # oxfmt

License

MIT

About

Validate, compact, and format standard identifiers in TypeScript

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

81 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

stella

@stll/stdnum

Validate, compact, format, generate, and parse standard identifiers through a single Rust implementation. The npm package uses a native N-API binding on Node.js and Bun; @stll/stdnum-wasm serves browsers and edge runtimes, and the same registry is available from Python as stella-stdnum.

This package covers 11 global identifiers and 96 countries through 176 per-module entry points.

Install

npm install @stll/stdnum
# or
bun add @stll/stdnum

For browser or edge runtimes, install both packages:

npm install @stll/stdnum @stll/stdnum-wasm

For Python 3.11 or newer:

pip install stella-stdnum

Usage

import{cz}from"@stll/stdnum";cz.ico.validate("25596641");// { valid: true, compact: "25596641" }cz.ico.validate("12345678");// { valid: false, error: { code: "INVALID_CHECKSUM", ... }}cz.rc.format("7103192745");// "710319/2745"cz.ico.validateMany(["25596641","12345678"]);// [{ valid: true, compact: "25596641" }, { valid: false, error: ... }]

Tree-shaking

Import only what you need:

importicofrom"@stll/stdnum/cz/ico";importibanfrom"@stll/stdnum/iban";ico.validate("25596641");iban.validate("CZ65 0800 0000 1920 0014 5399");

Browser and edge runtimes

Initialize the WebAssembly runtime once before calling the synchronous validator subpaths:

import{initialize}from"@stll/stdnum/browser";importicofrom"@stll/stdnum/cz/ico";awaitinitialize();ico.validate("25596641");

Concurrent calls to initialize() share the same promise. Calling a validator before initialization throws StdnumNotInitializedError from @stll/stdnum/browser.

The WASM package also exposes the registry API asynchronously:

import{validate}from"@stll/stdnum-wasm";awaitvalidate("cz.ico","25596641");

Python

importstella_stdnumresult=stella_stdnum.validate("cz.ico", "25596641")
assertresult.validassertresult.compact=="25596641"results=stella_stdnum.validate_many("cz.ico", ["25596641", "12345678"])
assert [result.validforresultinresults] == [True, False]

The Rust registry owns validator behavior and metadata. TypeScript subpaths, catalog files, and exact TypeScript/Python registry types are generated from that registry. One committed fixture set checks the Rust, Node.js, WASM, and Python surfaces, and ty checks the installed Python wheel as a real consumer.

Oracle Validation

The repo ships two oracle modes:

  • bun run oracle Runs the strict gate. It keeps only high-confidence cross-checks that are stable enough for CI.
  • bun run oracle:survey Runs the broader ecosystem survey. It includes noisier third-party validators and mutation probes to surface drift without treating every disagreement as a release blocker.

Both commands accept ORACLE_SAMPLES=<n> to trade off runtime against coverage.

bun run performance:oracles is an optional local diagnostic for the handful of specialized JavaScript oracles it covers. It is deliberately not a CI gate: those country-specific comparisons are not representative of the library as a whole. The benchmark alternates execution order and compares paired medians to reduce host noise.

Dependabot watches every JavaScript, Python, Ruby, PHP, and Rust oracle manifest. Its weekly update PRs rerun the strict correctness gates against the new upstream versions before they can merge.

Supported Identifiers

International

IdentifierModuleType
Business Identifier Codebiccompany
Credit Card Numbercreditcardany
Bitcoin Base58Check Addresscrypto/btcbase58any
Bitcoin Bech32 Addresscrypto/btcbech32any
Ethereum Addresscrypto/ethany
Cryptocurrency Wallet Addresscrypto/walletany
EU VAT Numbereu/vatcompany
IBANibanany
International Securities Identification Numberisinany
Legal Entity Identifierleicompany
Luhnluhnany

Countries

96 countries supported (click to expand)
CountryModuleIdentifier
ADad/nrtNRT
AEae/eidEID
AIai/tinTIN
ALal/niptNIPT
AMam/tinTIN
ARar/cbuCBU
ar/cuitCUIT
ar/dniDNI
ATat/businessidFN
at/tinTIN
at/uidUID
at/vnrVNR
AUau/abnABN
au/acnACN
au/tfnTFN
AZaz/voenVÖEN
BAba/jmbgJMBG
BDbd/nidNID
BEbe/bisBIS
be/nnNN
be/vatBTW
BGbg/egnЕГН
bg/pnfЛНЧ
bg/vatИН по ДДС
BHbh/cprCPR
BRbr/cnpjCNPJ
br/cpfCPF
BYby/unpУНП
BZbz/tinTIN
CAca/bnBN
ca/sinSIN
CHch/ssnAHV
ch/uidUID
ch/vatMWST
CLcl/rutRUT
CNcn/ricRIC
cn/usccUSCC
COco/nitNIT
CRcr/cpfCPF
CUcu/niNI
CYcy/vatΦΠΑ
CZcz/bankaccountČíslo účtu
cz/dicDIČ
cz/icoIČO
cz/rc
DEde/handelsregHReg
de/idnrIdNr
de/stnrStNr
de/svnrSVNR
de/vatUSt-IdNr.
DKdk/cprCPR
dk/cvrCVR
dk/vatCVR
DOdo/rncRNC
ECec/rucRUC
EEee/ikIK
ee/registrikoodRegistrikood
ee/vatKMKR
EGeg/tnTN
ESes/cifCIF
es/dniDNI
es/nieNIE
es/nssNSS
es/vatNIF
FIfi/hetuHETU
fi/vatALV nro
fi/ytunnusY-tunnus
FRfr/nifNIF
fr/nirNIR
fr/sirenSIREN
fr/siretSIRET
fr/tvaTVA
GBgb/nhsNHS
gb/ninoNINO
gb/sedolSEDOL
gb/utrUTR
gb/vatVAT
GEge/pinPIN
GHgh/tinTIN
GRgr/amkaΑΜΚΑ
gr/vatΑΦΜ
GTgt/nitNIT
HKhk/hkidHKID
HRhr/vatOIB
HUhu/vatANUM
IDid/npwpNPWP
IEie/ppsPPS
ie/vatVAT
ILil/idnrת.ז.
INin/aadhaarAadhaar
in/gstinGSTIN
in/panPAN
IQiq/nidNID
IRir/nidNID
ISis/kennitalakt.
is/vskVSK
ITit/codicefiscaleCF
it/ivaP.IVA
JPjp/cnCN
jp/mynumberMy Number
KRkr/brnBRN
kr/rrnRRN
KWkw/civilCivil ID
KZkz/iinIIN
LIli/peidPEID
LKlk/nicNIC
LTlt/asmensAK
lt/vatPVM kodas
LUlu/vatTVA
LVlv/vatPVN
MAma/iceICE
MCmc/tvaTVA
MDmd/idnoIDNO
MEme/pibPIB
MKmk/edbEDB
MTmt/vatVAT
MUmu/brnBRN
MXmx/clabeCLABE
mx/curpCURP
mx/rfcRFC
MYmy/nricNRIC
NGng/ninNIN
NIni/rucRUC
NLnl/bsnBSN
nl/kvkKvK
nl/vatBTW
NOno/fodselsnummerFødselsnr
no/mvaMVA
no/orgnrOrgnr
NZnz/irdIRD
PApa/rucRUC
PEpe/rucRUC
PHph/philidPhilID
PKpk/cnicCNIC
PLpl/nipNIP
pl/peselPESEL
pl/regonREGON
PTpt/ccCC
pt/vatNIF
ROro/cnpCNP
ro/vatCIF
RSrs/pibPIB
RUru/innИНН
SEse/orgnrOrgnr
se/personnummerPN
se/vatMomsnr.
SGsg/uenUEN
SIsi/emsoEMŠO
si/vatDDV
SKsk/dicIČ DPH
sk/icoIČO
sk/rc
THth/tinTIN
TRtr/tckimlikT.C. Kimlik
tr/vknVKN
TWtw/ubnUBN
UAua/edrpouЄДРПОУ
USus/einEIN
us/itinITIN
us/rtnRTN
us/ssnSSN
UYuy/rutRUT
VEve/rifRIF
VNvn/mstMST
ZAza/idnrSA ID

API

Every identifier exports the same interface:

typeValidator={name: string;localName: string;abbreviation: string;country?: CountryCode;entityType: "person"|"company"|"any";compact: (value: string)=>string;format: (value: string)=>string;validate: (value: string)=>ValidateResult;// Optional metadatadescription?: string;sourceUrl?: string;lengths?: readonlynumber[];examples?: readonlystring[];aliases?: readonlystring[];candidatePattern?: string;// Available on most validatorsgenerate?: ()=>string;};typeValidateResult=|{valid: true;compact: string}|{valid: false;error: {code:
|"INVALID_FORMAT"|"INVALID_LENGTH"|"INVALID_CHECKSUM"|"INVALID_COMPONENT";message: string;};};

Additional exports

Many person-type validators export parse(), returning structured data extracted from the identifier:

import{cz}from"@stll/stdnum";cz.rc.parse("7103192745");// { birthDate: Date, gender: "male" }

Validators with parse(): BA JMBG, BG ЕГН, CN RIC, CU NI, CZ RČ, DK CPR, EE IK, FI HETU, FR NIR, IT CF, KR RRN, KW Civil ID, KZ IIN, LK NIC, MX CURP, MY NRIC, NO Fødselsnr, PL PESEL, RO CNP, SE PN, SI EMŠO, SK RČ, ZA SA ID.

detectNetwork() and the CardNetwork type are top-level named exports:

import{detectNetwork}from"@stll/stdnum";importtype{CardNetwork}from"@stll/stdnum";detectNetwork("4111111111111111");// "visa"

Unicode handling

All validators normalize Unicode artifacts before validation: fullwidth digits, various dashes, non-breaking spaces, zero-width characters, and other common OCR/PDF copy-paste issues are handled automatically.

Development

bun install
bun run rust:check
bun run codegen:check
bun run typecheck
bun test
bun run lint # oxlint
bun run format # oxfmt

License

MIT

About

Validate, compact, and format standard identifiers in TypeScript

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Latest commit

History

81 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

stella

@stll/stdnum

Validate, compact, format, generate, and parse standard identifiers through a single Rust implementation. The npm package uses a native N-API binding on Node.js and Bun; @stll/stdnum-wasm serves browsers and edge runtimes, and the same registry is available from Python as stella-stdnum.

This package covers 11 global identifiers and 96 countries through 176 per-module entry points.

Install

npm install @stll/stdnum
# or
bun add @stll/stdnum

For browser or edge runtimes, install both packages:

npm install @stll/stdnum @stll/stdnum-wasm

For Python 3.11 or newer:

pip install stella-stdnum

Usage

import{cz}from"@stll/stdnum";cz.ico.validate("25596641");// { valid: true, compact: "25596641" }cz.ico.validate("12345678");// { valid: false, error: { code: "INVALID_CHECKSUM", ... }}cz.rc.format("7103192745");// "710319/2745"cz.ico.validateMany(["25596641","12345678"]);// [{ valid: true, compact: "25596641" }, { valid: false, error: ... }]

Tree-shaking

Import only what you need:

importicofrom"@stll/stdnum/cz/ico";importibanfrom"@stll/stdnum/iban";ico.validate("25596641");iban.validate("CZ65 0800 0000 1920 0014 5399");

Browser and edge runtimes

Initialize the WebAssembly runtime once before calling the synchronous validator subpaths:

import{initialize}from"@stll/stdnum/browser";importicofrom"@stll/stdnum/cz/ico";awaitinitialize();ico.validate("25596641");

Concurrent calls to initialize() share the same promise. Calling a validator before initialization throws StdnumNotInitializedError from @stll/stdnum/browser.

The WASM package also exposes the registry API asynchronously:

import{validate}from"@stll/stdnum-wasm";awaitvalidate("cz.ico","25596641");

Python

importstella_stdnumresult=stella_stdnum.validate("cz.ico", "25596641")
assertresult.validassertresult.compact=="25596641"results=stella_stdnum.validate_many("cz.ico", ["25596641", "12345678"])
assert [result.validforresultinresults] == [True, False]

The Rust registry owns validator behavior and metadata. TypeScript subpaths, catalog files, and exact TypeScript/Python registry types are generated from that registry. One committed fixture set checks the Rust, Node.js, WASM, and Python surfaces, and ty checks the installed Python wheel as a real consumer.

Oracle Validation

The repo ships two oracle modes:

  • bun run oracle Runs the strict gate. It keeps only high-confidence cross-checks that are stable enough for CI.
  • bun run oracle:survey Runs the broader ecosystem survey. It includes noisier third-party validators and mutation probes to surface drift without treating every disagreement as a release blocker.

Both commands accept ORACLE_SAMPLES=<n> to trade off runtime against coverage.

bun run performance:oracles is an optional local diagnostic for the handful of specialized JavaScript oracles it covers. It is deliberately not a CI gate: those country-specific comparisons are not representative of the library as a whole. The benchmark alternates execution order and compares paired medians to reduce host noise.

Dependabot watches every JavaScript, Python, Ruby, PHP, and Rust oracle manifest. Its weekly update PRs rerun the strict correctness gates against the new upstream versions before they can merge.

Supported Identifiers

International

IdentifierModuleType
Business Identifier Codebiccompany
Credit Card Numbercreditcardany
Bitcoin Base58Check Addresscrypto/btcbase58any
Bitcoin Bech32 Addresscrypto/btcbech32any
Ethereum Addresscrypto/ethany
Cryptocurrency Wallet Addresscrypto/walletany
EU VAT Numbereu/vatcompany
IBANibanany
International Securities Identification Numberisinany
Legal Entity Identifierleicompany
Luhnluhnany

Countries

96 countries supported (click to expand)
CountryModuleIdentifier
ADad/nrtNRT
AEae/eidEID
AIai/tinTIN
ALal/niptNIPT
AMam/tinTIN
ARar/cbuCBU
ar/cuitCUIT
ar/dniDNI
ATat/businessidFN
at/tinTIN
at/uidUID
at/vnrVNR
AUau/abnABN
au/acnACN
au/tfnTFN
AZaz/voenVÖEN
BAba/jmbgJMBG
BDbd/nidNID
BEbe/bisBIS
be/nnNN
be/vatBTW
BGbg/egnЕГН
bg/pnfЛНЧ
bg/vatИН по ДДС
BHbh/cprCPR
BRbr/cnpjCNPJ
br/cpfCPF
BYby/unpУНП
BZbz/tinTIN
CAca/bnBN
ca/sinSIN
CHch/ssnAHV
ch/uidUID
ch/vatMWST
CLcl/rutRUT
CNcn/ricRIC
cn/usccUSCC
COco/nitNIT
CRcr/cpfCPF
CUcu/niNI
CYcy/vatΦΠΑ
CZcz/bankaccountČíslo účtu
cz/dicDIČ
cz/icoIČO
cz/rc
DEde/handelsregHReg
de/idnrIdNr
de/stnrStNr
de/svnrSVNR
de/vatUSt-IdNr.
DKdk/cprCPR
dk/cvrCVR
dk/vatCVR
DOdo/rncRNC
ECec/rucRUC
EEee/ikIK
ee/registrikoodRegistrikood
ee/vatKMKR
EGeg/tnTN
ESes/cifCIF
es/dniDNI
es/nieNIE
es/nssNSS
es/vatNIF
FIfi/hetuHETU
fi/vatALV nro
fi/ytunnusY-tunnus
FRfr/nifNIF
fr/nirNIR
fr/sirenSIREN
fr/siretSIRET
fr/tvaTVA
GBgb/nhsNHS
gb/ninoNINO
gb/sedolSEDOL
gb/utrUTR
gb/vatVAT
GEge/pinPIN
GHgh/tinTIN
GRgr/amkaΑΜΚΑ
gr/vatΑΦΜ
GTgt/nitNIT
HKhk/hkidHKID
HRhr/vatOIB
HUhu/vatANUM
IDid/npwpNPWP
IEie/ppsPPS
ie/vatVAT
ILil/idnrת.ז.
INin/aadhaarAadhaar
in/gstinGSTIN
in/panPAN
IQiq/nidNID
IRir/nidNID
ISis/kennitalakt.
is/vskVSK
ITit/codicefiscaleCF
it/ivaP.IVA
JPjp/cnCN
jp/mynumberMy Number
KRkr/brnBRN
kr/rrnRRN
KWkw/civilCivil ID
KZkz/iinIIN
LIli/peidPEID
LKlk/nicNIC
LTlt/asmensAK
lt/vatPVM kodas
LUlu/vatTVA
LVlv/vatPVN
MAma/iceICE
MCmc/tvaTVA
MDmd/idnoIDNO
MEme/pibPIB
MKmk/edbEDB
MTmt/vatVAT
MUmu/brnBRN
MXmx/clabeCLABE
mx/curpCURP
mx/rfcRFC
MYmy/nricNRIC
NGng/ninNIN
NIni/rucRUC
NLnl/bsnBSN
nl/kvkKvK
nl/vatBTW
NOno/fodselsnummerFødselsnr
no/mvaMVA
no/orgnrOrgnr
NZnz/irdIRD
PApa/rucRUC
PEpe/rucRUC
PHph/philidPhilID
PKpk/cnicCNIC
PLpl/nipNIP
pl/peselPESEL
pl/regonREGON
PTpt/ccCC
pt/vatNIF
ROro/cnpCNP
ro/vatCIF
RSrs/pibPIB
RUru/innИНН
SEse/orgnrOrgnr
se/personnummerPN
se/vatMomsnr.
SGsg/uenUEN
SIsi/emsoEMŠO
si/vatDDV
SKsk/dicIČ DPH
sk/icoIČO
sk/rc
THth/tinTIN
TRtr/tckimlikT.C. Kimlik
tr/vknVKN
TWtw/ubnUBN
UAua/edrpouЄДРПОУ
USus/einEIN
us/itinITIN
us/rtnRTN
us/ssnSSN
UYuy/rutRUT
VEve/rifRIF
VNvn/mstMST
ZAza/idnrSA ID

API

Every identifier exports the same interface:

typeValidator={name: string;localName: string;abbreviation: string;country?: CountryCode;entityType: "person"|"company"|"any";compact: (value: string)=>string;format: (value: string)=>string;validate: (value: string)=>ValidateResult;// Optional metadatadescription?: string;sourceUrl?: string;lengths?: readonlynumber[];examples?: readonlystring[];aliases?: readonlystring[];candidatePattern?: string;// Available on most validatorsgenerate?: ()=>string;};typeValidateResult=|{valid: true;compact: string}|{valid: false;error: {code:
|"INVALID_FORMAT"|"INVALID_LENGTH"|"INVALID_CHECKSUM"|"INVALID_COMPONENT";message: string;};};

Additional exports

Many person-type validators export parse(), returning structured data extracted from the identifier:

import{cz}from"@stll/stdnum";cz.rc.parse("7103192745");// { birthDate: Date, gender: "male" }

Validators with parse(): BA JMBG, BG ЕГН, CN RIC, CU NI, CZ RČ, DK CPR, EE IK, FI HETU, FR NIR, IT CF, KR RRN, KW Civil ID, KZ IIN, LK NIC, MX CURP, MY NRIC, NO Fødselsnr, PL PESEL, RO CNP, SE PN, SI EMŠO, SK RČ, ZA SA ID.

detectNetwork() and the CardNetwork type are top-level named exports:

import{detectNetwork}from"@stll/stdnum";importtype{CardNetwork}from"@stll/stdnum";detectNetwork("4111111111111111");// "visa"

Unicode handling

All validators normalize Unicode artifacts before validation: fullwidth digits, various dashes, non-breaking spaces, zero-width characters, and other common OCR/PDF copy-paste issues are handled automatically.

Development

bun install
bun run rust:check
bun run codegen:check
bun run typecheck
bun test
bun run lint # oxlint
bun run format # oxfmt

License

MIT

About

Validate, compact, and format standard identifiers in TypeScript

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

81 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

stella

@stll/stdnum

Validate, compact, format, generate, and parse standard identifiers through a single Rust implementation. The npm package uses a native N-API binding on Node.js and Bun; @stll/stdnum-wasm serves browsers and edge runtimes, and the same registry is available from Python as stella-stdnum.

This package covers 11 global identifiers and 96 countries through 176 per-module entry points.

Install

npm install @stll/stdnum
# or
bun add @stll/stdnum

For browser or edge runtimes, install both packages:

npm install @stll/stdnum @stll/stdnum-wasm

For Python 3.11 or newer:

pip install stella-stdnum

Usage

import{cz}from"@stll/stdnum";cz.ico.validate("25596641");// { valid: true, compact: "25596641" }cz.ico.validate("12345678");// { valid: false, error: { code: "INVALID_CHECKSUM", ... }}cz.rc.format("7103192745");// "710319/2745"cz.ico.validateMany(["25596641","12345678"]);// [{ valid: true, compact: "25596641" }, { valid: false, error: ... }]

Tree-shaking

Import only what you need:

importicofrom"@stll/stdnum/cz/ico";importibanfrom"@stll/stdnum/iban";ico.validate("25596641");iban.validate("CZ65 0800 0000 1920 0014 5399");

Browser and edge runtimes

Initialize the WebAssembly runtime once before calling the synchronous validator subpaths:

import{initialize}from"@stll/stdnum/browser";importicofrom"@stll/stdnum/cz/ico";awaitinitialize();ico.validate("25596641");

Concurrent calls to initialize() share the same promise. Calling a validator before initialization throws StdnumNotInitializedError from @stll/stdnum/browser.

The WASM package also exposes the registry API asynchronously:

import{validate}from"@stll/stdnum-wasm";awaitvalidate("cz.ico","25596641");

Python

importstella_stdnumresult=stella_stdnum.validate("cz.ico", "25596641")
assertresult.validassertresult.compact=="25596641"results=stella_stdnum.validate_many("cz.ico", ["25596641", "12345678"])
assert [result.validforresultinresults] == [True, False]

The Rust registry owns validator behavior and metadata. TypeScript subpaths, catalog files, and exact TypeScript/Python registry types are generated from that registry. One committed fixture set checks the Rust, Node.js, WASM, and Python surfaces, and ty checks the installed Python wheel as a real consumer.

Oracle Validation

The repo ships two oracle modes:

  • bun run oracle Runs the strict gate. It keeps only high-confidence cross-checks that are stable enough for CI.
  • bun run oracle:survey Runs the broader ecosystem survey. It includes noisier third-party validators and mutation probes to surface drift without treating every disagreement as a release blocker.

Both commands accept ORACLE_SAMPLES=<n> to trade off runtime against coverage.

bun run performance:oracles is an optional local diagnostic for the handful of specialized JavaScript oracles it covers. It is deliberately not a CI gate: those country-specific comparisons are not representative of the library as a whole. The benchmark alternates execution order and compares paired medians to reduce host noise.

Dependabot watches every JavaScript, Python, Ruby, PHP, and Rust oracle manifest. Its weekly update PRs rerun the strict correctness gates against the new upstream versions before they can merge.

Supported Identifiers

International

IdentifierModuleType
Business Identifier Codebiccompany
Credit Card Numbercreditcardany
Bitcoin Base58Check Addresscrypto/btcbase58any
Bitcoin Bech32 Addresscrypto/btcbech32any
Ethereum Addresscrypto/ethany
Cryptocurrency Wallet Addresscrypto/walletany
EU VAT Numbereu/vatcompany
IBANibanany
International Securities Identification Numberisinany
Legal Entity Identifierleicompany
Luhnluhnany

Countries

96 countries supported (click to expand)
CountryModuleIdentifier
ADad/nrtNRT
AEae/eidEID
AIai/tinTIN
ALal/niptNIPT
AMam/tinTIN
ARar/cbuCBU
ar/cuitCUIT
ar/dniDNI
ATat/businessidFN
at/tinTIN
at/uidUID
at/vnrVNR
AUau/abnABN
au/acnACN
au/tfnTFN
AZaz/voenVÖEN
BAba/jmbgJMBG
BDbd/nidNID
BEbe/bisBIS
be/nnNN
be/vatBTW
BGbg/egnЕГН
bg/pnfЛНЧ
bg/vatИН по ДДС
BHbh/cprCPR
BRbr/cnpjCNPJ
br/cpfCPF
BYby/unpУНП
BZbz/tinTIN
CAca/bnBN
ca/sinSIN
CHch/ssnAHV
ch/uidUID
ch/vatMWST
CLcl/rutRUT
CNcn/ricRIC
cn/usccUSCC
COco/nitNIT
CRcr/cpfCPF
CUcu/niNI
CYcy/vatΦΠΑ
CZcz/bankaccountČíslo účtu
cz/dicDIČ
cz/icoIČO
cz/rc
DEde/handelsregHReg
de/idnrIdNr
de/stnrStNr
de/svnrSVNR
de/vatUSt-IdNr.
DKdk/cprCPR
dk/cvrCVR
dk/vatCVR
DOdo/rncRNC
ECec/rucRUC
EEee/ikIK
ee/registrikoodRegistrikood
ee/vatKMKR
EGeg/tnTN
ESes/cifCIF
es/dniDNI
es/nieNIE
es/nssNSS
es/vatNIF
FIfi/hetuHETU
fi/vatALV nro
fi/ytunnusY-tunnus
FRfr/nifNIF
fr/nirNIR
fr/sirenSIREN
fr/siretSIRET
fr/tvaTVA
GBgb/nhsNHS
gb/ninoNINO
gb/sedolSEDOL
gb/utrUTR
gb/vatVAT
GEge/pinPIN
GHgh/tinTIN
GRgr/amkaΑΜΚΑ
gr/vatΑΦΜ
GTgt/nitNIT
HKhk/hkidHKID
HRhr/vatOIB
HUhu/vatANUM
IDid/npwpNPWP
IEie/ppsPPS
ie/vatVAT
ILil/idnrת.ז.
INin/aadhaarAadhaar
in/gstinGSTIN
in/panPAN
IQiq/nidNID
IRir/nidNID
ISis/kennitalakt.
is/vskVSK
ITit/codicefiscaleCF
it/ivaP.IVA
JPjp/cnCN
jp/mynumberMy Number
KRkr/brnBRN
kr/rrnRRN
KWkw/civilCivil ID
KZkz/iinIIN
LIli/peidPEID
LKlk/nicNIC
LTlt/asmensAK
lt/vatPVM kodas
LUlu/vatTVA
LVlv/vatPVN
MAma/iceICE
MCmc/tvaTVA
MDmd/idnoIDNO
MEme/pibPIB
MKmk/edbEDB
MTmt/vatVAT
MUmu/brnBRN
MXmx/clabeCLABE
mx/curpCURP
mx/rfcRFC
MYmy/nricNRIC
NGng/ninNIN
NIni/rucRUC
NLnl/bsnBSN
nl/kvkKvK
nl/vatBTW
NOno/fodselsnummerFødselsnr
no/mvaMVA
no/orgnrOrgnr
NZnz/irdIRD
PApa/rucRUC
PEpe/rucRUC
PHph/philidPhilID
PKpk/cnicCNIC
PLpl/nipNIP
pl/peselPESEL
pl/regonREGON
PTpt/ccCC
pt/vatNIF
ROro/cnpCNP
ro/vatCIF
RSrs/pibPIB
RUru/innИНН
SEse/orgnrOrgnr
se/personnummerPN
se/vatMomsnr.
SGsg/uenUEN
SIsi/emsoEMŠO
si/vatDDV
SKsk/dicIČ DPH
sk/icoIČO
sk/rc
THth/tinTIN
TRtr/tckimlikT.C. Kimlik
tr/vknVKN
TWtw/ubnUBN
UAua/edrpouЄДРПОУ
USus/einEIN
us/itinITIN
us/rtnRTN
us/ssnSSN
UYuy/rutRUT
VEve/rifRIF
VNvn/mstMST
ZAza/idnrSA ID

API

Every identifier exports the same interface:

typeValidator={name: string;localName: string;abbreviation: string;country?: CountryCode;entityType: "person"|"company"|"any";compact: (value: string)=>string;format: (value: string)=>string;validate: (value: string)=>ValidateResult;// Optional metadatadescription?: string;sourceUrl?: string;lengths?: readonlynumber[];examples?: readonlystring[];aliases?: readonlystring[];candidatePattern?: string;// Available on most validatorsgenerate?: ()=>string;};typeValidateResult=|{valid: true;compact: string}|{valid: false;error: {code:
|"INVALID_FORMAT"|"INVALID_LENGTH"|"INVALID_CHECKSUM"|"INVALID_COMPONENT";message: string;};};

Additional exports

Many person-type validators export parse(), returning structured data extracted from the identifier:

import{cz}from"@stll/stdnum";cz.rc.parse("7103192745");// { birthDate: Date, gender: "male" }

Validators with parse(): BA JMBG, BG ЕГН, CN RIC, CU NI, CZ RČ, DK CPR, EE IK, FI HETU, FR NIR, IT CF, KR RRN, KW Civil ID, KZ IIN, LK NIC, MX CURP, MY NRIC, NO Fødselsnr, PL PESEL, RO CNP, SE PN, SI EMŠO, SK RČ, ZA SA ID.

detectNetwork() and the CardNetwork type are top-level named exports:

import{detectNetwork}from"@stll/stdnum";importtype{CardNetwork}from"@stll/stdnum";detectNetwork("4111111111111111");// "visa"

Unicode handling

All validators normalize Unicode artifacts before validation: fullwidth digits, various dashes, non-breaking spaces, zero-width characters, and other common OCR/PDF copy-paste issues are handled automatically.

Development

bun install
bun run rust:check
bun run codegen:check
bun run typecheck
bun test
bun run lint # oxlint
bun run format # oxfmt

License

MIT

About

Validate, compact, and format standard identifiers in TypeScript

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Latest commit

History

81 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

stella

@stll/stdnum

Validate, compact, format, generate, and parse standard identifiers through a single Rust implementation. The npm package uses a native N-API binding on Node.js and Bun; @stll/stdnum-wasm serves browsers and edge runtimes, and the same registry is available from Python as stella-stdnum.

This package covers 11 global identifiers and 96 countries through 176 per-module entry points.

Install

npm install @stll/stdnum
# or
bun add @stll/stdnum

For browser or edge runtimes, install both packages:

npm install @stll/stdnum @stll/stdnum-wasm

For Python 3.11 or newer:

pip install stella-stdnum

Usage

import{cz}from"@stll/stdnum";cz.ico.validate("25596641");// { valid: true, compact: "25596641" }cz.ico.validate("12345678");// { valid: false, error: { code: "INVALID_CHECKSUM", ... }}cz.rc.format("7103192745");// "710319/2745"cz.ico.validateMany(["25596641","12345678"]);// [{ valid: true, compact: "25596641" }, { valid: false, error: ... }]

Tree-shaking

Import only what you need:

importicofrom"@stll/stdnum/cz/ico";importibanfrom"@stll/stdnum/iban";ico.validate("25596641");iban.validate("CZ65 0800 0000 1920 0014 5399");

Browser and edge runtimes

Initialize the WebAssembly runtime once before calling the synchronous validator subpaths:

import{initialize}from"@stll/stdnum/browser";importicofrom"@stll/stdnum/cz/ico";awaitinitialize();ico.validate("25596641");

Concurrent calls to initialize() share the same promise. Calling a validator before initialization throws StdnumNotInitializedError from @stll/stdnum/browser.

The WASM package also exposes the registry API asynchronously:

import{validate}from"@stll/stdnum-wasm";awaitvalidate("cz.ico","25596641");

Python

importstella_stdnumresult=stella_stdnum.validate("cz.ico", "25596641")
assertresult.validassertresult.compact=="25596641"results=stella_stdnum.validate_many("cz.ico", ["25596641", "12345678"])
assert [result.validforresultinresults] == [True, False]

The Rust registry owns validator behavior and metadata. TypeScript subpaths, catalog files, and exact TypeScript/Python registry types are generated from that registry. One committed fixture set checks the Rust, Node.js, WASM, and Python surfaces, and ty checks the installed Python wheel as a real consumer.

Oracle Validation

The repo ships two oracle modes:

  • bun run oracle Runs the strict gate. It keeps only high-confidence cross-checks that are stable enough for CI.
  • bun run oracle:survey Runs the broader ecosystem survey. It includes noisier third-party validators and mutation probes to surface drift without treating every disagreement as a release blocker.

Both commands accept ORACLE_SAMPLES=<n> to trade off runtime against coverage.

bun run performance:oracles is an optional local diagnostic for the handful of specialized JavaScript oracles it covers. It is deliberately not a CI gate: those country-specific comparisons are not representative of the library as a whole. The benchmark alternates execution order and compares paired medians to reduce host noise.

Dependabot watches every JavaScript, Python, Ruby, PHP, and Rust oracle manifest. Its weekly update PRs rerun the strict correctness gates against the new upstream versions before they can merge.

Supported Identifiers

International

IdentifierModuleType
Business Identifier Codebiccompany
Credit Card Numbercreditcardany
Bitcoin Base58Check Addresscrypto/btcbase58any
Bitcoin Bech32 Addresscrypto/btcbech32any
Ethereum Addresscrypto/ethany
Cryptocurrency Wallet Addresscrypto/walletany
EU VAT Numbereu/vatcompany
IBANibanany
International Securities Identification Numberisinany
Legal Entity Identifierleicompany
Luhnluhnany

Countries

96 countries supported (click to expand)
CountryModuleIdentifier
ADad/nrtNRT
AEae/eidEID
AIai/tinTIN
ALal/niptNIPT
AMam/tinTIN
ARar/cbuCBU
ar/cuitCUIT
ar/dniDNI
ATat/businessidFN
at/tinTIN
at/uidUID
at/vnrVNR
AUau/abnABN
au/acnACN
au/tfnTFN
AZaz/voenVÖEN
BAba/jmbgJMBG
BDbd/nidNID
BEbe/bisBIS
be/nnNN
be/vatBTW
BGbg/egnЕГН
bg/pnfЛНЧ
bg/vatИН по ДДС
BHbh/cprCPR
BRbr/cnpjCNPJ
br/cpfCPF
BYby/unpУНП
BZbz/tinTIN
CAca/bnBN
ca/sinSIN
CHch/ssnAHV
ch/uidUID
ch/vatMWST
CLcl/rutRUT
CNcn/ricRIC
cn/usccUSCC
COco/nitNIT
CRcr/cpfCPF
CUcu/niNI
CYcy/vatΦΠΑ
CZcz/bankaccountČíslo účtu
cz/dicDIČ
cz/icoIČO
cz/rc
DEde/handelsregHReg
de/idnrIdNr
de/stnrStNr
de/svnrSVNR
de/vatUSt-IdNr.
DKdk/cprCPR
dk/cvrCVR
dk/vatCVR
DOdo/rncRNC
ECec/rucRUC
EEee/ikIK
ee/registrikoodRegistrikood
ee/vatKMKR
EGeg/tnTN
ESes/cifCIF
es/dniDNI
es/nieNIE
es/nssNSS
es/vatNIF
FIfi/hetuHETU
fi/vatALV nro
fi/ytunnusY-tunnus
FRfr/nifNIF
fr/nirNIR
fr/sirenSIREN
fr/siretSIRET
fr/tvaTVA
GBgb/nhsNHS
gb/ninoNINO
gb/sedolSEDOL
gb/utrUTR
gb/vatVAT
GEge/pinPIN
GHgh/tinTIN
GRgr/amkaΑΜΚΑ
gr/vatΑΦΜ
GTgt/nitNIT
HKhk/hkidHKID
HRhr/vatOIB
HUhu/vatANUM
IDid/npwpNPWP
IEie/ppsPPS
ie/vatVAT
ILil/idnrת.ז.
INin/aadhaarAadhaar
in/gstinGSTIN
in/panPAN
IQiq/nidNID
IRir/nidNID
ISis/kennitalakt.
is/vskVSK
ITit/codicefiscaleCF
it/ivaP.IVA
JPjp/cnCN
jp/mynumberMy Number
KRkr/brnBRN
kr/rrnRRN
KWkw/civilCivil ID
KZkz/iinIIN
LIli/peidPEID
LKlk/nicNIC
LTlt/asmensAK
lt/vatPVM kodas
LUlu/vatTVA
LVlv/vatPVN
MAma/iceICE
MCmc/tvaTVA
MDmd/idnoIDNO
MEme/pibPIB
MKmk/edbEDB
MTmt/vatVAT
MUmu/brnBRN
MXmx/clabeCLABE
mx/curpCURP
mx/rfcRFC
MYmy/nricNRIC
NGng/ninNIN
NIni/rucRUC
NLnl/bsnBSN
nl/kvkKvK
nl/vatBTW
NOno/fodselsnummerFødselsnr
no/mvaMVA
no/orgnrOrgnr
NZnz/irdIRD
PApa/rucRUC
PEpe/rucRUC
PHph/philidPhilID
PKpk/cnicCNIC
PLpl/nipNIP
pl/peselPESEL
pl/regonREGON
PTpt/ccCC
pt/vatNIF
ROro/cnpCNP
ro/vatCIF
RSrs/pibPIB
RUru/innИНН
SEse/orgnrOrgnr
se/personnummerPN
se/vatMomsnr.
SGsg/uenUEN
SIsi/emsoEMŠO
si/vatDDV
SKsk/dicIČ DPH
sk/icoIČO
sk/rc
THth/tinTIN
TRtr/tckimlikT.C. Kimlik
tr/vknVKN
TWtw/ubnUBN
UAua/edrpouЄДРПОУ
USus/einEIN
us/itinITIN
us/rtnRTN
us/ssnSSN
UYuy/rutRUT
VEve/rifRIF
VNvn/mstMST
ZAza/idnrSA ID

API

Every identifier exports the same interface:

typeValidator={name: string;localName: string;abbreviation: string;country?: CountryCode;entityType: "person"|"company"|"any";compact: (value: string)=>string;format: (value: string)=>string;validate: (value: string)=>ValidateResult;// Optional metadatadescription?: string;sourceUrl?: string;lengths?: readonlynumber[];examples?: readonlystring[];aliases?: readonlystring[];candidatePattern?: string;// Available on most validatorsgenerate?: ()=>string;};typeValidateResult=|{valid: true;compact: string}|{valid: false;error: {code:
|"INVALID_FORMAT"|"INVALID_LENGTH"|"INVALID_CHECKSUM"|"INVALID_COMPONENT";message: string;};};

Additional exports

Many person-type validators export parse(), returning structured data extracted from the identifier:

import{cz}from"@stll/stdnum";cz.rc.parse("7103192745");// { birthDate: Date, gender: "male" }

Validators with parse(): BA JMBG, BG ЕГН, CN RIC, CU NI, CZ RČ, DK CPR, EE IK, FI HETU, FR NIR, IT CF, KR RRN, KW Civil ID, KZ IIN, LK NIC, MX CURP, MY NRIC, NO Fødselsnr, PL PESEL, RO CNP, SE PN, SI EMŠO, SK RČ, ZA SA ID.

detectNetwork() and the CardNetwork type are top-level named exports:

import{detectNetwork}from"@stll/stdnum";importtype{CardNetwork}from"@stll/stdnum";detectNetwork("4111111111111111");// "visa"

Unicode handling

All validators normalize Unicode artifacts before validation: fullwidth digits, various dashes, non-breaking spaces, zero-width characters, and other common OCR/PDF copy-paste issues are handled automatically.

Development

bun install
bun run rust:check
bun run codegen:check
bun run typecheck
bun test
bun run lint # oxlint
bun run format # oxfmt

License

MIT

About

Validate, compact, and format standard identifiers in TypeScript

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Latest commit

History

81 Commits

Folders and files

NameName
Last commit message
Last commit date

Repository files navigation

stella

@stll/stdnum

Validate, compact, format, generate, and parse standard identifiers through a single Rust implementation. The npm package uses a native N-API binding on Node.js and Bun; @stll/stdnum-wasm serves browsers and edge runtimes, and the same registry is available from Python as stella-stdnum.

This package covers 11 global identifiers and 96 countries through 176 per-module entry points.

Install

npm install @stll/stdnum
# or
bun add @stll/stdnum

For browser or edge runtimes, install both packages:

npm install @stll/stdnum @stll/stdnum-wasm

For Python 3.11 or newer:

pip install stella-stdnum

Usage

import{cz}from"@stll/stdnum";cz.ico.validate("25596641");// { valid: true, compact: "25596641" }cz.ico.validate("12345678");// { valid: false, error: { code: "INVALID_CHECKSUM", ... }}cz.rc.format("7103192745");// "710319/2745"cz.ico.validateMany(["25596641","12345678"]);// [{ valid: true, compact: "25596641" }, { valid: false, error: ... }]

Tree-shaking

Import only what you need:

importicofrom"@stll/stdnum/cz/ico";importibanfrom"@stll/stdnum/iban";ico.validate("25596641");iban.validate("CZ65 0800 0000 1920 0014 5399");

Browser and edge runtimes

Initialize the WebAssembly runtime once before calling the synchronous validator subpaths:

import{initialize}from"@stll/stdnum/browser";importicofrom"@stll/stdnum/cz/ico";awaitinitialize();ico.validate("25596641");

Concurrent calls to initialize() share the same promise. Calling a validator before initialization throws StdnumNotInitializedError from @stll/stdnum/browser.

The WASM package also exposes the registry API asynchronously:

import{validate}from"@stll/stdnum-wasm";awaitvalidate("cz.ico","25596641");

Python

importstella_stdnumresult=stella_stdnum.validate("cz.ico", "25596641")
assertresult.validassertresult.compact=="25596641"results=stella_stdnum.validate_many("cz.ico", ["25596641", "12345678"])
assert [result.validforresultinresults] == [True, False]

The Rust registry owns validator behavior and metadata. TypeScript subpaths, catalog files, and exact TypeScript/Python registry types are generated from that registry. One committed fixture set checks the Rust, Node.js, WASM, and Python surfaces, and ty checks the installed Python wheel as a real consumer.

Oracle Validation

The repo ships two oracle modes:

  • bun run oracle Runs the strict gate. It keeps only high-confidence cross-checks that are stable enough for CI.
  • bun run oracle:survey Runs the broader ecosystem survey. It includes noisier third-party validators and mutation probes to surface drift without treating every disagreement as a release blocker.

Both commands accept ORACLE_SAMPLES=<n> to trade off runtime against coverage.

bun run performance:oracles is an optional local diagnostic for the handful of specialized JavaScript oracles it covers. It is deliberately not a CI gate: those country-specific comparisons are not representative of the library as a whole. The benchmark alternates execution order and compares paired medians to reduce host noise.

Dependabot watches every JavaScript, Python, Ruby, PHP, and Rust oracle manifest. Its weekly update PRs rerun the strict correctness gates against the new upstream versions before they can merge.

Supported Identifiers

International

IdentifierModuleType
Business Identifier Codebiccompany
Credit Card Numbercreditcardany
Bitcoin Base58Check Addresscrypto/btcbase58any
Bitcoin Bech32 Addresscrypto/btcbech32any
Ethereum Addresscrypto/ethany
Cryptocurrency Wallet Addresscrypto/walletany
EU VAT Numbereu/vatcompany
IBANibanany
International Securities Identification Numberisinany
Legal Entity Identifierleicompany
Luhnluhnany

Countries

96 countries supported (click to expand)
CountryModuleIdentifier
ADad/nrtNRT
AEae/eidEID
AIai/tinTIN
ALal/niptNIPT
AMam/tinTIN
ARar/cbuCBU
ar/cuitCUIT
ar/dniDNI
ATat/businessidFN
at/tinTIN
at/uidUID
at/vnrVNR
AUau/abnABN
au/acnACN
au/tfnTFN
AZaz/voenVÖEN
BAba/jmbgJMBG
BDbd/nidNID
BEbe/bisBIS
be/nnNN
be/vatBTW
BGbg/egnЕГН
bg/pnfЛНЧ
bg/vatИН по ДДС
BHbh/cprCPR
BRbr/cnpjCNPJ
br/cpfCPF
BYby/unpУНП
BZbz/tinTIN
CAca/bnBN
ca/sinSIN
CHch/ssnAHV
ch/uidUID
ch/vatMWST
CLcl/rutRUT
CNcn/ricRIC
cn/usccUSCC
COco/nitNIT
CRcr/cpfCPF
CUcu/niNI
CYcy/vatΦΠΑ
CZcz/bankaccountČíslo účtu
cz/dicDIČ
cz/icoIČO
cz/rc
DEde/handelsregHReg
de/idnrIdNr
de/stnrStNr
de/svnrSVNR
de/vatUSt-IdNr.
DKdk/cprCPR
dk/cvrCVR
dk/vatCVR
DOdo/rncRNC
ECec/rucRUC
EEee/ikIK
ee/registrikoodRegistrikood
ee/vatKMKR
EGeg/tnTN
ESes/cifCIF
es/dniDNI
es/nieNIE
es/nssNSS
es/vatNIF
FIfi/hetuHETU
fi/vatALV nro
fi/ytunnusY-tunnus
FRfr/nifNIF
fr/nirNIR
fr/sirenSIREN
fr/siretSIRET
fr/tvaTVA
GBgb/nhsNHS
gb/ninoNINO
gb/sedolSEDOL
gb/utrUTR
gb/vatVAT
GEge/pinPIN
GHgh/tinTIN
GRgr/amkaΑΜΚΑ
gr/vatΑΦΜ
GTgt/nitNIT
HKhk/hkidHKID
HRhr/vatOIB
HUhu/vatANUM
IDid/npwpNPWP
IEie/ppsPPS
ie/vatVAT
ILil/idnrת.ז.
INin/aadhaarAadhaar
in/gstinGSTIN
in/panPAN
IQiq/nidNID
IRir/nidNID
ISis/kennitalakt.
is/vskVSK
ITit/codicefiscaleCF
it/ivaP.IVA
JPjp/cnCN
jp/mynumberMy Number
KRkr/brnBRN
kr/rrnRRN
KWkw/civilCivil ID
KZkz/iinIIN
LIli/peidPEID
LKlk/nicNIC
LTlt/asmensAK
lt/vatPVM kodas
LUlu/vatTVA
LVlv/vatPVN
MAma/iceICE
MCmc/tvaTVA
MDmd/idnoIDNO
MEme/pibPIB
MKmk/edbEDB
MTmt/vatVAT
MUmu/brnBRN
MXmx/clabeCLABE
mx/curpCURP
mx/rfcRFC
MYmy/nricNRIC
NGng/ninNIN
NIni/rucRUC
NLnl/bsnBSN
nl/kvkKvK
nl/vatBTW
NOno/fodselsnummerFødselsnr
no/mvaMVA
no/orgnrOrgnr
NZnz/irdIRD
PApa/rucRUC
PEpe/rucRUC
PHph/philidPhilID
PKpk/cnicCNIC
PLpl/nipNIP
pl/peselPESEL
pl/regonREGON
PTpt/ccCC
pt/vatNIF
ROro/cnpCNP
ro/vatCIF
RSrs/pibPIB
RUru/innИНН
SEse/orgnrOrgnr
se/personnummerPN
se/vatMomsnr.
SGsg/uenUEN
SIsi/emsoEMŠO
si/vatDDV
SKsk/dicIČ DPH
sk/icoIČO
sk/rc
THth/tinTIN
TRtr/tckimlikT.C. Kimlik
tr/vknVKN
TWtw/ubnUBN
UAua/edrpouЄДРПОУ
USus/einEIN
us/itinITIN
us/rtnRTN
us/ssnSSN
UYuy/rutRUT
VEve/rifRIF
VNvn/mstMST
ZAza/idnrSA ID

API

Every identifier exports the same interface:

typeValidator={name: string;localName: string;abbreviation: string;country?: CountryCode;entityType: "person"|"company"|"any";compact: (value: string)=>string;format: (value: string)=>string;validate: (value: string)=>ValidateResult;// Optional metadatadescription?: string;sourceUrl?: string;lengths?: readonlynumber[];examples?: readonlystring[];aliases?: readonlystring[];candidatePattern?: string;// Available on most validatorsgenerate?: ()=>string;};typeValidateResult=|{valid: true;compact: string}|{valid: false;error: {code:
|"INVALID_FORMAT"|"INVALID_LENGTH"|"INVALID_CHECKSUM"|"INVALID_COMPONENT";message: string;};};

Additional exports

Many person-type validators export parse(), returning structured data extracted from the identifier:

import{cz}from"@stll/stdnum";cz.rc.parse("7103192745");// { birthDate: Date, gender: "male" }

Validators with parse(): BA JMBG, BG ЕГН, CN RIC, CU NI, CZ RČ, DK CPR, EE IK, FI HETU, FR NIR, IT CF, KR RRN, KW Civil ID, KZ IIN, LK NIC, MX CURP, MY NRIC, NO Fødselsnr, PL PESEL, RO CNP, SE PN, SI EMŠO, SK RČ, ZA SA ID.

detectNetwork() and the CardNetwork type are top-level named exports:

import{detectNetwork}from"@stll/stdnum";importtype{CardNetwork}from"@stll/stdnum";detectNetwork("4111111111111111");// "visa"

Unicode handling

All validators normalize Unicode artifacts before validation: fullwidth digits, various dashes, non-breaking spaces, zero-width characters, and other common OCR/PDF copy-paste issues are handled automatically.

Development

bun install
bun run rust:check
bun run codegen:check
bun run typecheck
bun test
bun run lint # oxlint
bun run format # oxfmt

License

MIT

About

Validate, compact, and format standard identifiers in TypeScript

Topics

Resources

Code of conduct

Contributing

Security policy

Stars

1 star

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages