1
mirror of https://github.com/jakejarvis/rdapper.git synced 2025-10-18 20:14:27 -04:00

Add CLI script for domain lookup and create package-lock.json; update README and package.json for clarity and dependencies

This commit is contained in:
2025-09-24 17:28:12 -04:00
parent 43fa2a12c7
commit 0a852ff3cd
8 changed files with 315 additions and 238 deletions

View File

@@ -1,19 +1,14 @@
# rdapper
🤵Domain RDAP/WHOIS fetched and parser for Node
rdapper
========
Fetch and parse domain registration data with RDAP-first and WHOIS fallback. Node 18+.
🤵 Fetch and parse domain registration data using RDAP and falling back to WHOIS.
Install
-------
## Install
```bash
pnpm add rdapper
npm install rdapper
```
Usage
-----
## Usage
```ts
import { lookupDomain } from "rdapper";
@@ -27,8 +22,12 @@ if (!ok) throw new Error(error);
console.log(record);
```
Notes
-----
## Notes
- Uses IANA RDAP bootstrap and RDAP JSON when available; falls back to WHOIS.
- Standardized output regardless of source.
- No external HTTP client deps; relies on global fetch. WHOIS uses TCP 43.
- No external HTTP client deps; relies on global fetch. WHOIS uses TCP 43.
## License
[MIT](LICENSE)

27
cli.mjs Executable file
View File

@@ -0,0 +1,27 @@
#!/usr/bin/env node
// Quick informal test runner for rdapper
// Usage:
// npm run build && node dev.mjs example.com
// Env:
// RDAP_ONLY=1 | WHOIS_ONLY=1 | TIMEOUT_MS=15000 | FOLLOW_REFERRAL=0
import { lookupDomain } from "./dist/index.js";
const domain = process.argv[2] ?? "example.com";
const options = {
timeoutMs: process.env.TIMEOUT_MS ? Number(process.env.TIMEOUT_MS) : 15000,
followWhoisReferral: process.env.FOLLOW_REFERRAL !== "0",
rdapOnly: process.env.RDAP_ONLY === "1",
whoisOnly: process.env.WHOIS_ONLY === "1",
};
const result = await lookupDomain(domain, options);
if (!result.ok) {
console.error("Error:", result.error);
process.exitCode = 1;
} else {
console.log(JSON.stringify(result.record, null, 2));
}

247
package-lock.json generated Normal file
View File

@@ -0,0 +1,247 @@
{
"name": "rdapper",
"version": "0.0.0-beta.1",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "rdapper",
"version": "0.0.0-beta.1",
"license": "MIT",
"dependencies": {
"psl": "1.15.0"
},
"devDependencies": {
"@biomejs/biome": "2.2.4",
"@types/node": "24.5.2",
"@types/psl": "1.1.3",
"typescript": "5.9.2"
},
"engines": {
"node": ">=18.17"
}
},
"node_modules/@biomejs/biome": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@biomejs/biome/-/biome-2.2.4.tgz",
"integrity": "sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg==",
"dev": true,
"license": "MIT OR Apache-2.0",
"bin": {
"biome": "bin/biome"
},
"engines": {
"node": ">=14.21.3"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/biome"
},
"optionalDependencies": {
"@biomejs/cli-darwin-arm64": "2.2.4",
"@biomejs/cli-darwin-x64": "2.2.4",
"@biomejs/cli-linux-arm64": "2.2.4",
"@biomejs/cli-linux-arm64-musl": "2.2.4",
"@biomejs/cli-linux-x64": "2.2.4",
"@biomejs/cli-linux-x64-musl": "2.2.4",
"@biomejs/cli-win32-arm64": "2.2.4",
"@biomejs/cli-win32-x64": "2.2.4"
}
},
"node_modules/@biomejs/cli-darwin-arm64": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-arm64/-/cli-darwin-arm64-2.2.4.tgz",
"integrity": "sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-darwin-x64": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-darwin-x64/-/cli-darwin-x64-2.2.4.tgz",
"integrity": "sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"darwin"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-linux-arm64": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64/-/cli-linux-arm64-2.2.4.tgz",
"integrity": "sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-linux-arm64-musl": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-arm64-musl/-/cli-linux-arm64-musl-2.2.4.tgz",
"integrity": "sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-linux-x64": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64/-/cli-linux-x64-2.2.4.tgz",
"integrity": "sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-linux-x64-musl": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-linux-x64-musl/-/cli-linux-x64-musl-2.2.4.tgz",
"integrity": "sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"linux"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-win32-arm64": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-arm64/-/cli-win32-arm64-2.2.4.tgz",
"integrity": "sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ==",
"cpu": [
"arm64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@biomejs/cli-win32-x64": {
"version": "2.2.4",
"resolved": "https://registry.npmjs.org/@biomejs/cli-win32-x64/-/cli-win32-x64-2.2.4.tgz",
"integrity": "sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg==",
"cpu": [
"x64"
],
"dev": true,
"license": "MIT OR Apache-2.0",
"optional": true,
"os": [
"win32"
],
"engines": {
"node": ">=14.21.3"
}
},
"node_modules/@types/node": {
"version": "24.5.2",
"resolved": "https://registry.npmjs.org/@types/node/-/node-24.5.2.tgz",
"integrity": "sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==",
"dev": true,
"license": "MIT",
"dependencies": {
"undici-types": "~7.12.0"
}
},
"node_modules/@types/psl": {
"version": "1.1.3",
"resolved": "https://registry.npmjs.org/@types/psl/-/psl-1.1.3.tgz",
"integrity": "sha512-Iu174JHfLd7i/XkXY6VDrqSlPvTDQOtQI7wNAXKKOAADJ9TduRLkNdMgjGiMxSttUIZnomv81JAbAbC0DhggxA==",
"dev": true,
"license": "MIT"
},
"node_modules/psl": {
"version": "1.15.0",
"resolved": "https://registry.npmjs.org/psl/-/psl-1.15.0.tgz",
"integrity": "sha512-JZd3gMVBAVQkSs6HdNZo9Sdo0LNcQeMNP3CozBJb3JYC/QUYZTnKxP+f8oWRX4rHP5EurWxqAHTSwUCjlNKa1w==",
"license": "MIT",
"dependencies": {
"punycode": "^2.3.1"
},
"funding": {
"url": "https://github.com/sponsors/lupomontero"
}
},
"node_modules/punycode": {
"version": "2.3.1",
"resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz",
"integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==",
"license": "MIT",
"engines": {
"node": ">=6"
}
},
"node_modules/typescript": {
"version": "5.9.2",
"resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.2.tgz",
"integrity": "sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==",
"dev": true,
"license": "Apache-2.0",
"bin": {
"tsc": "bin/tsc",
"tsserver": "bin/tsserver"
},
"engines": {
"node": ">=14.17"
}
},
"node_modules/undici-types": {
"version": "7.12.0",
"resolved": "https://registry.npmjs.org/undici-types/-/undici-types-7.12.0.tgz",
"integrity": "sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==",
"dev": true,
"license": "MIT"
}
}
}

