cry-synced-db-client 0.1.170 → 0.1.172

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
@@ -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 toDelete = [];
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
- toDelete.push(existingKey);
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 toDelete) delete accumulated[k];
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 {
@@ -876,6 +900,7 @@ var LeaderElectionManager = class {
876
900
  this.isLeaderFlag = false;
877
901
  this.closing = false;
878
902
  this.releasingDueToVisibility = false;
903
+ this._followerSince = /* @__PURE__ */ new Date();
879
904
  this.tenant = config.tenant;
880
905
  this.windowId = config.windowId;
881
906
  this.callbacks = config.callbacks;
@@ -973,6 +998,21 @@ var LeaderElectionManager = class {
973
998
  isLeader() {
974
999
  return this.isLeaderFlag;
975
1000
  }
1001
+ /**
1002
+ * Returns the timestamp when this tab became leader, or undefined if currently a follower.
1003
+ * Cleared on transition to follower.
1004
+ */
1005
+ leaderSince() {
1006
+ return this._leaderSince;
1007
+ }
1008
+ /**
1009
+ * Returns the timestamp when this tab became follower, or undefined if currently a leader.
1010
+ * Initialized at construction (every tab starts as a follower until it claims the lock).
1011
+ * Cleared on transition to leader.
1012
+ */
1013
+ followerSince() {
1014
+ return this._followerSince;
1015
+ }
976
1016
  /**
977
1017
  * Cleanup resources.
978
1018
  */
@@ -1019,25 +1059,29 @@ var LeaderElectionManager = class {
1019
1059
  try {
1020
1060
  this.leaderReelectionChannel.postMessage({ type: "reelect-leader" });
1021
1061
  } catch (err) {
1022
- console.error("Failed to broadcast leader reelection:", err);
1062
+ console.error("[LeaderElection] Failed to broadcast leader reelection:", err);
1023
1063
  }
1024
1064
  }
1025
1065
  }
1026
1066
  callOnBecameLeader() {
1067
+ this._leaderSince = /* @__PURE__ */ new Date();
1068
+ this._followerSince = void 0;
1027
1069
  if (this.callbacks.onBecameLeader) {
1028
1070
  try {
1029
1071
  this.callbacks.onBecameLeader();
1030
1072
  } catch (err) {
1031
- console.error("onBecameLeader callback failed:", err);
1073
+ console.error("[LeaderElection] onBecameLeader callback failed:", err);
1032
1074
  }
1033
1075
  }
1034
1076
  }
1035
1077
  callOnLostLeadership() {
1078
+ this._followerSince = /* @__PURE__ */ new Date();
1079
+ this._leaderSince = void 0;
1036
1080
  if (this.callbacks.onLostLeadership) {
1037
1081
  try {
1038
1082
  this.callbacks.onLostLeadership();
1039
1083
  } catch (err) {
1040
- console.error("onLostLeadership callback failed:", err);
1084
+ console.error("[LeaderElection] onLostLeadership callback failed:", err);
1041
1085
  }
1042
1086
  }
1043
1087
  }
@@ -1046,7 +1090,7 @@ var LeaderElectionManager = class {
1046
1090
  try {
1047
1091
  this.callbacks.onInfrastructureError(type, message, error);
1048
1092
  } catch (err) {
1049
- console.error("onInfrastructureError callback failed:", err);
1093
+ console.error("[LeaderElection] onInfrastructureError callback failed:", err);
1050
1094
  }
1051
1095
  }
1052
1096
  }
@@ -1171,7 +1215,7 @@ var CrossTabSyncManager = class {
1171
1215
  try {
1172
1216
  this.metaUpdateChannel.postMessage(payload);
1173
1217
  } catch (err) {
1174
- console.error("Failed to broadcast meta update:", err);
1218
+ console.error("[CrossTabSync] Failed to broadcast meta update:", err);
1175
1219
  }
1176
1220
  }
1177
1221
  this.pendingBroadcasts.clear();
@@ -1217,7 +1261,7 @@ var CrossTabSyncManager = class {
1217
1261
  try {
1218
1262
  await this.deps.reloadCollectionFromDexie(collection);
1219
1263
  } catch (err) {
1220
- console.error(`Error reloading collection ${collection} from Dexie:`, err);
1264
+ console.error(`[CrossTabSync] Error reloading collection ${collection} from Dexie:`, err);
1221
1265
  }
1222
1266
  }
1223
1267
  }
@@ -1265,11 +1309,11 @@ var CrossTabSyncManager = class {
1265
1309
  };
1266
1310
  this.callbacks.onCrossTabSync(info);
1267
1311
  } catch (err) {
1268
- console.error("onCrossTabSync callback failed:", err);
1312
+ console.error("[CrossTabSync] onCrossTabSync callback failed:", err);
1269
1313
  }
1270
1314
  }
1271
1315
  } catch (err) {
1272
- console.error(`Error handling cross-tab delta update for ${collection}:`, err);
1316
+ console.error(`[CrossTabSync] Error handling cross-tab delta update for ${collection}:`, err);
1273
1317
  }
1274
1318
  }
1275
1319
  }
@@ -1285,7 +1329,7 @@ var CrossTabSyncManager = class {
1285
1329
  try {
1286
1330
  this.metaUpdateChannel.postMessage(payload);
1287
1331
  } catch (err) {
1288
- console.error("Failed to broadcast reload:", err);
1332
+ console.error("[CrossTabSync] Failed to broadcast reload:", err);
1289
1333
  }
1290
1334
  }
1291
1335
  callOnInfrastructureError(type, message, error) {
@@ -1293,7 +1337,7 @@ var CrossTabSyncManager = class {
1293
1337
  try {
1294
1338
  this.callbacks.onInfrastructureError(type, message, error);
1295
1339
  } catch (err) {
1296
- console.error("onInfrastructureError callback failed:", err);
1340
+ console.error("[CrossTabSync] onInfrastructureError callback failed:", err);
1297
1341
  }
1298
1342
  }
1299
1343
  }
@@ -1359,7 +1403,7 @@ var ConnectionManager = class {
1359
1403
  } else {
1360
1404
  this.deps.tryBecomeLeader();
1361
1405
  this.tryGoOnline().catch((err) => {
1362
- console.error("Failed to go online after forceOffline release:", err);
1406
+ console.error("[Connection] Failed to go online after forceOffline release:", err);
1363
1407
  });
1364
1408
  }
1365
1409
  }
@@ -1383,13 +1427,13 @@ var ConnectionManager = class {
1383
1427
  "ping"
1384
1428
  );
