@fetchkit/ffetch 4.2.0 → 5.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
@@ -82,43 +82,10 @@ __export(index_exports, {
82
82
  NetworkError: () => NetworkError,
83
83
  RetryLimitError: () => RetryLimitError,
84
84
  TimeoutError: () => TimeoutError,
85
- createClient: () => createClient,
86
- default: () => index_default
85
+ createClient: () => createClient
87
86
  });
88
87
  module.exports = __toCommonJS(index_exports);
89
88
 
90
- // src/dedupeRequestHash.ts
91
- function dedupeRequestHash(params) {
92
- const { method, url, body } = params;
93
- let bodyString = "";
94
- if (body instanceof FormData) {
95
- return void 0;
96
- }
97
- if (typeof ReadableStream !== "undefined" && body instanceof ReadableStream) {
98
- return void 0;
99
- }
100
- if (typeof body === "string") {
101
- bodyString = body;
102
- } else if (body instanceof URLSearchParams) {
103
- bodyString = body.toString();
104
- } else if (body instanceof ArrayBuffer) {
105
- bodyString = Buffer.from(body).toString("base64");
106
- } else if (body instanceof Uint8Array) {
107
- bodyString = Buffer.from(body).toString("base64");
108
- } else if (body instanceof Blob) {
109
- bodyString = `[blob:${body.type}:${body.size}]`;
110
- } else if (body == null) {
111
- bodyString = "";
112
- } else {
113
- try {
114
- bodyString = JSON.stringify(body);
115
- } catch {
116
- bodyString = "[unserializable-body]";
117
- }
118
- }
119
- return `${method.toUpperCase()}|${url}|${bodyString}`;
120
- }
121
-
122
89
  // src/retry.ts
