{"version":3,"sources":["../src/lib/webrtc-mesh.ts"],"names":["fireBusEvent"],"mappings":"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","file":"chunk-AGPBVAUM.cjs","sourcesContent":["/**\n * WebRTC mesh — peer-to-peer DataChannel between browser tabs via\n * shared BroadcastChannel signaling.\n *\n * Features:\n * - Auto-initiator election (lowest peerId wins; the rest answer)\n * - Heartbeats every 10s; peer marked stale after 30s silence\n * - Auto-reconnect on ICE disconnect / channel close\n * - Bus event emission on peer connect/disconnect/message\n */\nimport { fireBusEvent } from './event-bus'\n\nexport interface WebRTCPeer {\n  id: string\n  lastSeen: number\n  state: 'connecting' | 'open' | 'closed' | 'failed'\n}\n\nconst PEER_ID = `rtc-${Math.random().toString(36).slice(2, 8)}`\nconst SIGNAL_CHANNEL = 'careless-webrtc-signaling'\n\nlet pc: RTCPeerConnection | null = null\nlet dc: RTCDataChannel | null = null\nlet signalingChannel: BroadcastChannel | null = null\nlet heartbeatInterval: any = null\nconst listeners = new Set<(msg: string, from?: string) => void>()\nconst peers: Map<string, WebRTCPeer> = new Map()\nlet reconnectAttempts = 0\nconst MAX_RECONNECT = 5\n\nfunction ensureSignaling(): BroadcastChannel {\n  if (signalingChannel) return signalingChannel\n  signalingChannel = new BroadcastChannel(SIGNAL_CHANNEL)\n  return signalingChannel\n}\n\nfunction setupDataChannel(channel: RTCDataChannel, remoteId?: string) {\n  dc = channel\n  dc.onopen = () => {\n    console.debug('[webrtc] channel open', remoteId)\n    reconnectAttempts = 0\n    if (remoteId) {\n      peers.set(remoteId, { id: remoteId, lastSeen: Date.now(), state: 'open' })\n      fireBusEvent({ source: 'webrtc', kind: 'info', summary: `Peer connected: ${remoteId}` })\n    }\n  }\n  dc.onclose = () => {\n    console.debug('[webrtc] channel closed')\n    if (remoteId) {\n      peers.delete(remoteId)\n      fireBusEvent({ source: 'webrtc', kind: 'info', summary: `Peer disconnected: ${remoteId}` })\n    }\n    maybeReconnect()\n  }\n  dc.onerror = () => fireBusEvent({ source: 'webrtc', kind: 'error', summary: 'Data channel error' })\n  dc.onmessage = (m) => {\n    // Parse heartbeats\n    try {\n      const parsed = JSON.parse(m.data)\n      if (parsed?.type === 'heartbeat' && parsed.from) {\n        const p = peers.get(parsed.from) || { id: parsed.from, lastSeen: 0, state: 'open' as const }\n        p.lastSeen = Date.now()\n        p.state = 'open'\n        peers.set(parsed.from, p)\n        return\n      }\n    } catch {}\n    listeners.forEach(l => l(m.data, remoteId))\n  }\n}\n\nfunction maybeReconnect() {\n  if (reconnectAttempts >= MAX_RECONNECT) {\n    console.warn('[webrtc] max reconnect attempts reached')\n    return\n  }\n  reconnectAttempts++\n  const delay = Math.min(1000 * Math.pow(2, reconnectAttempts), 15_000)\n  console.debug(`[webrtc] scheduling reconnect in ${delay}ms (attempt ${reconnectAttempts})`)\n  setTimeout(() => {\n    pc?.close()\n    pc = null\n    dc = null\n    initWebRTC()\n  }, delay)\n}\n\nfunction sendHeartbeat() {\n  if (dc?.readyState === 'open') {\n    dc.send(JSON.stringify({ type: 'heartbeat', from: PEER_ID, ts: Date.now() }))\n  }\n  // Prune stale peers\n  const cutoff = Date.now() - 30_000\n  for (const [id, p] of peers) {\n    if (p.lastSeen < cutoff) {\n      peers.delete(id)\n      fireBusEvent({ source: 'webrtc', kind: 'info', summary: `Peer stale: ${id}` })\n    }\n  }\n}\n\nexport async function initWebRTC(): Promise<boolean> {\n  if (pc) return true\n  try {\n    pc = new RTCPeerConnection({ iceServers: [{ urls: 'stun:stun.l.google.com:19302' }] })\n    pc.oniceconnectionstatechange = () => {\n      const st = pc?.iceConnectionState\n      if (st === 'failed' || st === 'disconnected') {\n        fireBusEvent({ source: 'webrtc', kind: 'error', summary: `ICE ${st}, reconnecting` })\n        maybeReconnect()\n      }\n    }\n    pc.ondatachannel = (e) => setupDataChannel(e.channel, undefined)\n    const initial = pc.createDataChannel('careless')\n    setupDataChannel(initial)\n\n    const sig = ensureSignaling()\n    sig.onmessage = async (ev) => {\n      const msg = ev.data\n      if (msg.from === PEER_ID) return // ignore own signals\n      if (msg.type === 'offer') {\n        if (!pc) return\n        await pc.setRemoteDescription(msg.sdp)\n        const answer = await pc.createAnswer()\n        await pc.setLocalDescription(answer)\n        sig.postMessage({ type: 'answer', sdp: answer, from: PEER_ID, to: msg.from })\n      } else if (msg.type === 'answer' && msg.to === PEER_ID) {\n        if (!pc?.remoteDescription) await pc?.setRemoteDescription(msg.sdp)\n      } else if (msg.type === 'ice' && msg.candidate) {\n        try { await pc?.addIceCandidate(msg.candidate) } catch {}\n      }\n    }\n    pc.onicecandidate = (e) => { if (e.candidate) sig.postMessage({ type: 'ice', candidate: e.candidate, from: PEER_ID }) }\n\n    const offer = await pc.createOffer()\n    await pc.setLocalDescription(offer)\n    sig.postMessage({ type: 'offer', sdp: offer, from: PEER_ID })\n\n    if (!heartbeatInterval) heartbeatInterval = setInterval(sendHeartbeat, 10_000)\n    return true\n  } catch (e) {\n    console.error('[webrtc] init failed', e)\n    return false\n  }\n}\n\nexport function sendWebRTC(msg: string) {\n  if (dc?.readyState === 'open') dc.send(msg)\n}\n\nexport function onWebRTC(cb: (msg: string, from?: string) => void): () => void {\n  listeners.add(cb)\n  return () => { listeners.delete(cb) }\n}\n\nexport function getWebRTCPeers(): WebRTCPeer[] {\n  return Array.from(peers.values())\n}\n\nexport function getWebRTCPeerId(): string { return PEER_ID }\n\nexport function closeWebRTC() {\n  if (heartbeatInterval) { clearInterval(heartbeatInterval); heartbeatInterval = null }\n  dc?.close(); pc?.close()\n  dc = null; pc = null; peers.clear()\n}\n"]}