@cleandns/whois-rdap 1.0.52 → 1.0.55
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/.github/workflows/test.yml +38 -0
- package/dist/index.d.ts +3 -1
- package/dist/index.js +69 -88
- package/dist/index.test.d.ts +1 -0
- package/dist/index.test.js +360 -0
- package/dist/ip.js +8 -1
- package/dist/polyfills.d.ts +11 -0
- package/dist/polyfills.js +64 -0
- package/dist/port43.js +28 -14
- package/dist/utils/escapeRegex.d.ts +5 -0
- package/dist/utils/escapeRegex.js +7 -0
- package/dist/utils/findInObject.js +14 -4
- package/dist/utils/findNameservers.d.ts +1 -0
- package/dist/utils/findNameservers.js +15 -0
- package/dist/utils/findStatus.d.ts +1 -0
- package/dist/utils/findStatus.js +10 -0
- package/dist/utils/findTimestamps.d.ts +6 -0
- package/dist/utils/findTimestamps.js +39 -0
- package/dist/utils/toArray.d.ts +5 -0
- package/dist/utils/toArray.js +17 -0
- package/dist/utils/validateDomain.d.ts +5 -0
- package/dist/utils/validateDomain.js +18 -0
- package/github-workflow.patch +1044 -0
- package/package.json +24 -17
- package/src/index.test.ts +440 -0
- package/src/index.ts +65 -89
- package/src/ip.ts +9 -1
- package/src/polyfills.ts +76 -0
- package/src/port43.ts +29 -21
- package/src/utils/escapeRegex.ts +7 -0
- package/src/utils/findInObject.ts +19 -4
- package/src/utils/findNameservers.ts +15 -0
- package/src/utils/findStatus.ts +12 -0
- package/src/utils/findTimestamps.ts +44 -0
- package/src/utils/toArray.ts +17 -0
- package/src/utils/validateDomain.ts +18 -0
- package/tsconfig.json +1 -1
- package/dist/wwwservers.d.ts +0 -1
- package/dist/wwwservers.js +0 -3
- package/pnpm-workspace.yaml +0 -2
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
name: Test
|
|
2
|
+
|
|
3
|
+
on:
|
|
4
|
+
push:
|
|
5
|
+
branches: [main, master]
|
|
6
|
+
pull_request:
|
|
7
|
+
branches: [main, master]
|
|
8
|
+
|
|
9
|
+
jobs:
|
|
10
|
+
test:
|
|
11
|
+
runs-on: ubuntu-latest
|
|
12
|
+
|
|
13
|
+
strategy:
|
|
14
|
+
matrix:
|
|
15
|
+
node-version: [18, 20, 22]
|
|
16
|
+
|
|
17
|
+
steps:
|
|
18
|
+
- uses: actions/checkout@v4
|
|
19
|
+
|
|
20
|
+
- name: Setup pnpm
|
|
21
|
+
uses: pnpm/action-setup@v4
|
|
22
|
+
with:
|
|
23
|
+
version: 9
|
|
24
|
+
|
|
25
|
+
- name: Setup Node.js ${{ matrix.node-version }}
|
|
26
|
+
uses: actions/setup-node@v4
|
|
27
|
+
with:
|
|
28
|
+
node-version: ${{ matrix.node-version }}
|
|
29
|
+
cache: pnpm
|
|
30
|
+
|
|
31
|
+
- name: Install dependencies
|
|
32
|
+
run: pnpm install
|
|
33
|
+
|
|
34
|
+
- name: Build
|
|
35
|
+
run: pnpm run build
|
|
36
|
+
|
|
37
|
+
- name: Run tests
|
|
38
|
+
run: pnpm test
|
package/dist/index.d.ts
CHANGED
|
@@ -1,2 +1,4 @@
|
|
|
1
|
-
import
|
|
1
|
+
import "./polyfills.js";
|
|
2
|
+
import { WhoisOptions, WhoisResponse, WhoisTimestampFields } from "../whois.js";
|
|
3
|
+
export declare const eventMap: Map<string, WhoisTimestampFields>;
|
|
2
4
|
export declare function whois(origDomain: string, options?: WhoisOptions): Promise<WhoisResponse>;
|
package/dist/index.js
CHANGED
|
@@ -1,12 +1,21 @@
|
|
|
1
|
+
// Polyfills for Node.js 18 compatibility
|
|
2
|
+
import "./polyfills.js";
|
|
1
3
|
import { parseIpResponse } from "./ip.js";
|
|
2
4
|
import { determinePort43Domain, port43 } from "./port43.js";
|
|
3
5
|
import { findInObject } from "./utils/findInObject.js";
|
|
4
6
|
import { fixArrays } from "./utils/fixArrays.js";
|
|
5
7
|
import { ianaIdToRegistrar } from "./utils/ianaIdToRegistrar.js";
|
|
6
8
|
import { tldToRdap } from "./utils/tldToRdap.js";
|
|
7
|
-
import { normalizeWhoisStatus } from "./whoisStatus.js";
|
|
8
9
|
import { resolve4 } from "dns/promises";
|
|
9
|
-
|
|
10
|
+
import { toArray } from "./utils/toArray.js";
|
|
11
|
+
import { validateDomain } from "./utils/validateDomain.js";
|
|
12
|
+
import { findStatus } from "./utils/findStatus.js";
|
|
13
|
+
import { findNameservers } from "./utils/findNameservers.js";
|
|
14
|
+
import { findTimestamps } from "./utils/findTimestamps.js";
|
|
15
|
+
import createDebug from "debug";
|
|
16
|
+
// Debug logger - enable with DEBUG=whois:* environment variable
|
|
17
|
+
const debug = createDebug("whois:rdap");
|
|
18
|
+
export const eventMap = new Map([
|
|
10
19
|
["registration", "created"],
|
|
11
20
|
["last changed", "updated"],
|
|
12
21
|
["expiration", "expires"],
|
|
@@ -15,9 +24,26 @@ const eventMap = new Map([
|
|
|
15
24
|
export async function whois(origDomain, options = { fetch: fetch, thinOnly: false }) {
|
|
16
25
|
var _a, _b, _c, _d, _e, _f;
|
|
17
26
|
const _fetch = options.fetch || fetch;
|
|
18
|
-
|
|
27
|
+
// Validate and sanitize input
|
|
28
|
+
let domain;
|
|
29
|
+
try {
|
|
30
|
+
domain = validateDomain(origDomain);
|
|
31
|
+
}
|
|
32
|
+
catch (e) {
|
|
33
|
+
return {
|
|
34
|
+
found: false,
|
|
35
|
+
statusCode: 400,
|
|
36
|
+
error: e.message,
|
|
37
|
+
registrar: { id: 0, name: null },
|
|
38
|
+
reseller: null,
|
|
39
|
+
status: [],
|
|
40
|
+
statusDelta: [],
|
|
41
|
+
nameservers: [],
|
|
42
|
+
ts: { created: null, updated: null, expires: null },
|
|
43
|
+
};
|
|
44
|
+
}
|
|
19
45
|
let url = null;
|
|
20
|
-
[domain, url] = await tldToRdap(
|
|
46
|
+
[domain, url] = await tldToRdap(domain);
|
|
21
47
|
const response = {
|
|
22
48
|
found: false,
|
|
23
49
|
statusCode: 0,
|
|
@@ -48,7 +74,6 @@ export async function whois(origDomain, options = { fetch: fetch, thinOnly: fals
|
|
|
48
74
|
thinResponse = await _fetch(thinRdap)
|
|
49
75
|
.then((r) => {
|
|
50
76
|
response.statusCode = r.status;
|
|
51
|
-
// console.log({ ok: r.ok, status: r.status, statusText: r.statusText });
|
|
52
77
|
if (r.status >= 200 && r.status < 400) {
|
|
53
78
|
return r.json();
|
|
54
79
|
}
|
|
@@ -56,7 +81,7 @@ export async function whois(origDomain, options = { fetch: fetch, thinOnly: fals
|
|
|
56
81
|
return null;
|
|
57
82
|
})
|
|
58
83
|
.catch((error) => {
|
|
59
|
-
|
|
84
|
+
debug("thin RDAP lookup failure for %s: %s", domain, error.message);
|
|
60
85
|
return null;
|
|
61
86
|
});
|
|
62
87
|
if (thinResponse && !thinResponse.errorCode) {
|
|
@@ -73,7 +98,7 @@ export async function whois(origDomain, options = { fetch: fetch, thinOnly: fals
|
|
|
73
98
|
link.type === "application/rdap+json")) === null || _d === void 0 ? void 0 : _d.href.replace("/domain/domain/", "/domain/")) || `${options.server}/domain/${domain}`;
|
|
74
99
|
let thickResponse = null;
|
|
75
100
|
if (!options.thinOnly && thickRdap) {
|
|
76
|
-
|
|
101
|
+
debug("fetching thick RDAP: %s", thickRdap);
|
|
77
102
|
thickResponse = await _fetch(thickRdap)
|
|
78
103
|
.then((r) => r.json())
|
|
79
104
|
.catch(() => null);
|
|
@@ -81,7 +106,7 @@ export async function whois(origDomain, options = { fetch: fetch, thinOnly: fals
|
|
|
81
106
|
}
|
|
82
107
|
else {
|
|
83
108
|
thickResponse = null;
|
|
84
|
-
|
|
109
|
+
debug("thick RDAP failed for %s", domain);
|
|
85
110
|
}
|
|
86
111
|
}
|
|
87
112
|
if ((_e = thickResponse === null || thickResponse === void 0 ? void 0 : thickResponse.rdapConformance) === null || _e === void 0 ? void 0 : _e["0"]) {
|
|
@@ -90,11 +115,14 @@ export async function whois(origDomain, options = { fetch: fetch, thinOnly: fals
|
|
|
90
115
|
const registrars = [];
|
|
91
116
|
const resellers = [];
|
|
92
117
|
async function extractRegistrarsAndResellers(response, url, isThick) {
|
|
93
|
-
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z;
|
|
94
|
-
|
|
95
|
-
|
|
118
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _j, _k, _l, _m, _o, _p, _q, _r, _s, _t, _u, _v, _w, _x, _y, _z, _0, _1, _2;
|
|
119
|
+
// Use toArray to safely handle entities that might not be iterable
|
|
120
|
+
const entities = toArray(response.entities);
|
|
121
|
+
const entityList = [
|
|
122
|
+
...entities,
|
|
96
123
|
response.entity ? { events: response.events, ...response.entity } : null,
|
|
97
|
-
].filter(Boolean)
|
|
124
|
+
].filter(Boolean);
|
|
125
|
+
for (const ent of entityList) {
|
|
98
126
|
if (((_a = ent.roles) === null || _a === void 0 ? void 0 : _a.includes("registrar")) || ent.role === "registrar") {
|
|
99
127
|
const pubIds = [];
|
|
100
128
|
if (ent.publicIds) {
|
|
@@ -116,53 +144,59 @@ export async function whois(origDomain, options = { fetch: fetch, thinOnly: fals
|
|
|
116
144
|
|| ((_h = pubIds.find((id) => id.type === "Registry Identifier")) === null || _h === void 0 ? void 0 : _h.identifier)
|
|
117
145
|
|| pubIds.find((id) => id.type === "IANA Registrar ID");
|
|
118
146
|
if (reg) {
|
|
119
|
-
// console.log(ent.vcardArray);
|
|
120
147
|
const id = typeof reg === 'object' ? 0 : reg;
|
|
121
148
|
const name = (parseInt(id) == id
|
|
122
149
|
&& ((_j = (await ianaIdToRegistrar(parseInt(id)))) === null || _j === void 0 ? void 0 : _j.name))
|
|
123
150
|
|| findInObject(ent.vcardArray, (el) => Array.isArray(el) && (el[0] === "fn" || el[0] === "org"), (el) => el[3], reg);
|
|
124
|
-
|
|
151
|
+
// Safely handle ent.entities
|
|
152
|
+
const entEntities = toArray(ent.entities);
|
|
153
|
+
const email = ((_k = [ent, ...entEntities]
|
|
125
154
|
.filter((e) => e === null || e === void 0 ? void 0 : e.vcardArray)
|
|
126
155
|
.map((e) => findInObject(e.vcardArray, (el) => Array.isArray(el) && el[0] === "email", (el) => el[3], ""))
|
|
127
156
|
.filter(Boolean)) === null || _k === void 0 ? void 0 : _k[0]) || "";
|
|
128
|
-
const abuseEmail = ((_l = [ent, ...
|
|
157
|
+
const abuseEmail = ((_l = [ent, ...entEntities]
|
|
129
158
|
.filter((e) => e === null || e === void 0 ? void 0 : e.vcardArray)
|
|
130
159
|
.map((e) => findInObject(e.vcardArray, (el) => { var _a; return Array.isArray(el) && ((_a = e.roles) === null || _a === void 0 ? void 0 : _a.includes("abuse")) && el[0] === "email"; }, (el) => el[3], ""))
|
|
131
160
|
.filter(Boolean)) === null || _l === void 0 ? void 0 : _l[0]) || "";
|
|
132
161
|
const events = ent.events || response.events || ent.enents || response.enents;
|
|
133
162
|
registrars.push({ id, name, email, abuseEmail, events });
|
|
134
163
|
}
|
|
135
|
-
// handles .ca
|
|
164
|
+
// handles .ca - with safe optional chaining
|
|
136
165
|
else if (((_p = (_o = (_m = ent.vcardArray) === null || _m === void 0 ? void 0 : _m[1]) === null || _o === void 0 ? void 0 : _o[3]) === null || _p === void 0 ? void 0 : _p[3]) === 'registrar') {
|
|
137
|
-
const
|
|
166
|
+
const entEntities = toArray(ent.entities);
|
|
167
|
+
const email = ((_q = [ent, ...entEntities]
|
|
138
168
|
.filter((e) => e === null || e === void 0 ? void 0 : e.vcardArray)
|
|
139
169
|
.map((e) => findInObject(e.vcardArray, (el) => Array.isArray(el) && el[0] === "email", (el) => el[3], ""))
|
|
140
170
|
.filter(Boolean)) === null || _q === void 0 ? void 0 : _q[0]) || "";
|
|
141
|
-
const abuseEmail = ((_r = [ent, ...
|
|
171
|
+
const abuseEmail = ((_r = [ent, ...entEntities]
|
|
142
172
|
.filter((e) => e === null || e === void 0 ? void 0 : e.vcardArray)
|
|
143
173
|
.map((e) => findInObject(e.vcardArray, (el) => { var _a; return Array.isArray(el) && ((_a = e.roles) === null || _a === void 0 ? void 0 : _a.includes("abuse")) && el[0] === "email"; }, (el) => el[3], ""))
|
|
144
174
|
.filter(Boolean)) === null || _r === void 0 ? void 0 : _r[0]) || "";
|
|
145
|
-
|
|
175
|
+
const vcardName = ((_u = (_t = (_s = ent.vcardArray) === null || _s === void 0 ? void 0 : _s[1]) === null || _t === void 0 ? void 0 : _t[1]) === null || _u === void 0 ? void 0 : _u[3]) || '';
|
|
176
|
+
registrars.push({ id: 0, name: vcardName, email, abuseEmail, events: ent.events || response.events || ent.enents || response.enents });
|
|
146
177
|
}
|
|
147
|
-
// handles .si
|
|
148
|
-
else if (ent.vcardArray && ent.vcardArray[1] && ent.vcardArray[1].find((el) => el[0] === 'fn')) {
|
|
149
|
-
const
|
|
178
|
+
// handles .si - with safe array access
|
|
179
|
+
else if (ent.vcardArray && Array.isArray(ent.vcardArray[1]) && ent.vcardArray[1].find((el) => el[0] === 'fn')) {
|
|
180
|
+
const entEntities = toArray(ent.entities);
|
|
181
|
+
const email = ((_v = [ent, ...entEntities]
|
|
150
182
|
.filter((e) => e === null || e === void 0 ? void 0 : e.vcardArray)
|
|
151
183
|
.map((e) => findInObject(e.vcardArray, (el) => Array.isArray(el) && el[0] === "email", (el) => el[3], ""))
|
|
152
|
-
.filter(Boolean)) === null ||
|
|
153
|
-
const abuseEmail = ((
|
|
184
|
+
.filter(Boolean)) === null || _v === void 0 ? void 0 : _v[0]) || "";
|
|
185
|
+
const abuseEmail = ((_w = [ent, ...entEntities]
|
|
154
186
|
.filter((e) => e === null || e === void 0 ? void 0 : e.vcardArray)
|
|
155
187
|
.map((e) => findInObject(e.vcardArray, (el) => { var _a; return Array.isArray(el) && ((_a = e.roles) === null || _a === void 0 ? void 0 : _a.includes("abuse")) && el[0] === "email"; }, (el) => el[3], ""))
|
|
156
|
-
.filter(Boolean)) === null ||
|
|
188
|
+
.filter(Boolean)) === null || _w === void 0 ? void 0 : _w[0]) || "";
|
|
157
189
|
if (ent.handle && ent.handle.toString().match(/^\d+$/)) {
|
|
158
190
|
const id = ent.handle;
|
|
159
191
|
const name = (parseInt(id) == id
|
|
160
|
-
&& ((
|
|
192
|
+
&& ((_x = (await ianaIdToRegistrar(parseInt(id)))) === null || _x === void 0 ? void 0 : _x.name))
|
|
161
193
|
|| findInObject(ent.vcardArray, (el) => Array.isArray(el) && (el[0] === "fn" || el[0] === "org"), (el) => el[3], id);
|
|
162
194
|
registrars.push({ id, name, email, abuseEmail, events: ent.events || response.events || ent.enents || response.enents });
|
|
163
195
|
}
|
|
164
196
|
else {
|
|
165
|
-
|
|
197
|
+
const fnEntry = ent.vcardArray[1].find((el) => el[0] === 'fn');
|
|
198
|
+
const name = fnEntry ? fnEntry[3] : ent.handle || '';
|
|
199
|
+
registrars.push({ id: ent.handle || 0, name, email, abuseEmail, events: ent.events || response.events || ent.enents || response.enents });
|
|
166
200
|
}
|
|
167
201
|
}
|
|
168
202
|
// handles .ar
|
|
@@ -171,23 +205,24 @@ export async function whois(origDomain, options = { fetch: fetch, thinOnly: fals
|
|
|
171
205
|
}
|
|
172
206
|
}
|
|
173
207
|
if (domain.endsWith(".is") &&
|
|
174
|
-
(((
|
|
208
|
+
(((_y = ent.roles) === null || _y === void 0 ? void 0 : _y.includes("technical")) || ent.role === "technical")) {
|
|
175
209
|
const id = ent.handle;
|
|
176
210
|
const name = (parseInt(id) == id
|
|
177
|
-
&& ((
|
|
211
|
+
&& ((_z = (await ianaIdToRegistrar(parseInt(id)))) === null || _z === void 0 ? void 0 : _z.name))
|
|
178
212
|
|| findInObject(ent.vcardArray, (el) => Array.isArray(el) && (el[0] === "fn" || el[0] === "org"), (el) => el[3], id);
|
|
179
|
-
const
|
|
213
|
+
const entEntities = toArray(ent.entities);
|
|
214
|
+
const email = ((_0 = [ent, ...entEntities]
|
|
180
215
|
.filter((e) => e === null || e === void 0 ? void 0 : e.vcardArray)
|
|
181
216
|
.map((e) => findInObject(e.vcardArray, (el) => Array.isArray(el) && el[0] === "email", (el) => el[3], ""))
|
|
182
|
-
.filter(Boolean)) === null ||
|
|
183
|
-
const abuseEmail = ((
|
|
217
|
+
.filter(Boolean)) === null || _0 === void 0 ? void 0 : _0[0]) || "";
|
|
218
|
+
const abuseEmail = ((_1 = [ent, ...entEntities]
|
|
184
219
|
.filter((e) => e === null || e === void 0 ? void 0 : e.vcardArray)
|
|
185
220
|
.map((e) => findInObject(e.vcardArray, (el) => { var _a; return Array.isArray(el) && ((_a = e.roles) === null || _a === void 0 ? void 0 : _a.includes("abuse")) && el[0] === "email"; }, (el) => el[3], ""))
|
|
186
|
-
.filter(Boolean)) === null ||
|
|
221
|
+
.filter(Boolean)) === null || _1 === void 0 ? void 0 : _1[0]) || "";
|
|
187
222
|
const events = ent.events || response.events || ent.enents || response.enents;
|
|
188
223
|
registrars.push({ id, name, email, abuseEmail, events });
|
|
189
224
|
}
|
|
190
|
-
if ((((
|
|
225
|
+
if ((((_2 = ent.roles) === null || _2 === void 0 ? void 0 : _2.includes("reseller")) || ent.role === "reseller") &&
|
|
191
226
|
ent.vcardArray) {
|
|
192
227
|
// vcard objects can be unexpectedly and arbitrarily nested
|
|
193
228
|
const name = findInObject(ent.vcardArray, (el) => Array.isArray(el) && (el[0] === "fn" || el[0] === "org"), (el) => el[3], "");
|
|
@@ -240,57 +275,3 @@ export async function whois(origDomain, options = { fetch: fetch, thinOnly: fals
|
|
|
240
275
|
parseIpResponse(domain, thinResponse, response);
|
|
241
276
|
return response;
|
|
242
277
|
}
|
|
243
|
-
function findStatus(statuses, domain) {
|
|
244
|
-
// console.warn({ domain, statuses });
|
|
245
|
-
return (Array.isArray(statuses)
|
|
246
|
-
? statuses
|
|
247
|
-
: statuses && typeof statuses === "object"
|
|
248
|
-
? Object.keys(statuses)
|
|
249
|
-
: typeof statuses === "string"
|
|
250
|
-
? statuses.trim().split(/\s*,\s*/)
|
|
251
|
-
: []).map((status) => normalizeWhoisStatus(status));
|
|
252
|
-
}
|
|
253
|
-
function findNameservers(values) {
|
|
254
|
-
let nameservers = [];
|
|
255
|
-
if (Array.isArray(values)) {
|
|
256
|
-
nameservers = values;
|
|
257
|
-
}
|
|
258
|
-
else if (typeof values === "object") {
|
|
259
|
-
nameservers = Object.values(values);
|
|
260
|
-
}
|
|
261
|
-
return nameservers
|
|
262
|
-
.map((ns) => { var _a; return ns.ldhName || ns.ldnName || ((_a = ns.ipAddresses) === null || _a === void 0 ? void 0 : _a.v4); })
|
|
263
|
-
.flat()
|
|
264
|
-
.filter((ns) => ns)
|
|
265
|
-
.map((ns) => (ns.stringValue || ns).toLocaleLowerCase())
|
|
266
|
-
.sort();
|
|
267
|
-
}
|
|
268
|
-
function findTimestamps(values) {
|
|
269
|
-
const ts = {
|
|
270
|
-
created: null,
|
|
271
|
-
updated: null,
|
|
272
|
-
expires: null,
|
|
273
|
-
};
|
|
274
|
-
let events = [];
|
|
275
|
-
if (Array.isArray(values)) {
|
|
276
|
-
events = values;
|
|
277
|
-
}
|
|
278
|
-
else if (typeof values === "object") {
|
|
279
|
-
events = Object.values(values);
|
|
280
|
-
}
|
|
281
|
-
for (const [event, field] of eventMap) {
|
|
282
|
-
events.find((ev) => {
|
|
283
|
-
var _a;
|
|
284
|
-
const isMatch = ((_a = ev === null || ev === void 0 ? void 0 : ev.eventAction) === null || _a === void 0 ? void 0 : _a.toLocaleLowerCase()) === event && ev.eventDate;
|
|
285
|
-
if (isMatch) {
|
|
286
|
-
const d = new Date(ev.eventDate.toString().replace(/\+0000Z$/, "Z"));
|
|
287
|
-
// console.log(field, ev.eventDate, d);
|
|
288
|
-
if (!isNaN(d.valueOf())) {
|
|
289
|
-
ts[field] = d;
|
|
290
|
-
return true;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
return ts;
|
|
296
|
-
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import "./polyfills.js";
|