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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (79) hide show
  1. package/dist/{chunk-3QDB3IA6.mjs → chunk-3WIRO3N7.mjs} +2 -2
  2. package/dist/chunk-3WIRO3N7.mjs.map +1 -0
  3. package/dist/chunk-53MVSX3R.mjs +168 -0
  4. package/dist/chunk-53MVSX3R.mjs.map +1 -0
  5. package/dist/{chunk-AW6I64AP.mjs → chunk-6ZXIQSXE.mjs} +16 -1
  6. package/dist/chunk-6ZXIQSXE.mjs.map +1 -0
  7. package/dist/chunk-7ZVN3YZI.mjs +333 -0
  8. package/dist/chunk-7ZVN3YZI.mjs.map +1 -0
  9. package/dist/{chunk-7N62DNSO.mjs → chunk-H3TE3LOV.mjs} +2 -2
  10. package/dist/{chunk-QTZZ2ISQ.mjs → chunk-KLA67WKG.mjs} +1 -1
  11. package/dist/chunk-KLA67WKG.mjs.map +1 -0
  12. package/dist/{chunk-K5ZTUF22.mjs → chunk-L2PE36GQ.mjs} +7 -3
  13. package/dist/chunk-L2PE36GQ.mjs.map +1 -0
  14. package/dist/{chunk-4LND3PXF.mjs → chunk-OPHK6OJN.mjs} +513 -48
  15. package/dist/chunk-OPHK6OJN.mjs.map +1 -0
  16. package/dist/chunk-USLFXFFH.mjs +46 -0
  17. package/dist/chunk-USLFXFFH.mjs.map +1 -0
  18. package/dist/{chunk-DTOWEQRV.mjs → chunk-VSDZEB5M.mjs} +368 -103
  19. package/dist/chunk-VSDZEB5M.mjs.map +1 -0
  20. package/dist/{chunk-E2AI2CW7.mjs → chunk-Z3YG4OZT.mjs} +727 -148
  21. package/dist/chunk-Z3YG4OZT.mjs.map +1 -0
  22. package/dist/client.cjs.map +1 -1
  23. package/dist/client.mjs +1 -1
  24. package/dist/{devtools-A22KUJZT.mjs → devtools-GAZ7F3KL.mjs} +4 -3
  25. package/dist/{devtools-A22KUJZT.mjs.map → devtools-GAZ7F3KL.mjs.map} +1 -1
  26. package/dist/docs.cjs +622 -124
  27. package/dist/docs.cjs.map +1 -1
  28. package/dist/docs.mjs +5 -3
  29. package/dist/font.cjs +19 -2
  30. package/dist/font.cjs.map +1 -1
  31. package/dist/font.mjs +7 -3
  32. package/dist/index.cjs +2208 -681
  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 +2035 -517
  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 +1523 -340
  57. package/dist/plugin.cjs.map +1 -1
  58. package/dist/plugin.mjs +9 -5
  59. package/dist/server.cjs +1636 -411
  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 +1577 -350
  67. package/dist/vite.cjs.map +1 -1
  68. package/dist/vite.mjs +9 -5
  69. package/package.json +10 -1
  70. package/dist/chunk-3QDB3IA6.mjs.map +0 -1
  71. package/dist/chunk-4LND3PXF.mjs.map +0 -1
  72. package/dist/chunk-AW6I64AP.mjs.map +0 -1
  73. package/dist/chunk-DTOWEQRV.mjs.map +0 -1
  74. package/dist/chunk-E2AI2CW7.mjs.map +0 -1
  75. package/dist/chunk-K5ZTUF22.mjs.map +0 -1
  76. package/dist/chunk-QM746KUO.mjs +0 -56
  77. package/dist/chunk-QM746KUO.mjs.map +0 -1
  78. package/dist/chunk-QTZZ2ISQ.mjs.map +0 -1
  79. /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");
@@ -15601,7 +16534,8 @@ __name(transformFarmFontCalls, "transformFarmFontCalls");
15601
16534
  function renderFarmFontDevHead(root) {
15602
16535
  const registry = registries.get(normalizeRoot(root));
15603
16536
  if (!registry || registry.production || registry.size === 0) return "";
15604
- return registry.renderPreloadTags(false);
16537
+ return `${registry.renderPreloadTags(false)}
16538
+ <link rel="stylesheet" href="/@farm/fonts.css">`;
15605
16539
  }
15606
16540
  __name(renderFarmFontDevHead, "renderFarmFontDevHead");
15607
16541
  var _FarmFontRegistry = class _FarmFontRegistry {
@@ -15783,6 +16717,10 @@ ${FONT_SECTION_END}
15783
16717
  fallback,
15784
16718
  display,
15785
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
+ })),
15786
16724
  weight: defaultWeight,
15787
16725
  style: raw.style === void 0 ? void 0 : defaultStyle,
15788
16726
  sources: normalizedSources
@@ -15879,7 +16817,7 @@ ${FONT_SECTION_END}
15879
16817
  const normalizedExtension = extension.toLowerCase();
15880
16818
  const hash = (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex").slice(0, 12);
15881
16819
  const baseName = sanitizeFileName(import_node_path4.default.basename(fileName, extension)) || "font";
15882
- const outputFileName = `assets/fonts/${baseName}-${hash}${normalizedExtension}`;
16820
+ const outputFileName = `assets/fonts/${baseName}-h${hash}${normalizedExtension}`;
15883
16821
  const key = `${hash}${normalizedExtension}`;
15884
16822
  const existing = this.resources.get(key);
15885
16823
  if (existing) return existing;
@@ -16149,7 +17087,8 @@ function serializeFontResult(font) {
16149
17087
  fontFamily: stack,
16150
17088
  ...font.style ? { fontStyle: font.style } : {},
16151
17089
  ...font.weight !== void 0 && !String(font.weight).includes(" ") ? { fontWeight: font.weight } : {}
16152
- }
17090
+ },
17091
+ preloads: font.preloadResources
16153
17092
  });
