@farm.js/core 0.1.51-beta.1 → 0.1.51-beta.3

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 (187) hide show
  1. package/dist/{agent-mcp-handler-WWCC4GLG.mjs → agent-mcp-handler-7JSKGSOW.mjs} +4 -4
  2. package/dist/{agent-mcp-handler-RT3COCEL.cjs → agent-mcp-handler-YE4AGWHN.cjs} +5 -5
  3. package/dist/{agent-mcp.d-ZuuNgrkQ.d.ts → agent-mcp.d-BAR9njbJ.d.ts} +1 -1
  4. package/dist/{agent-mcp.d-BPzpKBHQ.d.mts → agent-mcp.d-BJKChOrO.d.mts} +1 -1
  5. package/dist/agent-runtime.d.mts +2 -2
  6. package/dist/agent-runtime.d.ts +2 -2
  7. package/dist/{analyzer-V74FTD2F.mjs → analyzer-OTPEWH5W.mjs} +82 -22
  8. package/dist/{analyzer-AZULAOPL.cjs → analyzer-TSQXZCZN.cjs} +82 -22
  9. package/dist/api/route-manager.d.mts +4 -4
  10. package/dist/api/route-manager.d.ts +4 -4
  11. package/dist/api/runtime.d.mts +3 -3
  12. package/dist/api/runtime.d.ts +3 -3
  13. package/dist/api.d.mts +4 -4
  14. package/dist/api.d.ts +4 -4
  15. package/dist/{app-manifest.d-HWhqbP2X.d.ts → app-manifest.d-BLFbaqx8.d.ts} +1 -1
  16. package/dist/{app-manifest.d-BJDcZn3W.d.mts → app-manifest.d-DEQNnNhV.d.mts} +1 -1
  17. package/dist/app-markdown.cjs +4 -4
  18. package/dist/app-markdown.d.mts +3 -3
  19. package/dist/app-markdown.d.ts +3 -3
  20. package/dist/app-markdown.mjs +3 -3
  21. package/dist/{app.d-B5EWdob-.d.ts → app.d-CNPaUYaa.d.ts} +4 -4
  22. package/dist/{app.d-DOGW1KI8.d.mts → app.d-CT7xDkj2.d.mts} +4 -4
  23. package/dist/{chunk-YVJV7Q2X.mjs → chunk-3BLEXUGE.mjs} +1 -1
  24. package/dist/chunk-3HLQTOVU.cjs +6 -0
  25. package/dist/{chunk-KU7MBLDZ.mjs → chunk-3SMXQNFD.mjs} +2 -2
  26. package/dist/{chunk-EWC76P5D.mjs → chunk-3TLKUBXG.mjs} +411 -166
  27. package/dist/{chunk-BSEKNTMS.mjs → chunk-3WSIYBMG.mjs} +41 -19
  28. package/dist/{chunk-JVBNKWLV.cjs → chunk-3XQ57EFI.cjs} +16 -16
  29. package/dist/{chunk-GW4YUIHW.mjs → chunk-3Z63X45F.mjs} +1 -1
  30. package/dist/{chunk-SCRC5AMM.mjs → chunk-4353ZRE5.mjs} +1 -1
  31. package/dist/{chunk-SBABQKPT.cjs → chunk-46KXZBOG.cjs} +5 -5
  32. package/dist/{chunk-2756SHCI.cjs → chunk-4CGOMZP3.cjs} +221 -199
  33. package/dist/{chunk-RMPNYUGT.cjs → chunk-4SEHMBL4.cjs} +10 -10
  34. package/dist/{chunk-QAIAFCRZ.mjs → chunk-4TKAIO2F.mjs} +3 -3
  35. package/dist/{chunk-NJLTQR5P.cjs → chunk-53SACYBY.cjs} +7 -7
  36. package/dist/{chunk-I3EFXUAR.mjs → chunk-5FXPJIOT.mjs} +2 -2
  37. package/dist/chunk-7FVVQEVL.cjs +541 -0
  38. package/dist/{chunk-IFBINMI4.cjs → chunk-7MD37P5V.cjs} +5 -5
  39. package/dist/{chunk-JATTVAHA.cjs → chunk-B3GM5I6N.cjs} +4 -4
  40. package/dist/chunk-C3BOESDW.mjs +541 -0
  41. package/dist/{chunk-WJFFPXCV.cjs → chunk-D7UQC4O6.cjs} +10 -8
  42. package/dist/{chunk-KRRZJ77U.mjs → chunk-E5DXF5GE.mjs} +2 -2
  43. package/dist/chunk-EFZGB7JC.cjs +114 -0
  44. package/dist/{chunk-NYQSC4DX.cjs → chunk-FFE2EMF7.cjs} +5 -5
  45. package/dist/{chunk-KMB5BMFX.mjs → chunk-GAO7MS5O.mjs} +9 -3
  46. package/dist/{chunk-HIFIG6HL.cjs → chunk-H33PJEXN.cjs} +5 -5
  47. package/dist/{chunk-TT66CP3P.cjs → chunk-HLWOWZFX.cjs} +2 -2
  48. package/dist/chunk-JXEPLIXD.cjs +389 -0
  49. package/dist/{chunk-VITZRYW3.cjs → chunk-KLNFJDFM.cjs} +15 -9
  50. package/dist/{chunk-KBRZAYI7.mjs → chunk-LDNSSHEG.mjs} +2 -2
  51. package/dist/chunk-LLZSYFOM.mjs +6 -0
  52. package/dist/{chunk-N77JBPLC.mjs → chunk-LPDKG6RA.mjs} +9 -7
  53. package/dist/chunk-MIGFX2FN.mjs +758 -0
  54. package/dist/{chunk-72D2NX77.mjs → chunk-MRNDAJEV.mjs} +1 -1
  55. package/dist/{chunk-5A5GAZ75.mjs → chunk-NOTSHVRN.mjs} +406 -234
  56. package/dist/{chunk-AGR7VJH2.cjs → chunk-OAMUSC7H.cjs} +1 -1
  57. package/dist/chunk-P6MHI5O2.cjs +758 -0
  58. package/dist/{chunk-EOSFVNXZ.mjs → chunk-R4PHCZ3G.mjs} +1 -1
  59. package/dist/{chunk-X2M4XN4V.cjs → chunk-TQMI6BYO.cjs} +675 -430
  60. package/dist/{chunk-76GHW3WI.cjs → chunk-TSFMFPRU.cjs} +4 -4
  61. package/dist/{chunk-5N7QAGEA.cjs → chunk-TWHXBMLJ.cjs} +37 -7
  62. package/dist/{chunk-BOVGCUDZ.cjs → chunk-UJL4JOXV.cjs} +2 -2
  63. package/dist/chunk-UQF3OJVE.mjs +389 -0
  64. package/dist/{chunk-XY4DYGSU.cjs → chunk-WGUHDWT5.cjs} +607 -435
  65. package/dist/{chunk-LAZK6UV7.mjs → chunk-YAJTD3ZG.mjs} +1 -1
  66. package/dist/{chunk-7UPCJDB5.mjs → chunk-YUEYBL35.mjs} +2 -2
  67. package/dist/{chunk-EIMK7D2D.mjs → chunk-ZD2VKAUI.mjs} +34 -4
  68. package/dist/chunk-ZNXTU4YH.mjs +114 -0
  69. package/dist/{client-plugins.d-C_3CdALC.d.mts → client-plugins.d-DllmH8eM.d.mts} +31 -5
  70. package/dist/{client-plugins.d-C0n8Yhh-.d.ts → client-plugins.d-n3iWY1EJ.d.ts} +31 -5
  71. package/dist/client-plugins.d.mts +2 -2
  72. package/dist/client-plugins.d.ts +2 -2
  73. package/dist/client.d.mts +7 -7
  74. package/dist/client.d.ts +7 -7
  75. package/dist/{config.d-CnhyvvmT.d.ts → config.d-DqKaCr1r.d.mts} +14 -3
  76. package/dist/{config.d-CnhyvvmT.d.mts → config.d-DqKaCr1r.d.ts} +14 -3
  77. package/dist/config.d.mts +2 -2
  78. package/dist/config.d.ts +2 -2
  79. package/dist/desktop.d.mts +3 -3
  80. package/dist/desktop.d.ts +3 -3
  81. package/dist/{devtools-KJFUE5FO.cjs → devtools-TO6TAO32.cjs} +5 -5
  82. package/dist/{devtools-BU5IT2PD.mjs → devtools-UESYARTF.mjs} +4 -4
  83. package/dist/docs.cjs +7 -7
  84. package/dist/docs.d.mts +3 -3
  85. package/dist/docs.d.ts +3 -3
  86. package/dist/docs.mjs +6 -6
  87. package/dist/index.cjs +23 -18
  88. package/dist/{index.d-CsGxS-hZ.d.mts → index.d-BL7KKiwa.d.mts} +1 -1
  89. package/dist/{index.d-B6M_8kmo.d.ts → index.d-BXsAXm1U.d.ts} +1 -1
  90. package/dist/index.d.mts +17 -17
  91. package/dist/index.d.ts +17 -17
  92. package/dist/index.mjs +23 -18
  93. package/dist/integrations.d.mts +2 -2
  94. package/dist/integrations.d.ts +2 -2
  95. package/dist/internal/app-markdown-runtime.cjs +3 -3
  96. package/dist/internal/app-markdown-runtime.d.mts +3 -3
  97. package/dist/internal/app-markdown-runtime.d.ts +3 -3
  98. package/dist/internal/app-markdown-runtime.mjs +2 -2
  99. package/dist/internal/build-runtime.cjs +21 -18
  100. package/dist/internal/build-runtime.d.mts +4 -4
  101. package/dist/internal/build-runtime.d.ts +4 -4
  102. package/dist/internal/build-runtime.mjs +20 -17
  103. package/dist/internal/cli-runtime.cjs +7 -7
  104. package/dist/internal/cli-runtime.mjs +6 -6
  105. package/dist/internal/client-error-overlay.cjs +4 -4
  106. package/dist/internal/client-error-overlay.mjs +1 -1
  107. package/dist/internal/client-runtime.d.mts +5 -5
  108. package/dist/internal/client-runtime.d.ts +5 -5
  109. package/dist/internal/compression-runtime.d.mts +2 -2
  110. package/dist/internal/compression-runtime.d.ts +2 -2
  111. package/dist/internal/config-runtime.cjs +6 -6
  112. package/dist/internal/config-runtime.d.mts +41 -80
  113. package/dist/internal/config-runtime.d.ts +41 -80
  114. package/dist/internal/config-runtime.mjs +5 -5
  115. package/dist/internal/infra-analytics-runtime.cjs +8 -8
  116. package/dist/internal/infra-analytics-runtime.mjs +6 -6
  117. package/dist/internal/product-telemetry-runtime.cjs +2 -2
  118. package/dist/internal/product-telemetry-runtime.mjs +1 -1
  119. package/dist/internal/production-runtime.cjs +91 -9
  120. package/dist/internal/production-runtime.d.mts +67 -12
  121. package/dist/internal/production-runtime.d.ts +67 -12
  122. package/dist/internal/production-runtime.mjs +90 -8
  123. package/dist/markdown.cjs +4 -2
  124. package/dist/markdown.d.mts +2 -2
  125. package/dist/markdown.d.ts +2 -2
  126. package/dist/markdown.mjs +3 -1
  127. package/dist/middleware.d.mts +4 -4
  128. package/dist/middleware.d.ts +4 -4
  129. package/dist/mobile.d.mts +21 -5
  130. package/dist/mobile.d.ts +21 -5
  131. package/dist/native-tree-EBPDFTRW.mjs +25 -0
  132. package/dist/native-tree-MMZS7S6W.cjs +25 -0
  133. package/dist/{navigation-delegate.d-C0BbdUgN.d.mts → navigation-delegate.d-C76gXTmO.d.mts} +1 -1
  134. package/dist/{navigation-delegate.d-jArRF0ZP.d.ts → navigation-delegate.d-DIyDslxt.d.ts} +1 -1
  135. package/dist/navigation.d.mts +5 -5
  136. package/dist/navigation.d.ts +5 -5
  137. package/dist/plugin.cjs +14 -13
  138. package/dist/plugin.d.mts +5 -4
  139. package/dist/plugin.d.ts +5 -4
  140. package/dist/plugin.mjs +13 -12
  141. package/dist/{production-runtime.d-DFaQ1qaV.d.mts → production-runtime.d-CDHztggO.d.mts} +1 -1
  142. package/dist/{production-runtime.d-UnXmSS_S.d.ts → production-runtime.d-tyaGdbk6.d.ts} +1 -1
  143. package/dist/{programmatic-routes-client.d-VZ0qcbH4.d.ts → programmatic-routes-client.d-XfmIrCK1.d.ts} +2 -2
  144. package/dist/{programmatic-routes-client.d-BIowUXQC.d.mts → programmatic-routes-client.d-aAWiDd64.d.mts} +2 -2
  145. package/dist/{render-plan.d-BNeqyPyJ.d.mts → render-plan.d-Diwybxo8.d.mts} +1 -1
  146. package/dist/{render-plan.d-BB6KrYjP.d.ts → render-plan.d-Dr5WRjoV.d.ts} +1 -1
  147. package/dist/renderer-client.d.mts +5 -5
  148. package/dist/renderer-client.d.ts +5 -5
  149. package/dist/{request.d-Bxhghm76.d.ts → request.d-D01fRn--.d.ts} +1 -1
  150. package/dist/{request.d-BV1munmx.d.mts → request.d-Dd3AFR79.d.mts} +1 -1
  151. package/dist/request.d.mts +3 -3
  152. package/dist/request.d.ts +3 -3
  153. package/dist/{route-rendering.d-DShqKlgK.d.mts → route-rendering.d-BQ53lJ_D.d.mts} +1 -1
  154. package/dist/{route-rendering.d-BIf7zE9d.d.ts → route-rendering.d-Bhx4QoJt.d.ts} +1 -1
  155. package/dist/{router.d-BE7i84fb.d.mts → router.d-Bi23a6Xc.d.mts} +1 -1
  156. package/dist/{router.d-Bw3K3b0a.d.ts → router.d-DQ3k7EYn.d.ts} +1 -1
  157. package/dist/{routes.d-_5tBQexL.d.mts → routes.d-BgTd7vI4.d.mts} +91 -4
  158. package/dist/{routes.d-DVZQL0-H.d.ts → routes.d-C7jMJFvP.d.ts} +91 -4
  159. package/dist/routes.d.mts +2 -2
  160. package/dist/routes.d.ts +2 -2
  161. package/dist/{rsc-pipeline-FBM6NFYY.mjs → rsc-pipeline-K3ER3Q4N.mjs} +5 -5
  162. package/dist/{rsc-pipeline-IQI5XQLS.cjs → rsc-pipeline-OCZIT5OF.cjs} +6 -6
  163. package/dist/{runtime-DXEVFYLE.cjs → runtime-DKNSIH7V.cjs} +85 -29
  164. package/dist/{runtime-2JYIDUON.mjs → runtime-MKYDEUZT.mjs} +75 -19
  165. package/dist/{runtime.d-CQgoYR0Q.d.mts → runtime.d-B2Js6qZ2.d.mts} +1 -1
  166. package/dist/{runtime.d-CfhaLPl-.d.ts → runtime.d-BL-CGwoi.d.ts} +1 -1
  167. package/dist/server-plugins.d.mts +3 -3
  168. package/dist/server-plugins.d.ts +3 -3
  169. package/dist/server.cjs +31 -30
  170. package/dist/server.d.mts +11 -10
  171. package/dist/server.d.ts +11 -10
  172. package/dist/server.mjs +16 -15
  173. package/dist/session-AAU7RNRO.cjs +23 -0
  174. package/dist/session-IGEDQ5PY.mjs +23 -0
  175. package/dist/{spa-router.d-D63jb6nX.d.mts → spa-router.d-B2rIFGs0.d.mts} +2 -2
  176. package/dist/{spa-router.d-C5opIgXg.d.ts → spa-router.d-DqZ6Lfs0.d.ts} +2 -2
  177. package/dist/testing.d.mts +2 -2
  178. package/dist/testing.d.ts +2 -2
  179. package/dist/{utils.d-CkpTqTsC.d.ts → utils.d-Bv2MPeLR.d.ts} +1 -1
  180. package/dist/{utils.d-CO-U4OVZ.d.mts → utils.d-fqXe8lNc.d.mts} +1 -1
  181. package/dist/version.cjs +2 -2
  182. package/dist/version.mjs +1 -1
  183. package/dist/vite.cjs +14 -13
  184. package/dist/vite.d.mts +12 -6
  185. package/dist/vite.d.ts +12 -6
  186. package/dist/vite.mjs +13 -12
  187. package/package.json +3 -3
