@chromahq/react 1.0.27 → 1.0.29
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 +14 -5
- package/dist/index.js +37 -14
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -124,8 +124,9 @@ declare const useConnectionStatus: (store?: StoreReadyMethods) => ConnectionStat
|
|
|
124
124
|
* </ServiceWorkerHealthProvider>
|
|
125
125
|
* </BridgeProvider>
|
|
126
126
|
*
|
|
127
|
-
* // In any component, use the hook:
|
|
128
|
-
*
|
|
127
|
+
* // In any component, use the hook (optionally pass your store instance):
|
|
128
|
+
* import { appStore } from './stores/app';
|
|
129
|
+
* const { isHealthy, isRecovering } = useServiceWorkerHealth({ store: appStore });
|
|
129
130
|
*
|
|
130
131
|
* if (!isHealthy) {
|
|
131
132
|
* return <Spinner message="Reconnecting..." />;
|
|
@@ -148,6 +149,10 @@ interface ServiceWorkerHealthContextValue {
|
|
|
148
149
|
/** Force a reconnection attempt */
|
|
149
150
|
forceReconnect: () => void;
|
|
150
151
|
}
|
|
152
|
+
interface ServiceWorkerHealthResult extends ServiceWorkerHealthContextValue {
|
|
153
|
+
/** Optional readiness state for attached store */
|
|
154
|
+
storeReady: boolean;
|
|
155
|
+
}
|
|
151
156
|
interface ServiceWorkerHealthProviderProps {
|
|
152
157
|
children: ReactNode;
|
|
153
158
|
/**
|
|
@@ -156,6 +161,9 @@ interface ServiceWorkerHealthProviderProps {
|
|
|
156
161
|
*/
|
|
157
162
|
onHealthChange?: (status: HealthStatus, isHealthy: boolean) => void;
|
|
158
163
|
}
|
|
164
|
+
interface ServiceWorkerHealthOptions {
|
|
165
|
+
store?: StoreReadyMethods;
|
|
166
|
+
}
|
|
159
167
|
type HealthSubscriber = (status: HealthStatus, isHealthy: boolean) => void;
|
|
160
168
|
/**
|
|
161
169
|
* Subscribe to health status changes from outside React.
|
|
@@ -192,7 +200,7 @@ declare const ServiceWorkerHealthProvider: FC<ServiceWorkerHealthProviderProps>;
|
|
|
192
200
|
* @example
|
|
193
201
|
* ```tsx
|
|
194
202
|
* function App() {
|
|
195
|
-
* const { isHealthy, isRecovering } = useServiceWorkerHealth();
|
|
203
|
+
* const { isHealthy, isRecovering } = useServiceWorkerHealth({ store: appStore });
|
|
196
204
|
*
|
|
197
205
|
* if (!isHealthy) {
|
|
198
206
|
* return (
|
|
@@ -207,7 +215,7 @@ declare const ServiceWorkerHealthProvider: FC<ServiceWorkerHealthProviderProps>;
|
|
|
207
215
|
* }
|
|
208
216
|
* ```
|
|
209
217
|
*/
|
|
210
|
-
declare function useServiceWorkerHealth():
|
|
218
|
+
declare function useServiceWorkerHealth(options?: ServiceWorkerHealthOptions): ServiceWorkerHealthResult;
|
|
211
219
|
/**
|
|
212
220
|
* Lightweight hook that directly consumes BridgeContext without needing
|
|
213
221
|
* ServiceWorkerHealthProvider. Use this for simple cases where you don't
|
|
@@ -222,11 +230,12 @@ declare function useServiceWorkerHealth(): ServiceWorkerHealthContextValue;
|
|
|
222
230
|
* }
|
|
223
231
|
* ```
|
|
224
232
|
*/
|
|
225
|
-
declare function useServiceWorkerHealthSimple(): {
|
|
233
|
+
declare function useServiceWorkerHealthSimple(options?: ServiceWorkerHealthOptions): {
|
|
226
234
|
isHealthy: boolean;
|
|
227
235
|
isRecovering: boolean;
|
|
228
236
|
isLoading: boolean;
|
|
229
237
|
reconnect: () => void;
|
|
238
|
+
storeReady: boolean;
|
|
230
239
|
};
|
|
231
240
|
|
|
232
241
|
export { BridgeProvider, ServiceWorkerHealthProvider, getHealthStatus, subscribeToHealth, useBridge, useBridgeQuery, useConnectionStatus, useServiceWorkerHealth, useServiceWorkerHealthSimple };
|
package/dist/index.js
CHANGED
|
@@ -5,15 +5,17 @@ const BRIDGE_ENABLE_LOGS = true;
|
|
|
5
5
|
const CONFIG = {
|
|
6
6
|
RETRY_AFTER: 1e3,
|
|
7
7
|
MAX_RETRIES: 10,
|
|
8
|
-
PING_INTERVAL:
|
|
9
|
-
// Check every
|
|
8
|
+
PING_INTERVAL: 3e3,
|
|
9
|
+
// Check every 3s (balance between responsiveness and false positives)
|
|
10
10
|
MAX_RETRY_COOLDOWN: 3e4,
|
|
11
11
|
DEFAULT_TIMEOUT: 1e4,
|
|
12
12
|
MAX_RETRY_DELAY: 3e4,
|
|
13
|
-
PING_TIMEOUT:
|
|
13
|
+
PING_TIMEOUT: 5e3,
|
|
14
|
+
// Give SW 5s to respond (handles busy periods)
|
|
14
15
|
ERROR_CHECK_INTERVAL: 100,
|
|
15
16
|
MAX_ERROR_CHECKS: 10,
|
|
16
|
-
CONSECUTIVE_FAILURE_THRESHOLD:
|
|
17
|
+
CONSECUTIVE_FAILURE_THRESHOLD: 3,
|
|
18
|
+
// Require 3 consecutive failures (9s total) before reconnecting
|
|
17
19
|
RECONNECT_DELAY: 100,
|
|
18
20
|
PORT_NAME: "chroma-bridge",
|
|
19
21
|
// Service worker restart retry settings (indefinite retries)
|
|
@@ -275,7 +277,8 @@ const BridgeProvider = ({
|
|
|
275
277
|
[onError, updateStatus]
|
|
276
278
|
);
|
|
277
279
|
const cleanup = useCallback((emitDisconnect = true) => {
|
|
278
|
-
|
|
280
|
+
const wasConnected = isConnectedRef.current || portRef.current !== null;
|
|
281
|
+
if (emitDisconnect && wasConnected) {
|
|
279
282
|
eventListenersRef.current.get("bridge:disconnected")?.forEach((handler) => {
|
|
280
283
|
try {
|
|
281
284
|
handler(void 0);
|
|
@@ -411,7 +414,7 @@ const BridgeProvider = ({
|
|
|
411
414
|
return;
|
|
412
415
|
}
|
|
413
416
|
isConnectingRef.current = true;
|
|
414
|
-
cleanup();
|
|
417
|
+
cleanup(false);
|
|
415
418
|
if (!chrome?.runtime?.connect) {
|
|
416
419
|
handleError(new Error("Chrome runtime not available"));
|
|
417
420
|
isConnectingRef.current = false;
|
|
@@ -438,7 +441,7 @@ const BridgeProvider = ({
|
|
|
438
441
|
if (BRIDGE_ENABLE_LOGS) {
|
|
439
442
|
console.warn("[Bridge] Service worker not ready (may be restarting)...");
|
|
440
443
|
}
|
|
441
|
-
cleanup();
|
|
444
|
+
cleanup(false);
|
|
442
445
|
isConnectingRef.current = false;
|
|
443
446
|
scheduleSwRestartReconnect(connect);
|
|
444
447
|
}
|
|
@@ -481,7 +484,7 @@ const BridgeProvider = ({
|
|
|
481
484
|
clearTimeoutSafe(triggerReconnectTimeoutRef);
|
|
482
485
|
triggerReconnectTimeoutRef.current = setTimeout(() => {
|
|
483
486
|
if (!isMountedRef.current) return;
|
|
484
|
-
cleanup();
|
|
487
|
+
cleanup(false);
|
|
485
488
|
connect();
|
|
486
489
|
}, CONFIG.RECONNECT_DELAY);
|
|
487
490
|
};
|
|
@@ -762,6 +765,17 @@ const useConnectionStatus = (store) => {
|
|
|
762
765
|
};
|
|
763
766
|
};
|
|
764
767
|
|
|
768
|
+
const noopSubscribe = () => () => {
|
|
769
|
+
};
|
|
770
|
+
const alwaysTrue = () => true;
|
|
771
|
+
const ssrFallback = () => false;
|
|
772
|
+
function useStoreReady(store) {
|
|
773
|
+
return useSyncExternalStore(
|
|
774
|
+
store?.onReady ?? noopSubscribe,
|
|
775
|
+
store?.isReady ?? alwaysTrue,
|
|
776
|
+
ssrFallback
|
|
777
|
+
);
|
|
778
|
+
}
|
|
765
779
|
const globalSubscribers = /* @__PURE__ */ new Set();
|
|
766
780
|
function subscribeToHealth(callback) {
|
|
767
781
|
globalSubscribers.add(callback);
|
|
@@ -832,24 +846,33 @@ const ServiceWorkerHealthProvider = ({
|
|
|
832
846
|
);
|
|
833
847
|
return /* @__PURE__ */ jsx(ServiceWorkerHealthContext.Provider, { value, children });
|
|
834
848
|
};
|
|
835
|
-
function useServiceWorkerHealth() {
|
|
849
|
+
function useServiceWorkerHealth(options) {
|
|
836
850
|
const context = useContext(ServiceWorkerHealthContext);
|
|
837
851
|
if (!context) {
|
|
838
852
|
throw new Error(
|
|
839
853
|
"useServiceWorkerHealth must be used within a ServiceWorkerHealthProvider. Wrap your app with <ServiceWorkerHealthProvider> inside <BridgeProvider>."
|
|
840
854
|
);
|
|
841
855
|
}
|
|
842
|
-
|
|
856
|
+
const storeReady = useStoreReady(options?.store);
|
|
857
|
+
const combinedHealthy = context.isHealthy && (options?.store ? storeReady : true);
|
|
858
|
+
const combinedLoading = context.isLoading || (options?.store ? !storeReady : false);
|
|
859
|
+
return {
|
|
860
|
+
...context,
|
|
861
|
+
isHealthy: combinedHealthy,
|
|
862
|
+
isLoading: combinedLoading,
|
|
863
|
+
storeReady
|
|
864
|
+
};
|
|
843
865
|
}
|
|
844
|
-
function useServiceWorkerHealthSimple() {
|
|
866
|
+
function useServiceWorkerHealthSimple(options) {
|
|
845
867
|
const bridgeContext = useContext(BridgeContext);
|
|
846
|
-
const
|
|
868
|
+
const storeReady = useStoreReady(options?.store);
|
|
869
|
+
const isHealthy = bridgeContext?.status === "connected" && bridgeContext?.isServiceWorkerAlive === true && (options?.store ? storeReady : true);
|
|
847
870
|
const isRecovering = bridgeContext?.status === "reconnecting" || bridgeContext?.status === "connecting";
|
|
848
|
-
const isLoading = !isHealthy && (isRecovering || !bridgeContext);
|
|
871
|
+
const isLoading = !isHealthy && (isRecovering || !bridgeContext) || (options?.store ? !storeReady : false);
|
|
849
872
|
const reconnect = useCallback(() => {
|
|
850
873
|
bridgeContext?.reconnect();
|
|
851
874
|
}, [bridgeContext]);
|
|
852
|
-
return { isHealthy, isRecovering, isLoading, reconnect };
|
|
875
|
+
return { isHealthy, isRecovering, isLoading, reconnect, storeReady };
|
|
853
876
|
}
|
|
854
877
|
|
|
855
878
|
export { BridgeProvider, ServiceWorkerHealthProvider, getHealthStatus, subscribeToHealth, useBridge, useBridgeMutation, useBridgeQuery, useConnectionStatus, useServiceWorkerHealth, useServiceWorkerHealthSimple };
|