rdapper 0.3.0 → 0.4.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.
package/README.md CHANGED
@@ -4,7 +4,7 @@ RDAP‑first domain registration lookups with WHOIS fallback. Produces a single,
4
4
 
5
5
  - RDAP discovery via IANA bootstrap (`https://data.iana.org/rdap/dns.json`)
6
6
  - WHOIS TCP 43 client with TLD discovery, registrar referral follow, and curated exceptions
7
- - Normalized output: registrar, contacts, nameservers, statuses, dates, DNSSEC, source metadata
7
+ - Normalized output: registrar, contacts, nameservers, statuses, dates, DNSSEC, privacy flag, source metadata
8
8
  - TypeScript types included; ESM‑only; no external HTTP client (uses global `fetch`)
9
9
 
10
10
  ### [🦉 See it in action on hoot.sh!](https://hoot.sh)
@@ -117,6 +117,7 @@ interface DomainRecord {
117
117
  country?: string;
118
118
  countryCode?: string;
119
119
  }>;
120
+ privacyEnabled?: boolean; // registrant appears privacy-redacted based on keyword heuristics
120
121
  whoisServer?: string; // authoritative WHOIS queried (if any)
121
122
  rdapServers?: string[]; // RDAP base URLs tried
122
123
  rawRdap?: unknown; // raw RDAP JSON (only when options.includeRaw)
package/dist/index.d.ts CHANGED
@@ -32,21 +32,37 @@ interface StatusEvent {
32
32
  raw?: string;
33
33
  }
