cry-synced-db-client 2.0.1 → 2.0.4
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 +21 -13
- package/package.json +2 -2
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
|
@@ -509,6 +509,9 @@ function computeObjDiff(existing, update) {
|
|
|
509
509
|
continue;
|
|
510
510
|
computeDiffInto(existing[key], update[key], key, diff);
|
|
511
511
|
}
|
|
512
|
+
if (Object.prototype.hasOwnProperty.call(update, "_lastUpdaterId")) {
|
|
513
|
+
diff._lastUpdaterId = update._lastUpdaterId;
|
|
514
|
+
}
|
|
512
515
|
return diff;
|
|
513
516
|
}
|
|
514
517
|
function tokenizePath(path) {
|
|
@@ -2723,7 +2726,17 @@ var PendingChangesManager = class {
|
|
|
2723
2726
|
}
|
|
2724
2727
|
const deltaWithId = existing ? data : __spreadProps(__spreadValues({}, data), { _id: id });
|
|
2725
2728
|
savePendingWrite(this.tenant, collection, id, deltaWithId);
|
|
2726
|
-
|
|
2729
|
+
let fullData;
|
|
2730
|
+
if (existing) {
|
|
2731
|
+
fullData = __spreadProps(__spreadValues(__spreadValues({}, existing.data), data), { _id: id });
|
|
2732
|
+
for (const key2 of Object.keys(existing.data)) {
|
|
2733
|
+
if (key2 !== "_id" && !(key2 in data)) {
|
|
2734
|
+
delete fullData[key2];
|
|
2735
|
+
}
|
|
2736
|
+
}
|
|
2737
|
+
} else {
|
|
2738
|
+
fullData = __spreadProps(__spreadValues({}, data), { _id: id });
|
|
2739
|
+
}
|
|
2727
2740
|
const timer = setTimeout(() => {
|
|
2728
2741
|
this.executePendingChange(key);
|
|
2729
2742
|
}, this.debounceDexieWritesMs);
|
|
@@ -4784,8 +4797,7 @@ var ServerUpdateHandler = class {
|
|
|
4784
4797
|
return "ignore";
|
|
4785
4798
|
}
|
|
4786
4799
|
if (incomingRev === localRev + 1) {
|
|
4787
|
-
|
|
4788
|
-
if (updaterId === this.updaterId) {
|
|
4800
|
+
if (incomingUpdaterId !== void 0 && incomingUpdaterId === this.updaterId) {
|
|
4789
4801
|
return "bump-meta";
|
|
4790
4802
|
}
|
|
4791
4803
|
return "apply";
|
|
@@ -6515,26 +6527,22 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6515
6527
|
delete update._id;
|
|
6516
6528
|
}
|
|
6517
6529
|
update = _SyncedDb.stringifyObjectIds(update);
|
|
6530
|
+
const isWriteOnly = (_b = (_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly) != null ? _b : false;
|
|
6518
6531
|
const existing = await this.dexieDb.getById(collection, id);
|
|
6519
|
-
if (!existing && !
|
|
6532
|
+
if (!existing && !isWriteOnly) {
|
|
6520
6533
|
console.warn(
|
|
6521
6534
|
`[SyncedDb] SyncedDb.save: Object ${String(id)} not found in ${collection}, creating new`
|
|
6522
6535
|
);
|
|
6523
6536
|
}
|
|
6524
6537
|
const fullChanges = __spreadProps(__spreadValues({}, update), { _lastUpdaterId: this.updaterId });
|
|
6525
|
-
const
|
|
6538
|
+
const currentMem = isWriteOnly ? null : this.inMemDb.getById(collection, id);
|
|
6539
|
+
const base = currentMem != null ? currentMem : existing;
|
|
6540
|
+
const diff = computeObjDiff(base, fullChanges);
|
|
6526
6541
|
await this.dexieDb.addDirtyChange(collection, id, diff, {
|
|
6527
6542
|
_ts: existing == null ? void 0 : existing._ts,
|
|
6528
6543
|
_rev: existing == null ? void 0 : existing._rev
|
|
6529
6544
|
});
|
|
6530
|
-
const
|
|
6531
|
-
const currentMem = isWriteOnly ? null : this.inMemDb.getById(collection, id);
|
|
6532
|
-
const merged = applyObjDiff(
|
|
6533
|
-
currentMem != null ? currentMem : existing,
|
|
6534
|
-
diff,
|
|
6535
|
-
id,
|
|
6536
|
-
collection
|
|
6537
|
-
);
|
|
6545
|
+
const merged = applyObjDiff(base, diff, id, collection);
|
|
6538
6546
|
this.pendingChanges.schedule(
|
|
6539
6547
|
collection,
|
|
6540
6548
|
id,
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "cry-synced-db-client",
|
|
3
|
-
"version": "2.0.
|
|
3
|
+
"version": "2.0.4",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"module": "./dist/index.js",
|
|
@@ -37,7 +37,7 @@
|
|
|
37
37
|
"vitest": "^4.1.10"
|
|
38
38
|
},
|
|
39
39
|
"dependencies": {
|
|
40
|
-
"cry-db": "^2.6.
|
|
40
|
+
"cry-db": "^2.6.3",
|
|
41
41
|
"cry-helpers": "^2.1.207",
|
|
42
42
|
"msgpackr": "^2.0.4",
|
|
43
43
|
"superjson": "^2.2.6"
|