export interface CacheEntry { key: string; data: T; timestamp: number; metadata: Record; } interface StoredCacheEntry { data: any; timestamp: number; metadata: Record; } export class CacheManager { private readonly entries = new Map(); constructor() {} get(key: string): CacheEntry | null { const entry = this.entries.get(key); if (!entry) return null; return { key, data: entry.data, timestamp: entry.timestamp, metadata: { ...entry.metadata }, }; } set(key: string, data: any, metadata: Record = {}): void { this.entries.set(key, { data, timestamp: Date.now() / 1000, metadata: { ...metadata }, }); } invalidate(key: string): void { this.entries.delete(key); } cleanup(ttl: number): number { const cutoff = Date.now() / 1000 - ttl; let removed = 0; for (const [key, entry] of this.entries.entries()) { if (entry.timestamp < cutoff) { this.entries.delete(key); removed += 1; } } return removed; } getStats(): { entry_count: number; db_path: string; db_size_bytes: number } { return { entry_count: this.entries.size, db_path: "", db_size_bytes: JSON.stringify(Array.from(this.entries.values())).length, }; } getAllEntries(): Record> { const now = Date.now() / 1000; const sortedEntries = Array.from(this.entries.entries()).sort( (a, b) => b[1].timestamp - a[1].timestamp, ); const result: Record> = {}; for (const [key, entry] of sortedEntries) { const dataJson = JSON.stringify(entry.data); result[key] = { age_seconds: Math.round((now - entry.timestamp) * 100) / 100, size_bytes: Buffer.byteLength(dataJson ?? "", "utf8"), timestamp: entry.timestamp, content_preview: this.getPreview(entry.data), }; } return result; } getPreview(data: any, maxLength = 150): string { if (data && typeof data === "object" && !Array.isArray(data)) { if ("library" in data) { const library = String((data as Record).library ?? ""); let description = ""; const record = data as Record; if ("pypi" in record && record.pypi && typeof record.pypi === "object" && !Array.isArray(record.pypi)) { const pypi = record.pypi as Record; description = String((pypi.summary as string | undefined) ?? (pypi.description as string | undefined) ?? ""); } if (!description && "npm" in record && record.npm && typeof record.npm === "object" && !Array.isArray(record.npm)) { const npm = record.npm as Record; description = String((npm.summary as string | undefined) ?? (npm.description as string | undefined) ?? ""); } if (!description && "crates" in record && record.crates && typeof record.crates === "object" && !Array.isArray(record.crates)) { const crates = record.crates as Record; description = String((crates.description as string | undefined) ?? ""); } if (!description && "godocs" in record && record.godocs && typeof record.godocs === "object" && !Array.isArray(record.godocs)) { const godocs = record.godocs as Record; description = String((godocs.synopsis as string | undefined) ?? ""); } if (!description && Array.isArray(record.github_repos) && record.github_repos.length > 0) { const first = record.github_repos[0]; if (first && typeof first === "object" && !Array.isArray(first)) { const githubRepo = first as Record; description = String((githubRepo.description as string | undefined) ?? ""); } } if (description) { if (description.length > 100) { description = `${description.slice(0, 100)}...`; } return `search:${library} -> ${description}`; } const keys = Object.keys(record).filter((key) => key !== "library"); return `search:${library} -> ${keys.slice(0, 3).join(", ")}`; } const keys = Object.keys(data).slice(0, 3); return `dict: ${keys.join(", ")}`; } if (typeof data === "string") { return data.slice(0, maxLength); } return String(data).slice(0, maxLength); } }