@@ -1,3 +1,12 @@
1
+ import {
2
+ FARM_MOBILE_CLIENT_ENTRY_NAME,
3
+ FARM_MOBILE_SHELL_CLIENT_PLACEHOLDER,
4
+ createFarmMobileClientManifestPlugin,
5
+ encodeFarmMobileShellClient,
6
+ finalizeFarmMobileClient,
7
+ hasFarmMobileClientEntry,
8
+ removeFarmMobileClientManifest
9
+ } from "./chunk-ZNXTU4YH.mjs";
1
10
  import {
2
11
  loadFarmProductionVite
3
12
  } from "./chunk-PSUYE5HR.mjs";
@@ -13,13 +22,16 @@ import {
13
22
  import {
14
23
  generateNativeMCPPluginSource
15
24
  } from "./chunk-KQH2COEJ.mjs";
25
+ import {
26
+ FARM_MOBILE_SESSION_PLUGIN
27
+ } from "./chunk-C3BOESDW.mjs";
16
28
  import {
17
29
  assertFarmDocsAdapterServesNoGuardedPages,
18
30
  compileFarmDocsAdapterEdgeManifest,
19
31
  createFarmDocsAPIHandler,
20
32
  hasFarmDocsRuntimeAdapter,
21
33
  listFarmDocsAdapterPages
22
- } from "./chunk-I3EFXUAR.mjs";
34
+ } from "./chunk-5FXPJIOT.mjs";
23
35
  import {
24
36
  FARM_DOCS_LAST_MODIFIED_MANIFEST,
25
37
  createFarmDocsHandler,
@@ -31,7 +43,7 @@ import {
31
43
  loadFarmDocsPage,
32
44
  resolveFarmDocsContentDir,
33
45
  toFarmDocsMarkdownPage
34
- } from "./chunk-EIMK7D2D.mjs";
46
+ } from "./chunk-ZD2VKAUI.mjs";
35
47
  import {
36
48
  generateFarmDocsSearchClientRuntime,
37
49
  isFarmDocsSearchEnabled,
@@ -39,7 +51,7 @@ import {
39
51
  } from "./chunk-ALEUMLJN.mjs";
40
52
  import {
41
53
  FARM_PRERENDER_ENV
42
- } from "./chunk-KU7MBLDZ.mjs";
54
+ } from "./chunk-3SMXQNFD.mjs";
43
55
  import {
44
56
  resolveFarmInstrumentationFile
45
57
  } from "./chunk-N35YPV4N.mjs";
