@chromahq/react 1.0.41 → 1.0.43
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 +22 -0
- package/dist/index.js +136 -15
- package/package.json +1 -1
package/dist/index.d.ts
CHANGED
|
@@ -1,5 +1,27 @@
|
|
|
1
1
|
import { FC, ReactNode } from 'react';
|
|
2
2
|
|
|
3
|
+
interface BridgeDiagnosticsSnapshot {
|
|
4
|
+
lastUpdatedAt: number;
|
|
5
|
+
portDisconnects: number;
|
|
6
|
+
lastDisconnectError?: string;
|
|
7
|
+
lastDisconnectAt?: number;
|
|
8
|
+
sendMessageFallbacks: number;
|
|
9
|
+
lastFallbackReason?: string;
|
|
10
|
+
lastFallbackAt?: number;
|
|
11
|
+
sendMessageFallbackErrors: number;
|
|
12
|
+
lastFallbackError?: string;
|
|
13
|
+
pingFailures: number;
|
|
14
|
+
lastPingFailureAt?: number;
|
|
15
|
+
}
|
|
16
|
+
interface BridgeDiagnosticsApi {
|
|
17
|
+
get: () => BridgeDiagnosticsSnapshot;
|
|
18
|
+
reset: () => void;
|
|
19
|
+
}
|
|
20
|
+
declare global {
|
|
21
|
+
interface Window {
|
|
22
|
+
__CHROMA_BRIDGE_DIAGNOSTICS__?: BridgeDiagnosticsApi;
|
|
23
|
+
}
|
|
24
|
+
}
|
|
3
25
|
type ConnectionStatus = 'connecting' | 'connected' | 'disconnected' | 'error' | 'reconnecting';
|
|
4
26
|
interface Bridge {
|
|
5
27
|
send: <Req = unknown, Res = unknown>(key: string, payload?: Req, timeoutDuration?: number) => Promise<Res>;
|
package/dist/index.js
CHANGED
|
@@ -2,6 +2,68 @@ import { jsx } from 'react/jsx-runtime';
|
|
|
2
2
|
import { createContext, useState, useRef, useEffect, useCallback, useMemo, useContext, useSyncExternalStore } from 'react';
|
|
3
3
|
|
|
4
4
|
const BRIDGE_ENABLE_LOGS = true;
|
|
5
|
+
const DIRECT_MESSAGE_FLAG = "__CHROMA_BRIDGE_DIRECT_MESSAGE__";
|
|
6
|
+
const createInitialDiagnosticsState = () => ({
|
|
7
|
+
lastUpdatedAt: Date.now(),
|
|
8
|
+
portDisconnects: 0,
|
|
9
|
+
lastDisconnectError: void 0,
|
|
10
|
+
lastDisconnectAt: void 0,
|
|
11
|
+
sendMessageFallbacks: 0,
|
|
12
|
+
lastFallbackReason: void 0,
|
|
13
|
+
lastFallbackAt: void 0,
|
|
14
|
+
sendMessageFallbackErrors: 0,
|
|
15
|
+
lastFallbackError: void 0,
|
|
16
|
+
pingFailures: 0,
|
|
17
|
+
lastPingFailureAt: void 0
|
|
18
|
+
});
|
|
19
|
+
const bridgeDiagnosticsState = createInitialDiagnosticsState();
|
|
20
|
+
const updateDiagnosticsState = (mutator) => {
|
|
21
|
+
mutator(bridgeDiagnosticsState);
|
|
22
|
+
bridgeDiagnosticsState.lastUpdatedAt = Date.now();
|
|
23
|
+
};
|
|
24
|
+
const resetDiagnosticsState = () => {
|
|
25
|
+
Object.assign(bridgeDiagnosticsState, createInitialDiagnosticsState());
|
|
26
|
+
};
|
|
27
|
+
const attachDiagnosticsApi = () => {
|
|
28
|
+
if (typeof window === "undefined") {
|
|
29
|
+
return;
|
|
30
|
+
}
|
|
31
|
+
window.__CHROMA_BRIDGE_DIAGNOSTICS__ = {
|
|
32
|
+
get: () => ({ ...bridgeDiagnosticsState }),
|
|
33
|
+
reset: () => {
|
|
34
|
+
resetDiagnosticsState();
|
|
35
|
+
}
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
attachDiagnosticsApi();
|
|
39
|
+
const recordDiagnostics = {
|
|
40
|
+
portDisconnect: (error) => {
|
|
41
|
+
updateDiagnosticsState((state) => {
|
|
42
|
+
state.portDisconnects += 1;
|
|
43
|
+
state.lastDisconnectError = error;
|
|
44
|
+
state.lastDisconnectAt = Date.now();
|
|
45
|
+
});
|
|
46
|
+
},
|
|
47
|
+
fallbackSent: (reason) => {
|
|
48
|
+
updateDiagnosticsState((state) => {
|
|
49
|
+
state.sendMessageFallbacks += 1;
|
|
50
|
+
state.lastFallbackReason = reason;
|
|
51
|
+
state.lastFallbackAt = Date.now();
|
|
52
|
+
});
|
|
53
|
+
},
|
|
54
|
+
fallbackError: (error) => {
|
|
55
|
+
updateDiagnosticsState((state) => {
|
|
56
|
+
state.sendMessageFallbackErrors += 1;
|
|
57
|
+
state.lastFallbackError = error;
|
|
58
|
+
});
|
|
59
|
+
},
|
|
60
|
+
pingFailure: () => {
|
|
61
|
+
updateDiagnosticsState((state) => {
|
|
62
|
+
state.pingFailures += 1;
|
|
63
|
+
state.lastPingFailureAt = Date.now();
|
|
64
|
+
});
|
|
65
|
+
}
|
|
66
|
+
};
|
|
5
67
|
const CONFIG = {
|
|
6
68
|
RETRY_AFTER: 1e3,
|
|
7
69
|
MAX_RETRIES: 10,
|
|
@@ -71,11 +133,71 @@ function createBridgeInstance(deps) {
|
|
|
71
133
|
};
|
|
72
134
|
const send = (key, payload, timeoutDuration = defaultTimeout) => {
|
|
73
135
|
return new Promise((resolve, reject) => {
|
|
74
|
-
if (!portRef.current) {
|
|
75
|
-
reject(new Error("Bridge disconnected"));
|
|
76
|
-
return;
|
|
77
|
-
}
|
|
78
136
|
const id = `msg_${++messageIdRef.current}`;
|
|
137
|
+
const finalizePendingWithError = (error) => {
|
|
138
|
+
const pending = pendingRequestsRef.current.get(id);
|
|
139
|
+
if (!pending) return;
|
|
140
|
+
clearTimeout(pending.timeout);
|
|
141
|
+
pendingRequestsRef.current.delete(id);
|
|
142
|
+
pending.reject(error instanceof Error ? error : new Error(error));
|
|
143
|
+
};
|
|
144
|
+
const triggerRuntimeFallback = (reason) => {
|
|
145
|
+
{
|
|
146
|
+
console.warn(`[Bridge] Falling back to runtime.sendMessage (${reason})`);
|
|
147
|
+
}
|
|
148
|
+
recordDiagnostics.fallbackSent(reason);
|
|
149
|
+
onReconnectNeeded();
|
|
150
|
+
if (!chrome?.runtime?.sendMessage) {
|
|
151
|
+
const message = "chrome.runtime.sendMessage not available";
|
|
152
|
+
recordDiagnostics.fallbackError(message);
|
|
153
|
+
finalizePendingWithError(message);
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
try {
|
|
157
|
+
chrome.runtime.sendMessage(
|
|
158
|
+
{
|
|
159
|
+
id,
|
|
160
|
+
key,
|
|
161
|
+
payload,
|
|
162
|
+
metadata: { transport: "direct", fallbackReason: reason },
|
|
163
|
+
[DIRECT_MESSAGE_FLAG]: true
|
|
164
|
+
},
|
|
165
|
+
(response) => {
|
|
166
|
+
const runtimeError = consumeRuntimeError();
|
|
167
|
+
const pending = pendingRequestsRef.current.get(id);
|
|
168
|
+
if (!pending) return;
|
|
169
|
+
if (runtimeError) {
|
|
170
|
+
recordDiagnostics.fallbackError(runtimeError);
|
|
171
|
+
clearTimeout(pending.timeout);
|
|
172
|
+
pendingRequestsRef.current.delete(id);
|
|
173
|
+
pending.reject(new Error(runtimeError));
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
if (!response) {
|
|
177
|
+
const message = "No response from service worker";
|
|
178
|
+
recordDiagnostics.fallbackError(message);
|
|
179
|
+
clearTimeout(pending.timeout);
|
|
180
|
+
pendingRequestsRef.current.delete(id);
|
|
181
|
+
pending.reject(new Error(message));
|
|
182
|
+
return;
|
|
183
|
+
}
|
|
184
|
+
clearTimeout(pending.timeout);
|
|
185
|
+
pendingRequestsRef.current.delete(id);
|
|
186
|
+
consecutiveTimeoutsRef.current = 0;
|
|
187
|
+
if (response.error) {
|
|
188
|
+
recordDiagnostics.fallbackError(response.error);
|
|
189
|
+
pending.reject(new Error(response.error));
|
|
190
|
+
} else {
|
|
191
|
+
pending.resolve(response.data);
|
|
192
|
+
}
|
|
193
|
+
}
|
|
194
|
+
);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
const message = error instanceof Error ? error.message : "chrome.runtime.sendMessage failed";
|
|
197
|
+
recordDiagnostics.fallbackError(message);
|
|
198
|
+
finalizePendingWithError(error instanceof Error ? error : new Error(message));
|
|
199
|
+
}
|
|
200
|
+
};
|
|
79
201
|
const timeout = setTimeout(() => {
|
|
80
202
|
if (!pendingRequestsRef.current.has(id)) return;
|
|
81
203
|
pendingRequestsRef.current.delete(id);
|
|
@@ -106,17 +228,16 @@ function createBridgeInstance(deps) {
|
|
|
106
228
|
timeout,
|
|
107
229
|
timeoutDuration
|
|
108
230
|
});
|
|
231
|
+
if (!portRef.current) {
|
|
232
|
+
triggerRuntimeFallback("port-unavailable");
|
|
233
|
+
return;
|
|
234
|
+
}
|
|
109
235
|
try {
|
|
110
236
|
portRef.current.postMessage({ id, key, payload });
|
|
111
237
|
setTimeout(() => {
|
|
112
238
|
const errorMessage = consumeRuntimeError();
|
|
113
239
|
if (errorMessage && pendingRequestsRef.current.has(id)) {
|
|
114
|
-
|
|
115
|
-
if (pending) {
|
|
116
|
-
clearTimeout(pending.timeout);
|
|
117
|
-
pendingRequestsRef.current.delete(id);
|
|
118
|
-
reject(new Error(errorMessage));
|
|
119
|
-
}
|
|
240
|
+
finalizePendingWithError(errorMessage);
|
|
120
241
|
}
|
|
121
242
|
}, 0);
|
|
122
243
|
const immediateError = consumeRuntimeError();
|
|
@@ -124,12 +245,10 @@ function createBridgeInstance(deps) {
|
|
|
124
245
|
throw new Error(immediateError);
|
|
125
246
|
}
|
|
126
247
|
} catch (e) {
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
clearTimeout(pending.timeout);
|
|
130
|
-
pendingRequestsRef.current.delete(id);
|
|
248
|
+
{
|
|
249
|
+
console.warn("[Bridge] Port send failed, attempting fallback", e);
|
|
131
250
|
}
|
|
132
|
-
|
|
251
|
+
triggerRuntimeFallback("port-postmessage-error");
|
|
133
252
|
}
|
|
134
253
|
});
|
|
135
254
|
};
|
|
@@ -235,6 +354,7 @@ function startHealthMonitor(deps) {
|
|
|
235
354
|
consecutivePingFailuresRef.current = 0;
|
|
236
355
|
return;
|
|
237
356
|
}
|
|
357
|
+
recordDiagnostics.pingFailure();
|
|
238
358
|
consecutivePingFailuresRef.current++;
|
|
239
359
|
{
|
|
240
360
|
console.warn(`[Bridge] Ping failed (${consecutivePingFailuresRef.current}x)`);
|
|
@@ -502,6 +622,7 @@ const BridgeProvider = ({
|
|
|
502
622
|
}
|
|
503
623
|
isConnectingRef.current = false;
|
|
504
624
|
const disconnectError = consumeRuntimeError();
|
|
625
|
+
recordDiagnostics.portDisconnect(disconnectError);
|
|
505
626
|
if (BRIDGE_ENABLE_LOGS) {
|
|
506
627
|
console.warn("[Bridge] Disconnect error:", disconnectError || "(none)");
|
|
507
628
|
console.warn("[Bridge] isMounted:", isMountedRef.current);
|