From b53a7ba449c6d2b3d80bc3e032f393e9c43fb50a Mon Sep 17 00:00:00 2001 From: slhx agent Date: Sun, 10 May 2026 21:05:14 +0200 Subject: [PATCH] Add runtime SSE effect stream support --- slhx-js/runtime/slhx.js | 21 +++++++++++++++++++++ 1 file changed, 21 insertions(+) diff --git a/slhx-js/runtime/slhx.js b/slhx-js/runtime/slhx.js index 5bd2ae4..a9584f2 100644 --- a/slhx-js/runtime/slhx.js +++ b/slhx-js/runtime/slhx.js @@ -8,6 +8,7 @@ const pending = new WeakMap(); const queues = new WeakMap(); const timers = new WeakMap(); + const sseSources = new WeakMap(); const atomStores = new WeakMap(); function roots() { @@ -402,6 +403,25 @@ }); } + function bindSse(root) { + const url = root.getAttribute("data-slhx-sse"); + if (!url || sseSources.has(root) || typeof EventSource === "undefined") return; + const source = new EventSource(new URL(url, location.href).href); + source.addEventListener("slhx", (event) => applySseMessage(root, event)); + source.addEventListener("message", (event) => applySseMessage(root, event)); + source.addEventListener("error", () => emit(root, "slhx:sse-error", url)); + sseSources.set(root, source); + } + + function applySseMessage(root, event) { + try { + const bytes = base64UrlBytes(event.data); + applyBatch(bytes.buffer.slice(bytes.byteOffset, bytes.byteOffset + bytes.byteLength), root); + } catch (error) { + emit(root, "slhx:error", String(error)); + } + } + function duration(value) { if (!value) return 0; const match = String(value).trim().match(/^(\d+)(ms|s)?$/); @@ -523,6 +543,7 @@ roots().forEach((root) => { bootstrapState(root); bindRoot(root); + bindSse(root); }); history.replaceState(history.state || { slhx: true }, "", location.href); }