breaker-box 8.0.0 → 9.0.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 +7 -33
- package/dist/index.cjs +47 -105
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +18 -77
- package/dist/index.d.mts +18 -77
- package/dist/index.mjs +47 -104
- package/dist/index.mjs.map +1 -1
- package/package.json +5 -5
package/README.md
CHANGED
|
@@ -182,7 +182,7 @@ Creates a circuit breaker around the provided async function.
|
|
|
182
182
|
|
|
183
183
|
- `fn`: The async function to protect
|
|
184
184
|
- `options`: Configuration object (optional)
|
|
185
|
-
- `
|
|
185
|
+
- `errorIsTransient`: Function to determine if an error is transient; when true, the error is thrown to the caller but does NOT count toward the circuit breaker's failure rate (default: `() => false`)
|
|
186
186
|
- `errorThreshold`: Percentage (0-1) of errors that triggers circuit opening (default: `0`)
|
|
187
187
|
- `errorWindow`: Time window in ms for tracking errors (default: `10_000`)
|
|
188
188
|
- `fallback`: Function to call when an error occurs or circuit is open (default: undefined)
|
|
@@ -208,41 +208,15 @@ A function with the same signature as `fn` and additional methods:
|
|
|
208
208
|
|
|
209
209
|
### Helper Functions
|
|
210
210
|
|
|
211
|
-
#### `
|
|
211
|
+
#### `CircuitError`
|
|
212
212
|
|
|
213
|
-
|
|
213
|
+
Error class thrown by the circuit breaker. All errors from `createCircuitBreaker` are instances of `CircuitError` with a prefixed message (e.g., `ERR_CIRCUIT_BREAKER_CALL_FAILURE`, `ERR_CIRCUIT_BREAKER_MAX_RETRIES`).
|
|
214
214
|
|
|
215
|
-
|
|
215
|
+
**Properties:**
|
|
216
216
|
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
- `
|
|
220
|
-
- `options`: Configuration object (optional)
|
|
221
|
-
- `maxAttempts`: Maximum number of attempts (default: `3`)
|
|
222
|
-
- `retryDelay`: Function `(attempt: number, signal: AbortSignal) => Promise<void>` for delay before retry (default: immediate)
|
|
223
|
-
- `shouldRetry`: Function `(error: unknown, attempt: number) => boolean` to determine if error should be retried (default: `() => true`)
|
|
224
|
-
|
|
225
|
-
**Example:**
|
|
226
|
-
|
|
227
|
-
```typescript
|
|
228
|
-
const retryCall = withRetry(apiCall, {
|
|
229
|
-
maxAttempts: 5,
|
|
230
|
-
retryDelay: useExponentialBackoff(30),
|
|
231
|
-
shouldRetry: (error) => error.statusCode !== 404,
|
|
232
|
-
})
|
|
233
|
-
```
|
|
234
|
-
|
|
235
|
-
#### `withTimeout(fn, timeoutMs, message?)` *(Deprecated)*
|
|
236
|
-
|
|
237
|
-
> **Deprecated:** Use the `timeout` option on `createCircuitBreaker` instead.
|
|
238
|
-
|
|
239
|
-
Wraps a function with a timeout. Rejects with `Error(message)` if execution exceeds `timeoutMs`.
|
|
240
|
-
|
|
241
|
-
**Parameters:**
|
|
242
|
-
|
|
243
|
-
- `fn`: The async function to wrap with timeout
|
|
244
|
-
- `timeoutMs`: Timeout in milliseconds
|
|
245
|
-
- `message`: Error message to use when timeout occurs (default: `"ERR_CIRCUIT_BREAKER_TIMEOUT"`)
|
|
217
|
+
- `message`: Prefixed error code (e.g., `"ERR_CIRCUIT_BREAKER_OPEN"`)
|
|
218
|
+
- `cause`: The underlying error that triggered the circuit breaker error
|
|
219
|
+
- `isTransient`: `true` if the error was classified as transient via `errorIsTransient`
|
|
246
220
|
|
|
247
221
|
#### `useExponentialBackoff(maxSeconds)`
|
|
248
222
|
|
package/dist/index.cjs
CHANGED
|
@@ -28,16 +28,6 @@ const delayMs = (ms, signal) => {
|
|
|
28
28
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
29
29
|
}) : new Promise((next) => setTimeout(next, ms));
|
|
30
30
|
};
|
|
31
|
-
const deprecated = (fn, method, message) => {
|
|
32
|
-
let warned = false;
|
|
33
|
-
return ((...args) => {
|
|
34
|
-
if (!warned) {
|
|
35
|
-
console.warn(`[breaker-box] ${method} Deprecation: ${message}`);
|
|
36
|
-
warned = true;
|
|
37
|
-
}
|
|
38
|
-
return fn(...args);
|
|
39
|
-
});
|
|
40
|
-
};
|
|
41
31
|
const noop = () => {
|
|
42
32
|
};
|
|
43
33
|
function promiseTry(fn) {
|
|
@@ -47,64 +37,6 @@ function promiseTry(fn) {
|
|
|
47
37
|
return Promise.reject(error);
|
|
48
38
|
}
|
|
49
39
|
}
|
|
50
|
-
async function shouldRetry(options) {
|
|
51
|
-
const { retries, lastError, retryDelay, retryLimit, retryTest, signal } = options;
|
|
52
|
-
if (retries >= retryLimit) throw lastError;
|
|
53
|
-
if (!retryTest(lastError)) throw lastError;
|
|
54
|
-
try {
|
|
55
|
-
if (!retryDelay) return true;
|
|
56
|
-
else if (typeof retryDelay === "number") await delayMs(retryDelay, signal);
|
|
57
|
-
else if (typeof retryDelay === "function") await retryDelay(retries, signal);
|
|
58
|
-
} catch {
|
|
59
|
-
}
|
|
60
|
-
return true;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
|
-
function withRetry$1(main, options = {}) {
|
|
64
|
-
const {
|
|
65
|
-
shouldRetry = () => true,
|
|
66
|
-
maxAttempts = 3,
|
|
67
|
-
retryDelay = () => Promise.resolve()
|
|
68
|
-
} = options;
|
|
69
|
-
assert(maxAttempts >= 1, "maxAttempts must be a number greater than 0");
|
|
70
|
-
const controller = new AbortController();
|
|
71
|
-
const { signal } = controller;
|
|
72
|
-
async function withRetryFunction(...args) {
|
|
73
|
-
let attempt = 1;
|
|
74
|
-
while (true) {
|
|
75
|
-
try {
|
|
76
|
-
return await main(...args);
|
|
77
|
-
} catch (cause) {
|
|
78
|
-
if (attempt >= maxAttempts) {
|
|
79
|
-
throw new Error(`ERR_CIRCUIT_BREAKER_MAX_ATTEMPTS (${maxAttempts})`, {
|
|
80
|
-
cause
|
|
81
|
-
});
|
|
82
|
-
}
|
|
83
|
-
if (!shouldRetry(cause, attempt)) throw cause;
|
|
84
|
-
}
|
|
85
|
-
attempt++;
|
|
86
|
-
await abortable(signal, retryDelay(attempt, signal));
|
|
87
|
-
}
|
|
88
|
-
}
|
|
89
|
-
return Object.assign(withRetryFunction, {
|
|
90
|
-
[Symbol.dispose]: () => controller.abort()
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
function withTimeout$1(main, timeoutMs, timeoutMessage = "ERR_CIRCUIT_BREAKER_TIMEOUT") {
|
|
95
|
-
const error = new Error(timeoutMessage);
|
|
96
|
-
const controller = new AbortController();
|
|
97
|
-
const { signal } = controller;
|
|
98
|
-
function withTimeoutFunction(...args) {
|
|
99
|
-
return new Promise((resolve, reject) => {
|
|
100
|
-
const timer = setTimeout(reject, timeoutMs, error);
|
|
101
|
-
abortable(signal, main(...args)).then(resolve, reject).finally(() => clearTimeout(timer));
|
|
102
|
-
});
|
|
103
|
-
}
|
|
104
|
-
return Object.assign(withTimeoutFunction, {
|
|
105
|
-
[Symbol.dispose]: () => controller.abort()
|
|
106
|
-
});
|
|
107
|
-
}
|
|
108
40
|
|
|
109
41
|
function useExponentialBackoff(maxSeconds) {
|
|
110
42
|
return function exponentialBackoff(attempt, signal) {
|
|
@@ -122,9 +54,18 @@ function useFibonacciBackoff(maxSeconds) {
|
|
|
122
54
|
};
|
|
123
55
|
}
|
|
124
56
|
|
|
57
|
+
class CircuitError extends Error {
|
|
58
|
+
isTransient;
|
|
59
|
+
constructor(message, options) {
|
|
60
|
+
super(`ERR_CIRCUIT_BREAKER_${message}`, options);
|
|
61
|
+
this.isTransient = options?.isTransient ?? false;
|
|
62
|
+
}
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
let warnedErrorIsFailure = false;
|
|
125
66
|
function parseOptions(options) {
|
|
126
67
|
const {
|
|
127
|
-
|
|
68
|
+
errorIsTransient = options.errorIsFailure ?? (() => false),
|
|
128
69
|
errorThreshold = 0,
|
|
129
70
|
errorWindow = 1e4,
|
|
130
71
|
fallback,
|
|
@@ -138,9 +79,15 @@ function parseOptions(options) {
|
|
|
138
79
|
retryTest = () => true,
|
|
139
80
|
timeout = 0
|
|
140
81
|
} = options;
|
|
82
|
+
if ("errorIsFailure" in options && !("errorIsTransient" in options) && !warnedErrorIsFailure) {
|
|
83
|
+
warnedErrorIsFailure = true;
|
|
84
|
+
console.warn(
|
|
85
|
+
'breaker-box: "errorIsFailure" is deprecated. Use "errorIsTransient" instead.'
|
|
86
|
+
);
|
|
87
|
+
}
|
|
141
88
|
assert(
|
|
142
|
-
typeof
|
|
143
|
-
`"
|
|
89
|
+
typeof errorIsTransient === "function",
|
|
90
|
+
`"errorIsTransient" must be a function (received ${typeof errorIsTransient})`
|
|
144
91
|
);
|
|
145
92
|
assert(
|
|
146
93
|
errorThreshold >= 0 && errorThreshold <= 1,
|
|
@@ -195,7 +142,7 @@ function parseOptions(options) {
|
|
|
195
142
|
`"timeout" must be a finite, non-negative number (received ${timeout})`
|
|
196
143
|
);
|
|
197
144
|
return {
|
|
198
|
-
|
|
145
|
+
errorIsTransient,
|
|
199
146
|
errorThreshold,
|
|
200
147
|
errorWindow,
|
|
201
148
|
fallback,
|
|
@@ -211,6 +158,20 @@ function parseOptions(options) {
|
|
|
211
158
|
};
|
|
212
159
|
}
|
|
213
160
|
|
|
161
|
+
async function shouldContinue(options) {
|
|
162
|
+
const { retries, lastError, retryDelay, retryLimit, retryTest, signal } = options;
|
|
163
|
+
if (retries >= retryLimit)
|
|
164
|
+
throw new CircuitError("MAX_RETRIES", { cause: lastError });
|
|
165
|
+
if (!retryTest(lastError))
|
|
166
|
+
throw new CircuitError("NON_RETRYABLE", { cause: lastError });
|
|
167
|
+
try {
|
|
168
|
+
if (!retryDelay) return true;
|
|
169
|
+
else if (typeof retryDelay === "number") await delayMs(retryDelay, signal);
|
|
170
|
+
else if (typeof retryDelay === "function") await retryDelay(retries, signal);
|
|
171
|
+
} catch {
|
|
172
|
+
}
|
|
173
|
+
return true;
|
|
174
|
+
}
|
|
214
175
|
const validTransitions = {
|
|
215
176
|
closed: ["open", "disposed"],
|
|
216
177
|
open: ["halfOpen", "disposed"],
|
|
@@ -223,13 +184,6 @@ function assertTransition(from, to) {
|
|
|
223
184
|
`Invalid transition from ${from} to ${to}`
|
|
224
185
|
);
|
|
225
186
|
}
|
|
226
|
-
class CircuitError extends Error {
|
|
227
|
-
isTransient;
|
|
228
|
-
constructor(message, options) {
|
|
229
|
-
super(`ERR_CIRCUIT_BREAKER_${message}`, options);
|
|
230
|
-
this.isTransient = options?.isTransient ?? false;
|
|
231
|
-
}
|
|
232
|
-
}
|
|
233
187
|
function createState(status, failureCause) {
|
|
234
188
|
const controller = new AbortController();
|
|
235
189
|
return {
|
|
@@ -242,7 +196,7 @@ function createState(status, failureCause) {
|
|
|
242
196
|
}
|
|
243
197
|
function createCircuitBreaker(main, options = {}) {
|
|
244
198
|
const {
|
|
245
|
-
|
|
199
|
+
errorIsTransient,
|
|
246
200
|
errorThreshold,
|
|
247
201
|
errorWindow,
|
|
248
202
|
fallback,
|
|
@@ -268,7 +222,7 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
268
222
|
return result;
|
|
269
223
|
} catch (cause) {
|
|
270
224
|
historyItem.status = "rejected";
|
|
271
|
-
const isTransient =
|
|
225
|
+
const isTransient = errorIsTransient(cause);
|
|
272
226
|
if (isTransient) historyItem = void 0;
|
|
273
227
|
throw new CircuitError("CALL_FAILURE", { cause, isTransient });
|
|
274
228
|
} finally {
|
|
@@ -305,7 +259,7 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
305
259
|
if (state === nextState) transitionToHalfOpen();
|
|
306
260
|
}
|
|
307
261
|
function transitionToHalfOpen() {
|
|
308
|
-
transitionTo("halfOpen");
|
|
262
|
+
transitionTo("halfOpen", state.failureCause);
|
|
309
263
|
if (onHalfOpen) setImmediate(onHalfOpen);
|
|
310
264
|
}
|
|
311
265
|
function transitionToClosed() {
|
|
@@ -324,6 +278,7 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
324
278
|
let retries = 0;
|
|
325
279
|
do {
|
|
326
280
|
const current = state;
|
|
281
|
+
lastError = void 0;
|
|
327
282
|
if (current.status === "closed") {
|
|
328
283
|
try {
|
|
329
284
|
return await tryCall(current, args);
|
|
@@ -339,6 +294,7 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
339
294
|
return await tryCall(current, args);
|
|
340
295
|
} catch (error) {
|
|
341
296
|
if (guardIsCurrent(current, error)) lastError = error;
|
|
297
|
+
break;
|
|
342
298
|
} finally {
|
|
343
299
|
if (state === current && current.history.size >= minimumCandidates) {
|
|
344
300
|
const rate = current.failureRate = calculateFailureRate();
|
|
@@ -348,23 +304,21 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
348
304
|
}
|
|
349
305
|
}
|
|
350
306
|
} else if (current.status === "open" || current.status === "halfOpen") {
|
|
351
|
-
|
|
352
|
-
throw (
|
|
353
|
-
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
354
|
-
current.failureCause ?? new CircuitError("OPEN", { cause: current.failureCause })
|
|
355
|
-
);
|
|
356
|
-
}
|
|
357
|
-
return await fallback(...args);
|
|
307
|
+
break;
|
|
358
308
|
} else throw current.failureCause;
|
|
359
|
-
} while (await
|
|
309
|
+
} while (await shouldContinue({
|
|
360
310
|
retries: ++retries,
|
|
361
|
-
lastError,
|
|
311
|
+
lastError: lastError?.cause ?? lastError,
|
|
362
312
|
retryDelay,
|
|
363
313
|
retryLimit,
|
|
364
314
|
retryTest,
|
|
365
315
|
signal: state.controller.signal
|
|
316
|
+
}).catch((error) => {
|
|
317
|
+
lastError = error;
|
|
318
|
+
return false;
|
|
366
319
|
}));
|
|
367
|
-
|
|
320
|
+
if (!fallback) throw lastError ?? state.failureCause;
|
|
321
|
+
return fallback(...args);
|
|
368
322
|
}
|
|
369
323
|
function dispose(disposeMessage = "ERR_CIRCUIT_BREAKER_DISPOSED") {
|
|
370
324
|
if (state.status === "disposed") return;
|
|
@@ -380,21 +334,9 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
380
334
|
return wrapped;
|
|
381
335
|
}
|
|
382
336
|
|
|
383
|
-
|
|
384
|
-
withRetry$1,
|
|
385
|
-
"withRetry",
|
|
386
|
-
"Use `retryLimit`, `retryDelay`, and `retryTest` options on `createCircuitBreaker` instead."
|
|
387
|
-
);
|
|
388
|
-
const withTimeout = deprecated(
|
|
389
|
-
withTimeout$1,
|
|
390
|
-
"withTimeout",
|
|
391
|
-
"Use `options.timeout` on`createCircuitBreaker` instead."
|
|
392
|
-
);
|
|
393
|
-
|
|
337
|
+
exports.CircuitError = CircuitError;
|
|
394
338
|
exports.createCircuitBreaker = createCircuitBreaker;
|
|
395
339
|
exports.delayMs = delayMs;
|
|
396
340
|
exports.useExponentialBackoff = useExponentialBackoff;
|
|
397
341
|
exports.useFibonacciBackoff = useFibonacciBackoff;
|
|
398
|
-
exports.withRetry = withRetry;
|
|
399
|
-
exports.withTimeout = withTimeout;
|
|
400
342
|
//# sourceMappingURL=index.cjs.map
|
package/dist/index.cjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.cjs","sources":["../lib/util.ts","../lib/retry.ts","../lib/timeout.ts","../lib/backoff.ts","../lib/options.ts","../lib/circuit-breaker.ts","../lib/index.ts"],"sourcesContent":["import type { AnyFn, RetryDelayFn } from \"./types.js\"\n\n/**\n * Returns a promise which rejects when the abort signal is triggered or\n * resolves when the promise is fulfilled.\n */\nexport const abortable = <T>(\n\tsignal: AbortSignal,\n\tpending: PromiseLike<T>,\n): Promise<T> =>\n\tnew Promise((resolve, reject) => {\n\t\tsignal.throwIfAborted()\n\n\t\tconst onAbort = () => reject(signal.reason)\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\tPromise.resolve(pending)\n\t\t\t.finally(() => signal.removeEventListener(\"abort\", onAbort))\n\t\t\t.then(resolve, reject)\n\t})\n\n/**\n * Asserts that the given value is truthy. If not, throws a `TypeError`.\n */\nexport function assert(value: unknown, message?: string): asserts value {\n\tif (!value) throw new TypeError(message)\n}\n\n/**\n * Returns a promise that resolves after the specified number of milliseconds.\n */\nexport const delayMs = (ms: number, signal?: AbortSignal): Promise<void> => {\n\tif (!Number.isFinite(ms) || ms < 0) {\n\t\tthrow new RangeError(\n\t\t\t`\"ms\" must be a finite, non-negative number (received ${ms})`,\n\t\t)\n\t}\n\n\treturn signal\n\t\t? new Promise((resolve, reject) => {\n\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort)\n\t\t\t\t\tresolve()\n\t\t\t\t}, ms)\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\treject(signal.reason)\n\t\t\t\t}\n\n\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true })\n\t\t\t})\n\t\t: new Promise((next) => setTimeout(next, ms))\n}\n\nexport const deprecated = <T extends AnyFn>(\n\tfn: T,\n\tmethod: string,\n\tmessage: string,\n): T => {\n\tlet warned = false\n\treturn ((...args) => {\n\t\tif (!warned) {\n\t\t\tconsole.warn(`[breaker-box] ${method} Deprecation: ${message}`)\n\t\t\twarned = true\n\t\t}\n\t\treturn fn(...args)\n\t}) as T\n}\n\nexport const identity = <T>(value: T): T => value\n\nexport const noop: (...args: unknown[]) => void = () => {}\n\n/**\n * Polyfill for `Promise.try()`\n */\nexport function promiseTry<T>(fn: () => T): Promise<T> {\n\ttry {\n\t\treturn Promise.resolve(fn())\n\t} catch (error) {\n\t\treturn Promise.reject(error)\n\t}\n}\n\nexport async function shouldRetry(options: {\n\tretries: number\n\tlastError: unknown\n\tretryDelay: number | RetryDelayFn\n\tretryLimit: number\n\tretryTest: (error: unknown) => boolean\n\tsignal: AbortSignal\n}): Promise<true> {\n\tconst { retries, lastError, retryDelay, retryLimit, retryTest, signal } =\n\t\toptions\n\n\tif (retries >= retryLimit) throw lastError\n\tif (!retryTest(lastError)) throw lastError\n\n\ttry {\n\t\tif (!retryDelay) return true\n\t\telse if (typeof retryDelay === \"number\") await delayMs(retryDelay, signal)\n\t\telse if (typeof retryDelay === \"function\") await retryDelay(retries, signal)\n\t} catch {\n\t\t/* empty */\n\t}\n\n\treturn true\n}\n","import { type MainFn, type RetryOptions } from \"./types.js\"\nimport { assert, abortable } from \"./util.js\"\n\n/**\n * Wrap a function with retry logic. Errors will be retried according to the\n * provided options.\n *\n * @example\n * ```ts\n * // Compose with circuit breaker. Retry up to 3 times with no delay\n * const protectedA = createCircuitBreaker(\n * withRetry(unreliableApiCall, { maxAttempts: 3 })\n * )\n *\n * // Retry up to 5 times with exponential backoff\n * const protectedB = createCircuitBreaker(\n * withRetry(unreliableApiCall, {\n * maxAttempts: 5,\n * retryDelay: useExponentialBackoff(30),\n * })\n * )\n * ```\n */\nexport function withRetry<Ret, Args extends readonly unknown[]>(\n\tmain: MainFn<Ret, Args>,\n\toptions: Readonly<RetryOptions> = {},\n): MainFn<Ret, Args> & Disposable {\n\tconst {\n\t\tshouldRetry = () => true,\n\t\tmaxAttempts = 3,\n\t\tretryDelay = () => Promise.resolve(),\n\t} = options\n\n\tassert(maxAttempts >= 1, \"maxAttempts must be a number greater than 0\")\n\n\tconst controller = new AbortController()\n\tconst { signal } = controller\n\n\tasync function withRetryFunction(...args: Args): Promise<Ret> {\n\t\tlet attempt = 1\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\treturn await main(...args)\n\t\t\t} catch (cause) {\n\t\t\t\tif (attempt >= maxAttempts) {\n\t\t\t\t\tthrow new Error(`ERR_CIRCUIT_BREAKER_MAX_ATTEMPTS (${maxAttempts})`, {\n\t\t\t\t\t\tcause,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tif (!shouldRetry(cause, attempt)) throw cause\n\t\t\t}\n\n\t\t\tattempt++\n\t\t\tawait abortable(signal, retryDelay(attempt, signal))\n\t\t}\n\t}\n\n\treturn Object.assign(withRetryFunction, {\n\t\t[Symbol.dispose]: () => controller.abort(),\n\t})\n}\n","import { type MainFn } from \"./types.js\"\nimport { abortable } from \"./util.js\"\n\n/**\n * Wraps an async function with a timeout constraint. Rejects with an Error if\n * execution exceeds the specified timeout.\n *\n * @example\n * ```ts\n * const fetchWithTimeout = withTimeout(fetchData, 5000, \"Fetch timed out\")\n * try {\n * const data = await fetchWithTimeout(url)\n * } catch (error) {\n * console.error(error.message) // \"Fetch timed out\" after 5 seconds\n * }\n * ```\n */\nexport function withTimeout<Ret, Args extends readonly unknown[]>(\n\tmain: MainFn<Ret, Args>,\n\ttimeoutMs: number,\n\ttimeoutMessage = \"ERR_CIRCUIT_BREAKER_TIMEOUT\",\n): MainFn<Ret, Args> & Disposable {\n\tconst error = new Error(timeoutMessage)\n\tconst controller = new AbortController()\n\tconst { signal } = controller\n\n\tfunction withTimeoutFunction(...args: Args): Promise<Ret> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timer = setTimeout(reject, timeoutMs, error)\n\n\t\t\tabortable(signal, main(...args))\n\t\t\t\t.then(resolve, reject)\n\t\t\t\t.finally(() => clearTimeout(timer))\n\t\t})\n\t}\n\n\treturn Object.assign(withTimeoutFunction, {\n\t\t[Symbol.dispose]: () => controller.abort(),\n\t})\n}\n","import type { RetryDelayFn } from \"./types.js\"\nimport { delayMs } from \"./util.js\"\n\n/**\n * Creates an exponential backoff strategy for retry delays.\n * Delay grows as 2^(attempt-2) seconds, capped at maxSeconds.\n *\n * The sequence is: 1s, 2s, 4s, 8s, 16s, 32s, etc.\n *\n * @example\n * ```ts\n * const backoff = useExponentialBackoff(30)\n * await backoff(2) // waits 1 second\n * await backoff(3) // waits 2 seconds\n * await backoff(10) // waits 30 seconds (capped)\n * ```\n */\nexport function useExponentialBackoff(maxSeconds: number): RetryDelayFn {\n\treturn function exponentialBackoff(attempt, signal) {\n\t\tconst num = Math.max(attempt - 2, 0)\n\t\tconst delay = Math.min(2 ** num, maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n\nconst sqrt5 = /* @__PURE__ */ Math.sqrt(5)\n/**\n * Binet's formula for calculating Fibonacci numbers in constant time.\n * @see https://en.wikipedia.org/wiki/Fibonacci_sequence#Closed-form_expression\n */\nconst binet = (n: number) =>\n\tMath.round(((1 + sqrt5) ** n - (1 - sqrt5) ** n) / (2 ** n * sqrt5))\n\n/**\n * Creates a Fibonacci backoff strategy for retry delays. It is more gradual\n * than exponential backoff, useful for more aggressive retry patterns.\n *\n * The sequence is: 1s, 2s, 3s, 5s, 8s, 13s, etc.\n *\n * @example\n * ```ts\n * const backoff = useFibonacciBackoff(60)\n * await backoff(2) // waits 1 second\n * await backoff(5) // waits 5 seconds\n * await backoff(10) // waits 55 seconds\n * ```\n */\nexport function useFibonacciBackoff(maxSeconds: number): RetryDelayFn {\n\treturn function fibonacciBackoff(attempt, signal) {\n\t\tconst delay = Math.min(binet(attempt), maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n","import type { AnyFn, CircuitBreakerOptions } from \"./types.js\"\nimport { assert } from \"./util.js\"\n\nexport function parseOptions<Fallback extends AnyFn>(\n\toptions: CircuitBreakerOptions<Fallback>,\n) {\n\tconst {\n\t\terrorIsFailure = () => false,\n\t\terrorThreshold = 0,\n\t\terrorWindow = 10_000,\n\t\tfallback,\n\t\tminimumCandidates = 1,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter = 30_000,\n\t\tretryDelay = 0,\n\t\tretryLimit = Infinity,\n\t\tretryTest = () => true,\n\t\ttimeout = 0,\n\t} = options\n\n\t// errorIsFailure\n\tassert(\n\t\ttypeof errorIsFailure === \"function\",\n\t\t`\"errorIsFailure\" must be a function (received ${typeof errorIsFailure})`,\n\t)\n\n\t// errorThreshold\n\tassert(\n\t\terrorThreshold >= 0 && errorThreshold <= 1,\n\t\t`\"errorThreshold\" must be between 0 and 1 (received ${errorThreshold})`,\n\t)\n\n\t// errorWindow\n\tassert(\n\t\terrorWindow >= 1_000,\n\t\t`\"errorWindow\" must be milliseconds of at least 1 second (received ${errorWindow})`,\n\t)\n\n\t// (optional) fallback\n\tassert(\n\t\t!fallback || typeof fallback === \"function\",\n\t\t`\"fallback\" must be a function (received ${typeof fallback})`,\n\t)\n\n\t// minimumCandidates\n\tassert(\n\t\tminimumCandidates >= 1,\n\t\t`\"minimumCandidates\" must be greater than 0 (received ${minimumCandidates})`,\n\t)\n\n\t// (optional) onClose\n\tassert(\n\t\t!onClose || typeof onClose === \"function\",\n\t\t`\"onClose\" must be a function (received ${typeof onClose})`,\n\t)\n\n\t// (optional) onHalfOpen\n\tassert(\n\t\t!onHalfOpen || typeof onHalfOpen === \"function\",\n\t\t`\"onHalfOpen\" must be a function (received ${typeof onHalfOpen})`,\n\t)\n\n\t// (optional) onOpen\n\tassert(\n\t\t!onOpen || typeof onOpen === \"function\",\n\t\t`\"onOpen\" must be a function (received ${typeof onOpen})`,\n\t)\n\n\t// resetAfter\n\tassert(\n\t\tresetAfter >= 1_000,\n\t\t`\"resetAfter\" must be milliseconds of at least 1 second (received ${resetAfter})`,\n\t)\n\tassert(\n\t\tresetAfter >= errorWindow,\n\t\t`\"resetAfter\" must be greater than or equal to \"errorWindow\" (received ${resetAfter}, expected >= ${errorWindow})`,\n\t)\n\n\t// retryDelay\n\tassert(\n\t\ttypeof retryDelay === \"function\" ||\n\t\t\t(typeof retryDelay === \"number\" &&\n\t\t\t\tretryDelay >= 0 &&\n\t\t\t\tNumber.isFinite(retryDelay)),\n\t\t`\"retryDelay\" must be a function or a finite, non-negative number (received ${typeof retryDelay})`,\n\t)\n\n\t// retryLimit\n\tassert(\n\t\ttypeof retryLimit === \"number\" && retryLimit >= 1,\n\t\t`\"retryLimit\" must be greater than 0 (received ${retryLimit})`,\n\t)\n\n\t// retryTest\n\tassert(\n\t\ttypeof retryTest === \"function\",\n\t\t`\"retryTest\" must be a function (received ${typeof retryTest})`,\n\t)\n\n\t// timeout\n\tassert(\n\t\tNumber.isFinite(timeout) && timeout >= 0,\n\t\t`\"timeout\" must be a finite, non-negative number (received ${timeout})`,\n\t)\n\n\treturn {\n\t\terrorIsFailure,\n\t\terrorThreshold,\n\t\terrorWindow,\n\t\tfallback,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t\tretryDelay,\n\t\tretryLimit,\n\t\tretryTest,\n\t\ttimeout,\n\t}\n}\n","import { parseOptions } from \"./options.js\"\nimport type {\n\tCircuitBreakerOptions,\n\tCircuitBreakerProtectedFn,\n\tHistoryEntry,\n\tHistoryMap,\n\tMainFn,\n\tStateName,\n} from \"./types.js\"\nimport {\n\tabortable,\n\tassert,\n\tdelayMs,\n\tnoop,\n\tpromiseTry,\n\tshouldRetry,\n} from \"./util.js\"\n\nconst validTransitions: Record<StateName, StateName[]> = {\n\tclosed: [\"open\", \"disposed\"],\n\topen: [\"halfOpen\", \"disposed\"],\n\thalfOpen: [\"closed\", \"open\", \"disposed\"],\n\tdisposed: [],\n}\n\nfunction assertTransition(from: StateName, to: StateName): void {\n\tassert(\n\t\tvalidTransitions[from].includes(to),\n\t\t`Invalid transition from ${from} to ${to}`,\n\t)\n}\n\ninterface CircuitInternalState<T extends StateName = StateName> {\n\tcontroller: AbortController\n\tfailureCause: unknown\n\tfailureRate: number\n\thistory: HistoryMap\n\tstatus: T\n}\n\nclass CircuitError extends Error {\n\tisTransient: boolean\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: { cause?: unknown; isTransient?: boolean },\n\t) {\n\t\tsuper(`ERR_CIRCUIT_BREAKER_${message}`, options)\n\t\tthis.isTransient = options?.isTransient ?? false\n\t}\n}\n\nfunction createState(\n\tstatus: StateName,\n\tfailureCause?: unknown,\n): CircuitInternalState {\n\tconst controller = new AbortController()\n\treturn {\n\t\tcontroller,\n\t\tfailureCause,\n\t\tfailureRate: 0,\n\t\thistory: new Map(),\n\t\tstatus,\n\t}\n}\n\n/**\n * Creates a circuit breaker that wraps an async function with failure tracking\n * and automatic fallback behavior.\n *\n * The circuit breaker operates in four states:\n *\n * - `closed`: Normal operation, tracks failures in a sliding window\n * - `open`: Failed state, fallback is used until `resetAfter` milliseconds\n * - `halfOpen`: Testing recovery, allows trial calls\n * - `disposed`: Terminal state, all calls rejected\n *\n * When the failure rate exceeds `errorThreshold` within the `errorWindow`, the\n * circuit opens and rejects calls (using fallback if provided) for `resetAfter`\n * milliseconds. After this period, it transitions to half-open and allows up\n * to `minimumCandidates` concurrent trial calls. If their failure rate stays\n * at or below the threshold, the circuit closes; otherwise it reopens.\n *\n * @example\n * ```ts\n * const protectedFn = createCircuitBreaker(unreliableApiCall, {\n * errorThreshold: 0.5,\n * errorWindow: 10_000,\n * resetAfter: 30_000,\n * fallback: () => cachedResponse,\n * })\n *\n * try {\n * const result = await protectedFn(arg1, arg2)\n * } catch (error) {\n * console.error('Circuit breaker rejected call:', error)\n * }\n *\n * console.log(protectedFn.getState()) // 'closed' | 'open' | 'halfOpen' | 'disposed'\n * protectedFn[Symbol.dispose]() // Clean up timers and resources\n * ```\n */\nexport function createCircuitBreaker<Ret, Args extends unknown[]>(\n\tmain: MainFn<Ret, Args>,\n\toptions: CircuitBreakerOptions<MainFn<Ret, Args>> = {},\n): CircuitBreakerProtectedFn<Ret, Args> {\n\tconst {\n\t\terrorIsFailure,\n\t\terrorThreshold,\n\t\terrorWindow,\n\t\tfallback,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t\tretryDelay,\n\t\tretryLimit,\n\t\tretryTest,\n\t\ttimeout,\n\t} = parseOptions(options)\n\n\tlet state = createState(\"closed\")\n\n\tasync function tryCall(\n\t\tcurrent: CircuitInternalState,\n\t\targs: Args,\n\t): Promise<Ret> {\n\t\tconst { history } = current\n\t\tconst request = promiseTry(() => main(...args))\n\n\t\tlet historyItem: HistoryEntry | undefined = { status: \"pending\" }\n\t\thistory.set(request, historyItem)\n\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\ttimeout > 0\n\t\t\t\t\t? await abortable(AbortSignal.timeout(timeout), request)\n\t\t\t\t\t: await request\n\t\t\thistoryItem.status = \"resolved\"\n\t\t\treturn result\n\t\t} catch (cause) {\n\t\t\thistoryItem.status = \"rejected\"\n\t\t\t// Drop this request if it's a transient error that shouldn't count\n\t\t\t// towards the failure rate\n\t\t\tconst isTransient = errorIsFailure(cause)\n\t\t\tif (isTransient) historyItem = undefined\n\n\t\t\t// Wrap the error in a CircuitError to provide additional context and\n\t\t\t// control flow handling.\n\t\t\tthrow new CircuitError(\"CALL_FAILURE\", { cause, isTransient })\n\t\t} finally {\n\t\t\t// Remove the request if it was a transient failure, or if it's stale.\n\t\t\tif (!historyItem || state !== current) history.delete(request)\n\t\t\t// Keep the request in history until the end of the error window, or until\n\t\t\t// the circuit transitions.\n\t\t\telse {\n\t\t\t\tconst { signal } = current.controller\n\t\t\t\tdelayMs(errorWindow, signal)\n\t\t\t\t\t.catch(() => {})\n\t\t\t\t\t.finally(() => history.delete(request))\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction calculateFailureRate(): number {\n\t\tlet failures = 0\n\t\tlet total = 0\n\t\tfor (const { status } of state.history.values()) {\n\t\t\tif (status === \"rejected\") failures++\n\t\t\tif (status !== \"pending\") total++\n\t\t}\n\t\tif (!total || total < minimumCandidates) return 0\n\t\treturn failures / total\n\t}\n\n\tfunction transitionTo(\n\t\ttoStatus: StateName,\n\t\tfailureCause?: unknown,\n\t): CircuitInternalState {\n\t\tassertTransition(state.status, toStatus)\n\t\tstate.controller.abort()\n\t\treturn (state = createState(toStatus, failureCause))\n\t}\n\n\tasync function transitionToOpen(error: CircuitError): Promise<void> {\n\t\t// Race guard: a concurrent failure may have already changed state.\n\t\tif (state.status !== \"closed\" && state.status !== \"halfOpen\") return\n\n\t\tconst cause = error.cause ?? error\n\t\tconst nextState = transitionTo(\"open\", cause)\n\t\tif (onOpen) setImmediate(onOpen, cause)\n\n\t\tconst { signal } = nextState.controller\n\t\tawait delayMs(resetAfter, signal)\n\t\tif (state === nextState) transitionToHalfOpen()\n\t}\n\n\tfunction transitionToHalfOpen(): void {\n\t\ttransitionTo(\"halfOpen\")\n\t\tif (onHalfOpen) setImmediate(onHalfOpen)\n\t}\n\n\tfunction transitionToClosed(): void {\n\t\ttransitionTo(\"closed\")\n\t\tif (onClose) setImmediate(onClose)\n\t}\n\n\tfunction guardIsCurrent(\n\t\tcurrent: CircuitInternalState,\n\t\terror: unknown,\n\t): error is CircuitError {\n\t\tif (!(error instanceof CircuitError)) throw error\n\t\t// Transient errors shouldn't affect the circuit breaker's state. Re-throw\n\t\t// the original cause of the error.\n\t\tif (error.isTransient) throw error.cause\n\n\t\t// If the circuit breaker was disposed mid-flight, surface the underlying\n\t\t// cause of the in-flight call rather than the dispose error.\n\t\tif (state.status === \"disposed\")\n\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\tthrow error.cause ?? new CircuitError(\"DISPOSED\")\n\n\t\t// If the circuit breaker transitioned states, try again.\n\t\treturn state === current\n\t}\n\n\tasync function protectedFn(...args: Args): Promise<Ret> {\n\t\tlet lastError: CircuitError | undefined\n\t\tlet retries = 0\n\t\tdo {\n\t\t\tconst current = state\n\n\t\t\t// Closed: Normal Operation\n\t\t\tif (current.status === \"closed\") {\n\t\t\t\ttry {\n\t\t\t\t\treturn await tryCall(current, args)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (guardIsCurrent(current, error)) {\n\t\t\t\t\t\tlastError = error\n\t\t\t\t\t\t// Determine if the failure rate should open the circuit.\n\t\t\t\t\t\tconst rate = (current.failureRate = calculateFailureRate())\n\t\t\t\t\t\tif (rate > errorThreshold) transitionToOpen(error).catch(noop)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Half-Open: Execute trial calls until we have enough candidates.\n\t\t\telse if (\n\t\t\t\tcurrent.status === \"halfOpen\" &&\n\t\t\t\tcurrent.history.size < minimumCandidates\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\treturn await tryCall(current, args)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (guardIsCurrent(current, error)) lastError = error\n\t\t\t\t} finally {\n\t\t\t\t\t// Do nothing until we have enough candidates to make a decision.\n\t\t\t\t\tif (state === current && current.history.size >= minimumCandidates) {\n\t\t\t\t\t\tconst rate = (current.failureRate = calculateFailureRate())\n\t\t\t\t\t\t// Determine if the failure rate should re-open the circuit or\n\t\t\t\t\t\t// if it is healthy enough to close it again.\n\t\t\t\t\t\tif (rate > errorThreshold && lastError)\n\t\t\t\t\t\t\ttransitionToOpen(lastError).catch(noop)\n\t\t\t\t\t\telse if (rate <= errorThreshold) transitionToClosed()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Open: Skip calls and immediately return fallback if available.\n\t\t\telse if (current.status === \"open\" || current.status === \"halfOpen\") {\n\t\t\t\tif (!fallback) {\n\t\t\t\t\tthrow (\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\t\t\t\tcurrent.failureCause ??\n\t\t\t\t\t\tnew CircuitError(\"OPEN\", { cause: current.failureCause })\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn await fallback(...args)\n\t\t\t}\n\n\t\t\t// Disposed: Reject all calls with dispose error.\n\t\t\telse throw current.failureCause\n\t\t} while (\n\t\t\tawait shouldRetry({\n\t\t\t\tretries: ++retries,\n\t\t\t\tlastError,\n\t\t\t\tretryDelay,\n\t\t\t\tretryLimit,\n\t\t\t\tretryTest,\n\t\t\t\tsignal: state.controller.signal,\n\t\t\t})\n\t\t)\n\t\tthrow new Error(\"unknown error in circuit breaker retry logic\")\n\t}\n\n\tfunction dispose(disposeMessage = \"ERR_CIRCUIT_BREAKER_DISPOSED\"): void {\n\t\tif (state.status === \"disposed\") return\n\t\ttransitionTo(\"disposed\", new ReferenceError(disposeMessage))\n\t\tmain[Symbol.dispose]?.()\n\t}\n\n\tconst wrapped = protectedFn as CircuitBreakerProtectedFn<Ret, Args>\n\twrapped[Symbol.dispose] = () => dispose()\n\twrapped.dispose = dispose\n\twrapped.getFailureRate = () => state.failureRate\n\twrapped.getLatestError = () => state.failureCause\n\twrapped.getState = () => state.status\n\n\treturn wrapped\n}\n","import { withRetry as internalWithRetry } from \"./retry.js\"\nimport { withTimeout as internalWithTimeout } from \"./timeout.js\"\nimport type { StateName } from \"./types.js\"\n\nexport { useExponentialBackoff, useFibonacciBackoff } from \"./backoff.js\"\nexport { createCircuitBreaker } from \"./circuit-breaker.js\"\n\nexport type {\n\tCircuitBreakerOptions,\n\tCircuitBreakerProtectedFn,\n\tMainFn,\n\tRetryOptions,\n\tStateName,\n} from \"./types.js\"\nexport { delayMs } from \"./util.js\"\n\n// =============================================================================\n// Deprecated API properties\nimport { deprecated } from \"./util.js\"\n\n/** @deprecated Use `StateName` instead. */\nexport type CircuitState = StateName\n\n/** @deprecated Use `retryLimit`, `retryDelay`, and `retryTest` options on `createCircuitBreaker` instead. */\nexport const withRetry = deprecated(\n\tinternalWithRetry,\n\t\"withRetry\",\n\t\"Use `retryLimit`, `retryDelay`, and `retryTest` options on `createCircuitBreaker` instead.\",\n)\n\n/** @deprecated Use `options.timeout` on `createCircuitBreaker` instead. */\nexport const withTimeout = deprecated(\n\tinternalWithTimeout,\n\t\"withTimeout\",\n\t\"Use `options.timeout` on`createCircuitBreaker` instead.\",\n)\n"],"names":["withRetry","withTimeout","internalWithRetry","internalWithTimeout"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.cjs","sources":["../lib/util.ts","../lib/backoff.ts","../lib/circuit-error.ts","../lib/options.ts","../lib/circuit-breaker.ts"],"sourcesContent":["\n/**\n * Returns a promise which rejects when the abort signal is triggered or\n * resolves when the promise is fulfilled.\n */\nexport const abortable = <T>(\n\tsignal: AbortSignal,\n\tpending: PromiseLike<T>,\n): Promise<T> =>\n\tnew Promise((resolve, reject) => {\n\t\tsignal.throwIfAborted()\n\n\t\tconst onAbort = () => reject(signal.reason)\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\tPromise.resolve(pending)\n\t\t\t.finally(() => signal.removeEventListener(\"abort\", onAbort))\n\t\t\t.then(resolve, reject)\n\t})\n\n/**\n * Asserts that the given value is truthy. If not, throws a `TypeError`.\n */\nexport function assert(value: unknown, message?: string): asserts value {\n\tif (!value) throw new TypeError(message)\n}\n\n/**\n * Returns a promise that resolves after the specified number of milliseconds.\n */\nexport const delayMs = (ms: number, signal?: AbortSignal): Promise<void> => {\n\tif (!Number.isFinite(ms) || ms < 0) {\n\t\tthrow new RangeError(\n\t\t\t`\"ms\" must be a finite, non-negative number (received ${ms})`,\n\t\t)\n\t}\n\n\treturn signal\n\t\t? new Promise((resolve, reject) => {\n\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort)\n\t\t\t\t\tresolve()\n\t\t\t\t}, ms)\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\treject(signal.reason)\n\t\t\t\t}\n\n\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true })\n\t\t\t})\n\t\t: new Promise((next) => setTimeout(next, ms))\n}\n\nexport const noop: (...args: unknown[]) => void = () => {}\n\n/**\n * Polyfill for `Promise.try()`\n */\nexport function promiseTry<T>(fn: () => T): Promise<T> {\n\ttry {\n\t\treturn Promise.resolve(fn())\n\t} catch (error) {\n\t\treturn Promise.reject(error)\n\t}\n}\n\n\n","import type { RetryDelayFn } from \"./types.js\"\nimport { delayMs } from \"./util.js\"\n\n/**\n * Creates an exponential backoff strategy for retry delays.\n * Delay grows as 2^(attempt-2) seconds, capped at maxSeconds.\n *\n * The sequence is: 1s, 2s, 4s, 8s, 16s, 32s, etc.\n *\n * @example\n * ```ts\n * const backoff = useExponentialBackoff(30)\n * await backoff(2) // waits 1 second\n * await backoff(3) // waits 2 seconds\n * await backoff(10) // waits 30 seconds (capped)\n * ```\n */\nexport function useExponentialBackoff(maxSeconds: number): RetryDelayFn {\n\treturn function exponentialBackoff(attempt, signal) {\n\t\tconst num = Math.max(attempt - 2, 0)\n\t\tconst delay = Math.min(2 ** num, maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n\nconst sqrt5 = /* @__PURE__ */ Math.sqrt(5)\n/**\n * Binet's formula for calculating Fibonacci numbers in constant time.\n * @see https://en.wikipedia.org/wiki/Fibonacci_sequence#Closed-form_expression\n */\nconst binet = (n: number) =>\n\tMath.round(((1 + sqrt5) ** n - (1 - sqrt5) ** n) / (2 ** n * sqrt5))\n\n/**\n * Creates a Fibonacci backoff strategy for retry delays. It is more gradual\n * than exponential backoff, useful for more aggressive retry patterns.\n *\n * The sequence is: 1s, 2s, 3s, 5s, 8s, 13s, etc.\n *\n * @example\n * ```ts\n * const backoff = useFibonacciBackoff(60)\n * await backoff(2) // waits 1 second\n * await backoff(5) // waits 5 seconds\n * await backoff(10) // waits 55 seconds\n * ```\n */\nexport function useFibonacciBackoff(maxSeconds: number): RetryDelayFn {\n\treturn function fibonacciBackoff(attempt, signal) {\n\t\tconst delay = Math.min(binet(attempt), maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n","export class CircuitError extends Error {\n\tisTransient: boolean\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: { cause?: unknown; isTransient?: boolean },\n\t) {\n\t\tsuper(`ERR_CIRCUIT_BREAKER_${message}`, options)\n\t\tthis.isTransient = options?.isTransient ?? false\n\t}\n}\n","import type { AnyFn, CircuitBreakerOptions } from \"./types.js\"\nimport { assert } from \"./util.js\"\n\nlet warnedErrorIsFailure = false\n\nexport function parseOptions<Fallback extends AnyFn>(\n\toptions: CircuitBreakerOptions<Fallback>,\n) {\n\tconst {\n\t\terrorIsTransient = options.errorIsFailure ?? (() => false),\n\t\terrorThreshold = 0,\n\t\terrorWindow = 10_000,\n\t\tfallback,\n\t\tminimumCandidates = 1,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter = 30_000,\n\t\tretryDelay = 0,\n\t\tretryLimit = Infinity,\n\t\tretryTest = () => true,\n\t\ttimeout = 0,\n\t} = options\n\n\tif (\n\t\t\"errorIsFailure\" in options &&\n\t\t!(\"errorIsTransient\" in options) &&\n\t\t!warnedErrorIsFailure\n\t) {\n\t\twarnedErrorIsFailure = true\n\t\tconsole.warn(\n\t\t\t'breaker-box: \"errorIsFailure\" is deprecated. Use \"errorIsTransient\" instead.',\n\t\t)\n\t}\n\n\t// errorIsTransient\n\tassert(\n\t\ttypeof errorIsTransient === \"function\",\n\t\t`\"errorIsTransient\" must be a function (received ${typeof errorIsTransient})`,\n\t)\n\n\t// errorThreshold\n\tassert(\n\t\terrorThreshold >= 0 && errorThreshold <= 1,\n\t\t`\"errorThreshold\" must be between 0 and 1 (received ${errorThreshold})`,\n\t)\n\n\t// errorWindow\n\tassert(\n\t\terrorWindow >= 1_000,\n\t\t`\"errorWindow\" must be milliseconds of at least 1 second (received ${errorWindow})`,\n\t)\n\n\t// (optional) fallback\n\tassert(\n\t\t!fallback || typeof fallback === \"function\",\n\t\t`\"fallback\" must be a function (received ${typeof fallback})`,\n\t)\n\n\t// minimumCandidates\n\tassert(\n\t\tminimumCandidates >= 1,\n\t\t`\"minimumCandidates\" must be greater than 0 (received ${minimumCandidates})`,\n\t)\n\n\t// (optional) onClose\n\tassert(\n\t\t!onClose || typeof onClose === \"function\",\n\t\t`\"onClose\" must be a function (received ${typeof onClose})`,\n\t)\n\n\t// (optional) onHalfOpen\n\tassert(\n\t\t!onHalfOpen || typeof onHalfOpen === \"function\",\n\t\t`\"onHalfOpen\" must be a function (received ${typeof onHalfOpen})`,\n\t)\n\n\t// (optional) onOpen\n\tassert(\n\t\t!onOpen || typeof onOpen === \"function\",\n\t\t`\"onOpen\" must be a function (received ${typeof onOpen})`,\n\t)\n\n\t// resetAfter\n\tassert(\n\t\tresetAfter >= 1_000,\n\t\t`\"resetAfter\" must be milliseconds of at least 1 second (received ${resetAfter})`,\n\t)\n\tassert(\n\t\tresetAfter >= errorWindow,\n\t\t`\"resetAfter\" must be greater than or equal to \"errorWindow\" (received ${resetAfter}, expected >= ${errorWindow})`,\n\t)\n\n\t// retryDelay\n\tassert(\n\t\ttypeof retryDelay === \"function\" ||\n\t\t\t(typeof retryDelay === \"number\" &&\n\t\t\t\tretryDelay >= 0 &&\n\t\t\t\tNumber.isFinite(retryDelay)),\n\t\t`\"retryDelay\" must be a function or a finite, non-negative number (received ${typeof retryDelay})`,\n\t)\n\n\t// retryLimit\n\tassert(\n\t\ttypeof retryLimit === \"number\" && retryLimit >= 1,\n\t\t`\"retryLimit\" must be greater than 0 (received ${retryLimit})`,\n\t)\n\n\t// retryTest\n\tassert(\n\t\ttypeof retryTest === \"function\",\n\t\t`\"retryTest\" must be a function (received ${typeof retryTest})`,\n\t)\n\n\t// timeout\n\tassert(\n\t\tNumber.isFinite(timeout) && timeout >= 0,\n\t\t`\"timeout\" must be a finite, non-negative number (received ${timeout})`,\n\t)\n\n\treturn {\n\t\terrorIsTransient,\n\t\terrorThreshold,\n\t\terrorWindow,\n\t\tfallback,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t\tretryDelay,\n\t\tretryLimit,\n\t\tretryTest,\n\t\ttimeout,\n\t}\n}\n","import { parseOptions } from \"./options.js\"\nimport type {\n\tCircuitBreakerOptions,\n\tCircuitBreakerProtectedFn,\n\tHistoryEntry,\n\tHistoryMap,\n\tMainFn,\n\tRetryDelayFn,\n\tStateName,\n} from \"./types.js\"\nimport { CircuitError } from \"./circuit-error.js\"\nimport {\n\tabortable,\n\tassert,\n\tdelayMs,\n\tnoop,\n\tpromiseTry,\n} from \"./util.js\"\n\nasync function shouldContinue(options: {\n\tretries: number\n\tlastError: unknown\n\tretryDelay: number | RetryDelayFn\n\tretryLimit: number\n\tretryTest: (error: unknown) => boolean\n\tsignal: AbortSignal\n}): Promise<true> {\n\tconst { retries, lastError, retryDelay, retryLimit, retryTest, signal } =\n\t\toptions\n\n\tif (retries >= retryLimit)\n\t\tthrow new CircuitError(\"MAX_RETRIES\", { cause: lastError })\n\tif (!retryTest(lastError))\n\t\tthrow new CircuitError(\"NON_RETRYABLE\", { cause: lastError })\n\n\ttry {\n\t\tif (!retryDelay) return true\n\t\telse if (typeof retryDelay === \"number\") await delayMs(retryDelay, signal)\n\t\telse if (typeof retryDelay === \"function\") await retryDelay(retries, signal)\n\t} catch {\n\t\t/* empty */\n\t}\n\n\treturn true\n}\n\nconst validTransitions: Record<StateName, StateName[]> = {\n\tclosed: [\"open\", \"disposed\"],\n\topen: [\"halfOpen\", \"disposed\"],\n\thalfOpen: [\"closed\", \"open\", \"disposed\"],\n\tdisposed: [],\n}\n\nfunction assertTransition(from: StateName, to: StateName): void {\n\tassert(\n\t\tvalidTransitions[from].includes(to),\n\t\t`Invalid transition from ${from} to ${to}`,\n\t)\n}\n\ninterface CircuitInternalState<T extends StateName = StateName> {\n\tcontroller: AbortController\n\tfailureCause: unknown\n\tfailureRate: number\n\thistory: HistoryMap\n\tstatus: T\n}\n\nfunction createState(\n\tstatus: StateName,\n\tfailureCause?: unknown,\n): CircuitInternalState {\n\tconst controller = new AbortController()\n\treturn {\n\t\tcontroller,\n\t\tfailureCause,\n\t\tfailureRate: 0,\n\t\thistory: new Map(),\n\t\tstatus,\n\t}\n}\n\n/**\n * Creates a circuit breaker that wraps an async function with failure tracking\n * and automatic fallback behavior.\n *\n * The circuit breaker operates in four states:\n *\n * - `closed`: Normal operation, tracks failures in a sliding window\n * - `open`: Failed state, fallback is used until `resetAfter` milliseconds\n * - `halfOpen`: Testing recovery, allows trial calls\n * - `disposed`: Terminal state, all calls rejected\n *\n * When the failure rate exceeds `errorThreshold` within the `errorWindow`, the\n * circuit opens and rejects calls (using fallback if provided) for `resetAfter`\n * milliseconds. After this period, it transitions to half-open and allows up\n * to `minimumCandidates` concurrent trial calls. If their failure rate stays\n * at or below the threshold, the circuit closes; otherwise it reopens.\n *\n * @example\n * ```ts\n * const protectedFn = createCircuitBreaker(unreliableApiCall, {\n * errorThreshold: 0.5,\n * errorWindow: 10_000,\n * resetAfter: 30_000,\n * fallback: () => cachedResponse,\n * })\n *\n * try {\n * const result = await protectedFn(arg1, arg2)\n * } catch (error) {\n * console.error('Circuit breaker rejected call:', error)\n * }\n *\n * console.log(protectedFn.getState()) // 'closed' | 'open' | 'halfOpen' | 'disposed'\n * protectedFn[Symbol.dispose]() // Clean up timers and resources\n * ```\n */\nexport function createCircuitBreaker<Ret, Args extends unknown[]>(\n\tmain: MainFn<Ret, Args>,\n\toptions: CircuitBreakerOptions<MainFn<Ret, Args>> = {},\n): CircuitBreakerProtectedFn<Ret, Args> {\n\tconst {\n\t\terrorIsTransient,\n\t\terrorThreshold,\n\t\terrorWindow,\n\t\tfallback,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t\tretryDelay,\n\t\tretryLimit,\n\t\tretryTest,\n\t\ttimeout,\n\t} = parseOptions(options)\n\n\tlet state = createState(\"closed\")\n\n\tasync function tryCall(\n\t\tcurrent: CircuitInternalState,\n\t\targs: Args,\n\t): Promise<Ret> {\n\t\tconst { history } = current\n\t\tconst request = promiseTry(() => main(...args))\n\n\t\tlet historyItem: HistoryEntry | undefined = { status: \"pending\" }\n\t\thistory.set(request, historyItem)\n\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\ttimeout > 0\n\t\t\t\t\t? await abortable(AbortSignal.timeout(timeout), request)\n\t\t\t\t\t: await request\n\t\t\thistoryItem.status = \"resolved\"\n\t\t\treturn result\n\t\t} catch (cause) {\n\t\t\thistoryItem.status = \"rejected\"\n\t\t\t// Drop this request if it's a transient error that shouldn't count\n\t\t\t// towards the failure rate\n\t\t\tconst isTransient = errorIsTransient(cause)\n\t\t\tif (isTransient) historyItem = undefined\n\n\t\t\t// Wrap the error in a CircuitError to provide additional context and\n\t\t\t// control flow handling.\n\t\t\tthrow new CircuitError(\"CALL_FAILURE\", { cause, isTransient })\n\t\t} finally {\n\t\t\t// Remove the request if it was a transient failure, or if it's stale.\n\t\t\tif (!historyItem || state !== current) history.delete(request)\n\t\t\t// Keep the request in history until the end of the error window, or until\n\t\t\t// the circuit transitions.\n\t\t\telse {\n\t\t\t\tconst { signal } = current.controller\n\t\t\t\tdelayMs(errorWindow, signal)\n\t\t\t\t\t.catch(() => {})\n\t\t\t\t\t.finally(() => history.delete(request))\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction calculateFailureRate(): number {\n\t\tlet failures = 0\n\t\tlet total = 0\n\t\tfor (const { status } of state.history.values()) {\n\t\t\tif (status === \"rejected\") failures++\n\t\t\tif (status !== \"pending\") total++\n\t\t}\n\t\tif (!total || total < minimumCandidates) return 0\n\t\treturn failures / total\n\t}\n\n\tfunction transitionTo(\n\t\ttoStatus: StateName,\n\t\tfailureCause?: unknown,\n\t): CircuitInternalState {\n\t\tassertTransition(state.status, toStatus)\n\t\tstate.controller.abort()\n\t\treturn (state = createState(toStatus, failureCause))\n\t}\n\n\tasync function transitionToOpen(error: CircuitError): Promise<void> {\n\t\t// Race guard: a concurrent failure may have already changed state.\n\t\tif (state.status !== \"closed\" && state.status !== \"halfOpen\") return\n\n\t\tconst cause = error.cause ?? error\n\t\tconst nextState = transitionTo(\"open\", cause)\n\t\tif (onOpen) setImmediate(onOpen, cause)\n\n\t\tconst { signal } = nextState.controller\n\t\tawait delayMs(resetAfter, signal)\n\t\tif (state === nextState) transitionToHalfOpen()\n\t}\n\n\tfunction transitionToHalfOpen(): void {\n\t\ttransitionTo(\"halfOpen\", state.failureCause)\n\t\tif (onHalfOpen) setImmediate(onHalfOpen)\n\t}\n\n\tfunction transitionToClosed(): void {\n\t\ttransitionTo(\"closed\")\n\t\tif (onClose) setImmediate(onClose)\n\t}\n\n\tfunction guardIsCurrent(\n\t\tcurrent: CircuitInternalState,\n\t\terror: unknown,\n\t): error is CircuitError {\n\t\tif (!(error instanceof CircuitError)) throw error\n\t\t// Transient errors shouldn't affect the circuit breaker's state. Re-throw\n\t\t// the original cause of the error.\n\t\tif (error.isTransient) throw error.cause\n\n\t\t// If the circuit breaker was disposed mid-flight, surface the underlying\n\t\t// cause of the in-flight call rather than the dispose error.\n\t\tif (state.status === \"disposed\")\n\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\tthrow error.cause ?? new CircuitError(\"DISPOSED\")\n\n\t\t// If the circuit breaker transitioned states, try again.\n\t\treturn state === current\n\t}\n\n\tasync function protectedFn(...args: Args): Promise<Ret> {\n\t\tlet lastError: CircuitError | undefined\n\t\tlet retries = 0\n\t\tdo {\n\t\t\tconst current = state\n\t\t\tlastError = undefined\n\n\t\t\t// Closed: Normal Operation\n\t\t\tif (current.status === \"closed\") {\n\t\t\t\ttry {\n\t\t\t\t\treturn await tryCall(current, args)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (guardIsCurrent(current, error)) {\n\t\t\t\t\t\tlastError = error\n\t\t\t\t\t\t// Determine if the failure rate should open the circuit.\n\t\t\t\t\t\tconst rate = (current.failureRate = calculateFailureRate())\n\t\t\t\t\t\tif (rate > errorThreshold) transitionToOpen(error).catch(noop)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Half-Open: Execute trial calls until we have enough candidates.\n\t\t\telse if (\n\t\t\t\tcurrent.status === \"halfOpen\" &&\n\t\t\t\tcurrent.history.size < minimumCandidates\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\treturn await tryCall(current, args)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (guardIsCurrent(current, error)) lastError = error\n\t\t\t\t\tbreak\n\t\t\t\t} finally {\n\t\t\t\t\t// Do nothing until we have enough candidates to make a decision.\n\t\t\t\t\tif (state === current && current.history.size >= minimumCandidates) {\n\t\t\t\t\t\tconst rate = (current.failureRate = calculateFailureRate())\n\t\t\t\t\t\t// Determine if the failure rate should re-open the circuit or\n\t\t\t\t\t\t// if it is healthy enough to close it again.\n\t\t\t\t\t\tif (rate > errorThreshold && lastError)\n\t\t\t\t\t\t\ttransitionToOpen(lastError).catch(noop)\n\t\t\t\t\t\telse if (rate <= errorThreshold) transitionToClosed()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Open: Skip calls and immediately return fallback if available.\n\t\t\telse if (current.status === \"open\" || current.status === \"halfOpen\") {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Disposed: Reject all calls with dispose error.\n\t\t\telse throw current.failureCause\n\t\t} while (\n\t\t\tawait shouldContinue({\n\t\t\t\tretries: ++retries,\n\t\t\t\tlastError: lastError?.cause ?? lastError,\n\t\t\t\tretryDelay,\n\t\t\t\tretryLimit,\n\t\t\t\tretryTest,\n\t\t\t\tsignal: state.controller.signal,\n\t\t\t}).catch((error: CircuitError) => {\n\t\t\t\tlastError = error\n\t\t\t\treturn false\n\t\t\t})\n\t\t)\n\n\t\tif (!fallback) throw lastError ?? state.failureCause\n\t\treturn fallback(...args)\n\t}\n\n\tfunction dispose(disposeMessage = \"ERR_CIRCUIT_BREAKER_DISPOSED\"): void {\n\t\tif (state.status === \"disposed\") return\n\t\ttransitionTo(\"disposed\", new ReferenceError(disposeMessage))\n\t\tmain[Symbol.dispose]?.()\n\t}\n\n\tconst wrapped = protectedFn as CircuitBreakerProtectedFn<Ret, Args>\n\twrapped[Symbol.dispose] = () => dispose()\n\twrapped.dispose = dispose\n\twrapped.getFailureRate = () => state.failureRate\n\twrapped.getLatestError = () => state.failureCause\n\twrapped.getState = () => state.status\n\n\treturn wrapped\n}\n"],"names":[],"mappings":"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"}
|
package/dist/index.d.cts
CHANGED
|
@@ -14,13 +14,16 @@ type StateName = "closed" | "halfOpen" | "open" | "disposed";
|
|
|
14
14
|
*/
|
|
15
15
|
interface CircuitBreakerOptions<Fallback extends AnyFn = AnyFn> {
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* breaker decisions.
|
|
17
|
+
* When this returns true, the error is treated as transient — it will be
|
|
18
|
+
* thrown to the caller but will NOT count towards the circuit breaker's
|
|
19
|
+
* failure rate.
|
|
21
20
|
*
|
|
22
21
|
* @default () => false // No errors are excluded
|
|
23
22
|
*/
|
|
23
|
+
errorIsTransient?: ErrorTest;
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated Use `errorIsTransient` instead.
|
|
26
|
+
*/
|
|
24
27
|
errorIsFailure?: ErrorTest;
|
|
25
28
|
/**
|
|
26
29
|
* The percentage of errors (as a number between 0 and 1) which must occur
|
|
@@ -104,7 +107,7 @@ interface CircuitBreakerOptions<Fallback extends AnyFn = AnyFn> {
|
|
|
104
107
|
* If greater than zero, each call to `main` is raced against an
|
|
105
108
|
* `AbortSignal.timeout` of this many milliseconds. When the timeout fires
|
|
106
109
|
* first, the call rejects with the signal's reason and is counted as a
|
|
107
|
-
* failure (subject to `
|
|
110
|
+
* failure (subject to `errorIsTransient`).
|
|
108
111
|
*
|
|
109
112
|
* @default 0 // No per-call timeout
|
|
110
113
|
*/
|
|
@@ -139,69 +142,6 @@ interface MainFn<Ret = unknown, Args extends readonly unknown[] = never[]> {
|
|
|
139
142
|
interface RetryDelayFn {
|
|
140
143
|
(attempt: number, signal?: AbortSignal): Promise<void>;
|
|
141
144
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Configuration options for retry behavior.
|
|
144
|
-
*/
|
|
145
|
-
interface RetryOptions {
|
|
146
|
-
/**
|
|
147
|
-
* Whether an error should be treated as non-retryable. When this returns
|
|
148
|
-
* true, the error will be thrown immediately without retrying.
|
|
149
|
-
*
|
|
150
|
-
* @default () => true // All errors are retried
|
|
151
|
-
*/
|
|
152
|
-
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
153
|
-
/**
|
|
154
|
-
* Maximum number of retries
|
|
155
|
-
*
|
|
156
|
-
* @default 3
|
|
157
|
-
*/
|
|
158
|
-
maxAttempts?: number;
|
|
159
|
-
/**
|
|
160
|
-
* Function that returns a promise resolving when the next retry should occur.
|
|
161
|
-
* Receives the attempt number (starting at 2) and an abort signal.
|
|
162
|
-
*
|
|
163
|
-
* @default () => Promise.resolve() // Immediate retry
|
|
164
|
-
*/
|
|
165
|
-
retryDelay?: RetryDelayFn;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Wrap a function with retry logic. Errors will be retried according to the
|
|
170
|
-
* provided options.
|
|
171
|
-
*
|
|
172
|
-
* @example
|
|
173
|
-
* ```ts
|
|
174
|
-
* // Compose with circuit breaker. Retry up to 3 times with no delay
|
|
175
|
-
* const protectedA = createCircuitBreaker(
|
|
176
|
-
* withRetry(unreliableApiCall, { maxAttempts: 3 })
|
|
177
|
-
* )
|
|
178
|
-
*
|
|
179
|
-
* // Retry up to 5 times with exponential backoff
|
|
180
|
-
* const protectedB = createCircuitBreaker(
|
|
181
|
-
* withRetry(unreliableApiCall, {
|
|
182
|
-
* maxAttempts: 5,
|
|
183
|
-
* retryDelay: useExponentialBackoff(30),
|
|
184
|
-
* })
|
|
185
|
-
* )
|
|
186
|
-
* ```
|
|
187
|
-
*/
|
|
188
|
-
declare function withRetry$1<Ret, Args extends readonly unknown[]>(main: MainFn<Ret, Args>, options?: Readonly<RetryOptions>): MainFn<Ret, Args> & Disposable;
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Wraps an async function with a timeout constraint. Rejects with an Error if
|
|
192
|
-
* execution exceeds the specified timeout.
|
|
193
|
-
*
|
|
194
|
-
* @example
|
|
195
|
-
* ```ts
|
|
196
|
-
* const fetchWithTimeout = withTimeout(fetchData, 5000, "Fetch timed out")
|
|
197
|
-
* try {
|
|
198
|
-
* const data = await fetchWithTimeout(url)
|
|
199
|
-
* } catch (error) {
|
|
200
|
-
* console.error(error.message) // "Fetch timed out" after 5 seconds
|
|
201
|
-
* }
|
|
202
|
-
* ```
|
|
203
|
-
*/
|
|
204
|
-
declare function withTimeout$1<Ret, Args extends readonly unknown[]>(main: MainFn<Ret, Args>, timeoutMs: number, timeoutMessage?: string): MainFn<Ret, Args> & Disposable;
|
|
205
145
|
|
|
206
146
|
/**
|
|
207
147
|
* Creates an exponential backoff strategy for retry delays.
|
|
@@ -234,6 +174,14 @@ declare function useExponentialBackoff(maxSeconds: number): RetryDelayFn;
|
|
|
234
174
|
*/
|
|
235
175
|
declare function useFibonacciBackoff(maxSeconds: number): RetryDelayFn;
|
|
236
176
|
|
|
177
|
+
declare class CircuitError extends Error {
|
|
178
|
+
isTransient: boolean;
|
|
179
|
+
constructor(message: string, options?: {
|
|
180
|
+
cause?: unknown;
|
|
181
|
+
isTransient?: boolean;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
237
185
|
/**
|
|
238
186
|
* Creates a circuit breaker that wraps an async function with failure tracking
|
|
239
187
|
* and automatic fallback behavior.
|
|
@@ -277,12 +225,5 @@ declare function createCircuitBreaker<Ret, Args extends unknown[]>(main: MainFn<
|
|
|
277
225
|
*/
|
|
278
226
|
declare const delayMs: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
279
227
|
|
|
280
|
-
|
|
281
|
-
type
|
|
282
|
-
/** @deprecated Use `retryLimit`, `retryDelay`, and `retryTest` options on `createCircuitBreaker` instead. */
|
|
283
|
-
declare const withRetry: typeof withRetry$1;
|
|
284
|
-
/** @deprecated Use `options.timeout` on `createCircuitBreaker` instead. */
|
|
285
|
-
declare const withTimeout: typeof withTimeout$1;
|
|
286
|
-
|
|
287
|
-
export { createCircuitBreaker, delayMs, useExponentialBackoff, useFibonacciBackoff, withRetry, withTimeout };
|
|
288
|
-
export type { CircuitBreakerOptions, CircuitBreakerProtectedFn, CircuitState, MainFn, RetryOptions, StateName };
|
|
228
|
+
export { CircuitError, createCircuitBreaker, delayMs, useExponentialBackoff, useFibonacciBackoff };
|
|
229
|
+
export type { CircuitBreakerOptions, CircuitBreakerProtectedFn, MainFn, StateName };
|
package/dist/index.d.mts
CHANGED
|
@@ -14,13 +14,16 @@ type StateName = "closed" | "halfOpen" | "open" | "disposed";
|
|
|
14
14
|
*/
|
|
15
15
|
interface CircuitBreakerOptions<Fallback extends AnyFn = AnyFn> {
|
|
16
16
|
/**
|
|
17
|
-
*
|
|
18
|
-
*
|
|
19
|
-
*
|
|
20
|
-
* breaker decisions.
|
|
17
|
+
* When this returns true, the error is treated as transient — it will be
|
|
18
|
+
* thrown to the caller but will NOT count towards the circuit breaker's
|
|
19
|
+
* failure rate.
|
|
21
20
|
*
|
|
22
21
|
* @default () => false // No errors are excluded
|
|
23
22
|
*/
|
|
23
|
+
errorIsTransient?: ErrorTest;
|
|
24
|
+
/**
|
|
25
|
+
* @deprecated Use `errorIsTransient` instead.
|
|
26
|
+
*/
|
|
24
27
|
errorIsFailure?: ErrorTest;
|
|
25
28
|
/**
|
|
26
29
|
* The percentage of errors (as a number between 0 and 1) which must occur
|
|
@@ -104,7 +107,7 @@ interface CircuitBreakerOptions<Fallback extends AnyFn = AnyFn> {
|
|
|
104
107
|
* If greater than zero, each call to `main` is raced against an
|
|
105
108
|
* `AbortSignal.timeout` of this many milliseconds. When the timeout fires
|
|
106
109
|
* first, the call rejects with the signal's reason and is counted as a
|
|
107
|
-
* failure (subject to `
|
|
110
|
+
* failure (subject to `errorIsTransient`).
|
|
108
111
|
*
|
|
109
112
|
* @default 0 // No per-call timeout
|
|
110
113
|
*/
|
|
@@ -139,69 +142,6 @@ interface MainFn<Ret = unknown, Args extends readonly unknown[] = never[]> {
|
|
|
139
142
|
interface RetryDelayFn {
|
|
140
143
|
(attempt: number, signal?: AbortSignal): Promise<void>;
|
|
141
144
|
}
|
|
142
|
-
/**
|
|
143
|
-
* Configuration options for retry behavior.
|
|
144
|
-
*/
|
|
145
|
-
interface RetryOptions {
|
|
146
|
-
/**
|
|
147
|
-
* Whether an error should be treated as non-retryable. When this returns
|
|
148
|
-
* true, the error will be thrown immediately without retrying.
|
|
149
|
-
*
|
|
150
|
-
* @default () => true // All errors are retried
|
|
151
|
-
*/
|
|
152
|
-
shouldRetry?: (error: unknown, attempt: number) => boolean;
|
|
153
|
-
/**
|
|
154
|
-
* Maximum number of retries
|
|
155
|
-
*
|
|
156
|
-
* @default 3
|
|
157
|
-
*/
|
|
158
|
-
maxAttempts?: number;
|
|
159
|
-
/**
|
|
160
|
-
* Function that returns a promise resolving when the next retry should occur.
|
|
161
|
-
* Receives the attempt number (starting at 2) and an abort signal.
|
|
162
|
-
*
|
|
163
|
-
* @default () => Promise.resolve() // Immediate retry
|
|
164
|
-
*/
|
|
165
|
-
retryDelay?: RetryDelayFn;
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
/**
|
|
169
|
-
* Wrap a function with retry logic. Errors will be retried according to the
|
|
170
|
-
* provided options.
|
|
171
|
-
*
|
|
172
|
-
* @example
|
|
173
|
-
* ```ts
|
|
174
|
-
* // Compose with circuit breaker. Retry up to 3 times with no delay
|
|
175
|
-
* const protectedA = createCircuitBreaker(
|
|
176
|
-
* withRetry(unreliableApiCall, { maxAttempts: 3 })
|
|
177
|
-
* )
|
|
178
|
-
*
|
|
179
|
-
* // Retry up to 5 times with exponential backoff
|
|
180
|
-
* const protectedB = createCircuitBreaker(
|
|
181
|
-
* withRetry(unreliableApiCall, {
|
|
182
|
-
* maxAttempts: 5,
|
|
183
|
-
* retryDelay: useExponentialBackoff(30),
|
|
184
|
-
* })
|
|
185
|
-
* )
|
|
186
|
-
* ```
|
|
187
|
-
*/
|
|
188
|
-
declare function withRetry$1<Ret, Args extends readonly unknown[]>(main: MainFn<Ret, Args>, options?: Readonly<RetryOptions>): MainFn<Ret, Args> & Disposable;
|
|
189
|
-
|
|
190
|
-
/**
|
|
191
|
-
* Wraps an async function with a timeout constraint. Rejects with an Error if
|
|
192
|
-
* execution exceeds the specified timeout.
|
|
193
|
-
*
|
|
194
|
-
* @example
|
|
195
|
-
* ```ts
|
|
196
|
-
* const fetchWithTimeout = withTimeout(fetchData, 5000, "Fetch timed out")
|
|
197
|
-
* try {
|
|
198
|
-
* const data = await fetchWithTimeout(url)
|
|
199
|
-
* } catch (error) {
|
|
200
|
-
* console.error(error.message) // "Fetch timed out" after 5 seconds
|
|
201
|
-
* }
|
|
202
|
-
* ```
|
|
203
|
-
*/
|
|
204
|
-
declare function withTimeout$1<Ret, Args extends readonly unknown[]>(main: MainFn<Ret, Args>, timeoutMs: number, timeoutMessage?: string): MainFn<Ret, Args> & Disposable;
|
|
205
145
|
|
|
206
146
|
/**
|
|
207
147
|
* Creates an exponential backoff strategy for retry delays.
|
|
@@ -234,6 +174,14 @@ declare function useExponentialBackoff(maxSeconds: number): RetryDelayFn;
|
|
|
234
174
|
*/
|
|
235
175
|
declare function useFibonacciBackoff(maxSeconds: number): RetryDelayFn;
|
|
236
176
|
|
|
177
|
+
declare class CircuitError extends Error {
|
|
178
|
+
isTransient: boolean;
|
|
179
|
+
constructor(message: string, options?: {
|
|
180
|
+
cause?: unknown;
|
|
181
|
+
isTransient?: boolean;
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
|
|
237
185
|
/**
|
|
238
186
|
* Creates a circuit breaker that wraps an async function with failure tracking
|
|
239
187
|
* and automatic fallback behavior.
|
|
@@ -277,12 +225,5 @@ declare function createCircuitBreaker<Ret, Args extends unknown[]>(main: MainFn<
|
|
|
277
225
|
*/
|
|
278
226
|
declare const delayMs: (ms: number, signal?: AbortSignal) => Promise<void>;
|
|
279
227
|
|
|
280
|
-
|
|
281
|
-
type
|
|
282
|
-
/** @deprecated Use `retryLimit`, `retryDelay`, and `retryTest` options on `createCircuitBreaker` instead. */
|
|
283
|
-
declare const withRetry: typeof withRetry$1;
|
|
284
|
-
/** @deprecated Use `options.timeout` on `createCircuitBreaker` instead. */
|
|
285
|
-
declare const withTimeout: typeof withTimeout$1;
|
|
286
|
-
|
|
287
|
-
export { createCircuitBreaker, delayMs, useExponentialBackoff, useFibonacciBackoff, withRetry, withTimeout };
|
|
288
|
-
export type { CircuitBreakerOptions, CircuitBreakerProtectedFn, CircuitState, MainFn, RetryOptions, StateName };
|
|
228
|
+
export { CircuitError, createCircuitBreaker, delayMs, useExponentialBackoff, useFibonacciBackoff };
|
|
229
|
+
export type { CircuitBreakerOptions, CircuitBreakerProtectedFn, MainFn, StateName };
|
package/dist/index.mjs
CHANGED
|
@@ -26,16 +26,6 @@ const delayMs = (ms, signal) => {
|
|
|
26
26
|
signal.addEventListener("abort", onAbort, { once: true });
|
|
27
27
|
}) : new Promise((next) => setTimeout(next, ms));
|
|
28
28
|
};
|
|
29
|
-
const deprecated = (fn, method, message) => {
|
|
30
|
-
let warned = false;
|
|
31
|
-
return ((...args) => {
|
|
32
|
-
if (!warned) {
|
|
33
|
-
console.warn(`[breaker-box] ${method} Deprecation: ${message}`);
|
|
34
|
-
warned = true;
|
|
35
|
-
}
|
|
36
|
-
return fn(...args);
|
|
37
|
-
});
|
|
38
|
-
};
|
|
39
29
|
const noop = () => {
|
|
40
30
|
};
|
|
41
31
|
function promiseTry(fn) {
|
|
@@ -45,64 +35,6 @@ function promiseTry(fn) {
|
|
|
45
35
|
return Promise.reject(error);
|
|
46
36
|
}
|
|
47
37
|
}
|
|
48
|
-
async function shouldRetry(options) {
|
|
49
|
-
const { retries, lastError, retryDelay, retryLimit, retryTest, signal } = options;
|
|
50
|
-
if (retries >= retryLimit) throw lastError;
|
|
51
|
-
if (!retryTest(lastError)) throw lastError;
|
|
52
|
-
try {
|
|
53
|
-
if (!retryDelay) return true;
|
|
54
|
-
else if (typeof retryDelay === "number") await delayMs(retryDelay, signal);
|
|
55
|
-
else if (typeof retryDelay === "function") await retryDelay(retries, signal);
|
|
56
|
-
} catch {
|
|
57
|
-
}
|
|
58
|
-
return true;
|
|
59
|
-
}
|
|
60
|
-
|
|
61
|
-
function withRetry$1(main, options = {}) {
|
|
62
|
-
const {
|
|
63
|
-
shouldRetry = () => true,
|
|
64
|
-
maxAttempts = 3,
|
|
65
|
-
retryDelay = () => Promise.resolve()
|
|
66
|
-
} = options;
|
|
67
|
-
assert(maxAttempts >= 1, "maxAttempts must be a number greater than 0");
|
|
68
|
-
const controller = new AbortController();
|
|
69
|
-
const { signal } = controller;
|
|
70
|
-
async function withRetryFunction(...args) {
|
|
71
|
-
let attempt = 1;
|
|
72
|
-
while (true) {
|
|
73
|
-
try {
|
|
74
|
-
return await main(...args);
|
|
75
|
-
} catch (cause) {
|
|
76
|
-
if (attempt >= maxAttempts) {
|
|
77
|
-
throw new Error(`ERR_CIRCUIT_BREAKER_MAX_ATTEMPTS (${maxAttempts})`, {
|
|
78
|
-
cause
|
|
79
|
-
});
|
|
80
|
-
}
|
|
81
|
-
if (!shouldRetry(cause, attempt)) throw cause;
|
|
82
|
-
}
|
|
83
|
-
attempt++;
|
|
84
|
-
await abortable(signal, retryDelay(attempt, signal));
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return Object.assign(withRetryFunction, {
|
|
88
|
-
[Symbol.dispose]: () => controller.abort()
|
|
89
|
-
});
|
|
90
|
-
}
|
|
91
|
-
|
|
92
|
-
function withTimeout$1(main, timeoutMs, timeoutMessage = "ERR_CIRCUIT_BREAKER_TIMEOUT") {
|
|
93
|
-
const error = new Error(timeoutMessage);
|
|
94
|
-
const controller = new AbortController();
|
|
95
|
-
const { signal } = controller;
|
|
96
|
-
function withTimeoutFunction(...args) {
|
|
97
|
-
return new Promise((resolve, reject) => {
|
|
98
|
-
const timer = setTimeout(reject, timeoutMs, error);
|
|
99
|
-
abortable(signal, main(...args)).then(resolve, reject).finally(() => clearTimeout(timer));
|
|
100
|
-
});
|
|
101
|
-
}
|
|
102
|
-
return Object.assign(withTimeoutFunction, {
|
|
103
|
-
[Symbol.dispose]: () => controller.abort()
|
|
104
|
-
});
|
|
105
|
-
}
|
|
106
38
|
|
|
107
39
|
function useExponentialBackoff(maxSeconds) {
|
|
108
40
|
return function exponentialBackoff(attempt, signal) {
|
|
@@ -120,9 +52,18 @@ function useFibonacciBackoff(maxSeconds) {
|
|
|
120
52
|
};
|
|
121
53
|
}
|
|
122
54
|
|
|
55
|
+
class CircuitError extends Error {
|
|
56
|
+
isTransient;
|
|
57
|
+
constructor(message, options) {
|
|
58
|
+
super(`ERR_CIRCUIT_BREAKER_${message}`, options);
|
|
59
|
+
this.isTransient = options?.isTransient ?? false;
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
let warnedErrorIsFailure = false;
|
|
123
64
|
function parseOptions(options) {
|
|
124
65
|
const {
|
|
125
|
-
|
|
66
|
+
errorIsTransient = options.errorIsFailure ?? (() => false),
|
|
126
67
|
errorThreshold = 0,
|
|
127
68
|
errorWindow = 1e4,
|
|
128
69
|
fallback,
|
|
@@ -136,9 +77,15 @@ function parseOptions(options) {
|
|
|
136
77
|
retryTest = () => true,
|
|
137
78
|
timeout = 0
|
|
138
79
|
} = options;
|
|
80
|
+
if ("errorIsFailure" in options && !("errorIsTransient" in options) && !warnedErrorIsFailure) {
|
|
81
|
+
warnedErrorIsFailure = true;
|
|
82
|
+
console.warn(
|
|
83
|
+
'breaker-box: "errorIsFailure" is deprecated. Use "errorIsTransient" instead.'
|
|
84
|
+
);
|
|
85
|
+
}
|
|
139
86
|
assert(
|
|
140
|
-
typeof
|
|
141
|
-
`"
|
|
87
|
+
typeof errorIsTransient === "function",
|
|
88
|
+
`"errorIsTransient" must be a function (received ${typeof errorIsTransient})`
|
|
142
89
|
);
|
|
143
90
|
assert(
|
|
144
91
|
errorThreshold >= 0 && errorThreshold <= 1,
|
|
@@ -193,7 +140,7 @@ function parseOptions(options) {
|
|
|
193
140
|
`"timeout" must be a finite, non-negative number (received ${timeout})`
|
|
194
141
|
);
|
|
195
142
|
return {
|
|
196
|
-
|
|
143
|
+
errorIsTransient,
|
|
197
144
|
errorThreshold,
|
|
198
145
|
errorWindow,
|
|
199
146
|
fallback,
|
|
@@ -209,6 +156,20 @@ function parseOptions(options) {
|
|
|
209
156
|
};
|
|
210
157
|
}
|
|
211
158
|
|
|
159
|
+
async function shouldContinue(options) {
|
|
160
|
+
const { retries, lastError, retryDelay, retryLimit, retryTest, signal } = options;
|
|
161
|
+
if (retries >= retryLimit)
|
|
162
|
+
throw new CircuitError("MAX_RETRIES", { cause: lastError });
|
|
163
|
+
if (!retryTest(lastError))
|
|
164
|
+
throw new CircuitError("NON_RETRYABLE", { cause: lastError });
|
|
165
|
+
try {
|
|
166
|
+
if (!retryDelay) return true;
|
|
167
|
+
else if (typeof retryDelay === "number") await delayMs(retryDelay, signal);
|
|
168
|
+
else if (typeof retryDelay === "function") await retryDelay(retries, signal);
|
|
169
|
+
} catch {
|
|
170
|
+
}
|
|
171
|
+
return true;
|
|
172
|
+
}
|
|
212
173
|
const validTransitions = {
|
|
213
174
|
closed: ["open", "disposed"],
|
|
214
175
|
open: ["halfOpen", "disposed"],
|
|
@@ -221,13 +182,6 @@ function assertTransition(from, to) {
|
|
|
221
182
|
`Invalid transition from ${from} to ${to}`
|
|
222
183
|
);
|
|
223
184
|
}
|
|
224
|
-
class CircuitError extends Error {
|
|
225
|
-
isTransient;
|
|
226
|
-
constructor(message, options) {
|
|
227
|
-
super(`ERR_CIRCUIT_BREAKER_${message}`, options);
|
|
228
|
-
this.isTransient = options?.isTransient ?? false;
|
|
229
|
-
}
|
|
230
|
-
}
|
|
231
185
|
function createState(status, failureCause) {
|
|
232
186
|
const controller = new AbortController();
|
|
233
187
|
return {
|
|
@@ -240,7 +194,7 @@ function createState(status, failureCause) {
|
|
|
240
194
|
}
|
|
241
195
|
function createCircuitBreaker(main, options = {}) {
|
|
242
196
|
const {
|
|
243
|
-
|
|
197
|
+
errorIsTransient,
|
|
244
198
|
errorThreshold,
|
|
245
199
|
errorWindow,
|
|
246
200
|
fallback,
|
|
@@ -266,7 +220,7 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
266
220
|
return result;
|
|
267
221
|
} catch (cause) {
|
|
268
222
|
historyItem.status = "rejected";
|
|
269
|
-
const isTransient =
|
|
223
|
+
const isTransient = errorIsTransient(cause);
|
|
270
224
|
if (isTransient) historyItem = void 0;
|
|
271
225
|
throw new CircuitError("CALL_FAILURE", { cause, isTransient });
|
|
272
226
|
} finally {
|
|
@@ -303,7 +257,7 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
303
257
|
if (state === nextState) transitionToHalfOpen();
|
|
304
258
|
}
|
|
305
259
|
function transitionToHalfOpen() {
|
|
306
|
-
transitionTo("halfOpen");
|
|
260
|
+
transitionTo("halfOpen", state.failureCause);
|
|
307
261
|
if (onHalfOpen) setImmediate(onHalfOpen);
|
|
308
262
|
}
|
|
309
263
|
function transitionToClosed() {
|
|
@@ -322,6 +276,7 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
322
276
|
let retries = 0;
|
|
323
277
|
do {
|
|
324
278
|
const current = state;
|
|
279
|
+
lastError = void 0;
|
|
325
280
|
if (current.status === "closed") {
|
|
326
281
|
try {
|
|
327
282
|
return await tryCall(current, args);
|
|
@@ -337,6 +292,7 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
337
292
|
return await tryCall(current, args);
|
|
338
293
|
} catch (error) {
|
|
339
294
|
if (guardIsCurrent(current, error)) lastError = error;
|
|
295
|
+
break;
|
|
340
296
|
} finally {
|
|
341
297
|
if (state === current && current.history.size >= minimumCandidates) {
|
|
342
298
|
const rate = current.failureRate = calculateFailureRate();
|
|
@@ -346,23 +302,21 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
346
302
|
}
|
|
347
303
|
}
|
|
348
304
|
} else if (current.status === "open" || current.status === "halfOpen") {
|
|
349
|
-
|
|
350
|
-
throw (
|
|
351
|
-
// eslint-disable-next-line @typescript-eslint/only-throw-error
|
|
352
|
-
current.failureCause ?? new CircuitError("OPEN", { cause: current.failureCause })
|
|
353
|
-
);
|
|
354
|
-
}
|
|
355
|
-
return await fallback(...args);
|
|
305
|
+
break;
|
|
356
306
|
} else throw current.failureCause;
|
|
357
|
-
} while (await
|
|
307
|
+
} while (await shouldContinue({
|
|
358
308
|
retries: ++retries,
|
|
359
|
-
lastError,
|
|
309
|
+
lastError: lastError?.cause ?? lastError,
|
|
360
310
|
retryDelay,
|
|
361
311
|
retryLimit,
|
|
362
312
|
retryTest,
|
|
363
313
|
signal: state.controller.signal
|
|
314
|
+
}).catch((error) => {
|
|
315
|
+
lastError = error;
|
|
316
|
+
return false;
|
|
364
317
|
}));
|
|
365
|
-
|
|
318
|
+
if (!fallback) throw lastError ?? state.failureCause;
|
|
319
|
+
return fallback(...args);
|
|
366
320
|
}
|
|
367
321
|
function dispose(disposeMessage = "ERR_CIRCUIT_BREAKER_DISPOSED") {
|
|
368
322
|
if (state.status === "disposed") return;
|
|
@@ -378,16 +332,5 @@ function createCircuitBreaker(main, options = {}) {
|
|
|
378
332
|
return wrapped;
|
|
379
333
|
}
|
|
380
334
|
|
|
381
|
-
|
|
382
|
-
withRetry$1,
|
|
383
|
-
"withRetry",
|
|
384
|
-
"Use `retryLimit`, `retryDelay`, and `retryTest` options on `createCircuitBreaker` instead."
|
|
385
|
-
);
|
|
386
|
-
const withTimeout = deprecated(
|
|
387
|
-
withTimeout$1,
|
|
388
|
-
"withTimeout",
|
|
389
|
-
"Use `options.timeout` on`createCircuitBreaker` instead."
|
|
390
|
-
);
|
|
391
|
-
|
|
392
|
-
export { createCircuitBreaker, delayMs, useExponentialBackoff, useFibonacciBackoff, withRetry, withTimeout };
|
|
335
|
+
export { CircuitError, createCircuitBreaker, delayMs, useExponentialBackoff, useFibonacciBackoff };
|
|
393
336
|
//# sourceMappingURL=index.mjs.map
|
package/dist/index.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.mjs","sources":["../lib/util.ts","../lib/retry.ts","../lib/timeout.ts","../lib/backoff.ts","../lib/options.ts","../lib/circuit-breaker.ts","../lib/index.ts"],"sourcesContent":["import type { AnyFn, RetryDelayFn } from \"./types.js\"\n\n/**\n * Returns a promise which rejects when the abort signal is triggered or\n * resolves when the promise is fulfilled.\n */\nexport const abortable = <T>(\n\tsignal: AbortSignal,\n\tpending: PromiseLike<T>,\n): Promise<T> =>\n\tnew Promise((resolve, reject) => {\n\t\tsignal.throwIfAborted()\n\n\t\tconst onAbort = () => reject(signal.reason)\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\tPromise.resolve(pending)\n\t\t\t.finally(() => signal.removeEventListener(\"abort\", onAbort))\n\t\t\t.then(resolve, reject)\n\t})\n\n/**\n * Asserts that the given value is truthy. If not, throws a `TypeError`.\n */\nexport function assert(value: unknown, message?: string): asserts value {\n\tif (!value) throw new TypeError(message)\n}\n\n/**\n * Returns a promise that resolves after the specified number of milliseconds.\n */\nexport const delayMs = (ms: number, signal?: AbortSignal): Promise<void> => {\n\tif (!Number.isFinite(ms) || ms < 0) {\n\t\tthrow new RangeError(\n\t\t\t`\"ms\" must be a finite, non-negative number (received ${ms})`,\n\t\t)\n\t}\n\n\treturn signal\n\t\t? new Promise((resolve, reject) => {\n\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort)\n\t\t\t\t\tresolve()\n\t\t\t\t}, ms)\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\treject(signal.reason)\n\t\t\t\t}\n\n\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true })\n\t\t\t})\n\t\t: new Promise((next) => setTimeout(next, ms))\n}\n\nexport const deprecated = <T extends AnyFn>(\n\tfn: T,\n\tmethod: string,\n\tmessage: string,\n): T => {\n\tlet warned = false\n\treturn ((...args) => {\n\t\tif (!warned) {\n\t\t\tconsole.warn(`[breaker-box] ${method} Deprecation: ${message}`)\n\t\t\twarned = true\n\t\t}\n\t\treturn fn(...args)\n\t}) as T\n}\n\nexport const identity = <T>(value: T): T => value\n\nexport const noop: (...args: unknown[]) => void = () => {}\n\n/**\n * Polyfill for `Promise.try()`\n */\nexport function promiseTry<T>(fn: () => T): Promise<T> {\n\ttry {\n\t\treturn Promise.resolve(fn())\n\t} catch (error) {\n\t\treturn Promise.reject(error)\n\t}\n}\n\nexport async function shouldRetry(options: {\n\tretries: number\n\tlastError: unknown\n\tretryDelay: number | RetryDelayFn\n\tretryLimit: number\n\tretryTest: (error: unknown) => boolean\n\tsignal: AbortSignal\n}): Promise<true> {\n\tconst { retries, lastError, retryDelay, retryLimit, retryTest, signal } =\n\t\toptions\n\n\tif (retries >= retryLimit) throw lastError\n\tif (!retryTest(lastError)) throw lastError\n\n\ttry {\n\t\tif (!retryDelay) return true\n\t\telse if (typeof retryDelay === \"number\") await delayMs(retryDelay, signal)\n\t\telse if (typeof retryDelay === \"function\") await retryDelay(retries, signal)\n\t} catch {\n\t\t/* empty */\n\t}\n\n\treturn true\n}\n","import { type MainFn, type RetryOptions } from \"./types.js\"\nimport { assert, abortable } from \"./util.js\"\n\n/**\n * Wrap a function with retry logic. Errors will be retried according to the\n * provided options.\n *\n * @example\n * ```ts\n * // Compose with circuit breaker. Retry up to 3 times with no delay\n * const protectedA = createCircuitBreaker(\n * withRetry(unreliableApiCall, { maxAttempts: 3 })\n * )\n *\n * // Retry up to 5 times with exponential backoff\n * const protectedB = createCircuitBreaker(\n * withRetry(unreliableApiCall, {\n * maxAttempts: 5,\n * retryDelay: useExponentialBackoff(30),\n * })\n * )\n * ```\n */\nexport function withRetry<Ret, Args extends readonly unknown[]>(\n\tmain: MainFn<Ret, Args>,\n\toptions: Readonly<RetryOptions> = {},\n): MainFn<Ret, Args> & Disposable {\n\tconst {\n\t\tshouldRetry = () => true,\n\t\tmaxAttempts = 3,\n\t\tretryDelay = () => Promise.resolve(),\n\t} = options\n\n\tassert(maxAttempts >= 1, \"maxAttempts must be a number greater than 0\")\n\n\tconst controller = new AbortController()\n\tconst { signal } = controller\n\n\tasync function withRetryFunction(...args: Args): Promise<Ret> {\n\t\tlet attempt = 1\n\t\twhile (true) {\n\t\t\ttry {\n\t\t\t\treturn await main(...args)\n\t\t\t} catch (cause) {\n\t\t\t\tif (attempt >= maxAttempts) {\n\t\t\t\t\tthrow new Error(`ERR_CIRCUIT_BREAKER_MAX_ATTEMPTS (${maxAttempts})`, {\n\t\t\t\t\t\tcause,\n\t\t\t\t\t})\n\t\t\t\t}\n\n\t\t\t\tif (!shouldRetry(cause, attempt)) throw cause\n\t\t\t}\n\n\t\t\tattempt++\n\t\t\tawait abortable(signal, retryDelay(attempt, signal))\n\t\t}\n\t}\n\n\treturn Object.assign(withRetryFunction, {\n\t\t[Symbol.dispose]: () => controller.abort(),\n\t})\n}\n","import { type MainFn } from \"./types.js\"\nimport { abortable } from \"./util.js\"\n\n/**\n * Wraps an async function with a timeout constraint. Rejects with an Error if\n * execution exceeds the specified timeout.\n *\n * @example\n * ```ts\n * const fetchWithTimeout = withTimeout(fetchData, 5000, \"Fetch timed out\")\n * try {\n * const data = await fetchWithTimeout(url)\n * } catch (error) {\n * console.error(error.message) // \"Fetch timed out\" after 5 seconds\n * }\n * ```\n */\nexport function withTimeout<Ret, Args extends readonly unknown[]>(\n\tmain: MainFn<Ret, Args>,\n\ttimeoutMs: number,\n\ttimeoutMessage = \"ERR_CIRCUIT_BREAKER_TIMEOUT\",\n): MainFn<Ret, Args> & Disposable {\n\tconst error = new Error(timeoutMessage)\n\tconst controller = new AbortController()\n\tconst { signal } = controller\n\n\tfunction withTimeoutFunction(...args: Args): Promise<Ret> {\n\t\treturn new Promise((resolve, reject) => {\n\t\t\tconst timer = setTimeout(reject, timeoutMs, error)\n\n\t\t\tabortable(signal, main(...args))\n\t\t\t\t.then(resolve, reject)\n\t\t\t\t.finally(() => clearTimeout(timer))\n\t\t})\n\t}\n\n\treturn Object.assign(withTimeoutFunction, {\n\t\t[Symbol.dispose]: () => controller.abort(),\n\t})\n}\n","import type { RetryDelayFn } from \"./types.js\"\nimport { delayMs } from \"./util.js\"\n\n/**\n * Creates an exponential backoff strategy for retry delays.\n * Delay grows as 2^(attempt-2) seconds, capped at maxSeconds.\n *\n * The sequence is: 1s, 2s, 4s, 8s, 16s, 32s, etc.\n *\n * @example\n * ```ts\n * const backoff = useExponentialBackoff(30)\n * await backoff(2) // waits 1 second\n * await backoff(3) // waits 2 seconds\n * await backoff(10) // waits 30 seconds (capped)\n * ```\n */\nexport function useExponentialBackoff(maxSeconds: number): RetryDelayFn {\n\treturn function exponentialBackoff(attempt, signal) {\n\t\tconst num = Math.max(attempt - 2, 0)\n\t\tconst delay = Math.min(2 ** num, maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n\nconst sqrt5 = /* @__PURE__ */ Math.sqrt(5)\n/**\n * Binet's formula for calculating Fibonacci numbers in constant time.\n * @see https://en.wikipedia.org/wiki/Fibonacci_sequence#Closed-form_expression\n */\nconst binet = (n: number) =>\n\tMath.round(((1 + sqrt5) ** n - (1 - sqrt5) ** n) / (2 ** n * sqrt5))\n\n/**\n * Creates a Fibonacci backoff strategy for retry delays. It is more gradual\n * than exponential backoff, useful for more aggressive retry patterns.\n *\n * The sequence is: 1s, 2s, 3s, 5s, 8s, 13s, etc.\n *\n * @example\n * ```ts\n * const backoff = useFibonacciBackoff(60)\n * await backoff(2) // waits 1 second\n * await backoff(5) // waits 5 seconds\n * await backoff(10) // waits 55 seconds\n * ```\n */\nexport function useFibonacciBackoff(maxSeconds: number): RetryDelayFn {\n\treturn function fibonacciBackoff(attempt, signal) {\n\t\tconst delay = Math.min(binet(attempt), maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n","import type { AnyFn, CircuitBreakerOptions } from \"./types.js\"\nimport { assert } from \"./util.js\"\n\nexport function parseOptions<Fallback extends AnyFn>(\n\toptions: CircuitBreakerOptions<Fallback>,\n) {\n\tconst {\n\t\terrorIsFailure = () => false,\n\t\terrorThreshold = 0,\n\t\terrorWindow = 10_000,\n\t\tfallback,\n\t\tminimumCandidates = 1,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter = 30_000,\n\t\tretryDelay = 0,\n\t\tretryLimit = Infinity,\n\t\tretryTest = () => true,\n\t\ttimeout = 0,\n\t} = options\n\n\t// errorIsFailure\n\tassert(\n\t\ttypeof errorIsFailure === \"function\",\n\t\t`\"errorIsFailure\" must be a function (received ${typeof errorIsFailure})`,\n\t)\n\n\t// errorThreshold\n\tassert(\n\t\terrorThreshold >= 0 && errorThreshold <= 1,\n\t\t`\"errorThreshold\" must be between 0 and 1 (received ${errorThreshold})`,\n\t)\n\n\t// errorWindow\n\tassert(\n\t\terrorWindow >= 1_000,\n\t\t`\"errorWindow\" must be milliseconds of at least 1 second (received ${errorWindow})`,\n\t)\n\n\t// (optional) fallback\n\tassert(\n\t\t!fallback || typeof fallback === \"function\",\n\t\t`\"fallback\" must be a function (received ${typeof fallback})`,\n\t)\n\n\t// minimumCandidates\n\tassert(\n\t\tminimumCandidates >= 1,\n\t\t`\"minimumCandidates\" must be greater than 0 (received ${minimumCandidates})`,\n\t)\n\n\t// (optional) onClose\n\tassert(\n\t\t!onClose || typeof onClose === \"function\",\n\t\t`\"onClose\" must be a function (received ${typeof onClose})`,\n\t)\n\n\t// (optional) onHalfOpen\n\tassert(\n\t\t!onHalfOpen || typeof onHalfOpen === \"function\",\n\t\t`\"onHalfOpen\" must be a function (received ${typeof onHalfOpen})`,\n\t)\n\n\t// (optional) onOpen\n\tassert(\n\t\t!onOpen || typeof onOpen === \"function\",\n\t\t`\"onOpen\" must be a function (received ${typeof onOpen})`,\n\t)\n\n\t// resetAfter\n\tassert(\n\t\tresetAfter >= 1_000,\n\t\t`\"resetAfter\" must be milliseconds of at least 1 second (received ${resetAfter})`,\n\t)\n\tassert(\n\t\tresetAfter >= errorWindow,\n\t\t`\"resetAfter\" must be greater than or equal to \"errorWindow\" (received ${resetAfter}, expected >= ${errorWindow})`,\n\t)\n\n\t// retryDelay\n\tassert(\n\t\ttypeof retryDelay === \"function\" ||\n\t\t\t(typeof retryDelay === \"number\" &&\n\t\t\t\tretryDelay >= 0 &&\n\t\t\t\tNumber.isFinite(retryDelay)),\n\t\t`\"retryDelay\" must be a function or a finite, non-negative number (received ${typeof retryDelay})`,\n\t)\n\n\t// retryLimit\n\tassert(\n\t\ttypeof retryLimit === \"number\" && retryLimit >= 1,\n\t\t`\"retryLimit\" must be greater than 0 (received ${retryLimit})`,\n\t)\n\n\t// retryTest\n\tassert(\n\t\ttypeof retryTest === \"function\",\n\t\t`\"retryTest\" must be a function (received ${typeof retryTest})`,\n\t)\n\n\t// timeout\n\tassert(\n\t\tNumber.isFinite(timeout) && timeout >= 0,\n\t\t`\"timeout\" must be a finite, non-negative number (received ${timeout})`,\n\t)\n\n\treturn {\n\t\terrorIsFailure,\n\t\terrorThreshold,\n\t\terrorWindow,\n\t\tfallback,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t\tretryDelay,\n\t\tretryLimit,\n\t\tretryTest,\n\t\ttimeout,\n\t}\n}\n","import { parseOptions } from \"./options.js\"\nimport type {\n\tCircuitBreakerOptions,\n\tCircuitBreakerProtectedFn,\n\tHistoryEntry,\n\tHistoryMap,\n\tMainFn,\n\tStateName,\n} from \"./types.js\"\nimport {\n\tabortable,\n\tassert,\n\tdelayMs,\n\tnoop,\n\tpromiseTry,\n\tshouldRetry,\n} from \"./util.js\"\n\nconst validTransitions: Record<StateName, StateName[]> = {\n\tclosed: [\"open\", \"disposed\"],\n\topen: [\"halfOpen\", \"disposed\"],\n\thalfOpen: [\"closed\", \"open\", \"disposed\"],\n\tdisposed: [],\n}\n\nfunction assertTransition(from: StateName, to: StateName): void {\n\tassert(\n\t\tvalidTransitions[from].includes(to),\n\t\t`Invalid transition from ${from} to ${to}`,\n\t)\n}\n\ninterface CircuitInternalState<T extends StateName = StateName> {\n\tcontroller: AbortController\n\tfailureCause: unknown\n\tfailureRate: number\n\thistory: HistoryMap\n\tstatus: T\n}\n\nclass CircuitError extends Error {\n\tisTransient: boolean\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: { cause?: unknown; isTransient?: boolean },\n\t) {\n\t\tsuper(`ERR_CIRCUIT_BREAKER_${message}`, options)\n\t\tthis.isTransient = options?.isTransient ?? false\n\t}\n}\n\nfunction createState(\n\tstatus: StateName,\n\tfailureCause?: unknown,\n): CircuitInternalState {\n\tconst controller = new AbortController()\n\treturn {\n\t\tcontroller,\n\t\tfailureCause,\n\t\tfailureRate: 0,\n\t\thistory: new Map(),\n\t\tstatus,\n\t}\n}\n\n/**\n * Creates a circuit breaker that wraps an async function with failure tracking\n * and automatic fallback behavior.\n *\n * The circuit breaker operates in four states:\n *\n * - `closed`: Normal operation, tracks failures in a sliding window\n * - `open`: Failed state, fallback is used until `resetAfter` milliseconds\n * - `halfOpen`: Testing recovery, allows trial calls\n * - `disposed`: Terminal state, all calls rejected\n *\n * When the failure rate exceeds `errorThreshold` within the `errorWindow`, the\n * circuit opens and rejects calls (using fallback if provided) for `resetAfter`\n * milliseconds. After this period, it transitions to half-open and allows up\n * to `minimumCandidates` concurrent trial calls. If their failure rate stays\n * at or below the threshold, the circuit closes; otherwise it reopens.\n *\n * @example\n * ```ts\n * const protectedFn = createCircuitBreaker(unreliableApiCall, {\n * errorThreshold: 0.5,\n * errorWindow: 10_000,\n * resetAfter: 30_000,\n * fallback: () => cachedResponse,\n * })\n *\n * try {\n * const result = await protectedFn(arg1, arg2)\n * } catch (error) {\n * console.error('Circuit breaker rejected call:', error)\n * }\n *\n * console.log(protectedFn.getState()) // 'closed' | 'open' | 'halfOpen' | 'disposed'\n * protectedFn[Symbol.dispose]() // Clean up timers and resources\n * ```\n */\nexport function createCircuitBreaker<Ret, Args extends unknown[]>(\n\tmain: MainFn<Ret, Args>,\n\toptions: CircuitBreakerOptions<MainFn<Ret, Args>> = {},\n): CircuitBreakerProtectedFn<Ret, Args> {\n\tconst {\n\t\terrorIsFailure,\n\t\terrorThreshold,\n\t\terrorWindow,\n\t\tfallback,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t\tretryDelay,\n\t\tretryLimit,\n\t\tretryTest,\n\t\ttimeout,\n\t} = parseOptions(options)\n\n\tlet state = createState(\"closed\")\n\n\tasync function tryCall(\n\t\tcurrent: CircuitInternalState,\n\t\targs: Args,\n\t): Promise<Ret> {\n\t\tconst { history } = current\n\t\tconst request = promiseTry(() => main(...args))\n\n\t\tlet historyItem: HistoryEntry | undefined = { status: \"pending\" }\n\t\thistory.set(request, historyItem)\n\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\ttimeout > 0\n\t\t\t\t\t? await abortable(AbortSignal.timeout(timeout), request)\n\t\t\t\t\t: await request\n\t\t\thistoryItem.status = \"resolved\"\n\t\t\treturn result\n\t\t} catch (cause) {\n\t\t\thistoryItem.status = \"rejected\"\n\t\t\t// Drop this request if it's a transient error that shouldn't count\n\t\t\t// towards the failure rate\n\t\t\tconst isTransient = errorIsFailure(cause)\n\t\t\tif (isTransient) historyItem = undefined\n\n\t\t\t// Wrap the error in a CircuitError to provide additional context and\n\t\t\t// control flow handling.\n\t\t\tthrow new CircuitError(\"CALL_FAILURE\", { cause, isTransient })\n\t\t} finally {\n\t\t\t// Remove the request if it was a transient failure, or if it's stale.\n\t\t\tif (!historyItem || state !== current) history.delete(request)\n\t\t\t// Keep the request in history until the end of the error window, or until\n\t\t\t// the circuit transitions.\n\t\t\telse {\n\t\t\t\tconst { signal } = current.controller\n\t\t\t\tdelayMs(errorWindow, signal)\n\t\t\t\t\t.catch(() => {})\n\t\t\t\t\t.finally(() => history.delete(request))\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction calculateFailureRate(): number {\n\t\tlet failures = 0\n\t\tlet total = 0\n\t\tfor (const { status } of state.history.values()) {\n\t\t\tif (status === \"rejected\") failures++\n\t\t\tif (status !== \"pending\") total++\n\t\t}\n\t\tif (!total || total < minimumCandidates) return 0\n\t\treturn failures / total\n\t}\n\n\tfunction transitionTo(\n\t\ttoStatus: StateName,\n\t\tfailureCause?: unknown,\n\t): CircuitInternalState {\n\t\tassertTransition(state.status, toStatus)\n\t\tstate.controller.abort()\n\t\treturn (state = createState(toStatus, failureCause))\n\t}\n\n\tasync function transitionToOpen(error: CircuitError): Promise<void> {\n\t\t// Race guard: a concurrent failure may have already changed state.\n\t\tif (state.status !== \"closed\" && state.status !== \"halfOpen\") return\n\n\t\tconst cause = error.cause ?? error\n\t\tconst nextState = transitionTo(\"open\", cause)\n\t\tif (onOpen) setImmediate(onOpen, cause)\n\n\t\tconst { signal } = nextState.controller\n\t\tawait delayMs(resetAfter, signal)\n\t\tif (state === nextState) transitionToHalfOpen()\n\t}\n\n\tfunction transitionToHalfOpen(): void {\n\t\ttransitionTo(\"halfOpen\")\n\t\tif (onHalfOpen) setImmediate(onHalfOpen)\n\t}\n\n\tfunction transitionToClosed(): void {\n\t\ttransitionTo(\"closed\")\n\t\tif (onClose) setImmediate(onClose)\n\t}\n\n\tfunction guardIsCurrent(\n\t\tcurrent: CircuitInternalState,\n\t\terror: unknown,\n\t): error is CircuitError {\n\t\tif (!(error instanceof CircuitError)) throw error\n\t\t// Transient errors shouldn't affect the circuit breaker's state. Re-throw\n\t\t// the original cause of the error.\n\t\tif (error.isTransient) throw error.cause\n\n\t\t// If the circuit breaker was disposed mid-flight, surface the underlying\n\t\t// cause of the in-flight call rather than the dispose error.\n\t\tif (state.status === \"disposed\")\n\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\tthrow error.cause ?? new CircuitError(\"DISPOSED\")\n\n\t\t// If the circuit breaker transitioned states, try again.\n\t\treturn state === current\n\t}\n\n\tasync function protectedFn(...args: Args): Promise<Ret> {\n\t\tlet lastError: CircuitError | undefined\n\t\tlet retries = 0\n\t\tdo {\n\t\t\tconst current = state\n\n\t\t\t// Closed: Normal Operation\n\t\t\tif (current.status === \"closed\") {\n\t\t\t\ttry {\n\t\t\t\t\treturn await tryCall(current, args)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (guardIsCurrent(current, error)) {\n\t\t\t\t\t\tlastError = error\n\t\t\t\t\t\t// Determine if the failure rate should open the circuit.\n\t\t\t\t\t\tconst rate = (current.failureRate = calculateFailureRate())\n\t\t\t\t\t\tif (rate > errorThreshold) transitionToOpen(error).catch(noop)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Half-Open: Execute trial calls until we have enough candidates.\n\t\t\telse if (\n\t\t\t\tcurrent.status === \"halfOpen\" &&\n\t\t\t\tcurrent.history.size < minimumCandidates\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\treturn await tryCall(current, args)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (guardIsCurrent(current, error)) lastError = error\n\t\t\t\t} finally {\n\t\t\t\t\t// Do nothing until we have enough candidates to make a decision.\n\t\t\t\t\tif (state === current && current.history.size >= minimumCandidates) {\n\t\t\t\t\t\tconst rate = (current.failureRate = calculateFailureRate())\n\t\t\t\t\t\t// Determine if the failure rate should re-open the circuit or\n\t\t\t\t\t\t// if it is healthy enough to close it again.\n\t\t\t\t\t\tif (rate > errorThreshold && lastError)\n\t\t\t\t\t\t\ttransitionToOpen(lastError).catch(noop)\n\t\t\t\t\t\telse if (rate <= errorThreshold) transitionToClosed()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Open: Skip calls and immediately return fallback if available.\n\t\t\telse if (current.status === \"open\" || current.status === \"halfOpen\") {\n\t\t\t\tif (!fallback) {\n\t\t\t\t\tthrow (\n\t\t\t\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\t\t\t\tcurrent.failureCause ??\n\t\t\t\t\t\tnew CircuitError(\"OPEN\", { cause: current.failureCause })\n\t\t\t\t\t)\n\t\t\t\t}\n\t\t\t\treturn await fallback(...args)\n\t\t\t}\n\n\t\t\t// Disposed: Reject all calls with dispose error.\n\t\t\telse throw current.failureCause\n\t\t} while (\n\t\t\tawait shouldRetry({\n\t\t\t\tretries: ++retries,\n\t\t\t\tlastError,\n\t\t\t\tretryDelay,\n\t\t\t\tretryLimit,\n\t\t\t\tretryTest,\n\t\t\t\tsignal: state.controller.signal,\n\t\t\t})\n\t\t)\n\t\tthrow new Error(\"unknown error in circuit breaker retry logic\")\n\t}\n\n\tfunction dispose(disposeMessage = \"ERR_CIRCUIT_BREAKER_DISPOSED\"): void {\n\t\tif (state.status === \"disposed\") return\n\t\ttransitionTo(\"disposed\", new ReferenceError(disposeMessage))\n\t\tmain[Symbol.dispose]?.()\n\t}\n\n\tconst wrapped = protectedFn as CircuitBreakerProtectedFn<Ret, Args>\n\twrapped[Symbol.dispose] = () => dispose()\n\twrapped.dispose = dispose\n\twrapped.getFailureRate = () => state.failureRate\n\twrapped.getLatestError = () => state.failureCause\n\twrapped.getState = () => state.status\n\n\treturn wrapped\n}\n","import { withRetry as internalWithRetry } from \"./retry.js\"\nimport { withTimeout as internalWithTimeout } from \"./timeout.js\"\nimport type { StateName } from \"./types.js\"\n\nexport { useExponentialBackoff, useFibonacciBackoff } from \"./backoff.js\"\nexport { createCircuitBreaker } from \"./circuit-breaker.js\"\n\nexport type {\n\tCircuitBreakerOptions,\n\tCircuitBreakerProtectedFn,\n\tMainFn,\n\tRetryOptions,\n\tStateName,\n} from \"./types.js\"\nexport { delayMs } from \"./util.js\"\n\n// =============================================================================\n// Deprecated API properties\nimport { deprecated } from \"./util.js\"\n\n/** @deprecated Use `StateName` instead. */\nexport type CircuitState = StateName\n\n/** @deprecated Use `retryLimit`, `retryDelay`, and `retryTest` options on `createCircuitBreaker` instead. */\nexport const withRetry = deprecated(\n\tinternalWithRetry,\n\t\"withRetry\",\n\t\"Use `retryLimit`, `retryDelay`, and `retryTest` options on `createCircuitBreaker` instead.\",\n)\n\n/** @deprecated Use `options.timeout` on `createCircuitBreaker` instead. */\nexport const withTimeout = deprecated(\n\tinternalWithTimeout,\n\t\"withTimeout\",\n\t\"Use `options.timeout` on`createCircuitBreaker` instead.\",\n)\n"],"names":["withRetry","withTimeout","internalWithRetry","internalWithTimeout"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.mjs","sources":["../lib/util.ts","../lib/backoff.ts","../lib/circuit-error.ts","../lib/options.ts","../lib/circuit-breaker.ts"],"sourcesContent":["\n/**\n * Returns a promise which rejects when the abort signal is triggered or\n * resolves when the promise is fulfilled.\n */\nexport const abortable = <T>(\n\tsignal: AbortSignal,\n\tpending: PromiseLike<T>,\n): Promise<T> =>\n\tnew Promise((resolve, reject) => {\n\t\tsignal.throwIfAborted()\n\n\t\tconst onAbort = () => reject(signal.reason)\n\t\tsignal.addEventListener(\"abort\", onAbort, { once: true })\n\n\t\tPromise.resolve(pending)\n\t\t\t.finally(() => signal.removeEventListener(\"abort\", onAbort))\n\t\t\t.then(resolve, reject)\n\t})\n\n/**\n * Asserts that the given value is truthy. If not, throws a `TypeError`.\n */\nexport function assert(value: unknown, message?: string): asserts value {\n\tif (!value) throw new TypeError(message)\n}\n\n/**\n * Returns a promise that resolves after the specified number of milliseconds.\n */\nexport const delayMs = (ms: number, signal?: AbortSignal): Promise<void> => {\n\tif (!Number.isFinite(ms) || ms < 0) {\n\t\tthrow new RangeError(\n\t\t\t`\"ms\" must be a finite, non-negative number (received ${ms})`,\n\t\t)\n\t}\n\n\treturn signal\n\t\t? new Promise((resolve, reject) => {\n\t\t\t\tsignal.throwIfAborted()\n\n\t\t\t\tconst timer = setTimeout(() => {\n\t\t\t\t\tsignal.removeEventListener(\"abort\", onAbort)\n\t\t\t\t\tresolve()\n\t\t\t\t}, ms)\n\n\t\t\t\tconst onAbort = () => {\n\t\t\t\t\tclearTimeout(timer)\n\t\t\t\t\treject(signal.reason)\n\t\t\t\t}\n\n\t\t\t\tsignal.addEventListener(\"abort\", onAbort, { once: true })\n\t\t\t})\n\t\t: new Promise((next) => setTimeout(next, ms))\n}\n\nexport const noop: (...args: unknown[]) => void = () => {}\n\n/**\n * Polyfill for `Promise.try()`\n */\nexport function promiseTry<T>(fn: () => T): Promise<T> {\n\ttry {\n\t\treturn Promise.resolve(fn())\n\t} catch (error) {\n\t\treturn Promise.reject(error)\n\t}\n}\n\n\n","import type { RetryDelayFn } from \"./types.js\"\nimport { delayMs } from \"./util.js\"\n\n/**\n * Creates an exponential backoff strategy for retry delays.\n * Delay grows as 2^(attempt-2) seconds, capped at maxSeconds.\n *\n * The sequence is: 1s, 2s, 4s, 8s, 16s, 32s, etc.\n *\n * @example\n * ```ts\n * const backoff = useExponentialBackoff(30)\n * await backoff(2) // waits 1 second\n * await backoff(3) // waits 2 seconds\n * await backoff(10) // waits 30 seconds (capped)\n * ```\n */\nexport function useExponentialBackoff(maxSeconds: number): RetryDelayFn {\n\treturn function exponentialBackoff(attempt, signal) {\n\t\tconst num = Math.max(attempt - 2, 0)\n\t\tconst delay = Math.min(2 ** num, maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n\nconst sqrt5 = /* @__PURE__ */ Math.sqrt(5)\n/**\n * Binet's formula for calculating Fibonacci numbers in constant time.\n * @see https://en.wikipedia.org/wiki/Fibonacci_sequence#Closed-form_expression\n */\nconst binet = (n: number) =>\n\tMath.round(((1 + sqrt5) ** n - (1 - sqrt5) ** n) / (2 ** n * sqrt5))\n\n/**\n * Creates a Fibonacci backoff strategy for retry delays. It is more gradual\n * than exponential backoff, useful for more aggressive retry patterns.\n *\n * The sequence is: 1s, 2s, 3s, 5s, 8s, 13s, etc.\n *\n * @example\n * ```ts\n * const backoff = useFibonacciBackoff(60)\n * await backoff(2) // waits 1 second\n * await backoff(5) // waits 5 seconds\n * await backoff(10) // waits 55 seconds\n * ```\n */\nexport function useFibonacciBackoff(maxSeconds: number): RetryDelayFn {\n\treturn function fibonacciBackoff(attempt, signal) {\n\t\tconst delay = Math.min(binet(attempt), maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n","export class CircuitError extends Error {\n\tisTransient: boolean\n\tconstructor(\n\t\tmessage: string,\n\t\toptions?: { cause?: unknown; isTransient?: boolean },\n\t) {\n\t\tsuper(`ERR_CIRCUIT_BREAKER_${message}`, options)\n\t\tthis.isTransient = options?.isTransient ?? false\n\t}\n}\n","import type { AnyFn, CircuitBreakerOptions } from \"./types.js\"\nimport { assert } from \"./util.js\"\n\nlet warnedErrorIsFailure = false\n\nexport function parseOptions<Fallback extends AnyFn>(\n\toptions: CircuitBreakerOptions<Fallback>,\n) {\n\tconst {\n\t\terrorIsTransient = options.errorIsFailure ?? (() => false),\n\t\terrorThreshold = 0,\n\t\terrorWindow = 10_000,\n\t\tfallback,\n\t\tminimumCandidates = 1,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter = 30_000,\n\t\tretryDelay = 0,\n\t\tretryLimit = Infinity,\n\t\tretryTest = () => true,\n\t\ttimeout = 0,\n\t} = options\n\n\tif (\n\t\t\"errorIsFailure\" in options &&\n\t\t!(\"errorIsTransient\" in options) &&\n\t\t!warnedErrorIsFailure\n\t) {\n\t\twarnedErrorIsFailure = true\n\t\tconsole.warn(\n\t\t\t'breaker-box: \"errorIsFailure\" is deprecated. Use \"errorIsTransient\" instead.',\n\t\t)\n\t}\n\n\t// errorIsTransient\n\tassert(\n\t\ttypeof errorIsTransient === \"function\",\n\t\t`\"errorIsTransient\" must be a function (received ${typeof errorIsTransient})`,\n\t)\n\n\t// errorThreshold\n\tassert(\n\t\terrorThreshold >= 0 && errorThreshold <= 1,\n\t\t`\"errorThreshold\" must be between 0 and 1 (received ${errorThreshold})`,\n\t)\n\n\t// errorWindow\n\tassert(\n\t\terrorWindow >= 1_000,\n\t\t`\"errorWindow\" must be milliseconds of at least 1 second (received ${errorWindow})`,\n\t)\n\n\t// (optional) fallback\n\tassert(\n\t\t!fallback || typeof fallback === \"function\",\n\t\t`\"fallback\" must be a function (received ${typeof fallback})`,\n\t)\n\n\t// minimumCandidates\n\tassert(\n\t\tminimumCandidates >= 1,\n\t\t`\"minimumCandidates\" must be greater than 0 (received ${minimumCandidates})`,\n\t)\n\n\t// (optional) onClose\n\tassert(\n\t\t!onClose || typeof onClose === \"function\",\n\t\t`\"onClose\" must be a function (received ${typeof onClose})`,\n\t)\n\n\t// (optional) onHalfOpen\n\tassert(\n\t\t!onHalfOpen || typeof onHalfOpen === \"function\",\n\t\t`\"onHalfOpen\" must be a function (received ${typeof onHalfOpen})`,\n\t)\n\n\t// (optional) onOpen\n\tassert(\n\t\t!onOpen || typeof onOpen === \"function\",\n\t\t`\"onOpen\" must be a function (received ${typeof onOpen})`,\n\t)\n\n\t// resetAfter\n\tassert(\n\t\tresetAfter >= 1_000,\n\t\t`\"resetAfter\" must be milliseconds of at least 1 second (received ${resetAfter})`,\n\t)\n\tassert(\n\t\tresetAfter >= errorWindow,\n\t\t`\"resetAfter\" must be greater than or equal to \"errorWindow\" (received ${resetAfter}, expected >= ${errorWindow})`,\n\t)\n\n\t// retryDelay\n\tassert(\n\t\ttypeof retryDelay === \"function\" ||\n\t\t\t(typeof retryDelay === \"number\" &&\n\t\t\t\tretryDelay >= 0 &&\n\t\t\t\tNumber.isFinite(retryDelay)),\n\t\t`\"retryDelay\" must be a function or a finite, non-negative number (received ${typeof retryDelay})`,\n\t)\n\n\t// retryLimit\n\tassert(\n\t\ttypeof retryLimit === \"number\" && retryLimit >= 1,\n\t\t`\"retryLimit\" must be greater than 0 (received ${retryLimit})`,\n\t)\n\n\t// retryTest\n\tassert(\n\t\ttypeof retryTest === \"function\",\n\t\t`\"retryTest\" must be a function (received ${typeof retryTest})`,\n\t)\n\n\t// timeout\n\tassert(\n\t\tNumber.isFinite(timeout) && timeout >= 0,\n\t\t`\"timeout\" must be a finite, non-negative number (received ${timeout})`,\n\t)\n\n\treturn {\n\t\terrorIsTransient,\n\t\terrorThreshold,\n\t\terrorWindow,\n\t\tfallback,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t\tretryDelay,\n\t\tretryLimit,\n\t\tretryTest,\n\t\ttimeout,\n\t}\n}\n","import { parseOptions } from \"./options.js\"\nimport type {\n\tCircuitBreakerOptions,\n\tCircuitBreakerProtectedFn,\n\tHistoryEntry,\n\tHistoryMap,\n\tMainFn,\n\tRetryDelayFn,\n\tStateName,\n} from \"./types.js\"\nimport { CircuitError } from \"./circuit-error.js\"\nimport {\n\tabortable,\n\tassert,\n\tdelayMs,\n\tnoop,\n\tpromiseTry,\n} from \"./util.js\"\n\nasync function shouldContinue(options: {\n\tretries: number\n\tlastError: unknown\n\tretryDelay: number | RetryDelayFn\n\tretryLimit: number\n\tretryTest: (error: unknown) => boolean\n\tsignal: AbortSignal\n}): Promise<true> {\n\tconst { retries, lastError, retryDelay, retryLimit, retryTest, signal } =\n\t\toptions\n\n\tif (retries >= retryLimit)\n\t\tthrow new CircuitError(\"MAX_RETRIES\", { cause: lastError })\n\tif (!retryTest(lastError))\n\t\tthrow new CircuitError(\"NON_RETRYABLE\", { cause: lastError })\n\n\ttry {\n\t\tif (!retryDelay) return true\n\t\telse if (typeof retryDelay === \"number\") await delayMs(retryDelay, signal)\n\t\telse if (typeof retryDelay === \"function\") await retryDelay(retries, signal)\n\t} catch {\n\t\t/* empty */\n\t}\n\n\treturn true\n}\n\nconst validTransitions: Record<StateName, StateName[]> = {\n\tclosed: [\"open\", \"disposed\"],\n\topen: [\"halfOpen\", \"disposed\"],\n\thalfOpen: [\"closed\", \"open\", \"disposed\"],\n\tdisposed: [],\n}\n\nfunction assertTransition(from: StateName, to: StateName): void {\n\tassert(\n\t\tvalidTransitions[from].includes(to),\n\t\t`Invalid transition from ${from} to ${to}`,\n\t)\n}\n\ninterface CircuitInternalState<T extends StateName = StateName> {\n\tcontroller: AbortController\n\tfailureCause: unknown\n\tfailureRate: number\n\thistory: HistoryMap\n\tstatus: T\n}\n\nfunction createState(\n\tstatus: StateName,\n\tfailureCause?: unknown,\n): CircuitInternalState {\n\tconst controller = new AbortController()\n\treturn {\n\t\tcontroller,\n\t\tfailureCause,\n\t\tfailureRate: 0,\n\t\thistory: new Map(),\n\t\tstatus,\n\t}\n}\n\n/**\n * Creates a circuit breaker that wraps an async function with failure tracking\n * and automatic fallback behavior.\n *\n * The circuit breaker operates in four states:\n *\n * - `closed`: Normal operation, tracks failures in a sliding window\n * - `open`: Failed state, fallback is used until `resetAfter` milliseconds\n * - `halfOpen`: Testing recovery, allows trial calls\n * - `disposed`: Terminal state, all calls rejected\n *\n * When the failure rate exceeds `errorThreshold` within the `errorWindow`, the\n * circuit opens and rejects calls (using fallback if provided) for `resetAfter`\n * milliseconds. After this period, it transitions to half-open and allows up\n * to `minimumCandidates` concurrent trial calls. If their failure rate stays\n * at or below the threshold, the circuit closes; otherwise it reopens.\n *\n * @example\n * ```ts\n * const protectedFn = createCircuitBreaker(unreliableApiCall, {\n * errorThreshold: 0.5,\n * errorWindow: 10_000,\n * resetAfter: 30_000,\n * fallback: () => cachedResponse,\n * })\n *\n * try {\n * const result = await protectedFn(arg1, arg2)\n * } catch (error) {\n * console.error('Circuit breaker rejected call:', error)\n * }\n *\n * console.log(protectedFn.getState()) // 'closed' | 'open' | 'halfOpen' | 'disposed'\n * protectedFn[Symbol.dispose]() // Clean up timers and resources\n * ```\n */\nexport function createCircuitBreaker<Ret, Args extends unknown[]>(\n\tmain: MainFn<Ret, Args>,\n\toptions: CircuitBreakerOptions<MainFn<Ret, Args>> = {},\n): CircuitBreakerProtectedFn<Ret, Args> {\n\tconst {\n\t\terrorIsTransient,\n\t\terrorThreshold,\n\t\terrorWindow,\n\t\tfallback,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t\tretryDelay,\n\t\tretryLimit,\n\t\tretryTest,\n\t\ttimeout,\n\t} = parseOptions(options)\n\n\tlet state = createState(\"closed\")\n\n\tasync function tryCall(\n\t\tcurrent: CircuitInternalState,\n\t\targs: Args,\n\t): Promise<Ret> {\n\t\tconst { history } = current\n\t\tconst request = promiseTry(() => main(...args))\n\n\t\tlet historyItem: HistoryEntry | undefined = { status: \"pending\" }\n\t\thistory.set(request, historyItem)\n\n\t\ttry {\n\t\t\tconst result =\n\t\t\t\ttimeout > 0\n\t\t\t\t\t? await abortable(AbortSignal.timeout(timeout), request)\n\t\t\t\t\t: await request\n\t\t\thistoryItem.status = \"resolved\"\n\t\t\treturn result\n\t\t} catch (cause) {\n\t\t\thistoryItem.status = \"rejected\"\n\t\t\t// Drop this request if it's a transient error that shouldn't count\n\t\t\t// towards the failure rate\n\t\t\tconst isTransient = errorIsTransient(cause)\n\t\t\tif (isTransient) historyItem = undefined\n\n\t\t\t// Wrap the error in a CircuitError to provide additional context and\n\t\t\t// control flow handling.\n\t\t\tthrow new CircuitError(\"CALL_FAILURE\", { cause, isTransient })\n\t\t} finally {\n\t\t\t// Remove the request if it was a transient failure, or if it's stale.\n\t\t\tif (!historyItem || state !== current) history.delete(request)\n\t\t\t// Keep the request in history until the end of the error window, or until\n\t\t\t// the circuit transitions.\n\t\t\telse {\n\t\t\t\tconst { signal } = current.controller\n\t\t\t\tdelayMs(errorWindow, signal)\n\t\t\t\t\t.catch(() => {})\n\t\t\t\t\t.finally(() => history.delete(request))\n\t\t\t}\n\t\t}\n\t}\n\n\tfunction calculateFailureRate(): number {\n\t\tlet failures = 0\n\t\tlet total = 0\n\t\tfor (const { status } of state.history.values()) {\n\t\t\tif (status === \"rejected\") failures++\n\t\t\tif (status !== \"pending\") total++\n\t\t}\n\t\tif (!total || total < minimumCandidates) return 0\n\t\treturn failures / total\n\t}\n\n\tfunction transitionTo(\n\t\ttoStatus: StateName,\n\t\tfailureCause?: unknown,\n\t): CircuitInternalState {\n\t\tassertTransition(state.status, toStatus)\n\t\tstate.controller.abort()\n\t\treturn (state = createState(toStatus, failureCause))\n\t}\n\n\tasync function transitionToOpen(error: CircuitError): Promise<void> {\n\t\t// Race guard: a concurrent failure may have already changed state.\n\t\tif (state.status !== \"closed\" && state.status !== \"halfOpen\") return\n\n\t\tconst cause = error.cause ?? error\n\t\tconst nextState = transitionTo(\"open\", cause)\n\t\tif (onOpen) setImmediate(onOpen, cause)\n\n\t\tconst { signal } = nextState.controller\n\t\tawait delayMs(resetAfter, signal)\n\t\tif (state === nextState) transitionToHalfOpen()\n\t}\n\n\tfunction transitionToHalfOpen(): void {\n\t\ttransitionTo(\"halfOpen\", state.failureCause)\n\t\tif (onHalfOpen) setImmediate(onHalfOpen)\n\t}\n\n\tfunction transitionToClosed(): void {\n\t\ttransitionTo(\"closed\")\n\t\tif (onClose) setImmediate(onClose)\n\t}\n\n\tfunction guardIsCurrent(\n\t\tcurrent: CircuitInternalState,\n\t\terror: unknown,\n\t): error is CircuitError {\n\t\tif (!(error instanceof CircuitError)) throw error\n\t\t// Transient errors shouldn't affect the circuit breaker's state. Re-throw\n\t\t// the original cause of the error.\n\t\tif (error.isTransient) throw error.cause\n\n\t\t// If the circuit breaker was disposed mid-flight, surface the underlying\n\t\t// cause of the in-flight call rather than the dispose error.\n\t\tif (state.status === \"disposed\")\n\t\t\t// eslint-disable-next-line @typescript-eslint/only-throw-error\n\t\t\tthrow error.cause ?? new CircuitError(\"DISPOSED\")\n\n\t\t// If the circuit breaker transitioned states, try again.\n\t\treturn state === current\n\t}\n\n\tasync function protectedFn(...args: Args): Promise<Ret> {\n\t\tlet lastError: CircuitError | undefined\n\t\tlet retries = 0\n\t\tdo {\n\t\t\tconst current = state\n\t\t\tlastError = undefined\n\n\t\t\t// Closed: Normal Operation\n\t\t\tif (current.status === \"closed\") {\n\t\t\t\ttry {\n\t\t\t\t\treturn await tryCall(current, args)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (guardIsCurrent(current, error)) {\n\t\t\t\t\t\tlastError = error\n\t\t\t\t\t\t// Determine if the failure rate should open the circuit.\n\t\t\t\t\t\tconst rate = (current.failureRate = calculateFailureRate())\n\t\t\t\t\t\tif (rate > errorThreshold) transitionToOpen(error).catch(noop)\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Half-Open: Execute trial calls until we have enough candidates.\n\t\t\telse if (\n\t\t\t\tcurrent.status === \"halfOpen\" &&\n\t\t\t\tcurrent.history.size < minimumCandidates\n\t\t\t) {\n\t\t\t\ttry {\n\t\t\t\t\treturn await tryCall(current, args)\n\t\t\t\t} catch (error) {\n\t\t\t\t\tif (guardIsCurrent(current, error)) lastError = error\n\t\t\t\t\tbreak\n\t\t\t\t} finally {\n\t\t\t\t\t// Do nothing until we have enough candidates to make a decision.\n\t\t\t\t\tif (state === current && current.history.size >= minimumCandidates) {\n\t\t\t\t\t\tconst rate = (current.failureRate = calculateFailureRate())\n\t\t\t\t\t\t// Determine if the failure rate should re-open the circuit or\n\t\t\t\t\t\t// if it is healthy enough to close it again.\n\t\t\t\t\t\tif (rate > errorThreshold && lastError)\n\t\t\t\t\t\t\ttransitionToOpen(lastError).catch(noop)\n\t\t\t\t\t\telse if (rate <= errorThreshold) transitionToClosed()\n\t\t\t\t\t}\n\t\t\t\t}\n\t\t\t}\n\n\t\t\t// Open: Skip calls and immediately return fallback if available.\n\t\t\telse if (current.status === \"open\" || current.status === \"halfOpen\") {\n\t\t\t\tbreak\n\t\t\t}\n\n\t\t\t// Disposed: Reject all calls with dispose error.\n\t\t\telse throw current.failureCause\n\t\t} while (\n\t\t\tawait shouldContinue({\n\t\t\t\tretries: ++retries,\n\t\t\t\tlastError: lastError?.cause ?? lastError,\n\t\t\t\tretryDelay,\n\t\t\t\tretryLimit,\n\t\t\t\tretryTest,\n\t\t\t\tsignal: state.controller.signal,\n\t\t\t}).catch((error: CircuitError) => {\n\t\t\t\tlastError = error\n\t\t\t\treturn false\n\t\t\t})\n\t\t)\n\n\t\tif (!fallback) throw lastError ?? state.failureCause\n\t\treturn fallback(...args)\n\t}\n\n\tfunction dispose(disposeMessage = \"ERR_CIRCUIT_BREAKER_DISPOSED\"): void {\n\t\tif (state.status === \"disposed\") return\n\t\ttransitionTo(\"disposed\", new ReferenceError(disposeMessage))\n\t\tmain[Symbol.dispose]?.()\n\t}\n\n\tconst wrapped = protectedFn as CircuitBreakerProtectedFn<Ret, Args>\n\twrapped[Symbol.dispose] = () => dispose()\n\twrapped.dispose = dispose\n\twrapped.getFailureRate = () => state.failureRate\n\twrapped.getLatestError = () => state.failureCause\n\twrapped.getState = () => state.status\n\n\treturn wrapped\n}\n"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "breaker-box",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "9.0.0",
|
|
4
4
|
"description": "A zero-dependency circuit breaker implementation for Node.js",
|
|
5
5
|
"repository": {
|
|
6
6
|
"type": "git",
|
|
@@ -11,12 +11,12 @@
|
|
|
11
11
|
"types": "./dist/index.d.cts",
|
|
12
12
|
"exports": {
|
|
13
13
|
"require": {
|
|
14
|
-
"
|
|
15
|
-
"
|
|
14
|
+
"types": "./dist/index.d.cts",
|
|
15
|
+
"default": "./dist/index.cjs"
|
|
16
16
|
},
|
|
17
17
|
"import": {
|
|
18
|
-
"
|
|
19
|
-
"
|
|
18
|
+
"types": "./dist/index.d.mts",
|
|
19
|
+
"default": "./dist/index.mjs"
|
|
20
20
|
}
|
|
21
21
|
},
|
|
22
22
|
"files": [
|