UNPKG

semantic-ds-toolkit

Version:

Performance-first semantic layer for modern data stacks - Stable Column Anchors & intelligent inference

249 lines 10.2 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.PhoneNormalizer = void 0; exports.normalizePhone = normalizePhone; const COUNTRY_CODES = new Map([ ['1', { name: 'US/Canada', maxLength: 10 }], ['44', { name: 'UK', maxLength: 10 }], ['49', { name: 'Germany', maxLength: 11 }], ['33', { name: 'France', maxLength: 9 }], ['81', { name: 'Japan', maxLength: 10 }], ['86', { name: 'China', maxLength: 11 }], ['91', { name: 'India', maxLength: 10 }], ['55', { name: 'Brazil', maxLength: 11 }], ['61', { name: 'Australia', maxLength: 9 }], ['7', { name: 'Russia', maxLength: 10 }], ['39', { name: 'Italy', maxLength: 10 }], ['34', { name: 'Spain', maxLength: 9 }], ['31', { name: 'Netherlands', maxLength: 9 }], ['46', { name: 'Sweden', maxLength: 9 }], ['47', { name: 'Norway', maxLength: 8 }], ['45', { name: 'Denmark', maxLength: 8 }], ['41', { name: 'Switzerland', maxLength: 9 }], ['43', { name: 'Austria', maxLength: 10 }], ['32', { name: 'Belgium', maxLength: 9 }], ['351', { name: 'Portugal', maxLength: 9 }], ['30', { name: 'Greece', maxLength: 10 }], ['48', { name: 'Poland', maxLength: 9 }], ['420', { name: 'Czech Republic', maxLength: 9 }], ['36', { name: 'Hungary', maxLength: 9 }], ['40', { name: 'Romania', maxLength: 10 }], ['385', { name: 'Croatia', maxLength: 8 }], ['386', { name: 'Slovenia', maxLength: 8 }], ['421', { name: 'Slovakia', maxLength: 9 }], ['370', { name: 'Lithuania', maxLength: 8 }], ['371', { name: 'Latvia', maxLength: 8 }], ['372', { name: 'Estonia', maxLength: 8 }], ['358', { name: 'Finland', maxLength: 9 }], ['354', { name: 'Iceland', maxLength: 7 }], ['353', { name: 'Ireland', maxLength: 9 }], ['356', { name: 'Malta', maxLength: 8 }], ['357', { name: 'Cyprus', maxLength: 8 }], ['352', { name: 'Luxembourg', maxLength: 9 }], ['377', { name: 'Monaco', maxLength: 8 }], ['378', { name: 'San Marino', maxLength: 10 }], ['379', { name: 'Vatican', maxLength: 10 }], ['380', { name: 'Ukraine', maxLength: 9 }], ['375', { name: 'Belarus', maxLength: 9 }], ['374', { name: 'Armenia', maxLength: 8 }], ['995', { name: 'Georgia', maxLength: 9 }], ['994', { name: 'Azerbaijan', maxLength: 9 }], ['993', { name: 'Turkmenistan', maxLength: 8 }], ['992', { name: 'Tajikistan', maxLength: 9 }], ['998', { name: 'Uzbekistan', maxLength: 9 }], ['996', { name: 'Kyrgyzstan', maxLength: 9 }], ['212', { name: 'Morocco', maxLength: 9 }], ['213', { name: 'Algeria', maxLength: 9 }], ['216', { name: 'Tunisia', maxLength: 8 }], ['218', { name: 'Libya', maxLength: 9 }], ['220', { name: 'Gambia', maxLength: 7 }], ['221', { name: 'Senegal', maxLength: 9 }], ['222', { name: 'Mauritania', maxLength: 8 }], ['223', { name: 'Mali', maxLength: 8 }], ['224', { name: 'Guinea', maxLength: 9 }], ['225', { name: 'Ivory Coast', maxLength: 8 }], ['226', { name: 'Burkina Faso', maxLength: 8 }], ['227', { name: 'Niger', maxLength: 8 }], ['228', { name: 'Togo', maxLength: 8 }], ['229', { name: 'Benin', maxLength: 8 }], ['230', { name: 'Mauritius', maxLength: 7 }], ['231', { name: 'Liberia', maxLength: 8 }], ['232', { name: 'Sierra Leone', maxLength: 8 }], ['233', { name: 'Ghana', maxLength: 9 }], ['234', { name: 'Nigeria', maxLength: 10 }], ['235', { name: 'Chad', maxLength: 8 }], ['236', { name: 'Central African Republic', maxLength: 8 }], ['237', { name: 'Cameroon', maxLength: 9 }], ['238', { name: 'Cape Verde', maxLength: 7 }], ['239', { name: 'Sao Tome and Principe', maxLength: 7 }], ['240', { name: 'Equatorial Guinea', maxLength: 9 }], ['241', { name: 'Gabon', maxLength: 8 }], ['242', { name: 'Republic of the Congo', maxLength: 9 }], ['243', { name: 'Democratic Republic of the Congo', maxLength: 9 }], ['244', { name: 'Angola', maxLength: 9 }], ['245', { name: 'Guinea-Bissau', maxLength: 7 }], ['246', { name: 'British Indian Ocean Territory', maxLength: 7 }], ['248', { name: 'Seychelles', maxLength: 7 }], ['249', { name: 'Sudan', maxLength: 9 }], ['250', { name: 'Rwanda', maxLength: 9 }], ['251', { name: 'Ethiopia', maxLength: 9 }], ['252', { name: 'Somalia', maxLength: 8 }], ['253', { name: 'Djibouti', maxLength: 8 }], ['254', { name: 'Kenya', maxLength: 9 }], ['255', { name: 'Tanzania', maxLength: 9 }], ['256', { name: 'Uganda', maxLength: 9 }], ['257', { name: 'Burundi', maxLength: 8 }], ['258', { name: 'Mozambique', maxLength: 9 }], ['260', { name: 'Zambia', maxLength: 9 }], ['261', { name: 'Madagascar', maxLength: 9 }], ['262', { name: 'Reunion', maxLength: 9 }], ['263', { name: 'Zimbabwe', maxLength: 9 }], ['264', { name: 'Namibia', maxLength: 7 }], ['265', { name: 'Malawi', maxLength: 9 }], ['266', { name: 'Lesotho', maxLength: 8 }], ['267', { name: 'Botswana', maxLength: 8 }], ['268', { name: 'Swaziland', maxLength: 8 }], ['269', { name: 'Comoros', maxLength: 7 }], ['27', { name: 'South Africa', maxLength: 9 }], ]); class PhoneNormalizer { options; constructor(options = {}) { this.options = { defaultCountryCode: '1', formatE164: true, removeFormatting: true, ...options }; } normalize(phone) { const original = phone; let normalized = phone.trim(); if (this.options.removeFormatting) { normalized = this.removeFormatting(normalized); } const parseResult = this.parsePhone(normalized); if (!parseResult.isValid) { return { normalized: original, original, confidence: 0, isValid: false, format: 'UNKNOWN' }; } const { countryCode, nationalNumber } = parseResult; let finalFormat = 'E164'; let finalNormalized = normalized; if (this.options.formatE164) { finalNormalized = `+${countryCode}${nationalNumber}`; finalFormat = 'E164'; } else { finalNormalized = normalized; finalFormat = 'NATIONAL'; } const confidence = this.calculateConfidence(original, finalNormalized, parseResult); return { normalized: finalNormalized, original, confidence, countryCode, nationalNumber, isValid: parseResult.isValid, format: finalFormat }; } removeFormatting(phone) { return phone.replace(/[\s\-\(\)\.\+]/g, ''); } parsePhone(phone) { let cleanPhone = phone; if (cleanPhone.startsWith('+')) { cleanPhone = cleanPhone.substring(1); } if (cleanPhone.startsWith('00')) { cleanPhone = cleanPhone.substring(2); } if (!/^\d+$/.test(cleanPhone)) { return { isValid: false }; } if (cleanPhone.length < 7 || cleanPhone.length > 15) { return { isValid: false }; } const possibleCountryCodes = this.findPossibleCountryCodes(cleanPhone); if (cleanPhone.length === 10 && !cleanPhone.startsWith('0')) { return { countryCode: this.options.defaultCountryCode, nationalNumber: cleanPhone, isValid: true }; } for (const countryCode of possibleCountryCodes) { const nationalNumber = cleanPhone.substring(countryCode.length); const countryInfo = COUNTRY_CODES.get(countryCode); if (countryInfo && nationalNumber.length <= countryInfo.maxLength && nationalNumber.length >= 7) { return { countryCode, nationalNumber, isValid: true }; } } return { isValid: false }; } findPossibleCountryCodes(phone) { const codes = []; for (const [code] of COUNTRY_CODES) { if (phone.startsWith(code)) { codes.push(code); } } return codes.sort((a, b) => b.length - a.length); } calculateConfidence(original, normalized, parseResult) { if (!parseResult.isValid) return 0; let confidence = 0.9; const cleanOriginal = this.removeFormatting(original); const cleanNormalized = this.removeFormatting(normalized); if (cleanOriginal === cleanNormalized) { confidence = 1.0; } if (parseResult.countryCode === this.options.defaultCountryCode) { confidence *= 0.95; } const countryInfo = COUNTRY_CODES.get(parseResult.countryCode); if (countryInfo && parseResult.nationalNumber) { const lengthRatio = parseResult.nationalNumber.length / countryInfo.maxLength; if (lengthRatio < 0.7) { confidence *= 0.8; } } return Math.max(0.1, confidence); } formatNational(phone) { const result = this.normalize(phone); if (!result.isValid || !result.nationalNumber) return null; if (result.countryCode === '1') { const national = result.nationalNumber; if (national.length === 10) { return `(${national.substring(0, 3)}) ${national.substring(3, 6)}-${national.substring(6)}`; } } return result.nationalNumber; } formatInternational(phone) { const result = this.normalize(phone); if (!result.isValid || !result.countryCode || !result.nationalNumber) return null; return `+${result.countryCode} ${result.nationalNumber}`; } } exports.PhoneNormalizer = PhoneNormalizer; function normalizePhone(phone, options) { const normalizer = new PhoneNormalizer(options); return normalizer.normalize(phone); } //# sourceMappingURL=phone.js.map