@farm.js/core 0.1.48 → 0.1.49
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/{agent-mcp-DAjB8Eyu.d.ts → agent-mcp-Bb8hnnqd.d.ts} +1 -1
- package/dist/{agent-mcp-CXN3g0eX.d.mts → agent-mcp-ir3v4fHf.d.mts} +1 -1
- package/dist/agent-runtime.d.mts +1 -1
- package/dist/agent-runtime.d.ts +1 -1
- package/dist/api/route-manager.d.mts +3 -3
- package/dist/api/route-manager.d.ts +3 -3
- package/dist/api/runtime.d.mts +2 -2
- package/dist/api/runtime.d.ts +2 -2
- package/dist/api.d.mts +3 -3
- package/dist/api.d.ts +3 -3
- package/dist/{app-DytgRPjJ.d.mts → app-DLzz02du.d.mts} +4 -4
- package/dist/{app-manifest-DPgW5ke5.d.mts → app-manifest-Crn8Qd3U.d.mts} +1 -1
- package/dist/{app-manifest-BUu7YGAx.d.ts → app-manifest-DNeTKMRL.d.ts} +1 -1
- package/dist/app-markdown.d.mts +2 -2
- package/dist/app-markdown.d.ts +2 -2
- package/dist/{app-VK9sHDn5.d.ts → app-y649szYm.d.ts} +4 -4
- package/dist/{chunk-Q2ZKQ6GD.cjs → chunk-4HM5B5RR.cjs} +62 -62
- package/dist/{chunk-MZZA6ZQV.mjs → chunk-7PZVXMWG.mjs} +4 -4
- package/dist/{chunk-HRLYO2XJ.mjs → chunk-ACI52TCN.mjs} +1 -1
- package/dist/{chunk-VDLF2DJA.cjs → chunk-AIHD6VME.cjs} +276 -91
- package/dist/{chunk-YSPMY4B7.cjs → chunk-C5S457CK.cjs} +170 -108
- package/dist/{chunk-NRFUVDTH.mjs → chunk-DGVVVYKD.mjs} +1 -1
- package/dist/{chunk-WUEDTW6E.cjs → chunk-FR4HJDI6.cjs} +21 -0
- package/dist/{chunk-HUL4LTC6.cjs → chunk-HTLCLOXQ.cjs} +63 -20
- package/dist/{chunk-N7SPZJLY.mjs → chunk-IVHPRF4W.mjs} +5 -5
- package/dist/{chunk-6M3HXUBQ.mjs → chunk-J27BZO67.mjs} +21 -0
- package/dist/{chunk-JLS6ZLFM.cjs → chunk-J5VCA45K.cjs} +1 -1
- package/dist/{chunk-ZGS3ZDF7.cjs → chunk-NPPT4RWF.cjs} +2 -2
- package/dist/{chunk-TFA6BUAE.cjs → chunk-OY5XDG24.cjs} +4 -4
- package/dist/{chunk-N6SIQJEJ.mjs → chunk-RYDDUCNJ.mjs} +54 -11
- package/dist/{chunk-C2R5RVLA.cjs → chunk-VCKGOXFI.cjs} +73 -1
- package/dist/{chunk-TQWGOQ7O.mjs → chunk-Z3BXMMFF.mjs} +80 -18
- package/dist/{chunk-NE33FRQ5.mjs → chunk-Z4LTSH7D.mjs} +207 -22
- package/dist/{chunk-NIPKC4DA.mjs → chunk-ZIBIE7AH.mjs} +72 -0
- package/dist/{client-plugins-BlEzV5FO.d.mts → client-plugins-BNBeyLMi.d.mts} +4 -2
- package/dist/{client-plugins-DqjCsKMN.d.ts → client-plugins-DFDQUI9Q.d.ts} +4 -2
- package/dist/client-plugins.d.mts +1 -1
- package/dist/client-plugins.d.ts +1 -1
- package/dist/client.d.mts +5 -5
- package/dist/client.d.ts +5 -5
- package/dist/config.d.mts +1 -1
- package/dist/config.d.ts +1 -1
- package/dist/desktop.d.mts +2 -2
- package/dist/desktop.d.ts +2 -2
- package/dist/{dev-guard-4NQ3LFO5.cjs → dev-guard-PLHL6OTJ.cjs} +3 -2
- package/dist/{dev-guard-MPZUJFIV.mjs → dev-guard-YO6KPIOQ.mjs} +2 -1
- package/dist/docs.d.mts +2 -2
- package/dist/docs.d.ts +2 -2
- package/dist/{index-CMigAr9f.d.mts → index-CeJ9_s9p.d.mts} +1 -1
- package/dist/{index-n3hjOvVC.d.ts → index-DBdiTkNc.d.ts} +1 -1
- package/dist/index.cjs +7 -7
- package/dist/index.d.mts +15 -15
- package/dist/index.d.ts +15 -15
- package/dist/index.mjs +6 -6
- package/dist/integrations.d.mts +1 -1
- package/dist/integrations.d.ts +1 -1
- package/dist/internal/app-markdown-runtime.d.mts +2 -2
- package/dist/internal/app-markdown-runtime.d.ts +2 -2
- package/dist/internal/build-runtime.cjs +9 -8
- package/dist/internal/build-runtime.d.mts +3 -3
- package/dist/internal/build-runtime.d.ts +3 -3
- package/dist/internal/build-runtime.mjs +8 -7
- package/dist/internal/client-error-overlay.cjs +4 -4
- package/dist/internal/client-error-overlay.mjs +1 -1
- package/dist/internal/compression-runtime.d.mts +1 -1
- package/dist/internal/compression-runtime.d.ts +1 -1
- package/dist/internal/config-runtime.d.mts +2 -2
- package/dist/internal/config-runtime.d.ts +2 -2
- package/dist/internal/desktop-guard.cjs +2 -2
- package/dist/internal/desktop-guard.d.mts +7 -0
- package/dist/internal/desktop-guard.d.ts +7 -0
- package/dist/internal/desktop-guard.mjs +1 -1
- package/dist/internal/infra-analytics-runtime.cjs +5 -5
- package/dist/internal/infra-analytics-runtime.mjs +3 -3
- package/dist/internal/islands-runtime.cjs +4 -1
- package/dist/internal/islands-runtime.d.mts +9 -12
- package/dist/internal/islands-runtime.d.ts +9 -12
- package/dist/internal/islands-runtime.mjs +5 -2
- package/dist/internal/product-telemetry-runtime.cjs +2 -2
- package/dist/internal/product-telemetry-runtime.mjs +1 -1
- package/dist/internal/production-runtime.cjs +4 -4
- package/dist/internal/production-runtime.d.mts +10 -10
- package/dist/internal/production-runtime.d.ts +10 -10
- package/dist/internal/production-runtime.mjs +3 -3
- package/dist/markdown.d.mts +1 -1
- package/dist/markdown.d.ts +1 -1
- package/dist/middleware.d.mts +3 -3
- package/dist/middleware.d.ts +3 -3
- package/dist/navigation.d.mts +4 -4
- package/dist/navigation.d.ts +4 -4
- package/dist/plugin.cjs +6 -6
- package/dist/plugin.d.mts +3 -3
- package/dist/plugin.d.ts +3 -3
- package/dist/plugin.mjs +5 -5
- package/dist/{production-runtime-YVDGYWBG.d.ts → production-runtime-B3qFFtKF.d.ts} +1 -1
- package/dist/{production-runtime-bWZJMcQb.d.mts → production-runtime-DcD-GRAv.d.mts} +1 -1
- package/dist/{programmatic-routes-client-BFKOTEXN.d.mts → programmatic-routes-client-DzbYGiZQ.d.mts} +2 -2
- package/dist/{programmatic-routes-client-DviwGeAv.d.ts → programmatic-routes-client-fD9f4XqU.d.ts} +2 -2
- package/dist/{render-plan-DqWXo3-B.d.ts → render-plan-B6cfssrr.d.ts} +1 -1
- package/dist/{render-plan-CS8l0zaK.d.mts → render-plan-DqJ51ufr.d.mts} +1 -1
- package/dist/renderer-client.d.mts +3 -3
- package/dist/renderer-client.d.ts +3 -3
- package/dist/{request-D4XqmnJF.d.mts → request-UuAxHn8h.d.mts} +1 -1
- package/dist/{request-CBrHpaCC.d.ts → request-jhvUMXRx.d.ts} +1 -1
- package/dist/request.d.mts +2 -2
- package/dist/request.d.ts +2 -2
- package/dist/{route-rendering-3pRPtyO2.d.mts → route-rendering-Cou00t1X.d.mts} +1 -1
- package/dist/{route-rendering-Bltnu8a3.d.ts → route-rendering-Sn50cYZf.d.ts} +1 -1
- package/dist/{router-BFSmKGtI.d.mts → router-0X9DCTcm.d.mts} +1 -1
- package/dist/{router-DDT7bkg7.d.ts → router-xwLvi48p.d.ts} +1 -1
- package/dist/routes.d.mts +1 -1
- package/dist/routes.d.ts +1 -1
- package/dist/{rsc-pipeline-ULFIB3UT.mjs → rsc-pipeline-F6ZPH5MU.mjs} +2 -1
- package/dist/{rsc-pipeline-4KOITP64.cjs → rsc-pipeline-VKHMABDJ.cjs} +3 -2
- package/dist/{runtime-Cpylaypt.d.mts → runtime-CMrsyChI.d.mts} +1 -1
- package/dist/{runtime-C6yG64je.d.ts → runtime-tkSXZiqO.d.ts} +1 -1
- package/dist/server-plugins.d.mts +2 -2
- package/dist/server-plugins.d.ts +2 -2
- package/dist/server.cjs +18 -18
- package/dist/server.d.mts +9 -9
- package/dist/server.d.ts +9 -9
- package/dist/server.mjs +7 -7
- package/dist/{spa-router-CKuS81mi.d.ts → spa-router-B06APnGK.d.ts} +2 -2
- package/dist/{spa-router-CkTPnA4L.d.mts → spa-router-B48EjsS1.d.mts} +2 -2
- package/dist/testing.d.mts +1 -1
- package/dist/testing.d.ts +1 -1
- package/dist/{utils-C0ORLYXc.d.ts → utils-C5fyK2tE.d.ts} +1 -1
- package/dist/{utils-IA9wVkef.d.mts → utils-hyAwmcN6.d.mts} +1 -1
- package/dist/version.cjs +2 -2
- package/dist/version.mjs +1 -1
- package/dist/vite.cjs +6 -6
- package/dist/vite.d.mts +4 -4
- package/dist/vite.d.ts +4 -4
- package/dist/vite.mjs +5 -5
- package/package.json +2 -2
|
@@ -12,6 +12,7 @@ import {
|
|
|
12
12
|
createDefaultErrorMarkup,
|
|
13
13
|
createFarmMetadataRouteResponse,
|
|
14
14
|
createFarmRouteRenderPlan,
|
|
15
|
+
createFarmStreamedDocumentTransform,
|
|
15
16
|
createFarmThemeDocumentParts,
|
|
16
17
|
createLateNotFoundRecovery,
|
|
17
18
|
createLateRedirectRecovery,
|
|
@@ -24,14 +25,15 @@ import {
|
|
|
24
25
|
getRouteDirectoryDepth,
|
|
25
26
|
getRouteGroupScope,
|
|
26
27
|
isInRouteGroupScope,
|
|
28
|
+
openFarmStreamedDocument,
|
|
27
29
|
removeFarmDocumentTitles,
|
|
28
30
|
renderFarmLocaleAlternateLinks,
|
|
29
31
|
resolveDefaultErrorStatus,
|
|
30
32
|
routeBoundaryCoversDirectory
|
|
31
|
-
} from "./chunk-
|
|
33
|
+
} from "./chunk-ZIBIE7AH.mjs";
|
|
32
34
|
import {
|
|
33
35
|
listFarmMCPLlmsTxtLinks
|
|
34
|
-
} from "./chunk-
|
|
36
|
+
} from "./chunk-DGVVVYKD.mjs";
|
|
35
37
|
import {
|
|
36
38
|
FARM_DEFAULT_MCP_PATH,
|
|
37
39
|
collectFarmDefaultMCPPages,
|
|
@@ -248,7 +250,7 @@ import {
|
|
|
248
250
|
} from "./chunk-LM2YOGDK.mjs";
|
|
249
251
|
import {
|
|
250
252
|
FARM_VERSION
|
|
251
|
-
} from "./chunk-
|
|
253
|
+
} from "./chunk-ACI52TCN.mjs";
|
|
252
254
|
import {
|
|
253
255
|
resolveFarmTrailingSlashRedirect,
|
|
254
256
|
setFarmTrailingSlashPreference
|
|
@@ -4565,7 +4567,7 @@ function rendererRenderOptions() {
|
|
|
4565
4567
|
}
|
|
4566
4568
|
var FARM_DEV_EARLY_CLIENT_SCRIPT = `<script type="module" async src="/@farm/client.js"></script>`;
|
|
4567
4569
|
function createDocumentFooter(options) {
|
|
4568
|
-
return
|
|
4570
|
+
return `${options.closeRoot === false ? "" : "</div>"}
|
|
4569
4571
|
${options.suspenseRevealFallback}
|
|
4570
4572
|
${options.refreshPPR ? createPPRRefreshScript() : ""}
|
|
4571
4573
|
${options.deferredHydrationScript || ""}
|
|
@@ -6767,7 +6769,8 @@ window.__FARM_I18N__ = ${getFarmI18nClientSnapshot() ? serializeInlineValue(getF
|
|
|
6767
6769
|
async renderWithSSR(element, req, res, clearMiddlewareData, options = {}) {
|
|
6768
6770
|
const renderToPipeableStream = this.rendererRuntime.renderToPipeableStream;
|
|
6769
6771
|
const fullDocumentRouteKey = options.observabilityRoute || req.__FARM_ROUTE__ || req.url || "/";
|
|
6770
|
-
|
|
6772
|
+
const streamDocument = fullDocumentRoutes.has(fullDocumentRouteKey) || await this.rootLayoutRendersDocument();
|
|
6773
|
+
if (!renderToPipeableStream || _isFarmMarkdownPageCheck(req.headers?.[FARM_MARKDOWN_PAGE_CHECK_HEADER]) || streamDocument && (options.captureStaticShell || !isReactRenderer(this.config.renderer))) {
|
|
6771
6774
|
return this.renderBufferedSSR(element, req, res, clearMiddlewareData, options);
|
|
6772
6775
|
}
|
|
6773
6776
|
return new Promise((resolve3, reject) => {
|
|
@@ -6879,6 +6882,7 @@ window.__FARM_I18N__ = ${getFarmI18nClientSnapshot() ? serializeInlineValue(getF
|
|
|
6879
6882
|
title,
|
|
6880
6883
|
tags: metaTags,
|
|
6881
6884
|
hasFavicon,
|
|
6885
|
+
hasExplicitTitle,
|
|
6882
6886
|
viewport: viewportTags
|
|
6883
6887
|
} = renderMetadataHead(req.__FARM_METADATA__, {
|
|
6884
6888
|
pathname: getFarmMetadataPathname(req),
|
|
@@ -6944,25 +6948,70 @@ window.__FARM_I18N__ = ${getFarmI18nClientSnapshot() ? serializeInlineValue(getF
|
|
|
6944
6948
|
<body class="">${earlyClient ? `
|
|
6945
6949
|
${FARM_DEV_EARLY_CLIENT_SCRIPT}` : ""}
|
|
6946
6950
|
<div id="root">`;
|
|
6947
|
-
const securedShell = stampDocumentHTML(shell);
|
|
6948
|
-
|
|
6949
|
-
|
|
6951
|
+
const securedShell = streamDocument ? "" : stampDocumentHTML(shell);
|
|
6952
|
+
if (securedShell) {
|
|
6953
|
+
htmlParts.push(securedShell);
|
|
6954
|
+
staticShellParts?.push(securedShell);
|
|
6955
|
+
}
|
|
6956
|
+
const withEarlyClient = (document) => earlyClient ? {
|
|
6957
|
+
...document,
|
|
6958
|
+
html: document.html.replace(
|
|
6959
|
+
/<body\b[^>]*>/i,
|
|
6960
|
+
(open) => `${open}
|
|
6961
|
+
${stampDocumentHTML(FARM_DEV_EARLY_CLIENT_SCRIPT)}`
|
|
6962
|
+
)
|
|
6963
|
+
} : document;
|
|
6964
|
+
const documentTransform = streamDocument ? createFarmStreamedDocumentTransform(
|
|
6965
|
+
(opening) => withEarlyClient(
|
|
6966
|
+
openFarmStreamedDocument(
|
|
6967
|
+
hasExplicitTitle ? removeFarmDocumentTitles(opening) : opening,
|
|
6968
|
+
{
|
|
6969
|
+
fallbackIcon: !hasFavicon,
|
|
6970
|
+
viewport: viewportTags,
|
|
6971
|
+
htmlAttributes: `${i18nSnapshot ? ` lang="${escapeHtmlAttribute2(i18nSnapshot.locale)}" dir="${escapeHtmlAttribute2(i18nSnapshot.direction)}"` : ""}${themeDocument.attributes}`,
|
|
6972
|
+
replaceHtmlAttributes: [
|
|
6973
|
+
...i18nSnapshot ? ["lang", "dir"] : [],
|
|
6974
|
+
...themeDocument.attributes ? ["data-theme"] : []
|
|
6975
|
+
],
|
|
6976
|
+
headAssets: stampDocumentHTML(
|
|
6977
|
+
[
|
|
6978
|
+
themeDocument.head,
|
|
6979
|
+
`<meta name="farm-deployment-id" content="${escapeHtmlAttribute2(deploymentId)}">`,
|
|
6980
|
+
hasExplicitTitle ? `<title>${title}</title>` : "",
|
|
6981
|
+
metaTags,
|
|
6982
|
+
i18nAlternateTags,
|
|
6983
|
+
fontHead,
|
|
6984
|
+
globalStylesheetLink,
|
|
6985
|
+
...devStyleLinks,
|
|
6986
|
+
`<script type="module" src="/@vite/client"></script>`,
|
|
6987
|
+
propsScript,
|
|
6988
|
+
hydrationClickQueueScript
|
|
6989
|
+
].filter(Boolean).join("\n ")
|
|
6990
|
+
),
|
|
6991
|
+
// The footer follows the streamed body.
|
|
6992
|
+
bodyFooter: ""
|
|
6993
|
+
}
|
|
6994
|
+
)
|
|
6995
|
+
),
|
|
6996
|
+
stampDocumentHTML(shell)
|
|
6997
|
+
) : void 0;
|
|
6950
6998
|
let firstChunk = true;
|
|
6951
6999
|
let checkedFullDocument = false;
|
|
6952
7000
|
let documentPrefix = "";
|
|
6953
7001
|
const cspNonceRewriter = cspNonce ? createFarmCspNonceRewriter(cspNonce) : void 0;
|
|
6954
7002
|
const cspStreamDecoder = cspNonceRewriter ? new TextDecoder() : void 0;
|
|
6955
|
-
const recordRenderedChunk = (chunkText) => {
|
|
6956
|
-
if (!
|
|
6957
|
-
|
|
6958
|
-
documentPrefix += chunkText;
|
|
7003
|
+
const recordRenderedChunk = (chunkText, renderedText = chunkText) => {
|
|
7004
|
+
if (!checkedFullDocument && renderedText) {
|
|
7005
|
+
documentPrefix += renderedText;
|
|
6959
7006
|
const prefix = classifyFarmDocumentPrefix(documentPrefix);
|
|
6960
7007
|
if (prefix !== "pending") {
|
|
6961
7008
|
checkedFullDocument = true;
|
|
6962
7009
|
documentPrefix = "";
|
|
6963
7010
|
if (prefix === "document") fullDocumentRoutes.add(fullDocumentRouteKey);
|
|
7011
|
+
else fullDocumentRoutes.delete(fullDocumentRouteKey);
|
|
6964
7012
|
}
|
|
6965
7013
|
}
|
|
7014
|
+
if (!chunkText) return;
|
|
6966
7015
|
htmlParts.push(chunkText);
|
|
6967
7016
|
if (staticShellParts && findStaticShellBoundary && !staticShellClosed) {
|
|
6968
7017
|
const dynamicIndex = findStaticShellBoundary(chunkText);
|
|
@@ -6988,12 +7037,16 @@ window.__FARM_I18N__ = ${getFarmI18nClientSnapshot() ? serializeInlineValue(getF
|
|
|
6988
7037
|
}
|
|
6989
7038
|
const chunkText = Buffer.isBuffer(chunk) ? cspStreamDecoder ? cspStreamDecoder.decode(chunk, { stream: true }) : chunk.toString() : String(chunk);
|
|
6990
7039
|
const securedChunkText = cspNonceRewriter ? cspNonceRewriter.write(chunkText) : chunkText;
|
|
6991
|
-
|
|
7040
|
+
const outputText = documentTransform ? documentTransform.write(securedChunkText) : securedChunkText;
|
|
7041
|
+
recordRenderedChunk(outputText, securedChunkText);
|
|
6992
7042
|
const onWrite = () => {
|
|
6993
7043
|
if (typeof res.flush === "function") res.flush();
|
|
6994
7044
|
callback();
|
|
6995
7045
|
};
|
|
6996
|
-
if (
|
|
7046
|
+
if (documentTransform) {
|
|
7047
|
+
if (outputText) res.write(outputText, onWrite);
|
|
7048
|
+
else callback();
|
|
7049
|
+
} else if (cspNonceRewriter) res.write(securedChunkText, onWrite);
|
|
6997
7050
|
else res.write(chunk, encoding, onWrite);
|
|
6998
7051
|
},
|
|
6999
7052
|
final: (callback) => {
|
|
@@ -7002,7 +7055,8 @@ window.__FARM_I18N__ = ${getFarmI18nClientSnapshot() ? serializeInlineValue(getF
|
|
|
7002
7055
|
lateNavigationError = null;
|
|
7003
7056
|
createLateNavigationRecovery(navigationError).then(
|
|
7004
7057
|
(recovery) => {
|
|
7005
|
-
const
|
|
7058
|
+
const filtered = filterDocumentHTML(recovery);
|
|
7059
|
+
const secured = documentTransform ? documentTransform.write(filtered) : filtered;
|
|
7006
7060
|
htmlParts.push(secured);
|
|
7007
7061
|
res.write(secured);
|
|
7008
7062
|
finishStream(callback);
|
|
@@ -7019,16 +7073,24 @@ window.__FARM_I18N__ = ${getFarmI18nClientSnapshot() ? serializeInlineValue(getF
|
|
|
7019
7073
|
});
|
|
7020
7074
|
const finishStream = (callback) => {
|
|
7021
7075
|
if (cspNonceRewriter) {
|
|
7022
|
-
const
|
|
7023
|
-
|
|
7076
|
+
const rewritten = cspNonceRewriter.write(cspStreamDecoder.decode(), true);
|
|
7077
|
+
const tail = documentTransform ? documentTransform.write(rewritten) : rewritten;
|
|
7078
|
+
recordRenderedChunk(tail, rewritten);
|
|
7024
7079
|
if (tail) res.write(tail);
|
|
7025
7080
|
}
|
|
7081
|
+
const documentEnd = documentTransform?.end();
|
|
7082
|
+
if (documentEnd?.text) {
|
|
7083
|
+
recordRenderedChunk(documentEnd.text, "");
|
|
7084
|
+
res.write(documentEnd.text);
|
|
7085
|
+
}
|
|
7026
7086
|
const suspenseRevealFallback = `<script>(function(){function moveFragment(srcId,placeholderId){var src=document.getElementById(srcId),ph=document.getElementById(placeholderId);if(!src||!ph||!ph.parentNode)return false;while(src.firstChild)ph.parentNode.insertBefore(src.firstChild,ph);ph.parentNode.removeChild(ph);if(src.parentNode)src.parentNode.removeChild(src);return true}function revealBoundary(boundaryId,sectionId){var boundary=document.getElementById(boundaryId),section=document.getElementById(sectionId);if(!boundary||!section||!boundary.parentNode)return false;var start=boundary.previousSibling;if(!start||start.nodeType!==8)return false;var parent=boundary.parentNode;var node=boundary;var depth=0;while(node){if(node.nodeType===8){var data=node.data;if(data==="/$"||data==="/&"){if(depth===0)break;depth--;}else if(data==="$"||data==="$?"||data==="$~"||data==="$!"||data==="&"){depth++;}}var next=node.nextSibling;parent.removeChild(node);node=next;}while(section.firstChild)parent.insertBefore(section.firstChild,node);if(section.parentNode)section.parentNode.removeChild(section);start.data="$";return true}var tries=0;var timer=setInterval(function(){var changed=false;document.querySelectorAll('div[id^="S:"]').forEach(function(section){var suffix=section.id.slice(2);changed=moveFragment('S:'+suffix,'P:'+suffix)||changed;});document.querySelectorAll('template[id^="B:"]').forEach(function(boundary){var suffix=boundary.id.slice(2);changed=revealBoundary('B:'+suffix,'S:'+suffix)||changed;});tries++;if(tries>80||(!document.querySelector('template[id^="B:"]')&&!document.querySelector('template[id^="P:"]'))){clearInterval(timer);}},50);})();</script>`;
|
|
7027
7087
|
const footer = stampDocumentHTML(
|
|
7028
7088
|
createDocumentFooter({
|
|
7029
7089
|
suspenseRevealFallback,
|
|
7030
7090
|
deferredHydrationScript: createDeferredHydrationScript(deferredProps.records),
|
|
7031
|
-
earlyClient
|
|
7091
|
+
earlyClient,
|
|
7092
|
+
// A full document: Farm's #root closes only where Farm opened it.
|
|
7093
|
+
...documentEnd?.document ? { closeRoot: documentEnd.rootOpened } : {}
|
|
7032
7094
|
})
|
|
7033
7095
|
);
|
|
7034
7096
|
htmlParts.push(footer);
|
|
@@ -37,7 +37,7 @@ import {
|
|
|
37
37
|
} from "./chunk-WB6XES4V.mjs";
|
|
38
38
|
import {
|
|
39
39
|
FARM_PRERENDER_ENV
|
|
40
|
-
} from "./chunk-
|
|
40
|
+
} from "./chunk-RYDDUCNJ.mjs";
|
|
41
41
|
import {
|
|
42
42
|
resolveFarmInstrumentationFile
|
|
43
43
|
} from "./chunk-N35YPV4N.mjs";
|
|
@@ -84,7 +84,7 @@ import {
|
|
|
84
84
|
resolveFarmIsolatedClientHydrationMode,
|
|
85
85
|
resolveFarmNotFoundComponentPath,
|
|
86
86
|
usesFarmServerTree
|
|
87
|
-
} from "./chunk-
|
|
87
|
+
} from "./chunk-Z3BXMMFF.mjs";
|
|
88
88
|
import {
|
|
89
89
|
loadTailwindVitePlugin
|
|
90
90
|
} from "./chunk-XLLVWLWO.mjs";
|
|
@@ -108,7 +108,7 @@ import {
|
|
|
108
108
|
layoutsCanCoverSamePage,
|
|
109
109
|
routeBoundaryCoversDirectory,
|
|
110
110
|
stripRouteGroups
|
|
111
|
-
} from "./chunk-
|
|
111
|
+
} from "./chunk-ZIBIE7AH.mjs";
|
|
112
112
|
import {
|
|
113
113
|
farmPluginMayAffectRuntimePath
|
|
114
114
|
} from "./chunk-UUMKLGUU.mjs";
|
|
@@ -119,7 +119,7 @@ import {
|
|
|
119
119
|
listFarmBuiltInRoutePaths,
|
|
120
120
|
resolveFarmApiCatalogInput,
|
|
121
121
|
resolveFarmAppManifestInput
|
|
122
|
-
} from "./chunk-
|
|
122
|
+
} from "./chunk-DGVVVYKD.mjs";
|
|
123
123
|
import {
|
|
124
124
|
FARM_DEFAULT_MCP_PATH,
|
|
125
125
|
createFarmDefaultMCPServer,
|
|
@@ -231,7 +231,7 @@ import {
|
|
|
231
231
|
} from "./chunk-LM2YOGDK.mjs";
|
|
232
232
|
import {
|
|
233
233
|
FARM_VERSION
|
|
234
|
-
} from "./chunk-
|
|
234
|
+
} from "./chunk-ACI52TCN.mjs";
|
|
235
235
|
import {
|
|
236
236
|
getFarmPresetRuntime
|
|
237
237
|
} from "./chunk-UAQD67HM.mjs";
|
|
@@ -2510,7 +2510,7 @@ function toBakedMarkdownSourcePath(root, filePath) {
|
|
|
2510
2510
|
}
|
|
2511
2511
|
return relative.split(path10.sep).join("/");
|
|
2512
2512
|
}
|
|
2513
|
-
function generateUniversalRouterStateRuntime() {
|
|
2513
|
+
function generateUniversalRouterStateRuntime(streamNavigations = false) {
|
|
2514
2514
|
return `
|
|
2515
2515
|
const FARM_PAGE_STATE_KEY = "__farmPageState";
|
|
2516
2516
|
const FARM_HISTORY_INDEX_KEY = "__farmHistoryIndex";
|
|
@@ -2578,6 +2578,46 @@ async function fetchFarmNavigationDocument(url, headers, recover = true, signal)
|
|
|
2578
2578
|
return response;
|
|
2579
2579
|
}
|
|
2580
2580
|
|
|
2581
|
+
${streamNavigations ? `const FARM_FLUSH_MARKER = "<!--farm-flush-->";
|
|
2582
|
+
|
|
2583
|
+
// Reads a streamed navigation (F-Stream) up to the end of React's shell. \`next\` then resolves
|
|
2584
|
+
// with each later flush as it ends, and null at the end; a document without a marker is all shell.
|
|
2585
|
+
async function readFarmNavigationStream(body) {
|
|
2586
|
+
const reader = body.getReader();
|
|
2587
|
+
const decoder = new TextDecoder();
|
|
2588
|
+
let text = "";
|
|
2589
|
+
let done = false;
|
|
2590
|
+
const next = async function() {
|
|
2591
|
+
for (;;) {
|
|
2592
|
+
const at = text.indexOf(FARM_FLUSH_MARKER);
|
|
2593
|
+
if (at !== -1) {
|
|
2594
|
+
const flush = text.slice(0, at);
|
|
2595
|
+
text = text.slice(at + FARM_FLUSH_MARKER.length);
|
|
2596
|
+
return flush;
|
|
2597
|
+
}
|
|
2598
|
+
if (done) {
|
|
2599
|
+
const flush = text;
|
|
2600
|
+
text = "";
|
|
2601
|
+
return flush ? flush : null;
|
|
2602
|
+
}
|
|
2603
|
+
const result = await reader.read();
|
|
2604
|
+
if (result.done) {
|
|
2605
|
+
done = true;
|
|
2606
|
+
text += decoder.decode();
|
|
2607
|
+
} else {
|
|
2608
|
+
text += decoder.decode(result.value, { stream: true });
|
|
2609
|
+
}
|
|
2610
|
+
}
|
|
2611
|
+
};
|
|
2612
|
+
const shell = await next();
|
|
2613
|
+
if (done && !text) return { shell: shell || "" };
|
|
2614
|
+
return {
|
|
2615
|
+
shell: shell,
|
|
2616
|
+
next: next,
|
|
2617
|
+
cancel: function() { reader.cancel().catch(function() {}); },
|
|
2618
|
+
};
|
|
2619
|
+
}` : ""}
|
|
2620
|
+
|
|
2581
2621
|
function clearFarmPrefetchCacheOnDeploymentMismatch(router, error) {
|
|
2582
2622
|
if (error?.name === "FarmDeploymentMismatchError") {
|
|
2583
2623
|
router.clearCache();
|
|
@@ -2781,9 +2821,35 @@ function interpolateRedirectDestination(destination, params) {
|
|
|
2781
2821
|
}
|
|
2782
2822
|
`.trim();
|
|
2783
2823
|
}
|
|
2784
|
-
function generateUniversalRouterStateProperties() {
|
|
2824
|
+
function generateUniversalRouterStateProperties(streamNavigations = false) {
|
|
2785
2825
|
return `
|
|
2786
|
-
|
|
2826
|
+
${streamNavigations ? ` // A streamed navigation swaps in the page's shell; its Suspense content follows flush by flush.
|
|
2827
|
+
navigationStream: null,
|
|
2828
|
+
streamingNavigation: null,
|
|
2829
|
+
takeNavigationStream: function(html) {
|
|
2830
|
+
const stream = this.navigationStream;
|
|
2831
|
+
this.navigationStream = null;
|
|
2832
|
+
if (!stream) return;
|
|
2833
|
+
if (stream.html !== html) {
|
|
2834
|
+
stream.cancel();
|
|
2835
|
+
return;
|
|
2836
|
+
}
|
|
2837
|
+
this.streamingNavigation = stream;
|
|
2838
|
+
void streamFarmNavigation(
|
|
2839
|
+
stream,
|
|
2840
|
+
document.getElementById("root") || document.body,
|
|
2841
|
+
() => this.streamingNavigation === stream,
|
|
2842
|
+
);
|
|
2843
|
+
},
|
|
2844
|
+
stopNavigationStream: function() {
|
|
2845
|
+
this.navigationStream?.cancel();
|
|
2846
|
+
this.streamingNavigation?.cancel();
|
|
2847
|
+
this.navigationStream = null;
|
|
2848
|
+
this.streamingNavigation = null;
|
|
2849
|
+
},
|
|
2850
|
+
` : ` takeNavigationStream: function() {},
|
|
2851
|
+
stopNavigationStream: function() {},
|
|
2852
|
+
`} blockers: new Set(),
|
|
2787
2853
|
unloadBlockers: new Map(),
|
|
2788
2854
|
navigationListeners: new Set(),
|
|
2789
2855
|
navigationState: IDLE_NAVIGATION_STATE,
|
|
@@ -3077,6 +3143,11 @@ function generateUniversalRouterStateProperties() {
|
|
|
3077
3143
|
// Only speculative requests have shared ownership. Ordinary navigations
|
|
3078
3144
|
// keep their own abort signal and cannot cancel another caller's work.
|
|
3079
3145
|
const record = recover ? null : { valid: true, deploymentId, promise: null };
|
|
3146
|
+
// A navigation (not a prefetch) streams when the router supports it: the page goes in as
|
|
3147
|
+
// soon as its shell arrives, and its Suspense content follows (takeNavigationStream). A page
|
|
3148
|
+
// whose layouts hydrate, or one rendered in this page's React root, arrives whole.
|
|
3149
|
+
const streamShell = ${streamNavigations ? `recover && !interceptFrom && !partialHeaders["F-Render"] &&
|
|
3150
|
+
!hasHydratableLayout(new URL(url, window.location.href).pathname)` : "false"};
|
|
3080
3151
|
const request = async () => {
|
|
3081
3152
|
const response = await fetchFarmNavigationDocument(url, {
|
|
3082
3153
|
"Accept": "text/html",
|
|
@@ -3085,13 +3156,28 @@ function generateUniversalRouterStateProperties() {
|
|
|
3085
3156
|
// layouts it shows.
|
|
3086
3157
|
...getFarmIslandValueHeaders(),
|
|
3087
3158
|
...partialHeaders,
|
|
3159
|
+
...(streamShell ? { "F-Stream": "flush" } : {}),
|
|
3088
3160
|
}, recover, signal);
|
|
3089
3161
|
if (!response.ok) throw new Error("Failed to fetch page");
|
|
3090
|
-
const
|
|
3091
|
-
|
|
3092
|
-
|
|
3093
|
-
|
|
3162
|
+
const store = (html) => {
|
|
3163
|
+
if (cacheResult && !signal?.aborted && generation === this.prefetchGeneration &&
|
|
3164
|
+
deploymentId === window.__FARM_DEPLOYMENT_ID__ && (!record || record.valid)) {
|
|
3165
|
+
this.storePrefetchedHtml(cacheKey, html);
|
|
3166
|
+
}
|
|
3167
|
+
};
|
|
3168
|
+
${streamNavigations ? ` if (streamShell && response.body) {
|
|
3169
|
+
const streamed = await readFarmNavigationStream(response.body);
|
|
3170
|
+
if (!streamed.next) {
|
|
3171
|
+
store(streamed.shell);
|
|
3172
|
+
return streamed.shell;
|
|
3173
|
+
}
|
|
3174
|
+
this.stopNavigationStream();
|
|
3175
|
+
this.navigationStream = { html: streamed.shell, next: streamed.next, cancel: streamed.cancel, store };
|
|
3176
|
+
return streamed.shell;
|
|
3094
3177
|
}
|
|
3178
|
+
` : ""}
|
|
3179
|
+
const html = await response.text();
|
|
3180
|
+
store(html);
|
|
3095
3181
|
return html;
|
|
3096
3182
|
};
|
|
3097
3183
|
if (!record) return request();
|
|
@@ -4169,7 +4255,7 @@ let currentPathname = null;
|
|
|
4169
4255
|
let isHydrated = false;
|
|
4170
4256
|
let pendingPageHydrationController = null;
|
|
4171
4257
|
|
|
4172
|
-
${generateUniversalRouterStateRuntime()}
|
|
4258
|
+
${generateUniversalRouterStateRuntime(islandValues)}
|
|
4173
4259
|
|
|
4174
4260
|
function cancelPendingPageHydration() {
|
|
4175
4261
|
pendingPageHydrationController?.abort();
|
|
@@ -4357,6 +4443,55 @@ function activateNavigationScripts(root) {
|
|
|
4357
4443
|
}
|
|
4358
4444
|
}
|
|
4359
4445
|
|
|
4446
|
+
${islandValues ? `// Adds the flushes after a streamed navigation's shell: React's staged Suspense content and the
|
|
4447
|
+
// scripts that reveal it. In islands mode, the islands React reveals hydrate as they appear, and
|
|
4448
|
+
// the ones it removes (a replaced fallback) are released.
|
|
4449
|
+
async function streamFarmNavigation(stream, root, isCurrent) {
|
|
4450
|
+
let html = stream.html;
|
|
4451
|
+
let streaming = true;
|
|
4452
|
+
${isolatedHydrationEnabled ? `const removed = new Set();
|
|
4453
|
+
let queued = false;
|
|
4454
|
+
const run = function() {
|
|
4455
|
+
queued = false;
|
|
4456
|
+
if (!isCurrent()) return observer.disconnect();
|
|
4457
|
+
for (const node of removed) if (!node.isConnected) disposeFarmIsolatedClientBoundaries(node);
|
|
4458
|
+
removed.clear();
|
|
4459
|
+
if (!streaming && !root.querySelector('div[hidden][id^="S:"]')) observer.disconnect();
|
|
4460
|
+
hydrateFarmIsolatedClientBoundaries(root).catch(function(error) {
|
|
4461
|
+
console.error("[Farm.js] Hydrating streamed islands failed:", error);
|
|
4462
|
+
});
|
|
4463
|
+
};
|
|
4464
|
+
const observer = new MutationObserver(function(records) {
|
|
4465
|
+
for (const record of records) for (const node of record.removedNodes) removed.add(node);
|
|
4466
|
+
if (queued) return;
|
|
4467
|
+
queued = true;
|
|
4468
|
+
queueMicrotask(run);
|
|
4469
|
+
});
|
|
4470
|
+
observer.observe(root, { childList: true, subtree: true });` : "const observer = { disconnect: function() {} };"}
|
|
4471
|
+
try {
|
|
4472
|
+
for (let flush = await stream.next(); flush !== null; flush = await stream.next()) {
|
|
4473
|
+
if (!isCurrent()) return stream.cancel();
|
|
4474
|
+
html += flush;
|
|
4475
|
+
const parsed = new DOMParser().parseFromString('<div id="root">' + flush, "text/html");
|
|
4476
|
+
const fragment = document.createDocumentFragment();
|
|
4477
|
+
for (const node of Array.from(parsed.getElementById("root").childNodes)) fragment.appendChild(node);
|
|
4478
|
+
activateNavigationScripts(fragment);
|
|
4479
|
+
root.appendChild(fragment);
|
|
4480
|
+
}
|
|
4481
|
+
} catch (error) {
|
|
4482
|
+
streaming = false;
|
|
4483
|
+
if (!isCurrent()) return observer.disconnect();
|
|
4484
|
+
console.error("[Farm.js] A streamed navigation failed:", error);
|
|
4485
|
+
${isolatedHydrationEnabled ? "farmIsolatedHydrationRuntime.endStream?.(root);\n if (!queued) run();" : ""}
|
|
4486
|
+
return;
|
|
4487
|
+
}
|
|
4488
|
+
// A cancelled stream ends early: keep its partial document out of the cache.
|
|
4489
|
+
if (!isCurrent()) return observer.disconnect();
|
|
4490
|
+
streaming = false;
|
|
4491
|
+
stream.store(html);
|
|
4492
|
+
${isolatedHydrationEnabled ? "farmIsolatedHydrationRuntime.endStream?.(root);\n if (!queued) run();" : ""}
|
|
4493
|
+
}` : ""}
|
|
4494
|
+
|
|
4360
4495
|
function replaceSharedLayoutBoundary(currentRoot, nextRoot, refresh = false) {
|
|
4361
4496
|
const currentPatterns = getLayoutPatterns(currentRoot);
|
|
4362
4497
|
const nextPatterns = getLayoutPatterns(nextRoot);
|
|
@@ -4399,7 +4534,7 @@ function replaceSharedLayoutBoundary(currentRoot, nextRoot, refresh = false) {
|
|
|
4399
4534
|
|
|
4400
4535
|
// SPA Router
|
|
4401
4536
|
const spaRouter = {
|
|
4402
|
-
${generateUniversalRouterStateProperties()}
|
|
4537
|
+
${generateUniversalRouterStateProperties(islandValues)}
|
|
4403
4538
|
prefetchCache: new Map(),
|
|
4404
4539
|
${islandValues ? ` // Islands mode: navigations name the layouts on screen, and say when this page renders the
|
|
4405
4540
|
// next one in its own React root; the server leaves out what the browser keeps or rebuilds.
|
|
@@ -4411,7 +4546,7 @@ ${islandValues ? ` // Islands mode: navigations name the layouts on screen, and
|
|
|
4411
4546
|
...(reactRoot && reactRootContainer === root ? { "F-Render": "tree" } : {}),
|
|
4412
4547
|
};
|
|
4413
4548
|
},
|
|
4414
|
-
` : ""}
|
|
4549
|
+
` : ""}
|
|
4415
4550
|
navigate: async function(href, options = {}) {
|
|
4416
4551
|
const url = new URL(href, window.location.href);
|
|
4417
4552
|
if (isFarmBlockedNavigationURL(url)) return;
|
|
@@ -4464,6 +4599,7 @@ ${islandValues ? ` // Islands mode: navigations name the layouts on screen, and
|
|
|
4464
4599
|
|
|
4465
4600
|
// A route transition invalidates any trigger waiting on the previous DOM boundary.
|
|
4466
4601
|
cancelPendingPageHydration();
|
|
4602
|
+
this.stopNavigationStream();
|
|
4467
4603
|
this.saveScrollPosition(window.location.pathname + window.location.search);
|
|
4468
4604
|
const navigation = this.startNavigation(from, url, action);
|
|
4469
4605
|
// A refresh keeps the islands still on the page (islands mode).
|
|
@@ -4588,6 +4724,7 @@ ${islandValues ? ` // Islands mode: navigations name the layouts on screen, and
|
|
|
4588
4724
|
if (!this.isCurrentNavigation(navigation, clientNavigation)) return;
|
|
4589
4725
|
await this.runViewTransition(options.viewTransition, async () => {
|
|
4590
4726
|
if (!this.isCurrentNavigation(navigation, clientNavigation)) return;
|
|
4727
|
+
let swappedHtml = html;
|
|
4591
4728
|
let swapped = await this.swapContent(
|
|
4592
4729
|
html,
|
|
4593
4730
|
url.pathname + url.search,
|
|
@@ -4607,6 +4744,7 @@ ${islandValues ? ` // Islands mode: navigations name the layouts on screen, and
|
|
|
4607
4744
|
false,
|
|
4608
4745
|
);
|
|
4609
4746
|
if (!this.isCurrentNavigation(navigation, clientNavigation)) return;
|
|
4747
|
+
swappedHtml = documentHtml;
|
|
4610
4748
|
swapped = await this.swapContent(
|
|
4611
4749
|
documentHtml,
|
|
4612
4750
|
url.pathname + url.search,
|
|
@@ -4618,6 +4756,7 @@ ${islandValues ? ` // Islands mode: navigations name the layouts on screen, and
|
|
|
4618
4756
|
if (!swapped || swapped === "segment-mismatch") {
|
|
4619
4757
|
throw new Error("Farm could not swap the target document");
|
|
4620
4758
|
}
|
|
4759
|
+
this.takeNavigationStream(swappedHtml);
|
|
4621
4760
|
this.writeHistoryEntry(action, url.pathname + url.search, options.state, url);
|
|
4622
4761
|
currentPathname = pathname;
|
|
4623
4762
|
this.currentPath = to;
|
|
@@ -6796,11 +6935,16 @@ function escapeFarmHtmlAttribute(value) {
|
|
|
6796
6935
|
.replace(/>/g, ">");
|
|
6797
6936
|
}
|
|
6798
6937
|
|
|
6799
|
-
async function renderFarmElement(ReactDOMServer, element, routeKey) {
|
|
6938
|
+
async function renderFarmElement(ReactDOMServer, element, routeKey, flushMarkers = false) {
|
|
6939
|
+
// Written after each React flush in a streamed navigation (flushMarkers): the browser swaps in
|
|
6940
|
+
// the shell, then adds each Suspense boundary as soon as the flush that completed it ends.
|
|
6941
|
+
const FARM_FLUSH_MARKER = "<!--farm-flush-->";
|
|
6800
6942
|
// Islands mode: each render writes its islands' values itself, so a request that renders twice
|
|
6801
6943
|
// (notFound(), or a synchronous attempt that suspends and streams) still sends them.
|
|
6802
6944
|
if (typeof farmIslandsRender === "function") element = farmIslandsRender(element);
|
|
6803
6945
|
const streamErrors = [];
|
|
6946
|
+
// A browser that leaves the page cancels the stream: React's abort errors are not failures.
|
|
6947
|
+
let cancelled = false;
|
|
6804
6948
|
const syncHtml = renderFarmElementSync(ReactDOMServer, element, routeKey);
|
|
6805
6949
|
if (syncHtml !== undefined) return { html: syncHtml, shellHtml: syncHtml, streamErrors };
|
|
6806
6950
|
let encoder;
|
|
@@ -6838,7 +6982,7 @@ async function renderFarmElement(ReactDOMServer, element, routeKey) {
|
|
|
6838
6982
|
// request handler must still receive them to map the response status,
|
|
6839
6983
|
// but they should not produce a misleading SSR failure log.
|
|
6840
6984
|
streamErrors.push(error);
|
|
6841
|
-
if (isFarmRedirectError(error) || isFarmNotFoundError(error)) return;
|
|
6985
|
+
if (cancelled || isFarmRedirectError(error) || isFarmNotFoundError(error)) return;
|
|
6842
6986
|
console.error("[Farm SSR stream]", error);
|
|
6843
6987
|
},
|
|
6844
6988
|
});
|
|
@@ -6885,13 +7029,33 @@ async function renderFarmElement(ReactDOMServer, element, routeKey) {
|
|
|
6885
7029
|
}
|
|
6886
7030
|
|
|
6887
7031
|
let replayFirstChunk = true;
|
|
7032
|
+
let pendingRead = null;
|
|
7033
|
+
// A flush is the chunks React has queued when a task boundary passes.
|
|
7034
|
+
const drainFlush = async function(controller, waitForFirst) {
|
|
7035
|
+
for (let first = waitForFirst; ; first = false) {
|
|
7036
|
+
const read = pendingRead || (pendingRead = reader.read());
|
|
7037
|
+
const result = first
|
|
7038
|
+
? await read
|
|
7039
|
+
: await Promise.race([read, new Promise(function(resolve) { setTimeout(resolve, 0); })]);
|
|
7040
|
+
if (!result) break;
|
|
7041
|
+
pendingRead = null;
|
|
7042
|
+
if (result.done) {
|
|
7043
|
+
controller.close();
|
|
7044
|
+
return;
|
|
7045
|
+
}
|
|
7046
|
+
controller.enqueue(normalizeChunk(result.value));
|
|
7047
|
+
}
|
|
7048
|
+
controller.enqueue(normalizeChunk(FARM_FLUSH_MARKER));
|
|
7049
|
+
};
|
|
6888
7050
|
const replayStream = new ReadableStream({
|
|
6889
7051
|
pull(controller) {
|
|
6890
7052
|
if (replayFirstChunk) {
|
|
6891
7053
|
replayFirstChunk = false;
|
|
6892
7054
|
controller.enqueue(firstChunk);
|
|
7055
|
+
if (flushMarkers) return drainFlush(controller, false);
|
|
6893
7056
|
return;
|
|
6894
7057
|
}
|
|
7058
|
+
if (flushMarkers) return drainFlush(controller, true);
|
|
6895
7059
|
return reader.read().then(
|
|
6896
7060
|
function(result) {
|
|
6897
7061
|
if (result.done) controller.close();
|
|
@@ -6903,6 +7067,7 @@ async function renderFarmElement(ReactDOMServer, element, routeKey) {
|
|
|
6903
7067
|
);
|
|
6904
7068
|
},
|
|
6905
7069
|
cancel(reason) {
|
|
7070
|
+
cancelled = true;
|
|
6906
7071
|
return reader.cancel(reason);
|
|
6907
7072
|
},
|
|
6908
7073
|
});
|
|
@@ -6927,6 +7092,7 @@ async function renderFarmElement(ReactDOMServer, element, routeKey) {
|
|
|
6927
7092
|
let settleComplete;
|
|
6928
7093
|
let rejectComplete;
|
|
6929
7094
|
const chunks = [];
|
|
7095
|
+
let unflushed = false;
|
|
6930
7096
|
const decision = new Promise(function(resolve, reject) {
|
|
6931
7097
|
settleDecision = resolve;
|
|
6932
7098
|
rejectDecision = reject;
|
|
@@ -6949,8 +7115,15 @@ async function renderFarmElement(ReactDOMServer, element, routeKey) {
|
|
|
6949
7115
|
const normalized = normalizeChunk(chunk);
|
|
6950
7116
|
if (streamStarted) streamController.enqueue(normalized);
|
|
6951
7117
|
else chunks.push(normalized);
|
|
7118
|
+
unflushed = true;
|
|
6952
7119
|
return true;
|
|
6953
7120
|
},
|
|
7121
|
+
// React calls this after each flush.
|
|
7122
|
+
flush() {
|
|
7123
|
+
if (!flushMarkers || !streamStarted || streamClosed || !unflushed) return;
|
|
7124
|
+
unflushed = false;
|
|
7125
|
+
streamController.enqueue(normalizeChunk(FARM_FLUSH_MARKER));
|
|
7126
|
+
},
|
|
6954
7127
|
end() {
|
|
6955
7128
|
if (streamClosed) return;
|
|
6956
7129
|
streamClosed = true;
|
|
@@ -6979,6 +7152,9 @@ async function renderFarmElement(ReactDOMServer, element, routeKey) {
|
|
|
6979
7152
|
streamController = controller;
|
|
6980
7153
|
},
|
|
6981
7154
|
cancel(reason) {
|
|
7155
|
+
// React writes its abort instructions: the stream no longer takes them.
|
|
7156
|
+
cancelled = true;
|
|
7157
|
+
streamClosed = true;
|
|
6982
7158
|
if (pipeableStream && typeof pipeableStream.abort === "function") {
|
|
6983
7159
|
pipeableStream.abort(reason);
|
|
6984
7160
|
}
|
|
@@ -6998,7 +7174,7 @@ async function renderFarmElement(ReactDOMServer, element, routeKey) {
|
|
|
6998
7174
|
onError(error) {
|
|
6999
7175
|
// Same control-flow guard as the readable-stream path above.
|
|
7000
7176
|
streamErrors.push(error);
|
|
7001
|
-
if (isFarmRedirectError(error) || isFarmNotFoundError(error)) return;
|
|
7177
|
+
if (cancelled || isFarmRedirectError(error) || isFarmNotFoundError(error)) return;
|
|
7002
7178
|
console.error("[Farm SSR stream]", error);
|
|
7003
7179
|
},
|
|
7004
7180
|
});
|
|
@@ -7019,6 +7195,11 @@ async function renderFarmElement(ReactDOMServer, element, routeKey) {
|
|
|
7019
7195
|
|
|
7020
7196
|
streamStarted = true;
|
|
7021
7197
|
for (const chunk of chunks) streamController.enqueue(chunk);
|
|
7198
|
+
// What React wrote while its shell was ready is the shell; Suspense content follows.
|
|
7199
|
+
if (flushMarkers && unflushed) {
|
|
7200
|
+
unflushed = false;
|
|
7201
|
+
streamController.enqueue(normalizeChunk(FARM_FLUSH_MARKER));
|
|
7202
|
+
}
|
|
7022
7203
|
if (streamClosed) streamController.close();
|
|
7023
7204
|
return {
|
|
7024
7205
|
stream,
|
|
@@ -9293,8 +9474,12 @@ ${hasServerRuntimeIntegrations ? `
|
|
|
9293
9474
|
shouldHydrateLayout &&
|
|
9294
9475
|
route.serverTree === true &&
|
|
9295
9476
|
!route.shouldHydrate &&
|
|
9296
|
-
request.headers.get("f-render") === "tree"
|
|
9297
|
-
|
|
9477
|
+
request.headers.get("f-render") === "tree";
|
|
9478
|
+
// A navigation the browser streams marks where each React flush ends, so the browser
|
|
9479
|
+
// shows the page before its Suspense content arrives (FARM_FLUSH_MARKER).
|
|
9480
|
+
const farmFlushMarkers = request.headers.get("f-stream") === "flush";` : `const farmLayoutKeyOf = null;
|
|
9481
|
+
const farmPayloadOnlyNavigation = false;
|
|
9482
|
+
const farmFlushMarkers = false;`}
|
|
9298
9483
|
const renderPageElement = async () => {
|
|
9299
9484
|
// First, render the page content
|
|
9300
9485
|
let pageElement;
|
|
@@ -9416,7 +9601,7 @@ ${hasServerRuntimeIntegrations ? `
|
|
|
9416
9601
|
}
|
|
9417
9602
|
|
|
9418
9603
|
wrappedElement = wrapWithFarmIntegrationProviders(wrappedElement);
|
|
9419
|
-
return renderFarmElement(ReactDOMServer, wrappedElement, route.pattern);
|
|
9604
|
+
return renderFarmElement(ReactDOMServer, wrappedElement, route.pattern, farmFlushMarkers);
|
|
9420
9605
|
};
|
|
9421
9606
|
const renderedPage = ${hasMiddlewareRuntime ? `await _runWithMiddlewareData(middlewareData, () =>
|
|
9422
9607
|
_runWithMiddlewareContext(middlewareContext, renderPageElement)
|
|
@@ -9539,7 +9724,7 @@ ${hasServerRuntimeIntegrations ? `
|
|
|
9539
9724
|
// gets HTML without them.
|
|
9540
9725
|
responseHeaders["Vary"] = farmSharedCacheableRoute
|
|
9541
9726
|
? "F-Layouts"
|
|
9542
|
-
: "F-Values, F-Layouts, F-Render";` : ""}
|
|
9727
|
+
: "F-Values, F-Layouts, F-Render, F-Stream";` : ""}
|
|
9543
9728
|
let html = renderedPage.html;
|
|
9544
9729
|
|
|
9545
9730
|
// Check if the layout already rendered a full HTML document
|