@stackguide/mcp-server 3.8.2 → 3.10.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/handlers/review.d.ts +2 -1
- package/dist/handlers/review.d.ts.map +1 -1
- package/dist/handlers/review.js +75 -10
- package/dist/handlers/review.js.map +1 -1
- package/dist/services/codeAnalyzer.d.ts +24 -1
- package/dist/services/codeAnalyzer.d.ts.map +1 -1
- package/dist/services/codeAnalyzer.js +57 -2
- package/dist/services/codeAnalyzer.js.map +1 -1
- package/dist/services/cursorDirectory.d.ts.map +1 -1
- package/dist/services/cursorDirectory.js +45 -10
- package/dist/services/cursorDirectory.js.map +1 -1
- package/dist/services/httpClient.d.ts +33 -2
- package/dist/services/httpClient.d.ts.map +1 -1
- package/dist/services/httpClient.js +117 -17
- package/dist/services/httpClient.js.map +1 -1
- package/dist/services/projectFs.d.ts.map +1 -1
- package/dist/services/projectFs.js +25 -4
- package/dist/services/projectFs.js.map +1 -1
- package/dist/storage/sqlite.d.ts +1 -1
- package/dist/storage/sqlite.d.ts.map +1 -1
- package/dist/storage/sqlite.js +200 -24
- package/dist/storage/sqlite.js.map +1 -1
- package/dist/storage/types.d.ts +32 -2
- package/dist/storage/types.d.ts.map +1 -1
- package/dist/storage/types.js +4 -2
- package/dist/storage/types.js.map +1 -1
- package/dist/utils/circuitBreaker.d.ts +144 -0
- package/dist/utils/circuitBreaker.d.ts.map +1 -0
- package/dist/utils/circuitBreaker.js +329 -0
- package/dist/utils/circuitBreaker.js.map +1 -0
- package/dist/utils/resilienceMetrics.d.ts +104 -0
- package/dist/utils/resilienceMetrics.d.ts.map +1 -0
- package/dist/utils/resilienceMetrics.js +260 -0
- package/dist/utils/resilienceMetrics.js.map +1 -0
- package/dist/validation/schemas.d.ts +3 -1
- package/dist/validation/schemas.d.ts.map +1 -1
- package/dist/validation/schemas.js +35 -5
- package/dist/validation/schemas.js.map +1 -1
- package/package.json +1 -1
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Breaker Pattern Implementation
|
|
3
|
+
* Protects against cascading failures in external service calls
|
|
4
|
+
* @version 3.8.2
|
|
5
|
+
*/
|
|
6
|
+
export type CircuitState = 'CLOSED' | 'OPEN' | 'HALF_OPEN';
|
|
7
|
+
export interface CircuitBreakerOptions {
|
|
8
|
+
/** Name for logging/metrics */
|
|
9
|
+
name: string;
|
|
10
|
+
/** Number of failures before opening circuit */
|
|
11
|
+
failureThreshold?: number;
|
|
12
|
+
/** Time in ms before attempting recovery (half-open) */
|
|
13
|
+
resetTimeoutMs?: number;
|
|
14
|
+
/** Number of successful calls in half-open to close circuit */
|
|
15
|
+
successThreshold?: number;
|
|
16
|
+
/** Time window in ms for counting failures */
|
|
17
|
+
failureWindowMs?: number;
|
|
18
|
+
/** Timeout for individual calls */
|
|
19
|
+
callTimeoutMs?: number;
|
|
20
|
+
/** Custom function to determine if error should count as failure */
|
|
21
|
+
isFailure?: (error: Error) => boolean;
|
|
22
|
+
/** Callback when circuit opens */
|
|
23
|
+
onOpen?: (name: string, failures: number) => void;
|
|
24
|
+
/** Callback when circuit closes */
|
|
25
|
+
onClose?: (name: string) => void;
|
|
26
|
+
/** Callback when circuit half-opens */
|
|
27
|
+
onHalfOpen?: (name: string) => void;
|
|
28
|
+
}
|
|
29
|
+
export interface CircuitBreakerMetrics {
|
|
30
|
+
name: string;
|
|
31
|
+
state: CircuitState;
|
|
32
|
+
failures: number;
|
|
33
|
+
successes: number;
|
|
34
|
+
totalCalls: number;
|
|
35
|
+
rejectedCalls: number;
|
|
36
|
+
lastFailure: string | null;
|
|
37
|
+
lastSuccess: string | null;
|
|
38
|
+
lastStateChange: string;
|
|
39
|
+
consecutiveSuccesses: number;
|
|
40
|
+
failureRate: number;
|
|
41
|
+
}
|
|
42
|
+
export declare class CircuitBreaker {
|
|
43
|
+
private state;
|
|
44
|
+
private failures;
|
|
45
|
+
private successes;
|
|
46
|
+
private totalCalls;
|
|
47
|
+
private rejectedCalls;
|
|
48
|
+
private consecutiveSuccesses;
|
|
49
|
+
private lastFailure;
|
|
50
|
+
private lastSuccess;
|
|
51
|
+
private lastStateChange;
|
|
52
|
+
private openedAt;
|
|
53
|
+
private readonly name;
|
|
54
|
+
private readonly failureThreshold;
|
|
55
|
+
private readonly resetTimeoutMs;
|
|
56
|
+
private readonly successThreshold;
|
|
57
|
+
private readonly failureWindowMs;
|
|
58
|
+
private readonly callTimeoutMs;
|
|
59
|
+
private readonly isFailure;
|
|
60
|
+
private readonly onOpen?;
|
|
61
|
+
private readonly onClose?;
|
|
62
|
+
private readonly onHalfOpen?;
|
|
63
|
+
constructor(options: CircuitBreakerOptions);
|
|
64
|
+
/**
|
|
65
|
+
* Execute a function through the circuit breaker
|
|
66
|
+
*/
|
|
67
|
+
execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
68
|
+
/**
|
|
69
|
+
* Get current circuit state
|
|
70
|
+
*/
|
|
71
|
+
getState(): CircuitState;
|
|
72
|
+
/**
|
|
73
|
+
* Get circuit breaker metrics
|
|
74
|
+
*/
|
|
75
|
+
getMetrics(): CircuitBreakerMetrics;
|
|
76
|
+
/**
|
|
77
|
+
* Manually reset the circuit breaker
|
|
78
|
+
*/
|
|
79
|
+
reset(): void;
|
|
80
|
+
/**
|
|
81
|
+
* Force the circuit open (for testing or emergency)
|
|
82
|
+
*/
|
|
83
|
+
forceOpen(): void;
|
|
84
|
+
private onSuccess;
|
|
85
|
+
private onError;
|
|
86
|
+
private transitionTo;
|
|
87
|
+
private shouldAttemptReset;
|
|
88
|
+
private getRemainingResetTime;
|
|
89
|
+
private getRecentFailures;
|
|
90
|
+
private pruneOldFailures;
|
|
91
|
+
}
|
|
92
|
+
export declare class CircuitOpenError extends Error {
|
|
93
|
+
readonly circuitName: string;
|
|
94
|
+
readonly remainingMs: number;
|
|
95
|
+
constructor(circuitName: string, remainingMs: number);
|
|
96
|
+
}
|
|
97
|
+
declare class CircuitBreakerRegistry {
|
|
98
|
+
private circuits;
|
|
99
|
+
/**
|
|
100
|
+
* Get or create a circuit breaker by name
|
|
101
|
+
*/
|
|
102
|
+
getOrCreate(options: CircuitBreakerOptions): CircuitBreaker;
|
|
103
|
+
/**
|
|
104
|
+
* Get circuit breaker by name
|
|
105
|
+
*/
|
|
106
|
+
get(name: string): CircuitBreaker | undefined;
|
|
107
|
+
/**
|
|
108
|
+
* Get all circuit breakers
|
|
109
|
+
*/
|
|
110
|
+
getAll(): CircuitBreaker[];
|
|
111
|
+
/**
|
|
112
|
+
* Get metrics for all circuits
|
|
113
|
+
*/
|
|
114
|
+
getAllMetrics(): CircuitBreakerMetrics[];
|
|
115
|
+
/**
|
|
116
|
+
* Get health summary
|
|
117
|
+
*/
|
|
118
|
+
getHealthSummary(): {
|
|
119
|
+
total: number;
|
|
120
|
+
closed: number;
|
|
121
|
+
open: number;
|
|
122
|
+
halfOpen: number;
|
|
123
|
+
healthy: boolean;
|
|
124
|
+
};
|
|
125
|
+
/**
|
|
126
|
+
* Reset all circuit breakers
|
|
127
|
+
*/
|
|
128
|
+
resetAll(): void;
|
|
129
|
+
/**
|
|
130
|
+
* Clear all circuit breakers
|
|
131
|
+
*/
|
|
132
|
+
clear(): void;
|
|
133
|
+
}
|
|
134
|
+
export declare const circuitBreakerRegistry: CircuitBreakerRegistry;
|
|
135
|
+
/**
|
|
136
|
+
* Create a circuit breaker with default options for external services
|
|
137
|
+
*/
|
|
138
|
+
export declare function createServiceCircuitBreaker(serviceName: string, options?: Partial<CircuitBreakerOptions>): CircuitBreaker;
|
|
139
|
+
/**
|
|
140
|
+
* Wrap an async function with circuit breaker protection
|
|
141
|
+
*/
|
|
142
|
+
export declare function withCircuitBreaker<T extends (...args: unknown[]) => Promise<unknown>>(fn: T, circuitName: string, options?: Partial<CircuitBreakerOptions>): T;
|
|
143
|
+
export {};
|
|
144
|
+
//# sourceMappingURL=circuitBreaker.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuitBreaker.d.ts","sourceRoot":"","sources":["../../src/utils/circuitBreaker.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Circuit Breaker Pattern Implementation
|
|
3
|
+
* Protects against cascading failures in external service calls
|
|
4
|
+
* @version 3.8.2
|
|
5
|
+
*/
|
|
6
|
+
import { logger } from './logger.js';
|
|
7
|
+
// ============================================================================
|
|
8
|
+
// Circuit Breaker Implementation
|
|
9
|
+
// ============================================================================
|
|
10
|
+
export class CircuitBreaker {
|
|
11
|
+
state = 'CLOSED';
|
|
12
|
+
failures = [];
|
|
13
|
+
successes = 0;
|
|
14
|
+
totalCalls = 0;
|
|
15
|
+
rejectedCalls = 0;
|
|
16
|
+
consecutiveSuccesses = 0;
|
|
17
|
+
lastFailure = null;
|
|
18
|
+
lastSuccess = null;
|
|
19
|
+
lastStateChange = new Date();
|
|
20
|
+
openedAt = null;
|
|
21
|
+
name;
|
|
22
|
+
failureThreshold;
|
|
23
|
+
resetTimeoutMs;
|
|
24
|
+
successThreshold;
|
|
25
|
+
failureWindowMs;
|
|
26
|
+
callTimeoutMs;
|
|
27
|
+
isFailure;
|
|
28
|
+
onOpen;
|
|
29
|
+
onClose;
|
|
30
|
+
onHalfOpen;
|
|
31
|
+
constructor(options) {
|
|
32
|
+
this.name = options.name;
|
|
33
|
+
this.failureThreshold = options.failureThreshold ?? 5;
|
|
34
|
+
this.resetTimeoutMs = options.resetTimeoutMs ?? 30000; // 30 seconds
|
|
35
|
+
this.successThreshold = options.successThreshold ?? 2;
|
|
36
|
+
this.failureWindowMs = options.failureWindowMs ?? 60000; // 1 minute
|
|
37
|
+
this.callTimeoutMs = options.callTimeoutMs ?? 10000; // 10 seconds
|
|
38
|
+
this.isFailure = options.isFailure ?? (() => true);
|
|
39
|
+
this.onOpen = options.onOpen;
|
|
40
|
+
this.onClose = options.onClose;
|
|
41
|
+
this.onHalfOpen = options.onHalfOpen;
|
|
42
|
+
logger.debug('Circuit breaker initialized', { name: this.name, state: this.state });
|
|
43
|
+
}
|
|
44
|
+
/**
|
|
45
|
+
* Execute a function through the circuit breaker
|
|
46
|
+
*/
|
|
47
|
+
async execute(fn) {
|
|
48
|
+
this.totalCalls++;
|
|
49
|
+
// Check if circuit is open
|
|
50
|
+
if (this.state === 'OPEN') {
|
|
51
|
+
if (this.shouldAttemptReset()) {
|
|
52
|
+
this.transitionTo('HALF_OPEN');
|
|
53
|
+
}
|
|
54
|
+
else {
|
|
55
|
+
this.rejectedCalls++;
|
|
56
|
+
throw new CircuitOpenError(this.name, this.getRemainingResetTime());
|
|
57
|
+
}
|
|
58
|
+
}
|
|
59
|
+
// Apply timeout wrapper
|
|
60
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
61
|
+
setTimeout(() => {
|
|
62
|
+
reject(new Error(`Circuit breaker timeout (${this.callTimeoutMs}ms)`));
|
|
63
|
+
}, this.callTimeoutMs);
|
|
64
|
+
});
|
|
65
|
+
try {
|
|
66
|
+
const result = await Promise.race([fn(), timeoutPromise]);
|
|
67
|
+
this.onSuccess();
|
|
68
|
+
return result;
|
|
69
|
+
}
|
|
70
|
+
catch (error) {
|
|
71
|
+
this.onError(error);
|
|
72
|
+
throw error;
|
|
73
|
+
}
|
|
74
|
+
}
|
|
75
|
+
/**
|
|
76
|
+
* Get current circuit state
|
|
77
|
+
*/
|
|
78
|
+
getState() {
|
|
79
|
+
// Check for automatic half-open transition
|
|
80
|
+
if (this.state === 'OPEN' && this.shouldAttemptReset()) {
|
|
81
|
+
this.transitionTo('HALF_OPEN');
|
|
82
|
+
}
|
|
83
|
+
return this.state;
|
|
84
|
+
}
|
|
85
|
+
/**
|
|
86
|
+
* Get circuit breaker metrics
|
|
87
|
+
*/
|
|
88
|
+
getMetrics() {
|
|
89
|
+
const recentFailures = this.getRecentFailures();
|
|
90
|
+
return {
|
|
91
|
+
name: this.name,
|
|
92
|
+
state: this.getState(),
|
|
93
|
+
failures: recentFailures.length,
|
|
94
|
+
successes: this.successes,
|
|
95
|
+
totalCalls: this.totalCalls,
|
|
96
|
+
rejectedCalls: this.rejectedCalls,
|
|
97
|
+
lastFailure: this.lastFailure?.toISOString() ?? null,
|
|
98
|
+
lastSuccess: this.lastSuccess?.toISOString() ?? null,
|
|
99
|
+
lastStateChange: this.lastStateChange.toISOString(),
|
|
100
|
+
consecutiveSuccesses: this.consecutiveSuccesses,
|
|
101
|
+
failureRate: this.totalCalls > 0
|
|
102
|
+
? Math.round((recentFailures.length / Math.min(this.totalCalls, 100)) * 100)
|
|
103
|
+
: 0
|
|
104
|
+
};
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Manually reset the circuit breaker
|
|
108
|
+
*/
|
|
109
|
+
reset() {
|
|
110
|
+
this.failures = [];
|
|
111
|
+
this.consecutiveSuccesses = 0;
|
|
112
|
+
this.transitionTo('CLOSED');
|
|
113
|
+
logger.info('Circuit breaker manually reset', { name: this.name });
|
|
114
|
+
}
|
|
115
|
+
/**
|
|
116
|
+
* Force the circuit open (for testing or emergency)
|
|
117
|
+
*/
|
|
118
|
+
forceOpen() {
|
|
119
|
+
this.transitionTo('OPEN');
|
|
120
|
+
this.openedAt = Date.now();
|
|
121
|
+
logger.warn('Circuit breaker forced open', { name: this.name });
|
|
122
|
+
}
|
|
123
|
+
// ============================================================================
|
|
124
|
+
// Private Methods
|
|
125
|
+
// ============================================================================
|
|
126
|
+
onSuccess() {
|
|
127
|
+
this.successes++;
|
|
128
|
+
this.consecutiveSuccesses++;
|
|
129
|
+
this.lastSuccess = new Date();
|
|
130
|
+
if (this.state === 'HALF_OPEN') {
|
|
131
|
+
if (this.consecutiveSuccesses >= this.successThreshold) {
|
|
132
|
+
this.transitionTo('CLOSED');
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
// Clear old failures on success
|
|
136
|
+
this.pruneOldFailures();
|
|
137
|
+
}
|
|
138
|
+
onError(error) {
|
|
139
|
+
this.lastFailure = new Date();
|
|
140
|
+
this.consecutiveSuccesses = 0;
|
|
141
|
+
// Check if this error counts as a failure
|
|
142
|
+
if (!this.isFailure(error)) {
|
|
143
|
+
return;
|
|
144
|
+
}
|
|
145
|
+
this.failures.push({
|
|
146
|
+
timestamp: Date.now(),
|
|
147
|
+
error: error.message
|
|
148
|
+
});
|
|
149
|
+
this.pruneOldFailures();
|
|
150
|
+
if (this.state === 'HALF_OPEN') {
|
|
151
|
+
// Any failure in half-open state opens the circuit
|
|
152
|
+
this.transitionTo('OPEN');
|
|
153
|
+
}
|
|
154
|
+
else if (this.state === 'CLOSED') {
|
|
155
|
+
// Check if we've exceeded failure threshold
|
|
156
|
+
if (this.getRecentFailures().length >= this.failureThreshold) {
|
|
157
|
+
this.transitionTo('OPEN');
|
|
158
|
+
}
|
|
159
|
+
}
|
|
160
|
+
}
|
|
161
|
+
transitionTo(newState) {
|
|
162
|
+
if (this.state === newState)
|
|
163
|
+
return;
|
|
164
|
+
const oldState = this.state;
|
|
165
|
+
this.state = newState;
|
|
166
|
+
this.lastStateChange = new Date();
|
|
167
|
+
logger.info('Circuit breaker state transition', {
|
|
168
|
+
name: this.name,
|
|
169
|
+
from: oldState,
|
|
170
|
+
to: newState
|
|
171
|
+
});
|
|
172
|
+
switch (newState) {
|
|
173
|
+
case 'OPEN':
|
|
174
|
+
this.openedAt = Date.now();
|
|
175
|
+
this.consecutiveSuccesses = 0;
|
|
176
|
+
this.onOpen?.(this.name, this.getRecentFailures().length);
|
|
177
|
+
break;
|
|
178
|
+
case 'HALF_OPEN':
|
|
179
|
+
this.consecutiveSuccesses = 0;
|
|
180
|
+
this.onHalfOpen?.(this.name);
|
|
181
|
+
break;
|
|
182
|
+
case 'CLOSED':
|
|
183
|
+
this.failures = [];
|
|
184
|
+
this.openedAt = null;
|
|
185
|
+
this.onClose?.(this.name);
|
|
186
|
+
break;
|
|
187
|
+
}
|
|
188
|
+
}
|
|
189
|
+
shouldAttemptReset() {
|
|
190
|
+
if (this.openedAt === null)
|
|
191
|
+
return false;
|
|
192
|
+
return Date.now() - this.openedAt >= this.resetTimeoutMs;
|
|
193
|
+
}
|
|
194
|
+
getRemainingResetTime() {
|
|
195
|
+
if (this.openedAt === null)
|
|
196
|
+
return 0;
|
|
197
|
+
const elapsed = Date.now() - this.openedAt;
|
|
198
|
+
return Math.max(0, this.resetTimeoutMs - elapsed);
|
|
199
|
+
}
|
|
200
|
+
getRecentFailures() {
|
|
201
|
+
const cutoff = Date.now() - this.failureWindowMs;
|
|
202
|
+
return this.failures.filter(f => f.timestamp >= cutoff);
|
|
203
|
+
}
|
|
204
|
+
pruneOldFailures() {
|
|
205
|
+
const cutoff = Date.now() - this.failureWindowMs;
|
|
206
|
+
this.failures = this.failures.filter(f => f.timestamp >= cutoff);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
// ============================================================================
|
|
210
|
+
// Circuit Breaker Error
|
|
211
|
+
// ============================================================================
|
|
212
|
+
export class CircuitOpenError extends Error {
|
|
213
|
+
circuitName;
|
|
214
|
+
remainingMs;
|
|
215
|
+
constructor(circuitName, remainingMs) {
|
|
216
|
+
super(`Circuit breaker '${circuitName}' is OPEN. Retry after ${Math.ceil(remainingMs / 1000)}s`);
|
|
217
|
+
this.name = 'CircuitOpenError';
|
|
218
|
+
this.circuitName = circuitName;
|
|
219
|
+
this.remainingMs = remainingMs;
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
// ============================================================================
|
|
223
|
+
// Circuit Breaker Registry
|
|
224
|
+
// ============================================================================
|
|
225
|
+
class CircuitBreakerRegistry {
|
|
226
|
+
circuits = new Map();
|
|
227
|
+
/**
|
|
228
|
+
* Get or create a circuit breaker by name
|
|
229
|
+
*/
|
|
230
|
+
getOrCreate(options) {
|
|
231
|
+
const existing = this.circuits.get(options.name);
|
|
232
|
+
if (existing) {
|
|
233
|
+
return existing;
|
|
234
|
+
}
|
|
235
|
+
const circuit = new CircuitBreaker(options);
|
|
236
|
+
this.circuits.set(options.name, circuit);
|
|
237
|
+
return circuit;
|
|
238
|
+
}
|
|
239
|
+
/**
|
|
240
|
+
* Get circuit breaker by name
|
|
241
|
+
*/
|
|
242
|
+
get(name) {
|
|
243
|
+
return this.circuits.get(name);
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Get all circuit breakers
|
|
247
|
+
*/
|
|
248
|
+
getAll() {
|
|
249
|
+
return Array.from(this.circuits.values());
|
|
250
|
+
}
|
|
251
|
+
/**
|
|
252
|
+
* Get metrics for all circuits
|
|
253
|
+
*/
|
|
254
|
+
getAllMetrics() {
|
|
255
|
+
return this.getAll().map(c => c.getMetrics());
|
|
256
|
+
}
|
|
257
|
+
/**
|
|
258
|
+
* Get health summary
|
|
259
|
+
*/
|
|
260
|
+
getHealthSummary() {
|
|
261
|
+
const metrics = this.getAllMetrics();
|
|
262
|
+
const closed = metrics.filter(m => m.state === 'CLOSED').length;
|
|
263
|
+
const open = metrics.filter(m => m.state === 'OPEN').length;
|
|
264
|
+
const halfOpen = metrics.filter(m => m.state === 'HALF_OPEN').length;
|
|
265
|
+
return {
|
|
266
|
+
total: metrics.length,
|
|
267
|
+
closed,
|
|
268
|
+
open,
|
|
269
|
+
halfOpen,
|
|
270
|
+
healthy: open === 0
|
|
271
|
+
};
|
|
272
|
+
}
|
|
273
|
+
/**
|
|
274
|
+
* Reset all circuit breakers
|
|
275
|
+
*/
|
|
276
|
+
resetAll() {
|
|
277
|
+
this.circuits.forEach(circuit => circuit.reset());
|
|
278
|
+
logger.info('All circuit breakers reset', { count: this.circuits.size });
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* Clear all circuit breakers
|
|
282
|
+
*/
|
|
283
|
+
clear() {
|
|
284
|
+
this.circuits.clear();
|
|
285
|
+
}
|
|
286
|
+
}
|
|
287
|
+
// ============================================================================
|
|
288
|
+
// Singleton Registry Export
|
|
289
|
+
// ============================================================================
|
|
290
|
+
export const circuitBreakerRegistry = new CircuitBreakerRegistry();
|
|
291
|
+
/**
|
|
292
|
+
* Create a circuit breaker with default options for external services
|
|
293
|
+
*/
|
|
294
|
+
export function createServiceCircuitBreaker(serviceName, options = {}) {
|
|
295
|
+
return circuitBreakerRegistry.getOrCreate({
|
|
296
|
+
name: serviceName,
|
|
297
|
+
failureThreshold: 5,
|
|
298
|
+
resetTimeoutMs: 30000,
|
|
299
|
+
successThreshold: 2,
|
|
300
|
+
failureWindowMs: 60000,
|
|
301
|
+
callTimeoutMs: 10000,
|
|
302
|
+
...options,
|
|
303
|
+
onOpen: (name, failures) => {
|
|
304
|
+
logger.error('Circuit breaker opened', { service: name, failures });
|
|
305
|
+
options.onOpen?.(name, failures);
|
|
306
|
+
},
|
|
307
|
+
onClose: (name) => {
|
|
308
|
+
logger.info('Circuit breaker closed', { service: name });
|
|
309
|
+
options.onClose?.(name);
|
|
310
|
+
},
|
|
311
|
+
onHalfOpen: (name) => {
|
|
312
|
+
logger.info('Circuit breaker half-open', { service: name });
|
|
313
|
+
options.onHalfOpen?.(name);
|
|
314
|
+
}
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
// ============================================================================
|
|
318
|
+
// Decorator/Wrapper Helper
|
|
319
|
+
// ============================================================================
|
|
320
|
+
/**
|
|
321
|
+
* Wrap an async function with circuit breaker protection
|
|
322
|
+
*/
|
|
323
|
+
export function withCircuitBreaker(fn, circuitName, options = {}) {
|
|
324
|
+
const circuit = createServiceCircuitBreaker(circuitName, options);
|
|
325
|
+
return (async (...args) => {
|
|
326
|
+
return circuit.execute(() => fn(...args));
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
//# sourceMappingURL=circuitBreaker.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"circuitBreaker.js","sourceRoot":"","sources":["../../src/utils/circuitBreaker.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAE,MAAM,EAAE,MAAM,aAAa,CAAC;AAkDrC,+EAA+E;AAC/E,iCAAiC;AACjC,+EAA+E;AAE/E,MAAM,OAAO,cAAc;IACjB,KAAK,GAAiB,QAAQ,CAAC;IAC/B,QAAQ,GAAoB,EAAE,CAAC;IAC/B,SAAS,GAAW,CAAC,CAAC;IACtB,UAAU,GAAW,CAAC,CAAC;IACvB,aAAa,GAAW,CAAC,CAAC;IAC1B,oBAAoB,GAAW,CAAC,CAAC;IACjC,WAAW,GAAgB,IAAI,CAAC;IAChC,WAAW,GAAgB,IAAI,CAAC;IAChC,eAAe,GAAS,IAAI,IAAI,EAAE,CAAC;IACnC,QAAQ,GAAkB,IAAI,CAAC;IAEtB,IAAI,CAAS;IACb,gBAAgB,CAAS;IACzB,cAAc,CAAS;IACvB,gBAAgB,CAAS;IACzB,eAAe,CAAS;IACxB,aAAa,CAAS;IACtB,SAAS,CAA4B;IACrC,MAAM,CAA4C;IAClD,OAAO,CAA0B;IACjC,UAAU,CAA0B;IAErD,YAAY,OAA8B;QACxC,IAAI,CAAC,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC;QACzB,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,CAAC,CAAC;QACtD,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,cAAc,IAAI,KAAK,CAAC,CAAC,aAAa;QACpE,IAAI,CAAC,gBAAgB,GAAG,OAAO,CAAC,gBAAgB,IAAI,CAAC,CAAC;QACtD,IAAI,CAAC,eAAe,GAAG,OAAO,CAAC,eAAe,IAAI,KAAK,CAAC,CAAC,WAAW;QACpE,IAAI,CAAC,aAAa,GAAG,OAAO,CAAC,aAAa,IAAI,KAAK,CAAC,CAAC,aAAa;QAClE,IAAI,CAAC,SAAS,GAAG,OAAO,CAAC,SAAS,IAAI,CAAC,GAAG,EAAE,CAAC,IAAI,CAAC,CAAC;QACnD,IAAI,CAAC,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,CAAC,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC;QAC/B,IAAI,CAAC,UAAU,GAAG,OAAO,CAAC,UAAU,CAAC;QAErC,MAAM,CAAC,KAAK,CAAC,6BAA6B,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,KAAK,EAAE,IAAI,CAAC,KAAK,EAAE,CAAC,CAAC;IACtF,CAAC;IAED;;OAEG;IACH,KAAK,CAAC,OAAO,CAAI,EAAoB;QACnC,IAAI,CAAC,UAAU,EAAE,CAAC;QAElB,2BAA2B;QAC3B,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,EAAE,CAAC;YAC1B,IAAI,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC;gBAC9B,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;YACjC,CAAC;iBAAM,CAAC;gBACN,IAAI,CAAC,aAAa,EAAE,CAAC;gBACrB,MAAM,IAAI,gBAAgB,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,qBAAqB,EAAE,CAAC,CAAC;YACtE,CAAC;QACH,CAAC;QAED,wBAAwB;QACxB,MAAM,cAAc,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;YACtD,UAAU,CAAC,GAAG,EAAE;gBACd,MAAM,CAAC,IAAI,KAAK,CAAC,4BAA4B,IAAI,CAAC,aAAa,KAAK,CAAC,CAAC,CAAC;YACzE,CAAC,EAAE,IAAI,CAAC,aAAa,CAAC,CAAC;QACzB,CAAC,CAAC,CAAC;QAEH,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,cAAc,CAAC,CAAC,CAAC;YAC1D,IAAI,CAAC,SAAS,EAAE,CAAC;YACjB,OAAO,MAAM,CAAC;QAChB,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,IAAI,CAAC,OAAO,CAAC,KAAc,CAAC,CAAC;YAC7B,MAAM,KAAK,CAAC;QACd,CAAC;IACH,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,2CAA2C;QAC3C,IAAI,IAAI,CAAC,KAAK,KAAK,MAAM,IAAI,IAAI,CAAC,kBAAkB,EAAE,EAAE,CAAC;YACvD,IAAI,CAAC,YAAY,CAAC,WAAW,CAAC,CAAC;QACjC,CAAC;QACD,OAAO,IAAI,CAAC,KAAK,CAAC;IACpB,CAAC;IAED;;OAEG;IACH,UAAU;QACR,MAAM,cAAc,GAAG,IAAI,CAAC,iBAAiB,EAAE,CAAC;QAChD,OAAO;YACL,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,KAAK,EAAE,IAAI,CAAC,QAAQ,EAAE;YACtB,QAAQ,EAAE,cAAc,CAAC,MAAM;YAC/B,SAAS,EAAE,IAAI,CAAC,SAAS;YACzB,UAAU,EAAE,IAAI,CAAC,UAAU;YAC3B,aAAa,EAAE,IAAI,CAAC,aAAa;YACjC,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,IAAI;YACpD,WAAW,EAAE,IAAI,CAAC,WAAW,EAAE,WAAW,EAAE,IAAI,IAAI;YACpD,eAAe,EAAE,IAAI,CAAC,eAAe,CAAC,WAAW,EAAE;YACnD,oBAAoB,EAAE,IAAI,CAAC,oBAAoB;YAC/C,WAAW,EAAE,IAAI,CAAC,UAAU,GAAG,CAAC;gBAC9B,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC,cAAc,CAAC,MAAM,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,UAAU,EAAE,GAAG,CAAC,CAAC,GAAG,GAAG,CAAC;gBAC5E,CAAC,CAAC,CAAC;SACN,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;QACnB,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAC;QAC9B,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;QAC5B,MAAM,CAAC,IAAI,CAAC,gCAAgC,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IACrE,CAAC;IAED;;OAEG;IACH,SAAS;QACP,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC1B,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;QAC3B,MAAM,CAAC,IAAI,CAAC,6BAA6B,EAAE,EAAE,IAAI,EAAE,IAAI,CAAC,IAAI,EAAE,CAAC,CAAC;IAClE,CAAC;IAED,+EAA+E;IAC/E,kBAAkB;IAClB,+EAA+E;IAEvE,SAAS;QACf,IAAI,CAAC,SAAS,EAAE,CAAC;QACjB,IAAI,CAAC,oBAAoB,EAAE,CAAC;QAC5B,IAAI,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;QAE9B,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC/B,IAAI,IAAI,CAAC,oBAAoB,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBACvD,IAAI,CAAC,YAAY,CAAC,QAAQ,CAAC,CAAC;YAC9B,CAAC;QACH,CAAC;QAED,gCAAgC;QAChC,IAAI,CAAC,gBAAgB,EAAE,CAAC;IAC1B,CAAC;IAEO,OAAO,CAAC,KAAY;QAC1B,IAAI,CAAC,WAAW,GAAG,IAAI,IAAI,EAAE,CAAC;QAC9B,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAC;QAE9B,0CAA0C;QAC1C,IAAI,CAAC,IAAI,CAAC,SAAS,CAAC,KAAK,CAAC,EAAE,CAAC;YAC3B,OAAO;QACT,CAAC;QAED,IAAI,CAAC,QAAQ,CAAC,IAAI,CAAC;YACjB,SAAS,EAAE,IAAI,CAAC,GAAG,EAAE;YACrB,KAAK,EAAE,KAAK,CAAC,OAAO;SACrB,CAAC,CAAC;QAEH,IAAI,CAAC,gBAAgB,EAAE,CAAC;QAExB,IAAI,IAAI,CAAC,KAAK,KAAK,WAAW,EAAE,CAAC;YAC/B,mDAAmD;YACnD,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;QAC5B,CAAC;aAAM,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ,EAAE,CAAC;YACnC,4CAA4C;YAC5C,IAAI,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,IAAI,IAAI,CAAC,gBAAgB,EAAE,CAAC;gBAC7D,IAAI,CAAC,YAAY,CAAC,MAAM,CAAC,CAAC;YAC5B,CAAC;QACH,CAAC;IACH,CAAC;IAEO,YAAY,CAAC,QAAsB;QACzC,IAAI,IAAI,CAAC,KAAK,KAAK,QAAQ;YAAE,OAAO;QAEpC,MAAM,QAAQ,GAAG,IAAI,CAAC,KAAK,CAAC;QAC5B,IAAI,CAAC,KAAK,GAAG,QAAQ,CAAC;QACtB,IAAI,CAAC,eAAe,GAAG,IAAI,IAAI,EAAE,CAAC;QAElC,MAAM,CAAC,IAAI,CAAC,kCAAkC,EAAE;YAC9C,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,IAAI,EAAE,QAAQ;YACd,EAAE,EAAE,QAAQ;SACb,CAAC,CAAC;QAEH,QAAQ,QAAQ,EAAE,CAAC;YACjB,KAAK,MAAM;gBACT,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;gBAC3B,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAC;gBAC9B,IAAI,CAAC,MAAM,EAAE,CAAC,IAAI,CAAC,IAAI,EAAE,IAAI,CAAC,iBAAiB,EAAE,CAAC,MAAM,CAAC,CAAC;gBAC1D,MAAM;YACR,KAAK,WAAW;gBACd,IAAI,CAAC,oBAAoB,GAAG,CAAC,CAAC;gBAC9B,IAAI,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC7B,MAAM;YACR,KAAK,QAAQ;gBACX,IAAI,CAAC,QAAQ,GAAG,EAAE,CAAC;gBACnB,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC;gBACrB,IAAI,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;gBAC1B,MAAM;QACV,CAAC;IACH,CAAC;IAEO,kBAAkB;QACxB,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI;YAAE,OAAO,KAAK,CAAC;QACzC,OAAO,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,IAAI,IAAI,CAAC,cAAc,CAAC;IAC3D,CAAC;IAEO,qBAAqB;QAC3B,IAAI,IAAI,CAAC,QAAQ,KAAK,IAAI;YAAE,OAAO,CAAC,CAAC;QACrC,MAAM,OAAO,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,QAAQ,CAAC;QAC3C,OAAO,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,IAAI,CAAC,cAAc,GAAG,OAAO,CAAC,CAAC;IACpD,CAAC;IAEO,iBAAiB;QACvB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC;QACjD,OAAO,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC;IAC1D,CAAC;IAEO,gBAAgB;QACtB,MAAM,MAAM,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,IAAI,CAAC,eAAe,CAAC;QACjD,IAAI,CAAC,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,SAAS,IAAI,MAAM,CAAC,CAAC;IACnE,CAAC;CACF;AAED,+EAA+E;AAC/E,wBAAwB;AACxB,+EAA+E;AAE/E,MAAM,OAAO,gBAAiB,SAAQ,KAAK;IACzB,WAAW,CAAS;IACpB,WAAW,CAAS;IAEpC,YAAY,WAAmB,EAAE,WAAmB;QAClD,KAAK,CAAC,oBAAoB,WAAW,0BAA0B,IAAI,CAAC,IAAI,CAAC,WAAW,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC;QACjG,IAAI,CAAC,IAAI,GAAG,kBAAkB,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;QAC/B,IAAI,CAAC,WAAW,GAAG,WAAW,CAAC;IACjC,CAAC;CACF;AAED,+EAA+E;AAC/E,2BAA2B;AAC3B,+EAA+E;AAE/E,MAAM,sBAAsB;IAClB,QAAQ,GAAgC,IAAI,GAAG,EAAE,CAAC;IAE1D;;OAEG;IACH,WAAW,CAAC,OAA8B;QACxC,MAAM,QAAQ,GAAG,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;QACjD,IAAI,QAAQ,EAAE,CAAC;YACb,OAAO,QAAQ,CAAC;QAClB,CAAC;QAED,MAAM,OAAO,GAAG,IAAI,cAAc,CAAC,OAAO,CAAC,CAAC;QAC5C,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,OAAO,CAAC,IAAI,EAAE,OAAO,CAAC,CAAC;QACzC,OAAO,OAAO,CAAC;IACjB,CAAC;IAED;;OAEG;IACH,GAAG,CAAC,IAAY;QACd,OAAO,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,IAAI,CAAC,CAAC;IACjC,CAAC;IAED;;OAEG;IACH,MAAM;QACJ,OAAO,KAAK,CAAC,IAAI,CAAC,IAAI,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC,CAAC;IAC5C,CAAC;IAED;;OAEG;IACH,aAAa;QACX,OAAO,IAAI,CAAC,MAAM,EAAE,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,UAAU,EAAE,CAAC,CAAC;IAChD,CAAC;IAED;;OAEG;IACH,gBAAgB;QAOd,MAAM,OAAO,GAAG,IAAI,CAAC,aAAa,EAAE,CAAC;QACrC,MAAM,MAAM,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,QAAQ,CAAC,CAAC,MAAM,CAAC;QAChE,MAAM,IAAI,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,MAAM,CAAC,CAAC,MAAM,CAAC;QAC5D,MAAM,QAAQ,GAAG,OAAO,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,KAAK,KAAK,WAAW,CAAC,CAAC,MAAM,CAAC;QAErE,OAAO;YACL,KAAK,EAAE,OAAO,CAAC,MAAM;YACrB,MAAM;YACN,IAAI;YACJ,QAAQ;YACR,OAAO,EAAE,IAAI,KAAK,CAAC;SACpB,CAAC;IACJ,CAAC;IAED;;OAEG;IACH,QAAQ;QACN,IAAI,CAAC,QAAQ,CAAC,OAAO,CAAC,OAAO,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,EAAE,CAAC,CAAC;QAClD,MAAM,CAAC,IAAI,CAAC,4BAA4B,EAAE,EAAE,KAAK,EAAE,IAAI,CAAC,QAAQ,CAAC,IAAI,EAAE,CAAC,CAAC;IAC3E,CAAC;IAED;;OAEG;IACH,KAAK;QACH,IAAI,CAAC,QAAQ,CAAC,KAAK,EAAE,CAAC;IACxB,CAAC;CACF;AAED,+EAA+E;AAC/E,4BAA4B;AAC5B,+EAA+E;AAE/E,MAAM,CAAC,MAAM,sBAAsB,GAAG,IAAI,sBAAsB,EAAE,CAAC;AAEnE;;GAEG;AACH,MAAM,UAAU,2BAA2B,CACzC,WAAmB,EACnB,UAA0C,EAAE;IAE5C,OAAO,sBAAsB,CAAC,WAAW,CAAC;QACxC,IAAI,EAAE,WAAW;QACjB,gBAAgB,EAAE,CAAC;QACnB,cAAc,EAAE,KAAK;QACrB,gBAAgB,EAAE,CAAC;QACnB,eAAe,EAAE,KAAK;QACtB,aAAa,EAAE,KAAK;QACpB,GAAG,OAAO;QACV,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,EAAE,EAAE;YACzB,MAAM,CAAC,KAAK,CAAC,wBAAwB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,QAAQ,EAAE,CAAC,CAAC;YACpE,OAAO,CAAC,MAAM,EAAE,CAAC,IAAI,EAAE,QAAQ,CAAC,CAAC;QACnC,CAAC;QACD,OAAO,EAAE,CAAC,IAAI,EAAE,EAAE;YAChB,MAAM,CAAC,IAAI,CAAC,wBAAwB,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YACzD,OAAO,CAAC,OAAO,EAAE,CAAC,IAAI,CAAC,CAAC;QAC1B,CAAC;QACD,UAAU,EAAE,CAAC,IAAI,EAAE,EAAE;YACnB,MAAM,CAAC,IAAI,CAAC,2BAA2B,EAAE,EAAE,OAAO,EAAE,IAAI,EAAE,CAAC,CAAC;YAC5D,OAAO,CAAC,UAAU,EAAE,CAAC,IAAI,CAAC,CAAC;QAC7B,CAAC;KACF,CAAC,CAAC;AACL,CAAC;AAED,+EAA+E;AAC/E,2BAA2B;AAC3B,+EAA+E;AAE/E;;GAEG;AACH,MAAM,UAAU,kBAAkB,CAChC,EAAK,EACL,WAAmB,EACnB,UAA0C,EAAE;IAE5C,MAAM,OAAO,GAAG,2BAA2B,CAAC,WAAW,EAAE,OAAO,CAAC,CAAC;IAElE,OAAO,CAAC,KAAK,EAAE,GAAG,IAAmB,EAAE,EAAE;QACvC,OAAO,OAAO,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,IAAI,CAAC,CAAC,CAAC;IAC5C,CAAC,CAAM,CAAC;AACV,CAAC"}
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Resilience Metrics - Centralized metrics collection for service health
|
|
3
|
+
* @version 3.8.2
|
|
4
|
+
*/
|
|
5
|
+
import { type CircuitBreakerMetrics } from './circuitBreaker.js';
|
|
6
|
+
export interface ServiceMetrics {
|
|
7
|
+
name: string;
|
|
8
|
+
totalRequests: number;
|
|
9
|
+
successfulRequests: number;
|
|
10
|
+
failedRequests: number;
|
|
11
|
+
timeouts: number;
|
|
12
|
+
averageLatencyMs: number;
|
|
13
|
+
p95LatencyMs: number;
|
|
14
|
+
p99LatencyMs: number;
|
|
15
|
+
lastRequestAt: string | null;
|
|
16
|
+
lastErrorAt: string | null;
|
|
17
|
+
lastError: string | null;
|
|
18
|
+
successRate: number;
|
|
19
|
+
requestsPerMinute: number;
|
|
20
|
+
}
|
|
21
|
+
export interface OverallHealthMetrics {
|
|
22
|
+
timestamp: string;
|
|
23
|
+
uptime: number;
|
|
24
|
+
services: ServiceMetrics[];
|
|
25
|
+
circuits: CircuitBreakerMetrics[];
|
|
26
|
+
summary: {
|
|
27
|
+
totalServices: number;
|
|
28
|
+
healthyServices: number;
|
|
29
|
+
degradedServices: number;
|
|
30
|
+
unhealthyServices: number;
|
|
31
|
+
overallHealthScore: number;
|
|
32
|
+
status: 'healthy' | 'degraded' | 'unhealthy';
|
|
33
|
+
};
|
|
34
|
+
}
|
|
35
|
+
declare class ServiceMetricsCollector {
|
|
36
|
+
private requests;
|
|
37
|
+
private readonly name;
|
|
38
|
+
private readonly maxRecords;
|
|
39
|
+
private readonly windowMs;
|
|
40
|
+
constructor(name: string, maxRecords?: number, windowMs?: number);
|
|
41
|
+
recordSuccess(latencyMs: number): void;
|
|
42
|
+
recordFailure(latencyMs: number, error: string): void;
|
|
43
|
+
recordTimeout(latencyMs: number): void;
|
|
44
|
+
private addRecord;
|
|
45
|
+
private pruneOldRecords;
|
|
46
|
+
getMetrics(): ServiceMetrics;
|
|
47
|
+
private percentile;
|
|
48
|
+
reset(): void;
|
|
49
|
+
}
|
|
50
|
+
declare class ResilienceMetricsRegistry {
|
|
51
|
+
private collectors;
|
|
52
|
+
private startTime;
|
|
53
|
+
/**
|
|
54
|
+
* Get or create a metrics collector for a service
|
|
55
|
+
*/
|
|
56
|
+
getOrCreate(serviceName: string): ServiceMetricsCollector;
|
|
57
|
+
/**
|
|
58
|
+
* Record a successful request
|
|
59
|
+
*/
|
|
60
|
+
recordSuccess(serviceName: string, latencyMs: number): void;
|
|
61
|
+
/**
|
|
62
|
+
* Record a failed request
|
|
63
|
+
*/
|
|
64
|
+
recordFailure(serviceName: string, latencyMs: number, error: string): void;
|
|
65
|
+
/**
|
|
66
|
+
* Record a timeout
|
|
67
|
+
*/
|
|
68
|
+
recordTimeout(serviceName: string, latencyMs: number): void;
|
|
69
|
+
/**
|
|
70
|
+
* Get metrics for all services
|
|
71
|
+
*/
|
|
72
|
+
getAllServiceMetrics(): ServiceMetrics[];
|
|
73
|
+
/**
|
|
74
|
+
* Get overall health metrics
|
|
75
|
+
*/
|
|
76
|
+
getOverallHealth(): OverallHealthMetrics;
|
|
77
|
+
/**
|
|
78
|
+
* Get a quick health check
|
|
79
|
+
*/
|
|
80
|
+
quickHealthCheck(): {
|
|
81
|
+
healthy: boolean;
|
|
82
|
+
score: number;
|
|
83
|
+
issues: string[];
|
|
84
|
+
};
|
|
85
|
+
/**
|
|
86
|
+
* Reset all metrics
|
|
87
|
+
*/
|
|
88
|
+
reset(): void;
|
|
89
|
+
/**
|
|
90
|
+
* Clear all collectors
|
|
91
|
+
*/
|
|
92
|
+
clear(): void;
|
|
93
|
+
}
|
|
94
|
+
export declare const resilienceMetrics: ResilienceMetricsRegistry;
|
|
95
|
+
/**
|
|
96
|
+
* Time an async operation and record metrics
|
|
97
|
+
*/
|
|
98
|
+
export declare function withMetrics<T>(serviceName: string, fn: () => Promise<T>): Promise<T>;
|
|
99
|
+
/**
|
|
100
|
+
* Combine circuit breaker and metrics for a service call
|
|
101
|
+
*/
|
|
102
|
+
export declare function withResilience<T>(serviceName: string, fn: () => Promise<T>, circuitOptions?: Parameters<typeof import('./circuitBreaker.js').createServiceCircuitBreaker>[1]): Promise<T>;
|
|
103
|
+
export {};
|
|
104
|
+
//# sourceMappingURL=resilienceMetrics.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resilienceMetrics.d.ts","sourceRoot":"","sources":["../../src/utils/resilienceMetrics.ts"],"names":[],"mappings":"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"}
|