soff-id 0.1.1 → 0.2.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.
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/core/utils.ts","../../src/locales/mx.ts"],"names":["cleanDocument","value","RFC_PATTERN_FISICA","RFC_PATTERN_MORAL","CURP_PATTERN","validateRFC","rfc","cleaned","formatRFC","cleanRFC","validateCURP","curp","validateCURPCheckDigit","dictionary","sum","i","char","formatCURP","cleanCURP","getCURPGender","genderChar","getCURPBirthDate","yearPart","month","day","centuryChar","year","date"],"mappings":"AAGO,SAASA,CAAAA,CAAcC,CAAAA,CAAuB,CACnD,OAAOA,EAAM,OAAA,CAAQ,eAAA,CAAiB,EAAE,CAC1C,CCEA,IAAMC,CAAAA,CAAqB,8BAAA,CACrBC,EAAoB,8BAAA,CAMpBC,CAAAA,CAAe,uCAAA,CAMd,SAASC,CAAAA,CAAYC,CAAAA,CAAsB,CAChD,IAAMC,EAAUP,CAAAA,CAAcM,CAAG,CAAA,CAAE,WAAA,EAAY,CAE/C,OAAIC,CAAAA,CAAQ,MAAA,GAAW,IAAMA,CAAAA,CAAQ,MAAA,GAAW,EAAA,CACvC,KAAA,CAILA,CAAAA,CAAQ,MAAA,GAAW,EAAA,CACdL,CAAAA,CAAmB,KAAKK,CAAO,CAAA,CAGjCJ,CAAAA,CAAkB,IAAA,CAAKI,CAAO,CACvC,CAKO,SAASC,EAAUF,CAAAA,CAAqB,CAC7C,OAAON,CAAAA,CAAcM,CAAG,CAAA,CAAE,WAAA,EAC5B,CAKO,SAASG,CAAAA,CAASH,CAAAA,CAAqB,CAC5C,OAAON,CAAAA,CAAcM,CAAG,CAAA,CAAE,WAAA,EAC5B,CAMO,SAASI,CAAAA,CAAaC,CAAAA,CAAuB,CAClD,IAAMJ,CAAAA,CAAUP,CAAAA,CAAcW,CAAI,CAAA,CAAE,WAAA,EAAY,CAMhD,OAJIJ,CAAAA,CAAQ,MAAA,GAAW,EAAA,EAInB,CAACH,EAAa,IAAA,CAAKG,CAAO,CAAA,CACrB,KAAA,CAIFK,CAAAA,CAAuBL,CAAO,CACvC,CAKA,SAASK,CAAAA,CAAuBD,CAAAA,CAAuB,CACrD,IAAME,CAAAA,CAAa,0CAAA,CACfC,CAAAA,CAAM,CAAA,CAEV,QAASC,CAAAA,CAAI,CAAA,CAAGA,CAAAA,CAAI,EAAA,CAAIA,CAAAA,EAAAA,CAAK,CAC3B,IAAMC,CAAAA,CAAOL,EAAKI,CAAC,CAAA,CACbd,CAAAA,CAAQY,CAAAA,CAAW,OAAA,CAAQG,CAAI,CAAA,CACrC,GAAIf,IAAU,EAAA,CAAI,OAAO,MAAA,CACzBa,CAAAA,EAAOb,CAAAA,EAAS,EAAA,CAAKc,CAAAA,EACvB,CAGA,QADoB,EAAA,CAAMD,CAAAA,CAAM,EAAA,EAAO,EAAA,GACjB,SAASH,CAAAA,CAAK,EAAE,CAAA,CAAG,EAAE,CAC7C,CAKO,SAASM,CAAAA,CAAWN,CAAAA,CAAsB,CAC/C,OAAOX,CAAAA,CAAcW,CAAI,EAAE,WAAA,EAC7B,CAKO,SAASO,CAAAA,CAAUP,CAAAA,CAAsB,CAC9C,OAAOX,EAAcW,CAAI,CAAA,CAAE,WAAA,EAC7B,CAMO,SAASQ,CAAAA,CAAcR,CAAAA,CAAgC,CAC5D,IAAMJ,CAAAA,CAAUP,CAAAA,CAAcW,CAAI,CAAA,CAAE,WAAA,EAAY,CAChD,GAAIJ,EAAQ,MAAA,CAAS,EAAA,CAAI,OAAO,IAAA,CAEhC,IAAMa,CAAAA,CAAab,CAAAA,CAAQ,EAAE,EAC7B,OAAIa,CAAAA,GAAe,GAAA,CAAY,GAAA,CAC3BA,CAAAA,GAAe,GAAA,CAAY,GAAA,CACxB,IACT,CAMO,SAASC,CAAAA,CAAiBV,CAAAA,CAA2B,CAC1D,IAAMJ,CAAAA,CAAUP,CAAAA,CAAcW,CAAI,EAAE,WAAA,EAAY,CAChD,GAAIJ,CAAAA,CAAQ,OAAS,EAAA,CAAI,OAAO,IAAA,CAEhC,IAAMe,EAAWf,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,CAAC,CAAA,CAC7BgB,CAAAA,CAAQhB,CAAAA,CAAQ,KAAA,CAAM,EAAG,CAAC,CAAA,CAC1BiB,CAAAA,CAAMjB,CAAAA,CAAQ,KAAA,CAAM,CAAA,CAAG,EAAE,CAAA,CAIzBkB,EAAclB,CAAAA,CAAQ,EAAE,CAAA,CAC1BmB,CAAAA,CAEA,IAAA,CAAK,IAAA,CAAKD,CAAW,CAAA,CACvBC,EAAO,IAAA,CAAO,QAAA,CAASJ,CAAAA,CAAU,EAAE,EAEnCI,CAAAA,CAAO,GAAA,CAAO,QAAA,CAASJ,CAAAA,CAAU,EAAE,CAAA,CAGrC,IAAMK,CAAAA,CAAO,IAAI,IAAA,CAAKD,CAAAA,CAAM,QAAA,CAASH,CAAAA,CAAO,EAAE,CAAA,CAAI,CAAA,CAAG,QAAA,CAASC,CAAAA,CAAK,EAAE,CAAC,CAAA,CAGtE,OACEG,EAAK,WAAA,EAAY,GAAMD,CAAAA,EACvBC,CAAAA,CAAK,QAAA,EAAS,GAAM,QAAA,CAASJ,CAAAA,CAAO,EAAE,CAAA,CAAI,CAAA,EAC1CI,CAAAA,CAAK,OAAA,KAAc,QAAA,CAASH,CAAAA,CAAK,EAAE,CAAA,CAE5B,KAGFG,CACT","file":"mx.js","sourcesContent":["/**\n * Remove all non-alphanumeric characters\n */\nexport function cleanDocument(value: string): string {\n return value.replace(/[^a-zA-Z0-9]/g, '');\n}\n\n/**\n * Remove all non-digit characters\n */\nexport function cleanDigits(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/**\n * Pad a string with leading zeros\n */\nexport function padStart(value: string, length: number): string {\n return value.padStart(length, '0');\n}\n\n/**\n * Calculate modulo 11 check digit (common algorithm)\n */\nexport function mod11(digits: string, weights: number[]): number {\n let sum = 0;\n for (let i = 0; i < digits.length; i++) {\n sum += parseInt(digits[i], 10) * weights[i % weights.length];\n }\n const remainder = sum % 11;\n return remainder;\n}\n\n/**\n * Calculate modulo 10 check digit (Luhn-like algorithms)\n */\nexport function mod10(digits: string, weights: number[]): number {\n let sum = 0;\n for (let i = 0; i < digits.length; i++) {\n let product = parseInt(digits[i], 10) * weights[i % weights.length];\n if (product > 9) {\n product = Math.floor(product / 10) + (product % 10);\n }\n sum += product;\n }\n return sum % 10;\n}\n","import { cleanDocument } from '../core/utils.js';\n\n/**\n * Valid RFC patterns\n * - Persona física: 4 letters + 6 digits + 3 alphanumeric (homoclave)\n * - Persona moral: 3 letters + 6 digits + 3 alphanumeric (homoclave)\n */\nconst RFC_PATTERN_FISICA = /^[A-ZÑ&]{4}\\d{6}[A-Z0-9]{3}$/;\nconst RFC_PATTERN_MORAL = /^[A-ZÑ&]{3}\\d{6}[A-Z0-9]{3}$/;\n\n/**\n * CURP pattern\n * 18 characters: 4 letters + 6 digits + 6 letters + 2 alphanumeric\n */\nconst CURP_PATTERN = /^[A-Z]{4}\\d{6}[HM][A-Z]{5}[A-Z0-9]\\d$/;\n\n/**\n * Validate a Mexican RFC (Registro Federal de Contribuyentes)\n * @param rfc - The RFC to validate\n */\nexport function validateRFC(rfc: string): boolean {\n const cleaned = cleanDocument(rfc).toUpperCase();\n\n if (cleaned.length !== 12 && cleaned.length !== 13) {\n return false;\n }\n\n // 12 chars = Persona moral, 13 chars = Persona física\n if (cleaned.length === 13) {\n return RFC_PATTERN_FISICA.test(cleaned);\n }\n\n return RFC_PATTERN_MORAL.test(cleaned);\n}\n\n/**\n * Format an RFC for display (uppercase, no separators)\n */\nexport function formatRFC(rfc: string): string {\n return cleanDocument(rfc).toUpperCase();\n}\n\n/**\n * Clean an RFC\n */\nexport function cleanRFC(rfc: string): string {\n return cleanDocument(rfc).toUpperCase();\n}\n\n/**\n * Validate a Mexican CURP (Clave Única de Registro de Población)\n * @param curp - The CURP to validate (18 characters)\n */\nexport function validateCURP(curp: string): boolean {\n const cleaned = cleanDocument(curp).toUpperCase();\n\n if (cleaned.length !== 18) {\n return false;\n }\n\n if (!CURP_PATTERN.test(cleaned)) {\n return false;\n }\n\n // Validate check digit\n return validateCURPCheckDigit(cleaned);\n}\n\n/**\n * CURP check digit validation\n */\nfunction validateCURPCheckDigit(curp: string): boolean {\n const dictionary = '0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ';\n let sum = 0;\n\n for (let i = 0; i < 17; i++) {\n const char = curp[i];\n const value = dictionary.indexOf(char);\n if (value === -1) return false;\n sum += value * (18 - i);\n }\n\n const checkDigit = (10 - (sum % 10)) % 10;\n return checkDigit === parseInt(curp[17], 10);\n}\n\n/**\n * Format a CURP for display\n */\nexport function formatCURP(curp: string): string {\n return cleanDocument(curp).toUpperCase();\n}\n\n/**\n * Clean a CURP\n */\nexport function cleanCURP(curp: string): string {\n return cleanDocument(curp).toUpperCase();\n}\n\n/**\n * Get the gender from a CURP\n * @returns 'M' for male, 'F' for female, or null if invalid\n */\nexport function getCURPGender(curp: string): 'M' | 'F' | null {\n const cleaned = cleanDocument(curp).toUpperCase();\n if (cleaned.length < 11) return null;\n\n const genderChar = cleaned[10];\n if (genderChar === 'H') return 'M';\n if (genderChar === 'M') return 'F';\n return null;\n}\n\n/**\n * Get the birth date from a CURP\n * @returns Date object or null if invalid\n */\nexport function getCURPBirthDate(curp: string): Date | null {\n const cleaned = cleanDocument(curp).toUpperCase();\n if (cleaned.length < 10) return null;\n\n const yearPart = cleaned.slice(4, 6);\n const month = cleaned.slice(6, 8);\n const day = cleaned.slice(8, 10);\n\n // Determine century based on position 17 (digit for century)\n // 0-9 for people born 1900-1999, A for 2000+\n const centuryChar = cleaned[16];\n let year: number;\n\n if (/\\d/.test(centuryChar)) {\n year = 1900 + parseInt(yearPart, 10);\n } else {\n year = 2000 + parseInt(yearPart, 10);\n }\n\n const date = new Date(year, parseInt(month, 10) - 1, parseInt(day, 10));\n\n // Validate the date is real\n if (\n date.getFullYear() !== year ||\n date.getMonth() !== parseInt(month, 10) - 1 ||\n date.getDate() !== parseInt(day, 10)\n ) {\n return null;\n }\n\n return date;\n}\n"]}
1
+ {"version":3,"sources":["../../src/core/utils.ts","../../src/locales/mx.ts"],"names":["cleanDocument","value","RFC_PATTERN_FISICA","RFC_PATTERN_MORAL","CURP_PATTERN","validateRFC","rfc","cleaned","formatRFC","cleanRFC","validateCURP","curp","validateCURPCheckDigit","dictionary","sum","i","char","formatCURP","cleanCURP","getCURPGender","genderChar","getCURPBirthDate","yearPart","month","day","centuryChar","year","date","CURP_STATE_CODES","getCURPBirthState","stateCode","getCURPStateCode","isRFCCompany","isRFCPerson","getRFCDate","dateStart","dateStr","fullYear","parseCURP","parseRFC"],"mappings":"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","file":"mx.js","sourcesContent":["/**\n * Remove all non-alphanumeric characters\n */\nexport function cleanDocument(value: string): string {\n return value.replace(/[^a-zA-Z0-9]/g, '');\n}\n\n/**\n * Remove all non-digit characters\n */\nexport function cleanDigits(value: string): string {\n return value.replace(/\\D/g, '');\n}\n\n/**\n * Pad a string with leading zeros\n */\nexport function padStart(value: string, length: number): string {\n return value.padStart(length, '0');\n}\n\n/**\n * Calculate modulo 11 check digit (common algorithm)\n */\nexport function mod11(digits: string, weights: number[]): number {\n let sum = 0;\n for (let i = 0; i < digits.length; i++) {\n sum += parseInt(digits[i], 10) * weights[i % weights.length];\n }\n const remainder = sum % 11;\n return remainder;\n}\n\n/**\n * Calculate modulo 10 check digit (Luhn-like algorithms)\n */\nexport function mod10(digits: string, weights: number[]): number {\n let sum = 0;\n for (let i = 0; i < digits.length; i++) {\n let product = parseInt(digits[i], 10) * weights[i % weights.length];\n if (product > 9) {\n product = Math.floor(product / 10) + (product % 10);\n }\n sum += product;\n }\n return sum % 10;\n}\n","import { cleanDocument } from '../core/utils.js';\n\n/**\n * Valid RFC patterns\n * - Persona física: 4 letters + 6 digits + 3 alphanumeric (homoclave)\n * - Persona moral: 3 letters + 6 digits + 3 alphanumeric (homoclave)\n */\nconst RFC_PATTERN_FISICA = /^[A-ZÑ&]{4}\\d{6}[A-Z0-9]{3}$/;\nconst RFC_PATTERN_MORAL = /^[A-ZÑ&]{3}\\d{6}[A-Z0-9]{3}$/;\n\n/**\n * CURP pattern\n * 18 characters: 4 letters + 6 digits + 6 letters + 2 alphanumeric\n */\nconst CURP_PATTERN = /^[A-Z]{4}\\d{6}[HM][A-Z]{5}[A-Z0-9]\\d$/;\n\n/**\n * Validate a Mexican RFC (Registro Federal de Contribuyentes)\n * @param rfc - The RFC to validate\n */\nexport function validateRFC(rfc: string): boolean {\n const cleaned = cleanDocument(rfc).toUpperCase();\n\n if (cleaned.length !== 12 && cleaned.length !== 13) {\n return false;\n }\n\n // 12 chars = Persona moral, 13 chars = Persona física\n if (cleaned.length === 13) {\n return RFC_PATTERN_FISICA.test(cleaned);\n }\n\n return RFC_PATTERN_MORAL.test(cleaned);\n}\n\n/**\n * Format an RFC for display (uppercase, no separators)\n */\nexport function formatRFC(rfc: string): string {\n return cleanDocument(rfc).toUpperCase();\n}\n\n/**\n * Clean an RFC\n */\nexport function cleanRFC(rfc: string): string {\n return cleanDocument(rfc).toUpperCase();\n}\n\n/**\n * Validate a Mexican CURP (Clave Única de Registro de Población)\n * @param curp - The CURP to validate (18 characters)\n */\nexport function validateCURP(curp: string): boolean {\n const cleaned = cleanDocument(curp).toUpperCase();\n\n if (cleaned.length !== 18) {\n return false;\n }\n\n if (!CURP_PATTERN.test(cleaned)) {\n return false;\n }\n\n // Validate check digit\n return validateCURPCheckDigit(cleaned);\n}\n\n/**\n * CURP check digit validation\n */\nfunction validateCURPCheckDigit(curp: string): boolean {\n const dictionary = '0123456789ABCDEFGHIJKLMNÑOPQRSTUVWXYZ';\n let sum = 0;\n\n for (let i = 0; i < 17; i++) {\n const char = curp[i];\n const value = dictionary.indexOf(char);\n if (value === -1) return false;\n sum += value * (18 - i);\n }\n\n const checkDigit = (10 - (sum % 10)) % 10;\n return checkDigit === parseInt(curp[17], 10);\n}\n\n/**\n * Format a CURP for display\n */\nexport function formatCURP(curp: string): string {\n return cleanDocument(curp).toUpperCase();\n}\n\n/**\n * Clean a CURP\n */\nexport function cleanCURP(curp: string): string {\n return cleanDocument(curp).toUpperCase();\n}\n\n/**\n * Get the gender from a CURP\n * @returns 'M' for male, 'F' for female, or null if invalid\n */\nexport function getCURPGender(curp: string): 'M' | 'F' | null {\n const cleaned = cleanDocument(curp).toUpperCase();\n if (cleaned.length < 11) return null;\n\n const genderChar = cleaned[10];\n if (genderChar === 'H') return 'M';\n if (genderChar === 'M') return 'F';\n return null;\n}\n\n/**\n * Get the birth date from a CURP\n * @returns Date object or null if invalid\n */\nexport function getCURPBirthDate(curp: string): Date | null {\n const cleaned = cleanDocument(curp).toUpperCase();\n if (cleaned.length < 10) return null;\n\n const yearPart = cleaned.slice(4, 6);\n const month = cleaned.slice(6, 8);\n const day = cleaned.slice(8, 10);\n\n // Determine century based on position 17 (digit for century)\n // 0-9 for people born 1900-1999, A for 2000+\n const centuryChar = cleaned[16];\n let year: number;\n\n if (/\\d/.test(centuryChar)) {\n year = 1900 + parseInt(yearPart, 10);\n } else {\n year = 2000 + parseInt(yearPart, 10);\n }\n\n const date = new Date(year, parseInt(month, 10) - 1, parseInt(day, 10));\n\n // Validate the date is real\n if (\n date.getFullYear() !== year ||\n date.getMonth() !== parseInt(month, 10) - 1 ||\n date.getDate() !== parseInt(day, 10)\n ) {\n return null;\n }\n\n return date;\n}\n\n/**\n * Mexican state codes used in CURP\n */\nconst CURP_STATE_CODES: Record<string, string> = {\n AS: 'Aguascalientes',\n BC: 'Baja California',\n BS: 'Baja California Sur',\n CC: 'Campeche',\n CL: 'Coahuila',\n CM: 'Colima',\n CS: 'Chiapas',\n CH: 'Chihuahua',\n DF: 'Ciudad de México',\n DG: 'Durango',\n GT: 'Guanajuato',\n GR: 'Guerrero',\n HG: 'Hidalgo',\n JC: 'Jalisco',\n MC: 'Estado de México',\n MN: 'Michoacán',\n MS: 'Morelos',\n NT: 'Nayarit',\n NL: 'Nuevo León',\n OC: 'Oaxaca',\n PL: 'Puebla',\n QT: 'Querétaro',\n QR: 'Quintana Roo',\n SP: 'San Luis Potosí',\n SL: 'Sinaloa',\n SR: 'Sonora',\n TC: 'Tabasco',\n TS: 'Tamaulipas',\n TL: 'Tlaxcala',\n VZ: 'Veracruz',\n YN: 'Yucatán',\n ZS: 'Zacatecas',\n NE: 'Nacido en el Extranjero',\n};\n\n/**\n * Get the birth state from a CURP\n * @returns State name or null if invalid\n */\nexport function getCURPBirthState(curp: string): string | null {\n const cleaned = cleanDocument(curp).toUpperCase();\n if (cleaned.length < 13) return null;\n\n const stateCode = cleaned.slice(11, 13);\n return CURP_STATE_CODES[stateCode] || null;\n}\n\n/**\n * Get the state code from a CURP\n * @returns Two-letter state code or null if invalid\n */\nexport function getCURPStateCode(curp: string): string | null {\n const cleaned = cleanDocument(curp).toUpperCase();\n if (cleaned.length < 13) return null;\n\n const stateCode = cleaned.slice(11, 13);\n return CURP_STATE_CODES[stateCode] ? stateCode : null;\n}\n\n/**\n * Check if RFC belongs to a company (persona moral)\n */\nexport function isRFCCompany(rfc: string): boolean {\n const cleaned = cleanDocument(rfc).toUpperCase();\n return cleaned.length === 12;\n}\n\n/**\n * Check if RFC belongs to an individual (persona física)\n */\nexport function isRFCPerson(rfc: string): boolean {\n const cleaned = cleanDocument(rfc).toUpperCase();\n return cleaned.length === 13;\n}\n\n/**\n * Get the registration date from RFC\n * @returns Date object or null if invalid\n */\nexport function getRFCDate(rfc: string): Date | null {\n const cleaned = cleanDocument(rfc).toUpperCase();\n\n if (cleaned.length !== 12 && cleaned.length !== 13) return null;\n\n // Date starts at position 3 for companies, 4 for individuals\n const dateStart = cleaned.length === 12 ? 3 : 4;\n const dateStr = cleaned.slice(dateStart, dateStart + 6);\n\n const year = parseInt(dateStr.slice(0, 2), 10);\n const month = parseInt(dateStr.slice(2, 4), 10);\n const day = parseInt(dateStr.slice(4, 6), 10);\n\n // Assume 1900s for years > 30, 2000s otherwise (heuristic)\n const fullYear = year > 30 ? 1900 + year : 2000 + year;\n\n const date = new Date(fullYear, month - 1, day);\n\n // Validate the date\n if (date.getFullYear() !== fullYear || date.getMonth() !== month - 1 || date.getDate() !== day) {\n return null;\n }\n\n return date;\n}\n\n/**\n * Extract all info from a CURP\n */\nexport function parseCURP(curp: string): {\n valid: boolean;\n gender: 'M' | 'F' | null;\n birthDate: Date | null;\n birthState: string | null;\n stateCode: string | null;\n} {\n return {\n valid: validateCURP(curp),\n gender: getCURPGender(curp),\n birthDate: getCURPBirthDate(curp),\n birthState: getCURPBirthState(curp),\n stateCode: getCURPStateCode(curp),\n };\n}\n\n/**\n * Extract info from RFC\n */\nexport function parseRFC(rfc: string): {\n valid: boolean;\n type: 'company' | 'person' | null;\n date: Date | null;\n} {\n const valid = validateRFC(rfc);\n if (!valid) {\n return { valid: false, type: null, date: null };\n }\n\n return {\n valid: true,\n type: isRFCCompany(rfc) ? 'company' : 'person',\n date: getRFCDate(rfc),\n };\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "soff-id",
3
- "version": "0.1.1",
3
+ "version": "0.2.0",
4
4
  "description": "LATAM document validation library - Validate NIT, RUT, CPF, CUIT, and more",
5
5
  "type": "module",
6
6
  "main": "./dist/index.cjs",