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

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 (238) hide show
  1. package/dist/chunk-53MVSX3R.mjs +168 -0
  2. package/dist/chunk-53MVSX3R.mjs.map +1 -0
  3. package/dist/{chunk-AW6I64AP.mjs → chunk-6ZXIQSXE.mjs} +16 -1
  4. package/dist/chunk-6ZXIQSXE.mjs.map +1 -0
  5. package/dist/chunk-7ZVN3YZI.mjs +333 -0
  6. package/dist/chunk-7ZVN3YZI.mjs.map +1 -0
  7. package/dist/{chunk-5EVQFRL2.mjs → chunk-ARCGE2VT.mjs} +2 -2
  8. package/dist/chunk-ARCGE2VT.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-77BOAQHI.mjs → chunk-PNFQQSRA.mjs} +754 -156
  17. package/dist/chunk-PNFQQSRA.mjs.map +1 -0
  18. package/dist/chunk-USLFXFFH.mjs +46 -0
  19. package/dist/chunk-USLFXFFH.mjs.map +1 -0
  20. package/dist/{chunk-4677KXB7.mjs → chunk-VUL55IAX.mjs} +368 -103
  21. package/dist/chunk-VUL55IAX.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 +2235 -689
  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 +2062 -525
  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 +1550 -348
  57. package/dist/plugin.cjs.map +1 -1
  58. package/dist/plugin.mjs +9 -5
  59. package/dist/server.cjs +1663 -419
  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 +1604 -358
  67. package/dist/vite.cjs.map +1 -1
  68. package/dist/vite.mjs +9 -5
  69. package/package.json +10 -1
  70. package/types/client.d.ts +115 -4
  71. package/dist/after.d.mts +0 -25
  72. package/dist/after.d.ts +0 -25
  73. package/dist/agent-runtime.d.mts +0 -94
  74. package/dist/agent-runtime.d.ts +0 -94
  75. package/dist/api/route-manager.d.mts +0 -66
  76. package/dist/api/route-manager.d.ts +0 -66
  77. package/dist/api/runtime.d.mts +0 -3
  78. package/dist/api/runtime.d.ts +0 -3
  79. package/dist/api.d.mts +0 -48
  80. package/dist/api.d.ts +0 -48
  81. package/dist/app-BNE2FoZ4.d.mts +0 -302
  82. package/dist/app-DRGUmJ3s.d.ts +0 -302
  83. package/dist/app-markdown.d.mts +0 -22
  84. package/dist/app-markdown.d.ts +0 -22
  85. package/dist/cache.d.mts +0 -192
  86. package/dist/cache.d.ts +0 -192
  87. package/dist/chunk-4677KXB7.mjs.map +0 -1
  88. package/dist/chunk-4LND3PXF.mjs.map +0 -1
  89. package/dist/chunk-5EVQFRL2.mjs.map +0 -1
  90. package/dist/chunk-77BOAQHI.mjs.map +0 -1
  91. package/dist/chunk-AW6I64AP.mjs.map +0 -1
  92. package/dist/chunk-K5ZTUF22.mjs.map +0 -1
  93. package/dist/chunk-QM746KUO.mjs +0 -56
  94. package/dist/chunk-QM746KUO.mjs.map +0 -1
  95. package/dist/chunk-QTZZ2ISQ.mjs.map +0 -1
  96. package/dist/client-BT1mFQas.d.mts +0 -447
  97. package/dist/client-Nn0q4eO4.d.ts +0 -447
  98. package/dist/client-cache-CIlyBlmx.d.ts +0 -6
  99. package/dist/client-cache-D-EZ7XVW.d.mts +0 -6
  100. package/dist/client-plugins-Bc5jHcNr.d.ts +0 -1927
  101. package/dist/client-plugins-jsk-ZLzJ.d.mts +0 -1927
  102. package/dist/client-plugins.d.mts +0 -22
  103. package/dist/client-plugins.d.ts +0 -22
  104. package/dist/client.d.mts +0 -447
  105. package/dist/client.d.ts +0 -447
  106. package/dist/config.d.mts +0 -29
  107. package/dist/config.d.ts +0 -29
  108. package/dist/cron.d.mts +0 -78
  109. package/dist/cron.d.ts +0 -78
  110. package/dist/deferred.d.mts +0 -42
  111. package/dist/deferred.d.ts +0 -42
  112. package/dist/deployment.d.mts +0 -34
  113. package/dist/deployment.d.ts +0 -34
  114. package/dist/docs.d.mts +0 -69
  115. package/dist/docs.d.ts +0 -69
  116. package/dist/env-types.d.mts +0 -12
  117. package/dist/env-types.d.ts +0 -12
  118. package/dist/env.d.mts +0 -45
  119. package/dist/env.d.ts +0 -45
  120. package/dist/environment/vite.d.mts +0 -22
  121. package/dist/environment/vite.d.ts +0 -22
  122. package/dist/environment.d.mts +0 -18
  123. package/dist/environment.d.ts +0 -18
  124. package/dist/font.d.mts +0 -70
  125. package/dist/font.d.ts +0 -70
  126. package/dist/headers.d.mts +0 -22
  127. package/dist/headers.d.ts +0 -22
  128. package/dist/i18n/client.d.mts +0 -32
  129. package/dist/i18n/client.d.ts +0 -32
  130. package/dist/i18n/index.d.mts +0 -20
  131. package/dist/i18n/index.d.ts +0 -20
  132. package/dist/i18n/server.d.mts +0 -30
  133. package/dist/i18n/server.d.ts +0 -30
  134. package/dist/image/server.d.mts +0 -37
  135. package/dist/image/server.d.ts +0 -37
  136. package/dist/image/sharp.d.mts +0 -7
  137. package/dist/image/sharp.d.ts +0 -7
  138. package/dist/image-config-Duob7wER.d.mts +0 -50
  139. package/dist/image-config-Duob7wER.d.ts +0 -50
  140. package/dist/image.d.mts +0 -39
  141. package/dist/image.d.ts +0 -39
  142. package/dist/index.d.mts +0 -455
  143. package/dist/index.d.ts +0 -455
  144. package/dist/integration-api-CHT2OKAf.d.mts +0 -93
  145. package/dist/integration-api-CHT2OKAf.d.ts +0 -93
  146. package/dist/integrations.d.mts +0 -22
  147. package/dist/integrations.d.ts +0 -22
  148. package/dist/internal/build-runtime.d.mts +0 -39
  149. package/dist/internal/build-runtime.d.ts +0 -39
  150. package/dist/internal/client-runtime.d.mts +0 -2
  151. package/dist/internal/client-runtime.d.ts +0 -2
  152. package/dist/internal/config-runtime.d.mts +0 -39
  153. package/dist/internal/config-runtime.d.ts +0 -39
  154. package/dist/internal/production-node-env.d.mts +0 -12
  155. package/dist/internal/production-node-env.d.ts +0 -12
  156. package/dist/internal/production-runtime.d.mts +0 -41
  157. package/dist/internal/production-runtime.d.ts +0 -41
  158. package/dist/markdown.d.mts +0 -60
  159. package/dist/markdown.d.ts +0 -60
  160. package/dist/metadata-7ffpT3jr.d.mts +0 -22
  161. package/dist/metadata-JHn-8eht.d.ts +0 -22
  162. package/dist/middleware.d.mts +0 -164
  163. package/dist/middleware.d.ts +0 -164
  164. package/dist/navigation-errors-Dvwpzaer.d.mts +0 -13
  165. package/dist/navigation-errors-Dvwpzaer.d.ts +0 -13
  166. package/dist/navigation.d.mts +0 -14
  167. package/dist/navigation.d.ts +0 -14
  168. package/dist/observability.d.mts +0 -335
  169. package/dist/observability.d.ts +0 -335
  170. package/dist/params-BALtrRim.d.ts +0 -15
  171. package/dist/params-ry-lxqmy.d.mts +0 -15
  172. package/dist/plugin-BJDf43dc.d.mts +0 -165
  173. package/dist/plugin-BJDf43dc.d.ts +0 -165
  174. package/dist/plugin.d.mts +0 -22
  175. package/dist/plugin.d.ts +0 -22
  176. package/dist/production-runtime-9KG7GuvW.d.ts +0 -79
  177. package/dist/production-runtime-B-fweKts.d.mts +0 -79
  178. package/dist/production-runtime-C_B79w2P.d.mts +0 -28
  179. package/dist/production-runtime-DDEM1D2q.d.ts +0 -28
  180. package/dist/query/client.d.mts +0 -61
  181. package/dist/query/client.d.ts +0 -61
  182. package/dist/query/index.d.mts +0 -90
  183. package/dist/query/index.d.ts +0 -90
  184. package/dist/query/parsers.d.mts +0 -25
  185. package/dist/query/parsers.d.ts +0 -25
  186. package/dist/query/server.d.mts +0 -55
  187. package/dist/query/server.d.ts +0 -55
  188. package/dist/request-Dsq4kGW0.d.mts +0 -6
  189. package/dist/request-udtobOLv.d.ts +0 -6
  190. package/dist/request.d.mts +0 -2
  191. package/dist/request.d.ts +0 -2
  192. package/dist/resolver-DJqjLMg_.d.mts +0 -14
  193. package/dist/resolver-IH4RmPEl.d.ts +0 -14
  194. package/dist/route-runtime-DoOHzCAD.d.mts +0 -38
  195. package/dist/route-runtime-DoOHzCAD.d.ts +0 -38
  196. package/dist/router.d.mts +0 -36
  197. package/dist/router.d.ts +0 -36
  198. package/dist/routes.d.mts +0 -224
  199. package/dist/routes.d.ts +0 -224
  200. package/dist/runtime-9nQjVM7X.d.mts +0 -206
  201. package/dist/runtime-Cxpq6xZN.d.mts +0 -27
  202. package/dist/runtime-DgeS2skB.d.ts +0 -206
  203. package/dist/runtime-bv63eUiO.d.ts +0 -27
  204. package/dist/server-action-security.d.mts +0 -43
  205. package/dist/server-action-security.d.ts +0 -43
  206. package/dist/server-fn-client.d.mts +0 -32
  207. package/dist/server-fn-client.d.ts +0 -32
  208. package/dist/server-fn.d.mts +0 -93
  209. package/dist/server-fn.d.ts +0 -93
  210. package/dist/server-plugins.d.mts +0 -55
  211. package/dist/server-plugins.d.ts +0 -55
  212. package/dist/server-query-client.d.mts +0 -50
  213. package/dist/server-query-client.d.ts +0 -50
  214. package/dist/server-query.d.mts +0 -36
  215. package/dist/server-query.d.ts +0 -36
  216. package/dist/server.d.mts +0 -48
  217. package/dist/server.d.ts +0 -48
  218. package/dist/storage.d.mts +0 -52
  219. package/dist/storage.d.ts +0 -52
  220. package/dist/testing.d.mts +0 -101
  221. package/dist/testing.d.ts +0 -101
  222. package/dist/transport-BmbGTXA5.d.mts +0 -49
  223. package/dist/transport-BmbGTXA5.d.ts +0 -49
  224. package/dist/types-BMxWr97v.d.mts +0 -81
  225. package/dist/types-BMxWr97v.d.ts +0 -81
  226. package/dist/types-C9N-nk5R.d.mts +0 -53
  227. package/dist/types-C9N-nk5R.d.ts +0 -53
  228. package/dist/types-D1A6-XvH.d.mts +0 -73
  229. package/dist/types-D1A6-XvH.d.ts +0 -73
  230. package/dist/utils-CA0z0c-2.d.ts +0 -23
  231. package/dist/utils-mYVkSOt2.d.mts +0 -23
  232. package/dist/version.d.mts +0 -3
  233. package/dist/version.d.ts +0 -3
  234. package/dist/vite.d.mts +0 -22
  235. package/dist/vite.d.ts +0 -22
  236. package/dist/workflows.d.mts +0 -110
  237. package/dist/workflows.d.ts +0 -110
  238. /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-ARCGE2VT.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"