1385
1429
  } catch (err) {
1386
- console.warn("tryGoOnline: ping failed:", err);
1430
+ console.warn("[Connection] tryGoOnline: ping failed:", err);
1387
1431
  this.online = false;
1388
1432
  return;
1389
1433
  }
1390
1434
  if (!pingResult) {
1391
1435
  const url = (_a = this.restInterface.endpoint) != null ? _a : "unknown";
1392
- console.warn(`Ping to ${url} failed - staying offline`);
1436
+ console.warn(`[Connection] Ping to ${url} failed - staying offline`);
1393
1437
  return;
1394
1438
  }
1395
1439
  this.online = true;
@@ -1401,7 +1445,7 @@ var ConnectionManager = class {
1401
1445
  try {
1402
1446
  await this.deps.sync("INITIAL SYNC");
1403
1447
  } catch (err) {
1404
- console.warn("INITIAL SYNC after tryGoOnline failed (stays online):", err);
1448
+ console.warn("[Connection] INITIAL SYNC after tryGoOnline failed (stays online):", err);
1405
1449
  }
1406
1450
  } finally {
1407
1451
  this.tryGoOnlineInFlight = false;
@@ -1417,7 +1461,7 @@ var ConnectionManager = class {
1417
1461
  this.autoSyncTimer = setInterval(() => {
1418
1462
  if (this.forcedOffline || !this.online) return;
1419
1463
  this.deps.sync(`interval ${intervalMs}ms`).catch((err) => {
1420
- console.error("Auto-sync failed:", err);
1464
+ console.error("[Connection] Auto-sync failed:", err);
1421
1465
  });
1422
1466
  }, intervalMs);
1423
1467
  }
@@ -1426,7 +1470,7 @@ var ConnectionManager = class {
1426
1470
  this.reconnectTimer = setInterval(() => {
1427
1471
  if (this.forcedOffline || this.online || this.tryGoOnlineInFlight) return;
1428
1472
  this.tryGoOnline().catch((err) => {
1429
- console.error("Reconnect tryGoOnline failed:", err);
1473
+ console.error("[Connection] Reconnect tryGoOnline failed:", err);
1430
1474
  });
1431
1475
  }, retryMs);
1432
1476
  }
@@ -1510,7 +1554,7 @@ var ConnectionManager = class {
1510
1554
  try {
1511
1555
  this.callbacks.onInfrastructureError(type, message, error);
1512
1556
  } catch (err) {
1513
- console.error("onInfrastructureError callback failed:", err);
1557
+ console.error("[Connection] onInfrastructureError callback failed:", err);
1514
1558
  }
1515
1559
  }
1516
1560
  }
@@ -1523,7 +1567,7 @@ var ConnectionManager = class {
1523
1567
  try {
1524
1568
  this.callbacks.onSyncFailed(reason);
1525
1569
  } catch (err) {
1526
- console.error("onSyncFailed callback failed:", err);
1570
+ console.error("[Connection] onSyncFailed callback failed:", err);
1527
1571
  }
1528
1572
  }
1529
1573
  }
@@ -1535,7 +1579,7 @@ var ConnectionManager = class {
1535
1579
  try {
1536
1580
  this.callbacks.onWsConnect();
1537
1581
  } catch (err) {
1538
- console.error("onWsConnect callback failed:", err);
1582
+ console.error("[Connection] onWsConnect callback failed:", err);
1539
1583
  }
1540
1584
  }
1541
1585
  }
@@ -1547,7 +1591,7 @@ var ConnectionManager = class {
1547
1591
  try {
1548
1592
  this.callbacks.onWsDisconnect(reason);
1549
1593
  } catch (err) {
1550
- console.error("onWsDisconnect callback failed:", err);
1594
+ console.error("[Connection] onWsDisconnect callback failed:", err);
1551
1595
  }
1552
1596
  }
1553
1597
  this.reportInfrastructureError(
@@ -1563,7 +1607,7 @@ var ConnectionManager = class {
1563
1607
  try {
1564
1608
  this.callbacks.onWsReconnect(attempt);
1565
1609
  } catch (err) {
1566
- console.error("onWsReconnect callback failed:", err);
1610
+ console.error("[Connection] onWsReconnect callback failed:", err);
1567
1611
  }
1568
1612
  }
1569
1613
  }
@@ -2430,7 +2474,7 @@ function savePendingWrite(tenant, collection, id, delta) {
2430
2474
  }
2431
2475
  localStorage.setItem(key, dist_default.stringify(pending));
2432
2476
  } catch (e) {
2433
- console.warn("Failed to save pending write to localStorage");
2477
+ console.warn("[CrashRecovery] Failed to save pending write to localStorage");
2434
2478
  }
2435
2479
  }
2436
2480
  function clearPendingWrite(tenant, collection, id) {
@@ -2612,7 +2656,7 @@ var PendingChangesManager = class {
2612
2656
  clearPendingWrite(this.tenant, write.collection, write.id);
2613
2657
  }
2614
2658
  } catch (err) {
2615
- console.error(`Failed to recover pending writes for ${collection}:`, err);
2659
+ console.error(`[PendingChanges] Failed to recover pending writes for ${collection}:`, err);
2616
2660
  }
2617
2661
  }
2618
2662
  }
@@ -2662,7 +2706,7 @@ var PendingChangesManager = class {
2662
2706
  calledFrom: pending.calledFrom
2663
2707
  });
2664
2708
  } catch (err) {
2665
- console.error("onDexieWriteRequest callback failed:", err);
2709
+ console.error("[PendingChanges] onDexieWriteRequest callback failed:", err);
2666
2710
  }
2667
2711
  }
2668
2712
  if (existing) {
@@ -2677,7 +2721,7 @@ var PendingChangesManager = class {
2677
2721
  // ensure _id is after spread
2678
2722
  });
2679
2723
  if (typeof insertData._id === "object") {
2680
- 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);
2681
2725
  }
2682
2726
  await this.deps.dexieDb.insert(pending.collection, insertData);
2683
2727
  }
@@ -2691,7 +2735,7 @@ var PendingChangesManager = class {
2691
2735
  calledFrom: pending.calledFrom
2692
2736
  });
2693
2737
  } catch (err) {
2694
- console.error("onDexieWriteResult callback failed:", err);
2738
+ console.error("[PendingChanges] onDexieWriteResult callback failed:", err);
2695
2739
  }
2696
2740
  }
2697
2741
  clearPendingWrite(this.tenant, pending.collection, pending.id);
@@ -2705,12 +2749,12 @@ var PendingChangesManager = class {
2705
2749
  calledFrom: pending.calledFrom
2706
2750
  });
