@push.rocks/webrequest
Version:
Modern, fetch-compatible web request library with intelligent HTTP caching, retry strategies, and fault tolerance.
242 lines • 17.3 kB
JavaScript
/**
* 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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