domain-search-mcp 1.0.0

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.
Files changed (151) hide show
  1. package/.env.example +52 -0
  2. package/Dockerfile +15 -0
  3. package/LICENSE +21 -0
  4. package/README.md +426 -0
  5. package/SECURITY.md +252 -0
  6. package/dist/config.d.ts +25 -0
  7. package/dist/config.d.ts.map +1 -0
  8. package/dist/config.js +117 -0
  9. package/dist/config.js.map +1 -0
  10. package/dist/fallbacks/index.d.ts +6 -0
  11. package/dist/fallbacks/index.d.ts.map +1 -0
  12. package/dist/fallbacks/index.js +14 -0
  13. package/dist/fallbacks/index.js.map +1 -0
  14. package/dist/fallbacks/rdap.d.ts +18 -0
  15. package/dist/fallbacks/rdap.d.ts.map +1 -0
  16. package/dist/fallbacks/rdap.js +339 -0
  17. package/dist/fallbacks/rdap.js.map +1 -0
  18. package/dist/fallbacks/whois.d.ts +27 -0
  19. package/dist/fallbacks/whois.d.ts.map +1 -0
  20. package/dist/fallbacks/whois.js +219 -0
  21. package/dist/fallbacks/whois.js.map +1 -0
  22. package/dist/registrars/base.d.ts +89 -0
  23. package/dist/registrars/base.d.ts.map +1 -0
  24. package/dist/registrars/base.js +203 -0
  25. package/dist/registrars/base.js.map +1 -0
  26. package/dist/registrars/index.d.ts +7 -0
  27. package/dist/registrars/index.d.ts.map +1 -0
  28. package/dist/registrars/index.js +15 -0
  29. package/dist/registrars/index.js.map +1 -0
  30. package/dist/registrars/namecheap.d.ts +69 -0
  31. package/dist/registrars/namecheap.d.ts.map +1 -0
  32. package/dist/registrars/namecheap.js +307 -0
  33. package/dist/registrars/namecheap.js.map +1 -0
  34. package/dist/registrars/porkbun.d.ts +63 -0
  35. package/dist/registrars/porkbun.d.ts.map +1 -0
  36. package/dist/registrars/porkbun.js +299 -0
  37. package/dist/registrars/porkbun.js.map +1 -0
  38. package/dist/server.d.ts +19 -0
  39. package/dist/server.d.ts.map +1 -0
  40. package/dist/server.js +209 -0
  41. package/dist/server.js.map +1 -0
  42. package/dist/services/domain-search.d.ts +40 -0
  43. package/dist/services/domain-search.d.ts.map +1 -0
  44. package/dist/services/domain-search.js +438 -0
  45. package/dist/services/domain-search.js.map +1 -0
  46. package/dist/services/index.d.ts +5 -0
  47. package/dist/services/index.d.ts.map +1 -0
  48. package/dist/services/index.js +11 -0
  49. package/dist/services/index.js.map +1 -0
  50. package/dist/tools/bulk_search.d.ts +72 -0
  51. package/dist/tools/bulk_search.d.ts.map +1 -0
  52. package/dist/tools/bulk_search.js +108 -0
  53. package/dist/tools/bulk_search.js.map +1 -0
  54. package/dist/tools/check_socials.d.ts +71 -0
  55. package/dist/tools/check_socials.d.ts.map +1 -0
  56. package/dist/tools/check_socials.js +357 -0
  57. package/dist/tools/check_socials.js.map +1 -0
  58. package/dist/tools/compare_registrars.d.ts +80 -0
  59. package/dist/tools/compare_registrars.d.ts.map +1 -0
  60. package/dist/tools/compare_registrars.js +116 -0
  61. package/dist/tools/compare_registrars.js.map +1 -0
  62. package/dist/tools/index.d.ts +10 -0
  63. package/dist/tools/index.d.ts.map +1 -0
  64. package/dist/tools/index.js +31 -0
  65. package/dist/tools/index.js.map +1 -0
  66. package/dist/tools/search_domain.d.ts +61 -0
  67. package/dist/tools/search_domain.d.ts.map +1 -0
  68. package/dist/tools/search_domain.js +81 -0
  69. package/dist/tools/search_domain.js.map +1 -0
  70. package/dist/tools/suggest_domains.d.ts +82 -0
  71. package/dist/tools/suggest_domains.d.ts.map +1 -0
  72. package/dist/tools/suggest_domains.js +227 -0
  73. package/dist/tools/suggest_domains.js.map +1 -0
  74. package/dist/tools/tld_info.d.ts +56 -0
  75. package/dist/tools/tld_info.d.ts.map +1 -0
  76. package/dist/tools/tld_info.js +273 -0
  77. package/dist/tools/tld_info.js.map +1 -0
  78. package/dist/types.d.ts +193 -0
  79. package/dist/types.d.ts.map +1 -0
  80. package/dist/types.js +9 -0
  81. package/dist/types.js.map +1 -0
  82. package/dist/utils/cache.d.ts +81 -0
  83. package/dist/utils/cache.d.ts.map +1 -0
  84. package/dist/utils/cache.js +192 -0
  85. package/dist/utils/cache.js.map +1 -0
  86. package/dist/utils/errors.d.ts +87 -0
  87. package/dist/utils/errors.d.ts.map +1 -0
  88. package/dist/utils/errors.js +191 -0
  89. package/dist/utils/errors.js.map +1 -0
  90. package/dist/utils/index.d.ts +8 -0
  91. package/dist/utils/index.d.ts.map +1 -0
  92. package/dist/utils/index.js +24 -0
  93. package/dist/utils/index.js.map +1 -0
  94. package/dist/utils/logger.d.ts +27 -0
  95. package/dist/utils/logger.d.ts.map +1 -0
  96. package/dist/utils/logger.js +132 -0
  97. package/dist/utils/logger.js.map +1 -0
  98. package/dist/utils/premium-analyzer.d.ts +33 -0
  99. package/dist/utils/premium-analyzer.d.ts.map +1 -0
  100. package/dist/utils/premium-analyzer.js +273 -0
  101. package/dist/utils/premium-analyzer.js.map +1 -0
  102. package/dist/utils/validators.d.ts +53 -0
  103. package/dist/utils/validators.d.ts.map +1 -0
  104. package/dist/utils/validators.js +159 -0
  105. package/dist/utils/validators.js.map +1 -0
  106. package/docs/marketing/devto-post.md +135 -0
  107. package/docs/marketing/hackernews.md +42 -0
  108. package/docs/marketing/producthunt.md +109 -0
  109. package/docs/marketing/reddit-post.md +59 -0
  110. package/docs/marketing/twitter-thread.md +105 -0
  111. package/examples/bulk-search-50-domains.ts +131 -0
  112. package/examples/cli-interactive.ts +280 -0
  113. package/examples/compare-registrars.ts +78 -0
  114. package/examples/search-single-domain.ts +54 -0
  115. package/examples/suggest-names.ts +110 -0
  116. package/glama.json +6 -0
  117. package/jest.config.js +35 -0
  118. package/package.json +62 -0
  119. package/smithery.yaml +36 -0
  120. package/src/config.ts +121 -0
  121. package/src/fallbacks/index.ts +6 -0
  122. package/src/fallbacks/rdap.ts +407 -0
  123. package/src/fallbacks/whois.ts +250 -0
  124. package/src/registrars/base.ts +264 -0
  125. package/src/registrars/index.ts +7 -0
  126. package/src/registrars/namecheap.ts +378 -0
  127. package/src/registrars/porkbun.ts +380 -0
  128. package/src/server.ts +276 -0
  129. package/src/services/domain-search.ts +567 -0
  130. package/src/services/index.ts +9 -0
  131. package/src/tools/bulk_search.ts +142 -0
  132. package/src/tools/check_socials.ts +467 -0
  133. package/src/tools/compare_registrars.ts +162 -0
  134. package/src/tools/index.ts +45 -0
  135. package/src/tools/search_domain.ts +93 -0
  136. package/src/tools/suggest_domains.ts +284 -0
  137. package/src/tools/tld_info.ts +294 -0
  138. package/src/types.ts +289 -0
  139. package/src/utils/cache.ts +238 -0
  140. package/src/utils/errors.ts +262 -0
  141. package/src/utils/index.ts +8 -0
  142. package/src/utils/logger.ts +162 -0
  143. package/src/utils/premium-analyzer.ts +303 -0
  144. package/src/utils/validators.ts +193 -0
  145. package/tests/premium-analyzer.test.ts +310 -0
  146. package/tests/unit/cache.test.ts +123 -0
  147. package/tests/unit/errors.test.ts +190 -0
  148. package/tests/unit/tld-info.test.ts +62 -0
  149. package/tests/unit/tools.test.ts +200 -0
  150. package/tests/unit/validators.test.ts +146 -0
  151. package/tsconfig.json +25 -0
