@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.
Files changed (135) hide show
  1. package/dist/{agent-mcp-DAjB8Eyu.d.ts → agent-mcp-Bb8hnnqd.d.ts} +1 -1
  2. package/dist/{agent-mcp-CXN3g0eX.d.mts → agent-mcp-ir3v4fHf.d.mts} +1 -1
  3. package/dist/agent-runtime.d.mts +1 -1
  4. package/dist/agent-runtime.d.ts +1 -1
  5. package/dist/api/route-manager.d.mts +3 -3
  6. package/dist/api/route-manager.d.ts +3 -3
  7. package/dist/api/runtime.d.mts +2 -2
  8. package/dist/api/runtime.d.ts +2 -2
  9. package/dist/api.d.mts +3 -3
  10. package/dist/api.d.ts +3 -3
  11. package/dist/{app-DytgRPjJ.d.mts → app-DLzz02du.d.mts} +4 -4
  12. package/dist/{app-manifest-DPgW5ke5.d.mts → app-manifest-Crn8Qd3U.d.mts} +1 -1
  13. package/dist/{app-manifest-BUu7YGAx.d.ts → app-manifest-DNeTKMRL.d.ts} +1 -1
  14. package/dist/app-markdown.d.mts +2 -2
  15. package/dist/app-markdown.d.ts +2 -2
  16. package/dist/{app-VK9sHDn5.d.ts → app-y649szYm.d.ts} +4 -4
  17. package/dist/{chunk-Q2ZKQ6GD.cjs → chunk-4HM5B5RR.cjs} +62 -62
  18. package/dist/{chunk-MZZA6ZQV.mjs → chunk-7PZVXMWG.mjs} +4 -4
  19. package/dist/{chunk-HRLYO2XJ.mjs → chunk-ACI52TCN.mjs} +1 -1
  20. package/dist/{chunk-VDLF2DJA.cjs → chunk-AIHD6VME.cjs} +276 -91
  21. package/dist/{chunk-YSPMY4B7.cjs → chunk-C5S457CK.cjs} +170 -108
  22. package/dist/{chunk-NRFUVDTH.mjs → chunk-DGVVVYKD.mjs} +1 -1
  23. package/dist/{chunk-WUEDTW6E.cjs → chunk-FR4HJDI6.cjs} +21 -0
  24. package/dist/{chunk-HUL4LTC6.cjs → chunk-HTLCLOXQ.cjs} +63 -20
  25. package/dist/{chunk-N7SPZJLY.mjs → chunk-IVHPRF4W.mjs} +5 -5
  26. package/dist/{chunk-6M3HXUBQ.mjs → chunk-J27BZO67.mjs} +21 -0
  27. package/dist/{chunk-JLS6ZLFM.cjs → chunk-J5VCA45K.cjs} +1 -1
  28. package/dist/{chunk-ZGS3ZDF7.cjs → chunk-NPPT4RWF.cjs} +2 -2
  29. package/dist/{chunk-TFA6BUAE.cjs → chunk-OY5XDG24.cjs} +4 -4
  30. package/dist/{chunk-N6SIQJEJ.mjs → chunk-RYDDUCNJ.mjs} +54 -11
  31. package/dist/{chunk-C2R5RVLA.cjs → chunk-VCKGOXFI.cjs} +73 -1
  32. package/dist/{chunk-TQWGOQ7O.mjs → chunk-Z3BXMMFF.mjs} +80 -18
  33. package/dist/{chunk-NE33FRQ5.mjs → chunk-Z4LTSH7D.mjs} +207 -22
  34. package/dist/{chunk-NIPKC4DA.mjs → chunk-ZIBIE7AH.mjs} +72 -0
  35. package/dist/{client-plugins-BlEzV5FO.d.mts → client-plugins-BNBeyLMi.d.mts} +4 -2
  36. package/dist/{client-plugins-DqjCsKMN.d.ts → client-plugins-DFDQUI9Q.d.ts} +4 -2
  37. package/dist/client-plugins.d.mts +1 -1
  38. package/dist/client-plugins.d.ts +1 -1
  39. package/dist/client.d.mts +5 -5
  40. package/dist/client.d.ts +5 -5
  41. package/dist/config.d.mts +1 -1
  42. package/dist/config.d.ts +1 -1
  43. package/dist/desktop.d.mts +2 -2
  44. package/dist/desktop.d.ts +2 -2
  45. package/dist/{dev-guard-4NQ3LFO5.cjs → dev-guard-PLHL6OTJ.cjs} +3 -2
  46. package/dist/{dev-guard-MPZUJFIV.mjs → dev-guard-YO6KPIOQ.mjs} +2 -1
  47. package/dist/docs.d.mts +2 -2
  48. package/dist/docs.d.ts +2 -2
  49. package/dist/{index-CMigAr9f.d.mts → index-CeJ9_s9p.d.mts} +1 -1
  50. package/dist/{index-n3hjOvVC.d.ts → index-DBdiTkNc.d.ts} +1 -1
  51. package/dist/index.cjs +7 -7
  52. package/dist/index.d.mts +15 -15
  53. package/dist/index.d.ts +15 -15
  54. package/dist/index.mjs +6 -6
  55. package/dist/integrations.d.mts +1 -1
  56. package/dist/integrations.d.ts +1 -1
  57. package/dist/internal/app-markdown-runtime.d.mts +2 -2
  58. package/dist/internal/app-markdown-runtime.d.ts +2 -2
  59. package/dist/internal/build-runtime.cjs +9 -8
  60. package/dist/internal/build-runtime.d.mts +3 -3
  61. package/dist/internal/build-runtime.d.ts +3 -3
  62. package/dist/internal/build-runtime.mjs +8 -7
  63. package/dist/internal/client-error-overlay.cjs +4 -4
  64. package/dist/internal/client-error-overlay.mjs +1 -1
  65. package/dist/internal/compression-runtime.d.mts +1 -1
  66. package/dist/internal/compression-runtime.d.ts +1 -1
  67. package/dist/internal/config-runtime.d.mts +2 -2
  68. package/dist/internal/config-runtime.d.ts +2 -2
  69. package/dist/internal/desktop-guard.cjs +2 -2
  70. package/dist/internal/desktop-guard.d.mts +7 -0
  71. package/dist/internal/desktop-guard.d.ts +7 -0
  72. package/dist/internal/desktop-guard.mjs +1 -1
  73. package/dist/internal/infra-analytics-runtime.cjs +5 -5
  74. package/dist/internal/infra-analytics-runtime.mjs +3 -3
  75. package/dist/internal/islands-runtime.cjs +4 -1
  76. package/dist/internal/islands-runtime.d.mts +9 -12
  77. package/dist/internal/islands-runtime.d.ts +9 -12
  78. package/dist/internal/islands-runtime.mjs +5 -2
  79. package/dist/internal/product-telemetry-runtime.cjs +2 -2
  80. package/dist/internal/product-telemetry-runtime.mjs +1 -1
  81. package/dist/internal/production-runtime.cjs +4 -4
  82. package/dist/internal/production-runtime.d.mts +10 -10
  83. package/dist/internal/production-runtime.d.ts +10 -10
  84. package/dist/internal/production-runtime.mjs +3 -3
  85. package/dist/markdown.d.mts +1 -1
  86. package/dist/markdown.d.ts +1 -1
  87. package/dist/middleware.d.mts +3 -3
  88. package/dist/middleware.d.ts +3 -3
  89. package/dist/navigation.d.mts +4 -4
  90. package/dist/navigation.d.ts +4 -4
  91. package/dist/plugin.cjs +6 -6
  92. package/dist/plugin.d.mts +3 -3
  93. package/dist/plugin.d.ts +3 -3
  94. package/dist/plugin.mjs +5 -5
  95. package/dist/{production-runtime-YVDGYWBG.d.ts → production-runtime-B3qFFtKF.d.ts} +1 -1
  96. package/dist/{production-runtime-bWZJMcQb.d.mts → production-runtime-DcD-GRAv.d.mts} +1 -1
  97. package/dist/{programmatic-routes-client-BFKOTEXN.d.mts → programmatic-routes-client-DzbYGiZQ.d.mts} +2 -2
  98. package/dist/{programmatic-routes-client-DviwGeAv.d.ts → programmatic-routes-client-fD9f4XqU.d.ts} +2 -2
  99. package/dist/{render-plan-DqWXo3-B.d.ts → render-plan-B6cfssrr.d.ts} +1 -1
  100. package/dist/{render-plan-CS8l0zaK.d.mts → render-plan-DqJ51ufr.d.mts} +1 -1
  101. package/dist/renderer-client.d.mts +3 -3
  102. package/dist/renderer-client.d.ts +3 -3
  103. package/dist/{request-D4XqmnJF.d.mts → request-UuAxHn8h.d.mts} +1 -1
  104. package/dist/{request-CBrHpaCC.d.ts → request-jhvUMXRx.d.ts} +1 -1
  105. package/dist/request.d.mts +2 -2
  106. package/dist/request.d.ts +2 -2
  107. package/dist/{route-rendering-3pRPtyO2.d.mts → route-rendering-Cou00t1X.d.mts} +1 -1
  108. package/dist/{route-rendering-Bltnu8a3.d.ts → route-rendering-Sn50cYZf.d.ts} +1 -1
  109. package/dist/{router-BFSmKGtI.d.mts → router-0X9DCTcm.d.mts} +1 -1
  110. package/dist/{router-DDT7bkg7.d.ts → router-xwLvi48p.d.ts} +1 -1
  111. package/dist/routes.d.mts +1 -1
  112. package/dist/routes.d.ts +1 -1
  113. package/dist/{rsc-pipeline-ULFIB3UT.mjs → rsc-pipeline-F6ZPH5MU.mjs} +2 -1
  114. package/dist/{rsc-pipeline-4KOITP64.cjs → rsc-pipeline-VKHMABDJ.cjs} +3 -2
  115. package/dist/{runtime-Cpylaypt.d.mts → runtime-CMrsyChI.d.mts} +1 -1
  116. package/dist/{runtime-C6yG64je.d.ts → runtime-tkSXZiqO.d.ts} +1 -1
  117. package/dist/server-plugins.d.mts +2 -2
  118. package/dist/server-plugins.d.ts +2 -2
  119. package/dist/server.cjs +18 -18
  120. package/dist/server.d.mts +9 -9
  121. package/dist/server.d.ts +9 -9
  122. package/dist/server.mjs +7 -7
  123. package/dist/{spa-router-CKuS81mi.d.ts → spa-router-B06APnGK.d.ts} +2 -2
  124. package/dist/{spa-router-CkTPnA4L.d.mts → spa-router-B48EjsS1.d.mts} +2 -2
  125. package/dist/testing.d.mts +1 -1
  126. package/dist/testing.d.ts +1 -1
  127. package/dist/{utils-C0ORLYXc.d.ts → utils-C5fyK2tE.d.ts} +1 -1
  128. package/dist/{utils-IA9wVkef.d.mts → utils-hyAwmcN6.d.mts} +1 -1
  129. package/dist/version.cjs +2 -2
  130. package/dist/version.mjs +1 -1
  131. package/dist/vite.cjs +6 -6
  132. package/dist/vite.d.mts +4 -4
  133. package/dist/vite.d.ts +4 -4
  134. package/dist/vite.mjs +5 -5
  135. 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-NIPKC4DA.mjs";
33
+ } from "./chunk-ZIBIE7AH.mjs";
32
34
  import {
33
35
  listFarmMCPLlmsTxtLinks
34
- } from "./chunk-NRFUVDTH.mjs";
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-HRLYO2XJ.mjs";
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 `</div>
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
- if (!renderToPipeableStream || _isFarmMarkdownPageCheck(req.headers?.[FARM_MARKDOWN_PAGE_CHECK_HEADER]) || fullDocumentRoutes.has(fullDocumentRouteKey) || await this.rootLayoutRendersDocument()) {
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
- htmlParts.push(securedShell);
6949
- staticShellParts?.push(securedShell);
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 (!chunkText) return;
6957
- if (!checkedFullDocument) {
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
- recordRenderedChunk(securedChunkText);
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 (cspNonceRewriter) res.write(securedChunkText, onWrite);
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 secured = filterDocumentHTML(recovery);
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 tail = cspNonceRewriter.write(cspStreamDecoder.decode(), true);
7023
- recordRenderedChunk(tail);
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-N6SIQJEJ.mjs";
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-TQWGOQ7O.mjs";
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-NIPKC4DA.mjs";
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-NRFUVDTH.mjs";
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-HRLYO2XJ.mjs";
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
- blockers: new Set(),
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 html = await response.text();
3091
- if (cacheResult && !signal?.aborted && generation === this.prefetchGeneration &&
3092
- deploymentId === window.__FARM_DEPLOYMENT_ID__ && (!record || record.valid)) {
3093
- this.storePrefetchedHtml(cacheKey, html);
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, "&gt;");
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";` : `const farmLayoutKeyOf = null;
9297
- const farmPayloadOnlyNavigation = false;`}
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