@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.mjs CHANGED
@@ -25799,7 +25799,7 @@ function exposeProviderOnWindow(internals) {
25799
25799
  if (typeof window === "undefined") return;
25800
25800
  const w = window;
25801
25801
  const root = w.__UI_BRIDGE__ ?? (w.__UI_BRIDGE__ = {});
25802
- root.version = "0.10.0";
25802
+ root.version = "0.11.0";
25803
25803
  root.specs = { getGlobalSpecStore };
25804
25804
  root.registry = internals.registry;
25805
25805
  root.browserCapture = internals.browserCapture;
@@ -37540,11 +37540,16 @@ function transportHeaders(authHeader) {
37540
37540
  }
37541
37541
  return headers;
37542
37542
  }
37543
- function appendAuthQuery(url, authHeader) {
37544
- const token = resolveAuthToken(authHeader);
37545
- if (!token) return url;
37546
- const sep = url.includes("?") ? "&" : "?";
37547
- return `${url}${sep}_auth=${encodeURIComponent(token)}`;
37543
+ function parseSSEDataBlock(block) {
37544
+ const dataLines = [];
37545
+ for (const line of block.split("\n")) {
37546
+ if (line.startsWith(":")) continue;
37547
+ if (!line.startsWith("data:")) continue;
37548
+ let value = line.slice(5);
37549
+ if (value.startsWith(" ")) value = value.slice(1);
37550
+ dataLines.push(value);
37551
+ }
37552
+ return dataLines.length > 0 ? dataLines.join("\n") : null;
37548
37553
  }
37549
37554
  function useCommandRelay(options) {
37550
37555
  const enabled = options?.enabled ?? true;
@@ -37584,7 +37589,6 @@ function useCommandRelay(options) {
37584
37589
  }),
37585
37590
  [uiBridge, context]
37586
37591
  );
37587
- const eventSourceRef = useRef(null);
37588
37592
  const reconnectTimeoutRef = useRef(null);
37589
37593
  const processCommandRef = useRef(() => {
37590
37594
  });
