@qontinui/ui-bridge 0.10.0 → 0.11.0
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.js +73 -40
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +73 -40
- package/dist/index.mjs.map +1 -1
- package/dist/react/index.d.mts +4 -3
- package/dist/react/index.d.ts +4 -3
- package/dist/react/index.js +73 -40
- package/dist/react/index.js.map +1 -1
- package/dist/react/index.mjs +73 -40
- package/dist/react/index.mjs.map +1 -1
- package/package.json +1 -1
package/dist/react/index.d.mts
CHANGED
|
@@ -1623,9 +1623,10 @@ interface UseCommandRelayOptions {
|
|
|
1623
1623
|
*
|
|
1624
1624
|
* - attaches `Authorization: Bearer <value>` to outbound
|
|
1625
1625
|
* `POST {basePath}/commands` and `POST {basePath}/heartbeat`;
|
|
1626
|
-
* -
|
|
1627
|
-
* `GET {basePath}/commands/stream`
|
|
1628
|
-
*
|
|
1626
|
+
* - attaches `Authorization: Bearer <value>` to the SSE
|
|
1627
|
+
* `GET {basePath}/commands/stream` request as well — the stream is
|
|
1628
|
+
* consumed via `fetch` streaming, so the header rides on the request
|
|
1629
|
+
* (no token in the URL).
|
|
1629
1630
|
*
|
|
1630
1631
|
* Called fresh on every outbound request — implementations should read
|
|
1631
1632
|
* the token from a live source (e.g. `sessionStorage`) so a token
|
package/dist/react/index.d.ts
CHANGED
|
@@ -1623,9 +1623,10 @@ interface UseCommandRelayOptions {
|
|
|
1623
1623
|
*
|
|
1624
1624
|
* - attaches `Authorization: Bearer <value>` to outbound
|
|
1625
1625
|
* `POST {basePath}/commands` and `POST {basePath}/heartbeat`;
|
|
1626
|
-
* -
|
|
1627
|
-
* `GET {basePath}/commands/stream`
|
|
1628
|
-
*
|
|
1626
|
+
* - attaches `Authorization: Bearer <value>` to the SSE
|
|
1627
|
+
* `GET {basePath}/commands/stream` request as well — the stream is
|
|
1628
|
+
* consumed via `fetch` streaming, so the header rides on the request
|
|
1629
|
+
* (no token in the URL).
|
|
1629
1630
|
*
|
|
1630
1631
|
* Called fresh on every outbound request — implementations should read
|
|
1631
1632
|
* the token from a live source (e.g. `sessionStorage`) so a token
|
package/dist/react/index.js
CHANGED
|
@@ -24948,7 +24948,7 @@ function exposeProviderOnWindow(internals) {
|
|
|
24948
24948
|
if (typeof window === "undefined") return;
|
|
24949
24949
|
const w = window;
|
|
24950
24950
|
const root = w.__UI_BRIDGE__ ?? (w.__UI_BRIDGE__ = {});
|
|
24951
|
-
root.version = "0.
|
|
24951
|
+
root.version = "0.11.0";
|
|
24952
24952
|
root.specs = { getGlobalSpecStore };
|
|
24953
24953
|
root.registry = internals.registry;
|
|
24954
24954
|
root.browserCapture = internals.browserCapture;
|
|
@@ -32932,11 +32932,16 @@ function transportHeaders(authHeader) {
|
|
|
32932
32932
|
}
|
|
32933
32933
|
return headers;
|
|
32934
32934
|
}
|
|
32935
|
-
function
|
|
32936
|
-
const
|
|
32937
|
-
|
|
32938
|
-
|
|
32939
|
-
|
|
32935
|
+
function parseSSEDataBlock(block) {
|
|
32936
|
+
const dataLines = [];
|
|
32937
|
+
for (const line of block.split("\n")) {
|
|
32938
|
+
if (line.startsWith(":")) continue;
|
|
32939
|
+
if (!line.startsWith("data:")) continue;
|
|
32940
|
+
let value = line.slice(5);
|
|
32941
|
+
if (value.startsWith(" ")) value = value.slice(1);
|
|
32942
|
+
dataLines.push(value);
|
|
32943
|
+
}
|
|
32944
|
+
return dataLines.length > 0 ? dataLines.join("\n") : null;
|
|
32940
32945
|
}
|
|
32941
32946
|
function useCommandRelay(options) {
|
|
32942
32947
|
const enabled = options?.enabled ?? true;
|
|
@@ -32976,7 +32981,6 @@ function useCommandRelay(options) {
|
|
|
32976
32981
|
}),
|
|
32977
32982
|
[uiBridge, context]
|
|
32978
32983
|
);
|
|
32979
|
-
const eventSourceRef = react.useRef(null);
|
|
32980
32984
|
const reconnectTimeoutRef = react.useRef(null);
|
|
32981
32985
|
const processCommandRef = react.useRef(() => {
|
|
32982
32986
|
});
|
|
@@ -33052,10 +33056,6 @@ function useCommandRelay(options) {
|
|
|
33052
33056
|
processCommandRef.current = processCommand;
|
|
33053
33057
|
react.useEffect(() => {
|
|
33054
33058
|
if (!enabled) {
|
|
33055
|
-
if (eventSourceRef.current) {
|
|
33056
|
-
eventSourceRef.current.close();
|
|
33057
|
-
eventSourceRef.current = null;
|
|
33058
|
-
}
|
|
33059
33059
|
if (reconnectTimeoutRef.current) {
|
|
33060
33060
|
clearTimeout(reconnectTimeoutRef.current);
|
|
33061
33061
|
reconnectTimeoutRef.current = null;
|
|
@@ -33063,34 +33063,73 @@ function useCommandRelay(options) {
|
|
|
33063
33063
|
return;
|
|
33064
33064
|
}
|
|
33065
33065
|
let isMounted = true;
|
|
33066
|
+
let abortController = null;
|
|
33067
|
+
const closeStream = () => {
|
|
33068
|
+
if (abortController) {
|
|
33069
|
+
abortController.abort();
|
|
33070
|
+
abortController = null;
|
|
33071
|
+
}
|
|
33072
|
+
};
|
|
33073
|
+
const scheduleReconnect = () => {
|
|
33074
|
+
if (!isMounted) return;
|
|
33075
|
+
if (reconnectTimeoutRef.current) return;
|
|
33076
|
+
reconnectTimeoutRef.current = setTimeout(() => {
|
|
33077
|
+
reconnectTimeoutRef.current = null;
|
|
33078
|
+
if (isMounted) connect();
|
|
33079
|
+
}, SSE_RECONNECT_DELAY_MS);
|
|
33080
|
+
};
|
|
33066
33081
|
const connect = () => {
|
|
33067
33082
|
if (!isMounted) return;
|
|
33068
|
-
|
|
33069
|
-
const
|
|
33070
|
-
|
|
33071
|
-
|
|
33072
|
-
|
|
33073
|
-
|
|
33074
|
-
|
|
33075
|
-
|
|
33076
|
-
|
|
33077
|
-
|
|
33083
|
+
closeStream();
|
|
33084
|
+
const controller = new AbortController();
|
|
33085
|
+
abortController = controller;
|
|
33086
|
+
const url = `${basePath}/commands/stream${tabId ? `?tabId=${encodeURIComponent(tabId)}` : ""}`;
|
|
33087
|
+
const headers = { Accept: "text/event-stream" };
|
|
33088
|
+
const token = resolveAuthToken(authHeaderRef.current);
|
|
33089
|
+
if (token) headers.Authorization = `Bearer ${token}`;
|
|
33090
|
+
fetch(url, { method: "GET", headers, signal: controller.signal, cache: "no-store" }).then(async (resp) => {
|
|
33091
|
+
if (!resp.ok || !resp.body) {
|
|
33092
|
+
if (abortController === controller) abortController = null;
|
|
33093
|
+
scheduleReconnect();
|
|
33094
|
+
return;
|
|
33078
33095
|
}
|
|
33079
|
-
|
|
33080
|
-
|
|
33081
|
-
|
|
33082
|
-
|
|
33083
|
-
|
|
33084
|
-
|
|
33085
|
-
|
|
33086
|
-
|
|
33087
|
-
|
|
33096
|
+
const reader = resp.body.getReader();
|
|
33097
|
+
const decoder = new TextDecoder();
|
|
33098
|
+
let buffer = "";
|
|
33099
|
+
try {
|
|
33100
|
+
for (; ; ) {
|
|
33101
|
+
const { value, done } = await reader.read();
|
|
33102
|
+
if (done) break;
|
|
33103
|
+
buffer += decoder.decode(value, { stream: true });
|
|
33104
|
+
let sep;
|
|
33105
|
+
while ((sep = buffer.indexOf("\n\n")) !== -1) {
|
|
33106
|
+
const block = buffer.slice(0, sep);
|
|
33107
|
+
buffer = buffer.slice(sep + 2);
|
|
33108
|
+
const data = parseSSEDataBlock(block);
|
|
33109
|
+
if (data == null) continue;
|
|
33110
|
+
try {
|
|
33111
|
+
const parsed = JSON.parse(data);
|
|
33112
|
+
if (parsed.type === "connected") continue;
|
|
33113
|
+
if (parsed.commandId && parsed.action) {
|
|
33114
|
+
processCommandRef.current(parsed);
|
|
33115
|
+
}
|
|
33116
|
+
} catch (e) {
|
|
33117
|
+
console.error("[UIBridge] Failed to parse SSE message:", e);
|
|
33118
|
+
}
|
|
33119
|
+
}
|
|
33120
|
+
}
|
|
33121
|
+
} catch {
|
|
33088
33122
|
}
|
|
33089
|
-
|
|
33123
|
+
if (abortController === controller) abortController = null;
|
|
33124
|
+
if (isMounted && !controller.signal.aborted) scheduleReconnect();
|
|
33125
|
+
}).catch(() => {
|
|
33126
|
+
if (abortController === controller) abortController = null;
|
|
33127
|
+
if (isMounted && !controller.signal.aborted) scheduleReconnect();
|
|
33128
|
+
});
|
|
33090
33129
|
};
|
|
33091
33130
|
const handleVisibility = () => {
|
|
33092
33131
|
if (document.visibilityState === "visible") {
|
|
33093
|
-
if (!
|
|
33132
|
+
if (!abortController) {
|
|
33094
33133
|
if (reconnectTimeoutRef.current) {
|
|
33095
33134
|
clearTimeout(reconnectTimeoutRef.current);
|
|
33096
33135
|
reconnectTimeoutRef.current = null;
|
|
@@ -33103,10 +33142,7 @@ function useCommandRelay(options) {
|
|
|
33103
33142
|
document.addEventListener("visibilitychange", handleVisibility);
|
|
33104
33143
|
forceReconnectRef.current = () => {
|
|
33105
33144
|
if (!isMounted) return;
|
|
33106
|
-
|
|
33107
|
-
eventSourceRef.current.close();
|
|
33108
|
-
eventSourceRef.current = null;
|
|
33109
|
-
}
|
|
33145
|
+
closeStream();
|
|
33110
33146
|
if (reconnectTimeoutRef.current) {
|
|
33111
33147
|
clearTimeout(reconnectTimeoutRef.current);
|
|
33112
33148
|
reconnectTimeoutRef.current = null;
|
|
@@ -33117,10 +33153,7 @@ function useCommandRelay(options) {
|
|
|
33117
33153
|
isMounted = false;
|
|
33118
33154
|
forceReconnectRef.current = () => {
|
|
33119
33155
|
};
|
|
33120
|
-
|
|
33121
|
-
eventSourceRef.current.close();
|
|
33122
|
-
eventSourceRef.current = null;
|
|
33123
|
-
}
|
|
33156
|
+
closeStream();
|
|
33124
33157
|
if (reconnectTimeoutRef.current) {
|
|
33125
33158
|
clearTimeout(reconnectTimeoutRef.current);
|
|
33126
33159
|
reconnectTimeoutRef.current = null;
|