cry-synced-db-client 0.1.171 → 0.1.173
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
CHANGED
|
@@ -2,6 +2,80 @@
|
|
|
2
2
|
|
|
3
3
|
## Unreleased
|
|
4
4
|
|
|
5
|
+
### `preprocessDirtyItem` callback — per-item filter / transform before upload
|
|
6
|
+
|
|
7
|
+
New optional config callback fired for **every** dirty item just before it
|
|
8
|
+
becomes part of an `updateCollections` batch. Runs after `_ts`/`_rev` strip
|
|
9
|
+
and legacy-path fixup, so `item.update` already reflects the wire-shape
|
|
10
|
+
that would normally be uploaded.
|
|
11
|
+
|
|
12
|
+
Return values:
|
|
13
|
+
|
|
14
|
+
| Return | Result |
|
|
15
|
+
|---|---|
|
|
16
|
+
| same or modified `{_id, update}` | uploaded for this cycle |
|
|
17
|
+
| `undefined` | SKIP upload this cycle — dirty record **left untouched** |
|
|
18
|
+
| throw | SKIP upload + `console.error` — dirty record **left untouched** |
|
|
19
|
+
|
|
20
|
+
Both skip paths leave the underlying dirty change as-is so the next sync
|
|
21
|
+
cycle re-runs preprocessing. Use the regular `save`/`insert` API to
|
|
22
|
+
overwrite locally or `hardDeleteOne` to permanently remove.
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
new SyncedDb({
|
|
26
|
+
// ...
|
|
27
|
+
preprocessDirtyItem: (item, collection) => {
|
|
28
|
+
// e.g. skip uploads during a specific business window
|
|
29
|
+
if (collection === "logs" && now() < businessStart) return undefined;
|
|
30
|
+
|
|
31
|
+
// or transform: strip a field that another tab will recompute
|
|
32
|
+
const { ephemeral, ...rest } = item.update as any;
|
|
33
|
+
return { _id: item._id, update: rest };
|
|
34
|
+
},
|
|
35
|
+
});
|
|
36
|
+
```
|
|
37
|
+
|
|
38
|
+
Useful for: per-tenant data sanitization, conditional upload gating,
|
|
39
|
+
audit-trail injection.
|
|
40
|
+
|
|
41
|
+
### Nested-bracket terminal layering in `mergeDirtyPath` Case 2
|
|
42
|
+
|
|
43
|
+
When a new terminal-bracket whole-element write arrives AFTER pending
|
|
44
|
+
sub-field edits on the same element (e.g. existing `postavke[p1].kolicina
|
|
45
|
+
= 99`, new `postavke[p1] = [{_id:p1, opis:"fresh"}]`), the pending
|
|
46
|
+
sub-field values are now LAYERED into the new element value before the
|
|
47
|
+
descendant entries are dropped — producing one canonical
|
|
48
|
+
`postavke[p1] = [{_id:p1, opis:"fresh", kolicina:99}]` entry rather than
|
|
49
|
+
silently losing the prior sub-field write.
|
|
50
|
+
|
|
51
|
+
Applies only to terminal-bracket new paths whose value is element-shaped
|
|
52
|
+
(`[<el>]` insert wire form or plain object). Plain non-bracket ancestors
|
|
53
|
+
(`koraki = [...]` whole-array replace) and REMOVE markers (`undefined`)
|
|
54
|
+
keep the original "drop descendants" behavior.
|
|
55
|
+
|
|
56
|
+
Pairs with cry-db Unreleased's nested-bracket pipeline support — works
|
|
57
|
+
both with and without the lift (`containsIdArrayDescendant`) in
|
|
58
|
+
`computeArrayDiff`.
|
|
59
|
+
|
|
60
|
+
### Module-prefixed console logs
|
|
61
|
+
|
|
62
|
+
All `console.error` / `warn` / `log` / `info` / `debug` calls in
|
|
63
|
+
`src/db/**`, `src/utils/**`, and `src/types/**` are now tagged with a
|
|
64
|
+
module-scoped prefix (`[SyncedDb]`, `[SyncEngine]`, `[LeaderElection]`,
|
|
65
|
+
`[PendingChanges]`, `[Connection]`, `[CrossTabSync]`, `[InMem]`,
|
|
66
|
+
`[NetworkStatus]`, `[WakeSync]`, `[Ebus2ProxyNotifier]`, `[DexieDb]`,
|
|
67
|
+
`[CrashRecovery]`). Consumer log aggregators can now filter or
|
|
68
|
+
namespace-route library output by prefix.
|
|
69
|
+
|
|
70
|
+
### `DB-WARNING` tag for cry-db per-item warnings + `SUPRESS_DB_WARNINGS` kill-switch
|
|
71
|
+
|
|
72
|
+
Per-item `warnings` returned by `updateCollections` are now surfaced on
|
|
73
|
+
`console.error` with a `DB-WARNING [<collection>] _id=…:` prefix
|
|
74
|
+
(previously `console.warn` with a generic message), so they show up
|
|
75
|
+
alongside actual upload errors in observability pipelines.
|
|
76
|
+
`SUPRESS_DB_WARNINGS` constant in `SyncEngine.ts` silences them when
|
|
77
|
+
needed (e.g. during noisy migrations).
|
|
78
|
+
|
|
5
79
|
### Runtime collection registration (`addCollectionToSync`, `replaceSyncCollection`)
|
|
6
80
|
|
|
7
81
|
Two methods to install / replace collection configs at runtime; both load the
|
package/dist/index.js
CHANGED
|
@@ -600,6 +600,17 @@ function canExpandArrayToBrackets(value) {
|
|
|
600
600
|
}
|
|
601
601
|
return true;
|
|
602
602
|
}
|
|
603
|
+
function pickLayerTarget(newPath, newValue) {
|
|
604
|
+
if (!isTerminalBracketKey(newPath)) return null;
|
|
605
|
+
if (newValue === void 0) return null;
|
|
606
|
+
if (Array.isArray(newValue) && newValue.length === 1 && newValue[0] && typeof newValue[0] === "object") {
|
|
607
|
+
return newValue[0];
|
|
608
|
+
}
|
|
609
|
+
if (newValue && typeof newValue === "object" && !Array.isArray(newValue)) {
|
|
610
|
+
return newValue;
|
|
611
|
+
}
|
|
612
|
+
return null;
|
|
613
|
+
}
|
|
603
614
|
function mergeDirtyPath(accumulated, newPath, newValue) {
|
|
604
615
|
for (const existingKey of Object.keys(accumulated)) {
|
|
605
616
|
if (existingKey === newPath) continue;
|
|
@@ -628,14 +639,27 @@ function mergeDirtyPath(accumulated, newPath, newValue) {
|
|
|
628
639
|
break;
|
|
629
640
|
}
|
|
630
641
|
}
|
|
631
|
-
const
|
|
642
|
+
const descendants = [];
|
|
632
643
|
for (const existingKey of Object.keys(accumulated)) {
|
|
633
644
|
if (existingKey === newPath) continue;
|
|
634
645
|
if (isDescendantOrEqual(existingKey, newPath)) {
|
|
635
|
-
|
|
646
|
+
descendants.push(existingKey);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
const layerInto = pickLayerTarget(newPath, newValue);
|
|
650
|
+
if (layerInto && descendants.length > 0) {
|
|
651
|
+
for (const desc of descendants) {
|
|
652
|
+
const sepChar = desc[newPath.length];
|
|
653
|
+
const relativePath = sepChar === "[" ? desc.substring(newPath.length) : desc.substring(newPath.length + 1);
|
|
654
|
+
const descValue = accumulated[desc];
|
|
655
|
+
if (descValue === void 0) {
|
|
656
|
+
deleteByPath(layerInto, relativePath);
|
|
657
|
+
} else {
|
|
658
|
+
setByPath(layerInto, relativePath, descValue);
|
|
659
|
+
}
|
|
636
660
|
}
|
|
637
661
|
}
|
|
638
|
-
for (const k of
|
|
662
|
+
for (const k of descendants) delete accumulated[k];
|
|
639
663
|
accumulated[newPath] = newValue;
|
|
640
664
|
}
|
|
641
665
|
function mergeDirtyChanges(accumulated, newChanges) {
|
|
@@ -722,14 +746,14 @@ var InMemManager = class {
|
|
|
722
746
|
try {
|
|
723
747
|
metadatas = config.onObjectsUpdated(items);
|
|
724
748
|
} catch (err) {
|
|
725
|
-
console.error("onObjectsUpdated callback failed:", err);
|
|
749
|
+
console.error("[InMem] onObjectsUpdated callback failed:", err);
|
|
726
750
|
return;
|
|
727
751
|
}
|
|
728
752
|
} else if (config.onObjectUpdated) {
|
|
729
753
|
try {
|
|
730
754
|
metadatas = items.map((item) => config.onObjectUpdated(item));
|
|
731
755
|
} catch (err) {
|
|
732
|
-
console.error("onObjectUpdated callback failed:", err);
|
|
756
|
+
console.error("[InMem] onObjectUpdated callback failed:", err);
|
|
733
757
|
return;
|
|
734
758
|
}
|
|
735
759
|
} else {
|
|
@@ -762,14 +786,14 @@ var InMemManager = class {
|
|
|
762
786
|
try {
|
|
763
787
|
metadatas = config.onObjectsUpdated(items);
|
|
764
788
|
} catch (err) {
|
|
765
|
-
console.error("onObjectsUpdated callback failed:", err);
|
|
789
|
+
console.error("[InMem] onObjectsUpdated callback failed:", err);
|
|
766
790
|
return;
|
|
767
791
|
}
|
|
768
792
|
} else if (config.onObjectUpdated) {
|
|
769
793
|
try {
|
|
770
794
|
metadatas = items.map((item) => config.onObjectUpdated(item));
|
|
771
795
|
} catch (err) {
|
|
772
|
-
console.error("onObjectUpdated callback failed:", err);
|
|
796
|
+
console.error("[InMem] onObjectUpdated callback failed:", err);
|
|
773
797
|
return;
|
|
774
798
|
}
|
|
775
799
|
} else {
|
|
@@ -1035,7 +1059,7 @@ var LeaderElectionManager = class {
|
|
|
1035
1059
|
try {
|
|
1036
1060
|
this.leaderReelectionChannel.postMessage({ type: "reelect-leader" });
|
|
1037
1061
|
} catch (err) {
|
|
1038
|
-
console.error("Failed to broadcast leader reelection:", err);
|
|
1062
|
+
console.error("[LeaderElection] Failed to broadcast leader reelection:", err);
|
|
1039
1063
|
}
|
|
1040
1064
|
}
|
|
1041
1065
|
}
|
|
@@ -1046,7 +1070,7 @@ var LeaderElectionManager = class {
|
|
|
1046
1070
|
try {
|
|
1047
1071
|
this.callbacks.onBecameLeader();
|
|
1048
1072
|
} catch (err) {
|
|
1049
|
-
console.error("onBecameLeader callback failed:", err);
|
|
1073
|
+
console.error("[LeaderElection] onBecameLeader callback failed:", err);
|
|
1050
1074
|
}
|
|
1051
1075
|
}
|
|
1052
1076
|
}
|
|
@@ -1057,7 +1081,7 @@ var LeaderElectionManager = class {
|
|
|
1057
1081
|
try {
|
|
1058
1082
|
this.callbacks.onLostLeadership();
|
|
1059
1083
|
} catch (err) {
|
|
1060
|
-
console.error("onLostLeadership callback failed:", err);
|
|
1084
|
+
console.error("[LeaderElection] onLostLeadership callback failed:", err);
|
|
1061
1085
|
}
|
|
1062
1086
|
}
|
|
1063
1087
|
}
|
|
@@ -1066,7 +1090,7 @@ var LeaderElectionManager = class {
|
|
|
1066
1090
|
try {
|
|
1067
1091
|
this.callbacks.onInfrastructureError(type, message, error);
|
|
1068
1092
|
} catch (err) {
|
|
1069
|
-
console.error("onInfrastructureError callback failed:", err);
|
|
1093
|
+
console.error("[LeaderElection] onInfrastructureError callback failed:", err);
|
|
1070
1094
|
}
|
|
1071
1095
|
}
|
|
1072
1096
|
}
|
|
@@ -1191,7 +1215,7 @@ var CrossTabSyncManager = class {
|
|
|
1191
1215
|
try {
|
|
1192
1216
|
this.metaUpdateChannel.postMessage(payload);
|
|
1193
1217
|
} catch (err) {
|
|
1194
|
-
console.error("Failed to broadcast meta update:", err);
|
|
1218
|
+
console.error("[CrossTabSync] Failed to broadcast meta update:", err);
|
|
1195
1219
|
}
|
|
1196
1220
|
}
|
|
1197
1221
|
this.pendingBroadcasts.clear();
|
|
@@ -1237,7 +1261,7 @@ var CrossTabSyncManager = class {
|
|
|
1237
1261
|
try {
|
|
1238
1262
|
await this.deps.reloadCollectionFromDexie(collection);
|
|
1239
1263
|
} catch (err) {
|
|
1240
|
-
console.error(`Error reloading collection ${collection} from Dexie:`, err);
|
|
1264
|
+
console.error(`[CrossTabSync] Error reloading collection ${collection} from Dexie:`, err);
|
|
1241
1265
|
}
|
|
1242
1266
|
}
|
|
1243
1267
|
}
|
|
@@ -1285,11 +1309,11 @@ var CrossTabSyncManager = class {
|
|
|
1285
1309
|
};
|
|
1286
1310
|
this.callbacks.onCrossTabSync(info);
|
|
1287
1311
|
} catch (err) {
|
|
1288
|
-
console.error("onCrossTabSync callback failed:", err);
|
|
1312
|
+
console.error("[CrossTabSync] onCrossTabSync callback failed:", err);
|
|
1289
1313
|
}
|
|
1290
1314
|
}
|
|
1291
1315
|
} catch (err) {
|
|
1292
|
-
console.error(`Error handling cross-tab delta update for ${collection}:`, err);
|
|
1316
|
+
console.error(`[CrossTabSync] Error handling cross-tab delta update for ${collection}:`, err);
|
|
1293
1317
|
}
|
|
1294
1318
|
}
|
|
1295
1319
|
}
|
|
@@ -1305,7 +1329,7 @@ var CrossTabSyncManager = class {
|
|
|
1305
1329
|
try {
|
|
1306
1330
|
this.metaUpdateChannel.postMessage(payload);
|
|
1307
1331
|
} catch (err) {
|
|
1308
|
-
console.error("Failed to broadcast reload:", err);
|
|
1332
|
+
console.error("[CrossTabSync] Failed to broadcast reload:", err);
|
|
1309
1333
|
}
|
|
1310
1334
|
}
|
|
1311
1335
|
callOnInfrastructureError(type, message, error) {
|
|
@@ -1313,7 +1337,7 @@ var CrossTabSyncManager = class {
|
|
|
1313
1337
|
try {
|
|
1314
1338
|
this.callbacks.onInfrastructureError(type, message, error);
|
|
1315
1339
|
} catch (err) {
|
|
1316
|
-
console.error("onInfrastructureError callback failed:", err);
|
|
1340
|
+
console.error("[CrossTabSync] onInfrastructureError callback failed:", err);
|
|
1317
1341
|
}
|
|
1318
1342
|
}
|
|
1319
1343
|
}
|
|
@@ -1379,7 +1403,7 @@ var ConnectionManager = class {
|
|
|
1379
1403
|
} else {
|
|
1380
1404
|
this.deps.tryBecomeLeader();
|
|
1381
1405
|
this.tryGoOnline().catch((err) => {
|
|
1382
|
-
console.error("Failed to go online after forceOffline release:", err);
|
|
1406
|
+
console.error("[Connection] Failed to go online after forceOffline release:", err);
|
|
1383
1407
|
});
|
|
1384
1408
|
}
|
|
1385
1409
|
}
|
|
@@ -1403,13 +1427,13 @@ var ConnectionManager = class {
|
|
|
1403
1427
|
"ping"
|
|
1404
1428
|
);
|
|
1405
1429
|
} catch (err) {
|
|
1406
|
-
console.warn("tryGoOnline: ping failed:", err);
|
|
1430
|
+
console.warn("[Connection] tryGoOnline: ping failed:", err);
|
|
1407
1431
|
this.online = false;
|
|
1408
1432
|
return;
|
|
1409
1433
|
}
|
|
1410
1434
|
if (!pingResult) {
|
|
1411
1435
|
const url = (_a = this.restInterface.endpoint) != null ? _a : "unknown";
|
|
1412
|
-
console.warn(`Ping to ${url} failed - staying offline`);
|
|
1436
|
+
console.warn(`[Connection] Ping to ${url} failed - staying offline`);
|
|
1413
1437
|
return;
|
|
1414
1438
|
}
|
|
1415
1439
|
this.online = true;
|
|
@@ -1421,7 +1445,7 @@ var ConnectionManager = class {
|
|
|
1421
1445
|
try {
|
|
1422
1446
|
await this.deps.sync("INITIAL SYNC");
|
|
1423
1447
|
} catch (err) {
|
|
1424
|
-
console.warn("INITIAL SYNC after tryGoOnline failed (stays online):", err);
|
|
1448
|
+
console.warn("[Connection] INITIAL SYNC after tryGoOnline failed (stays online):", err);
|
|
1425
1449
|
}
|
|
1426
1450
|
} finally {
|
|
1427
1451
|
this.tryGoOnlineInFlight = false;
|
|
@@ -1437,7 +1461,7 @@ var ConnectionManager = class {
|
|
|
1437
1461
|
this.autoSyncTimer = setInterval(() => {
|
|
1438
1462
|
if (this.forcedOffline || !this.online) return;
|
|
1439
1463
|
this.deps.sync(`interval ${intervalMs}ms`).catch((err) => {
|
|
1440
|
-
console.error("Auto-sync failed:", err);
|
|
1464
|
+
console.error("[Connection] Auto-sync failed:", err);
|
|
1441
1465
|
});
|
|
1442
1466
|
}, intervalMs);
|
|
1443
1467
|
}
|
|
@@ -1446,7 +1470,7 @@ var ConnectionManager = class {
|
|
|
1446
1470
|
this.reconnectTimer = setInterval(() => {
|
|
1447
1471
|
if (this.forcedOffline || this.online || this.tryGoOnlineInFlight) return;
|
|
1448
1472
|
this.tryGoOnline().catch((err) => {
|
|
1449
|
-
console.error("Reconnect tryGoOnline failed:", err);
|
|
1473
|
+
console.error("[Connection] Reconnect tryGoOnline failed:", err);
|
|
1450
1474
|
});
|
|
1451
1475
|
}, retryMs);
|
|
1452
1476
|
}
|
|
@@ -1530,7 +1554,7 @@ var ConnectionManager = class {
|
|
|
1530
1554
|
try {
|
|
1531
1555
|
this.callbacks.onInfrastructureError(type, message, error);
|
|
1532
1556
|
} catch (err) {
|
|
1533
|
-
console.error("onInfrastructureError callback failed:", err);
|
|
1557
|
+
console.error("[Connection] onInfrastructureError callback failed:", err);
|
|
1534
1558
|
}
|
|
1535
1559
|
}
|
|
1536
1560
|
}
|
|
@@ -1543,7 +1567,7 @@ var ConnectionManager = class {
|
|
|
1543
1567
|
try {
|
|
1544
1568
|
this.callbacks.onSyncFailed(reason);
|
|
1545
1569
|
} catch (err) {
|
|
1546
|
-
console.error("onSyncFailed callback failed:", err);
|
|
1570
|
+
console.error("[Connection] onSyncFailed callback failed:", err);
|
|
1547
1571
|
}
|
|
1548
1572
|
}
|
|
1549
1573
|
}
|
|
@@ -1555,7 +1579,7 @@ var ConnectionManager = class {
|
|
|
1555
1579
|
try {
|
|
1556
1580
|
this.callbacks.onWsConnect();
|
|
1557
1581
|
} catch (err) {
|
|
1558
|
-
console.error("onWsConnect callback failed:", err);
|
|
1582
|
+
console.error("[Connection] onWsConnect callback failed:", err);
|
|
1559
1583
|
}
|
|
1560
1584
|
}
|
|
1561
1585
|
}
|
|
@@ -1567,7 +1591,7 @@ var ConnectionManager = class {
|
|
|
1567
1591
|
try {
|
|
1568
1592
|
this.callbacks.onWsDisconnect(reason);
|
|
1569
1593
|
} catch (err) {
|
|
1570
|
-
console.error("onWsDisconnect callback failed:", err);
|
|
1594
|
+
console.error("[Connection] onWsDisconnect callback failed:", err);
|
|
1571
1595
|
}
|
|
1572
1596
|
}
|
|
1573
1597
|
this.reportInfrastructureError(
|
|
@@ -1583,7 +1607,7 @@ var ConnectionManager = class {
|
|
|
1583
1607
|
try {
|
|
1584
1608
|
this.callbacks.onWsReconnect(attempt);
|
|
1585
1609
|
} catch (err) {
|
|
1586
|
-
console.error("onWsReconnect callback failed:", err);
|
|
1610
|
+
console.error("[Connection] onWsReconnect callback failed:", err);
|
|
1587
1611
|
}
|
|
1588
1612
|
}
|
|
1589
1613
|
}
|
|
@@ -2450,7 +2474,7 @@ function savePendingWrite(tenant, collection, id, delta) {
|
|
|
2450
2474
|
}
|
|
2451
2475
|
localStorage.setItem(key, dist_default.stringify(pending));
|
|
2452
2476
|
} catch (e) {
|
|
2453
|
-
console.warn("Failed to save pending write to localStorage");
|
|
2477
|
+
console.warn("[CrashRecovery] Failed to save pending write to localStorage");
|
|
2454
2478
|
}
|
|
2455
2479
|
}
|
|
2456
2480
|
function clearPendingWrite(tenant, collection, id) {
|
|
@@ -2632,7 +2656,7 @@ var PendingChangesManager = class {
|
|
|
2632
2656
|
clearPendingWrite(this.tenant, write.collection, write.id);
|
|
2633
2657
|
}
|
|
2634
2658
|
} catch (err) {
|
|
2635
|
-
console.error(`Failed to recover pending writes for ${collection}:`, err);
|
|
2659
|
+
console.error(`[PendingChanges] Failed to recover pending writes for ${collection}:`, err);
|
|
2636
2660
|
}
|
|
2637
2661
|
}
|
|
2638
2662
|
}
|
|
@@ -2682,7 +2706,7 @@ var PendingChangesManager = class {
|
|
|
2682
2706
|
calledFrom: pending.calledFrom
|
|
2683
2707
|
});
|
|
2684
2708
|
} catch (err) {
|
|
2685
|
-
console.error("onDexieWriteRequest callback failed:", err);
|
|
2709
|
+
console.error("[PendingChanges] onDexieWriteRequest callback failed:", err);
|
|
2686
2710
|
}
|
|
2687
2711
|
}
|
|
2688
2712
|
if (existing) {
|
|
@@ -2697,7 +2721,7 @@ var PendingChangesManager = class {
|
|
|
2697
2721
|
// ensure _id is after spread
|
|
2698
2722
|
});
|
|
2699
2723
|
if (typeof insertData._id === "object") {
|
|
2700
|
-
console.error(`Dexie: _id is object type in ${pending.collection}:`, typeof insertData._id, insertData._id);
|
|
2724
|
+
console.error(`[PendingChanges] Dexie: _id is object type in ${pending.collection}:`, typeof insertData._id, insertData._id);
|
|
2701
2725
|
}
|
|
2702
2726
|
await this.deps.dexieDb.insert(pending.collection, insertData);
|
|
2703
2727
|
}
|
|
@@ -2711,7 +2735,7 @@ var PendingChangesManager = class {
|
|
|
2711
2735
|
calledFrom: pending.calledFrom
|
|
2712
2736
|
});
|
|
2713
2737
|
} catch (err) {
|
|
2714
|
-
console.error("onDexieWriteResult callback failed:", err);
|
|
2738
|
+
console.error("[PendingChanges] onDexieWriteResult callback failed:", err);
|
|
2715
2739
|
}
|
|
2716
2740
|
}
|
|
2717
2741
|
clearPendingWrite(this.tenant, pending.collection, pending.id);
|
|
@@ -2725,12 +2749,12 @@ var PendingChangesManager = class {
|
|
|
2725
2749
|
calledFrom: pending.calledFrom
|
|
2726
2750
|
});
|
|
2727
2751
|
} catch (err) {
|
|
2728
|
-
console.error("onLocalstorageWriteResult callback failed:", err);
|
|
2752
|
+
console.error("[PendingChanges] onLocalstorageWriteResult callback failed:", err);
|
|
2729
2753
|
}
|
|
2730
2754
|
}
|
|
2731
2755
|
this.scheduleRestUpload();
|
|
2732
2756
|
} catch (err) {
|
|
2733
|
-
console.error("Failed to write to Dexie:", err);
|
|
2757
|
+
console.error("[PendingChanges] Failed to write to Dexie:", err);
|
|
2734
2758
|
if (this.callbacks.onDexieWriteResult) {
|
|
2735
2759
|
try {
|
|
2736
2760
|
this.callbacks.onDexieWriteResult({
|
|
@@ -2742,13 +2766,13 @@ var PendingChangesManager = class {
|
|
|
2742
2766
|
calledFrom: pending.calledFrom
|
|
2743
2767
|
});
|
|
2744
2768
|
} catch (callbackErr) {
|
|
2745
|
-
console.error("onDexieWriteResult callback failed:", callbackErr);
|
|
2769
|
+
console.error("[PendingChanges] onDexieWriteResult callback failed:", callbackErr);
|
|
2746
2770
|
}
|
|
2747
2771
|
}
|
|
2748
2772
|
const newRetryCount = pending.retryCount + 1;
|
|
2749
2773
|
if (newRetryCount >= MAX_RETRY_COUNT) {
|
|
2750
2774
|
console.error(
|
|
2751
|
-
`Max retry count (${MAX_RETRY_COUNT}) reached for pending change ${key}. Data remains in localStorage for crash recovery.`
|
|
2775
|
+
`[PendingChanges] Max retry count (${MAX_RETRY_COUNT}) reached for pending change ${key}. Data remains in localStorage for crash recovery.`
|
|
2752
2776
|
);
|
|
2753
2777
|
return;
|
|
2754
2778
|
}
|
|
@@ -2784,7 +2808,7 @@ var PendingChangesManager = class {
|
|
|
2784
2808
|
try {
|
|
2785
2809
|
await this.deps.uploadDirtyItems();
|
|
2786
2810
|
} catch (err) {
|
|
2787
|
-
console.error("REST upload failed:", err);
|
|
2811
|
+
console.error("[PendingChanges] REST upload failed:", err);
|
|
2788
2812
|
} finally {
|
|
2789
2813
|
this.isUploadingToRest = false;
|
|
2790
2814
|
resolveUpload();
|
|
@@ -2861,10 +2885,6 @@ function mergeObjectArrays(local, external, parentServerWins = false) {
|
|
|
2861
2885
|
}
|
|
2862
2886
|
if (objectItemsMissingId.length > 0) {
|
|
2863
2887
|
for (const item of objectItemsMissingId) {
|
|
2864
|
-
console.error(
|
|
2865
|
-
"[mergeObjectArrays] array element without _id \u2014 falling back to whole-array replace by higher _rev:",
|
|
2866
|
-
item
|
|
2867
|
-
);
|
|
2868
2888
|
}
|
|
2869
2889
|
return parentServerWins ? external.slice() : local.slice();
|
|
2870
2890
|
}
|
|
@@ -2992,6 +3012,7 @@ function stripServerManagedFromChanges(changes) {
|
|
|
2992
3012
|
}
|
|
2993
3013
|
|
|
2994
3014
|
// src/db/sync/SyncEngine.ts
|
|
3015
|
+
var SUPRESS_DB_WARNINGS = true;
|
|
2995
3016
|
var _SyncEngine = class _SyncEngine {
|
|
2996
3017
|
constructor(config) {
|
|
2997
3018
|
this.tenant = config.tenant;
|
|
@@ -3001,6 +3022,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3001
3022
|
this.restInterface = config.restInterface;
|
|
3002
3023
|
this.callbacks = config.callbacks;
|
|
3003
3024
|
this.deps = config.deps;
|
|
3025
|
+
this.preprocessDirtyItem = config.preprocessDirtyItem;
|
|
3004
3026
|
}
|
|
3005
3027
|
/**
|
|
3006
3028
|
* Execute full sync cycle.
|
|
@@ -3139,7 +3161,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3139
3161
|
}
|
|
3140
3162
|
} catch (err) {
|
|
3141
3163
|
console.error(
|
|
3142
|
-
"uploadDirtyItems failed (download succeeded, staying online):",
|
|
3164
|
+
"[SyncEngine] uploadDirtyItems failed (download succeeded, staying online):",
|
|
3143
3165
|
err
|
|
3144
3166
|
);
|
|
3145
3167
|
}
|
|
@@ -3165,7 +3187,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3165
3187
|
});
|
|
3166
3188
|
} catch (err) {
|
|
3167
3189
|
const reason = err instanceof Error ? err.message : String(err);
|
|
3168
|
-
console.error("Sync failed:", err);
|
|
3190
|
+
console.error("[SyncEngine] Sync failed:", err);
|
|
3169
3191
|
this.deps.onSyncFailed(`Sync failed: ${reason}`);
|
|
3170
3192
|
this.callOnSyncEnd({
|
|
3171
3193
|
durationMs: Date.now() - startTime,
|
|
@@ -3223,7 +3245,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3223
3245
|
}
|
|
3224
3246
|
if (updates.length === 0) {
|
|
3225
3247
|
console.warn(
|
|
3226
|
-
`uploadDirtyItems: ${collectionName} has ${dirtyChanges.length} dirty entries but 0 resolvable items`,
|
|
3248
|
+
`[SyncEngine] uploadDirtyItems: ${collectionName} has ${dirtyChanges.length} dirty entries but 0 resolvable items`,
|
|
3227
3249
|
skipped
|
|
3228
3250
|
);
|
|
3229
3251
|
if (this.callbacks.onUploadSkip) {
|
|
@@ -3238,7 +3260,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3238
3260
|
timestamp: /* @__PURE__ */ new Date()
|
|
3239
3261
|
});
|
|
3240
3262
|
} catch (err) {
|
|
3241
|
-
console.error("onUploadSkip callback failed:", err);
|
|
3263
|
+
console.error("[SyncEngine] onUploadSkip callback failed:", err);
|
|
3242
3264
|
}
|
|
3243
3265
|
}
|
|
3244
3266
|
continue;
|
|
@@ -3255,27 +3277,46 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3255
3277
|
timestamp: /* @__PURE__ */ new Date()
|
|
3256
3278
|
});
|
|
3257
3279
|
} catch (err) {
|
|
3258
|
-
console.error("onUploadSkip callback failed:", err);
|
|
3280
|
+
console.error("[SyncEngine] onUploadSkip callback failed:", err);
|
|
3281
|
+
}
|
|
3282
|
+
}
|
|
3283
|
+
const mappedUpdates = [];
|
|
3284
|
+
for (const item of updates) {
|
|
3285
|
+
const dirtyBaseRev = typeof item.delta._rev === "number" ? item.delta._rev : void 0;
|
|
3286
|
+
const stripped = stripServerManagedFromChanges(
|
|
3287
|
+
item.delta
|
|
3288
|
+
);
|
|
3289
|
+
const fixed = fixDotnetArrays(
|
|
3290
|
+
stripped,
|
|
3291
|
+
item.currentServerRev,
|
|
3292
|
+
dirtyBaseRev
|
|
3293
|
+
);
|
|
3294
|
+
let candidate = {
|
|
3295
|
+
_id: item._id,
|
|
3296
|
+
update: fixed
|
|
3297
|
+
};
|
|
3298
|
+
if (this.preprocessDirtyItem) {
|
|
3299
|
+
try {
|
|
3300
|
+
const processed = this.preprocessDirtyItem(candidate, collectionName);
|
|
3301
|
+
if (processed === void 0) {
|
|
3302
|
+
continue;
|
|
3303
|
+
}
|
|
3304
|
+
candidate = processed;
|
|
3305
|
+
} catch (err) {
|
|
3306
|
+
console.error(
|
|
3307
|
+
`[SyncEngine] preprocessDirtyItem(${collectionName}) failed for _id=${String(item._id)}; keeping dirty for retry:`,
|
|
3308
|
+
err
|
|
3309
|
+
);
|
|
3310
|
+
continue;
|
|
3311
|
+
}
|
|
3259
3312
|
}
|
|
3313
|
+
mappedUpdates.push(candidate);
|
|
3260
3314
|
}
|
|
3315
|
+
if (mappedUpdates.length === 0) continue;
|
|
3261
3316
|
collectionBatches.push([{
|
|
3262
3317
|
collection: collectionName,
|
|
3263
3318
|
batch: {
|
|
3264
|
-
updates:
|
|
3265
|
-
const dirtyBaseRev = typeof item.delta._rev === "number" ? item.delta._rev : void 0;
|
|
3266
|
-
const stripped = stripServerManagedFromChanges(
|
|
3267
|
-
item.delta
|
|
3268
|
-
);
|
|
3269
|
-
const fixed = fixDotnetArrays(
|
|
3270
|
-
stripped,
|
|
3271
|
-
item.currentServerRev,
|
|
3272
|
-
dirtyBaseRev
|
|
3273
|
-
);
|
|
3274
|
-
return {
|
|
3275
|
-
_id: item._id,
|
|
3276
|
-
update: fixed
|
|
3277
|
-
};
|
|
3278
|
-
}),
|
|
3319
|
+
updates: mappedUpdates,
|
|
3279
3320
|
deletes: []
|
|
3280
3321
|
}
|
|
3281
3322
|
}]);
|
|
@@ -3352,7 +3393,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3352
3393
|
}
|
|
3353
3394
|
if (ambiguous.length > 0) {
|
|
3354
3395
|
console.error(
|
|
3355
|
-
`Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
|
|
3396
|
+
`[SyncEngine] Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
|
|
3356
3397
|
);
|
|
3357
3398
|
}
|
|
3358
3399
|
if (allSuccessIds.length > 0) {
|
|
@@ -3450,14 +3491,14 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3450
3491
|
if (errors2 && errors2.length > 0) {
|
|
3451
3492
|
for (const e of errors2) {
|
|
3452
3493
|
console.error(
|
|
3453
|
-
`Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
|
|
3494
|
+
`[SyncEngine] Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
|
|
3454
3495
|
);
|
|
3455
3496
|
}
|
|
3456
3497
|
}
|
|
3457
|
-
if (warnings && warnings.length > 0) {
|
|
3498
|
+
if (warnings && warnings.length > 0 && !SUPRESS_DB_WARNINGS) {
|
|
3458
3499
|
for (const w of warnings) {
|
|
3459
|
-
console.
|
|
3460
|
-
`
|
|
3500
|
+
console.error(
|
|
3501
|
+
`[SyncEngine] DB-WARNING [${collection}] _id=${w._id}: ${w.error}`
|
|
3461
3502
|
);
|
|
3462
3503
|
}
|
|
3463
3504
|
}
|
|
@@ -3471,7 +3512,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3471
3512
|
const unacked = [...sentIds].filter((id) => !ackIds.has(id));
|
|
3472
3513
|
if (unacked.length > 0) {
|
|
3473
3514
|
console.warn(
|
|
3474
|
-
`uploadDirtyItems: ${collection}: ${unacked.length} items sent but not acknowledged:`,
|
|
3515
|
+
`[SyncEngine] uploadDirtyItems: ${collection}: ${unacked.length} items sent but not acknowledged:`,
|
|
3475
3516
|
unacked
|
|
3476
3517
|
);
|
|
3477
3518
|
}
|
|
@@ -3527,14 +3568,14 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3527
3568
|
for (const e of errors2) {
|
|
3528
3569
|
erroredIds.add(String(e._id));
|
|
3529
3570
|
console.error(
|
|
3530
|
-
`Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
|
|
3571
|
+
`[SyncEngine] Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
|
|
3531
3572
|
);
|
|
3532
3573
|
}
|
|
3533
3574
|
}
|
|
3534
|
-
if (warnings && warnings.length > 0) {
|
|
3575
|
+
if (warnings && warnings.length > 0 && !SUPRESS_DB_WARNINGS) {
|
|
3535
3576
|
for (const w of warnings) {
|
|
3536
|
-
console.
|
|
3537
|
-
`
|
|
3577
|
+
console.error(
|
|
3578
|
+
`[SyncEngine] DB-WARNING [${collection}] _id=${w._id}: ${w.error}`
|
|
3538
3579
|
);
|
|
3539
3580
|
}
|
|
3540
3581
|
}
|
|
@@ -3566,7 +3607,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3566
3607
|
}
|
|
3567
3608
|
if (ambiguous.length > 0) {
|
|
3568
3609
|
console.error(
|
|
3569
|
-
`Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
|
|
3610
|
+
`[SyncEngine] Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
|
|
3570
3611
|
);
|
|
3571
3612
|
}
|
|
3572
3613
|
if (allSuccessIds.length > 0) {
|
|
@@ -3705,7 +3746,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3705
3746
|
timestamp: /* @__PURE__ */ new Date()
|
|
3706
3747
|
});
|
|
3707
3748
|
} catch (err) {
|
|
3708
|
-
console.error("onConflictResolved callback failed:", err);
|
|
3749
|
+
console.error("[SyncEngine] onConflictResolved callback failed:", err);
|
|
3709
3750
|
}
|
|
3710
3751
|
}
|
|
3711
3752
|
return resolved;
|
|
@@ -3719,7 +3760,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3719
3760
|
try {
|
|
3720
3761
|
fn(info);
|
|
3721
3762
|
} catch (err) {
|
|
3722
|
-
console.error("Callback failed:", err);
|
|
3763
|
+
console.error("[SyncEngine] Callback failed:", err);
|
|
3723
3764
|
}
|
|
3724
3765
|
}
|
|
3725
3766
|
}
|
|
@@ -3728,7 +3769,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3728
3769
|
try {
|
|
3729
3770
|
this.callbacks.onSyncStart(info);
|
|
3730
3771
|
} catch (err) {
|
|
3731
|
-
console.error("onSyncStart callback failed:", err);
|
|
3772
|
+
console.error("[SyncEngine] onSyncStart callback failed:", err);
|
|
3732
3773
|
}
|
|
3733
3774
|
}
|
|
3734
3775
|
}
|
|
@@ -3737,7 +3778,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3737
3778
|
try {
|
|
3738
3779
|
this.callbacks.onSyncEnd(info);
|
|
3739
3780
|
} catch (err) {
|
|
3740
|
-
console.error("onSyncEnd callback failed:", err);
|
|
3781
|
+
console.error("[SyncEngine] onSyncEnd callback failed:", err);
|
|
3741
3782
|
}
|
|
3742
3783
|
}
|
|
3743
3784
|
}
|
|
@@ -3750,7 +3791,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3750
3791
|
calledFrom
|
|
3751
3792
|
});
|
|
3752
3793
|
} catch (err) {
|
|
3753
|
-
console.error("onFindNewerManyCall callback failed:", err);
|
|
3794
|
+
console.error("[SyncEngine] onFindNewerManyCall callback failed:", err);
|
|
3754
3795
|
}
|
|
3755
3796
|
}
|
|
3756
3797
|
}
|
|
@@ -3766,7 +3807,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3766
3807
|
calledFrom
|
|
3767
3808
|
});
|
|
3768
3809
|
} catch (err) {
|
|
3769
|
-
console.error("onFindNewerManyResult callback failed:", err);
|
|
3810
|
+
console.error("[SyncEngine] onFindNewerManyResult callback failed:", err);
|
|
3770
3811
|
}
|
|
3771
3812
|
}
|
|
3772
3813
|
}
|
|
@@ -3779,7 +3820,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3779
3820
|
calledFrom
|
|
3780
3821
|
});
|
|
3781
3822
|
} catch (err) {
|
|
3782
|
-
console.error("onServerWriteRequest callback failed:", err);
|
|
3823
|
+
console.error("[SyncEngine] onServerWriteRequest callback failed:", err);
|
|
3783
3824
|
}
|
|
3784
3825
|
}
|
|
3785
3826
|
}
|
|
@@ -3794,7 +3835,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3794
3835
|
calledFrom
|
|
3795
3836
|
});
|
|
3796
3837
|
} catch (err) {
|
|
3797
|
-
console.error("onServerWriteResult callback failed:", err);
|
|
3838
|
+
console.error("[SyncEngine] onServerWriteResult callback failed:", err);
|
|
3798
3839
|
}
|
|
3799
3840
|
}
|
|
3800
3841
|
}
|
|
@@ -3815,7 +3856,7 @@ var _SyncEngine = class _SyncEngine {
|
|
|
3815
3856
|
timestamp
|
|
3816
3857
|
});
|
|
3817
3858
|
} catch (err) {
|
|
3818
|
-
console.error("onServerSyncWrite callback failed:", err);
|
|
3859
|
+
console.error("[SyncEngine] onServerSyncWrite callback failed:", err);
|
|
3819
3860
|
}
|
|
3820
3861
|
}
|
|
3821
3862
|
};
|
|
@@ -4080,7 +4121,7 @@ var ServerUpdateHandler = class {
|
|
|
4080
4121
|
timestamp: /* @__PURE__ */ new Date()
|
|
4081
4122
|
});
|
|
4082
4123
|
} catch (err) {
|
|
4083
|
-
console.error("onWsNotification callback failed:", err);
|
|
4124
|
+
console.error("[ServerUpdateHandler] onWsNotification callback failed:", err);
|
|
4084
4125
|
}
|
|
4085
4126
|
}
|
|
4086
4127
|
}
|
|
@@ -4162,11 +4203,11 @@ var WakeSyncManager = class {
|
|
|
4162
4203
|
timestamp: /* @__PURE__ */ new Date()
|
|
4163
4204
|
});
|
|
4164
4205
|
} catch (err) {
|
|
4165
|
-
console.error("onWakeSync callback failed:", err);
|
|
4206
|
+
console.error("[WakeSync] onWakeSync callback failed:", err);
|
|
4166
4207
|
}
|
|
4167
4208
|
}
|
|
4168
4209
|
this.deps.sync(`wake-sync:${trigger}`).catch((err) => {
|
|
4169
|
-
console.error(`Wake sync (${trigger}) failed:`, err);
|
|
4210
|
+
console.error(`[WakeSync] Wake sync (${trigger}) failed:`, err);
|
|
4170
4211
|
});
|
|
4171
4212
|
}, this.debounceMs);
|
|
4172
4213
|
}
|
|
@@ -4235,7 +4276,7 @@ var NetworkStatusManager = class {
|
|
|
4235
4276
|
try {
|
|
4236
4277
|
this.callbacks.onBrowserNetworkChange(info);
|
|
4237
4278
|
} catch (err) {
|
|
4238
|
-
console.error("onBrowserNetworkChange callback failed:", err);
|
|
4279
|
+
console.error("[NetworkStatus] onBrowserNetworkChange callback failed:", err);
|
|
4239
4280
|
}
|
|
4240
4281
|
}
|
|
4241
4282
|
if (finalOnlineState) {
|
|
@@ -4243,7 +4284,7 @@ var NetworkStatusManager = class {
|
|
|
4243
4284
|
try {
|
|
4244
4285
|
this.callbacks.onBrowserOnline();
|
|
4245
4286
|
} catch (err) {
|
|
4246
|
-
console.error("onBrowserOnline callback failed:", err);
|
|
4287
|
+
console.error("[NetworkStatus] onBrowserOnline callback failed:", err);
|
|
4247
4288
|
}
|
|
4248
4289
|
}
|
|
4249
4290
|
} else {
|
|
@@ -4251,12 +4292,12 @@ var NetworkStatusManager = class {
|
|
|
4251
4292
|
try {
|
|
4252
4293
|
this.callbacks.onBrowserOffline();
|
|
4253
4294
|
} catch (err) {
|
|
4254
|
-
console.error("onBrowserOffline callback failed:", err);
|
|
4295
|
+
console.error("[NetworkStatus] onBrowserOffline callback failed:", err);
|
|
4255
4296
|
}
|
|
4256
4297
|
}
|
|
4257
4298
|
}
|
|
4258
4299
|
this.deps.setOnline(finalOnlineState).catch((err) => {
|
|
4259
|
-
console.error("Failed to set online status:", err);
|
|
4300
|
+
console.error("[NetworkStatus] Failed to set online status:", err);
|
|
4260
4301
|
});
|
|
4261
4302
|
}, this.debounceMs);
|
|
4262
4303
|
}
|
|
@@ -4321,14 +4362,14 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4321
4362
|
onBecameLeader: () => {
|
|
4322
4363
|
if (this.initialized && !this.connectionManager.isOnline() && !this.connectionManager.isForcedOffline()) {
|
|
4323
4364
|
this.connectionManager.tryGoOnline().catch((err) => {
|
|
4324
|
-
console.error("tryGoOnline on becameLeader failed:", err);
|
|
4365
|
+
console.error("[SyncedDb] tryGoOnline on becameLeader failed:", err);
|
|
4325
4366
|
});
|
|
4326
4367
|
}
|
|
4327
4368
|
if (config.onBecameLeader) {
|
|
4328
4369
|
try {
|
|
4329
4370
|
config.onBecameLeader();
|
|
4330
4371
|
} catch (err) {
|
|
4331
|
-
console.error("onBecameLeader callback failed:", err);
|
|
4372
|
+
console.error("[SyncedDb] onBecameLeader callback failed:", err);
|
|
4332
4373
|
}
|
|
4333
4374
|
}
|
|
4334
4375
|
},
|
|
@@ -4429,6 +4470,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4429
4470
|
collections: this.collections,
|
|
4430
4471
|
dexieDb: this.dexieDb,
|
|
4431
4472
|
restInterface: this.restInterface,
|
|
4473
|
+
preprocessDirtyItem: config.preprocessDirtyItem,
|
|
4432
4474
|
callbacks: {
|
|
4433
4475
|
onSyncStart: config.onSyncStart ? (info) => config.onSyncStart(__spreadProps(__spreadValues({}, info), { initialSync: !this._lastFullSyncDate })) : void 0,
|
|
4434
4476
|
onSyncEnd: config.onSyncEnd,
|
|
@@ -4631,7 +4673,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4631
4673
|
try {
|
|
4632
4674
|
this.onDatabaseCreated();
|
|
4633
4675
|
} catch (err) {
|
|
4634
|
-
console.error("onDatabaseCreated callback failed:", err);
|
|
4676
|
+
console.error("[SyncedDb] onDatabaseCreated callback failed:", err);
|
|
4635
4677
|
}
|
|
4636
4678
|
}
|
|
4637
4679
|
await this.pendingChanges.recoverPendingWrites();
|
|
@@ -4664,10 +4706,10 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4664
4706
|
try {
|
|
4665
4707
|
await this.serverUpdateNotifier.connect();
|
|
4666
4708
|
const ep = (_c = this.serverUpdateNotifier.endpoint) != null ? _c : "unknown";
|
|
4667
|
-
console.log(`SyncedDb: ebus-proxy connected to ${ep}`);
|
|
4709
|
+
console.log(`[SyncedDb] SyncedDb: ebus-proxy connected to ${ep}`);
|
|
4668
4710
|
} catch (err) {
|
|
4669
4711
|
const ep = (_d = this.serverUpdateNotifier.endpoint) != null ? _d : "unknown";
|
|
4670
|
-
console.warn(`SyncedDb: ebus-proxy connection to ${ep} failed`);
|
|
4712
|
+
console.warn(`[SyncedDb] SyncedDb: ebus-proxy connection to ${ep} failed`);
|
|
4671
4713
|
this.connectionManager.reportInfrastructureError(
|
|
4672
4714
|
"WEBSOCKET_CONNECTION_FAILED",
|
|
4673
4715
|
`WebSocket connection to ${ep} failed during initialization`,
|
|
@@ -4679,7 +4721,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4679
4721
|
this.beforeUnloadHandler = () => {
|
|
4680
4722
|
if (this.initialized && this.pendingChanges.hasPendingChanges()) {
|
|
4681
4723
|
console.warn(
|
|
4682
|
-
`SyncedDb: pending changes not flushed. Call close() before page unload.`
|
|
4724
|
+
`[SyncedDb] SyncedDb: pending changes not flushed. Call close() before page unload.`
|
|
4683
4725
|
);
|
|
4684
4726
|
}
|
|
4685
4727
|
};
|
|
@@ -4689,7 +4731,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4689
4731
|
this.visibilityFlushHandler = () => {
|
|
4690
4732
|
if (document.visibilityState !== "hidden") return;
|
|
4691
4733
|
this.flushToServer("visibility-hidden").catch((err) => {
|
|
4692
|
-
console.warn("flushToServer on visibility-hidden failed:", err == null ? void 0 : err.message);
|
|
4734
|
+
console.warn("[SyncedDb] flushToServer on visibility-hidden failed:", err == null ? void 0 : err.message);
|
|
4693
4735
|
});
|
|
4694
4736
|
};
|
|
4695
4737
|
document.addEventListener("visibilitychange", this.visibilityFlushHandler);
|
|
@@ -4830,6 +4872,11 @@ var _SyncedDb = class _SyncedDb {
|
|
|
4830
4872
|
async findById(collection, id, opts) {
|
|
4831
4873
|
var _a;
|
|
4832
4874
|
this.assertCollection(collection);
|
|
4875
|
+
if (!id) {
|
|
4876
|
+
const err = new Error(`[SyncedDb] findById ${collection} no id ${id}`);
|
|
4877
|
+
console.error(err);
|
|
4878
|
+
return null;
|
|
4879
|
+
}
|
|
4833
4880
|
id = this.normalizeId(id, "findById", collection);
|
|
4834
4881
|
opts = this.resolveOpts(opts);
|
|
4835
4882
|
if ((_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly) {
|
|
@@ -5062,7 +5109,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5062
5109
|
await this.syncEngine.processCollectionServerData(collection, serverData, { source: "refresh" });
|
|
5063
5110
|
}
|
|
5064
5111
|
}).catch((err) => {
|
|
5065
|
-
console.error(`referToServer failed for ${collection}:`, err);
|
|
5112
|
+
console.error(`[SyncedDb] referToServer failed for ${collection}:`, err);
|
|
5066
5113
|
});
|
|
5067
5114
|
}
|
|
5068
5115
|
/**
|
|
@@ -5083,7 +5130,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5083
5130
|
if (!serverItems || serverItems.length === 0) return;
|
|
5084
5131
|
await this.syncEngine.processCollectionServerData(collection, serverItems, { source: "incremental" });
|
|
5085
5132
|
}).catch((err) => {
|
|
5086
|
-
console.error(`refreshInBackground failed for ${collection}:`, err);
|
|
5133
|
+
console.error(`[SyncedDb] refreshInBackground failed for ${collection}:`, err);
|
|
5087
5134
|
});
|
|
5088
5135
|
}
|
|
5089
5136
|
async ensureItemsAreLoaded(collection, ids, withDeleted) {
|
|
@@ -5129,7 +5176,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5129
5176
|
id = this.normalizeId(id, "save", collection);
|
|
5130
5177
|
if ("_id" in update && !update._id) {
|
|
5131
5178
|
console.error(
|
|
5132
|
-
`SyncedDb.save("${collection}", "${String(id)}"): update._id is present but falsy (${JSON.stringify(update._id)}). Stripped from update to prevent overwriting valid id. This is a bug \u2014 the caller should not pass falsy _id in update. Data keys: [${Object.keys(update).join(", ")}]`
|
|
5179
|
+
`[SyncedDb] SyncedDb.save("${collection}", "${String(id)}"): update._id is present but falsy (${JSON.stringify(update._id)}). Stripped from update to prevent overwriting valid id. This is a bug \u2014 the caller should not pass falsy _id in update. Data keys: [${Object.keys(update).join(", ")}]`
|
|
5133
5180
|
);
|
|
5134
5181
|
delete update._id;
|
|
5135
5182
|
}
|
|
@@ -5143,7 +5190,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5143
5190
|
}
|
|
5144
5191
|
})();
|
|
5145
5192
|
console.error(
|
|
5146
|
-
`SyncedDb.save("${collection}", "${String(id)}"): update._id (${JSON.stringify(update._id)}) does NOT match id (${JSON.stringify(String(id))}). Stripped from update to prevent stuck-dirty bug. The caller likely passed a stale this._id while building update from a freshly-generated record. Data keys: [${updateKeys.join(", ")}]`
|
|
5193
|
+
`[SyncedDb] SyncedDb.save("${collection}", "${String(id)}"): update._id (${JSON.stringify(update._id)}) does NOT match id (${JSON.stringify(String(id))}). Stripped from update to prevent stuck-dirty bug. The caller likely passed a stale this._id while building update from a freshly-generated record. Data keys: [${updateKeys.join(", ")}]`
|
|
5147
5194
|
);
|
|
5148
5195
|
this.safeCallback(this.onSaveIdMismatch, {
|
|
5149
5196
|
collection,
|
|
@@ -5159,7 +5206,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5159
5206
|
const existing = await this.dexieDb.getById(collection, id);
|
|
5160
5207
|
if (!existing && !((_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly)) {
|
|
5161
5208
|
console.warn(
|
|
5162
|
-
`SyncedDb.save: Object ${String(id)} not found in ${collection}, creating new`
|
|
5209
|
+
`[SyncedDb] SyncedDb.save: Object ${String(id)} not found in ${collection}, creating new`
|
|
5163
5210
|
);
|
|
5164
5211
|
}
|
|
5165
5212
|
const fullChanges = __spreadProps(__spreadValues({}, update), { _lastUpdaterId: this.updaterId });
|
|
@@ -5209,7 +5256,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5209
5256
|
const existing = await this.dexieDb.getById(collection, id);
|
|
5210
5257
|
if (existing && !existing._deleted && !existing._archived) {
|
|
5211
5258
|
console.warn(
|
|
5212
|
-
`SyncedDb.insert: Object ${String(id)} already exists in ${collection}, overwriting`
|
|
5259
|
+
`[SyncedDb] SyncedDb.insert: Object ${String(id)} already exists in ${collection}, overwriting`
|
|
5213
5260
|
);
|
|
5214
5261
|
}
|
|
5215
5262
|
const insertChanges = __spreadProps(__spreadValues({}, data), { _lastUpdaterId: this.updaterId });
|
|
@@ -5348,7 +5395,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5348
5395
|
this.inMemManager.writeBatch(collection, [{ _id: item.id }], "delete", { source: "incremental" });
|
|
5349
5396
|
results.push(true);
|
|
5350
5397
|
} catch (err) {
|
|
5351
|
-
console.error(`Failed to hard delete ${String(item.id)}:`, err);
|
|
5398
|
+
console.error(`[SyncedDb] Failed to hard delete ${String(item.id)}:`, err);
|
|
5352
5399
|
results.push(false);
|
|
5353
5400
|
}
|
|
5354
5401
|
}
|
|
@@ -5383,7 +5430,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5383
5430
|
evictionPlan = await this._collectScopeExitPlan("auto");
|
|
5384
5431
|
} catch (err) {
|
|
5385
5432
|
console.error(
|
|
5386
|
-
"[evict] phase 1 failed (skipping bundled eviction):",
|
|
5433
|
+
"[SyncedDb] [evict] phase 1 failed (skipping bundled eviction):",
|
|
5387
5434
|
err
|
|
5388
5435
|
);
|
|
5389
5436
|
}
|
|
@@ -5400,11 +5447,11 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5400
5447
|
const now = /* @__PURE__ */ new Date();
|
|
5401
5448
|
if (!this._lastFullSyncDate) {
|
|
5402
5449
|
this._setLastInitialSync(now).catch((err) => {
|
|
5403
|
-
console.error("Failed to persist lastInitialSync:", err);
|
|
5450
|
+
console.error("[SyncedDb] Failed to persist lastInitialSync:", err);
|
|
5404
5451
|
});
|
|
5405
5452
|
}
|
|
5406
5453
|
this._setLastFullSync(now).catch((err) => {
|
|
5407
|
-
console.error("Failed to persist lastFullSync:", err);
|
|
5454
|
+
console.error("[SyncedDb] Failed to persist lastFullSync:", err);
|
|
5408
5455
|
});
|
|
5409
5456
|
}
|
|
5410
5457
|
} catch (err) {
|
|
@@ -5423,7 +5470,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5423
5470
|
);
|
|
5424
5471
|
await this._persistEvictionTimestamp();
|
|
5425
5472
|
} catch (err) {
|
|
5426
|
-
console.error("[evict] phase 3 failed:", err);
|
|
5473
|
+
console.error("[SyncedDb] [evict] phase 3 failed:", err);
|
|
5427
5474
|
}
|
|
5428
5475
|
}
|
|
5429
5476
|
}
|
|
@@ -5730,7 +5777,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5730
5777
|
serverEvictedCount = serverExits.length;
|
|
5731
5778
|
} catch (err) {
|
|
5732
5779
|
console.error(
|
|
5733
|
-
`[evict] server-assisted pass failed for ${collection} (proceeding with local-only):`,
|
|
5780
|
+
`[SyncedDb] [evict] server-assisted pass failed for ${collection} (proceeding with local-only):`,
|
|
5734
5781
|
err
|
|
5735
5782
|
);
|
|
5736
5783
|
}
|
|
@@ -5847,7 +5894,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
5847
5894
|
} catch (err) {
|
|
5848
5895
|
serverFailed = true;
|
|
5849
5896
|
console.error(
|
|
5850
|
-
"[evict] server-assisted batch failed (proceeding with local-only):",
|
|
5897
|
+
"[SyncedDb] [evict] server-assisted batch failed (proceeding with local-only):",
|
|
5851
5898
|
err
|
|
5852
5899
|
);
|
|
5853
5900
|
}
|
|
@@ -6159,7 +6206,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6159
6206
|
try {
|
|
6160
6207
|
fn(info);
|
|
6161
6208
|
} catch (err) {
|
|
6162
|
-
console.error("Callback failed:", err);
|
|
6209
|
+
console.error("[SyncedDb] Callback failed:", err);
|
|
6163
6210
|
}
|
|
6164
6211
|
}
|
|
6165
6212
|
}
|
|
@@ -6250,12 +6297,12 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6250
6297
|
normalizeId(id, method, collection) {
|
|
6251
6298
|
if (!id && id !== void 0) {
|
|
6252
6299
|
console.error(
|
|
6253
|
-
`SyncedDb.${method != null ? method : "?"}("${collection != null ? collection : "?"}"): id parameter is falsy (${JSON.stringify(id)}). This is a bug \u2014 the caller must provide a valid _id.`
|
|
6300
|
+
`[SyncedDb] SyncedDb.${method != null ? method : "?"}("${collection != null ? collection : "?"}"): id parameter is falsy (${JSON.stringify(id)}). This is a bug \u2014 the caller must provide a valid _id.`
|
|
6254
6301
|
);
|
|
6255
6302
|
}
|
|
6256
6303
|
if (typeof id === "string" && _SyncedDb.STRINGIFIED_FALSY.has(id)) {
|
|
6257
6304
|
console.error(
|
|
6258
|
-
`SyncedDb.${method != null ? method : "?"}("${collection != null ? collection : "?"}"): id is a stringified falsy value ("${id}"). This is a bug \u2014 a falsy value was coerced to string before being passed as _id.`
|
|
6305
|
+
`[SyncedDb] SyncedDb.${method != null ? method : "?"}("${collection != null ? collection : "?"}"): id is a stringified falsy value ("${id}"). This is a bug \u2014 a falsy value was coerced to string before being passed as _id.`
|
|
6259
6306
|
);
|
|
6260
6307
|
}
|
|
6261
6308
|
return typeof id === "object" && id !== null ? String(id) : id;
|
|
@@ -6267,12 +6314,12 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6267
6314
|
warnFalsyQueryId(data, method, collection) {
|
|
6268
6315
|
if ("_id" in data && !data._id) {
|
|
6269
6316
|
console.error(
|
|
6270
|
-
`SyncedDb.${method}("${collection}"): _id is present in query/data but falsy (${JSON.stringify(data._id)}). This is a bug \u2014 _id must be valid when specified. Data keys: [${Object.keys(data).join(", ")}]`
|
|
6317
|
+
`[SyncedDb] SyncedDb.${method}("${collection}"): _id is present in query/data but falsy (${JSON.stringify(data._id)}). This is a bug \u2014 _id must be valid when specified. Data keys: [${Object.keys(data).join(", ")}]`
|
|
6271
6318
|
);
|
|
6272
6319
|
}
|
|
6273
6320
|
if ("_id" in data && typeof data._id === "string" && _SyncedDb.STRINGIFIED_FALSY.has(data._id)) {
|
|
6274
6321
|
console.error(
|
|
6275
|
-
`SyncedDb.${method}("${collection}"): _id is a stringified falsy value ("${data._id}"). This is a bug \u2014 a falsy value was coerced to string before being passed as _id. Data keys: [${Object.keys(data).join(", ")}]`
|
|
6322
|
+
`[SyncedDb] SyncedDb.${method}("${collection}"): _id is a stringified falsy value ("${data._id}"). This is a bug \u2014 a falsy value was coerced to string before being passed as _id. Data keys: [${Object.keys(data).join(", ")}]`
|
|
6276
6323
|
);
|
|
6277
6324
|
}
|
|
6278
6325
|
}
|
|
@@ -6285,7 +6332,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6285
6332
|
ensureId(data, method, collection) {
|
|
6286
6333
|
if (typeof data._id === "string" && _SyncedDb.STRINGIFIED_FALSY.has(data._id)) {
|
|
6287
6334
|
console.error(
|
|
6288
|
-
`SyncedDb.${method}("${collection}"): _id is a stringified falsy value ("${data._id}"). This is a bug \u2014 a falsy value was coerced to string before being passed as _id. Data keys: [${Object.keys(data).join(", ")}]`
|
|
6335
|
+
`[SyncedDb] SyncedDb.${method}("${collection}"): _id is a stringified falsy value ("${data._id}"). This is a bug \u2014 a falsy value was coerced to string before being passed as _id. Data keys: [${Object.keys(data).join(", ")}]`
|
|
6289
6336
|
);
|
|
6290
6337
|
data._id = null;
|
|
6291
6338
|
}
|
|
@@ -6293,7 +6340,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6293
6340
|
const newId = new ObjectId2().toHexString();
|
|
6294
6341
|
if ("_id" in data) {
|
|
6295
6342
|
console.error(
|
|
6296
|
-
`SyncedDb.${method}("${collection}"): _id is present but falsy (${JSON.stringify(data._id)}). Replaced with "${newId}". This is a bug \u2014 the caller should provide a valid _id. Data keys: [${Object.keys(data).join(", ")}]`
|
|
6343
|
+
`[SyncedDb] SyncedDb.${method}("${collection}"): _id is present but falsy (${JSON.stringify(data._id)}). Replaced with "${newId}". This is a bug \u2014 the caller should provide a valid _id. Data keys: [${Object.keys(data).join(", ")}]`
|
|
6297
6344
|
);
|
|
6298
6345
|
}
|
|
6299
6346
|
data._id = newId;
|
|
@@ -6383,7 +6430,7 @@ var _SyncedDb = class _SyncedDb {
|
|
|
6383
6430
|
const drop = (reason) => {
|
|
6384
6431
|
if (dropSilently) return;
|
|
6385
6432
|
console.error(
|
|
6386
|
-
`SyncedDb
|
|
6433
|
+
`[SyncedDb] applyDiffLocally: dropping bracket-path diff entry (${reason})`,
|
|
6387
6434
|
{ collection, _id: String(id), path, value }
|
|
6388
6435
|
);
|
|
6389
6436
|
};
|
|
@@ -6599,7 +6646,7 @@ var DexieDb = class extends Dexie {
|
|
|
6599
6646
|
this.ensureStringId(item);
|
|
6600
6647
|
if (typeof item._id !== "string" || item._id.length === 0) {
|
|
6601
6648
|
console.error(
|
|
6602
|
-
`DexieDb.saveMany: skipping item with invalid _id in "${collection}"`,
|
|
6649
|
+
`[DexieDb] DexieDb.saveMany: skipping item with invalid _id in "${collection}"`,
|
|
6603
6650
|
{ _id: item._id, _idType: typeof item._id, item }
|
|
6604
6651
|
);
|
|
6605
6652
|
continue;
|
|
@@ -6612,7 +6659,7 @@ var DexieDb = class extends Dexie {
|
|
|
6612
6659
|
} catch (err) {
|
|
6613
6660
|
const ids = valid.map((it) => String(it._id));
|
|
6614
6661
|
console.error(
|
|
6615
|
-
`DexieDb.saveMany: bulkPut failed for "${collection}" (${valid.length} items):`,
|
|
6662
|
+
`[DexieDb] DexieDb.saveMany: bulkPut failed for "${collection}" (${valid.length} items):`,
|
|
6616
6663
|
err,
|
|
6617
6664
|
"_ids:",
|
|
6618
6665
|
ids
|
|
@@ -9567,7 +9614,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
|
|
|
9567
9614
|
this.reconnectAttempt = 0;
|
|
9568
9615
|
this.currentReconnectDelay = this.reconnectDelayMs;
|
|
9569
9616
|
console.log(
|
|
9570
|
-
`Ebus2Proxy connected to ${this.wsUrl} (db: ${this.dbName})`
|
|
9617
|
+
`[Ebus2ProxyNotifier] Ebus2Proxy connected to ${this.wsUrl} (db: ${this.dbName})`
|
|
9571
9618
|
);
|
|
9572
9619
|
this.sendSubscribe(`db/${this.dbName}`);
|
|
9573
9620
|
if (this.subscribeServices) {
|
|
@@ -9578,7 +9625,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
|
|
|
9578
9625
|
try {
|
|
9579
9626
|
callback();
|
|
9580
9627
|
} catch (err) {
|
|
9581
|
-
console.error("onWsConnect callback failed:", err);
|
|
9628
|
+
console.error("[Ebus2ProxyNotifier] onWsConnect callback failed:", err);
|
|
9582
9629
|
}
|
|
9583
9630
|
}
|
|
9584
9631
|
}
|
|
@@ -9593,7 +9640,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
|
|
|
9593
9640
|
try {
|
|
9594
9641
|
callback(reason);
|
|
9595
9642
|
} catch (err) {
|
|
9596
|
-
console.error("onWsDisconnect callback failed:", err);
|
|
9643
|
+
console.error("[Ebus2ProxyNotifier] onWsDisconnect callback failed:", err);
|
|
9597
9644
|
}
|
|
9598
9645
|
}
|
|
9599
9646
|
}
|
|
@@ -9602,7 +9649,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
|
|
|
9602
9649
|
}
|
|
9603
9650
|
}
|
|
9604
9651
|
handleError(event) {
|
|
9605
|
-
console.error("WebSocket error:", event);
|
|
9652
|
+
console.error("[Ebus2ProxyNotifier] WebSocket error:", event);
|
|
9606
9653
|
}
|
|
9607
9654
|
handleMessage(event) {
|
|
9608
9655
|
try {
|
|
@@ -9624,11 +9671,11 @@ var Ebus2ProxyServerUpdateNotifier = class {
|
|
|
9624
9671
|
this.handlePong();
|
|
9625
9672
|
break;
|
|
9626
9673
|
case "error":
|
|
9627
|
-
console.error("WebSocket server error:", message.error);
|
|
9674
|
+
console.error("[Ebus2ProxyNotifier] WebSocket server error:", message.error);
|
|
9628
9675
|
break;
|
|
9629
9676
|
}
|
|
9630
9677
|
} catch (err) {
|
|
9631
|
-
console.error("Failed to parse WebSocket message:", err);
|
|
9678
|
+
console.error("[Ebus2ProxyNotifier] Failed to parse WebSocket message:", err);
|
|
9632
9679
|
}
|
|
9633
9680
|
}
|
|
9634
9681
|
handleChannelMessage(message) {
|
|
@@ -9636,7 +9683,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
|
|
|
9636
9683
|
try {
|
|
9637
9684
|
this.onServicesChange(message.data);
|
|
9638
9685
|
} catch (err) {
|
|
9639
|
-
console.error("onServicesChange callback failed:", err);
|
|
9686
|
+
console.error("[Ebus2ProxyNotifier] onServicesChange callback failed:", err);
|
|
9640
9687
|
}
|
|
9641
9688
|
return;
|
|
9642
9689
|
}
|
|
@@ -9653,14 +9700,14 @@ var Ebus2ProxyServerUpdateNotifier = class {
|
|
|
9653
9700
|
try {
|
|
9654
9701
|
this.onWsNotification(payload);
|
|
9655
9702
|
} catch (err) {
|
|
9656
|
-
console.error("onWsNotification callback failed:", err);
|
|
9703
|
+
console.error("[Ebus2ProxyNotifier] onWsNotification callback failed:", err);
|
|
9657
9704
|
}
|
|
9658
9705
|
}
|
|
9659
9706
|
for (const callback of this.callbacks) {
|
|
9660
9707
|
try {
|
|
9661
9708
|
callback(payload);
|
|
9662
9709
|
} catch (err) {
|
|
9663
|
-
console.error("ServerUpdateCallback failed:", err);
|
|
9710
|
+
console.error("[Ebus2ProxyNotifier] ServerUpdateCallback failed:", err);
|
|
9664
9711
|
}
|
|
9665
9712
|
}
|
|
9666
9713
|
}
|
|
@@ -9679,14 +9726,14 @@ var Ebus2ProxyServerUpdateNotifier = class {
|
|
|
9679
9726
|
try {
|
|
9680
9727
|
callback(this.reconnectAttempt);
|
|
9681
9728
|
} catch (err) {
|
|
9682
|
-
console.error("onWsReconnect callback failed:", err);
|
|
9729
|
+
console.error("[Ebus2ProxyNotifier] onWsReconnect callback failed:", err);
|
|
9683
9730
|
}
|
|
9684
9731
|
}
|
|
9685
9732
|
this.reconnectTimer = setTimeout(() => {
|
|
9686
9733
|
this.reconnectTimer = void 0;
|
|
9687
9734
|
if (this.shouldReconnect && !this.forcedOffline) {
|
|
9688
9735
|
this.createWebSocket().catch((err) => {
|
|
9689
|
-
console.error("Reconnection failed:", err);
|
|
9736
|
+
console.error("[Ebus2ProxyNotifier] Reconnection failed:", err);
|
|
9690
9737
|
this.currentReconnectDelay = Math.min(
|
|
9691
9738
|
this.currentReconnectDelay * 2,
|
|
9692
9739
|
this.maxReconnectDelayMs
|
|
@@ -9709,7 +9756,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
|
|
|
9709
9756
|
const pingMsg = { type: "ping", id: `ping-${Date.now()}` };
|
|
9710
9757
|
this.ws.send(packr2.pack(preprocessForPack2(pingMsg)));
|
|
9711
9758
|
this.pongTimer = setTimeout(() => {
|
|
9712
|
-
console.warn("Pong timeout - closing WebSocket");
|
|
9759
|
+
console.warn("[Ebus2ProxyNotifier] Pong timeout - closing WebSocket");
|
|
9713
9760
|
if (this.ws) {
|
|
9714
9761
|
this.ws.close(4e3, "Pong timeout");
|
|
9715
9762
|
}
|
|
@@ -18,6 +18,7 @@ export declare class SyncEngine implements I_SyncEngine {
|
|
|
18
18
|
private readonly restInterface;
|
|
19
19
|
private readonly callbacks;
|
|
20
20
|
private readonly deps;
|
|
21
|
+
private readonly preprocessDirtyItem?;
|
|
21
22
|
constructor(config: SyncEngineConfig);
|
|
22
23
|
/**
|
|
23
24
|
* Execute full sync cycle.
|
|
@@ -292,6 +292,11 @@ export interface SyncEngineConfig {
|
|
|
292
292
|
collections: Map<string, CollectionConfig>;
|
|
293
293
|
dexieDb: I_DexieDb;
|
|
294
294
|
restInterface: I_RestInterface;
|
|
295
|
+
/**
|
|
296
|
+
* Optional per-item filter / transform applied before upload. See
|
|
297
|
+
* `SyncedDbConfig.preprocessDirtyItem` for full semantics.
|
|
298
|
+
*/
|
|
299
|
+
preprocessDirtyItem?: (item: import("../../types/I_SyncedDb").PreprocessDirtyItem, collection: string) => import("../../types/I_SyncedDb").PreprocessDirtyItem | undefined;
|
|
295
300
|
callbacks: SyncEngineCallbacks;
|
|
296
301
|
deps: SyncEngineDeps;
|
|
297
302
|
}
|
|
@@ -117,6 +117,16 @@ export interface ServerWriteResultInfo {
|
|
|
117
117
|
/** Where sync was called from (for debugging) */
|
|
118
118
|
calledFrom?: string;
|
|
119
119
|
}
|
|
120
|
+
/**
|
|
121
|
+
* Item handed to `preprocessDirtyItem` immediately before it would be sent
|
|
122
|
+
* to the server. Carries the per-record `_id` and the wire-form `update`
|
|
123
|
+
* payload (paths → values, with `_ts` / `_rev` already stripped and
|
|
124
|
+
* legacy `arr.0.field` paths fixed).
|
|
125
|
+
*/
|
|
126
|
+
export interface PreprocessDirtyItem {
|
|
127
|
+
_id: Id;
|
|
128
|
+
update: Partial<LocalDbEntity>;
|
|
129
|
+
}
|
|
120
130
|
/**
|
|
121
131
|
* Callback payload for a single `updateCollections` round-trip — fires
|
|
122
132
|
* once per call with both the request and either the response OR the
|
|
@@ -440,6 +450,33 @@ export interface SyncedDbConfig {
|
|
|
440
450
|
debounceDexieWritesMs?: number;
|
|
441
451
|
/** Debounce čas za pošiljanje na REST v ms (default: 1000) - po uspešnem zapisu v Dexie */
|
|
442
452
|
debounceRestWritesMs?: number;
|
|
453
|
+
/**
|
|
454
|
+
* Per-item filter / transform applied to each dirty payload just before it
|
|
455
|
+
* is sent to the server. Runs after `_ts`/`_rev` strip and legacy-path
|
|
456
|
+
* fixup, so `item.update` already reflects the wire-shape that would
|
|
457
|
+
* normally be uploaded.
|
|
458
|
+
*
|
|
459
|
+
* Return values:
|
|
460
|
+
* - the same or a modified `{ _id, update }` → use it for upload
|
|
461
|
+
* - `undefined` → SKIP upload of this item for the current cycle; the
|
|
462
|
+
* dirty change is **left untouched** so the next sync cycle
|
|
463
|
+
* re-runs preprocessing
|
|
464
|
+
* - throw → log `console.error` and SKIP upload for this item; the
|
|
465
|
+
* dirty change is **left untouched** as well (same as `undefined`,
|
|
466
|
+
* just additionally surfaced as an error)
|
|
467
|
+
*
|
|
468
|
+
* Neither return path clears or modifies the underlying dirty change —
|
|
469
|
+
* use the regular `save` / `insert` / `upsert` API to overwrite or
|
|
470
|
+
* `hardDeleteOne` to remove records.
|
|
471
|
+
*
|
|
472
|
+
* Items that survive are batched into a single `updateCollections` call
|
|
473
|
+
* per collection.
|
|
474
|
+
*
|
|
475
|
+
* @param item The candidate payload (id + wire-form update)
|
|
476
|
+
* @param collection The collection name the item belongs to
|
|
477
|
+
* @returns Modified item, the same item, or `undefined` to skip this cycle
|
|
478
|
+
*/
|
|
479
|
+
preprocessDirtyItem?: (item: PreprocessDirtyItem, collection: string) => PreprocessDirtyItem | undefined;
|
|
443
480
|
/**
|
|
444
481
|
* Callback fired on each sync failure. Unlike the removed `onForcedOffline`,
|
|
445
482
|
* this does NOT mutate online state — the library keeps trying on the next
|
|
@@ -84,9 +84,15 @@ export declare function deleteByPath(target: any, path: string): boolean;
|
|
|
84
84
|
* wrapped element[0], not into the array itself. The pending insert
|
|
85
85
|
* absorbs the sub-field edit.
|
|
86
86
|
*
|
|
87
|
-
* 2. New path is an ANCESTOR of existing keys
|
|
88
|
-
*
|
|
89
|
-
*
|
|
87
|
+
* 2. New path is an ANCESTOR of existing keys.
|
|
88
|
+
* - Plain ancestor (e.g. existing "koraki.0.diag", new "koraki" with full
|
|
89
|
+
* array): drop the descendants; the new full value supersedes any
|
|
90
|
+
* field-level deltas.
|
|
91
|
+
* - TERMINAL-bracket ancestor whose `newValue` is element-shaped
|
|
92
|
+
* (`arr[a].sub[b]` with `[<el>]` or plain object): LAYER pending
|
|
93
|
+
* descendant sub-field edits INTO `newValue` before dropping them,
|
|
94
|
+
* so the element-level write absorbs the prior sub-field-level
|
|
95
|
+
* writes instead of silently overwriting them.
|
|
90
96
|
*
|
|
91
97
|
* 3. New path is ORTHOGONAL to existing keys: simple set (Object.assign-equivalent).
|
|
92
98
|
*
|