// Subscribes to /ws/events/:id and emits per-message callbacks. // // Auto-reconnects with exponential backoff up to 30s. Returns a cleanup // fn the caller invokes (e.g. inside onUnmounted). interface WSMessage { type: string event_id: string payload: any timestamp: string } export function useEventWS(eventId: string, onMessage: (msg: WSMessage) => void) { if (import.meta.server) return () => {} const config = useRuntimeConfig() const base = (config.public.wsBase as string) || '' const url = `${base}/ws/events/${eventId}` let ws: WebSocket | null = null let attempt = 0 let stopped = false let reconnectTimer: ReturnType | null = null function connect() { if (stopped) return ws = new WebSocket(url) ws.onopen = () => { attempt = 0 } ws.onmessage = (e) => { try { const msg = JSON.parse(e.data) as WSMessage onMessage(msg) } catch { /* ignore */ } } ws.onclose = () => { if (stopped) return const backoff = Math.min(30_000, 500 * Math.pow(2, attempt++)) reconnectTimer = setTimeout(connect, backoff) } ws.onerror = () => { ws?.close() } } connect() return function stop() { stopped = true if (reconnectTimer) clearTimeout(reconnectTimer) ws?.close() } }