UNPKG

@eklabdev/blingts4

Version:

A comprehensive TypeScript decorators library implementing Stage 2 decorators for TS 4.9+

163 lines (162 loc) 6.21 kB
/** * Retries a method call on failure * @param options Retry configuration options */ export function Retry(options) { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const { maxRetries, strategy, backoff = 1000, onRetry } = options; descriptor.value = function (...args) { let attempts = 0; const execute = async () => { try { attempts++; // Increment attempts at the beginning const result = originalMethod.apply(this, args); // Handle both synchronous results and promises if (result instanceof Promise) { return await result; // This will throw if the promise is rejected } return result; } catch (error) { // If we've exceeded max retries, rethrow the error if (attempts >= maxRetries + 1) { // +1 because we're counting the initial attempt throw error; } if (onRetry) { onRetry(error, attempts); } const delay = strategy === 'exponential' ? backoff * Math.pow(2, attempts - 1) : backoff; // Wait before retrying await new Promise(resolve => setTimeout(resolve, delay)); // Retry the operation return execute(); } }; return execute(); }; return descriptor; }; } /** * Limits the execution time of a method * @param ms Timeout in milliseconds */ export function Timeout(ms) { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; const methodName = propertyKey; descriptor.value = function (...args) { const methodPromise = Promise.resolve(originalMethod.apply(this, args)); const timeoutPromise = new Promise((_, reject) => { setTimeout(() => { reject(new Error(`${methodName} timed out after ${ms}ms`)); }, ms); }); return Promise.race([methodPromise, timeoutPromise]); }; return descriptor; }; } /** * Implements the circuit breaker pattern * @param options Circuit breaker configuration options */ export function CircuitBreaker(options) { // Create a shared state object for this specific circuit breaker const circuitState = { failures: 0, state: 'closed', lastFailureTime: 0, }; return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; // Functions to manage circuit state function updateState(newState) { if (circuitState.state !== newState) { circuitState.state = newState; options.onStateChange?.(newState); } } function handleFailure(error) { circuitState.failures++; circuitState.lastFailureTime = Date.now(); if (circuitState.failures >= options.failureThreshold) { updateState('open'); } } descriptor.value = function (...args) { // Always return a Promise for consistent handling return new Promise((resolve, reject) => { // Check if circuit is open if (circuitState.state === 'open') { // Check if we should transition to half-open if (Date.now() - circuitState.lastFailureTime >= options.resetTimeout) { updateState('half-open'); } else { // Still open - reject with circuit breaker error reject(new Error('Circuit breaker is open')); return; } } try { const result = originalMethod.apply(this, args); if (result instanceof Promise) { result .then(value => { // Success in half-open state - transition to closed if (circuitState.state === 'half-open') { updateState('closed'); circuitState.failures = 0; } resolve(value); }) .catch(error => { handleFailure(error); reject(error); }); } else { // Synchronous success if (circuitState.state === 'half-open') { updateState('closed'); circuitState.failures = 0; } resolve(result); } } catch (error) { handleFailure(error); reject(error); } }); }; return descriptor; }; } /** * Provides a fallback implementation when the method fails * @param fallbackFn Fallback function to execute */ export function Fallback(fallbackFn) { return function (target, propertyKey, descriptor) { const originalMethod = descriptor.value; descriptor.value = function (...args) { try { const result = originalMethod.apply(this, args); if (result instanceof Promise) { return result.catch(error => { return fallbackFn.apply(this, args); }); } return result; } catch (error) { return fallbackFn.apply(this, args); } }; return descriptor; }; }