dexie-cloud-addon 4.2.0 → 4.2.1

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.
@@ -8,7 +8,7 @@
8
8
  *
9
9
  * ==========================================================================
10
10
  *
11
- * Version 4.2.0, Wed Aug 13 2025
11
+ * Version 4.2.1, Wed Oct 01 2025
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -412,17 +412,14 @@ function listClientChanges(mutationTables_1, db_1) {
412
412
  : mutationTable;
413
413
  if (limit < Infinity)
414
414
  query = query.limit(limit);
415
- const muts = yield query.toArray();
416
- //const objTable = db.table(tableName);
417
- /*for (const mut of muts) {
418
- if (mut.type === "insert" || mut.type === "upsert") {
419
- mut.values = await objTable.bulkGet(mut.keys);
420
- }
421
- }*/
422
- return muts.map((mut) => ({
415
+ let muts = yield query.toArray();
416
+ muts = canonicalizeToUpdateOps(muts);
417
+ muts = removeRedundantUpdateOps(muts);
418
+ const rv = muts.map((mut) => ({
423
419
  table: tableName,
424
420
  mut,
425
421
  }));
422
+ return rv;
426
423
  })));
427
424
  // Sort by time to get a true order of the operations (between tables)
428
425
  const sorted = flatten(allMutsOnTables).sort((a, b) => a.mut.txid === b.mut.txid
@@ -451,6 +448,69 @@ function listClientChanges(mutationTables_1, db_1) {
451
448
  return result;
452
449
  });
453
450
  }
451
+ function removeRedundantUpdateOps(muts) {
452
+ const updateCoverage = new Map();
453
+ for (const mut of muts) {
454
+ if (mut.type === 'update') {
455
+ if (mut.keys.length !== 1 || mut.changeSpecs.length !== 1) {
456
+ continue; // Don't optimize multi-key updates
457
+ }
458
+ const strKey = '' + mut.keys[0];
459
+ const changeSpecs = mut.changeSpecs[0];
460
+ if (Object.values(changeSpecs).some(v => typeof v === "object" && v && "@@propmod" in v)) {
461
+ continue; // Cannot optimize if any PropModification is present
462
+ }
463
+ let keyCoverage = updateCoverage.get(strKey);
464
+ if (keyCoverage) {
465
+ keyCoverage.push({ txid: mut.txid, updateSpec: changeSpecs });
466
+ }
467
+ else {
468
+ updateCoverage.set(strKey, [{ txid: mut.txid, updateSpec: changeSpecs }]);
469
+ }
470
+ }
471
+ }
472
+ muts = muts.filter(mut => {
473
+ // Only apply optimization to update mutations that are single-key
474
+ if (mut.type !== 'update')
475
+ return true;
476
+ if (mut.keys.length !== 1 || mut.changeSpecs.length !== 1)
477
+ return true;
478
+ // Keep track of properties that aren't overlapped by later transactions
479
+ const unoverlappedProps = new Set(Object.keys(mut.changeSpecs[0]));
480
+ const strKey = '' + mut.keys[0];
481
+ const keyCoverage = updateCoverage.get(strKey);
482
+ for (let i = keyCoverage.length - 1; i >= 0; --i) {
483
+ const { txid, updateSpec } = keyCoverage[i];
484
+ if (txid === mut.txid)
485
+ break; // Stop when reaching own txid
486
+ // If all changes in updateSpec are covered by all props on all mut.changeSpecs then
487
+ // txid is redundant and can be removed.
488
+ for (const keyPath of Object.keys(updateSpec)) {
489
+ unoverlappedProps.delete(keyPath);
490
+ }
491
+ }
492
+ if (unoverlappedProps.size === 0) {
493
+ // This operation is completely overlapped by later operations. It can be removed.
494
+ return false;
495
+ }
496
+ return true;
497
+ });
498
+ return muts;
499
+ }
500
+ function canonicalizeToUpdateOps(muts) {
501
+ muts = muts.map(mut => {
502
+ if (mut.type === 'modify' && mut.criteria.index === null) {
503
+ // The criteria is on primary key. Convert to an update operation instead.
504
+ // It is simpler for the server to handle and also more efficient.
505
+ const updateMut = Object.assign(Object.assign({}, mut), { criteria: undefined, changeSpec: undefined, type: 'update', keys: mut.keys, changeSpecs: [mut.changeSpec] });
506
+ delete updateMut.criteria;
507
+ delete updateMut.changeSpec;
508
+ return updateMut;
509
+ }
510
+ return mut;
511
+ });
512
+ return muts;
513
+ }
454
514
 
455
515
  function randomString$1(bytes) {
456
516
  const buf = new Uint8Array(bytes);
@@ -5966,7 +6026,7 @@ function dexieCloud(dexie) {
5966
6026
  const syncComplete = new Subject();
5967
6027
  dexie.cloud = {
5968
6028
  // @ts-ignore
5969
- version: "4.2.0",
6029
+ version: "4.2.1",
5970
6030
  options: Object.assign({}, DEFAULT_OPTIONS),
5971
6031
  schema: null,
5972
6032
  get currentUserId() {
@@ -6283,7 +6343,7 @@ function dexieCloud(dexie) {
6283
6343
  }
6284
6344
  }
6285
6345
  // @ts-ignore
6286
- dexieCloud.version = "4.2.0";
6346
+ dexieCloud.version = "4.2.1";
6287
6347
  Dexie.Cloud = dexieCloud;
6288
6348
 
6289
6349
  // In case the SW lives for a while, let it reuse already opened connections: