@stll/stdnum 0.0.1 → 2.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 (808) hide show
  1. package/LICENSE +1 -5
  2. package/README.md +28 -4
  3. package/dist/_checksums/luhn.js.map +1 -1
  4. package/dist/_checksums/mod1110.js +1 -1
  5. package/dist/_checksums/mod1110.js.map +1 -1
  6. package/dist/_checksums/mod112.js +5 -5
  7. package/dist/_checksums/mod112.js.map +1 -1
  8. package/dist/_checksums/mod97.js.map +1 -1
  9. package/dist/_checksums/verhoeff.js.map +1 -1
  10. package/dist/_checksums/weighted-sum.js.map +1 -1
  11. package/dist/_util/arabic.js.map +1 -1
  12. package/dist/_util/clean.js.map +1 -1
  13. package/dist/_util/date.js.map +1 -1
  14. package/dist/_util/generate.d.ts +13 -1
  15. package/dist/_util/generate.js +23 -1
  16. package/dist/_util/generate.js.map +1 -1
  17. package/dist/_util/result.js.map +1 -1
  18. package/dist/_util/strings.js.map +1 -1
  19. package/dist/ad/mod.d.ts +3 -2
  20. package/dist/ad/nrt.d.ts +16 -2
  21. package/dist/ad/nrt.js +4 -16
  22. package/dist/ad/nrt.js.map +1 -1
  23. package/dist/ae/eid.d.ts +2 -3
  24. package/dist/ae/eid.js +2 -12
  25. package/dist/ae/eid.js.map +1 -1
  26. package/dist/ae/mod.d.ts +3 -2
  27. package/dist/ai/mod.d.ts +3 -2
  28. package/dist/ai/tin.d.ts +20 -2
  29. package/dist/ai/tin.js +1 -18
  30. package/dist/ai/tin.js.map +1 -1
  31. package/dist/al/mod.d.ts +3 -2
  32. package/dist/al/nipt.d.ts +13 -2
  33. package/dist/al/nipt.js +1 -11
  34. package/dist/al/nipt.js.map +1 -1
  35. package/dist/am/mod.d.ts +3 -2
  36. package/dist/am/tin.d.ts +16 -2
  37. package/dist/am/tin.js +1 -14
  38. package/dist/am/tin.js.map +1 -1
  39. package/dist/ar/cbu.d.ts +2 -3
  40. package/dist/ar/cbu.js +2 -10
  41. package/dist/ar/cbu.js.map +1 -1
  42. package/dist/ar/cuit.d.ts +2 -3
  43. package/dist/ar/cuit.js +13 -29
  44. package/dist/ar/cuit.js.map +1 -1
  45. package/dist/ar/dni.d.ts +11 -2
  46. package/dist/ar/dni.js +1 -9
  47. package/dist/ar/dni.js.map +1 -1
  48. package/dist/ar/mod.d.ts +3 -2
  49. package/dist/at/businessid.d.ts +10 -2
  50. package/dist/at/businessid.js +1 -8
  51. package/dist/at/businessid.js.map +1 -1
  52. package/dist/at/mod.d.ts +3 -2
  53. package/dist/at/tin.d.ts +2 -3
  54. package/dist/at/tin.js +1 -11
  55. package/dist/at/tin.js.map +1 -1
  56. package/dist/at/uid.d.ts +2 -3
  57. package/dist/at/uid.js +2 -10
  58. package/dist/at/uid.js.map +1 -1
  59. package/dist/at/vnr.d.ts +5 -4
  60. package/dist/at/vnr.js +23 -16
  61. package/dist/at/vnr.js.map +1 -1
  62. package/dist/au/abn.d.ts +2 -3
  63. package/dist/au/abn.js +1 -10
  64. package/dist/au/abn.js.map +1 -1
  65. package/dist/au/acn.d.ts +2 -3
  66. package/dist/au/acn.js +1 -10
  67. package/dist/au/acn.js.map +1 -1
  68. package/dist/au/mod.d.ts +3 -2
  69. package/dist/au/tfn.d.ts +2 -3
  70. package/dist/au/tfn.js +1 -10
  71. package/dist/au/tfn.js.map +1 -1
  72. package/dist/az/mod.d.ts +3 -2
  73. package/dist/az/voen.d.ts +2 -3
  74. package/dist/az/voen.js +1 -14
  75. package/dist/az/voen.js.map +1 -1
  76. package/dist/ba/jmbg.d.ts +2 -3
  77. package/dist/ba/jmbg.js +4 -11
  78. package/dist/ba/jmbg.js.map +1 -1
  79. package/dist/ba/mod.d.ts +3 -2
  80. package/dist/bd/mod.d.ts +3 -2
  81. package/dist/bd/nid.d.ts +23 -2
  82. package/dist/bd/nid.js +11 -32
  83. package/dist/bd/nid.js.map +1 -1
  84. package/dist/be/bis.d.ts +2 -3
  85. package/dist/be/bis.js +1 -11
  86. package/dist/be/bis.js.map +1 -1
  87. package/dist/be/mod.d.ts +3 -2
  88. package/dist/be/nn.d.ts +2 -3
  89. package/dist/be/nn.js +1 -11
  90. package/dist/be/nn.js.map +1 -1
  91. package/dist/be/vat.d.ts +2 -3
  92. package/dist/be/vat.js +1 -9
  93. package/dist/be/vat.js.map +1 -1
  94. package/dist/bg/egn.d.ts +2 -3
  95. package/dist/bg/egn.js +5 -13
  96. package/dist/bg/egn.js.map +1 -1
  97. package/dist/bg/mod.d.ts +3 -2
  98. package/dist/bg/pnf.d.ts +2 -3
  99. package/dist/bg/pnf.js +1 -8
  100. package/dist/bg/pnf.js.map +1 -1
  101. package/dist/bg/vat.d.ts +2 -3
  102. package/dist/bg/vat.js +3 -9
  103. package/dist/bg/vat.js.map +1 -1
  104. package/dist/bh/cpr.d.ts +5 -4
  105. package/dist/bh/cpr.js +9 -19
  106. package/dist/bh/cpr.js.map +1 -1
  107. package/dist/bh/mod.d.ts +3 -2
  108. package/dist/bic.d.ts +13 -2
  109. package/dist/bic.js +14 -26
  110. package/dist/bic.js.map +1 -1
  111. package/dist/br/cnpj.d.ts +2 -3
  112. package/dist/br/cnpj.js +1 -20
  113. package/dist/br/cnpj.js.map +1 -1
  114. package/dist/br/cpf.d.ts +2 -3
  115. package/dist/br/cpf.js +1 -14
  116. package/dist/br/cpf.js.map +1 -1
  117. package/dist/br/mod.d.ts +3 -2
  118. package/dist/by/mod.d.ts +3 -2
  119. package/dist/by/unp.d.ts +2 -3
  120. package/dist/by/unp.js +9 -17
  121. package/dist/by/unp.js.map +1 -1
  122. package/dist/bz/mod.d.ts +3 -2
  123. package/dist/bz/tin.d.ts +5 -4
  124. package/dist/bz/tin.js +10 -27
  125. package/dist/bz/tin.js.map +1 -1
  126. package/dist/ca/bn.d.ts +2 -3
  127. package/dist/ca/bn.js +2 -10
  128. package/dist/ca/bn.js.map +1 -1
  129. package/dist/ca/mod.d.ts +3 -2
  130. package/dist/ca/sin.d.ts +2 -3
  131. package/dist/ca/sin.js +2 -10
  132. package/dist/ca/sin.js.map +1 -1
  133. package/dist/ch/mod.d.ts +3 -2
  134. package/dist/ch/ssn.d.ts +2 -3
  135. package/dist/ch/ssn.js +1 -9
  136. package/dist/ch/ssn.js.map +1 -1
  137. package/dist/ch/uid.d.ts +2 -3
  138. package/dist/ch/uid.js +1 -9
  139. package/dist/ch/uid.js.map +1 -1
  140. package/dist/ch/vat.d.ts +2 -3
  141. package/dist/ch/vat.js +1 -9
  142. package/dist/ch/vat.js.map +1 -1
  143. package/dist/cl/mod.d.ts +3 -2
  144. package/dist/cl/rut.d.ts +2 -3
  145. package/dist/cl/rut.js +2 -15
  146. package/dist/cl/rut.js.map +1 -1
  147. package/dist/cn/mod.d.ts +3 -2
  148. package/dist/cn/ric.d.ts +2 -3
  149. package/dist/cn/ric.js +4 -14
  150. package/dist/cn/ric.js.map +1 -1
  151. package/dist/cn/uscc.d.ts +2 -3
  152. package/dist/cn/uscc.js +8 -16
  153. package/dist/cn/uscc.js.map +1 -1
  154. package/dist/co/mod.d.ts +3 -2
  155. package/dist/co/nit.d.ts +2 -3
  156. package/dist/co/nit.js +6 -13
  157. package/dist/co/nit.js.map +1 -1
  158. package/dist/cr/cpf.d.ts +18 -2
  159. package/dist/cr/cpf.js +5 -19
  160. package/dist/cr/cpf.js.map +1 -1
  161. package/dist/cr/mod.d.ts +3 -2
  162. package/dist/creditcard.d.ts +2 -3
  163. package/dist/creditcard.js +5 -14
  164. package/dist/creditcard.js.map +1 -1
  165. package/dist/cu/mod.d.ts +3 -2
  166. package/dist/cu/ni.d.ts +5 -4
  167. package/dist/cu/ni.js +13 -18
  168. package/dist/cu/ni.js.map +1 -1
  169. package/dist/cy/mod.d.ts +3 -2
  170. package/dist/cy/vat.d.ts +2 -3
  171. package/dist/cy/vat.js +1 -8
  172. package/dist/cy/vat.js.map +1 -1
  173. package/dist/cz/dic.d.ts +2 -3
  174. package/dist/cz/dic.js +7 -13
  175. package/dist/cz/dic.js.map +1 -1
  176. package/dist/cz/ico.d.ts +2 -3
  177. package/dist/cz/ico.js +2 -10
  178. package/dist/cz/ico.js.map +1 -1
  179. package/dist/cz/mod.d.ts +3 -2
  180. package/dist/cz/rc.d.ts +2 -3
  181. package/dist/cz/rc.js +22 -21
  182. package/dist/cz/rc.js.map +1 -1
  183. package/dist/de/handelsreg.d.ts +19 -2
  184. package/dist/de/handelsreg.js +10 -26
  185. package/dist/de/handelsreg.js.map +1 -1
  186. package/dist/de/idnr.d.ts +2 -3
  187. package/dist/de/idnr.js +2 -12
  188. package/dist/de/idnr.js.map +1 -1
  189. package/dist/de/mod.d.ts +3 -2
  190. package/dist/de/stnr.d.ts +17 -3
  191. package/dist/de/stnr.js +1 -15
  192. package/dist/de/stnr.js.map +1 -1
  193. package/dist/de/svnr.d.ts +2 -3
  194. package/dist/de/svnr.js +4 -13
  195. package/dist/de/svnr.js.map +1 -1
  196. package/dist/de/vat.d.ts +2 -3
  197. package/dist/de/vat.js +2 -11
  198. package/dist/de/vat.js.map +1 -1
  199. package/dist/dk/cpr.d.ts +5 -4
  200. package/dist/dk/cpr.js +14 -12
  201. package/dist/dk/cpr.js.map +1 -1
  202. package/dist/dk/cvr.d.ts +2 -3
  203. package/dist/dk/cvr.js +2 -15
  204. package/dist/dk/cvr.js.map +1 -1
  205. package/dist/dk/mod.d.ts +3 -2
  206. package/dist/dk/vat.d.ts +2 -3
  207. package/dist/dk/vat.js +3 -13
  208. package/dist/dk/vat.js.map +1 -1
  209. package/dist/do/mod.d.ts +3 -2
  210. package/dist/do/rnc.d.ts +2 -3
  211. package/dist/do/rnc.js +3 -17
  212. package/dist/do/rnc.js.map +1 -1
  213. package/dist/ec/mod.d.ts +3 -2
  214. package/dist/ec/ruc.d.ts +2 -3
  215. package/dist/ec/ruc.js +2 -19
  216. package/dist/ec/ruc.js.map +1 -1
  217. package/dist/ee/ik.d.ts +2 -3
  218. package/dist/ee/ik.js +6 -15
  219. package/dist/ee/ik.js.map +1 -1
  220. package/dist/ee/mod.d.ts +3 -2
  221. package/dist/ee/registrikood.d.ts +2 -3
  222. package/dist/ee/registrikood.js +9 -15
  223. package/dist/ee/registrikood.js.map +1 -1
  224. package/dist/ee/vat.d.ts +2 -3
  225. package/dist/ee/vat.js +2 -10
  226. package/dist/ee/vat.js.map +1 -1
  227. package/dist/eg/mod.d.ts +3 -2
  228. package/dist/eg/tn.d.ts +11 -2
  229. package/dist/eg/tn.js +1 -9
  230. package/dist/eg/tn.js.map +1 -1
  231. package/dist/es/cif.d.ts +2 -3
  232. package/dist/es/cif.js +3 -12
  233. package/dist/es/cif.js.map +1 -1
  234. package/dist/es/dni.d.ts +2 -3
  235. package/dist/es/dni.js +1 -9
  236. package/dist/es/dni.js.map +1 -1
  237. package/dist/es/mod.d.ts +3 -2
  238. package/dist/es/nie.d.ts +2 -3
  239. package/dist/es/nie.js +14 -22
  240. package/dist/es/nie.js.map +1 -1
  241. package/dist/es/nss.d.ts +2 -3
  242. package/dist/es/nss.js +1 -16
  243. package/dist/es/nss.js.map +1 -1
  244. package/dist/es/vat.d.ts +2 -3
  245. package/dist/es/vat.js +5 -10
  246. package/dist/es/vat.js.map +1 -1
  247. package/dist/eu/mod.d.ts +3 -2
  248. package/dist/eu/vat.d.ts +5 -4
  249. package/dist/eu/vat.js +44 -2
  250. package/dist/eu/vat.js.map +1 -1
  251. package/dist/fi/hetu.d.ts +5 -4
  252. package/dist/fi/hetu.js +19 -12
  253. package/dist/fi/hetu.js.map +1 -1
  254. package/dist/fi/mod.d.ts +3 -2
  255. package/dist/fi/vat.d.ts +2 -3
  256. package/dist/fi/vat.js +3 -10
  257. package/dist/fi/vat.js.map +1 -1
  258. package/dist/fi/ytunnus.d.ts +2 -3
  259. package/dist/fi/ytunnus.js +2 -10
  260. package/dist/fi/ytunnus.js.map +1 -1
  261. package/dist/fr/mod.d.ts +3 -2
  262. package/dist/fr/nif.d.ts +2 -3
  263. package/dist/fr/nif.js +1 -9
  264. package/dist/fr/nif.js.map +1 -1
  265. package/dist/fr/nir.d.ts +2 -3
  266. package/dist/fr/nir.js +3 -14
  267. package/dist/fr/nir.js.map +1 -1
  268. package/dist/fr/siren.d.ts +2 -3
  269. package/dist/fr/siren.js +2 -10
  270. package/dist/fr/siren.js.map +1 -1
  271. package/dist/fr/siret.d.ts +2 -3
  272. package/dist/fr/siret.js +2 -11
  273. package/dist/fr/siret.js.map +1 -1
  274. package/dist/fr/tva.d.ts +2 -3
  275. package/dist/fr/tva.js +2 -12
  276. package/dist/fr/tva.js.map +1 -1
  277. package/dist/gb/mod.d.ts +3 -2
  278. package/dist/gb/nino.d.ts +12 -2
  279. package/dist/gb/nino.js +8 -17
  280. package/dist/gb/nino.js.map +1 -1
  281. package/dist/gb/sedol.d.ts +2 -3
  282. package/dist/gb/sedol.js +9 -18
  283. package/dist/gb/sedol.js.map +1 -1
  284. package/dist/gb/utr.d.ts +2 -3
  285. package/dist/gb/utr.js +2 -10
  286. package/dist/gb/utr.js.map +1 -1
  287. package/dist/gb/vat.d.ts +2 -3
  288. package/dist/gb/vat.js +2 -11
  289. package/dist/gb/vat.js.map +1 -1
  290. package/dist/ge/mod.d.ts +3 -2
  291. package/dist/ge/pin.d.ts +14 -2
  292. package/dist/ge/pin.js +1 -12
  293. package/dist/ge/pin.js.map +1 -1
  294. package/dist/gh/mod.d.ts +3 -2
  295. package/dist/gh/tin.d.ts +2 -3
  296. package/dist/gh/tin.js +10 -24
  297. package/dist/gh/tin.js.map +1 -1
  298. package/dist/gr/amka.d.ts +2 -3
  299. package/dist/gr/amka.js +3 -11
  300. package/dist/gr/amka.js.map +1 -1
  301. package/dist/gr/mod.d.ts +3 -2
  302. package/dist/gr/vat.d.ts +2 -3
  303. package/dist/gr/vat.js +1 -10
  304. package/dist/gr/vat.js.map +1 -1
  305. package/dist/gt/mod.d.ts +3 -2
  306. package/dist/gt/nit.d.ts +2 -3
  307. package/dist/gt/nit.js +6 -14
  308. package/dist/gt/nit.js.map +1 -1
  309. package/dist/hk/hkid.d.ts +2 -3
  310. package/dist/hk/hkid.js +2 -10
  311. package/dist/hk/hkid.js.map +1 -1
  312. package/dist/hk/mod.d.ts +3 -2
  313. package/dist/hr/mod.d.ts +3 -2
  314. package/dist/hr/vat.d.ts +2 -3
  315. package/dist/hr/vat.js +2 -9
  316. package/dist/hr/vat.js.map +1 -1
  317. package/dist/hu/mod.d.ts +3 -2
  318. package/dist/hu/vat.d.ts +2 -3
  319. package/dist/hu/vat.js +3 -10
  320. package/dist/hu/vat.js.map +1 -1
  321. package/dist/iban.d.ts +2 -3
  322. package/dist/iban.js +2 -8
  323. package/dist/iban.js.map +1 -1
  324. package/dist/id/mod.d.ts +3 -2
  325. package/dist/id/npwp.d.ts +2 -3
  326. package/dist/id/npwp.js +3 -13
  327. package/dist/id/npwp.js.map +1 -1
  328. package/dist/ie/mod.d.ts +3 -2
  329. package/dist/ie/pps.d.ts +2 -3
  330. package/dist/ie/pps.js +1 -10
  331. package/dist/ie/pps.js.map +1 -1
  332. package/dist/ie/vat.d.ts +2 -3
  333. package/dist/ie/vat.js +1 -9
  334. package/dist/ie/vat.js.map +1 -1
  335. package/dist/il/idnr.d.ts +2 -3
  336. package/dist/il/idnr.js +2 -9
  337. package/dist/il/idnr.js.map +1 -1
  338. package/dist/il/mod.d.ts +3 -2
  339. package/dist/in/aadhaar.d.ts +2 -3
  340. package/dist/in/aadhaar.js +2 -12
  341. package/dist/in/aadhaar.js.map +1 -1
  342. package/dist/in/gstin.d.ts +2 -3
  343. package/dist/in/gstin.js +4 -11
  344. package/dist/in/gstin.js.map +1 -1
  345. package/dist/in/mod.d.ts +3 -2
  346. package/dist/in/pan.d.ts +15 -2
  347. package/dist/in/pan.js +3 -15
  348. package/dist/in/pan.js.map +1 -1
  349. package/dist/index.d.ts +2 -2
  350. package/dist/index.js +6 -6
  351. package/dist/iq/mod.d.ts +3 -2
  352. package/dist/iq/nid.d.ts +16 -2
  353. package/dist/iq/nid.js +2 -15
  354. package/dist/iq/nid.js.map +1 -1
  355. package/dist/ir/mod.d.ts +3 -2
  356. package/dist/ir/nid.d.ts +2 -3
  357. package/dist/ir/nid.js +2 -24
  358. package/dist/ir/nid.js.map +1 -1
  359. package/dist/is/kennitala.d.ts +2 -3
  360. package/dist/is/kennitala.js +2 -13
  361. package/dist/is/kennitala.js.map +1 -1
  362. package/dist/is/mod.d.ts +3 -2
  363. package/dist/is/vsk.d.ts +9 -2
  364. package/dist/is/vsk.js +1 -7
  365. package/dist/is/vsk.js.map +1 -1
  366. package/dist/isin.d.ts +2 -3
  367. package/dist/isin.js +11 -21
  368. package/dist/isin.js.map +1 -1
  369. package/dist/it/codicefiscale.d.ts +5 -4
  370. package/dist/it/codicefiscale.js +27 -23
  371. package/dist/it/codicefiscale.js.map +1 -1
  372. package/dist/it/iva.d.ts +2 -3
  373. package/dist/it/iva.js +2 -9
  374. package/dist/it/iva.js.map +1 -1
  375. package/dist/it/mod.d.ts +3 -2
  376. package/dist/jp/cn.d.ts +2 -3
  377. package/dist/jp/cn.js +1 -9
  378. package/dist/jp/cn.js.map +1 -1
  379. package/dist/jp/mod.d.ts +3 -2
  380. package/dist/jp/mynumber.d.ts +2 -3
  381. package/dist/jp/mynumber.js +1 -10
  382. package/dist/jp/mynumber.js.map +1 -1
  383. package/dist/kr/brn.d.ts +11 -2
  384. package/dist/kr/brn.js +1 -9
  385. package/dist/kr/brn.js.map +1 -1
  386. package/dist/kr/mod.d.ts +3 -2
  387. package/dist/kr/rrn.d.ts +5 -4
  388. package/dist/kr/rrn.js +15 -13
  389. package/dist/kr/rrn.js.map +1 -1
  390. package/dist/kw/civil.d.ts +6 -5
  391. package/dist/kw/civil.js +20 -17
  392. package/dist/kw/civil.js.map +1 -1
  393. package/dist/kw/mod.d.ts +3 -2
  394. package/dist/kz/iin.d.ts +2 -3
  395. package/dist/kz/iin.js +4 -18
  396. package/dist/kz/iin.js.map +1 -1
  397. package/dist/kz/mod.d.ts +3 -2
  398. package/dist/lei.d.ts +2 -3
  399. package/dist/lei.js +2 -11
  400. package/dist/lei.js.map +1 -1
  401. package/dist/li/mod.d.ts +3 -2
  402. package/dist/li/peid.d.ts +11 -2
  403. package/dist/li/peid.js +1 -9
  404. package/dist/li/peid.js.map +1 -1
  405. package/dist/lk/mod.d.ts +3 -2
  406. package/dist/lk/nic.d.ts +5 -4
  407. package/dist/lk/nic.js +16 -16
  408. package/dist/lk/nic.js.map +1 -1
  409. package/dist/lt/asmens.d.ts +2 -3
  410. package/dist/lt/asmens.js +2 -1
  411. package/dist/lt/asmens.js.map +1 -1
  412. package/dist/lt/mod.d.ts +3 -2
  413. package/dist/lt/vat.d.ts +2 -3
  414. package/dist/lt/vat.js +2 -10
  415. package/dist/lt/vat.js.map +1 -1
  416. package/dist/lu/mod.d.ts +3 -2
  417. package/dist/lu/vat.d.ts +2 -3
  418. package/dist/lu/vat.js +2 -8
  419. package/dist/lu/vat.js.map +1 -1
  420. package/dist/luhn.d.ts +2 -3
  421. package/dist/luhn.js +9 -9
  422. package/dist/luhn.js.map +1 -1
  423. package/dist/lv/mod.d.ts +3 -2
  424. package/dist/lv/vat.d.ts +2 -3
  425. package/dist/lv/vat.js +19 -18
  426. package/dist/lv/vat.js.map +1 -1
  427. package/dist/ma/ice.d.ts +2 -3
  428. package/dist/ma/ice.js +2 -11
  429. package/dist/ma/ice.js.map +1 -1
  430. package/dist/ma/mod.d.ts +3 -2
  431. package/dist/mc/mod.d.ts +3 -2
  432. package/dist/mc/tva.d.ts +2 -3
  433. package/dist/mc/tva.js +1 -0
  434. package/dist/mc/tva.js.map +1 -1
  435. package/dist/md/idno.d.ts +2 -3
  436. package/dist/md/idno.js +1 -10
  437. package/dist/md/idno.js.map +1 -1
  438. package/dist/md/mod.d.ts +3 -2
  439. package/dist/me/mod.d.ts +3 -2
  440. package/dist/me/pib.d.ts +2 -3
  441. package/dist/me/pib.js +2 -10
  442. package/dist/me/pib.js.map +1 -1
  443. package/dist/mk/edb.d.ts +2 -3
  444. package/dist/mk/edb.js +2 -11
  445. package/dist/mk/edb.js.map +1 -1
  446. package/dist/mk/mod.d.ts +3 -2
  447. package/dist/mt/mod.d.ts +3 -2
  448. package/dist/mt/vat.d.ts +2 -3
  449. package/dist/mt/vat.js +3 -10
  450. package/dist/mt/vat.js.map +1 -1
  451. package/dist/mu/brn.d.ts +23 -2
  452. package/dist/mu/brn.js +1 -21
  453. package/dist/mu/brn.js.map +1 -1
  454. package/dist/mu/mod.d.ts +3 -2
  455. package/dist/mx/clabe.d.ts +2 -3
  456. package/dist/mx/clabe.js +1 -14
  457. package/dist/mx/clabe.js.map +1 -1
  458. package/dist/mx/curp.d.ts +5 -4
  459. package/dist/mx/curp.js +27 -19
  460. package/dist/mx/curp.js.map +1 -1
  461. package/dist/mx/mod.d.ts +3 -2
  462. package/dist/mx/rfc.d.ts +5 -4
  463. package/dist/mx/rfc.js +23 -20
  464. package/dist/mx/rfc.js.map +1 -1
  465. package/dist/my/mod.d.ts +3 -2
  466. package/dist/my/nric.d.ts +5 -4
  467. package/dist/my/nric.js +18 -16
  468. package/dist/my/nric.js.map +1 -1
  469. package/dist/ng/mod.d.ts +3 -2
  470. package/dist/ng/nin.d.ts +13 -2
  471. package/dist/ng/nin.js +1 -11
  472. package/dist/ng/nin.js.map +1 -1
  473. package/dist/ni/mod.d.ts +3 -2
  474. package/dist/ni/ruc.d.ts +2 -3
  475. package/dist/ni/ruc.js +1 -23
  476. package/dist/ni/ruc.js.map +1 -1
  477. package/dist/nl/bsn.d.ts +2 -3
  478. package/dist/nl/bsn.js +2 -10
  479. package/dist/nl/bsn.js.map +1 -1
  480. package/dist/nl/kvk.d.ts +9 -2
  481. package/dist/nl/kvk.js +1 -7
  482. package/dist/nl/kvk.js.map +1 -1
  483. package/dist/nl/mod.d.ts +3 -2
  484. package/dist/nl/vat.d.ts +2 -3
  485. package/dist/nl/vat.js +4 -50
  486. package/dist/nl/vat.js.map +1 -1
  487. package/dist/no/fodselsnummer.d.ts +2 -3
  488. package/dist/no/fodselsnummer.js +3 -10
  489. package/dist/no/fodselsnummer.js.map +1 -1
  490. package/dist/no/mod.d.ts +3 -2
  491. package/dist/no/mva.d.ts +2 -3
  492. package/dist/no/mva.js +1 -8
  493. package/dist/no/mva.js.map +1 -1
  494. package/dist/no/orgnr.d.ts +2 -3
  495. package/dist/no/orgnr.js +2 -8
  496. package/dist/no/orgnr.js.map +1 -1
  497. package/dist/nz/ird.d.ts +2 -3
  498. package/dist/nz/ird.js +4 -13
  499. package/dist/nz/ird.js.map +1 -1
  500. package/dist/nz/mod.d.ts +3 -2
  501. package/dist/pa/mod.d.ts +3 -2
  502. package/dist/pa/ruc.d.ts +2 -3
  503. package/dist/pa/ruc.js +4 -19
  504. package/dist/pa/ruc.js.map +1 -1
  505. package/dist/patterns.d.ts +8 -10
  506. package/dist/patterns.js +83 -28
  507. package/dist/patterns.js.map +1 -1
  508. package/dist/pe/mod.d.ts +3 -2
  509. package/dist/pe/ruc.d.ts +2 -3
  510. package/dist/pe/ruc.js +10 -24
  511. package/dist/pe/ruc.js.map +1 -1
  512. package/dist/ph/mod.d.ts +3 -2
  513. package/dist/ph/philid.d.ts +14 -2
  514. package/dist/ph/philid.js +1 -12
  515. package/dist/ph/philid.js.map +1 -1
  516. package/dist/pk/cnic.d.ts +14 -2
  517. package/dist/pk/cnic.js +1 -12
  518. package/dist/pk/cnic.js.map +1 -1
  519. package/dist/pk/mod.d.ts +3 -2
  520. package/dist/pl/mod.d.ts +3 -2
  521. package/dist/pl/nip.d.ts +2 -3
  522. package/dist/pl/nip.js +2 -10
  523. package/dist/pl/nip.js.map +1 -1
  524. package/dist/pl/pesel.d.ts +2 -3
  525. package/dist/pl/pesel.js +4 -13
  526. package/dist/pl/pesel.js.map +1 -1
  527. package/dist/pl/regon.d.ts +2 -3
  528. package/dist/pl/regon.js +2 -11
  529. package/dist/pl/regon.js.map +1 -1
  530. package/dist/pt/cc.d.ts +2 -3
  531. package/dist/pt/cc.js +7 -13
  532. package/dist/pt/cc.js.map +1 -1
  533. package/dist/pt/mod.d.ts +3 -2
  534. package/dist/pt/vat.d.ts +2 -3
  535. package/dist/pt/vat.js +3 -11
  536. package/dist/pt/vat.js.map +1 -1
  537. package/dist/ro/cnp.d.ts +2 -3
  538. package/dist/ro/cnp.js +5 -14
  539. package/dist/ro/cnp.js.map +1 -1
  540. package/dist/ro/mod.d.ts +3 -2
  541. package/dist/ro/vat.d.ts +2 -3
  542. package/dist/ro/vat.js +13 -11
  543. package/dist/ro/vat.js.map +1 -1
  544. package/dist/rs/mod.d.ts +3 -2
  545. package/dist/rs/pib.d.ts +2 -3
  546. package/dist/rs/pib.js +1 -9
  547. package/dist/rs/pib.js.map +1 -1
  548. package/dist/ru/inn.d.ts +2 -3
  549. package/dist/ru/inn.js +2 -12
  550. package/dist/ru/inn.js.map +1 -1
  551. package/dist/ru/mod.d.ts +3 -2
  552. package/dist/se/mod.d.ts +3 -2
  553. package/dist/se/orgnr.d.ts +2 -3
  554. package/dist/se/orgnr.js +2 -9
  555. package/dist/se/orgnr.js.map +1 -1
  556. package/dist/se/personnummer.d.ts +2 -3
  557. package/dist/se/personnummer.js +8 -15
  558. package/dist/se/personnummer.js.map +1 -1
  559. package/dist/se/vat.d.ts +2 -3
  560. package/dist/se/vat.js +3 -10
  561. package/dist/se/vat.js.map +1 -1
  562. package/dist/sg/mod.d.ts +3 -2
  563. package/dist/sg/uen.d.ts +2 -3
  564. package/dist/sg/uen.js +47 -70
  565. package/dist/sg/uen.js.map +1 -1
  566. package/dist/si/emso.d.ts +2 -3
  567. package/dist/si/emso.js +12 -14
  568. package/dist/si/emso.js.map +1 -1
  569. package/dist/si/mod.d.ts +3 -2
  570. package/dist/si/vat.d.ts +2 -3
  571. package/dist/si/vat.js +3 -12
  572. package/dist/si/vat.js.map +1 -1
  573. package/dist/sk/dic.d.ts +2 -3
  574. package/dist/sk/dic.js +1 -0
  575. package/dist/sk/dic.js.map +1 -1
  576. package/dist/sk/ico.d.ts +2 -3
  577. package/dist/sk/ico.js +1 -0
  578. package/dist/sk/ico.js.map +1 -1
  579. package/dist/sk/mod.d.ts +3 -2
  580. package/dist/sk/rc.d.ts +4 -5
  581. package/dist/sk/rc.js +6 -2
  582. package/dist/sk/rc.js.map +1 -1
  583. package/dist/th/mod.d.ts +3 -2
  584. package/dist/th/tin.d.ts +2 -3
  585. package/dist/th/tin.js +1 -11
  586. package/dist/th/tin.js.map +1 -1
  587. package/dist/tr/mod.d.ts +3 -2
  588. package/dist/tr/tckimlik.d.ts +2 -3
  589. package/dist/tr/tckimlik.js +1 -20
  590. package/dist/tr/tckimlik.js.map +1 -1
  591. package/dist/tr/vkn.d.ts +2 -3
  592. package/dist/tr/vkn.js +1 -20
  593. package/dist/tr/vkn.js.map +1 -1
  594. package/dist/tw/mod.d.ts +3 -2
  595. package/dist/tw/ubn.d.ts +2 -3
  596. package/dist/tw/ubn.js +2 -12
  597. package/dist/tw/ubn.js.map +1 -1
  598. package/dist/types.d.ts +56 -21
  599. package/dist/ua/edrpou.d.ts +2 -3
  600. package/dist/ua/edrpou.js +5 -12
  601. package/dist/ua/edrpou.js.map +1 -1
  602. package/dist/ua/mod.d.ts +3 -2
  603. package/dist/us/ein.d.ts +12 -2
  604. package/dist/us/ein.js +88 -97
  605. package/dist/us/ein.js.map +1 -1
  606. package/dist/us/itin.d.ts +13 -2
  607. package/dist/us/itin.js +4 -15
  608. package/dist/us/itin.js.map +1 -1
  609. package/dist/us/mod.d.ts +3 -2
  610. package/dist/us/rtn.d.ts +2 -3
  611. package/dist/us/rtn.js +11 -23
  612. package/dist/us/rtn.js.map +1 -1
  613. package/dist/us/ssn.d.ts +13 -2
  614. package/dist/us/ssn.js +1 -11
  615. package/dist/us/ssn.js.map +1 -1
  616. package/dist/uy/mod.d.ts +3 -2
  617. package/dist/uy/rut.d.ts +2 -3
  618. package/dist/uy/rut.js +2 -15
  619. package/dist/uy/rut.js.map +1 -1
  620. package/dist/ve/mod.d.ts +3 -2
  621. package/dist/ve/rif.d.ts +2 -3
  622. package/dist/ve/rif.js +13 -26
  623. package/dist/ve/rif.js.map +1 -1
  624. package/dist/vn/mod.d.ts +3 -2
  625. package/dist/vn/mst.d.ts +2 -3
  626. package/dist/vn/mst.js +1 -12
  627. package/dist/vn/mst.js.map +1 -1
  628. package/dist/za/idnr.d.ts +2 -3
  629. package/dist/za/idnr.js +4 -16
  630. package/dist/za/idnr.js.map +1 -1
  631. package/dist/za/mod.d.ts +3 -2
  632. package/package.json +34 -21
  633. package/src/_checksums/mod1110.ts +1 -1
  634. package/src/_checksums/mod112.ts +7 -6
  635. package/src/_util/generate.ts +38 -0
  636. package/src/ad/nrt.ts +19 -7
  637. package/src/ae/eid.ts +7 -3
  638. package/src/ai/tin.ts +7 -3
  639. package/src/al/nipt.ts +7 -3
  640. package/src/am/tin.ts +7 -3
  641. package/src/ar/cbu.ts +9 -5
  642. package/src/ar/cuit.ts +22 -10
  643. package/src/ar/dni.ts +7 -3
  644. package/src/at/businessid.ts +7 -3
  645. package/src/at/tin.ts +7 -3
  646. package/src/at/uid.ts +7 -3
  647. package/src/at/vnr.ts +62 -12
  648. package/src/au/abn.ts +7 -3
  649. package/src/au/acn.ts +7 -3
  650. package/src/au/tfn.ts +7 -3
  651. package/src/az/voen.ts +9 -3
  652. package/src/ba/jmbg.ts +9 -7
  653. package/src/bd/nid.ts +11 -7
  654. package/src/be/bis.ts +8 -4
  655. package/src/be/nn.ts +7 -3
  656. package/src/be/vat.ts +7 -3
  657. package/src/bg/egn.ts +19 -10
  658. package/src/bg/pnf.ts +7 -3
  659. package/src/bg/vat.ts +8 -3
  660. package/src/bh/cpr.ts +17 -4
  661. package/src/bic.ts +26 -20
  662. package/src/br/cnpj.ts +7 -3
  663. package/src/br/cpf.ts +7 -3
  664. package/src/by/unp.ts +19 -9
  665. package/src/bz/tin.ts +19 -4
  666. package/src/ca/bn.ts +7 -3
  667. package/src/ca/sin.ts +7 -3
  668. package/src/ch/ssn.ts +7 -3
  669. package/src/ch/uid.ts +7 -3
  670. package/src/ch/vat.ts +9 -5
  671. package/src/cl/rut.ts +11 -5
  672. package/src/cn/ric.ts +11 -7
  673. package/src/cn/uscc.ts +27 -11
  674. package/src/co/nit.ts +14 -6
  675. package/src/cr/cpf.ts +11 -6
  676. package/src/creditcard.ts +10 -6
  677. package/src/cu/ni.ts +28 -7
  678. package/src/cy/vat.ts +7 -3
  679. package/src/cz/dic.ts +9 -4
  680. package/src/cz/ico.ts +7 -3
  681. package/src/cz/rc.ts +61 -29
  682. package/src/de/handelsreg.ts +21 -6
  683. package/src/de/idnr.ts +10 -3
  684. package/src/de/stnr.ts +11 -3
  685. package/src/de/svnr.ts +14 -5
  686. package/src/de/vat.ts +7 -3
  687. package/src/dk/cpr.ts +23 -8
  688. package/src/dk/cvr.ts +7 -3
  689. package/src/dk/vat.ts +8 -3
  690. package/src/do/rnc.ts +9 -5
  691. package/src/ec/ruc.ts +9 -5
  692. package/src/ee/ik.ts +21 -10
  693. package/src/ee/registrikood.ts +12 -7
  694. package/src/ee/vat.ts +7 -3
  695. package/src/eg/tn.ts +7 -3
  696. package/src/es/cif.ts +10 -7
  697. package/src/es/dni.ts +7 -3
  698. package/src/es/nie.ts +18 -9
  699. package/src/es/nss.ts +7 -3
  700. package/src/es/vat.ts +13 -5
  701. package/src/eu/vat.ts +55 -6
  702. package/src/fi/hetu.ts +26 -8
  703. package/src/fi/vat.ts +8 -3
  704. package/src/fi/ytunnus.ts +7 -3
  705. package/src/fr/nif.ts +7 -3
  706. package/src/fr/nir.ts +9 -7
  707. package/src/fr/siren.ts +7 -3
  708. package/src/fr/siret.ts +7 -3
  709. package/src/fr/tva.ts +7 -3
  710. package/src/gb/nino.ts +18 -13
  711. package/src/gb/sedol.ts +21 -15
  712. package/src/gb/utr.ts +7 -3
  713. package/src/gb/vat.ts +7 -3
  714. package/src/ge/pin.ts +7 -3
  715. package/src/gh/tin.ts +11 -6
  716. package/src/gr/amka.ts +7 -3
  717. package/src/gr/vat.ts +7 -3
  718. package/src/gt/nit.ts +13 -6
  719. package/src/hk/hkid.ts +9 -4
  720. package/src/hr/vat.ts +7 -3
  721. package/src/hu/vat.ts +8 -3
  722. package/src/iban.ts +7 -3
  723. package/src/id/npwp.ts +7 -3
  724. package/src/ie/pps.ts +7 -3
  725. package/src/ie/vat.ts +7 -3
  726. package/src/il/idnr.ts +7 -3
  727. package/src/in/aadhaar.ts +7 -3
  728. package/src/in/gstin.ts +10 -4
  729. package/src/in/pan.ts +15 -6
  730. package/src/index.ts +6 -0
  731. package/src/iq/nid.ts +7 -3
  732. package/src/ir/nid.ts +7 -3
  733. package/src/is/kennitala.ts +7 -3
  734. package/src/is/vsk.ts +7 -3
  735. package/src/isin.ts +17 -7
  736. package/src/it/codicefiscale.ts +49 -7
  737. package/src/it/iva.ts +7 -3
  738. package/src/jp/cn.ts +7 -3
  739. package/src/jp/mynumber.ts +7 -3
  740. package/src/kr/brn.ts +7 -3
  741. package/src/kr/rrn.ts +27 -8
  742. package/src/kw/civil.ts +31 -14
  743. package/src/kz/iin.ts +9 -8
  744. package/src/lei.ts +7 -3
  745. package/src/li/peid.ts +7 -3
  746. package/src/lk/nic.ts +31 -8
  747. package/src/lt/asmens.ts +8 -4
  748. package/src/lt/vat.ts +8 -3
  749. package/src/lu/vat.ts +8 -3
  750. package/src/luhn.ts +10 -3
  751. package/src/lv/vat.ts +53 -23
  752. package/src/ma/ice.ts +7 -3
  753. package/src/mc/tva.ts +9 -5
  754. package/src/md/idno.ts +7 -3
  755. package/src/me/pib.ts +7 -3
  756. package/src/mk/edb.ts +7 -3
  757. package/src/mt/vat.ts +8 -3
  758. package/src/mu/brn.ts +7 -3
  759. package/src/mx/clabe.ts +7 -3
  760. package/src/mx/curp.ts +47 -10
  761. package/src/mx/rfc.ts +42 -7
  762. package/src/my/nric.ts +33 -8
  763. package/src/ng/nin.ts +7 -3
  764. package/src/ni/ruc.ts +7 -3
  765. package/src/nl/bsn.ts +7 -3
  766. package/src/nl/kvk.ts +7 -3
  767. package/src/nl/vat.ts +18 -69
  768. package/src/no/fodselsnummer.ts +12 -7
  769. package/src/no/mva.ts +9 -5
  770. package/src/no/orgnr.ts +7 -3
  771. package/src/nz/ird.ts +12 -8
  772. package/src/pa/ruc.ts +39 -11
  773. package/src/patterns.ts +184 -46
  774. package/src/pe/ruc.ts +13 -10
  775. package/src/ph/philid.ts +7 -3
  776. package/src/pk/cnic.ts +9 -3
  777. package/src/pl/nip.ts +7 -3
  778. package/src/pl/pesel.ts +9 -7
  779. package/src/pl/regon.ts +7 -3
  780. package/src/pt/cc.ts +13 -7
  781. package/src/pt/vat.ts +8 -3
  782. package/src/ro/cnp.ts +16 -8
  783. package/src/ro/vat.ts +8 -3
  784. package/src/rs/pib.ts +7 -3
  785. package/src/ru/inn.ts +9 -5
  786. package/src/se/orgnr.ts +7 -3
  787. package/src/se/personnummer.ts +35 -32
  788. package/src/se/vat.ts +8 -3
  789. package/src/sg/uen.ts +44 -23
  790. package/src/si/emso.ts +37 -11
  791. package/src/si/vat.ts +8 -3
  792. package/src/sk/dic.ts +8 -4
  793. package/src/sk/ico.ts +3 -2
  794. package/src/sk/rc.ts +17 -4
  795. package/src/th/tin.ts +7 -3
  796. package/src/tr/tckimlik.ts +7 -3
  797. package/src/tr/vkn.ts +7 -3
  798. package/src/tw/ubn.ts +9 -5
  799. package/src/types.ts +69 -7
  800. package/src/ua/edrpou.ts +10 -5
  801. package/src/us/ein.ts +95 -91
  802. package/src/us/itin.ts +14 -11
  803. package/src/us/rtn.ts +32 -19
  804. package/src/us/ssn.ts +7 -3
  805. package/src/uy/rut.ts +9 -5
  806. package/src/ve/rif.ts +19 -12
  807. package/src/vn/mst.ts +7 -3
  808. package/src/za/idnr.ts +9 -7
