@stll/stdnum 2.0.1 → 2.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +11 -9
- package/dist/_checksums/keccak.d.ts +5 -0
- package/dist/_checksums/keccak.js +118 -0
- package/dist/_checksums/keccak.js.map +1 -0
- package/dist/_checksums/mod112.js.map +1 -1
- package/dist/_checksums/sha256.d.ts +5 -0
- package/dist/_checksums/sha256.js +164 -0
- package/dist/_checksums/sha256.js.map +1 -0
- package/dist/_checksums/verhoeff.js.map +1 -1
- package/dist/_util/arabic.js.map +1 -1
- package/dist/_util/clean.js.map +1 -1
- package/dist/_util/date.js.map +1 -1
- package/dist/ad/nrt.js.map +1 -1
- package/dist/ae/eid.js.map +1 -1
- package/dist/ai/tin.js.map +1 -1
- package/dist/al/nipt.js.map +1 -1
- package/dist/am/tin.js.map +1 -1
- package/dist/ar/cbu.js.map +1 -1
- package/dist/ar/cuit.js.map +1 -1
- package/dist/ar/dni.js.map +1 -1
- package/dist/at/businessid.js.map +1 -1
- package/dist/at/vnr.js.map +1 -1
- package/dist/au/abn.js.map +1 -1
- package/dist/au/tfn.js.map +1 -1
- package/dist/az/voen.js.map +1 -1
- package/dist/ba/jmbg.js.map +1 -1
- package/dist/be/bis.js.map +1 -1
- package/dist/be/nn.js.map +1 -1
- package/dist/be/vat.js.map +1 -1
- package/dist/bg/egn.js.map +1 -1
- package/dist/bg/vat.js.map +1 -1
- package/dist/bh/cpr.js.map +1 -1
- package/dist/bic.js.map +1 -1
- package/dist/br/cnpj.js.map +1 -1
- package/dist/br/cpf.js.map +1 -1
- package/dist/by/unp.js.map +1 -1
- package/dist/bz/tin.js.map +1 -1
- package/dist/ch/uid.js.map +1 -1
- package/dist/ch/vat.js.map +1 -1
- package/dist/cl/rut.js.map +1 -1
- package/dist/cn/ric.js.map +1 -1
- package/dist/cn/uscc.js.map +1 -1
- package/dist/cr/cpf.js.map +1 -1
- package/dist/crypto/btcbase58.d.ts +11 -0
- package/dist/crypto/btcbase58.js +76 -0
- package/dist/crypto/btcbase58.js.map +1 -0
- package/dist/crypto/btcbech32.d.ts +19 -0
- package/dist/crypto/btcbech32.js +145 -0
- package/dist/crypto/btcbech32.js.map +1 -0
- package/dist/crypto/eth.d.ts +11 -0
- package/dist/crypto/eth.js +63 -0
- package/dist/crypto/eth.js.map +1 -0
- package/dist/crypto/mod.d.ts +12 -0
- package/dist/crypto/mod.js +16 -0
- package/dist/crypto/mod.js.map +1 -0
- package/dist/crypto/wallet.d.ts +10 -0
- package/dist/crypto/wallet.js +55 -0
- package/dist/crypto/wallet.js.map +1 -0
- package/dist/cu/ni.js.map +1 -1
- package/dist/cz/rc.js.map +1 -1
- package/dist/de/handelsreg.js.map +1 -1
- package/dist/de/idnr.js.map +1 -1
- package/dist/de/stnr.js.map +1 -1
- package/dist/de/svnr.js.map +1 -1
- package/dist/dk/cpr.js.map +1 -1
- package/dist/dk/cvr.js.map +1 -1
- package/dist/dk/vat.js.map +1 -1
- package/dist/do/rnc.js.map +1 -1
- package/dist/ec/ruc.js.map +1 -1
- package/dist/ee/ik.js.map +1 -1
- package/dist/ee/vat.js.map +1 -1
- package/dist/eg/tn.js.map +1 -1
- package/dist/es/dni.js.map +1 -1
- package/dist/es/nie.js.map +1 -1
- package/dist/es/nss.js.map +1 -1
- package/dist/eu/vat.js.map +1 -1
- package/dist/fi/hetu.js.map +1 -1
- package/dist/fi/vat.js.map +1 -1
- package/dist/fi/ytunnus.js.map +1 -1
- package/dist/fr/nif.js.map +1 -1
- package/dist/fr/nir.js.map +1 -1
- package/dist/fr/tva.js.map +1 -1
- package/dist/gb/mod.d.ts +3 -2
- package/dist/gb/mod.js +3 -1
- package/dist/gb/mod.js.map +1 -1
- package/dist/gb/nhs.d.ts +11 -0
- package/dist/gb/nhs.js +51 -0
- package/dist/gb/nhs.js.map +1 -0
- package/dist/gb/nino.js.map +1 -1
- package/dist/gb/sedol.js.map +1 -1
- package/dist/gb/utr.js.map +1 -1
- package/dist/gb/vat.js.map +1 -1
- package/dist/ge/pin.js.map +1 -1
- package/dist/gh/tin.js.map +1 -1
- package/dist/gr/amka.js.map +1 -1
- package/dist/gt/nit.js.map +1 -1
- package/dist/hk/hkid.js.map +1 -1
- package/dist/hu/vat.js.map +1 -1
- package/dist/iban.js.map +1 -1
- package/dist/id/npwp.js.map +1 -1
- package/dist/ie/pps.js.map +1 -1
- package/dist/ie/vat.js.map +1 -1
- package/dist/il/idnr.js.map +1 -1
- package/dist/in/aadhaar.js.map +1 -1
- package/dist/in/gstin.js.map +1 -1
- package/dist/in/pan.js.map +1 -1
- package/dist/index.d.ts +76 -75
- package/dist/index.js +2 -1
- package/dist/ir/nid.js.map +1 -1
- package/dist/is/kennitala.js.map +1 -1
- package/dist/isin.js.map +1 -1
- package/dist/it/codicefiscale.js.map +1 -1
- package/dist/it/iva.js.map +1 -1
- package/dist/kr/rrn.js.map +1 -1
- package/dist/kw/civil.js.map +1 -1
- package/dist/kz/iin.js.map +1 -1
- package/dist/lei.js.map +1 -1
- package/dist/li/peid.js.map +1 -1
- package/dist/lk/nic.js.map +1 -1
- package/dist/lt/asmens.js.map +1 -1
- package/dist/lt/vat.js.map +1 -1
- package/dist/lu/vat.js.map +1 -1
- package/dist/lv/vat.js.map +1 -1
- package/dist/ma/ice.js.map +1 -1
- package/dist/mc/tva.js.map +1 -1
- package/dist/mt/vat.js.map +1 -1
- package/dist/mu/brn.js.map +1 -1
- package/dist/mx/clabe.js.map +1 -1
- package/dist/mx/curp.js.map +1 -1
- package/dist/mx/rfc.js.map +1 -1
- package/dist/my/nric.js.map +1 -1
- package/dist/ni/ruc.js.map +1 -1
- package/dist/nl/bsn.js.map +1 -1
- package/dist/nl/vat.js.map +1 -1
- package/dist/no/fodselsnummer.js.map +1 -1
- package/dist/no/mva.js.map +1 -1
- package/dist/no/orgnr.js.map +1 -1
- package/dist/nz/ird.js.map +1 -1
- package/dist/pa/ruc.js.map +1 -1
- package/dist/patterns.js.map +1 -1
- package/dist/pe/ruc.js.map +1 -1
- package/dist/pl/pesel.js.map +1 -1
- package/dist/pl/regon.js.map +1 -1
- package/dist/pt/cc.js.map +1 -1
- package/dist/ro/cnp.js.map +1 -1
- package/dist/ro/vat.js.map +1 -1
- package/dist/rs/pib.js.map +1 -1
- package/dist/ru/inn.js.map +1 -1
- package/dist/se/personnummer.js.map +1 -1
- package/dist/sg/uen.js.map +1 -1
- package/dist/si/emso.js.map +1 -1
- package/dist/si/vat.js.map +1 -1
- package/dist/sk/dic.js.map +1 -1
- package/dist/tw/ubn.js.map +1 -1
- package/dist/ua/edrpou.js.map +1 -1
- package/dist/us/itin.js.map +1 -1
- package/dist/us/rtn.js.map +1 -1
- package/dist/us/ssn.js.map +1 -1
- package/dist/uy/rut.js.map +1 -1
- package/dist/ve/rif.js.map +1 -1
- package/dist/vn/mst.js.map +1 -1
- package/dist/za/idnr.js.map +1 -1
- package/package.json +31 -6
- package/src/_checksums/keccak.ts +145 -0
- package/src/_checksums/sha256.ts +125 -0
- package/src/crypto/btcbase58.ts +144 -0
- package/src/crypto/btcbech32.ts +210 -0
- package/src/crypto/eth.ts +114 -0
- package/src/crypto/mod.ts +4 -0
- package/src/crypto/wallet.ts +67 -0
- package/src/gb/mod.ts +1 -0
- package/src/gb/nhs.ts +92 -0
- package/src/index.ts +1 -0
package/dist/cn/ric.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ric.js","names":[],"sources":["../../src/cn/ric.ts"],"sourcesContent":["/**\n * RIC (Resident Identity Card number, 居民身份证号码).\n *\n * Chinese national identifier. 18 characters:\n * 6 (area code) + 8 (YYYYMMDD birth) + 3 (sequence,\n * odd = male) + 1 (check, 0-9 or X).\n *\n * Also accepts the legacy 15-digit format (no check\n * digit, 2-digit year).\n *\n * @see https://en.wikipedia.org/wiki/Resident_Identity_Card\n */\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport {\n mod112checkChar,\n mod112validate,\n} from \"#checksums/mod112\";\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nconst compact = (value: string): string =>\n clean(value, \" -\").toUpperCase();\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 15 && v.length !== 18) {\n return err(\n \"INVALID_LENGTH\",\n \"RIC must be 15 or 18 characters\",\n );\n }\n\n if (v.length === 15) {\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"15-digit RIC must contain only digits\",\n );\n }\n // Validate date: digits 6-11 are YYMMDD\n const year = 1900 + Number(v.slice(6, 8));\n const month = Number(v.slice(8, 10));\n const day = Number(v.slice(10, 12));\n if (!isValidDate(year, month, day)) {\n return err(\n \"INVALID_COMPONENT\",\n \"RIC contains an invalid birth date\",\n );\n }\n return { valid: true, compact: v };\n }\n\n // 18-character format\n if (!isdigits(v.slice(0, 17))) {\n return err(\n \"INVALID_FORMAT\",\n \"RIC must contain only digits (plus check char)\",\n );\n }\n // SAFETY: 18-character branch guarantees v[17] exists.\n // eslint-disable-next-line no-non-null-assertion\n const lastChar = v[17]!;\n if (!isdigits(lastChar) && lastChar !== \"X\") {\n return err(\n \"INVALID_FORMAT\",\n \"RIC check character must be 0-9 or X\",\n );\n }\n\n // Validate date: digits 6-13 are YYYYMMDD\n const year = Number(v.slice(6, 10));\n const month = Number(v.slice(10, 12));\n const day = Number(v.slice(12, 14));\n if (!isValidDate(year, month, day)) {\n return err(\n \"INVALID_COMPONENT\",\n \"RIC contains an invalid birth date\",\n );\n }\n\n if (!mod112validate(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"RIC check character does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Extract birth date and gender from an RIC number.\n * Returns null if the value is not valid.\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n\n let year: number;\n let month: number;\n let day: number;\n let seq: number;\n\n if (v.length === 15) {\n year = 1900 + Number(v.slice(6, 8));\n month = Number(v.slice(8, 10));\n day = Number(v.slice(10, 12));\n seq = Number(v.slice(12, 15));\n } else {\n year = Number(v.slice(6, 10));\n month = Number(v.slice(10, 12));\n day = Number(v.slice(12, 14));\n seq = Number(v.slice(14, 17));\n }\n\n const gender = seq % 2 === 1 ? \"male\" : \"female\";\n\n return {\n birthDate: new Date(year, month - 1, day),\n gender,\n };\n};\n\n/** Generate a random valid 18-digit RIC. */\nconst generate = (): string => {\n // Use a simple area code prefix\n const area = String(randomInt(110000, 659999));\n const year = randomInt(1950, 2005);\n const month = randomInt(1, 12);\n const day = randomInt(1, 28);\n const seqNum = randomInt(1, 999);\n const seq = String(seqNum).padStart(3, \"0\");\n\n const yStr = String(year);\n const mStr = String(month).padStart(2, \"0\");\n const dStr = String(day).padStart(2, \"0\");\n\n const payload = `${area}${yStr}${mStr}${dStr}${seq}`;\n const check = mod112checkChar(payload);\n return `${payload}${check}`;\n};\n\n/** Chinese Resident Identity Card number. */\nconst ric: CountryValidator<\"CN\", ParsedPersonId> = {\n name: \"Chinese Resident Identity Card\",\n localName: \"居民身份证号码\",\n abbreviation: \"RIC\",\n aliases: [\"身份证\", \"居民身份证号码\", \"RIC\"] as const,\n candidatePattern: \"\\\\d{17}[\\\\dX]\",\n scope: \"country\",\n country: \"CN\",\n entityType: \"person\",\n description:\n \"18-character national ID encoding area, birth date, gender, and check digit\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/Resident_Identity_Card\",\n lengths: [15, 18] as const,\n examples: [\n \"11010519491231002X\",\n \"440524188001010014\",\n ] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default ric;\nexport { compact, format, generate, parse, validate };\n"],"mappings":";;;;;;;AA6BA,MAAM,WAAW,UACf,MAAM,OAAO,IAAI,
|
|
1
|
+
{"version":3,"file":"ric.js","names":[],"sources":["../../src/cn/ric.ts"],"sourcesContent":["/**\n * RIC (Resident Identity Card number, 居民身份证号码).\n *\n * Chinese national identifier. 18 characters:\n * 6 (area code) + 8 (YYYYMMDD birth) + 3 (sequence,\n * odd = male) + 1 (check, 0-9 or X).\n *\n * Also accepts the legacy 15-digit format (no check\n * digit, 2-digit year).\n *\n * @see https://en.wikipedia.org/wiki/Resident_Identity_Card\n */\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport {\n mod112checkChar,\n mod112validate,\n} from \"#checksums/mod112\";\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nconst compact = (value: string): string =>\n clean(value, \" -\").toUpperCase();\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 15 && v.length !== 18) {\n return err(\n \"INVALID_LENGTH\",\n \"RIC must be 15 or 18 characters\",\n );\n }\n\n if (v.length === 15) {\n if (!isdigits(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"15-digit RIC must contain only digits\",\n );\n }\n // Validate date: digits 6-11 are YYMMDD\n const year = 1900 + Number(v.slice(6, 8));\n const month = Number(v.slice(8, 10));\n const day = Number(v.slice(10, 12));\n if (!isValidDate(year, month, day)) {\n return err(\n \"INVALID_COMPONENT\",\n \"RIC contains an invalid birth date\",\n );\n }\n return { valid: true, compact: v };\n }\n\n // 18-character format\n if (!isdigits(v.slice(0, 17))) {\n return err(\n \"INVALID_FORMAT\",\n \"RIC must contain only digits (plus check char)\",\n );\n }\n // SAFETY: 18-character branch guarantees v[17] exists.\n // eslint-disable-next-line no-non-null-assertion\n const lastChar = v[17]!;\n if (!isdigits(lastChar) && lastChar !== \"X\") {\n return err(\n \"INVALID_FORMAT\",\n \"RIC check character must be 0-9 or X\",\n );\n }\n\n // Validate date: digits 6-13 are YYYYMMDD\n const year = Number(v.slice(6, 10));\n const month = Number(v.slice(10, 12));\n const day = Number(v.slice(12, 14));\n if (!isValidDate(year, month, day)) {\n return err(\n \"INVALID_COMPONENT\",\n \"RIC contains an invalid birth date\",\n );\n }\n\n if (!mod112validate(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"RIC check character does not match\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Extract birth date and gender from an RIC number.\n * Returns null if the value is not valid.\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n\n let year: number;\n let month: number;\n let day: number;\n let seq: number;\n\n if (v.length === 15) {\n year = 1900 + Number(v.slice(6, 8));\n month = Number(v.slice(8, 10));\n day = Number(v.slice(10, 12));\n seq = Number(v.slice(12, 15));\n } else {\n year = Number(v.slice(6, 10));\n month = Number(v.slice(10, 12));\n day = Number(v.slice(12, 14));\n seq = Number(v.slice(14, 17));\n }\n\n const gender = seq % 2 === 1 ? \"male\" : \"female\";\n\n return {\n birthDate: new Date(year, month - 1, day),\n gender,\n };\n};\n\n/** Generate a random valid 18-digit RIC. */\nconst generate = (): string => {\n // Use a simple area code prefix\n const area = String(randomInt(110000, 659999));\n const year = randomInt(1950, 2005);\n const month = randomInt(1, 12);\n const day = randomInt(1, 28);\n const seqNum = randomInt(1, 999);\n const seq = String(seqNum).padStart(3, \"0\");\n\n const yStr = String(year);\n const mStr = String(month).padStart(2, \"0\");\n const dStr = String(day).padStart(2, \"0\");\n\n const payload = `${area}${yStr}${mStr}${dStr}${seq}`;\n const check = mod112checkChar(payload);\n return `${payload}${check}`;\n};\n\n/** Chinese Resident Identity Card number. */\nconst ric: CountryValidator<\"CN\", ParsedPersonId> = {\n name: \"Chinese Resident Identity Card\",\n localName: \"居民身份证号码\",\n abbreviation: \"RIC\",\n aliases: [\"身份证\", \"居民身份证号码\", \"RIC\"] as const,\n candidatePattern: \"\\\\d{17}[\\\\dX]\",\n scope: \"country\",\n country: \"CN\",\n entityType: \"person\",\n description:\n \"18-character national ID encoding area, birth date, gender, and check digit\",\n sourceUrl:\n \"https://en.wikipedia.org/wiki/Resident_Identity_Card\",\n lengths: [15, 18] as const,\n examples: [\n \"11010519491231002X\",\n \"440524188001010014\",\n ] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n};\n\nexport default ric;\nexport { compact, format, generate, parse, validate };\n"],"mappings":"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"}
|
package/dist/cn/uscc.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"uscc.js","names":[],"sources":["../../src/cn/uscc.ts"],"sourcesContent":["/**\n * USCC (Unified Social Credit Code, 统一社会信用代码).\n *\n * 18 alphanumeric characters (excluding I, O, Z, S, V):\n * 1 (registering authority) + 1 (entity type) +\n * 6 (region code, digits only) + 9 (organisation code) +\n * 1 (mod 31 check character).\n *\n * @see https://zh.wikipedia.org/wiki/统一社会信用代码\n */\n\nimport type {\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport { clean } from \"#util/clean\";\nimport {\n randomChar,\n randomDigits,\n randomInt,\n} from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nconst ALPHABET = \"0123456789ABCDEFGHJKLMNPQRTUWXY\";\n\nconst WEIGHTS = [\n 1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10,\n 30, 28,\n] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -\").toUpperCase();\n\nconst calcCheckChar = (value: string): string => {\n let total = 0;\n for (const [i, weight] of WEIGHTS.entries()) {\n const ch = value[i];\n if (ch === undefined) continue;\n total += ALPHABET.indexOf(ch) * weight;\n }\n // SAFETY: (31 - total % 31) % 31 is in 0..30 and\n // ALPHABET has 31 characters.\n // eslint-disable-next-line no-non-null-assertion\n return ALPHABET[(31 - (total % 31)) % 31]!;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 18) {\n return err(\n \"INVALID_LENGTH\",\n \"USCC must be 18 characters\",\n );\n }\n\n for (const ch of v) {\n if (!ALPHABET.includes(ch)) {\n return err(\n \"INVALID_FORMAT\",\n \"USCC contains invalid character\",\n );\n }\n }\n\n if (!isdigits(v.slice(2, 8))) {\n return err(\n \"INVALID_FORMAT\",\n \"USCC region code (positions 3-8) must be digits\",\n );\n }\n\n if (v[17] !== calcCheckChar(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"USCC check character 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 USCC. */\nconst generate = (): string => {\n // SAFETY: indices 1..9 are within ALPHABET (31 chars).\n // eslint-disable-next-line no-non-null-assertion\n const reg = ALPHABET[randomInt(1, 9)]!;\n // eslint-disable-next-line no-non-null-assertion\n const etype = ALPHABET[randomInt(1, 9)]!;\n const region = randomDigits(6);\n let org = \"\";\n for (let i = 0; i < 9; i++) {\n org += randomChar(ALPHABET);\n }\n const payload = reg + etype + region + org;\n return payload + calcCheckChar(payload);\n};\n\n/** Chinese Unified Social Credit Code. */\nconst uscc: CountryValidator<\"CN\"> = {\n name: \"Unified Social Credit Code\",\n localName: \"统一社会信用代码\",\n abbreviation: \"USCC\",\n aliases: [\"统一社会信用代码\", \"USCC\"] as const,\n candidatePattern:\n \"[0-9A-HJ-NP-RTUW-Y]{2}\\\\d{6}[0-9A-HJ-NP-RTUW-Y]{10}\",\n scope: \"country\",\n country: \"CN\",\n entityType: \"company\",\n description:\n \"18-character tax/registration code for Chinese entities\",\n sourceUrl:\n \"https://zh.wikipedia.org/wiki/统一社会信用代码\",\n lengths: [18] as const,\n examples: [\"91110000600037341L\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default uscc;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;AAyBA,MAAM,WAAW;AAEjB,MAAM,UAAU;CACd;CAAG;CAAG;CAAG;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAG;CAAI;CACpD;CAAI;AACN;AAEA,MAAM,WAAW,UACf,MAAM,OAAO,IAAI,
|
|
1
|
+
{"version":3,"file":"uscc.js","names":[],"sources":["../../src/cn/uscc.ts"],"sourcesContent":["/**\n * USCC (Unified Social Credit Code, 统一社会信用代码).\n *\n * 18 alphanumeric characters (excluding I, O, Z, S, V):\n * 1 (registering authority) + 1 (entity type) +\n * 6 (region code, digits only) + 9 (organisation code) +\n * 1 (mod 31 check character).\n *\n * @see https://zh.wikipedia.org/wiki/统一社会信用代码\n */\n\nimport type {\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport { clean } from \"#util/clean\";\nimport {\n randomChar,\n randomDigits,\n randomInt,\n} from \"#util/generate\";\nimport { err } from \"#util/result\";\nimport { isdigits } from \"#util/strings\";\n\nconst ALPHABET = \"0123456789ABCDEFGHJKLMNPQRTUWXY\";\n\nconst WEIGHTS = [\n 1, 3, 9, 27, 19, 26, 16, 17, 20, 29, 25, 13, 8, 24, 10,\n 30, 28,\n] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" -\").toUpperCase();\n\nconst calcCheckChar = (value: string): string => {\n let total = 0;\n for (const [i, weight] of WEIGHTS.entries()) {\n const ch = value[i];\n if (ch === undefined) continue;\n total += ALPHABET.indexOf(ch) * weight;\n }\n // SAFETY: (31 - total % 31) % 31 is in 0..30 and\n // ALPHABET has 31 characters.\n // eslint-disable-next-line no-non-null-assertion\n return ALPHABET[(31 - (total % 31)) % 31]!;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 18) {\n return err(\n \"INVALID_LENGTH\",\n \"USCC must be 18 characters\",\n );\n }\n\n for (const ch of v) {\n if (!ALPHABET.includes(ch)) {\n return err(\n \"INVALID_FORMAT\",\n \"USCC contains invalid character\",\n );\n }\n }\n\n if (!isdigits(v.slice(2, 8))) {\n return err(\n \"INVALID_FORMAT\",\n \"USCC region code (positions 3-8) must be digits\",\n );\n }\n\n if (v[17] !== calcCheckChar(v)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"USCC check character 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 USCC. */\nconst generate = (): string => {\n // SAFETY: indices 1..9 are within ALPHABET (31 chars).\n // eslint-disable-next-line no-non-null-assertion\n const reg = ALPHABET[randomInt(1, 9)]!;\n // eslint-disable-next-line no-non-null-assertion\n const etype = ALPHABET[randomInt(1, 9)]!;\n const region = randomDigits(6);\n let org = \"\";\n for (let i = 0; i < 9; i++) {\n org += randomChar(ALPHABET);\n }\n const payload = reg + etype + region + org;\n return payload + calcCheckChar(payload);\n};\n\n/** Chinese Unified Social Credit Code. */\nconst uscc: CountryValidator<\"CN\"> = {\n name: \"Unified Social Credit Code\",\n localName: \"统一社会信用代码\",\n abbreviation: \"USCC\",\n aliases: [\"统一社会信用代码\", \"USCC\"] as const,\n candidatePattern:\n \"[0-9A-HJ-NP-RTUW-Y]{2}\\\\d{6}[0-9A-HJ-NP-RTUW-Y]{10}\",\n scope: \"country\",\n country: \"CN\",\n entityType: \"company\",\n description:\n \"18-character tax/registration code for Chinese entities\",\n sourceUrl:\n \"https://zh.wikipedia.org/wiki/统一社会信用代码\",\n lengths: [18] as const,\n examples: [\"91110000600037341L\"] as const,\n compact,\n format,\n validate,\n generate,\n};\n\nexport default uscc;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;AAyBA,MAAM,WAAW;AAEjB,MAAM,UAAU;CACd;CAAG;CAAG;CAAG;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAI;CAAG;CAAI;CACpD;CAAI;AACN;AAEA,MAAM,WAAW,UACf,MAAM,OAAO,IAAI,CAAC,CAAC,YAAY;AAEjC,MAAM,iBAAiB,UAA0B;CAC/C,IAAI,QAAQ;CACZ,KAAK,MAAM,CAAC,GAAG,WAAW,QAAQ,QAAQ,GAAG;EAC3C,MAAM,KAAK,MAAM;EACjB,IAAI,OAAO,KAAA,GAAW;EACtB,SAAS,SAAS,QAAQ,EAAE,IAAI;CAClC;CAIA,OAAO,UAAU,KAAM,QAAQ,MAAO;AACxC;AAEA,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,KAAK;CAEvB,IAAI,EAAE,WAAW,IACf,OAAO,IACL,kBACA,4BACF;CAGF,KAAK,MAAM,MAAM,GACf,IAAI,CAAC,SAAS,SAAS,EAAE,GACvB,OAAO,IACL,kBACA,iCACF;CAIJ,IAAI,CAAC,SAAS,EAAE,MAAM,GAAG,CAAC,CAAC,GACzB,OAAO,IACL,kBACA,iDACF;CAGF,IAAI,EAAE,QAAQ,cAAc,CAAC,GAC3B,OAAO,IACL,oBACA,qCACF;CAGF,OAAO;EAAE,OAAO;EAAM,SAAS;CAAE;AACnC;AAEA,MAAM,UAAU,UAA0B,QAAQ,KAAK;;AAGvD,MAAM,iBAAyB;CAG7B,MAAM,MAAM,SAAS,UAAU,GAAG,CAAC;CAEnC,MAAM,QAAQ,SAAS,UAAU,GAAG,CAAC;CACrC,MAAM,SAAS,aAAa,CAAC;CAC7B,IAAI,MAAM;CACV,KAAK,IAAI,IAAI,GAAG,IAAI,GAAG,KACrB,OAAO,WAAW,QAAQ;CAE5B,MAAM,UAAU,MAAM,QAAQ,SAAS;CACvC,OAAO,UAAU,cAAc,OAAO;AACxC;;AAGA,MAAM,OAA+B;CACnC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,YAAY,MAAM;CAC5B,kBACE;CACF,OAAO;CACP,SAAS;CACT,YAAY;CACZ,aACE;CACF,WACE;CACF,SAAS,CAAC,EAAE;CACZ,UAAU,CAAC,oBAAoB;CAC/B;CACA;CACA;CACA;AACF"}
|
package/dist/cr/cpf.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"cpf.js","names":[],"sources":["../../src/cr/cpf.ts"],"sourcesContent":["/** Generate a random valid Costa Rican CPF. */\nconst generate = (): string => {\n const province = String(randomInt(1, 9));\n const volume = randomDigits(4);\n const entry = randomDigits(4);\n return `0${province}${volume}${entry}`;\n};\n\n/**\n * CPF (Cédula de Persona Física, Costa Rican\n * physical person ID number).\n *\n * 10 digits: 0P-TTTT-AAAA where:\n * - 0 = fixed leading zero\n * - P = province digit\n * - TTTT = volume (tomo), zero-padded\n * - AAAA = entry (asiento), zero-padded\n *\n * No check digit; validation is by format only.\n *\n * Ported from python-stdnum cr.cpf module.\n *\n * @see https://www.tse.go.cr/\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\";\n\n/**\n * Compact: strip spaces, pad hyphen-separated parts\n * (2-4-4), join, and prepend '0' if 9 digits.\n */\nconst compact = (value: string): string => {\n let number = clean(value, \" \").trim().toUpperCase();\n\n const parts = number.split(\"-\");\n if (parts.length === 3) {\n const [raw0, raw1, raw2] = parts;\n const p0 = (raw0 ?? \"\").padStart(2, \"0\");\n const p1 = (raw1 ?? \"\").padStart(4, \"0\");\n const p2 = (raw2 ?? \"\").padStart(4, \"0\");\n number = p0 + p1 + p2;\n } else {\n number = number.replaceAll(\"-\", \"\");\n }\n\n if (number.length === 9) {\n number = \"0\" + number;\n }\n\n return number;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"Costa Rican CPF must be 10 digits\",\n );\n }\n\n if (!/^\\d{10}$/.test(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Costa Rican CPF must contain only digits\",\n );\n }\n\n if (v[0] !== \"0\") {\n return err(\n \"INVALID_COMPONENT\",\n \"Costa Rican CPF must start with 0\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return `${v.slice(0, 2)}-${v.slice(2, 6)}-${v.slice(6, 10)}`;\n};\n\n/**\n * Costa Rican physical person ID number (CPF).\n *\n * Examples sourced from python-stdnum test suite.\n */\nconst cpf: CountryValidator<\"CR\"> = {\n name: \"Costa Rican Physical Person ID\",\n localName: \"Cédula de Persona Física\",\n abbreviation: \"CPF\",\n aliases: [\"CPF\", \"cédula de persona física\"] as const,\n candidatePattern: \"\\\\d{9,12}\",\n scope: \"country\",\n country: \"CR\",\n entityType: \"person\",\n lengths: [10] as const,\n examples: [\"0304550175\", \"0109130259\"] as const,\n compact,\n format,\n validate,\n sourceUrl: \"https://www.tse.go.cr/\",\n generate,\n};\n\nexport default cpf;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;AACA,MAAM,iBAAyB;CAI7B,OAAO,IAHU,OAAO,UAAU,GAAG,CAAC,CAGpB,IAFH,aAAa,CAED,IADb,aAAa,CACQ;AACrC;;;;;AAgCA,MAAM,WAAW,UAA0B;CACzC,IAAI,SAAS,MAAM,OAAO,GAAG,
|
|
1
|
+
{"version":3,"file":"cpf.js","names":[],"sources":["../../src/cr/cpf.ts"],"sourcesContent":["/** Generate a random valid Costa Rican CPF. */\nconst generate = (): string => {\n const province = String(randomInt(1, 9));\n const volume = randomDigits(4);\n const entry = randomDigits(4);\n return `0${province}${volume}${entry}`;\n};\n\n/**\n * CPF (Cédula de Persona Física, Costa Rican\n * physical person ID number).\n *\n * 10 digits: 0P-TTTT-AAAA where:\n * - 0 = fixed leading zero\n * - P = province digit\n * - TTTT = volume (tomo), zero-padded\n * - AAAA = entry (asiento), zero-padded\n *\n * No check digit; validation is by format only.\n *\n * Ported from python-stdnum cr.cpf module.\n *\n * @see https://www.tse.go.cr/\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\";\n\n/**\n * Compact: strip spaces, pad hyphen-separated parts\n * (2-4-4), join, and prepend '0' if 9 digits.\n */\nconst compact = (value: string): string => {\n let number = clean(value, \" \").trim().toUpperCase();\n\n const parts = number.split(\"-\");\n if (parts.length === 3) {\n const [raw0, raw1, raw2] = parts;\n const p0 = (raw0 ?? \"\").padStart(2, \"0\");\n const p1 = (raw1 ?? \"\").padStart(4, \"0\");\n const p2 = (raw2 ?? \"\").padStart(4, \"0\");\n number = p0 + p1 + p2;\n } else {\n number = number.replaceAll(\"-\", \"\");\n }\n\n if (number.length === 9) {\n number = \"0\" + number;\n }\n\n return number;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 10) {\n return err(\n \"INVALID_LENGTH\",\n \"Costa Rican CPF must be 10 digits\",\n );\n }\n\n if (!/^\\d{10}$/.test(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Costa Rican CPF must contain only digits\",\n );\n }\n\n if (v[0] !== \"0\") {\n return err(\n \"INVALID_COMPONENT\",\n \"Costa Rican CPF must start with 0\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => {\n const v = compact(value);\n return `${v.slice(0, 2)}-${v.slice(2, 6)}-${v.slice(6, 10)}`;\n};\n\n/**\n * Costa Rican physical person ID number (CPF).\n *\n * Examples sourced from python-stdnum test suite.\n */\nconst cpf: CountryValidator<\"CR\"> = {\n name: \"Costa Rican Physical Person ID\",\n localName: \"Cédula de Persona Física\",\n abbreviation: \"CPF\",\n aliases: [\"CPF\", \"cédula de persona física\"] as const,\n candidatePattern: \"\\\\d{9,12}\",\n scope: \"country\",\n country: \"CR\",\n entityType: \"person\",\n lengths: [10] as const,\n examples: [\"0304550175\", \"0109130259\"] as const,\n compact,\n format,\n validate,\n sourceUrl: \"https://www.tse.go.cr/\",\n generate,\n};\n\nexport default cpf;\nexport { compact, format, validate, generate };\n"],"mappings":";;;;;AACA,MAAM,iBAAyB;CAI7B,OAAO,IAHU,OAAO,UAAU,GAAG,CAAC,CAGpB,IAFH,aAAa,CAED,IADb,aAAa,CACQ;AACrC;;;;;AAgCA,MAAM,WAAW,UAA0B;CACzC,IAAI,SAAS,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,YAAY;CAElD,MAAM,QAAQ,OAAO,MAAM,GAAG;CAC9B,IAAI,MAAM,WAAW,GAAG;EACtB,MAAM,CAAC,MAAM,MAAM,QAAQ;EAC3B,MAAM,MAAM,QAAQ,GAAA,CAAI,SAAS,GAAG,GAAG;EACvC,MAAM,MAAM,QAAQ,GAAA,CAAI,SAAS,GAAG,GAAG;EACvC,MAAM,MAAM,QAAQ,GAAA,CAAI,SAAS,GAAG,GAAG;EACvC,SAAS,KAAK,KAAK;CACrB,OACE,SAAS,OAAO,WAAW,KAAK,EAAE;CAGpC,IAAI,OAAO,WAAW,GACpB,SAAS,MAAM;CAGjB,OAAO;AACT;AAEA,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,KAAK;CAEvB,IAAI,EAAE,WAAW,IACf,OAAO,IACL,kBACA,mCACF;CAGF,IAAI,CAAC,WAAW,KAAK,CAAC,GACpB,OAAO,IACL,kBACA,0CACF;CAGF,IAAI,EAAE,OAAO,KACX,OAAO,IACL,qBACA,mCACF;CAGF,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,CAAC,EAAE,GAAG,EAAE,MAAM,GAAG,EAAE;AAC3D;;;;;;AAOA,MAAM,MAA8B;CAClC,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS,CAAC,OAAO,0BAA0B;CAC3C,kBAAkB;CAClB,OAAO;CACP,SAAS;CACT,YAAY;CACZ,SAAS,CAAC,EAAE;CACZ,UAAU,CAAC,cAAc,YAAY;CACrC;CACA;CACA;CACA,WAAW;CACX;AACF"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GlobalValidator, ValidateResult } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/crypto/btcbase58.d.ts
|
|
4
|
+
declare const compact: (value: string) => string;
|
|
5
|
+
declare const decodeBase58: (value: string) => Uint8Array | null;
|
|
6
|
+
declare const validate: (value: string) => ValidateResult;
|
|
7
|
+
declare const format: (value: string) => string;
|
|
8
|
+
declare const btcBase58: GlobalValidator;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { compact, decodeBase58, btcBase58 as default, format, validate };
|
|
11
|
+
//# sourceMappingURL=btcbase58.d.ts.map
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import { sha256 } from "../_checksums/sha256.js";
|
|
2
|
+
import { clean } from "../_util/clean.js";
|
|
3
|
+
import { err } from "../_util/result.js";
|
|
4
|
+
//#region src/crypto/btcbase58.ts
|
|
5
|
+
const BASE58_ALPHABET = "123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz";
|
|
6
|
+
const MIN_LENGTH = 26;
|
|
7
|
+
const MAX_LENGTH = 35;
|
|
8
|
+
const DECODED_ADDRESS_LENGTH = 25;
|
|
9
|
+
const CHECKSUM_LENGTH = 4;
|
|
10
|
+
const MAINNET_P2PKH_VERSION = 0;
|
|
11
|
+
const MAINNET_P2SH_VERSION = 5;
|
|
12
|
+
const compact = (value) => clean(value, " ");
|
|
13
|
+
const decodeBase58 = (value) => {
|
|
14
|
+
let decoded = 0n;
|
|
15
|
+
for (const ch of value) {
|
|
16
|
+
const digit = BASE58_ALPHABET.indexOf(ch);
|
|
17
|
+
if (digit === -1) return null;
|
|
18
|
+
decoded = decoded * 58n + BigInt(digit);
|
|
19
|
+
}
|
|
20
|
+
const bytes = [];
|
|
21
|
+
while (decoded > 0n) {
|
|
22
|
+
bytes.push(Number(decoded & 255n));
|
|
23
|
+
decoded >>= 8n;
|
|
24
|
+
}
|
|
25
|
+
bytes.reverse();
|
|
26
|
+
let leadingZeros = 0;
|
|
27
|
+
while (value.charAt(leadingZeros) === "1") leadingZeros += 1;
|
|
28
|
+
const result = new Uint8Array(leadingZeros + bytes.length);
|
|
29
|
+
result.set(bytes, leadingZeros);
|
|
30
|
+
return result;
|
|
31
|
+
};
|
|
32
|
+
const hasValidChecksum = (decoded) => {
|
|
33
|
+
if (decoded.length <= CHECKSUM_LENGTH) return false;
|
|
34
|
+
const payload = decoded.slice(0, -4);
|
|
35
|
+
const checksum = decoded.slice(-4);
|
|
36
|
+
const digest = sha256(sha256(payload));
|
|
37
|
+
for (let i = 0; i < CHECKSUM_LENGTH; i += 1) if (checksum[i] !== digest[i]) return false;
|
|
38
|
+
return true;
|
|
39
|
+
};
|
|
40
|
+
const validate = (value) => {
|
|
41
|
+
const v = compact(value);
|
|
42
|
+
if (v.length < MIN_LENGTH || v.length > MAX_LENGTH) return err("INVALID_LENGTH", "Bitcoin Base58Check address must be 26-35 characters");
|
|
43
|
+
if (!/^[13]/.test(v)) return err("INVALID_COMPONENT", "Bitcoin Base58Check address must start with 1 or 3");
|
|
44
|
+
const decoded = decodeBase58(v);
|
|
45
|
+
if (decoded === null) return err("INVALID_FORMAT", "Bitcoin Base58Check address contains invalid characters");
|
|
46
|
+
if (decoded.length !== DECODED_ADDRESS_LENGTH) return err("INVALID_LENGTH", "Bitcoin Base58Check address decoded payload must be 25 bytes");
|
|
47
|
+
const version = decoded[0];
|
|
48
|
+
if (version !== MAINNET_P2PKH_VERSION && version !== MAINNET_P2SH_VERSION) return err("INVALID_COMPONENT", "Bitcoin Base58Check address has an unsupported version");
|
|
49
|
+
if (!hasValidChecksum(decoded)) return err("INVALID_CHECKSUM", "Bitcoin Base58Check address fails checksum validation");
|
|
50
|
+
return {
|
|
51
|
+
valid: true,
|
|
52
|
+
compact: v
|
|
53
|
+
};
|
|
54
|
+
};
|
|
55
|
+
const format = (value) => compact(value);
|
|
56
|
+
const btcBase58 = {
|
|
57
|
+
scope: "global",
|
|
58
|
+
name: "Bitcoin Base58Check Address",
|
|
59
|
+
localName: "Bitcoin Base58Check Address",
|
|
60
|
+
abbreviation: "BTC",
|
|
61
|
+
aliases: [
|
|
62
|
+
"Bitcoin address",
|
|
63
|
+
"Bitcoin legacy address",
|
|
64
|
+
"Base58Check address"
|
|
65
|
+
],
|
|
66
|
+
candidatePattern: "[13][1-9A-HJ-NP-Za-km-z]{25,34}",
|
|
67
|
+
entityType: "any",
|
|
68
|
+
examples: ["1BoatSLRHtKNngkdXEeobR76b53LETtpyT", "3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy"],
|
|
69
|
+
compact,
|
|
70
|
+
format,
|
|
71
|
+
validate
|
|
72
|
+
};
|
|
73
|
+
//#endregion
|
|
74
|
+
export { compact, decodeBase58, btcBase58 as default, format, validate };
|
|
75
|
+
|
|
76
|
+
//# sourceMappingURL=btcbase58.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"btcbase58.js","names":[],"sources":["../../src/crypto/btcbase58.ts"],"sourcesContent":["/**\n * Bitcoin Base58Check address.\n *\n * Validates legacy mainnet P2PKH and P2SH Bitcoin\n * addresses, including the double-SHA-256 checksum.\n */\n\nimport type {\n GlobalValidator,\n ValidateResult,\n} from \"../types\";\n\nimport { sha256 } from \"#checksums/sha256\";\nimport { clean } from \"#util/clean\";\nimport { err } from \"#util/result\";\n\nconst BASE58_ALPHABET =\n \"123456789ABCDEFGHJKLMNPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz\";\n\nconst MIN_LENGTH = 26;\nconst MAX_LENGTH = 35;\nconst DECODED_ADDRESS_LENGTH = 25;\nconst CHECKSUM_LENGTH = 4;\nconst MAINNET_P2PKH_VERSION = 0x00;\nconst MAINNET_P2SH_VERSION = 0x05;\n\nconst compact = (value: string): string =>\n clean(value, \" \");\n\nconst decodeBase58 = (value: string): Uint8Array | null => {\n let decoded = 0n;\n for (const ch of value) {\n const digit = BASE58_ALPHABET.indexOf(ch);\n if (digit === -1) return null;\n decoded = decoded * 58n + BigInt(digit);\n }\n\n const bytes: number[] = [];\n while (decoded > 0n) {\n bytes.push(Number(decoded & 0xffn));\n decoded >>= 8n;\n }\n bytes.reverse();\n\n let leadingZeros = 0;\n while (value.charAt(leadingZeros) === \"1\") {\n leadingZeros += 1;\n }\n\n const result = new Uint8Array(\n leadingZeros + bytes.length,\n );\n result.set(bytes, leadingZeros);\n return result;\n};\n\nconst hasValidChecksum = (decoded: Uint8Array): boolean => {\n if (decoded.length <= CHECKSUM_LENGTH) return false;\n\n const payload = decoded.slice(0, -CHECKSUM_LENGTH);\n const checksum = decoded.slice(-CHECKSUM_LENGTH);\n const digest = sha256(sha256(payload));\n\n for (let i = 0; i < CHECKSUM_LENGTH; i += 1) {\n if (checksum[i] !== digest[i]) return false;\n }\n return true;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n if (v.length < MIN_LENGTH || v.length > MAX_LENGTH) {\n return err(\n \"INVALID_LENGTH\",\n \"Bitcoin Base58Check address must be 26-35 characters\",\n );\n }\n if (!/^[13]/.test(v)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Bitcoin Base58Check address must start with 1 or 3\",\n );\n }\n\n const decoded = decodeBase58(v);\n if (decoded === null) {\n return err(\n \"INVALID_FORMAT\",\n \"Bitcoin Base58Check address contains invalid characters\",\n );\n }\n if (decoded.length !== DECODED_ADDRESS_LENGTH) {\n return err(\n \"INVALID_LENGTH\",\n \"Bitcoin Base58Check address decoded payload must be 25 bytes\",\n );\n }\n\n const version = decoded[0];\n if (\n version !== MAINNET_P2PKH_VERSION &&\n version !== MAINNET_P2SH_VERSION\n ) {\n return err(\n \"INVALID_COMPONENT\",\n \"Bitcoin Base58Check address has an unsupported version\",\n );\n }\n\n if (!hasValidChecksum(decoded)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Bitcoin Base58Check address fails checksum validation\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\nconst btcBase58: GlobalValidator = {\n scope: \"global\",\n name: \"Bitcoin Base58Check Address\",\n localName: \"Bitcoin Base58Check Address\",\n abbreviation: \"BTC\",\n aliases: [\n \"Bitcoin address\",\n \"Bitcoin legacy address\",\n \"Base58Check address\",\n ] as const,\n candidatePattern: \"[13][1-9A-HJ-NP-Za-km-z]{25,34}\",\n entityType: \"any\",\n examples: [\n \"1BoatSLRHtKNngkdXEeobR76b53LETtpyT\",\n \"3J98t1WpEZ73CNmQviecrnyiWrnqRhWNLy\",\n ] as const,\n compact,\n format,\n validate,\n};\n\nexport default btcBase58;\nexport { compact, decodeBase58, format, validate };\n"],"mappings":";;;;AAgBA,MAAM,kBACJ;AAEF,MAAM,aAAa;AACnB,MAAM,aAAa;AACnB,MAAM,yBAAyB;AAC/B,MAAM,kBAAkB;AACxB,MAAM,wBAAwB;AAC9B,MAAM,uBAAuB;AAE7B,MAAM,WAAW,UACf,MAAM,OAAO,GAAG;AAElB,MAAM,gBAAgB,UAAqC;CACzD,IAAI,UAAU;CACd,KAAK,MAAM,MAAM,OAAO;EACtB,MAAM,QAAQ,gBAAgB,QAAQ,EAAE;EACxC,IAAI,UAAU,IAAI,OAAO;EACzB,UAAU,UAAU,MAAM,OAAO,KAAK;CACxC;CAEA,MAAM,QAAkB,CAAC;CACzB,OAAO,UAAU,IAAI;EACnB,MAAM,KAAK,OAAO,UAAU,IAAK,CAAC;EAClC,YAAY;CACd;CACA,MAAM,QAAQ;CAEd,IAAI,eAAe;CACnB,OAAO,MAAM,OAAO,YAAY,MAAM,KACpC,gBAAgB;CAGlB,MAAM,SAAS,IAAI,WACjB,eAAe,MAAM,MACvB;CACA,OAAO,IAAI,OAAO,YAAY;CAC9B,OAAO;AACT;AAEA,MAAM,oBAAoB,YAAiC;CACzD,IAAI,QAAQ,UAAU,iBAAiB,OAAO;CAE9C,MAAM,UAAU,QAAQ,MAAM,GAAG,EAAgB;CACjD,MAAM,WAAW,QAAQ,MAAM,EAAgB;CAC/C,MAAM,SAAS,OAAO,OAAO,OAAO,CAAC;CAErC,KAAK,IAAI,IAAI,GAAG,IAAI,iBAAiB,KAAK,GACxC,IAAI,SAAS,OAAO,OAAO,IAAI,OAAO;CAExC,OAAO;AACT;AAEA,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,KAAK;CACvB,IAAI,EAAE,SAAS,cAAc,EAAE,SAAS,YACtC,OAAO,IACL,kBACA,sDACF;CAEF,IAAI,CAAC,QAAQ,KAAK,CAAC,GACjB,OAAO,IACL,qBACA,oDACF;CAGF,MAAM,UAAU,aAAa,CAAC;CAC9B,IAAI,YAAY,MACd,OAAO,IACL,kBACA,yDACF;CAEF,IAAI,QAAQ,WAAW,wBACrB,OAAO,IACL,kBACA,8DACF;CAGF,MAAM,UAAU,QAAQ;CACxB,IACE,YAAY,yBACZ,YAAY,sBAEZ,OAAO,IACL,qBACA,wDACF;CAGF,IAAI,CAAC,iBAAiB,OAAO,GAC3B,OAAO,IACL,oBACA,uDACF;CAGF,OAAO;EAAE,OAAO;EAAM,SAAS;CAAE;AACnC;AAEA,MAAM,UAAU,UAA0B,QAAQ,KAAK;AAEvD,MAAM,YAA6B;CACjC,OAAO;CACP,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EACP;EACA;EACA;CACF;CACA,kBAAkB;CAClB,YAAY;CACZ,UAAU,CACR,sCACA,oCACF;CACA;CACA;CACA;AACF"}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { GlobalValidator, ValidateResult } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/crypto/btcbech32.d.ts
|
|
4
|
+
declare const compact: (value: string) => string;
|
|
5
|
+
declare const polymod: (values: readonly number[]) => number;
|
|
6
|
+
declare const convertBits: (values: readonly number[], fromBits: number, toBits: number) => number[] | null;
|
|
7
|
+
type Bech32Validation = {
|
|
8
|
+
valid: true;
|
|
9
|
+
} | {
|
|
10
|
+
valid: false;
|
|
11
|
+
code: "format" | "checksum" | "component";
|
|
12
|
+
};
|
|
13
|
+
declare const validateBech32: (rawValue: string) => Bech32Validation;
|
|
14
|
+
declare const validate: (value: string) => ValidateResult;
|
|
15
|
+
declare const format: (value: string) => string;
|
|
16
|
+
declare const btcBech32: GlobalValidator;
|
|
17
|
+
//#endregion
|
|
18
|
+
export { compact, convertBits, btcBech32 as default, format, polymod, validate, validateBech32 };
|
|
19
|
+
//# sourceMappingURL=btcbech32.d.ts.map
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
import { clean } from "../_util/clean.js";
|
|
2
|
+
import { err } from "../_util/result.js";
|
|
3
|
+
//#region src/crypto/btcbech32.ts
|
|
4
|
+
const CHARSET = "qpzry9x8gf2tvdw0s3jn54khce6mua7l";
|
|
5
|
+
const GENERATORS = [
|
|
6
|
+
996825010,
|
|
7
|
+
642813549,
|
|
8
|
+
513874426,
|
|
9
|
+
1027748829,
|
|
10
|
+
705979059
|
|
11
|
+
];
|
|
12
|
+
const BECH32_CONST = 1;
|
|
13
|
+
const BECH32M_CONST = 734539939;
|
|
14
|
+
const MIN_DATA_LENGTH = 11;
|
|
15
|
+
const MAX_DATA_LENGTH = 71;
|
|
16
|
+
const BC_HRP_EXPANSION = [
|
|
17
|
+
3,
|
|
18
|
+
3,
|
|
19
|
+
0,
|
|
20
|
+
2,
|
|
21
|
+
3
|
|
22
|
+
];
|
|
23
|
+
const compact = (value) => clean(value, " ").toLowerCase();
|
|
24
|
+
const polymod = (values) => {
|
|
25
|
+
let chk = 1;
|
|
26
|
+
for (const value of values) {
|
|
27
|
+
const top = chk >>> 25;
|
|
28
|
+
chk = ((chk & 33554431) << 5 ^ value) >>> 0;
|
|
29
|
+
for (let i = 0; i < GENERATORS.length; i += 1) {
|
|
30
|
+
const generator = GENERATORS[i] ?? 0;
|
|
31
|
+
if ((top >>> i & 1) === 1) chk = (chk ^ generator) >>> 0;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
return chk >>> 0;
|
|
35
|
+
};
|
|
36
|
+
const dataValues = (data) => {
|
|
37
|
+
const values = [];
|
|
38
|
+
for (const ch of data) {
|
|
39
|
+
const value = CHARSET.indexOf(ch);
|
|
40
|
+
if (value === -1) return null;
|
|
41
|
+
values.push(value);
|
|
42
|
+
}
|
|
43
|
+
return values;
|
|
44
|
+
};
|
|
45
|
+
const convertBits = (values, fromBits, toBits) => {
|
|
46
|
+
let accumulator = 0;
|
|
47
|
+
let bits = 0;
|
|
48
|
+
const maxValue = (1 << toBits) - 1;
|
|
49
|
+
const result = [];
|
|
50
|
+
for (const value of values) {
|
|
51
|
+
if (value < 0 || value >>> fromBits !== 0) return null;
|
|
52
|
+
accumulator = accumulator << fromBits | value;
|
|
53
|
+
bits += fromBits;
|
|
54
|
+
while (bits >= toBits) {
|
|
55
|
+
bits -= toBits;
|
|
56
|
+
result.push(accumulator >>> bits & maxValue);
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
if (bits >= fromBits) return null;
|
|
60
|
+
if ((accumulator << toBits - bits & maxValue) !== 0) return null;
|
|
61
|
+
return result;
|
|
62
|
+
};
|
|
63
|
+
const validateBech32 = (rawValue) => {
|
|
64
|
+
const hasLower = /[a-z]/.test(rawValue);
|
|
65
|
+
const hasUpper = /[A-Z]/.test(rawValue);
|
|
66
|
+
if (hasLower && hasUpper) return {
|
|
67
|
+
valid: false,
|
|
68
|
+
code: "format"
|
|
69
|
+
};
|
|
70
|
+
const v = rawValue.toLowerCase();
|
|
71
|
+
if (!v.startsWith("bc1")) return {
|
|
72
|
+
valid: false,
|
|
73
|
+
code: "component"
|
|
74
|
+
};
|
|
75
|
+
const data = v.slice(3);
|
|
76
|
+
if (data.length < MIN_DATA_LENGTH || data.length > MAX_DATA_LENGTH) return {
|
|
77
|
+
valid: false,
|
|
78
|
+
code: "format"
|
|
79
|
+
};
|
|
80
|
+
const values = dataValues(data);
|
|
81
|
+
if (values === null) return {
|
|
82
|
+
valid: false,
|
|
83
|
+
code: "format"
|
|
84
|
+
};
|
|
85
|
+
const expanded = Array.from(BC_HRP_EXPANSION);
|
|
86
|
+
for (const value of values) expanded.push(value);
|
|
87
|
+
const check = polymod(expanded);
|
|
88
|
+
const version = values[0];
|
|
89
|
+
if (version === void 0 || version > 16) return {
|
|
90
|
+
valid: false,
|
|
91
|
+
code: "component"
|
|
92
|
+
};
|
|
93
|
+
if (version === 0 && check !== BECH32_CONST) return {
|
|
94
|
+
valid: false,
|
|
95
|
+
code: "checksum"
|
|
96
|
+
};
|
|
97
|
+
if (version > 0 && check !== BECH32M_CONST) return {
|
|
98
|
+
valid: false,
|
|
99
|
+
code: "checksum"
|
|
100
|
+
};
|
|
101
|
+
const program = convertBits(values.slice(1, -6), 5, 8);
|
|
102
|
+
if (program === null || program.length < 2 || program.length > 40) return {
|
|
103
|
+
valid: false,
|
|
104
|
+
code: "component"
|
|
105
|
+
};
|
|
106
|
+
if (version === 0 && program.length !== 20 && program.length !== 32) return {
|
|
107
|
+
valid: false,
|
|
108
|
+
code: "component"
|
|
109
|
+
};
|
|
110
|
+
return { valid: true };
|
|
111
|
+
};
|
|
112
|
+
const validate = (value) => {
|
|
113
|
+
const v = compact(value);
|
|
114
|
+
const result = validateBech32(clean(value, " "));
|
|
115
|
+
if (result.valid) return {
|
|
116
|
+
valid: true,
|
|
117
|
+
compact: v
|
|
118
|
+
};
|
|
119
|
+
if (result.code === "component") return err("INVALID_COMPONENT", "Bitcoin Bech32 address has an unsupported component");
|
|
120
|
+
if (result.code === "checksum") return err("INVALID_CHECKSUM", "Bitcoin Bech32 address fails checksum validation");
|
|
121
|
+
return err("INVALID_FORMAT", "Bitcoin Bech32 address has an invalid format");
|
|
122
|
+
};
|
|
123
|
+
const format = (value) => compact(value);
|
|
124
|
+
const btcBech32 = {
|
|
125
|
+
scope: "global",
|
|
126
|
+
name: "Bitcoin Bech32 Address",
|
|
127
|
+
localName: "Bitcoin Bech32 Address",
|
|
128
|
+
abbreviation: "BTC",
|
|
129
|
+
aliases: [
|
|
130
|
+
"Bitcoin address",
|
|
131
|
+
"Bitcoin SegWit address",
|
|
132
|
+
"Bech32 address",
|
|
133
|
+
"Bech32m address"
|
|
134
|
+
],
|
|
135
|
+
candidatePattern: "(?:bc1[ac-hj-np-z02-9]{11,71}|BC1[AC-HJ-NP-Z02-9]{11,71})",
|
|
136
|
+
entityType: "any",
|
|
137
|
+
examples: ["bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4", "bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3"],
|
|
138
|
+
compact,
|
|
139
|
+
format,
|
|
140
|
+
validate
|
|
141
|
+
};
|
|
142
|
+
//#endregion
|
|
143
|
+
export { compact, convertBits, btcBech32 as default, format, polymod, validate, validateBech32 };
|
|
144
|
+
|
|
145
|
+
//# sourceMappingURL=btcbech32.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"btcbech32.js","names":[],"sources":["../../src/crypto/btcbech32.ts"],"sourcesContent":["/**\n * Bitcoin Bech32/Bech32m address.\n *\n * Validates mainnet SegWit addresses with the\n * Bech32 checksum for v0 and Bech32m for v1+.\n */\n\nimport type {\n GlobalValidator,\n ValidateResult,\n} from \"../types\";\n\nimport { clean } from \"#util/clean\";\nimport { err } from \"#util/result\";\n\nconst CHARSET = \"qpzry9x8gf2tvdw0s3jn54khce6mua7l\";\nconst GENERATORS = [\n 0x3b6a57b2, 0x26508e6d, 0x1ea119fa, 0x3d4233dd,\n 0x2a1462b3,\n] as const;\nconst BECH32_CONST = 1;\nconst BECH32M_CONST = 0x2bc830a3;\nconst CHECKSUM_LENGTH = 6;\nconst MIN_DATA_LENGTH = 11;\nconst MAX_DATA_LENGTH = 71;\nconst BC_HRP_EXPANSION = [3, 3, 0, 2, 3] as const;\n\nconst compact = (value: string): string =>\n clean(value, \" \").toLowerCase();\n\nconst polymod = (values: readonly number[]): number => {\n let chk = 1;\n for (const value of values) {\n const top = chk >>> 25;\n chk = (((chk & 0x1ffffff) << 5) ^ value) >>> 0;\n for (let i = 0; i < GENERATORS.length; i += 1) {\n const generator = GENERATORS[i] ?? 0;\n if (((top >>> i) & 1) === 1) {\n chk = (chk ^ generator) >>> 0;\n }\n }\n }\n return chk >>> 0;\n};\n\nconst dataValues = (data: string): number[] | null => {\n const values: number[] = [];\n for (const ch of data) {\n const value = CHARSET.indexOf(ch);\n if (value === -1) return null;\n values.push(value);\n }\n return values;\n};\n\nconst convertBits = (\n values: readonly number[],\n fromBits: number,\n toBits: number,\n): number[] | null => {\n let accumulator = 0;\n let bits = 0;\n const maxValue = (1 << toBits) - 1;\n const result: number[] = [];\n\n for (const value of values) {\n if (value < 0 || value >>> fromBits !== 0) return null;\n accumulator = (accumulator << fromBits) | value;\n bits += fromBits;\n while (bits >= toBits) {\n bits -= toBits;\n result.push((accumulator >>> bits) & maxValue);\n }\n }\n\n if (bits >= fromBits) return null;\n if (((accumulator << (toBits - bits)) & maxValue) !== 0) {\n return null;\n }\n\n return result;\n};\n\ntype Bech32Validation =\n | { valid: true }\n | {\n valid: false;\n code: \"format\" | \"checksum\" | \"component\";\n };\n\nconst validateBech32 = (\n rawValue: string,\n): Bech32Validation => {\n const hasLower = /[a-z]/.test(rawValue);\n const hasUpper = /[A-Z]/.test(rawValue);\n if (hasLower && hasUpper)\n return { valid: false, code: \"format\" };\n\n const v = rawValue.toLowerCase();\n if (!v.startsWith(\"bc1\"))\n return { valid: false, code: \"component\" };\n\n const data = v.slice(3);\n if (\n data.length < MIN_DATA_LENGTH ||\n data.length > MAX_DATA_LENGTH\n ) {\n return { valid: false, code: \"format\" };\n }\n\n const values = dataValues(data);\n if (values === null)\n return { valid: false, code: \"format\" };\n\n const expanded: number[] = Array.from(BC_HRP_EXPANSION);\n for (const value of values) {\n expanded.push(value);\n }\n const check = polymod(expanded);\n\n const version = values[0];\n if (version === undefined || version > 16) {\n return { valid: false, code: \"component\" };\n }\n\n if (version === 0 && check !== BECH32_CONST) {\n return { valid: false, code: \"checksum\" };\n }\n if (version > 0 && check !== BECH32M_CONST) {\n return { valid: false, code: \"checksum\" };\n }\n\n const programValues = values.slice(1, -CHECKSUM_LENGTH);\n const program = convertBits(programValues, 5, 8);\n if (\n program === null ||\n program.length < 2 ||\n program.length > 40\n ) {\n return { valid: false, code: \"component\" };\n }\n\n if (\n version === 0 &&\n program.length !== 20 &&\n program.length !== 32\n ) {\n return { valid: false, code: \"component\" };\n }\n\n return { valid: true };\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n const result = validateBech32(clean(value, \" \"));\n if (result.valid) return { valid: true, compact: v };\n\n if (result.code === \"component\") {\n return err(\n \"INVALID_COMPONENT\",\n \"Bitcoin Bech32 address has an unsupported component\",\n );\n }\n if (result.code === \"checksum\") {\n return err(\n \"INVALID_CHECKSUM\",\n \"Bitcoin Bech32 address fails checksum validation\",\n );\n }\n return err(\n \"INVALID_FORMAT\",\n \"Bitcoin Bech32 address has an invalid format\",\n );\n};\n\nconst format = (value: string): string => compact(value);\n\nconst btcBech32: GlobalValidator = {\n scope: \"global\",\n name: \"Bitcoin Bech32 Address\",\n localName: \"Bitcoin Bech32 Address\",\n abbreviation: \"BTC\",\n aliases: [\n \"Bitcoin address\",\n \"Bitcoin SegWit address\",\n \"Bech32 address\",\n \"Bech32m address\",\n ] as const,\n candidatePattern:\n \"(?:bc1[ac-hj-np-z02-9]{11,71}|BC1[AC-HJ-NP-Z02-9]{11,71})\",\n entityType: \"any\",\n examples: [\n \"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\",\n \"bc1qrp33g0q5c5txsp9arysrx4k6zdkfs4nce4xj0gdcccefvpysxf3qccfmv3\",\n ] as const,\n compact,\n format,\n validate,\n};\n\nexport default btcBech32;\nexport {\n compact,\n convertBits,\n format,\n polymod,\n validate,\n validateBech32,\n};\n"],"mappings":"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"}
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
import { GlobalValidator, ValidateResult } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/crypto/eth.d.ts
|
|
4
|
+
declare const compact: (value: string) => string;
|
|
5
|
+
declare const hasValidEip55Checksum: (address: string) => boolean;
|
|
6
|
+
declare const validate: (value: string) => ValidateResult;
|
|
7
|
+
declare const format: (value: string) => string;
|
|
8
|
+
declare const eth: GlobalValidator;
|
|
9
|
+
//#endregion
|
|
10
|
+
export { compact, eth as default, format, hasValidEip55Checksum, validate };
|
|
11
|
+
//# sourceMappingURL=eth.d.ts.map
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { keccak256 } from "../_checksums/keccak.js";
|
|
2
|
+
import { clean } from "../_util/clean.js";
|
|
3
|
+
import { err } from "../_util/result.js";
|
|
4
|
+
//#region src/crypto/eth.ts
|
|
5
|
+
const ETH_ADDRESS_LENGTH = 42;
|
|
6
|
+
const HEX_PREFIX_LENGTH = 2;
|
|
7
|
+
const compact = (value) => clean(value, " ").toLowerCase();
|
|
8
|
+
const asciiBytes = (value) => {
|
|
9
|
+
const bytes = new Uint8Array(value.length);
|
|
10
|
+
for (let i = 0; i < value.length; i += 1) bytes[i] = value.charCodeAt(i);
|
|
11
|
+
return bytes;
|
|
12
|
+
};
|
|
13
|
+
const hexBytes = (bytes) => {
|
|
14
|
+
let result = "";
|
|
15
|
+
for (const byte of bytes) result += byte.toString(16).padStart(2, "0");
|
|
16
|
+
return result;
|
|
17
|
+
};
|
|
18
|
+
const isMixedCase = (value) => /[a-f]/.test(value) && /[A-F]/.test(value);
|
|
19
|
+
const hasValidEip55Checksum = (address) => {
|
|
20
|
+
const body = address.slice(HEX_PREFIX_LENGTH);
|
|
21
|
+
if (!isMixedCase(body)) return true;
|
|
22
|
+
const hash = hexBytes(keccak256(asciiBytes(body.toLowerCase())));
|
|
23
|
+
for (let i = 0; i < body.length; i += 1) {
|
|
24
|
+
const ch = body.charAt(i);
|
|
25
|
+
if (/\d/.test(ch)) continue;
|
|
26
|
+
if (ch !== (Number.parseInt(hash.charAt(i), 16) >= 8 ? ch.toUpperCase() : ch.toLowerCase())) return false;
|
|
27
|
+
}
|
|
28
|
+
return true;
|
|
29
|
+
};
|
|
30
|
+
const validate = (value) => {
|
|
31
|
+
const raw = clean(value, " ");
|
|
32
|
+
const v = raw.toLowerCase();
|
|
33
|
+
if (v.length !== ETH_ADDRESS_LENGTH) return err("INVALID_LENGTH", "Ethereum address must be 42 characters");
|
|
34
|
+
if (!/^0x[0-9a-f]{40}$/i.test(raw)) return err("INVALID_FORMAT", "Ethereum address must be 0x followed by 40 hexadecimal characters");
|
|
35
|
+
if (!hasValidEip55Checksum(raw)) return err("INVALID_CHECKSUM", "Ethereum address fails EIP-55 checksum validation");
|
|
36
|
+
return {
|
|
37
|
+
valid: true,
|
|
38
|
+
compact: v
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
const format = (value) => compact(value);
|
|
42
|
+
const eth = {
|
|
43
|
+
scope: "global",
|
|
44
|
+
name: "Ethereum Address",
|
|
45
|
+
localName: "Ethereum Address",
|
|
46
|
+
abbreviation: "ETH",
|
|
47
|
+
aliases: [
|
|
48
|
+
"Ethereum address",
|
|
49
|
+
"EVM address",
|
|
50
|
+
"crypto wallet"
|
|
51
|
+
],
|
|
52
|
+
candidatePattern: "0x[0-9A-Fa-f]{40}",
|
|
53
|
+
entityType: "any",
|
|
54
|
+
examples: ["0xde709f2102306220921060314715629080e2fb77"],
|
|
55
|
+
lengths: [ETH_ADDRESS_LENGTH],
|
|
56
|
+
compact,
|
|
57
|
+
format,
|
|
58
|
+
validate
|
|
59
|
+
};
|
|
60
|
+
//#endregion
|
|
61
|
+
export { compact, eth as default, format, hasValidEip55Checksum, validate };
|
|
62
|
+
|
|
63
|
+
//# sourceMappingURL=eth.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eth.js","names":[],"sources":["../../src/crypto/eth.ts"],"sourcesContent":["/**\n * Ethereum address.\n *\n * Validates the canonical 0x-prefixed 20-byte\n * hexadecimal address shape. The compact form is\n * lowercased so checksummed and non-checksummed\n * variants of the same address compare equal.\n */\n\nimport type {\n GlobalValidator,\n ValidateResult,\n} from \"../types\";\n\nimport { keccak256 } from \"#checksums/keccak\";\nimport { clean } from \"#util/clean\";\nimport { err } from \"#util/result\";\n\nconst ETH_ADDRESS_LENGTH = 42;\nconst HEX_PREFIX_LENGTH = 2;\n\nconst compact = (value: string): string =>\n clean(value, \" \").toLowerCase();\n\nconst asciiBytes = (value: string): Uint8Array => {\n const bytes = new Uint8Array(value.length);\n for (let i = 0; i < value.length; i += 1) {\n bytes[i] = value.charCodeAt(i);\n }\n return bytes;\n};\n\nconst hexBytes = (bytes: Uint8Array): string => {\n let result = \"\";\n for (const byte of bytes) {\n result += byte.toString(16).padStart(2, \"0\");\n }\n return result;\n};\n\nconst isMixedCase = (value: string): boolean =>\n /[a-f]/.test(value) && /[A-F]/.test(value);\n\nconst hasValidEip55Checksum = (\n address: string,\n): boolean => {\n const body = address.slice(HEX_PREFIX_LENGTH);\n if (!isMixedCase(body)) return true;\n\n const lower = body.toLowerCase();\n const hash = hexBytes(keccak256(asciiBytes(lower)));\n\n for (let i = 0; i < body.length; i += 1) {\n const ch = body.charAt(i);\n if (/\\d/.test(ch)) continue;\n\n const nibble = Number.parseInt(hash.charAt(i), 16);\n const expected =\n nibble >= 8 ? ch.toUpperCase() : ch.toLowerCase();\n if (ch !== expected) return false;\n }\n\n return true;\n};\n\nconst validate = (value: string): ValidateResult => {\n const raw = clean(value, \" \");\n const v = raw.toLowerCase();\n if (v.length !== ETH_ADDRESS_LENGTH) {\n return err(\n \"INVALID_LENGTH\",\n \"Ethereum address must be 42 characters\",\n );\n }\n if (!/^0x[0-9a-f]{40}$/i.test(raw)) {\n return err(\n \"INVALID_FORMAT\",\n \"Ethereum address must be 0x followed by 40 hexadecimal characters\",\n );\n }\n if (!hasValidEip55Checksum(raw)) {\n return err(\n \"INVALID_CHECKSUM\",\n \"Ethereum address fails EIP-55 checksum validation\",\n );\n }\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\nconst eth: GlobalValidator = {\n scope: \"global\",\n name: \"Ethereum Address\",\n localName: \"Ethereum Address\",\n abbreviation: \"ETH\",\n aliases: [\n \"Ethereum address\",\n \"EVM address\",\n \"crypto wallet\",\n ] as const,\n candidatePattern: \"0x[0-9A-Fa-f]{40}\",\n entityType: \"any\",\n examples: [\n \"0xde709f2102306220921060314715629080e2fb77\",\n ] as const,\n lengths: [ETH_ADDRESS_LENGTH] as const,\n compact,\n format,\n validate,\n};\n\nexport default eth;\nexport { compact, format, hasValidEip55Checksum, validate };\n"],"mappings":";;;;AAkBA,MAAM,qBAAqB;AAC3B,MAAM,oBAAoB;AAE1B,MAAM,WAAW,UACf,MAAM,OAAO,GAAG,CAAC,CAAC,YAAY;AAEhC,MAAM,cAAc,UAA8B;CAChD,MAAM,QAAQ,IAAI,WAAW,MAAM,MAAM;CACzC,KAAK,IAAI,IAAI,GAAG,IAAI,MAAM,QAAQ,KAAK,GACrC,MAAM,KAAK,MAAM,WAAW,CAAC;CAE/B,OAAO;AACT;AAEA,MAAM,YAAY,UAA8B;CAC9C,IAAI,SAAS;CACb,KAAK,MAAM,QAAQ,OACjB,UAAU,KAAK,SAAS,EAAE,CAAC,CAAC,SAAS,GAAG,GAAG;CAE7C,OAAO;AACT;AAEA,MAAM,eAAe,UACnB,QAAQ,KAAK,KAAK,KAAK,QAAQ,KAAK,KAAK;AAE3C,MAAM,yBACJ,YACY;CACZ,MAAM,OAAO,QAAQ,MAAM,iBAAiB;CAC5C,IAAI,CAAC,YAAY,IAAI,GAAG,OAAO;CAG/B,MAAM,OAAO,SAAS,UAAU,WADlB,KAAK,YAC4B,CAAC,CAAC,CAAC;CAElD,KAAK,IAAI,IAAI,GAAG,IAAI,KAAK,QAAQ,KAAK,GAAG;EACvC,MAAM,KAAK,KAAK,OAAO,CAAC;EACxB,IAAI,KAAK,KAAK,EAAE,GAAG;EAKnB,IAAI,QAHW,OAAO,SAAS,KAAK,OAAO,CAAC,GAAG,EAExC,KAAK,IAAI,GAAG,YAAY,IAAI,GAAG,YAAY,IAC7B,OAAO;CAC9B;CAEA,OAAO;AACT;AAEA,MAAM,YAAY,UAAkC;CAClD,MAAM,MAAM,MAAM,OAAO,GAAG;CAC5B,MAAM,IAAI,IAAI,YAAY;CAC1B,IAAI,EAAE,WAAW,oBACf,OAAO,IACL,kBACA,wCACF;CAEF,IAAI,CAAC,oBAAoB,KAAK,GAAG,GAC/B,OAAO,IACL,kBACA,mEACF;CAEF,IAAI,CAAC,sBAAsB,GAAG,GAC5B,OAAO,IACL,oBACA,mDACF;CAEF,OAAO;EAAE,OAAO;EAAM,SAAS;CAAE;AACnC;AAEA,MAAM,UAAU,UAA0B,QAAQ,KAAK;AAEvD,MAAM,MAAuB;CAC3B,OAAO;CACP,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EACP;EACA;EACA;CACF;CACA,kBAAkB;CAClB,YAAY;CACZ,UAAU,CACR,4CACF;CACA,SAAS,CAAC,kBAAkB;CAC5B;CACA;CACA;AACF"}
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import btcBase58 from "./btcbase58.js";
|
|
2
|
+
import btcBech32 from "./btcbech32.js";
|
|
3
|
+
import eth from "./eth.js";
|
|
4
|
+
import wallet from "./wallet.js";
|
|
5
|
+
|
|
6
|
+
//#region src/crypto/mod.d.ts
|
|
7
|
+
declare namespace mod_d_exports {
|
|
8
|
+
export { btcBase58 as btcbase58, btcBech32 as btcbech32, eth, wallet };
|
|
9
|
+
}
|
|
10
|
+
//#endregion
|
|
11
|
+
export { btcBase58 as btcbase58, btcBech32 as btcbech32, eth, mod_d_exports, wallet };
|
|
12
|
+
//# sourceMappingURL=mod.d.ts.map
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { __exportAll } from "../_virtual/_rolldown/runtime.js";
|
|
2
|
+
import btcBase58 from "./btcbase58.js";
|
|
3
|
+
import btcBech32 from "./btcbech32.js";
|
|
4
|
+
import eth from "./eth.js";
|
|
5
|
+
import wallet from "./wallet.js";
|
|
6
|
+
//#region src/crypto/mod.ts
|
|
7
|
+
var crypto_exports = /* @__PURE__ */ __exportAll({
|
|
8
|
+
btcbase58: () => btcBase58,
|
|
9
|
+
btcbech32: () => btcBech32,
|
|
10
|
+
eth: () => eth,
|
|
11
|
+
wallet: () => wallet
|
|
12
|
+
});
|
|
13
|
+
//#endregion
|
|
14
|
+
export { btcBase58 as btcbase58, btcBech32 as btcbech32, crypto_exports, eth, wallet };
|
|
15
|
+
|
|
16
|
+
//# sourceMappingURL=mod.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"mod.js","names":[],"sources":["../../src/crypto/mod.ts"],"sourcesContent":["export { default as btcbase58 } from \"./btcbase58\";\nexport { default as btcbech32 } from \"./btcbech32\";\nexport { default as eth } from \"./eth\";\nexport { default as wallet } from \"./wallet\";\n"],"mappings":""}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { GlobalValidator, ValidateResult } from "../types.js";
|
|
2
|
+
|
|
3
|
+
//#region src/crypto/wallet.d.ts
|
|
4
|
+
declare const compact: (value: string) => string;
|
|
5
|
+
declare const validate: (value: string) => ValidateResult;
|
|
6
|
+
declare const format: (value: string) => string;
|
|
7
|
+
declare const wallet: GlobalValidator;
|
|
8
|
+
//#endregion
|
|
9
|
+
export { compact, wallet as default, format, validate };
|
|
10
|
+
//# sourceMappingURL=wallet.d.ts.map
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import btcBase58 from "./btcbase58.js";
|
|
2
|
+
import btcBech32 from "./btcbech32.js";
|
|
3
|
+
import eth from "./eth.js";
|
|
4
|
+
//#region src/crypto/wallet.ts
|
|
5
|
+
const validators = [
|
|
6
|
+
eth,
|
|
7
|
+
btcBech32,
|
|
8
|
+
btcBase58
|
|
9
|
+
];
|
|
10
|
+
const compact = (value) => {
|
|
11
|
+
for (const validator of validators) {
|
|
12
|
+
const result = validator.validate(value);
|
|
13
|
+
if (result.valid) return result.compact;
|
|
14
|
+
}
|
|
15
|
+
return value.trim();
|
|
16
|
+
};
|
|
17
|
+
const validate = (value) => {
|
|
18
|
+
for (const validator of validators) {
|
|
19
|
+
const result = validator.validate(value);
|
|
20
|
+
if (result.valid) return result;
|
|
21
|
+
}
|
|
22
|
+
return {
|
|
23
|
+
valid: false,
|
|
24
|
+
error: {
|
|
25
|
+
code: "INVALID_FORMAT",
|
|
26
|
+
message: "Unsupported cryptocurrency wallet address"
|
|
27
|
+
}
|
|
28
|
+
};
|
|
29
|
+
};
|
|
30
|
+
const format = (value) => compact(value);
|
|
31
|
+
const wallet = {
|
|
32
|
+
scope: "global",
|
|
33
|
+
name: "Cryptocurrency Wallet Address",
|
|
34
|
+
localName: "Cryptocurrency Wallet Address",
|
|
35
|
+
abbreviation: "crypto",
|
|
36
|
+
aliases: [
|
|
37
|
+
"crypto address",
|
|
38
|
+
"wallet address",
|
|
39
|
+
"cryptocurrency wallet"
|
|
40
|
+
],
|
|
41
|
+
candidatePattern: "(?:0x[0-9A-Fa-f]{40}|[13][1-9A-HJ-NP-Za-km-z]{25,34}|bc1[ac-hj-np-z02-9]{11,71}|BC1[AC-HJ-NP-Z02-9]{11,71})",
|
|
42
|
+
entityType: "any",
|
|
43
|
+
examples: [
|
|
44
|
+
"0xde709f2102306220921060314715629080e2fb77",
|
|
45
|
+
"1BoatSLRHtKNngkdXEeobR76b53LETtpyT",
|
|
46
|
+
"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4"
|
|
47
|
+
],
|
|
48
|
+
compact,
|
|
49
|
+
format,
|
|
50
|
+
validate
|
|
51
|
+
};
|
|
52
|
+
//#endregion
|
|
53
|
+
export { compact, wallet as default, format, validate };
|
|
54
|
+
|
|
55
|
+
//# sourceMappingURL=wallet.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"wallet.js","names":[],"sources":["../../src/crypto/wallet.ts"],"sourcesContent":["/**\n * Cryptocurrency wallet address.\n *\n * Convenience validator for supported wallet address\n * families. Use the specific validators when the\n * network or format must be known.\n */\n\nimport type {\n GlobalValidator,\n ValidateResult,\n} from \"../types\";\nimport btcBase58 from \"./btcbase58\";\nimport btcBech32 from \"./btcbech32\";\nimport eth from \"./eth\";\n\nconst validators = [eth, btcBech32, btcBase58] as const;\n\nconst compact = (value: string): string => {\n for (const validator of validators) {\n const result = validator.validate(value);\n if (result.valid) return result.compact;\n }\n return value.trim();\n};\n\nconst validate = (value: string): ValidateResult => {\n for (const validator of validators) {\n const result = validator.validate(value);\n if (result.valid) return result;\n }\n return {\n valid: false,\n error: {\n code: \"INVALID_FORMAT\",\n message: \"Unsupported cryptocurrency wallet address\",\n },\n };\n};\n\nconst format = (value: string): string => compact(value);\n\nconst wallet: GlobalValidator = {\n scope: \"global\",\n name: \"Cryptocurrency Wallet Address\",\n localName: \"Cryptocurrency Wallet Address\",\n abbreviation: \"crypto\",\n aliases: [\n \"crypto address\",\n \"wallet address\",\n \"cryptocurrency wallet\",\n ] as const,\n candidatePattern:\n \"(?:0x[0-9A-Fa-f]{40}|[13][1-9A-HJ-NP-Za-km-z]{25,34}|bc1[ac-hj-np-z02-9]{11,71}|BC1[AC-HJ-NP-Z02-9]{11,71})\",\n entityType: \"any\",\n examples: [\n \"0xde709f2102306220921060314715629080e2fb77\",\n \"1BoatSLRHtKNngkdXEeobR76b53LETtpyT\",\n \"bc1qw508d6qejxtdg4y5r3zarvary0c5xw7kv8f3t4\",\n ] as const,\n compact,\n format,\n validate,\n};\n\nexport default wallet;\nexport { compact, format, validate };\n"],"mappings":";;;;AAgBA,MAAM,aAAa;CAAC;CAAK;CAAW;AAAS;AAE7C,MAAM,WAAW,UAA0B;CACzC,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,SAAS,UAAU,SAAS,KAAK;EACvC,IAAI,OAAO,OAAO,OAAO,OAAO;CAClC;CACA,OAAO,MAAM,KAAK;AACpB;AAEA,MAAM,YAAY,UAAkC;CAClD,KAAK,MAAM,aAAa,YAAY;EAClC,MAAM,SAAS,UAAU,SAAS,KAAK;EACvC,IAAI,OAAO,OAAO,OAAO;CAC3B;CACA,OAAO;EACL,OAAO;EACP,OAAO;GACL,MAAM;GACN,SAAS;EACX;CACF;AACF;AAEA,MAAM,UAAU,UAA0B,QAAQ,KAAK;AAEvD,MAAM,SAA0B;CAC9B,OAAO;CACP,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EACP;EACA;EACA;CACF;CACA,kBACE;CACF,YAAY;CACZ,UAAU;EACR;EACA;EACA;CACF;CACA;CACA;CACA;AACF"}
|
package/dist/cu/ni.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ni.js","names":[],"sources":["../../src/cu/ni.ts"],"sourcesContent":["/**\n * NI (Número de Identidad, Cuban identity card number).\n *\n * 11 digits encoding birth date, century, sequence, and\n * gender. Structure: YYMMDDXNNNN where:\n * - YYMMDD = birth date\n * - X = century digit (9=1800s, 0-5=1900s, 6-8=2000s)\n * - NNNN = sequence (digit at position 9 encodes gender:\n * even=male, odd=female)\n *\n * No check digit; validation is by date and format only.\n *\n * Ported from python-stdnum cu.ni module.\n *\n * @see https://www.ecured.cu/Carnet_de_Identidad\n */\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\nconst compact = (value: string): string =>\n clean(value, \" -\").trim();\n\n/**\n * Derive the full birth year from the two-digit year\n * and the century indicator at position 6.\n */\nconst centuryOffset = (digit: string): number => {\n if (digit === \"9\") return 1800;\n if (digit >= \"0\" && digit <= \"5\") return 1900;\n return 2000;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 11) {\n return err(\n \"INVALID_LENGTH\",\n \"Cuban NI must be 11 digits\",\n );\n }\n\n if (!/^\\d{11}$/.test(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Cuban NI must contain only digits\",\n );\n }\n\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n // SAFETY: regex check above guarantees v.length === 11.\n // eslint-disable-next-line no-non-null-assertion\n const year = centuryOffset(v[6]!) + yy;\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Cuban NI contains an invalid birth date\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Extract birth date and gender from a Cuban NI.\n * Returns null if the value is not valid.\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n // SAFETY: regex check above guarantees v.length === 11.\n // eslint-disable-next-line no-non-null-assertion\n const year = centuryOffset(v[6]!) + yy;\n\n const genderDigit = Number(v[9]);\n const gender: \"male\" | \"female\" =\n genderDigit % 2 === 0 ? \"male\" : \"female\";\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n gender,\n };\n};\n\n/** Generate a random valid Cuban NI. */\nconst generate = (): string => {\n const year = randomInt(1950, 2024);\n const month = String(randomInt(1, 12)).padStart(2, \"0\");\n const day = String(randomInt(1, 28)).padStart(2, \"0\");\n const yy = String(year % 100).padStart(2, \"0\");\n const century =\n year >= 2000\n ? String(randomInt(6, 8))\n : String(randomInt(0, 5));\n return `${yy}${month}${day}${century}${randomDigits(4)}`;\n};\n\n/**\n * Cuban identity card number (NI).\n *\n * Examples sourced from python-stdnum test suite.\n */\nconst ni: CountryValidator<\"CU\", ParsedPersonId> = {\n name: \"Cuban Identity Card Number\",\n localName: \"Número de Identidad\",\n abbreviation: \"NI\",\n aliases: [\n \"NI\",\n \"número de identidad\",\n \"carnet de identidad\",\n ] as const,\n candidatePattern: \"\\\\d{11}\",\n scope: \"country\",\n country: \"CU\",\n entityType: \"person\",\n lengths: [11] as const,\n examples: [\"91021027775\", \"72062506561\"] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n sourceUrl: \"https://www.ecured.cu/Carnet_de_Identidad\",\n};\n\nexport default ni;\nexport { compact, format, generate, parse, validate };\n"],"mappings":";;;;;AA4BA,MAAM,WAAW,UACf,MAAM,OAAO,IAAI,
|
|
1
|
+
{"version":3,"file":"ni.js","names":[],"sources":["../../src/cu/ni.ts"],"sourcesContent":["/**\n * NI (Número de Identidad, Cuban identity card number).\n *\n * 11 digits encoding birth date, century, sequence, and\n * gender. Structure: YYMMDDXNNNN where:\n * - YYMMDD = birth date\n * - X = century digit (9=1800s, 0-5=1900s, 6-8=2000s)\n * - NNNN = sequence (digit at position 9 encodes gender:\n * even=male, odd=female)\n *\n * No check digit; validation is by date and format only.\n *\n * Ported from python-stdnum cu.ni module.\n *\n * @see https://www.ecured.cu/Carnet_de_Identidad\n */\n\nimport type {\n ParsedPersonId,\n ValidateResult,\n CountryValidator,\n} from \"../types\";\n\nimport { clean } from \"#util/clean\";\nimport { isValidDate } from \"#util/date\";\nimport { randomDigits, randomInt } from \"#util/generate\";\nimport { err } from \"#util/result\";\n\nconst compact = (value: string): string =>\n clean(value, \" -\").trim();\n\n/**\n * Derive the full birth year from the two-digit year\n * and the century indicator at position 6.\n */\nconst centuryOffset = (digit: string): number => {\n if (digit === \"9\") return 1800;\n if (digit >= \"0\" && digit <= \"5\") return 1900;\n return 2000;\n};\n\nconst validate = (value: string): ValidateResult => {\n const v = compact(value);\n\n if (v.length !== 11) {\n return err(\n \"INVALID_LENGTH\",\n \"Cuban NI must be 11 digits\",\n );\n }\n\n if (!/^\\d{11}$/.test(v)) {\n return err(\n \"INVALID_FORMAT\",\n \"Cuban NI must contain only digits\",\n );\n }\n\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n // SAFETY: regex check above guarantees v.length === 11.\n // eslint-disable-next-line no-non-null-assertion\n const year = centuryOffset(v[6]!) + yy;\n\n if (!isValidDate(year, mm, dd)) {\n return err(\n \"INVALID_COMPONENT\",\n \"Cuban NI contains an invalid birth date\",\n );\n }\n\n return { valid: true, compact: v };\n};\n\nconst format = (value: string): string => compact(value);\n\n/**\n * Extract birth date and gender from a Cuban NI.\n * Returns null if the value is not valid.\n */\nconst parse = (value: string): ParsedPersonId | null => {\n const result = validate(value);\n if (!result.valid) return null;\n\n const v = result.compact;\n const yy = Number(v.slice(0, 2));\n const mm = Number(v.slice(2, 4));\n const dd = Number(v.slice(4, 6));\n // SAFETY: regex check above guarantees v.length === 11.\n // eslint-disable-next-line no-non-null-assertion\n const year = centuryOffset(v[6]!) + yy;\n\n const genderDigit = Number(v[9]);\n const gender: \"male\" | \"female\" =\n genderDigit % 2 === 0 ? \"male\" : \"female\";\n\n return {\n birthDate: new Date(year, mm - 1, dd),\n gender,\n };\n};\n\n/** Generate a random valid Cuban NI. */\nconst generate = (): string => {\n const year = randomInt(1950, 2024);\n const month = String(randomInt(1, 12)).padStart(2, \"0\");\n const day = String(randomInt(1, 28)).padStart(2, \"0\");\n const yy = String(year % 100).padStart(2, \"0\");\n const century =\n year >= 2000\n ? String(randomInt(6, 8))\n : String(randomInt(0, 5));\n return `${yy}${month}${day}${century}${randomDigits(4)}`;\n};\n\n/**\n * Cuban identity card number (NI).\n *\n * Examples sourced from python-stdnum test suite.\n */\nconst ni: CountryValidator<\"CU\", ParsedPersonId> = {\n name: \"Cuban Identity Card Number\",\n localName: \"Número de Identidad\",\n abbreviation: \"NI\",\n aliases: [\n \"NI\",\n \"número de identidad\",\n \"carnet de identidad\",\n ] as const,\n candidatePattern: \"\\\\d{11}\",\n scope: \"country\",\n country: \"CU\",\n entityType: \"person\",\n lengths: [11] as const,\n examples: [\"91021027775\", \"72062506561\"] as const,\n compact,\n format,\n parse,\n validate,\n generate,\n sourceUrl: \"https://www.ecured.cu/Carnet_de_Identidad\",\n};\n\nexport default ni;\nexport { compact, format, generate, parse, validate };\n"],"mappings":";;;;;AA4BA,MAAM,WAAW,UACf,MAAM,OAAO,IAAI,CAAC,CAAC,KAAK;;;;;AAM1B,MAAM,iBAAiB,UAA0B;CAC/C,IAAI,UAAU,KAAK,OAAO;CAC1B,IAAI,SAAS,OAAO,SAAS,KAAK,OAAO;CACzC,OAAO;AACT;AAEA,MAAM,YAAY,UAAkC;CAClD,MAAM,IAAI,QAAQ,KAAK;CAEvB,IAAI,EAAE,WAAW,IACf,OAAO,IACL,kBACA,4BACF;CAGF,IAAI,CAAC,WAAW,KAAK,CAAC,GACpB,OAAO,IACL,kBACA,mCACF;CAGF,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC;CAC/B,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC;CAC/B,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC;CAK/B,IAAI,CAAC,YAFQ,cAAc,EAAE,EAAG,IAAI,IAEb,IAAI,EAAE,GAC3B,OAAO,IACL,qBACA,yCACF;CAGF,OAAO;EAAE,OAAO;EAAM,SAAS;CAAE;AACnC;AAEA,MAAM,UAAU,UAA0B,QAAQ,KAAK;;;;;AAMvD,MAAM,SAAS,UAAyC;CACtD,MAAM,SAAS,SAAS,KAAK;CAC7B,IAAI,CAAC,OAAO,OAAO,OAAO;CAE1B,MAAM,IAAI,OAAO;CACjB,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC;CAC/B,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC;CAC/B,MAAM,KAAK,OAAO,EAAE,MAAM,GAAG,CAAC,CAAC;CAG/B,MAAM,OAAO,cAAc,EAAE,EAAG,IAAI;CAGpC,MAAM,SADc,OAAO,EAAE,EAEjB,IAAI,MAAM,IAAI,SAAS;CAEnC,OAAO;EACL,WAAW,IAAI,KAAK,MAAM,KAAK,GAAG,EAAE;EACpC;CACF;AACF;;AAGA,MAAM,iBAAyB;CAC7B,MAAM,OAAO,UAAU,MAAM,IAAI;CACjC,MAAM,QAAQ,OAAO,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CACtD,MAAM,MAAM,OAAO,UAAU,GAAG,EAAE,CAAC,CAAC,CAAC,SAAS,GAAG,GAAG;CAMpD,OAAO,GALI,OAAO,OAAO,GAAG,CAAC,CAAC,SAAS,GAAG,GAK/B,IAAI,QAAQ,MAHrB,QAAQ,MACJ,OAAO,UAAU,GAAG,CAAC,CAAC,IACtB,OAAO,UAAU,GAAG,CAAC,CAAC,IACW,aAAa,CAAC;AACvD;;;;;;AAOA,MAAM,KAA6C;CACjD,MAAM;CACN,WAAW;CACX,cAAc;CACd,SAAS;EACP;EACA;EACA;CACF;CACA,kBAAkB;CAClB,OAAO;CACP,SAAS;CACT,YAAY;CACZ,SAAS,CAAC,EAAE;CACZ,UAAU,CAAC,eAAe,aAAa;CACvC;CACA;CACA;CACA;CACA;CACA,WAAW;AACb"}
|