opensea-js
Version:
TypeScript SDK for the OpenSea marketplace helps developers build new experiences using NFTs and our marketplace data
97 lines • 4.15 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", { value: true });
exports.executeSequentialWithRateLimit = exports.executeWithRateLimit = void 0;
const stringHelper_1 = require("./stringHelper");
/**
* Default configuration for rate limit handling
*/
const DEFAULT_MAX_RETRIES = 3;
const DEFAULT_BASE_RETRY_DELAY_MS = 1000;
const EXPONENTIAL_BACKOFF_BASE = 2;
const MILLISECONDS_PER_SECOND = 1000;
/**
* HTTP status codes that indicate rate limiting
*/
const RATE_LIMIT_STATUS_CODE = 429;
const CUSTOM_RATE_LIMIT_STATUS_CODE = 599;
/**
* Sleep for a specified duration
* @param ms Duration in milliseconds
*/
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
/**
* Execute an async operation with automatic retry on rate limit errors.
* Respects the retry-after header when present, otherwise uses exponential backoff.
*
* @param operation The async operation to execute
* @param options Configuration for rate limit handling
* @returns The result of the operation
* @throws The last error encountered if all retries are exhausted
*/
async function executeWithRateLimit(operation, options = {}) {
const { logger = () => { }, maxRetries = DEFAULT_MAX_RETRIES, baseRetryDelay = DEFAULT_BASE_RETRY_DELAY_MS, } = options;
let lastError;
for (let attempt = 0; attempt <= maxRetries; attempt++) {
try {
return await operation();
}
catch (error) {
lastError = error;
const rateLimitError = error;
// Check if this is a rate limit error by status code (robust) or retry-after header
const isRateLimitError = rateLimitError.statusCode === RATE_LIMIT_STATUS_CODE ||
rateLimitError.statusCode === CUSTOM_RATE_LIMIT_STATUS_CODE ||
rateLimitError.retryAfter !== undefined;
if (!isRateLimitError || attempt === maxRetries) {
// Not a rate limit error or out of retries, throw the error
throw error;
}
// Calculate delay
let delayMs;
if (rateLimitError.retryAfter !== undefined) {
delayMs = rateLimitError.retryAfter * MILLISECONDS_PER_SECOND;
logger(`Rate limit hit. Waiting ${rateLimitError.retryAfter} seconds before retry (attempt ${attempt + 1}/${maxRetries})...`);
}
else {
// Exponential backoff
delayMs = baseRetryDelay * Math.pow(EXPONENTIAL_BACKOFF_BASE, attempt);
logger(`Rate limit hit. Waiting ${delayMs}ms before retry (attempt ${attempt + 1}/${maxRetries})...`);
}
await sleep(delayMs);
logger(`Retrying operation...`);
}
}
// Should never reach here, but TypeScript needs this
throw lastError;
}
exports.executeWithRateLimit = executeWithRateLimit;
/**
* Execute an array of async operations sequentially with rate limit handling.
* Logs progress after each operation.
*
* @param operations Array of async operations to execute
* @param options Configuration for rate limit handling and progress logging
* @returns Array of results from each operation
*/
async function executeSequentialWithRateLimit(operations, options = {}) {
const { logger = () => { }, operationName = "operation", ...rateLimitOptions } = options;
const results = [];
const total = operations.length;
logger(`Starting ${total} ${(0, stringHelper_1.pluralize)(total, operationName)}...`);
for (let i = 0; i < operations.length; i++) {
const operation = operations[i];
logger(`Executing ${operationName} ${i + 1}/${total}...`);
const result = await executeWithRateLimit(operation, {
...rateLimitOptions,
logger,
});
results.push(result);
logger(`Completed ${operationName} ${i + 1}/${total}`);
}
logger(`All ${total} ${(0, stringHelper_1.pluralize)(total, operationName)} completed successfully`);
return results;
}
exports.executeSequentialWithRateLimit = executeSequentialWithRateLimit;
//# sourceMappingURL=rateLimit.js.map