UNPKG

claude-flow-multilang

Version:

Revolutionary multilingual AI orchestration framework with cultural awareness and DDD architecture

217 lines (175 loc) 5.62 kB
/** * In-memory store for environments where SQLite is not available * Provides the same API as SQLite store but data is not persistent */ import { sessionSerializer } from './enhanced-session-serializer.js'; class InMemoryStore { constructor(options = {}) { this.options = options; this.data = new Map(); // namespace -> Map(key -> entry) this.isInitialized = false; this.cleanupInterval = null; } async initialize() { if (this.isInitialized) return; // Initialize default namespace this.data.set('default', new Map()); // Start cleanup interval for expired entries this.cleanupInterval = setInterval(() => { this.cleanup().catch((err) => console.error(`[${new Date().toISOString()}] ERROR [in-memory-store] Cleanup failed:`, err), ); }, 60000); // Run cleanup every minute this.isInitialized = true; console.error( `[${new Date().toISOString()}] INFO [in-memory-store] Initialized in-memory store`, ); } _getNamespaceMap(namespace) { if (!this.data.has(namespace)) { this.data.set(namespace, new Map()); } return this.data.get(namespace); } async store(key, value, options = {}) { await this.initialize(); const namespace = options.namespace || 'default'; const namespaceMap = this._getNamespaceMap(namespace); const now = Date.now(); const ttl = options.ttl || null; const expiresAt = ttl ? now + ttl * 1000 : null; const valueStr = typeof value === 'string' ? value : sessionSerializer.serializer.serialize(value); const entry = { key, value: valueStr, namespace, metadata: options.metadata || null, createdAt: namespaceMap.has(key) ? namespaceMap.get(key).createdAt : now, updatedAt: now, accessedAt: now, accessCount: namespaceMap.has(key) ? namespaceMap.get(key).accessCount + 1 : 1, ttl, expiresAt, }; namespaceMap.set(key, entry); return { success: true, id: `${namespace}:${key}`, size: valueStr.length, }; } async retrieve(key, options = {}) { await this.initialize(); const namespace = options.namespace || 'default'; const namespaceMap = this._getNamespaceMap(namespace); const entry = namespaceMap.get(key); if (!entry) { return null; } // Check if expired if (entry.expiresAt && entry.expiresAt < Date.now()) { namespaceMap.delete(key); return null; } // Update access stats entry.accessedAt = Date.now(); entry.accessCount++; // Try to deserialize, fall back to raw string try { return sessionSerializer.serializer.deserialize(entry.value); } catch { return entry.value; } } async list(options = {}) { await this.initialize(); const namespace = options.namespace || 'default'; const limit = options.limit || 100; const namespaceMap = this._getNamespaceMap(namespace); const entries = Array.from(namespaceMap.values()) .filter((entry) => !entry.expiresAt || entry.expiresAt > Date.now()) .sort((a, b) => b.updatedAt - a.updatedAt) .slice(0, limit); return entries.map((entry) => ({ key: entry.key, value: this._tryParseJson(entry.value), namespace: entry.namespace, metadata: entry.metadata, createdAt: new Date(entry.createdAt), updatedAt: new Date(entry.updatedAt), accessCount: entry.accessCount, })); } async delete(key, options = {}) { await this.initialize(); const namespace = options.namespace || 'default'; const namespaceMap = this._getNamespaceMap(namespace); return namespaceMap.delete(key); } async search(pattern, options = {}) { await this.initialize(); const namespace = options.namespace || 'default'; const limit = options.limit || 50; const namespaceMap = this._getNamespaceMap(namespace); const searchLower = pattern.toLowerCase(); const results = []; for (const [key, entry] of namespaceMap.entries()) { // Skip expired entries if (entry.expiresAt && entry.expiresAt < Date.now()) { continue; } // Search in key and value if ( key.toLowerCase().includes(searchLower) || entry.value.toLowerCase().includes(searchLower) ) { results.push({ key: entry.key, value: this._tryParseJson(entry.value), namespace: entry.namespace, score: entry.accessCount, updatedAt: new Date(entry.updatedAt), }); } if (results.length >= limit) { break; } } // Sort by score (access count) and updated time return results.sort((a, b) => { if (a.score !== b.score) return b.score - a.score; return b.updatedAt - a.updatedAt; }); } async cleanup() { await this.initialize(); let cleaned = 0; const now = Date.now(); for (const [namespace, namespaceMap] of this.data.entries()) { for (const [key, entry] of namespaceMap.entries()) { if (entry.expiresAt && entry.expiresAt <= now) { namespaceMap.delete(key); cleaned++; } } } return cleaned; } _tryParseJson(value) { try { return sessionSerializer.serializer.deserialize(value); } catch { return value; } } close() { if (this.cleanupInterval) { clearInterval(this.cleanupInterval); this.cleanupInterval = null; } this.data.clear(); this.isInitialized = false; } } export { InMemoryStore }; export default InMemoryStore;