34
34
  interface DomainRecord {
35
+ /** Normalized domain name */
35
36
  domain: string;
37
+ /** Terminal TLD */
36
38
  tld: string;
39
+ /** Whether the domain is registered */
37
40
  isRegistered: boolean;
41
+ /** Whether the domain is internationalized (IDN) */
38
42
  isIDN?: boolean;
43
+ /** Unicode name */
39
44
  unicodeName?: string;
45
+ /** Punycode name */
40
46
  punycodeName?: string;
47
+ /** Registry operator */
41
48
  registry?: string;
49
+ /** Registrar */
42
50
  registrar?: RegistrarInfo;
51
+ /** Reseller (if applicable) */
43
52
  reseller?: string;
53
+ /** EPP status codes */
44
54
  statuses?: StatusEvent[];
55
+ /** Creation date in ISO 8601 */
45
56
  creationDate?: string;
57
+ /** Updated date in ISO 8601 */
46
58
  updatedDate?: string;
59
+ /** Expiration date in ISO 8601 */
47
60
  expirationDate?: string;
61
+ /** Deletion date in ISO 8601 */
48
62
  deletionDate?: string;
63
+ /** Transfer lock */
49
64
  transferLock?: boolean;
65
+ /** DNSSEC data (if available) */
50
66
  dnssec?: {
51
67
  enabled: boolean;
52
68
  dsRecords?: Array<{
@@ -56,28 +72,51 @@ interface DomainRecord {
56
72
  digest?: string;
57
73
  }>;
58
74
  };
75
+ /** Nameservers */
59
76
  nameservers?: Nameserver[];
77
+ /** Contacts (registrant, admin, tech, billing, abuse, etc.) */
60
78
  contacts?: Contact[];
79
+ /** Best guess as to whether registrant is redacted based on keywords */
80
+ privacyEnabled?: boolean;
81
+ /** Authoritative WHOIS queried (if any) */
61
82
  whoisServer?: string;
83
+ /** RDAP base URLs tried */
62
84
  rdapServers?: string[];
85
+ /** Raw RDAP JSON */
63
86
  rawRdap?: unknown;
87
+ /** Raw WHOIS text (last authoritative) */
64
88
  rawWhois?: string;
89
+ /** Which source produced data */
65
90
  source: LookupSource;
91
+ /** ISO 8601 timestamp at time of lookup */
66
92
  fetchedAt: string;
93
+ /** Warnings generated during lookup */
67
94
  warnings?: string[];
68
95
  }
69
96
  interface LookupOptions {
97
+ /** Total timeout budget */
70
98
  timeoutMs?: number;
99
+ /** Don't fall back to WHOIS */
71
100
  rdapOnly?: boolean;
101
+ /** Don't attempt RDAP */
72
102
  whoisOnly?: boolean;
103
+ /** Follow referral server (default true) */
73
104
  followWhoisReferral?: boolean;
105
+ /** Maximum registrar WHOIS referral hops (default 2) */
74
106
  maxWhoisReferralHops?: number;
107
+ /** Follow RDAP related/entity links (default true) */
75
108
  rdapFollowLinks?: boolean;
109
+ /** Maximum RDAP related link fetches (default 2) */
76
110
  maxRdapLinkHops?: number;
111
+ /** RDAP link rels to consider (default ["related","entity","registrar","alternate"]) */
77
112
  rdapLinkRels?: string[];
113
+ /** Override IANA bootstrap */
78
114
  customBootstrapUrl?: string;
115
+ /** Override/add authoritative WHOIS per TLD */
79
116
  whoisHints?: Record<string, string>;
117
+ /** Include rawRdap/rawWhois in results (default false) */
80
118
  includeRaw?: boolean;
119
+ /** Optional cancellation signal */
81
120
  signal?: AbortSignal;
82
121
  }
83
122
  interface LookupResult {
package/dist/index.js CHANGED
@@ -302,6 +302,22 @@ function sameDomain(a, b) {
302
302
  return a.toLowerCase() === b.toLowerCase();
303
303
  }
304
304
 
305
+ //#endregion
306
+ //#region src/lib/privacy.ts
307
+ const PRIVACY_NAME_KEYWORDS = [
308
+ "redacted",
309
+ "privacy",
310
+ "private",
311
+ "withheld",
312
+ "not disclosed",
313
+ "protected",
314
+ "protection"
315
+ ];
316
+ function isPrivacyName(value) {
317
+ const v = value.toLowerCase();
318
+ return PRIVACY_NAME_KEYWORDS.some((k) => v.includes(k));
319
+ }
320
+
305
321
  //#endregion
306
322
  //#region src/lib/text.ts
307
323
  function uniq(arr) {
@@ -382,6 +398,8 @@ function normalizeRdap(inputDomain, tld, rdap, rdapServersTried, fetchedAtISO, i
382
398
  return n;
383
399
  }).filter((n) => !!n.host) : void 0;
384
400
  const contacts = extractContacts(doc.entities);
401
+ const registrant = contacts?.find((c) => c.type === "registrant");
402
+ const privacyEnabled = !!(registrant && [registrant.name, registrant.organization].filter(Boolean).some(isPrivacyName));
385
403
  const statuses = Array.isArray(doc.status) ? doc.status.filter((s) => typeof s === "string").map((s) => ({
386
404
  status: s,
387
405
  raw: s
@@ -426,6 +444,7 @@ function normalizeRdap(inputDomain, tld, rdap, rdapServersTried, fetchedAtISO, i
426
444
  host: n.host.toLowerCase()
427
445
  }))) : void 0,
428
446
  contacts,
447
+ privacyEnabled: privacyEnabled ? true : void 0,
429
448
  whoisServer,
430
449
  rdapServers: rdapServersTried,
431
450
  rawRdap: includeRaw ? rdap : void 0,
@@ -799,6 +818,8 @@ function normalizeWhois(domain, tld, whoisText, whoisServer, fetchedAtISO, inclu
799
818
  return ns;
800
819
  }).filter((x) => !!x)) : void 0;
801
820
  const contacts = collectContacts(map);
821
+ const registrant = contacts?.find((c) => c.type === "registrant");
822
+ const privacyEnabled = !!(registrant && [registrant.name, registrant.organization].filter(Boolean).some(isPrivacyName));
802
823
  const dnssecRaw = (map.dnssec?.[0] || "").toLowerCase();
803
824
  const dnssec = dnssecRaw ? { enabled: /signed|yes|true/.test(dnssecRaw) } : void 0;
804
825
  const transferLock = !!statuses?.some((s) => /transferprohibited/i.test(s.status));
@@ -821,6 +842,7 @@ function normalizeWhois(domain, tld, whoisText, whoisServer, fetchedAtISO, inclu
821
842
  dnssec,
822
843
  nameservers,
823
844
  contacts,
845
+ privacyEnabled: privacyEnabled ? true : void 0,
824
846
  whoisServer,
825
847
  rdapServers: void 0,
826
848
  rawRdap: void 0,
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "rdapper",
3
- "version": "0.3.0",
3
+ "version": "0.4.0",
4
4
  "license": "MIT",
5
5
  "description": "🎩 RDAP/WHOIS fetcher, parser, and normalizer for Node",
6
6
  "repository": {