@@ -0,0 +1,250 @@
1
+ /**
2
+ * WHOIS Fallback (RFC 3912).
3
+ *
4
+ * Legacy protocol for domain lookup.
5
+ * Public, no authentication required.
6
+ * Slower than RDAP - use as last resort.
7
+ *
8
+ * Note: We use a public WHOIS API to avoid raw TCP connections
9
+ * which aren't well-supported in all Node.js environments.
10
+ */
11
+
12
+ import axios, { type AxiosError } from 'axios';
13
+ import type { DomainResult } from '../types.js';
14
+ import { logger } from '../utils/logger.js';
15
+ import { TimeoutError, RegistrarApiError } from '../utils/errors.js';
16
+
17
+ /**
18
+ * WHOIS server mappings for common TLDs.
19
+ */
20
+ const WHOIS_SERVERS: Record<string, string> = {
21
+ com: 'whois.verisign-grs.com',
22
+ net: 'whois.verisign-grs.com',
23
+ org: 'whois.pir.org',
24
+ io: 'whois.nic.io',
25
+ dev: 'whois.nic.google',
26
+ app: 'whois.nic.google',
27
+ co: 'whois.nic.co',
28
+ ai: 'whois.nic.ai',
29
+ me: 'whois.nic.me',
30
+ cc: 'ccwhois.verisign-grs.com',
31
+ xyz: 'whois.nic.xyz',
32
+ sh: 'whois.nic.sh',
33
+ };
34
+
35
+ /**
36
+ * Patterns that indicate a domain is NOT available.
37
+ */
38
+ const REGISTERED_PATTERNS = [
39
+ /domain name:/i,
40
+ /registrant:/i,
41
+ /creation date:/i,
42
+ /name server:/i,
43
+ /status:\s*(?:active|ok|registered)/i,
44
+ ];
45
+
46
+ /**
47
+ * Patterns that indicate a domain IS available.
48
+ */
49
+ const AVAILABLE_PATTERNS = [
50
+ /no match/i,
51
+ /not found/i,
52
+ /no data found/i,
53
+ /no entries found/i,
54
+ /no object found/i,
55
+ /domain not found/i,
56
+ /no whois server/i,
57
+ /available for registration/i,
58
+ /is free/i,
59
+ /status:\s*free/i,
60
+ ];
61
+
62
+ /**
63
+ * Parse WHOIS response to determine availability.
64
+ */
65
+ function parseWhoisResponse(response: string): boolean {
66
+ const text = response.toLowerCase();
67
+
68
+ // Check for "available" patterns first
69
+ for (const pattern of AVAILABLE_PATTERNS) {
70
+ if (pattern.test(text)) {
71
+ return true;
72
+ }
73
+ }
74
+
75
+ // Check for "registered" patterns
76
+ for (const pattern of REGISTERED_PATTERNS) {
77
+ if (pattern.test(text)) {
78
+ return false;
79
+ }
80
+ }
81
+
82
+ // If no clear indication, assume not available (safer)
83
+ return false;
84
+ }
85
+
86
+ /**
87
+ * Check domain availability using a public WHOIS API.
88
+ *
89
+ * We use a web-based WHOIS lookup to avoid TCP connection issues.
90
+ * This is more reliable across different environments.
91
+ */
92
+ export async function checkWhois(
93
+ domain: string,
94
+ tld: string,
95
+ ): Promise<DomainResult> {
96
+ const fullDomain = `${domain}.${tld}`;
97
+ logger.debug('WHOIS check', { domain: fullDomain });
98
+
99
+ // Use a public WHOIS API service
100
+ // There are several options; we'll try a few
101
+ const apis = [
102
+ {
103
+ url: `https://whoisjson.com/api/v1/whois`,
104
+ params: { domain: fullDomain },
105
+ parser: (data: Record<string, unknown>) => {
106
+ // If we get a result, domain is registered
107
+ if (data.domain_name || data.registrar) {
108
+ return false;
109
+ }
110
+ // If error or no data, domain might be available
111
+ return true;
112
+ },
113
+ },
114
+ ];
115
+
116
+ // Try each API in order
117
+ for (const api of apis) {
118
+ try {
119
+ const response = await axios.get(api.url, {
120
+ params: api.params,
121
+ timeout: 15000,
122
+ headers: {
123
+ Accept: 'application/json',
124
+ },
125
+ validateStatus: () => true, // Don't throw on any status
126
+ });
127
+
128
+ if (response.status === 200 && response.data) {
129
+ // Try to parse the response
130
+ let available: boolean;
131
+
132
+ if (typeof response.data === 'string') {
133
+ available = parseWhoisResponse(response.data);
134
+ } else {
135
+ available = api.parser(response.data as Record<string, unknown>);
136
+ }
137
+
138
+ return createWhoisResult(domain, tld, available);
139
+ }
140
+ } catch (error) {
141
+ logger.debug('WHOIS API failed, trying next', {
142
+ api: api.url,
143
+ error: error instanceof Error ? error.message : String(error),
144
+ });
145
+ }
146
+ }
147
+
148
+ // If all APIs fail, try a simple text-based WHOIS lookup
149
+ try {
150
+ const available = await textBasedWhoisCheck(fullDomain, tld);
151
+ return createWhoisResult(domain, tld, available);
152
+ } catch (error) {
153
+ if (error instanceof Error && error.message.includes('timeout')) {
154
+ throw new TimeoutError('WHOIS lookup', 15000);
155
+ }
156
+
157
+ throw new RegistrarApiError(
158
+ 'whois',
159
+ error instanceof Error ? error.message : 'All WHOIS lookups failed',
160
+ );
161
+ }
162
+ }
163
+
164
+ /**
165
+ * Simple text-based WHOIS check using a web proxy.
166
+ */
167
+ async function textBasedWhoisCheck(
168
+ fullDomain: string,
169
+ tld: string,
170
+ ): Promise<boolean> {
171
+ // Try who.is web service
172
+ try {
173
+ const response = await axios.get(`https://who.is/whois/${fullDomain}`, {
174
+ timeout: 15000,
175
+ headers: {
176
+ 'User-Agent': 'Domain-Search-MCP/1.0',
177
+ },
178
+ });
179
+
180
+ const html = response.data as string;
181
+
182
+ // Check for "not registered" indicators in the page
183
+ if (
184
+ html.includes('is available for registration') ||
185
+ html.includes('No match for') ||
186
+ html.includes('not found')
187
+ ) {
188
+ return true;
189
+ }
190
+
191
+ // Check for registered indicators
192
+ if (
193
+ html.includes('Registrar:') ||
194
+ html.includes('Creation Date:') ||
195
+ html.includes('Name Server:')
196
+ ) {
197
+ return false;
198
+ }
199
+
200
+ // Default to not available
201
+ return false;
202
+ } catch (error) {
203
+ if (axios.isAxiosError(error)) {
204
+ const axiosError = error as AxiosError;
205
+
206
+ if (axiosError.code === 'ECONNABORTED') {
207
+ throw new Error('timeout');
208
+ }
209
+ }
210
+
211
+ throw error;
212
+ }
213
+ }
214
+
215
+ /**
216
+ * Create a standardized result from WHOIS.
217
+ */
218
+ function createWhoisResult(
219
+ domain: string,
220
+ tld: string,
221
+ available: boolean,
222
+ ): DomainResult {
223
+ return {
224
+ domain: `${domain}.${tld}`,
225
+ available,
226
+ premium: false, // WHOIS doesn't tell us about premium status
227
+ price_first_year: null, // WHOIS doesn't provide pricing
228
+ price_renewal: null,
229
+ currency: 'USD',
230
+ privacy_included: false,
231
+ transfer_price: null,
232
+ registrar: 'unknown',
233
+ source: 'whois',
234
+ checked_at: new Date().toISOString(),
235
+ };
236
+ }
237
+
238
+ /**
239
+ * Get WHOIS server for a TLD.
240
+ */
241
+ export function getWhoisServer(tld: string): string | null {
242
+ return WHOIS_SERVERS[tld] || null;
243
+ }
244
+
245
+ /**
246
+ * Check if WHOIS is available for a TLD.
247
+ */
248
+ export function isWhoisAvailable(tld: string): boolean {
249
+ return WHOIS_SERVERS[tld] !== undefined;
250
+ }
@@ -0,0 +1,264 @@
1
+ /**
2
+ * Base Registrar Adapter.
3
+ *
4
+ * Abstract class that all registrar adapters extend.
5
+ * Provides common functionality:
6
+ * - Rate limiting (token bucket)
7
+ * - Retry with exponential backoff
8
+ * - Error handling and logging
9
+ */
10
+
11
+ import type { DomainResult, TLDInfo } from '../types.js';
12
+ import { logger } from '../utils/logger.js';
13
+ import {
14
+ RateLimitError,
15
+ RegistrarApiError,
16
+ TimeoutError,
17
+ wrapError,
18
+ } from '../utils/errors.js';
19
+ import { config } from '../config.js';
20
+
21
+ /**
22
+ * Token bucket rate limiter.
23
+ */
24
+ export class RateLimiter {
25
+ private tokens: number;
26
+ private readonly maxTokens: number;
27
+ private readonly refillRate: number; // tokens per second
28
+ private lastRefill: number;
29
+
30
+ constructor(maxPerMinute: number = 60) {
31
+ this.maxTokens = maxPerMinute;
32
+ this.tokens = maxPerMinute;
33
+ this.refillRate = maxPerMinute / 60; // Convert to per-second
34
+ this.lastRefill = Date.now();
35
+ }
36
+
37
+ /**
38
+ * Try to consume a token. Returns true if successful.
39
+ */
40
+ tryConsume(): boolean {
41
+ this.refill();
42
+
43
+ if (this.tokens >= 1) {
44
+ this.tokens -= 1;
45
+ return true;
46
+ }
47
+
48
+ return false;
49
+ }
50
+
51
+ /**
52
+ * Wait until a token is available.
53
+ */
54
+ async waitForToken(): Promise<void> {
55
+ while (!this.tryConsume()) {
56
+ // Calculate wait time for next token
57
+ const waitMs = Math.ceil(1000 / this.refillRate);
58
+ await sleep(waitMs);
59
+ }
60
+ }
61
+
62
+ /**
63
+ * Refill tokens based on elapsed time.
64
+ */
65
+ private refill(): void {
66
+ const now = Date.now();
67
+ const elapsed = (now - this.lastRefill) / 1000;
68
+ const refillAmount = elapsed * this.refillRate;
69
+
70
+ this.tokens = Math.min(this.maxTokens, this.tokens + refillAmount);
71
+ this.lastRefill = now;
72
+ }
73
+
74
+ /**
75
+ * Get seconds until next token is available.
76
+ */
77
+ getWaitSeconds(): number {
78
+ if (this.tokens >= 1) return 0;
79
+ const neededTokens = 1 - this.tokens;
80
+ return Math.ceil(neededTokens / this.refillRate);
81
+ }
82
+ }
83
+
84
+ /**
85
+ * Sleep helper.
86
+ */
87
+ function sleep(ms: number): Promise<void> {
88
+ return new Promise((resolve) => setTimeout(resolve, ms));
89
+ }
90
+
91
+ /**
92
+ * Abstract base class for registrar adapters.
93
+ */
94
+ export abstract class RegistrarAdapter {
95
+ /** Human-readable name of the registrar */
96
+ abstract readonly name: string;
97
+
98
+ /** Identifier used in results */
99
+ abstract readonly id: string;
100
+
101
+ /** Rate limiter for this registrar */
102
+ protected readonly rateLimiter: RateLimiter;
103
+
104
+ /** Max retry attempts */
105
+ protected readonly maxRetries: number = 3;
106
+
107
+ /** Base delay for exponential backoff (ms) */
108
+ protected readonly baseDelayMs: number = 2000;
109
+
110
+ /** Request timeout (ms) */
111
+ protected readonly timeoutMs: number = 10000;
112
+
113
+ constructor(requestsPerMinute: number = 60) {
114
+ this.rateLimiter = new RateLimiter(requestsPerMinute);
115
+ }
116
+
117
+ /**
118
+ * Check domain availability and get pricing.
119
+ * This is the main method each adapter must implement.
120
+ */
121
+ abstract search(domain: string, tld: string): Promise<DomainResult>;
122
+
123
+ /**
124
+ * Get information about a TLD.
125
+ * Optional - not all registrars provide this.
126
+ */
127
+ abstract getTldInfo(tld: string): Promise<TLDInfo | null>;
128
+
129
+ /**
130
+ * Check if this adapter is enabled (has required credentials).
131
+ */
132
+ abstract isEnabled(): boolean;
133
+
134
+ /**
135
+ * Execute a function with rate limiting.
136
+ */
137
+ protected async rateLimitedCall<T>(fn: () => Promise<T>): Promise<T> {
138
+ // Check if we should even try (dry run mode)
139
+ if (config.dryRun) {
140
+ throw new RegistrarApiError(this.name, 'Dry run mode - no API calls made');
141
+ }
142
+
143
+ // Wait for rate limit
144
+ const waitSeconds = this.rateLimiter.getWaitSeconds();
145
+ if (waitSeconds > 0) {
146
+ logger.debug(`Rate limiting: waiting ${waitSeconds}s for ${this.name}`);
147
+ }
148
+ await this.rateLimiter.waitForToken();
149
+
150
+ return fn();
151
+ }
152
+
153
+ /**
154
+ * Execute with retry and exponential backoff.
155
+ */
156
+ protected async retryWithBackoff<T>(
157
+ fn: () => Promise<T>,
158
+ operation: string,
159
+ ): Promise<T> {
160
+ let lastError: Error | undefined;
161
+
162
+ for (let attempt = 1; attempt <= this.maxRetries; attempt++) {
163
+ try {
164
+ return await this.rateLimitedCall(fn);
165
+ } catch (error) {
166
+ lastError = error instanceof Error ? error : new Error(String(error));
167
+
168
+ // Check if we should retry
169
+ const wrapped = wrapError(error);
170
+ if (!wrapped.retryable) {
171
+ throw wrapped;
172
+ }
173
+
174
+ // Check if it's a rate limit error with retry-after
175
+ if (error instanceof RateLimitError && error.retryAfter) {
176
+ const waitMs = error.retryAfter - Date.now();
177
+ if (waitMs > 0 && waitMs < 60000) {
178
+ logger.info(`Rate limited, waiting ${waitMs}ms`, {
179
+ registrar: this.name,
180
+ attempt,
181
+ });
182
+ await sleep(waitMs);
183
+ continue;
184
+ }
185
+ }
186
+
187
+ // Exponential backoff
188
+ if (attempt < this.maxRetries) {
189
+ const delay = this.baseDelayMs * Math.pow(2, attempt - 1);
190
+ logger.warn(`Retry ${attempt}/${this.maxRetries} for ${operation}`, {
191
+ registrar: this.name,
192
+ delay_ms: delay,
193
+ error: lastError.message,
194
+ });
195
+ await sleep(delay);
196
+ }
197
+ }
198
+ }
199
+
200
+ // All retries failed
201
+ throw lastError || new Error(`Failed after ${this.maxRetries} retries`);
202
+ }
203
+
204
+ /**
205
+ * Create a timeout wrapper for a promise.
206
+ */
207
+ protected withTimeout<T>(
208
+ promise: Promise<T>,
209
+ operation: string,
210
+ timeoutMs: number = this.timeoutMs,
211
+ ): Promise<T> {
212
+ return new Promise((resolve, reject) => {
213
+ const timer = setTimeout(() => {
214
+ reject(new TimeoutError(operation, timeoutMs));
215
+ }, timeoutMs);
216
+
217
+ promise
218
+ .then((result) => {
219
+ clearTimeout(timer);
220
+ resolve(result);
221
+ })
222
+ .catch((error) => {
223
+ clearTimeout(timer);
224
+ reject(error);
225
+ });
226
+ });
227
+ }
228
+
229
+ /**
230
+ * Log an error with context.
231
+ */
232
+ protected logError(error: Error, context?: Record<string, unknown>): void {
233
+ logger.logError(`${this.name} error`, error, {
234
+ registrar: this.id,
235
+ ...context,
236
+ });
237
+ }
238
+
239
+ /**
240
+ * Create a standardized DomainResult.
241
+ */
242
+ protected createResult(
243
+ domain: string,
244
+ tld: string,
245
+ data: Partial<DomainResult>,
246
+ ): DomainResult {
247
+ return {
248
+ domain: `${domain}.${tld}`,
249
+ available: data.available ?? false,
250
+ premium: data.premium ?? false,
251
+ price_first_year: data.price_first_year ?? null,
252
+ price_renewal: data.price_renewal ?? null,
253
+ currency: data.currency ?? 'USD',
254
+ privacy_included: data.privacy_included ?? false,
255
+ transfer_price: data.transfer_price ?? null,
256
+ registrar: this.id,
257
+ source: data.source ?? (`${this.id}_api` as DomainResult['source']),
258
+ checked_at: new Date().toISOString(),
259
+ premium_reason: data.premium_reason,
260
+ tld_restrictions: data.tld_restrictions,
261
+ score: data.score,
262
+ };
263
+ }
264
+ }
@@ -0,0 +1,7 @@
1
+ /**
2
+ * Registrar Exports.
3
+ */
4
+
5
+ export { RegistrarAdapter } from './base.js';
6
+ export { PorkbunAdapter, porkbunAdapter } from './porkbun.js';
7
+ export { NamecheapAdapter, namecheapAdapter } from './namecheap.js';