@farm.js/core 0.1.28 → 0.1.30

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 (149) hide show
  1. package/dist/agent-runtime.d.mts +2 -2
  2. package/dist/agent-runtime.d.ts +2 -2
  3. package/dist/api/route-manager.d.mts +1 -1
  4. package/dist/api/route-manager.d.ts +1 -1
  5. package/dist/api/runtime.d.mts +2 -2
  6. package/dist/api/runtime.d.ts +2 -2
  7. package/dist/api.cjs +2 -2
  8. package/dist/api.d.mts +18 -4
  9. package/dist/api.d.ts +18 -4
  10. package/dist/api.mjs +1 -1
  11. package/dist/{app-DSSq6wiN.d.mts → app-DAuUTIZf.d.mts} +3 -3
  12. package/dist/app-markdown.cjs +5 -4
  13. package/dist/app-markdown.d.mts +3 -3
  14. package/dist/app-markdown.d.ts +3 -3
  15. package/dist/app-markdown.mjs +4 -3
  16. package/dist/{app-M8nVWvNU.d.ts → app-sUJ3jnTZ.d.ts} +3 -3
  17. package/dist/{chunk-N6UROPT7.mjs → chunk-2FQHH5SC.mjs} +12 -13
  18. package/dist/{chunk-2CNBR3EB.mjs → chunk-2UABTL3Y.mjs} +2 -2
  19. package/dist/{chunk-JUFRXDTK.cjs → chunk-3FCFQP5V.cjs} +17 -5
  20. package/dist/{chunk-XSPGWQWA.cjs → chunk-6CNX6Q3K.cjs} +96 -24
  21. package/dist/{chunk-NVBS5WSZ.mjs → chunk-6G3ZZ2IE.mjs} +1 -1
  22. package/dist/{chunk-QBPXNZGV.cjs → chunk-6MVFL3R5.cjs} +25 -6
  23. package/dist/{chunk-PZPDGOU5.mjs → chunk-7CXKSRKL.mjs} +11 -7
  24. package/dist/{chunk-K3YEZLSY.mjs → chunk-ATXI6LGT.mjs} +96 -24
  25. package/dist/{chunk-2DLN6HG4.mjs → chunk-BZ3CHOFX.mjs} +1 -1
  26. package/dist/{chunk-EWZ3XHKQ.cjs → chunk-BZAGKCKB.cjs} +2 -2
  27. package/dist/{chunk-FB3H4INL.mjs → chunk-D234YWNA.mjs} +1 -1
  28. package/dist/{chunk-KXGDXYUC.mjs → chunk-DGXN3L4D.mjs} +17 -5
  29. package/dist/chunk-FJ3M4CUX.cjs +46 -0
  30. package/dist/chunk-FXEF4PPL.mjs +47 -0
  31. package/dist/{chunk-ID6OVSLH.cjs → chunk-GIWC4CFJ.cjs} +175 -171
  32. package/dist/{chunk-F52CDFJM.cjs → chunk-HPQC2LHF.cjs} +435 -274
  33. package/dist/{chunk-FVCSLZWT.mjs → chunk-IVLUJUT4.mjs} +315 -154
  34. package/dist/{chunk-KOQ555NR.cjs → chunk-JFHKNFEO.cjs} +24 -20
  35. package/dist/{chunk-UX2DD2VK.mjs → chunk-K6V52QMI.mjs} +1 -1
  36. package/dist/{chunk-BYODCJD6.cjs → chunk-K7CELQLK.cjs} +12 -13
  37. package/dist/{chunk-A672WZEG.cjs → chunk-NPPL5YDT.cjs} +2 -2
  38. package/dist/{chunk-TII23GJC.cjs → chunk-NXG66MR7.cjs} +3 -3
  39. package/dist/{chunk-R6AIILII.cjs → chunk-OZVEHHMA.cjs} +15 -14
  40. package/dist/{chunk-T7HQ3I4C.mjs → chunk-PQD2AI3B.mjs} +1 -1
  41. package/dist/{chunk-VQXDC5UV.cjs → chunk-Q5JFU33Q.cjs} +4 -4
  42. package/dist/{chunk-FZC7EEPM.mjs → chunk-RSOJ7DRT.mjs} +26 -7
  43. package/dist/{chunk-ZRIE4KMG.mjs → chunk-S4K74WI4.mjs} +17 -13
  44. package/dist/{chunk-Y7GW7J46.cjs → chunk-X5TXZFMN.cjs} +1 -1
  45. package/dist/chunk-XD32KALF.cjs +47 -0
  46. package/dist/{chunk-RDV44SED.mjs → chunk-XJJVLDM6.mjs} +15 -14
  47. package/dist/{chunk-55ZY4ZA7.cjs → chunk-XLTV4VOG.cjs} +21 -16
  48. package/dist/{chunk-REGFZJEJ.mjs → chunk-YMUE7L3D.mjs} +18 -13
  49. package/dist/chunk-Z3HATSKO.mjs +46 -0
  50. package/dist/{chunk-MH24QDVJ.cjs → chunk-ZDILFMWN.cjs} +13 -13
  51. package/dist/{client-plugins-CgYNY7jF.d.mts → client-plugins-BX2ptm4H.d.mts} +7 -2
  52. package/dist/{client-plugins-Fl9Og3yB.d.ts → client-plugins-BmxM5KW_.d.ts} +7 -2
  53. package/dist/client-plugins.d.mts +2 -2
  54. package/dist/client-plugins.d.ts +2 -2
  55. package/dist/client.d.mts +5 -5
  56. package/dist/client.d.ts +5 -5
  57. package/dist/config.d.mts +2 -2
  58. package/dist/config.d.ts +2 -2
  59. package/dist/{devtools-5AREISIA.cjs → devtools-GGVPZHJX.cjs} +95 -20
  60. package/dist/{devtools-3E7ZANMZ.mjs → devtools-THVNPI2M.mjs} +79 -4
  61. package/dist/{devtools-client-4WAPJY5L.cjs → devtools-client-CXQ6PXI6.cjs} +1 -1
  62. package/dist/{devtools-client-LEUIHHAS.mjs → devtools-client-HJS2T34Y.mjs} +1 -1
  63. package/dist/docs.cjs +8 -7
  64. package/dist/docs.mjs +7 -6
  65. package/dist/index.cjs +18 -16
  66. package/dist/index.d.mts +24 -14
  67. package/dist/index.d.ts +24 -14
  68. package/dist/index.mjs +17 -15
  69. package/dist/integrations.d.mts +2 -2
  70. package/dist/integrations.d.ts +2 -2
  71. package/dist/internal/app-markdown-runtime.cjs +4 -3
  72. package/dist/internal/app-markdown-runtime.d.mts +3 -3
  73. package/dist/internal/app-markdown-runtime.d.ts +3 -3
  74. package/dist/internal/app-markdown-runtime.mjs +3 -2
  75. package/dist/internal/build-runtime.cjs +15 -13
  76. package/dist/internal/build-runtime.d.mts +4 -4
  77. package/dist/internal/build-runtime.d.ts +4 -4
  78. package/dist/internal/build-runtime.mjs +14 -12
  79. package/dist/internal/cli-runtime.cjs +7 -6
  80. package/dist/internal/cli-runtime.mjs +6 -5
  81. package/dist/internal/client-error-overlay.cjs +4 -4
  82. package/dist/internal/client-error-overlay.mjs +1 -1
  83. package/dist/internal/compression-runtime.cjs +3 -2
  84. package/dist/internal/compression-runtime.d.mts +5 -3
  85. package/dist/internal/compression-runtime.d.ts +5 -3
  86. package/dist/internal/compression-runtime.mjs +2 -1
  87. package/dist/internal/config-runtime.cjs +6 -5
  88. package/dist/internal/config-runtime.d.mts +3 -3
  89. package/dist/internal/config-runtime.d.ts +3 -3
  90. package/dist/internal/config-runtime.mjs +5 -4
  91. package/dist/internal/product-telemetry-runtime.cjs +10 -6
  92. package/dist/internal/product-telemetry-runtime.mjs +9 -5
  93. package/dist/internal/production-runtime.cjs +19 -5
  94. package/dist/internal/production-runtime.d.mts +35 -8
  95. package/dist/internal/production-runtime.d.ts +35 -8
  96. package/dist/internal/production-runtime.mjs +18 -4
  97. package/dist/markdown.cjs +3 -2
  98. package/dist/markdown.d.mts +2 -2
  99. package/dist/markdown.d.ts +2 -2
  100. package/dist/markdown.mjs +2 -1
  101. package/dist/{metadata-B0X-qtcQ.d.mts → metadata-DsEaK7mQ.d.mts} +1 -1
  102. package/dist/{metadata-BdDKgd7K.d.ts → metadata-fwZ3gfG6.d.ts} +1 -1
  103. package/dist/middleware.cjs +3 -2
  104. package/dist/middleware.d.mts +3 -3
  105. package/dist/middleware.d.ts +3 -3
  106. package/dist/middleware.mjs +2 -1
  107. package/dist/navigation.d.mts +4 -4
  108. package/dist/navigation.d.ts +4 -4
  109. package/dist/plugin.cjs +10 -9
  110. package/dist/plugin.d.mts +2 -2
  111. package/dist/plugin.d.ts +2 -2
  112. package/dist/plugin.mjs +9 -8
  113. package/dist/{production-runtime-BjgFgDIg.d.mts → production-runtime-Baqgwtvc.d.mts} +1 -1
  114. package/dist/{production-runtime-Dx_qMA3t.d.ts → production-runtime-DNkpFnmg.d.ts} +1 -1
  115. package/dist/{render-plan-_1TAWODt.d.mts → render-plan-DhwQnmmX.d.mts} +1 -1
  116. package/dist/{render-plan-B41EWmBb.d.ts → render-plan-rkIK9wd9.d.ts} +1 -1
  117. package/dist/renderer-client.d.mts +3 -3
  118. package/dist/renderer-client.d.ts +3 -3
  119. package/dist/{request-Dbf3G0zM.d.ts → request-BcY4RYPC.d.ts} +1 -1
  120. package/dist/{request-BhE4luLv.d.mts → request-CFy1BltJ.d.mts} +1 -1
  121. package/dist/request.d.mts +3 -3
  122. package/dist/request.d.ts +3 -3
  123. package/dist/{route-rendering-zzfx_GHf.d.mts → route-rendering-BkS0TBlB.d.mts} +1 -1
  124. package/dist/{route-rendering-Z7PveTZE.d.ts → route-rendering-D4XpMjam.d.ts} +1 -1
  125. package/dist/{router-BatNMsa1.d.mts → router-C4zPlC3V.d.mts} +1 -1
  126. package/dist/{router-DtNRhQl3.d.ts → router-Cq7BcZxg.d.ts} +1 -1
  127. package/dist/routes.d.mts +2 -2
  128. package/dist/routes.d.ts +2 -2
  129. package/dist/server-plugins.cjs +3 -2
  130. package/dist/server-plugins.d.mts +2 -2
  131. package/dist/server-plugins.d.ts +2 -2
  132. package/dist/server-plugins.mjs +2 -1
  133. package/dist/server.cjs +33 -28
  134. package/dist/server.d.mts +8 -8
  135. package/dist/server.d.ts +8 -8
  136. package/dist/server.mjs +16 -11
  137. package/dist/{spa-router-BE3bwU-J.d.ts → spa-router-B1HI3X9k.d.ts} +2 -2
  138. package/dist/{spa-router-DQOnHZVj.d.mts → spa-router-DzPZH-AO.d.mts} +2 -2
  139. package/dist/testing.d.mts +1 -1
  140. package/dist/testing.d.ts +1 -1
  141. package/dist/{utils-DLK1ERNl.d.mts → utils-CEBktlBG.d.mts} +1 -1
  142. package/dist/{utils-u7ZBSWAK.d.ts → utils-DMD1Hzta.d.ts} +1 -1
  143. package/dist/version.cjs +2 -2
  144. package/dist/version.mjs +1 -1
  145. package/dist/vite.cjs +10 -9
  146. package/dist/vite.d.mts +2 -2
  147. package/dist/vite.d.ts +2 -2
  148. package/dist/vite.mjs +9 -8
  149. package/package.json +1 -1
