@farm.js/core 0.1.0-beta.1 → 0.1.0-beta.4

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 (254) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-runtime.d.mts +2 -1
  3. package/dist/agent-runtime.d.ts +2 -1
  4. package/dist/api/route-manager.cjs +410 -15
  5. package/dist/api/route-manager.cjs.map +1 -1
  6. package/dist/api/route-manager.d.mts +4 -2
  7. package/dist/api/route-manager.d.ts +4 -2
  8. package/dist/api/route-manager.mjs +9 -8
  9. package/dist/api/runtime.cjs +748 -9
  10. package/dist/api/runtime.cjs.map +1 -1
  11. package/dist/api/runtime.d.mts +3 -16
  12. package/dist/api/runtime.d.ts +3 -16
  13. package/dist/api/runtime.mjs +8 -1
  14. package/dist/api.cjs +1243 -342
  15. package/dist/api.cjs.map +1 -1
  16. package/dist/api.d.mts +5 -147
  17. package/dist/api.d.ts +5 -147
  18. package/dist/api.mjs +51 -21
  19. package/dist/{app-CFRNJXP1.d.ts → app-BjN-da13.d.ts} +38 -3
  20. package/dist/{app-BiX4qb-d.d.mts → app-CakAr8Be.d.mts} +38 -3
  21. package/dist/app-markdown.cjs +1 -1
  22. package/dist/app-markdown.cjs.map +1 -1
  23. package/dist/app-markdown.d.mts +2 -1
  24. package/dist/app-markdown.d.ts +2 -1
  25. package/dist/app-markdown.mjs +1 -1
  26. package/dist/cache.cjs +414 -12
  27. package/dist/cache.cjs.map +1 -1
  28. package/dist/cache.d.mts +120 -6
  29. package/dist/cache.d.ts +120 -6
  30. package/dist/cache.mjs +11 -3
  31. package/dist/{chunk-Y7A27276.mjs → chunk-2CUKEMVR.mjs} +349 -15
  32. package/dist/chunk-2CUKEMVR.mjs.map +1 -0
  33. package/dist/{chunk-QF4PHCMA.mjs → chunk-2ED5Q2GH.mjs} +3 -3
  34. package/dist/chunk-2ED5Q2GH.mjs.map +1 -0
  35. package/dist/{chunk-GZP5W4K3.mjs → chunk-32W4QEJP.mjs} +1 -1
  36. package/dist/chunk-32W4QEJP.mjs.map +1 -0
  37. package/dist/{chunk-TRORP7E4.mjs → chunk-3U3ZUQAQ.mjs} +2 -2
  38. package/dist/{chunk-25D7G34K.mjs → chunk-65A7QIKI.mjs} +5 -5
  39. package/dist/{chunk-25D7G34K.mjs.map → chunk-65A7QIKI.mjs.map} +1 -1
  40. package/dist/{chunk-G3B5BRDY.mjs → chunk-AJWRO5WE.mjs} +539 -61
  41. package/dist/chunk-AJWRO5WE.mjs.map +1 -0
  42. package/dist/chunk-ALCLXXTY.mjs +187 -0
  43. package/dist/chunk-ALCLXXTY.mjs.map +1 -0
  44. package/dist/{chunk-CHFHNEMO.mjs → chunk-AOBXKNS5.mjs} +627 -84
  45. package/dist/chunk-AOBXKNS5.mjs.map +1 -0
  46. package/dist/{chunk-B6N4ZB2H.mjs → chunk-C2ROET2V.mjs} +3 -3
  47. package/dist/{chunk-JN3UU635.mjs → chunk-EJ7T4TPB.mjs} +2 -2
  48. package/dist/chunk-EJ7T4TPB.mjs.map +1 -0
  49. package/dist/{chunk-ZJQNATYV.mjs → chunk-FRPWCMMG.mjs} +3 -3
  50. package/dist/chunk-FRPWCMMG.mjs.map +1 -0
  51. package/dist/{chunk-QM6VV26B.mjs → chunk-GQVGIUSN.mjs} +3 -3
  52. package/dist/{chunk-QM6VV26B.mjs.map → chunk-GQVGIUSN.mjs.map} +1 -1
  53. package/dist/{chunk-UREJA4GZ.mjs → chunk-GXASWWJU.mjs} +1 -1
  54. package/dist/{chunk-UREJA4GZ.mjs.map → chunk-GXASWWJU.mjs.map} +1 -1
  55. package/dist/{chunk-3QJO55ZF.mjs → chunk-HNIQFZKB.mjs} +5 -3
  56. package/dist/{chunk-3QJO55ZF.mjs.map → chunk-HNIQFZKB.mjs.map} +1 -1
  57. package/dist/{chunk-PRNYWD7K.mjs → chunk-JDEOUK5V.mjs} +67 -239
  58. package/dist/chunk-JDEOUK5V.mjs.map +1 -0
  59. package/dist/{chunk-O3HADRW6.mjs → chunk-MIUW47JX.mjs} +2 -2
  60. package/dist/{chunk-O3HADRW6.mjs.map → chunk-MIUW47JX.mjs.map} +1 -1
  61. package/dist/{chunk-UHE2TTLK.mjs → chunk-NM3CRAAL.mjs} +9 -8
  62. package/dist/chunk-NM3CRAAL.mjs.map +1 -0
  63. package/dist/chunk-PFE6GMNE.mjs +603 -0
  64. package/dist/chunk-PFE6GMNE.mjs.map +1 -0
  65. package/dist/{chunk-S2XHZS7W.mjs → chunk-QGQX7SFC.mjs} +2 -2
  66. package/dist/chunk-QGQX7SFC.mjs.map +1 -0
  67. package/dist/{chunk-RKI6TN3A.mjs → chunk-QM746KUO.mjs} +9 -2
  68. package/dist/chunk-QM746KUO.mjs.map +1 -0
  69. package/dist/chunk-RBRLLZL5.mjs +81 -0
  70. package/dist/chunk-RBRLLZL5.mjs.map +1 -0
  71. package/dist/{chunk-HXP3ZSPC.mjs → chunk-SADGIRS2.mjs} +3 -3
  72. package/dist/{chunk-6XDUHOEM.mjs → chunk-TQAEEJB4.mjs} +3 -3
  73. package/dist/{chunk-AGS7UJ52.mjs → chunk-UB5UOSHB.mjs} +24 -12
  74. package/dist/chunk-UB5UOSHB.mjs.map +1 -0
  75. package/dist/{chunk-SXZTEK3V.mjs → chunk-UXUAWNO5.mjs} +2 -2
  76. package/dist/chunk-V3XURYJO.mjs +756 -0
  77. package/dist/chunk-V3XURYJO.mjs.map +1 -0
  78. package/dist/{chunk-TU4YJICB.mjs → chunk-VVKWFZAH.mjs} +41 -16
  79. package/dist/chunk-VVKWFZAH.mjs.map +1 -0
  80. package/dist/{chunk-JLWMCADZ.mjs → chunk-VY2UYN5R.mjs} +24 -4
  81. package/dist/chunk-VY2UYN5R.mjs.map +1 -0
  82. package/dist/{chunk-QFQRHRZS.mjs → chunk-W2CRQFF6.mjs} +151 -23
  83. package/dist/chunk-W2CRQFF6.mjs.map +1 -0
  84. package/dist/chunk-W4UIVMVI.mjs +10 -0
  85. package/dist/chunk-W4UIVMVI.mjs.map +1 -0
  86. package/dist/{chunk-Y2OJJD7R.mjs → chunk-XIVV5IWN.mjs} +7 -9
  87. package/dist/chunk-XIVV5IWN.mjs.map +1 -0
  88. package/dist/{chunk-OYXO65LI.mjs → chunk-YW7OBE6Q.mjs} +4 -4
  89. package/dist/chunk-YW7OBE6Q.mjs.map +1 -0
  90. package/dist/{chunk-YHU7SS6L.mjs → chunk-Z5CQMP54.mjs} +11 -11
  91. package/dist/{chunk-SOU3Q3ZA.mjs → chunk-ZZ57EH5Z.mjs} +1 -1
  92. package/dist/chunk-ZZ57EH5Z.mjs.map +1 -0
  93. package/dist/{client-D_yUjVhe.d.mts → client-BT1mFQas.d.mts} +55 -10
  94. package/dist/{client-Dkc03WP8.d.ts → client-Nn0q4eO4.d.ts} +55 -10
  95. package/dist/{client-plugins-BZnpeTGA.d.ts → client-plugins-Cw4qUGsw.d.ts} +7 -0
  96. package/dist/{client-plugins-g4bzuCmo.d.mts → client-plugins-DQQ1adXM.d.mts} +7 -0
  97. package/dist/client-plugins.d.mts +2 -1
  98. package/dist/client-plugins.d.ts +2 -1
  99. package/dist/client.cjs +922 -91
  100. package/dist/client.cjs.map +1 -1
  101. package/dist/client.d.mts +121 -3
  102. package/dist/client.d.ts +121 -3
  103. package/dist/client.mjs +23 -16
  104. package/dist/config.d.mts +2 -1
  105. package/dist/config.d.ts +2 -1
  106. package/dist/cron.cjs.map +1 -1
  107. package/dist/cron.mjs +1 -1
  108. package/dist/{devtools-AE4RVXCM.mjs → devtools-LVC3VB5P.mjs} +10 -10
  109. package/dist/{devtools-client-6WPY2AQU.mjs → devtools-client-3XRTBND5.mjs} +2 -11
  110. package/dist/devtools-client-3XRTBND5.mjs.map +1 -0
  111. package/dist/docs.cjs +14 -2
  112. package/dist/docs.cjs.map +1 -1
  113. package/dist/docs.mjs +10 -10
  114. package/dist/i18n/server.cjs +2 -2
  115. package/dist/i18n/server.cjs.map +1 -1
  116. package/dist/i18n/server.mjs +1 -1
  117. package/dist/index.cjs +2852 -463
  118. package/dist/index.cjs.map +1 -1
  119. package/dist/index.d.mts +13 -12
  120. package/dist/index.d.ts +13 -12
  121. package/dist/index.mjs +152 -109
  122. package/dist/index.mjs.map +1 -1
  123. package/dist/integrations.d.mts +2 -1
  124. package/dist/integrations.d.ts +2 -1
  125. package/dist/internal/build-runtime.cjs +1616 -149
  126. package/dist/internal/build-runtime.cjs.map +1 -1
  127. package/dist/internal/build-runtime.d.mts +4 -3
  128. package/dist/internal/build-runtime.d.ts +4 -3
  129. package/dist/internal/build-runtime.mjs +27 -25
  130. package/dist/internal/client-runtime.cjs.map +1 -1
  131. package/dist/internal/client-runtime.mjs +1 -1
  132. package/dist/internal/config-runtime.cjs +13 -2
  133. package/dist/internal/config-runtime.cjs.map +1 -1
  134. package/dist/internal/config-runtime.d.mts +3 -2
  135. package/dist/internal/config-runtime.d.ts +3 -2
  136. package/dist/internal/config-runtime.mjs +9 -9
  137. package/dist/internal/production-runtime.cjs +285 -9
  138. package/dist/internal/production-runtime.cjs.map +1 -1
  139. package/dist/internal/production-runtime.d.mts +4 -4
  140. package/dist/internal/production-runtime.d.ts +4 -4
  141. package/dist/internal/production-runtime.mjs +10 -8
  142. package/dist/{metadata-DAXRaOHx.d.ts → metadata-BAPg_Nmo.d.ts} +1 -1
  143. package/dist/{metadata-C9NZxMDW.d.mts → metadata-_6i9oFG3.d.mts} +1 -1
  144. package/dist/middleware.cjs +2 -2
  145. package/dist/middleware.cjs.map +1 -1
  146. package/dist/middleware.d.mts +4 -3
  147. package/dist/middleware.d.ts +4 -3
  148. package/dist/middleware.mjs +3 -3
  149. package/dist/navigation.cjs +40 -15
  150. package/dist/navigation.cjs.map +1 -1
  151. package/dist/navigation.mjs +3 -3
  152. package/dist/{nitro-KME62MWG.mjs → nitro-QO5LV6QK.mjs} +11 -13
  153. package/dist/{nitro-KME62MWG.mjs.map → nitro-QO5LV6QK.mjs.map} +1 -1
  154. package/dist/plugin.cjs +1032 -92
  155. package/dist/plugin.cjs.map +1 -1
  156. package/dist/plugin.d.mts +2 -1
  157. package/dist/plugin.d.ts +2 -1
  158. package/dist/plugin.mjs +23 -21
  159. package/dist/{production-runtime-DRkCHUvJ.d.ts → production-runtime-BoJDc5mc.d.ts} +1 -1
  160. package/dist/{production-runtime-flrjp_nC.d.mts → production-runtime-DPfgMV9Y.d.mts} +1 -1
  161. package/dist/routes.cjs +276 -6
  162. package/dist/routes.cjs.map +1 -1
  163. package/dist/routes.d.mts +1 -1
  164. package/dist/routes.d.ts +1 -1
  165. package/dist/routes.mjs +4 -4
  166. package/dist/runtime-9nQjVM7X.d.mts +206 -0
  167. package/dist/runtime-DgeS2skB.d.ts +206 -0
  168. package/dist/server-action-security.cjs +28 -10
  169. package/dist/server-action-security.cjs.map +1 -1
  170. package/dist/server-action-security.d.mts +2 -1
  171. package/dist/server-action-security.d.ts +2 -1
  172. package/dist/server-action-security.mjs +2 -2
  173. package/dist/server-fn-client.cjs.map +1 -1
  174. package/dist/server-fn-client.d.mts +1 -0
  175. package/dist/server-fn-client.d.ts +1 -0
  176. package/dist/server-fn-client.mjs +1 -1
  177. package/dist/server-fn.cjs +785 -3
  178. package/dist/server-fn.cjs.map +1 -1
  179. package/dist/server-fn.d.mts +10 -1
  180. package/dist/server-fn.d.ts +10 -1
  181. package/dist/server-fn.mjs +6 -3
  182. package/dist/server-plugins.d.mts +3 -2
  183. package/dist/server-plugins.d.ts +3 -2
  184. package/dist/server-query-client.cjs +278 -7
  185. package/dist/server-query-client.cjs.map +1 -1
  186. package/dist/server-query-client.mjs +4 -4
  187. package/dist/server-query.cjs +381 -10
  188. package/dist/server-query.cjs.map +1 -1
  189. package/dist/server-query.mjs +5 -5
  190. package/dist/server.cjs +1233 -118
  191. package/dist/server.cjs.map +1 -1
  192. package/dist/server.d.mts +8 -7
  193. package/dist/server.d.ts +8 -7
  194. package/dist/server.mjs +53 -33
  195. package/dist/server.mjs.map +1 -1
  196. package/dist/testing.cjs +436 -25
  197. package/dist/testing.cjs.map +1 -1
  198. package/dist/testing.d.mts +4 -3
  199. package/dist/testing.d.ts +4 -3
  200. package/dist/testing.mjs +19 -18
  201. package/dist/testing.mjs.map +1 -1
  202. package/dist/transport-BmbGTXA5.d.mts +49 -0
  203. package/dist/transport-BmbGTXA5.d.ts +49 -0
  204. package/dist/{utils-DOXXivjL.d.mts → utils-CHXlMDkg.d.mts} +1 -1
  205. package/dist/{utils-DA1yo54_.d.ts → utils-Drsyz1Vj.d.ts} +1 -1
  206. package/dist/version.cjs +36 -0
  207. package/dist/version.cjs.map +1 -0
  208. package/dist/version.d.mts +3 -0
  209. package/dist/version.d.ts +3 -0
  210. package/dist/version.mjs +8 -0
  211. package/dist/version.mjs.map +1 -0
  212. package/dist/vite.cjs +1039 -93
  213. package/dist/vite.cjs.map +1 -1
  214. package/dist/vite.d.mts +2 -1
  215. package/dist/vite.d.ts +2 -1
  216. package/dist/vite.mjs +23 -21
  217. package/dist/workflows.cjs +6 -8
  218. package/dist/workflows.cjs.map +1 -1
  219. package/dist/workflows.mjs +1 -1
  220. package/package.json +12 -5
  221. package/types/api.d.ts +44 -0
  222. package/types/client.d.ts +220 -16
  223. package/dist/chunk-4PTK6KT4.mjs +0 -364
  224. package/dist/chunk-4PTK6KT4.mjs.map +0 -1
  225. package/dist/chunk-AGS7UJ52.mjs.map +0 -1
  226. package/dist/chunk-CHFHNEMO.mjs.map +0 -1
  227. package/dist/chunk-EVGBEZDF.mjs +0 -215
  228. package/dist/chunk-EVGBEZDF.mjs.map +0 -1
  229. package/dist/chunk-G3B5BRDY.mjs.map +0 -1
  230. package/dist/chunk-GZP5W4K3.mjs.map +0 -1
  231. package/dist/chunk-JLWMCADZ.mjs.map +0 -1
  232. package/dist/chunk-JN3UU635.mjs.map +0 -1
  233. package/dist/chunk-OYXO65LI.mjs.map +0 -1
  234. package/dist/chunk-PRNYWD7K.mjs.map +0 -1
  235. package/dist/chunk-QF4PHCMA.mjs.map +0 -1
  236. package/dist/chunk-QFQRHRZS.mjs.map +0 -1
  237. package/dist/chunk-RKI6TN3A.mjs.map +0 -1
  238. package/dist/chunk-S2XHZS7W.mjs.map +0 -1
  239. package/dist/chunk-SOU3Q3ZA.mjs.map +0 -1
  240. package/dist/chunk-TU4YJICB.mjs.map +0 -1
  241. package/dist/chunk-TXCFGOHW.mjs +0 -42
  242. package/dist/chunk-TXCFGOHW.mjs.map +0 -1
  243. package/dist/chunk-UHE2TTLK.mjs.map +0 -1
  244. package/dist/chunk-Y2OJJD7R.mjs.map +0 -1
  245. package/dist/chunk-Y7A27276.mjs.map +0 -1
  246. package/dist/chunk-ZJQNATYV.mjs.map +0 -1
  247. package/dist/devtools-client-6WPY2AQU.mjs.map +0 -1
  248. /package/dist/{chunk-TRORP7E4.mjs.map → chunk-3U3ZUQAQ.mjs.map} +0 -0
  249. /package/dist/{chunk-B6N4ZB2H.mjs.map → chunk-C2ROET2V.mjs.map} +0 -0
  250. /package/dist/{chunk-HXP3ZSPC.mjs.map → chunk-SADGIRS2.mjs.map} +0 -0
  251. /package/dist/{chunk-6XDUHOEM.mjs.map → chunk-TQAEEJB4.mjs.map} +0 -0
  252. /package/dist/{chunk-SXZTEK3V.mjs.map → chunk-UXUAWNO5.mjs.map} +0 -0
  253. /package/dist/{chunk-YHU7SS6L.mjs.map → chunk-Z5CQMP54.mjs.map} +0 -0
  254. /package/dist/{devtools-AE4RVXCM.mjs.map → devtools-LVC3VB5P.mjs.map} +0 -0