123
90
  var defaultDelay = (ctx) => {
124
91
  const retryAfter = ctx.response?.headers.get("Retry-After");
@@ -171,104 +138,49 @@ function shouldRetry(ctx) {
171
138
  return response.status >= 500 || response.status === 429;
172
139
  }
173
140
 
174
- // src/circuit.ts
175
- init_error();
176
- var CircuitBreaker = class {
177
- constructor(threshold, resetTimeout) {
178
- this.threshold = threshold;
179
- this.resetTimeout = resetTimeout;
180
- this.failures = 0;
181
- this.nextAttempt = 0;
182
- this.isOpen = false;
183
- }
184
- // Returns true if the circuit breaker is currently open (blocking requests).
185
- get open() {
186
- return this.isOpen;
187
- }
188
- // Call this after each request to record the result and update circuit state.
189
- // Returns true if the result was counted as a failure.
190
- recordResult(response, error, req) {
191
- if (error && !(error instanceof RetryLimitError)) {
192
- this.setLastOpenRequest(req);
193
- this.onFailure();
194
- return true;
195
- }
196
- if (response && (response.status >= 500 || response.status === 429)) {
197
- this.setLastOpenRequest(req);
198
- this.onFailure();
199
- return true;
200
- }
201
- if (req) this.setLastSuccessRequest(req);
202
- this.onSuccess();
203
- return false;
204
- }
205
- setHooks(hooks) {
206
- this.hooks = hooks;
207
- }
208
- setLastOpenRequest(req) {
209
- this.lastOpenRequest = req;
210
- }
211
- setLastSuccessRequest(req) {
212
- this.lastSuccessRequest = req;
213
- }
214
- async invoke(fn) {
215
- if (Date.now() < this.nextAttempt)
216
- throw new CircuitOpenError("Circuit is open");
217
- try {
218
- const res = await fn();
219
- this.onSuccess();
220
- return res;
221
- } catch (err) {
222
- this.onFailure();
223
- throw err;
224
- }
225
- }
226
- onSuccess() {
227
- const wasOpen = this.isOpen;
228
- this.failures = 0;
229
- if (wasOpen) {
230
- this.isOpen = false;
231
- if (this.hooks?.onCircuitClose && this.lastSuccessRequest) {
232
- this.hooks.onCircuitClose(this.lastSuccessRequest);
233
- }
234
- }
235
- this.lastSuccessRequest = void 0;
236
- }
237
- onFailure() {
238
- this.failures++;
239
- if (this.failures >= this.threshold) {
240
- this.nextAttempt = Date.now() + this.resetTimeout;
241
- this.isOpen = true;
242
- if (this.hooks?.onCircuitOpen && this.lastOpenRequest) {
243
- this.hooks.onCircuitOpen(this.lastOpenRequest);
244
- }
245
- }
246
- }
247
- };
248
-
249
141
  // src/client.ts
250
142
  init_error();
251
143
  function createClient(opts = {}) {
252
- const dedupeMap = /* @__PURE__ */ new Map();
253
144
  const {
254
145
  timeout: clientDefaultTimeout = 5e3,
255
146
  retries: clientDefaultRetries = 0,
256
147
  retryDelay: clientDefaultRetryDelay = defaultDelay,
257
148
  shouldRetry: clientDefaultShouldRetry = shouldRetry,
258
149
  hooks: clientDefaultHooks = {},
259
- circuit: clientDefaultCircuit,
260
150
  fetchHandler,
261
- dedupe = false,
262
- dedupeHashFn = dedupeRequestHash
151
+ plugins: inputPlugins = []
263
152
  } = opts;
264
- const breaker = clientDefaultCircuit ? new CircuitBreaker(
265
- clientDefaultCircuit.threshold,
266
- clientDefaultCircuit.reset
267
- ) : null;
268
- if (breaker && (clientDefaultHooks.onCircuitClose || clientDefaultHooks.onCircuitOpen)) {
269
- breaker.setHooks({
270
- onCircuitClose: clientDefaultHooks.onCircuitClose,
271
- onCircuitOpen: clientDefaultHooks.onCircuitOpen
153
+ const extensionDescriptors = /* @__PURE__ */ Object.create(null);
154
+ const plugins = inputPlugins.map((plugin, index) => ({ plugin, index })).sort((a, b) => {
155
+ const aOrder = a.plugin.order ?? 0;
156
+ const bOrder = b.plugin.order ?? 0;
157
+ if (aOrder !== bOrder) return aOrder - bOrder;
158
+ return a.index - b.index;
159
+ }).map((entry) => entry.plugin);
160
+ for (const plugin of plugins) {
161
+ plugin.setup?.({
162
+ defineExtension: (key, descriptor) => {
163
+ const propertyKey = key;
164
+ if (propertyKey in extensionDescriptors) {
165
+ throw new Error(
166
+ `Plugin extension collision for property "${String(propertyKey)}"`
167
+ );
168
+ }
169
+ if ("get" in descriptor) {
170
+ extensionDescriptors[propertyKey] = {
171
+ get: descriptor.get,
172
+ enumerable: descriptor.enumerable ?? true,
173
+ configurable: false
174
+ };
175
+ return;
176
+ }
177
+ extensionDescriptors[propertyKey] = {
178
+ value: descriptor.value,
179
+ writable: false,
180
+ enumerable: descriptor.enumerable ?? true,
181
+ configurable: false
182
+ };
183
+ }
272
184
  });
273
185
  }
274
186
  const pendingRequests = [];
@@ -278,53 +190,39 @@ function createClient(opts = {}) {
278
190
  }
279
191
  }
280
192
  const client = async (input, init = {}) => {
281
- const effectiveDedupe = typeof init.dedupe !== "undefined" ? init.dedupe : dedupe;
282
- const effectiveDedupeHashFn = init.dedupeHashFn || dedupeHashFn;
283
- let dedupeKey;
284
193
  let request = new Request(input, init);
285
- if (effectiveDedupe) {
286
- dedupeKey = effectiveDedupeHashFn({
287
- method: request.method,
288
- url: request.url,
289
- body: init.body ?? null,
290
- headers: request.headers,
291
- signal: init.signal === void 0 || init.signal === null ? void 0 : init.signal,
292
- requestInit: init,
293
- request
294
- });
295
- if (dedupeKey) {
296
- if (dedupeMap.has(dedupeKey)) {
297
- return dedupeMap.get(dedupeKey).promise;
298
- }
299
- let settled = false;
300
- let resolveFn;
301
- let rejectFn;
302
- const inFlightPromise = new Promise((resolve, reject) => {
303
- resolveFn = (value) => {
304
- if (!settled) {
305
- settled = true;
306
- resolve(value);
307
- }
308
- };
309
- rejectFn = (reason) => {
310
- if (!settled) {
311
- settled = true;
312
- reject(reason);
313
- }
314
- };
315
- });
316
- dedupeMap.set(dedupeKey, {
317
- promise: inFlightPromise,
318
- resolve: resolveFn,
319
- reject: rejectFn
320
- });
321
- }
322
- }
323
194
  const effectiveHooks = { ...clientDefaultHooks, ...init.hooks || {} };
324
195
  if (effectiveHooks.transformRequest) {
325
196
  request = await effectiveHooks.transformRequest(request);
326
197
  }
327
198
  await effectiveHooks.before?.(request);
199
+ const effectiveRetries = init.retries ?? clientDefaultRetries;
200
+ const effectiveRetryDelay = typeof init.retryDelay !== "undefined" ? init.retryDelay : clientDefaultRetryDelay;
201
+ const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry;
202
+ const effectiveTimeout = init.timeout ?? clientDefaultTimeout;
203
+ const userSignal = init.signal;
204
+ const transformedSignal = request.signal;
205
+ const pluginContext = {
206
+ request,
207
+ init,
208
+ state: /* @__PURE__ */ Object.create(null),
209
+ metadata: {
210
+ startedAt: Date.now(),
211
+ timeoutMs: effectiveTimeout,
212
+ signals: {
213
+ user: userSignal === void 0 || userSignal === null ? void 0 : userSignal,
214
+ transformed: transformedSignal
215
+ },
216
+ retry: {
217
+ configuredRetries: effectiveRetries,
218
+ configuredDelay: effectiveRetryDelay,
219
+ attempt: 0
220
+ }
221
+ }
222
+ };
223
+ for (const plugin of plugins) {
224
+ await plugin.preRequest?.(pluginContext);
225
+ }
328
226
  const effectiveThrowOnHttpError = typeof init.throwOnHttpError !== "undefined" ? init.throwOnHttpError : opts.throwOnHttpError ?? false;
329
227
  function createTimeoutSignal(timeout) {
330
228
  if (typeof AbortSignal?.timeout === "function") {
@@ -339,14 +237,12 @@ function createClient(opts = {}) {
339
237
  );
340
238
  return controller2.signal;
341
239
  }
342
- const effectiveTimeout = init.timeout ?? clientDefaultTimeout;
343
- const userSignal = init.signal;
344
- const transformedSignal = request.signal;
345
240
  let timeoutSignal = void 0;
346
241
  let combinedSignal = void 0;
347
242
  let controller = void 0;
348
243
  if (effectiveTimeout > 0) {
349
244
  timeoutSignal = createTimeoutSignal(effectiveTimeout);
245
+ pluginContext.metadata.signals.timeout = timeoutSignal;
350
246
  }
351
247
  const signals = [];
352
248
  if (userSignal) signals.push(userSignal);
@@ -366,14 +262,16 @@ function createClient(opts = {}) {
366
262
  combinedSignal = AbortSignal.any(signals);
367
263
  controller = new AbortController();
368
264
  }
265
+ pluginContext.metadata.signals.combined = combinedSignal;
369
266
  const retryWithHooks = async () => {
370
- const effectiveRetries = init.retries ?? clientDefaultRetries;
371
- const effectiveRetryDelay = typeof init.retryDelay !== "undefined" ? init.retryDelay : clientDefaultRetryDelay;
372
- const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry;
373
267
  let attempt = 0;
374
268
  const shouldRetryWithHook = (ctx) => {
375
269
  attempt = ctx.attempt;
270
+ pluginContext.metadata.retry.attempt = attempt;
271
+ pluginContext.metadata.retry.lastError = ctx.error;
272
+ pluginContext.metadata.retry.lastResponse = ctx.response;
376
273
  const retrying = effectiveShouldRetry(ctx);
274
+ pluginContext.metadata.retry.shouldRetryResult = retrying;
377
275
  if (retrying && attempt <= effectiveRetries) {
378
276
  effectiveHooks.onRetry?.(
379
277
  request,
@@ -419,12 +317,10 @@ function createClient(opts = {}) {
419
317
  const handler = init.fetchHandler ?? fetchHandler ?? fetch;
420
318
  const response = await handler(reqWithSignal);
421
319
  lastResponse = response;
422
- if (breaker && (response.status >= 500 || response.status === 429)) {
423
- breaker.recordResult(response, void 0, request);
424
- }
320
+ pluginContext.metadata.retry.lastResponse = response;
425
321
  return response;
426
322
  } catch (err) {
427
- if (breaker) breaker.recordResult(void 0, err, request);
323
+ pluginContext.metadata.retry.lastError = err;
428
324
  if (err instanceof DOMException && err.name === "AbortError") {
429
325
  if (timeoutSignal?.aborted && (!userSignal || !userSignal.aborted)) {
430
326
  effectiveHooks.onTimeout?.(request);
@@ -465,6 +361,7 @@ function createClient(opts = {}) {
465
361
  }
466
362
  return res;
467
363
  } catch (err) {
364
+ pluginContext.metadata.retry.lastError = err;
468
365
  if (lastResponse) {
469
366
  const resp = lastResponse;
470
367
  if (effectiveThrowOnHttpError && (resp.status >= 400 && resp.status < 500 && resp.status !== 429 || resp.status >= 500 || resp.status === 429)) {
@@ -502,45 +399,39 @@ function createClient(opts = {}) {
502
399
  throw retryErr;
503
400
  }
504
401
  };
505
- const actualPromise = breaker ? breaker.invoke(retryWithHooks).catch(async (err) => {
506
- if (err instanceof CircuitOpenError) {
507
- await effectiveHooks.onCircuitOpen?.(request);
508
- await effectiveHooks.onError?.(request, err);
509
- await effectiveHooks.onComplete?.(request, void 0, err);
510
- } else {
511
- await effectiveHooks.onError?.(request, err);
512
- await effectiveHooks.onComplete?.(request, void 0, err);
513
- }
514
- throw err;
515
- }) : retryWithHooks();
516
- if (effectiveDedupe && dedupeKey && dedupeMap.has(dedupeKey)) {
517
- const entry = dedupeMap.get(dedupeKey);
518
- if (entry) {
519
- actualPromise.then(
520
- (result) => entry.resolve(result),
521
- (error) => entry.reject(error)
522
- );
523
- dedupeMap.set(dedupeKey, {
524
- promise: actualPromise,
525
- resolve: entry.resolve,
526
- reject: entry.reject
527
- });
402
+ const baseDispatch = async () => retryWithHooks();
403
+ let dispatch = baseDispatch;
404
+ for (let i = plugins.length - 1; i >= 0; i--) {
405
+ const plugin = plugins[i];
406
+ if (plugin.wrapDispatch) {
407
+ dispatch = plugin.wrapDispatch(dispatch);
528
408
  }
529
409
  }
410
+ const actualPromise = dispatch(pluginContext).then(async (response) => {
411
+ for (const plugin of plugins) {
412
+ await plugin.onSuccess?.(pluginContext, response);
413
+ }
414
+ return response;
415
+ }).catch(async (err) => {
416
+ for (const plugin of plugins) {
417
+ await plugin.onError?.(pluginContext, err);
418
+ }
419
+ throw err;
420
+ });
530
421
  const pendingEntry = {
531
422
  promise: actualPromise,
532
423
  request,
533
424
  controller
534
425
  };
535
426
  pendingRequests.push(pendingEntry);
536
- return actualPromise.finally(() => {
427
+ return actualPromise.finally(async () => {
428
+ for (const plugin of plugins) {
429
+ await plugin.onFinally?.(pluginContext);
430
+ }
537
431
  const index = pendingRequests.indexOf(pendingEntry);
538
432
  if (index > -1) {
539
433
  pendingRequests.splice(index, 1);
540
434
  }
541
- if (effectiveDedupe && dedupeKey && dedupeMap.get(dedupeKey)?.promise === actualPromise) {
542
- dedupeMap.delete(dedupeKey);
543
- }
544
435
  });
545
436
  };
546
437
  Object.defineProperty(client, "pendingRequests", {
@@ -556,18 +447,12 @@ function createClient(opts = {}) {
556
447
  enumerable: false,
557
448
  configurable: false
558
449
  });
559
- Object.defineProperty(client, "circuitOpen", {
560
- get() {
561
- return breaker ? breaker.open : false;
562
- },
563
- enumerable: true
564
- });
450
+ Object.defineProperties(client, extensionDescriptors);
565
451
  return client;
566
452
  }
567
453
 
568
454
  // src/index.ts
569
455
  init_error();
570
- var index_default = createClient;
571
456
  // Annotate the CommonJS export names for ESM import in node:
572
457
  0 && (module.exports = {
573
458
  AbortError,
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/error.ts","../src/index.ts","../src/dedupeRequestHash.ts","../src/retry.ts","../src/should-retry.ts","../src/circuit.ts","../src/client.ts"],"sourcesContent":["// Base error class to reduce duplication\nclass BaseError extends Error {\n public cause?: unknown\n constructor(name: string, message: string, cause?: unknown) {\n super(message)\n this.name = name\n if (cause !== undefined) this.cause = cause\n }\n}\n\nexport class TimeoutError extends BaseError {\n constructor(message = 'Request timed out', cause?: unknown) {\n super('TimeoutError', message, cause)\n }\n}\n\nexport class CircuitOpenError extends BaseError {\n constructor(message = 'Circuit is open', cause?: unknown) {\n super('CircuitOpenError', message, cause)\n }\n}\n\nexport class AbortError extends BaseError {\n constructor(message = 'Request was aborted', cause?: unknown) {\n super('AbortError', message, cause)\n }\n}\n\nexport class RetryLimitError extends BaseError {\n constructor(message = 'Retry limit reached', cause?: unknown) {\n super('RetryLimitError', message, cause)\n }\n}\n\nexport class NetworkError extends BaseError {\n constructor(message = 'Network error occurred', cause?: unknown) {\n super('NetworkError', message, cause)\n }\n}\n\nexport class HttpError extends BaseError {\n constructor(message = 'HTTP error occurred', cause?: unknown) {\n super('HttpError', message, cause)\n }\n}\n","export type { FFetch, FFetchOptions } from './types'\nexport type { DedupeHashParams } from './dedupeRequestHash'\nexport type { Hooks } from './hooks'\n\nimport { createClient } from './client'\nexport { createClient } from './client'\n\nexport {\n TimeoutError,\n CircuitOpenError,\n AbortError,\n RetryLimitError,\n NetworkError,\n} from './error'\n\nexport default createClient\n","export type DedupeHashParams = {\n method: string\n url: string\n body:\n | string\n | FormData\n | URLSearchParams\n | Blob\n | ArrayBuffer\n | BufferSource\n | null\n | ReadableStream<unknown>\n headers?: Headers | Record<string, string>\n signal?: AbortSignal\n requestInit?: RequestInit\n request?: Request\n}\n\nexport function dedupeRequestHash(\n params: DedupeHashParams\n): string | undefined {\n const { method, url, body } = params\n let bodyString = ''\n if (body instanceof FormData) {\n // Skip deduplication for FormData\n return undefined\n }\n // Skip deduplication for ReadableStream\n if (typeof ReadableStream !== 'undefined' && body instanceof ReadableStream) {\n return undefined\n }\n if (typeof body === 'string') {\n bodyString = body\n } else if (body instanceof URLSearchParams) {\n bodyString = body.toString()\n } else if (body instanceof ArrayBuffer) {\n bodyString = Buffer.from(body).toString('base64')\n } else if (body instanceof Uint8Array) {\n bodyString = Buffer.from(body).toString('base64')\n } else if (body instanceof Blob) {\n bodyString = `[blob:${body.type}:${body.size}]`\n } else if (body == null) {\n bodyString = ''\n } else {\n try {\n bodyString = JSON.stringify(body)\n } catch {\n bodyString = '[unserializable-body]'\n }\n }\n return `${method.toUpperCase()}|${url}|${bodyString}`\n}\n","import type { RetryContext } from './types.js'\n\nexport type RetryDelay = number | ((ctx: RetryContext) => number)\n\nexport const defaultDelay: RetryDelay = (ctx) => {\n const retryAfter = ctx.response?.headers.get('Retry-After')\n if (retryAfter) {\n const seconds = parseInt(retryAfter, 10)\n if (!isNaN(seconds)) return seconds * 1000\n const date = Date.parse(retryAfter)\n if (!isNaN(date)) return Math.max(0, date - Date.now())\n }\n return 2 ** ctx.attempt * 200 + Math.random() * 100\n}\n\nexport async function retry(\n fn: () => Promise<Response>,\n retries: number,\n delay: RetryDelay,\n shouldRetry: (ctx: RetryContext) => boolean = () => true,\n request: Request\n): Promise<Response> {\n let lastErr: unknown\n let lastRes: Response | undefined\n\n for (let i = 0; i <= retries; i++) {\n const ctx: RetryContext = {\n attempt: i + 1,\n request,\n response: lastRes,\n error: lastErr,\n }\n try {\n lastRes = await fn()\n ctx.response = lastRes\n ctx.error = undefined\n if (i < retries && shouldRetry(ctx)) {\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n continue\n }\n return lastRes\n } catch (err) {\n lastErr = err\n ctx.error = err\n if (i === retries || !shouldRetry(ctx)) throw err\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n throw lastErr\n}\n","import { AbortError, CircuitOpenError, TimeoutError } from './error.js'\nimport type { RetryContext } from './types.js'\n\nexport function shouldRetry(ctx: RetryContext): boolean {\n const { error, response } = ctx\n if (\n error instanceof AbortError ||\n error instanceof CircuitOpenError ||\n error instanceof TimeoutError\n )\n return false\n if (!response) return true // network error\n return response.status >= 500 || response.status === 429\n}\n","import { CircuitOpenError, RetryLimitError } from './error.js'\n\nexport class CircuitBreaker {\n private failures = 0\n private nextAttempt = 0\n private isOpen = false\n\n // Returns true if the circuit breaker is currently open (blocking requests).\n get open(): boolean {\n return this.isOpen\n }\n private hooks?: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }\n private lastSuccessRequest?: Request\n private lastOpenRequest?: Request\n\n constructor(\n private threshold: number,\n private resetTimeout: number\n ) {}\n\n // Call this after each request to record the result and update circuit state.\n // Returns true if the result was counted as a failure.\n recordResult(response?: Response, error?: unknown, req?: Request): boolean {\n // Count thrown errors (network, abort, timeout) as failures\n if (error && !(error instanceof RetryLimitError)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Count HTTP 5xx and 429 responses as failures\n if (response && (response.status >= 500 || response.status === 429)) {\n this.setLastOpenRequest(req!)\n this.onFailure()\n return true\n }\n // Otherwise, count as success\n if (req) this.setLastSuccessRequest(req)\n this.onSuccess()\n return false\n }\n\n setHooks(hooks: {\n onCircuitOpen?: (req: Request) => void | Promise<void>\n onCircuitClose?: (req: Request) => void | Promise<void>\n }) {\n this.hooks = hooks\n }\n setLastOpenRequest(req: Request) {\n this.lastOpenRequest = req\n }\n\n setLastSuccessRequest(req: Request) {\n this.lastSuccessRequest = req\n }\n\n async invoke<T>(fn: () => Promise<T>): Promise<T> {\n if (Date.now() < this.nextAttempt)\n throw new CircuitOpenError('Circuit is open')\n try {\n const res = await fn()\n this.onSuccess()\n return res\n } catch (err) {\n this.onFailure()\n throw err\n }\n }\n\n private onSuccess() {\n const wasOpen = this.isOpen\n this.failures = 0\n if (wasOpen) {\n this.isOpen = false\n if (this.hooks?.onCircuitClose && this.lastSuccessRequest) {\n this.hooks.onCircuitClose(this.lastSuccessRequest)\n }\n }\n this.lastSuccessRequest = undefined\n }\n\n private onFailure() {\n this.failures++\n if (this.failures >= this.threshold) {\n this.nextAttempt = Date.now() + this.resetTimeout\n this.isOpen = true\n if (this.hooks?.onCircuitOpen && this.lastOpenRequest) {\n this.hooks.onCircuitOpen(this.lastOpenRequest)\n }\n }\n }\n}\n","import type {\r\n FFetchOptions,\r\n FFetch,\r\n FFetchRequestInit,\r\n PendingRequest,\r\n} from './types.js'\r\nimport { dedupeRequestHash } from './dedupeRequestHash.js'\r\nimport { retry, defaultDelay } from './retry.js'\r\nimport { shouldRetry as defaultShouldRetry } from './should-retry.js'\r\nimport { CircuitBreaker } from './circuit.js'\r\nimport {\r\n TimeoutError,\r\n CircuitOpenError,\r\n AbortError,\r\n RetryLimitError,\r\n NetworkError,\r\n} from './error.js'\r\n\r\nexport function createClient(opts: FFetchOptions = {}): FFetch {\r\n // Track in-flight deduped requests and their resolvers\r\n const dedupeMap = new Map<\r\n string,\r\n {\r\n promise: Promise<Response>\r\n resolve: (value: Response | PromiseLike<Response>) => void\r\n reject: (reason?: unknown) => void\r\n }\r\n >()\r\n const {\r\n timeout: clientDefaultTimeout = 5_000,\r\n retries: clientDefaultRetries = 0,\r\n retryDelay: clientDefaultRetryDelay = defaultDelay,\r\n shouldRetry: clientDefaultShouldRetry = defaultShouldRetry,\r\n hooks: clientDefaultHooks = {},\r\n circuit: clientDefaultCircuit,\r\n fetchHandler,\r\n dedupe = false,\r\n dedupeHashFn = dedupeRequestHash,\r\n } = opts\r\n\r\n const breaker = clientDefaultCircuit\r\n ? new CircuitBreaker(\r\n clientDefaultCircuit.threshold,\r\n clientDefaultCircuit.reset\r\n )\r\n : null\r\n\r\n if (\r\n breaker &&\r\n (clientDefaultHooks.onCircuitClose || clientDefaultHooks.onCircuitOpen)\r\n ) {\r\n breaker.setHooks({\r\n onCircuitClose: clientDefaultHooks.onCircuitClose,\r\n onCircuitOpen: clientDefaultHooks.onCircuitOpen,\r\n })\r\n }\r\n\r\n const pendingRequests: PendingRequest[] = []\r\n\r\n // Helper to abort all pending requests\r\n function abortAll() {\r\n for (const entry of pendingRequests) {\r\n entry.controller?.abort()\r\n }\r\n }\r\n\r\n const client = async (\r\n input: RequestInfo | URL,\r\n init: FFetchRequestInit = {}\r\n ) => {\r\n // Deduplication logic\r\n const effectiveDedupe =\r\n typeof init.dedupe !== 'undefined' ? init.dedupe : dedupe\r\n const effectiveDedupeHashFn = init.dedupeHashFn || dedupeHashFn\r\n let dedupeKey: string | undefined\r\n\r\n let request = new Request(input, init)\r\n if (effectiveDedupe) {\r\n dedupeKey = effectiveDedupeHashFn({\r\n method: request.method,\r\n url: request.url,\r\n body: init.body ?? null,\r\n headers: request.headers,\r\n signal:\r\n init.signal === undefined || init.signal === null\r\n ? undefined\r\n : init.signal,\r\n requestInit: init,\r\n request,\r\n })\r\n if (dedupeKey) {\r\n if (dedupeMap.has(dedupeKey)) {\r\n return dedupeMap.get(dedupeKey)!.promise\r\n }\r\n let settled = false\r\n let resolveFn: (value: Response | PromiseLike<Response>) => void\r\n let rejectFn: (reason?: unknown) => void\r\n const inFlightPromise = new Promise<Response>((resolve, reject) => {\r\n resolveFn = (value) => {\r\n if (!settled) {\r\n settled = true\r\n resolve(value)\r\n }\r\n }\r\n rejectFn = (reason) => {\r\n if (!settled) {\r\n settled = true\r\n reject(reason)\r\n }\r\n }\r\n })\r\n dedupeMap.set(dedupeKey, {\r\n promise: inFlightPromise,\r\n resolve: resolveFn!,\r\n reject: rejectFn!,\r\n })\r\n }\r\n }\r\n\r\n // Merge hooks: per-request hooks override client hooks, but fallback to client hooks\r\n const effectiveHooks = { ...clientDefaultHooks, ...(init.hooks || {}) }\r\n if (effectiveHooks.transformRequest) {\r\n request = await effectiveHooks.transformRequest(request)\r\n }\r\n await effectiveHooks.before?.(request)\r\n\r\n // Determine throwOnHttpError (per-request overrides client default)\r\n const effectiveThrowOnHttpError =\r\n typeof init.throwOnHttpError !== 'undefined'\r\n ? init.throwOnHttpError\r\n : (opts.throwOnHttpError ?? false)\r\n\r\n // Create timeout signal (manual implementation if AbortSignal.timeout not available)\r\n function createTimeoutSignal(timeout: number): AbortSignal {\r\n if (typeof AbortSignal?.timeout === 'function') {\r\n return AbortSignal.timeout(timeout)\r\n }\r\n const controller = new AbortController()\r\n const timeoutId = setTimeout(() => controller.abort(), timeout)\r\n controller.signal.addEventListener(\r\n 'abort',\r\n () => clearTimeout(timeoutId),\r\n { once: true }\r\n )\r\n return controller.signal\r\n }\r\n\r\n // AbortSignal.timeout/any logic\r\n const effectiveTimeout = init.timeout ?? clientDefaultTimeout\r\n const userSignal = init.signal\r\n const transformedSignal = request.signal\r\n let timeoutSignal: AbortSignal | undefined = undefined\r\n let combinedSignal: AbortSignal | undefined = undefined\r\n let controller: AbortController | undefined = undefined\r\n\r\n if (effectiveTimeout > 0) {\r\n timeoutSignal = createTimeoutSignal(effectiveTimeout)\r\n }\r\n\r\n const signals: AbortSignal[] = []\r\n if (userSignal) signals.push(userSignal)\r\n if (transformedSignal && transformedSignal !== userSignal) {\r\n signals.push(transformedSignal)\r\n }\r\n if (timeoutSignal) signals.push(timeoutSignal)\r\n\r\n if (signals.length === 1) {\r\n combinedSignal = signals[0]\r\n controller = new AbortController()\r\n } else {\r\n if (typeof AbortSignal.any !== 'function') {\r\n throw new Error(\r\n 'AbortSignal.any is required for combining multiple signals. Please install a polyfill for environments that do not support it.'\r\n )\r\n }\r\n combinedSignal = AbortSignal.any(signals)\r\n controller = new AbortController()\r\n }\r\n\r\n const retryWithHooks = async () => {\r\n const effectiveRetries = init.retries ?? clientDefaultRetries\r\n const effectiveRetryDelay =\r\n typeof init.retryDelay !== 'undefined'\r\n ? init.retryDelay\r\n : clientDefaultRetryDelay\r\n const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry\r\n\r\n let attempt = 0\r\n const shouldRetryWithHook = (ctx: import('./types').RetryContext) => {\r\n attempt = ctx.attempt\r\n const retrying = effectiveShouldRetry(ctx)\r\n if (retrying && attempt <= effectiveRetries) {\r\n effectiveHooks.onRetry?.(\r\n request,\r\n attempt - 1,\r\n ctx.error,\r\n ctx.response\r\n )\r\n }\r\n return retrying\r\n }\r\n\r\n let lastResponse: Response | undefined = undefined\r\n try {\r\n let res = await retry(\r\n async () => {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n }\r\n if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n }\r\n if (typeof combinedSignal?.throwIfAborted === 'function') {\r\n combinedSignal.throwIfAborted()\r\n } else if (combinedSignal?.aborted) {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n }\r\n const reqWithSignal = new Request(request, {\r\n signal: combinedSignal,\r\n })\r\n try {\r\n const handler = init.fetchHandler ?? fetchHandler ?? fetch\r\n const response = await handler(reqWithSignal)\r\n lastResponse = response\r\n if (\r\n breaker &&\r\n (response.status >= 500 || response.status === 429)\r\n ) {\r\n breaker.recordResult(response, undefined, request)\r\n }\r\n return response\r\n } catch (err) {\r\n if (breaker) breaker.recordResult(undefined, err, request)\r\n if (err instanceof DOMException && err.name === 'AbortError') {\r\n if (\r\n timeoutSignal?.aborted &&\r\n (!userSignal || !userSignal.aborted)\r\n ) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out', err)\r\n } else if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n } else if (\r\n err instanceof TypeError &&\r\n /NetworkError|network error|failed to fetch|lost connection|NetworkError when attempting to fetch resource/i.test(\r\n err.message\r\n )\r\n ) {\r\n throw new NetworkError(err.message, err)\r\n }\r\n throw err\r\n }\r\n },\r\n effectiveRetries,\r\n effectiveRetryDelay,\r\n shouldRetryWithHook,\r\n request\r\n )\r\n if (effectiveHooks.transformResponse) {\r\n res = await effectiveHooks.transformResponse(res, request)\r\n }\r\n await effectiveHooks.after?.(request, res)\r\n await effectiveHooks.onComplete?.(request, res, undefined)\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((res.status >= 400 && res.status < 500 && res.status !== 429) ||\r\n res.status >= 500 ||\r\n res.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${res.status} ${res.statusText}`,\r\n res\r\n )\r\n }\r\n return res\r\n } catch (err: unknown) {\r\n if (lastResponse) {\r\n const resp = lastResponse as Response\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((resp.status >= 400 && resp.status < 500 && resp.status !== 429) ||\r\n resp.status >= 500 ||\r\n resp.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${resp.status} ${resp.statusText}`,\r\n resp\r\n )\r\n }\r\n return resp\r\n }\r\n if (err instanceof TimeoutError) {\r\n await effectiveHooks.onTimeout?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof AbortError) {\r\n await effectiveHooks.onAbort?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof NetworkError) {\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n const retryErr = new RetryLimitError(\r\n typeof err === 'object' &&\r\n err &&\r\n 'message' in err &&\r\n typeof (err as { message?: unknown }).message === 'string'\r\n ? (err as { message: string }).message\r\n : 'Retry limit reached',\r\n err\r\n )\r\n await effectiveHooks.onError?.(request, retryErr)\r\n await effectiveHooks.onComplete?.(request, undefined, retryErr)\r\n throw retryErr\r\n }\r\n }\r\n\r\n const actualPromise = breaker\r\n ? breaker.invoke(retryWithHooks).catch(async (err: unknown) => {\r\n if (err instanceof CircuitOpenError) {\r\n await effectiveHooks.onCircuitOpen?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n } else {\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n }\r\n throw err\r\n })\r\n : retryWithHooks()\r\n\r\n // If deduplication is enabled and dedupeKey is set, resolve/reject the in-flight promise\r\n if (effectiveDedupe && dedupeKey && dedupeMap.has(dedupeKey)) {\r\n const entry = dedupeMap.get(dedupeKey)\r\n if (entry) {\r\n actualPromise.then(\r\n (result) => entry.resolve(result),\r\n (error) => entry.reject(error)\r\n )\r\n // Replace the placeholder with the actual promise for future requests\r\n dedupeMap.set(dedupeKey, {\r\n promise: actualPromise,\r\n resolve: entry.resolve,\r\n reject: entry.reject,\r\n })\r\n }\r\n }\r\n\r\n const pendingEntry: PendingRequest = {\r\n promise: actualPromise,\r\n request,\r\n controller,\r\n }\r\n pendingRequests.push(pendingEntry)\r\n\r\n return actualPromise.finally(() => {\r\n const index = pendingRequests.indexOf(pendingEntry)\r\n if (index > -1) {\r\n pendingRequests.splice(index, 1)\r\n }\r\n // Only delete dedupeMap entry if the promise is the same as the one in the map\r\n if (\r\n effectiveDedupe &&\r\n dedupeKey &&\r\n dedupeMap.get(dedupeKey)?.promise === actualPromise\r\n ) {\r\n dedupeMap.delete(dedupeKey)\r\n }\r\n })\r\n }\r\n\r\n Object.defineProperty(client, 'pendingRequests', {\r\n get() {\r\n return pendingRequests\r\n },\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperty(client, 'abortAll', {\r\n value: abortAll,\r\n writable: false,\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperty(client, 'circuitOpen', {\r\n get() {\r\n return breaker ? breaker.open : false\r\n },\r\n enumerable: true,\r\n })\r\n\r\n return client as FFetch\r\n}\r\n"],"mappings":"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","names":["shouldRetry","controller","HttpError"]}
1
+ {"version":3,"sources":["../src/error.ts","../src/index.ts","../src/retry.ts","../src/should-retry.ts","../src/client.ts"],"sourcesContent":["// Base error class to reduce duplication\nclass BaseError extends Error {\n public cause?: unknown\n constructor(name: string, message: string, cause?: unknown) {\n super(message)\n this.name = name\n if (cause !== undefined) this.cause = cause\n }\n}\n\nexport class TimeoutError extends BaseError {\n constructor(message = 'Request timed out', cause?: unknown) {\n super('TimeoutError', message, cause)\n }\n}\n\nexport class CircuitOpenError extends BaseError {\n constructor(message = 'Circuit is open', cause?: unknown) {\n super('CircuitOpenError', message, cause)\n }\n}\n\nexport class AbortError extends BaseError {\n constructor(message = 'Request was aborted', cause?: unknown) {\n super('AbortError', message, cause)\n }\n}\n\nexport class RetryLimitError extends BaseError {\n constructor(message = 'Retry limit reached', cause?: unknown) {\n super('RetryLimitError', message, cause)\n }\n}\n\nexport class NetworkError extends BaseError {\n constructor(message = 'Network error occurred', cause?: unknown) {\n super('NetworkError', message, cause)\n }\n}\n\nexport class HttpError extends BaseError {\n constructor(message = 'HTTP error occurred', cause?: unknown) {\n super('HttpError', message, cause)\n }\n}\n","export type { FFetch, FFetchOptions } from './types'\r\nexport type { Hooks } from './hooks'\r\nexport type {\r\n ClientPlugin,\r\n PluginRequestContext,\r\n PluginDispatch,\r\n PluginSetupContext,\r\n} from './plugins'\r\n\r\nexport { createClient } from './client'\r\n\r\nexport {\r\n TimeoutError,\r\n CircuitOpenError,\r\n AbortError,\r\n RetryLimitError,\r\n NetworkError,\r\n} from './error'\r\n","import type { RetryContext } from './types.js'\n\nexport type RetryDelay = number | ((ctx: RetryContext) => number)\n\nexport const defaultDelay: RetryDelay = (ctx) => {\n const retryAfter = ctx.response?.headers.get('Retry-After')\n if (retryAfter) {\n const seconds = parseInt(retryAfter, 10)\n if (!isNaN(seconds)) return seconds * 1000\n const date = Date.parse(retryAfter)\n if (!isNaN(date)) return Math.max(0, date - Date.now())\n }\n return 2 ** ctx.attempt * 200 + Math.random() * 100\n}\n\nexport async function retry(\n fn: () => Promise<Response>,\n retries: number,\n delay: RetryDelay,\n shouldRetry: (ctx: RetryContext) => boolean = () => true,\n request: Request\n): Promise<Response> {\n let lastErr: unknown\n let lastRes: Response | undefined\n\n for (let i = 0; i <= retries; i++) {\n const ctx: RetryContext = {\n attempt: i + 1,\n request,\n response: lastRes,\n error: lastErr,\n }\n try {\n lastRes = await fn()\n ctx.response = lastRes\n ctx.error = undefined\n if (i < retries && shouldRetry(ctx)) {\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n continue\n }\n return lastRes\n } catch (err) {\n lastErr = err\n ctx.error = err\n if (i === retries || !shouldRetry(ctx)) throw err\n const wait = typeof delay === 'function' ? delay(ctx) : delay\n await new Promise((r) => setTimeout(r, wait))\n }\n }\n throw lastErr\n}\n","import { AbortError, CircuitOpenError, TimeoutError } from './error.js'\nimport type { RetryContext } from './types.js'\n\nexport function shouldRetry(ctx: RetryContext): boolean {\n const { error, response } = ctx\n if (\n error instanceof AbortError ||\n error instanceof CircuitOpenError ||\n error instanceof TimeoutError\n )\n return false\n if (!response) return true // network error\n return response.status >= 500 || response.status === 429\n}\n","import type {\r\n FFetchOptions,\r\n FFetch,\r\n FFetchRequestInit,\r\n PendingRequest,\r\n} from './types.js'\r\nimport { retry, defaultDelay } from './retry.js'\r\nimport { shouldRetry as defaultShouldRetry } from './should-retry.js'\r\nimport {\r\n type PluginDispatch,\r\n type PluginRequestContext,\r\n type PluginExtensions,\r\n type ClientPlugin,\r\n type PluginExtensionBase,\r\n} from './plugins.js'\r\nimport {\r\n TimeoutError,\r\n AbortError,\r\n RetryLimitError,\r\n NetworkError,\r\n} from './error.js'\r\n\r\nexport function createClient<\r\n TPlugins extends\r\n readonly ClientPlugin<PluginExtensionBase>[] = readonly ClientPlugin<PluginExtensionBase>[],\r\n>(\r\n opts: FFetchOptions<TPlugins> = {} as FFetchOptions<TPlugins>\r\n): FFetch<PluginExtensions<TPlugins>> {\r\n const {\r\n timeout: clientDefaultTimeout = 5_000,\r\n retries: clientDefaultRetries = 0,\r\n retryDelay: clientDefaultRetryDelay = defaultDelay,\r\n shouldRetry: clientDefaultShouldRetry = defaultShouldRetry,\r\n hooks: clientDefaultHooks = {},\r\n fetchHandler,\r\n plugins: inputPlugins = [] as unknown as TPlugins,\r\n } = opts\r\n\r\n const extensionDescriptors: PropertyDescriptorMap = Object.create(null)\r\n\r\n const plugins = inputPlugins\r\n .map((plugin, index) => ({ plugin, index }))\r\n .sort((a, b) => {\r\n const aOrder = a.plugin.order ?? 0\r\n const bOrder = b.plugin.order ?? 0\r\n if (aOrder !== bOrder) return aOrder - bOrder\r\n return a.index - b.index\r\n })\r\n .map((entry) => entry.plugin)\r\n\r\n for (const plugin of plugins) {\r\n plugin.setup?.({\r\n defineExtension: (key, descriptor) => {\r\n const propertyKey = key as PropertyKey\r\n if (propertyKey in extensionDescriptors) {\r\n throw new Error(\r\n `Plugin extension collision for property \"${String(propertyKey)}\"`\r\n )\r\n }\r\n if ('get' in descriptor) {\r\n extensionDescriptors[propertyKey] = {\r\n get: descriptor.get,\r\n enumerable: descriptor.enumerable ?? true,\r\n configurable: false,\r\n }\r\n return\r\n }\r\n extensionDescriptors[propertyKey] = {\r\n value: descriptor.value,\r\n writable: false,\r\n enumerable: descriptor.enumerable ?? true,\r\n configurable: false,\r\n }\r\n },\r\n })\r\n }\r\n\r\n const pendingRequests: PendingRequest[] = []\r\n\r\n // Helper to abort all pending requests\r\n function abortAll() {\r\n for (const entry of pendingRequests) {\r\n entry.controller?.abort()\r\n }\r\n }\r\n\r\n const client = async (\r\n input: RequestInfo | URL,\r\n init: FFetchRequestInit = {}\r\n ) => {\r\n let request = new Request(input, init)\r\n\r\n // Merge hooks: per-request hooks override client hooks, but fallback to client hooks\r\n const effectiveHooks = { ...clientDefaultHooks, ...(init.hooks || {}) }\r\n if (effectiveHooks.transformRequest) {\r\n request = await effectiveHooks.transformRequest(request)\r\n }\r\n await effectiveHooks.before?.(request)\r\n\r\n // Determine retry config (per-request overrides client default)\r\n const effectiveRetries = init.retries ?? clientDefaultRetries\r\n const effectiveRetryDelay =\r\n typeof init.retryDelay !== 'undefined'\r\n ? init.retryDelay\r\n : clientDefaultRetryDelay\r\n const effectiveShouldRetry = init.shouldRetry ?? clientDefaultShouldRetry\r\n\r\n // AbortSignal.timeout/any logic\r\n const effectiveTimeout = init.timeout ?? clientDefaultTimeout\r\n const userSignal = init.signal\r\n const transformedSignal = request.signal\r\n\r\n const pluginContext: PluginRequestContext = {\r\n request,\r\n init,\r\n state: Object.create(null),\r\n metadata: {\r\n startedAt: Date.now(),\r\n timeoutMs: effectiveTimeout,\r\n signals: {\r\n user:\r\n userSignal === undefined || userSignal === null\r\n ? undefined\r\n : userSignal,\r\n transformed: transformedSignal,\r\n },\r\n retry: {\r\n configuredRetries: effectiveRetries,\r\n configuredDelay: effectiveRetryDelay,\r\n attempt: 0,\r\n },\r\n },\r\n }\r\n\r\n for (const plugin of plugins) {\r\n await plugin.preRequest?.(pluginContext)\r\n }\r\n\r\n // Determine throwOnHttpError (per-request overrides client default)\r\n const effectiveThrowOnHttpError =\r\n typeof init.throwOnHttpError !== 'undefined'\r\n ? init.throwOnHttpError\r\n : (opts.throwOnHttpError ?? false)\r\n\r\n // Create timeout signal (manual implementation if AbortSignal.timeout not available)\r\n function createTimeoutSignal(timeout: number): AbortSignal {\r\n if (typeof AbortSignal?.timeout === 'function') {\r\n return AbortSignal.timeout(timeout)\r\n }\r\n const controller = new AbortController()\r\n const timeoutId = setTimeout(() => controller.abort(), timeout)\r\n controller.signal.addEventListener(\r\n 'abort',\r\n () => clearTimeout(timeoutId),\r\n { once: true }\r\n )\r\n return controller.signal\r\n }\r\n\r\n let timeoutSignal: AbortSignal | undefined = undefined\r\n let combinedSignal: AbortSignal | undefined = undefined\r\n let controller: AbortController | undefined = undefined\r\n\r\n if (effectiveTimeout > 0) {\r\n timeoutSignal = createTimeoutSignal(effectiveTimeout)\r\n pluginContext.metadata.signals.timeout = timeoutSignal\r\n }\r\n\r\n const signals: AbortSignal[] = []\r\n if (userSignal) signals.push(userSignal)\r\n if (transformedSignal && transformedSignal !== userSignal) {\r\n signals.push(transformedSignal)\r\n }\r\n if (timeoutSignal) signals.push(timeoutSignal)\r\n\r\n if (signals.length === 1) {\r\n combinedSignal = signals[0]\r\n controller = new AbortController()\r\n } else {\r\n if (typeof AbortSignal.any !== 'function') {\r\n throw new Error(\r\n 'AbortSignal.any is required for combining multiple signals. Please install a polyfill for environments that do not support it.'\r\n )\r\n }\r\n combinedSignal = AbortSignal.any(signals)\r\n controller = new AbortController()\r\n }\r\n pluginContext.metadata.signals.combined = combinedSignal\r\n\r\n const retryWithHooks = async () => {\r\n let attempt = 0\r\n const shouldRetryWithHook = (ctx: import('./types').RetryContext) => {\r\n attempt = ctx.attempt\r\n pluginContext.metadata.retry.attempt = attempt\r\n pluginContext.metadata.retry.lastError = ctx.error\r\n pluginContext.metadata.retry.lastResponse = ctx.response\r\n const retrying = effectiveShouldRetry(ctx)\r\n pluginContext.metadata.retry.shouldRetryResult = retrying\r\n if (retrying && attempt <= effectiveRetries) {\r\n effectiveHooks.onRetry?.(\r\n request,\r\n attempt - 1,\r\n ctx.error,\r\n ctx.response\r\n )\r\n }\r\n return retrying\r\n }\r\n\r\n let lastResponse: Response | undefined = undefined\r\n try {\r\n let res = await retry(\r\n async () => {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n }\r\n if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n }\r\n if (typeof combinedSignal?.throwIfAborted === 'function') {\r\n combinedSignal.throwIfAborted()\r\n } else if (combinedSignal?.aborted) {\r\n if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else if (timeoutSignal?.aborted) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n }\r\n const reqWithSignal = new Request(request, {\r\n signal: combinedSignal,\r\n })\r\n try {\r\n const handler = init.fetchHandler ?? fetchHandler ?? fetch\r\n const response = await handler(reqWithSignal)\r\n lastResponse = response\r\n pluginContext.metadata.retry.lastResponse = response\r\n return response\r\n } catch (err) {\r\n pluginContext.metadata.retry.lastError = err\r\n if (err instanceof DOMException && err.name === 'AbortError') {\r\n if (\r\n timeoutSignal?.aborted &&\r\n (!userSignal || !userSignal.aborted)\r\n ) {\r\n effectiveHooks.onTimeout?.(request)\r\n throw new TimeoutError('signal timed out', err)\r\n } else if (userSignal?.aborted) {\r\n effectiveHooks.onAbort?.(request)\r\n throw new AbortError('Request was aborted by user')\r\n } else {\r\n throw new AbortError(\r\n 'Request was aborted',\r\n new DOMException('Aborted', 'AbortError')\r\n )\r\n }\r\n } else if (\r\n err instanceof TypeError &&\r\n /NetworkError|network error|failed to fetch|lost connection|NetworkError when attempting to fetch resource/i.test(\r\n err.message\r\n )\r\n ) {\r\n throw new NetworkError(err.message, err)\r\n }\r\n throw err\r\n }\r\n },\r\n effectiveRetries,\r\n effectiveRetryDelay,\r\n shouldRetryWithHook,\r\n request\r\n )\r\n if (effectiveHooks.transformResponse) {\r\n res = await effectiveHooks.transformResponse(res, request)\r\n }\r\n await effectiveHooks.after?.(request, res)\r\n await effectiveHooks.onComplete?.(request, res, undefined)\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((res.status >= 400 && res.status < 500 && res.status !== 429) ||\r\n res.status >= 500 ||\r\n res.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${res.status} ${res.statusText}`,\r\n res\r\n )\r\n }\r\n return res\r\n } catch (err: unknown) {\r\n pluginContext.metadata.retry.lastError = err\r\n if (lastResponse) {\r\n const resp = lastResponse as Response\r\n if (\r\n effectiveThrowOnHttpError &&\r\n ((resp.status >= 400 && resp.status < 500 && resp.status !== 429) ||\r\n resp.status >= 500 ||\r\n resp.status === 429)\r\n ) {\r\n const { HttpError } = await import('./error.js')\r\n throw new HttpError(\r\n `HTTP error: ${resp.status} ${resp.statusText}`,\r\n resp\r\n )\r\n }\r\n return resp\r\n }\r\n if (err instanceof TimeoutError) {\r\n await effectiveHooks.onTimeout?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof AbortError) {\r\n await effectiveHooks.onAbort?.(request)\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n if (err instanceof NetworkError) {\r\n await effectiveHooks.onError?.(request, err)\r\n await effectiveHooks.onComplete?.(request, undefined, err)\r\n throw err\r\n }\r\n const retryErr = new RetryLimitError(\r\n typeof err === 'object' &&\r\n err &&\r\n 'message' in err &&\r\n typeof (err as { message?: unknown }).message === 'string'\r\n ? (err as { message: string }).message\r\n : 'Retry limit reached',\r\n err\r\n )\r\n await effectiveHooks.onError?.(request, retryErr)\r\n await effectiveHooks.onComplete?.(request, undefined, retryErr)\r\n throw retryErr\r\n }\r\n }\r\n\r\n const baseDispatch: PluginDispatch = async () => retryWithHooks()\r\n\r\n let dispatch = baseDispatch\r\n for (let i = plugins.length - 1; i >= 0; i--) {\r\n const plugin = plugins[i]\r\n if (plugin.wrapDispatch) {\r\n dispatch = plugin.wrapDispatch(dispatch)\r\n }\r\n }\r\n\r\n const actualPromise = dispatch(pluginContext)\r\n .then(async (response) => {\r\n for (const plugin of plugins) {\r\n await plugin.onSuccess?.(pluginContext, response)\r\n }\r\n return response\r\n })\r\n .catch(async (err: unknown) => {\r\n for (const plugin of plugins) {\r\n await plugin.onError?.(pluginContext, err)\r\n }\r\n throw err\r\n })\r\n\r\n const pendingEntry: PendingRequest = {\r\n promise: actualPromise,\r\n request,\r\n controller,\r\n }\r\n pendingRequests.push(pendingEntry)\r\n\r\n return actualPromise.finally(async () => {\r\n for (const plugin of plugins) {\r\n await plugin.onFinally?.(pluginContext)\r\n }\r\n\r\n const index = pendingRequests.indexOf(pendingEntry)\r\n if (index > -1) {\r\n pendingRequests.splice(index, 1)\r\n }\r\n })\r\n }\r\n\r\n Object.defineProperty(client, 'pendingRequests', {\r\n get() {\r\n return pendingRequests\r\n },\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperty(client, 'abortAll', {\r\n value: abortAll,\r\n writable: false,\r\n enumerable: false,\r\n configurable: false,\r\n })\r\n\r\n Object.defineProperties(client, extensionDescriptors)\r\n\r\n return client as FFetch<PluginExtensions<TPlugins>>\r\n}\r\n"],"mappings":"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","names":["shouldRetry","controller","HttpError"]}
package/dist/index.d.cts CHANGED
@@ -1,3 +1,6 @@
1
+ import { C as ClientPlugin, P as PluginExtensionBase, a as PluginExtensions } from './plugins-9qcU31nU.cjs';
2
+ export { c as PluginDispatch, b as PluginRequestContext, d as PluginSetupContext } from './plugins-9qcU31nU.cjs';
3
+
1
4
  type Hooks = {
2
5
  before?: (req: Request) => void | Promise<void>;
3
6
  after?: (req: Request, res: Response) => void | Promise<void>;
@@ -5,51 +8,36 @@ type Hooks = {
5
8
  onRetry?: (req: Request, attempt: number, err?: unknown, res?: Response) => void | Promise<void>;
6
9
  onTimeout?: (req: Request) => void | Promise<void>;
7
10
  onAbort?: (req: Request) => void | Promise<void>;
8
- onCircuitOpen?: (req: Request) => void | Promise<void>;
9
- onCircuitClose?: (req: Request) => void | Promise<void>;
10
11
  onComplete?: (req: Request, res?: Response, err?: unknown) => void | Promise<void>;
11
12
  transformRequest?: (req: Request) => Request | Promise<Request>;
12
13
  transformResponse?: (res: Response, req: Request) => Response | Promise<Response>;
13
14
  };
14
15
 
15
- type DedupeHashParams = {
16
- method: string;
17
- url: string;
18
- body: string | FormData | URLSearchParams | Blob | ArrayBuffer | BufferSource | null | ReadableStream<unknown>;
19
- headers?: Headers | Record<string, string>;
20
- signal?: AbortSignal;
21
- requestInit?: RequestInit;
22
- request?: Request;
23
- };
24
-
25
16
  interface RetryContext {
26
17
  attempt: number;
27
18
  request: Request;
28
19
  response?: Response;
29
20
  error?: unknown;
30
21
  }
31
- interface FFetchOptions {
22
+ interface CoreClientOptions {
32
23
  timeout?: number;
33
24
  retries?: number;
34
25
  retryDelay?: number | ((ctx: RetryContext) => number);
35
26
  shouldRetry?: (ctx: RetryContext) => boolean;
36
27
  throwOnHttpError?: boolean;
37
- circuit?: {
38
- threshold: number;
39
- reset: number;
40
- };
41
28
  hooks?: Hooks;
42
29
  fetchHandler?: (input: RequestInfo | URL, init?: RequestInit) => Promise<Response>;
43
- dedupe?: boolean;
44
- dedupeHashFn?: (params: DedupeHashParams) => string | undefined;
45
30
  }
46
- type FFetch = {
31
+ interface FFetchOptions<TPlugins extends readonly ClientPlugin<PluginExtensionBase>[] = readonly ClientPlugin<PluginExtensionBase>[]> extends CoreClientOptions {
32
+ plugins?: TPlugins;
33
+ }
34
+ type FFetchRequestOptions = CoreClientOptions;
35
+ type FFetch<TExtensions extends object = Record<never, never>> = {
47
36
  (input: RequestInfo | URL, init?: FFetchRequestInit): Promise<Response>;
48
37
  pendingRequests: PendingRequest[];
49
38
  abortAll: () => void;
50
- circuitOpen: boolean;
51
- };
52
- interface FFetchRequestInit extends RequestInit, FFetchOptions {
39
+ } & TExtensions;
40
+ interface FFetchRequestInit extends RequestInit, FFetchRequestOptions {
53
41
  }
54
42
  type PendingRequest = {
55
43
  promise: Promise<Response>;
@@ -57,7 +45,7 @@ type PendingRequest = {
57
45
  controller?: AbortController;
58
46
  };
59
47
 
60
- declare function createClient(opts?: FFetchOptions): FFetch;
48
+ declare function createClient<TPlugins extends readonly ClientPlugin<PluginExtensionBase>[] = readonly ClientPlugin<PluginExtensionBase>[]>(opts?: FFetchOptions<TPlugins>): FFetch<PluginExtensions<TPlugins>>;
61
49
 
62
50
  declare class BaseError extends Error {
63
51
  cause?: unknown;
@@ -79,4 +67,4 @@ declare class NetworkError extends BaseError {
79
67
  constructor(message?: string, cause?: unknown);
80
68
  }
81
69
 
82
- export { AbortError, CircuitOpenError, type DedupeHashParams, type FFetch, type FFetchOptions, type Hooks, NetworkError, RetryLimitError, TimeoutError, createClient, createClient as default };
70
+ export { AbortError, CircuitOpenError, ClientPlugin, type FFetch, type FFetchOptions, type Hooks, NetworkError, RetryLimitError, TimeoutError, createClient };