package/dist/nz/ird.js CHANGED
@@ -3,16 +3,6 @@ import { randomDigits } from "../_util/generate.js";
3
3
  import { err } from "../_util/result.js";
4
4
  import { isdigits } from "../_util/strings.js";
5
5
  //#region src/nz/ird.ts
6
- /**
7
- * IRD number (New Zealand Inland Revenue Department
8
- * number, Te Tari Take).
9
- *
10
- * 8 or 9 digit number with a weighted checksum.
11
- * Uses primary weights, falling back to secondary
12
- * weights if the primary check yields 10.
13
- *
14
- * @see https://www.ird.govt.nz/
15
- */
16
6
  const PRIMARY_WEIGHTS = [
17
7
  3,
18
8
  2,
@@ -41,11 +31,11 @@ const SECONDARY_WEIGHTS = [
41
31
  const calcCheckDigit = (payload) => {
42
32
  const padded = payload.padStart(8, "0");
43
33
  let sum = 0;
44
- for (let i = 0; i < 8; i++) sum += PRIMARY_WEIGHTS[i] * Number(padded[i]);
34
+ for (const [i, weight] of PRIMARY_WEIGHTS.entries()) sum += weight * Number(padded[i]);
45
35
  let remainder = (-sum % 11 + 11) % 11;
46
36
  if (remainder !== 10) return remainder;
47
37
  sum = 0;
48
- for (let i = 0; i < 8; i++) sum += SECONDARY_WEIGHTS[i] * Number(padded[i]);
38
+ for (const [i, weight] of SECONDARY_WEIGHTS.entries()) sum += weight * Number(padded[i]);
49
39
  remainder = (-sum % 11 + 11) % 11;
50
40
  if (remainder === 10) return null;
51
41
  return remainder;
@@ -63,7 +53,7 @@ const validate = (value) => {
63
53
  if (num < 1e7 || num >= 15e7) return err("INVALID_COMPONENT", "IRD number out of valid range");
64
54
  const expected = calcCheckDigit(v.slice(0, -1));
65
55
  if (expected === null) return err("INVALID_COMPONENT", "IRD number cannot have a valid check digit");
66
- if (expected !== Number(v[v.length - 1])) return err("INVALID_CHECKSUM", "IRD check digit does not match");
56
+ if (expected !== Number(v.at(-1))) return err("INVALID_CHECKSUM", "IRD check digit does not match");
67
57
  return {
68
58
  valid: true,
69
59
  compact: v
@@ -88,6 +78,7 @@ const ird = {
88
78
  abbreviation: "IRD",
89
79
  aliases: ["IRD number", "tax number NZ"],
90
80
  candidatePattern: "\\d{8,9}",
81
+ scope: "country",
91
82
  country: "NZ",
92
83
  entityType: "any",
93
84
  compact,
@@ -1 +1 @@
1
- {"version":3,"file":"ird.js","names":[],"sources":["../../src/nz/ird.ts"],"sourcesContent":["/**\n * IRD number (New Zealand Inland Revenue Department\n * number, Te Tari Take).\n *\n * 8 or 9 digit number with a weighted checksum.\n * Uses primary weights, falling back to secondary\n * weights if the primary check yields 10.\n *\n * @see https://www.ird.govt.nz/\n */\n\nimport { clean } from \"#util/clean\";\nimport { randomDigits } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst PRIMARY_WEIGHTS = [3, 2, 7, 6, 5, 4, 3, 2] as const;\n\nconst SECONDARY_WEIGHTS = [7, 4, 3, 2, 5, 2, 7, 6] as const;\n\n/**\n * Calculate the check digit for an IRD number.\n * The input should be the number without the check\n * digit, zero-padded to 8 digits.\n */\nconst calcCheckDigit = (payload: string): number | null => {\n const padded = payload.padStart(8, \"0\");\n let sum = 0;\n for (let i = 0; i < 8; i++) {\n sum += PRIMARY_WEIGHTS[i]! * Number(padded[i]);\n }\n let remainder = ((-sum % 11) + 11) % 11;\n if (remainder !== 10) return remainder;\n\n sum = 0;\n for (let i = 0; i < 8; i++) {\n sum += SECONDARY_WEIGHTS[i]! * Number(padded[i]);\n }\n remainder = ((-sum % 11) + 11) % 11;\n if (remainder === 10) return null;\n return remainder;\n};\n\nconst compact = (value: string): string => {\n let v = clean(value, \" -\").toUpperCase();\n if (v.startsWith(\"NZ\")) {\n v = v.slice(2);\n }\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 8 && v.length !== 9) {\n return err(\n \"INVALID_LENGTH\",\n \"IRD number must be 8 or 9 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"IRD number must contain only digits\",\n );\n }\n const num = Number(v);\n if (num < 10_000_000 || num >= 150_000_000) {\n return err(\n \"INVALID_COMPONENT\",\n \"IRD number out of valid range\",\n );\n }\n const payload = v.slice(0, -1);\n const expected = calcCheckDigit(payload);\n if (expected === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"IRD number cannot have a valid check digit\",\n );\n }\n if (expected !== Number(v[v.length - 1])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"IRD check digit does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n const last6 = v.slice(-6);\n const prefix = v.slice(0, -6);\n return `${prefix}-${last6.slice(0, 3)}-${last6.slice(3)}`;\n};\n\n/** Generate a random valid NZ IRD number. */\nconst generate = (): string => {\n for (;;) {\n const c = randomDigits(9);\n if (validate(c).valid) return c;\n }\n};\n\n/** New Zealand IRD Number. */\nconst ird: Validator = {\n name: \"IRD Number\",\n localName: \"IRD Number\",\n abbreviation: \"IRD\",\n aliases: [\"IRD number\", \"tax number NZ\"] as const,\n candidatePattern: \"\\\\d{8,9}\",\n country: \"NZ\",\n entityType: \"any\",\n compact,\n format,\n validate,\n description:\n \"New Zealand Inland Revenue Department number\",\n sourceUrl: \"https://www.ird.govt.nz/\",\n lengths: [8, 9] as const,\n examples: [\"49091850\", \"136410132\"] as const,\n generate,\n};\n\nexport default ird;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAM,kBAAkB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;AAEhD,MAAM,oBAAoB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;;;;;;AAOlD,MAAM,kBAAkB,YAAmC;CACzD,MAAM,SAAS,QAAQ,SAAS,GAAG,IAAI;CACvC,IAAI,MAAM;AACV,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IACrB,QAAO,gBAAgB,KAAM,OAAO,OAAO,GAAG;CAEhD,IAAI,aAAc,CAAC,MAAM,KAAM,MAAM;AACrC,KAAI,cAAc,GAAI,QAAO;AAE7B,OAAM;AACN,MAAK,IAAI,IAAI,GAAG,IAAI,GAAG,IACrB,QAAO,kBAAkB,KAAM,OAAO,OAAO,GAAG;AAElD,cAAc,CAAC,MAAM,KAAM,MAAM;AACjC,KAAI,cAAc,GAAI,QAAO;AAC7B,QAAO;;AAGT,MAAM,WAAW,UAA0B;CACzC,IAAI,IAAI,MAAM,OAAO,KAAK,CAAC,aAAa;AACxC,KAAI,EAAE,WAAW,KAAK,CACpB,KAAI,EAAE,MAAM,EAAE;AAEhB,QAAO;;AAGT,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,KAAK,EAAE,WAAW,EACjC,QAAO,IACL,kBACA,mCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,sCACD;CAEH,MAAM,MAAM,OAAO,EAAE;AACrB,KAAI,MAAM,OAAc,OAAO,KAC7B,QAAO,IACL,qBACA,gCACD;CAGH,MAAM,WAAW,eADD,EAAE,MAAM,GAAG,GAAG,CACU;AACxC,KAAI,aAAa,KACf,QAAO,IACL,qBACA,6CACD;AAEH,KAAI,aAAa,OAAO,EAAE,EAAE,SAAS,GAAG,CACtC,QAAO,IACL,oBACA,iCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;CACxB,MAAM,QAAQ,EAAE,MAAM,GAAG;AAEzB,QAAO,GADQ,EAAE,MAAM,GAAG,GAAG,CACZ,GAAG,MAAM,MAAM,GAAG,EAAE,CAAC,GAAG,MAAM,MAAM,EAAE;;;AAIzD,MAAM,iBAAyB;AAC7B,UAAS;EACP,MAAM,IAAI,aAAa,EAAE;AACzB,MAAI,SAAS,EAAE,CAAC,MAAO,QAAO;;;;AAKlC,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,cAAc,gBAAgB;CACxC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ;CACA;CACA;CACA,aACE;CACF,WAAW;CACX,SAAS,CAAC,GAAG,EAAE;CACf,UAAU,CAAC,YAAY,YAAY;CACnC;CACD"}
1
+ {"version":3,"file":"ird.js","names":[],"sources":["../../src/nz/ird.ts"],"sourcesContent":["/**\n * IRD number (New Zealand Inland Revenue Department\n * number, Te Tari Take).\n *\n * 8 or 9 digit number with a weighted checksum.\n * Uses primary weights, falling back to secondary\n * weights if the primary check yields 10.\n *\n * @see https://www.ird.govt.nz/\n */\n\nimport type {\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport { clean } from \"#util/clean\";\nimport { randomDigits } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nconst PRIMARY_WEIGHTS = [3, 2, 7, 6, 5, 4, 3, 2] as const;\n\nconst SECONDARY_WEIGHTS = [7, 4, 3, 2, 5, 2, 7, 6] as const;\n\n/**\n * Calculate the check digit for an IRD number.\n * The input should be the number without the check\n * digit, zero-padded to 8 digits.\n */\nconst calcCheckDigit = (payload: string): number | null => {\n const padded = payload.padStart(8, \"0\");\n let sum = 0;\n for (const [i, weight] of PRIMARY_WEIGHTS.entries()) {\n sum += weight * Number(padded[i]);\n }\n let remainder = ((-sum % 11) + 11) % 11;\n if (remainder !== 10) return remainder;\n\n sum = 0;\n for (const [i, weight] of SECONDARY_WEIGHTS.entries()) {\n sum += weight * Number(padded[i]);\n }\n remainder = ((-sum % 11) + 11) % 11;\n if (remainder === 10) return null;\n return remainder;\n};\n\nconst compact = (value: string): string => {\n let v = clean(value, \" -\").toUpperCase();\n if (v.startsWith(\"NZ\")) {\n v = v.slice(2);\n }\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 8 && v.length !== 9) {\n return err(\n \"INVALID_LENGTH\",\n \"IRD number must be 8 or 9 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"IRD number must contain only digits\",\n );\n }\n const num = Number(v);\n if (num < 10_000_000 || num >= 150_000_000) {\n return err(\n \"INVALID_COMPONENT\",\n \"IRD number out of valid range\",\n );\n }\n const payload = v.slice(0, -1);\n const expected = calcCheckDigit(payload);\n if (expected === null) {\n return err(\n \"INVALID_COMPONENT\",\n \"IRD number cannot have a valid check digit\",\n );\n }\n if (expected !== Number(v.at(-1))) {\n return err(\n \"INVALID_CHECKSUM\",\n \"IRD check digit does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n const last6 = v.slice(-6);\n const prefix = v.slice(0, -6);\n return `${prefix}-${last6.slice(0, 3)}-${last6.slice(3)}`;\n};\n\n/** Generate a random valid NZ IRD number. */\nconst generate = (): string => {\n for (;;) {\n const c = randomDigits(9);\n if (validate(c).valid) return c;\n }\n};\n\n/** New Zealand IRD Number. */\nconst ird: CountryValidator<\"NZ\"> = {\n name: \"IRD Number\",\n localName: \"IRD Number\",\n abbreviation: \"IRD\",\n aliases: [\"IRD number\", \"tax number NZ\"] as const,\n candidatePattern: \"\\\\d{8,9}\",\n scope: \"country\",\n country: \"NZ\",\n entityType: \"any\",\n compact,\n format,\n validate,\n description:\n \"New Zealand Inland Revenue Department number\",\n sourceUrl: \"https://www.ird.govt.nz/\",\n lengths: [8, 9] as const,\n examples: [\"49091850\", \"136410132\"] as const,\n generate,\n};\n\nexport default ird;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;AAqBA,MAAM,kBAAkB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;AAAC;AAE/C,MAAM,oBAAoB;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;AAAC;;;;;;AAOjD,MAAM,kBAAkB,YAAmC;CACzD,MAAM,SAAS,QAAQ,SAAS,GAAG,GAAG;CACtC,IAAI,MAAM;CACV,KAAK,MAAM,CAAC,GAAG,WAAW,gBAAgB,QAAQ,GAChD,OAAO,SAAS,OAAO,OAAO,EAAE;CAElC,IAAI,aAAc,CAAC,MAAM,KAAM,MAAM;CACrC,IAAI,cAAc,IAAI,OAAO;CAE7B,MAAM;CACN,KAAK,MAAM,CAAC,GAAG,WAAW,kBAAkB,QAAQ,GAClD,OAAO,SAAS,OAAO,OAAO,EAAE;CAElC,aAAc,CAAC,MAAM,KAAM,MAAM;CACjC,IAAI,cAAc,IAAI,OAAO;CAC7B,OAAO;AACT;AAEA,MAAM,WAAW,UAA0B;CACzC,IAAI,IAAI,MAAM,OAAO,IAAI,EAAE,YAAY;CACvC,IAAI,EAAE,WAAW,IAAI,GACnB,IAAI,EAAE,MAAM,CAAC;CAEf,OAAO;AACT;AAEA,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,KAAK;CACvB,IAAI,EAAE,WAAW,KAAK,EAAE,WAAW,GACjC,OAAO,IACL,kBACA,kCACF;CAEF,IAAI,CAAC,SAAS,CAAC,GACb,OAAO,IACL,kBACA,qCACF;CAEF,MAAM,MAAM,OAAO,CAAC;CACpB,IAAI,MAAM,OAAc,OAAO,MAC7B,OAAO,IACL,qBACA,+BACF;CAGF,MAAM,WAAW,eADD,EAAE,MAAM,GAAG,EACW,CAAC;CACvC,IAAI,aAAa,MACf,OAAO,IACL,qBACA,4CACF;CAEF,IAAI,aAAa,OAAO,EAAE,GAAG,EAAE,CAAC,GAC9B,OAAO,IACL,oBACA,gCACF;CAEF,OAAO;EAAE,OAAO;EAAM,SAAS;CAAE;AACnC;AAEA,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,KAAK;CACvB,MAAM,QAAQ,EAAE,MAAM,EAAE;CAExB,OAAO,GADQ,EAAE,MAAM,GAAG,EACX,EAAE,GAAG,MAAM,MAAM,GAAG,CAAC,EAAE,GAAG,MAAM,MAAM,CAAC;AACxD;;AAGA,MAAM,iBAAyB;CAC7B,SAAS;EACP,MAAM,IAAI,aAAa,CAAC;EACxB,IAAI,SAAS,CAAC,EAAE,OAAO,OAAO;CAChC;AACF;;AAGA,MAAM,MAA8B;CAClC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,cAAc,eAAe;CACvC,kBAAkB;CAClB,OAAO;CACP,SAAS;CACT,YAAY;CACZ;CACA;CACA;CACA,aACE;CACF,WAAW;CACX,SAAS,CAAC,GAAG,CAAC;CACd,UAAU,CAAC,YAAY,WAAW;CAClC;AACF"}
package/dist/nz/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import ird from "./ird.js";
3
2
 
4
3
  //#region src/nz/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { ird };
6
+ }
6
7
  //#endregion
7
8
  export { ird, mod_d_exports };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/pa/mod.d.ts CHANGED
@@ -1,8 +1,9 @@
1
- import { __exportAll } from "../_virtual/_rolldown/runtime.js";
2
1
  import ruc from "./ruc.js";
3
2
 
4
3
  //#region src/pa/mod.d.ts
5
-
4
+ declare namespace mod_d_exports {
5
+ export { ruc };
6
+ }
6
7
  //#endregion
7
8
  export { mod_d_exports, ruc };
8
9
  //# sourceMappingURL=mod.d.ts.map
package/dist/pa/ruc.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { ValidateResult, Validator } from "../types.js";
1
+ import { CountryValidator, ValidateResult } from "../types.js";
2
2
 
3
3
  //#region src/pa/ruc.d.ts
4
-
5
4
  /**
6
5
  * Compact: normalize Unicode, collapse whitespace,
7
6
  * strip colons around DV, and uppercase. Keeps
@@ -17,7 +16,7 @@ declare const format: (value: string) => string;
17
16
  /** Generate a random valid Panama RUC (natural person). */
18
17
  declare const generate: () => string;
19
18
  /** Panama tax identification number. */
20
- declare const ruc: Validator;
19
+ declare const ruc: CountryValidator<"PA">;
21
20
  //#endregion
22
21
  export { compact, ruc as default, format, generate, validate };
23
22
  //# sourceMappingURL=ruc.d.ts.map
package/dist/pa/ruc.js CHANGED
@@ -3,24 +3,6 @@ import { randomInt } from "../_util/generate.js";
3
3
  import { err } from "../_util/result.js";
4
4
  //#region src/pa/ruc.ts
5
5
  /**
6
- * RUC (Registro Único de Contribuyente, Panama
7
- * tax number).
8
- *
9
- * The RUC identifies taxpayers in Panama. It comes
10
- * in several formats depending on entity type
11
- * (natural person, legal entity, foreigner, etc.)
12
- * and is always accompanied by a two-digit check
13
- * code called the "dígito verificador" (DV).
14
- *
15
- * The DV is computed by encoding the hyphen-separated
16
- * RUC into a 20-position numeric buffer, then applying
17
- * a double weighted-sum mod 11 algorithm. The encoding
18
- * and legacy-format handling follow the official ANIP/DGI
19
- * specification.
20
- *
21
- * @see https://dgi.mef.gob.pa/
22
- */
23
- /**
24
6
  * Legacy cross-reference table: maps two-digit codes
25
7
  * at positions 5–6 of old-format juridical RUCs.
26
8
  */
@@ -96,7 +78,8 @@ const digitDV = (buf, isOldFormat) => {
96
78
  */
97
79
  const computeDV = (segments, raw) => {
98
80
  const len = segments.length;
99
- if (len < 3 || len > 4 || len === 4 && segments[1] !== "NT") return null;
81
+ if (len < 3 || len > 4) return null;
82
+ if (len === 4 !== (segments[1] === "NT")) return null;
100
83
  let buf;
101
84
  let isOld = false;
102
85
  const s0 = segments[0];
@@ -175,6 +158,7 @@ const generate = () => {
175
158
  String(randomInt(1, 99999))
176
159
  ];
177
160
  const dv = computeDV(segments, segments.join("-"));
161
+ if (dv === null) throw new Error("Failed to compute Panama RUC DV");
178
162
  return `${segments.join("-")} DV${dv}`;
179
163
  };
180
164
  /** Panama tax identification number. */
@@ -184,6 +168,7 @@ const ruc = {
184
168
  abbreviation: "RUC",
185
169
  aliases: ["RUC"],
186
170
  candidatePattern: "\\d{1,2}-?\\d{1,4}-?\\d{1,6}",
171
+ scope: "country",
187
172
  country: "PA",
188
173
  entityType: "any",
189
174
  description: "Tax identifier issued by Panama's DGI",
@@ -1 +1 @@
1
- {"version":3,"file":"ruc.js","names":[],"sources":["../../src/pa/ruc.ts"],"sourcesContent":["/**\n * RUC (Registro Único de Contribuyente, Panama\n * tax number).\n *\n * The RUC identifies taxpayers in Panama. It comes\n * in several formats depending on entity type\n * (natural person, legal entity, foreigner, etc.)\n * and is always accompanied by a two-digit check\n * code called the \"dígito verificador\" (DV).\n *\n * The DV is computed by encoding the hyphen-separated\n * RUC into a 20-position numeric buffer, then applying\n * a double weighted-sum mod 11 algorithm. The encoding\n * and legacy-format handling follow the official ANIP/DGI\n * specification.\n *\n * @see https://dgi.mef.gob.pa/\n */\n\nimport { clean } from \"#util/clean\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\n/**\n * Legacy cross-reference table: maps two-digit codes\n * at positions 5–6 of old-format juridical RUCs.\n */\nconst LEGACY_MAP: Record<string, string> = {\n \"00\": \"00\",\n \"10\": \"01\",\n \"11\": \"02\",\n \"12\": \"03\",\n \"13\": \"04\",\n \"14\": \"05\",\n \"15\": \"06\",\n \"16\": \"07\",\n \"17\": \"08\",\n \"18\": \"09\",\n \"19\": \"01\",\n \"20\": \"02\",\n \"21\": \"03\",\n \"22\": \"04\",\n \"23\": \"07\",\n \"24\": \"08\",\n \"25\": \"09\",\n \"26\": \"02\",\n \"27\": \"03\",\n \"28\": \"04\",\n \"29\": \"05\",\n \"30\": \"06\",\n \"31\": \"07\",\n \"32\": \"08\",\n \"33\": \"09\",\n \"34\": \"01\",\n \"35\": \"02\",\n \"36\": \"03\",\n \"37\": \"04\",\n \"38\": \"05\",\n \"39\": \"06\",\n \"40\": \"07\",\n \"41\": \"08\",\n \"42\": \"09\",\n \"43\": \"01\",\n \"44\": \"02\",\n \"45\": \"03\",\n \"46\": \"04\",\n \"47\": \"05\",\n \"48\": \"06\",\n \"49\": \"07\",\n};\n\n/** Prepend n leading zeros. */\nconst z = (n: number): string => \"0\".repeat(Math.max(0, n));\n\n/**\n * Compute a single DV digit via weighted sum mod 11.\n * Weights start at 2 and increment, applied\n * right-to-left. For old-format juridical RUCs,\n * weight 12 is skipped (jumps from 11 to 13).\n */\nconst digitDV = (\n buf: string,\n isOldFormat: boolean,\n): number => {\n let weight = 2;\n let sum = 0;\n for (let i = buf.length - 1; i >= 0; i--) {\n if (isOldFormat && weight === 12) {\n weight += 1; // skip 12, continue from 13\n }\n sum += weight * (buf.charCodeAt(i) - 48);\n weight += 1;\n }\n const r = sum % 11;\n return r > 1 ? 11 - r : 0;\n};\n\n/**\n * Encode the RUC segments into a numeric buffer\n * according to entity type, then compute the two\n * DV digits.\n *\n * Returns the two-digit DV string, or null if the\n * format is unrecognized.\n */\nconst computeDV = (\n segments: readonly string[],\n raw: string,\n): string | null => {\n const len = segments.length;\n\n if (\n len < 3 ||\n len > 4 ||\n (len === 4 && segments[1] !== \"NT\")\n ) {\n return null;\n }\n\n let buf: string;\n let isOld = false;\n\n const s0 = segments[0]!;\n const s1 = segments[1]!;\n const s2 = segments[2]!;\n\n if (raw[0] === \"E\") {\n // Foreign person (E-xxx-xxx)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"50\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s1 === \"NT\") {\n // NT designation (xxNT-xxx-xxx)\n const s3 = segments[3]!;\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"43\" +\n z(3 - s2.length) +\n s2 +\n z(5 - s3.length) +\n s3;\n } else if (s0.endsWith(\"AV\")) {\n // AV suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"15\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.endsWith(\"PI\")) {\n // PI suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"79\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0 === \"PE\") {\n // PE prefix (foreign-born)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"75\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (raw[0] === \"N\") {\n // N prefix (naturalized citizen)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"40\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.length >= 1 && s0.length <= 2) {\n // Natural person (province 1–2 digits)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(2 - s0.length) +\n s0 +\n \"00\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else {\n // Juridical (company)\n buf =\n z(10 - s0.length) +\n s0 +\n z(4 - s1.length) +\n s1 +\n z(6 - s2.length) +\n s2;\n isOld =\n buf[3] === \"0\" && buf[4] === \"0\" && buf[5]! < \"5\";\n }\n\n // Apply legacy cross-reference for old format\n if (isOld) {\n const key = buf.slice(5, 7);\n const mapped = LEGACY_MAP[key] ?? key;\n buf = buf.slice(0, 5) + mapped + buf.slice(7);\n }\n\n const dv1 = digitDV(buf, isOld);\n const dv2 = digitDV(\n buf + String.fromCharCode(48 + dv1),\n isOld,\n );\n\n return `${dv1}${dv2}`;\n};\n\n/**\n * Compact: normalize Unicode, collapse whitespace,\n * strip colons around DV, and uppercase. Keeps\n * hyphens (structural separators) and a single\n * space before \"DV\".\n */\nconst compact = (value: string): string => {\n let v = clean(value, \"\").trim().toUpperCase();\n // Normalize DV portion: \" DV: 49\" -> \" DV49\"\n v = v.replace(/\\s*DV[:\\s]*(\\d{2})$/, \" DV$1\");\n // Collapse any remaining internal whitespace\n v = v.replace(/\\s+/g, \" \");\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n // Extract the DV portion: \"...DV49\", \"...DV:49\",\n // or \"... DV 49\"\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n\n if (!dvMatch) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must include DV \" +\n \"(dígito verificador)\",\n );\n }\n\n const rucPart = dvMatch[1]!;\n const dvPart = dvMatch[2]!;\n\n // The RUC part should be hyphen-separated segments\n const segments = rucPart.replace(/-+$/, \"\").split(\"-\");\n\n if (segments.length < 3 || segments.length > 4) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must have 3–4 \" +\n \"hyphen-separated segments\",\n );\n }\n\n if (segments.some((s) => s.length === 0)) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC segments must not be empty\",\n );\n }\n\n const computed = computeDV(segments, rucPart);\n if (computed === null) {\n return err(\n \"INVALID_FORMAT\",\n \"Unrecognized Panama RUC format\",\n );\n }\n\n if (computed !== dvPart) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Panama RUC dígito verificador \" + \"does not match\",\n );\n }\n\n const compactForm = segments.join(\"-\") + \" DV\" + dvPart;\n return { valid: true, compact: compactForm };\n};\n\n/**\n * Format: display as segments with \" DV XX\".\n */\nconst format = (value: string): string => {\n const v = compact(value);\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n if (!dvMatch) return v;\n\n const rucPart = dvMatch[1]!.replace(/-+$/, \"\");\n const dvPart = dvMatch[2]!;\n const segments = rucPart.split(\"-\");\n return `${segments.join(\"-\")} DV ${dvPart}`;\n};\n\n/** Generate a random valid Panama RUC (natural person). */\nconst generate = (): string => {\n const province = String(randomInt(1, 13));\n const volume = String(randomInt(1, 9999));\n const folio = String(randomInt(1, 99999));\n const segments = [province, volume, folio];\n const dv = computeDV(segments, segments.join(\"-\"))!;\n return `${segments.join(\"-\")} DV${dv}`;\n};\n\n/** Panama tax identification number. */\nconst ruc: Validator = {\n name: \"Tax Identification Number\",\n localName: \"Registro Único de Contribuyente\",\n abbreviation: \"RUC\",\n aliases: [\"RUC\"] as const,\n candidatePattern: \"\\\\d{1,2}-?\\\\d{1,4}-?\\\\d{1,6}\",\n country: \"PA\",\n entityType: \"any\",\n description: \"Tax identifier issued by Panama's DGI\",\n sourceUrl: \"https://dgi.mef.gob.pa/\",\n examples: [\n \"1-184-921 DV49\",\n \"2588017-1-831938 DV20\",\n ] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default ruc;\nexport { compact, format, validate, generate };\n"],"mappings":"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"}
1
+ {"version":3,"file":"ruc.js","names":[],"sources":["../../src/pa/ruc.ts"],"sourcesContent":["/**\n * RUC (Registro Único de Contribuyente, Panama\n * tax number).\n *\n * The RUC identifies taxpayers in Panama. It comes\n * in several formats depending on entity type\n * (natural person, legal entity, foreigner, etc.)\n * and is always accompanied by a two-digit check\n * code called the \"dígito verificador\" (DV).\n *\n * The DV is computed by encoding the hyphen-separated\n * RUC into a 20-position numeric buffer, then applying\n * a double weighted-sum mod 11 algorithm. The encoding\n * and legacy-format handling follow the official ANIP/DGI\n * specification.\n *\n * @see https://dgi.mef.gob.pa/\n */\n\nimport type {\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport { clean } from \"#util/clean\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\n/**\n * Legacy cross-reference table: maps two-digit codes\n * at positions 5–6 of old-format juridical RUCs.\n */\nconst LEGACY_MAP: Record<string, string> = {\n \"00\": \"00\",\n \"10\": \"01\",\n \"11\": \"02\",\n \"12\": \"03\",\n \"13\": \"04\",\n \"14\": \"05\",\n \"15\": \"06\",\n \"16\": \"07\",\n \"17\": \"08\",\n \"18\": \"09\",\n \"19\": \"01\",\n \"20\": \"02\",\n \"21\": \"03\",\n \"22\": \"04\",\n \"23\": \"07\",\n \"24\": \"08\",\n \"25\": \"09\",\n \"26\": \"02\",\n \"27\": \"03\",\n \"28\": \"04\",\n \"29\": \"05\",\n \"30\": \"06\",\n \"31\": \"07\",\n \"32\": \"08\",\n \"33\": \"09\",\n \"34\": \"01\",\n \"35\": \"02\",\n \"36\": \"03\",\n \"37\": \"04\",\n \"38\": \"05\",\n \"39\": \"06\",\n \"40\": \"07\",\n \"41\": \"08\",\n \"42\": \"09\",\n \"43\": \"01\",\n \"44\": \"02\",\n \"45\": \"03\",\n \"46\": \"04\",\n \"47\": \"05\",\n \"48\": \"06\",\n \"49\": \"07\",\n};\n\n/** Prepend n leading zeros. */\nconst z = (n: number): string => \"0\".repeat(Math.max(0, n));\n\n/**\n * Compute a single DV digit via weighted sum mod 11.\n * Weights start at 2 and increment, applied\n * right-to-left. For old-format juridical RUCs,\n * weight 12 is skipped (jumps from 11 to 13).\n */\nconst digitDV = (\n buf: string,\n isOldFormat: boolean,\n): number => {\n let weight = 2;\n let sum = 0;\n for (let i = buf.length - 1; i >= 0; i--) {\n if (isOldFormat && weight === 12) {\n weight += 1; // skip 12, continue from 13\n }\n sum += weight * (buf.charCodeAt(i) - 48);\n weight += 1;\n }\n const r = sum % 11;\n return r > 1 ? 11 - r : 0;\n};\n\n/**\n * Encode the RUC segments into a numeric buffer\n * according to entity type, then compute the two\n * DV digits.\n *\n * Returns the two-digit DV string, or null if the\n * format is unrecognized.\n */\nconst computeDV = (\n segments: readonly string[],\n raw: string,\n): string | null => {\n const len = segments.length;\n\n if (len < 3 || len > 4) {\n return null;\n }\n // The NT designation requires exactly 4 segments\n // (xxNT-NT-xxx-xxx); any other 4-segment shape is\n // unrecognized, and a 3-segment \"NT\" in position 1\n // is malformed.\n if ((len === 4) !== (segments[1] === \"NT\")) {\n return null;\n }\n\n let buf: string;\n let isOld = false;\n\n // SAFETY: length check above ensures 3..4 segments.\n // eslint-disable-next-line no-non-null-assertion\n const s0 = segments[0]!;\n // eslint-disable-next-line no-non-null-assertion\n const s1 = segments[1]!;\n // eslint-disable-next-line no-non-null-assertion\n const s2 = segments[2]!;\n\n if (raw[0] === \"E\") {\n // Foreign person (E-xxx-xxx)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"50\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s1 === \"NT\") {\n // NT designation (xxNT-NT-xxx-xxx)\n // SAFETY: NT branch implies len === 4 (checked above).\n // eslint-disable-next-line no-non-null-assertion\n const s3 = segments[3]!;\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"43\" +\n z(3 - s2.length) +\n s2 +\n z(5 - s3.length) +\n s3;\n } else if (s0.endsWith(\"AV\")) {\n // AV suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"15\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.endsWith(\"PI\")) {\n // PI suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"79\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0 === \"PE\") {\n // PE prefix (foreign-born)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"75\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (raw[0] === \"N\") {\n // N prefix (naturalized citizen)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"40\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.length >= 1 && s0.length <= 2) {\n // Natural person (province 1–2 digits)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(2 - s0.length) +\n s0 +\n \"00\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else {\n // Juridical (company)\n buf =\n z(10 - s0.length) +\n s0 +\n z(4 - s1.length) +\n s1 +\n z(6 - s2.length) +\n s2;\n // SAFETY: juridical branch zero-pads s0 to at least\n // 10 chars (z(10 - s0.length)), so buf[3..6] are\n // always defined.\n isOld =\n buf[3] === \"0\" &&\n buf[4] === \"0\" &&\n // eslint-disable-next-line no-non-null-assertion\n buf[5]! < \"5\";\n }\n\n // Apply legacy cross-reference for old format\n if (isOld) {\n const key = buf.slice(5, 7);\n const mapped = LEGACY_MAP[key] ?? key;\n buf = buf.slice(0, 5) + mapped + buf.slice(7);\n }\n\n const dv1 = digitDV(buf, isOld);\n const dv2 = digitDV(\n buf + String.fromCharCode(48 + dv1),\n isOld,\n );\n\n return `${dv1}${dv2}`;\n};\n\n/**\n * Compact: normalize Unicode, collapse whitespace,\n * strip colons around DV, and uppercase. Keeps\n * hyphens (structural separators) and a single\n * space before \"DV\".\n */\nconst compact = (value: string): string => {\n let v = clean(value, \"\").trim().toUpperCase();\n // Normalize DV portion: \" DV: 49\" -> \" DV49\"\n v = v.replace(/\\s*DV[:\\s]*(\\d{2})$/, \" DV$1\");\n // Collapse any remaining internal whitespace\n v = v.replace(/\\s+/g, \" \");\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n // Extract the DV portion: \"...DV49\", \"...DV:49\",\n // or \"... DV 49\"\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n\n if (!dvMatch) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must include DV \" +\n \"(dígito verificador)\",\n );\n }\n\n // SAFETY: both DV regex capture groups are required.\n // eslint-disable-next-line no-non-null-assertion\n const rucPart = dvMatch[1]!;\n // eslint-disable-next-line no-non-null-assertion\n const dvPart = dvMatch[2]!;\n\n // The RUC part should be hyphen-separated segments\n const segments = rucPart.replace(/-+$/, \"\").split(\"-\");\n\n if (segments.length < 3 || segments.length > 4) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must have 3–4 \" +\n \"hyphen-separated segments\",\n );\n }\n\n if (segments.some((s) => s.length === 0)) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC segments must not be empty\",\n );\n }\n\n const computed = computeDV(segments, rucPart);\n if (computed === null) {\n return err(\n \"INVALID_FORMAT\",\n \"Unrecognized Panama RUC format\",\n );\n }\n\n if (computed !== dvPart) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Panama RUC dígito verificador \" + \"does not match\",\n );\n }\n\n const compactForm = segments.join(\"-\") + \" DV\" + dvPart;\n return { valid: true, compact: compactForm };\n};\n\n/**\n * Format: display as segments with \" DV XX\".\n */\nconst format = (value: string): string => {\n const v = compact(value);\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n if (!dvMatch) return v;\n\n // SAFETY: both DV regex capture groups are required.\n // eslint-disable-next-line no-non-null-assertion\n const rucPart = dvMatch[1]!.replace(/-+$/, \"\");\n // eslint-disable-next-line no-non-null-assertion\n const dvPart = dvMatch[2]!;\n const segments = rucPart.split(\"-\");\n return `${segments.join(\"-\")} DV ${dvPart}`;\n};\n\n/** Generate a random valid Panama RUC (natural person). */\nconst generate = (): string => {\n const province = String(randomInt(1, 13));\n const volume = String(randomInt(1, 9999));\n const folio = String(randomInt(1, 99999));\n const segments = [province, volume, folio];\n const dv = computeDV(segments, segments.join(\"-\"));\n if (dv === null) {\n throw new Error(\"Failed to compute Panama RUC DV\");\n }\n return `${segments.join(\"-\")} DV${dv}`;\n};\n\n/** Panama tax identification number. */\nconst ruc: CountryValidator<\"PA\"> = {\n name: \"Tax Identification Number\",\n localName: \"Registro Único de Contribuyente\",\n abbreviation: \"RUC\",\n aliases: [\"RUC\"] as const,\n candidatePattern: \"\\\\d{1,2}-?\\\\d{1,4}-?\\\\d{1,6}\",\n scope: \"country\",\n country: \"PA\",\n entityType: \"any\",\n description: \"Tax identifier issued by Panama's DGI\",\n sourceUrl: \"https://dgi.mef.gob.pa/\",\n examples: [\n \"1-184-921 DV49\",\n \"2588017-1-831938 DV20\",\n ] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default ruc;\nexport { compact, format, validate, generate };\n"],"mappings":"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"}
@@ -1,11 +1,8 @@
1
- import { CountryCode, Validator } from "./types.js";
1
+ import { CountryCode, CountryValidator, Validator } from "./types.js";
2
2
 
3
3
  //#region src/patterns.d.ts
4
-
5
- type ValidatorPattern = {
6
- /** The validator this pattern was derived from. */
7
- validator: Validator;
8
- /** Loose regex matching candidates in text. */
4
+ type ValidatorPattern<TValidator extends Validator = Validator> = {
5
+ /** The validator this pattern was derived from. */validator: TValidator; /** Loose regex matching candidates in text. */
9
6
  regex: RegExp;
10
7
  };
11
8
  /**
@@ -25,21 +22,22 @@ declare const toRegex: (v: Validator) => RegExp;
25
22
  /**
26
23
  * Build patterns for a list of validators.
27
24
  */
28
- declare const toPatterns: (validators: readonly Validator[]) => ValidatorPattern[];
25
+ declare const toPatterns: <TValidator extends Validator>(validators: readonly TValidator[]) => ValidatorPattern<TValidator>[];
29
26
  /**
30
27
  * Filter validators by country code and build
31
28
  * patterns.
32
29
  */
33
- declare const byCountry: (country: CountryCode, validators: readonly Validator[]) => ValidatorPattern[];
30
+ type CountryScopedValidator<TValidator extends Validator, TCountry extends CountryCode> = TValidator extends CountryValidator<infer TValidatorCountry, infer TParsed> ? TCountry extends TValidatorCountry ? TValidator & CountryValidator<TCountry, TParsed> : TValidatorCountry extends TCountry ? TValidator : never : never;
31
+ declare const byCountry: <TCountry extends CountryCode, TValidator extends Validator>(country: TCountry, validators: readonly TValidator[]) => ValidatorPattern<CountryScopedValidator<TValidator, TCountry>>[];
34
32
  /**
35
33
  * Filter validators by entity type and build
36
34
  * patterns.
37
35
  */
38
- declare const byEntityType: (entityType: "person" | "company" | "any", validators: readonly Validator[]) => ValidatorPattern[];
36
+ declare const byEntityType: <TValidator extends Validator>(entityType: "person" | "company" | "any", validators: readonly TValidator[]) => ValidatorPattern<TValidator>[];
39
37
  /**
40
38
  * Build patterns for ALL provided validators.
41
39
  */
42
- declare const allPatterns: (validators: readonly Validator[]) => ValidatorPattern[];
40
+ declare const allPatterns: <TValidator extends Validator>(validators: readonly TValidator[]) => ValidatorPattern<TValidator>[];
43
41
  //#endregion
44
42
  export { ValidatorPattern, allPatterns, byCountry, byEntityType, toPatterns, toRegex };
45
43
  //# sourceMappingURL=patterns.d.ts.map
package/dist/patterns.js CHANGED
@@ -19,8 +19,9 @@ const inferLengths = (v) => {
19
19
  * "25 123 891" → [2, 3, 3].
20
20
  */
21
21
  const inferGroups = (v) => {
22
- if (!v.examples || v.examples.length === 0) return null;
23
- const compact = v.compact(v.examples[0]);
22
+ const first = v.examples?.[0];
23
+ if (first === void 0) return null;
24
+ const compact = v.compact(first);
24
25
  const formatted = v.format(compact);
25
26
  if (formatted === compact) return null;
26
27
  const parts = formatted.split(/[^a-zA-Z0-9]+/);
@@ -34,43 +35,97 @@ const inferGroups = (v) => {
34
35
  * (e.g., "CZ" for DIČ, "CHE" for Swiss UID).
35
36
  */
36
37
  const inferPrefix = (v) => {
37
- if (!v.examples || v.examples.length === 0) return null;
38
- const compact = v.compact(v.examples[0]);
39
- const compactMatch = compact.match(/^([A-Z]+)\d/);
40
- if (compactMatch && v.examples.length > 1) {
41
- const pfx = compactMatch[1];
42
- if (v.examples.every((ex) => v.compact(ex).startsWith(pfx))) return pfx;
43
- } else if (compactMatch && v.examples.length === 1) return compactMatch[1];
44
- const fmtMatch = v.format(compact).match(/^([A-Z]+)[\s\-./]?\d/);
45
- if (fmtMatch && !compact.startsWith(fmtMatch[1])) return fmtMatch[1];
38
+ const examples = v.examples;
39
+ const first = examples?.[0];
40
+ if (!examples || first === void 0) return null;
41
+ const compact = v.compact(first);
42
+ const compactPrefix = compact.match(/^([A-Z]+)\d/)?.[1];
43
+ if (compactPrefix !== void 0 && examples.length > 1) {
44
+ if (examples.every((ex) => v.compact(ex).startsWith(compactPrefix))) return compactPrefix;
45
+ } else if (compactPrefix !== void 0 && examples.length === 1) return compactPrefix;
46
+ const fmtPrefix = v.format(compact).match(/^([A-Z]+)[\s\-./]?\d/)?.[1];
47
+ if (fmtPrefix !== void 0 && !compact.startsWith(fmtPrefix)) return fmtPrefix;
46
48
  return null;
47
49
  };
48
50
  /**
49
- * Determine the character class needed for the
50
- * compact form: `\d` if all digits, `[A-Z0-9]`
51
- * if alphanumeric, etc.
51
+ * Character class for a string fragment:
52
+ * `\d` if all digits, `[A-Z]` if all letters,
53
+ * `[A-Z0-9]` if mixed.
52
54
  */
53
- const inferCharClass = (v) => {
54
- if (!v.examples || v.examples.length === 0) return "\\d";
55
+ const charClassFor = (s) => {
55
56
  let hasLetter = false;
56
57
  let hasDigit = false;
57
- for (const ex of v.examples) {
58
- const c = v.compact(ex);
59
- for (const ch of c) {
60
- if (/[a-zA-Z]/.test(ch)) hasLetter = true;
61
- if (/\d/.test(ch)) hasDigit = true;
62
- }
58
+ for (const ch of s) {
59
+ if (/[a-zA-Z]/.test(ch)) hasLetter = true;
60
+ if (/\d/.test(ch)) hasDigit = true;
63
61
  }
64
62
  if (hasLetter && hasDigit) return "[A-Z0-9]";
65
63
  if (hasLetter) return "[A-Z]";
66
64
  return "\\d";
67
65
  };
68
66
  /**
67
+ * Determine the character class needed for the
68
+ * compact form: `\d` if all digits, `[A-Z0-9]`
69
+ * if alphanumeric, etc.
70
+ */
71
+ const inferCharClass = (v) => {
72
+ if (!v.examples || v.examples.length === 0) return "\\d";
73
+ return charClassFor(v.examples.map((ex) => v.compact(ex)).join(""));
74
+ };
75
+ /**
76
+ * Infer per-group sizes AND character classes from
77
+ * format(). Returns groups with per-position char
78
+ * classes, e.g., "12 010188 M 01 1" →
79
+ * sizes: [2, 6, 1, 2, 1]
80
+ * classes: ["\d", "\d", "[A-Z]", "\d", "\d"]
81
+ *
82
+ * Only returns non-null when per-group classes
83
+ * differ (some groups are letters, some digits).
84
+ * Letter-only groups before the first digit group
85
+ * are treated as prefixes and excluded (handled
86
+ * by inferPrefix separately).
87
+ */
88
+ const inferPerGroup = (v) => {
89
+ const first = v.examples?.[0];
90
+ if (first === void 0) return null;
91
+ const compact = v.compact(first);
92
+ const formatted = v.format(compact);
93
+ if (formatted === compact) return null;
94
+ if (!(/[a-zA-Z]/.test(compact) && /\d/.test(compact))) return null;
95
+ const parts = formatted.split(/[^a-zA-Z0-9]+/);
96
+ if (parts.some((p) => /[a-zA-Z]/.test(p) && /\d/.test(p))) return null;
97
+ const filtered = [];
98
+ let seenDigitGroup = false;
99
+ for (const p of parts) {
100
+ if (p.length === 0) continue;
101
+ if (/\d/.test(p)) {
102
+ seenDigitGroup = true;
103
+ filtered.push({
104
+ size: p.length,
105
+ charClass: charClassFor(p)
106
+ });
107
+ } else if (seenDigitGroup) filtered.push({
108
+ size: p.length,
109
+ charClass: charClassFor(p)
110
+ });
111
+ }
112
+ if (filtered.length <= 1) return null;
113
+ const firstClass = filtered[0]?.charClass;
114
+ if (filtered.every((g) => g.charClass === firstClass)) return null;
115
+ return filtered;
116
+ };
117
+ /**
69
118
  * Build a group regex from group sizes + char class.
70
119
  * [2, 3, 3] with \d → /\d{2}[\s\-.]?\d{3}[\s\-.]?\d{3}/
71
120
  */
72
121
  const groupsToPattern = (groups, cc) => groups.map((g) => `${cc}{${g}}`).join(SEP);
73
122
  /**
123
+ * Build a group regex with per-group char classes.
124
+ * Produces tighter patterns when groups have different
125
+ * character types (e.g., digits vs letters).
126
+ */
127
+ const groupsToPatternPerClass = (groups) => groups.map(({ size, charClass }) => `${charClass}{${size}}`).join(SEP);
128
+ /**
74
129
  * Derive a loose candidate-matching regex from
75
130
  * a validator. The regex finds potential matches;
76
131
  * use `validate()` to confirm.
@@ -84,7 +139,9 @@ const toRegex = (v) => {
84
139
  const lengths = inferLengths(v);
85
140
  const cc = inferCharClass(v);
86
141
  let pattern;
87
- if (groups && lengths.length <= 1) pattern = groupsToPattern(groups, cc);
142
+ const perGroup = inferPerGroup(v);
143
+ if (perGroup && lengths.length <= 1) pattern = groupsToPatternPerClass(perGroup);
144
+ else if (groups && lengths.length <= 1) pattern = groupsToPattern(groups, cc);
88
145
  else if (lengths.length === 1) pattern = `${cc}{${lengths[0]}}`;
89
146
  else if (lengths.length > 1) pattern = `${cc}{${Math.min(...lengths)},${Math.max(...lengths)}}`;
90
147
  else pattern = `${cc}{6,20}`;
@@ -103,11 +160,9 @@ const toPatterns = (validators) => validators.map((v) => ({
103
160
  validator: v,
104
161
  regex: toRegex(v)
105
162
  }));
106
- /**
107
- * Filter validators by country code and build
108
- * patterns.
109
- */
110
- const byCountry = (country, validators) => toPatterns(validators.filter((v) => v.country === country));
163
+ const byCountry = (country, validators) => {
164
+ return toPatterns(validators.filter((v) => v.scope === "country" && v.country === country));
165
+ };
111
166
  /**
112
167
  * Filter validators by entity type and build
113
168
  * patterns.
@@ -1 +1 @@
1
- {"version":3,"file":"patterns.js","names":[],"sources":["../src/patterns.ts"],"sourcesContent":["/**\n * Derive candidate-matching regex patterns from\n * validators. These patterns find CANDIDATES in\n * text — use `validate()` to confirm each match.\n *\n * Designed for integration with regex-set or\n * similar multi-pattern scanning engines.\n *\n * @example\n * ```ts\n * import {\n * toRegex, byCountry, byEntityType,\n * } from \"@stll/stdnum/patterns\";\n * import { cz } from \"@stll/stdnum\";\n *\n * // Single validator\n * toRegex(cz.ico);\n * // → /(?<!\\w)\\d{8}(?!\\w)/g\n *\n * // All Czech validators\n * byCountry(\"CZ\");\n * // → [{ validator: cz.ico, regex }, ...]\n *\n * // All company identifiers\n * byEntityType(\"company\");\n * // → [{ validator, regex }, ...]\n * ```\n */\n\nimport type { CountryCode, Validator } from \"./types\";\n\n// ─── Pattern type ─────────────────────────────\n\nexport type ValidatorPattern = {\n /** The validator this pattern was derived from. */\n validator: Validator;\n /** Loose regex matching candidates in text. */\n regex: RegExp;\n};\n\n// ─── Regex derivation ─────────────────────────\n\n/** Separator chars that compact() strips. */\nconst SEP = \"[\\\\s\\\\-./]?\";\n\n/**\n * Infer compact lengths from metadata or examples.\n */\nconst inferLengths = (v: Validator): number[] => {\n if (v.lengths && v.lengths.length > 0) {\n return [...v.lengths];\n }\n if (v.examples && v.examples.length > 0) {\n const lens = new Set<number>();\n for (const ex of v.examples) {\n lens.add(v.compact(ex).length);\n }\n return [...lens];\n }\n return [];\n};\n\n/**\n * Derive the display grouping from format().\n * Returns an array of group sizes, e.g.,\n * \"25 123 891\" → [2, 3, 3].\n */\nconst inferGroups = (v: Validator): number[] | null => {\n if (!v.examples || v.examples.length === 0) {\n return null;\n }\n const compact = v.compact(v.examples[0]!);\n const formatted = v.format(compact);\n if (formatted === compact) return null;\n\n // Split on non-alphanumeric separators\n const parts = formatted.split(/[^a-zA-Z0-9]+/);\n if (parts.length <= 1) return null;\n\n // For digit-only validators, skip letter-only\n // tokens (prefix like \"CHE\"). For alphanumeric\n // validators (IBAN, ISIN), keep all groups.\n const isAlphanumeric = parts.some(\n (p) => /[a-zA-Z]/.test(p) && /\\d/.test(p),\n );\n const groups = parts\n .filter(\n (p) =>\n p.length > 0 && (isAlphanumeric || /\\d/.test(p)),\n )\n .map((p) => p.length);\n\n return groups.length > 1 ? groups : null;\n};\n\n/**\n * Detect if this validator uses a known prefix\n * (e.g., \"CZ\" for DIČ, \"CHE\" for Swiss UID).\n */\nconst inferPrefix = (v: Validator): string | null => {\n if (!v.examples || v.examples.length === 0) {\n return null;\n }\n const compact = v.compact(v.examples[0]!);\n // Prefix already present in compact form.\n // But only if ALL examples share the same prefix\n // (e.g., \"CHE\" for Swiss UID). If examples have\n // different prefixes (IBAN: GB/DE/FR), there's\n // no fixed prefix — it's part of the value.\n const compactMatch = compact.match(/^([A-Z]+)\\d/);\n if (compactMatch && v.examples!.length > 1) {\n const pfx = compactMatch[1]!;\n const allSame = v.examples!.every((ex) =>\n v.compact(ex).startsWith(pfx),\n );\n if (allSame) return pfx;\n } else if (compactMatch && v.examples!.length === 1) {\n return compactMatch[1]!;\n }\n // Prefix added only by format()\n // (e.g., \"DE\" for de.vat, \"CZ\" for cz.dic)\n const formatted = v.format(compact);\n const fmtMatch = formatted.match(/^([A-Z]+)[\\s\\-./]?\\d/);\n if (fmtMatch && !compact.startsWith(fmtMatch[1]!)) {\n return fmtMatch[1]!;\n }\n return null;\n};\n\n/**\n * Determine the character class needed for the\n * compact form: `\\d` if all digits, `[A-Z0-9]`\n * if alphanumeric, etc.\n */\nconst inferCharClass = (v: Validator): string => {\n if (!v.examples || v.examples.length === 0) {\n return \"\\\\d\";\n }\n let hasLetter = false;\n let hasDigit = false;\n for (const ex of v.examples) {\n const c = v.compact(ex);\n for (const ch of c) {\n if (/[a-zA-Z]/.test(ch)) hasLetter = true;\n if (/\\d/.test(ch)) hasDigit = true;\n }\n }\n if (hasLetter && hasDigit) return \"[A-Z0-9]\";\n if (hasLetter) return \"[A-Z]\";\n return \"\\\\d\";\n};\n\n/**\n * Build a group regex from group sizes + char class.\n * [2, 3, 3] with \\d → /\\d{2}[\\s\\-.]?\\d{3}[\\s\\-.]?\\d{3}/\n */\nconst groupsToPattern = (\n groups: number[],\n cc: string,\n): string => groups.map((g) => `${cc}{${g}}`).join(SEP);\n\n/**\n * Derive a loose candidate-matching regex from\n * a validator. The regex finds potential matches;\n * use `validate()` to confirm.\n *\n * Handles both digit-only (IČO, NIP) and\n * alphanumeric (IBAN, BIC, ISIN, LEI) validators.\n */\nexport const toRegex = (v: Validator): RegExp => {\n const prefix = inferPrefix(v);\n const groups = inferGroups(v);\n const lengths = inferLengths(v);\n const cc = inferCharClass(v);\n\n let pattern: string;\n\n if (groups && lengths.length <= 1) {\n pattern = groupsToPattern(groups, cc);\n } else if (lengths.length === 1) {\n pattern = `${cc}{${lengths[0]}}`;\n } else if (lengths.length > 1) {\n const min = Math.min(...lengths);\n const max = Math.max(...lengths);\n pattern = `${cc}{${min},${max}}`;\n } else {\n pattern = `${cc}{6,20}`;\n }\n\n if (prefix) {\n pattern = `${prefix}${SEP}${pattern}`;\n }\n\n // Word boundary to avoid matching inside\n // larger numbers\n return new RegExp(`(?<!\\\\w)${pattern}(?!\\\\w)`, \"g\");\n};\n\n// ─── Grouped collections ──────────────────────\n\n/**\n * All discovered validators, lazily populated.\n * Import * from \"../src\" would pull everything;\n * instead, callers pass validators explicitly.\n */\n\n/**\n * Build patterns for a list of validators.\n */\nexport const toPatterns = (\n validators: readonly Validator[],\n): ValidatorPattern[] =>\n validators.map((v) => ({\n validator: v,\n regex: toRegex(v),\n }));\n\n/**\n * Filter validators by country code and build\n * patterns.\n */\nexport const byCountry = (\n country: CountryCode,\n validators: readonly Validator[],\n): ValidatorPattern[] =>\n toPatterns(\n validators.filter((v) => v.country === country),\n );\n\n/**\n * Filter validators by entity type and build\n * patterns.\n */\nexport const byEntityType = (\n entityType: \"person\" | \"company\" | \"any\",\n validators: readonly Validator[],\n): ValidatorPattern[] =>\n toPatterns(\n validators.filter(\n (v) =>\n entityType === \"any\" ||\n v.entityType === entityType ||\n v.entityType === \"any\",\n ),\n );\n\n/**\n * Build patterns for ALL provided validators.\n */\nexport const allPatterns = (\n validators: readonly Validator[],\n): ValidatorPattern[] => toPatterns(validators);\n"],"mappings":"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"}
1
+ {"version":3,"file":"patterns.js","names":[],"sources":["../src/patterns.ts"],"sourcesContent":["/**\n * Derive candidate-matching regex patterns from\n * validators. These patterns find CANDIDATES in\n * text — use `validate()` to confirm each match.\n *\n * Designed for integration with regex-set or\n * similar multi-pattern scanning engines.\n *\n * @example\n * ```ts\n * import {\n * toRegex, byCountry, byEntityType,\n * } from \"@stll/stdnum/patterns\";\n * import { cz } from \"@stll/stdnum\";\n *\n * // Single validator\n * toRegex(cz.ico);\n * // → /(?<!\\w)\\d{8}(?!\\w)/g\n *\n * // All Czech validators\n * byCountry(\"CZ\");\n * // → [{ validator: cz.ico, regex }, ...]\n *\n * // All company identifiers\n * byEntityType(\"company\");\n * // → [{ validator, regex }, ...]\n * ```\n */\n\nimport type {\n CountryCode,\n CountryValidator,\n Validator,\n} from \"./types\";\n\n// ─── Pattern type ─────────────────────────────\n\nexport type ValidatorPattern<\n TValidator extends Validator = Validator,\n> = {\n /** The validator this pattern was derived from. */\n validator: TValidator;\n /** Loose regex matching candidates in text. */\n regex: RegExp;\n};\n\n// ─── Regex derivation ─────────────────────────\n\n/** Separator chars that compact() strips. */\nconst SEP = \"[\\\\s\\\\-./]?\";\n\n/**\n * Infer compact lengths from metadata or examples.\n */\nconst inferLengths = (v: Validator): number[] => {\n if (v.lengths && v.lengths.length > 0) {\n return [...v.lengths];\n }\n if (v.examples && v.examples.length > 0) {\n const lens = new Set<number>();\n for (const ex of v.examples) {\n lens.add(v.compact(ex).length);\n }\n return [...lens];\n }\n return [];\n};\n\n/**\n * Derive the display grouping from format().\n * Returns an array of group sizes, e.g.,\n * \"25 123 891\" → [2, 3, 3].\n */\nconst inferGroups = (v: Validator): number[] | null => {\n const first = v.examples?.[0];\n if (first === undefined) return null;\n const compact = v.compact(first);\n const formatted = v.format(compact);\n if (formatted === compact) return null;\n\n // Split on non-alphanumeric separators\n const parts = formatted.split(/[^a-zA-Z0-9]+/);\n if (parts.length <= 1) return null;\n\n // For digit-only validators, skip letter-only\n // tokens (prefix like \"CHE\"). For alphanumeric\n // validators (IBAN, ISIN), keep all groups.\n const isAlphanumeric = parts.some(\n (p) => /[a-zA-Z]/.test(p) && /\\d/.test(p),\n );\n const groups = parts\n .filter(\n (p) =>\n p.length > 0 && (isAlphanumeric || /\\d/.test(p)),\n )\n .map((p) => p.length);\n\n return groups.length > 1 ? groups : null;\n};\n\n/**\n * Detect if this validator uses a known prefix\n * (e.g., \"CZ\" for DIČ, \"CHE\" for Swiss UID).\n */\nconst inferPrefix = (v: Validator): string | null => {\n const examples = v.examples;\n const first = examples?.[0];\n if (!examples || first === undefined) return null;\n const compact = v.compact(first);\n // Prefix already present in compact form.\n // But only if ALL examples share the same prefix\n // (e.g., \"CHE\" for Swiss UID). If examples have\n // different prefixes (IBAN: GB/DE/FR), there's\n // no fixed prefix — it's part of the value.\n const compactMatch = compact.match(/^([A-Z]+)\\d/);\n const compactPrefix = compactMatch?.[1];\n if (compactPrefix !== undefined && examples.length > 1) {\n const allSame = examples.every((ex) =>\n v.compact(ex).startsWith(compactPrefix),\n );\n if (allSame) return compactPrefix;\n } else if (\n compactPrefix !== undefined &&\n examples.length === 1\n ) {\n return compactPrefix;\n }\n // Prefix added only by format()\n // (e.g., \"DE\" for de.vat, \"CZ\" for cz.dic)\n const formatted = v.format(compact);\n const fmtMatch = formatted.match(/^([A-Z]+)[\\s\\-./]?\\d/);\n const fmtPrefix = fmtMatch?.[1];\n if (\n fmtPrefix !== undefined &&\n !compact.startsWith(fmtPrefix)\n ) {\n return fmtPrefix;\n }\n return null;\n};\n\n/**\n * Character class for a string fragment:\n * `\\d` if all digits, `[A-Z]` if all letters,\n * `[A-Z0-9]` if mixed.\n */\nconst charClassFor = (s: string): string => {\n let hasLetter = false;\n let hasDigit = false;\n for (const ch of s) {\n if (/[a-zA-Z]/.test(ch)) hasLetter = true;\n if (/\\d/.test(ch)) hasDigit = true;\n }\n if (hasLetter && hasDigit) return \"[A-Z0-9]\";\n if (hasLetter) return \"[A-Z]\";\n return \"\\\\d\";\n};\n\n/**\n * Determine the character class needed for the\n * compact form: `\\d` if all digits, `[A-Z0-9]`\n * if alphanumeric, etc.\n */\nconst inferCharClass = (v: Validator): string => {\n if (!v.examples || v.examples.length === 0) {\n return \"\\\\d\";\n }\n const combined = v.examples\n .map((ex) => v.compact(ex))\n .join(\"\");\n return charClassFor(combined);\n};\n\ntype PerGroup = { size: number; charClass: string };\n\n/**\n * Infer per-group sizes AND character classes from\n * format(). Returns groups with per-position char\n * classes, e.g., \"12 010188 M 01 1\" →\n * sizes: [2, 6, 1, 2, 1]\n * classes: [\"\\d\", \"\\d\", \"[A-Z]\", \"\\d\", \"\\d\"]\n *\n * Only returns non-null when per-group classes\n * differ (some groups are letters, some digits).\n * Letter-only groups before the first digit group\n * are treated as prefixes and excluded (handled\n * by inferPrefix separately).\n */\nconst inferPerGroup = (v: Validator): PerGroup[] | null => {\n const first = v.examples?.[0];\n if (first === undefined) return null;\n const compact = v.compact(first);\n const formatted = v.format(compact);\n if (formatted === compact) return null;\n\n // Only applies to mixed validators (compact has\n // both letters and digits in separate positions,\n // like German SVNR \"12010188M011\").\n const compactMixed =\n /[a-zA-Z]/.test(compact) && /\\d/.test(compact);\n if (!compactMixed) return null;\n\n // Also skip if any single part mixes letters and\n // digits (IBAN, ISIN) — inferGroups already\n // handles those correctly with the global class.\n const parts = formatted.split(/[^a-zA-Z0-9]+/);\n const isAlphanumeric = parts.some(\n (p) => /[a-zA-Z]/.test(p) && /\\d/.test(p),\n );\n if (isAlphanumeric) return null;\n\n // Keep digit groups and letter-only groups that\n // appear AFTER the first digit group (embedded\n // letters). Skip letter-only groups before the\n // first digit group (prefix like \"CHE\").\n const filtered: PerGroup[] = [];\n let seenDigitGroup = false;\n for (const p of parts) {\n if (p.length === 0) continue;\n if (/\\d/.test(p)) {\n seenDigitGroup = true;\n filtered.push({\n size: p.length,\n charClass: charClassFor(p),\n });\n } else if (seenDigitGroup) {\n filtered.push({\n size: p.length,\n charClass: charClassFor(p),\n });\n }\n }\n\n if (filtered.length <= 1) return null;\n\n const firstClass = filtered[0]?.charClass;\n const allSame = filtered.every(\n (g) => g.charClass === firstClass,\n );\n if (allSame) return null;\n\n return filtered;\n};\n\n/**\n * Build a group regex from group sizes + char class.\n * [2, 3, 3] with \\d → /\\d{2}[\\s\\-.]?\\d{3}[\\s\\-.]?\\d{3}/\n */\nconst groupsToPattern = (\n groups: number[],\n cc: string,\n): string => groups.map((g) => `${cc}{${g}}`).join(SEP);\n\n/**\n * Build a group regex with per-group char classes.\n * Produces tighter patterns when groups have different\n * character types (e.g., digits vs letters).\n */\nconst groupsToPatternPerClass = (\n groups: readonly { size: number; charClass: string }[],\n): string =>\n groups\n .map(({ size, charClass }) => `${charClass}{${size}}`)\n .join(SEP);\n\n/**\n * Derive a loose candidate-matching regex from\n * a validator. The regex finds potential matches;\n * use `validate()` to confirm.\n *\n * Handles both digit-only (IČO, NIP) and\n * alphanumeric (IBAN, BIC, ISIN, LEI) validators.\n */\nexport const toRegex = (v: Validator): RegExp => {\n const prefix = inferPrefix(v);\n const groups = inferGroups(v);\n const lengths = inferLengths(v);\n const cc = inferCharClass(v);\n\n let pattern: string;\n\n // Use per-group char classes when groups mix\n // digits and letters (e.g., German SVNR:\n // \"12 010188 M 01 1\" → [\\d, \\d, [A-Z], \\d, \\d]).\n // This prevents overly broad [A-Z0-9] from\n // matching all-caps prose as identifiers.\n const perGroup = inferPerGroup(v);\n\n if (perGroup && lengths.length <= 1) {\n pattern = groupsToPatternPerClass(perGroup);\n } else if (groups && lengths.length <= 1) {\n pattern = groupsToPattern(groups, cc);\n } else if (lengths.length === 1) {\n pattern = `${cc}{${lengths[0]}}`;\n } else if (lengths.length > 1) {\n const min = Math.min(...lengths);\n const max = Math.max(...lengths);\n pattern = `${cc}{${min},${max}}`;\n } else {\n pattern = `${cc}{6,20}`;\n }\n\n if (prefix) {\n pattern = `${prefix}${SEP}${pattern}`;\n }\n\n // Word boundary to avoid matching inside\n // larger numbers\n return new RegExp(`(?<!\\\\w)${pattern}(?!\\\\w)`, \"g\");\n};\n\n// ─── Grouped collections ──────────────────────\n\n/**\n * All discovered validators, lazily populated.\n * Import * from \"../src\" would pull everything;\n * instead, callers pass validators explicitly.\n */\n\n/**\n * Build patterns for a list of validators.\n */\nexport const toPatterns = <TValidator extends Validator>(\n validators: readonly TValidator[],\n): ValidatorPattern<TValidator>[] =>\n validators.map((v) => ({\n validator: v,\n regex: toRegex(v),\n }));\n\n/**\n * Filter validators by country code and build\n * patterns.\n */\ntype CountryScopedValidator<\n TValidator extends Validator,\n TCountry extends CountryCode,\n> =\n TValidator extends CountryValidator<\n infer TValidatorCountry,\n infer TParsed\n >\n ? TCountry extends TValidatorCountry\n ? TValidator & CountryValidator<TCountry, TParsed>\n : TValidatorCountry extends TCountry\n ? TValidator\n : never\n : never;\n\nexport const byCountry = <\n TCountry extends CountryCode,\n TValidator extends Validator,\n>(\n country: TCountry,\n validators: readonly TValidator[],\n): ValidatorPattern<\n CountryScopedValidator<TValidator, TCountry>\n>[] => {\n const countryValidators = validators.filter(\n (\n v,\n ): v is CountryScopedValidator<TValidator, TCountry> =>\n v.scope === \"country\" && v.country === country,\n );\n return toPatterns(countryValidators);\n};\n\n/**\n * Filter validators by entity type and build\n * patterns.\n */\nexport const byEntityType = <TValidator extends Validator>(\n entityType: \"person\" | \"company\" | \"any\",\n validators: readonly TValidator[],\n): ValidatorPattern<TValidator>[] =>\n toPatterns(\n validators.filter(\n (v) =>\n entityType === \"any\" ||\n v.entityType === entityType ||\n v.entityType === \"any\",\n ),\n );\n\n/**\n * Build patterns for ALL provided validators.\n */\nexport const allPatterns = <TValidator extends Validator>(\n validators: readonly TValidator[],\n): ValidatorPattern<TValidator>[] => toPatterns(validators);\n"],"mappings":"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"}
package/dist/pe/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/pe/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/pe/ruc.d.ts CHANGED
@@ -1,7 +1,6 @@
1
- import { ValidateResult, Validator } from "../types.js";
1
+ import { CountryValidator, ValidateResult } from "../types.js";
2
2
 
3
3
  //#region src/pe/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;
@@ -13,7 +12,7 @@ declare const generate: () => string;
13
12
  * Examples sourced from python-stdnum test suite
14
13
  * (pe.ruc module).
15
14
  */
16
- declare const ruc: Validator;
15
+ declare const ruc: CountryValidator<"PE">;
17
16
  //#endregion
18
17
  export { compact, ruc as default, format, generate, validate };
19
18
  //# sourceMappingURL=ruc.d.ts.map
package/dist/pe/ruc.js CHANGED
@@ -1,24 +1,9 @@
1
1
  import { clean } from "../_util/clean.js";
2
- import { randomDigits } from "../_util/generate.js";
2
+ import { randomDigits, randomPick } from "../_util/generate.js";
3
3
  import { err } from "../_util/result.js";
4
4
  import { isdigits } from "../_util/strings.js";
5
5
  //#region src/pe/ruc.ts
6
6
  /**
7
- * RUC (Registro Único de Contribuyentes).
8
- *
9
- * Peruvian tax identification number assigned by SUNAT
10
- * (Superintendencia Nacional de Aduanas y de
11
- * Administración Tributaria). The number consists of
12
- * 11 digits: a type prefix (10 = person, 20 = company,
13
- * among others), 8 identification digits, and a check
14
- * digit computed using a weighted modulo 11 algorithm.
15
- *
16
- * Format: XXXXXXXXXXX (11 digits, no separators).
17
- *
18
- * @see https://www.sunat.gob.pe/
19
- * @see https://en.wikipedia.org/wiki/Tax_identification_number#Peru
20
- */
21
- /**
22
7
  * Valid type prefixes for RUC numbers.
23
8
  * 10 = natural person, 15 = non-domiciled without RUC,
24
9
  * 17 = non-domiciled, 20 = legal entity,
@@ -42,6 +27,12 @@ const WEIGHTS = [
42
27
  3,
43
28
  2
44
29
  ];
30
+ const GENERATE_PREFIXES = [
31
+ "10",
32
+ "15",
33
+ "17",
34
+ "20"
35
+ ];
45
36
  const compact = (value) => clean(value, " -.").trim();
46
37
  /**
47
38
  * Compute the RUC check digit using weighted sum
@@ -51,7 +42,7 @@ const compact = (value) => clean(value, " -.").trim();
51
42
  */
52
43
  const calcCheckDigit = (body) => {
53
44
  let sum = 0;
54
- for (let i = 0; i < 10; i++) sum += Number(body[i]) * WEIGHTS[i];
45
+ for (const [i, weight] of WEIGHTS.entries()) sum += Number(body[i]) * weight;
55
46
  const remainder = 11 - sum % 11;
56
47
  if (remainder >= 10) return remainder - 10;
57
48
  return remainder;
@@ -72,13 +63,7 @@ const validate = (value) => {
72
63
  const format = (value) => compact(value);
73
64
  /** Generate a random valid Peruvian RUC. */
74
65
  const generate = () => {
75
- const prefixes = [
76
- "10",
77
- "15",
78
- "17",
79
- "20"
80
- ];
81
- const body = prefixes[Math.floor(Math.random() * prefixes.length)] + randomDigits(8);
66
+ const body = randomPick(GENERATE_PREFIXES) + randomDigits(8);
82
67
  return body + String(calcCheckDigit(body));
83
68
  };
84
69
  /**
@@ -93,6 +78,7 @@ const ruc = {
93
78
  abbreviation: "RUC",
94
79
  aliases: ["RUC"],
95
80
  candidatePattern: "\\d{11}",
81
+ scope: "country",
96
82
  country: "PE",
97
83
  entityType: "any",
98
84
  compact,