@farm.js/core 0.1.0-beta.3 → 0.1.0-beta.5

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 (193) hide show
  1. package/dist/agent-runtime.d.mts +2 -1
  2. package/dist/agent-runtime.d.ts +2 -1
  3. package/dist/api/route-manager.cjs +313 -10
  4. package/dist/api/route-manager.cjs.map +1 -1
  5. package/dist/api/route-manager.d.mts +3 -2
  6. package/dist/api/route-manager.d.ts +3 -2
  7. package/dist/api/route-manager.mjs +6 -5
  8. package/dist/api/runtime.cjs +313 -10
  9. package/dist/api/runtime.cjs.map +1 -1
  10. package/dist/api/runtime.d.mts +2 -1
  11. package/dist/api/runtime.d.ts +2 -1
  12. package/dist/api/runtime.mjs +4 -3
  13. package/dist/api.cjs +642 -57
  14. package/dist/api.cjs.map +1 -1
  15. package/dist/api.d.mts +3 -2
  16. package/dist/api.d.ts +3 -2
  17. package/dist/api.mjs +29 -9
  18. package/dist/{app-DOKxo-LH.d.ts → app-BjN-da13.d.ts} +38 -3
  19. package/dist/{app-BB3Iu3xh.d.mts → app-CakAr8Be.d.mts} +38 -3
  20. package/dist/app-markdown.d.mts +2 -1
  21. package/dist/app-markdown.d.ts +2 -1
  22. package/dist/cache.cjs +395 -12
  23. package/dist/cache.cjs.map +1 -1
  24. package/dist/cache.d.mts +100 -6
  25. package/dist/cache.d.ts +100 -6
  26. package/dist/cache.mjs +8 -2
  27. package/dist/{chunk-XUBZ7JI4.mjs → chunk-2CUKEMVR.mjs} +187 -16
  28. package/dist/chunk-2CUKEMVR.mjs.map +1 -0
  29. package/dist/{chunk-OHC6VCDI.mjs → chunk-2NHCNRF6.mjs} +521 -42
  30. package/dist/chunk-2NHCNRF6.mjs.map +1 -0
  31. package/dist/{chunk-6WXJ54TC.mjs → chunk-65A7QIKI.mjs} +5 -5
  32. package/dist/{chunk-6WXJ54TC.mjs.map → chunk-65A7QIKI.mjs.map} +1 -1
  33. package/dist/{chunk-5QH4IKY3.mjs → chunk-7N62DNSO.mjs} +2 -2
  34. package/dist/{chunk-XJSHJN4M.mjs → chunk-A2LUXHD2.mjs} +593 -49
  35. package/dist/chunk-A2LUXHD2.mjs.map +1 -0
  36. package/dist/chunk-ALCLXXTY.mjs +187 -0
  37. package/dist/chunk-ALCLXXTY.mjs.map +1 -0
  38. package/dist/{chunk-CSU4KJJF.mjs → chunk-C2ROET2V.mjs} +2 -2
  39. package/dist/{chunk-JNYVN7YP.mjs → chunk-EEABSBKK.mjs} +2 -2
  40. package/dist/{chunk-JNYVN7YP.mjs.map → chunk-EEABSBKK.mjs.map} +1 -1
  41. package/dist/{chunk-BIOIOWXZ.mjs → chunk-HNIQFZKB.mjs} +2 -2
  42. package/dist/{chunk-MH6FKW2I.mjs → chunk-J5YWQVI5.mjs} +4 -4
  43. package/dist/chunk-J5YWQVI5.mjs.map +1 -0
  44. package/dist/{chunk-G5I34UA6.mjs → chunk-JDEOUK5V.mjs} +4 -4
  45. package/dist/{chunk-T543TMFY.mjs → chunk-NM3CRAAL.mjs} +3 -2
  46. package/dist/chunk-NM3CRAAL.mjs.map +1 -0
  47. package/dist/{chunk-LWMGIEJA.mjs → chunk-PFE6GMNE.mjs} +47 -37
  48. package/dist/chunk-PFE6GMNE.mjs.map +1 -0
  49. package/dist/{chunk-6VS6CKAV.mjs → chunk-QM746KUO.mjs} +8 -1
  50. package/dist/chunk-QM746KUO.mjs.map +1 -0
  51. package/dist/{chunk-5FW6PQ3J.mjs → chunk-QTZZ2ISQ.mjs} +41 -16
  52. package/dist/chunk-QTZZ2ISQ.mjs.map +1 -0
  53. package/dist/{chunk-SHRJG6OG.mjs → chunk-RBRLLZL5.mjs} +18 -3
  54. package/dist/chunk-RBRLLZL5.mjs.map +1 -0
  55. package/dist/{chunk-NRRFGX45.mjs → chunk-TQAEEJB4.mjs} +3 -3
  56. package/dist/{chunk-45LKHSHJ.mjs → chunk-UB5UOSHB.mjs} +24 -12
  57. package/dist/chunk-UB5UOSHB.mjs.map +1 -0
  58. package/dist/chunk-V3XURYJO.mjs +756 -0
  59. package/dist/chunk-V3XURYJO.mjs.map +1 -0
  60. package/dist/{chunk-HZYXGV4K.mjs → chunk-VY2UYN5R.mjs} +24 -4
  61. package/dist/chunk-VY2UYN5R.mjs.map +1 -0
  62. package/dist/{chunk-C33RALN7.mjs → chunk-W2CRQFF6.mjs} +79 -15
  63. package/dist/chunk-W2CRQFF6.mjs.map +1 -0
  64. package/dist/{chunk-LG3YINQN.mjs → chunk-Z5CQMP54.mjs} +3 -3
  65. package/dist/{client-DiViK9yK.d.mts → client-BT1mFQas.d.mts} +23 -4
  66. package/dist/{client-DpEBJbts.d.ts → client-Nn0q4eO4.d.ts} +23 -4
  67. package/dist/{client-plugins-CTnsZz_A.d.ts → client-plugins-Cw4qUGsw.d.ts} +6 -0
  68. package/dist/{client-plugins-Cnr0D3kk.d.mts → client-plugins-DQQ1adXM.d.mts} +6 -0
  69. package/dist/client-plugins.d.mts +2 -1
  70. package/dist/client-plugins.d.ts +2 -1
  71. package/dist/client.cjs +665 -82
  72. package/dist/client.cjs.map +1 -1
  73. package/dist/client.d.mts +71 -5
  74. package/dist/client.d.ts +71 -5
  75. package/dist/client.mjs +10 -7
  76. package/dist/config.d.mts +2 -1
  77. package/dist/config.d.ts +2 -1
  78. package/dist/{devtools-HE4FTUDN.mjs → devtools-LVC3VB5P.mjs} +4 -4
  79. package/dist/docs.cjs +11 -1
  80. package/dist/docs.cjs.map +1 -1
  81. package/dist/docs.mjs +4 -4
  82. package/dist/index.cjs +2113 -198
  83. package/dist/index.cjs.map +1 -1
  84. package/dist/index.d.mts +16 -16
  85. package/dist/index.d.ts +16 -16
  86. package/dist/index.mjs +49 -21
  87. package/dist/index.mjs.map +1 -1
  88. package/dist/integrations.d.mts +2 -1
  89. package/dist/integrations.d.ts +2 -1
  90. package/dist/internal/build-runtime.cjs +1455 -90
  91. package/dist/internal/build-runtime.cjs.map +1 -1
  92. package/dist/internal/build-runtime.d.mts +4 -3
  93. package/dist/internal/build-runtime.d.ts +4 -3
  94. package/dist/internal/build-runtime.mjs +15 -14
  95. package/dist/internal/config-runtime.cjs +12 -1
  96. package/dist/internal/config-runtime.cjs.map +1 -1
  97. package/dist/internal/config-runtime.d.mts +3 -2
  98. package/dist/internal/config-runtime.d.ts +3 -2
  99. package/dist/internal/config-runtime.mjs +3 -3
  100. package/dist/internal/production-runtime.cjs +282 -6
  101. package/dist/internal/production-runtime.cjs.map +1 -1
  102. package/dist/internal/production-runtime.d.mts +4 -4
  103. package/dist/internal/production-runtime.d.ts +4 -4
  104. package/dist/internal/production-runtime.mjs +4 -2
  105. package/dist/{metadata-eoVmHoyv.d.ts → metadata-BAPg_Nmo.d.ts} +1 -1
  106. package/dist/{metadata-Dp8AdvsB.d.mts → metadata-_6i9oFG3.d.mts} +1 -1
  107. package/dist/middleware.d.mts +4 -3
  108. package/dist/middleware.d.ts +4 -3
  109. package/dist/navigation.cjs +40 -15
  110. package/dist/navigation.cjs.map +1 -1
  111. package/dist/navigation.mjs +2 -2
  112. package/dist/plugin.cjs +913 -53
  113. package/dist/plugin.cjs.map +1 -1
  114. package/dist/plugin.d.mts +2 -1
  115. package/dist/plugin.d.ts +2 -1
  116. package/dist/plugin.mjs +11 -10
  117. package/dist/{production-runtime-BqTnUTai.d.ts → production-runtime-BoJDc5mc.d.ts} +1 -1
  118. package/dist/{production-runtime-DtDdG9fc.d.mts → production-runtime-DPfgMV9Y.d.mts} +1 -1
  119. package/dist/routes.cjs +276 -6
  120. package/dist/routes.cjs.map +1 -1
  121. package/dist/routes.d.mts +1 -1
  122. package/dist/routes.d.ts +1 -1
  123. package/dist/routes.mjs +3 -3
  124. package/dist/{runtime-C1B7pSGT.d.mts → runtime-9nQjVM7X.d.mts} +8 -9
  125. package/dist/{runtime-CdPHt0bv.d.ts → runtime-DgeS2skB.d.ts} +8 -9
  126. package/dist/server-action-security.cjs +28 -10
  127. package/dist/server-action-security.cjs.map +1 -1
  128. package/dist/server-action-security.d.mts +2 -1
  129. package/dist/server-action-security.d.ts +2 -1
  130. package/dist/server-action-security.mjs +2 -2
  131. package/dist/server-fn-client.d.mts +1 -0
  132. package/dist/server-fn-client.d.ts +1 -0
  133. package/dist/server-fn.cjs +785 -3
  134. package/dist/server-fn.cjs.map +1 -1
  135. package/dist/server-fn.d.mts +10 -1
  136. package/dist/server-fn.d.ts +10 -1
  137. package/dist/server-fn.mjs +6 -3
  138. package/dist/server-plugins.d.mts +3 -2
  139. package/dist/server-plugins.d.ts +3 -2
  140. package/dist/server-query-client.cjs +279 -8
  141. package/dist/server-query-client.cjs.map +1 -1
  142. package/dist/server-query-client.mjs +4 -4
  143. package/dist/server-query.cjs +381 -10
  144. package/dist/server-query.cjs.map +1 -1
  145. package/dist/server-query.mjs +5 -5
  146. package/dist/server.cjs +1086 -76
  147. package/dist/server.cjs.map +1 -1
  148. package/dist/server.d.mts +8 -7
  149. package/dist/server.d.ts +8 -7
  150. package/dist/server.mjs +31 -16
  151. package/dist/server.mjs.map +1 -1
  152. package/dist/testing.cjs +341 -20
  153. package/dist/testing.cjs.map +1 -1
  154. package/dist/testing.d.mts +3 -2
  155. package/dist/testing.d.ts +3 -2
  156. package/dist/testing.mjs +7 -6
  157. package/dist/testing.mjs.map +1 -1
  158. package/dist/transport-BmbGTXA5.d.mts +49 -0
  159. package/dist/transport-BmbGTXA5.d.ts +49 -0
  160. package/dist/{utils-8USa5BiY.d.mts → utils-CHXlMDkg.d.mts} +1 -1
  161. package/dist/{utils-DKJb6K6G.d.ts → utils-Drsyz1Vj.d.ts} +1 -1
  162. package/dist/version.cjs +1 -1
  163. package/dist/version.cjs.map +1 -1
  164. package/dist/version.mjs +1 -1
  165. package/dist/vite.cjs +914 -54
  166. package/dist/vite.cjs.map +1 -1
  167. package/dist/vite.d.mts +2 -1
  168. package/dist/vite.d.ts +2 -1
  169. package/dist/vite.mjs +11 -10
  170. package/package.json +1 -1
  171. package/types/api.d.ts +44 -0
  172. package/types/client.d.ts +85 -4
  173. package/dist/chunk-45LKHSHJ.mjs.map +0 -1
  174. package/dist/chunk-5FW6PQ3J.mjs.map +0 -1
  175. package/dist/chunk-6VS6CKAV.mjs.map +0 -1
  176. package/dist/chunk-C33RALN7.mjs.map +0 -1
  177. package/dist/chunk-GZWT6VI5.mjs +0 -382
  178. package/dist/chunk-GZWT6VI5.mjs.map +0 -1
  179. package/dist/chunk-HZYXGV4K.mjs.map +0 -1
  180. package/dist/chunk-LWMGIEJA.mjs.map +0 -1
  181. package/dist/chunk-MH6FKW2I.mjs.map +0 -1
  182. package/dist/chunk-OHC6VCDI.mjs.map +0 -1
  183. package/dist/chunk-SHRJG6OG.mjs.map +0 -1
  184. package/dist/chunk-T543TMFY.mjs.map +0 -1
  185. package/dist/chunk-XJSHJN4M.mjs.map +0 -1
  186. package/dist/chunk-XUBZ7JI4.mjs.map +0 -1
  187. /package/dist/{chunk-5QH4IKY3.mjs.map → chunk-7N62DNSO.mjs.map} +0 -0
  188. /package/dist/{chunk-CSU4KJJF.mjs.map → chunk-C2ROET2V.mjs.map} +0 -0
  189. /package/dist/{chunk-BIOIOWXZ.mjs.map → chunk-HNIQFZKB.mjs.map} +0 -0
  190. /package/dist/{chunk-G5I34UA6.mjs.map → chunk-JDEOUK5V.mjs.map} +0 -0
  191. /package/dist/{chunk-NRRFGX45.mjs.map → chunk-TQAEEJB4.mjs.map} +0 -0
  192. /package/dist/{chunk-LG3YINQN.mjs.map → chunk-Z5CQMP54.mjs.map} +0 -0
  193. /package/dist/{devtools-HE4FTUDN.mjs.map → devtools-LVC3VB5P.mjs.map} +0 -0
