delta-sync
Version:
A lightweight framework for bi-directional database synchronization with automatic version tracking and conflict resolution.
118 lines (117 loc) • 3.81 kB
JavaScript
// 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 localKeys = new Set(local.items.keys());
const remoteKeys = new Set(remote.items.keys());
// Keys only in local (need to upload)
for (const key of localKeys) {
if (!remoteKeys.has(key)) {
toUpload.push(local.items.get(key));
}
}
// Keys only in remote (need to download)
for (const key of remoteKeys) {
if (!localKeys.has(key)) {
toDownload.push(remote.items.get(key));
}
}
// Keys in both (need version comparison)
for (const key of localKeys) {
if (remoteKeys.has(key)) {
const localItem = local.items.get(key);
const remoteItem = remote.items.get(key);
if (localItem._ver > remoteItem._ver) {
toUpload.push(localItem);
}
else if (localItem._ver < remoteItem._ver) {
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();
}
// Serialize view data (for persistence)
serialize() {
return JSON.stringify(Array.from(this.items.values()));
}
// Deserialize from data (for persistence)
static deserialize(data) {
const view = new SyncView();
const items = JSON.parse(data);
view.upsertBatch(items);
return view;
}
}