cry-synced-db-client 2.0.5 → 2.0.6
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/dist/index.js
CHANGED
|
@@ -4525,7 +4525,7 @@ var ServerUpdateHandler = class {
|
|
|
4525
4525
|
return updatedIds;
|
|
4526
4526
|
}
|
|
4527
4527
|
async processBracketedItem(collection, item) {
|
|
4528
|
-
var _a, _b, _c, _d, _e, _f, _g, _h;
|
|
4528
|
+
var _a, _b, _c, _d, _e, _f, _g, _h, _i, _j;
|
|
4529
4529
|
const localItem = await this.dexieDb.getById(collection, item._id);
|
|
4530
4530
|
if (!localItem) {
|
|
4531
4531
|
if (item.operation === "insert" && item.bracketedData) {
|
|
@@ -4580,13 +4580,18 @@ var ServerUpdateHandler = class {
|
|
|
4580
4580
|
case "apply":
|
|
4581
4581
|
break;
|
|
4582
4582
|
}
|
|
4583
|
+
const incomingUpdaterId = (_f = item._lastUpdaterId) != null ? _f : (_e = item.bracketedData) == null ? void 0 : _e._lastUpdaterId;
|
|
4583
4584
|
const pendingChange = this.deps.getPendingChange(collection, item._id);
|
|
4584
4585
|
if (pendingChange && item.operation === "update") {
|
|
4585
|
-
const diff = this.
|
|
4586
|
+
const diff = this.dropFieldsOwnedByPending(
|
|
4587
|
+
this.stripMetaFromDiff(item.bracketedData),
|
|
4588
|
+
pendingChange.data,
|
|
4589
|
+
incomingUpdaterId
|
|
4590
|
+
);
|
|
4586
4591
|
const currentInMemState = Object.assign({}, localItem, pendingChange.data);
|
|
4587
4592
|
const merged = applyObjDiff(currentInMemState, diff, item._id, "bracketed");
|
|
4588
|
-
merged._rev = (
|
|
4589
|
-
merged._ts = (
|
|
4593
|
+
merged._rev = (_g = item._rev) != null ? _g : localItem._rev;
|
|
4594
|
+
merged._ts = (_h = item._ts) != null ? _h : localItem._ts;
|
|
4590
4595
|
if (!merged._deleted && !merged._archived) {
|
|
4591
4596
|
this.deps.writeToInMemBatch(collection, [this.stripLocalFields(merged)], "upsert", {
|
|
4592
4597
|
source: "incremental"
|
|
@@ -4596,10 +4601,14 @@ var ServerUpdateHandler = class {
|
|
|
4596
4601
|
}
|
|
4597
4602
|
const dirtyChange = await this.dexieDb.getDirtyChange(collection, item._id);
|
|
4598
4603
|
if (dirtyChange && item.operation === "update") {
|
|
4599
|
-
const diff = this.
|
|
4604
|
+
const diff = this.dropFieldsOwnedByPending(
|
|
4605
|
+
this.stripMetaFromDiff(item.bracketedData),
|
|
4606
|
+
dirtyChange,
|
|
4607
|
+
incomingUpdaterId
|
|
4608
|
+
);
|
|
4600
4609
|
const merged = applyObjDiff(localItem, diff, item._id, "bracketed");
|
|
4601
|
-
merged._rev = (
|
|
4602
|
-
merged._ts = (
|
|
4610
|
+
merged._rev = (_i = item._rev) != null ? _i : localItem._rev;
|
|
4611
|
+
merged._ts = (_j = item._ts) != null ? _j : localItem._ts;
|
|
4603
4612
|
if (!merged._deleted && !merged._archived) {
|
|
4604
4613
|
await this.dexieDb.insert(collection, merged);
|
|
4605
4614
|
this.deps.writeToInMemBatch(collection, [this.stripLocalFields(merged)], "upsert", {
|
|
@@ -4883,7 +4892,12 @@ var ServerUpdateHandler = class {
|
|
|
4883
4892
|
Object.assign(pendingChange.data, newFields);
|
|
4884
4893
|
}
|
|
4885
4894
|
const currentInMemState = Object.assign({}, localItem, pendingChange.data);
|
|
4886
|
-
const
|
|
4895
|
+
const filteredDelta = this.dropFieldsOwnedByPending(
|
|
4896
|
+
serverDelta,
|
|
4897
|
+
pendingChange.data,
|
|
4898
|
+
serverDelta._lastUpdaterId
|
|
4899
|
+
);
|
|
4900
|
+
const merged = this.mergeLocalWithDelta(currentInMemState, filteredDelta);
|
|
4887
4901
|
if (!merged._deleted && !merged._archived) {
|
|
4888
4902
|
this.deps.writeToInMemBatch(
|
|
4889
4903
|
collection,
|
|
@@ -4897,7 +4911,12 @@ var ServerUpdateHandler = class {
|
|
|
4897
4911
|
const dirtyChange = await this.dexieDb.getDirtyChange(collection, serverDelta._id);
|
|
4898
4912
|
const metaChanged = localItem._rev !== serverDelta._rev || !this.timestampsEqual(localItem._ts, serverDelta._ts);
|
|
4899
4913
|
if (dirtyChange) {
|
|
4900
|
-
const
|
|
4914
|
+
const filteredDelta = this.dropFieldsOwnedByPending(
|
|
4915
|
+
serverDelta,
|
|
4916
|
+
dirtyChange,
|
|
4917
|
+
serverDelta._lastUpdaterId
|
|
4918
|
+
);
|
|
4919
|
+
const merged = this.mergeLocalWithDelta(localItem, filteredDelta);
|
|
4901
4920
|
if (metaChanged) {
|
|
4902
4921
|
await this.dexieDb.save(collection, serverDelta._id, merged);
|
|
4903
4922
|
}
|
|
@@ -4975,6 +4994,50 @@ var ServerUpdateHandler = class {
|
|
|
4975
4994
|
}
|
|
4976
4995
|
return local;
|
|
4977
4996
|
}
|
|
4997
|
+
/**
|
|
4998
|
+
* Drop fields from an incoming server delta/diff that our OWN in-flight
|
|
4999
|
+
* change (pending or already-dirty) already explicitly touches — but ONLY
|
|
5000
|
+
* when the incoming delta was itself authored by this same client
|
|
5001
|
+
* (`incomingUpdaterId === this.updaterId`). That is the one case where we
|
|
5002
|
+
* can safely assume our own newer local pending/dirty write supersedes the
|
|
5003
|
+
* incoming payload for that field: a slower/older echo of our OWN prior
|
|
5004
|
+
* write (e.g. a lock-write's confirmation arriving after we've already
|
|
5005
|
+
* locally reverted it, via a rev/timing gap the entry-level self-echo
|
|
5006
|
+
* check in `handleServerItemUpdate`/`checkRev` didn't catch) can otherwise
|
|
5007
|
+
* resurrect a field our own newer pending change had deliberately set or
|
|
5008
|
+
* cleared — both `mergeLocalWithDelta` and `applyObjDiff` apply whatever
|
|
5009
|
+
* the incoming payload says with no awareness of a competing, newer local
|
|
5010
|
+
* intent.
|
|
5011
|
+
*
|
|
5012
|
+
* When the incoming delta comes from a genuinely different actor (a true
|
|
5013
|
+
* second writer, or `incomingUpdaterId` is absent/unknown), this is a
|
|
5014
|
+
* skip-through no-op — that delta must always apply so real-time UX stays
|
|
5015
|
+
* live (see `test/secondActor.test.ts`): the field-name overlap alone does
|
|
5016
|
+
* NOT imply staleness for another writer's edit, only for our own.
|
|
5017
|
+
*
|
|
5018
|
+
* `ownedKeys` is checked via `hasOwnProperty` (not truthiness) because a
|
|
5019
|
+
* deliberately-cleared field is `undefined`, not absent, on the owning
|
|
5020
|
+
* diff/pendingChange.data.
|
|
5021
|
+
*
|
|
5022
|
+
* Path-based (bracketed) diff keys like `koraki[id].field` are matched by
|
|
5023
|
+
* their top-level segment — if `koraki` is owned, every path under it is
|
|
5024
|
+
* dropped, which is conservative but safe (reproduced with izdajRacun.vue's
|
|
5025
|
+
* sedajObdeluje lock/unlock race via the dbbracketed/ channel, 2026-07-14).
|
|
5026
|
+
*/
|
|
5027
|
+
dropFieldsOwnedByPending(incoming, ownedKeys, incomingUpdaterId) {
|
|
5028
|
+
var _a;
|
|
5029
|
+
if (incomingUpdaterId === void 0 || incomingUpdaterId !== this.updaterId) {
|
|
5030
|
+
return incoming;
|
|
5031
|
+
}
|
|
5032
|
+
const result = {};
|
|
5033
|
+
for (const key in incoming) {
|
|
5034
|
+
if (!Object.prototype.hasOwnProperty.call(incoming, key)) continue;
|
|
5035
|
+
const topField = (_a = key.split(/[.[]/)[0]) != null ? _a : key;
|
|
5036
|
+
if (Object.prototype.hasOwnProperty.call(ownedKeys, topField)) continue;
|
|
5037
|
+
result[key] = incoming[key];
|
|
5038
|
+
}
|
|
5039
|
+
return result;
|
|
5040
|
+
}
|
|
4978
5041
|
/**
|
|
4979
5042
|
* Fields from an incoming server delta that our OWN in-flight pending
|
|
4980
5043
|
* change doesn't already touch — safe to backfill into it so a later
|
|
@@ -81,6 +81,37 @@ export declare class ServerUpdateHandler implements I_ServerUpdateHandler {
|
|
|
81
81
|
private timestampsEqual;
|
|
82
82
|
/** @mutates local — mutira vhodni objekt namesto kopiranja */
|
|
83
83
|
private mergeLocalWithDelta;
|
|
84
|
+
/**
|
|
85
|
+
* Drop fields from an incoming server delta/diff that our OWN in-flight
|
|
86
|
+
* change (pending or already-dirty) already explicitly touches — but ONLY
|
|
87
|
+
* when the incoming delta was itself authored by this same client
|
|
88
|
+
* (`incomingUpdaterId === this.updaterId`). That is the one case where we
|
|
89
|
+
* can safely assume our own newer local pending/dirty write supersedes the
|
|
90
|
+
* incoming payload for that field: a slower/older echo of our OWN prior
|
|
91
|
+
* write (e.g. a lock-write's confirmation arriving after we've already
|
|
92
|
+
* locally reverted it, via a rev/timing gap the entry-level self-echo
|
|
93
|
+
* check in `handleServerItemUpdate`/`checkRev` didn't catch) can otherwise
|
|
94
|
+
* resurrect a field our own newer pending change had deliberately set or
|
|
95
|
+
* cleared — both `mergeLocalWithDelta` and `applyObjDiff` apply whatever
|
|
96
|
+
* the incoming payload says with no awareness of a competing, newer local
|
|
97
|
+
* intent.
|
|
98
|
+
*
|
|
99
|
+
* When the incoming delta comes from a genuinely different actor (a true
|
|
100
|
+
* second writer, or `incomingUpdaterId` is absent/unknown), this is a
|
|
101
|
+
* skip-through no-op — that delta must always apply so real-time UX stays
|
|
102
|
+
* live (see `test/secondActor.test.ts`): the field-name overlap alone does
|
|
103
|
+
* NOT imply staleness for another writer's edit, only for our own.
|
|
104
|
+
*
|
|
105
|
+
* `ownedKeys` is checked via `hasOwnProperty` (not truthiness) because a
|
|
106
|
+
* deliberately-cleared field is `undefined`, not absent, on the owning
|
|
107
|
+
* diff/pendingChange.data.
|
|
108
|
+
*
|
|
109
|
+
* Path-based (bracketed) diff keys like `koraki[id].field` are matched by
|
|
110
|
+
* their top-level segment — if `koraki` is owned, every path under it is
|
|
111
|
+
* dropped, which is conservative but safe (reproduced with izdajRacun.vue's
|
|
112
|
+
* sedajObdeluje lock/unlock race via the dbbracketed/ channel, 2026-07-14).
|
|
113
|
+
*/
|
|
114
|
+
private dropFieldsOwnedByPending;
|
|
84
115
|
/**
|
|
85
116
|
* Fields from an incoming server delta that our OWN in-flight pending
|
|
86
117
|
* change doesn't already touch — safe to backfill into it so a later
|