dexie-cloud-addon 4.2.0 → 4.2.2

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.2, Sat Oct 04 2025
12
12
  *
13
13
  * https://dexie.org
14
14
  *
@@ -2472,17 +2472,14 @@
2472
2472
  : mutationTable;
2473
2473
  if (limit < Infinity)
2474
2474
  query = query.limit(limit);
2475
- const muts = yield query.toArray();
2476
- //const objTable = db.table(tableName);
2477
- /*for (const mut of muts) {
2478
- if (mut.type === "insert" || mut.type === "upsert") {
2479
- mut.values = await objTable.bulkGet(mut.keys);
2480
- }
2481
- }*/
2482
- return muts.map((mut) => ({
2475
+ let muts = yield query.toArray();
2476
+ muts = canonicalizeToUpdateOps(muts);
2477
+ muts = removeRedundantUpdateOps(muts);
2478
+ const rv = muts.map((mut) => ({
2483
2479
  table: tableName,
2484
2480
  mut,
2485
2481
  }));
2482
+ return rv;
2486
2483
  })));
2487
2484
  // Sort by time to get a true order of the operations (between tables)
2488
2485
  const sorted = flatten(allMutsOnTables).sort((a, b) => a.mut.txid === b.mut.txid
@@ -2511,6 +2508,69 @@
2511
2508
  return result;
2512
2509
  });
2513
2510
  }
2511
+ function removeRedundantUpdateOps(muts) {
2512
+ const updateCoverage = new Map();
2513
+ for (const mut of muts) {
2514
+ if (mut.type === 'update') {
2515
+ if (mut.keys.length !== 1 || mut.changeSpecs.length !== 1) {
2516
+ continue; // Don't optimize multi-key updates
2517
+ }
2518
+ const strKey = '' + mut.keys[0];
2519
+ const changeSpecs = mut.changeSpecs[0];
2520
+ if (Object.values(changeSpecs).some(v => typeof v === "object" && v && "@@propmod" in v)) {
2521
+ continue; // Cannot optimize if any PropModification is present
2522
+ }
2523
+ let keyCoverage = updateCoverage.get(strKey);
2524
+ if (keyCoverage) {
2525
+ keyCoverage.push({ txid: mut.txid, updateSpec: changeSpecs });
2526
+ }
2527
+ else {
2528
+ updateCoverage.set(strKey, [{ txid: mut.txid, updateSpec: changeSpecs }]);
2529
+ }
2530
+ }
2531
+ }
2532
+ muts = muts.filter(mut => {
2533
+ // Only apply optimization to update mutations that are single-key
2534
+ if (mut.type !== 'update')
2535
+ return true;
2536
+ if (mut.keys.length !== 1 || mut.changeSpecs.length !== 1)
2537
+ return true;
2538
+ // Keep track of properties that aren't overlapped by later transactions
2539
+ const unoverlappedProps = new Set(Object.keys(mut.changeSpecs[0]));
2540
+ const strKey = '' + mut.keys[0];
2541
+ const keyCoverage = updateCoverage.get(strKey);
2542
+ for (let i = keyCoverage.length - 1; i >= 0; --i) {
2543
+ const { txid, updateSpec } = keyCoverage[i];
2544
+ if (txid === mut.txid)
2545
+ break; // Stop when reaching own txid
2546
+ // If all changes in updateSpec are covered by all props on all mut.changeSpecs then
2547
+ // txid is redundant and can be removed.
2548
+ for (const keyPath of Object.keys(updateSpec)) {
2549
+ unoverlappedProps.delete(keyPath);
2550
+ }
2551
+ }
2552
+ if (unoverlappedProps.size === 0) {
2553
+ // This operation is completely overlapped by later operations. It can be removed.
2554
+ return false;
2555
+ }
2556
+ return true;
2557
+ });
2558
+ return muts;
2559
+ }
2560
+ function canonicalizeToUpdateOps(muts) {
2561
+ muts = muts.map(mut => {
2562
+ if (mut.type === 'modify' && mut.criteria.index === null) {
2563
+ // The criteria is on primary key. Convert to an update operation instead.
2564
+ // It is simpler for the server to handle and also more efficient.
2565
+ const updateMut = Object.assign(Object.assign({}, mut), { criteria: undefined, changeSpec: undefined, type: 'update', keys: mut.keys, changeSpecs: [mut.changeSpec] });
2566
+ delete updateMut.criteria;
2567
+ delete updateMut.changeSpec;
2568
+ return updateMut;
2569
+ }
2570
+ return mut;
2571
+ });
2572
+ return muts;
2573
+ }
2514
2574
 
2515
2575
  function randomString(bytes) {
2516
2576
  const buf = new Uint8Array(bytes);
@@ -14193,7 +14253,7 @@
14193
14253
  *
14194
14254
  * ==========================================================================
14195
14255
  *
14196
- * Version 4.2.0, Wed Aug 13 2025
14256
+ * Version 4.2.1, Sat Oct 04 2025
14197
14257
  *
14198
14258
  * https://dexie.org
14199
14259
  *
@@ -16005,6 +16065,7 @@
16005
16065
  let values = 'values' in req ? req.values : [];
16006
16066
  let changeSpec = 'changeSpec' in req ? req.changeSpec : undefined;
16007
16067
  let updates = 'updates' in req ? req.updates : undefined;
16068
+ let upsert = updates && 'upsert' in req ? req.upsert : false;
16008
16069
  if (hasFailures) {
16009
16070
  keys = keys.filter((_, idx) => !failures[idx]);
16010
16071
  values = values.filter((_, idx) => !failures[idx]);
@@ -16036,29 +16097,32 @@
16036
16097
  };
16037
16098
  const validKeys = new Dexie.RangeSet();
16038
16099
  let anyChangeSpecBecameEmpty = false;
16039
- for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
16040
- if (Object.keys(strippedChangeSpecs[i]).length > 0) {
16041
- newUpdates.keys.push(updates.keys[i]);
16042
- newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
16043
- validKeys.addKey(updates.keys[i]);
16044
- }
16045
- else {
16046
- anyChangeSpecBecameEmpty = true;
16100
+ if (!upsert) {
16101
+ for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
16102
+ if (Object.keys(strippedChangeSpecs[i]).length > 0) {
16103
+ newUpdates.keys.push(updates.keys[i]);
16104
+ newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
16105
+ validKeys.addKey(updates.keys[i]);
16106
+ }
16107
+ else {
16108
+ anyChangeSpecBecameEmpty = true;
16109
+ }
16047
16110
  }
16048
- }
16049
- updates = newUpdates;
16050
- if (anyChangeSpecBecameEmpty) {
16051
- // Some keys were stripped. We must also strip them from keys and values
16052
- let newKeys = [];
16053
- let newValues = [];
16054
- for (let i = 0, l = keys.length; i < l; ++i) {
16055
- if (validKeys.hasKey(keys[i])) {
16056
- newKeys.push(keys[i]);
16057
- newValues.push(values[i]);
16111
+ updates = newUpdates;
16112
+ if (anyChangeSpecBecameEmpty) {
16113
+ // Some keys were stripped. We must also strip them from keys and values
16114
+ // unless this is an upsert operation in which case we want to send them all.
16115
+ let newKeys = [];
16116
+ let newValues = [];
16117
+ for (let i = 0, l = keys.length; i < l; ++i) {
16118
+ if (validKeys.hasKey(keys[i])) {
16119
+ newKeys.push(keys[i]);
16120
+ newValues.push(values[i]);
16121
+ }
16058
16122
  }
16123
+ keys = newKeys;
16124
+ values = newValues;
16059
16125
  }
16060
- keys = newKeys;
16061
- values = newValues;
16062
16126
  }
16063
16127
  }
16064
16128
  }
@@ -16100,49 +16164,59 @@
16100
16164
  userId,
16101
16165
  values,
16102
16166
  }
16103
- : criteria && changeSpec
16104
- ? {
16105
- // Common changeSpec for all keys
16106
- type: 'modify',
16107
- ts,
16108
- opNo,
16109
- keys,
16110
- criteria,
16111
- changeSpec,
16112
- txid,
16113
- userId,
16114
- }
16115
- : changeSpec
16167
+ : upsert ? {
16168
+ type: 'upsert',
16169
+ ts,
16170
+ opNo,
16171
+ keys,
16172
+ values,
16173
+ changeSpecs: updates.changeSpecs.filter((_, idx) => !failures[idx]),
16174
+ txid,
16175
+ userId,
16176
+ }
16177
+ : criteria && changeSpec
16116
16178
  ? {
16117
- // In case criteria involved an unsynced property, we go for keys instead.
16118
- type: 'update',
16179
+ // Common changeSpec for all keys
16180
+ type: 'modify',
16119
16181
  ts,
16120
16182
  opNo,
16121
16183
  keys,
16122
- changeSpecs: keys.map(() => changeSpec),
16184
+ criteria,
16185
+ changeSpec,
16123
16186
  txid,
16124
16187
  userId,
16125
16188
  }
16126
- : updates
16189
+ : changeSpec
16127
16190
  ? {
16128
- // One changeSpec per key
16191
+ // In case criteria involved an unsynced property, we go for keys instead.
16129
16192
  type: 'update',
16130
16193
  ts,
16131
16194
  opNo,
16132
- keys: updates.keys,
16133
- changeSpecs: updates.changeSpecs,
16134
- txid,
16135
- userId,
16136
- }
16137
- : {
16138
- type: 'upsert',
16139
- ts,
16140
- opNo,
16141
16195
  keys,
16142
- values,
16196
+ changeSpecs: keys.map(() => changeSpec),
16143
16197
  txid,
16144
16198
  userId,
16145
- };
16199
+ }
16200
+ : updates
16201
+ ? {
16202
+ // One changeSpec per key
16203
+ type: 'update',
16204
+ ts,
16205
+ opNo,
16206
+ keys: updates.keys,
16207
+ changeSpecs: updates.changeSpecs,
16208
+ txid,
16209
+ userId,
16210
+ }
16211
+ : {
16212
+ type: 'upsert',
16213
+ ts,
16214
+ opNo,
16215
+ keys,
16216
+ values,
16217
+ txid,
16218
+ userId,
16219
+ };
16146
16220
  if ('isAdditionalChunk' in req && req.isAdditionalChunk) {
16147
16221
  mut.isAdditionalChunk = true;
16148
16222
  }
@@ -17299,34 +17373,85 @@
17299
17373
  alignItems: "center",
17300
17374
  display: "flex",
17301
17375
  justifyContent: "center",
17376
+ padding: "16px",
17377
+ boxSizing: "border-box"
17302
17378
  },
17303
17379
  DialogInner: {
17304
17380
  position: "relative",
17305
17381
  color: "#222",
17306
17382
  backgroundColor: "#fff",
17307
- padding: "30px",
17383
+ padding: "24px",
17308
17384
  marginBottom: "2em",
17309
- maxWidth: "90%",
17385
+ maxWidth: "400px",
17386
+ width: "100%",
17310
17387
  maxHeight: "90%",
17311
17388
  overflowY: "auto",
17312
17389
  border: "3px solid #3d3d5d",
17313
17390
  borderRadius: "8px",
17314
17391
  boxShadow: "0 0 80px 10px #666",
17315
- width: "auto",
17316
17392
  fontFamily: "sans-serif",
17393
+ boxSizing: "border-box"
17317
17394
  },
17318
17395
  Input: {
17319
17396
  height: "35px",
17320
- width: "17em",
17397
+ width: "100%",
17398
+ maxWidth: "100%",
17321
17399
  borderColor: "#ccf4",
17322
17400
  outline: "none",
17323
- fontSize: "17pt",
17324
- padding: "8px"
17401
+ fontSize: "16px",
17402
+ padding: "8px",
17403
+ boxSizing: "border-box"
17404
+ },
17405
+ Button: {
17406
+ padding: "10px 20px",
17407
+ margin: "0 4px",
17408
+ border: "1px solid #d1d5db",
17409
+ borderRadius: "6px",
17410
+ backgroundColor: "#ffffff",
17411
+ cursor: "pointer",
17412
+ fontSize: "14px",
17413
+ fontWeight: "500",
17414
+ color: "#374151",
17415
+ transition: "all 0.2s ease"
17416
+ },
17417
+ PrimaryButton: {
17418
+ padding: "10px 20px",
17419
+ margin: "0 4px",
17420
+ border: "1px solid #3b82f6",
17421
+ borderRadius: "6px",
17422
+ backgroundColor: "#3b82f6",
17423
+ color: "white",
17424
+ cursor: "pointer",
17425
+ fontSize: "14px",
17426
+ fontWeight: "500",
17427
+ transition: "all 0.2s ease"
17428
+ },
17429
+ ButtonsDiv: {
17430
+ display: "flex",
17431
+ justifyContent: "flex-end",
17432
+ gap: "12px",
17433
+ marginTop: "24px",
17434
+ paddingTop: "20px"
17435
+ },
17436
+ Label: {
17437
+ display: "block",
17438
+ marginBottom: "12px",
17439
+ fontSize: "14px",
17440
+ fontWeight: "500",
17441
+ color: "#333"
17442
+ },
17443
+ WindowHeader: {
17444
+ margin: "0 0 20px 0",
17445
+ fontSize: "18px",
17446
+ fontWeight: "600",
17447
+ color: "#333",
17448
+ borderBottom: "1px solid #eee",
17449
+ paddingBottom: "10px"
17325
17450
  }
17326
17451
  };
17327
17452
 
17328
17453
  function Dialog({ children, className }) {
17329
- return (h("div", { className: className },
17454
+ return (h("div", { className: `dexie-dialog ${className || ''}` },
17330
17455
  h("div", { style: Styles.Darken }),
17331
17456
  h("div", { style: Styles.DialogOuter },
17332
17457
  h("div", { style: Styles.DialogInner }, children))));
@@ -17378,7 +17503,7 @@
17378
17503
  } })))))),
17379
17504
  h("div", { style: Styles.ButtonsDiv },
17380
17505
  h(p$1, null,
17381
- h("button", { type: "submit", style: Styles.Button, onClick: () => onSubmit(params) }, submitLabel),
17506
+ h("button", { type: "submit", style: Styles.PrimaryButton, onClick: () => onSubmit(params) }, submitLabel),
17382
17507
  cancelLabel && (h("button", { style: Styles.Button, onClick: onCancel }, cancelLabel))))));
17383
17508
  }
17384
17509
  function valueTransformer(type, value) {
@@ -18180,7 +18305,7 @@
18180
18305
  const syncComplete = new rxjs.Subject();
18181
18306
  dexie.cloud = {
18182
18307
  // @ts-ignore
18183
- version: "4.2.0",
18308
+ version: "4.2.2",
18184
18309
  options: Object.assign({}, DEFAULT_OPTIONS),
18185
18310
  schema: null,
18186
18311
  get currentUserId() {
@@ -18497,7 +18622,7 @@
18497
18622
  }
18498
18623
  }
18499
18624
  // @ts-ignore
18500
- dexieCloud.version = "4.2.0";
18625
+ dexieCloud.version = "4.2.2";
18501
18626
  Dexie.Cloud = dexieCloud;
18502
18627
 
18503
18628
  exports.default = dexieCloud;