UNPKG

@push.rocks/webrequest

Version:

Modern, fetch-compatible web request library with intelligent HTTP caching, retry strategies, and fault tolerance.

242 lines 17.3 kB
/** * WebrequestClient - Advanced configuration and global interceptors */ import { InterceptorManager } from './interceptors/interceptor.manager.js'; import { CacheManager } from './cache/cache.manager.js'; import { RetryManager } from './retry/retry.manager.js'; import { RequestDeduplicator } from './utils/deduplicator.js'; import { fetchWithTimeout } from './utils/timeout.js'; export class WebrequestClient { constructor(options = {}) { this.defaultOptions = options; this.interceptorManager = new InterceptorManager(); this.cacheManager = new CacheManager(); this.deduplicator = new RequestDeduplicator(); } /** * Add a global request interceptor */ addRequestInterceptor(interceptor) { this.interceptorManager.addRequestInterceptor(interceptor); } /** * Add a global response interceptor */ addResponseInterceptor(interceptor) { this.interceptorManager.addResponseInterceptor(interceptor); } /** * Add a global error interceptor */ addErrorInterceptor(interceptor) { this.interceptorManager.addErrorInterceptor(interceptor); } /** * Remove a request interceptor */ removeRequestInterceptor(interceptor) { this.interceptorManager.removeRequestInterceptor(interceptor); } /** * Remove a response interceptor */ removeResponseInterceptor(interceptor) { this.interceptorManager.removeResponseInterceptor(interceptor); } /** * Remove an error interceptor */ removeErrorInterceptor(interceptor) { this.interceptorManager.removeErrorInterceptor(interceptor); } /** * Clear all interceptors */ clearInterceptors() { this.interceptorManager.clearAll(); } /** * Clear the cache */ async clearCache() { await this.cacheManager.clear(); } /** * Execute a request with all configured features */ async request(url, options = {}) { // Merge default options with request options const mergedOptions = { ...this.defaultOptions, ...options, }; // Create Request object let request; if (typeof url === 'string') { request = new Request(url, mergedOptions); } else { request = url; } // Process through request interceptors request = await this.interceptorManager.processRequest(request); // Add per-request interceptors if provided if (mergedOptions.interceptors?.request) { for (const interceptor of mergedOptions.interceptors.request) { request = await interceptor(request); } } // Execute with deduplication if enabled const deduplicate = mergedOptions.deduplicate ?? false; if (deduplicate) { const dedupeKey = this.deduplicator.generateKey(request); const result = await this.deduplicator.execute(dedupeKey, async () => { return await this.executeRequest(request, mergedOptions); }); return result.response; } return await this.executeRequest(request, mergedOptions); } /** * Internal request execution with caching and retry */ async executeRequest(request, options) { try { // Determine if retry is enabled const retryOptions = typeof options.retry === 'object' ? options.retry : options.retry ? {} : undefined; // Create fetch function for Request objects (used with caching) const fetchFnForRequest = async (req) => { const timeout = options.timeout ?? 60000; return await fetchWithTimeout(req.url, { method: req.method, headers: req.headers, body: req.body, ...options, }, timeout); }; // Create fetch function for fallbacks (url + init) const fetchFnForFallbacks = async (url, init) => { const timeout = options.timeout ?? 60000; return await fetchWithTimeout(url, init, timeout); }; let response; // Execute with retry if enabled if (retryOptions) { const retryManager = new RetryManager(retryOptions); // Handle fallback URLs if provided if (options.fallbackUrls && options.fallbackUrls.length > 0) { const allUrls = [request.url, ...options.fallbackUrls]; response = await retryManager.executeWithFallbacks(allUrls, { method: request.method, headers: request.headers, body: request.body, ...options, }, fetchFnForFallbacks); } else { response = await retryManager.execute(async () => { // Execute with caching const result = await this.cacheManager.execute(request, options, fetchFnForRequest); return result.response; }); } } else { // Execute with caching (no retry) const result = await this.cacheManager.execute(request, options, fetchFnForRequest); response = result.response; } // Process through response interceptors response = await this.interceptorManager.processResponse(response); // Add per-request response interceptors if provided if (options.interceptors?.response) { for (const interceptor of options.interceptors.response) { response = await interceptor(response); } } return response; } catch (error) { // Process through error interceptors const processedError = await this.interceptorManager.processError(error instanceof Error ? error : new Error(String(error))); throw processedError; } } /** * Convenience method: GET request returning JSON */ async getJson(url, options = {}) { const response = await this.request(url, { ...options, method: 'GET', headers: { Accept: 'application/json', ...(options.headers || {}), }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } /** * Convenience method: POST request with JSON body */ async postJson(url, data, options = {}) { const response = await this.request(url, { ...options, method: 'POST', headers: { 'Content-Type': 'application/json', Accept: 'application/json', ...(options.headers || {}), }, body: JSON.stringify(data), }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } /** * Convenience method: PUT request with JSON body */ async putJson(url, data, options = {}) { const response = await this.request(url, { ...options, method: 'PUT', headers: { 'Content-Type': 'application/json', Accept: 'application/json', ...(options.headers || {}), }, body: JSON.stringify(data), }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } /** * Convenience method: DELETE request */ async deleteJson(url, options = {}) { const response = await this.request(url, { ...options, method: 'DELETE', headers: { Accept: 'application/json', ...(options.headers || {}), }, }); if (!response.ok) { throw new Error(`HTTP ${response.status}: ${response.statusText}`); } return await response.json(); } } //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2VicmVxdWVzdC5jbGllbnQuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi90cy93ZWJyZXF1ZXN0LmNsaWVudC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQTs7R0FFRztBQVFILE9BQU8sRUFBRSxrQkFBa0IsRUFBRSxNQUFNLHVDQUF1QyxDQUFDO0FBQzNFLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSwwQkFBMEIsQ0FBQztBQUN4RCxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMEJBQTBCLENBQUM7QUFDeEQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFDOUQsT0FBTyxFQUFFLGdCQUFnQixFQUFFLE1BQU0sb0JBQW9CLENBQUM7QUFFdEQsTUFBTSxPQUFPLGdCQUFnQjtJQU0zQixZQUFZLFVBQXVDLEVBQUU7UUFDbkQsSUFBSSxDQUFDLGNBQWMsR0FBRyxPQUFPLENBQUM7UUFDOUIsSUFBSSxDQUFDLGtCQUFrQixHQUFHLElBQUksa0JBQWtCLEVBQUUsQ0FBQztRQUNuRCxJQUFJLENBQUMsWUFBWSxHQUFHLElBQUksWUFBWSxFQUFFLENBQUM7UUFDdkMsSUFBSSxDQUFDLFlBQVksR0FBRyxJQUFJLG1CQUFtQixFQUFFLENBQUM7SUFDaEQsQ0FBQztJQUVEOztPQUVHO0lBQ0kscUJBQXFCLENBQUMsV0FBZ0M7UUFDM0QsSUFBSSxDQUFDLGtCQUFrQixDQUFDLHFCQUFxQixDQUFDLFdBQVcsQ0FBQyxDQUFDO0lBQzdELENBQUM7SUFFRDs7T0FFRztJQUNJLHNCQUFzQixDQUFDLFdBQWlDO1FBQzdELElBQUksQ0FBQyxrQkFBa0IsQ0FBQyxzQkFBc0IsQ0FBQyxXQUFXLENBQUMsQ0FBQztJQUM5RCxDQUFDO0lBRUQ7O09BRUc7SUFDSSxtQkFBbUIsQ0FBQyxXQUE4QjtRQUN2RCxJQUFJLENBQUMsa0JBQWtCLENBQUMsbUJBQW1CLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDM0QsQ0FBQztJQUVEOztPQUVH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