@fetchkit/ffetch 4.0.12 → 4.2.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 +23 -1
- package/dist/index.cjs +101 -8
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +13 -4
- package/dist/index.d.ts +13 -4
- package/dist/index.js +101 -8
- package/dist/index.js.map +1 -1
- package/dist/index.min.js +1 -1
- package/dist/index.min.js.map +1 -1
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -20,6 +20,7 @@ ffetch can wrap any fetch-compatible implementation (native fetch, node-fetch, u
|
|
|
20
20
|
- **Timeouts** – per-request or global
|
|
21
21
|
- **Retries** – exponential backoff + jitter
|
|
22
22
|
- **Circuit breaker** – automatic failure protection
|
|
23
|
+
- **Deduplication** – automatic deduping of in-flight identical requests
|
|
23
24
|
- **Hooks** – logging, auth, metrics, request/response transformation
|
|
24
25
|
- **Pending requests** – real-time monitoring of active requests
|
|
25
26
|
- **Per-request overrides** – customize behavior on a per-request basis
|
|
@@ -40,16 +41,23 @@ npm install @fetchkit/ffetch
|
|
|
40
41
|
```typescript
|
|
41
42
|
import createClient from '@fetchkit/ffetch'
|
|
42
43
|
|
|
43
|
-
// Create a client with timeout and
|
|
44
|
+
// Create a client with timeout, retries, and deduplication
|
|
44
45
|
const api = createClient({
|
|
45
46
|
timeout: 5000,
|
|
46
47
|
retries: 3,
|
|
48
|
+
dedupe: true, // Enable deduplication globally
|
|
47
49
|
retryDelay: ({ attempt }) => 2 ** attempt * 100 + Math.random() * 100,
|
|
48
50
|
})
|
|
49
51
|
|
|
50
52
|
// Make requests
|
|
51
53
|
const response = await api('https://api.example.com/users')
|
|
52
54
|
const data = await response.json()
|
|
55
|
+
|
|
56
|
+
// Deduplication example: these two requests will be deduped
|
|
57
|
+
const p1 = api('https://api.example.com/data')
|
|
58
|
+
const p2 = api('https://api.example.com/data')
|
|
59
|
+
const [r1, r2] = await Promise.all([p1, p2])
|
|
60
|
+
// Only one fetch will occur; both promises resolve to the same response
|
|
53
61
|
```
|
|
54
62
|
|
|
55
63
|
### Using a Custom fetchHandler (SSR, metaframeworks, or polyfills)
|
|
@@ -79,6 +87,8 @@ const response = await api('/api/data')
|
|
|
79
87
|
const client = createClient({
|
|
80
88
|
timeout: 10000,
|
|
81
89
|
retries: 2,
|
|
90
|
+
dedupe: true,
|
|
91
|
+
dedupeHashFn: (params) => `${params.method}|${params.url}|${params.body}`,
|
|
82
92
|
circuit: { threshold: 5, reset: 30000 },
|
|
83
93
|
fetchHandler: fetch, // Use custom fetch if needed
|
|
84
94
|
hooks: {
|
|
@@ -120,6 +130,7 @@ Native `fetch`'s controversial behavior of not throwing errors for HTTP error st
|
|
|
120
130
|
| --------------------------------------------- | ------------------------------------------------------------------------- |
|
|
121
131
|
| **[Complete Documentation](./docs/index.md)** | **Start here** - Documentation index and overview |
|
|
122
132
|
| **[API Reference](./docs/api.md)** | Complete API documentation and configuration options |
|
|
133
|
+
| **[Deduplication](./docs/deduplication.md)** | How deduplication works, config, custom hash, limitations |
|
|
123
134
|
| **[Error Handling](./docs/errorhandling.md)** | Strategies for managing errors, including `throwOnHttpError` |
|
|
124
135
|
| **[Advanced Features](./docs/advanced.md)** | Per-request overrides, pending requests, circuit breakers, custom errors |
|
|
125
136
|
| **[Hooks & Transformation](./docs/hooks.md)** | Lifecycle hooks, authentication, logging, request/response transformation |
|
|
@@ -157,6 +168,16 @@ npm install abort-controller-x
|
|
|
157
168
|
</script>
|
|
158
169
|
```
|
|
159
170
|
|
|
171
|
+
## Deduplication Limitations
|
|
172
|
+
|
|
173
|
+
- Deduplication is **off** by default. Enable it via the `dedupe` option.
|
|
174
|
+
- The default hash function is `dedupeRequestHash`, which handles common body types and skips deduplication for streams and FormData.
|
|
175
|
+
- **Stream bodies** (`ReadableStream`, `FormData`): Deduplication is skipped for requests with these body types, as they cannot be reliably hashed or replayed.
|
|
176
|
+
- **Non-idempotent requests**: Use deduplication with caution for non-idempotent methods (e.g., POST), as it may suppress multiple intended requests.
|
|
177
|
+
- **Custom hash function**: Ensure your hash function uniquely identifies requests to avoid accidental deduplication.
|
|
178
|
+
|
|
179
|
+
See [deduplication.md](./docs/deduplication.md) for full details.
|
|
180
|
+
|
|
160
181
|
## Fetch vs. Axios vs. `ffetch`
|
|
161
182
|
|
|
162
183
|
| Feature | Native Fetch | Axios | ffetch |
|
|
@@ -164,6 +185,7 @@ npm install abort-controller-x
|
|
|
164
185
|
| Timeouts | ❌ Manual AbortController | ✅ Built-in | ✅ Built-in with fallbacks |
|
|
165
186
|
| Retries | ❌ Manual implementation | ❌ Manual or plugins | ✅ Smart exponential backoff |
|
|
166
187
|
| Circuit Breaker | ❌ Not available | ❌ Manual or plugins | ✅ Automatic failure protection |
|
|
188
|
+
| Deduplication | ❌ Not available | ❌ Not available | ✅ Automatic deduplication of in-flight identical requests |
|
|
167
189
|
| Request Monitoring | ❌ Manual tracking | ❌ Manual tracking | ✅ Built-in pending requests |
|
|
168
190
|
| Error Types | ❌ Generic errors | ⚠️ HTTP errors only | ✅ Specific error classes |
|
|
169
191
|
| TypeScript | ⚠️ Basic types | ⚠️ Basic types | ✅ Full type safety |
|
package/dist/index.cjs
CHANGED
|
@@ -87,6 +87,38 @@ __export(index_exports, {
|
|
|
87
87
|
});
|
|
88
88
|
module.exports = __toCommonJS(index_exports);
|
|
89
89
|
|
|
90
|
+
// src/dedupeRequestHash.ts
|
|
91
|
+
function dedupeRequestHash(params) {
|
|
92
|
+
const { method, url, body } = params;
|
|
93
|
+
let bodyString = "";
|
|
94
|
+
if (body instanceof FormData) {
|
|
95
|
+
return void 0;
|
|
96
|
+
}
|
|
97
|
+
if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream) {
|
|
98
|
+
return void 0;
|
|
99
|
+
}
|
|
100
|
+
if (typeof body === "string") {
|
|
101
|
+
bodyString = body;
|
|
102
|
+
} else if (body instanceof URLSearchParams) {
|
|
103
|
+
bodyString = body.toString();
|
|
104
|
+
} else if (body instanceof ArrayBuffer) {
|
|
105
|
+
bodyString = Buffer.from(body).toString("base64");
|
|
106
|
+
} else if (body instanceof Uint8Array) {
|
|
107
|
+
bodyString = Buffer.from(body).toString("base64");
|
|
108
|
+
} else if (body instanceof Blob) {
|
|
109
|
+
bodyString = `[blob:${body.type}:${body.size}]`;
|
|
110
|
+
} else if (body == null) {
|
|
111
|
+
bodyString = "";
|
|
112
|
+
} else {
|
|
113
|
+
try {
|
|
114
|
+
bodyString = JSON.stringify(body);
|
|
115
|
+
} catch {
|
|
116
|
+
bodyString = "[unserializable-body]";
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
return `${method.toUpperCase()}|${url}|${bodyString}`;
|
|
120
|
+
}
|
|
121
|
+
|
|
90
122
|
// src/retry.ts
|
|
91
123
|
var defaultDelay = (ctx) => {
|
|
92
124
|
const retryAfter = ctx.response?.headers.get("Retry-After");
|
|
@@ -217,6 +249,7 @@ var CircuitBreaker = class {
|
|
|
217
249
|
// src/client.ts
|
|
218
250
|
init_error();
|
|
219
251
|
function createClient(opts = {}) {
|
|
252
|
+
const dedupeMap = /* @__PURE__ */ new Map();
|
|
220
253
|
const {
|
|
221
254
|
timeout: clientDefaultTimeout = 5e3,
|
|
222
255
|
retries: clientDefaultRetries = 0,
|
|
@@ -224,7 +257,9 @@ function createClient(opts = {}) {
|
|
|
224
257
|
shouldRetry: clientDefaultShouldRetry = shouldRetry,
|
|
225
258
|
hooks: clientDefaultHooks = {},
|
|
226
259
|
circuit: clientDefaultCircuit,
|
|
227
|
-
fetchHandler
|
|
260
|
+
fetchHandler,
|
|
261
|
+
dedupe = false,
|
|
262
|
+
dedupeHashFn = dedupeRequestHash
|
|
228
263
|
} = opts;
|
|
229
264
|
const breaker = clientDefaultCircuit ? new CircuitBreaker(
|
|
230
265
|
clientDefaultCircuit.threshold,
|
|
@@ -243,7 +278,48 @@ function createClient(opts = {}) {
|
|
|
243
278
|
}
|
|
244
279
|
}
|
|
245
280
|
const client = async (input, init = {}) => {
|
|
281
|
+
const effectiveDedupe = typeof init.dedupe !== "undefined" ? init.dedupe : dedupe;
|
|
282
|
+
const effectiveDedupeHashFn = init.dedupeHashFn || dedupeHashFn;
|
|
283
|
+
let dedupeKey;
|
|
246
284
|
let request = new Request(input, init);
|
|
285
|
+
if (effectiveDedupe) {
|
|
286
|
+
dedupeKey = effectiveDedupeHashFn({
|
|
287
|
+
method: request.method,
|
|
288
|
+
url: request.url,
|
|
289
|
+
body: init.body ?? null,
|
|
290
|
+
headers: request.headers,
|
|
291
|
+
signal: init.signal === void 0 || init.signal === null ? void 0 : init.signal,
|
|
292
|
+
requestInit: init,
|
|
293
|
+
request
|
|
294
|
+
});
|
|
295
|
+
if (dedupeKey) {
|
|
296
|
+
if (dedupeMap.has(dedupeKey)) {
|
|
297
|
+
return dedupeMap.get(dedupeKey).promise;
|
|
298
|
+
}
|
|
299
|
+
let settled = false;
|
|
300
|
+
let resolveFn;
|
|
301
|
+
let rejectFn;
|
|
302
|
+
const inFlightPromise = new Promise((resolve, reject) => {
|
|
303
|
+
resolveFn = (value) => {
|
|
304
|
+
if (!settled) {
|
|
305
|
+
settled = true;
|
|
306
|
+
resolve(value);
|
|
307
|
+
}
|
|
308
|
+
};
|
|
309
|
+
rejectFn = (reason) => {
|
|
310
|
+
if (!settled) {
|
|
311
|
+
settled = true;
|
|
312
|
+
reject(reason);
|
|
313
|
+
}
|
|
314
|
+
};
|
|
315
|
+
});
|
|
316
|
+
dedupeMap.set(dedupeKey, {
|
|
317
|
+
promise: inFlightPromise,
|
|
318
|
+
resolve: resolveFn,
|
|
319
|
+
reject: rejectFn
|
|
320
|
+
});
|
|
321
|
+
}
|
|
322
|
+
}
|
|
247
323
|
const effectiveHooks = { ...clientDefaultHooks, ...init.hooks || {} };
|
|
248
324
|
if (effectiveHooks.transformRequest) {
|
|
249
325
|
request = await effectiveHooks.transformRequest(request);
|
|
@@ -340,7 +416,7 @@ function createClient(opts = {}) {
|
|
|
340
416
|
signal: combinedSignal
|
|
341
417
|
});
|
|
342
418
|
try {
|
|
343
|
-
const handler = fetchHandler ?? fetch;
|
|
419
|
+
const handler = init.fetchHandler ?? fetchHandler ?? fetch;
|
|
344
420
|
const response = await handler(reqWithSignal);
|
|
345
421
|
lastResponse = response;
|
|
346
422
|
if (breaker && (response.status >= 500 || response.status === 429)) {
|
|
@@ -426,7 +502,7 @@ function createClient(opts = {}) {
|
|
|
426
502
|
throw retryErr;
|
|
427
503
|
}
|
|
428
504
|
};
|
|
429
|
-
const
|
|
505
|
+
const actualPromise = breaker ? breaker.invoke(retryWithHooks).catch(async (err) => {
|
|
430
506
|
if (err instanceof CircuitOpenError) {
|
|
431
507
|
await effectiveHooks.onCircuitOpen?.(request);
|
|
432
508
|
await effectiveHooks.onError?.(request, err);
|
|
@@ -437,17 +513,34 @@ function createClient(opts = {}) {
|
|
|
437
513
|
}
|
|
438
514
|
throw err;
|
|
439
515
|
}) : retryWithHooks();
|
|
440
|
-
|
|
441
|
-
|
|
516
|
+
if (effectiveDedupe && dedupeKey && dedupeMap.has(dedupeKey)) {
|
|
517
|
+
const entry = dedupeMap.get(dedupeKey);
|
|
518
|
+
if (entry) {
|
|
519
|
+
actualPromise.then(
|
|
520
|
+
(result) => entry.resolve(result),
|
|
521
|
+
(error) => entry.reject(error)
|
|
522
|
+
);
|
|
523
|
+
dedupeMap.set(dedupeKey, {
|
|
524
|
+
promise: actualPromise,
|
|
525
|
+
resolve: entry.resolve,
|
|
526
|
+
reject: entry.reject
|
|
527
|
+
});
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
const pendingEntry = {
|
|
531
|
+
promise: actualPromise,
|
|
442
532
|
request,
|
|
443
533
|
controller
|
|
444
534
|
};
|
|
445
|
-
pendingRequests.push(
|
|
446
|
-
return
|
|
447
|
-
const index = pendingRequests.indexOf(
|
|
535
|
+
pendingRequests.push(pendingEntry);
|
|
536
|
+
return actualPromise.finally(() => {
|
|
537
|
+
const index = pendingRequests.indexOf(pendingEntry);
|
|
448
538
|
if (index > -1) {
|
|
449
539
|
pendingRequests.splice(index, 1);
|
|
450
540
|
}
|
|
541
|
+
if (effectiveDedupe && dedupeKey && dedupeMap.get(dedupeKey)?.promise === actualPromise) {
|
|
542
|
+
dedupeMap.delete(dedupeKey);
|
|
543
|
+
}
|
|
451
544
|
});
|
|
452
545
|
};
|
|
453
546
|
Object.defineProperty(client, "pendingRequests", {
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/error.ts","../src/index.ts","../src/retry.ts","../src/should-retry.ts","../src/circuit.ts","../src/client.ts"],"sourcesContent":["// Base error class to reduce duplication\nclass BaseError extends Error {\n public cause?: unknown\n constructor(name: string, message: string, cause?: unknown) {\n super(message)\n this.name = name\n if (cause !== undefined) this.cause = cause\n }\n}\n\nexport class TimeoutError extends BaseError {\n constructor(message = 'Request timed out', cause?: unknown) {\n super('TimeoutError', message, cause)\n }\n}\n\nexport class CircuitOpenError extends BaseError {\n constructor(message = 'Circuit is open', cause?: unknown) {\n super('CircuitOpenError', message, cause)\n }\n}\n\nexport class AbortError extends BaseError {\n constructor(message = 'Request was aborted', cause?: unknown) {\n super('AbortError', message, cause)\n }\n}\n\nexport class RetryLimitError extends BaseError {\n constructor(message = 'Retry limit reached', cause?: unknown) {\n super('RetryLimitError', message, cause)\n }\n}\n\nexport class NetworkError extends BaseError {\n constructor(message = 'Network error occurred', cause?: unknown) {\n super('NetworkError', message, cause)\n }\n}\n\nexport class HttpError extends BaseError {\n constructor(message = 'HTTP error occurred', cause?: unknown) {\n super('HttpError', message, cause)\n }\n}\n","export type { FFetch, FFetchOptions } from './types'\nexport type { Hooks } from './hooks'\n\nimport { createClient } from './client'\nexport { createClient } from './client'\n\nexport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error'\n\nexport default createClient\n","import type { RetryContext } from './types.js'\n\nexport type RetryDelay = number | ((ctx: RetryContext) => number)\n\nexport const defaultDelay: RetryDelay = (ctx) => {\n const retryAfter = ctx.response?.headers.get('Retry-After')\n if (retryAfter) {\n const seconds = parseInt(retryAfter, 10)\n if (!isNaN(seconds)) return seconds * 1000\n const date = Date.parse(retryAfter)\n if (!isNaN(date)) return Math.max(0, date - Date.now())\n }\n return 2 ** ctx.attempt * 200 + Math.random() * 100\n}\n\nexport async function retry(\n fn: () => Promise<Response>,\n retries: number,\n delay: RetryDelay,\n shouldRetry: (ctx: RetryContext) => boolean = () => true,\n request: Request\n): Promise<Response> {\n let lastErr: unknown\n let lastRes: Response | undefined\n\n for (let i = 0; i <= retries; i++) {\n const ctx: RetryContext = {\n attempt: i + 1,\n request,\n response: lastRes,\n error: lastErr,\n }\n try {\n lastRes = await fn()\n ctx.response = lastRes\n ctx.error = undefined\n if (i < retries && shouldRetry(ctx)) {\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n continue\n }\n return lastRes\n } catch (err) {\n lastErr = err\n ctx.error = err\n if (i === retries || !shouldRetry(ctx)) throw err\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n throw lastErr\n}\n","import { AbortError, CircuitOpenError, TimeoutError } from './error.js'\nimport type { RetryContext } from './types.js'\n\nexport function shouldRetry(ctx: RetryContext): boolean {\n const { error, response } = ctx\n if (\n error instanceof AbortError ||\n error instanceof CircuitOpenError ||\n error instanceof TimeoutError\n )\n return false\n if (!response) return true // network error\n return response.status >= 500 || response.status === 429\n}\n","import { CircuitOpenError, RetryLimitError } from './error.js'\n\nexport class CircuitBreaker {\n private failures = 0\n private nextAttempt = 0\n private isOpen = false\n\n // Returns true if the circuit breaker is currently open (blocking requests).\n get open(): boolean {\n return this.isOpen\n }\n private hooks?: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }\n private lastSuccessRequest?: Request\n private lastOpenRequest?: Request\n\n constructor(\n private threshold: number,\n private resetTimeout: number\n ) {}\n\n // Call this after each request to record the result and update circuit state.\n // Returns true if the result was counted as a failure.\n recordResult(response?: Response, error?: unknown, req?: Request): boolean {\n // Count thrown errors (network, abort, timeout) as failures\n if (error && !(error instanceof RetryLimitError)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Count HTTP 5xx and 429 responses as failures\n if (response && (response.status >= 500 || response.status === 429)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Otherwise, count as success\n if (req) this.setLastSuccessRequest(req)\n this.onSuccess()\n return false\n }\n\n setHooks(hooks: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }) {\n this.hooks = hooks\n }\n setLastOpenRequest(req: Request) {\n this.lastOpenRequest = req\n }\n\n setLastSuccessRequest(req: Request) {\n this.lastSuccessRequest = req\n }\n\n async invoke<T>(fn: () => Promise<T>): Promise<T> {\n if (Date.now() < this.nextAttempt)\n throw new CircuitOpenError('Circuit is open')\n try {\n const res = await fn()\n this.onSuccess()\n return res\n } catch (err) {\n this.onFailure()\n throw err\n }\n }\n\n private onSuccess() {\n const wasOpen = this.isOpen\n this.failures = 0\n if (wasOpen) {\n this.isOpen = false\n if (this.hooks?.onCircuitClose && this.lastSuccessRequest) {\n this.hooks.onCircuitClose(this.lastSuccessRequest)\n }\n }\n this.lastSuccessRequest = undefined\n }\n\n private onFailure() {\n this.failures++\n if (this.failures >= this.threshold) {\n this.nextAttempt = Date.now() + this.resetTimeout\n this.isOpen = true\n if (this.hooks?.onCircuitOpen && this.lastOpenRequest) {\n this.hooks.onCircuitOpen(this.lastOpenRequest)\n }\n }\n }\n}\n","import type {\n FFetchOptions,\n FFetch,\n FFetchRequestInit,\n PendingRequest,\n} from './types.js'\nimport { retry, defaultDelay } from './retry.js'\nimport { shouldRetry as defaultShouldRetry } from './should-retry.js'\nimport { CircuitBreaker } from './circuit.js'\nimport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error.js'\n\nexport function createClient(opts: FFetchOptions = {}): FFetch {\n const {\n timeout: clientDefaultTimeout = 5_000,\n retries: clientDefaultRetries = 0,\n retryDelay: clientDefaultRetryDelay = defaultDelay,\n shouldRetry: clientDefaultShouldRetry = defaultShouldRetry,\n hooks: clientDefaultHooks = {},\n circuit: clientDefaultCircuit,\n fetchHandler,\n } = opts\n\n const breaker = clientDefaultCircuit\n ? new CircuitBreaker(\n clientDefaultCircuit.threshold,\n clientDefaultCircuit.reset\n )\n : null\n\n if (\n breaker &&\n (clientDefaultHooks.onCircuitClose || clientDefaultHooks.onCircuitOpen)\n ) {\n breaker.setHooks({\n onCircuitClose: clientDefaultHooks.onCircuitClose,\n onCircuitOpen: clientDefaultHooks.onCircuitOpen,\n })\n }\n\n const pendingRequests: PendingRequest[] = []\n\n // Helper to abort all pending requests\n function abortAll() {\n for (const entry of pendingRequests) {\n entry.controller?.abort()\n }\n }\n\n const client = async (\n input: RequestInfo | URL,\n init: FFetchRequestInit = {}\n ) => {\n // No longer require AbortSignal.timeout - we'll implement it manually if needed\n let request = new Request(input, init)\n\n // Merge hooks: per-request hooks override client hooks, but fallback to client hooks\n const effectiveHooks = { ...clientDefaultHooks, ...(init.hooks || {}) }\n if (effectiveHooks.transformRequest) {\n request = await effectiveHooks.transformRequest(request)\n }\n await effectiveHooks.before?.(request)\n\n // Determine throwOnHttpError (per-request overrides client default)\n const effectiveThrowOnHttpError =\n typeof init.throwOnHttpError !== 'undefined'\n ? init.throwOnHttpError\n : (opts.throwOnHttpError ?? false)\n\n // Create timeout signal (manual implementation if AbortSignal.timeout not available)\n function createTimeoutSignal(timeout: number): AbortSignal {\n if (typeof AbortSignal?.timeout === 'function') {\n return AbortSignal.timeout(timeout)\n }\n\n // Manual implementation for older environments\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n // Clean up timeout if signal is aborted early by other means\n controller.signal.addEventListener(\n 'abort',\n () => clearTimeout(timeoutId),\n { once: true }\n )\n\n return controller.signal\n }\n\n // AbortSignal.timeout/any logic ---\n const effectiveTimeout = init.timeout ?? clientDefaultTimeout\n const userSignal = init.signal\n const transformedSignal = request.signal // Extract signal from transformed request\n let timeoutSignal: AbortSignal | undefined = undefined\n let combinedSignal: AbortSignal | undefined = undefined\n let controller: AbortController | undefined = undefined\n\n if (effectiveTimeout > 0) {\n timeoutSignal = createTimeoutSignal(effectiveTimeout)\n }\n\n // Collect all signals that need to be combined\n const signals: AbortSignal[] = []\n if (userSignal) signals.push(userSignal)\n if (transformedSignal && transformedSignal !== userSignal) {\n signals.push(transformedSignal)\n }\n if (timeoutSignal) signals.push(timeoutSignal)\n\n // Use AbortSignal.any for signal combination. Requires native support or a polyfill.\n // If not available, instruct users to install a polyfill for environments lacking AbortSignal.any.\n // there are always 1 or more signals\n if (signals.length === 1) {\n combinedSignal = signals[0]\n controller = new AbortController()\n } else {\n if (typeof AbortSignal.any !== 'function') {\n throw new Error(\n 'AbortSignal.any is required for combining multiple signals. Please install a polyfill for environments that do not support it.'\n )\n }\n combinedSignal = AbortSignal.any(signals)\n controller = new AbortController()\n }\n const retryWithHooks = async () => {\n const effectiveRetries = init.retries ?? clientDefaultRetries\n const effectiveRetryDelay =\n typeof init.retryDelay !== 'undefined'\n ? init.retryDelay\n : clientDefaultRetryDelay\n const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry\n\n // Wrap shouldRetry to call onRetry hook\n let attempt = 0\n const shouldRetryWithHook = (ctx: import('./types').RetryContext) => {\n attempt = ctx.attempt\n const retrying = effectiveShouldRetry(ctx)\n if (retrying && attempt <= effectiveRetries) {\n effectiveHooks.onRetry?.(\n request,\n attempt - 1,\n ctx.error,\n ctx.response\n )\n }\n return retrying\n }\n\n let lastResponse: Response | undefined = undefined\n try {\n let res = await retry(\n async () => {\n // Check for aborts before making the request\n if (userSignal?.aborted) {\n effectiveHooks.onAbort?.(request)\n // Throw AbortError for user aborts, no cause needed\n throw new AbortError('Request was aborted by user')\n }\n if (timeoutSignal?.aborted) {\n effectiveHooks.onTimeout?.(request)\n throw new TimeoutError('signal timed out')\n }\n if (typeof combinedSignal?.throwIfAborted === 'function') {\n combinedSignal.throwIfAborted()\n } else if (combinedSignal?.aborted) {\n if (userSignal?.aborted) {\n effectiveHooks.onAbort?.(request)\n throw new AbortError('Request was aborted by user')\n } else if (timeoutSignal?.aborted) {\n effectiveHooks.onTimeout?.(request)\n throw new TimeoutError('signal timed out')\n } else {\n throw new AbortError(\n 'Request was aborted',\n new DOMException('Aborted', 'AbortError')\n )\n }\n }\n const reqWithSignal = new Request(request, {\n signal: combinedSignal,\n })\n try {\n const handler = fetchHandler ?? fetch\n const response = await handler(reqWithSignal)\n lastResponse = response\n if (\n breaker &&\n (response.status >= 500 || response.status === 429)\n ) {\n breaker.recordResult(response, undefined, request)\n }\n return response\n } catch (err) {\n if (breaker) breaker.recordResult(undefined, err, request)\n if (err instanceof DOMException && err.name === 'AbortError') {\n if (\n timeoutSignal?.aborted &&\n (!userSignal || !userSignal.aborted)\n ) {\n effectiveHooks.onTimeout?.(request)\n throw new TimeoutError('signal timed out', err)\n } else if (userSignal?.aborted) {\n effectiveHooks.onAbort?.(request)\n throw new AbortError('Request was aborted by user')\n } else {\n throw new AbortError(\n 'Request was aborted',\n new DOMException('Aborted', 'AbortError')\n )\n }\n } else if (\n err instanceof TypeError &&\n /NetworkError|network error|failed to fetch|lost connection|NetworkError when attempting to fetch resource/i.test(\n err.message\n )\n ) {\n throw new NetworkError(err.message, err)\n }\n throw err\n }\n },\n effectiveRetries,\n effectiveRetryDelay,\n shouldRetryWithHook,\n request\n )\n if (effectiveHooks.transformResponse) {\n res = await effectiveHooks.transformResponse(res, request)\n }\n await effectiveHooks.after?.(request, res)\n await effectiveHooks.onComplete?.(request, res, undefined)\n // After all retries, if throwOnHttpError is true and final response is error, throw HttpError\n if (\n effectiveThrowOnHttpError &&\n ((res.status >= 400 && res.status < 500 && res.status !== 429) ||\n res.status >= 500 ||\n res.status === 429)\n ) {\n const { HttpError } = await import('./error.js')\n throw new HttpError(\n `HTTP error: ${res.status} ${res.statusText}`,\n res\n )\n }\n return res\n } catch (err: unknown) {\n // If lastResponse is set (any status), return or throw as needed\n if (lastResponse) {\n const resp = lastResponse as Response\n // Only throw if throwOnHttpError is true and final response is 4xx (not 429), 5xx, or 429\n if (\n effectiveThrowOnHttpError &&\n ((resp.status >= 400 && resp.status < 500 && resp.status !== 429) ||\n resp.status >= 500 ||\n resp.status === 429)\n ) {\n const { HttpError } = await import('./error.js')\n throw new HttpError(\n `HTTP error: ${resp.status} ${resp.statusText}`,\n resp\n )\n }\n return resp\n }\n // If the error is a known error type, re-throw it directly and call correct hook\n if (err instanceof TimeoutError) {\n await effectiveHooks.onTimeout?.(request)\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n throw err\n }\n if (err instanceof AbortError) {\n await effectiveHooks.onAbort?.(request)\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n throw err\n }\n if (err instanceof NetworkError) {\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n throw err\n }\n // Otherwise, wrap in RetryLimitError\n const retryErr = new RetryLimitError(\n typeof err === 'object' &&\n err &&\n 'message' in err &&\n typeof (err as { message?: unknown }).message === 'string'\n ? (err as { message: string }).message\n : 'Retry limit reached',\n err\n )\n await effectiveHooks.onError?.(request, retryErr)\n await effectiveHooks.onComplete?.(request, undefined, retryErr)\n throw retryErr\n }\n }\n\n const promise = breaker\n ? breaker.invoke(retryWithHooks).catch(async (err: unknown) => {\n if (err instanceof CircuitOpenError) {\n await effectiveHooks.onCircuitOpen?.(request)\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n } else {\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n }\n throw err\n })\n : retryWithHooks()\n\n const entry: PendingRequest = {\n promise,\n request,\n controller,\n }\n pendingRequests.push(entry)\n\n return promise.finally(() => {\n const index = pendingRequests.indexOf(entry)\n if (index > -1) {\n pendingRequests.splice(index, 1)\n }\n })\n }\n\n // Add pendingRequests property to the client function (read-only)\n Object.defineProperty(client, 'pendingRequests', {\n get() {\n return pendingRequests\n },\n enumerable: false,\n configurable: false,\n })\n\n // Add abortAll method to the client function (read-only)\n Object.defineProperty(client, 'abortAll', {\n value: abortAll,\n writable: false,\n enumerable: false,\n configurable: false,\n })\n\n // Expose circuit breaker open state\n Object.defineProperty(client, 'circuitOpen', {\n get() {\n return breaker ? breaker.open : false\n },\n enumerable: true,\n })\n\n return client as FFetch\n}\n"],"mappings":"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","names":["shouldRetry","controller","HttpError"]}
|
|
1
|
+
{"version":3,"sources":["../src/error.ts","../src/index.ts","../src/dedupeRequestHash.ts","../src/retry.ts","../src/should-retry.ts","../src/circuit.ts","../src/client.ts"],"sourcesContent":["// Base error class to reduce duplication\nclass BaseError extends Error {\n public cause?: unknown\n constructor(name: string, message: string, cause?: unknown) {\n super(message)\n this.name = name\n if (cause !== undefined) this.cause = cause\n }\n}\n\nexport class TimeoutError extends BaseError {\n constructor(message = 'Request timed out', cause?: unknown) {\n super('TimeoutError', message, cause)\n }\n}\n\nexport class CircuitOpenError extends BaseError {\n constructor(message = 'Circuit is open', cause?: unknown) {\n super('CircuitOpenError', message, cause)\n }\n}\n\nexport class AbortError extends BaseError {\n constructor(message = 'Request was aborted', cause?: unknown) {\n super('AbortError', message, cause)\n }\n}\n\nexport class RetryLimitError extends BaseError {\n constructor(message = 'Retry limit reached', cause?: unknown) {\n super('RetryLimitError', message, cause)\n }\n}\n\nexport class NetworkError extends BaseError {\n constructor(message = 'Network error occurred', cause?: unknown) {\n super('NetworkError', message, cause)\n }\n}\n\nexport class HttpError extends BaseError {\n constructor(message = 'HTTP error occurred', cause?: unknown) {\n super('HttpError', message, cause)\n }\n}\n","export type { FFetch, FFetchOptions } from './types'\nexport type { DedupeHashParams } from './dedupeRequestHash'\nexport type { Hooks } from './hooks'\n\nimport { createClient } from './client'\nexport { createClient } from './client'\n\nexport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error'\n\nexport default createClient\n","export type DedupeHashParams = {\n method: string\n url: string\n body:\n | string\n | FormData\n | URLSearchParams\n | Blob\n | ArrayBuffer\n | BufferSource\n | null\n | ReadableStream<unknown>\n headers?: Headers | Record<string, string>\n signal?: AbortSignal\n requestInit?: RequestInit\n request?: Request\n}\n\nexport function dedupeRequestHash(\n params: DedupeHashParams\n): string | undefined {\n const { method, url, body } = params\n let bodyString = ''\n if (body instanceof FormData) {\n // Skip deduplication for FormData\n return undefined\n }\n // Skip deduplication for ReadableStream\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n return undefined\n }\n if (typeof body === 'string') {\n bodyString = body\n } else if (body instanceof URLSearchParams) {\n bodyString = body.toString()\n } else if (body instanceof ArrayBuffer) {\n bodyString = Buffer.from(body).toString('base64')\n } else if (body instanceof Uint8Array) {\n bodyString = Buffer.from(body).toString('base64')\n } else if (body instanceof Blob) {\n bodyString = `[blob:${body.type}:${body.size}]`\n } else if (body == null) {\n bodyString = ''\n } else {\n try {\n bodyString = JSON.stringify(body)\n } catch {\n bodyString = '[unserializable-body]'\n }\n }\n return `${method.toUpperCase()}|${url}|${bodyString}`\n}\n","import type { RetryContext } from './types.js'\n\nexport type RetryDelay = number | ((ctx: RetryContext) => number)\n\nexport const defaultDelay: RetryDelay = (ctx) => {\n const retryAfter = ctx.response?.headers.get('Retry-After')\n if (retryAfter) {\n const seconds = parseInt(retryAfter, 10)\n if (!isNaN(seconds)) return seconds * 1000\n const date = Date.parse(retryAfter)\n if (!isNaN(date)) return Math.max(0, date - Date.now())\n }\n return 2 ** ctx.attempt * 200 + Math.random() * 100\n}\n\nexport async function retry(\n fn: () => Promise<Response>,\n retries: number,\n delay: RetryDelay,\n shouldRetry: (ctx: RetryContext) => boolean = () => true,\n request: Request\n): Promise<Response> {\n let lastErr: unknown\n let lastRes: Response | undefined\n\n for (let i = 0; i <= retries; i++) {\n const ctx: RetryContext = {\n attempt: i + 1,\n request,\n response: lastRes,\n error: lastErr,\n }\n try {\n lastRes = await fn()\n ctx.response = lastRes\n ctx.error = undefined\n if (i < retries && shouldRetry(ctx)) {\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n continue\n }\n return lastRes\n } catch (err) {\n lastErr = err\n ctx.error = err\n if (i === retries || !shouldRetry(ctx)) throw err\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n throw lastErr\n}\n","import { AbortError, CircuitOpenError, TimeoutError } from './error.js'\nimport type { RetryContext } from './types.js'\n\nexport function shouldRetry(ctx: RetryContext): boolean {\n const { error, response } = ctx\n if (\n error instanceof AbortError ||\n error instanceof CircuitOpenError ||\n error instanceof TimeoutError\n )\n return false\n if (!response) return true // network error\n return response.status >= 500 || response.status === 429\n}\n","import { CircuitOpenError, RetryLimitError } from './error.js'\n\nexport class CircuitBreaker {\n private failures = 0\n private nextAttempt = 0\n private isOpen = false\n\n // Returns true if the circuit breaker is currently open (blocking requests).\n get open(): boolean {\n return this.isOpen\n }\n private hooks?: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }\n private lastSuccessRequest?: Request\n private lastOpenRequest?: Request\n\n constructor(\n private threshold: number,\n private resetTimeout: number\n ) {}\n\n // Call this after each request to record the result and update circuit state.\n // Returns true if the result was counted as a failure.\n recordResult(response?: Response, error?: unknown, req?: Request): boolean {\n // Count thrown errors (network, abort, timeout) as failures\n if (error && !(error instanceof RetryLimitError)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Count HTTP 5xx and 429 responses as failures\n if (response && (response.status >= 500 || response.status === 429)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Otherwise, count as success\n if (req) this.setLastSuccessRequest(req)\n this.onSuccess()\n return false\n }\n\n setHooks(hooks: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }) {\n this.hooks = hooks\n }\n setLastOpenRequest(req: Request) {\n this.lastOpenRequest = req\n }\n\n setLastSuccessRequest(req: Request) {\n this.lastSuccessRequest = req\n }\n\n async invoke<T>(fn: () => Promise<T>): Promise<T> {\n if (Date.now() < this.nextAttempt)\n throw new CircuitOpenError('Circuit is open')\n try {\n const res = await fn()\n this.onSuccess()\n return res\n } catch (err) {\n this.onFailure()\n throw err\n }\n }\n\n private onSuccess() {\n const wasOpen = this.isOpen\n this.failures = 0\n if (wasOpen) {\n this.isOpen = false\n if (this.hooks?.onCircuitClose && this.lastSuccessRequest) {\n this.hooks.onCircuitClose(this.lastSuccessRequest)\n }\n }\n this.lastSuccessRequest = undefined\n }\n\n private onFailure() {\n this.failures++\n if (this.failures >= this.threshold) {\n this.nextAttempt = Date.now() + this.resetTimeout\n this.isOpen = true\n if (this.hooks?.onCircuitOpen && this.lastOpenRequest) {\n this.hooks.onCircuitOpen(this.lastOpenRequest)\n }\n }\n }\n}\n","import type {\r\n FFetchOptions,\r\n FFetch,\r\n FFetchRequestInit,\r\n PendingRequest,\r\n} from './types.js'\r\nimport { dedupeRequestHash } from './dedupeRequestHash.js'\r\nimport { retry, defaultDelay } from './retry.js'\r\nimport { shouldRetry as defaultShouldRetry } from './should-retry.js'\r\nimport { CircuitBreaker } from './circuit.js'\r\nimport {\r\n TimeoutError,\r\n CircuitOpenError,\r\n AbortError,\r\n RetryLimitError,\r\n NetworkError,\r\n} from './error.js'\r\n\r\nexport function createClient(opts: FFetchOptions = {}): FFetch {\r\n // Track in-flight deduped requests and their resolvers\r\n const dedupeMap = new Map<\r\n string,\r\n {\r\n promise: Promise<Response>\r\n resolve: (value: Response | PromiseLike<Response>) => void\r\n reject: (reason?: unknown) => void\r\n }\r\n >()\r\n const {\r\n timeout: clientDefaultTimeout = 5_000,\r\n retries: clientDefaultRetries = 0,\r\n retryDelay: clientDefaultRetryDelay = defaultDelay,\r\n shouldRetry: clientDefaultShouldRetry = defaultShouldRetry,\r\n hooks: clientDefaultHooks = {},\r\n circuit: clientDefaultCircuit,\r\n fetchHandler,\r\n dedupe = false,\r\n dedupeHashFn = dedupeRequestHash,\r\n } = opts\r\n\r\n const breaker = clientDefaultCircuit\r\n ? new CircuitBreaker(\r\n clientDefaultCircuit.threshold,\r\n clientDefaultCircuit.reset\r\n )\r\n : null\r\n\r\n if (\r\n breaker &&\r\n (clientDefaultHooks.onCircuitClose || clientDefaultHooks.onCircuitOpen)\r\n ) {\r\n breaker.setHooks({\r\n onCircuitClose: clientDefaultHooks.onCircuitClose,\r\n onCircuitOpen: clientDefaultHooks.onCircuitOpen,\r\n })\r\n }\r\n\r\n const pendingRequests: PendingRequest[] = []\r\n\r\n // Helper to abort all pending requests\r\n function abortAll() {\r\n for (const entry of pendingRequests) {\r\n entry.controller?.abort()\r\n }\r\n }\r\n\r\n const client = async (\r\n input: RequestInfo | URL,\r\n init: FFetchRequestInit = {}\r\n ) => {\r\n // Deduplication logic\r\n const effectiveDedupe =\r\n typeof init.dedupe !== 'undefined' ? init.dedupe : dedupe\r\n const effectiveDedupeHashFn = init.dedupeHashFn || dedupeHashFn\r\n let dedupeKey: string | undefined\r\n\r\n let request = new Request(input, init)\r\n if (effectiveDedupe) {\r\n dedupeKey = effectiveDedupeHashFn({\r\n method: request.method,\r\n url: request.url,\r\n body: init.body ?? null,\r\n headers: request.headers,\r\n signal:\r\n init.signal === undefined || init.signal === null\r\n ? undefined\r\n : init.signal,\r\n requestInit: init,\r\n request,\r\n })\r\n if (dedupeKey) {\r\n if (dedupeMap.has(dedupeKey)) {\r\n return dedupeMap.get(dedupeKey)!.promise\r\n }\r\n let settled = false\r\n let resolveFn: (value: Response | PromiseLike<Response>) => void\r\n let rejectFn: (reason?: unknown) => void\r\n const inFlightPromise = new Promise<Response>((resolve, reject) => {\r\n resolveFn = (value) => {\r\n if (!settled) {\r\n settled = true\r\n resolve(value)\r\n }\r\n }\r\n rejectFn = (reason) => {\r\n if (!settled) {\r\n settled = true\r\n reject(reason)\r\n }\r\n }\r\n })\r\n dedupeMap.set(dedupeKey, {\r\n promise: inFlightPromise,\r\n resolve: resolveFn!,\r\n reject: rejectFn!,\r\n })\r\n }\r\n }\r\n\r\n // Merge hooks: per-request hooks override client hooks, but fallback to client hooks\r\n const effectiveHooks = { ...clientDefaultHooks, ...(init.hooks || {}) }\r\n if (effectiveHooks.transformRequest) {\r\n request = await effectiveHooks.transformRequest(request)\r\n }\r\n await effectiveHooks.before?.(request)\r\n\r\n // Determine throwOnHttpError (per-request overrides client default)\r\n const effectiveThrowOnHttpError =\r\n typeof init.throwOnHttpError !== 'undefined'\r\n ? init.throwOnHttpError\r\n : (opts.throwOnHttpError ?? false)\r\n\r\n // Create timeout signal (manual implementation if AbortSignal.timeout not available)\r\n function createTimeoutSignal(timeout: number): AbortSignal {\r\n if (typeof AbortSignal?.timeout === 'function') {\r\n return AbortSignal.timeout(timeout)\r\n }\r\n const controller = new AbortController()\r\n const timeoutId = setTimeout(() => controller.abort(), timeout)\r\n controller.signal.addEventListener(\r\n 'abort',\r\n () => clearTimeout(timeoutId),\r\n { once: true }\r\n )\r\n return controller.signal\r\n }\r\n\r\n // AbortSignal.timeout/any logic\r\n const effectiveTimeout = init.timeout ?? clientDefaultTimeout\r\n const userSignal = init.signal\r\n const transformedSignal = request.signal\r\n let timeoutSignal: AbortSignal | undefined = undefined\r\n let combinedSignal: AbortSignal | undefined = undefined\r\n let controller: AbortController | undefined = undefined\r\n\r\n if (effectiveTimeout > 0) {\r\n timeoutSignal = createTimeoutSignal(effectiveTimeout)\r\n }\r\n\r\n const signals: AbortSignal[] = []\r\n if (userSignal) signals.push(userSignal)\r\n if (transformedSignal && transformedSignal !== userSignal) {\r\n signals.push(transformedSignal)\r\n }\r\n if (timeoutSignal) signals.push(timeoutSignal)\r\n\r\n if (signals.length === 1) {\r\n combinedSignal = signals[0]\r\n controller = new AbortController()\r\n } else {\r\n if (typeof AbortSignal.any !== 'function') {\r\n throw new Error(\r\n 'AbortSignal.any is required for combining multiple signals. Please install a polyfill for environments that do not support it.'\r\n )\r\n }\r\n combinedSignal = AbortSignal.any(signals)\r\n controller = new AbortController()\r\n }\r\n\r\n const retryWithHooks = async () => {\r\n const effectiveRetries = init.retries ?? clientDefaultRetries\r\n const effectiveRetryDelay =\r\n typeof init.retryDelay !== 'undefined'\r\n ? init.retryDelay\r\n : clientDefaultRetryDelay\r\n const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry\r\n\r\n let attempt = 0\r\n const shouldRetryWithHook = (ctx: import('./types').RetryContext) => {\r\n attempt = ctx.attempt\r\n const retrying = effectiveShouldRetry(ctx)\r\n if (retrying && attempt <= effectiveRetries) {\r\n effectiveHooks.onRetry?.(\r\n request,\r\n attempt - 1,\r\n ctx.error,\r\n ctx.response\r\n )\r\n }\r\n return retrying\r\n }\r\n\r\n let lastResponse: Response | undefined = undefined\r\n try {\r\n let res = await retry(\r\n async () => {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n }\r\n if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n }\r\n if (typeof combinedSignal?.throwIfAborted === 'function') {\r\n combinedSignal.throwIfAborted()\r\n } else if (combinedSignal?.aborted) {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n }\r\n const reqWithSignal = new Request(request, {\r\n signal: combinedSignal,\r\n })\r\n try {\r\n const handler = init.fetchHandler ?? fetchHandler ?? fetch\r\n const response = await handler(reqWithSignal)\r\n lastResponse = response\r\n if (\r\n breaker &&\r\n (response.status >= 500 || response.status === 429)\r\n ) {\r\n breaker.recordResult(response, undefined, request)\r\n }\r\n return response\r\n } catch (err) {\r\n if (breaker) breaker.recordResult(undefined, err, request)\r\n if (err instanceof DOMException && err.name === 'AbortError') {\r\n if (\r\n timeoutSignal?.aborted &&\r\n (!userSignal || !userSignal.aborted)\r\n ) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out', err)\r\n } else if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n } else if (\r\n err instanceof TypeError &&\r\n /NetworkError|network error|failed to fetch|lost connection|NetworkError when attempting to fetch resource/i.test(\r\n err.message\r\n )\r\n ) {\r\n throw new NetworkError(err.message, err)\r\n }\r\n throw err\r\n }\r\n },\r\n effectiveRetries,\r\n effectiveRetryDelay,\r\n shouldRetryWithHook,\r\n request\r\n )\r\n if (effectiveHooks.transformResponse) {\r\n res = await effectiveHooks.transformResponse(res, request)\r\n }\r\n await effectiveHooks.after?.(request, res)\r\n await effectiveHooks.onComplete?.(request, res, undefined)\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((res.status >= 400 && res.status < 500 && res.status !== 429) ||\r\n res.status >= 500 ||\r\n res.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${res.status} ${res.statusText}`,\r\n res\r\n )\r\n }\r\n return res\r\n } catch (err: unknown) {\r\n if (lastResponse) {\r\n const resp = lastResponse as Response\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((resp.status >= 400 && resp.status < 500 && resp.status !== 429) ||\r\n resp.status >= 500 ||\r\n resp.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${resp.status} ${resp.statusText}`,\r\n resp\r\n )\r\n }\r\n return resp\r\n }\r\n if (err instanceof TimeoutError) {\r\n await effectiveHooks.onTimeout?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof AbortError) {\r\n await effectiveHooks.onAbort?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof NetworkError) {\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n const retryErr = new RetryLimitError(\r\n typeof err === 'object' &&\r\n err &&\r\n 'message' in err &&\r\n typeof (err as { message?: unknown }).message === 'string'\r\n ? (err as { message: string }).message\r\n : 'Retry limit reached',\r\n err\r\n )\r\n await effectiveHooks.onError?.(request, retryErr)\r\n await effectiveHooks.onComplete?.(request, undefined, retryErr)\r\n throw retryErr\r\n }\r\n }\r\n\r\n const actualPromise = breaker\r\n ? breaker.invoke(retryWithHooks).catch(async (err: unknown) => {\r\n if (err instanceof CircuitOpenError) {\r\n await effectiveHooks.onCircuitOpen?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n } else {\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n }\r\n throw err\r\n })\r\n : retryWithHooks()\r\n\r\n // If deduplication is enabled and dedupeKey is set, resolve/reject the in-flight promise\r\n if (effectiveDedupe && dedupeKey && dedupeMap.has(dedupeKey)) {\r\n const entry = dedupeMap.get(dedupeKey)\r\n if (entry) {\r\n actualPromise.then(\r\n (result) => entry.resolve(result),\r\n (error) => entry.reject(error)\r\n )\r\n // Replace the placeholder with the actual promise for future requests\r\n dedupeMap.set(dedupeKey, {\r\n promise: actualPromise,\r\n resolve: entry.resolve,\r\n reject: entry.reject,\r\n })\r\n }\r\n }\r\n\r\n const pendingEntry: PendingRequest = {\r\n promise: actualPromise,\r\n request,\r\n controller,\r\n }\r\n pendingRequests.push(pendingEntry)\r\n\r\n return actualPromise.finally(() => {\r\n const index = pendingRequests.indexOf(pendingEntry)\r\n if (index > -1) {\r\n pendingRequests.splice(index, 1)\r\n }\r\n // Only delete dedupeMap entry if the promise is the same as the one in the map\r\n if (\r\n effectiveDedupe &&\r\n dedupeKey &&\r\n dedupeMap.get(dedupeKey)?.promise === actualPromise\r\n ) {\r\n dedupeMap.delete(dedupeKey)\r\n }\r\n })\r\n }\r\n\r\n Object.defineProperty(client, 'pendingRequests', {\r\n get() {\r\n return pendingRequests\r\n },\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperty(client, 'abortAll', {\r\n value: abortAll,\r\n writable: false,\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperty(client, 'circuitOpen', {\r\n get() {\r\n return breaker ? breaker.open : false\r\n },\r\n enumerable: true,\r\n })\r\n\r\n return client as FFetch\r\n}\r\n"],"mappings":"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","names":["shouldRetry","controller","HttpError"]}
|
package/dist/index.d.cts
CHANGED
|
@@ -12,6 +12,16 @@ type Hooks = {
|
|
|
12
12
|
transformResponse?: (res: Response, req: Request) => Response | Promise<Response>;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
type DedupeHashParams = {
|
|
16
|
+
method: string;
|
|
17
|
+
url: string;
|
|
18
|
+
body: string | FormData | URLSearchParams | Blob | ArrayBuffer | BufferSource | null | ReadableStream<unknown>;
|
|
19
|
+
headers?: Headers | Record<string, string>;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
requestInit?: RequestInit;
|
|
22
|
+
request?: Request;
|
|
23
|
+
};
|
|
24
|
+
|
|
15
25
|
interface RetryContext {
|
|
16
26
|
attempt: number;
|
|
17
27
|
request: Request;
|
|
@@ -30,14 +40,13 @@ interface FFetchOptions {
|
|
|
30
40
|
};
|
|
31
41
|
hooks?: Hooks;
|
|
32
42
|
fetchHandler?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
43
|
+
dedupe?: boolean;
|
|
44
|
+
dedupeHashFn?: (params: DedupeHashParams) => string | undefined;
|
|
33
45
|
}
|
|
34
46
|
type FFetch = {
|
|
35
47
|
(input: RequestInfo | URL, init?: FFetchRequestInit): Promise<Response>;
|
|
36
48
|
pendingRequests: PendingRequest[];
|
|
37
49
|
abortAll: () => void;
|
|
38
|
-
/**
|
|
39
|
-
* True if the circuit breaker is open (blocking requests), false otherwise.
|
|
40
|
-
*/
|
|
41
50
|
circuitOpen: boolean;
|
|
42
51
|
};
|
|
43
52
|
interface FFetchRequestInit extends RequestInit, FFetchOptions {
|
|
@@ -70,4 +79,4 @@ declare class NetworkError extends BaseError {
|
|
|
70
79
|
constructor(message?: string, cause?: unknown);
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
export { AbortError, CircuitOpenError, type FFetch, type FFetchOptions, type Hooks, NetworkError, RetryLimitError, TimeoutError, createClient, createClient as default };
|
|
82
|
+
export { AbortError, CircuitOpenError, type DedupeHashParams, type FFetch, type FFetchOptions, type Hooks, NetworkError, RetryLimitError, TimeoutError, createClient, createClient as default };
|
package/dist/index.d.ts
CHANGED
|
@@ -12,6 +12,16 @@ type Hooks = {
|
|
|
12
12
|
transformResponse?: (res: Response, req: Request) => Response | Promise<Response>;
|
|
13
13
|
};
|
|
14
14
|
|
|
15
|
+
type DedupeHashParams = {
|
|
16
|
+
method: string;
|
|
17
|
+
url: string;
|
|
18
|
+
body: string | FormData | URLSearchParams | Blob | ArrayBuffer | BufferSource | null | ReadableStream<unknown>;
|
|
19
|
+
headers?: Headers | Record<string, string>;
|
|
20
|
+
signal?: AbortSignal;
|
|
21
|
+
requestInit?: RequestInit;
|
|
22
|
+
request?: Request;
|
|
23
|
+
};
|
|
24
|
+
|
|
15
25
|
interface RetryContext {
|
|
16
26
|
attempt: number;
|
|
17
27
|
request: Request;
|
|
@@ -30,14 +40,13 @@ interface FFetchOptions {
|
|
|
30
40
|
};
|
|
31
41
|
hooks?: Hooks;
|
|
32
42
|
fetchHandler?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
|
|
43
|
+
dedupe?: boolean;
|
|
44
|
+
dedupeHashFn?: (params: DedupeHashParams) => string | undefined;
|
|
33
45
|
}
|
|
34
46
|
type FFetch = {
|
|
35
47
|
(input: RequestInfo | URL, init?: FFetchRequestInit): Promise<Response>;
|
|
36
48
|
pendingRequests: PendingRequest[];
|
|
37
49
|
abortAll: () => void;
|
|
38
|
-
/**
|
|
39
|
-
* True if the circuit breaker is open (blocking requests), false otherwise.
|
|
40
|
-
*/
|
|
41
50
|
circuitOpen: boolean;
|
|
42
51
|
};
|
|
43
52
|
interface FFetchRequestInit extends RequestInit, FFetchOptions {
|
|
@@ -70,4 +79,4 @@ declare class NetworkError extends BaseError {
|
|
|
70
79
|
constructor(message?: string, cause?: unknown);
|
|
71
80
|
}
|
|
72
81
|
|
|
73
|
-
export { AbortError, CircuitOpenError, type FFetch, type FFetchOptions, type Hooks, NetworkError, RetryLimitError, TimeoutError, createClient, createClient as default };
|
|
82
|
+
export { AbortError, CircuitOpenError, type DedupeHashParams, type FFetch, type FFetchOptions, type Hooks, NetworkError, RetryLimitError, TimeoutError, createClient, createClient as default };
|
package/dist/index.js
CHANGED
|
@@ -6,6 +6,38 @@ import {
|
|
|
6
6
|
TimeoutError
|
|
7
7
|
} from "./chunk-RV6LC73N.js";
|
|
8
8
|
|
|
9
|
+
// src/dedupeRequestHash.ts
|
|
10
|
+
function dedupeRequestHash(params) {
|
|
11
|
+
const { method, url, body } = params;
|
|
12
|
+
let bodyString = "";
|
|
13
|
+
if (body instanceof FormData) {
|
|
14
|
+
return void 0;
|
|
15
|
+
}
|
|
16
|
+
if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream) {
|
|
17
|
+
return void 0;
|
|
18
|
+
}
|
|
19
|
+
if (typeof body === "string") {
|
|
20
|
+
bodyString = body;
|
|
21
|
+
} else if (body instanceof URLSearchParams) {
|
|
22
|
+
bodyString = body.toString();
|
|
23
|
+
} else if (body instanceof ArrayBuffer) {
|
|
24
|
+
bodyString = Buffer.from(body).toString("base64");
|
|
25
|
+
} else if (body instanceof Uint8Array) {
|
|
26
|
+
bodyString = Buffer.from(body).toString("base64");
|
|
27
|
+
} else if (body instanceof Blob) {
|
|
28
|
+
bodyString = `[blob:${body.type}:${body.size}]`;
|
|
29
|
+
} else if (body == null) {
|
|
30
|
+
bodyString = "";
|
|
31
|
+
} else {
|
|
32
|
+
try {
|
|
33
|
+
bodyString = JSON.stringify(body);
|
|
34
|
+
} catch {
|
|
35
|
+
bodyString = "[unserializable-body]";
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
return `${method.toUpperCase()}|${url}|${bodyString}`;
|
|
39
|
+
}
|
|
40
|
+
|
|
9
41
|
// src/retry.ts
|
|
10
42
|
var defaultDelay = (ctx) => {
|
|
11
43
|
const retryAfter = ctx.response?.headers.get("Retry-After");
|
|
@@ -133,6 +165,7 @@ var CircuitBreaker = class {
|
|
|
133
165
|
|
|
134
166
|
// src/client.ts
|
|
135
167
|
function createClient(opts = {}) {
|
|
168
|
+
const dedupeMap = /* @__PURE__ */ new Map();
|
|
136
169
|
const {
|
|
137
170
|
timeout: clientDefaultTimeout = 5e3,
|
|
138
171
|
retries: clientDefaultRetries = 0,
|
|
@@ -140,7 +173,9 @@ function createClient(opts = {}) {
|
|
|
140
173
|
shouldRetry: clientDefaultShouldRetry = shouldRetry,
|
|
141
174
|
hooks: clientDefaultHooks = {},
|
|
142
175
|
circuit: clientDefaultCircuit,
|
|
143
|
-
fetchHandler
|
|
176
|
+
fetchHandler,
|
|
177
|
+
dedupe = false,
|
|
178
|
+
dedupeHashFn = dedupeRequestHash
|
|
144
179
|
} = opts;
|
|
145
180
|
const breaker = clientDefaultCircuit ? new CircuitBreaker(
|
|
146
181
|
clientDefaultCircuit.threshold,
|
|
@@ -159,7 +194,48 @@ function createClient(opts = {}) {
|
|
|
159
194
|
}
|
|
160
195
|
}
|
|
161
196
|
const client = async (input, init = {}) => {
|
|
197
|
+
const effectiveDedupe = typeof init.dedupe !== "undefined" ? init.dedupe : dedupe;
|
|
198
|
+
const effectiveDedupeHashFn = init.dedupeHashFn || dedupeHashFn;
|
|
199
|
+
let dedupeKey;
|
|
162
200
|
let request = new Request(input, init);
|
|
201
|
+
if (effectiveDedupe) {
|
|
202
|
+
dedupeKey = effectiveDedupeHashFn({
|
|
203
|
+
method: request.method,
|
|
204
|
+
url: request.url,
|
|
205
|
+
body: init.body ?? null,
|
|
206
|
+
headers: request.headers,
|
|
207
|
+
signal: init.signal === void 0 || init.signal === null ? void 0 : init.signal,
|
|
208
|
+
requestInit: init,
|
|
209
|
+
request
|
|
210
|
+
});
|
|
211
|
+
if (dedupeKey) {
|
|
212
|
+
if (dedupeMap.has(dedupeKey)) {
|
|
213
|
+
return dedupeMap.get(dedupeKey).promise;
|
|
214
|
+
}
|
|
215
|
+
let settled = false;
|
|
216
|
+
let resolveFn;
|
|
217
|
+
let rejectFn;
|
|
218
|
+
const inFlightPromise = new Promise((resolve, reject) => {
|
|
219
|
+
resolveFn = (value) => {
|
|
220
|
+
if (!settled) {
|
|
221
|
+
settled = true;
|
|
222
|
+
resolve(value);
|
|
223
|
+
}
|
|
224
|
+
};
|
|
225
|
+
rejectFn = (reason) => {
|
|
226
|
+
if (!settled) {
|
|
227
|
+
settled = true;
|
|
228
|
+
reject(reason);
|
|
229
|
+
}
|
|
230
|
+
};
|
|
231
|
+
});
|
|
232
|
+
dedupeMap.set(dedupeKey, {
|
|
233
|
+
promise: inFlightPromise,
|
|
234
|
+
resolve: resolveFn,
|
|
235
|
+
reject: rejectFn
|
|
236
|
+
});
|
|
237
|
+
}
|
|
238
|
+
}
|
|
163
239
|
const effectiveHooks = { ...clientDefaultHooks, ...init.hooks || {} };
|
|
164
240
|
if (effectiveHooks.transformRequest) {
|
|
165
241
|
request = await effectiveHooks.transformRequest(request);
|
|
@@ -256,7 +332,7 @@ function createClient(opts = {}) {
|
|
|
256
332
|
signal: combinedSignal
|
|
257
333
|
});
|
|
258
334
|
try {
|
|
259
|
-
const handler = fetchHandler ?? fetch;
|
|
335
|
+
const handler = init.fetchHandler ?? fetchHandler ?? fetch;
|
|
260
336
|
const response = await handler(reqWithSignal);
|
|
261
337
|
lastResponse = response;
|
|
262
338
|
if (breaker && (response.status >= 500 || response.status === 429)) {
|
|
@@ -342,7 +418,7 @@ function createClient(opts = {}) {
|
|
|
342
418
|
throw retryErr;
|
|
343
419
|
}
|
|
344
420
|
};
|
|
345
|
-
const
|
|
421
|
+
const actualPromise = breaker ? breaker.invoke(retryWithHooks).catch(async (err) => {
|
|
346
422
|
if (err instanceof CircuitOpenError) {
|
|
347
423
|
await effectiveHooks.onCircuitOpen?.(request);
|
|
348
424
|
await effectiveHooks.onError?.(request, err);
|
|
@@ -353,17 +429,34 @@ function createClient(opts = {}) {
|
|
|
353
429
|
}
|
|
354
430
|
throw err;
|
|
355
431
|
}) : retryWithHooks();
|
|
356
|
-
|
|
357
|
-
|
|
432
|
+
if (effectiveDedupe && dedupeKey && dedupeMap.has(dedupeKey)) {
|
|
433
|
+
const entry = dedupeMap.get(dedupeKey);
|
|
434
|
+
if (entry) {
|
|
435
|
+
actualPromise.then(
|
|
436
|
+
(result) => entry.resolve(result),
|
|
437
|
+
(error) => entry.reject(error)
|
|
438
|
+
);
|
|
439
|
+
dedupeMap.set(dedupeKey, {
|
|
440
|
+
promise: actualPromise,
|
|
441
|
+
resolve: entry.resolve,
|
|
442
|
+
reject: entry.reject
|
|
443
|
+
});
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
const pendingEntry = {
|
|
447
|
+
promise: actualPromise,
|
|
358
448
|
request,
|
|
359
449
|
controller
|
|
360
450
|
};
|
|
361
|
-
pendingRequests.push(
|
|
362
|
-
return
|
|
363
|
-
const index = pendingRequests.indexOf(
|
|
451
|
+
pendingRequests.push(pendingEntry);
|
|
452
|
+
return actualPromise.finally(() => {
|
|
453
|
+
const index = pendingRequests.indexOf(pendingEntry);
|
|
364
454
|
if (index > -1) {
|
|
365
455
|
pendingRequests.splice(index, 1);
|
|
366
456
|
}
|
|
457
|
+
if (effectiveDedupe && dedupeKey && dedupeMap.get(dedupeKey)?.promise === actualPromise) {
|
|
458
|
+
dedupeMap.delete(dedupeKey);
|
|
459
|
+
}
|
|
367
460
|
});
|
|
368
461
|
};
|
|
369
462
|
Object.defineProperty(client, "pendingRequests", {
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/retry.ts","../src/should-retry.ts","../src/circuit.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["import type { RetryContext } from './types.js'\n\nexport type RetryDelay = number | ((ctx: RetryContext) => number)\n\nexport const defaultDelay: RetryDelay = (ctx) => {\n const retryAfter = ctx.response?.headers.get('Retry-After')\n if (retryAfter) {\n const seconds = parseInt(retryAfter, 10)\n if (!isNaN(seconds)) return seconds * 1000\n const date = Date.parse(retryAfter)\n if (!isNaN(date)) return Math.max(0, date - Date.now())\n }\n return 2 ** ctx.attempt * 200 + Math.random() * 100\n}\n\nexport async function retry(\n fn: () => Promise<Response>,\n retries: number,\n delay: RetryDelay,\n shouldRetry: (ctx: RetryContext) => boolean = () => true,\n request: Request\n): Promise<Response> {\n let lastErr: unknown\n let lastRes: Response | undefined\n\n for (let i = 0; i <= retries; i++) {\n const ctx: RetryContext = {\n attempt: i + 1,\n request,\n response: lastRes,\n error: lastErr,\n }\n try {\n lastRes = await fn()\n ctx.response = lastRes\n ctx.error = undefined\n if (i < retries && shouldRetry(ctx)) {\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n continue\n }\n return lastRes\n } catch (err) {\n lastErr = err\n ctx.error = err\n if (i === retries || !shouldRetry(ctx)) throw err\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n throw lastErr\n}\n","import { AbortError, CircuitOpenError, TimeoutError } from './error.js'\nimport type { RetryContext } from './types.js'\n\nexport function shouldRetry(ctx: RetryContext): boolean {\n const { error, response } = ctx\n if (\n error instanceof AbortError ||\n error instanceof CircuitOpenError ||\n error instanceof TimeoutError\n )\n return false\n if (!response) return true // network error\n return response.status >= 500 || response.status === 429\n}\n","import { CircuitOpenError, RetryLimitError } from './error.js'\n\nexport class CircuitBreaker {\n private failures = 0\n private nextAttempt = 0\n private isOpen = false\n\n // Returns true if the circuit breaker is currently open (blocking requests).\n get open(): boolean {\n return this.isOpen\n }\n private hooks?: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }\n private lastSuccessRequest?: Request\n private lastOpenRequest?: Request\n\n constructor(\n private threshold: number,\n private resetTimeout: number\n ) {}\n\n // Call this after each request to record the result and update circuit state.\n // Returns true if the result was counted as a failure.\n recordResult(response?: Response, error?: unknown, req?: Request): boolean {\n // Count thrown errors (network, abort, timeout) as failures\n if (error && !(error instanceof RetryLimitError)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Count HTTP 5xx and 429 responses as failures\n if (response && (response.status >= 500 || response.status === 429)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Otherwise, count as success\n if (req) this.setLastSuccessRequest(req)\n this.onSuccess()\n return false\n }\n\n setHooks(hooks: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }) {\n this.hooks = hooks\n }\n setLastOpenRequest(req: Request) {\n this.lastOpenRequest = req\n }\n\n setLastSuccessRequest(req: Request) {\n this.lastSuccessRequest = req\n }\n\n async invoke<T>(fn: () => Promise<T>): Promise<T> {\n if (Date.now() < this.nextAttempt)\n throw new CircuitOpenError('Circuit is open')\n try {\n const res = await fn()\n this.onSuccess()\n return res\n } catch (err) {\n this.onFailure()\n throw err\n }\n }\n\n private onSuccess() {\n const wasOpen = this.isOpen\n this.failures = 0\n if (wasOpen) {\n this.isOpen = false\n if (this.hooks?.onCircuitClose && this.lastSuccessRequest) {\n this.hooks.onCircuitClose(this.lastSuccessRequest)\n }\n }\n this.lastSuccessRequest = undefined\n }\n\n private onFailure() {\n this.failures++\n if (this.failures >= this.threshold) {\n this.nextAttempt = Date.now() + this.resetTimeout\n this.isOpen = true\n if (this.hooks?.onCircuitOpen && this.lastOpenRequest) {\n this.hooks.onCircuitOpen(this.lastOpenRequest)\n }\n }\n }\n}\n","import type {\n FFetchOptions,\n FFetch,\n FFetchRequestInit,\n PendingRequest,\n} from './types.js'\nimport { retry, defaultDelay } from './retry.js'\nimport { shouldRetry as defaultShouldRetry } from './should-retry.js'\nimport { CircuitBreaker } from './circuit.js'\nimport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error.js'\n\nexport function createClient(opts: FFetchOptions = {}): FFetch {\n const {\n timeout: clientDefaultTimeout = 5_000,\n retries: clientDefaultRetries = 0,\n retryDelay: clientDefaultRetryDelay = defaultDelay,\n shouldRetry: clientDefaultShouldRetry = defaultShouldRetry,\n hooks: clientDefaultHooks = {},\n circuit: clientDefaultCircuit,\n fetchHandler,\n } = opts\n\n const breaker = clientDefaultCircuit\n ? new CircuitBreaker(\n clientDefaultCircuit.threshold,\n clientDefaultCircuit.reset\n )\n : null\n\n if (\n breaker &&\n (clientDefaultHooks.onCircuitClose || clientDefaultHooks.onCircuitOpen)\n ) {\n breaker.setHooks({\n onCircuitClose: clientDefaultHooks.onCircuitClose,\n onCircuitOpen: clientDefaultHooks.onCircuitOpen,\n })\n }\n\n const pendingRequests: PendingRequest[] = []\n\n // Helper to abort all pending requests\n function abortAll() {\n for (const entry of pendingRequests) {\n entry.controller?.abort()\n }\n }\n\n const client = async (\n input: RequestInfo | URL,\n init: FFetchRequestInit = {}\n ) => {\n // No longer require AbortSignal.timeout - we'll implement it manually if needed\n let request = new Request(input, init)\n\n // Merge hooks: per-request hooks override client hooks, but fallback to client hooks\n const effectiveHooks = { ...clientDefaultHooks, ...(init.hooks || {}) }\n if (effectiveHooks.transformRequest) {\n request = await effectiveHooks.transformRequest(request)\n }\n await effectiveHooks.before?.(request)\n\n // Determine throwOnHttpError (per-request overrides client default)\n const effectiveThrowOnHttpError =\n typeof init.throwOnHttpError !== 'undefined'\n ? init.throwOnHttpError\n : (opts.throwOnHttpError ?? false)\n\n // Create timeout signal (manual implementation if AbortSignal.timeout not available)\n function createTimeoutSignal(timeout: number): AbortSignal {\n if (typeof AbortSignal?.timeout === 'function') {\n return AbortSignal.timeout(timeout)\n }\n\n // Manual implementation for older environments\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n // Clean up timeout if signal is aborted early by other means\n controller.signal.addEventListener(\n 'abort',\n () => clearTimeout(timeoutId),\n { once: true }\n )\n\n return controller.signal\n }\n\n // AbortSignal.timeout/any logic ---\n const effectiveTimeout = init.timeout ?? clientDefaultTimeout\n const userSignal = init.signal\n const transformedSignal = request.signal // Extract signal from transformed request\n let timeoutSignal: AbortSignal | undefined = undefined\n let combinedSignal: AbortSignal | undefined = undefined\n let controller: AbortController | undefined = undefined\n\n if (effectiveTimeout > 0) {\n timeoutSignal = createTimeoutSignal(effectiveTimeout)\n }\n\n // Collect all signals that need to be combined\n const signals: AbortSignal[] = []\n if (userSignal) signals.push(userSignal)\n if (transformedSignal && transformedSignal !== userSignal) {\n signals.push(transformedSignal)\n }\n if (timeoutSignal) signals.push(timeoutSignal)\n\n // Use AbortSignal.any for signal combination. Requires native support or a polyfill.\n // If not available, instruct users to install a polyfill for environments lacking AbortSignal.any.\n // there are always 1 or more signals\n if (signals.length === 1) {\n combinedSignal = signals[0]\n controller = new AbortController()\n } else {\n if (typeof AbortSignal.any !== 'function') {\n throw new Error(\n 'AbortSignal.any is required for combining multiple signals. Please install a polyfill for environments that do not support it.'\n )\n }\n combinedSignal = AbortSignal.any(signals)\n controller = new AbortController()\n }\n const retryWithHooks = async () => {\n const effectiveRetries = init.retries ?? clientDefaultRetries\n const effectiveRetryDelay =\n typeof init.retryDelay !== 'undefined'\n ? init.retryDelay\n : clientDefaultRetryDelay\n const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry\n\n // Wrap shouldRetry to call onRetry hook\n let attempt = 0\n const shouldRetryWithHook = (ctx: import('./types').RetryContext) => {\n attempt = ctx.attempt\n const retrying = effectiveShouldRetry(ctx)\n if (retrying && attempt <= effectiveRetries) {\n effectiveHooks.onRetry?.(\n request,\n attempt - 1,\n ctx.error,\n ctx.response\n )\n }\n return retrying\n }\n\n let lastResponse: Response | undefined = undefined\n try {\n let res = await retry(\n async () => {\n // Check for aborts before making the request\n if (userSignal?.aborted) {\n effectiveHooks.onAbort?.(request)\n // Throw AbortError for user aborts, no cause needed\n throw new AbortError('Request was aborted by user')\n }\n if (timeoutSignal?.aborted) {\n effectiveHooks.onTimeout?.(request)\n throw new TimeoutError('signal timed out')\n }\n if (typeof combinedSignal?.throwIfAborted === 'function') {\n combinedSignal.throwIfAborted()\n } else if (combinedSignal?.aborted) {\n if (userSignal?.aborted) {\n effectiveHooks.onAbort?.(request)\n throw new AbortError('Request was aborted by user')\n } else if (timeoutSignal?.aborted) {\n effectiveHooks.onTimeout?.(request)\n throw new TimeoutError('signal timed out')\n } else {\n throw new AbortError(\n 'Request was aborted',\n new DOMException('Aborted', 'AbortError')\n )\n }\n }\n const reqWithSignal = new Request(request, {\n signal: combinedSignal,\n })\n try {\n const handler = fetchHandler ?? fetch\n const response = await handler(reqWithSignal)\n lastResponse = response\n if (\n breaker &&\n (response.status >= 500 || response.status === 429)\n ) {\n breaker.recordResult(response, undefined, request)\n }\n return response\n } catch (err) {\n if (breaker) breaker.recordResult(undefined, err, request)\n if (err instanceof DOMException && err.name === 'AbortError') {\n if (\n timeoutSignal?.aborted &&\n (!userSignal || !userSignal.aborted)\n ) {\n effectiveHooks.onTimeout?.(request)\n throw new TimeoutError('signal timed out', err)\n } else if (userSignal?.aborted) {\n effectiveHooks.onAbort?.(request)\n throw new AbortError('Request was aborted by user')\n } else {\n throw new AbortError(\n 'Request was aborted',\n new DOMException('Aborted', 'AbortError')\n )\n }\n } else if (\n err instanceof TypeError &&\n /NetworkError|network error|failed to fetch|lost connection|NetworkError when attempting to fetch resource/i.test(\n err.message\n )\n ) {\n throw new NetworkError(err.message, err)\n }\n throw err\n }\n },\n effectiveRetries,\n effectiveRetryDelay,\n shouldRetryWithHook,\n request\n )\n if (effectiveHooks.transformResponse) {\n res = await effectiveHooks.transformResponse(res, request)\n }\n await effectiveHooks.after?.(request, res)\n await effectiveHooks.onComplete?.(request, res, undefined)\n // After all retries, if throwOnHttpError is true and final response is error, throw HttpError\n if (\n effectiveThrowOnHttpError &&\n ((res.status >= 400 && res.status < 500 && res.status !== 429) ||\n res.status >= 500 ||\n res.status === 429)\n ) {\n const { HttpError } = await import('./error.js')\n throw new HttpError(\n `HTTP error: ${res.status} ${res.statusText}`,\n res\n )\n }\n return res\n } catch (err: unknown) {\n // If lastResponse is set (any status), return or throw as needed\n if (lastResponse) {\n const resp = lastResponse as Response\n // Only throw if throwOnHttpError is true and final response is 4xx (not 429), 5xx, or 429\n if (\n effectiveThrowOnHttpError &&\n ((resp.status >= 400 && resp.status < 500 && resp.status !== 429) ||\n resp.status >= 500 ||\n resp.status === 429)\n ) {\n const { HttpError } = await import('./error.js')\n throw new HttpError(\n `HTTP error: ${resp.status} ${resp.statusText}`,\n resp\n )\n }\n return resp\n }\n // If the error is a known error type, re-throw it directly and call correct hook\n if (err instanceof TimeoutError) {\n await effectiveHooks.onTimeout?.(request)\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n throw err\n }\n if (err instanceof AbortError) {\n await effectiveHooks.onAbort?.(request)\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n throw err\n }\n if (err instanceof NetworkError) {\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n throw err\n }\n // Otherwise, wrap in RetryLimitError\n const retryErr = new RetryLimitError(\n typeof err === 'object' &&\n err &&\n 'message' in err &&\n typeof (err as { message?: unknown }).message === 'string'\n ? (err as { message: string }).message\n : 'Retry limit reached',\n err\n )\n await effectiveHooks.onError?.(request, retryErr)\n await effectiveHooks.onComplete?.(request, undefined, retryErr)\n throw retryErr\n }\n }\n\n const promise = breaker\n ? breaker.invoke(retryWithHooks).catch(async (err: unknown) => {\n if (err instanceof CircuitOpenError) {\n await effectiveHooks.onCircuitOpen?.(request)\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n } else {\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n }\n throw err\n })\n : retryWithHooks()\n\n const entry: PendingRequest = {\n promise,\n request,\n controller,\n }\n pendingRequests.push(entry)\n\n return promise.finally(() => {\n const index = pendingRequests.indexOf(entry)\n if (index > -1) {\n pendingRequests.splice(index, 1)\n }\n })\n }\n\n // Add pendingRequests property to the client function (read-only)\n Object.defineProperty(client, 'pendingRequests', {\n get() {\n return pendingRequests\n },\n enumerable: false,\n configurable: false,\n })\n\n // Add abortAll method to the client function (read-only)\n Object.defineProperty(client, 'abortAll', {\n value: abortAll,\n writable: false,\n enumerable: false,\n configurable: false,\n })\n\n // Expose circuit breaker open state\n Object.defineProperty(client, 'circuitOpen', {\n get() {\n return breaker ? breaker.open : false\n },\n enumerable: true,\n })\n\n return client as FFetch\n}\n","export type { FFetch, FFetchOptions } from './types'\nexport type { Hooks } from './hooks'\n\nimport { createClient } from './client'\nexport { createClient } from './client'\n\nexport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error'\n\nexport default createClient\n"],"mappings":"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","names":["shouldRetry","controller"]}
|
|
1
|
+
{"version":3,"sources":["../src/dedupeRequestHash.ts","../src/retry.ts","../src/should-retry.ts","../src/circuit.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["export type DedupeHashParams = {\n method: string\n url: string\n body:\n | string\n | FormData\n | URLSearchParams\n | Blob\n | ArrayBuffer\n | BufferSource\n | null\n | ReadableStream<unknown>\n headers?: Headers | Record<string, string>\n signal?: AbortSignal\n requestInit?: RequestInit\n request?: Request\n}\n\nexport function dedupeRequestHash(\n params: DedupeHashParams\n): string | undefined {\n const { method, url, body } = params\n let bodyString = ''\n if (body instanceof FormData) {\n // Skip deduplication for FormData\n return undefined\n }\n // Skip deduplication for ReadableStream\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n return undefined\n }\n if (typeof body === 'string') {\n bodyString = body\n } else if (body instanceof URLSearchParams) {\n bodyString = body.toString()\n } else if (body instanceof ArrayBuffer) {\n bodyString = Buffer.from(body).toString('base64')\n } else if (body instanceof Uint8Array) {\n bodyString = Buffer.from(body).toString('base64')\n } else if (body instanceof Blob) {\n bodyString = `[blob:${body.type}:${body.size}]`\n } else if (body == null) {\n bodyString = ''\n } else {\n try {\n bodyString = JSON.stringify(body)\n } catch {\n bodyString = '[unserializable-body]'\n }\n }\n return `${method.toUpperCase()}|${url}|${bodyString}`\n}\n","import type { RetryContext } from './types.js'\n\nexport type RetryDelay = number | ((ctx: RetryContext) => number)\n\nexport const defaultDelay: RetryDelay = (ctx) => {\n const retryAfter = ctx.response?.headers.get('Retry-After')\n if (retryAfter) {\n const seconds = parseInt(retryAfter, 10)\n if (!isNaN(seconds)) return seconds * 1000\n const date = Date.parse(retryAfter)\n if (!isNaN(date)) return Math.max(0, date - Date.now())\n }\n return 2 ** ctx.attempt * 200 + Math.random() * 100\n}\n\nexport async function retry(\n fn: () => Promise<Response>,\n retries: number,\n delay: RetryDelay,\n shouldRetry: (ctx: RetryContext) => boolean = () => true,\n request: Request\n): Promise<Response> {\n let lastErr: unknown\n let lastRes: Response | undefined\n\n for (let i = 0; i <= retries; i++) {\n const ctx: RetryContext = {\n attempt: i + 1,\n request,\n response: lastRes,\n error: lastErr,\n }\n try {\n lastRes = await fn()\n ctx.response = lastRes\n ctx.error = undefined\n if (i < retries && shouldRetry(ctx)) {\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n continue\n }\n return lastRes\n } catch (err) {\n lastErr = err\n ctx.error = err\n if (i === retries || !shouldRetry(ctx)) throw err\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n throw lastErr\n}\n","import { AbortError, CircuitOpenError, TimeoutError } from './error.js'\nimport type { RetryContext } from './types.js'\n\nexport function shouldRetry(ctx: RetryContext): boolean {\n const { error, response } = ctx\n if (\n error instanceof AbortError ||\n error instanceof CircuitOpenError ||\n error instanceof TimeoutError\n )\n return false\n if (!response) return true // network error\n return response.status >= 500 || response.status === 429\n}\n","import { CircuitOpenError, RetryLimitError } from './error.js'\n\nexport class CircuitBreaker {\n private failures = 0\n private nextAttempt = 0\n private isOpen = false\n\n // Returns true if the circuit breaker is currently open (blocking requests).\n get open(): boolean {\n return this.isOpen\n }\n private hooks?: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }\n private lastSuccessRequest?: Request\n private lastOpenRequest?: Request\n\n constructor(\n private threshold: number,\n private resetTimeout: number\n ) {}\n\n // Call this after each request to record the result and update circuit state.\n // Returns true if the result was counted as a failure.\n recordResult(response?: Response, error?: unknown, req?: Request): boolean {\n // Count thrown errors (network, abort, timeout) as failures\n if (error && !(error instanceof RetryLimitError)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Count HTTP 5xx and 429 responses as failures\n if (response && (response.status >= 500 || response.status === 429)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Otherwise, count as success\n if (req) this.setLastSuccessRequest(req)\n this.onSuccess()\n return false\n }\n\n setHooks(hooks: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }) {\n this.hooks = hooks\n }\n setLastOpenRequest(req: Request) {\n this.lastOpenRequest = req\n }\n\n setLastSuccessRequest(req: Request) {\n this.lastSuccessRequest = req\n }\n\n async invoke<T>(fn: () => Promise<T>): Promise<T> {\n if (Date.now() < this.nextAttempt)\n throw new CircuitOpenError('Circuit is open')\n try {\n const res = await fn()\n this.onSuccess()\n return res\n } catch (err) {\n this.onFailure()\n throw err\n }\n }\n\n private onSuccess() {\n const wasOpen = this.isOpen\n this.failures = 0\n if (wasOpen) {\n this.isOpen = false\n if (this.hooks?.onCircuitClose && this.lastSuccessRequest) {\n this.hooks.onCircuitClose(this.lastSuccessRequest)\n }\n }\n this.lastSuccessRequest = undefined\n }\n\n private onFailure() {\n this.failures++\n if (this.failures >= this.threshold) {\n this.nextAttempt = Date.now() + this.resetTimeout\n this.isOpen = true\n if (this.hooks?.onCircuitOpen && this.lastOpenRequest) {\n this.hooks.onCircuitOpen(this.lastOpenRequest)\n }\n }\n }\n}\n","import type {\r\n FFetchOptions,\r\n FFetch,\r\n FFetchRequestInit,\r\n PendingRequest,\r\n} from './types.js'\r\nimport { dedupeRequestHash } from './dedupeRequestHash.js'\r\nimport { retry, defaultDelay } from './retry.js'\r\nimport { shouldRetry as defaultShouldRetry } from './should-retry.js'\r\nimport { CircuitBreaker } from './circuit.js'\r\nimport {\r\n TimeoutError,\r\n CircuitOpenError,\r\n AbortError,\r\n RetryLimitError,\r\n NetworkError,\r\n} from './error.js'\r\n\r\nexport function createClient(opts: FFetchOptions = {}): FFetch {\r\n // Track in-flight deduped requests and their resolvers\r\n const dedupeMap = new Map<\r\n string,\r\n {\r\n promise: Promise<Response>\r\n resolve: (value: Response | PromiseLike<Response>) => void\r\n reject: (reason?: unknown) => void\r\n }\r\n >()\r\n const {\r\n timeout: clientDefaultTimeout = 5_000,\r\n retries: clientDefaultRetries = 0,\r\n retryDelay: clientDefaultRetryDelay = defaultDelay,\r\n shouldRetry: clientDefaultShouldRetry = defaultShouldRetry,\r\n hooks: clientDefaultHooks = {},\r\n circuit: clientDefaultCircuit,\r\n fetchHandler,\r\n dedupe = false,\r\n dedupeHashFn = dedupeRequestHash,\r\n } = opts\r\n\r\n const breaker = clientDefaultCircuit\r\n ? new CircuitBreaker(\r\n clientDefaultCircuit.threshold,\r\n clientDefaultCircuit.reset\r\n )\r\n : null\r\n\r\n if (\r\n breaker &&\r\n (clientDefaultHooks.onCircuitClose || clientDefaultHooks.onCircuitOpen)\r\n ) {\r\n breaker.setHooks({\r\n onCircuitClose: clientDefaultHooks.onCircuitClose,\r\n onCircuitOpen: clientDefaultHooks.onCircuitOpen,\r\n })\r\n }\r\n\r\n const pendingRequests: PendingRequest[] = []\r\n\r\n // Helper to abort all pending requests\r\n function abortAll() {\r\n for (const entry of pendingRequests) {\r\n entry.controller?.abort()\r\n }\r\n }\r\n\r\n const client = async (\r\n input: RequestInfo | URL,\r\n init: FFetchRequestInit = {}\r\n ) => {\r\n // Deduplication logic\r\n const effectiveDedupe =\r\n typeof init.dedupe !== 'undefined' ? init.dedupe : dedupe\r\n const effectiveDedupeHashFn = init.dedupeHashFn || dedupeHashFn\r\n let dedupeKey: string | undefined\r\n\r\n let request = new Request(input, init)\r\n if (effectiveDedupe) {\r\n dedupeKey = effectiveDedupeHashFn({\r\n method: request.method,\r\n url: request.url,\r\n body: init.body ?? null,\r\n headers: request.headers,\r\n signal:\r\n init.signal === undefined || init.signal === null\r\n ? undefined\r\n : init.signal,\r\n requestInit: init,\r\n request,\r\n })\r\n if (dedupeKey) {\r\n if (dedupeMap.has(dedupeKey)) {\r\n return dedupeMap.get(dedupeKey)!.promise\r\n }\r\n let settled = false\r\n let resolveFn: (value: Response | PromiseLike<Response>) => void\r\n let rejectFn: (reason?: unknown) => void\r\n const inFlightPromise = new Promise<Response>((resolve, reject) => {\r\n resolveFn = (value) => {\r\n if (!settled) {\r\n settled = true\r\n resolve(value)\r\n }\r\n }\r\n rejectFn = (reason) => {\r\n if (!settled) {\r\n settled = true\r\n reject(reason)\r\n }\r\n }\r\n })\r\n dedupeMap.set(dedupeKey, {\r\n promise: inFlightPromise,\r\n resolve: resolveFn!,\r\n reject: rejectFn!,\r\n })\r\n }\r\n }\r\n\r\n // Merge hooks: per-request hooks override client hooks, but fallback to client hooks\r\n const effectiveHooks = { ...clientDefaultHooks, ...(init.hooks || {}) }\r\n if (effectiveHooks.transformRequest) {\r\n request = await effectiveHooks.transformRequest(request)\r\n }\r\n await effectiveHooks.before?.(request)\r\n\r\n // Determine throwOnHttpError (per-request overrides client default)\r\n const effectiveThrowOnHttpError =\r\n typeof init.throwOnHttpError !== 'undefined'\r\n ? init.throwOnHttpError\r\n : (opts.throwOnHttpError ?? false)\r\n\r\n // Create timeout signal (manual implementation if AbortSignal.timeout not available)\r\n function createTimeoutSignal(timeout: number): AbortSignal {\r\n if (typeof AbortSignal?.timeout === 'function') {\r\n return AbortSignal.timeout(timeout)\r\n }\r\n const controller = new AbortController()\r\n const timeoutId = setTimeout(() => controller.abort(), timeout)\r\n controller.signal.addEventListener(\r\n 'abort',\r\n () => clearTimeout(timeoutId),\r\n { once: true }\r\n )\r\n return controller.signal\r\n }\r\n\r\n // AbortSignal.timeout/any logic\r\n const effectiveTimeout = init.timeout ?? clientDefaultTimeout\r\n const userSignal = init.signal\r\n const transformedSignal = request.signal\r\n let timeoutSignal: AbortSignal | undefined = undefined\r\n let combinedSignal: AbortSignal | undefined = undefined\r\n let controller: AbortController | undefined = undefined\r\n\r\n if (effectiveTimeout > 0) {\r\n timeoutSignal = createTimeoutSignal(effectiveTimeout)\r\n }\r\n\r\n const signals: AbortSignal[] = []\r\n if (userSignal) signals.push(userSignal)\r\n if (transformedSignal && transformedSignal !== userSignal) {\r\n signals.push(transformedSignal)\r\n }\r\n if (timeoutSignal) signals.push(timeoutSignal)\r\n\r\n if (signals.length === 1) {\r\n combinedSignal = signals[0]\r\n controller = new AbortController()\r\n } else {\r\n if (typeof AbortSignal.any !== 'function') {\r\n throw new Error(\r\n 'AbortSignal.any is required for combining multiple signals. Please install a polyfill for environments that do not support it.'\r\n )\r\n }\r\n combinedSignal = AbortSignal.any(signals)\r\n controller = new AbortController()\r\n }\r\n\r\n const retryWithHooks = async () => {\r\n const effectiveRetries = init.retries ?? clientDefaultRetries\r\n const effectiveRetryDelay =\r\n typeof init.retryDelay !== 'undefined'\r\n ? init.retryDelay\r\n : clientDefaultRetryDelay\r\n const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry\r\n\r\n let attempt = 0\r\n const shouldRetryWithHook = (ctx: import('./types').RetryContext) => {\r\n attempt = ctx.attempt\r\n const retrying = effectiveShouldRetry(ctx)\r\n if (retrying && attempt <= effectiveRetries) {\r\n effectiveHooks.onRetry?.(\r\n request,\r\n attempt - 1,\r\n ctx.error,\r\n ctx.response\r\n )\r\n }\r\n return retrying\r\n }\r\n\r\n let lastResponse: Response | undefined = undefined\r\n try {\r\n let res = await retry(\r\n async () => {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n }\r\n if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n }\r\n if (typeof combinedSignal?.throwIfAborted === 'function') {\r\n combinedSignal.throwIfAborted()\r\n } else if (combinedSignal?.aborted) {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n }\r\n const reqWithSignal = new Request(request, {\r\n signal: combinedSignal,\r\n })\r\n try {\r\n const handler = init.fetchHandler ?? fetchHandler ?? fetch\r\n const response = await handler(reqWithSignal)\r\n lastResponse = response\r\n if (\r\n breaker &&\r\n (response.status >= 500 || response.status === 429)\r\n ) {\r\n breaker.recordResult(response, undefined, request)\r\n }\r\n return response\r\n } catch (err) {\r\n if (breaker) breaker.recordResult(undefined, err, request)\r\n if (err instanceof DOMException && err.name === 'AbortError') {\r\n if (\r\n timeoutSignal?.aborted &&\r\n (!userSignal || !userSignal.aborted)\r\n ) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out', err)\r\n } else if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n } else if (\r\n err instanceof TypeError &&\r\n /NetworkError|network error|failed to fetch|lost connection|NetworkError when attempting to fetch resource/i.test(\r\n err.message\r\n )\r\n ) {\r\n throw new NetworkError(err.message, err)\r\n }\r\n throw err\r\n }\r\n },\r\n effectiveRetries,\r\n effectiveRetryDelay,\r\n shouldRetryWithHook,\r\n request\r\n )\r\n if (effectiveHooks.transformResponse) {\r\n res = await effectiveHooks.transformResponse(res, request)\r\n }\r\n await effectiveHooks.after?.(request, res)\r\n await effectiveHooks.onComplete?.(request, res, undefined)\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((res.status >= 400 && res.status < 500 && res.status !== 429) ||\r\n res.status >= 500 ||\r\n res.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${res.status} ${res.statusText}`,\r\n res\r\n )\r\n }\r\n return res\r\n } catch (err: unknown) {\r\n if (lastResponse) {\r\n const resp = lastResponse as Response\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((resp.status >= 400 && resp.status < 500 && resp.status !== 429) ||\r\n resp.status >= 500 ||\r\n resp.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${resp.status} ${resp.statusText}`,\r\n resp\r\n )\r\n }\r\n return resp\r\n }\r\n if (err instanceof TimeoutError) {\r\n await effectiveHooks.onTimeout?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof AbortError) {\r\n await effectiveHooks.onAbort?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof NetworkError) {\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n const retryErr = new RetryLimitError(\r\n typeof err === 'object' &&\r\n err &&\r\n 'message' in err &&\r\n typeof (err as { message?: unknown }).message === 'string'\r\n ? (err as { message: string }).message\r\n : 'Retry limit reached',\r\n err\r\n )\r\n await effectiveHooks.onError?.(request, retryErr)\r\n await effectiveHooks.onComplete?.(request, undefined, retryErr)\r\n throw retryErr\r\n }\r\n }\r\n\r\n const actualPromise = breaker\r\n ? breaker.invoke(retryWithHooks).catch(async (err: unknown) => {\r\n if (err instanceof CircuitOpenError) {\r\n await effectiveHooks.onCircuitOpen?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n } else {\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n }\r\n throw err\r\n })\r\n : retryWithHooks()\r\n\r\n // If deduplication is enabled and dedupeKey is set, resolve/reject the in-flight promise\r\n if (effectiveDedupe && dedupeKey && dedupeMap.has(dedupeKey)) {\r\n const entry = dedupeMap.get(dedupeKey)\r\n if (entry) {\r\n actualPromise.then(\r\n (result) => entry.resolve(result),\r\n (error) => entry.reject(error)\r\n )\r\n // Replace the placeholder with the actual promise for future requests\r\n dedupeMap.set(dedupeKey, {\r\n promise: actualPromise,\r\n resolve: entry.resolve,\r\n reject: entry.reject,\r\n })\r\n }\r\n }\r\n\r\n const pendingEntry: PendingRequest = {\r\n promise: actualPromise,\r\n request,\r\n controller,\r\n }\r\n pendingRequests.push(pendingEntry)\r\n\r\n return actualPromise.finally(() => {\r\n const index = pendingRequests.indexOf(pendingEntry)\r\n if (index > -1) {\r\n pendingRequests.splice(index, 1)\r\n }\r\n // Only delete dedupeMap entry if the promise is the same as the one in the map\r\n if (\r\n effectiveDedupe &&\r\n dedupeKey &&\r\n dedupeMap.get(dedupeKey)?.promise === actualPromise\r\n ) {\r\n dedupeMap.delete(dedupeKey)\r\n }\r\n })\r\n }\r\n\r\n Object.defineProperty(client, 'pendingRequests', {\r\n get() {\r\n return pendingRequests\r\n },\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperty(client, 'abortAll', {\r\n value: abortAll,\r\n writable: false,\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperty(client, 'circuitOpen', {\r\n get() {\r\n return breaker ? breaker.open : false\r\n },\r\n enumerable: true,\r\n })\r\n\r\n return client as FFetch\r\n}\r\n","export type { FFetch, FFetchOptions } from './types'\nexport type { DedupeHashParams } from './dedupeRequestHash'\nexport type { Hooks } from './hooks'\n\nimport { createClient } from './client'\nexport { createClient } from './client'\n\nexport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error'\n\nexport default createClient\n"],"mappings":"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","names":["shouldRetry","controller"]}
|
package/dist/index.min.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
|
-
import{a as
|
|
1
|
+
import{a as g,b as v,c as w,d as T,e as j}from"./chunk-UP35S5ZH.min.js";function G(l){let{method:r,url:i,body:s}=l,f="";if(!(s instanceof FormData)&&!(typeof ReadableStream<"u"&&s instanceof ReadableStream)){if(typeof s=="string")f=s;else if(s instanceof URLSearchParams)f=s.toString();else if(s instanceof ArrayBuffer)f=Buffer.from(s).toString("base64");else if(s instanceof Uint8Array)f=Buffer.from(s).toString("base64");else if(s instanceof Blob)f=`[blob:${s.type}:${s.size}]`;else if(s==null)f="";else try{f=JSON.stringify(s)}catch{f="[unserializable-body]"}return`${r.toUpperCase()}|${i}|${f}`}}var Q=l=>{let r=l.response?.headers.get("Retry-After");if(r){let i=parseInt(r,10);if(!isNaN(i))return i*1e3;let s=Date.parse(r);if(!isNaN(s))return Math.max(0,s-Date.now())}return 2**l.attempt*200+Math.random()*100};async function V(l,r,i,s=()=>!0,f){let k,h;for(let y=0;y<=r;y++){let R={attempt:y+1,request:f,response:h,error:k};try{if(h=await l(),R.response=h,R.error=void 0,y<r&&s(R)){let q=typeof i=="function"?i(R):i;await new Promise(x=>setTimeout(x,q));continue}return h}catch(q){if(k=q,R.error=q,y===r||!s(R))throw q;let x=typeof i=="function"?i(R):i;await new Promise(c=>setTimeout(c,x))}}throw k}function X(l){let{error:r,response:i}=l;return r instanceof w||r instanceof v||r instanceof g?!1:i?i.status>=500||i.status===429:!0}var I=class{constructor(r,i){this.threshold=r;this.resetTimeout=i;this.failures=0;this.nextAttempt=0;this.isOpen=!1}get open(){return this.isOpen}recordResult(r,i,s){return i&&!(i instanceof T)?(this.setLastOpenRequest(s),this.onFailure(),!0):r&&(r.status>=500||r.status===429)?(this.setLastOpenRequest(s),this.onFailure(),!0):(s&&this.setLastSuccessRequest(s),this.onSuccess(),!1)}setHooks(r){this.hooks=r}setLastOpenRequest(r){this.lastOpenRequest=r}setLastSuccessRequest(r){this.lastSuccessRequest=r}async invoke(r){if(Date.now()<this.nextAttempt)throw new v("Circuit is open");try{let i=await r();return this.onSuccess(),i}catch(i){throw this.onFailure(),i}}onSuccess(){let r=this.isOpen;this.failures=0,r&&(this.isOpen=!1,this.hooks?.onCircuitClose&&this.lastSuccessRequest&&this.hooks.onCircuitClose(this.lastSuccessRequest)),this.lastSuccessRequest=void 0}onFailure(){this.failures++,this.failures>=this.threshold&&(this.nextAttempt=Date.now()+this.resetTimeout,this.isOpen=!0,this.hooks?.onCircuitOpen&&this.lastOpenRequest&&this.hooks.onCircuitOpen(this.lastOpenRequest))}};function W(l={}){let r=new Map,{timeout:i=5e3,retries:s=0,retryDelay:f=Q,shouldRetry:k=X,hooks:h={},circuit:y,fetchHandler:R,dedupe:q=!1,dedupeHashFn:x=G}=l,c=y?new I(y.threshold,y.reset):null;c&&(h.onCircuitClose||h.onCircuitOpen)&&c.setHooks({onCircuitClose:h.onCircuitClose,onCircuitOpen:h.onCircuitOpen});let D=[];function Y(){for(let N of D)N.controller?.abort()}let L=async(N,u={})=>{let $=typeof u.dedupe<"u"?u.dedupe:q,Z=u.dedupeHashFn||x,p,t=new Request(N,u);if($&&(p=Z({method:t.method,url:t.url,body:u.body??null,headers:t.headers,signal:u.signal===void 0||u.signal===null?void 0:u.signal,requestInit:u,request:t}),p)){if(r.has(p))return r.get(p).promise;let n=!1,d,A,P=new Promise((U,H)=>{d=e=>{n||(n=!0,U(e))},A=e=>{n||(n=!0,H(e))}});r.set(p,{promise:P,resolve:d,reject:A})}let o={...h,...u.hooks||{}};o.transformRequest&&(t=await o.transformRequest(t)),await o.before?.(t);let z=typeof u.throwOnHttpError<"u"?u.throwOnHttpError:l.throwOnHttpError??!1;function ee(n){if(typeof AbortSignal?.timeout=="function")return AbortSignal.timeout(n);let d=new AbortController,A=setTimeout(()=>d.abort(),n);return d.signal.addEventListener("abort",()=>clearTimeout(A),{once:!0}),d.signal}let J=u.timeout??i,b=u.signal,B=t.signal,E,O,M;J>0&&(E=ee(J));let S=[];if(b&&S.push(b),B&&B!==b&&S.push(B),E&&S.push(E),S.length===1)O=S[0],M=new AbortController;else{if(typeof AbortSignal.any!="function")throw new Error("AbortSignal.any is required for combining multiple signals. Please install a polyfill for environments that do not support it.");O=AbortSignal.any(S),M=new AbortController}let K=async()=>{let n=u.retries??s,d=typeof u.retryDelay<"u"?u.retryDelay:f,A=u.shouldRetry??k,P=0,U=e=>{P=e.attempt;let m=A(e);return m&&P<=n&&o.onRetry?.(t,P-1,e.error,e.response),m},H;try{let e=await V(async()=>{if(b?.aborted)throw o.onAbort?.(t),new w("Request was aborted by user");if(E?.aborted)throw o.onTimeout?.(t),new g("signal timed out");if(typeof O?.throwIfAborted=="function")O.throwIfAborted();else if(O?.aborted)throw b?.aborted?(o.onAbort?.(t),new w("Request was aborted by user")):E?.aborted?(o.onTimeout?.(t),new g("signal timed out")):new w("Request was aborted",new DOMException("Aborted","AbortError"));let m=new Request(t,{signal:O});try{let C=await(u.fetchHandler??R??fetch)(m);return H=C,c&&(C.status>=500||C.status===429)&&c.recordResult(C,void 0,t),C}catch(a){throw c&&c.recordResult(void 0,a,t),a instanceof DOMException&&a.name==="AbortError"?E?.aborted&&(!b||!b.aborted)?(o.onTimeout?.(t),new g("signal timed out",a)):b?.aborted?(o.onAbort?.(t),new w("Request was aborted by user")):new w("Request was aborted",new DOMException("Aborted","AbortError")):a instanceof TypeError&&/NetworkError|network error|failed to fetch|lost connection|NetworkError when attempting to fetch resource/i.test(a.message)?new j(a.message,a):a}},n,d,U,t);if(o.transformResponse&&(e=await o.transformResponse(e,t)),await o.after?.(t,e),await o.onComplete?.(t,e,void 0),z&&(e.status>=400&&e.status<500&&e.status!==429||e.status>=500||e.status===429)){let{HttpError:m}=await import("./error-7EEQP46E.min.js");throw new m(`HTTP error: ${e.status} ${e.statusText}`,e)}return e}catch(e){if(H){let a=H;if(z&&(a.status>=400&&a.status<500&&a.status!==429||a.status>=500||a.status===429)){let{HttpError:C}=await import("./error-7EEQP46E.min.js");throw new C(`HTTP error: ${a.status} ${a.statusText}`,a)}return a}if(e instanceof g)throw await o.onTimeout?.(t),await o.onError?.(t,e),await o.onComplete?.(t,void 0,e),e;if(e instanceof w)throw await o.onAbort?.(t),await o.onError?.(t,e),await o.onComplete?.(t,void 0,e),e;if(e instanceof j)throw await o.onError?.(t,e),await o.onComplete?.(t,void 0,e),e;let m=new T(typeof e=="object"&&e&&"message"in e&&typeof e.message=="string"?e.message:"Retry limit reached",e);throw await o.onError?.(t,m),await o.onComplete?.(t,void 0,m),m}},F=c?c.invoke(K).catch(async n=>{throw n instanceof v?(await o.onCircuitOpen?.(t),await o.onError?.(t,n),await o.onComplete?.(t,void 0,n)):(await o.onError?.(t,n),await o.onComplete?.(t,void 0,n)),n}):K();if($&&p&&r.has(p)){let n=r.get(p);n&&(F.then(d=>n.resolve(d),d=>n.reject(d)),r.set(p,{promise:F,resolve:n.resolve,reject:n.reject}))}let _={promise:F,request:t,controller:M};return D.push(_),F.finally(()=>{let n=D.indexOf(_);n>-1&&D.splice(n,1),$&&p&&r.get(p)?.promise===F&&r.delete(p)})};return Object.defineProperty(L,"pendingRequests",{get(){return D},enumerable:!1,configurable:!1}),Object.defineProperty(L,"abortAll",{value:Y,writable:!1,enumerable:!1,configurable:!1}),Object.defineProperty(L,"circuitOpen",{get(){return c?c.open:!1},enumerable:!0}),L}var he=W;export{w as AbortError,v as CircuitOpenError,j as NetworkError,T as RetryLimitError,g as TimeoutError,W as createClient,he as default};
|
|
2
2
|
//# sourceMappingURL=index.min.js.map
|
package/dist/index.min.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/retry.ts","../src/should-retry.ts","../src/circuit.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["import type { RetryContext } from './types.js'\n\nexport type RetryDelay = number | ((ctx: RetryContext) => number)\n\nexport const defaultDelay: RetryDelay = (ctx) => {\n const retryAfter = ctx.response?.headers.get('Retry-After')\n if (retryAfter) {\n const seconds = parseInt(retryAfter, 10)\n if (!isNaN(seconds)) return seconds * 1000\n const date = Date.parse(retryAfter)\n if (!isNaN(date)) return Math.max(0, date - Date.now())\n }\n return 2 ** ctx.attempt * 200 + Math.random() * 100\n}\n\nexport async function retry(\n fn: () => Promise<Response>,\n retries: number,\n delay: RetryDelay,\n shouldRetry: (ctx: RetryContext) => boolean = () => true,\n request: Request\n): Promise<Response> {\n let lastErr: unknown\n let lastRes: Response | undefined\n\n for (let i = 0; i <= retries; i++) {\n const ctx: RetryContext = {\n attempt: i + 1,\n request,\n response: lastRes,\n error: lastErr,\n }\n try {\n lastRes = await fn()\n ctx.response = lastRes\n ctx.error = undefined\n if (i < retries && shouldRetry(ctx)) {\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n continue\n }\n return lastRes\n } catch (err) {\n lastErr = err\n ctx.error = err\n if (i === retries || !shouldRetry(ctx)) throw err\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n throw lastErr\n}\n","import { AbortError, CircuitOpenError, TimeoutError } from './error.js'\nimport type { RetryContext } from './types.js'\n\nexport function shouldRetry(ctx: RetryContext): boolean {\n const { error, response } = ctx\n if (\n error instanceof AbortError ||\n error instanceof CircuitOpenError ||\n error instanceof TimeoutError\n )\n return false\n if (!response) return true // network error\n return response.status >= 500 || response.status === 429\n}\n","import { CircuitOpenError, RetryLimitError } from './error.js'\n\nexport class CircuitBreaker {\n private failures = 0\n private nextAttempt = 0\n private isOpen = false\n\n // Returns true if the circuit breaker is currently open (blocking requests).\n get open(): boolean {\n return this.isOpen\n }\n private hooks?: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }\n private lastSuccessRequest?: Request\n private lastOpenRequest?: Request\n\n constructor(\n private threshold: number,\n private resetTimeout: number\n ) {}\n\n // Call this after each request to record the result and update circuit state.\n // Returns true if the result was counted as a failure.\n recordResult(response?: Response, error?: unknown, req?: Request): boolean {\n // Count thrown errors (network, abort, timeout) as failures\n if (error && !(error instanceof RetryLimitError)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Count HTTP 5xx and 429 responses as failures\n if (response && (response.status >= 500 || response.status === 429)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Otherwise, count as success\n if (req) this.setLastSuccessRequest(req)\n this.onSuccess()\n return false\n }\n\n setHooks(hooks: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }) {\n this.hooks = hooks\n }\n setLastOpenRequest(req: Request) {\n this.lastOpenRequest = req\n }\n\n setLastSuccessRequest(req: Request) {\n this.lastSuccessRequest = req\n }\n\n async invoke<T>(fn: () => Promise<T>): Promise<T> {\n if (Date.now() < this.nextAttempt)\n throw new CircuitOpenError('Circuit is open')\n try {\n const res = await fn()\n this.onSuccess()\n return res\n } catch (err) {\n this.onFailure()\n throw err\n }\n }\n\n private onSuccess() {\n const wasOpen = this.isOpen\n this.failures = 0\n if (wasOpen) {\n this.isOpen = false\n if (this.hooks?.onCircuitClose && this.lastSuccessRequest) {\n this.hooks.onCircuitClose(this.lastSuccessRequest)\n }\n }\n this.lastSuccessRequest = undefined\n }\n\n private onFailure() {\n this.failures++\n if (this.failures >= this.threshold) {\n this.nextAttempt = Date.now() + this.resetTimeout\n this.isOpen = true\n if (this.hooks?.onCircuitOpen && this.lastOpenRequest) {\n this.hooks.onCircuitOpen(this.lastOpenRequest)\n }\n }\n }\n}\n","import type {\n FFetchOptions,\n FFetch,\n FFetchRequestInit,\n PendingRequest,\n} from './types.js'\nimport { retry, defaultDelay } from './retry.js'\nimport { shouldRetry as defaultShouldRetry } from './should-retry.js'\nimport { CircuitBreaker } from './circuit.js'\nimport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error.js'\n\nexport function createClient(opts: FFetchOptions = {}): FFetch {\n const {\n timeout: clientDefaultTimeout = 5_000,\n retries: clientDefaultRetries = 0,\n retryDelay: clientDefaultRetryDelay = defaultDelay,\n shouldRetry: clientDefaultShouldRetry = defaultShouldRetry,\n hooks: clientDefaultHooks = {},\n circuit: clientDefaultCircuit,\n fetchHandler,\n } = opts\n\n const breaker = clientDefaultCircuit\n ? new CircuitBreaker(\n clientDefaultCircuit.threshold,\n clientDefaultCircuit.reset\n )\n : null\n\n if (\n breaker &&\n (clientDefaultHooks.onCircuitClose || clientDefaultHooks.onCircuitOpen)\n ) {\n breaker.setHooks({\n onCircuitClose: clientDefaultHooks.onCircuitClose,\n onCircuitOpen: clientDefaultHooks.onCircuitOpen,\n })\n }\n\n const pendingRequests: PendingRequest[] = []\n\n // Helper to abort all pending requests\n function abortAll() {\n for (const entry of pendingRequests) {\n entry.controller?.abort()\n }\n }\n\n const client = async (\n input: RequestInfo | URL,\n init: FFetchRequestInit = {}\n ) => {\n // No longer require AbortSignal.timeout - we'll implement it manually if needed\n let request = new Request(input, init)\n\n // Merge hooks: per-request hooks override client hooks, but fallback to client hooks\n const effectiveHooks = { ...clientDefaultHooks, ...(init.hooks || {}) }\n if (effectiveHooks.transformRequest) {\n request = await effectiveHooks.transformRequest(request)\n }\n await effectiveHooks.before?.(request)\n\n // Determine throwOnHttpError (per-request overrides client default)\n const effectiveThrowOnHttpError =\n typeof init.throwOnHttpError !== 'undefined'\n ? init.throwOnHttpError\n : (opts.throwOnHttpError ?? false)\n\n // Create timeout signal (manual implementation if AbortSignal.timeout not available)\n function createTimeoutSignal(timeout: number): AbortSignal {\n if (typeof AbortSignal?.timeout === 'function') {\n return AbortSignal.timeout(timeout)\n }\n\n // Manual implementation for older environments\n const controller = new AbortController()\n const timeoutId = setTimeout(() => controller.abort(), timeout)\n\n // Clean up timeout if signal is aborted early by other means\n controller.signal.addEventListener(\n 'abort',\n () => clearTimeout(timeoutId),\n { once: true }\n )\n\n return controller.signal\n }\n\n // AbortSignal.timeout/any logic ---\n const effectiveTimeout = init.timeout ?? clientDefaultTimeout\n const userSignal = init.signal\n const transformedSignal = request.signal // Extract signal from transformed request\n let timeoutSignal: AbortSignal | undefined = undefined\n let combinedSignal: AbortSignal | undefined = undefined\n let controller: AbortController | undefined = undefined\n\n if (effectiveTimeout > 0) {\n timeoutSignal = createTimeoutSignal(effectiveTimeout)\n }\n\n // Collect all signals that need to be combined\n const signals: AbortSignal[] = []\n if (userSignal) signals.push(userSignal)\n if (transformedSignal && transformedSignal !== userSignal) {\n signals.push(transformedSignal)\n }\n if (timeoutSignal) signals.push(timeoutSignal)\n\n // Use AbortSignal.any for signal combination. Requires native support or a polyfill.\n // If not available, instruct users to install a polyfill for environments lacking AbortSignal.any.\n // there are always 1 or more signals\n if (signals.length === 1) {\n combinedSignal = signals[0]\n controller = new AbortController()\n } else {\n if (typeof AbortSignal.any !== 'function') {\n throw new Error(\n 'AbortSignal.any is required for combining multiple signals. Please install a polyfill for environments that do not support it.'\n )\n }\n combinedSignal = AbortSignal.any(signals)\n controller = new AbortController()\n }\n const retryWithHooks = async () => {\n const effectiveRetries = init.retries ?? clientDefaultRetries\n const effectiveRetryDelay =\n typeof init.retryDelay !== 'undefined'\n ? init.retryDelay\n : clientDefaultRetryDelay\n const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry\n\n // Wrap shouldRetry to call onRetry hook\n let attempt = 0\n const shouldRetryWithHook = (ctx: import('./types').RetryContext) => {\n attempt = ctx.attempt\n const retrying = effectiveShouldRetry(ctx)\n if (retrying && attempt <= effectiveRetries) {\n effectiveHooks.onRetry?.(\n request,\n attempt - 1,\n ctx.error,\n ctx.response\n )\n }\n return retrying\n }\n\n let lastResponse: Response | undefined = undefined\n try {\n let res = await retry(\n async () => {\n // Check for aborts before making the request\n if (userSignal?.aborted) {\n effectiveHooks.onAbort?.(request)\n // Throw AbortError for user aborts, no cause needed\n throw new AbortError('Request was aborted by user')\n }\n if (timeoutSignal?.aborted) {\n effectiveHooks.onTimeout?.(request)\n throw new TimeoutError('signal timed out')\n }\n if (typeof combinedSignal?.throwIfAborted === 'function') {\n combinedSignal.throwIfAborted()\n } else if (combinedSignal?.aborted) {\n if (userSignal?.aborted) {\n effectiveHooks.onAbort?.(request)\n throw new AbortError('Request was aborted by user')\n } else if (timeoutSignal?.aborted) {\n effectiveHooks.onTimeout?.(request)\n throw new TimeoutError('signal timed out')\n } else {\n throw new AbortError(\n 'Request was aborted',\n new DOMException('Aborted', 'AbortError')\n )\n }\n }\n const reqWithSignal = new Request(request, {\n signal: combinedSignal,\n })\n try {\n const handler = fetchHandler ?? fetch\n const response = await handler(reqWithSignal)\n lastResponse = response\n if (\n breaker &&\n (response.status >= 500 || response.status === 429)\n ) {\n breaker.recordResult(response, undefined, request)\n }\n return response\n } catch (err) {\n if (breaker) breaker.recordResult(undefined, err, request)\n if (err instanceof DOMException && err.name === 'AbortError') {\n if (\n timeoutSignal?.aborted &&\n (!userSignal || !userSignal.aborted)\n ) {\n effectiveHooks.onTimeout?.(request)\n throw new TimeoutError('signal timed out', err)\n } else if (userSignal?.aborted) {\n effectiveHooks.onAbort?.(request)\n throw new AbortError('Request was aborted by user')\n } else {\n throw new AbortError(\n 'Request was aborted',\n new DOMException('Aborted', 'AbortError')\n )\n }\n } else if (\n err instanceof TypeError &&\n /NetworkError|network error|failed to fetch|lost connection|NetworkError when attempting to fetch resource/i.test(\n err.message\n )\n ) {\n throw new NetworkError(err.message, err)\n }\n throw err\n }\n },\n effectiveRetries,\n effectiveRetryDelay,\n shouldRetryWithHook,\n request\n )\n if (effectiveHooks.transformResponse) {\n res = await effectiveHooks.transformResponse(res, request)\n }\n await effectiveHooks.after?.(request, res)\n await effectiveHooks.onComplete?.(request, res, undefined)\n // After all retries, if throwOnHttpError is true and final response is error, throw HttpError\n if (\n effectiveThrowOnHttpError &&\n ((res.status >= 400 && res.status < 500 && res.status !== 429) ||\n res.status >= 500 ||\n res.status === 429)\n ) {\n const { HttpError } = await import('./error.js')\n throw new HttpError(\n `HTTP error: ${res.status} ${res.statusText}`,\n res\n )\n }\n return res\n } catch (err: unknown) {\n // If lastResponse is set (any status), return or throw as needed\n if (lastResponse) {\n const resp = lastResponse as Response\n // Only throw if throwOnHttpError is true and final response is 4xx (not 429), 5xx, or 429\n if (\n effectiveThrowOnHttpError &&\n ((resp.status >= 400 && resp.status < 500 && resp.status !== 429) ||\n resp.status >= 500 ||\n resp.status === 429)\n ) {\n const { HttpError } = await import('./error.js')\n throw new HttpError(\n `HTTP error: ${resp.status} ${resp.statusText}`,\n resp\n )\n }\n return resp\n }\n // If the error is a known error type, re-throw it directly and call correct hook\n if (err instanceof TimeoutError) {\n await effectiveHooks.onTimeout?.(request)\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n throw err\n }\n if (err instanceof AbortError) {\n await effectiveHooks.onAbort?.(request)\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n throw err\n }\n if (err instanceof NetworkError) {\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n throw err\n }\n // Otherwise, wrap in RetryLimitError\n const retryErr = new RetryLimitError(\n typeof err === 'object' &&\n err &&\n 'message' in err &&\n typeof (err as { message?: unknown }).message === 'string'\n ? (err as { message: string }).message\n : 'Retry limit reached',\n err\n )\n await effectiveHooks.onError?.(request, retryErr)\n await effectiveHooks.onComplete?.(request, undefined, retryErr)\n throw retryErr\n }\n }\n\n const promise = breaker\n ? breaker.invoke(retryWithHooks).catch(async (err: unknown) => {\n if (err instanceof CircuitOpenError) {\n await effectiveHooks.onCircuitOpen?.(request)\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n } else {\n await effectiveHooks.onError?.(request, err)\n await effectiveHooks.onComplete?.(request, undefined, err)\n }\n throw err\n })\n : retryWithHooks()\n\n const entry: PendingRequest = {\n promise,\n request,\n controller,\n }\n pendingRequests.push(entry)\n\n return promise.finally(() => {\n const index = pendingRequests.indexOf(entry)\n if (index > -1) {\n pendingRequests.splice(index, 1)\n }\n })\n }\n\n // Add pendingRequests property to the client function (read-only)\n Object.defineProperty(client, 'pendingRequests', {\n get() {\n return pendingRequests\n },\n enumerable: false,\n configurable: false,\n })\n\n // Add abortAll method to the client function (read-only)\n Object.defineProperty(client, 'abortAll', {\n value: abortAll,\n writable: false,\n enumerable: false,\n configurable: false,\n })\n\n // Expose circuit breaker open state\n Object.defineProperty(client, 'circuitOpen', {\n get() {\n return breaker ? breaker.open : false\n },\n enumerable: true,\n })\n\n return client as FFetch\n}\n","export type { FFetch, FFetchOptions } from './types'\nexport type { Hooks } from './hooks'\n\nimport { createClient } from './client'\nexport { createClient } from './client'\n\nexport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error'\n\nexport default createClient\n"],"mappings":"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","names":["defaultDelay","ctx","retryAfter","seconds","date","retry","fn","retries","delay","shouldRetry","request","lastErr","lastRes","i","wait","r","err","shouldRetry","ctx","error","response","AbortError","CircuitOpenError","TimeoutError","CircuitBreaker","threshold","resetTimeout","response","error","req","RetryLimitError","hooks","fn","CircuitOpenError","res","err","wasOpen","createClient","opts","clientDefaultTimeout","clientDefaultRetries","clientDefaultRetryDelay","defaultDelay","clientDefaultShouldRetry","shouldRetry","clientDefaultHooks","clientDefaultCircuit","fetchHandler","breaker","CircuitBreaker","pendingRequests","abortAll","entry","client","input","init","request","effectiveHooks","effectiveThrowOnHttpError","createTimeoutSignal","timeout","controller","timeoutId","effectiveTimeout","userSignal","transformedSignal","timeoutSignal","combinedSignal","signals","retryWithHooks","effectiveRetries","effectiveRetryDelay","effectiveShouldRetry","attempt","shouldRetryWithHook","ctx","retrying","lastResponse","res","retry","AbortError","TimeoutError","reqWithSignal","response","err","NetworkError","HttpError","resp","retryErr","RetryLimitError","promise","CircuitOpenError","index","index_default","createClient"]}
|
|
1
|
+
{"version":3,"sources":["../src/dedupeRequestHash.ts","../src/retry.ts","../src/should-retry.ts","../src/circuit.ts","../src/client.ts","../src/index.ts"],"sourcesContent":["export type DedupeHashParams = {\n method: string\n url: string\n body:\n | string\n | FormData\n | URLSearchParams\n | Blob\n | ArrayBuffer\n | BufferSource\n | null\n | ReadableStream<unknown>\n headers?: Headers | Record<string, string>\n signal?: AbortSignal\n requestInit?: RequestInit\n request?: Request\n}\n\nexport function dedupeRequestHash(\n params: DedupeHashParams\n): string | undefined {\n const { method, url, body } = params\n let bodyString = ''\n if (body instanceof FormData) {\n // Skip deduplication for FormData\n return undefined\n }\n // Skip deduplication for ReadableStream\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n return undefined\n }\n if (typeof body === 'string') {\n bodyString = body\n } else if (body instanceof URLSearchParams) {\n bodyString = body.toString()\n } else if (body instanceof ArrayBuffer) {\n bodyString = Buffer.from(body).toString('base64')\n } else if (body instanceof Uint8Array) {\n bodyString = Buffer.from(body).toString('base64')\n } else if (body instanceof Blob) {\n bodyString = `[blob:${body.type}:${body.size}]`\n } else if (body == null) {\n bodyString = ''\n } else {\n try {\n bodyString = JSON.stringify(body)\n } catch {\n bodyString = '[unserializable-body]'\n }\n }\n return `${method.toUpperCase()}|${url}|${bodyString}`\n}\n","import type { RetryContext } from './types.js'\n\nexport type RetryDelay = number | ((ctx: RetryContext) => number)\n\nexport const defaultDelay: RetryDelay = (ctx) => {\n const retryAfter = ctx.response?.headers.get('Retry-After')\n if (retryAfter) {\n const seconds = parseInt(retryAfter, 10)\n if (!isNaN(seconds)) return seconds * 1000\n const date = Date.parse(retryAfter)\n if (!isNaN(date)) return Math.max(0, date - Date.now())\n }\n return 2 ** ctx.attempt * 200 + Math.random() * 100\n}\n\nexport async function retry(\n fn: () => Promise<Response>,\n retries: number,\n delay: RetryDelay,\n shouldRetry: (ctx: RetryContext) => boolean = () => true,\n request: Request\n): Promise<Response> {\n let lastErr: unknown\n let lastRes: Response | undefined\n\n for (let i = 0; i <= retries; i++) {\n const ctx: RetryContext = {\n attempt: i + 1,\n request,\n response: lastRes,\n error: lastErr,\n }\n try {\n lastRes = await fn()\n ctx.response = lastRes\n ctx.error = undefined\n if (i < retries && shouldRetry(ctx)) {\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n continue\n }\n return lastRes\n } catch (err) {\n lastErr = err\n ctx.error = err\n if (i === retries || !shouldRetry(ctx)) throw err\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n throw lastErr\n}\n","import { AbortError, CircuitOpenError, TimeoutError } from './error.js'\nimport type { RetryContext } from './types.js'\n\nexport function shouldRetry(ctx: RetryContext): boolean {\n const { error, response } = ctx\n if (\n error instanceof AbortError ||\n error instanceof CircuitOpenError ||\n error instanceof TimeoutError\n )\n return false\n if (!response) return true // network error\n return response.status >= 500 || response.status === 429\n}\n","import { CircuitOpenError, RetryLimitError } from './error.js'\n\nexport class CircuitBreaker {\n private failures = 0\n private nextAttempt = 0\n private isOpen = false\n\n // Returns true if the circuit breaker is currently open (blocking requests).\n get open(): boolean {\n return this.isOpen\n }\n private hooks?: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }\n private lastSuccessRequest?: Request\n private lastOpenRequest?: Request\n\n constructor(\n private threshold: number,\n private resetTimeout: number\n ) {}\n\n // Call this after each request to record the result and update circuit state.\n // Returns true if the result was counted as a failure.\n recordResult(response?: Response, error?: unknown, req?: Request): boolean {\n // Count thrown errors (network, abort, timeout) as failures\n if (error && !(error instanceof RetryLimitError)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Count HTTP 5xx and 429 responses as failures\n if (response && (response.status >= 500 || response.status === 429)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Otherwise, count as success\n if (req) this.setLastSuccessRequest(req)\n this.onSuccess()\n return false\n }\n\n setHooks(hooks: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }) {\n this.hooks = hooks\n }\n setLastOpenRequest(req: Request) {\n this.lastOpenRequest = req\n }\n\n setLastSuccessRequest(req: Request) {\n this.lastSuccessRequest = req\n }\n\n async invoke<T>(fn: () => Promise<T>): Promise<T> {\n if (Date.now() < this.nextAttempt)\n throw new CircuitOpenError('Circuit is open')\n try {\n const res = await fn()\n this.onSuccess()\n return res\n } catch (err) {\n this.onFailure()\n throw err\n }\n }\n\n private onSuccess() {\n const wasOpen = this.isOpen\n this.failures = 0\n if (wasOpen) {\n this.isOpen = false\n if (this.hooks?.onCircuitClose && this.lastSuccessRequest) {\n this.hooks.onCircuitClose(this.lastSuccessRequest)\n }\n }\n this.lastSuccessRequest = undefined\n }\n\n private onFailure() {\n this.failures++\n if (this.failures >= this.threshold) {\n this.nextAttempt = Date.now() + this.resetTimeout\n this.isOpen = true\n if (this.hooks?.onCircuitOpen && this.lastOpenRequest) {\n this.hooks.onCircuitOpen(this.lastOpenRequest)\n }\n }\n }\n}\n","import type {\r\n FFetchOptions,\r\n FFetch,\r\n FFetchRequestInit,\r\n PendingRequest,\r\n} from './types.js'\r\nimport { dedupeRequestHash } from './dedupeRequestHash.js'\r\nimport { retry, defaultDelay } from './retry.js'\r\nimport { shouldRetry as defaultShouldRetry } from './should-retry.js'\r\nimport { CircuitBreaker } from './circuit.js'\r\nimport {\r\n TimeoutError,\r\n CircuitOpenError,\r\n AbortError,\r\n RetryLimitError,\r\n NetworkError,\r\n} from './error.js'\r\n\r\nexport function createClient(opts: FFetchOptions = {}): FFetch {\r\n // Track in-flight deduped requests and their resolvers\r\n const dedupeMap = new Map<\r\n string,\r\n {\r\n promise: Promise<Response>\r\n resolve: (value: Response | PromiseLike<Response>) => void\r\n reject: (reason?: unknown) => void\r\n }\r\n >()\r\n const {\r\n timeout: clientDefaultTimeout = 5_000,\r\n retries: clientDefaultRetries = 0,\r\n retryDelay: clientDefaultRetryDelay = defaultDelay,\r\n shouldRetry: clientDefaultShouldRetry = defaultShouldRetry,\r\n hooks: clientDefaultHooks = {},\r\n circuit: clientDefaultCircuit,\r\n fetchHandler,\r\n dedupe = false,\r\n dedupeHashFn = dedupeRequestHash,\r\n } = opts\r\n\r\n const breaker = clientDefaultCircuit\r\n ? new CircuitBreaker(\r\n clientDefaultCircuit.threshold,\r\n clientDefaultCircuit.reset\r\n )\r\n : null\r\n\r\n if (\r\n breaker &&\r\n (clientDefaultHooks.onCircuitClose || clientDefaultHooks.onCircuitOpen)\r\n ) {\r\n breaker.setHooks({\r\n onCircuitClose: clientDefaultHooks.onCircuitClose,\r\n onCircuitOpen: clientDefaultHooks.onCircuitOpen,\r\n })\r\n }\r\n\r\n const pendingRequests: PendingRequest[] = []\r\n\r\n // Helper to abort all pending requests\r\n function abortAll() {\r\n for (const entry of pendingRequests) {\r\n entry.controller?.abort()\r\n }\r\n }\r\n\r\n const client = async (\r\n input: RequestInfo | URL,\r\n init: FFetchRequestInit = {}\r\n ) => {\r\n // Deduplication logic\r\n const effectiveDedupe =\r\n typeof init.dedupe !== 'undefined' ? init.dedupe : dedupe\r\n const effectiveDedupeHashFn = init.dedupeHashFn || dedupeHashFn\r\n let dedupeKey: string | undefined\r\n\r\n let request = new Request(input, init)\r\n if (effectiveDedupe) {\r\n dedupeKey = effectiveDedupeHashFn({\r\n method: request.method,\r\n url: request.url,\r\n body: init.body ?? null,\r\n headers: request.headers,\r\n signal:\r\n init.signal === undefined || init.signal === null\r\n ? undefined\r\n : init.signal,\r\n requestInit: init,\r\n request,\r\n })\r\n if (dedupeKey) {\r\n if (dedupeMap.has(dedupeKey)) {\r\n return dedupeMap.get(dedupeKey)!.promise\r\n }\r\n let settled = false\r\n let resolveFn: (value: Response | PromiseLike<Response>) => void\r\n let rejectFn: (reason?: unknown) => void\r\n const inFlightPromise = new Promise<Response>((resolve, reject) => {\r\n resolveFn = (value) => {\r\n if (!settled) {\r\n settled = true\r\n resolve(value)\r\n }\r\n }\r\n rejectFn = (reason) => {\r\n if (!settled) {\r\n settled = true\r\n reject(reason)\r\n }\r\n }\r\n })\r\n dedupeMap.set(dedupeKey, {\r\n promise: inFlightPromise,\r\n resolve: resolveFn!,\r\n reject: rejectFn!,\r\n })\r\n }\r\n }\r\n\r\n // Merge hooks: per-request hooks override client hooks, but fallback to client hooks\r\n const effectiveHooks = { ...clientDefaultHooks, ...(init.hooks || {}) }\r\n if (effectiveHooks.transformRequest) {\r\n request = await effectiveHooks.transformRequest(request)\r\n }\r\n await effectiveHooks.before?.(request)\r\n\r\n // Determine throwOnHttpError (per-request overrides client default)\r\n const effectiveThrowOnHttpError =\r\n typeof init.throwOnHttpError !== 'undefined'\r\n ? init.throwOnHttpError\r\n : (opts.throwOnHttpError ?? false)\r\n\r\n // Create timeout signal (manual implementation if AbortSignal.timeout not available)\r\n function createTimeoutSignal(timeout: number): AbortSignal {\r\n if (typeof AbortSignal?.timeout === 'function') {\r\n return AbortSignal.timeout(timeout)\r\n }\r\n const controller = new AbortController()\r\n const timeoutId = setTimeout(() => controller.abort(), timeout)\r\n controller.signal.addEventListener(\r\n 'abort',\r\n () => clearTimeout(timeoutId),\r\n { once: true }\r\n )\r\n return controller.signal\r\n }\r\n\r\n // AbortSignal.timeout/any logic\r\n const effectiveTimeout = init.timeout ?? clientDefaultTimeout\r\n const userSignal = init.signal\r\n const transformedSignal = request.signal\r\n let timeoutSignal: AbortSignal | undefined = undefined\r\n let combinedSignal: AbortSignal | undefined = undefined\r\n let controller: AbortController | undefined = undefined\r\n\r\n if (effectiveTimeout > 0) {\r\n timeoutSignal = createTimeoutSignal(effectiveTimeout)\r\n }\r\n\r\n const signals: AbortSignal[] = []\r\n if (userSignal) signals.push(userSignal)\r\n if (transformedSignal && transformedSignal !== userSignal) {\r\n signals.push(transformedSignal)\r\n }\r\n if (timeoutSignal) signals.push(timeoutSignal)\r\n\r\n if (signals.length === 1) {\r\n combinedSignal = signals[0]\r\n controller = new AbortController()\r\n } else {\r\n if (typeof AbortSignal.any !== 'function') {\r\n throw new Error(\r\n 'AbortSignal.any is required for combining multiple signals. Please install a polyfill for environments that do not support it.'\r\n )\r\n }\r\n combinedSignal = AbortSignal.any(signals)\r\n controller = new AbortController()\r\n }\r\n\r\n const retryWithHooks = async () => {\r\n const effectiveRetries = init.retries ?? clientDefaultRetries\r\n const effectiveRetryDelay =\r\n typeof init.retryDelay !== 'undefined'\r\n ? init.retryDelay\r\n : clientDefaultRetryDelay\r\n const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry\r\n\r\n let attempt = 0\r\n const shouldRetryWithHook = (ctx: import('./types').RetryContext) => {\r\n attempt = ctx.attempt\r\n const retrying = effectiveShouldRetry(ctx)\r\n if (retrying && attempt <= effectiveRetries) {\r\n effectiveHooks.onRetry?.(\r\n request,\r\n attempt - 1,\r\n ctx.error,\r\n ctx.response\r\n )\r\n }\r\n return retrying\r\n }\r\n\r\n let lastResponse: Response | undefined = undefined\r\n try {\r\n let res = await retry(\r\n async () => {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n }\r\n if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n }\r\n if (typeof combinedSignal?.throwIfAborted === 'function') {\r\n combinedSignal.throwIfAborted()\r\n } else if (combinedSignal?.aborted) {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n }\r\n const reqWithSignal = new Request(request, {\r\n signal: combinedSignal,\r\n })\r\n try {\r\n const handler = init.fetchHandler ?? fetchHandler ?? fetch\r\n const response = await handler(reqWithSignal)\r\n lastResponse = response\r\n if (\r\n breaker &&\r\n (response.status >= 500 || response.status === 429)\r\n ) {\r\n breaker.recordResult(response, undefined, request)\r\n }\r\n return response\r\n } catch (err) {\r\n if (breaker) breaker.recordResult(undefined, err, request)\r\n if (err instanceof DOMException && err.name === 'AbortError') {\r\n if (\r\n timeoutSignal?.aborted &&\r\n (!userSignal || !userSignal.aborted)\r\n ) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out', err)\r\n } else if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n } else if (\r\n err instanceof TypeError &&\r\n /NetworkError|network error|failed to fetch|lost connection|NetworkError when attempting to fetch resource/i.test(\r\n err.message\r\n )\r\n ) {\r\n throw new NetworkError(err.message, err)\r\n }\r\n throw err\r\n }\r\n },\r\n effectiveRetries,\r\n effectiveRetryDelay,\r\n shouldRetryWithHook,\r\n request\r\n )\r\n if (effectiveHooks.transformResponse) {\r\n res = await effectiveHooks.transformResponse(res, request)\r\n }\r\n await effectiveHooks.after?.(request, res)\r\n await effectiveHooks.onComplete?.(request, res, undefined)\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((res.status >= 400 && res.status < 500 && res.status !== 429) ||\r\n res.status >= 500 ||\r\n res.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${res.status} ${res.statusText}`,\r\n res\r\n )\r\n }\r\n return res\r\n } catch (err: unknown) {\r\n if (lastResponse) {\r\n const resp = lastResponse as Response\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((resp.status >= 400 && resp.status < 500 && resp.status !== 429) ||\r\n resp.status >= 500 ||\r\n resp.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${resp.status} ${resp.statusText}`,\r\n resp\r\n )\r\n }\r\n return resp\r\n }\r\n if (err instanceof TimeoutError) {\r\n await effectiveHooks.onTimeout?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof AbortError) {\r\n await effectiveHooks.onAbort?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof NetworkError) {\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n const retryErr = new RetryLimitError(\r\n typeof err === 'object' &&\r\n err &&\r\n 'message' in err &&\r\n typeof (err as { message?: unknown }).message === 'string'\r\n ? (err as { message: string }).message\r\n : 'Retry limit reached',\r\n err\r\n )\r\n await effectiveHooks.onError?.(request, retryErr)\r\n await effectiveHooks.onComplete?.(request, undefined, retryErr)\r\n throw retryErr\r\n }\r\n }\r\n\r\n const actualPromise = breaker\r\n ? breaker.invoke(retryWithHooks).catch(async (err: unknown) => {\r\n if (err instanceof CircuitOpenError) {\r\n await effectiveHooks.onCircuitOpen?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n } else {\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n }\r\n throw err\r\n })\r\n : retryWithHooks()\r\n\r\n // If deduplication is enabled and dedupeKey is set, resolve/reject the in-flight promise\r\n if (effectiveDedupe && dedupeKey && dedupeMap.has(dedupeKey)) {\r\n const entry = dedupeMap.get(dedupeKey)\r\n if (entry) {\r\n actualPromise.then(\r\n (result) => entry.resolve(result),\r\n (error) => entry.reject(error)\r\n )\r\n // Replace the placeholder with the actual promise for future requests\r\n dedupeMap.set(dedupeKey, {\r\n promise: actualPromise,\r\n resolve: entry.resolve,\r\n reject: entry.reject,\r\n })\r\n }\r\n }\r\n\r\n const pendingEntry: PendingRequest = {\r\n promise: actualPromise,\r\n request,\r\n controller,\r\n }\r\n pendingRequests.push(pendingEntry)\r\n\r\n return actualPromise.finally(() => {\r\n const index = pendingRequests.indexOf(pendingEntry)\r\n if (index > -1) {\r\n pendingRequests.splice(index, 1)\r\n }\r\n // Only delete dedupeMap entry if the promise is the same as the one in the map\r\n if (\r\n effectiveDedupe &&\r\n dedupeKey &&\r\n dedupeMap.get(dedupeKey)?.promise === actualPromise\r\n ) {\r\n dedupeMap.delete(dedupeKey)\r\n }\r\n })\r\n }\r\n\r\n Object.defineProperty(client, 'pendingRequests', {\r\n get() {\r\n return pendingRequests\r\n },\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperty(client, 'abortAll', {\r\n value: abortAll,\r\n writable: false,\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperty(client, 'circuitOpen', {\r\n get() {\r\n return breaker ? breaker.open : false\r\n },\r\n enumerable: true,\r\n })\r\n\r\n return client as FFetch\r\n}\r\n","export type { FFetch, FFetchOptions } from './types'\nexport type { DedupeHashParams } from './dedupeRequestHash'\nexport type { Hooks } from './hooks'\n\nimport { createClient } from './client'\nexport { createClient } from './client'\n\nexport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error'\n\nexport default createClient\n"],"mappings":"wEAkBO,SAASA,EACdC,EACoB,CACpB,GAAM,CAAE,OAAAC,EAAQ,IAAAC,EAAK,KAAAC,CAAK,EAAIH,EAC1BI,EAAa,GACjB,GAAI,EAAAD,aAAgB,WAKhB,SAAO,eAAmB,KAAeA,aAAgB,gBAG7D,IAAI,OAAOA,GAAS,SAClBC,EAAaD,UACJA,aAAgB,gBACzBC,EAAaD,EAAK,SAAS,UAClBA,aAAgB,YACzBC,EAAa,OAAO,KAAKD,CAAI,EAAE,SAAS,QAAQ,UACvCA,aAAgB,WACzBC,EAAa,OAAO,KAAKD,CAAI,EAAE,SAAS,QAAQ,UACvCA,aAAgB,KACzBC,EAAa,SAASD,EAAK,IAAI,IAAIA,EAAK,IAAI,YACnCA,GAAQ,KACjBC,EAAa,OAEb,IAAI,CACFA,EAAa,KAAK,UAAUD,CAAI,CAClC,MAAQ,CACNC,EAAa,uBACf,CAEF,MAAO,GAAGH,EAAO,YAAY,CAAC,IAAIC,CAAG,IAAIE,CAAU,GACrD,CC/CO,IAAMC,EAA4BC,GAAQ,CAC/C,IAAMC,EAAaD,EAAI,UAAU,QAAQ,IAAI,aAAa,EAC1D,GAAIC,EAAY,CACd,IAAMC,EAAU,SAASD,EAAY,EAAE,EACvC,GAAI,CAAC,MAAMC,CAAO,EAAG,OAAOA,EAAU,IACtC,IAAMC,EAAO,KAAK,MAAMF,CAAU,EAClC,GAAI,CAAC,MAAME,CAAI,EAAG,OAAO,KAAK,IAAI,EAAGA,EAAO,KAAK,IAAI,CAAC,CACxD,CACA,MAAO,IAAKH,EAAI,QAAU,IAAM,KAAK,OAAO,EAAI,GAClD,EAEA,eAAsBI,EACpBC,EACAC,EACAC,EACAC,EAA8C,IAAM,GACpDC,EACmB,CACnB,IAAIC,EACAC,EAEJ,QAASC,EAAI,EAAGA,GAAKN,EAASM,IAAK,CACjC,IAAMZ,EAAoB,CACxB,QAASY,EAAI,EACb,QAAAH,EACA,SAAUE,EACV,MAAOD,CACT,EACA,GAAI,CAIF,GAHAC,EAAU,MAAMN,EAAG,EACnBL,EAAI,SAAWW,EACfX,EAAI,MAAQ,OACRY,EAAIN,GAAWE,EAAYR,CAAG,EAAG,CACnC,IAAMa,EAAO,OAAON,GAAU,WAAaA,EAAMP,CAAG,EAAIO,EACxD,MAAM,IAAI,QAASO,GAAM,WAAWA,EAAGD,CAAI,CAAC,EAC5C,QACF,CACA,OAAOF,CACT,OAASI,EAAK,CAGZ,GAFAL,EAAUK,EACVf,EAAI,MAAQe,EACRH,IAAMN,GAAW,CAACE,EAAYR,CAAG,EAAG,MAAMe,EAC9C,IAAMF,EAAO,OAAON,GAAU,WAAaA,EAAMP,CAAG,EAAIO,EACxD,MAAM,IAAI,QAASO,GAAM,WAAWA,EAAGD,CAAI,CAAC,CAC9C,CACF,CACA,MAAMH,CACR,CChDO,SAASM,EAAYC,EAA4B,CACtD,GAAM,CAAE,MAAAC,EAAO,SAAAC,CAAS,EAAIF,EAC5B,OACEC,aAAiBE,GACjBF,aAAiBG,GACjBH,aAAiBI,EAEV,GACJH,EACEA,EAAS,QAAU,KAAOA,EAAS,SAAW,IAD/B,EAExB,CCXO,IAAMI,EAAN,KAAqB,CAgB1B,YACUC,EACAC,EACR,CAFQ,eAAAD,EACA,kBAAAC,EAjBV,KAAQ,SAAW,EACnB,KAAQ,YAAc,EACtB,KAAQ,OAAS,EAgBd,CAbH,IAAI,MAAgB,CAClB,OAAO,KAAK,MACd,CAeA,aAAaC,EAAqBC,EAAiBC,EAAwB,CAEzE,OAAID,GAAS,EAAEA,aAAiBE,IAC9B,KAAK,mBAAmBD,CAAI,EAC5B,KAAK,UAAU,EACR,IAGLF,IAAaA,EAAS,QAAU,KAAOA,EAAS,SAAW,MAC7D,KAAK,mBAAmBE,CAAI,EAC5B,KAAK,UAAU,EACR,KAGLA,GAAK,KAAK,sBAAsBA,CAAG,EACvC,KAAK,UAAU,EACR,GACT,CAEA,SAASE,EAGN,CACD,KAAK,MAAQA,CACf,CACA,mBAAmBF,EAAc,CAC/B,KAAK,gBAAkBA,CACzB,CAEA,sBAAsBA,EAAc,CAClC,KAAK,mBAAqBA,CAC5B,CAEA,MAAM,OAAUG,EAAkC,CAChD,GAAI,KAAK,IAAI,EAAI,KAAK,YACpB,MAAM,IAAIC,EAAiB,iBAAiB,EAC9C,GAAI,CACF,IAAMC,EAAM,MAAMF,EAAG,EACrB,YAAK,UAAU,EACRE,CACT,OAASC,EAAK,CACZ,WAAK,UAAU,EACTA,CACR,CACF,CAEQ,WAAY,CAClB,IAAMC,EAAU,KAAK,OACrB,KAAK,SAAW,EACZA,IACF,KAAK,OAAS,GACV,KAAK,OAAO,gBAAkB,KAAK,oBACrC,KAAK,MAAM,eAAe,KAAK,kBAAkB,GAGrD,KAAK,mBAAqB,MAC5B,CAEQ,WAAY,CAClB,KAAK,WACD,KAAK,UAAY,KAAK,YACxB,KAAK,YAAc,KAAK,IAAI,EAAI,KAAK,aACrC,KAAK,OAAS,GACV,KAAK,OAAO,eAAiB,KAAK,iBACpC,KAAK,MAAM,cAAc,KAAK,eAAe,EAGnD,CACF,EC3EO,SAASC,EAAaC,EAAsB,CAAC,EAAW,CAE7D,IAAMC,EAAY,IAAI,IAQhB,CACJ,QAASC,EAAuB,IAChC,QAASC,EAAuB,EAChC,WAAYC,EAA0BC,EACtC,YAAaC,EAA2BC,EACxC,MAAOC,EAAqB,CAAC,EAC7B,QAASC,EACT,aAAAC,EACA,OAAAC,EAAS,GACT,aAAAC,EAAeC,CACjB,EAAIb,EAEEc,EAAUL,EACZ,IAAIM,EACFN,EAAqB,UACrBA,EAAqB,KACvB,EACA,KAGFK,IACCN,EAAmB,gBAAkBA,EAAmB,gBAEzDM,EAAQ,SAAS,CACf,eAAgBN,EAAmB,eACnC,cAAeA,EAAmB,aACpC,CAAC,EAGH,IAAMQ,EAAoC,CAAC,EAG3C,SAASC,GAAW,CAClB,QAAWC,KAASF,EAClBE,EAAM,YAAY,MAAM,CAE5B,CAEA,IAAMC,EAAS,MACbC,EACAC,EAA0B,CAAC,IACxB,CAEH,IAAMC,EACJ,OAAOD,EAAK,OAAW,IAAcA,EAAK,OAASV,EAC/CY,EAAwBF,EAAK,cAAgBT,EAC/CY,EAEAC,EAAU,IAAI,QAAQL,EAAOC,CAAI,EACrC,GAAIC,IACFE,EAAYD,EAAsB,CAChC,OAAQE,EAAQ,OAChB,IAAKA,EAAQ,IACb,KAAMJ,EAAK,MAAQ,KACnB,QAASI,EAAQ,QACjB,OACEJ,EAAK,SAAW,QAAaA,EAAK,SAAW,KACzC,OACAA,EAAK,OACX,YAAaA,EACb,QAAAI,CACF,CAAC,EACGD,GAAW,CACb,GAAIvB,EAAU,IAAIuB,CAAS,EACzB,OAAOvB,EAAU,IAAIuB,CAAS,EAAG,QAEnC,IAAIE,EAAU,GACVC,EACAC,EACEC,EAAkB,IAAI,QAAkB,CAACC,EAASC,IAAW,CACjEJ,EAAaK,GAAU,CAChBN,IACHA,EAAU,GACVI,EAAQE,CAAK,EAEjB,EACAJ,EAAYK,GAAW,CAChBP,IACHA,EAAU,GACVK,EAAOE,CAAM,EAEjB,CACF,CAAC,EACDhC,EAAU,IAAIuB,EAAW,CACvB,QAASK,EACT,QAASF,EACT,OAAQC,CACV,CAAC,CACH,CAIF,IAAMM,EAAiB,CAAE,GAAG1B,EAAoB,GAAIa,EAAK,OAAS,CAAC,CAAG,EAClEa,EAAe,mBACjBT,EAAU,MAAMS,EAAe,iBAAiBT,CAAO,GAEzD,MAAMS,EAAe,SAAST,CAAO,EAGrC,IAAMU,EACJ,OAAOd,EAAK,iBAAqB,IAC7BA,EAAK,iBACJrB,EAAK,kBAAoB,GAGhC,SAASoC,GAAoBC,EAA8B,CACzD,GAAI,OAAO,aAAa,SAAY,WAClC,OAAO,YAAY,QAAQA,CAAO,EAEpC,IAAMC,EAAa,IAAI,gBACjBC,EAAY,WAAW,IAAMD,EAAW,MAAM,EAAGD,CAAO,EAC9D,OAAAC,EAAW,OAAO,iBAChB,QACA,IAAM,aAAaC,CAAS,EAC5B,CAAE,KAAM,EAAK,CACf,EACOD,EAAW,MACpB,CAGA,IAAME,EAAmBnB,EAAK,SAAWnB,EACnCuC,EAAapB,EAAK,OAClBqB,EAAoBjB,EAAQ,OAC9BkB,EACAC,EACAN,EAEAE,EAAmB,IACrBG,EAAgBP,GAAoBI,CAAgB,GAGtD,IAAMK,EAAyB,CAAC,EAOhC,GANIJ,GAAYI,EAAQ,KAAKJ,CAAU,EACnCC,GAAqBA,IAAsBD,GAC7CI,EAAQ,KAAKH,CAAiB,EAE5BC,GAAeE,EAAQ,KAAKF,CAAa,EAEzCE,EAAQ,SAAW,EACrBD,EAAiBC,EAAQ,CAAC,EAC1BP,EAAa,IAAI,oBACZ,CACL,GAAI,OAAO,YAAY,KAAQ,WAC7B,MAAM,IAAI,MACR,gIACF,EAEFM,EAAiB,YAAY,IAAIC,CAAO,EACxCP,EAAa,IAAI,eACnB,CAEA,IAAMQ,EAAiB,SAAY,CACjC,IAAMC,EAAmB1B,EAAK,SAAWlB,EACnC6C,EACJ,OAAO3B,EAAK,WAAe,IACvBA,EAAK,WACLjB,EACA6C,EAAuB5B,EAAK,aAAef,EAE7C4C,EAAU,EACRC,EAAuBC,GAAwC,CACnEF,EAAUE,EAAI,QACd,IAAMC,EAAWJ,EAAqBG,CAAG,EACzC,OAAIC,GAAYH,GAAWH,GACzBb,EAAe,UACbT,EACAyB,EAAU,EACVE,EAAI,MACJA,EAAI,QACN,EAEKC,CACT,EAEIC,EACJ,GAAI,CACF,IAAIC,EAAM,MAAMC,EACd,SAAY,CACV,GAAIf,GAAY,QACd,MAAAP,EAAe,UAAUT,CAAO,EAC1B,IAAIgC,EAAW,6BAA6B,EAEpD,GAAId,GAAe,QACjB,MAAAT,EAAe,YAAYT,CAAO,EAC5B,IAAIiC,EAAa,kBAAkB,EAE3C,GAAI,OAAOd,GAAgB,gBAAmB,WAC5CA,EAAe,eAAe,UACrBA,GAAgB,QACzB,MAAIH,GAAY,SACdP,EAAe,UAAUT,CAAO,EAC1B,IAAIgC,EAAW,6BAA6B,GACzCd,GAAe,SACxBT,EAAe,YAAYT,CAAO,EAC5B,IAAIiC,EAAa,kBAAkB,GAEnC,IAAID,EACR,sBACA,IAAI,aAAa,UAAW,YAAY,CAC1C,EAGJ,IAAME,EAAgB,IAAI,QAAQlC,EAAS,CACzC,OAAQmB,CACV,CAAC,EACD,GAAI,CAEF,IAAMgB,EAAW,MADDvC,EAAK,cAAgBX,GAAgB,OACtBiD,CAAa,EAC5C,OAAAL,EAAeM,EAEb9C,IACC8C,EAAS,QAAU,KAAOA,EAAS,SAAW,MAE/C9C,EAAQ,aAAa8C,EAAU,OAAWnC,CAAO,EAE5CmC,CACT,OAASC,EAAK,CAEZ,MADI/C,GAASA,EAAQ,aAAa,OAAW+C,EAAKpC,CAAO,EACrDoC,aAAe,cAAgBA,EAAI,OAAS,aAE5ClB,GAAe,UACd,CAACF,GAAc,CAACA,EAAW,UAE5BP,EAAe,YAAYT,CAAO,EAC5B,IAAIiC,EAAa,mBAAoBG,CAAG,GACrCpB,GAAY,SACrBP,EAAe,UAAUT,CAAO,EAC1B,IAAIgC,EAAW,6BAA6B,GAE5C,IAAIA,EACR,sBACA,IAAI,aAAa,UAAW,YAAY,CAC1C,EAGFI,aAAe,WACf,6GAA6G,KAC3GA,EAAI,OACN,EAEM,IAAIC,EAAaD,EAAI,QAASA,CAAG,EAEnCA,CACR,CACF,EACAd,EACAC,EACAG,EACA1B,CACF,EAMA,GALIS,EAAe,oBACjBqB,EAAM,MAAMrB,EAAe,kBAAkBqB,EAAK9B,CAAO,GAE3D,MAAMS,EAAe,QAAQT,EAAS8B,CAAG,EACzC,MAAMrB,EAAe,aAAaT,EAAS8B,EAAK,MAAS,EAEvDpB,IACEoB,EAAI,QAAU,KAAOA,EAAI,OAAS,KAAOA,EAAI,SAAW,KACxDA,EAAI,QAAU,KACdA,EAAI,SAAW,KACjB,CACA,GAAM,CAAE,UAAAQ,CAAU,EAAI,KAAM,QAAO,yBAAY,EAC/C,MAAM,IAAIA,EACR,eAAeR,EAAI,MAAM,IAAIA,EAAI,UAAU,GAC3CA,CACF,CACF,CACA,OAAOA,CACT,OAASM,EAAc,CACrB,GAAIP,EAAc,CAChB,IAAMU,EAAOV,EACb,GACEnB,IACE6B,EAAK,QAAU,KAAOA,EAAK,OAAS,KAAOA,EAAK,SAAW,KAC3DA,EAAK,QAAU,KACfA,EAAK,SAAW,KAClB,CACA,GAAM,CAAE,UAAAD,CAAU,EAAI,KAAM,QAAO,yBAAY,EAC/C,MAAM,IAAIA,EACR,eAAeC,EAAK,MAAM,IAAIA,EAAK,UAAU,GAC7CA,CACF,CACF,CACA,OAAOA,CACT,CACA,GAAIH,aAAeH,EACjB,YAAMxB,EAAe,YAAYT,CAAO,EACxC,MAAMS,EAAe,UAAUT,EAASoC,CAAG,EAC3C,MAAM3B,EAAe,aAAaT,EAAS,OAAWoC,CAAG,EACnDA,EAER,GAAIA,aAAeJ,EACjB,YAAMvB,EAAe,UAAUT,CAAO,EACtC,MAAMS,EAAe,UAAUT,EAASoC,CAAG,EAC3C,MAAM3B,EAAe,aAAaT,EAAS,OAAWoC,CAAG,EACnDA,EAER,GAAIA,aAAeC,EACjB,YAAM5B,EAAe,UAAUT,EAASoC,CAAG,EAC3C,MAAM3B,EAAe,aAAaT,EAAS,OAAWoC,CAAG,EACnDA,EAER,IAAMI,EAAW,IAAIC,EACnB,OAAOL,GAAQ,UACfA,GACA,YAAaA,GACb,OAAQA,EAA8B,SAAY,SAC7CA,EAA4B,QAC7B,sBACJA,CACF,EACA,YAAM3B,EAAe,UAAUT,EAASwC,CAAQ,EAChD,MAAM/B,EAAe,aAAaT,EAAS,OAAWwC,CAAQ,EACxDA,CACR,CACF,EAEME,EAAgBrD,EAClBA,EAAQ,OAAOgC,CAAc,EAAE,MAAM,MAAOe,GAAiB,CAC3D,MAAIA,aAAeO,GACjB,MAAMlC,EAAe,gBAAgBT,CAAO,EAC5C,MAAMS,EAAe,UAAUT,EAASoC,CAAG,EAC3C,MAAM3B,EAAe,aAAaT,EAAS,OAAWoC,CAAG,IAEzD,MAAM3B,EAAe,UAAUT,EAASoC,CAAG,EAC3C,MAAM3B,EAAe,aAAaT,EAAS,OAAWoC,CAAG,GAErDA,CACR,CAAC,EACDf,EAAe,EAGnB,GAAIxB,GAAmBE,GAAavB,EAAU,IAAIuB,CAAS,EAAG,CAC5D,IAAMN,EAAQjB,EAAU,IAAIuB,CAAS,EACjCN,IACFiD,EAAc,KACXE,GAAWnD,EAAM,QAAQmD,CAAM,EAC/BC,GAAUpD,EAAM,OAAOoD,CAAK,CAC/B,EAEArE,EAAU,IAAIuB,EAAW,CACvB,QAAS2C,EACT,QAASjD,EAAM,QACf,OAAQA,EAAM,MAChB,CAAC,EAEL,CAEA,IAAMqD,EAA+B,CACnC,QAASJ,EACT,QAAA1C,EACA,WAAAa,CACF,EACA,OAAAtB,EAAgB,KAAKuD,CAAY,EAE1BJ,EAAc,QAAQ,IAAM,CACjC,IAAMK,EAAQxD,EAAgB,QAAQuD,CAAY,EAC9CC,EAAQ,IACVxD,EAAgB,OAAOwD,EAAO,CAAC,EAI/BlD,GACAE,GACAvB,EAAU,IAAIuB,CAAS,GAAG,UAAY2C,GAEtClE,EAAU,OAAOuB,CAAS,CAE9B,CAAC,CACH,EAEA,cAAO,eAAeL,EAAQ,kBAAmB,CAC/C,KAAM,CACJ,OAAOH,CACT,EACA,WAAY,GACZ,aAAc,EAChB,CAAC,EAED,OAAO,eAAeG,EAAQ,WAAY,CACxC,MAAOF,EACP,SAAU,GACV,WAAY,GACZ,aAAc,EAChB,CAAC,EAED,OAAO,eAAeE,EAAQ,cAAe,CAC3C,KAAM,CACJ,OAAOL,EAAUA,EAAQ,KAAO,EAClC,EACA,WAAY,EACd,CAAC,EAEMK,CACT,CCvZA,IAAOsD,GAAQC","names":["dedupeRequestHash","params","method","url","body","bodyString","defaultDelay","ctx","retryAfter","seconds","date","retry","fn","retries","delay","shouldRetry","request","lastErr","lastRes","i","wait","r","err","shouldRetry","ctx","error","response","AbortError","CircuitOpenError","TimeoutError","CircuitBreaker","threshold","resetTimeout","response","error","req","RetryLimitError","hooks","fn","CircuitOpenError","res","err","wasOpen","createClient","opts","dedupeMap","clientDefaultTimeout","clientDefaultRetries","clientDefaultRetryDelay","defaultDelay","clientDefaultShouldRetry","shouldRetry","clientDefaultHooks","clientDefaultCircuit","fetchHandler","dedupe","dedupeHashFn","dedupeRequestHash","breaker","CircuitBreaker","pendingRequests","abortAll","entry","client","input","init","effectiveDedupe","effectiveDedupeHashFn","dedupeKey","request","settled","resolveFn","rejectFn","inFlightPromise","resolve","reject","value","reason","effectiveHooks","effectiveThrowOnHttpError","createTimeoutSignal","timeout","controller","timeoutId","effectiveTimeout","userSignal","transformedSignal","timeoutSignal","combinedSignal","signals","retryWithHooks","effectiveRetries","effectiveRetryDelay","effectiveShouldRetry","attempt","shouldRetryWithHook","ctx","retrying","lastResponse","res","retry","AbortError","TimeoutError","reqWithSignal","response","err","NetworkError","HttpError","resp","retryErr","RetryLimitError","actualPromise","CircuitOpenError","result","error","pendingEntry","index","index_default","createClient"]}
|