Cache Manager with TTL
Generic cache manager with time-to-live and size limits
Code
interface CacheOptions<K, V> {
ttl?: number; // Time to live in milliseconds
maxSize?: number;
onEviction?: (key: K, value: V) => void;
}
interface CacheEntry<V> {
value: V;
expiresAt: number;
lastAccessed: number;
}
class CacheManager<K, V> {
private cache = new Map<K, CacheEntry<V>>();
private ttl: number;
private maxSize: number;
private onEviction?: (key: K, value: V) => void;
private cleanupInterval: NodeJS.Timeout;
constructor(options: CacheOptions<K, V> = {}) {
this.ttl = options.ttl || 60 * 1000; // Default 1 minute
this.maxSize = options.maxSize || 100;
this.onEviction = options.onEviction;
// Auto cleanup every minute
this.cleanupInterval = setInterval(() => this.cleanup(), 60 * 1000);
}
set(key: K, value: V, customTTL?: number): void {
const now = Date.now();
const ttl = customTTL || this.ttl;
// If cache is full, remove oldest item
__TOKEN_35__ (this.cache.size >= this.maxSize && !this.cache.has(key)) {
this.evictOldest();
}
this.cache.set(key, {
value,
expiresAt: now + ttl,
lastAccessed: now
});
}
get(key: K): V | undefined {
const entry = this.cache.get(key);
__TOKEN_43__ (!entry) return undefined;
const now = Date.now();
// Check if expired
__TOKEN_46__ (entry.expiresAt <= now) {
this.delete(key);
return undefined;
}
// Update last accessed time
entry.lastAccessed = now;
return entry.value;
}
has(key: K): boolean {
return this.get(key) !== undefined;
}
delete(key: K): boolean {
const entry = this.cache.get(key);
__TOKEN_54__ (entry && this.onEviction) {
this.onEviction(key, entry.value);
}
return this.cache.delete(key);
}
clear(): void {
__TOKEN_59__ (this.onEviction) {
this.cache.forEach((entry, key) => {
this.onEviction(key, entry.value);
});
}
this.cache.clear();
}
size(): number {
return this.cache.size;
}
keys(): K[] {
return Array.__TOKEN_67__(this.cache.keys());
}
values(): V[] {
return Array.__TOKEN_70__(this.cache.values()).map(entry => entry.value);
}
entries(): [K, V][] {
return Array.__TOKEN_73__(this.cache.entries()).map(([key, entry]) => [key, entry.value]);
}
private evictOldest(): void {
let oldestKey: K | null = null;
let oldestAccess = Infinity;
__TOKEN_78__ (const [key, entry] of this.cache.entries()) {
__TOKEN_82__ (entry.lastAccessed < oldestAccess) {
oldestAccess = entry.lastAccessed;
oldestKey = key;
}
}
__TOKEN_83__ (oldestKey) {
this.delete(oldestKey);
}
}
private cleanup(): void {
const now = Date.now();
__TOKEN_87__ (const [key, entry] of this.cache.entries()) {
__TOKEN_91__ (entry.expiresAt <= now) {
this.delete(key);
}
}
}
// Update TTL for a key
touch(key: K, newTTL?: number): boolean {
const entry = this.cache.get(key);
__TOKEN_95__ (!entry) return false;
const ttl = newTTL || this.ttl;
entry.expiresAt = Date.now() + ttl;
entry.lastAccessed = Date.now();
return true;
}
// Get remaining TTL
getRemainingTTL(key: K): number | undefined {
const entry = this.cache.get(key);
__TOKEN_102__ (!entry) return undefined;
const remaining = entry.expiresAt - Date.now();
return remaining > 0 ? remaining : 0;
}
// Async version with automatic value fetching
async getOrSet(
key: K,
fetcher: () => Promise<V>,
customTTL?: number
): Promise<V> {
const cached = this.get(key);
__TOKEN_109__ (cached !== undefined) {
return cached;
}
const value = await fetcher();
this.set(key, value, customTTL);
return value;
}
dispose(): void {
clearInterval(this.cleanupInterval);
this.clear();
}
}
// Usage examples
const cache = new CacheManager<string, number>({
ttl: 5000,
maxSize: 50,
onEviction: (key, value) => {
console.log(`Evicted ${key}: ${value}`);
}
});
// Basic operations
cache.set('result', 42);
const value = cache.get('result');
// Async fetch
const fetchUserData = __TOKEN_121__ (userId: string): Promise<any> => {
// Simulate API call
return { id: userId, name: 'John' };
};
const userCache = new CacheManager<string, any>({ ttl: 30000 });
async function getUser(userId: string) {
return userCache.getOrSet(
`user:${userId}`,
() => fetchUserData(userId)
);
}