@stll/stdnum 1.0.0 → 2.0.1

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 (705) hide show
  1. package/README.md +1 -4
  2. package/dist/_checksums/luhn.js.map +1 -1
  3. package/dist/_checksums/mod1110.js +1 -1
  4. package/dist/_checksums/mod1110.js.map +1 -1
  5. package/dist/_checksums/mod112.js.map +1 -1
  6. package/dist/_checksums/mod97.js.map +1 -1
  7. package/dist/_checksums/verhoeff.js.map +1 -1
  8. package/dist/_checksums/weighted-sum.js.map +1 -1
  9. package/dist/_util/arabic.js.map +1 -1
  10. package/dist/_util/clean.js.map +1 -1
  11. package/dist/_util/date.js.map +1 -1
  12. package/dist/_util/generate.js.map +1 -1
  13. package/dist/_util/result.js.map +1 -1
  14. package/dist/_util/strings.js.map +1 -1
  15. package/dist/ad/nrt.d.ts +16 -2
  16. package/dist/ad/nrt.js +1 -14
  17. package/dist/ad/nrt.js.map +1 -1
  18. package/dist/ae/eid.d.ts +2 -2
  19. package/dist/ae/eid.js +2 -12
  20. package/dist/ae/eid.js.map +1 -1
  21. package/dist/ai/tin.d.ts +20 -2
  22. package/dist/ai/tin.js +1 -18
  23. package/dist/ai/tin.js.map +1 -1
  24. package/dist/al/nipt.d.ts +13 -2
  25. package/dist/al/nipt.js +1 -11
  26. package/dist/al/nipt.js.map +1 -1
  27. package/dist/am/tin.d.ts +16 -2
  28. package/dist/am/tin.js +1 -14
  29. package/dist/am/tin.js.map +1 -1
  30. package/dist/ar/cbu.d.ts +2 -2
  31. package/dist/ar/cbu.js +1 -9
  32. package/dist/ar/cbu.js.map +1 -1
  33. package/dist/ar/cuit.d.ts +2 -2
  34. package/dist/ar/cuit.js +1 -17
  35. package/dist/ar/cuit.js.map +1 -1
  36. package/dist/ar/dni.d.ts +11 -2
  37. package/dist/ar/dni.js +1 -9
  38. package/dist/ar/dni.js.map +1 -1
  39. package/dist/at/businessid.d.ts +10 -2
  40. package/dist/at/businessid.js +1 -8
  41. package/dist/at/businessid.js.map +1 -1
  42. package/dist/at/tin.d.ts +2 -2
  43. package/dist/at/tin.js +1 -11
  44. package/dist/at/tin.js.map +1 -1
  45. package/dist/at/uid.d.ts +2 -2
  46. package/dist/at/uid.js +2 -10
  47. package/dist/at/uid.js.map +1 -1
  48. package/dist/at/vnr.d.ts +2 -2
  49. package/dist/at/vnr.js +4 -14
  50. package/dist/at/vnr.js.map +1 -1
  51. package/dist/au/abn.d.ts +2 -2
  52. package/dist/au/abn.js +1 -10
  53. package/dist/au/abn.js.map +1 -1
  54. package/dist/au/acn.d.ts +2 -2
  55. package/dist/au/acn.js +1 -10
  56. package/dist/au/acn.js.map +1 -1
  57. package/dist/au/tfn.d.ts +2 -2
  58. package/dist/au/tfn.js +1 -10
  59. package/dist/au/tfn.js.map +1 -1
  60. package/dist/az/voen.d.ts +2 -2
  61. package/dist/az/voen.js +1 -14
  62. package/dist/az/voen.js.map +1 -1
  63. package/dist/ba/jmbg.d.ts +2 -2
  64. package/dist/ba/jmbg.js +3 -11
  65. package/dist/ba/jmbg.js.map +1 -1
  66. package/dist/bd/nid.d.ts +23 -2
  67. package/dist/bd/nid.js +1 -21
  68. package/dist/bd/nid.js.map +1 -1
  69. package/dist/be/bis.d.ts +2 -2
  70. package/dist/be/bis.js +1 -11
  71. package/dist/be/bis.js.map +1 -1
  72. package/dist/be/nn.d.ts +2 -2
  73. package/dist/be/nn.js +1 -11
  74. package/dist/be/nn.js.map +1 -1
  75. package/dist/be/vat.d.ts +2 -2
  76. package/dist/be/vat.js +1 -9
  77. package/dist/be/vat.js.map +1 -1
  78. package/dist/bg/egn.d.ts +2 -2
  79. package/dist/bg/egn.js +3 -18
  80. package/dist/bg/egn.js.map +1 -1
  81. package/dist/bg/pnf.d.ts +2 -2
  82. package/dist/bg/pnf.js +1 -8
  83. package/dist/bg/pnf.js.map +1 -1
  84. package/dist/bg/vat.d.ts +2 -2
  85. package/dist/bg/vat.js +3 -9
  86. package/dist/bg/vat.js.map +1 -1
  87. package/dist/bh/cpr.d.ts +2 -2
  88. package/dist/bh/cpr.js +1 -17
  89. package/dist/bh/cpr.js.map +1 -1
  90. package/dist/bic.d.ts +13 -2
  91. package/dist/bic.js +1 -11
  92. package/dist/bic.js.map +1 -1
  93. package/dist/br/cnpj.d.ts +2 -2
  94. package/dist/br/cnpj.js +1 -20
  95. package/dist/br/cnpj.js.map +1 -1
  96. package/dist/br/cpf.d.ts +2 -2
  97. package/dist/br/cpf.js +1 -14
  98. package/dist/br/cpf.js.map +1 -1
  99. package/dist/by/unp.d.ts +2 -2
  100. package/dist/by/unp.js +3 -14
  101. package/dist/by/unp.js.map +1 -1
  102. package/dist/bz/tin.d.ts +2 -2
  103. package/dist/bz/tin.js +1 -26
  104. package/dist/bz/tin.js.map +1 -1
  105. package/dist/ca/bn.d.ts +2 -2
  106. package/dist/ca/bn.js +2 -10
  107. package/dist/ca/bn.js.map +1 -1
  108. package/dist/ca/sin.d.ts +2 -2
  109. package/dist/ca/sin.js +2 -10
  110. package/dist/ca/sin.js.map +1 -1
  111. package/dist/ch/ssn.d.ts +2 -2
  112. package/dist/ch/ssn.js +1 -9
  113. package/dist/ch/ssn.js.map +1 -1
  114. package/dist/ch/uid.d.ts +2 -2
  115. package/dist/ch/uid.js +1 -9
  116. package/dist/ch/uid.js.map +1 -1
  117. package/dist/ch/vat.d.ts +2 -2
  118. package/dist/ch/vat.js +1 -9
  119. package/dist/ch/vat.js.map +1 -1
  120. package/dist/cl/rut.d.ts +2 -2
  121. package/dist/cl/rut.js +1 -14
  122. package/dist/cl/rut.js.map +1 -1
  123. package/dist/cn/ric.d.ts +2 -2
  124. package/dist/cn/ric.js +3 -14
  125. package/dist/cn/ric.js.map +1 -1
  126. package/dist/cn/uscc.d.ts +2 -2
  127. package/dist/cn/uscc.js +1 -10
  128. package/dist/cn/uscc.js.map +1 -1
  129. package/dist/co/nit.d.ts +2 -2
  130. package/dist/co/nit.js +2 -12
  131. package/dist/co/nit.js.map +1 -1
  132. package/dist/cr/cpf.d.ts +18 -2
  133. package/dist/cr/cpf.js +1 -16
  134. package/dist/cr/cpf.js.map +1 -1
  135. package/dist/creditcard.d.ts +2 -2
  136. package/dist/creditcard.js +5 -14
  137. package/dist/creditcard.js.map +1 -1
  138. package/dist/cu/ni.d.ts +2 -2
  139. package/dist/cu/ni.js +2 -17
  140. package/dist/cu/ni.js.map +1 -1
  141. package/dist/cy/vat.d.ts +2 -2
  142. package/dist/cy/vat.js +1 -8
  143. package/dist/cy/vat.js.map +1 -1
  144. package/dist/cz/dic.d.ts +2 -2
  145. package/dist/cz/dic.js +7 -13
  146. package/dist/cz/dic.js.map +1 -1
  147. package/dist/cz/ico.d.ts +2 -2
  148. package/dist/cz/ico.js +2 -10
  149. package/dist/cz/ico.js.map +1 -1
  150. package/dist/cz/rc.d.ts +2 -2
  151. package/dist/cz/rc.js +2 -26
  152. package/dist/cz/rc.js.map +1 -1
  153. package/dist/de/handelsreg.d.ts +19 -2
  154. package/dist/de/handelsreg.js +1 -17
  155. package/dist/de/handelsreg.js.map +1 -1
  156. package/dist/de/idnr.d.ts +2 -2
  157. package/dist/de/idnr.js +2 -12
  158. package/dist/de/idnr.js.map +1 -1
  159. package/dist/de/stnr.d.ts +17 -2
  160. package/dist/de/stnr.js +1 -15
  161. package/dist/de/stnr.js.map +1 -1
  162. package/dist/de/svnr.d.ts +2 -2
  163. package/dist/de/svnr.js +2 -11
  164. package/dist/de/svnr.js.map +1 -1
  165. package/dist/de/vat.d.ts +2 -2
  166. package/dist/de/vat.js +2 -11
  167. package/dist/de/vat.js.map +1 -1
  168. package/dist/dk/cpr.d.ts +2 -2
  169. package/dist/dk/cpr.js +2 -10
  170. package/dist/dk/cpr.js.map +1 -1
  171. package/dist/dk/cvr.d.ts +2 -2
  172. package/dist/dk/cvr.js +2 -15
  173. package/dist/dk/cvr.js.map +1 -1
  174. package/dist/dk/vat.d.ts +2 -2
  175. package/dist/dk/vat.js +3 -13
  176. package/dist/dk/vat.js.map +1 -1
  177. package/dist/do/rnc.d.ts +2 -2
  178. package/dist/do/rnc.js +2 -16
  179. package/dist/do/rnc.js.map +1 -1
  180. package/dist/ec/ruc.d.ts +2 -2
  181. package/dist/ec/ruc.js +1 -18
  182. package/dist/ec/ruc.js.map +1 -1
  183. package/dist/ee/ik.d.ts +2 -2
  184. package/dist/ee/ik.js +3 -20
  185. package/dist/ee/ik.js.map +1 -1
  186. package/dist/ee/registrikood.d.ts +2 -2
  187. package/dist/ee/registrikood.js +1 -8
  188. package/dist/ee/registrikood.js.map +1 -1
  189. package/dist/ee/vat.d.ts +2 -2
  190. package/dist/ee/vat.js +2 -10
  191. package/dist/ee/vat.js.map +1 -1
  192. package/dist/eg/tn.d.ts +11 -2
  193. package/dist/eg/tn.js +1 -9
  194. package/dist/eg/tn.js.map +1 -1
  195. package/dist/es/cif.d.ts +2 -2
  196. package/dist/es/cif.js +1 -10
  197. package/dist/es/cif.js.map +1 -1
  198. package/dist/es/dni.d.ts +2 -2
  199. package/dist/es/dni.js +1 -9
  200. package/dist/es/dni.js.map +1 -1
  201. package/dist/es/nie.d.ts +2 -2
  202. package/dist/es/nie.js +1 -9
  203. package/dist/es/nie.js.map +1 -1
  204. package/dist/es/nss.d.ts +2 -2
  205. package/dist/es/nss.js +1 -16
  206. package/dist/es/nss.js.map +1 -1
  207. package/dist/es/vat.d.ts +2 -2
  208. package/dist/es/vat.js +5 -10
  209. package/dist/es/vat.js.map +1 -1
  210. package/dist/eu/vat.d.ts +2 -2
  211. package/dist/eu/vat.js +1 -0
  212. package/dist/eu/vat.js.map +1 -1
  213. package/dist/fi/hetu.d.ts +2 -2
  214. package/dist/fi/hetu.js +2 -10
  215. package/dist/fi/hetu.js.map +1 -1
  216. package/dist/fi/vat.d.ts +2 -2
  217. package/dist/fi/vat.js +3 -10
  218. package/dist/fi/vat.js.map +1 -1
  219. package/dist/fi/ytunnus.d.ts +2 -2
  220. package/dist/fi/ytunnus.js +2 -10
  221. package/dist/fi/ytunnus.js.map +1 -1
  222. package/dist/fr/nif.d.ts +2 -2
  223. package/dist/fr/nif.js +1 -9
  224. package/dist/fr/nif.js.map +1 -1
  225. package/dist/fr/nir.d.ts +2 -2
  226. package/dist/fr/nir.js +2 -14
  227. package/dist/fr/nir.js.map +1 -1
  228. package/dist/fr/siren.d.ts +2 -2
  229. package/dist/fr/siren.js +2 -10
  230. package/dist/fr/siren.js.map +1 -1
  231. package/dist/fr/siret.d.ts +2 -2
  232. package/dist/fr/siret.js +2 -11
  233. package/dist/fr/siret.js.map +1 -1
  234. package/dist/fr/tva.d.ts +2 -2
  235. package/dist/fr/tva.js +2 -12
  236. package/dist/fr/tva.js.map +1 -1
  237. package/dist/gb/nino.d.ts +12 -2
  238. package/dist/gb/nino.js +1 -10
  239. package/dist/gb/nino.js.map +1 -1
  240. package/dist/gb/sedol.d.ts +2 -2
  241. package/dist/gb/sedol.js +1 -11
  242. package/dist/gb/sedol.js.map +1 -1
  243. package/dist/gb/utr.d.ts +2 -2
  244. package/dist/gb/utr.js +2 -10
  245. package/dist/gb/utr.js.map +1 -1
  246. package/dist/gb/vat.d.ts +2 -2
  247. package/dist/gb/vat.js +2 -11
  248. package/dist/gb/vat.js.map +1 -1
  249. package/dist/ge/pin.d.ts +14 -2
  250. package/dist/ge/pin.js +1 -12
  251. package/dist/ge/pin.js.map +1 -1
  252. package/dist/gh/tin.d.ts +2 -2
  253. package/dist/gh/tin.js +1 -15
  254. package/dist/gh/tin.js.map +1 -1
  255. package/dist/gr/amka.d.ts +2 -2
  256. package/dist/gr/amka.js +3 -11
  257. package/dist/gr/amka.js.map +1 -1
  258. package/dist/gr/vat.d.ts +2 -2
  259. package/dist/gr/vat.js +1 -10
  260. package/dist/gr/vat.js.map +1 -1
  261. package/dist/gt/nit.d.ts +2 -2
  262. package/dist/gt/nit.js +1 -12
  263. package/dist/gt/nit.js.map +1 -1
  264. package/dist/hk/hkid.d.ts +2 -2
  265. package/dist/hk/hkid.js +1 -10
  266. package/dist/hk/hkid.js.map +1 -1
  267. package/dist/hr/vat.d.ts +2 -2
  268. package/dist/hr/vat.js +2 -9
  269. package/dist/hr/vat.js.map +1 -1
  270. package/dist/hu/vat.d.ts +2 -2
  271. package/dist/hu/vat.js +3 -10
  272. package/dist/hu/vat.js.map +1 -1
  273. package/dist/iban.d.ts +2 -2
  274. package/dist/iban.js +2 -8
  275. package/dist/iban.js.map +1 -1
  276. package/dist/id/npwp.d.ts +2 -2
  277. package/dist/id/npwp.js +3 -13
  278. package/dist/id/npwp.js.map +1 -1
  279. package/dist/ie/pps.d.ts +2 -2
  280. package/dist/ie/pps.js +1 -10
  281. package/dist/ie/pps.js.map +1 -1
  282. package/dist/ie/vat.d.ts +2 -2
  283. package/dist/ie/vat.js +1 -9
  284. package/dist/ie/vat.js.map +1 -1
  285. package/dist/il/idnr.d.ts +2 -2
  286. package/dist/il/idnr.js +2 -9
  287. package/dist/il/idnr.js.map +1 -1
  288. package/dist/in/aadhaar.d.ts +2 -2
  289. package/dist/in/aadhaar.js +2 -12
  290. package/dist/in/aadhaar.js.map +1 -1
  291. package/dist/in/gstin.d.ts +2 -2
  292. package/dist/in/gstin.js +1 -10
  293. package/dist/in/gstin.js.map +1 -1
  294. package/dist/in/pan.d.ts +15 -2
  295. package/dist/in/pan.js +1 -13
  296. package/dist/in/pan.js.map +1 -1
  297. package/dist/index.d.ts +2 -2
  298. package/dist/index.js +6 -6
  299. package/dist/iq/nid.d.ts +16 -2
  300. package/dist/iq/nid.js +2 -15
  301. package/dist/iq/nid.js.map +1 -1
  302. package/dist/ir/nid.d.ts +2 -2
  303. package/dist/ir/nid.js +2 -24
  304. package/dist/ir/nid.js.map +1 -1
  305. package/dist/is/kennitala.d.ts +2 -2
  306. package/dist/is/kennitala.js +2 -13
  307. package/dist/is/kennitala.js.map +1 -1
  308. package/dist/is/vsk.d.ts +9 -2
  309. package/dist/is/vsk.js +1 -7
  310. package/dist/is/vsk.js.map +1 -1
  311. package/dist/isin.d.ts +2 -2
  312. package/dist/isin.js +2 -12
  313. package/dist/isin.js.map +1 -1
  314. package/dist/it/codicefiscale.d.ts +2 -2
  315. package/dist/it/codicefiscale.js +1 -21
  316. package/dist/it/codicefiscale.js.map +1 -1
  317. package/dist/it/iva.d.ts +2 -2
  318. package/dist/it/iva.js +2 -9
  319. package/dist/it/iva.js.map +1 -1
  320. package/dist/jp/cn.d.ts +2 -2
  321. package/dist/jp/cn.js +1 -9
  322. package/dist/jp/cn.js.map +1 -1
  323. package/dist/jp/mynumber.d.ts +2 -2
  324. package/dist/jp/mynumber.js +1 -10
  325. package/dist/jp/mynumber.js.map +1 -1
  326. package/dist/kr/brn.d.ts +11 -2
  327. package/dist/kr/brn.js +1 -9
  328. package/dist/kr/brn.js.map +1 -1
  329. package/dist/kr/rrn.d.ts +2 -2
  330. package/dist/kr/rrn.js +2 -11
  331. package/dist/kr/rrn.js.map +1 -1
  332. package/dist/kw/civil.d.ts +2 -2
  333. package/dist/kw/civil.js +2 -14
  334. package/dist/kw/civil.js.map +1 -1
  335. package/dist/kz/iin.d.ts +2 -2
  336. package/dist/kz/iin.js +3 -18
  337. package/dist/kz/iin.js.map +1 -1
  338. package/dist/lei.d.ts +2 -2
  339. package/dist/lei.js +2 -11
  340. package/dist/lei.js.map +1 -1
  341. package/dist/li/peid.d.ts +11 -2
  342. package/dist/li/peid.js +1 -9
  343. package/dist/li/peid.js.map +1 -1
  344. package/dist/lk/nic.d.ts +2 -2
  345. package/dist/lk/nic.js +2 -14
  346. package/dist/lk/nic.js.map +1 -1
  347. package/dist/lt/asmens.d.ts +2 -2
  348. package/dist/lt/asmens.js +2 -1
  349. package/dist/lt/asmens.js.map +1 -1
  350. package/dist/lt/vat.d.ts +2 -2
  351. package/dist/lt/vat.js +2 -10
  352. package/dist/lt/vat.js.map +1 -1
  353. package/dist/lu/vat.d.ts +2 -2
  354. package/dist/lu/vat.js +2 -8
  355. package/dist/lu/vat.js.map +1 -1
  356. package/dist/luhn.d.ts +2 -2
  357. package/dist/luhn.js +2 -9
  358. package/dist/luhn.js.map +1 -1
  359. package/dist/lv/vat.d.ts +2 -2
  360. package/dist/lv/vat.js +2 -28
  361. package/dist/lv/vat.js.map +1 -1
  362. package/dist/ma/ice.d.ts +2 -2
  363. package/dist/ma/ice.js +2 -11
  364. package/dist/ma/ice.js.map +1 -1
  365. package/dist/mc/tva.d.ts +2 -2
  366. package/dist/mc/tva.js +1 -0
  367. package/dist/mc/tva.js.map +1 -1
  368. package/dist/md/idno.d.ts +2 -2
  369. package/dist/md/idno.js +1 -10
  370. package/dist/md/idno.js.map +1 -1
  371. package/dist/me/pib.d.ts +2 -2
  372. package/dist/me/pib.js +2 -10
  373. package/dist/me/pib.js.map +1 -1
  374. package/dist/mk/edb.d.ts +2 -2
  375. package/dist/mk/edb.js +2 -11
  376. package/dist/mk/edb.js.map +1 -1
  377. package/dist/mt/vat.d.ts +2 -2
  378. package/dist/mt/vat.js +3 -10
  379. package/dist/mt/vat.js.map +1 -1
  380. package/dist/mu/brn.d.ts +23 -2
  381. package/dist/mu/brn.js +1 -21
  382. package/dist/mu/brn.js.map +1 -1
  383. package/dist/mx/clabe.d.ts +2 -2
  384. package/dist/mx/clabe.js +1 -14
  385. package/dist/mx/clabe.js.map +1 -1
  386. package/dist/mx/curp.d.ts +2 -2
  387. package/dist/mx/curp.js +3 -17
  388. package/dist/mx/curp.js.map +1 -1
  389. package/dist/mx/rfc.d.ts +2 -2
  390. package/dist/mx/rfc.js +3 -19
  391. package/dist/mx/rfc.js.map +1 -1
  392. package/dist/my/nric.d.ts +2 -2
  393. package/dist/my/nric.js +2 -14
  394. package/dist/my/nric.js.map +1 -1
  395. package/dist/ng/nin.d.ts +13 -2
  396. package/dist/ng/nin.js +1 -11
  397. package/dist/ng/nin.js.map +1 -1
  398. package/dist/ni/ruc.d.ts +2 -2
  399. package/dist/ni/ruc.js +1 -23
  400. package/dist/ni/ruc.js.map +1 -1
  401. package/dist/nl/bsn.d.ts +2 -2
  402. package/dist/nl/bsn.js +2 -10
  403. package/dist/nl/bsn.js.map +1 -1
  404. package/dist/nl/kvk.d.ts +9 -2
  405. package/dist/nl/kvk.js +1 -7
  406. package/dist/nl/kvk.js.map +1 -1
  407. package/dist/nl/vat.d.ts +2 -2
  408. package/dist/nl/vat.js +1 -13
  409. package/dist/nl/vat.js.map +1 -1
  410. package/dist/no/fodselsnummer.d.ts +2 -2
  411. package/dist/no/fodselsnummer.js +2 -10
  412. package/dist/no/fodselsnummer.js.map +1 -1
  413. package/dist/no/mva.d.ts +2 -2
  414. package/dist/no/mva.js +1 -8
  415. package/dist/no/mva.js.map +1 -1
  416. package/dist/no/orgnr.d.ts +2 -2
  417. package/dist/no/orgnr.js +2 -8
  418. package/dist/no/orgnr.js.map +1 -1
  419. package/dist/nz/ird.d.ts +2 -2
  420. package/dist/nz/ird.js +2 -11
  421. package/dist/nz/ird.js.map +1 -1
  422. package/dist/pa/ruc.d.ts +2 -2
  423. package/dist/pa/ruc.js +1 -18
  424. package/dist/pa/ruc.js.map +1 -1
  425. package/dist/patterns.d.ts +8 -7
  426. package/dist/patterns.js +3 -5
  427. package/dist/patterns.js.map +1 -1
  428. package/dist/pe/ruc.d.ts +2 -2
  429. package/dist/pe/ruc.js +1 -15
  430. package/dist/pe/ruc.js.map +1 -1
  431. package/dist/ph/philid.d.ts +14 -2
  432. package/dist/ph/philid.js +1 -12
  433. package/dist/ph/philid.js.map +1 -1
  434. package/dist/pk/cnic.d.ts +14 -2
  435. package/dist/pk/cnic.js +1 -12
  436. package/dist/pk/cnic.js.map +1 -1
  437. package/dist/pl/nip.d.ts +2 -2
  438. package/dist/pl/nip.js +2 -10
  439. package/dist/pl/nip.js.map +1 -1
  440. package/dist/pl/pesel.d.ts +2 -2
  441. package/dist/pl/pesel.js +3 -13
  442. package/dist/pl/pesel.js.map +1 -1
  443. package/dist/pl/regon.d.ts +2 -2
  444. package/dist/pl/regon.js +2 -11
  445. package/dist/pl/regon.js.map +1 -1
  446. package/dist/pt/cc.d.ts +2 -2
  447. package/dist/pt/cc.js +1 -9
  448. package/dist/pt/cc.js.map +1 -1
  449. package/dist/pt/vat.d.ts +2 -2
  450. package/dist/pt/vat.js +3 -11
  451. package/dist/pt/vat.js.map +1 -1
  452. package/dist/ro/cnp.d.ts +2 -2
  453. package/dist/ro/cnp.js +4 -14
  454. package/dist/ro/cnp.js.map +1 -1
  455. package/dist/ro/vat.d.ts +2 -2
  456. package/dist/ro/vat.js +13 -11
  457. package/dist/ro/vat.js.map +1 -1
  458. package/dist/rs/pib.d.ts +2 -2
  459. package/dist/rs/pib.js +1 -9
  460. package/dist/rs/pib.js.map +1 -1
  461. package/dist/ru/inn.d.ts +2 -2
  462. package/dist/ru/inn.js +1 -11
  463. package/dist/ru/inn.js.map +1 -1
  464. package/dist/se/orgnr.d.ts +2 -2
  465. package/dist/se/orgnr.js +2 -9
  466. package/dist/se/orgnr.js.map +1 -1
  467. package/dist/se/personnummer.d.ts +2 -2
  468. package/dist/se/personnummer.js +7 -15
  469. package/dist/se/personnummer.js.map +1 -1
  470. package/dist/se/vat.d.ts +2 -2
  471. package/dist/se/vat.js +3 -10
  472. package/dist/se/vat.js.map +1 -1
  473. package/dist/sg/uen.d.ts +2 -2
  474. package/dist/sg/uen.js +1 -15
  475. package/dist/sg/uen.js.map +1 -1
  476. package/dist/si/emso.d.ts +2 -2
  477. package/dist/si/emso.js +3 -18
  478. package/dist/si/emso.js.map +1 -1
  479. package/dist/si/vat.d.ts +2 -2
  480. package/dist/si/vat.js +3 -12
  481. package/dist/si/vat.js.map +1 -1
  482. package/dist/sk/dic.d.ts +2 -2
  483. package/dist/sk/dic.js +1 -0
  484. package/dist/sk/dic.js.map +1 -1
  485. package/dist/sk/ico.d.ts +2 -2
  486. package/dist/sk/ico.js +1 -0
  487. package/dist/sk/ico.js.map +1 -1
  488. package/dist/sk/rc.d.ts +2 -2
  489. package/dist/sk/rc.js +1 -0
  490. package/dist/sk/rc.js.map +1 -1
  491. package/dist/th/tin.d.ts +2 -2
  492. package/dist/th/tin.js +1 -11
  493. package/dist/th/tin.js.map +1 -1
  494. package/dist/tr/tckimlik.d.ts +2 -2
  495. package/dist/tr/tckimlik.js +1 -20
  496. package/dist/tr/tckimlik.js.map +1 -1
  497. package/dist/tr/vkn.d.ts +2 -2
  498. package/dist/tr/vkn.js +1 -20
  499. package/dist/tr/vkn.js.map +1 -1
  500. package/dist/tw/ubn.d.ts +2 -2
  501. package/dist/tw/ubn.js +1 -11
  502. package/dist/tw/ubn.js.map +1 -1
  503. package/dist/types.d.ts +26 -4
  504. package/dist/ua/edrpou.d.ts +2 -2
  505. package/dist/ua/edrpou.js +1 -11
  506. package/dist/ua/edrpou.js.map +1 -1
  507. package/dist/us/ein.d.ts +12 -2
  508. package/dist/us/ein.js +1 -10
  509. package/dist/us/ein.js.map +1 -1
  510. package/dist/us/itin.d.ts +13 -2
  511. package/dist/us/itin.js +1 -11
  512. package/dist/us/itin.js.map +1 -1
  513. package/dist/us/rtn.d.ts +2 -2
  514. package/dist/us/rtn.js +3 -18
  515. package/dist/us/rtn.js.map +1 -1
  516. package/dist/us/ssn.d.ts +13 -2
  517. package/dist/us/ssn.js +1 -11
  518. package/dist/us/ssn.js.map +1 -1
  519. package/dist/uy/rut.d.ts +2 -2
  520. package/dist/uy/rut.js +1 -14
  521. package/dist/uy/rut.js.map +1 -1
  522. package/dist/ve/rif.d.ts +2 -2
  523. package/dist/ve/rif.js +2 -15
  524. package/dist/ve/rif.js.map +1 -1
  525. package/dist/vn/mst.d.ts +2 -2
  526. package/dist/vn/mst.js +1 -12
  527. package/dist/vn/mst.js.map +1 -1
  528. package/dist/za/idnr.d.ts +2 -2
  529. package/dist/za/idnr.js +3 -16
  530. package/dist/za/idnr.js.map +1 -1
  531. package/package.json +22 -12
  532. package/src/_checksums/mod1110.ts +1 -1
  533. package/src/ad/nrt.ts +7 -3
  534. package/src/ae/eid.ts +7 -3
  535. package/src/ai/tin.ts +7 -3
  536. package/src/al/nipt.ts +7 -3
  537. package/src/am/tin.ts +7 -3
  538. package/src/ar/cbu.ts +7 -3
  539. package/src/ar/cuit.ts +7 -3
  540. package/src/ar/dni.ts +7 -3
  541. package/src/at/businessid.ts +7 -3
  542. package/src/at/tin.ts +7 -3
  543. package/src/at/uid.ts +7 -3
  544. package/src/at/vnr.ts +37 -9
  545. package/src/au/abn.ts +7 -3
  546. package/src/au/acn.ts +7 -3
  547. package/src/au/tfn.ts +7 -3
  548. package/src/az/voen.ts +7 -3
  549. package/src/ba/jmbg.ts +8 -7
  550. package/src/bd/nid.ts +7 -3
  551. package/src/be/bis.ts +8 -4
  552. package/src/be/nn.ts +7 -3
  553. package/src/be/vat.ts +7 -3
  554. package/src/bg/egn.ts +8 -7
  555. package/src/bg/pnf.ts +7 -3
  556. package/src/bg/vat.ts +8 -3
  557. package/src/bh/cpr.ts +7 -3
  558. package/src/bic.ts +7 -3
  559. package/src/br/cnpj.ts +7 -3
  560. package/src/br/cpf.ts +7 -3
  561. package/src/by/unp.ts +10 -5
  562. package/src/bz/tin.ts +7 -3
  563. package/src/ca/bn.ts +7 -3
  564. package/src/ca/sin.ts +7 -3
  565. package/src/ch/ssn.ts +7 -3
  566. package/src/ch/uid.ts +7 -3
  567. package/src/ch/vat.ts +9 -5
  568. package/src/cl/rut.ts +7 -3
  569. package/src/cn/ric.ts +8 -7
  570. package/src/cn/uscc.ts +7 -3
  571. package/src/co/nit.ts +8 -4
  572. package/src/cr/cpf.ts +7 -3
  573. package/src/creditcard.ts +10 -6
  574. package/src/cu/ni.ts +8 -7
  575. package/src/cy/vat.ts +7 -3
  576. package/src/cz/dic.ts +9 -4
  577. package/src/cz/ico.ts +7 -3
  578. package/src/cz/rc.ts +8 -7
  579. package/src/de/handelsreg.ts +7 -3
  580. package/src/de/idnr.ts +7 -3
  581. package/src/de/stnr.ts +7 -3
  582. package/src/de/svnr.ts +7 -3
  583. package/src/de/vat.ts +7 -3
  584. package/src/dk/cpr.ts +8 -7
  585. package/src/dk/cvr.ts +7 -3
  586. package/src/dk/vat.ts +8 -3
  587. package/src/do/rnc.ts +7 -3
  588. package/src/ec/ruc.ts +7 -3
  589. package/src/ee/ik.ts +8 -7
  590. package/src/ee/registrikood.ts +8 -4
  591. package/src/ee/vat.ts +7 -3
  592. package/src/eg/tn.ts +7 -3
  593. package/src/es/cif.ts +8 -4
  594. package/src/es/dni.ts +7 -3
  595. package/src/es/nie.ts +8 -4
  596. package/src/es/nss.ts +7 -3
  597. package/src/es/vat.ts +13 -5
  598. package/src/eu/vat.ts +10 -6
  599. package/src/fi/hetu.ts +8 -7
  600. package/src/fi/vat.ts +8 -3
  601. package/src/fi/ytunnus.ts +7 -3
  602. package/src/fr/nif.ts +7 -3
  603. package/src/fr/nir.ts +8 -7
  604. package/src/fr/siren.ts +7 -3
  605. package/src/fr/siret.ts +7 -3
  606. package/src/fr/tva.ts +7 -3
  607. package/src/gb/nino.ts +7 -3
  608. package/src/gb/sedol.ts +7 -3
  609. package/src/gb/utr.ts +7 -3
  610. package/src/gb/vat.ts +7 -3
  611. package/src/ge/pin.ts +7 -3
  612. package/src/gh/tin.ts +7 -3
  613. package/src/gr/amka.ts +7 -3
  614. package/src/gr/vat.ts +7 -3
  615. package/src/gt/nit.ts +7 -3
  616. package/src/hk/hkid.ts +7 -3
  617. package/src/hr/vat.ts +7 -3
  618. package/src/hu/vat.ts +8 -3
  619. package/src/iban.ts +7 -3
  620. package/src/id/npwp.ts +7 -3
  621. package/src/ie/pps.ts +7 -3
  622. package/src/ie/vat.ts +7 -3
  623. package/src/il/idnr.ts +7 -3
  624. package/src/in/aadhaar.ts +7 -3
  625. package/src/in/gstin.ts +7 -3
  626. package/src/in/pan.ts +7 -3
  627. package/src/index.ts +3 -0
  628. package/src/iq/nid.ts +7 -3
  629. package/src/ir/nid.ts +7 -3
  630. package/src/is/kennitala.ts +7 -3
  631. package/src/is/vsk.ts +7 -3
  632. package/src/isin.ts +7 -3
  633. package/src/it/codicefiscale.ts +11 -7
  634. package/src/it/iva.ts +7 -3
  635. package/src/jp/cn.ts +7 -3
  636. package/src/jp/mynumber.ts +7 -3
  637. package/src/kr/brn.ts +7 -3
  638. package/src/kr/rrn.ts +8 -7
  639. package/src/kw/civil.ts +8 -7
  640. package/src/kz/iin.ts +8 -8
  641. package/src/lei.ts +7 -3
  642. package/src/li/peid.ts +7 -3
  643. package/src/lk/nic.ts +8 -7
  644. package/src/lt/asmens.ts +8 -4
  645. package/src/lt/vat.ts +8 -3
  646. package/src/lu/vat.ts +8 -3
  647. package/src/luhn.ts +7 -3
  648. package/src/lv/vat.ts +7 -3
  649. package/src/ma/ice.ts +7 -3
  650. package/src/mc/tva.ts +9 -5
  651. package/src/md/idno.ts +7 -3
  652. package/src/me/pib.ts +7 -3
  653. package/src/mk/edb.ts +7 -3
  654. package/src/mt/vat.ts +8 -3
  655. package/src/mu/brn.ts +7 -3
  656. package/src/mx/clabe.ts +7 -3
  657. package/src/mx/curp.ts +9 -8
  658. package/src/mx/rfc.ts +8 -4
  659. package/src/my/nric.ts +8 -7
  660. package/src/ng/nin.ts +7 -3
  661. package/src/ni/ruc.ts +7 -3
  662. package/src/nl/bsn.ts +7 -3
  663. package/src/nl/kvk.ts +7 -3
  664. package/src/nl/vat.ts +7 -3
  665. package/src/no/fodselsnummer.ts +11 -7
  666. package/src/no/mva.ts +9 -5
  667. package/src/no/orgnr.ts +7 -3
  668. package/src/nz/ird.ts +8 -4
  669. package/src/pa/ruc.ts +7 -3
  670. package/src/patterns.ts +49 -18
  671. package/src/pe/ruc.ts +7 -3
  672. package/src/ph/philid.ts +7 -3
  673. package/src/pk/cnic.ts +7 -3
  674. package/src/pl/nip.ts +7 -3
  675. package/src/pl/pesel.ts +8 -7
  676. package/src/pl/regon.ts +7 -3
  677. package/src/pt/cc.ts +7 -3
  678. package/src/pt/vat.ts +8 -3
  679. package/src/ro/cnp.ts +15 -8
  680. package/src/ro/vat.ts +8 -3
  681. package/src/rs/pib.ts +7 -3
  682. package/src/ru/inn.ts +7 -3
  683. package/src/se/orgnr.ts +7 -3
  684. package/src/se/personnummer.ts +35 -33
  685. package/src/se/vat.ts +8 -3
  686. package/src/sg/uen.ts +7 -3
  687. package/src/si/emso.ts +8 -7
  688. package/src/si/vat.ts +8 -3
  689. package/src/sk/dic.ts +8 -4
  690. package/src/sk/ico.ts +3 -2
  691. package/src/sk/rc.ts +6 -2
  692. package/src/th/tin.ts +7 -3
  693. package/src/tr/tckimlik.ts +7 -3
  694. package/src/tr/vkn.ts +7 -3
  695. package/src/tw/ubn.ts +7 -3
  696. package/src/types.ts +34 -3
  697. package/src/ua/edrpou.ts +7 -3
  698. package/src/us/ein.ts +7 -3
  699. package/src/us/itin.ts +7 -3
  700. package/src/us/rtn.ts +16 -8
  701. package/src/us/ssn.ts +7 -3
  702. package/src/uy/rut.ts +7 -3
  703. package/src/ve/rif.ts +8 -6
  704. package/src/vn/mst.ts +7 -3
  705. package/src/za/idnr.ts +8 -7
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
  */
