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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (238) hide show
  1. package/dist/chunk-53MVSX3R.mjs +168 -0
  2. package/dist/chunk-53MVSX3R.mjs.map +1 -0
  3. package/dist/{chunk-AW6I64AP.mjs → chunk-6ZXIQSXE.mjs} +16 -1
  4. package/dist/chunk-6ZXIQSXE.mjs.map +1 -0
  5. package/dist/chunk-7ZVN3YZI.mjs +333 -0
  6. package/dist/chunk-7ZVN3YZI.mjs.map +1 -0
  7. package/dist/{chunk-5EVQFRL2.mjs → chunk-ARCGE2VT.mjs} +2 -2
  8. package/dist/chunk-ARCGE2VT.mjs.map +1 -0
  9. package/dist/{chunk-7N62DNSO.mjs → chunk-H3TE3LOV.mjs} +2 -2
  10. package/dist/{chunk-QTZZ2ISQ.mjs → chunk-KLA67WKG.mjs} +1 -1
  11. package/dist/chunk-KLA67WKG.mjs.map +1 -0
  12. package/dist/{chunk-K5ZTUF22.mjs → chunk-L2PE36GQ.mjs} +7 -3
  13. package/dist/chunk-L2PE36GQ.mjs.map +1 -0
  14. package/dist/{chunk-4LND3PXF.mjs → chunk-OPHK6OJN.mjs} +513 -48
  15. package/dist/chunk-OPHK6OJN.mjs.map +1 -0
  16. package/dist/{chunk-77BOAQHI.mjs → chunk-PNFQQSRA.mjs} +754 -156
  17. package/dist/chunk-PNFQQSRA.mjs.map +1 -0
  18. package/dist/chunk-USLFXFFH.mjs +46 -0
  19. package/dist/chunk-USLFXFFH.mjs.map +1 -0
  20. package/dist/{chunk-4677KXB7.mjs → chunk-VUL55IAX.mjs} +368 -103
  21. package/dist/chunk-VUL55IAX.mjs.map +1 -0
  22. package/dist/client.cjs.map +1 -1
  23. package/dist/client.mjs +1 -1
  24. package/dist/{devtools-A22KUJZT.mjs → devtools-GAZ7F3KL.mjs} +4 -3
  25. package/dist/{devtools-A22KUJZT.mjs.map → devtools-GAZ7F3KL.mjs.map} +1 -1
  26. package/dist/docs.cjs +622 -124
  27. package/dist/docs.cjs.map +1 -1
  28. package/dist/docs.mjs +5 -3
  29. package/dist/font.cjs +19 -2
  30. package/dist/font.cjs.map +1 -1
  31. package/dist/font.mjs +7 -3
  32. package/dist/index.cjs +2235 -689
  33. package/dist/index.cjs.map +1 -1
  34. package/dist/index.mjs +27 -19
  35. package/dist/index.mjs.map +1 -1
  36. package/dist/internal/build-runtime.cjs +2062 -525
  37. package/dist/internal/build-runtime.cjs.map +1 -1
  38. package/dist/internal/build-runtime.mjs +13 -10
  39. package/dist/internal/client-runtime.cjs +169 -2
  40. package/dist/internal/client-runtime.cjs.map +1 -1
  41. package/dist/internal/client-runtime.mjs +170 -2
  42. package/dist/internal/client-runtime.mjs.map +1 -1
  43. package/dist/internal/config-runtime.cjs +22 -0
  44. package/dist/internal/config-runtime.cjs.map +1 -1
  45. package/dist/internal/config-runtime.mjs +3 -2
  46. package/dist/internal/metadata-image-runtime.cjs +199 -0
  47. package/dist/internal/metadata-image-runtime.cjs.map +1 -0
  48. package/dist/internal/metadata-image-runtime.mjs +8 -0
  49. package/dist/internal/metadata-image-runtime.mjs.map +1 -0
  50. package/dist/internal/production-runtime.cjs +310 -0
  51. package/dist/internal/production-runtime.cjs.map +1 -1
  52. package/dist/internal/production-runtime.mjs +10 -0
  53. package/dist/internal/production-runtime.mjs.map +1 -1
  54. package/dist/navigation.cjs.map +1 -1
  55. package/dist/navigation.mjs +2 -2
  56. package/dist/plugin.cjs +1550 -348
  57. package/dist/plugin.cjs.map +1 -1
  58. package/dist/plugin.mjs +9 -5
  59. package/dist/server.cjs +1663 -419
  60. package/dist/server.cjs.map +1 -1
  61. package/dist/server.mjs +17 -19
  62. package/dist/server.mjs.map +1 -1
  63. package/dist/version.cjs +1 -1
  64. package/dist/version.cjs.map +1 -1
  65. package/dist/version.mjs +1 -1
  66. package/dist/vite.cjs +1604 -358
  67. package/dist/vite.cjs.map +1 -1
  68. package/dist/vite.mjs +9 -5
  69. package/package.json +10 -1
  70. package/types/client.d.ts +115 -4
  71. package/dist/after.d.mts +0 -25
  72. package/dist/after.d.ts +0 -25
  73. package/dist/agent-runtime.d.mts +0 -94
  74. package/dist/agent-runtime.d.ts +0 -94
  75. package/dist/api/route-manager.d.mts +0 -66
  76. package/dist/api/route-manager.d.ts +0 -66
  77. package/dist/api/runtime.d.mts +0 -3
  78. package/dist/api/runtime.d.ts +0 -3
  79. package/dist/api.d.mts +0 -48
  80. package/dist/api.d.ts +0 -48
  81. package/dist/app-BNE2FoZ4.d.mts +0 -302
  82. package/dist/app-DRGUmJ3s.d.ts +0 -302
  83. package/dist/app-markdown.d.mts +0 -22
  84. package/dist/app-markdown.d.ts +0 -22
  85. package/dist/cache.d.mts +0 -192
  86. package/dist/cache.d.ts +0 -192
  87. package/dist/chunk-4677KXB7.mjs.map +0 -1
  88. package/dist/chunk-4LND3PXF.mjs.map +0 -1
  89. package/dist/chunk-5EVQFRL2.mjs.map +0 -1
  90. package/dist/chunk-77BOAQHI.mjs.map +0 -1
  91. package/dist/chunk-AW6I64AP.mjs.map +0 -1
  92. package/dist/chunk-K5ZTUF22.mjs.map +0 -1
  93. package/dist/chunk-QM746KUO.mjs +0 -56
  94. package/dist/chunk-QM746KUO.mjs.map +0 -1
  95. package/dist/chunk-QTZZ2ISQ.mjs.map +0 -1
  96. package/dist/client-BT1mFQas.d.mts +0 -447
  97. package/dist/client-Nn0q4eO4.d.ts +0 -447
  98. package/dist/client-cache-CIlyBlmx.d.ts +0 -6
  99. package/dist/client-cache-D-EZ7XVW.d.mts +0 -6
  100. package/dist/client-plugins-Bc5jHcNr.d.ts +0 -1927
  101. package/dist/client-plugins-jsk-ZLzJ.d.mts +0 -1927
  102. package/dist/client-plugins.d.mts +0 -22
  103. package/dist/client-plugins.d.ts +0 -22
  104. package/dist/client.d.mts +0 -447
  105. package/dist/client.d.ts +0 -447
  106. package/dist/config.d.mts +0 -29
  107. package/dist/config.d.ts +0 -29
  108. package/dist/cron.d.mts +0 -78
  109. package/dist/cron.d.ts +0 -78
  110. package/dist/deferred.d.mts +0 -42
  111. package/dist/deferred.d.ts +0 -42
  112. package/dist/deployment.d.mts +0 -34
  113. package/dist/deployment.d.ts +0 -34
  114. package/dist/docs.d.mts +0 -69
  115. package/dist/docs.d.ts +0 -69
  116. package/dist/env-types.d.mts +0 -12
  117. package/dist/env-types.d.ts +0 -12
  118. package/dist/env.d.mts +0 -45
  119. package/dist/env.d.ts +0 -45
  120. package/dist/environment/vite.d.mts +0 -22
  121. package/dist/environment/vite.d.ts +0 -22
  122. package/dist/environment.d.mts +0 -18
  123. package/dist/environment.d.ts +0 -18
  124. package/dist/font.d.mts +0 -70
  125. package/dist/font.d.ts +0 -70
  126. package/dist/headers.d.mts +0 -22
  127. package/dist/headers.d.ts +0 -22
  128. package/dist/i18n/client.d.mts +0 -32
  129. package/dist/i18n/client.d.ts +0 -32
  130. package/dist/i18n/index.d.mts +0 -20
  131. package/dist/i18n/index.d.ts +0 -20
  132. package/dist/i18n/server.d.mts +0 -30
  133. package/dist/i18n/server.d.ts +0 -30
  134. package/dist/image/server.d.mts +0 -37
  135. package/dist/image/server.d.ts +0 -37
  136. package/dist/image/sharp.d.mts +0 -7
  137. package/dist/image/sharp.d.ts +0 -7
  138. package/dist/image-config-Duob7wER.d.mts +0 -50
  139. package/dist/image-config-Duob7wER.d.ts +0 -50
  140. package/dist/image.d.mts +0 -39
  141. package/dist/image.d.ts +0 -39
  142. package/dist/index.d.mts +0 -455
  143. package/dist/index.d.ts +0 -455
  144. package/dist/integration-api-CHT2OKAf.d.mts +0 -93
  145. package/dist/integration-api-CHT2OKAf.d.ts +0 -93
  146. package/dist/integrations.d.mts +0 -22
  147. package/dist/integrations.d.ts +0 -22
  148. package/dist/internal/build-runtime.d.mts +0 -39
  149. package/dist/internal/build-runtime.d.ts +0 -39
  150. package/dist/internal/client-runtime.d.mts +0 -2
  151. package/dist/internal/client-runtime.d.ts +0 -2
  152. package/dist/internal/config-runtime.d.mts +0 -39
  153. package/dist/internal/config-runtime.d.ts +0 -39
  154. package/dist/internal/production-node-env.d.mts +0 -12
  155. package/dist/internal/production-node-env.d.ts +0 -12
  156. package/dist/internal/production-runtime.d.mts +0 -41
  157. package/dist/internal/production-runtime.d.ts +0 -41
  158. package/dist/markdown.d.mts +0 -60
  159. package/dist/markdown.d.ts +0 -60
  160. package/dist/metadata-7ffpT3jr.d.mts +0 -22
  161. package/dist/metadata-JHn-8eht.d.ts +0 -22
  162. package/dist/middleware.d.mts +0 -164
  163. package/dist/middleware.d.ts +0 -164
  164. package/dist/navigation-errors-Dvwpzaer.d.mts +0 -13
  165. package/dist/navigation-errors-Dvwpzaer.d.ts +0 -13
  166. package/dist/navigation.d.mts +0 -14
  167. package/dist/navigation.d.ts +0 -14
  168. package/dist/observability.d.mts +0 -335
  169. package/dist/observability.d.ts +0 -335
  170. package/dist/params-BALtrRim.d.ts +0 -15
  171. package/dist/params-ry-lxqmy.d.mts +0 -15
  172. package/dist/plugin-BJDf43dc.d.mts +0 -165
  173. package/dist/plugin-BJDf43dc.d.ts +0 -165
  174. package/dist/plugin.d.mts +0 -22
  175. package/dist/plugin.d.ts +0 -22
  176. package/dist/production-runtime-9KG7GuvW.d.ts +0 -79
  177. package/dist/production-runtime-B-fweKts.d.mts +0 -79
  178. package/dist/production-runtime-C_B79w2P.d.mts +0 -28
  179. package/dist/production-runtime-DDEM1D2q.d.ts +0 -28
  180. package/dist/query/client.d.mts +0 -61
  181. package/dist/query/client.d.ts +0 -61
  182. package/dist/query/index.d.mts +0 -90
  183. package/dist/query/index.d.ts +0 -90
  184. package/dist/query/parsers.d.mts +0 -25
  185. package/dist/query/parsers.d.ts +0 -25
  186. package/dist/query/server.d.mts +0 -55
  187. package/dist/query/server.d.ts +0 -55
  188. package/dist/request-Dsq4kGW0.d.mts +0 -6
  189. package/dist/request-udtobOLv.d.ts +0 -6
  190. package/dist/request.d.mts +0 -2
  191. package/dist/request.d.ts +0 -2
  192. package/dist/resolver-DJqjLMg_.d.mts +0 -14
  193. package/dist/resolver-IH4RmPEl.d.ts +0 -14
  194. package/dist/route-runtime-DoOHzCAD.d.mts +0 -38
  195. package/dist/route-runtime-DoOHzCAD.d.ts +0 -38
  196. package/dist/router.d.mts +0 -36
  197. package/dist/router.d.ts +0 -36
  198. package/dist/routes.d.mts +0 -224
  199. package/dist/routes.d.ts +0 -224
  200. package/dist/runtime-9nQjVM7X.d.mts +0 -206
  201. package/dist/runtime-Cxpq6xZN.d.mts +0 -27
  202. package/dist/runtime-DgeS2skB.d.ts +0 -206
  203. package/dist/runtime-bv63eUiO.d.ts +0 -27
  204. package/dist/server-action-security.d.mts +0 -43
  205. package/dist/server-action-security.d.ts +0 -43
  206. package/dist/server-fn-client.d.mts +0 -32
  207. package/dist/server-fn-client.d.ts +0 -32
  208. package/dist/server-fn.d.mts +0 -93
  209. package/dist/server-fn.d.ts +0 -93
  210. package/dist/server-plugins.d.mts +0 -55
  211. package/dist/server-plugins.d.ts +0 -55
  212. package/dist/server-query-client.d.mts +0 -50
  213. package/dist/server-query-client.d.ts +0 -50
  214. package/dist/server-query.d.mts +0 -36
  215. package/dist/server-query.d.ts +0 -36
  216. package/dist/server.d.mts +0 -48
  217. package/dist/server.d.ts +0 -48
  218. package/dist/storage.d.mts +0 -52
  219. package/dist/storage.d.ts +0 -52
  220. package/dist/testing.d.mts +0 -101
  221. package/dist/testing.d.ts +0 -101
  222. package/dist/transport-BmbGTXA5.d.mts +0 -49
  223. package/dist/transport-BmbGTXA5.d.ts +0 -49
  224. package/dist/types-BMxWr97v.d.mts +0 -81
  225. package/dist/types-BMxWr97v.d.ts +0 -81
  226. package/dist/types-C9N-nk5R.d.mts +0 -53
  227. package/dist/types-C9N-nk5R.d.ts +0 -53
  228. package/dist/types-D1A6-XvH.d.mts +0 -73
  229. package/dist/types-D1A6-XvH.d.ts +0 -73
  230. package/dist/utils-CA0z0c-2.d.ts +0 -23
  231. package/dist/utils-mYVkSOt2.d.mts +0 -23
  232. package/dist/version.d.mts +0 -3
  233. package/dist/version.d.ts +0 -3
  234. package/dist/vite.d.mts +0 -22
  235. package/dist/vite.d.ts +0 -22
  236. package/dist/workflows.d.mts +0 -110
  237. package/dist/workflows.d.ts +0 -110
  238. /package/dist/{chunk-7N62DNSO.mjs.map → chunk-H3TE3LOV.mjs.map} +0 -0
