/** * @fileoverview In-memory implementation of {@link MemoryStore}. * * All data lives in a plain `Map` and is never persisted to disk. This is the * default store used by {@link SharedMemory} and is suitable for testing and * single-process use-cases. Swap it for a Redis or SQLite-backed implementation * in production by satisfying the same {@link MemoryStore} interface. */ import type { MemoryEntry, MemoryStore } from '../types.js' // --------------------------------------------------------------------------- // InMemoryStore // --------------------------------------------------------------------------- /** * Synchronous-under-the-hood key/value store that exposes an `async` surface * so implementations can be swapped for async-native backends without changing * callers. * * All keys are treated as opaque strings. Values are always strings; structured * data must be serialised by the caller (e.g. `JSON.stringify`). * * @example * ```ts * const store = new InMemoryStore() * await store.set('config', JSON.stringify({ model: 'claude-opus-4-6' })) * const entry = await store.get('config') * ``` */ export class InMemoryStore implements MemoryStore { private readonly data = new Map() // --------------------------------------------------------------------------- // MemoryStore interface // --------------------------------------------------------------------------- /** Returns the entry for `key`, or `null` if not present. */ async get(key: string): Promise { return this.data.get(key) ?? null } /** * Upserts `key` with `value` and optional `metadata`. * * If the key already exists its `createdAt` is **preserved** so callers can * detect when a value was first written. */ async set( key: string, value: string, metadata?: Record, ): Promise { const existing = this.data.get(key) const entry: MemoryEntry = { key, value, metadata: metadata !== undefined ? { ...metadata } : undefined, createdAt: existing?.createdAt ?? new Date(), } this.data.set(key, entry) } /** Returns a snapshot of all entries in insertion order. */ async list(): Promise { return Array.from(this.data.values()) } /** * Removes the entry for `key`. * Deleting a non-existent key is a no-op. */ async delete(key: string): Promise { this.data.delete(key) } /** Removes **all** entries from the store. */ async clear(): Promise { this.data.clear() } // --------------------------------------------------------------------------- // Extensions beyond the base MemoryStore interface // --------------------------------------------------------------------------- /** * Returns entries whose `key` starts with `query` **or** whose `value` * contains `query` (case-insensitive substring match). * * This is a simple linear scan; it is not suitable for very large stores * without an index layer on top. * * @example * ```ts * // Find all entries related to "research" * const hits = await store.search('research') * ``` */ async search(query: string): Promise { if (query.length === 0) { return this.list() } const lower = query.toLowerCase() return Array.from(this.data.values()).filter( (entry) => entry.key.toLowerCase().includes(lower) || entry.value.toLowerCase().includes(lower), ) } // --------------------------------------------------------------------------- // Convenience helpers (not part of MemoryStore) // --------------------------------------------------------------------------- /** Returns the number of entries currently held in the store. */ get size(): number { return this.data.size } /** Returns `true` if `key` exists in the store. */ has(key: string): boolean { return this.data.has(key) } }