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
JavaScript
/**
* 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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