Dynamic SWR cache
Created by Anonymous
Public
Created March 16, 2024 Expires in 35691 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 };
};