fastfetch-api-fetch-enhancer 2.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.
Files changed (72) hide show
  1. package/.idea/FastFetch-Smart-API-Fetcher.iml +12 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/vcs.xml +6 -0
  4. package/LICENSE +21 -0
  5. package/README.md +101 -0
  6. package/__tests__/demo.test.ts +216 -0
  7. package/__tests__/test_database.json +5002 -0
  8. package/coverage/clover.xml +463 -0
  9. package/coverage/coverage-final.json +11 -0
  10. package/coverage/lcov-report/base.css +224 -0
  11. package/coverage/lcov-report/block-navigation.js +87 -0
  12. package/coverage/lcov-report/circuit-breaker.ts.html +547 -0
  13. package/coverage/lcov-report/client.ts.html +1858 -0
  14. package/coverage/lcov-report/errors.ts.html +415 -0
  15. package/coverage/lcov-report/fastFetch.ts.html +1045 -0
  16. package/coverage/lcov-report/favicon.png +0 -0
  17. package/coverage/lcov-report/index.html +251 -0
  18. package/coverage/lcov-report/index.ts.html +241 -0
  19. package/coverage/lcov-report/metrics.ts.html +685 -0
  20. package/coverage/lcov-report/middleware.ts.html +403 -0
  21. package/coverage/lcov-report/offline-queue.ts.html +535 -0
  22. package/coverage/lcov-report/prettify.css +1 -0
  23. package/coverage/lcov-report/prettify.js +2 -0
  24. package/coverage/lcov-report/queue.ts.html +421 -0
  25. package/coverage/lcov-report/sort-arrow-sprite.png +0 -0
  26. package/coverage/lcov-report/sorter.js +196 -0
  27. package/coverage/lcov-report/streaming.ts.html +466 -0
  28. package/coverage/lcov.info +908 -0
  29. package/dist/circuit-breaker.d.ts +61 -0
  30. package/dist/circuit-breaker.d.ts.map +1 -0
  31. package/dist/circuit-breaker.js +106 -0
  32. package/dist/client.d.ts +215 -0
  33. package/dist/client.d.ts.map +1 -0
  34. package/dist/client.js +391 -0
  35. package/dist/errors.d.ts +56 -0
  36. package/dist/errors.d.ts.map +1 -0
  37. package/dist/errors.js +91 -0
  38. package/dist/fastFetch.d.ts +65 -0
  39. package/dist/fastFetch.d.ts.map +1 -0
  40. package/dist/fastFetch.js +209 -0
  41. package/dist/index.d.ts +18 -0
  42. package/dist/index.d.ts.map +1 -0
  43. package/dist/index.js +18 -0
  44. package/dist/metrics.d.ts +71 -0
  45. package/dist/metrics.d.ts.map +1 -0
  46. package/dist/metrics.js +131 -0
  47. package/dist/middleware.d.ts +66 -0
  48. package/dist/middleware.d.ts.map +1 -0
  49. package/dist/middleware.js +45 -0
  50. package/dist/offline-queue.d.ts +65 -0
  51. package/dist/offline-queue.d.ts.map +1 -0
  52. package/dist/offline-queue.js +120 -0
  53. package/dist/queue.d.ts +33 -0
  54. package/dist/queue.d.ts.map +1 -0
  55. package/dist/queue.js +76 -0
  56. package/dist/streaming.d.ts +40 -0
  57. package/dist/streaming.d.ts.map +1 -0
  58. package/dist/streaming.js +98 -0
  59. package/index.d.ts +167 -0
  60. package/jest.config.js +16 -0
  61. package/package.json +55 -0
  62. package/src/circuit-breaker.ts +154 -0
  63. package/src/client.ts +591 -0
  64. package/src/errors.ts +110 -0
  65. package/src/fastFetch.ts +320 -0
  66. package/src/index.ts +52 -0
  67. package/src/metrics.ts +200 -0
  68. package/src/middleware.ts +106 -0
  69. package/src/offline-queue.ts +150 -0
  70. package/src/queue.ts +112 -0
  71. package/src/streaming.ts +127 -0
  72. package/tsconfig.json +18 -0
