@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/nl/mod.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import bsn from "./bsn.js";
3
2
  import kvk from "./kvk.js";
4
3
  import vat from "./vat.js";
5
4
 
6
5
  //#region src/nl/mod.d.ts
7
-
6
+ declare namespace mod_d_exports {
7
+ export { bsn, kvk, vat };
8
+ }
8
9
  //#endregion
9
10
  export { bsn, kvk, mod_d_exports, vat };
10
11
  //# sourceMappingURL=mod.d.ts.map
package/dist/nl/vat.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/nl/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/nl/vat.js CHANGED
@@ -7,10 +7,13 @@ import { isdigits } from "../_util/strings.js";
7
7
  * BTW (Dutch VAT number).
8
8
  *
9
9
  * 12 characters: 9 digits + "B" + 2 digits.
10
- * Mod 97 check: convert "NL" + number to numeric
11
- * (N=23, L=21, B=11), mod 97 === 1.
12
10
  *
13
- * @see https://www.vatify.eu/netherlands-vat-number.html
11
+ * The current Dutch VAT identification number
12
+ * (btw-id) format is structural: `NL` + 9 digits
13
+ * + `B` + 2 digits. The official guidance explicitly
14
+ * states that the 9 digits are not derived from BSN
15
+ * and the final 2 digits are random.
16
+ *
14
17
  * @see https://business.gov.nl/regulations/using-checking-vat-numbers/
15
18
  */