16154
17093
  }
16155
17094
  __name(serializeFontResult, "serializeFontResult");
@@ -16492,6 +17431,166 @@ function fontError(id, message) {
16492
17431
  }
16493
17432
  __name(fontError, "fontError");
16494
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
+
16495
17594
  // src/server/renderer.ts
16496
17595
  var cachedClerkProvider = null;
16497
17596
  var importRuntimeModule = new Function("specifier", "return import(specifier);");
@@ -16565,7 +17664,7 @@ function createPPRRefreshScript() {
16565
17664
  }
16566
17665
  __name(createPPRRefreshScript, "createPPRRefreshScript");
16567
17666
  function createPreHydrationClickQueueScript() {
16568
- 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>`;
16569
17668
  }
16570
17669
  __name(createPreHydrationClickQueueScript, "createPreHydrationClickQueueScript");
16571
17670
  function searchParamsToObject(searchParams) {
@@ -16616,7 +17715,7 @@ function isWebResponse(value) {
16616
17715
  return typeof Response !== "undefined" && value instanceof Response && typeof value.arrayBuffer === "function";
16617
17716
  }
16618
17717
  __name(isWebResponse, "isWebResponse");
16619
- var _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react4.default.Component {
17718
+ var _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react5.default.Component {
16620
17719
  constructor(props) {
16621
17720
  super(props);
16622
17721
  this.state = { hasError: false, error: null };
@@ -16627,7 +17726,7 @@ var _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react
16627
17726
  render() {
16628
17727
  if (this.state.hasError) {
16629
17728
  const Fallback = this.props.Fallback;
16630
- return import_react4.default.createElement(Fallback, {
17729
+ return import_react5.default.createElement(Fallback, {
16631
17730
  ...this.props.fallbackProps,
16632
17731
  error: this.state.error,
16633
17732
  reset: /* @__PURE__ */ __name(() => this.setState({ hasError: false, error: null }), "reset")
@@ -16843,14 +17942,14 @@ var _ServerRenderer = class _ServerRenderer {
16843
17942
  searchParams: Promise.resolve({}),
16844
17943
  path: page2.urlPath
16845
17944
  };
16846
- let pageElement = import_react4.default.createElement(
17945
+ let pageElement = import_react5.default.createElement(
16847
17946
  PageComponent,
16848
17947
  pageProps
16849
17948
  );
16850
17949
  for (let i = layoutModules.length - 1; i >= 0; i--) {
16851
17950
  const layoutModule = layoutModules[i];
16852
17951
  const LayoutComponent = layoutModule.default;
16853
- pageElement = import_react4.default.createElement(
17952
+ pageElement = import_react5.default.createElement(
16854
17953
  LayoutComponent,
16855
17954
  {
16856
17955
  children: pageElement,
@@ -17176,6 +18275,7 @@ var _ServerRenderer = class _ServerRenderer {
17176
18275
  req.__FARM_ROUTE__ = pathname;
17177
18276
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
17178
18277
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
18278
+ req.__FARM_ISLAND_STRATEGY__ = moduleMetadata.islandStrategy;
17179
18279
  req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
17180
18280
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
17181
18281
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
@@ -17228,12 +18328,12 @@ var _ServerRenderer = class _ServerRenderer {
17228
18328
  await _runWithMiddlewareData(middlewareDataForContext, async () => {
17229
18329
  await _runWithMiddlewareContext(middlewareContext, async () => {
17230
18330
  await _runWithCurrentRequest(currentRequest, async () => {
17231
- let pageElement = import_react4.default.createElement(
18331
+ let pageElement = import_react5.default.createElement(
17232
18332
  PageComponent,
17233
18333
  pageProps
17234
18334
  );
17235
18335
  if (LoadingFallbackComponent) {
17236
- const loadingFallback = import_react4.default.createElement(LoadingFallbackComponent, {
18336
+ const loadingFallback = import_react5.default.createElement(LoadingFallbackComponent, {
17237
18337
  ...createRouteStateProps({
17238
18338
  params,
17239
18339
  searchParamsObject,
@@ -17242,16 +18342,21 @@ var _ServerRenderer = class _ServerRenderer {
17242
18342
  pluginExposedContext
17243
18343
  })
17244
18344
  });
17245
- pageElement = import_react4.default.createElement(
17246
- import_react4.default.Suspense,
18345
+ pageElement = import_react5.default.createElement(
18346
+ import_react5.default.Suspense,
17247
18347
  { fallback: loadingFallback },
17248
18348
  pageElement
17249
18349
  );
17250
18350
  }
17251
18351
  if (isClientComponent || shouldHydrate) {
17252
- pageElement = import_react4.default.createElement(
18352
+ pageElement = import_react5.default.createElement(
17253
18353
  "div",
17254
- { 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
+ },
17255
18360
  pageElement
17256
18361
  );
17257
18362
  }
@@ -17263,11 +18368,11 @@ var _ServerRenderer = class _ServerRenderer {
17263
18368
  const slotProps = {};
17264
18369
  for (const slot of renderedRouteSlots) {
17265
18370
  if (slot.ownerPattern !== layoutEntry.pattern) continue;
17266
- let slotElement = import_react4.default.createElement(
18371
+ let slotElement = import_react5.default.createElement(
17267
18372
  slot.module.default,
17268
18373
  slot.props
17269
18374
  );
17270
- slotElement = import_react4.default.createElement(
18375
+ slotElement = import_react5.default.createElement(
17271
18376
  "div",
17272
18377
  {
17273
18378
  id: slot.containerId,
@@ -17278,7 +18383,7 @@ var _ServerRenderer = class _ServerRenderer {
17278
18383
  );
17279
18384
  slotProps[slot.name] = slotElement;
17280
18385
  }
17281
- wrappedElement = import_react4.default.createElement(
18386
+ wrappedElement = import_react5.default.createElement(
17282
18387
  LayoutComponent,
17283
18388
  {
17284
18389
  children: wrappedElement,
@@ -17288,7 +18393,7 @@ var _ServerRenderer = class _ServerRenderer {
17288
18393
  );
17289
18394
  }
17290
18395
  if (ErrorFallbackComponent) {
17291
- wrappedElement = import_react4.default.createElement(
18396
+ wrappedElement = import_react5.default.createElement(
17292
18397
  FarmRouteErrorBoundary,
17293
18398
  {
17294
18399
  Fallback: ErrorFallbackComponent,
@@ -17421,7 +18526,7 @@ var _ServerRenderer = class _ServerRenderer {
17421
18526
  if (!cachedClerkProvider) {
17422
18527
  cachedClerkProvider = await importRuntimeModule("@clerk/react");
17423
18528
  }
17424
- wrapped = import_react4.default.createElement(
18529
+ wrapped = import_react5.default.createElement(
17425
18530
  cachedClerkProvider.ClerkProvider,
17426
18531
  provider.props || {},
17427
18532
  wrapped
@@ -17496,6 +18601,13 @@ var _ServerRenderer = class _ServerRenderer {
17496
18601
  return reference;
17497
18602
  }
17498
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
+ }
17499
18611
  if (options.image.sourceType === "static") {
17500
18612
  if (!options.image.staticInfo) {
17501
18613
  throw new Error(`Static metadata image ${options.image.modulePath} is missing file info`);
@@ -17521,42 +18633,12 @@ var _ServerRenderer = class _ServerRenderer {
17521
18633
  await this.writeMetadataImageResponse(req, res, handlerResult, imageModule);
17522
18634
  }
17523
18635
  async writeMetadataImageResponse(req, res, value, imageModule) {
17524
- if (isWebResponse(value)) {
17525
- res.statusCode = value.status;
17526
- value.headers.forEach((headerValue, key) => {
17527
- res.setHeader(key, headerValue);
17528
- });
17529
- if (req.method === "HEAD") {
17530
- res.end();
17531
- return;
17532
- }
17533
- const body2 = Buffer.from(await value.arrayBuffer());
17534
- res.write(body2);
17535
- res.end();
17536
- return;
17537
- }
17538
- const contentType = imageModule.contentType || "image/svg+xml; charset=utf-8";
17539
- let body;
17540
- if (typeof value === "string") {
17541
- body = value;
17542
- } else if (value instanceof ArrayBuffer) {
17543
- body = Buffer.from(value);
17544
- } else if (ArrayBuffer.isView(value)) {
17545
- body = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
17546
- } else if (import_react4.default.isValidElement(value)) {
17547
- body = (0, import_server.renderToStaticMarkup)(value);
17548
- } else {
17549
- throw new Error("Metadata image must return a Response, string, bytes, or React element");
17550
- }
17551
- res.statusCode = res.statusCode || 200;
17552
- res.setHeader("Content-Type", contentType);
17553
- res.setHeader("Cache-Control", "public, max-age=0, must-revalidate");
17554
- if (req.method === "HEAD") {
17555
- res.end();
17556
- return;
17557
- }
17558
- res.write(body);
17559
- 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);
17560
18642
  }
17561
18643
  async writeStaticMetadataImageResponse(req, res, image) {
17562
18644
  const method = (req.method || "GET").toUpperCase();
@@ -17603,7 +18685,7 @@ var _ServerRenderer = class _ServerRenderer {
17603
18685
  return false;
17604
18686
  }
17605
18687
  const ErrorComponent = errorModule.default;
17606
- const errorElement = import_react4.default.createElement(ErrorComponent, {
18688
+ const errorElement = import_react5.default.createElement(ErrorComponent, {
17607
18689
  ...createRouteStateProps({
17608
18690
  params: options.params,
17609
18691
  searchParamsObject: options.searchParamsObject,
@@ -17621,7 +18703,7 @@ var _ServerRenderer = class _ServerRenderer {
17621
18703
  );
17622
18704
  for (let i = layoutModules.length - 1; i >= 0; i--) {
17623
18705
  const LayoutComponent = layoutModules[i].default;
17624
- wrapped = import_react4.default.createElement(
18706
+ wrapped = import_react5.default.createElement(
17625
18707
  LayoutComponent,
17626
18708
  {
17627
18709
  children: wrapped,
@@ -17686,6 +18768,7 @@ var _ServerRenderer = class _ServerRenderer {
17686
18768
  search: routeEntry.search,
17687
18769
  isClientComponent: routeEntry.isClientComponent,
17688
18770
  shouldHydrate: routeEntry.shouldHydrate,
18771
+ islandStrategy: routeEntry.islandStrategy,
17689
18772
  preloads: [routeEntry.modulePath],
17690
18773
  assets: []
17691
18774
  };
@@ -17722,6 +18805,7 @@ window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
17722
18805
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
17723
18806
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
17724
18807
  window.__FARM_SHOULD_HYDRATE__ = ${JSON.stringify(req.__FARM_SHOULD_HYDRATE__ === true)};
18808
+ window.__FARM_ISLAND_STRATEGY__ = ${JSON.stringify(req.__FARM_ISLAND_STRATEGY__ || "load")};
17725
18809
  window.__FARM_PAGE_MODULE__ = ${JSON.stringify(relativePath2)};
17726
18810
  window.__FARM_LOADING_MODULE__ = ${JSON.stringify(
17727
18811
  req.__FARM_LOADING_MODULE_PATH__ || null
@@ -17739,8 +18823,8 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
17739
18823
  const i18nSnapshot = getFarmI18nClientSnapshot();
17740
18824
  const i18nAlternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
17741
18825
  const fontHead = renderFarmFontDevHead(this.config.root || process.cwd());
17742
- const streamRoot = import_react4.default.createElement("div", { style: { display: "contents" } }, element);
17743
- 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, {
17744
18828
  onShellReady() {
17745
18829
  const shellReadyMs = Date.now() - streamStartTime;
17746
18830
  emitFarmEvent({
@@ -17898,12 +18982,12 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
17898
18982
  break;
17899
18983
  }
17900
18984
  }
17901
- let element = import_react4.default.createElement(
18985
+ let element = import_react5.default.createElement(
17902
18986
  NotFoundComponent,
17903
18987
  { pathname }
17904
18988
  );
17905
18989
  if (LayoutComponent) {
17906
- element = import_react4.default.createElement(
18990
+ element = import_react5.default.createElement(
17907
18991
  LayoutComponent,
17908
18992
  { children: element }
17909
18993
  );
@@ -18017,6 +19101,7 @@ var import_path6 = __toESM(require("path"));
18017
19101
  init_devtools_config();
18018
19102
  init_config();
18019
19103
  init_auth_config();
19104
+ init_preload();
18020
19105
  var defaultDocsConfig = {
18021
19106
  enabled: false,
18022
19107
  entry: "/docs",
@@ -18085,6 +19170,7 @@ var _FarmApp = class _FarmApp {
18085
19170
  context: config.context || (() => void 0),
18086
19171
  serverActions: resolveServerActionsConfig(config.serverActions),
18087
19172
  images: resolveFarmImageConfig(config.images),
19173
+ performance: resolveFarmPerformanceConfig(config.performance),
18088
19174
  i18n: isResolvedI18nConfig(config.i18n) ? config.i18n : resolveFarmI18nConfig(config.i18n, {
18089
19175
  root,
18090
19176
  mode: process.env.NODE_ENV === "production" ? "production" : "development"
@@ -20317,7 +21403,7 @@ init_response();
20317
21403
  init_integrations();
20318
21404
  init_devtools_config();
20319
21405
  var fs7 = __toESM(require("fs"));
20320
- var path23 = __toESM(require("path"));
21406
+ var path25 = __toESM(require("path"));
20321
21407
  init_layers();
20322
21408
 
20323
21409
  // src/after.ts
@@ -20494,8 +21580,25 @@ async function createBlurDataURL(bytes) {
20494
21580
  }
20495
21581
  __name(createBlurDataURL, "createBlurDataURL");
20496
21582
 
21583
+ // src/font.ts
21584
+ function resolveFarmLayoutFonts(layouts) {
21585
+ let resolved;
21586
+ for (const layout2 of layouts) {
21587
+ if (!layout2.fonts) continue;
21588
+ resolved = { ...resolved, ...layout2.fonts };
21589
+ }
21590
+ return resolved;
21591
+ }
21592
+ __name(resolveFarmLayoutFonts, "resolveFarmLayoutFonts");
21593
+
20497
21594
  // src/vite.ts
20498
21595
  init_image_server();
21596
+
21597
+ // src/server/vite-config.ts
21598
+ var import_node_path9 = __toESM(require("path"));
21599
+
21600
+ // src/vite.ts
21601
+ init_fonts();
20499
21602
  var ALL_TYPE_ARTIFACTS = {
20500
21603
  routes: true,
20501
21604
  api: true,
@@ -20698,7 +21801,7 @@ function fileContainsProgrammaticPageRoute(file) {
20698
21801
  __name(fileContainsProgrammaticPageRoute, "fileContainsProgrammaticPageRoute");
20699
21802
  function rewriteEarlySsrRelativeImports(options) {
20700
21803
  const cleanId = options.id.split("?", 1)[0];
20701
- if (!path23.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
21804
+ if (!path25.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
20702
21805
  return null;
20703
21806
  }
20704
21807
  const replacements = [];
@@ -20718,7 +21821,7 @@ function rewriteEarlySsrRelativeImports(options) {
20718
21821
  const suffixIndex = source.value.search(/[?#]/);
20719
21822
  const sourcePath = suffixIndex === -1 ? source.value : source.value.slice(0, suffixIndex);
20720
21823
  const suffix = suffixIndex === -1 ? "" : source.value.slice(suffixIndex);
20721
- const resolvedPath = path23.resolve(path23.dirname(cleanId), sourcePath);
21824
+ const resolvedPath = path25.resolve(path25.dirname(cleanId), sourcePath);
20722
21825
  replacements.push({
20723
21826
  start: source.start,
20724
21827
  end: source.end,
@@ -20803,7 +21906,7 @@ function farmPlugin(options = {}, initialPluginManager) {
20803
21906
  ...layerRoots.length ? {
20804
21907
  server: {
20805
21908
  fs: {
20806
- allow: [path23.resolve(options.root || process.cwd()), ...layerRoots]
21909
+ allow: [path25.resolve(options.root || process.cwd()), ...layerRoots]
20807
21910
  }
20808
21911
  }
20809
21912
  } : {}
@@ -20834,9 +21937,9 @@ function farmPlugin(options = {}, initialPluginManager) {
20834
21937
  },
20835
21938
  server
20836
21939
  );
20837
- const globalsCSSPath = path23.join(server.config.root, "src/app/globals.css");
21940
+ const globalsCSSPath = path25.join(server.config.root, "src/app/globals.css");
20838
21941
  if (!fs7.existsSync(globalsCSSPath)) {
20839
- const appDir = path23.join(server.config.root, "src/app");
21942
+ const appDir = path25.join(server.config.root, "src/app");
20840
21943
  if (!fs7.existsSync(appDir)) {
20841
21944
  fs7.mkdirSync(appDir, { recursive: true });
20842
21945
  }
@@ -20858,10 +21961,10 @@ function farmPlugin(options = {}, initialPluginManager) {
20858
21961
  });
20859
21962
  }
20860
21963
  const sourceRoots = getFarmSourceRoots(farmConfig);
20861
- server.watcher.add(sourceRoots.map((source) => path23.join(source.root, source.srcDir)));
21964
+ server.watcher.add(sourceRoots.map((source) => path25.join(source.root, source.srcDir)));
20862
21965
  if (farmConfig.i18n.enabled) {
20863
21966
  server.watcher.add(
20864
- farmConfig.i18n.messages.includes("{locale}") ? path23.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
21967
+ farmConfig.i18n.messages.includes("{locale}") ? path25.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
20865
21968
  );
20866
21969
  }
20867
21970
  const workflowConfig = farmConfig.workflows;
@@ -20871,7 +21974,7 @@ function farmPlugin(options = {}, initialPluginManager) {
20871
21974
  ...farmDocsDevRuntime?.getFarmDocsRouteTypeEntries(farmConfig.docs) ?? []
20872
21975
  ], "getExtraRouteTypes");
20873
21976
  const appDirSlugs = sourceRoots.map(
20874
- (source) => path23.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
21977
+ (source) => path25.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
20875
21978
  );
20876
21979
  const generateTypeArtifacts = /* @__PURE__ */ __name(async (reason, selection = ALL_TYPE_ARTIFACTS, log = false) => {
20877
21980
  try {
@@ -20911,7 +22014,7 @@ function farmPlugin(options = {}, initialPluginManager) {
20911
22014
  }, "generateTypeArtifacts");
20912
22015
  await generateTypeArtifacts("startup");
20913
22016
  const srcDirSlugs = sourceRoots.map(
20914
- (source) => path23.join(source.root, source.srcDir).replace(/\\/g, "/")
22017
+ (source) => path25.join(source.root, source.srcDir).replace(/\\/g, "/")
20915
22018
  );
20916
22019
  const layerConfigFiles = new Set(
20917
22020
  farmConfig.layers.map((layer) => layer.configFile?.replace(/\\/g, "/")).filter((file) => Boolean(file))
@@ -21029,7 +22132,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21029
22132
  await apiRouteManager.discoverRoutes();
21030
22133
  let discoveredWorkflows = [];
21031
22134
  const hasWorkflowDirectory = workflowConfig.enabled && workflowConfig.dirs.some(
21032
- (dir) => fs7.existsSync(path23.isAbsolute(dir) ? dir : path23.join(farmConfig.root, dir))
22135
+ (dir) => fs7.existsSync(path25.isAbsolute(dir) ? dir : path25.join(farmConfig.root, dir))
21033
22136
  );
21034
22137
  if (hasWorkflowDirectory) {
21035
22138
  const { createFarmWorkflowRequestHandler: createFarmWorkflowRequestHandler2, discoverFarmWorkflows: discoverFarmWorkflows2 } = await loadFarmWorkflowsDevRuntime();
@@ -21093,10 +22196,20 @@ function farmPlugin(options = {}, initialPluginManager) {
21093
22196
  logger.info(`Refreshed routes: ${reason}`);
21094
22197
  }
21095
22198
  }, "refreshRouteDiscovery");
22199
+ const farmDocsFontAssetList = farmDocsDevRuntime ? resolveFarmDocsFontAssets(farmConfig.root) : [];
21096
22200
  const farmDocsHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsHandler(farmConfig.docs, {
21097
22201
  root: farmConfig.root,
21098
22202
  srcDir: farmConfig.srcDir,
21099
- clientEntry: "/@farm/client.js"
22203
+ clientEntry: "/@farm/client.js",
22204
+ fontAssets: toFarmDocsPublicFontAssets(farmDocsFontAssetList),
22205
+ resolveLayoutFonts: /* @__PURE__ */ __name(async (pathname) => {
22206
+ const layouts = routeManager.matchRoute(pathname).layouts;
22207
+ const layoutModules = await Promise.all(
22208
+ layouts.map((layout2) => routeManager.loadLayoutModule(layout2.modulePath))
22209
+ );
22210
+ return resolveFarmLayoutFonts(layoutModules);
22211
+ }, "resolveLayoutFonts"),
22212
+ fontStylesheetHref: "/@farm/fonts.css"
21100
22213
  }) : null;
21101
22214
  const farmDocsAPIHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsAPIHandler({
21102
22215
  rootDir: farmConfig.root,
@@ -21104,22 +22217,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21104
22217
  docs: farmConfig.docs
21105
22218
  }) : null;
21106
22219
  const farmDocsFontAssets = new Map(
21107
- farmDocsDevRuntime ? [
21108
- [
21109
- "/assets/Geist-Variable-CrgPqtmy.woff2",
21110
- path23.join(
21111
- farmConfig.root,
21112
- "node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2"
21113
- )
21114
- ],
21115
- [
21116
- "/assets/GeistMono-Variable-BNLlm6Cd.woff2",
21117
- path23.join(
21118
- farmConfig.root,
21119
- "node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2"
21120
- )
21121
- ]
21122
- ] : []
22220
+ farmDocsFontAssetList.map(({ url, sourcePath }) => [url, sourcePath])
21123
22221
  );
21124
22222
  const logRequest = /* @__PURE__ */ __name((method, urlPath, tag) => {
21125
22223
  try {
@@ -21183,11 +22281,12 @@ function farmPlugin(options = {}, initialPluginManager) {
21183
22281
  }
21184
22282
  }
21185
22283
  const farmDocsFontPath = farmDocsFontAssets.get(requestPathname);
21186
- if (farmDocsFontPath && fs7.existsSync(farmDocsFontPath)) {
22284
+ if ((requestMethod === "GET" || requestMethod === "HEAD") && farmDocsFontPath && fs7.existsSync(farmDocsFontPath)) {
21187
22285
  res.statusCode = 200;
21188
22286
  res.setHeader("Content-Type", "font/woff2");
21189
22287
  res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
21190
- fs7.createReadStream(farmDocsFontPath).pipe(res);
22288
+ if (requestMethod === "HEAD") res.end();
22289
+ else fs7.createReadStream(farmDocsFontPath).pipe(res);
21191
22290
  return;
21192
22291
  }
21193
22292
  if (requestMethod === "GET" && (requestPathname === FARM_DEVTOOLS_PATH || requestPathname === `${FARM_DEVTOOLS_PATH}.json`)) {
@@ -21793,6 +22892,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21793
22892
  modulePath: toUrlPath(route.modulePath),
21794
22893
  isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
21795
22894
  shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
22895
+ islandStrategy: moduleMetadata.islandStrategy,
21796
22896
  metadata: {
21797
22897
  title: mergedMetadata.title,
21798
22898
  description: mergedMetadata.description
@@ -22177,6 +23277,7 @@ export const getManifest = () => ({
22177
23277
  search: route.search,
22178
23278
  isClientComponent: moduleMetadata.isClientComponent,
22179
23279
  shouldHydrate: moduleMetadata.shouldHydrate,
23280
+ islandStrategy: moduleMetadata.islandStrategy,
22180
23281
  preloads: [route.modulePath],
22181
23282
  // In dev, preload is just the module
22182
23283
  assets: []
@@ -22303,8 +23404,8 @@ if (import.meta.hot) {
22303
23404
  }
22304
23405
  logger.info(`Found ${ssgPages.length} SSG pages, ${ssrRoutes.length} SSR routes`);
22305
23406
  logger.info("Pre-rendering SSG pages...");
22306
- const outDir = path23.join(server?.config.root || process.cwd(), options.outDir || "dist");
22307
- const clientDir = path23.join(outDir, "client");
23407
+ const outDir = path25.join(server?.config.root || process.cwd(), options.outDir || "dist");
23408
+ const clientDir = path25.join(outDir, "client");
22308
23409
  for (const page2 of ssgPages) {
22309
23410
  try {
22310
23411
  const staticRequest = new Request(new URL(page2.urlPath, "http://farm.static"));
@@ -22315,7 +23416,7 @@ if (import.meta.hot) {
22315
23416
  const layoutModules = await Promise.all(
22316
23417
  layouts.map((l) => routeManager.loadLayoutModule(l.modulePath))
22317
23418
  );
22318
- const React3 = await import("react");
23419
+ const React4 = await import("react");
22319
23420
  const { renderToString } = await import("react-dom/server");
22320
23421
  const PageComponent = mod.default;
22321
23422
  const pageProps = {
@@ -22323,14 +23424,14 @@ if (import.meta.hot) {
22323
23424
  searchParams: Promise.resolve({}),
22324
23425
  path: page2.urlPath
22325
23426
  };
22326
- let pageElement = React3.createElement(
23427
+ let pageElement = React4.createElement(
22327
23428
  PageComponent,
22328
23429
  pageProps
22329
23430
  );
22330
23431
  for (let i = layoutModules.length - 1; i >= 0; i--) {
22331
23432
  const layoutModule = layoutModules[i];
22332
23433
  const LayoutComponent = layoutModule.default;
22333
- pageElement = React3.createElement(
23434
+ pageElement = React4.createElement(
22334
23435
  LayoutComponent,
22335
23436
  {
22336
23437
  children: pageElement,
@@ -22356,8 +23457,8 @@ if (import.meta.hot) {
22356
23457
  <script type="module" src="/assets/client.js"></script>
22357
23458
  </body>
22358
23459
  </html>`;
22359
- const outputPath = page2.urlPath === "/" ? path23.join(clientDir, "index.html") : path23.join(clientDir, page2.urlPath + ".html");
22360
- fs7.mkdirSync(path23.dirname(outputPath), { recursive: true });
23460
+ const outputPath = page2.urlPath === "/" ? path25.join(clientDir, "index.html") : path25.join(clientDir, page2.urlPath + ".html");
23461
+ fs7.mkdirSync(path25.dirname(outputPath), { recursive: true });
22361
23462
  fs7.writeFileSync(outputPath, fullHtml);
22362
23463
  const revalidateInfo = page2.revalidate ? ` (revalidate: ${page2.revalidate}s)` : "";
22363
23464
  logger.success(` \u2713 ${page2.urlPath}${revalidateInfo}`);
@@ -22366,7 +23467,7 @@ if (import.meta.hot) {
22366
23467
  logger.error(` \u2717 ${page2.urlPath}: ${error}`);
22367
23468
  }
22368
23469
  }
