mirror of
https://github.com/jakejarvis/rdapper.git
synced 2026-10-26 08:26:22 -04:00
fix: keep multi-line vCard street addresses
A street component holding several values is an array (RFC 7095 3.3.1.3), which was dropped, so contacts and registrars lost their street entirely. Read every value, and fall back to the "label" parameter when the structured address is empty. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
2 files changed
+37
-2
No files matched your search
@@ -60,3 +60,24 @@ test("parseVcard keeps PO box and extended address, and strips tel: URIs", () =>
|
||||
expect(v.countryCode).toBe("NL");
|
||||
expect(v.tel).toEqual(["+31.201234567"]);
|
||||
});
|
||||
|
||||
test("parseVcard reads multi-value street components and a label-only address", () => {
|
||||
const multi = parseVcard([
|
||||
"vcard",
|
||||
[
|
||||
[
|
||||
"adr",
|
||||
{},
|
||||
"text",
|
||||
["", "", ["3540 E Longwing Ln", "Suite 300"], "Meridian", "ID", "83646", "US"],
|
||||
],
|
||||
],
|
||||
]);
|
||||
expect(multi.street).toEqual(["3540 E Longwing Ln", "Suite 300"]);
|
||||
expect(multi.locality).toBe("Meridian");
|
||||
const label = parseVcard([
|
||||
"vcard",
|
||||
[["adr", { label: "123 Main St\nSpringfield\nUS" }, "text", ["", "", "", "", "", "", ""]]],
|
||||
]);
|
||||
expect(label.street).toEqual(["123 Main St", "Springfield", "US"]);
|
||||
});
|
||||
+16
-2
@@ -74,6 +74,17 @@ export interface ParsedVCard {
|
||||
|
||||
const lines = (s: string | undefined) => (s ? s.split(/\r?\n/).filter(Boolean) : []);
|
||||
|
||||
/**
|
||||
* Lines of a structured value's component, which holds an array when it has several values
|
||||
* (RFC 7095 3.3.1.3): ["", "", ["3540 E Longwing Ln", "Suite 300"], "Meridian", ...].
|
||||
*/
|
||||
function componentLines(value: unknown, i: number): string[] {
|
||||
const component = Array.isArray(value) ? value[i] : undefined;
|
||||
return (Array.isArray(component) ? component : [component])
|
||||
.filter((x): x is string => typeof x === "string")
|
||||
.flatMap((x) => lines(x.trim()));
|
||||
}
|
||||
|
||||
/** Extract the fields rdapper cares about from an RDAP "vcardArray". */
|
||||
export function parseVcard(vcardArray: unknown): ParsedVCard {
|
||||
const out: ParsedVCard = {};
|
||||
@@ -127,12 +138,15 @@ export function parseVcard(vcardArray: unknown): ParsedVCard {
|
||||
}
|
||||
// [postOfficeBox, extendedAddress, street, locality, region, postalCode, country]
|
||||
out.poBox = part(p.value, 0);
|
||||
const street = [...lines(part(p.value, 1)), ...lines(part(p.value, 2))];
|
||||
out.street = street.length ? street : undefined;
|
||||
const street = [...componentLines(p.value, 1), ...componentLines(p.value, 2)];
|
||||
out.locality = part(p.value, 3);
|
||||
out.region = part(p.value, 4);
|
||||
out.postcode = part(p.value, 5);
|
||||
out.country = part(p.value, 6);
|
||||
// With every component empty, the address may be given whole in the "label" parameter
|
||||
const structured = street.length || out.poBox || out.locality || out.country;
|
||||
if (!structured) street.push(...lines(asString(p.params.label)?.trim()));
|
||||
out.street = street.length ? street : undefined;
|
||||
// RFC 8605: ISO 3166-1 alpha-2 code lives in the "cc" parameter
|
||||
const cc = asString(p.params.cc);
|
||||
if (cc) out.countryCode = cc.toUpperCase();
|
||||
|
||||
Reference in new issue
Block a user