cry-synced-db-client 2.0.0 → 2.0.3
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/CHANGELOG.md +37 -0
- package/dist/index.js +25 -20
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,42 @@
|
|
|
1
1
|
# Versions
|
|
2
2
|
|
|
3
|
+
## 2.0.2 (2026-07-13)
|
|
4
|
+
|
|
5
|
+
### Fix: `PendingChangesManager.schedule` — deleted fields resurrected from prior pending save
|
|
6
|
+
|
|
7
|
+
When two rapid `save()` calls arrived before the first Dexie debounce
|
|
8
|
+
flushed, `PendingChangesManager.schedule` merged them via
|
|
9
|
+
`Object.assign(existing.data, data)`. The second call's `data` was the
|
|
10
|
+
result of `applyObjDiff` — which **physically removes** keys set to
|
|
11
|
+
`undefined` (delete-by-path). `Object.assign` doesn't delete keys
|
|
12
|
+
absent from the source, so the first save's value silently survived in
|
|
13
|
+
the pending payload, resurrecting fields the caller had explicitly
|
|
14
|
+
cleared.
|
|
15
|
+
|
|
16
|
+
**Real-world trigger (klikvet):** `mounted()` calls
|
|
17
|
+
`save({ sedajObdeluje: "primoz" })` → `beforeRouteLeave` calls
|
|
18
|
+
`save({ sedajObdeluje: undefined })`. Both land in `PendingChanges`
|
|
19
|
+
before Dexie debounce fires. The merged row kept `sedajObdeluje: "primoz"`
|
|
20
|
+
in Dexie indefinitely — `razveljaviSamoVpogledSpremembe` never saw the
|
|
21
|
+
field cleared in the `objDiff` between `racunObVstopu` and
|
|
22
|
+
`this.racun`.
|
|
23
|
+
|
|
24
|
+
**Fix in `PendingChangesManager.schedule`:** replace shallow
|
|
25
|
+
`Object.assign(existing.data, data)` with key-aware merge — copy all
|
|
26
|
+
keys from `existing.data`, overwrite with keys in `data`, and
|
|
27
|
+
**delete** any key that exists in the existing row but is absent from
|
|
28
|
+
`data` (excluding `_id`, `_rev`, `_ts`).
|
|
29
|
+
|
|
30
|
+
**Fix in `SyncedDb.save()`:** diff against **in-mem** state (when
|
|
31
|
+
available) instead of stale Dexie. If PendingChanges holds writes that
|
|
32
|
+
haven't reached Dexie, deletions made against the live in-mem value
|
|
33
|
+
are invisible when diffing against stale Dexie (existing field is
|
|
34
|
+
missing from Dexie → no diff), and the field silently resurrects
|
|
35
|
+
from the in-mem base during `applyObjDiff`.
|
|
36
|
+
|
|
37
|
+
**New test:** `test/node-only/pendingChangesDeleteField.test.ts` —
|
|
38
|
+
2 cases covering the two-rapid-save-delete scenario.
|
|
39
|
+
|
|
3
40
|
## 2.0.0 (2026-07-10)
|
|
4
41
|
|
|
5
42
|
### Dual-channel ebus support: `db/` + `dbbracketed/`
|
package/dist/index.js
CHANGED
|
@@ -2723,7 +2723,17 @@ var PendingChangesManager = class {
|
|
|
2723
2723
|
}
|
|
2724
2724
|
const deltaWithId = existing ? data : __spreadProps(__spreadValues({}, data), { _id: id });
|
|
2725
2725
|
savePendingWrite(this.tenant, collection, id, deltaWithId);
|
|
2726
|
-
|
|
2726
|
+
let fullData;
|
|
2727
|
+
if (existing) {
|
|
2728
|
+
fullData = __spreadProps(__spreadValues(__spreadValues({}, existing.data), data), { _id: id });
|
|
2729
|
+
for (const key2 of Object.keys(existing.data)) {
|
|
2730
|
+
if (key2 !== "_id" && !(key2 in data)) {
|
|
2731
|
+
delete fullData[key2];
|
|
2732
|
+
}
|
|
2733
|
+
}
|
|
2734
|
+
} else {
|
|
2735
|
+
fullData = __spreadProps(__spreadValues({}, data), { _id: id });
|
|
2736
|
+
}
|
|
2727
2737
|
const timer = setTimeout(() => {
|
|
2728
2738
|
this.executePendingChange(key);
|
|
2729
2739
|
}, this.debounceDexieWritesMs);
|
|
@@ -4512,7 +4522,7 @@ var ServerUpdateHandler = class {
|
|
|
4512
4522
|
return updatedIds;
|
|
4513
4523
|
}
|
|
4514
4524
|
async processBracketedItem(collection, item) {
|
|
4515
|
-
var _a, _b, _c, _d, _e, _f;
|
|
4525
|
+
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
4516
4526
|
const localItem = await this.dexieDb.getById(collection, item._id);
|
|
4517
4527
|
if (!localItem) {
|
|
4518
4528
|
if (item.operation === "insert" && item.bracketedData) {
|
|
@@ -4529,8 +4539,7 @@ var ServerUpdateHandler = class {
|
|
|
4529
4539
|
const revAction = this.checkRev(
|
|
4530
4540
|
item._rev,
|
|
4531
4541
|
item._ts,
|
|
4532
|
-
void 0,
|
|
4533
|
-
// bracketedData doesn't carry _lastUpdaterId
|
|
4542
|
+
(_b = item._lastUpdaterId) != null ? _b : (_a = item.bracketedData) == null ? void 0 : _a._lastUpdaterId,
|
|
4534
4543
|
localItem
|
|
4535
4544
|
);
|
|
4536
4545
|
switch (revAction) {
|
|
@@ -4559,8 +4568,8 @@ var ServerUpdateHandler = class {
|
|
|
4559
4568
|
this.callOnEbusUpdateSkipped({
|
|
4560
4569
|
collection,
|
|
4561
4570
|
_id: item._id,
|
|
4562
|
-
incomingRev: (
|
|
4563
|
-
localRev: (
|
|
4571
|
+
incomingRev: (_c = item._rev) != null ? _c : 0,
|
|
4572
|
+
localRev: (_d = localItem._rev) != null ? _d : 0,
|
|
4564
4573
|
reason: "fetch-failed"
|
|
4565
4574
|
});
|
|
4566
4575
|
return false;
|
|
@@ -4573,8 +4582,8 @@ var ServerUpdateHandler = class {
|
|
|
4573
4582
|
const diff = this.stripMetaFromDiff(item.bracketedData);
|
|
4574
4583
|
const currentInMemState = Object.assign({}, localItem, pendingChange.data);
|
|
4575
4584
|
const merged = applyObjDiff(currentInMemState, diff, item._id, "bracketed");
|
|
4576
|
-
merged._rev = (
|
|
4577
|
-
merged._ts = (
|
|
4585
|
+
merged._rev = (_e = item._rev) != null ? _e : localItem._rev;
|
|
4586
|
+
merged._ts = (_f = item._ts) != null ? _f : localItem._ts;
|
|
4578
4587
|
if (!merged._deleted && !merged._archived) {
|
|
4579
4588
|
this.deps.writeToInMemBatch(collection, [this.stripLocalFields(merged)], "upsert", {
|
|
4580
4589
|
source: "incremental"
|
|
@@ -4586,8 +4595,8 @@ var ServerUpdateHandler = class {
|
|
|
4586
4595
|
if (dirtyChange && item.operation === "update") {
|
|
4587
4596
|
const diff = this.stripMetaFromDiff(item.bracketedData);
|
|
4588
4597
|
const merged = applyObjDiff(localItem, diff, item._id, "bracketed");
|
|
4589
|
-
merged._rev = (
|
|
4590
|
-
merged._ts = (
|
|
4598
|
+
merged._rev = (_g = item._rev) != null ? _g : localItem._rev;
|
|
4599
|
+
merged._ts = (_h = item._ts) != null ? _h : localItem._ts;
|
|
4591
4600
|
if (!merged._deleted && !merged._archived) {
|
|
4592
4601
|
await this.dexieDb.insert(collection, merged);
|
|
4593
4602
|
this.deps.writeToInMemBatch(collection, [this.stripLocalFields(merged)], "upsert", {
|
|
@@ -6516,26 +6525,22 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6516
6525
|
delete update._id;
|
|
6517
6526
|
}
|
|
6518
6527
|
update = _SyncedDb.stringifyObjectIds(update);
|
|
6528
|
+
const isWriteOnly = (_b = (_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly) != null ? _b : false;
|
|
6519
6529
|
const existing = await this.dexieDb.getById(collection, id);
|
|
6520
|
-
if (!existing && !
|
|
6530
|
+
if (!existing && !isWriteOnly) {
|
|
6521
6531
|
console.warn(
|
|
6522
6532
|
`[SyncedDb] SyncedDb.save: Object ${String(id)} not found in ${collection}, creating new`
|
|
6523
6533
|
);
|
|
6524
6534
|
}
|
|
6525
6535
|
const fullChanges = __spreadProps(__spreadValues({}, update), { _lastUpdaterId: this.updaterId });
|
|
6526
|
-
const
|
|
6536
|
+
const currentMem = isWriteOnly ? null : this.inMemDb.getById(collection, id);
|
|
6537
|
+
const base = currentMem != null ? currentMem : existing;
|
|
6538
|
+
const diff = computeObjDiff(base, fullChanges);
|
|
6527
6539
|
await this.dexieDb.addDirtyChange(collection, id, diff, {
|
|
6528
6540
|
_ts: existing == null ? void 0 : existing._ts,
|
|
6529
6541
|
_rev: existing == null ? void 0 : existing._rev
|
|
6530
6542
|
});
|
|
6531
|
-
const
|
|
6532
|
-
const currentMem = isWriteOnly ? null : this.inMemDb.getById(collection, id);
|
|
6533
|
-
const merged = applyObjDiff(
|
|
6534
|
-
currentMem != null ? currentMem : existing,
|
|
6535
|
-
diff,
|
|
6536
|
-
id,
|
|
6537
|
-
collection
|
|
6538
|
-
);
|
|
6543
|
+
const merged = applyObjDiff(base, diff, id, collection);
|
|
6539
6544
|
this.pendingChanges.schedule(
|
|
6540
6545
|
collection,
|
|
6541
6546
|
id,
|