breaker-box 6.0.0 → 7.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/dist/index.cjs CHANGED
@@ -1,8 +1,16 @@
1
1
  'use strict';
2
2
 
3
+ const disposeKey = Symbol("disposeKey");
3
4
  function assert(value, message) {
4
5
  if (!value) throw new TypeError(message);
5
6
  }
7
+ function createDisposable(main, controller) {
8
+ return (disposeMessage = "ERR_CIRCUIT_BREAKER_DISPOSED") => {
9
+ const reason = new ReferenceError(disposeMessage);
10
+ main[disposeKey]?.(disposeMessage);
11
+ controller.abort(reason);
12
+ };
13
+ }
6
14
  /* v8 ignore next -- @preserve */
7
15
  const assertNever = (val, msg = "Unexpected value") => {
8
16
  throw new TypeError(`${msg}: ${val}`);
@@ -89,6 +97,7 @@ function parseOptions(options) {
89
97
  errorIsFailure,
90
98
  errorThreshold,
91
99
  errorWindow,
100
+ fallback,
92
101
  minimumCandidates,
93
102
  onClose,
94
103
  onHalfOpen,
@@ -97,7 +106,172 @@ function parseOptions(options) {
97
106
  };
98
107
  }
99
108
 
100
- const disposeKey = Symbol("disposeKey");
109
+ const validTransitions = {
110
+ closed: ["open", "disposed"],
111
+ open: ["halfOpen", "disposed"],
112
+ halfOpen: ["closed", "open", "disposed"],
113
+ disposed: []
114
+ };
115
+ function assertTransition(from, to) {
116
+ assert(
117
+ validTransitions[from].includes(to),
118
+ `Invalid transition from ${from} to ${to}`
119
+ );
120
+ }
121
+ function createCircuitBreaker(main, options = {}) {
122
+ const {
123
+ errorIsFailure,
124
+ errorThreshold,
125
+ errorWindow,
126
+ fallback: userFallback,
127
+ minimumCandidates,
128
+ onClose,
129
+ onHalfOpen,
130
+ onOpen,
131
+ resetAfter
132
+ } = parseOptions(options);
133
+ const controller = new AbortController();
134
+ const history = /* @__PURE__ */ new Map();
135
+ const signal = controller.signal;
136
+ let failureCause;
137
+ let failureRate = 0;
138
+ let fallback = userFallback || (() => Promise.reject(
139
+ new Error("ERR_CIRCUIT_BREAKER_OPEN", { cause: failureCause })
140
+ ));
141
+ let halfOpenPending;
142
+ let resetTimer;
143
+ let state = "closed";
144
+ function calculateFailureRate() {
145
+ let failures = 0;
146
+ let total = 0;
147
+ for (const { status } of history.values()) {
148
+ if (status === "rejected") failures++;
149
+ if (status !== "pending") total++;
150
+ }
151
+ if (!total || total < minimumCandidates) return 0;
152
+ return failures / total;
153
+ }
154
+ function transition(toState, options2 = {}) {
155
+ assertTransition(state, toState);
156
+ state = toState;
157
+ switch (toState) {
158
+ case "closed":
159
+ clearTimeout(resetTimer);
160
+ failureCause = void 0;
161
+ resetTimer = void 0;
162
+ if (onClose) setImmediate(onClose);
163
+ break;
164
+ case "open":
165
+ clearTimeout(resetTimer);
166
+ failureCause = options2.cause;
167
+ resetTimer = setTimeout(() => transition("halfOpen"), resetAfter);
168
+ if (onOpen) setImmediate(onOpen, failureCause);
169
+ break;
170
+ case "halfOpen":
171
+ halfOpenPending = void 0;
172
+ if (onHalfOpen) setImmediate(onHalfOpen);
173
+ break;
174
+ case "disposed":
175
+ failureCause = options2.cause;
176
+ assert(failureCause instanceof Error, "dispose cause must be an Error");
177
+ controller.abort(failureCause);
178
+ main[disposeKey]?.(failureCause.message);
179
+ history.forEach((entry) => clearTimeout(entry.timer));
180
+ history.clear();
181
+ halfOpenPending = void 0;
182
+ failureRate = 0;
183
+ fallback = void 0;
184
+ clearTimeout(resetTimer);
185
+ resetTimer = void 0;
186
+ break;
187
+ default:
188
+ assertNever(toState);
189
+ }
190
+ }
191
+ function createHistoryItem(pending) {
192
+ const entry = { status: "pending", timer: void 0 };
193
+ const teardown = () => {
194
+ clearTimeout(entry.timer);
195
+ history.delete(pending);
196
+ signal.removeEventListener("abort", teardown);
197
+ };
198
+ signal.addEventListener("abort", teardown, { once: true });
199
+ const settle = (value) => {
200
+ if (signal.aborted) return 0;
201
+ entry.status = value;
202
+ entry.timer = setTimeout(teardown, errorWindow);
203
+ return calculateFailureRate();
204
+ };
205
+ history.set(pending, entry);
206
+ return { pending, settle, teardown };
207
+ }
208
+ function executeClosed(args) {
209
+ const { pending, settle, teardown } = createHistoryItem(main(...args));
210
+ return pending.then(
211
+ (result) => {
212
+ failureRate = settle("resolved");
213
+ return result;
214
+ },
215
+ (cause) => {
216
+ if (signal.aborted || errorIsFailure(cause)) {
217
+ teardown();
218
+ throw cause;
219
+ }
220
+ failureRate = settle("rejected");
221
+ if (failureRate > errorThreshold) {
222
+ transition("open", { cause });
223
+ }
224
+ return fallback(...args);
225
+ }
226
+ );
227
+ }
228
+ function executeOpen(args) {
229
+ return fallback(...args);
230
+ }
231
+ function executeHalfOpen(args) {
232
+ if (halfOpenPending) return fallback(...args);
233
+ return (halfOpenPending = main(...args)).finally(() => halfOpenPending = void 0).then(
234
+ (result) => {
235
+ if (signal.aborted) return result;
236
+ transition("closed");
237
+ return result;
238
+ },
239
+ (cause) => {
240
+ if (signal.aborted || errorIsFailure(cause)) throw cause;
241
+ transition("open", { cause });
242
+ return fallback(...args);
243
+ }
244
+ );
245
+ }
246
+ function executeDisposed() {
247
+ return Promise.reject(failureCause);
248
+ }
249
+ function execute(...args) {
250
+ switch (state) {
251
+ case "closed":
252
+ return executeClosed(args);
253
+ case "open":
254
+ return executeOpen(args);
255
+ case "halfOpen":
256
+ return executeHalfOpen(args);
257
+ case "disposed":
258
+ return executeDisposed();
259
+ default:
260
+ return assertNever(state);
261
+ }
262
+ }
263
+ const dispose = (disposeMessage = "ERR_CIRCUIT_BREAKER_DISPOSED") => {
264
+ if (state === "disposed") return;
265
+ transition("disposed", { cause: new ReferenceError(disposeMessage) });
266
+ };
267
+ return Object.assign(execute, {
268
+ dispose,
269
+ getFailureRate: () => failureRate,
270
+ getLatestError: () => failureCause,
271
+ getState: () => state,
272
+ [Symbol.dispose]: () => dispose()
273
+ });
274
+ }
101
275
 
102
276
  function useExponentialBackoff(maxSeconds) {
103
277
  return function exponentialBackoff(attempt, signal) {
@@ -114,6 +288,7 @@ function useFibonacciBackoff(maxSeconds) {
114
288
  return delayMs(delay * 1e3, signal);
115
289
  };
116
290
  }
291
+
117
292
  function withRetry(main, options = {}) {
118
293
  const {
119
294
  shouldRetry = () => true,
@@ -141,13 +316,10 @@ function withRetry(main, options = {}) {
141
316
  }
142
317
  }
143
318
  return Object.assign(withRetryFunction, {
144
- [disposeKey]: (disposeMessage = "ERR_CIRCUIT_BREAKER_DISPOSED") => {
145
- const reason = new ReferenceError(disposeMessage);
146
- main[disposeKey]?.(disposeMessage);
147
- controller.abort(reason);
148
- }
319
+ [disposeKey]: createDisposable(main, controller)
149
320
  });
150
321
  }
322
+
151
323
  function withTimeout(main, timeoutMs, timeoutMessage = "ERR_CIRCUIT_BREAKER_TIMEOUT") {
152
324
  const error = new Error(timeoutMessage);
153
325
  const controller = new AbortController();
@@ -159,138 +331,7 @@ function withTimeout(main, timeoutMs, timeoutMessage = "ERR_CIRCUIT_BREAKER_TIME
159
331
  });
160
332
  }
161
333
  return Object.assign(withTimeoutFunction, {
162
- [disposeKey]: (disposeMessage = "ERR_CIRCUIT_BREAKER_DISPOSED") => {
163
- const reason = new ReferenceError(disposeMessage);
164
- main[disposeKey]?.(disposeMessage);
165
- controller.abort(reason);
166
- }
167
- });
168
- }
169
-
170
- function createCircuitBreaker(main, options = {}) {
171
- const {
172
- errorIsFailure,
173
- errorThreshold,
174
- errorWindow,
175
- minimumCandidates,
176
- onClose,
177
- onHalfOpen,
178
- onOpen,
179
- resetAfter
180
- } = parseOptions(options);
181
- const controller = new AbortController();
182
- const history = /* @__PURE__ */ new Map();
183
- const signal = controller.signal;
184
- let failureCause;
185
- let failureRate = 0;
186
- let fallback = options.fallback || (() => Promise.reject(failureCause));
187
- let halfOpenPending;
188
- let resetTimer;
189
- let state = "closed";
190
- function clearFailure() {
191
- failureCause = void 0;
192
- clearTimeout(resetTimer);
193
- resetTimer = void 0;
194
- }
195
- function closeCircuit() {
196
- state = "closed";
197
- clearFailure();
198
- if (onClose) setImmediate(onClose);
199
- }
200
- function calculateFailureRate() {
201
- let failures = 0;
202
- let total = 0;
203
- for (const { status } of history.values()) {
204
- if (status === "rejected") failures++;
205
- if (status !== "pending") total++;
206
- }
207
- if (!total || total < minimumCandidates) return 0;
208
- return failures / total;
209
- }
210
- function openCircuit(cause) {
211
- state = "open";
212
- failureCause = cause;
213
- clearTimeout(resetTimer);
214
- resetTimer = setTimeout(() => halfOpenCircuit(), resetAfter);
215
- if (onOpen) setImmediate(onOpen, cause);
216
- }
217
- function halfOpenCircuit() {
218
- state = "halfOpen";
219
- halfOpenPending = void 0;
220
- if (onHalfOpen) setImmediate(onHalfOpen);
221
- }
222
- function createHistoryItem(pending) {
223
- const entry = { status: "pending", timer: void 0 };
224
- const teardown = () => {
225
- clearTimeout(entry.timer);
226
- history.delete(pending);
227
- signal.removeEventListener("abort", teardown);
228
- };
229
- signal.addEventListener("abort", teardown, { once: true });
230
- const settle = (value) => {
231
- if (signal.aborted) return 0;
232
- entry.status = value;
233
- entry.timer = setTimeout(teardown, errorWindow);
234
- return failureRate = calculateFailureRate();
235
- };
236
- history.set(pending, entry);
237
- return { pending, settle, teardown };
238
- }
239
- function execute(...args) {
240
- if (state === "closed") {
241
- const { pending, settle, teardown } = createHistoryItem(main(...args));
242
- return pending.then(
243
- (result) => {
244
- settle("resolved");
245
- return result;
246
- },
247
- (cause) => {
248
- if (signal.aborted || errorIsFailure(cause)) {
249
- teardown();
250
- throw cause;
251
- }
252
- settle("rejected");
253
- if (failureRate > errorThreshold) openCircuit(cause);
254
- return fallback(...args);
255
- }
256
- );
257
- } else if (state === "open" || halfOpenPending) {
258
- return fallback(...args);
259
- } else if (state === "halfOpen") {
260
- return (halfOpenPending = main(...args)).finally(() => halfOpenPending = void 0).then(
261
- (result) => {
262
- if (signal.aborted) return result;
263
- closeCircuit();
264
- return result;
265
- },
266
- (cause) => {
267
- if (signal.aborted || errorIsFailure(cause)) throw cause;
268
- openCircuit(cause);
269
- return fallback(...args);
270
- }
271
- );
272
- }
273
- /* v8 ignore next -- @preserve */
274
- return assertNever(state);
275
- }
276
- return Object.assign(execute, {
277
- dispose: (disposeMessage = "ERR_CIRCUIT_BREAKER_DISPOSED") => {
278
- /* v8 ignore if -- @preserve */
279
- if (signal.aborted) return;
280
- const reason = new ReferenceError(disposeMessage);
281
- main[disposeKey]?.(disposeMessage);
282
- clearFailure();
283
- halfOpenPending = void 0;
284
- history.forEach((entry) => clearTimeout(entry.timer));
285
- history.clear();
286
- failureRate = 0;
287
- fallback = () => Promise.reject(reason);
288
- state = "open";
289
- controller.abort(reason);
290
- },
291
- getFailureRate: () => failureRate,
292
- getLatestError: () => failureCause,
293
- getState: () => state
334
+ [disposeKey]: createDisposable(main, controller)
294
335
  });
295
336
  }
296
337
 
@@ -1 +1 @@
1
- {"version":3,"file":"index.cjs","sources":["../lib/util.ts","../lib/options.ts","../lib/types.ts","../lib/helpers.ts","../lib/index.ts"],"sourcesContent":["export type AnyFn = (...args: never) => unknown\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 * `[TypeScript]` For exhaustive checks in switch statements or if/else. Add\n * this check to `default` case or final `else` to ensure all possible values\n * have been handled. If a new value is added to the type, TypeScript will\n * throw an error and the editor will underline the `value`.\n */\n/* v8 ignore next -- @preserve */\nexport const assertNever = (val: never, msg = \"Unexpected value\"): never => {\n\tthrow new TypeError(`${msg}: ${val as string}`)\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\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.then(resolve, reject)\n\t\t\t.finally(() => signal.removeEventListener(\"abort\", onAbort))\n\t})\n","import type { CircuitBreakerOptions } from \"./types.js\"\nimport { assert, type AnyFn } 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} = 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\treturn {\n\t\terrorIsFailure,\n\t\terrorThreshold,\n\t\terrorWindow,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t}\n}\n","import type { AnyFn } from \"./util\"\n\n/**\n * Symbol key for internal disposal protocol. Functions implementing this key\n * can be disposed by circuit breaker wrappers.\n */\nexport const disposeKey = Symbol(\"disposeKey\")\n\n/**\n * The three possible states of a circuit breaker.\n *\n * - `closed`: Normal operation, tracking failures\n * - `open`: Failing state, rejecting calls or using fallback\n * - `halfOpen`: Testing recovery with a single trial call\n */\nexport type CircuitState = \"closed\" | \"halfOpen\" | \"open\"\n\n/**\n * Configuration options for circuit breaker behavior.\n */\nexport interface CircuitBreakerOptions<Fallback extends AnyFn = AnyFn> {\n\t/**\n\t * Whether an error should be treated as non-retryable failure. When used and\n\t * when an error is considered a failure, the error will be thrown to the\n\t * caller.\n\t *\n\t * @default () => false // Errors are retryable by default\n\t */\n\terrorIsFailure?: (error: unknown) => boolean\n\n\t/**\n\t * The percentage of errors (as a number between 0 and 1) which must occur\n\t * within the error window before the circuit breaker opens.\n\t *\n\t * @default 0 // Any error opens the circuit\n\t */\n\terrorThreshold?: number\n\n\t/**\n\t * The sliding window of time in milliseconds over which errors are counted.\n\t *\n\t * @default 10_000 // 10 seconds\n\t */\n\terrorWindow?: number\n\n\t/**\n\t * If provided, then all rejected calls to `main` will be forwarded to\n\t * this function instead.\n\t *\n\t * @default undefined // No fallback, errors are propagated\n\t */\n\tfallback?: Fallback\n\n\t/**\n\t * The minimum number of calls that must be made before calculating the\n\t * error rate and determining whether the circuit breaker should open based on\n\t * the `errorThreshold`.\n\t *\n\t * @default 6\n\t */\n\tminimumCandidates?: number\n\n\t/**\n\t * Provide a function to be called when the circuit breaker is closed.\n\t */\n\tonClose?: () => void\n\n\t/**\n\t * Provide a function to be called when the circuit breaker transitions to\n\t * half-open state.\n\t */\n\tonHalfOpen?: () => void\n\n\t/**\n\t * Provide a function to be called when the circuit breaker is opened. It\n\t * receives the error as its only argument.\n\t */\n\tonOpen?: (cause: unknown) => void\n\n\t/**\n\t * The amount of time in milliseconds to wait before transitioning to a\n\t * half-open state.\n\t *\n\t * @default 30_000 // 30 seconds\n\t */\n\tresetAfter?: number\n}\n\n/**\n * A function wrapped with circuit breaker protection. Includes methods for\n * state inspection and resource cleanup.\n */\nexport interface CircuitBreakerProtectedFn<\n\tRet = unknown,\n\tArgs extends readonly unknown[] = readonly [],\n> {\n\t(...args: Args): Promise<Ret>\n\n\t/**\n\t * Free memory and stop timers. All future calls will be rejected with the\n\t * provided message.\n\t *\n\t * @default \"ERR_CIRCUIT_BREAKER_DISPOSED\"\n\t */\n\tdispose(this: void, disposeMessage?: string): void\n\n\t/** Get the current failure rate of the circuit breaker */\n\tgetFailureRate(this: void): number\n\n\t/** Get the last error which triggered the circuit breaker */\n\tgetLatestError(this: void): unknown\n\n\t/** Get the current state of the circuit breaker */\n\tgetState(this: void): CircuitState\n}\n\n/**\n * Tracks the status of a single call within the error window. Contains a timer\n * for automatic cleanup and the current resolution status.\n */\nexport interface HistoryEntry {\n\ttimer: NodeJS.Timeout | undefined\n\tstatus: \"pending\" | \"resolved\" | \"rejected\"\n}\n\n/**\n * Map tracking all in-flight and recent promises within the error window. Used\n * to calculate failure rates for circuit breaker decisions.\n */\nexport type HistoryMap<T = unknown> = Map<Promise<T>, HistoryEntry>\n\n/**\n * The main function signature that can be protected by a circuit breaker. May\n * optionally implement the disposal protocol via the `disposeKey` symbol.\n */\nexport interface MainFn<\n\tRet = unknown,\n\tArgs extends readonly unknown[] = never[],\n> {\n\t(...args: Args): Promise<Ret>\n\n\t[disposeKey]?: (disposeMessage?: string) => void\n}\n","import { disposeKey, type MainFn } from \"./types.js\"\nimport { assert, delayMs, abortable } 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 * @param maxSeconds - Maximum delay in seconds before capping\n * @returns Function accepting attempt number and returning delay promise\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) {\n\treturn function exponentialBackoff(attempt: number, signal?: AbortSignal) {\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.\n * Delay follows the Fibonacci sequence: 1s, 2s, 3s, 5s, 8s, 13s, etc.\n *\n * More gradual than exponential backoff, useful for less aggressive retry patterns.\n *\n * @param maxSeconds - Maximum delay in seconds before capping\n * @returns Function accepting attempt number and returning delay promise\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) {\n\treturn function fibonacciBackoff(attempt: number, signal?: AbortSignal) {\n\t\tconst delay = Math.min(binet(attempt), maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n\nexport interface RetryOptions {\n\t/**\n\t * Whether an error should be treated as non-retryable. When this returns\n\t * true, the error will be thrown immediately without retrying.\n\t *\n\t * @default () => false // All errors are retried\n\t */\n\tshouldRetry?: (error: unknown, attempt: number) => boolean\n\n\t/**\n\t * Maximum number of retries\n\t *\n\t * @default 3\n\t */\n\tmaxAttempts?: number\n\n\t/**\n\t * Function that returns a promise resolving when the next retry should occur.\n\t * Receives the attempt number (starting at 2) and an abort signal.\n\t *\n\t * @default () => Promise.resolve() // Immediate retry\n\t */\n\tretryDelay?: (attempt: number, signal: AbortSignal) => Promise<void>\n}\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> {\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[disposeKey]: (disposeMessage = \"ERR_CIRCUIT_BREAKER_DISPOSED\") => {\n\t\t\tconst reason = new ReferenceError(disposeMessage)\n\t\t\tmain[disposeKey]?.(disposeMessage)\n\t\t\tcontroller.abort(reason)\n\t\t},\n\t})\n}\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> {\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[disposeKey]: (disposeMessage = \"ERR_CIRCUIT_BREAKER_DISPOSED\") => {\n\t\t\tconst reason = new ReferenceError(disposeMessage)\n\t\t\tmain[disposeKey]?.(disposeMessage)\n\t\t\tcontroller.abort(reason)\n\t\t},\n\t})\n}\n","import { parseOptions } from \"./options.js\"\nimport {\n\tdisposeKey,\n\ttype CircuitBreakerOptions,\n\ttype CircuitBreakerProtectedFn,\n\ttype CircuitState,\n\ttype HistoryEntry,\n\ttype HistoryMap,\n\ttype MainFn,\n} from \"./types.js\"\nimport { assertNever } from \"./util.js\"\n\nexport * from \"./helpers.js\"\nexport {\n\ttype CircuitBreakerOptions,\n\ttype CircuitBreakerProtectedFn,\n\ttype CircuitState,\n\ttype MainFn,\n} from \"./types.js\"\nexport { delayMs } from \"./util.js\"\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 three 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 one trial call\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 one\n * trial call. Success closes the circuit; failure reopens it.\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'\n * protectedFn.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\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t} = parseOptions(options)\n\tconst controller = new AbortController()\n\tconst history: HistoryMap = new Map()\n\tconst signal = controller.signal\n\tlet failureCause: unknown\n\tlet failureRate = 0\n\tlet fallback: typeof main =\n\t\toptions.fallback || (() => Promise.reject(failureCause))\n\tlet halfOpenPending: Promise<unknown> | undefined\n\tlet resetTimer: NodeJS.Timeout | undefined\n\tlet state: CircuitState = \"closed\"\n\n\tfunction clearFailure() {\n\t\tfailureCause = undefined\n\t\tclearTimeout(resetTimer)\n\t\tresetTimer = undefined\n\t}\n\n\tfunction closeCircuit() {\n\t\tstate = \"closed\"\n\t\tclearFailure()\n\t\tif (onClose) setImmediate(onClose)\n\t}\n\n\tfunction calculateFailureRate(): number {\n\t\tlet failures = 0\n\t\tlet total = 0\n\t\tfor (const { status } of history.values()) {\n\t\t\tif (status === \"rejected\") failures++\n\t\t\tif (status !== \"pending\") total++\n\t\t}\n\t\t// Don't calculate anything until we have enough data\n\t\tif (!total || total < minimumCandidates) return 0\n\t\treturn failures / total\n\t}\n\n\tfunction openCircuit(cause: unknown) {\n\t\tstate = \"open\"\n\t\tfailureCause = cause\n\t\tclearTimeout(resetTimer)\n\t\tresetTimer = setTimeout(() => halfOpenCircuit(), resetAfter)\n\t\tif (onOpen) setImmediate(onOpen, cause)\n\t}\n\n\tfunction halfOpenCircuit() {\n\t\tstate = \"halfOpen\"\n\t\thalfOpenPending = undefined\n\t\tif (onHalfOpen) setImmediate(onHalfOpen)\n\t}\n\n\tfunction createHistoryItem<T>(pending: Promise<T>) {\n\t\tconst entry: HistoryEntry = { status: \"pending\", timer: undefined }\n\t\tconst teardown = (): void => {\n\t\t\tclearTimeout(entry.timer)\n\t\t\thistory.delete(pending)\n\t\t\tsignal.removeEventListener(\"abort\", teardown)\n\t\t}\n\t\tsignal.addEventListener(\"abort\", teardown, { once: true })\n\t\tconst settle = (value: \"resolved\" | \"rejected\"): number => {\n\t\t\tif (signal.aborted) return 0\n\t\t\tentry.status = value\n\t\t\t// Remove the entry from history when it falls outside of the error window\n\t\t\tentry.timer = setTimeout(teardown, errorWindow)\n\t\t\treturn (failureRate = calculateFailureRate())\n\t\t}\n\t\thistory.set(pending, entry)\n\t\treturn { pending, settle, teardown }\n\t}\n\n\t/**\n\t * Wrap calls to `main` with circuit breaker logic\n\t */\n\tfunction execute(...args: Args): Promise<Ret> {\n\t\t// Normal operation when circuit is closed. If an error occurs, keep track\n\t\t// of the failure count and open the circuit if it exceeds the threshold.\n\t\tif (state === \"closed\") {\n\t\t\tconst { pending, settle, teardown } = createHistoryItem(main(...args))\n\t\t\treturn pending.then(\n\t\t\t\t(result) => {\n\t\t\t\t\tsettle(\"resolved\")\n\t\t\t\t\treturn result\n\t\t\t\t},\n\t\t\t\t(cause: unknown) => {\n\t\t\t\t\t// Was the circuit disposed, or is this error considered a failure?\n\t\t\t\t\tif (signal.aborted || errorIsFailure(cause)) {\n\t\t\t\t\t\tteardown()\n\t\t\t\t\t\tthrow cause\n\t\t\t\t\t}\n\n\t\t\t\t\t// Should this error open the circuit?\n\t\t\t\t\tsettle(\"rejected\")\n\t\t\t\t\tif (failureRate > errorThreshold) openCircuit(cause)\n\n\t\t\t\t\treturn fallback(...args)\n\t\t\t\t},\n\t\t\t)\n\t\t}\n\n\t\t// Use the fallback while the circuit is open, or if a half-open trial\n\t\t// attempt was already made.\n\t\telse if (state === \"open\" || halfOpenPending) {\n\t\t\treturn fallback(...args)\n\t\t}\n\n\t\t// If the circuit is half-open, make one attempt. If it succeeds, close\n\t\t// the circuit and resume normal operation. If it fails, re-open the\n\t\t// circuit and run the fallback instead.\n\t\telse if (state === \"halfOpen\") {\n\t\t\treturn (halfOpenPending = main(...args))\n\t\t\t\t.finally(() => (halfOpenPending = undefined))\n\t\t\t\t.then(\n\t\t\t\t\t(result) => {\n\t\t\t\t\t\tif (signal.aborted) return result // disposed\n\t\t\t\t\t\tcloseCircuit()\n\t\t\t\t\t\treturn result\n\t\t\t\t\t},\n\t\t\t\t\t(cause: unknown) => {\n\t\t\t\t\t\t// Was the circuit disposed, or was this a non-retryable error?\n\t\t\t\t\t\tif (signal.aborted || errorIsFailure(cause)) throw cause\n\n\t\t\t\t\t\t// Open the circuit and use fallback\n\t\t\t\t\t\topenCircuit(cause)\n\t\t\t\t\t\treturn fallback(...args)\n\t\t\t\t\t},\n\t\t\t\t)\n\t\t}\n\n\t\t// exhaustive check\n\t\t/* v8 ignore next -- @preserve */\n\t\treturn assertNever(state)\n\t}\n\n\treturn Object.assign(execute, {\n\t\tdispose: (disposeMessage = \"ERR_CIRCUIT_BREAKER_DISPOSED\") => {\n\t\t\t/* v8 ignore if -- @preserve */\n\t\t\tif (signal.aborted) return\n\t\t\tconst reason = new ReferenceError(disposeMessage)\n\t\t\tmain[disposeKey]?.(disposeMessage)\n\t\t\tclearFailure()\n\t\t\thalfOpenPending = undefined\n\t\t\thistory.forEach((entry) => clearTimeout(entry.timer))\n\t\t\thistory.clear()\n\t\t\tfailureRate = 0\n\t\t\tfallback = () => Promise.reject(reason)\n\t\t\tstate = \"open\"\n\t\t\tcontroller.abort(reason)\n\t\t},\n\t\tgetFailureRate: () => failureRate,\n\t\tgetLatestError: () => failureCause,\n\t\tgetState: () => state,\n\t})\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.cjs","sources":["../lib/util.ts","../lib/options.ts","../lib/circuit-breaker.ts","../lib/backoff.ts","../lib/retry.ts","../lib/timeout.ts"],"sourcesContent":["export type AnyFn = (...args: never) => unknown\n\n/**\n * Symbol key for internal disposal protocol. Functions implementing this key\n * can be disposed by circuit breaker wrappers.\n */\nexport const disposeKey = Symbol(\"disposeKey\")\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 * Creates a dispose handler for wrapped functions. Used by timeout and retry\n * wrappers to propagate disposal through composed wrappers.\n */\nexport function createDisposable(\n\tmain: { [disposeKey]?: (message?: string) => void },\n\tcontroller: AbortController,\n) {\n\treturn (disposeMessage = \"ERR_CIRCUIT_BREAKER_DISPOSED\") => {\n\t\tconst reason = new ReferenceError(disposeMessage)\n\t\tmain[disposeKey]?.(disposeMessage)\n\t\tcontroller.abort(reason)\n\t}\n}\n\n/**\n * `[TypeScript]` For exhaustive checks in switch statements or if/else. Add\n * this check to `default` case or final `else` to ensure all possible values\n * have been handled. If a new value is added to the type, TypeScript will\n * throw an error and the editor will underline the `value`.\n */\n/* v8 ignore next -- @preserve */\nexport const assertNever = (val: never, msg = \"Unexpected value\"): never => {\n\tthrow new TypeError(`${msg}: ${val as string}`)\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\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.then(resolve, reject)\n\t\t\t.finally(() => signal.removeEventListener(\"abort\", onAbort))\n\t})\n","import type { CircuitBreakerOptions } from \"./types.js\"\nimport { assert, type AnyFn } 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} = 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\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}\n}\n","import { parseOptions } from \"./options.js\"\nimport {\n\ttype CircuitBreakerOptions,\n\ttype CircuitBreakerProtectedFn,\n\ttype CircuitState,\n\ttype HistoryEntry,\n\ttype HistoryMap,\n\ttype MainFn,\n} from \"./types.js\"\nimport { assert, assertNever, disposeKey } from \"./util.js\"\n\nconst validTransitions: Record<CircuitState, CircuitState[]> = {\n\tclosed: [\"open\", \"disposed\"],\n\topen: [\"halfOpen\", \"disposed\"],\n\thalfOpen: [\"closed\", \"open\", \"disposed\"],\n\tdisposed: [],\n}\n\nfunction assertTransition(from: CircuitState, to: CircuitState): void {\n\tassert(\n\t\tvalidTransitions[from].includes(to),\n\t\t`Invalid transition from ${from} to ${to}`,\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 one trial call\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 one\n * trial call. Success closes the circuit; failure reopens it.\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.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: userFallback,\n\t\tminimumCandidates,\n\t\tonClose,\n\t\tonHalfOpen,\n\t\tonOpen,\n\t\tresetAfter,\n\t} = parseOptions(options)\n\tconst controller = new AbortController()\n\tconst history: HistoryMap = new Map()\n\tconst signal = controller.signal\n\tlet failureCause: unknown\n\tlet failureRate = 0\n\tlet fallback: typeof main =\n\t\tuserFallback ||\n\t\t(() =>\n\t\t\tPromise.reject(\n\t\t\t\tnew Error(\"ERR_CIRCUIT_BREAKER_OPEN\", { cause: failureCause }),\n\t\t\t))\n\tlet halfOpenPending: Promise<unknown> | undefined\n\tlet resetTimer: NodeJS.Timeout | undefined\n\tlet state: CircuitState = \"closed\"\n\n\tfunction calculateFailureRate(): number {\n\t\tlet failures = 0\n\t\tlet total = 0\n\t\tfor (const { status } of 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 transition(\n\t\ttoState: CircuitState,\n\t\toptions: { cause?: unknown } = {},\n\t): void {\n\t\tassertTransition(state, toState)\n\t\tstate = toState\n\n\t\tswitch (toState) {\n\t\t\tcase \"closed\":\n\t\t\t\tclearTimeout(resetTimer)\n\t\t\t\tfailureCause = undefined\n\t\t\t\tresetTimer = undefined\n\t\t\t\tif (onClose) setImmediate(onClose)\n\t\t\t\tbreak\n\n\t\t\tcase \"open\":\n\t\t\t\tclearTimeout(resetTimer)\n\t\t\t\tfailureCause = options.cause\n\t\t\t\tresetTimer = setTimeout(() => transition(\"halfOpen\"), resetAfter)\n\t\t\t\tif (onOpen) setImmediate(onOpen, failureCause)\n\t\t\t\tbreak\n\n\t\t\tcase \"halfOpen\":\n\t\t\t\thalfOpenPending = undefined\n\t\t\t\tif (onHalfOpen) setImmediate(onHalfOpen)\n\t\t\t\tbreak\n\n\t\t\tcase \"disposed\":\n\t\t\t\tfailureCause = options.cause\n\t\t\t\tassert(failureCause instanceof Error, \"dispose cause must be an Error\")\n\t\t\t\tcontroller.abort(failureCause)\n\t\t\t\tmain[disposeKey]?.(failureCause.message)\n\n\t\t\t\thistory.forEach((entry) => clearTimeout(entry.timer))\n\t\t\t\thistory.clear()\n\n\t\t\t\thalfOpenPending = undefined\n\t\t\t\tfailureRate = 0\n\t\t\t\tfallback = undefined as never\n\n\t\t\t\tclearTimeout(resetTimer)\n\t\t\t\tresetTimer = undefined\n\t\t\t\tbreak\n\n\t\t\tdefault:\n\t\t\t\t/* v8 ignore next -- @preserve */\n\t\t\t\tassertNever(toState)\n\t\t}\n\t}\n\n\tfunction createHistoryItem<T>(pending: Promise<T>) {\n\t\tconst entry: HistoryEntry = { status: \"pending\", timer: undefined }\n\t\tconst teardown = (): void => {\n\t\t\tclearTimeout(entry.timer)\n\t\t\thistory.delete(pending)\n\t\t\tsignal.removeEventListener(\"abort\", teardown)\n\t\t}\n\t\tsignal.addEventListener(\"abort\", teardown, { once: true })\n\t\tconst settle = (value: \"resolved\" | \"rejected\"): number => {\n\t\t\tif (signal.aborted) return 0\n\t\t\tentry.status = value\n\t\t\tentry.timer = setTimeout(teardown, errorWindow)\n\t\t\treturn calculateFailureRate()\n\t\t}\n\t\thistory.set(pending, entry)\n\t\treturn { pending, settle, teardown }\n\t}\n\n\tfunction executeClosed(args: Args): Promise<Ret> {\n\t\tconst { pending, settle, teardown } = createHistoryItem(main(...args))\n\t\treturn pending.then(\n\t\t\t(result) => {\n\t\t\t\tfailureRate = settle(\"resolved\")\n\t\t\t\treturn result\n\t\t\t},\n\t\t\t(cause: unknown) => {\n\t\t\t\tif (signal.aborted || errorIsFailure(cause)) {\n\t\t\t\t\tteardown()\n\t\t\t\t\tthrow cause\n\t\t\t\t}\n\n\t\t\t\tfailureRate = settle(\"rejected\")\n\t\t\t\tif (failureRate > errorThreshold) {\n\t\t\t\t\ttransition(\"open\", { cause })\n\t\t\t\t}\n\n\t\t\t\treturn fallback(...args)\n\t\t\t},\n\t\t)\n\t}\n\n\tfunction executeOpen(args: Args): Promise<Ret> {\n\t\treturn fallback(...args)\n\t}\n\n\tfunction executeHalfOpen(args: Args): Promise<Ret> {\n\t\tif (halfOpenPending) return fallback(...args)\n\n\t\treturn (halfOpenPending = main(...args))\n\t\t\t.finally(() => (halfOpenPending = undefined))\n\t\t\t.then(\n\t\t\t\t(result) => {\n\t\t\t\t\tif (signal.aborted) return result\n\t\t\t\t\ttransition(\"closed\")\n\t\t\t\t\treturn result\n\t\t\t\t},\n\t\t\t\t(cause: unknown) => {\n\t\t\t\t\tif (signal.aborted || errorIsFailure(cause)) throw cause\n\t\t\t\t\ttransition(\"open\", { cause })\n\t\t\t\t\treturn fallback(...args)\n\t\t\t\t},\n\t\t\t)\n\t}\n\n\tfunction executeDisposed(): Promise<never> {\n\t\treturn Promise.reject(failureCause)\n\t}\n\n\tfunction execute(...args: Args): Promise<Ret> {\n\t\tswitch (state) {\n\t\t\tcase \"closed\":\n\t\t\t\treturn executeClosed(args)\n\t\t\tcase \"open\":\n\t\t\t\treturn executeOpen(args)\n\t\t\tcase \"halfOpen\":\n\t\t\t\treturn executeHalfOpen(args)\n\t\t\tcase \"disposed\":\n\t\t\t\treturn executeDisposed()\n\t\t\tdefault:\n\t\t\t\t/* v8 ignore next -- @preserve */\n\t\t\t\treturn assertNever(state)\n\t\t}\n\t}\n\n\tconst dispose = (disposeMessage = \"ERR_CIRCUIT_BREAKER_DISPOSED\") => {\n\t\tif (state === \"disposed\") return\n\t\ttransition(\"disposed\", { cause: new ReferenceError(disposeMessage) })\n\t}\n\n\treturn Object.assign(execute, {\n\t\tdispose,\n\t\tgetFailureRate: () => failureRate,\n\t\tgetLatestError: () => failureCause,\n\t\tgetState: () => state,\n\t\t[Symbol.dispose]: () => dispose(),\n\t})\n}\n","import { 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 * @param maxSeconds - Maximum delay in seconds before capping\n * @returns Function accepting attempt number and returning delay promise\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) {\n\treturn function exponentialBackoff(attempt: number, signal?: AbortSignal) {\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.\n * Delay follows the Fibonacci sequence: 1s, 2s, 3s, 5s, 8s, 13s, etc.\n *\n * More gradual than exponential backoff, useful for less aggressive retry patterns.\n *\n * @param maxSeconds - Maximum delay in seconds before capping\n * @returns Function accepting attempt number and returning delay promise\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) {\n\treturn function fibonacciBackoff(attempt: number, signal?: AbortSignal) {\n\t\tconst delay = Math.min(binet(attempt), maxSeconds)\n\t\treturn delayMs(delay * 1_000, signal)\n\t}\n}\n","import { type MainFn, type RetryOptions } from \"./types.js\"\nimport { assert, abortable, createDisposable, disposeKey } 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> {\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[disposeKey]: createDisposable(main, controller),\n\t})\n}\n","import { type MainFn } from \"./types.js\"\nimport { abortable, createDisposable, disposeKey } 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> {\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[disposeKey]: createDisposable(main, controller),\n\t})\n}\n"],"names":[],"mappings":"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"}