/** * Tracks which socket.io connection ids are subscribed to each logical channel. * Keys: "patch:", "waterTank", "power", "wateringSessions", "waterSchedules" */ export class SubscriptionRegistry { constructor() { /** @type {Map>} */ this._keyToSockets = new Map(); /** @type {Map>} */ this._socketToKeys = new Map(); } static channelKey(resource, id) { if (resource === "patch") { if (id == null || id === "") return null; return `patch:${id}`; } if (resource === "waterTank") return "waterTank"; if (resource === "power") return "power"; if (resource === "wateringSessions") return "wateringSessions"; if (resource === "waterSchedules") return "waterSchedules"; return null; } subscribe(socketId, key) { if (!key) return; if (!this._keyToSockets.has(key)) this._keyToSockets.set(key, new Set()); this._keyToSockets.get(key).add(socketId); if (!this._socketToKeys.has(socketId)) this._socketToKeys.set(socketId, new Set()); this._socketToKeys.get(socketId).add(key); } unsubscribe(socketId, key) { if (!key) return; this._keyToSockets.get(key)?.delete(socketId); if (this._keyToSockets.get(key)?.size === 0) { this._keyToSockets.delete(key); } this._socketToKeys.get(socketId)?.delete(key); if (this._socketToKeys.get(socketId)?.size === 0) { this._socketToKeys.delete(socketId); } } removeSocket(socketId) { const keys = this._socketToKeys.get(socketId); if (!keys) return; for (const key of keys) { this._keyToSockets.get(key)?.delete(socketId); if (this._keyToSockets.get(key)?.size === 0) { this._keyToSockets.delete(key); } } this._socketToKeys.delete(socketId); } /** @returns {Iterable} */ getSubscriberIds(key) { return this._keyToSockets.get(key) ?? []; } }