@fetchkit/ffetch 5.2.0 → 5.3.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.
Files changed (40) hide show
  1. package/README.md +27 -15
  2. package/dist/chunk-X64PXWPN.min.js +2 -0
  3. package/dist/chunk-X64PXWPN.min.js.map +1 -0
  4. package/dist/{chunk-RV6LC73N.js → chunk-ZIJ2DZYZ.js} +7 -1
  5. package/dist/chunk-ZIJ2DZYZ.js.map +1 -0
  6. package/dist/{error-XBHPSMAQ.js → error-H4GGJSCM.js} +4 -2
  7. package/dist/error-HT5UPPWM.min.js +2 -0
  8. package/dist/index.cjs +53 -37
  9. package/dist/index.cjs.map +1 -1
  10. package/dist/index.d.cts +4 -1
  11. package/dist/index.d.ts +4 -1
  12. package/dist/index.js +49 -39
  13. package/dist/index.js.map +1 -1
  14. package/dist/index.min.js +1 -1
  15. package/dist/index.min.js.map +1 -1
  16. package/dist/plugins/bulkhead.cjs +137 -0
  17. package/dist/plugins/bulkhead.cjs.map +1 -0
  18. package/dist/plugins/bulkhead.d.cts +15 -0
  19. package/dist/plugins/bulkhead.d.ts +15 -0
  20. package/dist/plugins/bulkhead.js +96 -0
  21. package/dist/plugins/bulkhead.js.map +1 -0
  22. package/dist/plugins/circuit.cjs +14 -8
  23. package/dist/plugins/circuit.cjs.map +1 -1
  24. package/dist/plugins/circuit.d.cts +16 -3
  25. package/dist/plugins/circuit.d.ts +16 -3
  26. package/dist/plugins/circuit.js +15 -9
  27. package/dist/plugins/circuit.js.map +1 -1
  28. package/dist/plugins/hedge.cjs +127 -0
  29. package/dist/plugins/hedge.cjs.map +1 -0
  30. package/dist/plugins/hedge.d.cts +12 -0
  31. package/dist/plugins/hedge.d.ts +12 -0
  32. package/dist/plugins/hedge.js +102 -0
  33. package/dist/plugins/hedge.js.map +1 -0
  34. package/package.json +12 -2
  35. package/dist/chunk-RV6LC73N.js.map +0 -1
  36. package/dist/chunk-UP35S5ZH.min.js +0 -2
  37. package/dist/chunk-UP35S5ZH.min.js.map +0 -1
  38. package/dist/error-7EEQP46E.min.js +0 -2
  39. /package/dist/{error-7EEQP46E.min.js.map → error-H4GGJSCM.js.map} +0 -0
  40. /package/dist/{error-XBHPSMAQ.js.map → error-HT5UPPWM.min.js.map} +0 -0
