import parsePhoneNumberFromString, { type CountryCode, type PhoneNumberType } from 'libphonenumber-js/mobile/es6' /** Result shape kept stable for consumers (same fields as previous `phone` package integration). */ export interface PhoneValidationResult { isValid: boolean phoneNumber: string | null countryIso2: string | null countryIso3: string | null countryCode: string | null } const INVALID_RESULT: PhoneValidationResult = { isValid: false, phoneNumber: null, countryIso2: null, countryIso3: null, countryCode: null, } const MOBILE_NUMBER_TYPES = new Set(['MOBILE', 'FIXED_LINE_OR_MOBILE']) function isMobileNumberType(type: PhoneNumberType | undefined): boolean { return type !== undefined && MOBILE_NUMBER_TYPES.has(type) } function buildCandidates(value: string): string[] { const trimmed = value.trim() const digits = value.replace(/\D/g, '') const candidates = [trimmed] if (digits && digits !== trimmed) { candidates.push(digits) } if (digits.startsWith('0') && digits.length > 1) { candidates.push(digits.slice(1)) } return [...new Set(candidates)] } function toValidationResult(parsed: NonNullable>): PhoneValidationResult { return { isValid: true, phoneNumber: parsed.format('E.164'), countryIso2: parsed.country ?? null, countryIso3: null, countryCode: parsed.countryCallingCode ? `+${parsed.countryCallingCode}` : null, } } function runPhoneValidation(value: string, countryIso2: string): PhoneValidationResult { const country = countryIso2 as CountryCode for (const candidate of buildCandidates(value)) { const parsed = parsePhoneNumberFromString(candidate, country) if (!parsed?.isValid()) { continue } if (isMobileNumberType(parsed.getType())) { return toValidationResult(parsed) } } return INVALID_RESULT } export function validatePhoneNumber(nationalNumber: string, countryIso2: string): PhoneValidationResult { const trimmed = nationalNumber.trim() if (!trimmed) { return INVALID_RESULT } return runPhoneValidation(trimmed, countryIso2) } /** `null` when empty, otherwise whether the number is valid for the selected country. */ export function getPhoneValidationState(nationalNumber: string, countryIso2: string): boolean | null { if (!nationalNumber.trim()) { return null } return validatePhoneNumber(nationalNumber, countryIso2).isValid }