@chromahq/react 1.0.22 → 1.0.24

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
@@ -76,7 +76,19 @@ interface ConnectionStatusResult {
76
76
  reconnect: (() => void) | undefined;
77
77
  /** Any connection error */
78
78
  error: Error | null | undefined;
79
+ /** Whether the system is fully ready (bridge connected + optional store ready) */
80
+ isReady: boolean;
81
+ /** Whether the system is loading (bridge connecting/reconnecting OR store not ready) */
82
+ isLoading: boolean;
79
83
  }
80
- declare const useConnectionStatus: () => ConnectionStatusResult;
84
+ interface StoreReadyMethods {
85
+ onReady: (callback: () => void) => () => void;
86
+ isReady: () => boolean;
87
+ }
88
+ /**
89
+ * Hook to get unified connection status
90
+ * @param store Optional store to include in readiness check
91
+ */
92
+ declare const useConnectionStatus: (store?: StoreReadyMethods) => ConnectionStatusResult;
81
93
 
82
94
  export { BridgeProvider, useBridge, useBridgeQuery, useConnectionStatus };
package/dist/index.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { jsx } from 'react/jsx-runtime';
2
- import { createContext, useState, useRef, useEffect, useCallback, useMemo, useContext } from 'react';
2
+ import { createContext, useState, useRef, useEffect, useCallback, useMemo, useContext, useSyncExternalStore } from 'react';
3
3
 
4
4
  const BRIDGE_ENABLE_LOGS = true;
5
5
  const CONFIG = {
@@ -46,6 +46,7 @@ function createBridgeInstance(deps) {
46
46
  messageIdRef,
47
47
  isConnectedRef,
48
48
  consecutiveTimeoutsRef,
49
+ reconnectionGracePeriodRef,
49
50
  defaultTimeout,
50
51
  onReconnectNeeded
51
52
  } = deps;
@@ -66,11 +67,15 @@ function createBridgeInstance(deps) {
66
67
  const timeout = setTimeout(() => {
67
68
  if (!pendingRequestsRef.current.has(id)) return;
68
69
  pendingRequestsRef.current.delete(id);
69
- consecutiveTimeoutsRef.current++;
70
+ if (!reconnectionGracePeriodRef.current) {
71
+ consecutiveTimeoutsRef.current++;
72
+ }
70
73
  {
71
- console.warn(`[Bridge] Request timed out: ${key} (${timeoutDuration}ms)`);
74
+ console.warn(
75
+ `[Bridge] Request timed out: ${key} (${timeoutDuration}ms)${reconnectionGracePeriodRef.current ? " [grace period]" : ""}`
76
+ );
72
77
  }
73
- if (consecutiveTimeoutsRef.current >= CONFIG.CONSECUTIVE_FAILURE_THRESHOLD) {
78
+ if (!reconnectionGracePeriodRef.current && consecutiveTimeoutsRef.current >= CONFIG.CONSECUTIVE_FAILURE_THRESHOLD) {
74
79
  {
75
80
  console.warn(
76
81
  `[Bridge] ${consecutiveTimeoutsRef.current} consecutive timeouts, reconnecting...`
@@ -230,12 +235,19 @@ const BridgeProvider = ({
230
235
  const errorCheckIntervalRef = useRef(null);
231
236
  const consecutivePingFailuresRef = useRef(0);
232
237
  const consecutiveTimeoutsRef = useRef(0);
238
+ const reconnectionGracePeriodRef = useRef(false);
233
239
  const pendingRequestsRef = useRef(/* @__PURE__ */ new Map());
234
240
  const eventListenersRef = useRef(/* @__PURE__ */ new Map());
235
241
  const messageIdRef = useRef(0);
236
242
  const statusRef = useRef(status);
237
243
  const bridgeRef = useRef(bridge);
238
244
  const isMountedRef = useRef(true);
245
+ useEffect(() => {
246
+ isMountedRef.current = true;
247
+ return () => {
248
+ isMountedRef.current = false;
249
+ };
250
+ }, []);
239
251
  useEffect(() => {
240
252
  statusRef.current = status;
241
253
  }, [status]);
@@ -465,6 +477,7 @@ const BridgeProvider = ({
465
477
  messageIdRef,
466
478
  isConnectedRef,
467
479
  consecutiveTimeoutsRef,
480
+ reconnectionGracePeriodRef,
468
481
  defaultTimeout,
469
482
  onReconnectNeeded: triggerReconnect
470
483
  });
@@ -477,6 +490,13 @@ const BridgeProvider = ({
477
490
  swRestartRetryCountRef.current = 0;
478
491
  consecutiveTimeoutsRef.current = 0;
479
492
  isConnectingRef.current = false;
493
+ reconnectionGracePeriodRef.current = true;
494
+ setTimeout(() => {
495
+ reconnectionGracePeriodRef.current = false;
496
+ if (BRIDGE_ENABLE_LOGS) {
497
+ console.log("[Bridge] Grace period ended, timeout monitoring active");
498
+ }
499
+ }, 3e3);
480
500
  eventListenersRef.current.get("bridge:connected")?.forEach((handler) => {
481
501
  try {
482
502
  handler({ timestamp: Date.now() });
@@ -549,7 +569,6 @@ const BridgeProvider = ({
549
569
  };
550
570
  document.addEventListener("visibilitychange", handleVisibilityChange);
551
571
  return () => {
552
- isMountedRef.current = false;
553
572
  document.removeEventListener("visibilitychange", handleVisibilityChange);
554
573
  clearTimeoutSafe(maxRetryCooldownRef);
555
574
  cleanup();
@@ -696,14 +715,26 @@ function useBridgeMutation(key, options = {}) {
696
715
  return { mutate, data, loading, error, reset };
697
716
  }
698
717
 
699
- const useConnectionStatus = () => {
718
+ const useConnectionStatus = (store) => {
700
719
  const context = useContext(BridgeContext);
720
+ const storeReady = useSyncExternalStore(
721
+ store?.onReady ?? (() => () => {
722
+ }),
723
+ store?.isReady ?? (() => true),
724
+ () => false
725
+ // Server-side fallback
726
+ );
727
+ const bridgeConnected = context?.status === "connected";
728
+ const isReady = bridgeConnected && storeReady;
729
+ const isLoading = !isReady && (context?.status === "connecting" || context?.status === "reconnecting" || bridgeConnected && !storeReady);
701
730
  return {
702
731
  status: context?.status,
703
- isConnected: context?.status === "connected",
732
+ isConnected: bridgeConnected,
704
733
  isServiceWorkerAlive: context?.isServiceWorkerAlive ?? false,
705
734
  reconnect: context?.reconnect,
706
- error: context?.error
735
+ error: context?.error,
736
+ isReady,
737
+ isLoading
707
738
  };
708
739
  };
709
740
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chromahq/react",
3
- "version": "1.0.22",
3
+ "version": "1.0.24",
4
4
  "description": "React bindings for the Chroma Chrome extension framework",
5
5
  "main": "dist/index.js",
6
6
  "types": "dist/index.d.ts",