@@ -0,0 +1,137 @@
1
+ "use strict";
2
+ var __defProp = Object.defineProperty;
3
+ var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
4
+ var __getOwnPropNames = Object.getOwnPropertyNames;
5
+ var __hasOwnProp = Object.prototype.hasOwnProperty;
6
+ var __export = (target, all) => {
7
+ for (var name in all)
8
+ __defProp(target, name, { get: all[name], enumerable: true });
9
+ };
10
+ var __copyProps = (to, from, except, desc) => {
11
+ if (from && typeof from === "object" || typeof from === "function") {
12
+ for (let key of __getOwnPropNames(from))
13
+ if (!__hasOwnProp.call(to, key) && key !== except)
14
+ __defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
15
+ }
16
+ return to;
17
+ };
18
+ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
19
+
20
+ // src/plugins/bulkhead.ts
21
+ var bulkhead_exports = {};
22
+ __export(bulkhead_exports, {
23
+ bulkheadPlugin: () => bulkheadPlugin
24
+ });
25
+ module.exports = __toCommonJS(bulkhead_exports);
26
+
27
+ // src/error.ts
28
+ var BaseError = class extends Error {
29
+ constructor(name, message, cause) {
30
+ super(message);
31
+ this.name = name;
32
+ if (cause !== void 0) this.cause = cause;
33
+ }
34
+ };
35
+ var BulkheadFullError = class extends BaseError {
36
+ constructor(message = "Bulkhead queue is full", cause) {
37
+ super("BulkheadFullError", message, cause);
38
+ }
39
+ };
40
+ var AbortError = class extends BaseError {
41
+ constructor(message = "Request was aborted", cause) {
42
+ super("AbortError", message, cause);
43
+ }
44
+ };
45
+
46
+ // src/plugins/bulkhead.ts
47
+ function bulkheadPlugin(options) {
48
+ const { maxConcurrent, maxQueue, onReject, order = 5 } = options;
49
+ let activeCount = 0;
50
+ const queue = [];
51
+ function makeAbortError(signal) {
52
+ return new AbortError("Request was aborted", signal.reason);
53
+ }
54
+ function drainQueue() {
55
+ while (activeCount < maxConcurrent && queue.length > 0) {
56
+ const next = queue.shift();
57
+ next.cleanup();
58
+ if (next.request.signal.aborted) {
59
+ next.reject(makeAbortError(next.request.signal));
60
+ continue;
61
+ }
62
+ activeCount++;
63
+ next.resolve();
64
+ }
65
+ }
66
+ function release() {
67
+ activeCount = Math.max(0, activeCount - 1);
68
+ drainQueue();
69
+ }
70
+ async function acquire(request) {
71
+ if (activeCount < maxConcurrent) {
72
+ activeCount++;
73
+ return;
74
+ }
75
+ if (typeof maxQueue === "number" && maxQueue >= 0 && queue.length >= maxQueue) {
76
+ await onReject?.(request);
77
+ throw new BulkheadFullError("Bulkhead queue is full");
78
+ }
79
+ await new Promise((resolve, reject) => {
80
+ const entry = {
81
+ request,
82
+ resolve,
83
+ reject,
84
+ cleanup: () => {
85
+ }
86
+ };
87
+ const onAbort = () => {
88
+ const idx = queue.indexOf(entry);
89
+ if (idx >= 0) {
90
+ queue.splice(idx, 1);
91
+ }
92
+ entry.cleanup();
93
+ reject(makeAbortError(request.signal));
94
+ };
95
+ entry.cleanup = () => {
96
+ request.signal.removeEventListener("abort", onAbort);
97
+ };
98
+ if (request.signal.aborted) {
99
+ reject(makeAbortError(request.signal));
100
+ return;
101
+ }
102
+ request.signal.addEventListener("abort", onAbort, { once: true });
103
+ queue.push(entry);
104
+ });
105
+ }
106
+ return {
107
+ name: "bulkhead",
108
+ order,
109
+ setup: ({ defineExtension }) => {
110
+ defineExtension("activeCount", {
111
+ get: () => activeCount,
112
+ enumerable: true
113
+ });
114
+ defineExtension("queueDepth", {
115
+ get: () => queue.length,
116
+ enumerable: true
117
+ });
118
+ },
119
+ wrapDispatch: (next) => async (ctx) => {
120
+ let acquired = false;
121
+ try {
122
+ await acquire(ctx.request);
123
+ acquired = true;
124
+ return await next(ctx);
125
+ } finally {
126
+ if (acquired) {
127
+ release();
128
+ }
129
+ }
130
+ }
131
+ };
132
+ }
133
+ // Annotate the CommonJS export names for ESM import in node:
134
+ 0 && (module.exports = {
135
+ bulkheadPlugin
136
+ });
137
+ //# sourceMappingURL=bulkhead.cjs.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/plugins/bulkhead.ts","../../src/error.ts"],"sourcesContent":["import type { ClientPlugin } from '../plugins.js'\nimport { AbortError, BulkheadFullError } from '../error.js'\n\nexport type BulkheadPluginExtension = {\n activeCount: number\n queueDepth: number\n}\n\nexport type BulkheadPluginOptions = {\n maxConcurrent: number\n maxQueue?: number\n onReject?: (req: Request) => void | Promise<void>\n order?: number\n}\n\ntype QueueEntry = {\n request: Request\n resolve: () => void\n reject: (err: unknown) => void\n cleanup: () => void\n}\n\nexport function bulkheadPlugin(\n options: BulkheadPluginOptions\n): ClientPlugin<BulkheadPluginExtension> {\n const { maxConcurrent, maxQueue, onReject, order = 5 } = options\n\n let activeCount = 0\n const queue: QueueEntry[] = []\n\n function makeAbortError(signal: AbortSignal): AbortError {\n return new AbortError('Request was aborted', signal.reason)\n }\n\n function drainQueue(): void {\n while (activeCount < maxConcurrent && queue.length > 0) {\n const next = queue.shift()!\n next.cleanup()\n\n if (next.request.signal.aborted) {\n next.reject(makeAbortError(next.request.signal))\n continue\n }\n\n activeCount++\n next.resolve()\n }\n }\n\n function release(): void {\n activeCount = Math.max(0, activeCount - 1)\n drainQueue()\n }\n\n async function acquire(request: Request): Promise<void> {\n if (activeCount < maxConcurrent) {\n activeCount++\n return\n }\n\n if (\n typeof maxQueue === 'number' &&\n maxQueue >= 0 &&\n queue.length >= maxQueue\n ) {\n await onReject?.(request)\n throw new BulkheadFullError('Bulkhead queue is full')\n }\n\n await new Promise<void>((resolve, reject) => {\n const entry: QueueEntry = {\n request,\n resolve,\n reject,\n cleanup: () => {},\n }\n\n const onAbort = () => {\n const idx = queue.indexOf(entry)\n if (idx >= 0) {\n queue.splice(idx, 1)\n }\n entry.cleanup()\n reject(makeAbortError(request.signal))\n }\n\n entry.cleanup = () => {\n request.signal.removeEventListener('abort', onAbort)\n }\n\n if (request.signal.aborted) {\n reject(makeAbortError(request.signal))\n return\n }\n\n request.signal.addEventListener('abort', onAbort, { once: true })\n queue.push(entry)\n })\n }\n\n return {\n name: 'bulkhead',\n order,\n setup: ({ defineExtension }) => {\n defineExtension('activeCount', {\n get: () => activeCount,\n enumerable: true,\n })\n defineExtension('queueDepth', {\n get: () => queue.length,\n enumerable: true,\n })\n },\n wrapDispatch: (next) => async (ctx) => {\n let acquired = false\n try {\n await acquire(ctx.request)\n acquired = true\n return await next(ctx)\n } finally {\n if (acquired) {\n release()\n }\n }\n },\n }\n}\n","// 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 BulkheadFullError extends BaseError {\n constructor(message = 'Bulkhead queue is full', cause?: unknown) {\n super('BulkheadFullError', 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"],"mappings":"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","names":[]}
@@ -0,0 +1,15 @@
1
+ import { C as ClientPlugin } from '../plugins-DqBQVM4I.cjs';
2
+
3
+ type BulkheadPluginExtension = {
4
+ activeCount: number;
5
+ queueDepth: number;
6
+ };
7
+ type BulkheadPluginOptions = {
8
+ maxConcurrent: number;
9
+ maxQueue?: number;
10
+ onReject?: (req: Request) => void | Promise<void>;
11
+ order?: number;
12
+ };
13
+ declare function bulkheadPlugin(options: BulkheadPluginOptions): ClientPlugin<BulkheadPluginExtension>;
14
+
15
+ export { type BulkheadPluginExtension, type BulkheadPluginOptions, bulkheadPlugin };
@@ -0,0 +1,15 @@
1
+ import { C as ClientPlugin } from '../plugins-DqBQVM4I.js';
2
+
3
+ type BulkheadPluginExtension = {
4
+ activeCount: number;
5
+ queueDepth: number;
6
+ };
7
+ type BulkheadPluginOptions = {
8
+ maxConcurrent: number;
9
+ maxQueue?: number;
10
+ onReject?: (req: Request) => void | Promise<void>;
11
+ order?: number;
12
+ };
13
+ declare function bulkheadPlugin(options: BulkheadPluginOptions): ClientPlugin<BulkheadPluginExtension>;
14
+
15
+ export { type BulkheadPluginExtension, type BulkheadPluginOptions, bulkheadPlugin };
@@ -0,0 +1,96 @@
1
+ import {
2
+ AbortError,
3
+ BulkheadFullError
4
+ } from "../chunk-ZIJ2DZYZ.js";
5
+
6
+ // src/plugins/bulkhead.ts
7
+ function bulkheadPlugin(options) {
8
+ const { maxConcurrent, maxQueue, onReject, order = 5 } = options;
9
+ let activeCount = 0;
10
+ const queue = [];
11
+ function makeAbortError(signal) {
12
+ return new AbortError("Request was aborted", signal.reason);
13
+ }
14
+ function drainQueue() {
15
+ while (activeCount < maxConcurrent && queue.length > 0) {
16
+ const next = queue.shift();
17
+ next.cleanup();
18
+ if (next.request.signal.aborted) {
19
+ next.reject(makeAbortError(next.request.signal));
20
+ continue;
21
+ }
22
+ activeCount++;
23
+ next.resolve();
24
+ }
25
+ }
26
+ function release() {
27
+ activeCount = Math.max(0, activeCount - 1);
28
+ drainQueue();
29
+ }
30
+ async function acquire(request) {
31
+ if (activeCount < maxConcurrent) {
32
+ activeCount++;
33
+ return;
34
+ }
35
+ if (typeof maxQueue === "number" && maxQueue >= 0 && queue.length >= maxQueue) {
36
+ await onReject?.(request);
37
+ throw new BulkheadFullError("Bulkhead queue is full");
38
+ }
39
+ await new Promise((resolve, reject) => {
40
+ const entry = {
41
+ request,
42
+ resolve,
43
+ reject,
44
+ cleanup: () => {
45
+ }
46
+ };
47
+ const onAbort = () => {
48
+ const idx = queue.indexOf(entry);
49
+ if (idx >= 0) {
50
+ queue.splice(idx, 1);
51
+ }
52
+ entry.cleanup();
53
+ reject(makeAbortError(request.signal));
54
+ };
55
+ entry.cleanup = () => {
56
+ request.signal.removeEventListener("abort", onAbort);
57
+ };
58
+ if (request.signal.aborted) {
59
+ reject(makeAbortError(request.signal));
60
+ return;
61
+ }
62
+ request.signal.addEventListener("abort", onAbort, { once: true });
63
+ queue.push(entry);
64
+ });
65
+ }
66
+ return {
67
+ name: "bulkhead",
68
+ order,
69
+ setup: ({ defineExtension }) => {
70
+ defineExtension("activeCount", {
71
+ get: () => activeCount,
72
+ enumerable: true
73
+ });
74
+ defineExtension("queueDepth", {
75
+ get: () => queue.length,
76
+ enumerable: true
77
+ });
78
+ },
79
+ wrapDispatch: (next) => async (ctx) => {
80
+ let acquired = false;
81
+ try {
82
+ await acquire(ctx.request);
83
+ acquired = true;
84
+ return await next(ctx);
85
+ } finally {
86
+ if (acquired) {
87
+ release();
88
+ }
89
+ }
90
+ }
91
+ };
92
+ }
93
+ export {
94
+ bulkheadPlugin
95
+ };
96
+ //# sourceMappingURL=bulkhead.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../src/plugins/bulkhead.ts"],"sourcesContent":["import type { ClientPlugin } from '../plugins.js'\nimport { AbortError, BulkheadFullError } from '../error.js'\n\nexport type BulkheadPluginExtension = {\n activeCount: number\n queueDepth: number\n}\n\nexport type BulkheadPluginOptions = {\n maxConcurrent: number\n maxQueue?: number\n onReject?: (req: Request) => void | Promise<void>\n order?: number\n}\n\ntype QueueEntry = {\n request: Request\n resolve: () => void\n reject: (err: unknown) => void\n cleanup: () => void\n}\n\nexport function bulkheadPlugin(\n options: BulkheadPluginOptions\n): ClientPlugin<BulkheadPluginExtension> {\n const { maxConcurrent, maxQueue, onReject, order = 5 } = options\n\n let activeCount = 0\n const queue: QueueEntry[] = []\n\n function makeAbortError(signal: AbortSignal): AbortError {\n return new AbortError('Request was aborted', signal.reason)\n }\n\n function drainQueue(): void {\n while (activeCount < maxConcurrent && queue.length > 0) {\n const next = queue.shift()!\n next.cleanup()\n\n if (next.request.signal.aborted) {\n next.reject(makeAbortError(next.request.signal))\n continue\n }\n\n activeCount++\n next.resolve()\n }\n }\n\n function release(): void {\n activeCount = Math.max(0, activeCount - 1)\n drainQueue()\n }\n\n async function acquire(request: Request): Promise<void> {\n if (activeCount < maxConcurrent) {\n activeCount++\n return\n }\n\n if (\n typeof maxQueue === 'number' &&\n maxQueue >= 0 &&\n queue.length >= maxQueue\n ) {\n await onReject?.(request)\n throw new BulkheadFullError('Bulkhead queue is full')\n }\n\n await new Promise<void>((resolve, reject) => {\n const entry: QueueEntry = {\n request,\n resolve,\n reject,\n cleanup: () => {},\n }\n\n const onAbort = () => {\n const idx = queue.indexOf(entry)\n if (idx >= 0) {\n queue.splice(idx, 1)\n }\n entry.cleanup()\n reject(makeAbortError(request.signal))\n }\n\n entry.cleanup = () => {\n request.signal.removeEventListener('abort', onAbort)\n }\n\n if (request.signal.aborted) {\n reject(makeAbortError(request.signal))\n return\n }\n\n request.signal.addEventListener('abort', onAbort, { once: true })\n queue.push(entry)\n })\n }\n\n return {\n name: 'bulkhead',\n order,\n setup: ({ defineExtension }) => {\n defineExtension('activeCount', {\n get: () => activeCount,\n enumerable: true,\n })\n defineExtension('queueDepth', {\n get: () => queue.length,\n enumerable: true,\n })\n },\n wrapDispatch: (next) => async (ctx) => {\n let acquired = false\n try {\n await acquire(ctx.request)\n acquired = true\n return await next(ctx)\n } finally {\n if (acquired) {\n release()\n }\n }\n },\n }\n}\n"],"mappings":";;;;;;AAsBO,SAAS,eACd,SACuC;AACvC,QAAM,EAAE,eAAe,UAAU,UAAU,QAAQ,EAAE,IAAI;AAEzD,MAAI,cAAc;AAClB,QAAM,QAAsB,CAAC;AAE7B,WAAS,eAAe,QAAiC;AACvD,WAAO,IAAI,WAAW,uBAAuB,OAAO,MAAM;AAAA,EAC5D;AAEA,WAAS,aAAmB;AAC1B,WAAO,cAAc,iBAAiB,MAAM,SAAS,GAAG;AACtD,YAAM,OAAO,MAAM,MAAM;AACzB,WAAK,QAAQ;AAEb,UAAI,KAAK,QAAQ,OAAO,SAAS;AAC/B,aAAK,OAAO,eAAe,KAAK,QAAQ,MAAM,CAAC;AAC/C;AAAA,MACF;AAEA;AACA,WAAK,QAAQ;AAAA,IACf;AAAA,EACF;AAEA,WAAS,UAAgB;AACvB,kBAAc,KAAK,IAAI,GAAG,cAAc,CAAC;AACzC,eAAW;AAAA,EACb;AAEA,iBAAe,QAAQ,SAAiC;AACtD,QAAI,cAAc,eAAe;AAC/B;AACA;AAAA,IACF;AAEA,QACE,OAAO,aAAa,YACpB,YAAY,KACZ,MAAM,UAAU,UAChB;AACA,YAAM,WAAW,OAAO;AACxB,YAAM,IAAI,kBAAkB,wBAAwB;AAAA,IACtD;AAEA,UAAM,IAAI,QAAc,CAAC,SAAS,WAAW;AAC3C,YAAM,QAAoB;AAAA,QACxB;AAAA,QACA;AAAA,QACA;AAAA,QACA,SAAS,MAAM;AAAA,QAAC;AAAA,MAClB;AAEA,YAAM,UAAU,MAAM;AACpB,cAAM,MAAM,MAAM,QAAQ,KAAK;AAC/B,YAAI,OAAO,GAAG;AACZ,gBAAM,OAAO,KAAK,CAAC;AAAA,QACrB;AACA,cAAM,QAAQ;AACd,eAAO,eAAe,QAAQ,MAAM,CAAC;AAAA,MACvC;AAEA,YAAM,UAAU,MAAM;AACpB,gBAAQ,OAAO,oBAAoB,SAAS,OAAO;AAAA,MACrD;AAEA,UAAI,QAAQ,OAAO,SAAS;AAC1B,eAAO,eAAe,QAAQ,MAAM,CAAC;AACrC;AAAA,MACF;AAEA,cAAQ,OAAO,iBAAiB,SAAS,SAAS,EAAE,MAAM,KAAK,CAAC;AAChE,YAAM,KAAK,KAAK;AAAA,IAClB,CAAC;AAAA,EACH;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,CAAC,EAAE,gBAAgB,MAAM;AAC9B,sBAAgB,eAAe;AAAA,QAC7B,KAAK,MAAM;AAAA,QACX,YAAY;AAAA,MACd,CAAC;AACD,sBAAgB,cAAc;AAAA,QAC5B,KAAK,MAAM,MAAM;AAAA,QACjB,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,IACA,cAAc,CAAC,SAAS,OAAO,QAAQ;AACrC,UAAI,WAAW;AACf,UAAI;AACF,cAAM,QAAQ,IAAI,OAAO;AACzB,mBAAW;AACX,eAAO,MAAM,KAAK,GAAG;AAAA,MACvB,UAAE;AACA,YAAI,UAAU;AACZ,kBAAQ;AAAA,QACV;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -59,20 +59,23 @@ function circuitPlugin(options) {
59
59
  }
