cry-synced-db-client 0.1.181 → 0.1.182

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
@@ -1,5 +1,68 @@
1
1
  # Versions
2
2
 
3
+ ## 0.1.182 (2026-05-14)
4
+
5
+ ### `networkError` utility — quiet log noise during sustained offline
6
+
7
+ New `src/utils/networkError.ts`:
8
+
9
+ ```ts
10
+ export function networkError(message: string, ...rest: unknown[]): void {
11
+ const isOnline =
12
+ typeof navigator === "undefined" || navigator.onLine !== false;
13
+ if (isOnline) {
14
+ console.error(message, ...rest);
15
+ } else {
16
+ console.info(message, ...rest);
17
+ }
18
+ }
19
+ ```
20
+
21
+ Applied to 15 sites where the failure mode reduces to "REST/WS
22
+ unreachable because the network is down":
23
+
24
+ | Module | Site |
25
+ |---|---|
26
+ | `ConnectionManager` | Failed to go online after forceOffline release |
27
+ | `ConnectionManager` | Auto-sync failed |
28
+ | `ConnectionManager` | Reconnect tryGoOnline failed |
29
+ | `PendingChangesManager` | REST upload failed |
30
+ | `WakeSyncManager` | Wake sync (`<trigger>`) failed |
31
+ | `SyncEngine` | uploadDirtyItems failed (download succeeded) |
32
+ | `SyncEngine` | Sync failed |
33
+ | `SyncedDb` | tryGoOnline on becameLeader failed |
34
+ | `SyncedDb` | referToServer failed for `<collection>` |
35
+ | `SyncedDb` | refreshInBackground failed for `<collection>` |
36
+ | `SyncedDb` | Failed to hard delete `<id>` |
37
+ | `SyncedDb` | `[evict]` server-assisted pass failed |
38
+ | `SyncedDb` | `[evict]` server-assisted batch failed |
39
+ | `Ebus2ProxyNotifier` | WebSocket error: `<event.type>` |
40
+ | `Ebus2ProxyNotifier` | Reconnection failed |
41
+
42
+ Effect: a 5-minute offline period that previously produced ~25–30
43
+ `console.error` lines (auto-sync ticks every 60s, reconnect probes
44
+ every 60s, WS reconnect backoff, dirty-flush retries) now produces 0.
45
+ The events are still logged at `console.info` so an operator inspecting
46
+ them sees the full timeline — they just don't surface as "errors" in
47
+ sentry/log dashboards.
48
+
49
+ What's NOT routed through `networkError` (intentional — these are real
50
+ bugs regardless of network state):
51
+ - Caller bugs (falsy `_id`, missing `_id`, id mismatch, no id provided)
52
+ - Dexie failures (`bulkPut failed`, `Failed to write to Dexie`, etc.)
53
+ - Consumer-supplied callback throws (`onSyncEnd callback failed`, etc.)
54
+ - WS protocol errors (malformed msgpack, server-side error frames)
55
+ - Server-side per-item rejection (`Sync upload error [coll] _id=...`)
56
+ - `DB-WARNING` per-item warnings
57
+ - BroadcastChannel failures (cross-tab, local, not network)
58
+
59
+ Caveat: `navigator.onLine` only reports the OS network interface, not
60
+ server reachability. WiFi-on-but-server-unreachable still logs at error
61
+ severity, which is the correct behavior — that failure is unexpected
62
+ from the browser's point of view.
63
+
64
+ Bundle: 356.0 KB → 356.2 KB. Tests: 736 bun + 18 vitest still pass.
65
+
3
66
  ## 0.1.181 (2026-05-14)
4
67
 
5
68
  ### `measureEndToEndRtt` now rides `callWorker` over the WebSocket
package/dist/index.js CHANGED
@@ -732,6 +732,16 @@ function childPathForArrayElement(prefix, element, index) {
732
732
  return prefix ? `${prefix}.${index}` : String(index);
733
733
  }
734
734
 
735
+ // src/utils/networkError.ts
736
+ function networkError(message, ...rest) {
737
+ const isOnline = typeof navigator === "undefined" || navigator.onLine !== false;
738
+ if (isOnline) {
739
+ console.error(message, ...rest);
740
+ } else {
741
+ console.info(message, ...rest);
742
+ }
743
+ }
744
+
735
745
  // src/db/managers/InMemManager.ts
736
746
  var InMemManager = class {
737
747
  constructor(config) {
@@ -1422,7 +1432,7 @@ var ConnectionManager = class {
1422
1432
  } else {
1423
1433
  this.deps.tryBecomeLeader();
1424
1434
  this.tryGoOnline().catch((err) => {
1425
- console.error(`[Connection] Failed to go online after forceOffline release: ${err}`, err);
1435
+ networkError(`[Connection] Failed to go online after forceOffline release: ${err}`, err);
1426
1436
  });
1427
1437
  }
1428
1438
  }
@@ -1480,7 +1490,7 @@ var ConnectionManager = class {
1480
1490
  this.autoSyncTimer = setInterval(() => {
1481
1491
  if (this.forcedOffline || !this.online) return;
1482
1492
  this.deps.sync(`interval ${intervalMs}ms`).catch((err) => {
1483
- console.error(`[Connection] Auto-sync failed: ${err}`, err);
1493
+ networkError(`[Connection] Auto-sync failed: ${err}`, err);
1484
1494
  });
1485
1495
  }, intervalMs);
1486
1496
  }
@@ -1489,7 +1499,7 @@ var ConnectionManager = class {
1489
1499
  this.reconnectTimer = setInterval(() => {
1490
1500
  if (this.forcedOffline || this.online || this.tryGoOnlineInFlight) return;
1491
1501
  this.tryGoOnline().catch((err) => {
1492
- console.error(`[Connection] Reconnect tryGoOnline failed: ${err}`, err);
1502
+ networkError(`[Connection] Reconnect tryGoOnline failed: ${err}`, err);
1493
1503
  });
1494
1504
  }, retryMs);
1495
1505
  }
@@ -2827,7 +2837,7 @@ var PendingChangesManager = class {
2827
2837
  try {
2828
2838
  await this.deps.uploadDirtyItems();
2829
2839
  } catch (err) {
2830
- console.error(`[PendingChanges] REST upload failed: ${err}`, err);
2840
+ networkError(`[PendingChanges] REST upload failed: ${err}`, err);
2831
2841
  } finally {
2832
2842
  this.isUploadingToRest = false;
2833
2843
  resolveUpload();
@@ -3209,7 +3219,7 @@ var _SyncEngine = class _SyncEngine {
3209
3219
  }
3210
3220
  }
3211
3221
  } catch (err) {
3212
- console.error(
3222
+ networkError(
3213
3223
  "[SyncEngine] uploadDirtyItems failed (download succeeded, staying online):",
3214
3224
  err
3215
3225
  );
@@ -3236,7 +3246,7 @@ var _SyncEngine = class _SyncEngine {
3236
3246
  });
3237
3247
  } catch (err) {
3238
3248
  const reason = err instanceof Error ? err.message : String(err);
3239
- console.error(`[SyncEngine] Sync failed: ${err}`, err);
3249
+ networkError(`[SyncEngine] Sync failed: ${err}`, err);
3240
3250
  this.deps.onSyncFailed(`Sync failed: ${reason}`);
3241
3251
  this.callOnSyncEnd({
3242
3252
  durationMs: Date.now() - startTime,
@@ -4291,7 +4301,7 @@ var WakeSyncManager = class {
4291
4301
  }
4292
4302
  }
4293
4303
  this.deps.sync(`wake-sync:${trigger}`).catch((err) => {
4294
- console.error(`[WakeSync] Wake sync (${trigger}) failed:`, err);
4304
+ networkError(`[WakeSync] Wake sync (${trigger}) failed:`, err);
4295
4305
  });
4296
4306
  }, this.debounceMs);
4297
4307
  }
@@ -4447,7 +4457,7 @@ var _SyncedDb = class _SyncedDb {
4447
4457
  onBecameLeader: () => {
4448
4458
  if (this.initialized && !this.connectionManager.isOnline() && !this.connectionManager.isForcedOffline()) {
4449
4459
  this.connectionManager.tryGoOnline().catch((err) => {
4450
- console.error(`[SyncedDb] tryGoOnline on becameLeader failed: ${err}`, err);
4460
+ networkError(`[SyncedDb] tryGoOnline on becameLeader failed: ${err}`, err);
4451
4461
  });
4452
4462
  }
4453
4463
  if (config.onBecameLeader) {
@@ -5246,7 +5256,7 @@ var _SyncedDb = class _SyncedDb {
5246
5256
  await this.syncEngine.processCollectionServerData(collection, serverData, { source: "refresh" });
5247
5257
  }
5248
5258
  }).catch((err) => {
5249
- console.error(`[SyncedDb] referToServer failed for ${collection}:`, err);
5259
+ networkError(`[SyncedDb] referToServer failed for ${collection}:`, err);
5250
5260
  });
5251
5261
  }
5252
5262
  /**
@@ -5267,7 +5277,7 @@ var _SyncedDb = class _SyncedDb {
5267
5277
  if (!serverItems || serverItems.length === 0) return;
5268
5278
  await this.syncEngine.processCollectionServerData(collection, serverItems, { source: "incremental" });
5269
5279
  }).catch((err) => {
5270
- console.error(`[SyncedDb] refreshInBackground failed for ${collection}:`, err);
5280
+ networkError(`[SyncedDb] refreshInBackground failed for ${collection}:`, err);
5271
5281
  });
5272
5282
  }
5273
5283
  async ensureItemsAreLoaded(collection, ids, withDeleted) {
@@ -5532,7 +5542,7 @@ var _SyncedDb = class _SyncedDb {
5532
5542
  this.inMemManager.writeBatch(collection, [{ _id: item.id }], "delete", { source: "incremental" });
5533
5543
  results.push(true);
5534
5544
  } catch (err) {
5535
- console.error(`[SyncedDb] Failed to hard delete ${String(item.id)}:`, err);
5545
+ networkError(`[SyncedDb] Failed to hard delete ${String(item.id)}:`, err);
5536
5546
  results.push(false);
5537
5547
  }
5538
5548
  }
@@ -5913,7 +5923,7 @@ var _SyncedDb = class _SyncedDb {
5913
5923
  for (const id of serverExits) evictIds.push(id);
5914
5924
  serverEvictedCount = serverExits.length;
5915
5925
  } catch (err) {
5916
- console.error(
5926
+ networkError(
5917
5927
  `[SyncedDb] [evict] server-assisted pass failed for ${collection} (proceeding with local-only):`,
5918
5928
  err
5919
5929
  );
@@ -6030,7 +6040,7 @@ var _SyncedDb = class _SyncedDb {
6030
6040
  }
6031
6041
  } catch (err) {
6032
6042
  serverFailed = true;
6033
- console.error(
6043
+ networkError(
6034
6044
  "[SyncedDb] [evict] server-assisted batch failed (proceeding with local-only):",
6035
6045
  err
6036
6046
  );
@@ -10113,7 +10123,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
10113
10123
  }
10114
10124
  }
10115
10125
  handleError(event) {
10116
- console.error(`[Ebus2ProxyNotifier] WebSocket error: ${event.type}`, event);
10126
+ networkError(`[Ebus2ProxyNotifier] WebSocket error: ${event.type}`, event);
10117
10127
  }
10118
10128
  handleMessage(event) {
10119
10129
  try {
@@ -10234,7 +10244,7 @@ var Ebus2ProxyServerUpdateNotifier = class {
10234
10244
  this.reconnectTimer = void 0;
10235
10245
  if (this.shouldReconnect && !this.forcedOffline) {
10236
10246
  this.createWebSocket().catch((err) => {
10237
- console.error(`[Ebus2ProxyNotifier] Reconnection failed: ${err}`, err);
10247
+ networkError(`[Ebus2ProxyNotifier] Reconnection failed: ${err}`, err);
10238
10248
  this.currentReconnectDelay = Math.min(
10239
10249
  this.currentReconnectDelay * 2,
10240
10250
  this.maxReconnectDelayMs
@@ -0,0 +1,28 @@
1
+ /**
2
+ * Log a failure at appropriate severity depending on perceived network
3
+ * connectivity.
4
+ *
5
+ * - **Online** (`navigator.onLine === true`, or `navigator` unavailable
6
+ * like in Node/SSR): routes to `console.error`. Failures while the
7
+ * browser thinks it's online are unexpected — operators should see
8
+ * them.
9
+ * - **Offline** (`navigator.onLine === false`): routes to
10
+ * `console.info`. Sync/upload/reconnect failures are the expected
11
+ * steady-state and shouldn't pollute the error stream.
12
+ *
13
+ * Use this ONLY for sites whose failure reduces to "the network is
14
+ * down" — caller bugs, Dexie failures, parse errors, and consumer
15
+ * callback throws must keep using `console.error` directly so they're
16
+ * visible regardless of connectivity.
17
+ *
18
+ * Signature mirrors `console.error` — first arg is the tag-line string
19
+ * (per the console-reporting skill), remaining args are objects /
20
+ * values for devtools inspection.
21
+ *
22
+ * Caveat: `navigator.onLine` only tells you whether the browser has a
23
+ * network interface; it can't detect "WiFi connected but server
24
+ * unreachable". In that case this still routes to `console.error`,
25
+ * which is correct — the failure is unexpected from the browser's
26
+ * point of view.
27
+ */
28
+ export declare function networkError(message: string, ...rest: unknown[]): void;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "cry-synced-db-client",
3
- "version": "0.1.181",
3
+ "version": "0.1.182",
4
4
  "type": "module",
5
5
  "main": "./dist/index.js",
6
6
  "module": "./dist/index.js",