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 +2 -1
- package/dist/index.d.ts +39 -0
- package/dist/index.js +22 -0
- package/package.json +1 -1
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,
|