cry-synced-db-client 0.1.29 → 0.1.30

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -39,9 +39,8 @@ export declare class DexieDb extends Dexie implements I_DexieDb {
39
39
  deleteSyncMeta(collection: string): Promise<void>;
40
40
  getTenant(): string;
41
41
  getInstanceId(): string;
42
- getNewerThan<T extends LocalDbEntity>(collection: string, timestamp: any): Promise<T[]>;
42
+ getNewerThan<T extends LocalDbEntity>(collection: string, rev: number): Promise<T[]>;
43
43
  onMetaUpdated(callback: (payload: MetaUpdateBroadcast) => void): () => void;
44
- private normalizeTimestamp;
45
44
  /** Close the broadcast channel when done */
46
45
  closeBroadcastChannel(): void;
47
46
  }
@@ -60,6 +60,8 @@ export declare class SyncedDb implements I_SyncedDb {
60
60
  private readonly syncedDbInstanceId;
61
61
  /** In-memory cache of sync metadata per collection */
62
62
  private syncMetaCache;
63
+ /** In-memory cache of max _rev seen per collection (for cross-tab sync) */
64
+ private lastSeenRevCache;
63
65
  /** Unsubscribe function for cross-tab meta updates */
64
66
  private unsubscribeMetaUpdates?;
65
67
  /** Whether this instance is the leader tab */
package/dist/index.js CHANGED
@@ -306,6 +306,7 @@ class SyncedDb {
306
306
  updaterId;
307
307
  syncedDbInstanceId;
308
308
  syncMetaCache = new Map;
309
+ lastSeenRevCache = new Map;
309
310
  unsubscribeMetaUpdates;
310
311
  isLeader = false;
311
312
  leaderLockAbortController;
@@ -1528,51 +1529,45 @@ class SyncedDb {
1528
1529
  }
1529
1530
  }
1530
1531
  async handleCrossTabMetaUpdate(payload) {
1531
- if (payload.instanceId === this.syncedDbInstanceId) {
1532
+ if (payload.instanceId === this.dexieDb.getInstanceId()) {
1532
1533
  return;
1533
1534
  }
1534
1535
  const collection = payload.collection;
1535
1536
  if (!this.collections.has(collection)) {
1536
1537
  return;
1537
1538
  }
1538
- const cachedMeta = this.syncMetaCache.get(collection);
1539
- const cachedTs = this.normalizeTimestamp(cachedMeta?.lastSyncTs);
1540
- const receivedTs = this.normalizeTimestamp(payload.lastSyncTs);
1541
- if (receivedTs > cachedTs) {
1542
- try {
1543
- const newMeta = await this.dexieDb.getSyncMeta(collection);
1544
- if (newMeta) {
1545
- this.syncMetaCache.set(collection, newMeta);
1546
- }
1547
- const newerItems = await this.dexieDb.getNewerThan(collection, cachedTs);
1548
- let newItemsCount = 0;
1549
- for (const item of newerItems) {
1550
- if (item._deleted) {
1551
- this.inMemDb.deleteOne(collection, item._id);
1552
- } else {
1553
- this.inMemDb.save(collection, item._id, item);
1554
- }
1555
- newItemsCount++;
1539
+ const lastSeenRev = this.lastSeenRevCache.get(collection) ?? 0;
1540
+ try {
1541
+ const newMeta = await this.dexieDb.getSyncMeta(collection);
1542
+ if (newMeta) {
1543
+ this.syncMetaCache.set(collection, newMeta);
1544
+ }
1545
+ const newerItems = await this.dexieDb.getNewerThan(collection, lastSeenRev);
1546
+ let newItemsCount = 0;
1547
+ let maxRev = lastSeenRev;
1548
+ for (const item of newerItems) {
1549
+ if (item._rev !== undefined && item._rev > maxRev) {
1550
+ maxRev = item._rev;
1556
1551
  }
1557
- if (this.onExternalSync) {
1558
- try {
1559
- this.onExternalSync(collection, newItemsCount);
1560
- } catch (err) {
1561
- console.error("onExternalSync callback failed:", err);
1562
- }
1552
+ if (item._deleted) {
1553
+ this.inMemDb.deleteOne(collection, item._id);
1554
+ } else {
1555
+ this.inMemDb.save(collection, item._id, this.stripLocalFields(item));
1563
1556
  }
1564
- } catch (err) {
1565
- console.error(`Error handling cross-tab meta update for ${collection}:`, err);
1557
+ newItemsCount++;
1558
+ }
1559
+ if (maxRev > lastSeenRev) {
1560
+ this.lastSeenRevCache.set(collection, maxRev);
1566
1561
  }
1567
- } else if (receivedTs < cachedTs) {
1568
- console.warn(`Cross-tab sync: Received stale metadata for ${collection}. ` + `In-memory: ${cachedTs}, Received: ${receivedTs}`);
1569
- if (this.onStaleMetaConflict) {
1562
+ if (this.onExternalSync) {
1570
1563
  try {
1571
- this.onStaleMetaConflict(collection, cachedMeta?.lastSyncTs, payload.lastSyncTs);
1564
+ this.onExternalSync(collection, newItemsCount);
1572
1565
  } catch (err) {
1573
- console.error("onStaleMetaConflict callback failed:", err);
1566
+ console.error("onExternalSync callback failed:", err);
1574
1567
  }
1575
1568
  }
1569
+ } catch (err) {
1570
+ console.error(`Error handling cross-tab meta update for ${collection}:`, err);
1576
1571
  }
1577
1572
  }
1578
1573
  async handleServerUpdate(payload) {
@@ -1731,7 +1726,7 @@ class DexieDb extends Dexie {
1731
1726
  schema[SYNC_META_TABLE] = "[tenant+collection]";
1732
1727
  for (const config of collectionConfigs) {
1733
1728
  const additionalIndexes = config.indexes || [];
1734
- const indexes = ["_id", "_dirty", "_ts", ...additionalIndexes.map(String)];
1729
+ const indexes = ["_id", "_dirty", "_rev", ...additionalIndexes.map(String)];
1735
1730
  schema[config.name] = indexes.join(", ");
1736
1731
  }
1737
1732
  this.version(1).stores(schema);
@@ -1893,10 +1888,9 @@ class DexieDb extends Dexie {
1893
1888
  getInstanceId() {
1894
1889
  return this.instanceId;
1895
1890
  }
1896
- async getNewerThan(collection, timestamp) {
1891
+ async getNewerThan(collection, rev) {
1897
1892
  const table = this.getTable(collection);
1898
- const normalizedTs = this.normalizeTimestamp(timestamp);
1899
- return await table.where("_ts").above(normalizedTs).toArray();
1893
+ return await table.where("_rev").above(rev).toArray();
1900
1894
  }
1901
1895
  onMetaUpdated(callback) {
1902
1896
  this.metaUpdateCallbacks.add(callback);
@@ -1904,19 +1898,6 @@ class DexieDb extends Dexie {
1904
1898
  this.metaUpdateCallbacks.delete(callback);
1905
1899
  };
1906
1900
  }
1907
- normalizeTimestamp(ts) {
1908
- if (ts === null || ts === undefined)
1909
- return 0;
1910
- if (typeof ts === "number")
1911
- return ts;
1912
- if (ts instanceof Date)
1913
- return ts.getTime();
1914
- if (typeof ts.toNumber === "function")
1915
- return ts.toNumber();
1916
- if (typeof ts.valueOf === "function")
1917
- return ts.valueOf();
1918
- return 0;
1919
- }
1920
1901
  closeBroadcastChannel() {
1921
1902
  if (this.debounceTimer) {
1922
1903
  clearTimeout(this.debounceTimer);
@@ -61,8 +61,8 @@ export interface I_DexieDb {
61
61
  deleteSyncMeta(collection: string): Promise<void>;
62
62
  /** Vrne tenant */
63
63
  getTenant(): string;
64
- /** Get records newer than timestamp (uses _ts index - server timestamp) */
65
- getNewerThan<T extends LocalDbEntity>(collection: string, timestamp: any): Promise<T[]>;
64
+ /** Get records newer than revision (uses _rev index) */
65
+ getNewerThan<T extends LocalDbEntity>(collection: string, rev: number): Promise<T[]>;
66
66
  /** Subscribe to metadata updates from other tabs. Returns unsubscribe function. */
67
67
  onMetaUpdated(callback: (payload: MetaUpdateBroadcast) => void): () => void;
68
68
  /** Get the instance ID */
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cry-synced-db-client",
3
- "version": "0.1.29",
3
+ "version": "0.1.30",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",