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

Update repository guidelines and README: enhance project structure documentation, clarify build and test commands, and improve API usage examples for better user understanding.

This commit is contained in:
2025-09-24 19:19:41 -04:00
parent 3429c70bd7
commit 05445ef831
3 changed files with 165 additions and 36 deletions

View File

@@ -1,37 +1,39 @@
# Repository Guidelines
## Project Structure & Module Organization
- `src/api/`: Public lookup orchestration (`lookup.ts`).
- `src/rdap/`: RDAP bootstrap, client, and normalization (`bootstrap.ts`, `client.ts`, `normalize.ts`).
- `src/whois/`: WHOIS TCP client, discovery, referral, normalization, catalog.
- `src/lib/`: Shared utilities (`dates.ts`, `async.ts`, `domain.ts`, `text.ts`).
- `src/types.ts`: Public types. `src/index.ts` re-exports API and types.
- Tests: per-module `__tests__/` folders with `*.test.ts` (e.g., `src/lib/__tests__/dates.test.ts`).
- `dist/`: Build output (generated). Do not edit.
- `cli.mjs`: Local CLI for manual checks.
- Source: `src/` (entry: `src/index.ts`).
- Protocol modules: `src/rdap/` and `src/whois/` (clients, normalize, helpers).
- Utilities: `src/lib/`, shared types: `src/types.ts`.
- Tests live beside code in `__tests__/` and use `*.test.ts`.
- Built output: `dist/` (ESM + types). Quick CLI: `cli.mjs` for manual checks.
## Build, Test, and Development Commands
- `npm run build`: Clean and compile with `tsc -p tsconfig.build.json` (excludes tests); outputs to `dist/`.
- `npm test`: Compile tests, then run Nodes test runner on `dist/**/*.test.js`.
- `npm run lint`: Biome format+lint with autofix per `biome.json`.
- Example CLI: `npm run build && node cli.mjs example.com`.
- `npm run build` — clean and compile TypeScript to `dist/`.
- `npm test` — type-check, build, and run Nodes test runner on `dist/**/*.test.js`.
- `npm run lint` format and lint with Biome (auto-fixes).
- Manual smoke: `node cli.mjs example.com` (after `npm run build`).
- Network smoke tests (optin): `SMOKE=1 npm test`.
## Coding Style & Naming Conventions
- TypeScript strict; ES2022 ESM (`tsconfig.json`).
- Biome-enforced: spaces indentation; double quotes; organized imports.
- Filenames: kebab-case for modules (e.g., `normalize-rdap.ts`).
- Identifiers: camelCase; avoid abbreviations; explicit return types for exported functions.
- TypeScript strict, ESM (`module`/`moduleResolution: NodeNext`).
- Formatting via Biome: spaces, 2space indent, double quotes.
- Naming: functions/vars `camelCase`, types/interfaces `PascalCase`, constants `UPPER_SNAKE_CASE`.
- Files: lowercase; tests in `__tests__/` with `*.test.ts`.
- Prefer named exports; avoid default exports unless ergonomic.
## Testing Guidelines
- Framework: Node `node:test`.
- Tests live under `src/**/__tests__` and are deterministic/offline by default.
- Smoke tests gated by `SMOKE=1` (e.g., `SMOKE=1 npm test`).
- Run all tests: `npm test`.
- Framework: Node `node:test` + `assert/strict`.
- Unit tests must be deterministic and offline. Gate network tests behind `SMOKE=1`.
- Place tests near the code (e.g., `src/whois/__tests__/normalize.test.ts`).
- Run locally: `npm test`; for smoke: `SMOKE=1 npm test`.
## Commit & Pull Request Guidelines
- Commits: imperative, concise summaries (e.g., “Refactor lookup: tighten error handling”).
- PRs: include what/why, linked issues, and test notes; ensure `npm run lint && npm test` pass.
- Commits: concise imperative subject (e.g., “Add WHOIS referral fallback”), with a brief “what/why” body.
- Scope changes narrowly; keep diffs readable.
- PRs: clear description, linked issues, test plan (commands + expected output), and any CLI screenshots/logs when relevant.
- Required checks before PR: `npm run lint && npm test`.
## Release & Security Notes
- Publish only `dist/`; `prepublishOnly` runs the build. Tests are excluded via `tsconfig.build.json` and `files` in `package.json`.
- Node >= 18.17 with global `fetch`. WHOIS uses TCP 43; be mindful of registry rate limits.
## Security & Configuration Tips
- Node `>= 18.17`. No external HTTP client; uses global `fetch` and TCP 43 for WHOIS.
- Avoid hardcoding endpoints. Respect options: `timeoutMs`, `followWhoisReferral`, `rdapOnly`, `whoisOnly`.
- Do not run network tests in CI by default; use `SMOKE=1` only when appropriate.

