shipmail 0.5.6 → 0.5.7
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 +25 -21
- package/dist/index.cjs +31 -5
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/index.js +31 -5
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -93,15 +93,15 @@ const shipmail = new ShipmailClient({
|
|
|
93
93
|
});
|
|
94
94
|
```
|
|
95
95
|
|
|
96
|
-
| Option | Type | Default | Description
|
|
97
|
-
| ---------------- | ------------------------ | ---------------------------- |
|
|
98
|
-
| `apiKey` | `string` | required | Shipmail API key (`sm_live_...`).
|
|
99
|
-
| `baseUrl` | `string` | `https://shipmail.to/api/v1` | API base URL.
|
|
100
|
-
| `maxRetries` | `number` | `2` | Retry count
|
|
101
|
-
| `timeout` | `number` | `30_000` | Per-request timeout in ms.
|
|
102
|
-
| `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation.
|
|
103
|
-
| `defaultHeaders` | `Record<string, string>` | `{}` | Headers added to every request.
|
|
104
|
-
| `organizationId` | `string` | none | Delegated child organization for approved infrastructure calls.
|
|
96
|
+
| Option | Type | Default | Description |
|
|
97
|
+
| ---------------- | ------------------------ | ---------------------------- | ------------------------------------------------------------------- |
|
|
98
|
+
| `apiKey` | `string` | required | Shipmail API key (`sm_live_...`). |
|
|
99
|
+
| `baseUrl` | `string` | `https://shipmail.to/api/v1` | API base URL. |
|
|
100
|
+
| `maxRetries` | `number` | `2` | Retry count for eligible requests. Up to `maxRetries + 1` attempts. |
|
|
101
|
+
| `timeout` | `number` | `30_000` | Per-request timeout in ms. |
|
|
102
|
+
| `fetch` | `typeof fetch` | `globalThis.fetch` | Custom fetch implementation. |
|
|
103
|
+
| `defaultHeaders` | `Record<string, string>` | `{}` | Headers added to every request. |
|
|
104
|
+
| `organizationId` | `string` | none | Delegated child organization for approved infrastructure calls. |
|
|
105
105
|
|
|
106
106
|
## Domains
|
|
107
107
|
|
|
@@ -730,21 +730,25 @@ try {
|
|
|
730
730
|
}
|
|
731
731
|
```
|
|
732
732
|
|
|
733
|
-
| Error | When
|
|
734
|
-
| --------------------- |
|
|
735
|
-
| `AuthenticationError` | 401. Bad or missing API key.
|
|
736
|
-
| `AuthorizationError` | 403. Key lacks permission for the resource.
|
|
737
|
-
| `ValidationError` | 400 or 422. See `details` for per-field errors.
|
|
738
|
-
| `NotFoundError` | 404.
|
|
739
|
-
| `ConflictError` | 409. Resource already exists or state conflict.
|
|
740
|
-
| `RateLimitError` | 429. Read `retryAfter` (seconds).
|
|
741
|
-
| `QuotaExceededError` | 402. Plan or sending quota exceeded.
|
|
742
|
-
| `InternalServerError` | 5xx.
|
|
743
|
-
| `ConnectionError` | Network error, timeout, or DNS failure.
|
|
733
|
+
| Error | When |
|
|
734
|
+
| --------------------- | ---------------------------------------------------------------- |
|
|
735
|
+
| `AuthenticationError` | 401. Bad or missing API key. |
|
|
736
|
+
| `AuthorizationError` | 403. Key lacks permission for the resource. |
|
|
737
|
+
| `ValidationError` | 400 or 422. See `details` for per-field errors. |
|
|
738
|
+
| `NotFoundError` | 404. |
|
|
739
|
+
| `ConflictError` | 409. Resource already exists or state conflict. |
|
|
740
|
+
| `RateLimitError` | 429. Read `retryAfter` (seconds). |
|
|
741
|
+
| `QuotaExceededError` | 402. Plan or sending quota exceeded. |
|
|
742
|
+
| `InternalServerError` | 5xx. Eligible requests retry up to `maxRetries`. |
|
|
743
|
+
| `ConnectionError` | Network error, timeout, or DNS failure. Eligible requests retry. |
|
|
744
744
|
|
|
745
745
|
## Retries
|
|
746
746
|
|
|
747
|
-
The SDK retries
|
|
747
|
+
The SDK retries eligible requests after connection errors, `429` responses, and retryable `5xx` errors. Automatic retries apply to `GET`, `HEAD`, and `OPTIONS`. Only POST, PATCH, and PUT mutations can retry, and they require an explicit `idempotencyKey` method option or an `Idempotency-Key` header. Reuse the same key for the same operation. DELETE requests and errors marked non-retryable are never retried.
|
|
748
|
+
|
|
749
|
+
`maxRetries` defaults to 2 (up to 3 total attempts) and must be a non-negative integer. Set it to 0 to disable retries.
|
|
750
|
+
|
|
751
|
+
A valid `Retry-After` header, in seconds or HTTP-date format, takes precedence over the rate-limit error body's `retry_after`. Otherwise, retries use exponential backoff with jitter. If the requested delay exceeds 60 seconds, the SDK returns the error without retrying automatically; schedule any later retry after that delay.
|
|
748
752
|
|
|
749
753
|
```ts
|
|
750
754
|
new ShipmailClient({ apiKey, maxRetries: 0 }); // disable retries
|
package/dist/index.cjs
CHANGED
|
@@ -1699,17 +1699,26 @@ var Webhooks = class extends ApiResource {
|
|
|
1699
1699
|
};
|
|
1700
1700
|
|
|
1701
1701
|
// src/version.ts
|
|
1702
|
-
var VERSION = "0.5.
|
|
1702
|
+
var VERSION = "0.5.7";
|
|
1703
1703
|
|
|
1704
1704
|
// src/client.ts
|
|
1705
1705
|
var DEFAULT_BASE_URL = "https://shipmail.to/api/v1";
|
|
1706
1706
|
var DEFAULT_MAX_RETRIES = 2;
|
|
1707
1707
|
var DEFAULT_TIMEOUT = 3e4;
|
|
1708
|
+
var MAX_RETRY_AFTER_MS = 6e4;
|
|
1709
|
+
function parseRetryAfter(value) {
|
|
1710
|
+
if (!value?.trim()) return void 0;
|
|
1711
|
+
const seconds = Number(value);
|
|
1712
|
+
if (Number.isFinite(seconds) && seconds >= 0) return seconds * 1e3;
|
|
1713
|
+
if (/^[+-]?[\d.]+$/.test(value.trim())) return void 0;
|
|
1714
|
+
const date = Date.parse(value);
|
|
1715
|
+
return Number.isFinite(date) ? Math.max(0, date - Date.now()) : void 0;
|
|
1716
|
+
}
|
|
1708
1717
|
function isRetryableStatus(status) {
|
|
1709
1718
|
return status === 429 || status >= 500;
|
|
1710
1719
|
}
|
|
1711
1720
|
function calculateBackoff(attempt, retryAfterMs) {
|
|
1712
|
-
if (retryAfterMs !== void 0 && retryAfterMs
|
|
1721
|
+
if (retryAfterMs !== void 0 && Number.isFinite(retryAfterMs) && retryAfterMs >= 0) {
|
|
1713
1722
|
return retryAfterMs;
|
|
1714
1723
|
}
|
|
1715
1724
|
const base2 = Math.min(2 ** attempt * 500, 8e3);
|
|
@@ -1752,6 +1761,9 @@ var ShipmailClient = class {
|
|
|
1752
1761
|
this.defaultHeaders = config.defaultHeaders ? { ...config.defaultHeaders } : {};
|
|
1753
1762
|
this.organizationId = config.organizationId;
|
|
1754
1763
|
}
|
|
1764
|
+
if (!Number.isSafeInteger(this.maxRetries) || this.maxRetries < 0) {
|
|
1765
|
+
throw new RangeError("maxRetries must be a non-negative safe integer");
|
|
1766
|
+
}
|
|
1755
1767
|
this.audiences = new Audiences(this);
|
|
1756
1768
|
this.automations = new Automations(this);
|
|
1757
1769
|
this.bookingPages = new BookingPages(this);
|
|
@@ -1787,9 +1799,13 @@ var ShipmailClient = class {
|
|
|
1787
1799
|
const url = this.buildUrl(options.path, options.query);
|
|
1788
1800
|
let lastError;
|
|
1789
1801
|
const methodOpts = options.methodOptions;
|
|
1802
|
+
const retryHeaders = new Headers({ ...this.defaultHeaders, ...methodOpts?.headers });
|
|
1803
|
+
const method = options.method.toUpperCase();
|
|
1804
|
+
const idempotencyKey = methodOpts?.idempotencyKey || retryHeaders.get("Idempotency-Key");
|
|
1805
|
+
const canRetry = ["GET", "HEAD", "OPTIONS"].includes(method) || ["POST", "PATCH", "PUT"].includes(method) && Boolean(idempotencyKey?.trim());
|
|
1806
|
+
let retryAfterMs;
|
|
1790
1807
|
for (let attempt = 0; attempt <= this.maxRetries; attempt++) {
|
|
1791
1808
|
if (attempt > 0) {
|
|
1792
|
-
const retryAfterMs = lastError instanceof RateLimitError && lastError.retryAfter !== void 0 ? lastError.retryAfter * 1e3 : void 0;
|
|
1793
1809
|
const delay = calculateBackoff(attempt - 1, retryAfterMs);
|
|
1794
1810
|
await new Promise((resolve) => setTimeout(resolve, delay));
|
|
1795
1811
|
}
|
|
@@ -1826,7 +1842,10 @@ var ShipmailClient = class {
|
|
|
1826
1842
|
});
|
|
1827
1843
|
} catch (err) {
|
|
1828
1844
|
lastError = new ConnectionError(err instanceof Error ? err.message : "Request failed");
|
|
1829
|
-
if (attempt < this.maxRetries)
|
|
1845
|
+
if (canRetry && !methodOpts?.signal?.aborted && attempt < this.maxRetries) {
|
|
1846
|
+
retryAfterMs = void 0;
|
|
1847
|
+
continue;
|
|
1848
|
+
}
|
|
1830
1849
|
throw lastError;
|
|
1831
1850
|
}
|
|
1832
1851
|
if (response.ok) {
|
|
@@ -1846,7 +1865,14 @@ var ShipmailClient = class {
|
|
|
1846
1865
|
retryable: isRetryableStatus(response.status)
|
|
1847
1866
|
});
|
|
1848
1867
|
}
|
|
1849
|
-
|
|
1868
|
+
retryAfterMs = parseRetryAfter(response.headers.get("Retry-After"));
|
|
1869
|
+
if (retryAfterMs === void 0 && lastError instanceof RateLimitError) {
|
|
1870
|
+
const seconds = lastError.retryAfter;
|
|
1871
|
+
if (typeof seconds === "number" && Number.isFinite(seconds) && seconds >= 0) {
|
|
1872
|
+
retryAfterMs = seconds * 1e3;
|
|
1873
|
+
}
|
|
1874
|
+
}
|
|
1875
|
+
if (!canRetry || !lastError.retryable || attempt >= this.maxRetries || retryAfterMs !== void 0 && retryAfterMs > MAX_RETRY_AFTER_MS) {
|
|
1850
1876
|
throw lastError;
|
|
1851
1877
|
}
|
|
1852
1878
|
}
|