60
60
  return false;
61
61
  };
62
- const onSuccess = async (req) => {
62
+ const onSuccess = async (req, response) => {
63
63
  const wasOpen = isOpen;
64
64
  failures = 0;
65
65
  if (wasOpen) {
66
66
  isOpen = false;
67
- await onCircuitClose?.(req);
67
+ await onCircuitClose?.({ request: req, response });
68
68
  }
69
69
  };
70
- const onFailure = async (req) => {
70
+ const onFailure = async (req, reason) => {
71
71
  failures++;
72
72
  if (failures >= threshold) {
73
73
  nextAttempt = Date.now() + reset;
74
74
  isOpen = true;
75
- await onCircuitOpen?.(req);
75
+ await onCircuitOpen?.({
76
+ request: req,
77
+ reason: { type: "threshold-reached", ...reason }
78
+ });
76
79
  return true;
77
80
  }
78
81
  return false;
@@ -88,18 +91,21 @@ function circuitPlugin(options) {
88
91
  },
89
92
  preRequest: async (ctx) => {
90
93
  if (Date.now() < nextAttempt) {
91
- await onCircuitOpen?.(ctx.request);
94
+ await onCircuitOpen?.({
95
+ request: ctx.request,
96
+ reason: { type: "already-open" }
97
+ });
92
98
  throw new CircuitOpenError("Circuit is open");
93
99
  }
94
100
  },
95
101
  onSuccess: async (ctx, response) => {
96
102
  if (shouldCountFailure(response, void 0)) {
97
- const opened = await onFailure(ctx.request);
103
+ const opened = await onFailure(ctx.request, { response });
98
104
  if (opened) {
99
105
  throw new CircuitOpenError("Circuit is open");
100
106
  }
101
107
  } else {
102
- await onSuccess(ctx.request);
108
+ await onSuccess(ctx.request, response);
103
109
  }
104
110
  },
105
111
  onError: async (ctx, error) => {
@@ -107,7 +113,7 @@ function circuitPlugin(options) {
107
113
  return;
108
114
  }
109
115
  if (shouldCountFailure(void 0, error)) {
110
- const opened = await onFailure(ctx.request);
116
+ const opened = await onFailure(ctx.request, { error });
111
117
  if (opened) {
112
118
  throw new CircuitOpenError("Circuit is open");
113
119
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/circuit.ts","../../src/error.ts"],"sourcesContent":["import type { ClientPlugin } from '../plugins.js'\r\nimport { CircuitOpenError } from '../error.js'\r\n\r\nexport type CircuitPluginExtension = {\r\n circuitOpen: boolean\r\n}\r\n\r\nexport type CircuitPluginOptions = {\r\n threshold: number\r\n reset: number\r\n onCircuitOpen?: (req: Request) => void | Promise<void>\r\n onCircuitClose?: (req: Request) => void | Promise<void>\r\n order?: number\r\n}\r\n\r\nexport function circuitPlugin(\r\n options: CircuitPluginOptions\r\n): ClientPlugin<CircuitPluginExtension> {\r\n const {\r\n threshold,\r\n reset,\r\n onCircuitOpen,\r\n onCircuitClose,\r\n order = 20,\r\n } = options\r\n\r\n let failures = 0\r\n let nextAttempt = 0\r\n let isOpen = false\r\n\r\n const shouldCountFailure = (\r\n response?: Response,\r\n error?: unknown\r\n ): boolean => {\r\n if (error) {\r\n return true\r\n }\r\n if (response && (response.status >= 500 || response.status === 429)) {\r\n return true\r\n }\r\n return false\r\n }\r\n\r\n const onSuccess = async (req: Request) => {\r\n const wasOpen = isOpen\r\n failures = 0\r\n if (wasOpen) {\r\n isOpen = false\r\n await onCircuitClose?.(req)\r\n }\r\n }\r\n\r\n const onFailure = async (req: Request): Promise<boolean> => {\r\n failures++\r\n if (failures >= threshold) {\r\n nextAttempt = Date.now() + reset\r\n isOpen = true\r\n await onCircuitOpen?.(req)\r\n return true\r\n }\r\n return false\r\n }\r\n\r\n return {\r\n name: 'circuit',\r\n order,\r\n setup: ({ defineExtension }) => {\r\n defineExtension('circuitOpen', {\r\n get: () => isOpen,\r\n enumerable: true,\r\n })\r\n },\r\n preRequest: async (ctx) => {\r\n if (Date.now() < nextAttempt) {\r\n await onCircuitOpen?.(ctx.request)\r\n throw new CircuitOpenError('Circuit is open')\r\n }\r\n },\r\n onSuccess: async (ctx, response) => {\r\n if (shouldCountFailure(response, undefined)) {\r\n const opened = await onFailure(ctx.request)\r\n if (opened) {\r\n throw new CircuitOpenError('Circuit is open')\r\n }\r\n } else {\r\n await onSuccess(ctx.request)\r\n }\r\n },\r\n onError: async (ctx, error) => {\r\n if (error instanceof CircuitOpenError) {\r\n return\r\n }\r\n if (shouldCountFailure(undefined, error)) {\r\n const opened = await onFailure(ctx.request)\r\n if (opened) {\r\n throw new CircuitOpenError('Circuit is open')\r\n }\r\n }\r\n },\r\n }\r\n}\r\n","// 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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,IAAM,YAAN,cAAwB,MAAM;AAAA,EAE5B,YAAY,MAAc,SAAiB,OAAiB;AAC1D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,QAAI,UAAU,OAAW,MAAK,QAAQ;AAAA,EACxC;AACF;AAQO,IAAM,mBAAN,cAA+B,UAAU;AAAA,EAC9C,YAAY,UAAU,mBAAmB,OAAiB;AACxD,UAAM,oBAAoB,SAAS,KAAK;AAAA,EAC1C;AACF;;;ADLO,SAAS,cACd,SACsC;AACtC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV,IAAI;AAEJ,MAAI,WAAW;AACf,MAAI,cAAc;AAClB,MAAI,SAAS;AAEb,QAAM,qBAAqB,CACzB,UACA,UACY;AACZ,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AACA,QAAI,aAAa,SAAS,UAAU,OAAO,SAAS,WAAW,MAAM;AACnE,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,OAAO,QAAiB;AACxC,UAAM,UAAU;AAChB,eAAW;AACX,QAAI,SAAS;AACX,eAAS;AACT,YAAM,iBAAiB,GAAG;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,YAAY,OAAO,QAAmC;AAC1D;AACA,QAAI,YAAY,WAAW;AACzB,oBAAc,KAAK,IAAI,IAAI;AAC3B,eAAS;AACT,YAAM,gBAAgB,GAAG;AACzB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,CAAC,EAAE,gBAAgB,MAAM;AAC9B,sBAAgB,eAAe;AAAA,QAC7B,KAAK,MAAM;AAAA,QACX,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,IACA,YAAY,OAAO,QAAQ;AACzB,UAAI,KAAK,IAAI,IAAI,aAAa;AAC5B,cAAM,gBAAgB,IAAI,OAAO;AACjC,cAAM,IAAI,iBAAiB,iBAAiB;AAAA,MAC9C;AAAA,IACF;AAAA,IACA,WAAW,OAAO,KAAK,aAAa;AAClC,UAAI,mBAAmB,UAAU,MAAS,GAAG;AAC3C,cAAM,SAAS,MAAM,UAAU,IAAI,OAAO;AAC1C,YAAI,QAAQ;AACV,gBAAM,IAAI,iBAAiB,iBAAiB;AAAA,QAC9C;AAAA,MACF,OAAO;AACL,cAAM,UAAU,IAAI,OAAO;AAAA,MAC7B;AAAA,IACF;AAAA,IACA,SAAS,OAAO,KAAK,UAAU;AAC7B,UAAI,iBAAiB,kBAAkB;AACrC;AAAA,MACF;AACA,UAAI,mBAAmB,QAAW,KAAK,GAAG;AACxC,cAAM,SAAS,MAAM,UAAU,IAAI,OAAO;AAC1C,YAAI,QAAQ;AACV,gBAAM,IAAI,iBAAiB,iBAAiB;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/plugins/circuit.ts","../../src/error.ts"],"sourcesContent":["import type { ClientPlugin } from '../plugins.js'\nimport { CircuitOpenError } from '../error.js'\n\nexport type CircuitPluginExtension = {\n circuitOpen: boolean\n}\n\nexport type CircuitOpenReason =\n | { type: 'already-open' }\n | {\n type: 'threshold-reached'\n response?: Response\n error?: unknown\n }\n\nexport type CircuitPluginOptions = {\n threshold: number\n reset: number\n onCircuitOpen?: (ctx: {\n request: Request\n reason: CircuitOpenReason\n }) => void | Promise<void>\n onCircuitClose?: (ctx: {\n request: Request\n response: Response\n }) => void | Promise<void>\n order?: number\n}\n\nexport function circuitPlugin(\n options: CircuitPluginOptions\n): ClientPlugin<CircuitPluginExtension> {\n const {\n threshold,\n reset,\n onCircuitOpen,\n onCircuitClose,\n order = 20,\n } = options\n\n let failures = 0\n let nextAttempt = 0\n let isOpen = false\n\n const shouldCountFailure = (\n response?: Response,\n error?: unknown\n ): boolean => {\n if (error) {\n return true\n }\n if (response && (response.status >= 500 || response.status === 429)) {\n return true\n }\n return false\n }\n\n const onSuccess = async (req: Request, response: Response) => {\n const wasOpen = isOpen\n failures = 0\n if (wasOpen) {\n isOpen = false\n await onCircuitClose?.({ request: req, response })\n }\n }\n\n const onFailure = async (\n req: Request,\n reason: Omit<\n Extract<CircuitOpenReason, { type: 'threshold-reached' }>,\n 'type'\n >\n ): Promise<boolean> => {\n failures++\n if (failures >= threshold) {\n nextAttempt = Date.now() + reset\n isOpen = true\n await onCircuitOpen?.({\n request: req,\n reason: { type: 'threshold-reached', ...reason },\n })\n return true\n }\n return false\n }\n\n return {\n name: 'circuit',\n order,\n setup: ({ defineExtension }) => {\n defineExtension('circuitOpen', {\n get: () => isOpen,\n enumerable: true,\n })\n },\n preRequest: async (ctx) => {\n if (Date.now() < nextAttempt) {\n await onCircuitOpen?.({\n request: ctx.request,\n reason: { type: 'already-open' },\n })\n throw new CircuitOpenError('Circuit is open')\n }\n },\n onSuccess: async (ctx, response) => {\n if (shouldCountFailure(response, undefined)) {\n const opened = await onFailure(ctx.request, { response })\n if (opened) {\n throw new CircuitOpenError('Circuit is open')\n }\n } else {\n await onSuccess(ctx.request, response)\n }\n },\n onError: async (ctx, error) => {\n if (error instanceof CircuitOpenError) {\n return\n }\n if (shouldCountFailure(undefined, error)) {\n const opened = await onFailure(ctx.request, { error })\n if (opened) {\n throw new CircuitOpenError('Circuit is open')\n }\n }\n },\n }\n}\n","// 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 BulkheadFullError extends BaseError {\n constructor(message = 'Bulkhead queue is full', cause?: unknown) {\n super('BulkheadFullError', 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"],"mappings":";;;;;;;;;;;;;;;;;;;;AAAA;AAAA;AAAA;AAAA;AAAA;;;ACCA,IAAM,YAAN,cAAwB,MAAM;AAAA,EAE5B,YAAY,MAAc,SAAiB,OAAiB;AAC1D,UAAM,OAAO;AACb,SAAK,OAAO;AACZ,QAAI,UAAU,OAAW,MAAK,QAAQ;AAAA,EACxC;AACF;AAQO,IAAM,mBAAN,cAA+B,UAAU;AAAA,EAC9C,YAAY,UAAU,mBAAmB,OAAiB;AACxD,UAAM,oBAAoB,SAAS,KAAK;AAAA,EAC1C;AACF;;;ADSO,SAAS,cACd,SACsC;AACtC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV,IAAI;AAEJ,MAAI,WAAW;AACf,MAAI,cAAc;AAClB,MAAI,SAAS;AAEb,QAAM,qBAAqB,CACzB,UACA,UACY;AACZ,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AACA,QAAI,aAAa,SAAS,UAAU,OAAO,SAAS,WAAW,MAAM;AACnE,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,OAAO,KAAc,aAAuB;AAC5D,UAAM,UAAU;AAChB,eAAW;AACX,QAAI,SAAS;AACX,eAAS;AACT,YAAM,iBAAiB,EAAE,SAAS,KAAK,SAAS,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,YAAY,OAChB,KACA,WAIqB;AACrB;AACA,QAAI,YAAY,WAAW;AACzB,oBAAc,KAAK,IAAI,IAAI;AAC3B,eAAS;AACT,YAAM,gBAAgB;AAAA,QACpB,SAAS;AAAA,QACT,QAAQ,EAAE,MAAM,qBAAqB,GAAG,OAAO;AAAA,MACjD,CAAC;AACD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,CAAC,EAAE,gBAAgB,MAAM;AAC9B,sBAAgB,eAAe;AAAA,QAC7B,KAAK,MAAM;AAAA,QACX,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,IACA,YAAY,OAAO,QAAQ;AACzB,UAAI,KAAK,IAAI,IAAI,aAAa;AAC5B,cAAM,gBAAgB;AAAA,UACpB,SAAS,IAAI;AAAA,UACb,QAAQ,EAAE,MAAM,eAAe;AAAA,QACjC,CAAC;AACD,cAAM,IAAI,iBAAiB,iBAAiB;AAAA,MAC9C;AAAA,IACF;AAAA,IACA,WAAW,OAAO,KAAK,aAAa;AAClC,UAAI,mBAAmB,UAAU,MAAS,GAAG;AAC3C,cAAM,SAAS,MAAM,UAAU,IAAI,SAAS,EAAE,SAAS,CAAC;AACxD,YAAI,QAAQ;AACV,gBAAM,IAAI,iBAAiB,iBAAiB;AAAA,QAC9C;AAAA,MACF,OAAO;AACL,cAAM,UAAU,IAAI,SAAS,QAAQ;AAAA,MACvC;AAAA,IACF;AAAA,IACA,SAAS,OAAO,KAAK,UAAU;AAC7B,UAAI,iBAAiB,kBAAkB;AACrC;AAAA,MACF;AACA,UAAI,mBAAmB,QAAW,KAAK,GAAG;AACxC,cAAM,SAAS,MAAM,UAAU,IAAI,SAAS,EAAE,MAAM,CAAC;AACrD,YAAI,QAAQ;AACV,gBAAM,IAAI,iBAAiB,iBAAiB;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
@@ -3,13 +3,26 @@ import { C as ClientPlugin } from '../plugins-DqBQVM4I.cjs';
3
3
  type CircuitPluginExtension = {
4
4
  circuitOpen: boolean;
5
5
  };
6
+ type CircuitOpenReason = {
7
+ type: 'already-open';
8
+ } | {
9
+ type: 'threshold-reached';
10
+ response?: Response;
11
+ error?: unknown;
12
+ };
6
13
  type CircuitPluginOptions = {
7
14
  threshold: number;
8
15
  reset: number;
9
- onCircuitOpen?: (req: Request) => void | Promise<void>;
10
- onCircuitClose?: (req: Request) => void | Promise<void>;
16
+ onCircuitOpen?: (ctx: {
17
+ request: Request;
18
+ reason: CircuitOpenReason;
19
+ }) => void | Promise<void>;
20
+ onCircuitClose?: (ctx: {
21
+ request: Request;
22
+ response: Response;
23
+ }) => void | Promise<void>;
11
24
  order?: number;
12
25
  };
13
26
  declare function circuitPlugin(options: CircuitPluginOptions): ClientPlugin<CircuitPluginExtension>;
14
27
 
15
- export { type CircuitPluginExtension, type CircuitPluginOptions, circuitPlugin };
28
+ export { type CircuitOpenReason, type CircuitPluginExtension, type CircuitPluginOptions, circuitPlugin };
@@ -3,13 +3,26 @@ import { C as ClientPlugin } from '../plugins-DqBQVM4I.js';
3
3
  type CircuitPluginExtension = {
4
4
  circuitOpen: boolean;
5
5
  };
6
+ type CircuitOpenReason = {
7
+ type: 'already-open';
8
+ } | {
9
+ type: 'threshold-reached';
10
+ response?: Response;
11
+ error?: unknown;
12
+ };
6
13
  type CircuitPluginOptions = {
7
14
  threshold: number;
8
15
  reset: number;
9
- onCircuitOpen?: (req: Request) => void | Promise<void>;
10
- onCircuitClose?: (req: Request) => void | Promise<void>;
16
+ onCircuitOpen?: (ctx: {
17
+ request: Request;
18
+ reason: CircuitOpenReason;
19
+ }) => void | Promise<void>;
20
+ onCircuitClose?: (ctx: {
21
+ request: Request;
22
+ response: Response;
23
+ }) => void | Promise<void>;
11
24
  order?: number;
12
25
  };
13
26
  declare function circuitPlugin(options: CircuitPluginOptions): ClientPlugin<CircuitPluginExtension>;
14
27
 
15
- export { type CircuitPluginExtension, type CircuitPluginOptions, circuitPlugin };
28
+ export { type CircuitOpenReason, type CircuitPluginExtension, type CircuitPluginOptions, circuitPlugin };
@@ -1,6 +1,6 @@
1
1
  import {
2
2
  CircuitOpenError
3
- } from "../chunk-RV6LC73N.js";
3
+ } from "../chunk-ZIJ2DZYZ.js";
4
4
 
5
5
  // src/plugins/circuit.ts
6
6
  function circuitPlugin(options) {
@@ -23,20 +23,23 @@ function circuitPlugin(options) {
23
23
  }
24
24
  return false;
25
25
  };
26
- const onSuccess = async (req) => {
26
+ const onSuccess = async (req, response) => {
27
27
  const wasOpen = isOpen;
28
28
  failures = 0;
29
29
  if (wasOpen) {
30
30
  isOpen = false;
31
- await onCircuitClose?.(req);
31
+ await onCircuitClose?.({ request: req, response });
32
32
  }
33
33
  };
34
- const onFailure = async (req) => {
34
+ const onFailure = async (req, reason) => {
35
35
  failures++;
36
36
  if (failures >= threshold) {
37
37
  nextAttempt = Date.now() + reset;
38
38
  isOpen = true;
39
- await onCircuitOpen?.(req);
39
+ await onCircuitOpen?.({
40
+ request: req,
41
+ reason: { type: "threshold-reached", ...reason }
42
+ });
40
43
  return true;
41
44
  }
42
45
  return false;
@@ -52,18 +55,21 @@ function circuitPlugin(options) {
52
55
  },
53
56
  preRequest: async (ctx) => {
54
57
  if (Date.now() < nextAttempt) {
55
- await onCircuitOpen?.(ctx.request);
58
+ await onCircuitOpen?.({
59
+ request: ctx.request,
60
+ reason: { type: "already-open" }
61
+ });
56
62
  throw new CircuitOpenError("Circuit is open");
57
63
  }
58
64
  },
59
65
  onSuccess: async (ctx, response) => {
60
66
  if (shouldCountFailure(response, void 0)) {
61
- const opened = await onFailure(ctx.request);
67
+ const opened = await onFailure(ctx.request, { response });
62
68
  if (opened) {
63
69
  throw new CircuitOpenError("Circuit is open");
64
70
  }
65
71
  } else {
66
- await onSuccess(ctx.request);
72
+ await onSuccess(ctx.request, response);
67
73
  }
68
74
  },
69
75
  onError: async (ctx, error) => {
@@ -71,7 +77,7 @@ function circuitPlugin(options) {
71
77
  return;
72
78
  }
73
79
  if (shouldCountFailure(void 0, error)) {
74
- const opened = await onFailure(ctx.request);
80
+ const opened = await onFailure(ctx.request, { error });
75
81
  if (opened) {
76
82
  throw new CircuitOpenError("Circuit is open");
77
83
  }
@@ -1 +1 @@
1
- {"version":3,"sources":["../../src/plugins/circuit.ts"],"sourcesContent":["import type { ClientPlugin } from '../plugins.js'\r\nimport { CircuitOpenError } from '../error.js'\r\n\r\nexport type CircuitPluginExtension = {\r\n circuitOpen: boolean\r\n}\r\n\r\nexport type CircuitPluginOptions = {\r\n threshold: number\r\n reset: number\r\n onCircuitOpen?: (req: Request) => void | Promise<void>\r\n onCircuitClose?: (req: Request) => void | Promise<void>\r\n order?: number\r\n}\r\n\r\nexport function circuitPlugin(\r\n options: CircuitPluginOptions\r\n): ClientPlugin<CircuitPluginExtension> {\r\n const {\r\n threshold,\r\n reset,\r\n onCircuitOpen,\r\n onCircuitClose,\r\n order = 20,\r\n } = options\r\n\r\n let failures = 0\r\n let nextAttempt = 0\r\n let isOpen = false\r\n\r\n const shouldCountFailure = (\r\n response?: Response,\r\n error?: unknown\r\n ): boolean => {\r\n if (error) {\r\n return true\r\n }\r\n if (response && (response.status >= 500 || response.status === 429)) {\r\n return true\r\n }\r\n return false\r\n }\r\n\r\n const onSuccess = async (req: Request) => {\r\n const wasOpen = isOpen\r\n failures = 0\r\n if (wasOpen) {\r\n isOpen = false\r\n await onCircuitClose?.(req)\r\n }\r\n }\r\n\r\n const onFailure = async (req: Request): Promise<boolean> => {\r\n failures++\r\n if (failures >= threshold) {\r\n nextAttempt = Date.now() + reset\r\n isOpen = true\r\n await onCircuitOpen?.(req)\r\n return true\r\n }\r\n return false\r\n }\r\n\r\n return {\r\n name: 'circuit',\r\n order,\r\n setup: ({ defineExtension }) => {\r\n defineExtension('circuitOpen', {\r\n get: () => isOpen,\r\n enumerable: true,\r\n })\r\n },\r\n preRequest: async (ctx) => {\r\n if (Date.now() < nextAttempt) {\r\n await onCircuitOpen?.(ctx.request)\r\n throw new CircuitOpenError('Circuit is open')\r\n }\r\n },\r\n onSuccess: async (ctx, response) => {\r\n if (shouldCountFailure(response, undefined)) {\r\n const opened = await onFailure(ctx.request)\r\n if (opened) {\r\n throw new CircuitOpenError('Circuit is open')\r\n }\r\n } else {\r\n await onSuccess(ctx.request)\r\n }\r\n },\r\n onError: async (ctx, error) => {\r\n if (error instanceof CircuitOpenError) {\r\n return\r\n }\r\n if (shouldCountFailure(undefined, error)) {\r\n const opened = await onFailure(ctx.request)\r\n if (opened) {\r\n throw new CircuitOpenError('Circuit is open')\r\n }\r\n }\r\n },\r\n }\r\n}\r\n"],"mappings":";;;;;AAeO,SAAS,cACd,SACsC;AACtC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV,IAAI;AAEJ,MAAI,WAAW;AACf,MAAI,cAAc;AAClB,MAAI,SAAS;AAEb,QAAM,qBAAqB,CACzB,UACA,UACY;AACZ,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AACA,QAAI,aAAa,SAAS,UAAU,OAAO,SAAS,WAAW,MAAM;AACnE,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,OAAO,QAAiB;AACxC,UAAM,UAAU;AAChB,eAAW;AACX,QAAI,SAAS;AACX,eAAS;AACT,YAAM,iBAAiB,GAAG;AAAA,IAC5B;AAAA,EACF;AAEA,QAAM,YAAY,OAAO,QAAmC;AAC1D;AACA,QAAI,YAAY,WAAW;AACzB,oBAAc,KAAK,IAAI,IAAI;AAC3B,eAAS;AACT,YAAM,gBAAgB,GAAG;AACzB,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,CAAC,EAAE,gBAAgB,MAAM;AAC9B,sBAAgB,eAAe;AAAA,QAC7B,KAAK,MAAM;AAAA,QACX,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,IACA,YAAY,OAAO,QAAQ;AACzB,UAAI,KAAK,IAAI,IAAI,aAAa;AAC5B,cAAM,gBAAgB,IAAI,OAAO;AACjC,cAAM,IAAI,iBAAiB,iBAAiB;AAAA,MAC9C;AAAA,IACF;AAAA,IACA,WAAW,OAAO,KAAK,aAAa;AAClC,UAAI,mBAAmB,UAAU,MAAS,GAAG;AAC3C,cAAM,SAAS,MAAM,UAAU,IAAI,OAAO;AAC1C,YAAI,QAAQ;AACV,gBAAM,IAAI,iBAAiB,iBAAiB;AAAA,QAC9C;AAAA,MACF,OAAO;AACL,cAAM,UAAU,IAAI,OAAO;AAAA,MAC7B;AAAA,IACF;AAAA,IACA,SAAS,OAAO,KAAK,UAAU;AAC7B,UAAI,iBAAiB,kBAAkB;AACrC;AAAA,MACF;AACA,UAAI,mBAAmB,QAAW,KAAK,GAAG;AACxC,cAAM,SAAS,MAAM,UAAU,IAAI,OAAO;AAC1C,YAAI,QAAQ;AACV,gBAAM,IAAI,iBAAiB,iBAAiB;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}
1
+ {"version":3,"sources":["../../src/plugins/circuit.ts"],"sourcesContent":["import type { ClientPlugin } from '../plugins.js'\nimport { CircuitOpenError } from '../error.js'\n\nexport type CircuitPluginExtension = {\n circuitOpen: boolean\n}\n\nexport type CircuitOpenReason =\n | { type: 'already-open' }\n | {\n type: 'threshold-reached'\n response?: Response\n error?: unknown\n }\n\nexport type CircuitPluginOptions = {\n threshold: number\n reset: number\n onCircuitOpen?: (ctx: {\n request: Request\n reason: CircuitOpenReason\n }) => void | Promise<void>\n onCircuitClose?: (ctx: {\n request: Request\n response: Response\n }) => void | Promise<void>\n order?: number\n}\n\nexport function circuitPlugin(\n options: CircuitPluginOptions\n): ClientPlugin<CircuitPluginExtension> {\n const {\n threshold,\n reset,\n onCircuitOpen,\n onCircuitClose,\n order = 20,\n } = options\n\n let failures = 0\n let nextAttempt = 0\n let isOpen = false\n\n const shouldCountFailure = (\n response?: Response,\n error?: unknown\n ): boolean => {\n if (error) {\n return true\n }\n if (response && (response.status >= 500 || response.status === 429)) {\n return true\n }\n return false\n }\n\n const onSuccess = async (req: Request, response: Response) => {\n const wasOpen = isOpen\n failures = 0\n if (wasOpen) {\n isOpen = false\n await onCircuitClose?.({ request: req, response })\n }\n }\n\n const onFailure = async (\n req: Request,\n reason: Omit<\n Extract<CircuitOpenReason, { type: 'threshold-reached' }>,\n 'type'\n >\n ): Promise<boolean> => {\n failures++\n if (failures >= threshold) {\n nextAttempt = Date.now() + reset\n isOpen = true\n await onCircuitOpen?.({\n request: req,\n reason: { type: 'threshold-reached', ...reason },\n })\n return true\n }\n return false\n }\n\n return {\n name: 'circuit',\n order,\n setup: ({ defineExtension }) => {\n defineExtension('circuitOpen', {\n get: () => isOpen,\n enumerable: true,\n })\n },\n preRequest: async (ctx) => {\n if (Date.now() < nextAttempt) {\n await onCircuitOpen?.({\n request: ctx.request,\n reason: { type: 'already-open' },\n })\n throw new CircuitOpenError('Circuit is open')\n }\n },\n onSuccess: async (ctx, response) => {\n if (shouldCountFailure(response, undefined)) {\n const opened = await onFailure(ctx.request, { response })\n if (opened) {\n throw new CircuitOpenError('Circuit is open')\n }\n } else {\n await onSuccess(ctx.request, response)\n }\n },\n onError: async (ctx, error) => {\n if (error instanceof CircuitOpenError) {\n return\n }\n if (shouldCountFailure(undefined, error)) {\n const opened = await onFailure(ctx.request, { error })\n if (opened) {\n throw new CircuitOpenError('Circuit is open')\n }\n }\n },\n }\n}\n"],"mappings":";;;;;AA6BO,SAAS,cACd,SACsC;AACtC,QAAM;AAAA,IACJ;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA,QAAQ;AAAA,EACV,IAAI;AAEJ,MAAI,WAAW;AACf,MAAI,cAAc;AAClB,MAAI,SAAS;AAEb,QAAM,qBAAqB,CACzB,UACA,UACY;AACZ,QAAI,OAAO;AACT,aAAO;AAAA,IACT;AACA,QAAI,aAAa,SAAS,UAAU,OAAO,SAAS,WAAW,MAAM;AACnE,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,QAAM,YAAY,OAAO,KAAc,aAAuB;AAC5D,UAAM,UAAU;AAChB,eAAW;AACX,QAAI,SAAS;AACX,eAAS;AACT,YAAM,iBAAiB,EAAE,SAAS,KAAK,SAAS,CAAC;AAAA,IACnD;AAAA,EACF;AAEA,QAAM,YAAY,OAChB,KACA,WAIqB;AACrB;AACA,QAAI,YAAY,WAAW;AACzB,oBAAc,KAAK,IAAI,IAAI;AAC3B,eAAS;AACT,YAAM,gBAAgB;AAAA,QACpB,SAAS;AAAA,QACT,QAAQ,EAAE,MAAM,qBAAqB,GAAG,OAAO;AAAA,MACjD,CAAC;AACD,aAAO;AAAA,IACT;AACA,WAAO;AAAA,EACT;AAEA,SAAO;AAAA,IACL,MAAM;AAAA,IACN;AAAA,IACA,OAAO,CAAC,EAAE,gBAAgB,MAAM;AAC9B,sBAAgB,eAAe;AAAA,QAC7B,KAAK,MAAM;AAAA,QACX,YAAY;AAAA,MACd,CAAC;AAAA,IACH;AAAA,IACA,YAAY,OAAO,QAAQ;AACzB,UAAI,KAAK,IAAI,IAAI,aAAa;AAC5B,cAAM,gBAAgB;AAAA,UACpB,SAAS,IAAI;AAAA,UACb,QAAQ,EAAE,MAAM,eAAe;AAAA,QACjC,CAAC;AACD,cAAM,IAAI,iBAAiB,iBAAiB;AAAA,MAC9C;AAAA,IACF;AAAA,IACA,WAAW,OAAO,KAAK,aAAa;AAClC,UAAI,mBAAmB,UAAU,MAAS,GAAG;AAC3C,cAAM,SAAS,MAAM,UAAU,IAAI,SAAS,EAAE,SAAS,CAAC;AACxD,YAAI,QAAQ;AACV,gBAAM,IAAI,iBAAiB,iBAAiB;AAAA,QAC9C;AAAA,MACF,OAAO;AACL,cAAM,UAAU,IAAI,SAAS,QAAQ;AAAA,MACvC;AAAA,IACF;AAAA,IACA,SAAS,OAAO,KAAK,UAAU;AAC7B,UAAI,iBAAiB,kBAAkB;AACrC;AAAA,MACF;AACA,UAAI,mBAAmB,QAAW,KAAK,GAAG;AACxC,cAAM,SAAS,MAAM,UAAU,IAAI,SAAS,EAAE,MAAM,CAAC;AACrD,YAAI,QAAQ;AACV,gBAAM,IAAI,iBAAiB,iBAAiB;AAAA,QAC9C;AAAA,MACF;AAAA,IACF;AAAA,EACF;AACF;","names":[]}