@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.
125 lines • 4.79 kB
JavaScript
;
/**
* Retry Handler with Exponential Backoff
* Implements retry logic for transient failures
*/
Object.defineProperty(exports, "__esModule", { value: true });
exports.RetryHandler = void 0;
exports.Retryable = Retryable;
class RetryHandler {
/**
* Execute an operation with retry logic
* @param operation The async operation to execute
* @param config Retry configuration
* @returns The result of the operation
* @throws The last error if all retries fail
*/
static async execute(operation, config = {}) {
const fullConfig = { ...RetryHandler.DEFAULT_CONFIG, ...config };
let lastError;
let attempt = 0;
let totalDelay = 0;
while (attempt < fullConfig.maxAttempts) {
try {
const result = await operation();
return result;
}
catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
attempt++;
// Check if error is retryable
if (fullConfig.isRetryable && !fullConfig.isRetryable(lastError)) {
throw lastError;
}
// If this was the last attempt, throw the error
if (attempt >= fullConfig.maxAttempts) {
throw lastError;
}
// Calculate delay with exponential backoff and jitter
const delay = RetryHandler.calculateDelay(attempt, fullConfig.initialDelay, fullConfig.maxDelay, fullConfig.backoffMultiplier, fullConfig.jitter);
totalDelay += delay;
console.log(`Retry attempt ${attempt}/${fullConfig.maxAttempts} after ${delay}ms delay. Error: ${lastError.message}`);
// Wait before retrying
await RetryHandler.sleep(delay);
}
}
// This should never be reached, but TypeScript needs it
throw lastError || new Error('All retry attempts failed');
}
/**
* Calculate delay with exponential backoff and jitter
*/
static calculateDelay(attempt, initialDelay, maxDelay, multiplier, jitter) {
// Calculate exponential backoff
const exponentialDelay = initialDelay * Math.pow(multiplier, attempt - 1);
// Cap at max delay
const cappedDelay = Math.min(exponentialDelay, maxDelay);
// Add jitter to prevent thundering herd
const jitterAmount = cappedDelay * jitter * (Math.random() * 2 - 1);
const finalDelay = Math.max(0, cappedDelay + jitterAmount);
return Math.floor(finalDelay);
}
/**
* Sleep for specified milliseconds
*/
static sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Execute with retry and return stats
*/
static async executeWithStats(operation, config = {}) {
const fullConfig = { ...RetryHandler.DEFAULT_CONFIG, ...config };
let lastError;
let attempt = 0;
let totalDelay = 0;
while (attempt < fullConfig.maxAttempts) {
try {
const result = await operation();
return {
result,
stats: {
attempts: attempt + 1,
totalDelay,
success: true,
},
};
}
catch (error) {
lastError = error instanceof Error ? error : new Error(String(error));
attempt++;
if (fullConfig.isRetryable && !fullConfig.isRetryable(lastError)) {
throw lastError;
}
if (attempt >= fullConfig.maxAttempts) {
throw lastError;
}
const delay = RetryHandler.calculateDelay(attempt, fullConfig.initialDelay, fullConfig.maxDelay, fullConfig.backoffMultiplier, fullConfig.jitter);
totalDelay += delay;
await RetryHandler.sleep(delay);
}
}
throw lastError || new Error('All retry attempts failed');
}
}
exports.RetryHandler = RetryHandler;
RetryHandler.DEFAULT_CONFIG = {
maxAttempts: 3,
initialDelay: 100,
maxDelay: 10000,
backoffMultiplier: 2,
jitter: 0.1,
};
/**
* Decorator for adding retry logic to methods
*/
function Retryable(config = {}) {
return function (target, propertyKey, descriptor) {
const originalMethod = descriptor.value;
descriptor.value = async function (...args) {
return RetryHandler.execute(() => originalMethod.apply(this, args), config);
};
return descriptor;
};
}
//# sourceMappingURL=retry-handler.js.map