@@ -4,10 +4,13 @@ import {
4
4
  import {
5
5
  withProductionNodeEnv
6
6
  } from "./chunk-655RLQQL.mjs";
7
+ import {
8
+ generateNativeMCPPluginSource
9
+ } from "./chunk-FXEF4PPL.mjs";
7
10
  import {
8
11
  compileFarmDocsAdapterEdgeManifest,
9
12
  createFarmDocsAPIHandler
10
- } from "./chunk-2CNBR3EB.mjs";
13
+ } from "./chunk-2UABTL3Y.mjs";
11
14
  import {
12
15
  FARM_DOCS_LAST_MODIFIED_MANIFEST,
13
16
  createFarmDocsHandler,
@@ -17,7 +20,7 @@ import {
17
20
  loadFarmDocsPage,
18
21
  resolveFarmDocsContentDir,
19
22
  toFarmDocsMarkdownPage
20
- } from "./chunk-T7HQ3I4C.mjs";
23
+ } from "./chunk-PQD2AI3B.mjs";
21
24
  import {
22
25
  generateFarmDocsSearchClientRuntime,
23
26
  isFarmDocsSearchEnabled,
@@ -34,7 +37,7 @@ import {
34
37
  getDeployTargetForPreset,
35
38
  hasCustomFarmRouteContext,
36
39
  resolveDeployOutputPath
37
- } from "./chunk-PZPDGOU5.mjs";
40
+ } from "./chunk-7CXKSRKL.mjs";
38
41
  import {
39
42
  resolveFarmInstrumentationFile
40
43
  } from "./chunk-ZZSRYEYU.mjs";
@@ -70,7 +73,7 @@ import {
70
73
  resolveFarmImageProviderForRuntime,
71
74
  resolveFarmIsolatedClientHydrationMode,
72
75
  resolveFarmNotFoundComponentPath
73
- } from "./chunk-ZRIE4KMG.mjs";
76
+ } from "./chunk-S4K74WI4.mjs";
74
77
  import {
75
78
  findRequestBoundSourceBlockers,
76
79
  generateFarmTypeArtifacts,
@@ -80,7 +83,7 @@ import {
80
83
  import {
81
84
  FARM_API_CATALOG_PATH,
82
85
  resolveFarmApiCatalogInput
83
- } from "./chunk-FB3H4INL.mjs";
86
+ } from "./chunk-D234YWNA.mjs";
84
87
  import {
85
88
  isFarmPreviewDeploymentEnvironment
86
89
  } from "./chunk-BOYNDCZL.mjs";
@@ -112,7 +115,7 @@ import {
112
115
  } from "./chunk-DAATZTKJ.mjs";
113
116
  import {
114
117
  applyFarmCspHashesToHtml
115
- } from "./chunk-REGFZJEJ.mjs";
118
+ } from "./chunk-YMUE7L3D.mjs";
116
119
  import {
117
120
  applyFarmCronVercelCrons,
118
121
  createFarmCronCloudflareConfig,
@@ -124,14 +127,14 @@ import {
124
127
  } from "./chunk-4OOBRCJX.mjs";
125
128
  import {
126
129
  isFarmMarkdownPageFile
127
- } from "./chunk-2DLN6HG4.mjs";
130
+ } from "./chunk-BZ3CHOFX.mjs";
128
131
  import {
129
132
  resolveFarmMdxComponentsModulePath,
130
133
  resolveMdxConfig
131
134
  } from "./chunk-FA2W42OV.mjs";
