/**
* Simple In-Memory Cache with TTL
* Used to reduce API load by caching frequently accessed data
*/
export interface CacheEntry<T> {
data: T;
expires: number;
}
export class SimpleCache<T> {
private cache = new Map<string, CacheEntry<T>>();
private defaultTtlSeconds: number;
constructor(defaultTtlSeconds: number = 300) {
this.defaultTtlSeconds = defaultTtlSeconds;
}
/**
* Store data in cache with optional TTL
*/
set(key: string, data: T, ttlSeconds?: number): void {
const ttl = ttlSeconds ?? this.defaultTtlSeconds;
this.cache.set(key, {
data,
expires: Date.now() + ttl * 1000
});
}
/**
* Retrieve data from cache
* Returns null if not found or expired
*/
get(key: string): T | null {
const entry = this.cache.get(key);
if (!entry) {
return null;
}
// Check if expired
if (entry.expires < Date.now()) {
this.cache.delete(key);
return null;
}
return entry.data;
}
/**
* Check if key exists and is not expired
*/
has(key: string): boolean {
return this.get(key) !== null;
}
/**
* Invalidate a specific cache key
*/
invalidate(key: string): void {
this.cache.delete(key);
}
/**
* Invalidate all keys matching a pattern
*/
invalidatePattern(pattern: RegExp): void {
for (const key of this.cache.keys()) {
if (pattern.test(key)) {
this.cache.delete(key);
}
}
}
/**
* Clear all cache entries
*/
clear(): void {
this.cache.clear();
}
/**
* Get cache statistics
*/
getStats(): { size: number; keys: string[] } {
// Clean expired entries first
for (const [key, entry] of this.cache.entries()) {
if (entry.expires < Date.now()) {
this.cache.delete(key);
}
}
return {
size: this.cache.size,
keys: Array.from(this.cache.keys())
};
}
}
/**
* Cache with automatic cleanup of expired entries
*/
export class AutoCleanCache<T> extends SimpleCache<T> {
private cleanupInterval: NodeJS.Timeout | null = null;
constructor(defaultTtlSeconds: number = 300, cleanupIntervalMs: number = 60000) {
super(defaultTtlSeconds);
this.startCleanup(cleanupIntervalMs);
}
private startCleanup(intervalMs: number): void {
this.cleanupInterval = setInterval(() => {
this.cleanup();
}, intervalMs);
}
private cleanup(): void {
const stats = this.getStats();
console.error(`[Cache] Cleanup: ${stats.size} entries in cache`);
}
stopCleanup(): void {
if (this.cleanupInterval) {
clearInterval(this.cleanupInterval);
this.cleanupInterval = null;
}
}
}