- 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.
157 lines
3.6 KiB
TypeScript
157 lines
3.6 KiB
TypeScript
/**
|
|
* 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;
|
|
}
|
|
}
|