package/dist/plugin.cjs CHANGED
@@ -309,7 +309,7 @@ function createFarmMarkdownRouteModule(input) {
309
309
  ...props,
310
310
  components: {
311
311
  ...components,
312
- ...props?.components || {}
312
+ ...props?.components
313
313
  }
314
314
  })
315
315
  );
@@ -826,7 +826,11 @@ async function prepareFarmWorkflowsForNitro(config) {
826
826
  };
827
827
  }
828
828
  const handlerPath = path22.join(generatedDir, "http-handler.mjs");
829
- await fs8.writeFile(handlerPath, createNitroWorkflowHTTPHandler(workflowConfig, workflows), "utf8");
829
+ await fs8.writeFile(
830
+ handlerPath,
831
+ createNitroWorkflowHTTPHandler(workflowConfig, workflows),
832
+ "utf8"
833
+ );
830
834
  const manifestPath = path22.join(generatedDir, "manifest.json");
831
835
  await fs8.writeFile(
832
836
  manifestPath,
@@ -900,13 +904,7 @@ function normalizeSchedule(schedule) {
900
904
  return (Array.isArray(schedule) ? schedule : [schedule]).map((value) => value.trim()).filter(Boolean);
901
905
  }
902
906
  function resolveWorkflowDefinition(module2) {
903
- const candidates = [
904
- module2.default,
905
- module2.workflow,
906
- module2.cron,
907
- module2.task,
908
- module2.job
909
- ];
907
+ const candidates = [module2.default, module2.workflow, module2.cron, module2.task, module2.job];
910
908
  for (const candidate of candidates) {
911
909
  if (candidate && typeof candidate === "object" && typeof candidate.run === "function") {
912
910
  return candidate;
@@ -1899,8 +1897,8 @@ function compareFarmRouteRuleSpecificity(left, right) {
1899
1897
  }
1900
1898
  function getFarmRouteRuleSpecificity(pattern) {
1901
1899
  return normalizeRoutePattern(pattern).split("/").filter(Boolean).reduce((score, segment) => {
1902
- if (segment === "**" || /^\[\[\.\.\./.test(segment)) return score + 1;
1903
- if (segment === "*" || /^\[\.\.\./.test(segment)) return score + 10;
1900
+ if (segment === "**" || segment.startsWith("[[...")) return score + 1;
1901
+ if (segment === "*" || segment.startsWith("[...")) return score + 10;
1904
1902
  if (/^\[.+\]$/.test(segment) || /^:.+$/.test(segment)) return score + 50;
1905
1903
  return score + 100;
1906
1904
  }, 0);
@@ -1960,22 +1958,50 @@ var init_route_rules = __esm({
1960
1958
  // src/cache-invalidation.ts
1961
1959
  function getFarmCacheInvalidationState() {
1962
1960
  return globalState[FARM_CACHE_INVALIDATION_STATE] ?? (globalState[FARM_CACHE_INVALIDATION_STATE] = {
1963
- listeners: /* @__PURE__ */ new Set()
1961
+ listeners: /* @__PURE__ */ new Set(),
1962
+ taskListeners: /* @__PURE__ */ new Set()
1964
1963
  });
1965
1964
  }
1965
+ function encodeFarmCacheInvalidations(keys) {
1966
+ const normalized = Array.from(
1967
+ new Set(keys.filter((key) => typeof key === "string" && key.length > 0))
1968
+ );
1969
+ if (normalized.length === 0) return null;
1970
+ return encodeURIComponent(JSON.stringify(normalized));
1971
+ }
1972
+ function decodeFarmCacheInvalidations(value) {
1973
+ if (!value) return [];
1974
+ try {
1975
+ const parsed = JSON.parse(decodeURIComponent(value));
1976
+ return Array.isArray(parsed) ? Array.from(
1977
+ new Set(parsed.filter((key) => typeof key === "string" && key.length > 0))
1978
+ ) : [];
1979
+ } catch {
1980
+ return [];
1981
+ }
1982
+ }
1966
1983
  function subscribeFarmCacheInvalidation(listener) {
1967
1984
  const state = getFarmCacheInvalidationState();
1968
1985
  state.listeners.add(listener);
1969
1986
  return () => state.listeners.delete(listener);
1970
1987
  }
1971
- var FARM_CACHE_INVALIDATION_STATE, globalState;
1988
+ function subscribeFarmCacheTask(listener) {
1989
+ const state = getFarmCacheInvalidationState();
1990
+ state.taskListeners.add(listener);
1991
+ return () => state.taskListeners.delete(listener);
1992
+ }
1993
+ var FARM_CACHE_INVALIDATION_HEADER, FARM_CACHE_INVALIDATION_STATE, globalState;
1972
1994
  var init_cache_invalidation = __esm({
1973
1995
  "src/cache-invalidation.ts"() {
1974
1996
  "use strict";
1997
+ FARM_CACHE_INVALIDATION_HEADER = "x-farm-cache-invalidations";
1975
1998
  FARM_CACHE_INVALIDATION_STATE = /* @__PURE__ */ Symbol.for("farm.cacheInvalidationState");
1976
1999
  globalState = globalThis;
1977
2000
  __name(getFarmCacheInvalidationState, "getFarmCacheInvalidationState");
2001
+ __name(encodeFarmCacheInvalidations, "encodeFarmCacheInvalidations");
2002
+ __name(decodeFarmCacheInvalidations, "decodeFarmCacheInvalidations");
1978
2003
  __name(subscribeFarmCacheInvalidation, "subscribeFarmCacheInvalidation");
2004
+ __name(subscribeFarmCacheTask, "subscribeFarmCacheTask");
1979
2005
  }
1980
2006
  });
1981
2007
 
@@ -2079,6 +2105,9 @@ var init_server_action_security = __esm({
2079
2105
  subscribeFarmCacheInvalidation((key) => {
2080
2106
  getServerActionExecutionContext()?.invalidations.add(key);
2081
2107
  });
2108
+ subscribeFarmCacheTask((task) => {
2109
+ getServerActionExecutionContext()?.cacheTasks.add(task);
2110
+ });
2082
2111
  __name(getServerActionStorage, "getServerActionStorage");
2083
2112
  __name(parseBodySizeLimit, "parseBodySizeLimit");
2084
2113
  __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
@@ -4534,7 +4563,9 @@ function readManifest(contentDir) {
4534
4563
  const manifestPath = import_node_path7.default.join(contentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
4535
4564
  if (!(0, import_node_fs5.existsSync)(manifestPath)) return null;
4536
4565
  try {
4537
- const parsed = JSON.parse((0, import_node_fs5.readFileSync)(manifestPath, "utf8"));
4566
+ const parsed = JSON.parse(
4567
+ (0, import_node_fs5.readFileSync)(manifestPath, "utf8")
4568
+ );
4538
4569
  if (parsed.version !== 1 || !parsed.pages || typeof parsed.pages !== "object") return null;
4539
4570
  const pages = Object.fromEntries(
4540
4571
  Object.entries(parsed.pages).filter(
@@ -10706,16 +10737,7 @@ __export(devtools_client_exports, {
10706
10737
  function parseShortcut(shortcut) {
10707
10738
  const tokens = shortcut.toLowerCase().split("+").map((token) => token.trim()).filter(Boolean);
10708
10739
  const key = tokens.find(
10709
- (token) => ![
10710
- "alt",
10711
- "ctrl",
10712
- "control",
10713
- "meta",
10714
- "cmd",
10715
- "command",
10716
- "mod",
10717
- "shift"
10718
- ].includes(token)
10740
+ (token) => !["alt", "ctrl", "control", "meta", "cmd", "command", "mod", "shift"].includes(token)
10719
10741
  );
10720
10742
  if (!key) {
10721
10743
  return null;
@@ -11655,11 +11677,51 @@ __name(getActiveFarmI18nSnapshot, "getActiveFarmI18nSnapshot");
11655
11677
  // src/cache.ts
11656
11678
  init_cache_invalidation();
11657
11679
  var _FarmDataCache = class _FarmDataCache {
11658
- constructor() {
11680
+ constructor(config = {}) {
11659
11681
  this.entries = /* @__PURE__ */ new Map();
11660
11682
  this.inflight = /* @__PURE__ */ new Map();
11661
11683
  this.invalidatedTagVersions = /* @__PURE__ */ new Map();
11662
11684
  this.version = 0;
11685
+ this.namespace = "farm";
11686
+ this.local = true;
11687
+ this.lease = {
11688
+ enabled: true,
11689
+ ttlMs: 1e4,
11690
+ waitTimeoutMs: 1e4,
11691
+ pollIntervalMs: 25
11692
+ };
11693
+ this.configure(config);
11694
+ }
11695
+ configure(config = {}) {
11696
+ this.adapter = config.adapter;
11697
+ this.namespace = normalizeCacheNamespace(config.namespace || "farm");
11698
+ this.local = !config.adapter;
11699
+ this.lease = config.lease === false ? { ...this.lease, enabled: false } : {
11700
+ enabled: true,
11701
+ ttlMs: normalizePositiveDuration(config.lease?.ttlMs, 1e4, "cache.lease.ttlMs"),
11702
+ waitTimeoutMs: normalizePositiveDuration(
11703
+ config.lease?.waitTimeoutMs,
11704
+ 1e4,
11705
+ "cache.lease.waitTimeoutMs"
11706
+ ),
11707
+ pollIntervalMs: normalizePositiveDuration(
11708
+ config.lease?.pollIntervalMs,
11709
+ 25,
11710
+ "cache.lease.pollIntervalMs"
11711
+ )
11712
+ };
11713
+ if (!this.local) {
11714
+ this.entries.clear();
11715
+ this.inflight.clear();
11716
+ this.invalidatedTagVersions.clear();
11717
+ this.version = 0;
11718
+ }
11719
+ }
11720
+ get adapterName() {
11721
+ return this.adapter?.name || (this.adapter ? "custom" : "memory");
11722
+ }
11723
+ get hasAdapter() {
11724
+ return this.adapter !== void 0;
11663
11725
  }
11664
11726
  get size() {
11665
11727
  return this.entries.size;
@@ -11695,6 +11757,45 @@ var _FarmDataCache = class _FarmDataCache {
11695
11757
  });
11696
11758
  return this.toPublicEntry(entry);
11697
11759
  }
11760
+ async getEntryAsync(key, options = {}) {
11761
+ if (this.local) {
11762
+ const localEntry = this.getEntry(key, options);
11763
+ if (localEntry) return localEntry;
11764
+ }
11765
+ if (!this.adapter) {
11766
+ return this.local ? void 0 : this.getEntry(key, options);
11767
+ }
11768
+ const entry = await this.adapter.get(this.createAdapterKey(key));
11769
+ if (!entry) {
11770
+ emitFarmEvent({ type: "cache.miss", key });
11771
+ return void 0;
11772
+ }
11773
+ assertFarmCacheEntry(entry, key);
11774
+ const stale = await this.isAdapterEntryStale(entry, options.now);
11775
+ if (stale) {
11776
+ emitFarmEvent({
11777
+ type: "cache.stale",
11778
+ key,
11779
+ tags: [...entry.tags],
11780
+ revalidate: entry.revalidate
11781
+ });
11782
+ if (!options.allowStale) {
11783
+ emitFarmEvent({ type: "cache.miss", key, reason: "stale" });
11784
+ return void 0;
11785
+ }
11786
+ }
11787
+ emitFarmEvent({
11788
+ type: "cache.hit",
11789
+ key,
11790
+ tags: [...entry.tags],
11791
+ revalidate: entry.revalidate,
11792
+ stale
11793
+ });
11794
+ if (this.local && !stale) {
11795
+ this.hydrateLocalEntry(entry);
11796
+ }
11797
+ return { ...entry, key };
11798
+ }
11698
11799
  set(key, value, options = {}) {
11699
11800
  const tags = /* @__PURE__ */ new Set();
11700
11801
  for (const tag of options.tags ?? []) {
@@ -11707,6 +11808,7 @@ var _FarmDataCache = class _FarmDataCache {
11707
11808
  key,
11708
11809
  value,
11709
11810
  tags,
11811
+ tagVersions: void 0,
11710
11812
  createdAt: options.createdAt ?? Date.now(),
11711
11813
  createdVersion: ++this.version,
11712
11814
  revalidate: normalizeRevalidate(options.revalidate)
@@ -11720,11 +11822,45 @@ var _FarmDataCache = class _FarmDataCache {
11720
11822
  });
11721
11823
  return this.toPublicEntry(entry);
11722
11824
  }
11825
+ async setAsync(key, value, options = {}, tagVersions) {
11826
+ const tags = normalizeCacheOptionsTags(options);
11827
+ const capturedVersions = tagVersions ?? await this.getAdapterTagVersions(Array.from(tags.values()));
11828
+ const entry = {
11829
+ key,
11830
+ value,
11831
+ tags: Array.from(tags),
11832
+ tagVersions: capturedVersions,
11833
+ createdAt: options.createdAt ?? Date.now(),
11834
+ createdVersion: ++this.version,
11835
+ revalidate: normalizeRevalidate(options.revalidate)
11836
+ };
11837
+ if (this.local) {
11838
+ this.hydrateLocalEntry(entry);
11839
+ }
11840
+ if (this.adapter) {
11841
+ await this.adapter.set(this.createAdapterKey(key), entry);
11842
+ }
11843
+ emitFarmEvent({
11844
+ type: "cache.set",
11845
+ key,
11846
+ tags: [...entry.tags],
11847
+ revalidate: entry.revalidate
11848
+ });
11849
+ return entry;
11850
+ }
11723
11851
  delete(key) {
11724
11852
  const deleted = this.entries.delete(key);
11725
11853
  emitFarmEvent({ type: "cache.delete", key, deleted });
11726
11854
  return deleted;
11727
11855
  }
11856
+ async deleteAsync(key) {
11857
+ const deleted = this.entries.delete(key);
11858
+ if (this.adapter) {
11859
+ await this.adapter.delete(this.createAdapterKey(key));
11860
+ }
11861
+ emitFarmEvent({ type: "cache.delete", key, deleted: this.adapter ? true : deleted });
11862
+ return this.adapter ? true : deleted;
11863
+ }
11728
11864
  clear() {
11729
11865
  const count = this.entries.size;
11730
11866
  this.entries.clear();
@@ -11733,6 +11869,10 @@ var _FarmDataCache = class _FarmDataCache {
11733
11869
  this.version = 0;
11734
11870
  emitFarmEvent({ type: "cache.clear", count });
11735
11871
  }
11872
+ async clearAsync() {
11873
+ this.clear();
11874
+ await this.adapter?.clear?.();
11875
+ }
11736
11876
  isStale(entry, now = Date.now()) {
11737
11877
  if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
11738
11878
  return true;
@@ -11745,6 +11885,12 @@ var _FarmDataCache = class _FarmDataCache {
11745
11885
  }
11746
11886
  return false;
11747
11887
  }
11888
+ async isStaleAsync(entry, now = Date.now()) {
11889
+ if (this.adapter) {
11890
+ return this.isAdapterEntryStale(entry, now);
11891
+ }
11892
+ return this.isStale(entry, now);
11893
+ }
11748
11894
  revalidateTag(tag, options = {}) {
11749
11895
  const normalized = normalizeCacheTag(tag);
11750
11896
  const count = this.invalidateTag(normalized);
@@ -11753,6 +11899,15 @@ var _FarmDataCache = class _FarmDataCache {
11753
11899
  );
11754
11900
  return count;
11755
11901
  }
11902
+ async revalidateTagAsync(tag, options = {}) {
11903
+ const normalized = normalizeCacheTag(tag);
11904
+ const count = this.invalidateTag(normalized);
11905
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(normalized)]);
11906
+ emitFarmEvent(
11907
+ options.source === "updateTag" ? { type: "cache.updateTag", tag: normalized, count } : { type: "cache.revalidateTag", tag: normalized, profile: options.profile, count }
11908
+ );
11909
+ return count;
11910
+ }
11756
11911
  revalidatePath(routePath) {
11757
11912
  const normalizedPath = normalizeRevalidatePath(routePath);
11758
11913
  const pathTag = createPathCacheTag(normalizedPath);
@@ -11769,8 +11924,25 @@ var _FarmDataCache = class _FarmDataCache {
11769
11924
  }
11770
11925
  return count;
11771
11926
  }
11927
+ async revalidatePathAsync(routePath) {
11928
+ const normalizedPath = normalizeRevalidatePath(routePath);
11929
+ const pathTag = createPathCacheTag(normalizedPath);
11930
+ const pprCount = this.countEntriesForTags([pathTag, "ppr"]);
11931
+ const count = this.invalidateTag(pathTag);
11932
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(pathTag)]);
11933
+ emitFarmEvent({ type: "cache.revalidatePath", path: normalizedPath, count });
11934
+ if (pprCount > 0) {
11935
+ emitFarmEvent({
11936
+ type: "ppr.shell.invalidated",
11937
+ route: normalizedPath,
11938
+ reason: "revalidatePath",
11939
+ count: pprCount
11940
+ });
11941
+ }
11942
+ return count;
11943
+ }
11772
11944
  async getOrSet(key, producer, options = {}) {
11773
- const cached = this.getEntry(key);
11945
+ const cached = await this.getEntryAsync(key);
11774
11946
  if (cached) {
11775
11947
  return cached.value;
11776
11948
  }
@@ -11779,10 +11951,8 @@ var _FarmDataCache = class _FarmDataCache {
11779
11951
  emitFarmEvent({ type: "cache.dedupe", key });
11780
11952
  return inflight;
11781
11953
  }
11782
- const promise = Promise.resolve().then(producer).then((value) => {
11783
- this.set(key, value, options);
11784
- return value;
11785
- }).catch((error) => {
11954
+ const tags = Array.from(normalizeCacheOptionsTags(options));
11955
+ const promise = this.fillCacheEntry(key, producer, options, tags).catch((error) => {
11786
11956
  emitFarmEvent({ type: "cache.error", key, operation: "set", error });
11787
11957
  throw error;
11788
11958
  }).finally(() => {
@@ -11791,6 +11961,40 @@ var _FarmDataCache = class _FarmDataCache {
11791
11961
  this.inflight.set(key, promise);
11792
11962
  return promise;
11793
11963
  }
11964
+ async fillCacheEntry(key, producer, options, tags) {
11965
+ const leaseKey = `${this.namespace}:lease:${key}`;
11966
+ let leaseToken;
11967
+ if (this.adapter?.acquireLease && this.adapter.releaseLease && this.lease.enabled) {
11968
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
11969
+ if (!leaseToken) {
11970
+ const shared = await this.waitForAdapterEntry(key);
11971
+ if (shared) {
11972
+ emitFarmEvent({ type: "cache.dedupe", key });
11973
+ return shared.value;
11974
+ }
11975
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
11976
+ }
11977
+ }
11978
+ try {
11979
+ const initialTagVersions = await this.getAdapterTagVersions(tags);
11980
+ const value = await producer();
11981
+ await this.setAsync(key, value, options, initialTagVersions);
11982
+ return value;
11983
+ } finally {
11984
+ if (leaseToken && this.adapter?.releaseLease) {
11985
+ await this.adapter.releaseLease(leaseKey, leaseToken);
11986
+ }
11987
+ }
11988
+ }
11989
+ async waitForAdapterEntry(key) {
11990
+ const deadline = Date.now() + this.lease.waitTimeoutMs;
11991
+ while (Date.now() < deadline) {
11992
+ await delay(this.lease.pollIntervalMs);
11993
+ const entry = await this.getEntryAsync(key);
11994
+ if (entry) return entry;
11995
+ }
11996
+ return void 0;
11997
+ }
11794
11998
  countEntriesForTag(tag) {
11795
11999
  let count = 0;
11796
12000
  for (const entry of this.entries.values()) {
@@ -11818,11 +12022,53 @@ var _FarmDataCache = class _FarmDataCache {
11818
12022
  key: entry.key,
11819
12023
  value: entry.value,
11820
12024
  tags: Array.from(entry.tags),
12025
+ tagVersions: entry.tagVersions,
11821
12026
  createdAt: entry.createdAt,
11822
12027
  createdVersion: entry.createdVersion,
11823
12028
  revalidate: entry.revalidate
11824
12029
  };
11825
12030
  }
12031
+ hydrateLocalEntry(entry) {
12032
+ this.entries.set(entry.key, {
12033
+ key: entry.key,
12034
+ value: entry.value,
12035
+ tags: new Set(entry.tags.map(normalizeCacheTag)),
12036
+ tagVersions: entry.tagVersions,
12037
+ createdAt: entry.createdAt,
12038
+ createdVersion: entry.createdVersion ?? ++this.version,
12039
+ revalidate: normalizeRevalidate(entry.revalidate)
12040
+ });
12041
+ }
12042
+ createAdapterKey(key) {
12043
+ return `${this.namespace}:entry:${key}`;
12044
+ }
12045
+ createAdapterTag(tag) {
12046
+ return `${this.namespace}:tag:${tag}`;
12047
+ }
12048
+ async getAdapterTagVersions(tags) {
12049
+ if (!this.adapter?.getTagVersions || tags.length === 0) return {};
12050
+ const normalized = tags.map(normalizeCacheTag);
12051
+ const physicalTags = normalized.map((tag) => this.createAdapterTag(tag));
12052
+ const versions = await this.adapter.getTagVersions(physicalTags);
12053
+ return Object.fromEntries(
12054
+ normalized.map((tag, index) => [
12055
+ tag,
12056
+ normalizeAdapterVersion(versions[physicalTags[index]])
12057
+ ])
12058
+ );
12059
+ }
12060
+ async isAdapterEntryStale(entry, now = Date.now()) {
12061
+ if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
12062
+ return true;
12063
+ }
12064
+ const currentVersions = await this.getAdapterTagVersions(entry.tags);
12065
+ for (const tag of entry.tags) {
12066
+ if (normalizeAdapterVersion(currentVersions[tag]) > normalizeAdapterVersion(entry.tagVersions?.[tag])) {
12067
+ return true;
12068
+ }
12069
+ }
12070
+ return false;
12071
+ }
11826
12072
  };
11827
12073
  __name(_FarmDataCache, "FarmDataCache");
11828
12074
  var FarmDataCache = _FarmDataCache;
@@ -11835,6 +12081,10 @@ function getFarmDataCache() {
11835
12081
  return sharedFarmDataCache;
11836
12082
  }
11837
12083
  __name(getFarmDataCache, "getFarmDataCache");
12084
+ function configureFarmCache(config) {
12085
+ sharedFarmDataCache.configure(config);
12086
+ }
12087
+ __name(configureFarmCache, "configureFarmCache");
11838
12088
  function createPathCacheTag(routePath) {
11839
12089
  return `path:${normalizeRevalidatePath(routePath)}`;
11840
12090
  }
@@ -11895,6 +12145,52 @@ function normalizeCacheTag(tag) {
11895
12145
  return normalized;
11896
12146
  }
11897
12147
  __name(normalizeCacheTag, "normalizeCacheTag");
12148
+ function normalizeCacheNamespace(namespace) {
12149
+ if (typeof namespace !== "string") {
12150
+ throw new TypeError("Cache namespace must be a string.");
12151
+ }
12152
+ const normalized = namespace.trim().replace(/:+$/g, "");
12153
+ if (!normalized) {
12154
+ throw new Error("Cache namespace cannot be empty.");
12155
+ }
12156
+ return normalized;
12157
+ }
12158
+ __name(normalizeCacheNamespace, "normalizeCacheNamespace");
12159
+ function normalizeAdapterVersion(value) {
12160
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
12161
+ }
12162
+ __name(normalizeAdapterVersion, "normalizeAdapterVersion");
12163
+ function normalizePositiveDuration(value, fallback, name) {
12164
+ if (value === void 0) return fallback;
12165
+ if (!Number.isFinite(value) || value <= 0) {
12166
+ throw new TypeError(`${name} must be a positive number of milliseconds.`);
12167
+ }
12168
+ return Math.floor(value);
12169
+ }
12170
+ __name(normalizePositiveDuration, "normalizePositiveDuration");
12171
+ function delay(ms) {
12172
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
12173
+ }
12174
+ __name(delay, "delay");
12175
+ function normalizeCacheOptionsTags(options) {
12176
+ const tags = /* @__PURE__ */ new Set();
12177
+ for (const tag of options.tags ?? []) {
12178
+ tags.add(normalizeCacheTag(tag));
12179
+ }
12180
+ for (const routePath of options.paths ?? []) {
12181
+ tags.add(createPathCacheTag(routePath));
12182
+ }
12183
+ return tags;
12184
+ }
12185
+ __name(normalizeCacheOptionsTags, "normalizeCacheOptionsTags");
12186
+ function assertFarmCacheEntry(entry, expectedKey) {
12187
+ if (!entry || typeof entry !== "object" || entry.key !== expectedKey || !Array.isArray(entry.tags) || typeof entry.createdAt !== "number") {
12188
+ throw new TypeError(
12189
+ `Cache adapter returned an invalid entry for ${JSON.stringify(expectedKey)}.`
12190
+ );
12191
+ }
12192
+ }
12193
+ __name(assertFarmCacheEntry, "assertFarmCacheEntry");
11898
12194
  function getFunctionCacheIdentity(fn) {
11899
12195
  if (fn.name) {
11900
12196
  return `name:${fn.name}`;
@@ -13035,6 +13331,76 @@ function normalizePathname(pathname) {
13035
13331
  }
13036
13332
  __name(normalizePathname, "normalizePathname");
13037
13333
 
13334
+ // src/routing/route-slots.ts
13335
+ var SLOT_SEGMENT = /^@([A-Za-z][A-Za-z0-9_-]*)$/;
13336
+ function parseRouteSlotFile(filePath) {
13337
+ const normalized = filePath.replace(/\\/g, "/");
13338
+ const parts = normalized.split("/").filter(Boolean);
13339
+ const fileName = parts.at(-1) ?? "";
13340
+ const baseName = fileName.replace(/\.(tsx?|jsx?)$/, "");
13341
+ if (baseName !== "page" && baseName !== "default") return null;
13342
+ const slotIndex = parts.findIndex((part) => SLOT_SEGMENT.test(part));
13343
+ if (slotIndex < 0) return null;
13344
+ if (parts.slice(slotIndex + 1, -1).some((part) => SLOT_SEGMENT.test(part))) {
13345
+ throw new Error(`Nested route slots are not supported in "${filePath}"`);
13346
+ }
13347
+ const slotMatch = parts[slotIndex].match(SLOT_SEGMENT);
13348
+ const name = slotMatch?.[1];
13349
+ if (!name) return null;
13350
+ const ownerParts = parts.slice(0, slotIndex);
13351
+ const contentParts = parts.slice(slotIndex + 1, -1);
13352
+ const marker = parseInterceptionMarker(contentParts[0]);
13353
+ let targetBase = ownerParts;
13354
+ let targetParts = contentParts;
13355
+ if (marker) {
13356
+ targetBase = marker.root === true ? [] : ownerParts.slice(0, Math.max(0, ownerParts.length - marker.up));
13357
+ targetParts = [...marker.remainder ? [marker.remainder] : [], ...contentParts.slice(1)];
13358
+ }
13359
+ const ownerFile = [...ownerParts, "layout.tsx"].join("/");
13360
+ const routeFile = [
13361
+ ...baseName === "default" ? ownerParts : targetBase,
13362
+ ...baseName === "default" ? [] : targetParts,
13363
+ "page.tsx"
13364
+ ].join("/");
13365
+ return {
13366
+ name,
13367
+ ownerRoute: parseRoutePath(ownerFile || "layout.tsx"),
13368
+ route: parseRoutePath(routeFile || "page.tsx"),
13369
+ interception: marker !== null,
13370
+ fallback: baseName === "default"
13371
+ };
13372
+ }
13373
+ __name(parseRouteSlotFile, "parseRouteSlotFile");
13374
+ function createRouteSlotContainerId(name, ownerPattern) {
13375
+ const owner = ownerPattern === "/" ? "root" : ownerPattern.replace(/^\//, "").replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
13376
+ return `__farm_slot_${name}_${owner || "root"}__`;
13377
+ }
13378
+ __name(createRouteSlotContainerId, "createRouteSlotContainerId");
13379
+ function parseInterceptionMarker(segment) {
13380
+ if (!segment) return null;
13381
+ if (segment.startsWith("(...)")) {
13382
+ return {
13383
+ up: 0,
13384
+ root: true,
13385
+ remainder: segment.slice(5)
13386
+ };
13387
+ }
13388
+ if (segment.startsWith("(.)")) {
13389
+ return {
13390
+ up: 0,
13391
+ remainder: segment.slice(3)
13392
+ };
13393
+ }
13394
+ let remainder = segment;
13395
+ let up = 0;
13396
+ while (remainder.startsWith("(..)")) {
13397
+ up += 1;
13398
+ remainder = remainder.slice(4);
13399
+ }
13400
+ return up > 0 ? { up, remainder } : null;
13401
+ }
13402
+ __name(parseInterceptionMarker, "parseInterceptionMarker");
13403
+
13038
13404
  // src/routing/route-manager.ts
13039
13405
  function routeSpecificity(entry) {
13040
13406
  return entry.route.segments.reduce((score, segment) => {
@@ -13048,6 +13414,7 @@ var _RouteManager = class _RouteManager {
13048
13414
  constructor(config, viteServer) {
13049
13415
  this.routes = /* @__PURE__ */ new Map();
13050
13416
  this.layouts = /* @__PURE__ */ new Map();
13417
+ this.routeSlots = /* @__PURE__ */ new Map();
13051
13418
  this.loadings = /* @__PURE__ */ new Map();
13052
13419
  this.errors = /* @__PURE__ */ new Map();
13053
13420
  this.metadataImages = /* @__PURE__ */ new Map();
@@ -13063,6 +13430,7 @@ var _RouteManager = class _RouteManager {
13063
13430
  async discoverRoutes() {
13064
13431
  this.routes.clear();
13065
13432
  this.layouts.clear();
13433
+ this.routeSlots.clear();
13066
13434
  this.loadings.clear();
13067
13435
  this.errors.clear();
13068
13436
  this.metadataImages.clear();
@@ -13073,6 +13441,13 @@ var _RouteManager = class _RouteManager {
13073
13441
  await this.discoverFileRoutes(source);
13074
13442
  await this.discoverProgrammaticRoutes(source);
13075
13443
  }
13444
+ for (const slot of this.routeSlots.values()) {
13445
+ if (slot.interception && !this.routes.has(slot.pattern)) {
13446
+ throw new Error(
13447
+ `Intercepting route slot "${slot.name}" targets "${slot.pattern}", but no canonical page exists for that URL`
13448
+ );
13449
+ }
13450
+ }
13076
13451
  this.routes = new Map(
13077
13452
  Array.from(this.routes.entries()).sort(
13078
13453
  ([, left], [, right]) => routeSpecificity(right) - routeSpecificity(left)
@@ -13088,7 +13463,7 @@ var _RouteManager = class _RouteManager {
13088
13463
  /**
13089
13464
  * Find matching route for a given URL path
13090
13465
  */
13091
- matchRoute(pathname) {
13466
+ matchRoute(pathname, options = {}) {
13092
13467
  const routePathname = this.toRoutePathname(pathname);
13093
13468
  const normalizedPath = routePathname === "/" ? "/" : routePathname.replace(/\/$/, "");
13094
13469
  let matchedRoute = null;
@@ -13102,10 +13477,12 @@ var _RouteManager = class _RouteManager {
13102
13477
  }
13103
13478
  }
13104
13479
  const layouts = this.findMatchingLayouts(normalizedPath);
13480
+ const slots = this.findMatchingRouteSlots(normalizedPath, options.interceptFrom);
13105
13481
  return {
13106
13482
  route: matchedRoute,
13107
13483
  params,
13108
- layouts
13484
+ layouts,
13485
+ slots
13109
13486
  };
13110
13487
  }
13111
13488
  /**
@@ -13120,6 +13497,9 @@ var _RouteManager = class _RouteManager {
13120
13497
  getLayouts() {
13121
13498
  return new Map(this.layouts);
13122
13499
  }
13500
+ getRouteSlots() {
13501
+ return new Map(this.routeSlots);
13502
+ }
13123
13503
  /** Resolve route rules, inherited layouts, and the page export in precedence order. */
13124
13504
  async resolveRouteRuntimeConfig(pattern) {
13125
13505
  const routeEntry = this.routes.get(pattern);
@@ -13278,7 +13658,22 @@ var _RouteManager = class _RouteManager {
13278
13658
  pattern: entry.pattern,
13279
13659
  modulePath: toUrlPath(entry.modulePath)
13280
13660
  }));
13281
- return { routes, layouts };
13661
+ const slots = Array.from(this.routeSlots.values()).map((entry) => {
13662
+ const metadata = getClientModuleMetadata(entry.modulePath, projectRoot);
13663
+ return {
13664
+ name: entry.name,
13665
+ ownerPattern: entry.ownerPattern,
13666
+ pattern: entry.pattern,
13667
+ modulePath: toUrlPath(entry.modulePath),
13668
+ containerId: entry.containerId,
13669
+ interception: entry.interception,
13670
+ fallback: entry.fallback,
13671
+ shouldHydrate: metadata.shouldHydrate,
13672
+ isClientComponent: metadata.isClientComponent,
13673
+ segments: entry.route.segments
13674
+ };
13675
+ });
13676
+ return { routes, layouts, slots };
13282
13677
  }
13283
13678
  /**
13284
13679
  * Load a route module dynamically
@@ -13355,6 +13750,7 @@ var _RouteManager = class _RouteManager {
13355
13750
  async discoverFileRoutes(source) {
13356
13751
  const appDir = resolveAppPath(source.root, source.srcDir, "app");
13357
13752
  const pageFiles = await safeGlobFiles("**/page.{ts,tsx,js,jsx,md,mdx}", appDir);
13753
+ const slotDefaultFiles = await safeGlobFiles("**/@*/**/default.{ts,tsx,js,jsx}", appDir);
13358
13754
  const layoutFiles = await safeGlobFiles("**/layout.{ts,tsx,js,jsx}", appDir);
13359
13755
  const loadingFiles = await safeGlobFiles("**/loading.{ts,tsx,js,jsx}", appDir);
13360
13756
  const errorFiles = await safeGlobFiles("**/error.{ts,tsx,js,jsx}", appDir);
@@ -13363,7 +13759,7 @@ var _RouteManager = class _RouteManager {
13363
13759
  appDir
13364
13760
  );
13365
13761
  for (const [kind, files, target] of [
13366
- ["page", pageFiles, this.routes],
13762
+ ["page", pageFiles.filter((file) => parseRouteSlotFile(file) === null), this.routes],
13367
13763
  ["layout", layoutFiles, this.layouts],
13368
13764
  ["loading", loadingFiles, this.loadings],
13369
13765
  ["error", errorFiles, this.errors]
@@ -13387,6 +13783,32 @@ var _RouteManager = class _RouteManager {
13387
13783
  });
13388
13784
  }
13389
13785
  }
13786
+ for (const file of [...pageFiles, ...slotDefaultFiles]) {
13787
+ const slot = parseRouteSlotFile(file);
13788
+ if (!slot) continue;
13789
+ const modulePath = import_path5.default.join(appDir, file);
13790
+ const pattern = this.createRoutePattern(slot.route);
13791
+ const ownerPattern = this.createRoutePattern(slot.ownerRoute);
13792
+ const key = `${ownerPattern}:${slot.name}:${slot.interception ? "intercept" : "slot"}:${slot.fallback ? "default" : pattern}`;
13793
+ const existing = this.routeSlots.get(key);
13794
+ if (existing?.sourceRoot === source.root) {
13795
+ throw new Error(
13796
+ `Duplicate route slot "${slot.name}" for "${pattern}". Found both ${existing.modulePath} and ${modulePath}.`
13797
+ );
13798
+ }
13799
+ this.routeSlots.set(key, {
13800
+ route: slot.route,
13801
+ modulePath,
13802
+ pattern,
13803
+ source: "file",
13804
+ sourceRoot: source.root,
13805
+ name: slot.name,
13806
+ ownerPattern,
13807
+ interception: slot.interception,
13808
+ fallback: slot.fallback,
13809
+ containerId: createRouteSlotContainerId(slot.name, ownerPattern)
13810
+ });
13811
+ }
13390
13812
  for (const file of metadataImageFiles) {
13391
13813
  const fileBase = import_path5.default.basename(file, import_path5.default.extname(file));
13392
13814
  const kind = fileBase === "twitter-image" ? "twitter" : "opengraph";
@@ -13512,6 +13934,71 @@ var _RouteManager = class _RouteManager {
13512
13934
  }
13513
13935
  return matchingLayouts;
13514
13936
  }
13937
+ findMatchingRouteSlots(pathname, interceptFrom) {
13938
+ const groups = /* @__PURE__ */ new Map();
13939
+ for (const entry of this.routeSlots.values()) {
13940
+ if (!this.matchesRoutePrefix(pathname, entry.ownerPattern)) continue;
13941
+ const key = `${entry.ownerPattern}:${entry.name}`;
13942
+ const entries = groups.get(key) ?? [];
13943
+ entries.push(entry);
13944
+ groups.set(key, entries);
13945
+ }
13946
+ const normalizedFrom = interceptFrom ? this.toRoutePathname(new URL(interceptFrom, "http://farm.local").pathname) : void 0;
13947
+ const matches = [];
13948
+ for (const entries of groups.values()) {
13949
+ const candidates = entries.filter((entry) => !entry.fallback).filter(
13950
+ (entry) => !entry.interception || normalizedFrom !== void 0 && this.matchesRoutePrefix(normalizedFrom, entry.ownerPattern)
13951
+ ).map((entry) => ({
13952
+ entry,
13953
+ match: matchRoute(pathname, entry.route.segments)
13954
+ })).filter((candidate) => candidate.match.matches).sort((left, right) => {
13955
+ if (left.entry.interception !== right.entry.interception) {
13956
+ return left.entry.interception ? -1 : 1;
13957
+ }
13958
+ return routeSpecificity(right.entry) - routeSpecificity(left.entry);
13959
+ });
13960
+ const selected = candidates[0];
13961
+ if (selected) {
13962
+ matches.push({
13963
+ name: selected.entry.name,
13964
+ ownerPattern: selected.entry.ownerPattern,
13965
+ containerId: selected.entry.containerId,
13966
+ interception: selected.entry.interception,
13967
+ fallback: false,
13968
+ route: selected.entry,
13969
+ params: selected.match.params
13970
+ });
13971
+ continue;
13972
+ }
13973
+ const fallback = entries.find((entry) => entry.fallback);
13974
+ if (fallback) {
13975
+ matches.push({
13976
+ name: fallback.name,
13977
+ ownerPattern: fallback.ownerPattern,
13978
+ containerId: fallback.containerId,
13979
+ interception: false,
13980
+ fallback: true,
13981
+ route: fallback,
13982
+ params: {}
13983
+ });
13984
+ }
13985
+ }
13986
+ return matches.sort((left, right) => {
13987
+ const ownerDifference = left.route.route.segments.length - right.route.route.segments.length;
13988
+ return ownerDifference || left.name.localeCompare(right.name);
13989
+ });
13990
+ }
13991
+ matchesRoutePrefix(pathname, pattern) {
13992
+ if (pattern === "/") return true;
13993
+ const patternSegments = parseRoutePath(`${pattern}/page.tsx`).segments;
13994
+ const pathSegments = pathname.split("/").filter(Boolean);
13995
+ if (patternSegments.length > pathSegments.length) return false;
13996
+ for (let index = 0; index < patternSegments.length; index++) {
13997
+ const segment = patternSegments[index];
13998
+ if (!segment.isDynamic && segment.segment !== pathSegments[index]) return false;
13999
+ }
14000
+ return true;
14001
+ }
13515
14002
  findNearestBoundary(pathname, boundaries) {
13516
14003
  const normalizedPath = pathname === "/" ? "/" : pathname.replace(/\/$/, "");
13517
14004
  const pathSegments = normalizedPath.split("/").filter(Boolean);
@@ -14114,7 +14601,7 @@ function mergeMetadata(base, next) {
14114
14601
  __name(mergeMetadata, "mergeMetadata");
14115
14602
  function addMetadataImageReference(metadata, reference) {
14116
14603
  if (reference.kind === "opengraph") {
14117
- const openGraph = { ...metadata.openGraph || {} };
14604
+ const openGraph = { ...metadata.openGraph };
14118
14605
  if (!hasMetadataImages(openGraph.images) && !hasMetadataImages(openGraph.image)) {
14119
14606
  openGraph.images = [
14120
14607
  {
@@ -14131,7 +14618,7 @@ function addMetadataImageReference(metadata, reference) {
14131
14618
  openGraph
14132
14619
  };
14133
14620
  }
14134
- const twitter = { ...metadata.twitter || {} };
14621
+ const twitter = { ...metadata.twitter };
14135
14622
  if (!hasMetadataImages(twitter.images)) {
14136
14623
  twitter.images = [reference.href];
14137
14624
  twitter.card = twitter.card || "summary_large_image";
@@ -14737,8 +15224,8 @@ var _FarmI18nRuntime = class _FarmI18nRuntime {
14737
15224
  cookie: this.config.cookie,
14738
15225
  direction: getFarmLocaleDirection(resolution.locale, this.config.direction),
14739
15226
  messages: {
14740
- ...this.catalogs[this.config.fallbackLocale] || {},
14741
- ...this.catalogs[resolution.locale] || {}
15227
+ ...this.catalogs[this.config.fallbackLocale],
15228
+ ...this.catalogs[resolution.locale]
14742
15229
  }
14743
15230
  };
14744
15231
  }
@@ -15040,7 +15527,9 @@ var _ServerRenderer = class _ServerRenderer {
15040
15527
  async runWithRequestContext(request, fn) {
15041
15528
  return _runWithCurrentRequest(
15042
15529
  request,
15043
- () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, { redirect: false }) : fn()
15530
+ () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, {
15531
+ redirect: false
15532
+ }) : fn()
15044
15533
  );
15045
15534
  }
15046
15535
  async resolveRouteContext(input) {
@@ -15063,11 +15552,11 @@ var _ServerRenderer = class _ServerRenderer {
15063
15552
  /**
15064
15553
  * Check if a path should be served from SSG cache
15065
15554
  */
15066
- shouldServeSSG(pathname) {
15555
+ async shouldServeSSG(pathname) {
15067
15556
  const ssgPage = matchSSGPage(pathname, this.ssgManifest);
15068
15557
  if (!ssgPage) return null;
15069
- const cached = this.getCachedSSGPage(pathname);
15070
- if (cached && this.dataCache.isStale(cached)) {
15558
+ const cached = await this.getCachedSSGPage(pathname);
15559
+ if (cached && await this.dataCache.isStaleAsync(cached)) {
15071
15560
  this.regenerateSSGPage(ssgPage);
15072
15561
  }
15073
15562
  return ssgPage;
@@ -15079,12 +15568,12 @@ var _ServerRenderer = class _ServerRenderer {
15079
15568
  return createFarmCacheKey(["ppr", normalizeRevalidatePath(pathname), search]);
15080
15569
  }
15081
15570
  getCachedSSGPage(urlPath) {
15082
- return this.dataCache.getEntry(this.getSSGCacheKey(urlPath), {
15571
+ return this.dataCache.getEntryAsync(this.getSSGCacheKey(urlPath), {
15083
15572
  allowStale: true
15084
15573
  });
15085
15574
  }
15086
- cacheSSGPage(page2, html, options) {
15087
- this.dataCache.set(
15575
+ async cacheSSGPage(page2, html, options) {
15576
+ await this.dataCache.setAsync(
15088
15577
  this.getSSGCacheKey(page2.urlPath),
15089
15578
  { html, document: options.document },
15090
15579
  {
@@ -15096,11 +15585,11 @@ var _ServerRenderer = class _ServerRenderer {
15096
15585
  );
15097
15586
  }
15098
15587
  getCachedPPRShell(pathname, search) {
15099
- return this.dataCache.getEntry(this.getPPRCacheKey(pathname, search));
15588
+ return this.dataCache.getEntryAsync(this.getPPRCacheKey(pathname, search));
15100
15589
  }
15101
- cachePPRShell(options, html) {
15590
+ async cachePPRShell(options, html) {
15102
15591
  const key = this.getPPRCacheKey(options.pathname, options.search);
15103
- this.dataCache.set(
15592
+ await this.dataCache.setAsync(
15104
15593
  key,
15105
15594
  { html },
15106
15595
  {
@@ -15198,7 +15687,7 @@ var _ServerRenderer = class _ServerRenderer {
15198
15687
  }
15199
15688
  const { renderToString } = await import("react-dom/server");
15200
15689
  const html = renderToString(await this.wrapWithIntegrationProviders(pageElement));
15201
- this.cacheSSGPage(page2, html, { document: false });
15690
+ await this.cacheSSGPage(page2, html, { document: false });
15202
15691
  logger.info(`ISR: Regenerated ${page2.urlPath}`);
15203
15692
  } catch (error) {
15204
15693
  logger.error(`ISR regeneration failed for ${page2.urlPath}: ${error}`);
@@ -15212,7 +15701,7 @@ var _ServerRenderer = class _ServerRenderer {
15212
15701
  * Serve a pre-rendered SSG page
15213
15702
  */
15214
15703
  async serveSSGPage(req, res, page2) {
15215
- const cached = this.getCachedSSGPage(page2.urlPath);
15704
+ const cached = await this.getCachedSSGPage(page2.urlPath);
15216
15705
  if (cached) {
15217
15706
  res.setHeader("Content-Type", "text/html; charset=utf-8");
15218
15707
  res.setHeader("X-Farm-SSG", "cached");
@@ -15230,9 +15719,12 @@ var _ServerRenderer = class _ServerRenderer {
15230
15719
  if (fs2.existsSync(htmlPath)) {
15231
15720
  const stat = fs2.statSync(htmlPath);
15232
15721
  const html = fs2.readFileSync(htmlPath, "utf-8");
15233
- this.cacheSSGPage(page2, html, { document: true, createdAt: stat.mtimeMs });
15234
- const fileCacheEntry = this.getCachedSSGPage(page2.urlPath);
15235
- if (fileCacheEntry && this.dataCache.isStale(fileCacheEntry)) {
15722
+ await this.cacheSSGPage(page2, html, {
15723
+ document: true,
15724
+ createdAt: stat.mtimeMs
15725
+ });
15726
+ const fileCacheEntry = await this.getCachedSSGPage(page2.urlPath);
15727
+ if (fileCacheEntry && await this.dataCache.isStaleAsync(fileCacheEntry)) {
15236
15728
  this.regenerateSSGPage(page2);
15237
15729
  }
15238
15730
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -15278,6 +15770,7 @@ var _ServerRenderer = class _ServerRenderer {
15278
15770
  let pathname = "/";
15279
15771
  let params = {};
15280
15772
  let layouts = [];
15773
+ let routeSlots = [];
15281
15774
  let searchParamsObject = {};
15282
15775
  let middlewareMap = /* @__PURE__ */ new Map();
15283
15776
  let middlewareContext = /* @__PURE__ */ new Map();
@@ -15310,7 +15803,7 @@ var _ServerRenderer = class _ServerRenderer {
15310
15803
  }
15311
15804
  this.applyDeploymentHeaders(req, res);
15312
15805
  if (process.env.NODE_ENV === "production") {
15313
- const ssgPage = this.shouldServeSSG(pathname);
15806
+ const ssgPage = await this.shouldServeSSG(pathname);
15314
15807
  if (ssgPage) {
15315
15808
  const served = await this.serveSSGPage(req, res, ssgPage);
15316
15809
  if (served) {
@@ -15323,6 +15816,7 @@ var _ServerRenderer = class _ServerRenderer {
15323
15816
  const route = match.route;
15324
15817
  params = match.params;
15325
15818
  layouts = match.layouts;
15819
+ routeSlots = match.slots ?? [];
15326
15820
  if (!route) {
15327
15821
  emitFarmEvent({ type: "route.notFound", pathname });
15328
15822
  await this.render404(req, res);
@@ -15339,7 +15833,9 @@ var _ServerRenderer = class _ServerRenderer {
15339
15833
  errorBoundaryEntry = this.routeManager.getMatchingError(pathname);
15340
15834
  middlewareMap = toMiddlewareMap2(req.__FARM_MIDDLEWARE_DATA__);
15341
15835
  middlewareContext = toMiddlewareMap2(req.__FARM_MIDDLEWARE_CONTEXT__);
15342
- pluginExposedContext = getRequestContextSnapshot(req, { exposedOnly: true });
15836
+ pluginExposedContext = getRequestContextSnapshot(req, {
15837
+ exposedOnly: true
15838
+ });
15343
15839
  const currentRequest = createWebRequestFromFarmRequest(req);
15344
15840
  const routeContext = await this.resolveRouteContext({
15345
15841
  request: currentRequest,
@@ -15403,8 +15899,16 @@ var _ServerRenderer = class _ServerRenderer {
15403
15899
  revalidate: renderingConfig.revalidate
15404
15900
  } : void 0;
15405
15901
  if (renderingConfig.ppr && pprBypassReason) {
15406
- emitFarmEvent({ type: "ppr.shell.bypass", route: pathname, reason: pprBypassReason });
15407
- emitFarmEvent({ type: "cache.bypass", route: pathname, reason: pprBypassReason });
15902
+ emitFarmEvent({
15903
+ type: "ppr.shell.bypass",
15904
+ route: pathname,
15905
+ reason: pprBypassReason
15906
+ });
15907
+ emitFarmEvent({
15908
+ type: "cache.bypass",
15909
+ route: pathname,
15910
+ reason: pprBypassReason
15911
+ });
15408
15912
  if (pprBypassReason === "refresh") {
15409
15913
  pprRefreshRoute = pathname;
15410
15914
  emitFarmEvent({ type: "ppr.refresh.start", route: pathname });
@@ -15412,14 +15916,22 @@ var _ServerRenderer = class _ServerRenderer {
15412
15916
  }
15413
15917
  if (pprShellOptions) {
15414
15918
  const pprCacheKey = this.getPPRCacheKey(pathname, url.search);
15415
- const cachedPPRShell = this.getCachedPPRShell(pathname, url.search);
15919
+ const cachedPPRShell = await this.getCachedPPRShell(pathname, url.search);
15416
15920
  if (cachedPPRShell) {
15417
- emitFarmEvent({ type: "ppr.shell.hit", route: pathname, key: pprCacheKey });
15921
+ emitFarmEvent({
15922
+ type: "ppr.shell.hit",
15923
+ route: pathname,
15924
+ key: pprCacheKey
15925
+ });
15418
15926
  this.serveCachedPPRShell(res, cachedPPRShell.value, renderingConfig.revalidate);
15419
15927
  completeRender(res.statusCode || 200, pathname);
15420
15928
  return;
15421
15929
  }
15422
- emitFarmEvent({ type: "ppr.shell.miss", route: pathname, key: pprCacheKey });
15930
+ emitFarmEvent({
15931
+ type: "ppr.shell.miss",
15932
+ route: pathname,
15933
+ key: pprCacheKey
15934
+ });
15423
15935
  }
15424
15936
  let LoadingFallbackComponent = null;
15425
15937
  if (loadingBoundaryEntry) {
@@ -15443,14 +15955,77 @@ var _ServerRenderer = class _ServerRenderer {
15443
15955
  );
15444
15956
  const moduleMetadata = routeManifestEntry || getClientModuleMetadata(route.modulePath, this.config.root);
15445
15957
  const isClientComponent = moduleMetadata.isClientComponent;
15958
+ const renderedRouteSlots = await Promise.all(
15959
+ routeSlots.map(async (slot) => {
15960
+ const slotModule = await this.routeManager.loadRouteModule(slot.route.modulePath);
15961
+ if (!slotModule.default) {
15962
+ throw new Error(
15963
+ `Route slot "${slot.name}" module ${slot.route.modulePath} does not export a default component`
15964
+ );
15965
+ }
15966
+ const slotContext = await this.resolveRouteContext({
15967
+ request: currentRequest,
15968
+ rawRequest: req,
15969
+ params: slot.params,
15970
+ search: searchParamsObject,
15971
+ path: pathname
15972
+ });
15973
+ const rawSlotProps = withFarmRouteContext(
15974
+ {
15975
+ params: slot.params,
15976
+ searchParams: Promise.resolve(searchParamsObject),
15977
+ path: pathname,
15978
+ middleware: middlewareMap.size > 0 ? { data: middlewareMap } : void 0,
15979
+ context: pluginExposedContext.size > 0 ? { data: pluginExposedContext } : void 0
15980
+ },
15981
+ slotContext
15982
+ );
15983
+ const slotProps = await parseRouteModuleProps(slotModule, {
15984
+ props: rawSlotProps,
15985
+ search: searchParamsObject,
15986
+ routePath: slot.route.pattern
15987
+ });
15988
+ const metadata = getClientModuleMetadata(slot.route.modulePath, this.config.root);
15989
+ return {
15990
+ ...slot,
15991
+ module: slotModule,
15992
+ props: slotProps,
15993
+ isClientComponent: metadata.isClientComponent,
15994
+ shouldHydrate: metadata.shouldHydrate
15995
+ };
15996
+ })
15997
+ );
15446
15998
  const shouldHydrate = moduleMetadata.shouldHydrate;
15999
+ const hasHydratableRouteSlots = renderedRouteSlots.some(
16000
+ (slot) => slot.isClientComponent || slot.shouldHydrate
16001
+ );
15447
16002
  req.__FARM_PAGE_PATH__ = route.modulePath;
15448
16003
  req.__FARM_ROUTE__ = pathname;
15449
16004
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
15450
16005
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
16006
+ req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
15451
16007
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
15452
16008
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
15453
16009
  ) : null;
16010
+ req.__FARM_ROUTE_SLOTS__ = renderedRouteSlots.map((slot) => ({
16011
+ name: slot.name,
16012
+ ownerPattern: slot.ownerPattern,
16013
+ containerId: slot.containerId,
16014
+ interception: slot.interception,
16015
+ fallback: slot.fallback,
16016
+ modulePath: slot.route.modulePath,
16017
+ isClientComponent: slot.isClientComponent,
16018
+ shouldHydrate: slot.shouldHydrate,
16019
+ props: {
16020
+ params: slot.props.params,
16021
+ search: slot.props.search,
16022
+ searchParams: slot.props.search,
16023
+ ..."data" in slot.props ? { data: slot.props.data } : {},
16024
+ ...slot.props.__farmCanonicalPath ? { __farmCanonicalPath: slot.props.__farmCanonicalPath } : {},
16025
+ ...slot.props.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
16026
+ path: pathname
16027
+ }
16028
+ }));
15454
16029
  req.__FARM_PROPS__ = {
15455
16030
  params: pageProps.params,
15456
16031
  search: pageProps.search,
@@ -15510,12 +16085,32 @@ var _ServerRenderer = class _ServerRenderer {
15510
16085
  let wrappedElement = pageElement;
15511
16086
  for (let i = layoutModules.length - 1; i >= 0; i--) {
15512
16087
  const layoutModule = layoutModules[i];
16088
+ const layoutEntry = layouts[i];
15513
16089
  const LayoutComponent = layoutModule.default;
16090
+ const slotProps = {};
16091
+ for (const slot of renderedRouteSlots) {
16092
+ if (slot.ownerPattern !== layoutEntry.pattern) continue;
16093
+ let slotElement = import_react4.default.createElement(
16094
+ slot.module.default,
16095
+ slot.props
16096
+ );
16097
+ slotElement = import_react4.default.createElement(
16098
+ "div",
16099
+ {
16100
+ id: slot.containerId,
16101
+ "data-farm-route-slot": slot.name,
16102
+ "data-farm-slot-owner": slot.ownerPattern
16103
+ },
16104
+ slotElement
16105
+ );
16106
+ slotProps[slot.name] = slotElement;
16107
+ }
15514
16108
  wrappedElement = import_react4.default.createElement(
15515
16109
  LayoutComponent,
15516
16110
  {
15517
16111
  children: wrappedElement,
15518
- params
16112
+ params,
16113
+ ...slotProps
15519
16114
  }
15520
16115
  );
15521
16116
  }
@@ -15552,7 +16147,10 @@ var _ServerRenderer = class _ServerRenderer {
15552
16147
  routeManifest,
15553
16148
  captureStaticShell: Boolean(pprShellOptions),
15554
16149
  observabilityRoute: pathname,
15555
- onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({ type: "ppr.suspense.holeDetected", route: pathname }) : void 0,
16150
+ onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({
16151
+ type: "ppr.suspense.holeDetected",
16152
+ route: pathname
16153
+ }) : void 0,
15556
16154
  onComplete: pprShellOptions && req.method !== "HEAD" ? (html) => this.cachePPRShell(pprShellOptions, html) : void 0
15557
16155
  }
15558
16156
  );
@@ -15600,7 +16198,11 @@ var _ServerRenderer = class _ServerRenderer {
15600
16198
  }
15601
16199
  emitFarmEvent({ type: "render.error", route: pathname, error });
15602
16200
  if (pprRefreshRoute) {
15603
- emitFarmEvent({ type: "ppr.refresh.error", route: pprRefreshRoute, error });
16201
+ emitFarmEvent({
16202
+ type: "ppr.refresh.error",
16203
+ route: pprRefreshRoute,
16204
+ error
16205
+ });
15604
16206
  }
15605
16207
  logger.error(`Error rendering page: ${error}`);
15606
16208
  if (res.headersSent || res.writableEnded) {
@@ -15653,7 +16255,9 @@ var _ServerRenderer = class _ServerRenderer {
15653
16255
  if (typeof layoutModule.generateMetadata === "function") {
15654
16256
  metadata = mergeMetadata(
15655
16257
  metadata,
15656
- await layoutModule.generateMetadata({ params: options.pageProps.params })
16258
+ await layoutModule.generateMetadata({
16259
+ params: options.pageProps.params
16260
+ })
15657
16261
  );
15658
16262
  }
15659
16263
  }
@@ -15884,7 +16488,13 @@ var _ServerRenderer = class _ServerRenderer {
15884
16488
  clientEntry: "/@farm/client.js",
15885
16489
  routes: {},
15886
16490
  layouts: {},
15887
- sharedAssets: [{ tag: "link", attrs: { rel: "stylesheet", href: "/src/app/globals.css" } }]
16491
+ slots: [],
16492
+ sharedAssets: [
16493
+ {
16494
+ tag: "link",
16495
+ attrs: { rel: "stylesheet", href: "/src/app/globals.css" }
16496
+ }
16497
+ ]
15888
16498
  };
15889
16499
  for (const routeEntry of manifest.routes) {
15890
16500
  clientManifest.routes[routeEntry.pattern] = {
@@ -15906,9 +16516,26 @@ var _ServerRenderer = class _ServerRenderer {
15906
16516
  assets: []
15907
16517
  };
15908
16518
  }
15909
- const deferredProps = prepareDeferredData(req.__FARM_PROPS__ || {});
16519
+ for (const slotEntry of manifest.slots ?? []) {
16520
+ clientManifest.slots.push({
16521
+ ...slotEntry,
16522
+ preloads: [slotEntry.modulePath],
16523
+ assets: []
16524
+ });
16525
+ }
16526
+ const routeSlotPayload = (req.__FARM_ROUTE_SLOTS__ || []).map(
16527
+ (slot) => ({
16528
+ ...slot,
16529
+ modulePath: typeof slot.modulePath === "string" && slot.modulePath.startsWith(this.config.root) ? slot.modulePath.slice(this.config.root.length) : slot.modulePath
16530
+ })
16531
+ );
16532
+ const deferredProps = prepareDeferredData({
16533
+ page: req.__FARM_PROPS__ || {},
16534
+ slots: routeSlotPayload
16535
+ });
15910
16536
  const propsScript = `<script>
15911
- window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data)};
16537
+ window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data.page)};
16538
+ window.__FARM_ROUTE_SLOTS__ = ${serializeInlineValue(deferredProps.data.slots)};
15912
16539
  window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
15913
16540
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
15914
16541
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
@@ -15921,7 +16548,7 @@ window.__FARM_MANIFEST__ = ${JSON.stringify(clientManifest)};
15921
16548
  window.__FARM_INTEGRATION_API_MANIFEST__ = ${JSON.stringify(getRegisteredIntegrationAPIManifest())};
15922
16549
  ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(getFarmI18nClientSnapshot())};` : ""}
15923
16550
  </script>`;
15924
- const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true ? createPreHydrationClickQueueScript() : "";
16551
+ const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true || req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ === true ? createPreHydrationClickQueueScript() : "";
15925
16552
  const {
15926
16553
  title,
15927
16554
  tags: metaTags,
@@ -16032,7 +16659,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
16032
16659
  didError = true;
16033
16660
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
16034
16661
  logger.error(`SSR shell error: ${error}`);
16035
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
16662
+ emitFarmEvent({
16663
+ type: "render.error",
16664
+ route: observabilityRoute,
16665
+ error
16666
+ });
16036
16667
  }
16037
16668
  if (clearMiddlewareData) {
16038
16669
  clearMiddlewareData();
@@ -16043,7 +16674,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
16043
16674
  didError = true;
16044
16675
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
16045
16676
  logger.error(`SSR streaming error: ${error}`);
16046
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
16677
+ emitFarmEvent({
16678
+ type: "render.error",
16679
+ route: observabilityRoute,
16680
+ error
16681
+ });
16047
16682
  }
16048
16683
  }
16049
16684
  });
@@ -16212,17 +16847,14 @@ var _FarmApp = class _FarmApp {
16212
16847
  _setDefaultFarmI18nRuntime(this.i18nRuntime);
16213
16848
  this.viteServer = viteServer;
16214
16849
  this.routeManager = new RouteManager(this.config, viteServer);
16215
- this.serverRenderer = new ServerRenderer(
16216
- this.config,
16217
- this.routeManager,
16218
- this.i18nRuntime
16219
- );
16850
+ this.serverRenderer = new ServerRenderer(this.config, this.routeManager, this.i18nRuntime);
16220
16851
  }
16221
16852
  async initialize() {
16222
16853
  if (process.env.FARM_VERBOSE) {
16223
16854
  logger.info("Initializing Farm.js application...");
16224
16855
  }
16225
16856
  await initStorage(this.config.storage);
16857
+ await configureFarmCache(this.config.cache);
16226
16858
  await this.i18nRuntime.initialize();
16227
16859
  await this.verifyAppStructure();
16228
16860
  await this.routeManager.discoverRoutes();
@@ -16254,6 +16886,7 @@ var _FarmApp = class _FarmApp {
16254
16886
  preset: config.preset ?? "node-server",
16255
16887
  deploy: config.deploy || {},
16256
16888
  storage: config.storage || {},
16889
+ cache: config.cache || {},
16257
16890
  integrations: config.integrations || {},
16258
16891
  plugins: config.plugins || [],
16259
16892
  migrations: config.migrations || { commands: [] },
@@ -16661,6 +17294,29 @@ var fs3 = __toESM(require("fs"));
16661
17294
  var path10 = __toESM(require("path"));
16662
17295
  init_route_runtime();
16663
17296
 
17297
+ // src/api/runtime.ts
17298
+ init_cache_invalidation();
17299
+
17300
+ // src/api/endpoint.ts
17301
+ var import_better_call = require("better-call");
17302
+ var _EndpointFailure = class _EndpointFailure extends Error {
17303
+ constructor(code, data, options) {
17304
+ super(options.message);
17305
+ this.name = "EndpointFailure";
17306
+ this.code = code;
17307
+ this.data = data;
17308
+ this.status = options.status;
17309
+ }
17310
+ };
17311
+ __name(_EndpointFailure, "EndpointFailure");
17312
+ var EndpointFailure = _EndpointFailure;
17313
+ function isEndpointFailure(value) {
17314
+ return value instanceof EndpointFailure;
17315
+ }
17316
+ __name(isEndpointFailure, "isEndpointFailure");
17317
+ var EMPTY_ENDPOINT_MIDDLEWARE = Object.freeze([]);
17318
+ var EMPTY_ENDPOINT_CONTEXT = Object.freeze(/* @__PURE__ */ Object.create(null));
17319
+
16664
17320
  // src/api/runtime.ts
16665
17321
  function matchAPIRoute(routes, pathname) {
16666
17322
  const exactRoute = routes.get(pathname);
@@ -16693,10 +17349,10 @@ __name(invokeAPIRouteEndpoint, "invokeAPIRouteEndpoint");
16693
17349
  async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
16694
17350
  const farmHandler = endpoint.__handler || (isFarmContextHandler(endpoint) ? endpoint : null);
16695
17351
  if (!farmHandler) {
16696
- const result2 = await endpoint(request, {
17352
+ const result = await endpoint(request, {
16697
17353
  params: Promise.resolve(params)
16698
17354
  });
16699
- return normalizeRouteResponse(result2);
17355
+ return normalizeRouteResponse(result);
16700
17356
  }
16701
17357
  const url = new URL(request.url);
16702
17358
  const query = {};
@@ -16705,7 +17361,7 @@ async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
16705
17361
  });
16706
17362
  let body = void 0;
16707
17363
  if (request.method.toUpperCase() !== "GET" && request.method.toUpperCase() !== "HEAD") {
16708
- body = await readJSONBody(request);
17364
+ body = await readRequestBody(request);
16709
17365
  }
16710
17366
  const headers = Object.fromEntries(request.headers.entries());
16711
17367
  const types = endpoint.__types || {};
@@ -16721,15 +17377,30 @@ async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
16721
17377
  if (headersValidation instanceof Response) {
16722
17378
  return headersValidation;
16723
17379
  }
16724
- const result = await farmHandler({
17380
+ const handlerContext = {
16725
17381
  query: queryValidation,
16726
17382
  body: bodyValidation,
16727
17383
  headers: headersValidation,
16728
17384
  request,
16729
17385
  context: {},
16730
17386
  params
16731
- });
16732
- return normalizeRouteResponse(result);
17387
+ };
17388
+ let execution;
17389
+ try {
17390
+ execution = typeof endpoint.__farmInvoke === "function" ? await endpoint.__farmInvoke(handlerContext) : {
17391
+ result: await farmHandler(handlerContext),
17392
+ context: handlerContext.context,
17393
+ handlerExecuted: true,
17394
+ invalidations: []
17395
+ };
17396
+ } catch (error) {
17397
+ if (isEndpointFailure(error)) {
17398
+ return createEndpointFailureResponse(error);
17399
+ }
17400
+ throw error;
17401
+ }
17402
+ const response = normalizeRouteResponse(execution.result);
17403
+ return attachEndpointInvalidations(response, execution.invalidations);
16733
17404
  }
16734
17405
  __name(invokeAPIRouteEndpointInContext, "invokeAPIRouteEndpointInContext");
16735
17406
  function normalizeRouteResponse(result) {
@@ -16749,6 +17420,40 @@ function isWebResponse2(value) {
16749
17420
  return value instanceof Response || typeof value === "object" && value !== null && "headers" in value && "status" in value && typeof value.arrayBuffer === "function";
16750
17421
  }
16751
17422
  __name(isWebResponse2, "isWebResponse");
17423
+ function attachEndpointInvalidations(response, keys) {
17424
+ const existing = decodeFarmCacheInvalidations(
17425
+ response.headers.get(FARM_CACHE_INVALIDATION_HEADER)
17426
+ );
17427
+ const encoded = encodeFarmCacheInvalidations([...existing, ...keys]);
17428
+ if (!encoded) return response;
17429
+ const headers = new Headers(response.headers);
17430
+ headers.set(FARM_CACHE_INVALIDATION_HEADER, encoded);
17431
+ return new Response(response.body, {
17432
+ status: response.status,
17433
+ statusText: response.statusText,
17434
+ headers
17435
+ });
17436
+ }
17437
+ __name(attachEndpointInvalidations, "attachEndpointInvalidations");
17438
+ function createEndpointFailureResponse(failure) {
17439
+ return new Response(
17440
+ JSON.stringify({
17441
+ error: {
17442
+ code: failure.code,
17443
+ message: failure.message,
17444
+ data: failure.data
17445
+ }
17446
+ }),
17447
+ {
17448
+ status: failure.status,
17449
+ headers: {
17450
+ "cache-control": "no-store",
17451
+ "content-type": "application/json"
17452
+ }
17453
+ }
17454
+ );
17455
+ }
17456
+ __name(createEndpointFailureResponse, "createEndpointFailureResponse");
16752
17457
  function isFarmContextHandler(endpoint) {
16753
17458
  if (typeof endpoint !== "function") {
16754
17459
  return false;
@@ -16760,17 +17465,50 @@ function isFarmContextHandler(endpoint) {
16760
17465
  return firstParameter === "ctx" || firstParameter === "context" || firstParameter.startsWith("{");
16761
17466
  }
16762
17467
  __name(isFarmContextHandler, "isFarmContextHandler");
16763
- async function readJSONBody(request) {
17468
+ async function readRequestBody(request) {
17469
+ const contentType = request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
16764
17470
  try {
17471
+ if (contentType === "multipart/form-data") {
17472
+ return formDataToObject(await request.clone().formData());
17473
+ }
16765
17474
  const text2 = await request.clone().text();
16766
- if (text2) {
17475
+ if (!text2) return void 0;
17476
+ if (contentType === "application/x-www-form-urlencoded") {
17477
+ return searchParamsToObject2(new URLSearchParams(text2));
17478
+ }
17479
+ if (contentType === "application/json" || contentType?.endsWith("+json")) {
16767
17480
  return JSON.parse(text2);
16768
17481
  }
17482
+ return JSON.parse(text2);
16769
17483
  } catch {
16770
17484
  }
16771
17485
  return void 0;
16772
17486
  }
16773
- __name(readJSONBody, "readJSONBody");
17487
+ __name(readRequestBody, "readRequestBody");
17488
+ function formDataToObject(formData) {
17489
+ return entriesToObject(formData.entries());
17490
+ }
17491
+ __name(formDataToObject, "formDataToObject");
17492
+ function searchParamsToObject2(searchParams) {
17493
+ return entriesToObject(searchParams.entries());
17494
+ }
17495
+ __name(searchParamsToObject2, "searchParamsToObject");
17496
+ function entriesToObject(entries) {
17497
+ const output = /* @__PURE__ */ Object.create(null);
17498
+ for (const [key, value] of entries) {
17499
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
17500
+ const current = output[key];
17501
+ if (current === void 0) {
17502
+ output[key] = value;
17503
+ } else if (Array.isArray(current)) {
17504
+ current.push(value);
17505
+ } else {
17506
+ output[key] = [current, value];
17507
+ }
17508
+ }
17509
+ return output;
17510
+ }
17511
+ __name(entriesToObject, "entriesToObject");
16774
17512
  function validateInput(schema, value, error) {
16775
17513
  if (!schema || typeof schema.parse !== "function") {
16776
17514
  return value;
@@ -17777,7 +18515,7 @@ var _MiddlewareManager = class _MiddlewareManager {
17777
18515
  if (!nestedMatch.matched) {
17778
18516
  return { matched: false };
17779
18517
  }
17780
- const params = { ...nestedMatch.params || {} };
18518
+ const params = { ...nestedMatch.params };
17781
18519
  delete params.__farmRest;
17782
18520
  return {
17783
18521
  matched: true,
@@ -17925,6 +18663,7 @@ async function generateRouteTypes(options) {
17925
18663
  absolute: false
17926
18664
  });
17927
18665
  for (const file of pageFiles) {
18666
+ if (parseRouteSlotFile(file)) continue;
17928
18667
  const route = parseRoutePath(file);
17929
18668
  if (route.type === "page") {
17930
18669
  const pattern = createRoutePattern(route);
@@ -18678,7 +19417,7 @@ function isResolvedEnvScope(value) {
18678
19417
  }
18679
19418
  __name(isResolvedEnvScope, "isResolvedEnvScope");
18680
19419
  function isPotentialProgrammaticRouteSourceFile(normalizedFile, srcDirSlug) {
18681
- return normalizedFile.startsWith(`${srcDirSlug}/`) && /\.(ts|tsx|js|jsx)$/.test(normalizedFile) && !/\.d\.ts$/.test(normalizedFile) && !normalizedFile.endsWith("/farm-routes.d.ts") && !normalizedFile.endsWith("/farm-env.d.ts") && !normalizedFile.endsWith("/lib/api.generated.ts");
19420
+ return normalizedFile.startsWith(`${srcDirSlug}/`) && /\.(ts|tsx|js|jsx)$/.test(normalizedFile) && !normalizedFile.endsWith(".d.ts") && !normalizedFile.endsWith("/farm-routes.d.ts") && !normalizedFile.endsWith("/farm-env.d.ts") && !normalizedFile.endsWith("/lib/api.generated.ts");
18682
19421
  }
18683
19422
  __name(isPotentialProgrammaticRouteSourceFile, "isPotentialProgrammaticRouteSourceFile");
18684
19423
  function fileContainsProgrammaticPageRoute(file) {
@@ -18921,7 +19660,7 @@ function farmPlugin(options = {}, initialPluginManager) {
18921
19660
  }, "isProgrammaticRouteSourceFile");
18922
19661
  const isAppRuntimeFile = /* @__PURE__ */ __name((file) => {
18923
19662
  const normalized = file.replace(/\\/g, "/");
18924
- 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(
19663
+ 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(
18925
19664
  normalized
18926
19665
  );
18927
19666
  }, "isAppRuntimeFile");
@@ -19612,7 +20351,10 @@ function farmPlugin(options = {}, initialPluginManager) {
19612
20351
  method: req.method || "GET"
19613
20352
  });
19614
20353
  }
19615
- const match = routeManager3.matchRoute(targetPath);
20354
+ const interceptFromHeader = request.headers.get("x-farm-intercept-from") || void 0;
20355
+ const match = routeManager3.matchRoute(targetRequestUrl.pathname, {
20356
+ interceptFrom: interceptFromHeader
20357
+ });
19616
20358
  if (pm) {
19617
20359
  await pm.runHookParallel("afterRouteMatch", {
19618
20360
  pathname: targetPath,
@@ -19628,7 +20370,7 @@ function farmPlugin(options = {}, initialPluginManager) {
19628
20370
  res.end(JSON.stringify({ error: "Route not found" }));
19629
20371
  return;
19630
20372
  }
19631
- const { route, params, layouts } = match;
20373
+ const { route, params, layouts, slots } = match;
19632
20374
  if (!route) {
19633
20375
  res.statusCode = 404;
19634
20376
  res.setHeader("Content-Type", "application/json");
@@ -19683,6 +20425,54 @@ function farmPlugin(options = {}, initialPluginManager) {
19683
20425
  search: searchParams,
19684
20426
  routePath: route.pattern
19685
20427
  });
20428
+ const routeSlots = await Promise.all(
20429
+ slots.map(async (slot) => {
20430
+ const slotModule = await routeManager3.loadRouteModule(slot.route.modulePath);
20431
+ const slotMetadata = getClientModuleMetadata(
20432
+ slot.route.modulePath,
20433
+ server.config.root
20434
+ );
20435
+ const slotContext = await resolveFarmRouteContext(farmApp.getConfig(), {
20436
+ request,
20437
+ params: slot.params,
20438
+ search: searchParams,
20439
+ path: targetUrl.pathname
20440
+ });
20441
+ const slotProps = await parseRouteModuleProps2(slotModule, {
20442
+ props: withFarmRouteContext(
20443
+ {
20444
+ params: slot.params,
20445
+ searchParams: Promise.resolve(searchParams),
20446
+ path: targetUrl.pathname
20447
+ },
20448
+ slotContext
20449
+ ),
20450
+ search: searchParams,
20451
+ routePath: slot.route.pattern
20452
+ });
20453
+ return {
20454
+ name: slot.name,
20455
+ ownerPattern: slot.ownerPattern,
20456
+ containerId: slot.containerId,
20457
+ interception: slot.interception,
20458
+ fallback: slot.fallback,
20459
+ modulePath: slot.route.modulePath,
20460
+ isClientComponent: slotMetadata.isClientComponent,
20461
+ shouldHydrate: slotMetadata.shouldHydrate,
20462
+ props: {
20463
+ params: slotProps.params,
20464
+ search: slotProps.search,
20465
+ searchParams: slotProps.search,
20466
+ ..."data" in slotProps ? { data: slotProps.data } : {},
20467
+ ...slotProps.__farmCanonicalPath ? {
20468
+ __farmCanonicalPath: slotProps.__farmCanonicalPath
20469
+ } : {},
20470
+ ...slotProps.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
20471
+ path: targetUrl.pathname
20472
+ }
20473
+ };
20474
+ })
20475
+ );
19686
20476
  const projectRoot = server.config.root;
19687
20477
  const toUrlPath = /* @__PURE__ */ __name((absolutePath) => {
19688
20478
  if (absolutePath.startsWith(projectRoot)) {
@@ -19703,13 +20493,21 @@ function farmPlugin(options = {}, initialPluginManager) {
19703
20493
  },
19704
20494
  canonicalPath: routeProps.__farmCanonicalPath,
19705
20495
  modulePath: toUrlPath(route.modulePath),
19706
- isClientComponent,
19707
- shouldHydrate,
20496
+ isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
20497
+ shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
19708
20498
  metadata: {
19709
20499
  title: mergedMetadata.title,
19710
20500
  description: mergedMetadata.description
19711
20501
  },
19712
20502
  layoutModules: layouts.map((l) => toUrlPath(l.modulePath)),
20503
+ routeSlots: routeSlots.map((slot) => ({
20504
+ ...slot,
20505
+ modulePath: toUrlPath(slot.modulePath)
20506
+ })),
20507
+ interception: routeSlots.some((slot) => slot.interception) ? {
20508
+ from: interceptFromHeader,
20509
+ slots: routeSlots.filter((slot) => slot.interception).map((slot) => slot.name)
20510
+ } : void 0,
19713
20511
  i18n: getFarmI18nClientSnapshot()
19714
20512
  };
19715
20513
  await sendWebResponse(
@@ -20045,6 +20843,7 @@ function farmPlugin(options = {}, initialPluginManager) {
20045
20843
  export const getManifest = () => ({
20046
20844
  routes: {},
20047
20845
  layouts: {},
20846
+ slots: [],
20048
20847
  clientEntry: "/@farm/client.js",
20049
20848
  sharedAssets: []
20050
20849
  });
@@ -20055,6 +20854,7 @@ export const getManifest = () => ({
20055
20854
  clientEntry: "/@farm/client.js",
20056
20855
  routes: {},
20057
20856
  layouts: {},
20857
+ slots: [],
20058
20858
  sharedAssets: [
20059
20859
  {
20060
20860
  tag: "link",
@@ -20084,6 +20884,13 @@ export const getManifest = () => ({
20084
20884
  assets: []
20085
20885
  };
20086
20886
  }
20887
+ for (const slot of manifest.slots) {
20888
+ fullManifest.slots.push({
20889
+ ...slot,
20890
+ preloads: [slot.modulePath],
20891
+ assets: []
20892
+ });
20893
+ }
20087
20894
  return `
20088
20895
  // Auto-generated manifest for SPA navigation (TanStack Start pattern)
20089
20896
  // This manifest is inlined in the server bundle - no file on disk
@@ -20726,7 +21533,7 @@ window.__FARM_WRAP_PROVIDERS__ = wrapWithIntegrationProviders;
20726
21533
 
20727
21534
  // Get manifest from window (inlined by server in HTML)
20728
21535
  // Fallback to empty manifest if not available yet
20729
- const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, clientEntry: '', sharedAssets: [] };
21536
+ const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, slots: [], clientEntry: '', sharedAssets: [] };
20730
21537
 
20731
21538
  // ====== CLIENT-SIDE ROUTE MATCHING ======
20732
21539
  // Matches URL to route using manifest (no server request!)
@@ -21065,6 +21872,9 @@ let LayoutComponent = null;
21065
21872
 
21066
21873
  // Track if we've taken over rendering from SSR
21067
21874
  let hasClientTakenOver = false;
21875
+ const routeSlotRoots = new Map();
21876
+ const routeSlotDefinitions = new Map();
21877
+ let activeRouteInterception = null;
21068
21878
 
21069
21879
  function normalizeServerProps(rawProps) {
21070
21880
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -21077,6 +21887,123 @@ function normalizeServerProps(rawProps) {
21077
21887
  return props;
21078
21888
  }
21079
21889
 
21890
+ function getRouteSlotKey(slot) {
21891
+ return slot.ownerPattern + ':' + slot.name;
21892
+ }
21893
+
21894
+ async function renderRouteSlot(slot, mode = 'render') {
21895
+ const container = document.getElementById(slot.containerId);
21896
+ if (!container || !slot.modulePath) return false;
21897
+ if (mode === 'intercept' && !slot.isClientComponent && !slot.shouldHydrate) {
21898
+ return false;
21899
+ }
21900
+
21901
+ let slotModule = pageModuleCache.get(slot.modulePath);
21902
+ if (!slotModule) {
21903
+ slotModule = await import(/* @vite-ignore */ slot.modulePath);
21904
+ pageModuleCache.set(slot.modulePath, slotModule);
21905
+ }
21906
+ const SlotComponent = slotModule?.default;
21907
+ if (!SlotComponent) return false;
21908
+
21909
+ const props = reviveDeferredData(
21910
+ normalizeServerProps(slot.props || {}),
21911
+ window.__FARM_DEFERRED_DATA__ || {},
21912
+ );
21913
+ const element = wrapWithIntegrationProviders(
21914
+ React.createElement(SlotComponent, props),
21915
+ );
21916
+ const key = getRouteSlotKey(slot);
21917
+ const existingRoot = routeSlotRoots.get(key);
21918
+
21919
+ if (mode === 'hydrate') {
21920
+ if (existingRoot) return true;
21921
+ const root = hydrateRoot(container, element);
21922
+ routeSlotRoots.set(key, root);
21923
+ } else {
21924
+ if (existingRoot) {
21925
+ try { existingRoot.unmount(); } catch (error) {}
21926
+ }
21927
+ const root = createRoot(container);
21928
+ root.render(element);
21929
+ routeSlotRoots.set(key, root);
21930
+ }
21931
+
21932
+ routeSlotDefinitions.set(key, slot);
21933
+ return true;
21934
+ }
21935
+
21936
+ async function hydrateInitialRouteSlots() {
21937
+ const slots = Array.isArray(window.__FARM_ROUTE_SLOTS__)
21938
+ ? window.__FARM_ROUTE_SLOTS__
21939
+ : [];
21940
+ let hydrated = false;
21941
+
21942
+ for (const slot of slots) {
21943
+ routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
21944
+ if (!slot.isClientComponent && !slot.shouldHydrate) continue;
21945
+ try {
21946
+ hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
21947
+ } catch (error) {
21948
+ console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
21949
+ }
21950
+ }
21951
+
21952
+ return hydrated;
21953
+ }
21954
+
21955
+ async function renderRouteInterception(pageData) {
21956
+ const slots = Array.isArray(pageData.routeSlots)
21957
+ ? pageData.routeSlots.filter((slot) => slot.interception)
21958
+ : [];
21959
+ if (!pageData.interception || slots.length === 0) return false;
21960
+
21961
+ const previous = new Map();
21962
+ for (const slot of slots) {
21963
+ const key = getRouteSlotKey(slot);
21964
+ previous.set(key, routeSlotDefinitions.get(key) || null);
21965
+ if (!(await renderRouteSlot(slot, 'intercept'))) {
21966
+ return false;
21967
+ }
21968
+ }
21969
+
21970
+ activeRouteInterception = {
21971
+ from: pageData.interception.from,
21972
+ slots,
21973
+ previous,
21974
+ };
21975
+ return true;
21976
+ }
21977
+
21978
+ async function clearRouteInterception(destination) {
21979
+ if (!activeRouteInterception) return false;
21980
+ const active = activeRouteInterception;
21981
+ activeRouteInterception = null;
21982
+
21983
+ for (const slot of active.slots) {
21984
+ const key = getRouteSlotKey(slot);
21985
+ const root = routeSlotRoots.get(key);
21986
+ if (root) {
21987
+ try { root.unmount(); } catch (error) {}
21988
+ routeSlotRoots.delete(key);
21989
+ }
21990
+
21991
+ const previous = active.previous.get(key);
21992
+ if (previous && (previous.isClientComponent || previous.shouldHydrate)) {
21993
+ await renderRouteSlot(previous, 'render');
21994
+ } else {
21995
+ const container = document.getElementById(slot.containerId);
21996
+ if (container) container.replaceChildren();
21997
+ if (previous) routeSlotDefinitions.set(key, previous);
21998
+ else routeSlotDefinitions.delete(key);
21999
+ }
22000
+ }
22001
+
22002
+ if (!active.from) return false;
22003
+ const background = new URL(active.from, window.location.origin);
22004
+ return destination === background.pathname + background.search;
22005
+ }
22006
+
21080
22007
  function applyCanonicalPathFromProps(props) {
21081
22008
  const canonicalPath = props && typeof props.__farmCanonicalPath === 'string'
21082
22009
  ? props.__farmCanonicalPath
@@ -21311,6 +22238,17 @@ async function tryHydrateImportedPage(
21311
22238
  async function renderPage(pageData) {
21312
22239
  const container = document.getElementById('root');
21313
22240
  if (!container) return;
22241
+ const destination = window.location.pathname + window.location.search;
22242
+
22243
+ if (pageData.interception) {
22244
+ if (await renderRouteInterception(pageData)) return;
22245
+ window.location.assign(destination);
22246
+ return;
22247
+ }
22248
+
22249
+ if (await clearRouteInterception(destination)) {
22250
+ return;
22251
+ }
21314
22252
 
21315
22253
  const route = {
21316
22254
  modulePath: pageData.modulePath,
@@ -21539,7 +22477,9 @@ async function hydrate() {
21539
22477
  window.__FARM_SHOULD_HYDRATE__ === true ||
21540
22478
  isClientComponent ||
21541
22479
  await moduleLooksClient(modulePath);
22480
+ const hydratedSlots = await hydrateInitialRouteSlots();
21542
22481
  if (!shouldHydrate) {
22482
+ if (hydratedSlots) replayPreHydrationClicks();
21543
22483
  console.log('[Farm.js] Server component - SPA router ready')
21544
22484
  return
21545
22485
  }