import type { Emitter } from 'mitt'; import { SSE_BUS_EVENT, type SseHandler, type SseMsgType, type SsePayload } from '../types/sse'; /** * Subscribes to a shared SSE event bus (sse.ts → mitt) and dispatches * customerservice payloads to msgType-keyed handlers. * * Does NOT create its own EventSource. The host bus is provided by * frontend/src/utils/sseEventBus.getSseEventBus(). */ export class SseSubscriber { private readonly handlers: Map void>> = new Map(); private readonly reconnectHandlers: Set<() => void> = new Set(); private readonly busListener: (raw: string) => void; private readonly reconnectListener: () => void; private disposed = false; constructor(private readonly bus: Emitter) { this.busListener = (raw: string) => this.handleRaw(raw); this.reconnectListener = () => this.reconnectHandlers.forEach((h) => h()); this.bus.on(SSE_BUS_EVENT, this.busListener); this.bus.on('reconnect', this.reconnectListener); } on(msgType: K, handler: SseHandler): void { if (this.disposed) return; let set = this.handlers.get(msgType); if (!set) { set = new Set(); this.handlers.set(msgType, set); } set.add(handler as (data: any) => void); } off(msgType: K, handler: SseHandler): void { this.handlers.get(msgType)?.delete(handler as (data: any) => void); } onReconnect(handler: () => void): void { if (this.disposed) return; this.reconnectHandlers.add(handler); } dispose(): void { this.bus.off(SSE_BUS_EVENT, this.busListener); this.bus.off('reconnect', this.reconnectListener); this.handlers.clear(); this.reconnectHandlers.clear(); this.disposed = true; } private handleRaw(raw: string): void { let envelope: SsePayload | null = null; try { const parsed = JSON.parse(raw); if (parsed && typeof parsed === 'object' && typeof parsed.msgType === 'string') { envelope = parsed as SsePayload; } } catch { return; } if (!envelope) return; const set = this.handlers.get(envelope.msgType); if (!set) return; set.forEach((h) => { try { h(envelope!.data); } catch (err) { console.error('[SseSubscriber] handler threw for', envelope!.msgType, err); } }); } }