Skip to content

Conversation

@jakejarvis
Copy link
Owner

@jakejarvis jakejarvis commented Oct 30, 2025

Summary by CodeRabbit

  • New Features

    • New public options: provide preloaded bootstrap data and supply a custom fetch implementation; preloaded data takes precedence.
    • Added several public types (bootstrap data, lookup result, registrar/nameserver/status metadata, FetchLike).
    • Renamed public API function: lookup (replaces lookupDomain).
  • Bug Fixes

    • Returns explicit error when WHOIS fallback yields no data instead of attempting a merge.
  • Documentation

    • Documented bootstrap data caching, precedence rules, and customFetch interplay.
  • Tests

    • Comprehensive tests for bootstrap handling, fetch resolution, fallbacks, validation, cancellation, and resolveFetch.

@jakejarvis jakejarvis marked this pull request as ready for review October 30, 2025 22:16
@coderabbitai
Copy link

coderabbitai bot commented Oct 30, 2025

Warning

Rate limit exceeded

@jakejarvis has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 10 minutes and 52 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 9496f4b and 643d06b.

📒 Files selected for processing (3)
  • src/lib/constants.ts (1 hunks)
  • src/lib/dates.ts (3 hunks)
  • src/rdap/bootstrap.ts (1 hunks)

Walkthrough

Adds a public BootstrapData type and options customBootstrapData and customFetch; implements priority-driven bootstrap resolution (custom data → custom URL → default IANA URL), introduces resolveFetch and wires it into RDAP fetch paths, expands public types for lookup results and domain records, and adds/updates tests and docs.

Changes

Cohort / File(s) Summary
Type System & Public API
src/types.ts
Added LookupSource, RegistrarInfo, Contact, Nameserver, StatusEvent, DomainRecord, BootstrapData, LookupOptions (now includes customBootstrapData?, customBootstrapUrl?, customFetch?, includeRaw?), LookupResult, and FetchLike. Expanded domain metadata and JSDoc.
Bootstrap Logic & Tests
src/rdap/bootstrap.ts, src/rdap/bootstrap.test.ts
Implemented priority resolution: use options.customBootstrapData (validated) → options.customBootstrapUrl → default IANA URL (DEFAULT_BOOTSTRAP_URL); use resolveFetch/withTimeout for network fetches; validate customBootstrapData shape and tuples; added comprehensive tests covering precedence, validation errors, fetch fallback, abort signals, deduplication, case/slash normalization, multi-label TLDs, and customFetch interactions.
Fetch Resolver & Tests
src/lib/fetch.ts, src/lib/fetch.test.ts
Added export function resolveFetch(options?: { customFetch?: FetchLike }): FetchLike selecting options.customFetch or global fetch; tests cover customFetch return, fallback to global fetch, preserving reference/signature, and async/custom implementations.
RDAP Fetch Wiring
src/rdap/client.ts, src/rdap/merge.ts
Replaced direct fetch(...) calls with fetchFn = resolveFetch(options) and fetchFn(...), preserving existing timeout, signal, and request shapes; no other logic changes.
Core Lookup & Error Handling
src/index.ts, src/index.test.ts, src/index.smoke.test.ts
Renamed public API function from lookupDomainlookup (tests updated); added guard returning { ok: false, error: "No WHOIS data retrieved" } when WHOIS normalization yields no first record; smoke/tests adjusted to use maybeTest.
Parsing Robustness
src/lib/dates.ts, src/lib/text.ts
Added defensive guards in date parsing and bracketed-form parsing to require capture groups before proceeding, returning undefined or skipping when matches are malformed.
WHOIS Normalization & Tests
src/whois/normalize.ts, src/whois/normalize.test.ts, src/whois/merge.test.ts, src/whois/referral.test.ts
Made status parsing defensive (discard lines without first token), added guards before applying transfer lock regex, and updated tests to use optional chaining / existence checks to avoid runtime assumptions.
RDAP Normalization Tests
src/rdap/normalize.test.ts
Tightened test assertions to check existence before accessing nameserver host fields.
Bootstrap Default URL Constant
src/rdap/bootstrap.ts
Added DEFAULT_BOOTSTRAP_URL = "https://data.iana.org/rdap/dns.json".
Documentation
README.md
Documented Bootstrap Data Caching, exported BootstrapData, customBootstrapData, customFetch; noted customBootstrapUrl is ignored when customBootstrapData is provided and clarified interplay with customFetch.
Build / Config
tsconfig.json, package.json
tsconfig.json adjusted to ES2022, stricter checks, source/declaration maps; package.json dev dependency @types/node bumped 24.9.024.9.2.