@@ -4793,39 +5190,48 @@ async function createRouteTypeDeclarations(options, outPath) {
4793
5190
  const { root, srcDir = "src", extraRoutes = [], suppressLintOnLink = false } = options;
4794
5191
  const sourceRoots = options.sourceRoots ?? [{ name: "project", root, srcDir, layer: false }];
4795
5192
  const patterns = /* @__PURE__ */ new Set();
5193
+ const routeModulePatterns = /* @__PURE__ */ new Set();
4796
5194
  const glob = await import("fast-glob");
4797
5195
  for (const source of sourceRoots) {
4798
5196
  const appDir = path7.join(source.root, source.srcDir, "app");
4799
5197
  if (fs3.existsSync(appDir)) {
4800
- const pageFiles = await glob.default("**/page.{ts,tsx,js,jsx,md,mdx}", {
4801
- cwd: appDir,
4802
- absolute: false
4803
- });
4804
- for (const file of pageFiles) {
5198
+ const routeModuleFiles = await glob.default(
5199
+ "**/{page,layout,loading,error}.{ts,tsx,js,jsx,md,mdx}",
5200
+ {
5201
+ cwd: appDir,
5202
+ absolute: false
5203
+ }
5204
+ );
5205
+ for (const file of routeModuleFiles) {
4805
5206
  if (parseRouteSlotFile(file)) continue;
4806
5207
  const route = parseRoutePath(file);
5208
+ const pattern = createRoutePattern(route);
5209
+ routeModulePatterns.add(pattern);
4807
5210
  if (route.type === "page") {
4808
- const pattern = createRoutePattern(route);
4809
5211
  patterns.add(pattern);
4810
5212
  }
4811
5213
  }
4812
5214
  }
4813
5215
  for (const route of await discoverProgrammaticRoutePaths(source.root, source.srcDir)) {
4814
5216
  patterns.add(route);
5217
+ routeModulePatterns.add(route);
4815
5218
  }
4816
5219
  }
4817
5220
  for (const route of extraRoutes) {
4818
5221
  if (route.startsWith("/")) {
4819
5222
  patterns.add(route);
5223
+ routeModulePatterns.add(route);
4820
5224
  }
4821
5225
  }
4822
5226
  const sortedPatterns = Array.from(patterns).sort();
4823
5227
  const typeLiterals = Array.from(new Set(sortedPatterns.flatMap(routePatternToTsTypeLiterals)));
4824
5228
  const patternLiterals = sortedPatterns.map(routePatternToRouteLiteral);
5229
+ const routeModulePatternLiterals = Array.from(routeModulePatterns).sort().map(routePatternToRouteLiteral);
4825
5230
  const routePathType = suppressLintOnLink ? "string" : typeLiterals.length ? typeLiterals.join(" | ") : "never";
4826
5231
  const routePatternType = suppressLintOnLink ? "string" : patternLiterals.length ? patternLiterals.join(" | ") : "never";
5232
+ const routeModulePatternType = routeModulePatternLiterals.length ? routeModulePatternLiterals.join(" | ") : "never";
4827
5233
  const routeTypesImportPath = `./${path7.basename(outPath).replace(/\.d\.ts$/, "")}`;
4828
- const augmentationBlock = suppressLintOnLink ? "" : `
5234
+ const linkAugmentationBlock = suppressLintOnLink ? "" : `
4829
5235
  declare module "@farm.js/core/client" {
4830
5236
  interface LinkDefaultRoute {
4831
5237
  _: import(${JSON.stringify(routeTypesImportPath)}).RoutePath;
@@ -4849,14 +5255,25 @@ declare module "@farm.js/core/dist/client.js" {
4849
5255
  pattern: import(${JSON.stringify(routeTypesImportPath)}).RoutePattern;
4850
5256
  }
4851
5257
  }
5258
+ `;
5259
+ const routeModuleAugmentationBlock = `
5260
+ declare global {
5261
+ namespace FarmJS {
5262
+ interface RouteRegistry {
5263
+ pattern: import(${JSON.stringify(routeTypesImportPath)}).RouteModulePattern;
5264
+ }
5265
+ }
5266
+ }
4852
5267
  `;
4853
5268
  const content = `/**
4854
5269
  * Auto-generated route types from src/app.
4855
- * Link href is typed automatically via module augmentation. Regenerated on dev start and when routes change.
5270
+ * Link href and route component props are typed automatically from generated declarations.
5271
+ * Regenerated on dev start and when routes change.
4856
5272
  * Set suppressLintOnLink: true in farm.config.ts to accept any string on Link href.
4857
5273
  */
4858
5274
  export type RoutePath = ${routePathType};
4859
- export type RoutePattern = ${routePatternType};${augmentationBlock}
5275
+ export type RoutePattern = ${routePatternType};
5276
+ export type RouteModulePattern = ${routeModulePatternType};${linkAugmentationBlock}${routeModuleAugmentationBlock}
4860
5277
  `;
4861
5278
  return content;
4862
5279
  }
@@ -5076,7 +5493,7 @@ __name(removeLegacyTypeArtifacts, "removeLegacyTypeArtifacts");
5076
5493
 
5077
5494
  // src/vite.ts
5078
5495
  import * as fs4 from "fs";
5079
- import * as path10 from "path";
5496
+ import * as path11 from "path";
5080
5497
 
5081
5498
  // src/image-vite.ts
5082
5499
  import { readFile as readFile4 } from "fs/promises";
@@ -5147,6 +5564,100 @@ async function createBlurDataURL(bytes) {
5147
5564
  }
5148
5565
  __name(createBlurDataURL, "createBlurDataURL");
5149
5566
 
5567
+ // src/server/vite-config.ts
5568
+ import path10 from "path";
5569
+ var FARM_CLIENT_ENTRY_PATTERN = "**/{page,layout,loading,error,not-found,default}.{js,jsx,ts,tsx}";
5570
+ var FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE = [
5571
+ "react",
5572
+ "react-dom",
5573
+ "react-dom/client",
5574
+ "react/jsx-runtime",
5575
+ "react/jsx-dev-runtime",
5576
+ "@farm.js/core/client",
5577
+ "@farm.js/core/plugin/client",
5578
+ "@farm.js/core/deferred",
5579
+ "@farm.js/core/deployment",
5580
+ "@farm.js/core/i18n/client",
5581
+ "@farm.js/core/query/client",
5582
+ "@farm.js/core/server-fn/client",
5583
+ "@farm.js/core/server-query/client"
5584
+ ];
5585
+ function createFarmClientOptimizeDepsEntries(projectRoot, appDirectories) {
5586
+ const resolvedProjectRoot = path10.resolve(projectRoot);
5587
+ return appDirectories.map((appDirectory) => {
5588
+ const relativeAppDirectory = path10.relative(resolvedProjectRoot, path10.resolve(appDirectory)).replace(/\\/g, "/");
5589
+ const prefix = relativeAppDirectory || ".";
5590
+ return `${prefix}/${FARM_CLIENT_ENTRY_PATTERN}`;
5591
+ });
5592
+ }
5593
+ __name(createFarmClientOptimizeDepsEntries, "createFarmClientOptimizeDepsEntries");
5594
+ function createFarmClientOptimizeDepsConfig(entries) {
5595
+ return {
5596
+ // Keep normal application dependency discovery for CJS-only packages, but
5597
+ // seed every Farm browser runtime so React is present in the first crawl.
5598
+ noDiscovery: false,
5599
+ // Waiting for the crawl avoids serving a partial optimizer generation and
5600
+ // then changing browser hashes while hydration is already in progress.
5601
+ holdUntilCrawlEnd: true,
5602
+ include: [...FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE],
5603
+ // Farm's HTML boots through a virtual module and imports route UI lazily.
5604
+ // Explicit entries let Vite finish that crawl before a navigation can
5605
+ // rotate the optimizer hash underneath a hydrated React runtime.
5606
+ ...entries?.length ? { entries: [...entries] } : {}
5607
+ };
5608
+ }
5609
+ __name(createFarmClientOptimizeDepsConfig, "createFarmClientOptimizeDepsConfig");
5610
+ function mergeNoExternal(farmValue, userValue) {
5611
+ if (farmValue === true || userValue === true) return true;
5612
+ const normalize = /* @__PURE__ */ __name((value) => value == null || value === true ? [] : Array.isArray(value) ? value : [value], "normalize");
5613
+ return Array.from(/* @__PURE__ */ new Set([...normalize(farmValue), ...normalize(userValue)]));
5614
+ }
5615
+ __name(mergeNoExternal, "mergeNoExternal");
5616
+ function mergeFarmViteConfig(farmConfig, userConfig = {}) {
5617
+ const noDiscovery = userConfig.optimizeDeps?.noDiscovery ?? farmConfig.optimizeDeps?.noDiscovery;
5618
+ const holdUntilCrawlEnd = userConfig.optimizeDeps?.holdUntilCrawlEnd ?? (userConfig.optimizeDeps?.noDiscovery === true ? false : farmConfig.optimizeDeps?.holdUntilCrawlEnd);
5619
+ return {
5620
+ ...farmConfig,
5621
+ ...userConfig,
5622
+ plugins: [...farmConfig.plugins || [], ...userConfig.plugins || []],
5623
+ server: {
5624
+ ...farmConfig.server,
5625
+ ...userConfig.server
5626
+ },
5627
+ resolve: {
5628
+ ...farmConfig.resolve,
5629
+ ...userConfig.resolve,
5630
+ dedupe: Array.from(
5631
+ /* @__PURE__ */ new Set([...farmConfig.resolve?.dedupe || [], ...userConfig.resolve?.dedupe || []])
5632
+ )
5633
+ },
5634
+ optimizeDeps: {
5635
+ ...farmConfig.optimizeDeps,
5636
+ ...userConfig.optimizeDeps,
5637
+ noDiscovery,
5638
+ holdUntilCrawlEnd,
5639
+ include: Array.from(
5640
+ /* @__PURE__ */ new Set([
5641
+ ...farmConfig.optimizeDeps?.include || [],
5642
+ ...userConfig.optimizeDeps?.include || []
5643
+ ])
5644
+ ),
5645
+ exclude: Array.from(
5646
+ /* @__PURE__ */ new Set([
5647
+ ...farmConfig.optimizeDeps?.exclude || [],
5648
+ ...userConfig.optimizeDeps?.exclude || []
5649
+ ])
5650
+ )
5651
+ },
5652
+ ssr: {
5653
+ ...farmConfig.ssr,
5654
+ ...userConfig.ssr,
5655
+ noExternal: mergeNoExternal(farmConfig.ssr?.noExternal, userConfig.ssr?.noExternal)
5656
+ }
5657
+ };
5658
+ }
5659
+ __name(mergeFarmViteConfig, "mergeFarmViteConfig");
5660
+
5150
5661
  // src/vite.ts
5151
5662
  var ALL_TYPE_ARTIFACTS = {
5152
5663
  routes: true,
@@ -5189,7 +5700,7 @@ function loadFarmWorkflowsDevRuntime() {
5189
5700
  }
5190
5701
  __name(loadFarmWorkflowsDevRuntime, "loadFarmWorkflowsDevRuntime");
5191
5702
  function loadFarmDevtoolsSnapshotRuntime() {
5192
- return import("./devtools-A22KUJZT.mjs");
5703
+ return import("./devtools-GAZ7F3KL.mjs");
5193
5704
  }
5194
5705
  __name(loadFarmDevtoolsSnapshotRuntime, "loadFarmDevtoolsSnapshotRuntime");
5195
5706
  function loadFarmDevtoolsUIRuntime() {
@@ -5368,7 +5879,7 @@ function fileContainsProgrammaticPageRoute(file) {
5368
5879
  __name(fileContainsProgrammaticPageRoute, "fileContainsProgrammaticPageRoute");
5369
5880
  function rewriteEarlySsrRelativeImports(options) {
5370
5881
  const cleanId = options.id.split("?", 1)[0];
5371
- if (!path10.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
5882
+ if (!path11.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
5372
5883
  return null;
5373
5884
  }
5374
5885
  const replacements = [];
@@ -5388,7 +5899,7 @@ function rewriteEarlySsrRelativeImports(options) {
5388
5899
  const suffixIndex = source.value.search(/[?#]/);
5389
5900
  const sourcePath = suffixIndex === -1 ? source.value : source.value.slice(0, suffixIndex);
5390
5901
  const suffix = suffixIndex === -1 ? "" : source.value.slice(suffixIndex);
5391
- const resolvedPath = path10.resolve(path10.dirname(cleanId), sourcePath);
5902
+ const resolvedPath = path11.resolve(path11.dirname(cleanId), sourcePath);
5392
5903
  replacements.push({
5393
5904
  start: source.start,
5394
5905
  end: source.end,
@@ -5473,7 +5984,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5473
5984
  ...layerRoots.length ? {
5474
5985
  server: {
5475
5986
  fs: {
5476
- allow: [path10.resolve(options.root || process.cwd()), ...layerRoots]
5987
+ allow: [path11.resolve(options.root || process.cwd()), ...layerRoots]
5477
5988
  }
5478
5989
  }
5479
5990
  } : {}
@@ -5504,9 +6015,9 @@ function farmPlugin(options = {}, initialPluginManager) {
5504
6015
  },
5505
6016
  server
5506
6017
  );
5507
- const globalsCSSPath = path10.join(server.config.root, "src/app/globals.css");
6018
+ const globalsCSSPath = path11.join(server.config.root, "src/app/globals.css");
5508
6019
  if (!fs4.existsSync(globalsCSSPath)) {
5509
- const appDir = path10.join(server.config.root, "src/app");
6020
+ const appDir = path11.join(server.config.root, "src/app");
5510
6021
  if (!fs4.existsSync(appDir)) {
5511
6022
  fs4.mkdirSync(appDir, { recursive: true });
5512
6023
  }
@@ -5528,10 +6039,10 @@ function farmPlugin(options = {}, initialPluginManager) {
5528
6039
  });
5529
6040
  }
5530
6041
  const sourceRoots = getFarmSourceRoots(farmConfig);
5531
- server.watcher.add(sourceRoots.map((source) => path10.join(source.root, source.srcDir)));
6042
+ server.watcher.add(sourceRoots.map((source) => path11.join(source.root, source.srcDir)));
5532
6043
  if (farmConfig.i18n.enabled) {
5533
6044
  server.watcher.add(
5534
- farmConfig.i18n.messages.includes("{locale}") ? path10.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
6045
+ farmConfig.i18n.messages.includes("{locale}") ? path11.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
5535
6046
  );
5536
6047
  }
5537
6048
  const workflowConfig = farmConfig.workflows;
@@ -5541,7 +6052,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5541
6052
  ...farmDocsDevRuntime?.getFarmDocsRouteTypeEntries(farmConfig.docs) ?? []
5542
6053
  ], "getExtraRouteTypes");
5543
6054
  const appDirSlugs = sourceRoots.map(
5544
- (source) => path10.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
6055
+ (source) => path11.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
5545
6056
  );
5546
6057
  const generateTypeArtifacts = /* @__PURE__ */ __name(async (reason, selection = ALL_TYPE_ARTIFACTS, log = false) => {
5547
6058
  try {
@@ -5581,7 +6092,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5581
6092
  }, "generateTypeArtifacts");
5582
6093
  await generateTypeArtifacts("startup");
5583
6094
  const srcDirSlugs = sourceRoots.map(
5584
- (source) => path10.join(source.root, source.srcDir).replace(/\\/g, "/")
6095
+ (source) => path11.join(source.root, source.srcDir).replace(/\\/g, "/")
5585
6096
  );
5586
6097
  const layerConfigFiles = new Set(
5587
6098
  farmConfig.layers.map((layer) => layer.configFile?.replace(/\\/g, "/")).filter((file) => Boolean(file))
@@ -5699,7 +6210,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5699
6210
  await apiRouteManager.discoverRoutes();
5700
6211
  let discoveredWorkflows = [];
5701
6212
  const hasWorkflowDirectory = workflowConfig.enabled && workflowConfig.dirs.some(
5702
- (dir) => fs4.existsSync(path10.isAbsolute(dir) ? dir : path10.join(farmConfig.root, dir))
6213
+ (dir) => fs4.existsSync(path11.isAbsolute(dir) ? dir : path11.join(farmConfig.root, dir))
5703
6214
  );
5704
6215
  if (hasWorkflowDirectory) {
5705
6216
  const { createFarmWorkflowRequestHandler, discoverFarmWorkflows } = await loadFarmWorkflowsDevRuntime();
@@ -5763,10 +6274,20 @@ function farmPlugin(options = {}, initialPluginManager) {
5763
6274
  logger.info(`Refreshed routes: ${reason}`);
5764
6275
  }
5765
6276
  }, "refreshRouteDiscovery");
6277
+ const farmDocsFontAssetList = farmDocsDevRuntime ? resolveFarmDocsFontAssets(farmConfig.root) : [];
5766
6278
  const farmDocsHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsHandler(farmConfig.docs, {
5767
6279
  root: farmConfig.root,
5768
6280
  srcDir: farmConfig.srcDir,
5769
- clientEntry: "/@farm/client.js"
6281
+ clientEntry: "/@farm/client.js",
6282
+ fontAssets: toFarmDocsPublicFontAssets(farmDocsFontAssetList),
6283
+ resolveLayoutFonts: /* @__PURE__ */ __name(async (pathname) => {
6284
+ const layouts = routeManager.matchRoute(pathname).layouts;
6285
+ const layoutModules = await Promise.all(
6286
+ layouts.map((layout) => routeManager.loadLayoutModule(layout.modulePath))
6287
+ );
6288
+ return resolveFarmLayoutFonts(layoutModules);
6289
+ }, "resolveLayoutFonts"),
6290
+ fontStylesheetHref: "/@farm/fonts.css"
5770
6291
  }) : null;
5771
6292
  const farmDocsAPIHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsAPIHandler({
5772
6293
  rootDir: farmConfig.root,
@@ -5774,22 +6295,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5774
6295
  docs: farmConfig.docs
5775
6296
  }) : null;
5776
6297
  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
- ] : []
6298
+ farmDocsFontAssetList.map(({ url, sourcePath }) => [url, sourcePath])
5793
6299
  );
5794
6300
  const logRequest = /* @__PURE__ */ __name((method, urlPath, tag) => {
5795
6301
  try {
@@ -5853,11 +6359,12 @@ function farmPlugin(options = {}, initialPluginManager) {
5853
6359
  }
5854
6360
  }
5855
6361
  const farmDocsFontPath = farmDocsFontAssets.get(requestPathname);
5856
- if (farmDocsFontPath && fs4.existsSync(farmDocsFontPath)) {
6362
+ if ((requestMethod === "GET" || requestMethod === "HEAD") && farmDocsFontPath && fs4.existsSync(farmDocsFontPath)) {
5857
6363
  res.statusCode = 200;
5858
6364
  res.setHeader("Content-Type", "font/woff2");
5859
6365
  res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
5860
- fs4.createReadStream(farmDocsFontPath).pipe(res);
6366
+ if (requestMethod === "HEAD") res.end();
6367
+ else fs4.createReadStream(farmDocsFontPath).pipe(res);
5861
6368
  return;
5862
6369
  }
5863
6370
  if (requestMethod === "GET" && (requestPathname === FARM_DEVTOOLS_PATH || requestPathname === `${FARM_DEVTOOLS_PATH}.json`)) {
@@ -6463,6 +6970,7 @@ function farmPlugin(options = {}, initialPluginManager) {
6463
6970
  modulePath: toUrlPath(route.modulePath),
6464
6971
  isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
6465
6972
  shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
6973
+ islandStrategy: moduleMetadata.islandStrategy,
6466
6974
  metadata: {
6467
6975
  title: mergedMetadata.title,
6468
6976
  description: mergedMetadata.description
@@ -6847,6 +7355,7 @@ export const getManifest = () => ({
6847
7355
  search: route.search,
6848
7356
  isClientComponent: moduleMetadata.isClientComponent,
6849
7357
  shouldHydrate: moduleMetadata.shouldHydrate,
7358
+ islandStrategy: moduleMetadata.islandStrategy,
6850
7359
  preloads: [route.modulePath],
6851
7360
  // In dev, preload is just the module
6852
7361
  assets: []
@@ -6973,8 +7482,8 @@ if (import.meta.hot) {
6973
7482
  }
6974
7483
  logger.info(`Found ${ssgPages.length} SSG pages, ${ssrRoutes.length} SSR routes`);
6975
7484
  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");
7485
+ const outDir = path11.join(server?.config.root || process.cwd(), options.outDir || "dist");
7486
+ const clientDir = path11.join(outDir, "client");
6978
7487
  for (const page of ssgPages) {
6979
7488
  try {
6980
7489
  const staticRequest = new Request(new URL(page.urlPath, "http://farm.static"));
@@ -7026,8 +7535,8 @@ if (import.meta.hot) {
7026
7535
  <script type="module" src="/assets/client.js"></script>
7027
7536
  </body>
7028
7537
  </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 });
7538
+ const outputPath = page.urlPath === "/" ? path11.join(clientDir, "index.html") : path11.join(clientDir, page.urlPath + ".html");
7539
+ fs4.mkdirSync(path11.dirname(outputPath), { recursive: true });
7031
7540
  fs4.writeFileSync(outputPath, fullHtml);
7032
7541
  const revalidateInfo = page.revalidate ? ` (revalidate: ${page.revalidate}s)` : "";
7033
7542
  logger.success(` \u2713 ${page.urlPath}${revalidateInfo}`);
@@ -7036,7 +7545,7 @@ if (import.meta.hot) {
7036
7545
  logger.error(` \u2717 ${page.urlPath}: ${error}`);
7037
7546
  }
7038
7547
  }
7039
- const manifestPath = path10.join(outDir, "__ssg_manifest.json");
7548
+ const manifestPath = path11.join(outDir, "__ssg_manifest.json");
7040
7549
  fs4.writeFileSync(
7041
7550
  manifestPath,
7042
7551
  JSON.stringify(
@@ -7072,9 +7581,9 @@ if (import.meta.hot) {
7072
7581
  }
7073
7582
  const currentFarmConfig = farmApp?.getConfig();
7074
7583
  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;
7584
+ const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path11.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
7076
7585
  if (currentSrcRoot && normalizedFile.startsWith(`${currentSrcRoot}/`) && isProgrammaticRoutesFileName(normalizedFile)) {
7077
- logUpdate("PAGE", `updated ${path10.basename(file)}`);
7586
+ logUpdate("PAGE", `updated ${path11.basename(file)}`);
7078
7587
  for (const mod of modules) {
7079
7588
  server2.moduleGraph.invalidateModule(mod);
7080
7589
  }
@@ -7471,6 +7980,7 @@ import React from 'react'
7471
7980
  import { hydrateRoot, createRoot } from 'react-dom/client'
7472
7981
  import { installChunkErrorRecovery, SPARouter } from '@farm.js/core/client'
7473
7982
  import { createClientPluginManager } from '@farm.js/core/plugin/client'
7983
+ import { scheduleFarmIslandHydration } from '@farm.js/core/internal/client-runtime'
7474
7984
  import { reviveDeferredData } from '@farm.js/core/deferred'
7475
7985
  import {
7476
7986
  createFarmDeploymentMismatchError,
@@ -7723,6 +8233,7 @@ class LegacyManifestSPARouter {
7723
8233
 
7724
8234
  // Only prefetch routes that will hydrate on the client.
7725
8235
  if (!match.route.isClientComponent && !match.route.shouldHydrate) return;
8236
+ if (match.route.islandStrategy && match.route.islandStrategy !== 'load') return;
7726
8237
 
7727
8238
  const modulePath = match.route.modulePath;
7728
8239
  if (this.moduleCache.has(modulePath)) return;
@@ -7867,6 +8378,12 @@ let hasClientTakenOver = false;
7867
8378
  const routeSlotRoots = new Map();
7868
8379
  const routeSlotDefinitions = new Map();
7869
8380
  let activeRouteInterception = null;
8381
+ let pendingPageHydrationController = null;
8382
+
8383
+ function cancelPendingPageHydration() {
8384
+ pendingPageHydrationController?.abort();
8385
+ pendingPageHydrationController = null;
8386
+ }
7870
8387
 
7871
8388
  function normalizeServerProps(rawProps) {
7872
8389
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -7935,7 +8452,11 @@ async function hydrateInitialRouteSlots() {
7935
8452
  routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
7936
8453
  if (!slot.isClientComponent && !slot.shouldHydrate) continue;
7937
8454
  try {
7938
- hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
8455
+ const slotHydrated = await renderRouteSlot(slot, 'hydrate');
8456
+ hydrated = slotHydrated || hydrated;
8457
+ if (slotHydrated) {
8458
+ replayPreHydrationClicks(document.getElementById(slot.containerId));
8459
+ }
7939
8460
  } catch (error) {
7940
8461
  console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
7941
8462
  }
@@ -8006,16 +8527,29 @@ function applyCanonicalPathFromProps(props) {
8006
8527
  window.history.replaceState({ ...(window.history.state || {}), path: canonicalPath }, '', canonicalPath);
8007
8528
  }
8008
8529
 
8009
- function replayPreHydrationClicks() {
8010
- window.__FARM_HYDRATED__ = true;
8011
- document.documentElement.dataset.farmHydrated = 'true';
8530
+ function replayPreHydrationClicks(container = null) {
8531
+ if (!container) {
8532
+ markFarmHydrated();
8533
+ } else {
8534
+ container.dataset.farmIslandHydrated = 'true';
8535
+ }
8012
8536
 
8013
8537
  const queue = Array.isArray(window.__FARM_PREHYDRATION_CLICK_QUEUE__)
8014
8538
  ? window.__FARM_PREHYDRATION_CLICK_QUEUE__
8015
8539
  : [];
8016
8540
  if (queue.length === 0) return;
8017
8541
 
8018
- const queuedClicks = queue.splice(0, queue.length);
8542
+ const queuedClicks = [];
8543
+ const remainingClicks = [];
8544
+ for (const queuedClick of queue) {
8545
+ const target = queuedClick?.target;
8546
+ if (!container || (target instanceof Node && container.contains(target))) {
8547
+ queuedClicks.push(queuedClick);
8548
+ } else {
8549
+ remainingClicks.push(queuedClick);
8550
+ }
8551
+ }
8552
+ queue.splice(0, queue.length, ...remainingClicks);
8019
8553
  for (const queuedClick of queuedClicks) {
8020
8554
  const target = queuedClick?.target;
8021
8555
  if (!target || typeof target.click !== 'function') continue;
@@ -8024,6 +8558,11 @@ function replayPreHydrationClicks() {
8024
8558
  }
8025
8559
  }
8026
8560
 
8561
+ function markFarmHydrated() {
8562
+ window.__FARM_HYDRATED__ = true;
8563
+ document.documentElement.dataset.farmHydrated = 'true';
8564
+ }
8565
+
8027
8566
  async function buildClientHydrationElement(PageComponent, pageProps) {
8028
8567
  let element = React.createElement(PageComponent, pageProps);
8029
8568
  const loadingModulePath = window.__FARM_LOADING_MODULE__;
@@ -8166,9 +8705,10 @@ async function tryHydrateImportedPage(
8166
8705
  layouts,
8167
8706
  useHydrate = false,
8168
8707
  existingProps = null,
8708
+ signal = null,
8169
8709
  ) {
8170
8710
  const modulePath = route?.modulePath;
8171
- if (!modulePath) {
8711
+ if (!modulePath || signal?.aborted) {
8172
8712
  return false;
8173
8713
  }
8174
8714
 
@@ -8177,6 +8717,7 @@ async function tryHydrateImportedPage(
8177
8717
  pageModule = await import(/* @vite-ignore */ modulePath);
8178
8718
  pageModuleCache.set(modulePath, pageModule);
8179
8719
  }
8720
+ if (signal?.aborted || !container?.isConnected) return false;
8180
8721
 
8181
8722
  const PageComponent = pageModule?.default;
8182
8723
  if (!PageComponent) {
@@ -8191,6 +8732,7 @@ async function tryHydrateImportedPage(
8191
8732
  window.location.pathname,
8192
8733
  existingProps,
8193
8734
  );
8735
+ if (signal?.aborted || !container?.isConnected) return false;
8194
8736
 
8195
8737
  const wrappedElement = useHydrate && container?.id === '__farm_page__'
8196
8738
  ? wrapWithIntegrationProviders(
@@ -8201,6 +8743,7 @@ async function tryHydrateImportedPage(
8201
8743
  currentPageProps,
8202
8744
  layouts,
8203
8745
  );
8746
+ if (signal?.aborted || !container?.isConnected) return false;
8204
8747
 
8205
8748
  if (useHydrate) {
8206
8749
  try {
@@ -8231,6 +8774,7 @@ async function tryHydrateImportedPage(
8231
8774
  async function renderPage(pageData) {
8232
8775
  const container = document.getElementById('root');
8233
8776
  if (!container) return;
8777
+ cancelPendingPageHydration();
8234
8778
  const destination = window.location.pathname + window.location.search;
8235
8779
 
8236
8780
  if (pageData.interception) {
@@ -8247,6 +8791,7 @@ async function renderPage(pageData) {
8247
8791
  modulePath: pageData.modulePath,
8248
8792
  isClientComponent: pageData.isClientComponent === true,
8249
8793
  shouldHydrate: pageData.shouldHydrate === true,
8794
+ islandStrategy: pageData.islandStrategy || 'load',
8250
8795
  };
8251
8796
  const params = pageData.props?.params || {};
8252
8797
  const layouts = (pageData.layoutModules || []).map((modulePath, index) => ({
@@ -8446,14 +8991,6 @@ async function hydrate() {
8446
8991
  }
8447
8992
 
8448
8993
  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
8994
  // Check if this is a client component (set by SSR)
8458
8995
  const isClientComponent = window.__FARM_IS_CLIENT__ === true;
8459
8996
  const modulePath = window.__FARM_PAGE_MODULE__;
@@ -8501,34 +9038,88 @@ async function hydrate() {
8501
9038
  return
8502
9039
  }
8503
9040
 
8504
- const hydrationSession = await farmClientRuntime.beginHydration({
8505
- container: pageContainer,
8506
- mode: shouldHydrate ? 'hydrate' : 'render',
8507
- });
9041
+ const currentRoute = findRoute(window.location.pathname)?.route;
9042
+ const islandStrategy = window.__FARM_ISLAND_STRATEGY__ || currentRoute?.islandStrategy || 'load';
9043
+ pageContainer.dataset.farmIsland = 'page';
9044
+ pageContainer.dataset.farmIslandStrategy = islandStrategy;
8508
9045
 
8509
- let hydrated = false;
9046
+ const hydrationPathname = window.location.pathname;
9047
+ const hydrationController = new AbortController();
9048
+ pendingPageHydrationController = hydrationController;
8510
9049
  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
- }
9050
+ await scheduleFarmIslandHydration({
9051
+ container: pageContainer,
9052
+ strategy: islandStrategy,
9053
+ signal: hydrationController.signal,
9054
+ hydrate: async () => {
9055
+ if (
9056
+ hydrationController.signal.aborted ||
9057
+ !pageContainer.isConnected ||
9058
+ window.location.pathname !== hydrationPathname
9059
+ ) {
9060
+ return;
9061
+ }
8524
9062
 
8525
- if (hydrated) {
8526
- replayPreHydrationClicks();
8527
- console.log('[Farm.js] \u2705 Hydrated:', modulePath);
8528
- return;
8529
- }
9063
+ // Layout code is only needed when this route boundary actually hydrates.
9064
+ try {
9065
+ const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
9066
+ LayoutComponent = layoutModule.default;
9067
+ } catch (e) {
9068
+ console.warn('[Farm.js] Could not preload layout:', e);
9069
+ }
9070
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) return;
9071
+
9072
+ const hydrationSession = await farmClientRuntime.beginHydration({
9073
+ container: pageContainer,
9074
+ mode: shouldHydrate ? 'hydrate' : 'render',
9075
+ });
9076
+
9077
+ let hydrated = false;
9078
+ try {
9079
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
9080
+ await farmClientRuntime.failHydration(
9081
+ hydrationSession,
9082
+ new DOMException('Route hydration was cancelled', 'AbortError'),
9083
+ );
9084
+ return;
9085
+ }
9086
+ hydrated = await tryHydrateImportedPage(
9087
+ pageContainer,
9088
+ { modulePath },
9089
+ currentPageProps.params || {},
9090
+ layouts,
9091
+ shouldHydrate,
9092
+ currentPageProps,
9093
+ hydrationController.signal,
9094
+ );
9095
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
9096
+ await farmClientRuntime.failHydration(
9097
+ hydrationSession,
9098
+ new DOMException('Route hydration was cancelled', 'AbortError'),
9099
+ );
9100
+ return;
9101
+ }
9102
+ await farmClientRuntime.completeHydration(hydrationSession);
9103
+ } catch (error) {
9104
+ await farmClientRuntime.failHydration(hydrationSession, error);
9105
+ throw error;
9106
+ }
9107
+
9108
+ if (hydrated) {
9109
+ // The scheduler owns queue draining and exactly-once click replay.
9110
+ markFarmHydrated();
9111
+ console.log('[Farm.js] \u2705 Hydrated:', modulePath, '(' + islandStrategy + ')');
9112
+ return;
9113
+ }
8530
9114
 
8531
- console.log('[Farm.js] Server component - SPA router ready')
9115
+ console.log('[Farm.js] Server component - SPA router ready')
9116
+ },
9117
+ });
9118
+ } finally {
9119
+ if (pendingPageHydrationController === hydrationController) {
9120
+ pendingPageHydrationController = null;
9121
+ }
9122
+ }
8532
9123
  } catch (error) {
8533
9124
  console.error('[Farm.js] Hydration error:', error)
8534
9125
  }
@@ -8634,7 +9225,7 @@ function generateClientManifest(bundle) {
8634
9225
  __name(generateClientManifest, "generateClientManifest");
8635
9226
  async function defineConfig(config = {}) {
8636
9227
  const tailwindcss = (await import("@tailwindcss/vite")).default;
8637
- const appRoot = path10.resolve(config.root || process.cwd());
9228
+ const appRoot = path11.resolve(config.root || process.cwd());
8638
9229
  if (config.extends?.length) {
8639
9230
  const { config: layeredConfig } = await import("./layers-VWVDTPUZ.mjs").then(
8640
9231
  ({ resolveFarmLayers }) => resolveFarmLayers(config, {
@@ -8779,13 +9370,16 @@ async function defineConfig(config = {}) {
8779
9370
  customLogger: farmLogger,
8780
9371
  clearScreen: false,
8781
9372
  optimizeDeps: {
8782
- include: [
8783
- "react",
8784
- "react-dom",
8785
- "react-dom/client",
8786
- "react/jsx-runtime",
8787
- "react/jsx-dev-runtime"
8788
- ],
9373
+ ...createFarmClientOptimizeDepsConfig(
9374
+ createFarmClientOptimizeDepsEntries(
9375
+ appRoot,
9376
+ getFarmAppDirectories({ ...config, root: appRoot })
9377
+ )
9378
+ ),
9379
+ // Pre-bundle every framework client-runtime entry with React. Without
9380
+ // this, Vite can discover a linked Farm entry after the page has loaded,
9381
+ // regenerate the optimizer browser hash, and leave React DOM holding a
9382
+ // different React module instance from a client component.
8789
9383
  // Exclude server-side packages from browser bundling
8790
9384
  exclude: [
8791
9385
  "@farm.js/core/server",
@@ -8820,7 +9414,7 @@ async function defineConfig(config = {}) {
8820
9414
  // Stub out problematic server-only modules during dev mode
8821
9415
  alias: {
8822
9416
  ...getFarmLayerAliases(config.layers),
8823
- "@": path10.resolve(appRoot, "src"),
9417
+ "@": path11.resolve(appRoot, "src"),
8824
9418
  // Nitro internals that should not be resolved in browser
8825
9419
  "supports-color": "data:text/javascript,export default false; export const supportsColor = false; export const stdout = false; export const stderr = false;",
8826
9420
  "@poppinss/dumper": "data:text/javascript,export default {};",
@@ -8842,6 +9436,7 @@ __name(defineConfig, "defineConfig");
8842
9436
  export {
8843
9437
  resolveRouteRenderingConfig,
8844
9438
  resolveRouteRenderingConfigFromFile,
9439
+ analyzeStaticRouteCandidate,
8845
9440
  parseRouteRenderingDirective,
8846
9441
  collectSSGPages,
8847
9442
  isSSGModule,
@@ -8864,9 +9459,12 @@ export {
8864
9459
  createFarmImageTypeDeclarations,
8865
9460
  generateFarmI18nTypes,
8866
9461
  generateFarmTypeArtifacts,
9462
+ createFarmClientOptimizeDepsEntries,
9463
+ createFarmClientOptimizeDepsConfig,
9464
+ mergeFarmViteConfig,
8867
9465
  farmI18nClientBridgePlugin,
8868
9466
  rewriteEarlySsrRelativeImports,
8869
9467
  farmPlugin,
8870
9468
  defineConfig
8871
9469
  };
8872
- //# sourceMappingURL=chunk-77BOAQHI.mjs.map
9470
+ //# sourceMappingURL=chunk-PNFQQSRA.mjs.map