Commit Graph
98 Commits
Author SHA1 Message Date
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 e77fc7f4ed 0.15.1 v0.15.1 2026-09-19 12:32:07 -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 35959b208b feat: surface retryAfterMs, add blocked error code, and harden private-address guard
- Parse `Retry-After` headers (delay-seconds or HTTP-date) into `retryAfterMs` via a new `parseRetryAfterMs` helper and expose the value on both `LookupAttempt` and `LookupResult` so callers can honour server-requested back-off without parsing the error string
- Propagate `retryAfterMs` through `classifyError`, `traced`, `failure`, and the `rdap_unavailable` path so the value surfaces on the top-level result
- Add `blocked` to `LookupErrorCode` for WHOIS servers that permanently refuse a client (distinct from `rate_limited`, which is a temporary throttle that may succeed on retry)
- Add `blockPrivateAddresses` to `WhoisTransportOptions`; when set, a custom `dns.lookup` shim rejects the connection before it opens if any resolved address is non-public, covering DNS rebinding and hostnames that resolve to private ranges
- Expand `isPrivateIp` / `isSafeWhoisReferralHost` to reject IPv4-mapped (`::ffff:7f00:1`), NAT64 (`64:ff9b::`), 6to4 (`2002:7f00::`), Teredo (`2001:0:`), deprecated site-local (`fec0::`), and bracketed IPv6 literals
2026-09-19 12:25:30 -04:00
jake 208fd6c69f 0.15.0 v0.15.0 2026-09-19 12:17:47 -04:00
jake 3023c5dd1b feat: add SSRF host validation, rate_limited/unparseable error codes, and referral chain warnings
- Add `isSafeWhoisReferralHost` to reject private/loopback/link-local IPs and malformed hostnames before following WHOIS referrals (SSRF guard)
- Detect WHOIS throttle replies in the referral chain: drop the registrar response, keep the registry record, and surface a warning instead of returning bad data
- Add `looksEmptyWhois` guard in the main lookup path: a "registered" record with no parseable fields now fails with `errorCode: "unparseable"` rather than resolving silently
- Map RDAP 429 responses to a structured `rate_limited` error (including `Retry-After` header) so callers can distinguish throttling from generic HTTP errors and the fallback to WHOIS is recorded in `attempts`
- Add `rate_limited` and `unparseable` to `LookupErrorCode`
- Change `collectWhoisReferralChain` to return `{ results, warnings }` instead of a bare array; warnings are merged onto the final `DomainRecord`
- Remove the `followWhoisReferrals` fallback path from `index.ts` (dead code after the chain API stabilised)
2026-09-19 12:14:19 -04:00
jake 61c5897317 0.14.0 v0.14.0 2026-09-19 00:56:46 -04:00
jake 56051dccd0 chore: fix lockfile 2026-09-19 00:56:30 -04:00
jake be8e88592f feat: add ccTLD-specific date formats, noisy-token parsing, and .gg/.je/.br/.ua normalization, closes #20 and #25
- Add `toISOFromTokens` to extract ISO dates from noisy values (e.g. ".ua" `0-UANIC 20111004161638`, `OK-UNTIL 20261004161638`)
- Parse compact `YYYYMMDDHHMMSS` (.ua) and `YYYYMMDD` (.br, including ticket suffix like `20260319 #31066859`) date formats
- Parse ordinal/prose dates used by .gg/.je (`28th December 2018 at 05:54:43.861`)
- Teach `parseKeyValueLines` to collect indented continuation lines for header-style blocks (.gg/.je) without mistaking URLs or times as key separators
- Extract .gg/.je expiry from `Relevant dates:` sentences and split inline registrar URL (`epag (http://www.epag.de)`)
- Map .ua `status: OK-UNTIL <timestamp>` to `expirationDate` when no explicit expiry field is present
- Treat RDAP `pending release` / `release process: waiting` statuses as `isRegistered: false` (previously always `true`)
- Mark WHOIS `.br` `release process: waiting` as available
2026-09-19 00:51:55 -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 5ff837b2bb chore(deps): update dependencies and build configuration 2026-09-18 14:10:09 -04:00
jake a37ab8a6d8 0.13.0 v0.13.0 2026-01-10 13:35:11 -05:00
jake 0f64809bb1 chore: add GitHub Actions workflow for testing across multiple Node.js versions 2026-01-10 13:34:27 -05:00
renovate[bot] 8deef1e145 chore(deps): update dependency tsdown to v0.19.0 2026-01-10 13:30:18 -05:00
renovate[bot] 5c7ee9734e chore(deps): update dependency @biomejs/biome to v2.3.11 2026-01-10 13:30:10 -05:00
renovate[bot] 72821dbfec chore(deps): update dependency @types/node to v25.0.6 2026-01-10 13:28:52 -05:00
renovate[bot] 6884504d88 chore(deps): update dependency node to v24 2026-01-10 13:28:36 -05:00
renovate[bot] efc0277399 chore(deps): update actions/checkout action to v6 2026-01-10 13:28:16 -05:00
Nodar Davituri 2ca7ce68ce fix: treat RDAP 404 responses as "domain not found" instead of failure (#29) 2026-01-10 13:26:45 -05:00
jake 6ef4291d0e 0.12.1 v0.12.1 2025-12-30 12:53:25 -05:00
jake e6b2e94614 fix: update tsdown outputs from .js to .mjs 2025-12-30 12:53:17 -05:00
jake c6da4c1f69 0.12.0 v0.12.0 2025-12-30 12:45:00 -05:00
jake ab1326d99c fix: improve transfer lock status detection 2025-12-30 12:43:55 -05:00
jake 1b73b7dc8d chore: bump deps 2025-12-30 12:41:13 -05:00
jake b72324fd95 0.11.0 v0.11.0 2025-10-30 19:07:47 -04:00
jake 619185b5d6 Add support for custom fetch function and/or IANA bootstrap data (#21) 2025-10-30 19:07:12 -04:00
renovate[bot] acae007a4a Update dependency vitest to v4 2025-10-30 18:39:10 -04:00
renovate[bot] 9bbc52b981 Update dependency tsdown to v0.15.12 2025-10-30 18:19:50 -04:00
renovate[bot] d47009dcd6 Update dependency @biomejs/biome to v2.3.2 2025-10-30 18:19:21 -04:00
jake 8c8dec5a37 0.10.4 v0.10.4 2025-10-27 15:01:07 -04:00
jake 2a92375116 Add support for DD-MM-YYYY date format in toISO function and update tests (fixes #19) 2025-10-27 14:59:14 -04:00
jake fd29899f4e 0.10.3 v0.10.3 2025-10-27 14:44:25 -04:00
jake 9d87ef8ecb Add to list of parser strings to improve WHOIS data normalization 2025-10-27 14:42:43 -04:00
jake 1c6fbad1f7 0.10.2 v0.10.2 2025-10-22 17:45:40 -04:00
jake 87b85f7d3d Add query transformation for non-English WHOIS servers, specifically for whois.jprs.jp (fixes #15) 2025-10-22 17:45:19 -04:00
jake c3b6477094 0.10.1 v0.10.1 2025-10-22 17:38:36 -04:00
jake b8357e38bc Fix .hk, .ee, .il date parsing (fixes #13, #14, #16) 2025-10-22 17:37:37 -04:00
jake f90c481c94 0.10.0 v0.10.0 2025-10-20 15:06:29 -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 52e5c54f9f 0.9.1 v0.9.1 2025-10-20 14:30:48 -04:00
jake b9d4d42e19 Update GitHub Actions workflow to install the latest npm version before publishing 2025-10-20 14:24:35 -04:00
jake 2f0e784cf7 Run npm pkg fix 2025-10-20 14:20:26 -04:00
renovate[bot] 32478e8f74 Update dependency node to v22 2025-10-20 14:16:02 -04:00
jake d87e898a9f Add additional synonyms for WHOIS normalization (fixes #10) 2025-10-20 14:15:23 -04:00
jake 2a1b7529cc Implement WHOIS referral chain collection and merging logic (fixes #11)
- Introduced `collectWhoisReferralChain` to gather WHOIS responses while avoiding contradictory data from registrars.
- Updated `lookupDomain` to utilize the new chain collection method, ensuring TLD responses are prioritized.
- Added `mergeWhoisRecords` function to consolidate WHOIS data from multiple sources.
- Enhanced tests for referral handling and merging behavior, ensuring accurate data retention across scenarios.
2025-10-20 14:08:26 -04:00