Sequence Diagram(s)

sequenceDiagram
    autonumber
    participant Caller
    participant getRdap as getRdapBaseUrlsForTld()
    participant Validate as Validate BootstrapData
    participant ResolveFetch as resolveFetch()
    participant Fetch as Fetch (withTimeout)
    participant Parse as Parse services

    Caller->>getRdap: call(tld, options)
    alt options.customBootstrapData present
        getRdap->>Validate: validate shape (object, services array, [tlds,url[]] tuples)
        alt valid
            Validate->>Parse: extract & normalize base URLs (case/slash/dedupe)
            Parse->>Caller: return base URLs
        else invalid
            Validate->>Caller: throw descriptive Error
        end
    else
        getRdap->>ResolveFetch: resolve fetch implementation (options.customFetch | global)
        ResolveFetch-->>Fetch: fetch(options.customBootstrapUrl || DEFAULT_BOOTSTRAP_URL) with signal/timeout
        alt fetch succeeds
            Fetch->>Parse: parse services → extract/normalize base URLs
            Parse->>Caller: return base URLs
        else fetch fails/timeout
            Fetch->>Caller: return []
        end
    end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~25 minutes

  • Review focus:
    • src/types.ts — confirm exported types reflect runtime structures and JSDoc accuracy.
    • src/rdap/bootstrap.ts — validate input-checking/error messages, precedence rules, tuple validation, normalization/deduplication, and signal/timeout propagation.
    • src/lib/fetch.ts and consumers — ensure resolveFetch forwards RequestInit (including signal) and is compatible with global/custom implementations.
    • Tests (bootstrap.test.ts, fetch.test.ts) — verify mocks assert correct fetch usage and cover abort, failure, customFetch, and caching scenarios.

Poem

🐰 I nibble through the bootstrap hay,
Custom data lights the path today.
Fetch resolved, types sprout new ears,
Tests hop in to calm our fears.
Code and carrots—cheers and cheers! 🥕

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 62.50% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The title "Add support for custom fetch function and/or IANA bootstrap data" accurately describes a real and significant aspect of this pull request. The changes include implementing customFetch and customBootstrapData options, creating the resolveFetch utility function, updating bootstrap resolution logic with priority handling, and extending the public API with corresponding types like FetchLike and BootstrapData. However, the PR also encompasses other substantial changes including a public API function rename (lookupDomainlookup), significant type system expansion (new types and interfaces like DomainRecord, LookupResult, Contact, etc.), and defensive programming enhancements throughout the codebase. The title is therefore partially related to the changeset—it covers a real primary feature but does not encompass all major improvements.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (3)
README.md (1)

120-133: Handle non-OK fetch responses in the caching examples.

If the bootstrap endpoint responds with 4xx/5xx, calling response.json() will throw a confusing error in user code. Showing an explicit response.ok guard in the README keeps the sample resilient and documents best practices for consumers.

Apply this diff in each example before response.json():

   const response = await fetch('https://data.iana.org/rdap/dns.json');
+  if (!response.ok) {
+    throw new Error(`Failed to load bootstrap data: ${response.status} ${response.statusText}`);
+  }
   const data: BootstrapData = await response.json();
src/rdap/bootstrap.ts (1)

35-66: Validate each services entry before iterating.

Right now we only check that services is an array. If a user supplies malformed tuples (e.g., missing the URL array), the subsequent svc[0].map blows up with a TypeError and we lose the nice, actionable error messaging you added. A quick structural check keeps the failure mode consistent and developer-friendly.

You can extend the validation block like this:

   if (!Array.isArray(data.services)) {
     throw new Error(
       'Invalid customBootstrapData: missing or invalid "services" array. See BootstrapData type for required structure.',
     );
   }
+  data.services.forEach((svc, idx) => {
+    if (
+      !Array.isArray(svc) ||
+      svc.length < 2 ||
+      !Array.isArray(svc[0]) ||
+      !Array.isArray(svc[1])
+    ) {
+      throw new Error(
+        `Invalid customBootstrapData: services[${idx}] must be a tuple of [string[], string[]].`,
+      );
+    }
+  });
src/rdap/bootstrap.test.ts (1)

