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.

318 lines 23.9 kB
"use strict"; /** * Real-time Sync System * Uses CRDT.merge() via BroadcastChannel with commutative monoid operations */ Object.defineProperty(exports, "__esModule", { value: true }); exports.createRealtimeSync = exports.createSyncedRegister = exports.createSyncedSet = exports.createSyncedCounter = exports.RealtimeSync = void 0; const signal_1 = require("../core/signal"); const crdt_1 = require("../crdt"); // Real-time sync manager using commutative monoid operations 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 = (0, signal_1.signal)([]); this.connectionState = (0, signal_1.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(), }); } } exports.RealtimeSync = RealtimeSync; // Factory functions for common sync patterns // Create synced counter const createSyncedCounter = (sync, id, nodeId, _initialValue = 0) => { const counter = (0, crdt_1.gCounter)(nodeId); sync.registerCRDT(id, counter); const counterSignal = (0, signal_1.signal)(counter.value()); // Listen for changes window.addEventListener('crdt-change', (e) => { if (e.detail.crdtId === id) { counterSignal._set(e.detail.value); } }); return counterSignal; }; exports.createSyncedCounter = createSyncedCounter; // Create synced set const createSyncedSet = (sync, id, nodeId, initialItems = []) => { const set = (0, crdt_1.orSet)(nodeId); initialItems.forEach((item) => set.add(item)); sync.registerCRDT(id, set); const setSignal = (0, signal_1.signal)(set.value()); // Listen for changes window.addEventListener('crdt-change', (e) => { if (e.detail.crdtId === id) { setSignal._set(e.detail.value); } }); return setSignal; }; exports.createSyncedSet = createSyncedSet; // Create synced register const createSyncedRegister = (sync, id, nodeId, initialValue) => { const register = (0, crdt_1.lwwRegister)(nodeId, initialValue); sync.registerCRDT(id, register); const registerSignal = (0, signal_1.signal)(register.value()); // Listen for changes window.addEventListener('crdt-change', (e) => { if (e.detail.crdtId === id) { registerSignal._set(e.detail.value); } }); return registerSignal; }; exports.createSyncedRegister = createSyncedRegister; // Create sync manager const createRealtimeSync = (config) => { return new RealtimeSync(config); }; exports.createRealtimeSync = createRealtimeSync; //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicmVhbHRpbWUtc3luYy5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uLy4uL3NyYy9leHRlbnNpb25zL3JlYWx0aW1lLXN5bmMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBOzs7R0FHRzs7O0FBRUgsMkNBQWdEO0FBRWhELGtDQUF1RDtBQXVDdkQsNkRBQTZEO0FBQzdELE1BQWEsWUFBWTtJQVN2QixZQUFvQixNQUFrQjtRQUFsQixXQUFNLEdBQU4sTUFBTSxDQUFZO1FBRjlCLG1CQUFjLEdBQWtCLElBQUksQ0FBQztRQUczQyxJQUFJLENBQUMsTUFBTSxHQUFHLE1BQU0sQ0FBQyxNQUFNLENBQUM7UUFDNUIsSUFBSSxDQUFDLE9BQU8sR0FBRyxJQUFJLGdCQUFnQixDQUFDLE1BQU0sQ0FBQyxXQUFXLENBQUMsQ0FBQztRQUN4RCxJQUFJLENBQUMsV0FBVyxHQUFHLEVBQUUsQ0FBQyxJQUFJLENBQUMsTUFBTSxDQUFDLEVBQUUsQ0FBQyxFQUFFLENBQUM7UUFDeEMsSUFBSSxDQUFDLEtBQUssR0FBRyxJQUFJLEdBQUcsRUFBRSxDQUFDO1FBQ3ZCLElBQUksQ0FBQyxZQUFZLEdBQUcsSUFBQSxlQUFNLEVBQUMsRUFBRSxDQUFDLENBQUM7UUFFL0IsSUFBSSxDQUFDLGVBQWUsR0FBRyxJQUFBLGVBQU0sRUFBQztZQUM1QixXQUFXLEVBQUUsS0FBSztZQUNsQixjQUFjLEVBQUUsSUFBSSxHQUFHLEVBQUU7WUFDekIsYUFBYSxFQUFFLElBQUksQ0FBQyxHQUFHLEVBQUU7WUFDekIsWUFBWSxFQUFFLEVBQUU7WUFDaEIsVUFBVSxFQUFFLEVBQUU7U0FDZixDQUFDLENBQUM7UUFFSCxJQUFJLENBQUMsWUFBWSxFQUFFLENBQUM7UUFDcEIsSUFBSSxDQUFDLGNBQWMsRUFBRSxDQUFDO1FBQ3RCLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztJQUN0QixDQUFDO0lBRUQsa0VBQWtFO0lBQzFELFlBQVk7UUFDbEIsSUFBSSxDQUFDLE9BQU8s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