@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/cz/ico.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/cz/ico.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/cz/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/cz/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/cz/rc.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ParsedPersonId, ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/cz/rc.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;
@@ -16,7 +15,7 @@ declare const parse: (value: string) => ParsedPersonId | null;
16
15
  */
17
16
  declare const generate: () => string;
18
17
  /** Czech/Slovak Birth Number. */
19
- declare const rc: Validator;
18
+ declare const rc: Validator<ParsedPersonId>;
20
19
  //#endregion
21
20
  export { compact, rc as default, format, generate, parse, validate };
22
21
  //# sourceMappingURL=rc.d.ts.map
package/dist/cz/rc.js CHANGED
@@ -12,14 +12,37 @@ import { isValidDate } from "../_util/date.js";
12
12
  * and gender. 10-digit numbers (post-1954) must
13
13
  * be divisible by 11.
14
14
  *
15
- * Women have 50 added to the month. Since 2004,
16
- * an extra +20 offset is used when the serial
17
- * range is exhausted.
15
+ * Women have 50 added to the month. For modern
16
+ * 10-digit numbers, an additional +20 offset may
17
+ * be used when the daily serial range is exhausted
18
+ * (+70 for women).
19
+ *
20
+ * Canonical source:
21
+ * - Czech Ministry of the Interior guidance on rodne
22
+ * cislo, with the modern overflow-month rules
23
+ *
24
+ * We use this to keep the 9-digit historical form
25
+ * valid while rejecting modern +20/+70 month offsets
26
+ * on 9-digit numbers. Those overflow offsets are a
27
+ * modern rule for 10-digit values.
18
28
  *
19
29
  * @see https://www.mvcr.cz/mvcren/docDetail.aspx?docid=21975362&doctype=ART
20
30
  * @see Law 133/2000 Sb.
21
31
  */
22
32
  const compact = (value) => clean(value, " /");
