fix: reject rdapOnly together with whoisOnly

With both set, RDAP was skipped and WHOIS queried, breaking rdapOnly's
guarantee (on edge runtimes it failed with a confusing unsupported_runtime).
Fail with invalid_input before querying anything.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jakeandClaude Opus 5.5 committed 2026-10-03 15:46:30 -04:00
1 parent fb7e279a27
commit 1cd15eb85c
4 files changed
+16 -4

No files matched your search

+1 -1
View File
@@ -433,7 +433,7 @@ const result = await lookup("example.com", {
- `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.
- `whoisOnly?: boolean` – Skip RDAP and query WHOIS directly. Setting both `rdapOnly` and `whoisOnly` fails with `invalid_input`.
- `followWhoisReferral?: boolean` – Follow registrar referral from the TLD WHOIS (default `true`).
- `maxWhoisReferralHops?: number` – Maximum registrar WHOIS referral hops to follow (default `2`).
- `rdapFollowLinks?: boolean` – Follow related/entity RDAP links to enrich data (default `true`).
+8
View File
@@ -119,6 +119,14 @@ describe("lookup orchestration", () => {
);
});
it("rejects rdapOnly together with whoisOnly without querying anything", async () => {
const res = await lookup("example.com", { rdapOnly: true, whoisOnly: true, timeoutMs: 200 });
expect(res.ok).toBe(false);
expect(res.errorCode).toBe("invalid_input");
expect(vi.mocked(rdapClient.fetchRdapDomain)).not.toHaveBeenCalled();
expect(vi.mocked(whoisClient.whoisQuery)).not.toHaveBeenCalled();
});
it("respects whoisOnly to skip RDAP entirely", async () => {
const res = await lookup("example.com", {
timeoutMs: 200,
+3
View File
@@ -90,6 +90,9 @@ async function runLookup(
opts: LookupOptions | undefined,
ctx: LookupContext,
): Promise<LookupResult> {
if (opts?.rdapOnly && opts?.whoisOnly) {
return failure(ctx, "invalid_input", "rdapOnly and whoisOnly can't both be set");
}
// Query the ASCII form: " München.DE." becomes "xn--mnchen-3ya.de"
const domain = toAsciiDomain(input);
if (!domain || !isLikelyDomain(domain)) {
+4 -3
View File
@@ -339,9 +339,9 @@ export interface LookupOptions {
* in-flight requests and WHOIS sockets are cancelled and the result has `errorCode: "timeout"`.
*/
deadlineMs?: number;
/** Don't fall back to WHOIS */
/** Don't fall back to WHOIS (not combinable with `whoisOnly`) */
rdapOnly?: boolean;
/** Don't attempt RDAP */
/** Don't attempt RDAP (not combinable with `rdapOnly`) */
whoisOnly?: boolean;
/** Follow referral server (default true) */
followWhoisReferral?: boolean;
@@ -479,7 +479,8 @@ export interface LookupResult {
/**
* Machine-readable reason a lookup (or one attempt within it) failed.
*
* - `invalid_input`: the input does not look like a domain name
* - `invalid_input`: the input does not look like a domain name, or the options conflict
* (`rdapOnly` with `whoisOnly`)
* - `invalid_tld`: the TLD is not valid
* - `timeout`: any timeout, including the overall `deadlineMs`
* - `aborted`: the caller's `AbortSignal` fired