@aoxborrow/registrar-client 0.7.0 → 0.9.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aoxborrow/registrar-client",
3
- "version": "0.7.0",
3
+ "version": "0.9.0",
4
4
  "description": "One consistent, fully-typed TypeScript interface to many domain registrar APIs (GoDaddy, Cloudflare, Namecheap, Spaceship, Dynadot, Porkbun, and more) behind a single pluggable multi-provider client",
5
5
  "author": {
6
6
  "name": "Aaron Oxborrow",
@@ -89,20 +89,20 @@
89
89
  },
90
90
  "devDependencies": {
91
91
  "@eslint/js": "^10.0.1",
92
- "@types/node": "^26.2.0",
93
- "@typescript-eslint/eslint-plugin": "^8.56.0",
94
- "@typescript-eslint/parser": "^8.56.0",
92
+ "@types/node": "^26.6.2",
93
+ "@typescript-eslint/eslint-plugin": "^8.71.1",
94
+ "@typescript-eslint/parser": "^8.71.1",
95
95
  "@vitest/coverage-v8": "^4.1.11",
96
- "dotenv": "^17.4.2",
97
- "eslint": "^10.9.0",
96
+ "dotenv": "^18.0.1",
97
+ "eslint": "^10.11.0",
98
98
  "eslint-config-prettier": "^10.1.8",
99
99
  "eslint-import-resolver-typescript": "^4.4.4",
100
100
  "eslint-plugin-import-x": "^4.17.1",
101
- "globals": "^17.11.0",
102
- "prettier": "^3.6.2",
101
+ "globals": "^17.12.0",
102
+ "prettier": "^3.9.8",
103
103
  "tsup": "^8.5.0",
104
104
  "typescript": "^5.9.3",
105
- "typescript-eslint": "^8.56.0",
105
+ "typescript-eslint": "^8.71.1",
106
106
  "vitest": "^4.1.11"
107
107
  },
108
108
  "dependencies": {
package/src/http.ts CHANGED
@@ -31,6 +31,9 @@ export interface HttpClientConfig {
31
31
  // unset for APIs where the method means nothing: Namecheap and NameSilo send
32
32
  // writes as GET. Those mark their nested reads with `asRead` instead.
33
33
  safeMethods?: readonly string[];
34
+ // A provider-specific delay for HTTP 429 without a usable Retry-After.
35
+ // Otherwise the normal exponential backoff applies.
36
+ rateLimitFallbackSeconds?: number;
34
37
  }
35
38
 
36
39
  export const REST_SAFE_METHODS: readonly string[] = ['GET', 'HEAD'];
@@ -260,8 +263,10 @@ export class HttpClient {
260
263
  case 404:
261
264
  return new NotFoundError(message);
262
265
  case 429: {
263
- const retryAfter = Number(response.headers.get('retry-after'));
264
- return new RateLimitError(message, Number.isFinite(retryAfter) ? retryAfter : undefined);
266
+ const retryAfter =
267
+ parseRetryAfter(response.headers.get('retry-after')) ??
268
+ this.config.rateLimitFallbackSeconds;
269
+ return new RateLimitError(message, retryAfter);
265
270
  }
266
271
  default: {
267
272
  if (response.status >= 500) {
@@ -277,6 +282,25 @@ export class HttpClient {
277
282
  }
278
283
  }
279
284
 
285
+ // RFC 9110 Retry-After is delay-seconds or an HTTP date. Absence must stay
286
+ // undefined: Number(null) is zero and would bypass the configured backoff.
287
+ function parseRetryAfter(value: string | null): number | undefined {
288
+ const text = value?.trim();
289
+ if (!text) return undefined;
290
+ if (/^\d+$/.test(text)) {
291
+ const seconds = Number(text);
292
+ return Number.isSafeInteger(seconds) ? seconds : undefined;
293
+ }
294
+ // Accept the HTTP-date formats from RFC 9110, not Date.parse's permissive
295
+ // numeric/calendar inputs. The obsolete asctime form has implicit UTC.
296
+ const zonedDate =
297
+ /^(?:[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;
298
+ const asctimeDate = /^[a-z]{3} [a-z]{3} (?: \d|\d{2}) \d{2}:\d{2}:\d{2} \d{4}$/i;
299
+ if (!zonedDate.test(text) && !asctimeDate.test(text)) return undefined;
300
+ const date = Date.parse(asctimeDate.test(text) ? `${text} GMT` : text);
301
+ return Number.isFinite(date) ? Math.max(0, Math.ceil((date - Date.now()) / 1000)) : undefined;
302
+ }
303
+
280
304
  // A retryable failure that leaves a write's result unknown: the request may
281
305
  // have reached the registrar. A rate limit is an explicit rejection, and a
282
306
  // connection error known to precede sending never reached it.
package/src/index.ts CHANGED
@@ -27,6 +27,7 @@ export type { RegistrarFeature } from './features';
27
27
 
28
28
  // bundled providers
29
29
  export { CloudflareRegistrar } from './registrars/cloudflare';
30
+ export { Domain101Registrar } from './registrars/domain101';
30
31
  export { DynadotRegistrar } from './registrars/dynadot';
31
32
  export { GandiRegistrar } from './registrars/gandi';
32
33
  export { GoDaddyRegistrar } from './registrars/godaddy';