2707
2751
  } catch (err) {
2708
- console.error("onLocalstorageWriteResult callback failed:", err);
2752
+ console.error("[PendingChanges] onLocalstorageWriteResult callback failed:", err);
2709
2753
  }
2710
2754
  }
2711
2755
  this.scheduleRestUpload();
2712
2756
  } catch (err) {
2713
- console.error("Failed to write to Dexie:", err);
2757
+ console.error("[PendingChanges] Failed to write to Dexie:", err);
2714
2758
  if (this.callbacks.onDexieWriteResult) {
2715
2759
  try {
2716
2760
  this.callbacks.onDexieWriteResult({
@@ -2722,13 +2766,13 @@ var PendingChangesManager = class {
2722
2766
  calledFrom: pending.calledFrom
2723
2767
  });
2724
2768
  } catch (callbackErr) {
2725
- console.error("onDexieWriteResult callback failed:", callbackErr);
2769
+ console.error("[PendingChanges] onDexieWriteResult callback failed:", callbackErr);
2726
2770
  }
2727
2771
  }
2728
2772
  const newRetryCount = pending.retryCount + 1;
2729
2773
  if (newRetryCount >= MAX_RETRY_COUNT) {
2730
2774
  console.error(
2731
- `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.`
2732
2776
  );
2733
2777
  return;
2734
2778
  }
@@ -2764,7 +2808,7 @@ var PendingChangesManager = class {
2764
2808
  try {
2765
2809
  await this.deps.uploadDirtyItems();
2766
2810
  } catch (err) {
2767
- console.error("REST upload failed:", err);
2811
+ console.error("[PendingChanges] REST upload failed:", err);
2768
2812
  } finally {
2769
2813
  this.isUploadingToRest = false;
2770
2814
  resolveUpload();
@@ -2841,10 +2885,6 @@ function mergeObjectArrays(local, external, parentServerWins = false) {
2841
2885
  }
2842
2886
  if (objectItemsMissingId.length > 0) {
2843
2887
  for (const item of objectItemsMissingId) {
2844
- console.error(
2845
- "[mergeObjectArrays] array element without _id \u2014 falling back to whole-array replace by higher _rev:",
2846
- item
2847
- );
2848
2888
  }
2849
2889
  return parentServerWins ? external.slice() : local.slice();
2850
2890
  }
@@ -2972,6 +3012,7 @@ function stripServerManagedFromChanges(changes) {
2972
3012
  }
2973
3013
 
2974
3014
  // src/db/sync/SyncEngine.ts
3015
+ var SUPRESS_DB_WARNINGS = false;
2975
3016
  var _SyncEngine = class _SyncEngine {
2976
3017
  constructor(config) {
2977
3018
  this.tenant = config.tenant;
@@ -3119,7 +3160,7 @@ var _SyncEngine = class _SyncEngine {
3119
3160
  }
3120
3161
  } catch (err) {
3121
3162
  console.error(
3122
- "uploadDirtyItems failed (download succeeded, staying online):",
3163
+ "[SyncEngine] uploadDirtyItems failed (download succeeded, staying online):",
3123
3164
  err
3124
3165
  );
3125
3166
  }
@@ -3145,7 +3186,7 @@ var _SyncEngine = class _SyncEngine {
3145
3186
  });
3146
3187
  } catch (err) {
3147
3188
  const reason = err instanceof Error ? err.message : String(err);
3148
- console.error("Sync failed:", err);
3189
+ console.error("[SyncEngine] Sync failed:", err);
3149
3190
  this.deps.onSyncFailed(`Sync failed: ${reason}`);
3150
3191
  this.callOnSyncEnd({
3151
3192
  durationMs: Date.now() - startTime,
@@ -3203,7 +3244,7 @@ var _SyncEngine = class _SyncEngine {
3203
3244
  }
3204
3245
  if (updates.length === 0) {
3205
3246
  console.warn(
3206
- `uploadDirtyItems: ${collectionName} has ${dirtyChanges.length} dirty entries but 0 resolvable items`,
3247
+ `[SyncEngine] uploadDirtyItems: ${collectionName} has ${dirtyChanges.length} dirty entries but 0 resolvable items`,
3207
3248
  skipped
3208
3249
  );
3209
3250
  if (this.callbacks.onUploadSkip) {
@@ -3218,7 +3259,7 @@ var _SyncEngine = class _SyncEngine {
3218
3259
  timestamp: /* @__PURE__ */ new Date()
3219
3260
  });
3220
3261
  } catch (err) {
3221
- console.error("onUploadSkip callback failed:", err);
3262
+ console.error("[SyncEngine] onUploadSkip callback failed:", err);
3222
3263
  }
3223
3264
  }
3224
3265
  continue;
@@ -3235,7 +3276,7 @@ var _SyncEngine = class _SyncEngine {
3235
3276
  timestamp: /* @__PURE__ */ new Date()
3236
3277
  });
3237
3278
  } catch (err) {
3238
- console.error("onUploadSkip callback failed:", err);
3279
+ console.error("[SyncEngine] onUploadSkip callback failed:", err);
3239
3280
  }
3240
3281
  }
3241
3282
  collectionBatches.push([{
@@ -3332,7 +3373,7 @@ var _SyncEngine = class _SyncEngine {
3332
3373
  }
3333
3374
  if (ambiguous.length > 0) {
3334
3375
  console.error(
3335
- `Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
3376
+ `[SyncEngine] Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
3336
3377
  );
3337
3378
  }
3338
3379
  if (allSuccessIds.length > 0) {
@@ -3430,14 +3471,14 @@ var _SyncEngine = class _SyncEngine {
3430
3471
  if (errors2 && errors2.length > 0) {
3431
3472
  for (const e of errors2) {
3432
3473
  console.error(
3433
- `Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
3474
+ `[SyncEngine] Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
3434
3475
  );
3435
3476
  }
3436
3477
  }
3437
- if (warnings && warnings.length > 0) {
3478
+ if (warnings && warnings.length > 0 && !SUPRESS_DB_WARNINGS) {
3438
3479
  for (const w of warnings) {
3439
- console.warn(
3440
- `Sync upload warning [${collection}] _id=${w._id}: ${w.error}`
3480
+ console.error(
3481
+ `[SyncEngine] DB-WARNING [${collection}] _id=${w._id}: ${w.error}`
3441
3482
  );
3442
3483
  }
3443
3484
  }
@@ -3451,7 +3492,7 @@ var _SyncEngine = class _SyncEngine {
3451
3492
  const unacked = [...sentIds].filter((id) => !ackIds.has(id));
3452
3493
  if (unacked.length > 0) {
3453
3494
  console.warn(
3454
- `uploadDirtyItems: ${collection}: ${unacked.length} items sent but not acknowledged:`,
3495
+ `[SyncEngine] uploadDirtyItems: ${collection}: ${unacked.length} items sent but not acknowledged:`,
3455
3496
  unacked
3456
3497
  );
3457
3498
  }
@@ -3507,14 +3548,14 @@ var _SyncEngine = class _SyncEngine {
3507
3548
  for (const e of errors2) {
3508
3549
  erroredIds.add(String(e._id));
3509
3550
  console.error(
3510
- `Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
3551
+ `[SyncEngine] Sync upload error [${collection}] _id=${e._id}: ${e.error} \u2014 dirty entry will persist until retry`
3511
3552
  );
3512
3553
  }
3513
3554
  }
3514
- if (warnings && warnings.length > 0) {
3555
+ if (warnings && warnings.length > 0 && !SUPRESS_DB_WARNINGS) {
3515
3556
  for (const w of warnings) {
3516
- console.warn(
3517
- `Sync upload warning [${collection}] _id=${w._id}: ${w.error}`
3557
+ console.error(
3558
+ `[SyncEngine] DB-WARNING [${collection}] _id=${w._id}: ${w.error}`
3518
3559
  );
3519
3560
  }
3520
3561
  }
@@ -3546,7 +3587,7 @@ var _SyncEngine = class _SyncEngine {
3546
3587
  }
3547
3588
  if (ambiguous.length > 0) {
3548
3589
  console.error(
3549
- `Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
3590
+ `[SyncEngine] Sync upload [${collection}]: ${ambiguous.length} id(s) appeared in BOTH inserted/updated/deleted AND errors[] \u2014 keeping dirty for safety. _ids: ${ambiguous.join(", ")}`
3550
3591
  );
3551
3592
  }
3552
3593
  if (allSuccessIds.length > 0) {
@@ -3685,7 +3726,7 @@ var _SyncEngine = class _SyncEngine {
3685
3726
  timestamp: /* @__PURE__ */ new Date()
3686
3727
  });
3687
3728
  } catch (err) {
3688
- console.error("onConflictResolved callback failed:", err);
3729
+ console.error("[SyncEngine] onConflictResolved callback failed:", err);
3689
3730
  }
3690
3731
  }
3691
3732
  return resolved;
@@ -3699,7 +3740,7 @@ var _SyncEngine = class _SyncEngine {
3699
3740
  try {
3700
3741
  fn(info);
3701
3742
  } catch (err) {
3702
- console.error("Callback failed:", err);
3743
+ console.error("[SyncEngine] Callback failed:", err);
3703
3744
  }
3704
3745
  }
3705
3746
  }
@@ -3708,7 +3749,7 @@ var _SyncEngine = class _SyncEngine {
3708
3749
  try {
3709
3750
  this.callbacks.onSyncStart(info);
3710
3751
  } catch (err) {
3711
- console.error("onSyncStart callback failed:", err);
3752
+ console.error("[SyncEngine] onSyncStart callback failed:", err);
3712
3753
  }
3713
3754
  }
3714
3755
  }
@@ -3717,7 +3758,7 @@ var _SyncEngine = class _SyncEngine {
3717
3758
  try {
3718
3759
  this.callbacks.onSyncEnd(info);
3719
3760
  } catch (err) {
3720
- console.error("onSyncEnd callback failed:", err);
3761
+ console.error("[SyncEngine] onSyncEnd callback failed:", err);
3721
3762
  }
3722
3763
  }
3723
3764
  }
@@ -3730,7 +3771,7 @@ var _SyncEngine = class _SyncEngine {
3730
3771
  calledFrom
3731
3772
  });
3732
3773
  } catch (err) {
3733
- console.error("onFindNewerManyCall callback failed:", err);
3774
+ console.error("[SyncEngine] onFindNewerManyCall callback failed:", err);
3734
3775
  }
3735
3776
  }
3736
3777
  }
@@ -3746,7 +3787,7 @@ var _SyncEngine = class _SyncEngine {
3746
3787
  calledFrom
3747
3788
  });
3748
3789
  } catch (err) {
3749
- console.error("onFindNewerManyResult callback failed:", err);
3790
+ console.error("[SyncEngine] onFindNewerManyResult callback failed:", err);
3750
3791
  }