@@ -54,7 +66,7 @@ import {
54
66
  getDeployTargetForPreset,
55
67
  hasCustomFarmRouteContext,
56
68
  resolveDeployOutputPath
57
- } from "./chunk-KMB5BMFX.mjs";
69
+ } from "./chunk-GAO7MS5O.mjs";
58
70
  import {
59
71
  DEFAULT_NOT_FOUND_STYLES,
60
72
  FARM_LAYOUT_ELEMENT_CLIENT_SOURCE,
@@ -85,8 +97,10 @@ import {
85
97
  resolveFarmImageServerRuntime,
86
98
  resolveFarmIsolatedClientHydrationMode,
87
99
  resolveFarmNotFoundComponentPath,
88
- usesFarmServerTree
89
- } from "./chunk-EWC76P5D.mjs";
100
+ usePackageClientBoundaryCacheFile,
101
+ usesFarmServerTree,
102
+ withPackageClientBoundaryCache
103
+ } from "./chunk-3TLKUBXG.mjs";
90
104
  import {
91
105
  loadTailwindVitePlugin
92
106
  } from "./chunk-PIHKO25G.mjs";
@@ -110,7 +124,10 @@ import {
110
124
  layoutsCanCoverSamePage,
111
125
  routeBoundaryCoversDirectory,
112
126
  stripRouteGroups
113
- } from "./chunk-7UPCJDB5.mjs";
127
+ } from "./chunk-YUEYBL35.mjs";
128
+ import {
129
+ FARM_NATIVE_TREE_CONTENT_TYPE
130
+ } from "./chunk-LLZSYFOM.mjs";
114
131
  import {
115
132
  farmPluginMayAffectRuntimePath
116
133
  } from "./chunk-UUMKLGUU.mjs";
@@ -124,18 +141,18 @@ import {
124
141
  listFarmBuiltInRoutePaths,
125
142
  resolveFarmApiCatalogInput,
126
143
  resolveFarmAppManifestInput
127
- } from "./chunk-QAIAFCRZ.mjs";
144
+ } from "./chunk-4TKAIO2F.mjs";
128
145
  import {
129
146
  FARM_DEFAULT_MCP_PATH,
130
147
  createFarmDefaultMCPServer,
131
148
  farmConfigMountsMCP
132
- } from "./chunk-KRRZJ77U.mjs";
149
+ } from "./chunk-E5DXF5GE.mjs";
133
150
  import {
134
151
  farmEnvironmentFunctionsPlugin
135
152
  } from "./chunk-ZQ5BDNKW.mjs";
136
153
  import {
137
154
  isFarmMarkdownPageFile
138
- } from "./chunk-GW4YUIHW.mjs";
155
+ } from "./chunk-3Z63X45F.mjs";
139
156
  import {
140
157
  generateFarmDocsAdapterClientRuntime,
141
158
  getFarmDocsAdapterConfigModule
@@ -188,7 +205,7 @@ import {
188
205
  } from "./chunk-FSQMN2MQ.mjs";
189
206
  import {
190
207
  getFarmMarkdownAlternatePath
191
- } from "./chunk-N77JBPLC.mjs";
208
+ } from "./chunk-LPDKG6RA.mjs";
192
209
  import {
193
210
  _collectFarmStorageModules
194
211
  } from "./chunk-TJVUTGED.mjs";
@@ -233,7 +250,7 @@ import {
233
250
  } from "./chunk-LM2YOGDK.mjs";
234
251
  import {
235
252
  FARM_VERSION
236
- } from "./chunk-EOSFVNXZ.mjs";
253
+ } from "./chunk-R4PHCZ3G.mjs";
237
254
  import {
238
255
  getFarmPresetRuntime
239
256
  } from "./chunk-UAQD67HM.mjs";
@@ -1612,7 +1629,7 @@ function restoreLiteralModuleSpecifiers(code, fileName, { parseSync, Visitor })
1612
1629
  throw new Error(`Failed to minify ${fileName}: ${parsed.errors[0].message}`);
1613
1630
  }
1614
1631
  const replacements = [];
1615
- let needsAst = hasRequireTemplate;
1632
+ let needsAst = hasRequireTemplate && /(?<![\w$])require\(`/.test(code);
1616
1633
  if (!needsAst) {
1617
1634
  const seenStarts = /* @__PURE__ */ new Set();
1618
1635
  for (const { moduleRequest } of parsed.module.dynamicImports) {
@@ -1755,7 +1772,9 @@ function hasFarmPluginRoutes(config) {
1755
1772
  }
1756
1773
  function farmServerRuntimePluginsMayHandlePath(config, pathname) {
1757
1774
  return (config.plugins || []).some(
1758
- (plugin) => getFarmIntegrationPluginServerRuntime(plugin) !== false && farmPluginMayAffectRuntimePath(plugin, pathname)
1775
+ (plugin) => getFarmIntegrationPluginServerRuntime(plugin) !== false && // Mobile session tokens change only bearer requests and cookie-clearing responses, which a
1776
+ // prerendered file never answers or sends; its endpoints live under /__farm/.
1777
+ plugin.name !== FARM_MOBILE_SESSION_PLUGIN && farmPluginMayAffectRuntimePath(plugin, pathname)
1759
1778
  );
1760
1779
  }
1761
1780
  async function discoverMiddlewareRoutes(appDir) {
@@ -1904,6 +1923,7 @@ ${islandsErrors.map((error) => ` - ${error}`).join("\n")}`
1904
1923
  }
1905
1924
  const productionVite = productionViteResult.value;
1906
1925
  logger.info("\u26A1 Building application bundles with Vite Rolldown");
1926
+ const mobileClient = shouldBuildFarmMobileClient(config);
1907
1927
  const buildClientBundle = () => buildClient(
1908
1928
  productionVite,
1909
1929
  config,
@@ -1916,7 +1936,8 @@ ${islandsErrors.map((error) => ` - ${error}`).join("\n")}`
1916
1936
  isolatedClientBoundaryModules,
1917
1937
  hydrationPlanCache,
1918
1938
  rendererBuildLifetime,
1919
- routeManager.getProgrammaticClientRoutes()
1939
+ routeManager.getProgrammaticClientRoutes(),
1940
+ mobileClient
1920
1941
  );
1921
1942
  const buildSSRBundle = () => buildSSRInMemory(
1922
1943
  productionVite,
@@ -1969,7 +1990,8 @@ ${islandsErrors.map((error) => ` - ${error}`).join("\n")}`
1969
1990
  isolatedClientBoundaryModules,
1970
1991
  hydrationPlanCache,
1971
1992
  rendererBuildLifetime,
1972
- routeManager.getProgrammaticClientRoutes()
1993
+ routeManager.getProgrammaticClientRoutes(),
1994
+ mobileClient
1973
1995
  );
