import type { SessionCapabilities } from '../../domain/systemManager/types'; /** Internal entry: capabilities plus computed expiry timestamp. */ interface StoreEntry { capabilities: SessionCapabilities; expiresAt: number; } type Listener = () => void; const capabilitiesBySessionId = new Map(); const listenersBySessionId = new Map>(); function isExpired(entry: StoreEntry): boolean { return Date.now() > entry.expiresAt; } function normalizeCapabilities(capabilities: SessionCapabilities): SessionCapabilities { return { ...capabilities, hasSs: capabilities.hasSs === true, hasNetstat: capabilities.hasNetstat === true, hasLsof: capabilities.hasLsof === true, hasSystemctl: capabilities.hasSystemctl === true, }; } function notifySession(sessionId: string) { listenersBySessionId.get(sessionId)?.forEach((listener) => listener()); } export const sessionCapabilitiesStore = { get(sessionId: string): SessionCapabilities | undefined { const entry = capabilitiesBySessionId.get(sessionId); if (!entry) return undefined; if (isExpired(entry)) { capabilitiesBySessionId.delete(sessionId); notifySession(sessionId); return undefined; } return normalizeCapabilities(entry.capabilities); }, set(sessionId: string, capabilities: SessionCapabilities, ttlMs: number) { const entry: StoreEntry = { capabilities: { ...normalizeCapabilities(capabilities), probedAt: Date.now(), }, expiresAt: Date.now() + ttlMs, }; capabilitiesBySessionId.set(sessionId, entry); notifySession(sessionId); }, delete(sessionId: string) { if (!capabilitiesBySessionId.delete(sessionId)) return; notifySession(sessionId); listenersBySessionId.delete(sessionId); }, /** Drop cached capabilities for sessions that no longer exist. */ prune(liveSessionIds: ReadonlySet) { for (const sessionId of capabilitiesBySessionId.keys()) { if (!liveSessionIds.has(sessionId)) { capabilitiesBySessionId.delete(sessionId); listenersBySessionId.delete(sessionId); } } }, subscribe(sessionId: string, listener: Listener): () => void { let set = listenersBySessionId.get(sessionId); if (!set) { set = new Set(); listenersBySessionId.set(sessionId, set); } set.add(listener); return () => { set?.delete(listener); if (set && set.size === 0) { listenersBySessionId.delete(sessionId); } }; }, };