@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
package/dist/server.cjs CHANGED
@@ -652,26 +652,26 @@ async function prepareFarmWorkflowsForNitro(config) {
652
652
  const root = config.root || process.cwd();
653
653
  const distDir = config.distDir || ".farm";
654
654
  const fs9 = await import("fs/promises");
655
- const path25 = await import("path");
656
- const generatedDir = path25.join(root, distDir, ".nitro", "farm-workflows");
655
+ const path27 = await import("path");
656
+ const generatedDir = path27.join(root, distDir, ".nitro", "farm-workflows");
657
657
  await fs9.mkdir(generatedDir, { recursive: true });
658
658
  const tasks = {};
659
659
  const scheduledTasks = createScheduledTasks(workflows);
660
660
  for (const workflow of workflows) {
661
- const wrapperPath = path25.join(generatedDir, `${safeFileName(workflow.id)}.mjs`);
661
+ const wrapperPath = path27.join(generatedDir, `${safeFileName(workflow.id)}.mjs`);
662
662
  await fs9.writeFile(wrapperPath, createNitroTaskWrapper(workflow), "utf8");
663
663
  tasks[workflow.id] = {
664
664
  handler: wrapperPath,
665
665
  description: workflow.description || `Farm workflow ${workflow.id}`
666
666
  };
667
667
  }
668
- const handlerPath = path25.join(generatedDir, "http-handler.mjs");
668
+ const handlerPath = path27.join(generatedDir, "http-handler.mjs");
669
669
  await fs9.writeFile(
670
670
  handlerPath,
671
671
  createNitroWorkflowHTTPHandler(workflowConfig, workflows),
672
672
  "utf8"
673
673
  );
674
- const manifestPath = path25.join(generatedDir, "manifest.json");
674
+ const manifestPath = path27.join(generatedDir, "manifest.json");
675
675
  await fs9.writeFile(
676
676
  manifestPath,
677
677
  JSON.stringify(
@@ -754,10 +754,10 @@ function resolveWorkflowDefinition(module2) {
754
754
  }
755
755
  async function findWorkflowFiles(root, dirs) {
756
756
  const fs9 = await import("fs/promises");
757
- const path25 = await import("path");
757
+ const path27 = await import("path");
758
758
  const files = [];
759
759
  for (const dir of dirs) {
760
- const absoluteDir = path25.isAbsolute(dir) ? dir : path25.join(root, dir);
760
+ const absoluteDir = path27.isAbsolute(dir) ? dir : path27.join(root, dir);
761
761
  if (!await pathExists(absoluteDir)) continue;
762
762
  await walkWorkflowDir(absoluteDir, files);
763
763
  }
@@ -765,10 +765,10 @@ async function findWorkflowFiles(root, dirs) {
765
765
  }
766
766
  async function walkWorkflowDir(dir, files) {
767
767
  const fs9 = await import("fs/promises");
768
- const path25 = await import("path");
768
+ const path27 = await import("path");
769
769
  const entries = await fs9.readdir(dir, { withFileTypes: true });
770
770
  for (const entry of entries) {
771
- const filePath = path25.join(dir, entry.name);
771
+ const filePath = path27.join(dir, entry.name);
772
772
  if (entry.isDirectory()) {
773
773
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
774
774
  await walkWorkflowDir(filePath, files);
@@ -788,12 +788,12 @@ function isWorkflowFile(fileName) {
788
788
  }
789
789
  async function loadWorkflowModule(filePath, root) {
790
790
  const fs9 = await import("fs/promises");
791
- const path25 = await import("path");
791
+ const path27 = await import("path");
792
792
  const { pathToFileURL: pathToFileURL5 } = await import("url");
793
793
  const { build } = await import("esbuild");
794
- const outDir = path25.join(root, ".farm", ".workflow-loader");
794
+ const outDir = path27.join(root, ".farm", ".workflow-loader");
795
795
  await fs9.mkdir(outDir, { recursive: true });
796
- const outfile = path25.join(
796
+ const outfile = path27.join(
797
797
  outDir,
798
798
  `workflow-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`
799
799
  );
@@ -1076,7 +1076,7 @@ function normalizeCronJob(name, job) {
1076
1076
  if (!job || typeof job !== "object" || Array.isArray(job)) {
1077
1077
  throw new TypeError(`Farm cron ${JSON.stringify(name)} must be a configuration object.`);
1078
1078
  }
1079
- const path25 = normalizeCronPath(name, job.path);
1079
+ const path27 = normalizeCronPath(name, job.path);
1080
1080
  const rawSchedule = Array.isArray(job.schedule) ? job.schedule : [job.schedule];
1081
1081
  const schedule = [...new Set(rawSchedule.map((value) => normalizeCronExpression(name, value)))];
1082
1082
  if (schedule.length === 0) {
@@ -1085,7 +1085,7 @@ function normalizeCronJob(name, job) {
1085
1085
  return {
1086
1086
  name,
1087
1087
  schedule,
1088
- path: path25,
1088
+ path: path27,
1089
1089
  description: job.description?.trim() || void 0
1090
1090
  };
1091
1091
  }
@@ -1093,13 +1093,13 @@ function normalizeCronPath(name, value) {
1093
1093
  if (typeof value !== "string" || !value.trim().startsWith("/")) {
1094
1094
  throw new TypeError(`Farm cron ${JSON.stringify(name)} path must start with "/".`);
1095
1095
  }
1096
- const path25 = value.trim();
1097
- if (path25.startsWith("//") || path25.includes("?") || path25.includes("#")) {
1096
+ const path27 = value.trim();
1097
+ if (path27.startsWith("//") || path27.includes("?") || path27.includes("#")) {
1098
1098
  throw new TypeError(
1099
1099
  `Farm cron ${JSON.stringify(name)} path must be an application pathname without a host, query, or hash.`
1100
1100
  );
1101
1101
  }
1102
- return path25.length > 1 ? path25.replace(/\/+$/, "") : path25;
1102
+ return path27.length > 1 ? path27.replace(/\/+$/, "") : path27;
1103
1103
  }
1104
1104
  function normalizeCronExpression(name, value) {
1105
1105
  if (typeof value !== "string") {
@@ -2364,7 +2364,7 @@ var init_server_action_security = __esm({
2364
2364
 
2365
2365
  // src/image-config.ts
2366
2366
  function resolveFarmImageConfig(config) {
2367
- const path25 = normalizeImagePath(config?.path ?? DEFAULT_FARM_IMAGE_PATH);
2367
+ const path27 = normalizeImagePath(config?.path ?? DEFAULT_FARM_IMAGE_PATH);
2368
2368
  const deviceSizes = normalizeIntegerList(
2369
2369
  config?.deviceSizes ?? DEFAULT_FARM_IMAGE_DEVICE_SIZES,
2370
2370
  "images.deviceSizes"
@@ -2392,7 +2392,7 @@ function resolveFarmImageConfig(config) {
2392
2392
  );
2393
2393
  return Object.freeze({
2394
2394
  provider: config?.provider ?? "auto",
2395
- path: path25,
2395
+ path: path27,
2396
2396
  domains: Object.freeze([
2397
2397
  ...new Set(
2398
2398
  (config?.domains ?? []).map((domain) => domain.trim().toLowerCase()).filter(Boolean)
@@ -2431,11 +2431,11 @@ function getPublicFarmImageConfig(config) {
2431
2431
  };
2432
2432
  }
2433
2433
  function normalizeImagePath(value) {
2434
- const path25 = value.trim().replace(/\/+$/, "") || "/";
2435
- if (!path25.startsWith("/") || path25.includes("?") || path25.includes("#")) {
2434
+ const path27 = value.trim().replace(/\/+$/, "") || "/";
2435
+ if (!path27.startsWith("/") || path27.includes("?") || path27.includes("#")) {
2436
2436
  throw new TypeError("images.path must be an absolute pathname without a query or hash");
2437
2437
  }
2438
- return path25;
2438
+ return path27;
2439
2439
  }
2440
2440
  function normalizeIntegerList(values, name, max) {
2441
2441
  if (values.length === 0) {
@@ -3274,9 +3274,9 @@ var init_integration_orm = __esm({
3274
3274
  });
3275
3275
 
3276
3276
  // src/integrations.ts
3277
- function defineTypedIntegrationRoute(method, path25, input) {
3277
+ function defineTypedIntegrationRoute(method, path27, input) {
3278
3278
  return {
3279
- path: path25,
3279
+ path: path27,
3280
3280
  method,
3281
3281
  middleware: input.middleware,
3282
3282
  before: input.before,
@@ -3286,7 +3286,7 @@ function defineTypedIntegrationRoute(method, path25, input) {
3286
3286
  input: normalizeIntegrationRouteInputSchemas(input),
3287
3287
  handler: input.handler,
3288
3288
  __operation: defineIntegrationAPIOperation({
3289
- path: path25,
3289
+ path: path27,
3290
3290
  method,
3291
3291
  bodyFormat: input.bodyFormat,
3292
3292
  responseFormat: input.responseFormat,
@@ -3298,52 +3298,52 @@ function defineTypedIntegrationRoute(method, path25, input) {
3298
3298
  }
3299
3299
  function createIntegrationRouteFactory() {
3300
3300
  return {
3301
- get(path25, input) {
3301
+ get(path27, input) {
3302
3302
  return defineTypedIntegrationRoute(
3303
3303
  "GET",
3304
- path25,
3304
+ path27,
3305
3305
  input
3306
3306
  );
3307
3307
  },
3308
- post(path25, input) {
3308
+ post(path27, input) {
3309
3309
  return defineTypedIntegrationRoute(
3310
3310
  "POST",
3311
- path25,
3311
+ path27,
3312
3312
  {
3313
3313
  bodyFormat: "json",
3314
3314
  ...input
3315
3315
  }
3316
3316
  );
3317
3317
  },
3318
- put(path25, input) {
3318
+ put(path27, input) {
3319
3319
  return defineTypedIntegrationRoute(
3320
3320
  "PUT",
3321
- path25,
3321
+ path27,
3322
3322
  {
3323
3323
  bodyFormat: "json",
3324
3324
  ...input
3325
3325
  }
3326
3326
  );
3327
3327
  },
3328
- patch(path25, input) {
3329
- return defineTypedIntegrationRoute("PATCH", path25, {
3328
+ patch(path27, input) {
3329
+ return defineTypedIntegrationRoute("PATCH", path27, {
3330
3330
  bodyFormat: "json",
3331
3331
  ...input
3332
3332
  });
3333
3333
  },
3334
- delete(path25, input) {
3335
- return defineTypedIntegrationRoute("DELETE", path25, {
3334
+ delete(path27, input) {
3335
+ return defineTypedIntegrationRoute("DELETE", path27, {
3336
3336
  bodyFormat: "json",
3337
3337
  ...input
3338
3338
  });
3339
3339
  },
3340
- options(path25, input) {
3341
- return defineTypedIntegrationRoute("OPTIONS", path25, input);
3340
+ options(path27, input) {
3341
+ return defineTypedIntegrationRoute("OPTIONS", path27, input);
3342
3342
  },
3343
- head(path25, input) {
3343
+ head(path27, input) {
3344
3344
  return defineTypedIntegrationRoute(
3345
3345
  "HEAD",
3346
- path25,
3346
+ path27,
3347
3347
  input
3348
3348
  );
3349
3349
  }
@@ -3560,8 +3560,8 @@ async function parseIntegrationConfigSchema(integration, schema, value) {
3560
3560
  function createIntegrationConfigValidationError(integration, error) {
3561
3561
  const issues = Array.isArray(error.issues) && error.issues.length > 0 ? error.issues.map((issue) => {
3562
3562
  const normalizedPath = normalizeIntegrationValidationPath(issue.path);
3563
- const path25 = normalizedPath.length ? `${normalizedPath.join(".")}: ` : "";
3564
- return `${path25}${issue.message || "Invalid config"}`;
3563
+ const path27 = normalizedPath.length ? `${normalizedPath.join(".")}: ` : "";
3564
+ return `${path27}${issue.message || "Invalid config"}`;
3565
3565
  }).join("; ") : error.message || "Invalid config";
3566
3566
  return new Error(`Integration "${integration.type}" config validation failed: ${issues}`);
3567
3567
  }
@@ -4246,8 +4246,8 @@ function normalizeIntegrationValidationIssues(source, error) {
4246
4246
  }
4247
4247
  ];
4248
4248
  }
4249
- function normalizeIntegrationValidationPath(path25) {
4250
- return (path25 || []).map((segment) => {
4249
+ function normalizeIntegrationValidationPath(path27) {
4250
+ return (path27 || []).map((segment) => {
4251
4251
  const key = typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment;
4252
4252
  return typeof key === "symbol" ? key.description || key.toString() : key;
4253
4253
  });
@@ -5040,8 +5040,8 @@ function createFarmDeploymentMismatchResponse(mismatch) {
5040
5040
  }
5041
5041
  );
5042
5042
  }
5043
- function createFarmDeploymentCookie(deploymentId, path25 = "/", secure = false) {
5044
- const normalizedPath = path25.startsWith("/") ? path25 : `/${path25}`;
5043
+ function createFarmDeploymentCookie(deploymentId, path27 = "/", secure = false) {
5044
+ const normalizedPath = path27.startsWith("/") ? path27 : `/${path27}`;
5045
5045
  return [
5046
5046
  `${FARM_DEPLOYMENT_COOKIE}=${encodeURIComponent(deploymentId)}`,
5047
5047
  `Path=${normalizedPath}`,
@@ -5325,6 +5325,33 @@ var init_auth_config = __esm({
5325
5325
  }
5326
5326
  });
5327
5327
 
5328
+ // src/preload.ts
5329
+ function resolveFarmPerformanceConfig(config) {
5330
+ return {
5331
+ preload: {
5332
+ mode: config?.preload?.mode === "warn" ? "warn" : "enforce",
5333
+ maxImages: normalizeBudget(config?.preload?.maxImages, DEFAULT_PRELOAD_CONFIG.maxImages),
5334
+ maxFonts: normalizeBudget(config?.preload?.maxFonts, DEFAULT_PRELOAD_CONFIG.maxFonts)
5335
+ }
5336
+ };
5337
+ }
5338
+ function normalizeBudget(value, fallback) {
5339
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
5340
+ }
5341
+ var DEFAULT_PRELOAD_CONFIG;
5342
+ var init_preload = __esm({
5343
+ "src/preload.ts"() {
5344
+ "use strict";
5345
+ DEFAULT_PRELOAD_CONFIG = {
5346
+ mode: "enforce",
5347
+ maxImages: 1,
5348
+ maxFonts: 2
5349
+ };
5350
+ __name(resolveFarmPerformanceConfig, "resolveFarmPerformanceConfig");
5351
+ __name(normalizeBudget, "normalizeBudget");
5352
+ }
5353
+ });
5354
+
5328
5355
  // src/write-file-if-changed.ts
5329
5356
  function writeFileIfChanged(filePath, content) {
5330
5357
  try {
@@ -5444,11 +5471,11 @@ var init_type_generator = __esm({
5444
5471
  routeGroups.get(key).push(route);
5445
5472
  }
5446
5473
  const nestedStructure = {};
5447
- for (const [path25, routeList] of routeGroups) {
5474
+ for (const [path27, routeList] of routeGroups) {
5448
5475
  const route = routeList[0];
5449
5476
  const importPath = this.getRouteImportPath(route, options.outFile);
5450
5477
  const routeName = this.pathToRouteName(route.path);
5451
- const cleanPath = path25 === "/api" ? "" : path25.replace(/^\/api\//, "");
5478
+ const cleanPath = path27 === "/api" ? "" : path27.replace(/^\/api\//, "");
5452
5479
  const parts = cleanPath ? cleanPath.split("/") : [];
5453
5480
  const allMethods = routeList.flatMap((r) => r.methods);
5454
5481
  for (const method of allMethods) {
@@ -5507,8 +5534,8 @@ ${typeExports}
5507
5534
  const relativeImport = (0, import_path7.relative)((0, import_path7.dirname)(outFile), route.filePath).replace(/\\/g, "/").replace(/\.(ts|tsx|js|jsx)$/, "");
5508
5535
  return relativeImport.startsWith(".") ? relativeImport : `./${relativeImport}`;
5509
5536
  }
5510
- pathToRouteName(path25) {
5511
- return (path25 === "/api" ? "root" : path25.replace(/^\/api\//, "")).replace(/\//g, "_").replace(/[^a-zA-Z0-9_]/g, "");
5537
+ pathToRouteName(path27) {
5538
+ return (path27 === "/api" ? "root" : path27.replace(/^\/api\//, "")).replace(/\//g, "_").replace(/[^a-zA-Z0-9_]/g, "");
5512
5539
  }
5513
5540
  structureToTypeString(obj, indent) {
5514
5541
  const spaces = " ".repeat(indent);
@@ -5528,15 +5555,15 @@ ${typeExports}
5528
5555
  toTypePropertyKey(key) {
5529
5556
  return /^[$A-Z_][0-9A-Z_$]*$/i.test(key) ? key : JSON.stringify(key);
5530
5557
  }
5531
- getBaseExportName(path25) {
5532
- const cleanPath = path25.replace(/^\/api\//, "");
5558
+ getBaseExportName(path27) {
5559
+ const cleanPath = path27.replace(/^\/api\//, "");
5533
5560
  if (cleanPath === "") {
5534
5561
  return "api";
5535
5562
  }
5536
5563
  return cleanPath;
5537
5564
  }
5538
- getExportName(path25, method) {
5539
- const cleanPath = path25.replace(/^\/api\//, "");
5565
+ getExportName(path27, method) {
5566
+ const cleanPath = path27.replace(/^\/api\//, "");
5540
5567
  if (cleanPath === "") {
5541
5568
  return method.toLowerCase();
5542
5569
  }
@@ -5978,28 +6005,71 @@ var init_image_server = __esm({
5978
6005
  }
5979
6006
  });
5980
6007
 
6008
+ // src/docs/fonts.ts
6009
+ function resolveFarmDocsFontAssets(root) {
6010
+ return FARM_DOCS_FONT_SOURCES.flatMap(({ family, packagePath }) => {
6011
+ const sourcePath = import_node_path10.default.join(root, "node_modules", packagePath);
6012
+ if (!(0, import_node_fs6.existsSync)(sourcePath)) return [];
6013
+ const source = (0, import_node_fs6.readFileSync)(sourcePath);
6014
+ const fingerprint = (0, import_node_crypto3.createHash)("sha256").update(source).digest("hex").slice(0, 12);
6015
+ const extension = import_node_path10.default.extname(packagePath);
6016
+ const baseName = import_node_path10.default.basename(packagePath, extension);
6017
+ return [
6018
+ {
6019
+ family,
6020
+ sourcePath,
6021
+ url: `/assets/fonts/${baseName}-h${fingerprint}${extension}`
6022
+ }
6023
+ ];
6024
+ });
6025
+ }
6026
+ function toFarmDocsPublicFontAssets(assets) {
6027
+ return assets.map(({ family, url }) => ({ family, url }));
6028
+ }
6029
+ var import_node_crypto3, import_node_fs6, import_node_path10, FARM_DOCS_FONT_SOURCES;
6030
+ var init_fonts = __esm({
6031
+ "src/docs/fonts.ts"() {
6032
+ "use strict";
6033
+ import_node_crypto3 = require("crypto");
6034
+ import_node_fs6 = require("fs");
6035
+ import_node_path10 = __toESM(require("path"));
6036
+ FARM_DOCS_FONT_SOURCES = [
6037
+ {
6038
+ family: "Geist Sans",
6039
+ packagePath: "geist/dist/fonts/geist-sans/Geist-Variable.woff2"
6040
+ },
6041
+ {
6042
+ family: "Geist Mono",
6043
+ packagePath: "geist/dist/fonts/geist-mono/GeistMono-Variable.woff2"
6044
+ }
6045
+ ];
6046
+ __name(resolveFarmDocsFontAssets, "resolveFarmDocsFontAssets");
6047
+ __name(toFarmDocsPublicFontAssets, "toFarmDocsPublicFontAssets");
6048
+ }
6049
+ });
6050
+
5981
6051
  // src/docs/last-modified.ts
5982
6052
  function normalizeRelativePath(value) {
5983
6053
  return value.replace(/\\/g, "/").replace(/^\.\/+/, "");
5984
6054
  }
5985
6055
  function resolveExistingPath(filePath) {
5986
6056
  try {
5987
- return (0, import_node_fs6.realpathSync)(filePath);
6057
+ return (0, import_node_fs7.realpathSync)(filePath);
5988
6058
  } catch {
5989
- return import_node_path9.default.resolve(filePath);
6059
+ return import_node_path11.default.resolve(filePath);
5990
6060
  }
5991
6061
  }
5992
6062
  function isDocsSourceFile(filePath) {
5993
- return DOCS_FILE_EXTENSIONS.has(import_node_path9.default.extname(filePath).toLowerCase());
6063
+ return DOCS_FILE_EXTENSIONS.has(import_node_path11.default.extname(filePath).toLowerCase());
5994
6064
  }
5995
6065
  function discoverDocsSourceFiles(contentDir) {
5996
6066
  const files = [];
5997
6067
  const visit = /* @__PURE__ */ __name((dir) => {
5998
- for (const entry of (0, import_node_fs6.readdirSync)(dir, { withFileTypes: true }).sort(
6068
+ for (const entry of (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true }).sort(
5999
6069
  (a, b) => a.name.localeCompare(b.name)
6000
6070
  )) {
6001
6071
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
6002
- const absolutePath = import_node_path9.default.join(dir, entry.name);
6072
+ const absolutePath = import_node_path11.default.join(dir, entry.name);
6003
6073
  if (entry.isDirectory()) {
6004
6074
  visit(absolutePath);
6005
6075
  } else if (entry.isFile() && isDocsSourceFile(absolutePath)) {
@@ -6007,7 +6077,7 @@ function discoverDocsSourceFiles(contentDir) {
6007
6077
  }
6008
6078
  }
6009
6079
  }, "visit");
6010
- if ((0, import_node_fs6.existsSync)(contentDir)) visit(contentDir);
6080
+ if ((0, import_node_fs7.existsSync)(contentDir)) visit(contentDir);
6011
6081
  return files;
6012
6082
  }
6013
6083
  function getGitLastModifiedDates(contentDir) {
@@ -6017,7 +6087,7 @@ function getGitLastModifiedDates(contentDir) {
6017
6087
  stdio: ["ignore", "pipe", "ignore"]
6018
6088
  }).trim();
6019
6089
  if (!gitRoot) return {};
6020
- const contentPathspec = normalizeRelativePath(import_node_path9.default.relative(gitRoot, contentDir)) || ".";
6090
+ const contentPathspec = normalizeRelativePath(import_node_path11.default.relative(gitRoot, contentDir)) || ".";
6021
6091
  const contentPrefix = contentPathspec === "." ? "" : `${contentPathspec}/`;
6022
6092
  const history = (0, import_node_child_process.execFileSync)(
6023
6093
  "git",
@@ -6059,11 +6129,11 @@ function getGitLastModifiedDates(contentDir) {
6059
6129
  }
6060
6130
  }
6061
6131
  function readManifest(contentDir) {
6062
- const manifestPath = import_node_path9.default.join(contentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
6063
- if (!(0, import_node_fs6.existsSync)(manifestPath)) return null;
6132
+ const manifestPath = import_node_path11.default.join(contentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
6133
+ if (!(0, import_node_fs7.existsSync)(manifestPath)) return null;
6064
6134
  try {
6065
6135
  const parsed = JSON.parse(
6066
- (0, import_node_fs6.readFileSync)(manifestPath, "utf8")
6136
+ (0, import_node_fs7.readFileSync)(manifestPath, "utf8")
6067
6137
  );
6068
6138
  if (parsed.version !== 1 || !parsed.pages || typeof parsed.pages !== "object") return null;
6069
6139
  const pages = Object.fromEntries(
@@ -6082,15 +6152,15 @@ function createFarmDocsLastModifiedManifest(contentDir, options = {}) {
6082
6152
  const fallbackDate = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
6083
6153
  const pages = {};
6084
6154
  for (const sourcePath of discoverDocsSourceFiles(resolvedContentDir)) {
6085
- const relativePath2 = normalizeRelativePath(import_node_path9.default.relative(resolvedContentDir, sourcePath));
6086
- pages[relativePath2] = gitDates[relativePath2] || (options.fallback === "now" ? fallbackDate : (0, import_node_fs6.statSync)(sourcePath).mtime.toISOString());
6155
+ const relativePath2 = normalizeRelativePath(import_node_path11.default.relative(resolvedContentDir, sourcePath));
6156
+ pages[relativePath2] = gitDates[relativePath2] || (options.fallback === "now" ? fallbackDate : (0, import_node_fs7.statSync)(sourcePath).mtime.toISOString());
6087
6157
  }
6088
6158
  return { version: 1, pages };
6089
6159
  }
6090
6160
  function getLastModifiedManifest(contentDir) {
6091
6161
  const resolvedContentDir = resolveExistingPath(contentDir);
6092
- const manifestPath = import_node_path9.default.join(resolvedContentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
6093
- const manifestStat = (0, import_node_fs6.existsSync)(manifestPath) ? (0, import_node_fs6.statSync)(manifestPath) : null;
6162
+ const manifestPath = import_node_path11.default.join(resolvedContentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
6163
+ const manifestStat = (0, import_node_fs7.existsSync)(manifestPath) ? (0, import_node_fs7.statSync)(manifestPath) : null;
6094
6164
  const signature = manifestStat ? `file:${manifestStat.mtimeMs}:${manifestStat.size}` : "generated";
6095
6165
  const cached = manifestCache.get(resolvedContentDir);
6096
6166
  if (cached?.signature === signature) return cached.manifest;
@@ -6103,21 +6173,21 @@ function resolveFarmDocsPageLastModified(contentDir, sourcePath, frontmatter) {
6103
6173
  if (configured) return configured;
6104
6174
  const resolvedContentDir = resolveExistingPath(contentDir);
6105
6175
  const resolvedSourcePath = resolveExistingPath(sourcePath);
6106
- const relativePath2 = normalizeRelativePath(import_node_path9.default.relative(resolvedContentDir, resolvedSourcePath));
6107
- const isInsideContentDir = relativePath2 !== ".." && !relativePath2.startsWith("../") && !import_node_path9.default.isAbsolute(relativePath2);
6176
+ const relativePath2 = normalizeRelativePath(import_node_path11.default.relative(resolvedContentDir, resolvedSourcePath));
6177
+ const isInsideContentDir = relativePath2 !== ".." && !relativePath2.startsWith("../") && !import_node_path11.default.isAbsolute(relativePath2);
6108
6178
  if (isInsideContentDir) {
6109
6179
  const generated = getLastModifiedManifest(resolvedContentDir).pages[relativePath2];
6110
6180
  if (generated) return generated;
6111
6181
  }
6112
- return (0, import_node_fs6.statSync)(resolvedSourcePath).mtime.toISOString();
6182
+ return (0, import_node_fs7.statSync)(resolvedSourcePath).mtime.toISOString();
6113
6183
  }
6114
- var import_node_child_process, import_node_fs6, import_node_path9, FARM_DOCS_LAST_MODIFIED_MANIFEST, COMMIT_MARKER, DOCS_FILE_EXTENSIONS, manifestCache;
6184
+ var import_node_child_process, import_node_fs7, import_node_path11, FARM_DOCS_LAST_MODIFIED_MANIFEST, COMMIT_MARKER, DOCS_FILE_EXTENSIONS, manifestCache;
6115
6185
  var init_last_modified = __esm({
6116
6186
  "src/docs/last-modified.ts"() {
6117
6187
  "use strict";
6118
6188
  import_node_child_process = require("child_process");
6119
- import_node_fs6 = require("fs");
6120
- import_node_path9 = __toESM(require("path"));
6189
+ import_node_fs7 = require("fs");
6190
+ import_node_path11 = __toESM(require("path"));
6121
6191
  FARM_DOCS_LAST_MODIFIED_MANIFEST = ".farm-docs-last-modified.json";
6122
6192
  COMMIT_MARKER = "__FARM_DOCS_COMMIT__";
6123
6193
  DOCS_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx"]);
@@ -6179,8 +6249,8 @@ var init_pixel_border_css = __esm({
6179
6249
  --scrollbar-thumb-hover: var(--color-fd-ring);
6180
6250
  --scrollbar-track: transparent;
6181
6251
  --radius: 0px;
6182
- --font-geist-sans: "Geist Sans";
6183
- --font-geist-mono: "Geist Mono";
6252
+ --font-geist-sans: ui-sans-serif, system-ui, sans-serif;
6253
+ --font-geist-mono: ui-monospace, monospace;
6184
6254
  }
6185
6255
 
6186
6256
  .dark {
@@ -6193,28 +6263,6 @@ body,
6193
6263
  background-color: var(--color-fd-background) !important;
6194
6264
  }
6195
6265
 
6196
- @font-face {
6197
- font-family: "Geist Sans";
6198
- font-display: block;
6199
- font-style: normal;
6200
- font-weight: 100 900;
6201
- src:
6202
- local("Geist Sans"),
6203
- url("/assets/Geist-Variable-CrgPqtmy.woff2") format("woff2"),
6204
- url("/node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2") format("woff2");
6205
- }
6206
-
6207
- @font-face {
6208
- font-family: "Geist Mono";
6209
- font-display: block;
6210
- font-style: normal;
6211
- font-weight: 100 900;
6212
- src:
6213
- local("Geist Mono"),
6214
- url("/assets/GeistMono-Variable-BNLlm6Cd.woff2") format("woff2"),
6215
- url("/node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2") format("woff2");
6216
- }
6217
-
6218
6266
  * {
6219
6267
  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
6220
6268
  scrollbar-width: thin;
@@ -7421,10 +7469,10 @@ function isFarmDocsSearchEnabled(docs) {
7421
7469
  }
7422
7470
  function resolveFarmDocsSearchClientModule(root) {
7423
7471
  try {
7424
- const requireFromApp = (0, import_node_module4.createRequire)(import_node_path10.default.join(import_node_path10.default.resolve(root), "package.json"));
7472
+ const requireFromApp = (0, import_node_module4.createRequire)(import_node_path12.default.join(import_node_path12.default.resolve(root), "package.json"));
7425
7473
  const themeEntry = requireFromApp.resolve("@farming-labs/theme");
7426
- const commandSearchModule = import_node_path10.default.join(import_node_path10.default.dirname(themeEntry), "docs-command-search.mjs");
7427
- if ((0, import_node_fs7.existsSync)(commandSearchModule)) return commandSearchModule;
7474
+ const commandSearchModule = import_node_path12.default.join(import_node_path12.default.dirname(themeEntry), "docs-command-search.mjs");
7475
+ if ((0, import_node_fs8.existsSync)(commandSearchModule)) return commandSearchModule;
7428
7476
  } catch {
7429
7477
  }
7430
7478
  return void 0;
@@ -7563,13 +7611,13 @@ async function mountFarmDocsSearch() {
7563
7611
  window.__FARM_MOUNT_DOCS_SEARCH__ = mountFarmDocsSearch;
7564
7612
  `;
7565
7613
  }
7566
- var import_node_fs7, import_node_module4, import_node_path10;
7614
+ var import_node_fs8, import_node_module4, import_node_path12;
7567
7615
  var init_search_client = __esm({
7568
7616
  "src/docs/search-client.ts"() {
7569
7617
  "use strict";
7570
- import_node_fs7 = require("fs");
7618
+ import_node_fs8 = require("fs");
7571
7619
  import_node_module4 = require("module");
7572
- import_node_path10 = __toESM(require("path"));
7620
+ import_node_path12 = __toESM(require("path"));
7573
7621
  __name(isFarmDocsSearchEnabled, "isFarmDocsSearchEnabled");
7574
7622
  __name(resolveFarmDocsSearchClientModule, "resolveFarmDocsSearchClientModule");
7575
7623
  __name(generateFarmDocsSearchBootstrapRuntime, "generateFarmDocsSearchBootstrapRuntime");
@@ -7577,11 +7625,418 @@ var init_search_client = __esm({
7577
7625
  }
7578
7626
  });
7579
7627
 
7628
+ // src/docs/social-image.ts
7629
+ function normalizeEntry(entry) {
7630
+ if (!entry || entry === "/") return "";
7631
+ return `/${entry.replace(/^\/+|\/+$/g, "")}`;
7632
+ }
7633
+ function encodeSlugPath(slug) {
7634
+ return slug.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
7635
+ }
7636
+ function getSocialImageConfig(docs) {
7637
+ return typeof docs.config.socialImage === "object" && docs.config.socialImage ? docs.config.socialImage : void 0;
7638
+ }
7639
+ function getNavTitle(docs) {
7640
+ if (typeof docs.config.nav !== "object" || !docs.config.nav || !("title" in docs.config.nav)) {
7641
+ return "Farm.js";
7642
+ }
7643
+ return String(docs.config.nav.title || "Farm.js");
7644
+ }
7645
+ function isFalse(value) {
7646
+ return value ? /^(false|no|none|off|disabled)$/i.test(value.trim()) : false;
7647
+ }
7648
+ function isExternalOrRootPath(value) {
7649
+ return /^(https?:)?\/\//i.test(value) || value.startsWith("/");
7650
+ }
7651
+ function isFarmDocsSocialImageEnabled(page2, docs) {
7652
+ if (docs.config.socialImage === false || getSocialImageConfig(docs)?.enabled === false) {
7653
+ return false;
7654
+ }
7655
+ return !isFalse(page2.frontmatter.socialImage);
7656
+ }
7657
+ function getFarmDocsCustomSocialImage(page2) {
7658
+ const value = page2.frontmatter.socialImage || page2.frontmatter.openGraphImage || page2.frontmatter.ogImage;
7659
+ return value && !isFalse(value) && isExternalOrRootPath(value) ? value : void 0;
7660
+ }
7661
+ function resolveIllustration(page2) {
7662
+ const explicit = page2.frontmatter.socialIllustration?.toLowerCase();
7663
+ if (explicit === "auth" || explicit === "cache" || explicit === "cli" || explicit === "integrations" || explicit === "project" || explicit === "routing" || explicit === "runtime") {
7664
+ return explicit;
7665
+ }
7666
+ const source = `${page2.slug} ${page2.title} ${page2.section || ""}`.toLowerCase();
7667
+ if (/auth|session|security|csrf/.test(source)) return "auth";
7668
+ if (/cache|ppr|render|static|revalid|stream/.test(source)) return "cache";
7669
+ if (/integration|stripe|prisma|better.auth|email|job|workflow/.test(source)) {
7670
+ return "integrations";
7671
+ }
7672
+ if (/cli|test|deploy|preview|upgrade|migration|install/.test(source)) return "cli";
7673
+ if (/project|structure|config|environment|markdown|docs.engine/.test(source)) {
7674
+ return "project";
7675
+ }
7676
+ if (/rout|endpoint|middleware|navigation|server.function|server.action|api/.test(source)) {
7677
+ return "routing";
7678
+ }
7679
+ return "runtime";
7680
+ }
7681
+ function createHashValue(value) {
7682
+ return (0, import_node_crypto4.createHash)("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 16);
7683
+ }
7684
+ function resolveBaseUrl(requestUrl, config) {
7685
+ return config?.baseUrl ? new URL(config.baseUrl) : new URL(requestUrl.origin);
7686
+ }
7687
+ function createFarmDocsSocialImageDescriptor(page2, docs, requestUrl) {
7688
+ const config = getSocialImageConfig(docs);
7689
+ const siteName = config?.siteName || getNavTitle(docs);
7690
+ const brand = config?.brand || siteName;
7691
+ const title = page2.frontmatter.socialTitle || page2.title;
7692
+ const description = page2.frontmatter.socialDescription || page2.description || docs.config.metadata?.description || `Documentation for ${siteName}`;
7693
+ const section = page2.frontmatter.section || page2.section || "Documentation";
7694
+ const illustration = resolveIllustration(page2);
7695
+ const entry = normalizeEntry(docs.entry);
7696
+ const slugPath = encodeSlugPath(page2.slug) || "index";
7697
+ const imagePath = `${entry}/_social/${slugPath}/opengraph-image.svg`;
7698
+ const baseUrl = resolveBaseUrl(requestUrl, config);
7699
+ const pageUrl = new URL(page2.href, baseUrl).href;
7700
+ const imageAlt = `${title} \u2014 ${siteName} documentation`;
7701
+ const hash = createHashValue({
7702
+ version: FARM_DOCS_SOCIAL_IMAGE_VERSION,
7703
+ title,
7704
+ description,
7705
+ section,
7706
+ siteName,
7707
+ brand,
7708
+ pageHref: page2.href,
7709
+ illustration,
7710
+ fonts: config?.fonts
7711
+ });
7712
+ return {
7713
+ title,
7714
+ description,
7715
+ section,
7716
+ siteName,
7717
+ brand,
7718
+ pageHref: page2.href,
7719
+ pageUrl,
7720
+ imagePath,
7721
+ imageUrl: new URL(`${imagePath}?v=${hash}`, baseUrl).href,
7722
+ imageAlt,
7723
+ illustration,
7724
+ fonts: config?.fonts,
7725
+ hash
7726
+ };
7727
+ }
7728
+ function getFarmDocsSocialImageSlug(docs, requestUrl) {
7729
+ const entry = normalizeEntry(docs.entry);
7730
+ const prefix = `${entry}/_social/`;
7731
+ const suffix = "/opengraph-image.svg";
7732
+ if (!requestUrl.pathname.startsWith(prefix) || !requestUrl.pathname.endsWith(suffix)) {
7733
+ return null;
7734
+ }
7735
+ const rawSlug = requestUrl.pathname.slice(prefix.length, -suffix.length);
7736
+ if (!rawSlug) return null;
7737
+ try {
7738
+ const slug = rawSlug.split("/").map((segment) => decodeURIComponent(segment)).join("/");
7739
+ if (slug.split("/").some((segment) => !segment || segment === ".." || segment === ".")) {
7740
+ return null;
7741
+ }
7742
+ return slug === "index" ? "" : slug;
7743
+ } catch {
7744
+ return null;
7745
+ }
7746
+ }
7747
+ function escapeXml(value) {
7748
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
7749
+ }
7750
+ function truncate(value, length) {
7751
+ if (value.length <= length) return value;
7752
+ return `${value.slice(0, Math.max(0, length - 1)).trimEnd()}\u2026`;
7753
+ }
7754
+ function wrapText(value, maxCharacters, maxLines) {
7755
+ const words = value.trim().split(/\s+/).filter(Boolean);
7756
+ const lines = [];
7757
+ let current = "";
7758
+ let consumedWords = 0;
7759
+ for (const word of words) {
7760
+ const candidate = current ? `${current} ${word}` : word;
7761
+ if (candidate.length <= maxCharacters || !current) {
7762
+ current = candidate;
7763
+ consumedWords += 1;
7764
+ continue;
7765
+ }
7766
+ lines.push(current);
7767
+ if (lines.length === maxLines) break;
7768
+ current = word;
7769
+ consumedWords += 1;
7770
+ }
7771
+ if (current && lines.length < maxLines) lines.push(current);
7772
+ if (consumedWords < words.length && lines.length) {
7773
+ lines[lines.length - 1] = truncate(`${lines[lines.length - 1]}\u2026`, maxCharacters);
7774
+ }
7775
+ return lines;
7776
+ }
7777
+ function renderTextLines(lines, x, y, lineHeight, attributes) {
7778
+ return lines.map(
7779
+ (line, index) => `<text x="${x}" y="${y + index * lineHeight}" ${attributes}>${escapeXml(line)}</text>`
7780
+ ).join("");
7781
+ }
7782
+ function renderEmbeddedFont(name, source, weight) {
7783
+ if (!source) return "";
7784
+ return `@font-face{font-family:"${name}";src:url("${escapeXml(source)}") format("woff2");font-style:normal;font-weight:${weight};font-display:block;}`;
7785
+ }
7786
+ function renderArrow(x1, y, x2, color = "#8b8b8b") {
7787
+ return `<path d="M${x1} ${y}H${x2 - 9}" fill="none" stroke="${color}"/><path d="m${x2 - 9} ${y - 5} 9 5-9 5" fill="none" stroke="${color}"/>`;
7788
+ }
7789
+ function renderPanelHeader(label, status) {
7790
+ return `<text x="819" y="171" class="label">${escapeXml(label)}</text><text x="1134" y="171" class="label bright" text-anchor="end">${escapeXml(status)}</text>`;
7791
+ }
7792
+ function renderRoutingIllustration() {
7793
+ return `${renderPanelHeader("ROUTE BLUEPRINT", "TYPED")}
7794
+ <rect x="819" y="296" width="92" height="54" fill="#a3a3a3"/>
7795
+ <text x="832" y="327" class="diagram-title ink">src/app</text>
7796
+ <path d="M911 323h41M952 232v181M952 232h44M952 323h44M952 413h44" fill="none" stroke="#696969"/>
7797
+ <rect x="996" y="205" width="138" height="54" fill="#0a0a0a" stroke="#414141"/>
7798
+ <text x="1010" y="228" class="diagram-title">/</text><text x="1010" y="246" class="micro">STATIC</text>
7799
+ <rect x="996" y="296" width="138" height="54" fill="#0a0a0a" stroke="#414141"/>
7800
+ <text x="1010" y="319" class="diagram-title">/blog/:slug</text><text x="1010" y="337" class="micro">DYNAMIC</text>
7801
+ <rect x="996" y="386" width="138" height="55" fill="#f0f0f0"/>
7802
+ <text x="1010" y="410" class="diagram-title ink">/docs/:path*</text><text x="1010" y="428" class="micro ink-muted">CATCH-ALL</text>
7803
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
7804
+ <text x="819" y="475" class="label bright">FILE ROUTES</text><text x="1134" y="475" class="label" text-anchor="end">TYPES INCLUDED</text>`;
7805
+ }
7806
+ function renderAuthIllustration() {
7807
+ return `${renderPanelHeader("REQUEST GUARD", "SERVER VERIFIED")}
7808
+ <rect x="819" y="269" width="86" height="54" fill="#0a0a0a" stroke="#454545"/>
7809
+ <text x="837" y="292" class="micro bright">REQUEST</text><text x="837" y="310" class="diagram-title">GET /app</text>
7810
+ ${renderArrow(905, 296, 943)}
7811
+ <rect x="943" y="224" width="104" height="118" fill="#f0f0f0"/>
7812
+ <path d="M974 271v-12a21 21 0 0 1 42 0v12M968 271h54v45h-54z" fill="#050505"/>
7813
+ <circle cx="995" cy="292" r="4" fill="#f0f0f0"/><path d="M995 296v8" stroke="#f0f0f0" stroke-width="3"/>
7814
+ <text x="995" y="332" class="micro ink" text-anchor="middle">auth()</text>
7815
+ ${renderArrow(1047, 296, 1071)}
7816
+ <rect x="1071" y="269" width="63" height="54" fill="#0a0a0a" stroke="#454545"/>
7817
+ <text x="1082" y="292" class="micro bright">ALLOW</text><text x="1082" y="310" class="micro">/app</text>
7818
+ <rect x="819" y="372" width="315" height="38" fill="#090909" stroke="#3d3d3d"/>
7819
+ <rect x="832" y="385" width="11" height="11" fill="#a3a3a3"/>
7820
+ <text x="856" y="395" class="code">session.user / verified</text>
7821
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
7822
+ <text x="819" y="475" class="label bright">SESSION TYPED</text><text x="1134" y="475" class="label" text-anchor="end">CSRF PROTECTED</text>`;
7823
+ }
7824
+ function renderCacheIllustration() {
7825
+ return `${renderPanelHeader("PAGE / PRODUCT", "CACHE + PPR")}
7826
+ <rect x="819" y="204" width="315" height="54" fill="#f0f0f0"/>
7827
+ <text x="836" y="235" class="diagram-title ink">STATIC SHELL</text><text x="1116" y="235" class="micro ink-muted" text-anchor="end">BUILD</text>
7828
+ <rect x="819" y="271" width="198" height="76" fill="#a3a3a3"/>
7829
+ <text x="836" y="301" class="diagram-title ink">CACHED DATA</text><text x="836" y="326" class="micro ink-muted">tag: products</text>
7830
+ <rect x="1029" y="271" width="105" height="76" fill="#0a0a0a" stroke="#555"/>
7831
+ <text x="1045" y="301" class="diagram-title">LIVE</text><text x="1045" y="326" class="micro">stream</text>
7832
+ <path d="M1081 347v43m-8-9 8 9 8-9" fill="none" stroke="#d4d4d4"/>
7833
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
7834
+ <text x="819" y="475" class="label bright">STATIC FIRST</text><text x="1134" y="475" class="label" text-anchor="end">DYNAMIC WHERE NEEDED</text>`;
7835
+ }
7836
+ function renderIntegrationsIllustration() {
7837
+ return `${renderPanelHeader("INTEGRATION GRAPH", "ONE PRODUCT")}
7838
+ <path d="M976 309H879V247M976 309H879v87M1030 309h93V247M1030 309h93v87" fill="none" stroke="#4a4a4a"/>
7839
+ <rect x="819" y="214" width="120" height="57" fill="#0a0a0a" stroke="#4b4b4b"/><text x="839" y="247" class="diagram-title">BETTER AUTH</text>
7840
+ <rect x="819" y="368" width="120" height="57" fill="#0a0a0a" stroke="#4b4b4b"/><text x="851" y="401" class="diagram-title">PRISMA</text>
7841
+ <rect x="1061" y="214" width="73" height="57" fill="#0a0a0a" stroke="#4b4b4b"/><text x="1077" y="247" class="diagram-title">STRIPE</text>
7842
+ <rect x="1061" y="368" width="73" height="57" fill="#a3a3a3"/><text x="1075" y="401" class="diagram-title ink">VERCEL</text>
7843
+ <rect x="976" y="277" width="94" height="64" fill="#f0f0f0"/>
7844
+ <text x="994" y="304" class="diagram-title ink">FARM.JS</text><text x="994" y="324" class="micro ink-muted">CORE</text>
7845
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
7846
+ <text x="819" y="475" class="label bright">BRING YOUR STACK</text><text x="1134" y="475" class="label" text-anchor="end">CONNECTED ONCE</text>`;
7847
+ }
7848
+ function renderCliIllustration() {
7849
+ return `${renderPanelHeader("TERMINAL", "PRODUCTION OUTPUT")}
7850
+ <rect x="819" y="203" width="315" height="213" fill="#050505" stroke="#555"/>
7851
+ <line x1="819" y1="236" x2="1134" y2="236" stroke="#555"/>
7852
+ <circle cx="837" cy="220" r="4" fill="#5d5d5d"/><circle cx="850" cy="220" r="4" fill="#7a7a7a"/><circle cx="863" cy="220" r="4" fill="#a3a3a3"/>
7853
+ <text x="837" y="273" class="code bright"><tspan fill="#7a7a7a">$</tspan> farm build</text>
7854
+ <rect x="837" y="293" width="11" height="11" fill="#a3a3a3"/><path d="m839 298 3 3 6-7" fill="none" stroke="#050505" stroke-width="1.5"/>
7855
+ <text x="861" y="303" class="code">64 routes discovered</text>
7856
+ <rect x="837" y="320" width="11" height="11" fill="#a3a3a3"/><path d="m839 325 3 3 6-7" fill="none" stroke="#050505" stroke-width="1.5"/>
7857
+ <text x="861" y="330" class="code">client + server bundles</text>
7858
+ <rect x="837" y="355" width="126" height="30" fill="#f0f0f0"/><text x="853" y="374" class="micro ink">BUILD COMPLETE</text>
7859
+ <text x="1115" y="374" class="micro" text-anchor="end">/dist</text>
7860
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
7861
+ <text x="819" y="475" class="label bright">REAL COMMANDS</text><text x="1134" y="475" class="label" text-anchor="end">842MS</text>`;
7862
+ }
7863
+ function renderProjectIllustration() {
7864
+ return `${renderPanelHeader("PROJECT MAP", "CONVENTION TYPED")}
7865
+ <rect x="819" y="202" width="315" height="216" fill="#050505" stroke="#555"/>
7866
+ <text x="837" y="227" class="diagram-title bright">my-app/</text>
7867
+ <path d="M847 243v143M847 259h23M847 293h23M847 327h23M847 361h23" fill="none" stroke="#666"/>
7868
+ <rect x="870" y="243" width="246" height="33" fill="#0a0a0a" stroke="#444"/><text x="886" y="264" class="code">src/app</text><text x="1098" y="264" class="micro" text-anchor="end">ROUTES</text>
7869
+ <rect x="870" y="277" width="246" height="33" fill="#f0f0f0"/><text x="886" y="298" class="code ink">src/app/page.tsx</text>
7870
+ <rect x="870" y="311" width="246" height="33" fill="#0a0a0a" stroke="#444"/><text x="886" y="332" class="code">src/app/api/users/route.ts</text><text x="1098" y="332" class="micro" text-anchor="end">API</text>
7871
+ <rect x="870" y="345" width="246" height="33" fill="#0a0a0a" stroke="#444"/><text x="886" y="366" class="code">farm.config.ts</text><text x="1098" y="366" class="micro" text-anchor="end">CONFIG</text>
7872
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
7873
+ <text x="819" y="475" class="label bright">FILES BECOME FEATURES</text><text x="1134" y="475" class="label" text-anchor="end">ONE CONFIG</text>`;
7874
+ }
7875
+ function renderRuntimeIllustration() {
7876
+ return `${renderPanelHeader("PRODUCT ARCHITECTURE", "FULL STACK")}
7877
+ <rect x="819" y="211" width="315" height="55" fill="#f0f0f0"/>
7878
+ <text x="836" y="244" class="diagram-title ink">PRODUCT SURFACE</text><text x="1116" y="244" class="micro ink-muted" text-anchor="end">UI + ROUTES</text>
7879
+ <path d="M976 266v20m0 61v20" stroke="#727272"/>
7880
+ <rect x="855" y="286" width="242" height="61" fill="#a3a3a3"/>
7881
+ <text x="874" y="314" class="diagram-title ink">APPLICATION CORE</text><text x="874" y="334" class="micro ink-muted">APIS \xB7 MIDDLEWARE</text>
7882
+ <rect x="819" y="367" width="315" height="55" fill="#4c4c4c"/>
7883
+ <text x="836" y="400" class="diagram-title bright">CONNECTED STACK</text><text x="1116" y="400" class="micro" text-anchor="end">DATA \xB7 AUTH \xB7 DEPLOY</text>
7884
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
7885
+ <text x="819" y="475" class="label bright">ONE FRAMEWORK</text><text x="1134" y="475" class="label" text-anchor="end">ONE DEPLOYMENT MODEL</text>`;
7886
+ }
7887
+ function renderIllustration(type) {
7888
+ switch (type) {
7889
+ case "auth":
7890
+ return renderAuthIllustration();
7891
+ case "cache":
7892
+ return renderCacheIllustration();
7893
+ case "cli":
7894
+ return renderCliIllustration();
7895
+ case "integrations":
7896
+ return renderIntegrationsIllustration();
7897
+ case "project":
7898
+ return renderProjectIllustration();
7899
+ case "routing":
7900
+ return renderRoutingIllustration();
7901
+ default:
7902
+ return renderRuntimeIllustration();
7903
+ }
7904
+ }
7905
+ function renderFarmDocsSocialImageSvg(descriptor) {
7906
+ const titleLines = wrapText(descriptor.title, 22, 2);
7907
+ const longestTitleLine = Math.max(...titleLines.map((line) => line.length));
7908
+ const titleSize = titleLines.length === 1 ? longestTitleLine > 17 ? 62 : 74 : 56;
7909
+ const titleLineHeight = Math.round(titleSize * 0.98);
7910
+ const titleY = titleLines.length === 1 ? 294 : 266;
7911
+ const descriptionY = titleY + (titleLines.length - 1) * titleLineHeight + 59;
7912
+ const descriptionLines = wrapText(descriptor.description, 68, 2);
7913
+ const routeY = Math.max(418, descriptionY + descriptionLines.length * 29 + 24);
7914
+ const section = truncate(descriptor.section.toUpperCase(), 27);
7915
+ const route = truncate(descriptor.pageHref, 42);
7916
+ const footerUrl = truncate(descriptor.pageUrl.replace(/^https?:\/\//, ""), 70);
7917
+ const brand = truncate(descriptor.brand.toUpperCase(), 25);
7918
+ const titleId = `farm-docs-og-${descriptor.hash}`;
7919
+ return `<?xml version="1.0" encoding="UTF-8"?>
7920
+ <svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="${FARM_DOCS_SOCIAL_IMAGE_WIDTH}" height="${FARM_DOCS_SOCIAL_IMAGE_HEIGHT}" viewBox="0 0 ${FARM_DOCS_SOCIAL_IMAGE_WIDTH} ${FARM_DOCS_SOCIAL_IMAGE_HEIGHT}" role="img" aria-labelledby="${titleId}-title ${titleId}-description">
7921
+ <title id="${titleId}-title">${escapeXml(descriptor.imageAlt)}</title>
7922
+ <desc id="${titleId}-description">${escapeXml(descriptor.description)}</desc>
7923
+ <metadata>${escapeXml(JSON.stringify({ generator: "Farm.js docs", page: descriptor.pageUrl, illustration: descriptor.illustration }))}</metadata>
7924
+ <style type="text/css">
7925
+ ${renderEmbeddedFont("Farm Sans", descriptor.fonts?.sans, "100 900")}
7926
+ ${renderEmbeddedFont("Farm Mono", descriptor.fonts?.mono, "100 900")}
7927
+ ${renderEmbeddedFont("Farm Pixel", descriptor.fonts?.display, "400 700")}
7928
+ .sans{font-family:"Farm Sans","Arial",sans-serif}.mono,.label,.code,.diagram-title,.micro{font-family:"Farm Mono","Menlo","DejaVu Sans Mono",monospace}.pixel{font-family:"Farm Pixel","Farm Mono",monospace}.label{fill:#8b8b8b;font-size:10px;font-weight:600;letter-spacing:1.2px}.code{fill:#8b8b8b;font-size:11px}.diagram-title{fill:#e7e7e7;font-size:11px;font-weight:650}.micro{fill:#777;font-size:8px;letter-spacing:.45px}.bright{fill:#f5f5f4}.ink{fill:#050505}.ink-muted{fill:#4c4c4c}
7929
+ </style>
7930
+ <defs>
7931
+ <pattern id="canvas-grid" width="40" height="40" patternUnits="userSpaceOnUse"><path d="M40 0H0V40" fill="none" stroke="#151515"/></pattern>
7932
+ <pattern id="diagonal" width="12" height="12" patternUnits="userSpaceOnUse" patternTransform="rotate(45)"><line x1="0" y1="0" x2="0" y2="12" stroke="#171717" stroke-width="2"/></pattern>
7933
+ <clipPath id="frame-clip"><rect x="28" y="28" width="1144" height="574"/></clipPath>
7934
+ </defs>
7935
+ <rect width="1200" height="630" fill="#050505"/>
7936
+ <rect width="1200" height="630" fill="url(#canvas-grid)"/>
7937
+ <g clip-path="url(#frame-clip)">
7938
+ <rect x="28" y="28" width="1144" height="574" fill="#020202"/>
7939
+ <rect x="782" y="114" width="390" height="411" fill="url(#diagonal)"/>
7940
+ </g>
7941
+ <rect x="28.5" y="28.5" width="1143" height="573" fill="none" stroke="#323232"/>
7942
+ <path d="M28 114.5h1144M28 525.5h1144M781.5 114v411" fill="none" stroke="#303030"/>
7943
+
7944
+ <g transform="translate(71 51)">
7945
+ <rect width="11" height="11" rx="1" fill="#f5f5f4"/><rect x="15" width="28" height="11" rx="1" fill="#f5f5f4"/>
7946
+ <rect y="15" width="11" height="11" rx="1" fill="#9b9b9b"/><rect x="15" y="15" width="28" height="11" rx="1" fill="#9b9b9b"/>
7947
+ <rect y="30" width="11" height="11" rx="1" fill="#5f5f5f"/><rect x="15" y="30" width="20" height="11" rx="1" fill="#5f5f5f"/>
7948
+ <text x="61" y="24" class="mono bright" font-size="24" font-weight="550">FARM<tspan fill="#777">.JS</tspan></text>
7949
+ <text x="61" y="43" class="label">BY FARMING LABS</text>
7950
+ </g>
7951
+ <text x="1134" y="72" class="label" text-anchor="end">${escapeXml(brand)} \xB7 DOCUMENTATION</text>
7952
+
7953
+ <text x="71" y="198" class="mono bright" font-size="13" font-weight="650">DOCS</text>
7954
+ <line x1="117" y1="193" x2="155" y2="193" stroke="#696969"/>
7955
+ <text x="166" y="198" class="mono" fill="#9b9b9b" font-size="13">${escapeXml(section)}</text>
7956
+ ${renderTextLines(titleLines, 71, titleY, titleLineHeight, `class="pixel bright" font-size="${titleSize}" font-weight="500" letter-spacing="-2.4"`)}
7957
+ ${renderTextLines(descriptionLines, 71, descriptionY, 29, 'class="sans" fill="#a3a3a3" font-size="20" font-weight="430" letter-spacing="-.3"')}
7958
+ <rect x="71" y="${routeY}" width="${Math.min(390, 56 + route.length * 8)}" height="36" fill="#050505" stroke="#3c3c3c"/>
7959
+ <text x="85" y="${routeY + 23}" class="mono bright" font-size="11" font-weight="600">GET</text>
7960
+ <text x="119" y="${routeY + 23}" class="mono" fill="#898989" font-size="11">${escapeXml(route)}</text>
7961
+
7962
+ ${renderIllustration(descriptor.illustration)}
7963
+
7964
+ <text x="71" y="570" class="mono bright" font-size="13" font-weight="550">${escapeXml(footerUrl)}</text>
7965
+ <text x="1134" y="570" class="mono" fill="#9b9b9b" font-size="12" text-anchor="end">FARM.JS DOCUMENTATION</text>
7966
+ <path d="M20 20h10M20 20v10M1180 20h-10M1180 20v10M20 610h10M20 610v-10M1180 610h-10M1180 610v-10" fill="none" stroke="#9b9b9b"/>
7967
+ </svg>`;
7968
+ }
7969
+ function renderFarmDocsSocialMetadata(descriptor, customImage) {
7970
+ const imageUrl = customImage ? new URL(customImage, descriptor.pageUrl).href : descriptor.imageUrl;
7971
+ const customExtension = customImage?.toLowerCase().split(/[?#]/, 1)[0];
7972
+ const imageType = customExtension?.endsWith(".webp") ? "image/webp" : customExtension?.endsWith(".png") ? "image/png" : customExtension?.match(/\.jpe?g$/) ? "image/jpeg" : "image/svg+xml";
7973
+ const tag = /* @__PURE__ */ __name((property, content, name = false) => `<meta ${name ? "name" : "property"}="${property}" content="${escapeXml(content)}">`, "tag");
7974
+ return [
7975
+ tag("og:type", "article"),
7976
+ tag("og:site_name", descriptor.siteName),
7977
+ tag("og:title", descriptor.title),
7978
+ tag("og:description", descriptor.description),
7979
+ tag("og:url", descriptor.pageUrl),
7980
+ tag("og:image", imageUrl),
7981
+ tag("og:image:type", imageType),
7982
+ ...customImage ? [] : [
7983
+ tag("og:image:width", String(FARM_DOCS_SOCIAL_IMAGE_WIDTH)),
7984
+ tag("og:image:height", String(FARM_DOCS_SOCIAL_IMAGE_HEIGHT))
7985
+ ],
7986
+ tag("og:image:alt", descriptor.imageAlt),
7987
+ tag("twitter:card", "summary_large_image", true),
7988
+ tag("twitter:title", descriptor.title, true),
7989
+ tag("twitter:description", descriptor.description, true),
7990
+ tag("twitter:image", imageUrl, true),
7991
+ tag("twitter:image:alt", descriptor.imageAlt, true)
7992
+ ].join("\n ");
7993
+ }
7994
+ var import_node_crypto4, FARM_DOCS_SOCIAL_IMAGE_WIDTH, FARM_DOCS_SOCIAL_IMAGE_HEIGHT, FARM_DOCS_SOCIAL_IMAGE_VERSION;
7995
+ var init_social_image = __esm({
7996
+ "src/docs/social-image.ts"() {
7997
+ "use strict";
7998
+ import_node_crypto4 = require("crypto");
7999
+ FARM_DOCS_SOCIAL_IMAGE_WIDTH = 1200;
8000
+ FARM_DOCS_SOCIAL_IMAGE_HEIGHT = 630;
8001
+ FARM_DOCS_SOCIAL_IMAGE_VERSION = 3;
8002
+ __name(normalizeEntry, "normalizeEntry");
8003
+ __name(encodeSlugPath, "encodeSlugPath");
8004
+ __name(getSocialImageConfig, "getSocialImageConfig");
8005
+ __name(getNavTitle, "getNavTitle");
8006
+ __name(isFalse, "isFalse");
8007
+ __name(isExternalOrRootPath, "isExternalOrRootPath");
8008
+ __name(isFarmDocsSocialImageEnabled, "isFarmDocsSocialImageEnabled");
8009
+ __name(getFarmDocsCustomSocialImage, "getFarmDocsCustomSocialImage");
8010
+ __name(resolveIllustration, "resolveIllustration");
8011
+ __name(createHashValue, "createHashValue");
8012
+ __name(resolveBaseUrl, "resolveBaseUrl");
8013
+ __name(createFarmDocsSocialImageDescriptor, "createFarmDocsSocialImageDescriptor");
8014
+ __name(getFarmDocsSocialImageSlug, "getFarmDocsSocialImageSlug");
8015
+ __name(escapeXml, "escapeXml");
8016
+ __name(truncate, "truncate");
8017
+ __name(wrapText, "wrapText");
8018
+ __name(renderTextLines, "renderTextLines");
8019
+ __name(renderEmbeddedFont, "renderEmbeddedFont");
8020
+ __name(renderArrow, "renderArrow");
8021
+ __name(renderPanelHeader, "renderPanelHeader");
8022
+ __name(renderRoutingIllustration, "renderRoutingIllustration");
8023
+ __name(renderAuthIllustration, "renderAuthIllustration");
8024
+ __name(renderCacheIllustration, "renderCacheIllustration");
8025
+ __name(renderIntegrationsIllustration, "renderIntegrationsIllustration");
8026
+ __name(renderCliIllustration, "renderCliIllustration");
8027
+ __name(renderProjectIllustration, "renderProjectIllustration");
8028
+ __name(renderRuntimeIllustration, "renderRuntimeIllustration");
8029
+ __name(renderIllustration, "renderIllustration");
8030
+ __name(renderFarmDocsSocialImageSvg, "renderFarmDocsSocialImageSvg");
8031
+ __name(renderFarmDocsSocialMetadata, "renderFarmDocsSocialMetadata");
8032
+ }
8033
+ });
8034
+
7580
8035
  // src/docs/handler.ts
7581
8036
  function trimSlashes2(value) {
7582
8037
  return value.replace(/^\/+|\/+$/g, "");
7583
8038
  }
7584
- function normalizeEntry(entry) {
8039
+ function normalizeEntry2(entry) {
7585
8040
  if (!entry || entry === "/") return "/";
7586
8041
  return `/${trimSlashes2(entry)}`;
7587
8042
  }
@@ -7593,10 +8048,10 @@ function resolveFarmDocsFavicon(docs, options) {
7593
8048
  if (typeof configuredFavicon === "string" && configuredFavicon.trim()) {
7594
8049
  return configuredFavicon.trim();
7595
8050
  }
7596
- const publicDir = import_node_path11.default.join(import_node_path11.default.resolve(options.root), "public");
8051
+ const publicDir = import_node_path13.default.join(import_node_path13.default.resolve(options.root), "public");
7597
8052
  for (const fileName of FARM_DOCS_FAVICON_FILES) {
7598
- const faviconPath = import_node_path11.default.join(publicDir, fileName);
7599
- if ((0, import_node_fs8.existsSync)(faviconPath) && (0, import_node_fs8.statSync)(faviconPath).isFile()) {
8053
+ const faviconPath = import_node_path13.default.join(publicDir, fileName);
8054
+ if ((0, import_node_fs9.existsSync)(faviconPath) && (0, import_node_fs9.statSync)(faviconPath).isFile()) {
7600
8055
  return `/${fileName}`;
7601
8056
  }
7602
8057
  }
@@ -7630,10 +8085,10 @@ function isSafeSegment(segment) {
7630
8085
  return segment !== ".." && !segment.includes("/") && !segment.includes("\\");
7631
8086
  }
7632
8087
  function resolveInside(root, target) {
7633
- const resolvedRoot = import_node_path11.default.resolve(root);
7634
- const resolvedTarget = import_node_path11.default.resolve(target);
7635
- const relative4 = import_node_path11.default.relative(resolvedRoot, resolvedTarget);
7636
- if (relative4 === "" || !relative4.startsWith("..") && !import_node_path11.default.isAbsolute(relative4)) {
8088
+ const resolvedRoot = import_node_path13.default.resolve(root);
8089
+ const resolvedTarget = import_node_path13.default.resolve(target);
8090
+ const relative4 = import_node_path13.default.relative(resolvedRoot, resolvedTarget);
8091
+ if (relative4 === "" || !relative4.startsWith("..") && !import_node_path13.default.isAbsolute(relative4)) {
7637
8092
  return resolvedTarget;
7638
8093
  }
7639
8094
  return null;
@@ -7642,36 +8097,36 @@ function isFarmDocsRequest(docs, request) {
7642
8097
  if (!docs?.enabled) return false;
7643
8098
  if (request.method !== "GET" && request.method !== "HEAD") return false;
7644
8099
  const pathname = new URL(request.url).pathname;
7645
- const entry = normalizeEntry(docs.entry);
8100
+ const entry = normalizeEntry2(docs.entry);
7646
8101
  if (entry === "/") return true;
7647
8102
  return pathname === entry || pathname === `${entry}.md` || pathname.startsWith(`${entry}/`);
7648
8103
  }
7649
8104
  function resolveFarmDocsContentDir(docs, options) {
7650
- const root = import_node_path11.default.resolve(options.root);
8105
+ const root = import_node_path13.default.resolve(options.root);
7651
8106
  const srcDir = options.srcDir || "src";
7652
8107
  const configuredContentDir = docs.contentDir || docs.config.contentDir;
7653
8108
  if (configuredContentDir) {
7654
- return import_node_path11.default.isAbsolute(configuredContentDir) ? configuredContentDir : import_node_path11.default.join(root, configuredContentDir);
8109
+ return import_node_path13.default.isAbsolute(configuredContentDir) ? configuredContentDir : import_node_path13.default.join(root, configuredContentDir);
7655
8110
  }
7656
8111
  const entryDir = docs.config.entry || trimSlashes2(docs.entry) || "docs";
7657
- const appDocsDir = import_node_path11.default.join(root, srcDir, "app", entryDir);
7658
- if ((0, import_node_fs8.existsSync)(appDocsDir)) return appDocsDir;
7659
- return import_node_path11.default.join(root, entryDir);
8112
+ const appDocsDir = import_node_path13.default.join(root, srcDir, "app", entryDir);
8113
+ if ((0, import_node_fs9.existsSync)(appDocsDir)) return appDocsDir;
8114
+ return import_node_path13.default.join(root, entryDir);
7660
8115
  }
7661
8116
  function getFarmDocsRouteTypeEntries(docs) {
7662
8117
  if (!docs?.enabled) return [];
7663
- const entry = normalizeEntry(docs.entry);
8118
+ const entry = normalizeEntry2(docs.entry);
7664
8119
  if (entry === "/") return ["/", "/[...docs]"];
7665
8120
  return [entry, `${entry}/[...docs]`];
7666
8121
  }
7667
8122
  function getFarmDocsDocumentNavigationMatchers(docs) {
7668
8123
  if (!docs?.enabled) return [];
7669
- const entry = normalizeEntry(docs.entry);
8124
+ const entry = normalizeEntry2(docs.entry);
7670
8125
  return [entry === "/" ? "/(.*)" : `${entry}(.*)`];
7671
8126
  }
7672
8127
  function getRequestSlug(docs, request) {
7673
8128
  const pathname = new URL(request.url).pathname;
7674
- const entry = normalizeEntry(docs.entry);
8129
+ const entry = normalizeEntry2(docs.entry);
7675
8130
  if (entry === "/") return normalizeSlug(pathname);
7676
8131
  if (pathname === entry || pathname === `${entry}.md`) return "";
7677
8132
  return normalizeSlug(pathname.slice(entry.length));
@@ -7679,14 +8134,14 @@ function getRequestSlug(docs, request) {
7679
8134
  function findDocsPageFile(contentDir, slug) {
7680
8135
  const segments = slug ? slug.split("/").filter(Boolean) : [];
7681
8136
  if (!segments.every(isSafeSegment)) return null;
7682
- const slugDir = import_node_path11.default.join(contentDir, ...segments);
7683
- const candidates = segments.length === 0 ? DOCS_FILE_NAMES.map((filename) => import_node_path11.default.join(contentDir, filename)) : [
7684
- ...DOCS_FILE_NAMES.map((filename) => import_node_path11.default.join(slugDir, filename)),
7685
- ...DOCS_FILE_EXTENSIONS2.map((extension) => import_node_path11.default.join(contentDir, `${slug}${extension}`))
8137
+ const slugDir = import_node_path13.default.join(contentDir, ...segments);
8138
+ const candidates = segments.length === 0 ? DOCS_FILE_NAMES.map((filename) => import_node_path13.default.join(contentDir, filename)) : [
8139
+ ...DOCS_FILE_NAMES.map((filename) => import_node_path13.default.join(slugDir, filename)),
8140
+ ...DOCS_FILE_EXTENSIONS2.map((extension) => import_node_path13.default.join(contentDir, `${slug}${extension}`))
7686
8141
  ];
7687
8142
  for (const candidate of candidates) {
7688
8143
  const safePath = resolveInside(contentDir, candidate);
7689
- if (safePath && (0, import_node_fs8.existsSync)(safePath) && (0, import_node_fs8.statSync)(safePath).isFile()) {
8144
+ if (safePath && (0, import_node_fs9.existsSync)(safePath) && (0, import_node_fs9.statSync)(safePath).isFile()) {
7690
8145
  return safePath;
7691
8146
  }
7692
8147
  }
@@ -7721,7 +8176,7 @@ function titleFromMarkdown2(body, fallback) {
7721
8176
  function loadFarmDocsPage(contentDir, docs, slug) {
7722
8177
  const sourcePath = findDocsPageFile(contentDir, slug);
7723
8178
  if (!sourcePath) return null;
7724
- const source = (0, import_node_fs8.readFileSync)(sourcePath, "utf8");
8179
+ const source = (0, import_node_fs9.readFileSync)(sourcePath, "utf8");
7725
8180
  const { frontmatter, body } = parseFrontmatter(source);
7726
8181
  const title = frontmatter.title || titleFromMarkdown2(body, titleFromSlug(slug));
7727
8182
  const href = createDocsHref(docs.entry, slug);
@@ -7729,6 +8184,7 @@ function loadFarmDocsPage(contentDir, docs, slug) {
7729
8184
  slug,
7730
8185
  title,
7731
8186
  description: frontmatter.description,
8187
+ section: frontmatter.section,
7732
8188
  href,
7733
8189
  sourcePath,
7734
8190
  lastModified: resolveFarmDocsPageLastModified(contentDir, sourcePath, frontmatter),
@@ -7737,15 +8193,15 @@ function loadFarmDocsPage(contentDir, docs, slug) {
7737
8193
  };
7738
8194
  }
7739
8195
  function createDocsHref(entry, slug) {
7740
- const normalizedEntry = normalizeEntry(entry);
8196
+ const normalizedEntry = normalizeEntry2(entry);
7741
8197
  const normalizedSlug = trimSlashes2(slug);
7742
8198
  if (normalizedEntry === "/") return normalizedSlug ? `/${normalizedSlug}` : "/";
7743
8199
  return normalizedSlug ? `${normalizedEntry}/${normalizedSlug}` : normalizedEntry;
7744
8200
  }
7745
8201
  function pathToSlug(contentDir, filePath) {
7746
- const relative4 = import_node_path11.default.relative(contentDir, filePath).replace(/\\/g, "/");
8202
+ const relative4 = import_node_path13.default.relative(contentDir, filePath).replace(/\\/g, "/");
7747
8203
  if (relative4.startsWith("..")) return null;
7748
- const extension = import_node_path11.default.extname(relative4);
8204
+ const extension = import_node_path13.default.extname(relative4);
7749
8205
  if (!DOCS_FILE_EXTENSIONS2.includes(extension)) return null;
7750
8206
  const withoutExtension = relative4.slice(0, -extension.length);
7751
8207
  if (withoutExtension === "page" || withoutExtension === "index") return "";
@@ -7758,12 +8214,12 @@ function loadPage(contentDir, docs, request) {
7758
8214
  return loadFarmDocsPage(contentDir, docs, getRequestSlug(docs, request));
7759
8215
  }
7760
8216
  function discoverFarmDocsPages(contentDir, docs) {
7761
- if (!(0, import_node_fs8.existsSync)(contentDir)) return [];
8217
+ if (!(0, import_node_fs9.existsSync)(contentDir)) return [];
7762
8218
  const pages = [];
7763
8219
  const visit = /* @__PURE__ */ __name((dir) => {
7764
- for (const entry of (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true })) {
8220
+ for (const entry of (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true })) {
7765
8221
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
7766
- const absolutePath = import_node_path11.default.join(dir, entry.name);
8222
+ const absolutePath = import_node_path13.default.join(dir, entry.name);
7767
8223
  if (entry.isDirectory()) {
7768
8224
  visit(absolutePath);
7769
8225
  continue;
@@ -7771,7 +8227,7 @@ function discoverFarmDocsPages(contentDir, docs) {
7771
8227
  if (!entry.isFile()) continue;
7772
8228
  const slug = pathToSlug(contentDir, absolutePath);
7773
8229
  if (slug === null) continue;
7774
- const source = (0, import_node_fs8.readFileSync)(absolutePath, "utf8");
8230
+ const source = (0, import_node_fs9.readFileSync)(absolutePath, "utf8");
7775
8231
  const { frontmatter, body } = parseFrontmatter(source);
7776
8232
  pages.push({
7777
8233
  slug,
@@ -8549,7 +9005,7 @@ function renderPixelBreadcrumb(page2, docs) {
8549
9005
  const parentSegments = segments.slice(0, -1);
8550
9006
  const parentSegment = parentSegments[parentSegments.length - 1];
8551
9007
  const currentSegment = segments[segments.length - 1];
8552
- const entry = normalizeEntry(docs.entry);
9008
+ const entry = normalizeEntry2(docs.entry);
8553
9009
  const parentPath = parentSegments.join("/");
8554
9010
  const parentHref = entry === "/" ? `/${parentPath}` : `${entry}/${parentPath}`;
8555
9011
  return `<nav class="fd-breadcrumb" aria-label="Breadcrumb">
@@ -8587,7 +9043,7 @@ ${items.map(
8587
9043
  </div>`;
8588
9044
  }
8589
9045
  function renderDocsRuntimeScript(docs) {
8590
- const docsEntry = JSON.stringify(normalizeEntry(docs.entry));
9046
+ const docsEntry = JSON.stringify(normalizeEntry2(docs.entry));
8591
9047
  return `<script>(()=>{if(window.__farmDocsRuntime)return;window.__farmDocsRuntime=true;document.documentElement.dataset.farmDocsRuntime="true";document.documentElement.dataset.farmDocsRuntimeId=Math.random().toString(36).slice(2);const docsEntry=${docsEntry};let cleanupToc=()=>{};let closeMobileSidebar=()=>{};const normalizePath=(path)=>path.length>1?path.replace(/\\/+$/,""):path;const isDocsPath=(path)=>{const next=normalizePath(path);const entry=normalizePath(docsEntry);if(next.endsWith(".md"))return false;if(entry==="/")return true;return next===entry||next.startsWith(entry+"/")};const initToc=()=>{cleanupToc();const toc=document.getElementById("nd-toc");if(!toc){cleanupToc=()=>{};return}const links=Array.from(toc.querySelectorAll("[data-toc-item]"));const thumb=toc.querySelector("[data-toc-thumb]");const pairs=links.map((link)=>{let id=link.hash.slice(1);try{id=decodeURIComponent(id)}catch{}return{link,heading:document.getElementById(id)}}).filter((item)=>item.heading);const setActive=(active)=>{for(const {link} of pairs)link.dataset.active=link===active.link?"true":"false";if(!thumb)return;const styles=getComputedStyle(active.link);const top=active.link.offsetTop+parseFloat(styles.paddingTop||"0");const bottom=active.link.offsetTop+active.link.clientHeight-parseFloat(styles.paddingBottom||"0");thumb.style.clipPath="polygon(0 "+top+"px,100% "+top+"px,100% "+bottom+"px,0 "+bottom+"px)"};const update=()=>{if(pairs.length===0)return;const offset=Math.min(window.innerHeight*0.3,160);let active=pairs[0];for(const pair of pairs){if(pair.heading.getBoundingClientRect().top<=offset)active=pair;else break}setActive(active)};let frame=0;const schedule=()=>{if(frame)return;frame=requestAnimationFrame(()=>{frame=0;update()})};const onHashChange=()=>setTimeout(schedule,0);window.addEventListener("scroll",schedule,{passive:true});window.addEventListener("resize",schedule);window.addEventListener("hashchange",onHashChange);cleanupToc=()=>{window.removeEventListener("scroll",schedule);window.removeEventListener("resize",schedule);window.removeEventListener("hashchange",onHashChange);if(frame)cancelAnimationFrame(frame);frame=0};update()};const getSidebar=()=>document.getElementById("nd-sidebar");const ensureActiveVisible=()=>{const sidebar=getSidebar();if(!sidebar)return;const active=sidebar.querySelector('a[data-active="true"]');if(!(active instanceof HTMLElement))return;const activeRect=active.getBoundingClientRect();const sidebarRect=sidebar.getBoundingClientRect();if(activeRect.top<sidebarRect.top||activeRect.bottom>sidebarRect.bottom)sidebar.scrollTop+=activeRect.top-sidebarRect.top-(sidebar.clientHeight-activeRect.height)/2};const setSidebarActive=(path)=>{const sidebar=getSidebar();if(!sidebar)return;const current=normalizePath(path);for(const link of Array.from(sidebar.querySelectorAll("a[href]"))){try{link.dataset.active=normalizePath(new URL(link.href,location.href).pathname)===current?"true":"false"}catch{}}ensureActiveVisible()};const initMobileSidebar=()=>{const layout=document.getElementById("nd-docs-layout");const sidebar=getSidebar();const toggle=document.querySelector("[data-sidebar-toggle]");const backdrop=document.querySelector("[data-sidebar-backdrop]");if(!layout||!sidebar||!(toggle instanceof HTMLButtonElement))return;const setOpen=(open)=>{layout.dataset.sidebarOpen=open?"true":"false";document.documentElement.dataset.farmDocsSidebar=open?"open":"closed";toggle.setAttribute("aria-expanded",open?"true":"false");toggle.setAttribute("aria-label",open?"Close menu":"Open menu")};const toggleOpen=()=>setOpen(layout.dataset.sidebarOpen!=="true");closeMobileSidebar=()=>setOpen(false);toggle.addEventListener("click",(event)=>{event.preventDefault();toggleOpen()});backdrop?.addEventListener("click",()=>closeMobileSidebar());document.addEventListener("keydown",(event)=>{if(event.key==="Escape")closeMobileSidebar()});sidebar.addEventListener("click",(event)=>{const target=event.target instanceof Element?event.target.closest("a[href]"):null;if(target)closeMobileSidebar()});closeMobileSidebar()};const initSidebarScroll=()=>{const sidebar=getSidebar();if(!sidebar)return;const key="farmdocs:sidebar-scroll:"+location.origin;const getStorage=()=>{try{return window.sessionStorage}catch{return null}};const readSaved=()=>{try{const raw=getStorage()?.getItem(key);if(!raw)return null;const parsed=JSON.parse(raw);return parsed&&typeof parsed==="object"?parsed:null}catch{return null}};const save=(path=location.pathname)=>{try{getStorage()?.setItem(key,JSON.stringify({path,scrollTop:sidebar.scrollTop}))}catch{}};const saved=readSaved();if(saved?.path===location.pathname&&Number.isFinite(Number(saved.scrollTop)))sidebar.scrollTop=Number(saved.scrollTop);ensureActiveVisible();save();sidebar.addEventListener("scroll",()=>save(),{passive:true});sidebar.addEventListener("click",(event)=>{const target=event.target instanceof Element?event.target.closest("a[href]"):null;if(!target)return;try{save(new URL(target.href,location.href).pathname)}catch{save()}});window.addEventListener("beforeunload",()=>save())};let navigateController=null;const getPageKey=(root)=>{const article=root.getElementById("nd-page");if(!article)return"";return article.querySelector("h1")?.textContent?.trim()||article.textContent?.replace(/\\s+/g," ").trim().slice(0,160)||""};const swapDocsPage=(html,url)=>{const nextDoc=new DOMParser().parseFromString(html,"text/html");const nextArticle=nextDoc.getElementById("nd-page");const currentArticle=document.getElementById("nd-page");if(!nextArticle||!currentArticle)return false;const nextKey=getPageKey(nextDoc);const importedArticle=document.importNode(nextArticle,true);currentArticle.replaceWith(importedArticle);const renderedArticle=document.getElementById("nd-page");if(!renderedArticle||renderedArticle===currentArticle||(nextKey&&getPageKey(document)!==nextKey))return false;const nextToc=nextDoc.getElementById("nd-toc");const currentToc=document.getElementById("nd-toc");if(nextToc&&currentToc)currentToc.replaceWith(document.importNode(nextToc,true));if(nextDoc.title)document.title=nextDoc.title;const nextDescription=nextDoc.querySelector('meta[name="description"]');const currentDescription=document.querySelector('meta[name="description"]');if(nextDescription&&currentDescription)currentDescription.setAttribute("content",nextDescription.getAttribute("content")||"");setSidebarActive(url.pathname);initToc();closeMobileSidebar();return true};const navigateDocs=async(url,{replace=false,scroll=true}={})=>{if(!isDocsPath(url.pathname))return false;if(navigateController)navigateController.abort();const controller=new AbortController();navigateController=controller;document.documentElement.dataset.farmDocsNavigating="true";try{const response=await fetch(url.href,{cache:"no-store",headers:{accept:"text/html","x-farm-docs-navigate":"1"},signal:controller.signal});if(!response.ok||!((response.headers.get("content-type")||"").includes("text/html")))return false;const html=await response.text();if(!swapDocsPage(html,url))return false;if(replace)history.replaceState({farmDocs:true},"",url.href);else history.pushState({farmDocs:true},"",url.href);if(scroll){if(url.hash){let id=url.hash.slice(1);try{id=decodeURIComponent(id)}catch{}document.getElementById(id)?.scrollIntoView({block:"start"})}else window.scrollTo({top:0,left:0})}return true}catch(error){if(error?.name==="AbortError")return true;return false}finally{if(navigateController===controller){delete document.documentElement.dataset.farmDocsNavigating;navigateController=null}}};const initClientNavigation=()=>{document.addEventListener("click",(event)=>{if(event.defaultPrevented||event.button!==0||event.metaKey||event.ctrlKey||event.shiftKey||event.altKey)return;const target=event.target instanceof Element?event.target.closest("a[href]"):null;if(!target||target.target||target.hasAttribute("download"))return;let url;try{url=new URL(target.href,location.href)}catch{return}if(url.origin!==location.origin||!isDocsPath(url.pathname))return;if(normalizePath(url.pathname)===normalizePath(location.pathname)&&url.hash)return;event.preventDefault();navigateDocs(url).then((handled)=>{if(!handled)location.href=url.href})});window.addEventListener("popstate",()=>{navigateDocs(new URL(location.href),{replace:true,scroll:false}).then((handled)=>{if(!handled)location.reload()})})};const init=()=>{initToc();initMobileSidebar();initSidebarScroll();initClientNavigation()};if(document.readyState==="loading")document.addEventListener("DOMContentLoaded",init,{once:true});else init()})();</script>`;
8592
9048
  }
8593
9049
  function renderDocsPageActionsRuntimeScript() {
@@ -8602,13 +9058,13 @@ ${renderDocsHashRuntimeScript()}
8602
9058
  ${renderDocsPageActionsRuntimeScript()}`;
8603
9059
  }
8604
9060
  function readCssWithImports(filePath, seen = /* @__PURE__ */ new Set()) {
8605
- const resolved = import_node_path11.default.resolve(filePath);
9061
+ const resolved = import_node_path13.default.resolve(filePath);
8606
9062
  if (seen.has(resolved)) return "";
8607
9063
  seen.add(resolved);
8608
- const source = (0, import_node_fs8.readFileSync)(resolved, "utf8");
9064
+ const source = (0, import_node_fs9.readFileSync)(resolved, "utf8");
8609
9065
  return source.replace(/@import\s+["']([^"']+)["'];?/g, (_match, importPath) => {
8610
9066
  try {
8611
- const importedPath = importPath.startsWith(".") ? import_node_path11.default.resolve(import_node_path11.default.dirname(resolved), importPath) : (0, import_node_module5.createRequire)(resolved).resolve(importPath);
9067
+ const importedPath = importPath.startsWith(".") ? import_node_path13.default.resolve(import_node_path13.default.dirname(resolved), importPath) : (0, import_node_module5.createRequire)(resolved).resolve(importPath);
8612
9068
  return readCssWithImports(importedPath, seen);
8613
9069
  } catch {
8614
9070
  return "";
@@ -8616,11 +9072,11 @@ function readCssWithImports(filePath, seen = /* @__PURE__ */ new Set()) {
8616
9072
  });
8617
9073
  }
8618
9074
  function resolvePixelBorderThemeCss(options) {
8619
- const cacheKey = import_node_path11.default.resolve(options.root);
9075
+ const cacheKey = import_node_path13.default.resolve(options.root);
8620
9076
  const cached = themeCssCache.get(cacheKey);
8621
9077
  if (cached !== void 0) return cached;
8622
9078
  try {
8623
- const requireFromApp = (0, import_node_module5.createRequire)(import_node_path11.default.join(cacheKey, "package.json"));
9079
+ const requireFromApp = (0, import_node_module5.createRequire)(import_node_path13.default.join(cacheKey, "package.json"));
8624
9080
  const themeCssPath = requireFromApp.resolve("@farming-labs/theme/pixel-border/css");
8625
9081
  const requireFromTheme = (0, import_node_module5.createRequire)(themeCssPath);
8626
9082
  const fumadocsCssPath = requireFromTheme.resolve("fumadocs-ui/style.css");
@@ -8633,17 +9089,28 @@ ${readCssWithImports(themeCssPath)}`;
8633
9089
  return "";
8634
9090
  }
8635
9091
  }
8636
- function renderFarmDocsBridgeCss(docs) {
9092
+ function renderFarmDocsBridgeCss(docs, fontAssets, layoutFonts) {
8637
9093
  const sidebarWidth = getThemeLayoutValue(docs, "sidebarWidth", 320);
8638
9094
  const contentWidth = getThemeLayoutValue(docs, "contentWidth", 860);
9095
+ const fontFaces = fontAssets.map(
9096
+ ({ family, url }) => `@font-face { font-family: "${family}"; src: url("${url}") format("woff2"); font-style: normal; font-weight: 100 900; font-display: block; }`
9097
+ ).join("\n");
9098
+ const fontVariables = [
9099
+ fontAssets.some(({ family }) => family === "Geist Sans") ? '--font-geist-sans: "Geist Sans", ui-sans-serif, system-ui, sans-serif;' : "",
9100
+ fontAssets.some(({ family }) => family === "Geist Mono") ? '--font-geist-mono: "Geist Mono", ui-monospace, monospace;' : ""
9101
+ ].filter(Boolean).join(" ");
9102
+ const fontCss = fontFaces ? `${fontFaces}
9103
+ :root { ${fontVariables} }` : "";
9104
+ const layoutFontVariables = [
9105
+ renderFarmDocsLayoutFontVariable("--fd-layout-font-body", layoutFonts?.body),
9106
+ renderFarmDocsLayoutFontVariable("--fd-layout-font-code", layoutFonts?.code)
9107
+ ].filter(Boolean).join(" ");
8639
9108
  return `
8640
- @font-face { font-family: "Geist Sans"; src: local("Geist Sans"), url("${GEIST_SANS_FONT_URL}") format("woff2"), url("/node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2") format("woff2"); font-display: block; font-style: normal; font-weight: 100 900; }
8641
- @font-face { font-family: "Geist Mono"; src: local("Geist Mono"), url("${GEIST_MONO_FONT_URL}") format("woff2"), url("/node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2") format("woff2"); font-display: block; font-style: normal; font-weight: 100 900; }
8642
- :root { color-scheme: dark; --font-geist-sans: "Geist Sans"; --font-geist-mono: "Geist Mono"; --fd-sidebar-width: ${sidebarWidth}px; --fd-content-width: ${contentWidth}px; --fd-toc-width: 240px; --fd-docs-height: 100vh; --fd-docs-row-1: var(--fd-nav-height, 44px); --fd-docs-font-sans: var(--font-geist-sans, "Geist Sans", var(--font-sans, system-ui, -apple-system, sans-serif)); --fd-docs-font-mono: var(--font-geist-mono, "Geist Mono", var(--font-mono, ui-monospace, monospace)); --fd-font-sans: var(--fd-docs-font-sans); --fd-font-mono: var(--fd-docs-font-mono); --fd-pixel-rail-width: 12px; --fd-sidebar-edge: calc(var(--fd-pixel-rail-width) + 18px); --fd-sidebar-guide-x: calc(var(--fd-sidebar-edge) + 16px); --fd-sidebar-link-x: calc(var(--fd-sidebar-guide-x) + 22px); --fd-sidebar-sub-guide-x: calc(var(--fd-sidebar-link-x) + 7px); --fd-sidebar-sub-link-x: calc(var(--fd-sidebar-sub-guide-x) + 28px); --fd-sidebar-branch-gap: 0px; --fd-sidebar-nested-icon-gap: 0px; --fd-sidebar-line-color: color-mix(in srgb, var(--color-fd-border, hsl(0 0% 15%)) 88%, transparent); }
9109
+ :root { color-scheme: dark; --font-geist-sans: ui-sans-serif, system-ui, sans-serif; --font-geist-mono: ui-monospace, monospace; ${layoutFontVariables} --fd-sidebar-width: ${sidebarWidth}px; --fd-content-width: ${contentWidth}px; --fd-toc-width: 240px; --fd-docs-height: 100vh; --fd-docs-row-1: var(--fd-nav-height, 44px); --fd-docs-font-sans: var(--fd-layout-font-body, var(--font-geist-sans, var(--font-sans, system-ui, -apple-system, sans-serif))); --fd-docs-font-mono: var(--fd-layout-font-code, var(--font-geist-mono, var(--font-mono, ui-monospace, monospace))); --fd-font-sans: var(--fd-docs-font-sans); --fd-font-mono: var(--fd-docs-font-mono); --fd-pixel-rail-width: 12px; --fd-sidebar-edge: calc(var(--fd-pixel-rail-width) + 18px); --fd-sidebar-guide-x: calc(var(--fd-sidebar-edge) + 16px); --fd-sidebar-link-x: calc(var(--fd-sidebar-guide-x) + 22px); --fd-sidebar-sub-guide-x: calc(var(--fd-sidebar-link-x) + 7px); --fd-sidebar-sub-link-x: calc(var(--fd-sidebar-sub-guide-x) + 28px); --fd-sidebar-branch-gap: 0px; --fd-sidebar-nested-icon-gap: 0px; --fd-sidebar-line-color: color-mix(in srgb, var(--color-fd-border, hsl(0 0% 15%)) 88%, transparent); }
8643
9110
  * { box-sizing: border-box; }
8644
9111
  html { background: var(--color-fd-background, hsl(0 0% 2%)); scroll-padding-top: calc(var(--fd-nav-height, 44px) + 24px); }
8645
9112
  html[data-farm-docs-sidebar="open"], html[data-farm-docs-sidebar="open"] body { overflow: hidden; }
8646
- body { margin: 0; min-height: 100vh; background: var(--color-fd-background, hsl(0 0% 2%)); color: var(--color-fd-foreground, oklch(0.985 0.001 106.423)); font-family: var(--fd-docs-font-sans); text-rendering: optimizeLegibility; }
9113
+ body { margin: 0; min-height: 100vh; background: var(--color-fd-background, hsl(0 0% 2%)); color: var(--color-fd-foreground, oklch(0.985 0.001 106.423)); font-family: var(--fd-docs-font-sans); font-synthesis: none; text-rendering: optimizeLegibility; }
8647
9114
  ::selection { background: var(--color-fd-foreground, #fff); color: var(--color-fd-background, #000); }
8648
9115
  a { color: inherit; }
8649
9116
  #nd-docs-layout { --fd-sidebar-col: var(--fd-sidebar-width); display: grid; grid-template: "sidebar header header" var(--fd-nav-height, 44px) "sidebar main toc" 1fr / var(--fd-sidebar-width) minmax(0, 1fr) var(--fd-toc-width) !important; min-height: 100vh; border-top: 1px solid var(--color-fd-border, hsl(0 0% 15%)); background: var(--color-fd-background, hsl(0 0% 2%)); }
@@ -8823,14 +9290,45 @@ function renderFarmDocsBridgeCss(docs) {
8823
9290
  .sh__line { min-height: 1.6em; }
8824
9291
  }
8825
9292
  ${farmDocsPixelBorderCss}
9293
+ ${fontCss}
8826
9294
  `;
8827
9295
  }
8828
- function renderPixelDocsHtml(page2, pages, docs, themeCss, clientEntry, faviconHref) {
9296
+ function renderFarmDocsLayoutFontVariable(name, font) {
9297
+ const family = font?.style.fontFamily;
9298
+ if (!family || /[;{}<>]/.test(family)) return "";
9299
+ return `${name}: ${family};`;
9300
+ }
9301
+ function renderFarmDocsFontPreloads(fontAssets) {
9302
+ return fontAssets.map(
9303
+ ({ url }) => `<link rel="preload" href="${escapeAttribute2(url)}" as="font" type="font/woff2" crossorigin>`
9304
+ ).join("\n ");
9305
+ }
9306
+ function renderFarmDocsFontPreloadHeader(fontAssets) {
9307
+ return fontAssets.map(({ url }) => `<${url}>; rel=preload; as=font; type=font/woff2; crossorigin`).join(", ");
9308
+ }
9309
+ function getFarmLayoutFontPreloads(layoutFonts) {
9310
+ const seen = /* @__PURE__ */ new Set();
9311
+ return [layoutFonts?.body, layoutFonts?.code].flatMap(
9312
+ (font) => (font?.preloads || []).filter(({ href }) => {
9313
+ if (seen.has(href)) return false;
9314
+ seen.add(href);
9315
+ return true;
9316
+ })
9317
+ );
9318
+ }
9319
+ function renderFarmLayoutFontPreloadHeader(layoutFonts) {
9320
+ return getFarmLayoutFontPreloads(layoutFonts).map(({ href, type }) => `<${href}>; rel=preload; as=font; type=${type}; crossorigin`).join(", ");
9321
+ }
9322
+ function renderPixelDocsHtml(page2, pages, docs, themeCss, clientEntry, faviconHref, requestUrl, fontAssets, layoutFonts, fontStylesheetHref) {
8829
9323
  const navTitle = typeof docs.config.nav === "object" && docs.config.nav && "title" in docs.config.nav ? String(docs.config.nav.title || "Docs") : "Docs";
8830
9324
  const description = page2.description || docs.config.metadata?.description || "";
8831
9325
  const tocItems = extractTocItems(page2.body, getThemeTocDepth(docs));
8832
9326
  const themeName = getThemeName(docs);
8833
9327
  const searchEnabled = isFarmDocsSearchEnabled(docs);
9328
+ const socialMetadata = isFarmDocsSocialImageEnabled(page2, docs) ? renderFarmDocsSocialMetadata(
9329
+ createFarmDocsSocialImageDescriptor(page2, docs, requestUrl),
9330
+ getFarmDocsCustomSocialImage(page2)
9331
+ ) : "";
8834
9332
  return `<!DOCTYPE html>
8835
9333
  <html class="dark" lang="en" data-docs-theme="${escapeAttribute2(themeName)}">
8836
9334
  <head>
@@ -8839,11 +9337,12 @@ function renderPixelDocsHtml(page2, pages, docs, themeCss, clientEntry, faviconH
8839
9337
  <meta name="generator" content="@farming-labs/docs via Farm.js">
8840
9338
  <title>${escapeHtml(page2.title)}</title>
8841
9339
  ${renderFarmDocsFaviconLink(faviconHref)}
8842
- <link rel="preload" href="${GEIST_SANS_FONT_URL}" as="font" type="font/woff2" crossorigin>
8843
- <link rel="preload" href="${GEIST_MONO_FONT_URL}" as="font" type="font/woff2" crossorigin>
9340
+ ${renderFarmDocsFontPreloads(fontAssets)}
9341
+ ${fontStylesheetHref ? `<link rel="stylesheet" href="${escapeAttribute2(fontStylesheetHref)}">` : ""}
8844
9342
  ${description ? `<meta name="description" content="${escapeHtml(description)}">` : ""}
9343
+ ${socialMetadata}
8845
9344
  <style>${themeCss}
8846
- ${renderFarmDocsBridgeCss(docs)}</style>
9345
+ ${renderFarmDocsBridgeCss(docs, fontAssets, layoutFonts)}</style>
8847
9346
  ${searchEnabled ? renderDocsSearchBootstrapScript() : ""}
8848
9347
  </head>
8849
9348
  <body>
@@ -8895,9 +9394,34 @@ ${renderMarkdownHtmlWithTitleMeta(page2, docs)}
8895
9394
  }
8896
9395
  function createFarmDocsHandler(docs, options) {
8897
9396
  const faviconHref = resolveFarmDocsFavicon(docs, options);
9397
+ const fontAssets = options.fontAssets ?? toFarmDocsPublicFontAssets(resolveFarmDocsFontAssets(options.root));
9398
+ const fallbackFontPreloadHeader = renderFarmDocsFontPreloadHeader(fontAssets);
8898
9399
  return /* @__PURE__ */ __name(async function handleFarmDocsRequest(request) {
8899
9400
  if (!docs?.enabled || request.method !== "GET" && request.method !== "HEAD") return null;
8900
9401
  const contentDir = resolveFarmDocsContentDir(docs, options);
9402
+ const requestUrl = new URL(request.url);
9403
+ const socialImageSlug = getFarmDocsSocialImageSlug(docs, requestUrl);
9404
+ if (socialImageSlug !== null) {
9405
+ const socialImagePage = loadFarmDocsPage(contentDir, docs, socialImageSlug);
9406
+ if (!socialImagePage || !isFarmDocsSocialImageEnabled(socialImagePage, docs) || getFarmDocsCustomSocialImage(socialImagePage)) {
9407
+ return null;
9408
+ }
9409
+ const descriptor = createFarmDocsSocialImageDescriptor(socialImagePage, docs, requestUrl);
9410
+ if (requestUrl.pathname !== descriptor.imagePath) return null;
9411
+ const etag = `"${descriptor.hash}"`;
9412
+ const immutable = requestUrl.searchParams.get("v") === descriptor.hash;
9413
+ const headers = new Headers({
9414
+ "Content-Type": "image/svg+xml; charset=utf-8",
9415
+ "Cache-Control": immutable ? "public, max-age=31536000, immutable" : "public, max-age=0, must-revalidate",
9416
+ ETag: etag,
9417
+ "X-Content-Type-Options": "nosniff"
9418
+ });
9419
+ if (request.headers.get("if-none-match") === etag) {
9420
+ return new Response(null, { status: 304, headers });
9421
+ }
9422
+ if (request.method === "HEAD") return new Response(null, { status: 200, headers });
9423
+ return new Response(renderFarmDocsSocialImageSvg(descriptor), { status: 200, headers });
9424
+ }
8901
9425
  const publicResponse = createFarmDocsPublicResponse(contentDir, docs, request);
8902
9426
  if (publicResponse) return publicResponse;
8903
9427
  if (!isFarmDocsRequest(docs, request)) return null;
@@ -8920,6 +9444,11 @@ function createFarmDocsHandler(docs, options) {
8920
9444
  }
8921
9445
  );
8922
9446
  }
9447
+ const pathname = new URL(request.url).pathname;
9448
+ const layoutFonts = await options.resolveLayoutFonts?.(pathname);
9449
+ const usesLayoutFonts = Boolean(layoutFonts?.body || layoutFonts?.code);
9450
+ const activeFontAssets = usesLayoutFonts ? [] : fontAssets;
9451
+ const fontPreloadHeader = usesLayoutFonts ? renderFarmLayoutFontPreloadHeader(layoutFonts) : fallbackFontPreloadHeader;
8923
9452
  return new Response(
8924
9453
  renderPixelDocsHtml(
8925
9454
  page2,
@@ -8927,39 +9456,44 @@ function createFarmDocsHandler(docs, options) {
8927
9456
  docs,
8928
9457
  resolvePixelBorderThemeCss(options),
8929
9458
  options.clientEntry || "/farm-client.js",
8930
- faviconHref
9459
+ faviconHref,
9460
+ requestUrl,
9461
+ activeFontAssets,
9462
+ layoutFonts,
9463
+ usesLayoutFonts ? options.fontStylesheetHref : void 0
8931
9464
  ),
8932
9465
  {
8933
9466
  status: 200,
8934
9467
  headers: {
8935
9468
  "Content-Type": "text/html; charset=utf-8",
8936
- "Cache-Control": "public, s-maxage=60, stale-while-revalidate=300"
9469
+ "Cache-Control": "public, s-maxage=60, stale-while-revalidate=300",
9470
+ ...fontPreloadHeader ? { Link: fontPreloadHeader } : {}
8937
9471
  }
8938
9472
  }
8939
9473
  );
8940
9474
  }, "handleFarmDocsRequest");
8941
9475
  }
8942
- var import_node_fs8, import_node_module5, import_node_path11, import_docs, import_marked, import_sugar_high, DOCS_FILE_NAMES, DOCS_FILE_EXTENSIONS2, FARM_DOCS_FALLBACK_FAVICON, FARM_DOCS_FAVICON_FILES, GEIST_SANS_FONT_URL, GEIST_MONO_FONT_URL, CODE_LANGUAGE_LABELS, EXTENSIONLESS_CODE_FILENAMES, SIDEBAR_SECTION_ORDER, SIDEBAR_PAGE_ORDER, themeCssCache;
9476
+ var import_node_fs9, import_node_module5, import_node_path13, import_docs, import_marked, import_sugar_high, DOCS_FILE_NAMES, DOCS_FILE_EXTENSIONS2, FARM_DOCS_FALLBACK_FAVICON, FARM_DOCS_FAVICON_FILES, CODE_LANGUAGE_LABELS, EXTENSIONLESS_CODE_FILENAMES, SIDEBAR_SECTION_ORDER, SIDEBAR_PAGE_ORDER, themeCssCache;
8943
9477
  var init_handler = __esm({
8944
9478
  "src/docs/handler.ts"() {
8945
9479
  "use strict";
8946
- import_node_fs8 = require("fs");
9480
+ import_node_fs9 = require("fs");
8947
9481
  import_node_module5 = require("module");
8948
- import_node_path11 = __toESM(require("path"));
9482
+ import_node_path13 = __toESM(require("path"));
8949
9483
  import_docs = require("@farming-labs/docs");
8950
9484
  import_marked = require("marked");
8951
9485
  import_sugar_high = require("sugar-high");
9486
+ init_fonts();
8952
9487
  init_last_modified();
8953
9488
  init_pixel_border_css();
8954
9489
  init_search_client();
9490
+ init_social_image();
8955
9491
  DOCS_FILE_NAMES = ["page.mdx", "page.md", "index.mdx", "index.md"];
8956
9492
  DOCS_FILE_EXTENSIONS2 = [".mdx", ".md"];
8957
9493
  FARM_DOCS_FALLBACK_FAVICON = "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 32 32'%3E%3Crect width='32' height='32' fill='black'/%3E%3Cpath d='M7 8h18v3H10v5h12v3H10v5H7z' fill='white'/%3E%3C/svg%3E";
8958
9494
  FARM_DOCS_FAVICON_FILES = ["favicon.svg", "favicon.ico", "favicon.png"];
8959
- GEIST_SANS_FONT_URL = "/assets/Geist-Variable-CrgPqtmy.woff2";
8960
- GEIST_MONO_FONT_URL = "/assets/GeistMono-Variable-BNLlm6Cd.woff2";
8961
9495
  __name(trimSlashes2, "trimSlashes");
8962
- __name(normalizeEntry, "normalizeEntry");
9496
+ __name(normalizeEntry2, "normalizeEntry");
8963
9497
  __name(normalizeSlug, "normalizeSlug");
8964
9498
  __name(resolveFarmDocsFavicon, "resolveFarmDocsFavicon");
8965
9499
  __name(renderFarmDocsFaviconLink, "renderFarmDocsFaviconLink");
@@ -9149,6 +9683,11 @@ var init_handler = __esm({
9149
9683
  __name(readCssWithImports, "readCssWithImports");
9150
9684
  __name(resolvePixelBorderThemeCss, "resolvePixelBorderThemeCss");
9151
9685
  __name(renderFarmDocsBridgeCss, "renderFarmDocsBridgeCss");
9686
+ __name(renderFarmDocsLayoutFontVariable, "renderFarmDocsLayoutFontVariable");
9687
+ __name(renderFarmDocsFontPreloads, "renderFarmDocsFontPreloads");
9688
+ __name(renderFarmDocsFontPreloadHeader, "renderFarmDocsFontPreloadHeader");
9689
+ __name(getFarmLayoutFontPreloads, "getFarmLayoutFontPreloads");
9690
+ __name(renderFarmLayoutFontPreloadHeader, "renderFarmLayoutFontPreloadHeader");
9152
9691
  __name(renderPixelDocsHtml, "renderPixelDocsHtml");
9153
9692
  __name(createFarmDocsHandler, "createFarmDocsHandler");
9154
9693
  }
@@ -9430,18 +9969,18 @@ function sanitizeDocsConfig(config) {
9430
9969
  return cloneSerializable(config) || {};
9431
9970
  }
9432
9971
  async function findDocsConfigPath(rootDir, configPath) {
9433
- const { existsSync: existsSync15 } = await import("fs");
9972
+ const { existsSync: existsSync16 } = await import("fs");
9434
9973
  const root = import_path9.default.resolve(rootDir || process.cwd());
9435
9974
  if (configPath) {
9436
9975
  const resolvedPath = import_path9.default.isAbsolute(configPath) ? configPath : import_path9.default.join(root, configPath);
9437
- if (!existsSync15(resolvedPath)) {
9976
+ if (!existsSync16(resolvedPath)) {
9438
9977
  throw new Error(`Could not find docs config at ${configPath}.`);
9439
9978
  }
9440
9979
  return import_path9.default.resolve(resolvedPath);
9441
9980
  }
9442
9981
  for (const filename of DOCS_CONFIG_FILENAMES) {
9443
9982
  const resolvedPath = import_path9.default.join(root, filename);
9444
- if (existsSync15(resolvedPath)) {
9983
+ if (existsSync16(resolvedPath)) {
9445
9984
  return resolvedPath;
9446
9985
  }
9447
9986
  }
@@ -9629,6 +10168,7 @@ async function resolveConfig(userConfig, mode) {
9629
10168
  headers: /* @__PURE__ */ __name(() => [...headers, ...routeRuleHeaders], "headers"),
9630
10169
  routeRules,
9631
10170
  images: resolveFarmImageConfig(userConfig.images),
10171
+ performance: resolveFarmPerformanceConfig(userConfig.performance),
9632
10172
  publicDir: userConfig.publicDir || "public",
9633
10173
  i18n: resolveFarmI18nConfig(userConfig.i18n, { root, mode }),
9634
10174
  openapi: {
@@ -9667,7 +10207,7 @@ async function resolveConfig(userConfig, mode) {
9667
10207
  return resolved;
9668
10208
  }
9669
10209
  async function loadConfig(rootDir, configPath, mode = process.env.NODE_ENV === "production" ? "production" : "development", loadEnvironment) {
9670
- const { existsSync: existsSync15 } = await import("fs");
10210
+ const { existsSync: existsSync16 } = await import("fs");
9671
10211
  const loadEnv = loadEnvironment || (await import("vite")).loadEnv;
9672
10212
  const root = rootDir || process.cwd();
9673
10213
  const loadedEnv = loadEnv(mode, root, "");
@@ -9691,7 +10231,7 @@ async function loadConfig(rootDir, configPath, mode = process.env.NODE_ENV === "
9691
10231
  try {
9692
10232
  const absolutePath = relativePath2.startsWith("/") ? relativePath2 : import_path9.default.join(root, relativePath2);
9693
10233
  const normalizedPath = import_path9.default.resolve(absolutePath);
9694
- if (!existsSync15(normalizedPath)) {
10234
+ if (!existsSync16(normalizedPath)) {
9695
10235
  continue;
9696
10236
  }
9697
10237
  const loadedConfig = await loadFarmConfigFile(normalizedPath, { root });
@@ -9722,6 +10262,7 @@ var init_config2 = __esm({
9722
10262
  init_image_config();
9723
10263
  init_config();
9724
10264
  init_auth_config();
10265
+ init_preload();
9725
10266
  init_config_entry();
9726
10267
  FARM_RESOLVED_CUSTOM_CONTEXT = /* @__PURE__ */ Symbol.for("farm.resolvedCustomContext");
9727
10268
  __name(normalizeDeployTarget, "normalizeDeployTarget");
@@ -10518,8 +11059,8 @@ var init_generator = __esm({
10518
11059
  }
10519
11060
  routeGroups.get(key).push(route);
10520
11061
  }
10521
- for (const [path25, routeList] of routeGroups) {
10522
- const openAPIPath = this.convertToOpenAPIPath(path25);
11062
+ for (const [path27, routeList] of routeGroups) {
11063
+ const openAPIPath = this.convertToOpenAPIPath(path27);
10523
11064
  spec.paths[openAPIPath] = {};
10524
11065
  for (const route of routeList) {
10525
11066
  for (const method of route.methods) {
@@ -10533,8 +11074,8 @@ var init_generator = __esm({
10533
11074
  /**
10534
11075
  * Convert Farm.js API path to OpenAPI path format
10535
11076
  */
10536
- convertToOpenAPIPath(path25) {
10537
- return path25.replace(/^\/api/, "");
11077
+ convertToOpenAPIPath(path27) {
11078
+ return path27.replace(/^\/api/, "");
10538
11079
  }
10539
11080
  /**
10540
11081
  * Extract request body schema from endpoint
@@ -10696,8 +11237,8 @@ var init_generator = __esm({
10696
11237
  /**
10697
11238
  * Generate operation summary
10698
11239
  */
10699
- generateSummary(path25, method) {
10700
- const cleanPath = path25.replace(/^\/api\//, "");
11240
+ generateSummary(path27, method) {
11241
+ const cleanPath = path27.replace(/^\/api\//, "");
10701
11242
  const pathParts = cleanPath.split("/");
10702
11243
  const lastPart = pathParts[pathParts.length - 1];
10703
11244
  const action = method === "GET" ? "Get" : method === "POST" ? "Create" : method === "PUT" ? "Update" : method === "DELETE" ? "Delete" : method === "PATCH" ? "Update" : method;
@@ -10706,22 +11247,22 @@ var init_generator = __esm({
10706
11247
  /**
10707
11248
  * Generate operation description
10708
11249
  */
10709
- generateDescription(path25, method) {
10710
- const cleanPath = path25.replace(/^\/api\//, "");
11250
+ generateDescription(path27, method) {
11251
+ const cleanPath = path27.replace(/^\/api\//, "");
10711
11252
  return `${method} ${cleanPath} endpoint`;
10712
11253
  }
10713
11254
  /**
10714
11255
  * Generate operation ID
10715
11256
  */
10716
- generateOperationId(path25, method) {
10717
- const cleanPath = path25.replace(/^\/api\//, "").replace(/\//g, "_");
11257
+ generateOperationId(path27, method) {
11258
+ const cleanPath = path27.replace(/^\/api\//, "").replace(/\//g, "_");
10718
11259
  return `${method.toLowerCase()}_${cleanPath}`;
10719
11260
  }
10720
11261
  /**
10721
11262
  * Generate tags for grouping operations
10722
11263
  */
10723
- generateTags(path25) {
10724
- const cleanPath = path25.replace(/^\/api\//, "");
11264
+ generateTags(path27) {
11265
+ const cleanPath = path27.replace(/^\/api\//, "");
10725
11266
  const pathParts = cleanPath.split("/");
10726
11267
  if (pathParts.length > 1) {
10727
11268
  return [pathParts[0]];
@@ -10734,8 +11275,8 @@ var init_generator = __esm({
10734
11275
  async generateSpecFile(routes, outputPath) {
10735
11276
  const spec = await this.generateSpec(routes);
10736
11277
  const fs9 = require("fs");
10737
- const path25 = require("path");
10738
- const dir = path25.dirname(outputPath);
11278
+ const path27 = require("path");
11279
+ const dir = path27.dirname(outputPath);
10739
11280
  if (!fs9.existsSync(dir)) {
10740
11281
  fs9.mkdirSync(dir, { recursive: true });
10741
11282
  }
@@ -11155,7 +11696,7 @@ async function createFarmDevtoolsSnapshot(input) {
11155
11696
  generatedAt: (input.now?.() || /* @__PURE__ */ new Date()).toISOString(),
11156
11697
  health: resolveHealth(diagnostics),
11157
11698
  project: {
11158
- name: import_node_path12.default.basename(import_node_path12.default.resolve(input.root)),
11699
+ name: import_node_path14.default.basename(import_node_path14.default.resolve(input.root)),
11159
11700
  root: input.root,
11160
11701
  srcDir: input.srcDir,
11161
11702
  basePath: config.basePath || "/",
@@ -11204,11 +11745,11 @@ async function createFarmDevtoolsSnapshot(input) {
11204
11745
  diagnostics
11205
11746
  };
11206
11747
  }
11207
- var import_node_path12;
11748
+ var import_node_path14;
11208
11749
  var init_devtools = __esm({
11209
11750
  "src/devtools.ts"() {
11210
11751
  "use strict";
11211
- import_node_path12 = __toESM(require("path"));
11752
+ import_node_path14 = __toESM(require("path"));
11212
11753
  init_config2();
11213
11754
  init_cron();
11214
11755
  init_route_runtime();
@@ -13279,6 +13820,22 @@ var import_promises3 = require("fs/promises");
13279
13820
  var OPTIONAL_CATCH_ALL_SEGMENT = /^\[\[\.\.\.([^\]]+)\]\]$/;
13280
13821
  var REQUIRED_CATCH_ALL_SEGMENT = /^\[\.\.\.([^\]]+)\]$/;
13281
13822
  var REQUIRED_DYNAMIC_SEGMENT = /^\[([^\]]+)\]$/;
13823
+ var REQUEST_API_LABELS = /* @__PURE__ */ new Map([
13824
+ ["cookies", "request cookies"],
13825
+ ["headers", "request headers"],
13826
+ ["getCurrentRequest", "the current Request"],
13827
+ ["useSearchParams", "URL search parameters"],
13828
+ ["auth", "request authentication"],
13829
+ ["getServerSession", "request authentication"],
13830
+ ["getSession", "request authentication"],
13831
+ ["currentUser", "request authentication"]
13832
+ ]);
13833
+ var ROUTE_PROP_LABELS = /* @__PURE__ */ new Map([
13834
+ ["searchParams", "URL search parameters"],
13835
+ ["middleware", "middleware request data"],
13836
+ ["middlewareData", "middleware request data"],
13837
+ ["context", "request context"]
13838
+ ]);
13282
13839
  function resolveRouteRenderingConfig(mod, source) {
13283
13840
  const directiveConfig = parseRouteRenderingDirective(source);
13284
13841
  let ssg = directiveConfig?.ssg ?? false;
@@ -13331,6 +13888,221 @@ async function resolveRouteRenderingConfigFromFile(mod, filePath) {
13331
13888
  return resolveRouteRenderingConfig(mod, source);
13332
13889
  }
13333
13890
  __name(resolveRouteRenderingConfigFromFile, "resolveRouteRenderingConfigFromFile");
13891
+ function analyzeStaticRouteCandidate(mod, source, options = {}) {
13892
+ const blockers = [];
13893
+ if (!source) {
13894
+ blockers.push("route source is unavailable");
13895
+ }
13896
+ if (options.isDynamic) {
13897
+ blockers.push("the route has dynamic path segments");
13898
+ }
13899
+ if (hasExplicitRenderingConfig(mod, source)) {
13900
+ blockers.push("the route already has explicit rendering config");
13901
+ }
13902
+ if (source) {
13903
+ blockers.push(...findRequestBoundSourceBlockers(source));
13904
+ }
13905
+ return {
13906
+ candidate: blockers.length === 0,
13907
+ blockers
13908
+ };
13909
+ }
13910
+ __name(analyzeStaticRouteCandidate, "analyzeStaticRouteCandidate");
13911
+ function findRequestBoundSourceBlockers(source) {
13912
+ const blockers = /* @__PURE__ */ new Set();
13913
+ const code = stripCommentsAndLiteralContents(source);
13914
+ const sourceWithoutComments = stripComments(source);
13915
+ for (const binding of readRequestApiImportBindings(sourceWithoutComments)) {
13916
+ if (new RegExp(`\\b${escapeRegExp2(binding.local)}\\s*(?:\\?\\.\\s*)?\\(`).test(code)) {
13917
+ blockers.add(`the route reads ${binding.label}`);
13918
+ }
13919
+ }
13920
+ for (const namespace of readNamespaceImportBindings(sourceWithoutComments)) {
13921
+ for (const [name, label] of REQUEST_API_LABELS) {
13922
+ if (new RegExp(
13923
+ `\\b${escapeRegExp2(namespace)}\\s*(?:\\?\\.\\s*|\\.\\s*)${name}\\s*(?:\\?\\.\\s*)?\\(`
13924
+ ).test(code)) {
13925
+ blockers.add(`the route reads ${label}`);
13926
+ }
13927
+ }
13928
+ }
13929
+ const params = readDefaultRouteParameters(code);
13930
+ if (params) {
13931
+ for (const [name, label] of ROUTE_PROP_LABELS) {
13932
+ if (new RegExp(`\\b${name}\\b`).test(params)) {
13933
+ blockers.add(`the route reads ${label}`);
13934
+ }
13935
+ }
13936
+ const propsName = params.match(/^\s*([A-Za-z_$][\w$]*)\s*(?::|,|$)/)?.[1];
13937
+ if (propsName) {
13938
+ for (const [name, label] of ROUTE_PROP_LABELS) {
13939
+ if (new RegExp(`\\b${escapeRegExp2(propsName)}\\s*\\.\\s*${name}\\b`).test(code)) {
13940
+ blockers.add(`the route reads ${label}`);
13941
+ }
13942
+ if (new RegExp(`\\{[^}]*\\b${name}\\b[^}]*\\}\\s*=\\s*${escapeRegExp2(propsName)}\\b`).test(
13943
+ code
13944
+ )) {
13945
+ blockers.add(`the route reads ${label}`);
13946
+ }
13947
+ }
13948
+ }
13949
+ }
13950
+ return [...blockers];
13951
+ }
13952
+ __name(findRequestBoundSourceBlockers, "findRequestBoundSourceBlockers");
13953
+ function readRequestApiImportBindings(source) {
13954
+ const bindings = [];
13955
+ for (const clause of readStaticImportClauses(source)) {
13956
+ const namedGroup = clause.match(/\{([\s\S]*?)\}/)?.[1];
13957
+ for (const rawSpecifier of (namedGroup ?? "").split(",")) {
13958
+ const specifier = rawSpecifier.trim().replace(/^type\s+/, "");
13959
+ const imported = specifier.match(/^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
13960
+ if (!imported?.[1]) continue;
13961
+ const label = REQUEST_API_LABELS.get(imported[1]);
13962
+ if (label) bindings.push({ local: imported[2] ?? imported[1], label });
13963
+ }
13964
+ const local = clause.match(/^\s*([A-Za-z_$][\w$]*)\s*(?:,|$)/)?.[1];
13965
+ if (local) {
13966
+ const label = REQUEST_API_LABELS.get(local);
13967
+ if (label) bindings.push({ local, label });
13968
+ }
13969
+ }
13970
+ return bindings;
13971
+ }
13972
+ __name(readRequestApiImportBindings, "readRequestApiImportBindings");
13973
+ function readNamespaceImportBindings(source) {
13974
+ return readStaticImportClauses(source).map((clause) => clause.match(/\*\s*as\s*([A-Za-z_$][\w$]*)/)?.[1]).filter((binding) => Boolean(binding));
13975
+ }
13976
+ __name(readNamespaceImportBindings, "readNamespaceImportBindings");
13977
+ function readStaticImportClauses(source) {
13978
+ return [...source.matchAll(/\bimport\s+(?!\()([\s\S]*?)\s+from\s*["'][^"']+["']/g)].map((match) => match[1]).filter((clause) => Boolean(clause));
13979
+ }
13980
+ __name(readStaticImportClauses, "readStaticImportClauses");
13981
+ function readDefaultRouteParameters(code) {
13982
+ const inlineParams = code.match(/\bexport\s+default\s+(?:async\s+)?function\b[^(]*\(([^)]*)\)/)?.[1] ?? code.match(/\bexport\s+default\s+(?:async\s+)?\(([^)]*)\)\s*=>/)?.[1];
13983
+ if (inlineParams !== void 0) return inlineParams;
13984
+ 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];
13985
+ if (!binding) return void 0;
13986
+ const escapedBinding = escapeRegExp2(binding);
13987
+ return code.match(new RegExp(`\\bfunction\\s+${escapedBinding}\\s*\\(([^)]*)\\)`))?.[1] ?? code.match(
13988
+ new RegExp(
13989
+ `\\b(?:const|let|var)\\s+${escapedBinding}\\s*=\\s*(?:async\\s+)?\\(([^)]*)\\)\\s*=>`
13990
+ )
13991
+ )?.[1] ?? code.match(
13992
+ new RegExp(`\\b(?:const|let|var)\\s+${escapedBinding}\\s*=\\s*function\\s*\\(([^)]*)\\)`)
13993
+ )?.[1];
13994
+ }
13995
+ __name(readDefaultRouteParameters, "readDefaultRouteParameters");
13996
+ function stripCommentsAndLiteralContents(source) {
13997
+ return sanitizeSource(source, true);
13998
+ }
13999
+ __name(stripCommentsAndLiteralContents, "stripCommentsAndLiteralContents");
14000
+ function stripComments(source) {
14001
+ return sanitizeSource(source, false);
14002
+ }
14003
+ __name(stripComments, "stripComments");
14004
+ function sanitizeSource(source, maskLiterals) {
14005
+ const output = [...source];
14006
+ const mask = /* @__PURE__ */ __name((index) => {
14007
+ if (output[index] !== "\n" && output[index] !== "\r") output[index] = " ";
14008
+ }, "mask");
14009
+ const scanQuoted = /* @__PURE__ */ __name((start, quote) => {
14010
+ let index = start;
14011
+ if (maskLiterals) mask(index);
14012
+ index += 1;
14013
+ while (index < source.length) {
14014
+ if (source[index] === "\\") {
14015
+ if (maskLiterals) mask(index);
14016
+ if (index + 1 < source.length && maskLiterals) mask(index + 1);
14017
+ index += 2;
14018
+ continue;
14019
+ }
14020
+ const character = source[index];
14021
+ if (maskLiterals) mask(index);
14022
+ index += 1;
14023
+ if (character === quote) break;
14024
+ }
14025
+ return index;
14026
+ }, "scanQuoted");
14027
+ const scanCode = /* @__PURE__ */ __name((start, stopAtTemplateBrace) => {
14028
+ let index = start;
14029
+ let braceDepth = 0;
14030
+ while (index < source.length) {
14031
+ const character = source[index];
14032
+ const next = source[index + 1];
14033
+ if (character === "/" && next === "/") {
14034
+ while (index < source.length && source[index] !== "\n" && source[index] !== "\r") {
14035
+ mask(index++);
14036
+ }
14037
+ continue;
14038
+ }
14039
+ if (character === "/" && next === "*") {
14040
+ mask(index++);
14041
+ mask(index++);
14042
+ while (index < source.length) {
14043
+ const closesComment = source[index] === "*" && source[index + 1] === "/";
14044
+ mask(index++);
14045
+ if (closesComment) {
14046
+ mask(index++);
14047
+ break;
14048
+ }
14049
+ }
14050
+ continue;
14051
+ }
14052
+ if (character === "'" || character === '"') {
14053
+ index = scanQuoted(index, character);
14054
+ continue;
14055
+ }
14056
+ if (character === "`") {
14057
+ if (maskLiterals) mask(index);
14058
+ index += 1;
14059
+ while (index < source.length) {
14060
+ if (source[index] === "\\") {
14061
+ if (maskLiterals) mask(index);
14062
+ if (index + 1 < source.length && maskLiterals) mask(index + 1);
14063
+ index += 2;
14064
+ } else if (source[index] === "`") {
14065
+ if (maskLiterals) mask(index);
14066
+ index += 1;
14067
+ break;
14068
+ } else if (source[index] === "$" && source[index + 1] === "{") {
14069
+ if (maskLiterals) {
14070
+ mask(index);
14071
+ mask(index + 1);
14072
+ }
14073
+ index = scanCode(index + 2, true);
14074
+ } else {
14075
+ if (maskLiterals) mask(index);
14076
+ index += 1;
14077
+ }
14078
+ }
14079
+ continue;
14080
+ }
14081
+ if (character === "{") {
14082
+ braceDepth += 1;
14083
+ } else if (character === "}") {
14084
+ if (stopAtTemplateBrace && braceDepth === 0) {
14085
+ if (maskLiterals) mask(index);
14086
+ return index + 1;
14087
+ }
14088
+ braceDepth = Math.max(0, braceDepth - 1);
14089
+ }
14090
+ index += 1;
14091
+ }
14092
+ return index;
14093
+ }, "scanCode");
14094
+ scanCode(0, false);
14095
+ return output.join("");
14096
+ }
14097
+ __name(sanitizeSource, "sanitizeSource");
14098
+ function escapeRegExp2(value) {
14099
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
14100
+ }
14101
+ __name(escapeRegExp2, "escapeRegExp");
14102
+ function hasExplicitRenderingConfig(mod, source) {
14103
+ 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";
14104
+ }
14105
+ __name(hasExplicitRenderingConfig, "hasExplicitRenderingConfig");
13334
14106
  function parseRouteRenderingDirective(source) {
13335
14107
  if (!source) {
13336
14108
  return void 0;
@@ -13462,9 +14234,10 @@ function skipHorizontalWhitespace(source, start) {
13462
14234
  return index;
13463
14235
  }
13464
14236
  __name(skipHorizontalWhitespace, "skipHorizontalWhitespace");
13465
- async function collectSSGPages(routes, loadModule2) {
14237
+ async function collectSSGPages(routes, loadModule2, options = {}) {
13466
14238
  const ssgPages = [];
13467
14239
  const ssrRoutes = [];
14240
+ const staticCandidateRoutes = [];
13468
14241
  for (const route of routes) {
13469
14242
  try {
13470
14243
  const mod = await loadModule2(route.filePath);
@@ -13472,7 +14245,8 @@ async function collectSSGPages(routes, loadModule2) {
13472
14245
  ssrRoutes.push(route.path);
13473
14246
  continue;
13474
14247
  }
13475
- const rendering = await resolveRouteRenderingConfigFromFile(mod, route.filePath);
14248
+ const source = await (0, import_promises3.readFile)(route.filePath, "utf8").catch(() => void 0);
14249
+ const rendering = resolveRouteRenderingConfig(mod, source);
13476
14250
  if (rendering.ssg) {
13477
14251
  if (route.isDynamic) {
13478
14252
  const getStaticPaths = mod.getStaticPaths || mod.generateStaticParams;
@@ -13499,12 +14273,26 @@ async function collectSSGPages(routes, loadModule2) {
13499
14273
  }
13500
14274
  } else {
13501
14275
  ssrRoutes.push(route.path);
14276
+ if (options.suggestStaticRendering !== false && analyzeStaticRouteCandidate(mod, source, { isDynamic: route.isDynamic }).candidate) {
14277
+ staticCandidateRoutes.push(route.path);
14278
+ }
13502
14279
  }
13503
14280
  } catch (error) {
13504
14281
  console.error(`Error processing route ${route.path}:`, error);
13505
14282
  ssrRoutes.push(route.path);
13506
14283
  }
13507
14284
  }
14285
+ if (staticCandidateRoutes.length > 0) {
14286
+ const visibleRoutes = staticCandidateRoutes.slice(0, 10);
14287
+ const remainingCount = staticCandidateRoutes.length - visibleRoutes.length;
14288
+ const remainingSuffix = remainingCount > 0 ? `
14289
+ \u2026and ${remainingCount} more` : "";
14290
+ console.warn(
14291
+ `[Farm.js] ${staticCandidateRoutes.length} route${staticCandidateRoutes.length === 1 ? " appears" : "s appear"} eligible for static rendering:
14292
+ ${visibleRoutes.join("\n ")}${remainingSuffix}
14293
+ Add \`export const dynamic = "force-static";\` after verifying that imported code does not read cookies, headers, authentication, or other request data.`
14294
+ );
14295
+ }
13508
14296
  return { ssg: ssgPages, ssr: ssrRoutes };
13509
14297
  }
13510
14298
  __name(collectSSGPages, "collectSSGPages");
@@ -14419,25 +15207,25 @@ function defineRoutes(input) {
14419
15207
  }
14420
15208
  __name(defineRoutes, "defineRoutes");
14421
15209
  var routesBuilder = {
14422
- page(path25, options) {
15210
+ page(path27, options) {
14423
15211
  return normalizeProgrammaticRoute({
14424
15212
  kind: "page",
14425
- path: path25,
15213
+ path: path27,
14426
15214
  ...options
14427
15215
  });
14428
15216
  },
14429
- layout(path25, options) {
15217
+ layout(path27, options) {
14430
15218
  return normalizeProgrammaticRoute({
14431
15219
  kind: "layout",
14432
- path: path25,
15220
+ path: path27,
14433
15221
  ...options
14434
15222
  });
14435
15223
  },
14436
- api(path25, options) {
15224
+ api(path27, options) {
14437
15225
  const { render, runtime, regions, maxDuration, ...methods } = options;
14438
15226
  return normalizeProgrammaticRoute({
14439
15227
  kind: "api",
14440
- path: path25,
15228
+ path: path27,
14441
15229
  render,
14442
15230
  runtime,
14443
15231
  regions,
@@ -14473,7 +15261,7 @@ function getProgrammaticRouteSearchClientOptions(search) {
14473
15261
  return Object.keys(options).length > 0 ? options : void 0;
14474
15262
  }
14475
15263
  __name(getProgrammaticRouteSearchClientOptions, "getProgrammaticRouteSearchClientOptions");
14476
- function resolveProgrammaticRouteSearch(searchConfig, rawSearch, path25, routePath) {
15264
+ function resolveProgrammaticRouteSearch(searchConfig, rawSearch, path27, routePath) {
14477
15265
  const schema = getProgrammaticRouteSearchSchema(searchConfig);
14478
15266
  const search = parseProgrammaticSchema(schema, rawSearch, "search", routePath);
14479
15267
  const options = getProgrammaticRouteSearchOptions(searchConfig);
@@ -14482,7 +15270,7 @@ function resolveProgrammaticRouteSearch(searchConfig, rawSearch, path25, routePa
14482
15270
  schema,
14483
15271
  rawSearch,
14484
15272
  parsedSearch: search,
14485
- path: path25
15273
+ path: path27
14486
15274
  });
14487
15275
  return { search, canonicalPath };
14488
15276
  }
@@ -14665,7 +15453,7 @@ function createRouteModuleFromProgrammaticPage(route) {
14665
15453
  mod.__farmRouteData = route.data;
14666
15454
  mod.__farmRouteParsesProps = true;
14667
15455
  mod.__farmResolveRouteProps = (props) => resolveProgrammaticRouteProps(route, props);
14668
- mod.__farmResolveRouteCanonicalPath = (rawSearch, path25) => resolveProgrammaticRouteSearch(route.search, rawSearch, path25, route.path).canonicalPath;
15456
+ mod.__farmResolveRouteCanonicalPath = (rawSearch, path27) => resolveProgrammaticRouteSearch(route.search, rawSearch, path27, route.path).canonicalPath;
14669
15457
  }
14670
15458
  if (route.pending || route.error || route.notFound) {
14671
15459
  mod.__farmRouteComponents = {
@@ -15141,6 +15929,15 @@ var import_path5 = __toESM(require("path"));
15141
15929
  var import_fs2 = __toESM(require("fs"));
15142
15930
  var import_path4 = __toESM(require("path"));
15143
15931
  var import_url2 = require("url");
15932
+
15933
+ // src/island.ts
15934
+ var FARM_ISLAND_STRATEGIES = ["load", "interaction", "visible", "idle"];
15935
+ function isFarmIslandStrategy(value) {
15936
+ return FARM_ISLAND_STRATEGIES.includes(value);
15937
+ }
15938
+ __name(isFarmIslandStrategy, "isFarmIslandStrategy");
15939
+
15940
+ // src/utils/client-component.ts
15144
15941
  function readIfExists(filePath) {
15145
15942
  try {
15146
15943
  if (!filePath || !import_fs2.default.existsSync(filePath)) {
@@ -15211,56 +16008,186 @@ function hasHydrateExport(content) {
15211
16008
  return /\bexport\s+const\s+hydrate\s*=\s*true\b/.test(content) || /\bexport\s*\{\s*hydrate\s*\}\b/.test(content);
15212
16009
  }
15213
16010
  __name(hasHydrateExport, "hasHydrateExport");
16011
+ function tokenizeModuleSource(content) {
16012
+ const tokens = [];
16013
+ let index = 0;
16014
+ let line = 1;
16015
+ while (index < content.length) {
16016
+ const character = content[index];
16017
+ if (/\s/.test(character)) {
16018
+ if (character === "\n") line++;
16019
+ index++;
16020
+ continue;
16021
+ }
16022
+ if (character === "/" && content[index + 1] === "/") {
16023
+ index += 2;
16024
+ while (index < content.length && content[index] !== "\n") index++;
16025
+ continue;
16026
+ }
16027
+ if (character === "/" && content[index + 1] === "*") {
16028
+ index += 2;
16029
+ while (index < content.length) {
16030
+ if (content[index] === "\n") line++;
16031
+ if (content[index] === "*" && content[index + 1] === "/") {
16032
+ index += 2;
16033
+ break;
16034
+ }
16035
+ index++;
16036
+ }
16037
+ continue;
16038
+ }
16039
+ if (character === '"' || character === "'") {
16040
+ const quote = character;
16041
+ const tokenLine = line;
16042
+ let value = "";
16043
+ index++;
16044
+ while (index < content.length) {
16045
+ const next = content[index];
16046
+ if (next === "\\") {
16047
+ value += next;
16048
+ if (index + 1 < content.length) value += content[index + 1];
16049
+ index += 2;
16050
+ continue;
16051
+ }
16052
+ if (next === quote) {
16053
+ index++;
16054
+ break;
16055
+ }
16056
+ if (next === "\n") line++;
16057
+ value += next;
16058
+ index++;
16059
+ }
16060
+ tokens.push({ kind: "string", value, line: tokenLine });
16061
+ continue;
16062
+ }
16063
+ if (character === "`") {
16064
+ index++;
16065
+ while (index < content.length) {
16066
+ const next = content[index];
16067
+ if (next === "\\") {
16068
+ index += 2;
16069
+ continue;
16070
+ }
16071
+ if (next === "`") {
16072
+ index++;
16073
+ break;
16074
+ }
16075
+ if (next === "\n") line++;
16076
+ index++;
16077
+ }
16078
+ continue;
16079
+ }
16080
+ if (/[A-Za-z_$]/.test(character)) {
16081
+ const start = index++;
16082
+ while (index < content.length && /[\w$]/.test(content[index])) index++;
16083
+ tokens.push({ kind: "identifier", value: content.slice(start, index), line });
16084
+ continue;
16085
+ }
16086
+ tokens.push({ kind: "punctuation", value: character, line });
16087
+ index++;
16088
+ }
16089
+ return tokens;
16090
+ }
16091
+ __name(tokenizeModuleSource, "tokenizeModuleSource");
16092
+ function isIslandExportStart(tokens, index) {
16093
+ const token = tokens[index];
16094
+ const previous = tokens[index - 1];
16095
+ const startsStatement = !previous || previous.value === ";" || previous.value === "{" || previous.value === "}" || token.line > previous.line;
16096
+ return startsStatement && token.value === "export" && tokens[index + 1]?.value === "const" && tokens[index + 2]?.value === "island";
16097
+ }
16098
+ __name(isIslandExportStart, "isIslandExportStart");
16099
+ function getIslandStrategyExport(content) {
16100
+ if (!content) return null;
16101
+ const tokens = tokenizeModuleSource(content);
16102
+ for (let index = 0; index < tokens.length - 2; index++) {
16103
+ if (!isIslandExportStart(tokens, index)) continue;
16104
+ let valueIndex = index + 3;
16105
+ if (tokens[valueIndex]?.value === ":") {
16106
+ while (valueIndex < tokens.length && tokens[valueIndex].value !== "=") valueIndex++;
16107
+ }
16108
+ if (tokens[valueIndex]?.value !== "=") break;
16109
+ valueIndex++;
16110
+ const literal = tokens[valueIndex];
16111
+ if (!literal || literal.kind !== "string" || !isFarmIslandStrategy(literal.value)) break;
16112
+ let trailingIndex = valueIndex + 1;
16113
+ if (tokens[trailingIndex]?.value === "as" && tokens[trailingIndex + 1]?.value === "const") {
16114
+ trailingIndex += 2;
16115
+ }
16116
+ const trailing = tokens[trailingIndex];
16117
+ if (trailing && trailing.value !== ";" && trailing.line === literal.line) break;
16118
+ return literal.value;
16119
+ }
16120
+ if (tokens.some((_token, index) => isIslandExportStart(tokens, index))) {
16121
+ throw new Error(
16122
+ 'Farm island configuration must be a static "load", "interaction", "visible", or "idle" string literal.'
16123
+ );
16124
+ }
16125
+ return null;
16126
+ }
16127
+ __name(getIslandStrategyExport, "getIslandStrategyExport");
15214
16128
  function stripUseClientDirective(content) {
15215
16129
  return content.replace(/^\s*(["'])use client\1\s*;?\s*/, "");
15216
16130
  }
15217
16131
  __name(stripUseClientDirective, "stripUseClientDirective");
15218
- function parseClientModuleMetadata(content) {
16132
+ function parseClientModuleMetadata(content, inspectIslandExport) {
15219
16133
  if (!content) {
15220
16134
  return {
15221
16135
  isClientComponent: false,
15222
- hasHydrateExport: false
16136
+ hasHydrateExport: false,
16137
+ islandStrategy: null
15223
16138
  };
15224
16139
  }
16140
+ const isClientComponent = hasUseClientDirective(content);
16141
+ const hasHydrate = hasHydrateExport(content);
15225
16142
  return {
15226
- isClientComponent: hasUseClientDirective(content),
15227
- hasHydrateExport: hasHydrateExport(content)
16143
+ isClientComponent,
16144
+ hasHydrateExport: hasHydrate,
16145
+ islandStrategy: inspectIslandExport || isClientComponent || hasHydrate ? getIslandStrategyExport(content) : null
15228
16146
  };
15229
16147
  }
15230
16148
  __name(parseClientModuleMetadata, "parseClientModuleMetadata");
15231
16149
  function getClientModuleMetadata(modulePath, root) {
15232
16150
  const resolvedPath = resolveModuleSourcePath(modulePath, root);
15233
- return inspectClientModuleMetadata(resolvedPath, root, /* @__PURE__ */ new Set());
16151
+ return inspectClientModuleMetadata(resolvedPath, root, /* @__PURE__ */ new Set(), true);
15234
16152
  }
15235
16153
  __name(getClientModuleMetadata, "getClientModuleMetadata");
15236
- function inspectClientModuleMetadata(resolvedPath, root, visited) {
16154
+ function inspectClientModuleMetadata(resolvedPath, root, visited, inspectIslandExport) {
15237
16155
  if (!resolvedPath || visited.has(resolvedPath)) {
15238
16156
  return {
15239
16157
  isClientComponent: false,
15240
- shouldHydrate: false
16158
+ shouldHydrate: false,
16159
+ islandStrategy: null
15241
16160
  };
15242
16161
  }
15243
16162
  visited.add(resolvedPath);
15244
16163
  const content = readIfExists(resolvedPath);
15245
- const parsed = parseClientModuleMetadata(content);
16164
+ const parsed = parseClientModuleMetadata(content, inspectIslandExport);
15246
16165
  if (parsed.isClientComponent) {
15247
16166
  return {
15248
16167
  isClientComponent: true,
15249
- shouldHydrate: true
16168
+ shouldHydrate: true,
16169
+ islandStrategy: parsed.islandStrategy ?? "load"
15250
16170
  };
15251
16171
  }
15252
16172
  let importsClientBoundary = false;
16173
+ let importedIslandStrategy = null;
15253
16174
  for (const specifier of getRelativeImportSpecifiers(content)) {
15254
16175
  const importedPath = resolveImportedModuleSourcePath(resolvedPath, specifier, root);
15255
- const importedMetadata = inspectClientModuleMetadata(importedPath, root, visited);
16176
+ const importedMetadata = inspectClientModuleMetadata(importedPath, root, visited, false);
15256
16177
  if (importedMetadata.isClientComponent || importedMetadata.shouldHydrate) {
15257
16178
  importsClientBoundary = true;
15258
- break;
16179
+ if (importedIslandStrategy === null) {
16180
+ importedIslandStrategy = importedMetadata.islandStrategy;
16181
+ } else if (importedIslandStrategy !== importedMetadata.islandStrategy) {
16182
+ importedIslandStrategy = "load";
16183
+ }
15259
16184
  }
15260
16185
  }
16186
+ const shouldHydrate = parsed.hasHydrateExport || importsClientBoundary;
15261
16187
  return {
15262
16188
  isClientComponent: false,
15263
- shouldHydrate: parsed.hasHydrateExport || importsClientBoundary
16189
+ shouldHydrate,
16190
+ islandStrategy: shouldHydrate ? parsed.islandStrategy ?? importedIslandStrategy ?? "load" : null
15264
16191
  };
15265
16192
  }
15266
16193
  __name(inspectClientModuleMetadata, "inspectClientModuleMetadata");
@@ -15516,6 +16443,10 @@ function parseInterceptionMarker(segment) {
15516
16443
  __name(parseInterceptionMarker, "parseInterceptionMarker");
15517
16444
 
15518
16445
  // src/routing/route-manager.ts
16446
+ function shouldSuggestStaticRenderingForI18n(i18n) {
16447
+ return !i18n || i18n.routing === "prefix-always" || i18n.routing === "prefix-except-default" && i18n.detection.every((signal) => signal === "url");
16448
+ }
16449
+ __name(shouldSuggestStaticRenderingForI18n, "shouldSuggestStaticRenderingForI18n");
15519
16450
  function routeSpecificity(entry) {
15520
16451
  return entry.route.segments.reduce((score, segment) => {
15521
16452
  if (!segment.isDynamic) return score + 100;
@@ -15759,6 +16690,7 @@ var _RouteManager = class _RouteManager {
15759
16690
  modulePath: toUrlPath(entry.modulePath),
15760
16691
  shouldHydrate: metadata.shouldHydrate,
15761
16692
  isClientComponent: metadata.isClientComponent,
16693
+ islandStrategy: metadata.islandStrategy,
15762
16694
  search: getProgrammaticRouteSearchClientOptions(programmaticPage?.search),
15763
16695
  segments: entry.route.segments.map((seg) => ({
15764
16696
  segment: seg.segment,
@@ -16221,8 +17153,10 @@ var _RouteManager = class _RouteManager {
16221
17153
  isDynamic: entry.route.segments.some((seg) => seg.isDynamic),
16222
17154
  pattern: entry.pattern
16223
17155
  }));
16224
- const result = await collectSSGPages(routes, (filePath) => this.loadRouteModule(filePath));
16225
17156
  const i18n = this.getI18nConfig();
17157
+ const result = await collectSSGPages(routes, (filePath) => this.loadRouteModule(filePath), {
17158
+ suggestStaticRendering: shouldSuggestStaticRenderingForI18n(i18n)
17159
+ });
16226
17160
  if (!i18n) {
16227
17161
  return result;
16228
17162
  }
@@ -16353,8 +17287,8 @@ async function safeGlobFiles(pattern, cwd) {
16353
17287
  __name(safeGlobFiles, "safeGlobFiles");
16354
17288
 
16355
17289
  // src/server/renderer.ts
16356
- var import_react4 = __toESM(require("react"));
16357
- var import_server = require("react-dom/server");
17290
+ var import_react5 = __toESM(require("react"));
17291
+ var import_server2 = require("react-dom/server");
16358
17292
  var fs2 = __toESM(require("fs"));
16359
17293
  var path9 = __toESM(require("path"));
16360
17294
  var import_stream = require("stream");
@@ -17827,6 +18761,10 @@ ${FONT_SECTION_END}
17827
18761
  fallback,
17828
18762
  display,
17829
18763
  preload: raw.preload !== false,
18764
+ preloadResources: raw.preload === false ? [] : normalizedSources.map(({ resource }) => ({
18765
+ href: resourceUrl(resource, production, this.basePath),
18766
+ type: fontContentType(resource.extension)
18767
+ })),
17830
18768
  weight: defaultWeight,
17831
18769
  style: raw.style === void 0 ? void 0 : defaultStyle,
17832
18770
  sources: normalizedSources
@@ -17923,7 +18861,7 @@ ${FONT_SECTION_END}
17923
18861
  const normalizedExtension = extension.toLowerCase();
17924
18862
  const hash = (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex").slice(0, 12);
17925
18863
  const baseName = sanitizeFileName(import_node_path4.default.basename(fileName, extension)) || "font";
17926
- const outputFileName = `assets/fonts/${baseName}-${hash}${normalizedExtension}`;
18864
+ const outputFileName = `assets/fonts/${baseName}-h${hash}${normalizedExtension}`;
17927
18865
  const key = `${hash}${normalizedExtension}`;
17928
18866
  const existing = this.resources.get(key);
17929
18867
  if (existing) return existing;
@@ -18193,7 +19131,8 @@ function serializeFontResult(font) {
18193
19131
  fontFamily: stack,
18194
19132
  ...font.style ? { fontStyle: font.style } : {},
18195
19133
  ...font.weight !== void 0 && !String(font.weight).includes(" ") ? { fontWeight: font.weight } : {}
18196
- }
19134
+ },
19135
+ preloads: font.preloadResources
18197
19136
  });
18198
19137
  }
18199
19138
  __name(serializeFontResult, "serializeFontResult");
@@ -18536,6 +19475,166 @@ function fontError(id, message) {
18536
19475
  }
18537
19476
  __name(fontError, "fontError");
18538
19477
 
19478
+ // src/metadata-image.ts
19479
+ var import_react4 = __toESM(require("react"));
19480
+ var import_server = require("react-dom/server");
19481
+ var REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref");
19482
+ var REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo");
19483
+ var REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
19484
+ function isResponse(value) {
19485
+ return typeof Response !== "undefined" && (value instanceof Response || Boolean(
19486
+ value && typeof value === "object" && typeof value.arrayBuffer === "function" && value.headers
19487
+ ));
19488
+ }
19489
+ __name(isResponse, "isResponse");
19490
+ function isThenable(value) {
19491
+ return Boolean(
19492
+ value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function"
19493
+ );
19494
+ }
19495
+ __name(isThenable, "isThenable");
19496
+ async function prepareMetadataImageNode(node) {
19497
+ if (isThenable(node)) {
19498
+ return prepareMetadataImageNode(await node);
19499
+ }
19500
+ if (Array.isArray(node)) {
19501
+ return Promise.all(node.map((child) => prepareMetadataImageNode(child)));
19502
+ }
19503
+ if (!import_react4.default.isValidElement(node)) {
19504
+ return node;
19505
+ }
19506
+ const element = node;
19507
+ const type = element.type;
19508
+ const props = element.props || {};
19509
+ if (typeof type === "function") {
19510
+ if (type.prototype?.isReactComponent) {
19511
+ throw new Error(
19512
+ "Metadata image components must be stateless function components; React class components are not supported."
19513
+ );
19514
+ }
19515
+ return prepareMetadataImageNode(await type(props));
19516
+ }
19517
+ if (type && typeof type === "object") {
19518
+ if (type.$$typeof === REACT_MEMO_TYPE) {
19519
+ return prepareMetadataImageNode(import_react4.default.createElement(type.type, props));
19520
+ }
19521
+ if (type.$$typeof === REACT_FORWARD_REF_TYPE) {
19522
+ return prepareMetadataImageNode(await type.render(props, null));
19523
+ }
19524
+ if (type.$$typeof === REACT_LAZY_TYPE) {
19525
+ return prepareMetadataImageNode(import_react4.default.createElement(type._init(type._payload), props));
19526
+ }
19527
+ }
19528
+ const preparedChildren = await prepareMetadataImageNode(props.children);
19529
+ const preparedProps = { ...props };
19530
+ if (typeof preparedProps.className === "string") {
19531
+ preparedProps.tw = [preparedProps.tw, preparedProps.className].filter(Boolean).join(" ");
19532
+ delete preparedProps.className;
19533
+ }
19534
+ delete preparedProps.children;
19535
+ return import_react4.default.createElement(type, {
19536
+ ...preparedProps,
19537
+ key: element.key,
19538
+ children: preparedChildren
19539
+ });
19540
+ }
19541
+ __name(prepareMetadataImageNode, "prepareMetadataImageNode");
19542
+ async function normalizeFonts(fonts) {
19543
+ if (!fonts?.length) return void 0;
19544
+ return Promise.all(
19545
+ fonts.map(async (font) => {
19546
+ const data = await font.data;
19547
+ const arrayBuffer = ArrayBuffer.isView(data) ? toArrayBuffer(data) : data;
19548
+ return { ...font, data: arrayBuffer };
19549
+ })
19550
+ );
19551
+ }
19552
+ __name(normalizeFonts, "normalizeFonts");
19553
+ function resolveCacheControl(revalidate) {
19554
+ if (revalidate === false) {
19555
+ return "public, max-age=31536000, immutable";
19556
+ }
19557
+ if (typeof revalidate === "number" && Number.isFinite(revalidate) && revalidate > 0) {
19558
+ return `public, s-maxage=${Math.floor(revalidate)}, stale-while-revalidate=300`;
19559
+ }
19560
+ return "public, max-age=0, must-revalidate";
19561
+ }
19562
+ __name(resolveCacheControl, "resolveCacheControl");
19563
+ function toArrayBuffer(value) {
19564
+ if (value instanceof ArrayBuffer) return value;
19565
+ const bytes = new Uint8Array(value.byteLength);
19566
+ bytes.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
19567
+ return bytes.buffer;
19568
+ }
19569
+ __name(toArrayBuffer, "toArrayBuffer");
19570
+ async function createEntityTag(body) {
19571
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", body);
19572
+ const hash = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 32);
19573
+ return `"${hash}"`;
19574
+ }
19575
+ __name(createEntityTag, "createEntityTag");
19576
+ async function finalizeMetadataImageResponse(body, headers, options) {
19577
+ const responseHeaders = new Headers(headers);
19578
+ const etag = await createEntityTag(body);
19579
+ responseHeaders.set("ETag", etag);
19580
+ responseHeaders.set("Content-Length", String(body.byteLength));
19581
+ responseHeaders.set("X-Content-Type-Options", "nosniff");
19582
+ const matchesEntityTag = options.ifNoneMatch?.trim() === "*" || options.ifNoneMatch?.split(",").some((candidate) => candidate.trim().replace(/^W\//, "") === etag);
19583
+ if (matchesEntityTag) {
19584
+ return new Response(null, { status: 304, headers: responseHeaders });
19585
+ }
19586
+ return new Response(options.method?.toUpperCase() === "HEAD" ? null : body, {
19587
+ status: 200,
19588
+ headers: responseHeaders
19589
+ });
19590
+ }
19591
+ __name(finalizeMetadataImageResponse, "finalizeMetadataImageResponse");
19592
+ async function createFarmMetadataImageResponse(value, imageModule, options = {}) {
19593
+ const method = (options.method || "GET").toUpperCase();
19594
+ if (method !== "GET" && method !== "HEAD") {
19595
+ return new Response(null, { status: 405, headers: { Allow: "GET, HEAD" } });
19596
+ }
19597
+ if (isResponse(value)) {
19598
+ return method === "HEAD" ? new Response(null, { status: value.status, headers: value.headers }) : value;
19599
+ }
19600
+ const cacheControl = resolveCacheControl(imageModule.revalidate);
19601
+ const explicitContentType = imageModule.contentType?.split(";", 1)[0]?.trim().toLowerCase();
19602
+ if (import_react4.default.isValidElement(value) && explicitContentType !== "image/svg+xml") {
19603
+ const { ImageResponse } = await import("@vercel/og");
19604
+ const element = await prepareMetadataImageNode(value);
19605
+ const response = new ImageResponse(element, {
19606
+ width: imageModule.size?.width || 1200,
19607
+ height: imageModule.size?.height || 630,
19608
+ fonts: await normalizeFonts(imageModule.fonts),
19609
+ emoji: imageModule.emoji,
19610
+ debug: imageModule.debug,
19611
+ headers: { "cache-control": cacheControl }
19612
+ });
19613
+ const headers = new Headers(response.headers);
19614
+ headers.set("Cache-Control", cacheControl);
19615
+ return finalizeMetadataImageResponse(await response.arrayBuffer(), headers, options);
19616
+ }
19617
+ let body;
19618
+ if (typeof value === "string") {
19619
+ body = toArrayBuffer(new TextEncoder().encode(value));
19620
+ } else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
19621
+ body = toArrayBuffer(value);
19622
+ } else if (import_react4.default.isValidElement(value)) {
19623
+ body = toArrayBuffer(new TextEncoder().encode((0, import_server.renderToStaticMarkup)(value)));
19624
+ } else {
19625
+ throw new Error("Metadata image must return a Response, string, bytes, or React element");
19626
+ }
19627
+ return finalizeMetadataImageResponse(
19628
+ body,
19629
+ {
19630
+ "Content-Type": imageModule.contentType || "image/svg+xml; charset=utf-8",
19631
+ "Cache-Control": cacheControl
19632
+ },
19633
+ options
19634
+ );
19635
+ }
19636
+ __name(createFarmMetadataImageResponse, "createFarmMetadataImageResponse");
19637
+
18539
19638
  // src/server/renderer.ts
18540
19639
  var cachedClerkProvider = null;
18541
19640
  var importRuntimeModule = new Function("specifier", "return import(specifier);");
@@ -18609,7 +19708,7 @@ function createPPRRefreshScript() {
18609
19708
  }
18610
19709
  __name(createPPRRefreshScript, "createPPRRefreshScript");
18611
19710
  function createPreHydrationClickQueueScript() {
18612
- 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>`;
19711
+ 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>`;
18613
19712
  }
18614
19713
  __name(createPreHydrationClickQueueScript, "createPreHydrationClickQueueScript");
18615
19714
  function searchParamsToObject(searchParams) {
@@ -18660,7 +19759,7 @@ function isWebResponse(value) {
18660
19759
  return typeof Response !== "undefined" && value instanceof Response && typeof value.arrayBuffer === "function";
18661
19760
  }
18662
19761
  __name(isWebResponse, "isWebResponse");
18663
- var _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react4.default.Component {
19762
+ var _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react5.default.Component {
18664
19763
  constructor(props) {
18665
19764
  super(props);
18666
19765
  this.state = { hasError: false, error: null };
@@ -18671,7 +19770,7 @@ var _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react
18671
19770
  render() {
18672
19771
  if (this.state.hasError) {
18673
19772
  const Fallback = this.props.Fallback;
18674
- return import_react4.default.createElement(Fallback, {
19773
+ return import_react5.default.createElement(Fallback, {
18675
19774
  ...this.props.fallbackProps,
18676
19775
  error: this.state.error,
18677
19776
  reset: /* @__PURE__ */ __name(() => this.setState({ hasError: false, error: null }), "reset")
@@ -18887,14 +19986,14 @@ var _ServerRenderer = class _ServerRenderer {
18887
19986
  searchParams: Promise.resolve({}),
18888
19987
  path: page2.urlPath
18889
19988
  };
18890
- let pageElement = import_react4.default.createElement(
19989
+ let pageElement = import_react5.default.createElement(
18891
19990
  PageComponent,
18892
19991
  pageProps
18893
19992
  );
18894
19993
  for (let i = layoutModules.length - 1; i >= 0; i--) {
18895
19994
  const layoutModule = layoutModules[i];
18896
19995
  const LayoutComponent = layoutModule.default;
18897
- pageElement = import_react4.default.createElement(
19996
+ pageElement = import_react5.default.createElement(
18898
19997
  LayoutComponent,
18899
19998
  {
18900
19999
  children: pageElement,
@@ -19220,6 +20319,7 @@ var _ServerRenderer = class _ServerRenderer {
19220
20319
  req.__FARM_ROUTE__ = pathname;
19221
20320
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
19222
20321
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
20322
+ req.__FARM_ISLAND_STRATEGY__ = moduleMetadata.islandStrategy;
19223
20323
  req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
19224
20324
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
19225
20325
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
@@ -19272,12 +20372,12 @@ var _ServerRenderer = class _ServerRenderer {
19272
20372
  await _runWithMiddlewareData(middlewareDataForContext, async () => {
19273
20373
  await _runWithMiddlewareContext(middlewareContext, async () => {
19274
20374
  await _runWithCurrentRequest(currentRequest, async () => {
19275
- let pageElement = import_react4.default.createElement(
20375
+ let pageElement = import_react5.default.createElement(
19276
20376
  PageComponent,
19277
20377
  pageProps
19278
20378
  );
19279
20379
  if (LoadingFallbackComponent) {
19280
- const loadingFallback = import_react4.default.createElement(LoadingFallbackComponent, {
20380
+ const loadingFallback = import_react5.default.createElement(LoadingFallbackComponent, {
19281
20381
  ...createRouteStateProps({
19282
20382
  params,
19283
20383
  searchParamsObject,
@@ -19286,16 +20386,21 @@ var _ServerRenderer = class _ServerRenderer {
19286
20386
  pluginExposedContext
19287
20387
  })
19288
20388
  });
19289
- pageElement = import_react4.default.createElement(
19290
- import_react4.default.Suspense,
20389
+ pageElement = import_react5.default.createElement(
20390
+ import_react5.default.Suspense,
19291
20391
  { fallback: loadingFallback },
19292
20392
  pageElement
19293
20393
  );
19294
20394
  }
19295
20395
  if (isClientComponent || shouldHydrate) {
19296
- pageElement = import_react4.default.createElement(
20396
+ pageElement = import_react5.default.createElement(
19297
20397
  "div",
19298
- { id: "__farm_page__", "data-farm-client": "true" },
20398
+ {
20399
+ id: "__farm_page__",
20400
+ "data-farm-client": "true",
20401
+ "data-farm-island": "page",
20402
+ "data-farm-island-strategy": moduleMetadata.islandStrategy ?? "load"
20403
+ },
19299
20404
  pageElement
19300
20405
  );
19301
20406
  }
@@ -19307,11 +20412,11 @@ var _ServerRenderer = class _ServerRenderer {
19307
20412
  const slotProps = {};
19308
20413
  for (const slot of renderedRouteSlots) {
19309
20414
  if (slot.ownerPattern !== layoutEntry.pattern) continue;
19310
- let slotElement = import_react4.default.createElement(
20415
+ let slotElement = import_react5.default.createElement(
19311
20416
  slot.module.default,
19312
20417
  slot.props
19313
20418
  );
19314
- slotElement = import_react4.default.createElement(
20419
+ slotElement = import_react5.default.createElement(
19315
20420
  "div",
19316
20421
  {
19317
20422
  id: slot.containerId,
@@ -19322,7 +20427,7 @@ var _ServerRenderer = class _ServerRenderer {
19322
20427
  );
19323
20428
  slotProps[slot.name] = slotElement;
19324
20429
  }
19325
- wrappedElement = import_react4.default.createElement(
20430
+ wrappedElement = import_react5.default.createElement(
19326
20431
  LayoutComponent,
19327
20432
  {
19328
20433
  children: wrappedElement,
@@ -19332,7 +20437,7 @@ var _ServerRenderer = class _ServerRenderer {
19332
20437
  );
19333
20438
  }
19334
20439
  if (ErrorFallbackComponent) {
19335
- wrappedElement = import_react4.default.createElement(
20440
+ wrappedElement = import_react5.default.createElement(
19336
20441
  FarmRouteErrorBoundary,
19337
20442
  {
19338
20443
  Fallback: ErrorFallbackComponent,
@@ -19465,7 +20570,7 @@ var _ServerRenderer = class _ServerRenderer {
19465
20570
  if (!cachedClerkProvider) {
19466
20571
  cachedClerkProvider = await importRuntimeModule("@clerk/react");
19467
20572
  }
19468
- wrapped = import_react4.default.createElement(
20573
+ wrapped = import_react5.default.createElement(
19469
20574
  cachedClerkProvider.ClerkProvider,
19470
20575
  provider.props || {},
19471
20576
  wrapped
@@ -19540,6 +20645,13 @@ var _ServerRenderer = class _ServerRenderer {
19540
20645
  return reference;
19541
20646
  }
19542
20647
  async renderMetadataImage(req, res, options) {
20648
+ const method = (req.method || "GET").toUpperCase();
20649
+ if (method !== "GET" && method !== "HEAD") {
20650
+ res.statusCode = 405;
20651
+ res.setHeader("Allow", "GET, HEAD");
20652
+ res.end();
20653
+ return;
20654
+ }
19543
20655
  if (options.image.sourceType === "static") {
19544
20656
  if (!options.image.staticInfo) {
19545
20657
  throw new Error(`Static metadata image ${options.image.modulePath} is missing file info`);
@@ -19565,42 +20677,12 @@ var _ServerRenderer = class _ServerRenderer {
19565
20677
  await this.writeMetadataImageResponse(req, res, handlerResult, imageModule);
19566
20678
  }
19567
20679
  async writeMetadataImageResponse(req, res, value, imageModule) {
19568
- if (isWebResponse(value)) {
19569
- res.statusCode = value.status;
19570
- value.headers.forEach((headerValue, key) => {
19571
- res.setHeader(key, headerValue);
19572
- });
19573
- if (req.method === "HEAD") {
19574
- res.end();
19575
- return;
19576
- }
19577
- const body2 = Buffer.from(await value.arrayBuffer());
19578
- res.write(body2);
19579
- res.end();
19580
- return;
19581
- }
19582
- const contentType = imageModule.contentType || "image/svg+xml; charset=utf-8";
19583
- let body;
19584
- if (typeof value === "string") {
19585
- body = value;
19586
- } else if (value instanceof ArrayBuffer) {
19587
- body = Buffer.from(value);
19588
- } else if (ArrayBuffer.isView(value)) {
19589
- body = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
19590
- } else if (import_react4.default.isValidElement(value)) {
19591
- body = (0, import_server.renderToStaticMarkup)(value);
19592
- } else {
19593
- throw new Error("Metadata image must return a Response, string, bytes, or React element");
19594
- }
19595
- res.statusCode = res.statusCode || 200;
19596
- res.setHeader("Content-Type", contentType);
19597
- res.setHeader("Cache-Control", "public, max-age=0, must-revalidate");
19598
- if (req.method === "HEAD") {
19599
- res.end();
19600
- return;
19601
- }
19602
- res.write(body);
19603
- res.end();
20680
+ const ifNoneMatch = req.headers["if-none-match"];
20681
+ const response = await createFarmMetadataImageResponse(value, imageModule, {
20682
+ method: req.method,
20683
+ ifNoneMatch: Array.isArray(ifNoneMatch) ? ifNoneMatch[0] : ifNoneMatch
20684
+ });
20685
+ await sendWebResponse(res, response);
19604
20686
  }
19605
20687
  async writeStaticMetadataImageResponse(req, res, image) {
19606
20688
  const method = (req.method || "GET").toUpperCase();
@@ -19647,7 +20729,7 @@ var _ServerRenderer = class _ServerRenderer {
19647
20729
  return false;
19648
20730
  }
19649
20731
  const ErrorComponent = errorModule.default;
19650
- const errorElement = import_react4.default.createElement(ErrorComponent, {
20732
+ const errorElement = import_react5.default.createElement(ErrorComponent, {
19651
20733
  ...createRouteStateProps({
19652
20734
  params: options.params,
19653
20735
  searchParamsObject: options.searchParamsObject,
@@ -19665,7 +20747,7 @@ var _ServerRenderer = class _ServerRenderer {
19665
20747
  );
19666
20748
  for (let i = layoutModules.length - 1; i >= 0; i--) {
19667
20749
  const LayoutComponent = layoutModules[i].default;
19668
- wrapped = import_react4.default.createElement(
20750
+ wrapped = import_react5.default.createElement(
19669
20751
  LayoutComponent,
19670
20752
  {
19671
20753
  children: wrapped,
@@ -19730,6 +20812,7 @@ var _ServerRenderer = class _ServerRenderer {
19730
20812
  search: routeEntry.search,
19731
20813
  isClientComponent: routeEntry.isClientComponent,
19732
20814
  shouldHydrate: routeEntry.shouldHydrate,
20815
+ islandStrategy: routeEntry.islandStrategy,
19733
20816
  preloads: [routeEntry.modulePath],
19734
20817
  assets: []
19735
20818
  };
@@ -19766,6 +20849,7 @@ window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
19766
20849
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
19767
20850
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
19768
20851
  window.__FARM_SHOULD_HYDRATE__ = ${JSON.stringify(req.__FARM_SHOULD_HYDRATE__ === true)};
20852
+ window.__FARM_ISLAND_STRATEGY__ = ${JSON.stringify(req.__FARM_ISLAND_STRATEGY__ || "load")};
19769
20853
  window.__FARM_PAGE_MODULE__ = ${JSON.stringify(relativePath2)};
19770
20854
  window.__FARM_LOADING_MODULE__ = ${JSON.stringify(
19771
20855
  req.__FARM_LOADING_MODULE_PATH__ || null
@@ -19783,8 +20867,8 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
19783
20867
  const i18nSnapshot = getFarmI18nClientSnapshot();
19784
20868
  const i18nAlternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
19785
20869
  const fontHead = renderFarmFontDevHead(this.config.root || process.cwd());
19786
- const streamRoot = import_react4.default.createElement("div", { style: { display: "contents" } }, element);
19787
- const { pipe, abort } = (0, import_server.renderToPipeableStream)(streamRoot, {
20870
+ const streamRoot = import_react5.default.createElement("div", { style: { display: "contents" } }, element);
20871
+ const { pipe, abort } = (0, import_server2.renderToPipeableStream)(streamRoot, {
19788
20872
  onShellReady() {
19789
20873
  const shellReadyMs = Date.now() - streamStartTime;
19790
20874
  emitFarmEvent({
@@ -19942,12 +21026,12 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
19942
21026
  break;
19943
21027
  }
19944
21028
  }
19945
- let element = import_react4.default.createElement(
21029
+ let element = import_react5.default.createElement(
19946
21030
  NotFoundComponent,
19947
21031
  { pathname }
19948
21032
  );
19949
21033
  if (LayoutComponent) {
19950
- element = import_react4.default.createElement(
21034
+ element = import_react5.default.createElement(
19951
21035
  LayoutComponent,
19952
21036
  { children: element }
19953
21037
  );
@@ -20061,6 +21145,7 @@ var import_path6 = __toESM(require("path"));
20061
21145
  init_devtools_config();
20062
21146
  init_config();
20063
21147
  init_auth_config();
21148
+ init_preload();
20064
21149
  var defaultDocsConfig = {
20065
21150
  enabled: false,
20066
21151
  entry: "/docs",
@@ -20129,6 +21214,7 @@ var _FarmApp = class _FarmApp {
20129
21214
  context: config.context || (() => void 0),
20130
21215
  serverActions: resolveServerActionsConfig(config.serverActions),
20131
21216
  images: resolveFarmImageConfig(config.images),
21217
+ performance: resolveFarmPerformanceConfig(config.performance),
20132
21218
  i18n: isResolvedI18nConfig(config.i18n) ? config.i18n : resolveFarmI18nConfig(config.i18n, {
20133
21219
  root,
20134
21220
  mode: process.env.NODE_ENV === "production" ? "production" : "development"
@@ -22365,7 +23451,7 @@ init_response();
22365
23451
  init_integrations();
22366
23452
  init_devtools_config();
22367
23453
  var fs7 = __toESM(require("fs"));
22368
- var path23 = __toESM(require("path"));
23454
+ var path25 = __toESM(require("path"));
22369
23455
  init_layers();
22370
23456
 
22371
23457
  // src/environment-vite.ts
@@ -22712,7 +23798,7 @@ function transformError(id, message) {
22712
23798
  __name(transformError, "transformError");
22713
23799
 
22714
23800
  // package.json
22715
- var version = "0.1.0-beta.11";
23801
+ var version = "0.1.0-beta.13";
22716
23802
 
22717
23803
  // src/version.ts
22718
23804
  var FARM_VERSION = version;
@@ -22982,8 +24068,116 @@ async function createBlurDataURL(bytes) {
22982
24068
  }
22983
24069
  __name(createBlurDataURL, "createBlurDataURL");
22984
24070
 
24071
+ // src/font.ts
24072
+ function resolveFarmLayoutFonts(layouts) {
24073
+ let resolved;
24074
+ for (const layout2 of layouts) {
24075
+ if (!layout2.fonts) continue;
24076
+ resolved = { ...resolved, ...layout2.fonts };
24077
+ }
24078
+ return resolved;
24079
+ }
24080
+ __name(resolveFarmLayoutFonts, "resolveFarmLayoutFonts");
24081
+
22985
24082
  // src/vite.ts
22986
24083
  init_image_server();
24084
+
24085
+ // src/server/vite-config.ts
24086
+ var import_node_path9 = __toESM(require("path"));
24087
+ var FARM_CLIENT_ENTRY_PATTERN = "**/{page,layout,loading,error,not-found,default}.{js,jsx,ts,tsx}";
24088
+ var FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE = [
24089
+ "react",
24090
+ "react-dom",
24091
+ "react-dom/client",
24092
+ "react/jsx-runtime",
24093
+ "react/jsx-dev-runtime",
24094
+ "@farm.js/core/client",
24095
+ "@farm.js/core/plugin/client",
24096
+ "@farm.js/core/deferred",
24097
+ "@farm.js/core/deployment",
24098
+ "@farm.js/core/i18n/client",
24099
+ "@farm.js/core/query/client",
24100
+ "@farm.js/core/server-fn/client",
24101
+ "@farm.js/core/server-query/client"
24102
+ ];
24103
+ function createFarmClientOptimizeDepsEntries(projectRoot, appDirectories) {
24104
+ const resolvedProjectRoot = import_node_path9.default.resolve(projectRoot);
24105
+ return appDirectories.map((appDirectory) => {
24106
+ const relativeAppDirectory = import_node_path9.default.relative(resolvedProjectRoot, import_node_path9.default.resolve(appDirectory)).replace(/\\/g, "/");
24107
+ const prefix = relativeAppDirectory || ".";
24108
+ return `${prefix}/${FARM_CLIENT_ENTRY_PATTERN}`;
24109
+ });
24110
+ }
24111
+ __name(createFarmClientOptimizeDepsEntries, "createFarmClientOptimizeDepsEntries");
24112
+ function createFarmClientOptimizeDepsConfig(entries) {
24113
+ return {
24114
+ // Keep normal application dependency discovery for CJS-only packages, but
24115
+ // seed every Farm browser runtime so React is present in the first crawl.
24116
+ noDiscovery: false,
24117
+ // Waiting for the crawl avoids serving a partial optimizer generation and
24118
+ // then changing browser hashes while hydration is already in progress.
24119
+ holdUntilCrawlEnd: true,
24120
+ include: [...FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE],
24121
+ // Farm's HTML boots through a virtual module and imports route UI lazily.
24122
+ // Explicit entries let Vite finish that crawl before a navigation can
24123
+ // rotate the optimizer hash underneath a hydrated React runtime.
24124
+ ...entries?.length ? { entries: [...entries] } : {}
24125
+ };
24126
+ }
24127
+ __name(createFarmClientOptimizeDepsConfig, "createFarmClientOptimizeDepsConfig");
24128
+ function mergeNoExternal(farmValue, userValue) {
24129
+ if (farmValue === true || userValue === true) return true;
24130
+ const normalize = /* @__PURE__ */ __name((value) => value == null || value === true ? [] : Array.isArray(value) ? value : [value], "normalize");
24131
+ return Array.from(/* @__PURE__ */ new Set([...normalize(farmValue), ...normalize(userValue)]));
24132
+ }
24133
+ __name(mergeNoExternal, "mergeNoExternal");
24134
+ function mergeFarmViteConfig(farmConfig, userConfig = {}) {
24135
+ const noDiscovery = userConfig.optimizeDeps?.noDiscovery ?? farmConfig.optimizeDeps?.noDiscovery;
24136
+ const holdUntilCrawlEnd = userConfig.optimizeDeps?.holdUntilCrawlEnd ?? (userConfig.optimizeDeps?.noDiscovery === true ? false : farmConfig.optimizeDeps?.holdUntilCrawlEnd);
24137
+ return {
24138
+ ...farmConfig,
24139
+ ...userConfig,
24140
+ plugins: [...farmConfig.plugins || [], ...userConfig.plugins || []],
24141
+ server: {
24142
+ ...farmConfig.server,
24143
+ ...userConfig.server
24144
+ },
24145
+ resolve: {
24146
+ ...farmConfig.resolve,
24147
+ ...userConfig.resolve,
24148
+ dedupe: Array.from(
24149
+ /* @__PURE__ */ new Set([...farmConfig.resolve?.dedupe || [], ...userConfig.resolve?.dedupe || []])
24150
+ )
24151
+ },
24152
+ optimizeDeps: {
24153
+ ...farmConfig.optimizeDeps,
24154
+ ...userConfig.optimizeDeps,
24155
+ noDiscovery,
24156
+ holdUntilCrawlEnd,
24157
+ include: Array.from(
24158
+ /* @__PURE__ */ new Set([
24159
+ ...farmConfig.optimizeDeps?.include || [],
24160
+ ...userConfig.optimizeDeps?.include || []
24161
+ ])
24162
+ ),
24163
+ exclude: Array.from(
24164
+ /* @__PURE__ */ new Set([
24165
+ ...farmConfig.optimizeDeps?.exclude || [],
24166
+ ...userConfig.optimizeDeps?.exclude || []
24167
+ ])
24168
+ )
24169
+ },
24170
+ ssr: {
24171
+ ...farmConfig.ssr,
24172
+ ...userConfig.ssr,
24173
+ noExternal: mergeNoExternal(farmConfig.ssr?.noExternal, userConfig.ssr?.noExternal)
24174
+ }
24175
+ };
24176
+ }
24177
+ __name(mergeFarmViteConfig, "mergeFarmViteConfig");
24178
+
24179
+ // src/vite.ts
24180
+ init_fonts();
22987
24181
  var ALL_TYPE_ARTIFACTS = {
22988
24182
  routes: true,
22989
24183
  api: true,
@@ -23204,7 +24398,7 @@ function fileContainsProgrammaticPageRoute(file) {
23204
24398
  __name(fileContainsProgrammaticPageRoute, "fileContainsProgrammaticPageRoute");
23205
24399
  function rewriteEarlySsrRelativeImports(options) {
23206
24400
  const cleanId = options.id.split("?", 1)[0];
23207
- if (!path23.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
24401
+ if (!path25.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
23208
24402
  return null;
23209
24403
  }
23210
24404
  const replacements = [];
@@ -23224,7 +24418,7 @@ function rewriteEarlySsrRelativeImports(options) {
23224
24418
  const suffixIndex = source.value.search(/[?#]/);
23225
24419
  const sourcePath = suffixIndex === -1 ? source.value : source.value.slice(0, suffixIndex);
23226
24420
  const suffix = suffixIndex === -1 ? "" : source.value.slice(suffixIndex);
23227
- const resolvedPath = path23.resolve(path23.dirname(cleanId), sourcePath);
24421
+ const resolvedPath = path25.resolve(path25.dirname(cleanId), sourcePath);
23228
24422
  replacements.push({
23229
24423
  start: source.start,
23230
24424
  end: source.end,
@@ -23309,7 +24503,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23309
24503
  ...layerRoots.length ? {
23310
24504
  server: {
23311
24505
  fs: {
23312
- allow: [path23.resolve(options.root || process.cwd()), ...layerRoots]
24506
+ allow: [path25.resolve(options.root || process.cwd()), ...layerRoots]
23313
24507
  }
23314
24508
  }
23315
24509
  } : {}
@@ -23340,9 +24534,9 @@ function farmPlugin(options = {}, initialPluginManager) {
23340
24534
  },
23341
24535
  server
23342
24536
  );
23343
- const globalsCSSPath = path23.join(server.config.root, "src/app/globals.css");
24537
+ const globalsCSSPath = path25.join(server.config.root, "src/app/globals.css");
23344
24538
  if (!fs7.existsSync(globalsCSSPath)) {
23345
- const appDir = path23.join(server.config.root, "src/app");
24539
+ const appDir = path25.join(server.config.root, "src/app");
23346
24540
  if (!fs7.existsSync(appDir)) {
23347
24541
  fs7.mkdirSync(appDir, { recursive: true });
23348
24542
  }
@@ -23364,10 +24558,10 @@ function farmPlugin(options = {}, initialPluginManager) {
23364
24558
  });
23365
24559
  }
23366
24560
  const sourceRoots = getFarmSourceRoots(farmConfig);
23367
- server.watcher.add(sourceRoots.map((source) => path23.join(source.root, source.srcDir)));
24561
+ server.watcher.add(sourceRoots.map((source) => path25.join(source.root, source.srcDir)));
23368
24562
  if (farmConfig.i18n.enabled) {
23369
24563
  server.watcher.add(
23370
- farmConfig.i18n.messages.includes("{locale}") ? path23.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
24564
+ farmConfig.i18n.messages.includes("{locale}") ? path25.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
23371
24565
  );
23372
24566
  }
23373
24567
  const workflowConfig = farmConfig.workflows;
@@ -23377,7 +24571,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23377
24571
  ...farmDocsDevRuntime?.getFarmDocsRouteTypeEntries(farmConfig.docs) ?? []
23378
24572
  ], "getExtraRouteTypes");
23379
24573
  const appDirSlugs = sourceRoots.map(
23380
- (source) => path23.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
24574
+ (source) => path25.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
23381
24575
  );
23382
24576
  const generateTypeArtifacts = /* @__PURE__ */ __name(async (reason, selection = ALL_TYPE_ARTIFACTS, log = false) => {
23383
24577
  try {
@@ -23417,7 +24611,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23417
24611
  }, "generateTypeArtifacts");
23418
24612
  await generateTypeArtifacts("startup");
23419
24613
  const srcDirSlugs = sourceRoots.map(
23420
- (source) => path23.join(source.root, source.srcDir).replace(/\\/g, "/")
24614
+ (source) => path25.join(source.root, source.srcDir).replace(/\\/g, "/")
23421
24615
  );
23422
24616
  const layerConfigFiles = new Set(
23423
24617
  farmConfig.layers.map((layer) => layer.configFile?.replace(/\\/g, "/")).filter((file) => Boolean(file))
@@ -23535,7 +24729,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23535
24729
  await apiRouteManager.discoverRoutes();
23536
24730
  let discoveredWorkflows = [];
23537
24731
  const hasWorkflowDirectory = workflowConfig.enabled && workflowConfig.dirs.some(
23538
- (dir) => fs7.existsSync(path23.isAbsolute(dir) ? dir : path23.join(farmConfig.root, dir))
24732
+ (dir) => fs7.existsSync(path25.isAbsolute(dir) ? dir : path25.join(farmConfig.root, dir))
23539
24733
  );
23540
24734
  if (hasWorkflowDirectory) {
23541
24735
  const { createFarmWorkflowRequestHandler: createFarmWorkflowRequestHandler2, discoverFarmWorkflows: discoverFarmWorkflows2 } = await loadFarmWorkflowsDevRuntime();
@@ -23599,10 +24793,20 @@ function farmPlugin(options = {}, initialPluginManager) {
23599
24793
  logger.info(`Refreshed routes: ${reason}`);
23600
24794
  }
23601
24795
  }, "refreshRouteDiscovery");
24796
+ const farmDocsFontAssetList = farmDocsDevRuntime ? resolveFarmDocsFontAssets(farmConfig.root) : [];
23602
24797
  const farmDocsHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsHandler(farmConfig.docs, {
23603
24798
  root: farmConfig.root,
23604
24799
  srcDir: farmConfig.srcDir,
23605
- clientEntry: "/@farm/client.js"
24800
+ clientEntry: "/@farm/client.js",
24801
+ fontAssets: toFarmDocsPublicFontAssets(farmDocsFontAssetList),
24802
+ resolveLayoutFonts: /* @__PURE__ */ __name(async (pathname) => {
24803
+ const layouts = routeManager.matchRoute(pathname).layouts;
24804
+ const layoutModules = await Promise.all(
24805
+ layouts.map((layout2) => routeManager.loadLayoutModule(layout2.modulePath))
24806
+ );
24807
+ return resolveFarmLayoutFonts(layoutModules);
24808
+ }, "resolveLayoutFonts"),
24809
+ fontStylesheetHref: "/@farm/fonts.css"
23606
24810
  }) : null;
23607
24811
  const farmDocsAPIHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsAPIHandler({
23608
24812
  rootDir: farmConfig.root,
@@ -23610,22 +24814,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23610
24814
  docs: farmConfig.docs
23611
24815
  }) : null;
23612
24816
  const farmDocsFontAssets = new Map(
23613
- farmDocsDevRuntime ? [
23614
- [
23615
- "/assets/Geist-Variable-CrgPqtmy.woff2",
23616
- path23.join(
23617
- farmConfig.root,
23618
- "node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2"
23619
- )
23620
- ],
23621
- [
23622
- "/assets/GeistMono-Variable-BNLlm6Cd.woff2",
23623
- path23.join(
23624
- farmConfig.root,
23625
- "node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2"
23626
- )
23627
- ]
23628
- ] : []
24817
+ farmDocsFontAssetList.map(({ url, sourcePath }) => [url, sourcePath])
23629
24818
  );
23630
24819
  const logRequest = /* @__PURE__ */ __name((method, urlPath, tag) => {
23631
24820
  try {
@@ -23689,11 +24878,12 @@ function farmPlugin(options = {}, initialPluginManager) {
23689
24878
  }
23690
24879
  }
23691
24880
  const farmDocsFontPath = farmDocsFontAssets.get(requestPathname);
23692
- if (farmDocsFontPath && fs7.existsSync(farmDocsFontPath)) {
24881
+ if ((requestMethod === "GET" || requestMethod === "HEAD") && farmDocsFontPath && fs7.existsSync(farmDocsFontPath)) {
23693
24882
  res.statusCode = 200;
23694
24883
  res.setHeader("Content-Type", "font/woff2");
23695
24884
  res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
23696
- fs7.createReadStream(farmDocsFontPath).pipe(res);
24885
+ if (requestMethod === "HEAD") res.end();
24886
+ else fs7.createReadStream(farmDocsFontPath).pipe(res);
23697
24887
  return;
23698
24888
  }
23699
24889
  if (requestMethod === "GET" && (requestPathname === FARM_DEVTOOLS_PATH || requestPathname === `${FARM_DEVTOOLS_PATH}.json`)) {
@@ -24299,6 +25489,7 @@ function farmPlugin(options = {}, initialPluginManager) {
24299
25489
  modulePath: toUrlPath(route.modulePath),
24300
25490
  isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
24301
25491
  shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
25492
+ islandStrategy: moduleMetadata.islandStrategy,
24302
25493
  metadata: {
24303
25494
  title: mergedMetadata.title,
24304
25495
  description: mergedMetadata.description
@@ -24683,6 +25874,7 @@ export const getManifest = () => ({
24683
25874
  search: route.search,
24684
25875
  isClientComponent: moduleMetadata.isClientComponent,
24685
25876
  shouldHydrate: moduleMetadata.shouldHydrate,
25877
+ islandStrategy: moduleMetadata.islandStrategy,
24686
25878
  preloads: [route.modulePath],
24687
25879
  // In dev, preload is just the module
24688
25880
  assets: []
@@ -24809,8 +26001,8 @@ if (import.meta.hot) {
24809
26001
  }
24810
26002
  logger.info(`Found ${ssgPages.length} SSG pages, ${ssrRoutes.length} SSR routes`);
24811
26003
  logger.info("Pre-rendering SSG pages...");
24812
- const outDir = path23.join(server?.config.root || process.cwd(), options.outDir || "dist");
24813
- const clientDir = path23.join(outDir, "client");
26004
+ const outDir = path25.join(server?.config.root || process.cwd(), options.outDir || "dist");
26005
+ const clientDir = path25.join(outDir, "client");
24814
26006
  for (const page2 of ssgPages) {
24815
26007
  try {
24816
26008
  const staticRequest = new Request(new URL(page2.urlPath, "http://farm.static"));
@@ -24821,7 +26013,7 @@ if (import.meta.hot) {
24821
26013
  const layoutModules = await Promise.all(
24822
26014
  layouts.map((l) => routeManager.loadLayoutModule(l.modulePath))
24823
26015
  );
24824
- const React3 = await import("react");
26016
+ const React4 = await import("react");
24825
26017
  const { renderToString } = await import("react-dom/server");
24826
26018
  const PageComponent = mod.default;
24827
26019
  const pageProps = {
@@ -24829,14 +26021,14 @@ if (import.meta.hot) {
24829
26021
  searchParams: Promise.resolve({}),
24830
26022
  path: page2.urlPath
24831
26023
  };
24832
- let pageElement = React3.createElement(
26024
+ let pageElement = React4.createElement(
24833
26025
  PageComponent,
24834
26026
  pageProps
24835
26027
  );
24836
26028
  for (let i = layoutModules.length - 1; i >= 0; i--) {
24837
26029
  const layoutModule = layoutModules[i];
24838
26030
  const LayoutComponent = layoutModule.default;
24839
- pageElement = React3.createElement(
26031
+ pageElement = React4.createElement(
24840
26032
  LayoutComponent,
24841
26033
  {
24842
26034
  children: pageElement,
@@ -24862,8 +26054,8 @@ if (import.meta.hot) {
24862
26054
  <script type="module" src="/assets/client.js"></script>
24863
26055
  </body>
24864
26056
  </html>`;
24865
- const outputPath = page2.urlPath === "/" ? path23.join(clientDir, "index.html") : path23.join(clientDir, page2.urlPath + ".html");
24866
- fs7.mkdirSync(path23.dirname(outputPath), { recursive: true });
26057
+ const outputPath = page2.urlPath === "/" ? path25.join(clientDir, "index.html") : path25.join(clientDir, page2.urlPath + ".html");
26058
+ fs7.mkdirSync(path25.dirname(outputPath), { recursive: true });
24867
26059
  fs7.writeFileSync(outputPath, fullHtml);
24868
26060
  const revalidateInfo = page2.revalidate ? ` (revalidate: ${page2.revalidate}s)` : "";
24869
26061
  logger.success(` \u2713 ${page2.urlPath}${revalidateInfo}`);
@@ -24872,7 +26064,7 @@ if (import.meta.hot) {
24872
26064
  logger.error(` \u2717 ${page2.urlPath}: ${error}`);
24873
26065
  }
24874
26066
  }
24875
- const manifestPath = path23.join(outDir, "__ssg_manifest.json");
26067
+ const manifestPath = path25.join(outDir, "__ssg_manifest.json");
24876
26068
  fs7.writeFileSync(
24877
26069
  manifestPath,
24878
26070
  JSON.stringify(
@@ -24908,9 +26100,9 @@ if (import.meta.hot) {
24908
26100
  }
24909
26101
  const currentFarmConfig = farmApp?.getConfig();
24910
26102
  const normalizedFile = file.replace(/\\/g, "/");
24911
- const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path23.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
26103
+ const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path25.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
24912
26104
  if (currentSrcRoot && normalizedFile.startsWith(`${currentSrcRoot}/`) && isProgrammaticRoutesFileName(normalizedFile)) {
24913
- logUpdate("PAGE", `updated ${path23.basename(file)}`);
26105
+ logUpdate("PAGE", `updated ${path25.basename(file)}`);
24914
26106
  for (const mod of modules) {
24915
26107
  server2.moduleGraph.invalidateModule(mod);
24916
26108
  }
@@ -25307,6 +26499,7 @@ import React from 'react'
25307
26499
  import { hydrateRoot, createRoot } from 'react-dom/client'
25308
26500
  import { installChunkErrorRecovery, SPARouter } from '@farm.js/core/client'
25309
26501
  import { createClientPluginManager } from '@farm.js/core/plugin/client'
26502
+ import { scheduleFarmIslandHydration } from '@farm.js/core/internal/client-runtime'
25310
26503
  import { reviveDeferredData } from '@farm.js/core/deferred'
25311
26504
  import {
25312
26505
  createFarmDeploymentMismatchError,
@@ -25559,6 +26752,7 @@ class LegacyManifestSPARouter {
25559
26752
 
25560
26753
  // Only prefetch routes that will hydrate on the client.
25561
26754
  if (!match.route.isClientComponent && !match.route.shouldHydrate) return;
26755
+ if (match.route.islandStrategy && match.route.islandStrategy !== 'load') return;
25562
26756
 
25563
26757
  const modulePath = match.route.modulePath;
25564
26758
  if (this.moduleCache.has(modulePath)) return;
@@ -25703,6 +26897,12 @@ let hasClientTakenOver = false;
25703
26897
  const routeSlotRoots = new Map();
25704
26898
  const routeSlotDefinitions = new Map();
25705
26899
  let activeRouteInterception = null;
26900
+ let pendingPageHydrationController = null;
26901
+
26902
+ function cancelPendingPageHydration() {
26903
+ pendingPageHydrationController?.abort();
26904
+ pendingPageHydrationController = null;
26905
+ }
25706
26906
 
25707
26907
  function normalizeServerProps(rawProps) {
25708
26908
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -25771,7 +26971,11 @@ async function hydrateInitialRouteSlots() {
25771
26971
  routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
25772
26972
  if (!slot.isClientComponent && !slot.shouldHydrate) continue;
25773
26973
  try {
25774
- hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
26974
+ const slotHydrated = await renderRouteSlot(slot, 'hydrate');
26975
+ hydrated = slotHydrated || hydrated;
26976
+ if (slotHydrated) {
26977
+ replayPreHydrationClicks(document.getElementById(slot.containerId));
26978
+ }
25775
26979
  } catch (error) {
25776
26980
  console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
25777
26981
  }
@@ -25842,16 +27046,29 @@ function applyCanonicalPathFromProps(props) {
25842
27046
  window.history.replaceState({ ...(window.history.state || {}), path: canonicalPath }, '', canonicalPath);
25843
27047
  }
25844
27048
 
25845
- function replayPreHydrationClicks() {
25846
- window.__FARM_HYDRATED__ = true;
25847
- document.documentElement.dataset.farmHydrated = 'true';
27049
+ function replayPreHydrationClicks(container = null) {
27050
+ if (!container) {
27051
+ markFarmHydrated();
27052
+ } else {
27053
+ container.dataset.farmIslandHydrated = 'true';
27054
+ }
25848
27055
 
25849
27056
  const queue = Array.isArray(window.__FARM_PREHYDRATION_CLICK_QUEUE__)
25850
27057
  ? window.__FARM_PREHYDRATION_CLICK_QUEUE__
25851
27058
  : [];
25852
27059
  if (queue.length === 0) return;
25853
27060
 
25854
- const queuedClicks = queue.splice(0, queue.length);
27061
+ const queuedClicks = [];
27062
+ const remainingClicks = [];
27063
+ for (const queuedClick of queue) {
27064
+ const target = queuedClick?.target;
27065
+ if (!container || (target instanceof Node && container.contains(target))) {
27066
+ queuedClicks.push(queuedClick);
27067
+ } else {
27068
+ remainingClicks.push(queuedClick);
27069
+ }
27070
+ }
27071
+ queue.splice(0, queue.length, ...remainingClicks);
25855
27072
  for (const queuedClick of queuedClicks) {
25856
27073
  const target = queuedClick?.target;
25857
27074
  if (!target || typeof target.click !== 'function') continue;
@@ -25860,6 +27077,11 @@ function replayPreHydrationClicks() {
25860
27077
  }
25861
27078
  }
25862
27079
 
27080
+ function markFarmHydrated() {
27081
+ window.__FARM_HYDRATED__ = true;
27082
+ document.documentElement.dataset.farmHydrated = 'true';
27083
+ }
27084
+
25863
27085
  async function buildClientHydrationElement(PageComponent, pageProps) {
25864
27086
  let element = React.createElement(PageComponent, pageProps);
25865
27087
  const loadingModulePath = window.__FARM_LOADING_MODULE__;
@@ -26002,9 +27224,10 @@ async function tryHydrateImportedPage(
26002
27224
  layouts,
26003
27225
  useHydrate = false,
26004
27226
  existingProps = null,
27227
+ signal = null,
26005
27228
  ) {
26006
27229
  const modulePath = route?.modulePath;
26007
- if (!modulePath) {
27230
+ if (!modulePath || signal?.aborted) {
26008
27231
  return false;
26009
27232
  }
26010
27233
 
@@ -26013,6 +27236,7 @@ async function tryHydrateImportedPage(
26013
27236
  pageModule = await import(/* @vite-ignore */ modulePath);
26014
27237
  pageModuleCache.set(modulePath, pageModule);
26015
27238
  }
27239
+ if (signal?.aborted || !container?.isConnected) return false;
26016
27240
 
26017
27241
  const PageComponent = pageModule?.default;
26018
27242
  if (!PageComponent) {
@@ -26027,6 +27251,7 @@ async function tryHydrateImportedPage(
26027
27251
  window.location.pathname,
26028
27252
  existingProps,
26029
27253
  );
27254
+ if (signal?.aborted || !container?.isConnected) return false;
26030
27255
 
26031
27256
  const wrappedElement = useHydrate && container?.id === '__farm_page__'
26032
27257
  ? wrapWithIntegrationProviders(
@@ -26037,6 +27262,7 @@ async function tryHydrateImportedPage(
26037
27262
  currentPageProps,
26038
27263
  layouts,
26039
27264
  );
27265
+ if (signal?.aborted || !container?.isConnected) return false;
26040
27266
 
26041
27267
  if (useHydrate) {
26042
27268
  try {
@@ -26067,6 +27293,7 @@ async function tryHydrateImportedPage(
26067
27293
  async function renderPage(pageData) {
26068
27294
  const container = document.getElementById('root');
26069
27295
  if (!container) return;
27296
+ cancelPendingPageHydration();
26070
27297
  const destination = window.location.pathname + window.location.search;
26071
27298
 
26072
27299
  if (pageData.interception) {
@@ -26083,6 +27310,7 @@ async function renderPage(pageData) {
26083
27310
  modulePath: pageData.modulePath,
26084
27311
  isClientComponent: pageData.isClientComponent === true,
26085
27312
  shouldHydrate: pageData.shouldHydrate === true,
27313
+ islandStrategy: pageData.islandStrategy || 'load',
26086
27314
  };
26087
27315
  const params = pageData.props?.params || {};
26088
27316
  const layouts = (pageData.layoutModules || []).map((modulePath, index) => ({
@@ -26282,14 +27510,6 @@ async function hydrate() {
26282
27510
  }
26283
27511
 
26284
27512
  try {
26285
- // Pre-load layout for SPA navigation
26286
- try {
26287
- const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
26288
- LayoutComponent = layoutModule.default;
26289
- } catch (e) {
26290
- console.warn('[Farm.js] Could not preload layout:', e);
26291
- }
26292
-
26293
27513
  // Check if this is a client component (set by SSR)
26294
27514
  const isClientComponent = window.__FARM_IS_CLIENT__ === true;
26295
27515
  const modulePath = window.__FARM_PAGE_MODULE__;
@@ -26337,34 +27557,88 @@ async function hydrate() {
26337
27557
  return
26338
27558
  }
26339
27559
 
26340
- const hydrationSession = await farmClientRuntime.beginHydration({
26341
- container: pageContainer,
26342
- mode: shouldHydrate ? 'hydrate' : 'render',
26343
- });
27560
+ const currentRoute = findRoute(window.location.pathname)?.route;
27561
+ const islandStrategy = window.__FARM_ISLAND_STRATEGY__ || currentRoute?.islandStrategy || 'load';
27562
+ pageContainer.dataset.farmIsland = 'page';
27563
+ pageContainer.dataset.farmIslandStrategy = islandStrategy;
26344
27564
 
26345
- let hydrated = false;
27565
+ const hydrationPathname = window.location.pathname;
27566
+ const hydrationController = new AbortController();
27567
+ pendingPageHydrationController = hydrationController;
26346
27568
  try {
26347
- hydrated = await tryHydrateImportedPage(
26348
- pageContainer,
26349
- { modulePath },
26350
- currentPageProps.params || {},
26351
- layouts,
26352
- shouldHydrate,
26353
- currentPageProps,
26354
- );
26355
- await farmClientRuntime.completeHydration(hydrationSession);
26356
- } catch (error) {
26357
- await farmClientRuntime.failHydration(hydrationSession, error);
26358
- throw error;
26359
- }
27569
+ await scheduleFarmIslandHydration({
27570
+ container: pageContainer,
27571
+ strategy: islandStrategy,
27572
+ signal: hydrationController.signal,
27573
+ hydrate: async () => {
27574
+ if (
27575
+ hydrationController.signal.aborted ||
27576
+ !pageContainer.isConnected ||
27577
+ window.location.pathname !== hydrationPathname
27578
+ ) {
27579
+ return;
27580
+ }
26360
27581
 
26361
- if (hydrated) {
26362
- replayPreHydrationClicks();
26363
- console.log('[Farm.js] \u2705 Hydrated:', modulePath);
26364
- return;
26365
- }
27582
+ // Layout code is only needed when this route boundary actually hydrates.
27583
+ try {
27584
+ const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
27585
+ LayoutComponent = layoutModule.default;
27586
+ } catch (e) {
27587
+ console.warn('[Farm.js] Could not preload layout:', e);
27588
+ }
27589
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) return;
27590
+
27591
+ const hydrationSession = await farmClientRuntime.beginHydration({
27592
+ container: pageContainer,
27593
+ mode: shouldHydrate ? 'hydrate' : 'render',
27594
+ });
27595
+
27596
+ let hydrated = false;
27597
+ try {
27598
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
27599
+ await farmClientRuntime.failHydration(
27600
+ hydrationSession,
27601
+ new DOMException('Route hydration was cancelled', 'AbortError'),
27602
+ );
27603
+ return;
27604
+ }
27605
+ hydrated = await tryHydrateImportedPage(
27606
+ pageContainer,
27607
+ { modulePath },
27608
+ currentPageProps.params || {},
27609
+ layouts,
27610
+ shouldHydrate,
27611
+ currentPageProps,
27612
+ hydrationController.signal,
27613
+ );
27614
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
27615
+ await farmClientRuntime.failHydration(
27616
+ hydrationSession,
27617
+ new DOMException('Route hydration was cancelled', 'AbortError'),
27618
+ );
27619
+ return;
27620
+ }
27621
+ await farmClientRuntime.completeHydration(hydrationSession);
27622
+ } catch (error) {
27623
+ await farmClientRuntime.failHydration(hydrationSession, error);
27624
+ throw error;
27625
+ }
27626
+
27627
+ if (hydrated) {
27628
+ // The scheduler owns queue draining and exactly-once click replay.
27629
+ markFarmHydrated();
27630
+ console.log('[Farm.js] \u2705 Hydrated:', modulePath, '(' + islandStrategy + ')');
27631
+ return;
27632
+ }
26366
27633
 
26367
- console.log('[Farm.js] Server component - SPA router ready')
27634
+ console.log('[Farm.js] Server component - SPA router ready')
27635
+ },
27636
+ });
27637
+ } finally {
27638
+ if (pendingPageHydrationController === hydrationController) {
27639
+ pendingPageHydrationController = null;
27640
+ }
27641
+ }
26368
27642
  } catch (error) {
26369
27643
  console.error('[Farm.js] Hydration error:', error)
26370
27644
  }
@@ -26470,7 +27744,7 @@ function generateClientManifest(bundle) {
26470
27744
  __name(generateClientManifest, "generateClientManifest");
26471
27745
  async function defineConfig2(config = {}) {
26472
27746
  const tailwindcss = (await import("@tailwindcss/vite")).default;
26473
- const appRoot = path23.resolve(config.root || process.cwd());
27747
+ const appRoot = path25.resolve(config.root || process.cwd());
26474
27748
  if (config.extends?.length) {
26475
27749
  const { config: layeredConfig } = await Promise.resolve().then(() => (init_layers(), layers_exports)).then(
26476
27750
  ({ resolveFarmLayers: resolveFarmLayers2 }) => resolveFarmLayers2(config, {
@@ -26615,13 +27889,16 @@ async function defineConfig2(config = {}) {
26615
27889
  customLogger: farmLogger,
26616
27890
  clearScreen: false,
26617
27891
  optimizeDeps: {
26618
- include: [
26619
- "react",
26620
- "react-dom",
26621
- "react-dom/client",
26622
- "react/jsx-runtime",
26623
- "react/jsx-dev-runtime"
26624
- ],
27892
+ ...createFarmClientOptimizeDepsConfig(
27893
+ createFarmClientOptimizeDepsEntries(
27894
+ appRoot,
27895
+ getFarmAppDirectories({ ...config, root: appRoot })
27896
+ )
27897
+ ),
27898
+ // Pre-bundle every framework client-runtime entry with React. Without
27899
+ // this, Vite can discover a linked Farm entry after the page has loaded,
27900
+ // regenerate the optimizer browser hash, and leave React DOM holding a
27901
+ // different React module instance from a client component.
26625
27902
  // Exclude server-side packages from browser bundling
26626
27903
  exclude: [
26627
27904
  "@farm.js/core/server",
@@ -26656,7 +27933,7 @@ async function defineConfig2(config = {}) {
26656
27933
  // Stub out problematic server-only modules during dev mode
26657
27934
  alias: {
26658
27935
  ...getFarmLayerAliases(config.layers),
26659
- "@": path23.resolve(appRoot, "src"),
27936
+ "@": path25.resolve(appRoot, "src"),
26660
27937
  // Nitro internals that should not be resolved in browser
26661
27938
  "supports-color": "data:text/javascript,export default false; export const supportsColor = false; export const stdout = false; export const stderr = false;",
26662
27939
  "@poppinss/dumper": "data:text/javascript,export default {};",
@@ -27751,55 +29028,8 @@ function createLoggerPlugin({
27751
29028
  }
27752
29029
  __name(createLoggerPlugin, "createLoggerPlugin");
27753
29030
 
27754
- // src/server/vite-config.ts
27755
- function mergeNoExternal(farmValue, userValue) {
27756
- if (farmValue === true || userValue === true) return true;
27757
- const normalize = /* @__PURE__ */ __name((value) => value == null || value === true ? [] : Array.isArray(value) ? value : [value], "normalize");
27758
- return Array.from(/* @__PURE__ */ new Set([...normalize(farmValue), ...normalize(userValue)]));
27759
- }
27760
- __name(mergeNoExternal, "mergeNoExternal");
27761
- function mergeFarmViteConfig(farmConfig, userConfig = {}) {
27762
- return {
27763
- ...farmConfig,
27764
- ...userConfig,
27765
- plugins: [...farmConfig.plugins || [], ...userConfig.plugins || []],
27766
- server: {
27767
- ...farmConfig.server,
27768
- ...userConfig.server
27769
- },
27770
- resolve: {
27771
- ...farmConfig.resolve,
27772
- ...userConfig.resolve,
27773
- dedupe: Array.from(
27774
- /* @__PURE__ */ new Set([...farmConfig.resolve?.dedupe || [], ...userConfig.resolve?.dedupe || []])
27775
- )
27776
- },
27777
- optimizeDeps: {
27778
- ...farmConfig.optimizeDeps,
27779
- ...userConfig.optimizeDeps,
27780
- include: Array.from(
27781
- /* @__PURE__ */ new Set([
27782
- ...farmConfig.optimizeDeps?.include || [],
27783
- ...userConfig.optimizeDeps?.include || []
27784
- ])
27785
- ),
27786
- exclude: Array.from(
27787
- /* @__PURE__ */ new Set([
27788
- ...farmConfig.optimizeDeps?.exclude || [],
27789
- ...userConfig.optimizeDeps?.exclude || []
27790
- ])
27791
- )
27792
- },
27793
- ssr: {
27794
- ...farmConfig.ssr,
27795
- ...userConfig.ssr,
27796
- noExternal: mergeNoExternal(farmConfig.ssr?.noExternal, userConfig.ssr?.noExternal)
27797
- }
27798
- };
27799
- }
27800
- __name(mergeFarmViteConfig, "mergeFarmViteConfig");
27801
-
27802
29031
  // src/server/create-server.ts
29032
+ init_layers();
27803
29033
  var DEFAULT_FARM_DEV_SERVER_PORT = 3e3;
27804
29034
  function createBrandingPlugin() {
27805
29035
  let serverStarted = false;
@@ -27971,15 +29201,9 @@ async function createServer(config = {}) {
27971
29201
  dedupe: ["react", "react-dom"]
27972
29202
  },
27973
29203
  optimizeDeps: {
27974
- // Avoid Vite scanning server/native-only deps from framework internals.
27975
- noDiscovery: true,
27976
- include: [
27977
- "react",
27978
- "react-dom",
27979
- "react-dom/client",
27980
- "react/jsx-runtime",
27981
- "react/jsx-dev-runtime"
27982
- ],
29204
+ ...createFarmClientOptimizeDepsConfig(
29205
+ createFarmClientOptimizeDepsEntries(projectRoot, getFarmAppDirectories(finalConfig))
29206
+ ),
27983
29207
  exclude: [
27984
29208
  "@farm.js/core/server",
27985
29209
  "@farm.js/core/api",