@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
@@ -784,9 +784,9 @@ function getFarmIntegrationPluginServerRuntime(plugin) {
784
784
  const value = plugin[FARM_INTEGRATION_PLUGIN_SERVER_RUNTIME];
785
785
  return typeof value === "boolean" ? value : void 0;
786
786
  }
787
- function defineTypedIntegrationRoute(method, path29, input) {
787
+ function defineTypedIntegrationRoute(method, path31, input) {
788
788
  return {
789
- path: path29,
789
+ path: path31,
790
790
  method,
791
791
  middleware: input.middleware,
792
792
  before: input.before,
@@ -796,7 +796,7 @@ function defineTypedIntegrationRoute(method, path29, input) {
796
796
  input: normalizeIntegrationRouteInputSchemas(input),
797
797
  handler: input.handler,
798
798
  __operation: defineIntegrationAPIOperation({
799
- path: path29,
799
+ path: path31,
800
800
  method,
801
801
  bodyFormat: input.bodyFormat,
802
802
  responseFormat: input.responseFormat,
@@ -808,52 +808,52 @@ function defineTypedIntegrationRoute(method, path29, input) {
808
808
  }
809
809
  function createIntegrationRouteFactory() {
810
810
  return {
811
- get(path29, input) {
811
+ get(path31, input) {
812
812
  return defineTypedIntegrationRoute(
813
813
  "GET",
814
- path29,
814
+ path31,
815
815
  input
816
816
  );
817
817
  },
818
- post(path29, input) {
818
+ post(path31, input) {
819
819
  return defineTypedIntegrationRoute(
820
820
  "POST",
821
- path29,
821
+ path31,
822
822
  {
823
823
  bodyFormat: "json",
824
824
  ...input
825
825
  }
826
826
  );
827
827
  },
828
- put(path29, input) {
828
+ put(path31, input) {
829
829
  return defineTypedIntegrationRoute(
830
830
  "PUT",
831
- path29,
831
+ path31,
832
832
  {
833
833
  bodyFormat: "json",
834
834
  ...input
835
835
  }
836
836
  );
837
837
  },
838
- patch(path29, input) {
839
- return defineTypedIntegrationRoute("PATCH", path29, {
838
+ patch(path31, input) {
839
+ return defineTypedIntegrationRoute("PATCH", path31, {
840
840
  bodyFormat: "json",
841
841
  ...input
842
842
  });
843
843
  },
844
- delete(path29, input) {
845
- return defineTypedIntegrationRoute("DELETE", path29, {
844
+ delete(path31, input) {
845
+ return defineTypedIntegrationRoute("DELETE", path31, {
846
846
  bodyFormat: "json",
847
847
  ...input
848
848
  });
849
849
  },
850
- options(path29, input) {
851
- return defineTypedIntegrationRoute("OPTIONS", path29, input);
850
+ options(path31, input) {
851
+ return defineTypedIntegrationRoute("OPTIONS", path31, input);
852
852
  },
853
- head(path29, input) {
853
+ head(path31, input) {
854
854
  return defineTypedIntegrationRoute(
855
855
  "HEAD",
856
- path29,
856
+ path31,
857
857
  input
858
858
  );
859
859
  }
@@ -1070,8 +1070,8 @@ async function parseIntegrationConfigSchema(integration, schema, value) {
1070
1070
  function createIntegrationConfigValidationError(integration, error) {
1071
1071
  const issues = Array.isArray(error.issues) && error.issues.length > 0 ? error.issues.map((issue) => {
1072
1072
  const normalizedPath = normalizeIntegrationValidationPath(issue.path);
1073
- const path29 = normalizedPath.length ? `${normalizedPath.join(".")}: ` : "";
1074
- return `${path29}${issue.message || "Invalid config"}`;
1073
+ const path31 = normalizedPath.length ? `${normalizedPath.join(".")}: ` : "";
1074
+ return `${path31}${issue.message || "Invalid config"}`;
1075
1075
  }).join("; ") : error.message || "Invalid config";
1076
1076
  return new Error(`Integration "${integration.type}" config validation failed: ${issues}`);
1077
1077
  }
@@ -1756,8 +1756,8 @@ function normalizeIntegrationValidationIssues(source, error) {
1756
1756
  }
1757
1757
  ];
1758
1758
  }
1759
- function normalizeIntegrationValidationPath(path29) {
1760
- return (path29 || []).map((segment) => {
1759
+ function normalizeIntegrationValidationPath(path31) {
1760
+ return (path31 || []).map((segment) => {
1761
1761
  const key = typeof segment === "object" && segment !== null && "key" in segment ? segment.key : segment;
1762
1762
  return typeof key === "symbol" ? key.description || key.toString() : key;
1763
1763
  });
@@ -2956,26 +2956,26 @@ async function prepareFarmWorkflowsForNitro(config) {
2956
2956
  const root = config.root || process.cwd();
2957
2957
  const distDir = config.distDir || ".farm";
2958
2958
  const fs8 = await import("fs/promises");
2959
- const path29 = await import("path");
2960
- const generatedDir = path29.join(root, distDir, ".nitro", "farm-workflows");
2959
+ const path31 = await import("path");
2960
+ const generatedDir = path31.join(root, distDir, ".nitro", "farm-workflows");
2961
2961
  await fs8.mkdir(generatedDir, { recursive: true });
2962
2962
  const tasks = {};
2963
2963
  const scheduledTasks = createScheduledTasks(workflows);
2964
2964
  for (const workflow of workflows) {
2965
- const wrapperPath = path29.join(generatedDir, `${safeFileName(workflow.id)}.mjs`);
2965
+ const wrapperPath = path31.join(generatedDir, `${safeFileName(workflow.id)}.mjs`);
2966
2966
  await fs8.writeFile(wrapperPath, createNitroTaskWrapper(workflow), "utf8");
2967
2967
  tasks[workflow.id] = {
2968
2968
  handler: wrapperPath,
2969
2969
  description: workflow.description || `Farm workflow ${workflow.id}`
2970
2970
  };
2971
2971
  }
2972
- const handlerPath = path29.join(generatedDir, "http-handler.mjs");
2972
+ const handlerPath = path31.join(generatedDir, "http-handler.mjs");
2973
2973
  await fs8.writeFile(
2974
2974
  handlerPath,
2975
2975
  createNitroWorkflowHTTPHandler(workflowConfig, workflows),
2976
2976
  "utf8"
2977
2977
  );
2978
- const manifestPath = path29.join(generatedDir, "manifest.json");
2978
+ const manifestPath = path31.join(generatedDir, "manifest.json");
2979
2979
  await fs8.writeFile(
2980
2980
  manifestPath,
2981
2981
  JSON.stringify(
@@ -3058,10 +3058,10 @@ function resolveWorkflowDefinition(module2) {
3058
3058
  }
3059
3059
  async function findWorkflowFiles(root, dirs) {
3060
3060
  const fs8 = await import("fs/promises");
3061
- const path29 = await import("path");
3061
+ const path31 = await import("path");
3062
3062
  const files = [];
3063
3063
  for (const dir of dirs) {
3064
- const absoluteDir = path29.isAbsolute(dir) ? dir : path29.join(root, dir);
3064
+ const absoluteDir = path31.isAbsolute(dir) ? dir : path31.join(root, dir);
3065
3065
  if (!await pathExists(absoluteDir)) continue;
3066
3066
  await walkWorkflowDir(absoluteDir, files);
3067
3067
  }
@@ -3069,10 +3069,10 @@ async function findWorkflowFiles(root, dirs) {
3069
3069
  }
3070
3070
  async function walkWorkflowDir(dir, files) {
3071
3071
  const fs8 = await import("fs/promises");
3072
- const path29 = await import("path");
3072
+ const path31 = await import("path");
3073
3073
  const entries = await fs8.readdir(dir, { withFileTypes: true });
3074
3074
  for (const entry of entries) {
3075
- const filePath = path29.join(dir, entry.name);
3075
+ const filePath = path31.join(dir, entry.name);
3076
3076
  if (entry.isDirectory()) {
3077
3077
  if (entry.name === "node_modules" || entry.name.startsWith(".")) continue;
3078
3078
  await walkWorkflowDir(filePath, files);
@@ -3092,12 +3092,12 @@ function isWorkflowFile(fileName) {
3092
3092
  }
3093
3093
  async function loadWorkflowModule(filePath, root) {
3094
3094
  const fs8 = await import("fs/promises");
3095
- const path29 = await import("path");
3095
+ const path31 = await import("path");
3096
3096
  const { pathToFileURL: pathToFileURL5 } = await import("url");
3097
3097
  const { build: build2 } = await import("esbuild");
3098
- const outDir = path29.join(root, ".farm", ".workflow-loader");
3098
+ const outDir = path31.join(root, ".farm", ".workflow-loader");
3099
3099
  await fs8.mkdir(outDir, { recursive: true });
3100
- const outfile = path29.join(
3100
+ const outfile = path31.join(
3101
3101
  outDir,
3102
3102
  `workflow-${Date.now()}-${Math.random().toString(36).slice(2)}.mjs`
3103
3103
  );
@@ -3376,9 +3376,9 @@ async function prepareFarmCronForNitro(config) {
3376
3376
  const root = config.root || process.cwd();
3377
3377
  const distDir = config.distDir || ".farm";
3378
3378
  const fs8 = await import("fs/promises");
3379
- const path29 = await import("path");
3380
- const manifestPath = path29.join(root, distDir, FARM_CRON_MANIFEST);
3381
- const generatedDir = path29.join(root, distDir, ".nitro", "farm-cron");
3379
+ const path31 = await import("path");
3380
+ const manifestPath = path31.join(root, distDir, FARM_CRON_MANIFEST);
3381
+ const generatedDir = path31.join(root, distDir, ".nitro", "farm-cron");
3382
3382
  if (!cron.enabled) {
3383
3383
  await Promise.all([
3384
3384
  fs8.rm(manifestPath, { force: true }),
@@ -3395,7 +3395,7 @@ async function prepareFarmCronForNitro(config) {
3395
3395
  const tasks = {};
3396
3396
  for (const job of cron.jobs) {
3397
3397
  const taskName = getFarmCronTaskName(job.name);
3398
- const wrapperPath = path29.join(generatedDir, `${safeFileName2(job.name)}.mjs`);
3398
+ const wrapperPath = path31.join(generatedDir, `${safeFileName2(job.name)}.mjs`);
3399
3399
  await fs8.writeFile(wrapperPath, createNitroCronTaskWrapper(job, cron.secretEnv), "utf8");
3400
3400
  tasks[taskName] = {
3401
3401
  handler: wrapperPath,
@@ -3403,7 +3403,7 @@ async function prepareFarmCronForNitro(config) {
3403
3403
  };
3404
3404
  }
3405
3405
  const manifest = createFarmCronManifest(cron);
3406
- await fs8.mkdir(path29.dirname(manifestPath), { recursive: true });
3406
+ await fs8.mkdir(path31.dirname(manifestPath), { recursive: true });
3407
3407
  await fs8.writeFile(manifestPath, `${JSON.stringify(manifest, null, 2)}
3408
3408
  `, "utf8");
3409
3409
  return {
@@ -3485,7 +3485,7 @@ function normalizeCronJob(name, job) {
3485
3485
  if (!job || typeof job !== "object" || Array.isArray(job)) {
3486
3486
  throw new TypeError(`Farm cron ${JSON.stringify(name)} must be a configuration object.`);
3487
3487
  }
3488
- const path29 = normalizeCronPath(name, job.path);
3488
+ const path31 = normalizeCronPath(name, job.path);
3489
3489
  const rawSchedule = Array.isArray(job.schedule) ? job.schedule : [job.schedule];
3490
3490
  const schedule = [...new Set(rawSchedule.map((value) => normalizeCronExpression(name, value)))];
3491
3491
  if (schedule.length === 0) {
@@ -3494,7 +3494,7 @@ function normalizeCronJob(name, job) {
3494
3494
  return {
3495
3495
  name,
3496
3496
  schedule,
3497
- path: path29,
3497
+ path: path31,
3498
3498
  description: job.description?.trim() || void 0
3499
3499
  };
3500
3500
  }
@@ -3502,13 +3502,13 @@ function normalizeCronPath(name, value) {
3502
3502
  if (typeof value !== "string" || !value.trim().startsWith("/")) {
3503
3503
  throw new TypeError(`Farm cron ${JSON.stringify(name)} path must start with "/".`);
3504
3504
  }
3505
- const path29 = value.trim();
3506
- if (path29.startsWith("//") || path29.includes("?") || path29.includes("#")) {
3505
+ const path31 = value.trim();
3506
+ if (path31.startsWith("//") || path31.includes("?") || path31.includes("#")) {
3507
3507
  throw new TypeError(
3508
3508
  `Farm cron ${JSON.stringify(name)} path must be an application pathname without a host, query, or hash.`
3509
3509
  );
3510
3510
  }
3511
- return path29.length > 1 ? path29.replace(/\/+$/, "") : path29;
3511
+ return path31.length > 1 ? path31.replace(/\/+$/, "") : path31;
3512
3512
  }
3513
3513
  function normalizeCronExpression(name, value) {
3514
3514
  if (typeof value !== "string") {
@@ -4904,8 +4904,8 @@ function createFarmDeploymentMismatchResponse(mismatch) {
4904
4904
  }
4905
4905
  );
4906
4906
  }
4907
- function createFarmDeploymentCookie(deploymentId, path29 = "/", secure = false) {
4908
- const normalizedPath = path29.startsWith("/") ? path29 : `/${path29}`;
4907
+ function createFarmDeploymentCookie(deploymentId, path31 = "/", secure = false) {
4908
+ const normalizedPath = path31.startsWith("/") ? path31 : `/${path31}`;
4909
4909
  return [
4910
4910
  `${FARM_DEPLOYMENT_COOKIE}=${encodeURIComponent(deploymentId)}`,
4911
4911
  `Path=${normalizedPath}`,
@@ -4976,7 +4976,7 @@ var init_devtools_config = __esm({
4976
4976
 
4977
4977
  // src/image-config.ts
4978
4978
  function resolveFarmImageConfig(config) {
4979
- const path29 = normalizeImagePath(config?.path ?? DEFAULT_FARM_IMAGE_PATH);
4979
+ const path31 = normalizeImagePath(config?.path ?? DEFAULT_FARM_IMAGE_PATH);
4980
4980
  const deviceSizes = normalizeIntegerList(
4981
4981
  config?.deviceSizes ?? DEFAULT_FARM_IMAGE_DEVICE_SIZES,
4982
4982
  "images.deviceSizes"
@@ -5004,7 +5004,7 @@ function resolveFarmImageConfig(config) {
5004
5004
  );
5005
5005
  return Object.freeze({
5006
5006
  provider: config?.provider ?? "auto",
5007
- path: path29,
5007
+ path: path31,
5008
5008
  domains: Object.freeze([
5009
5009
  ...new Set(
5010
5010
  (config?.domains ?? []).map((domain) => domain.trim().toLowerCase()).filter(Boolean)
@@ -5043,11 +5043,11 @@ function getPublicFarmImageConfig(config) {
5043
5043
  };
5044
5044
  }
5045
5045
  function normalizeImagePath(value) {
5046
- const path29 = value.trim().replace(/\/+$/, "") || "/";
5047
- if (!path29.startsWith("/") || path29.includes("?") || path29.includes("#")) {
5046
+ const path31 = value.trim().replace(/\/+$/, "") || "/";
5047
+ if (!path31.startsWith("/") || path31.includes("?") || path31.includes("#")) {
5048
5048
  throw new TypeError("images.path must be an absolute pathname without a query or hash");
5049
5049
  }
5050
- return path29;
5050
+ return path31;
5051
5051
  }
5052
5052
  function normalizeIntegerList(values, name, max) {
5053
5053
  if (values.length === 0) {
@@ -5361,6 +5361,33 @@ var init_auth_config = __esm({
5361
5361
  }
5362
5362
  });
5363
5363
 
5364
+ // src/preload.ts
5365
+ function resolveFarmPerformanceConfig(config) {
5366
+ return {
5367
+ preload: {
5368
+ mode: config?.preload?.mode === "warn" ? "warn" : "enforce",
5369
+ maxImages: normalizeBudget(config?.preload?.maxImages, DEFAULT_PRELOAD_CONFIG.maxImages),
5370
+ maxFonts: normalizeBudget(config?.preload?.maxFonts, DEFAULT_PRELOAD_CONFIG.maxFonts)
5371
+ }
5372
+ };
5373
+ }
5374
+ function normalizeBudget(value, fallback) {
5375
+ return typeof value === "number" && Number.isFinite(value) && value >= 0 ? Math.floor(value) : fallback;
5376
+ }
5377
+ var DEFAULT_PRELOAD_CONFIG;
5378
+ var init_preload = __esm({
5379
+ "src/preload.ts"() {
5380
+ "use strict";
5381
+ DEFAULT_PRELOAD_CONFIG = {
5382
+ mode: "enforce",
5383
+ maxImages: 1,
5384
+ maxFonts: 2
5385
+ };
5386
+ __name(resolveFarmPerformanceConfig, "resolveFarmPerformanceConfig");
5387
+ __name(normalizeBudget, "normalizeBudget");
5388
+ }
5389
+ });
5390
+
5364
5391
  // src/config-entry.ts
5365
5392
  var init_config_entry = __esm({
5366
5393
  "src/config-entry.ts"() {
@@ -5481,18 +5508,18 @@ function sanitizeDocsConfig(config) {
5481
5508
  return cloneSerializable(config) || {};
5482
5509
  }
5483
5510
  async function findDocsConfigPath(rootDir, configPath) {
5484
- const { existsSync: existsSync16 } = await import("fs");
5511
+ const { existsSync: existsSync17 } = await import("fs");
5485
5512
  const root = import_path2.default.resolve(rootDir || process.cwd());
5486
5513
  if (configPath) {
5487
5514
  const resolvedPath = import_path2.default.isAbsolute(configPath) ? configPath : import_path2.default.join(root, configPath);
5488
- if (!existsSync16(resolvedPath)) {
5515
+ if (!existsSync17(resolvedPath)) {
5489
5516
  throw new Error(`Could not find docs config at ${configPath}.`);
5490
5517
  }
5491
5518
  return import_path2.default.resolve(resolvedPath);
5492
5519
  }
5493
5520
  for (const filename of DOCS_CONFIG_FILENAMES) {
5494
5521
  const resolvedPath = import_path2.default.join(root, filename);
5495
- if (existsSync16(resolvedPath)) {
5522
+ if (existsSync17(resolvedPath)) {
5496
5523
  return resolvedPath;
5497
5524
  }
5498
5525
  }
@@ -5680,6 +5707,7 @@ async function resolveConfig(userConfig, mode) {
5680
5707
  headers: /* @__PURE__ */ __name(() => [...headers, ...routeRuleHeaders], "headers"),
5681
5708
  routeRules,
5682
5709
  images: resolveFarmImageConfig(userConfig.images),
5710
+ performance: resolveFarmPerformanceConfig(userConfig.performance),
5683
5711
  publicDir: userConfig.publicDir || "public",
5684
5712
  i18n: resolveFarmI18nConfig(userConfig.i18n, { root, mode }),
5685
5713
  openapi: {
@@ -5718,7 +5746,7 @@ async function resolveConfig(userConfig, mode) {
5718
5746
  return resolved;
5719
5747
  }
5720
5748
  async function loadConfig(rootDir, configPath, mode = process.env.NODE_ENV === "production" ? "production" : "development", loadEnvironment) {
5721
- const { existsSync: existsSync16 } = await import("fs");
5749
+ const { existsSync: existsSync17 } = await import("fs");
5722
5750
  const loadEnv = loadEnvironment || (await import("vite")).loadEnv;
5723
5751
  const root = rootDir || process.cwd();
5724
5752
  const loadedEnv = loadEnv(mode, root, "");
@@ -5742,7 +5770,7 @@ async function loadConfig(rootDir, configPath, mode = process.env.NODE_ENV === "
5742
5770
  try {
5743
5771
  const absolutePath = relativePath2.startsWith("/") ? relativePath2 : import_path2.default.join(root, relativePath2);
5744
5772
  const normalizedPath = import_path2.default.resolve(absolutePath);
5745
- if (!existsSync16(normalizedPath)) {
5773
+ if (!existsSync17(normalizedPath)) {
5746
5774
  continue;
5747
5775
  }
5748
5776
  const loadedConfig = await loadFarmConfigFile(normalizedPath, { root });
@@ -5773,6 +5801,7 @@ var init_config2 = __esm({
5773
5801
  init_image_config();
5774
5802
  init_config();
5775
5803
  init_auth_config();
5804
+ init_preload();
5776
5805
  init_config_entry();
5777
5806
  FARM_RESOLVED_CUSTOM_CONTEXT = /* @__PURE__ */ Symbol.for("farm.resolvedCustomContext");
5778
5807
  __name(hasCustomFarmRouteContext, "hasCustomFarmRouteContext");
@@ -6140,6 +6169,210 @@ async function resolveRouteRenderingConfigFromFile(mod, filePath) {
6140
6169
  const source = await (0, import_promises3.readFile)(filePath, "utf8").catch(() => void 0);
6141
6170
  return resolveRouteRenderingConfig(mod, source);
6142
6171
  }
6172
+ function analyzeStaticRouteCandidate(mod, source, options = {}) {
6173
+ const blockers = [];
6174
+ if (!source) {
6175
+ blockers.push("route source is unavailable");
6176
+ }
6177
+ if (options.isDynamic) {
6178
+ blockers.push("the route has dynamic path segments");
6179
+ }
6180
+ if (hasExplicitRenderingConfig(mod, source)) {
6181
+ blockers.push("the route already has explicit rendering config");
6182
+ }
6183
+ if (source) {
6184
+ blockers.push(...findRequestBoundSourceBlockers(source));
6185
+ }
6186
+ return {
6187
+ candidate: blockers.length === 0,
6188
+ blockers
6189
+ };
6190
+ }
6191
+ function findRequestBoundSourceBlockers(source) {
6192
+ const blockers = /* @__PURE__ */ new Set();
6193
+ const code = stripCommentsAndLiteralContents(source);
6194
+ const sourceWithoutComments = stripComments(source);
6195
+ for (const binding of readRequestApiImportBindings(sourceWithoutComments)) {
6196
+ if (new RegExp(`\\b${escapeRegExp2(binding.local)}\\s*(?:\\?\\.\\s*)?\\(`).test(code)) {
6197
+ blockers.add(`the route reads ${binding.label}`);
6198
+ }
6199
+ }
6200
+ for (const namespace of readNamespaceImportBindings(sourceWithoutComments)) {
6201
+ for (const [name, label] of REQUEST_API_LABELS) {
6202
+ if (new RegExp(
6203
+ `\\b${escapeRegExp2(namespace)}\\s*(?:\\?\\.\\s*|\\.\\s*)${name}\\s*(?:\\?\\.\\s*)?\\(`
6204
+ ).test(code)) {
6205
+ blockers.add(`the route reads ${label}`);
6206
+ }
6207
+ }
6208
+ }
6209
+ const params = readDefaultRouteParameters(code);
6210
+ if (params) {
6211
+ for (const [name, label] of ROUTE_PROP_LABELS) {
6212
+ if (new RegExp(`\\b${name}\\b`).test(params)) {
6213
+ blockers.add(`the route reads ${label}`);
6214
+ }
6215
+ }
6216
+ const propsName = params.match(/^\s*([A-Za-z_$][\w$]*)\s*(?::|,|$)/)?.[1];
6217
+ if (propsName) {
6218
+ for (const [name, label] of ROUTE_PROP_LABELS) {
6219
+ if (new RegExp(`\\b${escapeRegExp2(propsName)}\\s*\\.\\s*${name}\\b`).test(code)) {
6220
+ blockers.add(`the route reads ${label}`);
6221
+ }
6222
+ if (new RegExp(`\\{[^}]*\\b${name}\\b[^}]*\\}\\s*=\\s*${escapeRegExp2(propsName)}\\b`).test(
6223
+ code
6224
+ )) {
6225
+ blockers.add(`the route reads ${label}`);
6226
+ }
6227
+ }
6228
+ }
6229
+ }
6230
+ return [...blockers];
6231
+ }
6232
+ function readRequestApiImportBindings(source) {
6233
+ const bindings = [];
6234
+ for (const clause of readStaticImportClauses(source)) {
6235
+ const namedGroup = clause.match(/\{([\s\S]*?)\}/)?.[1];
6236
+ for (const rawSpecifier of (namedGroup ?? "").split(",")) {
6237
+ const specifier = rawSpecifier.trim().replace(/^type\s+/, "");
6238
+ const imported = specifier.match(/^([A-Za-z_$][\w$]*)(?:\s+as\s+([A-Za-z_$][\w$]*))?$/);
6239
+ if (!imported?.[1]) continue;
6240
+ const label = REQUEST_API_LABELS.get(imported[1]);
6241
+ if (label) bindings.push({ local: imported[2] ?? imported[1], label });
6242
+ }
6243
+ const local = clause.match(/^\s*([A-Za-z_$][\w$]*)\s*(?:,|$)/)?.[1];
6244
+ if (local) {
6245
+ const label = REQUEST_API_LABELS.get(local);
6246
+ if (label) bindings.push({ local, label });
6247
+ }
6248
+ }
6249
+ return bindings;
6250
+ }
6251
+ function readNamespaceImportBindings(source) {
6252
+ return readStaticImportClauses(source).map((clause) => clause.match(/\*\s*as\s*([A-Za-z_$][\w$]*)/)?.[1]).filter((binding) => Boolean(binding));
6253
+ }
6254
+ function readStaticImportClauses(source) {
6255
+ return [...source.matchAll(/\bimport\s+(?!\()([\s\S]*?)\s+from\s*["'][^"']+["']/g)].map((match) => match[1]).filter((clause) => Boolean(clause));
6256
+ }
6257
+ function readDefaultRouteParameters(code) {
6258
+ const inlineParams = code.match(/\bexport\s+default\s+(?:async\s+)?function\b[^(]*\(([^)]*)\)/)?.[1] ?? code.match(/\bexport\s+default\s+(?:async\s+)?\(([^)]*)\)\s*=>/)?.[1];
6259
+ if (inlineParams !== void 0) return inlineParams;
6260
+ 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];
6261
+ if (!binding) return void 0;
6262
+ const escapedBinding = escapeRegExp2(binding);
6263
+ return code.match(new RegExp(`\\bfunction\\s+${escapedBinding}\\s*\\(([^)]*)\\)`))?.[1] ?? code.match(
6264
+ new RegExp(
6265
+ `\\b(?:const|let|var)\\s+${escapedBinding}\\s*=\\s*(?:async\\s+)?\\(([^)]*)\\)\\s*=>`
6266
+ )
6267
+ )?.[1] ?? code.match(
6268
+ new RegExp(`\\b(?:const|let|var)\\s+${escapedBinding}\\s*=\\s*function\\s*\\(([^)]*)\\)`)
6269
+ )?.[1];
6270
+ }
6271
+ function stripCommentsAndLiteralContents(source) {
6272
+ return sanitizeSource(source, true);
6273
+ }
6274
+ function stripComments(source) {
6275
+ return sanitizeSource(source, false);
6276
+ }
6277
+ function sanitizeSource(source, maskLiterals) {
6278
+ const output = [...source];
6279
+ const mask = /* @__PURE__ */ __name((index) => {
6280
+ if (output[index] !== "\n" && output[index] !== "\r") output[index] = " ";
6281
+ }, "mask");
6282
+ const scanQuoted = /* @__PURE__ */ __name((start, quote) => {
6283
+ let index = start;
6284
+ if (maskLiterals) mask(index);
6285
+ index += 1;
6286
+ while (index < source.length) {
6287
+ if (source[index] === "\\") {
6288
+ if (maskLiterals) mask(index);
6289
+ if (index + 1 < source.length && maskLiterals) mask(index + 1);
6290
+ index += 2;
6291
+ continue;
6292
+ }
6293
+ const character = source[index];
6294
+ if (maskLiterals) mask(index);
6295
+ index += 1;
6296
+ if (character === quote) break;
6297
+ }
6298
+ return index;
6299
+ }, "scanQuoted");
6300
+ const scanCode = /* @__PURE__ */ __name((start, stopAtTemplateBrace) => {
6301
+ let index = start;
6302
+ let braceDepth = 0;
6303
+ while (index < source.length) {
6304
+ const character = source[index];
6305
+ const next = source[index + 1];
6306
+ if (character === "/" && next === "/") {
6307
+ while (index < source.length && source[index] !== "\n" && source[index] !== "\r") {
6308
+ mask(index++);
6309
+ }
6310
+ continue;
6311
+ }
6312
+ if (character === "/" && next === "*") {
6313
+ mask(index++);
6314
+ mask(index++);
6315
+ while (index < source.length) {
6316
+ const closesComment = source[index] === "*" && source[index + 1] === "/";
6317
+ mask(index++);
6318
+ if (closesComment) {
6319
+ mask(index++);
6320
+ break;
6321
+ }
6322
+ }
6323
+ continue;
6324
+ }
6325
+ if (character === "'" || character === '"') {
6326
+ index = scanQuoted(index, character);
6327
+ continue;
6328
+ }
6329
+ if (character === "`") {
6330
+ if (maskLiterals) mask(index);
6331
+ index += 1;
6332
+ while (index < source.length) {
6333
+ if (source[index] === "\\") {
6334
+ if (maskLiterals) mask(index);
6335
+ if (index + 1 < source.length && maskLiterals) mask(index + 1);
6336
+ index += 2;
6337
+ } else if (source[index] === "`") {
6338
+ if (maskLiterals) mask(index);
6339
+ index += 1;
6340
+ break;
6341
+ } else if (source[index] === "$" && source[index + 1] === "{") {
6342
+ if (maskLiterals) {
6343
+ mask(index);
6344
+ mask(index + 1);
6345
+ }
6346
+ index = scanCode(index + 2, true);
6347
+ } else {
6348
+ if (maskLiterals) mask(index);
6349
+ index += 1;
6350
+ }
6351
+ }
6352
+ continue;
6353
+ }
6354
+ if (character === "{") {
6355
+ braceDepth += 1;
6356
+ } else if (character === "}") {
6357
+ if (stopAtTemplateBrace && braceDepth === 0) {
6358
+ if (maskLiterals) mask(index);
6359
+ return index + 1;
6360
+ }
6361
+ braceDepth = Math.max(0, braceDepth - 1);
6362
+ }
6363
+ index += 1;
6364
+ }
6365
+ return index;
6366
+ }, "scanCode");
6367
+ scanCode(0, false);
6368
+ return output.join("");
6369
+ }
6370
+ function escapeRegExp2(value) {
6371
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
6372
+ }
6373
+ function hasExplicitRenderingConfig(mod, source) {
6374
+ 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";
6375
+ }
6143
6376
  function parseRouteRenderingDirective(source) {
6144
6377
  if (!source) {
6145
6378
  return void 0;
@@ -6264,9 +6497,10 @@ function skipHorizontalWhitespace(source, start) {
6264
6497
  }
6265
6498
  return index;
6266
6499
  }
6267
- async function collectSSGPages(routes, loadModule2) {
6500
+ async function collectSSGPages(routes, loadModule2, options = {}) {
6268
6501
  const ssgPages = [];
6269
6502
  const ssrRoutes = [];
6503
+ const staticCandidateRoutes = [];
6270
6504
  for (const route of routes) {
6271
6505
  try {
6272
6506
  const mod = await loadModule2(route.filePath);
@@ -6274,7 +6508,8 @@ async function collectSSGPages(routes, loadModule2) {
6274
6508
  ssrRoutes.push(route.path);
6275
6509
  continue;
6276
6510
  }
6277
- const rendering = await resolveRouteRenderingConfigFromFile(mod, route.filePath);
6511
+ const source = await (0, import_promises3.readFile)(route.filePath, "utf8").catch(() => void 0);
6512
+ const rendering = resolveRouteRenderingConfig(mod, source);
6278
6513
  if (rendering.ssg) {
6279
6514
  if (route.isDynamic) {
6280
6515
  const getStaticPaths = mod.getStaticPaths || mod.generateStaticParams;
@@ -6301,12 +6536,26 @@ async function collectSSGPages(routes, loadModule2) {
6301
6536
  }
6302
6537
  } else {
6303
6538
  ssrRoutes.push(route.path);
6539
+ if (options.suggestStaticRendering !== false && analyzeStaticRouteCandidate(mod, source, { isDynamic: route.isDynamic }).candidate) {
6540
+ staticCandidateRoutes.push(route.path);
6541
+ }
6304
6542
  }
6305
6543
  } catch (error) {
6306
6544
  console.error(`Error processing route ${route.path}:`, error);
6307
6545
  ssrRoutes.push(route.path);
6308
6546
  }
6309
6547
  }
6548
+ if (staticCandidateRoutes.length > 0) {
6549
+ const visibleRoutes = staticCandidateRoutes.slice(0, 10);
6550
+ const remainingCount = staticCandidateRoutes.length - visibleRoutes.length;
6551
+ const remainingSuffix = remainingCount > 0 ? `
6552
+ \u2026and ${remainingCount} more` : "";
6553
+ console.warn(
6554
+ `[Farm.js] ${staticCandidateRoutes.length} route${staticCandidateRoutes.length === 1 ? " appears" : "s appear"} eligible for static rendering:
6555
+ ${visibleRoutes.join("\n ")}${remainingSuffix}
6556
+ Add \`export const dynamic = "force-static";\` after verifying that imported code does not read cookies, headers, authentication, or other request data.`
6557
+ );
6558
+ }
6310
6559
  return { ssg: ssgPages, ssr: ssrRoutes };
6311
6560
  }
6312
6561
  function materializeSSGRoutePath(routePattern, params) {
@@ -6408,7 +6657,7 @@ function matchSSGPage(urlPath, ssgPages) {
6408
6657
  const normalizedPath = urlPath === "/" ? "/" : urlPath.replace(/\/$/, "");
6409
6658
  return ssgPages.find((page2) => page2.urlPath === normalizedPath);
6410
6659
  }
6411
- var import_promises3, OPTIONAL_CATCH_ALL_SEGMENT, REQUIRED_CATCH_ALL_SEGMENT, REQUIRED_DYNAMIC_SEGMENT;
6660
+ var import_promises3, OPTIONAL_CATCH_ALL_SEGMENT, REQUIRED_CATCH_ALL_SEGMENT, REQUIRED_DYNAMIC_SEGMENT, REQUEST_API_LABELS, ROUTE_PROP_LABELS;
6412
6661
  var init_ssg = __esm({
6413
6662
  "src/ssg.ts"() {
6414
6663
  "use strict";
@@ -6416,8 +6665,35 @@ var init_ssg = __esm({
6416
6665
  OPTIONAL_CATCH_ALL_SEGMENT = /^\[\[\.\.\.([^\]]+)\]\]$/;
6417
6666
  REQUIRED_CATCH_ALL_SEGMENT = /^\[\.\.\.([^\]]+)\]$/;
6418
6667
  REQUIRED_DYNAMIC_SEGMENT = /^\[([^\]]+)\]$/;
6668
+ REQUEST_API_LABELS = /* @__PURE__ */ new Map([
6669
+ ["cookies", "request cookies"],
6670
+ ["headers", "request headers"],
6671
+ ["getCurrentRequest", "the current Request"],
6672
+ ["useSearchParams", "URL search parameters"],
6673
+ ["auth", "request authentication"],
6674
+ ["getServerSession", "request authentication"],
6675
+ ["getSession", "request authentication"],
6676
+ ["currentUser", "request authentication"]
6677
+ ]);
6678
+ ROUTE_PROP_LABELS = /* @__PURE__ */ new Map([
6679
+ ["searchParams", "URL search parameters"],
6680
+ ["middleware", "middleware request data"],
6681
+ ["middlewareData", "middleware request data"],
6682
+ ["context", "request context"]
6683
+ ]);
6419
6684
  __name(resolveRouteRenderingConfig, "resolveRouteRenderingConfig");
6420
6685
  __name(resolveRouteRenderingConfigFromFile, "resolveRouteRenderingConfigFromFile");
6686
+ __name(analyzeStaticRouteCandidate, "analyzeStaticRouteCandidate");
6687
+ __name(findRequestBoundSourceBlockers, "findRequestBoundSourceBlockers");
6688
+ __name(readRequestApiImportBindings, "readRequestApiImportBindings");
6689
+ __name(readNamespaceImportBindings, "readNamespaceImportBindings");
6690
+ __name(readStaticImportClauses, "readStaticImportClauses");
6691
+ __name(readDefaultRouteParameters, "readDefaultRouteParameters");
6692
+ __name(stripCommentsAndLiteralContents, "stripCommentsAndLiteralContents");
6693
+ __name(stripComments, "stripComments");
6694
+ __name(sanitizeSource, "sanitizeSource");
6695
+ __name(escapeRegExp2, "escapeRegExp");
6696
+ __name(hasExplicitRenderingConfig, "hasExplicitRenderingConfig");
6421
6697
  __name(parseRouteRenderingDirective, "parseRouteRenderingDirective");
6422
6698
  __name(parseKnownRenderingDirective, "parseKnownRenderingDirective");
6423
6699
  __name(normalizeDynamicMode, "normalizeDynamicMode");
@@ -7098,7 +7374,7 @@ function getProgrammaticRouteSearchClientOptions(search) {
7098
7374
  if (search.temporary?.length) options.temporary = [...search.temporary];
7099
7375
  return Object.keys(options).length > 0 ? options : void 0;
7100
7376
  }
7101
- function resolveProgrammaticRouteSearch(searchConfig, rawSearch, path29, routePath) {
7377
+ function resolveProgrammaticRouteSearch(searchConfig, rawSearch, path31, routePath) {
7102
7378
  const schema = getProgrammaticRouteSearchSchema(searchConfig);
7103
7379
  const search = parseProgrammaticSchema(schema, rawSearch, "search", routePath);
7104
7380
  const options = getProgrammaticRouteSearchOptions(searchConfig);
@@ -7107,7 +7383,7 @@ function resolveProgrammaticRouteSearch(searchConfig, rawSearch, path29, routePa
7107
7383
  schema,
7108
7384
  rawSearch,
7109
7385
  parsedSearch: search,
7110
- path: path29
7386
+ path: path31
7111
7387
  });
7112
7388
  return { search, canonicalPath };
7113
7389
  }
@@ -7274,7 +7550,7 @@ function createRouteModuleFromProgrammaticPage(route) {
7274
7550
  mod.__farmRouteData = route.data;
7275
7551
  mod.__farmRouteParsesProps = true;
7276
7552
  mod.__farmResolveRouteProps = (props) => resolveProgrammaticRouteProps(route, props);
7277
- mod.__farmResolveRouteCanonicalPath = (rawSearch, path29) => resolveProgrammaticRouteSearch(route.search, rawSearch, path29, route.path).canonicalPath;
7553
+ mod.__farmResolveRouteCanonicalPath = (rawSearch, path31) => resolveProgrammaticRouteSearch(route.search, rawSearch, path31, route.path).canonicalPath;
7278
7554
  }
7279
7555
  if (route.pending || route.error || route.notFound) {
7280
7556
  mod.__farmRouteComponents = {
@@ -7672,25 +7948,25 @@ var init_routes = __esm({
7672
7948
  ];
7673
7949
  __name(defineRoutes, "defineRoutes");
7674
7950
  routesBuilder = {
7675
- page(path29, options) {
7951
+ page(path31, options) {
7676
7952
  return normalizeProgrammaticRoute({
7677
7953
  kind: "page",
7678
- path: path29,
7954
+ path: path31,
7679
7955
  ...options
7680
7956
  });
7681
7957
  },
7682
- layout(path29, options) {
7958
+ layout(path31, options) {
7683
7959
  return normalizeProgrammaticRoute({
7684
7960
  kind: "layout",
7685
- path: path29,
7961
+ path: path31,
7686
7962
  ...options
7687
7963
  });
7688
7964
  },
7689
- api(path29, options) {
7965
+ api(path31, options) {
7690
7966
  const { render, runtime, regions, maxDuration, ...methods } = options;
7691
7967
  return normalizeProgrammaticRoute({
7692
7968
  kind: "api",
7693
- path: path29,
7969
+ path: path31,
7694
7970
  render,
7695
7971
  runtime,
7696
7972
  regions,
@@ -7833,6 +8109,19 @@ var init_routes_server = __esm({
7833
8109
  }
7834
8110
  });
7835
8111
 
8112
+ // src/island.ts
8113
+ function isFarmIslandStrategy(value) {
8114
+ return FARM_ISLAND_STRATEGIES.includes(value);
8115
+ }
8116
+ var FARM_ISLAND_STRATEGIES;
8117
+ var init_island = __esm({
8118
+ "src/island.ts"() {
8119
+ "use strict";
8120
+ FARM_ISLAND_STRATEGIES = ["load", "interaction", "visible", "idle"];
8121
+ __name(isFarmIslandStrategy, "isFarmIslandStrategy");
8122
+ }
8123
+ });
8124
+
7836
8125
  // src/utils/client-component.ts
7837
8126
  function readIfExists(filePath) {
7838
8127
  try {
@@ -7890,53 +8179,180 @@ function hasHydrateExport(content) {
7890
8179
  }
7891
8180
  return /\bexport\s+const\s+hydrate\s*=\s*true\b/.test(content) || /\bexport\s*\{\s*hydrate\s*\}\b/.test(content);
7892
8181
  }
8182
+ function tokenizeModuleSource(content) {
8183
+ const tokens = [];
8184
+ let index = 0;
8185
+ let line = 1;
8186
+ while (index < content.length) {
8187
+ const character = content[index];
8188
+ if (/\s/.test(character)) {
8189
+ if (character === "\n") line++;
8190
+ index++;
8191
+ continue;
8192
+ }
8193
+ if (character === "/" && content[index + 1] === "/") {
8194
+ index += 2;
8195
+ while (index < content.length && content[index] !== "\n") index++;
8196
+ continue;
8197
+ }
8198
+ if (character === "/" && content[index + 1] === "*") {
8199
+ index += 2;
8200
+ while (index < content.length) {
8201
+ if (content[index] === "\n") line++;
8202
+ if (content[index] === "*" && content[index + 1] === "/") {
8203
+ index += 2;
8204
+ break;
8205
+ }
8206
+ index++;
8207
+ }
8208
+ continue;
8209
+ }
8210
+ if (character === '"' || character === "'") {
8211
+ const quote = character;
8212
+ const tokenLine = line;
8213
+ let value = "";
8214
+ index++;
8215
+ while (index < content.length) {
8216
+ const next = content[index];
8217
+ if (next === "\\") {
8218
+ value += next;
8219
+ if (index + 1 < content.length) value += content[index + 1];
8220
+ index += 2;
8221
+ continue;
8222
+ }
8223
+ if (next === quote) {
8224
+ index++;
8225
+ break;
8226
+ }
8227
+ if (next === "\n") line++;
8228
+ value += next;
8229
+ index++;
8230
+ }
8231
+ tokens.push({ kind: "string", value, line: tokenLine });
8232
+ continue;
8233
+ }
8234
+ if (character === "`") {
8235
+ index++;
8236
+ while (index < content.length) {
8237
+ const next = content[index];
8238
+ if (next === "\\") {
8239
+ index += 2;
8240
+ continue;
8241
+ }
8242
+ if (next === "`") {
8243
+ index++;
8244
+ break;
8245
+ }
8246
+ if (next === "\n") line++;
8247
+ index++;
8248
+ }
8249
+ continue;
8250
+ }
8251
+ if (/[A-Za-z_$]/.test(character)) {
8252
+ const start = index++;
8253
+ while (index < content.length && /[\w$]/.test(content[index])) index++;
8254
+ tokens.push({ kind: "identifier", value: content.slice(start, index), line });
8255
+ continue;
8256
+ }
8257
+ tokens.push({ kind: "punctuation", value: character, line });
8258
+ index++;
8259
+ }
8260
+ return tokens;
8261
+ }
8262
+ function isIslandExportStart(tokens, index) {
8263
+ const token = tokens[index];
8264
+ const previous = tokens[index - 1];
8265
+ const startsStatement = !previous || previous.value === ";" || previous.value === "{" || previous.value === "}" || token.line > previous.line;
8266
+ return startsStatement && token.value === "export" && tokens[index + 1]?.value === "const" && tokens[index + 2]?.value === "island";
8267
+ }
8268
+ function getIslandStrategyExport(content) {
8269
+ if (!content) return null;
8270
+ const tokens = tokenizeModuleSource(content);
8271
+ for (let index = 0; index < tokens.length - 2; index++) {
8272
+ if (!isIslandExportStart(tokens, index)) continue;
8273
+ let valueIndex = index + 3;
8274
+ if (tokens[valueIndex]?.value === ":") {
8275
+ while (valueIndex < tokens.length && tokens[valueIndex].value !== "=") valueIndex++;
8276
+ }
8277
+ if (tokens[valueIndex]?.value !== "=") break;
8278
+ valueIndex++;
8279
+ const literal = tokens[valueIndex];
8280
+ if (!literal || literal.kind !== "string" || !isFarmIslandStrategy(literal.value)) break;
8281
+ let trailingIndex = valueIndex + 1;
8282
+ if (tokens[trailingIndex]?.value === "as" && tokens[trailingIndex + 1]?.value === "const") {
8283
+ trailingIndex += 2;
8284
+ }
8285
+ const trailing = tokens[trailingIndex];
8286
+ if (trailing && trailing.value !== ";" && trailing.line === literal.line) break;
8287
+ return literal.value;
8288
+ }
8289
+ if (tokens.some((_token, index) => isIslandExportStart(tokens, index))) {
8290
+ throw new Error(
8291
+ 'Farm island configuration must be a static "load", "interaction", "visible", or "idle" string literal.'
8292
+ );
8293
+ }
8294
+ return null;
8295
+ }
7893
8296
  function stripUseClientDirective(content) {
7894
8297
  return content.replace(/^\s*(["'])use client\1\s*;?\s*/, "");
7895
8298
  }
7896
- function parseClientModuleMetadata(content) {
8299
+ function parseClientModuleMetadata(content, inspectIslandExport) {
7897
8300
  if (!content) {
7898
8301
  return {
7899
8302
  isClientComponent: false,
7900
- hasHydrateExport: false
8303
+ hasHydrateExport: false,
8304
+ islandStrategy: null
7901
8305
  };
7902
8306
  }
8307
+ const isClientComponent = hasUseClientDirective(content);
8308
+ const hasHydrate = hasHydrateExport(content);
7903
8309
  return {
7904
- isClientComponent: hasUseClientDirective(content),
7905
- hasHydrateExport: hasHydrateExport(content)
8310
+ isClientComponent,
8311
+ hasHydrateExport: hasHydrate,
8312
+ islandStrategy: inspectIslandExport || isClientComponent || hasHydrate ? getIslandStrategyExport(content) : null
7906
8313
  };
7907
8314
  }
7908
8315
  function getClientModuleMetadata(modulePath, root) {
7909
8316
  const resolvedPath = resolveModuleSourcePath(modulePath, root);
7910
- return inspectClientModuleMetadata(resolvedPath, root, /* @__PURE__ */ new Set());
8317
+ return inspectClientModuleMetadata(resolvedPath, root, /* @__PURE__ */ new Set(), true);
7911
8318
  }
7912
- function inspectClientModuleMetadata(resolvedPath, root, visited) {
8319
+ function inspectClientModuleMetadata(resolvedPath, root, visited, inspectIslandExport) {
7913
8320
  if (!resolvedPath || visited.has(resolvedPath)) {
7914
8321
  return {
7915
8322
  isClientComponent: false,
7916
- shouldHydrate: false
8323
+ shouldHydrate: false,
8324
+ islandStrategy: null
7917
8325
  };
7918
8326
  }
7919
8327
  visited.add(resolvedPath);
7920
8328
  const content = readIfExists(resolvedPath);
7921
- const parsed = parseClientModuleMetadata(content);
8329
+ const parsed = parseClientModuleMetadata(content, inspectIslandExport);
7922
8330
  if (parsed.isClientComponent) {
7923
8331
  return {
7924
8332
  isClientComponent: true,
7925
- shouldHydrate: true
8333
+ shouldHydrate: true,
8334
+ islandStrategy: parsed.islandStrategy ?? "load"
7926
8335
  };
7927
8336
  }
7928
8337
  let importsClientBoundary = false;
8338
+ let importedIslandStrategy = null;
7929
8339
  for (const specifier of getRelativeImportSpecifiers(content)) {
7930
8340
  const importedPath = resolveImportedModuleSourcePath(resolvedPath, specifier, root);
7931
- const importedMetadata = inspectClientModuleMetadata(importedPath, root, visited);
8341
+ const importedMetadata = inspectClientModuleMetadata(importedPath, root, visited, false);
7932
8342
  if (importedMetadata.isClientComponent || importedMetadata.shouldHydrate) {
7933
8343
  importsClientBoundary = true;
7934
- break;
8344
+ if (importedIslandStrategy === null) {
8345
+ importedIslandStrategy = importedMetadata.islandStrategy;
8346
+ } else if (importedIslandStrategy !== importedMetadata.islandStrategy) {
8347
+ importedIslandStrategy = "load";
8348
+ }
7935
8349
  }
7936
8350
  }
8351
+ const shouldHydrate = parsed.hasHydrateExport || importsClientBoundary;
7937
8352
  return {
7938
8353
  isClientComponent: false,
7939
- shouldHydrate: parsed.hasHydrateExport || importsClientBoundary
8354
+ shouldHydrate,
8355
+ islandStrategy: shouldHydrate ? parsed.islandStrategy ?? importedIslandStrategy ?? "load" : null
7940
8356
  };
7941
8357
  }
7942
8358
  function getRelativeImportSpecifiers(content) {
@@ -7976,6 +8392,7 @@ var init_client_component = __esm({
7976
8392
  import_fs2 = __toESM(require("fs"));
7977
8393
  import_path5 = __toESM(require("path"));
7978
8394
  import_url2 = require("url");
8395
+ init_island();
7979
8396
  __name(readIfExists, "readIfExists");
7980
8397
  __name(resolveModuleSourcePath, "resolveModuleSourcePath");
7981
8398
  RESOLVABLE_SOURCE_EXTENSIONS = [
@@ -7990,6 +8407,9 @@ var init_client_component = __esm({
7990
8407
  ];
7991
8408
  __name(hasUseClientDirective, "hasUseClientDirective");
7992
8409
  __name(hasHydrateExport, "hasHydrateExport");
8410
+ __name(tokenizeModuleSource, "tokenizeModuleSource");
8411
+ __name(isIslandExportStart, "isIslandExportStart");
8412
+ __name(getIslandStrategyExport, "getIslandStrategyExport");
7993
8413
  __name(stripUseClientDirective, "stripUseClientDirective");
7994
8414
  __name(parseClientModuleMetadata, "parseClientModuleMetadata");
7995
8415
  __name(getClientModuleMetadata, "getClientModuleMetadata");
@@ -8231,6 +8651,9 @@ var init_route_slots = __esm({
8231
8651
  });
8232
8652
 
8233
8653
  // src/routing/route-manager.ts
8654
+ function shouldSuggestStaticRenderingForI18n(i18n) {
8655
+ return !i18n || i18n.routing === "prefix-always" || i18n.routing === "prefix-except-default" && i18n.detection.every((signal) => signal === "url");
8656
+ }
8234
8657
  function routeSpecificity(entry) {
8235
8658
  return entry.route.segments.reduce((score, segment) => {
8236
8659
  if (!segment.isDynamic) return score + 100;
@@ -8302,6 +8725,7 @@ var init_route_manager = __esm({
8302
8725
  init_route_runtime();
8303
8726
  init_routing();
8304
8727
  init_route_slots();
8728
+ __name(shouldSuggestStaticRenderingForI18n, "shouldSuggestStaticRenderingForI18n");
8305
8729
  __name(routeSpecificity, "routeSpecificity");
8306
8730
  _RouteManager = class _RouteManager {
8307
8731
  constructor(config, viteServer) {
@@ -8538,6 +8962,7 @@ var init_route_manager = __esm({
8538
8962
  modulePath: toUrlPath(entry.modulePath),
8539
8963
  shouldHydrate: metadata.shouldHydrate,
8540
8964
  isClientComponent: metadata.isClientComponent,
8965
+ islandStrategy: metadata.islandStrategy,
8541
8966
  search: getProgrammaticRouteSearchClientOptions(programmaticPage?.search),
8542
8967
  segments: entry.route.segments.map((seg) => ({
8543
8968
  segment: seg.segment,
@@ -9000,8 +9425,10 @@ var init_route_manager = __esm({
9000
9425
  isDynamic: entry.route.segments.some((seg) => seg.isDynamic),
9001
9426
  pattern: entry.pattern
9002
9427
  }));
9003
- const result = await collectSSGPages(routes, (filePath) => this.loadRouteModule(filePath));
9004
9428
  const i18n = this.getI18nConfig();
9429
+ const result = await collectSSGPages(routes, (filePath) => this.loadRouteModule(filePath), {
9430
+ suggestStaticRendering: shouldSuggestStaticRenderingForI18n(i18n)
9431
+ });
9005
9432
  if (!i18n) {
9006
9433
  return result;
9007
9434
  }
@@ -10423,7 +10850,8 @@ ${imports}
10423
10850
  function renderFarmFontDevHead(root) {
10424
10851
  const registry = registries.get(normalizeRoot(root));
10425
10852
  if (!registry || registry.production || registry.size === 0) return "";
10426
- return registry.renderPreloadTags(false);
10853
+ return `${registry.renderPreloadTags(false)}
10854
+ <link rel="stylesheet" href="/@farm/fonts.css">`;
10427
10855
  }
10428
10856
  function findFontBindings(ast) {
10429
10857
  const named = /* @__PURE__ */ new Map();
@@ -10666,7 +11094,8 @@ function serializeFontResult(font) {
10666
11094
  fontFamily: stack,
10667
11095
  ...font.style ? { fontStyle: font.style } : {},
10668
11096
  ...font.weight !== void 0 && !String(font.weight).includes(" ") ? { fontWeight: font.weight } : {}
10669
- }
11097
+ },
11098
+ preloads: font.preloadResources
10670
11099
  });
10671
11100
  }
10672
11101
  function renderFontDefinitionCss(font, production, basePath) {
@@ -11179,6 +11608,10 @@ ${FONT_SECTION_END}
11179
11608
  fallback,
11180
11609
  display,
11181
11610
  preload: raw.preload !== false,
11611
+ preloadResources: raw.preload === false ? [] : normalizedSources.map(({ resource }) => ({
11612
+ href: resourceUrl(resource, production, this.basePath),
11613
+ type: fontContentType(resource.extension)
11614
+ })),
11182
11615
  weight: defaultWeight,
11183
11616
  style: raw.style === void 0 ? void 0 : defaultStyle,
11184
11617
  sources: normalizedSources
@@ -11275,7 +11708,7 @@ ${FONT_SECTION_END}
11275
11708
  const normalizedExtension = extension.toLowerCase();
11276
11709
  const hash = (0, import_node_crypto2.createHash)("sha256").update(bytes).digest("hex").slice(0, 12);
11277
11710
  const baseName = sanitizeFileName(import_node_path5.default.basename(fileName, extension)) || "font";
11278
- const outputFileName = `assets/fonts/${baseName}-${hash}${normalizedExtension}`;
11711
+ const outputFileName = `assets/fonts/${baseName}-h${hash}${normalizedExtension}`;
11279
11712
  const key = `${hash}${normalizedExtension}`;
11280
11713
  const existing = this.resources.get(key);
11281
11714
  if (existing) return existing;
@@ -11347,6 +11780,172 @@ ${FONT_SECTION_END}
11347
11780
  }
11348
11781
  });
11349
11782
 
11783
+ // src/metadata-image.ts
11784
+ function isResponse(value) {
11785
+ return typeof Response !== "undefined" && (value instanceof Response || Boolean(
11786
+ value && typeof value === "object" && typeof value.arrayBuffer === "function" && value.headers
11787
+ ));
11788
+ }
11789
+ function isThenable(value) {
11790
+ return Boolean(
11791
+ value && (typeof value === "object" || typeof value === "function") && typeof value.then === "function"
11792
+ );
11793
+ }
11794
+ async function prepareMetadataImageNode(node) {
11795
+ if (isThenable(node)) {
11796
+ return prepareMetadataImageNode(await node);
11797
+ }
11798
+ if (Array.isArray(node)) {
11799
+ return Promise.all(node.map((child) => prepareMetadataImageNode(child)));
11800
+ }
11801
+ if (!import_react4.default.isValidElement(node)) {
11802
+ return node;
11803
+ }
11804
+ const element = node;
11805
+ const type = element.type;
11806
+ const props = element.props || {};
11807
+ if (typeof type === "function") {
11808
+ if (type.prototype?.isReactComponent) {
11809
+ throw new Error(
11810
+ "Metadata image components must be stateless function components; React class components are not supported."
11811
+ );
11812
+ }
11813
+ return prepareMetadataImageNode(await type(props));
11814
+ }
11815
+ if (type && typeof type === "object") {
11816
+ if (type.$$typeof === REACT_MEMO_TYPE) {
11817
+ return prepareMetadataImageNode(import_react4.default.createElement(type.type, props));
11818
+ }
11819
+ if (type.$$typeof === REACT_FORWARD_REF_TYPE) {
11820
+ return prepareMetadataImageNode(await type.render(props, null));
11821
+ }
11822
+ if (type.$$typeof === REACT_LAZY_TYPE) {
11823
+ return prepareMetadataImageNode(import_react4.default.createElement(type._init(type._payload), props));
11824
+ }
11825
+ }
11826
+ const preparedChildren = await prepareMetadataImageNode(props.children);
11827
+ const preparedProps = { ...props };
11828
+ if (typeof preparedProps.className === "string") {
11829
+ preparedProps.tw = [preparedProps.tw, preparedProps.className].filter(Boolean).join(" ");
11830
+ delete preparedProps.className;
11831
+ }
11832
+ delete preparedProps.children;
11833
+ return import_react4.default.createElement(type, {
11834
+ ...preparedProps,
11835
+ key: element.key,
11836
+ children: preparedChildren
11837
+ });
11838
+ }
11839
+ async function normalizeFonts(fonts) {
11840
+ if (!fonts?.length) return void 0;
11841
+ return Promise.all(
11842
+ fonts.map(async (font) => {
11843
+ const data = await font.data;
11844
+ const arrayBuffer = ArrayBuffer.isView(data) ? toArrayBuffer(data) : data;
11845
+ return { ...font, data: arrayBuffer };
11846
+ })
11847
+ );
11848
+ }
11849
+ function resolveCacheControl(revalidate) {
11850
+ if (revalidate === false) {
11851
+ return "public, max-age=31536000, immutable";
11852
+ }
11853
+ if (typeof revalidate === "number" && Number.isFinite(revalidate) && revalidate > 0) {
11854
+ return `public, s-maxage=${Math.floor(revalidate)}, stale-while-revalidate=300`;
11855
+ }
11856
+ return "public, max-age=0, must-revalidate";
11857
+ }
11858
+ function toArrayBuffer(value) {
11859
+ if (value instanceof ArrayBuffer) return value;
11860
+ const bytes = new Uint8Array(value.byteLength);
11861
+ bytes.set(new Uint8Array(value.buffer, value.byteOffset, value.byteLength));
11862
+ return bytes.buffer;
11863
+ }
11864
+ async function createEntityTag(body) {
11865
+ const digest = await globalThis.crypto.subtle.digest("SHA-256", body);
11866
+ const hash = Array.from(new Uint8Array(digest), (byte) => byte.toString(16).padStart(2, "0")).join("").slice(0, 32);
11867
+ return `"${hash}"`;
11868
+ }
11869
+ async function finalizeMetadataImageResponse(body, headers, options) {
11870
+ const responseHeaders = new Headers(headers);
11871
+ const etag = await createEntityTag(body);
11872
+ responseHeaders.set("ETag", etag);
11873
+ responseHeaders.set("Content-Length", String(body.byteLength));
11874
+ responseHeaders.set("X-Content-Type-Options", "nosniff");
11875
+ const matchesEntityTag = options.ifNoneMatch?.trim() === "*" || options.ifNoneMatch?.split(",").some((candidate) => candidate.trim().replace(/^W\//, "") === etag);
11876
+ if (matchesEntityTag) {
11877
+ return new Response(null, { status: 304, headers: responseHeaders });
11878
+ }
11879
+ return new Response(options.method?.toUpperCase() === "HEAD" ? null : body, {
11880
+ status: 200,
11881
+ headers: responseHeaders
11882
+ });
11883
+ }
11884
+ async function createFarmMetadataImageResponse(value, imageModule, options = {}) {
11885
+ const method = (options.method || "GET").toUpperCase();
11886
+ if (method !== "GET" && method !== "HEAD") {
11887
+ return new Response(null, { status: 405, headers: { Allow: "GET, HEAD" } });
11888
+ }
11889
+ if (isResponse(value)) {
11890
+ return method === "HEAD" ? new Response(null, { status: value.status, headers: value.headers }) : value;
11891
+ }
11892
+ const cacheControl = resolveCacheControl(imageModule.revalidate);
11893
+ const explicitContentType = imageModule.contentType?.split(";", 1)[0]?.trim().toLowerCase();
11894
+ if (import_react4.default.isValidElement(value) && explicitContentType !== "image/svg+xml") {
11895
+ const { ImageResponse } = await import("@vercel/og");
11896
+ const element = await prepareMetadataImageNode(value);
11897
+ const response = new ImageResponse(element, {
11898
+ width: imageModule.size?.width || 1200,
11899
+ height: imageModule.size?.height || 630,
11900
+ fonts: await normalizeFonts(imageModule.fonts),
11901
+ emoji: imageModule.emoji,
11902
+ debug: imageModule.debug,
11903
+ headers: { "cache-control": cacheControl }
11904
+ });
11905
+ const headers = new Headers(response.headers);
11906
+ headers.set("Cache-Control", cacheControl);
11907
+ return finalizeMetadataImageResponse(await response.arrayBuffer(), headers, options);
11908
+ }
11909
+ let body;
11910
+ if (typeof value === "string") {
11911
+ body = toArrayBuffer(new TextEncoder().encode(value));
11912
+ } else if (value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
11913
+ body = toArrayBuffer(value);
11914
+ } else if (import_react4.default.isValidElement(value)) {
11915
+ body = toArrayBuffer(new TextEncoder().encode((0, import_server.renderToStaticMarkup)(value)));
11916
+ } else {
11917
+ throw new Error("Metadata image must return a Response, string, bytes, or React element");
11918
+ }
11919
+ return finalizeMetadataImageResponse(
11920
+ body,
11921
+ {
11922
+ "Content-Type": imageModule.contentType || "image/svg+xml; charset=utf-8",
11923
+ "Cache-Control": cacheControl
11924
+ },
11925
+ options
11926
+ );
11927
+ }
11928
+ var import_react4, import_server, REACT_FORWARD_REF_TYPE, REACT_MEMO_TYPE, REACT_LAZY_TYPE;
11929
+ var init_metadata_image = __esm({
11930
+ "src/metadata-image.ts"() {
11931
+ "use strict";
11932
+ import_react4 = __toESM(require("react"));
11933
+ import_server = require("react-dom/server");
11934
+ REACT_FORWARD_REF_TYPE = /* @__PURE__ */ Symbol.for("react.forward_ref");
11935
+ REACT_MEMO_TYPE = /* @__PURE__ */ Symbol.for("react.memo");
11936
+ REACT_LAZY_TYPE = /* @__PURE__ */ Symbol.for("react.lazy");
11937
+ __name(isResponse, "isResponse");
11938
+ __name(isThenable, "isThenable");
11939
+ __name(prepareMetadataImageNode, "prepareMetadataImageNode");
11940
+ __name(normalizeFonts, "normalizeFonts");
11941
+ __name(resolveCacheControl, "resolveCacheControl");
11942
+ __name(toArrayBuffer, "toArrayBuffer");
11943
+ __name(createEntityTag, "createEntityTag");
11944
+ __name(finalizeMetadataImageResponse, "finalizeMetadataImageResponse");
11945
+ __name(createFarmMetadataImageResponse, "createFarmMetadataImageResponse");
11946
+ }
11947
+ });
11948
+
11350
11949
  // src/server/renderer.ts
11351
11950
  function hasRequestHeader(req, name) {
11352
11951
  const value = req.headers[name.toLowerCase()];
@@ -11410,7 +12009,7 @@ function createPPRRefreshScript() {
11410
12009
  return `<script>(function(){if(window.__FARM_PPR_REFRESHING__)return;window.__FARM_PPR_REFRESHING__=true;function replaceRoot(html){var doc=new DOMParser().parseFromString(html,"text/html");var next=doc.getElementById("root");var current=document.getElementById("root");if(!next||!current)return;current.innerHTML=next.innerHTML;}fetch(window.location.href,{credentials:"same-origin",headers:{"x-farm-ppr-refresh":"1"}}).then(function(response){return response.ok?response.text():null;}).then(function(html){if(html)replaceRoot(html);}).catch(function(){});})();</script>`;
11411
12010
  }
11412
12011
  function createPreHydrationClickQueueScript() {
11413
- 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>`;
12012
+ 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>`;
11414
12013
  }
11415
12014
  function searchParamsToObject(searchParams) {
11416
12015
  const output = {};
@@ -11502,12 +12101,12 @@ function createRouteStateProps(input) {
11502
12101
  context: input.pluginExposedContext.size > 0 ? { data: input.pluginExposedContext } : void 0
11503
12102
  };
11504
12103
  }
11505
- var import_react4, import_server, fs2, path11, import_stream, cachedClerkProvider, importRuntimeModule, _FarmRouteErrorBoundary, FarmRouteErrorBoundary, _ServerRenderer, ServerRenderer;
12104
+ var import_react5, import_server2, fs2, path11, import_stream, cachedClerkProvider, importRuntimeModule, _FarmRouteErrorBoundary, FarmRouteErrorBoundary, _ServerRenderer, ServerRenderer;
11506
12105
  var init_renderer = __esm({
11507
12106
  "src/server/renderer.ts"() {
11508
12107
  "use strict";
11509
- import_react4 = __toESM(require("react"));
11510
- import_server = require("react-dom/server");
12108
+ import_react5 = __toESM(require("react"));
12109
+ import_server2 = require("react-dom/server");
11511
12110
  fs2 = __toESM(require("fs"));
11512
12111
  path11 = __toESM(require("path"));
11513
12112
  init_utils();
@@ -11530,6 +12129,7 @@ var init_renderer = __esm({
11530
12129
  init_routing();
11531
12130
  init_response();
11532
12131
  init_font_vite();
12132
+ init_metadata_image();
11533
12133
  cachedClerkProvider = null;
11534
12134
  importRuntimeModule = new Function("specifier", "return import(specifier);");
11535
12135
  __name(hasRequestHeader, "hasRequestHeader");
@@ -11546,7 +12146,7 @@ var init_renderer = __esm({
11546
12146
  __name(createDeferredHydrationScript, "createDeferredHydrationScript");
11547
12147
  __name(toMiddlewareMap2, "toMiddlewareMap");
11548
12148
  __name(isWebResponse, "isWebResponse");
11549
- _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react4.default.Component {
12149
+ _FarmRouteErrorBoundary = class _FarmRouteErrorBoundary extends import_react5.default.Component {
11550
12150
  constructor(props) {
11551
12151
  super(props);
11552
12152
  this.state = { hasError: false, error: null };
@@ -11557,7 +12157,7 @@ var init_renderer = __esm({
11557
12157
  render() {
11558
12158
  if (this.state.hasError) {
11559
12159
  const Fallback = this.props.Fallback;
11560
- return import_react4.default.createElement(Fallback, {
12160
+ return import_react5.default.createElement(Fallback, {
11561
12161
  ...this.props.fallbackProps,
11562
12162
  error: this.state.error,
11563
12163
  reset: /* @__PURE__ */ __name(() => this.setState({ hasError: false, error: null }), "reset")
@@ -11726,14 +12326,14 @@ var init_renderer = __esm({
11726
12326
  searchParams: Promise.resolve({}),
11727
12327
  path: page2.urlPath
11728
12328
  };
11729
- let pageElement = import_react4.default.createElement(
12329
+ let pageElement = import_react5.default.createElement(
11730
12330
  PageComponent,
11731
12331
  pageProps
11732
12332
  );
11733
12333
  for (let i = layoutModules.length - 1; i >= 0; i--) {
11734
12334
  const layoutModule = layoutModules[i];
11735
12335
  const LayoutComponent = layoutModule.default;
11736
- pageElement = import_react4.default.createElement(
12336
+ pageElement = import_react5.default.createElement(
11737
12337
  LayoutComponent,
11738
12338
  {
11739
12339
  children: pageElement,
@@ -12059,6 +12659,7 @@ var init_renderer = __esm({
12059
12659
  req.__FARM_ROUTE__ = pathname;
12060
12660
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
12061
12661
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
12662
+ req.__FARM_ISLAND_STRATEGY__ = moduleMetadata.islandStrategy;
12062
12663
  req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
12063
12664
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
12064
12665
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
@@ -12111,12 +12712,12 @@ var init_renderer = __esm({
12111
12712
  await _runWithMiddlewareData(middlewareDataForContext, async () => {
12112
12713
  await _runWithMiddlewareContext(middlewareContext, async () => {
12113
12714
  await _runWithCurrentRequest(currentRequest, async () => {
12114
- let pageElement = import_react4.default.createElement(
12715
+ let pageElement = import_react5.default.createElement(
12115
12716
  PageComponent,
12116
12717
  pageProps
12117
12718
  );
12118
12719
  if (LoadingFallbackComponent) {
12119
- const loadingFallback = import_react4.default.createElement(LoadingFallbackComponent, {
12720
+ const loadingFallback = import_react5.default.createElement(LoadingFallbackComponent, {
12120
12721
  ...createRouteStateProps({
12121
12722
  params,
12122
12723
  searchParamsObject,
@@ -12125,16 +12726,21 @@ var init_renderer = __esm({
12125
12726
  pluginExposedContext
12126
12727
  })
12127
12728
  });
12128
- pageElement = import_react4.default.createElement(
12129
- import_react4.default.Suspense,
12729
+ pageElement = import_react5.default.createElement(
12730
+ import_react5.default.Suspense,
12130
12731
  { fallback: loadingFallback },
12131
12732
  pageElement
12132
12733
  );
12133
12734
  }
12134
12735
  if (isClientComponent || shouldHydrate) {
12135
- pageElement = import_react4.default.createElement(
12736
+ pageElement = import_react5.default.createElement(
12136
12737
  "div",
12137
- { id: "__farm_page__", "data-farm-client": "true" },
12738
+ {
12739
+ id: "__farm_page__",
12740
+ "data-farm-client": "true",
12741
+ "data-farm-island": "page",
12742
+ "data-farm-island-strategy": moduleMetadata.islandStrategy ?? "load"
12743
+ },
12138
12744
  pageElement
12139
12745
  );
12140
12746
  }
@@ -12146,11 +12752,11 @@ var init_renderer = __esm({
12146
12752
  const slotProps = {};
12147
12753
  for (const slot of renderedRouteSlots) {
12148
12754
  if (slot.ownerPattern !== layoutEntry.pattern) continue;
12149
- let slotElement = import_react4.default.createElement(
12755
+ let slotElement = import_react5.default.createElement(
12150
12756
  slot.module.default,
12151
12757
  slot.props
12152
12758
  );
12153
- slotElement = import_react4.default.createElement(
12759
+ slotElement = import_react5.default.createElement(
12154
12760
  "div",
12155
12761
  {
12156
12762
  id: slot.containerId,
@@ -12161,7 +12767,7 @@ var init_renderer = __esm({
12161
12767
  );
12162
12768
  slotProps[slot.name] = slotElement;
12163
12769
  }
12164
- wrappedElement = import_react4.default.createElement(
12770
+ wrappedElement = import_react5.default.createElement(
12165
12771
  LayoutComponent,
12166
12772
  {
12167
12773
  children: wrappedElement,
@@ -12171,7 +12777,7 @@ var init_renderer = __esm({
12171
12777
  );
12172
12778
  }
12173
12779
  if (ErrorFallbackComponent) {
12174
- wrappedElement = import_react4.default.createElement(
12780
+ wrappedElement = import_react5.default.createElement(
12175
12781
  FarmRouteErrorBoundary,
12176
12782
  {
12177
12783
  Fallback: ErrorFallbackComponent,
@@ -12304,7 +12910,7 @@ var init_renderer = __esm({
12304
12910
  if (!cachedClerkProvider) {
12305
12911
  cachedClerkProvider = await importRuntimeModule("@clerk/react");
12306
12912
  }
12307
- wrapped = import_react4.default.createElement(
12913
+ wrapped = import_react5.default.createElement(
12308
12914
  cachedClerkProvider.ClerkProvider,
12309
12915
  provider.props || {},
12310
12916
  wrapped
@@ -12379,6 +12985,13 @@ var init_renderer = __esm({
12379
12985
  return reference;
12380
12986
  }
12381
12987
  async renderMetadataImage(req, res, options) {
12988
+ const method = (req.method || "GET").toUpperCase();
12989
+ if (method !== "GET" && method !== "HEAD") {
12990
+ res.statusCode = 405;
12991
+ res.setHeader("Allow", "GET, HEAD");
12992
+ res.end();
12993
+ return;
12994
+ }
12382
12995
  if (options.image.sourceType === "static") {
12383
12996
  if (!options.image.staticInfo) {
12384
12997
  throw new Error(`Static metadata image ${options.image.modulePath} is missing file info`);
@@ -12404,42 +13017,12 @@ var init_renderer = __esm({
12404
13017
  await this.writeMetadataImageResponse(req, res, handlerResult, imageModule);
12405
13018
  }
12406
13019
  async writeMetadataImageResponse(req, res, value, imageModule) {
12407
- if (isWebResponse(value)) {
12408
- res.statusCode = value.status;
12409
- value.headers.forEach((headerValue, key) => {
12410
- res.setHeader(key, headerValue);
12411
- });
12412
- if (req.method === "HEAD") {
12413
- res.end();
12414
- return;
12415
- }
12416
- const body2 = Buffer.from(await value.arrayBuffer());
12417
- res.write(body2);
12418
- res.end();
12419
- return;
12420
- }
12421
- const contentType = imageModule.contentType || "image/svg+xml; charset=utf-8";
12422
- let body;
12423
- if (typeof value === "string") {
12424
- body = value;
12425
- } else if (value instanceof ArrayBuffer) {
12426
- body = Buffer.from(value);
12427
- } else if (ArrayBuffer.isView(value)) {
12428
- body = Buffer.from(value.buffer, value.byteOffset, value.byteLength);
12429
- } else if (import_react4.default.isValidElement(value)) {
12430
- body = (0, import_server.renderToStaticMarkup)(value);
12431
- } else {
12432
- throw new Error("Metadata image must return a Response, string, bytes, or React element");
12433
- }
12434
- res.statusCode = res.statusCode || 200;
12435
- res.setHeader("Content-Type", contentType);
12436
- res.setHeader("Cache-Control", "public, max-age=0, must-revalidate");
12437
- if (req.method === "HEAD") {
12438
- res.end();
12439
- return;
12440
- }
12441
- res.write(body);
12442
- res.end();
13020
+ const ifNoneMatch = req.headers["if-none-match"];
13021
+ const response = await createFarmMetadataImageResponse(value, imageModule, {
13022
+ method: req.method,
13023
+ ifNoneMatch: Array.isArray(ifNoneMatch) ? ifNoneMatch[0] : ifNoneMatch
13024
+ });
13025
+ await sendWebResponse(res, response);
12443
13026
  }
12444
13027
  async writeStaticMetadataImageResponse(req, res, image) {
12445
13028
  const method = (req.method || "GET").toUpperCase();
@@ -12486,7 +13069,7 @@ var init_renderer = __esm({
12486
13069
  return false;
12487
13070
  }
12488
13071
  const ErrorComponent = errorModule.default;
12489
- const errorElement = import_react4.default.createElement(ErrorComponent, {
13072
+ const errorElement = import_react5.default.createElement(ErrorComponent, {
12490
13073
  ...createRouteStateProps({
12491
13074
  params: options.params,
12492
13075
  searchParamsObject: options.searchParamsObject,
@@ -12504,7 +13087,7 @@ var init_renderer = __esm({
12504
13087
  );
12505
13088
  for (let i = layoutModules.length - 1; i >= 0; i--) {
12506
13089
  const LayoutComponent = layoutModules[i].default;
12507
- wrapped = import_react4.default.createElement(
13090
+ wrapped = import_react5.default.createElement(
12508
13091
  LayoutComponent,
12509
13092
  {
12510
13093
  children: wrapped,
@@ -12569,6 +13152,7 @@ var init_renderer = __esm({
12569
13152
  search: routeEntry.search,
12570
13153
  isClientComponent: routeEntry.isClientComponent,
12571
13154
  shouldHydrate: routeEntry.shouldHydrate,
13155
+ islandStrategy: routeEntry.islandStrategy,
12572
13156
  preloads: [routeEntry.modulePath],
12573
13157
  assets: []
12574
13158
  };
@@ -12605,6 +13189,7 @@ window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
12605
13189
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
12606
13190
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
12607
13191
  window.__FARM_SHOULD_HYDRATE__ = ${JSON.stringify(req.__FARM_SHOULD_HYDRATE__ === true)};
13192
+ window.__FARM_ISLAND_STRATEGY__ = ${JSON.stringify(req.__FARM_ISLAND_STRATEGY__ || "load")};
12608
13193
  window.__FARM_PAGE_MODULE__ = ${JSON.stringify(relativePath2)};
12609
13194
  window.__FARM_LOADING_MODULE__ = ${JSON.stringify(
12610
13195
  req.__FARM_LOADING_MODULE_PATH__ || null
@@ -12622,8 +13207,8 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
12622
13207
  const i18nSnapshot = getFarmI18nClientSnapshot();
12623
13208
  const i18nAlternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
12624
13209
  const fontHead = renderFarmFontDevHead(this.config.root || process.cwd());
12625
- const streamRoot = import_react4.default.createElement("div", { style: { display: "contents" } }, element);
12626
- const { pipe, abort } = (0, import_server.renderToPipeableStream)(streamRoot, {
13210
+ const streamRoot = import_react5.default.createElement("div", { style: { display: "contents" } }, element);
13211
+ const { pipe, abort } = (0, import_server2.renderToPipeableStream)(streamRoot, {
12627
13212
  onShellReady() {
12628
13213
  const shellReadyMs = Date.now() - streamStartTime;
12629
13214
  emitFarmEvent({
@@ -12781,12 +13366,12 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
12781
13366
  break;
12782
13367
  }
12783
13368
  }
12784
- let element = import_react4.default.createElement(
13369
+ let element = import_react5.default.createElement(
12785
13370
  NotFoundComponent,
12786
13371
  { pathname }
12787
13372
  );
12788
13373
  if (LayoutComponent) {
12789
- element = import_react4.default.createElement(
13374
+ element = import_react5.default.createElement(
12790
13375
  LayoutComponent,
12791
13376
  { children: element }
12792
13377
  );
@@ -12936,6 +13521,7 @@ var init_app = __esm({
12936
13521
  init_server2();
12937
13522
  init_cache();
12938
13523
  init_auth_config();
13524
+ init_preload();
12939
13525
  defaultDocsConfig = {
12940
13526
  enabled: false,
12941
13527
  entry: "/docs",
@@ -13001,6 +13587,7 @@ var init_app = __esm({
13001
13587
  context: config.context || (() => void 0),
13002
13588
  serverActions: resolveServerActionsConfig(config.serverActions),
13003
13589
  images: resolveFarmImageConfig(config.images),
13590
+ performance: resolveFarmPerformanceConfig(config.performance),
13004
13591
  i18n: isResolvedI18nConfig(config.i18n) ? config.i18n : resolveFarmI18nConfig(config.i18n, {
13005
13592
  root,
13006
13593
  mode: process.env.NODE_ENV === "production" ? "production" : "development"
@@ -14594,6 +15181,49 @@ var init_search_client = __esm({
14594
15181
  }
14595
15182
  });
14596
15183
 
15184
+ // src/docs/fonts.ts
15185
+ function resolveFarmDocsFontAssets(root) {
15186
+ return FARM_DOCS_FONT_SOURCES.flatMap(({ family, packagePath }) => {
15187
+ const sourcePath = import_node_path8.default.join(root, "node_modules", packagePath);
15188
+ if (!(0, import_node_fs5.existsSync)(sourcePath)) return [];
15189
+ const source = (0, import_node_fs5.readFileSync)(sourcePath);
15190
+ const fingerprint = (0, import_node_crypto3.createHash)("sha256").update(source).digest("hex").slice(0, 12);
15191
+ const extension = import_node_path8.default.extname(packagePath);
15192
+ const baseName = import_node_path8.default.basename(packagePath, extension);
15193
+ return [
15194
+ {
15195
+ family,
15196
+ sourcePath,
15197
+ url: `/assets/fonts/${baseName}-h${fingerprint}${extension}`
15198
+ }
15199
+ ];
15200
+ });
15201
+ }
15202
+ function toFarmDocsPublicFontAssets(assets) {
15203
+ return assets.map(({ family, url }) => ({ family, url }));
15204
+ }
15205
+ var import_node_crypto3, import_node_fs5, import_node_path8, FARM_DOCS_FONT_SOURCES;
15206
+ var init_fonts = __esm({
15207
+ "src/docs/fonts.ts"() {
15208
+ "use strict";
15209
+ import_node_crypto3 = require("crypto");
15210
+ import_node_fs5 = require("fs");
15211
+ import_node_path8 = __toESM(require("path"));
15212
+ FARM_DOCS_FONT_SOURCES = [
15213
+ {
15214
+ family: "Geist Sans",
15215
+ packagePath: "geist/dist/fonts/geist-sans/Geist-Variable.woff2"
15216
+ },
15217
+ {
15218
+ family: "Geist Mono",
15219
+ packagePath: "geist/dist/fonts/geist-mono/GeistMono-Variable.woff2"
15220
+ }
15221
+ ];
15222
+ __name(resolveFarmDocsFontAssets, "resolveFarmDocsFontAssets");
15223
+ __name(toFarmDocsPublicFontAssets, "toFarmDocsPublicFontAssets");
15224
+ }
15225
+ });
15226
+
14597
15227
  // src/default-styles.ts
14598
15228
  var defaultGlobalCSS;
14599
15229
  var init_default_styles = __esm({
@@ -15001,12 +15631,12 @@ var init_module = __esm({
15001
15631
  function isMiddlewareResponse(value) {
15002
15632
  return value instanceof Response;
15003
15633
  }
15004
- var fs4, path18, _MiddlewareManager, MiddlewareManager;
15634
+ var fs4, path19, _MiddlewareManager, MiddlewareManager;
15005
15635
  var init_manager = __esm({
15006
15636
  "src/middleware/manager.ts"() {
15007
15637
  "use strict";
15008
15638
  fs4 = __toESM(require("fs"));
15009
- path18 = __toESM(require("path"));
15639
+ path19 = __toESM(require("path"));
15010
15640
  init_context();
15011
15641
  init_module();
15012
15642
  init_utils();
@@ -15086,8 +15716,8 @@ var init_manager = __esm({
15086
15716
  const entries = fs4.readdirSync(dir, { withFileTypes: true });
15087
15717
  for (const entry of entries) {
15088
15718
  if (entry.isDirectory() && !entry.name.startsWith(".") && !entry.name.startsWith("_")) {
15089
- const subPath = path18.join(dir, entry.name);
15090
- const subRoutePath = path18.posix.join(routePath, entry.name);
15719
+ const subPath = path19.join(dir, entry.name);
15720
+ const subRoutePath = path19.posix.join(routePath, entry.name);
15091
15721
  await this.discoverInDirectory(subPath, subRoutePath, discovered);
15092
15722
  }
15093
15723
  }
@@ -15098,7 +15728,7 @@ var init_manager = __esm({
15098
15728
  findMiddlewareFile(dir) {
15099
15729
  const extensions = [".ts", ".tsx", ".js", ".jsx"];
15100
15730
  for (const ext of extensions) {
15101
- const filePath = path18.join(dir, `middleware${ext}`);
15731
+ const filePath = path19.join(dir, `middleware${ext}`);
15102
15732
  if (fs4.existsSync(filePath)) {
15103
15733
  return filePath;
15104
15734
  }
@@ -15449,7 +16079,7 @@ var init_manager = __esm({
15449
16079
  // src/write-file-if-changed.ts
15450
16080
  function writeFileIfChanged(filePath, content) {
15451
16081
  try {
15452
- if ((0, import_node_fs5.readFileSync)(filePath, "utf8") === content) {
16082
+ if ((0, import_node_fs6.readFileSync)(filePath, "utf8") === content) {
15453
16083
  return false;
15454
16084
  }
15455
16085
  } catch (error) {
@@ -15457,14 +16087,14 @@ function writeFileIfChanged(filePath, content) {
15457
16087
  throw error;
15458
16088
  }
15459
16089
  }
15460
- (0, import_node_fs5.writeFileSync)(filePath, content, "utf8");
16090
+ (0, import_node_fs6.writeFileSync)(filePath, content, "utf8");
15461
16091
  return true;
15462
16092
  }
15463
- var import_node_fs5;
16093
+ var import_node_fs6;
15464
16094
  var init_write_file_if_changed = __esm({
15465
16095
  "src/write-file-if-changed.ts"() {
15466
16096
  "use strict";
15467
- import_node_fs5 = require("fs");
16097
+ import_node_fs6 = require("fs");
15468
16098
  __name(writeFileIfChanged, "writeFileIfChanged");
15469
16099
  }
15470
16100
  });
@@ -15565,11 +16195,11 @@ var init_type_generator = __esm({
15565
16195
  routeGroups.get(key).push(route);
15566
16196
  }
15567
16197
  const nestedStructure = {};
15568
- for (const [path29, routeList] of routeGroups) {
16198
+ for (const [path31, routeList] of routeGroups) {
15569
16199
  const route = routeList[0];
15570
16200
  const importPath = this.getRouteImportPath(route, options.outFile);
15571
16201
  const routeName = this.pathToRouteName(route.path);
15572
- const cleanPath = path29 === "/api" ? "" : path29.replace(/^\/api\//, "");
16202
+ const cleanPath = path31 === "/api" ? "" : path31.replace(/^\/api\//, "");
15573
16203
  const parts = cleanPath ? cleanPath.split("/") : [];
15574
16204
  const allMethods = routeList.flatMap((r) => r.methods);
15575
16205
  for (const method of allMethods) {
@@ -15628,8 +16258,8 @@ ${typeExports}
15628
16258
  const relativeImport = (0, import_path10.relative)((0, import_path10.dirname)(outFile), route.filePath).replace(/\\/g, "/").replace(/\.(ts|tsx|js|jsx)$/, "");
15629
16259
  return relativeImport.startsWith(".") ? relativeImport : `./${relativeImport}`;
15630
16260
  }
15631
- pathToRouteName(path29) {
15632
- return (path29 === "/api" ? "root" : path29.replace(/^\/api\//, "")).replace(/\//g, "_").replace(/[^a-zA-Z0-9_]/g, "");
16261
+ pathToRouteName(path31) {
16262
+ return (path31 === "/api" ? "root" : path31.replace(/^\/api\//, "")).replace(/\//g, "_").replace(/[^a-zA-Z0-9_]/g, "");
15633
16263
  }
15634
16264
  structureToTypeString(obj, indent) {
15635
16265
  const spaces = " ".repeat(indent);
@@ -15649,15 +16279,15 @@ ${typeExports}
15649
16279
  toTypePropertyKey(key) {
15650
16280
  return /^[$A-Z_][0-9A-Z_$]*$/i.test(key) ? key : JSON.stringify(key);
15651
16281
  }
15652
- getBaseExportName(path29) {
15653
- const cleanPath = path29.replace(/^\/api\//, "");
16282
+ getBaseExportName(path31) {
16283
+ const cleanPath = path31.replace(/^\/api\//, "");
15654
16284
  if (cleanPath === "") {
15655
16285
  return "api";
15656
16286
  }
15657
16287
  return cleanPath;
15658
16288
  }
15659
- getExportName(path29, method) {
15660
- const cleanPath = path29.replace(/^\/api\//, "");
16289
+ getExportName(path31, method) {
16290
+ const cleanPath = path31.replace(/^\/api\//, "");
15661
16291
  if (cleanPath === "") {
15662
16292
  return method.toLowerCase();
15663
16293
  }
@@ -15719,12 +16349,12 @@ function createRoutePattern(route) {
15719
16349
  }).join("/");
15720
16350
  }
15721
16351
  async function generateRouteTypes(options) {
15722
- const root = path19.resolve(options.root);
16352
+ const root = path20.resolve(options.root);
15723
16353
  const srcDir = options.srcDir || "src";
15724
16354
  const outFile = options.outFile || DEFAULT_OUT_FILE;
15725
- const outPath = path19.isAbsolute(outFile) ? outFile : path19.join(root, srcDir, outFile);
16355
+ const outPath = path20.isAbsolute(outFile) ? outFile : path20.join(root, srcDir, outFile);
15726
16356
  const content = await createRouteTypeDeclarations(options, outPath);
15727
- fs5.mkdirSync(path19.dirname(outPath), { recursive: true });
16357
+ fs5.mkdirSync(path20.dirname(outPath), { recursive: true });
15728
16358
  writeFileIfChanged(outPath, content);
15729
16359
  return outPath;
15730
16360
  }
@@ -15734,7 +16364,7 @@ async function createRouteTypeDeclarations(options, outPath) {
15734
16364
  const patterns = /* @__PURE__ */ new Set();
15735
16365
  const glob = await import("fast-glob");
15736
16366
  for (const source of sourceRoots) {
15737
- const appDir = path19.join(source.root, source.srcDir, "app");
16367
+ const appDir = path20.join(source.root, source.srcDir, "app");
15738
16368
  if (fs5.existsSync(appDir)) {
15739
16369
  const pageFiles = await glob.default("**/page.{ts,tsx,js,jsx,md,mdx}", {
15740
16370
  cwd: appDir,
@@ -15763,7 +16393,7 @@ async function createRouteTypeDeclarations(options, outPath) {
15763
16393
  const patternLiterals = sortedPatterns.map(routePatternToRouteLiteral);
15764
16394
  const routePathType = suppressLintOnLink ? "string" : typeLiterals.length ? typeLiterals.join(" | ") : "never";
15765
16395
  const routePatternType = suppressLintOnLink ? "string" : patternLiterals.length ? patternLiterals.join(" | ") : "never";
15766
- const routeTypesImportPath = `./${path19.basename(outPath).replace(/\.d\.ts$/, "")}`;
16396
+ const routeTypesImportPath = `./${path20.basename(outPath).replace(/\.d\.ts$/, "")}`;
15767
16397
  const augmentationBlock = suppressLintOnLink ? "" : `
15768
16398
  declare module "@farm.js/core/client" {
15769
16399
  interface LinkDefaultRoute {
@@ -15799,11 +16429,11 @@ export type RoutePattern = ${routePatternType};${augmentationBlock}
15799
16429
  `;
15800
16430
  return content;
15801
16431
  }
15802
- var path19, fs5, DEFAULT_OUT_FILE;
16432
+ var path20, fs5, DEFAULT_OUT_FILE;
15803
16433
  var init_generate_route_types = __esm({
15804
16434
  "src/routing/generate-route-types.ts"() {
15805
16435
  "use strict";
15806
- path19 = __toESM(require("path"));
16436
+ path20 = __toESM(require("path"));
15807
16437
  fs5 = __toESM(require("fs"));
15808
16438
  init_utils();
15809
16439
  init_route_slots();
@@ -15821,17 +16451,17 @@ var init_generate_route_types = __esm({
15821
16451
 
15822
16452
  // src/env-types.ts
15823
16453
  async function generateEnvTypes(options) {
15824
- const root = path20.resolve(options.root);
16454
+ const root = path21.resolve(options.root);
15825
16455
  const srcDir = options.srcDir || "src";
15826
16456
  const outFile = options.outFile || DEFAULT_OUT_FILE2;
15827
- const outPath = path20.isAbsolute(outFile) ? outFile : path20.join(root, srcDir, outFile);
16457
+ const outPath = path21.isAbsolute(outFile) ? outFile : path21.join(root, srcDir, outFile);
15828
16458
  const content = createEnvTypeDeclarations(options, outPath);
15829
- fs6.mkdirSync(path20.dirname(outPath), { recursive: true });
16459
+ fs6.mkdirSync(path21.dirname(outPath), { recursive: true });
15830
16460
  writeFileIfChanged(outPath, content);
15831
16461
  return outPath;
15832
16462
  }
15833
16463
  function createEnvTypeDeclarations(options, outPath) {
15834
- const root = path20.resolve(options.root);
16464
+ const root = path21.resolve(options.root);
15835
16465
  const configPath = findConfigPath(root, options.configPath);
15836
16466
  const configPaths = [
15837
16467
  ...(options.layerConfigPaths ?? []).filter((value) => fs6.existsSync(value)),
@@ -15894,11 +16524,11 @@ export {};
15894
16524
  }
15895
16525
  function findConfigPath(root, configPath) {
15896
16526
  if (configPath) {
15897
- const resolvedPath = path20.isAbsolute(configPath) ? configPath : path20.join(root, configPath);
16527
+ const resolvedPath = path21.isAbsolute(configPath) ? configPath : path21.join(root, configPath);
15898
16528
  return fs6.existsSync(resolvedPath) ? resolvedPath : null;
15899
16529
  }
15900
16530
  for (const filename of CONFIG_FILENAMES2) {
15901
- const resolvedPath = path20.join(root, filename);
16531
+ const resolvedPath = path21.join(root, filename);
15902
16532
  if (fs6.existsSync(resolvedPath)) {
15903
16533
  return resolvedPath;
15904
16534
  }
@@ -15959,15 +16589,15 @@ export {};
15959
16589
  `;
15960
16590
  }
15961
16591
  function toTypeImportPath(outPath, targetPath) {
15962
- const relativePath2 = path20.relative(path20.dirname(outPath), targetPath).replace(/\\/g, "/").replace(/\.(tsx?|jsx?|mjs|cjs|mts|cts)$/, "");
16592
+ const relativePath2 = path21.relative(path21.dirname(outPath), targetPath).replace(/\\/g, "/").replace(/\.(tsx?|jsx?|mjs|cjs|mts|cts)$/, "");
15963
16593
  return relativePath2.startsWith(".") ? relativePath2 : `./${relativePath2}`;
15964
16594
  }
15965
- var fs6, path20, DEFAULT_OUT_FILE2, CONFIG_FILENAMES2;
16595
+ var fs6, path21, DEFAULT_OUT_FILE2, CONFIG_FILENAMES2;
15966
16596
  var init_env_types = __esm({
15967
16597
  "src/env-types.ts"() {
15968
16598
  "use strict";
15969
16599
  fs6 = __toESM(require("fs"));
15970
- path20 = __toESM(require("path"));
16600
+ path21 = __toESM(require("path"));
15971
16601
  init_write_file_if_changed();
15972
16602
  DEFAULT_OUT_FILE2 = "farm-env.d.ts";
15973
16603
  CONFIG_FILENAMES2 = [
@@ -16000,8 +16630,8 @@ var init_env_types = __esm({
16000
16630
 
16001
16631
  // src/image-types.ts
16002
16632
  function generateFarmImageTypes(options) {
16003
- const outputPath = options.outFile ? import_node_path8.default.resolve(options.root, options.outFile) : import_node_path8.default.join(options.root, options.srcDir || "src", "farm-images.d.ts");
16004
- (0, import_node_fs6.mkdirSync)(import_node_path8.default.dirname(outputPath), { recursive: true });
16633
+ const outputPath = options.outFile ? import_node_path9.default.resolve(options.root, options.outFile) : import_node_path9.default.join(options.root, options.srcDir || "src", "farm-images.d.ts");
16634
+ (0, import_node_fs7.mkdirSync)(import_node_path9.default.dirname(outputPath), { recursive: true });
16005
16635
  writeFileIfChanged(outputPath, createFarmImageTypeDeclarations());
16006
16636
  return outputPath;
16007
16637
  }
@@ -16025,12 +16655,12 @@ declare module "*?url" {
16025
16655
  }
16026
16656
  `;
16027
16657
  }
16028
- var import_node_fs6, import_node_path8, FARM_STATIC_IMAGE_EXTENSIONS;
16658
+ var import_node_fs7, import_node_path9, FARM_STATIC_IMAGE_EXTENSIONS;
16029
16659
  var init_image_types = __esm({
16030
16660
  "src/image-types.ts"() {
16031
16661
  "use strict";
16032
- import_node_fs6 = require("fs");
16033
- import_node_path8 = __toESM(require("path"));
16662
+ import_node_fs7 = require("fs");
16663
+ import_node_path9 = __toESM(require("path"));
16034
16664
  init_write_file_if_changed();
16035
16665
  FARM_STATIC_IMAGE_EXTENSIONS = ["avif", "gif", "jpeg", "jpg", "png", "webp"];
16036
16666
  __name(generateFarmImageTypes, "generateFarmImageTypes");
@@ -16042,9 +16672,9 @@ var init_image_types = __esm({
16042
16672
  async function generateFarmI18nTypes(options) {
16043
16673
  if (!options.config.enabled) return void 0;
16044
16674
  const { signatures } = await readFarmI18nCatalogs(options.config);
16045
- const outFile = options.outFile ? (0, import_node_path9.resolve)(options.root, options.outFile) : (0, import_node_path9.join)((0, import_node_path9.resolve)(options.root), options.srcDir || "src", "farm-i18n.d.ts");
16675
+ const outFile = options.outFile ? (0, import_node_path10.resolve)(options.root, options.outFile) : (0, import_node_path10.join)((0, import_node_path10.resolve)(options.root), options.srcDir || "src", "farm-i18n.d.ts");
16046
16676
  const content = renderFarmI18nTypes(options.config.locales, signatures);
16047
- (0, import_node_fs7.mkdirSync)((0, import_node_path9.dirname)(outFile), { recursive: true });
16677
+ (0, import_node_fs8.mkdirSync)((0, import_node_path10.dirname)(outFile), { recursive: true });
16048
16678
  writeFileIfChanged(outFile, content);
16049
16679
  return outFile;
16050
16680
  }
@@ -16085,12 +16715,12 @@ function argumentType(kind) {
16085
16715
  return "string";
16086
16716
  }
16087
16717
  }
16088
- var import_node_fs7, import_node_path9;
16718
+ var import_node_fs8, import_node_path10;
16089
16719
  var init_type_generator2 = __esm({
16090
16720
  "src/i18n/type-generator.ts"() {
16091
16721
  "use strict";
16092
- import_node_fs7 = require("fs");
16093
- import_node_path9 = require("path");
16722
+ import_node_fs8 = require("fs");
16723
+ import_node_path10 = require("path");
16094
16724
  init_catalog();
16095
16725
  init_write_file_if_changed();
16096
16726
  __name(generateFarmI18nTypes, "generateFarmI18nTypes");
@@ -16245,7 +16875,7 @@ var init_type_artifacts = __esm({
16245
16875
  var version;
16246
16876
  var init_package = __esm({
16247
16877
  "package.json"() {
16248
- version = "0.1.0-beta.10";
16878
+ version = "0.1.0-beta.13";
16249
16879
  }
16250
16880
  });
16251
16881
 
@@ -16425,12 +17055,12 @@ async function createBlurDataURL(bytes) {
16425
17055
  return void 0;
16426
17056
  }
16427
17057
  }
16428
- var import_promises7, import_node_path10, import_image_size2, FARM_IMAGE_ID_PREFIX, STATIC_IMAGE_RE, SCRIPT_IMPORTER_RE;
17058
+ var import_promises7, import_node_path11, import_image_size2, FARM_IMAGE_ID_PREFIX, STATIC_IMAGE_RE, SCRIPT_IMPORTER_RE;
16429
17059
  var init_image_vite = __esm({
16430
17060
  "src/image-vite.ts"() {
16431
17061
  "use strict";
16432
17062
  import_promises7 = require("fs/promises");
16433
- import_node_path10 = __toESM(require("path"));
17063
+ import_node_path11 = __toESM(require("path"));
16434
17064
  import_image_size2 = require("image-size");
16435
17065
  FARM_IMAGE_ID_PREFIX = "\0farm:image:";
16436
17066
  STATIC_IMAGE_RE = /\.(?:avif|gif|jpe?g|png|webp)$/i;
@@ -16441,6 +17071,22 @@ var init_image_vite = __esm({
16441
17071
  }
16442
17072
  });
16443
17073
 
17074
+ // src/font.ts
17075
+ function resolveFarmLayoutFonts(layouts) {
17076
+ let resolved;
17077
+ for (const layout2 of layouts) {
17078
+ if (!layout2.fonts) continue;
17079
+ resolved = { ...resolved, ...layout2.fonts };
17080
+ }
17081
+ return resolved;
17082
+ }
17083
+ var init_font = __esm({
17084
+ "src/font.ts"() {
17085
+ "use strict";
17086
+ __name(resolveFarmLayoutFonts, "resolveFarmLayoutFonts");
17087
+ }
17088
+ });
17089
+
16444
17090
  // src/image-server.ts
16445
17091
  function createFarmImageHandler(config, options) {
16446
17092
  const fetcher = options.fetch ?? globalThis.fetch;
@@ -16857,6 +17503,106 @@ var init_image_server = __esm({
16857
17503
  }
16858
17504
  });
16859
17505
 
17506
+ // src/server/vite-config.ts
17507
+ function createFarmClientOptimizeDepsEntries(projectRoot, appDirectories) {
17508
+ const resolvedProjectRoot = import_node_path12.default.resolve(projectRoot);
17509
+ return appDirectories.map((appDirectory) => {
17510
+ const relativeAppDirectory = import_node_path12.default.relative(resolvedProjectRoot, import_node_path12.default.resolve(appDirectory)).replace(/\\/g, "/");
17511
+ const prefix = relativeAppDirectory || ".";
17512
+ return `${prefix}/${FARM_CLIENT_ENTRY_PATTERN}`;
17513
+ });
17514
+ }
17515
+ function createFarmClientOptimizeDepsConfig(entries) {
17516
+ return {
17517
+ // Keep normal application dependency discovery for CJS-only packages, but
17518
+ // seed every Farm browser runtime so React is present in the first crawl.
17519
+ noDiscovery: false,
17520
+ // Waiting for the crawl avoids serving a partial optimizer generation and
17521
+ // then changing browser hashes while hydration is already in progress.
17522
+ holdUntilCrawlEnd: true,
17523
+ include: [...FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE],
17524
+ // Farm's HTML boots through a virtual module and imports route UI lazily.
17525
+ // Explicit entries let Vite finish that crawl before a navigation can
17526
+ // rotate the optimizer hash underneath a hydrated React runtime.
17527
+ ...entries?.length ? { entries: [...entries] } : {}
17528
+ };
17529
+ }
17530
+ function mergeNoExternal(farmValue, userValue) {
17531
+ if (farmValue === true || userValue === true) return true;
17532
+ const normalize = /* @__PURE__ */ __name((value) => value == null || value === true ? [] : Array.isArray(value) ? value : [value], "normalize");
17533
+ return Array.from(/* @__PURE__ */ new Set([...normalize(farmValue), ...normalize(userValue)]));
17534
+ }
17535
+ function mergeFarmViteConfig(farmConfig, userConfig = {}) {
17536
+ const noDiscovery = userConfig.optimizeDeps?.noDiscovery ?? farmConfig.optimizeDeps?.noDiscovery;
17537
+ const holdUntilCrawlEnd = userConfig.optimizeDeps?.holdUntilCrawlEnd ?? (userConfig.optimizeDeps?.noDiscovery === true ? false : farmConfig.optimizeDeps?.holdUntilCrawlEnd);
17538
+ return {
17539
+ ...farmConfig,
17540
+ ...userConfig,
17541
+ plugins: [...farmConfig.plugins || [], ...userConfig.plugins || []],
17542
+ server: {
17543
+ ...farmConfig.server,
17544
+ ...userConfig.server
17545
+ },
17546
+ resolve: {
17547
+ ...farmConfig.resolve,
17548
+ ...userConfig.resolve,
17549
+ dedupe: Array.from(
17550
+ /* @__PURE__ */ new Set([...farmConfig.resolve?.dedupe || [], ...userConfig.resolve?.dedupe || []])
17551
+ )
17552
+ },
17553
+ optimizeDeps: {
17554
+ ...farmConfig.optimizeDeps,
17555
+ ...userConfig.optimizeDeps,
17556
+ noDiscovery,
17557
+ holdUntilCrawlEnd,
17558
+ include: Array.from(
17559
+ /* @__PURE__ */ new Set([
17560
+ ...farmConfig.optimizeDeps?.include || [],
17561
+ ...userConfig.optimizeDeps?.include || []
17562
+ ])
17563
+ ),
17564
+ exclude: Array.from(
17565
+ /* @__PURE__ */ new Set([
17566
+ ...farmConfig.optimizeDeps?.exclude || [],
17567
+ ...userConfig.optimizeDeps?.exclude || []
17568
+ ])
17569
+ )
17570
+ },
17571
+ ssr: {
17572
+ ...farmConfig.ssr,
17573
+ ...userConfig.ssr,
17574
+ noExternal: mergeNoExternal(farmConfig.ssr?.noExternal, userConfig.ssr?.noExternal)
17575
+ }
17576
+ };
17577
+ }
17578
+ var import_node_path12, FARM_CLIENT_ENTRY_PATTERN, FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE;
17579
+ var init_vite_config = __esm({
17580
+ "src/server/vite-config.ts"() {
17581
+ "use strict";
17582
+ import_node_path12 = __toESM(require("path"));
17583
+ FARM_CLIENT_ENTRY_PATTERN = "**/{page,layout,loading,error,not-found,default}.{js,jsx,ts,tsx}";
17584
+ FARM_CLIENT_OPTIMIZE_DEPS_INCLUDE = [
17585
+ "react",
17586
+ "react-dom",
17587
+ "react-dom/client",
17588
+ "react/jsx-runtime",
17589
+ "react/jsx-dev-runtime",
17590
+ "@farm.js/core/client",
17591
+ "@farm.js/core/plugin/client",
17592
+ "@farm.js/core/deferred",
17593
+ "@farm.js/core/deployment",
17594
+ "@farm.js/core/i18n/client",
17595
+ "@farm.js/core/query/client",
17596
+ "@farm.js/core/server-fn/client",
17597
+ "@farm.js/core/server-query/client"
17598
+ ];
17599
+ __name(createFarmClientOptimizeDepsEntries, "createFarmClientOptimizeDepsEntries");
17600
+ __name(createFarmClientOptimizeDepsConfig, "createFarmClientOptimizeDepsConfig");
17601
+ __name(mergeNoExternal, "mergeNoExternal");
17602
+ __name(mergeFarmViteConfig, "mergeFarmViteConfig");
17603
+ }
17604
+ });
17605
+
16860
17606
  // src/docs/pixel-border-css.ts
16861
17607
  var farmDocsPixelBorderCss;
16862
17608
  var init_pixel_border_css = __esm({
@@ -16902,8 +17648,8 @@ var init_pixel_border_css = __esm({
16902
17648
  --scrollbar-thumb-hover: var(--color-fd-ring);
16903
17649
  --scrollbar-track: transparent;
16904
17650
  --radius: 0px;
16905
- --font-geist-sans: "Geist Sans";
16906
- --font-geist-mono: "Geist Mono";
17651
+ --font-geist-sans: ui-sans-serif, system-ui, sans-serif;
17652
+ --font-geist-mono: ui-monospace, monospace;
16907
17653
  }
16908
17654
 
16909
17655
  .dark {
@@ -16916,28 +17662,6 @@ body,
16916
17662
  background-color: var(--color-fd-background) !important;
16917
17663
  }
16918
17664
 
16919
- @font-face {
16920
- font-family: "Geist Sans";
16921
- font-display: block;
16922
- font-style: normal;
16923
- font-weight: 100 900;
16924
- src:
16925
- local("Geist Sans"),
16926
- url("/assets/Geist-Variable-CrgPqtmy.woff2") format("woff2"),
16927
- url("/node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2") format("woff2");
16928
- }
16929
-
16930
- @font-face {
16931
- font-family: "Geist Mono";
16932
- font-display: block;
16933
- font-style: normal;
16934
- font-weight: 100 900;
16935
- src:
16936
- local("Geist Mono"),
16937
- url("/assets/GeistMono-Variable-BNLlm6Cd.woff2") format("woff2"),
16938
- url("/node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2") format("woff2");
16939
- }
16940
-
16941
17665
  * {
16942
17666
  scrollbar-color: var(--scrollbar-thumb) var(--scrollbar-track);
16943
17667
  scrollbar-width: thin;
@@ -18128,11 +18852,418 @@ code:not(pre code) {
18128
18852
  }
18129
18853
  });
18130
18854
 
18855
+ // src/docs/social-image.ts
18856
+ function normalizeEntry(entry) {
18857
+ if (!entry || entry === "/") return "";
18858
+ return `/${entry.replace(/^\/+|\/+$/g, "")}`;
18859
+ }
18860
+ function encodeSlugPath(slug) {
18861
+ return slug.split("/").filter(Boolean).map((segment) => encodeURIComponent(segment)).join("/");
18862
+ }
18863
+ function getSocialImageConfig(docs) {
18864
+ return typeof docs.config.socialImage === "object" && docs.config.socialImage ? docs.config.socialImage : void 0;
18865
+ }
18866
+ function getNavTitle(docs) {
18867
+ if (typeof docs.config.nav !== "object" || !docs.config.nav || !("title" in docs.config.nav)) {
18868
+ return "Farm.js";
18869
+ }
18870
+ return String(docs.config.nav.title || "Farm.js");
18871
+ }
18872
+ function isFalse(value) {
18873
+ return value ? /^(false|no|none|off|disabled)$/i.test(value.trim()) : false;
18874
+ }
18875
+ function isExternalOrRootPath(value) {
18876
+ return /^(https?:)?\/\//i.test(value) || value.startsWith("/");
18877
+ }
18878
+ function isFarmDocsSocialImageEnabled(page2, docs) {
18879
+ if (docs.config.socialImage === false || getSocialImageConfig(docs)?.enabled === false) {
18880
+ return false;
18881
+ }
18882
+ return !isFalse(page2.frontmatter.socialImage);
18883
+ }
18884
+ function getFarmDocsCustomSocialImage(page2) {
18885
+ const value = page2.frontmatter.socialImage || page2.frontmatter.openGraphImage || page2.frontmatter.ogImage;
18886
+ return value && !isFalse(value) && isExternalOrRootPath(value) ? value : void 0;
18887
+ }
18888
+ function resolveIllustration(page2) {
18889
+ const explicit = page2.frontmatter.socialIllustration?.toLowerCase();
18890
+ if (explicit === "auth" || explicit === "cache" || explicit === "cli" || explicit === "integrations" || explicit === "project" || explicit === "routing" || explicit === "runtime") {
18891
+ return explicit;
18892
+ }
18893
+ const source = `${page2.slug} ${page2.title} ${page2.section || ""}`.toLowerCase();
18894
+ if (/auth|session|security|csrf/.test(source)) return "auth";
18895
+ if (/cache|ppr|render|static|revalid|stream/.test(source)) return "cache";
18896
+ if (/integration|stripe|prisma|better.auth|email|job|workflow/.test(source)) {
18897
+ return "integrations";
18898
+ }
18899
+ if (/cli|test|deploy|preview|upgrade|migration|install/.test(source)) return "cli";
18900
+ if (/project|structure|config|environment|markdown|docs.engine/.test(source)) {
18901
+ return "project";
18902
+ }
18903
+ if (/rout|endpoint|middleware|navigation|server.function|server.action|api/.test(source)) {
18904
+ return "routing";
18905
+ }
18906
+ return "runtime";
18907
+ }
18908
+ function createHashValue(value) {
18909
+ return (0, import_node_crypto4.createHash)("sha256").update(JSON.stringify(value)).digest("hex").slice(0, 16);
18910
+ }
18911
+ function resolveBaseUrl(requestUrl, config) {
18912
+ return config?.baseUrl ? new URL(config.baseUrl) : new URL(requestUrl.origin);
18913
+ }
18914
+ function createFarmDocsSocialImageDescriptor(page2, docs, requestUrl) {
18915
+ const config = getSocialImageConfig(docs);
18916
+ const siteName = config?.siteName || getNavTitle(docs);
18917
+ const brand = config?.brand || siteName;
18918
+ const title = page2.frontmatter.socialTitle || page2.title;
18919
+ const description = page2.frontmatter.socialDescription || page2.description || docs.config.metadata?.description || `Documentation for ${siteName}`;
18920
+ const section = page2.frontmatter.section || page2.section || "Documentation";
18921
+ const illustration = resolveIllustration(page2);
18922
+ const entry = normalizeEntry(docs.entry);
18923
+ const slugPath = encodeSlugPath(page2.slug) || "index";
18924
+ const imagePath = `${entry}/_social/${slugPath}/opengraph-image.svg`;
18925
+ const baseUrl = resolveBaseUrl(requestUrl, config);
18926
+ const pageUrl = new URL(page2.href, baseUrl).href;
18927
+ const imageAlt = `${title} \u2014 ${siteName} documentation`;
18928
+ const hash = createHashValue({
18929
+ version: FARM_DOCS_SOCIAL_IMAGE_VERSION,
18930
+ title,
18931
+ description,
18932
+ section,
18933
+ siteName,
18934
+ brand,
18935
+ pageHref: page2.href,
18936
+ illustration,
18937
+ fonts: config?.fonts
18938
+ });
18939
+ return {
18940
+ title,
18941
+ description,
18942
+ section,
18943
+ siteName,
18944
+ brand,
18945
+ pageHref: page2.href,
18946
+ pageUrl,
18947
+ imagePath,
18948
+ imageUrl: new URL(`${imagePath}?v=${hash}`, baseUrl).href,
18949
+ imageAlt,
18950
+ illustration,
18951
+ fonts: config?.fonts,
18952
+ hash
18953
+ };
18954
+ }
18955
+ function getFarmDocsSocialImageSlug(docs, requestUrl) {
18956
+ const entry = normalizeEntry(docs.entry);
18957
+ const prefix = `${entry}/_social/`;
18958
+ const suffix = "/opengraph-image.svg";
18959
+ if (!requestUrl.pathname.startsWith(prefix) || !requestUrl.pathname.endsWith(suffix)) {
18960
+ return null;
18961
+ }
18962
+ const rawSlug = requestUrl.pathname.slice(prefix.length, -suffix.length);
18963
+ if (!rawSlug) return null;
18964
+ try {
18965
+ const slug = rawSlug.split("/").map((segment) => decodeURIComponent(segment)).join("/");
18966
+ if (slug.split("/").some((segment) => !segment || segment === ".." || segment === ".")) {
18967
+ return null;
18968
+ }
18969
+ return slug === "index" ? "" : slug;
18970
+ } catch {
18971
+ return null;
18972
+ }
18973
+ }
18974
+ function escapeXml(value) {
18975
+ return value.replace(/&/g, "&amp;").replace(/</g, "&lt;").replace(/>/g, "&gt;").replace(/"/g, "&quot;").replace(/'/g, "&apos;");
18976
+ }
18977
+ function truncate(value, length) {
18978
+ if (value.length <= length) return value;
18979
+ return `${value.slice(0, Math.max(0, length - 1)).trimEnd()}\u2026`;
18980
+ }
18981
+ function wrapText(value, maxCharacters, maxLines) {
18982
+ const words = value.trim().split(/\s+/).filter(Boolean);
18983
+ const lines = [];
18984
+ let current = "";
18985
+ let consumedWords = 0;
18986
+ for (const word of words) {
18987
+ const candidate = current ? `${current} ${word}` : word;
18988
+ if (candidate.length <= maxCharacters || !current) {
18989
+ current = candidate;
18990
+ consumedWords += 1;
18991
+ continue;
18992
+ }
18993
+ lines.push(current);
18994
+ if (lines.length === maxLines) break;
18995
+ current = word;
18996
+ consumedWords += 1;
18997
+ }
18998
+ if (current && lines.length < maxLines) lines.push(current);
18999
+ if (consumedWords < words.length && lines.length) {
19000
+ lines[lines.length - 1] = truncate(`${lines[lines.length - 1]}\u2026`, maxCharacters);
19001
+ }
19002
+ return lines;
19003
+ }
19004
+ function renderTextLines(lines, x, y, lineHeight, attributes) {
19005
+ return lines.map(
19006
+ (line, index) => `<text x="${x}" y="${y + index * lineHeight}" ${attributes}>${escapeXml(line)}</text>`
19007
+ ).join("");
19008
+ }
19009
+ function renderEmbeddedFont(name, source, weight) {
19010
+ if (!source) return "";
19011
+ return `@font-face{font-family:"${name}";src:url("${escapeXml(source)}") format("woff2");font-style:normal;font-weight:${weight};font-display:block;}`;
19012
+ }
19013
+ function renderArrow(x1, y, x2, color = "#8b8b8b") {
19014
+ 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}"/>`;
19015
+ }
19016
+ function renderPanelHeader(label, status) {
19017
+ 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>`;
19018
+ }
19019
+ function renderRoutingIllustration() {
19020
+ return `${renderPanelHeader("ROUTE BLUEPRINT", "TYPED")}
19021
+ <rect x="819" y="296" width="92" height="54" fill="#a3a3a3"/>
19022
+ <text x="832" y="327" class="diagram-title ink">src/app</text>
19023
+ <path d="M911 323h41M952 232v181M952 232h44M952 323h44M952 413h44" fill="none" stroke="#696969"/>
19024
+ <rect x="996" y="205" width="138" height="54" fill="#0a0a0a" stroke="#414141"/>
19025
+ <text x="1010" y="228" class="diagram-title">/</text><text x="1010" y="246" class="micro">STATIC</text>
19026
+ <rect x="996" y="296" width="138" height="54" fill="#0a0a0a" stroke="#414141"/>
19027
+ <text x="1010" y="319" class="diagram-title">/blog/:slug</text><text x="1010" y="337" class="micro">DYNAMIC</text>
19028
+ <rect x="996" y="386" width="138" height="55" fill="#f0f0f0"/>
19029
+ <text x="1010" y="410" class="diagram-title ink">/docs/:path*</text><text x="1010" y="428" class="micro ink-muted">CATCH-ALL</text>
19030
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
19031
+ <text x="819" y="475" class="label bright">FILE ROUTES</text><text x="1134" y="475" class="label" text-anchor="end">TYPES INCLUDED</text>`;
19032
+ }
19033
+ function renderAuthIllustration() {
19034
+ return `${renderPanelHeader("REQUEST GUARD", "SERVER VERIFIED")}
19035
+ <rect x="819" y="269" width="86" height="54" fill="#0a0a0a" stroke="#454545"/>
19036
+ <text x="837" y="292" class="micro bright">REQUEST</text><text x="837" y="310" class="diagram-title">GET /app</text>
19037
+ ${renderArrow(905, 296, 943)}
19038
+ <rect x="943" y="224" width="104" height="118" fill="#f0f0f0"/>
19039
+ <path d="M974 271v-12a21 21 0 0 1 42 0v12M968 271h54v45h-54z" fill="#050505"/>
19040
+ <circle cx="995" cy="292" r="4" fill="#f0f0f0"/><path d="M995 296v8" stroke="#f0f0f0" stroke-width="3"/>
19041
+ <text x="995" y="332" class="micro ink" text-anchor="middle">auth()</text>
19042
+ ${renderArrow(1047, 296, 1071)}
19043
+ <rect x="1071" y="269" width="63" height="54" fill="#0a0a0a" stroke="#454545"/>
19044
+ <text x="1082" y="292" class="micro bright">ALLOW</text><text x="1082" y="310" class="micro">/app</text>
19045
+ <rect x="819" y="372" width="315" height="38" fill="#090909" stroke="#3d3d3d"/>
19046
+ <rect x="832" y="385" width="11" height="11" fill="#a3a3a3"/>
19047
+ <text x="856" y="395" class="code">session.user / verified</text>
19048
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
19049
+ <text x="819" y="475" class="label bright">SESSION TYPED</text><text x="1134" y="475" class="label" text-anchor="end">CSRF PROTECTED</text>`;
19050
+ }
19051
+ function renderCacheIllustration() {
19052
+ return `${renderPanelHeader("PAGE / PRODUCT", "CACHE + PPR")}
19053
+ <rect x="819" y="204" width="315" height="54" fill="#f0f0f0"/>
19054
+ <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>
19055
+ <rect x="819" y="271" width="198" height="76" fill="#a3a3a3"/>
19056
+ <text x="836" y="301" class="diagram-title ink">CACHED DATA</text><text x="836" y="326" class="micro ink-muted">tag: products</text>
19057
+ <rect x="1029" y="271" width="105" height="76" fill="#0a0a0a" stroke="#555"/>
19058
+ <text x="1045" y="301" class="diagram-title">LIVE</text><text x="1045" y="326" class="micro">stream</text>
19059
+ <path d="M1081 347v43m-8-9 8 9 8-9" fill="none" stroke="#d4d4d4"/>
19060
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
19061
+ <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>`;
19062
+ }
19063
+ function renderIntegrationsIllustration() {
19064
+ return `${renderPanelHeader("INTEGRATION GRAPH", "ONE PRODUCT")}
19065
+ <path d="M976 309H879V247M976 309H879v87M1030 309h93V247M1030 309h93v87" fill="none" stroke="#4a4a4a"/>
19066
+ <rect x="819" y="214" width="120" height="57" fill="#0a0a0a" stroke="#4b4b4b"/><text x="839" y="247" class="diagram-title">BETTER AUTH</text>
19067
+ <rect x="819" y="368" width="120" height="57" fill="#0a0a0a" stroke="#4b4b4b"/><text x="851" y="401" class="diagram-title">PRISMA</text>
19068
+ <rect x="1061" y="214" width="73" height="57" fill="#0a0a0a" stroke="#4b4b4b"/><text x="1077" y="247" class="diagram-title">STRIPE</text>
19069
+ <rect x="1061" y="368" width="73" height="57" fill="#a3a3a3"/><text x="1075" y="401" class="diagram-title ink">VERCEL</text>
19070
+ <rect x="976" y="277" width="94" height="64" fill="#f0f0f0"/>
19071
+ <text x="994" y="304" class="diagram-title ink">FARM.JS</text><text x="994" y="324" class="micro ink-muted">CORE</text>
19072
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
19073
+ <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>`;
19074
+ }
19075
+ function renderCliIllustration() {
19076
+ return `${renderPanelHeader("TERMINAL", "PRODUCTION OUTPUT")}
19077
+ <rect x="819" y="203" width="315" height="213" fill="#050505" stroke="#555"/>
19078
+ <line x1="819" y1="236" x2="1134" y2="236" stroke="#555"/>
19079
+ <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"/>
19080
+ <text x="837" y="273" class="code bright"><tspan fill="#7a7a7a">$</tspan> farm build</text>
19081
+ <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"/>
19082
+ <text x="861" y="303" class="code">64 routes discovered</text>
19083
+ <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"/>
19084
+ <text x="861" y="330" class="code">client + server bundles</text>
19085
+ <rect x="837" y="355" width="126" height="30" fill="#f0f0f0"/><text x="853" y="374" class="micro ink">BUILD COMPLETE</text>
19086
+ <text x="1115" y="374" class="micro" text-anchor="end">/dist</text>
19087
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
19088
+ <text x="819" y="475" class="label bright">REAL COMMANDS</text><text x="1134" y="475" class="label" text-anchor="end">842MS</text>`;
19089
+ }
19090
+ function renderProjectIllustration() {
19091
+ return `${renderPanelHeader("PROJECT MAP", "CONVENTION TYPED")}
19092
+ <rect x="819" y="202" width="315" height="216" fill="#050505" stroke="#555"/>
19093
+ <text x="837" y="227" class="diagram-title bright">my-app/</text>
19094
+ <path d="M847 243v143M847 259h23M847 293h23M847 327h23M847 361h23" fill="none" stroke="#666"/>
19095
+ <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>
19096
+ <rect x="870" y="277" width="246" height="33" fill="#f0f0f0"/><text x="886" y="298" class="code ink">src/app/page.tsx</text>
19097
+ <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>
19098
+ <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>
19099
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
19100
+ <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>`;
19101
+ }
19102
+ function renderRuntimeIllustration() {
19103
+ return `${renderPanelHeader("PRODUCT ARCHITECTURE", "FULL STACK")}
19104
+ <rect x="819" y="211" width="315" height="55" fill="#f0f0f0"/>
19105
+ <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>
19106
+ <path d="M976 266v20m0 61v20" stroke="#727272"/>
19107
+ <rect x="855" y="286" width="242" height="61" fill="#a3a3a3"/>
19108
+ <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>
19109
+ <rect x="819" y="367" width="315" height="55" fill="#4c4c4c"/>
19110
+ <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>
19111
+ <line x1="819" y1="450" x2="1134" y2="450" stroke="#343434"/>
19112
+ <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>`;
19113
+ }
19114
+ function renderIllustration(type) {
19115
+ switch (type) {
19116
+ case "auth":
19117
+ return renderAuthIllustration();
19118
+ case "cache":
19119
+ return renderCacheIllustration();
19120
+ case "cli":
19121
+ return renderCliIllustration();
19122
+ case "integrations":
19123
+ return renderIntegrationsIllustration();
19124
+ case "project":
19125
+ return renderProjectIllustration();
19126
+ case "routing":
19127
+ return renderRoutingIllustration();
19128
+ default:
19129
+ return renderRuntimeIllustration();
19130
+ }
19131
+ }
19132
+ function renderFarmDocsSocialImageSvg(descriptor) {
19133
+ const titleLines = wrapText(descriptor.title, 22, 2);
19134
+ const longestTitleLine = Math.max(...titleLines.map((line) => line.length));
19135
+ const titleSize = titleLines.length === 1 ? longestTitleLine > 17 ? 62 : 74 : 56;
19136
+ const titleLineHeight = Math.round(titleSize * 0.98);
19137
+ const titleY = titleLines.length === 1 ? 294 : 266;
19138
+ const descriptionY = titleY + (titleLines.length - 1) * titleLineHeight + 59;
19139
+ const descriptionLines = wrapText(descriptor.description, 68, 2);
19140
+ const routeY = Math.max(418, descriptionY + descriptionLines.length * 29 + 24);
19141
+ const section = truncate(descriptor.section.toUpperCase(), 27);
19142
+ const route = truncate(descriptor.pageHref, 42);
19143
+ const footerUrl = truncate(descriptor.pageUrl.replace(/^https?:\/\//, ""), 70);
19144
+ const brand = truncate(descriptor.brand.toUpperCase(), 25);
19145
+ const titleId = `farm-docs-og-${descriptor.hash}`;
19146
+ return `<?xml version="1.0" encoding="UTF-8"?>
19147
+ <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">
19148
+ <title id="${titleId}-title">${escapeXml(descriptor.imageAlt)}</title>
19149
+ <desc id="${titleId}-description">${escapeXml(descriptor.description)}</desc>
19150
+ <metadata>${escapeXml(JSON.stringify({ generator: "Farm.js docs", page: descriptor.pageUrl, illustration: descriptor.illustration }))}</metadata>
19151
+ <style type="text/css">
19152
+ ${renderEmbeddedFont("Farm Sans", descriptor.fonts?.sans, "100 900")}
19153
+ ${renderEmbeddedFont("Farm Mono", descriptor.fonts?.mono, "100 900")}
19154
+ ${renderEmbeddedFont("Farm Pixel", descriptor.fonts?.display, "400 700")}
19155
+ .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}
19156
+ </style>
19157
+ <defs>
19158
+ <pattern id="canvas-grid" width="40" height="40" patternUnits="userSpaceOnUse"><path d="M40 0H0V40" fill="none" stroke="#151515"/></pattern>
19159
+ <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>
19160
+ <clipPath id="frame-clip"><rect x="28" y="28" width="1144" height="574"/></clipPath>
19161
+ </defs>
19162
+ <rect width="1200" height="630" fill="#050505"/>
19163
+ <rect width="1200" height="630" fill="url(#canvas-grid)"/>
19164
+ <g clip-path="url(#frame-clip)">
19165
+ <rect x="28" y="28" width="1144" height="574" fill="#020202"/>
19166
+ <rect x="782" y="114" width="390" height="411" fill="url(#diagonal)"/>
19167
+ </g>
19168
+ <rect x="28.5" y="28.5" width="1143" height="573" fill="none" stroke="#323232"/>
19169
+ <path d="M28 114.5h1144M28 525.5h1144M781.5 114v411" fill="none" stroke="#303030"/>
19170
+
19171
+ <g transform="translate(71 51)">
19172
+ <rect width="11" height="11" rx="1" fill="#f5f5f4"/><rect x="15" width="28" height="11" rx="1" fill="#f5f5f4"/>
19173
+ <rect y="15" width="11" height="11" rx="1" fill="#9b9b9b"/><rect x="15" y="15" width="28" height="11" rx="1" fill="#9b9b9b"/>
19174
+ <rect y="30" width="11" height="11" rx="1" fill="#5f5f5f"/><rect x="15" y="30" width="20" height="11" rx="1" fill="#5f5f5f"/>
19175
+ <text x="61" y="24" class="mono bright" font-size="24" font-weight="550">FARM<tspan fill="#777">.JS</tspan></text>
19176
+ <text x="61" y="43" class="label">BY FARMING LABS</text>
19177
+ </g>
19178
+ <text x="1134" y="72" class="label" text-anchor="end">${escapeXml(brand)} \xB7 DOCUMENTATION</text>
19179
+
19180
+ <text x="71" y="198" class="mono bright" font-size="13" font-weight="650">DOCS</text>
19181
+ <line x1="117" y1="193" x2="155" y2="193" stroke="#696969"/>
19182
+ <text x="166" y="198" class="mono" fill="#9b9b9b" font-size="13">${escapeXml(section)}</text>
19183
+ ${renderTextLines(titleLines, 71, titleY, titleLineHeight, `class="pixel bright" font-size="${titleSize}" font-weight="500" letter-spacing="-2.4"`)}
19184
+ ${renderTextLines(descriptionLines, 71, descriptionY, 29, 'class="sans" fill="#a3a3a3" font-size="20" font-weight="430" letter-spacing="-.3"')}
19185
+ <rect x="71" y="${routeY}" width="${Math.min(390, 56 + route.length * 8)}" height="36" fill="#050505" stroke="#3c3c3c"/>
19186
+ <text x="85" y="${routeY + 23}" class="mono bright" font-size="11" font-weight="600">GET</text>
19187
+ <text x="119" y="${routeY + 23}" class="mono" fill="#898989" font-size="11">${escapeXml(route)}</text>
19188
+
19189
+ ${renderIllustration(descriptor.illustration)}
19190
+
19191
+ <text x="71" y="570" class="mono bright" font-size="13" font-weight="550">${escapeXml(footerUrl)}</text>
19192
+ <text x="1134" y="570" class="mono" fill="#9b9b9b" font-size="12" text-anchor="end">FARM.JS DOCUMENTATION</text>
19193
+ <path d="M20 20h10M20 20v10M1180 20h-10M1180 20v10M20 610h10M20 610v-10M1180 610h-10M1180 610v-10" fill="none" stroke="#9b9b9b"/>
19194
+ </svg>`;
19195
+ }
19196
+ function renderFarmDocsSocialMetadata(descriptor, customImage) {
19197
+ const imageUrl = customImage ? new URL(customImage, descriptor.pageUrl).href : descriptor.imageUrl;
19198
+ const customExtension = customImage?.toLowerCase().split(/[?#]/, 1)[0];
19199
+ const imageType = customExtension?.endsWith(".webp") ? "image/webp" : customExtension?.endsWith(".png") ? "image/png" : customExtension?.match(/\.jpe?g$/) ? "image/jpeg" : "image/svg+xml";
19200
+ const tag = /* @__PURE__ */ __name((property, content, name = false) => `<meta ${name ? "name" : "property"}="${property}" content="${escapeXml(content)}">`, "tag");
19201
+ return [
19202
+ tag("og:type", "article"),
19203
+ tag("og:site_name", descriptor.siteName),
19204
+ tag("og:title", descriptor.title),
19205
+ tag("og:description", descriptor.description),
19206
+ tag("og:url", descriptor.pageUrl),
19207
+ tag("og:image", imageUrl),
19208
+ tag("og:image:type", imageType),
19209
+ ...customImage ? [] : [
19210
+ tag("og:image:width", String(FARM_DOCS_SOCIAL_IMAGE_WIDTH)),
19211
+ tag("og:image:height", String(FARM_DOCS_SOCIAL_IMAGE_HEIGHT))
19212
+ ],
19213
+ tag("og:image:alt", descriptor.imageAlt),
19214
+ tag("twitter:card", "summary_large_image", true),
19215
+ tag("twitter:title", descriptor.title, true),
19216
+ tag("twitter:description", descriptor.description, true),
19217
+ tag("twitter:image", imageUrl, true),
19218
+ tag("twitter:image:alt", descriptor.imageAlt, true)
19219
+ ].join("\n ");
19220
+ }
19221
+ var import_node_crypto4, FARM_DOCS_SOCIAL_IMAGE_WIDTH, FARM_DOCS_SOCIAL_IMAGE_HEIGHT, FARM_DOCS_SOCIAL_IMAGE_VERSION;
19222
+ var init_social_image = __esm({
19223
+ "src/docs/social-image.ts"() {
19224
+ "use strict";
19225
+ import_node_crypto4 = require("crypto");
19226
+ FARM_DOCS_SOCIAL_IMAGE_WIDTH = 1200;
19227
+ FARM_DOCS_SOCIAL_IMAGE_HEIGHT = 630;
19228
+ FARM_DOCS_SOCIAL_IMAGE_VERSION = 3;
19229
+ __name(normalizeEntry, "normalizeEntry");
19230
+ __name(encodeSlugPath, "encodeSlugPath");
19231
+ __name(getSocialImageConfig, "getSocialImageConfig");
19232
+ __name(getNavTitle, "getNavTitle");
19233
+ __name(isFalse, "isFalse");
19234
+ __name(isExternalOrRootPath, "isExternalOrRootPath");
19235
+ __name(isFarmDocsSocialImageEnabled, "isFarmDocsSocialImageEnabled");
19236
+ __name(getFarmDocsCustomSocialImage, "getFarmDocsCustomSocialImage");
19237
+ __name(resolveIllustration, "resolveIllustration");
19238
+ __name(createHashValue, "createHashValue");
19239
+ __name(resolveBaseUrl, "resolveBaseUrl");
19240
+ __name(createFarmDocsSocialImageDescriptor, "createFarmDocsSocialImageDescriptor");
19241
+ __name(getFarmDocsSocialImageSlug, "getFarmDocsSocialImageSlug");
19242
+ __name(escapeXml, "escapeXml");
19243
+ __name(truncate, "truncate");
19244
+ __name(wrapText, "wrapText");
19245
+ __name(renderTextLines, "renderTextLines");
19246
+ __name(renderEmbeddedFont, "renderEmbeddedFont");
19247
+ __name(renderArrow, "renderArrow");
19248
+ __name(renderPanelHeader, "renderPanelHeader");
19249
+ __name(renderRoutingIllustration, "renderRoutingIllustration");
19250
+ __name(renderAuthIllustration, "renderAuthIllustration");
19251
+ __name(renderCacheIllustration, "renderCacheIllustration");
19252
+ __name(renderIntegrationsIllustration, "renderIntegrationsIllustration");
19253
+ __name(renderCliIllustration, "renderCliIllustration");
19254
+ __name(renderProjectIllustration, "renderProjectIllustration");
19255
+ __name(renderRuntimeIllustration, "renderRuntimeIllustration");
19256
+ __name(renderIllustration, "renderIllustration");
19257
+ __name(renderFarmDocsSocialImageSvg, "renderFarmDocsSocialImageSvg");
19258
+ __name(renderFarmDocsSocialMetadata, "renderFarmDocsSocialMetadata");
19259
+ }
19260
+ });
19261
+
18131
19262
  // src/docs/handler.ts
18132
19263
  function trimSlashes2(value) {
18133
19264
  return value.replace(/^\/+|\/+$/g, "");
18134
19265
  }
18135
- function normalizeEntry(entry) {
19266
+ function normalizeEntry2(entry) {
18136
19267
  if (!entry || entry === "/") return "/";
18137
19268
  return `/${trimSlashes2(entry)}`;
18138
19269
  }
@@ -18144,10 +19275,10 @@ function resolveFarmDocsFavicon(docs, options) {
18144
19275
  if (typeof configuredFavicon === "string" && configuredFavicon.trim()) {
18145
19276
  return configuredFavicon.trim();
18146
19277
  }
18147
- const publicDir = import_node_path11.default.join(import_node_path11.default.resolve(options.root), "public");
19278
+ const publicDir = import_node_path13.default.join(import_node_path13.default.resolve(options.root), "public");
18148
19279
  for (const fileName of FARM_DOCS_FAVICON_FILES) {
18149
- const faviconPath = import_node_path11.default.join(publicDir, fileName);
18150
- if ((0, import_node_fs8.existsSync)(faviconPath) && (0, import_node_fs8.statSync)(faviconPath).isFile()) {
19280
+ const faviconPath = import_node_path13.default.join(publicDir, fileName);
19281
+ if ((0, import_node_fs9.existsSync)(faviconPath) && (0, import_node_fs9.statSync)(faviconPath).isFile()) {
18151
19282
  return `/${fileName}`;
18152
19283
  }
18153
19284
  }
@@ -18181,10 +19312,10 @@ function isSafeSegment(segment) {
18181
19312
  return segment !== ".." && !segment.includes("/") && !segment.includes("\\");
18182
19313
  }
18183
19314
  function resolveInside(root, target) {
18184
- const resolvedRoot = import_node_path11.default.resolve(root);
18185
- const resolvedTarget = import_node_path11.default.resolve(target);
18186
- const relative4 = import_node_path11.default.relative(resolvedRoot, resolvedTarget);
18187
- if (relative4 === "" || !relative4.startsWith("..") && !import_node_path11.default.isAbsolute(relative4)) {
19315
+ const resolvedRoot = import_node_path13.default.resolve(root);
19316
+ const resolvedTarget = import_node_path13.default.resolve(target);
19317
+ const relative4 = import_node_path13.default.relative(resolvedRoot, resolvedTarget);
19318
+ if (relative4 === "" || !relative4.startsWith("..") && !import_node_path13.default.isAbsolute(relative4)) {
18188
19319
  return resolvedTarget;
18189
19320
  }
18190
19321
  return null;
@@ -18193,36 +19324,36 @@ function isFarmDocsRequest(docs, request) {
18193
19324
  if (!docs?.enabled) return false;
18194
19325
  if (request.method !== "GET" && request.method !== "HEAD") return false;
18195
19326
  const pathname = new URL(request.url).pathname;
18196
- const entry = normalizeEntry(docs.entry);
19327
+ const entry = normalizeEntry2(docs.entry);
18197
19328
  if (entry === "/") return true;
18198
19329
  return pathname === entry || pathname === `${entry}.md` || pathname.startsWith(`${entry}/`);
18199
19330
  }
18200
19331
  function resolveFarmDocsContentDir(docs, options) {
18201
- const root = import_node_path11.default.resolve(options.root);
19332
+ const root = import_node_path13.default.resolve(options.root);
18202
19333
  const srcDir = options.srcDir || "src";
18203
19334
  const configuredContentDir = docs.contentDir || docs.config.contentDir;
18204
19335
  if (configuredContentDir) {
18205
- return import_node_path11.default.isAbsolute(configuredContentDir) ? configuredContentDir : import_node_path11.default.join(root, configuredContentDir);
19336
+ return import_node_path13.default.isAbsolute(configuredContentDir) ? configuredContentDir : import_node_path13.default.join(root, configuredContentDir);
18206
19337
  }
18207
19338
  const entryDir = docs.config.entry || trimSlashes2(docs.entry) || "docs";
18208
- const appDocsDir = import_node_path11.default.join(root, srcDir, "app", entryDir);
18209
- if ((0, import_node_fs8.existsSync)(appDocsDir)) return appDocsDir;
18210
- return import_node_path11.default.join(root, entryDir);
19339
+ const appDocsDir = import_node_path13.default.join(root, srcDir, "app", entryDir);
19340
+ if ((0, import_node_fs9.existsSync)(appDocsDir)) return appDocsDir;
19341
+ return import_node_path13.default.join(root, entryDir);
18211
19342
  }
18212
19343
  function getFarmDocsRouteTypeEntries(docs) {
18213
19344
  if (!docs?.enabled) return [];
18214
- const entry = normalizeEntry(docs.entry);
19345
+ const entry = normalizeEntry2(docs.entry);
18215
19346
  if (entry === "/") return ["/", "/[...docs]"];
18216
19347
  return [entry, `${entry}/[...docs]`];
18217
19348
  }
18218
19349
  function getFarmDocsDocumentNavigationMatchers(docs) {
18219
19350
  if (!docs?.enabled) return [];
18220
- const entry = normalizeEntry(docs.entry);
19351
+ const entry = normalizeEntry2(docs.entry);
18221
19352
  return [entry === "/" ? "/(.*)" : `${entry}(.*)`];
18222
19353
  }
18223
19354
  function getRequestSlug(docs, request) {
18224
19355
  const pathname = new URL(request.url).pathname;
18225
- const entry = normalizeEntry(docs.entry);
19356
+ const entry = normalizeEntry2(docs.entry);
18226
19357
  if (entry === "/") return normalizeSlug(pathname);
18227
19358
  if (pathname === entry || pathname === `${entry}.md`) return "";
18228
19359
  return normalizeSlug(pathname.slice(entry.length));
@@ -18230,14 +19361,14 @@ function getRequestSlug(docs, request) {
18230
19361
  function findDocsPageFile(contentDir, slug) {
18231
19362
  const segments = slug ? slug.split("/").filter(Boolean) : [];
18232
19363
  if (!segments.every(isSafeSegment)) return null;
18233
- const slugDir = import_node_path11.default.join(contentDir, ...segments);
18234
- const candidates = segments.length === 0 ? DOCS_FILE_NAMES.map((filename) => import_node_path11.default.join(contentDir, filename)) : [
18235
- ...DOCS_FILE_NAMES.map((filename) => import_node_path11.default.join(slugDir, filename)),
18236
- ...DOCS_FILE_EXTENSIONS2.map((extension) => import_node_path11.default.join(contentDir, `${slug}${extension}`))
19364
+ const slugDir = import_node_path13.default.join(contentDir, ...segments);
19365
+ const candidates = segments.length === 0 ? DOCS_FILE_NAMES.map((filename) => import_node_path13.default.join(contentDir, filename)) : [
19366
+ ...DOCS_FILE_NAMES.map((filename) => import_node_path13.default.join(slugDir, filename)),
19367
+ ...DOCS_FILE_EXTENSIONS2.map((extension) => import_node_path13.default.join(contentDir, `${slug}${extension}`))
18237
19368
  ];
18238
19369
  for (const candidate of candidates) {
18239
19370
  const safePath = resolveInside(contentDir, candidate);
18240
- if (safePath && (0, import_node_fs8.existsSync)(safePath) && (0, import_node_fs8.statSync)(safePath).isFile()) {
19371
+ if (safePath && (0, import_node_fs9.existsSync)(safePath) && (0, import_node_fs9.statSync)(safePath).isFile()) {
18241
19372
  return safePath;
18242
19373
  }
18243
19374
  }
@@ -18272,7 +19403,7 @@ function titleFromMarkdown2(body, fallback) {
18272
19403
  function loadFarmDocsPage(contentDir, docs, slug) {
18273
19404
  const sourcePath = findDocsPageFile(contentDir, slug);
18274
19405
  if (!sourcePath) return null;
18275
- const source = (0, import_node_fs8.readFileSync)(sourcePath, "utf8");
19406
+ const source = (0, import_node_fs9.readFileSync)(sourcePath, "utf8");
18276
19407
  const { frontmatter, body } = parseFrontmatter(source);
18277
19408
  const title = frontmatter.title || titleFromMarkdown2(body, titleFromSlug(slug));
18278
19409
  const href = createDocsHref(docs.entry, slug);
@@ -18280,6 +19411,7 @@ function loadFarmDocsPage(contentDir, docs, slug) {
18280
19411
  slug,
18281
19412
  title,
18282
19413
  description: frontmatter.description,
19414
+ section: frontmatter.section,
18283
19415
  href,
18284
19416
  sourcePath,
18285
19417
  lastModified: resolveFarmDocsPageLastModified(contentDir, sourcePath, frontmatter),
@@ -18288,15 +19420,15 @@ function loadFarmDocsPage(contentDir, docs, slug) {
18288
19420
  };
18289
19421
  }
18290
19422
  function createDocsHref(entry, slug) {
18291
- const normalizedEntry = normalizeEntry(entry);
19423
+ const normalizedEntry = normalizeEntry2(entry);
18292
19424
  const normalizedSlug = trimSlashes2(slug);
18293
19425
  if (normalizedEntry === "/") return normalizedSlug ? `/${normalizedSlug}` : "/";
18294
19426
  return normalizedSlug ? `${normalizedEntry}/${normalizedSlug}` : normalizedEntry;
18295
19427
  }
18296
19428
  function pathToSlug(contentDir, filePath) {
18297
- const relative4 = import_node_path11.default.relative(contentDir, filePath).replace(/\\/g, "/");
19429
+ const relative4 = import_node_path13.default.relative(contentDir, filePath).replace(/\\/g, "/");
18298
19430
  if (relative4.startsWith("..")) return null;
18299
- const extension = import_node_path11.default.extname(relative4);
19431
+ const extension = import_node_path13.default.extname(relative4);
18300
19432
  if (!DOCS_FILE_EXTENSIONS2.includes(extension)) return null;
18301
19433
  const withoutExtension = relative4.slice(0, -extension.length);
18302
19434
  if (withoutExtension === "page" || withoutExtension === "index") return "";
@@ -18309,12 +19441,12 @@ function loadPage(contentDir, docs, request) {
18309
19441
  return loadFarmDocsPage(contentDir, docs, getRequestSlug(docs, request));
18310
19442
  }
18311
19443
  function discoverFarmDocsPages(contentDir, docs) {
18312
- if (!(0, import_node_fs8.existsSync)(contentDir)) return [];
19444
+ if (!(0, import_node_fs9.existsSync)(contentDir)) return [];
18313
19445
  const pages = [];
18314
19446
  const visit = /* @__PURE__ */ __name((dir) => {
18315
- for (const entry of (0, import_node_fs8.readdirSync)(dir, { withFileTypes: true })) {
19447
+ for (const entry of (0, import_node_fs9.readdirSync)(dir, { withFileTypes: true })) {
18316
19448
  if (entry.name.startsWith(".") || entry.name === "node_modules") continue;
18317
- const absolutePath = import_node_path11.default.join(dir, entry.name);
19449
+ const absolutePath = import_node_path13.default.join(dir, entry.name);
18318
19450
  if (entry.isDirectory()) {
18319
19451
  visit(absolutePath);
18320
19452
  continue;
@@ -18322,7 +19454,7 @@ function discoverFarmDocsPages(contentDir, docs) {
18322
19454
  if (!entry.isFile()) continue;
18323
19455
  const slug = pathToSlug(contentDir, absolutePath);
18324
19456
  if (slug === null) continue;
18325
- const source = (0, import_node_fs8.readFileSync)(absolutePath, "utf8");
19457
+ const source = (0, import_node_fs9.readFileSync)(absolutePath, "utf8");
18326
19458
  const { frontmatter, body } = parseFrontmatter(source);
18327
19459
  pages.push({
18328
19460
  slug,
@@ -19100,7 +20232,7 @@ function renderPixelBreadcrumb(page2, docs) {
19100
20232
  const parentSegments = segments.slice(0, -1);
19101
20233
  const parentSegment = parentSegments[parentSegments.length - 1];
19102
20234
  const currentSegment = segments[segments.length - 1];
19103
- const entry = normalizeEntry(docs.entry);
20235
+ const entry = normalizeEntry2(docs.entry);
19104
20236
  const parentPath = parentSegments.join("/");
19105
20237
  const parentHref = entry === "/" ? `/${parentPath}` : `${entry}/${parentPath}`;
19106
20238
  return `<nav class="fd-breadcrumb" aria-label="Breadcrumb">
@@ -19138,7 +20270,7 @@ ${items.map(
19138
20270
  </div>`;
19139
20271
  }
19140
20272
  function renderDocsRuntimeScript(docs) {
19141
- const docsEntry = JSON.stringify(normalizeEntry(docs.entry));
20273
+ const docsEntry = JSON.stringify(normalizeEntry2(docs.entry));
19142
20274
  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>`;
19143
20275
  }
19144
20276
  function renderDocsPageActionsRuntimeScript() {
@@ -19153,13 +20285,13 @@ ${renderDocsHashRuntimeScript()}
19153
20285
  ${renderDocsPageActionsRuntimeScript()}`;
19154
20286
  }
19155
20287
  function readCssWithImports(filePath, seen = /* @__PURE__ */ new Set()) {
19156
- const resolved = import_node_path11.default.resolve(filePath);
20288
+ const resolved = import_node_path13.default.resolve(filePath);
19157
20289
  if (seen.has(resolved)) return "";
19158
20290
  seen.add(resolved);
19159
- const source = (0, import_node_fs8.readFileSync)(resolved, "utf8");
20291
+ const source = (0, import_node_fs9.readFileSync)(resolved, "utf8");
19160
20292
  return source.replace(/@import\s+["']([^"']+)["'];?/g, (_match, importPath) => {
19161
20293
  try {
19162
- const importedPath = importPath.startsWith(".") ? import_node_path11.default.resolve(import_node_path11.default.dirname(resolved), importPath) : (0, import_node_module5.createRequire)(resolved).resolve(importPath);
20294
+ const importedPath = importPath.startsWith(".") ? import_node_path13.default.resolve(import_node_path13.default.dirname(resolved), importPath) : (0, import_node_module5.createRequire)(resolved).resolve(importPath);
19163
20295
  return readCssWithImports(importedPath, seen);
19164
20296
  } catch {
19165
20297
  return "";
@@ -19167,11 +20299,11 @@ function readCssWithImports(filePath, seen = /* @__PURE__ */ new Set()) {
19167
20299
  });
19168
20300
  }
19169
20301
  function resolvePixelBorderThemeCss(options) {
19170
- const cacheKey = import_node_path11.default.resolve(options.root);
20302
+ const cacheKey = import_node_path13.default.resolve(options.root);
19171
20303
  const cached = themeCssCache.get(cacheKey);
19172
20304
  if (cached !== void 0) return cached;
19173
20305
  try {
19174
- const requireFromApp = (0, import_node_module5.createRequire)(import_node_path11.default.join(cacheKey, "package.json"));
20306
+ const requireFromApp = (0, import_node_module5.createRequire)(import_node_path13.default.join(cacheKey, "package.json"));
19175
20307
  const themeCssPath = requireFromApp.resolve("@farming-labs/theme/pixel-border/css");
19176
20308
  const requireFromTheme = (0, import_node_module5.createRequire)(themeCssPath);
19177
20309
  const fumadocsCssPath = requireFromTheme.resolve("fumadocs-ui/style.css");
@@ -19184,17 +20316,28 @@ ${readCssWithImports(themeCssPath)}`;
19184
20316
  return "";
19185
20317
  }
19186
20318
  }
19187
- function renderFarmDocsBridgeCss(docs) {
20319
+ function renderFarmDocsBridgeCss(docs, fontAssets, layoutFonts) {
19188
20320
  const sidebarWidth = getThemeLayoutValue(docs, "sidebarWidth", 320);
19189
20321
  const contentWidth = getThemeLayoutValue(docs, "contentWidth", 860);
20322
+ const fontFaces = fontAssets.map(
20323
+ ({ family, url }) => `@font-face { font-family: "${family}"; src: url("${url}") format("woff2"); font-style: normal; font-weight: 100 900; font-display: block; }`
20324
+ ).join("\n");
20325
+ const fontVariables = [
20326
+ fontAssets.some(({ family }) => family === "Geist Sans") ? '--font-geist-sans: "Geist Sans", ui-sans-serif, system-ui, sans-serif;' : "",
20327
+ fontAssets.some(({ family }) => family === "Geist Mono") ? '--font-geist-mono: "Geist Mono", ui-monospace, monospace;' : ""
20328
+ ].filter(Boolean).join(" ");
20329
+ const fontCss = fontFaces ? `${fontFaces}
20330
+ :root { ${fontVariables} }` : "";
20331
+ const layoutFontVariables = [
20332
+ renderFarmDocsLayoutFontVariable("--fd-layout-font-body", layoutFonts?.body),
20333
+ renderFarmDocsLayoutFontVariable("--fd-layout-font-code", layoutFonts?.code)
20334
+ ].filter(Boolean).join(" ");
19190
20335
  return `
19191
- @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; }
19192
- @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; }
19193
- :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); }
20336
+ :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); }
19194
20337
  * { box-sizing: border-box; }
19195
20338
  html { background: var(--color-fd-background, hsl(0 0% 2%)); scroll-padding-top: calc(var(--fd-nav-height, 44px) + 24px); }
19196
20339
  html[data-farm-docs-sidebar="open"], html[data-farm-docs-sidebar="open"] body { overflow: hidden; }
19197
- 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; }
20340
+ 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; }
19198
20341
  ::selection { background: var(--color-fd-foreground, #fff); color: var(--color-fd-background, #000); }
19199
20342
  a { color: inherit; }
19200
20343
  #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%)); }
@@ -19374,14 +20517,45 @@ function renderFarmDocsBridgeCss(docs) {
19374
20517
  .sh__line { min-height: 1.6em; }
19375
20518
  }
19376
20519
  ${farmDocsPixelBorderCss}
20520
+ ${fontCss}
19377
20521
  `;
19378
20522
  }
19379
- function renderPixelDocsHtml(page2, pages, docs, themeCss, clientEntry, faviconHref) {
20523
+ function renderFarmDocsLayoutFontVariable(name, font) {
20524
+ const family = font?.style.fontFamily;
20525
+ if (!family || /[;{}<>]/.test(family)) return "";
20526
+ return `${name}: ${family};`;
20527
+ }
20528
+ function renderFarmDocsFontPreloads(fontAssets) {
20529
+ return fontAssets.map(
20530
+ ({ url }) => `<link rel="preload" href="${escapeAttribute2(url)}" as="font" type="font/woff2" crossorigin>`
20531
+ ).join("\n ");
20532
+ }
20533
+ function renderFarmDocsFontPreloadHeader(fontAssets) {
20534
+ return fontAssets.map(({ url }) => `<${url}>; rel=preload; as=font; type=font/woff2; crossorigin`).join(", ");
20535
+ }
20536
+ function getFarmLayoutFontPreloads(layoutFonts) {
20537
+ const seen = /* @__PURE__ */ new Set();
20538
+ return [layoutFonts?.body, layoutFonts?.code].flatMap(
20539
+ (font) => (font?.preloads || []).filter(({ href }) => {
20540
+ if (seen.has(href)) return false;
20541
+ seen.add(href);
20542
+ return true;
20543
+ })
20544
+ );
20545
+ }
20546
+ function renderFarmLayoutFontPreloadHeader(layoutFonts) {
20547
+ return getFarmLayoutFontPreloads(layoutFonts).map(({ href, type }) => `<${href}>; rel=preload; as=font; type=${type}; crossorigin`).join(", ");
20548
+ }
20549
+ function renderPixelDocsHtml(page2, pages, docs, themeCss, clientEntry, faviconHref, requestUrl, fontAssets, layoutFonts, fontStylesheetHref) {
19380
20550
  const navTitle = typeof docs.config.nav === "object" && docs.config.nav && "title" in docs.config.nav ? String(docs.config.nav.title || "Docs") : "Docs";
19381
20551
  const description = page2.description || docs.config.metadata?.description || "";
19382
20552
  const tocItems = extractTocItems(page2.body, getThemeTocDepth(docs));
19383
20553
  const themeName = getThemeName(docs);
19384
20554
  const searchEnabled = isFarmDocsSearchEnabled(docs);
20555
+ const socialMetadata = isFarmDocsSocialImageEnabled(page2, docs) ? renderFarmDocsSocialMetadata(
20556
+ createFarmDocsSocialImageDescriptor(page2, docs, requestUrl),
20557
+ getFarmDocsCustomSocialImage(page2)
20558
+ ) : "";
19385
20559
  return `<!DOCTYPE html>
19386
20560
  <html class="dark" lang="en" data-docs-theme="${escapeAttribute2(themeName)}">
19387
20561
  <head>
@@ -19390,11 +20564,12 @@ function renderPixelDocsHtml(page2, pages, docs, themeCss, clientEntry, faviconH
19390
20564
  <meta name="generator" content="@farming-labs/docs via Farm.js">
19391
20565
  <title>${escapeHtml(page2.title)}</title>
19392
20566
  ${renderFarmDocsFaviconLink(faviconHref)}
19393
- <link rel="preload" href="${GEIST_SANS_FONT_URL}" as="font" type="font/woff2" crossorigin>
19394
- <link rel="preload" href="${GEIST_MONO_FONT_URL}" as="font" type="font/woff2" crossorigin>
20567
+ ${renderFarmDocsFontPreloads(fontAssets)}
20568
+ ${fontStylesheetHref ? `<link rel="stylesheet" href="${escapeAttribute2(fontStylesheetHref)}">` : ""}
19395
20569
  ${description ? `<meta name="description" content="${escapeHtml(description)}">` : ""}
20570
+ ${socialMetadata}
19396
20571
  <style>${themeCss}
19397
- ${renderFarmDocsBridgeCss(docs)}</style>
20572
+ ${renderFarmDocsBridgeCss(docs, fontAssets, layoutFonts)}</style>
19398
20573
  ${searchEnabled ? renderDocsSearchBootstrapScript() : ""}
19399
20574
  </head>
19400
20575
  <body>
@@ -19446,9 +20621,34 @@ ${renderMarkdownHtmlWithTitleMeta(page2, docs)}
19446
20621
  }
19447
20622
  function createFarmDocsHandler(docs, options) {
19448
20623
  const faviconHref = resolveFarmDocsFavicon(docs, options);
20624
+ const fontAssets = options.fontAssets ?? toFarmDocsPublicFontAssets(resolveFarmDocsFontAssets(options.root));
20625
+ const fallbackFontPreloadHeader = renderFarmDocsFontPreloadHeader(fontAssets);
19449
20626
  return /* @__PURE__ */ __name(async function handleFarmDocsRequest(request) {
19450
20627
  if (!docs?.enabled || request.method !== "GET" && request.method !== "HEAD") return null;
19451
20628
  const contentDir = resolveFarmDocsContentDir(docs, options);
20629
+ const requestUrl = new URL(request.url);
20630
+ const socialImageSlug = getFarmDocsSocialImageSlug(docs, requestUrl);
20631
+ if (socialImageSlug !== null) {
20632
+ const socialImagePage = loadFarmDocsPage(contentDir, docs, socialImageSlug);
20633
+ if (!socialImagePage || !isFarmDocsSocialImageEnabled(socialImagePage, docs) || getFarmDocsCustomSocialImage(socialImagePage)) {
20634
+ return null;
20635
+ }
20636
+ const descriptor = createFarmDocsSocialImageDescriptor(socialImagePage, docs, requestUrl);
20637
+ if (requestUrl.pathname !== descriptor.imagePath) return null;
20638
+ const etag = `"${descriptor.hash}"`;
20639
+ const immutable = requestUrl.searchParams.get("v") === descriptor.hash;
20640
+ const headers = new Headers({
20641
+ "Content-Type": "image/svg+xml; charset=utf-8",
20642
+ "Cache-Control": immutable ? "public, max-age=31536000, immutable" : "public, max-age=0, must-revalidate",
20643
+ ETag: etag,
20644
+ "X-Content-Type-Options": "nosniff"
20645
+ });
20646
+ if (request.headers.get("if-none-match") === etag) {
20647
+ return new Response(null, { status: 304, headers });
20648
+ }
20649
+ if (request.method === "HEAD") return new Response(null, { status: 200, headers });
20650
+ return new Response(renderFarmDocsSocialImageSvg(descriptor), { status: 200, headers });
20651
+ }
19452
20652
  const publicResponse = createFarmDocsPublicResponse(contentDir, docs, request);
19453
20653
  if (publicResponse) return publicResponse;
19454
20654
  if (!isFarmDocsRequest(docs, request)) return null;
@@ -19471,6 +20671,11 @@ function createFarmDocsHandler(docs, options) {
19471
20671
  }
19472
20672
  );
19473
20673
  }
20674
+ const pathname = new URL(request.url).pathname;
20675
+ const layoutFonts = await options.resolveLayoutFonts?.(pathname);
20676
+ const usesLayoutFonts = Boolean(layoutFonts?.body || layoutFonts?.code);
20677
+ const activeFontAssets = usesLayoutFonts ? [] : fontAssets;
20678
+ const fontPreloadHeader = usesLayoutFonts ? renderFarmLayoutFontPreloadHeader(layoutFonts) : fallbackFontPreloadHeader;
19474
20679
  return new Response(
19475
20680
  renderPixelDocsHtml(
19476
20681
  page2,
@@ -19478,39 +20683,44 @@ function createFarmDocsHandler(docs, options) {
19478
20683
  docs,
19479
20684
  resolvePixelBorderThemeCss(options),
19480
20685
  options.clientEntry || "/farm-client.js",
19481
- faviconHref
20686
+ faviconHref,
20687
+ requestUrl,
20688
+ activeFontAssets,
20689
+ layoutFonts,
20690
+ usesLayoutFonts ? options.fontStylesheetHref : void 0
19482
20691
  ),
19483
20692
  {
19484
20693
  status: 200,
19485
20694
  headers: {
19486
20695
  "Content-Type": "text/html; charset=utf-8",
19487
- "Cache-Control": "public, s-maxage=60, stale-while-revalidate=300"
20696
+ "Cache-Control": "public, s-maxage=60, stale-while-revalidate=300",
20697
+ ...fontPreloadHeader ? { Link: fontPreloadHeader } : {}
19488
20698
  }
19489
20699
  }
19490
20700
  );
19491
20701
  }, "handleFarmDocsRequest");
19492
20702
  }
19493
- 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;
20703
+ 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;
19494
20704
  var init_handler = __esm({
19495
20705
  "src/docs/handler.ts"() {
19496
20706
  "use strict";
19497
- import_node_fs8 = require("fs");
20707
+ import_node_fs9 = require("fs");
19498
20708
  import_node_module5 = require("module");
19499
- import_node_path11 = __toESM(require("path"));
20709
+ import_node_path13 = __toESM(require("path"));
19500
20710
  import_docs = require("@farming-labs/docs");
19501
20711
  import_marked = require("marked");
19502
20712
  import_sugar_high = require("sugar-high");
20713
+ init_fonts();
19503
20714
  init_last_modified();
19504
20715
  init_pixel_border_css();
19505
20716
  init_search_client();
20717
+ init_social_image();
19506
20718
  DOCS_FILE_NAMES = ["page.mdx", "page.md", "index.mdx", "index.md"];
19507
20719
  DOCS_FILE_EXTENSIONS2 = [".mdx", ".md"];
19508
20720
  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";
19509
20721
  FARM_DOCS_FAVICON_FILES = ["favicon.svg", "favicon.ico", "favicon.png"];
19510
- GEIST_SANS_FONT_URL = "/assets/Geist-Variable-CrgPqtmy.woff2";
19511
- GEIST_MONO_FONT_URL = "/assets/GeistMono-Variable-BNLlm6Cd.woff2";
19512
20722
  __name(trimSlashes2, "trimSlashes");
19513
- __name(normalizeEntry, "normalizeEntry");
20723
+ __name(normalizeEntry2, "normalizeEntry");
19514
20724
  __name(normalizeSlug, "normalizeSlug");
19515
20725
  __name(resolveFarmDocsFavicon, "resolveFarmDocsFavicon");
19516
20726
  __name(renderFarmDocsFaviconLink, "renderFarmDocsFaviconLink");
@@ -19700,6 +20910,11 @@ var init_handler = __esm({
19700
20910
  __name(readCssWithImports, "readCssWithImports");
19701
20911
  __name(resolvePixelBorderThemeCss, "resolvePixelBorderThemeCss");
19702
20912
  __name(renderFarmDocsBridgeCss, "renderFarmDocsBridgeCss");
20913
+ __name(renderFarmDocsLayoutFontVariable, "renderFarmDocsLayoutFontVariable");
20914
+ __name(renderFarmDocsFontPreloads, "renderFarmDocsFontPreloads");
20915
+ __name(renderFarmDocsFontPreloadHeader, "renderFarmDocsFontPreloadHeader");
20916
+ __name(getFarmLayoutFontPreloads, "getFarmLayoutFontPreloads");
20917
+ __name(renderFarmLayoutFontPreloadHeader, "renderFarmLayoutFontPreloadHeader");
19703
20918
  __name(renderPixelDocsHtml, "renderPixelDocsHtml");
19704
20919
  __name(createFarmDocsHandler, "createFarmDocsHandler");
19705
20920
  }
@@ -20468,8 +21683,8 @@ var init_generator = __esm({
20468
21683
  }
20469
21684
  routeGroups.get(key).push(route);
20470
21685
  }
20471
- for (const [path29, routeList] of routeGroups) {
20472
- const openAPIPath = this.convertToOpenAPIPath(path29);
21686
+ for (const [path31, routeList] of routeGroups) {
21687
+ const openAPIPath = this.convertToOpenAPIPath(path31);
20473
21688
  spec.paths[openAPIPath] = {};
20474
21689
  for (const route of routeList) {
20475
21690
  for (const method of route.methods) {
@@ -20483,8 +21698,8 @@ var init_generator = __esm({
20483
21698
  /**
20484
21699
  * Convert Farm.js API path to OpenAPI path format
20485
21700
  */
20486
- convertToOpenAPIPath(path29) {
20487
- return path29.replace(/^\/api/, "");
21701
+ convertToOpenAPIPath(path31) {
21702
+ return path31.replace(/^\/api/, "");
20488
21703
  }
20489
21704
  /**
20490
21705
  * Extract request body schema from endpoint
@@ -20646,8 +21861,8 @@ var init_generator = __esm({
20646
21861
  /**
20647
21862
  * Generate operation summary
20648
21863
  */
20649
- generateSummary(path29, method) {
20650
- const cleanPath = path29.replace(/^\/api\//, "");
21864
+ generateSummary(path31, method) {
21865
+ const cleanPath = path31.replace(/^\/api\//, "");
20651
21866
  const pathParts = cleanPath.split("/");
20652
21867
  const lastPart = pathParts[pathParts.length - 1];
20653
21868
  const action = method === "GET" ? "Get" : method === "POST" ? "Create" : method === "PUT" ? "Update" : method === "DELETE" ? "Delete" : method === "PATCH" ? "Update" : method;
@@ -20656,22 +21871,22 @@ var init_generator = __esm({
20656
21871
  /**
20657
21872
  * Generate operation description
20658
21873
  */
20659
- generateDescription(path29, method) {
20660
- const cleanPath = path29.replace(/^\/api\//, "");
21874
+ generateDescription(path31, method) {
21875
+ const cleanPath = path31.replace(/^\/api\//, "");
20661
21876
  return `${method} ${cleanPath} endpoint`;
20662
21877
  }
20663
21878
  /**
20664
21879
  * Generate operation ID
20665
21880
  */
20666
- generateOperationId(path29, method) {
20667
- const cleanPath = path29.replace(/^\/api\//, "").replace(/\//g, "_");
21881
+ generateOperationId(path31, method) {
21882
+ const cleanPath = path31.replace(/^\/api\//, "").replace(/\//g, "_");
20668
21883
  return `${method.toLowerCase()}_${cleanPath}`;
20669
21884
  }
20670
21885
  /**
20671
21886
  * Generate tags for grouping operations
20672
21887
  */
20673
- generateTags(path29) {
20674
- const cleanPath = path29.replace(/^\/api\//, "");
21888
+ generateTags(path31) {
21889
+ const cleanPath = path31.replace(/^\/api\//, "");
20675
21890
  const pathParts = cleanPath.split("/");
20676
21891
  if (pathParts.length > 1) {
20677
21892
  return [pathParts[0]];
@@ -20684,8 +21899,8 @@ var init_generator = __esm({
20684
21899
  async generateSpecFile(routes, outputPath) {
20685
21900
  const spec = await this.generateSpec(routes);
20686
21901
  const fs8 = require("fs");
20687
- const path29 = require("path");
20688
- const dir = path29.dirname(outputPath);
21902
+ const path31 = require("path");
21903
+ const dir = path31.dirname(outputPath);
20689
21904
  if (!fs8.existsSync(dir)) {
20690
21905
  fs8.mkdirSync(dir, { recursive: true });
20691
21906
  }
@@ -21105,7 +22320,7 @@ async function createFarmDevtoolsSnapshot(input) {
21105
22320
  generatedAt: (input.now?.() || /* @__PURE__ */ new Date()).toISOString(),
21106
22321
  health: resolveHealth(diagnostics),
21107
22322
  project: {
21108
- name: import_node_path12.default.basename(import_node_path12.default.resolve(input.root)),
22323
+ name: import_node_path14.default.basename(import_node_path14.default.resolve(input.root)),
21109
22324
  root: input.root,
21110
22325
  srcDir: input.srcDir,
21111
22326
  basePath: config.basePath || "/",
@@ -21154,11 +22369,11 @@ async function createFarmDevtoolsSnapshot(input) {
21154
22369
  diagnostics
21155
22370
  };
21156
22371
  }
21157
- var import_node_path12;
22372
+ var import_node_path14;
21158
22373
  var init_devtools = __esm({
21159
22374
  "src/devtools.ts"() {
21160
22375
  "use strict";
21161
- import_node_path12 = __toESM(require("path"));
22376
+ import_node_path14 = __toESM(require("path"));
21162
22377
  init_config2();
21163
22378
  init_cron();
21164
22379
  init_route_runtime();
@@ -22982,7 +24197,7 @@ function fileContainsProgrammaticPageRoute(file) {
22982
24197
  }
22983
24198
  function rewriteEarlySsrRelativeImports(options) {
22984
24199
  const cleanId = options.id.split("?", 1)[0];
22985
- if (!path25.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
24200
+ if (!path27.isAbsolute(cleanId) || cleanId.replace(/\\/g, "/").includes("/node_modules/")) {
22986
24201
  return null;
22987
24202
  }
22988
24203
  const replacements = [];
@@ -23002,7 +24217,7 @@ function rewriteEarlySsrRelativeImports(options) {
23002
24217
  const suffixIndex = source.value.search(/[?#]/);
23003
24218
  const sourcePath = suffixIndex === -1 ? source.value : source.value.slice(0, suffixIndex);
23004
24219
  const suffix = suffixIndex === -1 ? "" : source.value.slice(suffixIndex);
23005
- const resolvedPath = path25.resolve(path25.dirname(cleanId), sourcePath);
24220
+ const resolvedPath = path27.resolve(path27.dirname(cleanId), sourcePath);
23006
24221
  replacements.push({
23007
24222
  start: source.start,
23008
24223
  end: source.end,
@@ -23083,7 +24298,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23083
24298
  ...layerRoots.length ? {
23084
24299
  server: {
23085
24300
  fs: {
23086
- allow: [path25.resolve(options.root || process.cwd()), ...layerRoots]
24301
+ allow: [path27.resolve(options.root || process.cwd()), ...layerRoots]
23087
24302
  }
23088
24303
  }
23089
24304
  } : {}
@@ -23114,9 +24329,9 @@ function farmPlugin(options = {}, initialPluginManager) {
23114
24329
  },
23115
24330
  server
23116
24331
  );
23117
- const globalsCSSPath = path25.join(server.config.root, "src/app/globals.css");
24332
+ const globalsCSSPath = path27.join(server.config.root, "src/app/globals.css");
23118
24333
  if (!fs7.existsSync(globalsCSSPath)) {
23119
- const appDir = path25.join(server.config.root, "src/app");
24334
+ const appDir = path27.join(server.config.root, "src/app");
23120
24335
  if (!fs7.existsSync(appDir)) {
23121
24336
  fs7.mkdirSync(appDir, { recursive: true });
23122
24337
  }
@@ -23138,10 +24353,10 @@ function farmPlugin(options = {}, initialPluginManager) {
23138
24353
  });
23139
24354
  }
23140
24355
  const sourceRoots = getFarmSourceRoots(farmConfig);
23141
- server.watcher.add(sourceRoots.map((source) => path25.join(source.root, source.srcDir)));
24356
+ server.watcher.add(sourceRoots.map((source) => path27.join(source.root, source.srcDir)));
23142
24357
  if (farmConfig.i18n.enabled) {
23143
24358
  server.watcher.add(
23144
- farmConfig.i18n.messages.includes("{locale}") ? path25.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
24359
+ farmConfig.i18n.messages.includes("{locale}") ? path27.dirname(farmConfig.i18n.messages) : farmConfig.i18n.messages
23145
24360
  );
23146
24361
  }
23147
24362
  const workflowConfig = farmConfig.workflows;
@@ -23151,7 +24366,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23151
24366
  ...farmDocsDevRuntime?.getFarmDocsRouteTypeEntries(farmConfig.docs) ?? []
23152
24367
  ], "getExtraRouteTypes");
23153
24368
  const appDirSlugs = sourceRoots.map(
23154
- (source) => path25.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
24369
+ (source) => path27.join(source.root, source.srcDir, "app").replace(/\\/g, "/")
23155
24370
  );
23156
24371
  const generateTypeArtifacts = /* @__PURE__ */ __name(async (reason, selection = ALL_TYPE_ARTIFACTS, log = false) => {
23157
24372
  try {
@@ -23191,7 +24406,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23191
24406
  }, "generateTypeArtifacts");
23192
24407
  await generateTypeArtifacts("startup");
23193
24408
  const srcDirSlugs = sourceRoots.map(
23194
- (source) => path25.join(source.root, source.srcDir).replace(/\\/g, "/")
24409
+ (source) => path27.join(source.root, source.srcDir).replace(/\\/g, "/")
23195
24410
  );
23196
24411
  const layerConfigFiles = new Set(
23197
24412
  farmConfig.layers.map((layer) => layer.configFile?.replace(/\\/g, "/")).filter((file) => Boolean(file))
@@ -23309,7 +24524,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23309
24524
  await apiRouteManager.discoverRoutes();
23310
24525
  let discoveredWorkflows = [];
23311
24526
  const hasWorkflowDirectory = workflowConfig.enabled && workflowConfig.dirs.some(
23312
- (dir) => fs7.existsSync(path25.isAbsolute(dir) ? dir : path25.join(farmConfig.root, dir))
24527
+ (dir) => fs7.existsSync(path27.isAbsolute(dir) ? dir : path27.join(farmConfig.root, dir))
23313
24528
  );
23314
24529
  if (hasWorkflowDirectory) {
23315
24530
  const { createFarmWorkflowRequestHandler: createFarmWorkflowRequestHandler2, discoverFarmWorkflows: discoverFarmWorkflows2 } = await loadFarmWorkflowsDevRuntime();
@@ -23373,10 +24588,20 @@ function farmPlugin(options = {}, initialPluginManager) {
23373
24588
  logger.info(`Refreshed routes: ${reason}`);
23374
24589
  }
23375
24590
  }, "refreshRouteDiscovery");
24591
+ const farmDocsFontAssetList = farmDocsDevRuntime ? resolveFarmDocsFontAssets(farmConfig.root) : [];
23376
24592
  const farmDocsHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsHandler(farmConfig.docs, {
23377
24593
  root: farmConfig.root,
23378
24594
  srcDir: farmConfig.srcDir,
23379
- clientEntry: "/@farm/client.js"
24595
+ clientEntry: "/@farm/client.js",
24596
+ fontAssets: toFarmDocsPublicFontAssets(farmDocsFontAssetList),
24597
+ resolveLayoutFonts: /* @__PURE__ */ __name(async (pathname) => {
24598
+ const layouts = routeManager.matchRoute(pathname).layouts;
24599
+ const layoutModules = await Promise.all(
24600
+ layouts.map((layout2) => routeManager.loadLayoutModule(layout2.modulePath))
24601
+ );
24602
+ return resolveFarmLayoutFonts(layoutModules);
24603
+ }, "resolveLayoutFonts"),
24604
+ fontStylesheetHref: "/@farm/fonts.css"
23380
24605
  }) : null;
23381
24606
  const farmDocsAPIHandler = farmDocsDevRuntime ? farmDocsDevRuntime.createFarmDocsAPIHandler({
23382
24607
  rootDir: farmConfig.root,
@@ -23384,22 +24609,7 @@ function farmPlugin(options = {}, initialPluginManager) {
23384
24609
  docs: farmConfig.docs
23385
24610
  }) : null;
23386
24611
  const farmDocsFontAssets = new Map(
23387
- farmDocsDevRuntime ? [
23388
- [
23389
- "/assets/Geist-Variable-CrgPqtmy.woff2",
23390
- path25.join(
23391
- farmConfig.root,
23392
- "node_modules/geist/dist/fonts/geist-sans/Geist-Variable.woff2"
23393
- )
23394
- ],
23395
- [
23396
- "/assets/GeistMono-Variable-BNLlm6Cd.woff2",
23397
- path25.join(
23398
- farmConfig.root,
23399
- "node_modules/geist/dist/fonts/geist-mono/GeistMono-Variable.woff2"
23400
- )
23401
- ]
23402
- ] : []
24612
+ farmDocsFontAssetList.map(({ url, sourcePath }) => [url, sourcePath])
23403
24613
  );
23404
24614
  const logRequest = /* @__PURE__ */ __name((method, urlPath, tag) => {
23405
24615
  try {
@@ -23463,11 +24673,12 @@ function farmPlugin(options = {}, initialPluginManager) {
23463
24673
  }
23464
24674
  }
23465
24675
  const farmDocsFontPath = farmDocsFontAssets.get(requestPathname);
23466
- if (farmDocsFontPath && fs7.existsSync(farmDocsFontPath)) {
24676
+ if ((requestMethod === "GET" || requestMethod === "HEAD") && farmDocsFontPath && fs7.existsSync(farmDocsFontPath)) {
23467
24677
  res.statusCode = 200;
23468
24678
  res.setHeader("Content-Type", "font/woff2");
23469
24679
  res.setHeader("Cache-Control", "public, max-age=31536000, immutable");
23470
- fs7.createReadStream(farmDocsFontPath).pipe(res);
24680
+ if (requestMethod === "HEAD") res.end();
24681
+ else fs7.createReadStream(farmDocsFontPath).pipe(res);
23471
24682
  return;
23472
24683
  }
23473
24684
  if (requestMethod === "GET" && (requestPathname === FARM_DEVTOOLS_PATH || requestPathname === `${FARM_DEVTOOLS_PATH}.json`)) {
@@ -24073,6 +25284,7 @@ function farmPlugin(options = {}, initialPluginManager) {
24073
25284
  modulePath: toUrlPath(route.modulePath),
24074
25285
  isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
24075
25286
  shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
25287
+ islandStrategy: moduleMetadata.islandStrategy,
24076
25288
  metadata: {
24077
25289
  title: mergedMetadata.title,
24078
25290
  description: mergedMetadata.description
@@ -24457,6 +25669,7 @@ export const getManifest = () => ({
24457
25669
  search: route.search,
24458
25670
  isClientComponent: moduleMetadata.isClientComponent,
24459
25671
  shouldHydrate: moduleMetadata.shouldHydrate,
25672
+ islandStrategy: moduleMetadata.islandStrategy,
24460
25673
  preloads: [route.modulePath],
24461
25674
  // In dev, preload is just the module
24462
25675
  assets: []
@@ -24583,8 +25796,8 @@ if (import.meta.hot) {
24583
25796
  }
24584
25797
  logger.info(`Found ${ssgPages.length} SSG pages, ${ssrRoutes.length} SSR routes`);
24585
25798
  logger.info("Pre-rendering SSG pages...");
24586
- const outDir = path25.join(server?.config.root || process.cwd(), options.outDir || "dist");
24587
- const clientDir = path25.join(outDir, "client");
25799
+ const outDir = path27.join(server?.config.root || process.cwd(), options.outDir || "dist");
25800
+ const clientDir = path27.join(outDir, "client");
24588
25801
  for (const page2 of ssgPages) {
24589
25802
  try {
24590
25803
  const staticRequest = new Request(new URL(page2.urlPath, "http://farm.static"));
@@ -24595,7 +25808,7 @@ if (import.meta.hot) {
24595
25808
  const layoutModules = await Promise.all(
24596
25809
  layouts.map((l) => routeManager.loadLayoutModule(l.modulePath))
24597
25810
  );
24598
- const React3 = await import("react");
25811
+ const React4 = await import("react");
24599
25812
  const { renderToString } = await import("react-dom/server");
24600
25813
  const PageComponent = mod.default;
24601
25814
  const pageProps = {
@@ -24603,14 +25816,14 @@ if (import.meta.hot) {
24603
25816
  searchParams: Promise.resolve({}),
24604
25817
  path: page2.urlPath
24605
25818
  };
24606
- let pageElement = React3.createElement(
25819
+ let pageElement = React4.createElement(
24607
25820
  PageComponent,
24608
25821
  pageProps
24609
25822
  );
24610
25823
  for (let i = layoutModules.length - 1; i >= 0; i--) {
24611
25824
  const layoutModule = layoutModules[i];
24612
25825
  const LayoutComponent = layoutModule.default;
24613
- pageElement = React3.createElement(
25826
+ pageElement = React4.createElement(
24614
25827
  LayoutComponent,
24615
25828
  {
24616
25829
  children: pageElement,
@@ -24636,8 +25849,8 @@ if (import.meta.hot) {
24636
25849
  <script type="module" src="/assets/client.js"></script>
24637
25850
  </body>
24638
25851
  </html>`;
24639
- const outputPath = page2.urlPath === "/" ? path25.join(clientDir, "index.html") : path25.join(clientDir, page2.urlPath + ".html");
24640
- fs7.mkdirSync(path25.dirname(outputPath), { recursive: true });
25852
+ const outputPath = page2.urlPath === "/" ? path27.join(clientDir, "index.html") : path27.join(clientDir, page2.urlPath + ".html");
25853
+ fs7.mkdirSync(path27.dirname(outputPath), { recursive: true });
24641
25854
  fs7.writeFileSync(outputPath, fullHtml);
24642
25855
  const revalidateInfo = page2.revalidate ? ` (revalidate: ${page2.revalidate}s)` : "";
24643
25856
  logger.success(` \u2713 ${page2.urlPath}${revalidateInfo}`);
@@ -24646,7 +25859,7 @@ if (import.meta.hot) {
24646
25859
  logger.error(` \u2717 ${page2.urlPath}: ${error}`);
24647
25860
  }
24648
25861
  }
24649
- const manifestPath = path25.join(outDir, "__ssg_manifest.json");
25862
+ const manifestPath = path27.join(outDir, "__ssg_manifest.json");
24650
25863
  fs7.writeFileSync(
24651
25864
  manifestPath,
24652
25865
  JSON.stringify(
@@ -24682,9 +25895,9 @@ if (import.meta.hot) {
24682
25895
  }
24683
25896
  const currentFarmConfig = farmApp?.getConfig();
24684
25897
  const normalizedFile = file.replace(/\\/g, "/");
24685
- const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path25.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
25898
+ const currentSrcRoot = currentFarmConfig ? getFarmSourceRoots(currentFarmConfig).map((source) => path27.join(source.root, source.srcDir).replace(/\\/g, "/")).find((sourceRoot) => normalizedFile.startsWith(`${sourceRoot}/`)) || null : null;
24686
25899
  if (currentSrcRoot && normalizedFile.startsWith(`${currentSrcRoot}/`) && isProgrammaticRoutesFileName(normalizedFile)) {
24687
- logUpdate("PAGE", `updated ${path25.basename(file)}`);
25900
+ logUpdate("PAGE", `updated ${path27.basename(file)}`);
24688
25901
  for (const mod of modules) {
24689
25902
  server2.moduleGraph.invalidateModule(mod);
24690
25903
  }
@@ -25075,6 +26288,7 @@ import React from 'react'
25075
26288
  import { hydrateRoot, createRoot } from 'react-dom/client'
25076
26289
  import { installChunkErrorRecovery, SPARouter } from '@farm.js/core/client'
25077
26290
  import { createClientPluginManager } from '@farm.js/core/plugin/client'
26291
+ import { scheduleFarmIslandHydration } from '@farm.js/core/internal/client-runtime'
25078
26292
  import { reviveDeferredData } from '@farm.js/core/deferred'
25079
26293
  import {
25080
26294
  createFarmDeploymentMismatchError,
@@ -25327,6 +26541,7 @@ class LegacyManifestSPARouter {
25327
26541
 
25328
26542
  // Only prefetch routes that will hydrate on the client.
25329
26543
  if (!match.route.isClientComponent && !match.route.shouldHydrate) return;
26544
+ if (match.route.islandStrategy && match.route.islandStrategy !== 'load') return;
25330
26545
 
25331
26546
  const modulePath = match.route.modulePath;
25332
26547
  if (this.moduleCache.has(modulePath)) return;
@@ -25471,6 +26686,12 @@ let hasClientTakenOver = false;
25471
26686
  const routeSlotRoots = new Map();
25472
26687
  const routeSlotDefinitions = new Map();
25473
26688
  let activeRouteInterception = null;
26689
+ let pendingPageHydrationController = null;
26690
+
26691
+ function cancelPendingPageHydration() {
26692
+ pendingPageHydrationController?.abort();
26693
+ pendingPageHydrationController = null;
26694
+ }
25474
26695
 
25475
26696
  function normalizeServerProps(rawProps) {
25476
26697
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -25539,7 +26760,11 @@ async function hydrateInitialRouteSlots() {
25539
26760
  routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
25540
26761
  if (!slot.isClientComponent && !slot.shouldHydrate) continue;
25541
26762
  try {
25542
- hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
26763
+ const slotHydrated = await renderRouteSlot(slot, 'hydrate');
26764
+ hydrated = slotHydrated || hydrated;
26765
+ if (slotHydrated) {
26766
+ replayPreHydrationClicks(document.getElementById(slot.containerId));
26767
+ }
25543
26768
  } catch (error) {
25544
26769
  console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
25545
26770
  }
@@ -25610,16 +26835,29 @@ function applyCanonicalPathFromProps(props) {
25610
26835
  window.history.replaceState({ ...(window.history.state || {}), path: canonicalPath }, '', canonicalPath);
25611
26836
  }
25612
26837
 
25613
- function replayPreHydrationClicks() {
25614
- window.__FARM_HYDRATED__ = true;
25615
- document.documentElement.dataset.farmHydrated = 'true';
26838
+ function replayPreHydrationClicks(container = null) {
26839
+ if (!container) {
26840
+ markFarmHydrated();
26841
+ } else {
26842
+ container.dataset.farmIslandHydrated = 'true';
26843
+ }
25616
26844
 
25617
26845
  const queue = Array.isArray(window.__FARM_PREHYDRATION_CLICK_QUEUE__)
25618
26846
  ? window.__FARM_PREHYDRATION_CLICK_QUEUE__
25619
26847
  : [];
25620
26848
  if (queue.length === 0) return;
25621
26849
 
25622
- const queuedClicks = queue.splice(0, queue.length);
26850
+ const queuedClicks = [];
26851
+ const remainingClicks = [];
26852
+ for (const queuedClick of queue) {
26853
+ const target = queuedClick?.target;
26854
+ if (!container || (target instanceof Node && container.contains(target))) {
26855
+ queuedClicks.push(queuedClick);
26856
+ } else {
26857
+ remainingClicks.push(queuedClick);
26858
+ }
26859
+ }
26860
+ queue.splice(0, queue.length, ...remainingClicks);
25623
26861
  for (const queuedClick of queuedClicks) {
25624
26862
  const target = queuedClick?.target;
25625
26863
  if (!target || typeof target.click !== 'function') continue;
@@ -25628,6 +26866,11 @@ function replayPreHydrationClicks() {
25628
26866
  }
25629
26867
  }
25630
26868
 
26869
+ function markFarmHydrated() {
26870
+ window.__FARM_HYDRATED__ = true;
26871
+ document.documentElement.dataset.farmHydrated = 'true';
26872
+ }
26873
+
25631
26874
  async function buildClientHydrationElement(PageComponent, pageProps) {
25632
26875
  let element = React.createElement(PageComponent, pageProps);
25633
26876
  const loadingModulePath = window.__FARM_LOADING_MODULE__;
@@ -25770,9 +27013,10 @@ async function tryHydrateImportedPage(
25770
27013
  layouts,
25771
27014
  useHydrate = false,
25772
27015
  existingProps = null,
27016
+ signal = null,
25773
27017
  ) {
25774
27018
  const modulePath = route?.modulePath;
25775
- if (!modulePath) {
27019
+ if (!modulePath || signal?.aborted) {
25776
27020
  return false;
25777
27021
  }
25778
27022
 
@@ -25781,6 +27025,7 @@ async function tryHydrateImportedPage(
25781
27025
  pageModule = await import(/* @vite-ignore */ modulePath);
25782
27026
  pageModuleCache.set(modulePath, pageModule);
25783
27027
  }
27028
+ if (signal?.aborted || !container?.isConnected) return false;
25784
27029
 
25785
27030
  const PageComponent = pageModule?.default;
25786
27031
  if (!PageComponent) {
@@ -25795,6 +27040,7 @@ async function tryHydrateImportedPage(
25795
27040
  window.location.pathname,
25796
27041
  existingProps,
25797
27042
  );
27043
+ if (signal?.aborted || !container?.isConnected) return false;
25798
27044
 
25799
27045
  const wrappedElement = useHydrate && container?.id === '__farm_page__'
25800
27046
  ? wrapWithIntegrationProviders(
@@ -25805,6 +27051,7 @@ async function tryHydrateImportedPage(
25805
27051
  currentPageProps,
25806
27052
  layouts,
25807
27053
  );
27054
+ if (signal?.aborted || !container?.isConnected) return false;
25808
27055
 
25809
27056
  if (useHydrate) {
25810
27057
  try {
@@ -25835,6 +27082,7 @@ async function tryHydrateImportedPage(
25835
27082
  async function renderPage(pageData) {
25836
27083
  const container = document.getElementById('root');
25837
27084
  if (!container) return;
27085
+ cancelPendingPageHydration();
25838
27086
  const destination = window.location.pathname + window.location.search;
25839
27087
 
25840
27088
  if (pageData.interception) {
@@ -25851,6 +27099,7 @@ async function renderPage(pageData) {
25851
27099
  modulePath: pageData.modulePath,
25852
27100
  isClientComponent: pageData.isClientComponent === true,
25853
27101
  shouldHydrate: pageData.shouldHydrate === true,
27102
+ islandStrategy: pageData.islandStrategy || 'load',
25854
27103
  };
25855
27104
  const params = pageData.props?.params || {};
25856
27105
  const layouts = (pageData.layoutModules || []).map((modulePath, index) => ({
@@ -26050,14 +27299,6 @@ async function hydrate() {
26050
27299
  }
26051
27300
 
26052
27301
  try {
26053
- // Pre-load layout for SPA navigation
26054
- try {
26055
- const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
26056
- LayoutComponent = layoutModule.default;
26057
- } catch (e) {
26058
- console.warn('[Farm.js] Could not preload layout:', e);
26059
- }
26060
-
26061
27302
  // Check if this is a client component (set by SSR)
26062
27303
  const isClientComponent = window.__FARM_IS_CLIENT__ === true;
26063
27304
  const modulePath = window.__FARM_PAGE_MODULE__;
@@ -26105,34 +27346,88 @@ async function hydrate() {
26105
27346
  return
26106
27347
  }
26107
27348
 
26108
- const hydrationSession = await farmClientRuntime.beginHydration({
26109
- container: pageContainer,
26110
- mode: shouldHydrate ? 'hydrate' : 'render',
26111
- });
27349
+ const currentRoute = findRoute(window.location.pathname)?.route;
27350
+ const islandStrategy = window.__FARM_ISLAND_STRATEGY__ || currentRoute?.islandStrategy || 'load';
27351
+ pageContainer.dataset.farmIsland = 'page';
27352
+ pageContainer.dataset.farmIslandStrategy = islandStrategy;
26112
27353
 
26113
- let hydrated = false;
27354
+ const hydrationPathname = window.location.pathname;
27355
+ const hydrationController = new AbortController();
27356
+ pendingPageHydrationController = hydrationController;
26114
27357
  try {
26115
- hydrated = await tryHydrateImportedPage(
26116
- pageContainer,
26117
- { modulePath },
26118
- currentPageProps.params || {},
26119
- layouts,
26120
- shouldHydrate,
26121
- currentPageProps,
26122
- );
26123
- await farmClientRuntime.completeHydration(hydrationSession);
26124
- } catch (error) {
26125
- await farmClientRuntime.failHydration(hydrationSession, error);
26126
- throw error;
26127
- }
27358
+ await scheduleFarmIslandHydration({
27359
+ container: pageContainer,
27360
+ strategy: islandStrategy,
27361
+ signal: hydrationController.signal,
27362
+ hydrate: async () => {
27363
+ if (
27364
+ hydrationController.signal.aborted ||
27365
+ !pageContainer.isConnected ||
27366
+ window.location.pathname !== hydrationPathname
27367
+ ) {
27368
+ return;
27369
+ }
26128
27370
 
26129
- if (hydrated) {
26130
- replayPreHydrationClicks();
26131
- console.log('[Farm.js] \u2705 Hydrated:', modulePath);
26132
- return;
26133
- }
27371
+ // Layout code is only needed when this route boundary actually hydrates.
27372
+ try {
27373
+ const layoutModule = await import(/* @vite-ignore */ '/src/app/layout.tsx');
27374
+ LayoutComponent = layoutModule.default;
27375
+ } catch (e) {
27376
+ console.warn('[Farm.js] Could not preload layout:', e);
27377
+ }
27378
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) return;
27379
+
27380
+ const hydrationSession = await farmClientRuntime.beginHydration({
27381
+ container: pageContainer,
27382
+ mode: shouldHydrate ? 'hydrate' : 'render',
27383
+ });
27384
+
27385
+ let hydrated = false;
27386
+ try {
27387
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
27388
+ await farmClientRuntime.failHydration(
27389
+ hydrationSession,
27390
+ new DOMException('Route hydration was cancelled', 'AbortError'),
27391
+ );
27392
+ return;
27393
+ }
27394
+ hydrated = await tryHydrateImportedPage(
27395
+ pageContainer,
27396
+ { modulePath },
27397
+ currentPageProps.params || {},
27398
+ layouts,
27399
+ shouldHydrate,
27400
+ currentPageProps,
27401
+ hydrationController.signal,
27402
+ );
27403
+ if (hydrationController.signal.aborted || !pageContainer.isConnected) {
27404
+ await farmClientRuntime.failHydration(
27405
+ hydrationSession,
27406
+ new DOMException('Route hydration was cancelled', 'AbortError'),
27407
+ );
27408
+ return;
27409
+ }
27410
+ await farmClientRuntime.completeHydration(hydrationSession);
27411
+ } catch (error) {
27412
+ await farmClientRuntime.failHydration(hydrationSession, error);
27413
+ throw error;
27414
+ }
26134
27415
 
26135
- console.log('[Farm.js] Server component - SPA router ready')
27416
+ if (hydrated) {
27417
+ // The scheduler owns queue draining and exactly-once click replay.
27418
+ markFarmHydrated();
27419
+ console.log('[Farm.js] \u2705 Hydrated:', modulePath, '(' + islandStrategy + ')');
27420
+ return;
27421
+ }
27422
+
27423
+ console.log('[Farm.js] Server component - SPA router ready')
27424
+ },
27425
+ });
27426
+ } finally {
27427
+ if (pendingPageHydrationController === hydrationController) {
27428
+ pendingPageHydrationController = null;
27429
+ }
27430
+ }
26136
27431
  } catch (error) {
26137
27432
  console.error('[Farm.js] Hydration error:', error)
26138
27433
  }
@@ -26235,7 +27530,7 @@ function generateClientManifest(bundle) {
26235
27530
  }
26236
27531
  async function defineConfig2(config = {}) {
26237
27532
  const tailwindcss = (await import("@tailwindcss/vite")).default;
26238
- const appRoot = path25.resolve(config.root || process.cwd());
27533
+ const appRoot = path27.resolve(config.root || process.cwd());
26239
27534
  if (config.extends?.length) {
26240
27535
  const { config: layeredConfig } = await Promise.resolve().then(() => (init_layers(), layers_exports)).then(
26241
27536
  ({ resolveFarmLayers: resolveFarmLayers2 }) => resolveFarmLayers2(config, {
@@ -26380,13 +27675,16 @@ async function defineConfig2(config = {}) {
26380
27675
  customLogger: farmLogger,
26381
27676
  clearScreen: false,
26382
27677
  optimizeDeps: {
26383
- include: [
26384
- "react",
26385
- "react-dom",
26386
- "react-dom/client",
26387
- "react/jsx-runtime",
26388
- "react/jsx-dev-runtime"
26389
- ],
27678
+ ...createFarmClientOptimizeDepsConfig(
27679
+ createFarmClientOptimizeDepsEntries(
27680
+ appRoot,
27681
+ getFarmAppDirectories({ ...config, root: appRoot })
27682
+ )
27683
+ ),
27684
+ // Pre-bundle every framework client-runtime entry with React. Without
27685
+ // this, Vite can discover a linked Farm entry after the page has loaded,
27686
+ // regenerate the optimizer browser hash, and leave React DOM holding a
27687
+ // different React module instance from a client component.
26390
27688
  // Exclude server-side packages from browser bundling
26391
27689
  exclude: [
26392
27690
  "@farm.js/core/server",
@@ -26421,7 +27719,7 @@ async function defineConfig2(config = {}) {
26421
27719
  // Stub out problematic server-only modules during dev mode
26422
27720
  alias: {
26423
27721
  ...getFarmLayerAliases(config.layers),
26424
- "@": path25.resolve(appRoot, "src"),
27722
+ "@": path27.resolve(appRoot, "src"),
26425
27723
  // Nitro internals that should not be resolved in browser
26426
27724
  "supports-color": "data:text/javascript,export default false; export const supportsColor = false; export const stdout = false; export const stderr = false;",
26427
27725
  "@poppinss/dumper": "data:text/javascript,export default {};",
@@ -26438,7 +27736,7 @@ async function defineConfig2(config = {}) {
26438
27736
  }
26439
27737
  };
26440
27738
  }
26441
- var fs7, path25, ALL_TYPE_ARTIFACTS, createEmptyTypeArtifactSelection, FARM_I18N_CLIENT_BRIDGE_ID, EMPTY_FARM_DOCS_SEARCH_CLIENT_RUNTIME, FARM_CONFIG_FILENAMES;
27739
+ var fs7, path27, ALL_TYPE_ARTIFACTS, createEmptyTypeArtifactSelection, FARM_I18N_CLIENT_BRIDGE_ID, EMPTY_FARM_DOCS_SEARCH_CLIENT_RUNTIME, FARM_CONFIG_FILENAMES;
26442
27740
  var init_vite = __esm({
26443
27741
  "src/vite.ts"() {
26444
27742
  "use strict";
@@ -26459,7 +27757,7 @@ var init_vite = __esm({
26459
27757
  init_route_context();
26460
27758
  init_devtools_config();
26461
27759
  fs7 = __toESM(require("fs"));
26462
- path25 = __toESM(require("path"));
27760
+ path27 = __toESM(require("path"));
26463
27761
  init_layers();
26464
27762
  init_environment_vite();
26465
27763
  init_version();
@@ -26469,9 +27767,12 @@ var init_vite = __esm({
26469
27767
  init_image_config();
26470
27768
  init_image_vite();
26471
27769
  init_font_vite();
27770
+ init_font();
26472
27771
  init_image_server();
26473
27772
  init_server2();
26474
27773
  init_routing();
27774
+ init_vite_config();
27775
+ init_fonts();
26475
27776
  ALL_TYPE_ARTIFACTS = {
26476
27777
  routes: true,
26477
27778
  api: true,
@@ -27865,6 +29166,7 @@ init_layers();
27865
29166
  init_environment_vite();
27866
29167
  init_last_modified();
27867
29168
  init_search_client();
29169
+ init_fonts();
27868
29170
 
27869
29171
  // src/route-runtime-manifest.ts
27870
29172
  var import_path8 = __toESM(require("path"));
@@ -28046,7 +29348,7 @@ function farmRoutePatternToVercelSource(pattern) {
28046
29348
  } else if (segment === "*" || /^\[.+\]$/.test(segment)) {
28047
29349
  source += "/([^/]+)";
28048
29350
  } else {
28049
- source += `/${escapeRegExp2(segment)}`;
29351
+ source += `/${escapeRegExp3(segment)}`;
28050
29352
  }
28051
29353
  }
28052
29354
  return source || "/";
@@ -28091,10 +29393,35 @@ function normalizePattern(value) {
28091
29393
  return withSlash.length > 1 ? withSlash.replace(/\/+$/, "") : withSlash;
28092
29394
  }
28093
29395
  __name(normalizePattern, "normalizePattern");
28094
- function escapeRegExp2(value) {
29396
+ function escapeRegExp3(value) {
29397
+ return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
29398
+ }
29399
+ __name(escapeRegExp3, "escapeRegExp");
29400
+
29401
+ // src/nitro/vercel-assets.ts
29402
+ var FARM_IMMUTABLE_ASSET_CACHE_CONTROL = "public, max-age=31536000, immutable";
29403
+ function createFarmVercelImmutableAssetRoute(basePath = "/") {
29404
+ const normalizedBasePath = normalizeBasePath3(basePath);
29405
+ const escapedBasePath = escapeRegex(normalizedBasePath);
29406
+ const prefix = escapedBasePath ? `/${escapedBasePath}` : "";
29407
+ return {
29408
+ src: `^${prefix}/(?:assets|chunks)/(?:.+/)*[^/]+-h(?:[a-fA-F0-9]{8}|[a-fA-F0-9]{12}|[a-fA-F0-9]{16})\\.(?!(?:[hH][tT][mM][lL]?)$)[^/]+$`,
29409
+ headers: {
29410
+ "Cache-Control": FARM_IMMUTABLE_ASSET_CACHE_CONTROL
29411
+ },
29412
+ continue: true,
29413
+ caseSensitive: true
29414
+ };
29415
+ }
29416
+ __name(createFarmVercelImmutableAssetRoute, "createFarmVercelImmutableAssetRoute");
29417
+ function normalizeBasePath3(basePath) {
29418
+ return basePath.trim().replace(/^\/+|\/+$/g, "");
29419
+ }
29420
+ __name(normalizeBasePath3, "normalizeBasePath");
29421
+ function escapeRegex(value) {
28095
29422
  return value.replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
28096
29423
  }
28097
- __name(escapeRegExp2, "escapeRegExp");
29424
+ __name(escapeRegex, "escapeRegex");
28098
29425
 
28099
29426
  // src/nitro/universal-build.ts
28100
29427
  init_catalog();
@@ -28303,6 +29630,10 @@ function isCloudflareImagePreset(preset) {
28303
29630
  return preset === "cloudflare" || preset === "cloudflare-pages" || preset === "cloudflare-module";
28304
29631
  }
28305
29632
  __name(isCloudflareImagePreset, "isCloudflareImagePreset");
29633
+ function shouldUseExternalMetadataImageRuntime(preset, hasGeneratedMetadataImages) {
29634
+ return hasGeneratedMetadataImages && preset !== "vercel-edge" && !isCloudflareImagePreset(preset);
29635
+ }
29636
+ __name(shouldUseExternalMetadataImageRuntime, "shouldUseExternalMetadataImageRuntime");
28306
29637
  function resolveImageRuntime(config, preset) {
28307
29638
  if (config.images.provider === "none") return "none";
28308
29639
  const cloudflarePreset = isCloudflareImagePreset(preset);
@@ -28320,6 +29651,31 @@ function isNitroRollupExternal(id) {
28320
29651
  return NODE_BUILTIN_MODULES.has(id) || NITRO_EXTERNAL_MODULES.has(id) || normalizedId.startsWith(".prisma/") || normalizedId.includes("/node_modules/@prisma/client/") || normalizedId.includes("/node_modules/.prisma/client/");
28321
29652
  }
28322
29653
  __name(isNitroRollupExternal, "isNitroRollupExternal");
29654
+ var FARM_METADATA_IMAGE_WASM_PREFIX = "\0farm-metadata-image-wasm:";
29655
+ function createMetadataImageWasmPlugin() {
29656
+ return {
29657
+ name: "farm-metadata-image-wasm",
29658
+ resolveId(source, importer) {
29659
+ if (!source.endsWith(".wasm?module") || !importer?.replace(/\\/g, "/").includes("/@vercel/og/")) {
29660
+ return null;
29661
+ }
29662
+ const requestPath = source.slice(0, -"?module".length);
29663
+ const resolvedPath = import_path12.default.isAbsolute(requestPath) ? requestPath : import_path12.default.resolve(import_path12.default.dirname(importer.split("?", 1)[0]), requestPath);
29664
+ return `${FARM_METADATA_IMAGE_WASM_PREFIX}${resolvedPath}`;
29665
+ },
29666
+ load(id) {
29667
+ if (!id.startsWith(FARM_METADATA_IMAGE_WASM_PREFIX)) return null;
29668
+ const wasmPath = id.slice(FARM_METADATA_IMAGE_WASM_PREFIX.length);
29669
+ const encoded = (0, import_fs5.readFileSync)(wasmPath).toString("base64");
29670
+ return `
29671
+ const encoded = ${JSON.stringify(encoded)};
29672
+ const bytes = Uint8Array.from(atob(encoded), (character) => character.charCodeAt(0));
29673
+ export default new WebAssembly.Module(bytes);
29674
+ `;
29675
+ }
29676
+ };
29677
+ }
29678
+ __name(createMetadataImageWasmPlugin, "createMetadataImageWasmPlugin");
28323
29679
  function collectSSRExternalPackages(ssrBundle) {
28324
29680
  const emittedFiles = new Set(
28325
29681
  Object.keys(ssrBundle).map((fileName) => fileName.replace(/^\.\//, ""))
@@ -28703,6 +30059,9 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
28703
30059
  }
28704
30060
  const { bundle: ssrBundle, entryFile: ssrEntryFile, configuredHeaderRoutes } = ssrResult;
28705
30061
  await writeSSRAssetsToClient(ssrBundle, clientOutputDir);
30062
+ if (config.docs.enabled) {
30063
+ await copyFarmDocsFontAssetsToClient(root, clientOutputDir);
30064
+ }
28706
30065
  logger.info(`\u{1F680} Building server with Nitro (preset: ${preset})...`);
28707
30066
  await buildNitroUniversal(
28708
30067
  config,
@@ -28757,6 +30116,17 @@ async function writeSSRAssetsToClient(bundle, outputDir) {
28757
30116
  }
28758
30117
  }
28759
30118
  __name(writeSSRAssetsToClient, "writeSSRAssetsToClient");
30119
+ async function copyFarmDocsFontAssetsToClient(root, outputDir) {
30120
+ const fs8 = await import("fs/promises");
30121
+ await Promise.all(
30122
+ resolveFarmDocsFontAssets(root).map(async ({ sourcePath, url }) => {
30123
+ const targetPath = import_path12.default.join(outputDir, url.replace(/^\/+/, ""));
30124
+ await fs8.mkdir(import_path12.default.dirname(targetPath), { recursive: true });
30125
+ await fs8.copyFile(sourcePath, targetPath);
30126
+ })
30127
+ );
30128
+ }
30129
+ __name(copyFarmDocsFontAssetsToClient, "copyFarmDocsFontAssetsToClient");
28760
30130
  async function mergeBuiltFontCss(outputDir, fontCssSource) {
28761
30131
  const fs8 = await import("fs/promises");
28762
30132
  const fontCssPath = import_path12.default.join(outputDir, "farm-fonts.css");
@@ -28775,11 +30145,8 @@ async function mergeBuiltFontCss(outputDir, fontCssSource) {
28775
30145
  } catch {
28776
30146
  }
28777
30147
  const normalizedFontCss = typeof fontCss === "string" ? fontCss : Buffer.from(fontCss).toString("utf8");
30148
+ await fs8.writeFile(fontCssPath, normalizedFontCss);
28778
30149
  await fs8.writeFile(clientCssPath, mergeFarmFontCss(clientCss, normalizedFontCss));
28779
- try {
28780
- await fs8.rm(fontCssPath, { force: true });
28781
- } catch {
28782
- }
28783
30150
  }
28784
30151
  __name(mergeBuiltFontCss, "mergeBuiltFontCss");
28785
30152
  var _IncompleteClientBuildOutputError = class _IncompleteClientBuildOutputError extends Error {
@@ -28844,7 +30211,11 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
28844
30211
  const metadata = getClientModuleMetadata(route.modulePath, root);
28845
30212
  if (metadata.shouldHydrate) {
28846
30213
  const relativePath2 = route.modulePath.replace(root, "").replace(/^\//, "");
28847
- clientPages.push({ ...route, relativePath: relativePath2 });
30214
+ clientPages.push({
30215
+ ...route,
30216
+ relativePath: relativePath2,
30217
+ islandStrategy: metadata.islandStrategy ?? "load"
30218
+ });
28848
30219
  logger.info(`\u{1F4F1} Found hydratable route: ${route.pattern} -> ${route.modulePath}`);
28849
30220
  }
28850
30221
  } catch (error) {
@@ -28918,13 +30289,14 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
28918
30289
  },
28919
30290
  output: {
28920
30291
  entryFileNames: "[name].js",
28921
- chunkFileNames: "chunks/[name]-[hash].js",
30292
+ chunkFileNames: "chunks/[name]-h[hash].js",
30293
+ hashCharacters: "hex",
28922
30294
  // Use predictable name for CSS so we can reference it in SSR HTML
28923
30295
  assetFileNames: /* @__PURE__ */ __name((assetInfo) => {
28924
30296
  if (assetInfo.name?.endsWith(".css")) {
28925
30297
  return "farm-client.css";
28926
30298
  }
28927
- return "assets/[name]-[hash][extname]";
30299
+ return "assets/[name]-h[hash][extname]";
28928
30300
  }, "assetFileNames")
28929
30301
  },
28930
30302
  // Externalize Node.js built-ins and server-side modules for client build
@@ -29523,10 +30895,13 @@ document.addEventListener("click", function(e) {
29523
30895
  const imports = [];
29524
30896
  const routeEntries = [];
29525
30897
  const routeSlotEntries = [];
29526
- clientPages.forEach((page2, index) => {
30898
+ clientPages.forEach((page2) => {
29527
30899
  const importPath = toImportPath(page2.modulePath);
29528
- imports.push(`import Page${index} from "${importPath}";`);
29529
- routeEntries.push(` { pattern: ${JSON.stringify(page2.pattern)}, Component: Page${index} }`);
30900
+ routeEntries.push(` {
30901
+ pattern: ${JSON.stringify(page2.pattern)},
30902
+ islandStrategy: ${JSON.stringify(page2.islandStrategy)},
30903
+ load: () => import(${JSON.stringify(importPath)}).then((module) => module.default),
30904
+ }`);
29530
30905
  });
29531
30906
  clientRouteSlots.forEach((slot, index) => {
29532
30907
  const importPath = toImportPath(slot.modulePath);
@@ -29547,7 +30922,7 @@ ${cssImport}
29547
30922
  ${layoutImports}
29548
30923
  import React from "react";
29549
30924
  import { createRoot, hydrateRoot } from "react-dom/client";
29550
- import { createClientPluginManager, installChunkErrorRecovery } from "@farm.js/core/internal/client-runtime";
30925
+ import { createClientPluginManager, installChunkErrorRecovery, scheduleFarmIslandHydration } from "@farm.js/core/internal/client-runtime";
29551
30926
  import { matchFarmRoute } from "@farm.js/core/router";
29552
30927
  ${clientPluginEntry.imports}
29553
30928
  ${i18nClientRuntime}
@@ -29620,6 +30995,12 @@ function matchRoute(pathname) {
29620
30995
  return null;
29621
30996
  }
29622
30997
 
30998
+ async function loadRouteComponent(route) {
30999
+ if (route.Component) return route.Component;
31000
+ route.Component = await route.load();
31001
+ return route.Component;
31002
+ }
31003
+
29623
31004
  function matchesRoutePrefix(pathname, pattern) {
29624
31005
  if (pattern === "/") return true;
29625
31006
  const pathSegments = getFarmRoutePathname(pathname).split("/").filter(Boolean);
@@ -29762,15 +31143,24 @@ function clearRouteInterception(destination) {
29762
31143
 
29763
31144
  // State
29764
31145
  let reactRoot = null;
31146
+ let reactRootContainer = null;
29765
31147
  let currentPathname = null;
29766
31148
  let isHydrated = false;
31149
+ let pendingPageHydrationController = null;
29767
31150
 
29768
31151
  ${generateUniversalRouterStateRuntime()}
29769
31152
 
31153
+ function cancelPendingPageHydration() {
31154
+ pendingPageHydrationController?.abort();
31155
+ pendingPageHydrationController = null;
31156
+ }
31157
+
29770
31158
  function resetReactRoot() {
31159
+ cancelPendingPageHydration();
29771
31160
  if (!reactRoot) return;
29772
31161
  reactRoot.unmount();
29773
31162
  reactRoot = null;
31163
+ reactRootContainer = null;
29774
31164
  isHydrated = false;
29775
31165
  }
29776
31166
 
@@ -29784,49 +31174,77 @@ async function hydrate() {
29784
31174
  return;
29785
31175
  }
29786
31176
 
29787
- const hasRouteSlots =
29788
- Array.isArray(window.__FARM_ROUTE_SLOTS__) && window.__FARM_ROUTE_SLOTS__.length > 0;
29789
31177
  const container =
29790
- (hasRouteSlots ? document.getElementById("__farm_page__") : null) ||
31178
+ document.getElementById("__farm_page__") ||
29791
31179
  document.getElementById("root");
29792
31180
  if (!container) {
29793
31181
  console.error("[Farm.js] No root element found");
29794
31182
  return;
29795
31183
  }
29796
31184
 
29797
- const Component = matched.route.Component;
29798
- const params = matched.params;
29799
- const searchParams = Object.fromEntries(new URLSearchParams(window.location.search));
29800
-
29801
- const props = { params: params, searchParams: Promise.resolve(searchParams) };
29802
-
29803
- // Create page element and wrap with layouts
29804
- const pageElement = React.createElement(Component, props);
29805
- const wrappedElement = hasRouteSlots
29806
- ? pageElement
29807
- : wrapWithLayouts(pageElement, pathname, params);
29808
-
29809
- const shouldHydrate = !isHydrated && Boolean(container.innerHTML.trim());
29810
- const hydrationSession = await farmClientRuntime.beginHydration({
29811
- container,
29812
- mode: shouldHydrate ? "hydrate" : "render",
29813
- });
31185
+ container.dataset.farmIsland = "page";
31186
+ container.dataset.farmIslandStrategy = matched.route.islandStrategy || "load";
29814
31187
 
31188
+ const hydrationController = new AbortController();
31189
+ pendingPageHydrationController = hydrationController;
29815
31190
  try {
29816
- if (shouldHydrate) {
29817
- reactRoot = hydrateRoot(container, wrappedElement);
29818
- isHydrated = true;
29819
- } else {
29820
- if (!reactRoot) {
29821
- reactRoot = createRoot(container);
29822
- }
29823
- reactRoot.render(wrappedElement);
31191
+ await scheduleFarmIslandHydration({
31192
+ container,
31193
+ strategy: matched.route.islandStrategy,
31194
+ signal: hydrationController.signal,
31195
+ hydrate: async function() {
31196
+ const Component = await loadRouteComponent(matched.route);
31197
+ if (
31198
+ hydrationController.signal.aborted ||
31199
+ !container.isConnected ||
31200
+ window.location.pathname !== pathname
31201
+ ) {
31202
+ return;
31203
+ }
31204
+ const params = matched.params;
31205
+ const searchParams = Object.fromEntries(new URLSearchParams(window.location.search));
31206
+ const props = { params: params, searchParams: Promise.resolve(searchParams) };
31207
+ const pageElement = React.createElement(Component, props);
31208
+ const wrappedElement = container.id === "__farm_page__"
31209
+ ? pageElement
31210
+ : wrapWithLayouts(pageElement, pathname, params);
31211
+ const shouldHydrate = !isHydrated && Boolean(container.innerHTML.trim());
31212
+ const hydrationSession = await farmClientRuntime.beginHydration({
31213
+ container,
31214
+ mode: shouldHydrate ? "hydrate" : "render",
31215
+ });
31216
+
31217
+ try {
31218
+ if (hydrationController.signal.aborted || !container.isConnected) {
31219
+ await farmClientRuntime.failHydration(
31220
+ hydrationSession,
31221
+ new DOMException("Route hydration was cancelled", "AbortError"),
31222
+ );
31223
+ return;
31224
+ }
31225
+ if (shouldHydrate) {
31226
+ reactRoot = hydrateRoot(container, wrappedElement);
31227
+ reactRootContainer = container;
31228
+ isHydrated = true;
31229
+ } else {
31230
+ if (!reactRoot) {
31231
+ reactRoot = createRoot(container);
31232
+ reactRootContainer = container;
31233
+ }
31234
+ reactRoot.render(wrappedElement);
31235
+ }
31236
+ currentPathname = pathname;
31237
+ await farmClientRuntime.completeHydration(hydrationSession);
31238
+ } catch (error) {
31239
+ await farmClientRuntime.failHydration(hydrationSession, error);
31240
+ console.error("[Farm.js] Hydration error:", error);
31241
+ }
31242
+ },
31243
+ });
31244
+ } finally {
31245
+ if (pendingPageHydrationController === hydrationController) {
31246
+ pendingPageHydrationController = null;
29824
31247
  }
29825
- currentPathname = pathname;
29826
- await farmClientRuntime.completeHydration(hydrationSession);
29827
- } catch (error) {
29828
- await farmClientRuntime.failHydration(hydrationSession, error);
29829
- console.error("[Farm.js] Hydration error:", error);
29830
31248
  }
29831
31249
  }
29832
31250
 
@@ -29855,6 +31273,8 @@ ${generateUniversalRouterStateProperties()}
29855
31273
  const from = this.currentPath;
29856
31274
  if (await this.shouldBlockNavigation({ from, to, action })) return;
29857
31275
 
31276
+ // A route transition invalidates any trigger waiting on the previous DOM boundary.
31277
+ cancelPendingPageHydration();
29858
31278
  this.saveScrollPosition(window.location.pathname);
29859
31279
  this.startNavigation(from, url, action);
29860
31280
  let clientNavigation;
@@ -29921,7 +31341,9 @@ ${generateUniversalRouterStateProperties()}
29921
31341
  }
29922
31342
 
29923
31343
  if (matched) {
29924
- const Component = matched.route.Component;
31344
+ // Navigation itself signals intent, so destination routes load eagerly even
31345
+ // when their initial document hydration strategy is deferred.
31346
+ const Component = await loadRouteComponent(matched.route);
29925
31347
  const params = matched.params;
29926
31348
  const searchParams = Object.fromEntries(url.searchParams);
29927
31349
  const props = { params: params, searchParams: Promise.resolve(searchParams) };
@@ -29947,8 +31369,10 @@ ${generateUniversalRouterStateProperties()}
29947
31369
 
29948
31370
  const container = document.getElementById("root");
29949
31371
  if (container) {
31372
+ if (reactRoot && reactRootContainer !== container) resetReactRoot();
29950
31373
  if (!reactRoot) {
29951
31374
  reactRoot = createRoot(container);
31375
+ reactRootContainer = container;
29952
31376
  }
29953
31377
  reactRoot.render(wrappedElement);
29954
31378
  currentPathname = pathname;
@@ -29958,7 +31382,7 @@ ${generateUniversalRouterStateProperties()}
29958
31382
  const html = await this.fetchPage(url.pathname + url.search);
29959
31383
  await farmClientRuntime.markNavigationLoaded(clientNavigation, html);
29960
31384
  await this.runViewTransition(options.viewTransition, async () => {
29961
- if (!this.swapContent(html, url.pathname + url.search)) {
31385
+ if (!(await this.swapContent(html, url.pathname + url.search))) {
29962
31386
  throw new Error("Farm could not swap the target document");
29963
31387
  }
29964
31388
  const historyState = createHistoryState(
@@ -30013,7 +31437,7 @@ ${generateUniversalRouterStateProperties()}
30013
31437
  return html;
30014
31438
  },
30015
31439
 
30016
- swapContent: function(html, targetPath) {
31440
+ swapContent: async function(html, targetPath) {
30017
31441
  const parser = new DOMParser();
30018
31442
  const doc = parser.parseFromString(html, "text/html");
30019
31443
  if (isFarmLocaleDocumentChange(doc)) return false;
@@ -30053,18 +31477,18 @@ ${generateUniversalRouterStateProperties()}
30053
31477
  const matched = matchRoute(newPathname);
30054
31478
  if (matched) {
30055
31479
  // Re-hydrate the client component
30056
- const Component = matched.route.Component;
31480
+ const Component = await loadRouteComponent(matched.route);
30057
31481
  const params = matched.params;
30058
31482
  const searchParams = Object.fromEntries(targetUrl.searchParams);
30059
31483
  const props = { params: params, searchParams: Promise.resolve(searchParams) };
30060
31484
  const pageElement = React.createElement(Component, props);
30061
- const hasRouteSlots = nextRouteSlots.length > 0;
30062
31485
  const pageContainer =
30063
- (hasRouteSlots ? document.getElementById("__farm_page__") : null) || currentRoot;
30064
- const wrappedElement = hasRouteSlots
31486
+ document.getElementById("__farm_page__") || currentRoot;
31487
+ const wrappedElement = pageContainer.id === "__farm_page__"
30065
31488
  ? pageElement
30066
31489
  : wrapWithLayouts(pageElement, newPathname, params);
30067
31490
  reactRoot = hydrateRoot(pageContainer, wrappedElement);
31491
+ reactRootContainer = pageContainer;
30068
31492
  isHydrated = true;
30069
31493
  }
30070
31494
  return true;
@@ -30355,6 +31779,10 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
30355
31779
  i18nCatalogs
30356
31780
  );
30357
31781
  const virtualEntryId = "\0virtual:farm-ssr-entry";
31782
+ const useExternalMetadataImageRuntime = shouldUseExternalMetadataImageRuntime(
31783
+ preset,
31784
+ metadataImageRoutes.some((image) => image.sourceType === "module")
31785
+ );
30358
31786
  try {
30359
31787
  await viteBuild({
30360
31788
  root,
@@ -30375,6 +31803,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
30375
31803
  external: [
30376
31804
  "fsevents",
30377
31805
  "sharp",
31806
+ ...useExternalMetadataImageRuntime ? ["@vercel/og"] : [],
30378
31807
  "@prisma/client",
30379
31808
  "@prisma/client/default",
30380
31809
  "@prisma/client/default.js",
@@ -30403,6 +31832,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
30403
31832
  external: [
30404
31833
  "fsevents",
30405
31834
  "sharp",
31835
+ ...useExternalMetadataImageRuntime ? ["@vercel/og"] : [],
30406
31836
  "esbuild",
30407
31837
  "lightningcss",
30408
31838
  "rollup",
@@ -30429,7 +31859,12 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
30429
31859
  // React runtime. Leaving a React component package external can make it
30430
31860
  // resolve a second React instance at runtime and break hooks during SSR.
30431
31861
  // Native and build-only packages above remain explicitly external.
30432
- noExternal: preset === "cloudflare-module" ? ["@farm.js/core", "@farm.js/core/image", "better-call"] : true
31862
+ noExternal: preset === "cloudflare-module" ? [
31863
+ "@farm.js/core",
31864
+ "@farm.js/core/image",
31865
+ "better-call",
31866
+ ...metadataImageRoutes.some((image) => image.sourceType === "module") ? ["@vercel/og"] : []
31867
+ ] : true
30433
31868
  },
30434
31869
  define: {
30435
31870
  __FARM_ENV__: JSON.stringify(config.env || { server: {}, public: {} }),
@@ -30437,6 +31872,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
30437
31872
  },
30438
31873
  plugins: [
30439
31874
  ...tailwindVitePlugin ? [tailwindVitePlugin] : [],
31875
+ ...!useExternalMetadataImageRuntime && metadataImageRoutes.some((image) => image.sourceType === "module") ? [createMetadataImageWasmPlugin()] : [],
30440
31876
  {
30441
31877
  name: "farm-react-production-mode",
30442
31878
  enforce: "pre",
@@ -30515,8 +31951,12 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
30515
31951
  __name(buildSSRInMemory, "buildSSRInMemory");
30516
31952
  function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlots, errorRoutes, metadataImageRoutes, middlewareRoutes, redirectRoutes, configuredRewriteRoutes, configuredHeaderRoutes, notFoundPath, config, configModulePath, hasServerRuntimeIntegrations, hasRuntimeIntegrationConfig, hasConfiguredRuntimePlugins, preset, i18nCatalogs) {
30517
31953
  const hasPluginRuntime = hasRuntimeIntegrationConfig || hasConfiguredRuntimePlugins;
31954
+ const hasGeneratedMetadataImages = metadataImageRoutes.some(
31955
+ (image) => image.sourceType === "module"
31956
+ );
30518
31957
  const hasMiddlewareRuntime = middlewareRoutes.length > 0 || hasFarmMiddlewareConfig(config.middleware);
30519
31958
  const farmDocsBaseConfig = config.docs?.enabled ? { ...config.docs, config: void 0 } : void 0;
31959
+ const farmDocsFontAssets = config.docs?.enabled ? toFarmDocsPublicFontAssets(resolveFarmDocsFontAssets(config.root)) : [];
30520
31960
  const apiImports = [];
30521
31961
  const apiRegistrations = [];
30522
31962
  apiRoutes.forEach((route, index) => {
@@ -30555,12 +31995,13 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlot
30555
31995
  return;
30556
31996
  }
30557
31997
  pageImports.push(`import * as ${varName} from "${route.modulePath}";`);
30558
- const shouldHydrate = getClientModuleMetadata(route.modulePath, config.root).shouldHydrate;
31998
+ const clientMetadata = getClientModuleMetadata(route.modulePath, config.root);
30559
31999
  pageRegistrations.push(`
30560
32000
  {
30561
32001
  pattern: ${JSON.stringify(route.pattern)},
30562
32002
  module: ${varName},
30563
- shouldHydrate: ${JSON.stringify(shouldHydrate)},
32003
+ shouldHydrate: ${JSON.stringify(clientMetadata.shouldHydrate)},
32004
+ islandStrategy: ${JSON.stringify(clientMetadata.islandStrategy)},
30564
32005
  ${route.source !== void 0 ? `markdownSource: {
30565
32006
  source: ${JSON.stringify(route.source)},
30566
32007
  filePath: ${JSON.stringify(route.markdownSourcePath)},
@@ -30658,16 +32099,21 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlot
30658
32099
  isFarmRedirectError,
30659
32100
  localizeFarmHref,
30660
32101
  localizeFarmPathname,
32102
+ manageFarmDocumentPreloads,
32103
+ manageFarmLinkHeaderPreloads,
30661
32104
  mergeMetadata,
30662
32105
  normalizeRevalidatePath,
32106
+ reportFarmPreloadWarnings,
30663
32107
  renderMetadataHead,
30664
32108
  resolveFarmRouteContext,
30665
32109
  stripFarmLocaleFromPathname,
30666
32110
  withFarmRouteContext,
30667
32111
  } from "@farm.js/core/internal/production-runtime";`;
32112
+ const metadataImageRuntimeImport = hasGeneratedMetadataImages ? `import { createFarmMetadataImageResponse } from "@farm.js/core/internal/metadata-image-runtime";` : "";
30668
32113
  const pluginRuntimeImport = hasPluginRuntime ? `import { PluginManager } from "@farm.js/core/plugin";` : "";
30669
32114
  const i18nServerImport = config.i18n.enabled ? `import { _runWithFarmI18nRequest, _setDefaultFarmI18nRuntime, createFarmI18nRuntime, getFarmI18nClientSnapshot } from "@farm.js/core/i18n/server";` : "";
30670
32115
  const docsHandlerImport = config.docs?.enabled ? `import { createFarmDocsAPIHandler, createFarmDocsHandler, isFarmDocsAPIRequest } from "@farm.js/core/docs";` : "";
32116
+ const docsFontImport = config.docs?.enabled ? `import { resolveFarmLayoutFonts } from "@farm.js/core/font";` : "";
30671
32117
  const docsRuntimeImport = config.docs?.enabled ? `import { existsSync as farmDocsExistsSync } from "node:fs";
30672
32118
  import { dirname as farmDocsDirname, join as farmDocsJoin } from "node:path";
30673
32119
  import { fileURLToPath as farmDocsFileURLToPath } from "node:url";` : "";
@@ -30755,9 +32201,11 @@ ${middlewareImports.join("\n")}
30755
32201
  ${notFoundImport}
30756
32202
  ${apiRouteHelpersImport}
30757
32203
  ${productionRuntimeImport}
32204
+ ${metadataImageRuntimeImport}
30758
32205
  ${pluginRuntimeImport}
30759
32206
  ${i18nServerImport}
30760
32207
  ${docsHandlerImport}
32208
+ ${docsFontImport}
30761
32209
  ${docsRuntimeImport}
30762
32210
  ${markdownHandlerImport}
30763
32211
  ${appMarkdownImport}
@@ -30769,6 +32217,8 @@ import { farmFontPreloadHeader } from "virtual:farm-font-runtime";
30769
32217
  import * as React from "react";
30770
32218
  import * as ReactDOMServer from "react-dom/server";
30771
32219
 
32220
+ const farmPreloadConfig = ${JSON.stringify(config.performance.preload)};
32221
+
30772
32222
  // Custom 404 page component (if provided)
30773
32223
  const hasCustomNotFound = ${notFoundPath ? "true" : "false"};
30774
32224
  const CustomNotFoundComponent = ${notFoundPath ? "CustomNotFound.default || CustomNotFound" : "null"};
@@ -30860,7 +32310,17 @@ globalThis.__FARM_DOCS_RUNTIME_CONFIG__ = {
30860
32310
  srcDir: ${JSON.stringify(config.srcDir)},
30861
32311
  docs: farmDocsResolvedConfig,
30862
32312
  };
30863
- const farmDocsHandler = ${config.docs?.enabled ? `createFarmDocsHandler(farmDocsResolvedConfig, { root: farmDocsRuntimeRoot, srcDir: ${JSON.stringify(config.srcDir)}, clientEntry: "/farm-client.js" })` : "null"};
32313
+ const farmDocsHandler = ${config.docs?.enabled ? `createFarmDocsHandler(farmDocsResolvedConfig, {
32314
+ root: farmDocsRuntimeRoot,
32315
+ srcDir: ${JSON.stringify(config.srcDir)},
32316
+ clientEntry: "/farm-client.js",
32317
+ fontAssets: ${JSON.stringify(farmDocsFontAssets)},
32318
+ resolveLayoutFonts: (pathname) =>
32319
+ resolveFarmLayoutFonts(
32320
+ getApplicableLayouts(getFarmRoutePathname(pathname)).map((layout) => layout.module),
32321
+ ),
32322
+ fontStylesheetHref: "/farm-fonts.css",
32323
+ })` : "null"};
30864
32324
  const farmDocsAPIHandler = ${config.docs?.enabled ? `createFarmDocsAPIHandler({ rootDir: farmDocsRuntimeRoot, srcDir: ${JSON.stringify(config.srcDir)}, docs: farmDocsResolvedConfig })` : "null"};
30865
32325
 
30866
32326
  // API routes bundled at build time
@@ -31521,30 +32981,10 @@ async function handleMetadataImageRequest(request, routePathname) {
31521
32981
  ? await imageModule.default(props)
31522
32982
  : imageModule.default;
31523
32983
 
31524
- if (value instanceof Response) {
31525
- return method === "HEAD"
31526
- ? new Response(null, { status: value.status, headers: value.headers })
31527
- : value;
31528
- }
31529
-
31530
- let body;
31531
- if (typeof value === "string" || value instanceof ArrayBuffer || ArrayBuffer.isView(value)) {
31532
- body = value;
31533
- } else {
31534
- if (!React.isValidElement(value)) {
31535
- throw new Error("Metadata image must return a Response, string, bytes, or React element");
31536
- }
31537
- body = ReactDOMServer.renderToStaticMarkup(value);
31538
- }
31539
-
31540
- return new Response(method === "HEAD" ? null : body, {
31541
- status: 200,
31542
- headers: {
31543
- "Content-Type": imageModule.contentType || "image/svg+xml; charset=utf-8",
31544
- "Cache-Control": "public, max-age=0, must-revalidate",
31545
- "X-Content-Type-Options": "nosniff",
31546
- },
31547
- });
32984
+ ${hasGeneratedMetadataImages ? `return createFarmMetadataImageResponse(value, imageModule, {
32985
+ method,
32986
+ ifNoneMatch: request.headers.get("if-none-match"),
32987
+ });` : 'throw new Error("Generated metadata image runtime is unavailable");'}
31548
32988
  } catch (error) {
31549
32989
  console.error("Metadata image render failed:", error);
31550
32990
  return new Response("Internal Server Error", {
@@ -32080,7 +33520,16 @@ async function handleFarmRequestInContext(
32080
33520
  if (farmDocsHandler) {
32081
33521
  const docsResponse = await farmDocsHandler(request.clone());
32082
33522
  if (docsResponse) {
32083
- return docsResponse;
33523
+ if (!docsResponse.headers.get("content-type")?.toLowerCase().includes("text/html")) {
33524
+ return docsResponse;
33525
+ }
33526
+ const docsHeaders = new Headers(docsResponse.headers);
33527
+ docsHeaders.set("x-farm-preload-buffered", "1");
33528
+ return new Response(docsResponse.body, {
33529
+ status: docsResponse.status,
33530
+ statusText: docsResponse.statusText,
33531
+ headers: docsHeaders,
33532
+ });
32084
33533
  }
32085
33534
  }
32086
33535
  ` : ""}
@@ -32208,6 +33657,7 @@ async function handleFarmRequestInContext(
32208
33657
  status: 200,
32209
33658
  headers: {
32210
33659
  "Content-Type": "text/html; charset=utf-8",
33660
+ "x-farm-preload-buffered": "1",
32211
33661
  ...(farmFontPreloadHeader ? { "Link": farmFontPreloadHeader } : {}),
32212
33662
  ...getPPRHeaders("hit", pprConfig),
32213
33663
  },
@@ -32435,10 +33885,15 @@ async function handleFarmRequestInContext(
32435
33885
  pageElement = React.createElement(PageComponent, pageProps);
32436
33886
  }
32437
33887
 
32438
- if (route.shouldHydrate && renderedRouteSlots.length > 0) {
33888
+ if (route.shouldHydrate) {
32439
33889
  pageElement = React.createElement(
32440
33890
  "div",
32441
- { id: "__farm_page__", "data-farm-client": "true" },
33891
+ {
33892
+ id: "__farm_page__",
33893
+ "data-farm-client": "true",
33894
+ "data-farm-island": "page",
33895
+ "data-farm-island-strategy": route.islandStrategy || "load",
33896
+ },
32442
33897
  pageElement,
32443
33898
  );
32444
33899
  }
@@ -32589,7 +34044,7 @@ async function handleFarmRequestInContext(
32589
34044
  );
32590
34045
  return applyProductionMiddlewareHeaders(new Response(streamedDocument, {
32591
34046
  status: pageStatus,
32592
- headers: responseHeaders,
34047
+ headers: { ...responseHeaders, "x-farm-preload-streaming": "1" },
32593
34048
  }), middlewareHeaders);
32594
34049
  }
32595
34050
 
@@ -32690,7 +34145,7 @@ async function handleFarmRequestInContext(
32690
34145
  fullHtml,
32691
34146
  {
32692
34147
  status: pageStatus,
32693
- headers: responseHeaders
34148
+ headers: { ...responseHeaders, "x-farm-preload-buffered": "1" }
32694
34149
  }
32695
34150
  ), middlewareHeaders);
32696
34151
  }
@@ -32794,6 +34249,7 @@ async function handleFarmRequestInContext(
32794
34249
  headers: {
32795
34250
  "Content-Type": "text/html; charset=utf-8",
32796
34251
  "Cache-Control": "private, no-store",
34252
+ "x-farm-preload-buffered": "1",
32797
34253
  },
32798
34254
  }), middlewareHeaders);
32799
34255
  } catch (boundaryError) {
@@ -32816,6 +34272,7 @@ async function handleFarmRequestInContext(
32816
34272
  headers: {
32817
34273
  "Content-Type": "text/html; charset=utf-8",
32818
34274
  "Cache-Control": "private, no-store",
34275
+ "x-farm-preload-buffered": "1",
32819
34276
  },
32820
34277
  }
32821
34278
  ), middlewareHeaders);
@@ -32962,13 +34419,19 @@ async function handleFarmRequestInContext(
32962
34419
 
32963
34420
  return applyProductionMiddlewareHeaders(new Response(fullHtml, {
32964
34421
  status: 404,
32965
- headers: { "Content-Type": "text/html; charset=utf-8" }
34422
+ headers: {
34423
+ "Content-Type": "text/html; charset=utf-8",
34424
+ "x-farm-preload-buffered": "1",
34425
+ }
32966
34426
  }), middlewareHeaders);
32967
34427
  } catch (error) {
32968
34428
  console.error("404 render error:", error);
32969
34429
  return applyProductionMiddlewareHeaders(new Response(
32970
34430
  \`<!DOCTYPE html><html><head><title>404</title></head><body><h1>404 - Page Not Found</h1><p>The page \${pathname} doesn't exist.</p><a href="/">Go Home</a></body></html>\`,
32971
- { status: 404, headers: { "Content-Type": "text/html" } }
34431
+ {
34432
+ status: 404,
34433
+ headers: { "Content-Type": "text/html", "x-farm-preload-buffered": "1" },
34434
+ }
32972
34435
  ), middlewareHeaders);
32973
34436
  }
32974
34437
  ${hasMiddlewareRuntime ? `
@@ -33010,7 +34473,10 @@ async function handleFarmPluginRequest(request, runtimeOptions) {
33010
34473
  await farmPluginRuntime.runHookParallel("beforeRender", renderPayload);
33011
34474
 
33012
34475
  const response = await handleFarmRequest(request);
33013
- if (!hasFarmPluginHTMLTransforms || !response.headers.get("content-type")?.includes("text/html")) {
34476
+ if (
34477
+ !hasFarmPluginHTMLTransforms ||
34478
+ !response.headers.get("content-type")?.toLowerCase().includes("text/html")
34479
+ ) {
33014
34480
  return response;
33015
34481
  }
33016
34482
 
@@ -33020,6 +34486,8 @@ async function handleFarmPluginRequest(request, runtimeOptions) {
33020
34486
 
33021
34487
  const headers = new Headers(response.headers);
33022
34488
  headers.delete("content-length");
34489
+ headers.delete("x-farm-preload-streaming");
34490
+ headers.set("x-farm-preload-buffered", "1");
33023
34491
  return new Response(html, {
33024
34492
  status: response.status,
33025
34493
  statusText: response.statusText,
@@ -33027,6 +34495,49 @@ async function handleFarmPluginRequest(request, runtimeOptions) {
33027
34495
  });
33028
34496
  }
33029
34497
 
34498
+ async function applyFarmPreloadBudget(response, pathname) {
34499
+ const headers = new Headers(response.headers);
34500
+ const linkHeader = headers.get("Link") || "";
34501
+ const isHtml = headers.get("Content-Type")?.toLowerCase().includes("text/html");
34502
+ const isStreaming = headers.get("x-farm-preload-streaming") === "1";
34503
+ const isBuffered = headers.get("x-farm-preload-buffered") === "1";
34504
+ headers.delete("x-farm-preload-streaming");
34505
+ headers.delete("x-farm-preload-buffered");
34506
+
34507
+ if (!isHtml) {
34508
+ if (!isStreaming && !isBuffered) return response;
34509
+ return new Response(response.body, {
34510
+ status: response.status,
34511
+ statusText: response.statusText,
34512
+ headers,
34513
+ });
34514
+ }
34515
+
34516
+ if (isStreaming || !isBuffered || response.body === null) {
34517
+ const managed = manageFarmLinkHeaderPreloads(linkHeader, farmPreloadConfig);
34518
+ if (managed.value) headers.set("Link", managed.value);
34519
+ else headers.delete("Link");
34520
+ reportFarmPreloadWarnings(managed.warnings, "route " + pathname);
34521
+ return new Response(response.body, {
34522
+ status: response.status,
34523
+ statusText: response.statusText,
34524
+ headers,
34525
+ });
34526
+ }
34527
+
34528
+ const html = await response.text();
34529
+ const managed = manageFarmDocumentPreloads(html, linkHeader, farmPreloadConfig);
34530
+ if (managed.linkHeader) headers.set("Link", managed.linkHeader);
34531
+ else headers.delete("Link");
34532
+ if (managed.html !== html) headers.delete("Content-Length");
34533
+ reportFarmPreloadWarnings(managed.warnings, "route " + pathname);
34534
+ return new Response(managed.html, {
34535
+ status: response.status,
34536
+ statusText: response.statusText,
34537
+ headers,
34538
+ });
34539
+ }
34540
+
33030
34541
  // Export as Web Standard fetch API
33031
34542
  export async function fetch(request, context) {
33032
34543
  const runtimeOptions = farmPluginRuntime ? getFarmPluginRequestOptions(request) : null;
@@ -33049,7 +34560,8 @@ export async function fetch(request, context) {
33049
34560
  const response = await _runWithCurrentRequest(request, () =>
33050
34561
  _runWithAfterRequest(request, runRequest, context),
33051
34562
  );
33052
- return applyConfiguredResponseHeaders(response, new URL(request.url).pathname);
34563
+ const pathname = new URL(request.url).pathname;
34564
+ return applyFarmPreloadBudget(applyConfiguredResponseHeaders(response, pathname), pathname);
33053
34565
  }
33054
34566
  export default { fetch };
33055
34567
  `.trim();
@@ -33205,8 +34717,19 @@ async function buildNitroUniversal(config, routeManager, apiRouteManager, server
33205
34717
  const outputDir = resolveDeployOutputPath(root, config.deploy.outputDir);
33206
34718
  const ssrOutputDir = import_path12.default.join(root, distDir, "ssr");
33207
34719
  const imageRuntime = resolveImageRuntime(config, preset);
34720
+ const hasGeneratedMetadataImages = Array.from(routeManager.getMetadataImages().values()).some(
34721
+ (image) => image.sourceType === "module"
34722
+ );
34723
+ const useExternalMetadataImageRuntime = shouldUseExternalMetadataImageRuntime(
34724
+ preset,
34725
+ hasGeneratedMetadataImages
34726
+ );
34727
+ const nitroRollupExternal = /* @__PURE__ */ __name((id) => useExternalMetadataImageRuntime && id === "@vercel/og" || isNitroRollupExternal(id), "nitroRollupExternal");
33208
34728
  const ssrExternalPackages = collectSSRExternalPackages(ssrBundle);
33209
- const copiedRuntimePackages = new Set(imageRuntime === "node" ? ["sharp"] : []);
34729
+ const copiedRuntimePackages = /* @__PURE__ */ new Set([
34730
+ ...imageRuntime === "node" ? ["sharp"] : [],
34731
+ ...useExternalMetadataImageRuntime ? ["@vercel/og"] : []
34732
+ ]);
33210
34733
  const isPrebuiltSSRCandidate = preset === "node-server" && [...ssrExternalPackages].every((packageName) => copiedRuntimePackages.has(packageName));
33211
34734
  logger.info(`\u{1F4E6} Nitro output directory: ${outputDir}`);
33212
34735
  logger.info(`\u{1F4E6} SSR entry file: ${ssrEntryFile}`);
@@ -33422,11 +34945,13 @@ export default async function farmNitroEventHandler(event) {
33422
34945
  ".prisma/client",
33423
34946
  ".prisma/client/default",
33424
34947
  "better-sqlite3",
33425
- "sharp"
34948
+ "sharp",
34949
+ ...useExternalMetadataImageRuntime ? ["@vercel/og"] : []
33426
34950
  ]
33427
34951
  },
33428
34952
  rollupConfig: {
33429
- external: isNitroRollupExternal
34953
+ external: nitroRollupExternal,
34954
+ plugins: hasGeneratedMetadataImages && !useExternalMetadataImageRuntime ? [createMetadataImageWasmPlugin()] : []
33430
34955
  },
33431
34956
  // Keep the public Nitro boolean intact for build hooks. It is translated to
33432
34957
  // Nitro's existing esbuild pass after hooks run, avoiding a second Terser pass.
@@ -33454,7 +34979,7 @@ export default async function farmNitroEventHandler(event) {
33454
34979
  snapshotSSRRebundleOptions(nitroConfig)
33455
34980
  );
33456
34981
  const requestedBuilder = nitroConfig.builder ?? process.env.NITRO_BUILDER;
33457
- const canReusePrebuiltSSR = isPrebuiltSSRCandidate && nitroConfig.preset === "node-server" && (requestedBuilder === void 0 || requestedBuilder === "rollup" || requestedBuilder === "rolldown") && nitroConfig.sourceMap === false && !hasUnsupportedEsbuildOverrides && !hasLateBuildMutationConfig && !hasUnsupportedSSRRebundleOverrides && nitroConfig.rollupConfig?.external === isNitroRollupExternal && !hasRollupPlugins(nitroConfig.rollupConfig?.plugins) && customRollupKeys.length === 0 && import_path12.default.resolve(nitroConfig.output?.serverDir || "") === import_path12.default.resolve(expectedServerDir) && Boolean(
34982
+ const canReusePrebuiltSSR = isPrebuiltSSRCandidate && nitroConfig.preset === "node-server" && (requestedBuilder === void 0 || requestedBuilder === "rollup" || requestedBuilder === "rolldown") && nitroConfig.sourceMap === false && !hasUnsupportedEsbuildOverrides && !hasLateBuildMutationConfig && !hasUnsupportedSSRRebundleOverrides && nitroConfig.rollupConfig?.external === nitroRollupExternal && !hasRollupPlugins(nitroConfig.rollupConfig?.plugins) && customRollupKeys.length === 0 && import_path12.default.resolve(nitroConfig.output?.serverDir || "") === import_path12.default.resolve(expectedServerDir) && Boolean(
33458
34983
  nitroConfig.handlers?.some(
33459
34984
  (handler) => import_path12.default.resolve(handler?.handler || "") === nitroEntryPath
33460
34985
  )
@@ -33542,7 +35067,8 @@ export default async function farmNitroEventHandler(event) {
33542
35067
  effectiveMinify,
33543
35068
  fs8
33544
35069
  ) : Promise.resolve(),
33545
- imageRuntime === "node" ? copySharpRuntime(config, root, import_path12.default.join(outputDir, "server"), fs8) : Promise.resolve()
35070
+ imageRuntime === "node" ? copySharpRuntime(config, root, import_path12.default.join(outputDir, "server"), fs8) : Promise.resolve(),
35071
+ useExternalMetadataImageRuntime ? copyMetadataImageRuntime(root, import_path12.default.join(outputDir, "server"), fs8) : Promise.resolve()
33546
35072
  ]);
33547
35073
  const failedCopy = copyResults.find(
33548
35074
  (result) => result.status === "rejected"
@@ -33654,6 +35180,9 @@ async function postProcessVercelOutput(root, outputDir, fs8, config, farmWorkflo
33654
35180
  fs8
33655
35181
  );
33656
35182
  vercelConfig.routes = [
35183
+ // Apply the header before the filesystem handler. `continue` lets Vercel
35184
+ // serve the matching file while preserving the immutable cache policy.
35185
+ createFarmVercelImmutableAssetRoute(config.basePath),
33657
35186
  // Serve static files first
33658
35187
  {
33659
35188
  handle: "filesystem"
@@ -33771,6 +35300,42 @@ async function copySharpRuntime(config, root, nitroFuncDir, fs8) {
33771
35300
  logger.info(`\u{1F5BC}\uFE0F Bundled Sharp image runtime (${copiedPackages.size} packages)`);
33772
35301
  }
33773
35302
  __name(copySharpRuntime, "copySharpRuntime");
35303
+ async function copyMetadataImageRuntime(root, nitroFuncDir, fs8) {
35304
+ const projectRequire = (0, import_module2.createRequire)(import_path12.default.join(root, "package.json"));
35305
+ let runtimeRequire = projectRequire;
35306
+ let packageJsonPath = resolvePackageJson(runtimeRequire, "@vercel/og");
35307
+ if (!packageJsonPath) {
35308
+ try {
35309
+ runtimeRequire = (0, import_module2.createRequire)(projectRequire.resolve("@farm.js/core"));
35310
+ packageJsonPath = resolvePackageJson(runtimeRequire, "@vercel/og");
35311
+ } catch {
35312
+ }
35313
+ }
35314
+ if (!packageJsonPath) {
35315
+ throw new Error(
35316
+ "Generated metadata images require @vercel/og. Reinstall @farm.js/core with production dependencies."
35317
+ );
35318
+ }
35319
+ const packageJson = JSON.parse(await fs8.readFile(packageJsonPath, "utf8"));
35320
+ const packageDir = import_path12.default.dirname(packageJsonPath);
35321
+ const targetDir = import_path12.default.join(nitroFuncDir, "node_modules", "@vercel", "og");
35322
+ await fs8.mkdir(import_path12.default.dirname(targetDir), { recursive: true });
35323
+ await fs8.cp(packageDir, targetDir, {
35324
+ recursive: true,
35325
+ force: true,
35326
+ dereference: true,
35327
+ mode: import_fs5.constants.COPYFILE_FICLONE
35328
+ });
35329
+ const functionPackagePath = import_path12.default.join(nitroFuncDir, "package.json");
35330
+ const functionPackage = JSON.parse(await fs8.readFile(functionPackagePath, "utf8"));
35331
+ functionPackage.dependencies = {
35332
+ ...functionPackage.dependencies,
35333
+ "@vercel/og": String(packageJson.version || "0.11.1")
35334
+ };
35335
+ await fs8.writeFile(functionPackagePath, JSON.stringify(functionPackage, null, 2));
35336
+ logger.info("\u{1F5BC}\uFE0F Bundled generated metadata image runtime");
35337
+ }
35338
+ __name(copyMetadataImageRuntime, "copyMetadataImageRuntime");
33774
35339
  function resolvePackageJson(parentRequire, packageName) {
33775
35340
  for (const request of [`${packageName}/package.json`, `${packageName}/package`]) {
33776
35341
  try {
@@ -33830,54 +35395,7 @@ init_type_artifacts();
33830
35395
  init_plugin();
33831
35396
  init_environment_vite();
33832
35397
  init_font_vite();
33833
-
33834
- // src/server/vite-config.ts
33835
- function mergeNoExternal(farmValue, userValue) {
33836
- if (farmValue === true || userValue === true) return true;
33837
- const normalize = /* @__PURE__ */ __name((value) => value == null || value === true ? [] : Array.isArray(value) ? value : [value], "normalize");
33838
- return Array.from(/* @__PURE__ */ new Set([...normalize(farmValue), ...normalize(userValue)]));
33839
- }
33840
- __name(mergeNoExternal, "mergeNoExternal");
33841
- function mergeFarmViteConfig(farmConfig, userConfig = {}) {
33842
- return {
33843
- ...farmConfig,
33844
- ...userConfig,
33845
- plugins: [...farmConfig.plugins || [], ...userConfig.plugins || []],
33846
- server: {
33847
- ...farmConfig.server,
33848
- ...userConfig.server
33849
- },
33850
- resolve: {
33851
- ...farmConfig.resolve,
33852
- ...userConfig.resolve,
33853
- dedupe: Array.from(
33854
- /* @__PURE__ */ new Set([...farmConfig.resolve?.dedupe || [], ...userConfig.resolve?.dedupe || []])
33855
- )
33856
- },
33857
- optimizeDeps: {
33858
- ...farmConfig.optimizeDeps,
33859
- ...userConfig.optimizeDeps,
33860
- include: Array.from(
33861
- /* @__PURE__ */ new Set([
33862
- ...farmConfig.optimizeDeps?.include || [],
33863
- ...userConfig.optimizeDeps?.include || []
33864
- ])
33865
- ),
33866
- exclude: Array.from(
33867
- /* @__PURE__ */ new Set([
33868
- ...farmConfig.optimizeDeps?.exclude || [],
33869
- ...userConfig.optimizeDeps?.exclude || []
33870
- ])
33871
- )
33872
- },
33873
- ssr: {
33874
- ...farmConfig.ssr,
33875
- ...userConfig.ssr,
33876
- noExternal: mergeNoExternal(farmConfig.ssr?.noExternal, userConfig.ssr?.noExternal)
33877
- }
33878
- };
33879
- }
33880
- __name(mergeFarmViteConfig, "mergeFarmViteConfig");
35398
+ init_vite_config();
33881
35399
 
33882
35400
  // src/build/production-node-env.ts
33883
35401
  var productionNodeEnvStateKey = /* @__PURE__ */ Symbol.for("@farm.js/core/production-node-env-state");