package/dist/plugin.cjs CHANGED
@@ -843,26 +843,26 @@ async function prepareFarmWorkflowsForNitro(config) {
843
843
  const root = config.root || process.cwd();
844
844
  const distDir = config.distDir || ".farm";
845
845
  const fs8 = await import("fs/promises");
846
- const path24 = await import("path");
847
- const generatedDir = path24.join(root, distDir, ".nitro", "farm-workflows");
846
+ const path26 = await import("path");
847
+ const generatedDir = path26.join(root, distDir, ".nitro", "farm-workflows");
848
848
  await fs8.mkdir(generatedDir, { recursive: true });
849
849
  const tasks = {};
850
850
  const scheduledTasks = createScheduledTasks(workflows);
851
851
  for (const workflow of workflows) {
852
- const wrapperPath = path24.join(generatedDir, `${safeFileName(workflow.id)}.mjs`);
852
+ const wrapperPath = path26.join(generatedDir, `${safeFileName(workflow.id)}.mjs`);
853
853
  await fs8.writeFile(wrapperPath, createNitroTaskWrapper(workflow), "utf8");
854
854
  tasks[workflow.id] = {
855
855
  handler: wrapperPath,
856
856
  description: workflow.description || `Farm workflow ${workflow.id}`
857
857
  };
858
858
  }
859
- const handlerPath = path24.join(generatedDir, "http-handler.mjs");
859
+ const handlerPath = path26.join(generatedDir, "http-handler.mjs");
860
860
  await fs8.writeFile(
861
861
  handlerPath,
862
862
  createNitroWorkflowHTTPHandler(workflowConfig, workflows),
863
863
  "utf8"
864
864
  );
865
- const manifestPath = path24.join(generatedDir, "manifest.json");
865
+ const manifestPath = path26.join(generatedDir, "manifest.json");
866
866
  await fs8.writeFile(
867
867
  manifestPath,
868
868
  JSON.stringify(
@@ -945,10 +945,10 @@ function resolveWorkflowDefinition(module2) {
945
945
  }
946
946
  async function findWorkflowFiles(root, dirs) {
947
947
  const fs8 = await import("fs/promises");
948
- const path24 = await import("path");
948
+ const path26 = await import("path");
949
949
  const files = [];
950
950
  for (const dir of dirs) {
951
- const absoluteDir = path24.isAbsolute(dir) ? dir : path24.join(root, dir);
951
+ const absoluteDir = path26.isAbsolute(dir) ? dir : path26.join(root, dir);
952
952
  if (!await pathExists(absoluteDir)) continue;
953
953
  await walkWorkflowDir(absoluteDir, files);
954
954
  }
@@ -956,10 +956,10 @@ async function findWorkflowFiles(root, dirs) {
956
956
  }
957
957
  async function walkWorkflowDir(dir, files) {
958
958
  const fs8 = await import("fs/promises");
959
- const path24 = await import("path");
959
+ const path26 = await import("path");
960
960
  const entries = await fs8.readdir(dir, { withFileTypes: true });
961
961
  for (const entry of entries) {
962
- const filePath = path24.join(dir, entry.name);
962
+ const filePath = path26.join(dir, entry.name);
963
963
  if (entry.isDirectory()) {
964
964
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
965
965
  await walkWorkflowDir(filePath, files);
@@ -979,12 +979,12 @@ function isWorkflowFile(fileName) {
979
979
  }
980
980
  async function loadWorkflowModule(filePath, root) {
981
981
  const fs8 = await import("fs/promises");
982
- const path24 = await import("path");
982
+ const path26 = await import("path");
983
983
  const { pathToFileURL: pathToFileURL5 } = await import("url");
984
984
  const { build } = await import("esbuild");
985
- const outDir = path24.join(root, ".farm", ".workflow-loader");
985
+ const outDir = path26.join(root, ".farm", ".workflow-loader");
986
986
  await fs8.mkdir(outDir, { recursive: true });
987
- const outfile = path24.join(
987
+ const outfile = path26.join(
988
988
  outDir,
989
989
  `workflow-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`
990
990
  );
@@ -1267,7 +1267,7 @@ function normalizeCronJob(name, job) {
1267
1267
  if (!job || typeof job !== "object" || Array.isArray(job)) {
1268
1268
  throw new TypeError(`Farm cron ${JSON.stringify(name)} must be a configuration object.`);
1269
1269
  }
1270
- const path24 = normalizeCronPath(name, job.path);
1270
+ const path26 = normalizeCronPath(name, job.path);
1271
1271
  const rawSchedule = Array.isArray(job.schedule) ? job.schedule : [job.schedule];
1272
1272
  const schedule = [...new Set(rawSchedule.map((value) => normalizeCronExpression(name, value)))];
1273
1273
  if (schedule.length === 0) {
@@ -1276,7 +1276,7 @@ function normalizeCronJob(name, job) {
1276
1276
  return {
1277
1277
  name,
1278
1278
  schedule,
1279
- path: path24,
1279
+ path: path26,
1280
1280
  description: job.description?.trim() || void 0
1281
1281
  };
1282
1282
  }
@@ -1284,13 +1284,13 @@ function normalizeCronPath(name, value) {
1284
1284
  if (typeof value !== "string" || !value.trim().startsWith("/")) {
1285
1285
  throw new TypeError(`Farm cron ${JSON.stringify(name)} path must start with "/".`);
1286
1286
  }
1287
- const path24 = value.trim();
1288
- if (path24.startsWith("//") || path24.includes("?") || path24.includes("#")) {
1287
+ const path26 = value.trim();
1288
+ if (path26.startsWith("//") || path26.includes("?") || path26.includes("#")) {
1289
1289
  throw new TypeError(
1290
1290
  `Farm cron ${JSON.stringify(name)} path must be an application pathname without a host, query, or hash.`
1291
1291
  );
1292
1292
  }
1293
- return path24.length > 1 ? path24.replace(/\/+$/, "") : path24;
1293
+ return path26.length > 1 ? path26.replace(/\/+$/, "") : path26;
1294
1294
  }
1295
1295
  function normalizeCronExpression(name, value) {
1296
1296
  if (typeof value !== "string") {
@@ -2147,7 +2147,7 @@ var init_server_action_security = __esm({
2147
2147
 
2148
2148
  // src/image-config.ts
2149
2149
  function resolveFarmImageConfig(config) {
2150
- const path24 = normalizeImagePath(config?.path ?? DEFAULT_FARM_IMAGE_PATH);
2150
+ const path26 = normalizeImagePath(config?.path ?? DEFAULT_FARM_IMAGE_PATH);
2151
2151
  const deviceSizes = normalizeIntegerList(
2152
2152
  config?.deviceSizes ?? DEFAULT_FARM_IMAGE_DEVICE_SIZES,
2153
2153
  "images.deviceSizes"
@@ -2175,7 +2175,7 @@ function resolveFarmImageConfig(config) {
2175
2175
  );
2176
2176
  return Object.freeze({
2177
2177
  provider: config?.provider ?? "auto",
2178
- path: path24,
2178
+ path: path26,
2179
2179
  domains: Object.freeze([
2180
2180
  ...new Set(
2181
2181
  (config?.domains ?? []).map((domain) => domain.trim().toLowerCase()).filter(Boolean)
@@ -2214,11 +2214,11 @@ function getPublicFarmImageConfig(config) {
2214
2214
  };
2215
2215
  }
2216
2216
  function normalizeImagePath(value) {
2217
- const path24 = value.trim().replace(/\/+$/, "") || "/";
2218
- if (!path24.startsWith("/") || path24.includes("?") || path24.includes("#")) {
2217
+ const path26 = value.trim().replace(/\/+$/, "") || "/";
2218
+ if (!path26.startsWith("/") || path26.includes("?") || path26.includes("#")) {
2219
2219
  throw new TypeError("images.path must be an absolute pathname without a query or hash");
2220
2220
  }
2221
- return path24;
2221
+ return path26;
2222
2222
  }
2223
2223
  function normalizeIntegerList(values, name, max) {
2224
2224
  if (values.length === 0) {
@@ -2567,9 +2567,9 @@ var init_integration_orm = __esm({
2567
2567
  });
2568
2568
 
2569
2569
  // src/integrations.ts
2570
- function defineTypedIntegrationRoute(method, path24, input) {
2570
+ function defineTypedIntegrationRoute(method, path26, input) {
2571
2571
  return {
2572
- path: path24,
2572
+ path: path26,
2573
2573
  method,
2574
2574
  middleware: input.middleware,
2575
2575
  before: input.before,
@@ -2579,7 +2579,7 @@ function defineTypedIntegrationRoute(method, path24, input) {
2579
2579
  input: normalizeIntegrationRouteInputSchemas(input),
2580
2580
  handler: input.handler,
2581
2581
  __operation: defineIntegrationAPIOperation({
2582
- path: path24,
2582
+ path: path26,
2583
2583
  method,
2584
2584
  bodyFormat: input.bodyFormat,
2585
2585
  responseFormat: input.responseFormat,
@@ -2591,52 +2591,52 @@ function defineTypedIntegrationRoute(method, path24, input) {
2591
2591
  }
2592
2592
  function createIntegrationRouteFactory() {
2593
2593
  return {
2594
- get(path24, input) {
2594
+ get(path26, input) {
2595
2595
  return defineTypedIntegrationRoute(
2596
2596
  "GET",
2597
- path24,
2597
+ path26,
2598
2598
  input
2599
2599
  );
2600
2600
  },
2601
- post(path24, input) {
2601
+ post(path26, input) {
2602
2602
  return defineTypedIntegrationRoute(
2603
2603
  "POST",
2604
- path24,
2604
+ path26,
2605
2605
  {
2606
2606
  bodyFormat: "json",
2607
2607
  ...input
2608
2608
  }
2609
2609
  );
2610
2610
  },
2611
- put(path24, input) {
2611
+ put(path26, input) {
2612
2612
  return defineTypedIntegrationRoute(
2613
2613
  "PUT",
2614
- path24,
2614
+ path26,
2615
2615
  {
2616
2616
  bodyFormat: "json",
2617
2617
  ...input
2618
2618
  }
2619
2619
  );
2620
2620
  },
2621
- patch(path24, input) {
2622
- return defineTypedIntegrationRoute("PATCH", path24, {
2621
+ patch(path26, input) {
2622
+ return defineTypedIntegrationRoute("PATCH", path26, {
2623
2623
  bodyFormat: "json",
2624
2624
  ...input
2625
2625
  });
2626
2626
  },
2627
- delete(path24, input) {
2628
- return defineTypedIntegrationRoute("DELETE", path24, {
2627
+ delete(path26, input) {
2628
+ return defineTypedIntegrationRoute("DELETE", path26, {
2629
2629
  bodyFormat: "json",
2630
2630
  ...input
2631
2631
  });
2632
2632
  },
2633
- options(path24, input) {
2634
- return defineTypedIntegrationRoute("OPTIONS", path24, input);
2633
+ options(path26, input) {
2634
+ return defineTypedIntegrationRoute("OPTIONS", path26, input);
2635
2635
  },
2636
- head(path24, input) {
2636
+ head(path26, input) {
2637
2637
  return defineTypedIntegrationRoute(
2638
2638
  "HEAD",
2639
- path24,
2639
+ path26,
2640
2640
  input
2641
2641
  );
2642
2642
  }
@@ -2973,8 +2973,8 @@ function normalizeIntegrationValidationIssues(source, error) {
2973
2973
  }
2974
2974
  ];
2975
2975
  }
2976
- function normalizeIntegrationValidationPath(path24) {
2977
- return (path24 || []).map((segment) => {
2976
+ function normalizeIntegrationValidationPath(path26) {
2977
+ return (path26 || []).map((segment) => {
2978
2978
  const key = typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment;
2979
2979
  return typeof key === "symbol" ? key.description || key.toString() : key;
2980
2980
  });
@@ -3663,8 +3663,8 @@ function createFarmDeploymentMismatchResponse(mismatch) {
3663
3663
  }
3664
3664
  );
3665
3665
  }
3666
- function createFarmDeploymentCookie(deploymentId, path24 = "/", secure = false) {
3667
- const normalizedPath = path24.startsWith("/") ? path24 : `/${path24}`;
3666
+ function createFarmDeploymentCookie(deploymentId, path26 = "/", secure = false) {
3667
+ const normalizedPath = path26.startsWith("/") ? path26 : `/${path26}`;
3668
3668
  return [
3669
3669
  `${FARM_DEPLOYMENT_COOKIE}=${encodeURIComponent(deploymentId)}`,
3670
3670
  `Path=${normalizedPath}`,
@@ -3920,6 +3920,33 @@ var init_auth_config = __esm({
3920
3920
  }
3921
3921
  });
3922
3922
 
3923
+ // src/preload.ts
3924
+ function resolveFarmPerformanceConfig(config) {
3925
+ return {
3926
+ preload: {
3927
+ mode: config?.preload?.mode === "warn" ? "warn" : "enforce",
3928
+ maxImages: normalizeBudget(config?.preload?.maxImages, DEFAULT_PRELOAD_CONFIG.maxImages),
3929
+ maxFonts: normalizeBudget(config?.preload?.maxFonts, DEFAULT_PRELOAD_CONFIG.maxFonts)
3930
+ }
3931
+ };
3932
+ }
3933
+ function normalizeBudget(value, fallback) {
3934
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
3935
+ }
3936
+ var DEFAULT_PRELOAD_CONFIG;
3937
+ var init_preload = __esm({
3938
+ "src/preload.ts"() {
3939
+ "use strict";
3940
+ DEFAULT_PRELOAD_CONFIG = {
3941
+ mode: "enforce",
3942
+ maxImages: 1,
3943
+ maxFonts: 2
3944
+ };
3945
+ __name(resolveFarmPerformanceConfig, "resolveFarmPerformanceConfig");
3946
+ __name(normalizeBudget, "normalizeBudget");
3947
+ }
3948
+ });
3949
+
3923
3950
  // src/write-file-if-changed.ts
3924
3951
  function writeFileIfChanged(filePath, content) {
3925
3952
  try {
@@ -4039,11 +4066,11 @@ var init_type_generator = __esm({
4039
4066
  routeGroups.get(key).push(route);
4040
4067
  }
4041
4068
  const nestedStructure = {};
4042
- for (const [path24, routeList] of routeGroups) {
4069
+ for (const [path26, routeList] of routeGroups) {
4043
4070
  const route = routeList[0];
4044
4071
  const importPath = this.getRouteImportPath(route, options.outFile);
4045
4072
  const routeName = this.pathToRouteName(route.path);
4046
- const cleanPath = path24 === "/api" ? "" : path24.replace(/^\/api\//, "");
4073
+ const cleanPath = path26 === "/api" ? "" : path26.replace(/^\/api\//, "");
4047
4074
  const parts = cleanPath ? cleanPath.split("/") : [];
4048
4075
  const allMethods = routeList.flatMap((r) => r.methods);
4049
4076
  for (const method of allMethods) {
@@ -4102,8 +4129,8 @@ ${typeExports}
4102
4129
  const relativeImport = (0, import_path7.relative)((0, import_path7.dirname)(outFile), route.filePath).replace(/\\/g, "/").replace(/\.(ts|tsx|js|jsx)$/, "");
4103
4130
  return relativeImport.startsWith(".") ? relativeImport : `./${relativeImport}`;
4104
4131
  }
4105
- pathToRouteName(path24) {
4106
- return (path24 === "/api" ? "root" : path24.replace(/^\/api\//, "")).replace(/\//g, "_").replace(/[^a-zA-Z0-9_]/g, "");
4132
+ pathToRouteName(path26) {
4133
+ return (path26 === "/api" ? "root" : path26.replace(/^\/api\//, "")).replace(/\//g, "_").replace(/[^a-zA-Z0-9_]/g, "");
4107
4134
  }
4108
4135
  structureToTypeString(obj, indent) {
4109
4136
  const spaces = " ".repeat(indent);
@@ -4123,15 +4150,15 @@ ${typeExports}
4123
4150
  toTypePropertyKey(key) {
4124
4151
  return /^[$A-Z_][0-9A-Z_$]*$/i.test(key) ? key : JSON.stringify(key);
4125
4152
  }
4126
- getBaseExportName(path24) {
4127
- const cleanPath = path24.replace(/^\/api\//, "");
4153
+ getBaseExportName(path26) {
4154
+ const cleanPath = path26.replace(/^\/api\//, "");
4128
4155
  if (cleanPath === "") {
4129
4156
  return "api";
4130
4157
  }
4131
4158
  return cleanPath;
4132
4159
  }
4133
- getExportName(path24, method) {
4134
- const cleanPath = path24.replace(/^\/api\//, "");
4160
+ getExportName(path26, method) {
4161
+ const cleanPath = path26.replace(/^\/api\//, "");
4135
4162
  if (cleanPath === "") {
4136
4163
  return method.toLowerCase();
4137
4164
  }
@@ -4573,28 +4600,71 @@ var init_image_server = __esm({
4573
4600
  }
4574
4601
  });
4575
4602
 
4603
+ // src/docs/fonts.ts
4604
+ function resolveFarmDocsFontAssets(root) {
4605
+ return FARM_DOCS_FONT_SOURCES.flatMap(({ family, packagePath }) => {
4606
+ const sourcePath = import_node_path10.default.join(root, "node_modules", packagePath);
4607
+ if (!(0, import_node_fs6.existsSync)(sourcePath)) return [];
4608
+ const source = (0, import_node_fs6.readFileSync)(sourcePath);
4609
+ const fingerprint = (0, import_node_crypto3.createHash)("sha256").update(source).digest("hex").slice(0, 12);
4610
+ const extension = import_node_path10.default.extname(packagePath);
4611
+ const baseName = import_node_path10.default.basename(packagePath, extension);
4612
+ return [
4613
+ {
4614
+ family,
4615
+ sourcePath,
4616
+ url: `/assets/fonts/${baseName}-h${fingerprint}${extension}`
4617
+ }
4618
+ ];
4619
+ });
4620
+ }
4621
+ function toFarmDocsPublicFontAssets(assets) {
4622
+ return assets.map(({ family, url }) => ({ family, url }));
4623
+ }
4624
+ var import_node_crypto3, import_node_fs6, import_node_path10, FARM_DOCS_FONT_SOURCES;
4625
+ var init_fonts = __esm({
4626
+ "src/docs/fonts.ts"() {
4627
+ "use strict";
4628
+ import_node_crypto3 = require("crypto");
4629
+ import_node_fs6 = require("fs");
4630
+ import_node_path10 = __toESM(require("path"));
4631
+ FARM_DOCS_FONT_SOURCES = [
4632
+ {
4633
+ family: "Geist Sans",
4634
+ packagePath: "geist/dist/fonts/geist-sans/Geist-Variable.woff2"
4635
+ },
4636
+ {
4637
+ family: "Geist Mono",
4638
+ packagePath: "geist/dist/fonts/geist-mono/GeistMono-Variable.woff2"
4639
+ }
4640
+ ];
4641
+ __name(resolveFarmDocsFontAssets, "resolveFarmDocsFontAssets");
4642
+ __name(toFarmDocsPublicFontAssets, "toFarmDocsPublicFontAssets");
4643
+ }
4644
+ });
4645
+
4576
4646
  // src/docs/last-modified.ts
4577
4647
  function normalizeRelativePath(value) {
4578
4648
  return value.replace(/\\/g, "/").replace(/^\.\/+/, "");
4579
4649
  }
4580
4650
  function resolveExistingPath(filePath) {
4581
4651
  try {
4582
- return (0, import_node_fs6.realpathSync)(filePath);
4652
+ return (0, import_node_fs7.realpathSync)(filePath);
4583
4653
  } catch {
4584
- return import_node_path9.default.resolve(filePath);
4654
+ return import_node_path11.default.resolve(filePath);
4585
4655
  }
4586
4656
  }
4587
4657
  function isDocsSourceFile(filePath) {
4588
- return DOCS_FILE_EXTENSIONS.has(import_node_path9.default.extname(filePath).toLowerCase());
4658
+ return DOCS_FILE_EXTENSIONS.has(import_node_path11.default.extname(filePath).toLowerCase());
4589
4659
  }
4590
4660
  function discoverDocsSourceFiles(contentDir) {
4591
4661
  const files = [];
4592
4662
  const visit = /* @__PURE__ */ __name((dir) => {
4593
- for (const entry of (0, import_node_fs6.readdirSync)(dir, { withFileTypes: true }).sort(
4663
+ for (const entry of (0, import_node_fs7.readdirSync)(dir, { withFileTypes: true }).sort(
4594
4664
  (a, b) => a.name.localeCompare(b.name)
4595
4665
  )) {
4596
4666
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
4597
- const absolutePath = import_node_path9.default.join(dir, entry.name);
4667
+ const absolutePath = import_node_path11.default.join(dir, entry.name);
4598
4668
  if (entry.isDirectory()) {
4599
4669
  visit(absolutePath);
4600
4670
  } else if (entry.isFile() && isDocsSourceFile(absolutePath)) {
@@ -4602,7 +4672,7 @@ function discoverDocsSourceFiles(contentDir) {
4602
4672
  }
4603
4673
  }
4604
4674
  }, "visit");
4605
- if ((0, import_node_fs6.existsSync)(contentDir)) visit(contentDir);
4675
+ if ((0, import_node_fs7.existsSync)(contentDir)) visit(contentDir);
4606
4676
  return files;
4607
4677
  }
4608
4678
  function getGitLastModifiedDates(contentDir) {
@@ -4612,7 +4682,7 @@ function getGitLastModifiedDates(contentDir) {
4612
4682
  stdio: ["ignore", "pipe", "ignore"]
4613
4683
  }).trim();
4614
4684
  if (!gitRoot) return {};
4615
- const contentPathspec = normalizeRelativePath(import_node_path9.default.relative(gitRoot, contentDir)) || ".";
4685
+ const contentPathspec = normalizeRelativePath(import_node_path11.default.relative(gitRoot, contentDir)) || ".";
4616
4686
  const contentPrefix = contentPathspec === "." ? "" : `${contentPathspec}/`;
4617
4687
  const history = (0, import_node_child_process.execFileSync)(
4618
4688
  "git",
@@ -4654,11 +4724,11 @@ function getGitLastModifiedDates(contentDir) {
4654
4724
  }
4655
4725
  }
4656
4726
  function readManifest(contentDir) {
4657
- const manifestPath = import_node_path9.default.join(contentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
4658
- if (!(0, import_node_fs6.existsSync)(manifestPath)) return null;
4727
+ const manifestPath = import_node_path11.default.join(contentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
4728
+ if (!(0, import_node_fs7.existsSync)(manifestPath)) return null;
4659
4729
  try {
4660
4730
  const parsed = JSON.parse(
4661
- (0, import_node_fs6.readFileSync)(manifestPath, "utf8")
4731
+ (0, import_node_fs7.readFileSync)(manifestPath, "utf8")
4662
4732
  );
4663
4733
  if (parsed.version !== 1 || !parsed.pages || typeof parsed.pages !== "object") return null;
4664
4734
  const pages = Object.fromEntries(
@@ -4677,15 +4747,15 @@ function createFarmDocsLastModifiedManifest(contentDir, options = {}) {
4677
4747
  const fallbackDate = (options.now ?? /* @__PURE__ */ new Date()).toISOString();
4678
4748
  const pages = {};
4679
4749
  for (const sourcePath of discoverDocsSourceFiles(resolvedContentDir)) {
4680
- const relativePath2 = normalizeRelativePath(import_node_path9.default.relative(resolvedContentDir, sourcePath));
4681
- pages[relativePath2] = gitDates[relativePath2] || (options.fallback === "now" ? fallbackDate : (0, import_node_fs6.statSync)(sourcePath).mtime.toISOString());
4750
+ const relativePath2 = normalizeRelativePath(import_node_path11.default.relative(resolvedContentDir, sourcePath));
4751
+ pages[relativePath2] = gitDates[relativePath2] || (options.fallback === "now" ? fallbackDate : (0, import_node_fs7.statSync)(sourcePath).mtime.toISOString());
4682
4752
  }
4683
4753
  return { version: 1, pages };
4684
4754
  }
4685
4755
  function getLastModifiedManifest(contentDir) {
4686
4756
  const resolvedContentDir = resolveExistingPath(contentDir);
4687
- const manifestPath = import_node_path9.default.join(resolvedContentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
4688
- const manifestStat = (0, import_node_fs6.existsSync)(manifestPath) ? (0, import_node_fs6.statSync)(manifestPath) : null;
4757
+ const manifestPath = import_node_path11.default.join(resolvedContentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
4758
+ const manifestStat = (0, import_node_fs7.existsSync)(manifestPath) ? (0, import_node_fs7.statSync)(manifestPath) : null;
4689
4759
  const signature = manifestStat ? `file:${manifestStat.mtimeMs}:${manifestStat.size}` : "generated";
4690
4760
  const cached = manifestCache.get(resolvedContentDir);
4691
4761
  if (cached?.signature === signature) return cached.manifest;
@@ -4698,21 +4768,21 @@ function resolveFarmDocsPageLastModified(contentDir, sourcePath, frontmatter) {
4698
4768
  if (configured) return configured;
4699
4769
  const resolvedContentDir = resolveExistingPath(contentDir);
4700
4770
  const resolvedSourcePath = resolveExistingPath(sourcePath);
4701
- const relativePath2 = normalizeRelativePath(import_node_path9.default.relative(resolvedContentDir, resolvedSourcePath));
4702
- const isInsideContentDir = relativePath2 !== ".." && !relativePath2.startsWith("../") && !import_node_path9.default.isAbsolute(relativePath2);
4771
+ const relativePath2 = normalizeRelativePath(import_node_path11.default.relative(resolvedContentDir, resolvedSourcePath));
4772
+ const isInsideContentDir = relativePath2 !== ".." && !relativePath2.startsWith("../") && !import_node_path11.default.isAbsolute(relativePath2);
4703
4773
  if (isInsideContentDir) {
4704
4774
  const generated = getLastModifiedManifest(resolvedContentDir).pages[relativePath2];
4705
4775
  if (generated) return generated;
4706
4776
  }
4707
- return (0, import_node_fs6.statSync)(resolvedSourcePath).mtime.toISOString();
4777
+ return (0, import_node_fs7.statSync)(resolvedSourcePath).mtime.toISOString();
4708
4778
  }
4709
- var import_node_child_process, import_node_fs6, import_node_path9, FARM_DOCS_LAST_MODIFIED_MANIFEST, COMMIT_MARKER, DOCS_FILE_EXTENSIONS, manifestCache;
4779
+ var import_node_child_process, import_node_fs7, import_node_path11, FARM_DOCS_LAST_MODIFIED_MANIFEST, COMMIT_MARKER, DOCS_FILE_EXTENSIONS, manifestCache;
4710
4780
  var init_last_modified = __esm({
4711
4781
  "src/docs/last-modified.ts"() {
4712
4782
  "use strict";
4713
4783
  import_node_child_process = require("child_process");
4714
- import_node_fs6 = require("fs");
4715
- import_node_path9 = __toESM(require("path"));
4784
+ import_node_fs7 = require("fs");
4785
+ import_node_path11 = __toESM(require("path"));
4716
4786
  FARM_DOCS_LAST_MODIFIED_MANIFEST = ".farm-docs-last-modified.json";
4717
4787
  COMMIT_MARKER = "__FARM_DOCS_COMMIT__";
4718
4788
  DOCS_FILE_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx"]);
@@ -4774,8 +4844,8 @@ var init_pixel_border_css = __esm({
4774
4844
  --scrollbar-thumb-hover: var(--color-fd-ring);
4775
4845
  --scrollbar-track: transparent;
4776
4846
  --radius: 0px;
4777
- --font-geist-sans: "Geist Sans";
4778
- --font-geist-mono: "Geist Mono";
4847
+ --font-geist-sans: ui-sans-serif, system-ui, sans-serif;
4848
+ --font-geist-mono: ui-monospace, monospace;
4779
4849
  }
4780
4850
 
4781
4851
  .dark {
@@ -4788,28 +4858,6 @@ body,
4788
4858
  background-color: var(--color-fd-background) !important;
4789
4859
  }
4790
4860
 
4791
- @font-face {
4792
- font-family: "Geist Sans";
4793
- font-display: block;
4794
- font-style: normal;
4795
- font-weight: 100 900;
4796
- src:
4797
- local("Geist Sans"),
4798
- url("/assets/Geist-Variable-CrgPqtmy.woff2") format("woff2"),
4799
- url("/node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2") format("woff2");
4800
- }
4801
-
4802
- @font-face {
4803
- font-family: "Geist Mono";
4804
- font-display: block;
4805
- font-style: normal;
4806
- font-weight: 100 900;
4807
- src:
4808
- local("Geist Mono"),
4809
- url("/assets/GeistMono-Variable-BNLlm6Cd.woff2") format("woff2"),
4810
- url("/node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2") format("woff2");
4811
- }
4812
-
4813
4861
  * {
4814
4862
  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
4815
4863
  scrollbar-width: thin;
@@ -6016,10 +6064,10 @@ function isFarmDocsSearchEnabled(docs) {
6016
6064
  }
6017
6065
  function resolveFarmDocsSearchClientModule(root) {
6018
6066
  try {
6019
- const requireFromApp = (0, import_node_module4.createRequire)(import_node_path10.default.join(import_node_path10.default.resolve(root), "package.json"));
6067
+ const requireFromApp = (0, import_node_module4.createRequire)(import_node_path12.default.join(import_node_path12.default.resolve(root), "package.json"));
6020
6068
  const themeEntry = requireFromApp.resolve("@farming-labs/theme");
6021
- const commandSearchModule = import_node_path10.default.join(import_node_path10.default.dirname(themeEntry), "docs-command-search.mjs");
6022
- if ((0, import_node_fs7.existsSync)(commandSearchModule)) return commandSearchModule;
6069
+ const commandSearchModule = import_node_path12.default.join(import_node_path12.default.dirname(themeEntry), "docs-command-search.mjs");
6070
+ if ((0, import_node_fs8.existsSync)(commandSearchModule)) return commandSearchModule;
6023
6071
  } catch {
6024
6072
  }
6025
6073
  return void 0;
@@ -6158,13 +6206,13 @@ async function mountFarmDocsSearch() {
6158
6206
  window.__FARM_MOUNT_DOCS_SEARCH__ = mountFarmDocsSearch;
6159
6207
  `;
6160
6208
  }
6161
- var import_node_fs7, import_node_module4, import_node_path10;
6209
+ var import_node_fs8, import_node_module4, import_node_path12;
6162
6210
  var init_search_client = __esm({
6163
6211
  "src/docs/search-client.ts"() {
6164
6212
  "use strict";
6165
- import_node_fs7 = require("fs");
6213
+ import_node_fs8 = require("fs");
6166
6214
  import_node_module4 = require("module");
6167
- import_node_path10 = __toESM(require("path"));
6215
+ import_node_path12 = __toESM(require("path"));
6168
6216
  __name(isFarmDocsSearchEnabled, "isFarmDocsSearchEnabled");
6169
6217
  __name(resolveFarmDocsSearchClientModule, "resolveFarmDocsSearchClientModule");
6170
6218
  __name(generateFarmDocsSearchBootstrapRuntime, "generateFarmDocsSearchBootstrapRuntime");
@@ -6172,11 +6220,418 @@ var init_search_client = __esm({
6172
6220
  }
6173
6221
  });
6174
6222
 
6223
+ // src/docs/social-image.ts
6224
+ function normalizeEntry(entry) {
6225
+ if (!entry || entry === "/") return "";
6226
+ return `/${entry.replace(/^\/+|\/+$/g, "")}`;
6227
+ }
6228
+ function encodeSlugPath(slug) {
6229
+ return slug.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
6230
+ }
6231
+ function getSocialImageConfig(docs) {
6232
+ return typeof docs.config.socialImage === "object" && docs.config.socialImage ? docs.config.socialImage : void 0;
6233
+ }
6234
+ function getNavTitle(docs) {
6235
+ if (typeof docs.config.nav !== "object" || !docs.config.nav || !("title" in docs.config.nav)) {
6236
+ return "Farm.js";
6237
+ }
6238
+ return String(docs.config.nav.title || "Farm.js");
6239
+ }
6240
+ function isFalse(value) {
6241
+ return value ? /^(false|no|none|off|disabled)$/i.test(value.trim()) : false;
6242
+ }
6243
+ function isExternalOrRootPath(value) {
6244
+ return /^(https?:)?\/\//i.test(value) || value.startsWith("/");
6245
+ }
6246
+ function isFarmDocsSocialImageEnabled(page2, docs) {
6247
+ if (docs.config.socialImage === false || getSocialImageConfig(docs)?.enabled === false) {
6248
+ return false;
6249
+ }
6250
+ return !isFalse(page2.frontmatter.socialImage);
6251
+ }
6252
+ function getFarmDocsCustomSocialImage(page2) {
6253
+ const value = page2.frontmatter.socialImage || page2.frontmatter.openGraphImage || page2.frontmatter.ogImage;
6254
+ return value && !isFalse(value) && isExternalOrRootPath(value) ? value : void 0;
6255
+ }
6256
+ function resolveIllustration(page2) {
6257
+ const explicit = page2.frontmatter.socialIllustration?.toLowerCase();
6258
+ if (explicit === "auth" || explicit === "cache" || explicit === "cli" || explicit === "integrations" || explicit === "project" || explicit === "routing" || explicit === "runtime") {
6259
+ return explicit;
6260
+ }
6261
+ const source = `${page2.slug} ${page2.title} ${page2.section || ""}`.toLowerCase();
6262
+ if (/auth|session|security|csrf/.test(source)) return "auth";
6263
+ if (/cache|ppr|render|static|revalid|stream/.test(source)) return "cache";
6264
+ if (/integration|stripe|prisma|better.auth|email|job|workflow/.test(source)) {
6265
+ return "integrations";
6266
+ }
6267
+ if (/cli|test|deploy|preview|upgrade|migration|install/.test(source)) return "cli";
6268
+ if (/project|structure|config|environment|markdown|docs.engine/.test(source)) {
6269
+ return "project";
6270
+ }
6271
+ if (/rout|endpoint|middleware|navigation|server.function|server.action|api/.test(source)) {
6272
+ return "routing";
6273
+ }
6274
+ return "runtime";
6275
+ }
6276
+ function createHashValue(value) {
6277
+ return (0, import_node_crypto4.createHash)("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 16);
6278
+ }
6279
+ function resolveBaseUrl(requestUrl, config) {
6280
+ return config?.baseUrl ? new URL(config.baseUrl) : new URL(requestUrl.origin);
6281
+ }
6282
+ function createFarmDocsSocialImageDescriptor(page2, docs, requestUrl) {
6283
+ const config = getSocialImageConfig(docs);
6284
+ const siteName = config?.siteName || getNavTitle(docs);
6285
+ const brand = config?.brand || siteName;
6286
+ const title = page2.frontmatter.socialTitle || page2.title;
6287
+ const description = page2.frontmatter.socialDescription || page2.description || docs.config.metadata?.description || `Documentation for ${siteName}`;
6288
+ const section = page2.frontmatter.section || page2.section || "Documentation";
6289
+ const illustration = resolveIllustration(page2);
6290
+ const entry = normalizeEntry(docs.entry);
6291
+ const slugPath = encodeSlugPath(page2.slug) || "index";
6292
+ const imagePath = `${entry}/_social/${slugPath}/opengraph-image.svg`;
6293
+ const baseUrl = resolveBaseUrl(requestUrl, config);
6294
+ const pageUrl = new URL(page2.href, baseUrl).href;
6295
+ const imageAlt = `${title} \u2014 ${siteName} documentation`;
6296
+ const hash = createHashValue({
6297
+ version: FARM_DOCS_SOCIAL_IMAGE_VERSION,
6298
+ title,
6299
+ description,
6300
+ section,
6301
+ siteName,
6302
+ brand,
6303
+ pageHref: page2.href,
6304
+ illustration,
6305
+ fonts: config?.fonts
6306
+ });
6307
+ return {
6308
+ title,
6309
+ description,
6310
+ section,
6311
+ siteName,
6312
+ brand,
6313
+ pageHref: page2.href,
6314
+ pageUrl,
6315
+ imagePath,
6316
+ imageUrl: new URL(`${imagePath}?v=${hash}`, baseUrl).href,
6317
+ imageAlt,
6318
+ illustration,
6319
+ fonts: config?.fonts,
6320
+ hash
6321
+ };
6322
+ }
6323
+ function getFarmDocsSocialImageSlug(docs, requestUrl) {
6324
+ const entry = normalizeEntry(docs.entry);
6325
+ const prefix = `${entry}/_social/`;
6326
+ const suffix = "/opengraph-image.svg";
6327
+ if (!requestUrl.pathname.startsWith(prefix) || !requestUrl.pathname.endsWith(suffix)) {
6328
+ return null;
6329
+ }
6330
+ const rawSlug = requestUrl.pathname.slice(prefix.length, -suffix.length);
6331
+ if (!rawSlug) return null;
6332
+ try {
6333
+ const slug = rawSlug.split("/").map((segment) => decodeURIComponent(segment)).join("/");
6334
+ if (slug.split("/").some((segment) => !segment || segment === ".." || segment === ".")) {
6335
+ return null;
6336
+ }
6337
+ return slug === "index" ? "" : slug;
6338
+ } catch {
6339
+ return null;
6340
+ }
6341
+ }
6342
+ function escapeXml(value) {
6343
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
6344
+ }
6345
+ function truncate(value, length) {
6346
+ if (value.length <= length) return value;
6347
+ return `${value.slice(0, Math.max(0, length - 1)).trimEnd()}\u2026`;
6348
+ }
6349
+ function wrapText(value, maxCharacters, maxLines) {
6350
+ const words = value.trim().split(/\s+/).filter(Boolean);
6351
+ const lines = [];
6352
+ let current = "";
6353
+ let consumedWords = 0;
6354
+ for (const word of words) {
6355
+ const candidate = current ? `${current} ${word}` : word;
6356
+ if (candidate.length <= maxCharacters || !current) {
6357
+ current = candidate;
6358
+ consumedWords += 1;
6359
+ continue;
6360
+ }
6361
+ lines.push(current);
6362
+ if (lines.length === maxLines) break;
6363
+ current = word;
6364
+ consumedWords += 1;
6365
+ }
6366
+ if (current && lines.length < maxLines) lines.push(current);
6367
+ if (consumedWords < words.length && lines.length) {
6368
+ lines[lines.length - 1] = truncate(`${lines[lines.length - 1]}\u2026`, maxCharacters);
6369
+ }
6370
+ return lines;
6371
+ }
6372
+ function renderTextLines(lines, x, y, lineHeight, attributes) {
6373
+ return lines.map(
6374
+ (line, index) => `<text x="${x}" y="${y + index * lineHeight}" ${attributes}>${escapeXml(line)}</text>`
6375
+ ).join("");
6376
+ }
6377
+ function renderEmbeddedFont(name, source, weight) {
6378
+ if (!source) return "";
6379
+ return `@font-face{font-family:"${name}";src:url("${escapeXml(source)}") format("woff2");font-style:normal;font-weight:${weight};font-display:block;}`;
6380
+ }
6381
+ function renderArrow(x1, y, x2, color = "#8b8b8b") {
6382
+ 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}"/>`;
6383
+ }
6384
+ function renderPanelHeader(label, status) {
6385
+ 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>`;
6386
+ }
6387
+ function renderRoutingIllustration() {
6388
+ return `${renderPanelHeader("ROUTE BLUEPRINT", "TYPED")}
6389
+ <rect x="819" y="296" width="92" height="54" fill="#a3a3a3"/>
6390
+ <text x="832" y="327" class="diagram-title ink">src/app</text>
6391
+ <path d="M911 323h41M952 232v181M952 232h44M952 323h44M952 413h44" fill="none" stroke="#696969"/>
6392
+ <rect x="996" y="205" width="138" height="54" fill="#0a0a0a" stroke="#414141"/>
6393
+ <text x="1010" y="228" class="diagram-title">/</text><text x="1010" y="246" class="micro">STATIC</text>
6394
+ <rect x="996" y="296" width="138" height="54" fill="#0a0a0a" stroke="#414141"/>
6395
+ <text x="1010" y="319" class="diagram-title">/blog/:slug</text><text x="1010" y="337" class="micro">DYNAMIC</text>
6396
+ <rect x="996" y="386" width="138" height="55" fill="#f0f0f0"/>
6397
+ <text x="1010" y="410" class="diagram-title ink">/docs/:path*</text><text x="1010" y="428" class="micro ink-muted">CATCH-ALL</text>
6398
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
6399
+ <text x="819" y="475" class="label bright">FILE ROUTES</text><text x="1134" y="475" class="label" text-anchor="end">TYPES INCLUDED</text>`;
6400
+ }
6401
+ function renderAuthIllustration() {
6402
+ return `${renderPanelHeader("REQUEST GUARD", "SERVER VERIFIED")}
6403
+ <rect x="819" y="269" width="86" height="54" fill="#0a0a0a" stroke="#454545"/>
6404
+ <text x="837" y="292" class="micro bright">REQUEST</text><text x="837" y="310" class="diagram-title">GET /app</text>
6405
+ ${renderArrow(905, 296, 943)}
6406
+ <rect x="943" y="224" width="104" height="118" fill="#f0f0f0"/>
6407
+ <path d="M974 271v-12a21 21 0 0 1 42 0v12M968 271h54v45h-54z" fill="#050505"/>
6408
+ <circle cx="995" cy="292" r="4" fill="#f0f0f0"/><path d="M995 296v8" stroke="#f0f0f0" stroke-width="3"/>
6409
+ <text x="995" y="332" class="micro ink" text-anchor="middle">auth()</text>
6410
+ ${renderArrow(1047, 296, 1071)}
6411
+ <rect x="1071" y="269" width="63" height="54" fill="#0a0a0a" stroke="#454545"/>
6412
+ <text x="1082" y="292" class="micro bright">ALLOW</text><text x="1082" y="310" class="micro">/app</text>
6413
+ <rect x="819" y="372" width="315" height="38" fill="#090909" stroke="#3d3d3d"/>
6414
+ <rect x="832" y="385" width="11" height="11" fill="#a3a3a3"/>
6415
+ <text x="856" y="395" class="code">session.user / verified</text>
6416
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
6417
+ <text x="819" y="475" class="label bright">SESSION TYPED</text><text x="1134" y="475" class="label" text-anchor="end">CSRF PROTECTED</text>`;
6418
+ }
6419
+ function renderCacheIllustration() {
6420
+ return `${renderPanelHeader("PAGE / PRODUCT", "CACHE + PPR")}
6421
+ <rect x="819" y="204" width="315" height="54" fill="#f0f0f0"/>
6422
+ <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>
6423
+ <rect x="819" y="271" width="198" height="76" fill="#a3a3a3"/>
6424
+ <text x="836" y="301" class="diagram-title ink">CACHED DATA</text><text x="836" y="326" class="micro ink-muted">tag: products</text>
6425
+ <rect x="1029" y="271" width="105" height="76" fill="#0a0a0a" stroke="#555"/>
6426
+ <text x="1045" y="301" class="diagram-title">LIVE</text><text x="1045" y="326" class="micro">stream</text>
6427
+ <path d="M1081 347v43m-8-9 8 9 8-9" fill="none" stroke="#d4d4d4"/>
6428
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
6429
+ <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>`;
6430
+ }
6431
+ function renderIntegrationsIllustration() {
6432
+ return `${renderPanelHeader("INTEGRATION GRAPH", "ONE PRODUCT")}
6433
+ <path d="M976 309H879V247M976 309H879v87M1030 309h93V247M1030 309h93v87" fill="none" stroke="#4a4a4a"/>
6434
+ <rect x="819" y="214" width="120" height="57" fill="#0a0a0a" stroke="#4b4b4b"/><text x="839" y="247" class="diagram-title">BETTER AUTH</text>
6435
+ <rect x="819" y="368" width="120" height="57" fill="#0a0a0a" stroke="#4b4b4b"/><text x="851" y="401" class="diagram-title">PRISMA</text>
6436
+ <rect x="1061" y="214" width="73" height="57" fill="#0a0a0a" stroke="#4b4b4b"/><text x="1077" y="247" class="diagram-title">STRIPE</text>
6437
+ <rect x="1061" y="368" width="73" height="57" fill="#a3a3a3"/><text x="1075" y="401" class="diagram-title ink">VERCEL</text>
6438
+ <rect x="976" y="277" width="94" height="64" fill="#f0f0f0"/>
6439
+ <text x="994" y="304" class="diagram-title ink">FARM.JS</text><text x="994" y="324" class="micro ink-muted">CORE</text>
6440
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
6441
+ <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>`;
6442
+ }
6443
+ function renderCliIllustration() {
6444
+ return `${renderPanelHeader("TERMINAL", "PRODUCTION OUTPUT")}
6445
+ <rect x="819" y="203" width="315" height="213" fill="#050505" stroke="#555"/>
6446
+ <line x1="819" y1="236" x2="1134" y2="236" stroke="#555"/>
6447
+ <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"/>
6448
+ <text x="837" y="273" class="code bright"><tspan fill="#7a7a7a">$</tspan> farm build</text>
6449
+ <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"/>
6450
+ <text x="861" y="303" class="code">64 routes discovered</text>
6451
+ <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"/>
6452
+ <text x="861" y="330" class="code">client + server bundles</text>
6453
+ <rect x="837" y="355" width="126" height="30" fill="#f0f0f0"/><text x="853" y="374" class="micro ink">BUILD COMPLETE</text>
6454
+ <text x="1115" y="374" class="micro" text-anchor="end">/dist</text>
6455
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
6456
+ <text x="819" y="475" class="label bright">REAL COMMANDS</text><text x="1134" y="475" class="label" text-anchor="end">842MS</text>`;
6457
+ }
6458
+ function renderProjectIllustration() {
6459
+ return `${renderPanelHeader("PROJECT MAP", "CONVENTION TYPED")}
6460
+ <rect x="819" y="202" width="315" height="216" fill="#050505" stroke="#555"/>
6461
+ <text x="837" y="227" class="diagram-title bright">my-app/</text>
6462
+ <path d="M847 243v143M847 259h23M847 293h23M847 327h23M847 361h23" fill="none" stroke="#666"/>
6463
+ <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>
6464
+ <rect x="870" y="277" width="246" height="33" fill="#f0f0f0"/><text x="886" y="298" class="code ink">src/app/page.tsx</text>
6465
+ <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>
6466
+ <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>
6467
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
6468
+ <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>`;
6469
+ }
6470
+ function renderRuntimeIllustration() {
6471
+ return `${renderPanelHeader("PRODUCT ARCHITECTURE", "FULL STACK")}
6472
+ <rect x="819" y="211" width="315" height="55" fill="#f0f0f0"/>
6473
+ <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>
6474
+ <path d="M976 266v20m0 61v20" stroke="#727272"/>
6475
+ <rect x="855" y="286" width="242" height="61" fill="#a3a3a3"/>
6476
+ <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>
6477
+ <rect x="819" y="367" width="315" height="55" fill="#4c4c4c"/>
6478
+ <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>
6479
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
6480
+ <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>`;
6481
+ }
6482
+ function renderIllustration(type) {
6483
+ switch (type) {
6484
+ case "auth":
6485
+ return renderAuthIllustration();
6486
+ case "cache":
6487
+ return renderCacheIllustration();
6488
+ case "cli":
6489
+ return renderCliIllustration();
6490
+ case "integrations":
6491
+ return renderIntegrationsIllustration();
6492
+ case "project":
6493
+ return renderProjectIllustration();
6494
+ case "routing":
6495
+ return renderRoutingIllustration();
6496
+ default:
6497
+ return renderRuntimeIllustration();
6498
+ }
6499
+ }
6500
+ function renderFarmDocsSocialImageSvg(descriptor) {
6501
+ const titleLines = wrapText(descriptor.title, 22, 2);
6502
+ const longestTitleLine = Math.max(...titleLines.map((line) => line.length));
6503
+ const titleSize = titleLines.length === 1 ? longestTitleLine > 17 ? 62 : 74 : 56;
6504
+ const titleLineHeight = Math.round(titleSize * 0.98);
6505
+ const titleY = titleLines.length === 1 ? 294 : 266;
6506
+ const descriptionY = titleY + (titleLines.length - 1) * titleLineHeight + 59;
6507
+ const descriptionLines = wrapText(descriptor.description, 68, 2);
6508
+ const routeY = Math.max(418, descriptionY + descriptionLines.length * 29 + 24);
6509
+ const section = truncate(descriptor.section.toUpperCase(), 27);
6510
+ const route = truncate(descriptor.pageHref, 42);
6511
+ const footerUrl = truncate(descriptor.pageUrl.replace(/^https?:\/\//, ""), 70);
6512
+ const brand = truncate(descriptor.brand.toUpperCase(), 25);
6513
+ const titleId = `farm-docs-og-${descriptor.hash}`;
6514
+ return `<?xml version="1.0" encoding="UTF-8"?>
6515
+ <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">
6516
+ <title id="${titleId}-title">${escapeXml(descriptor.imageAlt)}</title>
6517
+ <desc id="${titleId}-description">${escapeXml(descriptor.description)}</desc>
6518
+ <metadata>${escapeXml(JSON.stringify({ generator: "Farm.js docs", page: descriptor.pageUrl, illustration: descriptor.illustration }))}</metadata>
6519
+ <style type="text/css">
6520
+ ${renderEmbeddedFont("Farm Sans", descriptor.fonts?.sans, "100 900")}
6521
+ ${renderEmbeddedFont("Farm Mono", descriptor.fonts?.mono, "100 900")}
6522
+ ${renderEmbeddedFont("Farm Pixel", descriptor.fonts?.display, "400 700")}
6523
+ .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}
6524
+ </style>
6525
+ <defs>
6526
+ <pattern id="canvas-grid" width="40" height="40" patternUnits="userSpaceOnUse"><path d="M40 0H0V40" fill="none" stroke="#151515"/></pattern>
6527
+ <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>
6528
+ <clipPath id="frame-clip"><rect x="28" y="28" width="1144" height="574"/></clipPath>
6529
+ </defs>
6530
+ <rect width="1200" height="630" fill="#050505"/>
6531
+ <rect width="1200" height="630" fill="url(#canvas-grid)"/>
6532
+ <g clip-path="url(#frame-clip)">
6533
+ <rect x="28" y="28" width="1144" height="574" fill="#020202"/>
6534
+ <rect x="782" y="114" width="390" height="411" fill="url(#diagonal)"/>
6535
+ </g>
6536
+ <rect x="28.5" y="28.5" width="1143" height="573" fill="none" stroke="#323232"/>
6537
+ <path d="M28 114.5h1144M28 525.5h1144M781.5 114v411" fill="none" stroke="#303030"/>
6538
+
6539
+ <g transform="translate(71 51)">
6540
+ <rect width="11" height="11" rx="1" fill="#f5f5f4"/><rect x="15" width="28" height="11" rx="1" fill="#f5f5f4"/>
6541
+ <rect y="15" width="11" height="11" rx="1" fill="#9b9b9b"/><rect x="15" y="15" width="28" height="11" rx="1" fill="#9b9b9b"/>
6542
+ <rect y="30" width="11" height="11" rx="1" fill="#5f5f5f"/><rect x="15" y="30" width="20" height="11" rx="1" fill="#5f5f5f"/>
6543
+ <text x="61" y="24" class="mono bright" font-size="24" font-weight="550">FARM<tspan fill="#777">.JS</tspan></text>
6544
+ <text x="61" y="43" class="label">BY FARMING LABS</text>
6545
+ </g>
6546
+ <text x="1134" y="72" class="label" text-anchor="end">${escapeXml(brand)} \xB7 DOCUMENTATION</text>
6547
+
6548
+ <text x="71" y="198" class="mono bright" font-size="13" font-weight="650">DOCS</text>
6549
+ <line x1="117" y1="193" x2="155" y2="193" stroke="#696969"/>
6550
+ <text x="166" y="198" class="mono" fill="#9b9b9b" font-size="13">${escapeXml(section)}</text>
6551
+ ${renderTextLines(titleLines, 71, titleY, titleLineHeight, `class="pixel bright" font-size="${titleSize}" font-weight="500" letter-spacing="-2.4"`)}
6552
+ ${renderTextLines(descriptionLines, 71, descriptionY, 29, 'class="sans" fill="#a3a3a3" font-size="20" font-weight="430" letter-spacing="-.3"')}
6553
+ <rect x="71" y="${routeY}" width="${Math.min(390, 56 + route.length * 8)}" height="36" fill="#050505" stroke="#3c3c3c"/>
6554
+ <text x="85" y="${routeY + 23}" class="mono bright" font-size="11" font-weight="600">GET</text>
6555
+ <text x="119" y="${routeY + 23}" class="mono" fill="#898989" font-size="11">${escapeXml(route)}</text>
6556
+
6557
+ ${renderIllustration(descriptor.illustration)}
6558
+
6559
+ <text x="71" y="570" class="mono bright" font-size="13" font-weight="550">${escapeXml(footerUrl)}</text>
6560
+ <text x="1134" y="570" class="mono" fill="#9b9b9b" font-size="12" text-anchor="end">FARM.JS DOCUMENTATION</text>
6561
+ <path d="M20 20h10M20 20v10M1180 20h-10M1180 20v10M20 610h10M20 610v-10M1180 610h-10M1180 610v-10" fill="none" stroke="#9b9b9b"/>
6562
+ </svg>`;
6563
+ }
6564
+ function renderFarmDocsSocialMetadata(descriptor, customImage) {
6565
+ const imageUrl = customImage ? new URL(customImage, descriptor.pageUrl).href : descriptor.imageUrl;
6566
+ const customExtension = customImage?.toLowerCase().split(/[?#]/, 1)[0];
6567
+ const imageType = customExtension?.endsWith(".webp") ? "image/webp" : customExtension?.endsWith(".png") ? "image/png" : customExtension?.match(/\.jpe?g$/) ? "image/jpeg" : "image/svg+xml";
6568
+ const tag = /* @__PURE__ */ __name((property, content, name = false) => `<meta ${name ? "name" : "property"}="${property}" content="${escapeXml(content)}">`, "tag");
6569
+ return [
6570
+ tag("og:type", "article"),
6571
+ tag("og:site_name", descriptor.siteName),
6572
+ tag("og:title", descriptor.title),
6573
+ tag("og:description", descriptor.description),
6574
+ tag("og:url", descriptor.pageUrl),
6575
+ tag("og:image", imageUrl),
6576
+ tag("og:image:type", imageType),
6577
+ ...customImage ? [] : [
6578
+ tag("og:image:width", String(FARM_DOCS_SOCIAL_IMAGE_WIDTH)),
6579
+ tag("og:image:height", String(FARM_DOCS_SOCIAL_IMAGE_HEIGHT))
6580
+ ],
6581
+ tag("og:image:alt", descriptor.imageAlt),
6582
+ tag("twitter:card", "summary_large_image", true),
6583
+ tag("twitter:title", descriptor.title, true),
6584
+ tag("twitter:description", descriptor.description, true),
6585
+ tag("twitter:image", imageUrl, true),
6586
+ tag("twitter:image:alt", descriptor.imageAlt, true)
6587
+ ].join("\n ");
6588
+ }
6589
+ var import_node_crypto4, FARM_DOCS_SOCIAL_IMAGE_WIDTH, FARM_DOCS_SOCIAL_IMAGE_HEIGHT, FARM_DOCS_SOCIAL_IMAGE_VERSION;
6590
+ var init_social_image = __esm({
6591
+ "src/docs/social-image.ts"() {
6592
+ "use strict";
6593
+ import_node_crypto4 = require("crypto");
6594
+ FARM_DOCS_SOCIAL_IMAGE_WIDTH = 1200;
6595
+ FARM_DOCS_SOCIAL_IMAGE_HEIGHT = 630;
6596
+ FARM_DOCS_SOCIAL_IMAGE_VERSION = 3;
6597
+ __name(normalizeEntry, "normalizeEntry");
6598
+ __name(encodeSlugPath, "encodeSlugPath");
6599
+ __name(getSocialImageConfig, "getSocialImageConfig");
6600
+ __name(getNavTitle, "getNavTitle");
6601
+ __name(isFalse, "isFalse");
6602
+ __name(isExternalOrRootPath, "isExternalOrRootPath");
6603
+ __name(isFarmDocsSocialImageEnabled, "isFarmDocsSocialImageEnabled");
6604
+ __name(getFarmDocsCustomSocialImage, "getFarmDocsCustomSocialImage");
6605
+ __name(resolveIllustration, "resolveIllustration");
6606
+ __name(createHashValue, "createHashValue");
6607
+ __name(resolveBaseUrl, "resolveBaseUrl");
6608
+ __name(createFarmDocsSocialImageDescriptor, "createFarmDocsSocialImageDescriptor");
6609
+ __name(getFarmDocsSocialImageSlug, "getFarmDocsSocialImageSlug");
6610
+ __name(escapeXml, "escapeXml");
6611
+ __name(truncate, "truncate");
6612
+ __name(wrapText, "wrapText");
6613
+ __name(renderTextLines, "renderTextLines");
6614
+ __name(renderEmbeddedFont, "renderEmbeddedFont");
6615
+ __name(renderArrow, "renderArrow");
6616
+ __name(renderPanelHeader, "renderPanelHeader");
6617
+ __name(renderRoutingIllustration, "renderRoutingIllustration");
6618
+ __name(renderAuthIllustration, "renderAuthIllustration");
6619
+ __name(renderCacheIllustration, "renderCacheIllustration");
6620
+ __name(renderIntegrationsIllustration, "renderIntegrationsIllustration");
6621
+ __name(renderCliIllustration, "renderCliIllustration");
6622
+ __name(renderProjectIllustration, "renderProjectIllustration");
6623
+ __name(renderRuntimeIllustration, "renderRuntimeIllustration");
6624
+ __name(renderIllustration, "renderIllustration");
6625
+ __name(renderFarmDocsSocialImageSvg, "renderFarmDocsSocialImageSvg");
6626
+ __name(renderFarmDocsSocialMetadata, "renderFarmDocsSocialMetadata");
6627
+ }
6628
+ });
6629
+
6175
6630
  // src/docs/handler.ts
6176
6631
  function trimSlashes2(value) {
6177
6632
  return value.replace(/^\/+|\/+$/g, "");
6178
6633
  }
6179
- function normalizeEntry(entry) {
6634
+ function normalizeEntry2(entry) {
6180
6635
  if (!entry || entry === "/") return "/";
6181
6636
  return `/${trimSlashes2(entry)}`;
6182
6637
  }
@@ -6188,10 +6643,10 @@ function resolveFarmDocsFavicon(docs, options) {
6188
6643
  if (typeof configuredFavicon === "string" && configuredFavicon.trim()) {
6189
6644
  return configuredFavicon.trim();
6190
6645
  }
6191
- const publicDir = import_node_path11.default.join(import_node_path11.default.resolve(options.root), "public");
6646
+ const publicDir = import_node_path13.default.join(import_node_path13.default.resolve(options.root), "public");
6192
6647
  for (const fileName of FARM_DOCS_FAVICON_FILES) {
6193
- const faviconPath = import_node_path11.default.join(publicDir, fileName);
6194
- if ((0, import_node_fs8.existsSync)(faviconPath) && (0, import_node_fs8.statSync)(faviconPath).isFile()) {
6648
+ const faviconPath = import_node_path13.default.join(publicDir, fileName);
6649
+ if ((0, import_node_fs9.existsSync)(faviconPath) && (0, import_node_fs9.statSync)(faviconPath).isFile()) {
6195
6650
  return `/${fileName}`;
6196
6651
  }
6197
6652
  }
@@ -6225,10 +6680,10 @@ function isSafeSegment(segment) {
6225
6680
  return segment !== ".." && !segment.includes("/") && !segment.includes("\\");
6226
6681
  }
6227
6682
  function resolveInside(root, target) {
6228
- const resolvedRoot = import_node_path11.default.resolve(root);
6229
- const resolvedTarget = import_node_path11.default.resolve(target);
6230
- const relative4 = import_node_path11.default.relative(resolvedRoot, resolvedTarget);
6231
- if (relative4 === "" || !relative4.startsWith("..") && !import_node_path11.default.isAbsolute(relative4)) {
6683
+ const resolvedRoot = import_node_path13.default.resolve(root);
6684
+ const resolvedTarget = import_node_path13.default.resolve(target);
6685
+ const relative4 = import_node_path13.default.relative(resolvedRoot, resolvedTarget);
6686
+ if (relative4 === "" || !relative4.startsWith("..") && !import_node_path13.default.isAbsolute(relative4)) {
6232
6687
  return resolvedTarget;
6233
6688
  }
6234
6689
  return null;
@@ -6237,36 +6692,36 @@ function isFarmDocsRequest(docs, request) {
6237
6692
  if (!docs?.enabled) return false;
6238
6693
  if (request.method !== "GET" && request.method !== "HEAD") return false;
6239
6694
  const pathname = new URL(request.url).pathname;
6240
- const entry = normalizeEntry(docs.entry);
6695
+ const entry = normalizeEntry2(docs.entry);
6241
6696
  if (entry === "/") return true;
6242
6697
  return pathname === entry || pathname === `${entry}.md` || pathname.startsWith(`${entry}/`);
6243
6698
  }
6244
6699
  function resolveFarmDocsContentDir(docs, options) {
6245
- const root = import_node_path11.default.resolve(options.root);
6700
+ const root = import_node_path13.default.resolve(options.root);
6246
6701
  const srcDir = options.srcDir || "src";
6247
6702
  const configuredContentDir = docs.contentDir || docs.config.contentDir;
6248
6703
  if (configuredContentDir) {
6249
- return import_node_path11.default.isAbsolute(configuredContentDir) ? configuredContentDir : import_node_path11.default.join(root, configuredContentDir);
6704
+ return import_node_path13.default.isAbsolute(configuredContentDir) ? configuredContentDir : import_node_path13.default.join(root, configuredContentDir);
6250
6705
  }
6251
6706
  const entryDir = docs.config.entry || trimSlashes2(docs.entry) || "docs";
6252
- const appDocsDir = import_node_path11.default.join(root, srcDir, "app", entryDir);
6253
- if ((0, import_node_fs8.existsSync)(appDocsDir)) return appDocsDir;
6254
- return import_node_path11.default.join(root, entryDir);
6707
+ const appDocsDir = import_node_path13.default.join(root, srcDir, "app", entryDir);
6708
+ if ((0, import_node_fs9.existsSync)(appDocsDir)) return appDocsDir;
6709
+ return import_node_path13.default.join(root, entryDir);
6255
6710
  }
6256
6711
  function getFarmDocsRouteTypeEntries(docs) {
6257
6712
  if (!docs?.enabled) return [];
6258
- const entry = normalizeEntry(docs.entry);
6713
+ const entry = normalizeEntry2(docs.entry);
6259
6714
  if (entry === "/") return ["/", "/[...docs]"];
6260
6715
  return [entry, `${entry}/[...docs]`];
6261
6716
  }
6262
6717
  function getFarmDocsDocumentNavigationMatchers(docs) {
6263
6718
  if (!docs?.enabled) return [];
6264
- const entry = normalizeEntry(docs.entry);
6719
+ const entry = normalizeEntry2(docs.entry);
6265
6720
  return [entry === "/" ? "/(.*)" : `${entry}(.*)`];
6266
6721
  }
6267
6722
  function getRequestSlug(docs, request) {
6268
6723
  const pathname = new URL(request.url).pathname;
6269
- const entry = normalizeEntry(docs.entry);
6724
+ const entry = normalizeEntry2(docs.entry);
6270
6725
  if (entry === "/") return normalizeSlug(pathname);
6271
6726
  if (pathname === entry || pathname === `${entry}.md`) return "";
6272
6727
  return normalizeSlug(pathname.slice(entry.length));
@@ -6274,14 +6729,14 @@ function getRequestSlug(docs, request) {
6274
6729
  function findDocsPageFile(contentDir, slug) {
6275
6730
  const segments = slug ? slug.split("/").filter(Boolean) : [];
6276
6731
  if (!segments.every(isSafeSegment)) return null;
6277
- const slugDir = import_node_path11.default.join(contentDir, ...segments);
6278
- const candidates = segments.length === 0 ? DOCS_FILE_NAMES.map((filename) => import_node_path11.default.join(contentDir, filename)) : [
6279
- ...DOCS_FILE_NAMES.map((filename) => import_node_path11.default.join(slugDir, filename)),
6280
- ...DOCS_FILE_EXTENSIONS2.map((extension) => import_node_path11.default.join(contentDir, `${slug}${extension}`))
6732
+ const slugDir = import_node_path13.default.join(contentDir, ...segments);
6733
+ const candidates = segments.length === 0 ? DOCS_FILE_NAMES.map((filename) => import_node_path13.default.join(contentDir, filename)) : [
6734
+ ...DOCS_FILE_NAMES.map((filename) => import_node_path13.default.join(slugDir, filename)),
6735
+ ...DOCS_FILE_EXTENSIONS2.map((extension) => import_node_path13.default.join(contentDir, `${slug}${extension}`))
6281
6736
  ];
6282
6737
  for (const candidate of candidates) {
6283
6738
  const safePath = resolveInside(contentDir, candidate);
6284
- if (safePath && (0, import_node_fs8.existsSync)(safePath) && (0, import_node_fs8.statSync)(safePath).isFile()) {
6739
+ if (safePath && (0, import_node_fs9.existsSync)(safePath) && (0, import_node_fs9.statSync)(safePath).isFile()) {
6285
6740
  return safePath;
6286
6741
  }
6287
6742
  }
@@ -6316,7 +6771,7 @@ function titleFromMarkdown2(body, fallback) {
6316
6771
  function loadFarmDocsPage(contentDir, docs, slug) {
6317
6772
  const sourcePath = findDocsPageFile(contentDir, slug);
6318
6773
  if (!sourcePath) return null;
6319
- const source = (0, import_node_fs8.readFileSync)(sourcePath, "utf8");
6774
+ const source = (0, import_node_fs9.readFileSync)(sourcePath, "utf8");
6320
6775
  const { frontmatter, body } = parseFrontmatter(source);
6321
6776
  const title = frontmatter.title || titleFromMarkdown2(body, titleFromSlug(slug));
6322
6777
  const href = createDocsHref(docs.entry, slug);
@@ -6324,6 +6779,7 @@ function loadFarmDocsPage(contentDir, docs, slug) {
6324
6779
  slug,
6325
6780
  title,
6326
6781
  description: frontmatter.description,
6782
+ section: frontmatter.section,
6327
6783
  href,
6328
6784
  sourcePath,
6329
6785
  lastModified: resolveFarmDocsPageLastModified(contentDir, sourcePath, frontmatter),
@@ -6332,15 +6788,15 @@ function loadFarmDocsPage(contentDir, docs, slug) {
6332
6788
  };
6333
6789
  }
6334
6790
  function createDocsHref(entry, slug) {
6335
- const normalizedEntry = normalizeEntry(entry);
6791
+ const normalizedEntry = normalizeEntry2(entry);
6336
6792
  const normalizedSlug = trimSlashes2(slug);
6337
6793
  if (normalizedEntry === "/") return normalizedSlug ? `/${normalizedSlug}` : "/";
6338
6794
  return normalizedSlug ? `${normalizedEntry}/${normalizedSlug}` : normalizedEntry;
6339
6795
  }
6340
6796
  function pathToSlug(contentDir, filePath) {
6341
- const relative4 = import_node_path11.default.relative(contentDir, filePath).replace(/\\/g, "/");
6797
+ const relative4 = import_node_path13.default.relative(contentDir, filePath).replace(/\\/g, "/");
6342
6798
  if (relative4.startsWith("..")) return null;
6343
- const extension = import_node_path11.default.extname(relative4);
6799
+ const extension = import_node_path13.default.extname(relative4);
6344
6800
  if (!DOCS_FILE_EXTENSIONS2.includes(extension)) return null;
6345
6801
  const withoutExtension = relative4.slice(0, -extension.length);
6346
6802
  if (withoutExtension === "page" || withoutExtension === "index") return "";
@@ -6353,12 +6809,12 @@ function loadPage(contentDir, docs, request) {
6353
6809
  return loadFarmDocsPage(contentDir, docs, getRequestSlug(docs, request));
6354
6810
  }
6355
6811
  function discoverFarmDocsPages(contentDir, docs) {
6356
- if (!(0, import_node_fs8.existsSync)(contentDir)) return [];
6812
+ if (!(0, import_node_fs9.existsSync)(contentDir)) return [];
6357
6813
  const pages = [];
6358
6814
  const visit = /* @__PURE__ */ __name((dir) => {
6359
- for (const entry of (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true })) {
6815
+ for (const entry of (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true })) {
6360
6816
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
6361
- const absolutePath = import_node_path11.default.join(dir, entry.name);
6817
+ const absolutePath = import_node_path13.default.join(dir, entry.name);
6362
6818
  if (entry.isDirectory()) {
6363
6819
  visit(absolutePath);
6364
6820
  continue;
@@ -6366,7 +6822,7 @@ function discoverFarmDocsPages(contentDir, docs) {
6366
6822
  if (!entry.isFile()) continue;
6367
6823
  const slug = pathToSlug(contentDir, absolutePath);
6368
6824
  if (slug === null) continue;
6369
- const source = (0, import_node_fs8.readFileSync)(absolutePath, "utf8");
6825
+ const source = (0, import_node_fs9.readFileSync)(absolutePath, "utf8");
6370
6826
  const { frontmatter, body } = parseFrontmatter(source);
6371
6827
  pages.push({
6372
6828
  slug,
@@ -7144,7 +7600,7 @@ function renderPixelBreadcrumb(page2, docs) {
7144
7600
  const parentSegments = segments.slice(0, -1);
7145
7601
  const parentSegment = parentSegments[parentSegments.length - 1];
7146
7602
  const currentSegment = segments[segments.length - 1];
7147
- const entry = normalizeEntry(docs.entry);
7603
+ const entry = normalizeEntry2(docs.entry);
7148
7604
  const parentPath = parentSegments.join("/");
7149
7605
  const parentHref = entry === "/" ? `/${parentPath}` : `${entry}/${parentPath}`;
7150
7606
  return `<nav class="fd-breadcrumb" aria-label="Breadcrumb">
@@ -7182,7 +7638,7 @@ ${items.map(
7182
7638
  </div>`;
7183
7639
  }
7184
7640
  function renderDocsRuntimeScript(docs) {
7185
- const docsEntry = JSON.stringify(normalizeEntry(docs.entry));
7641
+ const docsEntry = JSON.stringify(normalizeEntry2(docs.entry));
7186
7642
  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>`;
7187
7643
  }
7188
7644
  function renderDocsPageActionsRuntimeScript() {
@@ -7197,13 +7653,13 @@ ${renderDocsHashRuntimeScript()}
7197
7653
  ${renderDocsPageActionsRuntimeScript()}`;
7198
7654
  }
7199
7655
  function readCssWithImports(filePath, seen = /* @__PURE__ */ new Set()) {
7200
- const resolved = import_node_path11.default.resolve(filePath);
7656
+ const resolved = import_node_path13.default.resolve(filePath);
7201
7657
  if (seen.has(resolved)) return "";
7202
7658
  seen.add(resolved);
7203
- const source = (0, import_node_fs8.readFileSync)(resolved, "utf8");
7659
+ const source = (0, import_node_fs9.readFileSync)(resolved, "utf8");
7204
7660
  return source.replace(/@import\s+["']([^"']+)["'];?/g, (_match, importPath) => {
7205
7661
  try {
7206
- const importedPath = importPath.startsWith(".") ? import_node_path11.default.resolve(import_node_path11.default.dirname(resolved), importPath) : (0, import_node_module5.createRequire)(resolved).resolve(importPath);
7662
+ const importedPath = importPath.startsWith(".") ? import_node_path13.default.resolve(import_node_path13.default.dirname(resolved), importPath) : (0, import_node_module5.createRequire)(resolved).resolve(importPath);
7207
7663
  return readCssWithImports(importedPath, seen);
7208
7664
  } catch {
7209
7665
  return "";
@@ -7211,11 +7667,11 @@ function readCssWithImports(filePath, seen = /* @__PURE__ */ new Set()) {
7211
7667
  });
7212
7668
  }
7213
7669
  function resolvePixelBorderThemeCss(options) {
7214
- const cacheKey = import_node_path11.default.resolve(options.root);
7670
+ const cacheKey = import_node_path13.default.resolve(options.root);
7215
7671
  const cached = themeCssCache.get(cacheKey);
7216
7672
  if (cached !== void 0) return cached;
7217
7673
  try {
7218
- const requireFromApp = (0, import_node_module5.createRequire)(import_node_path11.default.join(cacheKey, "package.json"));
7674
+ const requireFromApp = (0, import_node_module5.createRequire)(import_node_path13.default.join(cacheKey, "package.json"));
7219
7675
  const themeCssPath = requireFromApp.resolve("@farming-labs/theme/pixel-border/css");
7220
7676
  const requireFromTheme = (0, import_node_module5.createRequire)(themeCssPath);
7221
7677
  const fumadocsCssPath = requireFromTheme.resolve("fumadocs-ui/style.css");
@@ -7228,17 +7684,28 @@ ${readCssWithImports(themeCssPath)}`;
7228
7684
  return "";
7229
7685
  }
7230
7686
  }
7231
- function renderFarmDocsBridgeCss(docs) {
7687
+ function renderFarmDocsBridgeCss(docs, fontAssets, layoutFonts) {
7232
7688
  const sidebarWidth = getThemeLayoutValue(docs, "sidebarWidth", 320);
7233
7689
  const contentWidth = getThemeLayoutValue(docs, "contentWidth", 860);
7690
+ const fontFaces = fontAssets.map(
7691
+ ({ family, url }) => `@font-face { font-family: "${family}"; src: url("${url}") format("woff2"); font-style: normal; font-weight: 100 900; font-display: block; }`
7692
+ ).join("\n");
7693
+ const fontVariables = [
7694
+ fontAssets.some(({ family }) => family === "Geist Sans") ? '--font-geist-sans: "Geist Sans", ui-sans-serif, system-ui, sans-serif;' : "",
7695
+ fontAssets.some(({ family }) => family === "Geist Mono") ? '--font-geist-mono: "Geist Mono", ui-monospace, monospace;' : ""
7696
+ ].filter(Boolean).join(" ");
7697
+ const fontCss = fontFaces ? `${fontFaces}
7698
+ :root { ${fontVariables} }` : "";
7699
+ const layoutFontVariables = [
7700
+ renderFarmDocsLayoutFontVariable("--fd-layout-font-body", layoutFonts?.body),
7701
+ renderFarmDocsLayoutFontVariable("--fd-layout-font-code", layoutFonts?.code)
7702
+ ].filter(Boolean).join(" ");
7234
7703
  return `
7235
- @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; }
7236
- @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; }
7237
- :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); }
7704
+ :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); }
7238
7705
  * { box-sizing: border-box; }
7239
7706
  html { background: var(--color-fd-background, hsl(0 0% 2%)); scroll-padding-top: calc(var(--fd-nav-height, 44px) + 24px); }
7240
7707
  html[data-farm-docs-sidebar="open"], html[data-farm-docs-sidebar="open"] body { overflow: hidden; }
7241
- 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; }
7708
+ 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; }
7242
7709
  ::selection { background: var(--color-fd-foreground, #fff); color: var(--color-fd-background, #000); }
7243
7710
  a { color: inherit; }
7244
7711
  #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%)); }
@@ -7418,14 +7885,45 @@ function renderFarmDocsBridgeCss(docs) {
7418
7885
  .sh__line { min-height: 1.6em; }
7419
7886
  }
7420
7887
  ${farmDocsPixelBorderCss}
7888
+ ${fontCss}
7421
7889
  `;
7422
7890
  }
7423
- function renderPixelDocsHtml(page2, pages, docs, themeCss, clientEntry, faviconHref) {
7891
+ function renderFarmDocsLayoutFontVariable(name, font) {
7892
+ const family = font?.style.fontFamily;
7893
+ if (!family || /[;{}<>]/.test(family)) return "";
7894
+ return `${name}: ${family};`;
7895
+ }
7896
+ function renderFarmDocsFontPreloads(fontAssets) {
7897
+ return fontAssets.map(
7898
+ ({ url }) => `<link rel="preload" href="${escapeAttribute2(url)}" as="font" type="font/woff2" crossorigin>`
7899
+ ).join("\n ");
7900
+ }
7901
+ function renderFarmDocsFontPreloadHeader(fontAssets) {
7902
+ return fontAssets.map(({ url }) => `<${url}>; rel=preload; as=font; type=font/woff2; crossorigin`).join(", ");
7903
+ }
7904
+ function getFarmLayoutFontPreloads(layoutFonts) {
7905
+ const seen = /* @__PURE__ */ new Set();
7906
+ return [layoutFonts?.body, layoutFonts?.code].flatMap(
7907
+ (font) => (font?.preloads || []).filter(({ href }) => {
7908
+ if (seen.has(href)) return false;
7909
+ seen.add(href);
7910
+ return true;
7911
+ })
7912
+ );
7913
+ }
7914
+ function renderFarmLayoutFontPreloadHeader(layoutFonts) {
7915
+ return getFarmLayoutFontPreloads(layoutFonts).map(({ href, type }) => `<${href}>; rel=preload; as=font; type=${type}; crossorigin`).join(", ");
7916
+ }
7917
+ function renderPixelDocsHtml(page2, pages, docs, themeCss, clientEntry, faviconHref, requestUrl, fontAssets, layoutFonts, fontStylesheetHref) {
7424
7918
  const navTitle = typeof docs.config.nav === "object" && docs.config.nav && "title" in docs.config.nav ? String(docs.config.nav.title || "Docs") : "Docs";
7425
7919
  const description = page2.description || docs.config.metadata?.description || "";
7426
7920
  const tocItems = extractTocItems(page2.body, getThemeTocDepth(docs));
7427
7921
  const themeName = getThemeName(docs);
7428
7922
  const searchEnabled = isFarmDocsSearchEnabled(docs);
7923
+ const socialMetadata = isFarmDocsSocialImageEnabled(page2, docs) ? renderFarmDocsSocialMetadata(
7924
+ createFarmDocsSocialImageDescriptor(page2, docs, requestUrl),
7925
+ getFarmDocsCustomSocialImage(page2)
7926
+ ) : "";
7429
7927
  return `<!DOCTYPE html>
7430
7928
  <html class="dark" lang="en" data-docs-theme="${escapeAttribute2(themeName)}">
7431
7929
  <head>
@@ -7434,11 +7932,12 @@ function renderPixelDocsHtml(page2, pages, docs, themeCss, clientEntry, faviconH
7434
7932
  <meta name="generator" content="@farming-labs/docs via Farm.js">
7435
7933
  <title>${escapeHtml(page2.title)}</title>
7436
7934
  ${renderFarmDocsFaviconLink(faviconHref)}
7437
- <link rel="preload" href="${GEIST_SANS_FONT_URL}" as="font" type="font/woff2" crossorigin>
7438
- <link rel="preload" href="${GEIST_MONO_FONT_URL}" as="font" type="font/woff2" crossorigin>
7935
+ ${renderFarmDocsFontPreloads(fontAssets)}
7936
+ ${fontStylesheetHref ? `<link rel="stylesheet" href="${escapeAttribute2(fontStylesheetHref)}">` : ""}
7439
7937
  ${description ? `<meta name="description" content="${escapeHtml(description)}">` : ""}
7938
+ ${socialMetadata}
7440
7939
  <style>${themeCss}
7441
- ${renderFarmDocsBridgeCss(docs)}</style>
7940
+ ${renderFarmDocsBridgeCss(docs, fontAssets, layoutFonts)}</style>
7442
7941
  ${searchEnabled ? renderDocsSearchBootstrapScript() : ""}
7443
7942
  </head>
7444
7943
  <body>
@@ -7490,9 +7989,34 @@ ${renderMarkdownHtmlWithTitleMeta(page2, docs)}
7490
7989
  }
7491
7990
  function createFarmDocsHandler(docs, options) {
7492
7991
  const faviconHref = resolveFarmDocsFavicon(docs, options);
7992
+ const fontAssets = options.fontAssets ?? toFarmDocsPublicFontAssets(resolveFarmDocsFontAssets(options.root));
7993
+ const fallbackFontPreloadHeader = renderFarmDocsFontPreloadHeader(fontAssets);
7493
7994
  return /* @__PURE__ */ __name(async function handleFarmDocsRequest(request) {
7494
7995
  if (!docs?.enabled || request.method !== "GET" && request.method !== "HEAD") return null;
7495
7996
  const contentDir = resolveFarmDocsContentDir(docs, options);
7997
+ const requestUrl = new URL(request.url);
7998
+ const socialImageSlug = getFarmDocsSocialImageSlug(docs, requestUrl);
7999
+ if (socialImageSlug !== null) {
8000
+ const socialImagePage = loadFarmDocsPage(contentDir, docs, socialImageSlug);
8001
+ if (!socialImagePage || !isFarmDocsSocialImageEnabled(socialImagePage, docs) || getFarmDocsCustomSocialImage(socialImagePage)) {
8002
+ return null;
8003
+ }
8004
+ const descriptor = createFarmDocsSocialImageDescriptor(socialImagePage, docs, requestUrl);
8005
+ if (requestUrl.pathname !== descriptor.imagePath) return null;
8006
+ const etag = `"${descriptor.hash}"`;
8007
+ const immutable = requestUrl.searchParams.get("v") === descriptor.hash;
8008
+ const headers = new Headers({
8009
+ "Content-Type": "image/svg+xml; charset=utf-8",
8010
+ "Cache-Control": immutable ? "public, max-age=31536000, immutable" : "public, max-age=0, must-revalidate",
8011
+ ETag: etag,
8012
+ "X-Content-Type-Options": "nosniff"
8013
+ });
8014
+ if (request.headers.get("if-none-match") === etag) {
8015
+ return new Response(null, { status: 304, headers });
8016
+ }
8017
+ if (request.method === "HEAD") return new Response(null, { status: 200, headers });
8018
+ return new Response(renderFarmDocsSocialImageSvg(descriptor), { status: 200, headers });
8019
+ }
7496
8020
  const publicResponse = createFarmDocsPublicResponse(contentDir, docs, request);
7497
8021
  if (publicResponse) return publicResponse;
7498
8022
  if (!isFarmDocsRequest(docs, request)) return null;
@@ -7515,6 +8039,11 @@ function createFarmDocsHandler(docs, options) {
7515
8039
  }
7516
8040
  );
7517
8041
  }
8042
+ const pathname = new URL(request.url).pathname;
8043
+ const layoutFonts = await options.resolveLayoutFonts?.(pathname);
8044
+ const usesLayoutFonts = Boolean(layoutFonts?.body || layoutFonts?.code);
8045
+ const activeFontAssets = usesLayoutFonts ? [] : fontAssets;
8046
+ const fontPreloadHeader = usesLayoutFonts ? renderFarmLayoutFontPreloadHeader(layoutFonts) : fallbackFontPreloadHeader;
7518
8047
  return new Response(
7519
8048
  renderPixelDocsHtml(
7520
8049
  page2,
@@ -7522,39 +8051,44 @@ function createFarmDocsHandler(docs, options) {
7522
8051
  docs,
7523
8052
  resolvePixelBorderThemeCss(options),
7524
8053
  options.clientEntry || "/farm-client.js",
7525
- faviconHref
8054
+ faviconHref,
8055
+ requestUrl,
8056
+ activeFontAssets,
8057
+ layoutFonts,
8058
+ usesLayoutFonts ? options.fontStylesheetHref : void 0
7526
8059
  ),
7527
8060
  {
7528
8061
  status: 200,
7529
8062
  headers: {
7530
8063
  "Content-Type": "text/html; charset=utf-8",
7531
- "Cache-Control": "public, s-maxage=60, stale-while-revalidate=300"
8064
+ "Cache-Control": "public, s-maxage=60, stale-while-revalidate=300",
8065
+ ...fontPreloadHeader ? { Link: fontPreloadHeader } : {}
7532
8066
  }
7533
8067
  }
7534
8068
  );
7535
8069
  }, "handleFarmDocsRequest");
7536
8070
  }
7537
- 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;
8071
+ 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;
7538
8072
  var init_handler = __esm({
7539
8073
  "src/docs/handler.ts"() {
7540
8074
  "use strict";
7541
- import_node_fs8 = require("fs");
8075
+ import_node_fs9 = require("fs");
7542
8076
  import_node_module5 = require("module");
7543
- import_node_path11 = __toESM(require("path"));
8077
+ import_node_path13 = __toESM(require("path"));
7544
8078
  import_docs = require("@farming-labs/docs");
7545
8079
  import_marked = require("marked");
7546
8080
  import_sugar_high = require("sugar-high");
8081
+ init_fonts();
7547
8082
  init_last_modified();
7548
8083
  init_pixel_border_css();
7549
8084
  init_search_client();
8085
+ init_social_image();
7550
8086
  DOCS_FILE_NAMES = ["page.mdx", "page.md", "index.mdx", "index.md"];
7551
8087
  DOCS_FILE_EXTENSIONS2 = [".mdx", ".md"];
7552
8088
  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";
7553
8089
  FARM_DOCS_FAVICON_FILES = ["favicon.svg", "favicon.ico", "favicon.png"];
7554
- GEIST_SANS_FONT_URL = "/assets/Geist-Variable-CrgPqtmy.woff2";
7555
- GEIST_MONO_FONT_URL = "/assets/GeistMono-Variable-BNLlm6Cd.woff2";
7556
8090
  __name(trimSlashes2, "trimSlashes");
7557
- __name(normalizeEntry, "normalizeEntry");
8091
+ __name(normalizeEntry2, "normalizeEntry");
7558
8092
  __name(normalizeSlug, "normalizeSlug");
7559
8093
  __name(resolveFarmDocsFavicon, "resolveFarmDocsFavicon");
7560
8094
  __name(renderFarmDocsFaviconLink, "renderFarmDocsFaviconLink");
@@ -7744,6 +8278,11 @@ var init_handler = __esm({
7744
8278
  __name(readCssWithImports, "readCssWithImports");
7745
8279
  __name(resolvePixelBorderThemeCss, "resolvePixelBorderThemeCss");
7746
8280
  __name(renderFarmDocsBridgeCss, "renderFarmDocsBridgeCss");
8281
+ __name(renderFarmDocsLayoutFontVariable, "renderFarmDocsLayoutFontVariable");
8282
+ __name(renderFarmDocsFontPreloads, "renderFarmDocsFontPreloads");
8283
+ __name(renderFarmDocsFontPreloadHeader, "renderFarmDocsFontPreloadHeader");
8284
+ __name(getFarmLayoutFontPreloads, "getFarmLayoutFontPreloads");
8285
+ __name(renderFarmLayoutFontPreloadHeader, "renderFarmLayoutFontPreloadHeader");
7747
8286
  __name(renderPixelDocsHtml, "renderPixelDocsHtml");
7748
8287
  __name(createFarmDocsHandler, "createFarmDocsHandler");
7749
8288
  }
@@ -7818,18 +8357,18 @@ function sanitizeDocsConfig(config) {
7818
8357
  return cloneSerializable(config) || {};
7819
8358
  }
7820
8359
  async function findDocsConfigPath(rootDir, configPath) {
7821
- const { existsSync: existsSync15 } = await import("fs");
8360
+ const { existsSync: existsSync16 } = await import("fs");
7822
8361
  const root = import_path9.default.resolve(rootDir || process.cwd());
7823
8362
  if (configPath) {
7824
8363
  const resolvedPath = import_path9.default.isAbsolute(configPath) ? configPath : import_path9.default.join(root, configPath);
7825
- if (!existsSync15(resolvedPath)) {
8364
+ if (!existsSync16(resolvedPath)) {
7826
8365
  throw new Error(`Could not find docs config at ${configPath}.`);
7827
8366
  }
7828
8367
  return import_path9.default.resolve(resolvedPath);
7829
8368
  }
7830
8369
  for (const filename of DOCS_CONFIG_FILENAMES) {
7831
8370
  const resolvedPath = import_path9.default.join(root, filename);
7832
- if (existsSync15(resolvedPath)) {
8371
+ if (existsSync16(resolvedPath)) {
7833
8372
  return resolvedPath;
7834
8373
  }
7835
8374
  }
@@ -7965,6 +8504,7 @@ var init_config2 = __esm({
7965
8504
  init_image_config();
7966
8505
  init_config();
7967
8506
  init_auth_config();
8507
+ init_preload();
7968
8508
  init_config_entry();
7969
8509
  __name(getDeployTargetForPreset, "getDeployTargetForPreset");
7970
8510
  DOCS_CONFIG_FILENAMES = [
@@ -8753,8 +9293,8 @@ var init_generator = __esm({
8753
9293
  }
8754
9294
  routeGroups.get(key).push(route);
8755
9295
  }
8756
- for (const [path24, routeList] of routeGroups) {
8757
- const openAPIPath = this.convertToOpenAPIPath(path24);
9296
+ for (const [path26, routeList] of routeGroups) {
9297
+ const openAPIPath = this.convertToOpenAPIPath(path26);
8758
9298
  spec.paths[openAPIPath] = {};
8759
9299
  for (const route of routeList) {
8760
9300
  for (const method of route.methods) {
@@ -8768,8 +9308,8 @@ var init_generator = __esm({
8768
9308
  /**
8769
9309
  * Convert Farm.js API path to OpenAPI path format
8770
9310
  */
8771
- convertToOpenAPIPath(path24) {
8772
- return path24.replace(/^\/api/, "");
9311
+ convertToOpenAPIPath(path26) {
9312
+ return path26.replace(/^\/api/, "");
8773
9313
  }
8774
9314
  /**
8775
9315
  * Extract request body schema from endpoint
@@ -8931,8 +9471,8 @@ var init_generator = __esm({
8931
9471
  /**
8932
9472
  * Generate operation summary
8933
9473
  */
8934
- generateSummary(path24, method) {
8935
- const cleanPath = path24.replace(/^\/api\//, "");
9474
+ generateSummary(path26, method) {
9475
+ const cleanPath = path26.replace(/^\/api\//, "");
8936
9476
  const pathParts = cleanPath.split("/");
8937
9477
  const lastPart = pathParts[pathParts.length - 1];
8938
9478
  const action = method === "GET" ? "Get" : method === "POST" ? "Create" : method === "PUT" ? "Update" : method === "DELETE" ? "Delete" : method === "PATCH" ? "Update" : method;
@@ -8941,22 +9481,22 @@ var init_generator = __esm({
8941
9481
  /**
8942
9482
  * Generate operation description
8943
9483
  */
8944
- generateDescription(path24, method) {
8945
- const cleanPath = path24.replace(/^\/api\//, "");
9484
+ generateDescription(path26, method) {
9485
+ const cleanPath = path26.replace(/^\/api\//, "");
8946
9486
  return `${method} ${cleanPath} endpoint`;
8947
9487
  }
8948
9488
  /**
8949
9489
  * Generate operation ID
8950
9490
  */
8951
- generateOperationId(path24, method) {
8952
- const cleanPath = path24.replace(/^\/api\//, "").replace(/\//g, "_");
9491
+ generateOperationId(path26, method) {
9492
+ const cleanPath = path26.replace(/^\/api\//, "").replace(/\//g, "_");
8953
9493
  return `${method.toLowerCase()}_${cleanPath}`;
8954
9494
  }
8955
9495
  /**
8956
9496
  * Generate tags for grouping operations
8957
9497
  */
8958
- generateTags(path24) {
8959
- const cleanPath = path24.replace(/^\/api\//, "");
9498
+ generateTags(path26) {
9499
+ const cleanPath = path26.replace(/^\/api\//, "");
8960
9500
  const pathParts = cleanPath.split("/");
8961
9501
  if (pathParts.length > 1) {
8962
9502
  return [pathParts[0]];
@@ -8969,8 +9509,8 @@ var init_generator = __esm({
8969
9509
  async generateSpecFile(routes, outputPath) {
8970
9510
  const spec = await this.generateSpec(routes);
8971
9511
  const fs8 = require("fs");
8972
- const path24 = require("path");
8973
- const dir = path24.dirname(outputPath);
9512
+ const path26 = require("path");
9513
+ const dir = path26.dirname(outputPath);
8974
9514
  if (!fs8.existsSync(dir)) {
8975
9515
  fs8.mkdirSync(dir, { recursive: true });
8976
9516
  }
@@ -9390,7 +9930,7 @@ async function createFarmDevtoolsSnapshot(input) {
9390
9930
  generatedAt: (input.now?.() || /* @__PURE__ */ new Date()).toISOString(),
9391
9931
  health: resolveHealth(diagnostics),
9392
9932
  project: {
9393
- name: import_node_path12.default.basename(import_node_path12.default.resolve(input.root)),
9933
+ name: import_node_path14.default.basename(import_node_path14.default.resolve(input.root)),
9394
9934
  root: input.root,
9395
9935
  srcDir: input.srcDir,
9396
9936
  basePath: config.basePath || "/",
@@ -9439,11 +9979,11 @@ async function createFarmDevtoolsSnapshot(input) {
9439
9979
  diagnostics
9440
9980
  };
9441
9981
  }
9442
- var import_node_path12;
9982
+ var import_node_path14;
9443
9983
  var init_devtools = __esm({
9444
9984
  "src/devtools.ts"() {
9445
9985
  "use strict";
9446
- import_node_path12 = __toESM(require("path"));
9986
+ import_node_path14 = __toESM(require("path"));
9447
9987
  init_config2();
9448
9988
  init_cron();
9449
9989
  init_route_runtime();
@@ -11402,6 +11942,22 @@ var import_promises3 = require("fs/promises");
11402
11942
  var OPTIONAL_CATCH_ALL_SEGMENT = /^\[\[\.\.\.([^\]]+)\]\]$/;
11403
11943
  var REQUIRED_CATCH_ALL_SEGMENT = /^\[\.\.\.([^\]]+)\]$/;
11404
11944
  var REQUIRED_DYNAMIC_SEGMENT = /^\[([^\]]+)\]$/;
11945
+ var REQUEST_API_LABELS = /* @__PURE__ */ new Map([
11946
+ ["cookies", "request cookies"],
11947
+ ["headers", "request headers"],
11948
+ ["getCurrentRequest", "the current Request"],
11949
+ ["useSearchParams", "URL search parameters"],
11950
+ ["auth", "request authentication"],
11951
+ ["getServerSession", "request authentication"],
11952
+ ["getSession", "request authentication"],
11953
+ ["currentUser", "request authentication"]
11954
+ ]);
11955
+ var ROUTE_PROP_LABELS = /* @__PURE__ */ new Map([
11956
+ ["searchParams", "URL search parameters"],
11957
+ ["middleware", "middleware request data"],
11958
+ ["middlewareData", "middleware request data"],
11959
+ ["context", "request context"]
11960
+ ]);
11405
11961
  function resolveRouteRenderingConfig(mod, source) {
11406
11962
  const directiveConfig = parseRouteRenderingDirective(source);
11407
11963
  let ssg = directiveConfig?.ssg ?? false;
@@ -11454,6 +12010,221 @@ async function resolveRouteRenderingConfigFromFile(mod, filePath) {
11454
12010
  return resolveRouteRenderingConfig(mod, source);
11455
12011
  }
11456
12012
  __name(resolveRouteRenderingConfigFromFile, "resolveRouteRenderingConfigFromFile");
12013
+ function analyzeStaticRouteCandidate(mod, source, options = {}) {
12014
+ const blockers = [];
12015
+ if (!source) {
12016
+ blockers.push("route source is unavailable");
12017
+ }
12018
+ if (options.isDynamic) {
12019
+ blockers.push("the route has dynamic path segments");
12020
+ }
12021
+ if (hasExplicitRenderingConfig(mod, source)) {
12022
+ blockers.push("the route already has explicit rendering config");
12023
+ }
12024
+ if (source) {
12025
+ blockers.push(...findRequestBoundSourceBlockers(source));
12026
+ }
12027
+ return {
12028
+ candidate: blockers.length === 0,
12029
+ blockers
12030
+ };
12031
+ }
12032
+ __name(analyzeStaticRouteCandidate, "analyzeStaticRouteCandidate");
12033
+ function findRequestBoundSourceBlockers(source) {
12034
+ const blockers = /* @__PURE__ */ new Set();
12035
+ const code = stripCommentsAndLiteralContents(source);
12036
+ const sourceWithoutComments = stripComments(source);
12037
+ for (const binding of readRequestApiImportBindings(sourceWithoutComments)) {
12038
+ if (new RegExp(`\\b${escapeRegExp2(binding.local)}\\s*(?:\\?\\.\\s*)?\\(`).test(code)) {
12039
+ blockers.add(`the route reads ${binding.label}`);
12040
+ }
12041
+ }
12042
+ for (const namespace of readNamespaceImportBindings(sourceWithoutComments)) {
12043
+ for (const [name, label] of REQUEST_API_LABELS) {
12044
+ if (new RegExp(
12045
+ `\\b${escapeRegExp2(namespace)}\\s*(?:\\?\\.\\s*|\\.\\s*)${name}\\s*(?:\\?\\.\\s*)?\\(`
12046
+ ).test(code)) {
12047
+ blockers.add(`the route reads ${label}`);
12048
+ }
12049
+ }
12050
+ }
12051
+ const params = readDefaultRouteParameters(code);
12052
+ if (params) {
12053
+ for (const [name, label] of ROUTE_PROP_LABELS) {
12054
+ if (new RegExp(`\\b${name}\\b`).test(params)) {
12055
+ blockers.add(`the route reads ${label}`);
12056
+ }
12057
+ }
12058
+ const propsName = params.match(/^\s*([A-Za-z_$][\w$]*)\s*(?::|,|$)/)?.[1];
12059
+ if (propsName) {
12060
+ for (const [name, label] of ROUTE_PROP_LABELS) {
12061
+ if (new RegExp(`\\b${escapeRegExp2(propsName)}\\s*\\.\\s*${name}\\b`).test(code)) {
12062
+ blockers.add(`the route reads ${label}`);
12063
+ }
12064
+ if (new RegExp(`\\{[^}]*\\b${name}\\b[^}]*\\}\\s*=\\s*${escapeRegExp2(propsName)}\\b`).test(
12065
+ code
12066
+ )) {
12067
+ blockers.add(`the route reads ${label}`);
12068
+ }
12069
+ }
12070
+ }
12071
+ }
12072
+ return [...blockers];
12073
+ }
12074
+ __name(findRequestBoundSourceBlockers, "findRequestBoundSourceBlockers");
12075
+ function readRequestApiImportBindings(source) {
12076
+ const bindings = [];
12077
+ for (const clause of readStaticImportClauses(source)) {
12078
+ const namedGroup = clause.match(/\{([\s\S]*?)\}/)?.[1];
12079
+ for (const rawSpecifier of (namedGroup ?? "").split(",")) {
12080
+ const specifier = rawSpecifier.trim().replace(/^type\s+/, "");
12081
+ const imported = specifier.match(/^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
12082
+ if (!imported?.[1]) continue;
12083
+ const label = REQUEST_API_LABELS.get(imported[1]);
12084
+ if (label) bindings.push({ local: imported[2] ?? imported[1], label });
12085
+ }
12086
+ const local = clause.match(/^\s*([A-Za-z_$][\w$]*)\s*(?:,|$)/)?.[1];
12087
+ if (local) {
12088
+ const label = REQUEST_API_LABELS.get(local);
12089
+ if (label) bindings.push({ local, label });
12090
+ }
12091
+ }
12092
+ return bindings;
12093
+ }
12094
+ __name(readRequestApiImportBindings, "readRequestApiImportBindings");
12095
+ function readNamespaceImportBindings(source) {
12096
+ return readStaticImportClauses(source).map((clause) => clause.match(/\*\s*as\s*([A-Za-z_$][\w$]*)/)?.[1]).filter((binding) => Boolean(binding));
12097
+ }
12098
+ __name(readNamespaceImportBindings, "readNamespaceImportBindings");
12099
+ function readStaticImportClauses(source) {
12100
+ return [...source.matchAll(/\bimport\s+(?!\()([\s\S]*?)\s+from\s*["'][^"']+["']/g)].map((match) => match[1]).filter((clause) => Boolean(clause));
12101
+ }
12102
+ __name(readStaticImportClauses, "readStaticImportClauses");
12103
+ function readDefaultRouteParameters(code) {
12104
+ const inlineParams = code.match(/\bexport\s+default\s+(?:async\s+)?function\b[^(]*\(([^)]*)\)/)?.[1] ?? code.match(/\bexport\s+default\s+(?:async\s+)?\(([^)]*)\)\s*=>/)?.[1];
12105
+ if (inlineParams !== void 0) return inlineParams;
12106
+ 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];
12107
+ if (!binding) return void 0;
12108
+ const escapedBinding = escapeRegExp2(binding);
12109
+ return code.match(new RegExp(`\\bfunction\\s+${escapedBinding}\\s*\\(([^)]*)\\)`))?.[1] ?? code.match(
12110
+ new RegExp(
12111
+ `\\b(?:const|let|var)\\s+${escapedBinding}\\s*=\\s*(?:async\\s+)?\\(([^)]*)\\)\\s*=>`
12112
+ )
12113
+ )?.[1] ?? code.match(
12114
+ new RegExp(`\\b(?:const|let|var)\\s+${escapedBinding}\\s*=\\s*function\\s*\\(([^)]*)\\)`)
12115
+ )?.[1];
12116
+ }
12117
+ __name(readDefaultRouteParameters, "readDefaultRouteParameters");
12118
+ function stripCommentsAndLiteralContents(source) {
12119
+ return sanitizeSource(source, true);
12120
+ }
12121
+ __name(stripCommentsAndLiteralContents, "stripCommentsAndLiteralContents");
12122
+ function stripComments(source) {
12123
+ return sanitizeSource(source, false);
12124
+ }
12125
+ __name(stripComments, "stripComments");
12126
+ function sanitizeSource(source, maskLiterals) {
12127
+ const output = [...source];
12128
+ const mask = /* @__PURE__ */ __name((index) => {
12129
+ if (output[index] !== "\n" && output[index] !== "\r") output[index] = " ";
12130
+ }, "mask");
12131
+ const scanQuoted = /* @__PURE__ */ __name((start, quote) => {
12132
+ let index = start;
12133
+ if (maskLiterals) mask(index);
12134
+ index += 1;
12135
+ while (index < source.length) {
12136
+ if (source[index] === "\\") {
12137
+ if (maskLiterals) mask(index);
12138
+ if (index + 1 < source.length && maskLiterals) mask(index + 1);
12139
+ index += 2;
12140
+ continue;
12141
+ }
12142
+ const character = source[index];
12143
+ if (maskLiterals) mask(index);
12144
+ index += 1;
12145
+ if (character === quote) break;
12146
+ }
12147
+ return index;
12148
+ }, "scanQuoted");
12149
+ const scanCode = /* @__PURE__ */ __name((start, stopAtTemplateBrace) => {
12150
+ let index = start;
12151
+ let braceDepth = 0;
12152
+ while (index < source.length) {
12153
+ const character = source[index];
12154
+ const next = source[index + 1];
12155
+ if (character === "/" && next === "/") {
12156
+ while (index < source.length && source[index] !== "\n" && source[index] !== "\r") {
12157
+ mask(index++);
12158
+ }
12159
+ continue;
12160
+ }
12161
+ if (character === "/" && next === "*") {
12162
+ mask(index++);
12163
+ mask(index++);
12164
+ while (index < source.length) {
12165
+ const closesComment = source[index] === "*" && source[index + 1] === "/";
12166
+ mask(index++);
12167
+ if (closesComment) {
12168
+ mask(index++);
12169
+ break;
12170
+ }
12171
+ }
12172
+ continue;
12173
+ }
12174
+ if (character === "'" || character === '"') {
12175
+ index = scanQuoted(index, character);
12176
+ continue;
12177
+ }
12178
+ if (character === "`") {
12179
+ if (maskLiterals) mask(index);
12180
+ index += 1;
12181
+ while (index < source.length) {
12182
+ if (source[index] === "\\") {
12183
+ if (maskLiterals) mask(index);
12184
+ if (index + 1 < source.length && maskLiterals) mask(index + 1);
12185
+ index += 2;
12186
+ } else if (source[index] === "`") {
12187
+ if (maskLiterals) mask(index);
12188
+ index += 1;
12189
+ break;
12190
+ } else if (source[index] === "$" && source[index + 1] === "{") {
12191
+ if (maskLiterals) {
12192
+ mask(index);
12193
+ mask(index + 1);
12194
+ }
12195
+ index = scanCode(index + 2, true);
12196
+ } else {
12197
+ if (maskLiterals) mask(index);
12198
+ index += 1;
12199
+ }
12200
+ }
12201
+ continue;
12202
+ }
12203
+ if (character === "{") {
12204
+ braceDepth += 1;
12205
+ } else if (character === "}") {
12206
+ if (stopAtTemplateBrace && braceDepth === 0) {
12207
+ if (maskLiterals) mask(index);
12208
+ return index + 1;
12209
+ }
12210
+ braceDepth = Math.max(0, braceDepth - 1);
12211
+ }
12212
+ index += 1;
12213
+ }
12214
+ return index;
12215
+ }, "scanCode");
12216
+ scanCode(0, false);
12217
+ return output.join("");
12218
+ }
12219
+ __name(sanitizeSource, "sanitizeSource");
12220
+ function escapeRegExp2(value) {
12221
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
12222
+ }
12223
+ __name(escapeRegExp2, "escapeRegExp");
12224
+ function hasExplicitRenderingConfig(mod, source) {
12225
+ 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";
12226
+ }
12227
+ __name(hasExplicitRenderingConfig, "hasExplicitRenderingConfig");
11457
12228
  function parseRouteRenderingDirective(source) {
11458
12229
  if (!source) {
11459
12230
  return void 0;
@@ -11585,9 +12356,10 @@ function skipHorizontalWhitespace(source, start) {
11585
12356
  return index;
11586
12357
  }
11587
12358
  __name(skipHorizontalWhitespace, "skipHorizontalWhitespace");
11588
- async function collectSSGPages(routes, loadModule2) {
12359
+ async function collectSSGPages(routes, loadModule2, options = {}) {
11589
12360
  const ssgPages = [];
11590
12361
  const ssrRoutes = [];
12362
+ const staticCandidateRoutes = [];
11591
12363
  for (const route of routes) {
11592
12364
  try {
11593
12365
  const mod = await loadModule2(route.filePath);
@@ -11595,7 +12367,8 @@ async function collectSSGPages(routes, loadModule2) {
11595
12367
  ssrRoutes.push(route.path);
11596
12368
  continue;
11597
12369
  }
11598
- const rendering = await resolveRouteRenderingConfigFromFile(mod, route.filePath);
12370
+ const source = await (0, import_promises3.readFile)(route.filePath, "utf8").catch(() => void 0);
12371
+ const rendering = resolveRouteRenderingConfig(mod, source);
11599
12372
  if (rendering.ssg) {
11600
12373
  if (route.isDynamic) {
11601
12374
  const getStaticPaths = mod.getStaticPaths || mod.generateStaticParams;
@@ -11622,12 +12395,26 @@ async function collectSSGPages(routes, loadModule2) {
11622
12395
  }
11623
12396
  } else {
11624
12397
  ssrRoutes.push(route.path);
12398
+ if (options.suggestStaticRendering !== false && analyzeStaticRouteCandidate(mod, source, { isDynamic: route.isDynamic }).candidate) {
12399
+ staticCandidateRoutes.push(route.path);
12400
+ }
11625
12401
  }
11626
12402
  } catch (error) {
11627
12403
  console.error(`Error processing route ${route.path}:`, error);
11628
12404
  ssrRoutes.push(route.path);
11629
12405
  }
11630
12406
  }
12407
+ if (staticCandidateRoutes.length > 0) {
12408
+ const visibleRoutes = staticCandidateRoutes.slice(0, 10);
12409
+ const remainingCount = staticCandidateRoutes.length - visibleRoutes.length;
12410
+ const remainingSuffix = remainingCount > 0 ? `
12411
+ \u2026and ${remainingCount} more` : "";
12412
+ console.warn(
12413
+ `[Farm.js] ${staticCandidateRoutes.length} route${staticCandidateRoutes.length === 1 ? " appears" : "s appear"} eligible for static rendering:
12414
+ ${visibleRoutes.join("\n ")}${remainingSuffix}
12415
+ Add \`export const dynamic = "force-static";\` after verifying that imported code does not read cookies, headers, authentication, or other request data.`
12416
+ );
12417
+ }
11631
12418
  return { ssg: ssgPages, ssr: ssrRoutes };
11632
12419
  }
11633
12420
  __name(collectSSGPages, "collectSSGPages");
@@ -12390,25 +13177,25 @@ function defineRoutes(input) {
12390
13177
  }
12391
13178
  __name(defineRoutes, "defineRoutes");
12392
13179
  var routesBuilder = {
12393
- page(path24, options) {
13180
+ page(path26, options) {
12394
13181
  return normalizeProgrammaticRoute({
12395
13182
  kind: "page",
12396
- path: path24,
13183
+ path: path26,
12397
13184
  ...options
12398
13185
  });
12399
13186
  },
12400
- layout(path24, options) {
13187
+ layout(path26, options) {
12401
13188
  return normalizeProgrammaticRoute({
12402
13189
  kind: "layout",
12403
- path: path24,
13190
+ path: path26,
12404
13191
  ...options
12405
13192
  });
12406
13193
  },
12407
- api(path24, options) {
13194
+ api(path26, options) {
12408
13195
  const { render, runtime, regions, maxDuration, ...methods } = options;
12409
13196
  return normalizeProgrammaticRoute({
12410
13197
  kind: "api",
12411
- path: path24,
13198
+ path: path26,
12412
13199
  render,
12413
13200
  runtime,
12414
13201
  regions,
@@ -12444,7 +13231,7 @@ function getProgrammaticRouteSearchClientOptions(search) {
12444
13231
  return Object.keys(options).length > 0 ? options : void 0;
12445
13232
  }
12446
13233
  __name(getProgrammaticRouteSearchClientOptions, "getProgrammaticRouteSearchClientOptions");
12447
- function resolveProgrammaticRouteSearch(searchConfig, rawSearch, path24, routePath) {
13234
+ function resolveProgrammaticRouteSearch(searchConfig, rawSearch, path26, routePath) {
12448
13235
  const schema = getProgrammaticRouteSearchSchema(searchConfig);
12449
13236
  const search = parseProgrammaticSchema(schema, rawSearch, "search", routePath);
12450
13237
  const options = getProgrammaticRouteSearchOptions(searchConfig);
@@ -12453,7 +13240,7 @@ function resolveProgrammaticRouteSearch(searchConfig, rawSearch, path24, routePa
12453
13240
  schema,
12454
13241
  rawSearch,
12455
13242
  parsedSearch: search,
12456
- path: path24
13243
+ path: path26
12457
13244
  });
12458
13245
  return { search, canonicalPath };
12459
13246
  }
@@ -12636,7 +13423,7 @@ function createRouteModuleFromProgrammaticPage(route) {
12636
13423
  mod.__farmRouteData = route.data;
12637
13424
  mod.__farmRouteParsesProps = true;
12638
13425
  mod.__farmResolveRouteProps = (props) => resolveProgrammaticRouteProps(route, props);
12639
- mod.__farmResolveRouteCanonicalPath = (rawSearch, path24) => resolveProgrammaticRouteSearch(route.search, rawSearch, path24, route.path).canonicalPath;
13426
+ mod.__farmResolveRouteCanonicalPath = (rawSearch, path26) => resolveProgrammaticRouteSearch(route.search, rawSearch, path26, route.path).canonicalPath;
12640
13427
  }
12641
13428
  if (route.pending || route.error || route.notFound) {
12642
13429
  mod.__farmRouteComponents = {
@@ -13112,6 +13899,15 @@ var import_path5 = __toESM(require("path"));
13112
13899
  var import_fs2 = __toESM(require("fs"));
13113
13900
  var import_path4 = __toESM(require("path"));
13114
13901
  var import_url2 = require("url");
13902
+
13903
+ // src/island.ts
13904
+ var FARM_ISLAND_STRATEGIES = ["load", "interaction", "visible", "idle"];
13905
+ function isFarmIslandStrategy(value) {
13906
+ return FARM_ISLAND_STRATEGIES.includes(value);
13907
+ }
13908
+ __name(isFarmIslandStrategy, "isFarmIslandStrategy");
13909
+
13910
+ // src/utils/client-component.ts
13115
13911
  function readIfExists(filePath) {
13116
13912
  try {
13117
13913
  if (!filePath || !import_fs2.default.existsSync(filePath)) {
@@ -13182,56 +13978,186 @@ function hasHydrateExport(content) {
13182
13978
  return /\bexport\s+const\s+hydrate\s*=\s*true\b/.test(content) || /\bexport\s*\{\s*hydrate\s*\}\b/.test(content);
13183
13979
  }
13184
13980
  __name(hasHydrateExport, "hasHydrateExport");
13981
+ function tokenizeModuleSource(content) {
13982
+ const tokens = [];
13983
+ let index = 0;
13984
+ let line = 1;
13985
+ while (index < content.length) {
13986
+ const character = content[index];
13987
+ if (/\s/.test(character)) {
13988
+ if (character === "\n") line++;
13989
+ index++;
13990
+ continue;
13991
+ }
13992
+ if (character === "/" && content[index + 1] === "/") {
13993
+ index += 2;
13994
+ while (index < content.length && content[index] !== "\n") index++;
13995
+ continue;
13996
+ }
13997
+ if (character === "/" && content[index + 1] === "*") {
13998
+ index += 2;
13999
+ while (index < content.length) {
14000
+ if (content[index] === "\n") line++;
14001
+ if (content[index] === "*" && content[index + 1] === "/") {
14002
+ index += 2;
14003
+ break;
14004
+ }
14005
+ index++;
14006
+ }
14007
+ continue;
14008
+ }
14009
+ if (character === '"' || character === "'") {
14010
+ const quote = character;
14011
+ const tokenLine = line;
14012
+ let value = "";
14013
+ index++;
14014
+ while (index < content.length) {
14015
+ const next = content[index];
14016
+ if (next === "\\") {
14017
+ value += next;
14018
+ if (index + 1 < content.length) value += content[index + 1];
14019
+ index += 2;
14020
+ continue;
14021
+ }
14022
+ if (next === quote) {
14023
+ index++;
14024
+ break;
14025
+ }
14026
+ if (next === "\n") line++;
14027
+ value += next;
14028
+ index++;
14029
+ }
14030
+ tokens.push({ kind: "string", value, line: tokenLine });
14031
+ continue;
14032
+ }
14033
+ if (character === "`") {
14034
+ index++;
14035
+ while (index < content.length) {
14036
+ const next = content[index];
14037
+ if (next === "\\") {
14038
+ index += 2;
14039
+ continue;
14040
+ }
14041
+ if (next === "`") {
14042
+ index++;
14043
+ break;
14044
+ }
14045
+ if (next === "\n") line++;
14046
+ index++;
14047
+ }
14048
+ continue;
14049
+ }
14050
+ if (/[A-Za-z_$]/.test(character)) {
14051
+ const start = index++;
14052
+ while (index < content.length && /[\w$]/.test(content[index])) index++;
14053
+ tokens.push({ kind: "identifier", value: content.slice(start, index), line });
14054
+ continue;
14055
+ }
14056
+ tokens.push({ kind: "punctuation", value: character, line });
14057
+ index++;
14058
+ }
14059
+ return tokens;
14060
+ }
14061
+ __name(tokenizeModuleSource, "tokenizeModuleSource");
14062
+ function isIslandExportStart(tokens, index) {
14063
+ const token = tokens[index];
14064
+ const previous = tokens[index - 1];
14065
+ const startsStatement = !previous || previous.value === ";" || previous.value === "{" || previous.value === "}" || token.line > previous.line;
14066
+ return startsStatement && token.value === "export" && tokens[index + 1]?.value === "const" && tokens[index + 2]?.value === "island";
14067
+ }
14068
+ __name(isIslandExportStart, "isIslandExportStart");
14069
+ function getIslandStrategyExport(content) {
14070
+ if (!content) return null;
14071
+ const tokens = tokenizeModuleSource(content);
14072
+ for (let index = 0; index < tokens.length - 2; index++) {
14073
+ if (!isIslandExportStart(tokens, index)) continue;
14074
+ let valueIndex = index + 3;
14075
+ if (tokens[valueIndex]?.value === ":") {
14076
+ while (valueIndex < tokens.length && tokens[valueIndex].value !== "=") valueIndex++;
14077
+ }
14078
+ if (tokens[valueIndex]?.value !== "=") break;
14079
+ valueIndex++;
14080
+ const literal = tokens[valueIndex];
14081
+ if (!literal || literal.kind !== "string" || !isFarmIslandStrategy(literal.value)) break;
14082
+ let trailingIndex = valueIndex + 1;
14083
+ if (tokens[trailingIndex]?.value === "as" && tokens[trailingIndex + 1]?.value === "const") {
14084
+ trailingIndex += 2;
14085
+ }
14086
+ const trailing = tokens[trailingIndex];
14087
+ if (trailing && trailing.value !== ";" && trailing.line === literal.line) break;
14088
+ return literal.value;
14089
+ }
14090
+ if (tokens.some((_token, index) => isIslandExportStart(tokens, index))) {
14091
+ throw new Error(
14092
+ 'Farm island configuration must be a static "load", "interaction", "visible", or "idle" string literal.'
14093
+ );
14094
+ }
14095
+ return null;
14096
+ }
14097
+ __name(getIslandStrategyExport, "getIslandStrategyExport");
13185
14098
  function stripUseClientDirective(content) {
13186
14099
  return content.replace(/^\s*(["'])use client\1\s*;?\s*/, "");
13187
14100
  }
13188
14101
  __name(stripUseClientDirective, "stripUseClientDirective");
13189
- function parseClientModuleMetadata(content) {
14102
+ function parseClientModuleMetadata(content, inspectIslandExport) {
13190
14103
  if (!content) {
13191
14104
  return {
13192
14105
  isClientComponent: false,
13193
- hasHydrateExport: false
14106
+ hasHydrateExport: false,
14107
+ islandStrategy: null
13194
14108
  };
13195
14109
  }
14110
+ const isClientComponent = hasUseClientDirective(content);
14111
+ const hasHydrate = hasHydrateExport(content);
13196
14112
  return {
13197
- isClientComponent: hasUseClientDirective(content),
13198
- hasHydrateExport: hasHydrateExport(content)
14113
+ isClientComponent,
14114
+ hasHydrateExport: hasHydrate,
14115
+ islandStrategy: inspectIslandExport || isClientComponent || hasHydrate ? getIslandStrategyExport(content) : null
13199
14116
  };
13200
14117
  }
13201
14118
  __name(parseClientModuleMetadata, "parseClientModuleMetadata");
13202
14119
  function getClientModuleMetadata(modulePath, root) {
13203
14120
  const resolvedPath = resolveModuleSourcePath(modulePath, root);
13204
- return inspectClientModuleMetadata(resolvedPath, root, /* @__PURE__ */ new Set());
14121
+ return inspectClientModuleMetadata(resolvedPath, root, /* @__PURE__ */ new Set(), true);
13205
14122
  }
13206
14123
  __name(getClientModuleMetadata, "getClientModuleMetadata");
13207
- function inspectClientModuleMetadata(resolvedPath, root, visited) {
14124
+ function inspectClientModuleMetadata(resolvedPath, root, visited, inspectIslandExport) {
13208
14125
  if (!resolvedPath || visited.has(resolvedPath)) {
13209
14126
  return {
13210
14127
  isClientComponent: false,
13211
- shouldHydrate: false
14128
+ shouldHydrate: false,
14129
+ islandStrategy: null
13212
14130
  };
13213
14131
  }
13214
14132
  visited.add(resolvedPath);
13215
14133
  const content = readIfExists(resolvedPath);
13216
- const parsed = parseClientModuleMetadata(content);
14134
+ const parsed = parseClientModuleMetadata(content, inspectIslandExport);
13217
14135
  if (parsed.isClientComponent) {
13218
14136
  return {
13219
14137
  isClientComponent: true,
13220
- shouldHydrate: true
14138
+ shouldHydrate: true,
14139
+ islandStrategy: parsed.islandStrategy ?? "load"
13221
14140
  };
13222
14141
  }
13223
14142
  let importsClientBoundary = false;
14143
+ let importedIslandStrategy = null;
13224
14144
  for (const specifier of getRelativeImportSpecifiers(content)) {
13225
14145
  const importedPath = resolveImportedModuleSourcePath(resolvedPath, specifier, root);
13226
- const importedMetadata = inspectClientModuleMetadata(importedPath, root, visited);
14146
+ const importedMetadata = inspectClientModuleMetadata(importedPath, root, visited, false);
13227
14147
  if (importedMetadata.isClientComponent || importedMetadata.shouldHydrate) {
13228
14148
  importsClientBoundary = true;
13229
- break;
14149
+ if (importedIslandStrategy === null) {
14150
+ importedIslandStrategy = importedMetadata.islandStrategy;
14151
+ } else if (importedIslandStrategy !== importedMetadata.islandStrategy) {
14152
+ importedIslandStrategy = "load";
14153
+ }
13230
14154
  }
13231
14155
  }
14156
+ const shouldHydrate = parsed.hasHydrateExport || importsClientBoundary;
13232
14157
  return {
13233
14158
  isClientComponent: false,
13234
- shouldHydrate: parsed.hasHydrateExport || importsClientBoundary
14159
+ shouldHydrate,
14160
+ islandStrategy: shouldHydrate ? parsed.islandStrategy ?? importedIslandStrategy ?? "load" : null
13235
14161
  };
13236
14162
  }
13237
14163
  __name(inspectClientModuleMetadata, "inspectClientModuleMetadata");
@@ -13487,6 +14413,10 @@ function parseInterceptionMarker(segment) {
13487
14413
  __name(parseInterceptionMarker, "parseInterceptionMarker");
13488
14414
 
13489
14415
  // src/routing/route-manager.ts
14416
+ function shouldSuggestStaticRenderingForI18n(i18n) {
14417
+ return !i18n || i18n.routing === "prefix-always" || i18n.routing === "prefix-except-default" && i18n.detection.every((signal) => signal === "url");
14418
+ }
14419
+ __name(shouldSuggestStaticRenderingForI18n, "shouldSuggestStaticRenderingForI18n");
13490
14420
  function routeSpecificity(entry) {
13491
14421
  return entry.route.segments.reduce((score, segment) => {
13492
14422
  if (!segment.isDynamic) return score + 100;
@@ -13730,6 +14660,7 @@ var _RouteManager = class _RouteManager {
13730
14660
  modulePath: toUrlPath(entry.modulePath),
13731
14661
  shouldHydrate: metadata.shouldHydrate,
13732
14662
  isClientComponent: metadata.isClientComponent,
14663
+ islandStrategy: metadata.islandStrategy,
13733
14664
  search: getProgrammaticRouteSearchClientOptions(programmaticPage?.search),
13734
14665
  segments: entry.route.segments.map((seg) => ({
13735
14666
  segment: seg.segment,
@@ -14192,8 +15123,10 @@ var _RouteManager = class _RouteManager {
14192
15123
  isDynamic: entry.route.segments.some((seg) => seg.isDynamic),
14193
15124
  pattern: entry.pattern
14194
15125
  }));
14195
- const result = await collectSSGPages(routes, (filePath) => this.loadRouteModule(filePath));
14196
15126
  const i18n = this.getI18nConfig();
15127
+ const result = await collectSSGPages(routes, (filePath) => this.loadRouteModule(filePath), {
15128
+ suggestStaticRendering: shouldSuggestStaticRenderingForI18n(i18n)
15129
+ });
14197
15130
  if (!i18n) {
14198
15131
  return result;
14199
15132
  }
@@ -14324,8 +15257,8 @@ async function safeGlobFiles(pattern, cwd) {
14324
15257
  __name(safeGlobFiles, "safeGlobFiles");
14325
15258
 
14326
15259
  // src/server/renderer.ts
14327
- var import_react4 = __toESM(require("react"));
14328
- var import_server = require("react-dom/server");
15260
+ var import_react5 = __toESM(require("react"));
15261
+ var import_server2 = require("react-dom/server");
14329
15262
  var fs2 = __toESM(require("fs"));
14330
15263
  var path9 = __toESM(require("path"));
14331
15264
  var import_stream = require("stream");
@@ -15784,6 +16717,10 @@ ${FONT_SECTION_END}
15784
16717
  fallback,
15785
16718
  display,
15786
16719
  preload: raw.preload !== false,
16720
+ preloadResources: raw.preload === false ? [] : normalizedSources.map(({ resource }) => ({
16721
+ href: resourceUrl(resource, production, this.basePath),
16722
+ type: fontContentType(resource.extension)
16723
+ })),
15787
16724
  weight: defaultWeight,
15788
16725
  style: raw.style === void 0 ? void 0 : defaultStyle,
15789
16726
  sources: normalizedSources
@@ -15880,7 +16817,7 @@ ${FONT_SECTION_END}
15880
16817
  const normalizedExtension = extension.toLowerCase();
15881
16818
  const hash = (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex").slice(0, 12);
15882
16819
  const baseName = sanitizeFileName(import_node_path4.default.basename(fileName, extension)) || "font";
15883
- const outputFileName = `assets/fonts/${baseName}-${hash}${normalizedExtension}`;
16820
+ const outputFileName = `assets/fonts/${baseName}-h${hash}${normalizedExtension}`;
15884
16821
  const key = `${hash}${normalizedExtension}`;
15885
16822
  const existing = this.resources.get(key);
15886
16823
  if (existing) return existing;
@@ -16150,7 +17087,8 @@ function serializeFontResult(font) {
16150
17087
  fontFamily: stack,
16151
17088
  ...font.style ? { fontStyle: font.style } : {},
16152
17089
  ...font.weight !== void 0 && !String(font.weight).includes(" ") ? { fontWeight: font.weight } : {}
16153
- }
17090
+ },
17091
+ preloads: font.preloadResources
16154
17092
  });
16155
17093
  }
16156
17094
  __name(serializeFontResult, "serializeFontResult");
@@ -16493,6 +17431,166 @@ function fontError(id, message) {
16493
17431
  }
16494
17432
  __name(fontError, "fontError");
16495
17433
 
17434
+ // src/metadata-image.ts
17435
+ var import_react4 = __toESM(require("react"));
17436
+ var import_server = require("react-dom/server");
17437
+ var REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref");
17438
+ var REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo");
17439
+ var REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
17440
+ function isResponse(value) {
17441
+ return typeof Response !== "undefined" && (value instanceof Response || Boolean(
17442
+ value && typeof value === "object" && typeof value.arrayBuffer === "function" && value.headers
17443
+ ));
17444
+ }
17445
+ __name(isResponse, "isResponse");
17446
+ function isThenable(value) {
17447
+ return Boolean(
17448
+ value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function"
17449
+ );
17450
+ }
17451
+ __name(isThenable, "isThenable");
17452
+ async function prepareMetadataImageNode(node) {
17453
+ if (isThenable(node)) {
17454
+ return prepareMetadataImageNode(await node);
17455
+ }
17456
+ if (Array.isArray(node)) {
17457
+ return Promise.all(node.map((child) => prepareMetadataImageNode(child)));
17458
+ }
17459
+ if (!import_react4.default.isValidElement(node)) {
17460
+ return node;
17461
+ }
17462
+ const element = node;
17463
+ const type = element.type;
17464
+ const props = element.props || {};
17465
+ if (typeof type === "function") {
17466
+ if (type.prototype?.isReactComponent) {
17467
+ throw new Error(
17468
+ "Metadata image components must be stateless function components; React class components are not supported."
17469
+ );
17470
+ }
17471
+ return prepareMetadataImageNode(await type(props));
17472
+ }
17473
+ if (type && typeof type === "object") {
17474
+ if (type.$$typeof === REACT_MEMO_TYPE) {
17475
+ return prepareMetadataImageNode(import_react4.default.createElement(type.type, props));
17476
+ }
17477
+ if (type.$$typeof === REACT_FORWARD_REF_TYPE) {
17478
+ return prepareMetadataImageNode(await type.render(props, null));
17479
+ }
17480
+ if (type.$$typeof === REACT_LAZY_TYPE) {
17481
+ return prepareMetadataImageNode(import_react4.default.createElement(type._init(type._payload), props));
17482
+ }
17483
+ }
17484
+ const preparedChildren = await prepareMetadataImageNode(props.children);
17485
+ const preparedProps = { ...props };
17486
+ if (typeof preparedProps.className === "string") {
17487
+ preparedProps.tw = [preparedProps.tw, preparedProps.className].filter(Boolean).join(" ");
17488
+ delete preparedProps.className;
17489
+ }
17490
+ delete preparedProps.children;
17491
+ return import_react4.default.createElement(type, {
17492
+ ...preparedProps,
17493
+ key: element.key,
17494
+ children: preparedChildren
17495
+ });
17496
+ }
17497
+ __name(prepareMetadataImageNode, "prepareMetadataImageNode");
17498
+ async function normalizeFonts(fonts) {
17499
+ if (!fonts?.length) return void 0;
17500
+ return Promise.all(
17501
+ fonts.map(async (font) => {
17502
+ const data = await font.data;
17503
+ const arrayBuffer = ArrayBuffer.isView(data) ? toArrayBuffer(data) : data;
17504
+ return { ...font, data: arrayBuffer };
17505
+ })
17506
+ );
17507
+ }
17508
+ __name(normalizeFonts, "normalizeFonts");
17509
+ function resolveCacheControl(revalidate) {
17510
+ if (revalidate === false) {
17511
+ return "public, max-age=31536000, immutable";
17512
+ }
17513
+ if (typeof revalidate === "number" && Number.isFinite(revalidate) && revalidate > 0) {
17514
+ return `public, s-maxage=${Math.floor(revalidate)}, stale-while-revalidate=300`;
17515
+ }
17516
+ return "public, max-age=0, must-revalidate";
17517
+ }
17518
+ __name(resolveCacheControl, "resolveCacheControl");
17519
+ function toArrayBuffer(value) {
17520
+ if (value instanceof ArrayBuffer) return value;
17521
+ const bytes = new Uint8Array(value.byteLength);
17522
+ bytes.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
17523
+ return bytes.buffer;
17524
+ }
17525
+ __name(toArrayBuffer, "toArrayBuffer");
17526
+ async function createEntityTag(body) {
17527
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", body);
17528
+ const hash = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 32);
17529
+ return `"${hash}"`;
17530
+ }
17531
+ __name(createEntityTag, "createEntityTag");
17532
+ async function finalizeMetadataImageResponse(body, headers, options) {
17533
+ const responseHeaders = new Headers(headers);
17534
+ const etag = await createEntityTag(body);
17535
+ responseHeaders.set("ETag", etag);
17536
+ responseHeaders.set("Content-Length", String(body.byteLength));
17537
+ responseHeaders.set("X-Content-Type-Options", "nosniff");
17538
+ const matchesEntityTag = options.ifNoneMatch?.trim() === "*" || options.ifNoneMatch?.split(",").some((candidate) => candidate.trim().replace(/^W\//, "") === etag);
17539
+ if (matchesEntityTag) {
17540
+ return new Response(null, { status: 304, headers: responseHeaders });
17541
+ }
17542
+ return new Response(options.method?.toUpperCase() === "HEAD" ? null : body, {
17543
+ status: 200,
17544
+ headers: responseHeaders
17545
+ });
17546
+ }
17547
+ __name(finalizeMetadataImageResponse, "finalizeMetadataImageResponse");
17548
+ async function createFarmMetadataImageResponse(value, imageModule, options = {}) {
17549
+ const method = (options.method || "GET").toUpperCase();
17550
+ if (method !== "GET" && method !== "HEAD") {
17551
+ return new Response(null, { status: 405, headers: { Allow: "GET, HEAD" } });
17552
+ }
17553
+ if (isResponse(value)) {
17554
+ return method === "HEAD" ? new Response(null, { status: value.status, headers: value.headers }) : value;
17555
+ }
17556
+ const cacheControl = resolveCacheControl(imageModule.revalidate);
17557
+ const explicitContentType = imageModule.contentType?.split(";", 1)[0]?.trim().toLowerCase();
17558
+ if (import_react4.default.isValidElement(value) && explicitContentType !== "image/svg+xml") {
17559
+ const { ImageResponse } = await import("@vercel/og");
17560
+ const element = await prepareMetadataImageNode(value);
17561
+ const response = new ImageResponse(element, {
17562
+ width: imageModule.size?.width || 1200,
17563
+ height: imageModule.size?.height || 630,
17564
+ fonts: await normalizeFonts(imageModule.fonts),
17565
+ emoji: imageModule.emoji,
17566
+ debug: imageModule.debug,
17567
+ headers: { "cache-control": cacheControl }
17568
+ });
17569
+ const headers = new Headers(response.headers);
17570
+ headers.set("Cache-Control", cacheControl);
17571
+ return finalizeMetadataImageResponse(await response.arrayBuffer(), headers, options);
17572
+ }
17573
+ let body;
17574
+ if (typeof value === "string") {
17575
+ body = toArrayBuffer(new TextEncoder().encode(value));
17576
+ } else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
17577
+ body = toArrayBuffer(value);
17578
+ } else if (import_react4.default.isValidElement(value)) {
17579
+ body = toArrayBuffer(new TextEncoder().encode((0, import_server.renderToStaticMarkup)(value)));
17580
+ } else {
17581
+ throw new Error("Metadata image must return a Response, string, bytes, or React element");
17582
+ }
17583
+ return finalizeMetadataImageResponse(
17584
+ body,
17585
+ {
17586
+ "Content-Type": imageModule.contentType || "image/svg+xml; charset=utf-8",
17587
+ "Cache-Control": cacheControl
17588
+ },
17589
+ options
17590
+ );
17591
+ }
17592
+ __name(createFarmMetadataImageResponse, "createFarmMetadataImageResponse");
17593
+
16496
17594
  // src/server/renderer.ts
16497
17595
  var cachedClerkProvider = null;
16498
17596
  var importRuntimeModule = new Function("specifier", "return import(specifier);");
@@ -16566,7 +17664,7 @@ function createPPRRefreshScript() {
16566
17664
  }
16567
17665
  __name(createPPRRefreshScript, "createPPRRefreshScript");
16568
17666
  function createPreHydrationClickQueueScript() {
16569
- 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>`;
17667
+ 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>`;
16570
17668
  }
16571
17669
  __name(createPreHydrationClickQueueScript, "createPreHydrationClickQueueScript");
16572
17670
  function searchParamsToObject(searchParams) {
@@ -16617,7 +17715,7 @@ function isWebResponse(value) {
16617
17715
  return typeof Response !== "undefined" && value instanceof Response && typeof value.arrayBuffer === "function";
16618
17716
  }
16619
17717
  __name(isWebResponse, "isWebResponse");
16620
- var _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react4.default.Component {
17718
+ var _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react5.default.Component {
16621
17719
  constructor(props) {
16622
17720
  super(props);
16623
17721
  this.state = { hasError: false, error: null };
@@ -16628,7 +17726,7 @@ var _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react
16628
17726
  render() {
16629
17727
  if (this.state.hasError) {
16630
17728
  const Fallback = this.props.Fallback;
16631
- return import_react4.default.createElement(Fallback, {
17729
+ return import_react5.default.createElement(Fallback, {
16632
17730
  ...this.props.fallbackProps,
16633
17731
  error: this.state.error,
16634
17732
  reset: /* @__PURE__ */ __name(() => this.setState({ hasError: false, error: null }), "reset")
@@ -16844,14 +17942,14 @@ var _ServerRenderer = class _ServerRenderer {
16844
17942
  searchParams: Promise.resolve({}),
16845
17943
  path: page2.urlPath
16846
17944
  };
16847
- let pageElement = import_react4.default.createElement(
17945
+ let pageElement = import_react5.default.createElement(
16848
17946
  PageComponent,
16849
17947
  pageProps
16850
17948
  );
16851
17949
  for (let i = layoutModules.length - 1; i >= 0; i--) {
16852
17950
  const layoutModule = layoutModules[i];
16853
17951
  const LayoutComponent = layoutModule.default;
16854
- pageElement = import_react4.default.createElement(
17952
+ pageElement = import_react5.default.createElement(
16855
17953
  LayoutComponent,
16856
17954
  {
16857
17955
  children: pageElement,
@@ -17177,6 +18275,7 @@ var _ServerRenderer = class _ServerRenderer {
17177
18275
  req.__FARM_ROUTE__ = pathname;
17178
18276
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
17179
18277
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
18278
+ req.__FARM_ISLAND_STRATEGY__ = moduleMetadata.islandStrategy;
17180
18279
  req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
17181
18280
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
17182
18281
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
@@ -17229,12 +18328,12 @@ var _ServerRenderer = class _ServerRenderer {
17229
18328
  await _runWithMiddlewareData(middlewareDataForContext, async () => {
17230
18329
  await _runWithMiddlewareContext(middlewareContext, async () => {
17231
18330
  await _runWithCurrentRequest(currentRequest, async () => {
17232
- let pageElement = import_react4.default.createElement(
18331
+ let pageElement = import_react5.default.createElement(
17233
18332
  PageComponent,
17234
18333
  pageProps
17235
18334
  );
17236
18335
  if (LoadingFallbackComponent) {
17237
- const loadingFallback = import_react4.default.createElement(LoadingFallbackComponent, {
18336
+ const loadingFallback = import_react5.default.createElement(LoadingFallbackComponent, {
17238
18337
  ...createRouteStateProps({
17239
18338
  params,
17240
18339
  searchParamsObject,
@@ -17243,16 +18342,21 @@ var _ServerRenderer = class _ServerRenderer {
17243
18342
  pluginExposedContext
17244
18343
  })
17245
18344
  });
17246
- pageElement = import_react4.default.createElement(
17247
- import_react4.default.Suspense,
18345
+ pageElement = import_react5.default.createElement(
18346
+ import_react5.default.Suspense,
17248
18347
  { fallback: loadingFallback },
17249
18348
  pageElement
17250
18349
  );
17251
18350
  }
17252
18351
  if (isClientComponent || shouldHydrate) {
17253
- pageElement = import_react4.default.createElement(
18352
+ pageElement = import_react5.default.createElement(
17254
18353
  "div",
17255
- { id: "__farm_page__", "data-farm-client": "true" },
18354
+ {
18355
+ id: "__farm_page__",
18356
+ "data-farm-client": "true",
18357
+ "data-farm-island": "page",
18358
+ "data-farm-island-strategy": moduleMetadata.islandStrategy ?? "load"
18359
+ },
17256
18360
  pageElement
17257
18361
  );
17258
18362
  }
@@ -17264,11 +18368,11 @@ var _ServerRenderer = class _ServerRenderer {
17264
18368
  const slotProps = {};
17265
18369
  for (const slot of renderedRouteSlots) {
17266
18370
  if (slot.ownerPattern !== layoutEntry.pattern) continue;
17267
- let slotElement = import_react4.default.createElement(
18371
+ let slotElement = import_react5.default.createElement(
17268
18372
  slot.module.default,
17269
18373
  slot.props
17270
18374
  );
17271
- slotElement = import_react4.default.createElement(
18375
+ slotElement = import_react5.default.createElement(
17272
18376
  "div",
17273
18377
  {
17274
18378
  id: slot.containerId,
@@ -17279,7 +18383,7 @@ var _ServerRenderer = class _ServerRenderer {
17279
18383
  );
17280
18384
  slotProps[slot.name] = slotElement;
17281
18385
  }
17282
- wrappedElement = import_react4.default.createElement(
18386
+ wrappedElement = import_react5.default.createElement(
17283
18387
  LayoutComponent,
17284
18388
  {
17285
18389
  children: wrappedElement,
@@ -17289,7 +18393,7 @@ var _ServerRenderer = class _ServerRenderer {
17289
18393
  );
17290
18394
  }
17291
18395
  if (ErrorFallbackComponent) {
17292
- wrappedElement = import_react4.default.createElement(
18396
+ wrappedElement = import_react5.default.createElement(
17293
18397
  FarmRouteErrorBoundary,
17294
18398
  {
17295
18399
  Fallback: ErrorFallbackComponent,
@@ -17422,7 +18526,7 @@ var _ServerRenderer = class _ServerRenderer {
17422
18526
  if (!cachedClerkProvider) {
17423
18527
  cachedClerkProvider = await importRuntimeModule("@clerk/react");
17424
18528
  }
17425
- wrapped = import_react4.default.createElement(
18529
+ wrapped = import_react5.default.createElement(
17426
18530
  cachedClerkProvider.ClerkProvider,
17427
18531
  provider.props || {},
17428
18532
  wrapped
@@ -17497,6 +18601,13 @@ var _ServerRenderer = class _ServerRenderer {
17497
18601
  return reference;
17498
18602
  }
17499
18603
  async renderMetadataImage(req, res, options) {
18604
+ const method = (req.method || "GET").toUpperCase();
18605
+ if (method !== "GET" && method !== "HEAD") {
18606
+ res.statusCode = 405;
18607
+ res.setHeader("Allow", "GET, HEAD");
18608
+ res.end();
18609
+ return;
18610
+ }
17500
18611
  if (options.image.sourceType === "static") {
17501
18612
  if (!options.image.staticInfo) {
17502
18613
  throw new Error(`Static metadata image ${options.image.modulePath} is missing file info`);
@@ -17522,42 +18633,12 @@ var _ServerRenderer = class _ServerRenderer {
17522
18633
  await this.writeMetadataImageResponse(req, res, handlerResult, imageModule);
17523
18634
  }
17524
18635
  async writeMetadataImageResponse(req, res, value, imageModule) {
17525
- if (isWebResponse(value)) {
17526
- res.statusCode = value.status;
17527
- value.headers.forEach((headerValue, key) => {
17528
- res.setHeader(key, headerValue);
17529
- });
17530
- if (req.method === "HEAD") {
17531
- res.end();
17532
- return;
17533
- }
17534
- const body2 = Buffer.from(await value.arrayBuffer());
17535
- res.write(body2);
17536
- res.end();
17537
- return;
17538
- }
17539
- const contentType = imageModule.contentType || "image/svg+xml; charset=utf-8";
17540
- let body;
17541
- if (typeof value === "string") {
17542
- body = value;
17543
- } else if (value instanceof ArrayBuffer) {
17544
- body = Buffer.from(value);
17545
- } else if (ArrayBuffer.isView(value)) {
17546
- body = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
17547
- } else if (import_react4.default.isValidElement(value)) {
17548
- body = (0, import_server.renderToStaticMarkup)(value);
17549
- } else {
17550
- throw new Error("Metadata image must return a Response, string, bytes, or React element");
17551
- }
17552
- res.statusCode = res.statusCode || 200;
17553
- res.setHeader("Content-Type", contentType);
17554
- res.setHeader("Cache-Control", "public, max-age=0, must-revalidate");
17555
- if (req.method === "HEAD") {
17556
- res.end();
17557
- return;
17558
- }
17559
- res.write(body);
17560
- res.end();
18636
+ const ifNoneMatch = req.headers["if-none-match"];
18637
+ const response = await createFarmMetadataImageResponse(value, imageModule, {
18638
+ method: req.method,
18639
+ ifNoneMatch: Array.isArray(ifNoneMatch) ? ifNoneMatch[0] : ifNoneMatch
18640
+ });
18641
+ await sendWebResponse(res, response);
17561
18642
  }
17562
18643
  async writeStaticMetadataImageResponse(req, res, image) {
17563
18644
  const method = (req.method || "GET").toUpperCase();
@@ -17604,7 +18685,7 @@ var _ServerRenderer = class _ServerRenderer {
17604
18685
  return false;
17605
18686
  }
17606
18687
  const ErrorComponent = errorModule.default;
17607
- const errorElement = import_react4.default.createElement(ErrorComponent, {
18688
+ const errorElement = import_react5.default.createElement(ErrorComponent, {
17608
18689
  ...createRouteStateProps({
17609
18690
  params: options.params,
17610
18691
  searchParamsObject: options.searchParamsObject,
@@ -17622,7 +18703,7 @@ var _ServerRenderer = class _ServerRenderer {
17622
18703
  );
17623
18704
  for (let i = layoutModules.length - 1; i >= 0; i--) {
17624
18705
  const LayoutComponent = layoutModules[i].default;
17625
- wrapped = import_react4.default.createElement(
18706
+ wrapped = import_react5.default.createElement(
17626
18707
  LayoutComponent,
17627
18708
  {
17628
18709
  children: wrapped,
@@ -17687,6 +18768,7 @@ var _ServerRenderer = class _ServerRenderer {
17687
18768
  search: routeEntry.search,
17688
18769
  isClientComponent: routeEntry.isClientComponent,
17689
18770
  shouldHydrate: routeEntry.shouldHydrate,
18771
+ islandStrategy: routeEntry.islandStrategy,
17690
18772
  preloads: [routeEntry.modulePath],
17691
18773
  assets: []
17692
18774
  };
@@ -17723,6 +18805,7 @@ window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
17723
18805
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
17724
18806
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
17725
18807
  window.__FARM_SHOULD_HYDRATE__ = ${JSON.stringify(req.__FARM_SHOULD_HYDRATE__ === true)};
18808
+ window.__FARM_ISLAND_STRATEGY__ = ${JSON.stringify(req.__FARM_ISLAND_STRATEGY__ || "load")};
17726
18809
  window.__FARM_PAGE_MODULE__ = ${JSON.stringify(relativePath2)};
17727
18810
  window.__FARM_LOADING_MODULE__ = ${JSON.stringify(
17728
18811
  req.__FARM_LOADING_MODULE_PATH__ || null
@@ -17740,8 +18823,8 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
17740
18823
  const i18nSnapshot = getFarmI18nClientSnapshot();
17741
18824
  const i18nAlternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
17742
18825
  const fontHead = renderFarmFontDevHead(this.config.root || process.cwd());
17743
- const streamRoot = import_react4.default.createElement("div", { style: { display: "contents" } }, element);
17744
- const { pipe, abort } = (0, import_server.renderToPipeableStream)(streamRoot, {
18826
+ const streamRoot = import_react5.default.createElement("div", { style: { display: "contents" } }, element);
18827
+ const { pipe, abort } = (0, import_server2.renderToPipeableStream)(streamRoot, {
17745
18828
  onShellReady() {
17746
18829
  const shellReadyMs = Date.now() - streamStartTime;
17747
18830
  emitFarmEvent({
@@ -17899,12 +18982,12 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
17899
18982
  break;
17900
18983
  }
17901
18984
  }
17902
- let element = import_react4.default.createElement(
18985
+ let element = import_react5.default.createElement(
17903
18986
  NotFoundComponent,
17904
18987
  { pathname }
17905
18988
  );
17906
18989
  if (LayoutComponent) {
17907
- element = import_react4.default.createElement(
18990
+ element = import_react5.default.createElement(
17908
18991
  LayoutComponent,
17909
18992
  { children: element }
17910
18993
  );
@@ -18018,6 +19101,7 @@ var import_path6 = __toESM(require("path"));
18018
19101
  init_devtools_config();
18019
19102
  init_config();
18020
19103
  init_auth_config();
19104
+ init_preload();
18021
19105
  var defaultDocsConfig = {
18022
19106
  enabled: false,
18023
19107
  entry: "/docs",
@@ -18086,6 +19170,7 @@ var _FarmApp = class _FarmApp {
18086
19170
  context: config.context || (() => void 0),
18087
19171
  serverActions: resolveServerActionsConfig(config.serverActions),
18088
19172
  images: resolveFarmImageConfig(config.images),
19173
+ performance: resolveFarmPerformanceConfig(config.performance),
18089
19174
  i18n: isResolvedI18nConfig(config.i18n) ? config.i18n : resolveFarmI18nConfig(config.i18n, {
18090
19175
  root,
18091
19176
  mode: process.env.NODE_ENV === "production" ? "production" : "development"
@@ -19853,39 +20938,48 @@ async function createRouteTypeDeclarations(options, outPath) {
19853
20938
  const { root, srcDir = "src", extraRoutes = [], suppressLintOnLink = false } = options;
19854
20939
  const sourceRoots = options.sourceRoots ?? [{ name: "project", root, srcDir, layer: false }];
19855
20940
  const patterns = /* @__PURE__ */ new Set();
20941
+ const routeModulePatterns = /* @__PURE__ */ new Set();
19856
20942
  const glob = await import("fast-glob");
19857
20943
  for (const source of sourceRoots) {
19858
20944
  const appDir = path14.join(source.root, source.srcDir, "app");
19859
20945
  if (fs5.existsSync(appDir)) {
19860
- const pageFiles = await glob.default("**/page.{ts,tsx,js,jsx,md,mdx}", {
19861
- cwd: appDir,
19862
- absolute: false
19863
- });
19864
- for (const file of pageFiles) {
20946
+ const routeModuleFiles = await glob.default(
20947
+ "**/{page,layout,loading,error}.{ts,tsx,js,jsx,md,mdx}",
20948
+ {
20949
+ cwd: appDir,
20950
+ absolute: false
20951
+ }
20952
+ );
20953
+ for (const file of routeModuleFiles) {
19865
20954
  if (parseRouteSlotFile(file)) continue;
19866
20955
  const route = parseRoutePath(file);
20956
+ const pattern = createRoutePattern(route);
20957
+ routeModulePatterns.add(pattern);
19867
20958
  if (route.type === "page") {
19868
- const pattern = createRoutePattern(route);
19869
20959
  patterns.add(pattern);
19870
20960
  }
19871
20961
  }
19872
20962
  }
19873
20963
  for (const route of await discoverProgrammaticRoutePaths(source.root, source.srcDir)) {
19874
20964
  patterns.add(route);
20965
+ routeModulePatterns.add(route);
19875
20966
  }
19876
20967
  }
19877
20968
  for (const route of extraRoutes) {
19878
20969
  if (route.startsWith("/")) {
19879
20970
  patterns.add(route);
20971
+ routeModulePatterns.add(route);
19880
20972
  }
19881
20973
  }
19882
20974
  const sortedPatterns = Array.from(patterns).sort();
19883
20975
  const typeLiterals = Array.from(new Set(sortedPatterns.flatMap(routePatternToTsTypeLiterals)));
19884
20976
  const patternLiterals = sortedPatterns.map(routePatternToRouteLiteral);
20977
+ const routeModulePatternLiterals = Array.from(routeModulePatterns).sort().map(routePatternToRouteLiteral);
19885
20978
  const routePathType = suppressLintOnLink ? "string" : typeLiterals.length ? typeLiterals.join(" | ") : "never";
19886
20979
  const routePatternType = suppressLintOnLink ? "string" : patternLiterals.length ? patternLiterals.join(" | ") : "never";
20980
+ const routeModulePatternType = routeModulePatternLiterals.length ? routeModulePatternLiterals.join(" | ") : "never";
19887
20981
  const routeTypesImportPath = `./${path14.basename(outPath).replace(/\.d\.ts$/, "")}`;
19888
- const augmentationBlock = suppressLintOnLink ? "" : `
20982
+ const linkAugmentationBlock = suppressLintOnLink ? "" : `
19889
20983
  declare module "@farm.js/core/client" {
19890
20984
  interface LinkDefaultRoute {
19891
20985
  _: import(${JSON.stringify(routeTypesImportPath)}).RoutePath;
@@ -19909,14 +21003,25 @@ declare module "@farm.js/core/dist/client.js" {
19909
21003
  pattern: import(${JSON.stringify(routeTypesImportPath)}).RoutePattern;
19910
21004
  }
19911
21005
  }
21006
+ `;
21007
+ const routeModuleAugmentationBlock = `
21008
+ declare global {
21009
+ namespace FarmJS {
21010
+ interface RouteRegistry {
21011
+ pattern: import(${JSON.stringify(routeTypesImportPath)}).RouteModulePattern;
21012
+ }
21013
+ }
21014
+ }
19912
21015
  `;
19913
21016
  const content = `/**
19914
21017
  * Auto-generated route types from src/app.
19915
- * Link href is typed automatically via module augmentation. Regenerated on dev start and when routes change.
21018
+ * Link href and route component props are typed automatically from generated declarations.
21019
+ * Regenerated on dev start and when routes change.
19916
21020
  * Set suppressLintOnLink: true in farm.config.ts to accept any string on Link href.
19917
21021
  */
19918
21022
  export type RoutePath = ${routePathType};
19919
- export type RoutePattern = ${routePatternType};${augmentationBlock}
21023
+ export type RoutePattern = ${routePatternType};
21024
+ export type RouteModulePattern = ${routeModulePatternType};${linkAugmentationBlock}${routeModuleAugmentationBlock}
19920
21025
  `;
19921
21026
  return content;
19922
21027
  }
@@ -20318,7 +21423,7 @@ init_response();
20318
21423
  init_integrations();
20319
21424
  init_devtools_config();
20320
21425
  var fs7 = __toESM(require("fs"));
20321
- var path23 = __toESM(require("path"));
21426
+ var path25 = __toESM(require("path"));
20322
21427
  init_layers();
20323
21428
 
20324
21429
  // src/after.ts
@@ -20495,8 +21600,25 @@ async function createBlurDataURL(bytes) {
20495
21600
  }
20496
21601
  __name(createBlurDataURL, "createBlurDataURL");
20497
21602
 
21603
+ // src/font.ts
21604
+ function resolveFarmLayoutFonts(layouts) {
21605
+ let resolved;
21606
+ for (const layout2 of layouts) {
21607
+ if (!layout2.fonts) continue;
21608
+ resolved = { ...resolved, ...layout2.fonts };
21609
+ }
21610
+ return resolved;
21611
+ }
21612
+ __name(resolveFarmLayoutFonts, "resolveFarmLayoutFonts");
21613
+
20498
21614
  // src/vite.ts
20499
21615
  init_image_server();
21616
+
21617
+ // src/server/vite-config.ts
21618
+ var import_node_path9 = __toESM(require("path"));
21619
+
21620
+ // src/vite.ts
21621
+ init_fonts();
20500
21622
  var ALL_TYPE_ARTIFACTS = {
20501
21623
  routes: true,
20502
21624
  api: true,
@@ -20699,7 +21821,7 @@ function fileContainsProgrammaticPageRoute(file) {
20699
21821
  __name(fileContainsProgrammaticPageRoute, "fileContainsProgrammaticPageRoute");
20700
21822
  function rewriteEarlySsrRelativeImports(options) {
20701
21823
  const cleanId = options.id.split("?", 1)[0];
20702
- if (!path23.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
21824
+ if (!path25.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
20703
21825
  return null;
20704
21826
  }
20705
21827
  const replacements = [];
@@ -20719,7 +21841,7 @@ function rewriteEarlySsrRelativeImports(options) {
20719
21841
  const suffixIndex = source.value.search(/[?#]/);
20720
21842
  const sourcePath = suffixIndex === -1 ? source.value : source.value.slice(0, suffixIndex);
20721
21843
  const suffix = suffixIndex === -1 ? "" : source.value.slice(suffixIndex);
20722
- const resolvedPath = path23.resolve(path23.dirname(cleanId), sourcePath);
21844
+ const resolvedPath = path25.resolve(path25.dirname(cleanId), sourcePath);
20723
21845
  replacements.push({
20724
21846
  start: source.start,
20725
21847
  end: source.end,
@@ -20804,7 +21926,7 @@ function farmPlugin(options = {}, initialPluginManager) {
20804
21926
  ...layerRoots.length ? {
20805
21927
  server: {
20806
21928
  fs: {
20807
- allow: [path23.resolve(options.root || process.cwd()), ...layerRoots]
21929
+ allow: [path25.resolve(options.root || process.cwd()), ...layerRoots]
20808
21930
  }
20809
21931
  }
20810
21932
  } : {}
@@ -20835,9 +21957,9 @@ function farmPlugin(options = {}, initialPluginManager) {
20835
21957
  },
20836
21958
  server
20837
21959
  );
20838
- const globalsCSSPath = path23.join(server.config.root, "src/app/globals.css");
21960
+ const globalsCSSPath = path25.join(server.config.root, "src/app/globals.css");
20839
21961
  if (!fs7.existsSync(globalsCSSPath)) {
20840
- const appDir = path23.join(server.config.root, "src/app");
21962
+ const appDir = path25.join(server.config.root, "src/app");
20841
21963
  if (!fs7.existsSync(appDir)) {
20842
21964
  fs7.mkdirSync(appDir, { recursive: true });
20843
21965
  }
@@ -20859,10 +21981,10 @@ function farmPlugin(options = {}, initialPluginManager) {
20859
21981
  });
20860
21982
  }
20861
21983
  const sourceRoots = getFarmSourceRoots(farmConfig);
20862
- server.watcher.add(sourceRoots.map((source) => path23.join(source.root, source.srcDir)));
21984
+ server.watcher.add(sourceRoots.map((source) => path25.join(source.root, source.srcDir)));
20863
21985
  if (farmConfig.i18n.enabled) {
20864
21986
  server.watcher.add(
20865
- farmConfig.i18n.messages.includes("{locale}") ? path23.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
21987
+ farmConfig.i18n.messages.includes("{locale}") ? path25.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
20866
21988
  );
20867
21989
  }
20868
21990
  const workflowConfig = farmConfig.workflows;
@@ -20872,7 +21994,7 @@ function farmPlugin(options = {}, initialPluginManager) {
20872
21994
  ...farmDocsDevRuntime?.getFarmDocsRouteTypeEntries(farmConfig.docs) ?? []
20873
21995
  ], "getExtraRouteTypes");
20874
21996
  const appDirSlugs = sourceRoots.map(
20875
- (source) => path23.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
21997
+ (source) => path25.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
20876
21998
  );
20877
21999
  const generateTypeArtifacts = /* @__PURE__ */ __name(async (reason, selection = ALL_TYPE_ARTIFACTS, log = false) => {
20878
22000
  try {
@@ -20912,7 +22034,7 @@ function farmPlugin(options = {}, initialPluginManager) {
20912
22034
  }, "generateTypeArtifacts");
20913
22035
  await generateTypeArtifacts("startup");
20914
22036
  const srcDirSlugs = sourceRoots.map(
20915
- (source) => path23.join(source.root, source.srcDir).replace(/\\/g, "/")
22037
+ (source) => path25.join(source.root, source.srcDir).replace(/\\/g, "/")
20916
22038
  );
20917
22039
  const layerConfigFiles = new Set(
20918
22040
  farmConfig.layers.map((layer) => layer.configFile?.replace(/\\/g, "/")).filter((file) => Boolean(file))
@@ -21030,7 +22152,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21030
22152
  await apiRouteManager.discoverRoutes();
21031
22153
  let discoveredWorkflows = [];
21032
22154
  const hasWorkflowDirectory = workflowConfig.enabled && workflowConfig.dirs.some(
21033
- (dir) => fs7.existsSync(path23.isAbsolute(dir) ? dir : path23.join(farmConfig.root, dir))
22155
+ (dir) => fs7.existsSync(path25.isAbsolute(dir) ? dir : path25.join(farmConfig.root, dir))
21034
22156
  );
21035
22157
  if (hasWorkflowDirectory) {
21036
22158
  const { createFarmWorkflowRequestHandler: createFarmWorkflowRequestHandler2, discoverFarmWorkflows: discoverFarmWorkflows2 } = await loadFarmWorkflowsDevRuntime();
@@ -21094,10 +22216,20 @@ function farmPlugin(options = {}, initialPluginManager) {
21094
22216
  logger.info(`Refreshed routes: ${reason}`);
21095
22217
  }
21096
22218
  }, "refreshRouteDiscovery");
22219
+ const farmDocsFontAssetList = farmDocsDevRuntime ? resolveFarmDocsFontAssets(farmConfig.root) : [];
21097
22220
  const farmDocsHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsHandler(farmConfig.docs, {
21098
22221
  root: farmConfig.root,
21099
22222
  srcDir: farmConfig.srcDir,
21100
- clientEntry: "/@farm/client.js"
22223
+ clientEntry: "/@farm/client.js",
22224
+ fontAssets: toFarmDocsPublicFontAssets(farmDocsFontAssetList),
22225
+ resolveLayoutFonts: /* @__PURE__ */ __name(async (pathname) => {
22226
+ const layouts = routeManager.matchRoute(pathname).layouts;
22227
+ const layoutModules = await Promise.all(
22228
+ layouts.map((layout2) => routeManager.loadLayoutModule(layout2.modulePath))
22229
+ );
22230
+ return resolveFarmLayoutFonts(layoutModules);
22231
+ }, "resolveLayoutFonts"),
22232
+ fontStylesheetHref: "/@farm/fonts.css"
21101
22233
  }) : null;
21102
22234
  const farmDocsAPIHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsAPIHandler({
21103
22235
  rootDir: farmConfig.root,
@@ -21105,22 +22237,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21105
22237
  docs: farmConfig.docs
21106
22238
  }) : null;
21107
22239
  const farmDocsFontAssets = new Map(
21108
- farmDocsDevRuntime ? [
21109
- [
21110
- "/assets/Geist-Variable-CrgPqtmy.woff2",
21111
- path23.join(
21112
- farmConfig.root,
21113
- "node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2"
21114
- )
21115
- ],
21116
- [
21117
- "/assets/GeistMono-Variable-BNLlm6Cd.woff2",
21118
- path23.join(
21119
- farmConfig.root,
21120
- "node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2"
21121
- )
21122
- ]
21123
- ] : []
22240
+ farmDocsFontAssetList.map(({ url, sourcePath }) => [url, sourcePath])
21124
22241
  );
21125
22242
  const logRequest = /* @__PURE__ */ __name((method, urlPath, tag) => {
21126
22243
  try {
@@ -21184,11 +22301,12 @@ function farmPlugin(options = {}, initialPluginManager) {
21184
22301
  }
21185
22302
  }
21186
22303
  const farmDocsFontPath = farmDocsFontAssets.get(requestPathname);
21187
- if (farmDocsFontPath && fs7.existsSync(farmDocsFontPath)) {
22304
+ if ((requestMethod === "GET" || requestMethod === "HEAD") && farmDocsFontPath && fs7.existsSync(farmDocsFontPath)) {
21188
22305
  res.statusCode = 200;
21189
22306
  res.setHeader("Content-Type", "font/woff2");
21190
22307
  res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
21191
- fs7.createReadStream(farmDocsFontPath).pipe(res);
22308
+ if (requestMethod === "HEAD") res.end();
22309
+ else fs7.createReadStream(farmDocsFontPath).pipe(res);
21192
22310
  return;
21193
22311
  }
21194
22312
  if (requestMethod === "GET" && (requestPathname === FARM_DEVTOOLS_PATH || requestPathname === `${FARM_DEVTOOLS_PATH}.json`)) {
@@ -21794,6 +22912,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21794
22912
  modulePath: toUrlPath(route.modulePath),
21795
22913
  isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
21796
22914
  shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
22915
+ islandStrategy: moduleMetadata.islandStrategy,
21797
22916
  metadata: {
21798
22917
  title: mergedMetadata.title,
21799
22918
  description: mergedMetadata.description
@@ -22178,6 +23297,7 @@ export const getManifest = () => ({
22178
23297
  search: route.search,
22179
23298
  isClientComponent: moduleMetadata.isClientComponent,
22180
23299
  shouldHydrate: moduleMetadata.shouldHydrate,
23300
+ islandStrategy: moduleMetadata.islandStrategy,
22181
23301
  preloads: [route.modulePath],
22182
23302
  // In dev, preload is just the module
22183
23303
  assets: []
@@ -22304,8 +23424,8 @@ if (import.meta.hot) {
22304
23424
  }
22305
23425
  logger.info(`Found ${ssgPages.length} SSG pages, ${ssrRoutes.length} SSR routes`);
22306
23426
  logger.info("Pre-rendering SSG pages...");
22307
- const outDir = path23.join(server?.config.root || process.cwd(), options.outDir || "dist");
22308
- const clientDir = path23.join(outDir, "client");
23427
+ const outDir = path25.join(server?.config.root || process.cwd(), options.outDir || "dist");
23428
+ const clientDir = path25.join(outDir, "client");
22309
23429
  for (const page2 of ssgPages) {
22310
23430
  try {
22311
23431
  const staticRequest = new Request(new URL(page2.urlPath, "http://farm.static"));
@@ -22316,7 +23436,7 @@ if (import.meta.hot) {
22316
23436
  const layoutModules = await Promise.all(
22317
23437
  layouts.map((l) => routeManager.loadLayoutModule(l.modulePath))
22318
23438
  );
22319
- const React3 = await import("react");
23439
+ const React4 = await import("react");
22320
23440
  const { renderToString } = await import("react-dom/server");
22321
23441
  const PageComponent = mod.default;
22322
23442
  const pageProps = {
@@ -22324,14 +23444,14 @@ if (import.meta.hot) {
22324
23444
  searchParams: Promise.resolve({}),
22325
23445
  path: page2.urlPath
22326
23446
  };
22327
- let pageElement = React3.createElement(
23447
+ let pageElement = React4.createElement(
22328
23448
  PageComponent,
22329
23449
  pageProps
22330
23450
  );
22331
23451
  for (let i = layoutModules.length - 1; i >= 0; i--) {
22332
23452
  const layoutModule = layoutModules[i];
22333
23453
  const LayoutComponent = layoutModule.default;
22334
- pageElement = React3.createElement(
23454
+ pageElement = React4.createElement(
22335
23455
  LayoutComponent,
22336
23456
  {
22337
23457
  children: pageElement,
@@ -22357,8 +23477,8 @@ if (import.meta.hot) {
22357
23477
  <script type="module" src="/assets/client.js"></script>
22358
23478
  </body>
22359
23479
  </html>`;
22360
- const outputPath = page2.urlPath === "/" ? path23.join(clientDir, "index.html") : path23.join(clientDir, page2.urlPath + ".html");
22361
- fs7.mkdirSync(path23.dirname(outputPath), { recursive: true });
23480
+ const outputPath = page2.urlPath === "/" ? path25.join(clientDir, "index.html") : path25.join(clientDir, page2.urlPath + ".html");
23481
+ fs7.mkdirSync(path25.dirname(outputPath), { recursive: true });
22362
23482
  fs7.writeFileSync(outputPath, fullHtml);
22363
23483
  const revalidateInfo = page2.revalidate ? ` (revalidate: ${page2.revalidate}s)` : "";
22364
23484
  logger.success(` \u2713 ${page2.urlPath}${revalidateInfo}`);
@@ -22367,7 +23487,7 @@ if (import.meta.hot) {
22367
23487
  logger.error(` \u2717 ${page2.urlPath}: ${error}`);
22368
23488
  }
22369
23489
  }
22370
- const manifestPath = path23.join(outDir, "__ssg_manifest.json");
23490
+ const manifestPath = path25.join(outDir, "__ssg_manifest.json");
22371
23491
  fs7.writeFileSync(
22372
23492
  manifestPath,
22373
23493
  JSON.stringify(
@@ -22403,9 +23523,9 @@ if (import.meta.hot) {
22403
23523
  }
22404
23524
  const currentFarmConfig = farmApp?.getConfig();
22405
23525
  const normalizedFile = file.replace(/\\/g, "/");
22406
- const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path23.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
23526
+ const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path25.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
22407
23527
  if (currentSrcRoot && normalizedFile.startsWith(`${currentSrcRoot}/`) && isProgrammaticRoutesFileName(normalizedFile)) {
22408
- logUpdate("PAGE", `updated ${path23.basename(file)}`);
23528
+ logUpdate("PAGE", `updated ${path25.basename(file)}`);
22409
23529
  for (const mod of modules) {
22410
23530
  server2.moduleGraph.invalidateModule(mod);
22411
23531
  }
@@ -22802,6 +23922,7 @@ import React from 'react'
22802
23922
  import { hydrateRoot, createRoot } from 'react-dom/client'
22803
23923
  import { installChunkErrorRecovery, SPARouter } from '@farm.js/core/client'
22804
23924
  import { createClientPluginManager } from '@farm.js/core/plugin/client'
23925
+ import { scheduleFarmIslandHydration } from '@farm.js/core/internal/client-runtime'
22805
23926
  import { reviveDeferredData } from '@farm.js/core/deferred'
22806
23927
  import {
22807
23928
  createFarmDeploymentMismatchError,
@@ -23054,6 +24175,7 @@ class LegacyManifestSPARouter {
23054
24175
 
23055
24176
  // Only prefetch routes that will hydrate on the client.
23056
24177
  if (!match.route.isClientComponent && !match.route.shouldHydrate) return;
24178
+ if (match.route.islandStrategy && match.route.islandStrategy !== 'load') return;
23057
24179
 
23058
24180
  const modulePath = match.route.modulePath;
23059
24181
  if (this.moduleCache.has(modulePath)) return;
@@ -23198,6 +24320,12 @@ let hasClientTakenOver = false;
23198
24320
  const routeSlotRoots = new Map();
23199
24321
  const routeSlotDefinitions = new Map();
23200
24322
  let activeRouteInterception = null;
24323
+ let pendingPageHydrationController = null;
24324
+
24325
+ function cancelPendingPageHydration() {
24326
+ pendingPageHydrationController?.abort();
24327
+ pendingPageHydrationController = null;
24328
+ }
23201
24329
 
23202
24330
  function normalizeServerProps(rawProps) {
23203
24331
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -23266,7 +24394,11 @@ async function hydrateInitialRouteSlots() {
23266
24394
  routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
23267
24395
  if (!slot.isClientComponent && !slot.shouldHydrate) continue;
23268
24396
  try {
23269
- hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
24397
+ const slotHydrated = await renderRouteSlot(slot, 'hydrate');
24398
+ hydrated = slotHydrated || hydrated;
24399
+ if (slotHydrated) {
24400
+ replayPreHydrationClicks(document.getElementById(slot.containerId));
24401
+ }
23270
24402
  } catch (error) {
23271
24403
  console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
23272
24404
  }
@@ -23337,16 +24469,29 @@ function applyCanonicalPathFromProps(props) {
23337
24469
  window.history.replaceState({ ...(window.history.state || {}), path: canonicalPath }, '', canonicalPath);
23338
24470
  }
23339
24471
 
23340
- function replayPreHydrationClicks() {
23341
- window.__FARM_HYDRATED__ = true;
23342
- document.documentElement.dataset.farmHydrated = 'true';
24472
+ function replayPreHydrationClicks(container = null) {
24473
+ if (!container) {
24474
+ markFarmHydrated();
24475
+ } else {
24476
+ container.dataset.farmIslandHydrated = 'true';
24477
+ }
23343
24478
 
23344
24479
  const queue = Array.isArray(window.__FARM_PREHYDRATION_CLICK_QUEUE__)
23345
24480
  ? window.__FARM_PREHYDRATION_CLICK_QUEUE__
23346
24481
  : [];
23347
24482
  if (queue.length === 0) return;
23348
24483
 
23349
- const queuedClicks = queue.splice(0, queue.length);
24484
+ const queuedClicks = [];
24485
+ const remainingClicks = [];
24486
+ for (const queuedClick of queue) {
24487
+ const target = queuedClick?.target;
24488
+ if (!container || (target instanceof Node && container.contains(target))) {
24489
+ queuedClicks.push(queuedClick);
24490
+ } else {
24491
+ remainingClicks.push(queuedClick);
24492
+ }
24493
+ }
24494
+ queue.splice(0, queue.length, ...remainingClicks);
23350
24495
  for (const queuedClick of queuedClicks) {
23351
24496
  const target = queuedClick?.target;
23352
24497
  if (!target || typeof target.click !== 'function') continue;
@@ -23355,6 +24500,11 @@ function replayPreHydrationClicks() {
23355
24500
  }
23356
24501
  }
23357
24502
 
24503
+ function markFarmHydrated() {
24504
+ window.__FARM_HYDRATED__ = true;
24505
+ document.documentElement.dataset.farmHydrated = 'true';
24506
+ }
24507
+
23358
24508
  async function buildClientHydrationElement(PageComponent, pageProps) {
23359
24509
  let element = React.createElement(PageComponent, pageProps);
23360
24510
  const loadingModulePath = window.__FARM_LOADING_MODULE__;
@@ -23497,9 +24647,10 @@ async function tryHydrateImportedPage(
23497
24647
  layouts,
23498
24648
  useHydrate = false,
23499
24649
  existingProps = null,
24650
+ signal = null,
23500
24651
  ) {
23501
24652
  const modulePath = route?.modulePath;
23502
- if (!modulePath) {
24653
+ if (!modulePath || signal?.aborted) {
23503
24654
  return false;
23504
24655
  }
23505
24656
 
@@ -23508,6 +24659,7 @@ async function tryHydrateImportedPage(
23508
24659
  pageModule = await import(/* @vite-ignore */ modulePath);
23509
24660
  pageModuleCache.set(modulePath, pageModule);
23510
24661
  }
24662
+ if (signal?.aborted || !container?.isConnected) return false;
23511
24663
 
23512
24664
  const PageComponent = pageModule?.default;
23513
24665
  if (!PageComponent) {
@@ -23522,6 +24674,7 @@ async function tryHydrateImportedPage(
23522
24674
  window.location.pathname,
23523
24675
  existingProps,
23524
24676
  );
24677
+ if (signal?.aborted || !container?.isConnected) return false;
23525
24678
 
23526
24679
  const wrappedElement = useHydrate && container?.id === '__farm_page__'
23527
24680
  ? wrapWithIntegrationProviders(
@@ -23532,6 +24685,7 @@ async function tryHydrateImportedPage(
23532
24685
  currentPageProps,
23533
24686
  layouts,
23534
24687
  );
24688
+ if (signal?.aborted || !container?.isConnected) return false;
23535
24689
 
23536
24690
  if (useHydrate) {
23537
24691
  try {
@@ -23562,6 +24716,7 @@ async function tryHydrateImportedPage(
23562
24716
  async function renderPage(pageData) {
23563
24717
  const container = document.getElementById('root');
23564
24718
  if (!container) return;
24719
+ cancelPendingPageHydration();
23565
24720
  const destination = window.location.pathname + window.location.search;
23566
24721
 
23567
24722
  if (pageData.interception) {
@@ -23578,6 +24733,7 @@ async function renderPage(pageData) {
23578
24733
  modulePath: pageData.modulePath,
23579
24734
  isClientComponent: pageData.isClientComponent === true,
23580
24735
  shouldHydrate: pageData.shouldHydrate === true,
24736
+ islandStrategy: pageData.islandStrategy || 'load',
23581
24737
  };
23582
24738
  const params = pageData.props?.params || {};
23583
24739
  const layouts = (pageData.layoutModules || []).map((modulePath, index) => ({
@@ -23777,14 +24933,6 @@ async function hydrate() {
23777
24933
  }
23778
24934
 
23779
24935
  try {
23780
- // Pre-load layout for SPA navigation
23781
- try {
23782
- const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
23783
- LayoutComponent = layoutModule.default;
23784
- } catch (e) {
23785
- console.warn('[Farm.js] Could not preload layout:', e);
23786
- }
23787
-
23788
24936
  // Check if this is a client component (set by SSR)
23789
24937
  const isClientComponent = window.__FARM_IS_CLIENT__ === true;
23790
24938
  const modulePath = window.__FARM_PAGE_MODULE__;
@@ -23832,34 +24980,88 @@ async function hydrate() {
23832
24980
  return
23833
24981
  }
23834
24982
 
23835
- const hydrationSession = await farmClientRuntime.beginHydration({
23836
- container: pageContainer,
23837
- mode: shouldHydrate ? 'hydrate' : 'render',
23838
- });
24983
+ const currentRoute = findRoute(window.location.pathname)?.route;
24984
+ const islandStrategy = window.__FARM_ISLAND_STRATEGY__ || currentRoute?.islandStrategy || 'load';
24985
+ pageContainer.dataset.farmIsland = 'page';
24986
+ pageContainer.dataset.farmIslandStrategy = islandStrategy;
23839
24987
 
23840
- let hydrated = false;
24988
+ const hydrationPathname = window.location.pathname;
24989
+ const hydrationController = new AbortController();
24990
+ pendingPageHydrationController = hydrationController;
23841
24991
  try {
23842
- hydrated = await tryHydrateImportedPage(
23843
- pageContainer,
23844
- { modulePath },
23845
- currentPageProps.params || {},
23846
- layouts,
23847
- shouldHydrate,
23848
- currentPageProps,
23849
- );
23850
- await farmClientRuntime.completeHydration(hydrationSession);
23851
- } catch (error) {
23852
- await farmClientRuntime.failHydration(hydrationSession, error);
23853
- throw error;
23854
- }
24992
+ await scheduleFarmIslandHydration({
24993
+ container: pageContainer,
24994
+ strategy: islandStrategy,
24995
+ signal: hydrationController.signal,
24996
+ hydrate: async () => {
24997
+ if (
24998
+ hydrationController.signal.aborted ||
24999
+ !pageContainer.isConnected ||
25000
+ window.location.pathname !== hydrationPathname
25001
+ ) {
25002
+ return;
25003
+ }
23855
25004
 
23856
- if (hydrated) {
23857
- replayPreHydrationClicks();
23858
- console.log('[Farm.js] \u2705 Hydrated:', modulePath);
23859
- return;
23860
- }
25005
+ // Layout code is only needed when this route boundary actually hydrates.
25006
+ try {
25007
+ const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
25008
+ LayoutComponent = layoutModule.default;
25009
+ } catch (e) {
25010
+ console.warn('[Farm.js] Could not preload layout:', e);
25011
+ }
25012
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) return;
25013
+
25014
+ const hydrationSession = await farmClientRuntime.beginHydration({
25015
+ container: pageContainer,
25016
+ mode: shouldHydrate ? 'hydrate' : 'render',
25017
+ });
25018
+
25019
+ let hydrated = false;
25020
+ try {
25021
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
25022
+ await farmClientRuntime.failHydration(
25023
+ hydrationSession,
25024
+ new DOMException('Route hydration was cancelled', 'AbortError'),
25025
+ );
25026
+ return;
25027
+ }
25028
+ hydrated = await tryHydrateImportedPage(
25029
+ pageContainer,
25030
+ { modulePath },
25031
+ currentPageProps.params || {},
25032
+ layouts,
25033
+ shouldHydrate,
25034
+ currentPageProps,
25035
+ hydrationController.signal,
25036
+ );
25037
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
25038
+ await farmClientRuntime.failHydration(
25039
+ hydrationSession,
25040
+ new DOMException('Route hydration was cancelled', 'AbortError'),
25041
+ );
25042
+ return;
25043
+ }
25044
+ await farmClientRuntime.completeHydration(hydrationSession);
25045
+ } catch (error) {
25046
+ await farmClientRuntime.failHydration(hydrationSession, error);
25047
+ throw error;
25048
+ }
25049
+
25050
+ if (hydrated) {
25051
+ // The scheduler owns queue draining and exactly-once click replay.
25052
+ markFarmHydrated();
25053
+ console.log('[Farm.js] \u2705 Hydrated:', modulePath, '(' + islandStrategy + ')');
25054
+ return;
25055
+ }
23861
25056
 
23862
- console.log('[Farm.js] Server component - SPA router ready')
25057
+ console.log('[Farm.js] Server component - SPA router ready')
25058
+ },
25059
+ });
25060
+ } finally {
25061
+ if (pendingPageHydrationController === hydrationController) {
25062
+ pendingPageHydrationController = null;
25063
+ }
25064
+ }
23863
25065
  } catch (error) {
23864
25066
  console.error('[Farm.js] Hydration error:', error)
23865
25067
  }