@chez14/rdap-sdk 0.1.1-5

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2024 Gunawan "chez14" Christianto <chris@christianto.net>
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,133 @@
1
+ # RDAP.net SDK
2
+
3
+ A TypeScript SDK for RDAP.net. Directly calls RDAP APIs from RDAP.net as the
4
+ main RDAP Server Database.
5
+
6
+ Support RDAP.net/RDAP.org here: https://about.rdap.org/
7
+
8
+ References:
9
+ - API Usage: \
10
+ https://www.openrdap.org/api
11
+
12
+ ## Installation
13
+
14
+ ```bash
15
+ # Using pnpm
16
+ pnpm add @chez14/rdap-sdk
17
+
18
+ # Using npm
19
+ npm install @chez14/rdap-sdk
20
+
21
+ # Using yarn
22
+ yarn add @chez14/rdap-sdk
23
+ ```
24
+
25
+ ## Usage
26
+
27
+ ```typescript
28
+ import { RdapClient } from '@chez14/rdap-sdk';
29
+
30
+ // Create a client
31
+ const client = new RdapClient();
32
+
33
+ // Query domain information
34
+ const domain = await client.domain('example.com');
35
+ console.log(domain.ldhName);
36
+
37
+ // Query internationalized domain name (IDN)
38
+ const idn = await client.domain('münchen.de');
39
+ console.log(idn.ldhName); // Automatically converts to punycode
40
+
41
+ // Query IP information
42
+ const ip = await client.ip('8.8.8.8');
43
+ console.log(ip.name);
44
+
45
+ // Query IP network (CIDR notation)
46
+ const network = await client.ip('2001:db8::1/128');
47
+ console.log(network.startAddress, network.endAddress);
48
+
49
+ // Query ASN information
50
+ const asn = await client.autnum(174);
51
+ console.log(asn.name);
52
+
53
+ // Use custom base domain (default is www.rdap.net)
54
+ const customClient = new RdapOrgClient({
55
+ baseDomain: 'custom.rdap.example.com'
56
+ });
57
+ ```
58
+
59
+ ## Advanced Usage
60
+
61
+ ### Custom Fetcher Implementation
62
+
63
+ You can provide your own fetcher implementation to customize how HTTP requests
64
+ are made. This is useful for:
65
+ - Adding custom headers
66
+ - Implementing custom retry logic
67
+ - Using different HTTP clients
68
+ - Adding request/response interceptors
69
+ - Custom error handling
70
+
71
+ ```typescript
72
+ import { RdapClient, Fetcher } from '@chez14/rdap-sdk';
73
+
74
+ // Define your custom types
75
+ interface CustomHeaders {
76
+ Accept: string;
77
+ 'User-Agent': string;
78
+ }
79
+
80
+ // Implement a typed fetcher
81
+ class CustomFetcher implements Fetcher {
82
+ constructor(private userAgent: string) {}
83
+
84
+ // Your implementation here
85
+ }
86
+
87
+ // Use your custom fetcher
88
+ const client = new RdapOrgClient({
89
+ fetcher: new CustomFetcher('MyApp/1.0 (https://example.com)'),
90
+ });
91
+ ```
92
+
93
+ ### Type Safety
94
+
95
+ The SDK is fully typed and provides interfaces for customizing request behavior.
96
+ You can implement your own fetcher with custom types:
97
+
98
+ The SDK exports these key types for customization:
99
+ - `HttpMethod`: Supported HTTP methods ('GET' | 'POST' | 'PUT' | 'DELETE' |
100
+ 'PATCH')
101
+ - `FetchOptions`: Request configuration including method, URL, body, and headers
102
+ - `FetchResponse`: Standardized response format with status, data, and headers
103
+ - `Fetcher`: Interface for implementing custom HTTP clients
104
+
105
+ Response types:
106
+ - `DomainResponse`: Type for domain query responses
107
+ - `IpNetworkResponse`: Type for IP network query responses
108
+ - `AutonomousSystemResponse`: Type for ASN query responses
109
+
110
+ For type safety, we recommend:
111
+ 1. Define interfaces for your request and response data
112
+ 2. Use TypeScript's strict mode
113
+ 3. Avoid using `unknown` or `any` types in your implementations
114
+
115
+ ## Development
116
+
117
+ ```bash
118
+ # Install dependencies
119
+ pnpm install
120
+
121
+ # Build
122
+ pnpm build
123
+
124
+ # Run tests
125
+ pnpm test
126
+
127
+ # Lint
128
+ pnpm lint
129
+ ```
130
+
131
+ ## License
132
+
133
+ MIT
@@ -0,0 +1,139 @@
1
+ /**
2
+ * Base response interface for RDAP queries
3
+ */
4
+ interface RdapResponse {
5
+ rdapConformance: string[];
6
+ notices?: Array<{
7
+ title?: string;
8
+ description: string[];
9
+ links?: Array<{
10
+ value: string;
11
+ rel: string;
12
+ href: string;
13
+ type?: string;
14
+ }>;
15
+ }>;
16
+ }
17
+ /**
18
+ * Domain response interface
19
+ */
20
+ interface DomainResponse extends RdapResponse {
21
+ objectClassName: 'domain';
22
+ handle?: string;
23
+ ldhName: string;
24
+ links: Array<{
25
+ rel: string;
26
+ href: string;
27
+ type?: string;
28
+ }>;
29
+ }
30
+ /**
31
+ * IP Network response interface
32
+ */
33
+ interface IpNetworkResponse extends RdapResponse {
34
+ objectClassName: 'ip network';
35
+ handle: string;
36
+ startAddress: string;
37
+ endAddress: string;
38
+ ipVersion: string;
39
+ name?: string;
40
+ type?: string;
41
+ }
42
+ /**
43
+ * Autonomous System Number response interface
44
+ */
45
+ interface AutonomousSystemResponse extends RdapResponse {
46
+ objectClassName: 'autnum';
47
+ handle: string;
48
+ startAutnum: number;
49
+ endAutnum: number;
50
+ name?: string;
51
+ type?: string;
52
+ }
53
+
54
+ /**
55
+ * HTTP methods supported by the fetcher
56
+ */
57
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
58
+ /**
59
+ * Options for the fetch request
60
+ */
61
+ interface FetchOptions<T = unknown> {
62
+ method: HttpMethod;
63
+ url: string;
64
+ body?: T;
65
+ headers?: Record<string, string>;
66
+ }
67
+ /**
68
+ * Response from the fetch request
69
+ */
70
+ interface FetchResponse<T = unknown> {
71
+ status: number;
72
+ data: T;
73
+ headers: Headers;
74
+ }
75
+ /**
76
+ * Interface for the fetcher implementation
77
+ */
78
+ interface Fetcher {
79
+ fetch<TBody = unknown, TResponse = unknown>(options: FetchOptions<TBody>): Promise<FetchResponse<TResponse>>;
80
+ }
81
+ /**
82
+ * Default fetcher implementation using node-fetch
83
+ */
84
+ declare class DefaultFetcher implements Fetcher {
85
+ fetch<TBody = unknown, TResponse = unknown>({ method, url, body, headers, }: FetchOptions<TBody>): Promise<FetchResponse<TResponse>>;
86
+ }
87
+
88
+ interface RdapOrgClientOptions {
89
+ fetcher?: Fetcher;
90
+ baseDomain?: string;
91
+ }
92
+ declare class RdapClient {
93
+ private readonly fetcher;
94
+ private readonly baseDomain;
95
+ constructor(options?: RdapOrgClientOptions);
96
+ /**
97
+ * Convert domain to punycode if needed
98
+ */
99
+ private prepareDomain;
100
+ /**
101
+ * Query domain information
102
+ * @param domain Domain name to query (supports IDN/punycode)
103
+ * @returns Domain information
104
+ * @example
105
+ * ```typescript
106
+ * const client = new RdapOrgClient();
107
+ * const domain = await client.domain('example.com');
108
+ * // IDN support
109
+ * const idn = await client.domain('münchen.de');
110
+ * ```
111
+ */
112
+ domain(domain: string): Promise<DomainResponse>;
113
+ /**
114
+ * Query IP address or network information
115
+ * @param ip IP address or CIDR notation
116
+ * @returns IP network information
117
+ * @example
118
+ * ```typescript
119
+ * const client = new RdapOrgClient();
120
+ * const ip = await client.ip('8.8.8.8');
121
+ * // or with CIDR notation
122
+ * const network = await client.ip('2001:db8::1/128');
123
+ * ```
124
+ */
125
+ ip(ip: string): Promise<IpNetworkResponse>;
126
+ /**
127
+ * Query Autonomous System Number information
128
+ * @param asn Autonomous System Number
129
+ * @returns ASN information
130
+ * @example
131
+ * ```typescript
132
+ * const client = new RdapOrgClient();
133
+ * const asn = await client.autnum(174);
134
+ * ```
135
+ */
136
+ autnum(asn: number): Promise<AutonomousSystemResponse>;
137
+ }
138
+
139
+ export { type AutonomousSystemResponse, DefaultFetcher, type DomainResponse, type FetchOptions, type FetchResponse, type Fetcher, type HttpMethod, type IpNetworkResponse, RdapClient, type RdapOrgClientOptions, type RdapResponse };
package/dist/index.js ADDED
@@ -0,0 +1,118 @@
1
+ // src/utils/fetcher.ts
2
+ var DefaultFetcher = class {
3
+ async fetch({
4
+ method,
5
+ url,
6
+ body,
7
+ headers = {}
8
+ }) {
9
+ const requestUrl = new URL(url);
10
+ if (method === "GET" && body) {
11
+ Object.entries(body).forEach(([key, value]) => {
12
+ requestUrl.searchParams.append(key, value);
13
+ });
14
+ }
15
+ const requestInit = {
16
+ method,
17
+ headers: {
18
+ "Content-Type": "application/json",
19
+ ...headers
20
+ }
21
+ };
22
+ if (method !== "GET" && body) {
23
+ requestInit.body = JSON.stringify(body);
24
+ }
25
+ const response = await fetch(requestUrl, requestInit);
26
+ const data = await response.json();
27
+ return {
28
+ status: response.status,
29
+ data,
30
+ headers: response.headers
31
+ };
32
+ }
33
+ };
34
+
35
+ // src/client.ts
36
+ var RdapClient = class {
37
+ constructor(options = {}) {
38
+ this.fetcher = options.fetcher ?? new DefaultFetcher();
39
+ this.baseDomain = options.baseDomain ?? "www.rdap.net";
40
+ }
41
+ /**
42
+ * Convert domain to punycode if needed
43
+ */
44
+ prepareDomain(domain) {
45
+ return domain.toLowerCase().split(".").map((part) => {
46
+ try {
47
+ if (part.includes("xn--")) {
48
+ return part;
49
+ }
50
+ const url = new URL(`https://${part}`);
51
+ return url.hostname.startsWith("xn--") ? url.hostname : part;
52
+ } catch {
53
+ return part;
54
+ }
55
+ }).join(".");
56
+ }
57
+ /**
58
+ * Query domain information
59
+ * @param domain Domain name to query (supports IDN/punycode)
60
+ * @returns Domain information
61
+ * @example
62
+ * ```typescript
63
+ * const client = new RdapOrgClient();
64
+ * const domain = await client.domain('example.com');
65
+ * // IDN support
66
+ * const idn = await client.domain('münchen.de');
67
+ * ```
68
+ */
69
+ async domain(domain) {
70
+ const punyDomain = this.prepareDomain(domain);
71
+ const response = await this.fetcher.fetch({
72
+ method: "GET",
73
+ url: `https://${this.baseDomain}/domain/${encodeURIComponent(punyDomain)}`
74
+ });
75
+ return response.data;
76
+ }
77
+ /**
78
+ * Query IP address or network information
79
+ * @param ip IP address or CIDR notation
80
+ * @returns IP network information
81
+ * @example
82
+ * ```typescript
83
+ * const client = new RdapOrgClient();
84
+ * const ip = await client.ip('8.8.8.8');
85
+ * // or with CIDR notation
86
+ * const network = await client.ip('2001:db8::1/128');
87
+ * ```
88
+ */
89
+ async ip(ip) {
90
+ const response = await this.fetcher.fetch({
91
+ method: "GET",
92
+ url: `https://${this.baseDomain}/ip/${encodeURIComponent(ip)}`
93
+ });
94
+ return response.data;
95
+ }
96
+ /**
97
+ * Query Autonomous System Number information
98
+ * @param asn Autonomous System Number
99
+ * @returns ASN information
100
+ * @example
101
+ * ```typescript
102
+ * const client = new RdapOrgClient();
103
+ * const asn = await client.autnum(174);
104
+ * ```
105
+ */
106
+ async autnum(asn) {
107
+ const response = await this.fetcher.fetch({
108
+ method: "GET",
109
+ url: `https://${this.baseDomain}/autnum/${asn}`
110
+ });
111
+ return response.data;
112
+ }
113
+ };
114
+ export {
115
+ DefaultFetcher,
116
+ RdapClient
117
+ };
118
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/fetcher.ts","../src/client.ts"],"sourcesContent":["/**\n * HTTP methods supported by the fetcher\n */\nexport type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';\n\n/**\n * Options for the fetch request\n */\nexport interface FetchOptions<T = unknown> {\n method: HttpMethod;\n url: string;\n body?: T;\n headers?: Record<string, string>;\n}\n\n/**\n * Response from the fetch request\n */\nexport interface FetchResponse<T = unknown> {\n status: number;\n data: T;\n headers: Headers;\n}\n\n/**\n * Interface for the fetcher implementation\n */\nexport interface Fetcher {\n fetch<TBody = unknown, TResponse = unknown>(\n options: FetchOptions<TBody>,\n ): Promise<FetchResponse<TResponse>>;\n}\n\n/**\n * Default fetcher implementation using node-fetch\n */\nexport class DefaultFetcher implements Fetcher {\n async fetch<TBody = unknown, TResponse = unknown>({\n method,\n url,\n body,\n headers = {},\n }: FetchOptions<TBody>): Promise<FetchResponse<TResponse>> {\n const requestUrl = new URL(url);\n\n // For GET requests, convert body to query parameters\n if (method === 'GET' && body) {\n Object.entries(body as Record<string, string>).forEach(([key, value]) => {\n requestUrl.searchParams.append(key, value);\n });\n }\n\n const requestInit: RequestInit = {\n method,\n headers: {\n 'Content-Type': 'application/json',\n ...headers,\n },\n };\n\n // Add body for non-GET requests\n if (method !== 'GET' && body) {\n requestInit.body = JSON.stringify(body);\n }\n\n const response = await fetch(requestUrl, requestInit);\n const data = (await response.json()) as TResponse;\n\n return {\n status: response.status,\n data,\n headers: response.headers,\n };\n }\n}\n","import { AutonomousSystemResponse, DomainResponse, IpNetworkResponse } from './types';\nimport { DefaultFetcher, Fetcher } from './utils/fetcher';\n\nexport interface RdapOrgClientOptions {\n fetcher?: Fetcher;\n baseDomain?: string;\n}\n\nexport class RdapClient {\n private readonly fetcher: Fetcher;\n private readonly baseDomain: string;\n\n constructor(options: RdapOrgClientOptions = {}) {\n this.fetcher = options.fetcher ?? new DefaultFetcher();\n this.baseDomain = options.baseDomain ?? 'www.rdap.net';\n }\n\n /**\n * Convert domain to punycode if needed\n */\n private prepareDomain(domain: string): string {\n return domain\n .toLowerCase()\n .split('.')\n .map((part) => {\n try {\n if (part.includes('xn--')) {\n return part;\n }\n // Use URL API to handle IDN conversion\n const url = new URL(`https://${part}`);\n return url.hostname.startsWith('xn--') ? url.hostname : part;\n } catch {\n return part;\n }\n })\n .join('.');\n }\n\n /**\n * Query domain information\n * @param domain Domain name to query (supports IDN/punycode)\n * @returns Domain information\n * @example\n * ```typescript\n * const client = new RdapOrgClient();\n * const domain = await client.domain('example.com');\n * // IDN support\n * const idn = await client.domain('münchen.de');\n * ```\n */\n async domain(domain: string): Promise<DomainResponse> {\n const punyDomain = this.prepareDomain(domain);\n const response = await this.fetcher.fetch<never, DomainResponse>({\n method: 'GET',\n url: `https://${this.baseDomain}/domain/${encodeURIComponent(punyDomain)}`,\n });\n\n return response.data;\n }\n\n /**\n * Query IP address or network information\n * @param ip IP address or CIDR notation\n * @returns IP network information\n * @example\n * ```typescript\n * const client = new RdapOrgClient();\n * const ip = await client.ip('8.8.8.8');\n * // or with CIDR notation\n * const network = await client.ip('2001:db8::1/128');\n * ```\n */\n async ip(ip: string): Promise<IpNetworkResponse> {\n const response = await this.fetcher.fetch<never, IpNetworkResponse>({\n method: 'GET',\n url: `https://${this.baseDomain}/ip/${encodeURIComponent(ip)}`,\n });\n\n return response.data;\n }\n\n /**\n * Query Autonomous System Number information\n * @param asn Autonomous System Number\n * @returns ASN information\n * @example\n * ```typescript\n * const client = new RdapOrgClient();\n * const asn = await client.autnum(174);\n * ```\n */\n async autnum(asn: number): Promise<AutonomousSystemResponse> {\n const response = await this.fetcher.fetch<never, AutonomousSystemResponse>({\n method: 'GET',\n url: `https://${this.baseDomain}/autnum/${asn}`,\n });\n\n return response.data;\n }\n}\n"],"mappings":";AAoCO,IAAM,iBAAN,MAAwC;AAAA,EAC7C,MAAM,MAA4C;AAAA,IAChD;AAAA,IACA;AAAA,IACA;AAAA,IACA,UAAU,CAAC;AAAA,EACb,GAA2D;AACzD,UAAM,aAAa,IAAI,IAAI,GAAG;AAG9B,QAAI,WAAW,SAAS,MAAM;AAC5B,aAAO,QAAQ,IAA8B,EAAE,QAAQ,CAAC,CAAC,KAAK,KAAK,MAAM;AACvE,mBAAW,aAAa,OAAO,KAAK,KAAK;AAAA,MAC3C,CAAC;AAAA,IACH;AAEA,UAAM,cAA2B;AAAA,MAC/B;AAAA,MACA,SAAS;AAAA,QACP,gBAAgB;AAAA,QAChB,GAAG;AAAA,MACL;AAAA,IACF;AAGA,QAAI,WAAW,SAAS,MAAM;AAC5B,kBAAY,OAAO,KAAK,UAAU,IAAI;AAAA,IACxC;AAEA,UAAM,WAAW,MAAM,MAAM,YAAY,WAAW;AACpD,UAAM,OAAQ,MAAM,SAAS,KAAK;AAElC,WAAO;AAAA,MACL,QAAQ,SAAS;AAAA,MACjB;AAAA,MACA,SAAS,SAAS;AAAA,IACpB;AAAA,EACF;AACF;;;AClEO,IAAM,aAAN,MAAiB;AAAA,EAItB,YAAY,UAAgC,CAAC,GAAG;AAC9C,SAAK,UAAU,QAAQ,WAAW,IAAI,eAAe;AACrD,SAAK,aAAa,QAAQ,cAAc;AAAA,EAC1C;AAAA;AAAA;AAAA;AAAA,EAKQ,cAAc,QAAwB;AAC5C,WAAO,OACJ,YAAY,EACZ,MAAM,GAAG,EACT,IAAI,CAAC,SAAS;AACb,UAAI;AACF,YAAI,KAAK,SAAS,MAAM,GAAG;AACzB,iBAAO;AAAA,QACT;AAEA,cAAM,MAAM,IAAI,IAAI,WAAW,IAAI,EAAE;AACrC,eAAO,IAAI,SAAS,WAAW,MAAM,IAAI,IAAI,WAAW;AAAA,MAC1D,QAAQ;AACN,eAAO;AAAA,MACT;AAAA,IACF,CAAC,EACA,KAAK,GAAG;AAAA,EACb;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,OAAO,QAAyC;AACpD,UAAM,aAAa,KAAK,cAAc,MAAM;AAC5C,UAAM,WAAW,MAAM,KAAK,QAAQ,MAA6B;AAAA,MAC/D,QAAQ;AAAA,MACR,KAAK,WAAW,KAAK,UAAU,WAAW,mBAAmB,UAAU,CAAC;AAAA,IAC1E,CAAC;AAED,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAcA,MAAM,GAAG,IAAwC;AAC/C,UAAM,WAAW,MAAM,KAAK,QAAQ,MAAgC;AAAA,MAClE,QAAQ;AAAA,MACR,KAAK,WAAW,KAAK,UAAU,OAAO,mBAAmB,EAAE,CAAC;AAAA,IAC9D,CAAC;AAED,WAAO,SAAS;AAAA,EAClB;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA;AAAA,EAYA,MAAM,OAAO,KAAgD;AAC3D,UAAM,WAAW,MAAM,KAAK,QAAQ,MAAuC;AAAA,MACzE,QAAQ;AAAA,MACR,KAAK,WAAW,KAAK,UAAU,WAAW,GAAG;AAAA,IAC/C,CAAC;AAED,WAAO,SAAS;AAAA,EAClB;AACF;","names":[]}
package/package.json ADDED
@@ -0,0 +1,54 @@
1
+ {
2
+ "name": "@chez14/rdap-sdk",
3
+ "version": "0.1.1-5",
4
+ "description": "A TypeScript SDK for RDAP.net. Directly calls RDAP APIs from RDAP.net as the main RDAP Server Database.",
5
+ "repository": {
6
+ "type": "git",
7
+ "url": "git+https://gitlab.com/chez14/rdap-sdk-ts.git"
8
+ },
9
+ "type": "module",
10
+ "main": "dist/index.js",
11
+ "module": "dist/index.mjs",
12
+ "types": "dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "require": "./dist/index.js",
16
+ "import": "./dist/index.mjs",
17
+ "types": "./dist/index.d.ts"
18
+ }
19
+ },
20
+ "scripts": {
21
+ "build": "tsup",
22
+ "test": "vitest --no-watch",
23
+ "test:watch": "vitest --watch",
24
+ "lint": "eslint .",
25
+ "lint:fix": "eslint . --fix",
26
+ "format": "prettier --write ."
27
+ },
28
+ "keywords": [],
29
+ "author": "",
30
+ "license": "MIT",
31
+ "engines": {
32
+ "node": ">=16"
33
+ },
34
+ "devDependencies": {
35
+ "@eslint/compat": "^1.2.4",
36
+ "@eslint/eslintrc": "^3.2.0",
37
+ "@eslint/js": "^9.17.0",
38
+ "@types/node": "^22.10.2",
39
+ "@typescript-eslint/eslint-plugin": "^8.18.1",
40
+ "@typescript-eslint/parser": "^8.18.1",
41
+ "eslint": "^9.17.0",
42
+ "eslint-config-prettier": "^9.1.0",
43
+ "eslint-plugin-import": "^2.31.0",
44
+ "eslint-plugin-prettier": "^5.2.1",
45
+ "eslint-plugin-simple-import-sort": "^12.1.1",
46
+ "globals": "^15.14.0",
47
+ "prettier": "^3.4.2",
48
+ "tsup": "^8.3.5",
49
+ "typescript": "^5.7.2",
50
+ "vitest": "^2.1.8"
51
+ },
52
+ "dependencies": {},
53
+ "packageManager": "pnpm@9.15.1+sha256.9e534e70afef06374f6126b44bda5760947135ce16a30aef1010e965fb7e3e3e"
54
+ }
package/src/client.ts ADDED
@@ -0,0 +1,101 @@
1
+ import { AutonomousSystemResponse, DomainResponse, IpNetworkResponse } from './types';
2
+ import { DefaultFetcher, Fetcher } from './utils/fetcher';
3
+
4
+ export interface RdapOrgClientOptions {
5
+ fetcher?: Fetcher;
6
+ baseDomain?: string;
7
+ }
8
+
9
+ export class RdapClient {
10
+ private readonly fetcher: Fetcher;
11
+ private readonly baseDomain: string;
12
+
13
+ constructor(options: RdapOrgClientOptions = {}) {
14
+ this.fetcher = options.fetcher ?? new DefaultFetcher();
15
+ this.baseDomain = options.baseDomain ?? 'www.rdap.net';
16
+ }
17
+
18
+ /**
19
+ * Convert domain to punycode if needed
20
+ */
21
+ private prepareDomain(domain: string): string {
22
+ return domain
23
+ .toLowerCase()
24
+ .split('.')
25
+ .map((part) => {
26
+ try {
27
+ if (part.includes('xn--')) {
28
+ return part;
29
+ }
30
+ // Use URL API to handle IDN conversion
31
+ const url = new URL(`https://${part}`);
32
+ return url.hostname.startsWith('xn--') ? url.hostname : part;
33
+ } catch {
34
+ return part;
35
+ }
36
+ })
37
+ .join('.');
38
+ }
39
+
40
+ /**
41
+ * Query domain information
42
+ * @param domain Domain name to query (supports IDN/punycode)
43
+ * @returns Domain information
44
+ * @example
45
+ * ```typescript
46
+ * const client = new RdapOrgClient();
47
+ * const domain = await client.domain('example.com');
48
+ * // IDN support
49
+ * const idn = await client.domain('münchen.de');
50
+ * ```
51
+ */
52
+ async domain(domain: string): Promise<DomainResponse> {
53
+ const punyDomain = this.prepareDomain(domain);
54
+ const response = await this.fetcher.fetch<never, DomainResponse>({
55
+ method: 'GET',
56
+ url: `https://${this.baseDomain}/domain/${encodeURIComponent(punyDomain)}`,
57
+ });
58
+
59
+ return response.data;
60
+ }
61
+
62
+ /**
63
+ * Query IP address or network information
64
+ * @param ip IP address or CIDR notation
65
+ * @returns IP network information
66
+ * @example
67
+ * ```typescript
68
+ * const client = new RdapOrgClient();
69
+ * const ip = await client.ip('8.8.8.8');
70
+ * // or with CIDR notation
71
+ * const network = await client.ip('2001:db8::1/128');
72
+ * ```
73
+ */
74
+ async ip(ip: string): Promise<IpNetworkResponse> {
75
+ const response = await this.fetcher.fetch<never, IpNetworkResponse>({
76
+ method: 'GET',
77
+ url: `https://${this.baseDomain}/ip/${encodeURIComponent(ip)}`,
78
+ });
79
+
80
+ return response.data;
81
+ }
82
+
83
+ /**
84
+ * Query Autonomous System Number information
85
+ * @param asn Autonomous System Number
86
+ * @returns ASN information
87
+ * @example
88
+ * ```typescript
89
+ * const client = new RdapOrgClient();
90
+ * const asn = await client.autnum(174);
91
+ * ```
92
+ */
93
+ async autnum(asn: number): Promise<AutonomousSystemResponse> {
94
+ const response = await this.fetcher.fetch<never, AutonomousSystemResponse>({
95
+ method: 'GET',
96
+ url: `https://${this.baseDomain}/autnum/${asn}`,
97
+ });
98
+
99
+ return response.data;
100
+ }
101
+ }
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ export * from './client';
2
+ export * from './types';
3
+ export * from './utils/fetcher';
package/src/types.ts ADDED
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Base response interface for RDAP queries
3
+ */
4
+ export interface RdapResponse {
5
+ rdapConformance: string[];
6
+ notices?: Array<{
7
+ title?: string;
8
+ description: string[];
9
+ links?: Array<{
10
+ value: string;
11
+ rel: string;
12
+ href: string;
13
+ type?: string;
14
+ }>;
15
+ }>;
16
+ }
17
+
18
+ /**
19
+ * Domain response interface
20
+ */
21
+ export interface DomainResponse extends RdapResponse {
22
+ objectClassName: 'domain';
23
+ handle?: string;
24
+ ldhName: string;
25
+ links: Array<{
26
+ rel: string;
27
+ href: string;
28
+ type?: string;
29
+ }>;
30
+ }
31
+
32
+ /**
33
+ * IP Network response interface
34
+ */
35
+ export interface IpNetworkResponse extends RdapResponse {
36
+ objectClassName: 'ip network';
37
+ handle: string;
38
+ startAddress: string;
39
+ endAddress: string;
40
+ ipVersion: string;
41
+ name?: string;
42
+ type?: string;
43
+ }
44
+
45
+ /**
46
+ * Autonomous System Number response interface
47
+ */
48
+ export interface AutonomousSystemResponse extends RdapResponse {
49
+ objectClassName: 'autnum';
50
+ handle: string;
51
+ startAutnum: number;
52
+ endAutnum: number;
53
+ name?: string;
54
+ type?: string;
55
+ }
@@ -0,0 +1,75 @@
1
+ /**
2
+ * HTTP methods supported by the fetcher
3
+ */
4
+ export type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
5
+
6
+ /**
7
+ * Options for the fetch request
8
+ */
9
+ export interface FetchOptions<T = unknown> {
10
+ method: HttpMethod;
11
+ url: string;
12
+ body?: T;
13
+ headers?: Record<string, string>;
14
+ }
15
+
16
+ /**
17
+ * Response from the fetch request
18
+ */
19
+ export interface FetchResponse<T = unknown> {
20
+ status: number;
21
+ data: T;
22
+ headers: Headers;
23
+ }
24
+
25
+ /**
26
+ * Interface for the fetcher implementation
27
+ */
28
+ export interface Fetcher {
29
+ fetch<TBody = unknown, TResponse = unknown>(
30
+ options: FetchOptions<TBody>,
31
+ ): Promise<FetchResponse<TResponse>>;
32
+ }
33
+
34
+ /**
35
+ * Default fetcher implementation using node-fetch
36
+ */
37
+ export class DefaultFetcher implements Fetcher {
38
+ async fetch<TBody = unknown, TResponse = unknown>({
39
+ method,
40
+ url,
41
+ body,
42
+ headers = {},
43
+ }: FetchOptions<TBody>): Promise<FetchResponse<TResponse>> {
44
+ const requestUrl = new URL(url);
45
+
46
+ // For GET requests, convert body to query parameters
47
+ if (method === 'GET' && body) {
48
+ Object.entries(body as Record<string, string>).forEach(([key, value]) => {
49
+ requestUrl.searchParams.append(key, value);
50
+ });
51
+ }
52
+
53
+ const requestInit: RequestInit = {
54
+ method,
55
+ headers: {
56
+ 'Content-Type': 'application/json',
57
+ ...headers,
58
+ },
59
+ };
60
+
61
+ // Add body for non-GET requests
62
+ if (method !== 'GET' && body) {
63
+ requestInit.body = JSON.stringify(body);
64
+ }
65
+
66
+ const response = await fetch(requestUrl, requestInit);
67
+ const data = (await response.json()) as TResponse;
68
+
69
+ return {
70
+ status: response.status,
71
+ data,
72
+ headers: response.headers,
73
+ };
74
+ }
75
+ }