22369
- const manifestPath = path23.join(outDir, "__ssg_manifest.json");
23470
+ const manifestPath = path25.join(outDir, "__ssg_manifest.json");
22370
23471
  fs7.writeFileSync(
22371
23472
  manifestPath,
22372
23473
  JSON.stringify(
@@ -22402,9 +23503,9 @@ if (import.meta.hot) {
22402
23503
  }
22403
23504
  const currentFarmConfig = farmApp?.getConfig();
22404
23505
  const normalizedFile = file.replace(/\\/g, "/");
22405
- const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path23.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
23506
+ const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path25.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
22406
23507
  if (currentSrcRoot && normalizedFile.startsWith(`${currentSrcRoot}/`) && isProgrammaticRoutesFileName(normalizedFile)) {
22407
- logUpdate("PAGE", `updated ${path23.basename(file)}`);
23508
+ logUpdate("PAGE", `updated ${path25.basename(file)}`);
22408
23509
  for (const mod of modules) {
22409
23510
  server2.moduleGraph.invalidateModule(mod);
22410
23511
  }
@@ -22801,6 +23902,7 @@ import React from 'react'
22801
23902
  import { hydrateRoot, createRoot } from 'react-dom/client'
22802
23903
  import { installChunkErrorRecovery, SPARouter } from '@farm.js/core/client'
22803
23904
  import { createClientPluginManager } from '@farm.js/core/plugin/client'
23905
+ import { scheduleFarmIslandHydration } from '@farm.js/core/internal/client-runtime'
22804
23906
  import { reviveDeferredData } from '@farm.js/core/deferred'
22805
23907
  import {
22806
23908
  createFarmDeploymentMismatchError,
@@ -23053,6 +24155,7 @@ class LegacyManifestSPARouter {
23053
24155
 
23054
24156
  // Only prefetch routes that will hydrate on the client.
23055
24157
  if (!match.route.isClientComponent && !match.route.shouldHydrate) return;
24158
+ if (match.route.islandStrategy && match.route.islandStrategy !== 'load') return;
23056
24159
 
23057
24160
  const modulePath = match.route.modulePath;
23058
24161
  if (this.moduleCache.has(modulePath)) return;
@@ -23197,6 +24300,12 @@ let hasClientTakenOver = false;
23197
24300
  const routeSlotRoots = new Map();
23198
24301
  const routeSlotDefinitions = new Map();
23199
24302
  let activeRouteInterception = null;
24303
+ let pendingPageHydrationController = null;
24304
+
24305
+ function cancelPendingPageHydration() {
24306
+ pendingPageHydrationController?.abort();
24307
+ pendingPageHydrationController = null;
24308
+ }
23200
24309
 
23201
24310
  function normalizeServerProps(rawProps) {
23202
24311
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -23265,7 +24374,11 @@ async function hydrateInitialRouteSlots() {
23265
24374
  routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
23266
24375
  if (!slot.isClientComponent && !slot.shouldHydrate) continue;
23267
24376
  try {
23268
- hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
24377
+ const slotHydrated = await renderRouteSlot(slot, 'hydrate');
24378
+ hydrated = slotHydrated || hydrated;
24379
+ if (slotHydrated) {
24380
+ replayPreHydrationClicks(document.getElementById(slot.containerId));
24381
+ }
23269
24382
  } catch (error) {
23270
24383
  console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
23271
24384
  }
@@ -23336,16 +24449,29 @@ function applyCanonicalPathFromProps(props) {
23336
24449
  window.history.replaceState({ ...(window.history.state || {}), path: canonicalPath }, '', canonicalPath);
23337
24450
  }
23338
24451
 
23339
- function replayPreHydrationClicks() {
23340
- window.__FARM_HYDRATED__ = true;
23341
- document.documentElement.dataset.farmHydrated = 'true';
24452
+ function replayPreHydrationClicks(container = null) {
24453
+ if (!container) {
24454
+ markFarmHydrated();
24455
+ } else {
24456
+ container.dataset.farmIslandHydrated = 'true';
24457
+ }
23342
24458
 
23343
24459
  const queue = Array.isArray(window.__FARM_PREHYDRATION_CLICK_QUEUE__)
23344
24460
  ? window.__FARM_PREHYDRATION_CLICK_QUEUE__
23345
24461
  : [];
23346
24462
  if (queue.length === 0) return;
23347
24463
 
23348
- const queuedClicks = queue.splice(0, queue.length);
24464
+ const queuedClicks = [];
24465
+ const remainingClicks = [];
24466
+ for (const queuedClick of queue) {
24467
+ const target = queuedClick?.target;
24468
+ if (!container || (target instanceof Node && container.contains(target))) {
24469
+ queuedClicks.push(queuedClick);
24470
+ } else {
24471
+ remainingClicks.push(queuedClick);
24472
+ }
24473
+ }
24474
+ queue.splice(0, queue.length, ...remainingClicks);
23349
24475
  for (const queuedClick of queuedClicks) {
23350
24476
  const target = queuedClick?.target;
23351
24477
  if (!target || typeof target.click !== 'function') continue;
@@ -23354,6 +24480,11 @@ function replayPreHydrationClicks() {
23354
24480
  }
23355
24481
  }
23356
24482
 
24483
+ function markFarmHydrated() {
24484
+ window.__FARM_HYDRATED__ = true;
24485
+ document.documentElement.dataset.farmHydrated = 'true';
24486
+ }
24487
+
23357
24488
  async function buildClientHydrationElement(PageComponent, pageProps) {
23358
24489
  let element = React.createElement(PageComponent, pageProps);
23359
24490
  const loadingModulePath = window.__FARM_LOADING_MODULE__;
@@ -23496,9 +24627,10 @@ async function tryHydrateImportedPage(
23496
24627
  layouts,
23497
24628
  useHydrate = false,
23498
24629
  existingProps = null,
24630
+ signal = null,
23499
24631
  ) {
23500
24632
  const modulePath = route?.modulePath;
23501
- if (!modulePath) {
24633
+ if (!modulePath || signal?.aborted) {
23502
24634
  return false;
23503
24635
  }
23504
24636
 
@@ -23507,6 +24639,7 @@ async function tryHydrateImportedPage(
23507
24639
  pageModule = await import(/* @vite-ignore */ modulePath);
23508
24640
  pageModuleCache.set(modulePath, pageModule);
23509
24641
  }
24642
+ if (signal?.aborted || !container?.isConnected) return false;
23510
24643
 
23511
24644
  const PageComponent = pageModule?.default;
23512
24645
  if (!PageComponent) {
@@ -23521,6 +24654,7 @@ async function tryHydrateImportedPage(
23521
24654
  window.location.pathname,
23522
24655
  existingProps,
23523
24656
  );
24657
+ if (signal?.aborted || !container?.isConnected) return false;
23524
24658
 
23525
24659
  const wrappedElement = useHydrate && container?.id === '__farm_page__'
23526
24660
  ? wrapWithIntegrationProviders(
@@ -23531,6 +24665,7 @@ async function tryHydrateImportedPage(
23531
24665
  currentPageProps,
23532
24666
  layouts,
23533
24667
  );
24668
+ if (signal?.aborted || !container?.isConnected) return false;
23534
24669
 
23535
24670
  if (useHydrate) {
23536
24671
  try {
@@ -23561,6 +24696,7 @@ async function tryHydrateImportedPage(
23561
24696
  async function renderPage(pageData) {
23562
24697
  const container = document.getElementById('root');
23563
24698
  if (!container) return;
24699
+ cancelPendingPageHydration();
23564
24700
  const destination = window.location.pathname + window.location.search;
23565
24701
 
23566
24702
  if (pageData.interception) {
@@ -23577,6 +24713,7 @@ async function renderPage(pageData) {
23577
24713
  modulePath: pageData.modulePath,
23578
24714
  isClientComponent: pageData.isClientComponent === true,
23579
24715
  shouldHydrate: pageData.shouldHydrate === true,
24716
+ islandStrategy: pageData.islandStrategy || 'load',
23580
24717
  };
23581
24718
  const params = pageData.props?.params || {};
23582
24719
  const layouts = (pageData.layoutModules || []).map((modulePath, index) => ({
@@ -23776,14 +24913,6 @@ async function hydrate() {
23776
24913
  }
23777
24914
 
23778
24915
  try {
23779
- // Pre-load layout for SPA navigation
23780
- try {
23781
- const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
23782
- LayoutComponent = layoutModule.default;
23783
- } catch (e) {
23784
- console.warn('[Farm.js] Could not preload layout:', e);
23785
- }
23786
-
23787
24916
  // Check if this is a client component (set by SSR)
23788
24917
  const isClientComponent = window.__FARM_IS_CLIENT__ === true;
23789
24918
  const modulePath = window.__FARM_PAGE_MODULE__;
@@ -23831,34 +24960,88 @@ async function hydrate() {
23831
24960
  return
23832
24961
  }
23833
24962
 
23834
- const hydrationSession = await farmClientRuntime.beginHydration({
23835
- container: pageContainer,
23836
- mode: shouldHydrate ? 'hydrate' : 'render',
23837
- });
24963
+ const currentRoute = findRoute(window.location.pathname)?.route;
24964
+ const islandStrategy = window.__FARM_ISLAND_STRATEGY__ || currentRoute?.islandStrategy || 'load';
24965
+ pageContainer.dataset.farmIsland = 'page';
24966
+ pageContainer.dataset.farmIslandStrategy = islandStrategy;
23838
24967
 
23839
- let hydrated = false;
24968
+ const hydrationPathname = window.location.pathname;
24969
+ const hydrationController = new AbortController();
24970
+ pendingPageHydrationController = hydrationController;
23840
24971
  try {
23841
- hydrated = await tryHydrateImportedPage(
23842
- pageContainer,
23843
- { modulePath },
23844
- currentPageProps.params || {},
23845
- layouts,
23846
- shouldHydrate,
23847
- currentPageProps,
23848
- );
23849
- await farmClientRuntime.completeHydration(hydrationSession);
23850
- } catch (error) {
23851
- await farmClientRuntime.failHydration(hydrationSession, error);
23852
- throw error;
23853
- }
24972
+ await scheduleFarmIslandHydration({
24973
+ container: pageContainer,
24974
+ strategy: islandStrategy,
24975
+ signal: hydrationController.signal,
24976
+ hydrate: async () => {
24977
+ if (
24978
+ hydrationController.signal.aborted ||
24979
+ !pageContainer.isConnected ||
24980
+ window.location.pathname !== hydrationPathname
24981
+ ) {
24982
+ return;
24983
+ }
23854
24984
 
23855
- if (hydrated) {
23856
- replayPreHydrationClicks();
23857
- console.log('[Farm.js] \u2705 Hydrated:', modulePath);
23858
- return;
23859
- }
24985
+ // Layout code is only needed when this route boundary actually hydrates.
24986
+ try {
24987
+ const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
24988
+ LayoutComponent = layoutModule.default;
24989
+ } catch (e) {
24990
+ console.warn('[Farm.js] Could not preload layout:', e);
24991
+ }
24992
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) return;
24993
+
24994
+ const hydrationSession = await farmClientRuntime.beginHydration({
24995
+ container: pageContainer,
24996
+ mode: shouldHydrate ? 'hydrate' : 'render',
24997
+ });
24998
+
24999
+ let hydrated = false;
25000
+ try {
25001
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
25002
+ await farmClientRuntime.failHydration(
25003
+ hydrationSession,
25004
+ new DOMException('Route hydration was cancelled', 'AbortError'),
25005
+ );
25006
+ return;
25007
+ }
25008
+ hydrated = await tryHydrateImportedPage(
25009
+ pageContainer,
25010
+ { modulePath },
25011
+ currentPageProps.params || {},
25012
+ layouts,
25013
+ shouldHydrate,
25014
+ currentPageProps,
25015
+ hydrationController.signal,
25016
+ );
25017
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
25018
+ await farmClientRuntime.failHydration(
25019
+ hydrationSession,
25020
+ new DOMException('Route hydration was cancelled', 'AbortError'),
25021
+ );
25022
+ return;
25023
+ }
25024
+ await farmClientRuntime.completeHydration(hydrationSession);
25025
+ } catch (error) {
25026
+ await farmClientRuntime.failHydration(hydrationSession, error);
25027
+ throw error;
25028
+ }
25029
+
25030
+ if (hydrated) {
25031
+ // The scheduler owns queue draining and exactly-once click replay.
25032
+ markFarmHydrated();
25033
+ console.log('[Farm.js] \u2705 Hydrated:', modulePath, '(' + islandStrategy + ')');
25034
+ return;
25035
+ }
23860
25036
 
23861
- console.log('[Farm.js] Server component - SPA router ready')
25037
+ console.log('[Farm.js] Server component - SPA router ready')
25038
+ },
25039
+ });
25040
+ } finally {
25041
+ if (pendingPageHydrationController === hydrationController) {
25042
+ pendingPageHydrationController = null;
25043
+ }
25044
+ }
23862
25045
  } catch (error) {
23863
25046
  console.error('[Farm.js] Hydration error:', error)
23864
25047
  }