UNPKG

resig.js

Version:

Universal reactive signal library with complete platform features: signals, animations, CRDTs, scheduling, DOM integration. Works identically across React, SolidJS, Svelte, Vue, and Qwik.

306 lines 24.6 kB
/** * WebRTC Streaming Signals * Real-time collaboration with automatic reconnection and peer discovery */ import { createStreamingSignal } from './coalgebra'; // WebRTC peer connection wrapper export class WebRTCPeer { constructor(peerId, config, signaling) { this.peerId = peerId; this.config = config; this.signaling = signaling; this.dataChannel = null; this.reconnectAttempts = 0; this.heartbeatTimer = null; this.messageHandlers = new Set(); this.connection = new RTCPeerConnection({ iceServers: config.iceServers || [ { urls: 'stun:stun.l.google.com:19302' }, ], }); this.setupConnection(); } setupConnection() { // Handle ICE candidates this.connection.onicecandidate = (event) => { if (event.candidate) { this.signaling.iceCandidate(this.peerId, event.candidate.toJSON()); } }; // Handle connection state changes this.connection.onconnectionstatechange = () => { const state = this.connection.connectionState; if (state === 'failed' || state === 'disconnected') { this.handleReconnection(); } }; // Handle incoming data channels this.connection.ondatachannel = (event) => { this.setupDataChannel(event.channel); }; } setupDataChannel(channel) { this.dataChannel = channel; channel.onopen = () => { this.startHeartbeat(); }; channel.onclose = () => { this.stopHeartbeat(); }; channel.onmessage = (event) => { try { const data = JSON.parse(event.data); if (data.type === 'heartbeat') { // Respond to heartbeat this.send({ type: 'heartbeat-response', timestamp: Date.now() }); } else { this.messageHandlers.forEach((handler) => handler(data)); } } catch (error) { console.error('Failed to parse WebRTC message:', error); } }; channel.onerror = (error) => { console.error('WebRTC data channel error:', error); }; } startHeartbeat() { if (this.heartbeatTimer) return; const interval = this.config.heartbeatInterval || 30000; // 30 seconds this.heartbeatTimer = window.setInterval(() => { this.send({ type: 'heartbeat', timestamp: Date.now() }); }, interval); } stopHeartbeat() { if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; } } async handleReconnection() { const maxAttempts = this.config.reconnectAttempts || 5; const delay = this.config.reconnectDelay || 1000; if (this.reconnectAttempts >= maxAttempts) { console.error(`Failed to reconnect to peer ${this.peerId} after ${maxAttempts} attempts`); return; } this.reconnectAttempts++; console.log(`Attempting to reconnect to peer ${this.peerId} (attempt ${this.reconnectAttempts})`); await new Promise((resolve) => setTimeout(resolve, delay * this.reconnectAttempts)); try { await this.createOffer(); } catch (error) { console.error('Reconnection failed:', error); this.handleReconnection(); } } async createOffer() { // Create data channel for outgoing connections if (!this.dataChannel) { this.dataChannel = this.connection.createDataChannel('data', this.config.dataChannelOptions); this.setupDataChannel(this.dataChannel); } const offer = await this.connection.createOffer(); await this.connection.setLocalDescription(offer); await this.signaling.offer(this.peerId, offer); } async handleOffer(offer) { await this.connection.setRemoteDescription(offer); const answer = await this.connection.createAnswer(); await this.connection.setLocalDescription(answer); await this.signaling.answer(this.peerId, answer); } async handleAnswer(answer) { await this.connection.setRemoteDescription(answer); this.reconnectAttempts = 0; // Reset on successful connection } async handleIceCandidate(candidate) { await this.connection.addIceCandidate(new RTCIceCandidate(candidate)); } send(data) { if (this.dataChannel && this.dataChannel.readyState === 'open') { try { this.dataChannel.send(JSON.stringify(data)); return true; } catch (error) { console.error('Failed to send WebRTC message:', error); return false; } } return false; } onMessage(handler) { this.messageHandlers.add(handler); return () => this.messageHandlers.delete(handler); } getConnectionState() { return this.connection.connectionState; } close() { this.stopHeartbeat(); if (this.dataChannel) { this.dataChannel.close(); } this.connection.close(); } } // Simple signaling implementation using BroadcastChannel export class BroadcastChannelSignaling { constructor(channelName = 'webrtc-signaling') { this.offerHandlers = new Set(); this.answerHandlers = new Set(); this.iceCandidateHandlers = new Set(); this.channel = new BroadcastChannel(channelName); this.channel.onmessage = this.handleMessage.bind(this); } handleMessage(event) { const { type, peerId, data } = event.data; switch (type) { case 'offer': this.offerHandlers.forEach((handler) => handler(peerId, data)); break; case 'answer': this.answerHandlers.forEach((handler) => handler(peerId, data)); break; case 'ice-candidate': this.iceCandidateHandlers.forEach((handler) => handler(peerId, data)); break; } } async offer(peerId, offer) { this.channel.postMessage({ type: 'offer', peerId, data: offer }); } async answer(peerId, answer) { this.channel.postMessage({ type: 'answer', peerId, data: answer }); } async iceCandidate(peerId, candidate) { this.channel.postMessage({ type: 'ice-candidate', peerId, data: candidate, }); } onOffer(callback) { this.offerHandlers.add(callback); } onAnswer(callback) { this.answerHandlers.add(callback); } onIceCandidate(callback) { this.iceCandidateHandlers.add(callback); } close() { this.channel.close(); } } // Create WebRTC streaming signal export const createWebRTCStream = (peerId, config = {}, signaling) => { const stream = createStreamingSignal(null); const sig = signaling || new BroadcastChannelSignaling(); const peer = new WebRTCPeer(peerId, config, sig); // Handle incoming messages peer.onMessage((data) => { if (typeof data === 'object' && data !== null && 'type' in data && data.type === 'stream-data' && 'value' in data) { stream._set(data.value); } }); // Setup signaling handlers sig.onOffer(async (fromPeerId, offer) => { if (fromPeerId === peerId) { await peer.handleOffer(offer); } }); sig.onAnswer(async (fromPeerId, answer) => { if (fromPeerId === peerId) { await peer.handleAnswer(answer); } }); sig.onIceCandidate(async (fromPeerId, candidate) => { if (fromPeerId === peerId) { await peer.handleIceCandidate(candidate); } }); // Override _set to broadcast changes const originalSet = stream ._set; stream._set = (value) => { originalSet(value); peer.send({ type: 'stream-data', value, timestamp: Date.now() }); }; // Add peer management methods stream.peer = peer; stream.connect = () => peer.createOffer(); stream.disconnect = () => peer.close(); stream.getConnectionState = () => peer.getConnectionState(); return stream; }; // Multi-peer WebRTC stream for broadcasting to multiple peers export const createWebRTCBroadcast = (peerIds, config = {}, signaling) => { const stream = createStreamingSignal(null); const sig = signaling || new BroadcastChannelSignaling(); const peers = new Map(); // Create peers for each peer ID peerIds.forEach((peerId) => { const peer = new WebRTCPeer(peerId, config, sig); peers.set(peerId, peer); // Handle incoming messages from any peer peer.onMessage((data) => { if (typeof data === 'object' && data !== null && 'type' in data && data.type === 'stream-data' && 'value' in data) { stream._set(data.value); } }); }); // Setup signaling for all peers sig.onOffer(async (fromPeerId, offer) => { const peer = peers.get(fromPeerId); if (peer) { await peer.handleOffer(offer); } }); sig.onAnswer(async (fromPeerId, answer) => { const peer = peers.get(fromPeerId); if (peer) { await peer.handleAnswer(answer); } }); sig.onIceCandidate(async (fromPeerId, candidate) => { const peer = peers.get(fromPeerId); if (peer) { await peer.handleIceCandidate(candidate); } }); // Override _set to broadcast to all peers const originalSet = stream ._set; stream._set = (value) => { originalSet(value); const message = { type: 'stream-data', value, timestamp: Date.now() }; peers.forEach((peer) => peer.send(message)); }; // Add broadcast management methods stream.peers = peers; stream.connectAll = async () => { const peerArray = Array.from(peers.values()); for (const peer of peerArray) { await peer.createOffer(); } }; stream.disconnectAll = () => { peers.forEach((peer) => peer.close()); }; return stream; }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoid2VicnRjLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vLi4vc3JjL3N0cmVhbWluZy93ZWJydGMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxFQUFFLHFCQUFxQixFQUFtQixNQUFNLGFBQWEsQ0FBQztBQW1DckUsaUNBQWlDO0FBQ2pDLE1BQU0sT0FBTyxVQUFVO0lBT3JCLFlBQ1MsTUFBYyxFQUNiLE1BQTBCLEVBQzFCLFNBQXdCO1FBRnpCLFdBQU0sR0FBTixNQUFNLENBQVE7UUFDYixXQUFNLEdBQU4sTUFBTSxDQUFvQjtRQUMxQixjQUFTLEdBQVQsU0FBUyxDQUFlO1FBUjFCLGdCQUFXLEdBQTBCLElBQUksQ0FBQztRQUMxQyxzQkFBaUIsR0FBRyxDQUFDLENBQUM7UUFDdEIsbUJBQWMsR0FBa0IsSUFBSSxDQUFDO1FBQ3JDLG9CQUFlLEdBQUcsSUFBSSxHQUFHLEVBQTJCLENBQUM7UUFPM0QsSUFBSSxDQUFDLFVBQVUsR0FBRyxJQUFJLGlCQUFpQixDQUFDO1lBQ3RDLFVBQVUsRUFBRSxNQUFNLENBQUMsVUFBVSxJQUFJO2dCQUMvQixFQUFFLElBQUksRUFBRSw4QkFBOEIsRUFBRTthQUN6QztTQUNGLENBQUMsQ0FBQztRQUVILElBQUksQ0FBQyxlQUFlLEVBQUUsQ0FBQztJQUN6QixDQUFDO0lBRU8sZUFBZTtRQUNyQix3QkFBd0I7UUFDeEIsSUFBSSxDQUFDLFVBQVUsQ0FBQyxjQUFjLEdBQUcsQ0FBQyxLQUFLLEVBQUUsRUFBRTtZQUN6QyxJQUFJLEtBQUssQ0FBQyxTQUFTLEVBQUUsQ0FBQztnQkFDcEIsSUFBSSxDQUFDLFNBQVMsQ0FBQyxZQUFZLENBQUMsSUFBSSxDQUFDLE1BQU0sRUFBRSxLQUFLLENBQUMsU0FBUyxDQUFDLE1BQU0sRUFBRSxDQUFDLENBQUM7WUFDckUsQ0FBQztRQUNILENBQUMsQ0FBQztRQUVGLGtDQUFrQztRQUNsQyxJQUFJLENBQUMsVUFBVSxDQUFDLHVC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