27 Commits
Author SHA1 Message Date
jake 992c54ad28 refactor: extract vCard parser into its own module and expand parsed fields
- Move `parseVcard` out of `normalize.ts` into a new `src/rdap/vcard.ts` module; export `readJCard` (jCard → `VCardProp[]`) and `paramList` as reusable primitives
- Parse `KIND` into a typed `VCardKind` union and split structured `ORG` values into a top-level `org` plus an `orgUnits` array for sub-levels
- Parse `TITLE` and `ROLE` properties from the vCard
- Extract the PO box (element 0) and extended address (element 1) from `ADR` into `poBox` and the beginning of `street` respectively, rather than discarding them
- Strip `tel:` URI scheme from `TEL` entries whose `valueType` is `uri` (RFC 6350)
- Add `kind`, `organizationUnits`, `title`, `role`, and `poBox` to the `Contact` type and wire them through `extractContacts` in `normalize.ts`
- Document new `Contact` fields in README
- Add `vcard.test.ts` covering malformed input tolerance, `paramList` normalisation, ORG level splitting, unknown KIND, PO box extraction, and `tel:` URI stripping
2026-09-19 23:20:15 -04:00
jake 89fa13ffef feat: add contact redacted flag, placeholder detection, and country name/code resolution
- Add `redacted?: boolean` to `Contact`; set when any field was dropped as a placeholder, the name matches a privacy-service heuristic, or an RFC 9537 redaction targets that entity's role
- Add `isPlaceholderValue` to `privacy.ts` to identify boilerplate registrar strings (`"Please query the RDDS…"`, `"redacted"`, `"n/a"`, etc.) used in place of real email/phone/fax values; drop them from the contact during finalization
- Add `countries.ts` backed by `Intl.DisplayNames` (with a curated `ALIASES` table for common alternate names) providing `countryNameFromCode`, `countryCodeFromName`, and `resolveCountry`; a two-letter `country` value is now treated as a code and expanded to a full name
- Introduce `finalizeContact` in `contacts.ts` to centralise all contact post-processing (placeholder stripping, country resolution, `redacted` flag); call it from both RDAP and WHOIS normalizers
- Wire RFC 9537 redaction role-matching into `extractContacts` via a new `redactionTargetsRole` helper so entities targeted by a `redacted` entry automatically receive `redacted: true`
- Add `countries.test.ts` and extend RDAP/WHOIS normalizer tests to cover placeholder stripping, country fill-in, and redaction flag propagation
2026-09-19 23:07:38 -04:00
jake 1d04be5c74 fix: tighten isPrivacyName to avoid false positives on ambiguous privacy words
- Split keyword list into `PRIVACY_STRONG_KEYWORDS` (matched as plain substrings) and two weaker sets (`PRIVACY_WEAK_WORDS` / `PRIVACY_CONTEXT_WORDS`) that only trigger when at least two distinct terms appear together, preventing names like "Private Equity Partners LLC" or "Protection One" from being misclassified
- Remove empty-placeholder values (`-`, `n/a`, `none`, etc.) from the privacy check; they are not privacy notices and should be handled separately by callers
- Add `privacy.test.ts` covering redaction notices, known privacy-service names, ordinary names with ambiguous words, and empty placeholders
- Update README comment to clarify that `privacyEnabled` is set by name heuristics (privacy-service and redaction phrases) or RFC 9537 redactions
2026-09-19 23:04:01 -04:00
jake 386006dc61 feat: add RFC 9537 redaction support, vCard address/fax parsing, and registrar address fields
- Parse the top-level RDAP `redacted` array (RFC 9537) into a new `redactions` field on `DomainRecord`; set `privacyEnabled` when any redaction references "registrant"
- Expose `street`, `city`, `state`, `postalCode`, `country`, and `countryCode` on both `RegistrarInfo` and `Contact`; extract ISO 3166-1 alpha-2 from the vCard `cc` parameter (RFC 8605)
- Split `tel` entries by fax type parameter (`"fax"` / `["work","fax"]`) into separate `tel` and `fax` lists; collect multiple email addresses as an array, collapsing singletons to a plain string
- Prefer an exact `registration` event match over `reregistration` when extracting `creationDate` (RFC 9083)
- Merge `redacted` arrays in `mergeRdapDocs`, deduplicating by JSON identity
- Split `adr` street on `\r?\n` only (not commas) to avoid splitting inline comma-separated addresses
2026-09-19 23:01:30 -04:00
jake 93f877c357 fix: extend Retry-After parsing to RDAP 503 and fix block pattern across newlines
- Parse `Retry-After` on RDAP `503` responses (previously only `429`), exposing `retryAfterMs` on the attempt and on the top-level result when `rdapOnly` makes it terminal
- Fix the WHOIS `BLOCK_PATTERNS` regex to use `[\s\S]` instead of `.` so a block notice that spans multiple lines (e.g. `"Your IP address\nhas been blocked"`) is still classified as `blocked`
- Update README to document `retryAfterMs` on `LookupResult`, clarify `rate_limited` / `blocked` / `unparseable` semantics, and note referral-host validation behaviour
2026-09-19 12:30:06 -04:00
jake e736045de1 fix: treat WHOIS empty-close as no_data and ETIMEDOUT as structured timeout
- A server that accepts the connection and closes it without sending data now rejects with `errorCode: "no_data"` and `stage: "read"` instead of resolving with an empty string
- OS-level `ETIMEDOUT` errors are mapped to the same `timeout` / `stage` shape as the library's own timer, with `stage` derived from whether the socket had connected yet
- `ECONNRESET` after data arrives is no longer marked `partial: true`; it was a complete reply and the reset just signalled the end of the stream
- `stage` on `LookupAttempt` is now documented as covering all WHOIS failures (not only timeout failures)
2026-09-18 14:47:31 -04:00
jake 69f85ee925 feat: add attempt tracking, deadlineMs, and structured error codes
- Resolve `LookupResult` with an `attempts` array describing every network operation (phase, server, duration, error) so failures recovered by fallback remain visible
- Add `deadlineMs` option for a hard cap on total lookup time, distinct from per-operation `timeoutMs` (lowered default from 15 s to 10 s)
- Add `errorCode` (machine-readable `LookupErrorCode`), `errorPhase`, and `errorServer` to `LookupResult`; `timeout` covers both per-op and deadline timeouts, `aborted` means the caller's signal fired
- WHOIS timeouts now distinguish `connect` vs `read` stage and resolve with partial text (marked `partial: true`) when data arrived before the socket stalled
- Drop Node 18 support; minimum engine is now 20
2026-09-18 14:41:16 -04:00
jake 87e20f4a47 chore: replace Biome with oxlint and oxfmt 2026-09-18 14:21:15 -04:00
jake e6b2e94614 fix: update tsdown outputs from .js to .mjs 2025-12-30 12:53:17 -05:00
jake 619185b5d6 Add support for custom fetch function and/or IANA bootstrap data (#21) 2025-10-30 19:07:12 -04:00
jake b3adbf8307 Rename lookupDomain() to lookup() (with backwards compatibility) 2025-10-20 15:05:59 -04:00
jake 5cbefe6a14 Update README.md with clearer usage instructions 2025-10-20 14:48:16 -04:00
jake 08a5b9a576 Make the (very minimal) CLI official 2025-10-12 17:14:37 -04:00
jake db96e8d933 Lazy load node:net for improved edge runtime compatibility 2025-10-12 13:52:50 -04:00
jake 6070d91424 Add toRegistrableDomain function for domain normalization 2025-10-10 10:44:04 -04:00
jake bfcecc5eff Migrate from psl to tldts for public suffix detection 2025-10-10 10:29:29 -04:00
jake 33203b2b47 Remove fetchedAt field from DomainRecord interface and related functions 2025-10-08 21:43:32 -04:00
jake 2c9904e5e8 New privacyEnabled field to detect redaction based on keyword heuristics
Updated the DomainRecord interface to include detailed comments for clarity and added a new privacyEnabled field to indicate if the registrant's information is privacy-redacted. Enhanced normalization functions for both RDAP and WHOIS to derive the privacy flag based on registrant keywords. Updated tests to verify the correct functionality of privacy detection in both normalization processes. Additionally, the README was updated to reflect the inclusion of the privacy flag in the normalized output.
2025-10-08 21:31:21 -04:00
jake f8280508ff Enhance RDAP and WHOIS functionality with referral handling
Added support for following registrar WHOIS referrals with configurable hop limits. Introduced new options in LookupOptions for maximum referral hops and RDAP link handling. Updated README to reflect these changes and improved the lookupDomain function to utilize the new referral logic. Added utility functions for merging RDAP documents and extracting related links.
2025-10-08 19:25:51 -04:00
jake 174cae8610 Update testing framework to Vitest (#4) 2025-09-26 11:49:44 -04:00
jake e873fb7629 Add link to hoot.sh in README
Updated README to include a link to hoot.sh
2025-09-24 21:29:45 -04:00
jake dc0ff2a00b Refine README examples and API documentation: simplify usage of lookupDomain function and clarify fallback behavior for RDAP and WHOIS. 2025-09-24 19:30:22 -04:00
jake fda6d09096 Add includeRaw option to CLI and API: enable inclusion of raw RDAP/WHOIS data in results 2025-09-24 19:24:57 -04:00
jake 05445ef831 Update repository guidelines and README: enhance project structure documentation, clarify build and test commands, and improve API usage examples for better user understanding. 2025-09-24 19:19:41 -04:00
jake 0a852ff3cd Add CLI script for domain lookup and create package-lock.json; update README and package.json for clarity and dependencies 2025-09-24 17:28:12 -04:00
jake 33a648460b Core functionality for domain lookup using RDAP and WHOIS protocols, along with utility functions and tests. 2025-09-24 14:28:09 -04:00
jake bd4846302d Initial commit 2025-09-24 13:06:40 -04:00