@chromahq/react 1.0.10 → 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 +63 -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,17 +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);
|
|
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
|
+
}
|
|
163
186
|
reject(
|
|
164
187
|
new Error(
|
|
165
|
-
`Request timed out after ${timeoutDuration} ms for key: ${key} with id: ${id}
|
|
188
|
+
`Request timed out after ${timeoutDuration} ms for key: ${key} with id: ${id}`
|
|
166
189
|
)
|
|
167
190
|
);
|
|
168
191
|
}
|
|
169
192
|
}, timeoutDuration);
|
|
193
|
+
pendingRef.current.set(id, { resolve, reject, timeout });
|
|
170
194
|
try {
|
|
171
195
|
portRef.current.postMessage({ id, key, payload });
|
|
172
196
|
setTimeout(() => {
|
|
@@ -175,7 +199,8 @@ const BridgeProvider = ({
|
|
|
175
199
|
console.warn("[Bridge] Async runtime error after postMessage:", errorMessage);
|
|
176
200
|
chrome.runtime.lastError;
|
|
177
201
|
if (pendingRef.current.has(id)) {
|
|
178
|
-
|
|
202
|
+
const pending = pendingRef.current.get(id);
|
|
203
|
+
if (pending) clearTimeout(pending.timeout);
|
|
179
204
|
pendingRef.current.delete(id);
|
|
180
205
|
reject(new Error(errorMessage || "Async send failed"));
|
|
181
206
|
}
|
|
@@ -185,7 +210,8 @@ const BridgeProvider = ({
|
|
|
185
210
|
throw new Error(chrome.runtime.lastError.message || "Failed to send message");
|
|
186
211
|
}
|
|
187
212
|
} catch (e) {
|
|
188
|
-
|
|
213
|
+
const pending = pendingRef.current.get(id);
|
|
214
|
+
if (pending) clearTimeout(pending.timeout);
|
|
189
215
|
pendingRef.current.delete(id);
|
|
190
216
|
if (chrome.runtime.lastError) {
|
|
191
217
|
console.warn(
|
|
@@ -226,13 +252,35 @@ const BridgeProvider = ({
|
|
|
226
252
|
}
|
|
227
253
|
}
|
|
228
254
|
},
|
|
255
|
+
ping: async () => {
|
|
256
|
+
try {
|
|
257
|
+
await bridgeInstance.send("__ping__", void 0, 2e3);
|
|
258
|
+
return true;
|
|
259
|
+
} catch {
|
|
260
|
+
return false;
|
|
261
|
+
}
|
|
262
|
+
},
|
|
229
263
|
isConnected: true
|
|
230
264
|
};
|
|
231
265
|
setBridge(bridgeInstance);
|
|
232
266
|
updateStatus("connected");
|
|
267
|
+
setIsServiceWorkerAlive(true);
|
|
233
268
|
setError(null);
|
|
234
269
|
retryCountRef.current = 0;
|
|
270
|
+
consecutiveTimeoutsRef.current = 0;
|
|
235
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);
|
|
236
284
|
} catch (e) {
|
|
237
285
|
isConnectingRef.current = false;
|
|
238
286
|
const error2 = e instanceof Error ? e : new Error("Connection failed");
|
|
@@ -242,9 +290,10 @@ const BridgeProvider = ({
|
|
|
242
290
|
reconnectTimeoutRef.current = setTimeout(connect, retryAfter);
|
|
243
291
|
}
|
|
244
292
|
}
|
|
245
|
-
}, [retryAfter, maxRetries, handleError, updateStatus, cleanup]);
|
|
293
|
+
}, [retryAfter, maxRetries, pingInterval, handleError, updateStatus, cleanup]);
|
|
246
294
|
const reconnect = useCallback(() => {
|
|
247
295
|
retryCountRef.current = 0;
|
|
296
|
+
consecutiveTimeoutsRef.current = 0;
|
|
248
297
|
updateStatus("connecting");
|
|
249
298
|
connect();
|
|
250
299
|
}, [connect, updateStatus]);
|
|
@@ -257,9 +306,10 @@ const BridgeProvider = ({
|
|
|
257
306
|
bridge,
|
|
258
307
|
status,
|
|
259
308
|
error,
|
|
260
|
-
reconnect
|
|
309
|
+
reconnect,
|
|
310
|
+
isServiceWorkerAlive
|
|
261
311
|
}),
|
|
262
|
-
[bridge, status, error, reconnect]
|
|
312
|
+
[bridge, status, error, reconnect, isServiceWorkerAlive]
|
|
263
313
|
);
|
|
264
314
|
return /* @__PURE__ */ jsx(BridgeContext.Provider, { value: contextValue, children });
|
|
265
315
|
};
|