5-7: Restore the original global fetch after the suite.

Overwriting global.fetch at module scope leaves the mock in place for every other test file in the run, which can cause hard-to-track leaks once another suite needs the real implementation. Cache the original and restore it in afterAll, or use vi.stubGlobal so Vitest handles cleanup automatically.

A minimal fix would be:

-// Mock the fetch function
-global.fetch = vi.fn();
+const originalFetch = global.fetch;
+
+beforeAll(() => {
+  vi.stubGlobal("fetch", vi.fn());
+});
+
+afterAll(() => {
+  global.fetch = originalFetch;
+});
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8c8dec5 and 1f2543d.

📒 Files selected for processing (4)
  • README.md (2 hunks)
  • src/rdap/bootstrap.test.ts (1 hunks)
  • src/rdap/bootstrap.ts (1 hunks)
  • src/types.ts (5 hunks)
🧰 Additional context used
🧬 Code graph analysis (2)
src/rdap/bootstrap.test.ts (2)
src/types.ts (1)
  • BootstrapData (200-213)
src/rdap/bootstrap.ts (1)
  • getRdapBaseUrlsForTld (20-72)
src/rdap/bootstrap.ts (3)
src/types.ts (2)
  • LookupOptions (239-290)
  • BootstrapData (200-213)
src/lib/async.ts (1)
  • withTimeout (1-17)
src/lib/constants.ts (1)
  • DEFAULT_TIMEOUT_MS (1-1)

@jakejarvis jakejarvis changed the title Add support for custom pre-loaded IANA bootstrap data Add support for custom fetch function and/or IANA bootstrap data Oct 30, 2025
@jakejarvis jakejarvis force-pushed the feat/custom-bootstrap-data branch from 7f1c346 to d38681f Compare October 30, 2025 22:40
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

🧹 Nitpick comments (1)
src/rdap/bootstrap.ts (1)

6-6: Excellent priority-driven bootstrap resolution with thorough validation!

The implementation clearly follows the documented priority order (customBootstrapData → customBootstrapUrl → default IANA URL) and includes comprehensive validation with helpful error messages. The use of resolveFetch properly enables custom fetch implementations, and the fallback to an empty array on fetch failure is appropriate.

Optional: Consider treating explicit undefined the same as missing option

The current check "customBootstrapData" in options (line 28) will throw an error if a user passes { customBootstrapData: undefined } explicitly, rather than falling back to fetching. While this is caught by validation with a clear error message, it might be more user-friendly to treat undefined the same as a missing option.

For example, a user with optional caching might write:

const cached = await getFromCache(); // returns BootstrapData | undefined
await lookup('example.com', { customBootstrapData: cached });

Consider using:

