// src/utils/pendingIgmpFixes.js // // In-memory store for pending IGMP-snooping / multicast-fix // confirmation cards. Identical shape to pendingHostAssigns — every // entry is stamped with a `timestamp` on insert, and a background // sweep expires un-acted cards after TTL_MS so the map never leaks. // // Entries typically hold: // { // networkId, networkName, storeNum, // requester, // from extractRequester(trigger) // summary, // { defaultSnoopOn, defaultFloodOff, deviatingOverrides.length } // timestamp, // auto-set // } // // TTL is deliberately longer than pendingHostAssigns because a // multicast change often gets a "hold on, let me check with the // store first" pause before someone actually clicks. 15 minutes is // enough for a normal handoff without the card staying live long // enough for a stale click to fire against a since-fixed network. import { logger } from './logger.js'; const TTL_MS = 15 * 60 * 1000; // expire un-acted cards after 15 min const SWEEP_INTERVAL_MS = 60 * 1000; // check once a minute const _store = new Map(); export const pendingIgmpFixes = { set(cardId, data) { _store.set(cardId, { ...data, timestamp: Date.now() }); return this; }, get(cardId) { return _store.get(cardId); }, has(cardId) { return _store.has(cardId); }, delete(cardId) { return _store.delete(cardId); }, get size() { return _store.size; }, entries() { return _store.entries(); }, }; const sweepHandle = setInterval(() => { const now = Date.now(); for (const [cardId, data] of _store.entries()) { const stamped = typeof data?.timestamp === 'number' ? data.timestamp : 0; if (now - stamped > TTL_MS) { logger('igmp:cleanup', `Expired card ${cardId} for store ${data?.storeNum || 'unknown'} network ${data?.networkName || 'unknown'}`); _store.delete(cardId); } } }, SWEEP_INTERVAL_MS); sweepHandle.unref?.();