@farm.js/core 0.1.0-beta.11 → 0.1.0-beta.13

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 (237) hide show
  1. package/dist/{chunk-5EVQFRL2.mjs → chunk-3WIRO3N7.mjs} +2 -2
  2. package/dist/chunk-3WIRO3N7.mjs.map +1 -0
  3. package/dist/chunk-53MVSX3R.mjs +168 -0
  4. package/dist/chunk-53MVSX3R.mjs.map +1 -0
  5. package/dist/{chunk-AW6I64AP.mjs → chunk-6ZXIQSXE.mjs} +16 -1
  6. package/dist/chunk-6ZXIQSXE.mjs.map +1 -0
  7. package/dist/chunk-7ZVN3YZI.mjs +333 -0
  8. package/dist/chunk-7ZVN3YZI.mjs.map +1 -0
  9. package/dist/{chunk-7N62DNSO.mjs → chunk-H3TE3LOV.mjs} +2 -2
  10. package/dist/{chunk-QTZZ2ISQ.mjs → chunk-KLA67WKG.mjs} +1 -1
  11. package/dist/chunk-KLA67WKG.mjs.map +1 -0
  12. package/dist/{chunk-K5ZTUF22.mjs → chunk-L2PE36GQ.mjs} +7 -3
  13. package/dist/chunk-L2PE36GQ.mjs.map +1 -0
  14. package/dist/{chunk-4LND3PXF.mjs → chunk-OPHK6OJN.mjs} +513 -48
  15. package/dist/chunk-OPHK6OJN.mjs.map +1 -0
  16. package/dist/chunk-USLFXFFH.mjs +46 -0
  17. package/dist/chunk-USLFXFFH.mjs.map +1 -0
  18. package/dist/{chunk-4677KXB7.mjs → chunk-VSDZEB5M.mjs} +368 -103
  19. package/dist/chunk-VSDZEB5M.mjs.map +1 -0
  20. package/dist/{chunk-77BOAQHI.mjs → chunk-Z3YG4OZT.mjs} +725 -147
  21. package/dist/chunk-Z3YG4OZT.mjs.map +1 -0
  22. package/dist/client.cjs.map +1 -1
  23. package/dist/client.mjs +1 -1
  24. package/dist/{devtools-A22KUJZT.mjs → devtools-GAZ7F3KL.mjs} +4 -3
  25. package/dist/{devtools-A22KUJZT.mjs.map → devtools-GAZ7F3KL.mjs.map} +1 -1
  26. package/dist/docs.cjs +622 -124
  27. package/dist/docs.cjs.map +1 -1
  28. package/dist/docs.mjs +5 -3
  29. package/dist/font.cjs +19 -2
  30. package/dist/font.cjs.map +1 -1
  31. package/dist/font.mjs +7 -3
  32. package/dist/index.cjs +2206 -680
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.mjs +27 -19
  35. package/dist/index.mjs.map +1 -1
  36. package/dist/internal/build-runtime.cjs +2033 -516
  37. package/dist/internal/build-runtime.cjs.map +1 -1
  38. package/dist/internal/build-runtime.mjs +13 -10
  39. package/dist/internal/client-runtime.cjs +169 -2
  40. package/dist/internal/client-runtime.cjs.map +1 -1
  41. package/dist/internal/client-runtime.mjs +170 -2
  42. package/dist/internal/client-runtime.mjs.map +1 -1
  43. package/dist/internal/config-runtime.cjs +22 -0
  44. package/dist/internal/config-runtime.cjs.map +1 -1
  45. package/dist/internal/config-runtime.mjs +3 -2
  46. package/dist/internal/metadata-image-runtime.cjs +199 -0
  47. package/dist/internal/metadata-image-runtime.cjs.map +1 -0
  48. package/dist/internal/metadata-image-runtime.mjs +8 -0
  49. package/dist/internal/metadata-image-runtime.mjs.map +1 -0
  50. package/dist/internal/production-runtime.cjs +310 -0
  51. package/dist/internal/production-runtime.cjs.map +1 -1
  52. package/dist/internal/production-runtime.mjs +10 -0
  53. package/dist/internal/production-runtime.mjs.map +1 -1
  54. package/dist/navigation.cjs.map +1 -1
  55. package/dist/navigation.mjs +2 -2
  56. package/dist/plugin.cjs +1521 -339
  57. package/dist/plugin.cjs.map +1 -1
  58. package/dist/plugin.mjs +9 -5
  59. package/dist/server.cjs +1634 -410
  60. package/dist/server.cjs.map +1 -1
  61. package/dist/server.mjs +17 -19
  62. package/dist/server.mjs.map +1 -1
  63. package/dist/version.cjs +1 -1
  64. package/dist/version.cjs.map +1 -1
  65. package/dist/version.mjs +1 -1
  66. package/dist/vite.cjs +1575 -349
  67. package/dist/vite.cjs.map +1 -1
  68. package/dist/vite.mjs +9 -5
  69. package/package.json +10 -1
  70. package/dist/after.d.mts +0 -25
  71. package/dist/after.d.ts +0 -25
  72. package/dist/agent-runtime.d.mts +0 -94
  73. package/dist/agent-runtime.d.ts +0 -94
  74. package/dist/api/route-manager.d.mts +0 -66
  75. package/dist/api/route-manager.d.ts +0 -66
  76. package/dist/api/runtime.d.mts +0 -3
  77. package/dist/api/runtime.d.ts +0 -3
  78. package/dist/api.d.mts +0 -48
  79. package/dist/api.d.ts +0 -48
  80. package/dist/app-BNE2FoZ4.d.mts +0 -302
  81. package/dist/app-DRGUmJ3s.d.ts +0 -302
  82. package/dist/app-markdown.d.mts +0 -22
  83. package/dist/app-markdown.d.ts +0 -22
  84. package/dist/cache.d.mts +0 -192
  85. package/dist/cache.d.ts +0 -192
  86. package/dist/chunk-4677KXB7.mjs.map +0 -1
  87. package/dist/chunk-4LND3PXF.mjs.map +0 -1
  88. package/dist/chunk-5EVQFRL2.mjs.map +0 -1
  89. package/dist/chunk-77BOAQHI.mjs.map +0 -1
  90. package/dist/chunk-AW6I64AP.mjs.map +0 -1
  91. package/dist/chunk-K5ZTUF22.mjs.map +0 -1
  92. package/dist/chunk-QM746KUO.mjs +0 -56
  93. package/dist/chunk-QM746KUO.mjs.map +0 -1
  94. package/dist/chunk-QTZZ2ISQ.mjs.map +0 -1
  95. package/dist/client-BT1mFQas.d.mts +0 -447
  96. package/dist/client-Nn0q4eO4.d.ts +0 -447
  97. package/dist/client-cache-CIlyBlmx.d.ts +0 -6
  98. package/dist/client-cache-D-EZ7XVW.d.mts +0 -6
  99. package/dist/client-plugins-Bc5jHcNr.d.ts +0 -1927
  100. package/dist/client-plugins-jsk-ZLzJ.d.mts +0 -1927
  101. package/dist/client-plugins.d.mts +0 -22
  102. package/dist/client-plugins.d.ts +0 -22
  103. package/dist/client.d.mts +0 -447
  104. package/dist/client.d.ts +0 -447
  105. package/dist/config.d.mts +0 -29
  106. package/dist/config.d.ts +0 -29
  107. package/dist/cron.d.mts +0 -78
  108. package/dist/cron.d.ts +0 -78
  109. package/dist/deferred.d.mts +0 -42
  110. package/dist/deferred.d.ts +0 -42
  111. package/dist/deployment.d.mts +0 -34
  112. package/dist/deployment.d.ts +0 -34
  113. package/dist/docs.d.mts +0 -69
  114. package/dist/docs.d.ts +0 -69
  115. package/dist/env-types.d.mts +0 -12
  116. package/dist/env-types.d.ts +0 -12
  117. package/dist/env.d.mts +0 -45
  118. package/dist/env.d.ts +0 -45
  119. package/dist/environment/vite.d.mts +0 -22
  120. package/dist/environment/vite.d.ts +0 -22
  121. package/dist/environment.d.mts +0 -18
  122. package/dist/environment.d.ts +0 -18
  123. package/dist/font.d.mts +0 -70
  124. package/dist/font.d.ts +0 -70
  125. package/dist/headers.d.mts +0 -22
  126. package/dist/headers.d.ts +0 -22
  127. package/dist/i18n/client.d.mts +0 -32
  128. package/dist/i18n/client.d.ts +0 -32
  129. package/dist/i18n/index.d.mts +0 -20
  130. package/dist/i18n/index.d.ts +0 -20
  131. package/dist/i18n/server.d.mts +0 -30
  132. package/dist/i18n/server.d.ts +0 -30
  133. package/dist/image/server.d.mts +0 -37
  134. package/dist/image/server.d.ts +0 -37
  135. package/dist/image/sharp.d.mts +0 -7
  136. package/dist/image/sharp.d.ts +0 -7
  137. package/dist/image-config-Duob7wER.d.mts +0 -50
  138. package/dist/image-config-Duob7wER.d.ts +0 -50
  139. package/dist/image.d.mts +0 -39
  140. package/dist/image.d.ts +0 -39
  141. package/dist/index.d.mts +0 -455
  142. package/dist/index.d.ts +0 -455
  143. package/dist/integration-api-CHT2OKAf.d.mts +0 -93
  144. package/dist/integration-api-CHT2OKAf.d.ts +0 -93
  145. package/dist/integrations.d.mts +0 -22
  146. package/dist/integrations.d.ts +0 -22
  147. package/dist/internal/build-runtime.d.mts +0 -39
  148. package/dist/internal/build-runtime.d.ts +0 -39
  149. package/dist/internal/client-runtime.d.mts +0 -2
  150. package/dist/internal/client-runtime.d.ts +0 -2
  151. package/dist/internal/config-runtime.d.mts +0 -39
  152. package/dist/internal/config-runtime.d.ts +0 -39
  153. package/dist/internal/production-node-env.d.mts +0 -12
  154. package/dist/internal/production-node-env.d.ts +0 -12
  155. package/dist/internal/production-runtime.d.mts +0 -41
  156. package/dist/internal/production-runtime.d.ts +0 -41
  157. package/dist/markdown.d.mts +0 -60
  158. package/dist/markdown.d.ts +0 -60
  159. package/dist/metadata-7ffpT3jr.d.mts +0 -22
  160. package/dist/metadata-JHn-8eht.d.ts +0 -22
  161. package/dist/middleware.d.mts +0 -164
  162. package/dist/middleware.d.ts +0 -164
  163. package/dist/navigation-errors-Dvwpzaer.d.mts +0 -13
  164. package/dist/navigation-errors-Dvwpzaer.d.ts +0 -13
  165. package/dist/navigation.d.mts +0 -14
  166. package/dist/navigation.d.ts +0 -14
  167. package/dist/observability.d.mts +0 -335
  168. package/dist/observability.d.ts +0 -335
  169. package/dist/params-BALtrRim.d.ts +0 -15
  170. package/dist/params-ry-lxqmy.d.mts +0 -15
  171. package/dist/plugin-BJDf43dc.d.mts +0 -165
  172. package/dist/plugin-BJDf43dc.d.ts +0 -165
  173. package/dist/plugin.d.mts +0 -22
  174. package/dist/plugin.d.ts +0 -22
  175. package/dist/production-runtime-9KG7GuvW.d.ts +0 -79
  176. package/dist/production-runtime-B-fweKts.d.mts +0 -79
  177. package/dist/production-runtime-C_B79w2P.d.mts +0 -28
  178. package/dist/production-runtime-DDEM1D2q.d.ts +0 -28
  179. package/dist/query/client.d.mts +0 -61
  180. package/dist/query/client.d.ts +0 -61
  181. package/dist/query/index.d.mts +0 -90
  182. package/dist/query/index.d.ts +0 -90
  183. package/dist/query/parsers.d.mts +0 -25
  184. package/dist/query/parsers.d.ts +0 -25
  185. package/dist/query/server.d.mts +0 -55
  186. package/dist/query/server.d.ts +0 -55
  187. package/dist/request-Dsq4kGW0.d.mts +0 -6
  188. package/dist/request-udtobOLv.d.ts +0 -6
  189. package/dist/request.d.mts +0 -2
  190. package/dist/request.d.ts +0 -2
  191. package/dist/resolver-DJqjLMg_.d.mts +0 -14
  192. package/dist/resolver-IH4RmPEl.d.ts +0 -14
  193. package/dist/route-runtime-DoOHzCAD.d.mts +0 -38
  194. package/dist/route-runtime-DoOHzCAD.d.ts +0 -38
  195. package/dist/router.d.mts +0 -36
  196. package/dist/router.d.ts +0 -36
  197. package/dist/routes.d.mts +0 -224
  198. package/dist/routes.d.ts +0 -224
  199. package/dist/runtime-9nQjVM7X.d.mts +0 -206
  200. package/dist/runtime-Cxpq6xZN.d.mts +0 -27
  201. package/dist/runtime-DgeS2skB.d.ts +0 -206
  202. package/dist/runtime-bv63eUiO.d.ts +0 -27
  203. package/dist/server-action-security.d.mts +0 -43
  204. package/dist/server-action-security.d.ts +0 -43
  205. package/dist/server-fn-client.d.mts +0 -32
  206. package/dist/server-fn-client.d.ts +0 -32
  207. package/dist/server-fn.d.mts +0 -93
  208. package/dist/server-fn.d.ts +0 -93
  209. package/dist/server-plugins.d.mts +0 -55
  210. package/dist/server-plugins.d.ts +0 -55
  211. package/dist/server-query-client.d.mts +0 -50
  212. package/dist/server-query-client.d.ts +0 -50
  213. package/dist/server-query.d.mts +0 -36
  214. package/dist/server-query.d.ts +0 -36
  215. package/dist/server.d.mts +0 -48
  216. package/dist/server.d.ts +0 -48
  217. package/dist/storage.d.mts +0 -52
  218. package/dist/storage.d.ts +0 -52
  219. package/dist/testing.d.mts +0 -101
  220. package/dist/testing.d.ts +0 -101
  221. package/dist/transport-BmbGTXA5.d.mts +0 -49
  222. package/dist/transport-BmbGTXA5.d.ts +0 -49
  223. package/dist/types-BMxWr97v.d.mts +0 -81
  224. package/dist/types-BMxWr97v.d.ts +0 -81
  225. package/dist/types-C9N-nk5R.d.mts +0 -53
  226. package/dist/types-C9N-nk5R.d.ts +0 -53
  227. package/dist/types-D1A6-XvH.d.mts +0 -73
  228. package/dist/types-D1A6-XvH.d.ts +0 -73
  229. package/dist/utils-CA0z0c-2.d.ts +0 -23
  230. package/dist/utils-mYVkSOt2.d.mts +0 -23
  231. package/dist/version.d.mts +0 -3
  232. package/dist/version.d.ts +0 -3
  233. package/dist/vite.d.mts +0 -22
  234. package/dist/vite.d.ts +0 -22
  235. package/dist/workflows.d.mts +0 -110
  236. package/dist/workflows.d.ts +0 -110
  237. /package/dist/{chunk-7N62DNSO.mjs.map → chunk-H3TE3LOV.mjs.map} +0 -0
