@aoxborrow/registrar-client 0.6.0 → 0.8.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.
- package/README.md +70 -5
- package/dist/index.cjs +241 -32
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +48 -3
- package/dist/index.d.ts +48 -3
- package/dist/index.js +236 -33
- package/dist/index.js.map +1 -1
- package/package.json +9 -9
- package/src/errors.ts +69 -1
- package/src/features.ts +42 -0
- package/src/http.ts +102 -7
- package/src/index.ts +9 -2
- package/src/registrar.ts +47 -2
- package/src/registrars/cloudflare.ts +3 -0
- package/src/registrars/dynadot.ts +6 -0
- package/src/registrars/gandi.ts +3 -0
- package/src/registrars/godaddy.ts +3 -0
- package/src/registrars/namebright.ts +6 -2
- package/src/registrars/namecheap.ts +17 -6
- package/src/registrars/namecom.ts +18 -18
- package/src/registrars/namesilo.ts +4 -4
- package/src/registrars/porkbun.ts +3 -3
- package/src/registrars/spaceship.ts +3 -0
- package/src/types.ts +33 -1
- package/src/utils.ts +16 -1
package/dist/index.js
CHANGED
|
@@ -55,11 +55,61 @@ var TimeoutError = class extends RetryableRegistrarError {
|
|
|
55
55
|
var ConnectionError = class extends RetryableRegistrarError {
|
|
56
56
|
status = 503;
|
|
57
57
|
// Service Unavailable
|
|
58
|
-
|
|
58
|
+
// true when the failure is known to have happened before the request left:
|
|
59
|
+
// DNS, connection refused, TLS handshake, or a proxy that never opened its
|
|
60
|
+
// tunnel. The registrar cannot have acted on it, so it is safe to re-send
|
|
61
|
+
// even for a write.
|
|
62
|
+
notSent;
|
|
63
|
+
constructor(message, details = {}) {
|
|
59
64
|
super(message);
|
|
60
65
|
this.name = "ConnectionError";
|
|
66
|
+
this.notSent = details.notSent ?? false;
|
|
67
|
+
}
|
|
68
|
+
};
|
|
69
|
+
var OutcomeUnknownError = class extends RegistrarError {
|
|
70
|
+
status = 504;
|
|
71
|
+
// Gateway Timeout
|
|
72
|
+
feature;
|
|
73
|
+
cause;
|
|
74
|
+
constructor(message, details = {}) {
|
|
75
|
+
super(message);
|
|
76
|
+
this.name = "OutcomeUnknownError";
|
|
77
|
+
this.feature = details.feature;
|
|
78
|
+
this.cause = details.cause;
|
|
61
79
|
}
|
|
62
80
|
};
|
|
81
|
+
var NOT_SENT = /* @__PURE__ */ Symbol.for("registrar-client.notSent");
|
|
82
|
+
function markNotSent(error) {
|
|
83
|
+
if (typeof error === "object" && error !== null) {
|
|
84
|
+
Object.defineProperty(error, NOT_SENT, { value: true, enumerable: false });
|
|
85
|
+
}
|
|
86
|
+
return error;
|
|
87
|
+
}
|
|
88
|
+
var PRE_SEND_CODES = /* @__PURE__ */ new Set([
|
|
89
|
+
"ENOTFOUND",
|
|
90
|
+
"EAI_AGAIN",
|
|
91
|
+
"ECONNREFUSED",
|
|
92
|
+
"EHOSTUNREACH",
|
|
93
|
+
"ENETUNREACH",
|
|
94
|
+
"EADDRNOTAVAIL",
|
|
95
|
+
"UND_ERR_CONNECT_TIMEOUT",
|
|
96
|
+
"CERT_HAS_EXPIRED",
|
|
97
|
+
"CERT_NOT_YET_VALID",
|
|
98
|
+
"DEPTH_ZERO_SELF_SIGNED_CERT",
|
|
99
|
+
"SELF_SIGNED_CERT_IN_CHAIN",
|
|
100
|
+
"UNABLE_TO_VERIFY_LEAF_SIGNATURE",
|
|
101
|
+
"UNABLE_TO_GET_ISSUER_CERT_LOCALLY",
|
|
102
|
+
"ERR_TLS_CERT_ALTNAME_INVALID"
|
|
103
|
+
]);
|
|
104
|
+
function wasNotSent(error) {
|
|
105
|
+
for (let e = error, depth = 0; e && typeof e === "object" && depth < 5; depth++) {
|
|
106
|
+
if (e[NOT_SENT] === true) return true;
|
|
107
|
+
const code = e.code;
|
|
108
|
+
if (typeof code === "string" && PRE_SEND_CODES.has(code)) return true;
|
|
109
|
+
e = e.cause;
|
|
110
|
+
}
|
|
111
|
+
return false;
|
|
112
|
+
}
|
|
63
113
|
var AuthenticationError = class extends RegistrarError {
|
|
64
114
|
status = 401;
|
|
65
115
|
// Unauthorized
|
|
@@ -226,6 +276,9 @@ function createDomain(data = {}) {
|
|
|
226
276
|
deleted: data.deleted ?? false
|
|
227
277
|
};
|
|
228
278
|
}
|
|
279
|
+
function asRead(opts) {
|
|
280
|
+
return { ...opts, call: { ...opts?.call, intent: "read" } };
|
|
281
|
+
}
|
|
229
282
|
|
|
230
283
|
// src/client.ts
|
|
231
284
|
var RegistrarClient = class {
|
|
@@ -376,6 +429,7 @@ var DEFAULT_OPTIONS = {
|
|
|
376
429
|
var USER_AGENT = "@aoxborrow/registrar-client";
|
|
377
430
|
|
|
378
431
|
// src/http.ts
|
|
432
|
+
var REST_SAFE_METHODS = ["GET", "HEAD"];
|
|
379
433
|
var HttpClient = class {
|
|
380
434
|
constructor(config) {
|
|
381
435
|
this.config = config;
|
|
@@ -399,7 +453,20 @@ var HttpClient = class {
|
|
|
399
453
|
async requestText(req) {
|
|
400
454
|
return this.withRetries(req, () => this.send(req));
|
|
401
455
|
}
|
|
402
|
-
//
|
|
456
|
+
// Shared retry-with-backoff wrapper around a single-attempt operation.
|
|
457
|
+
//
|
|
458
|
+
// What may be re-sent depends on where the failure happened and on whether
|
|
459
|
+
// the call reads or writes:
|
|
460
|
+
//
|
|
461
|
+
// failure read write
|
|
462
|
+
// never sent (DNS, refused, TLS, proxy stage) retry retry
|
|
463
|
+
// 429 retry retry
|
|
464
|
+
// unknown (timeout, dropped mid-response, 5xx) retry OutcomeUnknownError
|
|
465
|
+
// any other response fail fail
|
|
466
|
+
//
|
|
467
|
+
// A write whose outcome is unknown may have been applied, so it is never
|
|
468
|
+
// re-sent: a second renewal charges twice, a second record-create leaves a
|
|
469
|
+
// duplicate. The caller re-reads the domain instead.
|
|
403
470
|
async withRetries(req, attemptFn) {
|
|
404
471
|
const retries = req.retries ?? this.config.options.retries;
|
|
405
472
|
const backoff = req.backoff ?? this.config.options.backoff;
|
|
@@ -409,6 +476,14 @@ var HttpClient = class {
|
|
|
409
476
|
return await attemptFn();
|
|
410
477
|
} catch (error) {
|
|
411
478
|
lastError = toRegistrarError(error);
|
|
479
|
+
if (this.intentOf(req) === "write" && isOutcomeUnknown(lastError)) {
|
|
480
|
+
const unknown = new OutcomeUnknownError(
|
|
481
|
+
`The registrar did not confirm this change, so it may or may not have been applied. Check the domain before trying again. (${lastError.message})`,
|
|
482
|
+
{ feature: req.call?.feature, cause: lastError }
|
|
483
|
+
);
|
|
484
|
+
if (req.call) req.call.outcomeUnknown = unknown;
|
|
485
|
+
throw unknown;
|
|
486
|
+
}
|
|
412
487
|
if (attempt >= retries || !lastError.shouldRetry()) {
|
|
413
488
|
throw lastError;
|
|
414
489
|
}
|
|
@@ -418,6 +493,14 @@ var HttpClient = class {
|
|
|
418
493
|
}
|
|
419
494
|
throw lastError ?? new RegistrarError("Request failed");
|
|
420
495
|
}
|
|
496
|
+
// Whether a request only reads. A method the API reserves for reads wins;
|
|
497
|
+
// otherwise it is whatever the feature being served is, and a request that
|
|
498
|
+
// says nothing is assumed to write.
|
|
499
|
+
intentOf(req) {
|
|
500
|
+
const method = (req.method ?? "GET").toUpperCase();
|
|
501
|
+
if (this.config.safeMethods?.includes(method)) return "read";
|
|
502
|
+
return req.call?.intent ?? "write";
|
|
503
|
+
}
|
|
421
504
|
// build the full URL for a request, applying query parameters
|
|
422
505
|
buildUrl(req) {
|
|
423
506
|
let base;
|
|
@@ -460,7 +543,8 @@ var HttpClient = class {
|
|
|
460
543
|
try {
|
|
461
544
|
let response;
|
|
462
545
|
try {
|
|
463
|
-
|
|
546
|
+
const doFetch = req.fetch ?? this.config.options.fetch ?? fetch;
|
|
547
|
+
response = await doFetch(url, {
|
|
464
548
|
method: req.method ?? "GET",
|
|
465
549
|
headers,
|
|
466
550
|
body: req.body !== void 0 ? JSON.stringify(req.body) : void 0,
|
|
@@ -471,7 +555,9 @@ var HttpClient = class {
|
|
|
471
555
|
if (externalSignal?.aborted) throw new AbortError("Request was aborted");
|
|
472
556
|
throw new TimeoutError(`Request to '${url}' timed out after ${timeout}ms`);
|
|
473
557
|
}
|
|
474
|
-
throw new ConnectionError(`Failed to reach '${url}': ${errorMessage(error)}
|
|
558
|
+
throw new ConnectionError(`Failed to reach '${url}': ${errorMessage(error)}`, {
|
|
559
|
+
notSent: wasNotSent(error)
|
|
560
|
+
});
|
|
475
561
|
}
|
|
476
562
|
if (!response.ok) {
|
|
477
563
|
throw await this.toStatusError(response, url);
|
|
@@ -479,7 +565,17 @@ var HttpClient = class {
|
|
|
479
565
|
if (response.status === 204) {
|
|
480
566
|
return "";
|
|
481
567
|
}
|
|
482
|
-
|
|
568
|
+
try {
|
|
569
|
+
return await response.text();
|
|
570
|
+
} catch (error) {
|
|
571
|
+
if (isAbortError(error)) {
|
|
572
|
+
if (externalSignal?.aborted) throw new AbortError("Request was aborted");
|
|
573
|
+
throw new TimeoutError(`Response from '${url}' timed out after ${timeout}ms`);
|
|
574
|
+
}
|
|
575
|
+
throw new ConnectionError(
|
|
576
|
+
`Connection to '${url}' was lost while reading the response: ${errorMessage(error)}`
|
|
577
|
+
);
|
|
578
|
+
}
|
|
483
579
|
} finally {
|
|
484
580
|
clearTimeout(timeoutId);
|
|
485
581
|
externalSignal?.removeEventListener("abort", onAbort);
|
|
@@ -501,8 +597,8 @@ var HttpClient = class {
|
|
|
501
597
|
case 404:
|
|
502
598
|
return new NotFoundError(message);
|
|
503
599
|
case 429: {
|
|
504
|
-
const retryAfter =
|
|
505
|
-
return new RateLimitError(message,
|
|
600
|
+
const retryAfter = parseRetryAfter(response.headers.get("retry-after")) ?? this.config.rateLimitFallbackSeconds;
|
|
601
|
+
return new RateLimitError(message, retryAfter);
|
|
506
602
|
}
|
|
507
603
|
default: {
|
|
508
604
|
if (response.status >= 500) {
|
|
@@ -515,6 +611,23 @@ var HttpClient = class {
|
|
|
515
611
|
}
|
|
516
612
|
}
|
|
517
613
|
};
|
|
614
|
+
function parseRetryAfter(value) {
|
|
615
|
+
const text2 = value?.trim();
|
|
616
|
+
if (!text2) return void 0;
|
|
617
|
+
if (/^\d+$/.test(text2)) {
|
|
618
|
+
const seconds = Number(text2);
|
|
619
|
+
return Number.isSafeInteger(seconds) ? seconds : void 0;
|
|
620
|
+
}
|
|
621
|
+
const zonedDate = /^(?:[a-z]{3}, \d{2} [a-z]{3} \d{4}|[a-z]+, \d{2}-[a-z]{3}-\d{2}) \d{2}:\d{2}:\d{2} GMT$/i;
|
|
622
|
+
const asctimeDate = /^[a-z]{3} [a-z]{3} (?: \d|\d{2}) \d{2}:\d{2}:\d{2} \d{4}$/i;
|
|
623
|
+
if (!zonedDate.test(text2) && !asctimeDate.test(text2)) return void 0;
|
|
624
|
+
const date = Date.parse(asctimeDate.test(text2) ? `${text2} GMT` : text2);
|
|
625
|
+
return Number.isFinite(date) ? Math.max(0, Math.ceil((date - Date.now()) / 1e3)) : void 0;
|
|
626
|
+
}
|
|
627
|
+
function isOutcomeUnknown(error) {
|
|
628
|
+
if (error instanceof TimeoutError || error instanceof InvalidResponseError) return true;
|
|
629
|
+
return error instanceof ConnectionError && !error.notSent;
|
|
630
|
+
}
|
|
518
631
|
function isAbortError(error) {
|
|
519
632
|
return typeof error === "object" && error !== null && "name" in error && error.name === "AbortError";
|
|
520
633
|
}
|
|
@@ -611,6 +724,33 @@ var CORE_FEATURE_SET = new Set(CORE_FEATURES);
|
|
|
611
724
|
function isCoreFeature(feature) {
|
|
612
725
|
return CORE_FEATURE_SET.has(feature);
|
|
613
726
|
}
|
|
727
|
+
var FEATURE_CALLS = {
|
|
728
|
+
testConnection: { intent: "read", optsIndex: 0 },
|
|
729
|
+
listDomains: { intent: "read", optsIndex: 0 },
|
|
730
|
+
getDomain: { intent: "read", optsIndex: 1 },
|
|
731
|
+
checkAvailability: { intent: "read", optsIndex: 1 },
|
|
732
|
+
getPricing: { intent: "read", optsIndex: 1 },
|
|
733
|
+
registerDomain: { intent: "write", optsIndex: 2 },
|
|
734
|
+
renewDomain: { intent: "write", optsIndex: 2 },
|
|
735
|
+
setAutoRenew: { intent: "write", optsIndex: 2 },
|
|
736
|
+
transferIn: { intent: "write", optsIndex: 2 },
|
|
737
|
+
updateNameservers: { intent: "write", optsIndex: 2 },
|
|
738
|
+
getNameservers: { intent: "read", optsIndex: 1 },
|
|
739
|
+
lockDomain: { intent: "write", optsIndex: 1 },
|
|
740
|
+
unlockDomain: { intent: "write", optsIndex: 1 },
|
|
741
|
+
setPrivacy: { intent: "write", optsIndex: 2 },
|
|
742
|
+
getContacts: { intent: "read", optsIndex: 1 },
|
|
743
|
+
updateContacts: { intent: "write", optsIndex: 2 },
|
|
744
|
+
getDnsRecords: { intent: "read", optsIndex: 1 },
|
|
745
|
+
setDnsRecords: { intent: "write", optsIndex: 2 },
|
|
746
|
+
getAuthCode: { intent: "write", optsIndex: 1 },
|
|
747
|
+
getDnssec: { intent: "read", optsIndex: 1 },
|
|
748
|
+
disableDnssec: { intent: "write", optsIndex: 1 },
|
|
749
|
+
getEmailForwarding: { intent: "read", optsIndex: 1 },
|
|
750
|
+
setEmailForwarding: { intent: "write", optsIndex: 2 },
|
|
751
|
+
getDomainForwarding: { intent: "read", optsIndex: 1 },
|
|
752
|
+
setDomainForwarding: { intent: "write", optsIndex: 2 }
|
|
753
|
+
};
|
|
614
754
|
|
|
615
755
|
// src/registrar.ts
|
|
616
756
|
function selectBaseUrl(registrar, environment, urls) {
|
|
@@ -636,6 +776,9 @@ var BaseRegistrar = class {
|
|
|
636
776
|
static get features() {
|
|
637
777
|
return [...CORE_FEATURES, ...this.extendedFeatures];
|
|
638
778
|
}
|
|
779
|
+
// HTTP methods this provider's API reserves for reads; see
|
|
780
|
+
// `HttpClientConfig.safeMethods`. REST providers set `REST_SAFE_METHODS`.
|
|
781
|
+
static safeMethods = [];
|
|
639
782
|
// instance mirror of the static `features`
|
|
640
783
|
get features() {
|
|
641
784
|
return this.constructor.features;
|
|
@@ -654,7 +797,39 @@ var BaseRegistrar = class {
|
|
|
654
797
|
this.credentials = credentials;
|
|
655
798
|
this.environment = environment;
|
|
656
799
|
this.options = { ...DEFAULT_OPTIONS, ...clientOptions };
|
|
657
|
-
this.http = new HttpClient({
|
|
800
|
+
this.http = new HttpClient({
|
|
801
|
+
safeMethods: this.constructor.safeMethods,
|
|
802
|
+
...httpConfig,
|
|
803
|
+
options: this.options
|
|
804
|
+
});
|
|
805
|
+
this.threadFeatureCalls();
|
|
806
|
+
}
|
|
807
|
+
// Wrap each feature method so its `RequestOptions` carry a `FeatureCall`:
|
|
808
|
+
// the read/write intent the HTTP layer needs to decide what may be re-sent,
|
|
809
|
+
// and a slot where it reports a write whose outcome is unknown. Providers
|
|
810
|
+
// already forward their options into every request, so nothing else changes.
|
|
811
|
+
//
|
|
812
|
+
// A feature calling another feature (a write re-reading the domain, say)
|
|
813
|
+
// goes through that feature's own wrapper, so the inner read is still a read.
|
|
814
|
+
// Providers fold most failures into an `OperationResult`; the unknown outcome
|
|
815
|
+
// is copied onto that result so it is not lost along with the error.
|
|
816
|
+
threadFeatureCalls() {
|
|
817
|
+
const self = this;
|
|
818
|
+
for (const [feature, { intent, optsIndex }] of Object.entries(FEATURE_CALLS)) {
|
|
819
|
+
const original = self[feature];
|
|
820
|
+
if (typeof original !== "function") continue;
|
|
821
|
+
self[feature] = (...args) => {
|
|
822
|
+
const call = { intent, feature };
|
|
823
|
+
const next = [...args];
|
|
824
|
+
while (next.length <= optsIndex) next.push(void 0);
|
|
825
|
+
next[optsIndex] = { ...next[optsIndex], call };
|
|
826
|
+
const result = original.apply(this, next);
|
|
827
|
+
if (!(result instanceof Promise)) return result;
|
|
828
|
+
return result.then(
|
|
829
|
+
(value) => call.outcomeUnknown && value !== null && typeof value === "object" && value.success === false ? { ...value, outcome: "unknown" } : value
|
|
830
|
+
);
|
|
831
|
+
};
|
|
832
|
+
}
|
|
658
833
|
}
|
|
659
834
|
testConnection(_opts) {
|
|
660
835
|
return Promise.reject(
|
|
@@ -745,6 +920,8 @@ var BaseRegistrar = class {
|
|
|
745
920
|
// src/registrars/cloudflare.ts
|
|
746
921
|
var CloudflareRegistrar = class _CloudflareRegistrar extends BaseRegistrar {
|
|
747
922
|
name = "cloudflare";
|
|
923
|
+
// a REST API: GET and HEAD never change state
|
|
924
|
+
static safeMethods = REST_SAFE_METHODS;
|
|
748
925
|
static displayName = "Cloudflare";
|
|
749
926
|
static website = "cloudflare.com";
|
|
750
927
|
static helpText = 'Create an API token in your Cloudflare dashboard under My Profile > API Tokens. The token needs "Account.Registrar" read/write permissions. Find your Account ID in the URL or on the Overview page of any zone.';
|
|
@@ -1602,6 +1779,8 @@ var SEARCH_BATCH_SIZE = 100;
|
|
|
1602
1779
|
var SUPPORTED_DNS_TYPES = /* @__PURE__ */ new Set(["A", "AAAA", "CNAME", "MX", "TXT", "NS", "SRV", "CAA"]);
|
|
1603
1780
|
var DynadotRegistrar = class extends BaseRegistrar {
|
|
1604
1781
|
name = "dynadot";
|
|
1782
|
+
// a REST API: GET and HEAD never change state
|
|
1783
|
+
static safeMethods = REST_SAFE_METHODS;
|
|
1605
1784
|
static displayName = "Dynadot";
|
|
1606
1785
|
static website = "dynadot.com";
|
|
1607
1786
|
static helpText = "Under Tools > API in your Dynadot account, generate an API Key and API Secret. The account must be unlocked with API access enabled. The key identifies you; the secret signs requests (X-Signature) and is never sent directly.";
|
|
@@ -1632,7 +1811,10 @@ var DynadotRegistrar = class extends BaseRegistrar {
|
|
|
1632
1811
|
production: "https://api.dynadot.com",
|
|
1633
1812
|
sandbox: "https://api-sandbox.dynadot.com"
|
|
1634
1813
|
}),
|
|
1635
|
-
headers: { Authorization: `Bearer ${credentials.apiKey}` }
|
|
1814
|
+
headers: { Authorization: `Bearer ${credentials.apiKey}` },
|
|
1815
|
+
// HTTP 429 can omit Retry-After. Avoid exhausting all attempts inside
|
|
1816
|
+
// Dynadot's regular-tier minute window in that case.
|
|
1817
|
+
rateLimitFallbackSeconds: 60
|
|
1636
1818
|
},
|
|
1637
1819
|
options
|
|
1638
1820
|
);
|
|
@@ -2353,6 +2535,8 @@ var GANDI_TYPE_TO_FORWARD = {
|
|
|
2353
2535
|
};
|
|
2354
2536
|
var GandiRegistrar = class extends BaseRegistrar {
|
|
2355
2537
|
name = "gandi";
|
|
2538
|
+
// a REST API: GET and HEAD never change state
|
|
2539
|
+
static safeMethods = REST_SAFE_METHODS;
|
|
2356
2540
|
static displayName = "Gandi.net";
|
|
2357
2541
|
static website = "gandi.net";
|
|
2358
2542
|
static helpText = "Generate a Personal Access Token (PAT) in your Gandi account under Account Settings > Security. The token needs permission to manage domains. (Gandi has deprecated the legacy API Key; use a PAT.)";
|
|
@@ -3040,6 +3224,8 @@ var UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
|
|
|
3040
3224
|
var PRICE_MICRO_UNITS = 1e6;
|
|
3041
3225
|
var GoDaddyRegistrar = class _GoDaddyRegistrar extends BaseRegistrar {
|
|
3042
3226
|
name = "godaddy";
|
|
3227
|
+
// a REST API: GET and HEAD never change state
|
|
3228
|
+
static safeMethods = REST_SAFE_METHODS;
|
|
3043
3229
|
static displayName = "GoDaddy";
|
|
3044
3230
|
static website = "godaddy.com";
|
|
3045
3231
|
static helpText = "Create a Personal Access Token (PAT) at https://developer.godaddy.com and paste it here. The PAT is GoDaddy's modern credential: it unlocks the v3 API (discovery, registration, DNS, nameservers) and also authenticates the legacy v1 API for management operations (renew, transfer, auto-renew, lock, privacy, contacts) \u2014 one token covers everything.";
|
|
@@ -3920,6 +4106,8 @@ function dnsFieldsDiffer(a, b) {
|
|
|
3920
4106
|
var TOKEN_URL = "https://api.namebright.com/auth/token";
|
|
3921
4107
|
var NameBrightRegistrar = class extends BaseRegistrar {
|
|
3922
4108
|
name = "namebright";
|
|
4109
|
+
// a REST API: GET and HEAD never change state
|
|
4110
|
+
static safeMethods = REST_SAFE_METHODS;
|
|
3923
4111
|
static displayName = "NameBright";
|
|
3924
4112
|
static website = "namebright.com";
|
|
3925
4113
|
static helpText = 'API access is not enabled by default \u2014 you must request it from NameBright (contact support / your account manager) before the API Management page is available. Once enabled, create an API Application under my.namebright.com > My Account > API Management. The Client ID is "<accountName>:<applicationName>"; NameBright issues the Client Secret. Note the application also enforces an IP whitelist.';
|
|
@@ -3958,7 +4146,8 @@ var NameBrightRegistrar = class extends BaseRegistrar {
|
|
|
3958
4146
|
client_id: this.credentials.clientId,
|
|
3959
4147
|
client_secret: this.credentials.clientSecret
|
|
3960
4148
|
},
|
|
3961
|
-
|
|
4149
|
+
// issuing a token changes nothing on the account, whatever it precedes
|
|
4150
|
+
...asRead(opts)
|
|
3962
4151
|
});
|
|
3963
4152
|
if (!res.access_token) {
|
|
3964
4153
|
throw new AuthenticationError("NameBright: token endpoint returned no access_token");
|
|
@@ -4566,9 +4755,10 @@ var NamecheapRegistrar = class extends BaseRegistrar {
|
|
|
4566
4755
|
}
|
|
4567
4756
|
/**
|
|
4568
4757
|
* Availability via domains.check (batched at 50 per request, Namecheap's cap).
|
|
4569
|
-
* The check response only carries
|
|
4570
|
-
* (`PremiumRegistrationPrice`); regular
|
|
4571
|
-
* `getPricing`, so `price`
|
|
4758
|
+
* The check response only carries prices for *premium* names
|
|
4759
|
+
* (`PremiumRegistrationPrice`, `PremiumRenewalPrice`); regular pricing comes
|
|
4760
|
+
* from `getPricing`, so `price` and `renewalPrice` are set only for premium
|
|
4761
|
+
* results.
|
|
4572
4762
|
*/
|
|
4573
4763
|
async checkAvailability(domainNames, opts) {
|
|
4574
4764
|
const results = [];
|
|
@@ -4581,12 +4771,16 @@ var NamecheapRegistrar = class extends BaseRegistrar {
|
|
|
4581
4771
|
);
|
|
4582
4772
|
for (const el of ensureArray(cr.DomainCheckResult)) {
|
|
4583
4773
|
const premium = el["@_IsPremiumName"] === "true";
|
|
4584
|
-
const premiumPrice =
|
|
4774
|
+
const premiumPrice = (attr) => {
|
|
4775
|
+
const n = premium ? Number(attr) : NaN;
|
|
4776
|
+
return Number.isFinite(n) && n > 0 ? n : void 0;
|
|
4777
|
+
};
|
|
4585
4778
|
results.push({
|
|
4586
4779
|
domainName: el["@_Domain"] ?? "",
|
|
4587
4780
|
available: el["@_Available"] === "true",
|
|
4588
4781
|
premium,
|
|
4589
|
-
price:
|
|
4782
|
+
price: premiumPrice(el["@_PremiumRegistrationPrice"]),
|
|
4783
|
+
renewalPrice: premiumPrice(el["@_PremiumRenewalPrice"])
|
|
4590
4784
|
});
|
|
4591
4785
|
}
|
|
4592
4786
|
}
|
|
@@ -4737,7 +4931,11 @@ var NamecheapRegistrar = class extends BaseRegistrar {
|
|
|
4737
4931
|
* has no WhoisGuard allotted (e.g. TLDs that don't offer it).
|
|
4738
4932
|
*/
|
|
4739
4933
|
async setPrivacy(domainName, enabled, opts) {
|
|
4740
|
-
const cr = await this.command(
|
|
4934
|
+
const cr = await this.command(
|
|
4935
|
+
"namecheap.domains.getInfo",
|
|
4936
|
+
{ DomainName: domainName },
|
|
4937
|
+
asRead(opts)
|
|
4938
|
+
);
|
|
4741
4939
|
const id = cr.DomainGetInfoResult?.Whoisguard?.ID;
|
|
4742
4940
|
if (id == null || String(id) === "0") {
|
|
4743
4941
|
throw new Error(`${this.name}: ${domainName} has no WhoisGuard subscription to toggle`);
|
|
@@ -4977,7 +5175,7 @@ var NamecomHttpClient = class extends HttpClient {
|
|
|
4977
5175
|
} catch (error) {
|
|
4978
5176
|
if (error instanceof ParsingError) throw new ParsingError("namecom: invalid JSON response");
|
|
4979
5177
|
if (error instanceof ConnectionError)
|
|
4980
|
-
throw new ConnectionError("namecom: network request failed");
|
|
5178
|
+
throw new ConnectionError("namecom: network request failed", { notSent: error.notSent });
|
|
4981
5179
|
throw error;
|
|
4982
5180
|
}
|
|
4983
5181
|
}
|
|
@@ -4990,18 +5188,19 @@ var NamecomHttpClient = class extends HttpClient {
|
|
|
4990
5188
|
}),
|
|
4991
5189
|
url
|
|
4992
5190
|
);
|
|
4993
|
-
if (error instanceof RateLimitError) {
|
|
4994
|
-
const
|
|
4995
|
-
|
|
4996
|
-
|
|
4997
|
-
|
|
4998
|
-
error.retryAfter = Number.isFinite(seconds) ? Math.max(0, seconds) : Number.isFinite(date) ? Math.max(0, (date - Date.now()) / 1e3) : reset != null && Number.isFinite(Number(reset)) ? Math.max(0, Number(reset) - Date.now() / 1e3) : void 0;
|
|
5191
|
+
if (error instanceof RateLimitError && error.retryAfter === void 0) {
|
|
5192
|
+
const reset = response.headers.get("x-ratelimit-reset")?.trim();
|
|
5193
|
+
if (reset && /^\d+(?:\.\d+)?$/.test(reset)) {
|
|
5194
|
+
error.retryAfter = Math.max(0, Math.ceil(Number(reset) - Date.now() / 1e3));
|
|
5195
|
+
}
|
|
4999
5196
|
}
|
|
5000
5197
|
return error;
|
|
5001
5198
|
}
|
|
5002
5199
|
};
|
|
5003
5200
|
var NamecomRegistrar = class extends BaseRegistrar {
|
|
5004
5201
|
name = "namecom";
|
|
5202
|
+
// a REST API: GET and HEAD never change state
|
|
5203
|
+
static safeMethods = REST_SAFE_METHODS;
|
|
5005
5204
|
static displayName = "Name.com";
|
|
5006
5205
|
static website = "name.com";
|
|
5007
5206
|
static supportsSandbox = true;
|
|
@@ -5030,7 +5229,11 @@ var NamecomRegistrar = class extends BaseRegistrar {
|
|
|
5030
5229
|
headers: { Authorization: `Basic ${auth}` }
|
|
5031
5230
|
};
|
|
5032
5231
|
super(credentials, config, options);
|
|
5033
|
-
this.http = new NamecomHttpClient({
|
|
5232
|
+
this.http = new NamecomHttpClient({
|
|
5233
|
+
...config,
|
|
5234
|
+
safeMethods: REST_SAFE_METHODS,
|
|
5235
|
+
options: this.options
|
|
5236
|
+
});
|
|
5034
5237
|
}
|
|
5035
5238
|
async testConnection(opts) {
|
|
5036
5239
|
try {
|
|
@@ -5258,7 +5461,6 @@ var NamecomRegistrar = class extends BaseRegistrar {
|
|
|
5258
5461
|
const old = index >= 0 ? remaining.splice(index, 1)[0] : void 0;
|
|
5259
5462
|
await this.http.request({
|
|
5260
5463
|
...opts,
|
|
5261
|
-
retries: 0,
|
|
5262
5464
|
method: old ? "PUT" : "POST",
|
|
5263
5465
|
path: old ? `${path}/${old.id}` : path,
|
|
5264
5466
|
body: record
|
|
@@ -5267,7 +5469,6 @@ var NamecomRegistrar = class extends BaseRegistrar {
|
|
|
5267
5469
|
for (const old of remaining) {
|
|
5268
5470
|
await this.http.request({
|
|
5269
5471
|
...opts,
|
|
5270
|
-
retries: 0,
|
|
5271
5472
|
method: "DELETE",
|
|
5272
5473
|
path: `${path}/${old.id}`
|
|
5273
5474
|
});
|
|
@@ -5331,7 +5532,7 @@ var NamecomRegistrar = class extends BaseRegistrar {
|
|
|
5331
5532
|
}
|
|
5332
5533
|
async mutate(req, opts) {
|
|
5333
5534
|
try {
|
|
5334
|
-
await this.http.request({ ...req, ...opts
|
|
5535
|
+
await this.http.request({ ...req, ...opts });
|
|
5335
5536
|
return { success: true, message: "Domain updated successfully" };
|
|
5336
5537
|
} catch (error) {
|
|
5337
5538
|
return { success: false, message: toRegistrarError(error).message };
|
|
@@ -5744,7 +5945,7 @@ var NameSiloRegistrar = class extends BaseRegistrar {
|
|
|
5744
5945
|
*/
|
|
5745
5946
|
async disableDnssec(domainName, opts) {
|
|
5746
5947
|
try {
|
|
5747
|
-
const res = await this.call("dnsSecListRecords", { domain: domainName }, opts);
|
|
5948
|
+
const res = await this.call("dnsSecListRecords", { domain: domainName }, asRead(opts));
|
|
5748
5949
|
if (!replyOk(res)) return statusResult2(res);
|
|
5749
5950
|
for (const d of ensureArray(res.reply?.ds_record)) {
|
|
5750
5951
|
const del = await this.call(
|
|
@@ -5817,7 +6018,7 @@ var NameSiloRegistrar = class extends BaseRegistrar {
|
|
|
5817
6018
|
desired.set(f.alias, list);
|
|
5818
6019
|
}
|
|
5819
6020
|
const currentAliases = new Set(
|
|
5820
|
-
(await this.listEmailForwards(domainName, opts)).map((r) => r.alias).filter(Boolean)
|
|
6021
|
+
(await this.listEmailForwards(domainName, asRead(opts))).map((r) => r.alias).filter(Boolean)
|
|
5821
6022
|
);
|
|
5822
6023
|
for (const alias of currentAliases) {
|
|
5823
6024
|
if (!desired.has(alias)) {
|
|
@@ -5908,7 +6109,7 @@ var NameSiloRegistrar = class extends BaseRegistrar {
|
|
|
5908
6109
|
* delegation nameservers aren't part of this list (see `updateNameservers`).
|
|
5909
6110
|
*/
|
|
5910
6111
|
async setDnsRecords(domainName, records, opts) {
|
|
5911
|
-
const listRes = await this.call("dnsListRecords", { domain: domainName }, opts);
|
|
6112
|
+
const listRes = await this.call("dnsListRecords", { domain: domainName }, asRead(opts));
|
|
5912
6113
|
if (!replyOk(listRes)) return { success: false, message: replyDetail(listRes) };
|
|
5913
6114
|
const existing = ensureArray(listRes.reply?.resource_record);
|
|
5914
6115
|
const existingByKey = /* @__PURE__ */ new Map();
|
|
@@ -6435,7 +6636,7 @@ var PorkbunRegistrar = class extends BaseRegistrar {
|
|
|
6435
6636
|
* renewals aren't supported via the API.
|
|
6436
6637
|
*/
|
|
6437
6638
|
async renewDomain(domainName, _years = 1, opts) {
|
|
6438
|
-
const pricing = await this.tldPricing(extractTld(domainName), opts);
|
|
6639
|
+
const pricing = await this.tldPricing(extractTld(domainName), asRead(opts));
|
|
6439
6640
|
if (pricing.renewal == null) {
|
|
6440
6641
|
throw new Error(`${this.name}: could not determine the renewal price for ${domainName}`);
|
|
6441
6642
|
}
|
|
@@ -6466,7 +6667,7 @@ var PorkbunRegistrar = class extends BaseRegistrar {
|
|
|
6466
6667
|
* aren't supported via the API.
|
|
6467
6668
|
*/
|
|
6468
6669
|
async transferIn(domainName, input, opts) {
|
|
6469
|
-
const pricing = await this.tldPricing(extractTld(domainName), opts);
|
|
6670
|
+
const pricing = await this.tldPricing(extractTld(domainName), asRead(opts));
|
|
6470
6671
|
if (pricing.transfer == null) {
|
|
6471
6672
|
throw new Error(`${this.name}: could not determine the transfer price for ${domainName}`);
|
|
6472
6673
|
}
|
|
@@ -6759,6 +6960,8 @@ function toDnsRecord3(r, domainName) {
|
|
|
6759
6960
|
var WRITABLE_DNS_TYPES = /* @__PURE__ */ new Set(["A", "AAAA", "CNAME", "NS", "TXT", "MX"]);
|
|
6760
6961
|
var SpaceshipRegistrar = class extends BaseRegistrar {
|
|
6761
6962
|
name = "spaceship";
|
|
6963
|
+
// a REST API: GET and HEAD never change state
|
|
6964
|
+
static safeMethods = REST_SAFE_METHODS;
|
|
6762
6965
|
static displayName = "Spaceship";
|
|
6763
6966
|
static website = "spaceship.com";
|
|
6764
6967
|
static helpText = 'Generate your API key and secret in the API Manager at Spaceship using the "New API key" button. The API requires both X-API-Key and X-API-Secret headers.';
|
|
@@ -7279,6 +7482,6 @@ function createRegistrar(name, credentials, options) {
|
|
|
7279
7482
|
return new RegistrarClass(credentials, options);
|
|
7280
7483
|
}
|
|
7281
7484
|
|
|
7282
|
-
export { ALL_FEATURES, AbortError, AuthenticationError, AuthorizationError, BaseRegistrar, CORE_FEATURES, CloudflareRegistrar, ConfigurationError, ConnectionError, ConsentRequiredError, DEFAULT_OPTIONS, DynadotRegistrar, EXTENDED_FEATURES, Feature, GandiRegistrar, GoDaddyRegistrar, HttpClient, InvalidResponseError, MASKED_FORWARD_UNSUPPORTED, NameBrightRegistrar, NameSiloRegistrar, NamecheapRegistrar, NamecomRegistrar, NotFoundError, NotImplementedError, ParsingError, PorkbunRegistrar, RateLimitError, RegistrarClient, RegistrarError, RetryableRegistrarError, SpaceshipRegistrar, TimeoutError, USER_AGENT, backoffDelay, createDomain, createRegistrar, ensureArray, filterDomains, isCoreFeature, listPortfolio, normalizeDomain, normalizeNameservers, parseDate, parseXml, registrars, requireConsent, selectBaseUrl, settableForwards, sleep, toRegistrarError };
|
|
7485
|
+
export { ALL_FEATURES, AbortError, AuthenticationError, AuthorizationError, BaseRegistrar, CORE_FEATURES, CloudflareRegistrar, ConfigurationError, ConnectionError, ConsentRequiredError, DEFAULT_OPTIONS, DynadotRegistrar, EXTENDED_FEATURES, FEATURE_CALLS, Feature, GandiRegistrar, GoDaddyRegistrar, HttpClient, InvalidResponseError, MASKED_FORWARD_UNSUPPORTED, NameBrightRegistrar, NameSiloRegistrar, NamecheapRegistrar, NamecomRegistrar, NotFoundError, NotImplementedError, OutcomeUnknownError, ParsingError, PorkbunRegistrar, REST_SAFE_METHODS, RateLimitError, RegistrarClient, RegistrarError, RetryableRegistrarError, SpaceshipRegistrar, TimeoutError, USER_AGENT, asRead, backoffDelay, createDomain, createRegistrar, ensureArray, filterDomains, isCoreFeature, listPortfolio, markNotSent, normalizeDomain, normalizeNameservers, parseDate, parseXml, registrars, requireConsent, selectBaseUrl, settableForwards, sleep, toRegistrarError, wasNotSent };
|
|
7283
7486
|
//# sourceMappingURL=index.js.map
|
|
7284
7487
|
//# sourceMappingURL=index.js.map
|