@stll/stdnum 0.0.1 → 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (474) hide show
  1. package/LICENSE +1 -5
  2. package/README.md +28 -4
  3. package/dist/_checksums/mod112.js +5 -5
  4. package/dist/_checksums/mod112.js.map +1 -1
  5. package/dist/_util/generate.d.ts +13 -1
  6. package/dist/_util/generate.js +23 -1
  7. package/dist/_util/generate.js.map +1 -1
  8. package/dist/ad/mod.d.ts +3 -2
  9. package/dist/ad/nrt.js +3 -2
  10. package/dist/ad/nrt.js.map +1 -1
  11. package/dist/ae/eid.d.ts +0 -1
  12. package/dist/ae/mod.d.ts +3 -2
  13. package/dist/ai/mod.d.ts +3 -2
  14. package/dist/al/mod.d.ts +3 -2
  15. package/dist/am/mod.d.ts +3 -2
  16. package/dist/ar/cbu.d.ts +0 -1
  17. package/dist/ar/cbu.js +1 -1
  18. package/dist/ar/cbu.js.map +1 -1
  19. package/dist/ar/cuit.d.ts +0 -1
  20. package/dist/ar/cuit.js +12 -12
  21. package/dist/ar/cuit.js.map +1 -1
  22. package/dist/ar/mod.d.ts +3 -2
  23. package/dist/at/mod.d.ts +3 -2
  24. package/dist/at/tin.d.ts +0 -1
  25. package/dist/at/uid.d.ts +0 -1
  26. package/dist/at/vnr.d.ts +3 -2
  27. package/dist/at/vnr.js +20 -3
  28. package/dist/at/vnr.js.map +1 -1
  29. package/dist/au/abn.d.ts +0 -1
  30. package/dist/au/acn.d.ts +0 -1
  31. package/dist/au/mod.d.ts +3 -2
  32. package/dist/au/tfn.d.ts +0 -1
  33. package/dist/az/mod.d.ts +3 -2
  34. package/dist/az/voen.d.ts +0 -1
  35. package/dist/az/voen.js.map +1 -1
  36. package/dist/ba/jmbg.d.ts +1 -2
  37. package/dist/ba/jmbg.js +1 -0
  38. package/dist/ba/jmbg.js.map +1 -1
  39. package/dist/ba/mod.d.ts +3 -2
  40. package/dist/bd/mod.d.ts +3 -2
  41. package/dist/bd/nid.js +10 -11
  42. package/dist/bd/nid.js.map +1 -1
  43. package/dist/be/bis.d.ts +0 -1
  44. package/dist/be/mod.d.ts +3 -2
  45. package/dist/be/nn.d.ts +0 -1
  46. package/dist/be/vat.d.ts +0 -1
  47. package/dist/bg/egn.d.ts +1 -2
  48. package/dist/bg/egn.js +10 -3
  49. package/dist/bg/egn.js.map +1 -1
  50. package/dist/bg/mod.d.ts +3 -2
  51. package/dist/bg/pnf.d.ts +0 -1
  52. package/dist/bg/vat.d.ts +0 -1
  53. package/dist/bh/cpr.d.ts +3 -2
  54. package/dist/bh/cpr.js +8 -2
  55. package/dist/bh/cpr.js.map +1 -1
  56. package/dist/bh/mod.d.ts +3 -2
  57. package/dist/bic.js +13 -15
  58. package/dist/bic.js.map +1 -1
  59. package/dist/br/cnpj.d.ts +0 -1
  60. package/dist/br/cpf.d.ts +0 -1
  61. package/dist/br/mod.d.ts +3 -2
  62. package/dist/by/mod.d.ts +3 -2
  63. package/dist/by/unp.d.ts +0 -1
  64. package/dist/by/unp.js +6 -3
  65. package/dist/by/unp.js.map +1 -1
  66. package/dist/bz/mod.d.ts +3 -2
  67. package/dist/bz/tin.d.ts +3 -2
  68. package/dist/bz/tin.js +9 -1
  69. package/dist/bz/tin.js.map +1 -1
  70. package/dist/ca/bn.d.ts +0 -1
  71. package/dist/ca/mod.d.ts +3 -2
  72. package/dist/ca/sin.d.ts +0 -1
  73. package/dist/ch/mod.d.ts +3 -2
  74. package/dist/ch/ssn.d.ts +0 -1
  75. package/dist/ch/uid.d.ts +0 -1
  76. package/dist/ch/vat.d.ts +0 -1
  77. package/dist/cl/mod.d.ts +3 -2
  78. package/dist/cl/rut.d.ts +0 -1
  79. package/dist/cl/rut.js +1 -1
  80. package/dist/cl/rut.js.map +1 -1
  81. package/dist/cn/mod.d.ts +3 -2
  82. package/dist/cn/ric.d.ts +1 -2
  83. package/dist/cn/ric.js +1 -0
  84. package/dist/cn/ric.js.map +1 -1
  85. package/dist/cn/uscc.d.ts +0 -1
  86. package/dist/cn/uscc.js +7 -6
  87. package/dist/cn/uscc.js.map +1 -1
  88. package/dist/co/mod.d.ts +3 -2
  89. package/dist/co/nit.d.ts +0 -1
  90. package/dist/co/nit.js +4 -1
  91. package/dist/co/nit.js.map +1 -1
  92. package/dist/cr/cpf.js +4 -3
  93. package/dist/cr/cpf.js.map +1 -1
  94. package/dist/cr/mod.d.ts +3 -2
  95. package/dist/creditcard.d.ts +0 -1
  96. package/dist/cu/mod.d.ts +3 -2
  97. package/dist/cu/ni.d.ts +4 -3
  98. package/dist/cu/ni.js +11 -1
  99. package/dist/cu/ni.js.map +1 -1
  100. package/dist/cy/mod.d.ts +3 -2
  101. package/dist/cy/vat.d.ts +0 -1
  102. package/dist/cz/dic.d.ts +0 -1
  103. package/dist/cz/ico.d.ts +0 -1
  104. package/dist/cz/mod.d.ts +3 -2
  105. package/dist/cz/rc.d.ts +1 -2
  106. package/dist/cz/rc.js +33 -8
  107. package/dist/cz/rc.js.map +1 -1
  108. package/dist/de/handelsreg.js +9 -9
  109. package/dist/de/handelsreg.js.map +1 -1
  110. package/dist/de/idnr.d.ts +0 -1
  111. package/dist/de/idnr.js.map +1 -1
  112. package/dist/de/mod.d.ts +3 -2
  113. package/dist/de/stnr.d.ts +0 -1
  114. package/dist/de/stnr.js.map +1 -1
  115. package/dist/de/svnr.d.ts +0 -1
  116. package/dist/de/svnr.js +2 -2
  117. package/dist/de/svnr.js.map +1 -1
  118. package/dist/de/vat.d.ts +0 -1
  119. package/dist/dk/cpr.d.ts +4 -3
  120. package/dist/dk/cpr.js +12 -2
  121. package/dist/dk/cpr.js.map +1 -1
  122. package/dist/dk/cvr.d.ts +0 -1
  123. package/dist/dk/mod.d.ts +3 -2
  124. package/dist/dk/vat.d.ts +0 -1
  125. package/dist/do/mod.d.ts +3 -2
  126. package/dist/do/rnc.d.ts +0 -1
  127. package/dist/do/rnc.js +1 -1
  128. package/dist/do/rnc.js.map +1 -1
  129. package/dist/ec/mod.d.ts +3 -2
  130. package/dist/ec/ruc.d.ts +0 -1
  131. package/dist/ec/ruc.js +1 -1
  132. package/dist/ec/ruc.js.map +1 -1
  133. package/dist/ee/ik.d.ts +1 -2
  134. package/dist/ee/ik.js +12 -4
  135. package/dist/ee/ik.js.map +1 -1
  136. package/dist/ee/mod.d.ts +3 -2
  137. package/dist/ee/registrikood.d.ts +0 -1
  138. package/dist/ee/registrikood.js +8 -7
  139. package/dist/ee/registrikood.js.map +1 -1
  140. package/dist/ee/vat.d.ts +0 -1
  141. package/dist/eg/mod.d.ts +3 -2
  142. package/dist/es/cif.d.ts +0 -1
  143. package/dist/es/cif.js +2 -2
  144. package/dist/es/cif.js.map +1 -1
  145. package/dist/es/dni.d.ts +0 -1
  146. package/dist/es/mod.d.ts +3 -2
  147. package/dist/es/nie.d.ts +0 -1
  148. package/dist/es/nie.js +13 -13
  149. package/dist/es/nie.js.map +1 -1
  150. package/dist/es/nss.d.ts +0 -1
  151. package/dist/es/vat.d.ts +0 -1
  152. package/dist/eu/mod.d.ts +3 -2
  153. package/dist/eu/vat.d.ts +3 -2
  154. package/dist/eu/vat.js +43 -2
  155. package/dist/eu/vat.js.map +1 -1
  156. package/dist/fi/hetu.d.ts +4 -3
  157. package/dist/fi/hetu.js +17 -2
  158. package/dist/fi/hetu.js.map +1 -1
  159. package/dist/fi/mod.d.ts +3 -2
  160. package/dist/fi/vat.d.ts +0 -1
  161. package/dist/fi/ytunnus.d.ts +0 -1
  162. package/dist/fr/mod.d.ts +3 -2
  163. package/dist/fr/nif.d.ts +0 -1
  164. package/dist/fr/nir.d.ts +1 -2
  165. package/dist/fr/nir.js +1 -0
  166. package/dist/fr/nir.js.map +1 -1
  167. package/dist/fr/siren.d.ts +0 -1
  168. package/dist/fr/siret.d.ts +0 -1
  169. package/dist/fr/tva.d.ts +0 -1
  170. package/dist/gb/mod.d.ts +3 -2
  171. package/dist/gb/nino.js +7 -7
  172. package/dist/gb/nino.js.map +1 -1
  173. package/dist/gb/sedol.d.ts +0 -1
  174. package/dist/gb/sedol.js +8 -7
  175. package/dist/gb/sedol.js.map +1 -1
  176. package/dist/gb/utr.d.ts +0 -1
  177. package/dist/gb/vat.d.ts +0 -1
  178. package/dist/ge/mod.d.ts +3 -2
  179. package/dist/gh/mod.d.ts +3 -2
  180. package/dist/gh/tin.d.ts +0 -1
  181. package/dist/gh/tin.js +9 -9
  182. package/dist/gh/tin.js.map +1 -1
  183. package/dist/gr/amka.d.ts +0 -1
  184. package/dist/gr/mod.d.ts +3 -2
  185. package/dist/gr/vat.d.ts +0 -1
  186. package/dist/gt/mod.d.ts +3 -2
  187. package/dist/gt/nit.d.ts +0 -1
  188. package/dist/gt/nit.js +5 -2
  189. package/dist/gt/nit.js.map +1 -1
  190. package/dist/hk/hkid.d.ts +0 -1
  191. package/dist/hk/hkid.js +1 -0
  192. package/dist/hk/hkid.js.map +1 -1
  193. package/dist/hk/mod.d.ts +3 -2
  194. package/dist/hr/mod.d.ts +3 -2
  195. package/dist/hr/vat.d.ts +0 -1
  196. package/dist/hu/mod.d.ts +3 -2
  197. package/dist/hu/vat.d.ts +0 -1
  198. package/dist/iban.d.ts +0 -1
  199. package/dist/id/mod.d.ts +3 -2
  200. package/dist/id/npwp.d.ts +0 -1
  201. package/dist/ie/mod.d.ts +3 -2
  202. package/dist/ie/pps.d.ts +0 -1
  203. package/dist/ie/vat.d.ts +0 -1
  204. package/dist/il/idnr.d.ts +0 -1
  205. package/dist/il/mod.d.ts +3 -2
  206. package/dist/in/aadhaar.d.ts +0 -1
  207. package/dist/in/gstin.d.ts +0 -1
  208. package/dist/in/gstin.js +3 -1
  209. package/dist/in/gstin.js.map +1 -1
  210. package/dist/in/mod.d.ts +3 -2
  211. package/dist/in/pan.js +2 -2
  212. package/dist/in/pan.js.map +1 -1
  213. package/dist/index.d.ts +2 -2
  214. package/dist/iq/mod.d.ts +3 -2
  215. package/dist/ir/mod.d.ts +3 -2
  216. package/dist/ir/nid.d.ts +0 -1
  217. package/dist/is/kennitala.d.ts +0 -1
  218. package/dist/is/mod.d.ts +3 -2
  219. package/dist/isin.d.ts +0 -1
  220. package/dist/isin.js +9 -9
  221. package/dist/isin.js.map +1 -1
  222. package/dist/it/codicefiscale.d.ts +4 -3
  223. package/dist/it/codicefiscale.js +26 -2
  224. package/dist/it/codicefiscale.js.map +1 -1
  225. package/dist/it/iva.d.ts +0 -1
  226. package/dist/it/mod.d.ts +3 -2
  227. package/dist/jp/cn.d.ts +0 -1
  228. package/dist/jp/mod.d.ts +3 -2
  229. package/dist/jp/mynumber.d.ts +0 -1
  230. package/dist/kr/mod.d.ts +3 -2
  231. package/dist/kr/rrn.d.ts +4 -3
  232. package/dist/kr/rrn.js +13 -2
  233. package/dist/kr/rrn.js.map +1 -1
  234. package/dist/kw/civil.d.ts +6 -5
  235. package/dist/kw/civil.js +18 -3
  236. package/dist/kw/civil.js.map +1 -1
  237. package/dist/kw/mod.d.ts +3 -2
  238. package/dist/kz/iin.d.ts +1 -2
  239. package/dist/kz/iin.js +1 -0
  240. package/dist/kz/iin.js.map +1 -1
  241. package/dist/kz/mod.d.ts +3 -2
  242. package/dist/lei.d.ts +0 -1
  243. package/dist/li/mod.d.ts +3 -2
  244. package/dist/lk/mod.d.ts +3 -2
  245. package/dist/lk/nic.d.ts +4 -3
  246. package/dist/lk/nic.js +14 -2
  247. package/dist/lk/nic.js.map +1 -1
  248. package/dist/lt/asmens.d.ts +0 -1
  249. package/dist/lt/mod.d.ts +3 -2
  250. package/dist/lt/vat.d.ts +0 -1
  251. package/dist/lu/mod.d.ts +3 -2
  252. package/dist/lu/vat.d.ts +0 -1
  253. package/dist/luhn.d.ts +0 -1
  254. package/dist/luhn.js +7 -0
  255. package/dist/luhn.js.map +1 -1
  256. package/dist/lv/mod.d.ts +3 -2
  257. package/dist/lv/vat.d.ts +0 -1
  258. package/dist/lv/vat.js +39 -12
  259. package/dist/lv/vat.js.map +1 -1
  260. package/dist/ma/ice.d.ts +0 -1
  261. package/dist/ma/mod.d.ts +3 -2
  262. package/dist/mc/mod.d.ts +3 -2
  263. package/dist/mc/tva.d.ts +0 -1
  264. package/dist/md/idno.d.ts +0 -1
  265. package/dist/md/mod.d.ts +3 -2
  266. package/dist/me/mod.d.ts +3 -2
  267. package/dist/me/pib.d.ts +0 -1
  268. package/dist/mk/edb.d.ts +0 -1
  269. package/dist/mk/mod.d.ts +3 -2
  270. package/dist/mt/mod.d.ts +3 -2
  271. package/dist/mt/vat.d.ts +0 -1
  272. package/dist/mu/mod.d.ts +3 -2
  273. package/dist/mx/clabe.d.ts +0 -1
  274. package/dist/mx/curp.d.ts +4 -3
  275. package/dist/mx/curp.js +25 -3
  276. package/dist/mx/curp.js.map +1 -1
  277. package/dist/mx/mod.d.ts +3 -2
  278. package/dist/mx/rfc.d.ts +3 -2
  279. package/dist/mx/rfc.js +21 -2
  280. package/dist/mx/rfc.js.map +1 -1
  281. package/dist/my/mod.d.ts +3 -2
  282. package/dist/my/nric.d.ts +4 -3
  283. package/dist/my/nric.js +16 -2
  284. package/dist/my/nric.js.map +1 -1
  285. package/dist/ng/mod.d.ts +3 -2
  286. package/dist/ni/mod.d.ts +3 -2
  287. package/dist/ni/ruc.d.ts +0 -1
  288. package/dist/nl/bsn.d.ts +0 -1
  289. package/dist/nl/mod.d.ts +3 -2
  290. package/dist/nl/vat.d.ts +0 -1
  291. package/dist/nl/vat.js +9 -43
  292. package/dist/nl/vat.js.map +1 -1
  293. package/dist/no/fodselsnummer.d.ts +1 -2
  294. package/dist/no/fodselsnummer.js +1 -0
  295. package/dist/no/fodselsnummer.js.map +1 -1
  296. package/dist/no/mod.d.ts +3 -2
  297. package/dist/no/mva.d.ts +0 -1
  298. package/dist/no/orgnr.d.ts +0 -1
  299. package/dist/nz/ird.d.ts +0 -1
  300. package/dist/nz/ird.js +2 -2
  301. package/dist/nz/ird.js.map +1 -1
  302. package/dist/nz/mod.d.ts +3 -2
  303. package/dist/pa/mod.d.ts +3 -2
  304. package/dist/pa/ruc.d.ts +0 -1
  305. package/dist/pa/ruc.js +3 -1
  306. package/dist/pa/ruc.js.map +1 -1
  307. package/dist/patterns.d.ts +1 -4
  308. package/dist/patterns.js +80 -23
  309. package/dist/patterns.js.map +1 -1
  310. package/dist/pe/mod.d.ts +3 -2
  311. package/dist/pe/ruc.d.ts +0 -1
  312. package/dist/pe/ruc.js +9 -9
  313. package/dist/pe/ruc.js.map +1 -1
  314. package/dist/ph/mod.d.ts +3 -2
  315. package/dist/pk/cnic.js.map +1 -1
  316. package/dist/pk/mod.d.ts +3 -2
  317. package/dist/pl/mod.d.ts +3 -2
  318. package/dist/pl/nip.d.ts +0 -1
  319. package/dist/pl/pesel.d.ts +1 -2
  320. package/dist/pl/pesel.js +1 -0
  321. package/dist/pl/pesel.js.map +1 -1
  322. package/dist/pl/regon.d.ts +0 -1
  323. package/dist/pt/cc.d.ts +0 -1
  324. package/dist/pt/cc.js +6 -4
  325. package/dist/pt/cc.js.map +1 -1
  326. package/dist/pt/mod.d.ts +3 -2
  327. package/dist/pt/vat.d.ts +0 -1
  328. package/dist/ro/cnp.d.ts +1 -2
  329. package/dist/ro/cnp.js +1 -0
  330. package/dist/ro/cnp.js.map +1 -1
  331. package/dist/ro/mod.d.ts +3 -2
  332. package/dist/ro/vat.d.ts +0 -1
  333. package/dist/rs/mod.d.ts +3 -2
  334. package/dist/rs/pib.d.ts +0 -1
  335. package/dist/ru/inn.d.ts +0 -1
  336. package/dist/ru/inn.js +1 -1
  337. package/dist/ru/inn.js.map +1 -1
  338. package/dist/ru/mod.d.ts +3 -2
  339. package/dist/se/mod.d.ts +3 -2
  340. package/dist/se/orgnr.d.ts +0 -1
  341. package/dist/se/personnummer.d.ts +1 -2
  342. package/dist/se/personnummer.js +1 -0
  343. package/dist/se/personnummer.js.map +1 -1
  344. package/dist/se/vat.d.ts +0 -1
  345. package/dist/sg/mod.d.ts +3 -2
  346. package/dist/sg/uen.d.ts +0 -1
  347. package/dist/sg/uen.js +46 -55
  348. package/dist/sg/uen.js.map +1 -1
  349. package/dist/si/emso.d.ts +1 -2
  350. package/dist/si/emso.js +17 -4
  351. package/dist/si/emso.js.map +1 -1
  352. package/dist/si/mod.d.ts +3 -2
  353. package/dist/si/vat.d.ts +0 -1
  354. package/dist/sk/dic.d.ts +0 -1
  355. package/dist/sk/ico.d.ts +0 -1
  356. package/dist/sk/mod.d.ts +3 -2
  357. package/dist/sk/rc.d.ts +4 -5
  358. package/dist/sk/rc.js +5 -2
  359. package/dist/sk/rc.js.map +1 -1
  360. package/dist/th/mod.d.ts +3 -2
  361. package/dist/th/tin.d.ts +0 -1
  362. package/dist/tr/mod.d.ts +3 -2
  363. package/dist/tr/tckimlik.d.ts +0 -1
  364. package/dist/tr/vkn.d.ts +0 -1
  365. package/dist/tw/mod.d.ts +3 -2
  366. package/dist/tw/ubn.d.ts +0 -1
  367. package/dist/tw/ubn.js +1 -1
  368. package/dist/tw/ubn.js.map +1 -1
  369. package/dist/types.d.ts +35 -22
  370. package/dist/ua/edrpou.d.ts +0 -1
  371. package/dist/ua/edrpou.js +4 -1
  372. package/dist/ua/edrpou.js.map +1 -1
  373. package/dist/ua/mod.d.ts +3 -2
  374. package/dist/us/ein.js +87 -87
  375. package/dist/us/ein.js.map +1 -1
  376. package/dist/us/itin.js +3 -4
  377. package/dist/us/itin.js.map +1 -1
  378. package/dist/us/mod.d.ts +3 -2
  379. package/dist/us/rtn.d.ts +0 -1
  380. package/dist/us/rtn.js +8 -5
  381. package/dist/us/rtn.js.map +1 -1
  382. package/dist/uy/mod.d.ts +3 -2
  383. package/dist/uy/rut.d.ts +0 -1
  384. package/dist/uy/rut.js +1 -1
  385. package/dist/uy/rut.js.map +1 -1
  386. package/dist/ve/mod.d.ts +3 -2
  387. package/dist/ve/rif.d.ts +0 -1
  388. package/dist/ve/rif.js +11 -11
  389. package/dist/ve/rif.js.map +1 -1
  390. package/dist/vn/mod.d.ts +3 -2
  391. package/dist/vn/mst.d.ts +0 -1
  392. package/dist/za/idnr.d.ts +1 -2
  393. package/dist/za/idnr.js +1 -0
  394. package/dist/za/idnr.js.map +1 -1
  395. package/dist/za/mod.d.ts +3 -2
  396. package/package.json +18 -15
  397. package/src/_checksums/mod112.ts +7 -6
  398. package/src/_util/generate.ts +38 -0
  399. package/src/ad/nrt.ts +12 -4
  400. package/src/ar/cbu.ts +2 -2
  401. package/src/ar/cuit.ts +15 -7
  402. package/src/at/vnr.ts +26 -4
  403. package/src/az/voen.ts +2 -0
  404. package/src/ba/jmbg.ts +2 -1
  405. package/src/bd/nid.ts +4 -4
  406. package/src/bg/egn.ts +12 -4
  407. package/src/bh/cpr.ts +10 -1
  408. package/src/bic.ts +19 -17
  409. package/src/by/unp.ts +9 -4
  410. package/src/bz/tin.ts +12 -1
  411. package/src/cl/rut.ts +4 -2
  412. package/src/cn/ric.ts +4 -1
  413. package/src/cn/uscc.ts +20 -8
  414. package/src/co/nit.ts +6 -2
  415. package/src/cr/cpf.ts +4 -3
  416. package/src/cu/ni.ts +22 -2
  417. package/src/cz/rc.ts +54 -23
  418. package/src/de/handelsreg.ts +14 -3
  419. package/src/de/idnr.ts +3 -0
  420. package/src/de/stnr.ts +4 -0
  421. package/src/de/svnr.ts +7 -2
  422. package/src/dk/cpr.ts +16 -2
  423. package/src/do/rnc.ts +2 -2
  424. package/src/ec/ruc.ts +2 -2
  425. package/src/ee/ik.ts +14 -4
  426. package/src/ee/registrikood.ts +4 -3
  427. package/src/es/cif.ts +2 -3
  428. package/src/es/nie.ts +10 -5
  429. package/src/eu/vat.ts +46 -1
  430. package/src/fi/hetu.ts +19 -2
  431. package/src/fr/nir.ts +2 -1
  432. package/src/gb/nino.ts +11 -10
  433. package/src/gb/sedol.ts +14 -12
  434. package/src/gh/tin.ts +4 -3
  435. package/src/gt/nit.ts +6 -3
  436. package/src/hk/hkid.ts +2 -1
  437. package/src/in/gstin.ts +3 -1
  438. package/src/in/pan.ts +8 -3
  439. package/src/index.ts +3 -0
  440. package/src/isin.ts +10 -4
  441. package/src/it/codicefiscale.ts +40 -2
  442. package/src/kr/rrn.ts +20 -2
  443. package/src/kw/civil.ts +25 -9
  444. package/src/kz/iin.ts +2 -1
  445. package/src/lk/nic.ts +24 -2
  446. package/src/luhn.ts +3 -0
  447. package/src/lv/vat.ts +46 -20
  448. package/src/mx/curp.ts +41 -5
  449. package/src/mx/rfc.ts +35 -4
  450. package/src/my/nric.ts +26 -2
  451. package/src/nl/vat.ts +11 -66
  452. package/src/no/fodselsnummer.ts +2 -1
  453. package/src/nz/ird.ts +4 -4
  454. package/src/pa/ruc.ts +32 -8
  455. package/src/patterns.ts +135 -28
  456. package/src/pe/ruc.ts +6 -7
  457. package/src/pk/cnic.ts +2 -0
  458. package/src/pl/pesel.ts +2 -1
  459. package/src/pt/cc.ts +6 -4
  460. package/src/ro/cnp.ts +2 -1
  461. package/src/ru/inn.ts +2 -2
  462. package/src/se/personnummer.ts +2 -1
  463. package/src/sg/uen.ts +37 -20
  464. package/src/si/emso.ts +30 -5
  465. package/src/sk/rc.ts +13 -4
  466. package/src/tw/ubn.ts +2 -2
  467. package/src/types.ts +37 -6
  468. package/src/ua/edrpou.ts +3 -2
  469. package/src/us/ein.ts +88 -88
  470. package/src/us/itin.ts +7 -8
  471. package/src/us/rtn.ts +16 -11
  472. package/src/uy/rut.ts +2 -2
  473. package/src/ve/rif.ts +11 -6
  474. package/src/za/idnr.ts +2 -1