16
19
  const compact = (value) => {
@@ -21,44 +24,12 @@ const compact = (value) => {
21
24
  if (bIdx > 0 && bIdx < 9) v = v.slice(0, bIdx).padStart(9, "0") + v.slice(bIdx);
22
25
  return v;
23
26
  };
24
- /**
25
- * Compute mod 97 of a string containing digits and
26
- * uppercase letters (A=10, B=11, ..., Z=35).
27
- */
28
- const mod97 = (value) => {
29
- let remainder = 0;
30
- for (const ch of value) {
31
- const code = ch.charCodeAt(0);
32
- if (code >= 48 && code <= 57) remainder = (remainder * 10 + (code - 48)) % 97;
33
- else if (code >= 65 && code <= 90) {
34
- const num = code - 55;
35
- remainder = (remainder * 100 + num) % 97;
36
- }
37
- }
38
- return remainder;
39
- };
40
- /**
41
- * Check if a 9-digit string is a valid BSN (Dutch citizen
42
- * identification number). Weights [9,8,7,6,5,4,3,2,-1],
43
- * sum % 11 === 0.
44
- */
45
- const bsnValid = (num) => {
46
- if (!isdigits(num) || num.length !== 9) return false;
47
- if (Number.parseInt(num, 10) <= 0) return false;
48
- let sum = 0;
49
- for (let i = 0; i < 8; i++) sum += (9 - i) * Number(num[i]);
50
- sum -= Number(num[8]);
51
- return sum % 11 === 0;
52
- };
53
27
  const validate = (value) => {
54
28
  const v = compact(value);
55
29
  if (v.length !== 12) return err("INVALID_LENGTH", "Dutch VAT number must be 12 characters");
56
30
  if (!isdigits(v.slice(0, 9))) return err("INVALID_FORMAT", "Dutch VAT number must start with 9 digits");
57
31
  if (v[9] !== "B") return err("INVALID_COMPONENT", "Dutch VAT number digit 10 must be B");
58
32
  if (!isdigits(v.slice(10, 12))) return err("INVALID_FORMAT", "Dutch VAT number must end with 2 digits");
59
- if (Number.parseInt(v.slice(0, 9), 10) <= 0) return err("INVALID_FORMAT", "Dutch VAT number first 9 digits must be > 0");
60
- if (Number.parseInt(v.slice(10, 12), 10) <= 0) return err("INVALID_FORMAT", "Dutch VAT number last 2 digits must be > 0");
61
- if (!bsnValid(v.slice(0, 9)) && mod97(`NL${v}`) !== 1) return err("INVALID_CHECKSUM", "Dutch VAT number checksum failed");
62
33
  return {
63
34
  valid: true,
64
35
  compact: v
@@ -66,12 +37,7 @@ const validate = (value) => {
66
37
  };
67
38
  const format = (value) => `NL${compact(value)}`;
68
39
  /** Generate a random valid Dutch VAT number. */
69
- const generate = () => {
70
- for (;;) {
71
- const c = randomDigits(9) + "B" + randomDigits(2);
72
- if (validate(c).valid) return c;
73
- }
74
- };
40
+ const generate = () => `${randomDigits(9)}B${randomDigits(2)}`;
75
41
  /** Dutch VAT Number. */
76
42
  const vat = {
77
43
  name: "Dutch VAT Number",
@@ -81,8 +47,8 @@ const vat = {
81
47
  candidatePattern: "NL\\d{9}B\\d{2}",
82
48
  country: "NL",
83
49
  entityType: "company",
84
- sourceUrl: "https://www.belastingdienst.nl/",
85
- examples: ["123456789B13"],
50
+ sourceUrl: "https://business.gov.nl/regulations/using-checking-vat-numbers/",
51
+ examples: ["000099998B57"],
86
52
  compact,
87
53
  format,
88
54
  validate,
@@ -1 +1 @@
1
- {"version":3,"file":"vat.js","names":[],"sources":["../../src/nl/vat.ts"],"sourcesContent":["/**\n * BTW (Dutch VAT number).\n *\n * 12 characters: 9 digits + \"B\" + 2 digits.\n * Mod 97 check: convert \"NL\" + number to numeric\n * (N=23, L=21, B=11), mod 97 === 1.\n *\n * @see https://www.vatify.eu/netherlands-vat-number.html\n * @see https://business.gov.nl/regulations/using-checking-vat-numbers/\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 compact = (value: string): string => {\n let v = clean(value, \" -/.\");\n if (v.startsWith(\"NL\") || v.startsWith(\"nl\")) {\n v = v.slice(2);\n }\n v = v.toUpperCase();\n // Zero-pad numeric part to 9 digits if B is found\n const bIdx = v.indexOf(\"B\");\n if (bIdx > 0 && bIdx < 9) {\n v = v.slice(0, bIdx).padStart(9, \"0\") + v.slice(bIdx);\n }\n return v;\n};\n\n/**\n * Compute mod 97 of a string containing digits and\n * uppercase letters (A=10, B=11, ..., Z=35).\n */\nconst mod97 = (value: string): number => {\n let remainder = 0;\n for (const ch of value) {\n const code = ch.charCodeAt(0);\n if (code >= 48 && code <= 57) {\n // Digit\n remainder = (remainder * 10 + (code - 48)) % 97;\n } else if (code >= 65 && code <= 90) {\n // Letter: two decimal digits (A=10, ..., Z=35)\n const num = code - 55;\n remainder = (remainder * 100 + num) % 97;\n }\n }\n return remainder;\n};\n\n/**\n * Check if a 9-digit string is a valid BSN (Dutch citizen\n * identification number). Weights [9,8,7,6,5,4,3,2,-1],\n * sum % 11 === 0.\n */\nconst bsnValid = (num: string): boolean => {\n if (!isdigits(num) || num.length !== 9) return false;\n if (Number.parseInt(num, 10) <= 0) return false;\n let sum = 0;\n for (let i = 0; i < 8; i++) {\n sum += (9 - i) * Number(num[i]);\n }\n sum -= Number(num[8]);\n return sum % 11 === 0;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"Dutch VAT number must be 12 characters\",\n );\n }\n if (!isdigits(v.slice(0, 9))) {\n return err(\n \"INVALID_FORMAT\",\n \"Dutch VAT number must start with 9 digits\",\n );\n }\n if (v[9] !== \"B\") {\n return err(\n \"INVALID_COMPONENT\",\n \"Dutch VAT number digit 10 must be B\",\n );\n }\n if (!isdigits(v.slice(10, 12))) {\n return err(\n \"INVALID_FORMAT\",\n \"Dutch VAT number must end with 2 digits\",\n );\n }\n if (Number.parseInt(v.slice(0, 9), 10) <= 0) {\n return err(\n \"INVALID_FORMAT\",\n \"Dutch VAT number first 9 digits must be > 0\",\n );\n }\n if (Number.parseInt(v.slice(10, 12), 10) <= 0) {\n return err(\n \"INVALID_FORMAT\",\n \"Dutch VAT number last 2 digits must be > 0\",\n );\n }\n // Valid if BSN check passes OR mod 97 check passes\n if (!bsnValid(v.slice(0, 9)) && mod97(`NL${v}`) !== 1) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Dutch VAT number checksum failed\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string =>\n `NL${compact(value)}`;\n\n/** Generate a random valid Dutch VAT number. */\nconst generate = (): string => {\n for (;;) {\n const c = randomDigits(9) + \"B\" + randomDigits(2);\n if (validate(c).valid) return c;\n }\n};\n\n/** Dutch VAT Number. */\nconst vat: Validator = {\n name: \"Dutch VAT Number\",\n localName: \"BTW-identificatienummer\",\n abbreviation: \"BTW\",\n aliases: [\"BTW-nummer\", \"BTW-id\"] as const,\n candidatePattern: \"NL\\\\d{9}B\\\\d{2}\",\n country: \"NL\",\n entityType: \"company\",\n sourceUrl: \"https://www.belastingdienst.nl/\",\n examples: [\"123456789B13\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default vat;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAM,WAAW,UAA0B;CACzC,IAAI,IAAI,MAAM,OAAO,OAAO;AAC5B,KAAI,EAAE,WAAW,KAAK,IAAI,EAAE,WAAW,KAAK,CAC1C,KAAI,EAAE,MAAM,EAAE;AAEhB,KAAI,EAAE,aAAa;CAEnB,MAAM,OAAO,EAAE,QAAQ,IAAI;AAC3B,KAAI,OAAO,KAAK,OAAO,EACrB,KAAI,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI,GAAG,EAAE,MAAM,KAAK;AAEvD,QAAO;;;;;;AAOT,MAAM,SAAS,UAA0B;CACvC,IAAI,YAAY;AAChB,MAAK,MAAM,MAAM,OAAO;EACtB,MAAM,OAAO,GAAG,WAAW,EAAE;AAC7B,MAAI,QAAQ,MAAM,QAAQ,GAExB,cAAa,YAAY,MAAM,OAAO,OAAO;WACpC,QAAQ,MAAM,QAAQ,IAAI;GAEnC,MAAM,MAAM,OAAO;AACnB,gBAAa,YAAY,MAAM,OAAO;;;AAG1C,QAAO;;;;;;;AAQT,MAAM,YAAY,QAAyB;AACzC,KAAI,CAAC,SAAS,IAAI,IAAI,IAAI,WAAW,EAAG,QAAO;AAC/C,KAAI,OAAO,SAAS,KAAK,GAAG,IAAI,EAAG,QAAO;CAC1C,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IACrB,SAAQ,IAAI,KAAK,OAAO,IAAI,GAAG;AAEjC,QAAO,OAAO,IAAI,GAAG;AACrB,QAAO,MAAM,OAAO;;AAGtB,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,yCACD;AAEH,KAAI,CAAC,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC,CAC1B,QAAO,IACL,kBACA,4CACD;AAEH,KAAI,EAAE,OAAO,IACX,QAAO,IACL,qBACA,sCACD;AAEH,KAAI,CAAC,SAAS,EAAE,MAAM,IAAI,GAAG,CAAC,CAC5B,QAAO,IACL,kBACA,0CACD;AAEH,KAAI,OAAO,SAAS,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,IAAI,EACxC,QAAO,IACL,kBACA,8CACD;AAEH,KAAI,OAAO,SAAS,EAAE,MAAM,IAAI,GAAG,EAAE,GAAG,IAAI,EAC1C,QAAO,IACL,kBACA,6CACD;AAGH,KAAI,CAAC,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC,IAAI,MAAM,KAAK,IAAI,KAAK,EAClD,QAAO,IACL,oBACA,mCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UACd,KAAK,QAAQ,MAAM;;AAGrB,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,IAAI,aAAa,EAAE,GAAG,MAAM,aAAa,EAAE;AACjD,MAAI,SAAS,EAAE,CAAC,MAAO,QAAO;;;;AAKlC,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,cAAc,SAAS;CACjC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WAAW;CACX,UAAU,CAAC,eAAe;CAC1B;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"vat.js","names":[],"sources":["../../src/nl/vat.ts"],"sourcesContent":["/**\n * BTW (Dutch VAT number).\n *\n * 12 characters: 9 digits + \"B\" + 2 digits.\n *\n * The current Dutch VAT identification number\n * (btw-id) format is structural: `NL` + 9 digits\n * + `B` + 2 digits. The official guidance explicitly\n * states that the 9 digits are not derived from BSN\n * and the final 2 digits are random.\n *\n * @see https://business.gov.nl/regulations/using-checking-vat-numbers/\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 compact = (value: string): string => {\n let v = clean(value, \" -/.\");\n if (v.startsWith(\"NL\") || v.startsWith(\"nl\")) {\n v = v.slice(2);\n }\n v = v.toUpperCase();\n // Zero-pad numeric part to 9 digits if B is found\n const bIdx = v.indexOf(\"B\");\n if (bIdx > 0 && bIdx < 9) {\n v = v.slice(0, bIdx).padStart(9, \"0\") + v.slice(bIdx);\n }\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"Dutch VAT number must be 12 characters\",\n );\n }\n if (!isdigits(v.slice(0, 9))) {\n return err(\n \"INVALID_FORMAT\",\n \"Dutch VAT number must start with 9 digits\",\n );\n }\n if (v[9] !== \"B\") {\n return err(\n \"INVALID_COMPONENT\",\n \"Dutch VAT number digit 10 must be B\",\n );\n }\n if (!isdigits(v.slice(10, 12))) {\n return err(\n \"INVALID_FORMAT\",\n \"Dutch VAT number must end with 2 digits\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string =>\n `NL${compact(value)}`;\n\n/** Generate a random valid Dutch VAT number. */\nconst generate = (): string =>\n `${randomDigits(9)}B${randomDigits(2)}`;\n\n/** Dutch VAT Number. */\nconst vat: Validator = {\n name: \"Dutch VAT Number\",\n localName: \"BTW-identificatienummer\",\n abbreviation: \"BTW\",\n aliases: [\"BTW-nummer\", \"BTW-id\"] as const,\n candidatePattern: \"NL\\\\d{9}B\\\\d{2}\",\n country: \"NL\",\n entityType: \"company\",\n sourceUrl:\n \"https://business.gov.nl/regulations/using-checking-vat-numbers/\",\n examples: [\"000099998B57\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default vat;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;AAqBA,MAAM,WAAW,UAA0B;CACzC,IAAI,IAAI,MAAM,OAAO,OAAO;AAC5B,KAAI,EAAE,WAAW,KAAK,IAAI,EAAE,WAAW,KAAK,CAC1C,KAAI,EAAE,MAAM,EAAE;AAEhB,KAAI,EAAE,aAAa;CAEnB,MAAM,OAAO,EAAE,QAAQ,IAAI;AAC3B,KAAI,OAAO,KAAK,OAAO,EACrB,KAAI,EAAE,MAAM,GAAG,KAAK,CAAC,SAAS,GAAG,IAAI,GAAG,EAAE,MAAM,KAAK;AAEvD,QAAO;;AAGT,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,yCACD;AAEH,KAAI,CAAC,SAAS,EAAE,MAAM,GAAG,EAAE,CAAC,CAC1B,QAAO,IACL,kBACA,4CACD;AAEH,KAAI,EAAE,OAAO,IACX,QAAO,IACL,qBACA,sCACD;AAEH,KAAI,CAAC,SAAS,EAAE,MAAM,IAAI,GAAG,CAAC,CAC5B,QAAO,IACL,kBACA,0CACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UACd,KAAK,QAAQ,MAAM;;AAGrB,MAAM,iBACJ,GAAG,aAAa,EAAE,CAAC,GAAG,aAAa,EAAE;;AAGvC,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,cAAc,SAAS;CACjC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WACE;CACF,UAAU,CAAC,eAAe;CAC1B;CACA;CACA;CACA;CACD"}
@@ -1,7 +1,6 @@
1
1
  import { ParsedPersonId, ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/no/fodselsnummer.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;
@@ -14,7 +13,7 @@ declare const parse: (value: string) => ParsedPersonId | null;
14
13
  /** Generate a random valid Norwegian birth number. */
15
14
  declare const generate: () => string;
16
15
  /** Norwegian Birth Number. */
17
- declare const fodselsnummer: Validator;
16
+ declare const fodselsnummer: Validator<ParsedPersonId>;
18
17
  //#endregion
19
18
  export { compact, fodselsnummer as default, format, generate, parse, validate };
20
19
  //# sourceMappingURL=fodselsnummer.d.ts.map
@@ -125,6 +125,7 @@ const fodselsnummer = {
125
125
  examples: ["15108695088"],
126
126
  compact,
127
127
  format,
128
+ parse,
128
129
  validate,
129
130
  generate
130
131
  };
@@ -1 +1 @@
1
- {"version":3,"file":"fodselsnummer.js","names":[],"sources":["../../src/no/fodselsnummer.ts"],"sourcesContent":["/**\n * Fødselsnummer (Norwegian birth number).\n *\n * 11 digits: DDMMYY + 3-digit individual number + 2\n * check digits. Supports D-numbers (day + 40) and\n * H-numbers (month + 40).\n *\n * @see https://www.skatteetaten.no/\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 WEIGHTS_D1 = [3, 7, 6, 1, 8, 9, 4, 5, 2] as const;\nconst WEIGHTS_D2 = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\nconst getCentury = (\n yy: number,\n individual: number,\n): number | undefined => {\n if (individual >= 0 && individual <= 499) return 1900;\n if (individual >= 500 && individual <= 749 && yy >= 54) {\n return 1800;\n }\n if (individual >= 500 && individual <= 999 && yy < 40) {\n return 2000;\n }\n if (individual >= 900 && individual <= 999 && yy >= 40) {\n return 1900;\n }\n return undefined;\n};\n\nconst checkDigit = (\n v: string,\n weights: readonly number[],\n): number | undefined => {\n let sum = 0;\n for (let i = 0; i < weights.length; i++) {\n sum += Number(v.charAt(i)) * (weights[i] ?? 0);\n }\n const remainder = (11 - (sum % 11)) % 11;\n if (remainder === 10) return undefined;\n return remainder;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 11) {\n return err(\n \"INVALID_LENGTH\",\n \"Norwegian birth number must be 11 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Norwegian birth number must contain only digits\",\n );\n }\n\n // Check digits\n const d1 = checkDigit(v, WEIGHTS_D1);\n if (d1 === undefined || d1 !== Number(v[9])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Norwegian birth number check digit 1 mismatch\",\n );\n }\n const d2 = checkDigit(v, WEIGHTS_D2);\n if (d2 === undefined || d2 !== Number(v[10])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Norwegian birth number check digit 2 mismatch\",\n );\n }\n\n // Parse date components\n let dd = Number(v.slice(0, 2));\n let mm = Number(v.slice(2, 4));\n const yy = Number(v.slice(4, 6));\n const individual = Number(v.slice(6, 9));\n\n // D-number: day > 40\n if (dd > 40) dd -= 40;\n // H-number: month > 40\n if (mm > 40) mm -= 40;\n\n const century = getCentury(yy, individual);\n if (century === undefined) {\n return err(\n \"INVALID_COMPONENT\",\n \"Norwegian birth number has invalid individual/century combination\",\n );\n }\n\n const year = century + yy;\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Norwegian birth number contains an invalid date\",\n );\n }\n\n // Reject future birth dates\n if (new Date(year, mm - 1, dd) > new Date()) {\n return err(\n \"INVALID_COMPONENT\",\n \"Birth date is in the future\",\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 Norwegian\n * birth number. Returns null if the value is not\n * 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 let dd = Number(v.slice(0, 2));\n let mm = Number(v.slice(2, 4));\n const yy = Number(v.slice(4, 6));\n const individual = Number(v.slice(6, 9));\n\n if (dd > 40) dd -= 40;\n if (mm > 40) mm -= 40;\n\n const century = getCentury(yy, individual);\n if (century === undefined) return null;\n\n const year = century + yy;\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n gender: individual % 2 === 0 ? \"female\" : \"male\",\n };\n};\n\n/** Generate a random valid Norwegian birth number. */\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 yy = String(randomInt(50, 99)).padStart(2, \"0\");\n const ind = String(randomInt(0, 499)).padStart(3, \"0\");\n const c = dd + mm + yy + ind + randomDigits(2);\n if (validate(c).valid) return c;\n }\n};\n\n/** Norwegian Birth Number. */\nconst fodselsnummer: Validator = {\n name: \"Norwegian Birth Number\",\n localName: \"Fødselsnummer\",\n abbreviation: \"Fødselsnr\",\n aliases: [\"fødselsnummer\", \"personnummer\"] as const,\n candidatePattern: \"\\\\d{11}\",\n country: \"NO\",\n entityType: \"person\",\n sourceUrl: \"https://www.skatteetaten.no/\",\n examples: [\"15108695088\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default fodselsnummer;\nexport { compact, format, parse, validate, generate };\n"],"mappings":"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"}
1
+ {"version":3,"file":"fodselsnummer.js","names":[],"sources":["../../src/no/fodselsnummer.ts"],"sourcesContent":["/**\n * Fødselsnummer (Norwegian birth number).\n *\n * 11 digits: DDMMYY + 3-digit individual number + 2\n * check digits. Supports D-numbers (day + 40) and\n * H-numbers (month + 40).\n *\n * @see https://www.skatteetaten.no/\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 WEIGHTS_D1 = [3, 7, 6, 1, 8, 9, 4, 5, 2] as const;\nconst WEIGHTS_D2 = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\nconst getCentury = (\n yy: number,\n individual: number,\n): number | undefined => {\n if (individual >= 0 && individual <= 499) return 1900;\n if (individual >= 500 && individual <= 749 && yy >= 54) {\n return 1800;\n }\n if (individual >= 500 && individual <= 999 && yy < 40) {\n return 2000;\n }\n if (individual >= 900 && individual <= 999 && yy >= 40) {\n return 1900;\n }\n return undefined;\n};\n\nconst checkDigit = (\n v: string,\n weights: readonly number[],\n): number | undefined => {\n let sum = 0;\n for (let i = 0; i < weights.length; i++) {\n sum += Number(v.charAt(i)) * (weights[i] ?? 0);\n }\n const remainder = (11 - (sum % 11)) % 11;\n if (remainder === 10) return undefined;\n return remainder;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 11) {\n return err(\n \"INVALID_LENGTH\",\n \"Norwegian birth number must be 11 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Norwegian birth number must contain only digits\",\n );\n }\n\n // Check digits\n const d1 = checkDigit(v, WEIGHTS_D1);\n if (d1 === undefined || d1 !== Number(v[9])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Norwegian birth number check digit 1 mismatch\",\n );\n }\n const d2 = checkDigit(v, WEIGHTS_D2);\n if (d2 === undefined || d2 !== Number(v[10])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Norwegian birth number check digit 2 mismatch\",\n );\n }\n\n // Parse date components\n let dd = Number(v.slice(0, 2));\n let mm = Number(v.slice(2, 4));\n const yy = Number(v.slice(4, 6));\n const individual = Number(v.slice(6, 9));\n\n // D-number: day > 40\n if (dd > 40) dd -= 40;\n // H-number: month > 40\n if (mm > 40) mm -= 40;\n\n const century = getCentury(yy, individual);\n if (century === undefined) {\n return err(\n \"INVALID_COMPONENT\",\n \"Norwegian birth number has invalid individual/century combination\",\n );\n }\n\n const year = century + yy;\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Norwegian birth number contains an invalid date\",\n );\n }\n\n // Reject future birth dates\n if (new Date(year, mm - 1, dd) > new Date()) {\n return err(\n \"INVALID_COMPONENT\",\n \"Birth date is in the future\",\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 Norwegian\n * birth number. Returns null if the value is not\n * 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 let dd = Number(v.slice(0, 2));\n let mm = Number(v.slice(2, 4));\n const yy = Number(v.slice(4, 6));\n const individual = Number(v.slice(6, 9));\n\n if (dd > 40) dd -= 40;\n if (mm > 40) mm -= 40;\n\n const century = getCentury(yy, individual);\n if (century === undefined) return null;\n\n const year = century + yy;\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n gender: individual % 2 === 0 ? \"female\" : \"male\",\n };\n};\n\n/** Generate a random valid Norwegian birth number. */\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 yy = String(randomInt(50, 99)).padStart(2, \"0\");\n const ind = String(randomInt(0, 499)).padStart(3, \"0\");\n const c = dd + mm + yy + ind + randomDigits(2);\n if (validate(c).valid) return c;\n }\n};\n\n/** Norwegian Birth Number. */\nconst fodselsnummer: Validator<ParsedPersonId> = {\n name: \"Norwegian Birth Number\",\n localName: \"Fødselsnummer\",\n abbreviation: \"Fødselsnr\",\n aliases: [\"fødselsnummer\", \"personnummer\"] as const,\n candidatePattern: \"\\\\d{11}\",\n country: \"NO\",\n entityType: \"person\",\n sourceUrl: \"https://www.skatteetaten.no/\",\n examples: [\"15108695088\"] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default fodselsnummer;\nexport { compact, format, parse, validate, generate };\n"],"mappings":"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"}
package/dist/no/mod.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import orgnr from "./orgnr.js";
3
2
  import mva from "./mva.js";
4
3
  import fodselsnummer from "./fodselsnummer.js";
5
4
 
6
5
  //#region src/no/mod.d.ts
7
-
6
+ declare namespace mod_d_exports {
7
+ export { fodselsnummer, mva, orgnr };
8
+ }
8
9
  //#endregion
9
10
  export { fodselsnummer, mod_d_exports, mva, orgnr };
10
11
  //# sourceMappingURL=mod.d.ts.map
package/dist/no/mva.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/no/mva.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;
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/no/orgnr.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/nz/ird.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/nz/ird.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/nz/ird.js CHANGED
@@ -41,11 +41,11 @@ const SECONDARY_WEIGHTS = [
41
41
  const calcCheckDigit = (payload) => {
42
42
  const padded = payload.padStart(8, "0");
43
43
  let sum = 0;
44
- for (let i = 0; i < 8; i++) sum += PRIMARY_WEIGHTS[i] * Number(padded[i]);
44
+ for (const [i, weight] of PRIMARY_WEIGHTS.entries()) sum += weight * Number(padded[i]);
45
45
  let remainder = (-sum % 11 + 11) % 11;
46
46
  if (remainder !== 10) return remainder;
47
47
  sum = 0;
48
- for (let i = 0; i < 8; i++) sum += SECONDARY_WEIGHTS[i] * Number(padded[i]);
48
+ for (const [i, weight] of SECONDARY_WEIGHTS.entries()) sum += weight * Number(padded[i]);
49
49
  remainder = (-sum % 11 + 11) % 11;
50
50
  if (remainder === 10) return null;
51
51
  return remainder;
@@ -1 +1 @@
1
- {"version":3,"file":"ird.js","names":[],"sources":["../../src/nz/ird.ts"],"sourcesContent":["/**\n * IRD number (New Zealand Inland Revenue Department\n * number, Te Tari Take).\n *\n * 8 or 9 digit number with a weighted checksum.\n * Uses primary weights, falling back to secondary\n * weights if the primary check yields 10.\n *\n * @see https://www.ird.govt.nz/\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 PRIMARY_WEIGHTS = [3, 2, 7, 6, 5, 4, 3, 2] as const;\n\nconst SECONDARY_WEIGHTS = [7, 4, 3, 2, 5, 2, 7, 6] as const;\n\n/**\n * Calculate the check digit for an IRD number.\n * The input should be the number without the check\n * digit, zero-padded to 8 digits.\n */\nconst calcCheckDigit = (payload: string): number | null => {\n const padded = payload.padStart(8, \"0\");\n let sum = 0;\n for (let i = 0; i < 8; i++) {\n sum += PRIMARY_WEIGHTS[i]! * Number(padded[i]);\n }\n let remainder = ((-sum % 11) + 11) % 11;\n if (remainder !== 10) return remainder;\n\n sum = 0;\n for (let i = 0; i < 8; i++) {\n sum += SECONDARY_WEIGHTS[i]! * Number(padded[i]);\n }\n remainder = ((-sum % 11) + 11) % 11;\n if (remainder === 10) return null;\n return remainder;\n};\n\nconst compact = (value: string): string => {\n let v = clean(value, \" -\").toUpperCase();\n if (v.startsWith(\"NZ\")) {\n v = v.slice(2);\n }\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 8 && v.length !== 9) {\n return err(\n \"INVALID_LENGTH\",\n \"IRD number must be 8 or 9 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"IRD number must contain only digits\",\n );\n }\n const num = Number(v);\n if (num < 10_000_000 || num >= 150_000_000) {\n return err(\n \"INVALID_COMPONENT\",\n \"IRD number out of valid range\",\n );\n }\n const payload = v.slice(0, -1);\n const expected = calcCheckDigit(payload);\n if (expected === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"IRD number cannot have a valid check digit\",\n );\n }\n if (expected !== Number(v[v.length - 1])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"IRD 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 const last6 = v.slice(-6);\n const prefix = v.slice(0, -6);\n return `${prefix}-${last6.slice(0, 3)}-${last6.slice(3)}`;\n};\n\n/** Generate a random valid NZ IRD number. */\nconst generate = (): string => {\n for (;;) {\n const c = randomDigits(9);\n if (validate(c).valid) return c;\n }\n};\n\n/** New Zealand IRD Number. */\nconst ird: Validator = {\n name: \"IRD Number\",\n localName: \"IRD Number\",\n abbreviation: \"IRD\",\n aliases: [\"IRD number\", \"tax number NZ\"] as const,\n candidatePattern: \"\\\\d{8,9}\",\n country: \"NZ\",\n entityType: \"any\",\n compact,\n format,\n validate,\n description:\n \"New Zealand Inland Revenue Department number\",\n sourceUrl: \"https://www.ird.govt.nz/\",\n lengths: [8, 9] as const,\n examples: [\"49091850\", \"136410132\"] as const,\n generate,\n};\n\nexport default ird;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAM,kBAAkB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;AAEhD,MAAM,oBAAoB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;;;;;;AAOlD,MAAM,kBAAkB,YAAmC;CACzD,MAAM,SAAS,QAAQ,SAAS,GAAG,IAAI;CACvC,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IACrB,QAAO,gBAAgB,KAAM,OAAO,OAAO,GAAG;CAEhD,IAAI,aAAc,CAAC,MAAM,KAAM,MAAM;AACrC,KAAI,cAAc,GAAI,QAAO;AAE7B,OAAM;AACN,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IACrB,QAAO,kBAAkB,KAAM,OAAO,OAAO,GAAG;AAElD,cAAc,CAAC,MAAM,KAAM,MAAM;AACjC,KAAI,cAAc,GAAI,QAAO;AAC7B,QAAO;;AAGT,MAAM,WAAW,UAA0B;CACzC,IAAI,IAAI,MAAM,OAAO,KAAK,CAAC,aAAa;AACxC,KAAI,EAAE,WAAW,KAAK,CACpB,KAAI,EAAE,MAAM,EAAE;AAEhB,QAAO;;AAGT,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EACjC,QAAO,IACL,kBACA,mCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,sCACD;CAEH,MAAM,MAAM,OAAO,EAAE;AACrB,KAAI,MAAM,OAAc,OAAO,KAC7B,QAAO,IACL,qBACA,gCACD;CAGH,MAAM,WAAW,eADD,EAAE,MAAM,GAAG,GAAG,CACU;AACxC,KAAI,aAAa,KACf,QAAO,IACL,qBACA,6CACD;AAEH,KAAI,aAAa,OAAO,EAAE,EAAE,SAAS,GAAG,CACtC,QAAO,IACL,oBACA,iCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;CACxB,MAAM,QAAQ,EAAE,MAAM,GAAG;AAEzB,QAAO,GADQ,EAAE,MAAM,GAAG,GAAG,CACZ,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,MAAM,EAAE;;;AAIzD,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,IAAI,aAAa,EAAE;AACzB,MAAI,SAAS,EAAE,CAAC,MAAO,QAAO;;;;AAKlC,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,cAAc,gBAAgB;CACxC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ;CACA;CACA;CACA,aACE;CACF,WAAW;CACX,SAAS,CAAC,GAAG,EAAE;CACf,UAAU,CAAC,YAAY,YAAY;CACnC;CACD"}
1
+ {"version":3,"file":"ird.js","names":[],"sources":["../../src/nz/ird.ts"],"sourcesContent":["/**\n * IRD number (New Zealand Inland Revenue Department\n * number, Te Tari Take).\n *\n * 8 or 9 digit number with a weighted checksum.\n * Uses primary weights, falling back to secondary\n * weights if the primary check yields 10.\n *\n * @see https://www.ird.govt.nz/\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 PRIMARY_WEIGHTS = [3, 2, 7, 6, 5, 4, 3, 2] as const;\n\nconst SECONDARY_WEIGHTS = [7, 4, 3, 2, 5, 2, 7, 6] as const;\n\n/**\n * Calculate the check digit for an IRD number.\n * The input should be the number without the check\n * digit, zero-padded to 8 digits.\n */\nconst calcCheckDigit = (payload: string): number | null => {\n const padded = payload.padStart(8, \"0\");\n let sum = 0;\n for (const [i, weight] of PRIMARY_WEIGHTS.entries()) {\n sum += weight * Number(padded[i]);\n }\n let remainder = ((-sum % 11) + 11) % 11;\n if (remainder !== 10) return remainder;\n\n sum = 0;\n for (const [i, weight] of SECONDARY_WEIGHTS.entries()) {\n sum += weight * Number(padded[i]);\n }\n remainder = ((-sum % 11) + 11) % 11;\n if (remainder === 10) return null;\n return remainder;\n};\n\nconst compact = (value: string): string => {\n let v = clean(value, \" -\").toUpperCase();\n if (v.startsWith(\"NZ\")) {\n v = v.slice(2);\n }\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 8 && v.length !== 9) {\n return err(\n \"INVALID_LENGTH\",\n \"IRD number must be 8 or 9 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"IRD number must contain only digits\",\n );\n }\n const num = Number(v);\n if (num < 10_000_000 || num >= 150_000_000) {\n return err(\n \"INVALID_COMPONENT\",\n \"IRD number out of valid range\",\n );\n }\n const payload = v.slice(0, -1);\n const expected = calcCheckDigit(payload);\n if (expected === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"IRD number cannot have a valid check digit\",\n );\n }\n if (expected !== Number(v[v.length - 1])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"IRD 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 const last6 = v.slice(-6);\n const prefix = v.slice(0, -6);\n return `${prefix}-${last6.slice(0, 3)}-${last6.slice(3)}`;\n};\n\n/** Generate a random valid NZ IRD number. */\nconst generate = (): string => {\n for (;;) {\n const c = randomDigits(9);\n if (validate(c).valid) return c;\n }\n};\n\n/** New Zealand IRD Number. */\nconst ird: Validator = {\n name: \"IRD Number\",\n localName: \"IRD Number\",\n abbreviation: \"IRD\",\n aliases: [\"IRD number\", \"tax number NZ\"] as const,\n candidatePattern: \"\\\\d{8,9}\",\n country: \"NZ\",\n entityType: \"any\",\n compact,\n format,\n validate,\n description:\n \"New Zealand Inland Revenue Department number\",\n sourceUrl: \"https://www.ird.govt.nz/\",\n lengths: [8, 9] as const,\n examples: [\"49091850\", \"136410132\"] as const,\n generate,\n};\n\nexport default ird;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAM,kBAAkB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;AAEhD,MAAM,oBAAoB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;;;;;;AAOlD,MAAM,kBAAkB,YAAmC;CACzD,MAAM,SAAS,QAAQ,SAAS,GAAG,IAAI;CACvC,IAAI,MAAM;AACV,MAAK,MAAM,CAAC,GAAG,WAAW,gBAAgB,SAAS,CACjD,QAAO,SAAS,OAAO,OAAO,GAAG;CAEnC,IAAI,aAAc,CAAC,MAAM,KAAM,MAAM;AACrC,KAAI,cAAc,GAAI,QAAO;AAE7B,OAAM;AACN,MAAK,MAAM,CAAC,GAAG,WAAW,kBAAkB,SAAS,CACnD,QAAO,SAAS,OAAO,OAAO,GAAG;AAEnC,cAAc,CAAC,MAAM,KAAM,MAAM;AACjC,KAAI,cAAc,GAAI,QAAO;AAC7B,QAAO;;AAGT,MAAM,WAAW,UAA0B;CACzC,IAAI,IAAI,MAAM,OAAO,KAAK,CAAC,aAAa;AACxC,KAAI,EAAE,WAAW,KAAK,CACpB,KAAI,EAAE,MAAM,EAAE;AAEhB,QAAO;;AAGT,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EACjC,QAAO,IACL,kBACA,mCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,sCACD;CAEH,MAAM,MAAM,OAAO,EAAE;AACrB,KAAI,MAAM,OAAc,OAAO,KAC7B,QAAO,IACL,qBACA,gCACD;CAGH,MAAM,WAAW,eADD,EAAE,MAAM,GAAG,GAAG,CACU;AACxC,KAAI,aAAa,KACf,QAAO,IACL,qBACA,6CACD;AAEH,KAAI,aAAa,OAAO,EAAE,EAAE,SAAS,GAAG,CACtC,QAAO,IACL,oBACA,iCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;CACxB,MAAM,QAAQ,EAAE,MAAM,GAAG;AAEzB,QAAO,GADQ,EAAE,MAAM,GAAG,GAAG,CACZ,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,MAAM,EAAE;;;AAIzD,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,IAAI,aAAa,EAAE;AACzB,MAAI,SAAS,EAAE,CAAC,MAAO,QAAO;;;;AAKlC,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,cAAc,gBAAgB;CACxC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ;CACA;CACA;CACA,aACE;CACF,WAAW;CACX,SAAS,CAAC,GAAG,EAAE;CACf,UAAU,CAAC,YAAY,YAAY;CACnC;CACD"}
package/dist/nz/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import ird from "./ird.js";
3
2
 
4
3
  //#region src/nz/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { ird };
6
+ }
6
7
  //#endregion
7
8
  export { ird, mod_d_exports };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/pa/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import ruc from "./ruc.js";
3
2
 
4
3
  //#region src/pa/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { ruc };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, ruc };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/pa/ruc.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/pa/ruc.d.ts
4
-
5
4
  /**
6
5
  * Compact: normalize Unicode, collapse whitespace,
7
6
  * strip colons around DV, and uppercase. Keeps
package/dist/pa/ruc.js CHANGED
@@ -96,7 +96,8 @@ const digitDV = (buf, isOldFormat) => {
96
96
  */
97
97
  const computeDV = (segments, raw) => {
98
98
  const len = segments.length;
99
- if (len < 3 || len > 4 || len === 4 && segments[1] !== "NT") return null;
99
+ if (len < 3 || len > 4) return null;
100
+ if (len === 4 !== (segments[1] === "NT")) return null;
100
101
  let buf;
101
102
  let isOld = false;
102
103
  const s0 = segments[0];
@@ -175,6 +176,7 @@ const generate = () => {
175
176
  String(randomInt(1, 99999))
176
177
  ];
177
178
  const dv = computeDV(segments, segments.join("-"));
179
+ if (dv === null) throw new Error("Failed to compute Panama RUC DV");
178
180
  return `${segments.join("-")} DV${dv}`;
179
181
  };
180
182
  /** Panama tax identification number. */
@@ -1 +1 @@
1
- {"version":3,"file":"ruc.js","names":[],"sources":["../../src/pa/ruc.ts"],"sourcesContent":["/**\n * RUC (Registro Único de Contribuyente, Panama\n * tax number).\n *\n * The RUC identifies taxpayers in Panama. It comes\n * in several formats depending on entity type\n * (natural person, legal entity, foreigner, etc.)\n * and is always accompanied by a two-digit check\n * code called the \"dígito verificador\" (DV).\n *\n * The DV is computed by encoding the hyphen-separated\n * RUC into a 20-position numeric buffer, then applying\n * a double weighted-sum mod 11 algorithm. The encoding\n * and legacy-format handling follow the official ANIP/DGI\n * specification.\n *\n * @see https://dgi.mef.gob.pa/\n */\n\nimport { clean } from \"#util/clean\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\n/**\n * Legacy cross-reference table: maps two-digit codes\n * at positions 5–6 of old-format juridical RUCs.\n */\nconst LEGACY_MAP: Record<string, string> = {\n \"00\": \"00\",\n \"10\": \"01\",\n \"11\": \"02\",\n \"12\": \"03\",\n \"13\": \"04\",\n \"14\": \"05\",\n \"15\": \"06\",\n \"16\": \"07\",\n \"17\": \"08\",\n \"18\": \"09\",\n \"19\": \"01\",\n \"20\": \"02\",\n \"21\": \"03\",\n \"22\": \"04\",\n \"23\": \"07\",\n \"24\": \"08\",\n \"25\": \"09\",\n \"26\": \"02\",\n \"27\": \"03\",\n \"28\": \"04\",\n \"29\": \"05\",\n \"30\": \"06\",\n \"31\": \"07\",\n \"32\": \"08\",\n \"33\": \"09\",\n \"34\": \"01\",\n \"35\": \"02\",\n \"36\": \"03\",\n \"37\": \"04\",\n \"38\": \"05\",\n \"39\": \"06\",\n \"40\": \"07\",\n \"41\": \"08\",\n \"42\": \"09\",\n \"43\": \"01\",\n \"44\": \"02\",\n \"45\": \"03\",\n \"46\": \"04\",\n \"47\": \"05\",\n \"48\": \"06\",\n \"49\": \"07\",\n};\n\n/** Prepend n leading zeros. */\nconst z = (n: number): string => \"0\".repeat(Math.max(0, n));\n\n/**\n * Compute a single DV digit via weighted sum mod 11.\n * Weights start at 2 and increment, applied\n * right-to-left. For old-format juridical RUCs,\n * weight 12 is skipped (jumps from 11 to 13).\n */\nconst digitDV = (\n buf: string,\n isOldFormat: boolean,\n): number => {\n let weight = 2;\n let sum = 0;\n for (let i = buf.length - 1; i >= 0; i--) {\n if (isOldFormat && weight === 12) {\n weight += 1; // skip 12, continue from 13\n }\n sum += weight * (buf.charCodeAt(i) - 48);\n weight += 1;\n }\n const r = sum % 11;\n return r > 1 ? 11 - r : 0;\n};\n\n/**\n * Encode the RUC segments into a numeric buffer\n * according to entity type, then compute the two\n * DV digits.\n *\n * Returns the two-digit DV string, or null if the\n * format is unrecognized.\n */\nconst computeDV = (\n segments: readonly string[],\n raw: string,\n): string | null => {\n const len = segments.length;\n\n if (\n len < 3 ||\n len > 4 ||\n (len === 4 && segments[1] !== \"NT\")\n ) {\n return null;\n }\n\n let buf: string;\n let isOld = false;\n\n const s0 = segments[0]!;\n const s1 = segments[1]!;\n const s2 = segments[2]!;\n\n if (raw[0] === \"E\") {\n // Foreign person (E-xxx-xxx)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"50\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s1 === \"NT\") {\n // NT designation (xxNT-xxx-xxx)\n const s3 = segments[3]!;\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"43\" +\n z(3 - s2.length) +\n s2 +\n z(5 - s3.length) +\n s3;\n } else if (s0.endsWith(\"AV\")) {\n // AV suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"15\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.endsWith(\"PI\")) {\n // PI suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"79\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0 === \"PE\") {\n // PE prefix (foreign-born)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"75\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (raw[0] === \"N\") {\n // N prefix (naturalized citizen)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"40\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.length >= 1 && s0.length <= 2) {\n // Natural person (province 1–2 digits)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(2 - s0.length) +\n s0 +\n \"00\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else {\n // Juridical (company)\n buf =\n z(10 - s0.length) +\n s0 +\n z(4 - s1.length) +\n s1 +\n z(6 - s2.length) +\n s2;\n isOld =\n buf[3] === \"0\" && buf[4] === \"0\" && buf[5]! < \"5\";\n }\n\n // Apply legacy cross-reference for old format\n if (isOld) {\n const key = buf.slice(5, 7);\n const mapped = LEGACY_MAP[key] ?? key;\n buf = buf.slice(0, 5) + mapped + buf.slice(7);\n }\n\n const dv1 = digitDV(buf, isOld);\n const dv2 = digitDV(\n buf + String.fromCharCode(48 + dv1),\n isOld,\n );\n\n return `${dv1}${dv2}`;\n};\n\n/**\n * Compact: normalize Unicode, collapse whitespace,\n * strip colons around DV, and uppercase. Keeps\n * hyphens (structural separators) and a single\n * space before \"DV\".\n */\nconst compact = (value: string): string => {\n let v = clean(value, \"\").trim().toUpperCase();\n // Normalize DV portion: \" DV: 49\" -> \" DV49\"\n v = v.replace(/\\s*DV[:\\s]*(\\d{2})$/, \" DV$1\");\n // Collapse any remaining internal whitespace\n v = v.replace(/\\s+/g, \" \");\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n // Extract the DV portion: \"...DV49\", \"...DV:49\",\n // or \"... DV 49\"\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n\n if (!dvMatch) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must include DV \" +\n \"(dígito verificador)\",\n );\n }\n\n const rucPart = dvMatch[1]!;\n const dvPart = dvMatch[2]!;\n\n // The RUC part should be hyphen-separated segments\n const segments = rucPart.replace(/-+$/, \"\").split(\"-\");\n\n if (segments.length < 3 || segments.length > 4) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must have 3–4 \" +\n \"hyphen-separated segments\",\n );\n }\n\n if (segments.some((s) => s.length === 0)) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC segments must not be empty\",\n );\n }\n\n const computed = computeDV(segments, rucPart);\n if (computed === null) {\n return err(\n \"INVALID_FORMAT\",\n \"Unrecognized Panama RUC format\",\n );\n }\n\n if (computed !== dvPart) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Panama RUC dígito verificador \" + \"does not match\",\n );\n }\n\n const compactForm = segments.join(\"-\") + \" DV\" + dvPart;\n return { valid: true, compact: compactForm };\n};\n\n/**\n * Format: display as segments with \" DV XX\".\n */\nconst format = (value: string): string => {\n const v = compact(value);\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n if (!dvMatch) return v;\n\n const rucPart = dvMatch[1]!.replace(/-+$/, \"\");\n const dvPart = dvMatch[2]!;\n const segments = rucPart.split(\"-\");\n return `${segments.join(\"-\")} DV ${dvPart}`;\n};\n\n/** Generate a random valid Panama RUC (natural person). */\nconst generate = (): string => {\n const province = String(randomInt(1, 13));\n const volume = String(randomInt(1, 9999));\n const folio = String(randomInt(1, 99999));\n const segments = [province, volume, folio];\n const dv = computeDV(segments, segments.join(\"-\"))!;\n return `${segments.join(\"-\")} DV${dv}`;\n};\n\n/** Panama tax identification number. */\nconst ruc: Validator = {\n name: \"Tax Identification Number\",\n localName: \"Registro Único de Contribuyente\",\n abbreviation: \"RUC\",\n aliases: [\"RUC\"] as const,\n candidatePattern: \"\\\\d{1,2}-?\\\\d{1,4}-?\\\\d{1,6}\",\n country: \"PA\",\n entityType: \"any\",\n description: \"Tax identifier issued by Panama's DGI\",\n sourceUrl: \"https://dgi.mef.gob.pa/\",\n examples: [\n \"1-184-921 DV49\",\n \"2588017-1-831938 DV20\",\n ] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default ruc;\nexport { compact, format, validate, generate };\n"],"mappings":"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"}
1
+ {"version":3,"file":"ruc.js","names":[],"sources":["../../src/pa/ruc.ts"],"sourcesContent":["/**\n * RUC (Registro Único de Contribuyente, Panama\n * tax number).\n *\n * The RUC identifies taxpayers in Panama. It comes\n * in several formats depending on entity type\n * (natural person, legal entity, foreigner, etc.)\n * and is always accompanied by a two-digit check\n * code called the \"dígito verificador\" (DV).\n *\n * The DV is computed by encoding the hyphen-separated\n * RUC into a 20-position numeric buffer, then applying\n * a double weighted-sum mod 11 algorithm. The encoding\n * and legacy-format handling follow the official ANIP/DGI\n * specification.\n *\n * @see https://dgi.mef.gob.pa/\n */\n\nimport { clean } from \"#util/clean\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\n/**\n * Legacy cross-reference table: maps two-digit codes\n * at positions 5–6 of old-format juridical RUCs.\n */\nconst LEGACY_MAP: Record<string, string> = {\n \"00\": \"00\",\n \"10\": \"01\",\n \"11\": \"02\",\n \"12\": \"03\",\n \"13\": \"04\",\n \"14\": \"05\",\n \"15\": \"06\",\n \"16\": \"07\",\n \"17\": \"08\",\n \"18\": \"09\",\n \"19\": \"01\",\n \"20\": \"02\",\n \"21\": \"03\",\n \"22\": \"04\",\n \"23\": \"07\",\n \"24\": \"08\",\n \"25\": \"09\",\n \"26\": \"02\",\n \"27\": \"03\",\n \"28\": \"04\",\n \"29\": \"05\",\n \"30\": \"06\",\n \"31\": \"07\",\n \"32\": \"08\",\n \"33\": \"09\",\n \"34\": \"01\",\n \"35\": \"02\",\n \"36\": \"03\",\n \"37\": \"04\",\n \"38\": \"05\",\n \"39\": \"06\",\n \"40\": \"07\",\n \"41\": \"08\",\n \"42\": \"09\",\n \"43\": \"01\",\n \"44\": \"02\",\n \"45\": \"03\",\n \"46\": \"04\",\n \"47\": \"05\",\n \"48\": \"06\",\n \"49\": \"07\",\n};\n\n/** Prepend n leading zeros. */\nconst z = (n: number): string => \"0\".repeat(Math.max(0, n));\n\n/**\n * Compute a single DV digit via weighted sum mod 11.\n * Weights start at 2 and increment, applied\n * right-to-left. For old-format juridical RUCs,\n * weight 12 is skipped (jumps from 11 to 13).\n */\nconst digitDV = (\n buf: string,\n isOldFormat: boolean,\n): number => {\n let weight = 2;\n let sum = 0;\n for (let i = buf.length - 1; i >= 0; i--) {\n if (isOldFormat && weight === 12) {\n weight += 1; // skip 12, continue from 13\n }\n sum += weight * (buf.charCodeAt(i) - 48);\n weight += 1;\n }\n const r = sum % 11;\n return r > 1 ? 11 - r : 0;\n};\n\n/**\n * Encode the RUC segments into a numeric buffer\n * according to entity type, then compute the two\n * DV digits.\n *\n * Returns the two-digit DV string, or null if the\n * format is unrecognized.\n */\nconst computeDV = (\n segments: readonly string[],\n raw: string,\n): string | null => {\n const len = segments.length;\n\n if (len < 3 || len > 4) {\n return null;\n }\n // The NT designation requires exactly 4 segments\n // (xxNT-NT-xxx-xxx); any other 4-segment shape is\n // unrecognized, and a 3-segment \"NT\" in position 1\n // is malformed.\n if ((len === 4) !== (segments[1] === \"NT\")) {\n return null;\n }\n\n let buf: string;\n let isOld = false;\n\n // SAFETY: length check above ensures 3..4 segments.\n // eslint-disable-next-line no-non-null-assertion\n const s0 = segments[0]!;\n // eslint-disable-next-line no-non-null-assertion\n const s1 = segments[1]!;\n // eslint-disable-next-line no-non-null-assertion\n const s2 = segments[2]!;\n\n if (raw[0] === \"E\") {\n // Foreign person (E-xxx-xxx)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"50\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s1 === \"NT\") {\n // NT designation (xxNT-NT-xxx-xxx)\n // SAFETY: NT branch implies len === 4 (checked above).\n // eslint-disable-next-line no-non-null-assertion\n const s3 = segments[3]!;\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"43\" +\n z(3 - s2.length) +\n s2 +\n z(5 - s3.length) +\n s3;\n } else if (s0.endsWith(\"AV\")) {\n // AV suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"15\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.endsWith(\"PI\")) {\n // PI suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"79\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0 === \"PE\") {\n // PE prefix (foreign-born)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"75\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (raw[0] === \"N\") {\n // N prefix (naturalized citizen)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"40\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.length >= 1 && s0.length <= 2) {\n // Natural person (province 1–2 digits)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(2 - s0.length) +\n s0 +\n \"00\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else {\n // Juridical (company)\n buf =\n z(10 - s0.length) +\n s0 +\n z(4 - s1.length) +\n s1 +\n z(6 - s2.length) +\n s2;\n // SAFETY: juridical branch zero-pads s0 to at least\n // 10 chars (z(10 - s0.length)), so buf[3..6] are\n // always defined.\n isOld =\n buf[3] === \"0\" &&\n buf[4] === \"0\" &&\n // eslint-disable-next-line no-non-null-assertion\n buf[5]! < \"5\";\n }\n\n // Apply legacy cross-reference for old format\n if (isOld) {\n const key = buf.slice(5, 7);\n const mapped = LEGACY_MAP[key] ?? key;\n buf = buf.slice(0, 5) + mapped + buf.slice(7);\n }\n\n const dv1 = digitDV(buf, isOld);\n const dv2 = digitDV(\n buf + String.fromCharCode(48 + dv1),\n isOld,\n );\n\n return `${dv1}${dv2}`;\n};\n\n/**\n * Compact: normalize Unicode, collapse whitespace,\n * strip colons around DV, and uppercase. Keeps\n * hyphens (structural separators) and a single\n * space before \"DV\".\n */\nconst compact = (value: string): string => {\n let v = clean(value, \"\").trim().toUpperCase();\n // Normalize DV portion: \" DV: 49\" -> \" DV49\"\n v = v.replace(/\\s*DV[:\\s]*(\\d{2})$/, \" DV$1\");\n // Collapse any remaining internal whitespace\n v = v.replace(/\\s+/g, \" \");\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n // Extract the DV portion: \"...DV49\", \"...DV:49\",\n // or \"... DV 49\"\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n\n if (!dvMatch) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must include DV \" +\n \"(dígito verificador)\",\n );\n }\n\n // SAFETY: both DV regex capture groups are required.\n // eslint-disable-next-line no-non-null-assertion\n const rucPart = dvMatch[1]!;\n // eslint-disable-next-line no-non-null-assertion\n const dvPart = dvMatch[2]!;\n\n // The RUC part should be hyphen-separated segments\n const segments = rucPart.replace(/-+$/, \"\").split(\"-\");\n\n if (segments.length < 3 || segments.length > 4) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must have 3–4 \" +\n \"hyphen-separated segments\",\n );\n }\n\n if (segments.some((s) => s.length === 0)) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC segments must not be empty\",\n );\n }\n\n const computed = computeDV(segments, rucPart);\n if (computed === null) {\n return err(\n \"INVALID_FORMAT\",\n \"Unrecognized Panama RUC format\",\n );\n }\n\n if (computed !== dvPart) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Panama RUC dígito verificador \" + \"does not match\",\n );\n }\n\n const compactForm = segments.join(\"-\") + \" DV\" + dvPart;\n return { valid: true, compact: compactForm };\n};\n\n/**\n * Format: display as segments with \" DV XX\".\n */\nconst format = (value: string): string => {\n const v = compact(value);\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n if (!dvMatch) return v;\n\n // SAFETY: both DV regex capture groups are required.\n // eslint-disable-next-line no-non-null-assertion\n const rucPart = dvMatch[1]!.replace(/-+$/, \"\");\n // eslint-disable-next-line no-non-null-assertion\n const dvPart = dvMatch[2]!;\n const segments = rucPart.split(\"-\");\n return `${segments.join(\"-\")} DV ${dvPart}`;\n};\n\n/** Generate a random valid Panama RUC (natural person). */\nconst generate = (): string => {\n const province = String(randomInt(1, 13));\n const volume = String(randomInt(1, 9999));\n const folio = String(randomInt(1, 99999));\n const segments = [province, volume, folio];\n const dv = computeDV(segments, segments.join(\"-\"));\n if (dv === null) {\n throw new Error(\"Failed to compute Panama RUC DV\");\n }\n return `${segments.join(\"-\")} DV${dv}`;\n};\n\n/** Panama tax identification number. */\nconst ruc: Validator = {\n name: \"Tax Identification Number\",\n localName: \"Registro Único de Contribuyente\",\n abbreviation: \"RUC\",\n aliases: [\"RUC\"] as const,\n candidatePattern: \"\\\\d{1,2}-?\\\\d{1,4}-?\\\\d{1,6}\",\n country: \"PA\",\n entityType: \"any\",\n description: \"Tax identifier issued by Panama's DGI\",\n sourceUrl: \"https://dgi.mef.gob.pa/\",\n examples: [\n \"1-184-921 DV49\",\n \"2588017-1-831938 DV20\",\n ] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default ruc;\nexport { compact, format, validate, generate };\n"],"mappings":"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"}
@@ -1,11 +1,8 @@
1
1
  import { CountryCode, Validator } from "./types.js";
2
2
 
3
3
  //#region src/patterns.d.ts
4
-
5
4
  type ValidatorPattern = {
6
- /** The validator this pattern was derived from. */
7
- validator: Validator;
8
- /** Loose regex matching candidates in text. */
5
+ /** The validator this pattern was derived from. */validator: Validator; /** Loose regex matching candidates in text. */
9
6
  regex: RegExp;
10
7
  };
11
8
  /**
package/dist/patterns.js CHANGED
@@ -19,8 +19,9 @@ const inferLengths = (v) => {
19
19
  * "25 123 891" → [2, 3, 3].
20
20
  */
21
21
  const inferGroups = (v) => {
22
- if (!v.examples || v.examples.length === 0) return null;
23
- const compact = v.compact(v.examples[0]);
22
+ const first = v.examples?.[0];
23
+ if (first === void 0) return null;
24
+ const compact = v.compact(first);
24
25
  const formatted = v.format(compact);
25
26
  if (formatted === compact) return null;
26
27
  const parts = formatted.split(/[^a-zA-Z0-9]+/);
@@ -34,43 +35,97 @@ const inferGroups = (v) => {
34
35
  * (e.g., "CZ" for DIČ, "CHE" for Swiss UID).
35
36
  */
36
37
  const inferPrefix = (v) => {
37
- if (!v.examples || v.examples.length === 0) return null;
38
- const compact = v.compact(v.examples[0]);
39
- const compactMatch = compact.match(/^([A-Z]+)\d/);
40
- if (compactMatch && v.examples.length > 1) {
41
- const pfx = compactMatch[1];
42
- if (v.examples.every((ex) => v.compact(ex).startsWith(pfx))) return pfx;
43
- } else if (compactMatch && v.examples.length === 1) return compactMatch[1];
44
- const fmtMatch = v.format(compact).match(/^([A-Z]+)[\s\-./]?\d/);
45
- if (fmtMatch && !compact.startsWith(fmtMatch[1])) return fmtMatch[1];
38
+ const examples = v.examples;
39
+ const first = examples?.[0];
40
+ if (!examples || first === void 0) return null;
41
+ const compact = v.compact(first);
42
+ const compactPrefix = compact.match(/^([A-Z]+)\d/)?.[1];
43
+ if (compactPrefix !== void 0 && examples.length > 1) {
44
+ if (examples.every((ex) => v.compact(ex).startsWith(compactPrefix))) return compactPrefix;
45
+ } else if (compactPrefix !== void 0 && examples.length === 1) return compactPrefix;
46
+ const fmtPrefix = v.format(compact).match(/^([A-Z]+)[\s\-./]?\d/)?.[1];
47
+ if (fmtPrefix !== void 0 && !compact.startsWith(fmtPrefix)) return fmtPrefix;
46
48
  return null;
47
49
  };
48
50
  /**
49
- * Determine the character class needed for the
50
- * compact form: `\d` if all digits, `[A-Z0-9]`
51
- * if alphanumeric, etc.
51
+ * Character class for a string fragment:
52
+ * `\d` if all digits, `[A-Z]` if all letters,
53
+ * `[A-Z0-9]` if mixed.
52
54
  */
53
- const inferCharClass = (v) => {
54
- if (!v.examples || v.examples.length === 0) return "\\d";
55
+ const charClassFor = (s) => {
55
56
  let hasLetter = false;
56
57
  let hasDigit = false;
57
- for (const ex of v.examples) {
58
- const c = v.compact(ex);
59
- for (const ch of c) {
60
- if (/[a-zA-Z]/.test(ch)) hasLetter = true;
61
- if (/\d/.test(ch)) hasDigit = true;
62
- }
58
+ for (const ch of s) {
59
+ if (/[a-zA-Z]/.test(ch)) hasLetter = true;
60
+ if (/\d/.test(ch)) hasDigit = true;
63
61
  }
64
62
  if (hasLetter && hasDigit) return "[A-Z0-9]";
65
63
  if (hasLetter) return "[A-Z]";
66
64
  return "\\d";
67
65
  };
68
66
  /**
67
+ * Determine the character class needed for the
68
+ * compact form: `\d` if all digits, `[A-Z0-9]`
69
+ * if alphanumeric, etc.
70
+ */
71
+ const inferCharClass = (v) => {
72
+ if (!v.examples || v.examples.length === 0) return "\\d";
73
+ return charClassFor(v.examples.map((ex) => v.compact(ex)).join(""));
74
+ };
75
+ /**
76
+ * Infer per-group sizes AND character classes from
77
+ * format(). Returns groups with per-position char
78
+ * classes, e.g., "12 010188 M 01 1" →
79
+ * sizes: [2, 6, 1, 2, 1]
80
+ * classes: ["\d", "\d", "[A-Z]", "\d", "\d"]
81
+ *
82
+ * Only returns non-null when per-group classes
83
+ * differ (some groups are letters, some digits).
84
+ * Letter-only groups before the first digit group
85
+ * are treated as prefixes and excluded (handled
86
+ * by inferPrefix separately).
87
+ */
88
+ const inferPerGroup = (v) => {
89
+ const first = v.examples?.[0];
90
+ if (first === void 0) return null;
91
+ const compact = v.compact(first);
92
+ const formatted = v.format(compact);
93
+ if (formatted === compact) return null;
94
+ if (!(/[a-zA-Z]/.test(compact) && /\d/.test(compact))) return null;
95
+ const parts = formatted.split(/[^a-zA-Z0-9]+/);
96
+ if (parts.some((p) => /[a-zA-Z]/.test(p) && /\d/.test(p))) return null;
97
+ const filtered = [];
98
+ let seenDigitGroup = false;
99
+ for (const p of parts) {
100
+ if (p.length === 0) continue;
101
+ if (/\d/.test(p)) {
102
+ seenDigitGroup = true;
103
+ filtered.push({
104
+ size: p.length,
105
+ charClass: charClassFor(p)
106
+ });
107
+ } else if (seenDigitGroup) filtered.push({
108
+ size: p.length,
109
+ charClass: charClassFor(p)
110
+ });
111
+ }
112
+ if (filtered.length <= 1) return null;
113
+ const firstClass = filtered[0]?.charClass;
114
+ if (filtered.every((g) => g.charClass === firstClass)) return null;
115
+ return filtered;
116
+ };
117
+ /**
69
118
  * Build a group regex from group sizes + char class.
70
119
  * [2, 3, 3] with \d → /\d{2}[\s\-.]?\d{3}[\s\-.]?\d{3}/
71
120
  */
72
121
  const groupsToPattern = (groups, cc) => groups.map((g) => `${cc}{${g}}`).join(SEP);
73
122
  /**
123
+ * Build a group regex with per-group char classes.
124
+ * Produces tighter patterns when groups have different
125
+ * character types (e.g., digits vs letters).
126
+ */
127
+ const groupsToPatternPerClass = (groups) => groups.map(({ size, charClass }) => `${charClass}{${size}}`).join(SEP);
128
+ /**
74
129
  * Derive a loose candidate-matching regex from
75
130
  * a validator. The regex finds potential matches;
76
131
  * use `validate()` to confirm.
@@ -84,7 +139,9 @@ const toRegex = (v) => {
84
139
  const lengths = inferLengths(v);
85
140
  const cc = inferCharClass(v);
86
141
  let pattern;
87
- if (groups && lengths.length <= 1) pattern = groupsToPattern(groups, cc);
142
+ const perGroup = inferPerGroup(v);
143
+ if (perGroup && lengths.length <= 1) pattern = groupsToPatternPerClass(perGroup);
144
+ else if (groups && lengths.length <= 1) pattern = groupsToPattern(groups, cc);
88
145
  else if (lengths.length === 1) pattern = `${cc}{${lengths[0]}}`;
89
146
  else if (lengths.length > 1) pattern = `${cc}{${Math.min(...lengths)},${Math.max(...lengths)}}`;
90
147
  else pattern = `${cc}{6,20}`;