View File

@@ -21,13 +21,16 @@
"test": "tsc && node --test dist/**/*.test.js",
"lint": "biome check --write"
},
"engines": {
"node": ">=18.17"
"dependencies": {
"psl": "1.15.0"
},
"devDependencies": {
"@biomejs/biome": "2.2.4",
"@types/node": "^24.5.2",
"typescript": "^5.9.2"
"@types/node": "24.5.2",
"@types/psl": "1.1.3",
"typescript": "5.9.2"
},
"packageManager": "pnpm@10.17.1+sha512.17c560fca4867ae9473a3899ad84a88334914f379be46d455cbf92e5cf4b39d34985d452d2583baf19967fa76cb5c17bc9e245529d0b98745721aa7200ecaf7a"
"engines": {
"node": ">=18.17"
}
}

130
pnpm-lock.yaml generated
View File

@@ -1,130 +0,0 @@
lockfileVersion: '9.0'
settings:
autoInstallPeers: true
excludeLinksFromLockfile: false
importers:
.:
devDependencies:
'@biomejs/biome':
specifier: 2.2.4
version: 2.2.4
'@types/node':
specifier: ^24.5.2
version: 24.5.2
typescript:
specifier: ^5.9.2
version: 5.9.2
packages:
'@biomejs/biome@2.2.4':
resolution: {integrity: sha512-TBHU5bUy/Ok6m8c0y3pZiuO/BZoY/OcGxoLlrfQof5s8ISVwbVBdFINPQZyFfKwil8XibYWb7JMwnT8wT4WVPg==}
engines: {node: '>=14.21.3'}
hasBin: true
'@biomejs/cli-darwin-arm64@2.2.4':
resolution: {integrity: sha512-RJe2uiyaloN4hne4d2+qVj3d3gFJFbmrr5PYtkkjei1O9c+BjGXgpUPVbi8Pl8syumhzJjFsSIYkcLt2VlVLMA==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [darwin]
'@biomejs/cli-darwin-x64@2.2.4':
resolution: {integrity: sha512-cFsdB4ePanVWfTnPVaUX+yr8qV8ifxjBKMkZwN7gKb20qXPxd/PmwqUH8mY5wnM9+U0QwM76CxFyBRJhC9tQwg==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [darwin]
'@biomejs/cli-linux-arm64-musl@2.2.4':
resolution: {integrity: sha512-7TNPkMQEWfjvJDaZRSkDCPT/2r5ESFPKx+TEev+I2BXDGIjfCZk2+b88FOhnJNHtksbOZv8ZWnxrA5gyTYhSsQ==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-arm64@2.2.4':
resolution: {integrity: sha512-M/Iz48p4NAzMXOuH+tsn5BvG/Jb07KOMTdSVwJpicmhN309BeEyRyQX+n1XDF0JVSlu28+hiTQ2L4rZPvu7nMw==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [linux]
'@biomejs/cli-linux-x64-musl@2.2.4':
resolution: {integrity: sha512-m41nFDS0ksXK2gwXL6W6yZTYPMH0LughqbsxInSKetoH6morVj43szqKx79Iudkp8WRT5SxSh7qVb8KCUiewGg==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-linux-x64@2.2.4':
resolution: {integrity: sha512-orr3nnf2Dpb2ssl6aihQtvcKtLySLta4E2UcXdp7+RTa7mfJjBgIsbS0B9GC8gVu0hjOu021aU8b3/I1tn+pVQ==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [linux]
'@biomejs/cli-win32-arm64@2.2.4':
resolution: {integrity: sha512-NXnfTeKHDFUWfxAefa57DiGmu9VyKi0cDqFpdI+1hJWQjGJhJutHPX0b5m+eXvTKOaf+brU+P0JrQAZMb5yYaQ==}
engines: {node: '>=14.21.3'}
cpu: [arm64]
os: [win32]
'@biomejs/cli-win32-x64@2.2.4':
resolution: {integrity: sha512-3Y4V4zVRarVh/B/eSHczR4LYoSVyv3Dfuvm3cWs5w/HScccS0+Wt/lHOcDTRYeHjQmMYVC3rIRWqyN2EI52+zg==}
engines: {node: '>=14.21.3'}
cpu: [x64]
os: [win32]
'@types/node@24.5.2':
resolution: {integrity: sha512-FYxk1I7wPv3K2XBaoyH2cTnocQEu8AOZ60hPbsyukMPLv5/5qr7V1i8PLHdl6Zf87I+xZXFvPCXYjiTFq+YSDQ==}
typescript@5.9.2:
resolution: {integrity: sha512-CWBzXQrc/qOkhidw1OzBTQuYRbfyxDXJMVJ1XNwUHGROVmuaeiEm3OslpZ1RV96d7SKKjZKrSJu3+t/xlw3R9A==}
engines: {node: '>=14.17'}
hasBin: true
undici-types@7.12.0:
resolution: {integrity: sha512-goOacqME2GYyOZZfb5Lgtu+1IDmAlAEu5xnD3+xTzS10hT0vzpf0SPjkXwAw9Jm+4n/mQGDP3LO8CPbYROeBfQ==}
snapshots:
'@biomejs/biome@2.2.4':
optionalDependencies:
'@biomejs/cli-darwin-arm64': 2.2.4
'@biomejs/cli-darwin-x64': 2.2.4
'@biomejs/cli-linux-arm64': 2.2.4
'@biomejs/cli-linux-arm64-musl': 2.2.4
'@biomejs/cli-linux-x64': 2.2.4
'@biomejs/cli-linux-x64-musl': 2.2.4
'@biomejs/cli-win32-arm64': 2.2.4
'@biomejs/cli-win32-x64': 2.2.4
'@biomejs/cli-darwin-arm64@2.2.4':
optional: true
'@biomejs/cli-darwin-x64@2.2.4':
optional: true
'@biomejs/cli-linux-arm64-musl@2.2.4':
optional: true
'@biomejs/cli-linux-arm64@2.2.4':
optional: true
'@biomejs/cli-linux-x64-musl@2.2.4':
optional: true
'@biomejs/cli-linux-x64@2.2.4':
optional: true
'@biomejs/cli-win32-arm64@2.2.4':
optional: true
'@biomejs/cli-win32-x64@2.2.4':
optional: true
'@types/node@24.5.2':
dependencies:
undici-types: 7.12.0
typescript@5.9.2: {}
undici-types@7.12.0: {}

View File

@@ -1,3 +1,5 @@
import psl from "psl";
// Lightweight date parsing helpers to avoid external dependencies.
// We aim to parse common RDAP and WHOIS date representations and return a UTC ISO string.
export function toISO(
@@ -189,6 +191,19 @@ export function sleep(ms: number): Promise<void> {
export function extractTld(domain: string): string {
const lower = domain.trim().toLowerCase();
try {
const parsed = psl.parse?.(lower);
if (!("tld" in parsed)) {
return lower;
}
const suffix = parsed?.tld;
if (suffix) {
const labels = String(suffix).split(".").filter(Boolean);
if (labels.length) return labels[labels.length - 1];
}
} catch {
// ignore and fall back
}
const parts = lower.split(".").filter(Boolean);
return parts[parts.length - 1] ?? lower;
}

View File

@@ -1,84 +0,0 @@
// Centralized WHOIS server fallbacks by TLD.
// These are used when IANA's TLD page does not list a server or is unreachable.
export const WHOIS_FALLBACKS: Record<string, string> = {
// gTLDs
com: "whois.verisign-grs.com",
net: "whois.verisign-grs.com",
org: "whois.pir.org",
info: "whois.afilias.net",
biz: "whois.nic.biz",
xyz: "whois.nic.xyz",
top: "whois.nic.top",
club: "whois.nic.club",
site: "whois.nic.site",
online: "whois.nic.online",
shop: "whois.nic.shop",
store: "whois.nic.store",
tech: "whois.nic.tech",
space: "whois.nic.space",
link: "whois.nic.link",
guru: "whois.nic.guru",
mobi: "whois.nic.mobi",
name: "whois.nic.name",
pro: "whois.nic.pro",
tel: "whois.nic.tel",
travel: "whois.nic.travel",
aero: "whois.aero",
jobs: "whois.nic.jobs",
cat: "whois.nic.cat",
museum: "whois.museum",
coop: "whois.nic.coop",
asia: "whois.nic.asia",
app: "whois.nic.google",
dev: "whois.nic.google",
page: "whois.nic.google",
blog: "whois.nic.blog",
// ccTLDs (port 43 policy varies by registry)
us: "whois.nic.us",
uk: "whois.nic.uk",
co: "whois.nic.co",
me: "whois.nic.me",
tv: "whois.nic.tv",
io: "whois.nic.io",
ai: "whois.nic.ai",
de: "whois.denic.de",
fr: "whois.nic.fr",
nl: "whois.domain-registry.nl",
ru: "whois.tcinet.ru",
pl: "whois.dns.pl",
br: "whois.registro.br",
it: "whois.nic.it",
es: "whois.nic.es",
se: "whois.iis.se",
no: "whois.norid.no",
dk: "whois.dk-hostmaster.dk",
fi: "whois.fi",
ch: "whois.nic.ch",
at: "whois.nic.at",
be: "whois.dns.be",
cz: "whois.nic.cz",
sk: "whois.sk-nic.sk",
hu: "whois.nic.hu",
ro: "whois.rotld.ro",
bg: "whois.register.bg",
gr: "whois.ics.forth.gr",
tr: "whois.trabis.gov.tr",
il: "whois.isoc.org.il",
za: "whois.registry.net.za",
nz: "whois.srs.net.nz",
jp: "whois.jprs.jp",
cn: "whois.cnnic.cn",
kr: "whois.kr",
tw: "whois.twnic.net.tw",
sg: "whois.sgnic.sg",
hk: "whois.hkirc.hk",
id: "whois.id",
my: "whois.mynic.my",
ph: "whois.dot.ph",
th: "whois.thnic.co.th",
vn: "whois.vnnic.vn",
mx: "whois.mx",
ar: "whois.nic.ar",
cl: "whois.nic.cl",
pe: "kero.yachay.pe",
};

View File

@@ -2,7 +2,6 @@ import { createConnection } from "node:net";
import { DEFAULT_TIMEOUT_MS } from "./config.js";
import type { LookupOptions } from "./types.js";
import { withTimeout } from "./utils.js";
import { WHOIS_FALLBACKS } from "./whois-fallbacks.js";
export interface WhoisQueryResult {
serverQueried: string;
@@ -73,6 +72,11 @@ export async function ianaWhoisServerForTld(
tld: string,
options?: LookupOptions,
): Promise<string | undefined> {
const EXCEPTIONS: Record<string, string> = {
com: "whois.verisign-grs.com",
net: "whois.verisign-grs.com",
org: "whois.pir.org",
};
const url = `https://www.iana.org/domains/root/db/${encodeURIComponent(tld)}.html`;
try {
const res = await withTimeout(
@@ -86,14 +90,10 @@ export async function ianaWhoisServerForTld(
html.match(/Whois Server:\s*([^<\n]+)/i);
const server = m?.[1]?.trim();
if (!server)
return (
WHOIS_FALLBACKS[tld.toLowerCase()] ?? `whois.nic.${tld.toLowerCase()}`
);
return EXCEPTIONS[tld.toLowerCase()] ?? `whois.nic.${tld.toLowerCase()}`;
return server.replace(/^https?:\/\//i, "").replace(/\/$/, "");
} catch {
return (
WHOIS_FALLBACKS[tld.toLowerCase()] ?? `whois.nic.${tld.toLowerCase()}`
);
return EXCEPTIONS[tld.toLowerCase()] ?? `whois.nic.${tld.toLowerCase()}`;
}
}