@coinbase/onchaintestkit
Version:
End-to-end testing toolkit for blockchain applications, powered by Playwright
92 lines (91 loc) • 2.99 kB
JavaScript
;
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));
}