1974
1996
  } catch (retryError) {
1975
1997
  if (!(retryError instanceof IncompleteClientBuildOutputError)) throw retryError;
@@ -2028,6 +2050,27 @@ ${islandsErrors.map((error) => ` - ${error}`).join("\n")}`
2028
2050
  rendererBuild?.abort();
2029
2051
  }
2030
2052
  }
2053
+ function shouldBuildFarmMobileClient(config, options = {}) {
2054
+ if (!config.mobileClient || config.desktop) return false;
2055
+ const mode = resolveFarmServerComponentsMode(config.experimental?.serverComponents);
2056
+ if (mode === void 0) return true;
2057
+ if (!options.quiet)
2058
+ logger.info(
2059
+ `\u{1F4F1} No mobile client for experimental.serverComponents (${mode}) yet; a mobile shell loads the web client.`
2060
+ );
2061
+ return false;
2062
+ }
2063
+ function varyFarmNitroCacheRulesByShell(rules) {
2064
+ return Object.fromEntries(
2065
+ Object.entries(rules).map(([source, rule]) => {
2066
+ const cached = rule?.swr !== void 0 && rule.swr !== false || rule?.cache !== void 0 && rule.cache !== false;
2067
+ if (!cached || rule.cache === false) return [source, rule];
2068
+ const cache = rule.cache && typeof rule.cache === "object" ? rule.cache : {};
2069
+ const varies = [...Array.isArray(cache.varies) ? cache.varies : [], "x-farm-shell"];
2070
+ return [source, { ...rule, cache: { ...cache, varies } }];
2071
+ })
2072
+ );
2073
+ }
2031
2074
  async function writeSSRAssetsToClient(bundle, outputDir) {
2032
2075
  const fs4 = await import("fs/promises");
2033
2076
  for (const [fileName, output] of Object.entries(bundle)) {
@@ -2121,7 +2164,8 @@ function getCachedClientModuleHydrationPlan(modulePath, root, mode, cache, optio
2121
2164
  }
2122
2165
  return { ...plan, isolatedBoundaries: [...plan.isolatedBoundaries] };
2123
2166
  }
2124
- async function buildClient(productionVite, config, root, srcDir, outputDir, pageRoutes, layoutRoutes = [], routeSlots = [], isolatedClientBoundaryModules = /* @__PURE__ */ new Set(), hydrationPlanCache = /* @__PURE__ */ new Map(), rendererBuildLifetime, programmaticClientRoutes) {
2167
+ async function buildClient(productionVite, config, root, srcDir, outputDir, pageRoutes, layoutRoutes = [], routeSlots = [], isolatedClientBoundaryModules = /* @__PURE__ */ new Set(), hydrationPlanCache = /* @__PURE__ */ new Map(), rendererBuildLifetime, programmaticClientRoutes, mobile = false) {
2168
+ if (!mobile) await removeFarmMobileClientManifest(path10.dirname(outputDir));
2125
2169
  const viteBuild = productionVite.build;
2126
2170
  const { farmPlugin, farmServerOnlyEntriesPlugin } = await import("./vite.mjs");
2127
2171
  const { PluginManager: PluginManager2 } = await import("./plugin-53SY5YBS.mjs");
@@ -2364,206 +2408,229 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
2364
2408
  logger.info(
2365
2409
  `\u{1F4F1} Total hydratable routes detected: ${clientPages.length} pages, ${clientLayouts.filter((layout) => layout.shouldHydrate || layout.hasIsolatedClientBoundaries).length} layouts, and ${clientRouteSlots.length} slots`
2366
2410
  );
2367
- const clientEntryRoot = path10.join(root, ".farm", "tmp");
2368
- await fs4.mkdir(clientEntryRoot, { recursive: true });
2369
- const clientEntryDir = await fs4.mkdtemp(path10.join(clientEntryRoot, "client-entry-"));
2370
- const clientEntryPath = path10.join(clientEntryDir, "farm-client-entry.tsx");
2371
- const clientHydrationCode = generateClientHydrationEntry(
2372
- clientPages,
2373
- clientLayouts,
2374
- clientRouteSlots,
2375
- root,
2376
- srcDir,
2377
- adapterOwnsDocsRuntime ? false : isFarmDocsSearchEnabled(config.docs),
2378
- adapterOwnsDocsRuntime ? void 0 : resolveFarmDocsSearchClientModule(root),
2379
- config.docs?.enabled ? getFarmDocsRoots(config.docs).map((docsRoot) => docsRoot.entry) : void 0,
2380
- adapterOwnsDocsRuntime ? config.docs.adapter?.react : void 0,
2381
- config.i18n,
2382
- config.plugins,
2383
- config.renderer,
2384
- config.publicRuntimeConfig,
2385
- config.trailingSlash,
2386
- getIntegrationProviders(config.integrations),
2387
- config.basePath,
2388
- generateClientCachePersistenceCode(resolveFarmClientCacheAdapterEntry(root, config.cache)),
2389
- adapterOwnsDocsRuntime ? getFarmDocsAdapterConfigModule(config.docs) : void 0,
2390
- resolveFarmServerComponentsMode(config.experimental?.serverComponents) === "islands"
2391
- );
2392
- await fs4.writeFile(clientEntryPath, clientHydrationCode);
2393
- const hasScopedPostcssConfig = hasProjectPostcssConfig(root);
2394
- let postcssSearchPath;
2395
- let tailwindVitePlugin = void 0;
2396
- const rendererVitePlugins = await loadFarmRendererVitePlugins(config.renderer, root, {
2397
- ssr: false,
2398
- buildLifetime: rendererBuildLifetime
2399
- });
2400
- if (hasScopedPostcssConfig) {
2401
- logger.info("\u{1F4E6} Using project PostCSS/Tailwind configuration");
2402
- } else {
2403
- const postcssConfigPath = path10.join(clientEntryDir, "postcss.config.cjs");
2404
- await fs4.writeFile(postcssConfigPath, "module.exports = { plugins: [] };\n");
2405
- postcssSearchPath = clientEntryDir;
2406
- tailwindVitePlugin = await loadTailwindVitePlugin({
2407
- builder: productionVite.builder,
2408
- announce: true
2409
- });
2410
- }
2411
- try {
2412
- const clientViteConfig = {
2411
+ const buildTarget = async (platform) => {
2412
+ const clientEntryRoot = path10.join(root, ".farm", "tmp");
2413
+ await fs4.mkdir(clientEntryRoot, { recursive: true });
2414
+ const clientEntryDir = await fs4.mkdtemp(path10.join(clientEntryRoot, "client-entry-"));
2415
+ const clientEntryPath = path10.join(clientEntryDir, "farm-client-entry.tsx");
2416
+ const clientHydrationCode = generateClientHydrationEntry(
2417
+ clientPages,
2418
+ clientLayouts,
2419
+ clientRouteSlots,
2413
2420
  root,
2414
- // Vite otherwise falls back to <root>/public, silently dropping every
2415
- // asset in a custom config.publicDir from the production client output.
2416
- publicDir: config.publicDir,
2417
- esbuild: {
2418
- jsxDev: false
2419
- },
2420
- build: {
2421
- // Preserve Farm's Vite 5 browser baseline when the production builder
2422
- // uses Vite 8, whose default target only covers newer browsers.
2423
- target: FARM_CLIENT_BUILD_TARGET,
2424
- outDir: outputDir,
2425
- emptyOutDir: true,
2426
- // Never public: see resolveFarmClientSourcemap.
2427
- sourcemap: resolveFarmClientSourcemap(config.vite?.build?.sourcemap),
2428
- assetsInlineLimit: 0,
2429
- cssCodeSplit: false,
2430
- // Bundle all CSS into one file
2431
- rollupOptions: {
2432
- input: {
2433
- "farm-client": clientEntryPath
2434
- },
2435
- output: {
2436
- entryFileNames: "[name]-h[hash].js",
2437
- chunkFileNames: "chunks/[name]-h[hash].js",
2438
- hashCharacters: "hex",
2439
- // Use predictable name for CSS so we can reference it in SSR HTML
2440
- assetFileNames: (assetInfo) => {
2441
- if (assetInfo.name?.endsWith(".css")) {
2442
- return "farm-client.css";
2421
+ srcDir,
2422
+ adapterOwnsDocsRuntime ? false : isFarmDocsSearchEnabled(config.docs),
2423
+ adapterOwnsDocsRuntime ? void 0 : resolveFarmDocsSearchClientModule(root),
2424
+ config.docs?.enabled ? getFarmDocsRoots(config.docs).map((docsRoot) => docsRoot.entry) : void 0,
2425
+ adapterOwnsDocsRuntime ? config.docs.adapter?.react : void 0,
2426
+ config.i18n,
2427
+ config.plugins,
2428
+ config.renderer,
2429
+ config.publicRuntimeConfig,
2430
+ config.trailingSlash,
2431
+ getIntegrationProviders(config.integrations),
2432
+ config.basePath,
2433
+ generateClientCachePersistenceCode(resolveFarmClientCacheAdapterEntry(root, config.cache)),
2434
+ adapterOwnsDocsRuntime ? getFarmDocsAdapterConfigModule(config.docs) : void 0,
2435
+ resolveFarmServerComponentsMode(config.experimental?.serverComponents) === "islands",
2436
+ platform
2437
+ );
2438
+ const entryName = platform === "mobile" ? FARM_MOBILE_CLIENT_ENTRY_NAME : "farm-client";
2439
+ await fs4.writeFile(clientEntryPath, clientHydrationCode);
2440
+ const hasScopedPostcssConfig = hasProjectPostcssConfig(root);
2441
+ let postcssSearchPath;
2442
+ let tailwindVitePlugin = void 0;
2443
+ const rendererVitePlugins = await loadFarmRendererVitePlugins(config.renderer, root, {
2444
+ ssr: false,
2445
+ buildLifetime: rendererBuildLifetime
2446
+ });
2447
+ if (hasScopedPostcssConfig) {
2448
+ logger.info("\u{1F4E6} Using project PostCSS/Tailwind configuration");
2449
+ } else {
2450
+ const postcssConfigPath = path10.join(clientEntryDir, "postcss.config.cjs");
2451
+ await fs4.writeFile(postcssConfigPath, "module.exports = { plugins: [] };\n");
2452
+ postcssSearchPath = clientEntryDir;
2453
+ tailwindVitePlugin = await loadTailwindVitePlugin({
2454
+ builder: productionVite.builder,
2455
+ announce: true
2456
+ });
2457
+ }
2458
+ try {
2459
+ const clientViteConfig = {
2460
+ root,
2461
+ // Vite otherwise falls back to <root>/public, silently dropping every
2462
+ // asset in a custom config.publicDir from the production client output.
2463
+ // The mobile client adds to the web client's output; public files are copied once.
2464
+ publicDir: platform === "mobile" ? false : config.publicDir,
2465
+ esbuild: {
2466
+ jsxDev: false
2467
+ },
2468
+ build: {
2469
+ // Preserve Farm's Vite 5 browser baseline when the production builder
2470
+ // uses Vite 8, whose default target only covers newer browsers.
2471
+ target: FARM_CLIENT_BUILD_TARGET,
2472
+ outDir: outputDir,
2473
+ emptyOutDir: platform === "web",
2474
+ // Never public: see resolveFarmClientSourcemap.
2475
+ sourcemap: resolveFarmClientSourcemap(config.vite?.build?.sourcemap),
2476
+ assetsInlineLimit: 0,
2477
+ cssCodeSplit: false,
2478
+ // Bundle all CSS into one file
2479
+ rollupOptions: {
2480
+ input: {
2481
+ [entryName]: clientEntryPath
2482
+ },
2483
+ output: {
2484
+ entryFileNames: "[name]-h[hash].js",
2485
+ chunkFileNames: "chunks/[name]-h[hash].js",
2486
+ hashCharacters: "hex",
2487
+ // Use predictable name for CSS so we can reference it in SSR HTML
2488
+ assetFileNames: (assetInfo) => {
2489
+ if (assetInfo.name?.endsWith(".css")) {
2490
+ return `${entryName}.css`;
2491
+ }
2492
+ return "assets/[name]-h[hash][extname]";
2443
2493
  }
2444
- return "assets/[name]-h[hash][extname]";
2445
- }
2446
- },
2447
- // A browser cannot load a Node builtin: farmServerOnlyEntriesPlugin
2448
- // resolves one to Farm's browser module for it instead.
2449
- external: (id) => {
2450
- if (id === "fsevents" || id.includes("fsevents") || id.endsWith(".node")) {
2451
- return true;
2494
+ },
2495
+ // A browser cannot load a Node builtin: farmServerOnlyEntriesPlugin
2496
+ // resolves one to Farm's browser module for it instead.
2497
+ external: (id) => {
2498
+ if (id === "fsevents" || id.includes("fsevents") || id.endsWith(".node")) {
2499
+ return true;
2500
+ }
2501
+ return false;
2452
2502
  }
2453
- return false;
2454
2503
  }
2455
- }
2456
- },
2457
- plugins: [
2458
- createFarmThemeCssPlugin(config.theme, config.basePath),
2459
- createFarmPrivateClientSourcemapsPlugin({
2460
- outDir: path10.join(path10.dirname(outputDir), FARM_CLIENT_SOURCEMAPS_DIR)
2461
- }),
2462
- createFarmClientModulePreloadPlugin({
2463
- root,
2464
- buildDir: path10.dirname(outputDir),
2465
- entryName: "farm-client"
2466
- }),
2467
- ...adapterOwnsDocsRuntime ? [
2468
- createFarmDocsModulePreloadPlugin({
2504
+ },
2505
+ plugins: [
2506
+ createFarmThemeCssPlugin(config.theme, config.basePath),
2507
+ createFarmPrivateClientSourcemapsPlugin({
2508
+ outDir: path10.join(
2509
+ path10.dirname(outputDir),
2510
+ platform === "mobile" ? `${FARM_CLIENT_SOURCEMAPS_DIR}-mobile` : FARM_CLIENT_SOURCEMAPS_DIR
2511
+ )
2512
+ }),
2513
+ // The web client's preload manifests; the mobile client records its files instead.
2514
+ platform === "mobile" ? createFarmMobileClientManifestPlugin({
2515
+ buildDir: path10.dirname(outputDir),
2516
+ entryName
2517
+ }) : createFarmClientModulePreloadPlugin({
2469
2518
  root,
2470
2519
  buildDir: path10.dirname(outputDir),
2471
- importer: clientEntryPath,
2472
- entryName: "farm-client",
2473
- modules: [
2474
- config.docs.adapter.react,
2475
- getFarmDocsAdapterConfigModule(config.docs)
2476
- ].filter((specifier) => Boolean(specifier))
2477
- })
2478
- ] : [],
2479
- ...tailwindVitePlugin ? [tailwindVitePlugin] : [],
2480
- ...rendererVitePlugins,
2481
- ...config.vite.plugins || [],
2482
- farmServerOnlyEntriesPlugin(),
2483
- farmDocsClientConfigPlugin(
2484
- adapterOwnsDocsRuntime ? getFarmDocsAdapterConfigModule(config.docs) : void 0
2485
- ),
2486
- // Plugin to redirect @farm.js/core imports to client-only exports
2487
- {
2488
- name: "farm-client-only-imports",
2489
- enforce: "pre",
2490
- resolveId(id) {
2491
- if (id === "@farm.js/core") {
2492
- return { id: "\0farm-client-exports", external: false };
2493
- }
2494
- if (id === "@farm.js/core/i18n/server") {
2495
- return { id: "\0farm-i18n-client-bridge", external: false };
2496
- }
2497
- if (id === "@farm.js/core/server" || id === "@farm.js/core/api" || id === "@farm.js/core/middleware" || id === "@farm.js/core/headers" || id === "@farm.js/core/config" || id.includes("@farm.js/core/middleware") || id.includes("@farm.js/core/query/server")) {
2498
- return { id: "\0empty-module", external: false };
2499
- }
2500
- if (id.includes("/api/") && id.includes("/route")) {
2501
- return { id: "\0empty-api-route", external: false };
2502
- }
2503
- if (id === "fsevents" || id.includes("fsevents") || id.endsWith(".node") || id === "nitro" || id === "vite" || id === "esbuild" || id === "rolldown" || id.startsWith("rolldown/") || id === "rollup" || id.startsWith("nitro/") || id.includes("node-pre-gyp") || id.includes("nf3")) {
2504
- return { id: "\0empty-module", external: false };
2520
+ entryName: "farm-client"
2521
+ }),
2522
+ ...adapterOwnsDocsRuntime && platform === "web" ? [
2523
+ createFarmDocsModulePreloadPlugin({
2524
+ root,
2525
+ buildDir: path10.dirname(outputDir),
2526
+ importer: clientEntryPath,
2527
+ entryName: "farm-client",
2528
+ modules: [
2529
+ config.docs.adapter.react,
2530
+ getFarmDocsAdapterConfigModule(config.docs)
2531
+ ].filter((specifier) => Boolean(specifier))
2532
+ })
2533
+ ] : [],
2534
+ ...tailwindVitePlugin ? [tailwindVitePlugin] : [],
2535
+ ...rendererVitePlugins,
2536
+ ...config.vite.plugins || [],
2537
+ farmServerOnlyEntriesPlugin({ platform }),
2538
+ farmDocsClientConfigPlugin(
2539
+ adapterOwnsDocsRuntime ? getFarmDocsAdapterConfigModule(config.docs) : void 0
2540
+ ),
2541
+ // Plugin to redirect @farm.js/core imports to client-only exports
2542
+ {
2543
+ name: "farm-client-only-imports",
2544
+ enforce: "pre",
2545
+ resolveId(id) {
2546
+ if (id === "@farm.js/core") {
2547
+ return { id: "\0farm-client-exports", external: false };
2548
+ }
2549
+ if (id === "@farm.js/core/i18n/server") {
2550
+ return { id: "\0farm-i18n-client-bridge", external: false };
2551
+ }
2552
+ if (id === "@farm.js/core/server" || id === "@farm.js/core/api" || id === "@farm.js/core/middleware" || id === "@farm.js/core/headers" || id === "@farm.js/core/config" || id.includes("@farm.js/core/middleware") || id.includes("@farm.js/core/query/server")) {
2553
+ return { id: "\0empty-module", external: false };
2554
+ }
2555
+ if (id.includes("/api/") && id.includes("/route")) {
2556
+ return { id: "\0empty-api-route", external: false };
2557
+ }
2558
+ if (id === "fsevents" || id.includes("fsevents") || id.endsWith(".node") || id === "nitro" || id === "vite" || id === "esbuild" || id === "rolldown" || id.startsWith("rolldown/") || id === "rollup" || id.startsWith("nitro/") || id.includes("node-pre-gyp") || id.includes("nf3")) {
2559
+ return { id: "\0empty-module", external: false };
2560
+ }
2561
+ return null;
2562
+ },
2563
+ load(id) {
2564
+ if (id === "\0empty-module") {
2565
+ return "const emptyMiddlewareStore = new Map(); export default {}; export const getMiddlewareContext = () => emptyMiddlewareStore; export const getMiddlewareData = () => emptyMiddlewareStore; export const getMiddlewareValue = () => undefined; export const middleware = () => ({});";
2566
+ }
2567
+ if (id === "\0empty-api-route") {
2568
+ return "export const GET = () => {}; export const HEAD = () => {}; export const QUERY = () => {}; export const POST = () => {}; export const PUT = () => {}; export const DELETE = () => {}; export const PATCH = () => {}; export const OPTIONS = () => {}; export default {};";
2569
+ }
2570
+ if (id === "\0farm-i18n-client-bridge") {
2571
+ return 'export { createTranslator, format, getLocale, getLocaleSource, t } from "@farm.js/core/i18n/client";';
2572
+ }
2573
+ if (id === "\0farm-client-exports") {
2574
+ return [
2575
+ "// Farm.js Client Exports - Safe for browser bundling",
2576
+ 'export { Link } from "@farm.js/core/client";',
2577
+ 'export { useRouter } from "@farm.js/core/client";',
2578
+ 'export { usePathname, useSearchParams } from "@farm.js/core/navigation";',
2579
+ // A page that hydrates ships to the browser with its notFound()/redirect() calls.
2580
+ 'export { getFarmRedirectError, isFarmNotFoundError, isFarmRedirectError, notFound, permanentRedirect, redirect } from "@farm.js/core/navigation";',
2581
+ 'export { createAPIClient } from "@farm.js/core/client";',
2582
+ 'export { localFont, remoteFont } from "@farm.js/core/font";'
2583
+ ].join("\n");
2584
+ }
2585
+ return null;
2505
2586
  }
2506
- return null;
2507
2587
  },
2508
- load(id) {
2509
- if (id === "\0empty-module") {
2510
- return "const emptyMiddlewareStore = new Map(); export default {}; export const getMiddlewareContext = () => emptyMiddlewareStore; export const getMiddlewareData = () => emptyMiddlewareStore; export const getMiddlewareValue = () => undefined; export const middleware = () => ({});";
2511
- }
2512
- if (id === "\0empty-api-route") {
2513
- return "export const GET = () => {}; export const HEAD = () => {}; export const QUERY = () => {}; export const POST = () => {}; export const PUT = () => {}; export const DELETE = () => {}; export const PATCH = () => {}; export const OPTIONS = () => {}; export default {};";
2514
- }
2515
- if (id === "\0farm-i18n-client-bridge") {
2516
- return 'export { createTranslator, format, getLocale, getLocaleSource, t } from "@farm.js/core/i18n/client";';
2517
- }
2518
- if (id === "\0farm-client-exports") {
2519
- return [
2520
- "// Farm.js Client Exports - Safe for browser bundling",
2521
- 'export { Link } from "@farm.js/core/client";',
2522
- 'export { useRouter } from "@farm.js/core/client";',
2523
- 'export { usePathname, useSearchParams } from "@farm.js/core/navigation";',
2524
- // A page that hydrates ships to the browser with its notFound()/redirect() calls.
2525
- 'export { getFarmRedirectError, isFarmNotFoundError, isFarmRedirectError, notFound, permanentRedirect, redirect } from "@farm.js/core/navigation";',
2526
- 'export { createAPIClient } from "@farm.js/core/client";',
2527
- 'export { localFont, remoteFont } from "@farm.js/core/font";'
2528
- ].join("\n");
2529
- }
2530
- return null;
2531
- }
2588
+ farmPlugin(
2589
+ { ...config, isolatedClientBoundaryModules, programmaticClientRoutes },
2590
+ pluginManager
2591
+ ),
2592
+ farmEnvironmentFunctionsPlugin(config)
2593
+ ],
2594
+ mode: "production",
2595
+ css: postcssSearchPath ? {
2596
+ postcss: postcssSearchPath
2597
+ } : void 0,
2598
+ // Ensure React is bundled for client
2599
+ resolve: {
2600
+ alias: createFarmSourceAlias(root, config.srcDir),
2601
+ dedupe: [...config.renderer.dedupe || []]
2532
2602
  },
2533
- farmPlugin(
2534
- { ...config, isolatedClientBoundaryModules, programmaticClientRoutes },
2535
- pluginManager
2536
- ),
2537
- farmEnvironmentFunctionsPlugin(config)
2538
- ],
2539
- mode: "production",
2540
- css: postcssSearchPath ? {
2541
- postcss: postcssSearchPath
2542
- } : void 0,
2543
- // Ensure React is bundled for client
2544
- resolve: {
2545
- alias: createFarmSourceAlias(root, config.srcDir),
2546
- dedupe: [...config.renderer.dedupe || []]
2547
- },
2548
- // Optimize dependencies - exclude server-side code from client bundle
2549
- optimizeDeps: {
2550
- exclude: [
2551
- "@farm.js/core/server",
2552
- "@farm.js/core/api",
2553
- "@farm.js/core/middleware",
2554
- "@farm.js/core/headers"
2555
- ]
2603
+ // Optimize dependencies - exclude server-side code from client bundle
2604
+ optimizeDeps: {
2605
+ exclude: [
2606
+ "@farm.js/core/server",
2607
+ "@farm.js/core/api",
2608
+ "@farm.js/core/middleware",
2609
+ "@farm.js/core/headers"
2610
+ ]
2611
+ }
2612
+ };
2613
+ await viteBuild(mergeFarmProductionViteConfig(clientViteConfig, config.vite));
2614
+ if (platform === "mobile") {
2615
+ await fs4.rm(path10.join(outputDir, `${entryName}.css`), { force: true });
2616
+ if (!await hasFarmMobileClientEntry(path10.dirname(outputDir), outputDir)) {
2617
+ throw new IncompleteClientBuildOutputError(
2618
+ `${FARM_MOBILE_CLIENT_ENTRY_NAME}.js is missing or empty`
2619
+ );
2620
+ }
2621
+ } else {
2622
+ await mergeBuiltFontCss(outputDir);
2623
+ await validateClientBuildOutput(root, srcDir, outputDir);
2624
+ }
2625
+ } finally {
2626
+ try {
2627
+ await fs4.rm(clientEntryDir, { recursive: true, force: true });
2628
+ } catch {
2556
2629
  }
2557
- };
2558
- await viteBuild(mergeFarmProductionViteConfig(clientViteConfig, config.vite));
2559
- await mergeBuiltFontCss(outputDir);
2560
- await validateClientBuildOutput(root, srcDir, outputDir);
2561
- } finally {
2562
- try {
2563
- await fs4.rm(clientEntryDir, { recursive: true, force: true });
2564
- } catch {
2565
2630
  }
2566
- }
2631
+ };
2632
+ await buildTarget("web");
2633
+ if (mobile) await buildTarget("mobile");
2567
2634
  }
2568
2635
  function toBakedMarkdownSourcePath(root, filePath) {
2569
2636
  if (!filePath) return "";
@@ -2642,21 +2709,24 @@ async function fetchFarmNavigationDocument(url, headers, recover = true, signal)
2642
2709
  return response;
2643
2710
  }
2644
2711
 
2645
- ${streamNavigations ? `const FARM_FLUSH_MARKER = "<!--farm-flush-->";
2646
-
2647
- // Reads a streamed navigation (F-Stream) up to the end of React's shell. \`next\` then resolves
2712
+ ${streamNavigations ? `// Reads a streamed navigation (F-Stream) up to the end of React's shell. \`next\` then resolves
2648
2713
  // with each later flush as it ends, and null at the end; a document without a marker is all shell.
2649
- async function readFarmNavigationStream(body) {
2714
+ // The marker carries the random token the server sent in the F-Flush header, so page content
2715
+ // cannot forge a boundary; a response without a valid token is all shell.
2716
+ async function readFarmNavigationStream(body, flushToken) {
2650
2717
  const reader = body.getReader();
2651
2718
  const decoder = new TextDecoder();
2719
+ const marker = typeof flushToken === "string" && /^[0-9a-f]{32}$/.test(flushToken)
2720
+ ? "<!--farm-flush:" + flushToken + "-->"
2721
+ : "";
2652
2722
  let text = "";
2653
2723
  let done = false;
2654
2724
  const next = async function() {
2655
2725
  for (;;) {
2656
- const at = text.indexOf(FARM_FLUSH_MARKER);
2726
+ const at = marker ? text.indexOf(marker) : -1;
2657
2727
  if (at !== -1) {
2658
2728
  const flush = text.slice(0, at);
2659
- text = text.slice(at + FARM_FLUSH_MARKER.length);
2729
+ text = text.slice(at + marker.length);
2660
2730
  return flush;
2661
2731
  }
2662
2732
  if (done) {
@@ -3230,7 +3300,7 @@ ${streamNavigations ? ` // A streamed navigation swaps in the page's shell; its
3230
3300
  }
3231
3301
  };
