cry-synced-db-client 2.0.11 → 2.0.14

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.
package/dist/index.js CHANGED
@@ -3325,20 +3325,35 @@ var SyncEngine = class {
3325
3325
  async (collection, items, specId) => {
3326
3326
  if (specId !== void 0) {
3327
3327
  if (specId.endsWith(":exit")) {
3328
- const exitItems = items.filter((it) => it._evictIds);
3329
- if (exitItems.length > 0) {
3330
- const exitIds = exitItems.map((it) => String(it._id));
3331
- await this.dexieDb.deleteMany(collection, exitIds);
3328
+ const exitItems = items.filter((item) => item._evictIds);
3329
+ const state2 = collectionState.get(collection);
3330
+ for (const exitItem of exitItems) {
3331
+ if (exitItem._ts && (!(state2 == null ? void 0 : state2.maxTs) || this.compareTimestamps(exitItem._ts, state2.maxTs) > 0)) {
3332
+ state2.maxTs = exitItem._ts;
3333
+ }
3334
+ }
3335
+ const exitIds = exitItems.map((item) => String(item._id));
3336
+ const dirtyChanges = await this.dexieDb.getDirtyChangesBatch(
3337
+ collection,
3338
+ exitIds
3339
+ );
3340
+ const evictIds = exitIds.filter((id) => !dirtyChanges.has(id));
3341
+ const dirtySkipped = exitIds.length - evictIds.length;
3342
+ if (evictIds.length > 0) {
3343
+ await this.dexieDb.deleteMany(collection, evictIds);
3332
3344
  this.deps.writeToInMemBatch(
3333
3345
  collection,
3334
- exitIds.map((id) => ({ _id: id })),
3346
+ evictIds.map((id) => ({ _id: id })),
3335
3347
  "delete",
3336
3348
  { source: "incremental" }
3337
3349
  );
3350
+ }
3351
+ if (exitIds.length > 0) {
3338
3352
  this.callbackSafe(this.callbacks.onEviction, {
3339
3353
  collection,
3340
- totalEvicted: exitIds.length,
3341
- totalServerEvicted: exitIds.length
3354
+ totalEvicted: evictIds.length,
3355
+ totalServerEvicted: evictIds.length,
3356
+ dirtySkipped
3342
3357
  });
3343
3358
  }
3344
3359
  } else if (extras) {
@@ -3403,6 +3418,15 @@ var SyncEngine = class {
3403
3418
  ),
3404
3419
  "findNewerManyStream"
3405
3420
  );
3421
+ for (const [collection, state] of collectionState) {
3422
+ if (!state.maxTs) continue;
3423
+ await this.dexieDb.setSyncMeta(collection, state.maxTs);
3424
+ this.deps.setSyncMetaCache(collection, {
3425
+ tenant: this.tenant,
3426
+ collection,
3427
+ lastSyncTs: state.maxTs
3428
+ });
3429
+ }
3406
3430
  for (const [name, state] of collectionState) {
3407
3431
  receivedCount += state.receivedCount;
3408
3432
  conflictsResolved += state.conflicts;
@@ -4111,28 +4135,6 @@ var SyncEngine = class {
4111
4135
  if (collectionSentCount > 0) {
4112
4136
  collectionSentCounts[collection] = collectionSentCount;
4113
4137
  }
4114
- let maxTs;
4115
- for (const arr of [inserted, updated, deleted]) {
4116
- for (const item of arr) {
4117
- if (item._ts) {
4118
- if (!maxTs || this.compareTimestamps(item._ts, maxTs) > 0) {
4119
- maxTs = item._ts;
4120
- }
4121
- }
4122
- }
4123
- }
4124
- if (maxTs) {
4125
- const currentMeta = this.deps.getSyncMetaCache().get(collection);
4126
- const currentTs = currentMeta == null ? void 0 : currentMeta.lastSyncTs;
4127
- if (!currentTs || this.compareTimestamps(maxTs, currentTs) > 0) {
4128
- await this.dexieDb.setSyncMeta(collection, maxTs);
4129
- this.deps.setSyncMetaCache(collection, {
4130
- tenant: this.tenant,
4131
- collection,
4132
- lastSyncTs: maxTs
4133
- });
4134
- }
4135
- }
4136
4138
  if (errors2 && errors2.length > 0) {
4137
4139
  for (const e of errors2) {
4138
4140
  console.error(
@@ -4338,7 +4340,7 @@ var SyncEngine = class {
4338
4340
  async processCollectionServerData(collectionName, serverData, opts) {
4339
4341
  var _a;
4340
4342
  const config = this.collections.get(collectionName);
4341
- if (!config) return { updatedIds: [] };
4343
+ if (!config) return { updatedIds: [], maxTs: void 0 };
4342
4344
  const source = (_a = opts == null ? void 0 : opts.source) != null ? _a : "incremental";
4343
4345
  const result = await this.processIncomingServerData(
4344
4346
  collectionName,
@@ -4349,7 +4351,7 @@ var SyncEngine = class {
4349
4351
  if (result.updatedIds.length > 0) {
4350
4352
  this.deps.broadcastUpdates({ [collectionName]: result.updatedIds });
4351
4353
  }
4352
- return { updatedIds: result.updatedIds };
4354
+ return { updatedIds: result.updatedIds, maxTs: result.maxTs };
4353
4355
  }
4354
4356
  async processIncomingServerData(collectionName, config, serverData, source = "incremental") {
4355
4357
  if (serverData.length === 0) {
@@ -4443,14 +4445,6 @@ var SyncEngine = class {
4443
4445
  );
4444
4446
  }
4445
4447
  }
4446
- if (maxTs) {
4447
- await this.dexieDb.setSyncMeta(collectionName, maxTs);
4448
- this.deps.setSyncMetaCache(collectionName, {
4449
- tenant: this.tenant,
4450
- collection: collectionName,
4451
- lastSyncTs: maxTs
4452
- });
4453
- }
4454
4448
  return { conflictsResolved, maxTs, updatedIds: allUpdatedIds, dexieMs: totalDexieMs, inMemMs: totalInMemMs };
4455
4449
  }
4456
4450
  // ============================================================
@@ -5911,11 +5905,14 @@ var _SyncedDb = class _SyncedDb {
5911
5905
  if (!data || data.length === 0) continue;
5912
5906
  const source = plan.wasFirstTime ? "initial" : "incremental";
5913
5907
  try {
5914
- await this.syncEngine.processCollectionServerData(
5908
+ const result = await this.syncEngine.processCollectionServerData(
5915
5909
  plan.spec.name,
5916
5910
  data,
5917
5911
  { source }
5918
5912
  );
5913
+ if (plan.wasFirstTime) {
5914
+ await this._persistInitialCollectionCursor(plan.spec.name, result.maxTs);
5915
+ }
5919
5916
  } catch (err) {
5920
5917
  console.error(
5921
5918
  `[SyncedDb] addCollectionsToSync: processCollectionServerData failed for "${plan.spec.name}":`,
@@ -5954,9 +5951,12 @@ var _SyncedDb = class _SyncedDb {
5954
5951
  if (!spec.writeOnly && this.connectionManager.canSync()) {
5955
5952
  const rawQuery = (_a = spec.syncConfig) == null ? void 0 : _a.query;
5956
5953
  const query = typeof rawQuery === "function" ? rawQuery() : rawQuery;
5957
- await this.syncCollectionForFind(spec.name, query, {
5958
- returnDeleted: true
5959
- });
5954
+ await this.syncCollectionForFind(
5955
+ spec.name,
5956
+ query,
5957
+ { returnDeleted: true },
5958
+ true
5959
+ );
5960
5960
  }
5961
5961
  }
5962
5962
  /**
@@ -6586,7 +6586,7 @@ var _SyncedDb = class _SyncedDb {
6586
6586
  * Sync a collection from server before querying locally.
6587
6587
  * Used when returnDeleted/returnArchived is specified on find/findOne.
6588
6588
  */
6589
- async syncCollectionForFind(collection, query, opts) {
6589
+ async syncCollectionForFind(collection, query, opts, checkpointInitialCollection = false) {
6590
6590
  const meta = this.syncMetaCache.get(collection);
6591
6591
  const timestamp = (meta == null ? void 0 : meta.lastSyncTs) || 0;
6592
6592
  try {
@@ -6604,15 +6604,28 @@ var _SyncedDb = class _SyncedDb {
6604
6604
  );
6605
6605
  if (serverData.length > 0) {
6606
6606
  const source = timestamp === 0 ? "initial" : "incremental";
6607
- await this.syncEngine.processCollectionServerData(
6607
+ const result = await this.syncEngine.processCollectionServerData(
6608
6608
  collection,
6609
6609
  serverData,
6610
6610
  { source }
6611
6611
  );
6612
+ if (checkpointInitialCollection && timestamp === 0) {
6613
+ await this._persistInitialCollectionCursor(collection, result.maxTs);
6614
+ }
6612
6615
  }
6613
6616
  } catch (e) {
6614
6617
  }
6615
6618
  }
6619
+ async _persistInitialCollectionCursor(collection, maxTs) {
6620
+ var _a;
6621
+ if (!maxTs || ((_a = this.syncMetaCache.get(collection)) == null ? void 0 : _a.lastSyncTs)) return;
6622
+ await this.dexieDb.setSyncMeta(collection, maxTs);
6623
+ this.syncMetaCache.set(collection, {
6624
+ tenant: this.tenant,
6625
+ collection,
6626
+ lastSyncTs: maxTs
6627
+ });
6628
+ }
6616
6629
  /**
6617
6630
  * Fire-and-forget background query to server.
6618
6631
  * Uses timestamp 0 to fetch all matching items (not just newer than
@@ -8641,30 +8654,15 @@ var DexieDb = class extends Dexie {
8641
8654
  async saveMany(collection, items) {
8642
8655
  if (items.length === 0) return;
8643
8656
  const table = this.getTable(collection);
8644
- const valid = [];
8645
8657
  for (const item of items) {
8646
8658
  this.ensureStringId(item);
8647
8659
  if (typeof item._id !== "string" || item._id.length === 0) {
8648
- console.error(
8649
- `[DexieDb] DexieDb.saveMany: skipping item with invalid _id in "${collection}"`,
8650
- { _id: item._id, _idType: typeof item._id, item }
8660
+ throw new Error(
8661
+ `[DexieDb] DexieDb.saveMany: invalid _id in "${collection}": ${String(item._id)}`
8651
8662
  );
8652
- continue;
8653
8663
  }
8654
- valid.push(item);
8655
- }
8656
- if (valid.length === 0) return;
8657
- try {
8658
- await table.bulkPut(valid);
8659
- } catch (err) {
8660
- const ids = valid.map((it) => String(it._id));
8661
- console.error(
8662
- `[DexieDb] DexieDb.saveMany: bulkPut failed for "${collection}" (${valid.length} items):`,
8663
- err,
8664
- "_ids:",
8665
- ids
8666
- );
8667
8664
  }
8665
+ await table.bulkPut(items);
8668
8666
  }
8669
8667
  async deleteOne(collection, id) {
8670
8668
  const table = this.getTable(collection);
@@ -9435,36 +9433,45 @@ function readStringJS(length) {
9435
9433
  if ((byte1 & 128) === 0) {
9436
9434
  units.push(byte1);
9437
9435
  } else if ((byte1 & 224) === 192) {
9438
- const byte2 = src[position++] & 63;
9439
- const codePoint = (byte1 & 31) << 6 | byte2;
9440
- if (codePoint < 128) {
9436
+ if (byte1 < 194 || position >= end || (src[position] & 192) !== 128) {
9441
9437
  units.push(65533);
9442
9438
  } else {
9443
- units.push(codePoint);
9439
+ const byte2 = src[position++] & 63;
9440
+ units.push((byte1 & 31) << 6 | byte2);
9444
9441
  }
9445
9442
  } else if ((byte1 & 240) === 224) {
9446
- const byte2 = src[position++] & 63;
9447
- const byte3 = src[position++] & 63;
9448
- const codePoint = (byte1 & 31) << 12 | byte2 << 6 | byte3;
9449
- if (codePoint < 2048 || codePoint >= 55296 && codePoint <= 57343) {
9443
+ const byte2 = position < end ? src[position] : 0;
9444
+ if (position >= end || (byte2 & 192) !== 128 || byte1 === 224 && byte2 < 160 || byte1 === 237 && byte2 >= 160) {
9450
9445
  units.push(65533);
9451
9446
  } else {
9452
- units.push(codePoint);
9447
+ position++;
9448
+ if (position >= end || (src[position] & 192) !== 128) {
9449
+ units.push(65533);
9450
+ } else {
9451
+ const byte3 = src[position++] & 63;
9452
+ units.push((byte1 & 31) << 12 | (byte2 & 63) << 6 | byte3);
9453
+ }
9453
9454
  }
9454
9455
  } else if ((byte1 & 248) === 240) {
9455
- const byte2 = src[position++] & 63;
9456
- const byte3 = src[position++] & 63;
9457
- const byte4 = src[position++] & 63;
9458
- let unit = (byte1 & 7) << 18 | byte2 << 12 | byte3 << 6 | byte4;
9459
- if (unit < 65536 || unit > 1114111) {
9456
+ const byte2 = position < end ? src[position] : 0;
9457
+ if (byte1 > 244 || position >= end || (byte2 & 192) !== 128 || byte1 === 240 && byte2 < 144 || byte1 === 244 && byte2 >= 144) {
9460
9458
  units.push(65533);
9461
- } else if (unit > 65535) {
9462
- unit -= 65536;
9463
- units.push(unit >>> 10 & 1023 | 55296);
9464
- unit = 56320 | unit & 1023;
9465
- units.push(unit);
9466
9459
  } else {
9467
- units.push(unit);
9460
+ position++;
9461
+ if (position >= end || (src[position] & 192) !== 128) {
9462
+ units.push(65533);
9463
+ } else {
9464
+ const byte3 = src[position++] & 63;
9465
+ if (position >= end || (src[position] & 192) !== 128) {
9466
+ units.push(65533);
9467
+ } else {
9468
+ const byte4 = src[position++] & 63;
9469
+ let unit = (byte1 & 7) << 18 | (byte2 & 63) << 12 | byte3 << 6 | byte4;
9470
+ unit -= 65536;
9471
+ units.push(unit >>> 10 & 1023 | 55296);
9472
+ units.push(56320 | unit & 1023);
9473
+ }
9474
+ }
9468
9475
  }
9469
9476
  } else {
9470
9477
  units.push(65533);
@@ -234,6 +234,7 @@ export declare class SyncedDb implements I_SyncedDb {
234
234
  * Used when returnDeleted/returnArchived is specified on find/findOne.
235
235
  */
236
236
  private syncCollectionForFind;
237
+ private _persistInitialCollectionCursor;
237
238
  /**
238
239
  * Fire-and-forget background query to server.
239
240
  * Uses timestamp 0 to fetch all matching items (not just newer than
@@ -6,6 +6,7 @@
6
6
  * - Processing incoming data with conflict resolution
7
7
  * - Uploading dirty items to server
8
8
  */
9
+ import type { Timestamp } from "mongodb";
9
10
  import type { LocalDbEntity } from "../../types/DbEntity";
10
11
  import type { SyncSource } from "../../types/I_InMemDb";
11
12
  import type { I_SyncEngine, SyncEngineConfig, SyncExtras } from "../types/managers";
@@ -118,6 +119,7 @@ export declare class SyncEngine implements I_SyncEngine {
118
119
  source?: SyncSource;
119
120
  }): Promise<{
120
121
  updatedIds: string[];
122
+ maxTs: Timestamp | undefined;
121
123
  }>;
122
124
  private readonly syncBatchSize;
123
125
  private processIncomingServerData;
@@ -347,6 +347,7 @@ export interface I_SyncEngine {
347
347
  source?: SyncSource;
348
348
  }): Promise<{
349
349
  updatedIds: string[];
350
+ maxTs: import("mongodb").Timestamp | undefined;
350
351
  }>;
351
352
  }
352
353
  export interface ServerUpdateHandlerCallbacks {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cry-synced-db-client",
3
- "version": "2.0.11",
3
+ "version": "2.0.14",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",
@@ -38,9 +38,9 @@
38
38
  "vitest": "^4.1.10"
39
39
  },
40
40
  "dependencies": {
41
- "cry-db": "^2.6.3",
42
- "cry-helpers": "^2.1.207",
43
- "msgpackr": "^2.0.4",
41
+ "cry-db": "^2.6.4",
42
+ "cry-helpers": "^2.1.208",
43
+ "msgpackr": "^2.0.5",
44
44
  "superjson": "^2.2.6"
45
45
  },
46
46
  "peerDependencies": {