UNPKG

delta-sync

Version:

A lightweight framework for bi-directional database synchronization with automatic version tracking and conflict resolution.

127 lines (126 loc) 4.2 kB
// core/types.ts // Synchronization status enumeration export var SyncStatus; (function (SyncStatus) { SyncStatus[SyncStatus["ERROR"] = -2] = "ERROR"; SyncStatus[SyncStatus["OFFLINE"] = -1] = "OFFLINE"; SyncStatus[SyncStatus["IDLE"] = 0] = "IDLE"; SyncStatus[SyncStatus["UPLOADING"] = 1] = "UPLOADING"; SyncStatus[SyncStatus["DOWNLOADING"] = 2] = "DOWNLOADING"; SyncStatus[SyncStatus["OPERATING"] = 3] = "OPERATING"; })(SyncStatus || (SyncStatus = {})); export class SyncView { constructor() { this.items = new Map(); this.storeIndex = new Map(); } // Add or update record upsert(item) { const key = this.getKey(item.store, item.id); this.items.set(key, item); if (!this.storeIndex.has(item.store)) { this.storeIndex.set(item.store, new Set()); } this.storeIndex.get(item.store).add(item.id); } // Batch update records upsertBatch(items) { for (const item of items) { this.upsert(item); } } // Get specific record get(store, id) { return this.items.get(this.getKey(store, id)); } // Get paginated records for specified store getByStore(store, offset = 0, limit = 100) { const storeItems = this.storeIndex.get(store); if (!storeItems) return []; return Array.from(storeItems) .slice(offset, offset + limit) .map(id => this.items.get(this.getKey(store, id))) .filter(item => item !== undefined); } // Get all store names getStores() { return Array.from(this.storeIndex.keys()); } // Compare differences between two views static diffViews(local, remote) { const toDownload = []; const toUpload = []; const allKeys = new Set([...local.items.keys(), ...remote.items.keys()]); for (const key of allKeys) { const localItem = local.items.get(key); const remoteItem = remote.items.get(key); if (!localItem && remoteItem) { // 本地没有,远程有 toDownload.push(remoteItem); } else if (localItem && !remoteItem) { // 本地有,远程没有 toUpload.push(localItem); } else if (localItem && remoteItem) { // 两边都有,需要比较版本和删除状态 if (localItem._ver !== remoteItem._ver || localItem.deleted !== remoteItem.deleted) { if (localItem._ver > remoteItem._ver) { toUpload.push(localItem); } else if (localItem._ver < remoteItem._ver) { toDownload.push(remoteItem); } else { // 版本相同但删除状态不同时,以远程为准 toDownload.push(remoteItem); } } } } return { toDownload, toUpload }; } // Generate composite key getKey(store, id) { return `${store}:${id}`; } // Delete record delete(store, id) { const key = this.getKey(store, id); this.items.delete(key); this.storeIndex.get(store)?.delete(id); } // Get total storage size size() { return this.items.size; } // Get record count for specific store storeSize(store) { return this.storeIndex.get(store)?.size || 0; } // Clear all data clear() { this.items.clear(); this.storeIndex.clear(); } countByStore(store, includeDeleted = false) { const storeItems = this.storeIndex.get(store); if (!storeItems) { return 0; } if (includeDeleted) { return storeItems.size; } else { let activeCount = 0; for (const id of storeItems) { const item = this.get(store, id); if (item && !item.deleted) { activeCount++; } } return activeCount; } } }