Dynamic SWR cache
Created by Anonymous
Public
Created March 16, 2024 Expires in 36195 days
Loading editor...
interface CacheEntry<T> { value: T; timestamp: number; // Timestamp when the data was fetched } interface Options { swr: number; // Time in seconds to allow stale data to be used while revalidating maxAge: number; // Maximum age in seconds before data is considered stale debug: boolean; // Enable debug mode } const defaultOptions: Options = { swr: 0, maxAge: 0, debug: false, }; export const createAsyncCache = <T, Args extends any[]>( func: (...args: Args) => Promise<T>, paramOptions: Partial<Options> = {} ) => { const options: Options = { ...defaultOptions, ...paramOptions }; const cache: Record<string, CacheEntry<T>> = {}; const debugLog = (message: string, ...data: any[]) => { if (options.debug) { console.log(`[AsyncCache]: ${message}`, ...data); } }; const fetchAndCache = async (...args: Args): Promise<T> => { const key = JSON.stringify(args); try { const result = await func(...args); cache[key] = { value: result, timestamp: Date.now() }; return result; } catch (error) { debugLog(`Error fetching data for key ${key}:`, error); throw error; // Rethrow to handle upstream } }; const isEntryStale = (entry: CacheEntry<T> | undefined): boolean => { if (!entry) return true; const ageInSeconds = (Date.now() - entry.timestamp) / 1000; return ageInSeconds > options.maxAge; }; const get = async (...args: Args): Promise<T> => { const key = JSON.stringify(args); const entry = cache[key]; const entryExists = !!entry; const stale = isEntryStale(entry); if (!entryExists || stale) { if (entryExists && stale) { const ageInSeconds = (Date.now() - entry.timestamp) / 1000; if (ageInSeconds <= options.maxAge + options.swr) { debugLog("STALE (revalidating)", { key, ageInSeconds }); fetchAndCache(...args).catch(error => debugLog(`Error revalidating data for key ${key}:`, error) ); } else { debugLog("STALE (no revalidation)", { key, ageInSeconds }); return fetchAndCache(...args); } } else { debugLog("MISS", { key }); return fetchAndCache(...args); } } else { debugLog("HIT", { key }); } return entry.value; }; return { get }; };