132
135
  import {
133
136
  getFarmMarkdownAlternatePath
134
- } from "./chunk-K3YEZLSY.mjs";
137
+ } from "./chunk-ATXI6LGT.mjs";
135
138
  import {
136
139
  applyFarmWorkflowVercelCrons,
137
140
  prepareFarmWorkflowsForNitro
@@ -1255,6 +1258,19 @@ function isNitroRollupExternal(id) {
1255
1258
  }
1256
1259
  __name(isNitroRollupExternal, "isNitroRollupExternal");
1257
1260
  var FARM_METADATA_IMAGE_WASM_PREFIX = "\0farm-metadata-image-wasm:";
1261
+ function createReactProductionModePlugin() {
1262
+ const nodeEnvExpression = ["process", "env", "NODE_ENV"].join(".");
1263
+ return {
1264
+ name: "farm-react-production-mode",
1265
+ transform(code, id) {
1266
+ const normalizedId = id.replace(/\\/g, "/");
1267
+ const isReactRuntime = normalizedId.includes("/node_modules/react/") || normalizedId.includes("/node_modules/react-dom/");
1268
+ if (!isReactRuntime || !code.includes(nodeEnvExpression)) return null;
1269
+ return { code: code.split(nodeEnvExpression).join('"production"'), map: null };
1270
+ }
1271
+ };
1272
+ }
1273
+ __name(createReactProductionModePlugin, "createReactProductionModePlugin");
1258
1274
  function createMetadataImageWasmPlugin() {
1259
1275
  return {
1260
1276
  name: "farm-metadata-image-wasm",
@@ -1349,42 +1365,75 @@ async function minifyFarmServerChunk(code, fileName, options) {
1349
1365
  }
1350
1366
  __name(minifyFarmServerChunk, "minifyFarmServerChunk");
1351
1367
  function restoreLiteralModuleSpecifiers(code, fileName, { parseSync, Visitor }) {
1352
- if (!code.includes("import(`") && !code.includes("require(`")) return code;
1368
+ const hasImportTemplate = code.includes("import(`");
1369
+ const hasRequireTemplate = code.includes("require(`");
1370
+ if (!hasImportTemplate && !hasRequireTemplate) return code;
1353
1371
  const parsed = parseSync(fileName, code, { lang: "js", sourceType: "module" });
1354
1372
  if (parsed.errors.length > 0) {
1355
1373
  throw new Error(`Failed to minify ${fileName}: ${parsed.errors[0].message}`);
1356
1374
  }
1357
1375
  const replacements = [];
1358
- const restore = /* @__PURE__ */ __name((node) => {
1359
- if (node?.type !== "TemplateLiteral") return;
1360
- const template = node;
1361
- const value = template.quasis[0]?.value.cooked;
1362
- if (template.expressions.length > 0 || typeof value !== "string") return;
1363
- const text = JSON.stringify(value).replace(
1364
- /[\u007f-￿]/g,
1365
- (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`
1366
- );
1367
- replacements.push({ start: template.start, end: template.end, text });
1368
- }, "restore");
1369
- new Visitor({
1370
- ImportExpression(node) {
1371
- restore(node.source);
1372
- },
1373
- CallExpression(node) {
1374
- if (node.callee.type === "Identifier" && node.callee.name === "require" && node.arguments.length === 1) {
1375
- restore(node.arguments[0]);
1376
+ let needsAst = hasRequireTemplate;
1377
+ if (!needsAst) {
1378
+ const seenStarts = /* @__PURE__ */ new Set();
1379
+ for (const { moduleRequest } of parsed.module.dynamicImports) {
1380
+ if (seenStarts.has(moduleRequest.start)) continue;
1381
+ seenStarts.add(moduleRequest.start);
1382
+ const source = code.slice(moduleRequest.start, moduleRequest.end);
1383
+ if (!source.startsWith("`")) continue;
1384
+ const value = source.slice(1, -1);
1385
+ if (source.length < 2 || !source.endsWith("`") || /[\\\r\n`]|\$\{/.test(value)) {
1386
+ needsAst = true;
1387
+ break;
1376
1388
  }
1389
+ replacements.push({
1390
+ start: moduleRequest.start,
1391
+ end: moduleRequest.end,
1392
+ text: toAsciiStringLiteral(value)
1393
+ });
1377
1394
  }
1378
- }).visit(parsed.program);
1395
+ }
1396
+ if (needsAst) {
1397
+ replacements.length = 0;
1398
+ const restore = /* @__PURE__ */ __name((node) => {
1399
+ if (node?.type !== "TemplateLiteral") return;
1400
+ const template = node;
1401
+ const value = template.quasis[0]?.value.cooked;
1402
+ if (template.expressions.length > 0 || typeof value !== "string") return;
1403
+ replacements.push({
1404
+ start: template.start,
1405
+ end: template.end,
1406
+ text: toAsciiStringLiteral(value)
1407
+ });
1408
+ }, "restore");
1409
+ new Visitor({
1410
+ ImportExpression(node) {
1411
+ restore(node.source);
1412
+ },
1413
+ CallExpression(node) {
1414
+ if (node.callee.type === "Identifier" && node.callee.name === "require" && node.arguments.length === 1) {
1415
+ restore(node.arguments[0]);
1416
+ }
1417
+ }
1418
+ }).visit(parsed.program);
1419
+ }
1379
1420
  let restored = "";
1380
1421
  let offset = 0;
1381
1422
  for (const { start, end, text } of replacements.sort((a, b) => a.start - b.start)) {
1423
+ if (start < offset) continue;
1382
1424
  restored += code.slice(offset, start) + text;
1383
1425
  offset = end;
1384
1426
  }
1385
1427
  return restored + code.slice(offset);
1386
1428
  }
1387
1429
  __name(restoreLiteralModuleSpecifiers, "restoreLiteralModuleSpecifiers");
1430
+ function toAsciiStringLiteral(value) {
1431
+ return JSON.stringify(value).replace(
1432
+ /[\u007f-￿]/g,
1433
+ (character) => `\\u${character.charCodeAt(0).toString(16).padStart(4, "0")}`
1434
+ );
1435
+ }
1436
+ __name(toAsciiStringLiteral, "toAsciiStringLiteral");
1388
1437
  function createServerChunkMinifyPlugin(options) {
1389
1438
  return {
1390
1439
  name: "farm-server-chunk-minify",
@@ -4713,20 +4762,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
4713
4762
  ...rendererVitePlugins,
4714
4763
  ...config.vite.plugins || [],
4715
4764
  ...!useExternalMetadataImageRuntime && metadataImageRoutes.some((image) => image.sourceType === "module") ? [createMetadataImageWasmPlugin()] : [],
4716
- {
4717
- name: "farm-react-production-mode",
4718
- enforce: "pre",
4719
- transform(code, id) {
4720
- const normalizedId = id.replace(/\\/g, "/");
4721
- const isReactRuntime = normalizedId.includes("/node_modules/react/") || normalizedId.includes("/node_modules/react-dom/");
4722
- const nodeEnvExpression = ["process", "env", "NODE_ENV"].join(".");
4723
- if (!isReactRuntime || !code.includes(nodeEnvExpression)) return null;
4724
- return {
4725
- code: code.split(nodeEnvExpression).join('"production"'),
4726
- map: null
4727
- };
4728
- }
4729
- },
4765
+ { ...createReactProductionModePlugin(), enforce: "pre" },
4730
4766
  farmServerOnlyEntriesPlugin(),
4731
4767
  farmPlugin({ ...config, isolatedClientBoundaryModules }, pluginManager),
4732
4768
  farmEnvironmentFunctionsPlugin(),
@@ -4857,44 +4893,6 @@ function generateNativeAuthIntegrationSource(auth) {
4857
4893
  };
4858
4894
  }
4859
4895
  __name(generateNativeAuthIntegrationSource, "generateNativeAuthIntegrationSource");
4860
- function generateNativeMCPPluginSource(mcp) {
4861
- if (mcp?.enabled !== true) {
4862
- return { importSource: "", registerSource: "", pluginSource: "" };
4863
- }
4864
- return {
4865
- importSource: `import { createFarmMCPPlugin as __farmCreateMCPPlugin } from "@farm.js/mcp/internal";`,
4866
- registerSource: `function __farmMCPIsPlainObject(value) {
4867
- if (!value || typeof value !== "object" || Array.isArray(value)) return false;
4868
- const prototype = Object.getPrototypeOf(value);
4869
- return prototype === Object.prototype || prototype === null;
4870
- }
4871
-
4872
- function __farmMergeMCPConfig(base, override) {
4873
- const output = { ...base };
4874
- for (const [key, value] of Object.entries(override)) {
4875
- if (value === undefined) continue;
4876
- output[key] =
4877
- __farmMCPIsPlainObject(output[key]) && __farmMCPIsPlainObject(value)
4878
- ? __farmMergeMCPConfig(output[key], value)
4879
- : value;
4880
- }
4881
- return output;
4882
- }
4883
-
4884
- const farmNativeMCPConfig = farmRuntimeConfigs.reduce((merged, runtimeConfig) => {
4885
- const value = runtimeConfig?.mcp;
4886
- if (value === undefined) return merged;
4887
- if (value === false) return false;
4888
- return __farmMergeMCPConfig(merged && merged !== false ? merged : {}, value);
4889
- }, undefined);
4890
- const farmNativeMCPPlugin =
4891
- farmNativeMCPConfig && farmNativeMCPConfig.enabled !== false
4892
- ? __farmCreateMCPPlugin(farmNativeMCPConfig)
4893
- : null;`,
4894
- pluginSource: `...(farmNativeMCPPlugin ? [farmNativeMCPPlugin] : []),`
4895
- };
4896
- }
4897
- __name(generateNativeMCPPluginSource, "generateNativeMCPPluginSource");
4898
4896
  function generateIntegrationRequestRuntimeSource(options) {
4899
4897
  return `
4900
4898
  function matchLocalIntegrationRequest(request) {
@@ -4945,6 +4943,24 @@ function getConfiguredSetCookieHeaders(headers) {
4945
4943
  return value ? [value] : [];
4946
4944
  }
4947
4945
 
4946
+ // Owned responses (core's owned-response.ts, RFC 0002) are edited in place.
4947
+ function farmEditableHeaders(response) {
4948
+ return (globalThis[Symbol.for("farm.ownedResponses")] ??= new WeakSet()).has(response)
4949
+ ? response.headers
4950
+ : new Headers(response.headers);
4951
+ }
4952
+
4953
+ function farmResponseWithHeaders(response, headers) {
4954
+ if (!headers || headers === response.headers) return response;
4955
+ const output = new Response(response.body, {
4956
+ status: response.status,
4957
+ statusText: response.statusText,
4958
+ headers,
4959
+ });
4960
+ (globalThis[Symbol.for("farm.ownedResponses")] ??= new WeakSet()).add(output);
4961
+ return output;
4962
+ }
4963
+
4948
4964
  function applyConfiguredResponseHeaders(response, pathname) {
4949
4965
  if (preparedHeaderRoutes.length === 0) return response;
4950
4966
  const pathnameSegments = splitRuntimePath(pathname).map(decodeRouteSegment);
@@ -4959,12 +4975,12 @@ function applyConfiguredResponseHeaders(response, pathname) {
4959
4975
  const normalizedKey = header.key.toLowerCase();
4960
4976
  if (normalizedKey === "set-cookie") {
4961
4977
  if (getConfiguredSetCookieHeaders(currentHeaders).includes(header.value)) continue;
4962
- if (!headers) headers = new Headers(response.headers);
4978
+ if (!headers) headers = farmEditableHeaders(response);
4963
4979
  headers.append("Set-Cookie", header.value);
4964
4980
  } else {
4965
4981
  const currentValue = currentHeaders.get(header.key);
4966
4982
  if (currentValue === header.value) continue;
4967
- if (!headers) headers = new Headers(response.headers);
4983
+ if (!headers) headers = farmEditableHeaders(response);
4968
4984
  if (normalizedKey === "link") {
4969
4985
  appendFarmLinkHeader(headers, header.value);
4970
4986
  } else {
@@ -4974,12 +4990,7 @@ function applyConfiguredResponseHeaders(response, pathname) {
4974
4990
  }
4975
4991
  }
4976
4992
 
4977
- if (!headers) return response;
4978
- return new Response(response.body, {
4979
- status: response.status,
4980
- statusText: response.statusText,
4981
- headers,
4982
- });
4993
+ return farmResponseWithHeaders(response, headers);
4983
4994
  }
4984
4995
  `.trim();
4985
4996
  }
@@ -5006,7 +5017,73 @@ function generatePreloadResponseRuntimeSource() {
5006
5017
  // reading it, so a changed body is never paired with a stale string.
5007
5018
  const farmBufferedHtml = new WeakMap();
5008
5019
 
5020
+ // A Response Farm creates for this request. Later header steps edit it in place
5021
+ // instead of copying it; responses from app code are never owned. Same registry
5022
+ // as core's owned-response.ts (RFC 0002).
5023
+ function farmOwnedResponse(body, init) {
5024
+ const response = new Response(body, init);
5025
+ (globalThis[Symbol.for("farm.ownedResponses")] ??= new WeakSet()).add(response);
5026
+ return response;
5027
+ }
5028
+
5029
+ // node-server registers srvx's FastResponse here (RFC 0002). A buffered page
5030
+ // built with it keeps its HTML as a string until something reads the body, and
5031
+ // srvx writes that string in one call; other runtimes keep the Web Response.
5032
+ let FarmNodePageResponse;
5033
+ function useFarmNodePageResponse(ResponseClass) {
5034
+ FarmNodePageResponse = ResponseClass;
5035
+ }
5036
+
5037
+ // HTML of buffered pages built as FarmNodePageResponse, keyed by the response:
5038
+ // its body always comes from this string, and looking it up never creates the
5039
+ // Web body the way reading response.body would.
5040
+ const farmLazyBufferedHtml = new WeakMap();
5041
+
5042
+ function hasFarmContentType(headers) {
5043
+ if (!headers) return false;
5044
+ if (typeof headers.has === "function") return headers.has("content-type");
5045
+ for (const key in headers) if (key.toLowerCase() === "content-type") return true;
5046
+ return false;
5047
+ }
5048
+
5049
+ // A complete HTML document Farm built for this request. Same contract as
5050
+ // farmOwnedResponse followed by rememberFarmBufferedHtml.
5051
+ function farmOwnedHtmlResponse(html, init) {
5052
+ if (
5053
+ !FarmNodePageResponse ||
5054
+ typeof html !== "string" ||
5055
+ html.charCodeAt(0) === 0xfeff ||
5056
+ !hasFarmContentType(init?.headers)
5057
+ ) {
5058
+ return rememberFarmBufferedHtml(farmOwnedResponse(html, init), html);
5059
+ }
5060
+ const response = new FarmNodePageResponse(html, init);
5061
+ (globalThis[Symbol.for("farm.ownedResponses")] ??= new WeakSet()).add(response);
5062
+ farmLazyBufferedHtml.set(response, html);
5063
+ return response;
5064
+ }
5065
+
5066
+ function isFarmOwnedResponseForPreload(response) {
5067
+ return (globalThis[Symbol.for("farm.ownedResponses")] ??= new WeakSet()).has(response);
5068
+ }
5069
+
5070
+ // Header-only rebuild: in place for an owned response, otherwise a copy Farm owns.
5071
+ function farmPreloadHeadersResponse(response, edit) {
5072
+ if (isFarmOwnedResponseForPreload(response)) {
5073
+ edit(response.headers);
5074
+ return response;
5075
+ }
5076
+ const headers = new Headers(response.headers);
5077
+ edit(headers);
5078
+ return farmOwnedResponse(response.body, {
5079
+ status: response.status,
5080
+ statusText: response.statusText,
5081
+ headers,
5082
+ });
5083
+ }
5084
+
5009
5085
  function rememberFarmBufferedHtml(response, html) {
5086
+ if (farmLazyBufferedHtml.has(response)) return response;
5010
5087
  // Response.text() strips a leading BOM, so a BOM document keeps reading its
5011
5088
  // body to preserve that behavior.
5012
5089
  if (typeof html === "string" && html.charCodeAt(0) !== 0xfeff && response.body) {
@@ -5016,6 +5093,8 @@ function rememberFarmBufferedHtml(response, html) {
5016
5093
  }
5017
5094
 
5018
5095
  function getFarmOwnedBufferedHtml(response) {
5096
+ const lazyHtml = farmLazyBufferedHtml.get(response);
5097
+ if (lazyHtml !== undefined) return lazyHtml;
5019
5098
  const body = response.body;
5020
5099
  if (!body || response.bodyUsed || body.locked) return undefined;
5021
5100
  return farmBufferedHtml.get(body);
@@ -5038,17 +5117,14 @@ async function applyFarmPreloadBudget(response, pathname, request) {
5038
5117
  const isBuffered = bufferedMarker === "1" || hasNoHtmlPreloads;
5039
5118
  if (!isHtml) {
5040
5119
  if (!isStreaming && !isBuffered) return response;
5041
- headers = new Headers(headers);
5042
- headers.delete("x-farm-preload-streaming");
5043
- headers.delete("x-farm-preload-buffered");
5044
- return new Response(response.body, {
5045
- status: response.status,
5046
- statusText: response.statusText,
5047
- headers,
5120
+ return farmPreloadHeadersResponse(response, (editable) => {
5121
+ editable.delete("x-farm-preload-streaming");
5122
+ editable.delete("x-farm-preload-buffered");
5048
5123
  });
5049
5124
  }
5050
5125
 
5051
- if (isStreaming || !isBuffered || response.body === null) {
5126
+ const isLazyPage = farmLazyBufferedHtml.has(response);
5127
+ if (isStreaming || !isBuffered || (!isLazyPage && response.body === null)) {
5052
5128
  const managed = manageFarmLinkHeaderPreloads(linkHeader, farmPreloadConfig);
5053
5129
  reportFarmPreloadWarnings(managed.warnings, managed.warnings.length > 0
5054
5130
  ? "route " + (pathname ?? getFarmRoutePathname(new URL(request.url).pathname))
@@ -5058,42 +5134,43 @@ async function applyFarmPreloadBudget(response, pathname, request) {
5058
5134
  if (!headers.has("x-farm-preload-streaming") &&
5059
5135
  !headers.has("x-farm-preload-buffered") &&
5060
5136
  (managed.value || null) === headers.get("Link")) return response;
5061
- headers = new Headers(headers);
5062
- headers.delete("x-farm-preload-streaming");
5063
- headers.delete("x-farm-preload-buffered");
5064
- if (managed.value) headers.set("Link", managed.value);
5065
- else headers.delete("Link");
5066
- return new Response(response.body, {
5067
- status: response.status,
5068
- statusText: response.statusText,
5069
- headers,
5137
+ return farmPreloadHeadersResponse(response, (editable) => {
5138
+ editable.delete("x-farm-preload-streaming");
5139
+ editable.delete("x-farm-preload-buffered");
5140
+ if (managed.value) editable.set("Link", managed.value);
5141
+ else editable.delete("Link");
5070
5142
  });
5071
5143
  }
5072
5144
 
5073
5145
  // Keep the document/header budget semantics (including unchanged Link
5074
5146
  // formatting), but do not consume and re-encode a proven preload-free body.
5075
- headers = new Headers(headers);
5076
- headers.delete("x-farm-preload-streaming");
5077
- headers.delete("x-farm-preload-buffered");
5078
5147
  const ownedHtml = hasNoHtmlPreloads ? undefined : getFarmOwnedBufferedHtml(response);
5079
5148
  const html = hasNoHtmlPreloads ? "" : (ownedHtml ?? await response.text());
5080
5149
  const managed = manageFarmDocumentPreloads(html, linkHeader, farmPreloadConfig);
5081
5150
  // A remembered document the budget left unchanged keeps its unread body.
5082
5151
  const reusesBody = hasNoHtmlPreloads || (ownedHtml !== undefined && managed.html === html);
5083
- if (ownedHtml !== undefined && !reusesBody) response.body?.cancel().catch(() => {});
5084
- if (managed.linkHeader) headers.set("Link", managed.linkHeader);
5085
- else headers.delete("Link");
5086
- if (managed.html !== html) headers.delete("Content-Length");
5152
+ if (ownedHtml !== undefined && !reusesBody && !isLazyPage) {
5153
+ response.body?.cancel().catch(() => {});
5154
+ }
5087
5155
  reportFarmPreloadWarnings(managed.warnings, managed.warnings.length > 0
5088
5156
  ? "route " + (pathname ?? getFarmRoutePathname(new URL(request.url).pathname))
5089
5157
  : undefined);
5090
- const output = new Response(reusesBody ? response.body : managed.html, {
5158
+ const applyBudgetHeaders = (editable) => {
5159
+ editable.delete("x-farm-preload-streaming");
5160
+ editable.delete("x-farm-preload-buffered");
5161
+ if (managed.linkHeader) editable.set("Link", managed.linkHeader);
5162
+ else editable.delete("Link");
5163
+ if (managed.html !== html) editable.delete("Content-Length");
5164
+ };
5165
+ if (reusesBody) return farmPreloadHeadersResponse(response, applyBudgetHeaders);
5166
+ const rebuiltHeaders = new Headers(headers);
5167
+ applyBudgetHeaders(rebuiltHeaders);
5168
+ // A rebuilt document is still a complete string Farm holds.
5169
+ return farmOwnedHtmlResponse(managed.html, {
5091
5170
  status: response.status,
5092
5171
  statusText: response.statusText,
5093
- headers,
5172
+ headers: rebuiltHeaders,
5094
5173
  });
5095
- // A rebuilt document is still a complete string Farm holds.
5096
- return reusesBody ? output : rememberFarmBufferedHtml(output, managed.html);
5097
5174
  }
5098
5175
  `.trim();
5099
5176
  }
@@ -5126,6 +5203,7 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlot
5126
5203
  config.renderer,
5127
5204
  getFarmPresetRuntime(preset)
5128
5205
  );
5206
+ const hasReactSyncRender = isReactRenderer(config.renderer);
5129
5207
  const rendererServerImports = isReactRenderer(config.renderer) ? `import * as React from "react";
5130
5208
  import * as ReactDOMServer from "react-dom/server";${hasIsolatedClientGraphRuntime ? '\nimport { isolateFarmServerPageGraph, wrapFarmIsolatedClientGraph } from "@farm.js/core/internal/isolated-boundary";' : ""}` : `import React, * as ReactDOMServer from ${JSON.stringify(config.renderer.server)};${generateFarmRendererAppSetupServerCode(config)}`;
5131
5209
  const routeClientGraphServerRuntime = hasIsolatedClientGraphRuntime ? `function wrapFarmRouteClientGraph(element) {
@@ -5651,6 +5729,7 @@ ${routeClientGraphServerRuntime}
5651
5729
 
5652
5730
  const farmRendererStreamingCapabilities = ${JSON.stringify(rendererStreamingCapabilities)};
5653
5731
  const farmRendererName = ${JSON.stringify(config.renderer.name)};
5732
+ const farmReactSyncRender = ${JSON.stringify(hasReactSyncRender)};
5654
5733
  const farmRendererRuntime = ${JSON.stringify(getFarmPresetRuntime(preset))};
5655
5734
  const farmRendererRuntimeStreamingCapabilities = ReactDOMServer.capabilities &&
5656
5735
  ReactDOMServer.capabilities.streaming;
@@ -6050,8 +6129,10 @@ function escapeFarmHtmlAttribute(value) {
6050
6129
  .replace(/>/g, "&gt;");
6051
6130
  }
6052
6131
 
6053
- async function renderFarmElement(ReactDOMServer, element) {
6132
+ async function renderFarmElement(ReactDOMServer, element, routeKey) {
6054
6133
  const streamErrors = [];
6134
+ const syncHtml = renderFarmElementSync(ReactDOMServer, element, routeKey);
6135
+ if (syncHtml !== undefined) return { html: syncHtml, shellHtml: syncHtml, streamErrors };
6055
6136
  let encoder;
6056
6137
  const normalizeChunk = function(chunk) {
6057
6138
  if (typeof chunk === "string") return (encoder ||= new TextEncoder()).encode(chunk);
@@ -6122,6 +6203,7 @@ async function renderFarmElement(ReactDOMServer, element) {
6122
6203
  }
6123
6204
  throwStreamError();
6124
6205
  const html = decodeChunks(chunks);
6206
+ rememberFarmSyncRender(routeKey, html);
6125
6207
  return { html, shellHtml: html, streamErrors };
6126
6208
  }
6127
6209
 
@@ -6253,6 +6335,7 @@ async function renderFarmElement(ReactDOMServer, element) {
6253
6335
  await complete;
6254
6336
  throwStreamError();
6255
6337
  const html = decodeChunks(chunks);
6338
+ rememberFarmSyncRender(routeKey, html);
6256
6339
  return { html, shellHtml: html, streamErrors };
6257
6340
  }
6258
6341
 
@@ -6293,6 +6376,45 @@ async function renderFarmElementToString(ReactDOMServer, element) {
6293
6376
  return html;
6294
6377
  }
6295
6378
 
6379
+ // Most pages render without suspending. React's renderToString renders them in
6380
+ // one synchronous pass with string output, about half the CPU of the streaming
6381
+ // renderer, and the same markup, except that React 19's streaming renderer adds
6382
+ // a doctype before an <html> root. A route uses it only after a streaming render
6383
+ // of that route finished without suspending (remembering whether that render
6384
+ // started with a doctype), so a page that suspends keeps streaming and renders
6385
+ // once. If such a route suspends later (a Suspense boundary left to the
6386
+ // client, marked <!--$!-->, or a throw), that request renders again through the
6387
+ // streaming path and the route streams for the rest of this server process.
6388
+ const farmSyncRenderRoutes = new Map();
6389
+ const farmStreamingRenderRoutes = new Set();
6390
+ const FARM_STREAMED_DOCTYPE = "<!DOCTYPE html>";
6391
+
6392
+ function rememberFarmSyncRender(routeKey, streamedHtml) {
6393
+ if (!farmReactSyncRender || routeKey === undefined) return;
6394
+ if (!farmStreamingRenderRoutes.has(routeKey)) {
6395
+ farmSyncRenderRoutes.set(routeKey, streamedHtml.startsWith(FARM_STREAMED_DOCTYPE));
6396
+ }
6397
+ }
6398
+
6399
+ function renderFarmElementSync(ReactDOMServer, element, routeKey) {
6400
+ if (!farmReactSyncRender) return undefined;
6401
+ const addsDoctype = routeKey === undefined ? undefined : farmSyncRenderRoutes.get(routeKey);
6402
+ if (addsDoctype === undefined) return undefined;
6403
+ if (typeof ReactDOMServer.renderToString !== "function") return undefined;
6404
+ let html;
6405
+ try {
6406
+ html = ReactDOMServer.renderToString(element);
6407
+ } catch {
6408
+ html = undefined;
6409
+ }
6410
+ if (typeof html !== "string" || html.includes("<!--$!-->")) {
6411
+ farmSyncRenderRoutes.delete(routeKey);
6412
+ farmStreamingRenderRoutes.add(routeKey);
6413
+ return undefined;
6414
+ }
6415
+ return addsDoctype && html.startsWith("<html") ? FARM_STREAMED_DOCTYPE + html : html;
6416
+ }
6417
+
6296
6418
  function createFarmDocumentStream(contentStream, prefix, suffix, onComplete) {
6297
6419
  const encoder = new TextEncoder();
6298
6420
  const reader = contentStream.getReader();
@@ -7422,7 +7544,7 @@ async function handleFarmRequestInContext(
7422
7544
  url.search,
7423
7545
  );
7424
7546
  if (redirectMatch) {
7425
- return new Response("Redirecting to " + redirectMatch.destination, {
7547
+ return farmOwnedResponse("Redirecting to " + redirectMatch.destination, {
7426
7548
  status: redirectMatch.statusCode,
7427
7549
  headers: { Location: redirectMatch.destination },
7428
7550
  });
@@ -7526,7 +7648,7 @@ async function handleFarmRequestInContext(
7526
7648
  );
7527
7649
  const docsHeaders = new Headers(wrappedDocsResponse.headers);
7528
7650
  docsHeaders.set("x-farm-preload-buffered", "1");
7529
- return applyProductionMiddlewareHeaders(new Response(wrappedDocsResponse.body, {
7651
+ return applyProductionMiddlewareHeaders(farmOwnedResponse(wrappedDocsResponse.body, {
7530
7652
  status: wrappedDocsResponse.status,
7531
7653
  statusText: wrappedDocsResponse.statusText,
7532
7654
  headers: docsHeaders,
@@ -7572,9 +7694,13 @@ async function handleFarmRequestInContext(
7572
7694
  ` : ""}
7573
7695
 
7574
7696
  ${apiRoutes.length > 0 ? `
7575
- const apiResponse = await handleAPIRequest(cloneFarmRequestWithContext(request));
7576
- if (apiResponse) {
7577
- return applyProductionMiddlewareHeaders(apiResponse, middlewareHeaders);
7697
+ // Clone only for an API request: cloning a Request costs more than the match,
7698
+ // and handleAPIRequest returns null without one.
7699
+ if (matchLocalAPIRequest(request)) {
7700
+ const apiResponse = await handleAPIRequest(cloneFarmRequestWithContext(request));
7701
+ if (apiResponse) {
7702
+ return applyProductionMiddlewareHeaders(apiResponse, middlewareHeaders);
7703
+ }
7578
7704
  }
7579
7705
  ` : ""}
7580
7706
 
@@ -7585,7 +7711,7 @@ async function handleFarmRequestInContext(
7585
7711
  ) {
7586
7712
  const method = request.method.toUpperCase();
7587
7713
  const specResponse = method === "GET" || method === "HEAD"
7588
- ? new Response(method === "HEAD" ? null : JSON.stringify(farmOpenAPIReference.spec), {
7714
+ ? farmOwnedResponse(method === "HEAD" ? null : JSON.stringify(farmOpenAPIReference.spec), {
7589
7715
  status: 200,
7590
7716
  headers: {
7591
7717
  "Content-Type": "application/json; charset=utf-8",
@@ -7593,7 +7719,7 @@ async function handleFarmRequestInContext(
7593
7719
  "X-Content-Type-Options": "nosniff",
7594
7720
  },
7595
7721
  })
7596
- : new Response("Method Not Allowed", {
7722
+ : farmOwnedResponse("Method Not Allowed", {
7597
7723
  status: 405,
7598
7724
  headers: {
7599
7725
  "Allow": "GET, HEAD",
@@ -7609,7 +7735,7 @@ async function handleFarmRequestInContext(
7609
7735
  ) {
7610
7736
  const method = request.method.toUpperCase();
7611
7737
  const response = method === "GET" || method === "HEAD"
7612
- ? new Response(method === "HEAD" ? null : farmOpenAPIReference.html, {
7738
+ ? farmOwnedResponse(method === "HEAD" ? null : farmOpenAPIReference.html, {
7613
7739
  status: 200,
7614
7740
  headers: {
7615
7741
  "Content-Type": "text/html; charset=utf-8",
@@ -7617,7 +7743,7 @@ async function handleFarmRequestInContext(
7617
7743
  "X-Content-Type-Options": "nosniff",
7618
7744
  },
7619
7745
  })
7620
- : new Response("Method Not Allowed", {
7746
+ : farmOwnedResponse("Method Not Allowed", {
7621
7747
  status: 405,
7622
7748
  headers: {
7623
7749
  "Allow": "GET, HEAD",
@@ -7712,7 +7838,7 @@ async function handleFarmRequestInContext(
7712
7838
 
7713
7839
  // Preserve the explicit JSON 404 for misses below the configured local API path.
7714
7840
  if (isFarmLocalAPIPathname(pathname)) {
7715
- return applyProductionMiddlewareHeaders(new Response(
7841
+ return applyProductionMiddlewareHeaders(farmOwnedResponse(
7716
7842
  JSON.stringify({ error: "API route not found", pathname }),
7717
7843
  { status: 404, headers: { "Content-Type": "application/json" } }
7718
7844
  ), middlewareHeaders);
@@ -7728,7 +7854,7 @@ async function handleFarmRequestInContext(
7728
7854
  );
7729
7855
  if (trailingSlashLocation) {
7730
7856
  return applyProductionMiddlewareHeaders(
7731
- new Response(null, {
7857
+ farmOwnedResponse(null, {
7732
7858
  status: 308,
7733
7859
  headers: { Location: trailingSlashLocation },
7734
7860
  }),
@@ -7771,7 +7897,7 @@ async function handleFarmRequestInContext(
7771
7897
  status: 200,
7772
7898
  durationMs: Date.now() - requestStartTime,
7773
7899
  });
7774
- return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(new Response(cachedPPRShell, {
7900
+ return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(farmOwnedHtmlResponse(cachedPPRShell, {
7775
7901
  status: 200,
7776
7902
  headers: {
7777
7903
  "Content-Type": "text/html; charset=utf-8",
@@ -8078,7 +8204,7 @@ async function handleFarmRequestInContext(
8078
8204
  }
8079
8205
 
8080
8206
  wrappedElement = wrapWithFarmIntegrationProviders(wrappedElement);
8081
- return renderFarmElement(ReactDOMServer, wrappedElement);
8207
+ return renderFarmElement(ReactDOMServer, wrappedElement, route.pattern);
8082
8208
  };
8083
8209
  const renderedPage = ${hasMiddlewareRuntime ? `await _runWithMiddlewareData(middlewareData, () =>
8084
8210
  _runWithMiddlewareContext(middlewareContext, renderPageElement)
@@ -8266,7 +8392,7 @@ async function handleFarmRequestInContext(
8266
8392
  });
8267
8393
  }
8268
8394
  );
8269
- return applyProductionMiddlewareHeaders(new Response(streamedDocument, {
8395
+ return applyProductionMiddlewareHeaders(farmOwnedResponse(streamedDocument, {
8270
8396
  status: pageStatus,
8271
8397
  headers: { ...responseHeaders, "x-farm-preload-streaming": "1" },
8272
8398
  }), middlewareHeaders);
@@ -8407,7 +8533,7 @@ async function handleFarmRequestInContext(
8407
8533
  durationMs: Date.now() - requestStartTime,
8408
8534
  });
8409
8535
 
8410
- return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(new Response(
8536
+ return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(farmOwnedHtmlResponse(
8411
8537
  fullHtml,
8412
8538
  {
8413
8539
  status: pageStatus,
@@ -8441,7 +8567,7 @@ async function handleFarmRequestInContext(
8441
8567
  status: redirect.status,
8442
8568
  durationMs: Date.now() - requestStartTime,
8443
8569
  });
8444
- return applyProductionMiddlewareHeaders(new Response(null, {
8570
+ return applyProductionMiddlewareHeaders(farmOwnedResponse(null, {
8445
8571
  status: redirect.status,
8446
8572
  headers: {
8447
8573
  Location: redirectUrl,
@@ -8509,7 +8635,7 @@ async function handleFarmRequestInContext(
8509
8635
  status: errorStatus,
8510
8636
  durationMs: Date.now() - requestStartTime,
8511
8637
  });
8512
- return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(new Response(errorDocument, {
8638
+ return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(farmOwnedHtmlResponse(errorDocument, {
8513
8639
  status: errorStatus,
8514
8640
  headers: {
8515
8641
  "Content-Type": "text/html; charset=utf-8",
@@ -8544,7 +8670,7 @@ async function handleFarmRequestInContext(
8544
8670
  farmResolvedRuntimeConfig.basePath,
8545
8671
  getFarmTheme(request)
8546
8672
  );
8547
- return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(new Response(
8673
+ return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(farmOwnedHtmlResponse(
8548
8674
  fallbackDocument,
8549
8675
  {
8550
8676
  status: errorStatus,
@@ -8575,7 +8701,7 @@ async function handleFarmRequestInContext(
8575
8701
  status: 404,
8576
8702
  durationMs: Date.now() - requestStartTime,
8577
8703
  });
8578
- return applyProductionMiddlewareHeaders(new Response(
8704
+ return applyProductionMiddlewareHeaders(farmOwnedResponse(
8579
8705
  createFarmMarkdownErrorBody(404, pathname, applyFarmBasePath("/", farmResolvedRuntimeConfig.basePath)),
8580
8706
  {
8581
8707
  status: 404,
@@ -8672,7 +8798,7 @@ async function handleFarmRequestInContext(
8672
8798
  durationMs: Date.now() - requestStartTime,
8673
8799
  });
8674
8800
 
8675
- return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(new Response(fullHtml, {
8801
+ return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(farmOwnedHtmlResponse(fullHtml, {
8676
8802
  status: 404,
8677
8803
  headers: {
8678
8804
  "Content-Type": "text/html; charset=utf-8",
@@ -8687,7 +8813,7 @@ async function handleFarmRequestInContext(
8687
8813
  farmResolvedRuntimeConfig.basePath,
8688
8814
  getFarmTheme(request)
8689
8815
  );
8690
- return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(new Response(
8816
+ return rememberFarmBufferedHtml(applyProductionMiddlewareHeaders(farmOwnedHtmlResponse(
8691
8817
  fallbackDocument,
8692
8818
  {
8693
8819
  status: 404,
@@ -8750,11 +8876,11 @@ async function handleFarmPluginRequest(request, runtimeOptions, adapterContext)
8750
8876
  headers.delete("content-length");
8751
8877
  headers.delete("x-farm-preload-streaming");
8752
8878
  headers.set("x-farm-preload-buffered", getFarmBufferedPreloadMarker(html));
8753
- return rememberFarmBufferedHtml(new Response(html, {
8879
+ return farmOwnedHtmlResponse(html, {
8754
8880
  status: response.status,
8755
8881
  statusText: response.statusText,
8756
8882
  headers,
8757
- }), html);
8883
+ });
8758
8884
  }
8759
8885
 
8760
8886
  ${generatePreloadResponseRuntimeSource()}
@@ -8831,7 +8957,7 @@ async function handleFarmFetch(request, context) {
8831
8957
  const response = await _runWithCurrentRequest(request, () =>
8832
8958
  _runWithAfterRequest(request, runRequest, context),
8833
8959
  );
8834
- return ${hasCompressionRuntime && !hasPluginRuntime ? "compressFarmResponse(request, response, getFarmOwnedBufferedHtml(response))" : "response"};
8960
+ return ${hasCompressionRuntime && !hasPluginRuntime ? "compressFarmResponse(request, response, getFarmOwnedBufferedHtml(response), farmLazyBufferedHtml.has(response))" : "response"};
8835
8961
  }),
8836
8962
  {
8837
8963
  onResponseFinished: typeof context?.onResponseFinished === "function"
@@ -8840,7 +8966,7 @@ async function handleFarmFetch(request, context) {
8840
8966
  },
8841
8967
  );
8842
8968
  }
8843
- export { createFarmNodeRequestAbortSignal };
8969
+ export { createFarmNodeRequestAbortSignal, getFarmOwnedBufferedHtml, useFarmNodePageResponse };
8844
8970
  export default { fetch, lifecycle: farmProductionLifecycle };
8845
8971
  `.trim();
8846
8972
  }
@@ -9081,12 +9207,16 @@ async function buildNitroUniversal(config, routeManager, apiRouteManager, server
9081
9207
  await fs.writeFile(filePath, output.type === "chunk" ? output.code : output.source);
9082
9208
  }
9083
9209
  const nitroEntryPath = path5.join(ssrOutputDir, "nitro-entry.mjs");
9210
+ const nodeAdapterModule = preset === "node-server" ? resolveNitroRuntimeDependency(root, "srvx/node") : null;
9084
9211
  const nitroEntryCode = `
9085
9212
  // Farm.js Nitro Entry
9086
9213
  // This file adapts Farm's Web fetch handler to Nitro's event handler contract.
9087
9214
 
9088
9215
  import { useNitroHooks } from '#nitro/runtime/app'
9089
- import handler, { farmProductionLifecycle, createFarmNodeRequestAbortSignal } from './${ssrEntryFile}'
9216
+ import handler, { farmProductionLifecycle, createFarmNodeRequestAbortSignal${nodeAdapterModule ? ", getFarmOwnedBufferedHtml, useFarmNodePageResponse" : ""} } from './${ssrEntryFile}'${nodeAdapterModule ? `
9217
+ import { FastResponse } from ${JSON.stringify(nodeAdapterModule)}
9218
+
9219
+ useFarmNodePageResponse(FastResponse)` : ""}
9090
9220
 
9091
9221
  export { farmProductionLifecycle }
9092
9222
 
@@ -9182,11 +9312,14 @@ function resolveTrustedLocalOrigin(event, request) {
9182
9312
  // Export the event handler for Nitro
9183
9313
  export default async function farmNitroEventHandler(event) {
9184
9314
  registerFarmCloseHook()
9185
- // Some Node adapters abort their Request on normal IncomingMessage close.
9186
- // Use the same actual disconnect events as Farm's development server.
9315
+ // Abort only on an actual disconnect, the same events as Farm's development
9316
+ // server. srvx's Request already aborts that way (on a response close before
9317
+ // it ended, never on a normal IncomingMessage close), so use it as is. Other
9318
+ // Node adapters get a copy with Farm's own signal.
9187
9319
  const node = event.node
9188
- const webRequest = event.req?._request || event.req
9189
- const request = node?.req && node?.res
9320
+ const srvxRequest = event.req?._request
9321
+ const webRequest = srvxRequest || event.req
9322
+ const request = !srvxRequest && node?.req && node?.res
9190
9323
  ? new Request(webRequest, { signal: createFarmNodeRequestAbortSignal(node.req, node.res) })
9191
9324
  : webRequest
9192
9325
  const response = await handler.fetch(request, {
@@ -9197,20 +9330,42 @@ export default async function farmNitroEventHandler(event) {
9197
9330
  })
9198
9331
 
9199
9332
  // Nitro records asset compression negotiation on the event response. Merge
9200
- // Farm's cache signals there so H3 preserves both sets of Vary values.
9333
+ // Farm's cache signals there so H3 preserves both sets of Vary values. Only
9334
+ // merge into a Vary Nitro set: any event header makes H3 rebuild the response
9335
+ // around its body, and without one H3 keeps Farm's Vary as it is.
9201
9336
  const farmVary = response.headers.get('Vary')
9202
- if (farmVary) mergeVaryHeaders(event.res.headers, farmVary)
9203
- return response
9204
- }
9337
+ if (farmVary) {
9338
+ const nitroHeaders = event.res.headers
9339
+ if (nitroHeaders.has('Vary')) mergeVaryHeaders(nitroHeaders, farmVary)
9340
+ }
9341
+ return ${nodeAdapterModule ? "toFarmNodeResponse(request, response)" : "response"}
9342
+ }${nodeAdapterModule ? `
9343
+
9344
+ // A page Farm still holds as one complete string is sent in a single write with
9345
+ // Content-Length, instead of srvx reading the Web stream chunk by chunk. Any
9346
+ // step that read, cloned or replaced the body made a new stream, so its lookup
9347
+ // misses and that response is sent as it is.
9348
+ function toFarmNodeResponse(request, response) {
9349
+ if (request.method === 'HEAD') return response
9350
+ // Already a FastResponse (a buffered page built as one): srvx sends it as is.
9351
+ if (typeof response._toNodeResponse === 'function') return response
9352
+ const html = getFarmOwnedBufferedHtml(response)
9353
+ if (html === undefined) return response
9354
+ return new FastResponse(html, {
9355
+ status: response.status,
9356
+ statusText: response.statusText,
9357
+ headers: response.headers,
9358
+ })
9359
+ }` : ""}
9205
9360
  `.trim();
9206
9361
  await fs.writeFile(nitroEntryPath, nitroEntryCode);
9207
9362
  const farmNodeServerEntryPath = preset === "node-server" ? path5.join(ssrOutputDir, "farm-node-server-entry.mjs") : null;
9208
- if (farmNodeServerEntryPath) {
9363
+ if (farmNodeServerEntryPath && nodeAdapterModule) {
9209
9364
  await fs.writeFile(
9210
9365
  farmNodeServerEntryPath,
9211
9366
  createFarmNodeServerEntry({
9212
9367
  nitroEntryFile: path5.basename(nitroEntryPath),
9213
- nodeAdapterModule: resolveNitroRuntimeDependency(root, "srvx/node"),
9368
+ nodeAdapterModule,
9214
9369
  server: config.server,
9215
9370
  websocketAdapterModule: resolveNitroRuntimeDependency(root, "crossws/adapters/node")
9216
9371
  })
@@ -9301,7 +9456,13 @@ export default async function farmNitroEventHandler(event) {
9301
9456
  },
9302
9457
  rollupConfig: {
9303
9458
  external: nitroRollupExternal,
9304
- plugins: hasGeneratedMetadataImages && !useExternalMetadataImageRuntime ? [createMetadataImageWasmPlugin()] : []
9459
+ plugins: [
9460
+ ...hasGeneratedMetadataImages && !useExternalMetadataImageRuntime ? [createMetadataImageWasmPlugin()] : [],
9461
+ // Workers bundle React here rather than loading it from node_modules.
9462
+ // Unenv rewrites `process` before Nitro's own replacements run, so
9463
+ // React's development builds would otherwise ship next to production.
9464
+ ...isCloudflareWorker ? [createReactProductionModePlugin()] : []
9465
+ ]
9305
9466
  },
9306
9467
  // Keep the public Nitro boolean intact for build hooks. Farm replaces
9307
9468
  // Nitro's minifier only when no custom OXC transform was requested.