@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
@@ -1 +1 @@
1
- {"version":3,"file":"personnummer.js","names":[],"sources":["../../src/se/personnummer.ts"],"sourcesContent":["/**\n * Personnummer (Personal Identity Number).\n *\n * Swedish personal identification number. 10 digits\n * in compact form (YYMMDDNNNC) with a Luhn checksum\n * on all 10 digits.\n *\n * @see https://www.skatteverket.se/privat/folkbokforing/personnummer.4.3810a01c150939e893f18c29.html\n */\n\nimport {\n luhnValidate,\n luhnChecksum,\n} from \"#checksums/luhn\";\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { 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/**\n * Match python-stdnum's compact: preserve the '-' or '+'\n * separator at position -5, and strip any others.\n */\nconst compact = (value: string): string => {\n let v = clean(value, \" :\");\n if (\n (v.length === 10 || v.length === 12) &&\n v[v.length - 5] !== \"-\" &&\n v[v.length - 5] !== \"+\"\n ) {\n v = `${v.slice(0, -4)}-${v.slice(-4)}`;\n }\n const prefix = v\n .slice(0, -5)\n .replace(/-/g, \"\")\n .replace(/\\+/g, \"\");\n return `${prefix}${v.slice(-5)}`;\n};\n\n/**\n * Extract the birth date from the compacted number.\n * Matches python-stdnum's get_birth_date.\n */\nconst getBirthDate = (\n v: string,\n): { year: number; month: number; day: number } | null => {\n let year: number;\n let month: number;\n let day: number;\n\n if (v.length === 13) {\n year = Number(v.slice(0, 4));\n month = Number(v.slice(4, 6));\n day = Number(v.slice(6, 8));\n } else {\n const currentYear = new Date().getFullYear();\n let century = Math.floor(currentYear / 100);\n const yy = Number(v.slice(0, 2));\n if (yy > currentYear % 100) century -= 1;\n if (v[v.length - 5] === \"+\") century -= 1;\n year = century * 100 + yy;\n month = Number(v.slice(2, 4));\n day = Number(v.slice(4, 6));\n }\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 !== 11 && v.length !== 13) {\n return err(\n \"INVALID_LENGTH\",\n \"Personnummer must be 10 or 12 digits\",\n );\n }\n\n const digits = v.slice(0, -5) + v.slice(-4);\n const sep = v[v.length - 5];\n if ((sep !== \"-\" && sep !== \"+\") || !isdigits(digits)) {\n return err(\n \"INVALID_FORMAT\",\n \"Personnummer has invalid format\",\n );\n }\n\n if (getBirthDate(v) === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"Personnummer contains an invalid date\",\n );\n }\n\n if (!luhnValidate(digits.slice(-10))) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Personnummer Luhn check does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Extract birth date and gender from a Personnummer.\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 birthDate = getBirthDate(v);\n if (birthDate === null) return null;\n\n const genderDigit = Number(v[v.length - 2]);\n\n return {\n birthDate: new Date(\n birthDate.year,\n birthDate.month - 1,\n birthDate.day,\n ),\n gender: genderDigit % 2 === 0 ? \"female\" : \"male\",\n };\n};\n\n/** Generate a random valid Swedish personnummer. */\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 serial = String(randomInt(0, 999)).padStart(\n 3,\n \"0\",\n );\n const payload = yy + mm + dd + serial;\n const cs = luhnChecksum(payload + \"0\");\n const c = payload + String((10 - cs) % 10);\n const result = validate(c);\n if (result.valid) return result.compact;\n }\n};\n\n/** Swedish Personal Identity Number. */\nconst personnummer: Validator = {\n name: \"Swedish Personal ID\",\n localName: \"Personnummer\",\n abbreviation: \"PN\",\n aliases: [\n \"personnummer\",\n \"personnr\",\n \"pers.nr\",\n \"personal identity number\",\n ] as const,\n candidatePattern: \"\\\\d{6,8}-\\\\d{4}\",\n country: \"SE\",\n entityType: \"person\",\n sourceUrl:\n \"https://www.skatteverket.se/privat/folkbokforing/personnummer.4.3810a01c150939e893f18c29.html\",\n examples: [\"880320-0016\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default personnummer;\nexport { compact, format, parse, validate, generate };\n"],"mappings":"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"}
1
+ {"version":3,"file":"personnummer.js","names":[],"sources":["../../src/se/personnummer.ts"],"sourcesContent":["/**\n * Personnummer (Personal Identity Number).\n *\n * Swedish personal identification number. 10 digits\n * in compact form (YYMMDDNNNC) with a Luhn checksum\n * on all 10 digits.\n *\n * @see https://www.skatteverket.se/privat/folkbokforing/personnummer.4.3810a01c150939e893f18c29.html\n */\n\nimport {\n luhnValidate,\n luhnChecksum,\n} from \"#checksums/luhn\";\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { 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/**\n * Match python-stdnum's compact: preserve the '-' or '+'\n * separator at position -5, and strip any others.\n */\nconst compact = (value: string): string => {\n let v = clean(value, \" :\");\n if (\n (v.length === 10 || v.length === 12) &&\n v[v.length - 5] !== \"-\" &&\n v[v.length - 5] !== \"+\"\n ) {\n v = `${v.slice(0, -4)}-${v.slice(-4)}`;\n }\n const prefix = v\n .slice(0, -5)\n .replace(/-/g, \"\")\n .replace(/\\+/g, \"\");\n return `${prefix}${v.slice(-5)}`;\n};\n\n/**\n * Extract the birth date from the compacted number.\n * Matches python-stdnum's get_birth_date.\n */\nconst getBirthDate = (\n v: string,\n): { year: number; month: number; day: number } | null => {\n let year: number;\n let month: number;\n let day: number;\n\n if (v.length === 13) {\n year = Number(v.slice(0, 4));\n month = Number(v.slice(4, 6));\n day = Number(v.slice(6, 8));\n } else {\n const currentYear = new Date().getFullYear();\n let century = Math.floor(currentYear / 100);\n const yy = Number(v.slice(0, 2));\n if (yy > currentYear % 100) century -= 1;\n if (v[v.length - 5] === \"+\") century -= 1;\n year = century * 100 + yy;\n month = Number(v.slice(2, 4));\n day = Number(v.slice(4, 6));\n }\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 !== 11 && v.length !== 13) {\n return err(\n \"INVALID_LENGTH\",\n \"Personnummer must be 10 or 12 digits\",\n );\n }\n\n const digits = v.slice(0, -5) + v.slice(-4);\n const sep = v[v.length - 5];\n if ((sep !== \"-\" && sep !== \"+\") || !isdigits(digits)) {\n return err(\n \"INVALID_FORMAT\",\n \"Personnummer has invalid format\",\n );\n }\n\n if (getBirthDate(v) === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"Personnummer contains an invalid date\",\n );\n }\n\n if (!luhnValidate(digits.slice(-10))) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Personnummer Luhn check does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Extract birth date and gender from a Personnummer.\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 birthDate = getBirthDate(v);\n if (birthDate === null) return null;\n\n const genderDigit = Number(v[v.length - 2]);\n\n return {\n birthDate: new Date(\n birthDate.year,\n birthDate.month - 1,\n birthDate.day,\n ),\n gender: genderDigit % 2 === 0 ? \"female\" : \"male\",\n };\n};\n\n/** Generate a random valid Swedish personnummer. */\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 serial = String(randomInt(0, 999)).padStart(\n 3,\n \"0\",\n );\n const payload = yy + mm + dd + serial;\n const cs = luhnChecksum(payload + \"0\");\n const c = payload + String((10 - cs) % 10);\n const result = validate(c);\n if (result.valid) return result.compact;\n }\n};\n\n/** Swedish Personal Identity Number. */\nconst personnummer: Validator<ParsedPersonId> = {\n name: \"Swedish Personal ID\",\n localName: \"Personnummer\",\n abbreviation: \"PN\",\n aliases: [\n \"personnummer\",\n \"personnr\",\n \"pers.nr\",\n \"personal identity number\",\n ] as const,\n candidatePattern: \"\\\\d{6,8}-\\\\d{4}\",\n country: \"SE\",\n entityType: \"person\",\n sourceUrl:\n \"https://www.skatteverket.se/privat/folkbokforing/personnummer.4.3810a01c150939e893f18c29.html\",\n examples: [\"880320-0016\"] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default personnummer;\nexport { compact, format, parse, validate, generate };\n"],"mappings":"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"}
package/dist/se/vat.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/se/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/sg/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import uen from "./uen.js";
3
2
 
