@chromahq/react 1.0.9 → 1.0.11
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 +4 -1
- package/dist/index.js +67 -13
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,21 +1,24 @@
|
|
|
1
|
-
type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error';
|
|
1
|
+
type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error' | 'reconnecting';
|
|
2
2
|
interface Bridge {
|
|
3
3
|
send: <Req = unknown, Res = unknown>(key: string, payload?: Req, timeoutDuration?: number) => Promise<Res>;
|
|
4
4
|
broadcast: (key: string, payload: any) => void;
|
|
5
5
|
on: (key: string, handler: (payload: any) => void) => void;
|
|
6
6
|
off: (key: string, handler: (payload: any) => void) => void;
|
|
7
7
|
isConnected: boolean;
|
|
8
|
+
ping: () => Promise<boolean>;
|
|
8
9
|
}
|
|
9
10
|
interface BridgeContextValue {
|
|
10
11
|
bridge: Bridge | null;
|
|
11
12
|
status: ConnectionStatus;
|
|
12
13
|
error: Error | null;
|
|
13
14
|
reconnect: () => void;
|
|
15
|
+
isServiceWorkerAlive: boolean;
|
|
14
16
|
}
|
|
15
17
|
interface Props {
|
|
16
18
|
children: React.ReactNode;
|
|
17
19
|
retryAfter?: number;
|
|
18
20
|
maxRetries?: number;
|
|
21
|
+
pingInterval?: number;
|
|
19
22
|
onConnectionChange?: (status: ConnectionStatus) => void;
|
|
20
23
|
onError?: (error: Error) => void;
|
|
21
24
|
}
|
package/dist/index.js
CHANGED
|
@@ -4,14 +4,16 @@ import { createContext, useState, useRef, useCallback, useEffect, useMemo, useCo
|
|
|
4
4
|
const BridgeContext = createContext(null);
|
|
5
5
|
const BridgeProvider = ({
|
|
6
6
|
children,
|
|
7
|
-
retryAfter =
|
|
8
|
-
maxRetries =
|
|
7
|
+
retryAfter = 1e3,
|
|
8
|
+
maxRetries = 10,
|
|
9
|
+
pingInterval = 5e3,
|
|
9
10
|
onConnectionChange,
|
|
10
11
|
onError
|
|
11
12
|
}) => {
|
|
12
13
|
const [bridge, setBridge] = useState(null);
|
|
13
14
|
const [status, setStatus] = useState("connecting");
|
|
14
15
|
const [error, setError] = useState(null);
|
|
16
|
+
const [isServiceWorkerAlive, setIsServiceWorkerAlive] = useState(false);
|
|
15
17
|
const portRef = useRef(null);
|
|
16
18
|
const pendingRef = useRef(
|
|
17
19
|
/* @__PURE__ */ new Map()
|
|
@@ -22,6 +24,8 @@ const BridgeProvider = ({
|
|
|
22
24
|
const retryCountRef = useRef(0);
|
|
23
25
|
const isConnectingRef = useRef(false);
|
|
24
26
|
const errorCheckIntervalRef = useRef(null);
|
|
27
|
+
const pingIntervalRef = useRef(null);
|
|
28
|
+
const consecutiveTimeoutsRef = useRef(0);
|
|
25
29
|
const updateStatus = useCallback(
|
|
26
30
|
(newStatus) => {
|
|
27
31
|
setStatus(newStatus);
|
|
@@ -46,6 +50,10 @@ const BridgeProvider = ({
|
|
|
46
50
|
clearInterval(errorCheckIntervalRef.current);
|
|
47
51
|
errorCheckIntervalRef.current = null;
|
|
48
52
|
}
|
|
53
|
+
if (pingIntervalRef.current) {
|
|
54
|
+
clearInterval(pingIntervalRef.current);
|
|
55
|
+
pingIntervalRef.current = null;
|
|
56
|
+
}
|
|
49
57
|
if (portRef.current) {
|
|
50
58
|
try {
|
|
51
59
|
portRef.current.disconnect();
|
|
@@ -53,11 +61,13 @@ const BridgeProvider = ({
|
|
|
53
61
|
}
|
|
54
62
|
portRef.current = null;
|
|
55
63
|
}
|
|
56
|
-
pendingRef.current.forEach(({ reject }) => {
|
|
64
|
+
pendingRef.current.forEach(({ reject, timeout }) => {
|
|
65
|
+
clearTimeout(timeout);
|
|
57
66
|
reject(new Error("Bridge disconnected"));
|
|
58
67
|
});
|
|
59
68
|
pendingRef.current.clear();
|
|
60
69
|
eventListenersRef.current.clear();
|
|
70
|
+
setIsServiceWorkerAlive(false);
|
|
61
71
|
setBridge(null);
|
|
62
72
|
isConnectingRef.current = false;
|
|
63
73
|
}, []);
|
|
@@ -110,11 +120,13 @@ const BridgeProvider = ({
|
|
|
110
120
|
}, 100);
|
|
111
121
|
port.onMessage.addListener((msg) => {
|
|
112
122
|
if (msg.id && pendingRef.current.has(msg.id)) {
|
|
113
|
-
const
|
|
123
|
+
const pending = pendingRef.current.get(msg.id);
|
|
124
|
+
clearTimeout(pending.timeout);
|
|
125
|
+
consecutiveTimeoutsRef.current = 0;
|
|
114
126
|
if (msg.error) {
|
|
115
|
-
reject(new Error(msg.error));
|
|
127
|
+
pending.reject(new Error(msg.error));
|
|
116
128
|
} else {
|
|
117
|
-
resolve(msg.data);
|
|
129
|
+
pending.resolve(msg.data);
|
|
118
130
|
}
|
|
119
131
|
pendingRef.current.delete(msg.id);
|
|
120
132
|
} else if (msg.type === "broadcast" && msg.key) {
|
|
@@ -156,13 +168,29 @@ const BridgeProvider = ({
|
|
|
156
168
|
return;
|
|
157
169
|
}
|
|
158
170
|
const id = `msg${uidRef.current++}`;
|
|
159
|
-
pendingRef.current.set(id, { resolve, reject });
|
|
160
171
|
const timeout = setTimeout(() => {
|
|
161
172
|
if (pendingRef.current.has(id)) {
|
|
162
173
|
pendingRef.current.delete(id);
|
|
163
|
-
|
|
174
|
+
consecutiveTimeoutsRef.current++;
|
|
175
|
+
if (consecutiveTimeoutsRef.current >= 2) {
|
|
176
|
+
console.warn(
|
|
177
|
+
"[Bridge] Multiple timeouts detected, service worker may be unresponsive. Reconnecting..."
|
|
178
|
+
);
|
|
179
|
+
setIsServiceWorkerAlive(false);
|
|
180
|
+
updateStatus("reconnecting");
|
|
181
|
+
cleanup();
|
|
182
|
+
retryCountRef.current = 0;
|
|
183
|
+
isConnectingRef.current = false;
|
|
184
|
+
setTimeout(connect, 500);
|
|
185
|
+
}
|
|
186
|
+
reject(
|
|
187
|
+
new Error(
|
|
188
|
+
`Request timed out after ${timeoutDuration} ms for key: ${key} with id: ${id}`
|
|
189
|
+
)
|
|
190
|
+
);
|
|
164
191
|
}
|
|
165
192
|
}, timeoutDuration);
|
|
193
|
+
pendingRef.current.set(id, { resolve, reject, timeout });
|
|
166
194
|
try {
|
|
167
195
|
portRef.current.postMessage({ id, key, payload });
|
|
168
196
|
setTimeout(() => {
|
|
@@ -171,7 +199,8 @@ const BridgeProvider = ({
|
|
|
171
199
|
console.warn("[Bridge] Async runtime error after postMessage:", errorMessage);
|
|
172
200
|
chrome.runtime.lastError;
|
|
173
201
|
if (pendingRef.current.has(id)) {
|
|
174
|
-
|
|
202
|
+
const pending = pendingRef.current.get(id);
|
|
203
|
+
if (pending) clearTimeout(pending.timeout);
|
|
175
204
|
pendingRef.current.delete(id);
|
|
176
205
|
reject(new Error(errorMessage || "Async send failed"));
|
|
177
206
|
}
|
|
@@ -181,7 +210,8 @@ const BridgeProvider = ({
|
|
|
181
210
|
throw new Error(chrome.runtime.lastError.message || "Failed to send message");
|
|
182
211
|
}
|
|
183
212
|
} catch (e) {
|
|
184
|
-
|
|
213
|
+
const pending = pendingRef.current.get(id);
|
|
214
|
+
if (pending) clearTimeout(pending.timeout);
|
|
185
215
|
pendingRef.current.delete(id);
|
|
186
216
|
if (chrome.runtime.lastError) {
|
|
187
217
|
console.warn(
|
|
@@ -222,13 +252,35 @@ const BridgeProvider = ({
|
|
|
222
252
|
}
|
|
223
253
|
}
|
|
224
254
|
},
|
|
255
|
+
ping: async () => {
|
|
256
|
+
try {
|
|
257
|
+
await bridgeInstance.send("__ping__", void 0, 2e3);
|
|
258
|
+
return true;
|
|
259
|
+
} catch {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
},
|
|
225
263
|
isConnected: true
|
|
226
264
|
};
|
|
227
265
|
setBridge(bridgeInstance);
|
|
228
266
|
updateStatus("connected");
|
|
267
|
+
setIsServiceWorkerAlive(true);
|
|
229
268
|
setError(null);
|
|
230
269
|
retryCountRef.current = 0;
|
|
270
|
+
consecutiveTimeoutsRef.current = 0;
|
|
231
271
|
isConnectingRef.current = false;
|
|
272
|
+
if (pingIntervalRef.current) {
|
|
273
|
+
clearInterval(pingIntervalRef.current);
|
|
274
|
+
}
|
|
275
|
+
pingIntervalRef.current = setInterval(async () => {
|
|
276
|
+
if (bridgeInstance && portRef.current) {
|
|
277
|
+
const alive = await bridgeInstance.ping();
|
|
278
|
+
setIsServiceWorkerAlive(alive);
|
|
279
|
+
if (!alive) {
|
|
280
|
+
console.warn("[Bridge] Service worker ping failed, may be unresponsive");
|
|
281
|
+
}
|
|
282
|
+
}
|
|
283
|
+
}, pingInterval);
|
|
232
284
|
} catch (e) {
|
|
233
285
|
isConnectingRef.current = false;
|
|
234
286
|
const error2 = e instanceof Error ? e : new Error("Connection failed");
|
|
@@ -238,9 +290,10 @@ const BridgeProvider = ({
|
|
|
238
290
|
reconnectTimeoutRef.current = setTimeout(connect, retryAfter);
|
|
239
291
|
}
|
|
240
292
|
}
|
|
241
|
-
}, [retryAfter, maxRetries, handleError, updateStatus, cleanup]);
|
|
293
|
+
}, [retryAfter, maxRetries, pingInterval, handleError, updateStatus, cleanup]);
|
|
242
294
|
const reconnect = useCallback(() => {
|
|
243
295
|
retryCountRef.current = 0;
|
|
296
|
+
consecutiveTimeoutsRef.current = 0;
|
|
244
297
|
updateStatus("connecting");
|
|
245
298
|
connect();
|
|
246
299
|
}, [connect, updateStatus]);
|
|
@@ -253,9 +306,10 @@ const BridgeProvider = ({
|
|
|
253
306
|
bridge,
|
|
254
307
|
status,
|
|
255
308
|
error,
|
|
256
|
-
reconnect
|
|
309
|
+
reconnect,
|
|
310
|
+
isServiceWorkerAlive
|
|
257
311
|
}),
|
|
258
|
-
[bridge, status, error, reconnect]
|
|
312
|
+
[bridge, status, error, reconnect, isServiceWorkerAlive]
|
|
259
313
|
);
|
|
260
314
|
return /* @__PURE__ */ jsx(BridgeContext.Provider, { value: contextValue, children });
|
|
261
315
|
};
|