/** * Cache backends — MemoryCache for testing. * * Same API as Python's MemoryCache. RedisCache is implemented * per-adapter for production (not included here). */ export interface CacheBackend { get(key: string): string | null put(key: string, value: string, indexes: string[]): void deleteMany(keys: string[]): number getIndex(indexKey: string): Set removeFromIndex(indexKey: string, members: Set): void deleteIndex(indexKey: string): void deleteIndexesByPrefix(prefix: string): void clear(): void } export class MemoryCache implements CacheBackend { private _store = new Map() private _indexes = new Map>() get(key: string): string | null { return this._store.get(key) ?? null } put(key: string, value: string, indexes: string[]): void { this._store.set(key, value) for (const idx of indexes) { if (!this._indexes.has(idx)) { this._indexes.set(idx, new Set()) } this._indexes.get(idx)!.add(key) } } deleteMany(keys: string[]): number { let count = 0 for (const key of keys) { if (this._store.delete(key)) count++ } return count } getIndex(indexKey: string): Set { return new Set(this._indexes.get(indexKey) ?? []) } removeFromIndex(indexKey: string, members: Set): void { const idx = this._indexes.get(indexKey) if (!idx) return for (const m of members) idx.delete(m) if (idx.size === 0) this._indexes.delete(indexKey) } deleteIndex(indexKey: string): void { this._indexes.delete(indexKey) } deleteIndexesByPrefix(prefix: string): void { for (const key of [...this._indexes.keys()]) { if (key.startsWith(prefix)) this._indexes.delete(key) } } clear(): void { this._store.clear() this._indexes.clear() } }