jsonified-whois 1.1.0 → 1.1.1

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/dist/index.cjs ADDED
@@ -0,0 +1,316 @@
1
+ "use strict";
2
+ var __create = Object.create;
3
+ var __defProp = Object.defineProperty;
4
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
5
+ var __getOwnPropNames = Object.getOwnPropertyNames;
6
+ var __getProtoOf = Object.getPrototypeOf;
7
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
8
+ var __export = (target, all) => {
9
+ for (var name in all)
10
+ __defProp(target, name, { get: all[name], enumerable: true });
11
+ };
12
+ var __copyProps = (to, from, except, desc) => {
13
+ if (from && typeof from === "object" || typeof from === "function") {
14
+ for (let key of __getOwnPropNames(from))
15
+ if (!__hasOwnProp.call(to, key) && key !== except)
16
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
17
+ }
18
+ return to;
19
+ };
20
+ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__getProtoOf(mod)) : {}, __copyProps(
21
+ // If the importer is in node compatibility mode or this is not an ESM
22
+ // file that has been converted to a CommonJS file using a Babel-
23
+ // compatible transform (i.e. "__esModule" has not been set), then set
24
+ // "default" to the CommonJS "module.exports" for node compatibility.
25
+ isNodeMode || !mod || !mod.__esModule ? __defProp(target, "default", { value: mod, enumerable: true }) : target,
26
+ mod
27
+ ));
28
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
29
+
30
+ // src/index.ts
31
+ var index_exports = {};
32
+ __export(index_exports, {
33
+ default: () => index_default
34
+ });
35
+ module.exports = __toCommonJS(index_exports);
36
+
37
+ // src/constants.ts
38
+ var timeoutSeconds = 60;
39
+ var ianaWhoIsServer = "whois.iana.org";
40
+ var parserInitialData = {
41
+ createdAt: "",
42
+ domainName: "",
43
+ expiresAt: "",
44
+ ipAddresses: {
45
+ ipv4: [],
46
+ ipv6: []
47
+ },
48
+ nameServers: [],
49
+ registrar: "",
50
+ updateAt: "",
51
+ queriedWhoisServer: "",
52
+ raw: ""
53
+ };
54
+ var ianaFieldMatchers = [
55
+ {
56
+ keywords: ["creation", "created", "registered"],
57
+ targetKey: "createdAt"
58
+ },
59
+ {
60
+ keywords: ["updated", "changed", "last updated"],
61
+ targetKey: "updateAt"
62
+ },
63
+ {
64
+ keywords: ["expiry", "expiration", "expires"],
65
+ targetKey: "expiresAt"
66
+ },
67
+ {
68
+ keywords: ["name server", "nameserver", "nserver"],
69
+ targetKey: "nameServers",
70
+ isArray: true
71
+ },
72
+ {
73
+ keywords: ["whois"],
74
+ targetKey: "registrar"
75
+ }
76
+ ];
77
+ var gtldFiledMatchers = [
78
+ {
79
+ keywords: ["creation", "created", "registered"],
80
+ targetKey: "createdAt"
81
+ },
82
+ {
83
+ keywords: ["updated", "changed", "last updated"],
84
+ targetKey: "updateAt"
85
+ },
86
+ {
87
+ keywords: ["expiry", "expiration", "expires"],
88
+ targetKey: "expiresAt"
89
+ },
90
+ {
91
+ keywords: [
92
+ "name server",
93
+ "nameserver",
94
+ "nserver",
95
+ "nameservers",
96
+ "hostname"
97
+ ],
98
+ targetKey: "nameServers",
99
+ isArray: true
100
+ },
101
+ {
102
+ keywords: ["registrar whois server"],
103
+ targetKey: "registrar"
104
+ }
105
+ ];
106
+ var warnColor = "\x1B[33m";
107
+ var errorColor = "\x1B[31m";
108
+
109
+ // src/utils/normalizer.ts
110
+ var normalizer = (str) => {
111
+ return str.toLowerCase().replace(/[^a-z\s]/g, "").trim();
112
+ };
113
+
114
+ // src/parser/index.ts
115
+ var nameServers = "nameServers";
116
+ var parser = (data, isIana) => {
117
+ const splitter = data.endsWith("\r\n") ? "\r\n" : "\n";
118
+ const matchers = isIana ? ianaFieldMatchers : gtldFiledMatchers;
119
+ const arr = data.split(">>>");
120
+ const responses = arr[0].split(splitter);
121
+ const result = { ...parserInitialData };
122
+ responses.forEach((item) => {
123
+ const [key, value] = item.split(/:(.*)/s);
124
+ if (!key || !value) return;
125
+ const normalizedKey = normalizer(key);
126
+ const trimmedValue = value.trim();
127
+ for (const matcher of matchers) {
128
+ const includesKeyword = matcher.keywords.some(
129
+ (kw) => normalizedKey.includes(kw)
130
+ );
131
+ if (!includesKeyword) {
132
+ continue;
133
+ }
134
+ if (matcher.targetKey === nameServers) {
135
+ const values = trimmedValue.split("\r\n").map((v) => v.trim()).filter(Boolean);
136
+ result.nameServers = [...result.nameServers || [], ...values];
137
+ break;
138
+ }
139
+ result[matcher.targetKey] = trimmedValue;
140
+ break;
141
+ }
142
+ });
143
+ return result;
144
+ };
145
+
146
+ // src/utils/getIpAddresses.ts
147
+ var import_promises = require("dns/promises");
148
+
149
+ // src/utils/log.ts
150
+ var logWarning = (message) => {
151
+ console.warn(warnColor, message);
152
+ };
153
+ var logError = (message) => {
154
+ console.error(errorColor, message);
155
+ };
156
+
157
+ // src/utils/getIpAddresses.ts
158
+ var getIpv6 = async (domain) => {
159
+ try {
160
+ return await (0, import_promises.resolve6)(domain);
161
+ } catch (error) {
162
+ logWarning("Unable to find IPv6 addresses for " + domain + "\n");
163
+ return [];
164
+ }
165
+ };
166
+ var getIpv4 = async (domain) => {
167
+ try {
168
+ return await (0, import_promises.resolve4)(domain);
169
+ } catch (error) {
170
+ logWarning("Unable to find IPv4 addresses for " + domain + "\n");
171
+ return [];
172
+ }
173
+ };
174
+ var getIpAddresses = async (domain) => {
175
+ const ipv4 = await getIpv4(domain);
176
+ const ipv6 = await getIpv6(domain);
177
+ return {
178
+ ipv4,
179
+ ipv6
180
+ };
181
+ };
182
+
183
+ // src/utils/verifyDomains.ts
184
+ var import_tldts = __toESM(require("tldts"), 1);
185
+ var verifyDomain = (domain) => {
186
+ if (!domain.length) {
187
+ throw new Error("Domain input is required");
188
+ }
189
+ const domainWithoutSuffix = import_tldts.default.getDomainWithoutSuffix(domain);
190
+ if (!domainWithoutSuffix) {
191
+ throw new Error("Domain is not valid");
192
+ }
193
+ };
194
+
195
+ // src/utils/queryWhoisServer.ts
196
+ var import_net = __toESM(require("net"), 1);
197
+ var queryWhoisServer = async (domain, whoisServer, port) => {
198
+ return new Promise((resolve, reject) => {
199
+ let data = "";
200
+ const socket = import_net.default.createConnection(
201
+ { host: whoisServer, port },
202
+ () => socket.write(domain + "\r\n")
203
+ );
204
+ socket.setTimeout(timeoutSeconds * 1e3);
205
+ socket.on("data", (chunk) => data += chunk);
206
+ socket.on("close", () => resolve(data));
207
+ socket.on("timeout", () => socket.destroy(new Error("Timeout")));
208
+ socket.on("error", reject);
209
+ });
210
+ };
211
+
212
+ // src/index.ts
213
+ var import_tldts2 = __toESM(require("tldts"), 1);
214
+
215
+ // src/utils/getFallbackData.ts
216
+ var getFallbackData = (results) => {
217
+ const reversedArr = [...results].reverse();
218
+ let counter = [];
219
+ for (let index in reversedArr) {
220
+ counter[index] = 0;
221
+ for (let key in reversedArr[index]) {
222
+ if (reversedArr[index][key]) {
223
+ continue;
224
+ }
225
+ counter[index]++;
226
+ }
227
+ }
228
+ const minLossIndex = counter.indexOf(Math.min(...counter));
229
+ return { ...reversedArr[minLossIndex] };
230
+ };
231
+
232
+ // src/index.ts
233
+ var WhoisClient = class {
234
+ constructor({ url, fallback, port, whoisServer }) {
235
+ try {
236
+ url && verifyDomain(url);
237
+ this.port = port || 43;
238
+ this.whoisServer = whoisServer || ianaWhoIsServer;
239
+ this.fallbackEnabled = fallback || false;
240
+ this.domain = import_tldts2.default.getDomain(url) || "";
241
+ this.hostname = import_tldts2.default.getHostname(url) || "";
242
+ } catch (error) {
243
+ logError("Something went wrong when initializing the client \n");
244
+ throw error;
245
+ }
246
+ }
247
+ port = 43;
248
+ whoisServer = ianaWhoIsServer;
249
+ ipAddresses = {
250
+ ipv4: [],
251
+ ipv6: []
252
+ };
253
+ hostname = "";
254
+ domain = "";
255
+ fallbackEnabled = false;
256
+ whoisResults = [];
257
+ /**
258
+ * Fetch data for the instantiated domain
259
+ */
260
+ async fetchData() {
261
+ this.ipAddresses = await getIpAddresses(this.hostname);
262
+ const response = await this.collectWhoisChain(
263
+ this.domain,
264
+ this.whoisServer
265
+ );
266
+ let lastQuery = { ...response.at(-1) };
267
+ if (this.fallbackEnabled) {
268
+ lastQuery = getFallbackData(response);
269
+ }
270
+ delete lastQuery.raw;
271
+ const data = {
272
+ queryData: response,
273
+ ...lastQuery
274
+ };
275
+ return JSON.stringify(data);
276
+ }
277
+ /**
278
+ * Queries whois servers and collects whois info recursively
279
+ */
280
+ async collectWhoisChain(domain, whoisServer) {
281
+ const existingRegistryData = this.whoisResults.find(
282
+ (item) => item.queriedWhoisServer === whoisServer
283
+ );
284
+ if (existingRegistryData && (existingRegistryData.registrar.length === 0 || existingRegistryData.registrar === whoisServer)) {
285
+ return this.whoisResults;
286
+ }
287
+ const response = await this.queryWhoisServer(domain, whoisServer);
288
+ this.whoisResults.push({
289
+ ...response,
290
+ ipAddresses: this.ipAddresses
291
+ });
292
+ if (!response.registrar) {
293
+ return this.whoisResults;
294
+ }
295
+ return await this.collectWhoisChain(domain, response.registrar);
296
+ }
297
+ /**
298
+ * Query given whois server and return parsed data with matched keys.
299
+ */
300
+ async queryWhoisServer(domain, whoisServer) {
301
+ try {
302
+ const response = await queryWhoisServer(domain, whoisServer, this.port);
303
+ const isIana = whoisServer === ianaWhoIsServer;
304
+ const conversionResult = parser(response, isIana);
305
+ conversionResult.domainName = this.domain;
306
+ conversionResult.queriedWhoisServer = whoisServer;
307
+ conversionResult.raw = response;
308
+ conversionResult.ipAddresses = this.ipAddresses;
309
+ return conversionResult;
310
+ } catch (error) {
311
+ logError(error);
312
+ return parserInitialData;
313
+ }
314
+ }
315
+ };
316
+ var index_default = WhoisClient;
@@ -0,0 +1,34 @@
1
+ interface WhoisClientConstructor {
2
+ fallback?: boolean;
3
+ url: string;
4
+ port?: number;
5
+ whoisServer?: string;
6
+ }
7
+
8
+ /**
9
+ * A client to get basic whois information
10
+ */
11
+ declare class WhoisClient {
12
+ constructor({ url, fallback, port, whoisServer }: WhoisClientConstructor);
13
+ private port;
14
+ private whoisServer;
15
+ private ipAddresses;
16
+ private hostname;
17
+ private domain;
18
+ private fallbackEnabled;
19
+ private whoisResults;
20
+ /**
21
+ * Fetch data for the instantiated domain
22
+ */
23
+ fetchData(): Promise<string>;
24
+ /**
25
+ * Queries whois servers and collects whois info recursively
26
+ */
27
+ private collectWhoisChain;
28
+ /**
29
+ * Query given whois server and return parsed data with matched keys.
30
+ */
31
+ private queryWhoisServer;
32
+ }
33
+
34
+ export { WhoisClient as default };
@@ -0,0 +1,34 @@
1
+ interface WhoisClientConstructor {
2
+ fallback?: boolean;
3
+ url: string;
4
+ port?: number;
5
+ whoisServer?: string;
6
+ }
7
+
8
+ /**
9
+ * A client to get basic whois information
10
+ */
11
+ declare class WhoisClient {
12
+ constructor({ url, fallback, port, whoisServer }: WhoisClientConstructor);
13
+ private port;
14
+ private whoisServer;
15
+ private ipAddresses;
16
+ private hostname;
17
+ private domain;
18
+ private fallbackEnabled;
19
+ private whoisResults;
20
+ /**
21
+ * Fetch data for the instantiated domain
22
+ */
23
+ fetchData(): Promise<string>;
24
+ /**
25
+ * Queries whois servers and collects whois info recursively
26
+ */
27
+ private collectWhoisChain;
28
+ /**
29
+ * Query given whois server and return parsed data with matched keys.
30
+ */
31
+ private queryWhoisServer;
32
+ }
33
+
34
+ export { WhoisClient as default };
package/dist/index.js ADDED
@@ -0,0 +1,283 @@
1
+ // src/constants.ts
2
+ var timeoutSeconds = 60;
3
+ var ianaWhoIsServer = "whois.iana.org";
4
+ var parserInitialData = {
5
+ createdAt: "",
6
+ domainName: "",
7
+ expiresAt: "",
8
+ ipAddresses: {
9
+ ipv4: [],
10
+ ipv6: []
11
+ },
12
+ nameServers: [],
13
+ registrar: "",
14
+ updateAt: "",
15
+ queriedWhoisServer: "",
16
+ raw: ""
17
+ };
18
+ var ianaFieldMatchers = [
19
+ {
20
+ keywords: ["creation", "created", "registered"],
21
+ targetKey: "createdAt"
22
+ },
23
+ {
24
+ keywords: ["updated", "changed", "last updated"],
25
+ targetKey: "updateAt"
26
+ },
27
+ {
28
+ keywords: ["expiry", "expiration", "expires"],
29
+ targetKey: "expiresAt"
30
+ },
31
+ {
32
+ keywords: ["name server", "nameserver", "nserver"],
33
+ targetKey: "nameServers",
34
+ isArray: true
35
+ },
36
+ {
37
+ keywords: ["whois"],
38
+ targetKey: "registrar"
39
+ }
40
+ ];
41
+ var gtldFiledMatchers = [
42
+ {
43
+ keywords: ["creation", "created", "registered"],
44
+ targetKey: "createdAt"
45
+ },
46
+ {
47
+ keywords: ["updated", "changed", "last updated"],
48
+ targetKey: "updateAt"
49
+ },
50
+ {
51
+ keywords: ["expiry", "expiration", "expires"],
52
+ targetKey: "expiresAt"
53
+ },
54
+ {
55
+ keywords: [
56
+ "name server",
57
+ "nameserver",
58
+ "nserver",
59
+ "nameservers",
60
+ "hostname"
61
+ ],
62
+ targetKey: "nameServers",
63
+ isArray: true
64
+ },
65
+ {
66
+ keywords: ["registrar whois server"],
67
+ targetKey: "registrar"
68
+ }
69
+ ];
70
+ var warnColor = "\x1B[33m";
71
+ var errorColor = "\x1B[31m";
72
+
73
+ // src/utils/normalizer.ts
74
+ var normalizer = (str) => {
75
+ return str.toLowerCase().replace(/[^a-z\s]/g, "").trim();
76
+ };
77
+
78
+ // src/parser/index.ts
79
+ var nameServers = "nameServers";
80
+ var parser = (data, isIana) => {
81
+ const splitter = data.endsWith("\r\n") ? "\r\n" : "\n";
82
+ const matchers = isIana ? ianaFieldMatchers : gtldFiledMatchers;
83
+ const arr = data.split(">>>");
84
+ const responses = arr[0].split(splitter);
85
+ const result = { ...parserInitialData };
86
+ responses.forEach((item) => {
87
+ const [key, value] = item.split(/:(.*)/s);
88
+ if (!key || !value) return;
89
+ const normalizedKey = normalizer(key);
90
+ const trimmedValue = value.trim();
91
+ for (const matcher of matchers) {
92
+ const includesKeyword = matcher.keywords.some(
93
+ (kw) => normalizedKey.includes(kw)
94
+ );
95
+ if (!includesKeyword) {
96
+ continue;
97
+ }
98
+ if (matcher.targetKey === nameServers) {
99
+ const values = trimmedValue.split("\r\n").map((v) => v.trim()).filter(Boolean);
100
+ result.nameServers = [...result.nameServers || [], ...values];
101
+ break;
102
+ }
103
+ result[matcher.targetKey] = trimmedValue;
104
+ break;
105
+ }
106
+ });
107
+ return result;
108
+ };
109
+
110
+ // src/utils/getIpAddresses.ts
111
+ import { resolve4, resolve6 } from "dns/promises";
112
+
113
+ // src/utils/log.ts
114
+ var logWarning = (message) => {
115
+ console.warn(warnColor, message);
116
+ };
117
+ var logError = (message) => {
118
+ console.error(errorColor, message);
119
+ };
120
+
121
+ // src/utils/getIpAddresses.ts
122
+ var getIpv6 = async (domain) => {
123
+ try {
124
+ return await resolve6(domain);
125
+ } catch (error) {
126
+ logWarning("Unable to find IPv6 addresses for " + domain + "\n");
127
+ return [];
128
+ }
129
+ };
130
+ var getIpv4 = async (domain) => {
131
+ try {
132
+ return await resolve4(domain);
133
+ } catch (error) {
134
+ logWarning("Unable to find IPv4 addresses for " + domain + "\n");
135
+ return [];
136
+ }
137
+ };
138
+ var getIpAddresses = async (domain) => {
139
+ const ipv4 = await getIpv4(domain);
140
+ const ipv6 = await getIpv6(domain);
141
+ return {
142
+ ipv4,
143
+ ipv6
144
+ };
145
+ };
146
+
147
+ // src/utils/verifyDomains.ts
148
+ import tldts from "tldts";
149
+ var verifyDomain = (domain) => {
150
+ if (!domain.length) {
151
+ throw new Error("Domain input is required");
152
+ }
153
+ const domainWithoutSuffix = tldts.getDomainWithoutSuffix(domain);
154
+ if (!domainWithoutSuffix) {
155
+ throw new Error("Domain is not valid");
156
+ }
157
+ };
158
+
159
+ // src/utils/queryWhoisServer.ts
160
+ import net from "net";
161
+ var queryWhoisServer = async (domain, whoisServer, port) => {
162
+ return new Promise((resolve, reject) => {
163
+ let data = "";
164
+ const socket = net.createConnection(
165
+ { host: whoisServer, port },
166
+ () => socket.write(domain + "\r\n")
167
+ );
168
+ socket.setTimeout(timeoutSeconds * 1e3);
169
+ socket.on("data", (chunk) => data += chunk);
170
+ socket.on("close", () => resolve(data));
171
+ socket.on("timeout", () => socket.destroy(new Error("Timeout")));
172
+ socket.on("error", reject);
173
+ });
174
+ };
175
+
176
+ // src/index.ts
177
+ import tldts2 from "tldts";
178
+
179
+ // src/utils/getFallbackData.ts
180
+ var getFallbackData = (results) => {
181
+ const reversedArr = [...results].reverse();
182
+ let counter = [];
183
+ for (let index in reversedArr) {
184
+ counter[index] = 0;
185
+ for (let key in reversedArr[index]) {
186
+ if (reversedArr[index][key]) {
187
+ continue;
188
+ }
189
+ counter[index]++;
190
+ }
191
+ }
192
+ const minLossIndex = counter.indexOf(Math.min(...counter));
193
+ return { ...reversedArr[minLossIndex] };
194
+ };
195
+
196
+ // src/index.ts
197
+ var WhoisClient = class {
198
+ constructor({ url, fallback, port, whoisServer }) {
199
+ try {
200
+ url && verifyDomain(url);
201
+ this.port = port || 43;
202
+ this.whoisServer = whoisServer || ianaWhoIsServer;
203
+ this.fallbackEnabled = fallback || false;
204
+ this.domain = tldts2.getDomain(url) || "";
205
+ this.hostname = tldts2.getHostname(url) || "";
206
+ } catch (error) {
207
+ logError("Something went wrong when initializing the client \n");
208
+ throw error;
209
+ }
210
+ }
211
+ port = 43;
212
+ whoisServer = ianaWhoIsServer;
213
+ ipAddresses = {
214
+ ipv4: [],
215
+ ipv6: []
216
+ };
217
+ hostname = "";
218
+ domain = "";
219
+ fallbackEnabled = false;
220
+ whoisResults = [];
221
+ /**
222
+ * Fetch data for the instantiated domain
223
+ */
224
+ async fetchData() {
225
+ this.ipAddresses = await getIpAddresses(this.hostname);
226
+ const response = await this.collectWhoisChain(
227
+ this.domain,
228
+ this.whoisServer
229
+ );
230
+ let lastQuery = { ...response.at(-1) };
231
+ if (this.fallbackEnabled) {
232
+ lastQuery = getFallbackData(response);
233
+ }
234
+ delete lastQuery.raw;
235
+ const data = {
236
+ queryData: response,
237
+ ...lastQuery
238
+ };
239
+ return JSON.stringify(data);
240
+ }
241
+ /**
242
+ * Queries whois servers and collects whois info recursively
243
+ */
244
+ async collectWhoisChain(domain, whoisServer) {
245
+ const existingRegistryData = this.whoisResults.find(
246
+ (item) => item.queriedWhoisServer === whoisServer
247
+ );
248
+ if (existingRegistryData && (existingRegistryData.registrar.length === 0 || existingRegistryData.registrar === whoisServer)) {
249
+ return this.whoisResults;
250
+ }
251
+ const response = await this.queryWhoisServer(domain, whoisServer);
252
+ this.whoisResults.push({
253
+ ...response,
254
+ ipAddresses: this.ipAddresses
255
+ });
256
+ if (!response.registrar) {
257
+ return this.whoisResults;
258
+ }
259
+ return await this.collectWhoisChain(domain, response.registrar);
260
+ }
261
+ /**
262
+ * Query given whois server and return parsed data with matched keys.
263
+ */
264
+ async queryWhoisServer(domain, whoisServer) {
265
+ try {
266
+ const response = await queryWhoisServer(domain, whoisServer, this.port);
267
+ const isIana = whoisServer === ianaWhoIsServer;
268
+ const conversionResult = parser(response, isIana);
269
+ conversionResult.domainName = this.domain;
270
+ conversionResult.queriedWhoisServer = whoisServer;
271
+ conversionResult.raw = response;
272
+ conversionResult.ipAddresses = this.ipAddresses;
273
+ return conversionResult;
274
+ } catch (error) {
275
+ logError(error);
276
+ return parserInitialData;
277
+ }
278
+ }
279
+ };
280
+ var index_default = WhoisClient;
281
+ export {
282
+ index_default as default
283
+ };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "jsonified-whois",
3
- "version": "1.1.0",
3
+ "version": "1.1.1",
4
4
  "description": "A simple, open-source WHOIS client for Node.js that returns unified data as json",
5
5
  "main": "./dist/index.js",
6
6
  "type": "module",