@@ -6,6 +6,12 @@ import {
6
6
  mergeMetadata,
7
7
  renderMetadataHead
8
8
  } from "./chunk-FRPWCMMG.mjs";
9
+ import {
10
+ createFarmMetadataImageResponse
11
+ } from "./chunk-53MVSX3R.mjs";
12
+ import {
13
+ resolveFarmLayoutFonts
14
+ } from "./chunk-6ZXIQSXE.mjs";
9
15
  import {
10
16
  farmEnvironmentFunctionsPlugin
11
17
  } from "./chunk-V6WE6JSZ.mjs";
@@ -24,9 +30,18 @@ import {
24
30
  isFarmNotFoundError,
25
31
  isFarmRedirectError
26
32
  } from "./chunk-7HO6AEOP.mjs";
33
+ import {
34
+ resolveFarmDocsFontAssets,
35
+ toFarmDocsPublicFontAssets
36
+ } from "./chunk-USLFXFFH.mjs";
27
37
  import {
28
38
  resolveFarmAuthConfig
29
39
  } from "./chunk-I4CUBGVF.mjs";
40
+ import {
41
+ FARM_DEVTOOLS_LAUNCH_PARAM,
42
+ FARM_DEVTOOLS_PATH,
43
+ resolveFarmDevtoolsConfig
44
+ } from "./chunk-7MABZJIM.mjs";
30
45
  import {
31
46
  normalizeRouteRules
32
47
  } from "./chunk-YW7OBE6Q.mjs";
@@ -36,10 +51,8 @@ import {
36
51
  getFarmSourceRoots
37
52
  } from "./chunk-74YTBPTY.mjs";
38
53
  import {
39
- FARM_DEVTOOLS_LAUNCH_PARAM,
40
- FARM_DEVTOOLS_PATH,
41
- resolveFarmDevtoolsConfig
42
- } from "./chunk-7MABZJIM.mjs";
54
+ resolveFarmPerformanceConfig
55
+ } from "./chunk-7ZVN3YZI.mjs";
43
56
  import {
44
57
  getPublicFarmImageConfig,
45
58
  resolveFarmImageConfig
@@ -147,7 +160,7 @@ import {
147
160
  } from "./chunk-RHIFZZUI.mjs";
148
161
  import {
149
162
  FARM_VERSION
150
- } from "./chunk-5EVQFRL2.mjs";
163
+ } from "./chunk-3WIRO3N7.mjs";
151
164
  import {
152
165
  FARM_DEPLOYMENT_ID_HEADER,
153
166
  createFarmDeploymentCookie,
@@ -185,6 +198,22 @@ import { readFile } from "fs/promises";
185
198
  var OPTIONAL_CATCH_ALL_SEGMENT = /^\[\[\.\.\.([^\]]+)\]\]$/;
186
199
  var REQUIRED_CATCH_ALL_SEGMENT = /^\[\.\.\.([^\]]+)\]$/;
187
200
  var REQUIRED_DYNAMIC_SEGMENT = /^\[([^\]]+)\]$/;
