@aoxborrow/registrar-client 0.7.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 CHANGED
@@ -503,6 +503,12 @@ Requests retry with exponential backoff (`retries`, default 2). What may be
503
503
  re-sent depends on where the failure happened and on whether the call reads or
504
504
  writes, and is the same for every provider:
505
505
 
506
+ HTTP 429 honors valid `Retry-After` delay-seconds and HTTP dates. Missing or
507
+ invalid headers use the provider's fallback or the configured backoff: Dynadot
508
+ waits a conservative 60 seconds, and Name.com waits until its
509
+ `X-RateLimit-Reset` time. An explicit zero or past date remains
510
+ an immediate retry instruction from the server.
511
+
506
512
  | Failure | Read | Write |
507
513
  | -------------------------------------------------------------------------- | ----- | -------------------------------------- |
508
514
  | The registrar never saw it: DNS failure, connection refused, TLS handshake | Retry | Retry |
package/dist/index.cjs CHANGED
@@ -599,8 +599,8 @@ var HttpClient = class {
599
599
  case 404:
600
600
  return new NotFoundError(message);
601
601
  case 429: {
602
- const retryAfter = Number(response.headers.get("retry-after"));
603
- return new RateLimitError(message, Number.isFinite(retryAfter) ? retryAfter : void 0);
602
+ const retryAfter = parseRetryAfter(response.headers.get("retry-after")) ?? this.config.rateLimitFallbackSeconds;
603
+ return new RateLimitError(message, retryAfter);
604
604
  }
605
605
  default: {
606
606
  if (response.status >= 500) {
@@ -613,6 +613,19 @@ var HttpClient = class {
613
613
  }
614
614
  }
615
615
  };
616
+ function parseRetryAfter(value) {
617
+ const text2 = value?.trim();
618
+ if (!text2) return void 0;
619
+ if (/^\d+$/.test(text2)) {
620
+ const seconds = Number(text2);
621
+ return Number.isSafeInteger(seconds) ? seconds : void 0;
622
+ }
623
+ 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;
624
+ const asctimeDate = /^[a-z]{3} [a-z]{3} (?: \d|\d{2}) \d{2}:\d{2}:\d{2} \d{4}$/i;
625
+ if (!zonedDate.test(text2) && !asctimeDate.test(text2)) return void 0;
626
+ const date = Date.parse(asctimeDate.test(text2) ? `${text2} GMT` : text2);
627
+ return Number.isFinite(date) ? Math.max(0, Math.ceil((date - Date.now()) / 1e3)) : void 0;
628
+ }
616
629
  function isOutcomeUnknown(error) {
617
630
  if (error instanceof TimeoutError || error instanceof InvalidResponseError) return true;
618
631
  return error instanceof ConnectionError && !error.notSent;
@@ -1800,7 +1813,10 @@ var DynadotRegistrar = class extends BaseRegistrar {
1800
1813
  production: "https://api.dynadot.com",
1801
1814
  sandbox: "https://api-sandbox.dynadot.com"
1802
1815
  }),
1803
- headers: { Authorization: `Bearer ${credentials.apiKey}` }
1816
+ headers: { Authorization: `Bearer ${credentials.apiKey}` },
1817
+ // HTTP 429 can omit Retry-After. Avoid exhausting all attempts inside
1818
+ // Dynadot's regular-tier minute window in that case.
1819
+ rateLimitFallbackSeconds: 60
1804
1820
  },
1805
1821
  options
1806
1822
  );
@@ -4741,9 +4757,10 @@ var NamecheapRegistrar = class extends BaseRegistrar {
4741
4757
  }
4742
4758
  /**
4743
4759
  * Availability via domains.check (batched at 50 per request, Namecheap's cap).
4744
- * The check response only carries a price for *premium* names
4745
- * (`PremiumRegistrationPrice`); regular registration pricing comes from
4746
- * `getPricing`, so `price` is set only for premium results.
4760
+ * The check response only carries prices for *premium* names
4761
+ * (`PremiumRegistrationPrice`, `PremiumRenewalPrice`); regular pricing comes
4762
+ * from `getPricing`, so `price` and `renewalPrice` are set only for premium
4763
+ * results.
4747
4764
  */
4748
4765
  async checkAvailability(domainNames, opts) {
4749
4766
  const results = [];
@@ -4756,12 +4773,16 @@ var NamecheapRegistrar = class extends BaseRegistrar {
4756
4773
  );
4757
4774
  for (const el of ensureArray(cr.DomainCheckResult)) {
4758
4775
  const premium = el["@_IsPremiumName"] === "true";
4759
- const premiumPrice = premium ? Number(el["@_PremiumRegistrationPrice"]) : NaN;
4776
+ const premiumPrice = (attr) => {
4777
+ const n = premium ? Number(attr) : NaN;
4778
+ return Number.isFinite(n) && n > 0 ? n : void 0;
4779
+ };
4760
4780
  results.push({
4761
4781
  domainName: el["@_Domain"] ?? "",
4762
4782
  available: el["@_Available"] === "true",
4763
4783
  premium,
4764
- price: Number.isFinite(premiumPrice) && premiumPrice > 0 ? premiumPrice : void 0
4784
+ price: premiumPrice(el["@_PremiumRegistrationPrice"]),
4785
+ renewalPrice: premiumPrice(el["@_PremiumRenewalPrice"])
4765
4786
  });
4766
4787
  }
4767
4788
  }
@@ -5169,12 +5190,11 @@ var NamecomHttpClient = class extends HttpClient {
5169
5190
  }),
5170
5191
  url
5171
5192
  );
5172
- if (error instanceof RateLimitError) {
5173
- const retryAfter = response.headers.get("retry-after");
5174
- const seconds = retryAfter == null ? NaN : Number(retryAfter);
5175
- const date = retryAfter == null ? NaN : Date.parse(retryAfter);
5176
- const reset = response.headers.get("x-ratelimit-reset");
5177
- 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;
5193
+ if (error instanceof RateLimitError && error.retryAfter === void 0) {
5194
+ const reset = response.headers.get("x-ratelimit-reset")?.trim();
5195
+ if (reset && /^\d+(?:\.\d+)?$/.test(reset)) {
5196
+ error.retryAfter = Math.max(0, Math.ceil(Number(reset) - Date.now() / 1e3));
5197
+ }
5178
5198
  }
5179
5199
  return error;
5180
5200
  }