package/src/lv/vat.ts CHANGED
@@ -1,12 +1,29 @@
1
1
  /**
2
2
  * PVN (Latvian VAT number).
3
3
  *
4
- * 11 digits. Legal entities (first digit > 3):
5
- * weights [9,1,4,8,3,10,2,5,7,6,1], sum % 11 === 3.
6
- * Personal (first digit <= 3): 11-digit format only.
4
+ * 11 digits.
7
5
  *
8
- * @see https://www.vatify.eu/latvia-vat-number.html
9
- * @see https://www.vid.gov.lv/en
6
+ * Individuals:
7
+ * - legacy personal codes: DDMMYY + century digit 0/1/2 + 4 digits
8
+ * - since 1 July 2017: codes start with 3, the second digit is 2-9,
9
+ * and the remaining digits are random
10
+ *
11
+ * Entities:
12
+ * - tax administration issued: 9XXXXXXXXXX
13
+ * - enterprise register issued: 4XXXXXXXXXX / 5XXXXXXXXXX
14
+ *
15
+ * Canonical sources:
16
+ * - OECD Latvia TIN sheet for the tax-facing formats
17
+ * - PMLP page documenting the post-2017 personal code
18
+ *
19
+ * These are the reasons we accept the new personal
20
+ * code shape (`3` + second digit `2-9`) without the
21
+ * legacy birth-date checksum, and why we reject
22
+ * legacy personal codes with century digits outside
23
+ * 0/1/2.
24
+ *
25
+ * @see https://www.oecd.org/tax/automatic-exchange/crs-implementation-and-assistance/tax-identification-numbers/Latvia-TIN.pdf
26
+ * @see https://www.pmlp.gov.lv/en/change-personal-identity-number
10
27
  */