201
+ var REQUEST_API_LABELS = /* @__PURE__ */ new Map([
202
+ ["cookies", "request cookies"],
203
+ ["headers", "request headers"],
204
+ ["getCurrentRequest", "the current Request"],
205
+ ["useSearchParams", "URL search parameters"],
206
+ ["auth", "request authentication"],
207
+ ["getServerSession", "request authentication"],
208
+ ["getSession", "request authentication"],
209
+ ["currentUser", "request authentication"]
210
+ ]);
211
+ var ROUTE_PROP_LABELS = /* @__PURE__ */ new Map([
212
+ ["searchParams", "URL search parameters"],
213
+ ["middleware", "middleware request data"],
214
+ ["middlewareData", "middleware request data"],
215
+ ["context", "request context"]
216
+ ]);
188
217
  function resolveRouteRenderingConfig(mod, source) {
189
218
  const directiveConfig = parseRouteRenderingDirective(source);
190
219
  let ssg = directiveConfig?.ssg ?? false;
@@ -237,6 +266,221 @@ async function resolveRouteRenderingConfigFromFile(mod, filePath) {
237
266
  return resolveRouteRenderingConfig(mod, source);
238
267
  }
239
268
  __name(resolveRouteRenderingConfigFromFile, "resolveRouteRenderingConfigFromFile");
269
+ function analyzeStaticRouteCandidate(mod, source, options = {}) {
270
+ const blockers = [];
271
+ if (!source) {
272
+ blockers.push("route source is unavailable");
273
+ }
274
+ if (options.isDynamic) {
275
+ blockers.push("the route has dynamic path segments");
276
+ }
277
+ if (hasExplicitRenderingConfig(mod, source)) {
278
+ blockers.push("the route already has explicit rendering config");
279
+ }
280
+ if (source) {
281
+ blockers.push(...findRequestBoundSourceBlockers(source));
282
+ }
283
+ return {
284
+ candidate: blockers.length === 0,
285
+ blockers
286
+ };
287
+ }
288
+ __name(analyzeStaticRouteCandidate, "analyzeStaticRouteCandidate");
289
+ function findRequestBoundSourceBlockers(source) {
290
+ const blockers = /* @__PURE__ */ new Set();
291
+ const code = stripCommentsAndLiteralContents(source);
292
+ const sourceWithoutComments = stripComments(source);
293
+ for (const binding of readRequestApiImportBindings(sourceWithoutComments)) {
294
+ if (new RegExp(`\\b${escapeRegExp(binding.local)}\\s*(?:\\?\\.\\s*)?\\(`).test(code)) {
295
+ blockers.add(`the route reads ${binding.label}`);
296
+ }
297
+ }
298
+ for (const namespace of readNamespaceImportBindings(sourceWithoutComments)) {
299
+ for (const [name, label] of REQUEST_API_LABELS) {
300
+ if (new RegExp(
301
+ `\\b${escapeRegExp(namespace)}\\s*(?:\\?\\.\\s*|\\.\\s*)${name}\\s*(?:\\?\\.\\s*)?\\(`
302
+ ).test(code)) {
303
+ blockers.add(`the route reads ${label}`);
304
+ }
305
+ }
306
+ }
307
+ const params = readDefaultRouteParameters(code);
308
+ if (params) {
309
+ for (const [name, label] of ROUTE_PROP_LABELS) {
310
+ if (new RegExp(`\\b${name}\\b`).test(params)) {
311
+ blockers.add(`the route reads ${label}`);
312
+ }
313
+ }
314
+ const propsName = params.match(/^\s*([A-Za-z_$][\w$]*)\s*(?::|,|$)/)?.[1];
315
+ if (propsName) {
316
+ for (const [name, label] of ROUTE_PROP_LABELS) {
317
+ if (new RegExp(`\\b${escapeRegExp(propsName)}\\s*\\.\\s*${name}\\b`).test(code)) {
318
+ blockers.add(`the route reads ${label}`);
319
+ }
320
+ if (new RegExp(`\\{[^}]*\\b${name}\\b[^}]*\\}\\s*=\\s*${escapeRegExp(propsName)}\\b`).test(
321
+ code
322
+ )) {
323
+ blockers.add(`the route reads ${label}`);
324
+ }
325
+ }
326
+ }
327
+ }
328
+ return [...blockers];
329
+ }
330
+ __name(findRequestBoundSourceBlockers, "findRequestBoundSourceBlockers");
331
+ function readRequestApiImportBindings(source) {
332
+ const bindings = [];
333
+ for (const clause of readStaticImportClauses(source)) {
334
+ const namedGroup = clause.match(/\{([\s\S]*?)\}/)?.[1];
335
+ for (const rawSpecifier of (namedGroup ?? "").split(",")) {
336
+ const specifier = rawSpecifier.trim().replace(/^type\s+/, "");
337
+ const imported = specifier.match(/^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
338
+ if (!imported?.[1]) continue;
339
+ const label = REQUEST_API_LABELS.get(imported[1]);
340
+ if (label) bindings.push({ local: imported[2] ?? imported[1], label });
341
+ }
342
+ const local = clause.match(/^\s*([A-Za-z_$][\w$]*)\s*(?:,|$)/)?.[1];
343
+ if (local) {
344
+ const label = REQUEST_API_LABELS.get(local);
345
+ if (label) bindings.push({ local, label });
346
+ }
347
+ }
348
+ return bindings;
349
+ }
350
+ __name(readRequestApiImportBindings, "readRequestApiImportBindings");
351
+ function readNamespaceImportBindings(source) {
352
+ return readStaticImportClauses(source).map((clause) => clause.match(/\*\s*as\s*([A-Za-z_$][\w$]*)/)?.[1]).filter((binding) => Boolean(binding));
353
+ }
354
+ __name(readNamespaceImportBindings, "readNamespaceImportBindings");
355
+ function readStaticImportClauses(source) {
356
+ return [...source.matchAll(/\bimport\s+(?!\()([\s\S]*?)\s+from\s*["'][^"']+["']/g)].map((match) => match[1]).filter((clause) => Boolean(clause));
357
+ }
358
+ __name(readStaticImportClauses, "readStaticImportClauses");
359
+ function readDefaultRouteParameters(code) {
360
+ const inlineParams = code.match(/\bexport\s+default\s+(?:async\s+)?function\b[^(]*\(([^)]*)\)/)?.[1] ?? code.match(/\bexport\s+default\s+(?:async\s+)?\(([^)]*)\)\s*=>/)?.[1];
361
+ if (inlineParams !== void 0) return inlineParams;
362
+ const binding = code.match(/\bexport\s+default\s+([A-Za-z_$][\w$]*)\s*;?/)?.[1] ?? code.match(/\bexport\s*\{\s*([A-Za-z_$][\w$]*)\s+as\s+default\s*\}/)?.[1];
363
+ if (!binding) return void 0;
364
+ const escapedBinding = escapeRegExp(binding);
365
+ return code.match(new RegExp(`\\bfunction\\s+${escapedBinding}\\s*\\(([^)]*)\\)`))?.[1] ?? code.match(
366
+ new RegExp(
367
+ `\\b(?:const|let|var)\\s+${escapedBinding}\\s*=\\s*(?:async\\s+)?\\(([^)]*)\\)\\s*=>`
368
+ )
369
+ )?.[1] ?? code.match(
370
+ new RegExp(`\\b(?:const|let|var)\\s+${escapedBinding}\\s*=\\s*function\\s*\\(([^)]*)\\)`)
371
+ )?.[1];
372
+ }
373
+ __name(readDefaultRouteParameters, "readDefaultRouteParameters");
374
+ function stripCommentsAndLiteralContents(source) {
375
+ return sanitizeSource(source, true);
376
+ }
377
+ __name(stripCommentsAndLiteralContents, "stripCommentsAndLiteralContents");
378
+ function stripComments(source) {
379
+ return sanitizeSource(source, false);
380
+ }
381
+ __name(stripComments, "stripComments");
382
+ function sanitizeSource(source, maskLiterals) {
383
+ const output = [...source];
384
+ const mask = /* @__PURE__ */ __name((index) => {
385
+ if (output[index] !== "\n" && output[index] !== "\r") output[index] = " ";
386
+ }, "mask");
387
+ const scanQuoted = /* @__PURE__ */ __name((start, quote) => {
388
+ let index = start;
389
+ if (maskLiterals) mask(index);
390
+ index += 1;
391
+ while (index < source.length) {
392
+ if (source[index] === "\\") {
393
+ if (maskLiterals) mask(index);
394
+ if (index + 1 < source.length && maskLiterals) mask(index + 1);
395
+ index += 2;
396
+ continue;
397
+ }
398
+ const character = source[index];
399
+ if (maskLiterals) mask(index);
400
+ index += 1;
401
+ if (character === quote) break;
402
+ }
403
+ return index;
404
+ }, "scanQuoted");
405
+ const scanCode = /* @__PURE__ */ __name((start, stopAtTemplateBrace) => {
406
+ let index = start;
407
+ let braceDepth = 0;
408
+ while (index < source.length) {
409
+ const character = source[index];
410
+ const next = source[index + 1];
411
+ if (character === "/" && next === "/") {
412
+ while (index < source.length && source[index] !== "\n" && source[index] !== "\r") {
413
+ mask(index++);
414
+ }
415
+ continue;
416
+ }
417
+ if (character === "/" && next === "*") {
418
+ mask(index++);
419
+ mask(index++);
420
+ while (index < source.length) {
421
+ const closesComment = source[index] === "*" && source[index + 1] === "/";
422
+ mask(index++);
423
+ if (closesComment) {
424
+ mask(index++);
425
+ break;
426
+ }
427
+ }
428
+ continue;
429
+ }
430
+ if (character === "'" || character === '"') {
431
+ index = scanQuoted(index, character);
432
+ continue;
433
+ }
434
+ if (character === "`") {
435
+ if (maskLiterals) mask(index);
436
+ index += 1;
437
+ while (index < source.length) {
438
+ if (source[index] === "\\") {
439
+ if (maskLiterals) mask(index);
440
+ if (index + 1 < source.length && maskLiterals) mask(index + 1);
441
+ index += 2;
442
+ } else if (source[index] === "`") {
443
+ if (maskLiterals) mask(index);
444
+ index += 1;
445
+ break;
446
+ } else if (source[index] === "$" && source[index + 1] === "{") {
447
+ if (maskLiterals) {
448
+ mask(index);
449
+ mask(index + 1);
450
+ }
451
+ index = scanCode(index + 2, true);
452
+ } else {
453
+ if (maskLiterals) mask(index);
454
+ index += 1;
455
+ }
456
+ }
457
+ continue;
458
+ }
459
+ if (character === "{") {
460
+ braceDepth += 1;
461
+ } else if (character === "}") {
462
+ if (stopAtTemplateBrace && braceDepth === 0) {
463
+ if (maskLiterals) mask(index);
464
+ return index + 1;
465
+ }
466
+ braceDepth = Math.max(0, braceDepth - 1);
467
+ }
468
+ index += 1;
469
+ }
470
+ return index;
471
+ }, "scanCode");
472
+ scanCode(0, false);
473
+ return output.join("");
474
+ }
475
+ __name(sanitizeSource, "sanitizeSource");
476
+ function escapeRegExp(value) {
477
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
478
+ }
479
+ __name(escapeRegExp, "escapeRegExp");
480
+ function hasExplicitRenderingConfig(mod, source) {
481
+ return typeof mod?.ssg === "boolean" || typeof mod?.ppr === "boolean" || typeof mod?.experimental_ppr === "boolean" || typeof mod?.revalidate !== "undefined" || typeof mod?.dynamic !== "undefined" || typeof parseRouteRenderingDirective(source) !== "undefined";
482
+ }
483
+ __name(hasExplicitRenderingConfig, "hasExplicitRenderingConfig");
240
484
  function parseRouteRenderingDirective(source) {
241
485
  if (!source) {
242
486
  return void 0;
@@ -368,9 +612,10 @@ function skipHorizontalWhitespace(source, start) {
368
612
  return index;
369
613
  }
370
614
  __name(skipHorizontalWhitespace, "skipHorizontalWhitespace");
371
- async function collectSSGPages(routes, loadModule) {
615
+ async function collectSSGPages(routes, loadModule, options = {}) {
372
616
  const ssgPages = [];
373
617
  const ssrRoutes = [];
618
+ const staticCandidateRoutes = [];
374
619
  for (const route of routes) {
375
620
  try {
376
621
  const mod = await loadModule(route.filePath);
@@ -378,7 +623,8 @@ async function collectSSGPages(routes, loadModule) {
378
623
  ssrRoutes.push(route.path);
379
624
  continue;
380
625
  }
381
- const rendering = await resolveRouteRenderingConfigFromFile(mod, route.filePath);
626
+ const source = await readFile(route.filePath, "utf8").catch(() => void 0);
627
+ const rendering = resolveRouteRenderingConfig(mod, source);
382
628
  if (rendering.ssg) {
383
629
  if (route.isDynamic) {
384
630
  const getStaticPaths = mod.getStaticPaths || mod.generateStaticParams;
@@ -405,12 +651,26 @@ async function collectSSGPages(routes, loadModule) {
405
651
  }
406
652
  } else {
407
653
  ssrRoutes.push(route.path);
654
+ if (options.suggestStaticRendering !== false && analyzeStaticRouteCandidate(mod, source, { isDynamic: route.isDynamic }).candidate) {
655
+ staticCandidateRoutes.push(route.path);
656
+ }
408
657
  }
409
658
  } catch (error) {
410
659
  console.error(`Error processing route ${route.path}:`, error);
411
660
  ssrRoutes.push(route.path);
412
661
  }
413
662
  }
663
+ if (staticCandidateRoutes.length > 0) {
664
+ const visibleRoutes = staticCandidateRoutes.slice(0, 10);
665
+ const remainingCount = staticCandidateRoutes.length - visibleRoutes.length;
666
+ const remainingSuffix = remainingCount > 0 ? `
667
+ \u2026and ${remainingCount} more` : "";
668
+ console.warn(
669
+ `[Farm.js] ${staticCandidateRoutes.length} route${staticCandidateRoutes.length === 1 ? " appears" : "s appear"} eligible for static rendering:
670
+ ${visibleRoutes.join("\n ")}${remainingSuffix}
671
+ Add \`export const dynamic = "force-static";\` after verifying that imported code does not read cookies, headers, authentication, or other request data.`
672
+ );
673
+ }
414
674
  return { ssg: ssgPages, ssr: ssrRoutes };
415
675
  }
416
676
  __name(collectSSGPages, "collectSSGPages");
@@ -558,6 +818,15 @@ import path3 from "path";
558
818
  import fs from "fs";
559
819
  import path from "path";
560
820
  import { fileURLToPath } from "url";
821
+
822
+ // src/island.ts
823
+ var FARM_ISLAND_STRATEGIES = ["load", "interaction", "visible", "idle"];
824
+ function isFarmIslandStrategy(value) {
825
+ return FARM_ISLAND_STRATEGIES.includes(value);
826
+ }
827
+ __name(isFarmIslandStrategy, "isFarmIslandStrategy");
828
+
829
+ // src/utils/client-component.ts
561
830
  function readIfExists(filePath) {
562
831
  try {
563
832
  if (!filePath || !fs.existsSync(filePath)) {
@@ -628,56 +897,186 @@ function hasHydrateExport(content) {
628
897
  return /\bexport\s+const\s+hydrate\s*=\s*true\b/.test(content) || /\bexport\s*\{\s*hydrate\s*\}\b/.test(content);
629
898
  }
630
899
  __name(hasHydrateExport, "hasHydrateExport");
900
+ function tokenizeModuleSource(content) {
901
+ const tokens = [];
902
+ let index = 0;
903
+ let line = 1;
904
+ while (index < content.length) {
905
+ const character = content[index];
906
+ if (/\s/.test(character)) {
907
+ if (character === "\n") line++;
908
+ index++;
909
+ continue;
910
+ }
911
+ if (character === "/" && content[index + 1] === "/") {
912
+ index += 2;
913
+ while (index < content.length && content[index] !== "\n") index++;
914
+ continue;
915
+ }
916
+ if (character === "/" && content[index + 1] === "*") {
917
+ index += 2;
918
+ while (index < content.length) {
919
+ if (content[index] === "\n") line++;
920
+ if (content[index] === "*" && content[index + 1] === "/") {
921
+ index += 2;
922
+ break;
923
+ }
924
+ index++;
925
+ }
926
+ continue;
927
+ }
928
+ if (character === '"' || character === "'") {
929
+ const quote = character;
930
+ const tokenLine = line;
931
+ let value = "";
932
+ index++;
933
+ while (index < content.length) {
934
+ const next = content[index];
935
+ if (next === "\\") {
936
+ value += next;
937
+ if (index + 1 < content.length) value += content[index + 1];
938
+ index += 2;
939
+ continue;
940
+ }
941
+ if (next === quote) {
942
+ index++;
943
+ break;
944
+ }
945
+ if (next === "\n") line++;
946
+ value += next;
947
+ index++;
948
+ }
949
+ tokens.push({ kind: "string", value, line: tokenLine });
950
+ continue;
951
+ }
952
+ if (character === "`") {
953
+ index++;
954
+ while (index < content.length) {
955
+ const next = content[index];
956
+ if (next === "\\") {
957
+ index += 2;
958
+ continue;
959
+ }
960
+ if (next === "`") {
961
+ index++;
962
+ break;
963
+ }
964
+ if (next === "\n") line++;
965
+ index++;
966
+ }
967
+ continue;
968
+ }
969
+ if (/[A-Za-z_$]/.test(character)) {
970
+ const start = index++;
971
+ while (index < content.length && /[\w$]/.test(content[index])) index++;
972
+ tokens.push({ kind: "identifier", value: content.slice(start, index), line });
973
+ continue;
974
+ }
975
+ tokens.push({ kind: "punctuation", value: character, line });
976
+ index++;
977
+ }
978
+ return tokens;
979
+ }
980
+ __name(tokenizeModuleSource, "tokenizeModuleSource");
981
+ function isIslandExportStart(tokens, index) {
982
+ const token = tokens[index];
983
+ const previous = tokens[index - 1];
984
+ const startsStatement = !previous || previous.value === ";" || previous.value === "{" || previous.value === "}" || token.line > previous.line;
985
+ return startsStatement && token.value === "export" && tokens[index + 1]?.value === "const" && tokens[index + 2]?.value === "island";
986
+ }
987
+ __name(isIslandExportStart, "isIslandExportStart");
988
+ function getIslandStrategyExport(content) {
989
+ if (!content) return null;
990
+ const tokens = tokenizeModuleSource(content);
991
+ for (let index = 0; index < tokens.length - 2; index++) {
992
+ if (!isIslandExportStart(tokens, index)) continue;
993
+ let valueIndex = index + 3;
994
+ if (tokens[valueIndex]?.value === ":") {
995
+ while (valueIndex < tokens.length && tokens[valueIndex].value !== "=") valueIndex++;
996
+ }
997
+ if (tokens[valueIndex]?.value !== "=") break;
998
+ valueIndex++;
999
+ const literal = tokens[valueIndex];
1000
+ if (!literal || literal.kind !== "string" || !isFarmIslandStrategy(literal.value)) break;
1001
+ let trailingIndex = valueIndex + 1;
1002
+ if (tokens[trailingIndex]?.value === "as" && tokens[trailingIndex + 1]?.value === "const") {
1003
+ trailingIndex += 2;
1004
+ }
1005
+ const trailing = tokens[trailingIndex];
1006
+ if (trailing && trailing.value !== ";" && trailing.line === literal.line) break;
1007
+ return literal.value;
1008
+ }
1009
+ if (tokens.some((_token, index) => isIslandExportStart(tokens, index))) {
1010
+ throw new Error(
1011
+ 'Farm island configuration must be a static "load", "interaction", "visible", or "idle" string literal.'
1012
+ );
1013
+ }
1014
+ return null;
1015
+ }
1016
+ __name(getIslandStrategyExport, "getIslandStrategyExport");
631
1017
  function stripUseClientDirective(content) {
632
1018
  return content.replace(/^\s*(["'])use client\1\s*;?\s*/, "");
633
1019
  }
634
1020
  __name(stripUseClientDirective, "stripUseClientDirective");
635
- function parseClientModuleMetadata(content) {
1021
+ function parseClientModuleMetadata(content, inspectIslandExport) {
636
1022
  if (!content) {
637
1023
  return {
638
1024
  isClientComponent: false,
639
- hasHydrateExport: false
1025
+ hasHydrateExport: false,
1026
+ islandStrategy: null
640
1027
  };
641
1028
  }
1029
+ const isClientComponent = hasUseClientDirective(content);
1030
+ const hasHydrate = hasHydrateExport(content);
642
1031
  return {
643
- isClientComponent: hasUseClientDirective(content),
644
- hasHydrateExport: hasHydrateExport(content)
1032
+ isClientComponent,
1033
+ hasHydrateExport: hasHydrate,
1034
+ islandStrategy: inspectIslandExport || isClientComponent || hasHydrate ? getIslandStrategyExport(content) : null
645
1035
  };
646
1036
  }
647
1037
  __name(parseClientModuleMetadata, "parseClientModuleMetadata");
648
1038
  function getClientModuleMetadata(modulePath, root) {
649
1039
  const resolvedPath = resolveModuleSourcePath(modulePath, root);
650
- return inspectClientModuleMetadata(resolvedPath, root, /* @__PURE__ */ new Set());
1040
+ return inspectClientModuleMetadata(resolvedPath, root, /* @__PURE__ */ new Set(), true);
651
1041
  }
652
1042
  __name(getClientModuleMetadata, "getClientModuleMetadata");
653
- function inspectClientModuleMetadata(resolvedPath, root, visited) {
1043
+ function inspectClientModuleMetadata(resolvedPath, root, visited, inspectIslandExport) {
654
1044
  if (!resolvedPath || visited.has(resolvedPath)) {
655
1045
  return {
656
1046
  isClientComponent: false,
657
- shouldHydrate: false
1047
+ shouldHydrate: false,
1048
+ islandStrategy: null
658
1049
  };
659
1050
  }
660
1051
  visited.add(resolvedPath);
661
1052
  const content = readIfExists(resolvedPath);
662
- const parsed = parseClientModuleMetadata(content);
1053
+ const parsed = parseClientModuleMetadata(content, inspectIslandExport);
663
1054
  if (parsed.isClientComponent) {
664
1055
  return {
665
1056
  isClientComponent: true,
666
- shouldHydrate: true
1057
+ shouldHydrate: true,
1058
+ islandStrategy: parsed.islandStrategy ?? "load"
667
1059
  };
668
1060
  }
669
1061
  let importsClientBoundary = false;
1062
+ let importedIslandStrategy = null;
670
1063
  for (const specifier of getRelativeImportSpecifiers(content)) {
671
1064
  const importedPath = resolveImportedModuleSourcePath(resolvedPath, specifier, root);
672
- const importedMetadata = inspectClientModuleMetadata(importedPath, root, visited);
1065
+ const importedMetadata = inspectClientModuleMetadata(importedPath, root, visited, false);
673
1066
  if (importedMetadata.isClientComponent || importedMetadata.shouldHydrate) {
674
1067
  importsClientBoundary = true;
675
- break;
1068
+ if (importedIslandStrategy === null) {
1069
+ importedIslandStrategy = importedMetadata.islandStrategy;
1070
+ } else if (importedIslandStrategy !== importedMetadata.islandStrategy) {
1071
+ importedIslandStrategy = "load";
1072
+ }
676
1073
  }
677
1074
  }
1075
+ const shouldHydrate = parsed.hasHydrateExport || importsClientBoundary;
678
1076
  return {
679
1077
  isClientComponent: false,
680
- shouldHydrate: parsed.hasHydrateExport || importsClientBoundary
1078
+ shouldHydrate,
1079
+ islandStrategy: shouldHydrate ? parsed.islandStrategy ?? importedIslandStrategy ?? "load" : null
681
1080
  };
682
1081
  }
683
1082
  __name(inspectClientModuleMetadata, "inspectClientModuleMetadata");
@@ -855,6 +1254,10 @@ function parseInterceptionMarker(segment) {
855
1254
  __name(parseInterceptionMarker, "parseInterceptionMarker");
856
1255
 
857
1256
  // src/routing/route-manager.ts
1257
+ function shouldSuggestStaticRenderingForI18n(i18n) {
1258
+ return !i18n || i18n.routing === "prefix-always" || i18n.routing === "prefix-except-default" && i18n.detection.every((signal) => signal === "url");
1259
+ }
1260
+ __name(shouldSuggestStaticRenderingForI18n, "shouldSuggestStaticRenderingForI18n");
858
1261
  function routeSpecificity(entry) {
859
1262
  return entry.route.segments.reduce((score, segment) => {
860
1263
  if (!segment.isDynamic) return score + 100;
@@ -1098,6 +1501,7 @@ var _RouteManager = class _RouteManager {
1098
1501
  modulePath: toUrlPath(entry.modulePath),
1099
1502
  shouldHydrate: metadata.shouldHydrate,
1100
1503
  isClientComponent: metadata.isClientComponent,
1504
+ islandStrategy: metadata.islandStrategy,
1101
1505
  search: getProgrammaticRouteSearchClientOptions(programmaticPage?.search),
1102
1506
  segments: entry.route.segments.map((seg) => ({
1103
1507
  segment: seg.segment,
@@ -1560,8 +1964,10 @@ var _RouteManager = class _RouteManager {
1560
1964
  isDynamic: entry.route.segments.some((seg) => seg.isDynamic),
1561
1965
  pattern: entry.pattern
1562
1966
  }));
1563
- const result = await collectSSGPages(routes, (filePath) => this.loadRouteModule(filePath));
1564
1967
  const i18n = this.getI18nConfig();
1968
+ const result = await collectSSGPages(routes, (filePath) => this.loadRouteModule(filePath), {
1969
+ suggestStaticRendering: shouldSuggestStaticRenderingForI18n(i18n)
1970
+ });
1565
1971
  if (!i18n) {
1566
1972
  return result;
1567
1973
  }
@@ -1693,7 +2099,7 @@ __name(safeGlobFiles, "safeGlobFiles");
1693
2099
 
1694
2100
  // src/server/renderer.ts
1695
2101
  import React from "react";
1696
- import { renderToPipeableStream, renderToStaticMarkup } from "react-dom/server";
2102
+ import { renderToPipeableStream } from "react-dom/server";
1697
2103
  import * as fs2 from "fs";
1698
2104
  import * as path5 from "path";
1699
2105
  import { Writable } from "stream";
@@ -2027,6 +2433,10 @@ ${FONT_SECTION_END}
2027
2433
  fallback,
2028
2434
  display,
2029
2435
  preload: raw.preload !== false,
2436
+ preloadResources: raw.preload === false ? [] : normalizedSources.map(({ resource }) => ({
2437
+ href: resourceUrl(resource, production, this.basePath),
2438
+ type: fontContentType(resource.extension)
2439
+ })),
2030
2440
  weight: defaultWeight,
2031
2441
  style: raw.style === void 0 ? void 0 : defaultStyle,
2032
2442
  sources: normalizedSources
@@ -2123,7 +2533,7 @@ ${FONT_SECTION_END}
2123
2533
  const normalizedExtension = extension.toLowerCase();
2124
2534
  const hash = createHash2("sha256").update(bytes).digest("hex").slice(0, 12);
2125
2535
  const baseName = sanitizeFileName(path4.basename(fileName, extension)) || "font";
2126
- const outputFileName = `assets/fonts/${baseName}-${hash}${normalizedExtension}`;
2536
+ const outputFileName = `assets/fonts/${baseName}-h${hash}${normalizedExtension}`;
2127
2537
  const key = `${hash}${normalizedExtension}`;
2128
2538
  const existing = this.resources.get(key);
2129
2539
  if (existing) return existing;
@@ -2393,7 +2803,8 @@ function serializeFontResult(font) {
2393
2803
  fontFamily: stack,
2394
2804
  ...font.style ? { fontStyle: font.style } : {},
2395
2805
  ...font.weight !== void 0 && !String(font.weight).includes(" ") ? { fontWeight: font.weight } : {}
2396
- }
2806
+ },
2807
+ preloads: font.preloadResources
2397
2808
  });
2398
2809
  }
2399
2810
  __name(serializeFontResult, "serializeFontResult");
@@ -2813,7 +3224,7 @@ function createPPRRefreshScript() {
2813
3224
  }
2814
3225
  __name(createPPRRefreshScript, "createPPRRefreshScript");
2815
3226
  function createPreHydrationClickQueueScript() {
2816
- return `<script>(function(){if(window.__FARM_PREHYDRATION_CLICK_QUEUE__)return;var queue=[];window.__FARM_PREHYDRATION_CLICK_QUEUE__=queue;window.__FARM_HYDRATED__=false;document.documentElement.dataset.farmHydrated="false";function isModified(event){return !!(event.metaKey||event.altKey||event.ctrlKey||event.shiftKey)}function closestQueuedTarget(target){while(target&&target!==document.documentElement){if(target.matches&&target.matches('button,[role="button"],input[type="button"],input[type="submit"],input[type="reset"]'))return target;target=target.parentElement}return null}document.addEventListener("click",function(event){if(window.__FARM_HYDRATED__)return;if(event.defaultPrevented||event.button!==0||isModified(event))return;var target=closestQueuedTarget(event.target);if(!target||target.closest&&target.closest("a[href]"))return;if(queue.some(function(item){return item.target===target}))return;queue.push({target:target,createdAt:Date.now()});event.preventDefault();event.stopImmediatePropagation()},true);})();</script>`;
3227
+ return `<script>(function(){if(window.__FARM_PREHYDRATION_CLICK_QUEUE__)return;var queue=[];window.__FARM_PREHYDRATION_CLICK_QUEUE__=queue;window.__FARM_HYDRATED__=false;document.documentElement.dataset.farmHydrated="false";function isModified(event){return !!(event.metaKey||event.altKey||event.ctrlKey||event.shiftKey)}function closestQueuedTarget(target){while(target&&target!==document.documentElement){if(target.matches&&target.matches('button,[role="button"],input[type="button"],input[type="submit"],input[type="reset"]'))return target;target=target.parentElement}return null}document.addEventListener("click",function(event){if(window.__FARM_HYDRATED__)return;if(event.defaultPrevented||event.button!==0||isModified(event))return;var target=closestQueuedTarget(event.target);if(!target||target.closest&&target.closest("a[href]")||target.closest&&target.closest('[data-farm-island-hydrated="true"]'))return;if(queue.some(function(item){return item.target===target}))return;queue.push({target:target,createdAt:Date.now()});document.dispatchEvent(new CustomEvent("farm:island-interaction",{detail:{target:target}}));event.preventDefault();event.stopImmediatePropagation()},true);})();</script>`;
2817
3228
  }
2818
3229
  __name(createPreHydrationClickQueueScript, "createPreHydrationClickQueueScript");
2819
3230
  function searchParamsToObject(searchParams) {
@@ -3424,6 +3835,7 @@ var _ServerRenderer = class _ServerRenderer {
3424
3835
  req.__FARM_ROUTE__ = pathname;
3425
3836
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
3426
3837
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
3838
+ req.__FARM_ISLAND_STRATEGY__ = moduleMetadata.islandStrategy;
3427
3839
  req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
3428
3840
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
3429
3841
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
@@ -3499,7 +3911,12 @@ var _ServerRenderer = class _ServerRenderer {
3499
3911
  if (isClientComponent || shouldHydrate) {
3500
3912
  pageElement = React.createElement(
3501
3913
  "div",
3502
- { id: "__farm_page__", "data-farm-client": "true" },
3914
+ {
3915
+ id: "__farm_page__",
3916
+ "data-farm-client": "true",
3917
+ "data-farm-island": "page",
3918
+ "data-farm-island-strategy": moduleMetadata.islandStrategy ?? "load"
3919
+ },
3503
3920
  pageElement
3504
3921
  );
3505
3922
  }
@@ -3744,6 +4161,13 @@ var _ServerRenderer = class _ServerRenderer {
3744
4161
  return reference;
3745
4162
  }
3746
4163
  async renderMetadataImage(req, res, options) {
4164
+ const method = (req.method || "GET").toUpperCase();
4165
+ if (method !== "GET" && method !== "HEAD") {
4166
+ res.statusCode = 405;
4167
+ res.setHeader("Allow", "GET, HEAD");
4168
+ res.end();
4169
+ return;
4170
+ }
3747
4171
  if (options.image.sourceType === "static") {
3748
4172
  if (!options.image.staticInfo) {
3749
4173
  throw new Error(`Static metadata image ${options.image.modulePath} is missing file info`);
@@ -3769,42 +4193,12 @@ var _ServerRenderer = class _ServerRenderer {
3769
4193
  await this.writeMetadataImageResponse(req, res, handlerResult, imageModule);
3770
4194
  }
3771
4195
  async writeMetadataImageResponse(req, res, value, imageModule) {
3772
- if (isWebResponse(value)) {
3773
- res.statusCode = value.status;
3774
- value.headers.forEach((headerValue, key) => {
3775
- res.setHeader(key, headerValue);
3776
- });
3777
- if (req.method === "HEAD") {
3778
- res.end();
3779
- return;
3780
- }
3781
- const body2 = Buffer.from(await value.arrayBuffer());
3782
- res.write(body2);
3783
- res.end();
3784
- return;
3785
- }
3786
- const contentType = imageModule.contentType || "image/svg+xml; charset=utf-8";
3787
- let body;
3788
- if (typeof value === "string") {
3789
- body = value;
3790
- } else if (value instanceof ArrayBuffer) {
3791
- body = Buffer.from(value);
3792
- } else if (ArrayBuffer.isView(value)) {
3793
- body = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
3794
- } else if (React.isValidElement(value)) {
3795
- body = renderToStaticMarkup(value);
3796
- } else {
3797
- throw new Error("Metadata image must return a Response, string, bytes, or React element");
3798
- }
3799
- res.statusCode = res.statusCode || 200;
3800
- res.setHeader("Content-Type", contentType);
3801
- res.setHeader("Cache-Control", "public, max-age=0, must-revalidate");
3802
- if (req.method === "HEAD") {
3803
- res.end();
3804
- return;
3805
- }
3806
- res.write(body);
3807
- res.end();
4196
+ const ifNoneMatch = req.headers["if-none-match"];
4197
+ const response = await createFarmMetadataImageResponse(value, imageModule, {
4198
+ method: req.method,
4199
+ ifNoneMatch: Array.isArray(ifNoneMatch) ? ifNoneMatch[0] : ifNoneMatch
4200
+ });
4201
+ await sendWebResponse(res, response);
3808
4202
  }
3809
4203
  async writeStaticMetadataImageResponse(req, res, image) {
3810
4204
  const method = (req.method || "GET").toUpperCase();
@@ -3934,6 +4328,7 @@ var _ServerRenderer = class _ServerRenderer {
3934
4328
  search: routeEntry.search,
3935
4329
  isClientComponent: routeEntry.isClientComponent,
3936
4330
  shouldHydrate: routeEntry.shouldHydrate,
4331
+ islandStrategy: routeEntry.islandStrategy,
3937
4332
  preloads: [routeEntry.modulePath],
3938
4333
  assets: []
3939
4334
  };
@@ -3970,6 +4365,7 @@ window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
3970
4365
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
3971
4366
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
3972
4367
  window.__FARM_SHOULD_HYDRATE__ = ${JSON.stringify(req.__FARM_SHOULD_HYDRATE__ === true)};
4368
+ window.__FARM_ISLAND_STRATEGY__ = ${JSON.stringify(req.__FARM_ISLAND_STRATEGY__ || "load")};
3973
4369
  window.__FARM_PAGE_MODULE__ = ${JSON.stringify(relativePath)};
3974
4370
  window.__FARM_LOADING_MODULE__ = ${JSON.stringify(
3975
4371
  req.__FARM_LOADING_MODULE_PATH__ || null
@@ -4330,6 +4726,7 @@ var _FarmApp = class _FarmApp {
4330
4726
  context: config.context || (() => void 0),
4331
4727
  serverActions: resolveServerActionsConfig(config.serverActions),
4332
4728
  images: resolveFarmImageConfig(config.images),
4729
+ performance: resolveFarmPerformanceConfig(config.performance),
4333
4730
  i18n: isResolvedI18nConfig(config.i18n) ? config.i18n : resolveFarmI18nConfig(config.i18n, {
4334
4731
  root,
4335
4732
  mode: process.env.NODE_ENV === "production" ? "production" : "development"
@@ -5076,7 +5473,7 @@ __name(removeLegacyTypeArtifacts, "removeLegacyTypeArtifacts");
5076
5473
 
5077
5474
  // src/vite.ts
5078
5475
  import * as fs4 from "fs";
5079
- import * as path10 from "path";
5476
+ import * as path11 from "path";
5080
5477
 
5081
5478
  // src/image-vite.ts
5082
5479
  import { readFile as readFile4 } from "fs/promises";
@@ -5147,6 +5544,100 @@ async function createBlurDataURL(bytes) {
5147
5544
  }
5148
5545
  __name(createBlurDataURL, "createBlurDataURL");
5149
5546
 
5547
+ // src/server/vite-config.ts
5548
+ import path10 from "path";
5549
+ var FARM_CLIENT_ENTRY_PATTERN = "**/{page,layout,loading,error,not-found,default}.{js,jsx,ts,tsx}";
5550
+ var FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE = [
5551
+ "react",
5552
+ "react-dom",
5553
+ "react-dom/client",
5554
+ "react/jsx-runtime",
5555
+ "react/jsx-dev-runtime",
5556
+ "@farm.js/core/client",
5557
+ "@farm.js/core/plugin/client",
5558
+ "@farm.js/core/deferred",
5559
+ "@farm.js/core/deployment",
5560
+ "@farm.js/core/i18n/client",
5561
+ "@farm.js/core/query/client",
5562
+ "@farm.js/core/server-fn/client",
5563
+ "@farm.js/core/server-query/client"
5564
+ ];
5565
+ function createFarmClientOptimizeDepsEntries(projectRoot, appDirectories) {
5566
+ const resolvedProjectRoot = path10.resolve(projectRoot);
5567
+ return appDirectories.map((appDirectory) => {
5568
+ const relativeAppDirectory = path10.relative(resolvedProjectRoot, path10.resolve(appDirectory)).replace(/\\/g, "/");
5569
+ const prefix = relativeAppDirectory || ".";
5570
+ return `${prefix}/${FARM_CLIENT_ENTRY_PATTERN}`;
5571
+ });
5572
+ }
5573
+ __name(createFarmClientOptimizeDepsEntries, "createFarmClientOptimizeDepsEntries");
5574
+ function createFarmClientOptimizeDepsConfig(entries) {
5575
+ return {
5576
+ // Keep normal application dependency discovery for CJS-only packages, but
5577
+ // seed every Farm browser runtime so React is present in the first crawl.
5578
+ noDiscovery: false,
5579
+ // Waiting for the crawl avoids serving a partial optimizer generation and
5580
+ // then changing browser hashes while hydration is already in progress.
5581
+ holdUntilCrawlEnd: true,
5582
+ include: [...FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE],
5583
+ // Farm's HTML boots through a virtual module and imports route UI lazily.
5584
+ // Explicit entries let Vite finish that crawl before a navigation can
5585
+ // rotate the optimizer hash underneath a hydrated React runtime.
5586
+ ...entries?.length ? { entries: [...entries] } : {}
5587
+ };
5588
+ }
5589
+ __name(createFarmClientOptimizeDepsConfig, "createFarmClientOptimizeDepsConfig");
5590
+ function mergeNoExternal(farmValue, userValue) {
5591
+ if (farmValue === true || userValue === true) return true;
5592
+ const normalize = /* @__PURE__ */ __name((value) => value == null || value === true ? [] : Array.isArray(value) ? value : [value], "normalize");
5593
+ return Array.from(/* @__PURE__ */ new Set([...normalize(farmValue), ...normalize(userValue)]));
5594
+ }
5595
+ __name(mergeNoExternal, "mergeNoExternal");
5596
+ function mergeFarmViteConfig(farmConfig, userConfig = {}) {
5597
+ const noDiscovery = userConfig.optimizeDeps?.noDiscovery ?? farmConfig.optimizeDeps?.noDiscovery;
5598
+ const holdUntilCrawlEnd = userConfig.optimizeDeps?.holdUntilCrawlEnd ?? (userConfig.optimizeDeps?.noDiscovery === true ? false : farmConfig.optimizeDeps?.holdUntilCrawlEnd);
5599
+ return {
5600
+ ...farmConfig,
5601
+ ...userConfig,
5602
+ plugins: [...farmConfig.plugins || [], ...userConfig.plugins || []],
5603
+ server: {
5604
+ ...farmConfig.server,
5605
+ ...userConfig.server
5606
+ },
5607
+ resolve: {
5608
+ ...farmConfig.resolve,
5609
+ ...userConfig.resolve,
5610
+ dedupe: Array.from(
5611
+ /* @__PURE__ */ new Set([...farmConfig.resolve?.dedupe || [], ...userConfig.resolve?.dedupe || []])
5612
+ )
5613
+ },
5614
+ optimizeDeps: {
5615
+ ...farmConfig.optimizeDeps,
5616
+ ...userConfig.optimizeDeps,
5617
+ noDiscovery,
5618
+ holdUntilCrawlEnd,
5619
+ include: Array.from(
5620
+ /* @__PURE__ */ new Set([
5621
+ ...farmConfig.optimizeDeps?.include || [],
5622
+ ...userConfig.optimizeDeps?.include || []
5623
+ ])
5624
+ ),
5625
+ exclude: Array.from(
5626
+ /* @__PURE__ */ new Set([
5627
+ ...farmConfig.optimizeDeps?.exclude || [],
5628
+ ...userConfig.optimizeDeps?.exclude || []
5629
+ ])
5630
+ )
5631
+ },
5632
+ ssr: {
5633
+ ...farmConfig.ssr,
5634
+ ...userConfig.ssr,
5635
+ noExternal: mergeNoExternal(farmConfig.ssr?.noExternal, userConfig.ssr?.noExternal)
5636
+ }
5637
+ };
5638
+ }
5639
+ __name(mergeFarmViteConfig, "mergeFarmViteConfig");
5640
+
5150
5641
  // src/vite.ts
5151
5642
  var ALL_TYPE_ARTIFACTS = {
5152
5643
  routes: true,
@@ -5189,7 +5680,7 @@ function loadFarmWorkflowsDevRuntime() {
5189
5680
  }
5190
5681
  __name(loadFarmWorkflowsDevRuntime, "loadFarmWorkflowsDevRuntime");
5191
5682
  function loadFarmDevtoolsSnapshotRuntime() {
5192
- return import("./devtools-A22KUJZT.mjs");
5683
+ return import("./devtools-GAZ7F3KL.mjs");
5193
5684
  }
5194
5685
  __name(loadFarmDevtoolsSnapshotRuntime, "loadFarmDevtoolsSnapshotRuntime");
5195
5686
  function loadFarmDevtoolsUIRuntime() {
@@ -5368,7 +5859,7 @@ function fileContainsProgrammaticPageRoute(file) {
5368
5859
  __name(fileContainsProgrammaticPageRoute, "fileContainsProgrammaticPageRoute");
5369
5860
  function rewriteEarlySsrRelativeImports(options) {
5370
5861
  const cleanId = options.id.split("?", 1)[0];
5371
- if (!path10.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
5862
+ if (!path11.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
5372
5863
  return null;
5373
5864
  }
5374
5865
  const replacements = [];
@@ -5388,7 +5879,7 @@ function rewriteEarlySsrRelativeImports(options) {
5388
5879
  const suffixIndex = source.value.search(/[?#]/);
5389
5880
  const sourcePath = suffixIndex === -1 ? source.value : source.value.slice(0, suffixIndex);
5390
5881
  const suffix = suffixIndex === -1 ? "" : source.value.slice(suffixIndex);
5391
- const resolvedPath = path10.resolve(path10.dirname(cleanId), sourcePath);
5882
+ const resolvedPath = path11.resolve(path11.dirname(cleanId), sourcePath);
5392
5883
  replacements.push({
5393
5884
  start: source.start,
5394
5885
  end: source.end,
@@ -5473,7 +5964,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5473
5964
  ...layerRoots.length ? {
5474
5965
  server: {
5475
5966
  fs: {
5476
- allow: [path10.resolve(options.root || process.cwd()), ...layerRoots]
5967
+ allow: [path11.resolve(options.root || process.cwd()), ...layerRoots]
5477
5968
  }
5478
5969
  }
5479
5970
  } : {}
@@ -5504,9 +5995,9 @@ function farmPlugin(options = {}, initialPluginManager) {
5504
5995
  },
5505
5996
  server
5506
5997
  );
5507
- const globalsCSSPath = path10.join(server.config.root, "src/app/globals.css");
5998
+ const globalsCSSPath = path11.join(server.config.root, "src/app/globals.css");
5508
5999
  if (!fs4.existsSync(globalsCSSPath)) {
5509
- const appDir = path10.join(server.config.root, "src/app");
6000
+ const appDir = path11.join(server.config.root, "src/app");
5510
6001
  if (!fs4.existsSync(appDir)) {
5511
6002
  fs4.mkdirSync(appDir, { recursive: true });
5512
6003
  }
@@ -5528,10 +6019,10 @@ function farmPlugin(options = {}, initialPluginManager) {
5528
6019
  });
5529
6020
  }
5530
6021
  const sourceRoots = getFarmSourceRoots(farmConfig);
5531
- server.watcher.add(sourceRoots.map((source) => path10.join(source.root, source.srcDir)));
6022
+ server.watcher.add(sourceRoots.map((source) => path11.join(source.root, source.srcDir)));
5532
6023
  if (farmConfig.i18n.enabled) {
5533
6024
  server.watcher.add(
5534
- farmConfig.i18n.messages.includes("{locale}") ? path10.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
6025
+ farmConfig.i18n.messages.includes("{locale}") ? path11.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
5535
6026
  );
5536
6027
  }
5537
6028
  const workflowConfig = farmConfig.workflows;
@@ -5541,7 +6032,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5541
6032
  ...farmDocsDevRuntime?.getFarmDocsRouteTypeEntries(farmConfig.docs) ?? []
5542
6033
  ], "getExtraRouteTypes");
5543
6034
  const appDirSlugs = sourceRoots.map(
5544
- (source) => path10.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
6035
+ (source) => path11.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
5545
6036
  );
5546
6037
  const generateTypeArtifacts = /* @__PURE__ */ __name(async (reason, selection = ALL_TYPE_ARTIFACTS, log = false) => {
5547
6038
  try {
@@ -5581,7 +6072,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5581
6072
  }, "generateTypeArtifacts");
5582
6073
  await generateTypeArtifacts("startup");
5583
6074
  const srcDirSlugs = sourceRoots.map(
5584
- (source) => path10.join(source.root, source.srcDir).replace(/\\/g, "/")
6075
+ (source) => path11.join(source.root, source.srcDir).replace(/\\/g, "/")
5585
6076
  );
5586
6077
  const layerConfigFiles = new Set(
5587
6078
  farmConfig.layers.map((layer) => layer.configFile?.replace(/\\/g, "/")).filter((file) => Boolean(file))
@@ -5699,7 +6190,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5699
6190
  await apiRouteManager.discoverRoutes();
5700
6191
  let discoveredWorkflows = [];
5701
6192
  const hasWorkflowDirectory = workflowConfig.enabled && workflowConfig.dirs.some(
5702
- (dir) => fs4.existsSync(path10.isAbsolute(dir) ? dir : path10.join(farmConfig.root, dir))
6193
+ (dir) => fs4.existsSync(path11.isAbsolute(dir) ? dir : path11.join(farmConfig.root, dir))
5703
6194
  );
5704
6195
  if (hasWorkflowDirectory) {
5705
6196
  const { createFarmWorkflowRequestHandler, discoverFarmWorkflows } = await loadFarmWorkflowsDevRuntime();
@@ -5763,10 +6254,20 @@ function farmPlugin(options = {}, initialPluginManager) {
5763
6254
  logger.info(`Refreshed routes: ${reason}`);
5764
6255
  }
5765
6256
  }, "refreshRouteDiscovery");
6257
+ const farmDocsFontAssetList = farmDocsDevRuntime ? resolveFarmDocsFontAssets(farmConfig.root) : [];
5766
6258
  const farmDocsHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsHandler(farmConfig.docs, {
5767
6259
  root: farmConfig.root,
5768
6260
  srcDir: farmConfig.srcDir,
5769
- clientEntry: "/@farm/client.js"
6261
+ clientEntry: "/@farm/client.js",
6262
+ fontAssets: toFarmDocsPublicFontAssets(farmDocsFontAssetList),
6263
+ resolveLayoutFonts: /* @__PURE__ */ __name(async (pathname) => {
6264
+ const layouts = routeManager.matchRoute(pathname).layouts;
6265
+ const layoutModules = await Promise.all(
6266
+ layouts.map((layout) => routeManager.loadLayoutModule(layout.modulePath))
6267
+ );
6268
+ return resolveFarmLayoutFonts(layoutModules);
6269
+ }, "resolveLayoutFonts"),
6270
+ fontStylesheetHref: "/@farm/fonts.css"
5770
6271
  }) : null;
5771
6272
  const farmDocsAPIHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsAPIHandler({
5772
6273
  rootDir: farmConfig.root,
@@ -5774,22 +6275,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5774
6275
  docs: farmConfig.docs
5775
6276
  }) : null;
5776
6277
  const farmDocsFontAssets = new Map(
5777
- farmDocsDevRuntime ? [
5778
- [
5779
- "/assets/Geist-Variable-CrgPqtmy.woff2",
5780
- path10.join(
5781
- farmConfig.root,
5782
- "node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2"
5783
- )
5784
- ],
5785
- [
5786
- "/assets/GeistMono-Variable-BNLlm6Cd.woff2",
5787
- path10.join(
5788
- farmConfig.root,
5789
- "node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2"
5790
- )
5791
- ]
5792
- ] : []
6278
+ farmDocsFontAssetList.map(({ url, sourcePath }) => [url, sourcePath])
5793
6279
  );
5794
6280
  const logRequest = /* @__PURE__ */ __name((method, urlPath, tag) => {
5795
6281
  try {
@@ -5853,11 +6339,12 @@ function farmPlugin(options = {}, initialPluginManager) {
5853
6339
  }
5854
6340
  }
5855
6341
  const farmDocsFontPath = farmDocsFontAssets.get(requestPathname);
5856
- if (farmDocsFontPath && fs4.existsSync(farmDocsFontPath)) {
6342
+ if ((requestMethod === "GET" || requestMethod === "HEAD") && farmDocsFontPath && fs4.existsSync(farmDocsFontPath)) {
5857
6343
  res.statusCode = 200;
5858
6344
  res.setHeader("Content-Type", "font/woff2");
5859
6345
  res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
5860
- fs4.createReadStream(farmDocsFontPath).pipe(res);
6346
+ if (requestMethod === "HEAD") res.end();
6347
+ else fs4.createReadStream(farmDocsFontPath).pipe(res);
5861
6348
  return;
5862
6349
  }
5863
6350
  if (requestMethod === "GET" && (requestPathname === FARM_DEVTOOLS_PATH || requestPathname === `${FARM_DEVTOOLS_PATH}.json`)) {
@@ -6463,6 +6950,7 @@ function farmPlugin(options = {}, initialPluginManager) {
6463
6950
  modulePath: toUrlPath(route.modulePath),
6464
6951
  isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
6465
6952
  shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
6953
+ islandStrategy: moduleMetadata.islandStrategy,
6466
6954
  metadata: {
6467
6955
  title: mergedMetadata.title,
6468
6956
  description: mergedMetadata.description
@@ -6847,6 +7335,7 @@ export const getManifest = () => ({
6847
7335
  search: route.search,
6848
7336
  isClientComponent: moduleMetadata.isClientComponent,
6849
7337
  shouldHydrate: moduleMetadata.shouldHydrate,
7338
+ islandStrategy: moduleMetadata.islandStrategy,
6850
7339
  preloads: [route.modulePath],
6851
7340
  // In dev, preload is just the module
6852
7341
  assets: []
@@ -6973,8 +7462,8 @@ if (import.meta.hot) {
6973
7462
  }
6974
7463
  logger.info(`Found ${ssgPages.length} SSG pages, ${ssrRoutes.length} SSR routes`);
6975
7464
  logger.info("Pre-rendering SSG pages...");
6976
- const outDir = path10.join(server?.config.root || process.cwd(), options.outDir || "dist");
6977
- const clientDir = path10.join(outDir, "client");
7465
+ const outDir = path11.join(server?.config.root || process.cwd(), options.outDir || "dist");
7466
+ const clientDir = path11.join(outDir, "client");
6978
7467
  for (const page of ssgPages) {
6979
7468
  try {
6980
7469
  const staticRequest = new Request(new URL(page.urlPath, "http://farm.static"));
@@ -7026,8 +7515,8 @@ if (import.meta.hot) {
7026
7515
  <script type="module" src="/assets/client.js"></script>
7027
7516
  </body>
7028
7517
  </html>`;
7029
- const outputPath = page.urlPath === "/" ? path10.join(clientDir, "index.html") : path10.join(clientDir, page.urlPath + ".html");
7030
- fs4.mkdirSync(path10.dirname(outputPath), { recursive: true });
7518
+ const outputPath = page.urlPath === "/" ? path11.join(clientDir, "index.html") : path11.join(clientDir, page.urlPath + ".html");
7519
+ fs4.mkdirSync(path11.dirname(outputPath), { recursive: true });
7031
7520
  fs4.writeFileSync(outputPath, fullHtml);
7032
7521
  const revalidateInfo = page.revalidate ? ` (revalidate: ${page.revalidate}s)` : "";
7033
7522
  logger.success(` \u2713 ${page.urlPath}${revalidateInfo}`);
@@ -7036,7 +7525,7 @@ if (import.meta.hot) {
7036
7525
  logger.error(` \u2717 ${page.urlPath}: ${error}`);
7037
7526
  }
7038
7527
  }
7039
- const manifestPath = path10.join(outDir, "__ssg_manifest.json");
7528
+ const manifestPath = path11.join(outDir, "__ssg_manifest.json");
7040
7529
  fs4.writeFileSync(
7041
7530
  manifestPath,
7042
7531
  JSON.stringify(
@@ -7072,9 +7561,9 @@ if (import.meta.hot) {
7072
7561
  }
7073
7562
  const currentFarmConfig = farmApp?.getConfig();
7074
7563
  const normalizedFile = file.replace(/\\/g, "/");
7075
- const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path10.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
7564
+ const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path11.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
7076
7565
  if (currentSrcRoot && normalizedFile.startsWith(`${currentSrcRoot}/`) && isProgrammaticRoutesFileName(normalizedFile)) {
7077
- logUpdate("PAGE", `updated ${path10.basename(file)}`);
7566
+ logUpdate("PAGE", `updated ${path11.basename(file)}`);
7078
7567
  for (const mod of modules) {
7079
7568
  server2.moduleGraph.invalidateModule(mod);
7080
7569
  }
@@ -7471,6 +7960,7 @@ import React from 'react'
7471
7960
  import { hydrateRoot, createRoot } from 'react-dom/client'
7472
7961
  import { installChunkErrorRecovery, SPARouter } from '@farm.js/core/client'
7473
7962
  import { createClientPluginManager } from '@farm.js/core/plugin/client'
7963
+ import { scheduleFarmIslandHydration } from '@farm.js/core/internal/client-runtime'
7474
7964
  import { reviveDeferredData } from '@farm.js/core/deferred'
7475
7965
  import {
7476
7966
  createFarmDeploymentMismatchError,
@@ -7723,6 +8213,7 @@ class LegacyManifestSPARouter {
7723
8213
 
7724
8214
  // Only prefetch routes that will hydrate on the client.
7725
8215
  if (!match.route.isClientComponent && !match.route.shouldHydrate) return;
8216
+ if (match.route.islandStrategy && match.route.islandStrategy !== 'load') return;
7726
8217
 
7727
8218
  const modulePath = match.route.modulePath;
7728
8219
  if (this.moduleCache.has(modulePath)) return;
@@ -7867,6 +8358,12 @@ let hasClientTakenOver = false;
7867
8358
  const routeSlotRoots = new Map();
7868
8359
  const routeSlotDefinitions = new Map();
7869
8360
  let activeRouteInterception = null;
8361
+ let pendingPageHydrationController = null;
8362
+
8363
+ function cancelPendingPageHydration() {
8364
+ pendingPageHydrationController?.abort();
8365
+ pendingPageHydrationController = null;
8366
+ }
7870
8367
 
7871
8368
  function normalizeServerProps(rawProps) {
7872
8369
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -7935,7 +8432,11 @@ async function hydrateInitialRouteSlots() {
7935
8432
  routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
7936
8433
  if (!slot.isClientComponent && !slot.shouldHydrate) continue;
7937
8434
  try {
7938
- hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
8435
+ const slotHydrated = await renderRouteSlot(slot, 'hydrate');
8436
+ hydrated = slotHydrated || hydrated;
8437
+ if (slotHydrated) {
8438
+ replayPreHydrationClicks(document.getElementById(slot.containerId));
8439
+ }
7939
8440
  } catch (error) {
7940
8441
  console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
7941
8442
  }
@@ -8006,16 +8507,29 @@ function applyCanonicalPathFromProps(props) {
8006
8507
  window.history.replaceState({ ...(window.history.state || {}), path: canonicalPath }, '', canonicalPath);
8007
8508
  }
8008
8509
 
8009
- function replayPreHydrationClicks() {
8010
- window.__FARM_HYDRATED__ = true;
8011
- document.documentElement.dataset.farmHydrated = 'true';
8510
+ function replayPreHydrationClicks(container = null) {
8511
+ if (!container) {
8512
+ markFarmHydrated();
8513
+ } else {
8514
+ container.dataset.farmIslandHydrated = 'true';
8515
+ }
8012
8516
 
8013
8517
  const queue = Array.isArray(window.__FARM_PREHYDRATION_CLICK_QUEUE__)
8014
8518
  ? window.__FARM_PREHYDRATION_CLICK_QUEUE__
8015
8519
  : [];
8016
8520
  if (queue.length === 0) return;
8017
8521
 
8018
- const queuedClicks = queue.splice(0, queue.length);
8522
+ const queuedClicks = [];
8523
+ const remainingClicks = [];
8524
+ for (const queuedClick of queue) {
8525
+ const target = queuedClick?.target;
8526
+ if (!container || (target instanceof Node && container.contains(target))) {
8527
+ queuedClicks.push(queuedClick);
8528
+ } else {
8529
+ remainingClicks.push(queuedClick);
8530
+ }
8531
+ }
8532
+ queue.splice(0, queue.length, ...remainingClicks);
8019
8533
  for (const queuedClick of queuedClicks) {
8020
8534
  const target = queuedClick?.target;
8021
8535
  if (!target || typeof target.click !== 'function') continue;
@@ -8024,6 +8538,11 @@ function replayPreHydrationClicks() {
8024
8538
  }
8025
8539
  }
8026
8540
 
8541
+ function markFarmHydrated() {
8542
+ window.__FARM_HYDRATED__ = true;
8543
+ document.documentElement.dataset.farmHydrated = 'true';
8544
+ }
8545
+
8027
8546
  async function buildClientHydrationElement(PageComponent, pageProps) {
8028
8547
  let element = React.createElement(PageComponent, pageProps);
8029
8548
  const loadingModulePath = window.__FARM_LOADING_MODULE__;
@@ -8166,9 +8685,10 @@ async function tryHydrateImportedPage(
8166
8685
  layouts,
8167
8686
  useHydrate = false,
8168
8687
  existingProps = null,
8688
+ signal = null,
8169
8689
  ) {
8170
8690
  const modulePath = route?.modulePath;
8171
- if (!modulePath) {
8691
+ if (!modulePath || signal?.aborted) {
8172
8692
  return false;
8173
8693
  }
8174
8694
 
@@ -8177,6 +8697,7 @@ async function tryHydrateImportedPage(
8177
8697
  pageModule = await import(/* @vite-ignore */ modulePath);
8178
8698
  pageModuleCache.set(modulePath, pageModule);
8179
8699
  }
8700
+ if (signal?.aborted || !container?.isConnected) return false;
8180
8701
 
8181
8702
  const PageComponent = pageModule?.default;
8182
8703
  if (!PageComponent) {
@@ -8191,6 +8712,7 @@ async function tryHydrateImportedPage(
8191
8712
  window.location.pathname,
8192
8713
  existingProps,
8193
8714
  );
8715
+ if (signal?.aborted || !container?.isConnected) return false;
8194
8716
 
8195
8717
  const wrappedElement = useHydrate && container?.id === '__farm_page__'
8196
8718
  ? wrapWithIntegrationProviders(
@@ -8201,6 +8723,7 @@ async function tryHydrateImportedPage(
8201
8723
  currentPageProps,
8202
8724
  layouts,
8203
8725
  );
8726
+ if (signal?.aborted || !container?.isConnected) return false;
8204
8727
 
8205
8728
  if (useHydrate) {
8206
8729
  try {
@@ -8231,6 +8754,7 @@ async function tryHydrateImportedPage(
8231
8754
  async function renderPage(pageData) {
8232
8755
  const container = document.getElementById('root');
8233
8756
  if (!container) return;
8757
+ cancelPendingPageHydration();
8234
8758
  const destination = window.location.pathname + window.location.search;
8235
8759
 
8236
8760
  if (pageData.interception) {
@@ -8247,6 +8771,7 @@ async function renderPage(pageData) {
8247
8771
  modulePath: pageData.modulePath,
8248
8772
  isClientComponent: pageData.isClientComponent === true,
8249
8773
  shouldHydrate: pageData.shouldHydrate === true,
8774
+ islandStrategy: pageData.islandStrategy || 'load',
8250
8775
  };
8251
8776
  const params = pageData.props?.params || {};
8252
8777
  const layouts = (pageData.layoutModules || []).map((modulePath, index) => ({
@@ -8446,14 +8971,6 @@ async function hydrate() {
8446
8971
  }
8447
8972
 
8448
8973
  try {
8449
- // Pre-load layout for SPA navigation
8450
- try {
8451
- const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
8452
- LayoutComponent = layoutModule.default;
8453
- } catch (e) {
8454
- console.warn('[Farm.js] Could not preload layout:', e);
8455
- }
8456
-
8457
8974
  // Check if this is a client component (set by SSR)
8458
8975
  const isClientComponent = window.__FARM_IS_CLIENT__ === true;
8459
8976
  const modulePath = window.__FARM_PAGE_MODULE__;
@@ -8501,34 +9018,88 @@ async function hydrate() {
8501
9018
  return
8502
9019
  }
8503
9020
 
8504
- const hydrationSession = await farmClientRuntime.beginHydration({
8505
- container: pageContainer,
8506
- mode: shouldHydrate ? 'hydrate' : 'render',
8507
- });
9021
+ const currentRoute = findRoute(window.location.pathname)?.route;
9022
+ const islandStrategy = window.__FARM_ISLAND_STRATEGY__ || currentRoute?.islandStrategy || 'load';
9023
+ pageContainer.dataset.farmIsland = 'page';
9024
+ pageContainer.dataset.farmIslandStrategy = islandStrategy;
8508
9025
 
8509
- let hydrated = false;
9026
+ const hydrationPathname = window.location.pathname;
9027
+ const hydrationController = new AbortController();
9028
+ pendingPageHydrationController = hydrationController;
8510
9029
  try {
8511
- hydrated = await tryHydrateImportedPage(
8512
- pageContainer,
8513
- { modulePath },
8514
- currentPageProps.params || {},
8515
- layouts,
8516
- shouldHydrate,
8517
- currentPageProps,
8518
- );
8519
- await farmClientRuntime.completeHydration(hydrationSession);
8520
- } catch (error) {
8521
- await farmClientRuntime.failHydration(hydrationSession, error);
8522
- throw error;
8523
- }
9030
+ await scheduleFarmIslandHydration({
9031
+ container: pageContainer,
9032
+ strategy: islandStrategy,
9033
+ signal: hydrationController.signal,
9034
+ hydrate: async () => {
9035
+ if (
9036
+ hydrationController.signal.aborted ||
9037
+ !pageContainer.isConnected ||
9038
+ window.location.pathname !== hydrationPathname
9039
+ ) {
9040
+ return;
9041
+ }
8524
9042
 
8525
- if (hydrated) {
8526
- replayPreHydrationClicks();
8527
- console.log('[Farm.js] \u2705 Hydrated:', modulePath);
8528
- return;
8529
- }
9043
+ // Layout code is only needed when this route boundary actually hydrates.
9044
+ try {
9045
+ const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
9046
+ LayoutComponent = layoutModule.default;
9047
+ } catch (e) {
9048
+ console.warn('[Farm.js] Could not preload layout:', e);
9049
+ }
9050
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) return;
8530
9051
 
8531
- console.log('[Farm.js] Server component - SPA router ready')
9052
+ const hydrationSession = await farmClientRuntime.beginHydration({
9053
+ container: pageContainer,
9054
+ mode: shouldHydrate ? 'hydrate' : 'render',
9055
+ });
9056
+
9057
+ let hydrated = false;
9058
+ try {
9059
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
9060
+ await farmClientRuntime.failHydration(
9061
+ hydrationSession,
9062
+ new DOMException('Route hydration was cancelled', 'AbortError'),
9063
+ );
9064
+ return;
9065
+ }
9066
+ hydrated = await tryHydrateImportedPage(
9067
+ pageContainer,
9068
+ { modulePath },
9069
+ currentPageProps.params || {},
9070
+ layouts,
9071
+ shouldHydrate,
9072
+ currentPageProps,
9073
+ hydrationController.signal,
9074
+ );
9075
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
9076
+ await farmClientRuntime.failHydration(
9077
+ hydrationSession,
9078
+ new DOMException('Route hydration was cancelled', 'AbortError'),
9079
+ );
9080
+ return;
9081
+ }
9082
+ await farmClientRuntime.completeHydration(hydrationSession);
9083
+ } catch (error) {
9084
+ await farmClientRuntime.failHydration(hydrationSession, error);
9085
+ throw error;
9086
+ }
9087
+
9088
+ if (hydrated) {
9089
+ // The scheduler owns queue draining and exactly-once click replay.
9090
+ markFarmHydrated();
9091
+ console.log('[Farm.js] \u2705 Hydrated:', modulePath, '(' + islandStrategy + ')');
9092
+ return;
9093
+ }
9094
+
9095
+ console.log('[Farm.js] Server component - SPA router ready')
9096
+ },
9097
+ });
9098
+ } finally {
9099
+ if (pendingPageHydrationController === hydrationController) {
9100
+ pendingPageHydrationController = null;
9101
+ }
9102
+ }
8532
9103
  } catch (error) {
8533
9104
  console.error('[Farm.js] Hydration error:', error)
8534
9105
  }
@@ -8634,7 +9205,7 @@ function generateClientManifest(bundle) {
8634
9205
  __name(generateClientManifest, "generateClientManifest");
8635
9206
  async function defineConfig(config = {}) {
8636
9207
  const tailwindcss = (await import("@tailwindcss/vite")).default;
8637
- const appRoot = path10.resolve(config.root || process.cwd());
9208
+ const appRoot = path11.resolve(config.root || process.cwd());
8638
9209
  if (config.extends?.length) {
8639
9210
  const { config: layeredConfig } = await import("./layers-VWVDTPUZ.mjs").then(
8640
9211
  ({ resolveFarmLayers }) => resolveFarmLayers(config, {
@@ -8779,13 +9350,16 @@ async function defineConfig(config = {}) {
8779
9350
  customLogger: farmLogger,
8780
9351
  clearScreen: false,
8781
9352
  optimizeDeps: {
8782
- include: [
8783
- "react",
8784
- "react-dom",
8785
- "react-dom/client",
8786
- "react/jsx-runtime",
8787
- "react/jsx-dev-runtime"
8788
- ],
9353
+ ...createFarmClientOptimizeDepsConfig(
9354
+ createFarmClientOptimizeDepsEntries(
9355
+ appRoot,
9356
+ getFarmAppDirectories({ ...config, root: appRoot })
9357
+ )
9358
+ ),
9359
+ // Pre-bundle every framework client-runtime entry with React. Without
9360
+ // this, Vite can discover a linked Farm entry after the page has loaded,
9361
+ // regenerate the optimizer browser hash, and leave React DOM holding a
9362
+ // different React module instance from a client component.
8789
9363
  // Exclude server-side packages from browser bundling
8790
9364
  exclude: [
8791
9365
  "@farm.js/core/server",
@@ -8820,7 +9394,7 @@ async function defineConfig(config = {}) {
8820
9394
  // Stub out problematic server-only modules during dev mode
8821
9395
  alias: {
8822
9396
  ...getFarmLayerAliases(config.layers),
8823
- "@": path10.resolve(appRoot, "src"),
9397
+ "@": path11.resolve(appRoot, "src"),
8824
9398
  // Nitro internals that should not be resolved in browser
8825
9399
  "supports-color": "data:text/javascript,export default false; export const supportsColor = false; export const stdout = false; export const stderr = false;",
8826
9400
  "@poppinss/dumper": "data:text/javascript,export default {};",
@@ -8842,6 +9416,7 @@ __name(defineConfig, "defineConfig");
8842
9416
  export {
8843
9417
  resolveRouteRenderingConfig,
8844
9418
  resolveRouteRenderingConfigFromFile,
9419
+ analyzeStaticRouteCandidate,
8845
9420
  parseRouteRenderingDirective,
8846
9421
  collectSSGPages,
8847
9422
  isSSGModule,
@@ -8864,9 +9439,12 @@ export {
8864
9439
  createFarmImageTypeDeclarations,
8865
9440
  generateFarmI18nTypes,
8866
9441
  generateFarmTypeArtifacts,
9442
+ createFarmClientOptimizeDepsEntries,
9443
+ createFarmClientOptimizeDepsConfig,
9444
+ mergeFarmViteConfig,
8867
9445
  farmI18nClientBridgePlugin,
8868
9446
  rewriteEarlySsrRelativeImports,
8869
9447
  farmPlugin,
8870
9448
  defineConfig
8871
9449
  };
8872
- //# sourceMappingURL=chunk-77BOAQHI.mjs.map
9450
+ //# sourceMappingURL=chunk-Z3YG4OZT.mjs.map