@chez14/rdap-sdk 0.1.1-13

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 RdapClient({
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 RdapClient({
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,250 @@
1
+ interface RdapLink {
2
+ value: string;
3
+ rel: string;
4
+ href: string;
5
+ hreflang?: string[];
6
+ type?: string;
7
+ media?: string;
8
+ title?: string;
9
+ }
10
+ interface RdapEvent {
11
+ eventAction: string;
12
+ eventActor?: string;
13
+ eventDate: string;
14
+ links?: RdapLink[];
15
+ }
16
+ interface RdapPublicID {
17
+ type: string;
18
+ identifier: string;
19
+ }
20
+ type RdapStatus = 'validated' | 'renew prohibited' | 'update prohibited' | 'transfer prohibited' | 'delete prohibited' | 'proxy' | 'private' | 'removed' | 'obscured' | 'associated' | 'active' | 'inactive' | 'locked' | 'pending create' | 'pending renew' | 'pending transfer' | 'pending update' | 'pending delete';
21
+ type RdapNoticeAndRemarkTypes = 'result set truncated due to authorization' | 'result set truncated due to excessive load' | 'result set truncated due to unexplainable reasons' | 'object truncated due to authorization' | 'object truncated due to excessive load' | 'object truncated due to unexplainable reasons';
22
+ interface RdapRemark {
23
+ title?: string;
24
+ type?: RdapNoticeAndRemarkTypes;
25
+ description: string[];
26
+ links?: RdapLink[];
27
+ }
28
+ interface RdapDsData {
29
+ keyTag?: number;
30
+ algorithm?: number;
31
+ digest?: string;
32
+ digestType?: string;
33
+ events?: RdapEvent[];
34
+ links?: RdapLink[];
35
+ }
36
+ interface RdapKeyData {
37
+ flags?: number;
38
+ protocol?: number;
39
+ publicKey?: string;
40
+ algorithm?: number;
41
+ events?: RdapEvent[];
42
+ links?: RdapLink[];
43
+ }
44
+ interface RdapSecureDNS {
45
+ zoneSigned?: boolean;
46
+ delegationSigned?: boolean;
47
+ maxSigLife?: number;
48
+ dsData?: RdapDsData[];
49
+ keyData?: RdapKeyData[];
50
+ }
51
+ type RdapNotice = RdapRemark;
52
+ type RdapObjectClass = RdapEntityObjectClass | RdapNameserverObjectClass | RdapDomainObjectClass;
53
+ interface RdapEntityObjectClass {
54
+ objectClassName: 'entity';
55
+ handle?: string;
56
+ vcardArray?: any[];
57
+ roles?: string[];
58
+ publicIds?: RdapPublicID[];
59
+ entities?: RdapEntityObjectClass[];
60
+ remarks?: RdapRemark[];
61
+ links?: RdapLink[];
62
+ events?: RdapEvent[];
63
+ asEventActor?: Omit<RdapEvent, 'eventActor'>[];
64
+ status?: RdapStatus[];
65
+ port43?: string;
66
+ networks?: RdapIPNetworkObjectClass[];
67
+ autnums?: RdapAutonomousSystemNumberObjectClass[];
68
+ }
69
+ interface RdapNameserverObjectClass {
70
+ objectClassName: 'nameserver';
71
+ handle?: string;
72
+ ldhName?: string;
73
+ unicodeName?: string;
74
+ ipAddresses?: {
75
+ v6: string[];
76
+ v4: string[];
77
+ };
78
+ entities?: RdapEntityObjectClass[];
79
+ status?: RdapStatus[];
80
+ remarks?: RdapRemark[];
81
+ links?: RdapLink[];
82
+ port43?: string;
83
+ events?: RdapEvent[];
84
+ }
85
+ interface RdapDomainObjectClass {
86
+ objectClassName: 'domain';
87
+ handle?: string;
88
+ ldhName?: string;
89
+ unicodeName?: string;
90
+ variants: {
91
+ relation: string[];
92
+ idnTable: string;
93
+ variantName: {
94
+ ldhName: string;
95
+ unicodeName: string;
96
+ }[];
97
+ }[];
98
+ nameservers: RdapNameserverObjectClass[];
99
+ secureDNS: RdapSecureDNS;
100
+ entities?: RdapEntityObjectClass[];
101
+ status?: RdapStatus[];
102
+ publicIds?: RdapPublicID[];
103
+ remarks: RdapRemark[];
104
+ links?: RdapLink[];
105
+ port43?: string;
106
+ events?: RdapEvent[];
107
+ network?: RdapIPNetworkObjectClass;
108
+ }
109
+ interface RdapIPNetworkObjectClass {
110
+ objectClassName: 'ip network';
111
+ handle?: string;
112
+ startAddress?: string;
113
+ endAddress?: string;
114
+ ipVersion?: 'v4' | 'v6';
115
+ name?: string;
116
+ type?: string;
117
+ country?: string;
118
+ parentHandle?: string;
119
+ status?: RdapStatus[];
120
+ entities?: RdapEntityObjectClass[];
121
+ remarks: RdapRemark[];
122
+ links?: RdapLink[];
123
+ port43?: string;
124
+ events?: RdapEvent[];
125
+ }
126
+ interface RdapAutonomousSystemNumberObjectClass {
127
+ objectClassName: 'autnum';
128
+ handle?: string;
129
+ startAutnum?: string;
130
+ endAutnum?: string;
131
+ name?: string;
132
+ type?: string;
133
+ status?: RdapStatus[];
134
+ country?: string;
135
+ entities?: RdapEntityObjectClass[];
136
+ remarks: RdapRemark[];
137
+ links?: RdapLink[];
138
+ port43?: string;
139
+ events?: RdapEvent[];
140
+ }
141
+ interface RdapDomainSuccessResponse extends RdapDomainObjectClass {
142
+ rdapConformance: string[];
143
+ }
144
+ interface RdapIpSuccessResponse extends RdapIPNetworkObjectClass {
145
+ rdapConformance: string[];
146
+ }
147
+ interface RdapAutnumSuccessResponse extends RdapAutonomousSystemNumberObjectClass {
148
+ rdapConformance: string[];
149
+ }
150
+ interface RdapHelpSuccessResponse {
151
+ rdapConformance: string[];
152
+ notices?: RdapNotice[];
153
+ }
154
+ interface RdapErrorResponse {
155
+ errorCode: number;
156
+ title?: string;
157
+ description?: string[];
158
+ }
159
+ type RdapDomainResponse = RdapErrorResponse & RdapDomainSuccessResponse;
160
+ type RdapIpResponse = RdapErrorResponse & RdapIpSuccessResponse;
161
+ type RdapAutnumResponse = RdapErrorResponse & RdapAutnumSuccessResponse;
162
+ type RdapHelpResponse = RdapErrorResponse & RdapHelpSuccessResponse;
163
+
164
+ /**
165
+ * HTTP methods supported by the fetcher
166
+ */
167
+ type HttpMethod = 'GET' | 'POST' | 'PUT' | 'DELETE' | 'PATCH';
168
+ /**
169
+ * Options for the fetch request
170
+ */
171
+ interface FetchOptions<T = unknown> {
172
+ method: HttpMethod;
173
+ url: string;
174
+ body?: T;
175
+ headers?: Record<string, string>;
176
+ }
177
+ /**
178
+ * Response from the fetch request
179
+ */
180
+ interface FetchResponse<T = unknown> {
181
+ status: number;
182
+ data: T;
183
+ headers: Headers;
184
+ url: string;
185
+ }
186
+ /**
187
+ * Interface for the fetcher implementation
188
+ */
189
+ interface Fetcher {
190
+ fetch<TBody = unknown, TResponse = unknown>(options: FetchOptions<TBody>): Promise<FetchResponse<TResponse>>;
191
+ }
192
+ /**
193
+ * Default fetcher implementation using node-fetch
194
+ */
195
+ declare class DefaultFetcher implements Fetcher {
196
+ fetch<TBody = unknown, TResponse = unknown>({ method, url, body, headers, }: FetchOptions<TBody>): Promise<FetchResponse<TResponse>>;
197
+ }
198
+
199
+ interface RdapClientOptions {
200
+ fetcher?: Fetcher;
201
+ baseDomain?: string;
202
+ }
203
+ declare class RdapClient {
204
+ private readonly fetcher;
205
+ private readonly baseDomain;
206
+ constructor(options?: RdapClientOptions);
207
+ /**
208
+ * Convert domain to punycode if needed
209
+ */
210
+ private prepareDomain;
211
+ /**
212
+ * Query domain information
213
+ * @param domain Domain name to query (supports IDN/punycode)
214
+ * @returns Domain information
215
+ * @example
216
+ * ```typescript
217
+ * const client = new RdapClient();
218
+ * const domain = await client.domain('example.com');
219
+ * // IDN support
220
+ * const idn = await client.domain('münchen.de');
221
+ * ```
222
+ */
223
+ domain(domain: string): Promise<RdapDomainResponse>;
224
+ /**
225
+ * Query IP address or network information
226
+ * @param ip IP address or CIDR notation
227
+ * @returns IP network information
228
+ * @example
229
+ * ```typescript
230
+ * const client = new RdapClient();
231
+ * const ip = await client.ip('8.8.8.8');
232
+ * // or with CIDR notation
233
+ * const network = await client.ip('2001:db8::1/128');
234
+ * ```
235
+ */
236
+ ip(ip: string): Promise<RdapIpResponse>;
237
+ /**
238
+ * Query Autonomous System Number information
239
+ * @param asn Autonomous System Number
240
+ * @returns ASN information
241
+ * @example
242
+ * ```typescript
243
+ * const client = new RdapClient();
244
+ * const asn = await client.autnum(174);
245
+ * ```
246
+ */
247
+ autnum(asn: number): Promise<RdapAutnumResponse>;
248
+ }
249
+
250
+ export { DefaultFetcher, type FetchOptions, type FetchResponse, type Fetcher, type HttpMethod, type RdapAutnumResponse, type RdapAutnumSuccessResponse, type RdapAutonomousSystemNumberObjectClass, RdapClient, type RdapClientOptions, type RdapDomainObjectClass, type RdapDomainResponse, type RdapDomainSuccessResponse, type RdapDsData, type RdapEntityObjectClass, type RdapErrorResponse, type RdapEvent, type RdapHelpResponse, type RdapHelpSuccessResponse, type RdapIPNetworkObjectClass, type RdapIpResponse, type RdapIpSuccessResponse, type RdapKeyData, type RdapLink, type RdapNameserverObjectClass, type RdapNotice, type RdapNoticeAndRemarkTypes, type RdapObjectClass, type RdapPublicID, type RdapRemark, type RdapSecureDNS, type RdapStatus };
package/dist/index.js ADDED
@@ -0,0 +1,139 @@
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
+ url: response.url
32
+ };
33
+ }
34
+ };
35
+
36
+ // src/utils/throwables.ts
37
+ var RdapNotFoundError = class extends Error {
38
+ constructor(message) {
39
+ super(message);
40
+ this.name = "RdapNotFoundError";
41
+ }
42
+ };
43
+
44
+ // src/client.ts
45
+ var RdapClient = class {
46
+ constructor(options = {}) {
47
+ this.fetcher = options.fetcher ?? new DefaultFetcher();
48
+ this.baseDomain = options.baseDomain ?? "www.rdap.net";
49
+ }
50
+ /**
51
+ * Convert domain to punycode if needed
52
+ */
53
+ prepareDomain(domain) {
54
+ return domain.toLowerCase().split(".").map((part) => {
55
+ try {
56
+ if (part.includes("xn--")) {
57
+ return part;
58
+ }
59
+ const url = new URL(`https://${part}`);
60
+ return url.hostname.startsWith("xn--") ? url.hostname : part;
61
+ } catch {
62
+ return part;
63
+ }
64
+ }).join(".");
65
+ }
66
+ /**
67
+ * Query domain information
68
+ * @param domain Domain name to query (supports IDN/punycode)
69
+ * @returns Domain information
70
+ * @example
71
+ * ```typescript
72
+ * const client = new RdapClient();
73
+ * const domain = await client.domain('example.com');
74
+ * // IDN support
75
+ * const idn = await client.domain('münchen.de');
76
+ * ```
77
+ */
78
+ async domain(domain) {
79
+ const punyDomain = this.prepareDomain(domain);
80
+ const url = `https://${this.baseDomain}/domain/${encodeURIComponent(punyDomain)}`;
81
+ try {
82
+ const response = await this.fetcher.fetch({
83
+ method: "GET",
84
+ url
85
+ });
86
+ if (response.status === 404 && response.url === url) {
87
+ throw new RdapNotFoundError(`Domain ${domain} has no RDAP server for its TLD`);
88
+ }
89
+ return response.data;
90
+ } catch (error) {
91
+ if (error instanceof SyntaxError) {
92
+ throw new RdapNotFoundError(`Domain ${domain} has no RDAP server for its TLD`);
93
+ } else {
94
+ throw error;
95
+ }
96
+ }
97
+ }
98
+ /**
99
+ * Query IP address or network information
100
+ * @param ip IP address or CIDR notation
101
+ * @returns IP network information
102
+ * @example
103
+ * ```typescript
104
+ * const client = new RdapClient();
105
+ * const ip = await client.ip('8.8.8.8');
106
+ * // or with CIDR notation
107
+ * const network = await client.ip('2001:db8::1/128');
108
+ * ```
109
+ */
110
+ async ip(ip) {
111
+ const response = await this.fetcher.fetch({
112
+ method: "GET",
113
+ url: `https://${this.baseDomain}/ip/${encodeURIComponent(ip)}`
114
+ });
115
+ return response.data;
116
+ }
117
+ /**
118
+ * Query Autonomous System Number information
119
+ * @param asn Autonomous System Number
120
+ * @returns ASN information
121
+ * @example
122
+ * ```typescript
123
+ * const client = new RdapClient();
124
+ * const asn = await client.autnum(174);
125
+ * ```
126
+ */
127
+ async autnum(asn) {
128
+ const response = await this.fetcher.fetch({
129
+ method: "GET",
130
+ url: `https://${this.baseDomain}/autnum/${asn}`
131
+ });
132
+ return response.data;
133
+ }
134
+ };
135
+ export {
136
+ DefaultFetcher,
137
+ RdapClient
138
+ };
139
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../src/utils/fetcher.ts","../src/utils/throwables.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 url: string;\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 url: response.url,\n };\n }\n}\n","export class RdapNotFoundError extends Error {\n constructor(message: string) {\n super(message);\n this.name = 'RdapNotFoundError';\n }\n}\n","import { RdapAutnumResponse, RdapDomainResponse, RdapIpResponse } from './types';\nimport { DefaultFetcher, Fetcher } from './utils/fetcher';\nimport { RdapNotFoundError } from './utils/throwables';\n\nexport interface RdapClientOptions {\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: RdapClientOptions = {}) {\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 RdapClient();\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<RdapDomainResponse> {\n const punyDomain = this.prepareDomain(domain);\n const url = `https://${this.baseDomain}/domain/${encodeURIComponent(punyDomain)}`;\n try {\n const response = await this.fetcher.fetch<never, RdapDomainResponse>({\n method: 'GET',\n url,\n });\n\n if (response.status === 404 && response.url === url) {\n throw new RdapNotFoundError(`Domain ${domain} has no RDAP server for its TLD`);\n }\n\n return response.data;\n } catch (error) {\n if (error instanceof SyntaxError) {\n throw new RdapNotFoundError(`Domain ${domain} has no RDAP server for its TLD`);\n } else {\n throw error;\n }\n }\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 RdapClient();\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<RdapIpResponse> {\n const response = await this.fetcher.fetch<never, RdapIpResponse>({\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 RdapClient();\n * const asn = await client.autnum(174);\n * ```\n */\n async autnum(asn: number): Promise<RdapAutnumResponse> {\n const response = await this.fetcher.fetch<never, RdapAutnumResponse>({\n method: 'GET',\n url: `https://${this.baseDomain}/autnum/${asn}`,\n });\n\n return response.data;\n }\n}\n"],"mappings":"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","names":[]}
package/package.json ADDED
@@ -0,0 +1,57 @@
1
+ {
2
+ "name": "@chez14/rdap-sdk",
3
+ "version": "0.1.1-13",
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.js",
12
+ "types": "dist/index.d.ts",
13
+ "exports": {
14
+ ".": {
15
+ "types": "./dist/index.d.ts",
16
+ "default": "./dist/index.js"
17
+ },
18
+ "./ts/*": "./src/*"
19
+ },
20
+ "keywords": [],
21
+ "author": "",
22
+ "license": "MIT",
23
+ "engines": {
24
+ "node": ">=16"
25
+ },
26
+ "devDependencies": {
27
+ "@eslint/compat": "^1.2.4",
28
+ "@eslint/eslintrc": "^3.2.0",
29
+ "@eslint/js": "^9.17.0",
30
+ "@types/node": "^22.10.2",
31
+ "@typescript-eslint/eslint-plugin": "^8.18.1",
32
+ "@typescript-eslint/parser": "^8.18.1",
33
+ "eslint": "^9.17.0",
34
+ "eslint-config-prettier": "^9.1.0",
35
+ "eslint-plugin-import": "^2.31.0",
36
+ "eslint-plugin-prettier": "^5.2.1",
37
+ "eslint-plugin-simple-import-sort": "^12.1.1",
38
+ "globals": "^15.14.0",
39
+ "prettier": "^3.4.2",
40
+ "tsup": "^8.3.5",
41
+ "typescript": "^5.7.2",
42
+ "vitest": "^2.1.8"
43
+ },
44
+ "dependencies": {},
45
+ "publishConfig": {
46
+ "provenance": true,
47
+ "access": "public"
48
+ },
49
+ "scripts": {
50
+ "build": "tsup",
51
+ "test": "vitest --no-watch",
52
+ "test:watch": "vitest --watch",
53
+ "lint": "eslint .",
54
+ "lint:fix": "eslint . --fix",
55
+ "format": "prettier --write ."
56
+ }
57
+ }
package/src/client.ts ADDED
@@ -0,0 +1,115 @@
1
+ import { RdapAutnumResponse, RdapDomainResponse, RdapIpResponse } from './types';
2
+ import { DefaultFetcher, Fetcher } from './utils/fetcher';
3
+ import { RdapNotFoundError } from './utils/throwables';
4
+
5
+ export interface RdapClientOptions {
6
+ fetcher?: Fetcher;
7
+ baseDomain?: string;
8
+ }
9
+
10
+ export class RdapClient {
11
+ private readonly fetcher: Fetcher;
12
+ private readonly baseDomain: string;
13
+
14
+ constructor(options: RdapClientOptions = {}) {
15
+ this.fetcher = options.fetcher ?? new DefaultFetcher();
16
+ this.baseDomain = options.baseDomain ?? 'www.rdap.net';
17
+ }
18
+
19
+ /**
20
+ * Convert domain to punycode if needed
21
+ */
22
+ private prepareDomain(domain: string): string {
23
+ return domain
24
+ .toLowerCase()
25
+ .split('.')
26
+ .map((part) => {
27
+ try {
28
+ if (part.includes('xn--')) {
29
+ return part;
30
+ }
31
+ // Use URL API to handle IDN conversion
32
+ const url = new URL(`https://${part}`);
33
+ return url.hostname.startsWith('xn--') ? url.hostname : part;
34
+ } catch {
35
+ return part;
36
+ }
37
+ })
38
+ .join('.');
39
+ }
40
+
41
+ /**
42
+ * Query domain information
43
+ * @param domain Domain name to query (supports IDN/punycode)
44
+ * @returns Domain information
45
+ * @example
46
+ * ```typescript
47
+ * const client = new RdapClient();
48
+ * const domain = await client.domain('example.com');
49
+ * // IDN support
50
+ * const idn = await client.domain('münchen.de');
51
+ * ```
52
+ */
53
+ async domain(domain: string): Promise<RdapDomainResponse> {
54
+ const punyDomain = this.prepareDomain(domain);
55
+ const url = `https://${this.baseDomain}/domain/${encodeURIComponent(punyDomain)}`;
56
+ try {
57
+ const response = await this.fetcher.fetch<never, RdapDomainResponse>({
58
+ method: 'GET',
59
+ url,
60
+ });
61
+
62
+ if (response.status === 404 && response.url === url) {
63
+ throw new RdapNotFoundError(`Domain ${domain} has no RDAP server for its TLD`);
64
+ }
65
+
66
+ return response.data;
67
+ } catch (error) {
68
+ if (error instanceof SyntaxError) {
69
+ throw new RdapNotFoundError(`Domain ${domain} has no RDAP server for its TLD`);
70
+ } else {
71
+ throw error;
72
+ }
73
+ }
74
+ }
75
+
76
+ /**
77
+ * Query IP address or network information
78
+ * @param ip IP address or CIDR notation
79
+ * @returns IP network information
80
+ * @example
81
+ * ```typescript
82
+ * const client = new RdapClient();
83
+ * const ip = await client.ip('8.8.8.8');
84
+ * // or with CIDR notation
85
+ * const network = await client.ip('2001:db8::1/128');
86
+ * ```
87
+ */
88
+ async ip(ip: string): Promise<RdapIpResponse> {
89
+ const response = await this.fetcher.fetch<never, RdapIpResponse>({
90
+ method: 'GET',
91
+ url: `https://${this.baseDomain}/ip/${encodeURIComponent(ip)}`,
92
+ });
93
+
94
+ return response.data;
95
+ }
96
+
97
+ /**
98
+ * Query Autonomous System Number information
99
+ * @param asn Autonomous System Number
100
+ * @returns ASN information
101
+ * @example
102
+ * ```typescript
103
+ * const client = new RdapClient();
104
+ * const asn = await client.autnum(174);
105
+ * ```
106
+ */
107
+ async autnum(asn: number): Promise<RdapAutnumResponse> {
108
+ const response = await this.fetcher.fetch<never, RdapAutnumResponse>({
109
+ method: 'GET',
110
+ url: `https://${this.baseDomain}/autnum/${asn}`,
111
+ });
112
+
113
+ return response.data;
114
+ }
115
+ }
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,216 @@
1
+ /*
2
+ * These files were copied directly from
3
+ * https://github.com/dcoffey-zengenti/node-rdap/blob/aa294812f01730f3ce5f1c384fb92cfbfeaf846f/src/types.ts.
4
+ */
5
+
6
+ export interface RdapLink {
7
+ value: string;
8
+ rel: string;
9
+ href: string;
10
+ hreflang?: string[];
11
+ type?: string;
12
+ media?: string;
13
+ title?: string;
14
+ }
15
+
16
+ export interface RdapEvent {
17
+ eventAction: string;
18
+ eventActor?: string;
19
+ eventDate: string;
20
+ links?: RdapLink[];
21
+ }
22
+
23
+ export interface RdapPublicID {
24
+ type: string;
25
+ identifier: string;
26
+ }
27
+
28
+ export type RdapStatus =
29
+ | 'validated'
30
+ | 'renew prohibited'
31
+ | 'update prohibited'
32
+ | 'transfer prohibited'
33
+ | 'delete prohibited'
34
+ | 'proxy'
35
+ | 'private'
36
+ | 'removed'
37
+ | 'obscured'
38
+ | 'associated'
39
+ | 'active'
40
+ | 'inactive'
41
+ | 'locked'
42
+ | 'pending create'
43
+ | 'pending renew'
44
+ | 'pending transfer'
45
+ | 'pending update'
46
+ | 'pending delete';
47
+
48
+ export type RdapNoticeAndRemarkTypes =
49
+ | 'result set truncated due to authorization'
50
+ | 'result set truncated due to excessive load'
51
+ | 'result set truncated due to unexplainable reasons'
52
+ | 'object truncated due to authorization'
53
+ | 'object truncated due to excessive load'
54
+ | 'object truncated due to unexplainable reasons';
55
+
56
+ export interface RdapRemark {
57
+ title?: string;
58
+ type?: RdapNoticeAndRemarkTypes;
59
+ description: string[];
60
+ links?: RdapLink[];
61
+ }
62
+
63
+ export interface RdapDsData {
64
+ keyTag?: number;
65
+ algorithm?: number;
66
+ digest?: string;
67
+ digestType?: string;
68
+ events?: RdapEvent[];
69
+ links?: RdapLink[];
70
+ }
71
+
72
+ export interface RdapKeyData {
73
+ flags?: number;
74
+ protocol?: number;
75
+ publicKey?: string;
76
+ algorithm?: number;
77
+ events?: RdapEvent[];
78
+ links?: RdapLink[];
79
+ }
80
+
81
+ export interface RdapSecureDNS {
82
+ zoneSigned?: boolean;
83
+ delegationSigned?: boolean;
84
+ maxSigLife?: number;
85
+ dsData?: RdapDsData[];
86
+ keyData?: RdapKeyData[];
87
+ }
88
+
89
+ export type RdapNotice = RdapRemark;
90
+
91
+ export type RdapObjectClass =
92
+ | RdapEntityObjectClass
93
+ | RdapNameserverObjectClass
94
+ | RdapDomainObjectClass;
95
+
96
+ export interface RdapEntityObjectClass {
97
+ objectClassName: 'entity';
98
+ handle?: string;
99
+ // eslint-disable-next-line @typescript-eslint/no-explicit-any
100
+ vcardArray?: any[];
101
+ roles?: string[];
102
+ publicIds?: RdapPublicID[];
103
+ entities?: RdapEntityObjectClass[];
104
+ remarks?: RdapRemark[];
105
+ links?: RdapLink[];
106
+ events?: RdapEvent[];
107
+ asEventActor?: Omit<RdapEvent, 'eventActor'>[];
108
+ status?: RdapStatus[];
109
+ port43?: string;
110
+ networks?: RdapIPNetworkObjectClass[];
111
+ autnums?: RdapAutonomousSystemNumberObjectClass[];
112
+ }
113
+
114
+ export interface RdapNameserverObjectClass {
115
+ objectClassName: 'nameserver';
116
+ handle?: string;
117
+ ldhName?: string;
118
+ unicodeName?: string;
119
+ ipAddresses?: {
120
+ v6: string[];
121
+ v4: string[];
122
+ };
123
+ entities?: RdapEntityObjectClass[];
124
+ status?: RdapStatus[];
125
+ remarks?: RdapRemark[];
126
+ links?: RdapLink[];
127
+ port43?: string;
128
+ events?: RdapEvent[];
129
+ }
130
+
131
+ export interface RdapDomainObjectClass {
132
+ objectClassName: 'domain';
133
+ handle?: string;
134
+ ldhName?: string;
135
+ unicodeName?: string;
136
+ variants: {
137
+ relation: string[];
138
+ idnTable: string;
139
+ variantName: {
140
+ ldhName: string;
141
+ unicodeName: string;
142
+ }[];
143
+ }[];
144
+ nameservers: RdapNameserverObjectClass[];
145
+ secureDNS: RdapSecureDNS;
146
+ entities?: RdapEntityObjectClass[];
147
+ status?: RdapStatus[];
148
+ publicIds?: RdapPublicID[];
149
+ remarks: RdapRemark[];
150
+ links?: RdapLink[];
151
+ port43?: string;
152
+ events?: RdapEvent[];
153
+ network?: RdapIPNetworkObjectClass;
154
+ }
155
+
156
+ export interface RdapIPNetworkObjectClass {
157
+ objectClassName: 'ip network';
158
+ handle?: string;
159
+ startAddress?: string;
160
+ endAddress?: string;
161
+ ipVersion?: 'v4' | 'v6';
162
+ name?: string;
163
+ type?: string;
164
+ country?: string;
165
+ parentHandle?: string;
166
+ status?: RdapStatus[];
167
+ entities?: RdapEntityObjectClass[];
168
+ remarks: RdapRemark[];
169
+ links?: RdapLink[];
170
+ port43?: string;
171
+ events?: RdapEvent[];
172
+ }
173
+
174
+ export interface RdapAutonomousSystemNumberObjectClass {
175
+ objectClassName: 'autnum';
176
+ handle?: string;
177
+ startAutnum?: string;
178
+ endAutnum?: string;
179
+ name?: string;
180
+ type?: string;
181
+ status?: RdapStatus[];
182
+ country?: string;
183
+ entities?: RdapEntityObjectClass[];
184
+ remarks: RdapRemark[];
185
+ links?: RdapLink[];
186
+ port43?: string;
187
+ events?: RdapEvent[];
188
+ }
189
+
190
+ export interface RdapDomainSuccessResponse extends RdapDomainObjectClass {
191
+ rdapConformance: string[];
192
+ }
193
+
194
+ export interface RdapIpSuccessResponse extends RdapIPNetworkObjectClass {
195
+ rdapConformance: string[];
196
+ }
197
+
198
+ export interface RdapAutnumSuccessResponse extends RdapAutonomousSystemNumberObjectClass {
199
+ rdapConformance: string[];
200
+ }
201
+
202
+ export interface RdapHelpSuccessResponse {
203
+ rdapConformance: string[];
204
+ notices?: RdapNotice[];
205
+ }
206
+
207
+ export interface RdapErrorResponse {
208
+ errorCode: number;
209
+ title?: string;
210
+ description?: string[];
211
+ }
212
+
213
+ export type RdapDomainResponse = RdapErrorResponse & RdapDomainSuccessResponse;
214
+ export type RdapIpResponse = RdapErrorResponse & RdapIpSuccessResponse;
215
+ export type RdapAutnumResponse = RdapErrorResponse & RdapAutnumSuccessResponse;
216
+ export type RdapHelpResponse = RdapErrorResponse & RdapHelpSuccessResponse;
@@ -0,0 +1,77 @@
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
+ url: string;
24
+ }
25
+
26
+ /**
27
+ * Interface for the fetcher implementation
28
+ */
29
+ export interface Fetcher {
30
+ fetch<TBody = unknown, TResponse = unknown>(
31
+ options: FetchOptions<TBody>,
32
+ ): Promise<FetchResponse<TResponse>>;
33
+ }
34
+
35
+ /**
36
+ * Default fetcher implementation using node-fetch
37
+ */
38
+ export class DefaultFetcher implements Fetcher {
39
+ async fetch<TBody = unknown, TResponse = unknown>({
40
+ method,
41
+ url,
42
+ body,
43
+ headers = {},
44
+ }: FetchOptions<TBody>): Promise<FetchResponse<TResponse>> {
45
+ const requestUrl = new URL(url);
46
+
47
+ // For GET requests, convert body to query parameters
48
+ if (method === 'GET' && body) {
49
+ Object.entries(body as Record<string, string>).forEach(([key, value]) => {
50
+ requestUrl.searchParams.append(key, value);
51
+ });
52
+ }
53
+
54
+ const requestInit: RequestInit = {
55
+ method,
56
+ headers: {
57
+ 'Content-Type': 'application/json',
58
+ ...headers,
59
+ },
60
+ };
61
+
62
+ // Add body for non-GET requests
63
+ if (method !== 'GET' && body) {
64
+ requestInit.body = JSON.stringify(body);
65
+ }
66
+
67
+ const response = await fetch(requestUrl, requestInit);
68
+ const data = (await response.json()) as TResponse;
69
+
70
+ return {
71
+ status: response.status,
72
+ data,
73
+ headers: response.headers,
74
+ url: response.url,
75
+ };
76
+ }
77
+ }
@@ -0,0 +1,6 @@
1
+ export class RdapNotFoundError extends Error {
2
+ constructor(message: string) {
3
+ super(message);
4
+ this.name = 'RdapNotFoundError';
5
+ }
6
+ }