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.

310 lines 23.3 kB
/** * Real-time Sync System * Uses CRDT.merge() via BroadcastChannel with commutative monoid operations */ import { signal } from '../core/signal'; import { gCounter, orSet, lwwRegister } from '../crdt'; // Real-time sync manager using commutative monoid operations export class RealtimeSync { constructor(config) { this.config = config; this.heartbeatTimer = null; this.nodeId = config.nodeId; this.channel = new BroadcastChannel(config.channelName); this.vectorClock = { [this.nodeId]: 0 }; this.crdts = new Map(); this.operationLog = signal([]); this.connectionState = signal({ isConnected: false, connectedNodes: new Set(), lastHeartbeat: Date.now(), messageQueue: [], syncErrors: [], }); this.setupChannel(); this.startHeartbeat(); this.announceJoin(); } // Setup BroadcastChannel with commutative monoid message handling setupChannel() { this.channel.addEventListener('message', (event) => { const message = event.data; // Ignore messages from self if (message.nodeId === this.nodeId) return; this.handleMessage(message); }); // Update connection state this.connectionState._set({ ...this.connectionState.value(), isConnected: true, }); } // Handle incoming sync messages using commutative operations handleMessage(message) { try { switch (message.type) { case 'operation': this.handleOperation(message); break; case 'state': this.handleStateSync(message); break; case 'heartbeat': this.handleHeartbeat(message); break; case 'join': this.handleNodeJoin(message); break; case 'leave': this.handleNodeLeave(message); break; } // Update vector clock (commutative monoid operation) this.mergeVectorClock(message.vectorClock); // Log operation this.operationLog._set([...this.operationLog.value(), message]); } catch (error) { this.handleSyncError(error, message); } } // Handle CRDT operation with commutative merge handleOperation(message) { const { data: operation } = message; const crdt = this.crdts.get(operation.crdtId); if (!crdt) { console.warn(`CRDT ${operation.crdtId} not found for operation`, operation); return; } // Apply operation using CRDT's commutative merge try { // CRDTs guarantee commutativity: merge(a, b) = merge(b, a) const updatedCRDT = crdt; // Simplified - no applyOperation method this.crdts.set(operation.crdtId, updatedCRDT); // Notify subscribers this.notifySubscribers(operation.crdtId, updatedCRDT.value()); } catch (error) { console.error('Failed to apply CRDT operation:', error); this.requestStateSync(operation.crdtId); } } // Handle full state synchronization handleStateSync(message) { const { crdtId, state } = message.data; const localCRDT = this.crdts.get(crdtId); if (!localCRDT) return; try { // Merge states using commutative monoid operation localCRDT.merge(state); this.crdts.set(crdtId, localCRDT); // Notify subscribers this.notifySubscribers(crdtId, localCRDT.value()); } catch (error) { console.error('Failed to merge CRDT state:', error); } } // Handle node heartbeat handleHeartbeat(message) { const state = this.connectionState.value(); state.connectedNodes.add(message.nodeId); state.lastHeartbeat = Date.now(); this.connectionState._set({ ...state }); } // Handle node join handleNodeJoin(message) { const state = this.connectionState.value(); state.connectedNodes.add(message.nodeId); this.connectionState._set({ ...state }); // Send current state to new node this.sendStateToNode(message.nodeId); } // Handle node leave handleNodeLeave(message) { const state = this.connectionState.value(); state.connectedNodes.delete(message.nodeId); this.connectionState._set({ ...state }); } // Merge vector clocks (commutative monoid operation) mergeVectorClock(remoteClock) { // Vector clock merge is commutative: max(local, remote) for each node Object.entries(remoteClock).forEach(([nodeId, timestamp]) => { this.vectorClock[nodeId] = Math.max(this.vectorClock[nodeId] || 0, timestamp); }); } // Increment local vector clock incrementVectorClock() { this.vectorClock[this.nodeId] = (this.vectorClock[this.nodeId] || 0) + 1; } // Send message via BroadcastChannel sendMessage(type, data) { this.incrementVectorClock(); const message = { type, nodeId: this.nodeId, timestamp: Date.now(), data, operationId: this.generateOperationId(), vectorClock: { ...this.vectorClock }, }; this.channel.postMessage(message); } // Generate unique operation ID generateOperationId() { return `${this.nodeId}-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`; } // Start heartbeat mechanism startHeartbeat() { const interval = this.config.heartbeatInterval || 5000; this.heartbeatTimer = window.setInterval(() => { this.sendMessage('heartbeat', { connectedNodes: Array.from(this.connectionState.value().connectedNodes), }); }, interval); } // Announce node join announceJoin() { this.sendMessage('join', { capabilities: ['crdt-sync', 'vector-clock', 'broadcast-channel'], }); } // Send current state to specific node sendStateToNode(targetNodeId) { this.crdts.forEach((crdt, crdtId) => { this.sendMessage('state', { crdtId, state: crdt.toJSON(), targetNode: targetNodeId, }); }); } // Request state sync for specific CRDT requestStateSync(crdtId) { this.sendMessage('state', { crdtId, request: true, }); } // Handle sync errors handleSyncError(error, message) { const state = this.connectionState.value(); state.syncErrors.push(error); this.connectionState._set({ ...state }); console.error('Sync error:', error, message); } // Notify CRDT subscribers notifySubscribers(crdtId, value) { // Emit change event for reactive systems const event = new CustomEvent('crdt-change', { detail: { crdtId, value }, }); window.dispatchEvent(event); } // Public API // Register CRDT for synchronization registerCRDT(id, crdt) { this.crdts.set(id, crdt); // Subscribe to CRDT changes if ('subscribe' in crdt) { crdt.subscribe((value) => { // Broadcast operation to other nodes this.sendMessage('operation', { crdtId: id, operation: { type: 'update', value }, // Simplified - no getLastOperation value, }); }); } } // Unregister CRDT unregisterCRDT(id) { this.crdts.delete(id); } // Get CRDT by ID getCRDT(id) { return this.crdts.get(id); } // Get connection state signal getConnectionState() { return this.connectionState; } // Get operation log signal getOperationLog() { return this.operationLog; } // Force sync with all nodes forcSync() { this.crdts.forEach((crdt, crdtId) => { this.sendMessage('state', { crdtId, state: crdt.toJSON(), force: true, }); }); } // Disconnect and cleanup disconnect() { this.sendMessage('leave', { reason: 'manual-disconnect', }); if (this.heartbeatTimer) { clearInterval(this.heartbeatTimer); this.heartbeatTimer = null; } this.channel.close(); this.connectionState._set({ ...this.connectionState.value(), isConnected: false, connectedNodes: new Set(), }); } } // Factory functions for common sync patterns // Create synced counter export const createSyncedCounter = (sync, id, nodeId, _initialValue = 0) => { const counter = gCounter(nodeId); sync.registerCRDT(id, counter); const counterSignal = signal(counter.value()); // Listen for changes window.addEventListener('crdt-change', (e) => { if (e.detail.crdtId === id) { counterSignal._set(e.detail.value); } }); return counterSignal; }; // Create synced set export const createSyncedSet = (sync, id, nodeId, initialItems = []) => { const set = orSet(nodeId); initialItems.forEach((item) => set.add(item)); sync.registerCRDT(id, set); const setSignal = signal(set.value()); // Listen for changes window.addEventListener('crdt-change', (e) => { if (e.detail.crdtId === id) { setSignal._set(e.detail.value); } }); return setSignal; }; // Create synced register export const createSyncedRegister = (sync, id, nodeId, initialValue) => { const register = lwwRegister(nodeId, initialValue); sync.registerCRDT(id, register); const registerSignal = signal(register.value()); // Listen for changes window.addEventListener('crdt-change', (e) => { if (e.detail.crdtId === id) { registerSignal._set(e.detail.value); } }); return registerSignal; }; // Create sync manager export const createRealtimeSync = (config) => { return new RealtimeSync(config); }; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhbHRpbWUtc3luYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9leHRlbnNpb25zL3JlYWx0aW1lLXN5bmMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUE7OztHQUdHO0FBRUgsT0FBTyxFQUFVLE1BQU0sRUFBRSxNQUFNLGdCQUFnQixDQUFDO0FBRWhELE9BQU8sRUFBRSxRQUFRLEVBQUUsS0FBSyxFQUFFLFdBQVcsRUFBRSxNQUFNLFNBQVMsQ0FBQztBQXVDdkQsNkRBQTZEO0FBQzdELE1BQU0sT0FBTyxZQUFZO0lBU3ZCLFlBQW9CLE1BQWtCO1FBQWxCLFdBQU0sR0FBTixNQUFNLENBQVk7UUFGOUIsbUJBQWMsR0FBa0IsSUFBSSxDQUFDO1FBRzNDLElBQUksQ0FBQyxNQUFNLEdBQUcsTUFBTSxDQUFDLE1BQU0sQ0FBQztRQUM1QixJQUFJLENBQUMsT0FBTyxHQUFHLElBQUksZ0JBQWdCLENBQUMsTUFBTSxDQUFDLFdBQVcsQ0FBQyxDQUFDO1FBQ3hELElBQUksQ0FBQyxXQUFXLEdBQUcsRUFBRSxDQUFDLElBQUksQ0FBQyxNQUFNLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQztRQUN4QyxJQUFJLENBQUMsS0FBSyxHQUFHLElBQUksR0FBRyxFQUFFLENBQUM7UUFDdkIsSUFBSSxDQUFDLFlBQVksR0FBRyxNQUFNLENBQUMsRUFBRSxDQUFDLENBQUM7UUFFL0IsSUFBSSxDQUFDLGVBQWUsR0FBRyxNQUFNLENBQUM7WUFDNUIsV0FBVyxFQUFFLEtBQUs7WUFDbEIsY0FBYyxFQUFFLElBQUksR0FBRyxFQUFFO1lBQ3pCLGFBQWEsRUFBRSxJQUFJLENBQUMsR0FBRyxFQUFFO1lBQ3pCLFlBQVksRUFBRSxFQUFFO1lBQ2hCLFVBQVUsRUFBRSxFQUFFO1NBQ2YsQ0FBQyxDQUFDO1FBRUgsSUFBSSxDQUFDLFlBQVksRUFBRSxDQUFDO1FBQ3BCLElBQUksQ0FBQyxjQUFjLEVBQUUsQ0FBQztRQUN0QixJQUFJLENBQUMs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