3232
3302
  ${streamNavigations ? ` if (streamShell && response.body) {
3233
- const streamed = await readFarmNavigationStream(response.body);
3303
+ const streamed = await readFarmNavigationStream(response.body, response.headers.get("F-Flush"));
3234
3304
  if (!streamed.next) {
3235
3305
  store(streamed.shell);
3236
3306
  return streamed.shell;
@@ -3436,13 +3506,14 @@ function generateClientHydrationEntry(clientPages, layoutRoutes, clientRouteSlot
3436
3506
  secure: false
3437
3507
  },
3438
3508
  direction: {}
3439
- }, plugins = [], renderer = REACT_RENDERER, publicRuntimeConfig = void 0, trailingSlash = false, integrationProviders = [], basePath = "/", clientCachePersistence = { imports: "", init: "" }, docsConfigModule, islandValues = false) {
3509
+ }, plugins = [], renderer = REACT_RENDERER, publicRuntimeConfig = void 0, trailingSlash = false, integrationProviders = [], basePath = "/", clientCachePersistence = { imports: "", init: "" }, docsConfigModule, islandValues = false, platform) {
3440
3510
  const toImportPath = (targetPath) => targetPath.replace(/\\/g, "/");
3441
3511
  const clientPluginEntry = generateFarmClientPluginEntryCode(
3442
3512
  plugins,
3443
3513
  root,
3444
3514
  srcDir,
3445
- publicRuntimeConfig
3515
+ publicRuntimeConfig,
3516
+ platform
3446
3517
  );
3447
3518
  const appSetupPath = resolveFarmRendererAppSetup(root, renderer);
3448
3519
  const rendererClientImports = isReactRenderer(renderer) ? `import React from "react";
@@ -5953,6 +6024,7 @@ ReactDOMServer.configureApp(__farmAppSetup);`;
5953
6024
  }
5954
6025
  function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlots, errorRoutes, metadataImageRoutes, applicationMetadataRoutes, middlewareRoutes, redirectRoutes, configuredRewriteRoutes, configuredHeaderRoutes, openAPIReference, notFoundPath, instrumentationPath, config, farmDocsPrecompiledManifest, configModulePath, hasServerRuntimeIntegrations, hasRuntimeIntegrationConfig, hasConfiguredRuntimePlugins, hasServerRuntimePlugins, preset, i18nCatalogs, isolatedClientBoundaryModules, hydrationPlanCache) {
5955
6026
  const farmIslandsMode = resolveFarmServerComponentsMode(config.experimental?.serverComponents) === "islands";
6027
+ const farmMobileClient = shouldBuildFarmMobileClient(config, { quiet: true });
5956
6028
  const hasCompressionRuntime = config.compress && resolveFarmInstrumentationRuntime(preset) === "nodejs";
5957
6029
  const servesAgentRobotsTxt = Boolean(config.agent?.crawlers?.enabled) && !applicationMetadataRoutes.some(
5958
6030
  (metadata) => metadata.kind === "robots" && metadata.pattern === "/"
@@ -6381,8 +6453,22 @@ function renderFarmServerTreeLayout(Layout, props, outlets) {
6381
6453
  stripFarmLocaleFromPathname,
6382
6454
  withFarmRouteContext,
6383
6455
  createLateNotFoundRecovery,
6384
- createLateRedirectRecovery,${hasInfraAnalyticsRuntime ? "\n resolveFarmDocsEngineRouteLabel,\n resolveFarmDocsRouteLabel,\n resolveFarmMarkdownRouteLabel,\n resolveFarmMCPRouteLabel,\n setFarmRouteLabel," : ""}${hasFarmServerStorageConfig(config.storage) ? "\n _registerFarmStorageModules,\n disposeFarmStorage,\n initFarmStorage," : ""}
6385
- } from "@farm.js/core/internal/production-runtime";${farmIslandsMode ? `
6456
+ createLateRedirectRecovery,${config.mobileClient ? "\n createFarmNativeTreeResponse," : ""}${hasInfraAnalyticsRuntime ? "\n resolveFarmDocsEngineRouteLabel,\n resolveFarmDocsRouteLabel,\n resolveFarmMarkdownRouteLabel,\n resolveFarmMCPRouteLabel,\n setFarmRouteLabel," : ""}${hasFarmServerStorageConfig(config.storage) ? "\n _registerFarmStorageModules,\n disposeFarmStorage,\n initFarmStorage," : ""}
6457
+ } from "@farm.js/core/internal/production-runtime";${farmMobileClient ? `
6458
+ import { _applyFarmMobileShellClient } from "@farm.js/core/internal/production-runtime";
6459
+ // The mobile client documents load when a mobile shell asks for them (RFC 0004, 7.2), filled in
6460
+ // once the client build has finished (see buildNitroUniversal). A shell's document stays out of
6461
+ // shared caches, which the adapter learns as it does for a guarded response.
6462
+ const farmMobileShellClient = JSON.parse("${FARM_MOBILE_SHELL_CLIENT_PLACEHOLDER}");
6463
+ function applyFarmMobileShellResponse(request, response, context) {
6464
+ return _applyFarmMobileShellClient(request, response, farmMobileShellClient, () =>
6465
+ context?.onGuardedResponse?.()
6466
+ );
6467
+ }` : `
6468
+ // No mobile block: no mobile client, and documents ignore X-Farm-Shell.
6469
+ function applyFarmMobileShellResponse(request, response) {
6470
+ return response;
6471
+ }`}${farmIslandsMode ? `
6386
6472
  import { getSharedLayoutPrefixLength, handleFarmFormActionRequest, ignoreFarmHeldValues, installFarmIslandValues, parseFarmLayoutChainHeader } from "@farm.js/core/internal/production-runtime";
6387
6473
  import { installFarmIslandsServer } from "@farm.js/core/internal/islands-server";
6388
6474
  installFarmIslandsServer();
@@ -6433,12 +6519,16 @@ import { fileURLToPath as farmDocsFileURLToPath } from "node:url";` : "";
6433
6519
  const instrumentationImport = instrumentationPath ? `import * as FarmInstrumentationModule from ${toVirtualEntryImportSpecifier(instrumentationPath)};` : "";
6434
6520
  const nativeAuth = generateNativeAuthIntegrationSource(config.auth);
6435
6521
  const nativeMCP = generateNativeMCPPluginSource(config.mcp);
6522
+ const mobileSession = (config.plugins || []).some(
6523
+ (plugin) => plugin?.name === FARM_MOBILE_SESSION_PLUGIN
6524
+ );
6436
6525
  const integrationImports = `
6437
6526
  ${configModulePath ? `import * as FarmUserConfigModule from ${toVirtualEntryImportSpecifier(configModulePath)};` : ""}
6438
6527
  ${layerConfigImports}
6439
6528
  ${integrationRuntimeImport}
6440
6529
  ${nativeAuth.importSource}
6441
6530
  ${nativeMCP.importSource}
6531
+ ${mobileSession ? `import { createFarmMobileSessionPlugin as __farmCreateMobileSessionPlugin } from "@farm.js/core/internal/production-runtime";` : ""}
6442
6532
  `;
6443
6533
  const imageRuntime = resolveFarmImageServerRuntime(config.images, preset);
6444
6534
  const imageRuntimeImport = imageRuntime === "none" ? "" : `import { _withFarmImageSourceClientAddress, createCloudflareImageTransformer, createFarmImageHandler } from "@farm.js/core/image/server";`;
@@ -6719,6 +6809,10 @@ const configuredPlugins = [
6719
6809
  ${hasCompressionRuntime && hasPluginRuntime ? "createCompressionPlugin()," : ""}
6720
6810
  ${hasRuntimeIntegrationConfig ? "...resolveIntegrationPlugins(serverRuntimeIntegrations)," : ""}
6721
6811
  ${nativeMCP.pluginSource}
6812
+ ${mobileSession ? `__farmCreateMobileSessionPlugin({
6813
+ basePath: ${JSON.stringify(config.basePath)},
6814
+ integrations: () => serverRuntimeIntegrations,
6815
+ }),` : ""}
6722
6816
  ${hasConfiguredRuntimePlugins ? `...farmRuntimeConfigs.flatMap((runtimeConfig) =>
6723
6817
  Array.isArray(runtimeConfig.plugins) ? runtimeConfig.plugins : [],
6724
6818
  ),` : ""}
@@ -7078,10 +7172,22 @@ function escapeFarmHtmlAttribute(value) {
7078
7172
  .replace(/>/g, "&gt;");
7079
7173
  }
7080
7174
 
7081
- async function renderFarmElement(ReactDOMServer, element, routeKey, flushMarkers = false) {
7082
- // Written after each React flush in a streamed navigation (flushMarkers): the browser swaps in
7175
+ // A streamed navigation's flush marker token: 128 random bits, hex.
7176
+ function createFarmFlushToken() {
7177
+ const bytes = new Uint8Array(16);
7178
+ globalThis.crypto.getRandomValues(bytes);
7179
+ let token = "";
7180
+ for (const byte of bytes) token += byte.toString(16).padStart(2, "0");
7181
+ return token;
7182
+ }
7183
+
7184
+ async function renderFarmElement(ReactDOMServer, element, routeKey, flushToken = "") {
7185
+ // Written after each React flush in a streamed navigation (flushToken): the browser swaps in
7083
7186
  // the shell, then adds each Suspense boundary as soon as the flush that completed it ends.
7084
- const FARM_FLUSH_MARKER = "<!--farm-flush-->";
7187
+ // The token is random per response and reaches the browser in the F-Flush header, so page
7188
+ // content cannot forge a boundary.
7189
+ const flushMarkers = Boolean(flushToken);
7190
+ const FARM_FLUSH_MARKER = flushMarkers ? "<!--farm-flush:" + flushToken + "-->" : "";
7085
7191
  // Islands mode: each render writes its islands' values itself, so a request that renders twice
7086
7192
  // (notFound(), or a synchronous attempt that suspends and streams) still sends them.
7087
7193
  if (typeof farmIslandsRender === "function") element = farmIslandsRender(element);
@@ -9097,6 +9203,21 @@ ${hasServerRuntimeIntegrations ? `
9097
9203
  }
9098
9204
  ` : ""}
9099
9205
 
9206
+ ${config.mobileClient ? `
9207
+ // The mobile app's native-tier screens: the page, rendered as for a browser, read into a
9208
+ // tree of native views.
9209
+ if (
9210
+ (request.method === "GET" || request.method === "HEAD") &&
9211
+ request.headers.get("accept")?.includes(${JSON.stringify(FARM_NATIVE_TREE_CONTENT_TYPE)})
9212
+ ) {
9213
+ const treeResponse = await createFarmNativeTreeResponse({
9214
+ request,
9215
+ renderPage: (targetRequest) => fetchFarmInnerRequest(targetRequest, adapterContext),
9216
+ });
9217
+ return applyProductionMiddlewareHeaders(treeResponse, middlewareHeaders);
9218
+ }
9219
+ ` : ""}
9220
+
9100
9221
  ${config.md?.enabled ? `
9101
9222
  if (farmMarkdownConfig?.enabled) {
9102
9223
  const markdownResponse = await createMarkdownMirrorResponse({
@@ -9619,10 +9740,13 @@ ${hasServerRuntimeIntegrations ? `
9619
9740
  !route.shouldHydrate &&
9620
9741
  request.headers.get("f-render") === "tree";
9621
9742
  // A navigation the browser streams marks where each React flush ends, so the browser
9622
- // shows the page before its Suspense content arrives (FARM_FLUSH_MARKER).
9623
- const farmFlushMarkers = request.headers.get("f-stream") === "flush";` : `const farmLayoutKeyOf = null;
9743
+ // shows the page before its Suspense content arrives (FARM_FLUSH_MARKER). The marker
9744
+ // carries a random token sent in the F-Flush header: page content cannot forge one.
9745
+ const farmFlushToken = request.headers.get("f-stream") === "flush"
9746
+ ? createFarmFlushToken()
9747
+ : "";` : `const farmLayoutKeyOf = null;
9624
9748
  const farmPayloadOnlyNavigation = false;
9625
- const farmFlushMarkers = false;`}
9749
+ const farmFlushToken = "";`}
9626
9750
  const renderPageElement = async () => {
9627
9751
  // First, render the page content
9628
9752
  let pageElement;
@@ -9744,7 +9868,7 @@ ${hasServerRuntimeIntegrations ? `
9744
9868
  }
9745
9869
 
9746
9870
  wrappedElement = wrapWithFarmIntegrationProviders(wrappedElement);
9747
- return renderFarmElement(ReactDOMServer, wrappedElement, route.pattern, farmFlushMarkers);
9871
+ return renderFarmElement(ReactDOMServer, wrappedElement, route.pattern, farmFlushToken);
9748
9872
  };
9749
9873
  const renderedPage = ${hasMiddlewareRuntime ? `await _runWithMiddlewareData(middlewareData, () =>
9750
9874
  _runWithMiddlewareContext(middlewareContext, renderPageElement)
@@ -10051,7 +10175,11 @@ ${hasServerRuntimeIntegrations ? `
10051
10175
  );
10052
10176
  return applyProductionMiddlewareHeaders(farmOwnedResponse(streamedDocument, {
10053
10177
  status: pageStatus,
10054
- headers: { ...responseHeaders, "x-farm-preload-streaming": "1" },
10178
+ headers: {
10179
+ ...responseHeaders,
10180
+ "x-farm-preload-streaming": "1",
10181
+ ...(farmFlushToken ? { "F-Flush": farmFlushToken } : {}),
10182
+ },
10055
10183
  }), middlewareHeaders);
10056
10184
  }
10057
10185
 
@@ -10576,10 +10704,11 @@ async function handleFarmFetch(request, context) {
10576
10704
  ? getFarmRoutePathname(new URL(runtimeRequest.url).pathname)
10577
10705
  : undefined;
10578
10706
  const configured = applyConfiguredResponseHeaders(response, routePathname);
10707
+ // Configured headers may edit the response in place, so check it either way.
10708
+ const guarded = farmGuardedResponse ? _withFarmGuardedResponseCache(configured) : configured;
10579
10709
  return applyFarmPreloadBudget(
10580
10710
  applyFarmCspNonceToResponse(
10581
- // Configured headers may edit the response in place, so check it either way.
10582
- farmGuardedResponse ? _withFarmGuardedResponseCache(configured) : configured,
10711
+ applyFarmMobileShellResponse(runtimeRequest, guarded, context),
10583
10712
  farmSecurityConfig,
10584
10713
  cspNonce,
10585
10714
  ),
@@ -10886,8 +11015,20 @@ async function buildNitroUniversal(config, routeManager, apiRouteManager, server
10886
11015
  ]);
10887
11016
  const encodedDocsModulePreloads = encodeFarmDocsModulePreloads(docsModulePreloads);
10888
11017
  const encodedClientModulePreloads = encodeFarmClientModulePreloads(clientModulePreloads);
11018
+ const encodedMobileShellClient = shouldBuildFarmMobileClient(config, { quiet: true }) ? encodeFarmMobileShellClient(
11019
+ await finalizeFarmMobileClient({
11020
+ buildDir: path10.join(root, distDir),
11021
+ clientOutputDir,
11022
+ webEntry: clientJsSrc,
11023
+ stylesheet: clientCssHref,
11024
+ webFiles: clientModulePreloads?.files ?? []
11025
+ })
11026
+ ) : null;
10889
11027
  for (const output of Object.values(ssrBundle)) {
10890
11028
  if (output.type !== "chunk") continue;
11029
+ if (encodedMobileShellClient && output.code.includes(FARM_MOBILE_SHELL_CLIENT_PLACEHOLDER)) {
11030
+ output.code = output.code.split(FARM_MOBILE_SHELL_CLIENT_PLACEHOLDER).join(encodedMobileShellClient);
11031
+ }
10891
11032
  if (output.code.includes(FARM_DOCS_MODULE_PRELOADS_PLACEHOLDER)) {
10892
11033
  output.code = output.code.split(FARM_DOCS_MODULE_PRELOADS_PLACEHOLDER).join(encodedDocsModulePreloads);
10893
11034
  }
@@ -10902,11 +11043,13 @@ async function buildNitroUniversal(config, routeManager, apiRouteManager, server
10902
11043
  }
10903
11044
  }
10904
11045
  await fs4.mkdir(ssrOutputDir, { recursive: true });
10905
- for (const [fileName, output] of Object.entries(ssrBundle)) {
10906
- const filePath = path10.join(ssrOutputDir, fileName);
10907
- await fs4.mkdir(path10.dirname(filePath), { recursive: true });
10908
- await fs4.writeFile(filePath, output.type === "chunk" ? output.code : output.source);
10909
- }
11046
+ await Promise.all(
11047
+ Object.entries(ssrBundle).map(async ([fileName, output]) => {
11048
+ const filePath = path10.join(ssrOutputDir, fileName);
11049
+ await fs4.mkdir(path10.dirname(filePath), { recursive: true });
11050
+ await fs4.writeFile(filePath, output.type === "chunk" ? output.code : output.source);
11051
+ })
11052
+ );
10910
11053
  const nitroEntryPath = path10.join(ssrOutputDir, "nitro-entry.mjs");
10911
11054
  const nodeAdapterModule = preset === "node-server" ? resolveNitroRuntimeDependency(root, "srvx/node") : null;
10912
11055
  const hasNitroCacheRouteRules = hasFarmNitroCacheRouteRule(config.routeRules);
@@ -11163,7 +11306,7 @@ function toFarmNodeResponse(request, response) {
11163
11306
  ssrOutputDir,
11164
11307
  ssrEntryFile
11165
11308
  );
11166
- const configuredNitroRouteRules = routeRulesToNitroRouteRules(config.routeRules);
11309
+ const configuredNitroRouteRules = shouldBuildFarmMobileClient(config, { quiet: true }) ? varyFarmNitroCacheRulesByShell(routeRulesToNitroRouteRules(config.routeRules)) : routeRulesToNitroRouteRules(config.routeRules);
11167
11310
  const prerenderNitroRouteRules = Object.fromEntries(
11168
11311
  prerenderRoutes.map((route) => {
11169
11312
  const configuredRule = configuredNitroRouteRules[route] || {};
@@ -11210,10 +11353,18 @@ function toFarmNodeResponse(request, response) {
11210
11353
  // fixed top quality cost a docs app ~20 s of build CPU for a small further
11211
11354
  // saving. Serverless and edge targets sit behind a CDN that compresses.
11212
11355
  ...preset === "node-server" ? { compressPublicAssets: { gzip: true } } : {},
11356
+ // Nitro gzips every server output file at level 9, node_modules included, to
11357
+ // print sizes after the build. A self-hosted server is never shipped compressed,
11358
+ // so list plain sizes; zipped serverless and edge bundles keep the gzip column.
11359
+ ...preset === "node-server" || preset === "bun" ? { logging: { compressedSizes: false } } : {},
11213
11360
  ...prerenderRoutes.length > 0 ? {
11214
11361
  prerender: {
11215
11362
  crawlLinks: false,
11216
11363
  failOnError: true,
11364
+ // Nitro's default renders one route at a time. Each prerender request is
11365
+ // answered independently, as by the production server, so keeping several
11366
+ // in flight overlaps the worker's rendering with this thread's per-route work.
11367
+ concurrency: Math.min(os.availableParallelism(), 8),
11217
11368
  routes: prerenderRoutes
11218
11369
  }
11219
11370
  } : {},
@@ -11311,6 +11462,22 @@ function toFarmNodeResponse(request, response) {
11311
11462
  }
11312
11463
  nitroConfig.minify = useFarmServerMinifier ? false : shouldMinify;
11313
11464
  const nitroInstance = await nitroBuilder.createNitro(nitroConfig);
11465
+ let prerenderOrder = /* @__PURE__ */ new Map();
11466
+ nitroInstance.hooks.hook("prerender:routes", (routes) => {
11467
+ prerenderOrder = new Map(
11468
+ Array.from(routes, (route, index) => {
11469
+ try {
11470
+ return [decodeURI(route), index];
11471
+ } catch {
11472
+ return [route, index];
11473
+ }
11474
+ })
11475
+ );
11476
+ });
11477
+ nitroInstance.hooks.hook("prerender:done", ({ prerenderedRoutes }) => {
11478
+ const orderOf = (route) => prerenderOrder.get(route) ?? Number.MAX_SAFE_INTEGER;
11479
+ prerenderedRoutes.sort((a, b) => orderOf(a.route) - orderOf(b.route));
11480
+ });
11314
11481
  if (config.md?.enabled) {
11315
11482
  nitroInstance.hooks.hook("prerender:generate", (route) => {
11316
11483
  if (!route.contents || !route.contentType?.toLowerCase().includes("html")) return;
@@ -11918,7 +12085,9 @@ async function createFarmProjectModuleSsrEnvironment() {
11918
12085
 
11919
12086
  // src/build/index.ts
11920
12087
  async function build(config, options = {}) {
11921
- return withProductionNodeEnv(() => buildWithProductionNodeEnv(config, options));
12088
+ return withProductionNodeEnv(
12089
+ () => withPackageClientBoundaryCache(() => buildWithProductionNodeEnv(config, options))
12090
+ );
11922
12091
  }
11923
12092
  function createBuildTargetBaseline(config) {
11924
12093
  const preset = config.preset || "node-server";
@@ -11991,6 +12160,9 @@ async function buildWithProductionNodeEnv(inputConfig, options) {
11991
12160
  if (config.docs?.enabled) await loadFarmDocsEngine();
11992
12161
  pluginManager.updateContext({ config });
11993
12162
  const { root, preset, srcDir, distDir, deployOutputDir } = targets;
12163
+ usePackageClientBoundaryCacheFile(
12164
+ path11.resolve(root, distDir, "cache", "client-boundaries.json")
12165
+ );
11994
12166
  logger.info(`\u{1F69C} Building Farm.js application with preset: ${preset}...`);
11995
12167
  await pluginManager.setupPlugins();
11996
12168
  await pluginManager.runHookParallel("beforeBundle", {