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.
292 lines • 26.9 kB
JavaScript
/**
* Event Sourcing with CRDTs
* Complete state reconstruction with event replay, snapshots, and compaction
*/
import { gCounter, orSet } from '../crdt';
import { createStreamingSignal } from './coalgebra';
// In-memory event store implementation
export class MemoryEventStore {
constructor() {
this.events = [];
this.snapshots = [];
}
async append(events) {
this.events.push(...events);
this.events.sort((a, b) => a.version - b.version);
}
async getEvents(fromVersion = 0, toVersion = Infinity) {
return this.events.filter((e) => e.version >= fromVersion && e.version <= toVersion);
}
async getSnapshot(beforeVersion = Infinity) {
const validSnapshots = this.snapshots.filter((s) => s.version < beforeVersion);
return validSnapshots.length > 0
? validSnapshots[validSnapshots.length - 1]
: null;
}
async saveSnapshot(snapshot) {
this.snapshots.push(snapshot);
this.snapshots.sort((a, b) => a.version - b.version);
}
async compact(beforeVersion) {
this.events = this.events.filter((e) => e.version >= beforeVersion);
this.snapshots = this.snapshots.filter((s) => s.version >= beforeVersion);
}
}
// IndexedDB event store for browser persistence
export class IndexedDBEventStore {
constructor(dbName) {
this.db = null;
this.dbName = dbName;
}
async getDB() {
if (this.db)
return this.db;
return new Promise((resolve, reject) => {
const request = indexedDB.open(this.dbName, 1);
request.onerror = () => reject(request.error);
request.onsuccess = () => {
this.db = request.result;
resolve(this.db);
};
request.onupgradeneeded = () => {
const db = request.result;
if (!db.objectStoreNames.contains('events')) {
const eventStore = db.createObjectStore('events', { keyPath: 'id' });
eventStore.createIndex('version', 'version', { unique: false });
eventStore.createIndex('timestamp', 'timestamp', { unique: false });
}
if (!db.objectStoreNames.contains('snapshots')) {
const snapshotStore = db.createObjectStore('snapshots', {
keyPath: 'version',
});
snapshotStore.createIndex('timestamp', 'timestamp', {
unique: false,
});
}
};
});
}
async append(events) {
const db = await this.getDB();
const transaction = db.transaction(['events'], 'readwrite');
const store = transaction.objectStore('events');
for (const event of events) {
store.add(event);
}
return new Promise((resolve, reject) => {
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
});
}
async getEvents(fromVersion = 0, toVersion = Infinity) {
const db = await this.getDB();
const transaction = db.transaction(['events'], 'readonly');
const store = transaction.objectStore('events');
const index = store.index('version');
const range = IDBKeyRange.bound(fromVersion, toVersion);
const request = index.getAll(range);
return new Promise((resolve, reject) => {
request.onsuccess = () => resolve(request.result);
request.onerror = () => reject(request.error);
});
}
async getSnapshot(beforeVersion = Infinity) {
const db = await this.getDB();
const transaction = db.transaction(['snapshots'], 'readonly');
const store = transaction.objectStore('snapshots');
const range = IDBKeyRange.upperBound(beforeVersion, true);
const request = store.openCursor(range, 'prev');
return new Promise((resolve, reject) => {
request.onsuccess = () => {
const cursor = request.result;
resolve(cursor ? cursor.value : null);
};
request.onerror = () => reject(request.error);
});
}
async saveSnapshot(snapshot) {
const db = await this.getDB();
const transaction = db.transaction(['snapshots'], 'readwrite');
const store = transaction.objectStore('snapshots');
store.put(snapshot);
return new Promise((resolve, reject) => {
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
});
}
async compact(beforeVersion) {
const db = await this.getDB();
const transaction = db.transaction(['events', 'snapshots'], 'readwrite');
// Remove old events
const eventStore = transaction.objectStore('events');
const eventIndex = eventStore.index('version');
const eventRange = IDBKeyRange.upperBound(beforeVersion, true);
const eventRequest = eventIndex.openCursor(eventRange);
eventRequest.onsuccess = () => {
const cursor = eventRequest.result;
if (cursor) {
cursor.delete();
cursor.continue();
}
};
// Remove old snapshots
const snapshotStore = transaction.objectStore('snapshots');
const snapshotRange = IDBKeyRange.upperBound(beforeVersion, true);
const snapshotRequest = snapshotStore.openCursor(snapshotRange);
snapshotRequest.onsuccess = () => {
const cursor = snapshotRequest.result;
if (cursor) {
cursor.delete();
cursor.continue();
}
};
return new Promise((resolve, reject) => {
transaction.oncomplete = () => resolve();
transaction.onerror = () => reject(transaction.error);
});
}
}
// Create event sourced CRDT wrapper
export const createEventSourcedCRDT = (baseCRDT, config, nodeId = Math.random().toString(36)) => {
let version = 0;
let eventCount = 0;
const eventStream = createStreamingSignal(null);
// Track pending events for batching
const pendingEvents = [];
let flushTimeout = null;
const flushEvents = async () => {
if (pendingEvents.length === 0)
return;
const eventsToFlush = [...pendingEvents];
pendingEvents.length = 0;
await config.eventStore.append(eventsToFlush);
// Check if we need to create a snapshot
if (eventCount % config.snapshotInterval === 0) {
await createSnapshot();
}
// Check if we need to compact
await checkCompaction();
};
const createSnapshot = async () => {
const snapshot = {
timestamp: Date.now(),
version,
state: baseCRDT.value(),
eventCount,
checksum: generateChecksum(baseCRDT.value()),
};
await config.eventStore.saveSnapshot(snapshot);
return snapshot;
};
const checkCompaction = async () => {
const { compactionStrategy, maxEvents, maxAge } = config;
switch (compactionStrategy) {
case 'event-count':
if (maxEvents && eventCount > maxEvents) {
const compactBefore = version - Math.floor(maxEvents / 2);
await config.eventStore.compact(compactBefore);
}
break;
case 'time-based':
if (maxAge) {
const cutoffTime = Date.now() - maxAge;
const events = await config.eventStore.getEvents();
const cutoffVersion = events.find((e) => e.timestamp > cutoffTime)?.version || version;
await config.eventStore.compact(cutoffVersion);
}
break;
case 'sliding-window':
if (maxEvents && eventCount > maxEvents) {
await config.eventStore.compact(version - maxEvents);
}
break;
}
};
const generateChecksum = (state) => {
return btoa(JSON.stringify(state)).slice(0, 16);
};
const addEvent = (event) => {
const fullEvent = {
...event,
id: `${nodeId}-${Date.now()}-${Math.random().toString(36)}`,
version: ++version,
nodeId,
timestamp: Date.now(),
};
pendingEvents.push(fullEvent);
eventCount++;
// Emit event immediately
eventStream._set(fullEvent);
// Batch flush events
if (flushTimeout)
clearTimeout(flushTimeout);
flushTimeout = (typeof window !== 'undefined' ? window.setTimeout : setTimeout)(flushEvents, 10);
};
const eventSourcedCRDT = {
value: baseCRDT.value,
merge: (other) => {
const merged = baseCRDT.merge(other);
addEvent({ type: 'merge', data: other.value() });
return merged;
},
events: () => eventStream,
replayFrom: async (timestamp) => {
// Get the latest snapshot before the timestamp
const snapshot = await config.eventStore.getSnapshot();
const state = snapshot ? snapshot.state : baseCRDT.value();
// Get events from snapshot version or beginning
const fromVersion = snapshot ? snapshot.version : 0;
const events = await config.eventStore.getEvents(fromVersion);
// Filter events by timestamp and replay
events.filter((e) => e.timestamp >= timestamp);
// This is a simplified replay - in practice, you'd need to apply events to recreate state
// For now, we return the current state
return state;
},
snapshot: createSnapshot,
compact: async () => {
await createSnapshot();
await config.eventStore.compact(version - config.snapshotInterval);
},
getEventCount: () => eventCount,
getVersion: () => version,
clone: () => {
return createEventSourcedCRDT(baseCRDT, config, nodeId);
},
toJSON: () => {
return {
value: baseCRDT.value(),
version,
eventCount,
nodeId,
};
},
fromJSON: (data) => {
return createEventSourcedCRDT(baseCRDT, config, data.nodeId || nodeId);
},
};
// Wrap original CRDT methods to emit events
const originalMethods = Object.getOwnPropertyNames(Object.getPrototypeOf(baseCRDT));
originalMethods.forEach((methodName) => {
if (typeof baseCRDT[methodName] === 'function' &&
methodName !== 'value' &&
methodName !== 'merge') {
const originalMethod = baseCRDT[methodName];
eventSourcedCRDT[methodName] = (...args) => {
const result = originalMethod.apply(baseCRDT, args);
addEvent({ type: methodName, data: args });
return result;
};
}
});
return eventSourcedCRDT;
};
// Convenience functions for common CRDT types
export const eventSourcedORSet = (nodeId, config) => {
return createEventSourcedCRDT(orSet(nodeId), config, nodeId);
};
export const eventSourcedGCounter = (nodeId, config) => {
return createEventSourcedCRDT(gCounter(nodeId), config, nodeId);
};
export const indexedDBEventStore = (dbName) => new IndexedDBEventStore(dbName);
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZXZlbnQtc291cmNpbmcuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi8uLi9zcmMvc3RyZWFtaW5nL2V2ZW50LXNvdXJjaW5nLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBOzs7R0FHRztBQUVILE9BQU8sRUFBUSxRQUFRLEVBQUUsS0FBSyxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBRWhELE9BQU8sRUFBRSxxQkFBcUIsRUFBbUIsTUFBTSxhQUFhLENBQUM7QUEyRHJFLHVDQUF1QztBQUN2QyxNQUFNLE9BQU8sZ0JBQWdCO0lBQTdCO1FBQ1UsV0FBTSxHQUFZLEVBQUUsQ0FBQztRQUNyQixjQUFTLEdBQXdCLEVBQUUsQ0FBQztJQWlDOUMsQ0FBQztJQS9CQyxLQUFLLENBQUMsTUFBTSxDQUFDLE1BQWU7UUFDMUIsSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsR0FBRyxNQUFNLENBQUMsQ0FBQztRQUM1QixJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLEdBQUcsQ0FBQyxDQUFDLE9BQU8sQ0FBQyxDQUFDO0lBQ3BELENBQUM7SUFFRCxLQUFLLENBQUMsU0FBUyxDQUFDLFdBQVcsR0FBRyxDQUFDLEVBQUUsU0FBUyxHQUFHLFFBQVE7UUFDbkQsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLE1BQU0sQ0FDdkIsQ0FBQyxDQUFDLEVBQUUsRUFBRSxDQUFDLENBQUMsQ0FBQyxPQUFPLElBQUksV0FBVyxJQUFJLENBQUMsQ0FBQyxPQUFPLElBQUksU0FBUyxDQUMxRCxDQUFDO0lBQ0osQ0FBQztJQUVELEtBQUssQ0FBQyxXQUFXLENBQ2YsYUFBYSxHQUFHLFFBQVE7UUFFeEIsTUFBTSxjQUFjLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxNQUFNLENBQzFDLENBQUMsQ0FBQyxFQUFFLEVBQUUsQ0FBQyxDQUFDLENBQUMsT0FBTyxHQUFH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