UNPKG

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
"use strict"; 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