4
3
  //#region src/sg/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { uen };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, uen };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/sg/uen.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/sg/uen.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/sg/uen.js CHANGED
@@ -65,23 +65,23 @@ const compact = (value) => clean(value, " -").toUpperCase();
65
65
  * Validate Business (ROB) UEN (9 chars):
66
66
  * 8 digits + 1 check letter.
67
67
  */
68
+ const BUSINESS_WEIGHTS = [
69
+ 10,
70
+ 4,
71
+ 9,
72
+ 3,
73
+ 8,
74
+ 2,
75
+ 7,
76
+ 1
77
+ ];
78
+ const BUSINESS_CHECK_ALPHA = "XMKECAWLJDB";
68
79
  const validateBusiness = (v) => {
69
80
  if (!isdigits(v.slice(0, 8))) return err("INVALID_FORMAT", "Business UEN must start with 8 digits");
70
81
  if (!/^[A-Z]$/.test(v[8])) return err("INVALID_FORMAT", "Business UEN must end with a letter");
71
- const weights = [
72
- 10,
73
- 4,
74
- 9,
75
- 3,
76
- 8,
77
- 2,
78
- 7,
79
- 1
80
- ];
81
- const checkAlpha = "XMKECAWLJDB";
82
82
  let sum = 0;
83
- for (let i = 0; i < 8; i++) sum += Number(v[i]) * weights[i];
84
- const expected = checkAlpha[sum % 11];
83
+ for (const [i, weight] of BUSINESS_WEIGHTS.entries()) sum += Number(v[i]) * weight;
84
+ const expected = BUSINESS_CHECK_ALPHA[sum % 11];
85
85
  if (v[8] !== expected) return err("INVALID_CHECKSUM", "UEN check letter does not match");
86
86
  return {
87
87
  valid: true,
@@ -92,24 +92,24 @@ const validateBusiness = (v) => {
92
92
  * Validate Local Company (ROC) UEN (10 chars):
93
93
  * 9 digits (year prefix) + 1 check letter.
94
94
  */
95
+ const LOCAL_WEIGHTS = [
96
+ 10,
97
+ 8,
98
+ 6,
99
+ 4,
100
+ 9,
101
+ 7,
102
+ 5,
103
+ 3,
104
+ 1
105
+ ];
106
+ const LOCAL_CHECK_ALPHA = "ZKCMDNERGWH";
95
107
  const validateLocalCompany = (v) => {
96
108
  if (!isdigits(v.slice(0, 9))) return err("INVALID_FORMAT", "Company UEN must have 9 digits");
97
109
  if (!/^[A-Z]$/.test(v[9])) return err("INVALID_FORMAT", "Company UEN must end with a letter");
98
- const weights = [
99
- 10,
100
- 8,
101
- 6,
102
- 4,
103
- 9,
104
- 7,
105
- 5,
106
- 3,
107
- 1
108
- ];
109
- const checkAlpha = "ZKCMDNERGWH";
110
110
  let sum = 0;
111
- for (let i = 0; i < 9; i++) sum += Number(v[i]) * weights[i];
112
- const expected = checkAlpha[sum % 11];
111
+ for (const [i, weight] of LOCAL_WEIGHTS.entries()) sum += Number(v[i]) * weight;
112
+ const expected = LOCAL_CHECK_ALPHA[sum % 11];
113
113
  if (v[9] !== expected) return err("INVALID_CHECKSUM", "UEN check letter does not match");
114
114
  return {
115
115
  valid: true,
@@ -121,6 +121,17 @@ const validateLocalCompany = (v) => {
121
121
  * R/S/T + 2 digits + 2-letter type + 4 digits
122
122
  * + 1 check letter.
123
123
  */
124
+ const OTHER_WEIGHTS = [
125
+ 4,
126
+ 3,
127
+ 5,
128
+ 3,
129
+ 10,
130
+ 2,
131
+ 2,
132
+ 5,
133
+ 7
134
+ ];
124
135
  const validateOther = (v) => {
125
136
  const prefix = v[0];
126
137
  if (prefix !== "R" && prefix !== "S" && prefix !== "T") return err("INVALID_COMPONENT", "Other UEN must start with R, S, or T");
@@ -128,22 +139,13 @@ const validateOther = (v) => {
128
139
  const entityType = v.slice(3, 5);
129
140
  if (!ENTITY_TYPES.has(entityType)) return err("INVALID_COMPONENT", "Unknown entity type: " + entityType);
130
141
  if (!isdigits(v.slice(5, 9))) return err("INVALID_FORMAT", "Other UEN positions 6-9 must be digits");
131
- const weights = [
132
- 4,
133
- 3,
134
- 5,
135
- 3,
136
- 10,
137
- 2,
138
- 2,
139
- 5,
140
- 7
141
- ];
142
142
  let sum = 0;
143
- for (let i = 0; i < 9; i++) {
144
- const idx = OTHER_ALPHA.indexOf(v[i]);
145
- if (idx === -1) return err("INVALID_FORMAT", `Unexpected character at position ${i + 1}: ${v[i]}`);
146
- sum += idx * weights[i];
143
+ for (const [i, weight] of OTHER_WEIGHTS.entries()) {
144
+ const ch = v[i];
145
+ if (ch === void 0) continue;
146
+ const idx = OTHER_ALPHA.indexOf(ch);
147
+ if (idx === -1) return err("INVALID_FORMAT", `Unexpected character at position ${i + 1}: ${ch}`);
148
+ sum += idx * weight;
147
149
  }
148
150
  const expected = OTHER_ALPHA[((sum - 5) % 11 + 11) % 11];
149
151
  if (v[9] !== expected) return err("INVALID_CHECKSUM", "UEN check letter does not match");
@@ -163,21 +165,10 @@ const validate = (value) => {
163
165
  const format = (value) => compact(value);
164
166
  /** Generate a random valid Singapore UEN (business format). */
165
167
  const generate = () => {
166
- const weights = [
167
- 10,
168
- 4,
169
- 9,
170
- 3,
171
- 8,
172
- 2,
173
- 7,
174
- 1
175
- ];
176
- const checkAlpha = "XMKECAWLJDB";
177
168
  const body = randomDigits(8);
178
169
  let sum = 0;
179
- for (let i = 0; i < 8; i++) sum += Number(body[i]) * weights[i];
180
- return body + checkAlpha[sum % 11];
170
+ for (const [i, weight] of BUSINESS_WEIGHTS.entries()) sum += Number(body[i]) * weight;
171
+ return body + BUSINESS_CHECK_ALPHA[sum % 11];
181
172
  };
182
173
  /** Singapore Unique Entity Number. */
183
174
  const uen = {
@@ -1 +1 @@
1
- {"version":3,"file":"uen.js","names":[],"sources":["../../src/sg/uen.ts"],"sourcesContent":["/**\n * UEN (Singapore Unique Entity Number).\n *\n * 9 or 10 characters identifying businesses and\n * entities registered in Singapore. Three formats:\n *\n * - Business (ROB): 8 digits + 1 check letter\n * - Local company (ROC): 4-digit year + 5 digits\n * + 1 check letter\n * - Other entities: R/S/T + 2-digit year + 2-letter\n * entity type + 4 digits + 1 check letter\n *\n * @see https://www.uen.gov.sg/\n * @see https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Singapore-TIN.pdf\n */\n\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\n/** Check letter alphabet for \"Other\" format. */\nconst OTHER_ALPHA = \"ABCDEFGHJKLMNPQRSTUVWX0123456789\";\n\nconst ENTITY_TYPES = new Set([\n \"CC\",\n \"CD\",\n \"CH\",\n \"CL\",\n \"CM\",\n \"CP\",\n \"CS\",\n \"CX\",\n \"DP\",\n \"FB\",\n \"FC\",\n \"FM\",\n \"FN\",\n \"GA\",\n \"GB\",\n \"GS\",\n \"HS\",\n \"LL\",\n \"LP\",\n \"MB\",\n \"MC\",\n \"MD\",\n \"MH\",\n \"MM\",\n \"MQ\",\n \"NB\",\n \"NR\",\n \"PA\",\n \"PB\",\n \"PF\",\n \"RF\",\n \"RP\",\n \"SM\",\n \"SS\",\n \"TC\",\n \"TU\",\n \"VH\",\n \"XL\",\n]);\n\nconst compact = (value: string): string =>\n clean(value, \" -\").toUpperCase();\n\n/**\n * Validate Business (ROB) UEN (9 chars):\n * 8 digits + 1 check letter.\n */\nconst validateBusiness = (v: string): ValidateResult => {\n if (!isdigits(v.slice(0, 8))) {\n return err(\n \"INVALID_FORMAT\",\n \"Business UEN must start with 8 digits\",\n );\n }\n if (!/^[A-Z]$/.test(v[8]!)) {\n return err(\n \"INVALID_FORMAT\",\n \"Business UEN must end with a letter\",\n );\n }\n\n const weights = [10, 4, 9, 3, 8, 2, 7, 1];\n const checkAlpha = \"XMKECAWLJDB\";\n let sum = 0;\n for (let i = 0; i < 8; i++) {\n sum += Number(v[i]) * weights[i]!;\n }\n const expected = checkAlpha[sum % 11];\n if (v[8] !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"UEN check letter does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\n/**\n * Validate Local Company (ROC) UEN (10 chars):\n * 9 digits (year prefix) + 1 check letter.\n */\nconst validateLocalCompany = (\n v: string,\n): ValidateResult => {\n if (!isdigits(v.slice(0, 9))) {\n return err(\n \"INVALID_FORMAT\",\n \"Company UEN must have 9 digits\",\n );\n }\n if (!/^[A-Z]$/.test(v[9]!)) {\n return err(\n \"INVALID_FORMAT\",\n \"Company UEN must end with a letter\",\n );\n }\n\n const weights = [10, 8, 6, 4, 9, 7, 5, 3, 1];\n const checkAlpha = \"ZKCMDNERGWH\";\n let sum = 0;\n for (let i = 0; i < 9; i++) {\n sum += Number(v[i]) * weights[i]!;\n }\n const expected = checkAlpha[sum % 11];\n if (v[9] !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"UEN check letter does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\n/**\n * Validate Other entity UEN (10 chars):\n * R/S/T + 2 digits + 2-letter type + 4 digits\n * + 1 check letter.\n */\nconst validateOther = (v: string): ValidateResult => {\n const prefix = v[0]!;\n if (prefix !== \"R\" && prefix !== \"S\" && prefix !== \"T\") {\n return err(\n \"INVALID_COMPONENT\",\n \"Other UEN must start with R, S, or T\",\n );\n }\n if (!isdigits(v.slice(1, 3))) {\n return err(\n \"INVALID_FORMAT\",\n \"Other UEN positions 2-3 must be digits\",\n );\n }\n const entityType = v.slice(3, 5);\n if (!ENTITY_TYPES.has(entityType)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Unknown entity type: \" + entityType,\n );\n }\n if (!isdigits(v.slice(5, 9))) {\n return err(\n \"INVALID_FORMAT\",\n \"Other UEN positions 6-9 must be digits\",\n );\n }\n\n const weights = [4, 3, 5, 3, 10, 2, 2, 5, 7];\n let sum = 0;\n for (let i = 0; i < 9; i++) {\n const idx = OTHER_ALPHA.indexOf(v[i]!);\n if (idx === -1) {\n return err(\n \"INVALID_FORMAT\",\n `Unexpected character at position ${i + 1}: ${v[i]}`,\n );\n }\n sum += idx * weights[i]!;\n }\n const expected =\n OTHER_ALPHA[(((sum - 5) % 11) + 11) % 11];\n if (v[9] !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"UEN check letter does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 9 && v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"UEN must be 9 or 10 characters\",\n );\n }\n\n if (!/^[A-Z0-9]+$/.test(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"UEN must contain only digits and letters\",\n );\n }\n\n // 9 chars: business (ROB)\n if (v.length === 9) {\n return validateBusiness(v);\n }\n\n // 10 chars: first char digit => local company,\n // otherwise \"other\" entity\n if (isdigits(v[0]!)) {\n return validateLocalCompany(v);\n }\n return validateOther(v);\n};\n\nconst format = (value: string): string => compact(value);\n\n/** Generate a random valid Singapore UEN (business format). */\nconst generate = (): string => {\n const weights = [10, 4, 9, 3, 8, 2, 7, 1];\n const checkAlpha = \"XMKECAWLJDB\";\n const body = randomDigits(8);\n let sum = 0;\n for (let i = 0; i < 8; i++) {\n sum += Number(body[i]) * weights[i]!;\n }\n const check = checkAlpha[sum % 11]!;\n return body + check;\n};\n\n/** Singapore Unique Entity Number. */\nconst uen: Validator = {\n name: \"Singapore Unique Entity Number\",\n localName: \"Unique Entity Number\",\n abbreviation: \"UEN\",\n aliases: [\"UEN\", \"Unique Entity Number\"] as const,\n candidatePattern: \"[\\\\dSTR]\\\\d{7}[A-Z]\",\n country: \"SG\",\n entityType: \"company\",\n description:\n \"9 or 10-character business identifier issued by ACRA\",\n sourceUrl: \"https://www.uen.gov.sg/\",\n lengths: [9, 10] as const,\n examples: [\n \"00192200M\",\n \"197401143C\",\n \"S16FC0121D\",\n ] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default uen;\nexport { compact, format, validate, generate };\n"],"mappings":"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"}
1
+ {"version":3,"file":"uen.js","names":[],"sources":["../../src/sg/uen.ts"],"sourcesContent":["/**\n * UEN (Singapore Unique Entity Number).\n *\n * 9 or 10 characters identifying businesses and\n * entities registered in Singapore. Three formats:\n *\n * - Business (ROB): 8 digits + 1 check letter\n * - Local company (ROC): 4-digit year + 5 digits\n * + 1 check letter\n * - Other entities: R/S/T + 2-digit year + 2-letter\n * entity type + 4 digits + 1 check letter\n *\n * @see https://www.uen.gov.sg/\n * @see https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Singapore-TIN.pdf\n */\n\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\n/** Check letter alphabet for \"Other\" format. */\nconst OTHER_ALPHA = \"ABCDEFGHJKLMNPQRSTUVWX0123456789\";\n\nconst ENTITY_TYPES = new Set([\n \"CC\",\n \"CD\",\n \"CH\",\n \"CL\",\n \"CM\",\n \"CP\",\n \"CS\",\n \"CX\",\n \"DP\",\n \"FB\",\n \"FC\",\n \"FM\",\n \"FN\",\n \"GA\",\n \"GB\",\n \"GS\",\n \"HS\",\n \"LL\",\n \"LP\",\n \"MB\",\n \"MC\",\n \"MD\",\n \"MH\",\n \"MM\",\n \"MQ\",\n \"NB\",\n \"NR\",\n \"PA\",\n \"PB\",\n \"PF\",\n \"RF\",\n \"RP\",\n \"SM\",\n \"SS\",\n \"TC\",\n \"TU\",\n \"VH\",\n \"XL\",\n]);\n\nconst compact = (value: string): string =>\n clean(value, \" -\").toUpperCase();\n\n/**\n * Validate Business (ROB) UEN (9 chars):\n * 8 digits + 1 check letter.\n */\nconst BUSINESS_WEIGHTS = [10, 4, 9, 3, 8, 2, 7, 1] as const;\nconst BUSINESS_CHECK_ALPHA = \"XMKECAWLJDB\";\n\nconst validateBusiness = (v: string): ValidateResult => {\n if (!isdigits(v.slice(0, 8))) {\n return err(\n \"INVALID_FORMAT\",\n \"Business UEN must start with 8 digits\",\n );\n }\n // SAFETY: caller routes to this only when\n // v.length === 9.\n // eslint-disable-next-line no-non-null-assertion\n if (!/^[A-Z]$/.test(v[8]!)) {\n return err(\n \"INVALID_FORMAT\",\n \"Business UEN must end with a letter\",\n );\n }\n\n let sum = 0;\n for (const [i, weight] of BUSINESS_WEIGHTS.entries()) {\n sum += Number(v[i]) * weight;\n }\n const expected = BUSINESS_CHECK_ALPHA[sum % 11];\n if (v[8] !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"UEN check letter does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\n/**\n * Validate Local Company (ROC) UEN (10 chars):\n * 9 digits (year prefix) + 1 check letter.\n */\nconst LOCAL_WEIGHTS = [10, 8, 6, 4, 9, 7, 5, 3, 1] as const;\nconst LOCAL_CHECK_ALPHA = \"ZKCMDNERGWH\";\n\nconst validateLocalCompany = (\n v: string,\n): ValidateResult => {\n if (!isdigits(v.slice(0, 9))) {\n return err(\n \"INVALID_FORMAT\",\n \"Company UEN must have 9 digits\",\n );\n }\n // SAFETY: caller routes to this only when\n // v.length === 10.\n // eslint-disable-next-line no-non-null-assertion\n if (!/^[A-Z]$/.test(v[9]!)) {\n return err(\n \"INVALID_FORMAT\",\n \"Company UEN must end with a letter\",\n );\n }\n\n let sum = 0;\n for (const [i, weight] of LOCAL_WEIGHTS.entries()) {\n sum += Number(v[i]) * weight;\n }\n const expected = LOCAL_CHECK_ALPHA[sum % 11];\n if (v[9] !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"UEN check letter does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\n/**\n * Validate Other entity UEN (10 chars):\n * R/S/T + 2 digits + 2-letter type + 4 digits\n * + 1 check letter.\n */\nconst OTHER_WEIGHTS = [4, 3, 5, 3, 10, 2, 2, 5, 7] as const;\n\nconst validateOther = (v: string): ValidateResult => {\n // SAFETY: caller routes to this only when\n // v.length === 10.\n // eslint-disable-next-line no-non-null-assertion\n const prefix = v[0]!;\n if (prefix !== \"R\" && prefix !== \"S\" && prefix !== \"T\") {\n return err(\n \"INVALID_COMPONENT\",\n \"Other UEN must start with R, S, or T\",\n );\n }\n if (!isdigits(v.slice(1, 3))) {\n return err(\n \"INVALID_FORMAT\",\n \"Other UEN positions 2-3 must be digits\",\n );\n }\n const entityType = v.slice(3, 5);\n if (!ENTITY_TYPES.has(entityType)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Unknown entity type: \" + entityType,\n );\n }\n if (!isdigits(v.slice(5, 9))) {\n return err(\n \"INVALID_FORMAT\",\n \"Other UEN positions 6-9 must be digits\",\n );\n }\n\n let sum = 0;\n for (const [i, weight] of OTHER_WEIGHTS.entries()) {\n const ch = v[i];\n if (ch === undefined) continue;\n const idx = OTHER_ALPHA.indexOf(ch);\n if (idx === -1) {\n return err(\n \"INVALID_FORMAT\",\n `Unexpected character at position ${i + 1}: ${ch}`,\n );\n }\n sum += idx * weight;\n }\n const expected =\n OTHER_ALPHA[(((sum - 5) % 11) + 11) % 11];\n if (v[9] !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"UEN check letter does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 9 && v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"UEN must be 9 or 10 characters\",\n );\n }\n\n if (!/^[A-Z0-9]+$/.test(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"UEN must contain only digits and letters\",\n );\n }\n\n // 9 chars: business (ROB)\n if (v.length === 9) {\n return validateBusiness(v);\n }\n\n // 10 chars: first char digit => local company,\n // otherwise \"other\" entity\n // SAFETY: v.length === 10 here.\n // eslint-disable-next-line no-non-null-assertion\n if (isdigits(v[0]!)) {\n return validateLocalCompany(v);\n }\n return validateOther(v);\n};\n\nconst format = (value: string): string => compact(value);\n\n/** Generate a random valid Singapore UEN (business format). */\nconst generate = (): string => {\n const body = randomDigits(8);\n let sum = 0;\n for (const [i, weight] of BUSINESS_WEIGHTS.entries()) {\n sum += Number(body[i]) * weight;\n }\n // SAFETY: sum % 11 is in 0..10 and BUSINESS_CHECK_ALPHA\n // has 11 characters.\n // eslint-disable-next-line no-non-null-assertion\n const check = BUSINESS_CHECK_ALPHA[sum % 11]!;\n return body + check;\n};\n\n/** Singapore Unique Entity Number. */\nconst uen: Validator = {\n name: \"Singapore Unique Entity Number\",\n localName: \"Unique Entity Number\",\n abbreviation: \"UEN\",\n aliases: [\"UEN\", \"Unique Entity Number\"] as const,\n candidatePattern: \"[\\\\dSTR]\\\\d{7}[A-Z]\",\n country: \"SG\",\n entityType: \"company\",\n description:\n \"9 or 10-character business identifier issued by ACRA\",\n sourceUrl: \"https://www.uen.gov.sg/\",\n lengths: [9, 10] as const,\n examples: [\n \"00192200M\",\n \"197401143C\",\n \"S16FC0121D\",\n ] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default uen;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAwBA,MAAM,cAAc;AAEpB,MAAM,eAAe,IAAI,IAAI;CAC3B;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAM,WAAW,UACf,MAAM,OAAO,KAAK,CAAC,aAAa;;;;;AAMlC,MAAM,mBAAmB;CAAC;CAAI;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;AAClD,MAAM,uBAAuB;AAE7B,MAAM,oBAAoB,MAA8B;AACtD,KAAI,CAAC,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC,CAC1B,QAAO,IACL,kBACA,wCACD;AAKH,KAAI,CAAC,UAAU,KAAK,EAAE,GAAI,CACxB,QAAO,IACL,kBACA,sCACD;CAGH,IAAI,MAAM;AACV,MAAK,MAAM,CAAC,GAAG,WAAW,iBAAiB,SAAS,CAClD,QAAO,OAAO,EAAE,GAAG,GAAG;CAExB,MAAM,WAAW,qBAAqB,MAAM;AAC5C,KAAI,EAAE,OAAO,SACX,QAAO,IACL,oBACA,kCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;;;;;AAOpC,MAAM,gBAAgB;CAAC;CAAI;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;AAClD,MAAM,oBAAoB;AAE1B,MAAM,wBACJ,MACmB;AACnB,KAAI,CAAC,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC,CAC1B,QAAO,IACL,kBACA,iCACD;AAKH,KAAI,CAAC,UAAU,KAAK,EAAE,GAAI,CACxB,QAAO,IACL,kBACA,qCACD;CAGH,IAAI,MAAM;AACV,MAAK,MAAM,CAAC,GAAG,WAAW,cAAc,SAAS,CAC/C,QAAO,OAAO,EAAE,GAAG,GAAG;CAExB,MAAM,WAAW,kBAAkB,MAAM;AACzC,KAAI,EAAE,OAAO,SACX,QAAO,IACL,oBACA,kCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;;;;;;AAQpC,MAAM,gBAAgB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAI;CAAG;CAAG;CAAG;CAAE;AAElD,MAAM,iBAAiB,MAA8B;CAInD,MAAM,SAAS,EAAE;AACjB,KAAI,WAAW,OAAO,WAAW,OAAO,WAAW,IACjD,QAAO,IACL,qBACA,uCACD;AAEH,KAAI,CAAC,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC,CAC1B,QAAO,IACL,kBACA,yCACD;CAEH,MAAM,aAAa,EAAE,MAAM,GAAG,EAAE;AAChC,KAAI,CAAC,aAAa,IAAI,WAAW,CAC/B,QAAO,IACL,qBACA,0BAA0B,WAC3B;AAEH,KAAI,CAAC,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC,CAC1B,QAAO,IACL,kBACA,yCACD;CAGH,IAAI,MAAM;AACV,MAAK,MAAM,CAAC,GAAG,WAAW,cAAc,SAAS,EAAE;EACjD,MAAM,KAAK,EAAE;AACb,MAAI,OAAO,KAAA,EAAW;EACtB,MAAM,MAAM,YAAY,QAAQ,GAAG;AACnC,MAAI,QAAQ,GACV,QAAO,IACL,kBACA,oCAAoC,IAAI,EAAE,IAAI,KAC/C;AAEH,SAAO,MAAM;;CAEf,MAAM,WACJ,cAAe,MAAM,KAAK,KAAM,MAAM;AACxC,KAAI,EAAE,OAAO,SACX,QAAO,IACL,oBACA,kCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AAExB,KAAI,EAAE,WAAW,KAAK,EAAE,WAAW,GACjC,QAAO,IACL,kBACA,iCACD;AAGH,KAAI,CAAC,cAAc,KAAK,EAAE,CACxB,QAAO,IACL,kBACA,2CACD;AAIH,KAAI,EAAE,WAAW,EACf,QAAO,iBAAiB,EAAE;AAO5B,KAAI,SAAS,EAAE,GAAI,CACjB,QAAO,qBAAqB,EAAE;AAEhC,QAAO,cAAc,EAAE;;AAGzB,MAAM,UAAU,UAA0B,QAAQ,MAAM;;AAGxD,MAAM,iBAAyB;CAC7B,MAAM,OAAO,aAAa,EAAE;CAC5B,IAAI,MAAM;AACV,MAAK,MAAM,CAAC,GAAG,WAAW,iBAAiB,SAAS,CAClD,QAAO,OAAO,KAAK,GAAG,GAAG;AAM3B,QAAO,OADO,qBAAqB,MAAM;;;AAK3C,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,OAAO,uBAAuB;CACxC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,aACE;CACF,WAAW;CACX,SAAS,CAAC,GAAG,GAAG;CAChB,UAAU;EACR;EACA;EACA;EACD;CACD;CACA;CACA;CACA;CACD"}
package/dist/si/emso.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ParsedPersonId, ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/si/emso.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;
@@ -13,7 +12,7 @@ declare const parse: (value: string) => ParsedPersonId | null;
13
12
  /** Generate a random valid Slovenian EMSO. */
14
13
  declare const generate: () => string;
15
14
  /** Slovenian Unique Master Citizen Number. */
16
- declare const emso: Validator;
15
+ declare const emso: Validator<ParsedPersonId>;
17
16
  //#endregion
18
17
  export { compact, emso as default, format, generate, parse, validate };
19
18
  //# sourceMappingURL=emso.d.ts.map
package/dist/si/emso.js CHANGED
@@ -12,8 +12,14 @@ import { isValidDate } from "../_util/date.js";
12
12
  * encoding DDMMYYY (7-digit birth year), region code,
13
13
  * serial number, and a check digit.
14
14
  *
15
- * @see https://en.wikipedia.org/wiki/Unique_Master_Citizen_Number
16
- * @see https://www.gov.si/
15
+ * Canonical source:
16
+ * - Slovenia government registry overview
17
+ *
18
+ * This is the source behind the stricter register
19
+ * code check (50-59) and the decision to reject
20
+ * future birth dates in the embedded date field.
21
+ *
22
+ * @see https://www.gov.si/teme/registri-in-evidence-prebivalstva/
17
23
  */
18
24
  const WEIGHTS = [
19
25
  7,
@@ -47,6 +53,12 @@ const validate = (value) => {
47
53
  if (yyyy < 900) yyyy += 2e3;
48
54
  else yyyy += 1e3;
49
55
  if (!isValidDate(yyyy, mm, dd)) return err("INVALID_COMPONENT", "EMŠO contains an invalid date");
56
+ const birthDate = new Date(yyyy, mm - 1, dd);
57
+ const today = /* @__PURE__ */ new Date();
58
+ today.setHours(0, 0, 0, 0);
59
+ if (birthDate > today) return err("INVALID_COMPONENT", "EMŠO cannot contain a future birth date");
60
+ const register = Number(v.slice(7, 9));
61
+ if (register < 50 || register > 59) return err("INVALID_COMPONENT", "EMSO register code must be between 50 and 59");
50
62
  if (calcCheckDigit(v) !== Number(v[12])) return err("INVALID_CHECKSUM", "EMŠO check digit does not match");
51
63
  return {
52
64
  valid: true,
@@ -79,7 +91,7 @@ const generate = () => {
79
91
  const dd = String(randomInt(1, 28)).padStart(2, "0");
80
92
  const mm = String(randomInt(1, 12)).padStart(2, "0");
81
93
  const yyy = String(randomInt(900, 999));
82
- const rr = String(randomInt(50, 99));
94
+ const rr = String(randomInt(50, 59));
83
95
  const sss = String(randomInt(0, 999)).padStart(3, "0");
84
96
  const partial = dd + mm + yyy + rr + sss;
85
97
  const c = partial + String(calcCheckDigit(partial));
@@ -95,10 +107,11 @@ const emso = {
95
107
  candidatePattern: "\\d{13}",
96
108
  country: "SI",
97
109
  entityType: "person",
98
- sourceUrl: "https://www.gov.si/",
110
+ sourceUrl: "https://www.gov.si/teme/registri-in-evidence-prebivalstva/",
99
111
  examples: ["0101006500006"],
100
112
  compact,
101
113
  format,
114
+ parse,
102
115
  validate,
103
116
  generate
104
117
  };
@@ -1 +1 @@
1
- {"version":3,"file":"emso.js","names":[],"sources":["../../src/si/emso.ts"],"sourcesContent":["/**\n * EMŠO (Enotna matična številka občana).\n *\n * Slovenian unique master citizen number. 13 digits\n * encoding DDMMYYY (7-digit birth year), region code,\n * serial number, and a check digit.\n *\n * @see https://en.wikipedia.org/wiki/Unique_Master_Citizen_Number\n * @see https://www.gov.si/\n */\n\nimport { weightedSum } from \"#checksums/weighted-sum\";\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { 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 = [\n 7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2,\n] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\n/**\n * Compute the check digit matching python-stdnum:\n * `str(-total % 11 % 10)`.\n */\nconst calcCheckDigit = (v: string): number => {\n const total = weightedSum(v.slice(0, 12), WEIGHTS, 11);\n // Python's (-total % 11 % 10) — always 0..9\n // weightedSum already returns (sum % 11 + 11) % 11,\n // so total is 0..10. Negate within mod 11:\n return ((11 - total) % 11) % 10;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 13) {\n return err(\n \"INVALID_LENGTH\",\n \"EMŠO must be exactly 13 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"EMŠO must contain only digits\",\n );\n }\n\n // Validate embedded birth date (DDMMYYY)\n const dd = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n let yyyy = Number(v.slice(4, 7));\n // JMBG standard: 900-999 → 1900s, 000-899 → 2000s\n // Note: python-stdnum uses 800 as threshold, but\n // the official JMBG spec uses 900. No practical\n // difference (800-899 range has no living citizens).\n if (yyyy < 900) {\n yyyy += 2000;\n } else {\n yyyy += 1000;\n }\n if (!isValidDate(yyyy, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"EMŠO contains an invalid date\",\n );\n }\n\n if (calcCheckDigit(v) !== Number(v[12])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"EMŠO check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Extract birth date and gender from an EMSO.\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 dd = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n let yyyy = Number(v.slice(4, 7));\n if (yyyy < 900) {\n yyyy += 2000;\n } else {\n yyyy += 1000;\n }\n\n const serial = Number(v.slice(9, 12));\n\n return {\n birthDate: new Date(yyyy, mm - 1, dd),\n gender: serial < 500 ? \"male\" : \"female\",\n };\n};\n\n/** Generate a random valid Slovenian EMSO. */\nconst generate = (): string => {\n for (;;) {\n const dd = String(randomInt(1, 28)).padStart(2, \"0\");\n const mm = String(randomInt(1, 12)).padStart(2, \"0\");\n const yyy = String(randomInt(900, 999));\n const rr = String(randomInt(50, 99));\n const sss = String(randomInt(0, 999)).padStart(3, \"0\");\n const partial = dd + mm + yyy + rr + sss;\n const c = partial + String(calcCheckDigit(partial));\n if (validate(c).valid) return c;\n }\n};\n\n/** Slovenian Unique Master Citizen Number. */\nconst emso: Validator = {\n name: \"Slovenian Personal ID\",\n localName: \"Enotna matična številka občana\",\n abbreviation: \"EMŠO\",\n aliases: [\n \"EMŠO\",\n \"enotna matična številka občana\",\n ] as const,\n candidatePattern: \"\\\\d{13}\",\n country: \"SI\",\n entityType: \"person\",\n sourceUrl: \"https://www.gov.si/\",\n examples: [\"0101006500006\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default emso;\nexport { compact, format, parse, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;AAwBA,MAAM,UAAU;CACd;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAClC;AAED,MAAM,WAAW,UACf,MAAM,OAAO,KAAK;;;;;AAMpB,MAAM,kBAAkB,MAAsB;AAK5C,SAAS,KAJK,YAAY,EAAE,MAAM,GAAG,GAAG,EAAE,SAAS,GAAG,IAI/B,KAAM;;AAG/B,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,iCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,gCACD;CAIH,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,IAAI,OAAO,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;AAKhC,KAAI,OAAO,IACT,SAAQ;KAER,SAAQ;AAEV,KAAI,CAAC,YAAY,MAAM,IAAI,GAAG,CAC5B,QAAO,IACL,qBACA,gCACD;AAGH,KAAI,eAAe,EAAE,KAAK,OAAO,EAAE,IAAI,CACrC,QAAO,IACL,oBACA,kCACD;AAGH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B,QAAQ,MAAM;;;;;AAMxD,MAAM,SAAS,UAAyC;CACtD,MAAM,SAAS,SAAS,MAAM;AAC9B,KAAI,CAAC,OAAO,MAAO,QAAO;CAE1B,MAAM,IAAI,OAAO;CACjB,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,IAAI,OAAO,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;AAChC,KAAI,OAAO,IACT,SAAQ;KAER,SAAQ;CAGV,MAAM,SAAS,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;AAErC,QAAO;EACL,WAAW,IAAI,KAAK,MAAM,KAAK,GAAG,GAAG;EACrC,QAAQ,SAAS,MAAM,SAAS;EACjC;;;AAIH,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,KAAK,OAAO,UAAU,GAAG,GAAG,CAAC,CAAC,SAAS,GAAG,IAAI;EACpD,MAAM,KAAK,OAAO,UAAU,GAAG,GAAG,CAAC,CAAC,SAAS,GAAG,IAAI;EACpD,MAAM,MAAM,OAAO,UAAU,KAAK,IAAI,CAAC;EACvC,MAAM,KAAK,OAAO,UAAU,IAAI,GAAG,CAAC;EACpC,MAAM,MAAM,OAAO,UAAU,GAAG,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI;EACtD,MAAM,UAAU,KAAK,KAAK,MAAM,KAAK;EACrC,MAAM,IAAI,UAAU,OAAO,eAAe,QAAQ,CAAC;AACnD,MAAI,SAAS,EAAE,CAAC,MAAO,QAAO;;;;AAKlC,MAAM,OAAkB;CACtB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CACP,QACA,iCACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WAAW;CACX,UAAU,CAAC,gBAAgB;CAC3B;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"emso.js","names":[],"sources":["../../src/si/emso.ts"],"sourcesContent":["/**\n * EMŠO (Enotna matična številka občana).\n *\n * Slovenian unique master citizen number. 13 digits\n * encoding DDMMYYY (7-digit birth year), region code,\n * serial number, and a check digit.\n *\n * Canonical source:\n * - Slovenia government registry overview\n *\n * This is the source behind the stricter register\n * code check (50-59) and the decision to reject\n * future birth dates in the embedded date field.\n *\n * @see https://www.gov.si/teme/registri-in-evidence-prebivalstva/\n */\n\nimport { weightedSum } from \"#checksums/weighted-sum\";\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { 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 = [\n 7, 6, 5, 4, 3, 2, 7, 6, 5, 4, 3, 2,\n] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\n/**\n * Compute the check digit matching python-stdnum:\n * `str(-total % 11 % 10)`.\n */\nconst calcCheckDigit = (v: string): number => {\n const total = weightedSum(v.slice(0, 12), WEIGHTS, 11);\n // Python's (-total % 11 % 10) — always 0..9\n // weightedSum already returns (sum % 11 + 11) % 11,\n // so total is 0..10. Negate within mod 11:\n return ((11 - total) % 11) % 10;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 13) {\n return err(\n \"INVALID_LENGTH\",\n \"EMŠO must be exactly 13 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"EMŠO must contain only digits\",\n );\n }\n\n // Validate embedded birth date (DDMMYYY)\n const dd = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n let yyyy = Number(v.slice(4, 7));\n // JMBG standard: 900-999 → 1900s, 000-899 → 2000s\n // Note: python-stdnum uses 800 as threshold, but\n // the official JMBG spec uses 900. No practical\n // difference (800-899 range has no living citizens).\n if (yyyy < 900) {\n yyyy += 2000;\n } else {\n yyyy += 1000;\n }\n if (!isValidDate(yyyy, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"EMŠO contains an invalid date\",\n );\n }\n const birthDate = new Date(yyyy, mm - 1, dd);\n const today = new Date();\n today.setHours(0, 0, 0, 0);\n if (birthDate > today) {\n return err(\n \"INVALID_COMPONENT\",\n \"EMŠO cannot contain a future birth date\",\n );\n }\n\n const register = Number(v.slice(7, 9));\n if (register < 50 || register > 59) {\n return err(\n \"INVALID_COMPONENT\",\n \"EMSO register code must be between 50 and 59\",\n );\n }\n\n if (calcCheckDigit(v) !== Number(v[12])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"EMŠO check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Extract birth date and gender from an EMSO.\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 dd = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n let yyyy = Number(v.slice(4, 7));\n if (yyyy < 900) {\n yyyy += 2000;\n } else {\n yyyy += 1000;\n }\n\n const serial = Number(v.slice(9, 12));\n\n return {\n birthDate: new Date(yyyy, mm - 1, dd),\n gender: serial < 500 ? \"male\" : \"female\",\n };\n};\n\n/** Generate a random valid Slovenian EMSO. */\nconst generate = (): string => {\n for (;;) {\n const dd = String(randomInt(1, 28)).padStart(2, \"0\");\n const mm = String(randomInt(1, 12)).padStart(2, \"0\");\n const yyy = String(randomInt(900, 999));\n const rr = String(randomInt(50, 59));\n const sss = String(randomInt(0, 999)).padStart(3, \"0\");\n const partial = dd + mm + yyy + rr + sss;\n const c = partial + String(calcCheckDigit(partial));\n if (validate(c).valid) return c;\n }\n};\n\n/** Slovenian Unique Master Citizen Number. */\nconst emso: Validator<ParsedPersonId> = {\n name: \"Slovenian Personal ID\",\n localName: \"Enotna matična številka občana\",\n abbreviation: \"EMŠO\",\n aliases: [\n \"EMŠO\",\n \"enotna matična številka občana\",\n ] as const,\n candidatePattern: \"\\\\d{13}\",\n country: \"SI\",\n entityType: \"person\",\n sourceUrl:\n \"https://www.gov.si/teme/registri-in-evidence-prebivalstva/\",\n examples: [\"0101006500006\"] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default emso;\nexport { compact, format, parse, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;AA8BA,MAAM,UAAU;CACd;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAClC;AAED,MAAM,WAAW,UACf,MAAM,OAAO,KAAK;;;;;AAMpB,MAAM,kBAAkB,MAAsB;AAK5C,SAAS,KAJK,YAAY,EAAE,MAAM,GAAG,GAAG,EAAE,SAAS,GAAG,IAI/B,KAAM;;AAG/B,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,iCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,gCACD;CAIH,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,IAAI,OAAO,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;AAKhC,KAAI,OAAO,IACT,SAAQ;KAER,SAAQ;AAEV,KAAI,CAAC,YAAY,MAAM,IAAI,GAAG,CAC5B,QAAO,IACL,qBACA,gCACD;CAEH,MAAM,YAAY,IAAI,KAAK,MAAM,KAAK,GAAG,GAAG;CAC5C,MAAM,wBAAQ,IAAI,MAAM;AACxB,OAAM,SAAS,GAAG,GAAG,GAAG,EAAE;AAC1B,KAAI,YAAY,MACd,QAAO,IACL,qBACA,0CACD;CAGH,MAAM,WAAW,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;AACtC,KAAI,WAAW,MAAM,WAAW,GAC9B,QAAO,IACL,qBACA,+CACD;AAGH,KAAI,eAAe,EAAE,KAAK,OAAO,EAAE,IAAI,CACrC,QAAO,IACL,oBACA,kCACD;AAGH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B,QAAQ,MAAM;;;;;AAMxD,MAAM,SAAS,UAAyC;CACtD,MAAM,SAAS,SAAS,MAAM;AAC9B,KAAI,CAAC,OAAO,MAAO,QAAO;CAE1B,MAAM,IAAI,OAAO;CACjB,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,IAAI,OAAO,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;AAChC,KAAI,OAAO,IACT,SAAQ;KAER,SAAQ;CAGV,MAAM,SAAS,OAAO,EAAE,MAAM,GAAG,GAAG,CAAC;AAErC,QAAO;EACL,WAAW,IAAI,KAAK,MAAM,KAAK,GAAG,GAAG;EACrC,QAAQ,SAAS,MAAM,SAAS;EACjC;;;AAIH,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,KAAK,OAAO,UAAU,GAAG,GAAG,CAAC,CAAC,SAAS,GAAG,IAAI;EACpD,MAAM,KAAK,OAAO,UAAU,GAAG,GAAG,CAAC,CAAC,SAAS,GAAG,IAAI;EACpD,MAAM,MAAM,OAAO,UAAU,KAAK,IAAI,CAAC;EACvC,MAAM,KAAK,OAAO,UAAU,IAAI,GAAG,CAAC;EACpC,MAAM,MAAM,OAAO,UAAU,GAAG,IAAI,CAAC,CAAC,SAAS,GAAG,IAAI;EACtD,MAAM,UAAU,KAAK,KAAK,MAAM,KAAK;EACrC,MAAM,IAAI,UAAU,OAAO,eAAe,QAAQ,CAAC;AACnD,MAAI,SAAS,EAAE,CAAC,MAAO,QAAO;;;;AAKlC,MAAM,OAAkC;CACtC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CACP,QACA,iCACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WACE;CACF,UAAU,CAAC,gBAAgB;CAC3B;CACA;CACA;CACA;CACA;CACD"}
package/dist/si/mod.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import emso from "./emso.js";
3
2
  import vat from "./vat.js";
4
3
 
5
4
  //#region src/si/mod.d.ts
6
-
5
+ declare namespace mod_d_exports {
6
+ export { emso, vat };
7
+ }
7
8
  //#endregion
8
9
  export { emso, mod_d_exports, vat };
9
10
  //# sourceMappingURL=mod.d.ts.map
package/dist/si/vat.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/si/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/sk/dic.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/sk/dic.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/sk/ico.d.ts CHANGED
@@ -2,7 +2,6 @@ import { Validator } from "../types.js";
2
2
  import { compact, format, generate, validate } from "../cz/ico.js";
3
3
 
4
4
  //#region src/sk/ico.d.ts
5
-
6
5
  /** Slovak Company Identification Number. */
7
6
  declare const ico: Validator;
8
7
  //#endregion
package/dist/sk/mod.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import dic from "./dic.js";
3
2
  import ico from "./ico.js";
4
3
  import rc from "./rc.js";
5
4
 
6
5
  //#region src/sk/mod.d.ts
7
-
6
+ declare namespace mod_d_exports {
7
+ export { dic, ico, rc };
8
+ }
8
9
  //#endregion
9
10
  export { dic, ico, mod_d_exports, rc };
10
11
  //# sourceMappingURL=mod.d.ts.map
package/dist/sk/rc.d.ts CHANGED
@@ -1,10 +1,9 @@
1
- import { Validator } from "../types.js";
2
- import { compact, format, parse, validate } from "../cz/rc.js";
1
+ import { ParsedPersonId, Validator } from "../types.js";
2
+ import { compact, format, generate, parse, validate } from "../cz/rc.js";
3
3
 
4
4
  //#region src/sk/rc.d.ts
5
-
6
5
  /** Slovak Birth Number. */
7
- declare const rc: Validator;
6
+ declare const rc: Validator<ParsedPersonId>;
8
7
  //#endregion
9
- export { compact, rc as default, format, parse, validate };
8
+ export { compact, rc as default, format, generate, parse, validate };
10
9
  //# sourceMappingURL=rc.d.ts.map
package/dist/sk/rc.js CHANGED
@@ -1,4 +1,4 @@
1
- import { compact, format, parse, validate } from "../cz/rc.js";
1
+ import { compact, format, generate, parse, validate } from "../cz/rc.js";
2
2
  //#region src/sk/rc.ts
3
3
  /**
4
4
  * RČ (Rodné číslo).
@@ -19,12 +19,15 @@ const rc = {
19
19
  country: "SK",
20
20
  entityType: "person",
21
21
  sourceUrl: "https://www.minv.sk/",
22
+ lengths: [9, 10],
22
23
  examples: ["7103192745"],
23
24
  compact,
24
25
  format,
26
+ generate,
27
+ parse,
25
28
  validate
26
29
  };
27
30
  //#endregion
28
- export { compact, rc as default, format, parse, validate };
31
+ export { compact, rc as default, format, generate, parse, validate };
29
32
 
30
33
  //# sourceMappingURL=rc.js.map
package/dist/sk/rc.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"rc.js","names":[],"sources":["../../src/sk/rc.ts"],"sourcesContent":["/**\n * RČ (Rodné číslo).\n *\n * Slovak birth number. Identical algorithm to\n * the Czech birth number.\n *\n * @see https://www.mvcr.cz/mvcren/docDetail.aspx?docid=21975362&doctype=ART\n * @see https://www.minv.sk/\n */\n\nimport { compact, format, parse, validate } from \"../cz/rc\";\nimport type { Validator } from \"../types\";\n\n/** Slovak Birth Number. */\nconst rc: Validator = {\n name: \"Slovak Birth Number\",\n localName: \"Rodné číslo\",\n abbreviation: \"RČ\",\n aliases: [\"rodné číslo\", \"RČ\"] as const,\n candidatePattern: \"\\\\d{6}/\\\\d{3,4}\",\n country: \"SK\",\n entityType: \"person\",\n sourceUrl: \"https://www.minv.sk/\",\n examples: [\"7103192745\"] as const,\n compact,\n format,\n validate,\n};\n\nexport default rc;\nexport { compact, format, parse, validate };\n"],"mappings":";;;;;;;;;;;;AAcA,MAAM,KAAgB;CACpB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,eAAe,KAAK;CAC9B,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WAAW;CACX,UAAU,CAAC,aAAa;CACxB;CACA;CACA;CACD"}
1
+ {"version":3,"file":"rc.js","names":[],"sources":["../../src/sk/rc.ts"],"sourcesContent":["/**\n * RČ (Rodné číslo).\n *\n * Slovak birth number. Identical algorithm to\n * the Czech birth number.\n *\n * @see https://www.mvcr.cz/mvcren/docDetail.aspx?docid=21975362&doctype=ART\n * @see https://www.minv.sk/\n */\n\nimport {\n compact,\n format,\n generate,\n parse,\n validate,\n} from \"../cz/rc\";\nimport type { ParsedPersonId, Validator } from \"../types\";\n\n/** Slovak Birth Number. */\nconst rc: Validator<ParsedPersonId> = {\n name: \"Slovak Birth Number\",\n localName: \"Rodné číslo\",\n abbreviation: \"RČ\",\n aliases: [\"rodné číslo\", \"RČ\"] as const,\n candidatePattern: \"\\\\d{6}/\\\\d{3,4}\",\n country: \"SK\",\n entityType: \"person\",\n sourceUrl: \"https://www.minv.sk/\",\n lengths: [9, 10] as const,\n examples: [\"7103192745\"] as const,\n compact,\n format,\n generate,\n parse,\n validate,\n};\n\nexport default rc;\nexport { compact, format, generate, parse, validate };\n"],"mappings":";;;;;;;;;;;;AAoBA,MAAM,KAAgC;CACpC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,eAAe,KAAK;CAC9B,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WAAW;CACX,SAAS,CAAC,GAAG,GAAG;CAChB,UAAU,CAAC,aAAa;CACxB;CACA;CACA;CACA;CACA;CACD"}
package/dist/th/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import tin from "./tin.js";
3
2
 
4
3
  //#region src/th/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { tin };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, tin };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/th/tin.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/th/tin.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/tr/mod.d.ts CHANGED
@@ -1,9 +1,10 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import tckimlik from "./tckimlik.js";
3
2
  import vkn from "./vkn.js";
4
3
 
5
4
  //#region src/tr/mod.d.ts
6
-
5
+ declare namespace mod_d_exports {
6
+ export { tckimlik, vkn };
7
+ }
7
8
  //#endregion
8
9
  export { mod_d_exports, tckimlik, vkn };
9
10
  //# sourceMappingURL=mod.d.ts.map
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/tr/tckimlik.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  /** TR IDs are displayed without separators. */
package/dist/tr/vkn.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/tr/vkn.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  /** TR IDs are displayed without separators. */
package/dist/tw/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import ubn from "./ubn.js";
3
2
 
4
3
  //#region src/tw/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { ubn };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, ubn };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/tw/ubn.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/tw/ubn.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/tw/ubn.js CHANGED
@@ -39,7 +39,7 @@ const validate = (value) => {
39
39
  if (v.length !== 8) return err("INVALID_LENGTH", "UBN must be exactly 8 digits");
40
40
  if (!isdigits(v)) return err("INVALID_FORMAT", "UBN must contain only digits");
41
41
  let sum = 0;
42
- for (let i = 0; i < 8; i++) sum += digitSum(Number(v[i]) * WEIGHTS[i]);
42
+ for (const [i, weight] of WEIGHTS.entries()) sum += digitSum(Number(v[i]) * weight);
43
43
  const checksum = sum % 10;
44
44
  if (checksum === 0) return {
45
45
  valid: true,
@@ -1 +1 @@
1
- {"version":3,"file":"ubn.js","names":[],"sources":["../../src/tw/ubn.ts"],"sourcesContent":["/**\n * UBN (Unified Business Number, 統一編號).\n *\n * Taiwanese company identifier. 8 digits with a\n * weighted checksum. Weights [1,2,1,2,1,2,4,1];\n * sum the individual digits of each product, total\n * mod 10 must be 0. Special case: when the 7th digit\n * is 7, checksum 9 is also accepted.\n *\n * @see https://zh.wikipedia.org/wiki/統一編號\n */\n\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 WEIGHTS = [1, 2, 1, 2, 1, 2, 4, 1] as const;\n\n/** Sum the individual digits of a number. */\nconst digitSum = (n: number): number => {\n let sum = 0;\n while (n > 0) {\n sum += n % 10;\n n = Math.floor(n / 10);\n }\n return sum;\n};\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 8) {\n return err(\n \"INVALID_LENGTH\",\n \"UBN must be exactly 8 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"UBN must contain only digits\",\n );\n }\n\n let sum = 0;\n for (let i = 0; i < 8; i++) {\n sum += digitSum(Number(v[i]) * WEIGHTS[i]!);\n }\n\n const checksum = sum % 10;\n if (checksum === 0) {\n return { valid: true, compact: v };\n }\n\n // Special case: when the 7th digit is 7 (product\n // 4*7=28), the checksum may be 9 instead of 0.\n if (checksum === 9 && v[6] === \"7\") {\n return { valid: true, compact: v };\n }\n\n return err(\n \"INVALID_CHECKSUM\",\n \"UBN checksum does not match\",\n );\n};\n\nconst format = (value: string): string => compact(value);\n\n/** Generate a random valid UBN. */\nconst generate = (): string => {\n for (;;) {\n const prefix = randomDigits(7);\n for (let d = 0; d < 10; d++) {\n const candidate = `${prefix}${d}`;\n const r = validate(candidate);\n if (r.valid) return candidate;\n }\n }\n};\n\n/** Taiwanese Unified Business Number. */\nconst ubn: Validator = {\n name: \"Unified Business Number\",\n localName: \"統一編號\",\n abbreviation: \"UBN\",\n aliases: [\"統一編號\", \"UBN\"] as const,\n candidatePattern: \"\\\\d{8}\",\n country: \"TW\",\n entityType: \"company\",\n description:\n \"8-digit company identifier issued by the Ministry of Economic Affairs\",\n sourceUrl:\n \"https://zh.wikipedia.org/wiki/%E7%B5%B1%E4%B8%80%E7%B7%A8%E8%99%9F\",\n lengths: [8] as const,\n examples: [\"00501503\", \"04595257\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default ubn;\nexport { compact, format, generate, validate };\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,MAAM,UAAU;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;;AAGxC,MAAM,YAAY,MAAsB;CACtC,IAAI,MAAM;AACV,QAAO,IAAI,GAAG;AACZ,SAAO,IAAI;AACX,MAAI,KAAK,MAAM,IAAI,GAAG;;AAExB,QAAO;;AAGT,MAAM,WAAW,UACf,MAAM,OAAO,KAAK;AAEpB,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,EACf,QAAO,IACL,kBACA,+BACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,+BACD;CAGH,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IACrB,QAAO,SAAS,OAAO,EAAE,GAAG,GAAG,QAAQ,GAAI;CAG7C,MAAM,WAAW,MAAM;AACvB,KAAI,aAAa,EACf,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;AAKpC,KAAI,aAAa,KAAK,EAAE,OAAO,IAC7B,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;AAGpC,QAAO,IACL,oBACA,8BACD;;AAGH,MAAM,UAAU,UAA0B,QAAQ,MAAM;;AAGxD,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,SAAS,aAAa,EAAE;AAC9B,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;GAC3B,MAAM,YAAY,GAAG,SAAS;AAE9B,OADU,SAAS,UAAU,CACvB,MAAO,QAAO;;;;;AAM1B,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,QAAQ,MAAM;CACxB,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,aACE;CACF,WACE;CACF,SAAS,CAAC,EAAE;CACZ,UAAU,CAAC,YAAY,WAAW;CAClC;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"ubn.js","names":[],"sources":["../../src/tw/ubn.ts"],"sourcesContent":["/**\n * UBN (Unified Business Number, 統一編號).\n *\n * Taiwanese company identifier. 8 digits with a\n * weighted checksum. Weights [1,2,1,2,1,2,4,1];\n * sum the individual digits of each product, total\n * mod 10 must be 0. Special case: when the 7th digit\n * is 7, checksum 9 is also accepted.\n *\n * @see https://zh.wikipedia.org/wiki/統一編號\n */\n\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 WEIGHTS = [1, 2, 1, 2, 1, 2, 4, 1] as const;\n\n/** Sum the individual digits of a number. */\nconst digitSum = (n: number): number => {\n let sum = 0;\n while (n > 0) {\n sum += n % 10;\n n = Math.floor(n / 10);\n }\n return sum;\n};\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 8) {\n return err(\n \"INVALID_LENGTH\",\n \"UBN must be exactly 8 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"UBN must contain only digits\",\n );\n }\n\n let sum = 0;\n for (const [i, weight] of WEIGHTS.entries()) {\n sum += digitSum(Number(v[i]) * weight);\n }\n\n const checksum = sum % 10;\n if (checksum === 0) {\n return { valid: true, compact: v };\n }\n\n // Special case: when the 7th digit is 7 (product\n // 4*7=28), the checksum may be 9 instead of 0.\n if (checksum === 9 && v[6] === \"7\") {\n return { valid: true, compact: v };\n }\n\n return err(\n \"INVALID_CHECKSUM\",\n \"UBN checksum does not match\",\n );\n};\n\nconst format = (value: string): string => compact(value);\n\n/** Generate a random valid UBN. */\nconst generate = (): string => {\n for (;;) {\n const prefix = randomDigits(7);\n for (let d = 0; d < 10; d++) {\n const candidate = `${prefix}${d}`;\n const r = validate(candidate);\n if (r.valid) return candidate;\n }\n }\n};\n\n/** Taiwanese Unified Business Number. */\nconst ubn: Validator = {\n name: \"Unified Business Number\",\n localName: \"統一編號\",\n abbreviation: \"UBN\",\n aliases: [\"統一編號\", \"UBN\"] as const,\n candidatePattern: \"\\\\d{8}\",\n country: \"TW\",\n entityType: \"company\",\n description:\n \"8-digit company identifier issued by the Ministry of Economic Affairs\",\n sourceUrl:\n \"https://zh.wikipedia.org/wiki/%E7%B5%B1%E4%B8%80%E7%B7%A8%E8%99%9F\",\n lengths: [8] as const,\n examples: [\"00501503\", \"04595257\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default ubn;\nexport { compact, format, generate, validate };\n"],"mappings":";;;;;;;;;;;;;;;;AAmBA,MAAM,UAAU;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;;AAGxC,MAAM,YAAY,MAAsB;CACtC,IAAI,MAAM;AACV,QAAO,IAAI,GAAG;AACZ,SAAO,IAAI;AACX,MAAI,KAAK,MAAM,IAAI,GAAG;;AAExB,QAAO;;AAGT,MAAM,WAAW,UACf,MAAM,OAAO,KAAK;AAEpB,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,EACf,QAAO,IACL,kBACA,+BACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,+BACD;CAGH,IAAI,MAAM;AACV,MAAK,MAAM,CAAC,GAAG,WAAW,QAAQ,SAAS,CACzC,QAAO,SAAS,OAAO,EAAE,GAAG,GAAG,OAAO;CAGxC,MAAM,WAAW,MAAM;AACvB,KAAI,aAAa,EACf,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;AAKpC,KAAI,aAAa,KAAK,EAAE,OAAO,IAC7B,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;AAGpC,QAAO,IACL,oBACA,8BACD;;AAGH,MAAM,UAAU,UAA0B,QAAQ,MAAM;;AAGxD,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,SAAS,aAAa,EAAE;AAC9B,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;GAC3B,MAAM,YAAY,GAAG,SAAS;AAE9B,OADU,SAAS,UAAU,CACvB,MAAO,QAAO;;;;;AAM1B,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,QAAQ,MAAM;CACxB,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,aACE;CACF,WACE;CACF,SAAS,CAAC,EAAE;CACZ,UAAU,CAAC,YAAY,WAAW;CAClC;CACA;CACA;CACA;CACD"}