UNPKG

@oxyhq/services

Version:

Reusable OxyHQ module to handle authentication, user management, karma system, device-based session management and more 🚀

210 lines (198 loc) • 5.11 kB
"use strict"; /** * Request utilities for HTTP clients * * Provides reusable components for request deduplication, queuing, and logging */ /** * Request deduplication - prevents duplicate concurrent requests * * When multiple requests with the same key are made simultaneously, * only one request is executed and all callers receive the same result. * * @example * ```typescript * const deduplicator = new RequestDeduplicator(); * * // Multiple calls with same key will share the same promise * const promise1 = deduplicator.deduplicate('user-123', () => fetchUser('123')); * const promise2 = deduplicator.deduplicate('user-123', () => fetchUser('123')); * // promise1 === promise2, only one API call is made * ``` */ export class RequestDeduplicator { pendingRequests = new Map(); /** * Deduplicate a request by key * @param key Unique key for the request * @param requestFn Function that returns a promise * @returns Promise that will be shared if key already exists */ async deduplicate(key, requestFn) { const existing = this.pendingRequests.get(key); if (existing) { return existing; } const promise = requestFn().finally(() => { this.pendingRequests.delete(key); }); this.pendingRequests.set(key, promise); return promise; } /** * Clear all pending requests */ clear() { this.pendingRequests.clear(); } /** * Get number of pending requests */ size() { return this.pendingRequests.size; } } /** * Request queue with concurrency control * * Limits the number of concurrent requests and queues excess requests. * Useful for rate limiting and preventing request flooding. * * @example * ```typescript * const queue = new RequestQueue(5, 100); // Max 5 concurrent, queue up to 100 * * // All requests will be queued and processed with max 5 concurrent * await queue.enqueue(() => fetchUser('1')); * await queue.enqueue(() => fetchUser('2')); * // ... * ``` */ export class RequestQueue { queue = []; running = 0; /** * Create a new request queue * @param maxConcurrent Maximum number of concurrent requests (default: 10) * @param maxQueueSize Maximum queue size (default: 100) */ constructor(maxConcurrent = 10, maxQueueSize = 100) { this.maxConcurrent = maxConcurrent; this.maxQueueSize = maxQueueSize; } /** * Enqueue a request * @param requestFn Function that returns a promise * @returns Promise that resolves when request completes */ async enqueue(requestFn) { if (this.queue.length >= this.maxQueueSize) { throw new Error('Request queue is full'); } return new Promise((resolve, reject) => { this.queue.push(async () => { try { const result = await requestFn(); resolve(result); } catch (error) { reject(error); } }); this.process(); }); } /** * Process queued requests */ async process() { if (this.running >= this.maxConcurrent || this.queue.length === 0) { return; } this.running++; const requestFn = this.queue.shift(); if (requestFn) { try { await requestFn(); } finally { this.running--; this.process(); } } else { this.running--; } } /** * Clear all queued requests */ clear() { this.queue = []; } /** * Get queue size */ size() { return this.queue.length; } /** * Get number of currently running requests */ runningCount() { return this.running; } } /** * Simple logger with level support * * Lightweight logger for HTTP clients and utilities. * For more advanced logging, use loggerUtils.ts * * @example * ```typescript * const logger = new SimpleLogger(true, 'debug'); * logger.debug('Debug message'); * logger.info('Info message'); * logger.error('Error message'); * ``` */ export class SimpleLogger { /** * Create a new simple logger * @param enabled Whether logging is enabled * @param level Minimum log level * @param prefix Prefix for log messages (default: '') */ constructor(enabled = false, level = 'error', prefix = '') { this.enabled = enabled; this.level = level; this.prefix = prefix; } shouldLog(level) { if (!this.enabled || this.level === 'none') return false; const levels = ['none', 'error', 'warn', 'info', 'debug']; return levels.indexOf(level) <= levels.indexOf(this.level); } formatMessage(...args) { return this.prefix ? [`[${this.prefix}]`, ...args] : args; } error(...args) { if (this.shouldLog('error')) { console.error(...this.formatMessage(...args)); } } warn(...args) { if (this.shouldLog('warn')) { console.warn(...this.formatMessage(...args)); } } info(...args) { if (this.shouldLog('info')) { console.info(...this.formatMessage(...args)); } } debug(...args) { if (this.shouldLog('debug')) { console.log(...this.formatMessage(...args)); } } } //# sourceMappingURL=requestUtils.js.map