@defai.digital/provider-domain 13.0.3
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/LICENSE +214 -0
- package/dist/circuit-breaker.d.ts +51 -0
- package/dist/circuit-breaker.d.ts.map +1 -0
- package/dist/circuit-breaker.js +168 -0
- package/dist/circuit-breaker.js.map +1 -0
- package/dist/health-monitor.d.ts +60 -0
- package/dist/health-monitor.d.ts.map +1 -0
- package/dist/health-monitor.js +301 -0
- package/dist/health-monitor.js.map +1 -0
- package/dist/index.d.ts +13 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +17 -0
- package/dist/index.js.map +1 -0
- package/dist/provider-manager.d.ts +76 -0
- package/dist/provider-manager.d.ts.map +1 -0
- package/dist/provider-manager.js +186 -0
- package/dist/provider-manager.js.map +1 -0
- package/dist/rate-limiter.d.ts +53 -0
- package/dist/rate-limiter.d.ts.map +1 -0
- package/dist/rate-limiter.js +197 -0
- package/dist/rate-limiter.js.map +1 -0
- package/package.json +41 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Health Monitor Implementation
|
|
3
|
+
*
|
|
4
|
+
* Tracks provider health through latency, error rates, and availability.
|
|
5
|
+
* Enables intelligent routing based on provider health status.
|
|
6
|
+
*
|
|
7
|
+
* Invariants:
|
|
8
|
+
* - INV-HM-001: Latency samples bounded by configured size
|
|
9
|
+
* - INV-HM-002: Error rate calculated over sliding window
|
|
10
|
+
* - INV-HM-003: Availability derived from circuit and error state
|
|
11
|
+
*/
|
|
12
|
+
import { createDefaultHealthCheckConfig, calculatePercentile, HealthErrorCodes, } from '@defai.digital/contracts';
|
|
13
|
+
/**
|
|
14
|
+
* Creates a health monitor for a provider
|
|
15
|
+
*/
|
|
16
|
+
export function createHealthMonitor(providerId, config = {}, healthCheckFn) {
|
|
17
|
+
const cfg = { ...createDefaultHealthCheckConfig(), ...config };
|
|
18
|
+
// Latency tracking (INV-HM-001)
|
|
19
|
+
const latencySamples = [];
|
|
20
|
+
// Request tracking (INV-HM-002)
|
|
21
|
+
let totalRequests = 0;
|
|
22
|
+
let totalErrors = 0;
|
|
23
|
+
let consecutiveFailures = 0;
|
|
24
|
+
let consecutiveSuccesses = 0;
|
|
25
|
+
// Timestamps
|
|
26
|
+
let lastCheckTime = new Date();
|
|
27
|
+
let lastSuccessTime;
|
|
28
|
+
let lastErrorTime;
|
|
29
|
+
let lastError;
|
|
30
|
+
let lastErrorCode;
|
|
31
|
+
// External state
|
|
32
|
+
let circuitState = 'closed';
|
|
33
|
+
let rateLimitState = 'normal';
|
|
34
|
+
// Current health level
|
|
35
|
+
let currentLevel = 'unknown';
|
|
36
|
+
const events = [];
|
|
37
|
+
const listeners = [];
|
|
38
|
+
function emitEvent(type, details) {
|
|
39
|
+
const previousLevel = currentLevel;
|
|
40
|
+
updateHealthLevel();
|
|
41
|
+
const event = {
|
|
42
|
+
eventId: crypto.randomUUID(),
|
|
43
|
+
type,
|
|
44
|
+
providerId,
|
|
45
|
+
timestamp: new Date().toISOString(),
|
|
46
|
+
previousLevel,
|
|
47
|
+
currentLevel,
|
|
48
|
+
details,
|
|
49
|
+
};
|
|
50
|
+
events.push(event);
|
|
51
|
+
listeners.forEach((listener) => { listener(event); });
|
|
52
|
+
}
|
|
53
|
+
// INV-HM-001: Maintain bounded latency samples
|
|
54
|
+
function addLatencySample(latencyMs) {
|
|
55
|
+
latencySamples.push(latencyMs);
|
|
56
|
+
if (latencySamples.length > cfg.latencySampleSize) {
|
|
57
|
+
latencySamples.shift();
|
|
58
|
+
}
|
|
59
|
+
}
|
|
60
|
+
function calculateAverageLatency() {
|
|
61
|
+
if (latencySamples.length === 0)
|
|
62
|
+
return 0;
|
|
63
|
+
const sum = latencySamples.reduce((a, b) => a + b, 0);
|
|
64
|
+
return sum / latencySamples.length;
|
|
65
|
+
}
|
|
66
|
+
// INV-HM-002: Calculate error rate over window
|
|
67
|
+
function calculateErrorRate() {
|
|
68
|
+
if (totalRequests === 0)
|
|
69
|
+
return 0;
|
|
70
|
+
return totalErrors / totalRequests;
|
|
71
|
+
}
|
|
72
|
+
// INV-HM-003: Derive availability from state
|
|
73
|
+
function updateHealthLevel() {
|
|
74
|
+
const errorRate = calculateErrorRate();
|
|
75
|
+
const previousLevel = currentLevel;
|
|
76
|
+
// Circuit open = unhealthy
|
|
77
|
+
if (circuitState === 'open') {
|
|
78
|
+
currentLevel = 'unhealthy';
|
|
79
|
+
}
|
|
80
|
+
// High error rate = unhealthy
|
|
81
|
+
else if (errorRate >= cfg.unhealthyErrorRate) {
|
|
82
|
+
currentLevel = 'unhealthy';
|
|
83
|
+
}
|
|
84
|
+
// Degraded conditions
|
|
85
|
+
else if (circuitState === 'halfOpen' ||
|
|
86
|
+
rateLimitState === 'throttled' ||
|
|
87
|
+
rateLimitState === 'blocked' ||
|
|
88
|
+
consecutiveFailures >= cfg.degradedThreshold) {
|
|
89
|
+
currentLevel = 'degraded';
|
|
90
|
+
}
|
|
91
|
+
// No data yet
|
|
92
|
+
else if (totalRequests === 0) {
|
|
93
|
+
currentLevel = 'unknown';
|
|
94
|
+
}
|
|
95
|
+
// All good
|
|
96
|
+
else {
|
|
97
|
+
currentLevel = 'healthy';
|
|
98
|
+
}
|
|
99
|
+
// Emit level change events
|
|
100
|
+
if (previousLevel !== currentLevel) {
|
|
101
|
+
if (currentLevel === 'unhealthy') {
|
|
102
|
+
emitEvent('health.unhealthy');
|
|
103
|
+
}
|
|
104
|
+
else if (currentLevel === 'degraded') {
|
|
105
|
+
emitEvent('health.degraded');
|
|
106
|
+
}
|
|
107
|
+
else if (currentLevel === 'healthy' &&
|
|
108
|
+
(previousLevel === 'degraded' || previousLevel === 'unhealthy')) {
|
|
109
|
+
emitEvent('health.recovered');
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
function isAvailable() {
|
|
114
|
+
return currentLevel === 'healthy' || currentLevel === 'degraded';
|
|
115
|
+
}
|
|
116
|
+
return {
|
|
117
|
+
getStatus() {
|
|
118
|
+
updateHealthLevel();
|
|
119
|
+
const sortedLatencies = [...latencySamples].sort((a, b) => a - b);
|
|
120
|
+
return {
|
|
121
|
+
providerId,
|
|
122
|
+
available: isAvailable(),
|
|
123
|
+
level: currentLevel,
|
|
124
|
+
latencyMs: calculateAverageLatency(),
|
|
125
|
+
latencyP50Ms: sortedLatencies.length > 0
|
|
126
|
+
? calculatePercentile(sortedLatencies, 50)
|
|
127
|
+
: undefined,
|
|
128
|
+
latencyP95Ms: sortedLatencies.length > 0
|
|
129
|
+
? calculatePercentile(sortedLatencies, 95)
|
|
130
|
+
: undefined,
|
|
131
|
+
latencyP99Ms: sortedLatencies.length > 0
|
|
132
|
+
? calculatePercentile(sortedLatencies, 99)
|
|
133
|
+
: undefined,
|
|
134
|
+
errorRate: calculateErrorRate(),
|
|
135
|
+
consecutiveFailures,
|
|
136
|
+
consecutiveSuccesses,
|
|
137
|
+
lastCheckTime: lastCheckTime.toISOString(),
|
|
138
|
+
lastSuccessTime: lastSuccessTime?.toISOString(),
|
|
139
|
+
lastErrorTime: lastErrorTime?.toISOString(),
|
|
140
|
+
lastError,
|
|
141
|
+
lastErrorCode,
|
|
142
|
+
circuitState,
|
|
143
|
+
rateLimitState,
|
|
144
|
+
totalRequests,
|
|
145
|
+
totalErrors,
|
|
146
|
+
uptimePercent: totalRequests > 0
|
|
147
|
+
? ((totalRequests - totalErrors) / totalRequests) * 100
|
|
148
|
+
: undefined,
|
|
149
|
+
};
|
|
150
|
+
},
|
|
151
|
+
recordSuccess(latencyMs) {
|
|
152
|
+
totalRequests++;
|
|
153
|
+
consecutiveSuccesses++;
|
|
154
|
+
consecutiveFailures = 0;
|
|
155
|
+
lastSuccessTime = new Date();
|
|
156
|
+
lastCheckTime = new Date();
|
|
157
|
+
addLatencySample(latencyMs);
|
|
158
|
+
emitEvent('health.check.passed', { latencyMs });
|
|
159
|
+
},
|
|
160
|
+
recordFailure(error, errorCode) {
|
|
161
|
+
totalRequests++;
|
|
162
|
+
totalErrors++;
|
|
163
|
+
consecutiveFailures++;
|
|
164
|
+
consecutiveSuccesses = 0;
|
|
165
|
+
lastErrorTime = new Date();
|
|
166
|
+
lastCheckTime = new Date();
|
|
167
|
+
lastError = error;
|
|
168
|
+
lastErrorCode = errorCode;
|
|
169
|
+
emitEvent('health.check.failed', { error, errorCode });
|
|
170
|
+
},
|
|
171
|
+
updateCircuitState(state) {
|
|
172
|
+
const previous = circuitState;
|
|
173
|
+
circuitState = state;
|
|
174
|
+
if (previous !== state) {
|
|
175
|
+
updateHealthLevel();
|
|
176
|
+
emitEvent('health.status.changed', {
|
|
177
|
+
circuitState: state,
|
|
178
|
+
previousCircuitState: previous,
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
},
|
|
182
|
+
updateRateLimitState(state) {
|
|
183
|
+
const previous = rateLimitState;
|
|
184
|
+
rateLimitState = state;
|
|
185
|
+
if (previous !== state) {
|
|
186
|
+
updateHealthLevel();
|
|
187
|
+
emitEvent('health.status.changed', {
|
|
188
|
+
rateLimitState: state,
|
|
189
|
+
previousRateLimitState: previous,
|
|
190
|
+
});
|
|
191
|
+
}
|
|
192
|
+
},
|
|
193
|
+
async performHealthCheck() {
|
|
194
|
+
const checkId = crypto.randomUUID();
|
|
195
|
+
const startTime = Date.now();
|
|
196
|
+
emitEvent('health.check.started', { checkId });
|
|
197
|
+
if (!healthCheckFn) {
|
|
198
|
+
// No health check function provided, return unknown
|
|
199
|
+
return {
|
|
200
|
+
checkId,
|
|
201
|
+
providerId,
|
|
202
|
+
success: false,
|
|
203
|
+
latencyMs: 0,
|
|
204
|
+
timestamp: new Date().toISOString(),
|
|
205
|
+
error: 'No health check function configured',
|
|
206
|
+
errorCode: HealthErrorCodes.CHECK_FAILED,
|
|
207
|
+
checkType: 'active',
|
|
208
|
+
};
|
|
209
|
+
}
|
|
210
|
+
// Create cancellable timeout to prevent timer leaks
|
|
211
|
+
let timeoutId;
|
|
212
|
+
const timeoutPromise = new Promise((_, reject) => {
|
|
213
|
+
timeoutId = setTimeout(() => { reject(new Error('Health check timeout')); }, cfg.timeoutMs);
|
|
214
|
+
});
|
|
215
|
+
try {
|
|
216
|
+
const result = await Promise.race([
|
|
217
|
+
healthCheckFn(),
|
|
218
|
+
timeoutPromise,
|
|
219
|
+
]);
|
|
220
|
+
// Clean up timeout
|
|
221
|
+
if (timeoutId !== undefined) {
|
|
222
|
+
clearTimeout(timeoutId);
|
|
223
|
+
}
|
|
224
|
+
const latencyMs = Date.now() - startTime;
|
|
225
|
+
if (result.success) {
|
|
226
|
+
this.recordSuccess(latencyMs);
|
|
227
|
+
}
|
|
228
|
+
else {
|
|
229
|
+
this.recordFailure(result.error ?? 'Health check failed', HealthErrorCodes.CHECK_FAILED);
|
|
230
|
+
}
|
|
231
|
+
return {
|
|
232
|
+
checkId,
|
|
233
|
+
providerId,
|
|
234
|
+
success: result.success,
|
|
235
|
+
latencyMs,
|
|
236
|
+
timestamp: new Date().toISOString(),
|
|
237
|
+
error: result.error,
|
|
238
|
+
checkType: 'active',
|
|
239
|
+
};
|
|
240
|
+
}
|
|
241
|
+
catch (error) {
|
|
242
|
+
// Clean up timeout on error too
|
|
243
|
+
if (timeoutId !== undefined) {
|
|
244
|
+
clearTimeout(timeoutId);
|
|
245
|
+
}
|
|
246
|
+
const latencyMs = Date.now() - startTime;
|
|
247
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
248
|
+
this.recordFailure(errorMessage, HealthErrorCodes.CHECK_TIMEOUT);
|
|
249
|
+
return {
|
|
250
|
+
checkId,
|
|
251
|
+
providerId,
|
|
252
|
+
success: false,
|
|
253
|
+
latencyMs,
|
|
254
|
+
timestamp: new Date().toISOString(),
|
|
255
|
+
error: errorMessage,
|
|
256
|
+
errorCode: HealthErrorCodes.CHECK_TIMEOUT,
|
|
257
|
+
checkType: 'active',
|
|
258
|
+
};
|
|
259
|
+
}
|
|
260
|
+
},
|
|
261
|
+
getEvents() {
|
|
262
|
+
return [...events];
|
|
263
|
+
},
|
|
264
|
+
reset() {
|
|
265
|
+
latencySamples.length = 0;
|
|
266
|
+
totalRequests = 0;
|
|
267
|
+
totalErrors = 0;
|
|
268
|
+
consecutiveFailures = 0;
|
|
269
|
+
consecutiveSuccesses = 0;
|
|
270
|
+
lastCheckTime = new Date();
|
|
271
|
+
lastSuccessTime = undefined;
|
|
272
|
+
lastErrorTime = undefined;
|
|
273
|
+
lastError = undefined;
|
|
274
|
+
lastErrorCode = undefined;
|
|
275
|
+
circuitState = 'closed';
|
|
276
|
+
rateLimitState = 'normal';
|
|
277
|
+
currentLevel = 'unknown';
|
|
278
|
+
events.length = 0;
|
|
279
|
+
},
|
|
280
|
+
};
|
|
281
|
+
}
|
|
282
|
+
/**
|
|
283
|
+
* Health monitor error
|
|
284
|
+
*/
|
|
285
|
+
export class HealthMonitorError extends Error {
|
|
286
|
+
code;
|
|
287
|
+
providerId;
|
|
288
|
+
constructor(code, providerId, message) {
|
|
289
|
+
super(message);
|
|
290
|
+
this.code = code;
|
|
291
|
+
this.providerId = providerId;
|
|
292
|
+
this.name = 'HealthMonitorError';
|
|
293
|
+
}
|
|
294
|
+
static checkTimeout(providerId) {
|
|
295
|
+
return new HealthMonitorError(HealthErrorCodes.CHECK_TIMEOUT, providerId, `Health check timed out for provider ${providerId}`);
|
|
296
|
+
}
|
|
297
|
+
static providerUnavailable(providerId) {
|
|
298
|
+
return new HealthMonitorError(HealthErrorCodes.PROVIDER_UNAVAILABLE, providerId, `Provider ${providerId} is unavailable`);
|
|
299
|
+
}
|
|
300
|
+
}
|
|
301
|
+
//# sourceMappingURL=health-monitor.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"health-monitor.js","sourceRoot":"","sources":["../src/health-monitor.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,EASL,8BAA8B,EAC9B,mBAAmB,EACnB,gBAAgB,GACjB,MAAM,0BAA0B,CAAC;AA6ClC;;GAEG;AACH,MAAM,UAAU,mBAAmB,CACjC,UAAkB,EAClB,SAAqC,EAAE,EACvC,aAA6B;IAE7B,MAAM,GAAG,GAAG,EAAE,GAAG,8BAA8B,EAAE,EAAE,GAAG,MAAM,EAAE,CAAC;IAE/D,gCAAgC;IAChC,MAAM,cAAc,GAAa,EAAE,CAAC;IAEpC,gCAAgC;IAChC,IAAI,aAAa,GAAG,CAAC,CAAC;IACtB,IAAI,WAAW,GAAG,CAAC,CAAC;IACpB,IAAI,mBAAmB,GAAG,CAAC,CAAC;IAC5B,IAAI,oBAAoB,GAAG,CAAC,CAAC;IAE7B,aAAa;IACb,IAAI,aAAa,GAAG,IAAI,IAAI,EAAE,CAAC;IAC/B,IAAI,eAAiC,CAAC;IACtC,IAAI,aAA+B,CAAC;IACpC,IAAI,SAA6B,CAAC;IAClC,IAAI,aAAiC,CAAC;IAEtC,iBAAiB;IACjB,IAAI,YAAY,GAAiB,QAAQ,CAAC;IAC1C,IAAI,cAAc,GAAuB,QAAQ,CAAC;IAElD,uBAAuB;IACvB,IAAI,YAAY,GAAgB,SAAS,CAAC;IAE1C,MAAM,MAAM,GAAkB,EAAE,CAAC;IACjC,MAAM,SAAS,GAA0B,EAAE,CAAC;IAE5C,SAAS,SAAS,CAChB,IAAqB,EACrB,OAAiC;QAEjC,MAAM,aAAa,GAAG,YAAY,CAAC;QACnC,iBAAiB,EAAE,CAAC;QAEpB,MAAM,KAAK,GAAgB;YACzB,OAAO,EAAE,MAAM,CAAC,UAAU,EAAE;YAC5B,IAAI;YACJ,UAAU;YACV,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;YACnC,aAAa;YACb,YAAY;YACZ,OAAO;SACR,CAAC;QACF,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QACnB,SAAS,CAAC,OAAO,CAAC,CAAC,QAAQ,EAAE,EAAE,GAAG,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACxD,CAAC;IAED,+CAA+C;IAC/C,SAAS,gBAAgB,CAAC,SAAiB;QACzC,cAAc,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QAC/B,IAAI,cAAc,CAAC,MAAM,GAAG,GAAG,CAAC,iBAAiB,EAAE,CAAC;YAClD,cAAc,CAAC,KAAK,EAAE,CAAC;QACzB,CAAC;IACH,CAAC;IAED,SAAS,uBAAuB;QAC9B,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAC1C,MAAM,GAAG,GAAG,cAAc,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC,CAAC;QACtD,OAAO,GAAG,GAAG,cAAc,CAAC,MAAM,CAAC;IACrC,CAAC;IAED,+CAA+C;IAC/C,SAAS,kBAAkB;QACzB,IAAI,aAAa,KAAK,CAAC;YAAE,OAAO,CAAC,CAAC;QAClC,OAAO,WAAW,GAAG,aAAa,CAAC;IACrC,CAAC;IAED,6CAA6C;IAC7C,SAAS,iBAAiB;QACxB,MAAM,SAAS,GAAG,kBAAkB,EAAE,CAAC;QACvC,MAAM,aAAa,GAAG,YAAY,CAAC;QAEnC,2BAA2B;QAC3B,IAAI,YAAY,KAAK,MAAM,EAAE,CAAC;YAC5B,YAAY,GAAG,WAAW,CAAC;QAC7B,CAAC;QACD,8BAA8B;aACzB,IAAI,SAAS,IAAI,GAAG,CAAC,kBAAkB,EAAE,CAAC;YAC7C,YAAY,GAAG,WAAW,CAAC;QAC7B,CAAC;QACD,sBAAsB;aACjB,IACH,YAAY,KAAK,UAAU;YAC3B,cAAc,KAAK,WAAW;YAC9B,cAAc,KAAK,SAAS;YAC5B,mBAAmB,IAAI,GAAG,CAAC,iBAAiB,EAC5C,CAAC;YACD,YAAY,GAAG,UAAU,CAAC;QAC5B,CAAC;QACD,cAAc;aACT,IAAI,aAAa,KAAK,CAAC,EAAE,CAAC;YAC7B,YAAY,GAAG,SAAS,CAAC;QAC3B,CAAC;QACD,WAAW;aACN,CAAC;YACJ,YAAY,GAAG,SAAS,CAAC;QAC3B,CAAC;QAED,2BAA2B;QAC3B,IAAI,aAAa,KAAK,YAAY,EAAE,CAAC;YACnC,IAAI,YAAY,KAAK,WAAW,EAAE,CAAC;gBACjC,SAAS,CAAC,kBAAkB,CAAC,CAAC;YAChC,CAAC;iBAAM,IAAI,YAAY,KAAK,UAAU,EAAE,CAAC;gBACvC,SAAS,CAAC,iBAAiB,CAAC,CAAC;YAC/B,CAAC;iBAAM,IACL,YAAY,KAAK,SAAS;gBAC1B,CAAC,aAAa,KAAK,UAAU,IAAI,aAAa,KAAK,WAAW,CAAC,EAC/D,CAAC;gBACD,SAAS,CAAC,kBAAkB,CAAC,CAAC;YAChC,CAAC;QACH,CAAC;IACH,CAAC;IAED,SAAS,WAAW;QAClB,OAAO,YAAY,KAAK,SAAS,IAAI,YAAY,KAAK,UAAU,CAAC;IACnE,CAAC;IAED,OAAO;QACL,SAAS;YACP,iBAAiB,EAAE,CAAC;YACpB,MAAM,eAAe,GAAG,CAAC,GAAG,cAAc,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,GAAG,CAAC,CAAC,CAAC;YAElE,OAAO;gBACL,UAAU;gBACV,SAAS,EAAE,WAAW,EAAE;gBACxB,KAAK,EAAE,YAAY;gBACnB,SAAS,EAAE,uBAAuB,EAAE;gBACpC,YAAY,EACV,eAAe,CAAC,MAAM,GAAG,CAAC;oBACxB,CAAC,CAAC,mBAAmB,CAAC,eAAe,EAAE,EAAE,CAAC;oBAC1C,CAAC,CAAC,SAAS;gBACf,YAAY,EACV,eAAe,CAAC,MAAM,GAAG,CAAC;oBACxB,CAAC,CAAC,mBAAmB,CAAC,eAAe,EAAE,EAAE,CAAC;oBAC1C,CAAC,CAAC,SAAS;gBACf,YAAY,EACV,eAAe,CAAC,MAAM,GAAG,CAAC;oBACxB,CAAC,CAAC,mBAAmB,CAAC,eAAe,EAAE,EAAE,CAAC;oBAC1C,CAAC,CAAC,SAAS;gBACf,SAAS,EAAE,kBAAkB,EAAE;gBAC/B,mBAAmB;gBACnB,oBAAoB;gBACpB,aAAa,EAAE,aAAa,CAAC,WAAW,EAAE;gBAC1C,eAAe,EAAE,eAAe,EAAE,WAAW,EAAE;gBAC/C,aAAa,EAAE,aAAa,EAAE,WAAW,EAAE;gBAC3C,SAAS;gBACT,aAAa;gBACb,YAAY;gBACZ,cAAc;gBACd,aAAa;gBACb,WAAW;gBACX,aAAa,EACX,aAAa,GAAG,CAAC;oBACf,CAAC,CAAC,CAAC,CAAC,aAAa,GAAG,WAAW,CAAC,GAAG,aAAa,CAAC,GAAG,GAAG;oBACvD,CAAC,CAAC,SAAS;aAChB,CAAC;QACJ,CAAC;QAED,aAAa,CAAC,SAAiB;YAC7B,aAAa,EAAE,CAAC;YAChB,oBAAoB,EAAE,CAAC;YACvB,mBAAmB,GAAG,CAAC,CAAC;YACxB,eAAe,GAAG,IAAI,IAAI,EAAE,CAAC;YAC7B,aAAa,GAAG,IAAI,IAAI,EAAE,CAAC;YAE3B,gBAAgB,CAAC,SAAS,CAAC,CAAC;YAC5B,SAAS,CAAC,qBAAqB,EAAE,EAAE,SAAS,EAAE,CAAC,CAAC;QAClD,CAAC;QAED,aAAa,CAAC,KAAa,EAAE,SAAkB;YAC7C,aAAa,EAAE,CAAC;YAChB,WAAW,EAAE,CAAC;YACd,mBAAmB,EAAE,CAAC;YACtB,oBAAoB,GAAG,CAAC,CAAC;YACzB,aAAa,GAAG,IAAI,IAAI,EAAE,CAAC;YAC3B,aAAa,GAAG,IAAI,IAAI,EAAE,CAAC;YAC3B,SAAS,GAAG,KAAK,CAAC;YAClB,aAAa,GAAG,SAAS,CAAC;YAE1B,SAAS,CAAC,qBAAqB,EAAE,EAAE,KAAK,EAAE,SAAS,EAAE,CAAC,CAAC;QACzD,CAAC;QAED,kBAAkB,CAAC,KAAmB;YACpC,MAAM,QAAQ,GAAG,YAAY,CAAC;YAC9B,YAAY,GAAG,KAAK,CAAC;YACrB,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;gBACvB,iBAAiB,EAAE,CAAC;gBACpB,SAAS,CAAC,uBAAuB,EAAE;oBACjC,YAAY,EAAE,KAAK;oBACnB,oBAAoB,EAAE,QAAQ;iBAC/B,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,oBAAoB,CAAC,KAAyB;YAC5C,MAAM,QAAQ,GAAG,cAAc,CAAC;YAChC,cAAc,GAAG,KAAK,CAAC;YACvB,IAAI,QAAQ,KAAK,KAAK,EAAE,CAAC;gBACvB,iBAAiB,EAAE,CAAC;gBACpB,SAAS,CAAC,uBAAuB,EAAE;oBACjC,cAAc,EAAE,KAAK;oBACrB,sBAAsB,EAAE,QAAQ;iBACjC,CAAC,CAAC;YACL,CAAC;QACH,CAAC;QAED,KAAK,CAAC,kBAAkB;YACtB,MAAM,OAAO,GAAG,MAAM,CAAC,UAAU,EAAE,CAAC;YACpC,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,CAAC;YAE7B,SAAS,CAAC,sBAAsB,EAAE,EAAE,OAAO,EAAE,CAAC,CAAC;YAE/C,IAAI,CAAC,aAAa,EAAE,CAAC;gBACnB,oDAAoD;gBACpD,OAAO;oBACL,OAAO;oBACP,UAAU;oBACV,OAAO,EAAE,KAAK;oBACd,SAAS,EAAE,CAAC;oBACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,KAAK,EAAE,qCAAqC;oBAC5C,SAAS,EAAE,gBAAgB,CAAC,YAAY;oBACxC,SAAS,EAAE,QAAQ;iBACpB,CAAC;YACJ,CAAC;YAED,oDAAoD;YACpD,IAAI,SAAoD,CAAC;YACzD,MAAM,cAAc,GAAG,IAAI,OAAO,CAAQ,CAAC,CAAC,EAAE,MAAM,EAAE,EAAE;gBACtD,SAAS,GAAG,UAAU,CACpB,GAAG,EAAE,GAAG,MAAM,CAAC,IAAI,KAAK,CAAC,sBAAsB,CAAC,CAAC,CAAC,CAAC,CAAC,EACpD,GAAG,CAAC,SAAS,CACd,CAAC;YACJ,CAAC,CAAC,CAAC;YAEH,IAAI,CAAC;gBACH,MAAM,MAAM,GAAG,MAAM,OAAO,CAAC,IAAI,CAAC;oBAChC,aAAa,EAAE;oBACf,cAAc;iBACf,CAAC,CAAC;gBAEH,mBAAmB;gBACnB,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC5B,YAAY,CAAC,SAAS,CAAC,CAAC;gBAC1B,CAAC;gBAED,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;gBAEzC,IAAI,MAAM,CAAC,OAAO,EAAE,CAAC;oBACnB,IAAI,CAAC,aAAa,CAAC,SAAS,CAAC,CAAC;gBAChC,CAAC;qBAAM,CAAC;oBACN,IAAI,CAAC,aAAa,CAChB,MAAM,CAAC,KAAK,IAAI,qBAAqB,EACrC,gBAAgB,CAAC,YAAY,CAC9B,CAAC;gBACJ,CAAC;gBAED,OAAO;oBACL,OAAO;oBACP,UAAU;oBACV,OAAO,EAAE,MAAM,CAAC,OAAO;oBACvB,SAAS;oBACT,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,KAAK,EAAE,MAAM,CAAC,KAAK;oBACnB,SAAS,EAAE,QAAQ;iBACpB,CAAC;YACJ,CAAC;YAAC,OAAO,KAAK,EAAE,CAAC;gBACf,gCAAgC;gBAChC,IAAI,SAAS,KAAK,SAAS,EAAE,CAAC;oBAC5B,YAAY,CAAC,SAAS,CAAC,CAAC;gBAC1B,CAAC;gBACD,MAAM,SAAS,GAAG,IAAI,CAAC,GAAG,EAAE,GAAG,SAAS,CAAC;gBACzC,MAAM,YAAY,GAChB,KAAK,YAAY,KAAK,CAAC,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,eAAe,CAAC;gBAE3D,IAAI,CAAC,aAAa,CAAC,YAAY,EAAE,gBAAgB,CAAC,aAAa,CAAC,CAAC;gBAEjE,OAAO;oBACL,OAAO;oBACP,UAAU;oBACV,OAAO,EAAE,KAAK;oBACd,SAAS;oBACT,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;oBACnC,KAAK,EAAE,YAAY;oBACnB,SAAS,EAAE,gBAAgB,CAAC,aAAa;oBACzC,SAAS,EAAE,QAAQ;iBACpB,CAAC;YACJ,CAAC;QACH,CAAC;QAED,SAAS;YACP,OAAO,CAAC,GAAG,MAAM,CAAC,CAAC;QACrB,CAAC;QAED,KAAK;YACH,cAAc,CAAC,MAAM,GAAG,CAAC,CAAC;YAC1B,aAAa,GAAG,CAAC,CAAC;YAClB,WAAW,GAAG,CAAC,CAAC;YAChB,mBAAmB,GAAG,CAAC,CAAC;YACxB,oBAAoB,GAAG,CAAC,CAAC;YACzB,aAAa,GAAG,IAAI,IAAI,EAAE,CAAC;YAC3B,eAAe,GAAG,SAAS,CAAC;YAC5B,aAAa,GAAG,SAAS,CAAC;YAC1B,SAAS,GAAG,SAAS,CAAC;YACtB,aAAa,GAAG,SAAS,CAAC;YAC1B,YAAY,GAAG,QAAQ,CAAC;YACxB,cAAc,GAAG,QAAQ,CAAC;YAC1B,YAAY,GAAG,SAAS,CAAC;YACzB,MAAM,CAAC,MAAM,GAAG,CAAC,CAAC;QACpB,CAAC;KACF,CAAC;AACJ,CAAC;AAED;;GAEG;AACH,MAAM,OAAO,kBAAmB,SAAQ,KAAK;IAEzB;IACA;IAFlB,YACkB,IAAY,EACZ,UAAkB,EAClC,OAAe;QAEf,KAAK,CAAC,OAAO,CAAC,CAAC;QAJC,SAAI,GAAJ,IAAI,CAAQ;QACZ,eAAU,GAAV,UAAU,CAAQ;QAIlC,IAAI,CAAC,IAAI,GAAG,oBAAoB,CAAC;IACnC,CAAC;IAED,MAAM,CAAC,YAAY,CAAC,UAAkB;QACpC,OAAO,IAAI,kBAAkB,CAC3B,gBAAgB,CAAC,aAAa,EAC9B,UAAU,EACV,uCAAuC,UAAU,EAAE,CACpD,CAAC;IACJ,CAAC;IAED,MAAM,CAAC,mBAAmB,CAAC,UAAkB;QAC3C,OAAO,IAAI,kBAAkB,CAC3B,gBAAgB,CAAC,oBAAoB,EACrC,UAAU,EACV,YAAY,UAAU,iBAAiB,CACxC,CAAC;IACJ,CAAC;CACF"}
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @defai.digital/provider-domain
|
|
3
|
+
*
|
|
4
|
+
* Provider Resilience Domain
|
|
5
|
+
*
|
|
6
|
+
* Provides circuit breaker, rate limiting, and health monitoring
|
|
7
|
+
* for intelligent provider management and fault tolerance.
|
|
8
|
+
*/
|
|
9
|
+
export { createCircuitBreaker, CircuitBreakerError, type CircuitBreaker, type CircuitEventListener, } from './circuit-breaker.js';
|
|
10
|
+
export { createRateLimiter, RateLimitError, type RateLimiter, type RateLimitEventListener, } from './rate-limiter.js';
|
|
11
|
+
export { createHealthMonitor, HealthMonitorError, type HealthMonitor, type HealthEventListener, type HealthCheckFn, } from './health-monitor.js';
|
|
12
|
+
export { createManagedProvider, createProviderManager, type ProviderConfig, type ManagedProvider, type ProviderManager, } from './provider-manager.js';
|
|
13
|
+
//# sourceMappingURL=index.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,EACL,oBAAoB,EACpB,mBAAmB,EACnB,KAAK,cAAc,EACnB,KAAK,oBAAoB,GAC1B,MAAM,sBAAsB,CAAC;AAG9B,OAAO,EACL,iBAAiB,EACjB,cAAc,EACd,KAAK,WAAW,EAChB,KAAK,sBAAsB,GAC5B,MAAM,mBAAmB,CAAC;AAG3B,OAAO,EACL,mBAAmB,EACnB,kBAAkB,EAClB,KAAK,aAAa,EAClB,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACnB,MAAM,qBAAqB,CAAC;AAG7B,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,KAAK,cAAc,EACnB,KAAK,eAAe,EACpB,KAAK,eAAe,GACrB,MAAM,uBAAuB,CAAC"}
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @defai.digital/provider-domain
|
|
3
|
+
*
|
|
4
|
+
* Provider Resilience Domain
|
|
5
|
+
*
|
|
6
|
+
* Provides circuit breaker, rate limiting, and health monitoring
|
|
7
|
+
* for intelligent provider management and fault tolerance.
|
|
8
|
+
*/
|
|
9
|
+
// Circuit Breaker
|
|
10
|
+
export { createCircuitBreaker, CircuitBreakerError, } from './circuit-breaker.js';
|
|
11
|
+
// Rate Limiter
|
|
12
|
+
export { createRateLimiter, RateLimitError, } from './rate-limiter.js';
|
|
13
|
+
// Health Monitor
|
|
14
|
+
export { createHealthMonitor, HealthMonitorError, } from './health-monitor.js';
|
|
15
|
+
// Provider Manager
|
|
16
|
+
export { createManagedProvider, createProviderManager, } from './provider-manager.js';
|
|
17
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,kBAAkB;AAClB,OAAO,EACL,oBAAoB,EACpB,mBAAmB,GAGpB,MAAM,sBAAsB,CAAC;AAE9B,eAAe;AACf,OAAO,EACL,iBAAiB,EACjB,cAAc,GAGf,MAAM,mBAAmB,CAAC;AAE3B,iBAAiB;AACjB,OAAO,EACL,mBAAmB,EACnB,kBAAkB,GAInB,MAAM,qBAAqB,CAAC;AAE7B,mBAAmB;AACnB,OAAO,EACL,qBAAqB,EACrB,qBAAqB,GAItB,MAAM,uBAAuB,CAAC"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Manager
|
|
3
|
+
*
|
|
4
|
+
* Integrates circuit breaker, rate limiter, and health monitor
|
|
5
|
+
* to provide unified provider resilience management.
|
|
6
|
+
*
|
|
7
|
+
* This is the main entry point for provider resilience features.
|
|
8
|
+
*/
|
|
9
|
+
import type { ProviderCircuitBreakerConfig as CircuitBreakerConfig, ProviderRateLimitConfig as RateLimitConfig, HealthCheckConfig, HealthStatus } from '@defai.digital/contracts';
|
|
10
|
+
import { CircuitBreaker } from './circuit-breaker.js';
|
|
11
|
+
import { RateLimiter } from './rate-limiter.js';
|
|
12
|
+
import { HealthMonitor, HealthCheckFn } from './health-monitor.js';
|
|
13
|
+
/**
|
|
14
|
+
* Provider configuration
|
|
15
|
+
*/
|
|
16
|
+
export interface ProviderConfig {
|
|
17
|
+
/** Provider identifier */
|
|
18
|
+
providerId: string;
|
|
19
|
+
/** Circuit breaker configuration */
|
|
20
|
+
circuitBreaker?: Partial<CircuitBreakerConfig>;
|
|
21
|
+
/** Rate limiter configuration */
|
|
22
|
+
rateLimiter?: Partial<RateLimitConfig>;
|
|
23
|
+
/** Health monitor configuration */
|
|
24
|
+
healthMonitor?: Partial<HealthCheckConfig>;
|
|
25
|
+
/** Health check function for active checks */
|
|
26
|
+
healthCheckFn?: HealthCheckFn;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* Managed provider with resilience features
|
|
30
|
+
*/
|
|
31
|
+
export interface ManagedProvider {
|
|
32
|
+
/** Provider identifier */
|
|
33
|
+
readonly providerId: string;
|
|
34
|
+
/** Circuit breaker instance */
|
|
35
|
+
readonly circuitBreaker: CircuitBreaker;
|
|
36
|
+
/** Rate limiter instance */
|
|
37
|
+
readonly rateLimiter: RateLimiter;
|
|
38
|
+
/** Health monitor instance */
|
|
39
|
+
readonly healthMonitor: HealthMonitor;
|
|
40
|
+
/** Check if provider can handle a request */
|
|
41
|
+
canExecute(tokens?: number): boolean;
|
|
42
|
+
/** Execute a request with resilience wrapping */
|
|
43
|
+
execute<T>(fn: () => Promise<T>, tokens?: number): Promise<T>;
|
|
44
|
+
/** Get combined health status */
|
|
45
|
+
getHealth(): HealthStatus;
|
|
46
|
+
/** Reset all resilience state */
|
|
47
|
+
reset(): void;
|
|
48
|
+
}
|
|
49
|
+
/**
|
|
50
|
+
* Provider manager for multiple providers
|
|
51
|
+
*/
|
|
52
|
+
export interface ProviderManager {
|
|
53
|
+
/** Register a provider */
|
|
54
|
+
register(config: ProviderConfig): ManagedProvider;
|
|
55
|
+
/** Get a registered provider */
|
|
56
|
+
get(providerId: string): ManagedProvider | undefined;
|
|
57
|
+
/** List all registered providers */
|
|
58
|
+
list(): ManagedProvider[];
|
|
59
|
+
/** Get health status for all providers */
|
|
60
|
+
getHealthAll(): Map<string, HealthStatus>;
|
|
61
|
+
/** Get the healthiest available provider */
|
|
62
|
+
selectBest(providerIds?: string[]): ManagedProvider | undefined;
|
|
63
|
+
/** Remove a provider */
|
|
64
|
+
remove(providerId: string): boolean;
|
|
65
|
+
/** Reset all providers */
|
|
66
|
+
resetAll(): void;
|
|
67
|
+
}
|
|
68
|
+
/**
|
|
69
|
+
* Creates a managed provider with integrated resilience
|
|
70
|
+
*/
|
|
71
|
+
export declare function createManagedProvider(config: ProviderConfig): ManagedProvider;
|
|
72
|
+
/**
|
|
73
|
+
* Creates a provider manager
|
|
74
|
+
*/
|
|
75
|
+
export declare function createProviderManager(): ProviderManager;
|
|
76
|
+
//# sourceMappingURL=provider-manager.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-manager.d.ts","sourceRoot":"","sources":["../src/provider-manager.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,OAAO,KAAK,EACV,4BAA4B,IAAI,oBAAoB,EACpD,uBAAuB,IAAI,eAAe,EAC1C,iBAAiB,EACjB,YAAY,EACb,MAAM,0BAA0B,CAAC;AAElC,OAAO,EAEL,cAAc,EAEf,MAAM,sBAAsB,CAAC;AAC9B,OAAO,EAEL,WAAW,EAEZ,MAAM,mBAAmB,CAAC;AAC3B,OAAO,EAEL,aAAa,EACb,aAAa,EACd,MAAM,qBAAqB,CAAC;AAE7B;;GAEG;AACH,MAAM,WAAW,cAAc;IAC7B,0BAA0B;IAC1B,UAAU,EAAE,MAAM,CAAC;IAEnB,oCAAoC;IACpC,cAAc,CAAC,EAAE,OAAO,CAAC,oBAAoB,CAAC,CAAC;IAE/C,iCAAiC;IACjC,WAAW,CAAC,EAAE,OAAO,CAAC,eAAe,CAAC,CAAC;IAEvC,mCAAmC;IACnC,aAAa,CAAC,EAAE,OAAO,CAAC,iBAAiB,CAAC,CAAC;IAE3C,8CAA8C;IAC9C,aAAa,CAAC,EAAE,aAAa,CAAC;CAC/B;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,0BAA0B;IAC1B,QAAQ,CAAC,UAAU,EAAE,MAAM,CAAC;IAE5B,+BAA+B;IAC/B,QAAQ,CAAC,cAAc,EAAE,cAAc,CAAC;IAExC,4BAA4B;IAC5B,QAAQ,CAAC,WAAW,EAAE,WAAW,CAAC;IAElC,8BAA8B;IAC9B,QAAQ,CAAC,aAAa,EAAE,aAAa,CAAC;IAEtC,6CAA6C;IAC7C,UAAU,CAAC,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAErC,iDAAiD;IACjD,OAAO,CAAC,CAAC,EAAE,EAAE,EAAE,MAAM,OAAO,CAAC,CAAC,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAE9D,iCAAiC;IACjC,SAAS,IAAI,YAAY,CAAC;IAE1B,iCAAiC;IACjC,KAAK,IAAI,IAAI,CAAC;CACf;AAED;;GAEG;AACH,MAAM,WAAW,eAAe;IAC9B,0BAA0B;IAC1B,QAAQ,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CAAC;IAElD,gCAAgC;IAChC,GAAG,CAAC,UAAU,EAAE,MAAM,GAAG,eAAe,GAAG,SAAS,CAAC;IAErD,oCAAoC;IACpC,IAAI,IAAI,eAAe,EAAE,CAAC;IAE1B,0CAA0C;IAC1C,YAAY,IAAI,GAAG,CAAC,MAAM,EAAE,YAAY,CAAC,CAAC;IAE1C,4CAA4C;IAC5C,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,EAAE,GAAG,eAAe,GAAG,SAAS,CAAC;IAEhE,wBAAwB;IACxB,MAAM,CAAC,UAAU,EAAE,MAAM,GAAG,OAAO,CAAC;IAEpC,0BAA0B;IAC1B,QAAQ,IAAI,IAAI,CAAC;CAClB;AAED;;GAEG;AACH,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,cAAc,GAAG,eAAe,CAkG7E;AAED;;GAEG;AACH,wBAAgB,qBAAqB,IAAI,eAAe,CA6EvD"}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Provider Manager
|
|
3
|
+
*
|
|
4
|
+
* Integrates circuit breaker, rate limiter, and health monitor
|
|
5
|
+
* to provide unified provider resilience management.
|
|
6
|
+
*
|
|
7
|
+
* This is the main entry point for provider resilience features.
|
|
8
|
+
*/
|
|
9
|
+
import { createCircuitBreaker, CircuitBreakerError, } from './circuit-breaker.js';
|
|
10
|
+
import { createRateLimiter, RateLimitError, } from './rate-limiter.js';
|
|
11
|
+
import { createHealthMonitor, } from './health-monitor.js';
|
|
12
|
+
/**
|
|
13
|
+
* Creates a managed provider with integrated resilience
|
|
14
|
+
*/
|
|
15
|
+
export function createManagedProvider(config) {
|
|
16
|
+
const { providerId, circuitBreaker: cbConfig, rateLimiter: rlConfig, healthMonitor: hmConfig, healthCheckFn, } = config;
|
|
17
|
+
const circuitBreaker = createCircuitBreaker(providerId, cbConfig);
|
|
18
|
+
const rateLimiter = createRateLimiter(providerId, rlConfig);
|
|
19
|
+
const healthMonitor = createHealthMonitor(providerId, hmConfig, healthCheckFn);
|
|
20
|
+
// Sync circuit breaker state to health monitor
|
|
21
|
+
const originalRecordSuccess = circuitBreaker.recordSuccess.bind(circuitBreaker);
|
|
22
|
+
const originalRecordFailure = circuitBreaker.recordFailure.bind(circuitBreaker);
|
|
23
|
+
circuitBreaker.recordSuccess = () => {
|
|
24
|
+
originalRecordSuccess();
|
|
25
|
+
healthMonitor.updateCircuitState(circuitBreaker.getState().state);
|
|
26
|
+
};
|
|
27
|
+
circuitBreaker.recordFailure = (error) => {
|
|
28
|
+
originalRecordFailure(error);
|
|
29
|
+
healthMonitor.updateCircuitState(circuitBreaker.getState().state);
|
|
30
|
+
};
|
|
31
|
+
// Sync rate limiter state to health monitor
|
|
32
|
+
const originalTryConsume = rateLimiter.tryConsume.bind(rateLimiter);
|
|
33
|
+
rateLimiter.tryConsume = (tokens) => {
|
|
34
|
+
const result = originalTryConsume(tokens);
|
|
35
|
+
healthMonitor.updateRateLimitState(rateLimiter.getState().state);
|
|
36
|
+
return result;
|
|
37
|
+
};
|
|
38
|
+
return {
|
|
39
|
+
providerId,
|
|
40
|
+
circuitBreaker,
|
|
41
|
+
rateLimiter,
|
|
42
|
+
healthMonitor,
|
|
43
|
+
canExecute(tokens = 1) {
|
|
44
|
+
return circuitBreaker.canExecute() && rateLimiter.canRequest(tokens);
|
|
45
|
+
},
|
|
46
|
+
async execute(fn, tokens = 1) {
|
|
47
|
+
// Check circuit breaker
|
|
48
|
+
if (!circuitBreaker.canExecute()) {
|
|
49
|
+
throw CircuitBreakerError.circuitOpen(providerId);
|
|
50
|
+
}
|
|
51
|
+
// Check rate limiter
|
|
52
|
+
if (!rateLimiter.tryConsume(tokens)) {
|
|
53
|
+
const waitTime = rateLimiter.getWaitTime(tokens);
|
|
54
|
+
throw RateLimitError.exceeded(providerId, waitTime);
|
|
55
|
+
}
|
|
56
|
+
const startTime = Date.now();
|
|
57
|
+
try {
|
|
58
|
+
const result = await fn();
|
|
59
|
+
const latencyMs = Date.now() - startTime;
|
|
60
|
+
// Record success
|
|
61
|
+
circuitBreaker.recordSuccess();
|
|
62
|
+
healthMonitor.recordSuccess(latencyMs);
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
catch (error) {
|
|
66
|
+
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
|
|
67
|
+
// Check if it's a rate limit error from the API
|
|
68
|
+
if (isRateLimitError(error)) {
|
|
69
|
+
const retryAfter = extractRetryAfter(error);
|
|
70
|
+
rateLimiter.recordExternalLimit(retryAfter);
|
|
71
|
+
healthMonitor.recordFailure(errorMessage, 'RATE_LIMIT');
|
|
72
|
+
throw RateLimitError.exceeded(providerId, retryAfter);
|
|
73
|
+
}
|
|
74
|
+
// Record failure
|
|
75
|
+
circuitBreaker.recordFailure(errorMessage);
|
|
76
|
+
healthMonitor.recordFailure(errorMessage);
|
|
77
|
+
throw error;
|
|
78
|
+
}
|
|
79
|
+
},
|
|
80
|
+
getHealth() {
|
|
81
|
+
return healthMonitor.getStatus();
|
|
82
|
+
},
|
|
83
|
+
reset() {
|
|
84
|
+
circuitBreaker.reset();
|
|
85
|
+
rateLimiter.reset();
|
|
86
|
+
healthMonitor.reset();
|
|
87
|
+
},
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
/**
|
|
91
|
+
* Creates a provider manager
|
|
92
|
+
*/
|
|
93
|
+
export function createProviderManager() {
|
|
94
|
+
const providers = new Map();
|
|
95
|
+
return {
|
|
96
|
+
register(config) {
|
|
97
|
+
const provider = createManagedProvider(config);
|
|
98
|
+
providers.set(config.providerId, provider);
|
|
99
|
+
return provider;
|
|
100
|
+
},
|
|
101
|
+
get(providerId) {
|
|
102
|
+
return providers.get(providerId);
|
|
103
|
+
},
|
|
104
|
+
list() {
|
|
105
|
+
return Array.from(providers.values());
|
|
106
|
+
},
|
|
107
|
+
getHealthAll() {
|
|
108
|
+
const healthMap = new Map();
|
|
109
|
+
providers.forEach((provider, id) => {
|
|
110
|
+
healthMap.set(id, provider.getHealth());
|
|
111
|
+
});
|
|
112
|
+
return healthMap;
|
|
113
|
+
},
|
|
114
|
+
selectBest(providerIds) {
|
|
115
|
+
const candidates = providerIds
|
|
116
|
+
? providerIds
|
|
117
|
+
.map((id) => providers.get(id))
|
|
118
|
+
.filter((p) => p !== undefined)
|
|
119
|
+
: Array.from(providers.values());
|
|
120
|
+
// Filter to available providers
|
|
121
|
+
const available = candidates.filter((p) => {
|
|
122
|
+
const health = p.getHealth();
|
|
123
|
+
return health.available && p.canExecute();
|
|
124
|
+
});
|
|
125
|
+
if (available.length === 0)
|
|
126
|
+
return undefined;
|
|
127
|
+
// Sort by health score (lower latency, lower error rate = better)
|
|
128
|
+
available.sort((a, b) => {
|
|
129
|
+
const healthA = a.getHealth();
|
|
130
|
+
const healthB = b.getHealth();
|
|
131
|
+
// Prefer healthy over degraded
|
|
132
|
+
if (healthA.level === 'healthy' && healthB.level !== 'healthy')
|
|
133
|
+
return -1;
|
|
134
|
+
if (healthB.level === 'healthy' && healthA.level !== 'healthy')
|
|
135
|
+
return 1;
|
|
136
|
+
// Then by error rate
|
|
137
|
+
const errorDiff = healthA.errorRate - healthB.errorRate;
|
|
138
|
+
if (Math.abs(errorDiff) > 0.1)
|
|
139
|
+
return errorDiff;
|
|
140
|
+
// Then by latency
|
|
141
|
+
return healthA.latencyMs - healthB.latencyMs;
|
|
142
|
+
});
|
|
143
|
+
return available[0];
|
|
144
|
+
},
|
|
145
|
+
remove(providerId) {
|
|
146
|
+
const provider = providers.get(providerId);
|
|
147
|
+
if (provider) {
|
|
148
|
+
provider.reset();
|
|
149
|
+
providers.delete(providerId);
|
|
150
|
+
return true;
|
|
151
|
+
}
|
|
152
|
+
return false;
|
|
153
|
+
},
|
|
154
|
+
resetAll() {
|
|
155
|
+
providers.forEach((provider) => {
|
|
156
|
+
provider.reset();
|
|
157
|
+
});
|
|
158
|
+
},
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
/**
|
|
162
|
+
* Helper to detect rate limit errors
|
|
163
|
+
*/
|
|
164
|
+
function isRateLimitError(error) {
|
|
165
|
+
if (error instanceof Error) {
|
|
166
|
+
const message = error.message.toLowerCase();
|
|
167
|
+
return (message.includes('rate limit') ||
|
|
168
|
+
message.includes('too many requests') ||
|
|
169
|
+
message.includes('429'));
|
|
170
|
+
}
|
|
171
|
+
return false;
|
|
172
|
+
}
|
|
173
|
+
/**
|
|
174
|
+
* Helper to extract retry-after from error
|
|
175
|
+
*/
|
|
176
|
+
function extractRetryAfter(error) {
|
|
177
|
+
if (error instanceof Error) {
|
|
178
|
+
// Try to extract from error message or properties
|
|
179
|
+
const match = /retry[- ]?after[:\s]*(\d+)/i.exec(error.message);
|
|
180
|
+
if (match?.[1]) {
|
|
181
|
+
return parseInt(match[1], 10) * 1000; // Convert to ms
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
return undefined;
|
|
185
|
+
}
|
|
186
|
+
//# sourceMappingURL=provider-manager.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"provider-manager.js","sourceRoot":"","sources":["../src/provider-manager.ts"],"names":[],"mappings":"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"}
|