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.
- package/dexie-cloud-import.json +6 -0
- package/dist/modern/dexie-cloud-addon.js +193 -68
- package/dist/modern/dexie-cloud-addon.js.map +1 -1
- package/dist/modern/dexie-cloud-addon.min.js +1 -1
- package/dist/modern/dexie-cloud-addon.min.js.map +1 -1
- package/dist/modern/service-worker.js +193 -68
- package/dist/modern/service-worker.js.map +1 -1
- package/dist/modern/service-worker.min.js +1 -1
- package/dist/modern/service-worker.min.js.map +1 -1
- package/dist/umd/dexie-cloud-addon.js +194 -69
- package/dist/umd/dexie-cloud-addon.js.map +1 -1
- package/dist/umd/dexie-cloud-addon.min.js +1 -1
- package/dist/umd/dexie-cloud-addon.min.js.map +1 -1
- package/dist/umd/service-worker.js +194 -69
- package/dist/umd/service-worker.js.map +1 -1
- package/dist/umd/service-worker.min.js +1 -1
- package/dist/umd/service-worker.min.js.map +1 -1
- package/package.json +4 -4
|
@@ -8,7 +8,7 @@
|
|
|
8
8
|
*
|
|
9
9
|
* ==========================================================================
|
|
10
10
|
*
|
|
11
|
-
* Version 4.2.
|
|
11
|
+
* Version 4.2.2, Sat Oct 04 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
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
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);
|
|
@@ -4224,6 +4284,7 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
4224
4284
|
let values = 'values' in req ? req.values : [];
|
|
4225
4285
|
let changeSpec = 'changeSpec' in req ? req.changeSpec : undefined;
|
|
4226
4286
|
let updates = 'updates' in req ? req.updates : undefined;
|
|
4287
|
+
let upsert = updates && 'upsert' in req ? req.upsert : false;
|
|
4227
4288
|
if (hasFailures) {
|
|
4228
4289
|
keys = keys.filter((_, idx) => !failures[idx]);
|
|
4229
4290
|
values = values.filter((_, idx) => !failures[idx]);
|
|
@@ -4255,29 +4316,32 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
4255
4316
|
};
|
|
4256
4317
|
const validKeys = new RangeSet();
|
|
4257
4318
|
let anyChangeSpecBecameEmpty = false;
|
|
4258
|
-
|
|
4259
|
-
|
|
4260
|
-
|
|
4261
|
-
|
|
4262
|
-
|
|
4263
|
-
|
|
4264
|
-
|
|
4265
|
-
|
|
4319
|
+
if (!upsert) {
|
|
4320
|
+
for (let i = 0, l = strippedChangeSpecs.length; i < l; ++i) {
|
|
4321
|
+
if (Object.keys(strippedChangeSpecs[i]).length > 0) {
|
|
4322
|
+
newUpdates.keys.push(updates.keys[i]);
|
|
4323
|
+
newUpdates.changeSpecs.push(strippedChangeSpecs[i]);
|
|
4324
|
+
validKeys.addKey(updates.keys[i]);
|
|
4325
|
+
}
|
|
4326
|
+
else {
|
|
4327
|
+
anyChangeSpecBecameEmpty = true;
|
|
4328
|
+
}
|
|
4266
4329
|
}
|
|
4267
|
-
|
|
4268
|
-
|
|
4269
|
-
|
|
4270
|
-
|
|
4271
|
-
|
|
4272
|
-
|
|
4273
|
-
|
|
4274
|
-
|
|
4275
|
-
|
|
4276
|
-
|
|
4330
|
+
updates = newUpdates;
|
|
4331
|
+
if (anyChangeSpecBecameEmpty) {
|
|
4332
|
+
// Some keys were stripped. We must also strip them from keys and values
|
|
4333
|
+
// unless this is an upsert operation in which case we want to send them all.
|
|
4334
|
+
let newKeys = [];
|
|
4335
|
+
let newValues = [];
|
|
4336
|
+
for (let i = 0, l = keys.length; i < l; ++i) {
|
|
4337
|
+
if (validKeys.hasKey(keys[i])) {
|
|
4338
|
+
newKeys.push(keys[i]);
|
|
4339
|
+
newValues.push(values[i]);
|
|
4340
|
+
}
|
|
4277
4341
|
}
|
|
4342
|
+
keys = newKeys;
|
|
4343
|
+
values = newValues;
|
|
4278
4344
|
}
|
|
4279
|
-
keys = newKeys;
|
|
4280
|
-
values = newValues;
|
|
4281
4345
|
}
|
|
4282
4346
|
}
|
|
4283
4347
|
}
|
|
@@ -4319,49 +4383,59 @@ function createMutationTrackingMiddleware({ currentUserObservable, db, }) {
|
|
|
4319
4383
|
userId,
|
|
4320
4384
|
values,
|
|
4321
4385
|
}
|
|
4322
|
-
:
|
|
4323
|
-
|
|
4324
|
-
|
|
4325
|
-
|
|
4326
|
-
|
|
4327
|
-
|
|
4328
|
-
|
|
4329
|
-
|
|
4330
|
-
|
|
4331
|
-
|
|
4332
|
-
|
|
4333
|
-
}
|
|
4334
|
-
: changeSpec
|
|
4386
|
+
: upsert ? {
|
|
4387
|
+
type: 'upsert',
|
|
4388
|
+
ts,
|
|
4389
|
+
opNo,
|
|
4390
|
+
keys,
|
|
4391
|
+
values,
|
|
4392
|
+
changeSpecs: updates.changeSpecs.filter((_, idx) => !failures[idx]),
|
|
4393
|
+
txid,
|
|
4394
|
+
userId,
|
|
4395
|
+
}
|
|
4396
|
+
: criteria && changeSpec
|
|
4335
4397
|
? {
|
|
4336
|
-
//
|
|
4337
|
-
type: '
|
|
4398
|
+
// Common changeSpec for all keys
|
|
4399
|
+
type: 'modify',
|
|
4338
4400
|
ts,
|
|
4339
4401
|
opNo,
|
|
4340
4402
|
keys,
|
|
4341
|
-
|
|
4403
|
+
criteria,
|
|
4404
|
+
changeSpec,
|
|
4342
4405
|
txid,
|
|
4343
4406
|
userId,
|
|
4344
4407
|
}
|
|
4345
|
-
:
|
|
4408
|
+
: changeSpec
|
|
4346
4409
|
? {
|
|
4347
|
-
//
|
|
4410
|
+
// In case criteria involved an unsynced property, we go for keys instead.
|
|
4348
4411
|
type: 'update',
|
|
4349
4412
|
ts,
|
|
4350
4413
|
opNo,
|
|
4351
|
-
keys: updates.keys,
|
|
4352
|
-
changeSpecs: updates.changeSpecs,
|
|
4353
|
-
txid,
|
|
4354
|
-
userId,
|
|
4355
|
-
}
|
|
4356
|
-
: {
|
|
4357
|
-
type: 'upsert',
|
|
4358
|
-
ts,
|
|
4359
|
-
opNo,
|
|
4360
4414
|
keys,
|
|
4361
|
-
|
|
4415
|
+
changeSpecs: keys.map(() => changeSpec),
|
|
4362
4416
|
txid,
|
|
4363
4417
|
userId,
|
|
4364
|
-
}
|
|
4418
|
+
}
|
|
4419
|
+
: updates
|
|
4420
|
+
? {
|
|
4421
|
+
// One changeSpec per key
|
|
4422
|
+
type: 'update',
|
|
4423
|
+
ts,
|
|
4424
|
+
opNo,
|
|
4425
|
+
keys: updates.keys,
|
|
4426
|
+
changeSpecs: updates.changeSpecs,
|
|
4427
|
+
txid,
|
|
4428
|
+
userId,
|
|
4429
|
+
}
|
|
4430
|
+
: {
|
|
4431
|
+
type: 'upsert',
|
|
4432
|
+
ts,
|
|
4433
|
+
opNo,
|
|
4434
|
+
keys,
|
|
4435
|
+
values,
|
|
4436
|
+
txid,
|
|
4437
|
+
userId,
|
|
4438
|
+
};
|
|
4365
4439
|
if ('isAdditionalChunk' in req && req.isAdditionalChunk) {
|
|
4366
4440
|
mut.isAdditionalChunk = true;
|
|
4367
4441
|
}
|
|
@@ -5256,34 +5330,85 @@ const Styles = {
|
|
|
5256
5330
|
alignItems: "center",
|
|
5257
5331
|
display: "flex",
|
|
5258
5332
|
justifyContent: "center",
|
|
5333
|
+
padding: "16px",
|
|
5334
|
+
boxSizing: "border-box"
|
|
5259
5335
|
},
|
|
5260
5336
|
DialogInner: {
|
|
5261
5337
|
position: "relative",
|
|
5262
5338
|
color: "#222",
|
|
5263
5339
|
backgroundColor: "#fff",
|
|
5264
|
-
padding: "
|
|
5340
|
+
padding: "24px",
|
|
5265
5341
|
marginBottom: "2em",
|
|
5266
|
-
maxWidth: "
|
|
5342
|
+
maxWidth: "400px",
|
|
5343
|
+
width: "100%",
|
|
5267
5344
|
maxHeight: "90%",
|
|
5268
5345
|
overflowY: "auto",
|
|
5269
5346
|
border: "3px solid #3d3d5d",
|
|
5270
5347
|
borderRadius: "8px",
|
|
5271
5348
|
boxShadow: "0 0 80px 10px #666",
|
|
5272
|
-
width: "auto",
|
|
5273
5349
|
fontFamily: "sans-serif",
|
|
5350
|
+
boxSizing: "border-box"
|
|
5274
5351
|
},
|
|
5275
5352
|
Input: {
|
|
5276
5353
|
height: "35px",
|
|
5277
|
-
width: "
|
|
5354
|
+
width: "100%",
|
|
5355
|
+
maxWidth: "100%",
|
|
5278
5356
|
borderColor: "#ccf4",
|
|
5279
5357
|
outline: "none",
|
|
5280
|
-
fontSize: "
|
|
5281
|
-
padding: "8px"
|
|
5358
|
+
fontSize: "16px",
|
|
5359
|
+
padding: "8px",
|
|
5360
|
+
boxSizing: "border-box"
|
|
5361
|
+
},
|
|
5362
|
+
Button: {
|
|
5363
|
+
padding: "10px 20px",
|
|
5364
|
+
margin: "0 4px",
|
|
5365
|
+
border: "1px solid #d1d5db",
|
|
5366
|
+
borderRadius: "6px",
|
|
5367
|
+
backgroundColor: "#ffffff",
|
|
5368
|
+
cursor: "pointer",
|
|
5369
|
+
fontSize: "14px",
|
|
5370
|
+
fontWeight: "500",
|
|
5371
|
+
color: "#374151",
|
|
5372
|
+
transition: "all 0.2s ease"
|
|
5373
|
+
},
|
|
5374
|
+
PrimaryButton: {
|
|
5375
|
+
padding: "10px 20px",
|
|
5376
|
+
margin: "0 4px",
|
|
5377
|
+
border: "1px solid #3b82f6",
|
|
5378
|
+
borderRadius: "6px",
|
|
5379
|
+
backgroundColor: "#3b82f6",
|
|
5380
|
+
color: "white",
|
|
5381
|
+
cursor: "pointer",
|
|
5382
|
+
fontSize: "14px",
|
|
5383
|
+
fontWeight: "500",
|
|
5384
|
+
transition: "all 0.2s ease"
|
|
5385
|
+
},
|
|
5386
|
+
ButtonsDiv: {
|
|
5387
|
+
display: "flex",
|
|
5388
|
+
justifyContent: "flex-end",
|
|
5389
|
+
gap: "12px",
|
|
5390
|
+
marginTop: "24px",
|
|
5391
|
+
paddingTop: "20px"
|
|
5392
|
+
},
|
|
5393
|
+
Label: {
|
|
5394
|
+
display: "block",
|
|
5395
|
+
marginBottom: "12px",
|
|
5396
|
+
fontSize: "14px",
|
|
5397
|
+
fontWeight: "500",
|
|
5398
|
+
color: "#333"
|
|
5399
|
+
},
|
|
5400
|
+
WindowHeader: {
|
|
5401
|
+
margin: "0 0 20px 0",
|
|
5402
|
+
fontSize: "18px",
|
|
5403
|
+
fontWeight: "600",
|
|
5404
|
+
color: "#333",
|
|
5405
|
+
borderBottom: "1px solid #eee",
|
|
5406
|
+
paddingBottom: "10px"
|
|
5282
5407
|
}
|
|
5283
5408
|
};
|
|
5284
5409
|
|
|
5285
5410
|
function Dialog({ children, className }) {
|
|
5286
|
-
return (h("div", { className: className },
|
|
5411
|
+
return (h("div", { className: `dexie-dialog ${className || ''}` },
|
|
5287
5412
|
h("div", { style: Styles.Darken }),
|
|
5288
5413
|
h("div", { style: Styles.DialogOuter },
|
|
5289
5414
|
h("div", { style: Styles.DialogInner }, children))));
|
|
@@ -5335,7 +5460,7 @@ function LoginDialog({ title, type, alerts, fields, submitLabel, cancelLabel, on
|
|
|
5335
5460
|
} })))))),
|
|
5336
5461
|
h("div", { style: Styles.ButtonsDiv },
|
|
5337
5462
|
h(p$1, null,
|
|
5338
|
-
h("button", { type: "submit", style: Styles.
|
|
5463
|
+
h("button", { type: "submit", style: Styles.PrimaryButton, onClick: () => onSubmit(params) }, submitLabel),
|
|
5339
5464
|
cancelLabel && (h("button", { style: Styles.Button, onClick: onCancel }, cancelLabel))))));
|
|
5340
5465
|
}
|
|
5341
5466
|
function valueTransformer(type, value) {
|
|
@@ -5966,7 +6091,7 @@ function dexieCloud(dexie) {
|
|
|
5966
6091
|
const syncComplete = new Subject();
|
|
5967
6092
|
dexie.cloud = {
|
|
5968
6093
|
// @ts-ignore
|
|
5969
|
-
version: "4.2.
|
|
6094
|
+
version: "4.2.2",
|
|
5970
6095
|
options: Object.assign({}, DEFAULT_OPTIONS),
|
|
5971
6096
|
schema: null,
|
|
5972
6097
|
get currentUserId() {
|
|
@@ -6283,7 +6408,7 @@ function dexieCloud(dexie) {
|
|
|
6283
6408
|
}
|
|
6284
6409
|
}
|
|
6285
6410
|
// @ts-ignore
|
|
6286
|
-
dexieCloud.version = "4.2.
|
|
6411
|
+
dexieCloud.version = "4.2.2";
|
|
6287
6412
|
Dexie.Cloud = dexieCloud;
|
|
6288
6413
|
|
|
6289
6414
|
// In case the SW lives for a while, let it reuse already opened connections:
|