3751
3792
  }
3752
3793
  }
@@ -3759,7 +3800,7 @@ var _SyncEngine = class _SyncEngine {
3759
3800
  calledFrom
3760
3801
  });
3761
3802
  } catch (err) {
3762
- console.error("onServerWriteRequest callback failed:", err);
3803
+ console.error("[SyncEngine] onServerWriteRequest callback failed:", err);
3763
3804
  }
3764
3805
  }
3765
3806
  }
@@ -3774,7 +3815,7 @@ var _SyncEngine = class _SyncEngine {
3774
3815
  calledFrom
3775
3816
  });
3776
3817
  } catch (err) {
3777
- console.error("onServerWriteResult callback failed:", err);
3818
+ console.error("[SyncEngine] onServerWriteResult callback failed:", err);
3778
3819
  }
3779
3820
  }
3780
3821
  }
@@ -3795,7 +3836,7 @@ var _SyncEngine = class _SyncEngine {
3795
3836
  timestamp
3796
3837
  });
3797
3838
  } catch (err) {
3798
- console.error("onServerSyncWrite callback failed:", err);
3839
+ console.error("[SyncEngine] onServerSyncWrite callback failed:", err);
3799
3840
  }
3800
3841
  }
3801
3842
  };
@@ -4060,7 +4101,7 @@ var ServerUpdateHandler = class {
4060
4101
  timestamp: /* @__PURE__ */ new Date()
4061
4102
  });
4062
4103
  } catch (err) {
4063
- console.error("onWsNotification callback failed:", err);
4104
+ console.error("[ServerUpdateHandler] onWsNotification callback failed:", err);
4064
4105
  }
4065
4106
  }
4066
4107
  }
@@ -4142,11 +4183,11 @@ var WakeSyncManager = class {
4142
4183
  timestamp: /* @__PURE__ */ new Date()
4143
4184
  });
4144
4185
  } catch (err) {
4145
- console.error("onWakeSync callback failed:", err);
4186
+ console.error("[WakeSync] onWakeSync callback failed:", err);
4146
4187
  }
4147
4188
  }
4148
4189
  this.deps.sync(`wake-sync:${trigger}`).catch((err) => {
4149
- console.error(`Wake sync (${trigger}) failed:`, err);
4190
+ console.error(`[WakeSync] Wake sync (${trigger}) failed:`, err);
4150
4191
  });
4151
4192
  }, this.debounceMs);
4152
4193
  }
@@ -4215,7 +4256,7 @@ var NetworkStatusManager = class {
4215
4256
  try {
4216
4257
  this.callbacks.onBrowserNetworkChange(info);
4217
4258
  } catch (err) {
4218
- console.error("onBrowserNetworkChange callback failed:", err);
4259
+ console.error("[NetworkStatus] onBrowserNetworkChange callback failed:", err);
4219
4260
  }
4220
4261
  }
4221
4262
  if (finalOnlineState) {
@@ -4223,7 +4264,7 @@ var NetworkStatusManager = class {
4223
4264
  try {
4224
4265
  this.callbacks.onBrowserOnline();
4225
4266
  } catch (err) {
4226
- console.error("onBrowserOnline callback failed:", err);
4267
+ console.error("[NetworkStatus] onBrowserOnline callback failed:", err);
4227
4268
  }
4228
4269
  }
4229
4270
  } else {
@@ -4231,12 +4272,12 @@ var NetworkStatusManager = class {
4231
4272
  try {
4232
4273
  this.callbacks.onBrowserOffline();
4233
4274
  } catch (err) {
4234
- console.error("onBrowserOffline callback failed:", err);
4275
+ console.error("[NetworkStatus] onBrowserOffline callback failed:", err);
4235
4276
  }
4236
4277
  }
4237
4278
  }
4238
4279
  this.deps.setOnline(finalOnlineState).catch((err) => {
4239
- console.error("Failed to set online status:", err);
4280
+ console.error("[NetworkStatus] Failed to set online status:", err);
4240
4281
  });
4241
4282
  }, this.debounceMs);
4242
4283
  }
@@ -4301,14 +4342,14 @@ var _SyncedDb = class _SyncedDb {
4301
4342
  onBecameLeader: () => {
4302
4343
  if (this.initialized && !this.connectionManager.isOnline() && !this.connectionManager.isForcedOffline()) {
4303
4344
  this.connectionManager.tryGoOnline().catch((err) => {
4304
- console.error("tryGoOnline on becameLeader failed:", err);
4345
+ console.error("[SyncedDb] tryGoOnline on becameLeader failed:", err);
4305
4346
  });
4306
4347
  }
4307
4348
  if (config.onBecameLeader) {
4308
4349
  try {
4309
4350
  config.onBecameLeader();
4310
4351
  } catch (err) {
4311
- console.error("onBecameLeader callback failed:", err);
4352
+ console.error("[SyncedDb] onBecameLeader callback failed:", err);
4312
4353
  }
4313
4354
  }
4314
4355
  },
@@ -4499,6 +4540,55 @@ var _SyncedDb = class _SyncedDb {
4499
4540
  isLeaderTab() {
4500
4541
  return this.leaderElection.isLeader();
4501
4542
  }
4543
+ leaderSince() {
4544
+ return this.leaderElection.leaderSince();
4545
+ }
4546
+ followerSince() {
4547
+ return this.leaderElection.followerSince();
4548
+ }
4549
+ /**
4550
+ * Register a collection for sync at runtime. See `I_SyncedDb.addCollectionToSync`.
4551
+ */
4552
+ async addCollectionToSync(spec) {
4553
+ const existing = this.collections.get(spec.name);
4554
+ if (existing && !existing.temporaryConfig) {
4555
+ return;
4556
+ }
4557
+ await this._installCollectionConfig(spec);
4558
+ }
4559
+ /**
4560
+ * Replace the collection config and re-sync. See `I_SyncedDb.replaceSyncCollection`.
4561
+ */
4562
+ async replaceSyncCollection(spec) {
4563
+ const existing = this.collections.get(spec.name);
4564
+ if (existing && !existing.temporaryConfig && spec.temporaryConfig) {
4565
+ return false;
4566
+ }
4567
+ await this._installCollectionConfig(spec);
4568
+ return true;
4569
+ }
4570
+ /**
4571
+ * Shared install path for addCollectionToSync / replaceSyncCollection:
4572
+ * write config, extend active sync filter, load Dexie cursor + in-mem,
4573
+ * fire a targeted one-shot download for just this collection.
4574
+ */
4575
+ async _installCollectionConfig(spec) {
4576
+ var _a;
4577
+ this.collections.set(spec.name, spec);
4578
+ if (this.syncOnlyCollections) {
4579
+ this.syncOnlyCollections.add(spec.name);
4580
+ }
4581
+ const meta = await this.dexieDb.getSyncMeta(spec.name);
4582
+ if (meta) this.syncMetaCache.set(spec.name, meta);
4583
+ if (!spec.writeOnly) {
4584
+ await this.loadCollectionToInMem(spec.name);
4585
+ }
4586
+ if (!spec.writeOnly && this.connectionManager.canSync()) {
4587
+ const rawQuery = (_a = spec.syncConfig) == null ? void 0 : _a.query;
4588
+ const query = typeof rawQuery === "function" ? rawQuery() : rawQuery;
4589
+ await this.syncCollectionForFind(spec.name, query, { returnDeleted: true });
4590
+ }
4591
+ }
4502
4592
  /**
4503
4593
  * Restrict sync to only these collections. When non-empty, only the listed
4504
4594
  * collections load from Dexie→in-mem and download from server. Pass an empty
@@ -4562,7 +4652,7 @@ var _SyncedDb = class _SyncedDb {
4562
4652
  try {
4563
4653
  this.onDatabaseCreated();
4564
4654
  } catch (err) {
4565
- console.error("onDatabaseCreated callback failed:", err);
4655
+ console.error("[SyncedDb] onDatabaseCreated callback failed:", err);
4566
4656
  }
4567
4657
  }
4568
4658
  await this.pendingChanges.recoverPendingWrites();
@@ -4595,10 +4685,10 @@ var _SyncedDb = class _SyncedDb {
4595
4685
  try {
4596
4686
  await this.serverUpdateNotifier.connect();
4597
4687
  const ep = (_c = this.serverUpdateNotifier.endpoint) != null ? _c : "unknown";
4598
- console.log(`SyncedDb: ebus-proxy connected to ${ep}`);
4688
+ console.log(`[SyncedDb] SyncedDb: ebus-proxy connected to ${ep}`);
4599
4689
  } catch (err) {
4600
4690
  const ep = (_d = this.serverUpdateNotifier.endpoint) != null ? _d : "unknown";
4601
- console.warn(`SyncedDb: ebus-proxy connection to ${ep} failed`);
4691
+ console.warn(`[SyncedDb] SyncedDb: ebus-proxy connection to ${ep} failed`);
4602
4692
  this.connectionManager.reportInfrastructureError(
4603
4693
  "WEBSOCKET_CONNECTION_FAILED",
4604
4694
  `WebSocket connection to ${ep} failed during initialization`,
@@ -4610,12 +4700,21 @@ var _SyncedDb = class _SyncedDb {
4610
4700
  this.beforeUnloadHandler = () => {
4611
4701
  if (this.initialized && this.pendingChanges.hasPendingChanges()) {
4612
4702
  console.warn(
4613
- `SyncedDb: pending changes not flushed. Call close() before page unload.`
4703
+ `[SyncedDb] SyncedDb: pending changes not flushed. Call close() before page unload.`
4614
4704
  );
4615
4705
  }
4616
4706
  };
4617
4707
  window.addEventListener("beforeunload", this.beforeUnloadHandler);
4618
4708
  }
4709
+ if (typeof document !== "undefined") {
4710
+ this.visibilityFlushHandler = () => {
4711
+ if (document.visibilityState !== "hidden") return;
4712
+ this.flushToServer("visibility-hidden").catch((err) => {
4713
+ console.warn("[SyncedDb] flushToServer on visibility-hidden failed:", err == null ? void 0 : err.message);
4714
+ });
4715
+ };
4716
+ document.addEventListener("visibilitychange", this.visibilityFlushHandler);
4717
+ }
4619
4718
  this.initialized = true;
4620
4719
  }
4621
4720
  /**
@@ -4626,6 +4725,25 @@ var _SyncedDb = class _SyncedDb {
4626
4725
  async flush() {
4627
4726
  await this.pendingChanges.flushAll();
4628
4727
  }
4728
+ /**
4729
+ * Push dirty data to the server immediately, bypassing the upload debounce.
4730
+ *
4731
+ * Flushes pending Dexie writes, awaits any in-flight REST upload, then fires
4732
+ * a fresh `uploadDirtyItems()`. Called automatically when the tab becomes
4733
+ * hidden so data is not stranded by browser tab-throttling / suspension.
4734
+ *
4735
+ * No-op when offline, forced offline, or not yet initialized.
4736
+ *
4737
+ * @param calledFrom Diagnostic tag threaded through to upload callbacks
4738
+ */
4739
+ async flushToServer(calledFrom) {
4740
+ if (!this.initialized) return;
4741
+ if (!this.connectionManager.canSync()) return;
4742
+ this.pendingChanges.cancelRestUploadTimer();
4743
+ await this.pendingChanges.flushAll();
4744
+ await this.pendingChanges.awaitRestUpload();
4745
+ await this.syncEngine.uploadDirtyItems(calledFrom);
4746
+ }
4629
4747
  /**
4630
4748
  * Returns when all collections were last successfully synced
4631
4749
  * from the server, or undefined if never.
@@ -4706,6 +4824,10 @@ var _SyncedDb = class _SyncedDb {
4706
4824
  window.removeEventListener("beforeunload", this.beforeUnloadHandler);
4707
4825
  this.beforeUnloadHandler = void 0;
4708
4826
  }
4827
+ if (typeof document !== "undefined" && this.visibilityFlushHandler) {
4828
+ document.removeEventListener("visibilitychange", this.visibilityFlushHandler);
4829
+ this.visibilityFlushHandler = void 0;
4830
+ }
4709
4831
  this.syncMetaCache.clear();
4710
4832
  for (const collectionName of this.collections.keys()) {
4711
4833
  this.inMemManager.clearCollection(collectionName);
@@ -4729,6 +4851,11 @@ var _SyncedDb = class _SyncedDb {
4729
4851
  async findById(collection, id, opts) {
4730
4852
  var _a;
4731
4853
  this.assertCollection(collection);
4854
+ if (!id) {
4855
+ const err = new Error(`[SyncedDb] findById ${collection} no id ${id}`);
4856
+ console.error(err);
4857
+ return null;
4858
+ }
4732
4859
  id = this.normalizeId(id, "findById", collection);
4733
4860
  opts = this.resolveOpts(opts);
4734
4861
  if ((_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly) {
@@ -4961,7 +5088,7 @@ var _SyncedDb = class _SyncedDb {
4961
5088
  await this.syncEngine.processCollectionServerData(collection, serverData, { source: "refresh" });
4962
5089
  }
4963
5090
  }).catch((err) => {
4964
- console.error(`referToServer failed for ${collection}:`, err);
5091
+ console.error(`[SyncedDb] referToServer failed for ${collection}:`, err);
4965
5092
  });
4966
5093
  }
4967
5094
  /**
@@ -4982,7 +5109,7 @@ var _SyncedDb = class _SyncedDb {
4982
5109
  if (!serverItems || serverItems.length === 0) return;
4983
5110
  await this.syncEngine.processCollectionServerData(collection, serverItems, { source: "incremental" });
4984
5111
  }).catch((err) => {
4985
- console.error(`refreshInBackground failed for ${collection}:`, err);
5112
+ console.error(`[SyncedDb] refreshInBackground failed for ${collection}:`, err);
4986
5113
  });
4987
5114
  }
4988
5115
  async ensureItemsAreLoaded(collection, ids, withDeleted) {
@@ -5028,7 +5155,7 @@ var _SyncedDb = class _SyncedDb {
5028
5155
  id = this.normalizeId(id, "save", collection);
5029
5156
  if ("_id" in update && !update._id) {
5030
5157
  console.error(
5031
- `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(", ")}]`
5158
+ `[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(", ")}]`
5032
5159
  );
5033
5160
  delete update._id;
5034
5161
  }
@@ -5042,7 +5169,7 @@ var _SyncedDb = class _SyncedDb {
5042
5169
  }
5043
5170
  })();
5044
5171
  console.error(
5045
- `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(", ")}]`
5172
+ `[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(", ")}]`
5046
5173
  );
5047
5174
  this.safeCallback(this.onSaveIdMismatch, {
5048
5175
  collection,
@@ -5058,7 +5185,7 @@ var _SyncedDb = class _SyncedDb {
5058
5185
  const existing = await this.dexieDb.getById(collection, id);
5059
5186
  if (!existing && !((_a = this.collections.get(collection)) == null ? void 0 : _a.writeOnly)) {
5060
5187
  console.warn(
5061
- `SyncedDb.save: Object ${String(id)} not found in ${collection}, creating new`
5188
+ `[SyncedDb] SyncedDb.save: Object ${String(id)} not found in ${collection}, creating new`
5062
5189
  );
5063
5190
  }
5064
5191
  const fullChanges = __spreadProps(__spreadValues({}, update), { _lastUpdaterId: this.updaterId });
@@ -5108,7 +5235,7 @@ var _SyncedDb = class _SyncedDb {
5108
5235
  const existing = await this.dexieDb.getById(collection, id);
5109
5236
  if (existing && !existing._deleted && !existing._archived) {
5110
5237
  console.warn(
5111
- `SyncedDb.insert: Object ${String(id)} already exists in ${collection}, overwriting`
5238
+ `[SyncedDb] SyncedDb.insert: Object ${String(id)} already exists in ${collection}, overwriting`
5112
5239
  );
5113
5240
  }
5114
5241
  const insertChanges = __spreadProps(__spreadValues({}, data), { _lastUpdaterId: this.updaterId });
@@ -5247,7 +5374,7 @@ var _SyncedDb = class _SyncedDb {
5247
5374
  this.inMemManager.writeBatch(collection, [{ _id: item.id }], "delete", { source: "incremental" });
5248
5375
  results.push(true);
5249
5376
  } catch (err) {
5250
- console.error(`Failed to hard delete ${String(item.id)}:`, err);
5377
+ console.error(`[SyncedDb] Failed to hard delete ${String(item.id)}:`, err);
5251
5378
  results.push(false);
5252
5379
  }
5253
5380
  }
@@ -5282,7 +5409,7 @@ var _SyncedDb = class _SyncedDb {
5282
5409
  evictionPlan = await this._collectScopeExitPlan("auto");
5283
5410
  } catch (err) {
5284
5411
  console.error(
5285
- "[evict] phase 1 failed (skipping bundled eviction):",
5412
+ "[SyncedDb] [evict] phase 1 failed (skipping bundled eviction):",
5286
5413
  err
5287
5414
  );
5288
5415
  }
@@ -5299,11 +5426,11 @@ var _SyncedDb = class _SyncedDb {
5299
5426
  const now = /* @__PURE__ */ new Date();
5300
5427
  if (!this._lastFullSyncDate) {
5301
5428
  this._setLastInitialSync(now).catch((err) => {
5302
- console.error("Failed to persist lastInitialSync:", err);
5429
+ console.error("[SyncedDb] Failed to persist lastInitialSync:", err);
5303
5430
  });
5304
5431
  }
5305
5432
  this._setLastFullSync(now).catch((err) => {
5306
- console.error("Failed to persist lastFullSync:", err);
5433
+ console.error("[SyncedDb] Failed to persist lastFullSync:", err);
5307
5434
  });
5308
5435
  }
5309
5436
  } catch (err) {
@@ -5322,7 +5449,7 @@ var _SyncedDb = class _SyncedDb {
5322
5449
  );
5323
5450
  await this._persistEvictionTimestamp();
5324
5451
  } catch (err) {
5325
- console.error("[evict] phase 3 failed:", err);
5452
+ console.error("[SyncedDb] [evict] phase 3 failed:", err);
5326
5453
  }
5327
5454
  }
5328
5455
  }
@@ -5629,7 +5756,7 @@ var _SyncedDb = class _SyncedDb {
5629
5756
  serverEvictedCount = serverExits.length;
5630
5757
  } catch (err) {
5631
5758
  console.error(
5632
- `[evict] server-assisted pass failed for ${collection} (proceeding with local-only):`,
5759
+ `[SyncedDb] [evict] server-assisted pass failed for ${collection} (proceeding with local-only):`,
5633
5760
  err
5634
5761
  );
5635
5762
  }
@@ -5746,7 +5873,7 @@ var _SyncedDb = class _SyncedDb {
5746
5873
  } catch (err) {
5747
5874
  serverFailed = true;
5748
5875
  console.error(
5749
- "[evict] server-assisted batch failed (proceeding with local-only):",
5876
+ "[SyncedDb] [evict] server-assisted batch failed (proceeding with local-only):",
5750
5877
  err
5751
5878
  );
5752
5879
  }
@@ -6058,7 +6185,7 @@ var _SyncedDb = class _SyncedDb {
6058
6185
  try {
6059
6186
  fn(info);
6060
6187
  } catch (err) {
6061
- console.error("Callback failed:", err);
6188
+ console.error("[SyncedDb] Callback failed:", err);
6062
6189
  }
6063
6190
  }
6064
6191
  }
@@ -6149,12 +6276,12 @@ var _SyncedDb = class _SyncedDb {
6149
6276
  normalizeId(id, method, collection) {
6150
6277
  if (!id && id !== void 0) {
6151
6278
  console.error(
6152
- `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.`
6279
+ `[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.`
6153
6280
  );
6154
6281
  }
6155
6282
  if (typeof id === "string" && _SyncedDb.STRINGIFIED_FALSY.has(id)) {
6156
6283
  console.error(
6157
- `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.`
6284
+ `[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.`
6158
6285
  );
6159
6286
  }
6160
6287
  return typeof id === "object" && id !== null ? String(id) : id;
@@ -6166,12 +6293,12 @@ var _SyncedDb = class _SyncedDb {
6166
6293
  warnFalsyQueryId(data, method, collection) {
6167
6294
  if ("_id" in data && !data._id) {
6168
6295
  console.error(
6169
- `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(", ")}]`
6296
+ `[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(", ")}]`
6170
6297
  );
6171
6298
  }
6172
6299
  if ("_id" in data && typeof data._id === "string" && _SyncedDb.STRINGIFIED_FALSY.has(data._id)) {
6173
6300
  console.error(
6174
- `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(", ")}]`
6301
+ `[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(", ")}]`
6175
6302
  );
6176
6303
  }
6177
6304
  }
@@ -6184,7 +6311,7 @@ var _SyncedDb = class _SyncedDb {
6184
6311
  ensureId(data, method, collection) {
6185
6312
  if (typeof data._id === "string" && _SyncedDb.STRINGIFIED_FALSY.has(data._id)) {
6186
6313
  console.error(
6187
- `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(", ")}]`
6314
+ `[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(", ")}]`
6188
6315
  );
6189
6316
  data._id = null;
6190
6317
  }
@@ -6192,7 +6319,7 @@ var _SyncedDb = class _SyncedDb {
6192
6319
  const newId = new ObjectId2().toHexString();
6193
6320
  if ("_id" in data) {
6194
6321
  console.error(
6195
- `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(", ")}]`
6322
+ `[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(", ")}]`
6196
6323
  );
6197
6324
  }
6198
6325
  data._id = newId;
@@ -6282,7 +6409,7 @@ var _SyncedDb = class _SyncedDb {
6282
6409
  const drop = (reason) => {
6283
6410
  if (dropSilently) return;
6284
6411
  console.error(
6285
- `SyncedDb.applyDiffLocally: dropping bracket-path diff entry (${reason})`,
6412
+ `[SyncedDb] applyDiffLocally: dropping bracket-path diff entry (${reason})`,
6286
6413
  { collection, _id: String(id), path, value }
6287
6414
  );
6288
6415
  };
@@ -6498,7 +6625,7 @@ var DexieDb = class extends Dexie {
6498
6625
  this.ensureStringId(item);
6499
6626
  if (typeof item._id !== "string" || item._id.length === 0) {
6500
6627
  console.error(
6501
- `DexieDb.saveMany: skipping item with invalid _id in "${collection}"`,
6628
+ `[DexieDb] DexieDb.saveMany: skipping item with invalid _id in "${collection}"`,
6502
6629
  { _id: item._id, _idType: typeof item._id, item }
6503
6630
  );
6504
6631
  continue;
@@ -6511,7 +6638,7 @@ var DexieDb = class extends Dexie {
6511
6638
  } catch (err) {
6512
6639
  const ids = valid.map((it) => String(it._id));
6513
6640
  console.error(
6514
- `DexieDb.saveMany: bulkPut failed for "${collection}" (${valid.length} items):`,
6641
+ `[DexieDb] DexieDb.saveMany: bulkPut failed for "${collection}" (${valid.length} items):`,
6515
6642
  err,
6516
6643
  "_ids:",
6517
6644
  ids
@@ -9466,7 +9593,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
9466
9593
  this.reconnectAttempt = 0;
9467
9594
  this.currentReconnectDelay = this.reconnectDelayMs;
9468
9595
  console.log(
9469
- `Ebus2Proxy connected to ${this.wsUrl} (db: ${this.dbName})`
9596
+ `[Ebus2ProxyNotifier] Ebus2Proxy connected to ${this.wsUrl} (db: ${this.dbName})`
9470
9597
  );
9471
9598
  this.sendSubscribe(`db/${this.dbName}`);
9472
9599
  if (this.subscribeServices) {
@@ -9477,7 +9604,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
9477
9604
  try {
9478
9605
  callback();
9479
9606
  } catch (err) {
9480
- console.error("onWsConnect callback failed:", err);
9607
+ console.error("[Ebus2ProxyNotifier] onWsConnect callback failed:", err);
9481
9608
  }
9482
9609
  }
9483
9610
  }
@@ -9492,7 +9619,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
9492
9619
  try {
9493
9620
  callback(reason);
9494
9621
  } catch (err) {
9495
- console.error("onWsDisconnect callback failed:", err);
9622
+ console.error("[Ebus2ProxyNotifier] onWsDisconnect callback failed:", err);
9496
9623
  }
9497
9624
  }
9498
9625
  }
@@ -9501,7 +9628,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
9501
9628
  }
9502
9629
  }
9503
9630
  handleError(event) {
9504
- console.error("WebSocket error:", event);
9631
+ console.error("[Ebus2ProxyNotifier] WebSocket error:", event);
9505
9632
  }
9506
9633
  handleMessage(event) {
9507
9634
  try {
@@ -9523,11 +9650,11 @@ var Ebus2ProxyServerUpdateNotifier = class {
9523
9650
  this.handlePong();
9524
9651
  break;
9525
9652
  case "error":
9526
- console.error("WebSocket server error:", message.error);
9653
+ console.error("[Ebus2ProxyNotifier] WebSocket server error:", message.error);
9527
9654
  break;
9528
9655
  }
9529
9656
  } catch (err) {
9530
- console.error("Failed to parse WebSocket message:", err);
9657
+ console.error("[Ebus2ProxyNotifier] Failed to parse WebSocket message:", err);
9531
9658
  }
9532
9659
  }
9533
9660
  handleChannelMessage(message) {
@@ -9535,7 +9662,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
9535
9662
  try {
9536
9663
  this.onServicesChange(message.data);
9537
9664
  } catch (err) {
9538
- console.error("onServicesChange callback failed:", err);
9665
+ console.error("[Ebus2ProxyNotifier] onServicesChange callback failed:", err);
9539
9666
  }
9540
9667
  return;
9541
9668
  }
@@ -9552,14 +9679,14 @@ var Ebus2ProxyServerUpdateNotifier = class {
9552
9679
  try {
9553
9680
  this.onWsNotification(payload);
9554
9681
  } catch (err) {
9555
- console.error("onWsNotification callback failed:", err);
9682
+ console.error("[Ebus2ProxyNotifier] onWsNotification callback failed:", err);
9556
9683
  }
9557
9684
  }
9558
9685
  for (const callback of this.callbacks) {
9559
9686
  try {
9560
9687
  callback(payload);
9561
9688
  } catch (err) {
9562
- console.error("ServerUpdateCallback failed:", err);
9689
+ console.error("[Ebus2ProxyNotifier] ServerUpdateCallback failed:", err);
9563
9690
  }
9564
9691
  }
9565
9692
  }
@@ -9578,14 +9705,14 @@ var Ebus2ProxyServerUpdateNotifier = class {
9578
9705
  try {
9579
9706
  callback(this.reconnectAttempt);
9580
9707
  } catch (err) {
9581
- console.error("onWsReconnect callback failed:", err);
9708
+ console.error("[Ebus2ProxyNotifier] onWsReconnect callback failed:", err);
9582
9709
  }
9583
9710
  }
9584
9711
  this.reconnectTimer = setTimeout(() => {
9585
9712
  this.reconnectTimer = void 0;
9586
9713
  if (this.shouldReconnect && !this.forcedOffline) {
9587
9714
  this.createWebSocket().catch((err) => {
9588
- console.error("Reconnection failed:", err);
9715
+ console.error("[Ebus2ProxyNotifier] Reconnection failed:", err);
9589
9716
  this.currentReconnectDelay = Math.min(
9590
9717
  this.currentReconnectDelay * 2,
9591
9718
  this.maxReconnectDelayMs
@@ -9608,7 +9735,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
9608
9735
  const pingMsg = { type: "ping", id: `ping-${Date.now()}` };
9609
9736
  this.ws.send(packr2.pack(preprocessForPack2(pingMsg)));
9610
9737
  this.pongTimer = setTimeout(() => {
9611
- console.warn("Pong timeout - closing WebSocket");
9738
+ console.warn("[Ebus2ProxyNotifier] Pong timeout - closing WebSocket");
9612
9739
  if (this.ws) {
9613
9740
  this.ws.close(4e3, "Pong timeout");
9614
9741
  }