cry-synced-db-client 2.0.7 → 2.0.9
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 +59 -0
- package/dist/src/db/sync/SyncEngine.d.ts +16 -0
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -3592,6 +3592,42 @@ var SyncEngine = class {
|
|
|
3592
3592
|
}
|
|
3593
3593
|
return out;
|
|
3594
3594
|
}
|
|
3595
|
+
/**
|
|
3596
|
+
* Diagnostic guard for the ACK writeback path: after pending writes have
|
|
3597
|
+
* been flushed and the row re-read, every plain scalar top-level field of
|
|
3598
|
+
* the just-uploaded delta should already be present with the uploaded
|
|
3599
|
+
* value. A mismatch means the re-read row still lags the server-confirmed
|
|
3600
|
+
* write — either a legitimate NEWER local edit landed after the upload
|
|
3601
|
+
* snapshot was taken (its own pending write will re-assert it), or a
|
|
3602
|
+
* residual race in the flush/re-fetch ordering. Either way, stamping the
|
|
3603
|
+
* new _rev onto such a row is exactly the mechanism that canonized stale
|
|
3604
|
+
* content in the 2026-07-17 vbptuj/obravnave incident, so it must be
|
|
3605
|
+
* observable in production logs. Comparison is deliberately limited to
|
|
3606
|
+
* plain scalar top-level keys: dot/bracket paths and object/array values
|
|
3607
|
+
* are skipped (deep-equality cost + mongo key-order make them unreliable
|
|
3608
|
+
* for a hot-path check).
|
|
3609
|
+
*/
|
|
3610
|
+
warnIfRowLagsUploadedDelta(collection, freshItem, uploaded) {
|
|
3611
|
+
if (!uploaded) return;
|
|
3612
|
+
const mismatches = [];
|
|
3613
|
+
for (const key of Object.keys(uploaded)) {
|
|
3614
|
+
if (key === "_id" || key === "_lastUpdaterId") continue;
|
|
3615
|
+
if (key.includes(".") || key.includes("[")) continue;
|
|
3616
|
+
const up = uploaded[key];
|
|
3617
|
+
if (up !== null && typeof up === "object") continue;
|
|
3618
|
+
const local = freshItem[key];
|
|
3619
|
+
if (local !== up) {
|
|
3620
|
+
mismatches.push(
|
|
3621
|
+
`${key} (local=${JSON.stringify(local)}, uploaded=${JSON.stringify(up)})`
|
|
3622
|
+
);
|
|
3623
|
+
}
|
|
3624
|
+
}
|
|
3625
|
+
if (mismatches.length > 0) {
|
|
3626
|
+
console.warn(
|
|
3627
|
+
`[SyncEngine] ACK writeback: re-read row lags uploaded delta for ${collection}:${String(freshItem._id)} \u2014 ${mismatches.join(", ")}`
|
|
3628
|
+
);
|
|
3629
|
+
}
|
|
3630
|
+
}
|
|
3595
3631
|
/**
|
|
3596
3632
|
* Upload dirty items for all collections.
|
|
3597
3633
|
*/
|
|
@@ -3803,6 +3839,7 @@ var SyncEngine = class {
|
|
|
3803
3839
|
}
|
|
3804
3840
|
throw err;
|
|
3805
3841
|
}
|
|
3842
|
+
await this.deps.flushAllPendingChanges();
|
|
3806
3843
|
let sentCount = 0;
|
|
3807
3844
|
const collectionSentCounts = {};
|
|
3808
3845
|
for (const result of results) {
|
|
@@ -3914,10 +3951,16 @@ var SyncEngine = class {
|
|
|
3914
3951
|
const inMemUpdateBatch = [];
|
|
3915
3952
|
if (saveIds.length > 0) {
|
|
3916
3953
|
const freshItems = await this.dexieDb.getByIds(collection, saveIds);
|
|
3954
|
+
const uploadedById = this.uploadedSnapshotFor(collectionBatches, collection);
|
|
3917
3955
|
for (const freshItem of freshItems) {
|
|
3918
3956
|
if (!freshItem) continue;
|
|
3919
3957
|
const revTs = revById.get(String(freshItem._id));
|
|
3920
3958
|
if (!revTs) continue;
|
|
3959
|
+
this.warnIfRowLagsUploadedDelta(
|
|
3960
|
+
collection,
|
|
3961
|
+
freshItem,
|
|
3962
|
+
uploadedById.get(String(freshItem._id))
|
|
3963
|
+
);
|
|
3921
3964
|
freshItem._rev = revTs.rev;
|
|
3922
3965
|
freshItem._ts = revTs.ts;
|
|
3923
3966
|
dexieSaveBatch.push(freshItem);
|
|
@@ -8279,6 +8322,13 @@ function canExpandArrayToBrackets(value) {
|
|
|
8279
8322
|
}
|
|
8280
8323
|
return true;
|
|
8281
8324
|
}
|
|
8325
|
+
function unwrapWholeElement(value) {
|
|
8326
|
+
if (Array.isArray(value)) {
|
|
8327
|
+
return value.length === 1 && value[0] && typeof value[0] === "object" ? value[0] : null;
|
|
8328
|
+
}
|
|
8329
|
+
if (value && typeof value === "object") return value;
|
|
8330
|
+
return null;
|
|
8331
|
+
}
|
|
8282
8332
|
function pickLayerTarget(newPath, newValue) {
|
|
8283
8333
|
if (!isTerminalBracketKey(newPath)) return null;
|
|
8284
8334
|
if (newValue === void 0) return null;
|
|
@@ -8345,6 +8395,15 @@ function mergeDirtyPath(accumulated, newPath, newValue) {
|
|
|
8345
8395
|
}
|
|
8346
8396
|
}
|
|
8347
8397
|
for (const k of descendants) delete accumulated[k];
|
|
8398
|
+
if (newValue !== void 0 && isTerminalBracketKey(newPath) && Object.prototype.hasOwnProperty.call(accumulated, newPath)) {
|
|
8399
|
+
const existingValue = accumulated[newPath];
|
|
8400
|
+
const existingElement = unwrapWholeElement(existingValue);
|
|
8401
|
+
const newElement = unwrapWholeElement(newValue);
|
|
8402
|
+
if (existingElement && newElement && (Array.isArray(existingValue) || Array.isArray(newValue))) {
|
|
8403
|
+
accumulated[newPath] = [newElement];
|
|
8404
|
+
return;
|
|
8405
|
+
}
|
|
8406
|
+
}
|
|
8348
8407
|
accumulated[newPath] = newValue;
|
|
8349
8408
|
}
|
|
8350
8409
|
function mergeDirtyChanges(accumulated, newChanges) {
|
|
@@ -54,6 +54,22 @@ export declare class SyncEngine implements I_SyncEngine {
|
|
|
54
54
|
* server-reconciled paths from local edits made during the round-trip.
|
|
55
55
|
*/
|
|
56
56
|
private uploadedSnapshotFor;
|
|
57
|
+
/**
|
|
58
|
+
* Diagnostic guard for the ACK writeback path: after pending writes have
|
|
59
|
+
* been flushed and the row re-read, every plain scalar top-level field of
|
|
60
|
+
* the just-uploaded delta should already be present with the uploaded
|
|
61
|
+
* value. A mismatch means the re-read row still lags the server-confirmed
|
|
62
|
+
* write — either a legitimate NEWER local edit landed after the upload
|
|
63
|
+
* snapshot was taken (its own pending write will re-assert it), or a
|
|
64
|
+
* residual race in the flush/re-fetch ordering. Either way, stamping the
|
|
65
|
+
* new _rev onto such a row is exactly the mechanism that canonized stale
|
|
66
|
+
* content in the 2026-07-17 vbptuj/obravnave incident, so it must be
|
|
67
|
+
* observable in production logs. Comparison is deliberately limited to
|
|
68
|
+
* plain scalar top-level keys: dot/bracket paths and object/array values
|
|
69
|
+
* are skipped (deep-equality cost + mongo key-order make them unreliable
|
|
70
|
+
* for a hot-path check).
|
|
71
|
+
*/
|
|
72
|
+
private warnIfRowLagsUploadedDelta;
|
|
57
73
|
/**
|
|
58
74
|
* Upload dirty items for all collections.
|
|
59
75
|
*/
|