UNPKG

@coinbase/onchaintestkit

Version:

End-to-end testing toolkit for blockchain applications, powered by Playwright

92 lines (91 loc) 2.99 kB
"use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.exponentialBackoff = exponentialBackoff; exports.linearBackoff = linearBackoff; exports.customStrategy = customStrategy; exports.createDelayFunction = createDelayFunction; exports.executeWithRetry = executeWithRetry; const DEFAULT_CONFIG = { baseDelay: 100, maxDelay: 5000, multiplier: 1.5, jitter: true, maxAttempts: 10, }; /** * Exponential backoff with optional jitter to prevent thundering herd */ function exponentialBackoff(attempt, config = {}) { const mergedConfig = { ...DEFAULT_CONFIG, ...config }; const { baseDelay, maxDelay, multiplier, jitter } = mergedConfig; let delay = baseDelay * multiplier ** attempt; delay = Math.min(delay, maxDelay); if (jitter) { // Add random jitter of ±25% const jitterRange = delay * 0.25; delay += (Math.random() - 0.5) * 2 * jitterRange; } return Math.max(0, Math.floor(delay)); } /** * Linear backoff with consistent increments */ function linearBackoff(attempt, increment) { return attempt * increment; } /** * Custom backoff strategy with user-defined function */ function customStrategy(attempt, strategy, config = {}) { const mergedConfig = { ...DEFAULT_CONFIG, ...config }; return strategy(attempt, mergedConfig); } /** * Create a delay function based on retry options */ function createDelayFunction(options) { return (attempt) => { let delay; switch (options.strategy) { case "exponential": delay = exponentialBackoff(attempt, options.config); break; case "linear": delay = linearBackoff(attempt, options.config.baseDelay); break; case "custom": delay = customStrategy(attempt, () => options.config.baseDelay, options.config); break; default: delay = exponentialBackoff(attempt, options.config); } if (options.onRetry) { options.onRetry(attempt, delay); } return delay; }; } /** * Execute an operation with retry logic */ async function executeWithRetry(operation, options) { const delayFn = createDelayFunction(options); let lastError = null; for (let attempt = 0; attempt < options.config.maxAttempts; attempt++) { try { return await operation(); } catch (error) { lastError = error instanceof Error ? error : new Error(String(error)); // Don't delay after the last attempt if (attempt < options.config.maxAttempts - 1) { const delay = delayFn(attempt); await sleep(delay); } } } throw new Error(`Operation failed after ${options.config.maxAttempts} attempts. Last error: ${lastError?.message}`); } function sleep(ms) { return new Promise(resolve => setTimeout(resolve, ms)); }