memorium/lib/cache.ts

114 lines
2.8 KiB
TypeScript

interface CreateCacheOptions {
expires?: number; // Default expiration time for all cache entries
}
interface SetCacheOptions {
expires?: number; // Override expiration for individual cache entries
}
export const caches = new Map<
string,
{
info: () => { name: string; count: number; sizeInKB: number };
clear: () => void;
}
>();
export function createCache<T>(
cacheName: string,
createOpts: CreateCacheOptions = {},
) {
const cache = new Map<string, { value: T; expiresAt?: number }>();
const api = {
get(key: string): T | undefined {
const entry = cache.get(key);
if (!entry) return undefined;
const now = Date.now();
if (entry.expiresAt && entry.expiresAt <= now) {
cache.delete(key); // Remove expired entry
return undefined;
}
return entry.value; // Return value if not expired
},
clear() {
cache.clear();
},
set(key: string, value: T | unknown, opts: SetCacheOptions = {}) {
const now = Date.now();
const expiresIn = opts.expires ?? createOpts.expires;
const expiresAt = expiresIn ? now + expiresIn : undefined;
cache.set(key, { value: value as T, expiresAt });
},
cleanup() {
const now = Date.now();
for (const [key, entry] of cache.entries()) {
if (entry.expiresAt && entry.expiresAt <= now) {
cache.delete(key);
}
}
},
info() {
// Cleanup expired entries before calculating info
this.cleanup();
// Count the number of objects in the cache
const count = cache.size;
// Approximate the size in KB by serializing each key and value
let totalBytes = 0;
for (const [key, entry] of cache.entries()) {
const keySize = new TextEncoder().encode(key).length;
const valueSize = new TextEncoder().encode(
JSON.stringify(entry.value),
).length;
totalBytes += keySize + valueSize;
}
const sizeInKB = Math.floor(totalBytes / 1024); // Convert bytes to kilobytes
return { name: cacheName, count, sizeInKB };
},
has(key: string): boolean {
const entry = cache.get(key);
if (!entry) return false;
const now = Date.now();
if (entry.expiresAt && entry.expiresAt <= now) {
cache.delete(key); // Remove expired entry
return false;
}
return true;
},
keys(): string[] {
this.cleanup(); // Cleanup before returning keys
return Array.from(cache.keys());
},
size(): number {
this.cleanup(); // Cleanup before returning size
return cache.size;
},
};
caches.set(cacheName, {
clear: api.clear.bind(api),
info: api.info.bind(api),
});
return api;
}
export function getCacheInfo() {
return [...caches.values().map((c) => c.info())];
}