11
28
 
12
29
  import { weightedSum } from "#checksums/weighted-sum";
@@ -46,6 +63,9 @@ const validateBirthDate = (v: string): boolean => {
46
63
  const month = Number(v.slice(2, 4));
47
64
  const year2 = Number(v.slice(4, 6));
48
65
  const century = Number(v[6]);
66
+ if (century < 0 || century > 2) {
67
+ return false;
68
+ }
49
69
  const fullYear = 1800 + century * 100 + year2;
50
70
  const date = new Date(fullYear, month - 1, day);
51
71
  return (
@@ -69,9 +89,19 @@ const validate = (value: string): ValidateResult => {
69
89
  "Latvian VAT number must contain only digits",
70
90
  );
71
91
  }
72
- const first = Number(v[0]);
73
- // Legal entity: first digit > 3
74
- if (first > 3) {
92
+ if (
93
+ v[0] === "3" &&
94
+ v[1] !== undefined &&
95
+ v[1] >= "2" &&
96
+ v[1] <= "9"
97
+ ) {
98
+ // Since 1 July 2017, new personal codes start with 3
99
+ // and are syntactic identifiers only.
100
+ return { valid: true, compact: v };
101
+ }
102
+
103
+ const first = v[0];
104
+ if (first === "4" || first === "5" || first === "9") {
75
105
  const sum = weightedSum(v, WEIGHTS, 11);
76
106
  if (sum !== 3) {
77
107
  return err(
@@ -79,15 +109,6 @@ const validate = (value: string): ValidateResult => {
79
109
  "Latvian VAT number check digit mismatch",
80
110
  );
81
111
  }
82
- } else if (v.startsWith("32")) {
83
- // New-format personal code (from July 2017),
84
- // no birth date; checksum only
85
- if (!checkPersonal(v)) {
86
- return err(
87
- "INVALID_CHECKSUM",
88
- "Latvian personal code check digit mismatch",
89
- );
90
- }
91
112
  } else {
92
113
  // Old-format personal code: first 6 digits encode
93
114
  // birth date as DDMMYY, digit 7 encodes century
@@ -112,8 +133,12 @@ const format = (value: string): string =>
112
133
 
113
134
  /** Generate a random valid Latvian VAT number. */
114
135
  const generate = (): string => {
136
+ const entityPrefixes = ["4", "5", "9"] as const;
115
137
  for (;;) {
116
- const c = String(randomInt(4, 9)) + randomDigits(10);
138
+ const c =
139
+ randomInt(0, 1) === 0
140
+ ? `3${randomInt(2, 9)}${randomDigits(9)}`
141
+ : `${entityPrefixes[randomInt(0, entityPrefixes.length - 1)] ?? "4"}${randomDigits(10)}`;
117
142
  if (validate(c).valid) return c;
118
143
  }
119
144
  };
@@ -127,8 +152,9 @@ const vat: Validator = {
127
152
  candidatePattern: "LV\\d{11}",
128
153
  country: "LV",
129
154
  entityType: "any",
130
- sourceUrl: "https://www.vid.gov.lv/en",
131
- examples: ["40003521600"] as const,
155
+ sourceUrl:
156
+ "https://www.pmlp.gov.lv/en/change-personal-identity-number",
157
+ examples: ["40003521600", "32999999999"] as const,
132
158
  compact,
133
159
  format,
134
160
  validate,
package/src/mx/curp.ts CHANGED
@@ -16,6 +16,7 @@
16
16
 
17
17
  import { clean } from "#util/clean";
18
18
  import { isValidDate } from "#util/date";
19
+ import { randomInt } from "#util/generate";
19
20
  import { err } from "#util/result";
20
21
 
21
22
  import type {
@@ -60,6 +61,8 @@ const STATE_CODES = new Set([
60
61
  "ZS",
61
62
  "NE", // Born abroad
62
63
  ]);
64
+ const STATE_CODE_LIST = [...STATE_CODES];
65
+ const LETTERS = "ABCDEFGHIJKLMNOPQRSTUVWXYZ";
63
66
 
64
67
  /**
65
68
  * Alphabet for CURP check digit computation.
@@ -69,8 +72,8 @@ const STATE_CODES = new Set([
69
72
  const ALPHABET = "0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ";
70
73
 
71
74
  const CHAR_MAP = new Map<string, number>();
72
- for (let i = 0; i < ALPHABET.length; i++) {
73
- CHAR_MAP.set(ALPHABET[i]!, i);
75
+ for (const [i, ch] of [...ALPHABET].entries()) {
76
+ CHAR_MAP.set(ch, i);
74
77
  }
75
78
 
76
79
  const compact = (value: string): string =>
@@ -84,7 +87,9 @@ const compact = (value: string): string =>
84
87
  const calcCheckDigit = (value: string): string => {
85
88
  let sum = 0;
86
89
  for (let i = 0; i < 17; i++) {
87
- sum += (CHAR_MAP.get(value[i]!) ?? 0) * (18 - i);
90
+ const ch = value[i];
91
+ if (ch === undefined) continue;
92
+ sum += (CHAR_MAP.get(ch) ?? 0) * (18 - i);
88
93
  }
89
94
  return String((10 - (sum % 10)) % 10);
90
95
  };
@@ -126,6 +131,8 @@ const validate = (value: string): ValidateResult => {
126
131
  const mm = Number(v.slice(6, 8));
127
132
  const dd = Number(v.slice(8, 10));
128
133
 
134
+ // SAFETY: regex validation ensures v.length === 18.
135
+ // eslint-disable-next-line no-non-null-assertion
129
136
  const year = resolveCurpYear(yy, v[16]!);
130
137
  if (!isValidDate(year, mm, dd)) {
131
138
  return err(
@@ -170,6 +177,8 @@ const parse = (value: string): ParsedPersonId | null => {
170
177
  const mm = Number(v.slice(6, 8));
171
178
  const dd = Number(v.slice(8, 10));
172
179
 
180
+ // SAFETY: regex validation ensures v.length === 18.
181
+ // eslint-disable-next-line no-non-null-assertion
173
182
  const year = resolveCurpYear(yy, v[16]!);
174
183
 
175
184
  const genderChar = v[10];
@@ -182,12 +191,37 @@ const parse = (value: string): ParsedPersonId | null => {
182
191
  };
183
192
  };
184
193
 
194
+ const randomLetter = (): string =>
195
+ LETTERS.charAt(randomInt(0, LETTERS.length - 1));
196
+
197
+ /** Generate a random valid CURP. */
198
+ const generate = (): string => {
199
+ const year = randomInt(1980, 2024);
200
+ const month = String(randomInt(1, 12)).padStart(2, "0");
201
+ const day = String(randomInt(1, 28)).padStart(2, "0");
202
+ const yy = String(year % 100).padStart(2, "0");
203
+ const gender = Math.random() < 0.5 ? "H" : "M";
204
+ const state =
205
+ STATE_CODE_LIST[
206
+ randomInt(0, STATE_CODE_LIST.length - 1)
207
+ ] ?? "NE";
208
+ const centuryChar =
209
+ year >= 2000 ? randomLetter() : String(randomInt(0, 9));
210
+ const body =
211
+ `${randomLetter()}${randomLetter()}` +
212
+ `${randomLetter()}${randomLetter()}` +
213
+ `${yy}${month}${day}${gender}${state}` +
214
+ `${randomLetter()}${randomLetter()}${randomLetter()}` +
215
+ `${centuryChar}`;
216
+ return `${body}${calcCheckDigit(body)}`;
217
+ };
218
+
185
219
  /**
186
220
  * Mexican CURP (personal identification number).
187
221
  *
188
222
  * Example sourced from python-stdnum test suite.
189
223
  */
190
- const curp: Validator = {
224
+ const curp: Validator<ParsedPersonId> = {
191
225
  name: "Mexican Personal ID",
192
226
  localName: "Clave Única de Registro de Población",
193
227
  abbreviation: "CURP",
@@ -202,9 +236,11 @@ const curp: Validator = {
202
236
  examples: ["BOXW310820HNERXN09"] as const,
203
237
  compact,
204
238
  format,
239
+ parse,
205
240
  validate,
241
+ generate,
206
242
  sourceUrl: "https://en.wikipedia.org/wiki/CURP",
207
243
  };
208
244
 
209
245
  export default curp;
210
- export { compact, format, parse, validate };
246
+ export { compact, format, generate, parse, validate };
package/src/mx/rfc.ts CHANGED
@@ -18,6 +18,7 @@
18
18
 
19
19
  import { clean } from "#util/clean";
20
20
  import { isValidDate } from "#util/date";
21
+ import { randomInt } from "#util/generate";
21
22
  import { err } from "#util/result";
22
23
 
23
24
  import type { ValidateResult, Validator } from "../types";
@@ -26,8 +27,8 @@ import type { ValidateResult, Validator } from "../types";
26
27
  const ALPHABET = "0123456789ABCDEFGHIJKLMN&OPQRSTUVWXYZ Ñ";
27
28
 
28
29
  const CHAR_MAP = new Map<string, number>();
29
- for (let i = 0; i < ALPHABET.length; i++) {
30
- CHAR_MAP.set(ALPHABET[i]!, i);
30
+ for (const [i, ch] of [...ALPHABET].entries()) {
31
+ CHAR_MAP.set(ch, i);
31
32
  }
32
33
 
33
34
  const compact = (value: string): string =>
@@ -48,7 +49,8 @@ const calcCheckDigit = (value: string): string => {
48
49
  const padded = value.length === 11 ? ` ${value}` : value;
49
50
  let sum = 0;
50
51
  for (let i = 0; i < 12; i++) {
51
- const ch = padded[i]!;
52
+ const ch = padded[i];
53
+ if (ch === undefined) continue;
52
54
  const v = CHAR_MAP.get(ch) ?? 0;
53
55
  sum += v * (13 - i);
54
56
  }
@@ -60,6 +62,8 @@ const calcCheckDigit = (value: string): string => {
60
62
 
61
63
  const PERSON_RE = /^[A-ZÑ&]{4}\d{6}[A-Z\d]{3}$/;
62
64
  const COMPANY_RE = /^[A-ZÑ&]{3}\d{6}[A-Z\d]{3}$/;
65
+ const PREFIX_ALPHA = "ABCDEFGHIJKLMNOPQRSTUVWXYZÑ&";
66
+ const RFC_ALNUM = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
63
67
 
64
68
  const validate = (value: string): ValidateResult => {
65
69
  const v = compact(value);
@@ -115,6 +119,32 @@ const validate = (value: string): ValidateResult => {
115
119
 
116
120
  const format = (value: string): string => compact(value);
117
121
 
122
+ const randomCharFrom = (chars: string): string =>
123
+ chars.charAt(randomInt(0, chars.length - 1));
124
+
125
+ /** Generate a random valid RFC. */
126
+ const generate = (): string => {
127
+ const isPerson = Math.random() < 0.5;
128
+ const prefixLength = isPerson ? 4 : 3;
129
+ let prefix = "";
130
+ for (let i = 0; i < prefixLength; i++) {
131
+ prefix += randomCharFrom(PREFIX_ALPHA);
132
+ }
133
+
134
+ const year = randomInt(2000, 2099);
135
+ const month = String(randomInt(1, 12)).padStart(2, "0");
136
+ const day = String(randomInt(1, 28)).padStart(2, "0");
137
+ let homoclave = "";
138
+ for (let i = 0; i < 2; i++) {
139
+ homoclave += randomCharFrom(RFC_ALNUM);
140
+ }
141
+
142
+ const body =
143
+ `${prefix}${String(year % 100).padStart(2, "0")}` +
144
+ `${month}${day}${homoclave}`;
145
+ return `${body}${calcCheckDigit(body)}`;
146
+ };
147
+
118
148
  /**
119
149
  * Mexican RFC (tax identification number).
120
150
  *
@@ -136,9 +166,10 @@ const rfc: Validator = {
136
166
  compact,
137
167
  format,
138
168
  validate,
169
+ generate,
139
170
  sourceUrl:
140
171
  "https://en.wikipedia.org/wiki/Tax_Identification_Number_(Mexico)",
141
172
  };
142
173
 
143
174
  export default rfc;
144
- export { compact, format, validate };
175
+ export { compact, format, generate, validate };
package/src/my/nric.ts CHANGED
@@ -14,6 +14,7 @@
14
14
 
15
15
  import { clean } from "#util/clean";
16
16
  import { isValidDate } from "#util/date";
17
+ import { randomInt } from "#util/generate";
17
18
  import { err } from "#util/result";
18
19
  import { isdigits } from "#util/strings";
19
20
 
@@ -211,8 +212,29 @@ const parse = (value: string): ParsedPersonId | null => {
211
212
  };
212
213
  };
213
214
 
215
+ const PB_CODES = [...VALID_PB_CODES];
216
+
217
+ /** Generate a random valid Malaysian NRIC. */
218
+ const generate = (): string => {
219
+ const year = randomInt(1950, 2024);
220
+ const month = String(randomInt(1, 12)).padStart(2, "0");
221
+ const day = String(randomInt(1, 28)).padStart(2, "0");
222
+ const pb =
223
+ PB_CODES[randomInt(0, PB_CODES.length - 1)] ?? "01";
224
+ const serial = String(randomInt(0, 999)).padStart(3, "0");
225
+ const isMale = Math.random() < 0.5;
226
+ const lastDigit = isMale
227
+ ? String(randomInt(0, 4) * 2 + 1)
228
+ : String(randomInt(0, 4) * 2);
229
+
230
+ return (
231
+ `${String(year % 100).padStart(2, "0")}` +
232
+ `${month}${day}${pb}${serial}${lastDigit}`
233
+ );
234
+ };
235
+
214
236
  /** Malaysian National Registration Identity Card. */
215
- const nric: Validator = {
237
+ const nric: Validator<ParsedPersonId> = {
216
238
  name: "Malaysian National Registration Identity Card Number",
217
239
  localName: "Nombor Kad Pengenalan",
218
240
  abbreviation: "NRIC",
@@ -228,8 +250,10 @@ const nric: Validator = {
228
250
  examples: ["770305021234", "880715141234"] as const,
229
251
  compact,
230
252
  format,
253
+ parse,
231
254
  validate,
255
+ generate,
232
256
  };
233
257
 
234
258
  export default nric;
235
- export { compact, format, parse, validate };
259
+ export { compact, format, generate, parse, validate };
package/src/nl/vat.ts CHANGED
@@ -2,10 +2,13 @@
2
2
  * BTW (Dutch VAT number).
3
3
  *
4
4
  * 12 characters: 9 digits + "B" + 2 digits.
5
- * Mod 97 check: convert "NL" + number to numeric
6
- * (N=23, L=21, B=11), mod 97 === 1.
7
5
  *
8
- * @see https://www.vatify.eu/netherlands-vat-number.html
6
+ * The current Dutch VAT identification number
7
+ * (btw-id) format is structural: `NL` + 9 digits
8
+ * + `B` + 2 digits. The official guidance explicitly
9
+ * states that the 9 digits are not derived from BSN
10
+ * and the final 2 digits are random.
11
+ *
9
12
  * @see https://business.gov.nl/regulations/using-checking-vat-numbers/
10
13
  */
11
14
 
@@ -30,42 +33,6 @@ const compact = (value: string): string => {
30
33
  return v;
31
34
  };
32
35
 
33
- /**
34
- * Compute mod 97 of a string containing digits and
35
- * uppercase letters (A=10, B=11, ..., Z=35).
36
- */
37
- const mod97 = (value: string): number => {
38
- let remainder = 0;
39
- for (const ch of value) {
40
- const code = ch.charCodeAt(0);
41
- if (code >= 48 && code <= 57) {
42
- // Digit
43
- remainder = (remainder * 10 + (code - 48)) % 97;
44
- } else if (code >= 65 && code <= 90) {
45
- // Letter: two decimal digits (A=10, ..., Z=35)
46
- const num = code - 55;
47
- remainder = (remainder * 100 + num) % 97;
48
- }
49
- }
50
- return remainder;
51
- };
52
-
53
- /**
54
- * Check if a 9-digit string is a valid BSN (Dutch citizen
55
- * identification number). Weights [9,8,7,6,5,4,3,2,-1],
56
- * sum % 11 === 0.
57
- */
58
- const bsnValid = (num: string): boolean => {
59
- if (!isdigits(num) || num.length !== 9) return false;
60
- if (Number.parseInt(num, 10) <= 0) return false;
61
- let sum = 0;
62
- for (let i = 0; i < 8; i++) {
63
- sum += (9 - i) * Number(num[i]);
64
- }
65
- sum -= Number(num[8]);
66
- return sum % 11 === 0;
67
- };
68
-
69
36
  const validate = (value: string): ValidateResult => {
70
37
  const v = compact(value);
71
38
  if (v.length !== 12) {
@@ -92,25 +59,6 @@ const validate = (value: string): ValidateResult => {
92
59
  "Dutch VAT number must end with 2 digits",
93
60
  );
94
61
  }
95
- if (Number.parseInt(v.slice(0, 9), 10) <= 0) {
96
- return err(
97
- "INVALID_FORMAT",
98
- "Dutch VAT number first 9 digits must be > 0",
99
- );
100
- }
101
- if (Number.parseInt(v.slice(10, 12), 10) <= 0) {
102
- return err(
103
- "INVALID_FORMAT",
104
- "Dutch VAT number last 2 digits must be > 0",
105
- );
106
- }
107
- // Valid if BSN check passes OR mod 97 check passes
108
- if (!bsnValid(v.slice(0, 9)) && mod97(`NL${v}`) !== 1) {
109
- return err(
110
- "INVALID_CHECKSUM",
111
- "Dutch VAT number checksum failed",
112
- );
113
- }
114
62
  return { valid: true, compact: v };
115
63
  };
116
64
 
@@ -118,12 +66,8 @@ const format = (value: string): string =>
118
66
  `NL${compact(value)}`;
119
67
 
120
68
  /** Generate a random valid Dutch VAT number. */
121
- const generate = (): string => {
122
- for (;;) {
123
- const c = randomDigits(9) + "B" + randomDigits(2);
124
- if (validate(c).valid) return c;
125
- }
126
- };
69
+ const generate = (): string =>
70
+ `${randomDigits(9)}B${randomDigits(2)}`;
127
71
 
128
72
  /** Dutch VAT Number. */
129
73
  const vat: Validator = {
@@ -134,8 +78,9 @@ const vat: Validator = {
134
78
  candidatePattern: "NL\\d{9}B\\d{2}",
135
79
  country: "NL",
136
80
  entityType: "company",
137
- sourceUrl: "https://www.belastingdienst.nl/",
138
- examples: ["123456789B13"] as const,
81
+ sourceUrl:
82
+ "https://business.gov.nl/regulations/using-checking-vat-numbers/",
83
+ examples: ["000099998B57"] as const,
139
84
  compact,
140
85
  format,
141
86
  validate,
@@ -172,7 +172,7 @@ const generate = (): string => {
172
172
  };
173
173
 
174
174
  /** Norwegian Birth Number. */
175
- const fodselsnummer: Validator = {
175
+ const fodselsnummer: Validator<ParsedPersonId> = {
176
176
  name: "Norwegian Birth Number",
177
177
  localName: "Fødselsnummer",
178
178
  abbreviation: "Fødselsnr",
@@ -184,6 +184,7 @@ const fodselsnummer: Validator = {
184
184
  examples: ["15108695088"] as const,
185
185
  compact,
186
186
  format,
187
+ parse,
187
188
  validate,
188
189
  generate,
189
190
  };
package/src/nz/ird.ts CHANGED
@@ -28,15 +28,15 @@ const SECONDARY_WEIGHTS = [7, 4, 3, 2, 5, 2, 7, 6] as const;
28
28
  const calcCheckDigit = (payload: string): number | null => {
29
29
  const padded = payload.padStart(8, "0");
30
30
  let sum = 0;
31
- for (let i = 0; i < 8; i++) {
32
- sum += PRIMARY_WEIGHTS[i]! * Number(padded[i]);
31
+ for (const [i, weight] of PRIMARY_WEIGHTS.entries()) {
32
+ sum += weight * Number(padded[i]);
33
33
  }
34
34
  let remainder = ((-sum % 11) + 11) % 11;
35
35
  if (remainder !== 10) return remainder;
36
36
 
37
37
  sum = 0;
38
- for (let i = 0; i < 8; i++) {
39
- sum += SECONDARY_WEIGHTS[i]! * Number(padded[i]);
38
+ for (const [i, weight] of SECONDARY_WEIGHTS.entries()) {
39
+ sum += weight * Number(padded[i]);
40
40
  }
41
41
  remainder = ((-sum % 11) + 11) % 11;
42
42
  if (remainder === 10) return null;
package/src/pa/ruc.ts CHANGED
@@ -111,19 +111,26 @@ const computeDV = (
111
111
  ): string | null => {
112
112
  const len = segments.length;
113
113
 
114
- if (
115
- len < 3 ||
116
- len > 4 ||
117
- (len === 4 && segments[1] !== "NT")
118
- ) {
114
+ if (len < 3 || len > 4) {
115
+ return null;
116
+ }
117
+ // The NT designation requires exactly 4 segments
118
+ // (xxNT-NT-xxx-xxx); any other 4-segment shape is
119
+ // unrecognized, and a 3-segment "NT" in position 1
120
+ // is malformed.
121
+ if ((len === 4) !== (segments[1] === "NT")) {
119
122
  return null;
120
123
  }
121
124
 
122
125
  let buf: string;
123
126
  let isOld = false;
124
127
 
128
+ // SAFETY: length check above ensures 3..4 segments.
129
+ // eslint-disable-next-line no-non-null-assertion
125
130
  const s0 = segments[0]!;
131
+ // eslint-disable-next-line no-non-null-assertion
126
132
  const s1 = segments[1]!;
133
+ // eslint-disable-next-line no-non-null-assertion
127
134
  const s2 = segments[2]!;
128
135
 
129
136
  if (raw[0] === "E") {
@@ -138,7 +145,9 @@ const computeDV = (
138
145
  z(5 - s2.length) +
139
146
  s2;
140
147
  } else if (s1 === "NT") {
141
- // NT designation (xxNT-xxx-xxx)
148
+ // NT designation (xxNT-NT-xxx-xxx)
149
+ // SAFETY: NT branch implies len === 4 (checked above).
150
+ // eslint-disable-next-line no-non-null-assertion
142
151
  const s3 = segments[3]!;
143
152
  const prefix = s0.slice(0, -2);
144
153
  buf =
@@ -220,8 +229,14 @@ const computeDV = (
220
229
  s1 +
221
230
  z(6 - s2.length) +
222
231
  s2;
232
+ // SAFETY: juridical branch zero-pads s0 to at least
233
+ // 10 chars (z(10 - s0.length)), so buf[3..6] are
234
+ // always defined.
223
235
  isOld =
224
- buf[3] === "0" && buf[4] === "0" && buf[5]! < "5";
236
+ buf[3] === "0" &&
237
+ buf[4] === "0" &&
238
+ // eslint-disable-next-line no-non-null-assertion
239
+ buf[5]! < "5";
225
240
  }
226
241
 
227
242
  // Apply legacy cross-reference for old format
@@ -270,7 +285,10 @@ const validate = (value: string): ValidateResult => {
270
285
  );
271
286
  }
272
287
 
288
+ // SAFETY: both DV regex capture groups are required.
289
+ // eslint-disable-next-line no-non-null-assertion
273
290
  const rucPart = dvMatch[1]!;
291
+ // eslint-disable-next-line no-non-null-assertion
274
292
  const dvPart = dvMatch[2]!;
275
293
 
276
294
  // The RUC part should be hyphen-separated segments
@@ -318,7 +336,10 @@ const format = (value: string): string => {
318
336
  const dvMatch = v.match(/^(.+?)-?\s*DV[:\s]*(\d{2})$/);
319
337
  if (!dvMatch) return v;
320
338
 
339
+ // SAFETY: both DV regex capture groups are required.
340
+ // eslint-disable-next-line no-non-null-assertion
321
341
  const rucPart = dvMatch[1]!.replace(/-+$/, "");
342
+ // eslint-disable-next-line no-non-null-assertion
322
343
  const dvPart = dvMatch[2]!;
323
344
  const segments = rucPart.split("-");
324
345
  return `${segments.join("-")} DV ${dvPart}`;
@@ -330,7 +351,10 @@ const generate = (): string => {
330
351
  const volume = String(randomInt(1, 9999));
331
352
  const folio = String(randomInt(1, 99999));
332
353
  const segments = [province, volume, folio];
333
- const dv = computeDV(segments, segments.join("-"))!;
354
+ const dv = computeDV(segments, segments.join("-"));
355
+ if (dv === null) {
356
+ throw new Error("Failed to compute Panama RUC DV");
357
+ }
334
358
  return `${segments.join("-")} DV${dv}`;
335
359
  };
336
360