fix(sync): bound acknowledgement stream

req: operations/004
This commit is contained in:
slhx agent
2026-07-13 21:48:00 +02:00
parent 56ace537f3
commit 2bc68c5777
4 changed files with 178 additions and 8 deletions
+17 -1
View File
@@ -8,6 +8,7 @@ const MIGRATION_KEY = "commandSchemaMigration";
const MAX_ATTEMPTS = 3;
const BACKOFF_MS = [25, 50];
const REQUEST_TIMEOUT_MS = 1_000;
const ACKNOWLEDGEMENT_STREAM_BUFFER_LIMIT = 64;
const root = document.querySelector("[data-kanban-sync]");
const TAB_ID = sessionStorage.getItem("hemx-kanban-sync-tab-id") || crypto.randomUUID();
const LEASE_MS = 5000;
@@ -481,6 +482,17 @@ async function synchronize(command) {
source.addEventListener("open", () => {
opens += 1;
root.setAttribute("data-sync-transport-opens", String(opens));
root.setAttribute("data-sync-stream-state", "open");
});
source.addEventListener("heartbeat", () => {
const heartbeats = Number(root.getAttribute("data-sync-heartbeats") || "0") + 1;
root.setAttribute("data-sync-heartbeats", String(heartbeats));
root.setAttribute("data-sync-stream-state", "healthy");
});
source.addEventListener("error", () => {
const reconnects = Number(root.getAttribute("data-sync-reconnects") || "0") + 1;
root.setAttribute("data-sync-reconnects", String(reconnects));
root.setAttribute("data-sync-stream-state", "reconnecting");
});
source.addEventListener("acknowledgement", async (event) => {
const canonical = JSON.parse(event.data);
@@ -631,6 +643,7 @@ async function runLeaseLoop(command) {
async function start() {
if (!root) return;
root.setAttribute("data-sync-request-timeout-ms", String(REQUEST_TIMEOUT_MS));
root.setAttribute("data-sync-stream-buffer-limit", String(ACKNOWLEDGEMENT_STREAM_BUFFER_LIMIT));
const contextResponse = await fetchWithTimeout("/sync/context", { credentials: "same-origin", cache: "no-store" });
if (!contextResponse.ok) throw new Error(`account context failed with ${contextResponse.status}`);
const context = await contextResponse.json();
@@ -701,7 +714,10 @@ window.addEventListener("pagehide", () => {
stopped = true;
clearTimeout(leaseTimer);
clearTimeout(retryTimer);
acknowledgementSource?.close();
if (acknowledgementSource) {
acknowledgementSource.close();
root.setAttribute("data-sync-stream-state", "cancelled");
}
acknowledgementSource = undefined;
for (const controller of activeRequests) {
controller.abort(new DOMException("sync cancelled because page is hidden", "AbortError"));