@startanaicompany/dns 1.3.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.
@@ -0,0 +1,754 @@
1
+ 'use strict';
2
+
3
+ const NAMESILO_BASE = 'https://www.namesilo.com/api';
4
+
5
+ // ─── Custom Error ──────────────────────────────────────────────────────────
6
+ class NameSiloError extends Error {
7
+ constructor(detail, code) {
8
+ super(detail || 'NameSilo API error');
9
+ this.name = 'NameSiloError';
10
+ this.code = code;
11
+ this.detail = detail;
12
+ }
13
+ }
14
+
15
+ // ─── Success codes ─────────────────────────────────────────────────────────
16
+ const SUCCESS_CODES = new Set([300, 301, 302]);
17
+
18
+ // ─── Core request function ─────────────────────────────────────────────────
19
+ async function call(operation, params = {}) {
20
+ const key = process.env.NAMESILO_API_KEY;
21
+
22
+ // Use mock mode when: NAMESILO_MOCK=true explicitly set, OR no API key configured
23
+ if (process.env.NAMESILO_MOCK === 'true' || !key) {
24
+ return mockResponse(operation, params);
25
+ }
26
+
27
+ const url = new URL(`${NAMESILO_BASE}/${operation}`);
28
+ url.searchParams.set('version', '1');
29
+ url.searchParams.set('type', 'json');
30
+ url.searchParams.set('key', key);
31
+
32
+ Object.entries(params).forEach(([k, v]) => {
33
+ if (v !== undefined && v !== null) {
34
+ url.searchParams.set(k, String(v));
35
+ }
36
+ });
37
+
38
+ const res = await fetch(url.toString());
39
+
40
+ if (!res.ok) {
41
+ throw new NameSiloError(`HTTP ${res.status}: ${res.statusText}`, res.status);
42
+ }
43
+
44
+ let json;
45
+ try {
46
+ json = await res.json();
47
+ } catch (e) {
48
+ throw new NameSiloError('Invalid JSON response from NameSilo', 0);
49
+ }
50
+
51
+ const reply = json.reply || json;
52
+ const code = parseInt(reply.code, 10);
53
+ const detail = reply.detail || 'Unknown error';
54
+
55
+ if (SUCCESS_CODES.has(code)) {
56
+ return reply;
57
+ }
58
+
59
+ // Map known error codes to descriptive messages
60
+ const errorMessages = {
61
+ 110: 'Invalid API key',
62
+ 108: 'Missing required parameters',
63
+ 200: 'Domain not active or not owned by this account',
64
+ 261: 'Domain not available for registration',
65
+ 280: 'Insufficient account funds',
66
+ 101: 'Operation failed',
67
+ 102: 'Invalid input',
68
+ 103: 'Authorization failed',
69
+ 104: 'Domain locked',
70
+ 105: 'Domain already exists',
71
+ 106: 'Domain transfer prohibited',
72
+ 107: 'Domain not eligible for renewal',
73
+ 109: 'Invalid domain name',
74
+ 111: 'Invalid contact ID',
75
+ 112: 'Invalid nameserver',
76
+ 113: 'Invalid years value',
77
+ 114: 'Domain already private',
78
+ 115: 'Domain already public',
79
+ 116: 'Domain already locked',
80
+ 117: 'Domain already unlocked',
81
+ 118: 'Domain already has auto-renewal enabled',
82
+ 119: 'Domain already has auto-renewal disabled',
83
+ };
84
+
85
+ const message = errorMessages[code] || detail;
86
+ throw new NameSiloError(message, code);
87
+ }
88
+
89
+ // ─── Mock mode data ────────────────────────────────────────────────────────
90
+ // In-memory storage for mock operations that require persistence between calls
91
+ const _mockStore = {
92
+ emailForwards: {} // { domain: [{ email2, forward2 }] }
93
+ };
94
+
95
+ function mockResponse(operation, params) {
96
+ const base = { code: 300, detail: 'success', operationtype: operation };
97
+
98
+ switch (operation) {
99
+ case 'checkRegisterAvailability': {
100
+ const domains = (params.domains || '').split(',').filter(Boolean);
101
+ const available = {};
102
+ const unavailable = {};
103
+ domains.forEach((d, i) => {
104
+ // Mock: every other domain is unavailable for realism
105
+ if (i % 2 === 0) {
106
+ available[d] = { price: '12.99' };
107
+ } else {
108
+ unavailable[d] = {};
109
+ }
110
+ });
111
+ return { ...base, available, unavailable };
112
+ }
113
+
114
+ case 'registerDomain':
115
+ return {
116
+ ...base,
117
+ code: 302,
118
+ detail: 'Successful order',
119
+ domain: params.domain,
120
+ order_amount: '12.99',
121
+ total_amount: '12.99',
122
+ message: 'Your domain registration was successfully processed.'
123
+ };
124
+
125
+ case 'getPrices':
126
+ return {
127
+ ...base,
128
+ com: { registration: '12.99', renewal: '12.99', transfer: '12.99' },
129
+ net: { registration: '11.99', renewal: '11.99', transfer: '11.99' },
130
+ org: { registration: '10.99', renewal: '10.99', transfer: '10.99' },
131
+ io: { registration: '39.99', renewal: '39.99', transfer: '39.99' },
132
+ dev: { registration: '12.99', renewal: '12.99', transfer: '12.99' },
133
+ app: { registration: '14.99', renewal: '14.99', transfer: '14.99' },
134
+ ai: { registration: '69.99', renewal: '69.99', transfer: '69.99' },
135
+ co: { registration: '24.99', renewal: '24.99', transfer: '24.99' },
136
+ xyz: { registration: '3.99', renewal: '3.99', transfer: '3.99' },
137
+ me: { registration: '19.99', renewal: '19.99', transfer: '19.99' }
138
+ };
139
+
140
+ case 'listDomains':
141
+ return {
142
+ ...base,
143
+ domains: {
144
+ domain: [
145
+ {
146
+ domain: 'example.com',
147
+ created: '2024-01-15',
148
+ expires: '2025-01-15',
149
+ status: 'Active',
150
+ locked: 'Yes',
151
+ private: 'No',
152
+ auto_renew: 'Yes',
153
+ traffic_type: 'Forwarding',
154
+ email_verified: 'Yes',
155
+ portfolio: ''
156
+ },
157
+ {
158
+ domain: 'mysite.io',
159
+ created: '2024-06-01',
160
+ expires: '2025-06-01',
161
+ status: 'Active',
162
+ locked: 'Yes',
163
+ private: 'Yes',
164
+ auto_renew: 'No',
165
+ traffic_type: 'Forwarding',
166
+ email_verified: 'Yes',
167
+ portfolio: ''
168
+ }
169
+ ]
170
+ }
171
+ };
172
+
173
+ case 'getDomainInfo':
174
+ return {
175
+ ...base,
176
+ domain: params.domain || 'example.com',
177
+ created: '2024-01-15',
178
+ expires: '2025-01-15',
179
+ status: 'Active',
180
+ locked: 'Yes',
181
+ private: 'No',
182
+ auto_renew: 'Yes',
183
+ nameservers: { nameserver: ['ns1.namesilo.com', 'ns2.namesilo.com'] },
184
+ contact_ids: {
185
+ registrant: '12345',
186
+ administrative: '12345',
187
+ technical: '12345',
188
+ billing: '12345'
189
+ }
190
+ };
191
+
192
+ case 'renewDomain':
193
+ return {
194
+ ...base,
195
+ code: 302,
196
+ detail: 'Successful order',
197
+ domain: params.domain,
198
+ years: params.years || 1,
199
+ order_amount: '12.99',
200
+ total_amount: '12.99'
201
+ };
202
+
203
+ case 'dnsListRecords':
204
+ return {
205
+ ...base,
206
+ domain: params.domain || 'example.com',
207
+ resource_record: [
208
+ { record_id: 'mock-rr-1', type: 'A', host: params.domain, value: '93.184.216.34', ttl: '3600', distance: '0' },
209
+ { record_id: 'mock-rr-2', type: 'MX', host: params.domain, value: 'mail.example.com', ttl: '3600', distance: '10' },
210
+ { record_id: 'mock-rr-3', type: 'TXT', host: params.domain, value: 'v=spf1 include:_spf.google.com ~all', ttl: '3600', distance: '0' }
211
+ ]
212
+ };
213
+
214
+ case 'dnsAddRecord':
215
+ return {
216
+ ...base,
217
+ record_id: 'mock-rr-' + Math.floor(Math.random() * 100000)
218
+ };
219
+
220
+ case 'dnsUpdateRecord':
221
+ return { ...base };
222
+
223
+ case 'dnsDeleteRecord':
224
+ return { ...base };
225
+
226
+ case 'changeNameServers':
227
+ return { ...base };
228
+
229
+ case 'contactList':
230
+ return {
231
+ ...base,
232
+ contact: [
233
+ {
234
+ contact_id: '12345',
235
+ fn: 'John',
236
+ ln: 'Doe',
237
+ ad: '123 Main St',
238
+ cy: 'Anytown',
239
+ st: 'CA',
240
+ zp: '12345',
241
+ ct: 'US',
242
+ ph: '3125551234',
243
+ em: 'john.doe@example.com',
244
+ org: 'ACME Corp'
245
+ }
246
+ ]
247
+ };
248
+
249
+ case 'contactAdd':
250
+ return {
251
+ ...base,
252
+ contact_id: 'mock-contact-' + Math.floor(Math.random() * 100000)
253
+ };
254
+
255
+ case 'domainLock':
256
+ return { ...base };
257
+
258
+ case 'domainUnlock':
259
+ return { ...base };
260
+
261
+ case 'addPrivacy':
262
+ return { ...base };
263
+
264
+ case 'removePrivacy':
265
+ return { ...base };
266
+
267
+ case 'addAutoRenewal':
268
+ return { ...base };
269
+
270
+ case 'removeAutoRenewal':
271
+ return { ...base };
272
+
273
+ case 'retrieveAuthCode':
274
+ return {
275
+ ...base,
276
+ domain: params.domain,
277
+ message: 'The auth code for your domain has been sent to the registrant email address on file.'
278
+ };
279
+
280
+ case 'getAccountBalance':
281
+ return {
282
+ ...base,
283
+ balance: '100.00'
284
+ };
285
+
286
+ case 'whoisInfo':
287
+ return {
288
+ ...base,
289
+ domain: params.domain || 'example.com',
290
+ registrar: 'NameSilo, LLC',
291
+ registrar_url: 'https://www.namesilo.com',
292
+ creation_date: '2024-01-15T00:00:00Z',
293
+ updated_date: '2024-06-01T00:00:00Z',
294
+ expiry_date: '2025-01-15T00:00:00Z',
295
+ status: 'clientTransferProhibited',
296
+ nameservers: { nameserver: ['ns1.namesilo.com', 'ns2.namesilo.com'] },
297
+ registrant: {
298
+ name: 'Domain Admin',
299
+ organization: 'NameSilo Privacy Protection',
300
+ email: 'proxy@namesilo.com',
301
+ country: 'US'
302
+ }
303
+ };
304
+
305
+ case 'listRegisteredNameServers':
306
+ return {
307
+ ...base,
308
+ hosts: [
309
+ { host: `ns1.${params.domain || 'example.com'}`, ip: '1.2.3.4' }
310
+ ]
311
+ };
312
+ case 'addRegisteredNameServer':
313
+ case 'modifyRegisteredNameServer':
314
+ case 'deleteRegisteredNameServer':
315
+ return { ...base };
316
+
317
+ case 'portfolioList':
318
+ return { ...base, portfolios: { portfolio: [{ name: 'my-portfolio', domains: 5 }] } };
319
+ case 'portfolioAdd':
320
+ return { ...base };
321
+ case 'portfolioDelete':
322
+ return { ...base };
323
+ case 'portfolioDomainAssociate':
324
+ return { ...base };
325
+
326
+ case 'listOrders':
327
+ return { ...base, orders: { order: [{ order_number: 'ORD-1001', domain: 'example.com', amount: '12.99', date: '2024-01-15', status: 'Complete' }] } };
328
+ case 'orderDetails':
329
+ return { ...base, order_number: params.order_number, domain: 'example.com', amount: '12.99', date: '2024-01-15', status: 'Complete' };
330
+
331
+ case 'domainPush':
332
+ return { ...base, domain: params.domain };
333
+ case 'registrantVerificationStatus':
334
+ return { ...base, verified: true, email: params.email || 'user@example.com' };
335
+ case 'emailVerification':
336
+ return { ...base, message: 'Verification email sent' };
337
+
338
+ case 'transferUpdateChangeEPPCode':
339
+ return { ...base };
340
+ case 'transferUpdateResendAdminEmail':
341
+ return { ...base };
342
+ case 'transferUpdateResubmitToRegistry':
343
+ return { ...base };
344
+ case 'registerDomainDrop':
345
+ return { ...base, code: 302, detail: 'Successful order', domain: params.domain, order_amount: '12.99' };
346
+
347
+ case 'listEmailForwards': {
348
+ const domain = params.domain || '';
349
+ const forwards = _mockStore.emailForwards[domain] || [];
350
+ return { ...base, forwards };
351
+ }
352
+
353
+ case 'configureEmailForward': {
354
+ const domain = params.domain || '';
355
+ if (!_mockStore.emailForwards[domain]) _mockStore.emailForwards[domain] = [];
356
+ // Remove existing forward for same address if any, then add
357
+ _mockStore.emailForwards[domain] = _mockStore.emailForwards[domain].filter(
358
+ f => f.email2 !== params.email2
359
+ );
360
+ _mockStore.emailForwards[domain].push({ email2: params.email2, forward2: params.forward2 });
361
+ return { ...base };
362
+ }
363
+
364
+ case 'deleteEmailForward': {
365
+ const domain = params.domain || '';
366
+ if (_mockStore.emailForwards[domain]) {
367
+ _mockStore.emailForwards[domain] = _mockStore.emailForwards[domain].filter(
368
+ f => f.email2 !== params.email2
369
+ );
370
+ }
371
+ return { ...base };
372
+ }
373
+
374
+ default:
375
+ return { ...base, code: 300, detail: 'success (mock)' };
376
+ }
377
+ }
378
+
379
+ // ─── Public API operations ─────────────────────────────────────────────────
380
+
381
+ /**
382
+ * Check domain availability for registration.
383
+ * @param {string[]} domains - Array of FQDNs
384
+ */
385
+ async function checkRegisterAvailability(domains) {
386
+ if (!Array.isArray(domains) || domains.length === 0) {
387
+ throw new NameSiloError('domains must be a non-empty array', 108);
388
+ }
389
+ return call('checkRegisterAvailability', { domains: domains.join(',') });
390
+ }
391
+
392
+ /**
393
+ * Register a domain.
394
+ * @param {string} domain - FQDN to register
395
+ * @param {number} years - Registration years (1-10)
396
+ * @param {Object} opts - Optional: auto_renew, private, contact_id
397
+ */
398
+ async function registerDomain(domain, years = 1, opts = {}) {
399
+ if (!domain) throw new NameSiloError('domain is required', 108);
400
+ return call('registerDomain', {
401
+ domain,
402
+ years,
403
+ auto_renew: opts.auto_renew !== undefined ? (opts.auto_renew ? 1 : 0) : 1,
404
+ private: opts.private !== undefined ? (opts.private ? 1 : 0) : 0,
405
+ contact_id: opts.contact_id
406
+ });
407
+ }
408
+
409
+ /**
410
+ * Get current pricing for TLDs.
411
+ * @param {string[]} tlds - Optional array of TLDs to filter (without dot)
412
+ */
413
+ async function getPrices(tlds = []) {
414
+ return call('getPrices');
415
+ }
416
+
417
+ /**
418
+ * List all domains in the account.
419
+ */
420
+ async function listDomains() {
421
+ return call('listDomains');
422
+ }
423
+
424
+ /**
425
+ * Get detailed info about a specific domain.
426
+ * @param {string} domain - FQDN
427
+ */
428
+ async function getDomainInfo(domain) {
429
+ if (!domain) throw new NameSiloError('domain is required', 108);
430
+ return call('getDomainInfo', { domain });
431
+ }
432
+
433
+ /**
434
+ * Renew a domain.
435
+ * @param {string} domain - FQDN
436
+ * @param {number} years - Number of years to renew
437
+ */
438
+ async function renewDomain(domain, years = 1) {
439
+ if (!domain) throw new NameSiloError('domain is required', 108);
440
+ return call('renewDomain', { domain, years });
441
+ }
442
+
443
+ /**
444
+ * List DNS records for a domain.
445
+ * @param {string} domain - FQDN
446
+ */
447
+ async function dnsListRecords(domain) {
448
+ if (!domain) throw new NameSiloError('domain is required', 108);
449
+ return call('dnsListRecords', { domain });
450
+ }
451
+
452
+ /**
453
+ * Add a DNS record.
454
+ * @param {string} domain
455
+ * @param {string} type - Record type (A, AAAA, CNAME, MX, TXT, etc.)
456
+ * @param {string} host - Hostname/subdomain
457
+ * @param {string} value - Record value
458
+ * @param {number} ttl - TTL in seconds (default 3600)
459
+ * @param {number} distance - MX priority / SRV distance
460
+ */
461
+ async function dnsAddRecord(domain, type, host, value, ttl = 3600, distance = 0) {
462
+ if (!domain || !type || !host || !value) {
463
+ throw new NameSiloError('domain, type, host, and value are required', 108);
464
+ }
465
+ return call('dnsAddRecord', {
466
+ domain,
467
+ rrtype: type,
468
+ rrhost: host,
469
+ rrvalue: value,
470
+ rrttl: ttl,
471
+ rrdistance: distance
472
+ });
473
+ }
474
+
475
+ /**
476
+ * Update an existing DNS record.
477
+ * @param {string} domain
478
+ * @param {string} rrid - Record ID from dnsListRecords
479
+ * @param {string} host
480
+ * @param {string} value
481
+ * @param {number} ttl
482
+ * @param {number} distance
483
+ */
484
+ async function dnsUpdateRecord(domain, rrid, host, value, ttl = 3600, distance = 0) {
485
+ if (!domain || !rrid || !host || !value) {
486
+ throw new NameSiloError('domain, rrid, host, and value are required', 108);
487
+ }
488
+ return call('dnsUpdateRecord', {
489
+ domain,
490
+ rrid,
491
+ rrhost: host,
492
+ rrvalue: value,
493
+ rrttl: ttl,
494
+ rrdistance: distance
495
+ });
496
+ }
497
+
498
+ /**
499
+ * Delete a DNS record.
500
+ * @param {string} domain
501
+ * @param {string} rrid - Record ID
502
+ */
503
+ async function dnsDeleteRecord(domain, rrid) {
504
+ if (!domain || !rrid) {
505
+ throw new NameSiloError('domain and rrid are required', 108);
506
+ }
507
+ return call('dnsDeleteRecord', { domain, rrid });
508
+ }
509
+
510
+ /**
511
+ * Change nameservers for a domain.
512
+ * @param {string} domain
513
+ * @param {string[]} ns - Array of nameserver hostnames (2-13)
514
+ */
515
+ async function changeNameServers(domain, ns) {
516
+ if (!domain) throw new NameSiloError('domain is required', 108);
517
+ if (!Array.isArray(ns) || ns.length < 2) {
518
+ throw new NameSiloError('At least 2 nameservers required', 108);
519
+ }
520
+ const nsParams = {};
521
+ ns.forEach((n, i) => { nsParams[`ns${i + 1}`] = n; });
522
+ return call('changeNameServers', { domain, ...nsParams });
523
+ }
524
+
525
+ /**
526
+ * List contacts in the account.
527
+ */
528
+ async function contactList() {
529
+ return call('contactList');
530
+ }
531
+
532
+ /**
533
+ * Add a contact to the account.
534
+ * @param {Object} contact - Contact details
535
+ */
536
+ async function contactAdd(contact) {
537
+ const required = ['fn', 'ln', 'ad', 'cy', 'st', 'zp', 'ct', 'ph', 'em'];
538
+ for (const field of required) {
539
+ if (!contact[field]) {
540
+ throw new NameSiloError(`Contact field '${field}' is required`, 108);
541
+ }
542
+ }
543
+ return call('contactAdd', contact);
544
+ }
545
+
546
+ /**
547
+ * Lock a domain (prevent transfers).
548
+ * @param {string} domain
549
+ */
550
+ async function domainLock(domain) {
551
+ if (!domain) throw new NameSiloError('domain is required', 108);
552
+ return call('domainLock', { domain });
553
+ }
554
+
555
+ /**
556
+ * Unlock a domain (allow transfers).
557
+ * @param {string} domain
558
+ */
559
+ async function domainUnlock(domain) {
560
+ if (!domain) throw new NameSiloError('domain is required', 108);
561
+ return call('domainUnlock', { domain });
562
+ }
563
+
564
+ /**
565
+ * Enable WHOIS privacy for a domain.
566
+ * @param {string} domain
567
+ */
568
+ async function addPrivacy(domain) {
569
+ if (!domain) throw new NameSiloError('domain is required', 108);
570
+ return call('addPrivacy', { domain });
571
+ }
572
+
573
+ /**
574
+ * Disable WHOIS privacy for a domain.
575
+ * @param {string} domain
576
+ */
577
+ async function removePrivacy(domain) {
578
+ if (!domain) throw new NameSiloError('domain is required', 108);
579
+ return call('removePrivacy', { domain });
580
+ }
581
+
582
+ /**
583
+ * Enable auto-renewal for a domain.
584
+ * @param {string} domain
585
+ */
586
+ async function addAutoRenewal(domain) {
587
+ if (!domain) throw new NameSiloError('domain is required', 108);
588
+ return call('addAutoRenewal', { domain });
589
+ }
590
+
591
+ /**
592
+ * Disable auto-renewal for a domain.
593
+ * @param {string} domain
594
+ */
595
+ async function removeAutoRenewal(domain) {
596
+ if (!domain) throw new NameSiloError('domain is required', 108);
597
+ return call('removeAutoRenewal', { domain });
598
+ }
599
+
600
+ /**
601
+ * Request the EPP auth/transfer code for a domain (sent via email).
602
+ * @param {string} domain
603
+ */
604
+ async function retrieveAuthCode(domain) {
605
+ if (!domain) throw new NameSiloError('domain is required', 108);
606
+ return call('retrieveAuthCode', { domain });
607
+ }
608
+
609
+ /**
610
+ * Get the current account balance.
611
+ */
612
+ async function getAccountBalance() {
613
+ return call('getAccountBalance');
614
+ }
615
+
616
+ /**
617
+ * Get WHOIS info for a domain.
618
+ * @param {string} domain
619
+ */
620
+ async function whoisInfo(domain) {
621
+ if (!domain) throw new NameSiloError('domain is required', 108);
622
+ return call('whoisInfo', { domain });
623
+ }
624
+
625
+ /**
626
+ * List registered nameservers for a domain.
627
+ */
628
+ async function listRegisteredNameServers(domain) {
629
+ if (!domain) throw new NameSiloError('domain is required', 108);
630
+ return call('listRegisteredNameServers', { domain });
631
+ }
632
+
633
+ /**
634
+ * Add/register a private nameserver.
635
+ * @param {string} domain
636
+ * @param {string} host - Nameserver hostname (e.g. ns1.mydomain.com)
637
+ * @param {string[]} ips - IP addresses for the nameserver
638
+ */
639
+ async function addRegisteredNameServer(domain, host, ips) {
640
+ if (!domain || !host || !ips || !ips.length) {
641
+ throw new NameSiloError('domain, host, and ips are required', 108);
642
+ }
643
+ const params = { domain, host };
644
+ ips.forEach((ip, i) => { params[`ip${i + 1}`] = ip; });
645
+ return call('addRegisteredNameServer', params);
646
+ }
647
+
648
+ /**
649
+ * Modify a registered nameserver.
650
+ */
651
+ async function modifyRegisteredNameServer(domain, host, ips) {
652
+ if (!domain || !host || !ips || !ips.length) {
653
+ throw new NameSiloError('domain, host, and ips are required', 108);
654
+ }
655
+ const params = { domain, host };
656
+ ips.forEach((ip, i) => { params[`ip${i + 1}`] = ip; });
657
+ return call('modifyRegisteredNameServer', params);
658
+ }
659
+
660
+ /**
661
+ * Delete a registered nameserver.
662
+ */
663
+ async function deleteRegisteredNameServer(domain, host) {
664
+ if (!domain || !host) throw new NameSiloError('domain and host are required', 108);
665
+ return call('deleteRegisteredNameServer', { domain, host });
666
+ }
667
+
668
+ // ─── Portfolio Management ─────────────────────────────────────────────────
669
+ async function portfolioList() { return call('portfolioList'); }
670
+ async function portfolioAdd(portfolio) { return call('portfolioAdd', { portfolio }); }
671
+ async function portfolioDelete(portfolio) { return call('portfolioDelete', { portfolio }); }
672
+ async function portfolioDomainAssociate(portfolio, domains) {
673
+ return call('portfolioDomainAssociate', { portfolio, domains: Array.isArray(domains) ? domains.join(',') : domains });
674
+ }
675
+
676
+ // ─── Orders ───────────────────────────────────────────────────────────────
677
+ async function listOrders() { return call('listOrders'); }
678
+ async function orderDetails(orderNumber) {
679
+ if (!orderNumber) throw new NameSiloError('order_number is required', 108);
680
+ return call('orderDetails', { order_number: orderNumber });
681
+ }
682
+
683
+ // ─── Domain Push & Registrant Verification ────────────────────────────────
684
+ async function domainPush(domain, push_user) {
685
+ if (!domain || !push_user) throw new NameSiloError('domain and push_user (email) are required', 108);
686
+ return call('domainPush', { domain, push_user });
687
+ }
688
+ async function registrantVerificationStatus() { return call('registrantVerificationStatus'); }
689
+ async function emailVerification(email) {
690
+ if (!email) throw new NameSiloError('email is required', 108);
691
+ return call('emailVerification', { email });
692
+ }
693
+
694
+ async function transferUpdateChangeEPPCode(domain, auth) {
695
+ if (!domain || !auth) throw new NameSiloError('domain and auth are required', 108);
696
+ return call('transferUpdateChangeEPPCode', { domain, auth });
697
+ }
698
+ async function transferUpdateResendAdminEmail(domain) {
699
+ if (!domain) throw new NameSiloError('domain is required', 108);
700
+ return call('transferUpdateResendAdminEmail', { domain });
701
+ }
702
+ async function transferUpdateResubmitToRegistry(domain) {
703
+ if (!domain) throw new NameSiloError('domain is required', 108);
704
+ return call('transferUpdateResubmitToRegistry', { domain });
705
+ }
706
+ async function registerDomainDrop(domain, opts = {}) {
707
+ if (!domain) throw new NameSiloError('domain is required', 108);
708
+ return call('registerDomainDrop', { domain, years: opts.years || 1, auto_renew: opts.auto_renew !== false ? 1 : 0, private: opts.private ? 1 : 0 });
709
+ }
710
+
711
+ // ─── Exports ───────────────────────────────────────────────────────────────
712
+ module.exports = {
713
+ NameSiloError,
714
+ call,
715
+ checkRegisterAvailability,
716
+ registerDomain,
717
+ getPrices,
718
+ listDomains,
719
+ getDomainInfo,
720
+ renewDomain,
721
+ dnsListRecords,
722
+ dnsAddRecord,
723
+ dnsUpdateRecord,
724
+ dnsDeleteRecord,
725
+ changeNameServers,
726
+ contactList,
727
+ contactAdd,
728
+ domainLock,
729
+ domainUnlock,
730
+ addPrivacy,
731
+ removePrivacy,
732
+ addAutoRenewal,
733
+ removeAutoRenewal,
734
+ retrieveAuthCode,
735
+ getAccountBalance,
736
+ whoisInfo,
737
+ listRegisteredNameServers,
738
+ addRegisteredNameServer,
739
+ modifyRegisteredNameServer,
740
+ deleteRegisteredNameServer,
741
+ portfolioList,
742
+ portfolioAdd,
743
+ portfolioDelete,
744
+ portfolioDomainAssociate,
745
+ listOrders,
746
+ orderDetails,
747
+ domainPush,
748
+ registrantVerificationStatus,
749
+ emailVerification,
750
+ transferUpdateChangeEPPCode,
751
+ transferUpdateResendAdminEmail,
752
+ transferUpdateResubmitToRegistry,
753
+ registerDomainDrop
754
+ };