interface CacheEntry<T> {
data: T;
timestamp: number;
ttl: number;
}
export const TTL = {
PAGE_CONTENT: 15 * 60 * 1000, // 15 minutes for individual pages
DOCS_INDEX: 60 * 60 * 1000, // 1 hour for docs index
SEARCH_RESULTS: 5 * 60 * 1000, // 5 minutes for search results
FULL_DOCS: 60 * 60 * 1000, // 1 hour for full docs
} as const;
export type TTLType = keyof typeof TTL;
const MAX_CACHE_SIZE = 50;
export class CacheService {
private cache: Map<string, CacheEntry<unknown>> = new Map();
set<T>(key: string, data: T, ttlType: TTLType): void {
// Evict oldest entries if cache is full
if (this.cache.size >= MAX_CACHE_SIZE) {
const oldestKey = this.cache.keys().next().value;
if (oldestKey) {
this.cache.delete(oldestKey);
}
}
this.cache.set(key, {
data,
timestamp: Date.now(),
ttl: TTL[ttlType],
});
}
get<T>(key: string): T | null {
const entry = this.cache.get(key);
if (!entry) {
return null;
}
// Check if entry has expired
if (Date.now() - entry.timestamp > entry.ttl) {
this.cache.delete(key);
return null;
}
return entry.data as T;
}
has(key: string): boolean {
return this.get(key) !== null;
}
invalidate(pattern?: string): void {
if (!pattern) {
this.cache.clear();
return;
}
for (const key of this.cache.keys()) {
if (key.includes(pattern)) {
this.cache.delete(key);
}
}
}
size(): number {
return this.cache.size;
}
}
// Singleton instance
export const cache = new CacheService();