@@ -42,7 +42,7 @@ import {
42
42
  } from "./chunk-GY2STC2S.mjs";
43
43
  import {
44
44
  resolveServerActionsConfig
45
- } from "./chunk-45LKHSHJ.mjs";
45
+ } from "./chunk-UB5UOSHB.mjs";
46
46
  import {
47
47
  createMarkdownMirrorResponse,
48
48
  resolveMarkdownConfig
@@ -91,7 +91,7 @@ import {
91
91
  discoverProgrammaticRoutePaths,
92
92
  findProgrammaticRouteFiles,
93
93
  loadProgrammaticRouteManifests
94
- } from "./chunk-LG3YINQN.mjs";
94
+ } from "./chunk-Z5CQMP54.mjs";
95
95
  import {
96
96
  _runWithFarmI18nRequest,
97
97
  _setDefaultFarmI18nRuntime,
@@ -115,7 +115,7 @@ import {
115
115
  parseProgrammaticRouteModuleId,
116
116
  parseProgrammaticRoutePath,
117
117
  scanProgrammaticPagePaths
118
- } from "./chunk-CSU4KJJF.mjs";
118
+ } from "./chunk-C2ROET2V.mjs";
119
119
  import {
120
120
  resolveFarmRouteContext,
121
121
  withFarmRouteContext
@@ -142,7 +142,7 @@ import {
142
142
  } from "./chunk-RHIFZZUI.mjs";
143
143
  import {
144
144
  FARM_VERSION
145
- } from "./chunk-JNYVN7YP.mjs";
145
+ } from "./chunk-EEABSBKK.mjs";
146
146
  import {
147
147
  FARM_DEPLOYMENT_ID_HEADER,
148
148
  createFarmDeploymentCookie,
@@ -161,10 +161,11 @@ import {
161
161
  stripFarmLocaleFromPathname
162
162
  } from "./chunk-44ZCFSKE.mjs";
163
163
  import {
164
+ configureFarmCache,
164
165
  createFarmCacheKey,
165
166
  getFarmDataCache,
166
167
  normalizeRevalidatePath
167
- } from "./chunk-GZWT6VI5.mjs";
168
+ } from "./chunk-V3XURYJO.mjs";
168
169
  import {
169
170
  configureFarmObservability,
170
171
  emitFarmEvent
@@ -778,6 +779,76 @@ async function readAltText(filePath) {
778
779
  }
779
780
  __name(readAltText, "readAltText");
780
781
 
782
+ // src/routing/route-slots.ts
783
+ var SLOT_SEGMENT = /^@([A-Za-z][A-Za-z0-9_-]*)$/;
784
+ function parseRouteSlotFile(filePath) {
785
+ const normalized = filePath.replace(/\\/g, "/");
786
+ const parts = normalized.split("/").filter(Boolean);
787
+ const fileName = parts.at(-1) ?? "";
788
+ const baseName = fileName.replace(/\.(tsx?|jsx?)$/, "");
789
+ if (baseName !== "page" && baseName !== "default") return null;
790
+ const slotIndex = parts.findIndex((part) => SLOT_SEGMENT.test(part));
791
+ if (slotIndex < 0) return null;
792
+ if (parts.slice(slotIndex + 1, -1).some((part) => SLOT_SEGMENT.test(part))) {
793
+ throw new Error(`Nested route slots are not supported in "${filePath}"`);
794
+ }
795
+ const slotMatch = parts[slotIndex].match(SLOT_SEGMENT);
796
+ const name = slotMatch?.[1];
797
+ if (!name) return null;
798
+ const ownerParts = parts.slice(0, slotIndex);
799
+ const contentParts = parts.slice(slotIndex + 1, -1);
800
+ const marker = parseInterceptionMarker(contentParts[0]);
801
+ let targetBase = ownerParts;
802
+ let targetParts = contentParts;
803
+ if (marker) {
804
+ targetBase = marker.root === true ? [] : ownerParts.slice(0, Math.max(0, ownerParts.length - marker.up));
805
+ targetParts = [...marker.remainder ? [marker.remainder] : [], ...contentParts.slice(1)];
806
+ }
807
+ const ownerFile = [...ownerParts, "layout.tsx"].join("/");
808
+ const routeFile = [
809
+ ...baseName === "default" ? ownerParts : targetBase,
810
+ ...baseName === "default" ? [] : targetParts,
811
+ "page.tsx"
812
+ ].join("/");
813
+ return {
814
+ name,
815
+ ownerRoute: parseRoutePath(ownerFile || "layout.tsx"),
816
+ route: parseRoutePath(routeFile || "page.tsx"),
817
+ interception: marker !== null,
818
+ fallback: baseName === "default"
819
+ };
820
+ }
821
+ __name(parseRouteSlotFile, "parseRouteSlotFile");
822
+ function createRouteSlotContainerId(name, ownerPattern) {
823
+ const owner = ownerPattern === "/" ? "root" : ownerPattern.replace(/^\//, "").replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
824
+ return `__farm_slot_${name}_${owner || "root"}__`;
825
+ }
826
+ __name(createRouteSlotContainerId, "createRouteSlotContainerId");
827
+ function parseInterceptionMarker(segment) {
828
+ if (!segment) return null;
829
+ if (segment.startsWith("(...)")) {
830
+ return {
831
+ up: 0,
832
+ root: true,
833
+ remainder: segment.slice(5)
834
+ };
835
+ }
836
+ if (segment.startsWith("(.)")) {
837
+ return {
838
+ up: 0,
839
+ remainder: segment.slice(3)
840
+ };
841
+ }
842
+ let remainder = segment;
843
+ let up = 0;
844
+ while (remainder.startsWith("(..)")) {
845
+ up += 1;
846
+ remainder = remainder.slice(4);
847
+ }
848
+ return up > 0 ? { up, remainder } : null;
849
+ }
850
+ __name(parseInterceptionMarker, "parseInterceptionMarker");
851
+
781
852
  // src/routing/route-manager.ts
782
853
  function routeSpecificity(entry) {
783
854
  return entry.route.segments.reduce((score, segment) => {
@@ -791,6 +862,7 @@ var _RouteManager = class _RouteManager {
791
862
  constructor(config, viteServer) {
792
863
  this.routes = /* @__PURE__ */ new Map();
793
864
  this.layouts = /* @__PURE__ */ new Map();
865
+ this.routeSlots = /* @__PURE__ */ new Map();
794
866
  this.loadings = /* @__PURE__ */ new Map();
795
867
  this.errors = /* @__PURE__ */ new Map();
796
868
  this.metadataImages = /* @__PURE__ */ new Map();
@@ -806,6 +878,7 @@ var _RouteManager = class _RouteManager {
806
878
  async discoverRoutes() {
807
879
  this.routes.clear();
808
880
  this.layouts.clear();
881
+ this.routeSlots.clear();
809
882
  this.loadings.clear();
810
883
  this.errors.clear();
811
884
  this.metadataImages.clear();
@@ -816,6 +889,13 @@ var _RouteManager = class _RouteManager {
816
889
  await this.discoverFileRoutes(source);
817
890
  await this.discoverProgrammaticRoutes(source);
818
891
  }
892
+ for (const slot of this.routeSlots.values()) {
893
+ if (slot.interception && !this.routes.has(slot.pattern)) {
894
+ throw new Error(
895
+ `Intercepting route slot "${slot.name}" targets "${slot.pattern}", but no canonical page exists for that URL`
896
+ );
897
+ }
898
+ }
819
899
  this.routes = new Map(
820
900
  Array.from(this.routes.entries()).sort(
821
901
  ([, left], [, right]) => routeSpecificity(right) - routeSpecificity(left)
@@ -831,7 +911,7 @@ var _RouteManager = class _RouteManager {
831
911
  /**
832
912
  * Find matching route for a given URL path
833
913
  */
834
- matchRoute(pathname) {
914
+ matchRoute(pathname, options = {}) {
835
915
  const routePathname = this.toRoutePathname(pathname);
836
916
  const normalizedPath = routePathname === "/" ? "/" : routePathname.replace(/\/$/, "");
837
917
  let matchedRoute = null;
@@ -845,10 +925,12 @@ var _RouteManager = class _RouteManager {
845
925
  }
846
926
  }
847
927
  const layouts = this.findMatchingLayouts(normalizedPath);
928
+ const slots = this.findMatchingRouteSlots(normalizedPath, options.interceptFrom);
848
929
  return {
849
930
  route: matchedRoute,
850
931
  params,
851
- layouts
932
+ layouts,
933
+ slots
852
934
  };
853
935
  }
854
936
  /**
@@ -863,6 +945,9 @@ var _RouteManager = class _RouteManager {
863
945
  getLayouts() {
864
946
  return new Map(this.layouts);
865
947
  }
948
+ getRouteSlots() {
949
+ return new Map(this.routeSlots);
950
+ }
866
951
  /** Resolve route rules, inherited layouts, and the page export in precedence order. */
867
952
  async resolveRouteRuntimeConfig(pattern) {
868
953
  const routeEntry = this.routes.get(pattern);
@@ -1021,7 +1106,22 @@ var _RouteManager = class _RouteManager {
1021
1106
  pattern: entry.pattern,
1022
1107
  modulePath: toUrlPath(entry.modulePath)
1023
1108
  }));
1024
- return { routes, layouts };
1109
+ const slots = Array.from(this.routeSlots.values()).map((entry) => {
1110
+ const metadata = getClientModuleMetadata(entry.modulePath, projectRoot);
1111
+ return {
1112
+ name: entry.name,
1113
+ ownerPattern: entry.ownerPattern,
1114
+ pattern: entry.pattern,
1115
+ modulePath: toUrlPath(entry.modulePath),
1116
+ containerId: entry.containerId,
1117
+ interception: entry.interception,
1118
+ fallback: entry.fallback,
1119
+ shouldHydrate: metadata.shouldHydrate,
1120
+ isClientComponent: metadata.isClientComponent,
1121
+ segments: entry.route.segments
1122
+ };
1123
+ });
1124
+ return { routes, layouts, slots };
1025
1125
  }
1026
1126
  /**
1027
1127
  * Load a route module dynamically
@@ -1098,6 +1198,7 @@ var _RouteManager = class _RouteManager {
1098
1198
  async discoverFileRoutes(source) {
1099
1199
  const appDir = resolveAppPath(source.root, source.srcDir, "app");
1100
1200
  const pageFiles = await safeGlobFiles("**/page.{ts,tsx,js,jsx,md,mdx}", appDir);
1201
+ const slotDefaultFiles = await safeGlobFiles("**/@*/**/default.{ts,tsx,js,jsx}", appDir);
1101
1202
  const layoutFiles = await safeGlobFiles("**/layout.{ts,tsx,js,jsx}", appDir);
1102
1203
  const loadingFiles = await safeGlobFiles("**/loading.{ts,tsx,js,jsx}", appDir);
1103
1204
  const errorFiles = await safeGlobFiles("**/error.{ts,tsx,js,jsx}", appDir);
@@ -1106,7 +1207,7 @@ var _RouteManager = class _RouteManager {
1106
1207
  appDir
1107
1208
  );
1108
1209
  for (const [kind, files, target] of [
1109
- ["page", pageFiles, this.routes],
1210
+ ["page", pageFiles.filter((file) => parseRouteSlotFile(file) === null), this.routes],
1110
1211
  ["layout", layoutFiles, this.layouts],
1111
1212
  ["loading", loadingFiles, this.loadings],
1112
1213
  ["error", errorFiles, this.errors]
@@ -1130,6 +1231,32 @@ var _RouteManager = class _RouteManager {
1130
1231
  });
1131
1232
  }
1132
1233
  }
1234
+ for (const file of [...pageFiles, ...slotDefaultFiles]) {
1235
+ const slot = parseRouteSlotFile(file);
1236
+ if (!slot) continue;
1237
+ const modulePath = path3.join(appDir, file);
1238
+ const pattern = this.createRoutePattern(slot.route);
1239
+ const ownerPattern = this.createRoutePattern(slot.ownerRoute);
1240
+ const key = `${ownerPattern}:${slot.name}:${slot.interception ? "intercept" : "slot"}:${slot.fallback ? "default" : pattern}`;
1241
+ const existing = this.routeSlots.get(key);
1242
+ if (existing?.sourceRoot === source.root) {
1243
+ throw new Error(
1244
+ `Duplicate route slot "${slot.name}" for "${pattern}". Found both ${existing.modulePath} and ${modulePath}.`
1245
+ );
1246
+ }
1247
+ this.routeSlots.set(key, {
1248
+ route: slot.route,
1249
+ modulePath,
1250
+ pattern,
1251
+ source: "file",
1252
+ sourceRoot: source.root,
1253
+ name: slot.name,
1254
+ ownerPattern,
1255
+ interception: slot.interception,
1256
+ fallback: slot.fallback,
1257
+ containerId: createRouteSlotContainerId(slot.name, ownerPattern)
1258
+ });
1259
+ }
1133
1260
  for (const file of metadataImageFiles) {
1134
1261
  const fileBase = path3.basename(file, path3.extname(file));
1135
1262
  const kind = fileBase === "twitter-image" ? "twitter" : "opengraph";
@@ -1255,6 +1382,71 @@ var _RouteManager = class _RouteManager {
1255
1382
  }
1256
1383
  return matchingLayouts;
1257
1384
  }
1385
+ findMatchingRouteSlots(pathname, interceptFrom) {
1386
+ const groups = /* @__PURE__ */ new Map();
1387
+ for (const entry of this.routeSlots.values()) {
1388
+ if (!this.matchesRoutePrefix(pathname, entry.ownerPattern)) continue;
1389
+ const key = `${entry.ownerPattern}:${entry.name}`;
1390
+ const entries = groups.get(key) ?? [];
1391
+ entries.push(entry);
1392
+ groups.set(key, entries);
1393
+ }
1394
+ const normalizedFrom = interceptFrom ? this.toRoutePathname(new URL(interceptFrom, "http://farm.local").pathname) : void 0;
1395
+ const matches = [];
1396
+ for (const entries of groups.values()) {
1397
+ const candidates = entries.filter((entry) => !entry.fallback).filter(
1398
+ (entry) => !entry.interception || normalizedFrom !== void 0 && this.matchesRoutePrefix(normalizedFrom, entry.ownerPattern)
1399
+ ).map((entry) => ({
1400
+ entry,
1401
+ match: matchRoute(pathname, entry.route.segments)
1402
+ })).filter((candidate) => candidate.match.matches).sort((left, right) => {
1403
+ if (left.entry.interception !== right.entry.interception) {
1404
+ return left.entry.interception ? -1 : 1;
1405
+ }
1406
+ return routeSpecificity(right.entry) - routeSpecificity(left.entry);
1407
+ });
1408
+ const selected = candidates[0];
1409
+ if (selected) {
1410
+ matches.push({
1411
+ name: selected.entry.name,
1412
+ ownerPattern: selected.entry.ownerPattern,
1413
+ containerId: selected.entry.containerId,
1414
+ interception: selected.entry.interception,
1415
+ fallback: false,
1416
+ route: selected.entry,
1417
+ params: selected.match.params
1418
+ });
1419
+ continue;
1420
+ }
1421
+ const fallback = entries.find((entry) => entry.fallback);
1422
+ if (fallback) {
1423
+ matches.push({
1424
+ name: fallback.name,
1425
+ ownerPattern: fallback.ownerPattern,
1426
+ containerId: fallback.containerId,
1427
+ interception: false,
1428
+ fallback: true,
1429
+ route: fallback,
1430
+ params: {}
1431
+ });
1432
+ }
1433
+ }
1434
+ return matches.sort((left, right) => {
1435
+ const ownerDifference = left.route.route.segments.length - right.route.route.segments.length;
1436
+ return ownerDifference || left.name.localeCompare(right.name);
1437
+ });
1438
+ }
1439
+ matchesRoutePrefix(pathname, pattern) {
1440
+ if (pattern === "/") return true;
1441
+ const patternSegments = parseRoutePath(`${pattern}/page.tsx`).segments;
1442
+ const pathSegments = pathname.split("/").filter(Boolean);
1443
+ if (patternSegments.length > pathSegments.length) return false;
1444
+ for (let index = 0; index < patternSegments.length; index++) {
1445
+ const segment = patternSegments[index];
1446
+ if (!segment.isDynamic && segment.segment !== pathSegments[index]) return false;
1447
+ }
1448
+ return true;
1449
+ }
1258
1450
  findNearestBoundary(pathname, boundaries) {
1259
1451
  const normalizedPath = pathname === "/" ? "/" : pathname.replace(/\/$/, "");
1260
1452
  const pathSegments = normalizedPath.split("/").filter(Boolean);
@@ -1659,7 +1851,9 @@ var _ServerRenderer = class _ServerRenderer {
1659
1851
  async runWithRequestContext(request, fn) {
1660
1852
  return _runWithCurrentRequest(
1661
1853
  request,
1662
- () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, { redirect: false }) : fn()
1854
+ () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, {
1855
+ redirect: false
1856
+ }) : fn()
1663
1857
  );
1664
1858
  }
1665
1859
  async resolveRouteContext(input) {
@@ -1682,11 +1876,11 @@ var _ServerRenderer = class _ServerRenderer {
1682
1876
  /**
1683
1877
  * Check if a path should be served from SSG cache
1684
1878
  */
1685
- shouldServeSSG(pathname) {
1879
+ async shouldServeSSG(pathname) {
1686
1880
  const ssgPage = matchSSGPage(pathname, this.ssgManifest);
1687
1881
  if (!ssgPage) return null;
1688
- const cached = this.getCachedSSGPage(pathname);
1689
- if (cached && this.dataCache.isStale(cached)) {
1882
+ const cached = await this.getCachedSSGPage(pathname);
1883
+ if (cached && await this.dataCache.isStaleAsync(cached)) {
1690
1884
  this.regenerateSSGPage(ssgPage);
1691
1885
  }
1692
1886
  return ssgPage;
@@ -1698,12 +1892,12 @@ var _ServerRenderer = class _ServerRenderer {
1698
1892
  return createFarmCacheKey(["ppr", normalizeRevalidatePath(pathname), search]);
1699
1893
  }
1700
1894
  getCachedSSGPage(urlPath) {
1701
- return this.dataCache.getEntry(this.getSSGCacheKey(urlPath), {
1895
+ return this.dataCache.getEntryAsync(this.getSSGCacheKey(urlPath), {
1702
1896
  allowStale: true
1703
1897
  });
1704
1898
  }
1705
- cacheSSGPage(page, html, options) {
1706
- this.dataCache.set(
1899
+ async cacheSSGPage(page, html, options) {
1900
+ await this.dataCache.setAsync(
1707
1901
  this.getSSGCacheKey(page.urlPath),
1708
1902
  { html, document: options.document },
1709
1903
  {
@@ -1715,11 +1909,11 @@ var _ServerRenderer = class _ServerRenderer {
1715
1909
  );
1716
1910
  }
1717
1911
  getCachedPPRShell(pathname, search) {
1718
- return this.dataCache.getEntry(this.getPPRCacheKey(pathname, search));
1912
+ return this.dataCache.getEntryAsync(this.getPPRCacheKey(pathname, search));
1719
1913
  }
1720
- cachePPRShell(options, html) {
1914
+ async cachePPRShell(options, html) {
1721
1915
  const key = this.getPPRCacheKey(options.pathname, options.search);
1722
- this.dataCache.set(
1916
+ await this.dataCache.setAsync(
1723
1917
  key,
1724
1918
  { html },
1725
1919
  {
@@ -1817,7 +2011,7 @@ var _ServerRenderer = class _ServerRenderer {
1817
2011
  }
1818
2012
  const { renderToString } = await import("react-dom/server");
1819
2013
  const html = renderToString(await this.wrapWithIntegrationProviders(pageElement));
1820
- this.cacheSSGPage(page, html, { document: false });
2014
+ await this.cacheSSGPage(page, html, { document: false });
1821
2015
  logger.info(`ISR: Regenerated ${page.urlPath}`);
1822
2016
  } catch (error) {
1823
2017
  logger.error(`ISR regeneration failed for ${page.urlPath}: ${error}`);
@@ -1831,7 +2025,7 @@ var _ServerRenderer = class _ServerRenderer {
1831
2025
  * Serve a pre-rendered SSG page
1832
2026
  */
1833
2027
  async serveSSGPage(req, res, page) {
1834
- const cached = this.getCachedSSGPage(page.urlPath);
2028
+ const cached = await this.getCachedSSGPage(page.urlPath);
1835
2029
  if (cached) {
1836
2030
  res.setHeader("Content-Type", "text/html; charset=utf-8");
1837
2031
  res.setHeader("X-Farm-SSG", "cached");
@@ -1849,9 +2043,12 @@ var _ServerRenderer = class _ServerRenderer {
1849
2043
  if (fs2.existsSync(htmlPath)) {
1850
2044
  const stat = fs2.statSync(htmlPath);
1851
2045
  const html = fs2.readFileSync(htmlPath, "utf-8");
1852
- this.cacheSSGPage(page, html, { document: true, createdAt: stat.mtimeMs });
1853
- const fileCacheEntry = this.getCachedSSGPage(page.urlPath);
1854
- if (fileCacheEntry && this.dataCache.isStale(fileCacheEntry)) {
2046
+ await this.cacheSSGPage(page, html, {
2047
+ document: true,
2048
+ createdAt: stat.mtimeMs
2049
+ });
2050
+ const fileCacheEntry = await this.getCachedSSGPage(page.urlPath);
2051
+ if (fileCacheEntry && await this.dataCache.isStaleAsync(fileCacheEntry)) {
1855
2052
  this.regenerateSSGPage(page);
1856
2053
  }
1857
2054
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -1897,6 +2094,7 @@ var _ServerRenderer = class _ServerRenderer {
1897
2094
  let pathname = "/";
1898
2095
  let params = {};
1899
2096
  let layouts = [];
2097
+ let routeSlots = [];
1900
2098
  let searchParamsObject = {};
1901
2099
  let middlewareMap = /* @__PURE__ */ new Map();
1902
2100
  let middlewareContext = /* @__PURE__ */ new Map();
@@ -1929,7 +2127,7 @@ var _ServerRenderer = class _ServerRenderer {
1929
2127
  }
1930
2128
  this.applyDeploymentHeaders(req, res);
1931
2129
  if (process.env.NODE_ENV === "production") {
1932
- const ssgPage = this.shouldServeSSG(pathname);
2130
+ const ssgPage = await this.shouldServeSSG(pathname);
1933
2131
  if (ssgPage) {
1934
2132
  const served = await this.serveSSGPage(req, res, ssgPage);
1935
2133
  if (served) {
@@ -1942,6 +2140,7 @@ var _ServerRenderer = class _ServerRenderer {
1942
2140
  const route = match.route;
1943
2141
  params = match.params;
1944
2142
  layouts = match.layouts;
2143
+ routeSlots = match.slots ?? [];
1945
2144
  if (!route) {
1946
2145
  emitFarmEvent({ type: "route.notFound", pathname });
1947
2146
  await this.render404(req, res);
@@ -1958,7 +2157,9 @@ var _ServerRenderer = class _ServerRenderer {
1958
2157
  errorBoundaryEntry = this.routeManager.getMatchingError(pathname);
1959
2158
  middlewareMap = toMiddlewareMap(req.__FARM_MIDDLEWARE_DATA__);
1960
2159
  middlewareContext = toMiddlewareMap(req.__FARM_MIDDLEWARE_CONTEXT__);
1961
- pluginExposedContext = getRequestContextSnapshot(req, { exposedOnly: true });
2160
+ pluginExposedContext = getRequestContextSnapshot(req, {
2161
+ exposedOnly: true
2162
+ });
1962
2163
  const currentRequest = createWebRequestFromFarmRequest(req);
1963
2164
  const routeContext = await this.resolveRouteContext({
1964
2165
  request: currentRequest,
@@ -2022,8 +2223,16 @@ var _ServerRenderer = class _ServerRenderer {
2022
2223
  revalidate: renderingConfig.revalidate
2023
2224
  } : void 0;
2024
2225
  if (renderingConfig.ppr && pprBypassReason) {
2025
- emitFarmEvent({ type: "ppr.shell.bypass", route: pathname, reason: pprBypassReason });
2026
- emitFarmEvent({ type: "cache.bypass", route: pathname, reason: pprBypassReason });
2226
+ emitFarmEvent({
2227
+ type: "ppr.shell.bypass",
2228
+ route: pathname,
2229
+ reason: pprBypassReason
2230
+ });
2231
+ emitFarmEvent({
2232
+ type: "cache.bypass",
2233
+ route: pathname,
2234
+ reason: pprBypassReason
2235
+ });
2027
2236
  if (pprBypassReason === "refresh") {
2028
2237
  pprRefreshRoute = pathname;
2029
2238
  emitFarmEvent({ type: "ppr.refresh.start", route: pathname });
@@ -2031,14 +2240,22 @@ var _ServerRenderer = class _ServerRenderer {
2031
2240
  }
2032
2241
  if (pprShellOptions) {
2033
2242
  const pprCacheKey = this.getPPRCacheKey(pathname, url.search);
2034
- const cachedPPRShell = this.getCachedPPRShell(pathname, url.search);
2243
+ const cachedPPRShell = await this.getCachedPPRShell(pathname, url.search);
2035
2244
  if (cachedPPRShell) {
2036
- emitFarmEvent({ type: "ppr.shell.hit", route: pathname, key: pprCacheKey });
2245
+ emitFarmEvent({
2246
+ type: "ppr.shell.hit",
2247
+ route: pathname,
2248
+ key: pprCacheKey
2249
+ });
2037
2250
  this.serveCachedPPRShell(res, cachedPPRShell.value, renderingConfig.revalidate);
2038
2251
  completeRender(res.statusCode || 200, pathname);
2039
2252
  return;
2040
2253
  }
2041
- emitFarmEvent({ type: "ppr.shell.miss", route: pathname, key: pprCacheKey });
2254
+ emitFarmEvent({
2255
+ type: "ppr.shell.miss",
2256
+ route: pathname,
2257
+ key: pprCacheKey
2258
+ });
2042
2259
  }
2043
2260
  let LoadingFallbackComponent = null;
2044
2261
  if (loadingBoundaryEntry) {
@@ -2062,14 +2279,77 @@ var _ServerRenderer = class _ServerRenderer {
2062
2279
  );
2063
2280
  const moduleMetadata = routeManifestEntry || getClientModuleMetadata(route.modulePath, this.config.root);
2064
2281
  const isClientComponent = moduleMetadata.isClientComponent;
2282
+ const renderedRouteSlots = await Promise.all(
2283
+ routeSlots.map(async (slot) => {
2284
+ const slotModule = await this.routeManager.loadRouteModule(slot.route.modulePath);
2285
+ if (!slotModule.default) {
2286
+ throw new Error(
2287
+ `Route slot "${slot.name}" module ${slot.route.modulePath} does not export a default component`
2288
+ );
2289
+ }
2290
+ const slotContext = await this.resolveRouteContext({
2291
+ request: currentRequest,
2292
+ rawRequest: req,
2293
+ params: slot.params,
2294
+ search: searchParamsObject,
2295
+ path: pathname
2296
+ });
2297
+ const rawSlotProps = withFarmRouteContext(
2298
+ {
2299
+ params: slot.params,
2300
+ searchParams: Promise.resolve(searchParamsObject),
2301
+ path: pathname,
2302
+ middleware: middlewareMap.size > 0 ? { data: middlewareMap } : void 0,
2303
+ context: pluginExposedContext.size > 0 ? { data: pluginExposedContext } : void 0
2304
+ },
2305
+ slotContext
2306
+ );
2307
+ const slotProps = await parseRouteModuleProps(slotModule, {
2308
+ props: rawSlotProps,
2309
+ search: searchParamsObject,
2310
+ routePath: slot.route.pattern
2311
+ });
2312
+ const metadata = getClientModuleMetadata(slot.route.modulePath, this.config.root);
2313
+ return {
2314
+ ...slot,
2315
+ module: slotModule,
2316
+ props: slotProps,
2317
+ isClientComponent: metadata.isClientComponent,
2318
+ shouldHydrate: metadata.shouldHydrate
2319
+ };
2320
+ })
2321
+ );
2065
2322
  const shouldHydrate = moduleMetadata.shouldHydrate;
2323
+ const hasHydratableRouteSlots = renderedRouteSlots.some(
2324
+ (slot) => slot.isClientComponent || slot.shouldHydrate
2325
+ );
2066
2326
  req.__FARM_PAGE_PATH__ = route.modulePath;
2067
2327
  req.__FARM_ROUTE__ = pathname;
2068
2328
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
2069
2329
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
2330
+ req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
2070
2331
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
2071
2332
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
2072
2333
  ) : null;
2334
+ req.__FARM_ROUTE_SLOTS__ = renderedRouteSlots.map((slot) => ({
2335
+ name: slot.name,
2336
+ ownerPattern: slot.ownerPattern,
2337
+ containerId: slot.containerId,
2338
+ interception: slot.interception,
2339
+ fallback: slot.fallback,
2340
+ modulePath: slot.route.modulePath,
2341
+ isClientComponent: slot.isClientComponent,
2342
+ shouldHydrate: slot.shouldHydrate,
2343
+ props: {
2344
+ params: slot.props.params,
2345
+ search: slot.props.search,
2346
+ searchParams: slot.props.search,
2347
+ ..."data" in slot.props ? { data: slot.props.data } : {},
2348
+ ...slot.props.__farmCanonicalPath ? { __farmCanonicalPath: slot.props.__farmCanonicalPath } : {},
2349
+ ...slot.props.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
2350
+ path: pathname
2351
+ }
2352
+ }));
2073
2353
  req.__FARM_PROPS__ = {
2074
2354
  params: pageProps.params,
2075
2355
  search: pageProps.search,
@@ -2129,12 +2409,32 @@ var _ServerRenderer = class _ServerRenderer {
2129
2409
  let wrappedElement = pageElement;
2130
2410
  for (let i = layoutModules.length - 1; i >= 0; i--) {
2131
2411
  const layoutModule = layoutModules[i];
2412
+ const layoutEntry = layouts[i];
2132
2413
  const LayoutComponent = layoutModule.default;
2414
+ const slotProps = {};
2415
+ for (const slot of renderedRouteSlots) {
2416
+ if (slot.ownerPattern !== layoutEntry.pattern) continue;
2417
+ let slotElement = React.createElement(
2418
+ slot.module.default,
2419
+ slot.props
2420
+ );
2421
+ slotElement = React.createElement(
2422
+ "div",
2423
+ {
2424
+ id: slot.containerId,
2425
+ "data-farm-route-slot": slot.name,
2426
+ "data-farm-slot-owner": slot.ownerPattern
2427
+ },
2428
+ slotElement
2429
+ );
2430
+ slotProps[slot.name] = slotElement;
2431
+ }
2133
2432
  wrappedElement = React.createElement(
2134
2433
  LayoutComponent,
2135
2434
  {
2136
2435
  children: wrappedElement,
2137
- params
2436
+ params,
2437
+ ...slotProps
2138
2438
  }
2139
2439
  );
2140
2440
  }
@@ -2171,7 +2471,10 @@ var _ServerRenderer = class _ServerRenderer {
2171
2471
  routeManifest,
2172
2472
  captureStaticShell: Boolean(pprShellOptions),
2173
2473
  observabilityRoute: pathname,
2174
- onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({ type: "ppr.suspense.holeDetected", route: pathname }) : void 0,
2474
+ onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({
2475
+ type: "ppr.suspense.holeDetected",
2476
+ route: pathname
2477
+ }) : void 0,
2175
2478
  onComplete: pprShellOptions && req.method !== "HEAD" ? (html) => this.cachePPRShell(pprShellOptions, html) : void 0
2176
2479
  }
2177
2480
  );
@@ -2219,7 +2522,11 @@ var _ServerRenderer = class _ServerRenderer {
2219
2522
  }
2220
2523
  emitFarmEvent({ type: "render.error", route: pathname, error });
2221
2524
  if (pprRefreshRoute) {
2222
- emitFarmEvent({ type: "ppr.refresh.error", route: pprRefreshRoute, error });
2525
+ emitFarmEvent({
2526
+ type: "ppr.refresh.error",
2527
+ route: pprRefreshRoute,
2528
+ error
2529
+ });
2223
2530
  }
2224
2531
  logger.error(`Error rendering page: ${error}`);
2225
2532
  if (res.headersSent || res.writableEnded) {
@@ -2272,7 +2579,9 @@ var _ServerRenderer = class _ServerRenderer {
2272
2579
  if (typeof layoutModule.generateMetadata === "function") {
2273
2580
  metadata = mergeMetadata(
2274
2581
  metadata,
2275
- await layoutModule.generateMetadata({ params: options.pageProps.params })
2582
+ await layoutModule.generateMetadata({
2583
+ params: options.pageProps.params
2584
+ })
2276
2585
  );
2277
2586
  }
2278
2587
  }
@@ -2503,7 +2812,13 @@ var _ServerRenderer = class _ServerRenderer {
2503
2812
  clientEntry: "/@farm/client.js",
2504
2813
  routes: {},
2505
2814
  layouts: {},
2506
- sharedAssets: [{ tag: "link", attrs: { rel: "stylesheet", href: "/src/app/globals.css" } }]
2815
+ slots: [],
2816
+ sharedAssets: [
2817
+ {
2818
+ tag: "link",
2819
+ attrs: { rel: "stylesheet", href: "/src/app/globals.css" }
2820
+ }
2821
+ ]
2507
2822
  };
2508
2823
  for (const routeEntry of manifest.routes) {
2509
2824
  clientManifest.routes[routeEntry.pattern] = {
@@ -2525,9 +2840,26 @@ var _ServerRenderer = class _ServerRenderer {
2525
2840
  assets: []
2526
2841
  };
2527
2842
  }
2528
- const deferredProps = prepareDeferredData(req.__FARM_PROPS__ || {});
2843
+ for (const slotEntry of manifest.slots ?? []) {
2844
+ clientManifest.slots.push({
2845
+ ...slotEntry,
2846
+ preloads: [slotEntry.modulePath],
2847
+ assets: []
2848
+ });
2849
+ }
2850
+ const routeSlotPayload = (req.__FARM_ROUTE_SLOTS__ || []).map(
2851
+ (slot) => ({
2852
+ ...slot,
2853
+ modulePath: typeof slot.modulePath === "string" && slot.modulePath.startsWith(this.config.root) ? slot.modulePath.slice(this.config.root.length) : slot.modulePath
2854
+ })
2855
+ );
2856
+ const deferredProps = prepareDeferredData({
2857
+ page: req.__FARM_PROPS__ || {},
2858
+ slots: routeSlotPayload
2859
+ });
2529
2860
  const propsScript = `<script>
2530
- window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data)};
2861
+ window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data.page)};
2862
+ window.__FARM_ROUTE_SLOTS__ = ${serializeInlineValue(deferredProps.data.slots)};
2531
2863
  window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
2532
2864
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
2533
2865
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
@@ -2540,7 +2872,7 @@ window.__FARM_MANIFEST__ = ${JSON.stringify(clientManifest)};
2540
2872
  window.__FARM_INTEGRATION_API_MANIFEST__ = ${JSON.stringify(getRegisteredIntegrationAPIManifest())};
2541
2873
  ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(getFarmI18nClientSnapshot())};` : ""}
2542
2874
  </script>`;
2543
- const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true ? createPreHydrationClickQueueScript() : "";
2875
+ const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true || req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ === true ? createPreHydrationClickQueueScript() : "";
2544
2876
  const {
2545
2877
  title,
2546
2878
  tags: metaTags,
@@ -2651,7 +2983,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
2651
2983
  didError = true;
2652
2984
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
2653
2985
  logger.error(`SSR shell error: ${error}`);
2654
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
2986
+ emitFarmEvent({
2987
+ type: "render.error",
2988
+ route: observabilityRoute,
2989
+ error
2990
+ });
2655
2991
  }
2656
2992
  if (clearMiddlewareData) {
2657
2993
  clearMiddlewareData();
@@ -2662,7 +2998,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
2662
2998
  didError = true;
2663
2999
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
2664
3000
  logger.error(`SSR streaming error: ${error}`);
2665
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
3001
+ emitFarmEvent({
3002
+ type: "render.error",
3003
+ route: observabilityRoute,
3004
+ error
3005
+ });
2666
3006
  }
2667
3007
  }
2668
3008
  });
@@ -2836,6 +3176,7 @@ var _FarmApp = class _FarmApp {
2836
3176
  logger.info("Initializing Farm.js application...");
2837
3177
  }
2838
3178
  await initStorage(this.config.storage);
3179
+ await configureFarmCache(this.config.cache);
2839
3180
  await this.i18nRuntime.initialize();
2840
3181
  await this.verifyAppStructure();
2841
3182
  await this.routeManager.discoverRoutes();
@@ -2867,6 +3208,7 @@ var _FarmApp = class _FarmApp {
2867
3208
  preset: config.preset ?? "node-server",
2868
3209
  deploy: config.deploy || {},
2869
3210
  storage: config.storage || {},
3211
+ cache: config.cache || {},
2870
3212
  integrations: config.integrations || {},
2871
3213
  plugins: config.plugins || [],
2872
3214
  migrations: config.migrations || { commands: [] },
@@ -3339,6 +3681,7 @@ async function generateRouteTypes(options) {
3339
3681
  absolute: false
3340
3682
  });
3341
3683
  for (const file of pageFiles) {
3684
+ if (parseRouteSlotFile(file)) continue;
3342
3685
  const route = parseRoutePath(file);
3343
3686
  if (route.type === "page") {
3344
3687
  const pattern = createRoutePattern(route);
@@ -3665,7 +4008,7 @@ function loadFarmWorkflowsDevRuntime() {
3665
4008
  }
3666
4009
  __name(loadFarmWorkflowsDevRuntime, "loadFarmWorkflowsDevRuntime");
3667
4010
  function loadFarmDevtoolsSnapshotRuntime() {
3668
- return import("./devtools-HE4FTUDN.mjs");
4011
+ return import("./devtools-LVC3VB5P.mjs");
3669
4012
  }
3670
4013
  __name(loadFarmDevtoolsSnapshotRuntime, "loadFarmDevtoolsSnapshotRuntime");
3671
4014
  function loadFarmDevtoolsUIRuntime() {
@@ -4071,7 +4414,7 @@ function farmPlugin(options = {}, initialPluginManager) {
4071
4414
  }, "isProgrammaticRouteSourceFile");
4072
4415
  const isAppRuntimeFile = /* @__PURE__ */ __name((file) => {
4073
4416
  const normalized = file.replace(/\\/g, "/");
4074
- return appDirSlugs.some((appDir) => normalized.startsWith(`${appDir}/`)) && /\/(?:page|layout|loading|error|middleware|route)\.(?:ts|tsx|js|jsx|md|mdx)$|\/(?:opengraph-image|twitter-image)(?:\.(?:ts|tsx|js|jsx|png|jpg|jpeg|gif|webp)|\.alt\.txt)$/.test(
4417
+ return appDirSlugs.some((appDir) => normalized.startsWith(`${appDir}/`)) && /\/(?:page|default|layout|loading|error|middleware|route)\.(?:ts|tsx|js|jsx|md|mdx)$|\/(?:opengraph-image|twitter-image)(?:\.(?:ts|tsx|js|jsx|png|jpg|jpeg|gif|webp)|\.alt\.txt)$/.test(
4075
4418
  normalized
4076
4419
  );
4077
4420
  }, "isAppRuntimeFile");
@@ -4762,7 +5105,10 @@ function farmPlugin(options = {}, initialPluginManager) {
4762
5105
  method: req.method || "GET"
4763
5106
  });
4764
5107
  }
4765
- const match = routeManager3.matchRoute(targetPath);
5108
+ const interceptFromHeader = request.headers.get("x-farm-intercept-from") || void 0;
5109
+ const match = routeManager3.matchRoute(targetRequestUrl.pathname, {
5110
+ interceptFrom: interceptFromHeader
5111
+ });
4766
5112
  if (pm) {
4767
5113
  await pm.runHookParallel("afterRouteMatch", {
4768
5114
  pathname: targetPath,
@@ -4778,7 +5124,7 @@ function farmPlugin(options = {}, initialPluginManager) {
4778
5124
  res.end(JSON.stringify({ error: "Route not found" }));
4779
5125
  return;
4780
5126
  }
4781
- const { route, params, layouts } = match;
5127
+ const { route, params, layouts, slots } = match;
4782
5128
  if (!route) {
4783
5129
  res.statusCode = 404;
4784
5130
  res.setHeader("Content-Type", "application/json");
@@ -4833,6 +5179,54 @@ function farmPlugin(options = {}, initialPluginManager) {
4833
5179
  search: searchParams,
4834
5180
  routePath: route.pattern
4835
5181
  });
5182
+ const routeSlots = await Promise.all(
5183
+ slots.map(async (slot) => {
5184
+ const slotModule = await routeManager3.loadRouteModule(slot.route.modulePath);
5185
+ const slotMetadata = getClientModuleMetadata(
5186
+ slot.route.modulePath,
5187
+ server.config.root
5188
+ );
5189
+ const slotContext = await resolveFarmRouteContext(farmApp.getConfig(), {
5190
+ request,
5191
+ params: slot.params,
5192
+ search: searchParams,
5193
+ path: targetUrl.pathname
5194
+ });
5195
+ const slotProps = await parseRouteModuleProps2(slotModule, {
5196
+ props: withFarmRouteContext(
5197
+ {
5198
+ params: slot.params,
5199
+ searchParams: Promise.resolve(searchParams),
5200
+ path: targetUrl.pathname
5201
+ },
5202
+ slotContext
5203
+ ),
5204
+ search: searchParams,
5205
+ routePath: slot.route.pattern
5206
+ });
5207
+ return {
5208
+ name: slot.name,
5209
+ ownerPattern: slot.ownerPattern,
5210
+ containerId: slot.containerId,
5211
+ interception: slot.interception,
5212
+ fallback: slot.fallback,
5213
+ modulePath: slot.route.modulePath,
5214
+ isClientComponent: slotMetadata.isClientComponent,
5215
+ shouldHydrate: slotMetadata.shouldHydrate,
5216
+ props: {
5217
+ params: slotProps.params,
5218
+ search: slotProps.search,
5219
+ searchParams: slotProps.search,
5220
+ ..."data" in slotProps ? { data: slotProps.data } : {},
5221
+ ...slotProps.__farmCanonicalPath ? {
5222
+ __farmCanonicalPath: slotProps.__farmCanonicalPath
5223
+ } : {},
5224
+ ...slotProps.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
5225
+ path: targetUrl.pathname
5226
+ }
5227
+ };
5228
+ })
5229
+ );
4836
5230
  const projectRoot = server.config.root;
4837
5231
  const toUrlPath = /* @__PURE__ */ __name((absolutePath) => {
4838
5232
  if (absolutePath.startsWith(projectRoot)) {
@@ -4853,13 +5247,21 @@ function farmPlugin(options = {}, initialPluginManager) {
4853
5247
  },
4854
5248
  canonicalPath: routeProps.__farmCanonicalPath,
4855
5249
  modulePath: toUrlPath(route.modulePath),
4856
- isClientComponent,
4857
- shouldHydrate,
5250
+ isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
5251
+ shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
4858
5252
  metadata: {
4859
5253
  title: mergedMetadata.title,
4860
5254
  description: mergedMetadata.description
4861
5255
  },
4862
5256
  layoutModules: layouts.map((l) => toUrlPath(l.modulePath)),
5257
+ routeSlots: routeSlots.map((slot) => ({
5258
+ ...slot,
5259
+ modulePath: toUrlPath(slot.modulePath)
5260
+ })),
5261
+ interception: routeSlots.some((slot) => slot.interception) ? {
5262
+ from: interceptFromHeader,
5263
+ slots: routeSlots.filter((slot) => slot.interception).map((slot) => slot.name)
5264
+ } : void 0,
4863
5265
  i18n: getFarmI18nClientSnapshot()
4864
5266
  };
4865
5267
  await sendWebResponse(
@@ -5195,6 +5597,7 @@ function farmPlugin(options = {}, initialPluginManager) {
5195
5597
  export const getManifest = () => ({
5196
5598
  routes: {},
5197
5599
  layouts: {},
5600
+ slots: [],
5198
5601
  clientEntry: "/@farm/client.js",
5199
5602
  sharedAssets: []
5200
5603
  });
@@ -5205,6 +5608,7 @@ export const getManifest = () => ({
5205
5608
  clientEntry: "/@farm/client.js",
5206
5609
  routes: {},
5207
5610
  layouts: {},
5611
+ slots: [],
5208
5612
  sharedAssets: [
5209
5613
  {
5210
5614
  tag: "link",
@@ -5234,6 +5638,13 @@ export const getManifest = () => ({
5234
5638
  assets: []
5235
5639
  };
5236
5640
  }
5641
+ for (const slot of manifest.slots) {
5642
+ fullManifest.slots.push({
5643
+ ...slot,
5644
+ preloads: [slot.modulePath],
5645
+ assets: []
5646
+ });
5647
+ }
5237
5648
  return `
5238
5649
  // Auto-generated manifest for SPA navigation (TanStack Start pattern)
5239
5650
  // This manifest is inlined in the server bundle - no file on disk
@@ -5876,7 +6287,7 @@ window.__FARM_WRAP_PROVIDERS__ = wrapWithIntegrationProviders;
5876
6287
 
5877
6288
  // Get manifest from window (inlined by server in HTML)
5878
6289
  // Fallback to empty manifest if not available yet
5879
- const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, clientEntry: '', sharedAssets: [] };
6290
+ const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, slots: [], clientEntry: '', sharedAssets: [] };
5880
6291
 
5881
6292
  // ====== CLIENT-SIDE ROUTE MATCHING ======
5882
6293
  // Matches URL to route using manifest (no server request!)
@@ -6215,6 +6626,9 @@ let LayoutComponent = null;
6215
6626
 
6216
6627
  // Track if we've taken over rendering from SSR
6217
6628
  let hasClientTakenOver = false;
6629
+ const routeSlotRoots = new Map();
6630
+ const routeSlotDefinitions = new Map();
6631
+ let activeRouteInterception = null;
6218
6632
 
6219
6633
  function normalizeServerProps(rawProps) {
6220
6634
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -6227,6 +6641,123 @@ function normalizeServerProps(rawProps) {
6227
6641
  return props;
6228
6642
  }
6229
6643
 
6644
+ function getRouteSlotKey(slot) {
6645
+ return slot.ownerPattern + ':' + slot.name;
6646
+ }
6647
+
6648
+ async function renderRouteSlot(slot, mode = 'render') {
6649
+ const container = document.getElementById(slot.containerId);
6650
+ if (!container || !slot.modulePath) return false;
6651
+ if (mode === 'intercept' && !slot.isClientComponent && !slot.shouldHydrate) {
6652
+ return false;
6653
+ }
6654
+
6655
+ let slotModule = pageModuleCache.get(slot.modulePath);
6656
+ if (!slotModule) {
6657
+ slotModule = await import(/* @vite-ignore */ slot.modulePath);
6658
+ pageModuleCache.set(slot.modulePath, slotModule);
6659
+ }
6660
+ const SlotComponent = slotModule?.default;
6661
+ if (!SlotComponent) return false;
6662
+
6663
+ const props = reviveDeferredData(
6664
+ normalizeServerProps(slot.props || {}),
6665
+ window.__FARM_DEFERRED_DATA__ || {},
6666
+ );
6667
+ const element = wrapWithIntegrationProviders(
6668
+ React.createElement(SlotComponent, props),
6669
+ );
6670
+ const key = getRouteSlotKey(slot);
6671
+ const existingRoot = routeSlotRoots.get(key);
6672
+
6673
+ if (mode === 'hydrate') {
6674
+ if (existingRoot) return true;
6675
+ const root = hydrateRoot(container, element);
6676
+ routeSlotRoots.set(key, root);
6677
+ } else {
6678
+ if (existingRoot) {
6679
+ try { existingRoot.unmount(); } catch (error) {}
6680
+ }
6681
+ const root = createRoot(container);
6682
+ root.render(element);
6683
+ routeSlotRoots.set(key, root);
6684
+ }
6685
+
6686
+ routeSlotDefinitions.set(key, slot);
6687
+ return true;
6688
+ }
6689
+
6690
+ async function hydrateInitialRouteSlots() {
6691
+ const slots = Array.isArray(window.__FARM_ROUTE_SLOTS__)
6692
+ ? window.__FARM_ROUTE_SLOTS__
6693
+ : [];
6694
+ let hydrated = false;
6695
+
6696
+ for (const slot of slots) {
6697
+ routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
6698
+ if (!slot.isClientComponent && !slot.shouldHydrate) continue;
6699
+ try {
6700
+ hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
6701
+ } catch (error) {
6702
+ console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
6703
+ }
6704
+ }
6705
+
6706
+ return hydrated;
6707
+ }
6708
+
6709
+ async function renderRouteInterception(pageData) {
6710
+ const slots = Array.isArray(pageData.routeSlots)
6711
+ ? pageData.routeSlots.filter((slot) => slot.interception)
6712
+ : [];
6713
+ if (!pageData.interception || slots.length === 0) return false;
6714
+
6715
+ const previous = new Map();
6716
+ for (const slot of slots) {
6717
+ const key = getRouteSlotKey(slot);
6718
+ previous.set(key, routeSlotDefinitions.get(key) || null);
6719
+ if (!(await renderRouteSlot(slot, 'intercept'))) {
6720
+ return false;
6721
+ }
6722
+ }
6723
+
6724
+ activeRouteInterception = {
6725
+ from: pageData.interception.from,
6726
+ slots,
6727
+ previous,
6728
+ };
6729
+ return true;
6730
+ }
6731
+
6732
+ async function clearRouteInterception(destination) {
6733
+ if (!activeRouteInterception) return false;
6734
+ const active = activeRouteInterception;
6735
+ activeRouteInterception = null;
6736
+
6737
+ for (const slot of active.slots) {
6738
+ const key = getRouteSlotKey(slot);
6739
+ const root = routeSlotRoots.get(key);
6740
+ if (root) {
6741
+ try { root.unmount(); } catch (error) {}
6742
+ routeSlotRoots.delete(key);
6743
+ }
6744
+
6745
+ const previous = active.previous.get(key);
6746
+ if (previous && (previous.isClientComponent || previous.shouldHydrate)) {
6747
+ await renderRouteSlot(previous, 'render');
6748
+ } else {
6749
+ const container = document.getElementById(slot.containerId);
6750
+ if (container) container.replaceChildren();
6751
+ if (previous) routeSlotDefinitions.set(key, previous);
6752
+ else routeSlotDefinitions.delete(key);
6753
+ }
6754
+ }
6755
+
6756
+ if (!active.from) return false;
6757
+ const background = new URL(active.from, window.location.origin);
6758
+ return destination === background.pathname + background.search;
6759
+ }
6760
+
6230
6761
  function applyCanonicalPathFromProps(props) {
6231
6762
  const canonicalPath = props && typeof props.__farmCanonicalPath === 'string'
6232
6763
  ? props.__farmCanonicalPath
@@ -6461,6 +6992,17 @@ async function tryHydrateImportedPage(
6461
6992
  async function renderPage(pageData) {
6462
6993
  const container = document.getElementById('root');
6463
6994
  if (!container) return;
6995
+ const destination = window.location.pathname + window.location.search;
6996
+
6997
+ if (pageData.interception) {
6998
+ if (await renderRouteInterception(pageData)) return;
6999
+ window.location.assign(destination);
7000
+ return;
7001
+ }
7002
+
7003
+ if (await clearRouteInterception(destination)) {
7004
+ return;
7005
+ }
6464
7006
 
6465
7007
  const route = {
6466
7008
  modulePath: pageData.modulePath,
@@ -6689,7 +7231,9 @@ async function hydrate() {
6689
7231
  window.__FARM_SHOULD_HYDRATE__ === true ||
6690
7232
  isClientComponent ||
6691
7233
  await moduleLooksClient(modulePath);
7234
+ const hydratedSlots = await hydrateInitialRouteSlots();
6692
7235
  if (!shouldHydrate) {
7236
+ if (hydratedSlots) replayPreHydrationClicks();
6693
7237
  console.log('[Farm.js] Server component - SPA router ready')
6694
7238
  return
6695
7239
  }
@@ -7084,4 +7628,4 @@ export {
7084
7628
  farmPlugin,
7085
7629
  defineConfig
7086
7630
  };
7087
- //# sourceMappingURL=chunk-XJSHJN4M.mjs.map
7631
+ //# sourceMappingURL=chunk-A2LUXHD2.mjs.map