@@ -0,0 +1,154 @@
1
+ /**
2
+ * FastFetch Circuit Breaker — Finite State Machine implementation.
3
+ *
4
+ * Prevents cascading failures by "opening" the circuit after a configurable
5
+ * number of consecutive failures, then probing with a single request after
6
+ * the reset timeout before fully re-closing.
7
+ *
8
+ * States:
9
+ * CLOSED — normal operation; every request goes through
10
+ * OPEN — requests are immediately rejected (CircuitOpenError)
11
+ * HALF_OPEN — one probe request is allowed; success → CLOSED, failure → OPEN
12
+ */
13
+
14
+ import { CircuitOpenError } from "./errors.js";
15
+
16
+ export type CircuitState = "CLOSED" | "OPEN" | "HALF_OPEN";
17
+
18
+ export interface CircuitBreakerOptions {
19
+ /**
20
+ * Number of consecutive failures before the circuit opens (default: 5).
21
+ */
22
+ threshold?: number;
23
+ /**
24
+ * Milliseconds to wait in the OPEN state before transitioning to HALF_OPEN
25
+ * and allowing a single probe request (default: 30 000 ms = 30 s).
26
+ */
27
+ timeout?: number;
28
+ /** Called when the circuit transitions CLOSED → OPEN. */
29
+ onOpen?: () => void;
30
+ /** Called when the circuit transitions HALF_OPEN → CLOSED (recovered). */
31
+ onClose?: () => void;
32
+ /** Called when the circuit transitions OPEN → HALF_OPEN (probing). */
33
+ onHalfOpen?: () => void;
34
+ }
35
+
36
+ export class CircuitBreaker {
37
+ private _state: CircuitState = "CLOSED";
38
+ private consecutiveFailures = 0;
39
+ private openedAt = 0;
40
+ private probeInFlight = false;
41
+
42
+ private readonly threshold: number;
43
+ private readonly timeoutMs: number;
44
+ private readonly onOpen?: () => void;
45
+ private readonly onClose?: () => void;
46
+ private readonly onHalfOpen?: () => void;
47
+
48
+ constructor(options: CircuitBreakerOptions = {}) {
49
+ this.threshold = options.threshold ?? 5;
50
+ this.timeoutMs = options.timeout ?? 30_000;
51
+ this.onOpen = options.onOpen;
52
+ this.onClose = options.onClose;
53
+ this.onHalfOpen = options.onHalfOpen;
54
+ }
55
+
56
+ // ── State accessor ───────────────────────────────────────────────────────
57
+
58
+ /**
59
+ * Current circuit state. Reading this property can trigger an automatic
60
+ * OPEN → HALF_OPEN transition when the reset timeout has elapsed.
61
+ */
62
+ get state(): CircuitState {
63
+ if (
64
+ this._state === "OPEN" &&
65
+ Date.now() - this.openedAt >= this.timeoutMs
66
+ ) {
67
+ this._state = "HALF_OPEN";
68
+ this.probeInFlight = false;
69
+ this.onHalfOpen?.();
70
+ }
71
+ return this._state;
72
+ }
73
+
74
+ // ── Core execute ─────────────────────────────────────────────────────────
75
+
76
+ /**
77
+ * Execute `fn` through the circuit breaker.
78
+ * - CLOSED → run `fn`, track success/failure
79
+ * - OPEN → throw `CircuitOpenError` immediately (no network call)
80
+ * - HALF_OPEN → allow one probe; success closes, failure re-opens
81
+ */
82
+ async execute<T>(fn: () => Promise<T>): Promise<T> {
83
+ const currentState = this.state; // may trigger OPEN→HALF_OPEN
84
+
85
+ if (currentState === "OPEN") {
86
+ throw new CircuitOpenError();
87
+ }
88
+
89
+ if (currentState === "HALF_OPEN") {
90
+ // Reject parallel probes — only one probe allowed at a time
91
+ if (this.probeInFlight) {
92
+ throw new CircuitOpenError();
93
+ }
94
+ this.probeInFlight = true;
95
+ }
96
+
97
+ try {
98
+ const result = await fn();
99
+ this.recordSuccess();
100
+ return result;
101
+ } catch (err) {
102
+ this.recordFailure();
103
+ throw err;
104
+ }
105
+ }
106
+
107
+ // ── Private FSM transitions ──────────────────────────────────────────────
108
+
109
+ private recordSuccess(): void {
110
+ this.consecutiveFailures = 0;
111
+ this.probeInFlight = false;
112
+
113
+ if (this._state === "HALF_OPEN") {
114
+ this._state = "CLOSED";
115
+ this.onClose?.();
116
+ }
117
+ }
118
+
119
+ private recordFailure(): void {
120
+ this.consecutiveFailures++;
121
+ this.probeInFlight = false;
122
+
123
+ if (this._state === "HALF_OPEN") {
124
+ // Probe failed — re-open the circuit
125
+ this._state = "OPEN";
126
+ this.openedAt = Date.now();
127
+ return;
128
+ }
129
+
130
+ if (
131
+ this._state === "CLOSED" &&
132
+ this.consecutiveFailures >= this.threshold
133
+ ) {
134
+ this._state = "OPEN";
135
+ this.openedAt = Date.now();
136
+ this.onOpen?.();
137
+ }
138
+ }
139
+
140
+ // ── Manual controls ──────────────────────────────────────────────────────
141
+
142
+ /** Manually reset the circuit to CLOSED. Useful for testing. */
143
+ reset(): void {
144
+ this._state = "CLOSED";
145
+ this.consecutiveFailures = 0;
146
+ this.openedAt = 0;
147
+ this.probeInFlight = false;
148
+ }
149
+
150
+ /** Number of consecutive failures recorded since last success. */
151
+ get failures(): number {
152
+ return this.consecutiveFailures;
153
+ }
154
+ }