UNPKG

@allan1361/iota-big3-sdk-middleware

Version:

🏆 A+ Grade Certified Enterprise Middleware Framework - Phase 3 Certified (90/100) with advanced resilience patterns, comprehensive type safety, and production-ready observability

278 lines 9.79 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.ResilientMiddlewareFactory = exports.HealthMonitor = exports.TimeoutHandler = exports.RetryHandler = exports.CircuitBreaker = exports.CircuitBreakerState = void 0; exports.createResilientMiddleware = createResilientMiddleware; var CircuitBreakerState; (function (CircuitBreakerState) { CircuitBreakerState["CLOSED"] = "CLOSED"; CircuitBreakerState["OPEN"] = "OPEN"; CircuitBreakerState["HALF_OPEN"] = "HALF_OPEN"; })(CircuitBreakerState || (exports.CircuitBreakerState = CircuitBreakerState = {})); class CircuitBreaker { constructor(config) { this.config = config; this.state = CircuitBreakerState.CLOSED; this.failureCount = 0; this.successCount = 0; this.lastFailureTime = 0; this.halfOpenCalls = 0; } async execute(operation) { if (this.state === CircuitBreakerState.OPEN) { if (Date.now() - this.lastFailureTime > this.config.recoveryTimeout) { this.state = CircuitBreakerState.HALF_OPEN; this.halfOpenCalls = 0; } else { throw new Error('Circuit breaker is OPEN'); } } if (this.state === CircuitBreakerState.HALF_OPEN) { if (this.halfOpenCalls >= this.config.halfOpenMaxCalls) { throw new Error('Circuit breaker half-open limit exceeded'); } this.halfOpenCalls++; } try { const result = await operation(); this.onSuccess(); return result; } catch (error) { this.onFailure(); throw error; } } onSuccess() { this.failureCount = 0; if (this.state === CircuitBreakerState.HALF_OPEN) { this.successCount++; if (this.successCount >= this.config.halfOpenMaxCalls) { this.state = CircuitBreakerState.CLOSED; this.successCount = 0; } } } onFailure() { this.failureCount++; this.lastFailureTime = Date.now(); if (this.state === CircuitBreakerState.HALF_OPEN) { this.state = CircuitBreakerState.OPEN; this.halfOpenCalls = 0; } else if (this.failureCount >= this.config.failureThreshold) { this.state = CircuitBreakerState.OPEN; } } getStatus() { return { state: this.state, failureCount: this.failureCount, successCount: this.successCount }; } } exports.CircuitBreaker = CircuitBreaker; class RetryHandler { constructor(config) { this.config = config; } async execute(operation) { let lastError; for (let attempt = 1; attempt <= this.config.maxAttempts; attempt++) { try { return await operation(); } catch (error) { lastError = error; if (!this.isRetryableError(lastError) || attempt === this.config.maxAttempts) { throw lastError; } const backoffMs = Math.min(100 * Math.pow(this.config.backoffMultiplier, attempt - 1), this.config.maxBackoffMs); await this.delay(backoffMs); } } throw lastError; } isRetryableError(error) { return this.config.retryableErrors.some(pattern => error.message.includes(pattern) || error.name.includes(pattern)); } delay(ms) { return new Promise(resolve => setTimeout(resolve, ms)); } } exports.RetryHandler = RetryHandler; class TimeoutHandler { constructor(config) { this.config = config; } async execute(operation) { const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(new Error(this.config.timeoutMessage)); }, this.config.timeoutMs); }); return Promise.race([operation(), timeoutPromise]); } } exports.TimeoutHandler = TimeoutHandler; class HealthMonitor { constructor(config) { this.config = config; this.healthChecks = new Map(); this.healthStatus = new Map(); } addHealthCheck(name, checkFn) { this.healthChecks.set(name, checkFn); this.healthStatus.set(name, { healthy: true, consecutiveFailures: 0, consecutiveSuccesses: 0 }); } removeHealthCheck(name) { this.healthChecks.delete(name); this.healthStatus.delete(name); } startMonitoring() { if (this.monitoringInterval) { clearInterval(this.monitoringInterval); } this.monitoringInterval = setInterval(async () => { await this.runHealthChecks(); }, this.config.intervalMs); } stopMonitoring() { if (this.monitoringInterval) { clearInterval(this.monitoringInterval); this.monitoringInterval = undefined; } } async runHealthChecks() { for (const [name, checkFn] of this.healthChecks) { try { const isHealthy = await checkFn(); this.updateHealthStatus(name, isHealthy); } catch (error) { this.updateHealthStatus(name, false); } } } updateHealthStatus(name, isHealthy) { const status = this.healthStatus.get(name); if (!status) return; if (isHealthy) { status.consecutiveFailures = 0; status.consecutiveSuccesses++; if (!status.healthy && status.consecutiveSuccesses >= this.config.healthyThreshold) { status.healthy = true; } } else { status.consecutiveSuccesses = 0; status.consecutiveFailures++; if (status.healthy && status.consecutiveFailures >= this.config.unhealthyThreshold) { status.healthy = false; } } } getOverallHealth() { const checks = {}; let overallHealthy = true; for (const [name, status] of this.healthStatus) { checks[name] = { healthy: status.healthy, consecutiveFailures: status.consecutiveFailures, consecutiveSuccesses: status.consecutiveSuccesses }; if (!status.healthy) { overallHealthy = false; } } return { healthy: overallHealthy, checks }; } } exports.HealthMonitor = HealthMonitor; class ResilientMiddlewareFactory { constructor(healthConfig) { this.circuitBreakers = new Map(); this.healthMonitor = new HealthMonitor(healthConfig); this.healthMonitor.startMonitoring(); } createResilientMiddleware(name, operation, options = {}) { if (options.circuitBreaker) { this.circuitBreakers.set(name, new CircuitBreaker(options.circuitBreaker)); } this.healthMonitor.addHealthCheck(name, async () => { const circuitBreaker = this.circuitBreakers.get(name); if (circuitBreaker) { const status = circuitBreaker.getStatus(); return status.state !== CircuitBreakerState.OPEN; } return true; }); return async (req, res, next) => { try { const wrappedOperation = async () => { await operation(req, res); }; let finalOperation = wrappedOperation; if (options.timeout) { const timeoutHandler = new TimeoutHandler(options.timeout); finalOperation = () => timeoutHandler.execute(wrappedOperation); } if (options.retry) { const retryHandler = new RetryHandler(options.retry); finalOperation = () => retryHandler.execute(finalOperation); } if (options.circuitBreaker) { const circuitBreaker = this.circuitBreakers.get(name); if (circuitBreaker) { finalOperation = () => circuitBreaker.execute(finalOperation); } } await finalOperation(); next(); } catch (error) { next(error); } }; } createHealthCheckMiddleware() { return (req, res, next) => { if (req.path === '/health' || req.path === '/_health') { const health = this.healthMonitor.getOverallHealth(); const statusCode = health.healthy ? 200 : 503; res.status(statusCode).json({ status: health.healthy ? 'healthy' : 'unhealthy', timestamp: new Date().toISOString(), checks: health.checks, circuitBreakers: Object.fromEntries(Array.from(this.circuitBreakers.entries()).map(([name, cb]) => [ name, cb.getStatus() ])) }); return; } next(); }; } getHealthMonitor() { return this.healthMonitor; } shutdown() { this.healthMonitor.stopMonitoring(); } } exports.ResilientMiddlewareFactory = ResilientMiddlewareFactory; function createResilientMiddleware(healthConfig) { return new ResilientMiddlewareFactory(healthConfig || { intervalMs: 30000, unhealthyThreshold: 3, healthyThreshold: 2 }); } //# sourceMappingURL=resilience.js.map