claude-flow 1.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/README.md +612 -0
- package/bin/claude-flow +0 -0
- package/bin/claude-flow-simple +0 -0
- package/bin/claude-flow-typecheck +0 -0
- package/deno.json +84 -0
- package/package.json +45 -0
- package/scripts/check-links.ts +274 -0
- package/scripts/check-performance-regression.ts +168 -0
- package/scripts/claude-sparc.sh +562 -0
- package/scripts/coverage-report.ts +692 -0
- package/scripts/demo-task-system.ts +224 -0
- package/scripts/install.js +72 -0
- package/scripts/test-batch-tasks.ts +29 -0
- package/scripts/test-coordination-features.ts +238 -0
- package/scripts/test-mcp.ts +251 -0
- package/scripts/test-runner.ts +571 -0
- package/scripts/validate-examples.ts +288 -0
- package/src/cli/cli-core.ts +273 -0
- package/src/cli/commands/agent.ts +83 -0
- package/src/cli/commands/config.ts +442 -0
- package/src/cli/commands/help.ts +765 -0
- package/src/cli/commands/index.ts +963 -0
- package/src/cli/commands/mcp.ts +191 -0
- package/src/cli/commands/memory.ts +74 -0
- package/src/cli/commands/monitor.ts +403 -0
- package/src/cli/commands/session.ts +595 -0
- package/src/cli/commands/start.ts +156 -0
- package/src/cli/commands/status.ts +345 -0
- package/src/cli/commands/task.ts +79 -0
- package/src/cli/commands/workflow.ts +763 -0
- package/src/cli/completion.ts +553 -0
- package/src/cli/formatter.ts +310 -0
- package/src/cli/index.ts +211 -0
- package/src/cli/main.ts +23 -0
- package/src/cli/repl.ts +1050 -0
- package/src/cli/simple-cli.js +211 -0
- package/src/cli/simple-cli.ts +211 -0
- package/src/coordination/README.md +400 -0
- package/src/coordination/advanced-scheduler.ts +487 -0
- package/src/coordination/circuit-breaker.ts +366 -0
- package/src/coordination/conflict-resolution.ts +490 -0
- package/src/coordination/dependency-graph.ts +475 -0
- package/src/coordination/index.ts +63 -0
- package/src/coordination/manager.ts +460 -0
- package/src/coordination/messaging.ts +290 -0
- package/src/coordination/metrics.ts +585 -0
- package/src/coordination/resources.ts +322 -0
- package/src/coordination/scheduler.ts +390 -0
- package/src/coordination/work-stealing.ts +224 -0
- package/src/core/config.ts +627 -0
- package/src/core/event-bus.ts +186 -0
- package/src/core/json-persistence.ts +183 -0
- package/src/core/logger.ts +262 -0
- package/src/core/orchestrator-fixed.ts +312 -0
- package/src/core/orchestrator.ts +1234 -0
- package/src/core/persistence.ts +276 -0
- package/src/mcp/auth.ts +438 -0
- package/src/mcp/claude-flow-tools.ts +1280 -0
- package/src/mcp/load-balancer.ts +510 -0
- package/src/mcp/router.ts +240 -0
- package/src/mcp/server.ts +548 -0
- package/src/mcp/session-manager.ts +418 -0
- package/src/mcp/tools.ts +180 -0
- package/src/mcp/transports/base.ts +21 -0
- package/src/mcp/transports/http.ts +457 -0
- package/src/mcp/transports/stdio.ts +254 -0
- package/src/memory/backends/base.ts +22 -0
- package/src/memory/backends/markdown.ts +283 -0
- package/src/memory/backends/sqlite.ts +329 -0
- package/src/memory/cache.ts +238 -0
- package/src/memory/indexer.ts +238 -0
- package/src/memory/manager.ts +572 -0
- package/src/terminal/adapters/base.ts +29 -0
- package/src/terminal/adapters/native.ts +504 -0
- package/src/terminal/adapters/vscode.ts +340 -0
- package/src/terminal/manager.ts +308 -0
- package/src/terminal/pool.ts +271 -0
- package/src/terminal/session.ts +250 -0
- package/src/terminal/vscode-bridge.ts +242 -0
- package/src/utils/errors.ts +231 -0
- package/src/utils/helpers.ts +476 -0
- package/src/utils/types.ts +493 -0
|
@@ -0,0 +1,476 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Utility helper functions for Claude-Flow
|
|
3
|
+
*/
|
|
4
|
+
|
|
5
|
+
// Utility helper functions
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Generates a unique identifier
|
|
9
|
+
*/
|
|
10
|
+
export function generateId(prefix?: string): string {
|
|
11
|
+
const timestamp = Date.now().toString(36);
|
|
12
|
+
const random = Math.random().toString(36).substr(2, 9);
|
|
13
|
+
return prefix ? `${prefix}_${timestamp}_${random}` : `${timestamp}_${random}`;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
/**
|
|
17
|
+
* Creates a timeout promise that rejects after the specified time
|
|
18
|
+
*/
|
|
19
|
+
export function timeout<T>(promise: Promise<T>, ms: number, message?: string): Promise<T> {
|
|
20
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
21
|
+
let completed = false;
|
|
22
|
+
|
|
23
|
+
const timeoutPromise = new Promise<never>((_, reject) => {
|
|
24
|
+
timeoutId = setTimeout(() => {
|
|
25
|
+
if (!completed) {
|
|
26
|
+
completed = true;
|
|
27
|
+
reject(new Error(message || 'Operation timed out'));
|
|
28
|
+
}
|
|
29
|
+
}, ms);
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
const wrappedPromise = promise.then((result) => {
|
|
33
|
+
completed = true;
|
|
34
|
+
if (timeoutId !== undefined) {
|
|
35
|
+
clearTimeout(timeoutId);
|
|
36
|
+
}
|
|
37
|
+
return result;
|
|
38
|
+
}, (error) => {
|
|
39
|
+
completed = true;
|
|
40
|
+
if (timeoutId !== undefined) {
|
|
41
|
+
clearTimeout(timeoutId);
|
|
42
|
+
}
|
|
43
|
+
throw error;
|
|
44
|
+
});
|
|
45
|
+
|
|
46
|
+
return Promise.race([
|
|
47
|
+
wrappedPromise,
|
|
48
|
+
timeoutPromise,
|
|
49
|
+
]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
/**
|
|
53
|
+
* Delays execution for specified milliseconds
|
|
54
|
+
*/
|
|
55
|
+
export function delay(ms: number): Promise<void> {
|
|
56
|
+
return new Promise((resolve) => setTimeout(resolve, ms));
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
/**
|
|
60
|
+
* Retries a function with exponential backoff
|
|
61
|
+
*/
|
|
62
|
+
export async function retry<T>(
|
|
63
|
+
fn: () => Promise<T>,
|
|
64
|
+
options: {
|
|
65
|
+
maxAttempts?: number;
|
|
66
|
+
initialDelay?: number;
|
|
67
|
+
maxDelay?: number;
|
|
68
|
+
factor?: number;
|
|
69
|
+
onRetry?: (attempt: number, error: Error) => void;
|
|
70
|
+
} = {},
|
|
71
|
+
): Promise<T> {
|
|
72
|
+
const {
|
|
73
|
+
maxAttempts = 3,
|
|
74
|
+
initialDelay = 1000,
|
|
75
|
+
maxDelay = 30000,
|
|
76
|
+
factor = 2,
|
|
77
|
+
onRetry,
|
|
78
|
+
} = options;
|
|
79
|
+
|
|
80
|
+
let lastError: Error;
|
|
81
|
+
let delayMs = initialDelay;
|
|
82
|
+
|
|
83
|
+
for (let attempt = 1; attempt <= maxAttempts; attempt++) {
|
|
84
|
+
try {
|
|
85
|
+
return await fn();
|
|
86
|
+
} catch (error) {
|
|
87
|
+
lastError = error instanceof Error ? error : new Error(String(error));
|
|
88
|
+
|
|
89
|
+
if (attempt === maxAttempts) {
|
|
90
|
+
throw lastError;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
if (onRetry) {
|
|
94
|
+
onRetry(attempt, lastError);
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
await delay(Math.min(delayMs, maxDelay));
|
|
98
|
+
delayMs *= factor;
|
|
99
|
+
}
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
throw lastError!;
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Debounces a function
|
|
107
|
+
*/
|
|
108
|
+
export function debounce<T extends (...args: unknown[]) => unknown>(
|
|
109
|
+
fn: T,
|
|
110
|
+
delayMs: number,
|
|
111
|
+
): (...args: Parameters<T>) => void {
|
|
112
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
113
|
+
|
|
114
|
+
return (...args: Parameters<T>) => {
|
|
115
|
+
if (timeoutId !== undefined) {
|
|
116
|
+
clearTimeout(timeoutId);
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
timeoutId = setTimeout(() => {
|
|
120
|
+
fn(...args);
|
|
121
|
+
timeoutId = undefined;
|
|
122
|
+
}, delayMs);
|
|
123
|
+
};
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Throttles a function
|
|
128
|
+
*/
|
|
129
|
+
export function throttle<T extends (...args: unknown[]) => unknown>(
|
|
130
|
+
fn: T,
|
|
131
|
+
limitMs: number,
|
|
132
|
+
): (...args: Parameters<T>) => void {
|
|
133
|
+
let inThrottle = false;
|
|
134
|
+
let lastArgs: Parameters<T> | null = null;
|
|
135
|
+
|
|
136
|
+
return (...args: Parameters<T>) => {
|
|
137
|
+
if (!inThrottle) {
|
|
138
|
+
fn(...args);
|
|
139
|
+
inThrottle = true;
|
|
140
|
+
|
|
141
|
+
setTimeout(() => {
|
|
142
|
+
inThrottle = false;
|
|
143
|
+
if (lastArgs !== null) {
|
|
144
|
+
fn(...lastArgs);
|
|
145
|
+
lastArgs = null;
|
|
146
|
+
}
|
|
147
|
+
}, limitMs);
|
|
148
|
+
} else {
|
|
149
|
+
lastArgs = args;
|
|
150
|
+
}
|
|
151
|
+
};
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
/**
|
|
155
|
+
* Deep clones an object
|
|
156
|
+
*/
|
|
157
|
+
export function deepClone<T>(obj: T): T {
|
|
158
|
+
if (obj === null || typeof obj !== 'object') {
|
|
159
|
+
return obj;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
if (obj instanceof Date) {
|
|
163
|
+
return new Date(obj.getTime()) as T;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
if (obj instanceof Array) {
|
|
167
|
+
return obj.map((item) => deepClone(item)) as T;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
if (obj instanceof Map) {
|
|
171
|
+
const map = new Map();
|
|
172
|
+
obj.forEach((value, key) => {
|
|
173
|
+
map.set(key, deepClone(value));
|
|
174
|
+
});
|
|
175
|
+
return map as T;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
if (obj instanceof Set) {
|
|
179
|
+
const set = new Set();
|
|
180
|
+
obj.forEach((value) => {
|
|
181
|
+
set.add(deepClone(value));
|
|
182
|
+
});
|
|
183
|
+
return set as T;
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
const cloned = {} as T;
|
|
187
|
+
for (const key in obj) {
|
|
188
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
189
|
+
cloned[key] = deepClone(obj[key]);
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
return cloned;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
/**
|
|
197
|
+
* Merges multiple objects deeply
|
|
198
|
+
*/
|
|
199
|
+
export function deepMerge<T extends Record<string, unknown>>(
|
|
200
|
+
target: T,
|
|
201
|
+
...sources: Partial<T>[]
|
|
202
|
+
): T {
|
|
203
|
+
// Create a deep clone of the target to avoid mutation
|
|
204
|
+
const result = deepClone(target);
|
|
205
|
+
|
|
206
|
+
if (!sources.length) return result;
|
|
207
|
+
|
|
208
|
+
const source = sources.shift();
|
|
209
|
+
if (!source) return result;
|
|
210
|
+
|
|
211
|
+
for (const key in source) {
|
|
212
|
+
if (Object.prototype.hasOwnProperty.call(source, key)) {
|
|
213
|
+
const sourceValue = source[key];
|
|
214
|
+
const resultValue = result[key];
|
|
215
|
+
|
|
216
|
+
if (isObject(resultValue) && isObject(sourceValue)) {
|
|
217
|
+
result[key] = deepMerge(
|
|
218
|
+
resultValue as Record<string, unknown>,
|
|
219
|
+
sourceValue as Record<string, unknown>,
|
|
220
|
+
) as T[Extract<keyof T, string>];
|
|
221
|
+
} else {
|
|
222
|
+
result[key] = sourceValue as T[Extract<keyof T, string>];
|
|
223
|
+
}
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return deepMerge(result, ...sources);
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
/**
|
|
231
|
+
* Checks if a value is a plain object
|
|
232
|
+
*/
|
|
233
|
+
function isObject(value: unknown): value is Record<string, unknown> {
|
|
234
|
+
return value !== null && typeof value === 'object' && !Array.isArray(value);
|
|
235
|
+
}
|
|
236
|
+
|
|
237
|
+
/**
|
|
238
|
+
* Creates a typed event emitter
|
|
239
|
+
*/
|
|
240
|
+
export class TypedEventEmitter<T extends Record<string, unknown>> {
|
|
241
|
+
private listeners = new Map<keyof T, Set<(data: unknown) => void>>();
|
|
242
|
+
|
|
243
|
+
on<K extends keyof T>(event: K, handler: (data: T[K]) => void): void {
|
|
244
|
+
if (!this.listeners.has(event)) {
|
|
245
|
+
this.listeners.set(event, new Set());
|
|
246
|
+
}
|
|
247
|
+
this.listeners.get(event)!.add(handler as (data: unknown) => void);
|
|
248
|
+
}
|
|
249
|
+
|
|
250
|
+
off<K extends keyof T>(event: K, handler: (data: T[K]) => void): void {
|
|
251
|
+
const handlers = this.listeners.get(event);
|
|
252
|
+
if (handlers) {
|
|
253
|
+
handlers.delete(handler as (data: unknown) => void);
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
|
|
257
|
+
emit<K extends keyof T>(event: K, data: T[K]): void {
|
|
258
|
+
const handlers = this.listeners.get(event);
|
|
259
|
+
if (handlers) {
|
|
260
|
+
handlers.forEach((handler) => handler(data));
|
|
261
|
+
}
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
once<K extends keyof T>(event: K, handler: (data: T[K]) => void): void {
|
|
265
|
+
const onceHandler = (data: T[K]) => {
|
|
266
|
+
handler(data);
|
|
267
|
+
this.off(event, onceHandler);
|
|
268
|
+
};
|
|
269
|
+
this.on(event, onceHandler);
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
removeAllListeners(event?: keyof T): void {
|
|
273
|
+
if (event) {
|
|
274
|
+
this.listeners.delete(event);
|
|
275
|
+
} else {
|
|
276
|
+
this.listeners.clear();
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
|
|
281
|
+
/**
|
|
282
|
+
* Formats bytes to human-readable string
|
|
283
|
+
*/
|
|
284
|
+
export function formatBytes(bytes: number, decimals = 2): string {
|
|
285
|
+
if (bytes === 0) return '0 Bytes';
|
|
286
|
+
|
|
287
|
+
const k = 1024;
|
|
288
|
+
const dm = decimals < 0 ? 0 : decimals;
|
|
289
|
+
const sizes = ['Bytes', 'KB', 'MB', 'GB', 'TB'];
|
|
290
|
+
|
|
291
|
+
// Handle negative numbers
|
|
292
|
+
const absBytes = Math.abs(bytes);
|
|
293
|
+
const i = Math.floor(Math.log(absBytes) / Math.log(k));
|
|
294
|
+
|
|
295
|
+
const value = parseFloat((absBytes / Math.pow(k, i)).toFixed(dm));
|
|
296
|
+
const sign = bytes < 0 ? '-' : '';
|
|
297
|
+
|
|
298
|
+
return sign + value + ' ' + sizes[i];
|
|
299
|
+
}
|
|
300
|
+
|
|
301
|
+
/**
|
|
302
|
+
* Parses duration string to milliseconds
|
|
303
|
+
*/
|
|
304
|
+
export function parseDuration(duration: string): number {
|
|
305
|
+
const match = duration.match(/^(\d+)(ms|s|m|h|d)$/);
|
|
306
|
+
if (!match) {
|
|
307
|
+
throw new Error(`Invalid duration format: ${duration}`);
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
const value = parseInt(match[1], 10);
|
|
311
|
+
const unit = match[2];
|
|
312
|
+
|
|
313
|
+
switch (unit) {
|
|
314
|
+
case 'ms':
|
|
315
|
+
return value;
|
|
316
|
+
case 's':
|
|
317
|
+
return value * 1000;
|
|
318
|
+
case 'm':
|
|
319
|
+
return value * 60 * 1000;
|
|
320
|
+
case 'h':
|
|
321
|
+
return value * 60 * 60 * 1000;
|
|
322
|
+
case 'd':
|
|
323
|
+
return value * 24 * 60 * 60 * 1000;
|
|
324
|
+
default:
|
|
325
|
+
throw new Error(`Unknown duration unit: ${unit}`);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
|
|
329
|
+
/**
|
|
330
|
+
* Ensures a value is an array
|
|
331
|
+
*/
|
|
332
|
+
export function ensureArray<T>(value: T | T[]): T[] {
|
|
333
|
+
return Array.isArray(value) ? value : [value];
|
|
334
|
+
}
|
|
335
|
+
|
|
336
|
+
/**
|
|
337
|
+
* Groups an array by a key function
|
|
338
|
+
*/
|
|
339
|
+
export function groupBy<T, K extends string | number | symbol>(
|
|
340
|
+
items: T[],
|
|
341
|
+
keyFn: (item: T) => K,
|
|
342
|
+
): Record<K, T[]> {
|
|
343
|
+
return items.reduce((groups, item) => {
|
|
344
|
+
const key = keyFn(item);
|
|
345
|
+
if (!groups[key]) {
|
|
346
|
+
groups[key] = [];
|
|
347
|
+
}
|
|
348
|
+
groups[key].push(item);
|
|
349
|
+
return groups;
|
|
350
|
+
}, {} as Record<K, T[]>);
|
|
351
|
+
}
|
|
352
|
+
|
|
353
|
+
/**
|
|
354
|
+
* Creates a promise that can be resolved/rejected externally
|
|
355
|
+
*/
|
|
356
|
+
export function createDeferred<T>(): {
|
|
357
|
+
promise: Promise<T>;
|
|
358
|
+
resolve: (value: T) => void;
|
|
359
|
+
reject: (reason?: unknown) => void;
|
|
360
|
+
} {
|
|
361
|
+
let resolve: (value: T) => void;
|
|
362
|
+
let reject: (reason?: unknown) => void;
|
|
363
|
+
|
|
364
|
+
const promise = new Promise<T>((res, rej) => {
|
|
365
|
+
resolve = res;
|
|
366
|
+
reject = rej;
|
|
367
|
+
});
|
|
368
|
+
|
|
369
|
+
return { promise, resolve: resolve!, reject: reject! };
|
|
370
|
+
}
|
|
371
|
+
|
|
372
|
+
/**
|
|
373
|
+
* Safely parses JSON with error handling
|
|
374
|
+
*/
|
|
375
|
+
export function safeParseJSON<T>(json: string, fallback?: T): T | undefined {
|
|
376
|
+
try {
|
|
377
|
+
return JSON.parse(json) as T;
|
|
378
|
+
} catch {
|
|
379
|
+
return fallback;
|
|
380
|
+
}
|
|
381
|
+
}
|
|
382
|
+
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* Circuit breaker state
|
|
386
|
+
*/
|
|
387
|
+
export interface CircuitBreakerState {
|
|
388
|
+
failureCount: number;
|
|
389
|
+
lastFailureTime: number;
|
|
390
|
+
state: 'closed' | 'open' | 'half-open';
|
|
391
|
+
}
|
|
392
|
+
|
|
393
|
+
/**
|
|
394
|
+
* Circuit breaker options
|
|
395
|
+
*/
|
|
396
|
+
export interface CircuitBreakerOptions {
|
|
397
|
+
threshold: number;
|
|
398
|
+
timeout: number;
|
|
399
|
+
resetTimeout: number;
|
|
400
|
+
}
|
|
401
|
+
|
|
402
|
+
/**
|
|
403
|
+
* Circuit breaker interface
|
|
404
|
+
*/
|
|
405
|
+
export interface CircuitBreaker {
|
|
406
|
+
execute<T>(fn: () => Promise<T>): Promise<T>;
|
|
407
|
+
getState(): CircuitBreakerState;
|
|
408
|
+
reset(): void;
|
|
409
|
+
}
|
|
410
|
+
|
|
411
|
+
/**
|
|
412
|
+
* Creates a circuit breaker
|
|
413
|
+
*/
|
|
414
|
+
export function circuitBreaker(
|
|
415
|
+
name: string,
|
|
416
|
+
options: CircuitBreakerOptions,
|
|
417
|
+
): CircuitBreaker {
|
|
418
|
+
const state: CircuitBreakerState = {
|
|
419
|
+
failureCount: 0,
|
|
420
|
+
lastFailureTime: 0,
|
|
421
|
+
state: 'closed',
|
|
422
|
+
};
|
|
423
|
+
|
|
424
|
+
const isOpen = (): boolean => {
|
|
425
|
+
if (state.state === 'open') {
|
|
426
|
+
const now = Date.now();
|
|
427
|
+
if (now - state.lastFailureTime >= options.resetTimeout) {
|
|
428
|
+
state.state = 'half-open';
|
|
429
|
+
return false;
|
|
430
|
+
}
|
|
431
|
+
return true;
|
|
432
|
+
}
|
|
433
|
+
return false;
|
|
434
|
+
};
|
|
435
|
+
|
|
436
|
+
const recordSuccess = (): void => {
|
|
437
|
+
state.failureCount = 0;
|
|
438
|
+
state.state = 'closed';
|
|
439
|
+
};
|
|
440
|
+
|
|
441
|
+
const recordFailure = (): void => {
|
|
442
|
+
state.failureCount++;
|
|
443
|
+
state.lastFailureTime = Date.now();
|
|
444
|
+
|
|
445
|
+
if (state.failureCount >= options.threshold) {
|
|
446
|
+
state.state = 'open';
|
|
447
|
+
}
|
|
448
|
+
};
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
async execute<T>(fn: () => Promise<T>): Promise<T> {
|
|
452
|
+
if (isOpen()) {
|
|
453
|
+
throw new Error(`Circuit breaker ${name} is open`);
|
|
454
|
+
}
|
|
455
|
+
|
|
456
|
+
try {
|
|
457
|
+
const result = await timeout(fn(), options.timeout);
|
|
458
|
+
recordSuccess();
|
|
459
|
+
return result;
|
|
460
|
+
} catch (error) {
|
|
461
|
+
recordFailure();
|
|
462
|
+
throw error;
|
|
463
|
+
}
|
|
464
|
+
},
|
|
465
|
+
|
|
466
|
+
getState(): CircuitBreakerState {
|
|
467
|
+
return { ...state };
|
|
468
|
+
},
|
|
469
|
+
|
|
470
|
+
reset(): void {
|
|
471
|
+
state.failureCount = 0;
|
|
472
|
+
state.lastFailureTime = 0;
|
|
473
|
+
state.state = 'closed';
|
|
474
|
+
},
|
|
475
|
+
};
|
|
476
|
+
}
|