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:
		
							
								
								
									
										156
									
								
								ts/cache/cache.manager.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										156
									
								
								ts/cache/cache.manager.ts
									
									
									
									
										vendored
									
									
										Normal file
									
								
							| @@ -0,0 +1,156 @@ | ||||
| /** | ||||
|  * Cache manager - orchestrates caching logic | ||||
|  */ | ||||
|  | ||||
| import type { | ||||
|   ICacheOptions, | ||||
|   TCacheStrategy, | ||||
|   TStandardCacheMode, | ||||
| } from '../webrequest.types.js'; | ||||
| import { CacheStore } from './cache.store.js'; | ||||
| import { | ||||
|   getStrategyHandler, | ||||
|   type IStrategyContext, | ||||
|   type IStrategyResult, | ||||
| } from './cache.strategies.js'; | ||||
| import { extractCacheMetadata } from './cache.headers.js'; | ||||
|  | ||||
| export class CacheManager { | ||||
|   private cacheStore: CacheStore; | ||||
|  | ||||
|   constructor(dbName?: string, storeName?: string) { | ||||
|     this.cacheStore = new CacheStore(dbName, storeName); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Execute a request with caching | ||||
|    */ | ||||
|   public async execute( | ||||
|     request: Request, | ||||
|     options: ICacheOptions & { logging?: boolean }, | ||||
|     fetchFn: (request: Request) => Promise<Response>, | ||||
|   ): Promise<IStrategyResult> { | ||||
|     // Determine the cache strategy | ||||
|     const strategy = this.determineStrategy(request, options); | ||||
|  | ||||
|     // If no caching (no-store or network-only), bypass cache | ||||
|     if (strategy === 'network-only') { | ||||
|       const response = await fetchFn(request); | ||||
|       return { | ||||
|         response, | ||||
|         fromCache: false, | ||||
|         revalidated: false, | ||||
|       }; | ||||
|     } | ||||
|  | ||||
|     // Generate cache key | ||||
|     const cacheKey = this.generateCacheKey(request, options); | ||||
|  | ||||
|     // Get strategy handler | ||||
|     const handler = getStrategyHandler(strategy); | ||||
|  | ||||
|     // Execute strategy | ||||
|     const context: IStrategyContext = { | ||||
|       request, | ||||
|       cacheKey, | ||||
|       cacheStore: this.cacheStore, | ||||
|       fetchFn, | ||||
|       logging: options.logging, | ||||
|     }; | ||||
|  | ||||
|     return await handler.execute(context); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Determine the caching strategy based on options and request | ||||
|    */ | ||||
|   private determineStrategy( | ||||
|     request: Request, | ||||
|     options: ICacheOptions, | ||||
|   ): TCacheStrategy { | ||||
|     // If explicit strategy provided, use it | ||||
|     if (options.cacheStrategy) { | ||||
|       return options.cacheStrategy; | ||||
|     } | ||||
|  | ||||
|     // Map standard cache modes to strategies | ||||
|     if (options.cache) { | ||||
|       return this.mapCacheModeToStrategy(options.cache); | ||||
|     } | ||||
|  | ||||
|     // Check request cache mode | ||||
|     if (request.cache) { | ||||
|       return this.mapCacheModeToStrategy(request.cache as TStandardCacheMode); | ||||
|     } | ||||
|  | ||||
|     // Default strategy | ||||
|     return 'network-first'; | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Map standard fetch cache modes to our strategies | ||||
|    */ | ||||
|   private mapCacheModeToStrategy( | ||||
|     cacheMode: TStandardCacheMode, | ||||
|   ): TCacheStrategy { | ||||
|     switch (cacheMode) { | ||||
|       case 'default': | ||||
|         return 'network-first'; | ||||
|       case 'no-store': | ||||
|       case 'reload': | ||||
|         return 'network-only'; | ||||
|       case 'no-cache': | ||||
|         return 'network-first'; // Will use revalidation | ||||
|       case 'force-cache': | ||||
|         return 'cache-first'; | ||||
|       case 'only-if-cached': | ||||
|         return 'cache-only'; | ||||
|       default: | ||||
|         return 'network-first'; | ||||
|     } | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Generate cache key | ||||
|    */ | ||||
|   private generateCacheKey(request: Request, options: ICacheOptions): string { | ||||
|     // If custom cache key provided | ||||
|     if (options.cacheKey) { | ||||
|       if (typeof options.cacheKey === 'function') { | ||||
|         return options.cacheKey(request); | ||||
|       } | ||||
|       return options.cacheKey; | ||||
|     } | ||||
|  | ||||
|     // Default cache key generation | ||||
|     return this.cacheStore.generateCacheKey(request); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Clear the cache | ||||
|    */ | ||||
|   public async clear(): Promise<void> { | ||||
|     await this.cacheStore.clear(); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Delete a specific cache entry | ||||
|    */ | ||||
|   public async delete(cacheKey: string): Promise<void> { | ||||
|     await this.cacheStore.delete(cacheKey); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Check if a cache entry exists | ||||
|    */ | ||||
|   public async has(cacheKey: string): Promise<boolean> { | ||||
|     return await this.cacheStore.has(cacheKey); | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    * Get the underlying cache store | ||||
|    */ | ||||
|   public getStore(): CacheStore { | ||||
|     return this.cacheStore; | ||||
|   } | ||||
| } | ||||
		Reference in New Issue
	
	Block a user