rdapper 0.11.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -407,4 +407,4 @@ declare function isRegistered(domain: string, opts?: LookupOptions): Promise<boo
407
407
  declare const lookupDomain: typeof lookup;
408
408
  //#endregion
409
409
  export { BootstrapData, Contact, DomainRecord, FetchLike, LookupOptions, LookupResult, LookupSource, Nameserver, RegistrarInfo, StatusEvent, getDomainParts, getDomainTld, isAvailable, isLikelyDomain, isRegistered, lookup, lookupDomain, toRegistrableDomain };
410
- //# sourceMappingURL=index.d.ts.map
410
+ //# sourceMappingURL=index.d.mts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.mts","names":[],"sources":["../src/types.ts","../src/lib/domain.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;AAMA;AAQA;AAoBA;AA6BiB,KAzDL,YAAA,GAyDe,MAAA,GAAA,OAAA;AAoB3B;AAkCA;;;;;AA4Ca,UAnJI,aAAA,CAmJJ;EAYH;EAAY,IAAA,CAAA,EAAA,MAAA;EA0BL;EAuCA,MAAA,CAAA,EAAA,MAAA;EA0CO;EAmDR,GAAA,CAAA,EAAA,MAAA;EAED;EAIJ,KAAA,CAAA,EAAA,MAAA;EAAW;EAsBL,KAAA,CAAA,EAAA,MAAA;AAejB;;;;;;;;UApViB,OAAA;EChCZ,IAAA,EAAA,YAAY,GAAA,OAAqB,GAAA,MAAlB,GAAA,SAAU,GAAA,OAAA,GAAA,WAAA,GAAA,UAAA,GAAA,SAAA;EAMd,IAAA,CAAA,EAAA,MAAA;EAEP,YAAA,CAAA,EAAA,MAAA;EACY,KAAA,CAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAAlB,KAAA,CAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAAU,GAAA,CAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAQG,MAAA,CAAA,EAAA,MAAY,EAAA;EAcZ,IAAA,CAAA,EAAA,MAAA;EAuBA,KAAA,CAAA,EAAA,MAAA;;;;AClChB;;;;;AAyGA;AAaA;AAYa,UFzFI,UAAA,CEyFiB;;;;;;;;;;;;;;;;;;;UFrEjB,WAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAkCA,YAAA;;;;;;;;;;;;;;;;cAgBH;;;;aAID;;;;;;;;;;;;;;gBAcG;;;;;;;;gBAQA;;aAEH;;;;;;;;;;;;UAYH;;;;;;;;;;;;;;;;;;;;;;;;;UA0BO,aAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAuCA,aAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA0CO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAmDR;;eAED;;;;WAIJ;;;;;;;;;;;;;;;;;;;;;UAsBM,YAAA;;;;WAIN;;;;;;;;;;KAWC,SAAA,oBACM,YACT,gBACJ,QAAQ;;;KCvXR,YAAA,GAAe,kBAAkB;;ADItC;AAQA;AAoBA;AA6BiB,iBCvDD,cAAA,CDuDW,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,ECrDlB,YDqDkB,CAAA,ECpDxB,UDoDwB,CAAA,OCpDN,KDoDM,CAAA;AAoB3B;AAkCA;;;AAkCgB,iBCpIA,YAAA,CDoIA,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EClIP,YDkIO,CAAA,EAAA,MAAA,GAAA,IAAA;;;;AAsBM,iBC5IN,cAAA,CD4IM,KAAA,EAAA,MAAA,CAAA,EAAA,OAAA;AAiEtB;;;;;;AAyHiB,iBC/SD,mBAAA,CDmTO,KAAA,EAAA,MAAA,EAAA,IAAA,CAAA,ECjTd,YDiTc,CAAA,EAAA,MAAA,GAAA,IAAA;;;;;AArWvB;AAQA;AAoBiB,iBEZK,MAAA,CFYE,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EEVf,aFUe,CAAA,EETrB,OFSqB,CETb,YFSa,CAAA;AA6BxB;AAoBA;AAkCA;;AAoBa,iBEVS,WAAA,CFUT,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EERJ,aFQI,CAAA,EEPV,OFOU,CAAA,OAAA,CAAA;;;;;AAoCS,iBEjCA,YAAA,CFiCA,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EE/Bb,aF+Ba,CAAA,EE9BnB,OF8BmB,CAAA,OAAA,CAAA;AA0BtB;AAuCA;;AA6FgB,cEnLH,YFmLG,EAAA,OEnLS,MFmLT"}
@@ -518,7 +518,7 @@ function normalizeRdap(inputDomain, tld, rdap, rdapServersTried, includeRaw = fa
518
518
  const updatedDate = toISO(asDateLike(byAction("last changed")?.eventDate) ?? asDateLike(doc.lastChangedDate));
519
519
  const expirationDate = toISO(asDateLike(byAction("expiration")?.eventDate) ?? asDateLike(doc.expirationDate));
520
520
  const deletionDate = toISO(asDateLike(byAction("deletion")?.eventDate) ?? asDateLike(doc.deletionDate));
521
- const transferLock = !!statuses?.some((s) => /transferprohibited/i.test(s.status));
521
+ const transferLock = !!statuses?.some((s) => /transfer[-\s]*prohibited/i.test(s.status));
522
522
  const whoisServer = asString(doc.port43);
523
523
  return {
524
524
  domain: unicodeName || ldhName || inputDomain,
@@ -933,16 +933,25 @@ const WHOIS_AVAILABLE_PATTERNS = [
933
933
  /\bnot found\b/i,
934
934
  /\bno entries found\b/i,
935
935
  /\bno data found\b/i,
936
+ /\bno information available\b/i,
937
+ /\bno information was found\b/i,
938
+ /\bno data was found\b/i,
936
939
  /\bavailable for registration\b/i,
937
940
  /\bdomain\s+available\b/i,
938
941
  /\bdomain status[:\s]+available\b/i,
939
942
  /\bobject does not exist\b/i,
940
943
  /\bthe queried object does not exist\b/i,
941
944
  /\bqueried object does not exist\b/i,
945
+ /\bdoes not exist\b/i,
942
946
  /\breturned 0 objects\b/i,
947
+ /\bnot been registered\b/i,
948
+ /\bunassignable\b/i,
949
+ /\bis free\b/i,
943
950
  /\bstatus:\s*free\b/i,
944
951
  /\bstatus:\s*available\b/i,
945
952
  /\bno object found\b/i,
953
+ /\bobject_not_found\b/i,
954
+ /\bno se encuentra registrado\b/i,
946
955
  /\bnicht gefunden\b/i,
947
956
  /\bpending release\b/i
948
957
  ];
@@ -1086,7 +1095,7 @@ function normalizeWhois(domain, tld, whoisText, whoisServer, includeRaw = false)
1086
1095
  const privacyEnabled = !!(registrant && [registrant.name, registrant.organization].filter(Boolean).some(isPrivacyName));
1087
1096
  const dnssecRaw = (map.dnssec?.[0] || "").toLowerCase();
1088
1097
  const dnssec = dnssecRaw ? { enabled: /signed|yes|true/.test(dnssecRaw) } : void 0;
1089
- const transferLock = !!statuses?.some((s) => s.status && /transferprohibited/i.test(s.status));
1098
+ const transferLock = !!statuses?.some((s) => /transfer[-\s]*prohibited/i.test(s.raw || s.status || ""));
1090
1099
  return {
1091
1100
  domain,
1092
1101
  tld,
@@ -1377,4 +1386,4 @@ const lookupDomain = lookup;
1377
1386
 
1378
1387
  //#endregion
1379
1388
  export { getDomainParts, getDomainTld, isAvailable, isLikelyDomain, isRegistered, lookup, lookupDomain, toRegistrableDomain };
1380
- //# sourceMappingURL=index.js.map
1389
+ //# sourceMappingURL=index.mjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.mjs","names":["timer: ReturnType<typeof setTimeout> | undefined","data: BootstrapData","err: unknown","bases: string[]","out: string[]","merged: Json","tried: string[]","fetchedDocs: unknown[]","out: T[]","monthMap: Record<string, number>","_","dd","monStr","yyyy","mon","lastKey: string | undefined","ldhName: string | undefined","unicodeName: string | undefined","registrar: RegistrarInfo | undefined","nameservers: Nameserver[] | undefined","n: Nameserver","contacts: Contact[] | undefined","events: RdapEvent[]","whoisServer: string | undefined","out: Contact[]","roles: string[]","out: ParsedVCard","WHOIS_QUERY_TRANSFORMERS: Record<string, (query: string) => string>","net: typeof import(\"node:net\") | null","out: NonNullable<DomainRecord[\"statuses\"]>","out: Contact[]","merged: DomainRecord","WHOIS_AVAILABLE_PATTERNS: RegExp[]","registrar: RegistrarInfo | undefined","nsLines: string[]","nameservers: Nameserver[] | undefined","ipv4: string[]","ipv6: string[]","ns: Nameserver","roles: Array<{\n role: Contact[\"type\"];\n prefixes: string[];\n }>","contacts: Contact[]","nameKeys: string[]","orgKeys: string[]","emailKeys: string[]","phoneKeys: string[]","faxKeys: string[]","streetKeys: string[]","cityKeys: string[]","stateKeys: string[]","postalCodeKeys: string[]","countryKeys: string[]","results: WhoisQueryResult[]","tried: string[]","err: unknown"],"sources":["../src/lib/domain.ts","../src/lib/async.ts","../src/lib/constants.ts","../src/lib/fetch.ts","../src/rdap/bootstrap.ts","../src/rdap/client.ts","../src/rdap/links.ts","../src/rdap/merge.ts","../src/lib/dates.ts","../src/lib/privacy.ts","../src/lib/text.ts","../src/rdap/normalize.ts","../src/whois/client.ts","../src/whois/servers.ts","../src/whois/discovery.ts","../src/whois/merge.ts","../src/whois/normalize.ts","../src/whois/referral.ts","../src/index.ts"],"sourcesContent":["import { parse } from \"tldts\";\n\ntype ParseOptions = Parameters<typeof parse>[1];\n\n/**\n * Parse a domain into its parts. Passes options to `tldts.parse()`.\n * @see https://github.com/remusao/tldts/blob/master/packages/tldts-core/src/options.ts\n */\nexport function getDomainParts(\n domain: string,\n opts?: ParseOptions,\n): ReturnType<typeof parse> {\n return parse(domain, { ...opts });\n}\n\n/**\n * Get the TLD (ICANN-only public suffix) of a domain. Passes options to `tldts.parse()`.\n * @see https://github.com/remusao/tldts/blob/master/packages/tldts-core/src/options.ts\n */\nexport function getDomainTld(\n domain: string,\n opts?: ParseOptions,\n): string | null {\n const result = getDomainParts(domain, {\n allowPrivateDomains: false,\n ...opts,\n });\n return result.publicSuffix ?? null;\n}\n\n/**\n * Basic domain validity check (hostname-like), not performing DNS or RDAP.\n */\nexport function isLikelyDomain(value: string): boolean {\n const v = (value ?? \"\").trim();\n // Accept punycoded labels (xn--) by allowing digits and hyphens in TLD as well,\n // while disallowing leading/trailing hyphens in any label.\n return /^(?=.{1,253}$)(?:(?!-)[a-z0-9-]{1,63}(?<!-)\\.)+(?!-)[a-z0-9-]{2,63}(?<!-)$/.test(\n v.toLowerCase(),\n );\n}\n\nexport function punyToUnicode(domain: string): string {\n try {\n return domain.normalize(\"NFC\");\n } catch {\n return domain;\n }\n}\n\n/**\n * Normalize arbitrary input (domain or URL) to its registrable domain (eTLD+1).\n * Passes options to `tldts.parse()`.\n * Returns null when the input is not a valid ICANN domain (e.g., invalid TLD, IPs)\n * @see https://github.com/remusao/tldts/blob/master/packages/tldts-core/src/options.ts\n */\nexport function toRegistrableDomain(\n input: string,\n opts?: ParseOptions,\n): string | null {\n const raw = (input ?? \"\").trim();\n if (raw === \"\") return null;\n\n const result = getDomainParts(raw, {\n allowPrivateDomains: false,\n ...opts,\n });\n\n // Reject IPs and non-ICANN/public suffixes.\n if (result.isIp) return null;\n if (!result.isIcann) return null;\n\n const domain = result.domain ?? \"\";\n if (domain === \"\") return null;\n return domain.toLowerCase();\n}\n","export function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n reason = \"Timeout\",\n): Promise<T> {\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return promise;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(reason)), timeoutMs);\n });\n return Promise.race([\n promise.finally(() => {\n if (timer !== undefined) clearTimeout(timer);\n }),\n timeout,\n ]);\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * The timeout for HTTP requests in milliseconds. Defaults to 10 seconds.\n */\nexport const DEFAULT_TIMEOUT_MS = 10_000 as const;\n\n/**\n * The default URL for the IANA RDAP bootstrap file.\n *\n * @see {@link https://data.iana.org/rdap/dns.json IANA RDAP Bootstrap File (dns.json)}\n */\nexport const DEFAULT_BOOTSTRAP_URL = \"https://data.iana.org/rdap/dns.json\";\n","import type { FetchLike } from \"../types\";\n\n/**\n * Resolve the fetch implementation to use for HTTP requests.\n *\n * Returns the custom fetch from options if provided, otherwise falls back\n * to the global fetch function. This centralized helper ensures consistent\n * fetch resolution across all RDAP HTTP operations.\n *\n * Used internally by:\n * - Bootstrap registry fetching (`src/rdap/bootstrap.ts`)\n * - RDAP domain lookups (`src/rdap/client.ts`)\n * - RDAP related/entity link requests (`src/rdap/merge.ts`)\n *\n * @param options - Any object that may contain a custom fetch implementation\n * @returns The fetch function to use for HTTP requests\n *\n * @example\n * ```ts\n * import { resolveFetch } from './lib/fetch';\n *\n * const fetchFn = resolveFetch(options);\n * const response = await fetchFn('https://example.com/api', { method: 'GET' });\n * ```\n */\nexport function resolveFetch(options?: { customFetch?: FetchLike }): FetchLike {\n return options?.customFetch ?? fetch;\n}\n","import { withTimeout } from \"../lib/async\";\nimport { DEFAULT_BOOTSTRAP_URL, DEFAULT_TIMEOUT_MS } from \"../lib/constants\";\nimport { resolveFetch } from \"../lib/fetch\";\nimport type { BootstrapData, LookupOptions } from \"../types\";\n\n/**\n * Resolve RDAP base URLs for a given TLD using IANA's bootstrap registry.\n * Returns zero or more base URLs (always suffixed with a trailing slash).\n *\n * Bootstrap data is resolved in the following priority order:\n * 1. `options.customBootstrapData` - pre-loaded bootstrap data (no fetch)\n * 2. `options.customBootstrapUrl` - custom URL to fetch bootstrap data from\n * 3. Default IANA URL - https://data.iana.org/rdap/dns.json\n *\n * @param tld - The top-level domain to look up (e.g., \"com\", \"co.uk\")\n * @param options - Optional lookup options including custom bootstrap data/URL\n * @returns Array of RDAP base URLs for the TLD, or empty array if none found\n */\nexport async function getRdapBaseUrlsForTld(\n tld: string,\n options?: LookupOptions,\n): Promise<string[]> {\n let data: BootstrapData;\n\n // Priority 1: Use pre-loaded bootstrap data if provided (no fetch)\n if (options && \"customBootstrapData\" in options) {\n const provided = options.customBootstrapData;\n // Validate the structure to provide helpful error messages\n if (!provided || typeof provided !== \"object\") {\n throw new Error(\n \"Invalid customBootstrapData: expected an object. See BootstrapData type for required structure.\",\n );\n }\n if (!Array.isArray(provided.services)) {\n throw new Error(\n 'Invalid customBootstrapData: missing or invalid \"services\" array. See BootstrapData type for required structure.',\n );\n }\n provided.services.forEach((svc, idx) => {\n if (\n !Array.isArray(svc) ||\n svc.length < 2 ||\n !Array.isArray(svc[0]) ||\n !Array.isArray(svc[1])\n ) {\n throw new Error(\n `Invalid customBootstrapData: services[${idx}] must be a tuple of [string[], string[]].`,\n );\n }\n });\n data = provided;\n } else {\n // Priority 2 & 3: Fetch from custom URL or default IANA URL\n // Use custom fetch implementation if provided for caching/logging/monitoring\n const fetchFn = resolveFetch(options);\n const bootstrapUrl = options?.customBootstrapUrl ?? DEFAULT_BOOTSTRAP_URL;\n try {\n const res = await withTimeout(\n fetchFn(bootstrapUrl, {\n method: \"GET\",\n headers: { accept: \"application/json\" },\n signal: options?.signal,\n }),\n options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n \"RDAP bootstrap timeout\",\n );\n if (!res.ok) return [];\n data = (await res.json()) as BootstrapData;\n } catch (err: unknown) {\n // Preserve caller cancellation behavior - rethrow if explicitly aborted\n if (err instanceof Error && err.name === \"AbortError\") {\n throw err;\n }\n // Network, timeout, or JSON parse errors - return empty array to fall back to WHOIS\n return [];\n }\n }\n\n // Parse the bootstrap data to find matching base URLs for the TLD\n const target = tld.toLowerCase();\n const bases: string[] = [];\n for (const svc of data.services) {\n if (!svc[0] || !svc[1]) continue;\n const tlds = svc[0].map((x) => x.toLowerCase());\n const urls = svc[1];\n // Match exact TLD, and also support multi-label public suffixes present in IANA (rare)\n if (tlds.includes(target)) {\n for (const u of urls) {\n const base = u.endsWith(\"/\") ? u : `${u}/`;\n bases.push(base);\n }\n }\n }\n return Array.from(new Set(bases));\n}\n","import { withTimeout } from \"../lib/async\";\nimport { DEFAULT_TIMEOUT_MS } from \"../lib/constants\";\nimport { resolveFetch } from \"../lib/fetch\";\nimport type { LookupOptions } from \"../types\";\n\n/**\n * Fetch RDAP JSON for a domain from a specific RDAP base URL.\n * Throws on HTTP >= 400 (includes RDAP error JSON payloads).\n */\nexport async function fetchRdapDomain(\n domain: string,\n baseUrl: string,\n options?: LookupOptions,\n): Promise<{ url: string; json: unknown }> {\n const url = new URL(\n `domain/${encodeURIComponent(domain)}`,\n baseUrl,\n ).toString();\n const fetchFn = resolveFetch(options);\n const res = await withTimeout(\n fetchFn(url, {\n method: \"GET\",\n headers: { accept: \"application/rdap+json, application/json\" },\n signal: options?.signal,\n }),\n options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n \"RDAP lookup timeout\",\n );\n if (!res.ok) {\n const bodyText = await res.text();\n throw new Error(`RDAP ${res.status}: ${bodyText.slice(0, 500)}`);\n }\n const json = await res.json();\n return { url, json };\n}\n","import type { LookupOptions } from \"../types\";\n\ntype RdapLink = {\n value?: string;\n rel?: string;\n href?: string;\n type?: string;\n};\n\n/** Extract candidate RDAP link URLs from an RDAP document. */\nexport function extractRdapRelatedLinks(\n doc: unknown,\n opts?: Pick<LookupOptions, \"rdapLinkRels\">,\n): string[] {\n const rels = (\n opts?.rdapLinkRels?.length\n ? opts.rdapLinkRels\n : [\"related\", \"entity\", \"registrar\", \"alternate\"]\n ).map((r) => r.toLowerCase());\n const d = (doc ?? {}) as Record<string, unknown> & { links?: RdapLink[] };\n const arr = Array.isArray(d?.links) ? (d.links as RdapLink[]) : [];\n const out: string[] = [];\n for (const link of arr) {\n const rel = String(link.rel || \"\").toLowerCase();\n const type = String(link.type || \"\").toLowerCase();\n if (!rels.includes(rel)) continue;\n if (type && !/application\\/rdap\\+json/i.test(type)) continue;\n const url = link.href || link.value;\n if (url && /^https?:\\/\\//i.test(url)) out.push(url);\n }\n return Array.from(new Set(out));\n}\n","import { withTimeout } from \"../lib/async\";\nimport { DEFAULT_TIMEOUT_MS } from \"../lib/constants\";\nimport { resolveFetch } from \"../lib/fetch\";\nimport type { LookupOptions } from \"../types\";\nimport { extractRdapRelatedLinks } from \"./links\";\n\ntype Json = Record<string, unknown>;\n\n/** Merge RDAP documents with a conservative, additive strategy. */\nexport function mergeRdapDocs(baseDoc: unknown, others: unknown[]): unknown {\n const merged: Json = { ...(baseDoc as Json) };\n for (const doc of others) {\n const cur = (doc ?? {}) as Json;\n // status: array of strings\n merged.status = uniqStrings([\n ...toStringArray(merged.status),\n ...toStringArray(cur.status),\n ]);\n // events: array of objects; dedupe by eventAction + eventDate\n merged.events = uniqBy(\n [...toArray<Json>(merged.events), ...toArray<Json>(cur.events)],\n (e) =>\n `${String(e?.eventAction ?? \"\").toLowerCase()}|${String(e?.eventDate ?? \"\")}`,\n );\n // nameservers: array of objects; dedupe by ldhName/unicodeName\n merged.nameservers = uniqBy(\n [...toArray<Json>(merged.nameservers), ...toArray<Json>(cur.nameservers)],\n (n) => `${String(n?.ldhName ?? n?.unicodeName ?? \"\").toLowerCase()}`,\n );\n // entities: array; dedupe by handle if present, else by roles+vcard hash\n merged.entities = uniqBy(\n [...toArray<Json>(merged.entities), ...toArray<Json>(cur.entities)],\n (e) =>\n `${String(e?.handle ?? \"\").toLowerCase()}|${String(\n JSON.stringify(e?.roles || []),\n ).toLowerCase()}|${String(JSON.stringify(e?.vcardArray || [])).toLowerCase()}`,\n );\n // secureDNS: prefer existing; fill if missing\n if (merged.secureDNS == null && cur.secureDNS != null)\n merged.secureDNS = cur.secureDNS;\n // port43 (authoritative WHOIS): prefer existing; fill if missing\n if (merged.port43 == null && cur.port43 != null) merged.port43 = cur.port43;\n // remarks: concat simple strings if present\n const mergedRemarks = (merged as { remarks?: Json[] }).remarks;\n const curRemarks = (cur as { remarks?: Json[] }).remarks;\n if (Array.isArray(mergedRemarks) || Array.isArray(curRemarks)) {\n const a = toArray<Json>(mergedRemarks);\n const b = toArray<Json>(curRemarks);\n (merged as { remarks?: Json[] }).remarks = [...a, ...b];\n }\n }\n return merged;\n}\n\n/** Fetch and merge RDAP related documents up to a hop limit. */\nexport async function fetchAndMergeRdapRelated(\n domain: string,\n baseDoc: unknown,\n opts?: LookupOptions,\n): Promise<{ merged: unknown; serversTried: string[] }> {\n const tried: string[] = [];\n if (opts?.rdapFollowLinks === false)\n return { merged: baseDoc, serversTried: tried };\n const maxHops = Math.max(0, opts?.maxRdapLinkHops ?? 2);\n if (maxHops === 0) return { merged: baseDoc, serversTried: tried };\n\n const visited = new Set<string>();\n let current = baseDoc;\n let hops = 0;\n\n // BFS: collect links from the latest merged doc only to keep it simple and bounded\n while (hops < maxHops) {\n const links = extractRdapRelatedLinks(current, {\n rdapLinkRels: opts?.rdapLinkRels,\n });\n const nextBatch = links.filter((u) => !visited.has(u));\n if (nextBatch.length === 0) break;\n const fetchedDocs: unknown[] = [];\n for (const url of nextBatch) {\n visited.add(url);\n try {\n const { json } = await fetchRdapUrl(url, opts);\n tried.push(url);\n // only accept docs that appear related to the same domain when possible\n // if ldhName/unicodeName present, they should match the queried domain (case-insensitive)\n const ldh = String((json as Json)?.ldhName ?? \"\").toLowerCase();\n const uni = String((json as Json)?.unicodeName ?? \"\").toLowerCase();\n if (ldh && !sameDomain(ldh, domain)) continue;\n if (uni && !sameDomain(uni, domain)) continue;\n fetchedDocs.push(json);\n } catch {\n // ignore failures and continue\n }\n }\n if (fetchedDocs.length === 0) break;\n current = mergeRdapDocs(current, fetchedDocs);\n hops += 1;\n }\n return { merged: current, serversTried: tried };\n}\n\nasync function fetchRdapUrl(\n url: string,\n options?: LookupOptions,\n): Promise<{ url: string; json: unknown }> {\n const fetchFn = resolveFetch(options);\n const res = await withTimeout(\n fetchFn(url, {\n method: \"GET\",\n headers: { accept: \"application/rdap+json, application/json\" },\n signal: options?.signal,\n }),\n options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n \"RDAP link fetch timeout\",\n );\n if (!res.ok) {\n const bodyText = await res.text();\n throw new Error(`RDAP ${res.status}: ${bodyText.slice(0, 500)}`);\n }\n const json = await res.json();\n // Optionally parse Link header for future iterations; the main loop inspects body.links\n return { url, json };\n}\n\nfunction toArray<T>(val: unknown): T[] {\n return Array.isArray(val) ? (val as T[]) : [];\n}\nfunction toStringArray(val: unknown): string[] {\n return Array.isArray(val) ? (val as unknown[]).map((v) => String(v)) : [];\n}\nfunction uniqStrings(arr: string[]): string[] {\n return Array.from(new Set(arr));\n}\nfunction uniqBy<T>(arr: T[], key: (t: T) => string): T[] {\n const seen = new Set<string>();\n const out: T[] = [];\n for (const item of arr) {\n const k = key(item);\n if (seen.has(k)) continue;\n seen.add(k);\n out.push(item);\n }\n return out;\n}\nfunction sameDomain(a: string, b: string): boolean {\n return a.toLowerCase() === b.toLowerCase();\n}\n","// Lightweight date parsing helpers to avoid external dependencies.\n// We aim to parse common RDAP and WHOIS date representations and return a UTC ISO string.\nexport function toISO(\n dateLike: string | number | Date | undefined | null,\n): string | undefined {\n if (dateLike == null) return undefined;\n if (dateLike instanceof Date) return toIsoFromDate(dateLike);\n if (typeof dateLike === \"number\") return toIsoFromDate(new Date(dateLike));\n const raw = String(dateLike).trim();\n if (!raw) return undefined;\n // Try several structured formats seen in WHOIS outputs (treat as UTC when no TZ provided)\n const tryFormats = [\n // 2023-01-02 03:04:05Z or without Z\n /^(\\d{4})-(\\d{2})-(\\d{2})[ T](\\d{2}):(\\d{2}):(\\d{2})(?:Z|([+-]\\d{2})(?::?(\\d{2}))?)?$/,\n // 2023/01/02 03:04:05\n /^(\\d{4})\\/(\\d{2})\\/(\\d{2})[ T](\\d{2}):(\\d{2}):(\\d{2})(?:Z|([+-]\\d{2})(?::?(\\d{2}))?)?$/,\n // 02-Jan-2023\n /^(\\d{2})-([A-Za-z]{3})-(\\d{4})$/,\n // 21-07-2026 (DD-MM-YYYY used by .il, .hk)\n /^(\\d{2})-(\\d{2})-(\\d{4})$/,\n // Jan 02 2023\n /^([A-Za-z]{3})\\s+(\\d{1,2})\\s+(\\d{4})$/,\n ];\n for (const re of tryFormats) {\n const m = raw.match(re);\n if (!m) continue;\n const d = parseDateWithRegex(m, re);\n if (d) return toIsoFromDate(d);\n }\n // Fallback to native Date parsing (handles ISO and RFC2822 with TZ)\n const native = new Date(raw);\n if (!Number.isNaN(native.getTime())) return toIsoFromDate(native);\n return undefined;\n}\n\nfunction toIsoFromDate(d: Date): string | undefined {\n try {\n return new Date(\n Date.UTC(\n d.getUTCFullYear(),\n d.getUTCMonth(),\n d.getUTCDate(),\n d.getUTCHours(),\n d.getUTCMinutes(),\n d.getUTCSeconds(),\n 0,\n ),\n )\n .toISOString()\n .replace(/\\.\\d{3}Z$/, \"Z\");\n } catch {\n return undefined;\n }\n}\n\nfunction parseDateWithRegex(\n m: RegExpMatchArray,\n _re: RegExp,\n): Date | undefined {\n const monthMap: Record<string, number> = {\n jan: 0,\n feb: 1,\n mar: 2,\n apr: 3,\n may: 4,\n jun: 5,\n jul: 6,\n aug: 7,\n sep: 8,\n oct: 9,\n nov: 10,\n dec: 11,\n };\n try {\n // If the matched string contains time components, parse as Y-M-D H:M:S\n if (m[0].includes(\":\")) {\n const [_, y, mo, d, hh, mm, ss, offH, offM] = m;\n if (!y || !mo || !d || !hh || !mm || !ss) return undefined;\n // Base time as UTC\n let dt = Date.UTC(\n Number(y),\n Number(mo) - 1,\n Number(d),\n Number(hh),\n Number(mm),\n Number(ss),\n );\n // Apply timezone offset if present (e.g., +0000, -0500, +05:30)\n if (offH) {\n const sign = offH.startsWith(\"-\") ? -1 : 1;\n const hours = Math.abs(Number(offH));\n const minutes = offM ? Number(offM) : 0;\n const offsetMs = sign * (hours * 60 + minutes) * 60 * 1000;\n // The captured time is local with an explicit offset; convert to UTC\n dt -= offsetMs;\n }\n return new Date(dt);\n }\n // If the matched string contains hyphens, check if numeric (DD-MM-YYYY) or alpha (DD-MMM-YYYY)\n if (m[0].includes(\"-\")) {\n const [_, dd, monStr, yyyy] = m;\n if (!monStr || !dd || !yyyy) return undefined;\n // Check if month component is numeric (DD-MM-YYYY) or alphabetic (DD-MMM-YYYY)\n if (/^\\d+$/.test(monStr)) {\n // DD-MM-YYYY format (e.g., 21-07-2026)\n return new Date(Date.UTC(Number(yyyy), Number(monStr) - 1, Number(dd)));\n }\n // DD-MMM-YYYY format (e.g., 02-Jan-2023)\n const mon = monthMap[monStr.toLowerCase()];\n return new Date(Date.UTC(Number(yyyy), mon, Number(dd)));\n }\n // Otherwise treat as MMM DD YYYY\n const [_, monStr, dd, yyyy] = m;\n if (!monStr || !dd || !yyyy) return undefined;\n const mon = monthMap[monStr.toLowerCase()];\n return new Date(Date.UTC(Number(yyyy), mon, Number(dd)));\n } catch {\n // fall through to undefined\n }\n return undefined;\n}\n","export const PRIVACY_NAME_KEYWORDS = [\n \"redacted\",\n \"privacy\",\n \"private\",\n \"withheld\",\n \"not disclosed\",\n \"protected\",\n \"protection\",\n \"privado\", // Spanish\n \"datos privados\", // Spanish\n \"data protected\",\n \"data redacted\",\n \"gdpr redacted\",\n \"gdpr masked\",\n \"non-public data\",\n \"statutory masking\",\n \"redacted.forprivacy\",\n \"registration private\",\n \"hidden upon user request\",\n \"not available from registry\",\n];\n\n// Completely unusable/empty values that should be filtered\nexport const NO_DATA_VALUES = [\n \"-\",\n \".\",\n \"n/a\",\n \"na\",\n \"no data\",\n \"not available\",\n \"not applicable\",\n \"none\",\n];\n\nexport function isPrivacyName(value: string): boolean {\n const v = value.toLowerCase().trim();\n // Check for complete no-data values\n if (NO_DATA_VALUES.includes(v)) return true;\n // Check for privacy keywords\n return PRIVACY_NAME_KEYWORDS.some((k) => v.includes(k));\n}\n","export function uniq<T>(arr: T[] | undefined | null): T[] | undefined {\n if (!arr) return undefined;\n return Array.from(new Set(arr));\n}\n\nexport function parseKeyValueLines(text: string): Record<string, string[]> {\n const map = new Map<string, string[]>();\n const lines = text.split(/\\r?\\n/);\n let lastKey: string | undefined;\n for (const rawLine of lines) {\n const line = rawLine.replace(/\\s+$/, \"\");\n if (!line.trim()) continue;\n // Bracketed form: [Key] value (common in .jp and some ccTLDs)\n const bracket = line.match(/^\\s*\\[([^\\]]+)\\]\\s*(.*)$/);\n if (bracket?.[1] !== undefined && bracket?.[2] !== undefined) {\n const key = bracket[1].trim().toLowerCase();\n const value = bracket[2].trim();\n const list = map.get(key) ?? [];\n if (value) list.push(value);\n map.set(key, list);\n lastKey = key;\n continue;\n }\n // Colon form: Key: value\n const idx = line.indexOf(\":\");\n if (idx !== -1) {\n const key = line.slice(0, idx).trim().toLowerCase();\n const value = line.slice(idx + 1).trim();\n if (!key) {\n lastKey = undefined;\n continue;\n }\n const list = map.get(key) ?? [];\n if (value) list.push(value);\n map.set(key, list);\n lastKey = key;\n continue;\n }\n // Continuation line: starts with indentation after a key appeared\n if (lastKey && /^\\s+/.test(line)) {\n const value = line.trim();\n if (value) {\n const list = map.get(lastKey) ?? [];\n list.push(value);\n map.set(lastKey, list);\n }\n }\n // Otherwise ignore non key-value lines\n }\n return Object.fromEntries(map);\n}\n\nexport function parseCsv(value: string | undefined): string[] | undefined {\n if (!value) return undefined;\n return value\n .split(/[,\\s]+/)\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\nexport function asString(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nexport function asStringArray(value: unknown): string[] | undefined {\n return Array.isArray(value)\n ? (value.filter((x) => typeof x === \"string\") as string[])\n : undefined;\n}\n\nexport function asDateLike(value: unknown): string | number | Date | undefined {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n value instanceof Date\n )\n return value;\n return undefined;\n}\n","import { toISO } from \"../lib/dates\";\nimport { isPrivacyName } from \"../lib/privacy\";\nimport { asDateLike, asString, asStringArray, uniq } from \"../lib/text\";\nimport type {\n Contact,\n DomainRecord,\n Nameserver,\n RegistrarInfo,\n} from \"../types\";\n\ntype RdapDoc = Record<string, unknown>;\n\n/**\n * Convert RDAP JSON into our normalized DomainRecord.\n * This function is defensive: RDAP servers vary in completeness and field naming.\n */\nexport function normalizeRdap(\n inputDomain: string,\n tld: string,\n rdap: unknown,\n rdapServersTried: string[],\n includeRaw = false,\n): DomainRecord {\n const doc = (rdap ?? {}) as RdapDoc;\n\n // Prefer ldhName (punycode) and unicodeName if provided\n const ldhName: string | undefined =\n asString(doc.ldhName) || asString(doc.handle);\n const unicodeName: string | undefined = asString(doc.unicodeName);\n\n // Registrar entity can be provided with role \"registrar\"\n const registrar: RegistrarInfo | undefined = extractRegistrar(\n doc.entities as unknown,\n );\n\n // Nameservers: normalize host + IPs\n const nameservers: Nameserver[] | undefined = Array.isArray(doc.nameservers)\n ? (doc.nameservers as RdapDoc[])\n .map((ns) => {\n const host = (\n asString(ns.ldhName) ??\n asString(ns.unicodeName) ??\n \"\"\n ).toLowerCase();\n const ip = ns.ipAddresses as RdapDoc | undefined;\n const ipv4 = asStringArray(ip?.v4);\n const ipv6 = asStringArray(ip?.v6);\n const n: Nameserver = { host };\n if (ipv4?.length) n.ipv4 = ipv4;\n if (ipv6?.length) n.ipv6 = ipv6;\n return n;\n })\n .filter((n) => !!n.host)\n : undefined;\n\n // Contacts: RDAP entities include roles like registrant, administrative, technical, billing, abuse\n const contacts: Contact[] | undefined = extractContacts(\n doc.entities as unknown,\n );\n\n // Derive privacy flag from registrant name/org keywords\n const registrant = contacts?.find((c) => c.type === \"registrant\");\n const privacyEnabled = !!(\n registrant &&\n (\n [registrant.name, registrant.organization].filter(Boolean) as string[]\n ).some(isPrivacyName)\n );\n\n // RDAP uses IANA EPP status values. Preserve raw plus a description if any remarks are present.\n const statuses = Array.isArray(doc.status)\n ? (doc.status as unknown[])\n .filter((s): s is string => typeof s === \"string\")\n .map((s) => ({ status: s, raw: s }))\n : undefined;\n\n // Secure DNS info\n const secureDNS = doc.secureDNS as\n | { delegationSigned?: unknown; dsData?: Array<Record<string, unknown>> }\n | undefined;\n const dnssec = secureDNS\n ? {\n enabled: !!secureDNS.delegationSigned,\n dsRecords: Array.isArray(secureDNS.dsData)\n ? (secureDNS.dsData as Array<Record<string, unknown>>).map((d) => ({\n keyTag: d.keyTag as number | undefined,\n algorithm: d.algorithm as number | undefined,\n digestType: d.digestType as number | undefined,\n digest: d.digest as string | undefined,\n }))\n : undefined,\n }\n : undefined;\n\n // RDAP \"events\" contain timestamps for registration, last changed, expiration, deletion, etc.\n type RdapEvent = { eventAction?: string; eventDate?: string | number | Date };\n const events: RdapEvent[] = Array.isArray(doc.events)\n ? (doc.events as unknown[] as RdapEvent[])\n : [];\n const byAction = (action: string) =>\n events.find(\n (e) =>\n typeof e?.eventAction === \"string\" &&\n e.eventAction.toLowerCase().includes(action),\n );\n const creationDate = toISO(\n asDateLike(byAction(\"registration\")?.eventDate) ??\n asDateLike(doc.registrationDate),\n );\n const updatedDate = toISO(\n asDateLike(byAction(\"last changed\")?.eventDate) ??\n asDateLike(doc.lastChangedDate),\n );\n const expirationDate = toISO(\n asDateLike(byAction(\"expiration\")?.eventDate) ??\n asDateLike(doc.expirationDate),\n );\n const deletionDate = toISO(\n asDateLike(byAction(\"deletion\")?.eventDate) ?? asDateLike(doc.deletionDate),\n );\n\n // Derive a simple transfer lock flag from statuses\n const transferLock = !!statuses?.some((s: { status: string }) =>\n /transfer[-\\s]*prohibited/i.test(s.status),\n );\n\n // The RDAP document may include \"port43\" pointer to authoritative WHOIS\n const whoisServer: string | undefined = asString(doc.port43);\n\n const record: DomainRecord = {\n domain: unicodeName || ldhName || inputDomain,\n tld,\n isRegistered: true,\n isIDN: /(^|\\.)xn--/i.test(ldhName || inputDomain),\n unicodeName: unicodeName || undefined,\n punycodeName: ldhName || undefined,\n registry: undefined, // RDAP rarely includes a clean registry operator name\n registrar: registrar,\n reseller: undefined,\n statuses: statuses,\n creationDate,\n updatedDate,\n expirationDate,\n deletionDate,\n transferLock,\n dnssec,\n nameservers: nameservers\n ? uniq(nameservers.map((n) => ({ ...n, host: n.host.toLowerCase() })))\n : undefined,\n contacts,\n privacyEnabled: privacyEnabled ? true : undefined,\n whoisServer,\n rdapServers: rdapServersTried,\n rawRdap: includeRaw ? rdap : undefined,\n rawWhois: undefined,\n source: \"rdap\",\n warnings: undefined,\n };\n\n return record;\n}\n\nfunction extractRegistrar(entities: unknown): RegistrarInfo | undefined {\n if (!Array.isArray(entities)) return undefined;\n for (const ent of entities) {\n const roles: string[] = Array.isArray((ent as RdapDoc)?.roles)\n ? ((ent as RdapDoc).roles as unknown[]).filter(\n (r): r is string => typeof r === \"string\",\n )\n : [];\n if (!roles.some((r) => /registrar/i.test(r))) continue;\n const v = parseVcard((ent as RdapDoc)?.vcardArray);\n const ianaId = Array.isArray((ent as RdapDoc)?.publicIds)\n ? ((ent as RdapDoc).publicIds as Array<RdapDoc>).find((id) =>\n /iana\\s*registrar\\s*id/i.test(String(id?.type)),\n )?.identifier\n : undefined;\n return {\n name: v.fn || v.org || asString((ent as RdapDoc)?.handle) || undefined,\n ianaId: asString(ianaId),\n url: v.url ?? undefined,\n email: v.email ?? undefined,\n phone: v.tel ?? undefined,\n };\n }\n return undefined;\n}\n\nfunction extractContacts(entities: unknown): Contact[] | undefined {\n if (!Array.isArray(entities)) return undefined;\n const out: Contact[] = [];\n for (const ent of entities) {\n const roles: string[] = Array.isArray((ent as RdapDoc)?.roles)\n ? ((ent as RdapDoc).roles as unknown[]).filter(\n (r): r is string => typeof r === \"string\",\n )\n : [];\n const v = parseVcard((ent as RdapDoc)?.vcardArray);\n const type = roles.find((r) =>\n /registrant|administrative|technical|billing|abuse|reseller/i.test(r),\n );\n if (!type) continue;\n const map: Record<string, Contact[\"type\"]> = {\n registrant: \"registrant\",\n administrative: \"admin\",\n technical: \"tech\",\n billing: \"billing\",\n abuse: \"abuse\",\n reseller: \"reseller\",\n } as const;\n const roleKey = (map[type.toLowerCase()] ?? \"unknown\") as Contact[\"type\"];\n out.push({\n type: roleKey,\n name: v.fn,\n organization: v.org,\n email: v.email,\n phone: v.tel,\n fax: v.fax,\n street: v.street,\n city: v.locality,\n state: v.region,\n postalCode: v.postcode,\n country: v.country,\n countryCode: v.countryCode,\n });\n }\n return out.length ? out : undefined;\n}\n\ninterface ParsedVCard {\n fn?: string;\n org?: string;\n email?: string;\n tel?: string;\n fax?: string;\n url?: string;\n street?: string[];\n locality?: string;\n region?: string;\n postcode?: string;\n country?: string;\n countryCode?: string;\n}\n\n// Parse a minimal subset of vCard 4.0 arrays as used in RDAP \"vcardArray\" fields\nfunction parseVcard(vcardArray: unknown): ParsedVCard {\n // vcardArray is typically [\"vcard\", [[\"version\",{} ,\"text\",\"4.0\"], [\"fn\",{} ,\"text\",\"Example\"], ...]]\n if (\n !Array.isArray(vcardArray) ||\n vcardArray[0] !== \"vcard\" ||\n !Array.isArray(vcardArray[1])\n )\n return {};\n const entries = vcardArray[1] as Array<\n [string, Record<string, unknown>, string, unknown]\n >;\n const out: ParsedVCard = {};\n for (const e of entries) {\n const key = e?.[0];\n const value = e?.[3];\n if (!key) continue;\n switch (String(key).toLowerCase()) {\n case \"fn\":\n out.fn = asString(value);\n break;\n case \"org\":\n out.org = Array.isArray(value)\n ? value.map((x) => String(x)).join(\" \")\n : asString(value);\n break;\n case \"email\":\n out.email = asString(value);\n break;\n case \"tel\":\n out.tel = asString(value);\n break;\n case \"url\":\n out.url = asString(value);\n break;\n case \"adr\": {\n // adr value is [postOfficeBox, extendedAddress, street, locality, region, postalCode, country]\n if (Array.isArray(value)) {\n out.street = value[2] ? String(value[2]).split(/\\n|,\\s*/) : undefined;\n out.locality = asString(value[3]);\n out.region = asString(value[4]);\n out.postcode = asString(value[5]);\n out.country = asString(value[6]);\n }\n break;\n }\n }\n }\n // Best effort country code from country name (often omitted). Leaving undefined unless explicitly provided.\n return out;\n}\n","import { withTimeout } from \"../lib/async\";\nimport { DEFAULT_TIMEOUT_MS } from \"../lib/constants\";\nimport type { LookupOptions } from \"../types\";\n\nexport interface WhoisQueryResult {\n serverQueried: string;\n text: string;\n}\n\n/**\n * Some WHOIS servers default to non-English responses. This mapping allows automatic\n * query transformation to request English-only output for easier parsing.\n *\n * To add new servers: Add an entry with the hostname and transformation function:\n * \"whois.example.org\": (query) => `${query}/english`,\n */\nconst WHOIS_QUERY_TRANSFORMERS: Record<string, (query: string) => string> = {\n \"whois.jprs.jp\": (query) => `${query}/e`, // Append /e for English-only response\n};\n\n/**\n * Perform a WHOIS query against an RFC 3912 server over TCP 43.\n * Returns the raw text and the server used.\n */\nexport async function whoisQuery(\n server: string,\n query: string,\n options?: LookupOptions,\n): Promise<WhoisQueryResult> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const port = 43;\n const host = server.replace(/^whois:\\/\\//i, \"\");\n\n // Transform query if server requires special formatting\n const transformer = WHOIS_QUERY_TRANSFORMERS[host];\n const transformedQuery = transformer ? transformer(query) : query;\n\n const text = await withTimeout(\n queryTcp(host, port, transformedQuery, options),\n timeoutMs,\n \"WHOIS timeout\",\n );\n return { serverQueried: server, text };\n}\n\n// Low-level WHOIS TCP client. Some registries require CRLF after the domain query.\nasync function queryTcp(\n host: string,\n port: number,\n query: string,\n options?: LookupOptions,\n): Promise<string> {\n let net: typeof import(\"node:net\") | null;\n try {\n net = await import(\"node:net\");\n } catch {\n net = null;\n }\n\n if (!net?.createConnection) {\n throw new Error(\n \"WHOIS client is only available in Node.js runtimes; try setting `rdapOnly: true`.\",\n );\n }\n\n return new Promise((resolve, reject) => {\n const socket = net.createConnection({ host, port });\n let data = \"\";\n let done = false;\n const cleanup = () => {\n if (done) return;\n done = true;\n socket.destroy();\n };\n socket.setTimeout((options?.timeoutMs ?? DEFAULT_TIMEOUT_MS) - 1000, () => {\n cleanup();\n reject(new Error(\"WHOIS socket timeout\"));\n });\n socket.on(\"error\", (err) => {\n cleanup();\n reject(err);\n });\n socket.on(\"data\", (chunk) => {\n data += chunk.toString(\"utf8\");\n });\n socket.on(\"end\", () => {\n cleanup();\n resolve(data);\n });\n socket.on(\"connect\", () => {\n socket.write(`${query}\\r\\n`);\n });\n });\n}\n","// Curated authoritative WHOIS servers by TLD (exceptions to default/referral logic)\n// Source of truth checked against IANA delegation records; prefer RDAP first.\nexport const WHOIS_TLD_EXCEPTIONS = {\n // gTLDs (port-43 still available at registry)\n com: \"whois.verisign-grs.com\",\n net: \"whois.verisign-grs.com\",\n org: \"whois.publicinterestregistry.org\", // PIR\n biz: \"whois.nic.biz\",\n name: \"whois.nic.name\",\n edu: \"whois.educause.edu\",\n gov: \"whois.nic.gov\", // was whois.dotgov.gov\n\n // ccTLDs & other TLDs with working port-43 WHOIS\n de: \"whois.denic.de\",\n jp: \"whois.jprs.jp\",\n fr: \"whois.nic.fr\",\n it: \"whois.nic.it\",\n pl: \"whois.dns.pl\",\n nl: \"whois.domain-registry.nl\",\n be: \"whois.dns.be\",\n se: \"whois.iis.se\",\n no: \"whois.norid.no\",\n fi: \"whois.fi\",\n cz: \"whois.nic.cz\",\n es: \"whois.nic.es\",\n br: \"whois.registro.br\",\n ca: \"whois.cira.ca\",\n dk: \"whois.punktum.dk\", // was whois.dk-hostmaster.dk\n hk: \"whois.hkirc.hk\",\n sg: \"whois.sgnic.sg\",\n in: \"whois.nixiregistry.in\", // was whois.registry.in\n nz: \"whois.irs.net.nz\", // was whois.srs.net.nz\n ch: \"whois.nic.ch\",\n li: \"whois.nic.li\",\n io: \"whois.nic.io\",\n ai: \"whois.nic.ai\",\n ru: \"whois.tcinet.ru\",\n su: \"whois.tcinet.ru\",\n us: \"whois.nic.us\",\n co: \"whois.nic.co\",\n me: \"whois.nic.me\",\n tv: \"whois.nic.tv\",\n cc: \"ccwhois.verisign-grs.com\",\n eu: \"whois.eu\",\n au: \"whois.auda.org.au\",\n kr: \"whois.kr\",\n tw: \"whois.twnic.net.tw\",\n uk: \"whois.nic.uk\",\n nu: \"whois.iis.nu\",\n \"xn--p1ai\": \"whois.tcinet.ru\", // .рф\n\n // CentralNic-operated public SLD zones (still WHOIS @ centralnic)\n \"uk.com\": \"whois.centralnic.com\",\n \"uk.net\": \"whois.centralnic.com\",\n \"gb.com\": \"whois.centralnic.com\",\n \"gb.net\": \"whois.centralnic.com\",\n \"eu.com\": \"whois.centralnic.com\",\n \"us.com\": \"whois.centralnic.com\",\n \"se.com\": \"whois.centralnic.com\",\n \"de.com\": \"whois.centralnic.com\",\n \"br.com\": \"whois.centralnic.com\",\n \"ru.com\": \"whois.centralnic.com\",\n \"cn.com\": \"whois.centralnic.com\",\n \"sa.com\": \"whois.centralnic.com\",\n \"co.com\": \"whois.centralnic.com\",\n} as Record<string, string>;\n","import type { LookupOptions } from \"../types\";\nimport { whoisQuery } from \"./client\";\nimport { WHOIS_TLD_EXCEPTIONS } from \"./servers\";\n\n/**\n * Parse the IANA WHOIS response for a TLD and extract the WHOIS server\n * without crossing line boundaries. Some TLDs (e.g. .np) leave the field\n * blank, in which case this returns undefined.\n */\nexport function parseIanaWhoisServer(text: string): string | undefined {\n // Search lines in priority order: whois, refer, whois server\n const fields = [\"whois\", \"refer\", \"whois server\"];\n const lines = String(text).split(/\\r?\\n/);\n for (const field of fields) {\n for (const raw of lines) {\n const line = raw.trimEnd();\n // Match beginning of line, allowing leading spaces, case-insensitive\n const re = new RegExp(`^\\\\s*${field}\\\\s*:\\\\s*(.*?)$`, \"i\");\n const m = line.match(re);\n if (m) {\n const value = (m[1] || \"\").trim();\n if (value) return value;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Parse a likely registration information URL from an IANA WHOIS response.\n * Looks at lines like:\n * remarks: Registration information: http://example.tld\n * url: https://registry.example\n */\nexport function parseIanaRegistrationInfoUrl(text: string): string | undefined {\n const lines = String(text).split(/\\r?\\n/);\n for (const raw of lines) {\n const line = raw.trim();\n if (!/^\\s*(remarks|url|website)\\s*:/i.test(line)) continue;\n const urlMatch = line.match(/https?:\\/\\/\\S+/i);\n if (urlMatch?.[0]) return urlMatch[0];\n }\n return undefined;\n}\n\n/** Fetch raw IANA WHOIS text for a TLD (best-effort). */\nexport async function getIanaWhoisTextForTld(\n tld: string,\n options?: LookupOptions,\n): Promise<string | undefined> {\n try {\n const res = await whoisQuery(\"whois.iana.org\", tld.toLowerCase(), options);\n return res.text;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Best-effort discovery of the authoritative WHOIS server for a TLD via IANA root DB.\n */\nexport async function ianaWhoisServerForTld(\n tld: string,\n options?: LookupOptions,\n): Promise<string | undefined> {\n const key = tld.toLowerCase();\n // 1) Explicit hint override\n const hint = options?.whoisHints?.[key];\n if (hint) return normalizeServer(hint);\n\n // 2) IANA WHOIS authoritative discovery over TCP 43\n try {\n const res = await whoisQuery(\"whois.iana.org\", key, options);\n const txt = res.text;\n const server = parseIanaWhoisServer(txt);\n if (server) return normalizeServer(server);\n } catch {\n // fallthrough to exceptions/guess\n }\n\n // 3) Curated exceptions\n const exception = WHOIS_TLD_EXCEPTIONS[key];\n if (exception) return normalizeServer(exception);\n\n return undefined;\n}\n\n/**\n * Extract registrar referral WHOIS server from a WHOIS response, if present.\n */\nexport function extractWhoisReferral(text: string): string | undefined {\n const patterns = [\n /^Registrar WHOIS Server:\\s*(.+)$/im,\n /^Whois Server:\\s*(.+)$/im,\n /^ReferralServer:\\s*whois:\\/\\/(.+)$/im,\n ];\n for (const re of patterns) {\n const m = text.match(re);\n if (m?.[1]) return m[1].trim();\n }\n return undefined;\n}\n\nfunction normalizeServer(server: string): string {\n return server.replace(/^whois:\\/\\//i, \"\").replace(/\\/$/, \"\");\n}\n","import { uniq } from \"../lib/text\";\nimport type { Contact, DomainRecord, Nameserver } from \"../types\";\n\nfunction dedupeStatuses(\n a?: DomainRecord[\"statuses\"],\n b?: DomainRecord[\"statuses\"],\n) {\n const list = [...(a || []), ...(b || [])];\n const seen = new Set<string>();\n const out: NonNullable<DomainRecord[\"statuses\"]> = [];\n for (const s of list) {\n const key = (s?.status || \"\").toLowerCase();\n if (!key || seen.has(key)) continue;\n seen.add(key);\n out.push(s);\n }\n return out.length ? out : undefined;\n}\n\nfunction dedupeNameservers(a?: Nameserver[], b?: Nameserver[]) {\n const map = new Map<string, Nameserver>();\n for (const ns of [...(a || []), ...(b || [])]) {\n const host = ns.host.toLowerCase();\n const prev = map.get(host);\n if (!prev) {\n map.set(host, { ...ns, host });\n continue;\n }\n const ipv4 = uniq([...(prev.ipv4 || []), ...(ns.ipv4 || [])]);\n const ipv6 = uniq([...(prev.ipv6 || []), ...(ns.ipv6 || [])]);\n map.set(host, { host, ipv4, ipv6 });\n }\n const out = Array.from(map.values());\n return out.length ? out : undefined;\n}\n\nfunction dedupeContacts(a?: Contact[], b?: Contact[]) {\n const list = [...(a || []), ...(b || [])];\n const seen = new Set<string>();\n const out: Contact[] = [];\n for (const c of list) {\n const key = `${c.type}|${(c.organization || c.name || c.email || \"\").toString().toLowerCase()}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(c);\n }\n return out.length ? out : undefined;\n}\n\n/** Conservative merge: start with base; fill missing scalars; union arrays; prefer more informative dates. */\nexport function mergeWhoisRecords(\n base: DomainRecord,\n others: DomainRecord[],\n): DomainRecord {\n const merged: DomainRecord = { ...base };\n for (const cur of others) {\n merged.isRegistered = merged.isRegistered || cur.isRegistered;\n merged.registry = merged.registry ?? cur.registry;\n merged.registrar = merged.registrar ?? cur.registrar;\n merged.reseller = merged.reseller ?? cur.reseller;\n merged.statuses = dedupeStatuses(merged.statuses, cur.statuses);\n // Dates: prefer earliest creation, latest updated/expiration when available\n merged.creationDate = preferEarliestIso(\n merged.creationDate,\n cur.creationDate,\n );\n merged.updatedDate = preferLatestIso(merged.updatedDate, cur.updatedDate);\n merged.expirationDate = preferLatestIso(\n merged.expirationDate,\n cur.expirationDate,\n );\n merged.deletionDate = merged.deletionDate ?? cur.deletionDate;\n merged.transferLock = Boolean(merged.transferLock || cur.transferLock);\n merged.dnssec = merged.dnssec ?? cur.dnssec;\n merged.nameservers = dedupeNameservers(merged.nameservers, cur.nameservers);\n merged.contacts = dedupeContacts(merged.contacts, cur.contacts);\n merged.privacyEnabled = merged.privacyEnabled ?? cur.privacyEnabled;\n // Keep whoisServer pointing to the latest contributing authoritative server\n merged.whoisServer = cur.whoisServer ?? merged.whoisServer;\n // rawWhois: keep last contributing text\n merged.rawWhois = cur.rawWhois ?? merged.rawWhois;\n }\n return merged;\n}\n\nfunction preferEarliestIso(a?: string, b?: string): string | undefined {\n if (!a) return b;\n if (!b) return a;\n return new Date(a) <= new Date(b) ? a : b;\n}\n\nfunction preferLatestIso(a?: string, b?: string): string | undefined {\n if (!a) return b;\n if (!b) return a;\n return new Date(a) >= new Date(b) ? a : b;\n}\n","import { toISO } from \"../lib/dates\";\nimport { isPrivacyName } from \"../lib/privacy\";\nimport { parseKeyValueLines, uniq } from \"../lib/text\";\nimport type {\n Contact,\n DomainRecord,\n Nameserver,\n RegistrarInfo,\n} from \"../types\";\n\n// Common WHOIS availability phrases seen across registries/registrars\nconst WHOIS_AVAILABLE_PATTERNS: RegExp[] = [\n /\\bno match\\b/i,\n /\\bnot found\\b/i,\n /\\bno entries found\\b/i,\n /\\bno data found\\b/i,\n /\\bno information available\\b/i,\n /\\bno information was found\\b/i,\n /\\bno data was found\\b/i,\n /\\bavailable for registration\\b/i,\n /\\bdomain\\s+available\\b/i,\n /\\bdomain status[:\\s]+available\\b/i,\n /\\bobject does not exist\\b/i,\n /\\bthe queried object does not exist\\b/i,\n /\\bqueried object does not exist\\b/i,\n /\\bdoes not exist\\b/i,\n /\\breturned 0 objects\\b/i,\n /\\bnot been registered\\b/i,\n /\\bunassignable\\b/i,\n /\\bis free\\b/i,\n // Common variants across ccTLDs/registrars\n /\\bstatus:\\s*free\\b/i,\n /\\bstatus:\\s*available\\b/i,\n /\\bno object found\\b/i,\n /\\bobject_not_found\\b/i,\n /\\bno se encuentra registrado\\b/i, // Spanish: \"not found registered\"\n /\\bnicht gefunden\\b/i, // German: \"not found\"\n /\\bpending release\\b/i, // often signals not registered/being deleted\n];\n\n/**\n * Best-effort heuristic to determine if a WHOIS response indicates the domain is available.\n */\nexport function isAvailableByWhois(text: string | undefined): boolean {\n if (!text) return false;\n return WHOIS_AVAILABLE_PATTERNS.some((re) => re.test(text));\n}\n\n/**\n * Convert raw WHOIS text into our normalized DomainRecord.\n * Heuristics cover many gTLD and ccTLD formats; exact fields vary per registry.\n */\nexport function normalizeWhois(\n domain: string,\n tld: string,\n whoisText: string,\n whoisServer: string | undefined,\n includeRaw = false,\n): DomainRecord {\n const map = parseKeyValueLines(whoisText);\n\n // Date extraction across common synonyms\n const creationDate = anyValue(map, [\n \"creation date\",\n \"created on\",\n \"created\",\n \"registered on\",\n \"registered\",\n \"registration date\",\n \"domain registration date\",\n \"domain create date\",\n \"domain name commencement date\",\n \"registration time\", // .cn\n \"domain record activated\", // .edu\n \"domain registered\",\n \"registered date\", // .co.jp\n \"assigned\", // .il\n ]);\n const updatedDate = anyValue(map, [\n \"updated date\",\n \"updated\",\n \"last updated\",\n \"last updated on\", // .mx\n \"last update\", // .co.jp\n \"last-update\", // .fr\n \"last modified\",\n \"modified\",\n \"changed\",\n \"modification date\",\n \"domain record last updated\", // .edu\n ]);\n const expirationDate = anyValue(map, [\n \"registry expiry date\",\n \"registry expiration date\",\n \"registrar registration expiration date\",\n \"registrar registration expiry date\",\n \"registrar expiration date\",\n \"registrar expiry date\",\n \"expiry date\",\n \"expiration date\",\n \"expiry\",\n \"expire date\", // .it\n \"expire\",\n \"expired\", // .ly\n \"expires on\",\n \"expires\",\n \"expiration time\", // .cn\n \"domain expires\", // .edu\n \"paid-till\",\n \"renewal date\", // .pl\n \"validity\", // .il\n \"record will expire on\",\n ]);\n\n // Registrar info (thin registries like .com/.net require referral follow for full data)\n const registrar: RegistrarInfo | undefined = (() => {\n const name = anyValue(map, [\n \"registrar\",\n \"registrar name\",\n \"registrar organization\",\n \"registrar organization name\", // .tr\n \"sponsoring registrar\",\n \"organisation\",\n \"record maintained by\",\n ]);\n const ianaId = anyValue(map, [\n \"registrar iana id\",\n \"sponsoring registrar iana id\",\n \"iana id\",\n ]);\n const url = anyValue(map, [\n \"registrar url\",\n \"registrar website\",\n \"registrar web\", // .it\n \"url of the registrar\",\n \"referrer\",\n ]);\n const abuseEmail = anyValue(map, [\n \"registrar abuse contact email\",\n \"abuse contact email\",\n ]);\n const abusePhone = anyValue(map, [\n \"registrar abuse contact phone\",\n \"abuse contact phone\",\n ]);\n if (!name && !ianaId && !url && !abuseEmail && !abusePhone)\n return undefined;\n return {\n name: name || undefined,\n ianaId: ianaId || undefined,\n url: url || undefined,\n email: abuseEmail || undefined,\n phone: abusePhone || undefined,\n };\n })();\n\n // Statuses: multiple entries are expected; keep raw\n const statusLines =\n map[\"domain status\"] ||\n map.status ||\n map.flags ||\n map.state || // .ru\n map[\"registration status\"] ||\n map.eppstatus || // .fr\n [];\n const statuses = statusLines.length\n ? statusLines\n .map((line) => {\n const status = line.split(/\\s+/)[0];\n return status ? { status, raw: line } : null;\n })\n .filter((s): s is { status: string; raw: string } => s !== null)\n : undefined;\n\n // Nameservers: also appear as \"nserver\" on some ccTLDs (.de, .ru) and as \"name server\"\n const nsLines: string[] = [\n ...(map[\"name server\"] || []),\n ...(map.nameserver || []),\n ...(map[\"name servers\"] || []),\n ...(map.nserver || []),\n ...(map[\"name server information\"] || []),\n ...(map.dns || []),\n ...(map.hostname || []),\n ...(map[\"domain nameservers\"] || []),\n ...(map[\"domain servers in listed order\"] || []), // .ly\n ...(map[\"domain servers\"] || []), // .tr\n ...(map[\"name servers dns\"] || []), // .mx\n ...(map[\"ns 1\"] || []),\n ...(map[\"ns 2\"] || []),\n ...(map[\"ns 3\"] || []),\n ...(map[\"ns 4\"] || []),\n ];\n const nameservers: Nameserver[] | undefined = nsLines.length\n ? (uniq(\n nsLines\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => {\n // Common formats: \"ns1.example.com\" or \"ns1.example.com 192.0.2.1\" or \"ns1.example.com 2001:db8::1\"\n const parts = line.split(/\\s+/);\n const host = parts.shift()?.toLowerCase() || \"\";\n const ipv4: string[] = [];\n const ipv6: string[] = [];\n for (const p of parts) {\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(p)) ipv4.push(p);\n else if (/^[0-9a-f:]+$/i.test(p)) ipv6.push(p);\n }\n if (!host) return undefined;\n const ns: Nameserver = { host };\n if (ipv4.length) ns.ipv4 = ipv4;\n if (ipv6.length) ns.ipv6 = ipv6;\n return ns;\n })\n .filter((x): x is Nameserver => !!x),\n ) as Nameserver[])\n : undefined;\n\n // Contacts: best-effort parse common keys\n const contacts = collectContacts(map);\n\n // Derive privacy flag from registrant name/org keywords\n const registrant = contacts?.find((c) => c.type === \"registrant\");\n const privacyEnabled = !!(\n registrant &&\n (\n [registrant.name, registrant.organization].filter(Boolean) as string[]\n ).some(isPrivacyName)\n );\n\n const dnssecRaw = (map.dnssec?.[0] || \"\").toLowerCase();\n const dnssec = dnssecRaw\n ? { enabled: /signed|yes|true/.test(dnssecRaw) }\n : undefined;\n\n // Simple lock derivation from statuses\n const transferLock = !!statuses?.some((s) =>\n /transfer[-\\s]*prohibited/i.test(s.raw || s.status || \"\"),\n );\n\n const record: DomainRecord = {\n domain,\n tld,\n isRegistered: !isAvailableByWhois(whoisText),\n isIDN: /(^|\\.)xn--/i.test(domain),\n unicodeName: undefined,\n punycodeName: undefined,\n registry: undefined,\n registrar,\n reseller: anyValue(map, [\"reseller\"]) || undefined,\n statuses,\n creationDate: toISO(creationDate || undefined),\n updatedDate: toISO(updatedDate || undefined),\n expirationDate: toISO(expirationDate || undefined),\n deletionDate: undefined,\n transferLock,\n dnssec,\n nameservers,\n contacts,\n privacyEnabled: privacyEnabled ? true : undefined,\n whoisServer,\n rdapServers: undefined,\n rawRdap: undefined,\n rawWhois: includeRaw ? whoisText : undefined,\n source: \"whois\",\n warnings: undefined,\n };\n\n return record;\n}\n\nfunction anyValue(\n map: Record<string, string[]>,\n keys: string[],\n): string | undefined {\n for (const k of keys) {\n const v = map[k];\n if (v?.length) return v[0];\n }\n return undefined;\n}\n\nfunction collectContacts(map: Record<string, string[]>): Contact[] | undefined {\n const roles: Array<{\n role: Contact[\"type\"];\n prefixes: string[];\n }> = [\n {\n role: \"registrant\",\n prefixes: [\"registrant\", \"owner\", \"holder\"],\n },\n {\n role: \"admin\",\n prefixes: [\"admin\", \"administrative\"],\n },\n {\n role: \"tech\",\n prefixes: [\"tech\", \"technical\"],\n },\n {\n role: \"billing\",\n prefixes: [\"billing\"],\n },\n {\n role: \"abuse\",\n prefixes: [\"abuse\"],\n },\n ];\n const contacts: Contact[] = [];\n for (const r of roles) {\n const nameKeys: string[] = [];\n const orgKeys: string[] = [];\n const emailKeys: string[] = [];\n const phoneKeys: string[] = [];\n const faxKeys: string[] = [];\n const streetKeys: string[] = [];\n const cityKeys: string[] = [];\n const stateKeys: string[] = [];\n const postalCodeKeys: string[] = [];\n const countryKeys: string[] = [];\n\n for (const prefix of r.prefixes) {\n nameKeys.push(`${prefix} name`, `${prefix} contact name`, `${prefix}`);\n if (prefix === \"registrant\") {\n nameKeys.push(\"registrant person\"); // .ua\n }\n if (prefix === \"owner\") {\n nameKeys.push(\"owner name\"); // .tm\n }\n\n orgKeys.push(\n `${prefix} organization`,\n `${prefix} organisation`,\n `${prefix} org`,\n );\n if (prefix === \"registrant\") {\n orgKeys.push(\"trading as\"); // .uk, .co.uk\n orgKeys.push(\"org\"); // .ru\n }\n if (prefix === \"owner\") {\n orgKeys.push(\"owner orgname\"); // .tm\n }\n\n emailKeys.push(\n `${prefix} email`,\n `${prefix} contact email`,\n `${prefix} e-mail`,\n );\n\n phoneKeys.push(\n `${prefix} phone`,\n `${prefix} contact phone`,\n `${prefix} telephone`,\n );\n\n faxKeys.push(`${prefix} fax`, `${prefix} facsimile`);\n\n streetKeys.push(\n `${prefix} street`,\n `${prefix} address`,\n `${prefix}'s address`,\n );\n if (prefix === \"owner\") {\n streetKeys.push(\"owner addr\"); // .tm\n }\n\n cityKeys.push(`${prefix} city`);\n\n stateKeys.push(\n `${prefix} state`,\n `${prefix} province`,\n `${prefix} state/province`,\n );\n\n postalCodeKeys.push(\n `${prefix} postal code`,\n `${prefix} postcode`,\n `${prefix} zip`,\n );\n\n countryKeys.push(`${prefix} country`);\n }\n\n const name = anyValue(map, nameKeys);\n const org = anyValue(map, orgKeys);\n const email = anyValue(map, emailKeys);\n const phone = anyValue(map, phoneKeys);\n const fax = anyValue(map, faxKeys);\n const street = multi(map, streetKeys);\n const city = anyValue(map, cityKeys);\n const state = anyValue(map, stateKeys);\n const postalCode = anyValue(map, postalCodeKeys);\n const country = anyValue(map, countryKeys);\n\n if (name || org || email || phone || street?.length) {\n contacts.push({\n type: r.role,\n name: name || undefined,\n organization: org || undefined,\n email: email || undefined,\n phone: phone || undefined,\n fax: fax || undefined,\n street: street,\n city: city || undefined,\n state: state || undefined,\n postalCode: postalCode || undefined,\n country: country || undefined,\n });\n }\n }\n return contacts.length ? contacts : undefined;\n}\n\nfunction multi(\n map: Record<string, string[]>,\n keys: string[],\n): string[] | undefined {\n for (const k of keys) {\n const v = map[k];\n if (v?.length) return v;\n }\n return undefined;\n}\n","import type { LookupOptions } from \"../types\";\nimport type { WhoisQueryResult } from \"./client\";\nimport { whoisQuery } from \"./client\";\nimport { extractWhoisReferral } from \"./discovery\";\nimport { isAvailableByWhois } from \"./normalize\";\n\n/**\n * Follow registrar WHOIS referrals up to a configured hop limit.\n * Returns the last successful WHOIS response (best-effort; keeps original on failures).\n */\nexport async function followWhoisReferrals(\n initialServer: string,\n domain: string,\n opts?: LookupOptions,\n): Promise<WhoisQueryResult> {\n const maxHops = Math.max(0, opts?.maxWhoisReferralHops ?? 2);\n // First query against the provided server\n let current = await whoisQuery(initialServer, domain, opts);\n if (opts?.followWhoisReferral === false || maxHops === 0) return current;\n\n const visited = new Set<string>([normalize(current.serverQueried)]);\n let hops = 0;\n // Iterate while we see a new referral and are under hop limit\n while (hops < maxHops) {\n const next = extractWhoisReferral(current.text);\n if (!next) break;\n const normalized = normalize(next);\n if (visited.has(normalized)) break; // cycle protection / same as current\n visited.add(normalized);\n try {\n const res = await whoisQuery(next, domain, opts);\n // Prefer authoritative TLD response when registrar contradicts availability\n const registeredBefore = !isAvailableByWhois(current.text);\n const registeredAfter = !isAvailableByWhois(res.text);\n if (registeredBefore && !registeredAfter) {\n // Registrar claims availability but TLD shows registered: keep TLD\n break;\n }\n current = res; // adopt registrar when it does not downgrade registration\n } catch {\n // If referral server fails, stop following and keep the last good response\n break;\n }\n hops += 1;\n }\n return current;\n}\n\n/**\n * Collect the WHOIS referral chain starting from the TLD server.\n * Always includes the initial TLD response; may include one or more registrar responses.\n * Stops on contradiction (registrar claims availability) or failures.\n */\nexport async function collectWhoisReferralChain(\n initialServer: string,\n domain: string,\n opts?: LookupOptions,\n): Promise<WhoisQueryResult[]> {\n const results: WhoisQueryResult[] = [];\n const maxHops = Math.max(0, opts?.maxWhoisReferralHops ?? 2);\n const first = await whoisQuery(initialServer, domain, opts);\n results.push(first);\n if (opts?.followWhoisReferral === false || maxHops === 0) return results;\n\n const visited = new Set<string>([normalize(first.serverQueried)]);\n let current = first;\n let hops = 0;\n while (hops < maxHops) {\n const next = extractWhoisReferral(current.text);\n if (!next) break;\n const normalized = normalize(next);\n if (visited.has(normalized)) break;\n visited.add(normalized);\n try {\n const res = await whoisQuery(next, domain, opts);\n // If registrar claims availability while TLD indicated registered, stop.\n const registeredBefore = !isAvailableByWhois(current.text);\n const registeredAfter = !isAvailableByWhois(res.text);\n if (registeredBefore && !registeredAfter) {\n // Do not adopt or append contradictory registrar; keep authoritative TLD only.\n break;\n }\n results.push(res);\n current = res;\n } catch {\n break;\n }\n hops += 1;\n }\n return results;\n}\n\nfunction normalize(server: string): string {\n return server.replace(/^whois:\\/\\//i, \"\").toLowerCase();\n}\n","import { getDomainParts, isLikelyDomain } from \"./lib/domain\";\nimport { getRdapBaseUrlsForTld } from \"./rdap/bootstrap\";\nimport { fetchRdapDomain } from \"./rdap/client\";\nimport { fetchAndMergeRdapRelated } from \"./rdap/merge\";\nimport { normalizeRdap } from \"./rdap/normalize\";\nimport type { DomainRecord, LookupOptions, LookupResult } from \"./types\";\nimport {\n getIanaWhoisTextForTld,\n ianaWhoisServerForTld,\n parseIanaRegistrationInfoUrl,\n} from \"./whois/discovery\";\nimport { mergeWhoisRecords } from \"./whois/merge\";\nimport { normalizeWhois } from \"./whois/normalize\";\nimport {\n collectWhoisReferralChain,\n followWhoisReferrals,\n} from \"./whois/referral\";\n\n/**\n * High-level lookup that prefers RDAP and falls back to WHOIS.\n * Ensures a standardized DomainRecord, independent of the source.\n */\nexport async function lookup(\n domain: string,\n opts?: LookupOptions,\n): Promise<LookupResult> {\n try {\n if (!isLikelyDomain(domain)) {\n return { ok: false, error: \"Input does not look like a domain\" };\n }\n\n const { publicSuffix: tld } = getDomainParts(domain);\n if (!tld) {\n return { ok: false, error: \"Invalid TLD\" };\n }\n\n // If WHOIS-only, skip RDAP path\n if (!opts?.whoisOnly) {\n let bases = await getRdapBaseUrlsForTld(tld, opts);\n // Some ccTLD registries publish RDAP only at the registry TLD (e.g., br),\n // while the public suffix can be multi-label (e.g., com.br). Fallback to last label.\n if (bases.length === 0 && tld.includes(\".\")) {\n const registryTld = tld.split(\".\").pop() ?? tld;\n bases = await getRdapBaseUrlsForTld(registryTld, opts);\n }\n const tried: string[] = [];\n for (const base of bases) {\n tried.push(base);\n try {\n const { json } = await fetchRdapDomain(domain, base, opts);\n const rdapEnriched = await fetchAndMergeRdapRelated(\n domain,\n json,\n opts,\n );\n const record: DomainRecord = normalizeRdap(\n domain,\n tld,\n rdapEnriched.merged,\n [...tried, ...rdapEnriched.serversTried],\n !!opts?.includeRaw,\n );\n return { ok: true, record };\n } catch {\n // try next base\n }\n }\n // Some TLDs are not in bootstrap yet; continue to WHOIS fallback unless rdapOnly\n if (opts?.rdapOnly) {\n return {\n ok: false,\n error: `RDAP not available or failed for TLD '${tld}'. Many TLDs do not publish RDAP; try WHOIS fallback (omit rdapOnly).`,\n };\n }\n }\n\n // WHOIS fallback path\n const whoisServer = await ianaWhoisServerForTld(tld, opts);\n if (!whoisServer) {\n // Provide a clearer, actionable message\n const ianaText = await getIanaWhoisTextForTld(tld, opts);\n const regUrl = ianaText\n ? parseIanaRegistrationInfoUrl(ianaText)\n : undefined;\n const hint = regUrl ? ` See registration info at ${regUrl}.` : \"\";\n return {\n ok: false,\n error: `No WHOIS server discovered for TLD '${tld}'. This registry may not publish public WHOIS over port 43.${hint}`,\n };\n }\n\n // Query the TLD server first; optionally follow registrar referrals (multi-hop)\n // Collect the chain and coalesce so we don't lose details when a registrar returns minimal/empty data.\n const chain = await collectWhoisReferralChain(whoisServer, domain, opts);\n if (chain.length === 0) {\n // Fallback to previous behavior as a safety net\n const res = await followWhoisReferrals(whoisServer, domain, opts);\n const record: DomainRecord = normalizeWhois(\n domain,\n tld,\n res.text,\n res.serverQueried,\n !!opts?.includeRaw,\n );\n return { ok: true, record };\n }\n\n // Normalize all WHOIS texts in the chain and merge conservatively\n const normalizedRecords = chain.map((r) =>\n normalizeWhois(domain, tld, r.text, r.serverQueried, !!opts?.includeRaw),\n );\n const [first, ...rest] = normalizedRecords;\n if (!first) {\n return { ok: false, error: \"No WHOIS data retrieved\" };\n }\n const mergedRecord = rest.length ? mergeWhoisRecords(first, rest) : first;\n return { ok: true, record: mergedRecord };\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n return { ok: false, error: message };\n }\n}\n\n/**\n * Determine if a domain appears available (not registered).\n * Performs a lookup and resolves to a boolean. Rejects on lookup error.\n */\nexport async function isAvailable(\n domain: string,\n opts?: LookupOptions,\n): Promise<boolean> {\n const res = await lookup(domain, opts);\n if (!res.ok || !res.record) throw new Error(res.error || \"Lookup failed\");\n return res.record.isRegistered === false;\n}\n\n/**\n * Determine if a domain appears registered.\n * Performs a lookup and resolves to a boolean. Rejects on lookup error.\n */\nexport async function isRegistered(\n domain: string,\n opts?: LookupOptions,\n): Promise<boolean> {\n const res = await lookup(domain, opts);\n if (!res.ok || !res.record) throw new Error(res.error || \"Lookup failed\");\n return res.record.isRegistered === true;\n}\n\n/**\n * @deprecated Use `lookup` instead.\n */\nexport const lookupDomain = lookup;\n\nexport {\n getDomainParts,\n getDomainTld,\n isLikelyDomain,\n toRegistrableDomain,\n} from \"./lib/domain\";\nexport type * from \"./types\";\n"],"mappings":"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,EAAE,IAAI,IAAI,KAAK,EAAE,GAAG,IAAI;;AAG1C,SAAS,gBAAgB,GAAY,GAAgC;AACnE,KAAI,CAAC,EAAG,QAAO;AACf,KAAI,CAAC,EAAG,QAAO;AACf,QAAO,IAAI,KAAK,EAAE,IAAI,IAAI,KAAK,EAAE,GAAG,IAAI;;;;;ACnF1C,MAAMC,2BAAqC;CACzC;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CACA;CAEA;CACA;CACA;CACA;CACA;CACA;CACA;CACD;;;;AAKD,SAAgB,mBAAmB,MAAmC;AACpE,KAAI,CAAC,KAAM,QAAO;AAClB,QAAO,yBAAyB,MAAM,OAAO,GAAG,KAAK,KAAK,CAAC;;;;;;AAO7D,SAAgB,eACd,QACA,KACA,WACA,aACA,aAAa,OACC;CACd,MAAM,MAAM,mBAAmB,UAAU;CAGzC,MAAM,eAAe,SAAS,KAAK;EACjC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CACF,MAAM,cAAc,SAAS,KAAK;EAChC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CACF,MAAM,iBAAiB,SAAS,KAAK;EACnC;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACA;EACD,CAAC;CAGF,MAAMC,mBAA8C;EAClD,MAAM,OAAO,SAAS,KAAK;GACzB;GACA;GACA;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,MAAM,SAAS,SAAS,KAAK;GAC3B;GACA;GACA;GACD,CAAC;EACF,MAAM,MAAM,SAAS,KAAK;GACxB;GACA;GACA;GACA;GACA;GACD,CAAC;EACF,MAAM,aAAa,SAAS,KAAK,CAC/B,iCACA,sBACD,CAAC;EACF,MAAM,aAAa,SAAS,KAAK,CAC/B,iCACA,sBACD,CAAC;AACF,MAAI,CAAC,QAAQ,CAAC,UAAU,CAAC,OAAO,CAAC,cAAc,CAAC,WAC9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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rdapper",
3
- "version": "0.11.0",
3
+ "version": "0.12.0",
4
4
  "license": "MIT",
5
5
  "description": "🎩 RDAP/WHOIS fetcher, parser, and normalizer for Node",
6
6
  "repository": {
@@ -46,14 +46,14 @@
46
46
  "prepublishOnly": "npm run build"
47
47
  },
48
48
  "dependencies": {
49
- "tldts": "7.0.17"
49
+ "tldts": "7.0.19"
50
50
  },
51
51
  "devDependencies": {
52
- "@biomejs/biome": "2.3.2",
53
- "@types/node": "24.9.2",
54
- "tsdown": "0.15.12",
52
+ "@biomejs/biome": "2.3.10",
53
+ "@types/node": "25.0.3",
54
+ "tsdown": "0.18.4",
55
55
  "typescript": "5.9.3",
56
- "vitest": "^4.0.0"
56
+ "vitest": "^4.0.16"
57
57
  },
58
58
  "engines": {
59
59
  "node": ">=18.17"
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.d.ts","names":[],"sources":["../src/types.ts","../src/lib/domain.ts","../src/index.ts"],"sourcesContent":[],"mappings":";;;;;;;AAMA;AAQA;AAoBA;AA6BiB,KAzDL,YAAA,GAyDe,MAAA,GAAA,OAAA;AAoB3B;AAkCA;;;;;AA4Ca,UAnJI,aAAA,CAmJJ;EAYH;EAAY,IAAA,CAAA,EAAA,MAAA;EA0BL;EAuCA,MAAA,CAAA,EAAA,MAAA;EA0CO;EAmDR,GAAA,CAAA,EAAA,MAAA;EAED;EAIJ,KAAA,CAAA,EAAA,MAAA;EAAW;EAsBL,KAAA,CAAA,EAAA,MAAA;AAejB;;;;;;;;UApViB,OAAA;EChCZ,IAAA,EAAA,YAAY,GAAA,OAAqB,GAAA,MAAlB,GAAA,SAAU,GAAA,OAAA,GAAA,WAAA,GAAA,UAAA,GAAA,SAAA;EAMd,IAAA,CAAA,EAAA,MAAA;EAEP,YAAA,CAAA,EAAA,MAAA;EACY,KAAA,CAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAAlB,KAAA,CAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAAU,GAAA,CAAA,EAAA,MAAA,GAAA,MAAA,EAAA;EAQG,MAAA,CAAA,EAAA,MAAY,EAAA;EAcZ,IAAA,CAAA,EAAA,MAAA;EAuBA,KAAA,CAAA,EAAA,MAAA;;;;AClChB;;;;;AAyGA;AAaA;AAYa,UFzFI,UAAA,CEyFiB;;;;;;;;;;;;;;;;;;;UFrEjB,WAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAkCA,YAAA;;;;;;;;;;;;;;;;cAgBH;;;;aAID;;;;;;;;;;;;;;gBAcG;;;;;;;;gBAQA;;aAEH;;;;;;;;;;;;UAYH;;;;;;;;;;;;;;;;;;;;;;;;;UA0BO,aAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;UAuCA,aAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;wBA0CO;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;gBAmDR;;eAED;;;;WAIJ;;;;;;;;;;;;;;;;;;;;;UAsBM,YAAA;;;;WAIN;;;;;;;;;;KAWC,SAAA,oBACM,YACT,gBACJ,QAAQ;;;KCvXR,YAAA,GAAe,kBAAkB;;ADItC;AAQA;AAoBA;AA6BiB,iBCvDD,cAAA,CDuDW,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,ECrDlB,YDqDkB,CAAA,ECpDxB,UDoDwB,CAAA,OCpDN,KDoDM,CAAA;AAoB3B;AAkCA;;;AAkCgB,iBCpIA,YAAA,CDoIA,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EClIP,YDkIO,CAAA,EAAA,MAAA,GAAA,IAAA;;;;AAsBM,iBC5IN,cAAA,CD4IM,KAAA,EAAA,MAAA,CAAA,EAAA,OAAA;AAiEtB;;;;;;AAyHiB,iBC/SD,mBAAA,CDmTO,KAAA,EAAA,MAAA,EAAA,IAAA,CAAA,ECjTd,YDiTc,CAAA,EAAA,MAAA,GAAA,IAAA;;;;;AArWvB;AAQA;AAoBiB,iBEZK,MAAA,CFYE,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EEVf,aFUe,CAAA,EETrB,OFSqB,CETb,YFSa,CAAA;AA6BxB;AAoBA;AAkCA;;AAoBa,iBEVS,WAAA,CFUT,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EERJ,aFQI,CAAA,EEPV,OFOU,CAAA,OAAA,CAAA;;;;;AAoCS,iBEjCA,YAAA,CFiCA,MAAA,EAAA,MAAA,EAAA,IAAA,CAAA,EE/Bb,aF+Ba,CAAA,EE9BnB,OF8BmB,CAAA,OAAA,CAAA;AA0BtB;AAuCA;;AA6FgB,cEnLH,YFmLG,EAAA,OEnLS,MFmLT"}
package/dist/index.js.map DELETED
@@ -1 +0,0 @@
1
- {"version":3,"file":"index.js","names":["timer: ReturnType<typeof setTimeout> | undefined","data: BootstrapData","err: unknown","bases: string[]","out: string[]","merged: Json","tried: string[]","fetchedDocs: unknown[]","out: T[]","monthMap: Record<string, number>","_","dd","monStr","yyyy","mon","lastKey: string | undefined","ldhName: string | undefined","unicodeName: string | undefined","registrar: RegistrarInfo | undefined","nameservers: Nameserver[] | undefined","n: Nameserver","contacts: Contact[] | undefined","events: RdapEvent[]","whoisServer: string | undefined","out: Contact[]","roles: string[]","out: ParsedVCard","WHOIS_QUERY_TRANSFORMERS: Record<string, (query: string) => string>","net: typeof import(\"node:net\") | null","out: NonNullable<DomainRecord[\"statuses\"]>","out: Contact[]","merged: DomainRecord","WHOIS_AVAILABLE_PATTERNS: RegExp[]","registrar: RegistrarInfo | undefined","nsLines: string[]","nameservers: Nameserver[] | undefined","ipv4: string[]","ipv6: string[]","ns: Nameserver","roles: Array<{\n role: Contact[\"type\"];\n prefixes: string[];\n }>","contacts: Contact[]","nameKeys: string[]","orgKeys: string[]","emailKeys: string[]","phoneKeys: string[]","faxKeys: string[]","streetKeys: string[]","cityKeys: string[]","stateKeys: string[]","postalCodeKeys: string[]","countryKeys: string[]","results: WhoisQueryResult[]","tried: string[]","err: unknown"],"sources":["../src/lib/domain.ts","../src/lib/async.ts","../src/lib/constants.ts","../src/lib/fetch.ts","../src/rdap/bootstrap.ts","../src/rdap/client.ts","../src/rdap/links.ts","../src/rdap/merge.ts","../src/lib/dates.ts","../src/lib/privacy.ts","../src/lib/text.ts","../src/rdap/normalize.ts","../src/whois/client.ts","../src/whois/servers.ts","../src/whois/discovery.ts","../src/whois/merge.ts","../src/whois/normalize.ts","../src/whois/referral.ts","../src/index.ts"],"sourcesContent":["import { parse } from \"tldts\";\n\ntype ParseOptions = Parameters<typeof parse>[1];\n\n/**\n * Parse a domain into its parts. Passes options to `tldts.parse()`.\n * @see https://github.com/remusao/tldts/blob/master/packages/tldts-core/src/options.ts\n */\nexport function getDomainParts(\n domain: string,\n opts?: ParseOptions,\n): ReturnType<typeof parse> {\n return parse(domain, { ...opts });\n}\n\n/**\n * Get the TLD (ICANN-only public suffix) of a domain. Passes options to `tldts.parse()`.\n * @see https://github.com/remusao/tldts/blob/master/packages/tldts-core/src/options.ts\n */\nexport function getDomainTld(\n domain: string,\n opts?: ParseOptions,\n): string | null {\n const result = getDomainParts(domain, {\n allowPrivateDomains: false,\n ...opts,\n });\n return result.publicSuffix ?? null;\n}\n\n/**\n * Basic domain validity check (hostname-like), not performing DNS or RDAP.\n */\nexport function isLikelyDomain(value: string): boolean {\n const v = (value ?? \"\").trim();\n // Accept punycoded labels (xn--) by allowing digits and hyphens in TLD as well,\n // while disallowing leading/trailing hyphens in any label.\n return /^(?=.{1,253}$)(?:(?!-)[a-z0-9-]{1,63}(?<!-)\\.)+(?!-)[a-z0-9-]{2,63}(?<!-)$/.test(\n v.toLowerCase(),\n );\n}\n\nexport function punyToUnicode(domain: string): string {\n try {\n return domain.normalize(\"NFC\");\n } catch {\n return domain;\n }\n}\n\n/**\n * Normalize arbitrary input (domain or URL) to its registrable domain (eTLD+1).\n * Passes options to `tldts.parse()`.\n * Returns null when the input is not a valid ICANN domain (e.g., invalid TLD, IPs)\n * @see https://github.com/remusao/tldts/blob/master/packages/tldts-core/src/options.ts\n */\nexport function toRegistrableDomain(\n input: string,\n opts?: ParseOptions,\n): string | null {\n const raw = (input ?? \"\").trim();\n if (raw === \"\") return null;\n\n const result = getDomainParts(raw, {\n allowPrivateDomains: false,\n ...opts,\n });\n\n // Reject IPs and non-ICANN/public suffixes.\n if (result.isIp) return null;\n if (!result.isIcann) return null;\n\n const domain = result.domain ?? \"\";\n if (domain === \"\") return null;\n return domain.toLowerCase();\n}\n","export function withTimeout<T>(\n promise: Promise<T>,\n timeoutMs: number,\n reason = \"Timeout\",\n): Promise<T> {\n if (!Number.isFinite(timeoutMs) || timeoutMs <= 0) return promise;\n let timer: ReturnType<typeof setTimeout> | undefined;\n const timeout = new Promise<never>((_, reject) => {\n timer = setTimeout(() => reject(new Error(reason)), timeoutMs);\n });\n return Promise.race([\n promise.finally(() => {\n if (timer !== undefined) clearTimeout(timer);\n }),\n timeout,\n ]);\n}\n\nexport function sleep(ms: number): Promise<void> {\n return new Promise((resolve) => setTimeout(resolve, ms));\n}\n","/**\n * The timeout for HTTP requests in milliseconds. Defaults to 10 seconds.\n */\nexport const DEFAULT_TIMEOUT_MS = 10_000 as const;\n\n/**\n * The default URL for the IANA RDAP bootstrap file.\n *\n * @see {@link https://data.iana.org/rdap/dns.json IANA RDAP Bootstrap File (dns.json)}\n */\nexport const DEFAULT_BOOTSTRAP_URL = \"https://data.iana.org/rdap/dns.json\";\n","import type { FetchLike } from \"../types\";\n\n/**\n * Resolve the fetch implementation to use for HTTP requests.\n *\n * Returns the custom fetch from options if provided, otherwise falls back\n * to the global fetch function. This centralized helper ensures consistent\n * fetch resolution across all RDAP HTTP operations.\n *\n * Used internally by:\n * - Bootstrap registry fetching (`src/rdap/bootstrap.ts`)\n * - RDAP domain lookups (`src/rdap/client.ts`)\n * - RDAP related/entity link requests (`src/rdap/merge.ts`)\n *\n * @param options - Any object that may contain a custom fetch implementation\n * @returns The fetch function to use for HTTP requests\n *\n * @example\n * ```ts\n * import { resolveFetch } from './lib/fetch';\n *\n * const fetchFn = resolveFetch(options);\n * const response = await fetchFn('https://example.com/api', { method: 'GET' });\n * ```\n */\nexport function resolveFetch(options?: { customFetch?: FetchLike }): FetchLike {\n return options?.customFetch ?? fetch;\n}\n","import { withTimeout } from \"../lib/async\";\nimport { DEFAULT_BOOTSTRAP_URL, DEFAULT_TIMEOUT_MS } from \"../lib/constants\";\nimport { resolveFetch } from \"../lib/fetch\";\nimport type { BootstrapData, LookupOptions } from \"../types\";\n\n/**\n * Resolve RDAP base URLs for a given TLD using IANA's bootstrap registry.\n * Returns zero or more base URLs (always suffixed with a trailing slash).\n *\n * Bootstrap data is resolved in the following priority order:\n * 1. `options.customBootstrapData` - pre-loaded bootstrap data (no fetch)\n * 2. `options.customBootstrapUrl` - custom URL to fetch bootstrap data from\n * 3. Default IANA URL - https://data.iana.org/rdap/dns.json\n *\n * @param tld - The top-level domain to look up (e.g., \"com\", \"co.uk\")\n * @param options - Optional lookup options including custom bootstrap data/URL\n * @returns Array of RDAP base URLs for the TLD, or empty array if none found\n */\nexport async function getRdapBaseUrlsForTld(\n tld: string,\n options?: LookupOptions,\n): Promise<string[]> {\n let data: BootstrapData;\n\n // Priority 1: Use pre-loaded bootstrap data if provided (no fetch)\n if (options && \"customBootstrapData\" in options) {\n const provided = options.customBootstrapData;\n // Validate the structure to provide helpful error messages\n if (!provided || typeof provided !== \"object\") {\n throw new Error(\n \"Invalid customBootstrapData: expected an object. See BootstrapData type for required structure.\",\n );\n }\n if (!Array.isArray(provided.services)) {\n throw new Error(\n 'Invalid customBootstrapData: missing or invalid \"services\" array. See BootstrapData type for required structure.',\n );\n }\n provided.services.forEach((svc, idx) => {\n if (\n !Array.isArray(svc) ||\n svc.length < 2 ||\n !Array.isArray(svc[0]) ||\n !Array.isArray(svc[1])\n ) {\n throw new Error(\n `Invalid customBootstrapData: services[${idx}] must be a tuple of [string[], string[]].`,\n );\n }\n });\n data = provided;\n } else {\n // Priority 2 & 3: Fetch from custom URL or default IANA URL\n // Use custom fetch implementation if provided for caching/logging/monitoring\n const fetchFn = resolveFetch(options);\n const bootstrapUrl = options?.customBootstrapUrl ?? DEFAULT_BOOTSTRAP_URL;\n try {\n const res = await withTimeout(\n fetchFn(bootstrapUrl, {\n method: \"GET\",\n headers: { accept: \"application/json\" },\n signal: options?.signal,\n }),\n options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n \"RDAP bootstrap timeout\",\n );\n if (!res.ok) return [];\n data = (await res.json()) as BootstrapData;\n } catch (err: unknown) {\n // Preserve caller cancellation behavior - rethrow if explicitly aborted\n if (err instanceof Error && err.name === \"AbortError\") {\n throw err;\n }\n // Network, timeout, or JSON parse errors - return empty array to fall back to WHOIS\n return [];\n }\n }\n\n // Parse the bootstrap data to find matching base URLs for the TLD\n const target = tld.toLowerCase();\n const bases: string[] = [];\n for (const svc of data.services) {\n if (!svc[0] || !svc[1]) continue;\n const tlds = svc[0].map((x) => x.toLowerCase());\n const urls = svc[1];\n // Match exact TLD, and also support multi-label public suffixes present in IANA (rare)\n if (tlds.includes(target)) {\n for (const u of urls) {\n const base = u.endsWith(\"/\") ? u : `${u}/`;\n bases.push(base);\n }\n }\n }\n return Array.from(new Set(bases));\n}\n","import { withTimeout } from \"../lib/async\";\nimport { DEFAULT_TIMEOUT_MS } from \"../lib/constants\";\nimport { resolveFetch } from \"../lib/fetch\";\nimport type { LookupOptions } from \"../types\";\n\n/**\n * Fetch RDAP JSON for a domain from a specific RDAP base URL.\n * Throws on HTTP >= 400 (includes RDAP error JSON payloads).\n */\nexport async function fetchRdapDomain(\n domain: string,\n baseUrl: string,\n options?: LookupOptions,\n): Promise<{ url: string; json: unknown }> {\n const url = new URL(\n `domain/${encodeURIComponent(domain)}`,\n baseUrl,\n ).toString();\n const fetchFn = resolveFetch(options);\n const res = await withTimeout(\n fetchFn(url, {\n method: \"GET\",\n headers: { accept: \"application/rdap+json, application/json\" },\n signal: options?.signal,\n }),\n options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n \"RDAP lookup timeout\",\n );\n if (!res.ok) {\n const bodyText = await res.text();\n throw new Error(`RDAP ${res.status}: ${bodyText.slice(0, 500)}`);\n }\n const json = await res.json();\n return { url, json };\n}\n","import type { LookupOptions } from \"../types\";\n\ntype RdapLink = {\n value?: string;\n rel?: string;\n href?: string;\n type?: string;\n};\n\n/** Extract candidate RDAP link URLs from an RDAP document. */\nexport function extractRdapRelatedLinks(\n doc: unknown,\n opts?: Pick<LookupOptions, \"rdapLinkRels\">,\n): string[] {\n const rels = (\n opts?.rdapLinkRels?.length\n ? opts.rdapLinkRels\n : [\"related\", \"entity\", \"registrar\", \"alternate\"]\n ).map((r) => r.toLowerCase());\n const d = (doc ?? {}) as Record<string, unknown> & { links?: RdapLink[] };\n const arr = Array.isArray(d?.links) ? (d.links as RdapLink[]) : [];\n const out: string[] = [];\n for (const link of arr) {\n const rel = String(link.rel || \"\").toLowerCase();\n const type = String(link.type || \"\").toLowerCase();\n if (!rels.includes(rel)) continue;\n if (type && !/application\\/rdap\\+json/i.test(type)) continue;\n const url = link.href || link.value;\n if (url && /^https?:\\/\\//i.test(url)) out.push(url);\n }\n return Array.from(new Set(out));\n}\n","import { withTimeout } from \"../lib/async\";\nimport { DEFAULT_TIMEOUT_MS } from \"../lib/constants\";\nimport { resolveFetch } from \"../lib/fetch\";\nimport type { LookupOptions } from \"../types\";\nimport { extractRdapRelatedLinks } from \"./links\";\n\ntype Json = Record<string, unknown>;\n\n/** Merge RDAP documents with a conservative, additive strategy. */\nexport function mergeRdapDocs(baseDoc: unknown, others: unknown[]): unknown {\n const merged: Json = { ...(baseDoc as Json) };\n for (const doc of others) {\n const cur = (doc ?? {}) as Json;\n // status: array of strings\n merged.status = uniqStrings([\n ...toStringArray(merged.status),\n ...toStringArray(cur.status),\n ]);\n // events: array of objects; dedupe by eventAction + eventDate\n merged.events = uniqBy(\n [...toArray<Json>(merged.events), ...toArray<Json>(cur.events)],\n (e) =>\n `${String(e?.eventAction ?? \"\").toLowerCase()}|${String(e?.eventDate ?? \"\")}`,\n );\n // nameservers: array of objects; dedupe by ldhName/unicodeName\n merged.nameservers = uniqBy(\n [...toArray<Json>(merged.nameservers), ...toArray<Json>(cur.nameservers)],\n (n) => `${String(n?.ldhName ?? n?.unicodeName ?? \"\").toLowerCase()}`,\n );\n // entities: array; dedupe by handle if present, else by roles+vcard hash\n merged.entities = uniqBy(\n [...toArray<Json>(merged.entities), ...toArray<Json>(cur.entities)],\n (e) =>\n `${String(e?.handle ?? \"\").toLowerCase()}|${String(\n JSON.stringify(e?.roles || []),\n ).toLowerCase()}|${String(JSON.stringify(e?.vcardArray || [])).toLowerCase()}`,\n );\n // secureDNS: prefer existing; fill if missing\n if (merged.secureDNS == null && cur.secureDNS != null)\n merged.secureDNS = cur.secureDNS;\n // port43 (authoritative WHOIS): prefer existing; fill if missing\n if (merged.port43 == null && cur.port43 != null) merged.port43 = cur.port43;\n // remarks: concat simple strings if present\n const mergedRemarks = (merged as { remarks?: Json[] }).remarks;\n const curRemarks = (cur as { remarks?: Json[] }).remarks;\n if (Array.isArray(mergedRemarks) || Array.isArray(curRemarks)) {\n const a = toArray<Json>(mergedRemarks);\n const b = toArray<Json>(curRemarks);\n (merged as { remarks?: Json[] }).remarks = [...a, ...b];\n }\n }\n return merged;\n}\n\n/** Fetch and merge RDAP related documents up to a hop limit. */\nexport async function fetchAndMergeRdapRelated(\n domain: string,\n baseDoc: unknown,\n opts?: LookupOptions,\n): Promise<{ merged: unknown; serversTried: string[] }> {\n const tried: string[] = [];\n if (opts?.rdapFollowLinks === false)\n return { merged: baseDoc, serversTried: tried };\n const maxHops = Math.max(0, opts?.maxRdapLinkHops ?? 2);\n if (maxHops === 0) return { merged: baseDoc, serversTried: tried };\n\n const visited = new Set<string>();\n let current = baseDoc;\n let hops = 0;\n\n // BFS: collect links from the latest merged doc only to keep it simple and bounded\n while (hops < maxHops) {\n const links = extractRdapRelatedLinks(current, {\n rdapLinkRels: opts?.rdapLinkRels,\n });\n const nextBatch = links.filter((u) => !visited.has(u));\n if (nextBatch.length === 0) break;\n const fetchedDocs: unknown[] = [];\n for (const url of nextBatch) {\n visited.add(url);\n try {\n const { json } = await fetchRdapUrl(url, opts);\n tried.push(url);\n // only accept docs that appear related to the same domain when possible\n // if ldhName/unicodeName present, they should match the queried domain (case-insensitive)\n const ldh = String((json as Json)?.ldhName ?? \"\").toLowerCase();\n const uni = String((json as Json)?.unicodeName ?? \"\").toLowerCase();\n if (ldh && !sameDomain(ldh, domain)) continue;\n if (uni && !sameDomain(uni, domain)) continue;\n fetchedDocs.push(json);\n } catch {\n // ignore failures and continue\n }\n }\n if (fetchedDocs.length === 0) break;\n current = mergeRdapDocs(current, fetchedDocs);\n hops += 1;\n }\n return { merged: current, serversTried: tried };\n}\n\nasync function fetchRdapUrl(\n url: string,\n options?: LookupOptions,\n): Promise<{ url: string; json: unknown }> {\n const fetchFn = resolveFetch(options);\n const res = await withTimeout(\n fetchFn(url, {\n method: \"GET\",\n headers: { accept: \"application/rdap+json, application/json\" },\n signal: options?.signal,\n }),\n options?.timeoutMs ?? DEFAULT_TIMEOUT_MS,\n \"RDAP link fetch timeout\",\n );\n if (!res.ok) {\n const bodyText = await res.text();\n throw new Error(`RDAP ${res.status}: ${bodyText.slice(0, 500)}`);\n }\n const json = await res.json();\n // Optionally parse Link header for future iterations; the main loop inspects body.links\n return { url, json };\n}\n\nfunction toArray<T>(val: unknown): T[] {\n return Array.isArray(val) ? (val as T[]) : [];\n}\nfunction toStringArray(val: unknown): string[] {\n return Array.isArray(val) ? (val as unknown[]).map((v) => String(v)) : [];\n}\nfunction uniqStrings(arr: string[]): string[] {\n return Array.from(new Set(arr));\n}\nfunction uniqBy<T>(arr: T[], key: (t: T) => string): T[] {\n const seen = new Set<string>();\n const out: T[] = [];\n for (const item of arr) {\n const k = key(item);\n if (seen.has(k)) continue;\n seen.add(k);\n out.push(item);\n }\n return out;\n}\nfunction sameDomain(a: string, b: string): boolean {\n return a.toLowerCase() === b.toLowerCase();\n}\n","// Lightweight date parsing helpers to avoid external dependencies.\n// We aim to parse common RDAP and WHOIS date representations and return a UTC ISO string.\nexport function toISO(\n dateLike: string | number | Date | undefined | null,\n): string | undefined {\n if (dateLike == null) return undefined;\n if (dateLike instanceof Date) return toIsoFromDate(dateLike);\n if (typeof dateLike === \"number\") return toIsoFromDate(new Date(dateLike));\n const raw = String(dateLike).trim();\n if (!raw) return undefined;\n // Try several structured formats seen in WHOIS outputs (treat as UTC when no TZ provided)\n const tryFormats = [\n // 2023-01-02 03:04:05Z or without Z\n /^(\\d{4})-(\\d{2})-(\\d{2})[ T](\\d{2}):(\\d{2}):(\\d{2})(?:Z|([+-]\\d{2})(?::?(\\d{2}))?)?$/,\n // 2023/01/02 03:04:05\n /^(\\d{4})\\/(\\d{2})\\/(\\d{2})[ T](\\d{2}):(\\d{2}):(\\d{2})(?:Z|([+-]\\d{2})(?::?(\\d{2}))?)?$/,\n // 02-Jan-2023\n /^(\\d{2})-([A-Za-z]{3})-(\\d{4})$/,\n // 21-07-2026 (DD-MM-YYYY used by .il, .hk)\n /^(\\d{2})-(\\d{2})-(\\d{4})$/,\n // Jan 02 2023\n /^([A-Za-z]{3})\\s+(\\d{1,2})\\s+(\\d{4})$/,\n ];\n for (const re of tryFormats) {\n const m = raw.match(re);\n if (!m) continue;\n const d = parseDateWithRegex(m, re);\n if (d) return toIsoFromDate(d);\n }\n // Fallback to native Date parsing (handles ISO and RFC2822 with TZ)\n const native = new Date(raw);\n if (!Number.isNaN(native.getTime())) return toIsoFromDate(native);\n return undefined;\n}\n\nfunction toIsoFromDate(d: Date): string | undefined {\n try {\n return new Date(\n Date.UTC(\n d.getUTCFullYear(),\n d.getUTCMonth(),\n d.getUTCDate(),\n d.getUTCHours(),\n d.getUTCMinutes(),\n d.getUTCSeconds(),\n 0,\n ),\n )\n .toISOString()\n .replace(/\\.\\d{3}Z$/, \"Z\");\n } catch {\n return undefined;\n }\n}\n\nfunction parseDateWithRegex(\n m: RegExpMatchArray,\n _re: RegExp,\n): Date | undefined {\n const monthMap: Record<string, number> = {\n jan: 0,\n feb: 1,\n mar: 2,\n apr: 3,\n may: 4,\n jun: 5,\n jul: 6,\n aug: 7,\n sep: 8,\n oct: 9,\n nov: 10,\n dec: 11,\n };\n try {\n // If the matched string contains time components, parse as Y-M-D H:M:S\n if (m[0].includes(\":\")) {\n const [_, y, mo, d, hh, mm, ss, offH, offM] = m;\n if (!y || !mo || !d || !hh || !mm || !ss) return undefined;\n // Base time as UTC\n let dt = Date.UTC(\n Number(y),\n Number(mo) - 1,\n Number(d),\n Number(hh),\n Number(mm),\n Number(ss),\n );\n // Apply timezone offset if present (e.g., +0000, -0500, +05:30)\n if (offH) {\n const sign = offH.startsWith(\"-\") ? -1 : 1;\n const hours = Math.abs(Number(offH));\n const minutes = offM ? Number(offM) : 0;\n const offsetMs = sign * (hours * 60 + minutes) * 60 * 1000;\n // The captured time is local with an explicit offset; convert to UTC\n dt -= offsetMs;\n }\n return new Date(dt);\n }\n // If the matched string contains hyphens, check if numeric (DD-MM-YYYY) or alpha (DD-MMM-YYYY)\n if (m[0].includes(\"-\")) {\n const [_, dd, monStr, yyyy] = m;\n if (!monStr || !dd || !yyyy) return undefined;\n // Check if month component is numeric (DD-MM-YYYY) or alphabetic (DD-MMM-YYYY)\n if (/^\\d+$/.test(monStr)) {\n // DD-MM-YYYY format (e.g., 21-07-2026)\n return new Date(Date.UTC(Number(yyyy), Number(monStr) - 1, Number(dd)));\n }\n // DD-MMM-YYYY format (e.g., 02-Jan-2023)\n const mon = monthMap[monStr.toLowerCase()];\n return new Date(Date.UTC(Number(yyyy), mon, Number(dd)));\n }\n // Otherwise treat as MMM DD YYYY\n const [_, monStr, dd, yyyy] = m;\n if (!monStr || !dd || !yyyy) return undefined;\n const mon = monthMap[monStr.toLowerCase()];\n return new Date(Date.UTC(Number(yyyy), mon, Number(dd)));\n } catch {\n // fall through to undefined\n }\n return undefined;\n}\n","export const PRIVACY_NAME_KEYWORDS = [\n \"redacted\",\n \"privacy\",\n \"private\",\n \"withheld\",\n \"not disclosed\",\n \"protected\",\n \"protection\",\n \"privado\", // Spanish\n \"datos privados\", // Spanish\n \"data protected\",\n \"data redacted\",\n \"gdpr redacted\",\n \"gdpr masked\",\n \"non-public data\",\n \"statutory masking\",\n \"redacted.forprivacy\",\n \"registration private\",\n \"hidden upon user request\",\n \"not available from registry\",\n];\n\n// Completely unusable/empty values that should be filtered\nexport const NO_DATA_VALUES = [\n \"-\",\n \".\",\n \"n/a\",\n \"na\",\n \"no data\",\n \"not available\",\n \"not applicable\",\n \"none\",\n];\n\nexport function isPrivacyName(value: string): boolean {\n const v = value.toLowerCase().trim();\n // Check for complete no-data values\n if (NO_DATA_VALUES.includes(v)) return true;\n // Check for privacy keywords\n return PRIVACY_NAME_KEYWORDS.some((k) => v.includes(k));\n}\n","export function uniq<T>(arr: T[] | undefined | null): T[] | undefined {\n if (!arr) return undefined;\n return Array.from(new Set(arr));\n}\n\nexport function parseKeyValueLines(text: string): Record<string, string[]> {\n const map = new Map<string, string[]>();\n const lines = text.split(/\\r?\\n/);\n let lastKey: string | undefined;\n for (const rawLine of lines) {\n const line = rawLine.replace(/\\s+$/, \"\");\n if (!line.trim()) continue;\n // Bracketed form: [Key] value (common in .jp and some ccTLDs)\n const bracket = line.match(/^\\s*\\[([^\\]]+)\\]\\s*(.*)$/);\n if (bracket?.[1] !== undefined && bracket?.[2] !== undefined) {\n const key = bracket[1].trim().toLowerCase();\n const value = bracket[2].trim();\n const list = map.get(key) ?? [];\n if (value) list.push(value);\n map.set(key, list);\n lastKey = key;\n continue;\n }\n // Colon form: Key: value\n const idx = line.indexOf(\":\");\n if (idx !== -1) {\n const key = line.slice(0, idx).trim().toLowerCase();\n const value = line.slice(idx + 1).trim();\n if (!key) {\n lastKey = undefined;\n continue;\n }\n const list = map.get(key) ?? [];\n if (value) list.push(value);\n map.set(key, list);\n lastKey = key;\n continue;\n }\n // Continuation line: starts with indentation after a key appeared\n if (lastKey && /^\\s+/.test(line)) {\n const value = line.trim();\n if (value) {\n const list = map.get(lastKey) ?? [];\n list.push(value);\n map.set(lastKey, list);\n }\n }\n // Otherwise ignore non key-value lines\n }\n return Object.fromEntries(map);\n}\n\nexport function parseCsv(value: string | undefined): string[] | undefined {\n if (!value) return undefined;\n return value\n .split(/[,\\s]+/)\n .map((s) => s.trim())\n .filter(Boolean);\n}\n\nexport function asString(value: unknown): string | undefined {\n return typeof value === \"string\" ? value : undefined;\n}\n\nexport function asStringArray(value: unknown): string[] | undefined {\n return Array.isArray(value)\n ? (value.filter((x) => typeof x === \"string\") as string[])\n : undefined;\n}\n\nexport function asDateLike(value: unknown): string | number | Date | undefined {\n if (\n typeof value === \"string\" ||\n typeof value === \"number\" ||\n value instanceof Date\n )\n return value;\n return undefined;\n}\n","import { toISO } from \"../lib/dates\";\nimport { isPrivacyName } from \"../lib/privacy\";\nimport { asDateLike, asString, asStringArray, uniq } from \"../lib/text\";\nimport type {\n Contact,\n DomainRecord,\n Nameserver,\n RegistrarInfo,\n} from \"../types\";\n\ntype RdapDoc = Record<string, unknown>;\n\n/**\n * Convert RDAP JSON into our normalized DomainRecord.\n * This function is defensive: RDAP servers vary in completeness and field naming.\n */\nexport function normalizeRdap(\n inputDomain: string,\n tld: string,\n rdap: unknown,\n rdapServersTried: string[],\n includeRaw = false,\n): DomainRecord {\n const doc = (rdap ?? {}) as RdapDoc;\n\n // Prefer ldhName (punycode) and unicodeName if provided\n const ldhName: string | undefined =\n asString(doc.ldhName) || asString(doc.handle);\n const unicodeName: string | undefined = asString(doc.unicodeName);\n\n // Registrar entity can be provided with role \"registrar\"\n const registrar: RegistrarInfo | undefined = extractRegistrar(\n doc.entities as unknown,\n );\n\n // Nameservers: normalize host + IPs\n const nameservers: Nameserver[] | undefined = Array.isArray(doc.nameservers)\n ? (doc.nameservers as RdapDoc[])\n .map((ns) => {\n const host = (\n asString(ns.ldhName) ??\n asString(ns.unicodeName) ??\n \"\"\n ).toLowerCase();\n const ip = ns.ipAddresses as RdapDoc | undefined;\n const ipv4 = asStringArray(ip?.v4);\n const ipv6 = asStringArray(ip?.v6);\n const n: Nameserver = { host };\n if (ipv4?.length) n.ipv4 = ipv4;\n if (ipv6?.length) n.ipv6 = ipv6;\n return n;\n })\n .filter((n) => !!n.host)\n : undefined;\n\n // Contacts: RDAP entities include roles like registrant, administrative, technical, billing, abuse\n const contacts: Contact[] | undefined = extractContacts(\n doc.entities as unknown,\n );\n\n // Derive privacy flag from registrant name/org keywords\n const registrant = contacts?.find((c) => c.type === \"registrant\");\n const privacyEnabled = !!(\n registrant &&\n (\n [registrant.name, registrant.organization].filter(Boolean) as string[]\n ).some(isPrivacyName)\n );\n\n // RDAP uses IANA EPP status values. Preserve raw plus a description if any remarks are present.\n const statuses = Array.isArray(doc.status)\n ? (doc.status as unknown[])\n .filter((s): s is string => typeof s === \"string\")\n .map((s) => ({ status: s, raw: s }))\n : undefined;\n\n // Secure DNS info\n const secureDNS = doc.secureDNS as\n | { delegationSigned?: unknown; dsData?: Array<Record<string, unknown>> }\n | undefined;\n const dnssec = secureDNS\n ? {\n enabled: !!secureDNS.delegationSigned,\n dsRecords: Array.isArray(secureDNS.dsData)\n ? (secureDNS.dsData as Array<Record<string, unknown>>).map((d) => ({\n keyTag: d.keyTag as number | undefined,\n algorithm: d.algorithm as number | undefined,\n digestType: d.digestType as number | undefined,\n digest: d.digest as string | undefined,\n }))\n : undefined,\n }\n : undefined;\n\n // RDAP \"events\" contain timestamps for registration, last changed, expiration, deletion, etc.\n type RdapEvent = { eventAction?: string; eventDate?: string | number | Date };\n const events: RdapEvent[] = Array.isArray(doc.events)\n ? (doc.events as unknown[] as RdapEvent[])\n : [];\n const byAction = (action: string) =>\n events.find(\n (e) =>\n typeof e?.eventAction === \"string\" &&\n e.eventAction.toLowerCase().includes(action),\n );\n const creationDate = toISO(\n asDateLike(byAction(\"registration\")?.eventDate) ??\n asDateLike(doc.registrationDate),\n );\n const updatedDate = toISO(\n asDateLike(byAction(\"last changed\")?.eventDate) ??\n asDateLike(doc.lastChangedDate),\n );\n const expirationDate = toISO(\n asDateLike(byAction(\"expiration\")?.eventDate) ??\n asDateLike(doc.expirationDate),\n );\n const deletionDate = toISO(\n asDateLike(byAction(\"deletion\")?.eventDate) ?? asDateLike(doc.deletionDate),\n );\n\n // Derive a simple transfer lock flag from statuses\n const transferLock = !!statuses?.some((s: { status: string }) =>\n /transferprohibited/i.test(s.status),\n );\n\n // The RDAP document may include \"port43\" pointer to authoritative WHOIS\n const whoisServer: string | undefined = asString(doc.port43);\n\n const record: DomainRecord = {\n domain: unicodeName || ldhName || inputDomain,\n tld,\n isRegistered: true,\n isIDN: /(^|\\.)xn--/i.test(ldhName || inputDomain),\n unicodeName: unicodeName || undefined,\n punycodeName: ldhName || undefined,\n registry: undefined, // RDAP rarely includes a clean registry operator name\n registrar: registrar,\n reseller: undefined,\n statuses: statuses,\n creationDate,\n updatedDate,\n expirationDate,\n deletionDate,\n transferLock,\n dnssec,\n nameservers: nameservers\n ? uniq(nameservers.map((n) => ({ ...n, host: n.host.toLowerCase() })))\n : undefined,\n contacts,\n privacyEnabled: privacyEnabled ? true : undefined,\n whoisServer,\n rdapServers: rdapServersTried,\n rawRdap: includeRaw ? rdap : undefined,\n rawWhois: undefined,\n source: \"rdap\",\n warnings: undefined,\n };\n\n return record;\n}\n\nfunction extractRegistrar(entities: unknown): RegistrarInfo | undefined {\n if (!Array.isArray(entities)) return undefined;\n for (const ent of entities) {\n const roles: string[] = Array.isArray((ent as RdapDoc)?.roles)\n ? ((ent as RdapDoc).roles as unknown[]).filter(\n (r): r is string => typeof r === \"string\",\n )\n : [];\n if (!roles.some((r) => /registrar/i.test(r))) continue;\n const v = parseVcard((ent as RdapDoc)?.vcardArray);\n const ianaId = Array.isArray((ent as RdapDoc)?.publicIds)\n ? ((ent as RdapDoc).publicIds as Array<RdapDoc>).find((id) =>\n /iana\\s*registrar\\s*id/i.test(String(id?.type)),\n )?.identifier\n : undefined;\n return {\n name: v.fn || v.org || asString((ent as RdapDoc)?.handle) || undefined,\n ianaId: asString(ianaId),\n url: v.url ?? undefined,\n email: v.email ?? undefined,\n phone: v.tel ?? undefined,\n };\n }\n return undefined;\n}\n\nfunction extractContacts(entities: unknown): Contact[] | undefined {\n if (!Array.isArray(entities)) return undefined;\n const out: Contact[] = [];\n for (const ent of entities) {\n const roles: string[] = Array.isArray((ent as RdapDoc)?.roles)\n ? ((ent as RdapDoc).roles as unknown[]).filter(\n (r): r is string => typeof r === \"string\",\n )\n : [];\n const v = parseVcard((ent as RdapDoc)?.vcardArray);\n const type = roles.find((r) =>\n /registrant|administrative|technical|billing|abuse|reseller/i.test(r),\n );\n if (!type) continue;\n const map: Record<string, Contact[\"type\"]> = {\n registrant: \"registrant\",\n administrative: \"admin\",\n technical: \"tech\",\n billing: \"billing\",\n abuse: \"abuse\",\n reseller: \"reseller\",\n } as const;\n const roleKey = (map[type.toLowerCase()] ?? \"unknown\") as Contact[\"type\"];\n out.push({\n type: roleKey,\n name: v.fn,\n organization: v.org,\n email: v.email,\n phone: v.tel,\n fax: v.fax,\n street: v.street,\n city: v.locality,\n state: v.region,\n postalCode: v.postcode,\n country: v.country,\n countryCode: v.countryCode,\n });\n }\n return out.length ? out : undefined;\n}\n\ninterface ParsedVCard {\n fn?: string;\n org?: string;\n email?: string;\n tel?: string;\n fax?: string;\n url?: string;\n street?: string[];\n locality?: string;\n region?: string;\n postcode?: string;\n country?: string;\n countryCode?: string;\n}\n\n// Parse a minimal subset of vCard 4.0 arrays as used in RDAP \"vcardArray\" fields\nfunction parseVcard(vcardArray: unknown): ParsedVCard {\n // vcardArray is typically [\"vcard\", [[\"version\",{} ,\"text\",\"4.0\"], [\"fn\",{} ,\"text\",\"Example\"], ...]]\n if (\n !Array.isArray(vcardArray) ||\n vcardArray[0] !== \"vcard\" ||\n !Array.isArray(vcardArray[1])\n )\n return {};\n const entries = vcardArray[1] as Array<\n [string, Record<string, unknown>, string, unknown]\n >;\n const out: ParsedVCard = {};\n for (const e of entries) {\n const key = e?.[0];\n const value = e?.[3];\n if (!key) continue;\n switch (String(key).toLowerCase()) {\n case \"fn\":\n out.fn = asString(value);\n break;\n case \"org\":\n out.org = Array.isArray(value)\n ? value.map((x) => String(x)).join(\" \")\n : asString(value);\n break;\n case \"email\":\n out.email = asString(value);\n break;\n case \"tel\":\n out.tel = asString(value);\n break;\n case \"url\":\n out.url = asString(value);\n break;\n case \"adr\": {\n // adr value is [postOfficeBox, extendedAddress, street, locality, region, postalCode, country]\n if (Array.isArray(value)) {\n out.street = value[2] ? String(value[2]).split(/\\n|,\\s*/) : undefined;\n out.locality = asString(value[3]);\n out.region = asString(value[4]);\n out.postcode = asString(value[5]);\n out.country = asString(value[6]);\n }\n break;\n }\n }\n }\n // Best effort country code from country name (often omitted). Leaving undefined unless explicitly provided.\n return out;\n}\n","import { withTimeout } from \"../lib/async\";\nimport { DEFAULT_TIMEOUT_MS } from \"../lib/constants\";\nimport type { LookupOptions } from \"../types\";\n\nexport interface WhoisQueryResult {\n serverQueried: string;\n text: string;\n}\n\n/**\n * Some WHOIS servers default to non-English responses. This mapping allows automatic\n * query transformation to request English-only output for easier parsing.\n *\n * To add new servers: Add an entry with the hostname and transformation function:\n * \"whois.example.org\": (query) => `${query}/english`,\n */\nconst WHOIS_QUERY_TRANSFORMERS: Record<string, (query: string) => string> = {\n \"whois.jprs.jp\": (query) => `${query}/e`, // Append /e for English-only response\n};\n\n/**\n * Perform a WHOIS query against an RFC 3912 server over TCP 43.\n * Returns the raw text and the server used.\n */\nexport async function whoisQuery(\n server: string,\n query: string,\n options?: LookupOptions,\n): Promise<WhoisQueryResult> {\n const timeoutMs = options?.timeoutMs ?? DEFAULT_TIMEOUT_MS;\n const port = 43;\n const host = server.replace(/^whois:\\/\\//i, \"\");\n\n // Transform query if server requires special formatting\n const transformer = WHOIS_QUERY_TRANSFORMERS[host];\n const transformedQuery = transformer ? transformer(query) : query;\n\n const text = await withTimeout(\n queryTcp(host, port, transformedQuery, options),\n timeoutMs,\n \"WHOIS timeout\",\n );\n return { serverQueried: server, text };\n}\n\n// Low-level WHOIS TCP client. Some registries require CRLF after the domain query.\nasync function queryTcp(\n host: string,\n port: number,\n query: string,\n options?: LookupOptions,\n): Promise<string> {\n let net: typeof import(\"node:net\") | null;\n try {\n net = await import(\"node:net\");\n } catch {\n net = null;\n }\n\n if (!net?.createConnection) {\n throw new Error(\n \"WHOIS client is only available in Node.js runtimes; try setting `rdapOnly: true`.\",\n );\n }\n\n return new Promise((resolve, reject) => {\n const socket = net.createConnection({ host, port });\n let data = \"\";\n let done = false;\n const cleanup = () => {\n if (done) return;\n done = true;\n socket.destroy();\n };\n socket.setTimeout((options?.timeoutMs ?? DEFAULT_TIMEOUT_MS) - 1000, () => {\n cleanup();\n reject(new Error(\"WHOIS socket timeout\"));\n });\n socket.on(\"error\", (err) => {\n cleanup();\n reject(err);\n });\n socket.on(\"data\", (chunk) => {\n data += chunk.toString(\"utf8\");\n });\n socket.on(\"end\", () => {\n cleanup();\n resolve(data);\n });\n socket.on(\"connect\", () => {\n socket.write(`${query}\\r\\n`);\n });\n });\n}\n","// Curated authoritative WHOIS servers by TLD (exceptions to default/referral logic)\n// Source of truth checked against IANA delegation records; prefer RDAP first.\nexport const WHOIS_TLD_EXCEPTIONS = {\n // gTLDs (port-43 still available at registry)\n com: \"whois.verisign-grs.com\",\n net: \"whois.verisign-grs.com\",\n org: \"whois.publicinterestregistry.org\", // PIR\n biz: \"whois.nic.biz\",\n name: \"whois.nic.name\",\n edu: \"whois.educause.edu\",\n gov: \"whois.nic.gov\", // was whois.dotgov.gov\n\n // ccTLDs & other TLDs with working port-43 WHOIS\n de: \"whois.denic.de\",\n jp: \"whois.jprs.jp\",\n fr: \"whois.nic.fr\",\n it: \"whois.nic.it\",\n pl: \"whois.dns.pl\",\n nl: \"whois.domain-registry.nl\",\n be: \"whois.dns.be\",\n se: \"whois.iis.se\",\n no: \"whois.norid.no\",\n fi: \"whois.fi\",\n cz: \"whois.nic.cz\",\n es: \"whois.nic.es\",\n br: \"whois.registro.br\",\n ca: \"whois.cira.ca\",\n dk: \"whois.punktum.dk\", // was whois.dk-hostmaster.dk\n hk: \"whois.hkirc.hk\",\n sg: \"whois.sgnic.sg\",\n in: \"whois.nixiregistry.in\", // was whois.registry.in\n nz: \"whois.irs.net.nz\", // was whois.srs.net.nz\n ch: \"whois.nic.ch\",\n li: \"whois.nic.li\",\n io: \"whois.nic.io\",\n ai: \"whois.nic.ai\",\n ru: \"whois.tcinet.ru\",\n su: \"whois.tcinet.ru\",\n us: \"whois.nic.us\",\n co: \"whois.nic.co\",\n me: \"whois.nic.me\",\n tv: \"whois.nic.tv\",\n cc: \"ccwhois.verisign-grs.com\",\n eu: \"whois.eu\",\n au: \"whois.auda.org.au\",\n kr: \"whois.kr\",\n tw: \"whois.twnic.net.tw\",\n uk: \"whois.nic.uk\",\n nu: \"whois.iis.nu\",\n \"xn--p1ai\": \"whois.tcinet.ru\", // .рф\n\n // CentralNic-operated public SLD zones (still WHOIS @ centralnic)\n \"uk.com\": \"whois.centralnic.com\",\n \"uk.net\": \"whois.centralnic.com\",\n \"gb.com\": \"whois.centralnic.com\",\n \"gb.net\": \"whois.centralnic.com\",\n \"eu.com\": \"whois.centralnic.com\",\n \"us.com\": \"whois.centralnic.com\",\n \"se.com\": \"whois.centralnic.com\",\n \"de.com\": \"whois.centralnic.com\",\n \"br.com\": \"whois.centralnic.com\",\n \"ru.com\": \"whois.centralnic.com\",\n \"cn.com\": \"whois.centralnic.com\",\n \"sa.com\": \"whois.centralnic.com\",\n \"co.com\": \"whois.centralnic.com\",\n} as Record<string, string>;\n","import type { LookupOptions } from \"../types\";\nimport { whoisQuery } from \"./client\";\nimport { WHOIS_TLD_EXCEPTIONS } from \"./servers\";\n\n/**\n * Parse the IANA WHOIS response for a TLD and extract the WHOIS server\n * without crossing line boundaries. Some TLDs (e.g. .np) leave the field\n * blank, in which case this returns undefined.\n */\nexport function parseIanaWhoisServer(text: string): string | undefined {\n // Search lines in priority order: whois, refer, whois server\n const fields = [\"whois\", \"refer\", \"whois server\"];\n const lines = String(text).split(/\\r?\\n/);\n for (const field of fields) {\n for (const raw of lines) {\n const line = raw.trimEnd();\n // Match beginning of line, allowing leading spaces, case-insensitive\n const re = new RegExp(`^\\\\s*${field}\\\\s*:\\\\s*(.*?)$`, \"i\");\n const m = line.match(re);\n if (m) {\n const value = (m[1] || \"\").trim();\n if (value) return value;\n }\n }\n }\n return undefined;\n}\n\n/**\n * Parse a likely registration information URL from an IANA WHOIS response.\n * Looks at lines like:\n * remarks: Registration information: http://example.tld\n * url: https://registry.example\n */\nexport function parseIanaRegistrationInfoUrl(text: string): string | undefined {\n const lines = String(text).split(/\\r?\\n/);\n for (const raw of lines) {\n const line = raw.trim();\n if (!/^\\s*(remarks|url|website)\\s*:/i.test(line)) continue;\n const urlMatch = line.match(/https?:\\/\\/\\S+/i);\n if (urlMatch?.[0]) return urlMatch[0];\n }\n return undefined;\n}\n\n/** Fetch raw IANA WHOIS text for a TLD (best-effort). */\nexport async function getIanaWhoisTextForTld(\n tld: string,\n options?: LookupOptions,\n): Promise<string | undefined> {\n try {\n const res = await whoisQuery(\"whois.iana.org\", tld.toLowerCase(), options);\n return res.text;\n } catch {\n return undefined;\n }\n}\n\n/**\n * Best-effort discovery of the authoritative WHOIS server for a TLD via IANA root DB.\n */\nexport async function ianaWhoisServerForTld(\n tld: string,\n options?: LookupOptions,\n): Promise<string | undefined> {\n const key = tld.toLowerCase();\n // 1) Explicit hint override\n const hint = options?.whoisHints?.[key];\n if (hint) return normalizeServer(hint);\n\n // 2) IANA WHOIS authoritative discovery over TCP 43\n try {\n const res = await whoisQuery(\"whois.iana.org\", key, options);\n const txt = res.text;\n const server = parseIanaWhoisServer(txt);\n if (server) return normalizeServer(server);\n } catch {\n // fallthrough to exceptions/guess\n }\n\n // 3) Curated exceptions\n const exception = WHOIS_TLD_EXCEPTIONS[key];\n if (exception) return normalizeServer(exception);\n\n return undefined;\n}\n\n/**\n * Extract registrar referral WHOIS server from a WHOIS response, if present.\n */\nexport function extractWhoisReferral(text: string): string | undefined {\n const patterns = [\n /^Registrar WHOIS Server:\\s*(.+)$/im,\n /^Whois Server:\\s*(.+)$/im,\n /^ReferralServer:\\s*whois:\\/\\/(.+)$/im,\n ];\n for (const re of patterns) {\n const m = text.match(re);\n if (m?.[1]) return m[1].trim();\n }\n return undefined;\n}\n\nfunction normalizeServer(server: string): string {\n return server.replace(/^whois:\\/\\//i, \"\").replace(/\\/$/, \"\");\n}\n","import { uniq } from \"../lib/text\";\nimport type { Contact, DomainRecord, Nameserver } from \"../types\";\n\nfunction dedupeStatuses(\n a?: DomainRecord[\"statuses\"],\n b?: DomainRecord[\"statuses\"],\n) {\n const list = [...(a || []), ...(b || [])];\n const seen = new Set<string>();\n const out: NonNullable<DomainRecord[\"statuses\"]> = [];\n for (const s of list) {\n const key = (s?.status || \"\").toLowerCase();\n if (!key || seen.has(key)) continue;\n seen.add(key);\n out.push(s);\n }\n return out.length ? out : undefined;\n}\n\nfunction dedupeNameservers(a?: Nameserver[], b?: Nameserver[]) {\n const map = new Map<string, Nameserver>();\n for (const ns of [...(a || []), ...(b || [])]) {\n const host = ns.host.toLowerCase();\n const prev = map.get(host);\n if (!prev) {\n map.set(host, { ...ns, host });\n continue;\n }\n const ipv4 = uniq([...(prev.ipv4 || []), ...(ns.ipv4 || [])]);\n const ipv6 = uniq([...(prev.ipv6 || []), ...(ns.ipv6 || [])]);\n map.set(host, { host, ipv4, ipv6 });\n }\n const out = Array.from(map.values());\n return out.length ? out : undefined;\n}\n\nfunction dedupeContacts(a?: Contact[], b?: Contact[]) {\n const list = [...(a || []), ...(b || [])];\n const seen = new Set<string>();\n const out: Contact[] = [];\n for (const c of list) {\n const key = `${c.type}|${(c.organization || c.name || c.email || \"\").toString().toLowerCase()}`;\n if (seen.has(key)) continue;\n seen.add(key);\n out.push(c);\n }\n return out.length ? out : undefined;\n}\n\n/** Conservative merge: start with base; fill missing scalars; union arrays; prefer more informative dates. */\nexport function mergeWhoisRecords(\n base: DomainRecord,\n others: DomainRecord[],\n): DomainRecord {\n const merged: DomainRecord = { ...base };\n for (const cur of others) {\n merged.isRegistered = merged.isRegistered || cur.isRegistered;\n merged.registry = merged.registry ?? cur.registry;\n merged.registrar = merged.registrar ?? cur.registrar;\n merged.reseller = merged.reseller ?? cur.reseller;\n merged.statuses = dedupeStatuses(merged.statuses, cur.statuses);\n // Dates: prefer earliest creation, latest updated/expiration when available\n merged.creationDate = preferEarliestIso(\n merged.creationDate,\n cur.creationDate,\n );\n merged.updatedDate = preferLatestIso(merged.updatedDate, cur.updatedDate);\n merged.expirationDate = preferLatestIso(\n merged.expirationDate,\n cur.expirationDate,\n );\n merged.deletionDate = merged.deletionDate ?? cur.deletionDate;\n merged.transferLock = Boolean(merged.transferLock || cur.transferLock);\n merged.dnssec = merged.dnssec ?? cur.dnssec;\n merged.nameservers = dedupeNameservers(merged.nameservers, cur.nameservers);\n merged.contacts = dedupeContacts(merged.contacts, cur.contacts);\n merged.privacyEnabled = merged.privacyEnabled ?? cur.privacyEnabled;\n // Keep whoisServer pointing to the latest contributing authoritative server\n merged.whoisServer = cur.whoisServer ?? merged.whoisServer;\n // rawWhois: keep last contributing text\n merged.rawWhois = cur.rawWhois ?? merged.rawWhois;\n }\n return merged;\n}\n\nfunction preferEarliestIso(a?: string, b?: string): string | undefined {\n if (!a) return b;\n if (!b) return a;\n return new Date(a) <= new Date(b) ? a : b;\n}\n\nfunction preferLatestIso(a?: string, b?: string): string | undefined {\n if (!a) return b;\n if (!b) return a;\n return new Date(a) >= new Date(b) ? a : b;\n}\n","import { toISO } from \"../lib/dates\";\nimport { isPrivacyName } from \"../lib/privacy\";\nimport { parseKeyValueLines, uniq } from \"../lib/text\";\nimport type {\n Contact,\n DomainRecord,\n Nameserver,\n RegistrarInfo,\n} from \"../types\";\n\n// Common WHOIS availability phrases seen across registries/registrars\nconst WHOIS_AVAILABLE_PATTERNS: RegExp[] = [\n /\\bno match\\b/i,\n /\\bnot found\\b/i,\n /\\bno entries found\\b/i,\n /\\bno data found\\b/i,\n /\\bavailable for registration\\b/i,\n /\\bdomain\\s+available\\b/i,\n /\\bdomain status[:\\s]+available\\b/i,\n /\\bobject does not exist\\b/i,\n /\\bthe queried object does not exist\\b/i,\n /\\bqueried object does not exist\\b/i,\n /\\breturned 0 objects\\b/i,\n // Common variants across ccTLDs/registrars\n /\\bstatus:\\s*free\\b/i,\n /\\bstatus:\\s*available\\b/i,\n /\\bno object found\\b/i,\n /\\bnicht gefunden\\b/i, // German: \"not found\"\n /\\bpending release\\b/i, // often signals not registered/being deleted\n];\n\n/**\n * Best-effort heuristic to determine if a WHOIS response indicates the domain is available.\n */\nexport function isAvailableByWhois(text: string | undefined): boolean {\n if (!text) return false;\n return WHOIS_AVAILABLE_PATTERNS.some((re) => re.test(text));\n}\n\n/**\n * Convert raw WHOIS text into our normalized DomainRecord.\n * Heuristics cover many gTLD and ccTLD formats; exact fields vary per registry.\n */\nexport function normalizeWhois(\n domain: string,\n tld: string,\n whoisText: string,\n whoisServer: string | undefined,\n includeRaw = false,\n): DomainRecord {\n const map = parseKeyValueLines(whoisText);\n\n // Date extraction across common synonyms\n const creationDate = anyValue(map, [\n \"creation date\",\n \"created on\",\n \"created\",\n \"registered on\",\n \"registered\",\n \"registration date\",\n \"domain registration date\",\n \"domain create date\",\n \"domain name commencement date\",\n \"registration time\", // .cn\n \"domain record activated\", // .edu\n \"domain registered\",\n \"registered date\", // .co.jp\n \"assigned\", // .il\n ]);\n const updatedDate = anyValue(map, [\n \"updated date\",\n \"updated\",\n \"last updated\",\n \"last updated on\", // .mx\n \"last update\", // .co.jp\n \"last-update\", // .fr\n \"last modified\",\n \"modified\",\n \"changed\",\n \"modification date\",\n \"domain record last updated\", // .edu\n ]);\n const expirationDate = anyValue(map, [\n \"registry expiry date\",\n \"registry expiration date\",\n \"registrar registration expiration date\",\n \"registrar registration expiry date\",\n \"registrar expiration date\",\n \"registrar expiry date\",\n \"expiry date\",\n \"expiration date\",\n \"expiry\",\n \"expire date\", // .it\n \"expire\",\n \"expired\", // .ly\n \"expires on\",\n \"expires\",\n \"expiration time\", // .cn\n \"domain expires\", // .edu\n \"paid-till\",\n \"renewal date\", // .pl\n \"validity\", // .il\n \"record will expire on\",\n ]);\n\n // Registrar info (thin registries like .com/.net require referral follow for full data)\n const registrar: RegistrarInfo | undefined = (() => {\n const name = anyValue(map, [\n \"registrar\",\n \"registrar name\",\n \"registrar organization\",\n \"registrar organization name\", // .tr\n \"sponsoring registrar\",\n \"organisation\",\n \"record maintained by\",\n ]);\n const ianaId = anyValue(map, [\n \"registrar iana id\",\n \"sponsoring registrar iana id\",\n \"iana id\",\n ]);\n const url = anyValue(map, [\n \"registrar url\",\n \"registrar website\",\n \"registrar web\", // .it\n \"url of the registrar\",\n \"referrer\",\n ]);\n const abuseEmail = anyValue(map, [\n \"registrar abuse contact email\",\n \"abuse contact email\",\n ]);\n const abusePhone = anyValue(map, [\n \"registrar abuse contact phone\",\n \"abuse contact phone\",\n ]);\n if (!name && !ianaId && !url && !abuseEmail && !abusePhone)\n return undefined;\n return {\n name: name || undefined,\n ianaId: ianaId || undefined,\n url: url || undefined,\n email: abuseEmail || undefined,\n phone: abusePhone || undefined,\n };\n })();\n\n // Statuses: multiple entries are expected; keep raw\n const statusLines =\n map[\"domain status\"] ||\n map.status ||\n map.flags ||\n map.state || // .ru\n map[\"registration status\"] ||\n map.eppstatus || // .fr\n [];\n const statuses = statusLines.length\n ? statusLines\n .map((line) => {\n const status = line.split(/\\s+/)[0];\n return status ? { status, raw: line } : null;\n })\n .filter((s): s is { status: string; raw: string } => s !== null)\n : undefined;\n\n // Nameservers: also appear as \"nserver\" on some ccTLDs (.de, .ru) and as \"name server\"\n const nsLines: string[] = [\n ...(map[\"name server\"] || []),\n ...(map.nameserver || []),\n ...(map[\"name servers\"] || []),\n ...(map.nserver || []),\n ...(map[\"name server information\"] || []),\n ...(map.dns || []),\n ...(map.hostname || []),\n ...(map[\"domain nameservers\"] || []),\n ...(map[\"domain servers in listed order\"] || []), // .ly\n ...(map[\"domain servers\"] || []), // .tr\n ...(map[\"name servers dns\"] || []), // .mx\n ...(map[\"ns 1\"] || []),\n ...(map[\"ns 2\"] || []),\n ...(map[\"ns 3\"] || []),\n ...(map[\"ns 4\"] || []),\n ];\n const nameservers: Nameserver[] | undefined = nsLines.length\n ? (uniq(\n nsLines\n .map((line) => line.trim())\n .filter(Boolean)\n .map((line) => {\n // Common formats: \"ns1.example.com\" or \"ns1.example.com 192.0.2.1\" or \"ns1.example.com 2001:db8::1\"\n const parts = line.split(/\\s+/);\n const host = parts.shift()?.toLowerCase() || \"\";\n const ipv4: string[] = [];\n const ipv6: string[] = [];\n for (const p of parts) {\n if (/^\\d+\\.\\d+\\.\\d+\\.\\d+$/.test(p)) ipv4.push(p);\n else if (/^[0-9a-f:]+$/i.test(p)) ipv6.push(p);\n }\n if (!host) return undefined;\n const ns: Nameserver = { host };\n if (ipv4.length) ns.ipv4 = ipv4;\n if (ipv6.length) ns.ipv6 = ipv6;\n return ns;\n })\n .filter((x): x is Nameserver => !!x),\n ) as Nameserver[])\n : undefined;\n\n // Contacts: best-effort parse common keys\n const contacts = collectContacts(map);\n\n // Derive privacy flag from registrant name/org keywords\n const registrant = contacts?.find((c) => c.type === \"registrant\");\n const privacyEnabled = !!(\n registrant &&\n (\n [registrant.name, registrant.organization].filter(Boolean) as string[]\n ).some(isPrivacyName)\n );\n\n const dnssecRaw = (map.dnssec?.[0] || \"\").toLowerCase();\n const dnssec = dnssecRaw\n ? { enabled: /signed|yes|true/.test(dnssecRaw) }\n : undefined;\n\n // Simple lock derivation from statuses\n const transferLock = !!statuses?.some(\n (s) => s.status && /transferprohibited/i.test(s.status),\n );\n\n const record: DomainRecord = {\n domain,\n tld,\n isRegistered: !isAvailableByWhois(whoisText),\n isIDN: /(^|\\.)xn--/i.test(domain),\n unicodeName: undefined,\n punycodeName: undefined,\n registry: undefined,\n registrar,\n reseller: anyValue(map, [\"reseller\"]) || undefined,\n statuses,\n creationDate: toISO(creationDate || undefined),\n updatedDate: toISO(updatedDate || undefined),\n expirationDate: toISO(expirationDate || undefined),\n deletionDate: undefined,\n transferLock,\n dnssec,\n nameservers,\n contacts,\n privacyEnabled: privacyEnabled ? true : undefined,\n whoisServer,\n rdapServers: undefined,\n rawRdap: undefined,\n rawWhois: includeRaw ? whoisText : undefined,\n source: \"whois\",\n warnings: undefined,\n };\n\n return record;\n}\n\nfunction anyValue(\n map: Record<string, string[]>,\n keys: string[],\n): string | undefined {\n for (const k of keys) {\n const v = map[k];\n if (v?.length) return v[0];\n }\n return undefined;\n}\n\nfunction collectContacts(map: Record<string, string[]>): Contact[] | undefined {\n const roles: Array<{\n role: Contact[\"type\"];\n prefixes: string[];\n }> = [\n {\n role: \"registrant\",\n prefixes: [\"registrant\", \"owner\", \"holder\"],\n },\n {\n role: \"admin\",\n prefixes: [\"admin\", \"administrative\"],\n },\n {\n role: \"tech\",\n prefixes: [\"tech\", \"technical\"],\n },\n {\n role: \"billing\",\n prefixes: [\"billing\"],\n },\n {\n role: \"abuse\",\n prefixes: [\"abuse\"],\n },\n ];\n const contacts: Contact[] = [];\n for (const r of roles) {\n const nameKeys: string[] = [];\n const orgKeys: string[] = [];\n const emailKeys: string[] = [];\n const phoneKeys: string[] = [];\n const faxKeys: string[] = [];\n const streetKeys: string[] = [];\n const cityKeys: string[] = [];\n const stateKeys: string[] = [];\n const postalCodeKeys: string[] = [];\n const countryKeys: string[] = [];\n\n for (const prefix of r.prefixes) {\n nameKeys.push(`${prefix} name`, `${prefix} contact name`, `${prefix}`);\n if (prefix === \"registrant\") {\n nameKeys.push(\"registrant person\"); // .ua\n }\n if (prefix === \"owner\") {\n nameKeys.push(\"owner name\"); // .tm\n }\n\n orgKeys.push(\n `${prefix} organization`,\n `${prefix} organisation`,\n `${prefix} org`,\n );\n if (prefix === \"registrant\") {\n orgKeys.push(\"trading as\"); // .uk, .co.uk\n orgKeys.push(\"org\"); // .ru\n }\n if (prefix === \"owner\") {\n orgKeys.push(\"owner orgname\"); // .tm\n }\n\n emailKeys.push(\n `${prefix} email`,\n `${prefix} contact email`,\n `${prefix} e-mail`,\n );\n\n phoneKeys.push(\n `${prefix} phone`,\n `${prefix} contact phone`,\n `${prefix} telephone`,\n );\n\n faxKeys.push(`${prefix} fax`, `${prefix} facsimile`);\n\n streetKeys.push(\n `${prefix} street`,\n `${prefix} address`,\n `${prefix}'s address`,\n );\n if (prefix === \"owner\") {\n streetKeys.push(\"owner addr\"); // .tm\n }\n\n cityKeys.push(`${prefix} city`);\n\n stateKeys.push(\n `${prefix} state`,\n `${prefix} province`,\n `${prefix} state/province`,\n );\n\n postalCodeKeys.push(\n `${prefix} postal code`,\n `${prefix} postcode`,\n `${prefix} zip`,\n );\n\n countryKeys.push(`${prefix} country`);\n }\n\n const name = anyValue(map, nameKeys);\n const org = anyValue(map, orgKeys);\n const email = anyValue(map, emailKeys);\n const phone = anyValue(map, phoneKeys);\n const fax = anyValue(map, faxKeys);\n const street = multi(map, streetKeys);\n const city = anyValue(map, cityKeys);\n const state = anyValue(map, stateKeys);\n const postalCode = anyValue(map, postalCodeKeys);\n const country = anyValue(map, countryKeys);\n\n if (name || org || email || phone || street?.length) {\n contacts.push({\n type: r.role,\n name: name || undefined,\n organization: org || undefined,\n email: email || undefined,\n phone: phone || undefined,\n fax: fax || undefined,\n street: street,\n city: city || undefined,\n state: state || undefined,\n postalCode: postalCode || undefined,\n country: country || undefined,\n });\n }\n }\n return contacts.length ? contacts : undefined;\n}\n\nfunction multi(\n map: Record<string, string[]>,\n keys: string[],\n): string[] | undefined {\n for (const k of keys) {\n const v = map[k];\n if (v?.length) return v;\n }\n return undefined;\n}\n","import type { LookupOptions } from \"../types\";\nimport type { WhoisQueryResult } from \"./client\";\nimport { whoisQuery } from \"./client\";\nimport { extractWhoisReferral } from \"./discovery\";\nimport { isAvailableByWhois } from \"./normalize\";\n\n/**\n * Follow registrar WHOIS referrals up to a configured hop limit.\n * Returns the last successful WHOIS response (best-effort; keeps original on failures).\n */\nexport async function followWhoisReferrals(\n initialServer: string,\n domain: string,\n opts?: LookupOptions,\n): Promise<WhoisQueryResult> {\n const maxHops = Math.max(0, opts?.maxWhoisReferralHops ?? 2);\n // First query against the provided server\n let current = await whoisQuery(initialServer, domain, opts);\n if (opts?.followWhoisReferral === false || maxHops === 0) return current;\n\n const visited = new Set<string>([normalize(current.serverQueried)]);\n let hops = 0;\n // Iterate while we see a new referral and are under hop limit\n while (hops < maxHops) {\n const next = extractWhoisReferral(current.text);\n if (!next) break;\n const normalized = normalize(next);\n if (visited.has(normalized)) break; // cycle protection / same as current\n visited.add(normalized);\n try {\n const res = await whoisQuery(next, domain, opts);\n // Prefer authoritative TLD response when registrar contradicts availability\n const registeredBefore = !isAvailableByWhois(current.text);\n const registeredAfter = !isAvailableByWhois(res.text);\n if (registeredBefore && !registeredAfter) {\n // Registrar claims availability but TLD shows registered: keep TLD\n break;\n }\n current = res; // adopt registrar when it does not downgrade registration\n } catch {\n // If referral server fails, stop following and keep the last good response\n break;\n }\n hops += 1;\n }\n return current;\n}\n\n/**\n * Collect the WHOIS referral chain starting from the TLD server.\n * Always includes the initial TLD response; may include one or more registrar responses.\n * Stops on contradiction (registrar claims availability) or failures.\n */\nexport async function collectWhoisReferralChain(\n initialServer: string,\n domain: string,\n opts?: LookupOptions,\n): Promise<WhoisQueryResult[]> {\n const results: WhoisQueryResult[] = [];\n const maxHops = Math.max(0, opts?.maxWhoisReferralHops ?? 2);\n const first = await whoisQuery(initialServer, domain, opts);\n results.push(first);\n if (opts?.followWhoisReferral === false || maxHops === 0) return results;\n\n const visited = new Set<string>([normalize(first.serverQueried)]);\n let current = first;\n let hops = 0;\n while (hops < maxHops) {\n const next = extractWhoisReferral(current.text);\n if (!next) break;\n const normalized = normalize(next);\n if (visited.has(normalized)) break;\n visited.add(normalized);\n try {\n const res = await whoisQuery(next, domain, opts);\n // If registrar claims availability while TLD indicated registered, stop.\n const registeredBefore = !isAvailableByWhois(current.text);\n const registeredAfter = !isAvailableByWhois(res.text);\n if (registeredBefore && !registeredAfter) {\n // Do not adopt or append contradictory registrar; keep authoritative TLD only.\n break;\n }\n results.push(res);\n current = res;\n } catch {\n break;\n }\n hops += 1;\n }\n return results;\n}\n\nfunction normalize(server: string): string {\n return server.replace(/^whois:\\/\\//i, \"\").toLowerCase();\n}\n","import { getDomainParts, isLikelyDomain } from \"./lib/domain\";\nimport { getRdapBaseUrlsForTld } from \"./rdap/bootstrap\";\nimport { fetchRdapDomain } from \"./rdap/client\";\nimport { fetchAndMergeRdapRelated } from \"./rdap/merge\";\nimport { normalizeRdap } from \"./rdap/normalize\";\nimport type { DomainRecord, LookupOptions, LookupResult } from \"./types\";\nimport {\n getIanaWhoisTextForTld,\n ianaWhoisServerForTld,\n parseIanaRegistrationInfoUrl,\n} from \"./whois/discovery\";\nimport { mergeWhoisRecords } from \"./whois/merge\";\nimport { normalizeWhois } from \"./whois/normalize\";\nimport {\n collectWhoisReferralChain,\n followWhoisReferrals,\n} from \"./whois/referral\";\n\n/**\n * High-level lookup that prefers RDAP and falls back to WHOIS.\n * Ensures a standardized DomainRecord, independent of the source.\n */\nexport async function lookup(\n domain: string,\n opts?: LookupOptions,\n): Promise<LookupResult> {\n try {\n if (!isLikelyDomain(domain)) {\n return { ok: false, error: \"Input does not look like a domain\" };\n }\n\n const { publicSuffix: tld } = getDomainParts(domain);\n if (!tld) {\n return { ok: false, error: \"Invalid TLD\" };\n }\n\n // If WHOIS-only, skip RDAP path\n if (!opts?.whoisOnly) {\n let bases = await getRdapBaseUrlsForTld(tld, opts);\n // Some ccTLD registries publish RDAP only at the registry TLD (e.g., br),\n // while the public suffix can be multi-label (e.g., com.br). Fallback to last label.\n if (bases.length === 0 && tld.includes(\".\")) {\n const registryTld = tld.split(\".\").pop() ?? tld;\n bases = await getRdapBaseUrlsForTld(registryTld, opts);\n }\n const tried: string[] = [];\n for (const base of bases) {\n tried.push(base);\n try {\n const { json } = await fetchRdapDomain(domain, base, opts);\n const rdapEnriched = await fetchAndMergeRdapRelated(\n domain,\n json,\n opts,\n );\n const record: DomainRecord = normalizeRdap(\n domain,\n tld,\n rdapEnriched.merged,\n [...tried, ...rdapEnriched.serversTried],\n !!opts?.includeRaw,\n );\n return { ok: true, record };\n } catch {\n // try next base\n }\n }\n // Some TLDs are not in bootstrap yet; continue to WHOIS fallback unless rdapOnly\n if (opts?.rdapOnly) {\n return {\n ok: false,\n error: `RDAP not available or failed for TLD '${tld}'. Many TLDs do not publish RDAP; try WHOIS fallback (omit rdapOnly).`,\n };\n }\n }\n\n // WHOIS fallback path\n const whoisServer = await ianaWhoisServerForTld(tld, opts);\n if (!whoisServer) {\n // Provide a clearer, actionable message\n const ianaText = await getIanaWhoisTextForTld(tld, opts);\n const regUrl = ianaText\n ? parseIanaRegistrationInfoUrl(ianaText)\n : undefined;\n const hint = regUrl ? ` See registration info at ${regUrl}.` : \"\";\n return {\n ok: false,\n error: `No WHOIS server discovered for TLD '${tld}'. This registry may not publish public WHOIS over port 43.${hint}`,\n };\n }\n\n // Query the TLD server first; optionally follow registrar referrals (multi-hop)\n // Collect the chain and coalesce so we don't lose details when a registrar returns minimal/empty data.\n const chain = await collectWhoisReferralChain(whoisServer, domain, opts);\n if (chain.length === 0) {\n // Fallback to previous behavior as a safety net\n const res = await followWhoisReferrals(whoisServer, domain, opts);\n const record: DomainRecord = normalizeWhois(\n domain,\n tld,\n res.text,\n res.serverQueried,\n !!opts?.includeRaw,\n );\n return { ok: true, record };\n }\n\n // Normalize all WHOIS texts in the chain and merge conservatively\n const normalizedRecords = chain.map((r) =>\n normalizeWhois(domain, tld, r.text, r.serverQueried, !!opts?.includeRaw),\n );\n const [first, ...rest] = normalizedRecords;\n if (!first) {\n return { ok: false, error: \"No WHOIS data retrieved\" };\n }\n const mergedRecord = rest.length ? mergeWhoisRecords(first, rest) : first;\n return { ok: true, record: mergedRecord };\n } catch (err: unknown) {\n const message = err instanceof Error ? err.message : String(err);\n return { ok: false, error: message };\n }\n}\n\n/**\n * Determine if a domain appears available (not registered).\n * Performs a lookup and resolves to a boolean. Rejects on lookup error.\n */\nexport async function isAvailable(\n domain: string,\n opts?: LookupOptions,\n): Promise<boolean> {\n const res = await lookup(domain, opts);\n if (!res.ok || !res.record) throw new Error(res.error || \"Lookup failed\");\n return res.record.isRegistered === false;\n}\n\n/**\n * Determine if a domain appears registered.\n * Performs a lookup and resolves to a boolean. Rejects on lookup error.\n */\nexport async function isRegistered(\n domain: string,\n opts?: LookupOptions,\n): Promise<boolean> {\n const res = await lookup(domain, opts);\n if (!res.ok || !res.record) throw new Error(res.error || \"Lookup failed\");\n return res.record.isRegistered === true;\n}\n\n/**\n * @deprecated Use `lookup` instead.\n */\nexport const lookupDomain = lookup;\n\nexport {\n getDomainParts,\n getDomainTld,\n isLikelyDomain,\n toRegistrableDomain,\n} from \"./lib/domain\";\nexport type * from \"./types\";\n"],"mappings":"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"}