mirror of
https://github.com/jakejarvis/rdapper.git
synced 2026-09-23 00:15:31 -04:00
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
This commit is contained in:
@@ -421,7 +421,8 @@ const result = await lookup("example.com", {
|
||||
|
||||
### Options
|
||||
|
||||
- `timeoutMs?: number` – Total timeout budget per network operation (default `15000`).
|
||||
- `timeoutMs?: number` – Timeout for each individual network operation (default `10000`). A lookup performs several operations in sequence, so see [Timeouts and diagnostics](#timeouts-and-diagnostics) for the worst case. A value that is not a finite number > 0 disables the timeout.
|
||||
- `deadlineMs?: number` – Overall deadline for the whole lookup (default: none). When it elapses, in-flight requests and WHOIS sockets are cancelled and the result has `errorCode: "timeout"`.
|
||||
- `rdapOnly?: boolean` – Only attempt RDAP; do not fall back to WHOIS.
|
||||
- `whoisOnly?: boolean` – Skip RDAP and query WHOIS directly.
|
||||
- `followWhoisReferral?: boolean` – Follow registrar referral from the TLD WHOIS (default `true`).
|
||||
@@ -434,7 +435,50 @@ const result = await lookup("example.com", {
|
||||
- `customFetch?: FetchLike` – Custom fetch implementation for all HTTP requests (see [Custom Fetch Implementation](#custom-fetch-implementation)).
|
||||
- `whoisHints?: Record<string, string>` – Override/add authoritative WHOIS per TLD (keys are lowercase TLDs, values may include or omit `whois://`).
|
||||
- `includeRaw?: boolean` – Include `rawRdap`/`rawWhois` in the returned record (default `false`).
|
||||
- `signal?: AbortSignal` – Optional cancellation signal.
|
||||
- `signal?: AbortSignal` – Optional cancellation signal. Honored by RDAP requests _and_ WHOIS sockets; an abort stops the lookup rather than falling through to the next phase.
|
||||
|
||||
### Timeouts and diagnostics
|
||||
|
||||
`lookup()` never throws for lookup failures; it resolves to a `LookupResult`:
|
||||
|
||||
```ts
|
||||
interface LookupResult {
|
||||
ok: boolean;
|
||||
record?: DomainRecord;
|
||||
error?: string; // human-readable
|
||||
errorCode?: LookupErrorCode; // machine-readable, present when ok is false
|
||||
errorPhase?: "rdap_bootstrap" | "rdap" | "rdap_link" | "iana" | "whois";
|
||||
errorServer?: string; // RDAP URL or WHOIS host involved in the failure
|
||||
attempts: LookupAttempt[]; // every network operation, in order (always present)
|
||||
}
|
||||
```
|
||||
|
||||
`errorCode` is one of `invalid_input`, `invalid_tld`, `timeout`, `aborted`, `connect_failed`, `http_error`, `rdap_unavailable`, `no_server`, `no_data`, `unsupported_runtime`, or `unknown`. Prefer it over matching `error` text. `timeout` covers every timeout, including `deadlineMs`; `aborted` means your own `signal` fired.
|
||||
|
||||
Each entry in `attempts` describes one operation, successful or not, so a failure that was recovered from (say, an RDAP server that was down before WHOIS answered) is still visible:
|
||||
|
||||
```json
|
||||
{
|
||||
"phase": "whois",
|
||||
"server": "whois.example",
|
||||
"ok": false,
|
||||
"durationMs": 1503,
|
||||
"errorCode": "timeout",
|
||||
"error": "WHOIS connect timeout (whois.example)",
|
||||
"stage": "connect"
|
||||
}
|
||||
```
|
||||
|
||||
`stage` (`"connect"` or `"read"`) is set on WHOIS timeouts. If a WHOIS server sends some data but never closes the connection, the timeout **resolves with the partial text** instead of failing, and the attempt is marked `partial: true`.
|
||||
|
||||
`timeoutMs` applies to each network operation (including reading the response body), not to the lookup as a whole. Without `deadlineMs`, the worst case is roughly `timeoutMs × (1 bootstrap + N RDAP servers + up to 2 RDAP links + 1 IANA + 1 + maxWhoisReferralHops WHOIS queries)`. Set `deadlineMs` to put a hard cap on the total, e.g. for serverless functions with an execution limit:
|
||||
|
||||
```ts
|
||||
const result = await lookup("example.sh", { timeoutMs: 4000, deadlineMs: 9000 });
|
||||
if (!result.ok && result.errorCode === "timeout") {
|
||||
console.warn(result.errorPhase, result.attempts);
|
||||
}
|
||||
```
|
||||
|
||||
### `DomainRecord` schema
|
||||
|
||||
@@ -536,7 +580,7 @@ interface DomainRecord {
|
||||
- Queries the TLD WHOIS and follows registrar referrals recursively up to `maxWhoisReferralHops` (unless disabled).
|
||||
- Normalizes common key/value variants across gTLD/ccTLD formats (dates, statuses, nameservers, contacts). Availability is inferred from common phrases (best‑effort heuristic).
|
||||
|
||||
Timeouts are enforced per request using a simple race against `timeoutMs` (default 15s). All network I/O is performed with global `fetch` (RDAP) and a raw TCP socket (WHOIS).
|
||||
Each network operation is bounded by `timeoutMs` (default 10s) and cancelled when it elapses (`fetch` is aborted through its `signal`; WHOIS sockets are destroyed); an optional `deadlineMs` bounds the whole lookup. All network I/O is performed with global `fetch` (RDAP) and a raw TCP socket (WHOIS).
|
||||
|
||||
## Development
|
||||
|
||||
|
||||
Reference in New Issue
Block a user