@@ -37660,10 +37664,6 @@ function useCommandRelay(options) {
37660
37664
  processCommandRef.current = processCommand;
37661
37665
  useEffect(() => {
37662
37666
  if (!enabled) {
37663
- if (eventSourceRef.current) {
37664
- eventSourceRef.current.close();
37665
- eventSourceRef.current = null;
37666
- }
37667
37667
  if (reconnectTimeoutRef.current) {
37668
37668
  clearTimeout(reconnectTimeoutRef.current);
37669
37669
  reconnectTimeoutRef.current = null;
@@ -37671,34 +37671,73 @@ function useCommandRelay(options) {
37671
37671
  return;
37672
37672
  }
37673
37673
  let isMounted = true;
37674
+ let abortController = null;
37675
+ const closeStream = () => {
37676
+ if (abortController) {
37677
+ abortController.abort();
37678
+ abortController = null;
37679
+ }
37680
+ };
37681
+ const scheduleReconnect = () => {
37682
+ if (!isMounted) return;
37683
+ if (reconnectTimeoutRef.current) return;
37684
+ reconnectTimeoutRef.current = setTimeout(() => {
37685
+ reconnectTimeoutRef.current = null;
37686
+ if (isMounted) connect();
37687
+ }, SSE_RECONNECT_DELAY_MS);
37688
+ };
37674
37689
  const connect = () => {
37675
37690
  if (!isMounted) return;
37676
- const baseUrl = `${basePath}/commands/stream${tabId ? `?tabId=${encodeURIComponent(tabId)}` : ""}`;
37677
- const es = new EventSource(appendAuthQuery(baseUrl, authHeaderRef.current));
37678
- eventSourceRef.current = es;
37679
- es.onmessage = (event) => {
37680
- try {
37681
- const data = JSON.parse(event.data);
37682
- if (data.type === "connected") return;
37683
- if (data.commandId && data.action) processCommandRef.current(data);
37684
- } catch (e) {
37685
- console.error("[UIBridge] Failed to parse SSE message:", e);
37691
+ closeStream();
37692
+ const controller = new AbortController();
37693
+ abortController = controller;
37694
+ const url = `${basePath}/commands/stream${tabId ? `?tabId=${encodeURIComponent(tabId)}` : ""}`;
37695
+ const headers = { Accept: "text/event-stream" };
37696
+ const token = resolveAuthToken(authHeaderRef.current);
37697
+ if (token) headers.Authorization = `Bearer ${token}`;
37698
+ fetch(url, { method: "GET", headers, signal: controller.signal, cache: "no-store" }).then(async (resp) => {
37699
+ if (!resp.ok || !resp.body) {
37700
+ if (abortController === controller) abortController = null;
37701
+ scheduleReconnect();
37702
+ return;
37686
37703
  }
37687
- };
37688
- es.onerror = () => {
37689
- es.close();
37690
- if (eventSourceRef.current === es) eventSourceRef.current = null;
37691
- if (isMounted) {
37692
- reconnectTimeoutRef.current = setTimeout(() => {
37693
- reconnectTimeoutRef.current = null;
37694
- if (isMounted) connect();
37695
- }, SSE_RECONNECT_DELAY_MS);
37704
+ const reader = resp.body.getReader();
37705
+ const decoder = new TextDecoder();
37706
+ let buffer = "";
37707
+ try {
37708
+ for (; ; ) {
37709
+ const { value, done } = await reader.read();
37710
+ if (done) break;
37711
+ buffer += decoder.decode(value, { stream: true });
37712
+ let sep;
37713
+ while ((sep = buffer.indexOf("\n\n")) !== -1) {
37714
+ const block = buffer.slice(0, sep);
37715
+ buffer = buffer.slice(sep + 2);
37716
+ const data = parseSSEDataBlock(block);
37717
+ if (data == null) continue;
37718
+ try {
37719
+ const parsed = JSON.parse(data);
37720
+ if (parsed.type === "connected") continue;
37721
+ if (parsed.commandId && parsed.action) {
37722
+ processCommandRef.current(parsed);
37723
+ }
37724
+ } catch (e) {
37725
+ console.error("[UIBridge] Failed to parse SSE message:", e);
37726
+ }
37727
+ }
37728
+ }
37729
+ } catch {
37696
37730
  }
37697
- };
37731
+ if (abortController === controller) abortController = null;
37732
+ if (isMounted && !controller.signal.aborted) scheduleReconnect();
37733
+ }).catch(() => {
37734
+ if (abortController === controller) abortController = null;
37735
+ if (isMounted && !controller.signal.aborted) scheduleReconnect();
37736
+ });
37698
37737
  };
37699
37738
  const handleVisibility = () => {
37700
37739
  if (document.visibilityState === "visible") {
37701
- if (!eventSourceRef.current) {
37740
+ if (!abortController) {
37702
37741
  if (reconnectTimeoutRef.current) {
37703
37742
  clearTimeout(reconnectTimeoutRef.current);
37704
37743
  reconnectTimeoutRef.current = null;
@@ -37711,10 +37750,7 @@ function useCommandRelay(options) {
37711
37750
  document.addEventListener("visibilitychange", handleVisibility);
37712
37751
  forceReconnectRef.current = () => {
37713
37752
  if (!isMounted) return;
37714
- if (eventSourceRef.current) {
37715
- eventSourceRef.current.close();
37716
- eventSourceRef.current = null;
37717
- }
37753
+ closeStream();
37718
37754
  if (reconnectTimeoutRef.current) {
37719
37755
  clearTimeout(reconnectTimeoutRef.current);
37720
37756
  reconnectTimeoutRef.current = null;
@@ -37725,10 +37761,7 @@ function useCommandRelay(options) {
37725
37761
  isMounted = false;
37726
37762
  forceReconnectRef.current = () => {
37727
37763
  };
37728
- if (eventSourceRef.current) {
37729
- eventSourceRef.current.close();
37730
- eventSourceRef.current = null;
37731
- }
37764
+ closeStream();
37732
37765
  if (reconnectTimeoutRef.current) {
37733
37766
  clearTimeout(reconnectTimeoutRef.current);
37734
37767
  reconnectTimeoutRef.current = null;