@stll/stdnum 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/LICENSE +1 -5
  2. package/README.md +28 -4
  3. package/dist/_checksums/mod112.js +5 -5
  4. package/dist/_checksums/mod112.js.map +1 -1
  5. package/dist/_util/generate.d.ts +13 -1
  6. package/dist/_util/generate.js +23 -1
  7. package/dist/_util/generate.js.map +1 -1
  8. package/dist/ad/mod.d.ts +3 -2
  9. package/dist/ad/nrt.js +3 -2
  10. package/dist/ad/nrt.js.map +1 -1
  11. package/dist/ae/eid.d.ts +0 -1
  12. package/dist/ae/mod.d.ts +3 -2
  13. package/dist/ai/mod.d.ts +3 -2
  14. package/dist/al/mod.d.ts +3 -2
  15. package/dist/am/mod.d.ts +3 -2
  16. package/dist/ar/cbu.d.ts +0 -1
  17. package/dist/ar/cbu.js +1 -1
  18. package/dist/ar/cbu.js.map +1 -1
  19. package/dist/ar/cuit.d.ts +0 -1
  20. package/dist/ar/cuit.js +12 -12
  21. package/dist/ar/cuit.js.map +1 -1
  22. package/dist/ar/mod.d.ts +3 -2
  23. package/dist/at/mod.d.ts +3 -2
  24. package/dist/at/tin.d.ts +0 -1
  25. package/dist/at/uid.d.ts +0 -1
  26. package/dist/at/vnr.d.ts +3 -2
  27. package/dist/at/vnr.js +20 -3
  28. package/dist/at/vnr.js.map +1 -1
  29. package/dist/au/abn.d.ts +0 -1
  30. package/dist/au/acn.d.ts +0 -1
  31. package/dist/au/mod.d.ts +3 -2
  32. package/dist/au/tfn.d.ts +0 -1
  33. package/dist/az/mod.d.ts +3 -2
  34. package/dist/az/voen.d.ts +0 -1
  35. package/dist/az/voen.js.map +1 -1
  36. package/dist/ba/jmbg.d.ts +1 -2
  37. package/dist/ba/jmbg.js +1 -0
  38. package/dist/ba/jmbg.js.map +1 -1
  39. package/dist/ba/mod.d.ts +3 -2
  40. package/dist/bd/mod.d.ts +3 -2
  41. package/dist/bd/nid.js +10 -11
  42. package/dist/bd/nid.js.map +1 -1
  43. package/dist/be/bis.d.ts +0 -1
  44. package/dist/be/mod.d.ts +3 -2
  45. package/dist/be/nn.d.ts +0 -1
  46. package/dist/be/vat.d.ts +0 -1
  47. package/dist/bg/egn.d.ts +1 -2
  48. package/dist/bg/egn.js +10 -3
  49. package/dist/bg/egn.js.map +1 -1
  50. package/dist/bg/mod.d.ts +3 -2
  51. package/dist/bg/pnf.d.ts +0 -1
  52. package/dist/bg/vat.d.ts +0 -1
  53. package/dist/bh/cpr.d.ts +3 -2
  54. package/dist/bh/cpr.js +8 -2
  55. package/dist/bh/cpr.js.map +1 -1
  56. package/dist/bh/mod.d.ts +3 -2
  57. package/dist/bic.js +13 -15
  58. package/dist/bic.js.map +1 -1
  59. package/dist/br/cnpj.d.ts +0 -1
  60. package/dist/br/cpf.d.ts +0 -1
  61. package/dist/br/mod.d.ts +3 -2
  62. package/dist/by/mod.d.ts +3 -2
  63. package/dist/by/unp.d.ts +0 -1
  64. package/dist/by/unp.js +6 -3
  65. package/dist/by/unp.js.map +1 -1
  66. package/dist/bz/mod.d.ts +3 -2
  67. package/dist/bz/tin.d.ts +3 -2
  68. package/dist/bz/tin.js +9 -1
  69. package/dist/bz/tin.js.map +1 -1
  70. package/dist/ca/bn.d.ts +0 -1
  71. package/dist/ca/mod.d.ts +3 -2
  72. package/dist/ca/sin.d.ts +0 -1
  73. package/dist/ch/mod.d.ts +3 -2
  74. package/dist/ch/ssn.d.ts +0 -1
  75. package/dist/ch/uid.d.ts +0 -1
  76. package/dist/ch/vat.d.ts +0 -1
  77. package/dist/cl/mod.d.ts +3 -2
  78. package/dist/cl/rut.d.ts +0 -1
  79. package/dist/cl/rut.js +1 -1
  80. package/dist/cl/rut.js.map +1 -1
  81. package/dist/cn/mod.d.ts +3 -2
  82. package/dist/cn/ric.d.ts +1 -2
  83. package/dist/cn/ric.js +1 -0
  84. package/dist/cn/ric.js.map +1 -1
  85. package/dist/cn/uscc.d.ts +0 -1
  86. package/dist/cn/uscc.js +7 -6
  87. package/dist/cn/uscc.js.map +1 -1
  88. package/dist/co/mod.d.ts +3 -2
  89. package/dist/co/nit.d.ts +0 -1
  90. package/dist/co/nit.js +4 -1
  91. package/dist/co/nit.js.map +1 -1
  92. package/dist/cr/cpf.js +4 -3
  93. package/dist/cr/cpf.js.map +1 -1
  94. package/dist/cr/mod.d.ts +3 -2
  95. package/dist/creditcard.d.ts +0 -1
  96. package/dist/cu/mod.d.ts +3 -2
  97. package/dist/cu/ni.d.ts +4 -3
  98. package/dist/cu/ni.js +11 -1
  99. package/dist/cu/ni.js.map +1 -1
  100. package/dist/cy/mod.d.ts +3 -2
  101. package/dist/cy/vat.d.ts +0 -1
  102. package/dist/cz/dic.d.ts +0 -1
  103. package/dist/cz/ico.d.ts +0 -1
  104. package/dist/cz/mod.d.ts +3 -2
  105. package/dist/cz/rc.d.ts +1 -2
  106. package/dist/cz/rc.js +33 -8
  107. package/dist/cz/rc.js.map +1 -1
  108. package/dist/de/handelsreg.js +9 -9
  109. package/dist/de/handelsreg.js.map +1 -1
  110. package/dist/de/idnr.d.ts +0 -1
  111. package/dist/de/idnr.js.map +1 -1
  112. package/dist/de/mod.d.ts +3 -2
  113. package/dist/de/stnr.d.ts +0 -1
  114. package/dist/de/stnr.js.map +1 -1
  115. package/dist/de/svnr.d.ts +0 -1
  116. package/dist/de/svnr.js +2 -2
  117. package/dist/de/svnr.js.map +1 -1
  118. package/dist/de/vat.d.ts +0 -1
  119. package/dist/dk/cpr.d.ts +4 -3
  120. package/dist/dk/cpr.js +12 -2
  121. package/dist/dk/cpr.js.map +1 -1
  122. package/dist/dk/cvr.d.ts +0 -1
  123. package/dist/dk/mod.d.ts +3 -2
  124. package/dist/dk/vat.d.ts +0 -1
  125. package/dist/do/mod.d.ts +3 -2
  126. package/dist/do/rnc.d.ts +0 -1
  127. package/dist/do/rnc.js +1 -1
  128. package/dist/do/rnc.js.map +1 -1
  129. package/dist/ec/mod.d.ts +3 -2
  130. package/dist/ec/ruc.d.ts +0 -1
  131. package/dist/ec/ruc.js +1 -1
  132. package/dist/ec/ruc.js.map +1 -1
  133. package/dist/ee/ik.d.ts +1 -2
  134. package/dist/ee/ik.js +12 -4
  135. package/dist/ee/ik.js.map +1 -1
  136. package/dist/ee/mod.d.ts +3 -2
  137. package/dist/ee/registrikood.d.ts +0 -1
  138. package/dist/ee/registrikood.js +8 -7
  139. package/dist/ee/registrikood.js.map +1 -1
  140. package/dist/ee/vat.d.ts +0 -1
  141. package/dist/eg/mod.d.ts +3 -2
  142. package/dist/es/cif.d.ts +0 -1
  143. package/dist/es/cif.js +2 -2
  144. package/dist/es/cif.js.map +1 -1
  145. package/dist/es/dni.d.ts +0 -1
  146. package/dist/es/mod.d.ts +3 -2
  147. package/dist/es/nie.d.ts +0 -1
  148. package/dist/es/nie.js +13 -13
  149. package/dist/es/nie.js.map +1 -1
  150. package/dist/es/nss.d.ts +0 -1
  151. package/dist/es/vat.d.ts +0 -1
  152. package/dist/eu/mod.d.ts +3 -2
  153. package/dist/eu/vat.d.ts +3 -2
  154. package/dist/eu/vat.js +43 -2
  155. package/dist/eu/vat.js.map +1 -1
  156. package/dist/fi/hetu.d.ts +4 -3
  157. package/dist/fi/hetu.js +17 -2
  158. package/dist/fi/hetu.js.map +1 -1
  159. package/dist/fi/mod.d.ts +3 -2
  160. package/dist/fi/vat.d.ts +0 -1
  161. package/dist/fi/ytunnus.d.ts +0 -1
  162. package/dist/fr/mod.d.ts +3 -2
  163. package/dist/fr/nif.d.ts +0 -1
  164. package/dist/fr/nir.d.ts +1 -2
  165. package/dist/fr/nir.js +1 -0
  166. package/dist/fr/nir.js.map +1 -1
  167. package/dist/fr/siren.d.ts +0 -1
  168. package/dist/fr/siret.d.ts +0 -1
  169. package/dist/fr/tva.d.ts +0 -1
  170. package/dist/gb/mod.d.ts +3 -2
  171. package/dist/gb/nino.js +7 -7
  172. package/dist/gb/nino.js.map +1 -1
  173. package/dist/gb/sedol.d.ts +0 -1
  174. package/dist/gb/sedol.js +8 -7
  175. package/dist/gb/sedol.js.map +1 -1
  176. package/dist/gb/utr.d.ts +0 -1
  177. package/dist/gb/vat.d.ts +0 -1
  178. package/dist/ge/mod.d.ts +3 -2
  179. package/dist/gh/mod.d.ts +3 -2
  180. package/dist/gh/tin.d.ts +0 -1
  181. package/dist/gh/tin.js +9 -9
  182. package/dist/gh/tin.js.map +1 -1
  183. package/dist/gr/amka.d.ts +0 -1
  184. package/dist/gr/mod.d.ts +3 -2
  185. package/dist/gr/vat.d.ts +0 -1
  186. package/dist/gt/mod.d.ts +3 -2
  187. package/dist/gt/nit.d.ts +0 -1
  188. package/dist/gt/nit.js +5 -2
  189. package/dist/gt/nit.js.map +1 -1
  190. package/dist/hk/hkid.d.ts +0 -1
  191. package/dist/hk/hkid.js +1 -0
  192. package/dist/hk/hkid.js.map +1 -1
  193. package/dist/hk/mod.d.ts +3 -2
  194. package/dist/hr/mod.d.ts +3 -2
  195. package/dist/hr/vat.d.ts +0 -1
  196. package/dist/hu/mod.d.ts +3 -2
  197. package/dist/hu/vat.d.ts +0 -1
  198. package/dist/iban.d.ts +0 -1
  199. package/dist/id/mod.d.ts +3 -2
  200. package/dist/id/npwp.d.ts +0 -1
  201. package/dist/ie/mod.d.ts +3 -2
  202. package/dist/ie/pps.d.ts +0 -1
  203. package/dist/ie/vat.d.ts +0 -1
  204. package/dist/il/idnr.d.ts +0 -1
  205. package/dist/il/mod.d.ts +3 -2
  206. package/dist/in/aadhaar.d.ts +0 -1
  207. package/dist/in/gstin.d.ts +0 -1
  208. package/dist/in/gstin.js +3 -1
  209. package/dist/in/gstin.js.map +1 -1
  210. package/dist/in/mod.d.ts +3 -2
  211. package/dist/in/pan.js +2 -2
  212. package/dist/in/pan.js.map +1 -1
  213. package/dist/index.d.ts +2 -2
  214. package/dist/iq/mod.d.ts +3 -2
  215. package/dist/ir/mod.d.ts +3 -2
  216. package/dist/ir/nid.d.ts +0 -1
  217. package/dist/is/kennitala.d.ts +0 -1
  218. package/dist/is/mod.d.ts +3 -2
  219. package/dist/isin.d.ts +0 -1
  220. package/dist/isin.js +9 -9
  221. package/dist/isin.js.map +1 -1
  222. package/dist/it/codicefiscale.d.ts +4 -3
  223. package/dist/it/codicefiscale.js +26 -2
  224. package/dist/it/codicefiscale.js.map +1 -1
  225. package/dist/it/iva.d.ts +0 -1
  226. package/dist/it/mod.d.ts +3 -2
  227. package/dist/jp/cn.d.ts +0 -1
  228. package/dist/jp/mod.d.ts +3 -2
  229. package/dist/jp/mynumber.d.ts +0 -1
  230. package/dist/kr/mod.d.ts +3 -2
  231. package/dist/kr/rrn.d.ts +4 -3
  232. package/dist/kr/rrn.js +13 -2
  233. package/dist/kr/rrn.js.map +1 -1
  234. package/dist/kw/civil.d.ts +6 -5
  235. package/dist/kw/civil.js +18 -3
  236. package/dist/kw/civil.js.map +1 -1
  237. package/dist/kw/mod.d.ts +3 -2
  238. package/dist/kz/iin.d.ts +1 -2
  239. package/dist/kz/iin.js +1 -0
  240. package/dist/kz/iin.js.map +1 -1
  241. package/dist/kz/mod.d.ts +3 -2
  242. package/dist/lei.d.ts +0 -1
  243. package/dist/li/mod.d.ts +3 -2
  244. package/dist/lk/mod.d.ts +3 -2
  245. package/dist/lk/nic.d.ts +4 -3
  246. package/dist/lk/nic.js +14 -2
  247. package/dist/lk/nic.js.map +1 -1
  248. package/dist/lt/asmens.d.ts +0 -1
  249. package/dist/lt/mod.d.ts +3 -2
  250. package/dist/lt/vat.d.ts +0 -1
  251. package/dist/lu/mod.d.ts +3 -2
  252. package/dist/lu/vat.d.ts +0 -1
  253. package/dist/luhn.d.ts +0 -1
  254. package/dist/luhn.js +7 -0
  255. package/dist/luhn.js.map +1 -1
  256. package/dist/lv/mod.d.ts +3 -2
  257. package/dist/lv/vat.d.ts +0 -1
  258. package/dist/lv/vat.js +39 -12
  259. package/dist/lv/vat.js.map +1 -1
  260. package/dist/ma/ice.d.ts +0 -1
  261. package/dist/ma/mod.d.ts +3 -2
  262. package/dist/mc/mod.d.ts +3 -2
  263. package/dist/mc/tva.d.ts +0 -1
  264. package/dist/md/idno.d.ts +0 -1
  265. package/dist/md/mod.d.ts +3 -2
  266. package/dist/me/mod.d.ts +3 -2
  267. package/dist/me/pib.d.ts +0 -1
  268. package/dist/mk/edb.d.ts +0 -1
  269. package/dist/mk/mod.d.ts +3 -2
  270. package/dist/mt/mod.d.ts +3 -2
  271. package/dist/mt/vat.d.ts +0 -1
  272. package/dist/mu/mod.d.ts +3 -2
  273. package/dist/mx/clabe.d.ts +0 -1
  274. package/dist/mx/curp.d.ts +4 -3
  275. package/dist/mx/curp.js +25 -3
  276. package/dist/mx/curp.js.map +1 -1
  277. package/dist/mx/mod.d.ts +3 -2
  278. package/dist/mx/rfc.d.ts +3 -2
  279. package/dist/mx/rfc.js +21 -2
  280. package/dist/mx/rfc.js.map +1 -1
  281. package/dist/my/mod.d.ts +3 -2
  282. package/dist/my/nric.d.ts +4 -3
  283. package/dist/my/nric.js +16 -2
  284. package/dist/my/nric.js.map +1 -1
  285. package/dist/ng/mod.d.ts +3 -2
  286. package/dist/ni/mod.d.ts +3 -2
  287. package/dist/ni/ruc.d.ts +0 -1
  288. package/dist/nl/bsn.d.ts +0 -1
  289. package/dist/nl/mod.d.ts +3 -2
  290. package/dist/nl/vat.d.ts +0 -1
  291. package/dist/nl/vat.js +9 -43
  292. package/dist/nl/vat.js.map +1 -1
  293. package/dist/no/fodselsnummer.d.ts +1 -2
  294. package/dist/no/fodselsnummer.js +1 -0
  295. package/dist/no/fodselsnummer.js.map +1 -1
  296. package/dist/no/mod.d.ts +3 -2
  297. package/dist/no/mva.d.ts +0 -1
  298. package/dist/no/orgnr.d.ts +0 -1
  299. package/dist/nz/ird.d.ts +0 -1
  300. package/dist/nz/ird.js +2 -2
  301. package/dist/nz/ird.js.map +1 -1
  302. package/dist/nz/mod.d.ts +3 -2
  303. package/dist/pa/mod.d.ts +3 -2
  304. package/dist/pa/ruc.d.ts +0 -1
  305. package/dist/pa/ruc.js +3 -1
  306. package/dist/pa/ruc.js.map +1 -1
  307. package/dist/patterns.d.ts +1 -4
  308. package/dist/patterns.js +80 -23
  309. package/dist/patterns.js.map +1 -1
  310. package/dist/pe/mod.d.ts +3 -2
  311. package/dist/pe/ruc.d.ts +0 -1
  312. package/dist/pe/ruc.js +9 -9
  313. package/dist/pe/ruc.js.map +1 -1
  314. package/dist/ph/mod.d.ts +3 -2
  315. package/dist/pk/cnic.js.map +1 -1
  316. package/dist/pk/mod.d.ts +3 -2
  317. package/dist/pl/mod.d.ts +3 -2
  318. package/dist/pl/nip.d.ts +0 -1
  319. package/dist/pl/pesel.d.ts +1 -2
  320. package/dist/pl/pesel.js +1 -0
  321. package/dist/pl/pesel.js.map +1 -1
  322. package/dist/pl/regon.d.ts +0 -1
  323. package/dist/pt/cc.d.ts +0 -1
  324. package/dist/pt/cc.js +6 -4
  325. package/dist/pt/cc.js.map +1 -1
  326. package/dist/pt/mod.d.ts +3 -2
  327. package/dist/pt/vat.d.ts +0 -1
  328. package/dist/ro/cnp.d.ts +1 -2
  329. package/dist/ro/cnp.js +1 -0
  330. package/dist/ro/cnp.js.map +1 -1
  331. package/dist/ro/mod.d.ts +3 -2
  332. package/dist/ro/vat.d.ts +0 -1
  333. package/dist/rs/mod.d.ts +3 -2
  334. package/dist/rs/pib.d.ts +0 -1
  335. package/dist/ru/inn.d.ts +0 -1
  336. package/dist/ru/inn.js +1 -1
  337. package/dist/ru/inn.js.map +1 -1
  338. package/dist/ru/mod.d.ts +3 -2
  339. package/dist/se/mod.d.ts +3 -2
  340. package/dist/se/orgnr.d.ts +0 -1
  341. package/dist/se/personnummer.d.ts +1 -2
  342. package/dist/se/personnummer.js +1 -0
  343. package/dist/se/personnummer.js.map +1 -1
  344. package/dist/se/vat.d.ts +0 -1
  345. package/dist/sg/mod.d.ts +3 -2
  346. package/dist/sg/uen.d.ts +0 -1
  347. package/dist/sg/uen.js +46 -55
  348. package/dist/sg/uen.js.map +1 -1
  349. package/dist/si/emso.d.ts +1 -2
  350. package/dist/si/emso.js +17 -4
  351. package/dist/si/emso.js.map +1 -1
  352. package/dist/si/mod.d.ts +3 -2
  353. package/dist/si/vat.d.ts +0 -1
  354. package/dist/sk/dic.d.ts +0 -1
  355. package/dist/sk/ico.d.ts +0 -1
  356. package/dist/sk/mod.d.ts +3 -2
  357. package/dist/sk/rc.d.ts +4 -5
  358. package/dist/sk/rc.js +5 -2
  359. package/dist/sk/rc.js.map +1 -1
  360. package/dist/th/mod.d.ts +3 -2
  361. package/dist/th/tin.d.ts +0 -1
  362. package/dist/tr/mod.d.ts +3 -2
  363. package/dist/tr/tckimlik.d.ts +0 -1
  364. package/dist/tr/vkn.d.ts +0 -1
  365. package/dist/tw/mod.d.ts +3 -2
  366. package/dist/tw/ubn.d.ts +0 -1
  367. package/dist/tw/ubn.js +1 -1
  368. package/dist/tw/ubn.js.map +1 -1
  369. package/dist/types.d.ts +35 -22
  370. package/dist/ua/edrpou.d.ts +0 -1
  371. package/dist/ua/edrpou.js +4 -1
  372. package/dist/ua/edrpou.js.map +1 -1
  373. package/dist/ua/mod.d.ts +3 -2
  374. package/dist/us/ein.js +87 -87
  375. package/dist/us/ein.js.map +1 -1
  376. package/dist/us/itin.js +3 -4
  377. package/dist/us/itin.js.map +1 -1
  378. package/dist/us/mod.d.ts +3 -2
  379. package/dist/us/rtn.d.ts +0 -1
  380. package/dist/us/rtn.js +8 -5
  381. package/dist/us/rtn.js.map +1 -1
  382. package/dist/uy/mod.d.ts +3 -2
  383. package/dist/uy/rut.d.ts +0 -1
  384. package/dist/uy/rut.js +1 -1
  385. package/dist/uy/rut.js.map +1 -1
  386. package/dist/ve/mod.d.ts +3 -2
  387. package/dist/ve/rif.d.ts +0 -1
  388. package/dist/ve/rif.js +11 -11
  389. package/dist/ve/rif.js.map +1 -1
  390. package/dist/vn/mod.d.ts +3 -2
  391. package/dist/vn/mst.d.ts +0 -1
  392. package/dist/za/idnr.d.ts +1 -2
  393. package/dist/za/idnr.js +1 -0
  394. package/dist/za/idnr.js.map +1 -1
  395. package/dist/za/mod.d.ts +3 -2
  396. package/package.json +18 -15
  397. package/src/_checksums/mod112.ts +7 -6
  398. package/src/_util/generate.ts +38 -0
  399. package/src/ad/nrt.ts +12 -4
  400. package/src/ar/cbu.ts +2 -2
  401. package/src/ar/cuit.ts +15 -7
  402. package/src/at/vnr.ts +26 -4
  403. package/src/az/voen.ts +2 -0
  404. package/src/ba/jmbg.ts +2 -1
  405. package/src/bd/nid.ts +4 -4
  406. package/src/bg/egn.ts +12 -4
  407. package/src/bh/cpr.ts +10 -1
  408. package/src/bic.ts +19 -17
  409. package/src/by/unp.ts +9 -4
  410. package/src/bz/tin.ts +12 -1
  411. package/src/cl/rut.ts +4 -2
  412. package/src/cn/ric.ts +4 -1
  413. package/src/cn/uscc.ts +20 -8
  414. package/src/co/nit.ts +6 -2
  415. package/src/cr/cpf.ts +4 -3
  416. package/src/cu/ni.ts +22 -2
  417. package/src/cz/rc.ts +54 -23
  418. package/src/de/handelsreg.ts +14 -3
  419. package/src/de/idnr.ts +3 -0
  420. package/src/de/stnr.ts +4 -0
  421. package/src/de/svnr.ts +7 -2
  422. package/src/dk/cpr.ts +16 -2
  423. package/src/do/rnc.ts +2 -2
  424. package/src/ec/ruc.ts +2 -2
  425. package/src/ee/ik.ts +14 -4
  426. package/src/ee/registrikood.ts +4 -3
  427. package/src/es/cif.ts +2 -3
  428. package/src/es/nie.ts +10 -5
  429. package/src/eu/vat.ts +46 -1
  430. package/src/fi/hetu.ts +19 -2
  431. package/src/fr/nir.ts +2 -1
  432. package/src/gb/nino.ts +11 -10
  433. package/src/gb/sedol.ts +14 -12
  434. package/src/gh/tin.ts +4 -3
  435. package/src/gt/nit.ts +6 -3
  436. package/src/hk/hkid.ts +2 -1
  437. package/src/in/gstin.ts +3 -1
  438. package/src/in/pan.ts +8 -3
  439. package/src/index.ts +3 -0
  440. package/src/isin.ts +10 -4
  441. package/src/it/codicefiscale.ts +40 -2
  442. package/src/kr/rrn.ts +20 -2
  443. package/src/kw/civil.ts +25 -9
  444. package/src/kz/iin.ts +2 -1
  445. package/src/lk/nic.ts +24 -2
  446. package/src/luhn.ts +3 -0
  447. package/src/lv/vat.ts +46 -20
  448. package/src/mx/curp.ts +41 -5
  449. package/src/mx/rfc.ts +35 -4
  450. package/src/my/nric.ts +26 -2
  451. package/src/nl/vat.ts +11 -66
  452. package/src/no/fodselsnummer.ts +2 -1
  453. package/src/nz/ird.ts +4 -4
  454. package/src/pa/ruc.ts +32 -8
  455. package/src/patterns.ts +135 -28
  456. package/src/pe/ruc.ts +6 -7
  457. package/src/pk/cnic.ts +2 -0
  458. package/src/pl/pesel.ts +2 -1
  459. package/src/pt/cc.ts +6 -4
  460. package/src/ro/cnp.ts +2 -1
  461. package/src/ru/inn.ts +2 -2
  462. package/src/se/personnummer.ts +2 -1
  463. package/src/sg/uen.ts +37 -20
  464. package/src/si/emso.ts +30 -5
  465. package/src/sk/rc.ts +13 -4
  466. package/src/tw/ubn.ts +2 -2
  467. package/src/types.ts +37 -6
  468. package/src/ua/edrpou.ts +3 -2
  469. package/src/us/ein.ts +88 -88
  470. package/src/us/itin.ts +7 -8
  471. package/src/us/rtn.ts +16 -11
  472. package/src/uy/rut.ts +2 -2
  473. package/src/ve/rif.ts +11 -6
  474. package/src/za/idnr.ts +2 -1