33
+ const decodeMonth = (rawMonth, year, length) => {
34
+ const candidates = length === 10 && year >= 2004 ? [
35
+ 0,
36
+ 50,
37
+ 20,
38
+ 70
39
+ ] : [0, 50];
40
+ for (const offset of candidates) {
41
+ const month = rawMonth - offset;
42
+ if (month >= 1 && month <= 12) return month;
43
+ }
44
+ return null;
45
+ };
23
46
  const validate = (value) => {
24
47
  const v = compact(value);
25
48
  if (v.length !== 9 && v.length !== 10) return err("INVALID_LENGTH", "Birth number must be 9 or 10 digits");
@@ -27,14 +50,14 @@ const validate = (value) => {
27
50
  const yy = Number(v.slice(0, 2));
28
51
  const mm = Number(v.slice(2, 4));
29
52
  const dd = Number(v.slice(4, 6));
30
- const month = mm % 50 % 20 || mm % 50;
31
- if (month < 1 || month > 12) return err("INVALID_COMPONENT", "Birth number contains an invalid month");
32
- if (dd < 1 || dd > 31) return err("INVALID_COMPONENT", "Birth number contains an invalid day");
33
53
  let year = yy + 1900;
34
54
  if (v.length === 9) {
35
55
  if (year >= 1980) year -= 100;
36
56
  if (year > 1953) return err("INVALID_COMPONENT", "9-digit birth numbers are pre-1954 only");
37
57
  } else if (year < 1954) year += 100;
58
+ const month = decodeMonth(mm, year, v.length);
59
+ if (month === null) return err("INVALID_COMPONENT", "Birth number contains an invalid month");
60
+ if (dd < 1 || dd > 31) return err("INVALID_COMPONENT", "Birth number contains an invalid day");
38
61
  if (!isValidDate(year, month, dd)) return err("INVALID_COMPONENT", "Birth number contains an invalid date");
39
62
  if (v.length === 10) {
40
63
  const check = Number(v.slice(0, 9)) % 11 % 10;
@@ -60,11 +83,12 @@ const parse = (value) => {
60
83
  const yy = Number(v.slice(0, 2));
61
84
  const mm = Number(v.slice(2, 4));
62
85
  const dd = Number(v.slice(4, 6));
63
- const gender = mm > 50 ? "female" : "male";
64
- const month = mm % 50 % 20 || mm % 50;
86
+ const gender = mm >= 50 ? "female" : "male";
65
87
  let year = yy + 1900;
66
88
  if (v.length === 10 && year < 1954) year += 100;
67
89
  if (v.length === 9 && year >= 1980) year -= 100;
90
+ const month = decodeMonth(mm, year, v.length);
91
+ if (month === null) return null;
68
92
  return {
69
93
  birthDate: new Date(year, month - 1, dd),
70
94
  gender
@@ -107,6 +131,7 @@ const rc = {
107
131
  examples: ["7103192745"],
108
132
  compact,
109
133
  format,
134
+ parse,
110
135
  validate,
111
136
  generate
112
137
  };
package/dist/cz/rc.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"rc.js","names":[],"sources":["../../src/cz/rc.ts"],"sourcesContent":["/**\n * RČ (Rodné číslo).\n *\n * Czech and Slovak national identifier (birth\n * number). 9 or 10 digits encoding date of birth\n * and gender. 10-digit numbers (post-1954) must\n * be divisible by 11.\n *\n * Women have 50 added to the month. Since 2004,\n * an extra +20 offset is used when the serial\n * range is exhausted.\n *\n * @see https://www.mvcr.cz/mvcren/docDetail.aspx?docid=21975362&doctype=ART\n * @see Law 133/2000 Sb.\n */\n\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 compact = (value: string): string =>\n clean(value, \" /\");\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 9 && v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"Birth number must be 9 or 10 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Birth number must contain only digits\",\n );\n }\n\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n\n // Strip gender (+50) and overflow (+20) offsets\n const month = (mm % 50) % 20 || mm % 50;\n if (month < 1 || month > 12) {\n return err(\n \"INVALID_COMPONENT\",\n \"Birth number contains an invalid month\",\n );\n }\n\n if (dd < 1 || dd > 31) {\n return err(\n \"INVALID_COMPONENT\",\n \"Birth number contains an invalid day\",\n );\n }\n\n // Resolve full year\n let year = yy + 1900;\n if (v.length === 9) {\n // 9-digit: pre-1954 only\n if (year >= 1980) year -= 100;\n if (year > 1953) {\n return err(\n \"INVALID_COMPONENT\",\n \"9-digit birth numbers are pre-1954 only\",\n );\n }\n } else {\n // 10-digit: if year < 1954, it's 2000s\n if (year < 1954) year += 100;\n }\n\n // Validate the actual date\n if (!isValidDate(year, month, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Birth number contains an invalid date\",\n );\n }\n\n // 10-digit: check digit is first 9 digits\n // mod 11 mod 10\n if (v.length === 10) {\n const front = Number(v.slice(0, 9));\n const check = (front % 11) % 10;\n if (String(check) !== v[9]) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Birth number is not divisible by 11\",\n );\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 birth number.\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 yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n\n const gender = mm > 50 ? \"female\" : \"male\";\n const month = (mm % 50) % 20 || mm % 50;\n\n let year = yy + 1900;\n if (v.length === 10 && year < 1954) year += 100;\n if (v.length === 9 && year >= 1980) year -= 100;\n\n return {\n birthDate: new Date(year, month - 1, dd),\n gender,\n };\n};\n\n/**\n * Generate a random valid 10-digit birth number.\n * Uses a random date between 1954 and 2024.\n */\nconst generate = (): string => {\n const year = randomInt(1954, 2024);\n const month = randomInt(1, 12);\n const day = randomInt(1, 28);\n const isFemale = Math.random() < 0.5;\n const mm = isFemale ? month + 50 : month;\n\n const yy = String(year % 100).padStart(2, \"0\");\n const mmStr = String(mm).padStart(2, \"0\");\n const dd = String(day).padStart(2, \"0\");\n\n const datePart = `${yy}${mmStr}${dd}`;\n for (let seq = 0; seq < 1000; seq++) {\n const seqStr = String(seq).padStart(3, \"0\");\n const front = `${datePart}${seqStr}`;\n const num = Number(front);\n const check = (num % 11) % 10;\n const result = `${front}${String(check)}`;\n if (validate(result).valid) {\n return result;\n }\n }\n\n throw new Error(\"Failed to generate valid birth number\");\n};\n\n/** Czech/Slovak Birth Number. */\nconst rc: Validator = {\n name: \"Czech Birth Number\",\n localName: \"Rodné číslo\",\n abbreviation: \"RČ\",\n aliases: [\"rodné číslo\", \"RČ\", \"birth number\"] as const,\n candidatePattern: \"\\\\d{6}/\\\\d{3,4}\",\n country: \"CZ\",\n entityType: \"person\",\n description:\n \"Czech/Slovak national identifier encoding birth date and gender\",\n sourceUrl:\n \"https://www.mvcr.cz/mvcren/docDetail.aspx?docid=21975362&doctype=ART\",\n lengths: [9, 10] as const,\n examples: [\"7103192745\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default rc;\nexport { compact, format, generate, parse, validate };\n"],"mappings":"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"}
1
+ {"version":3,"file":"rc.js","names":[],"sources":["../../src/cz/rc.ts"],"sourcesContent":["/**\n * RČ (Rodné číslo).\n *\n * Czech and Slovak national identifier (birth\n * number). 9 or 10 digits encoding date of birth\n * and gender. 10-digit numbers (post-1954) must\n * be divisible by 11.\n *\n * Women have 50 added to the month. For modern\n * 10-digit numbers, an additional +20 offset may\n * be used when the daily serial range is exhausted\n * (+70 for women).\n *\n * Canonical source:\n * - Czech Ministry of the Interior guidance on rodne\n * cislo, with the modern overflow-month rules\n *\n * We use this to keep the 9-digit historical form\n * valid while rejecting modern +20/+70 month offsets\n * on 9-digit numbers. Those overflow offsets are a\n * modern rule for 10-digit values.\n *\n * @see https://www.mvcr.cz/mvcren/docDetail.aspx?docid=21975362&doctype=ART\n * @see Law 133/2000 Sb.\n */\n\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 compact = (value: string): string =>\n clean(value, \" /\");\n\nconst decodeMonth = (\n rawMonth: number,\n year: number,\n length: number,\n): number | null => {\n const candidates =\n length === 10 && year >= 2004\n ? [0, 50, 20, 70]\n : [0, 50];\n for (const offset of candidates) {\n const month = rawMonth - offset;\n if (month >= 1 && month <= 12) {\n return month;\n }\n }\n return null;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 9 && v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"Birth number must be 9 or 10 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Birth number must contain only digits\",\n );\n }\n\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n\n // Resolve full year\n let year = yy + 1900;\n if (v.length === 9) {\n // 9-digit: pre-1954 only\n if (year >= 1980) year -= 100;\n if (year > 1953) {\n return err(\n \"INVALID_COMPONENT\",\n \"9-digit birth numbers are pre-1954 only\",\n );\n }\n } else {\n // 10-digit: if year < 1954, it's 2000s\n if (year < 1954) year += 100;\n }\n\n const month = decodeMonth(mm, year, v.length);\n if (month === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"Birth number contains an invalid month\",\n );\n }\n\n if (dd < 1 || dd > 31) {\n return err(\n \"INVALID_COMPONENT\",\n \"Birth number contains an invalid day\",\n );\n }\n\n // Validate the actual date\n if (!isValidDate(year, month, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Birth number contains an invalid date\",\n );\n }\n\n // 10-digit: check digit is first 9 digits\n // mod 11 mod 10\n if (v.length === 10) {\n const front = Number(v.slice(0, 9));\n const check = (front % 11) % 10;\n if (String(check) !== v[9]) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Birth number is not divisible by 11\",\n );\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 birth number.\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 yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n\n const gender = mm >= 50 ? \"female\" : \"male\";\n let year = yy + 1900;\n if (v.length === 10 && year < 1954) year += 100;\n if (v.length === 9 && year >= 1980) year -= 100;\n\n const month = decodeMonth(mm, year, v.length);\n if (month === null) {\n return null;\n }\n\n return {\n birthDate: new Date(year, month - 1, dd),\n gender,\n };\n};\n\n/**\n * Generate a random valid 10-digit birth number.\n * Uses a random date between 1954 and 2024.\n */\nconst generate = (): string => {\n const year = randomInt(1954, 2024);\n const month = randomInt(1, 12);\n const day = randomInt(1, 28);\n const isFemale = Math.random() < 0.5;\n const mm = isFemale ? month + 50 : month;\n\n const yy = String(year % 100).padStart(2, \"0\");\n const mmStr = String(mm).padStart(2, \"0\");\n const dd = String(day).padStart(2, \"0\");\n\n const datePart = `${yy}${mmStr}${dd}`;\n for (let seq = 0; seq < 1000; seq++) {\n const seqStr = String(seq).padStart(3, \"0\");\n const front = `${datePart}${seqStr}`;\n const num = Number(front);\n const check = (num % 11) % 10;\n const result = `${front}${String(check)}`;\n if (validate(result).valid) {\n return result;\n }\n }\n\n throw new Error(\"Failed to generate valid birth number\");\n};\n\n/** Czech/Slovak Birth Number. */\nconst rc: Validator<ParsedPersonId> = {\n name: \"Czech Birth Number\",\n localName: \"Rodné číslo\",\n abbreviation: \"RČ\",\n aliases: [\"rodné číslo\", \"RČ\", \"birth number\"] as const,\n candidatePattern: \"\\\\d{6}/\\\\d{3,4}\",\n country: \"CZ\",\n entityType: \"person\",\n description:\n \"Czech/Slovak national identifier encoding birth date and gender\",\n sourceUrl:\n \"https://www.mvcr.cz/mvcren/docDetail.aspx?docid=21975362&doctype=ART\",\n lengths: [9, 10] as const,\n examples: [\"7103192745\"] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default rc;\nexport { compact, format, generate, parse, validate };\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  import { clean } from "../_util/clean.js";
2
- import { randomDigits, randomInt } from "../_util/generate.js";
2
+ import { randomDigits, randomPick } from "../_util/generate.js";
3
3
  import { err } from "../_util/result.js";
4
4
  //#region src/de/handelsreg.ts
5
5
  /**
@@ -19,16 +19,16 @@ import { err } from "../_util/result.js";
19
19
  *
20
20
  * @see https://de.wikipedia.org/wiki/Handelsregister_(Deutschland)
21
21
  */
22
+ const GENERATE_TYPES = [
23
+ "HRA",
24
+ "HRB",
25
+ "GNR",
26
+ "PR",
27
+ "VR"
28
+ ];
22
29
  /** Generate a random valid Handelsregisternummer. */
23
30
  const generate = () => {
24
- const types = [
25
- "HRA",
26
- "HRB",
27
- "GNR",
28
- "PR",
29
- "VR"
30
- ];
31
- return `${types[randomInt(0, types.length - 1)]} ${randomDigits(5)}`;
31
+ return `${randomPick(GENERATE_TYPES)} ${randomDigits(5)}`;
32
32
  };
33
33
  const REGISTER_TYPES = new Set([
34
34
  "HRA",
@@ -1 +1 @@
1
- {"version":3,"file":"handelsreg.js","names":[],"sources":["../../src/de/handelsreg.ts"],"sourcesContent":["/** Generate a random valid Handelsregisternummer. */\nconst generate = (): string => {\n const types = [\"HRA\", \"HRB\", \"GNR\", \"PR\", \"VR\"];\n const type = types[randomInt(0, types.length - 1)]!;\n const number = randomDigits(5);\n return `${type} ${number}`;\n};\n\n/**\n * Handelsregisternummer (German Company Register Number).\n *\n * German commercial register number assigned by the\n * local court (Amtsgericht). Format: register type\n * (HRA, HRB, GnR, PR, VR) + number.\n *\n * HRA = Handelsregister Abteilung A (partnerships,\n * sole proprietors). HRB = Abteilung B (corporations).\n * GnR = Genossenschaftsregister.\n * PR = Partnerschaftsregister.\n * VR = Vereinsregister.\n *\n * No check digit; validation is structural only.\n *\n * @see https://de.wikipedia.org/wiki/Handelsregister_(Deutschland)\n */\n\nimport { clean } from \"#util/clean\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst REGISTER_TYPES = new Set([\n \"HRA\",\n \"HRB\",\n \"GNR\",\n \"PR\",\n \"VR\",\n]);\n\n/**\n * Strip separators and normalize to canonical form.\n * Accepts \"HRB 12345\", \"HRB12345\", \"hrb 12345\".\n */\nconst compact = (value: string): string => {\n const v = clean(value, \" -.\").trim().toUpperCase();\n const match = v.match(/^(HRA|HRB|GNR|PR|VR)(\\d{1,7})$/);\n if (!match) return v;\n return `${match[1]} ${match[2]}`;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = clean(value, \" -.\").trim().toUpperCase();\n\n const match = v.match(/^([A-Z]{2,3})(\\d{1,7})$/);\n if (!match) {\n return err(\n \"INVALID_FORMAT\",\n \"Handelsregisternummer must be a register type \" +\n \"(HRA, HRB, GnR, PR, VR) followed by \" +\n \"1-7 digits\",\n );\n }\n\n const type = match[1]!;\n const number = match[2]!;\n\n if (!REGISTER_TYPES.has(type)) {\n return err(\n \"INVALID_COMPONENT\",\n `Unrecognised register type \"${type}\"`,\n );\n }\n\n return {\n valid: true,\n compact: `${type} ${number}`,\n };\n};\n\nconst format = (value: string): string => compact(value);\n\n/** German Company Register Number. */\nconst handelsreg: Validator = {\n name: \"German Company Register Number\",\n localName: \"Handelsregisternummer\",\n abbreviation: \"HReg\",\n aliases: [\n \"Handelsregisternummer\",\n \"Handelsregister\",\n \"HRB\",\n \"HRA\",\n ] as const,\n candidatePattern: \"(?:HRA|HRB|GnR|PR|VR)\\\\s*\\\\d{1,7}\",\n country: \"DE\",\n entityType: \"company\",\n sourceUrl:\n \"https://de.wikipedia.org/wiki/Handelsregister_(Deutschland)\",\n examples: [\"HRB 12345\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default handelsreg;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;AACA,MAAM,iBAAyB;CAC7B,MAAM,QAAQ;EAAC;EAAO;EAAO;EAAO;EAAM;EAAK;AAG/C,QAAO,GAFM,MAAM,UAAU,GAAG,MAAM,SAAS,EAAE,EAElC,GADA,aAAa,EAAE;;AA4BhC,MAAM,iBAAiB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;AAMF,MAAM,WAAW,UAA0B;CACzC,MAAM,IAAI,MAAM,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa;CAClD,MAAM,QAAQ,EAAE,MAAM,iCAAiC;AACvD,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO,GAAG,MAAM,GAAG,GAAG,MAAM;;AAG9B,MAAM,YAAY,UAAkC;CAGlD,MAAM,QAFI,MAAM,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,CAElC,MAAM,0BAA0B;AAChD,KAAI,CAAC,MACH,QAAO,IACL,kBACA,+FAGD;CAGH,MAAM,OAAO,MAAM;CACnB,MAAM,SAAS,MAAM;AAErB,KAAI,CAAC,eAAe,IAAI,KAAK,CAC3B,QAAO,IACL,qBACA,+BAA+B,KAAK,GACrC;AAGH,QAAO;EACL,OAAO;EACP,SAAS,GAAG,KAAK,GAAG;EACrB;;AAGH,MAAM,UAAU,UAA0B,QAAQ,MAAM;;AAGxD,MAAM,aAAwB;CAC5B,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EACP;EACA;EACA;EACA;EACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WACE;CACF,UAAU,CAAC,YAAY;CACvB;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"handelsreg.js","names":[],"sources":["../../src/de/handelsreg.ts"],"sourcesContent":["const GENERATE_TYPES = [\n \"HRA\",\n \"HRB\",\n \"GNR\",\n \"PR\",\n \"VR\",\n] as const;\n\n/** Generate a random valid Handelsregisternummer. */\nconst generate = (): string => {\n const type = randomPick(GENERATE_TYPES);\n const number = randomDigits(5);\n return `${type} ${number}`;\n};\n\n/**\n * Handelsregisternummer (German Company Register Number).\n *\n * German commercial register number assigned by the\n * local court (Amtsgericht). Format: register type\n * (HRA, HRB, GnR, PR, VR) + number.\n *\n * HRA = Handelsregister Abteilung A (partnerships,\n * sole proprietors). HRB = Abteilung B (corporations).\n * GnR = Genossenschaftsregister.\n * PR = Partnerschaftsregister.\n * VR = Vereinsregister.\n *\n * No check digit; validation is structural only.\n *\n * @see https://de.wikipedia.org/wiki/Handelsregister_(Deutschland)\n */\n\nimport { clean } from \"#util/clean\";\nimport { randomDigits, randomPick } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst REGISTER_TYPES = new Set([\n \"HRA\",\n \"HRB\",\n \"GNR\",\n \"PR\",\n \"VR\",\n]);\n\n/**\n * Strip separators and normalize to canonical form.\n * Accepts \"HRB 12345\", \"HRB12345\", \"hrb 12345\".\n */\nconst compact = (value: string): string => {\n const v = clean(value, \" -.\").trim().toUpperCase();\n const match = v.match(/^(HRA|HRB|GNR|PR|VR)(\\d{1,7})$/);\n if (!match) return v;\n return `${match[1]} ${match[2]}`;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = clean(value, \" -.\").trim().toUpperCase();\n\n const match = v.match(/^([A-Z]{2,3})(\\d{1,7})$/);\n if (!match) {\n return err(\n \"INVALID_FORMAT\",\n \"Handelsregisternummer must be a register type \" +\n \"(HRA, HRB, GnR, PR, VR) followed by \" +\n \"1-7 digits\",\n );\n }\n\n // SAFETY: both capture groups are required, so a\n // successful match always populates [1] and [2].\n // eslint-disable-next-line no-non-null-assertion\n const type = match[1]!;\n // eslint-disable-next-line no-non-null-assertion\n const number = match[2]!;\n\n if (!REGISTER_TYPES.has(type)) {\n return err(\n \"INVALID_COMPONENT\",\n `Unrecognised register type \"${type}\"`,\n );\n }\n\n return {\n valid: true,\n compact: `${type} ${number}`,\n };\n};\n\nconst format = (value: string): string => compact(value);\n\n/** German Company Register Number. */\nconst handelsreg: Validator = {\n name: \"German Company Register Number\",\n localName: \"Handelsregisternummer\",\n abbreviation: \"HReg\",\n aliases: [\n \"Handelsregisternummer\",\n \"Handelsregister\",\n \"HRB\",\n \"HRA\",\n ] as const,\n candidatePattern: \"(?:HRA|HRB|GnR|PR|VR)\\\\s*\\\\d{1,7}\",\n country: \"DE\",\n entityType: \"company\",\n sourceUrl:\n \"https://de.wikipedia.org/wiki/Handelsregister_(Deutschland)\",\n examples: [\"HRB 12345\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default handelsreg;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;AAAA,MAAM,iBAAiB;CACrB;CACA;CACA;CACA;CACA;CACD;;AAGD,MAAM,iBAAyB;AAG7B,QAAO,GAFM,WAAW,eAAe,CAExB,GADA,aAAa,EAAE;;AA4BhC,MAAM,iBAAiB,IAAI,IAAI;CAC7B;CACA;CACA;CACA;CACA;CACD,CAAC;;;;;AAMF,MAAM,WAAW,UAA0B;CACzC,MAAM,IAAI,MAAM,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa;CAClD,MAAM,QAAQ,EAAE,MAAM,iCAAiC;AACvD,KAAI,CAAC,MAAO,QAAO;AACnB,QAAO,GAAG,MAAM,GAAG,GAAG,MAAM;;AAG9B,MAAM,YAAY,UAAkC;CAGlD,MAAM,QAFI,MAAM,OAAO,MAAM,CAAC,MAAM,CAAC,aAAa,CAElC,MAAM,0BAA0B;AAChD,KAAI,CAAC,MACH,QAAO,IACL,kBACA,+FAGD;CAMH,MAAM,OAAO,MAAM;CAEnB,MAAM,SAAS,MAAM;AAErB,KAAI,CAAC,eAAe,IAAI,KAAK,CAC3B,QAAO,IACL,qBACA,+BAA+B,KAAK,GACrC;AAGH,QAAO;EACL,OAAO;EACP,SAAS,GAAG,KAAK,GAAG;EACrB;;AAGH,MAAM,UAAU,UAA0B,QAAQ,MAAM;;AAGxD,MAAM,aAAwB;CAC5B,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EACP;EACA;EACA;EACA;EACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WACE;CACF,UAAU,CAAC,YAAY;CACvB;CACA;CACA;CACA;CACD"}
package/dist/de/idnr.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/de/idnr.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 +1 @@
1
- {"version":3,"file":"idnr.js","names":[],"sources":["../../src/de/idnr.ts"],"sourcesContent":["/**\n * IdNr (Steuerliche Identifikationsnummer).\n *\n * German personal tax identification number.\n * 11 digits; the last digit is a check digit\n * (ISO 7064 Mod 11,10). Exactly one digit\n * appears twice or three times; the rest are\n * unique. The first digit cannot be 0.\n *\n * @see https://www.bzst.de/DE/Privatpersonen/SteuerlicheIdentifikationsnummer/steuerlicheidentifikationsnummer_node.html\n */\n\nimport {\n mod1110validate,\n mod1110checkDigit,\n} from \"#checksums/mod1110\";\nimport { clean } from \"#util/clean\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -/\");\n\n/**\n * Validate digit distribution: in the first 10\n * digits, exactly one digit appears 2 or 3\n * times; all others appear exactly once.\n */\nconst validDistribution = (value: string): boolean => {\n const counts = Array.from<number>({ length: 10 }).fill(0);\n for (let i = 0; i < 10; i++) {\n const idx = Number(value.charAt(i));\n const prev = counts[idx];\n if (prev !== undefined) counts[idx] = prev + 1;\n }\n let doubles = 0;\n let triples = 0;\n for (const c of counts) {\n if (c === 2) doubles++;\n if (c === 3) triples++;\n if (c > 3) return false;\n }\n // Exactly one digit repeated (2 or 3 times)\n return (\n (doubles === 1 && triples === 0) ||\n (doubles === 0 && triples === 1)\n );\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 11) {\n return err(\n \"INVALID_LENGTH\",\n \"German IdNr must be 11 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"German IdNr must contain only digits\",\n );\n }\n if (v[0] === \"0\") {\n return err(\n \"INVALID_COMPONENT\",\n \"German IdNr cannot start with 0\",\n );\n }\n if (!validDistribution(v)) {\n return err(\n \"INVALID_COMPONENT\",\n \"German IdNr digit distribution invalid\",\n );\n }\n if (!mod1110validate(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"German IdNr check digit mismatch\",\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, 2)} ${v.slice(2, 5)} ${v.slice(5, 8)} ${v.slice(8)}`;\n};\n\n/** Generate a random valid German IdNr. */\nconst generate = (): string => {\n for (;;) {\n const repeated = randomInt(0, 9);\n const times = Math.random() < 0.8 ? 2 : 3;\n const pool: number[] = [];\n for (let d = 0; d < 10; d++) {\n const count = d === repeated ? times : 1;\n for (let t = 0; t < count; t++) pool.push(d);\n }\n while (pool.length < 10) pool.push(randomInt(0, 9));\n for (let i = pool.length - 1; i > 0; i--) {\n const j = randomInt(0, i);\n [pool[i], pool[j]] = [pool[j]!, pool[i]!];\n }\n if (pool[0] === 0) continue;\n const payload = pool.slice(0, 10).join(\"\");\n if (!validDistribution(payload)) continue;\n const check = mod1110checkDigit(payload);\n const c = payload + String(check);\n if (validate(c).valid) return c;\n }\n};\n\n/** German Personal Tax ID. */\nconst idnr: Validator = {\n name: \"German Tax ID\",\n localName: \"Steuerliche Identifikationsnummer\",\n abbreviation: \"IdNr\",\n aliases: [\n \"Steuerliche Identifikationsnummer\",\n \"IdNr\",\n \"Steuer-ID\",\n ] as const,\n candidatePattern: \"\\\\d{2}\\\\s?\\\\d{3}\\\\s?\\\\d{3}\\\\s?\\\\d{3}\",\n country: \"DE\",\n entityType: \"person\",\n sourceUrl:\n \"https://www.bzst.de/DE/Privatpersonen/SteuerlicheIdentifikationsnummer/steuerlicheidentifikationsnummer_node.html\",\n examples: [\"36574261809\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default idnr;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;AAuBA,MAAM,WAAW,UACf,MAAM,OAAO,MAAM;;;;;;AAOrB,MAAM,qBAAqB,UAA2B;CACpD,MAAM,SAAS,MAAM,KAAa,EAAE,QAAQ,IAAI,CAAC,CAAC,KAAK,EAAE;AACzD,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;EAC3B,MAAM,MAAM,OAAO,MAAM,OAAO,EAAE,CAAC;EACnC,MAAM,OAAO,OAAO;AACpB,MAAI,SAAS,KAAA,EAAW,QAAO,OAAO,OAAO;;CAE/C,IAAI,UAAU;CACd,IAAI,UAAU;AACd,MAAK,MAAM,KAAK,QAAQ;AACtB,MAAI,MAAM,EAAG;AACb,MAAI,MAAM,EAAG;AACb,MAAI,IAAI,EAAG,QAAO;;AAGpB,QACG,YAAY,KAAK,YAAY,KAC7B,YAAY,KAAK,YAAY;;AAIlC,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,gCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,uCACD;AAEH,KAAI,EAAE,OAAO,IACX,QAAO,IACL,qBACA,kCACD;AAEH,KAAI,CAAC,kBAAkB,EAAE,CACvB,QAAO,IACL,qBACA,yCACD;AAEH,KAAI,CAAC,gBAAgB,EAAE,CACrB,QAAO,IACL,oBACA,mCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE;;;AAIzE,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,WAAW,UAAU,GAAG,EAAE;EAChC,MAAM,QAAQ,KAAK,QAAQ,GAAG,KAAM,IAAI;EACxC,MAAM,OAAiB,EAAE;AACzB,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;GAC3B,MAAM,QAAQ,MAAM,WAAW,QAAQ;AACvC,QAAK,IAAI,IAAI,GAAG,IAAI,OAAO,IAAK,MAAK,KAAK,EAAE;;AAE9C,SAAO,KAAK,SAAS,GAAI,MAAK,KAAK,UAAU,GAAG,EAAE,CAAC;AACnD,OAAK,IAAI,IAAI,KAAK,SAAS,GAAG,IAAI,GAAG,KAAK;GACxC,MAAM,IAAI,UAAU,GAAG,EAAE;AACzB,IAAC,KAAK,IAAI,KAAK,MAAM,CAAC,KAAK,IAAK,KAAK,GAAI;;AAE3C,MAAI,KAAK,OAAO,EAAG;EACnB,MAAM,UAAU,KAAK,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG;AAC1C,MAAI,CAAC,kBAAkB,QAAQ,CAAE;EACjC,MAAM,QAAQ,kBAAkB,QAAQ;EACxC,MAAM,IAAI,UAAU,OAAO,MAAM;AACjC,MAAI,SAAS,EAAE,CAAC,MAAO,QAAO;;;;AAKlC,MAAM,OAAkB;CACtB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EACP;EACA;EACA;EACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WACE;CACF,UAAU,CAAC,cAAc;CACzB;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"idnr.js","names":[],"sources":["../../src/de/idnr.ts"],"sourcesContent":["/**\n * IdNr (Steuerliche Identifikationsnummer).\n *\n * German personal tax identification number.\n * 11 digits; the last digit is a check digit\n * (ISO 7064 Mod 11,10). Exactly one digit\n * appears twice or three times; the rest are\n * unique. The first digit cannot be 0.\n *\n * @see https://www.bzst.de/DE/Privatpersonen/SteuerlicheIdentifikationsnummer/steuerlicheidentifikationsnummer_node.html\n */\n\nimport {\n mod1110validate,\n mod1110checkDigit,\n} from \"#checksums/mod1110\";\nimport { clean } from \"#util/clean\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -/\");\n\n/**\n * Validate digit distribution: in the first 10\n * digits, exactly one digit appears 2 or 3\n * times; all others appear exactly once.\n */\nconst validDistribution = (value: string): boolean => {\n const counts = Array.from<number>({ length: 10 }).fill(0);\n for (let i = 0; i < 10; i++) {\n const idx = Number(value.charAt(i));\n const prev = counts[idx];\n if (prev !== undefined) counts[idx] = prev + 1;\n }\n let doubles = 0;\n let triples = 0;\n for (const c of counts) {\n if (c === 2) doubles++;\n if (c === 3) triples++;\n if (c > 3) return false;\n }\n // Exactly one digit repeated (2 or 3 times)\n return (\n (doubles === 1 && triples === 0) ||\n (doubles === 0 && triples === 1)\n );\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 11) {\n return err(\n \"INVALID_LENGTH\",\n \"German IdNr must be 11 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"German IdNr must contain only digits\",\n );\n }\n if (v[0] === \"0\") {\n return err(\n \"INVALID_COMPONENT\",\n \"German IdNr cannot start with 0\",\n );\n }\n if (!validDistribution(v)) {\n return err(\n \"INVALID_COMPONENT\",\n \"German IdNr digit distribution invalid\",\n );\n }\n if (!mod1110validate(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"German IdNr check digit mismatch\",\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, 2)} ${v.slice(2, 5)} ${v.slice(5, 8)} ${v.slice(8)}`;\n};\n\n/** Generate a random valid German IdNr. */\nconst generate = (): string => {\n for (;;) {\n const repeated = randomInt(0, 9);\n const times = Math.random() < 0.8 ? 2 : 3;\n const pool: number[] = [];\n for (let d = 0; d < 10; d++) {\n const count = d === repeated ? times : 1;\n for (let t = 0; t < count; t++) pool.push(d);\n }\n while (pool.length < 10) pool.push(randomInt(0, 9));\n for (let i = pool.length - 1; i > 0; i--) {\n const j = randomInt(0, i);\n // SAFETY: i and j are valid indices of `pool`\n // (i > 0, j ∈ [0, i]).\n // eslint-disable-next-line no-non-null-assertion\n [pool[i], pool[j]] = [pool[j]!, pool[i]!];\n }\n if (pool[0] === 0) continue;\n const payload = pool.slice(0, 10).join(\"\");\n if (!validDistribution(payload)) continue;\n const check = mod1110checkDigit(payload);\n const c = payload + String(check);\n if (validate(c).valid) return c;\n }\n};\n\n/** German Personal Tax ID. */\nconst idnr: Validator = {\n name: \"German Tax ID\",\n localName: \"Steuerliche Identifikationsnummer\",\n abbreviation: \"IdNr\",\n aliases: [\n \"Steuerliche Identifikationsnummer\",\n \"IdNr\",\n \"Steuer-ID\",\n ] as const,\n candidatePattern: \"\\\\d{2}\\\\s?\\\\d{3}\\\\s?\\\\d{3}\\\\s?\\\\d{3}\",\n country: \"DE\",\n entityType: \"person\",\n sourceUrl:\n \"https://www.bzst.de/DE/Privatpersonen/SteuerlicheIdentifikationsnummer/steuerlicheidentifikationsnummer_node.html\",\n examples: [\"36574261809\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default idnr;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;AAuBA,MAAM,WAAW,UACf,MAAM,OAAO,MAAM;;;;;;AAOrB,MAAM,qBAAqB,UAA2B;CACpD,MAAM,SAAS,MAAM,KAAa,EAAE,QAAQ,IAAI,CAAC,CAAC,KAAK,EAAE;AACzD,MAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;EAC3B,MAAM,MAAM,OAAO,MAAM,OAAO,EAAE,CAAC;EACnC,MAAM,OAAO,OAAO;AACpB,MAAI,SAAS,KAAA,EAAW,QAAO,OAAO,OAAO;;CAE/C,IAAI,UAAU;CACd,IAAI,UAAU;AACd,MAAK,MAAM,KAAK,QAAQ;AACtB,MAAI,MAAM,EAAG;AACb,MAAI,MAAM,EAAG;AACb,MAAI,IAAI,EAAG,QAAO;;AAGpB,QACG,YAAY,KAAK,YAAY,KAC7B,YAAY,KAAK,YAAY;;AAIlC,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,gCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,uCACD;AAEH,KAAI,EAAE,OAAO,IACX,QAAO,IACL,qBACA,kCACD;AAEH,KAAI,CAAC,kBAAkB,EAAE,CACvB,QAAO,IACL,qBACA,yCACD;AAEH,KAAI,CAAC,gBAAgB,EAAE,CACrB,QAAO,IACL,oBACA,mCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE;;;AAIzE,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,WAAW,UAAU,GAAG,EAAE;EAChC,MAAM,QAAQ,KAAK,QAAQ,GAAG,KAAM,IAAI;EACxC,MAAM,OAAiB,EAAE;AACzB,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,KAAK;GAC3B,MAAM,QAAQ,MAAM,WAAW,QAAQ;AACvC,QAAK,IAAI,IAAI,GAAG,IAAI,OAAO,IAAK,MAAK,KAAK,EAAE;;AAE9C,SAAO,KAAK,SAAS,GAAI,MAAK,KAAK,UAAU,GAAG,EAAE,CAAC;AACnD,OAAK,IAAI,IAAI,KAAK,SAAS,GAAG,IAAI,GAAG,KAAK;GACxC,MAAM,IAAI,UAAU,GAAG,EAAE;AAIzB,IAAC,KAAK,IAAI,KAAK,MAAM,CAAC,KAAK,IAAK,KAAK,GAAI;;AAE3C,MAAI,KAAK,OAAO,EAAG;EACnB,MAAM,UAAU,KAAK,MAAM,GAAG,GAAG,CAAC,KAAK,GAAG;AAC1C,MAAI,CAAC,kBAAkB,QAAQ,CAAE;EACjC,MAAM,QAAQ,kBAAkB,QAAQ;EACxC,MAAM,IAAI,UAAU,OAAO,MAAM;AACjC,MAAI,SAAS,EAAE,CAAC,MAAO,QAAO;;;;AAKlC,MAAM,OAAkB;CACtB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EACP;EACA;EACA;EACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WACE;CACF,UAAU,CAAC,cAAc;CACzB;CACA;CACA;CACA;CACD"}
package/dist/de/mod.d.ts CHANGED
@@ -1,4 +1,3 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import handelsreg from "./handelsreg.js";
3
2
  import idnr from "./idnr.js";
4
3
  import stnr from "./stnr.js";
@@ -6,7 +5,9 @@ import svnr from "./svnr.js";
6
5
  import vat from "./vat.js";
7
6
 
8
7
  //#region src/de/mod.d.ts
9
-
8
+ declare namespace mod_d_exports {
9
+ export { handelsreg, idnr, stnr, svnr, vat };
10
+ }
10
11
  //#endregion
11
12
  export { handelsreg, idnr, mod_d_exports, stnr, svnr, vat };
12
13
  //# sourceMappingURL=mod.d.ts.map
package/dist/de/stnr.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/de/stnr.d.ts
4
-
5
4
  /**
6
5
  * Generate a random valid Steuernummer.
7
6
  * Uses Berlin regional format (FFBBBUUUUP, 10 digits).
@@ -1 +1 @@
1
- {"version":3,"file":"stnr.js","names":[],"sources":["../../src/de/stnr.ts"],"sourcesContent":["/**\n * Generate a random valid Steuernummer.\n * Uses Berlin regional format (FFBBBUUUUP, 10 digits).\n */\nconst generate = (): string => {\n const ff = randomDigits(2);\n const bbb = randomDigits(3);\n const uuuu = randomDigits(4);\n const p = randomDigits(1);\n return `${ff}${bbb}${uuuu}${p}`;\n};\n\n/**\n * StNr (Steuernummer).\n *\n * German tax number assigned by the local tax office.\n * Regional format: 10 or 11 digits. Unified federal\n * format: 13 digits (prefixed with a 2–4 digit BUFA\n * code). Structure varies by federal state.\n *\n * The number encodes: BUFA (Bundesfinanzamtsnummer),\n * district code, serial number, and check digit. No\n * algorithmic check digit validation is standardised\n * across all states; validation is format-only.\n *\n * @see https://de.wikipedia.org/wiki/Steuernummer\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/**\n * Format patterns per federal state.\n * [0] = regional format, [1] = unified (federal) format.\n *\n * Legend: F = finance office, B = district,\n * U = serial, P = check digit,\n * leading digits are literal.\n */\nconst STATES = {\n \"Baden-Württemberg\": [\"FFBBBUUUUP\", \"28FF0BBBUUUUP\"],\n Bayern: [\"FFFBBBUUUUP\", \"9FFF0BBBUUUUP\"],\n Berlin: [\"FFBBBUUUUP\", \"11FF0BBBUUUUP\"],\n Brandenburg: [\"0FFBBBUUUUP\", \"30FF0BBBUUUUP\"],\n Bremen: [\"FFBBBUUUUP\", \"24FF0BBBUUUUP\"],\n Hamburg: [\"FFBBBUUUUP\", \"22FF0BBBUUUUP\"],\n Hessen: [\"0FFBBBUUUUP\", \"26FF0BBBUUUUP\"],\n \"Mecklenburg-Vorpommern\": [\n \"0FFBBBUUUUP\",\n \"40FF0BBBUUUUP\",\n ],\n Niedersachsen: [\"FFBBBUUUUP\", \"23FF0BBBUUUUP\"],\n \"Nordrhein-Westfalen\": [\"FFFBBBBUUUP\", \"5FFF0BBBBUUUP\"],\n \"Rheinland-Pfalz\": [\"FFBBBUUUUP\", \"27FF0BBBUUUUP\"],\n Saarland: [\"0FFBBBUUUUP\", \"10FF0BBBUUUUP\"],\n Sachsen: [\"2FFBBBUUUUP\", \"32FF0BBBUUUUP\"],\n \"Sachsen-Anhalt\": [\"1FFBBBUUUUP\", \"31FF0BBBUUUUP\"],\n \"Schleswig-Holstein\": [\"FFBBBUUUUP\", \"21FF0BBBUUUUP\"],\n Thüringen: [\"1FFBBBUUUUP\", \"41FF0BBBUUUUP\"],\n} as const;\n\ntype StateName = keyof typeof STATES;\n\n/**\n * Convert a format pattern to a regex.\n * Literal digits match themselves; F/B/U match any\n * digit; P (check digit) matches any digit.\n */\nconst patternToRegex = (pattern: string): RegExp => {\n let re = \"^\";\n for (const ch of pattern) {\n if (ch >= \"0\" && ch <= \"9\") {\n re += ch;\n } else {\n re += \"\\\\d\";\n }\n }\n re += \"$\";\n return new RegExp(re);\n};\n\n/** Pre-compiled regexes for each state + format. */\nconst COMPILED: Array<{\n state: StateName;\n regional: RegExp;\n regionalFmt: string;\n federal: RegExp;\n federalFmt: string;\n}> = (\n Object.entries(STATES) as Array<\n [StateName, readonly [string, string]]\n >\n).map(([state, [regionalFmt, federalFmt]]) => ({\n state,\n regional: patternToRegex(regionalFmt),\n regionalFmt,\n federal: patternToRegex(federalFmt),\n federalFmt,\n}));\n\nconst compact = (value: string): string =>\n clean(value, \" -/.\");\n\nconst matchesAnyPattern = (v: string): boolean => {\n for (const { regional, federal } of COMPILED) {\n if (regional.test(v) || federal.test(v)) return true;\n }\n return false;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"German Steuernummer must contain only digits\",\n );\n }\n\n if (\n v.length !== 10 &&\n v.length !== 11 &&\n v.length !== 13\n ) {\n return err(\n \"INVALID_LENGTH\",\n \"German Steuernummer must be 10, 11, or 13 digits\",\n );\n }\n\n if (!matchesAnyPattern(v)) {\n return err(\n \"INVALID_COMPONENT\",\n \"German Steuernummer does not match any \" +\n \"known state format\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\n/**\n * Format with slashes at segment boundaries.\n * Uses the first matching state pattern to determine\n * segment positions.\n */\nconst format = (value: string): string => {\n const v = compact(value);\n\n for (const entry of COMPILED) {\n const pattern =\n v.length === 13 ? entry.federal : entry.regional;\n if (!pattern.test(v)) continue;\n\n const fmt =\n v.length === 13\n ? entry.federalFmt\n : entry.regionalFmt;\n\n // Group by consecutive runs of the same letter\n // category: literal digits, F, B, U, P\n const segments: string[] = [];\n let segStart = 0;\n let prevCat = charCat(fmt[0]!);\n\n for (let i = 1; i < fmt.length; i++) {\n const cat = charCat(fmt[i]!);\n if (cat !== prevCat) {\n segments.push(v.slice(segStart, i));\n segStart = i;\n prevCat = cat;\n }\n }\n segments.push(v.slice(segStart));\n\n return segments.join(\"/\");\n }\n\n return v;\n};\n\nconst charCat = (ch: string): string => {\n if (ch >= \"0\" && ch <= \"9\") return \"L\"; // literal\n return ch; // F, B, U, P\n};\n\n/** German Tax Number (Steuernummer). */\nconst stnr: Validator = {\n name: \"German Tax Number\",\n localName: \"Steuernummer\",\n abbreviation: \"StNr\",\n aliases: [\"Steuernummer\", \"St.Nr.\"] as const,\n candidatePattern: \"\\\\d{2,4}/\\\\d{3,4}/\\\\d{4,5}\",\n country: \"DE\",\n entityType: \"any\",\n description:\n \"German tax number assigned by the local \" +\n \"tax office, varies by federal state\",\n sourceUrl: \"https://de.wikipedia.org/wiki/Steuernummer\",\n lengths: [10, 11, 13] as const,\n examples: [\"2181508150\", \"18181508155\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default stnr;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;AAIA,MAAM,iBAAyB;AAK7B,QAAO,GAJI,aAAa,EAAE,GACd,aAAa,EAAE,GACd,aAAa,EAAE,GAClB,aAAa,EAAE;;;;;;;;;;AAmC3B,MAAM,SAAS;CACb,qBAAqB,CAAC,cAAc,gBAAgB;CACpD,QAAQ,CAAC,eAAe,gBAAgB;CACxC,QAAQ,CAAC,cAAc,gBAAgB;CACvC,aAAa,CAAC,eAAe,gBAAgB;CAC7C,QAAQ,CAAC,cAAc,gBAAgB;CACvC,SAAS,CAAC,cAAc,gBAAgB;CACxC,QAAQ,CAAC,eAAe,gBAAgB;CACxC,0BAA0B,CACxB,eACA,gBACD;CACD,eAAe,CAAC,cAAc,gBAAgB;CAC9C,uBAAuB,CAAC,eAAe,gBAAgB;CACvD,mBAAmB,CAAC,cAAc,gBAAgB;CAClD,UAAU,CAAC,eAAe,gBAAgB;CAC1C,SAAS,CAAC,eAAe,gBAAgB;CACzC,kBAAkB,CAAC,eAAe,gBAAgB;CAClD,sBAAsB,CAAC,cAAc,gBAAgB;CACrD,WAAW,CAAC,eAAe,gBAAgB;CAC5C;;;;;;AASD,MAAM,kBAAkB,YAA4B;CAClD,IAAI,KAAK;AACT,MAAK,MAAM,MAAM,QACf,KAAI,MAAM,OAAO,MAAM,IACrB,OAAM;KAEN,OAAM;AAGV,OAAM;AACN,QAAO,IAAI,OAAO,GAAG;;;AAIvB,MAAM,WAOJ,OAAO,QAAQ,OAAO,CAGtB,KAAK,CAAC,OAAO,CAAC,aAAa,kBAAkB;CAC7C;CACA,UAAU,eAAe,YAAY;CACrC;CACA,SAAS,eAAe,WAAW;CACnC;CACD,EAAE;AAEH,MAAM,WAAW,UACf,MAAM,OAAO,OAAO;AAEtB,MAAM,qBAAqB,MAAuB;AAChD,MAAK,MAAM,EAAE,UAAU,aAAa,SAClC,KAAI,SAAS,KAAK,EAAE,IAAI,QAAQ,KAAK,EAAE,CAAE,QAAO;AAElD,QAAO;;AAGT,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AAExB,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,+CACD;AAGH,KACE,EAAE,WAAW,MACb,EAAE,WAAW,MACb,EAAE,WAAW,GAEb,QAAO,IACL,kBACA,mDACD;AAGH,KAAI,CAAC,kBAAkB,EAAE,CACvB,QAAO,IACL,qBACA,4DAED;AAGH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;;;;;;AAQpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AAExB,MAAK,MAAM,SAAS,UAAU;AAG5B,MAAI,EADF,EAAE,WAAW,KAAK,MAAM,UAAU,MAAM,UAC7B,KAAK,EAAE,CAAE;EAEtB,MAAM,MACJ,EAAE,WAAW,KACT,MAAM,aACN,MAAM;EAIZ,MAAM,WAAqB,EAAE;EAC7B,IAAI,WAAW;EACf,IAAI,UAAU,QAAQ,IAAI,GAAI;AAE9B,OAAK,IAAI,IAAI,GAAG,IAAI,IAAI,QAAQ,KAAK;GACnC,MAAM,MAAM,QAAQ,IAAI,GAAI;AAC5B,OAAI,QAAQ,SAAS;AACnB,aAAS,KAAK,EAAE,MAAM,UAAU,EAAE,CAAC;AACnC,eAAW;AACX,cAAU;;;AAGd,WAAS,KAAK,EAAE,MAAM,SAAS,CAAC;AAEhC,SAAO,SAAS,KAAK,IAAI;;AAG3B,QAAO;;AAGT,MAAM,WAAW,OAAuB;AACtC,KAAI,MAAM,OAAO,MAAM,IAAK,QAAO;AACnC,QAAO;;;AAIT,MAAM,OAAkB;CACtB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,gBAAgB,SAAS;CACnC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,aACE;CAEF,WAAW;CACX,SAAS;EAAC;EAAI;EAAI;EAAG;CACrB,UAAU,CAAC,cAAc,cAAc;CACvC;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"stnr.js","names":[],"sources":["../../src/de/stnr.ts"],"sourcesContent":["/**\n * Generate a random valid Steuernummer.\n * Uses Berlin regional format (FFBBBUUUUP, 10 digits).\n */\nconst generate = (): string => {\n const ff = randomDigits(2);\n const bbb = randomDigits(3);\n const uuuu = randomDigits(4);\n const p = randomDigits(1);\n return `${ff}${bbb}${uuuu}${p}`;\n};\n\n/**\n * StNr (Steuernummer).\n *\n * German tax number assigned by the local tax office.\n * Regional format: 10 or 11 digits. Unified federal\n * format: 13 digits (prefixed with a 2–4 digit BUFA\n * code). Structure varies by federal state.\n *\n * The number encodes: BUFA (Bundesfinanzamtsnummer),\n * district code, serial number, and check digit. No\n * algorithmic check digit validation is standardised\n * across all states; validation is format-only.\n *\n * @see https://de.wikipedia.org/wiki/Steuernummer\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/**\n * Format patterns per federal state.\n * [0] = regional format, [1] = unified (federal) format.\n *\n * Legend: F = finance office, B = district,\n * U = serial, P = check digit,\n * leading digits are literal.\n */\nconst STATES = {\n \"Baden-Württemberg\": [\"FFBBBUUUUP\", \"28FF0BBBUUUUP\"],\n Bayern: [\"FFFBBBUUUUP\", \"9FFF0BBBUUUUP\"],\n Berlin: [\"FFBBBUUUUP\", \"11FF0BBBUUUUP\"],\n Brandenburg: [\"0FFBBBUUUUP\", \"30FF0BBBUUUUP\"],\n Bremen: [\"FFBBBUUUUP\", \"24FF0BBBUUUUP\"],\n Hamburg: [\"FFBBBUUUUP\", \"22FF0BBBUUUUP\"],\n Hessen: [\"0FFBBBUUUUP\", \"26FF0BBBUUUUP\"],\n \"Mecklenburg-Vorpommern\": [\n \"0FFBBBUUUUP\",\n \"40FF0BBBUUUUP\",\n ],\n Niedersachsen: [\"FFBBBUUUUP\", \"23FF0BBBUUUUP\"],\n \"Nordrhein-Westfalen\": [\"FFFBBBBUUUP\", \"5FFF0BBBBUUUP\"],\n \"Rheinland-Pfalz\": [\"FFBBBUUUUP\", \"27FF0BBBUUUUP\"],\n Saarland: [\"0FFBBBUUUUP\", \"10FF0BBBUUUUP\"],\n Sachsen: [\"2FFBBBUUUUP\", \"32FF0BBBUUUUP\"],\n \"Sachsen-Anhalt\": [\"1FFBBBUUUUP\", \"31FF0BBBUUUUP\"],\n \"Schleswig-Holstein\": [\"FFBBBUUUUP\", \"21FF0BBBUUUUP\"],\n Thüringen: [\"1FFBBBUUUUP\", \"41FF0BBBUUUUP\"],\n} as const;\n\ntype StateName = keyof typeof STATES;\n\n/**\n * Convert a format pattern to a regex.\n * Literal digits match themselves; F/B/U match any\n * digit; P (check digit) matches any digit.\n */\nconst patternToRegex = (pattern: string): RegExp => {\n let re = \"^\";\n for (const ch of pattern) {\n if (ch >= \"0\" && ch <= \"9\") {\n re += ch;\n } else {\n re += \"\\\\d\";\n }\n }\n re += \"$\";\n return new RegExp(re);\n};\n\n/** Pre-compiled regexes for each state + format. */\nconst COMPILED: Array<{\n state: StateName;\n regional: RegExp;\n regionalFmt: string;\n federal: RegExp;\n federalFmt: string;\n}> = (\n Object.entries(STATES) as Array<\n [StateName, readonly [string, string]]\n >\n).map(([state, [regionalFmt, federalFmt]]) => ({\n state,\n regional: patternToRegex(regionalFmt),\n regionalFmt,\n federal: patternToRegex(federalFmt),\n federalFmt,\n}));\n\nconst compact = (value: string): string =>\n clean(value, \" -/.\");\n\nconst matchesAnyPattern = (v: string): boolean => {\n for (const { regional, federal } of COMPILED) {\n if (regional.test(v) || federal.test(v)) return true;\n }\n return false;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"German Steuernummer must contain only digits\",\n );\n }\n\n if (\n v.length !== 10 &&\n v.length !== 11 &&\n v.length !== 13\n ) {\n return err(\n \"INVALID_LENGTH\",\n \"German Steuernummer must be 10, 11, or 13 digits\",\n );\n }\n\n if (!matchesAnyPattern(v)) {\n return err(\n \"INVALID_COMPONENT\",\n \"German Steuernummer does not match any \" +\n \"known state format\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\n/**\n * Format with slashes at segment boundaries.\n * Uses the first matching state pattern to determine\n * segment positions.\n */\nconst format = (value: string): string => {\n const v = compact(value);\n\n for (const entry of COMPILED) {\n const pattern =\n v.length === 13 ? entry.federal : entry.regional;\n if (!pattern.test(v)) continue;\n\n const fmt =\n v.length === 13\n ? entry.federalFmt\n : entry.regionalFmt;\n\n // Group by consecutive runs of the same letter\n // category: literal digits, F, B, U, P\n const segments: string[] = [];\n let segStart = 0;\n // SAFETY: fmt is a non-empty literal from COMPILED.\n // eslint-disable-next-line no-non-null-assertion\n let prevCat = charCat(fmt[0]!);\n\n for (let i = 1; i < fmt.length; i++) {\n // SAFETY: i < fmt.length.\n // eslint-disable-next-line no-non-null-assertion\n const cat = charCat(fmt[i]!);\n if (cat !== prevCat) {\n segments.push(v.slice(segStart, i));\n segStart = i;\n prevCat = cat;\n }\n }\n segments.push(v.slice(segStart));\n\n return segments.join(\"/\");\n }\n\n return v;\n};\n\nconst charCat = (ch: string): string => {\n if (ch >= \"0\" && ch <= \"9\") return \"L\"; // literal\n return ch; // F, B, U, P\n};\n\n/** German Tax Number (Steuernummer). */\nconst stnr: Validator = {\n name: \"German Tax Number\",\n localName: \"Steuernummer\",\n abbreviation: \"StNr\",\n aliases: [\"Steuernummer\", \"St.Nr.\"] as const,\n candidatePattern: \"\\\\d{2,4}/\\\\d{3,4}/\\\\d{4,5}\",\n country: \"DE\",\n entityType: \"any\",\n description:\n \"German tax number assigned by the local \" +\n \"tax office, varies by federal state\",\n sourceUrl: \"https://de.wikipedia.org/wiki/Steuernummer\",\n lengths: [10, 11, 13] as const,\n examples: [\"2181508150\", \"18181508155\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default stnr;\nexport { compact, format, validate, generate };\n"],"mappings":"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"}
package/dist/de/svnr.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/de/svnr.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/de/svnr.js CHANGED
@@ -42,8 +42,8 @@ const computeCheck = (v) => {
42
42
  digits.push(Number(v[9]));
43
43
  digits.push(Number(v[10]));
44
44
  let sum = 0;
45
- for (let i = 0; i < 12; i++) {
46
- const product = digits[i] * WEIGHTS[i];
45
+ for (const [i, weight] of WEIGHTS.entries()) {
46
+ const product = digits[i] * weight;
47
47
  if (product >= 10) sum += Math.floor(product / 10) + product % 10;
48
48
  else sum += product;
49
49
  }
@@ -1 +1 @@
1
- {"version":3,"file":"svnr.js","names":[],"sources":["../../src/de/svnr.ts"],"sourcesContent":["/**\n * SVNR (Sozialversicherungsnummer).\n *\n * German social insurance number. 12 characters:\n * area number (2 digits) + date of birth (DDMMYY)\n * + initial letter of birth name (1 letter) +\n * serial number (2 digits) + check digit (1 digit).\n *\n * @see https://de.wikipedia.org/wiki/Sozialversicherungsnummer\n */\n\nimport { clean } from \"#util/clean\";\nimport {\n isValidDate,\n resolveTwoDigitYear,\n} from \"#util/date\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst letterValue = (ch: string): number => {\n const code = ch.charCodeAt(0);\n if (code >= 65 && code <= 90) return code - 64;\n return -1;\n};\n\nconst WEIGHTS = [\n 2, 1, 2, 5, 7, 1, 2, 1, 2, 1, 2, 1,\n] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -/\").toUpperCase();\n\nconst computeCheck = (v: string): number => {\n const digits: number[] = [];\n\n for (let i = 0; i < 8; i++) {\n digits.push(Number(v[i]));\n }\n\n const lv = letterValue(v[8]!);\n digits.push(Math.floor(lv / 10));\n digits.push(lv % 10);\n\n digits.push(Number(v[9]));\n digits.push(Number(v[10]));\n\n let sum = 0;\n for (let i = 0; i < 12; i++) {\n const product = digits[i]! * WEIGHTS[i]!;\n if (product >= 10) {\n sum += Math.floor(product / 10) + (product % 10);\n } else {\n sum += product;\n }\n }\n\n return sum % 10;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"German SVNR must be 12 characters\",\n );\n }\n\n if (!/^\\d{8}[A-Z]\\d{3}$/.test(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"German SVNR must be 8 digits, 1 letter, \" +\n \"3 digits\",\n );\n }\n\n const dd = Number(v.slice(2, 4));\n const mm = Number(v.slice(4, 6));\n const yy = Number(v.slice(6, 8));\n\n const year = resolveTwoDigitYear(yy);\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"German SVNR contains an invalid birth date\",\n );\n }\n\n const expected = computeCheck(v);\n const actual = Number(v[11]);\n\n if (expected !== actual) {\n return err(\n \"INVALID_CHECKSUM\",\n \"German SVNR check digit mismatch\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return (\n `${v.slice(0, 2)} ${v.slice(2, 8)}` +\n ` ${v.slice(8, 9)} ${v.slice(9, 11)}` +\n ` ${v.slice(11)}`\n );\n};\n\n/** Generate a random valid German SVNR. */\nconst generate = (): string => {\n for (;;) {\n const area = String(randomInt(1, 99)).padStart(2, \"0\");\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(40, 99)).padStart(2, \"0\");\n const letter = String.fromCharCode(\n 65 + randomInt(0, 25),\n );\n const serial = String(randomInt(0, 49)).padStart(\n 2,\n \"0\",\n );\n const partial = area + dd + mm + yy + letter + serial;\n const c = partial + String(computeCheck(partial));\n if (validate(c).valid) return c;\n }\n};\n\n/** German Social Insurance Number. */\nconst svnr: Validator = {\n name: \"German Social Insurance Number\",\n localName: \"Sozialversicherungsnummer\",\n abbreviation: \"SVNR\",\n aliases: [\n \"Sozialversicherungsnummer\",\n \"SVNR\",\n \"Versicherungsnummer\",\n ] as const,\n candidatePattern: \"\\\\d{2}\\\\s?\\\\d{6}\\\\s?[A-Z]\\\\s?\\\\d{3}\",\n country: \"DE\",\n entityType: \"person\",\n description:\n \"German social insurance number encoding \" +\n \"birth date and name initial\",\n sourceUrl:\n \"https://de.wikipedia.org/wiki/\" +\n \"Sozialversicherungsnummer\",\n lengths: [12] as const,\n examples: [\"12010188M011\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default svnr;\nexport { compact, format, validate, generate };\n"],"mappings":"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"}
1
+ {"version":3,"file":"svnr.js","names":[],"sources":["../../src/de/svnr.ts"],"sourcesContent":["/**\n * SVNR (Sozialversicherungsnummer).\n *\n * German social insurance number. 12 characters:\n * area number (2 digits) + date of birth (DDMMYY)\n * + initial letter of birth name (1 letter) +\n * serial number (2 digits) + check digit (1 digit).\n *\n * @see https://de.wikipedia.org/wiki/Sozialversicherungsnummer\n */\n\nimport { clean } from \"#util/clean\";\nimport {\n isValidDate,\n resolveTwoDigitYear,\n} from \"#util/date\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst letterValue = (ch: string): number => {\n const code = ch.charCodeAt(0);\n if (code >= 65 && code <= 90) return code - 64;\n return -1;\n};\n\nconst WEIGHTS = [\n 2, 1, 2, 5, 7, 1, 2, 1, 2, 1, 2, 1,\n] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -/\").toUpperCase();\n\nconst computeCheck = (v: string): number => {\n const digits: number[] = [];\n\n for (let i = 0; i < 8; i++) {\n digits.push(Number(v[i]));\n }\n\n // SAFETY: caller guarantees v.length === 12.\n // eslint-disable-next-line no-non-null-assertion\n const lv = letterValue(v[8]!);\n digits.push(Math.floor(lv / 10));\n digits.push(lv % 10);\n\n digits.push(Number(v[9]));\n digits.push(Number(v[10]));\n\n let sum = 0;\n for (const [i, weight] of WEIGHTS.entries()) {\n // SAFETY: WEIGHTS.length === 12 and we just pushed\n // 12 entries onto `digits`.\n // eslint-disable-next-line no-non-null-assertion\n const product = digits[i]! * weight;\n if (product >= 10) {\n sum += Math.floor(product / 10) + (product % 10);\n } else {\n sum += product;\n }\n }\n\n return sum % 10;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"German SVNR must be 12 characters\",\n );\n }\n\n if (!/^\\d{8}[A-Z]\\d{3}$/.test(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"German SVNR must be 8 digits, 1 letter, \" +\n \"3 digits\",\n );\n }\n\n const dd = Number(v.slice(2, 4));\n const mm = Number(v.slice(4, 6));\n const yy = Number(v.slice(6, 8));\n\n const year = resolveTwoDigitYear(yy);\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"German SVNR contains an invalid birth date\",\n );\n }\n\n const expected = computeCheck(v);\n const actual = Number(v[11]);\n\n if (expected !== actual) {\n return err(\n \"INVALID_CHECKSUM\",\n \"German SVNR check digit mismatch\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return (\n `${v.slice(0, 2)} ${v.slice(2, 8)}` +\n ` ${v.slice(8, 9)} ${v.slice(9, 11)}` +\n ` ${v.slice(11)}`\n );\n};\n\n/** Generate a random valid German SVNR. */\nconst generate = (): string => {\n for (;;) {\n const area = String(randomInt(1, 99)).padStart(2, \"0\");\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(40, 99)).padStart(2, \"0\");\n const letter = String.fromCharCode(\n 65 + randomInt(0, 25),\n );\n const serial = String(randomInt(0, 49)).padStart(\n 2,\n \"0\",\n );\n const partial = area + dd + mm + yy + letter + serial;\n const c = partial + String(computeCheck(partial));\n if (validate(c).valid) return c;\n }\n};\n\n/** German Social Insurance Number. */\nconst svnr: Validator = {\n name: \"German Social Insurance Number\",\n localName: \"Sozialversicherungsnummer\",\n abbreviation: \"SVNR\",\n aliases: [\n \"Sozialversicherungsnummer\",\n \"SVNR\",\n \"Versicherungsnummer\",\n ] as const,\n candidatePattern: \"\\\\d{2}\\\\s?\\\\d{6}\\\\s?[A-Z]\\\\s?\\\\d{3}\",\n country: \"DE\",\n entityType: \"person\",\n description:\n \"German social insurance number encoding \" +\n \"birth date and name initial\",\n sourceUrl:\n \"https://de.wikipedia.org/wiki/\" +\n \"Sozialversicherungsnummer\",\n lengths: [12] as const,\n examples: [\"12010188M011\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default svnr;\nexport { compact, format, validate, generate };\n"],"mappings":"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"}
package/dist/de/vat.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/de/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/dk/cpr.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ParsedPersonId, ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/dk/cpr.d.ts
4
-
5
4
  declare const compact: (value: string) => string;
6
5
  declare const validate: (value: string) => ValidateResult;
7
6
  declare const format: (value: string) => string;
@@ -10,8 +9,10 @@ declare const format: (value: string) => string;
10
9
  * Returns null if the value is not valid.
11
10
  */
12
11
  declare const parse: (value: string) => ParsedPersonId | null;
12
+ /** Generate a random valid Danish CPR. */
13
+ declare const generate: () => string;
13
14
  /** Danish Personal Identification Number. */
14
- declare const cpr: Validator;
15
+ declare const cpr: Validator<ParsedPersonId>;
15
16
  //#endregion
16
- export { compact, cpr as default, format, parse, validate };
17
+ export { compact, cpr as default, format, generate, parse, validate };
17
18
  //# sourceMappingURL=cpr.d.ts.map
package/dist/dk/cpr.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { clean } from "../_util/clean.js";
2
+ import { randomDigits, randomInt } from "../_util/generate.js";
2
3
  import { err } from "../_util/result.js";
3
4
  import { isdigits } from "../_util/strings.js";
4
5
  import { isValidDate } from "../_util/date.js";
@@ -56,6 +57,12 @@ const parse = (value) => {
56
57
  gender: lastDigit % 2 === 0 ? "female" : "male"
57
58
  };
58
59
  };
60
+ /** Generate a random valid Danish CPR. */
61
+ const generate = () => {
62
+ const year = randomInt(1900, 2024);
63
+ const month = String(randomInt(1, 12)).padStart(2, "0");
64
+ return `${String(randomInt(1, 28)).padStart(2, "0")}${month}${String(year % 100).padStart(2, "0")}${String(randomInt(0, 3))}${randomDigits(3)}`;
65
+ };
59
66
  /** Danish Personal Identification Number. */
60
67
  const cpr = {
61
68
  name: "Danish Personal ID",
@@ -70,12 +77,15 @@ const cpr = {
70
77
  country: "DK",
71
78
  entityType: "person",
72
79
  sourceUrl: "https://cpr.dk/",
80
+ lengths: [10],
73
81
  examples: ["2110625629"],
74
82
  compact,
75
83
  format,
76
- validate
84
+ parse,
85
+ validate,
86
+ generate
77
87
  };
78
88
  //#endregion
79
- export { compact, cpr as default, format, parse, validate };
89
+ export { compact, cpr as default, format, generate, parse, validate };
80
90
 
81
91
  //# sourceMappingURL=cpr.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"cpr.js","names":[],"sources":["../../src/dk/cpr.ts"],"sourcesContent":["/**\n * CPR (Det Centrale Personregister).\n *\n * Danish personal identification number. 10 digits\n * in format DDMMYY-SSSS. Checksum enforcement was\n * dropped in 2007; only date validation is performed.\n *\n * @see https://cpr.dk/\n */\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n Validator,\n} from \"../types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\nconst getCentury = (yy: number, s7: number): number => {\n // 7th digit (first of serial) determines century\n if (s7 >= 0 && s7 <= 3) return 1900;\n if (s7 === 4 || s7 === 9) {\n return yy <= 36 ? 2000 : 1900;\n }\n // s7 5..8\n return yy <= 57 ? 2000 : 1800;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"CPR must be exactly 10 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"CPR must contain only digits\",\n );\n }\n\n const dd = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const yy = Number(v.slice(4, 6));\n const s7 = Number(v[6]);\n\n const century = getCentury(yy, s7);\n const year = century + yy;\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"CPR 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 \"CPR 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 CPR number.\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 const yy = Number(v.slice(4, 6));\n const s7 = Number(v[6]);\n const lastDigit = Number(v[9]);\n\n const century = getCentury(yy, s7);\n const year = century + yy;\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n gender: lastDigit % 2 === 0 ? \"female\" : \"male\",\n };\n};\n\n/** Danish Personal Identification Number. */\nconst cpr: Validator = {\n name: \"Danish Personal ID\",\n localName: \"Det Centrale Personregister\",\n abbreviation: \"CPR\",\n aliases: [\"CPR-nummer\", \"personnummer\", \"CPR\"] as const,\n candidatePattern: \"\\\\d{6}-?\\\\d{4}\",\n country: \"DK\",\n entityType: \"person\",\n sourceUrl: \"https://cpr.dk/\",\n examples: [\"2110625629\"] as const,\n compact,\n format,\n validate,\n};\n\nexport default cpr;\nexport { compact, format, parse, validate };\n"],"mappings":";;;;;;;;;;;;;;AAqBA,MAAM,WAAW,UACf,MAAM,OAAO,KAAK;AAEpB,MAAM,cAAc,IAAY,OAAuB;AAErD,KAAI,MAAM,KAAK,MAAM,EAAG,QAAO;AAC/B,KAAI,OAAO,KAAK,OAAO,EACrB,QAAO,MAAM,KAAK,MAAO;AAG3B,QAAO,MAAM,KAAK,MAAO;;AAG3B,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,gCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,+BACD;CAGH,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAIhC,MAAM,OADU,WAAW,IAFhB,OAAO,EAAE,GAAG,CAEW,GACX;AAEvB,KAAI,CAAC,YAAY,MAAM,IAAI,GAAG,CAC5B,QAAO,IACL,qBACA,+BACD;AAIH,KAAI,IAAI,KAAK,MAAM,KAAK,GAAG,GAAG,mBAAG,IAAI,MAAM,CACzC,QAAO,IACL,qBACA,kCACD;AAGH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE;;;;;;AAOvC,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,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,GAAG;CACvB,MAAM,YAAY,OAAO,EAAE,GAAG;CAG9B,MAAM,OADU,WAAW,IAAI,GAAG,GACX;AAEvB,QAAO;EACL,WAAW,IAAI,KAAK,MAAM,KAAK,GAAG,GAAG;EACrC,QAAQ,YAAY,MAAM,IAAI,WAAW;EAC1C;;;AAIH,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EAAC;EAAc;EAAgB;EAAM;CAC9C,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WAAW;CACX,UAAU,CAAC,aAAa;CACxB;CACA;CACA;CACD"}
1
+ {"version":3,"file":"cpr.js","names":[],"sources":["../../src/dk/cpr.ts"],"sourcesContent":["/**\n * CPR (Det Centrale Personregister).\n *\n * Danish personal identification number. 10 digits\n * in format DDMMYY-SSSS. Checksum enforcement was\n * dropped in 2007; only date validation is performed.\n *\n * @see https://cpr.dk/\n */\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n Validator,\n} from \"../types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\nconst getCentury = (yy: number, s7: number): number => {\n // 7th digit (first of serial) determines century\n if (s7 >= 0 && s7 <= 3) return 1900;\n if (s7 === 4 || s7 === 9) {\n return yy <= 36 ? 2000 : 1900;\n }\n // s7 5..8\n return yy <= 57 ? 2000 : 1800;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"CPR must be exactly 10 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"CPR must contain only digits\",\n );\n }\n\n const dd = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const yy = Number(v.slice(4, 6));\n const s7 = Number(v[6]);\n\n const century = getCentury(yy, s7);\n const year = century + yy;\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"CPR 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 \"CPR 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 CPR number.\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 const yy = Number(v.slice(4, 6));\n const s7 = Number(v[6]);\n const lastDigit = Number(v[9]);\n\n const century = getCentury(yy, s7);\n const year = century + yy;\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n gender: lastDigit % 2 === 0 ? \"female\" : \"male\",\n };\n};\n\n/** Generate a random valid Danish CPR. */\nconst generate = (): string => {\n const year = randomInt(1900, 2024);\n const month = String(randomInt(1, 12)).padStart(2, \"0\");\n const day = String(randomInt(1, 28)).padStart(2, \"0\");\n const yy = String(year % 100).padStart(2, \"0\");\n const serialPrefix = String(randomInt(0, 3));\n return `${day}${month}${yy}${serialPrefix}${randomDigits(3)}`;\n};\n\n/** Danish Personal Identification Number. */\nconst cpr: Validator<ParsedPersonId> = {\n name: \"Danish Personal ID\",\n localName: \"Det Centrale Personregister\",\n abbreviation: \"CPR\",\n aliases: [\"CPR-nummer\", \"personnummer\", \"CPR\"] as const,\n candidatePattern: \"\\\\d{6}-?\\\\d{4}\",\n country: \"DK\",\n entityType: \"person\",\n sourceUrl: \"https://cpr.dk/\",\n lengths: [10] as const,\n examples: [\"2110625629\"] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default cpr;\nexport { compact, format, generate, parse, validate };\n"],"mappings":";;;;;;;;;;;;;;;AAsBA,MAAM,WAAW,UACf,MAAM,OAAO,KAAK;AAEpB,MAAM,cAAc,IAAY,OAAuB;AAErD,KAAI,MAAM,KAAK,MAAM,EAAG,QAAO;AAC/B,KAAI,OAAO,KAAK,OAAO,EACrB,QAAO,MAAM,KAAK,MAAO;AAG3B,QAAO,MAAM,KAAK,MAAO;;AAG3B,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,gCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,+BACD;CAGH,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAIhC,MAAM,OADU,WAAW,IAFhB,OAAO,EAAE,GAAG,CAEW,GACX;AAEvB,KAAI,CAAC,YAAY,MAAM,IAAI,GAAG,CAC5B,QAAO,IACL,qBACA,+BACD;AAIH,KAAI,IAAI,KAAK,MAAM,KAAK,GAAG,GAAG,mBAAG,IAAI,MAAM,CACzC,QAAO,IACL,qBACA,kCACD;AAGH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE;;;;;;AAOvC,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,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,EAAE,CAAC;CAChC,MAAM,KAAK,OAAO,EAAE,GAAG;CACvB,MAAM,YAAY,OAAO,EAAE,GAAG;CAG9B,MAAM,OADU,WAAW,IAAI,GAAG,GACX;AAEvB,QAAO;EACL,WAAW,IAAI,KAAK,MAAM,KAAK,GAAG,GAAG;EACrC,QAAQ,YAAY,MAAM,IAAI,WAAW;EAC1C;;;AAIH,MAAM,iBAAyB;CAC7B,MAAM,OAAO,UAAU,MAAM,KAAK;CAClC,MAAM,QAAQ,OAAO,UAAU,GAAG,GAAG,CAAC,CAAC,SAAS,GAAG,IAAI;AAIvD,QAAO,GAHK,OAAO,UAAU,GAAG,GAAG,CAAC,CAAC,SAAS,GAAG,IAAI,GAGrC,QAFL,OAAO,OAAO,IAAI,CAAC,SAAS,GAAG,IAAI,GACzB,OAAO,UAAU,GAAG,EAAE,CAAC,GACA,aAAa,EAAE;;;AAI7D,MAAM,MAAiC;CACrC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EAAC;EAAc;EAAgB;EAAM;CAC9C,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WAAW;CACX,SAAS,CAAC,GAAG;CACb,UAAU,CAAC,aAAa;CACxB;CACA;CACA;CACA;CACA;CACD"}
package/dist/dk/cvr.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/dk/cvr.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/dk/mod.d.ts CHANGED
@@ -1,10 +1,11 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import cpr from "./cpr.js";
3
2
  import cvr from "./cvr.js";
4
3
  import vat from "./vat.js";
5
4
 
6
5
  //#region src/dk/mod.d.ts
7
-
6
+ declare namespace mod_d_exports {
7
+ export { cpr, cvr, vat };
8
+ }
8
9
  //#endregion
9
10
  export { cpr, cvr, mod_d_exports, vat };
10
11
  //# sourceMappingURL=mod.d.ts.map
package/dist/dk/vat.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/dk/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/do/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import rnc from "./rnc.js";
3
2
 
4
3
  //#region src/do/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { rnc };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, rnc };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/do/rnc.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/do/rnc.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/do/rnc.js CHANGED
@@ -41,7 +41,7 @@ const compact = (value) => clean(value, " -.").trim();
41
41
  */
42
42
  const calcRncCheckDigit = (value) => {
43
43
  let sum = 0;
44
- for (let i = 0; i < 8; i++) sum += Number(value[i]) * RNC_WEIGHTS[i];
44
+ for (const [i, weight] of RNC_WEIGHTS.entries()) sum += Number(value[i]) * weight;
45
45
  const remainder = sum % 11;
46
46
  return String((10 - remainder) % 9 + 1);
47
47
  };
@@ -1 +1 @@
1
- {"version":3,"file":"rnc.js","names":[],"sources":["../../src/do/rnc.ts"],"sourcesContent":["/**\n * RNC (Registro Nacional del Contribuyente) /\n * Cédula de Identidad.\n *\n * Dominican Republic tax identification number.\n * Companies use a 9-digit RNC with a check digit\n * computed via weighted sum mod 11. Individuals\n * use an 11-digit Cédula validated with the Luhn\n * algorithm.\n *\n * Format: ######### (9 digits, company RNC)\n * ########### (11 digits, individual Cédula)\n *\n * @see https://dgii.gov.do/\n */\n\nimport { luhnValidate } from \"#checksums/luhn\";\nimport { clean } from \"#util/clean\";\nimport { randomDigits } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\n/**\n * Weights for the 9-digit RNC check digit.\n * Applied to positions 0-7; check digit is at\n * position 8.\n */\nconst RNC_WEIGHTS = [7, 9, 8, 6, 5, 4, 3, 2] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -.\").trim();\n\n/**\n * RNC check digit: weighted sum mod 11, then\n * ((10 - remainder) % 9) + 1.\n */\nconst calcRncCheckDigit = (value: string): string => {\n let sum = 0;\n for (let i = 0; i < 8; i++) {\n sum += Number(value[i]) * RNC_WEIGHTS[i]!;\n }\n const remainder = sum % 11;\n return String(((10 - remainder) % 9) + 1);\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 9 && v.length !== 11) {\n return err(\n \"INVALID_LENGTH\",\n \"Must be 9 digits (RNC) or 11 digits (Cédula)\",\n );\n }\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Must contain only digits\",\n );\n }\n\n if (v.length === 9) {\n const expected = calcRncCheckDigit(v);\n if (v.at(-1) !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"RNC check digit does not match\",\n );\n }\n } else {\n // 11-digit Cédula uses Luhn checksum\n if (!luhnValidate(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Cédula Luhn checksum does not match\",\n );\n }\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n if (v.length === 9) {\n return `${v[0]}-${v.slice(1, 3)}-${v.slice(3, 8)}-${v.slice(8)}`;\n }\n return `${v.slice(0, 3)}-${v.slice(3, 10)}-${v.slice(10)}`;\n};\n\n/** Generate a random valid 9-digit RNC. */\nconst generate = (): string => {\n const payload = randomDigits(8);\n return payload + calcRncCheckDigit(payload);\n};\n\n/**\n * Dominican Republic RNC / Cédula.\n *\n * Examples sourced from python-stdnum test suite.\n */\nconst rnc: Validator = {\n name: \"Dominican Republic Tax ID\",\n localName: \"Registro Nacional del Contribuyente\",\n abbreviation: \"RNC\",\n aliases: [\n \"RNC\",\n \"Registro Nacional del Contribuyente\",\n ] as const,\n candidatePattern: \"\\\\d{9}\",\n country: \"DO\",\n entityType: \"any\",\n lengths: [9, 11] as const,\n examples: [\"131098193\", \"00113918205\"] as const,\n compact,\n format,\n validate,\n sourceUrl: \"https://dgii.gov.do/\",\n generate,\n};\n\nexport default rnc;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAM,cAAc;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;AAE5C,MAAM,WAAW,UACf,MAAM,OAAO,MAAM,CAAC,MAAM;;;;;AAM5B,MAAM,qBAAqB,UAA0B;CACnD,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IACrB,QAAO,OAAO,MAAM,GAAG,GAAG,YAAY;CAExC,MAAM,YAAY,MAAM;AACxB,QAAO,QAAS,KAAK,aAAa,IAAK,EAAE;;AAG3C,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AAExB,KAAI,EAAE,WAAW,KAAK,EAAE,WAAW,GACjC,QAAO,IACL,kBACA,+CACD;AAGH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,2BACD;AAGH,KAAI,EAAE,WAAW,GAAG;EAClB,MAAM,WAAW,kBAAkB,EAAE;AACrC,MAAI,EAAE,GAAG,GAAG,KAAK,SACf,QAAO,IACL,oBACA,iCACD;YAIC,CAAC,aAAa,EAAE,CAClB,QAAO,IACL,oBACA,sCACD;AAIL,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,EACf,QAAO,GAAG,EAAE,GAAG,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE;AAEhE,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG;;;AAI1D,MAAM,iBAAyB;CAC7B,MAAM,UAAU,aAAa,EAAE;AAC/B,QAAO,UAAU,kBAAkB,QAAQ;;;;;;;AAQ7C,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CACP,OACA,sCACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,SAAS,CAAC,GAAG,GAAG;CAChB,UAAU,CAAC,aAAa,cAAc;CACtC;CACA;CACA;CACA,WAAW;CACX;CACD"}
1
+ {"version":3,"file":"rnc.js","names":[],"sources":["../../src/do/rnc.ts"],"sourcesContent":["/**\n * RNC (Registro Nacional del Contribuyente) /\n * Cédula de Identidad.\n *\n * Dominican Republic tax identification number.\n * Companies use a 9-digit RNC with a check digit\n * computed via weighted sum mod 11. Individuals\n * use an 11-digit Cédula validated with the Luhn\n * algorithm.\n *\n * Format: ######### (9 digits, company RNC)\n * ########### (11 digits, individual Cédula)\n *\n * @see https://dgii.gov.do/\n */\n\nimport { luhnValidate } from \"#checksums/luhn\";\nimport { clean } from \"#util/clean\";\nimport { randomDigits } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\n/**\n * Weights for the 9-digit RNC check digit.\n * Applied to positions 0-7; check digit is at\n * position 8.\n */\nconst RNC_WEIGHTS = [7, 9, 8, 6, 5, 4, 3, 2] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -.\").trim();\n\n/**\n * RNC check digit: weighted sum mod 11, then\n * ((10 - remainder) % 9) + 1.\n */\nconst calcRncCheckDigit = (value: string): string => {\n let sum = 0;\n for (const [i, weight] of RNC_WEIGHTS.entries()) {\n sum += Number(value[i]) * weight;\n }\n const remainder = sum % 11;\n return String(((10 - remainder) % 9) + 1);\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 9 && v.length !== 11) {\n return err(\n \"INVALID_LENGTH\",\n \"Must be 9 digits (RNC) or 11 digits (Cédula)\",\n );\n }\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Must contain only digits\",\n );\n }\n\n if (v.length === 9) {\n const expected = calcRncCheckDigit(v);\n if (v.at(-1) !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"RNC check digit does not match\",\n );\n }\n } else {\n // 11-digit Cédula uses Luhn checksum\n if (!luhnValidate(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Cédula Luhn checksum does not match\",\n );\n }\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n if (v.length === 9) {\n return `${v[0]}-${v.slice(1, 3)}-${v.slice(3, 8)}-${v.slice(8)}`;\n }\n return `${v.slice(0, 3)}-${v.slice(3, 10)}-${v.slice(10)}`;\n};\n\n/** Generate a random valid 9-digit RNC. */\nconst generate = (): string => {\n const payload = randomDigits(8);\n return payload + calcRncCheckDigit(payload);\n};\n\n/**\n * Dominican Republic RNC / Cédula.\n *\n * Examples sourced from python-stdnum test suite.\n */\nconst rnc: Validator = {\n name: \"Dominican Republic Tax ID\",\n localName: \"Registro Nacional del Contribuyente\",\n abbreviation: \"RNC\",\n aliases: [\n \"RNC\",\n \"Registro Nacional del Contribuyente\",\n ] as const,\n candidatePattern: \"\\\\d{9}\",\n country: \"DO\",\n entityType: \"any\",\n lengths: [9, 11] as const,\n examples: [\"131098193\", \"00113918205\"] as const,\n compact,\n format,\n validate,\n sourceUrl: \"https://dgii.gov.do/\",\n generate,\n};\n\nexport default rnc;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAM,cAAc;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;AAE5C,MAAM,WAAW,UACf,MAAM,OAAO,MAAM,CAAC,MAAM;;;;;AAM5B,MAAM,qBAAqB,UAA0B;CACnD,IAAI,MAAM;AACV,MAAK,MAAM,CAAC,GAAG,WAAW,YAAY,SAAS,CAC7C,QAAO,OAAO,MAAM,GAAG,GAAG;CAE5B,MAAM,YAAY,MAAM;AACxB,QAAO,QAAS,KAAK,aAAa,IAAK,EAAE;;AAG3C,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AAExB,KAAI,EAAE,WAAW,KAAK,EAAE,WAAW,GACjC,QAAO,IACL,kBACA,+CACD;AAGH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,2BACD;AAGH,KAAI,EAAE,WAAW,GAAG;EAClB,MAAM,WAAW,kBAAkB,EAAE;AACrC,MAAI,EAAE,GAAG,GAAG,KAAK,SACf,QAAO,IACL,oBACA,iCACD;YAIC,CAAC,aAAa,EAAE,CAClB,QAAO,IACL,oBACA,sCACD;AAIL,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,EACf,QAAO,GAAG,EAAE,GAAG,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,EAAE;AAEhE,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG;;;AAI1D,MAAM,iBAAyB;CAC7B,MAAM,UAAU,aAAa,EAAE;AAC/B,QAAO,UAAU,kBAAkB,QAAQ;;;;;;;AAQ7C,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CACP,OACA,sCACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,SAAS,CAAC,GAAG,GAAG;CAChB,UAAU,CAAC,aAAa,cAAc;CACtC;CACA;CACA;CACA,WAAW;CACX;CACD"}
package/dist/ec/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/ec/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/ec/ruc.d.ts CHANGED
@@ -1,7 +1,6 @@
1
1
  import { ValidateResult, Validator } from "../types.js";
2
2
 
3
3
  //#region src/ec/ruc.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/ec/ruc.js CHANGED
@@ -37,7 +37,7 @@ const ciChecksum = (digits) => {
37
37
  /** Weighted mod-11 checksum. */
38
38
  const mod11Checksum = (digits, weights) => {
39
39
  let sum = 0;
40
- for (let i = 0; i < weights.length; i++) sum += Number(digits[i]) * weights[i];
40
+ for (const [i, weight] of weights.entries()) sum += Number(digits[i]) * weight;
41
41
  return sum % 11;
42
42
  };
43
43
  const PUBLIC_WEIGHTS = [