feat: add redactedFields, privacyService, and ContactField to contact post-processing

- Add `ContactField` union type and `redactedFields?: ContactField[]` to `Contact` so callers can see exactly which fields were dropped as placeholders or known-redacted, rather than just a boolean flag
- Add `privacyService?: boolean` to `Contact`, set by `finalizeContact` when the name/organization text identifies a privacy proxy service (value is kept; `redacted` is still set)
- Expand `finalizeContact` to clean all address/identity fields (not just email/phone/fax), record each dropped field in `redactedFields`, and accept `ContactField[]` as `redactedHint` so RFC 9537 field names can be passed through directly
- Add `redactionFields(text)` to map RFC 9537 redaction names (e.g. `"Registrant Email"`) to the corresponding `ContactField` entries; use it in `extractContacts` to narrow `redactedHint` to only fields that are actually absent
- Add `isPrivacyContact` helper and switch both RDAP and WHOIS normalizers to use it instead of inlining `isPrivacyName` checks
- Expand `PLACEHOLDER_VALUE_PATTERNS` in `privacy.ts` to cover `"Not available from registry"`, `"Not applicable"`, `"Data Protected"`, `"STATUTORY/GDPR Masking"`, `"Select Request Email Form"`, and `"Unknown"` (as an exact word)
- Export `resolveCountry`, `isPlaceholderValue`, and `isPrivacyName` from the package entry point
- Add `contacts.test.ts` covering placeholder-dropping, `redactedFields` population, `privacyService` flag, hint merging, `redactionFields` mapping, and the new placeholder patterns
This commit is contained in:
2026-09-20 00:04:25 -04:00
parent 7ab25ceecc
commit adac9d3349
9 changed files with 220 additions and 27 deletions
+13
View File
@@ -560,6 +560,19 @@ interface DomainRecord {
country?: string;
countryCode?: string; // ISO 3166-1 alpha-2; country/countryCode are resolved from each other when possible
redacted?: boolean; // some contact data was redacted/withheld or replaced by a placeholder
redactedFields?: Array<
| "name"
| "organization"
| "email"
| "phone"
| "fax"
| "street"
| "city"
| "state"
| "postalCode"
| "poBox"
>; // fields that were redacted (and are therefore absent)
privacyService?: boolean; // name/organization is a privacy/proxy service (text kept), not a redaction notice
}>;
privacyEnabled?: boolean; // registrant appears privacy-redacted based on name heuristics (privacy-service and redaction phrases) or RFC 9537 redactions
redactions?: Array<{