-if (options && "customBootstrapData" in options) {
+if (options?.customBootstrapData !== undefined) {
   data = options.customBootstrapData;

This would allow undefined to fall through to the fetch logic naturally. However, this is a minor edge case and the current behavior is defensible.

Also applies to: 11-19, 25-69

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 7f1c346 and d38681f.

📒 Files selected for processing (8)
  • README.md (2 hunks)
  • src/lib/fetch.test.ts (1 hunks)
  • src/lib/fetch.ts (1 hunks)
  • src/rdap/bootstrap.test.ts (1 hunks)
  • src/rdap/bootstrap.ts (1 hunks)
  • src/rdap/client.ts (2 hunks)
  • src/rdap/merge.ts (2 hunks)
  • src/types.ts (5 hunks)
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/rdap/merge.ts
  • src/lib/fetch.test.ts
  • src/rdap/bootstrap.test.ts
🧰 Additional context used
🧬 Code graph analysis (3)
src/lib/fetch.ts (1)
src/types.ts (1)
  • FetchLike (375-378)
src/rdap/bootstrap.ts (4)
src/types.ts (2)
  • LookupOptions (239-339)
  • BootstrapData (200-213)
src/lib/fetch.ts (1)
  • resolveFetch (26-28)
src/lib/async.ts (1)
  • withTimeout (1-17)
src/lib/constants.ts (1)
  • DEFAULT_TIMEOUT_MS (1-1)
src/rdap/client.ts (2)
src/lib/fetch.ts (1)
  • resolveFetch (26-28)
src/lib/async.ts (1)
  • withTimeout (1-17)
🪛 LanguageTool
README.md

[style] ~419-~419: Consider replacing ‘only’ with a different word to let your writing stand out.
Context: ...t be fetched, so your custom fetch will only be used for RDAP domain and entity/rela...

(ONLY_EXCLUSIVELY_STYLE)

🔇 Additional comments (4)
src/types.ts (1)

1-378: Excellent public API documentation!

The comprehensive JSDoc comments and code examples significantly improve the developer experience. The type definitions are well-structured and the new BootstrapData, LookupResult, and FetchLike types provide clear contracts for the new functionality.

src/rdap/client.ts (1)

3-3: Clean integration of fetch resolution!

The changes properly wire in the resolveFetch utility while maintaining existing timeout behavior via withTimeout. The dependency injection pattern enables custom fetch implementations without disrupting the existing logic.

Also applies to: 19-21

src/lib/fetch.ts (1)

1-28: Well-designed utility function!

The resolveFetch utility provides a clean, centralized way to resolve fetch implementations across all RDAP HTTP operations. The comprehensive JSDoc documentation with usage examples makes the intent and usage clear.

README.md (1)

89-420: Outstanding documentation with practical examples!

The new documentation sections for bootstrap data caching and custom fetch implementation are comprehensive and user-friendly. The multiple examples covering different use cases (in-memory, Redis, filesystem, retry logic, etc.) make it easy for developers to implement these features in their specific contexts. The explanations of why these features are useful provide excellent motivation.

Also applies to: 431-433

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/lib/dates.ts (1)

101-101: Good defensive guards.

These checks prevent invalid Date construction when regex captures are unexpectedly missing. The guards correctly validate the destructured variables before usage, improving robustness.

For consistency, consider adding similar guards to the time-based parsing path:

 if (m[0].includes(":")) {
   const [_, y, mo, d, hh, mm, ss, offH, offM] = m;
+  if (!y || !mo || !d || !hh || !mm || !ss) return undefined;
   // Base time as UTC
   let dt = Date.UTC(

Also applies to: 113-113

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between d38681f and 9496f4b.

⛔ Files ignored due to path filters (1)
  • package-lock.json is excluded by !**/package-lock.json
📒 Files selected for processing (14)
  • package.json (1 hunks)
  • src/index.smoke.test.ts (3 hunks)
  • src/index.test.ts (5 hunks)
  • src/index.ts (1 hunks)
  • src/lib/dates.ts (2 hunks)
  • src/lib/text.ts (1 hunks)
  • src/rdap/bootstrap.ts (1 hunks)
  • src/rdap/normalize.test.ts (1 hunks)
  • src/types.ts (5 hunks)
  • src/whois/merge.test.ts (1 hunks)
  • src/whois/normalize.test.ts (1 hunks)
  • src/whois/normalize.ts (2 hunks)
  • src/whois/referral.test.ts (1 hunks)
  • tsconfig.json (1 hunks)
🧰 Additional context used
🧬 Code graph analysis (3)
src/index.test.ts (1)
src/index.ts (1)
  • lookup (23-122)
src/index.smoke.test.ts (1)
src/index.ts (3)
  • lookup (23-122)
  • isRegistered (141-148)
  • isAvailable (128-135)
src/rdap/bootstrap.ts (4)
src/types.ts (2)
  • LookupOptions (239-339)
  • BootstrapData (200-213)
src/lib/fetch.ts (1)
  • resolveFetch (26-28)
src/lib/async.ts (1)
  • withTimeout (1-17)
src/lib/constants.ts (1)
  • DEFAULT_TIMEOUT_MS (1-1)
🔇 Additional comments (16)
package.json (1)

53-53: LGTM: Type definitions patch update.

The @types/node update from 24.9.0 to 24.9.2 is a standard patch version bump that keeps Node.js type definitions current without introducing breaking changes.

src/whois/merge.test.ts (1)

32-33: Good defensive test guard.

The runtime check ensures the test fails explicitly with a clear message if the chain is unexpectedly empty, rather than failing obscurely on the destructuring or subsequent assertions.

src/lib/text.ts (1)

15-15: LGTM: Stricter bracket parsing guard.

The updated condition ensures both capture groups are defined before processing the bracketed form, preventing edge cases where the regex match succeeds but groups are unexpectedly undefined.

src/whois/normalize.test.ts (1)

14-15: LGTM: Defensive test assertions.

The two-step assertion pattern (first checking array existence, then accessing nested properties with optional chaining) makes test failures more specific and aligns with similar updates across test files.

src/rdap/normalize.test.ts (1)

71-72: LGTM: Consistent defensive test pattern.

The two-step assertion matches the pattern introduced in src/whois/normalize.test.ts, ensuring consistent test practices across the codebase.

src/whois/normalize.ts (2)

158-164: LGTM: Stricter status parsing with null filtering.

The updated logic correctly filters out status lines where the first token is empty or undefined, ensuring only valid status objects are included. The type guard provides proper type narrowing for TypeScript.


227-229: LGTM: Defensive transfer lock check.

The additional guard ensures s.status exists before applying the regex, preventing potential runtime issues with missing status fields. This aligns with the stricter type checking enabled in tsconfig.json (noUncheckedIndexedAccess).

tsconfig.json (1)

4-23: LGTM: Enhanced type safety and modern target.

The compiler configuration updates introduce several beneficial changes:

  • ES2022 target: Modern JavaScript features with good runtime support
  • Stricter type checking: noUncheckedIndexedAccess, noUnusedParameters, noFallthroughCasesInSwitch, and forceConsistentCasingInFileNames help catch bugs at compile time
  • Better debugging: declarationMap and sourceMap enable source-level debugging

The noUncheckedIndexedAccess option is particularly strict and explains the defensive coding patterns introduced throughout this PR (e.g., explicit undefined checks in normalize.ts and tests).

src/types.ts (4)

179-213: Excellent BootstrapData interface design.

The interface accurately models the IANA RDAP bootstrap JSON structure with:

  • Clear documentation and JSON example
  • Proper RFC 7484 reference
  • Type-safe services array structure

This enables users to implement their own caching strategies for bootstrap data.


256-332: Outstanding documentation for customFetch option.

The documentation provides:

  • Clear explanation of the feature's purpose
  • Multiple practical use cases (caching, logging, retry, rate limiting, proxies, testing)
  • Two concrete code examples
  • Proper linkage to the FetchLike type

This empowers users to implement sophisticated HTTP request handling strategies.


1-367: Well-structured public API expansion.

The type definitions introduce a clean, well-documented public API with:

  • Comprehensive JSDoc comments throughout
  • Practical code examples
  • Clear relationships between types
  • Appropriate granularity (RegistrarInfo, Contact, Nameserver, StatusEvent)

The additions enable advanced use cases (custom bootstrap data, custom fetch implementations) while maintaining backward compatibility.


375-378: FetchLike type narrowing is correct and intentional—no changes needed.

Verification confirms the narrowing from RequestInfo | URL to string | URL is safe and consistent with actual usage:

  • No code instantiates or passes Request objects to custom fetch functions
  • All test invocations use string URLs with init containing only standard properties (method, signal)
  • Documentation examples match the implementation
  • The change aligns with the actual API contract

The concern raised in the original review comment does not reflect the codebase's actual usage patterns.

src/whois/referral.test.ts (1)

43-44: Safer chain assertion acknowledged

Thanks for guarding the optional referral entry while keeping the assertion intact.

src/index.ts (1)

113-115: Good short-circuit for missing WHOIS data

Appreciate the explicit failure response when normalization produces nothing; this avoids merging undefined records and surfaces a clear error.

src/index.test.ts (1)

84-110: Test coverage still exercises full orchestration

Nice job updating the orchestration tests to call lookup directly; this keeps the original scenarios intact while covering the renamed public entry point.

src/index.smoke.test.ts (1)

7-21: Conditional smoke harness works well

Using maybeTest keeps the smoke suite available locally while skipping by default in CI—sensible toggle.

… minute, and second components in parseDateWithRegex function
…-catch block to manage network, timeout, and JSON parse errors, while preserving cancellation behavior.
@jakejarvis jakejarvis merged commit 619185b into main Oct 30, 2025
1 check passed
@jakejarvis jakejarvis deleted the feat/custom-bootstrap-data branch October 30, 2025 23:07
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants