@chromahq/react 1.0.36 → 1.0.39

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.d.ts CHANGED
@@ -32,8 +32,15 @@ interface BridgeProviderProps {
32
32
  pingInterval?: number;
33
33
  /** How long to wait before resetting retry count in ms. Default: 30000 */
34
34
  maxRetryCooldown?: number;
35
- /** Default timeout for messages in ms. Default: 10000 */
35
+ /** Default timeout for messages in ms. Default: 30000 */
36
36
  defaultTimeout?: number;
37
+ /**
38
+ * Timeout threshold for counting failures toward reconnection.
39
+ * Only requests with timeouts <= this value are counted as potential SW issues.
40
+ * Requests with longer timeouts (intentional slow operations) won't trigger reconnection.
41
+ * Default: 15000 (15s)
42
+ */
43
+ timeoutFailureThreshold?: number;
37
44
  /** Callback when connection status changes */
38
45
  onConnectionChange?: (status: ConnectionStatus) => void;
39
46
  /** Callback when an error occurs */
package/dist/index.js CHANGED
@@ -5,22 +5,26 @@ const BRIDGE_ENABLE_LOGS = true;
5
5
  const CONFIG = {
6
6
  RETRY_AFTER: 1e3,
7
7
  MAX_RETRIES: 10,
8
- PING_INTERVAL: 3e3,
9
- // Check every 3s (balance between responsiveness and false positives)
8
+ PING_INTERVAL: 5e3,
9
+ // Check every 5s (reduced frequency to avoid false positives during heavy operations)
10
10
  MAX_RETRY_COOLDOWN: 3e4,
11
- DEFAULT_TIMEOUT: 1e4,
11
+ DEFAULT_TIMEOUT: 3e4,
12
+ // Increased from 20s to 30s for slow operations
12
13
  MAX_RETRY_DELAY: 3e4,
13
- PING_TIMEOUT: 5e3,
14
- // Give SW 5s to respond (handles busy periods)
14
+ PING_TIMEOUT: 1e4,
15
+ // Give SW 10s to respond (handles busy periods like large storage reads)
15
16
  ERROR_CHECK_INTERVAL: 100,
16
17
  MAX_ERROR_CHECKS: 10,
17
- CONSECUTIVE_FAILURE_THRESHOLD: 3,
18
- // Require 3 consecutive failures (9s total) before reconnecting
18
+ CONSECUTIVE_FAILURE_THRESHOLD: 5,
19
+ // Require 5 consecutive failures (25s total) before reconnecting
19
20
  RECONNECT_DELAY: 100,
20
21
  PORT_NAME: "chroma-bridge",
21
22
  // Service worker restart retry settings (indefinite retries)
22
23
  SW_RESTART_RETRY_DELAY: 500,
23
- SW_RESTART_MAX_DELAY: 5e3
24
+ SW_RESTART_MAX_DELAY: 5e3,
25
+ // Threshold for counting timeouts toward reconnection (only count fast timeouts as failures)
26
+ TIMEOUT_FAILURE_THRESHOLD_MS: 15e3
27
+ // Only count timeouts < 15s as potential SW issues
24
28
  };
25
29
  const calculateBackoffDelay = (attempt, baseDelay, maxDelay) => Math.min(baseDelay * Math.pow(2, attempt - 1), maxDelay);
26
30
  const clearTimeoutSafe = (ref) => {
@@ -52,14 +56,17 @@ function createBridgeInstance(deps) {
52
56
  reconnectionGracePeriodRef,
53
57
  healthPausedUntilRef,
54
58
  defaultTimeout,
59
+ timeoutFailureThreshold,
55
60
  onReconnectNeeded
56
61
  } = deps;
57
62
  const rejectAllPending = (message) => {
58
- pendingRequestsRef.current.forEach(({ reject, timeout }) => {
59
- clearTimeout(timeout);
60
- reject(new Error(message));
63
+ pendingRequestsRef.current.forEach(({ reject, timeout, timeoutDuration }, id) => {
64
+ if (timeoutDuration <= timeoutFailureThreshold) {
65
+ clearTimeout(timeout);
66
+ reject(new Error(message));
67
+ pendingRequestsRef.current.delete(id);
68
+ }
61
69
  });
62
- pendingRequestsRef.current.clear();
63
70
  };
64
71
  const send = (key, payload, timeoutDuration = defaultTimeout) => {
65
72
  return new Promise((resolve, reject) => {
@@ -71,15 +78,16 @@ function createBridgeInstance(deps) {
71
78
  const timeout = setTimeout(() => {
72
79
  if (!pendingRequestsRef.current.has(id)) return;
73
80
  pendingRequestsRef.current.delete(id);
74
- if (!reconnectionGracePeriodRef.current) {
81
+ const isShortTimeout = timeoutDuration <= timeoutFailureThreshold;
82
+ if (!reconnectionGracePeriodRef.current && isShortTimeout) {
75
83
  consecutiveTimeoutsRef.current++;
76
84
  }
77
85
  {
78
86
  console.warn(
79
- `[Bridge] Request timed out: ${key} (${timeoutDuration}ms)${reconnectionGracePeriodRef.current ? " [grace period]" : ""}`
87
+ `[Bridge] Request timed out: ${key} (${timeoutDuration}ms)${reconnectionGracePeriodRef.current ? " [grace period]" : ""}${!isShortTimeout ? " [long operation, not counted toward reconnect]" : ""}`
80
88
  );
81
89
  }
82
- if (!reconnectionGracePeriodRef.current && consecutiveTimeoutsRef.current >= CONFIG.CONSECUTIVE_FAILURE_THRESHOLD) {
90
+ if (!reconnectionGracePeriodRef.current && isShortTimeout && consecutiveTimeoutsRef.current >= CONFIG.CONSECUTIVE_FAILURE_THRESHOLD) {
83
91
  {
84
92
  console.warn(
85
93
  `[Bridge] ${consecutiveTimeoutsRef.current} consecutive timeouts, reconnecting...`
@@ -94,7 +102,8 @@ function createBridgeInstance(deps) {
94
102
  pendingRequestsRef.current.set(id, {
95
103
  resolve,
96
104
  reject,
97
- timeout
105
+ timeout,
106
+ timeoutDuration
98
107
  });
99
108
  try {
100
109
  portRef.current.postMessage({ id, key, payload });
@@ -248,6 +257,7 @@ const BridgeProvider = ({
248
257
  pingInterval = CONFIG.PING_INTERVAL,
249
258
  maxRetryCooldown = CONFIG.MAX_RETRY_COOLDOWN,
250
259
  defaultTimeout = CONFIG.DEFAULT_TIMEOUT,
260
+ timeoutFailureThreshold = CONFIG.TIMEOUT_FAILURE_THRESHOLD_MS,
251
261
  onConnectionChange,
252
262
  onError
253
263
  }) => {
@@ -331,6 +341,9 @@ const BridgeProvider = ({
331
341
  reject(new Error("Bridge disconnected"));
332
342
  });
333
343
  pendingRequestsRef.current.clear();
344
+ if (!emitDisconnect) {
345
+ eventListenersRef.current.clear();
346
+ }
334
347
  setIsServiceWorkerAlive(false);
335
348
  setBridge(null);
336
349
  isConnectingRef.current = false;
@@ -359,6 +372,10 @@ const BridgeProvider = ({
359
372
  }
360
373
  }
361
374
  });
375
+ return;
376
+ }
377
+ if (message.id && BRIDGE_ENABLE_LOGS) {
378
+ console.warn("[Bridge] Received response for unknown/expired request:", message.id);
362
379
  }
363
380
  }, []);
364
381
  const scheduleReconnect = useCallback(
@@ -524,6 +541,7 @@ const BridgeProvider = ({
524
541
  reconnectionGracePeriodRef,
525
542
  healthPausedUntilRef,
526
543
  defaultTimeout,
544
+ timeoutFailureThreshold,
527
545
  onReconnectNeeded: triggerReconnect
528
546
  });
529
547
  setBridge(bridgeInstance);
@@ -552,11 +570,13 @@ const BridgeProvider = ({
552
570
  }
553
571
  });
554
572
  const rejectAllPendingRequests = (message) => {
555
- pendingRequestsRef.current.forEach(({ reject, timeout }) => {
556
- clearTimeout(timeout);
557
- reject(new Error(message));
573
+ pendingRequestsRef.current.forEach(({ reject, timeout, timeoutDuration }, id) => {
574
+ if (timeoutDuration <= timeoutFailureThreshold) {
575
+ clearTimeout(timeout);
576
+ reject(new Error(message));
577
+ pendingRequestsRef.current.delete(id);
578
+ }
558
579
  });
559
- pendingRequestsRef.current.clear();
560
580
  consecutiveTimeoutsRef.current = 0;
561
581
  };
562
582
  startHealthMonitor({
@@ -614,17 +634,21 @@ const BridgeProvider = ({
614
634
  isConnectingRef.current = false;
615
635
  connect();
616
636
  } else if (currentStatus === "connected" && currentBridge) {
637
+ if (isConnectingRef.current) return;
617
638
  currentBridge.ping().then((alive) => {
618
639
  if (!isMountedRef.current) return;
640
+ if (isConnectingRef.current) return;
619
641
  if (!alive) {
620
642
  {
621
643
  console.warn("[Bridge] Tab visible but unresponsive, reconnecting...");
622
644
  }
623
645
  retryCountRef.current = 0;
646
+ swRestartRetryCountRef.current = 0;
624
647
  isConnectingRef.current = false;
625
648
  cleanup();
626
649
  connect();
627
650
  }
651
+ }).catch(() => {
628
652
  });
629
653
  }
630
654
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chromahq/react",
3
- "version": "1.0.36",
3
+ "version": "1.0.39",
4
4
  "description": "React bindings for the Chroma Chrome extension framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",