package/src/patterns.ts CHANGED
@@ -66,10 +66,9 @@ const inferLengths = (v: Validator): number[] => {
66
66
  * "25 123 891" → [2, 3, 3].
67
67
  */
68
68
  const inferGroups = (v: Validator): number[] | null => {
69
- if (!v.examples || v.examples.length === 0) {
70
- return null;
71
- }
72
- const compact = v.compact(v.examples[0]!);
69
+ const first = v.examples?.[0];
70
+ if (first === undefined) return null;
71
+ const compact = v.compact(first);
73
72
  const formatted = v.format(compact);
74
73
  if (formatted === compact) return null;
75
74
 
@@ -98,35 +97,59 @@ const inferGroups = (v: Validator): number[] | null => {
98
97
  * (e.g., "CZ" for DIČ, "CHE" for Swiss UID).
99
98
  */
100
99
  const inferPrefix = (v: Validator): string | null => {
101
- if (!v.examples || v.examples.length === 0) {
102
- return null;
103
- }
104
- const compact = v.compact(v.examples[0]!);
100
+ const examples = v.examples;
101
+ const first = examples?.[0];
102
+ if (!examples || first === undefined) return null;
103
+ const compact = v.compact(first);
105
104
  // Prefix already present in compact form.
106
105
  // But only if ALL examples share the same prefix
107
106
  // (e.g., "CHE" for Swiss UID). If examples have
108
107
  // different prefixes (IBAN: GB/DE/FR), there's
109
108
  // no fixed prefix — it's part of the value.
110
109
  const compactMatch = compact.match(/^([A-Z]+)\d/);
111
- if (compactMatch && v.examples!.length > 1) {
112
- const pfx = compactMatch[1]!;
113
- const allSame = v.examples!.every((ex) =>
114
- v.compact(ex).startsWith(pfx),
110
+ const compactPrefix = compactMatch?.[1];
111
+ if (compactPrefix !== undefined && examples.length > 1) {
112
+ const allSame = examples.every((ex) =>
113
+ v.compact(ex).startsWith(compactPrefix),
115
114
  );
116
- if (allSame) return pfx;
117
- } else if (compactMatch && v.examples!.length === 1) {
118
- return compactMatch[1]!;
115
+ if (allSame) return compactPrefix;
116
+ } else if (
117
+ compactPrefix !== undefined &&
118
+ examples.length === 1
119
+ ) {
120
+ return compactPrefix;
119
121
  }
120
122
  // Prefix added only by format()
121
123
  // (e.g., "DE" for de.vat, "CZ" for cz.dic)
122
124
  const formatted = v.format(compact);
123
125
  const fmtMatch = formatted.match(/^([A-Z]+)[\s\-./]?\d/);
124
- if (fmtMatch && !compact.startsWith(fmtMatch[1]!)) {
125
- return fmtMatch[1]!;
126
+ const fmtPrefix = fmtMatch?.[1];
127
+ if (
128
+ fmtPrefix !== undefined &&
129
+ !compact.startsWith(fmtPrefix)
130
+ ) {
131
+ return fmtPrefix;
126
132
  }
127
133
  return null;
128
134
  };
129
135
 
136
+ /**
137
+ * Character class for a string fragment:
138
+ * `\d` if all digits, `[A-Z]` if all letters,
139
+ * `[A-Z0-9]` if mixed.
140
+ */
141
+ const charClassFor = (s: string): string => {
142
+ let hasLetter = false;
143
+ let hasDigit = false;
144
+ for (const ch of s) {
145
+ if (/[a-zA-Z]/.test(ch)) hasLetter = true;
146
+ if (/\d/.test(ch)) hasDigit = true;
147
+ }
148
+ if (hasLetter && hasDigit) return "[A-Z0-9]";
149
+ if (hasLetter) return "[A-Z]";
150
+ return "\\d";
151
+ };
152
+
130
153
  /**
131
154
  * Determine the character class needed for the
132
155
  * compact form: `\d` if all digits, `[A-Z0-9]`
@@ -136,18 +159,81 @@ const inferCharClass = (v: Validator): string => {
136
159
  if (!v.examples || v.examples.length === 0) {
137
160
  return "\\d";
138
161
  }
139
- let hasLetter = false;
140
- let hasDigit = false;
141
- for (const ex of v.examples) {
142
- const c = v.compact(ex);
143
- for (const ch of c) {
144
- if (/[a-zA-Z]/.test(ch)) hasLetter = true;
145
- if (/\d/.test(ch)) hasDigit = true;
162
+ const combined = v.examples
163
+ .map((ex) => v.compact(ex))
164
+ .join("");
165
+ return charClassFor(combined);
166
+ };
167
+
168
+ type PerGroup = { size: number; charClass: string };
169
+
170
+ /**
171
+ * Infer per-group sizes AND character classes from
172
+ * format(). Returns groups with per-position char
173
+ * classes, e.g., "12 010188 M 01 1" →
174
+ * sizes: [2, 6, 1, 2, 1]
175
+ * classes: ["\d", "\d", "[A-Z]", "\d", "\d"]
176
+ *
177
+ * Only returns non-null when per-group classes
178
+ * differ (some groups are letters, some digits).
179
+ * Letter-only groups before the first digit group
180
+ * are treated as prefixes and excluded (handled
181
+ * by inferPrefix separately).
182
+ */
183
+ const inferPerGroup = (v: Validator): PerGroup[] | null => {
184
+ const first = v.examples?.[0];
185
+ if (first === undefined) return null;
186
+ const compact = v.compact(first);
187
+ const formatted = v.format(compact);
188
+ if (formatted === compact) return null;
189
+
190
+ // Only applies to mixed validators (compact has
191
+ // both letters and digits in separate positions,
192
+ // like German SVNR "12010188M011").
193
+ const compactMixed =
194
+ /[a-zA-Z]/.test(compact) && /\d/.test(compact);
195
+ if (!compactMixed) return null;
196
+
197
+ // Also skip if any single part mixes letters and
198
+ // digits (IBAN, ISIN) — inferGroups already
199
+ // handles those correctly with the global class.
200
+ const parts = formatted.split(/[^a-zA-Z0-9]+/);
201
+ const isAlphanumeric = parts.some(
202
+ (p) => /[a-zA-Z]/.test(p) && /\d/.test(p),
203
+ );
204
+ if (isAlphanumeric) return null;
205
+
206
+ // Keep digit groups and letter-only groups that
207
+ // appear AFTER the first digit group (embedded
208
+ // letters). Skip letter-only groups before the
209
+ // first digit group (prefix like "CHE").
210
+ const filtered: PerGroup[] = [];
211
+ let seenDigitGroup = false;
212
+ for (const p of parts) {
213
+ if (p.length === 0) continue;
214
+ if (/\d/.test(p)) {
215
+ seenDigitGroup = true;
216
+ filtered.push({
217
+ size: p.length,
218
+ charClass: charClassFor(p),
219
+ });
220
+ } else if (seenDigitGroup) {
221
+ filtered.push({
222
+ size: p.length,
223
+ charClass: charClassFor(p),
224
+ });
146
225
  }
147
226
  }
148
- if (hasLetter && hasDigit) return "[A-Z0-9]";
149
- if (hasLetter) return "[A-Z]";
150
- return "\\d";
227
+
228
+ if (filtered.length <= 1) return null;
229
+
230
+ const firstClass = filtered[0]?.charClass;
231
+ const allSame = filtered.every(
232
+ (g) => g.charClass === firstClass,
233
+ );
234
+ if (allSame) return null;
235
+
236
+ return filtered;
151
237
  };
152
238
 
153
239
  /**
@@ -159,6 +245,18 @@ const groupsToPattern = (
159
245
  cc: string,
160
246
  ): string => groups.map((g) => `${cc}{${g}}`).join(SEP);
161
247
 
248
+ /**
249
+ * Build a group regex with per-group char classes.
250
+ * Produces tighter patterns when groups have different
251
+ * character types (e.g., digits vs letters).
252
+ */
253
+ const groupsToPatternPerClass = (
254
+ groups: readonly { size: number; charClass: string }[],
255
+ ): string =>
256
+ groups
257
+ .map(({ size, charClass }) => `${charClass}{${size}}`)
258
+ .join(SEP);
259
+
162
260
  /**
163
261
  * Derive a loose candidate-matching regex from
164
262
  * a validator. The regex finds potential matches;
@@ -175,7 +273,16 @@ export const toRegex = (v: Validator): RegExp => {
175
273
 
176
274
  let pattern: string;
177
275
 
178
- if (groups && lengths.length <= 1) {
276
+ // Use per-group char classes when groups mix
277
+ // digits and letters (e.g., German SVNR:
278
+ // "12 010188 M 01 1" → [\d, \d, [A-Z], \d, \d]).
279
+ // This prevents overly broad [A-Z0-9] from
280
+ // matching all-caps prose as identifiers.
281
+ const perGroup = inferPerGroup(v);
282
+
283
+ if (perGroup && lengths.length <= 1) {
284
+ pattern = groupsToPatternPerClass(perGroup);
285
+ } else if (groups && lengths.length <= 1) {
179
286
  pattern = groupsToPattern(groups, cc);
180
287
  } else if (lengths.length === 1) {
181
288
  pattern = `${cc}{${lengths[0]}}`;
package/src/pe/ruc.ts CHANGED
@@ -15,7 +15,7 @@
15
15
  */
16
16
 
17
17
  import { clean } from "#util/clean";
18
- import { randomDigits } from "#util/generate";
18
+ import { randomDigits, randomPick } from "#util/generate";
19
19
  import { err } from "#util/result";
20
20
  import { isdigits } from "#util/strings";
21
21
 
@@ -29,7 +29,8 @@ import type { ValidateResult, Validator } from "../types";
29
29
  */
30
30
  const VALID_PREFIXES = new Set(["10", "15", "17", "20"]);
31
31
 
32
- const WEIGHTS = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2];
32
+ const WEIGHTS = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2] as const;
33
+ const GENERATE_PREFIXES = ["10", "15", "17", "20"] as const;
33
34
 
34
35
  const compact = (value: string): string =>
35
36
  clean(value, " -.").trim();
@@ -42,8 +43,8 @@ const compact = (value: string): string =>
42
43
  */
43
44
  const calcCheckDigit = (body: string): number => {
44
45
  let sum = 0;
45
- for (let i = 0; i < 10; i++) {
46
- sum += Number(body[i]) * WEIGHTS[i]!;
46
+ for (const [i, weight] of WEIGHTS.entries()) {
47
+ sum += Number(body[i]) * weight;
47
48
  }
48
49
  const remainder = 11 - (sum % 11);
49
50
  if (remainder >= 10) return remainder - 10;
@@ -87,9 +88,7 @@ const format = (value: string): string => compact(value);
87
88
 
88
89
  /** Generate a random valid Peruvian RUC. */
89
90
  const generate = (): string => {
90
- const prefixes = ["10", "15", "17", "20"] as const;
91
- const prefix =
92
- prefixes[Math.floor(Math.random() * prefixes.length)]!;
91
+ const prefix = randomPick(GENERATE_PREFIXES);
93
92
  const body = prefix + randomDigits(8);
94
93
  return body + String(calcCheckDigit(body));
95
94
  };
package/src/pk/cnic.ts CHANGED
@@ -53,6 +53,8 @@ const validate = (value: string): ValidateResult => {
53
53
  );
54
54
  }
55
55
  // First digit must be a valid province code
56
+ // SAFETY: v.length === 13 above guarantees v[0] exists.
57
+ // eslint-disable-next-line no-non-null-assertion
56
58
  if (!VALID_PROVINCES.has(v[0]!)) {
57
59
  return err(
58
60
  "INVALID_COMPONENT",
package/src/pl/pesel.ts CHANGED
@@ -131,7 +131,7 @@ const generate = (): string => {
131
131
  };
132
132
 
133
133
  /** Polish National Identification Number. */
134
- const pesel: Validator = {
134
+ const pesel: Validator<ParsedPersonId> = {
135
135
  name: "Polish National ID",
136
136
  localName:
137
137
  "Powszechny Elektroniczny System Ewidencji Ludności",
@@ -145,6 +145,7 @@ const pesel: Validator = {
145
145
  examples: ["02070803628"] as const,
146
146
  compact,
147
147
  format,
148
+ parse,
148
149
  validate,
149
150
  generate,
150
151
  };
package/src/pt/cc.ts CHANGED
@@ -9,7 +9,7 @@
9
9
  */
10
10
 
11
11
  import { clean } from "#util/clean";
12
- import { randomDigits, randomInt } from "#util/generate";
12
+ import { randomChar, randomDigits } from "#util/generate";
13
13
  import { err } from "#util/result";
14
14
 
15
15
  import type { ValidateResult, Validator } from "../types";
@@ -24,8 +24,10 @@ const compact = (value: string): string =>
24
24
  const calcCheckDigit = (value: string): string => {
25
25
  let sum = 0;
26
26
  for (let i = value.length - 1; i >= 0; i--) {
27
+ const ch = value[i];
28
+ if (ch === undefined) continue;
27
29
  const pos = value.length - 1 - i;
28
- let n = ALPHABET.indexOf(value[i]!);
30
+ let n = ALPHABET.indexOf(ch);
29
31
  if (pos % 2 === 0) {
30
32
  n *= 2;
31
33
  if (n > 9) n = Math.floor(n / 10) + (n % 10);
@@ -70,8 +72,8 @@ const format = (value: string): string => {
70
72
  /** Generate a random valid Portuguese CC number. */
71
73
  const generate = (): string => {
72
74
  const digits = randomDigits(9);
73
- const v0 = ALPHABET[randomInt(0, 35)]!;
74
- const v1 = ALPHABET[randomInt(0, 35)]!;
75
+ const v0 = randomChar(ALPHABET);
76
+ const v1 = randomChar(ALPHABET);
75
77
  const body = digits + v0 + v1;
76
78
  const check = calcCheckDigit(body);
77
79
  return body + check;
package/src/ro/cnp.ts CHANGED
@@ -150,7 +150,7 @@ const generate = (): string => {
150
150
  };
151
151
 
152
152
  /** Romanian Personal Identification Number. */
153
- const cnp: Validator = {
153
+ const cnp: Validator<ParsedPersonId> = {
154
154
  name: "Romanian Personal ID",
155
155
  localName: "Cod Numeric Personal",
156
156
  abbreviation: "CNP",
@@ -163,6 +163,7 @@ const cnp: Validator = {
163
163
  examples: ["1630615123457"] as const,
164
164
  compact,
165
165
  format,
166
+ parse,
166
167
  validate,
167
168
  generate,
168
169
  };
package/src/ru/inn.ts CHANGED
@@ -28,8 +28,8 @@ const checkDigit = (
28
28
  weights: readonly number[],
29
29
  ): number => {
30
30
  let sum = 0;
31
- for (let i = 0; i < weights.length; i++) {
32
- sum += Number(value[i]) * weights[i]!;
31
+ for (const [i, weight] of weights.entries()) {
32
+ sum += Number(value[i]) * weight;
33
33
  }
34
34
  return (sum % 11) % 10;
35
35
  };
@@ -154,7 +154,7 @@ const generate = (): string => {
154
154
  };
155
155
 
156
156
  /** Swedish Personal Identity Number. */
157
- const personnummer: Validator = {
157
+ const personnummer: Validator<ParsedPersonId> = {
158
158
  name: "Swedish Personal ID",
159
159
  localName: "Personnummer",
160
160
  abbreviation: "PN",
@@ -172,6 +172,7 @@ const personnummer: Validator = {
172
172
  examples: ["880320-0016"] as const,
173
173
  compact,
174
174
  format,
175
+ parse,
175
176
  validate,
176
177
  generate,
177
178
  };
package/src/sg/uen.ts CHANGED
@@ -72,6 +72,9 @@ const compact = (value: string): string =>
72
72
  * Validate Business (ROB) UEN (9 chars):
73
73
  * 8 digits + 1 check letter.
74
74
  */
75
+ const BUSINESS_WEIGHTS = [10, 4, 9, 3, 8, 2, 7, 1] as const;
76
+ const BUSINESS_CHECK_ALPHA = "XMKECAWLJDB";
77
+
75
78
  const validateBusiness = (v: string): ValidateResult => {
76
79
  if (!isdigits(v.slice(0, 8))) {
77
80
  return err(
@@ -79,6 +82,9 @@ const validateBusiness = (v: string): ValidateResult => {
79
82
  "Business UEN must start with 8 digits",
80
83
  );
81
84
  }
85
+ // SAFETY: caller routes to this only when
86
+ // v.length === 9.
87
+ // eslint-disable-next-line no-non-null-assertion
82
88
  if (!/^[A-Z]$/.test(v[8]!)) {
83
89
  return err(
84
90
  "INVALID_FORMAT",
@@ -86,13 +92,11 @@ const validateBusiness = (v: string): ValidateResult => {
86
92
  );
87
93
  }
88
94
 
89
- const weights = [10, 4, 9, 3, 8, 2, 7, 1];
90
- const checkAlpha = "XMKECAWLJDB";
91
95
  let sum = 0;
92
- for (let i = 0; i < 8; i++) {
93
- sum += Number(v[i]) * weights[i]!;
96
+ for (const [i, weight] of BUSINESS_WEIGHTS.entries()) {
97
+ sum += Number(v[i]) * weight;
94
98
  }
95
- const expected = checkAlpha[sum % 11];
99
+ const expected = BUSINESS_CHECK_ALPHA[sum % 11];
96
100
  if (v[8] !== expected) {
97
101
  return err(
98
102
  "INVALID_CHECKSUM",
@@ -106,6 +110,9 @@ const validateBusiness = (v: string): ValidateResult => {
106
110
  * Validate Local Company (ROC) UEN (10 chars):
107
111
  * 9 digits (year prefix) + 1 check letter.
108
112
  */
113
+ const LOCAL_WEIGHTS = [10, 8, 6, 4, 9, 7, 5, 3, 1] as const;
114
+ const LOCAL_CHECK_ALPHA = "ZKCMDNERGWH";
115
+
109
116
  const validateLocalCompany = (
110
117
  v: string,
111
118
  ): ValidateResult => {
@@ -115,6 +122,9 @@ const validateLocalCompany = (
115
122
  "Company UEN must have 9 digits",
116
123
  );
117
124
  }
125
+ // SAFETY: caller routes to this only when
126
+ // v.length === 10.
127
+ // eslint-disable-next-line no-non-null-assertion
118
128
  if (!/^[A-Z]$/.test(v[9]!)) {
119
129
  return err(
120
130
  "INVALID_FORMAT",
@@ -122,13 +132,11 @@ const validateLocalCompany = (
122
132
  );
123
133
  }
124
134
 
125
- const weights = [10, 8, 6, 4, 9, 7, 5, 3, 1];
126
- const checkAlpha = "ZKCMDNERGWH";
127
135
  let sum = 0;
128
- for (let i = 0; i < 9; i++) {
129
- sum += Number(v[i]) * weights[i]!;
136
+ for (const [i, weight] of LOCAL_WEIGHTS.entries()) {
137
+ sum += Number(v[i]) * weight;
130
138
  }
131
- const expected = checkAlpha[sum % 11];
139
+ const expected = LOCAL_CHECK_ALPHA[sum % 11];
132
140
  if (v[9] !== expected) {
133
141
  return err(
134
142
  "INVALID_CHECKSUM",
@@ -143,7 +151,12 @@ const validateLocalCompany = (
143
151
  * R/S/T + 2 digits + 2-letter type + 4 digits
144
152
  * + 1 check letter.
145
153
  */
154
+ const OTHER_WEIGHTS = [4, 3, 5, 3, 10, 2, 2, 5, 7] as const;
155
+
146
156
  const validateOther = (v: string): ValidateResult => {
157
+ // SAFETY: caller routes to this only when
158
+ // v.length === 10.
159
+ // eslint-disable-next-line no-non-null-assertion
147
160
  const prefix = v[0]!;
148
161
  if (prefix !== "R" && prefix !== "S" && prefix !== "T") {
149
162
  return err(
@@ -171,17 +184,18 @@ const validateOther = (v: string): ValidateResult => {
171
184
  );
172
185
  }
173
186
 
174
- const weights = [4, 3, 5, 3, 10, 2, 2, 5, 7];
175
187
  let sum = 0;
176
- for (let i = 0; i < 9; i++) {
177
- const idx = OTHER_ALPHA.indexOf(v[i]!);
188
+ for (const [i, weight] of OTHER_WEIGHTS.entries()) {
189
+ const ch = v[i];
190
+ if (ch === undefined) continue;
191
+ const idx = OTHER_ALPHA.indexOf(ch);
178
192
  if (idx === -1) {
179
193
  return err(
180
194
  "INVALID_FORMAT",
181
- `Unexpected character at position ${i + 1}: ${v[i]}`,
195
+ `Unexpected character at position ${i + 1}: ${ch}`,
182
196
  );
183
197
  }
184
- sum += idx * weights[i]!;
198
+ sum += idx * weight;
185
199
  }
186
200
  const expected =
187
201
  OTHER_ALPHA[(((sum - 5) % 11) + 11) % 11];
@@ -218,6 +232,8 @@ const validate = (value: string): ValidateResult => {
218
232
 
219
233
  // 10 chars: first char digit => local company,
220
234
  // otherwise "other" entity
235
+ // SAFETY: v.length === 10 here.
236
+ // eslint-disable-next-line no-non-null-assertion
221
237
  if (isdigits(v[0]!)) {
222
238
  return validateLocalCompany(v);
223
239
  }
@@ -228,14 +244,15 @@ const format = (value: string): string => compact(value);
228
244
 
229
245
  /** Generate a random valid Singapore UEN (business format). */
230
246
  const generate = (): string => {
231
- const weights = [10, 4, 9, 3, 8, 2, 7, 1];
232
- const checkAlpha = "XMKECAWLJDB";
233
247
  const body = randomDigits(8);
234
248
  let sum = 0;
235
- for (let i = 0; i < 8; i++) {
236
- sum += Number(body[i]) * weights[i]!;
249
+ for (const [i, weight] of BUSINESS_WEIGHTS.entries()) {
250
+ sum += Number(body[i]) * weight;
237
251
  }
238
- const check = checkAlpha[sum % 11]!;
252
+ // SAFETY: sum % 11 is in 0..10 and BUSINESS_CHECK_ALPHA
253
+ // has 11 characters.
254
+ // eslint-disable-next-line no-non-null-assertion
255
+ const check = BUSINESS_CHECK_ALPHA[sum % 11]!;
239
256
  return body + check;
240
257
  };
241
258
 
package/src/si/emso.ts CHANGED
@@ -5,8 +5,14 @@
5
5
  * encoding DDMMYYY (7-digit birth year), region code,
6
6
  * serial number, and a check digit.
7
7
  *
8
- * @see https://en.wikipedia.org/wiki/Unique_Master_Citizen_Number
9
- * @see https://www.gov.si/
8
+ * Canonical source:
9
+ * - Slovenia government registry overview
10
+ *
11
+ * This is the source behind the stricter register
12
+ * code check (50-59) and the decision to reject
13
+ * future birth dates in the embedded date field.
14
+ *
15
+ * @see https://www.gov.si/teme/registri-in-evidence-prebivalstva/
10
16
  */
11
17
 
12
18
  import { weightedSum } from "#checksums/weighted-sum";
@@ -75,6 +81,23 @@ const validate = (value: string): ValidateResult => {
75
81
  "EMŠO contains an invalid date",
76
82
  );
77
83
  }
84
+ const birthDate = new Date(yyyy, mm - 1, dd);
85
+ const today = new Date();
86
+ today.setHours(0, 0, 0, 0);
87
+ if (birthDate > today) {
88
+ return err(
89
+ "INVALID_COMPONENT",
90
+ "EMŠO cannot contain a future birth date",
91
+ );
92
+ }
93
+
94
+ const register = Number(v.slice(7, 9));
95
+ if (register < 50 || register > 59) {
96
+ return err(
97
+ "INVALID_COMPONENT",
98
+ "EMSO register code must be between 50 and 59",
99
+ );
100
+ }
78
101
 
79
102
  if (calcCheckDigit(v) !== Number(v[12])) {
80
103
  return err(
@@ -120,7 +143,7 @@ const generate = (): string => {
120
143
  const dd = String(randomInt(1, 28)).padStart(2, "0");
121
144
  const mm = String(randomInt(1, 12)).padStart(2, "0");
122
145
  const yyy = String(randomInt(900, 999));
123
- const rr = String(randomInt(50, 99));
146
+ const rr = String(randomInt(50, 59));
124
147
  const sss = String(randomInt(0, 999)).padStart(3, "0");
125
148
  const partial = dd + mm + yyy + rr + sss;
126
149
  const c = partial + String(calcCheckDigit(partial));
@@ -129,7 +152,7 @@ const generate = (): string => {
129
152
  };
130
153
 
131
154
  /** Slovenian Unique Master Citizen Number. */
132
- const emso: Validator = {
155
+ const emso: Validator<ParsedPersonId> = {
133
156
  name: "Slovenian Personal ID",
134
157
  localName: "Enotna matična številka občana",
135
158
  abbreviation: "EMŠO",
@@ -140,10 +163,12 @@ const emso: Validator = {
140
163
  candidatePattern: "\\d{13}",
141
164
  country: "SI",
142
165
  entityType: "person",
143
- sourceUrl: "https://www.gov.si/",
166
+ sourceUrl:
167
+ "https://www.gov.si/teme/registri-in-evidence-prebivalstva/",
144
168
  examples: ["0101006500006"] as const,
145
169
  compact,
146
170
  format,
171
+ parse,
147
172
  validate,
148
173
  generate,
149
174
  };
package/src/sk/rc.ts CHANGED
@@ -8,11 +8,17 @@
8
8
  * @see https://www.minv.sk/
9
9
  */
10
10
 
11
- import { compact, format, parse, validate } from "../cz/rc";
12
- import type { Validator } from "../types";
11
+ import {
12
+ compact,
13
+ format,
14
+ generate,
15
+ parse,
16
+ validate,
17
+ } from "../cz/rc";
18
+ import type { ParsedPersonId, Validator } from "../types";
13
19
 
14
20
  /** Slovak Birth Number. */
15
- const rc: Validator = {
21
+ const rc: Validator<ParsedPersonId> = {
16
22
  name: "Slovak Birth Number",
17
23
  localName: "Rodné číslo",
18
24
  abbreviation: "RČ",
@@ -21,11 +27,14 @@ const rc: Validator = {
21
27
  country: "SK",
22
28
  entityType: "person",
23
29
  sourceUrl: "https://www.minv.sk/",
30
+ lengths: [9, 10] as const,
24
31
  examples: ["7103192745"] as const,
25
32
  compact,
26
33
  format,
34
+ generate,
35
+ parse,
27
36
  validate,
28
37
  };
29
38
 
30
39
  export default rc;
31
- export { compact, format, parse, validate };
40
+ export { compact, format, generate, parse, validate };
package/src/tw/ubn.ts CHANGED
@@ -48,8 +48,8 @@ const validate = (value: string): ValidateResult => {
48
48
  }
49
49
 
50
50
  let sum = 0;
51
- for (let i = 0; i < 8; i++) {
52
- sum += digitSum(Number(v[i]) * WEIGHTS[i]!);
51
+ for (const [i, weight] of WEIGHTS.entries()) {
52
+ sum += digitSum(Number(v[i]) * weight);
53
53
  }
54
54
 
55
55
  const checksum = sum % 10;
package/src/types.ts CHANGED
@@ -116,16 +116,28 @@ export type ValidateResult =
116
116
  | { valid: false; error: StdnumError };
117
117
 
118
118
  /**
119
- * Structured data extracted from a personal ID number.
120
- * Returned by `parse()` on validators that encode
121
- * birth date and gender.
119
+ * Structured data extracted from an identifier that
120
+ * encodes a birth date.
122
121
  */
123
- export type ParsedPersonId = {
122
+ export type ParsedBirthDate = {
124
123
  birthDate: Date;
124
+ };
125
+
126
+ /**
127
+ * Structured data extracted from a personal ID number
128
+ * that also encodes gender.
129
+ */
130
+ export type ParsedPersonId = ParsedBirthDate & {
125
131
  gender: "male" | "female";
126
132
  };
127
133
 
128
- export type Validator = {
134
+ export type ParsedIdentifier =
135
+ | ParsedBirthDate
136
+ | ParsedPersonId;
137
+
138
+ export type Validator<
139
+ TParsed extends ParsedIdentifier | undefined = undefined,
140
+ > = {
129
141
  /** English name. */
130
142
  name: string;
131
143
  /** Name in the local language. */
@@ -197,4 +209,23 @@ export type Validator = {
197
209
  * passed to validate() for confirmation.
198
210
  */
199
211
  candidatePattern?: string;
200
- };
212
+
213
+ /**
214
+ * Extract structured data (e.g. birth date,
215
+ * gender) from a known-valid identifier.
216
+ * Available on personal IDs that encode such
217
+ * data. Returns null if the input cannot be
218
+ * parsed.
219
+ *
220
+ * On the base `Validator` type this is optional
221
+ * with a widened return; narrow it by typing the
222
+ * validator as `Validator<ParsedPersonId>` etc.
223
+ */
224
+ parse?: (value: string) => ParsedIdentifier | null;
225
+ } & ([TParsed] extends [undefined]
226
+ ? unknown
227
+ : { parse: (value: string) => TParsed | null });
228
+
229
+ export type ParsableValidator<
230
+ TParsed extends ParsedIdentifier = ParsedIdentifier,
231
+ > = Validator<TParsed>;