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