@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/vite.cjs CHANGED
@@ -118,7 +118,7 @@ function createFarmMarkdownRouteModule(input) {
118
118
  ...props,
119
119
  components: {
120
120
  ...components,
121
- ...props?.components || {}
121
+ ...props?.components
122
122
  }
123
123
  })
124
124
  );
@@ -635,7 +635,11 @@ async function prepareFarmWorkflowsForNitro(config) {
635
635
  };
636
636
  }
637
637
  const handlerPath = path22.join(generatedDir, "http-handler.mjs");
638
- await fs8.writeFile(handlerPath, createNitroWorkflowHTTPHandler(workflowConfig, workflows), "utf8");
638
+ await fs8.writeFile(
639
+ handlerPath,
640
+ createNitroWorkflowHTTPHandler(workflowConfig, workflows),
641
+ "utf8"
642
+ );
639
643
  const manifestPath = path22.join(generatedDir, "manifest.json");
640
644
  await fs8.writeFile(
641
645
  manifestPath,
@@ -709,13 +713,7 @@ function normalizeSchedule(schedule) {
709
713
  return (Array.isArray(schedule) ? schedule : [schedule]).map((value) => value.trim()).filter(Boolean);
710
714
  }
711
715
  function resolveWorkflowDefinition(module2) {
712
- const candidates = [
713
- module2.default,
714
- module2.workflow,
715
- module2.cron,
716
- module2.task,
717
- module2.job
718
- ];
716
+ const candidates = [module2.default, module2.workflow, module2.cron, module2.task, module2.job];
719
717
  for (const candidate of candidates) {
720
718
  if (candidate && typeof candidate === "object" && typeof candidate.run === "function") {
721
719
  return candidate;
@@ -1708,8 +1706,8 @@ function compareFarmRouteRuleSpecificity(left, right) {
1708
1706
  }
1709
1707
  function getFarmRouteRuleSpecificity(pattern) {
1710
1708
  return normalizeRoutePattern(pattern).split("/").filter(Boolean).reduce((score, segment) => {
1711
- if (segment === "**" || /^\[\[\.\.\./.test(segment)) return score + 1;
1712
- if (segment === "*" || /^\[\.\.\./.test(segment)) return score + 10;
1709
+ if (segment === "**" || segment.startsWith("[[...")) return score + 1;
1710
+ if (segment === "*" || segment.startsWith("[...")) return score + 10;
1713
1711
  if (/^\[.+\]$/.test(segment) || /^:.+$/.test(segment)) return score + 50;
1714
1712
  return score + 100;
1715
1713
  }, 0);
@@ -1769,22 +1767,50 @@ var init_route_rules = __esm({
1769
1767
  // src/cache-invalidation.ts
1770
1768
  function getFarmCacheInvalidationState() {
1771
1769
  return globalState[FARM_CACHE_INVALIDATION_STATE] ?? (globalState[FARM_CACHE_INVALIDATION_STATE] = {
1772
- listeners: /* @__PURE__ */ new Set()
1770
+ listeners: /* @__PURE__ */ new Set(),
1771
+ taskListeners: /* @__PURE__ */ new Set()
1773
1772
  });
1774
1773
  }
1774
+ function encodeFarmCacheInvalidations(keys) {
1775
+ const normalized = Array.from(
1776
+ new Set(keys.filter((key) => typeof key === "string" && key.length > 0))
1777
+ );
1778
+ if (normalized.length === 0) return null;
1779
+ return encodeURIComponent(JSON.stringify(normalized));
1780
+ }
1781
+ function decodeFarmCacheInvalidations(value) {
1782
+ if (!value) return [];
1783
+ try {
1784
+ const parsed = JSON.parse(decodeURIComponent(value));
1785
+ return Array.isArray(parsed) ? Array.from(
1786
+ new Set(parsed.filter((key) => typeof key === "string" && key.length > 0))
1787
+ ) : [];
1788
+ } catch {
1789
+ return [];
1790
+ }
1791
+ }
1775
1792
  function subscribeFarmCacheInvalidation(listener) {
1776
1793
  const state = getFarmCacheInvalidationState();
1777
1794
  state.listeners.add(listener);
1778
1795
  return () => state.listeners.delete(listener);
1779
1796
  }
1780
- var FARM_CACHE_INVALIDATION_STATE, globalState;
1797
+ function subscribeFarmCacheTask(listener) {
1798
+ const state = getFarmCacheInvalidationState();
1799
+ state.taskListeners.add(listener);
1800
+ return () => state.taskListeners.delete(listener);
1801
+ }
1802
+ var FARM_CACHE_INVALIDATION_HEADER, FARM_CACHE_INVALIDATION_STATE, globalState;
1781
1803
  var init_cache_invalidation = __esm({
1782
1804
  "src/cache-invalidation.ts"() {
1783
1805
  "use strict";
1806
+ FARM_CACHE_INVALIDATION_HEADER = "x-farm-cache-invalidations";
1784
1807
  FARM_CACHE_INVALIDATION_STATE = /* @__PURE__ */ Symbol.for("farm.cacheInvalidationState");
1785
1808
  globalState = globalThis;
1786
1809
  __name(getFarmCacheInvalidationState, "getFarmCacheInvalidationState");
1810
+ __name(encodeFarmCacheInvalidations, "encodeFarmCacheInvalidations");
1811
+ __name(decodeFarmCacheInvalidations, "decodeFarmCacheInvalidations");
1787
1812
  __name(subscribeFarmCacheInvalidation, "subscribeFarmCacheInvalidation");
1813
+ __name(subscribeFarmCacheTask, "subscribeFarmCacheTask");
1788
1814
  }
1789
1815
  });
1790
1816
 
@@ -1888,6 +1914,9 @@ var init_server_action_security = __esm({
1888
1914
  subscribeFarmCacheInvalidation((key) => {
1889
1915
  getServerActionExecutionContext()?.invalidations.add(key);
1890
1916
  });
1917
+ subscribeFarmCacheTask((task) => {
1918
+ getServerActionExecutionContext()?.cacheTasks.add(task);
1919
+ });
1891
1920
  __name(getServerActionStorage, "getServerActionStorage");
1892
1921
  __name(parseBodySizeLimit, "parseBodySizeLimit");
1893
1922
  __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
@@ -4833,7 +4862,9 @@ function readManifest(contentDir) {
4833
4862
  const manifestPath = import_node_path7.default.join(contentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
4834
4863
  if (!(0, import_node_fs5.existsSync)(manifestPath)) return null;
4835
4864
  try {
4836
- const parsed = JSON.parse((0, import_node_fs5.readFileSync)(manifestPath, "utf8"));
4865
+ const parsed = JSON.parse(
4866
+ (0, import_node_fs5.readFileSync)(manifestPath, "utf8")
4867
+ );
4837
4868
  if (parsed.version !== 1 || !parsed.pages || typeof parsed.pages !== "object") return null;
4838
4869
  const pages = Object.fromEntries(
4839
4870
  Object.entries(parsed.pages).filter(
@@ -11115,16 +11146,7 @@ __export(devtools_client_exports, {
11115
11146
  function parseShortcut(shortcut) {
11116
11147
  const tokens = shortcut.toLowerCase().split("+").map((token) => token.trim()).filter(Boolean);
11117
11148
  const key = tokens.find(
11118
- (token) => ![
11119
- "alt",
11120
- "ctrl",
11121
- "control",
11122
- "meta",
11123
- "cmd",
11124
- "command",
11125
- "mod",
11126
- "shift"
11127
- ].includes(token)
11149
+ (token) => !["alt", "ctrl", "control", "meta", "cmd", "command", "mod", "shift"].includes(token)
11128
11150
  );
11129
11151
  if (!key) {
11130
11152
  return null;
@@ -12063,11 +12085,51 @@ __name(getActiveFarmI18nSnapshot, "getActiveFarmI18nSnapshot");
12063
12085
  // src/cache.ts
12064
12086
  init_cache_invalidation();
12065
12087
  var _FarmDataCache = class _FarmDataCache {
12066
- constructor() {
12088
+ constructor(config = {}) {
12067
12089
  this.entries = /* @__PURE__ */ new Map();
12068
12090
  this.inflight = /* @__PURE__ */ new Map();
12069
12091
  this.invalidatedTagVersions = /* @__PURE__ */ new Map();
12070
12092
  this.version = 0;
12093
+ this.namespace = "farm";
12094
+ this.local = true;
12095
+ this.lease = {
12096
+ enabled: true,
12097
+ ttlMs: 1e4,
12098
+ waitTimeoutMs: 1e4,
12099
+ pollIntervalMs: 25
12100
+ };
12101
+ this.configure(config);
12102
+ }
12103
+ configure(config = {}) {
12104
+ this.adapter = config.adapter;
12105
+ this.namespace = normalizeCacheNamespace(config.namespace || "farm");
12106
+ this.local = !config.adapter;
12107
+ this.lease = config.lease === false ? { ...this.lease, enabled: false } : {
12108
+ enabled: true,
12109
+ ttlMs: normalizePositiveDuration(config.lease?.ttlMs, 1e4, "cache.lease.ttlMs"),
12110
+ waitTimeoutMs: normalizePositiveDuration(
12111
+ config.lease?.waitTimeoutMs,
12112
+ 1e4,
12113
+ "cache.lease.waitTimeoutMs"
12114
+ ),
12115
+ pollIntervalMs: normalizePositiveDuration(
12116
+ config.lease?.pollIntervalMs,
12117
+ 25,
12118
+ "cache.lease.pollIntervalMs"
12119
+ )
12120
+ };
12121
+ if (!this.local) {
12122
+ this.entries.clear();
12123
+ this.inflight.clear();
12124
+ this.invalidatedTagVersions.clear();
12125
+ this.version = 0;
12126
+ }
12127
+ }
12128
+ get adapterName() {
12129
+ return this.adapter?.name || (this.adapter ? "custom" : "memory");
12130
+ }
12131
+ get hasAdapter() {
12132
+ return this.adapter !== void 0;
12071
12133
  }
12072
12134
  get size() {
12073
12135
  return this.entries.size;
@@ -12103,6 +12165,45 @@ var _FarmDataCache = class _FarmDataCache {
12103
12165
  });
12104
12166
  return this.toPublicEntry(entry);
12105
12167
  }
12168
+ async getEntryAsync(key, options = {}) {
12169
+ if (this.local) {
12170
+ const localEntry = this.getEntry(key, options);
12171
+ if (localEntry) return localEntry;
12172
+ }
12173
+ if (!this.adapter) {
12174
+ return this.local ? void 0 : this.getEntry(key, options);
12175
+ }
12176
+ const entry = await this.adapter.get(this.createAdapterKey(key));
12177
+ if (!entry) {
12178
+ emitFarmEvent({ type: "cache.miss", key });
12179
+ return void 0;
12180
+ }
12181
+ assertFarmCacheEntry(entry, key);
12182
+ const stale = await this.isAdapterEntryStale(entry, options.now);
12183
+ if (stale) {
12184
+ emitFarmEvent({
12185
+ type: "cache.stale",
12186
+ key,
12187
+ tags: [...entry.tags],
12188
+ revalidate: entry.revalidate
12189
+ });
12190
+ if (!options.allowStale) {
12191
+ emitFarmEvent({ type: "cache.miss", key, reason: "stale" });
12192
+ return void 0;
12193
+ }
12194
+ }
12195
+ emitFarmEvent({
12196
+ type: "cache.hit",
12197
+ key,
12198
+ tags: [...entry.tags],
12199
+ revalidate: entry.revalidate,
12200
+ stale
12201
+ });
12202
+ if (this.local && !stale) {
12203
+ this.hydrateLocalEntry(entry);
12204
+ }
12205
+ return { ...entry, key };
12206
+ }
12106
12207
  set(key, value, options = {}) {
12107
12208
  const tags = /* @__PURE__ */ new Set();
12108
12209
  for (const tag of options.tags ?? []) {
@@ -12115,6 +12216,7 @@ var _FarmDataCache = class _FarmDataCache {
12115
12216
  key,
12116
12217
  value,
12117
12218
  tags,
12219
+ tagVersions: void 0,
12118
12220
  createdAt: options.createdAt ?? Date.now(),
12119
12221
  createdVersion: ++this.version,
12120
12222
  revalidate: normalizeRevalidate(options.revalidate)
@@ -12128,11 +12230,45 @@ var _FarmDataCache = class _FarmDataCache {
12128
12230
  });
12129
12231
  return this.toPublicEntry(entry);
12130
12232
  }
12233
+ async setAsync(key, value, options = {}, tagVersions) {
12234
+ const tags = normalizeCacheOptionsTags(options);
12235
+ const capturedVersions = tagVersions ?? await this.getAdapterTagVersions(Array.from(tags.values()));
12236
+ const entry = {
12237
+ key,
12238
+ value,
12239
+ tags: Array.from(tags),
12240
+ tagVersions: capturedVersions,
12241
+ createdAt: options.createdAt ?? Date.now(),
12242
+ createdVersion: ++this.version,
12243
+ revalidate: normalizeRevalidate(options.revalidate)
12244
+ };
12245
+ if (this.local) {
12246
+ this.hydrateLocalEntry(entry);
12247
+ }
12248
+ if (this.adapter) {
12249
+ await this.adapter.set(this.createAdapterKey(key), entry);
12250
+ }
12251
+ emitFarmEvent({
12252
+ type: "cache.set",
12253
+ key,
12254
+ tags: [...entry.tags],
12255
+ revalidate: entry.revalidate
12256
+ });
12257
+ return entry;
12258
+ }
12131
12259
  delete(key) {
12132
12260
  const deleted = this.entries.delete(key);
12133
12261
  emitFarmEvent({ type: "cache.delete", key, deleted });
12134
12262
  return deleted;
12135
12263
  }
12264
+ async deleteAsync(key) {
12265
+ const deleted = this.entries.delete(key);
12266
+ if (this.adapter) {
12267
+ await this.adapter.delete(this.createAdapterKey(key));
12268
+ }
12269
+ emitFarmEvent({ type: "cache.delete", key, deleted: this.adapter ? true : deleted });
12270
+ return this.adapter ? true : deleted;
12271
+ }
12136
12272
  clear() {
12137
12273
  const count = this.entries.size;
12138
12274
  this.entries.clear();
@@ -12141,6 +12277,10 @@ var _FarmDataCache = class _FarmDataCache {
12141
12277
  this.version = 0;
12142
12278
  emitFarmEvent({ type: "cache.clear", count });
12143
12279
  }
12280
+ async clearAsync() {
12281
+ this.clear();
12282
+ await this.adapter?.clear?.();
12283
+ }
12144
12284
  isStale(entry, now = Date.now()) {
12145
12285
  if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
12146
12286
  return true;
@@ -12153,6 +12293,12 @@ var _FarmDataCache = class _FarmDataCache {
12153
12293
  }
12154
12294
  return false;
12155
12295
  }
12296
+ async isStaleAsync(entry, now = Date.now()) {
12297
+ if (this.adapter) {
12298
+ return this.isAdapterEntryStale(entry, now);
12299
+ }
12300
+ return this.isStale(entry, now);
12301
+ }
12156
12302
  revalidateTag(tag, options = {}) {
12157
12303
  const normalized = normalizeCacheTag(tag);
12158
12304
  const count = this.invalidateTag(normalized);
@@ -12161,6 +12307,15 @@ var _FarmDataCache = class _FarmDataCache {
12161
12307
  );
12162
12308
  return count;
12163
12309
  }
12310
+ async revalidateTagAsync(tag, options = {}) {
12311
+ const normalized = normalizeCacheTag(tag);
12312
+ const count = this.invalidateTag(normalized);
12313
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(normalized)]);
12314
+ emitFarmEvent(
12315
+ options.source === "updateTag" ? { type: "cache.updateTag", tag: normalized, count } : { type: "cache.revalidateTag", tag: normalized, profile: options.profile, count }
12316
+ );
12317
+ return count;
12318
+ }
12164
12319
  revalidatePath(routePath) {
12165
12320
  const normalizedPath = normalizeRevalidatePath(routePath);
12166
12321
  const pathTag = createPathCacheTag(normalizedPath);
@@ -12177,8 +12332,25 @@ var _FarmDataCache = class _FarmDataCache {
12177
12332
  }
12178
12333
  return count;
12179
12334
  }
12335
+ async revalidatePathAsync(routePath) {
12336
+ const normalizedPath = normalizeRevalidatePath(routePath);
12337
+ const pathTag = createPathCacheTag(normalizedPath);
12338
+ const pprCount = this.countEntriesForTags([pathTag, "ppr"]);
12339
+ const count = this.invalidateTag(pathTag);
12340
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(pathTag)]);
12341
+ emitFarmEvent({ type: "cache.revalidatePath", path: normalizedPath, count });
12342
+ if (pprCount > 0) {
12343
+ emitFarmEvent({
12344
+ type: "ppr.shell.invalidated",
12345
+ route: normalizedPath,
12346
+ reason: "revalidatePath",
12347
+ count: pprCount
12348
+ });
12349
+ }
12350
+ return count;
12351
+ }
12180
12352
  async getOrSet(key, producer, options = {}) {
12181
- const cached = this.getEntry(key);
12353
+ const cached = await this.getEntryAsync(key);
12182
12354
  if (cached) {
12183
12355
  return cached.value;
12184
12356
  }
@@ -12187,10 +12359,8 @@ var _FarmDataCache = class _FarmDataCache {
12187
12359
  emitFarmEvent({ type: "cache.dedupe", key });
12188
12360
  return inflight;
12189
12361
  }
12190
- const promise = Promise.resolve().then(producer).then((value) => {
12191
- this.set(key, value, options);
12192
- return value;
12193
- }).catch((error) => {
12362
+ const tags = Array.from(normalizeCacheOptionsTags(options));
12363
+ const promise = this.fillCacheEntry(key, producer, options, tags).catch((error) => {
12194
12364
  emitFarmEvent({ type: "cache.error", key, operation: "set", error });
12195
12365
  throw error;
12196
12366
  }).finally(() => {
@@ -12199,6 +12369,40 @@ var _FarmDataCache = class _FarmDataCache {
12199
12369
  this.inflight.set(key, promise);
12200
12370
  return promise;
12201
12371
  }
12372
+ async fillCacheEntry(key, producer, options, tags) {
12373
+ const leaseKey = `${this.namespace}:lease:${key}`;
12374
+ let leaseToken;
12375
+ if (this.adapter?.acquireLease && this.adapter.releaseLease && this.lease.enabled) {
12376
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
12377
+ if (!leaseToken) {
12378
+ const shared = await this.waitForAdapterEntry(key);
12379
+ if (shared) {
12380
+ emitFarmEvent({ type: "cache.dedupe", key });
12381
+ return shared.value;
12382
+ }
12383
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
12384
+ }
12385
+ }
12386
+ try {
12387
+ const initialTagVersions = await this.getAdapterTagVersions(tags);
12388
+ const value = await producer();
12389
+ await this.setAsync(key, value, options, initialTagVersions);
12390
+ return value;
12391
+ } finally {
12392
+ if (leaseToken && this.adapter?.releaseLease) {
12393
+ await this.adapter.releaseLease(leaseKey, leaseToken);
12394
+ }
12395
+ }
12396
+ }
12397
+ async waitForAdapterEntry(key) {
12398
+ const deadline = Date.now() + this.lease.waitTimeoutMs;
12399
+ while (Date.now() < deadline) {
12400
+ await delay(this.lease.pollIntervalMs);
12401
+ const entry = await this.getEntryAsync(key);
12402
+ if (entry) return entry;
12403
+ }
12404
+ return void 0;
12405
+ }
12202
12406
  countEntriesForTag(tag) {
12203
12407
  let count = 0;
12204
12408
  for (const entry of this.entries.values()) {
@@ -12226,11 +12430,53 @@ var _FarmDataCache = class _FarmDataCache {
12226
12430
  key: entry.key,
12227
12431
  value: entry.value,
12228
12432
  tags: Array.from(entry.tags),
12433
+ tagVersions: entry.tagVersions,
12229
12434
  createdAt: entry.createdAt,
12230
12435
  createdVersion: entry.createdVersion,
12231
12436
  revalidate: entry.revalidate
12232
12437
  };
12233
12438
  }
12439
+ hydrateLocalEntry(entry) {
12440
+ this.entries.set(entry.key, {
12441
+ key: entry.key,
12442
+ value: entry.value,
12443
+ tags: new Set(entry.tags.map(normalizeCacheTag)),
12444
+ tagVersions: entry.tagVersions,
12445
+ createdAt: entry.createdAt,
12446
+ createdVersion: entry.createdVersion ?? ++this.version,
12447
+ revalidate: normalizeRevalidate(entry.revalidate)
12448
+ });
12449
+ }
12450
+ createAdapterKey(key) {
12451
+ return `${this.namespace}:entry:${key}`;
12452
+ }
12453
+ createAdapterTag(tag) {
12454
+ return `${this.namespace}:tag:${tag}`;
12455
+ }
12456
+ async getAdapterTagVersions(tags) {
12457
+ if (!this.adapter?.getTagVersions || tags.length === 0) return {};
12458
+ const normalized = tags.map(normalizeCacheTag);
12459
+ const physicalTags = normalized.map((tag) => this.createAdapterTag(tag));
12460
+ const versions = await this.adapter.getTagVersions(physicalTags);
12461
+ return Object.fromEntries(
12462
+ normalized.map((tag, index) => [
12463
+ tag,
12464
+ normalizeAdapterVersion(versions[physicalTags[index]])
12465
+ ])
12466
+ );
12467
+ }
12468
+ async isAdapterEntryStale(entry, now = Date.now()) {
12469
+ if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
12470
+ return true;
12471
+ }
12472
+ const currentVersions = await this.getAdapterTagVersions(entry.tags);
12473
+ for (const tag of entry.tags) {
12474
+ if (normalizeAdapterVersion(currentVersions[tag]) > normalizeAdapterVersion(entry.tagVersions?.[tag])) {
12475
+ return true;
12476
+ }
12477
+ }
12478
+ return false;
12479
+ }
12234
12480
  };
12235
12481
  __name(_FarmDataCache, "FarmDataCache");
12236
12482
  var FarmDataCache = _FarmDataCache;
@@ -12243,6 +12489,10 @@ function getFarmDataCache() {
12243
12489
  return sharedFarmDataCache;
12244
12490
  }
12245
12491
  __name(getFarmDataCache, "getFarmDataCache");
12492
+ function configureFarmCache(config) {
12493
+ sharedFarmDataCache.configure(config);
12494
+ }
12495
+ __name(configureFarmCache, "configureFarmCache");
12246
12496
  function createPathCacheTag(routePath) {
12247
12497
  return `path:${normalizeRevalidatePath(routePath)}`;
12248
12498
  }
@@ -12303,6 +12553,52 @@ function normalizeCacheTag(tag) {
12303
12553
  return normalized;
12304
12554
  }
12305
12555
  __name(normalizeCacheTag, "normalizeCacheTag");
12556
+ function normalizeCacheNamespace(namespace) {
12557
+ if (typeof namespace !== "string") {
12558
+ throw new TypeError("Cache namespace must be a string.");
12559
+ }
12560
+ const normalized = namespace.trim().replace(/:+$/g, "");
12561
+ if (!normalized) {
12562
+ throw new Error("Cache namespace cannot be empty.");
12563
+ }
12564
+ return normalized;
12565
+ }
12566
+ __name(normalizeCacheNamespace, "normalizeCacheNamespace");
12567
+ function normalizeAdapterVersion(value) {
12568
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
12569
+ }
12570
+ __name(normalizeAdapterVersion, "normalizeAdapterVersion");
12571
+ function normalizePositiveDuration(value, fallback, name) {
12572
+ if (value === void 0) return fallback;
12573
+ if (!Number.isFinite(value) || value <= 0) {
12574
+ throw new TypeError(`${name} must be a positive number of milliseconds.`);
12575
+ }
12576
+ return Math.floor(value);
12577
+ }
12578
+ __name(normalizePositiveDuration, "normalizePositiveDuration");
12579
+ function delay(ms) {
12580
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
12581
+ }
12582
+ __name(delay, "delay");
12583
+ function normalizeCacheOptionsTags(options) {
12584
+ const tags = /* @__PURE__ */ new Set();
12585
+ for (const tag of options.tags ?? []) {
12586
+ tags.add(normalizeCacheTag(tag));
12587
+ }
12588
+ for (const routePath of options.paths ?? []) {
12589
+ tags.add(createPathCacheTag(routePath));
12590
+ }
12591
+ return tags;
12592
+ }
12593
+ __name(normalizeCacheOptionsTags, "normalizeCacheOptionsTags");
12594
+ function assertFarmCacheEntry(entry, expectedKey) {
12595
+ if (!entry || typeof entry !== "object" || entry.key !== expectedKey || !Array.isArray(entry.tags) || typeof entry.createdAt !== "number") {
12596
+ throw new TypeError(
12597
+ `Cache adapter returned an invalid entry for ${JSON.stringify(expectedKey)}.`
12598
+ );
12599
+ }
12600
+ }
12601
+ __name(assertFarmCacheEntry, "assertFarmCacheEntry");
12306
12602
  function getFunctionCacheIdentity(fn) {
12307
12603
  if (fn.name) {
12308
12604
  return `name:${fn.name}`;
@@ -13443,6 +13739,76 @@ function normalizePathname(pathname) {
13443
13739
  }
13444
13740
  __name(normalizePathname, "normalizePathname");
13445
13741
 
13742
+ // src/routing/route-slots.ts
13743
+ var SLOT_SEGMENT = /^@([A-Za-z][A-Za-z0-9_-]*)$/;
13744
+ function parseRouteSlotFile(filePath) {
13745
+ const normalized = filePath.replace(/\\/g, "/");
13746
+ const parts = normalized.split("/").filter(Boolean);
13747
+ const fileName = parts.at(-1) ?? "";
13748
+ const baseName = fileName.replace(/\.(tsx?|jsx?)$/, "");
13749
+ if (baseName !== "page" && baseName !== "default") return null;
13750
+ const slotIndex = parts.findIndex((part) => SLOT_SEGMENT.test(part));
13751
+ if (slotIndex < 0) return null;
13752
+ if (parts.slice(slotIndex + 1, -1).some((part) => SLOT_SEGMENT.test(part))) {
13753
+ throw new Error(`Nested route slots are not supported in "${filePath}"`);
13754
+ }
13755
+ const slotMatch = parts[slotIndex].match(SLOT_SEGMENT);
13756
+ const name = slotMatch?.[1];
13757
+ if (!name) return null;
13758
+ const ownerParts = parts.slice(0, slotIndex);
13759
+ const contentParts = parts.slice(slotIndex + 1, -1);
13760
+ const marker = parseInterceptionMarker(contentParts[0]);
13761
+ let targetBase = ownerParts;
13762
+ let targetParts = contentParts;
13763
+ if (marker) {
13764
+ targetBase = marker.root === true ? [] : ownerParts.slice(0, Math.max(0, ownerParts.length - marker.up));
13765
+ targetParts = [...marker.remainder ? [marker.remainder] : [], ...contentParts.slice(1)];
13766
+ }
13767
+ const ownerFile = [...ownerParts, "layout.tsx"].join("/");
13768
+ const routeFile = [
13769
+ ...baseName === "default" ? ownerParts : targetBase,
13770
+ ...baseName === "default" ? [] : targetParts,
13771
+ "page.tsx"
13772
+ ].join("/");
13773
+ return {
13774
+ name,
13775
+ ownerRoute: parseRoutePath(ownerFile || "layout.tsx"),
13776
+ route: parseRoutePath(routeFile || "page.tsx"),
13777
+ interception: marker !== null,
13778
+ fallback: baseName === "default"
13779
+ };
13780
+ }
13781
+ __name(parseRouteSlotFile, "parseRouteSlotFile");
13782
+ function createRouteSlotContainerId(name, ownerPattern) {
13783
+ const owner = ownerPattern === "/" ? "root" : ownerPattern.replace(/^\//, "").replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
13784
+ return `__farm_slot_${name}_${owner || "root"}__`;
13785
+ }
13786
+ __name(createRouteSlotContainerId, "createRouteSlotContainerId");
13787
+ function parseInterceptionMarker(segment) {
13788
+ if (!segment) return null;
13789
+ if (segment.startsWith("(...)")) {
13790
+ return {
13791
+ up: 0,
13792
+ root: true,
13793
+ remainder: segment.slice(5)
13794
+ };
13795
+ }
13796
+ if (segment.startsWith("(.)")) {
13797
+ return {
13798
+ up: 0,
13799
+ remainder: segment.slice(3)
13800
+ };
13801
+ }
13802
+ let remainder = segment;
13803
+ let up = 0;
13804
+ while (remainder.startsWith("(..)")) {
13805
+ up += 1;
13806
+ remainder = remainder.slice(4);
13807
+ }
13808
+ return up > 0 ? { up, remainder } : null;
13809
+ }
13810
+ __name(parseInterceptionMarker, "parseInterceptionMarker");
13811
+
13446
13812
  // src/routing/route-manager.ts
13447
13813
  function routeSpecificity(entry) {
13448
13814
  return entry.route.segments.reduce((score, segment) => {
@@ -13456,6 +13822,7 @@ var _RouteManager = class _RouteManager {
13456
13822
  constructor(config, viteServer) {
13457
13823
  this.routes = /* @__PURE__ */ new Map();
13458
13824
  this.layouts = /* @__PURE__ */ new Map();
13825
+ this.routeSlots = /* @__PURE__ */ new Map();
13459
13826
  this.loadings = /* @__PURE__ */ new Map();
13460
13827
  this.errors = /* @__PURE__ */ new Map();
13461
13828
  this.metadataImages = /* @__PURE__ */ new Map();
@@ -13471,6 +13838,7 @@ var _RouteManager = class _RouteManager {
13471
13838
  async discoverRoutes() {
13472
13839
  this.routes.clear();
13473
13840
  this.layouts.clear();
13841
+ this.routeSlots.clear();
13474
13842
  this.loadings.clear();
13475
13843
  this.errors.clear();
13476
13844
  this.metadataImages.clear();
@@ -13481,6 +13849,13 @@ var _RouteManager = class _RouteManager {
13481
13849
  await this.discoverFileRoutes(source);
13482
13850
  await this.discoverProgrammaticRoutes(source);
13483
13851
  }
13852
+ for (const slot of this.routeSlots.values()) {
13853
+ if (slot.interception && !this.routes.has(slot.pattern)) {
13854
+ throw new Error(
13855
+ `Intercepting route slot "${slot.name}" targets "${slot.pattern}", but no canonical page exists for that URL`
13856
+ );
13857
+ }
13858
+ }
13484
13859
  this.routes = new Map(
13485
13860
  Array.from(this.routes.entries()).sort(
13486
13861
  ([, left], [, right]) => routeSpecificity(right) - routeSpecificity(left)
@@ -13496,7 +13871,7 @@ var _RouteManager = class _RouteManager {
13496
13871
  /**
13497
13872
  * Find matching route for a given URL path
13498
13873
  */
13499
- matchRoute(pathname) {
13874
+ matchRoute(pathname, options = {}) {
13500
13875
  const routePathname = this.toRoutePathname(pathname);
13501
13876
  const normalizedPath = routePathname === "/" ? "/" : routePathname.replace(/\/$/, "");
13502
13877
  let matchedRoute = null;
@@ -13510,10 +13885,12 @@ var _RouteManager = class _RouteManager {
13510
13885
  }
13511
13886
  }
13512
13887
  const layouts = this.findMatchingLayouts(normalizedPath);
13888
+ const slots = this.findMatchingRouteSlots(normalizedPath, options.interceptFrom);
13513
13889
  return {
13514
13890
  route: matchedRoute,
13515
13891
  params,
13516
- layouts
13892
+ layouts,
13893
+ slots
13517
13894
  };
13518
13895
  }
13519
13896
  /**
@@ -13528,6 +13905,9 @@ var _RouteManager = class _RouteManager {
13528
13905
  getLayouts() {
13529
13906
  return new Map(this.layouts);
13530
13907
  }
13908
+ getRouteSlots() {
13909
+ return new Map(this.routeSlots);
13910
+ }
13531
13911
  /** Resolve route rules, inherited layouts, and the page export in precedence order. */
13532
13912
  async resolveRouteRuntimeConfig(pattern) {
13533
13913
  const routeEntry = this.routes.get(pattern);
@@ -13686,7 +14066,22 @@ var _RouteManager = class _RouteManager {
13686
14066
  pattern: entry.pattern,
13687
14067
  modulePath: toUrlPath(entry.modulePath)
13688
14068
  }));
13689
- return { routes, layouts };
14069
+ const slots = Array.from(this.routeSlots.values()).map((entry) => {
14070
+ const metadata = getClientModuleMetadata(entry.modulePath, projectRoot);
14071
+ return {
14072
+ name: entry.name,
14073
+ ownerPattern: entry.ownerPattern,
14074
+ pattern: entry.pattern,
14075
+ modulePath: toUrlPath(entry.modulePath),
14076
+ containerId: entry.containerId,
14077
+ interception: entry.interception,
14078
+ fallback: entry.fallback,
14079
+ shouldHydrate: metadata.shouldHydrate,
14080
+ isClientComponent: metadata.isClientComponent,
14081
+ segments: entry.route.segments
14082
+ };
14083
+ });
14084
+ return { routes, layouts, slots };
13690
14085
  }
13691
14086
  /**
13692
14087
  * Load a route module dynamically
@@ -13763,6 +14158,7 @@ var _RouteManager = class _RouteManager {
13763
14158
  async discoverFileRoutes(source) {
13764
14159
  const appDir = resolveAppPath(source.root, source.srcDir, "app");
13765
14160
  const pageFiles = await safeGlobFiles("**/page.{ts,tsx,js,jsx,md,mdx}", appDir);
14161
+ const slotDefaultFiles = await safeGlobFiles("**/@*/**/default.{ts,tsx,js,jsx}", appDir);
13766
14162
  const layoutFiles = await safeGlobFiles("**/layout.{ts,tsx,js,jsx}", appDir);
13767
14163
  const loadingFiles = await safeGlobFiles("**/loading.{ts,tsx,js,jsx}", appDir);
13768
14164
  const errorFiles = await safeGlobFiles("**/error.{ts,tsx,js,jsx}", appDir);
@@ -13771,7 +14167,7 @@ var _RouteManager = class _RouteManager {
13771
14167
  appDir
13772
14168
  );
13773
14169
  for (const [kind, files, target] of [
13774
- ["page", pageFiles, this.routes],
14170
+ ["page", pageFiles.filter((file) => parseRouteSlotFile(file) === null), this.routes],
13775
14171
  ["layout", layoutFiles, this.layouts],
13776
14172
  ["loading", loadingFiles, this.loadings],
13777
14173
  ["error", errorFiles, this.errors]
@@ -13795,6 +14191,32 @@ var _RouteManager = class _RouteManager {
13795
14191
  });
13796
14192
  }
13797
14193
  }
14194
+ for (const file of [...pageFiles, ...slotDefaultFiles]) {
14195
+ const slot = parseRouteSlotFile(file);
14196
+ if (!slot) continue;
14197
+ const modulePath = import_path5.default.join(appDir, file);
14198
+ const pattern = this.createRoutePattern(slot.route);
14199
+ const ownerPattern = this.createRoutePattern(slot.ownerRoute);
14200
+ const key = `${ownerPattern}:${slot.name}:${slot.interception ? "intercept" : "slot"}:${slot.fallback ? "default" : pattern}`;
14201
+ const existing = this.routeSlots.get(key);
14202
+ if (existing?.sourceRoot === source.root) {
14203
+ throw new Error(
14204
+ `Duplicate route slot "${slot.name}" for "${pattern}". Found both ${existing.modulePath} and ${modulePath}.`
14205
+ );
14206
+ }
14207
+ this.routeSlots.set(key, {
14208
+ route: slot.route,
14209
+ modulePath,
14210
+ pattern,
14211
+ source: "file",
14212
+ sourceRoot: source.root,
14213
+ name: slot.name,
14214
+ ownerPattern,
14215
+ interception: slot.interception,
14216
+ fallback: slot.fallback,
14217
+ containerId: createRouteSlotContainerId(slot.name, ownerPattern)
14218
+ });
14219
+ }
13798
14220
  for (const file of metadataImageFiles) {
13799
14221
  const fileBase = import_path5.default.basename(file, import_path5.default.extname(file));
13800
14222
  const kind = fileBase === "twitter-image" ? "twitter" : "opengraph";
@@ -13920,6 +14342,71 @@ var _RouteManager = class _RouteManager {
13920
14342
  }
13921
14343
  return matchingLayouts;
13922
14344
  }
14345
+ findMatchingRouteSlots(pathname, interceptFrom) {
14346
+ const groups = /* @__PURE__ */ new Map();
14347
+ for (const entry of this.routeSlots.values()) {
14348
+ if (!this.matchesRoutePrefix(pathname, entry.ownerPattern)) continue;
14349
+ const key = `${entry.ownerPattern}:${entry.name}`;
14350
+ const entries = groups.get(key) ?? [];
14351
+ entries.push(entry);
14352
+ groups.set(key, entries);
14353
+ }
14354
+ const normalizedFrom = interceptFrom ? this.toRoutePathname(new URL(interceptFrom, "http://farm.local").pathname) : void 0;
14355
+ const matches = [];
14356
+ for (const entries of groups.values()) {
14357
+ const candidates = entries.filter((entry) => !entry.fallback).filter(
14358
+ (entry) => !entry.interception || normalizedFrom !== void 0 && this.matchesRoutePrefix(normalizedFrom, entry.ownerPattern)
14359
+ ).map((entry) => ({
14360
+ entry,
14361
+ match: matchRoute(pathname, entry.route.segments)
14362
+ })).filter((candidate) => candidate.match.matches).sort((left, right) => {
14363
+ if (left.entry.interception !== right.entry.interception) {
14364
+ return left.entry.interception ? -1 : 1;
14365
+ }
14366
+ return routeSpecificity(right.entry) - routeSpecificity(left.entry);
14367
+ });
14368
+ const selected = candidates[0];
14369
+ if (selected) {
14370
+ matches.push({
14371
+ name: selected.entry.name,
14372
+ ownerPattern: selected.entry.ownerPattern,
14373
+ containerId: selected.entry.containerId,
14374
+ interception: selected.entry.interception,
14375
+ fallback: false,
14376
+ route: selected.entry,
14377
+ params: selected.match.params
14378
+ });
14379
+ continue;
14380
+ }
14381
+ const fallback = entries.find((entry) => entry.fallback);
14382
+ if (fallback) {
14383
+ matches.push({
14384
+ name: fallback.name,
14385
+ ownerPattern: fallback.ownerPattern,
14386
+ containerId: fallback.containerId,
14387
+ interception: false,
14388
+ fallback: true,
14389
+ route: fallback,
14390
+ params: {}
14391
+ });
14392
+ }
14393
+ }
14394
+ return matches.sort((left, right) => {
14395
+ const ownerDifference = left.route.route.segments.length - right.route.route.segments.length;
14396
+ return ownerDifference || left.name.localeCompare(right.name);
14397
+ });
14398
+ }
14399
+ matchesRoutePrefix(pathname, pattern) {
14400
+ if (pattern === "/") return true;
14401
+ const patternSegments = parseRoutePath(`${pattern}/page.tsx`).segments;
14402
+ const pathSegments = pathname.split("/").filter(Boolean);
14403
+ if (patternSegments.length > pathSegments.length) return false;
14404
+ for (let index = 0; index < patternSegments.length; index++) {
14405
+ const segment = patternSegments[index];
14406
+ if (!segment.isDynamic && segment.segment !== pathSegments[index]) return false;
14407
+ }
14408
+ return true;
14409
+ }
13923
14410
  findNearestBoundary(pathname, boundaries) {
13924
14411
  const normalizedPath = pathname === "/" ? "/" : pathname.replace(/\/$/, "");
13925
14412
  const pathSegments = normalizedPath.split("/").filter(Boolean);
@@ -14522,7 +15009,7 @@ function mergeMetadata(base, next) {
14522
15009
  __name(mergeMetadata, "mergeMetadata");
14523
15010
  function addMetadataImageReference(metadata, reference) {
14524
15011
  if (reference.kind === "opengraph") {
14525
- const openGraph = { ...metadata.openGraph || {} };
15012
+ const openGraph = { ...metadata.openGraph };
14526
15013
  if (!hasMetadataImages(openGraph.images) && !hasMetadataImages(openGraph.image)) {
14527
15014
  openGraph.images = [
14528
15015
  {
@@ -14539,7 +15026,7 @@ function addMetadataImageReference(metadata, reference) {
14539
15026
  openGraph
14540
15027
  };
14541
15028
  }
14542
- const twitter = { ...metadata.twitter || {} };
15029
+ const twitter = { ...metadata.twitter };
14543
15030
  if (!hasMetadataImages(twitter.images)) {
14544
15031
  twitter.images = [reference.href];
14545
15032
  twitter.card = twitter.card || "summary_large_image";
@@ -15145,8 +15632,8 @@ var _FarmI18nRuntime = class _FarmI18nRuntime {
15145
15632
  cookie: this.config.cookie,
15146
15633
  direction: getFarmLocaleDirection(resolution.locale, this.config.direction),
15147
15634
  messages: {
15148
- ...this.catalogs[this.config.fallbackLocale] || {},
15149
- ...this.catalogs[resolution.locale] || {}
15635
+ ...this.catalogs[this.config.fallbackLocale],
15636
+ ...this.catalogs[resolution.locale]
15150
15637
  }
15151
15638
  };
15152
15639
  }
@@ -15448,7 +15935,9 @@ var _ServerRenderer = class _ServerRenderer {
15448
15935
  async runWithRequestContext(request, fn) {
15449
15936
  return _runWithCurrentRequest(
15450
15937
  request,
15451
- () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, { redirect: false }) : fn()
15938
+ () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, {
15939
+ redirect: false
15940
+ }) : fn()
15452
15941
  );
15453
15942
  }
15454
15943
  async resolveRouteContext(input) {
@@ -15471,11 +15960,11 @@ var _ServerRenderer = class _ServerRenderer {
15471
15960
  /**
15472
15961
  * Check if a path should be served from SSG cache
15473
15962
  */
15474
- shouldServeSSG(pathname) {
15963
+ async shouldServeSSG(pathname) {
15475
15964
  const ssgPage = matchSSGPage(pathname, this.ssgManifest);
15476
15965
  if (!ssgPage) return null;
15477
- const cached = this.getCachedSSGPage(pathname);
15478
- if (cached && this.dataCache.isStale(cached)) {
15966
+ const cached = await this.getCachedSSGPage(pathname);
15967
+ if (cached && await this.dataCache.isStaleAsync(cached)) {
15479
15968
  this.regenerateSSGPage(ssgPage);
15480
15969
  }
15481
15970
  return ssgPage;
@@ -15487,12 +15976,12 @@ var _ServerRenderer = class _ServerRenderer {
15487
15976
  return createFarmCacheKey(["ppr", normalizeRevalidatePath(pathname), search]);
15488
15977
  }
15489
15978
  getCachedSSGPage(urlPath) {
15490
- return this.dataCache.getEntry(this.getSSGCacheKey(urlPath), {
15979
+ return this.dataCache.getEntryAsync(this.getSSGCacheKey(urlPath), {
15491
15980
  allowStale: true
15492
15981
  });
15493
15982
  }
15494
- cacheSSGPage(page2, html, options) {
15495
- this.dataCache.set(
15983
+ async cacheSSGPage(page2, html, options) {
15984
+ await this.dataCache.setAsync(
15496
15985
  this.getSSGCacheKey(page2.urlPath),
15497
15986
  { html, document: options.document },
15498
15987
  {
@@ -15504,11 +15993,11 @@ var _ServerRenderer = class _ServerRenderer {
15504
15993
  );
15505
15994
  }
15506
15995
  getCachedPPRShell(pathname, search) {
15507
- return this.dataCache.getEntry(this.getPPRCacheKey(pathname, search));
15996
+ return this.dataCache.getEntryAsync(this.getPPRCacheKey(pathname, search));
15508
15997
  }
15509
- cachePPRShell(options, html) {
15998
+ async cachePPRShell(options, html) {
15510
15999
  const key = this.getPPRCacheKey(options.pathname, options.search);
15511
- this.dataCache.set(
16000
+ await this.dataCache.setAsync(
15512
16001
  key,
15513
16002
  { html },
15514
16003
  {
@@ -15606,7 +16095,7 @@ var _ServerRenderer = class _ServerRenderer {
15606
16095
  }
15607
16096
  const { renderToString } = await import("react-dom/server");
15608
16097
  const html = renderToString(await this.wrapWithIntegrationProviders(pageElement));
15609
- this.cacheSSGPage(page2, html, { document: false });
16098
+ await this.cacheSSGPage(page2, html, { document: false });
15610
16099
  logger.info(`ISR: Regenerated ${page2.urlPath}`);
15611
16100
  } catch (error) {
15612
16101
  logger.error(`ISR regeneration failed for ${page2.urlPath}: ${error}`);
@@ -15620,7 +16109,7 @@ var _ServerRenderer = class _ServerRenderer {
15620
16109
  * Serve a pre-rendered SSG page
15621
16110
  */
15622
16111
  async serveSSGPage(req, res, page2) {
15623
- const cached = this.getCachedSSGPage(page2.urlPath);
16112
+ const cached = await this.getCachedSSGPage(page2.urlPath);
15624
16113
  if (cached) {
15625
16114
  res.setHeader("Content-Type", "text/html; charset=utf-8");
15626
16115
  res.setHeader("X-Farm-SSG", "cached");
@@ -15638,9 +16127,12 @@ var _ServerRenderer = class _ServerRenderer {
15638
16127
  if (fs2.existsSync(htmlPath)) {
15639
16128
  const stat = fs2.statSync(htmlPath);
15640
16129
  const html = fs2.readFileSync(htmlPath, "utf-8");
15641
- this.cacheSSGPage(page2, html, { document: true, createdAt: stat.mtimeMs });
15642
- const fileCacheEntry = this.getCachedSSGPage(page2.urlPath);
15643
- if (fileCacheEntry && this.dataCache.isStale(fileCacheEntry)) {
16130
+ await this.cacheSSGPage(page2, html, {
16131
+ document: true,
16132
+ createdAt: stat.mtimeMs
16133
+ });
16134
+ const fileCacheEntry = await this.getCachedSSGPage(page2.urlPath);
16135
+ if (fileCacheEntry && await this.dataCache.isStaleAsync(fileCacheEntry)) {
15644
16136
  this.regenerateSSGPage(page2);
15645
16137
  }
15646
16138
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -15686,6 +16178,7 @@ var _ServerRenderer = class _ServerRenderer {
15686
16178
  let pathname = "/";
15687
16179
  let params = {};
15688
16180
  let layouts = [];
16181
+ let routeSlots = [];
15689
16182
  let searchParamsObject = {};
15690
16183
  let middlewareMap = /* @__PURE__ */ new Map();
15691
16184
  let middlewareContext = /* @__PURE__ */ new Map();
@@ -15718,7 +16211,7 @@ var _ServerRenderer = class _ServerRenderer {
15718
16211
  }
15719
16212
  this.applyDeploymentHeaders(req, res);
15720
16213
  if (process.env.NODE_ENV === "production") {
15721
- const ssgPage = this.shouldServeSSG(pathname);
16214
+ const ssgPage = await this.shouldServeSSG(pathname);
15722
16215
  if (ssgPage) {
15723
16216
  const served = await this.serveSSGPage(req, res, ssgPage);
15724
16217
  if (served) {
@@ -15731,6 +16224,7 @@ var _ServerRenderer = class _ServerRenderer {
15731
16224
  const route = match.route;
15732
16225
  params = match.params;
15733
16226
  layouts = match.layouts;
16227
+ routeSlots = match.slots ?? [];
15734
16228
  if (!route) {
15735
16229
  emitFarmEvent({ type: "route.notFound", pathname });
15736
16230
  await this.render404(req, res);
@@ -15747,7 +16241,9 @@ var _ServerRenderer = class _ServerRenderer {
15747
16241
  errorBoundaryEntry = this.routeManager.getMatchingError(pathname);
15748
16242
  middlewareMap = toMiddlewareMap2(req.__FARM_MIDDLEWARE_DATA__);
15749
16243
  middlewareContext = toMiddlewareMap2(req.__FARM_MIDDLEWARE_CONTEXT__);
15750
- pluginExposedContext = getRequestContextSnapshot(req, { exposedOnly: true });
16244
+ pluginExposedContext = getRequestContextSnapshot(req, {
16245
+ exposedOnly: true
16246
+ });
15751
16247
  const currentRequest = createWebRequestFromFarmRequest(req);
15752
16248
  const routeContext = await this.resolveRouteContext({
15753
16249
  request: currentRequest,
@@ -15811,8 +16307,16 @@ var _ServerRenderer = class _ServerRenderer {
15811
16307
  revalidate: renderingConfig.revalidate
15812
16308
  } : void 0;
15813
16309
  if (renderingConfig.ppr && pprBypassReason) {
15814
- emitFarmEvent({ type: "ppr.shell.bypass", route: pathname, reason: pprBypassReason });
15815
- emitFarmEvent({ type: "cache.bypass", route: pathname, reason: pprBypassReason });
16310
+ emitFarmEvent({
16311
+ type: "ppr.shell.bypass",
16312
+ route: pathname,
16313
+ reason: pprBypassReason
16314
+ });
16315
+ emitFarmEvent({
16316
+ type: "cache.bypass",
16317
+ route: pathname,
16318
+ reason: pprBypassReason
16319
+ });
15816
16320
  if (pprBypassReason === "refresh") {
15817
16321
  pprRefreshRoute = pathname;
15818
16322
  emitFarmEvent({ type: "ppr.refresh.start", route: pathname });
@@ -15820,14 +16324,22 @@ var _ServerRenderer = class _ServerRenderer {
15820
16324
  }
15821
16325
  if (pprShellOptions) {
15822
16326
  const pprCacheKey = this.getPPRCacheKey(pathname, url.search);
15823
- const cachedPPRShell = this.getCachedPPRShell(pathname, url.search);
16327
+ const cachedPPRShell = await this.getCachedPPRShell(pathname, url.search);
15824
16328
  if (cachedPPRShell) {
15825
- emitFarmEvent({ type: "ppr.shell.hit", route: pathname, key: pprCacheKey });
16329
+ emitFarmEvent({
16330
+ type: "ppr.shell.hit",
16331
+ route: pathname,
16332
+ key: pprCacheKey
16333
+ });
15826
16334
  this.serveCachedPPRShell(res, cachedPPRShell.value, renderingConfig.revalidate);
15827
16335
  completeRender(res.statusCode || 200, pathname);
15828
16336
  return;
15829
16337
  }
15830
- emitFarmEvent({ type: "ppr.shell.miss", route: pathname, key: pprCacheKey });
16338
+ emitFarmEvent({
16339
+ type: "ppr.shell.miss",
16340
+ route: pathname,
16341
+ key: pprCacheKey
16342
+ });
15831
16343
  }
15832
16344
  let LoadingFallbackComponent = null;
15833
16345
  if (loadingBoundaryEntry) {
@@ -15851,14 +16363,77 @@ var _ServerRenderer = class _ServerRenderer {
15851
16363
  );
15852
16364
  const moduleMetadata = routeManifestEntry || getClientModuleMetadata(route.modulePath, this.config.root);
15853
16365
  const isClientComponent = moduleMetadata.isClientComponent;
16366
+ const renderedRouteSlots = await Promise.all(
16367
+ routeSlots.map(async (slot) => {
16368
+ const slotModule = await this.routeManager.loadRouteModule(slot.route.modulePath);
16369
+ if (!slotModule.default) {
16370
+ throw new Error(
16371
+ `Route slot "${slot.name}" module ${slot.route.modulePath} does not export a default component`
16372
+ );
16373
+ }
16374
+ const slotContext = await this.resolveRouteContext({
16375
+ request: currentRequest,
16376
+ rawRequest: req,
16377
+ params: slot.params,
16378
+ search: searchParamsObject,
16379
+ path: pathname
16380
+ });
16381
+ const rawSlotProps = withFarmRouteContext(
16382
+ {
16383
+ params: slot.params,
16384
+ searchParams: Promise.resolve(searchParamsObject),
16385
+ path: pathname,
16386
+ middleware: middlewareMap.size > 0 ? { data: middlewareMap } : void 0,
16387
+ context: pluginExposedContext.size > 0 ? { data: pluginExposedContext } : void 0
16388
+ },
16389
+ slotContext
16390
+ );
16391
+ const slotProps = await parseRouteModuleProps(slotModule, {
16392
+ props: rawSlotProps,
16393
+ search: searchParamsObject,
16394
+ routePath: slot.route.pattern
16395
+ });
16396
+ const metadata = getClientModuleMetadata(slot.route.modulePath, this.config.root);
16397
+ return {
16398
+ ...slot,
16399
+ module: slotModule,
16400
+ props: slotProps,
16401
+ isClientComponent: metadata.isClientComponent,
16402
+ shouldHydrate: metadata.shouldHydrate
16403
+ };
16404
+ })
16405
+ );
15854
16406
  const shouldHydrate = moduleMetadata.shouldHydrate;
16407
+ const hasHydratableRouteSlots = renderedRouteSlots.some(
16408
+ (slot) => slot.isClientComponent || slot.shouldHydrate
16409
+ );
15855
16410
  req.__FARM_PAGE_PATH__ = route.modulePath;
15856
16411
  req.__FARM_ROUTE__ = pathname;
15857
16412
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
15858
16413
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
16414
+ req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
15859
16415
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
15860
16416
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
15861
16417
  ) : null;
16418
+ req.__FARM_ROUTE_SLOTS__ = renderedRouteSlots.map((slot) => ({
16419
+ name: slot.name,
16420
+ ownerPattern: slot.ownerPattern,
16421
+ containerId: slot.containerId,
16422
+ interception: slot.interception,
16423
+ fallback: slot.fallback,
16424
+ modulePath: slot.route.modulePath,
16425
+ isClientComponent: slot.isClientComponent,
16426
+ shouldHydrate: slot.shouldHydrate,
16427
+ props: {
16428
+ params: slot.props.params,
16429
+ search: slot.props.search,
16430
+ searchParams: slot.props.search,
16431
+ ..."data" in slot.props ? { data: slot.props.data } : {},
16432
+ ...slot.props.__farmCanonicalPath ? { __farmCanonicalPath: slot.props.__farmCanonicalPath } : {},
16433
+ ...slot.props.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
16434
+ path: pathname
16435
+ }
16436
+ }));
15862
16437
  req.__FARM_PROPS__ = {
15863
16438
  params: pageProps.params,
15864
16439
  search: pageProps.search,
@@ -15918,12 +16493,32 @@ var _ServerRenderer = class _ServerRenderer {
15918
16493
  let wrappedElement = pageElement;
15919
16494
  for (let i = layoutModules.length - 1; i >= 0; i--) {
15920
16495
  const layoutModule = layoutModules[i];
16496
+ const layoutEntry = layouts[i];
15921
16497
  const LayoutComponent = layoutModule.default;
16498
+ const slotProps = {};
16499
+ for (const slot of renderedRouteSlots) {
16500
+ if (slot.ownerPattern !== layoutEntry.pattern) continue;
16501
+ let slotElement = import_react4.default.createElement(
16502
+ slot.module.default,
16503
+ slot.props
16504
+ );
16505
+ slotElement = import_react4.default.createElement(
16506
+ "div",
16507
+ {
16508
+ id: slot.containerId,
16509
+ "data-farm-route-slot": slot.name,
16510
+ "data-farm-slot-owner": slot.ownerPattern
16511
+ },
16512
+ slotElement
16513
+ );
16514
+ slotProps[slot.name] = slotElement;
16515
+ }
15922
16516
  wrappedElement = import_react4.default.createElement(
15923
16517
  LayoutComponent,
15924
16518
  {
15925
16519
  children: wrappedElement,
15926
- params
16520
+ params,
16521
+ ...slotProps
15927
16522
  }
15928
16523
  );
15929
16524
  }
@@ -15960,7 +16555,10 @@ var _ServerRenderer = class _ServerRenderer {
15960
16555
  routeManifest,
15961
16556
  captureStaticShell: Boolean(pprShellOptions),
15962
16557
  observabilityRoute: pathname,
15963
- onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({ type: "ppr.suspense.holeDetected", route: pathname }) : void 0,
16558
+ onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({
16559
+ type: "ppr.suspense.holeDetected",
16560
+ route: pathname
16561
+ }) : void 0,
15964
16562
  onComplete: pprShellOptions && req.method !== "HEAD" ? (html) => this.cachePPRShell(pprShellOptions, html) : void 0
15965
16563
  }
15966
16564
  );
@@ -16008,7 +16606,11 @@ var _ServerRenderer = class _ServerRenderer {
16008
16606
  }
16009
16607
  emitFarmEvent({ type: "render.error", route: pathname, error });
16010
16608
  if (pprRefreshRoute) {
16011
- emitFarmEvent({ type: "ppr.refresh.error", route: pprRefreshRoute, error });
16609
+ emitFarmEvent({
16610
+ type: "ppr.refresh.error",
16611
+ route: pprRefreshRoute,
16612
+ error
16613
+ });
16012
16614
  }
16013
16615
  logger.error(`Error rendering page: ${error}`);
16014
16616
  if (res.headersSent || res.writableEnded) {
@@ -16061,7 +16663,9 @@ var _ServerRenderer = class _ServerRenderer {
16061
16663
  if (typeof layoutModule.generateMetadata === "function") {
16062
16664
  metadata = mergeMetadata(
16063
16665
  metadata,
16064
- await layoutModule.generateMetadata({ params: options.pageProps.params })
16666
+ await layoutModule.generateMetadata({
16667
+ params: options.pageProps.params
16668
+ })
16065
16669
  );
16066
16670
  }
16067
16671
  }
@@ -16292,7 +16896,13 @@ var _ServerRenderer = class _ServerRenderer {
16292
16896
  clientEntry: "/@farm/client.js",
16293
16897
  routes: {},
16294
16898
  layouts: {},
16295
- sharedAssets: [{ tag: "link", attrs: { rel: "stylesheet", href: "/src/app/globals.css" } }]
16899
+ slots: [],
16900
+ sharedAssets: [
16901
+ {
16902
+ tag: "link",
16903
+ attrs: { rel: "stylesheet", href: "/src/app/globals.css" }
16904
+ }
16905
+ ]
16296
16906
  };
16297
16907
  for (const routeEntry of manifest.routes) {
16298
16908
  clientManifest.routes[routeEntry.pattern] = {
@@ -16314,9 +16924,26 @@ var _ServerRenderer = class _ServerRenderer {
16314
16924
  assets: []
16315
16925
  };
16316
16926
  }
16317
- const deferredProps = prepareDeferredData(req.__FARM_PROPS__ || {});
16927
+ for (const slotEntry of manifest.slots ?? []) {
16928
+ clientManifest.slots.push({
16929
+ ...slotEntry,
16930
+ preloads: [slotEntry.modulePath],
16931
+ assets: []
16932
+ });
16933
+ }
16934
+ const routeSlotPayload = (req.__FARM_ROUTE_SLOTS__ || []).map(
16935
+ (slot) => ({
16936
+ ...slot,
16937
+ modulePath: typeof slot.modulePath === "string" && slot.modulePath.startsWith(this.config.root) ? slot.modulePath.slice(this.config.root.length) : slot.modulePath
16938
+ })
16939
+ );
16940
+ const deferredProps = prepareDeferredData({
16941
+ page: req.__FARM_PROPS__ || {},
16942
+ slots: routeSlotPayload
16943
+ });
16318
16944
  const propsScript = `<script>
16319
- window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data)};
16945
+ window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data.page)};
16946
+ window.__FARM_ROUTE_SLOTS__ = ${serializeInlineValue(deferredProps.data.slots)};
16320
16947
  window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
16321
16948
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
16322
16949
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
@@ -16329,7 +16956,7 @@ window.__FARM_MANIFEST__ = ${JSON.stringify(clientManifest)};
16329
16956
  window.__FARM_INTEGRATION_API_MANIFEST__ = ${JSON.stringify(getRegisteredIntegrationAPIManifest())};
16330
16957
  ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(getFarmI18nClientSnapshot())};` : ""}
16331
16958
  </script>`;
16332
- const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true ? createPreHydrationClickQueueScript() : "";
16959
+ const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true || req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ === true ? createPreHydrationClickQueueScript() : "";
16333
16960
  const {
16334
16961
  title,
16335
16962
  tags: metaTags,
@@ -16440,7 +17067,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
16440
17067
  didError = true;
16441
17068
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
16442
17069
  logger.error(`SSR shell error: ${error}`);
16443
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
17070
+ emitFarmEvent({
17071
+ type: "render.error",
17072
+ route: observabilityRoute,
17073
+ error
17074
+ });
16444
17075
  }
16445
17076
  if (clearMiddlewareData) {
16446
17077
  clearMiddlewareData();
@@ -16451,7 +17082,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
16451
17082
  didError = true;
16452
17083
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
16453
17084
  logger.error(`SSR streaming error: ${error}`);
16454
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
17085
+ emitFarmEvent({
17086
+ type: "render.error",
17087
+ route: observabilityRoute,
17088
+ error
17089
+ });
16455
17090
  }
16456
17091
  }
16457
17092
  });
@@ -16620,17 +17255,14 @@ var _FarmApp = class _FarmApp {
16620
17255
  _setDefaultFarmI18nRuntime(this.i18nRuntime);
16621
17256
  this.viteServer = viteServer;
16622
17257
  this.routeManager = new RouteManager(this.config, viteServer);
16623
- this.serverRenderer = new ServerRenderer(
16624
- this.config,
16625
- this.routeManager,
16626
- this.i18nRuntime
16627
- );
17258
+ this.serverRenderer = new ServerRenderer(this.config, this.routeManager, this.i18nRuntime);
16628
17259
  }
16629
17260
  async initialize() {
16630
17261
  if (process.env.FARM_VERBOSE) {
16631
17262
  logger.info("Initializing Farm.js application...");
16632
17263
  }
16633
17264
  await initStorage(this.config.storage);
17265
+ await configureFarmCache(this.config.cache);
16634
17266
  await this.i18nRuntime.initialize();
16635
17267
  await this.verifyAppStructure();
16636
17268
  await this.routeManager.discoverRoutes();
@@ -16662,6 +17294,7 @@ var _FarmApp = class _FarmApp {
16662
17294
  preset: config.preset ?? "node-server",
16663
17295
  deploy: config.deploy || {},
16664
17296
  storage: config.storage || {},
17297
+ cache: config.cache || {},
16665
17298
  integrations: config.integrations || {},
16666
17299
  plugins: config.plugins || [],
16667
17300
  migrations: config.migrations || { commands: [] },
@@ -17069,6 +17702,29 @@ var fs3 = __toESM(require("fs"));
17069
17702
  var path10 = __toESM(require("path"));
17070
17703
  init_route_runtime();
17071
17704
 
17705
+ // src/api/runtime.ts
17706
+ init_cache_invalidation();
17707
+
17708
+ // src/api/endpoint.ts
17709
+ var import_better_call = require("better-call");
17710
+ var _EndpointFailure = class _EndpointFailure extends Error {
17711
+ constructor(code, data, options) {
17712
+ super(options.message);
17713
+ this.name = "EndpointFailure";
17714
+ this.code = code;
17715
+ this.data = data;
17716
+ this.status = options.status;
17717
+ }
17718
+ };
17719
+ __name(_EndpointFailure, "EndpointFailure");
17720
+ var EndpointFailure = _EndpointFailure;
17721
+ function isEndpointFailure(value) {
17722
+ return value instanceof EndpointFailure;
17723
+ }
17724
+ __name(isEndpointFailure, "isEndpointFailure");
17725
+ var EMPTY_ENDPOINT_MIDDLEWARE = Object.freeze([]);
17726
+ var EMPTY_ENDPOINT_CONTEXT = Object.freeze(/* @__PURE__ */ Object.create(null));
17727
+
17072
17728
  // src/api/runtime.ts
17073
17729
  function matchAPIRoute(routes, pathname) {
17074
17730
  const exactRoute = routes.get(pathname);
@@ -17101,10 +17757,10 @@ __name(invokeAPIRouteEndpoint, "invokeAPIRouteEndpoint");
17101
17757
  async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
17102
17758
  const farmHandler = endpoint.__handler || (isFarmContextHandler(endpoint) ? endpoint : null);
17103
17759
  if (!farmHandler) {
17104
- const result2 = await endpoint(request, {
17760
+ const result = await endpoint(request, {
17105
17761
  params: Promise.resolve(params)
17106
17762
  });
17107
- return normalizeRouteResponse(result2);
17763
+ return normalizeRouteResponse(result);
17108
17764
  }
17109
17765
  const url = new URL(request.url);
17110
17766
  const query = {};
@@ -17113,7 +17769,7 @@ async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
17113
17769
  });
17114
17770
  let body = void 0;
17115
17771
  if (request.method.toUpperCase() !== "GET" && request.method.toUpperCase() !== "HEAD") {
17116
- body = await readJSONBody(request);
17772
+ body = await readRequestBody(request);
17117
17773
  }
17118
17774
  const headers = Object.fromEntries(request.headers.entries());
17119
17775
  const types = endpoint.__types || {};
@@ -17129,15 +17785,30 @@ async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
17129
17785
  if (headersValidation instanceof Response) {
17130
17786
  return headersValidation;
17131
17787
  }
17132
- const result = await farmHandler({
17788
+ const handlerContext = {
17133
17789
  query: queryValidation,
17134
17790
  body: bodyValidation,
17135
17791
  headers: headersValidation,
17136
17792
  request,
17137
17793
  context: {},
17138
17794
  params
17139
- });
17140
- return normalizeRouteResponse(result);
17795
+ };
17796
+ let execution;
17797
+ try {
17798
+ execution = typeof endpoint.__farmInvoke === "function" ? await endpoint.__farmInvoke(handlerContext) : {
17799
+ result: await farmHandler(handlerContext),
17800
+ context: handlerContext.context,
17801
+ handlerExecuted: true,
17802
+ invalidations: []
17803
+ };
17804
+ } catch (error) {
17805
+ if (isEndpointFailure(error)) {
17806
+ return createEndpointFailureResponse(error);
17807
+ }
17808
+ throw error;
17809
+ }
17810
+ const response = normalizeRouteResponse(execution.result);
17811
+ return attachEndpointInvalidations(response, execution.invalidations);
17141
17812
  }
17142
17813
  __name(invokeAPIRouteEndpointInContext, "invokeAPIRouteEndpointInContext");
17143
17814
  function normalizeRouteResponse(result) {
@@ -17157,6 +17828,40 @@ function isWebResponse2(value) {
17157
17828
  return value instanceof Response || typeof value === "object" && value !== null && "headers" in value && "status" in value && typeof value.arrayBuffer === "function";
17158
17829
  }
17159
17830
  __name(isWebResponse2, "isWebResponse");
17831
+ function attachEndpointInvalidations(response, keys) {
17832
+ const existing = decodeFarmCacheInvalidations(
17833
+ response.headers.get(FARM_CACHE_INVALIDATION_HEADER)
17834
+ );
17835
+ const encoded = encodeFarmCacheInvalidations([...existing, ...keys]);
17836
+ if (!encoded) return response;
17837
+ const headers = new Headers(response.headers);
17838
+ headers.set(FARM_CACHE_INVALIDATION_HEADER, encoded);
17839
+ return new Response(response.body, {
17840
+ status: response.status,
17841
+ statusText: response.statusText,
17842
+ headers
17843
+ });
17844
+ }
17845
+ __name(attachEndpointInvalidations, "attachEndpointInvalidations");
17846
+ function createEndpointFailureResponse(failure) {
17847
+ return new Response(
17848
+ JSON.stringify({
17849
+ error: {
17850
+ code: failure.code,
17851
+ message: failure.message,
17852
+ data: failure.data
17853
+ }
17854
+ }),
17855
+ {
17856
+ status: failure.status,
17857
+ headers: {
17858
+ "cache-control": "no-store",
17859
+ "content-type": "application/json"
17860
+ }
17861
+ }
17862
+ );
17863
+ }
17864
+ __name(createEndpointFailureResponse, "createEndpointFailureResponse");
17160
17865
  function isFarmContextHandler(endpoint) {
17161
17866
  if (typeof endpoint !== "function") {
17162
17867
  return false;
@@ -17168,17 +17873,50 @@ function isFarmContextHandler(endpoint) {
17168
17873
  return firstParameter === "ctx" || firstParameter === "context" || firstParameter.startsWith("{");
17169
17874
  }
17170
17875
  __name(isFarmContextHandler, "isFarmContextHandler");
17171
- async function readJSONBody(request) {
17876
+ async function readRequestBody(request) {
17877
+ const contentType = request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
17172
17878
  try {
17879
+ if (contentType === "multipart/form-data") {
17880
+ return formDataToObject(await request.clone().formData());
17881
+ }
17173
17882
  const text2 = await request.clone().text();
17174
- if (text2) {
17883
+ if (!text2) return void 0;
17884
+ if (contentType === "application/x-www-form-urlencoded") {
17885
+ return searchParamsToObject2(new URLSearchParams(text2));
17886
+ }
17887
+ if (contentType === "application/json" || contentType?.endsWith("+json")) {
17175
17888
  return JSON.parse(text2);
17176
17889
  }
17890
+ return JSON.parse(text2);
17177
17891
  } catch {
17178
17892
  }
17179
17893
  return void 0;
17180
17894
  }
17181
- __name(readJSONBody, "readJSONBody");
17895
+ __name(readRequestBody, "readRequestBody");
17896
+ function formDataToObject(formData) {
17897
+ return entriesToObject(formData.entries());
17898
+ }
17899
+ __name(formDataToObject, "formDataToObject");
17900
+ function searchParamsToObject2(searchParams) {
17901
+ return entriesToObject(searchParams.entries());
17902
+ }
17903
+ __name(searchParamsToObject2, "searchParamsToObject");
17904
+ function entriesToObject(entries) {
17905
+ const output = /* @__PURE__ */ Object.create(null);
17906
+ for (const [key, value] of entries) {
17907
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
17908
+ const current = output[key];
17909
+ if (current === void 0) {
17910
+ output[key] = value;
17911
+ } else if (Array.isArray(current)) {
17912
+ current.push(value);
17913
+ } else {
17914
+ output[key] = [current, value];
17915
+ }
17916
+ }
17917
+ return output;
17918
+ }
17919
+ __name(entriesToObject, "entriesToObject");
17182
17920
  function validateInput(schema, value, error) {
17183
17921
  if (!schema || typeof schema.parse !== "function") {
17184
17922
  return value;
@@ -18185,7 +18923,7 @@ var _MiddlewareManager = class _MiddlewareManager {
18185
18923
  if (!nestedMatch.matched) {
18186
18924
  return { matched: false };
18187
18925
  }
18188
- const params = { ...nestedMatch.params || {} };
18926
+ const params = { ...nestedMatch.params };
18189
18927
  delete params.__farmRest;
18190
18928
  return {
18191
18929
  matched: true,
@@ -18333,6 +19071,7 @@ async function generateRouteTypes(options) {
18333
19071
  absolute: false
18334
19072
  });
18335
19073
  for (const file of pageFiles) {
19074
+ if (parseRouteSlotFile(file)) continue;
18336
19075
  const route = parseRoutePath(file);
18337
19076
  if (route.type === "page") {
18338
19077
  const pattern = createRoutePattern(route);
@@ -19067,6 +19806,12 @@ function transformError(id, message) {
19067
19806
  }
19068
19807
  __name(transformError, "transformError");
19069
19808
 
19809
+ // package.json
19810
+ var version = "0.1.0-beta.4";
19811
+
19812
+ // src/version.ts
19813
+ var FARM_VERSION = version;
19814
+
19070
19815
  // src/after.ts
19071
19816
  var import_node_async_hooks4 = require("async_hooks");
19072
19817
  var AFTER_STORAGE = /* @__PURE__ */ Symbol.for("@farm.js/core/after-storage");
@@ -19447,7 +20192,7 @@ function isResolvedEnvScope(value) {
19447
20192
  }
19448
20193
  __name(isResolvedEnvScope, "isResolvedEnvScope");
19449
20194
  function isPotentialProgrammaticRouteSourceFile(normalizedFile, srcDirSlug) {
19450
- 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");
20195
+ 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");
19451
20196
  }
19452
20197
  __name(isPotentialProgrammaticRouteSourceFile, "isPotentialProgrammaticRouteSourceFile");
19453
20198
  function fileContainsProgrammaticPageRoute(file) {
@@ -19690,7 +20435,7 @@ function farmPlugin(options = {}, initialPluginManager) {
19690
20435
  }, "isProgrammaticRouteSourceFile");
19691
20436
  const isAppRuntimeFile = /* @__PURE__ */ __name((file) => {
19692
20437
  const normalized = file.replace(/\\/g, "/");
19693
- 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(
20438
+ 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(
19694
20439
  normalized
19695
20440
  );
19696
20441
  }, "isAppRuntimeFile");
@@ -20381,7 +21126,10 @@ function farmPlugin(options = {}, initialPluginManager) {
20381
21126
  method: req.method || "GET"
20382
21127
  });
20383
21128
  }
20384
- const match = routeManager3.matchRoute(targetPath);
21129
+ const interceptFromHeader = request.headers.get("x-farm-intercept-from") || void 0;
21130
+ const match = routeManager3.matchRoute(targetRequestUrl.pathname, {
21131
+ interceptFrom: interceptFromHeader
21132
+ });
20385
21133
  if (pm) {
20386
21134
  await pm.runHookParallel("afterRouteMatch", {
20387
21135
  pathname: targetPath,
@@ -20397,7 +21145,7 @@ function farmPlugin(options = {}, initialPluginManager) {
20397
21145
  res.end(JSON.stringify({ error: "Route not found" }));
20398
21146
  return;
20399
21147
  }
20400
- const { route, params, layouts } = match;
21148
+ const { route, params, layouts, slots } = match;
20401
21149
  if (!route) {
20402
21150
  res.statusCode = 404;
20403
21151
  res.setHeader("Content-Type", "application/json");
@@ -20452,6 +21200,54 @@ function farmPlugin(options = {}, initialPluginManager) {
20452
21200
  search: searchParams,
20453
21201
  routePath: route.pattern
20454
21202
  });
21203
+ const routeSlots = await Promise.all(
21204
+ slots.map(async (slot) => {
21205
+ const slotModule = await routeManager3.loadRouteModule(slot.route.modulePath);
21206
+ const slotMetadata = getClientModuleMetadata(
21207
+ slot.route.modulePath,
21208
+ server.config.root
21209
+ );
21210
+ const slotContext = await resolveFarmRouteContext(farmApp.getConfig(), {
21211
+ request,
21212
+ params: slot.params,
21213
+ search: searchParams,
21214
+ path: targetUrl.pathname
21215
+ });
21216
+ const slotProps = await parseRouteModuleProps2(slotModule, {
21217
+ props: withFarmRouteContext(
21218
+ {
21219
+ params: slot.params,
21220
+ searchParams: Promise.resolve(searchParams),
21221
+ path: targetUrl.pathname
21222
+ },
21223
+ slotContext
21224
+ ),
21225
+ search: searchParams,
21226
+ routePath: slot.route.pattern
21227
+ });
21228
+ return {
21229
+ name: slot.name,
21230
+ ownerPattern: slot.ownerPattern,
21231
+ containerId: slot.containerId,
21232
+ interception: slot.interception,
21233
+ fallback: slot.fallback,
21234
+ modulePath: slot.route.modulePath,
21235
+ isClientComponent: slotMetadata.isClientComponent,
21236
+ shouldHydrate: slotMetadata.shouldHydrate,
21237
+ props: {
21238
+ params: slotProps.params,
21239
+ search: slotProps.search,
21240
+ searchParams: slotProps.search,
21241
+ ..."data" in slotProps ? { data: slotProps.data } : {},
21242
+ ...slotProps.__farmCanonicalPath ? {
21243
+ __farmCanonicalPath: slotProps.__farmCanonicalPath
21244
+ } : {},
21245
+ ...slotProps.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
21246
+ path: targetUrl.pathname
21247
+ }
21248
+ };
21249
+ })
21250
+ );
20455
21251
  const projectRoot = server.config.root;
20456
21252
  const toUrlPath = /* @__PURE__ */ __name((absolutePath) => {
20457
21253
  if (absolutePath.startsWith(projectRoot)) {
@@ -20472,13 +21268,21 @@ function farmPlugin(options = {}, initialPluginManager) {
20472
21268
  },
20473
21269
  canonicalPath: routeProps.__farmCanonicalPath,
20474
21270
  modulePath: toUrlPath(route.modulePath),
20475
- isClientComponent,
20476
- shouldHydrate,
21271
+ isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
21272
+ shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
20477
21273
  metadata: {
20478
21274
  title: mergedMetadata.title,
20479
21275
  description: mergedMetadata.description
20480
21276
  },
20481
21277
  layoutModules: layouts.map((l) => toUrlPath(l.modulePath)),
21278
+ routeSlots: routeSlots.map((slot) => ({
21279
+ ...slot,
21280
+ modulePath: toUrlPath(slot.modulePath)
21281
+ })),
21282
+ interception: routeSlots.some((slot) => slot.interception) ? {
21283
+ from: interceptFromHeader,
21284
+ slots: routeSlots.filter((slot) => slot.interception).map((slot) => slot.name)
21285
+ } : void 0,
20482
21286
  i18n: getFarmI18nClientSnapshot()
20483
21287
  };
20484
21288
  await sendWebResponse(
@@ -20814,6 +21618,7 @@ function farmPlugin(options = {}, initialPluginManager) {
20814
21618
  export const getManifest = () => ({
20815
21619
  routes: {},
20816
21620
  layouts: {},
21621
+ slots: [],
20817
21622
  clientEntry: "/@farm/client.js",
20818
21623
  sharedAssets: []
20819
21624
  });
@@ -20824,6 +21629,7 @@ export const getManifest = () => ({
20824
21629
  clientEntry: "/@farm/client.js",
20825
21630
  routes: {},
20826
21631
  layouts: {},
21632
+ slots: [],
20827
21633
  sharedAssets: [
20828
21634
  {
20829
21635
  tag: "link",
@@ -20853,6 +21659,13 @@ export const getManifest = () => ({
20853
21659
  assets: []
20854
21660
  };
20855
21661
  }
21662
+ for (const slot of manifest.slots) {
21663
+ fullManifest.slots.push({
21664
+ ...slot,
21665
+ preloads: [slot.modulePath],
21666
+ assets: []
21667
+ });
21668
+ }
20856
21669
  return `
20857
21670
  // Auto-generated manifest for SPA navigation (TanStack Start pattern)
20858
21671
  // This manifest is inlined in the server bundle - no file on disk
@@ -21495,7 +22308,7 @@ window.__FARM_WRAP_PROVIDERS__ = wrapWithIntegrationProviders;
21495
22308
 
21496
22309
  // Get manifest from window (inlined by server in HTML)
21497
22310
  // Fallback to empty manifest if not available yet
21498
- const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, clientEntry: '', sharedAssets: [] };
22311
+ const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, slots: [], clientEntry: '', sharedAssets: [] };
21499
22312
 
21500
22313
  // ====== CLIENT-SIDE ROUTE MATCHING ======
21501
22314
  // Matches URL to route using manifest (no server request!)
@@ -21834,6 +22647,9 @@ let LayoutComponent = null;
21834
22647
 
21835
22648
  // Track if we've taken over rendering from SSR
21836
22649
  let hasClientTakenOver = false;
22650
+ const routeSlotRoots = new Map();
22651
+ const routeSlotDefinitions = new Map();
22652
+ let activeRouteInterception = null;
21837
22653
 
21838
22654
  function normalizeServerProps(rawProps) {
21839
22655
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -21846,6 +22662,123 @@ function normalizeServerProps(rawProps) {
21846
22662
  return props;
21847
22663
  }
21848
22664
 
22665
+ function getRouteSlotKey(slot) {
22666
+ return slot.ownerPattern + ':' + slot.name;
22667
+ }
22668
+
22669
+ async function renderRouteSlot(slot, mode = 'render') {
22670
+ const container = document.getElementById(slot.containerId);
22671
+ if (!container || !slot.modulePath) return false;
22672
+ if (mode === 'intercept' && !slot.isClientComponent && !slot.shouldHydrate) {
22673
+ return false;
22674
+ }
22675
+
22676
+ let slotModule = pageModuleCache.get(slot.modulePath);
22677
+ if (!slotModule) {
22678
+ slotModule = await import(/* @vite-ignore */ slot.modulePath);
22679
+ pageModuleCache.set(slot.modulePath, slotModule);
22680
+ }
22681
+ const SlotComponent = slotModule?.default;
22682
+ if (!SlotComponent) return false;
22683
+
22684
+ const props = reviveDeferredData(
22685
+ normalizeServerProps(slot.props || {}),
22686
+ window.__FARM_DEFERRED_DATA__ || {},
22687
+ );
22688
+ const element = wrapWithIntegrationProviders(
22689
+ React.createElement(SlotComponent, props),
22690
+ );
22691
+ const key = getRouteSlotKey(slot);
22692
+ const existingRoot = routeSlotRoots.get(key);
22693
+
22694
+ if (mode === 'hydrate') {
22695
+ if (existingRoot) return true;
22696
+ const root = hydrateRoot(container, element);
22697
+ routeSlotRoots.set(key, root);
22698
+ } else {
22699
+ if (existingRoot) {
22700
+ try { existingRoot.unmount(); } catch (error) {}
22701
+ }
22702
+ const root = createRoot(container);
22703
+ root.render(element);
22704
+ routeSlotRoots.set(key, root);
22705
+ }
22706
+
22707
+ routeSlotDefinitions.set(key, slot);
22708
+ return true;
22709
+ }
22710
+
22711
+ async function hydrateInitialRouteSlots() {
22712
+ const slots = Array.isArray(window.__FARM_ROUTE_SLOTS__)
22713
+ ? window.__FARM_ROUTE_SLOTS__
22714
+ : [];
22715
+ let hydrated = false;
22716
+
22717
+ for (const slot of slots) {
22718
+ routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
22719
+ if (!slot.isClientComponent && !slot.shouldHydrate) continue;
22720
+ try {
22721
+ hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
22722
+ } catch (error) {
22723
+ console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
22724
+ }
22725
+ }
22726
+
22727
+ return hydrated;
22728
+ }
22729
+
22730
+ async function renderRouteInterception(pageData) {
22731
+ const slots = Array.isArray(pageData.routeSlots)
22732
+ ? pageData.routeSlots.filter((slot) => slot.interception)
22733
+ : [];
22734
+ if (!pageData.interception || slots.length === 0) return false;
22735
+
22736
+ const previous = new Map();
22737
+ for (const slot of slots) {
22738
+ const key = getRouteSlotKey(slot);
22739
+ previous.set(key, routeSlotDefinitions.get(key) || null);
22740
+ if (!(await renderRouteSlot(slot, 'intercept'))) {
22741
+ return false;
22742
+ }
22743
+ }
22744
+
22745
+ activeRouteInterception = {
22746
+ from: pageData.interception.from,
22747
+ slots,
22748
+ previous,
22749
+ };
22750
+ return true;
22751
+ }
22752
+
22753
+ async function clearRouteInterception(destination) {
22754
+ if (!activeRouteInterception) return false;
22755
+ const active = activeRouteInterception;
22756
+ activeRouteInterception = null;
22757
+
22758
+ for (const slot of active.slots) {
22759
+ const key = getRouteSlotKey(slot);
22760
+ const root = routeSlotRoots.get(key);
22761
+ if (root) {
22762
+ try { root.unmount(); } catch (error) {}
22763
+ routeSlotRoots.delete(key);
22764
+ }
22765
+
22766
+ const previous = active.previous.get(key);
22767
+ if (previous && (previous.isClientComponent || previous.shouldHydrate)) {
22768
+ await renderRouteSlot(previous, 'render');
22769
+ } else {
22770
+ const container = document.getElementById(slot.containerId);
22771
+ if (container) container.replaceChildren();
22772
+ if (previous) routeSlotDefinitions.set(key, previous);
22773
+ else routeSlotDefinitions.delete(key);
22774
+ }
22775
+ }
22776
+
22777
+ if (!active.from) return false;
22778
+ const background = new URL(active.from, window.location.origin);
22779
+ return destination === background.pathname + background.search;
22780
+ }
22781
+
21849
22782
  function applyCanonicalPathFromProps(props) {
21850
22783
  const canonicalPath = props && typeof props.__farmCanonicalPath === 'string'
21851
22784
  ? props.__farmCanonicalPath
@@ -22080,6 +23013,17 @@ async function tryHydrateImportedPage(
22080
23013
  async function renderPage(pageData) {
22081
23014
  const container = document.getElementById('root');
22082
23015
  if (!container) return;
23016
+ const destination = window.location.pathname + window.location.search;
23017
+
23018
+ if (pageData.interception) {
23019
+ if (await renderRouteInterception(pageData)) return;
23020
+ window.location.assign(destination);
23021
+ return;
23022
+ }
23023
+
23024
+ if (await clearRouteInterception(destination)) {
23025
+ return;
23026
+ }
22083
23027
 
22084
23028
  const route = {
22085
23029
  modulePath: pageData.modulePath,
@@ -22308,7 +23252,9 @@ async function hydrate() {
22308
23252
  window.__FARM_SHOULD_HYDRATE__ === true ||
22309
23253
  isClientComponent ||
22310
23254
  await moduleLooksClient(modulePath);
23255
+ const hydratedSlots = await hydrateInitialRouteSlots();
22311
23256
  if (!shouldHydrate) {
23257
+ if (hydratedSlots) replayPreHydrationClicks();
22312
23258
  console.log('[Farm.js] Server component - SPA router ready')
22313
23259
  return
22314
23260
  }
@@ -22573,7 +23519,7 @@ async function defineConfig2(config = {}) {
22573
23519
  const isExposed = hostConfig === true || hostConfig === "0.0.0.0";
22574
23520
  console.log("");
22575
23521
  console.log(
22576
- ` ${pc.bold(pc.green("Farm.js"))} ${pc.dim("v1.0.0")} ${pc.dim(`ready in ${elapsed}ms`)}`
23522
+ ` ${pc.bold(pc.green("Farm.js"))} ${pc.dim(`v${FARM_VERSION}`)} ${pc.dim(`ready in ${elapsed}ms`)}`
22577
23523
  );
22578
23524
  console.log("");
22579
23525
  console.log(