feat: Implement comprehensive web request handling with caching, retry, and interceptors
- Added cache strategies: NetworkFirst, CacheFirst, StaleWhileRevalidate, NetworkOnly, and CacheOnly. - Introduced InterceptorManager for managing request, response, and error interceptors. - Developed RetryManager for handling request retries with customizable backoff strategies. - Implemented RequestDeduplicator to prevent simultaneous identical requests. - Created timeout utilities for handling request timeouts. - Enhanced WebrequestClient to support global interceptors, caching, and retry logic. - Added convenience methods for common HTTP methods (GET, POST, PUT, DELETE) with JSON handling. - Established a fetch-compatible webrequest function for seamless integration. - Defined core type structures for caching, retry options, interceptors, and web request configurations.
This commit is contained in:
149
ts/interceptors/interceptor.manager.ts
Normal file
149
ts/interceptors/interceptor.manager.ts
Normal file
@@ -0,0 +1,149 @@
|
||||
/**
|
||||
* Interceptor manager for request/response transformation
|
||||
*/
|
||||
|
||||
import type {
|
||||
TRequestInterceptor,
|
||||
TResponseInterceptor,
|
||||
TErrorInterceptor,
|
||||
} from './interceptor.types.js';
|
||||
|
||||
export class InterceptorManager {
|
||||
private requestInterceptors: TRequestInterceptor[] = [];
|
||||
private responseInterceptors: TResponseInterceptor[] = [];
|
||||
private errorInterceptors: TErrorInterceptor[] = [];
|
||||
|
||||
/**
|
||||
* Add a request interceptor
|
||||
*/
|
||||
public addRequestInterceptor(interceptor: TRequestInterceptor): void {
|
||||
this.requestInterceptors.push(interceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add a response interceptor
|
||||
*/
|
||||
public addResponseInterceptor(interceptor: TResponseInterceptor): void {
|
||||
this.responseInterceptors.push(interceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add an error interceptor
|
||||
*/
|
||||
public addErrorInterceptor(interceptor: TErrorInterceptor): void {
|
||||
this.errorInterceptors.push(interceptor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a request interceptor
|
||||
*/
|
||||
public removeRequestInterceptor(interceptor: TRequestInterceptor): void {
|
||||
const index = this.requestInterceptors.indexOf(interceptor);
|
||||
if (index > -1) {
|
||||
this.requestInterceptors.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove a response interceptor
|
||||
*/
|
||||
public removeResponseInterceptor(interceptor: TResponseInterceptor): void {
|
||||
const index = this.responseInterceptors.indexOf(interceptor);
|
||||
if (index > -1) {
|
||||
this.responseInterceptors.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove an error interceptor
|
||||
*/
|
||||
public removeErrorInterceptor(interceptor: TErrorInterceptor): void {
|
||||
const index = this.errorInterceptors.indexOf(interceptor);
|
||||
if (index > -1) {
|
||||
this.errorInterceptors.splice(index, 1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Clear all interceptors
|
||||
*/
|
||||
public clearAll(): void {
|
||||
this.requestInterceptors = [];
|
||||
this.responseInterceptors = [];
|
||||
this.errorInterceptors = [];
|
||||
}
|
||||
|
||||
/**
|
||||
* Process request through all request interceptors
|
||||
*/
|
||||
public async processRequest(request: Request): Promise<Request> {
|
||||
let processedRequest = request;
|
||||
|
||||
for (const interceptor of this.requestInterceptors) {
|
||||
try {
|
||||
processedRequest = await interceptor(processedRequest);
|
||||
} catch (error) {
|
||||
// If interceptor throws, process through error interceptors
|
||||
throw await this.processError(
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return processedRequest;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process response through all response interceptors
|
||||
*/
|
||||
public async processResponse(response: Response): Promise<Response> {
|
||||
let processedResponse = response;
|
||||
|
||||
for (const interceptor of this.responseInterceptors) {
|
||||
try {
|
||||
processedResponse = await interceptor(processedResponse);
|
||||
} catch (error) {
|
||||
// If interceptor throws, process through error interceptors
|
||||
throw await this.processError(
|
||||
error instanceof Error ? error : new Error(String(error)),
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
return processedResponse;
|
||||
}
|
||||
|
||||
/**
|
||||
* Process error through all error interceptors
|
||||
*/
|
||||
public async processError(error: Error): Promise<Error> {
|
||||
let processedError = error;
|
||||
|
||||
for (const interceptor of this.errorInterceptors) {
|
||||
try {
|
||||
processedError = await interceptor(processedError);
|
||||
} catch (newError) {
|
||||
// If error interceptor throws, use the new error
|
||||
processedError =
|
||||
newError instanceof Error ? newError : new Error(String(newError));
|
||||
}
|
||||
}
|
||||
|
||||
return processedError;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get count of registered interceptors
|
||||
*/
|
||||
public getInterceptorCounts(): {
|
||||
request: number;
|
||||
response: number;
|
||||
error: number;
|
||||
} {
|
||||
return {
|
||||
request: this.requestInterceptors.length,
|
||||
response: this.responseInterceptors.length,
|
||||
error: this.errorInterceptors.length,
|
||||
};
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user