Spaces:
Paused
Paused
| type ParsedVcard = { | |
| name?: string; | |
| phones: string[]; | |
| }; | |
| const ALLOWED_VCARD_KEYS = new Set(["FN", "N", "TEL"]); | |
| export function parseVcard(vcard?: string): ParsedVcard { | |
| if (!vcard) { | |
| return { phones: [] }; | |
| } | |
| const lines = vcard.split(/\r?\n/); | |
| let nameFromN: string | undefined; | |
| let nameFromFn: string | undefined; | |
| const phones: string[] = []; | |
| for (const rawLine of lines) { | |
| const line = rawLine.trim(); | |
| if (!line) { | |
| continue; | |
| } | |
| const colonIndex = line.indexOf(":"); | |
| if (colonIndex === -1) { | |
| continue; | |
| } | |
| const key = line.slice(0, colonIndex).toUpperCase(); | |
| const rawValue = line.slice(colonIndex + 1).trim(); | |
| if (!rawValue) { | |
| continue; | |
| } | |
| const baseKey = normalizeVcardKey(key); | |
| if (!baseKey || !ALLOWED_VCARD_KEYS.has(baseKey)) { | |
| continue; | |
| } | |
| const value = cleanVcardValue(rawValue); | |
| if (!value) { | |
| continue; | |
| } | |
| if (baseKey === "FN" && !nameFromFn) { | |
| nameFromFn = normalizeVcardName(value); | |
| continue; | |
| } | |
| if (baseKey === "N" && !nameFromN) { | |
| nameFromN = normalizeVcardName(value); | |
| continue; | |
| } | |
| if (baseKey === "TEL") { | |
| const phone = normalizeVcardPhone(value); | |
| if (phone) { | |
| phones.push(phone); | |
| } | |
| } | |
| } | |
| return { name: nameFromFn ?? nameFromN, phones }; | |
| } | |
| function normalizeVcardKey(key: string): string | undefined { | |
| const [primary] = key.split(";"); | |
| if (!primary) { | |
| return undefined; | |
| } | |
| const segments = primary.split("."); | |
| return segments[segments.length - 1] || undefined; | |
| } | |
| function cleanVcardValue(value: string): string { | |
| return value.replace(/\\n/gi, " ").replace(/\\,/g, ",").replace(/\\;/g, ";").trim(); | |
| } | |
| function normalizeVcardName(value: string): string { | |
| return value.replace(/;/g, " ").replace(/\s+/g, " ").trim(); | |
| } | |
| function normalizeVcardPhone(value: string): string { | |
| const trimmed = value.trim(); | |
| if (!trimmed) { | |
| return ""; | |
| } | |
| if (trimmed.toLowerCase().startsWith("tel:")) { | |
| return trimmed.slice(4).trim(); | |
| } | |
| return trimmed; | |
| } | |