@ai-capabilities-suite/mcp-debugger-core
Version:
Core debugging engine for Node.js and TypeScript applications. Provides Inspector Protocol integration, breakpoint management, variable inspection, execution control, profiling, hang detection, and source map support.
230 lines • 6.93 kB
JavaScript
"use strict";
/**
* Circuit Breaker Pattern Implementation
* Prevents cascading failures by failing fast when a service is unhealthy
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.CircuitBreakerManager = exports.CircuitBreaker = exports.CircuitState = void 0;
var CircuitState;
(function (CircuitState) {
CircuitState["CLOSED"] = "CLOSED";
CircuitState["OPEN"] = "OPEN";
CircuitState["HALF_OPEN"] = "HALF_OPEN";
})(CircuitState || (exports.CircuitState = CircuitState = {}));
class CircuitBreaker {
constructor(name, config) {
this.name = name;
this.config = config;
this.state = CircuitState.CLOSED;
this.failures = 0;
this.successes = 0;
this.consecutiveFailures = 0;
this.consecutiveSuccesses = 0;
}
/**
* Execute an operation through the circuit breaker
* @param operation The async operation to execute
* @returns The result of the operation
* @throws Error if circuit is open or operation fails
*/
async execute(operation) {
// Check if circuit is open
if (this.state === CircuitState.OPEN) {
throw new Error(`Circuit breaker '${this.name}' is OPEN. Failing fast.`);
}
try {
// Execute with timeout if configured
const result = this.config.timeout
? await this.executeWithTimeout(operation, this.config.timeout)
: await operation();
this.onSuccess();
return result;
}
catch (error) {
this.onFailure();
throw error;
}
}
/**
* Execute operation with timeout
*/
async executeWithTimeout(operation, timeout) {
return Promise.race([
operation(),
new Promise((_, reject) => setTimeout(() => reject(new Error(`Operation timed out after ${timeout}ms`)), timeout)),
]);
}
/**
* Handle successful operation
*/
onSuccess() {
this.successes++;
this.consecutiveSuccesses++;
this.consecutiveFailures = 0;
this.lastSuccessTime = Date.now();
// If in half-open state and reached success threshold, close circuit
if (this.state === CircuitState.HALF_OPEN &&
this.consecutiveSuccesses >= this.config.successThreshold) {
this.closeCircuit();
}
}
/**
* Handle failed operation
*/
onFailure() {
this.failures++;
this.consecutiveFailures++;
this.consecutiveSuccesses = 0;
this.lastFailureTime = Date.now();
// If reached failure threshold, open circuit
if (this.consecutiveFailures >= this.config.failureThreshold) {
this.openCircuit();
}
}
/**
* Open the circuit (start failing fast)
*/
openCircuit() {
if (this.state === CircuitState.OPEN) {
return;
}
this.state = CircuitState.OPEN;
console.warn(`Circuit breaker '${this.name}' opened after ${this.consecutiveFailures} consecutive failures`);
// Schedule automatic transition to half-open
this.resetTimer = setTimeout(() => {
this.halfOpenCircuit();
}, this.config.resetTimeout);
}
/**
* Transition to half-open state (test if service recovered)
*/
halfOpenCircuit() {
this.state = CircuitState.HALF_OPEN;
this.consecutiveSuccesses = 0;
console.log(`Circuit breaker '${this.name}' transitioned to HALF_OPEN, testing recovery`);
}
/**
* Close the circuit (resume normal operation)
*/
closeCircuit() {
this.state = CircuitState.CLOSED;
this.consecutiveFailures = 0;
console.log(`Circuit breaker '${this.name}' closed, resuming normal operation`);
if (this.resetTimer) {
clearTimeout(this.resetTimer);
this.resetTimer = undefined;
}
}
/**
* Manually reset the circuit breaker
*/
reset() {
this.state = CircuitState.CLOSED;
this.failures = 0;
this.successes = 0;
this.consecutiveFailures = 0;
this.consecutiveSuccesses = 0;
this.lastFailureTime = undefined;
this.lastSuccessTime = undefined;
if (this.resetTimer) {
clearTimeout(this.resetTimer);
this.resetTimer = undefined;
}
console.log(`Circuit breaker '${this.name}' manually reset`);
}
/**
* Get current circuit breaker statistics
*/
getStats() {
return {
state: this.state,
failures: this.failures,
successes: this.successes,
consecutiveFailures: this.consecutiveFailures,
consecutiveSuccesses: this.consecutiveSuccesses,
lastFailureTime: this.lastFailureTime,
lastSuccessTime: this.lastSuccessTime,
};
}
/**
* Get current circuit state
*/
getState() {
return this.state;
}
/**
* Check if circuit is open
*/
isOpen() {
return this.state === CircuitState.OPEN;
}
/**
* Check if circuit is closed
*/
isClosed() {
return this.state === CircuitState.CLOSED;
}
/**
* Check if circuit is half-open
*/
isHalfOpen() {
return this.state === CircuitState.HALF_OPEN;
}
}
exports.CircuitBreaker = CircuitBreaker;
/**
* Circuit Breaker Manager
* Manages multiple circuit breakers for different operations
*/
class CircuitBreakerManager {
constructor() {
this.breakers = new Map();
}
/**
* Create or get a circuit breaker
* @param name Circuit breaker name
* @param config Circuit breaker configuration
* @returns The circuit breaker instance
*/
getOrCreate(name, config) {
if (!this.breakers.has(name)) {
this.breakers.set(name, new CircuitBreaker(name, config));
}
return this.breakers.get(name);
}
/**
* Get a circuit breaker by name
* @param name Circuit breaker name
* @returns The circuit breaker or undefined
*/
get(name) {
return this.breakers.get(name);
}
/**
* Get all circuit breakers
* @returns Map of all circuit breakers
*/
getAll() {
return new Map(this.breakers);
}
/**
* Reset all circuit breakers
*/
resetAll() {
for (const breaker of this.breakers.values()) {
breaker.reset();
}
}
/**
* Get statistics for all circuit breakers
*/
getAllStats() {
const stats = new Map();
for (const [name, breaker] of this.breakers.entries()) {
stats.set(name, breaker.getStats());
}
return stats;
}
}
exports.CircuitBreakerManager = CircuitBreakerManager;
//# sourceMappingURL=circuit-breaker.js.map