@@ -186,6 +168,7 @@ const ruc = {
186
168
  abbreviation: "RUC",
187
169
  aliases: ["RUC"],
188
170
  candidatePattern: "\\d{1,2}-?\\d{1,4}-?\\d{1,6}",
171
+ scope: "country",
189
172
  country: "PA",
190
173
  entityType: "any",
191
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 (len < 3 || len > 4) {\n return null;\n }\n // The NT designation requires exactly 4 segments\n // (xxNT-NT-xxx-xxx); any other 4-segment shape is\n // unrecognized, and a 3-segment \"NT\" in position 1\n // is malformed.\n if ((len === 4) !== (segments[1] === \"NT\")) {\n return null;\n }\n\n let buf: string;\n let isOld = false;\n\n // SAFETY: length check above ensures 3..4 segments.\n // eslint-disable-next-line no-non-null-assertion\n const s0 = segments[0]!;\n // eslint-disable-next-line no-non-null-assertion\n const s1 = segments[1]!;\n // eslint-disable-next-line no-non-null-assertion\n const s2 = segments[2]!;\n\n if (raw[0] === \"E\") {\n // Foreign person (E-xxx-xxx)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"50\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s1 === \"NT\") {\n // NT designation (xxNT-NT-xxx-xxx)\n // SAFETY: NT branch implies len === 4 (checked above).\n // eslint-disable-next-line no-non-null-assertion\n const s3 = segments[3]!;\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"43\" +\n z(3 - s2.length) +\n s2 +\n z(5 - s3.length) +\n s3;\n } else if (s0.endsWith(\"AV\")) {\n // AV suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"15\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.endsWith(\"PI\")) {\n // PI suffix\n const prefix = s0.slice(0, -2);\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(4 - prefix.length) +\n prefix +\n \"79\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0 === \"PE\") {\n // PE prefix (foreign-born)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"75\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (raw[0] === \"N\") {\n // N prefix (naturalized citizen)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n \"00\" +\n \"40\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else if (s0.length >= 1 && s0.length <= 2) {\n // Natural person (province 1–2 digits)\n buf =\n z(4 - s1.length) +\n \"0000005\" +\n z(2 - s0.length) +\n s0 +\n \"00\" +\n z(3 - s1.length) +\n s1 +\n z(5 - s2.length) +\n s2;\n } else {\n // Juridical (company)\n buf =\n z(10 - s0.length) +\n s0 +\n z(4 - s1.length) +\n s1 +\n z(6 - s2.length) +\n s2;\n // SAFETY: juridical branch zero-pads s0 to at least\n // 10 chars (z(10 - s0.length)), so buf[3..6] are\n // always defined.\n isOld =\n buf[3] === \"0\" &&\n buf[4] === \"0\" &&\n // eslint-disable-next-line no-non-null-assertion\n buf[5]! < \"5\";\n }\n\n // Apply legacy cross-reference for old format\n if (isOld) {\n const key = buf.slice(5, 7);\n const mapped = LEGACY_MAP[key] ?? key;\n buf = buf.slice(0, 5) + mapped + buf.slice(7);\n }\n\n const dv1 = digitDV(buf, isOld);\n const dv2 = digitDV(\n buf + String.fromCharCode(48 + dv1),\n isOld,\n );\n\n return `${dv1}${dv2}`;\n};\n\n/**\n * Compact: normalize Unicode, collapse whitespace,\n * strip colons around DV, and uppercase. Keeps\n * hyphens (structural separators) and a single\n * space before \"DV\".\n */\nconst compact = (value: string): string => {\n let v = clean(value, \"\").trim().toUpperCase();\n // Normalize DV portion: \" DV: 49\" -> \" DV49\"\n v = v.replace(/\\s*DV[:\\s]*(\\d{2})$/, \" DV$1\");\n // Collapse any remaining internal whitespace\n v = v.replace(/\\s+/g, \" \");\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n // Extract the DV portion: \"...DV49\", \"...DV:49\",\n // or \"... DV 49\"\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n\n if (!dvMatch) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must include DV \" +\n \"(dígito verificador)\",\n );\n }\n\n // SAFETY: both DV regex capture groups are required.\n // eslint-disable-next-line no-non-null-assertion\n const rucPart = dvMatch[1]!;\n // eslint-disable-next-line no-non-null-assertion\n const dvPart = dvMatch[2]!;\n\n // The RUC part should be hyphen-separated segments\n const segments = rucPart.replace(/-+$/, \"\").split(\"-\");\n\n if (segments.length < 3 || segments.length > 4) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC must have 3–4 \" +\n \"hyphen-separated segments\",\n );\n }\n\n if (segments.some((s) => s.length === 0)) {\n return err(\n \"INVALID_FORMAT\",\n \"Panama RUC segments must not be empty\",\n );\n }\n\n const computed = computeDV(segments, rucPart);\n if (computed === null) {\n return err(\n \"INVALID_FORMAT\",\n \"Unrecognized Panama RUC format\",\n );\n }\n\n if (computed !== dvPart) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Panama RUC dígito verificador \" + \"does not match\",\n );\n }\n\n const compactForm = segments.join(\"-\") + \" DV\" + dvPart;\n return { valid: true, compact: compactForm };\n};\n\n/**\n * Format: display as segments with \" DV XX\".\n */\nconst format = (value: string): string => {\n const v = compact(value);\n const dvMatch = v.match(/^(.+?)-?\\s*DV[:\\s]*(\\d{2})$/);\n if (!dvMatch) return v;\n\n // SAFETY: both DV regex capture groups are required.\n // eslint-disable-next-line no-non-null-assertion\n const rucPart = dvMatch[1]!.replace(/-+$/, \"\");\n // eslint-disable-next-line no-non-null-assertion\n const dvPart = dvMatch[2]!;\n const segments = rucPart.split(\"-\");\n return `${segments.join(\"-\")} DV ${dvPart}`;\n};\n\n/** Generate a random valid Panama RUC (natural person). */\nconst generate = (): string => {\n const province = String(randomInt(1, 13));\n const volume = String(randomInt(1, 9999));\n const folio = String(randomInt(1, 99999));\n const segments = [province, volume, folio];\n const dv = computeDV(segments, segments.join(\"-\"));\n if (dv === null) {\n throw new Error(\"Failed to compute Panama RUC DV\");\n }\n return `${segments.join(\"-\")} DV${dv}`;\n};\n\n/** Panama tax identification number. */\nconst ruc: Validator = {\n name: \"Tax Identification Number\",\n localName: \"Registro Único de Contribuyente\",\n abbreviation: \"RUC\",\n aliases: [\"RUC\"] as const,\n candidatePattern: \"\\\\d{1,2}-?\\\\d{1,4}-?\\\\d{1,6}\",\n country: \"PA\",\n entityType: \"any\",\n description: \"Tax identifier issued by Panama's DGI\",\n sourceUrl: \"https://dgi.mef.gob.pa/\",\n examples: [\n \"1-184-921 DV49\",\n \"2588017-1-831938 DV20\",\n ] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default ruc;\nexport { compact, format, validate, generate };\n"],"mappings":"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"}
1
+ {"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,8 +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
- type ValidatorPattern = {
5
- /** The validator this pattern was derived from. */validator: Validator; /** 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. */
6
6
  regex: RegExp;
7
7
  };
8
8
  /**
@@ -22,21 +22,22 @@ declare const toRegex: (v: Validator) => RegExp;
22
22
  /**
23
23
  * Build patterns for a list of validators.
24
24
  */
25
- declare const toPatterns: (validators: readonly Validator[]) => ValidatorPattern[];
25
+ declare const toPatterns: <TValidator extends Validator>(validators: readonly TValidator[]) => ValidatorPattern<TValidator>[];
26
26
  /**
27
27
  * Filter validators by country code and build
28
28
  * patterns.
29
29
  */
30
- 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>>[];
31
32
  /**
32
33
  * Filter validators by entity type and build
33
34
  * patterns.
34
35
  */
35
- 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>[];
36
37
  /**
37
38
  * Build patterns for ALL provided validators.
38
39
  */
39
- declare const allPatterns: (validators: readonly Validator[]) => ValidatorPattern[];
40
+ declare const allPatterns: <TValidator extends Validator>(validators: readonly TValidator[]) => ValidatorPattern<TValidator>[];
40
41
  //#endregion
41
42
  export { ValidatorPattern, allPatterns, byCountry, byEntityType, toPatterns, toRegex };
42
43
  //# sourceMappingURL=patterns.d.ts.map
package/dist/patterns.js CHANGED
@@ -160,11 +160,9 @@ const toPatterns = (validators) => validators.map((v) => ({
160
160
  validator: v,
161
161
  regex: toRegex(v)
162
162
  }));
163
- /**
164
- * Filter validators by country code and build
165
- * patterns.
166
- */
167
- 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
+ };
168
166
  /**
169
167
  * Filter validators by entity type and build
170
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 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 = (\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/ruc.d.ts CHANGED
@@ -1,4 +1,4 @@
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
4
  declare const compact: (value: string) => string;
@@ -12,7 +12,7 @@ declare const generate: () => string;
12
12
  * Examples sourced from python-stdnum test suite
13
13
  * (pe.ruc module).
14
14
  */
15
- declare const ruc: Validator;
15
+ declare const ruc: CountryValidator<"PE">;
16
16
  //#endregion
17
17
  export { compact, ruc as default, format, generate, validate };
18
18
  //# sourceMappingURL=ruc.d.ts.map
package/dist/pe/ruc.js CHANGED
@@ -4,21 +4,6 @@ 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,
@@ -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,
@@ -1 +1 @@
1
- {"version":3,"file":"ruc.js","names":[],"sources":["../../src/pe/ruc.ts"],"sourcesContent":["/**\n * RUC (Registro Único de Contribuyentes).\n *\n * Peruvian tax identification number assigned by SUNAT\n * (Superintendencia Nacional de Aduanas y de\n * Administración Tributaria). The number consists of\n * 11 digits: a type prefix (10 = person, 20 = company,\n * among others), 8 identification digits, and a check\n * digit computed using a weighted modulo 11 algorithm.\n *\n * Format: XXXXXXXXXXX (11 digits, no separators).\n *\n * @see https://www.sunat.gob.pe/\n * @see https://en.wikipedia.org/wiki/Tax_identification_number#Peru\n */\n\nimport { clean } from \"#util/clean\";\nimport { randomDigits, randomPick } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\n/**\n * Valid type prefixes for RUC numbers.\n * 10 = natural person, 15 = non-domiciled without RUC,\n * 17 = non-domiciled, 20 = legal entity,\n * others for specific entity types.\n */\nconst VALID_PREFIXES = new Set([\"10\", \"15\", \"17\", \"20\"]);\n\nconst WEIGHTS = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2] as const;\nconst GENERATE_PREFIXES = [\"10\", \"15\", \"17\", \"20\"] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -.\").trim();\n\n/**\n * Compute the RUC check digit using weighted sum\n * mod 11. Weights: [5, 4, 3, 2, 7, 6, 5, 4, 3, 2].\n *\n * check = 11 - (sum % 11); if >= 10, subtract 10.\n */\nconst calcCheckDigit = (body: string): number => {\n let sum = 0;\n for (const [i, weight] of WEIGHTS.entries()) {\n sum += Number(body[i]) * weight;\n }\n const remainder = 11 - (sum % 11);\n if (remainder >= 10) return remainder - 10;\n return remainder;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 11) {\n return err(\"INVALID_LENGTH\", \"RUC must be 11 digits\");\n }\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"RUC must contain only digits\",\n );\n }\n\n const prefix = v.slice(0, 2);\n if (!VALID_PREFIXES.has(prefix)) {\n return err(\n \"INVALID_COMPONENT\",\n \"RUC has an invalid type prefix\",\n );\n }\n\n const expected = calcCheckDigit(v.slice(0, 10));\n if (Number(v[10]) !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"RUC check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/** Generate a random valid Peruvian RUC. */\nconst generate = (): string => {\n const prefix = randomPick(GENERATE_PREFIXES);\n const body = prefix + randomDigits(8);\n return body + String(calcCheckDigit(body));\n};\n\n/**\n * Peruvian RUC (tax identification number).\n *\n * Examples sourced from python-stdnum test suite\n * (pe.ruc module).\n */\nconst ruc: Validator = {\n name: \"Peruvian Tax ID\",\n localName: \"Registro Único de Contribuyentes\",\n abbreviation: \"RUC\",\n aliases: [\"RUC\"] as const,\n candidatePattern: \"\\\\d{11}\",\n country: \"PE\",\n entityType: \"any\",\n compact,\n format,\n validate,\n sourceUrl: \"https://www.sunat.gob.pe/\",\n lengths: [11] as const,\n examples: [\"20131312955\", \"20100047218\"] as const,\n generate,\n};\n\nexport default ruc;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;;;;;;;;;AA6BA,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAM;CAAM;CAAM;CAAK,CAAC;AAExD,MAAM,UAAU;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;AAC9C,MAAM,oBAAoB;CAAC;CAAM;CAAM;CAAM;CAAK;AAElD,MAAM,WAAW,UACf,MAAM,OAAO,MAAM,CAAC,MAAM;;;;;;;AAQ5B,MAAM,kBAAkB,SAAyB;CAC/C,IAAI,MAAM;AACV,MAAK,MAAM,CAAC,GAAG,WAAW,QAAQ,SAAS,CACzC,QAAO,OAAO,KAAK,GAAG,GAAG;CAE3B,MAAM,YAAY,KAAM,MAAM;AAC9B,KAAI,aAAa,GAAI,QAAO,YAAY;AACxC,QAAO;;AAGT,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AAExB,KAAI,EAAE,WAAW,GACf,QAAO,IAAI,kBAAkB,wBAAwB;AAGvD,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,+BACD;CAGH,MAAM,SAAS,EAAE,MAAM,GAAG,EAAE;AAC5B,KAAI,CAAC,eAAe,IAAI,OAAO,CAC7B,QAAO,IACL,qBACA,iCACD;CAGH,MAAM,WAAW,eAAe,EAAE,MAAM,GAAG,GAAG,CAAC;AAC/C,KAAI,OAAO,EAAE,IAAI,KAAK,SACpB,QAAO,IACL,oBACA,iCACD;AAGH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B,QAAQ,MAAM;;AAGxD,MAAM,iBAAyB;CAE7B,MAAM,OADS,WAAW,kBAAkB,GACtB,aAAa,EAAE;AACrC,QAAO,OAAO,OAAO,eAAe,KAAK,CAAC;;;;;;;;AAS5C,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,MAAM;CAChB,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ;CACA;CACA;CACA,WAAW;CACX,SAAS,CAAC,GAAG;CACb,UAAU,CAAC,eAAe,cAAc;CACxC;CACD"}
1
+ {"version":3,"file":"ruc.js","names":[],"sources":["../../src/pe/ruc.ts"],"sourcesContent":["/**\n * RUC (Registro Único de Contribuyentes).\n *\n * Peruvian tax identification number assigned by SUNAT\n * (Superintendencia Nacional de Aduanas y de\n * Administración Tributaria). The number consists of\n * 11 digits: a type prefix (10 = person, 20 = company,\n * among others), 8 identification digits, and a check\n * digit computed using a weighted modulo 11 algorithm.\n *\n * Format: XXXXXXXXXXX (11 digits, no separators).\n *\n * @see https://www.sunat.gob.pe/\n * @see https://en.wikipedia.org/wiki/Tax_identification_number#Peru\n */\n\nimport type {\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport { clean } from \"#util/clean\";\nimport { randomDigits, randomPick } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\n/**\n * Valid type prefixes for RUC numbers.\n * 10 = natural person, 15 = non-domiciled without RUC,\n * 17 = non-domiciled, 20 = legal entity,\n * others for specific entity types.\n */\nconst VALID_PREFIXES = new Set([\"10\", \"15\", \"17\", \"20\"]);\n\nconst WEIGHTS = [5, 4, 3, 2, 7, 6, 5, 4, 3, 2] as const;\nconst GENERATE_PREFIXES = [\"10\", \"15\", \"17\", \"20\"] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -.\").trim();\n\n/**\n * Compute the RUC check digit using weighted sum\n * mod 11. Weights: [5, 4, 3, 2, 7, 6, 5, 4, 3, 2].\n *\n * check = 11 - (sum % 11); if >= 10, subtract 10.\n */\nconst calcCheckDigit = (body: string): number => {\n let sum = 0;\n for (const [i, weight] of WEIGHTS.entries()) {\n sum += Number(body[i]) * weight;\n }\n const remainder = 11 - (sum % 11);\n if (remainder >= 10) return remainder - 10;\n return remainder;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 11) {\n return err(\"INVALID_LENGTH\", \"RUC must be 11 digits\");\n }\n\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"RUC must contain only digits\",\n );\n }\n\n const prefix = v.slice(0, 2);\n if (!VALID_PREFIXES.has(prefix)) {\n return err(\n \"INVALID_COMPONENT\",\n \"RUC has an invalid type prefix\",\n );\n }\n\n const expected = calcCheckDigit(v.slice(0, 10));\n if (Number(v[10]) !== expected) {\n return err(\n \"INVALID_CHECKSUM\",\n \"RUC check digit does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/** Generate a random valid Peruvian RUC. */\nconst generate = (): string => {\n const prefix = randomPick(GENERATE_PREFIXES);\n const body = prefix + randomDigits(8);\n return body + String(calcCheckDigit(body));\n};\n\n/**\n * Peruvian RUC (tax identification number).\n *\n * Examples sourced from python-stdnum test suite\n * (pe.ruc module).\n */\nconst ruc: CountryValidator<\"PE\"> = {\n name: \"Peruvian Tax ID\",\n localName: \"Registro Único de Contribuyentes\",\n abbreviation: \"RUC\",\n aliases: [\"RUC\"] as const,\n candidatePattern: \"\\\\d{11}\",\n scope: \"country\",\n country: \"PE\",\n entityType: \"any\",\n compact,\n format,\n validate,\n sourceUrl: \"https://www.sunat.gob.pe/\",\n lengths: [11] as const,\n examples: [\"20131312955\", \"20100047218\"] as const,\n generate,\n};\n\nexport default ruc;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;AAgCA,MAAM,iBAAiB,IAAI,IAAI;CAAC;CAAM;CAAM;CAAM;AAAI,CAAC;AAEvD,MAAM,UAAU;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;AAAC;AAC7C,MAAM,oBAAoB;CAAC;CAAM;CAAM;CAAM;AAAI;AAEjD,MAAM,WAAW,UACf,MAAM,OAAO,KAAK,EAAE,KAAK;;;;;;;AAQ3B,MAAM,kBAAkB,SAAyB;CAC/C,IAAI,MAAM;CACV,KAAK,MAAM,CAAC,GAAG,WAAW,QAAQ,QAAQ,GACxC,OAAO,OAAO,KAAK,EAAE,IAAI;CAE3B,MAAM,YAAY,KAAM,MAAM;CAC9B,IAAI,aAAa,IAAI,OAAO,YAAY;CACxC,OAAO;AACT;AAEA,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,KAAK;CAEvB,IAAI,EAAE,WAAW,IACf,OAAO,IAAI,kBAAkB,uBAAuB;CAGtD,IAAI,CAAC,SAAS,CAAC,GACb,OAAO,IACL,kBACA,8BACF;CAGF,MAAM,SAAS,EAAE,MAAM,GAAG,CAAC;CAC3B,IAAI,CAAC,eAAe,IAAI,MAAM,GAC5B,OAAO,IACL,qBACA,gCACF;CAGF,MAAM,WAAW,eAAe,EAAE,MAAM,GAAG,EAAE,CAAC;CAC9C,IAAI,OAAO,EAAE,GAAG,MAAM,UACpB,OAAO,IACL,oBACA,gCACF;CAGF,OAAO;EAAE,OAAO;EAAM,SAAS;CAAE;AACnC;AAEA,MAAM,UAAU,UAA0B,QAAQ,KAAK;;AAGvD,MAAM,iBAAyB;CAE7B,MAAM,OADS,WAAW,iBACR,IAAI,aAAa,CAAC;CACpC,OAAO,OAAO,OAAO,eAAe,IAAI,CAAC;AAC3C;;;;;;;AAQA,MAAM,MAA8B;CAClC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,KAAK;CACf,kBAAkB;CAClB,OAAO;CACP,SAAS;CACT,YAAY;CACZ;CACA;CACA;CACA,WAAW;CACX,SAAS,CAAC,EAAE;CACZ,UAAU,CAAC,eAAe,aAAa;CACvC;AACF"}
@@ -1,13 +1,25 @@
1
- import { ValidateResult, Validator } from "../types.js";
1
+ import { CountryValidator, ValidateResult } from "../types.js";
2
2
 
3
3
  //#region src/ph/philid.d.ts
4
4
  /** Generate a random valid PhilID. */
5
5
  declare const generate: () => string;
6
+ /**
7
+ * PhilID (Philippine Identification System Number,
8
+ * PhilSys Card Number, PCN).
9
+ *
10
+ * 12-digit personal identifier issued under the
11
+ * Philippine Identification System (PhilSys).
12
+ * Formatted as XXXX-XXXXXXX-X. No check digit
13
+ * algorithm is publicly documented.
14
+ *
15
+ * @see https://en.wikipedia.org/wiki/Philippine_national_identity_card
16
+ * @see https://philsys.gov.ph/
17
+ */
6
18
  declare const compact: (value: string) => string;
7
19
  declare const validate: (value: string) => ValidateResult;
8
20
  declare const format: (value: string) => string;
9
21
  /** Philippine Identification System Number. */
10
- declare const philid: Validator;
22
+ declare const philid: CountryValidator<"PH">;
11
23
  //#endregion
12
24
  export { compact, philid as default, format, generate, validate };
13
25
  //# sourceMappingURL=philid.d.ts.map
package/dist/ph/philid.js CHANGED
@@ -3,18 +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/ph/philid.ts
6
- /**
7
- * PhilID (Philippine Identification System Number,
8
- * PhilSys Card Number, PCN).
9
- *
10
- * 12-digit personal identifier issued under the
11
- * Philippine Identification System (PhilSys).
12
- * Formatted as XXXX-XXXXXXX-X. No check digit
13
- * algorithm is publicly documented.
14
- *
15
- * @see https://en.wikipedia.org/wiki/Philippine_national_identity_card
16
- * @see https://philsys.gov.ph/
17
- */
18
6
  /** Generate a random valid PhilID. */
19
7
  const generate = () => randomDigits(12);
20
8
  const compact = (value) => clean(value, " -");
@@ -43,6 +31,7 @@ const philid = {
43
31
  "Philippine ID"
44
32
  ],
45
33
  candidatePattern: "\\d{4}-\\d{4}-\\d{4}-\\d{4}",
34
+ scope: "country",
46
35
  country: "PH",
47
36
  entityType: "person",
48
37
  description: "12-digit personal identifier issued under the Philippine Identification System",
@@ -1 +1 @@
1
- {"version":3,"file":"philid.js","names":[],"sources":["../../src/ph/philid.ts"],"sourcesContent":["/** Generate a random valid PhilID. */\nconst generate = (): string => randomDigits(12);\n\n/**\n * PhilID (Philippine Identification System Number,\n * PhilSys Card Number, PCN).\n *\n * 12-digit personal identifier issued under the\n * Philippine Identification System (PhilSys).\n * Formatted as XXXX-XXXXXXX-X. No check digit\n * algorithm is publicly documented.\n *\n * @see https://en.wikipedia.org/wiki/Philippine_national_identity_card\n * @see https://philsys.gov.ph/\n */\n\nimport { clean } from \"#util/clean\";\nimport { randomDigits } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"PhilID must be exactly 12 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"PhilID must contain only digits\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n if (v.length === 12) {\n return `${v.slice(0, 4)}-${v.slice(4, 11)}-${v.slice(11)}`;\n }\n return v;\n};\n\n/** Philippine Identification System Number. */\nconst philid: Validator = {\n name: \"Philippine Identification System Number\",\n localName: \"PhilSys Card Number\",\n abbreviation: \"PhilID\",\n aliases: [\"PhilID\", \"PhilSys\", \"Philippine ID\"] as const,\n candidatePattern: \"\\\\d{4}-\\\\d{4}-\\\\d{4}-\\\\d{4}\",\n country: \"PH\",\n entityType: \"person\",\n description:\n \"12-digit personal identifier issued under\" +\n \" the Philippine Identification System\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/\" +\n \"Philippine_national_identity_card\",\n lengths: [12] as const,\n examples: [\"123456789012\", \"000011112222\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default philid;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;AACA,MAAM,iBAAyB,aAAa,GAAG;AAsB/C,MAAM,WAAW,UACf,MAAM,OAAO,KAAK;AAEpB,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,mCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,kCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG;AAE1D,QAAO;;;AAIT,MAAM,SAAoB;CACxB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EAAC;EAAU;EAAW;EAAgB;CAC/C,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,aACE;CAEF,WACE;CAEF,SAAS,CAAC,GAAG;CACb,UAAU,CAAC,gBAAgB,eAAe;CAC1C;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"philid.js","names":[],"sources":["../../src/ph/philid.ts"],"sourcesContent":["/** Generate a random valid PhilID. */\nconst generate = (): string => randomDigits(12);\n\n/**\n * PhilID (Philippine Identification System Number,\n * PhilSys Card Number, PCN).\n *\n * 12-digit personal identifier issued under the\n * Philippine Identification System (PhilSys).\n * Formatted as XXXX-XXXXXXX-X. No check digit\n * algorithm is publicly documented.\n *\n * @see https://en.wikipedia.org/wiki/Philippine_national_identity_card\n * @see https://philsys.gov.ph/\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 compact = (value: string): string =>\n clean(value, \" -\");\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 12) {\n return err(\n \"INVALID_LENGTH\",\n \"PhilID must be exactly 12 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"PhilID must contain only digits\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n if (v.length === 12) {\n return `${v.slice(0, 4)}-${v.slice(4, 11)}-${v.slice(11)}`;\n }\n return v;\n};\n\n/** Philippine Identification System Number. */\nconst philid: CountryValidator<\"PH\"> = {\n name: \"Philippine Identification System Number\",\n localName: \"PhilSys Card Number\",\n abbreviation: \"PhilID\",\n aliases: [\"PhilID\", \"PhilSys\", \"Philippine ID\"] as const,\n candidatePattern: \"\\\\d{4}-\\\\d{4}-\\\\d{4}-\\\\d{4}\",\n scope: \"country\",\n country: \"PH\",\n entityType: \"person\",\n description:\n \"12-digit personal identifier issued under\" +\n \" the Philippine Identification System\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/\" +\n \"Philippine_national_identity_card\",\n lengths: [12] as const,\n examples: [\"123456789012\", \"000011112222\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default philid;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;AACA,MAAM,iBAAyB,aAAa,EAAE;AAyB9C,MAAM,WAAW,UACf,MAAM,OAAO,IAAI;AAEnB,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,KAAK;CACvB,IAAI,EAAE,WAAW,IACf,OAAO,IACL,kBACA,kCACF;CAEF,IAAI,CAAC,SAAS,CAAC,GACb,OAAO,IACL,kBACA,iCACF;CAEF,OAAO;EAAE,OAAO;EAAM,SAAS;CAAE;AACnC;AAEA,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,KAAK;CACvB,IAAI,EAAE,WAAW,IACf,OAAO,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE;CAEzD,OAAO;AACT;;AAGA,MAAM,SAAiC;CACrC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EAAC;EAAU;EAAW;CAAe;CAC9C,kBAAkB;CAClB,OAAO;CACP,SAAS;CACT,YAAY;CACZ,aACE;CAEF,WACE;CAEF,SAAS,CAAC,EAAE;CACZ,UAAU,CAAC,gBAAgB,cAAc;CACzC;CACA;CACA;CACA;AACF"}
package/dist/pk/cnic.d.ts CHANGED
@@ -1,13 +1,25 @@
1
- import { ValidateResult, Validator } from "../types.js";
1
+ import { CountryValidator, ValidateResult } from "../types.js";
2
2
 
3
3
  //#region src/pk/cnic.d.ts
4
4
  /** Generate a random valid Pakistan CNIC. */
5
5
  declare const generate: () => string;
6
+ /**
7
+ * CNIC (Computerized National Identity Card, Pakistan).
8
+ *
9
+ * 13 digits: PPPPP-DDDDDDD-G where P is a province/
10
+ * district code (5 digits), D is a sequential number
11
+ * (7 digits), and G is a gender digit (odd = male,
12
+ * even = female, including 0). No checksum; structural
13
+ * validation.
14
+ *
15
+ * @see https://www.geo.tv/latest/157233-secret-behind-every-digit-of-the-cnic-number
16
+ * @see https://en.wikipedia.org/wiki/CNIC_(Pakistan)
17
+ */
6
18
  declare const compact: (value: string) => string;
7
19
  declare const validate: (value: string) => ValidateResult;
8
20
  declare const format: (value: string) => string;
9
21
  /** Pakistan Computerized National Identity Card. */
10
- declare const cnic: Validator;
22
+ declare const cnic: CountryValidator<"PK">;
11
23
  //#endregion
12
24
  export { compact, cnic as default, format, generate, validate };
13
25
  //# sourceMappingURL=cnic.d.ts.map
package/dist/pk/cnic.js CHANGED
@@ -3,18 +3,6 @@ import { randomDigits, randomInt } from "../_util/generate.js";
3
3
  import { err } from "../_util/result.js";
4
4
  import { isdigits } from "../_util/strings.js";
5
5
  //#region src/pk/cnic.ts
6
- /**
7
- * CNIC (Computerized National Identity Card, Pakistan).
8
- *
9
- * 13 digits: PPPPP-DDDDDDD-G where P is a province/
10
- * district code (5 digits), D is a sequential number
11
- * (7 digits), and G is a gender digit (odd = male,
12
- * even = female, including 0). No checksum; structural
13
- * validation.
14
- *
15
- * @see https://www.geo.tv/latest/157233-secret-behind-every-digit-of-the-cnic-number
16
- * @see https://en.wikipedia.org/wiki/CNIC_(Pakistan)
17
- */
18
6
  /** Generate a random valid Pakistan CNIC. */
19
7
  const generate = () => {
20
8
  return `${String(randomInt(1, 7))}${randomDigits(4)}${randomDigits(7)}${String(randomInt(1, 9))}`;
@@ -52,6 +40,7 @@ const cnic = {
52
40
  abbreviation: "CNIC",
53
41
  aliases: ["CNIC", "شناختی کارڈ"],
54
42
  candidatePattern: "\\d{5}-?\\d{7}-?\\d",
43
+ scope: "country",
55
44
  country: "PK",
56
45
  entityType: "person",
57
46
  lengths: [13],
@@ -1 +1 @@
1
- {"version":3,"file":"cnic.js","names":[],"sources":["../../src/pk/cnic.ts"],"sourcesContent":["/** Generate a random valid Pakistan CNIC. */\nconst generate = (): string => {\n const province = String(randomInt(1, 7));\n const district = randomDigits(4);\n const serial = randomDigits(7);\n const gender = String(randomInt(1, 9));\n return `${province}${district}${serial}${gender}`;\n};\n\n/**\n * CNIC (Computerized National Identity Card, Pakistan).\n *\n * 13 digits: PPPPP-DDDDDDD-G where P is a province/\n * district code (5 digits), D is a sequential number\n * (7 digits), and G is a gender digit (odd = male,\n * even = female, including 0). No checksum; structural\n * validation.\n *\n * @see https://www.geo.tv/latest/157233-secret-behind-every-digit-of-the-cnic-number\n * @see https://en.wikipedia.org/wiki/CNIC_(Pakistan)\n */\n\nimport { clean } from \"#util/clean\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\n/** Valid province IDs (first digit). */\nconst VALID_PROVINCES = new Set([\n \"1\",\n \"2\",\n \"3\",\n \"4\",\n \"5\",\n \"6\",\n \"7\",\n]);\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 13) {\n return err(\"INVALID_LENGTH\", \"CNIC must be 13 digits\");\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"CNIC must contain only digits\",\n );\n }\n // First digit must be a valid province code\n // SAFETY: v.length === 13 above guarantees v[0] exists.\n // eslint-disable-next-line no-non-null-assertion\n if (!VALID_PROVINCES.has(v[0]!)) {\n return err(\n \"INVALID_COMPONENT\",\n \"CNIC province code is invalid\",\n );\n }\n // Last digit indicates gender: odd = male,\n // even = female. Digit 0 is rejected per\n // python-stdnum convention; NADRA does not\n // publish whether 0 is a valid gender code.\n // @see https://www.geo.tv/latest/157233\n if (v[12] === \"0\") {\n return err(\n \"INVALID_COMPONENT\",\n \"CNIC gender digit must not be 0\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return `${v.slice(0, 5)}-${v.slice(5, 12)}-${v.slice(12)}`;\n};\n\n/** Pakistan Computerized National Identity Card. */\nconst cnic: Validator = {\n name: \"Computerized National Identity Card\",\n localName: \"Computerized National Identity Card\",\n abbreviation: \"CNIC\",\n aliases: [\"CNIC\", \"شناختی کارڈ\"] as const,\n candidatePattern: \"\\\\d{5}-?\\\\d{7}-?\\\\d\",\n country: \"PK\",\n entityType: \"person\",\n lengths: [13],\n examples: [\"3520112345671\", \"4210112345672\"],\n description:\n \"13-digit identity card number issued by NADRA\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/CNIC_(Pakistan)\",\n compact,\n format,\n validate,\n generate,\n};\n\nexport default cnic;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;;;;;;;;;;;;;AACA,MAAM,iBAAyB;AAK7B,QAAO,GAJU,OAAO,UAAU,GAAG,EAAE,CAAC,GACvB,aAAa,EAAE,GACjB,aAAa,EAAE,GACf,OAAO,UAAU,GAAG,EAAE,CAAC;;AAwBxC,MAAM,WAAW,UACf,MAAM,OAAO,KAAK;;AAGpB,MAAM,kBAAkB,IAAI,IAAI;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;CACD,CAAC;AAEF,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IAAI,kBAAkB,yBAAyB;AAExD,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,gCACD;AAKH,KAAI,CAAC,gBAAgB,IAAI,EAAE,GAAI,CAC7B,QAAO,IACL,qBACA,gCACD;AAOH,KAAI,EAAE,QAAQ,IACZ,QAAO,IACL,qBACA,kCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,MAAM;AACxB,QAAO,GAAG,EAAE,MAAM,GAAG,EAAE,CAAC,GAAG,EAAE,MAAM,GAAG,GAAG,CAAC,GAAG,EAAE,MAAM,GAAG;;;AAI1D,MAAM,OAAkB;CACtB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,QAAQ,cAAc;CAChC,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,SAAS,CAAC,GAAG;CACb,UAAU,CAAC,iBAAiB,gBAAgB;CAC5C,aACE;CACF,WACE;CACF;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"cnic.js","names":[],"sources":["../../src/pk/cnic.ts"],"sourcesContent":["/** Generate a random valid Pakistan CNIC. */\nconst generate = (): string => {\n const province = String(randomInt(1, 7));\n const district = randomDigits(4);\n const serial = randomDigits(7);\n const gender = String(randomInt(1, 9));\n return `${province}${district}${serial}${gender}`;\n};\n\n/**\n * CNIC (Computerized National Identity Card, Pakistan).\n *\n * 13 digits: PPPPP-DDDDDDD-G where P is a province/\n * district code (5 digits), D is a sequential number\n * (7 digits), and G is a gender digit (odd = male,\n * even = female, including 0). No checksum; structural\n * validation.\n *\n * @see https://www.geo.tv/latest/157233-secret-behind-every-digit-of-the-cnic-number\n * @see https://en.wikipedia.org/wiki/CNIC_(Pakistan)\n */\n\nimport type {\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport { clean } from \"#util/clean\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nconst compact = (value: string): string =>\n clean(value, \" -\");\n\n/** Valid province IDs (first digit). */\nconst VALID_PROVINCES = new Set([\n \"1\",\n \"2\",\n \"3\",\n \"4\",\n \"5\",\n \"6\",\n \"7\",\n]);\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 13) {\n return err(\"INVALID_LENGTH\", \"CNIC must be 13 digits\");\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"CNIC must contain only digits\",\n );\n }\n // First digit must be a valid province code\n // SAFETY: v.length === 13 above guarantees v[0] exists.\n // eslint-disable-next-line no-non-null-assertion\n if (!VALID_PROVINCES.has(v[0]!)) {\n return err(\n \"INVALID_COMPONENT\",\n \"CNIC province code is invalid\",\n );\n }\n // Last digit indicates gender: odd = male,\n // even = female. Digit 0 is rejected per\n // python-stdnum convention; NADRA does not\n // publish whether 0 is a valid gender code.\n // @see https://www.geo.tv/latest/157233\n if (v[12] === \"0\") {\n return err(\n \"INVALID_COMPONENT\",\n \"CNIC gender digit must not be 0\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return `${v.slice(0, 5)}-${v.slice(5, 12)}-${v.slice(12)}`;\n};\n\n/** Pakistan Computerized National Identity Card. */\nconst cnic: CountryValidator<\"PK\"> = {\n name: \"Computerized National Identity Card\",\n localName: \"Computerized National Identity Card\",\n abbreviation: \"CNIC\",\n aliases: [\"CNIC\", \"شناختی کارڈ\"] as const,\n candidatePattern: \"\\\\d{5}-?\\\\d{7}-?\\\\d\",\n scope: \"country\",\n country: \"PK\",\n entityType: \"person\",\n lengths: [13],\n examples: [\"3520112345671\", \"4210112345672\"],\n description:\n \"13-digit identity card number issued by NADRA\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/CNIC_(Pakistan)\",\n compact,\n format,\n validate,\n generate,\n};\n\nexport default cnic;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;;AACA,MAAM,iBAAyB;CAK7B,OAAO,GAJU,OAAO,UAAU,GAAG,CAAC,CAIrB,IAHA,aAAa,CAGF,IAFb,aAAa,CAES,IADtB,OAAO,UAAU,GAAG,CAAC,CACU;AAChD;AAyBA,MAAM,WAAW,UACf,MAAM,OAAO,IAAI;;AAGnB,MAAM,kBAAkB,IAAI,IAAI;CAC9B;CACA;CACA;CACA;CACA;CACA;CACA;AACF,CAAC;AAED,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,KAAK;CACvB,IAAI,EAAE,WAAW,IACf,OAAO,IAAI,kBAAkB,wBAAwB;CAEvD,IAAI,CAAC,SAAS,CAAC,GACb,OAAO,IACL,kBACA,+BACF;CAKF,IAAI,CAAC,gBAAgB,IAAI,EAAE,EAAG,GAC5B,OAAO,IACL,qBACA,+BACF;CAOF,IAAI,EAAE,QAAQ,KACZ,OAAO,IACL,qBACA,iCACF;CAEF,OAAO;EAAE,OAAO;EAAM,SAAS;CAAE;AACnC;AAEA,MAAM,UAAU,UAA0B;CACxC,MAAM,IAAI,QAAQ,KAAK;CACvB,OAAO,GAAG,EAAE,MAAM,GAAG,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,EAAE,EAAE,GAAG,EAAE,MAAM,EAAE;AACzD;;AAGA,MAAM,OAA+B;CACnC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,QAAQ,aAAa;CAC/B,kBAAkB;CAClB,OAAO;CACP,SAAS;CACT,YAAY;CACZ,SAAS,CAAC,EAAE;CACZ,UAAU,CAAC,iBAAiB,eAAe;CAC3C,aACE;CACF,WACE;CACF;CACA;CACA;CACA;AACF"}
package/dist/pl/nip.d.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { ValidateResult, Validator } from "../types.js";
1
+ import { CountryValidator, ValidateResult } from "../types.js";
2
2
 
3
3
  //#region src/pl/nip.d.ts
4
4
  declare const compact: (value: string) => string;
@@ -11,7 +11,7 @@ declare const format: (value: string) => string;
11
11
  */
12
12
  declare const generate: () => string;
13
13
  /** Polish VAT Number. */
14
- declare const nip: Validator;
14
+ declare const nip: CountryValidator<"PL">;
15
15
  //#endregion
16
16
  export { compact, nip as default, format, generate, validate };
17
17
  //# sourceMappingURL=nip.d.ts.map
package/dist/pl/nip.js CHANGED
@@ -1,18 +1,9 @@
1
+ import { weightedSum } from "../_checksums/weighted-sum.js";
1
2
  import { clean } from "../_util/clean.js";
2
3
  import { randomDigits } from "../_util/generate.js";
3
4
  import { err } from "../_util/result.js";
4
5
  import { isdigits } from "../_util/strings.js";
5
- import { weightedSum } from "../_checksums/weighted-sum.js";
6
6
  //#region src/pl/nip.ts
7
- /**
8
- * NIP (Numer Identyfikacji Podatkowej).
9
- *
10
- * Polish VAT number. 10 digits with a weighted
11
- * checksum. The last digit is the check digit.
12
- *
13
- * @see https://www.biznes.gov.pl/en/portal/004124
14
- * @see https://www.oecd.org/content/dam/oecd/en/topics/policy-issue-focus/aeoi/poland-tin.pdf
15
- */
16
7
  const WEIGHTS = [
17
8
  6,
18
9
  5,
@@ -62,6 +53,7 @@ const nip = {
62
53
  abbreviation: "NIP",
63
54
  aliases: ["NIP", "numer identyfikacji podatkowej"],
64
55
  candidatePattern: "\\d{3}-?\\d{3}-?\\d{2}-?\\d{2}",
56
+ scope: "country",
65
57
  country: "PL",
66
58
  entityType: "company",
67
59
  sourceUrl: "https://www.biznes.gov.pl/en/portal/004124",
@@ -1 +1 @@
1
- {"version":3,"file":"nip.js","names":[],"sources":["../../src/pl/nip.ts"],"sourcesContent":["/**\n * NIP (Numer Identyfikacji Podatkowej).\n *\n * Polish VAT number. 10 digits with a weighted\n * checksum. The last digit is the check digit.\n *\n * @see https://www.biznes.gov.pl/en/portal/004124\n * @see https://www.oecd.org/content/dam/oecd/en/topics/policy-issue-focus/aeoi/poland-tin.pdf\n */\n\nimport { weightedSum } from \"#checksums/weighted-sum\";\nimport { clean } from \"#util/clean\";\nimport { randomDigits } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nimport type { ValidateResult, Validator } from \"../types\";\n\nconst WEIGHTS = [6, 5, 7, 2, 3, 4, 5, 6, 7] as const;\n\nconst compact = (value: string): string => {\n const v = clean(value, \" -\");\n if (v.startsWith(\"PL\") || v.startsWith(\"pl\")) {\n return v.slice(2);\n }\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"NIP must be exactly 10 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"NIP must contain only digits\",\n );\n }\n const sum = weightedSum(v.slice(0, 9), WEIGHTS, 11);\n // If remainder is 10, no valid check digit exists\n if (sum >= 10 || sum !== Number(v[9])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"NIP check digit does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string =>\n `PL${compact(value)}`;\n\nconst MAX_GENERATE_ATTEMPTS = 100;\n\n/**\n * Generate a random valid NIP. Retries if the\n * weighted sum mod 11 yields 10 (no valid check\n * digit for that payload).\n */\nconst generate = (): string => {\n for (let i = 0; i < MAX_GENERATE_ATTEMPTS; i++) {\n const payload = randomDigits(9);\n const sum = weightedSum(payload, WEIGHTS, 11);\n if (sum < 10) {\n return `${payload}${String(sum)}`;\n }\n }\n throw new Error(\"Failed to generate valid NIP\");\n};\n\n/** Polish VAT Number. */\nconst nip: Validator = {\n name: \"Polish VAT Number\",\n localName: \"Numer Identyfikacji Podatkowej\",\n abbreviation: \"NIP\",\n aliases: [\n \"NIP\",\n \"numer identyfikacji podatkowej\",\n ] as const,\n candidatePattern: \"\\\\d{3}-?\\\\d{3}-?\\\\d{2}-?\\\\d{2}\",\n country: \"PL\",\n entityType: \"company\",\n sourceUrl: \"https://www.biznes.gov.pl/en/portal/004124\",\n examples: [\"2234567895\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default nip;\nexport { compact, format, generate, validate };\n"],"mappings":";;;;;;;;;;;;;;;AAkBA,MAAM,UAAU;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAE;AAE3C,MAAM,WAAW,UAA0B;CACzC,MAAM,IAAI,MAAM,OAAO,KAAK;AAC5B,KAAI,EAAE,WAAW,KAAK,IAAI,EAAE,WAAW,KAAK,CAC1C,QAAO,EAAE,MAAM,EAAE;AAEnB,QAAO;;AAGT,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,MAAM;AACxB,KAAI,EAAE,WAAW,GACf,QAAO,IACL,kBACA,gCACD;AAEH,KAAI,CAAC,SAAS,EAAE,CACd,QAAO,IACL,kBACA,+BACD;CAEH,MAAM,MAAM,YAAY,EAAE,MAAM,GAAG,EAAE,EAAE,SAAS,GAAG;AAEnD,KAAI,OAAO,MAAM,QAAQ,OAAO,EAAE,GAAG,CACnC,QAAO,IACL,oBACA,iCACD;AAEH,QAAO;EAAE,OAAO;EAAM,SAAS;EAAG;;AAGpC,MAAM,UAAU,UACd,KAAK,QAAQ,MAAM;AAErB,MAAM,wBAAwB;;;;;;AAO9B,MAAM,iBAAyB;AAC7B,MAAK,IAAI,IAAI,GAAG,IAAI,uBAAuB,KAAK;EAC9C,MAAM,UAAU,aAAa,EAAE;EAC/B,MAAM,MAAM,YAAY,SAAS,SAAS,GAAG;AAC7C,MAAI,MAAM,GACR,QAAO,GAAG,UAAU,OAAO,IAAI;;AAGnC,OAAM,IAAI,MAAM,+BAA+B;;;AAIjD,MAAM,MAAiB;CACrB,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CACP,OACA,iCACD;CACD,kBAAkB;CAClB,SAAS;CACT,YAAY;CACZ,WAAW;CACX,UAAU,CAAC,aAAa;CACxB;CACA;CACA;CACA;CACD"}
1
+ {"version":3,"file":"nip.js","names":[],"sources":["../../src/pl/nip.ts"],"sourcesContent":["/**\n * NIP (Numer Identyfikacji Podatkowej).\n *\n * Polish VAT number. 10 digits with a weighted\n * checksum. The last digit is the check digit.\n *\n * @see https://www.biznes.gov.pl/en/portal/004124\n * @see https://www.oecd.org/content/dam/oecd/en/topics/policy-issue-focus/aeoi/poland-tin.pdf\n */\n\nimport type {\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport { weightedSum } from \"#checksums/weighted-sum\";\nimport { clean } from \"#util/clean\";\nimport { randomDigits } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nconst WEIGHTS = [6, 5, 7, 2, 3, 4, 5, 6, 7] as const;\n\nconst compact = (value: string): string => {\n const v = clean(value, \" -\");\n if (v.startsWith(\"PL\") || v.startsWith(\"pl\")) {\n return v.slice(2);\n }\n return v;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"NIP must be exactly 10 digits\",\n );\n }\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"NIP must contain only digits\",\n );\n }\n const sum = weightedSum(v.slice(0, 9), WEIGHTS, 11);\n // If remainder is 10, no valid check digit exists\n if (sum >= 10 || sum !== Number(v[9])) {\n return err(\n \"INVALID_CHECKSUM\",\n \"NIP check digit does not match\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string =>\n `PL${compact(value)}`;\n\nconst MAX_GENERATE_ATTEMPTS = 100;\n\n/**\n * Generate a random valid NIP. Retries if the\n * weighted sum mod 11 yields 10 (no valid check\n * digit for that payload).\n */\nconst generate = (): string => {\n for (let i = 0; i < MAX_GENERATE_ATTEMPTS; i++) {\n const payload = randomDigits(9);\n const sum = weightedSum(payload, WEIGHTS, 11);\n if (sum < 10) {\n return `${payload}${String(sum)}`;\n }\n }\n throw new Error(\"Failed to generate valid NIP\");\n};\n\n/** Polish VAT Number. */\nconst nip: CountryValidator<\"PL\"> = {\n name: \"Polish VAT Number\",\n localName: \"Numer Identyfikacji Podatkowej\",\n abbreviation: \"NIP\",\n aliases: [\n \"NIP\",\n \"numer identyfikacji podatkowej\",\n ] as const,\n candidatePattern: \"\\\\d{3}-?\\\\d{3}-?\\\\d{2}-?\\\\d{2}\",\n scope: \"country\",\n country: \"PL\",\n entityType: \"company\",\n sourceUrl: \"https://www.biznes.gov.pl/en/portal/004124\",\n examples: [\"2234567895\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default nip;\nexport { compact, format, generate, validate };\n"],"mappings":";;;;;;AAqBA,MAAM,UAAU;CAAC;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;CAAG;AAAC;AAE1C,MAAM,WAAW,UAA0B;CACzC,MAAM,IAAI,MAAM,OAAO,IAAI;CAC3B,IAAI,EAAE,WAAW,IAAI,KAAK,EAAE,WAAW,IAAI,GACzC,OAAO,EAAE,MAAM,CAAC;CAElB,OAAO;AACT;AAEA,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,KAAK;CACvB,IAAI,EAAE,WAAW,IACf,OAAO,IACL,kBACA,+BACF;CAEF,IAAI,CAAC,SAAS,CAAC,GACb,OAAO,IACL,kBACA,8BACF;CAEF,MAAM,MAAM,YAAY,EAAE,MAAM,GAAG,CAAC,GAAG,SAAS,EAAE;CAElD,IAAI,OAAO,MAAM,QAAQ,OAAO,EAAE,EAAE,GAClC,OAAO,IACL,oBACA,gCACF;CAEF,OAAO;EAAE,OAAO;EAAM,SAAS;CAAE;AACnC;AAEA,MAAM,UAAU,UACd,KAAK,QAAQ,KAAK;AAEpB,MAAM,wBAAwB;;;;;;AAO9B,MAAM,iBAAyB;CAC7B,KAAK,IAAI,IAAI,GAAG,IAAI,uBAAuB,KAAK;EAC9C,MAAM,UAAU,aAAa,CAAC;EAC9B,MAAM,MAAM,YAAY,SAAS,SAAS,EAAE;EAC5C,IAAI,MAAM,IACR,OAAO,GAAG,UAAU,OAAO,GAAG;CAElC;CACA,MAAM,IAAI,MAAM,8BAA8B;AAChD;;AAGA,MAAM,MAA8B;CAClC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CACP,OACA,gCACF;CACA,kBAAkB;CAClB,OAAO;CACP,SAAS;CACT,YAAY;CACZ,WAAW;CACX,UAAU,CAAC,YAAY;CACvB;CACA;CACA;CACA;AACF"}
@@ -1,4 +1,4 @@
1
- import { ParsedPersonId, ValidateResult, Validator } from "../types.js";
1
+ import { CountryValidator, ParsedPersonId, ValidateResult } from "../types.js";
2
2
 
3
3
  //#region src/pl/pesel.d.ts
4
4
  declare const compact: (value: string) => string;
@@ -12,7 +12,7 @@ declare const parse: (value: string) => ParsedPersonId | null;
12
12
  /** Generate a random valid Polish PESEL. */
13
13
  declare const generate: () => string;
14
14
  /** Polish National Identification Number. */
15
- declare const pesel: Validator<ParsedPersonId>;
15
+ declare const pesel: CountryValidator<"PL", ParsedPersonId>;
16
16
  //#endregion
17
17
  export { compact, pesel as default, format, generate, parse, validate };
18
18
  //# sourceMappingURL=pesel.d.ts.map
package/dist/pl/pesel.js CHANGED
@@ -1,21 +1,10 @@
1
+ import { weightedSum } from "../_checksums/weighted-sum.js";
1
2
  import { clean } from "../_util/clean.js";
3
+ import { isValidDate } from "../_util/date.js";
2
4
  import { randomDigits, randomInt } from "../_util/generate.js";
3
5
  import { err } from "../_util/result.js";
4
6
  import { isdigits } from "../_util/strings.js";
5
- import { weightedSum } from "../_checksums/weighted-sum.js";
6
- import { isValidDate } from "../_util/date.js";
7
7
  //#region src/pl/pesel.ts
8
- /**
9
- * PESEL (Powszechny Elektroniczny System
10
- * Ewidencji Ludności).
11
- *
12
- * Polish national identification number.
13
- * 11 digits encoding date of birth, serial
14
- * number, gender, and a check digit.
15
- *
16
- * @see https://www.oecd.org/content/dam/oecd/en/topics/policy-issue-focus/aeoi/poland-tin.pdf
17
- * @see https://www.gov.pl/web/cyfryzacja/numer-pesel
18
- */
19
8
  const WEIGHTS = [
20
9
  1,
21
10
  3,
@@ -94,6 +83,7 @@ const pesel = {
94
83
  abbreviation: "PESEL",
95
84
  aliases: ["PESEL"],
96
85
  candidatePattern: "\\d{11}",
86
+ scope: "country",
97
87
  country: "PL",
98
88
  entityType: "person",
99
89
  sourceUrl: "https://www.gov.pl/web/cyfryzacja/numer-pesel",