Restore stashed WIP: live streaming plan, forms, CI pipeline, streaming demo
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
@@ -361,6 +361,7 @@ class JSEmitter:
|
||||
"fetch-request": "fetchRequest",
|
||||
"fetch-location": "fetchLocation",
|
||||
"fetch-and-restore": "fetchAndRestore",
|
||||
"fetch-streaming": "fetchStreaming",
|
||||
"fetch-preload": "fetchPreload",
|
||||
"dom-query-by-id": "domQueryById",
|
||||
"dom-matches?": "domMatches",
|
||||
@@ -3123,6 +3124,134 @@ PLATFORM_ORCHESTRATION_JS = """
|
||||
}).catch(function() { location.reload(); });
|
||||
}
|
||||
|
||||
function fetchStreaming(target, url, headers) {
|
||||
// Streaming fetch for multi-stream pages.
|
||||
// First chunk = OOB SX swap (shell with skeletons).
|
||||
// Subsequent chunks = __sxResolve script tags filling suspense slots.
|
||||
var opts = { headers: headers };
|
||||
try {
|
||||
var h = new URL(url, location.href).hostname;
|
||||
if (h !== location.hostname &&
|
||||
(h.indexOf(".rose-ash.com") >= 0 || h.indexOf(".localhost") >= 0)) {
|
||||
opts.credentials = "include";
|
||||
}
|
||||
} catch (e) {}
|
||||
|
||||
fetch(url, opts).then(function(resp) {
|
||||
if (!resp.ok || !resp.body) {
|
||||
// Fallback: non-streaming
|
||||
return resp.text().then(function(text) {
|
||||
text = stripComponentScripts(text);
|
||||
text = extractResponseCss(text);
|
||||
text = text.trim();
|
||||
if (text.charAt(0) === "(") {
|
||||
var dom = sxRender(text);
|
||||
var container = document.createElement("div");
|
||||
container.appendChild(dom);
|
||||
processOobSwaps(container, function(t, oob, s) {
|
||||
swapDomNodes(t, oob, s);
|
||||
sxHydrate(t);
|
||||
processElements(t);
|
||||
});
|
||||
var newMain = container.querySelector("#main-panel");
|
||||
morphChildren(target, newMain || container);
|
||||
postSwap(target);
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
var reader = resp.body.getReader();
|
||||
var decoder = new TextDecoder();
|
||||
var buffer = "";
|
||||
var initialSwapDone = false;
|
||||
// Regex to match __sxResolve script tags
|
||||
var RESOLVE_START = "<script>window.__sxResolve&&window.__sxResolve(";
|
||||
var RESOLVE_END = ")</script>";
|
||||
|
||||
function processResolveScripts() {
|
||||
// Strip and load any extra component defs before resolve scripts
|
||||
buffer = stripSxScripts(buffer);
|
||||
var idx;
|
||||
while ((idx = buffer.indexOf(RESOLVE_START)) >= 0) {
|
||||
var endIdx = buffer.indexOf(RESOLVE_END, idx);
|
||||
if (endIdx < 0) break; // incomplete, wait for more data
|
||||
var argsStr = buffer.substring(idx + RESOLVE_START.length, endIdx);
|
||||
buffer = buffer.substring(endIdx + RESOLVE_END.length);
|
||||
// argsStr is: "stream-id","sx source"
|
||||
var commaIdx = argsStr.indexOf(",");
|
||||
if (commaIdx >= 0) {
|
||||
try {
|
||||
var id = JSON.parse(argsStr.substring(0, commaIdx));
|
||||
var sx = JSON.parse(argsStr.substring(commaIdx + 1));
|
||||
if (typeof Sx !== "undefined" && Sx.resolveSuspense) {
|
||||
Sx.resolveSuspense(id, sx);
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("[sx-ref] resolve parse error:", e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
function pump() {
|
||||
return reader.read().then(function(result) {
|
||||
buffer += decoder.decode(result.value || new Uint8Array(), { stream: !result.done });
|
||||
|
||||
if (!initialSwapDone) {
|
||||
// Look for the first resolve script — everything before it is OOB content
|
||||
var scriptIdx = buffer.indexOf("<script>window.__sxResolve");
|
||||
// If we found a script tag, or the stream is done, process OOB
|
||||
var oobEnd = scriptIdx >= 0 ? scriptIdx : (result.done ? buffer.length : -1);
|
||||
if (oobEnd >= 0) {
|
||||
var oobContent = buffer.substring(0, oobEnd);
|
||||
buffer = buffer.substring(oobEnd);
|
||||
initialSwapDone = true;
|
||||
|
||||
// Process OOB SX content (same as fetchAndRestore)
|
||||
oobContent = stripComponentScripts(oobContent);
|
||||
// Also strip bare <script type="text/sx"> (extra defs from resolve chunks)
|
||||
oobContent = stripSxScripts(oobContent);
|
||||
oobContent = extractResponseCss(oobContent);
|
||||
oobContent = oobContent.trim();
|
||||
if (oobContent.charAt(0) === "(") {
|
||||
try {
|
||||
var dom = sxRender(oobContent);
|
||||
var container = document.createElement("div");
|
||||
container.appendChild(dom);
|
||||
processOobSwaps(container, function(t, oob, s) {
|
||||
swapDomNodes(t, oob, s);
|
||||
sxHydrate(t);
|
||||
processElements(t);
|
||||
});
|
||||
var newMain = container.querySelector("#main-panel");
|
||||
morphChildren(target, newMain || container);
|
||||
postSwap(target);
|
||||
// Dispatch clientRoute so nav links update active state
|
||||
domDispatch(target, "sx:clientRoute",
|
||||
{ pathname: new URL(url, location.href).pathname });
|
||||
} catch (err) {
|
||||
console.error("[sx-ref] streaming OOB swap error:", err);
|
||||
}
|
||||
}
|
||||
// Process any resolve scripts already in buffer
|
||||
processResolveScripts();
|
||||
}
|
||||
} else {
|
||||
// Process resolve scripts as they arrive
|
||||
processResolveScripts();
|
||||
}
|
||||
|
||||
if (!result.done) return pump();
|
||||
});
|
||||
}
|
||||
|
||||
return pump();
|
||||
}).catch(function(err) {
|
||||
console.error("[sx-ref] streaming fetch error:", err);
|
||||
location.reload();
|
||||
});
|
||||
}
|
||||
|
||||
function fetchPreload(url, headers, cache) {
|
||||
fetch(url, { headers: headers }).then(function(resp) {
|
||||
if (!resp.ok) return;
|
||||
@@ -3587,6 +3716,14 @@ PLATFORM_ORCHESTRATION_JS = """
|
||||
function(_, defs) { if (SxObj && SxObj.loadComponents) SxObj.loadComponents(defs); return ""; });
|
||||
}
|
||||
|
||||
function stripSxScripts(text) {
|
||||
// Strip <script type="text/sx">...</script> (without data-components).
|
||||
// These contain extra component defs from streaming resolve chunks.
|
||||
var SxObj = typeof Sx !== "undefined" ? Sx : null;
|
||||
return text.replace(/<script[^>]*type="text\\/sx"[^>]*>([\\s\\S]*?)<\\/script>/gi,
|
||||
function(_, defs) { if (SxObj && SxObj.loadComponents) SxObj.loadComponents(defs); return ""; });
|
||||
}
|
||||
|
||||
function extractResponseCss(text) {
|
||||
if (!_hasDom) return text;
|
||||
var target = document.getElementById("sx-css");
|
||||
|
||||
Reference in New Issue
Block a user