@magiceden/magiceden-sdk
Version:
A TypeScript SDK for interacting with Magic Eden's API across multiple chains.
88 lines (87 loc) • 3.17 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.isRetryableError = isRetryableError;
exports.retryOperation = retryOperation;
const ts_retry_promise_1 = require("ts-retry-promise");
const errors_1 = require("../../errors");
const errors_2 = require("../../errors");
const errors_3 = require("../../errors");
/**
* Default retry configuration
*/
const DEFAULT_RETRY_CONFIG = {
retries: 3,
delay: 1000,
timeout: 60000, // 1 minute total timeout
maxBackOff: 30000, // 30 seconds max delay between retries
backoff: (attempt, delay) => {
// Exponential backoff with jitter to prevent thundering herd
const calculatedDelay = delay * Math.pow(2, attempt);
const jitter = calculatedDelay * 0.2 * Math.random(); // 20% jitter
return Math.min(calculatedDelay + jitter, 30000); // Cap at 30 seconds
},
retryIf: (error) => isRetryableError(error),
};
/**
* Determines if an error is retryable based on its type and properties
*/
function isRetryableError(error) {
// Handle null/undefined errors
if (!error) {
return false;
}
// Always retry network errors (connection issues, timeouts)
if (error instanceof errors_3.NetworkError) {
return true;
}
// Always retry rate limit errors (with backoff)
if (error instanceof errors_2.RateLimitError) {
return true;
}
// Handle API errors based on status code
if (error instanceof errors_1.ApiError) {
const status = error.status;
// Don't retry 4xx client errors (except those handled above like rate limits)
if (status >= 400 && status < 500) {
// Some 4xx errors might be worth retrying
return (status === 400 || // Bad Request
status === 408 || // Request Timeout
status === 429); // Too Many Requests (should be caught by RateLimitError but just in case)
}
// Retry 5xx server errors
if (status >= 500 && status < 600) {
return true;
}
// By default, don't retry unknown errors
return false;
}
return false;
}
/**
* Retries an operation with exponential backoff
*
* @param operation The async function to retry
* @param options Optional configuration to override defaults
* @returns The result of the operation
* @throws The last error encountered if all retries fail
*/
async function retryOperation(operation, options) {
const mergedOptions = {
...DEFAULT_RETRY_CONFIG,
...options,
};
try {
return await (0, ts_retry_promise_1.retry)(operation, mergedOptions);
}
catch (error) {
// Add retry information to the error message
if (error instanceof Error) {
const wasRetried = isRetryableError(error);
const retries = wasRetried ? mergedOptions.retries || DEFAULT_RETRY_CONFIG.retries || 3 : 0;
if (!error.message.includes('Failed after')) {
error.message = `Failed after ${retries} ${retries === 1 ? 'retry' : 'retries'}: ${error.message}`;
}
}
throw error;
}
}