148
README.md
View File

@@ -1,6 +1,13 @@
# rdapper
# 🎩 rdapper
🤵 Fetch and parse domain registration data using RDAP and falling back to WHOIS.
RDAPfirst domain registration lookups with WHOIS fallback. Produces a single, normalized record shape regardless of source.
- RDAP discovery via IANA bootstrap (`https://data.iana.org/rdap/dns.json`)
- WHOIS TCP 43 client with TLD discovery, registrar referral follow, and curated exceptions
- Normalized output: registrar, contacts, nameservers, statuses, dates, DNSSEC, source metadata
- TypeScript types included; ESMonly; no external HTTP client (uses global `fetch`)
Requirements: Node >= 18.17 (global `fetch`). WHOIS uses TCP port 43.
## Install
@@ -8,25 +15,146 @@
npm install rdapper
```
## Usage
## Quick Start
```ts
import { lookupDomain } from "rdapper";
const { ok, record, error } = await lookupDomain("example.com", {
timeoutMs: 15000,
followWhoisReferral: true,
timeoutMs: 15000, // default 15000
followWhoisReferral: true, // default true
});
if (!ok) throw new Error(error);
console.log(record);
console.log(record); // normalized DomainRecord
```
## Notes
Also available:
- 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.
```ts
import { isRegistered, isAvailable } from "rdapper";
await isRegistered("example.com"); // => true
await isAvailable("likely-unregistered-thing-320485230458.com"); // => false
```
## API
- `lookupDomain(domain, options?) => Promise<LookupResult>`
- Tries RDAP first for the domains TLD; if unavailable or fails, falls back to WHOIS unless toggled off.
- Result is `{ ok: boolean, record?: DomainRecord, error?: string }`.
- `isRegistered(domain, options?) => Promise<boolean>`
- `isAvailable(domain, options?) => Promise<boolean>`
### Options
- `timeoutMs?: number` Total timeout budget per network operation (default `15000`).
- `rdapOnly?: boolean` Only attempt RDAP; do not fall back to WHOIS.
- `whoisOnly?: boolean` Skip RDAP and query WHOIS directly.
- `followWhoisReferral?: boolean` Follow registrar referral from the TLD WHOIS (default `true`).
- `customBootstrapUrl?: string` Override RDAP bootstrap URL.
- `whoisHints?: Record<string, string>` Override/add authoritative WHOIS per TLD (keys are lowercase TLDs, values may include or omit `whois://`).
- `signal?: AbortSignal` Optional cancellation signal.
### `DomainRecord` schema
The exact presence of fields depends on registry/registrar data and whether RDAP or WHOIS was used.
```ts
interface DomainRecord {
domain: string; // normalized name (unicode when available)
tld: string; // terminal TLD label (e.g., "com")
isRegistered: boolean; // availability heuristic (WHOIS) or true (RDAP)
isIDN?: boolean; // uses punycode labels (xn--)
unicodeName?: string; // RDAP unicodeName when provided
punycodeName?: string; // RDAP ldhName when provided
registry?: string; // registry operator (rarely available)
registrar?: {
name?: string;
ianaId?: string;
url?: string;
email?: string;
phone?: string;
};
reseller?: string;
statuses?: Array<{ status: string; description?: string; raw?: string }>;
creationDate?: string; // ISO 8601 (UTC)
updatedDate?: string; // ISO 8601 (UTC)
expirationDate?: string; // ISO 8601 (UTC)
deletionDate?: string; // ISO 8601 (UTC)
transferLock?: boolean; // derived from EPP statuses
dnssec?: {
enabled: boolean;
dsRecords?: Array<{ keyTag?: number; algorithm?: number; digestType?: number; digest?: string }>;
};
nameservers?: Array<{ host: string; ipv4?: string[]; ipv6?: string[] }>;
contacts?: Array<{
type: "registrant" | "admin" | "tech" | "billing" | "abuse" | "registrar" | "reseller" | "unknown";
name?: string; organization?: string; email?: string | string[]; phone?: string | string[]; fax?: string | string[];
street?: string[]; city?: string; state?: string; postalCode?: string; country?: string; countryCode?: string;
}>;
whoisServer?: string; // authoritative WHOIS queried (if any)
rdapServers?: string[]; // RDAP base URLs tried
rawRdap?: unknown; // raw RDAP JSON (RDAP source)
rawWhois?: string; // raw WHOIS text (WHOIS source)
source: "rdap" | "whois"; // which path produced data
fetchedAt: string; // ISO 8601 timestamp
warnings?: string[];
}
```
### Example output
```json
{
"domain": "example.com",
"tld": "com",
"isRegistered": true,
"registrar": { "name": "Internet Assigned Numbers Authority", "ianaId": "376" },
"statuses": [{ "status": "clientTransferProhibited" }],
"nameservers": [{ "host": "a.iana-servers.net" }, { "host": "b.iana-servers.net" }],
"dnssec": { "enabled": true },
"source": "rdap",
"fetchedAt": "2025-01-01T00:00:00Z"
}
```
## How it works
- RDAP
- Discovers base URLs for the TLD via IANAs RDAP bootstrap JSON.
- Tries each base until one responds successfully; parses standard RDAP domain JSON.
- Normalizes registrar (from `entities`), contacts (vCard), nameservers (`ipAddresses`), events (created/changed/expiration), statuses, and DNSSEC (`secureDNS`).
- WHOIS
- Discovers the authoritative TLD WHOIS via `whois.iana.org` (TCP 43), with curated exceptions for tricky zones and public SLDs.
- Queries the TLD WHOIS; if a registrar referral is present and `followWhoisReferral !== false`, follows one hop to the registrar WHOIS.
- Normalizes common key/value variants across gTLD/ccTLD formats (dates, statuses, nameservers, contacts). Availability is inferred from common phrases (besteffort heuristic).
Timeouts are enforced per request using a simple race against `timeoutMs` (default 15s). All network I/O is performed with global `fetch` (RDAP) and a raw TCP socket (WHOIS).
## Development
- Build: `npm run build`
- Test: `npm test`
- By default, tests are offline/deterministic.
- Smoke tests that hit the network are gated by `SMOKE=1`, e.g. `SMOKE=1 npm test`.
- Lint/format: `npm run lint` (Biome)
Project layout:
- `src/rdap/` RDAP bootstrap, client, and normalization
- `src/whois/` WHOIS TCP client, discovery/referral, normalization, exceptions
- `src/lib/` utilities for dates, text parsing, domain processing, async
- `src/types.ts` public types; `src/index.ts` reexports API and types
- `cli.mjs` local CLI helper for quick testing
## Caveats
- WHOIS text formats vary significantly across registries/registrars; normalization is besteffort.
- Availability detection relies on common WHOIS phrases and is not authoritative.
- Some TLDs provide no RDAP service; `rdapOnly: true` will fail for them.
- Registries may throttle or block WHOIS; respect rate limits and usage policies.
- Field presence depends on source and privacy policies (e.g., redaction/withholding).
## License

View File

@@ -87,7 +87,6 @@ export interface LookupOptions {
customBootstrapUrl?: string; // override IANA bootstrap
// WHOIS discovery and query tuning
whoisHints?: Record<string, string>; // override/add authoritative WHOIS per TLD
maxWhoisHops?: number; // max referral hops to follow (default 2)
signal?: AbortSignal;
}