@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/dist/kr/rrn.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { clean } from "../_util/clean.js";
2
+ import { randomDigits, randomInt } from "../_util/generate.js";
2
3
  import { err } from "../_util/result.js";
3
4
  import { isdigits } from "../_util/strings.js";
4
5
  import { isValidDate } from "../_util/date.js";
@@ -106,6 +107,14 @@ const parse = (value) => {
106
107
  gender: isMale ? "male" : "female"
107
108
  };
108
109
  };
110
+ /** Generate a random valid Korean RRN. */
111
+ const generate = () => {
112
+ const year = randomInt(1900, 2024);
113
+ const month = String(randomInt(1, 12)).padStart(2, "0");
114
+ const day = String(randomInt(1, 28)).padStart(2, "0");
115
+ const body = `${String(year % 100).padStart(2, "0")}${month}${day}${year >= 2e3 ? String(randomInt(3, 4)) : String(randomInt(1, 2))}${String(randomInt(0, 96)).padStart(2, "0")}${randomDigits(3)}`;
116
+ return `${body}${String(checkDigit(body))}`;
117
+ };
109
118
  /** Korean Resident Registration Number. */
110
119
  const rrn = {
111
120
  name: "Korean Resident Registration Number",
@@ -125,9 +134,11 @@ const rrn = {
125
134
  examples: ["9710139019902", "9501011000109"],
126
135
  compact,
127
136
  format,
128
- validate
137
+ parse,
138
+ validate,
139
+ generate
129
140
  };
130
141
  //#endregion
131
- export { compact, rrn as default, format, parse, validate };
142
+ export { compact, rrn as default, format, generate, parse, validate };
132
143
 
133
144
  //# sourceMappingURL=rrn.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"rrn.js","names":[],"sources":["../../src/kr/rrn.ts"],"sourcesContent":["/**\n * RRN (Resident Registration Number, 주민등록번호).\n *\n * Korean Resident Registration Number. 13 digits\n * formatted as YYMMDD-GPPCCSD. Encodes date of\n * birth, gender/century, place of birth, community\n * center, serial, and a check digit.\n *\n * @see https://en.wikipedia.org/wiki/Resident_registration_number\n */\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n Validator,\n} from \"../types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -/\");\n\nconst CHECK_WEIGHTS = [2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5];\n\n/**\n * Compute the check digit for the first 12 digits.\n * Formula: (11 - sum(w_i * d_i) % 11) % 10.\n */\nconst checkDigit = (digits: string): number => {\n let sum = 0;\n for (let i = 0; i < 12; i++) {\n sum +=\n (CHECK_WEIGHTS[i] ?? 0) * Number(digits.charAt(i));\n }\n return (11 - (sum % 11)) % 10;\n};\n\n/** Century prefix derived from the 7th digit. */\nconst CENTURY_BY_GENDER: Record<string, number> = {\n \"0\": 1800,\n \"1\": 1900,\n \"2\": 1900,\n \"3\": 2000,\n \"4\": 2000,\n \"5\": 1900,\n \"6\": 1900,\n \"7\": 2000,\n \"8\": 2000,\n \"9\": 1800,\n};\n\n/**\n * Resolve birth date components from a compacted RRN.\n * Returns null if the date is invalid.\n */\nconst resolveBirthDate = (\n v: string,\n): { year: number; month: number; day: number } | null => {\n const century = CENTURY_BY_GENDER[v.charAt(6)];\n if (century === undefined) return null;\n\n const year = century + Number(v.slice(0, 2));\n const month = Number(v.slice(2, 4));\n const day = Number(v.slice(4, 6));\n\n if (!isValidDate(year, month, day)) return null;\n return { year, month, day };\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 13) {\n return err(\n \"INVALID_LENGTH\",\n \"RRN must be exactly 13 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"RRN must contain only digits\",\n );\n }\n if (resolveBirthDate(v) === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"RRN contains an invalid date of birth\",\n );\n }\n const place = Number(v.slice(7, 9));\n if (place > 96) {\n return err(\n \"INVALID_COMPONENT\",\n \"RRN place-of-birth code must be <= 96\",\n );\n }\n if (checkDigit(v.slice(0, 12)) !== Number(v[12])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"RRN check digit does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n if (v.length === 13) {\n return `${v.slice(0, 6)}-${v.slice(6)}`;\n }\n return v;\n};\n\n/**\n * Extract birth date and gender from an RRN.\n * Returns null if the value is not valid.\n *\n * Gender digit mapping:\n * 1, 3 = male Korean; 2, 4 = female Korean\n * 5, 7 = male foreigner; 6, 8 = female foreigner\n * 0, 9 = pre-1800 (male/female by convention)\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const bd = resolveBirthDate(v);\n if (bd === null) return null;\n\n const genderDigit = v.charAt(6);\n const isMale = \"13579\".includes(genderDigit);\n\n return {\n birthDate: new Date(bd.year, bd.month - 1, bd.day),\n gender: isMale ? \"male\" : \"female\",\n };\n};\n\n/** Korean Resident Registration Number. */\nconst rrn: Validator = {\n name: \"Korean Resident Registration Number\",\n localName: \"주민등록번호\",\n abbreviation: \"RRN\",\n aliases: [\n \"RRN\",\n \"주민등록번호\",\n \"Resident Registration Number\",\n ] as const,\n candidatePattern: \"\\\\d{6}[\\\\s-]?\\\\d{7}\",\n country: \"KR\",\n entityType: \"person\",\n description:\n \"13-digit personal identifier issued to all\" +\n \" residents of South Korea\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/\" +\n \"Resident_registration_number\",\n lengths: [13] as const,\n examples: [\"9710139019902\", \"9501011000109\"] as const,\n compact,\n format,\n validate,\n};\n\nexport default rrn;\nexport { compact, format, parse, validate };\n"],"mappings":";;;;;;;;;;;;;;;AAsBA,MAAM,WAAW,UACf,MAAM,OAAO,MAAM;AAErB,MAAM,gBAAgB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;;;;;AAM1D,MAAM,cAAc,WAA2B;CAC7C,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,IACtB,SACG,cAAc,MAAM,KAAK,OAAO,OAAO,OAAO,EAAE,CAAC;AAEtD,SAAQ,KAAM,MAAM,MAAO;;;AAI7B,MAAM,oBAA4C;CAChD,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACL,KAAK;CACN;;;;;AAMD,MAAM,oBACJ,MACwD;CACxD,MAAM,UAAU,kBAAkB,EAAE,OAAO,EAAE;AAC7C,KAAI,YAAY,KAAA,EAAW,QAAO;CAElC,MAAM,OAAO,UAAU,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAC5C,MAAM,QAAQ,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CACnC,MAAM,MAAM,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;AAEjC,KAAI,CAAC,YAAY,MAAM,OAAO,IAAI,CAAE,QAAO;AAC3C,QAAO;EAAE;EAAM;EAAO;EAAK;;AAG7B,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,gCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,+BACD;AAEH,KAAI,iBAAiB,EAAE,KAAK,KAC1B,QAAO,IACL,qBACA,wCACD;AAGH,KADc,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC,GACvB,GACV,QAAO,IACL,qBACA,wCACD;AAEH,KAAI,WAAW,EAAE,MAAM,GAAG,GAAG,CAAC,KAAK,OAAO,EAAE,IAAI,CAC9C,QAAO,IACL,oBACA,iCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE;AAEvC,QAAO;;;;;;;;;;;AAYT,MAAM,SAAS,UAAyC;CACtD,MAAM,SAAS,SAAS,MAAM;AAC9B,KAAI,CAAC,OAAO,MAAO,QAAO;CAE1B,MAAM,IAAI,OAAO;CACjB,MAAM,KAAK,iBAAiB,EAAE;AAC9B,KAAI,OAAO,KAAM,QAAO;CAExB,MAAM,cAAc,EAAE,OAAO,EAAE;CAC/B,MAAM,SAAS,QAAQ,SAAS,YAAY;AAE5C,QAAO;EACL,WAAW,IAAI,KAAK,GAAG,MAAM,GAAG,QAAQ,GAAG,GAAG,IAAI;EAClD,QAAQ,SAAS,SAAS;EAC3B;;;AAIH,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EACP;EACA;EACA;EACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,aACE;CAEF,WACE;CAEF,SAAS,CAAC,GAAG;CACb,UAAU,CAAC,iBAAiB,gBAAgB;CAC5C;CACA;CACA;CACD"}
1
+ {"version":3,"file":"rrn.js","names":[],"sources":["../../src/kr/rrn.ts"],"sourcesContent":["/**\n * RRN (Resident Registration Number, 주민등록번호).\n *\n * Korean Resident Registration Number. 13 digits\n * formatted as YYMMDD-GPPCCSD. Encodes date of\n * birth, gender/century, place of birth, community\n * center, serial, and a check digit.\n *\n * @see https://en.wikipedia.org/wiki/Resident_registration_number\n */\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n Validator,\n} from \"../types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -/\");\n\nconst CHECK_WEIGHTS = [2, 3, 4, 5, 6, 7, 8, 9, 2, 3, 4, 5];\n\n/**\n * Compute the check digit for the first 12 digits.\n * Formula: (11 - sum(w_i * d_i) % 11) % 10.\n */\nconst checkDigit = (digits: string): number => {\n let sum = 0;\n for (let i = 0; i < 12; i++) {\n sum +=\n (CHECK_WEIGHTS[i] ?? 0) * Number(digits.charAt(i));\n }\n return (11 - (sum % 11)) % 10;\n};\n\n/** Century prefix derived from the 7th digit. */\nconst CENTURY_BY_GENDER: Record<string, number> = {\n \"0\": 1800,\n \"1\": 1900,\n \"2\": 1900,\n \"3\": 2000,\n \"4\": 2000,\n \"5\": 1900,\n \"6\": 1900,\n \"7\": 2000,\n \"8\": 2000,\n \"9\": 1800,\n};\n\n/**\n * Resolve birth date components from a compacted RRN.\n * Returns null if the date is invalid.\n */\nconst resolveBirthDate = (\n v: string,\n): { year: number; month: number; day: number } | null => {\n const century = CENTURY_BY_GENDER[v.charAt(6)];\n if (century === undefined) return null;\n\n const year = century + Number(v.slice(0, 2));\n const month = Number(v.slice(2, 4));\n const day = Number(v.slice(4, 6));\n\n if (!isValidDate(year, month, day)) return null;\n return { year, month, day };\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 13) {\n return err(\n \"INVALID_LENGTH\",\n \"RRN must be exactly 13 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"RRN must contain only digits\",\n );\n }\n if (resolveBirthDate(v) === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"RRN contains an invalid date of birth\",\n );\n }\n const place = Number(v.slice(7, 9));\n if (place > 96) {\n return err(\n \"INVALID_COMPONENT\",\n \"RRN place-of-birth code must be <= 96\",\n );\n }\n if (checkDigit(v.slice(0, 12)) !== Number(v[12])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"RRN check digit does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n if (v.length === 13) {\n return `${v.slice(0, 6)}-${v.slice(6)}`;\n }\n return v;\n};\n\n/**\n * Extract birth date and gender from an RRN.\n * Returns null if the value is not valid.\n *\n * Gender digit mapping:\n * 1, 3 = male Korean; 2, 4 = female Korean\n * 5, 7 = male foreigner; 6, 8 = female foreigner\n * 0, 9 = pre-1800 (male/female by convention)\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const bd = resolveBirthDate(v);\n if (bd === null) return null;\n\n const genderDigit = v.charAt(6);\n const isMale = \"13579\".includes(genderDigit);\n\n return {\n birthDate: new Date(bd.year, bd.month - 1, bd.day),\n gender: isMale ? \"male\" : \"female\",\n };\n};\n\n/** Generate a random valid Korean RRN. */\nconst generate = (): string => {\n const year = randomInt(1900, 2024);\n const month = String(randomInt(1, 12)).padStart(2, \"0\");\n const day = String(randomInt(1, 28)).padStart(2, \"0\");\n const yy = String(year % 100).padStart(2, \"0\");\n const genderDigit =\n year >= 2000\n ? String(randomInt(3, 4))\n : String(randomInt(1, 2));\n const place = String(randomInt(0, 96)).padStart(2, \"0\");\n const body = `${yy}${month}${day}${genderDigit}${place}${randomDigits(3)}`;\n return `${body}${String(checkDigit(body))}`;\n};\n\n/** Korean Resident Registration Number. */\nconst rrn: Validator<ParsedPersonId> = {\n name: \"Korean Resident Registration Number\",\n localName: \"주민등록번호\",\n abbreviation: \"RRN\",\n aliases: [\n \"RRN\",\n \"주민등록번호\",\n \"Resident Registration Number\",\n ] as const,\n candidatePattern: \"\\\\d{6}[\\\\s-]?\\\\d{7}\",\n country: \"KR\",\n entityType: \"person\",\n description:\n \"13-digit personal identifier issued to all\" +\n \" residents of South Korea\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/\" +\n \"Resident_registration_number\",\n lengths: [13] as const,\n examples: [\"9710139019902\", \"9501011000109\"] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default rrn;\nexport { compact, format, generate, parse, validate };\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
- import { ParsedPersonId, ValidateResult, Validator } from "../types.js";
1
+ import { ParsedBirthDate, ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/kw/civil.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
@@ -10,9 +9,11 @@ declare const format: (value: string) => string;
10
9
  * Gender is not encoded in the number.
11
10
  * Returns null if the value is not valid.
12
11
  */
13
- declare const parse: (value: string) => Omit<ParsedPersonId, "gender"> | null;
12
+ declare const parse: (value: string) => ParsedBirthDate | null;
13
+ /** Generate a random valid Kuwait civil number. */
14
+ declare const generate: () => string;
14
15
  /** Kuwait Civil Number (الرقم المدني). */
15
- declare const civil: Validator;
16
+ declare const civil: Validator<ParsedBirthDate>;
16
17
  //#endregion
17
- export { compact, civil as default, format, parse, validate };
18
+ export { compact, civil as default, format, generate, parse, validate };
18
19
  //# sourceMappingURL=civil.d.ts.map
package/dist/kw/civil.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { clean } from "../_util/clean.js";
2
+ import { randomDigits, randomInt } from "../_util/generate.js";
2
3
  import { err } from "../_util/result.js";
3
4
  import { isdigits } from "../_util/strings.js";
4
5
  import { isValidDate } from "../_util/date.js";
@@ -36,7 +37,7 @@ const centuryMap = {
36
37
  const compact = (value) => clean(value, " -");
37
38
  const calcCheckDigit = (digits) => {
38
39
  let sum = 0;
39
- for (let i = 0; i < 11; i++) sum += Number(digits[i]) * WEIGHTS[i];
40
+ for (const [i, weight] of WEIGHTS.entries()) sum += Number(digits[i]) * weight;
40
41
  return (11 - sum % 11) % 11;
41
42
  };
42
43
  const validate = (value) => {
@@ -76,6 +77,18 @@ const parse = (value) => {
76
77
  const year = yearBase + yy;
77
78
  return { birthDate: new Date(year, mm - 1, dd) };
78
79
  };
80
+ /** Generate a random valid Kuwait civil number. */
81
+ const generate = () => {
82
+ for (;;) {
83
+ const year = randomInt(1950, 2024);
84
+ const month = String(randomInt(1, 12)).padStart(2, "0");
85
+ const day = String(randomInt(1, 28)).padStart(2, "0");
86
+ const body = `${year >= 2e3 ? "3" : "2"}${String(year % 100).padStart(2, "0")}${month}${day}${randomDigits(4)}`;
87
+ const check = calcCheckDigit(body);
88
+ if (check > 9) continue;
89
+ return `${body}${String(check)}`;
90
+ }
91
+ };
79
92
  /** Kuwait Civil Number (الرقم المدني). */
80
93
  const civil = {
81
94
  name: "Civil Number",
@@ -91,9 +104,11 @@ const civil = {
91
104
  sourceUrl: "https://kuwaitsexpat.com/kuwait-civil-id-format/",
92
105
  compact,
93
106
  format,
94
- validate
107
+ parse,
108
+ validate,
109
+ generate
95
110
  };
96
111
  //#endregion
97
- export { compact, civil as default, format, parse, validate };
112
+ export { compact, civil as default, format, generate, parse, validate };
98
113
 
99
114
  //# sourceMappingURL=civil.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"civil.js","names":[],"sources":["../../src/kw/civil.ts"],"sourcesContent":["/**\n * Civil Number (الرقم المدني, Kuwait).\n *\n * 12-digit number issued by PACI (Public Authority\n * for Civil Information):\n * C = century indicator (2 = 1900s, 3 = 2000s)\n * YYMMDD = birth date\n * SSSS = serial number\n * K = check digit (mod 11 weighted sum)\n *\n * @see https://prakhar.me/articles/kuwait-civil-id-checksum/\n * @see https://kuwaitsexpat.com/kuwait-civil-id-format/\n */\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n Validator,\n} from \"../types\";\n\nconst WEIGHTS = [2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] as const;\n\nconst centuryMap: Record<number, number> = {\n 2: 1900,\n 3: 2000,\n};\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\nconst calcCheckDigit = (digits: string): number => {\n let sum = 0;\n for (let i = 0; i < 11; i++) {\n // SAFETY: loop bound guarantees valid index\n sum += Number(digits[i]) * WEIGHTS[i]!;\n }\n return (11 - (sum % 11)) % 11;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"Kuwait civil number must be 12 digits\",\n );\n }\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Kuwait civil number must contain only digits\",\n );\n }\n\n const centuryDigit = Number(v[0]);\n if (centuryDigit !== 2 && centuryDigit !== 3) {\n return err(\n \"INVALID_COMPONENT\",\n \"Century digit must be 2 or 3\",\n );\n }\n\n const yearBase = centuryMap[centuryDigit] ?? 1900;\n const yy = Number(v.slice(1, 3));\n const mm = Number(v.slice(3, 5));\n const dd = Number(v.slice(5, 7));\n const year = yearBase + yy;\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Civil number contains an invalid birth date\",\n );\n }\n\n const expected = calcCheckDigit(v.slice(0, 11));\n if (expected !== Number(v[11])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Civil number check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return `${v.slice(0, 1)} ${v.slice(1, 7)} ${v.slice(7, 11)} ${v.slice(11)}`;\n};\n\n/**\n * Extract birth date from a Kuwait civil number.\n * Gender is not encoded in the number.\n * Returns null if the value is not valid.\n */\nconst parse = (\n value: string,\n): Omit<ParsedPersonId, \"gender\"> | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const centuryDigit = Number(v[0]);\n const yearBase = centuryMap[centuryDigit] ?? 1900;\n const yy = Number(v.slice(1, 3));\n const mm = Number(v.slice(3, 5));\n const dd = Number(v.slice(5, 7));\n const year = yearBase + yy;\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n };\n};\n\n/** Kuwait Civil Number (الرقم المدني). */\nconst civil: Validator = {\n name: \"Civil Number\",\n localName: \"الرقم المدني\",\n abbreviation: \"Civil ID\",\n aliases: [\"Civil ID\", \"الرقم المدني\"] as const,\n candidatePattern: \"\\\\d{12}\",\n country: \"KW\",\n entityType: \"person\",\n lengths: [12] as const,\n examples: [\"289011200032\", \"305031512348\"] as const,\n description:\n \"12-digit civil identification number issued by PACI\",\n sourceUrl:\n \"https://kuwaitsexpat.com/kuwait-civil-id-format/\",\n compact,\n format,\n validate,\n};\n\nexport default civil;\nexport { compact, format, parse, validate };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAyBA,MAAM,UAAU;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAI;CAAG;CAAG;CAAG;CAAE;AAElD,MAAM,aAAqC;CACzC,GAAG;CACH,GAAG;CACJ;AAED,MAAM,WAAW,UACf,MAAM,OAAO,KAAK;AAEpB,MAAM,kBAAkB,WAA2B;CACjD,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,IAEtB,QAAO,OAAO,OAAO,GAAG,GAAG,QAAQ;AAErC,SAAQ,KAAM,MAAM,MAAO;;AAG7B,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AAExB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,wCACD;AAGH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,+CACD;CAGH,MAAM,eAAe,OAAO,EAAE,GAAG;AACjC,KAAI,iBAAiB,KAAK,iBAAiB,EACzC,QAAO,IACL,qBACA,+BACD;CAGH,MAAM,WAAW,WAAW,iBAAiB;CAC7C,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;AAGhC,KAAI,CAAC,YAFQ,WAAW,IAED,IAAI,GAAG,CAC5B,QAAO,IACL,qBACA,8CACD;AAIH,KADiB,eAAe,EAAE,MAAM,GAAG,GAAG,CAAC,KAC9B,OAAO,EAAE,IAAI,CAC5B,QAAO,IACL,oBACA,0CACD;AAGH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG;;;;;;;AAQ3E,MAAM,SACJ,UAC0C;CAC1C,MAAM,SAAS,SAAS,MAAM;AAC9B,KAAI,CAAC,OAAO,MAAO,QAAO;CAE1B,MAAM,IAAI,OAAO;CAEjB,MAAM,WAAW,WADI,OAAO,EAAE,GAAG,KACY;CAC7C,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,OAAO,WAAW;AAExB,QAAO,EACL,WAAW,IAAI,KAAK,MAAM,KAAK,GAAG,GAAG,EACtC;;;AAIH,MAAM,QAAmB;CACvB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,YAAY,eAAe;CACrC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,SAAS,CAAC,GAAG;CACb,UAAU,CAAC,gBAAgB,eAAe;CAC1C,aACE;CACF,WACE;CACF;CACA;CACA;CACD"}
1
+ {"version":3,"file":"civil.js","names":[],"sources":["../../src/kw/civil.ts"],"sourcesContent":["/**\n * Civil Number (الرقم المدني, Kuwait).\n *\n * 12-digit number issued by PACI (Public Authority\n * for Civil Information):\n * C = century indicator (2 = 1900s, 3 = 2000s)\n * YYMMDD = birth date\n * SSSS = serial number\n * K = check digit (mod 11 weighted sum)\n *\n * @see https://prakhar.me/articles/kuwait-civil-id-checksum/\n * @see https://kuwaitsexpat.com/kuwait-civil-id-format/\n */\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedBirthDate,\n ValidateResult,\n Validator,\n} from \"../types\";\n\nconst WEIGHTS = [2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2] as const;\n\nconst centuryMap: Record<number, number> = {\n 2: 1900,\n 3: 2000,\n};\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\nconst calcCheckDigit = (digits: string): number => {\n let sum = 0;\n for (const [i, weight] of WEIGHTS.entries()) {\n sum += Number(digits[i]) * weight;\n }\n return (11 - (sum % 11)) % 11;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"Kuwait civil number must be 12 digits\",\n );\n }\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Kuwait civil number must contain only digits\",\n );\n }\n\n const centuryDigit = Number(v[0]);\n if (centuryDigit !== 2 && centuryDigit !== 3) {\n return err(\n \"INVALID_COMPONENT\",\n \"Century digit must be 2 or 3\",\n );\n }\n\n const yearBase = centuryMap[centuryDigit] ?? 1900;\n const yy = Number(v.slice(1, 3));\n const mm = Number(v.slice(3, 5));\n const dd = Number(v.slice(5, 7));\n const year = yearBase + yy;\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Civil number contains an invalid birth date\",\n );\n }\n\n const expected = calcCheckDigit(v.slice(0, 11));\n if (expected !== Number(v[11])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Civil number check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return `${v.slice(0, 1)} ${v.slice(1, 7)} ${v.slice(7, 11)} ${v.slice(11)}`;\n};\n\n/**\n * Extract birth date from a Kuwait civil number.\n * Gender is not encoded in the number.\n * Returns null if the value is not valid.\n */\nconst parse = (value: string): ParsedBirthDate | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const centuryDigit = Number(v[0]);\n const yearBase = centuryMap[centuryDigit] ?? 1900;\n const yy = Number(v.slice(1, 3));\n const mm = Number(v.slice(3, 5));\n const dd = Number(v.slice(5, 7));\n const year = yearBase + yy;\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n };\n};\n\n/** Generate a random valid Kuwait civil number. */\nconst generate = (): string => {\n for (;;) {\n const year = randomInt(1950, 2024);\n const month = String(randomInt(1, 12)).padStart(2, \"0\");\n const day = String(randomInt(1, 28)).padStart(2, \"0\");\n const body =\n `${year >= 2000 ? \"3\" : \"2\"}` +\n `${String(year % 100).padStart(2, \"0\")}` +\n `${month}${day}${randomDigits(4)}`;\n const check = calcCheckDigit(body);\n if (check > 9) continue;\n return `${body}${String(check)}`;\n }\n};\n\n/** Kuwait Civil Number (الرقم المدني). */\nconst civil: Validator<ParsedBirthDate> = {\n name: \"Civil Number\",\n localName: \"الرقم المدني\",\n abbreviation: \"Civil ID\",\n aliases: [\"Civil ID\", \"الرقم المدني\"] as const,\n candidatePattern: \"\\\\d{12}\",\n country: \"KW\",\n entityType: \"person\",\n lengths: [12] as const,\n examples: [\"289011200032\", \"305031512348\"] as const,\n description:\n \"12-digit civil identification number issued by PACI\",\n sourceUrl:\n \"https://kuwaitsexpat.com/kuwait-civil-id-format/\",\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default civil;\nexport { compact, format, generate, parse, validate };\n"],"mappings":"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"}
package/dist/kw/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import civil from "./civil.js";
3
2
 
4
3
  //#region src/kw/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { civil };
6
+ }
6
7
  //#endregion
7
8
  export { civil, mod_d_exports };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/kz/iin.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ParsedPersonId, ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/kz/iin.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
@@ -17,7 +16,7 @@ declare const generate: () => string;
17
16
  *
18
17
  * Examples sourced from identique/idnumbers test suite.
19
18
  */
20
- declare const iin: Validator;
19
+ declare const iin: Validator<ParsedPersonId>;
21
20
  //#endregion
22
21
  export { compact, iin as default, format, generate, parse, validate };
23
22
  //# sourceMappingURL=iin.d.ts.map
package/dist/kz/iin.js CHANGED
@@ -140,6 +140,7 @@ const iin = {
140
140
  examples: ["880515300120", "950101400012"],
141
141
  compact,
142
142
  format,
143
+ parse,
143
144
  validate,
144
145
  sourceUrl: "https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Kazakhstan-TIN.pdf",
145
146
  generate
@@ -1 +1 @@
1
- {"version":3,"file":"iin.js","names":[],"sources":["../../src/kz/iin.ts"],"sourcesContent":["/**\n * IIN (Individual Identification Number,\n * Жеке сәйкестендіру нөмірі / ИИН).\n *\n * Kazakhstan individual identification number. 12 digits:\n * YYMMDD = birth date\n * C = century/gender (1-6)\n * SSSS = serial number\n * K = check digit (two-pass weighted sum mod 11)\n *\n * Odd century digits = male, even = female.\n * 1-2 = 1800s, 3-4 = 1900s, 5-6 = 2000s.\n *\n * @see https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Kazakhstan-TIN.pdf\n */\n\nimport { weightedSum } from \"#checksums/weighted-sum\";\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n Validator,\n} from \"../types\";\n\nconst WEIGHTS_1 = [\n 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,\n] as const;\nconst WEIGHTS_2 = [\n 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2,\n] as const;\n\nconst centuryMap: Record<number, number> = {\n 1: 1800,\n 2: 1800,\n 3: 1900,\n 4: 1900,\n 5: 2000,\n 6: 2000,\n};\n\nconst compact = (value: string): string =>\n clean(value, \" -\").trim();\n\n/**\n * Two-pass check digit: weighted sum mod 11 with\n * fallback to second weight set.\n */\nconst calcCheckDigit = (digits: string): number => {\n let remainder = weightedSum(digits, WEIGHTS_1, 11);\n if (remainder === 10) {\n remainder = weightedSum(digits, WEIGHTS_2, 11);\n }\n return remainder === 10 ? 0 : remainder;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"Kazakhstan IIN must be 12 digits\",\n );\n }\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Kazakhstan IIN must contain only digits\",\n );\n }\n\n const centuryDigit = Number(v[6]);\n if (centuryDigit < 1 || centuryDigit > 6) {\n return err(\n \"INVALID_COMPONENT\",\n \"IIN century/gender digit must be 1-6\",\n );\n }\n\n const yearBase = centuryMap[centuryDigit] ?? 1900;\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n const year = yearBase + yy;\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"IIN contains an invalid birth date\",\n );\n }\n\n const expected = calcCheckDigit(v.slice(0, 11));\n if (expected !== Number(v[11])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"IIN check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return `${v.slice(0, 6)} ${v.slice(6)}`;\n};\n\n/**\n * Extract birth date and gender from a Kazakhstan IIN.\n * Returns null if the value is not valid.\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const centuryDigit = Number(v[6]);\n const yearBase = centuryMap[centuryDigit] ?? 1900;\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n const year = yearBase + yy;\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n gender: centuryDigit % 2 === 1 ? \"male\" : \"female\",\n };\n};\n\n/** Generate a random valid Kazakhstan IIN. */\nconst generate = (): string => {\n for (;;) {\n const yy = String(randomInt(50, 99)).padStart(2, \"0\");\n const mm = String(randomInt(1, 12)).padStart(2, \"0\");\n const dd = String(randomInt(1, 28)).padStart(2, \"0\");\n const gender = String(randomInt(1, 6));\n const serial = randomDigits(4);\n const payload = yy + mm + dd + gender + serial;\n const check = calcCheckDigit(payload);\n if (check === null) continue;\n const c = payload + String(check);\n if (validate(c).valid) return c;\n }\n};\n\n/**\n * Kazakhstan Individual Identification Number (IIN).\n *\n * Examples sourced from identique/idnumbers test suite.\n */\nconst iin: Validator = {\n name: \"Kazakhstan Individual ID\",\n localName: \"Жеке сәйкестендіру нөмірі\",\n abbreviation: \"IIN\",\n aliases: [\n \"ИИН\",\n \"IIN\",\n \"индивидуальный идентификационный номер\",\n ] as const,\n candidatePattern: \"\\\\d{12}\",\n country: \"KZ\",\n entityType: \"person\",\n lengths: [12] as const,\n examples: [\"880515300120\", \"950101400012\"] as const,\n compact,\n format,\n validate,\n sourceUrl:\n \"https://www.oecd.org/tax/automatic-exchange/\" +\n \"crs-implementation-and-assistance/\" +\n \"tax-identification-numbers/Kazakhstan-TIN.pdf\",\n generate,\n};\n\nexport default iin;\nexport { compact, format, parse, validate, generate };\n"],"mappings":"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"}
1
+ {"version":3,"file":"iin.js","names":[],"sources":["../../src/kz/iin.ts"],"sourcesContent":["/**\n * IIN (Individual Identification Number,\n * Жеке сәйкестендіру нөмірі / ИИН).\n *\n * Kazakhstan individual identification number. 12 digits:\n * YYMMDD = birth date\n * C = century/gender (1-6)\n * SSSS = serial number\n * K = check digit (two-pass weighted sum mod 11)\n *\n * Odd century digits = male, even = female.\n * 1-2 = 1800s, 3-4 = 1900s, 5-6 = 2000s.\n *\n * @see https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Kazakhstan-TIN.pdf\n */\n\nimport { weightedSum } from \"#checksums/weighted-sum\";\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n Validator,\n} from \"../types\";\n\nconst WEIGHTS_1 = [\n 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,\n] as const;\nconst WEIGHTS_2 = [\n 3, 4, 5, 6, 7, 8, 9, 10, 11, 1, 2,\n] as const;\n\nconst centuryMap: Record<number, number> = {\n 1: 1800,\n 2: 1800,\n 3: 1900,\n 4: 1900,\n 5: 2000,\n 6: 2000,\n};\n\nconst compact = (value: string): string =>\n clean(value, \" -\").trim();\n\n/**\n * Two-pass check digit: weighted sum mod 11 with\n * fallback to second weight set.\n */\nconst calcCheckDigit = (digits: string): number => {\n let remainder = weightedSum(digits, WEIGHTS_1, 11);\n if (remainder === 10) {\n remainder = weightedSum(digits, WEIGHTS_2, 11);\n }\n return remainder === 10 ? 0 : remainder;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"Kazakhstan IIN must be 12 digits\",\n );\n }\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Kazakhstan IIN must contain only digits\",\n );\n }\n\n const centuryDigit = Number(v[6]);\n if (centuryDigit < 1 || centuryDigit > 6) {\n return err(\n \"INVALID_COMPONENT\",\n \"IIN century/gender digit must be 1-6\",\n );\n }\n\n const yearBase = centuryMap[centuryDigit] ?? 1900;\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n const year = yearBase + yy;\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"IIN contains an invalid birth date\",\n );\n }\n\n const expected = calcCheckDigit(v.slice(0, 11));\n if (expected !== Number(v[11])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"IIN check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return `${v.slice(0, 6)} ${v.slice(6)}`;\n};\n\n/**\n * Extract birth date and gender from a Kazakhstan IIN.\n * Returns null if the value is not valid.\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const centuryDigit = Number(v[6]);\n const yearBase = centuryMap[centuryDigit] ?? 1900;\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n const year = yearBase + yy;\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n gender: centuryDigit % 2 === 1 ? \"male\" : \"female\",\n };\n};\n\n/** Generate a random valid Kazakhstan IIN. */\nconst generate = (): string => {\n for (;;) {\n const yy = String(randomInt(50, 99)).padStart(2, \"0\");\n const mm = String(randomInt(1, 12)).padStart(2, \"0\");\n const dd = String(randomInt(1, 28)).padStart(2, \"0\");\n const gender = String(randomInt(1, 6));\n const serial = randomDigits(4);\n const payload = yy + mm + dd + gender + serial;\n const check = calcCheckDigit(payload);\n if (check === null) continue;\n const c = payload + String(check);\n if (validate(c).valid) return c;\n }\n};\n\n/**\n * Kazakhstan Individual Identification Number (IIN).\n *\n * Examples sourced from identique/idnumbers test suite.\n */\nconst iin: Validator<ParsedPersonId> = {\n name: \"Kazakhstan Individual ID\",\n localName: \"Жеке сәйкестендіру нөмірі\",\n abbreviation: \"IIN\",\n aliases: [\n \"ИИН\",\n \"IIN\",\n \"индивидуальный идентификационный номер\",\n ] as const,\n candidatePattern: \"\\\\d{12}\",\n country: \"KZ\",\n entityType: \"person\",\n lengths: [12] as const,\n examples: [\"880515300120\", \"950101400012\"] as const,\n compact,\n format,\n parse,\n validate,\n sourceUrl:\n \"https://www.oecd.org/tax/automatic-exchange/\" +\n \"crs-implementation-and-assistance/\" +\n \"tax-identification-numbers/Kazakhstan-TIN.pdf\",\n generate,\n};\n\nexport default iin;\nexport { compact, format, parse, validate, generate };\n"],"mappings":"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"}
package/dist/kz/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import iin from "./iin.js";
3
2
 
4
3
  //#region src/kz/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { iin };
6
+ }
6
7
  //#endregion
7
8
  export { iin, mod_d_exports };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/lei.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "./types.js";
2
2
 
3
3
  //#region src/lei.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
package/dist/li/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import peid from "./peid.js";
3
2
 
4
3
  //#region src/li/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { peid };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, peid };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/lk/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import nic from "./nic.js";
3
2
 
4
3
  //#region src/lk/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { nic };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, nic };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/lk/nic.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ParsedPersonId, ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/lk/nic.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
@@ -10,8 +9,10 @@ declare const format: (value: string) => string;
10
9
  * Returns null if the value is not valid.
11
10
  */
12
11
  declare const parse: (value: string) => ParsedPersonId | null;
12
+ /** Generate a random valid Sri Lankan NIC. */
13
+ declare const generate: () => string;
13
14
  /** Sri Lankan National Identity Card. */
14
- declare const nic: Validator;
15
+ declare const nic: Validator<ParsedPersonId>;
15
16
  //#endregion
16
- export { compact, nic as default, format, parse, validate };
17
+ export { compact, nic as default, format, generate, parse, validate };
17
18
  //# sourceMappingURL=nic.d.ts.map
package/dist/lk/nic.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { clean } from "../_util/clean.js";
2
+ import { randomDigits, randomInt } from "../_util/generate.js";
2
3
  import { err } from "../_util/result.js";
3
4
  import { isdigits } from "../_util/strings.js";
4
5
  import { isValidDate } from "../_util/date.js";
@@ -116,6 +117,15 @@ const parse = (value) => {
116
117
  gender: info.gender
117
118
  };
118
119
  };
120
+ /** Generate a random valid Sri Lankan NIC. */
121
+ const generate = () => {
122
+ const year = randomInt(1950, 2024);
123
+ const start = new Date(year, 0, 1);
124
+ const end = new Date(year, 11, 31);
125
+ const dayOfYear = randomInt(1, Math.round((end.getTime() - start.getTime()) / 864e5) + 1);
126
+ const body = `${year}${String(dayOfYear + (Math.random() < .5 ? 0 : 500)).padStart(3, "0")}0${randomDigits(3)}`;
127
+ return `${body}${String(checkDigit(body))}`;
128
+ };
119
129
  /** Sri Lankan National Identity Card. */
120
130
  const nic = {
121
131
  name: "National Identity Card",
@@ -131,9 +141,11 @@ const nic = {
131
141
  examples: ["197819202757", "862348753V"],
132
142
  compact,
133
143
  format,
134
- validate
144
+ parse,
145
+ validate,
146
+ generate
135
147
  };
136
148
  //#endregion
137
- export { compact, nic as default, format, parse, validate };
149
+ export { compact, nic as default, format, generate, parse, validate };
138
150
 
139
151
  //# sourceMappingURL=nic.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"nic.js","names":[],"sources":["../../src/lk/nic.ts"],"sourcesContent":["/**\n * NIC (National Identity Card, ජාතික හැඳුනුම්පත).\n *\n * Sri Lankan personal identity number. Two formats:\n * Old (pre-2016): 9 digits + V or X (10 chars)\n * New (2016+): 12 digits\n *\n * Both encode birth date (day-of-year), gender, a\n * serial number, and a check digit.\n *\n * @see https://en.wikipedia.org/wiki/National_identification_number#Sri_Lanka\n * @see https://drp.gov.lk/Templates/Artical%20-%20English%20new%20number.html\n */\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n Validator,\n} from \"../types\";\n\n/** Old format: 9 digits + V or X (case-insensitive). */\nconst OLD_RE = /^\\d{9}[VvXx]$/;\n\n/** New format: exactly 12 digits. */\nconst NEW_RE = /^\\d{12}$/;\n\nconst compact = (value: string): string =>\n clean(value, \" -\").toUpperCase();\n\n/**\n * Convert an old-format NIC to the 12-digit new\n * format for uniform validation.\n *\n * Old: YY DDD SSS C V/X (10 chars)\n * New: 19YY DDD 0SSS C (12 digits)\n */\nconst toNewFormat = (v: string): string => {\n const yy = v.slice(0, 2);\n const ddd = v.slice(2, 5);\n const sss = v.slice(5, 8);\n const check = v[8];\n return `19${yy}${ddd}0${sss}${check}`;\n};\n\n/**\n * Checksum weights for the first 11 digits.\n * Algorithm: weighted sum mod 11, then mod 10\n * if >= 10.\n */\nconst CHECK_WEIGHTS = [\n 8, 4, 3, 2, 7, 6, 5, 7, 4, 3, 2,\n] as const;\n\nconst checkDigit = (digits: string): number => {\n let sum = 0;\n for (let i = 0; i < 11; i++) {\n sum +=\n (CHECK_WEIGHTS[i] ?? 0) * Number(digits.charAt(i));\n }\n const d = 11 - (sum % 11);\n return d > 9 ? d % 10 : d;\n};\n\n/**\n * Resolve birth date and gender from the 12-digit\n * normalised form (YYYY DDD ...).\n *\n * Day-of-year encoding:\n * Male: 1–366\n * Female: 501–866 (subtract 500)\n */\nconst resolveBirthInfo = (\n v: string,\n): {\n year: number;\n month: number;\n day: number;\n gender: \"male\" | \"female\";\n} | null => {\n const year = Number(v.slice(0, 4));\n let days = Number(v.slice(4, 7));\n\n const gender: \"male\" | \"female\" =\n days > 500 ? \"female\" : \"male\";\n if (days > 500) days -= 500;\n\n if (days < 1 || days > 366) return null;\n\n // Convert day-of-year to month/day via Date\n const d = new Date(year, 0, days);\n const month = d.getMonth() + 1;\n const dayOfMonth = d.getDate();\n\n if (!isValidDate(year, month, dayOfMonth)) {\n return null;\n }\n\n // Guard against day 366 in non-leap years\n if (d.getFullYear() !== year) return null;\n\n return { year, month, day: dayOfMonth, gender };\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n const isOld = OLD_RE.test(v);\n const isNew = NEW_RE.test(v);\n\n if (!isOld && !isNew) {\n if (v.length !== 10 && v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"NIC must be 10 (old) or 12 (new) characters\",\n );\n }\n return err(\n \"INVALID_FORMAT\",\n \"NIC must be 9 digits + V/X (old) or\" +\n \" 12 digits (new)\",\n );\n }\n\n const normalized = isOld ? toNewFormat(v) : v;\n\n if (!isdigits(normalized)) {\n return err(\n \"INVALID_FORMAT\",\n \"NIC contains invalid characters\",\n );\n }\n\n if (resolveBirthInfo(normalized) === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"NIC contains an invalid date of birth\",\n );\n }\n\n const expected = checkDigit(normalized.slice(0, 11));\n if (expected !== Number(normalized[11])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"NIC check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n // Old format: YYDDDSSSXV → no standard separator\n // New format: YYYYDDDSSSSC → no standard separator\n return v;\n};\n\n/**\n * Extract birth date and gender from an NIC.\n * Returns null if the value is not valid.\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const isOld = OLD_RE.test(v);\n const normalized = isOld ? toNewFormat(v) : v;\n\n const info = resolveBirthInfo(normalized);\n if (info === null) return null;\n\n return {\n birthDate: new Date(\n info.year,\n info.month - 1,\n info.day,\n ),\n gender: info.gender,\n };\n};\n\n/** Sri Lankan National Identity Card. */\nconst nic: Validator = {\n name: \"National Identity Card\",\n localName: \"ජාතික හැඳුනුම්පත\",\n abbreviation: \"NIC\",\n aliases: [\"NIC\", \"ජාතික හැඳුනුම්පත\"] as const,\n candidatePattern: \"\\\\d{9}[VXvx]|\\\\d{12}\",\n country: \"LK\",\n entityType: \"person\",\n description:\n \"Sri Lankan personal identity number issued to\" +\n \" all citizens\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/\" +\n \"National_identification_number#Sri_Lanka\",\n lengths: [10, 12] as const,\n examples: [\"197819202757\", \"862348753V\"] as const,\n compact,\n format,\n validate,\n};\n\nexport default nic;\nexport { compact, format, parse, validate };\n"],"mappings":";;;;;;;;;;;;;;;;;;;AA0BA,MAAM,SAAS;;AAGf,MAAM,SAAS;AAEf,MAAM,WAAW,UACf,MAAM,OAAO,KAAK,CAAC,aAAa;;;;;;;;AASlC,MAAM,eAAe,MAAsB;AAKzC,QAAO,KAJI,EAAE,MAAM,GAAG,EAAE,GACZ,EAAE,MAAM,GAAG,EAAE,CAGJ,GAFT,EAAE,MAAM,GAAG,EAAE,GACX,EAAE;;;;;;;AASlB,MAAM,gBAAgB;CACpB;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAC/B;AAED,MAAM,cAAc,WAA2B;CAC7C,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,IACtB,SACG,cAAc,MAAM,KAAK,OAAO,OAAO,OAAO,EAAE,CAAC;CAEtD,MAAM,IAAI,KAAM,MAAM;AACtB,QAAO,IAAI,IAAI,IAAI,KAAK;;;;;;;;;;AAW1B,MAAM,oBACJ,MAMU;CACV,MAAM,OAAO,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAClC,IAAI,OAAO,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAEhC,MAAM,SACJ,OAAO,MAAM,WAAW;AAC1B,KAAI,OAAO,IAAK,SAAQ;AAExB,KAAI,OAAO,KAAK,OAAO,IAAK,QAAO;CAGnC,MAAM,IAAI,IAAI,KAAK,MAAM,GAAG,KAAK;CACjC,MAAM,QAAQ,EAAE,UAAU,GAAG;CAC7B,MAAM,aAAa,EAAE,SAAS;AAE9B,KAAI,CAAC,YAAY,MAAM,OAAO,WAAW,CACvC,QAAO;AAIT,KAAI,EAAE,aAAa,KAAK,KAAM,QAAO;AAErC,QAAO;EAAE;EAAM;EAAO,KAAK;EAAY;EAAQ;;AAGjD,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;CACxB,MAAM,QAAQ,OAAO,KAAK,EAAE;CAC5B,MAAM,QAAQ,OAAO,KAAK,EAAE;AAE5B,KAAI,CAAC,SAAS,CAAC,OAAO;AACpB,MAAI,EAAE,WAAW,MAAM,EAAE,WAAW,GAClC,QAAO,IACL,kBACA,8CACD;AAEH,SAAO,IACL,kBACA,sDAED;;CAGH,MAAM,aAAa,QAAQ,YAAY,EAAE,GAAG;AAE5C,KAAI,CAAC,SAAS,WAAW,CACvB,QAAO,IACL,kBACA,kCACD;AAGH,KAAI,iBAAiB,WAAW,KAAK,KACnC,QAAO,IACL,qBACA,wCACD;AAIH,KADiB,WAAW,WAAW,MAAM,GAAG,GAAG,CAAC,KACnC,OAAO,WAAW,IAAI,CACrC,QAAO,IACL,oBACA,iCACD;AAGH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;AAIxC,QAHU,QAAQ,MAAM;;;;;;AAU1B,MAAM,SAAS,UAAyC;CACtD,MAAM,SAAS,SAAS,MAAM;AAC9B,KAAI,CAAC,OAAO,MAAO,QAAO;CAE1B,MAAM,IAAI,OAAO;CAIjB,MAAM,OAAO,iBAHC,OAAO,KAAK,EAAE,GACD,YAAY,EAAE,GAAG,EAEH;AACzC,KAAI,SAAS,KAAM,QAAO;AAE1B,QAAO;EACL,WAAW,IAAI,KACb,KAAK,MACL,KAAK,QAAQ,GACb,KAAK,IACN;EACD,QAAQ,KAAK;EACd;;;AAIH,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,OAAO,mBAAmB;CACpC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,aACE;CAEF,WACE;CAEF,SAAS,CAAC,IAAI,GAAG;CACjB,UAAU,CAAC,gBAAgB,aAAa;CACxC;CACA;CACA;CACD"}
1
+ {"version":3,"file":"nic.js","names":[],"sources":["../../src/lk/nic.ts"],"sourcesContent":["/**\n * NIC (National Identity Card, ජාතික හැඳුනුම්පත).\n *\n * Sri Lankan personal identity number. Two formats:\n * Old (pre-2016): 9 digits + V or X (10 chars)\n * New (2016+): 12 digits\n *\n * Both encode birth date (day-of-year), gender, a\n * serial number, and a check digit.\n *\n * @see https://en.wikipedia.org/wiki/National_identification_number#Sri_Lanka\n * @see https://drp.gov.lk/Templates/Artical%20-%20English%20new%20number.html\n */\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n Validator,\n} from \"../types\";\n\n/** Old format: 9 digits + V or X (case-insensitive). */\nconst OLD_RE = /^\\d{9}[VvXx]$/;\n\n/** New format: exactly 12 digits. */\nconst NEW_RE = /^\\d{12}$/;\n\nconst compact = (value: string): string =>\n clean(value, \" -\").toUpperCase();\n\n/**\n * Convert an old-format NIC to the 12-digit new\n * format for uniform validation.\n *\n * Old: YY DDD SSS C V/X (10 chars)\n * New: 19YY DDD 0SSS C (12 digits)\n */\nconst toNewFormat = (v: string): string => {\n const yy = v.slice(0, 2);\n const ddd = v.slice(2, 5);\n const sss = v.slice(5, 8);\n const check = v[8];\n return `19${yy}${ddd}0${sss}${check}`;\n};\n\n/**\n * Checksum weights for the first 11 digits.\n * Algorithm: weighted sum mod 11, then mod 10\n * if >= 10.\n */\nconst CHECK_WEIGHTS = [\n 8, 4, 3, 2, 7, 6, 5, 7, 4, 3, 2,\n] as const;\n\nconst checkDigit = (digits: string): number => {\n let sum = 0;\n for (let i = 0; i < 11; i++) {\n sum +=\n (CHECK_WEIGHTS[i] ?? 0) * Number(digits.charAt(i));\n }\n const d = 11 - (sum % 11);\n return d > 9 ? d % 10 : d;\n};\n\n/**\n * Resolve birth date and gender from the 12-digit\n * normalised form (YYYY DDD ...).\n *\n * Day-of-year encoding:\n * Male: 1–366\n * Female: 501–866 (subtract 500)\n */\nconst resolveBirthInfo = (\n v: string,\n): {\n year: number;\n month: number;\n day: number;\n gender: \"male\" | \"female\";\n} | null => {\n const year = Number(v.slice(0, 4));\n let days = Number(v.slice(4, 7));\n\n const gender: \"male\" | \"female\" =\n days > 500 ? \"female\" : \"male\";\n if (days > 500) days -= 500;\n\n if (days < 1 || days > 366) return null;\n\n // Convert day-of-year to month/day via Date\n const d = new Date(year, 0, days);\n const month = d.getMonth() + 1;\n const dayOfMonth = d.getDate();\n\n if (!isValidDate(year, month, dayOfMonth)) {\n return null;\n }\n\n // Guard against day 366 in non-leap years\n if (d.getFullYear() !== year) return null;\n\n return { year, month, day: dayOfMonth, gender };\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n const isOld = OLD_RE.test(v);\n const isNew = NEW_RE.test(v);\n\n if (!isOld && !isNew) {\n if (v.length !== 10 && v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"NIC must be 10 (old) or 12 (new) characters\",\n );\n }\n return err(\n \"INVALID_FORMAT\",\n \"NIC must be 9 digits + V/X (old) or\" +\n \" 12 digits (new)\",\n );\n }\n\n const normalized = isOld ? toNewFormat(v) : v;\n\n if (!isdigits(normalized)) {\n return err(\n \"INVALID_FORMAT\",\n \"NIC contains invalid characters\",\n );\n }\n\n if (resolveBirthInfo(normalized) === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"NIC contains an invalid date of birth\",\n );\n }\n\n const expected = checkDigit(normalized.slice(0, 11));\n if (expected !== Number(normalized[11])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"NIC check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n // Old format: YYDDDSSSXV → no standard separator\n // New format: YYYYDDDSSSSC → no standard separator\n return v;\n};\n\n/**\n * Extract birth date and gender from an NIC.\n * Returns null if the value is not valid.\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const isOld = OLD_RE.test(v);\n const normalized = isOld ? toNewFormat(v) : v;\n\n const info = resolveBirthInfo(normalized);\n if (info === null) return null;\n\n return {\n birthDate: new Date(\n info.year,\n info.month - 1,\n info.day,\n ),\n gender: info.gender,\n };\n};\n\n/** Generate a random valid Sri Lankan NIC. */\nconst generate = (): string => {\n const year = randomInt(1950, 2024);\n const start = new Date(year, 0, 1);\n const end = new Date(year, 11, 31);\n const maxDay =\n Math.round(\n (end.getTime() - start.getTime()) / 86_400_000,\n ) + 1;\n const dayOfYear = randomInt(1, maxDay);\n const genderOffset = Math.random() < 0.5 ? 0 : 500;\n const ddd = String(dayOfYear + genderOffset).padStart(\n 3,\n \"0\",\n );\n const body = `${year}${ddd}0${randomDigits(3)}`;\n return `${body}${String(checkDigit(body))}`;\n};\n\n/** Sri Lankan National Identity Card. */\nconst nic: Validator<ParsedPersonId> = {\n name: \"National Identity Card\",\n localName: \"ජාතික හැඳුනුම්පත\",\n abbreviation: \"NIC\",\n aliases: [\"NIC\", \"ජාතික හැඳුනුම්පත\"] as const,\n candidatePattern: \"\\\\d{9}[VXvx]|\\\\d{12}\",\n country: \"LK\",\n entityType: \"person\",\n description:\n \"Sri Lankan personal identity number issued to\" +\n \" all citizens\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/\" +\n \"National_identification_number#Sri_Lanka\",\n lengths: [10, 12] as const,\n examples: [\"197819202757\", \"862348753V\"] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default nic;\nexport { compact, format, generate, parse, validate };\n"],"mappings":"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"}
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/lt/asmens.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
package/dist/lt/mod.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import asmens from "./asmens.js";
3
2
  import vat from "./vat.js";
4
3
 
5
4
  //#region src/lt/mod.d.ts
6
-
5
+ declare namespace mod_d_exports {
6
+ export { asmens, vat };
7
+ }
7
8
  //#endregion
8
9
  export { asmens, mod_d_exports, vat };
9
10
  //# sourceMappingURL=mod.d.ts.map
package/dist/lt/vat.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/lt/vat.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
package/dist/lu/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import vat from "./vat.js";
3
2
 
4
3
  //#region src/lu/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { vat };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, vat };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/lu/vat.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/lu/vat.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
package/dist/luhn.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "./types.js";
2
2
 
3
3
  //#region src/luhn.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
package/dist/luhn.js CHANGED
@@ -38,7 +38,14 @@ const luhn = {
38
38
  name: "Luhn",
39
39
  localName: "Luhn",
40
40
  abbreviation: "Luhn",
41
+ aliases: [
42
+ "Luhn",
43
+ "Luhn algorithm",
44
+ "mod 10"
45
+ ],
46
+ candidatePattern: "\\d{2,}",
41
47
  entityType: "any",
48
+ sourceUrl: "https://en.wikipedia.org/wiki/Luhn_algorithm",
42
49
  examples: ["4111111111111111", "18"],
43
50
  compact,
44
51
  format,
package/dist/luhn.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"luhn.js","names":[],"sources":["../src/luhn.ts"],"sourcesContent":["/**\n * Generic Luhn checksum validation.\n *\n * Validates any digit string using the Luhn\n * algorithm (ISO/IEC 7812-1). No length\n * restriction. For credit card validation\n * (13-19 digits), use `creditcard` instead.\n */\n\nimport {\n luhnChecksum,\n luhnValidate,\n} from \"#checksums/luhn\";\nimport { clean } from \"#util/clean\";\nimport { randomDigits } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"./types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -.\");\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length < 1) {\n return err(\"INVALID_LENGTH\", \"Value must not be empty\");\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Value must contain only digits\",\n );\n }\n if (!luhnValidate(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Luhn check digit does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Generate a random Luhn-valid number of the\n * given length (default 16).\n */\nconst generate = (length = 16): string => {\n const payload = randomDigits(length - 1);\n const partial = `${payload}0`;\n const remainder = luhnChecksum(partial);\n const check = (10 - remainder) % 10;\n return `${payload}${String(check)}`;\n};\n\n/** Generic Luhn Validator. */\nconst luhn: Validator = {\n name: \"Luhn\",\n localName: \"Luhn\",\n abbreviation: \"Luhn\",\n entityType: \"any\",\n examples: [\"4111111111111111\", \"18\"] as const,\n compact,\n format,\n validate,\n generate: () => generate(),\n};\n\nexport default luhn;\nexport { compact, format, generate, validate };\n"],"mappings":";;;;;;;;;;;;;;AAoBA,MAAM,WAAW,UACf,MAAM,OAAO,MAAM;AAErB,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,SAAS,EACb,QAAO,IAAI,kBAAkB,0BAA0B;AAEzD,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,iCACD;AAEH,KAAI,CAAC,aAAa,EAAE,CAClB,QAAO,IACL,oBACA,kCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B,QAAQ,MAAM;;;;;AAMxD,MAAM,YAAY,SAAS,OAAe;CACxC,MAAM,UAAU,aAAa,SAAS,EAAE;CAGxC,MAAM,SAAS,KADG,aADF,GAAG,QAAQ,GACY,IACN;AACjC,QAAO,GAAG,UAAU,OAAO,MAAM;;;AAInC,MAAM,OAAkB;CACtB,MAAM;CACN,WAAW;CACX,cAAc;CACd,YAAY;CACZ,UAAU,CAAC,oBAAoB,KAAK;CACpC;CACA;CACA;CACA,gBAAgB,UAAU;CAC3B"}
1
+ {"version":3,"file":"luhn.js","names":[],"sources":["../src/luhn.ts"],"sourcesContent":["/**\n * Generic Luhn checksum validation.\n *\n * Validates any digit string using the Luhn\n * algorithm (ISO/IEC 7812-1). No length\n * restriction. For credit card validation\n * (13-19 digits), use `creditcard` instead.\n */\n\nimport {\n luhnChecksum,\n luhnValidate,\n} from \"#checksums/luhn\";\nimport { clean } from \"#util/clean\";\nimport { randomDigits } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"./types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -.\");\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length < 1) {\n return err(\"INVALID_LENGTH\", \"Value must not be empty\");\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Value must contain only digits\",\n );\n }\n if (!luhnValidate(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Luhn check digit does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Generate a random Luhn-valid number of the\n * given length (default 16).\n */\nconst generate = (length = 16): string => {\n const payload = randomDigits(length - 1);\n const partial = `${payload}0`;\n const remainder = luhnChecksum(partial);\n const check = (10 - remainder) % 10;\n return `${payload}${String(check)}`;\n};\n\n/** Generic Luhn Validator. */\nconst luhn: Validator = {\n name: \"Luhn\",\n localName: \"Luhn\",\n abbreviation: \"Luhn\",\n aliases: [\"Luhn\", \"Luhn algorithm\", \"mod 10\"] as const,\n candidatePattern: \"\\\\d{2,}\",\n entityType: \"any\",\n sourceUrl: \"https://en.wikipedia.org/wiki/Luhn_algorithm\",\n examples: [\"4111111111111111\", \"18\"] as const,\n compact,\n format,\n validate,\n generate: () => generate(),\n};\n\nexport default luhn;\nexport { compact, format, generate, validate };\n"],"mappings":";;;;;;;;;;;;;;AAoBA,MAAM,WAAW,UACf,MAAM,OAAO,MAAM;AAErB,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,SAAS,EACb,QAAO,IAAI,kBAAkB,0BAA0B;AAEzD,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,iCACD;AAEH,KAAI,CAAC,aAAa,EAAE,CAClB,QAAO,IACL,oBACA,kCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B,QAAQ,MAAM;;;;;AAMxD,MAAM,YAAY,SAAS,OAAe;CACxC,MAAM,UAAU,aAAa,SAAS,EAAE;CAGxC,MAAM,SAAS,KADG,aADF,GAAG,QAAQ,GACY,IACN;AACjC,QAAO,GAAG,UAAU,OAAO,MAAM;;;AAInC,MAAM,OAAkB;CACtB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EAAC;EAAQ;EAAkB;EAAS;CAC7C,kBAAkB;CAClB,YAAY;CACZ,WAAW;CACX,UAAU,CAAC,oBAAoB,KAAK;CACpC;CACA;CACA;CACA,gBAAgB,UAAU;CAC3B"}
package/dist/lv/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import vat from "./vat.js";
3
2
 
4
3
  //#region src/lv/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { vat };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, vat };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/lv/vat.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/lv/vat.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
package/dist/lv/vat.js CHANGED
@@ -7,12 +7,29 @@ import { weightedSum } from "../_checksums/weighted-sum.js";
7
7
  /**
8
8
  * PVN (Latvian VAT number).
9
9
  *
10
- * 11 digits. Legal entities (first digit > 3):
11
- * weights [9,1,4,8,3,10,2,5,7,6,1], sum % 11 === 3.
12
- * Personal (first digit <= 3): 11-digit format only.
10
+ * 11 digits.
13
11
  *
14
- * @see https://www.vatify.eu/latvia-vat-number.html
15
- * @see https://www.vid.gov.lv/en
12
+ * Individuals:
13
+ * - legacy personal codes: DDMMYY + century digit 0/1/2 + 4 digits
14
+ * - since 1 July 2017: codes start with 3, the second digit is 2-9,
15
+ * and the remaining digits are random
16
+ *
17
+ * Entities:
18
+ * - tax administration issued: 9XXXXXXXXXX
19
+ * - enterprise register issued: 4XXXXXXXXXX / 5XXXXXXXXXX
20
+ *
21
+ * Canonical sources:
22
+ * - OECD Latvia TIN sheet for the tax-facing formats
23
+ * - PMLP page documenting the post-2017 personal code
24
+ *
25
+ * These are the reasons we accept the new personal
26
+ * code shape (`3` + second digit `2-9`) without the
27
+ * legacy birth-date checksum, and why we reject
28
+ * legacy personal codes with century digits outside
29
+ * 0/1/2.
30
+ *
31
+ * @see https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Latvia-TIN.pdf
32
+ * @see https://www.pmlp.gov.lv/en/change-personal-identity-number
16
33
  */
17
34
  const WEIGHTS = [
18
35
  9,
@@ -53,7 +70,9 @@ const validateBirthDate = (v) => {
53
70
  const day = Number(v.slice(0, 2));
54
71
  const month = Number(v.slice(2, 4));
55
72
  const year2 = Number(v.slice(4, 6));
56
- const fullYear = 1800 + Number(v[6]) * 100 + year2;
73
+ const century = Number(v[6]);
74
+ if (century < 0 || century > 2) return false;
75
+ const fullYear = 1800 + century * 100 + year2;
57
76
  const date = new Date(fullYear, month - 1, day);
58
77
  return date.getFullYear() === fullYear && date.getMonth() === month - 1 && date.getDate() === day;
59
78
  };
@@ -61,10 +80,13 @@ const validate = (value) => {
61
80
  const v = compact(value);
62
81
  if (v.length !== 11) return err("INVALID_LENGTH", "Latvian VAT number must be 11 digits");
63
82
  if (!isdigits(v)) return err("INVALID_FORMAT", "Latvian VAT number must contain only digits");
64
- if (Number(v[0]) > 3) {
83
+ if (v[0] === "3" && v[1] !== void 0 && v[1] >= "2" && v[1] <= "9") return {
84
+ valid: true,
85
+ compact: v
86
+ };
87
+ const first = v[0];
88
+ if (first === "4" || first === "5" || first === "9") {
65
89
  if (weightedSum(v, WEIGHTS, 11) !== 3) return err("INVALID_CHECKSUM", "Latvian VAT number check digit mismatch");
66
- } else if (v.startsWith("32")) {
67
- if (!checkPersonal(v)) return err("INVALID_CHECKSUM", "Latvian personal code check digit mismatch");
68
90
  } else {
69
91
  if (!validateBirthDate(v)) return err("INVALID_COMPONENT", "Latvian personal code has invalid birth date");
70
92
  if (!checkPersonal(v)) return err("INVALID_CHECKSUM", "Latvian personal code check digit mismatch");
@@ -77,8 +99,13 @@ const validate = (value) => {
77
99
  const format = (value) => `LV${compact(value)}`;
78
100
  /** Generate a random valid Latvian VAT number. */
79
101
  const generate = () => {
102
+ const entityPrefixes = [
103
+ "4",
104
+ "5",
105
+ "9"
106
+ ];
80
107
  for (;;) {
81
- const c = String(randomInt(4, 9)) + randomDigits(10);
108
+ const c = randomInt(0, 1) === 0 ? `3${randomInt(2, 9)}${randomDigits(9)}` : `${entityPrefixes[randomInt(0, entityPrefixes.length - 1)] ?? "4"}${randomDigits(10)}`;
82
109
  if (validate(c).valid) return c;
83
110
  }
84
111
  };
@@ -91,8 +118,8 @@ const vat = {
91
118
  candidatePattern: "LV\\d{11}",
92
119
  country: "LV",
93
120
  entityType: "any",
94
- sourceUrl: "https://www.vid.gov.lv/en",
95
- examples: ["40003521600"],
121
+ sourceUrl: "https://www.pmlp.gov.lv/en/change-personal-identity-number",
122
+ examples: ["40003521600", "32999999999"],
96
123
  compact,
97
124
  format,
98
125
  validate,