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, | ||||
|     }; | ||||
|   } | ||||
| } | ||||
							
								
								
									
										31
									
								
								ts/interceptors/interceptor.types.ts
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								ts/interceptors/interceptor.types.ts
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,31 @@ | ||||
| /** | ||||
|  * Interceptor type definitions | ||||
|  */ | ||||
|  | ||||
| /** | ||||
|  * Request interceptor | ||||
|  * Transforms the request before it's sent | ||||
|  */ | ||||
| export type TRequestInterceptor = ( | ||||
|   request: Request, | ||||
| ) => Request | Promise<Request>; | ||||
|  | ||||
| /** | ||||
|  * Response interceptor | ||||
|  * Transforms the response after it's received | ||||
|  */ | ||||
| export type TResponseInterceptor = ( | ||||
|   response: Response, | ||||
| ) => Response | Promise<Response>; | ||||
|  | ||||
| /** | ||||
|  * Error interceptor | ||||
|  * Handles errors during request/response processing | ||||
|  */ | ||||
| export type TErrorInterceptor = (error: Error) => Error | Promise<Error>; | ||||
|  | ||||
| export interface IInterceptors { | ||||
|   request?: TRequestInterceptor[]; | ||||
|   response?: TResponseInterceptor[]; | ||||
|   error?: TErrorInterceptor[]; | ||||
| } | ||||
		Reference in New Issue
	
	Block a user