@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
@@ -2946,7 +2946,11 @@ async function prepareFarmWorkflowsForNitro(config) {
2946
2946
  };
2947
2947
  }
2948
2948
  const handlerPath = path27.join(generatedDir, "http-handler.mjs");
2949
- await fs8.writeFile(handlerPath, createNitroWorkflowHTTPHandler(workflowConfig, workflows), "utf8");
2949
+ await fs8.writeFile(
2950
+ handlerPath,
2951
+ createNitroWorkflowHTTPHandler(workflowConfig, workflows),
2952
+ "utf8"
2953
+ );
2950
2954
  const manifestPath = path27.join(generatedDir, "manifest.json");
2951
2955
  await fs8.writeFile(
2952
2956
  manifestPath,
@@ -3020,13 +3024,7 @@ function normalizeSchedule(schedule) {
3020
3024
  return (Array.isArray(schedule) ? schedule : [schedule]).map((value) => value.trim()).filter(Boolean);
3021
3025
  }
3022
3026
  function resolveWorkflowDefinition(module2) {
3023
- const candidates = [
3024
- module2.default,
3025
- module2.workflow,
3026
- module2.cron,
3027
- module2.task,
3028
- module2.job
3029
- ];
3027
+ const candidates = [module2.default, module2.workflow, module2.cron, module2.task, module2.job];
3030
3028
  for (const candidate of candidates) {
3031
3029
  if (candidate && typeof candidate === "object" && typeof candidate.run === "function") {
3032
3030
  return candidate;
@@ -3892,7 +3890,7 @@ function createFarmMarkdownRouteModule(input) {
3892
3890
  ...props,
3893
3891
  components: {
3894
3892
  ...components,
3895
- ...props?.components || {}
3893
+ ...props?.components
3896
3894
  }
3897
3895
  })
3898
3896
  );
@@ -4082,8 +4080,8 @@ function compareFarmRouteRuleSpecificity(left, right) {
4082
4080
  }
4083
4081
  function getFarmRouteRuleSpecificity(pattern) {
4084
4082
  return normalizeRoutePattern(pattern).split("/").filter(Boolean).reduce((score, segment) => {
4085
- if (segment === "**" || /^\[\[\.\.\./.test(segment)) return score + 1;
4086
- if (segment === "*" || /^\[\.\.\./.test(segment)) return score + 10;
4083
+ if (segment === "**" || segment.startsWith("[[...")) return score + 1;
4084
+ if (segment === "*" || segment.startsWith("[...")) return score + 10;
4087
4085
  if (/^\[.+\]$/.test(segment) || /^:.+$/.test(segment)) return score + 50;
4088
4086
  return score + 100;
4089
4087
  }, 0);
@@ -4145,7 +4143,7 @@ function routeRulesToHeaders(routeRules) {
4145
4143
  for (const [source, rule] of Object.entries(routeRules)) {
4146
4144
  const headers = {
4147
4145
  ...normalizeCorsHeaders(rule.cors),
4148
- ...rule.headers || {}
4146
+ ...rule.headers
4149
4147
  };
4150
4148
  const entries = Object.entries(headers);
4151
4149
  if (entries.length === 0) continue;
@@ -4175,7 +4173,7 @@ function routeRulesToNitroRouteRules(routeRules) {
4175
4173
  nitroRule.cors = true;
4176
4174
  nitroRule.headers = {
4177
4175
  ...normalizeCorsHeaders(rule.cors),
4178
- ...rule.headers || {}
4176
+ ...rule.headers
4179
4177
  };
4180
4178
  }
4181
4179
  delete nitroRule.render;
@@ -4227,22 +4225,50 @@ var init_route_rules = __esm({
4227
4225
  // src/cache-invalidation.ts
4228
4226
  function getFarmCacheInvalidationState() {
4229
4227
  return globalState[FARM_CACHE_INVALIDATION_STATE] ?? (globalState[FARM_CACHE_INVALIDATION_STATE] = {
4230
- listeners: /* @__PURE__ */ new Set()
4228
+ listeners: /* @__PURE__ */ new Set(),
4229
+ taskListeners: /* @__PURE__ */ new Set()
4231
4230
  });
4232
4231
  }
4232
+ function encodeFarmCacheInvalidations(keys) {
4233
+ const normalized = Array.from(
4234
+ new Set(keys.filter((key) => typeof key === "string" && key.length > 0))
4235
+ );
4236
+ if (normalized.length === 0) return null;
4237
+ return encodeURIComponent(JSON.stringify(normalized));
4238
+ }
4239
+ function decodeFarmCacheInvalidations(value) {
4240
+ if (!value) return [];
4241
+ try {
4242
+ const parsed = JSON.parse(decodeURIComponent(value));
4243
+ return Array.isArray(parsed) ? Array.from(
4244
+ new Set(parsed.filter((key) => typeof key === "string" && key.length > 0))
4245
+ ) : [];
4246
+ } catch {
4247
+ return [];
4248
+ }
4249
+ }
4233
4250
  function subscribeFarmCacheInvalidation(listener) {
4234
4251
  const state = getFarmCacheInvalidationState();
4235
4252
  state.listeners.add(listener);
4236
4253
  return () => state.listeners.delete(listener);
4237
4254
  }
4238
- var FARM_CACHE_INVALIDATION_STATE, globalState;
4255
+ function subscribeFarmCacheTask(listener) {
4256
+ const state = getFarmCacheInvalidationState();
4257
+ state.taskListeners.add(listener);
4258
+ return () => state.taskListeners.delete(listener);
4259
+ }
4260
+ var FARM_CACHE_INVALIDATION_HEADER, FARM_CACHE_INVALIDATION_STATE, globalState;
4239
4261
  var init_cache_invalidation = __esm({
4240
4262
  "src/cache-invalidation.ts"() {
4241
4263
  "use strict";
4264
+ FARM_CACHE_INVALIDATION_HEADER = "x-farm-cache-invalidations";
4242
4265
  FARM_CACHE_INVALIDATION_STATE = /* @__PURE__ */ Symbol.for("farm.cacheInvalidationState");
4243
4266
  globalState = globalThis;
4244
4267
  __name(getFarmCacheInvalidationState, "getFarmCacheInvalidationState");
4268
+ __name(encodeFarmCacheInvalidations, "encodeFarmCacheInvalidations");
4269
+ __name(decodeFarmCacheInvalidations, "decodeFarmCacheInvalidations");
4245
4270
  __name(subscribeFarmCacheInvalidation, "subscribeFarmCacheInvalidation");
4271
+ __name(subscribeFarmCacheTask, "subscribeFarmCacheTask");
4246
4272
  }
4247
4273
  });
4248
4274
 
@@ -4346,6 +4372,9 @@ var init_server_action_security = __esm({
4346
4372
  subscribeFarmCacheInvalidation((key) => {
4347
4373
  getServerActionExecutionContext()?.invalidations.add(key);
4348
4374
  });
4375
+ subscribeFarmCacheTask((task) => {
4376
+ getServerActionExecutionContext()?.cacheTasks.add(task);
4377
+ });
4349
4378
  __name(getServerActionStorage, "getServerActionStorage");
4350
4379
  __name(parseBodySizeLimit, "parseBodySizeLimit");
4351
4380
  __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
@@ -5503,6 +5532,7 @@ async function resolveConfig(userConfig, mode) {
5503
5532
  observability: userConfig.observability ?? false,
5504
5533
  devtools: resolveFarmDevtoolsConfig(userConfig.devtools, mode),
5505
5534
  storage: userConfig.storage || {},
5535
+ cache: userConfig.cache || {},
5506
5536
  suppressLintOnLink: userConfig.suppressLintOnLink ?? false,
5507
5537
  experimental: {
5508
5538
  serverComponents: false,
@@ -6297,6 +6327,9 @@ var init_bridge = __esm({
6297
6327
  function getFarmDataCache() {
6298
6328
  return sharedFarmDataCache;
6299
6329
  }
6330
+ function configureFarmCache(config) {
6331
+ sharedFarmDataCache.configure(config);
6332
+ }
6300
6333
  function createPathCacheTag(routePath) {
6301
6334
  return `path:${normalizeRevalidatePath(routePath)}`;
6302
6335
  }
@@ -6350,6 +6383,46 @@ function normalizeCacheTag(tag) {
6350
6383
  }
6351
6384
  return normalized;
6352
6385
  }
6386
+ function normalizeCacheNamespace(namespace) {
6387
+ if (typeof namespace !== "string") {
6388
+ throw new TypeError("Cache namespace must be a string.");
6389
+ }
6390
+ const normalized = namespace.trim().replace(/:+$/g, "");
6391
+ if (!normalized) {
6392
+ throw new Error("Cache namespace cannot be empty.");
6393
+ }
6394
+ return normalized;
6395
+ }
6396
+ function normalizeAdapterVersion(value) {
6397
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
6398
+ }
6399
+ function normalizePositiveDuration(value, fallback, name) {
6400
+ if (value === void 0) return fallback;
6401
+ if (!Number.isFinite(value) || value <= 0) {
6402
+ throw new TypeError(`${name} must be a positive number of milliseconds.`);
6403
+ }
6404
+ return Math.floor(value);
6405
+ }
6406
+ function delay(ms) {
6407
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
6408
+ }
6409
+ function normalizeCacheOptionsTags(options) {
6410
+ const tags = /* @__PURE__ */ new Set();
6411
+ for (const tag of options.tags ?? []) {
6412
+ tags.add(normalizeCacheTag(tag));
6413
+ }
6414
+ for (const routePath of options.paths ?? []) {
6415
+ tags.add(createPathCacheTag(routePath));
6416
+ }
6417
+ return tags;
6418
+ }
6419
+ function assertFarmCacheEntry(entry, expectedKey) {
6420
+ if (!entry || typeof entry !== "object" || entry.key !== expectedKey || !Array.isArray(entry.tags) || typeof entry.createdAt !== "number") {
6421
+ throw new TypeError(
6422
+ `Cache adapter returned an invalid entry for ${JSON.stringify(expectedKey)}.`
6423
+ );
6424
+ }
6425
+ }
6353
6426
  function getFunctionCacheIdentity(fn) {
6354
6427
  if (fn.name) {
6355
6428
  return `name:${fn.name}`;
@@ -6410,11 +6483,51 @@ var init_cache = __esm({
6410
6483
  init_bridge();
6411
6484
  init_cache_invalidation();
6412
6485
  _FarmDataCache = class _FarmDataCache {
6413
- constructor() {
6486
+ constructor(config = {}) {
6414
6487
  this.entries = /* @__PURE__ */ new Map();
6415
6488
  this.inflight = /* @__PURE__ */ new Map();
6416
6489
  this.invalidatedTagVersions = /* @__PURE__ */ new Map();
6417
6490
  this.version = 0;
6491
+ this.namespace = "farm";
6492
+ this.local = true;
6493
+ this.lease = {
6494
+ enabled: true,
6495
+ ttlMs: 1e4,
6496
+ waitTimeoutMs: 1e4,
6497
+ pollIntervalMs: 25
6498
+ };
6499
+ this.configure(config);
6500
+ }
6501
+ configure(config = {}) {
6502
+ this.adapter = config.adapter;
6503
+ this.namespace = normalizeCacheNamespace(config.namespace || "farm");
6504
+ this.local = !config.adapter;
6505
+ this.lease = config.lease === false ? { ...this.lease, enabled: false } : {
6506
+ enabled: true,
6507
+ ttlMs: normalizePositiveDuration(config.lease?.ttlMs, 1e4, "cache.lease.ttlMs"),
6508
+ waitTimeoutMs: normalizePositiveDuration(
6509
+ config.lease?.waitTimeoutMs,
6510
+ 1e4,
6511
+ "cache.lease.waitTimeoutMs"
6512
+ ),
6513
+ pollIntervalMs: normalizePositiveDuration(
6514
+ config.lease?.pollIntervalMs,
6515
+ 25,
6516
+ "cache.lease.pollIntervalMs"
6517
+ )
6518
+ };
6519
+ if (!this.local) {
6520
+ this.entries.clear();
6521
+ this.inflight.clear();
6522
+ this.invalidatedTagVersions.clear();
6523
+ this.version = 0;
6524
+ }
6525
+ }
6526
+ get adapterName() {
6527
+ return this.adapter?.name || (this.adapter ? "custom" : "memory");
6528
+ }
6529
+ get hasAdapter() {
6530
+ return this.adapter !== void 0;
6418
6531
  }
6419
6532
  get size() {
6420
6533
  return this.entries.size;
@@ -6450,6 +6563,45 @@ var init_cache = __esm({
6450
6563
  });
6451
6564
  return this.toPublicEntry(entry);
6452
6565
  }
6566
+ async getEntryAsync(key, options = {}) {
6567
+ if (this.local) {
6568
+ const localEntry = this.getEntry(key, options);
6569
+ if (localEntry) return localEntry;
6570
+ }
6571
+ if (!this.adapter) {
6572
+ return this.local ? void 0 : this.getEntry(key, options);
6573
+ }
6574
+ const entry = await this.adapter.get(this.createAdapterKey(key));
6575
+ if (!entry) {
6576
+ emitFarmEvent({ type: "cache.miss", key });
6577
+ return void 0;
6578
+ }
6579
+ assertFarmCacheEntry(entry, key);
6580
+ const stale = await this.isAdapterEntryStale(entry, options.now);
6581
+ if (stale) {
6582
+ emitFarmEvent({
6583
+ type: "cache.stale",
6584
+ key,
6585
+ tags: [...entry.tags],
6586
+ revalidate: entry.revalidate
6587
+ });
6588
+ if (!options.allowStale) {
6589
+ emitFarmEvent({ type: "cache.miss", key, reason: "stale" });
6590
+ return void 0;
6591
+ }
6592
+ }
6593
+ emitFarmEvent({
6594
+ type: "cache.hit",
6595
+ key,
6596
+ tags: [...entry.tags],
6597
+ revalidate: entry.revalidate,
6598
+ stale
6599
+ });
6600
+ if (this.local && !stale) {
6601
+ this.hydrateLocalEntry(entry);
6602
+ }
6603
+ return { ...entry, key };
6604
+ }
6453
6605
  set(key, value, options = {}) {
6454
6606
  const tags = /* @__PURE__ */ new Set();
6455
6607
  for (const tag of options.tags ?? []) {
@@ -6462,6 +6614,7 @@ var init_cache = __esm({
6462
6614
  key,
6463
6615
  value,
6464
6616
  tags,
6617
+ tagVersions: void 0,
6465
6618
  createdAt: options.createdAt ?? Date.now(),
6466
6619
  createdVersion: ++this.version,
6467
6620
  revalidate: normalizeRevalidate(options.revalidate)
@@ -6475,11 +6628,45 @@ var init_cache = __esm({
6475
6628
  });
6476
6629
  return this.toPublicEntry(entry);
6477
6630
  }
6631
+ async setAsync(key, value, options = {}, tagVersions) {
6632
+ const tags = normalizeCacheOptionsTags(options);
6633
+ const capturedVersions = tagVersions ?? await this.getAdapterTagVersions(Array.from(tags.values()));
6634
+ const entry = {
6635
+ key,
6636
+ value,
6637
+ tags: Array.from(tags),
6638
+ tagVersions: capturedVersions,
6639
+ createdAt: options.createdAt ?? Date.now(),
6640
+ createdVersion: ++this.version,
6641
+ revalidate: normalizeRevalidate(options.revalidate)
6642
+ };
6643
+ if (this.local) {
6644
+ this.hydrateLocalEntry(entry);
6645
+ }
6646
+ if (this.adapter) {
6647
+ await this.adapter.set(this.createAdapterKey(key), entry);
6648
+ }
6649
+ emitFarmEvent({
6650
+ type: "cache.set",
6651
+ key,
6652
+ tags: [...entry.tags],
6653
+ revalidate: entry.revalidate
6654
+ });
6655
+ return entry;
6656
+ }
6478
6657
  delete(key) {
6479
6658
  const deleted = this.entries.delete(key);
6480
6659
  emitFarmEvent({ type: "cache.delete", key, deleted });
6481
6660
  return deleted;
6482
6661
  }
6662
+ async deleteAsync(key) {
6663
+ const deleted = this.entries.delete(key);
6664
+ if (this.adapter) {
6665
+ await this.adapter.delete(this.createAdapterKey(key));
6666
+ }
6667
+ emitFarmEvent({ type: "cache.delete", key, deleted: this.adapter ? true : deleted });
6668
+ return this.adapter ? true : deleted;
6669
+ }
6483
6670
  clear() {
6484
6671
  const count = this.entries.size;
6485
6672
  this.entries.clear();
@@ -6488,6 +6675,10 @@ var init_cache = __esm({
6488
6675
  this.version = 0;
6489
6676
  emitFarmEvent({ type: "cache.clear", count });
6490
6677
  }
6678
+ async clearAsync() {
6679
+ this.clear();
6680
+ await this.adapter?.clear?.();
6681
+ }
6491
6682
  isStale(entry, now = Date.now()) {
6492
6683
  if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
6493
6684
  return true;
@@ -6500,6 +6691,12 @@ var init_cache = __esm({
6500
6691
  }
6501
6692
  return false;
6502
6693
  }
6694
+ async isStaleAsync(entry, now = Date.now()) {
6695
+ if (this.adapter) {
6696
+ return this.isAdapterEntryStale(entry, now);
6697
+ }
6698
+ return this.isStale(entry, now);
6699
+ }
6503
6700
  revalidateTag(tag, options = {}) {
6504
6701
  const normalized = normalizeCacheTag(tag);
6505
6702
  const count = this.invalidateTag(normalized);
@@ -6508,6 +6705,15 @@ var init_cache = __esm({
6508
6705
  );
6509
6706
  return count;
6510
6707
  }
6708
+ async revalidateTagAsync(tag, options = {}) {
6709
+ const normalized = normalizeCacheTag(tag);
6710
+ const count = this.invalidateTag(normalized);
6711
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(normalized)]);
6712
+ emitFarmEvent(
6713
+ options.source === "updateTag" ? { type: "cache.updateTag", tag: normalized, count } : { type: "cache.revalidateTag", tag: normalized, profile: options.profile, count }
6714
+ );
6715
+ return count;
6716
+ }
6511
6717
  revalidatePath(routePath) {
6512
6718
  const normalizedPath = normalizeRevalidatePath(routePath);
6513
6719
  const pathTag = createPathCacheTag(normalizedPath);
@@ -6524,8 +6730,25 @@ var init_cache = __esm({
6524
6730
  }
6525
6731
  return count;
6526
6732
  }
6733
+ async revalidatePathAsync(routePath) {
6734
+ const normalizedPath = normalizeRevalidatePath(routePath);
6735
+ const pathTag = createPathCacheTag(normalizedPath);
6736
+ const pprCount = this.countEntriesForTags([pathTag, "ppr"]);
6737
+ const count = this.invalidateTag(pathTag);
6738
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(pathTag)]);
6739
+ emitFarmEvent({ type: "cache.revalidatePath", path: normalizedPath, count });
6740
+ if (pprCount > 0) {
6741
+ emitFarmEvent({
6742
+ type: "ppr.shell.invalidated",
6743
+ route: normalizedPath,
6744
+ reason: "revalidatePath",
6745
+ count: pprCount
6746
+ });
6747
+ }
6748
+ return count;
6749
+ }
6527
6750
  async getOrSet(key, producer, options = {}) {
6528
- const cached = this.getEntry(key);
6751
+ const cached = await this.getEntryAsync(key);
6529
6752
  if (cached) {
6530
6753
  return cached.value;
6531
6754
  }
@@ -6534,10 +6757,8 @@ var init_cache = __esm({
6534
6757
  emitFarmEvent({ type: "cache.dedupe", key });
6535
6758
  return inflight;
6536
6759
  }
6537
- const promise = Promise.resolve().then(producer).then((value) => {
6538
- this.set(key, value, options);
6539
- return value;
6540
- }).catch((error) => {
6760
+ const tags = Array.from(normalizeCacheOptionsTags(options));
6761
+ const promise = this.fillCacheEntry(key, producer, options, tags).catch((error) => {
6541
6762
  emitFarmEvent({ type: "cache.error", key, operation: "set", error });
6542
6763
  throw error;
6543
6764
  }).finally(() => {
@@ -6546,6 +6767,40 @@ var init_cache = __esm({
6546
6767
  this.inflight.set(key, promise);
6547
6768
  return promise;
6548
6769
  }
6770
+ async fillCacheEntry(key, producer, options, tags) {
6771
+ const leaseKey = `${this.namespace}:lease:${key}`;
6772
+ let leaseToken;
6773
+ if (this.adapter?.acquireLease && this.adapter.releaseLease && this.lease.enabled) {
6774
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
6775
+ if (!leaseToken) {
6776
+ const shared = await this.waitForAdapterEntry(key);
6777
+ if (shared) {
6778
+ emitFarmEvent({ type: "cache.dedupe", key });
6779
+ return shared.value;
6780
+ }
6781
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
6782
+ }
6783
+ }
6784
+ try {
6785
+ const initialTagVersions = await this.getAdapterTagVersions(tags);
6786
+ const value = await producer();
6787
+ await this.setAsync(key, value, options, initialTagVersions);
6788
+ return value;
6789
+ } finally {
6790
+ if (leaseToken && this.adapter?.releaseLease) {
6791
+ await this.adapter.releaseLease(leaseKey, leaseToken);
6792
+ }
6793
+ }
6794
+ }
6795
+ async waitForAdapterEntry(key) {
6796
+ const deadline = Date.now() + this.lease.waitTimeoutMs;
6797
+ while (Date.now() < deadline) {
6798
+ await delay(this.lease.pollIntervalMs);
6799
+ const entry = await this.getEntryAsync(key);
6800
+ if (entry) return entry;
6801
+ }
6802
+ return void 0;
6803
+ }
6549
6804
  countEntriesForTag(tag) {
6550
6805
  let count = 0;
6551
6806
  for (const entry of this.entries.values()) {
@@ -6573,11 +6828,53 @@ var init_cache = __esm({
6573
6828
  key: entry.key,
6574
6829
  value: entry.value,
6575
6830
  tags: Array.from(entry.tags),
6831
+ tagVersions: entry.tagVersions,
6576
6832
  createdAt: entry.createdAt,
6577
6833
  createdVersion: entry.createdVersion,
6578
6834
  revalidate: entry.revalidate
6579
6835
  };
6580
6836
  }
6837
+ hydrateLocalEntry(entry) {
6838
+ this.entries.set(entry.key, {
6839
+ key: entry.key,
6840
+ value: entry.value,
6841
+ tags: new Set(entry.tags.map(normalizeCacheTag)),
6842
+ tagVersions: entry.tagVersions,
6843
+ createdAt: entry.createdAt,
6844
+ createdVersion: entry.createdVersion ?? ++this.version,
6845
+ revalidate: normalizeRevalidate(entry.revalidate)
6846
+ });
6847
+ }
6848
+ createAdapterKey(key) {
6849
+ return `${this.namespace}:entry:${key}`;
6850
+ }
6851
+ createAdapterTag(tag) {
6852
+ return `${this.namespace}:tag:${tag}`;
6853
+ }
6854
+ async getAdapterTagVersions(tags) {
6855
+ if (!this.adapter?.getTagVersions || tags.length === 0) return {};
6856
+ const normalized = tags.map(normalizeCacheTag);
6857
+ const physicalTags = normalized.map((tag) => this.createAdapterTag(tag));
6858
+ const versions = await this.adapter.getTagVersions(physicalTags);
6859
+ return Object.fromEntries(
6860
+ normalized.map((tag, index) => [
6861
+ tag,
6862
+ normalizeAdapterVersion(versions[physicalTags[index]])
6863
+ ])
6864
+ );
6865
+ }
6866
+ async isAdapterEntryStale(entry, now = Date.now()) {
6867
+ if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
6868
+ return true;
6869
+ }
6870
+ const currentVersions = await this.getAdapterTagVersions(entry.tags);
6871
+ for (const tag of entry.tags) {
6872
+ if (normalizeAdapterVersion(currentVersions[tag]) > normalizeAdapterVersion(entry.tagVersions?.[tag])) {
6873
+ return true;
6874
+ }
6875
+ }
6876
+ return false;
6877
+ }
6581
6878
  };
6582
6879
  __name(_FarmDataCache, "FarmDataCache");
6583
6880
  FarmDataCache = _FarmDataCache;
@@ -6587,6 +6884,7 @@ var init_cache = __esm({
6587
6884
  functionIds = /* @__PURE__ */ new WeakMap();
6588
6885
  nextFunctionId = 0;
6589
6886
  __name(getFarmDataCache, "getFarmDataCache");
6887
+ __name(configureFarmCache, "configureFarmCache");
6590
6888
  __name(createPathCacheTag, "createPathCacheTag");
6591
6889
  __name(createRouteDataCacheTag, "createRouteDataCacheTag");
6592
6890
  __name(createRouteDataCacheKey, "createRouteDataCacheKey");
@@ -6594,6 +6892,12 @@ var init_cache = __esm({
6594
6892
  __name(createFarmCacheKey, "createFarmCacheKey");
6595
6893
  __name(normalizeRevalidate, "normalizeRevalidate");
6596
6894
  __name(normalizeCacheTag, "normalizeCacheTag");
6895
+ __name(normalizeCacheNamespace, "normalizeCacheNamespace");
6896
+ __name(normalizeAdapterVersion, "normalizeAdapterVersion");
6897
+ __name(normalizePositiveDuration, "normalizePositiveDuration");
6898
+ __name(delay, "delay");
6899
+ __name(normalizeCacheOptionsTags, "normalizeCacheOptionsTags");
6900
+ __name(assertFarmCacheEntry, "assertFarmCacheEntry");
6597
6901
  __name(getFunctionCacheIdentity, "getFunctionCacheIdentity");
6598
6902
  __name(stableSerialize, "stableSerialize");
6599
6903
  }
@@ -7715,6 +8019,83 @@ var init_routing = __esm({
7715
8019
  }
7716
8020
  });
7717
8021
 
8022
+ // src/routing/route-slots.ts
8023
+ function parseRouteSlotFile(filePath) {
8024
+ const normalized = filePath.replace(/\\/g, "/");
8025
+ const parts = normalized.split("/").filter(Boolean);
8026
+ const fileName = parts.at(-1) ?? "";
8027
+ const baseName = fileName.replace(/\.(tsx?|jsx?)$/, "");
8028
+ if (baseName !== "page" && baseName !== "default") return null;
8029
+ const slotIndex = parts.findIndex((part) => SLOT_SEGMENT.test(part));
8030
+ if (slotIndex < 0) return null;
8031
+ if (parts.slice(slotIndex + 1, -1).some((part) => SLOT_SEGMENT.test(part))) {
8032
+ throw new Error(`Nested route slots are not supported in "${filePath}"`);
8033
+ }
8034
+ const slotMatch = parts[slotIndex].match(SLOT_SEGMENT);
8035
+ const name = slotMatch?.[1];
8036
+ if (!name) return null;
8037
+ const ownerParts = parts.slice(0, slotIndex);
8038
+ const contentParts = parts.slice(slotIndex + 1, -1);
8039
+ const marker = parseInterceptionMarker(contentParts[0]);
8040
+ let targetBase = ownerParts;
8041
+ let targetParts = contentParts;
8042
+ if (marker) {
8043
+ targetBase = marker.root === true ? [] : ownerParts.slice(0, Math.max(0, ownerParts.length - marker.up));
8044
+ targetParts = [...marker.remainder ? [marker.remainder] : [], ...contentParts.slice(1)];
8045
+ }
8046
+ const ownerFile = [...ownerParts, "layout.tsx"].join("/");
8047
+ const routeFile = [
8048
+ ...baseName === "default" ? ownerParts : targetBase,
8049
+ ...baseName === "default" ? [] : targetParts,
8050
+ "page.tsx"
8051
+ ].join("/");
8052
+ return {
8053
+ name,
8054
+ ownerRoute: parseRoutePath(ownerFile || "layout.tsx"),
8055
+ route: parseRoutePath(routeFile || "page.tsx"),
8056
+ interception: marker !== null,
8057
+ fallback: baseName === "default"
8058
+ };
8059
+ }
8060
+ function createRouteSlotContainerId(name, ownerPattern) {
8061
+ const owner = ownerPattern === "/" ? "root" : ownerPattern.replace(/^\//, "").replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
8062
+ return `__farm_slot_${name}_${owner || "root"}__`;
8063
+ }
8064
+ function parseInterceptionMarker(segment) {
8065
+ if (!segment) return null;
8066
+ if (segment.startsWith("(...)")) {
8067
+ return {
8068
+ up: 0,
8069
+ root: true,
8070
+ remainder: segment.slice(5)
8071
+ };
8072
+ }
8073
+ if (segment.startsWith("(.)")) {
8074
+ return {
8075
+ up: 0,
8076
+ remainder: segment.slice(3)
8077
+ };
8078
+ }
8079
+ let remainder = segment;
8080
+ let up = 0;
8081
+ while (remainder.startsWith("(..)")) {
8082
+ up += 1;
8083
+ remainder = remainder.slice(4);
8084
+ }
8085
+ return up > 0 ? { up, remainder } : null;
8086
+ }
8087
+ var SLOT_SEGMENT;
8088
+ var init_route_slots = __esm({
8089
+ "src/routing/route-slots.ts"() {
8090
+ "use strict";
8091
+ init_utils();
8092
+ SLOT_SEGMENT = /^@([A-Za-z][A-Za-z0-9_-]*)$/;
8093
+ __name(parseRouteSlotFile, "parseRouteSlotFile");
8094
+ __name(createRouteSlotContainerId, "createRouteSlotContainerId");
8095
+ __name(parseInterceptionMarker, "parseInterceptionMarker");
8096
+ }
8097
+ });
8098
+
7718
8099
  // src/routing/route-manager.ts
7719
8100
  function routeSpecificity(entry) {
7720
8101
  return entry.route.segments.reduce((score, segment) => {
@@ -7786,11 +8167,13 @@ var init_route_manager = __esm({
7786
8167
  init_static_metadata_image();
7787
8168
  init_route_runtime();
7788
8169
  init_routing();
8170
+ init_route_slots();
7789
8171
  __name(routeSpecificity, "routeSpecificity");
7790
8172
  _RouteManager = class _RouteManager {
7791
8173
  constructor(config, viteServer) {
7792
8174
  this.routes = /* @__PURE__ */ new Map();
7793
8175
  this.layouts = /* @__PURE__ */ new Map();
8176
+ this.routeSlots = /* @__PURE__ */ new Map();
7794
8177
  this.loadings = /* @__PURE__ */ new Map();
7795
8178
  this.errors = /* @__PURE__ */ new Map();
7796
8179
  this.metadataImages = /* @__PURE__ */ new Map();
@@ -7806,6 +8189,7 @@ var init_route_manager = __esm({
7806
8189
  async discoverRoutes() {
7807
8190
  this.routes.clear();
7808
8191
  this.layouts.clear();
8192
+ this.routeSlots.clear();
7809
8193
  this.loadings.clear();
7810
8194
  this.errors.clear();
7811
8195
  this.metadataImages.clear();
@@ -7816,6 +8200,13 @@ var init_route_manager = __esm({
7816
8200
  await this.discoverFileRoutes(source);
7817
8201
  await this.discoverProgrammaticRoutes(source);
7818
8202
  }
8203
+ for (const slot of this.routeSlots.values()) {
8204
+ if (slot.interception && !this.routes.has(slot.pattern)) {
8205
+ throw new Error(
8206
+ `Intercepting route slot "${slot.name}" targets "${slot.pattern}", but no canonical page exists for that URL`
8207
+ );
8208
+ }
8209
+ }
7819
8210
  this.routes = new Map(
7820
8211
  Array.from(this.routes.entries()).sort(
7821
8212
  ([, left], [, right]) => routeSpecificity(right) - routeSpecificity(left)
@@ -7831,7 +8222,7 @@ var init_route_manager = __esm({
7831
8222
  /**
7832
8223
  * Find matching route for a given URL path
7833
8224
  */
7834
- matchRoute(pathname) {
8225
+ matchRoute(pathname, options = {}) {
7835
8226
  const routePathname = this.toRoutePathname(pathname);
7836
8227
  const normalizedPath = routePathname === "/" ? "/" : routePathname.replace(/\/$/, "");
7837
8228
  let matchedRoute = null;
@@ -7845,10 +8236,12 @@ var init_route_manager = __esm({
7845
8236
  }
7846
8237
  }
7847
8238
  const layouts = this.findMatchingLayouts(normalizedPath);
8239
+ const slots = this.findMatchingRouteSlots(normalizedPath, options.interceptFrom);
7848
8240
  return {
7849
8241
  route: matchedRoute,
7850
8242
  params,
7851
- layouts
8243
+ layouts,
8244
+ slots
7852
8245
  };
7853
8246
  }
7854
8247
  /**
@@ -7863,6 +8256,9 @@ var init_route_manager = __esm({
7863
8256
  getLayouts() {
7864
8257
  return new Map(this.layouts);
7865
8258
  }
8259
+ getRouteSlots() {
8260
+ return new Map(this.routeSlots);
8261
+ }
7866
8262
  /** Resolve route rules, inherited layouts, and the page export in precedence order. */
7867
8263
  async resolveRouteRuntimeConfig(pattern) {
7868
8264
  const routeEntry = this.routes.get(pattern);
@@ -8021,7 +8417,22 @@ var init_route_manager = __esm({
8021
8417
  pattern: entry.pattern,
8022
8418
  modulePath: toUrlPath(entry.modulePath)
8023
8419
  }));
8024
- return { routes, layouts };
8420
+ const slots = Array.from(this.routeSlots.values()).map((entry) => {
8421
+ const metadata = getClientModuleMetadata(entry.modulePath, projectRoot);
8422
+ return {
8423
+ name: entry.name,
8424
+ ownerPattern: entry.ownerPattern,
8425
+ pattern: entry.pattern,
8426
+ modulePath: toUrlPath(entry.modulePath),
8427
+ containerId: entry.containerId,
8428
+ interception: entry.interception,
8429
+ fallback: entry.fallback,
8430
+ shouldHydrate: metadata.shouldHydrate,
8431
+ isClientComponent: metadata.isClientComponent,
8432
+ segments: entry.route.segments
8433
+ };
8434
+ });
8435
+ return { routes, layouts, slots };
8025
8436
  }
8026
8437
  /**
8027
8438
  * Load a route module dynamically
@@ -8098,6 +8509,7 @@ var init_route_manager = __esm({
8098
8509
  async discoverFileRoutes(source) {
8099
8510
  const appDir = resolveAppPath(source.root, source.srcDir, "app");
8100
8511
  const pageFiles = await safeGlobFiles("**/page.{ts,tsx,js,jsx,md,mdx}", appDir);
8512
+ const slotDefaultFiles = await safeGlobFiles("**/@*/**/default.{ts,tsx,js,jsx}", appDir);
8101
8513
  const layoutFiles = await safeGlobFiles("**/layout.{ts,tsx,js,jsx}", appDir);
8102
8514
  const loadingFiles = await safeGlobFiles("**/loading.{ts,tsx,js,jsx}", appDir);
8103
8515
  const errorFiles = await safeGlobFiles("**/error.{ts,tsx,js,jsx}", appDir);
@@ -8106,7 +8518,7 @@ var init_route_manager = __esm({
8106
8518
  appDir
8107
8519
  );
8108
8520
  for (const [kind, files, target] of [
8109
- ["page", pageFiles, this.routes],
8521
+ ["page", pageFiles.filter((file) => parseRouteSlotFile(file) === null), this.routes],
8110
8522
  ["layout", layoutFiles, this.layouts],
8111
8523
  ["loading", loadingFiles, this.loadings],
8112
8524
  ["error", errorFiles, this.errors]
@@ -8130,6 +8542,32 @@ var init_route_manager = __esm({
8130
8542
  });
8131
8543
  }
8132
8544
  }
8545
+ for (const file of [...pageFiles, ...slotDefaultFiles]) {
8546
+ const slot = parseRouteSlotFile(file);
8547
+ if (!slot) continue;
8548
+ const modulePath = import_path6.default.join(appDir, file);
8549
+ const pattern = this.createRoutePattern(slot.route);
8550
+ const ownerPattern = this.createRoutePattern(slot.ownerRoute);
8551
+ const key = `${ownerPattern}:${slot.name}:${slot.interception ? "intercept" : "slot"}:${slot.fallback ? "default" : pattern}`;
8552
+ const existing = this.routeSlots.get(key);
8553
+ if (existing?.sourceRoot === source.root) {
8554
+ throw new Error(
8555
+ `Duplicate route slot "${slot.name}" for "${pattern}". Found both ${existing.modulePath} and ${modulePath}.`
8556
+ );
8557
+ }
8558
+ this.routeSlots.set(key, {
8559
+ route: slot.route,
8560
+ modulePath,
8561
+ pattern,
8562
+ source: "file",
8563
+ sourceRoot: source.root,
8564
+ name: slot.name,
8565
+ ownerPattern,
8566
+ interception: slot.interception,
8567
+ fallback: slot.fallback,
8568
+ containerId: createRouteSlotContainerId(slot.name, ownerPattern)
8569
+ });
8570
+ }
8133
8571
  for (const file of metadataImageFiles) {
8134
8572
  const fileBase = import_path6.default.basename(file, import_path6.default.extname(file));
8135
8573
  const kind = fileBase === "twitter-image" ? "twitter" : "opengraph";
@@ -8255,6 +8693,71 @@ var init_route_manager = __esm({
8255
8693
  }
8256
8694
  return matchingLayouts;
8257
8695
  }
8696
+ findMatchingRouteSlots(pathname, interceptFrom) {
8697
+ const groups = /* @__PURE__ */ new Map();
8698
+ for (const entry of this.routeSlots.values()) {
8699
+ if (!this.matchesRoutePrefix(pathname, entry.ownerPattern)) continue;
8700
+ const key = `${entry.ownerPattern}:${entry.name}`;
8701
+ const entries = groups.get(key) ?? [];
8702
+ entries.push(entry);
8703
+ groups.set(key, entries);
8704
+ }
8705
+ const normalizedFrom = interceptFrom ? this.toRoutePathname(new URL(interceptFrom, "http://farm.local").pathname) : void 0;
8706
+ const matches = [];
8707
+ for (const entries of groups.values()) {
8708
+ const candidates = entries.filter((entry) => !entry.fallback).filter(
8709
+ (entry) => !entry.interception || normalizedFrom !== void 0 && this.matchesRoutePrefix(normalizedFrom, entry.ownerPattern)
8710
+ ).map((entry) => ({
8711
+ entry,
8712
+ match: matchRoute(pathname, entry.route.segments)
8713
+ })).filter((candidate) => candidate.match.matches).sort((left, right) => {
8714
+ if (left.entry.interception !== right.entry.interception) {
8715
+ return left.entry.interception ? -1 : 1;
8716
+ }
8717
+ return routeSpecificity(right.entry) - routeSpecificity(left.entry);
8718
+ });
8719
+ const selected = candidates[0];
8720
+ if (selected) {
8721
+ matches.push({
8722
+ name: selected.entry.name,
8723
+ ownerPattern: selected.entry.ownerPattern,
8724
+ containerId: selected.entry.containerId,
8725
+ interception: selected.entry.interception,
8726
+ fallback: false,
8727
+ route: selected.entry,
8728
+ params: selected.match.params
8729
+ });
8730
+ continue;
8731
+ }
8732
+ const fallback = entries.find((entry) => entry.fallback);
8733
+ if (fallback) {
8734
+ matches.push({
8735
+ name: fallback.name,
8736
+ ownerPattern: fallback.ownerPattern,
8737
+ containerId: fallback.containerId,
8738
+ interception: false,
8739
+ fallback: true,
8740
+ route: fallback,
8741
+ params: {}
8742
+ });
8743
+ }
8744
+ }
8745
+ return matches.sort((left, right) => {
8746
+ const ownerDifference = left.route.route.segments.length - right.route.route.segments.length;
8747
+ return ownerDifference || left.name.localeCompare(right.name);
8748
+ });
8749
+ }
8750
+ matchesRoutePrefix(pathname, pattern) {
8751
+ if (pattern === "/") return true;
8752
+ const patternSegments = parseRoutePath(`${pattern}/page.tsx`).segments;
8753
+ const pathSegments = pathname.split("/").filter(Boolean);
8754
+ if (patternSegments.length > pathSegments.length) return false;
8755
+ for (let index = 0; index < patternSegments.length; index++) {
8756
+ const segment = patternSegments[index];
8757
+ if (!segment.isDynamic && segment.segment !== pathSegments[index]) return false;
8758
+ }
8759
+ return true;
8760
+ }
8258
8761
  findNearestBoundary(pathname, boundaries) {
8259
8762
  const normalizedPath = pathname === "/" ? "/" : pathname.replace(/\/$/, "");
8260
8763
  const pathSegments = normalizedPath.split("/").filter(Boolean);
@@ -8869,7 +9372,7 @@ function mergeMetadata(base, next) {
8869
9372
  }
8870
9373
  function addMetadataImageReference(metadata, reference) {
8871
9374
  if (reference.kind === "opengraph") {
8872
- const openGraph = { ...metadata.openGraph || {} };
9375
+ const openGraph = { ...metadata.openGraph };
8873
9376
  if (!hasMetadataImages(openGraph.images) && !hasMetadataImages(openGraph.image)) {
8874
9377
  openGraph.images = [
8875
9378
  {
@@ -8886,7 +9389,7 @@ function addMetadataImageReference(metadata, reference) {
8886
9389
  openGraph
8887
9390
  };
8888
9391
  }
8889
- const twitter = { ...metadata.twitter || {} };
9392
+ const twitter = { ...metadata.twitter };
8890
9393
  if (!hasMetadataImages(twitter.images)) {
8891
9394
  twitter.images = [reference.href];
8892
9395
  twitter.card = twitter.card || "summary_large_image";
@@ -9512,8 +10015,8 @@ var init_runtime = __esm({
9512
10015
  cookie: this.config.cookie,
9513
10016
  direction: getFarmLocaleDirection(resolution.locale, this.config.direction),
9514
10017
  messages: {
9515
- ...this.catalogs[this.config.fallbackLocale] || {},
9516
- ...this.catalogs[resolution.locale] || {}
10018
+ ...this.catalogs[this.config.fallbackLocale],
10019
+ ...this.catalogs[resolution.locale]
9517
10020
  }
9518
10021
  };
9519
10022
  }
@@ -9849,7 +10352,9 @@ var init_renderer = __esm({
9849
10352
  async runWithRequestContext(request, fn) {
9850
10353
  return _runWithCurrentRequest(
9851
10354
  request,
9852
- () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, { redirect: false }) : fn()
10355
+ () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, {
10356
+ redirect: false
10357
+ }) : fn()
9853
10358
  );
9854
10359
  }
9855
10360
  async resolveRouteContext(input) {
@@ -9872,11 +10377,11 @@ var init_renderer = __esm({
9872
10377
  /**
9873
10378
  * Check if a path should be served from SSG cache
9874
10379
  */
9875
- shouldServeSSG(pathname) {
10380
+ async shouldServeSSG(pathname) {
9876
10381
  const ssgPage = matchSSGPage(pathname, this.ssgManifest);
9877
10382
  if (!ssgPage) return null;
9878
- const cached = this.getCachedSSGPage(pathname);
9879
- if (cached && this.dataCache.isStale(cached)) {
10383
+ const cached = await this.getCachedSSGPage(pathname);
10384
+ if (cached && await this.dataCache.isStaleAsync(cached)) {
9880
10385
  this.regenerateSSGPage(ssgPage);
9881
10386
  }
9882
10387
  return ssgPage;
@@ -9888,12 +10393,12 @@ var init_renderer = __esm({
9888
10393
  return createFarmCacheKey(["ppr", normalizeRevalidatePath(pathname), search]);
9889
10394
  }
9890
10395
  getCachedSSGPage(urlPath) {
9891
- return this.dataCache.getEntry(this.getSSGCacheKey(urlPath), {
10396
+ return this.dataCache.getEntryAsync(this.getSSGCacheKey(urlPath), {
9892
10397
  allowStale: true
9893
10398
  });
9894
10399
  }
9895
- cacheSSGPage(page2, html, options) {
9896
- this.dataCache.set(
10400
+ async cacheSSGPage(page2, html, options) {
10401
+ await this.dataCache.setAsync(
9897
10402
  this.getSSGCacheKey(page2.urlPath),
9898
10403
  { html, document: options.document },
9899
10404
  {
@@ -9905,11 +10410,11 @@ var init_renderer = __esm({
9905
10410
  );
9906
10411
  }
9907
10412
  getCachedPPRShell(pathname, search) {
9908
- return this.dataCache.getEntry(this.getPPRCacheKey(pathname, search));
10413
+ return this.dataCache.getEntryAsync(this.getPPRCacheKey(pathname, search));
9909
10414
  }
9910
- cachePPRShell(options, html) {
10415
+ async cachePPRShell(options, html) {
9911
10416
  const key = this.getPPRCacheKey(options.pathname, options.search);
9912
- this.dataCache.set(
10417
+ await this.dataCache.setAsync(
9913
10418
  key,
9914
10419
  { html },
9915
10420
  {
@@ -10007,7 +10512,7 @@ var init_renderer = __esm({
10007
10512
  }
10008
10513
  const { renderToString } = await import("react-dom/server");
10009
10514
  const html = renderToString(await this.wrapWithIntegrationProviders(pageElement));
10010
- this.cacheSSGPage(page2, html, { document: false });
10515
+ await this.cacheSSGPage(page2, html, { document: false });
10011
10516
  logger.info(`ISR: Regenerated ${page2.urlPath}`);
10012
10517
  } catch (error) {
10013
10518
  logger.error(`ISR regeneration failed for ${page2.urlPath}: ${error}`);
@@ -10021,7 +10526,7 @@ var init_renderer = __esm({
10021
10526
  * Serve a pre-rendered SSG page
10022
10527
  */
10023
10528
  async serveSSGPage(req, res, page2) {
10024
- const cached = this.getCachedSSGPage(page2.urlPath);
10529
+ const cached = await this.getCachedSSGPage(page2.urlPath);
10025
10530
  if (cached) {
10026
10531
  res.setHeader("Content-Type", "text/html; charset=utf-8");
10027
10532
  res.setHeader("X-Farm-SSG", "cached");
@@ -10039,9 +10544,12 @@ var init_renderer = __esm({
10039
10544
  if (fs2.existsSync(htmlPath)) {
10040
10545
  const stat = fs2.statSync(htmlPath);
10041
10546
  const html = fs2.readFileSync(htmlPath, "utf-8");
10042
- this.cacheSSGPage(page2, html, { document: true, createdAt: stat.mtimeMs });
10043
- const fileCacheEntry = this.getCachedSSGPage(page2.urlPath);
10044
- if (fileCacheEntry && this.dataCache.isStale(fileCacheEntry)) {
10547
+ await this.cacheSSGPage(page2, html, {
10548
+ document: true,
10549
+ createdAt: stat.mtimeMs
10550
+ });
10551
+ const fileCacheEntry = await this.getCachedSSGPage(page2.urlPath);
10552
+ if (fileCacheEntry && await this.dataCache.isStaleAsync(fileCacheEntry)) {
10045
10553
  this.regenerateSSGPage(page2);
10046
10554
  }
10047
10555
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -10087,6 +10595,7 @@ var init_renderer = __esm({
10087
10595
  let pathname = "/";
10088
10596
  let params = {};
10089
10597
  let layouts = [];
10598
+ let routeSlots = [];
10090
10599
  let searchParamsObject = {};
10091
10600
  let middlewareMap = /* @__PURE__ */ new Map();
10092
10601
  let middlewareContext = /* @__PURE__ */ new Map();
@@ -10119,7 +10628,7 @@ var init_renderer = __esm({
10119
10628
  }
10120
10629
  this.applyDeploymentHeaders(req, res);
10121
10630
  if (process.env.NODE_ENV === "production") {
10122
- const ssgPage = this.shouldServeSSG(pathname);
10631
+ const ssgPage = await this.shouldServeSSG(pathname);
10123
10632
  if (ssgPage) {
10124
10633
  const served = await this.serveSSGPage(req, res, ssgPage);
10125
10634
  if (served) {
@@ -10132,6 +10641,7 @@ var init_renderer = __esm({
10132
10641
  const route = match.route;
10133
10642
  params = match.params;
10134
10643
  layouts = match.layouts;
10644
+ routeSlots = match.slots ?? [];
10135
10645
  if (!route) {
10136
10646
  emitFarmEvent({ type: "route.notFound", pathname });
10137
10647
  await this.render404(req, res);
@@ -10148,7 +10658,9 @@ var init_renderer = __esm({
10148
10658
  errorBoundaryEntry = this.routeManager.getMatchingError(pathname);
10149
10659
  middlewareMap = toMiddlewareMap2(req.__FARM_MIDDLEWARE_DATA__);
10150
10660
  middlewareContext = toMiddlewareMap2(req.__FARM_MIDDLEWARE_CONTEXT__);
10151
- pluginExposedContext = getRequestContextSnapshot(req, { exposedOnly: true });
10661
+ pluginExposedContext = getRequestContextSnapshot(req, {
10662
+ exposedOnly: true
10663
+ });
10152
10664
  const currentRequest = createWebRequestFromFarmRequest(req);
10153
10665
  const routeContext = await this.resolveRouteContext({
10154
10666
  request: currentRequest,
@@ -10212,8 +10724,16 @@ var init_renderer = __esm({
10212
10724
  revalidate: renderingConfig.revalidate
10213
10725
  } : void 0;
10214
10726
  if (renderingConfig.ppr && pprBypassReason) {
10215
- emitFarmEvent({ type: "ppr.shell.bypass", route: pathname, reason: pprBypassReason });
10216
- emitFarmEvent({ type: "cache.bypass", route: pathname, reason: pprBypassReason });
10727
+ emitFarmEvent({
10728
+ type: "ppr.shell.bypass",
10729
+ route: pathname,
10730
+ reason: pprBypassReason
10731
+ });
10732
+ emitFarmEvent({
10733
+ type: "cache.bypass",
10734
+ route: pathname,
10735
+ reason: pprBypassReason
10736
+ });
10217
10737
  if (pprBypassReason === "refresh") {
10218
10738
  pprRefreshRoute = pathname;
10219
10739
  emitFarmEvent({ type: "ppr.refresh.start", route: pathname });
@@ -10221,14 +10741,22 @@ var init_renderer = __esm({
10221
10741
  }
10222
10742
  if (pprShellOptions) {
10223
10743
  const pprCacheKey = this.getPPRCacheKey(pathname, url.search);
10224
- const cachedPPRShell = this.getCachedPPRShell(pathname, url.search);
10744
+ const cachedPPRShell = await this.getCachedPPRShell(pathname, url.search);
10225
10745
  if (cachedPPRShell) {
10226
- emitFarmEvent({ type: "ppr.shell.hit", route: pathname, key: pprCacheKey });
10746
+ emitFarmEvent({
10747
+ type: "ppr.shell.hit",
10748
+ route: pathname,
10749
+ key: pprCacheKey
10750
+ });
10227
10751
  this.serveCachedPPRShell(res, cachedPPRShell.value, renderingConfig.revalidate);
10228
10752
  completeRender(res.statusCode || 200, pathname);
10229
10753
  return;
10230
10754
  }
10231
- emitFarmEvent({ type: "ppr.shell.miss", route: pathname, key: pprCacheKey });
10755
+ emitFarmEvent({
10756
+ type: "ppr.shell.miss",
10757
+ route: pathname,
10758
+ key: pprCacheKey
10759
+ });
10232
10760
  }
10233
10761
  let LoadingFallbackComponent = null;
10234
10762
  if (loadingBoundaryEntry) {
@@ -10252,14 +10780,77 @@ var init_renderer = __esm({
10252
10780
  );
10253
10781
  const moduleMetadata = routeManifestEntry || getClientModuleMetadata(route.modulePath, this.config.root);
10254
10782
  const isClientComponent = moduleMetadata.isClientComponent;
10783
+ const renderedRouteSlots = await Promise.all(
10784
+ routeSlots.map(async (slot) => {
10785
+ const slotModule = await this.routeManager.loadRouteModule(slot.route.modulePath);
10786
+ if (!slotModule.default) {
10787
+ throw new Error(
10788
+ `Route slot "${slot.name}" module ${slot.route.modulePath} does not export a default component`
10789
+ );
10790
+ }
10791
+ const slotContext = await this.resolveRouteContext({
10792
+ request: currentRequest,
10793
+ rawRequest: req,
10794
+ params: slot.params,
10795
+ search: searchParamsObject,
10796
+ path: pathname
10797
+ });
10798
+ const rawSlotProps = withFarmRouteContext(
10799
+ {
10800
+ params: slot.params,
10801
+ searchParams: Promise.resolve(searchParamsObject),
10802
+ path: pathname,
10803
+ middleware: middlewareMap.size > 0 ? { data: middlewareMap } : void 0,
10804
+ context: pluginExposedContext.size > 0 ? { data: pluginExposedContext } : void 0
10805
+ },
10806
+ slotContext
10807
+ );
10808
+ const slotProps = await parseRouteModuleProps(slotModule, {
10809
+ props: rawSlotProps,
10810
+ search: searchParamsObject,
10811
+ routePath: slot.route.pattern
10812
+ });
10813
+ const metadata = getClientModuleMetadata(slot.route.modulePath, this.config.root);
10814
+ return {
10815
+ ...slot,
10816
+ module: slotModule,
10817
+ props: slotProps,
10818
+ isClientComponent: metadata.isClientComponent,
10819
+ shouldHydrate: metadata.shouldHydrate
10820
+ };
10821
+ })
10822
+ );
10255
10823
  const shouldHydrate = moduleMetadata.shouldHydrate;
10824
+ const hasHydratableRouteSlots = renderedRouteSlots.some(
10825
+ (slot) => slot.isClientComponent || slot.shouldHydrate
10826
+ );
10256
10827
  req.__FARM_PAGE_PATH__ = route.modulePath;
10257
10828
  req.__FARM_ROUTE__ = pathname;
10258
10829
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
10259
10830
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
10831
+ req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
10260
10832
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
10261
10833
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
10262
10834
  ) : null;
10835
+ req.__FARM_ROUTE_SLOTS__ = renderedRouteSlots.map((slot) => ({
10836
+ name: slot.name,
10837
+ ownerPattern: slot.ownerPattern,
10838
+ containerId: slot.containerId,
10839
+ interception: slot.interception,
10840
+ fallback: slot.fallback,
10841
+ modulePath: slot.route.modulePath,
10842
+ isClientComponent: slot.isClientComponent,
10843
+ shouldHydrate: slot.shouldHydrate,
10844
+ props: {
10845
+ params: slot.props.params,
10846
+ search: slot.props.search,
10847
+ searchParams: slot.props.search,
10848
+ ..."data" in slot.props ? { data: slot.props.data } : {},
10849
+ ...slot.props.__farmCanonicalPath ? { __farmCanonicalPath: slot.props.__farmCanonicalPath } : {},
10850
+ ...slot.props.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
10851
+ path: pathname
10852
+ }
10853
+ }));
10263
10854
  req.__FARM_PROPS__ = {
10264
10855
  params: pageProps.params,
10265
10856
  search: pageProps.search,
@@ -10319,12 +10910,32 @@ var init_renderer = __esm({
10319
10910
  let wrappedElement = pageElement;
10320
10911
  for (let i = layoutModules.length - 1; i >= 0; i--) {
10321
10912
  const layoutModule = layoutModules[i];
10913
+ const layoutEntry = layouts[i];
10322
10914
  const LayoutComponent = layoutModule.default;
10915
+ const slotProps = {};
10916
+ for (const slot of renderedRouteSlots) {
10917
+ if (slot.ownerPattern !== layoutEntry.pattern) continue;
10918
+ let slotElement = import_react4.default.createElement(
10919
+ slot.module.default,
10920
+ slot.props
10921
+ );
10922
+ slotElement = import_react4.default.createElement(
10923
+ "div",
10924
+ {
10925
+ id: slot.containerId,
10926
+ "data-farm-route-slot": slot.name,
10927
+ "data-farm-slot-owner": slot.ownerPattern
10928
+ },
10929
+ slotElement
10930
+ );
10931
+ slotProps[slot.name] = slotElement;
10932
+ }
10323
10933
  wrappedElement = import_react4.default.createElement(
10324
10934
  LayoutComponent,
10325
10935
  {
10326
10936
  children: wrappedElement,
10327
- params
10937
+ params,
10938
+ ...slotProps
10328
10939
  }
10329
10940
  );
10330
10941
  }
@@ -10361,7 +10972,10 @@ var init_renderer = __esm({
10361
10972
  routeManifest,
10362
10973
  captureStaticShell: Boolean(pprShellOptions),
10363
10974
  observabilityRoute: pathname,
10364
- onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({ type: "ppr.suspense.holeDetected", route: pathname }) : void 0,
10975
+ onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({
10976
+ type: "ppr.suspense.holeDetected",
10977
+ route: pathname
10978
+ }) : void 0,
10365
10979
  onComplete: pprShellOptions && req.method !== "HEAD" ? (html) => this.cachePPRShell(pprShellOptions, html) : void 0
10366
10980
  }
10367
10981
  );
@@ -10409,7 +11023,11 @@ var init_renderer = __esm({
10409
11023
  }
10410
11024
  emitFarmEvent({ type: "render.error", route: pathname, error });
10411
11025
  if (pprRefreshRoute) {
10412
- emitFarmEvent({ type: "ppr.refresh.error", route: pprRefreshRoute, error });
11026
+ emitFarmEvent({
11027
+ type: "ppr.refresh.error",
11028
+ route: pprRefreshRoute,
11029
+ error
11030
+ });
10413
11031
  }
10414
11032
  logger.error(`Error rendering page: ${error}`);
10415
11033
  if (res.headersSent || res.writableEnded) {
@@ -10462,7 +11080,9 @@ var init_renderer = __esm({
10462
11080
  if (typeof layoutModule.generateMetadata === "function") {
10463
11081
  metadata = mergeMetadata(
10464
11082
  metadata,
10465
- await layoutModule.generateMetadata({ params: options.pageProps.params })
11083
+ await layoutModule.generateMetadata({
11084
+ params: options.pageProps.params
11085
+ })
10466
11086
  );
10467
11087
  }
10468
11088
  }
@@ -10693,7 +11313,13 @@ var init_renderer = __esm({
10693
11313
  clientEntry: "/@farm/client.js",
10694
11314
  routes: {},
10695
11315
  layouts: {},
10696
- sharedAssets: [{ tag: "link", attrs: { rel: "stylesheet", href: "/src/app/globals.css" } }]
11316
+ slots: [],
11317
+ sharedAssets: [
11318
+ {
11319
+ tag: "link",
11320
+ attrs: { rel: "stylesheet", href: "/src/app/globals.css" }
11321
+ }
11322
+ ]
10697
11323
  };
10698
11324
  for (const routeEntry of manifest.routes) {
10699
11325
  clientManifest.routes[routeEntry.pattern] = {
@@ -10715,9 +11341,26 @@ var init_renderer = __esm({
10715
11341
  assets: []
10716
11342
  };
10717
11343
  }
10718
- const deferredProps = prepareDeferredData(req.__FARM_PROPS__ || {});
11344
+ for (const slotEntry of manifest.slots ?? []) {
11345
+ clientManifest.slots.push({
11346
+ ...slotEntry,
11347
+ preloads: [slotEntry.modulePath],
11348
+ assets: []
11349
+ });
11350
+ }
11351
+ const routeSlotPayload = (req.__FARM_ROUTE_SLOTS__ || []).map(
11352
+ (slot) => ({
11353
+ ...slot,
11354
+ modulePath: typeof slot.modulePath === "string" && slot.modulePath.startsWith(this.config.root) ? slot.modulePath.slice(this.config.root.length) : slot.modulePath
11355
+ })
11356
+ );
11357
+ const deferredProps = prepareDeferredData({
11358
+ page: req.__FARM_PROPS__ || {},
11359
+ slots: routeSlotPayload
11360
+ });
10719
11361
  const propsScript = `<script>
10720
- window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data)};
11362
+ window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data.page)};
11363
+ window.__FARM_ROUTE_SLOTS__ = ${serializeInlineValue(deferredProps.data.slots)};
10721
11364
  window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
10722
11365
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
10723
11366
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
@@ -10730,7 +11373,7 @@ window.__FARM_MANIFEST__ = ${JSON.stringify(clientManifest)};
10730
11373
  window.__FARM_INTEGRATION_API_MANIFEST__ = ${JSON.stringify(getRegisteredIntegrationAPIManifest())};
10731
11374
  ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(getFarmI18nClientSnapshot())};` : ""}
10732
11375
  </script>`;
10733
- const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true ? createPreHydrationClickQueueScript() : "";
11376
+ const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true || req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ === true ? createPreHydrationClickQueueScript() : "";
10734
11377
  const {
10735
11378
  title,
10736
11379
  tags: metaTags,
@@ -10841,7 +11484,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
10841
11484
  didError = true;
10842
11485
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
10843
11486
  logger.error(`SSR shell error: ${error}`);
10844
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
11487
+ emitFarmEvent({
11488
+ type: "render.error",
11489
+ route: observabilityRoute,
11490
+ error
11491
+ });
10845
11492
  }
10846
11493
  if (clearMiddlewareData) {
10847
11494
  clearMiddlewareData();
@@ -10852,7 +11499,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
10852
11499
  didError = true;
10853
11500
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
10854
11501
  logger.error(`SSR streaming error: ${error}`);
10855
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
11502
+ emitFarmEvent({
11503
+ type: "render.error",
11504
+ route: observabilityRoute,
11505
+ error
11506
+ });
10856
11507
  }
10857
11508
  }
10858
11509
  });
@@ -11034,6 +11685,7 @@ var init_app = __esm({
11034
11685
  init_devtools_config();
11035
11686
  init_config();
11036
11687
  init_server2();
11688
+ init_cache();
11037
11689
  defaultDocsConfig = {
11038
11690
  enabled: false,
11039
11691
  entry: "/docs",
@@ -11048,17 +11700,14 @@ var init_app = __esm({
11048
11700
  _setDefaultFarmI18nRuntime(this.i18nRuntime);
11049
11701
  this.viteServer = viteServer;
11050
11702
  this.routeManager = new RouteManager(this.config, viteServer);
11051
- this.serverRenderer = new ServerRenderer(
11052
- this.config,
11053
- this.routeManager,
11054
- this.i18nRuntime
11055
- );
11703
+ this.serverRenderer = new ServerRenderer(this.config, this.routeManager, this.i18nRuntime);
11056
11704
  }
11057
11705
  async initialize() {
11058
11706
  if (process.env.FARM_VERBOSE) {
11059
11707
  logger.info("Initializing Farm.js application...");
11060
11708
  }
11061
11709
  await initStorage(this.config.storage);
11710
+ await configureFarmCache(this.config.cache);
11062
11711
  await this.i18nRuntime.initialize();
11063
11712
  await this.verifyAppStructure();
11064
11713
  await this.routeManager.discoverRoutes();
@@ -11090,6 +11739,7 @@ var init_app = __esm({
11090
11739
  preset: config.preset ?? "node-server",
11091
11740
  deploy: config.deploy || {},
11092
11741
  storage: config.storage || {},
11742
+ cache: config.cache || {},
11093
11743
  integrations: config.integrations || {},
11094
11744
  plugins: config.plugins || [],
11095
11745
  migrations: config.migrations || { commands: [] },
@@ -11163,6 +11813,41 @@ var init_app = __esm({
11163
11813
  }
11164
11814
  });
11165
11815
 
11816
+ // src/api/transport.ts
11817
+ var init_transport = __esm({
11818
+ "src/api/transport.ts"() {
11819
+ "use strict";
11820
+ }
11821
+ });
11822
+
11823
+ // src/api/endpoint.ts
11824
+ function isEndpointFailure(value) {
11825
+ return value instanceof EndpointFailure;
11826
+ }
11827
+ var import_better_call, _EndpointFailure, EndpointFailure, EMPTY_ENDPOINT_MIDDLEWARE, EMPTY_ENDPOINT_CONTEXT;
11828
+ var init_endpoint = __esm({
11829
+ "src/api/endpoint.ts"() {
11830
+ "use strict";
11831
+ import_better_call = require("better-call");
11832
+ init_cache();
11833
+ init_transport();
11834
+ _EndpointFailure = class _EndpointFailure extends Error {
11835
+ constructor(code, data, options) {
11836
+ super(options.message);
11837
+ this.name = "EndpointFailure";
11838
+ this.code = code;
11839
+ this.data = data;
11840
+ this.status = options.status;
11841
+ }
11842
+ };
11843
+ __name(_EndpointFailure, "EndpointFailure");
11844
+ EndpointFailure = _EndpointFailure;
11845
+ __name(isEndpointFailure, "isEndpointFailure");
11846
+ EMPTY_ENDPOINT_MIDDLEWARE = Object.freeze([]);
11847
+ EMPTY_ENDPOINT_CONTEXT = Object.freeze(/* @__PURE__ */ Object.create(null));
11848
+ }
11849
+ });
11850
+
11166
11851
  // src/api/runtime.ts
11167
11852
  function matchAPIRoute(routes, pathname) {
11168
11853
  const exactRoute = routes.get(pathname);
@@ -11193,10 +11878,10 @@ async function invokeAPIRouteEndpoint(endpoint, request, params = {}) {
11193
11878
  async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
11194
11879
  const farmHandler = endpoint.__handler || (isFarmContextHandler(endpoint) ? endpoint : null);
11195
11880
  if (!farmHandler) {
11196
- const result2 = await endpoint(request, {
11881
+ const result = await endpoint(request, {
11197
11882
  params: Promise.resolve(params)
11198
11883
  });
11199
- return normalizeRouteResponse(result2);
11884
+ return normalizeRouteResponse(result);
11200
11885
  }
11201
11886
  const url = new URL(request.url);
11202
11887
  const query = {};
@@ -11205,7 +11890,7 @@ async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
11205
11890
  });
11206
11891
  let body = void 0;
11207
11892
  if (request.method.toUpperCase() !== "GET" && request.method.toUpperCase() !== "HEAD") {
11208
- body = await readJSONBody(request);
11893
+ body = await readRequestBody2(request);
11209
11894
  }
11210
11895
  const headers = Object.fromEntries(request.headers.entries());
11211
11896
  const types = endpoint.__types || {};
@@ -11221,15 +11906,30 @@ async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
11221
11906
  if (headersValidation instanceof Response) {
11222
11907
  return headersValidation;
11223
11908
  }
11224
- const result = await farmHandler({
11909
+ const handlerContext = {
11225
11910
  query: queryValidation,
11226
11911
  body: bodyValidation,
11227
11912
  headers: headersValidation,
11228
11913
  request,
11229
11914
  context: {},
11230
11915
  params
11231
- });
11232
- return normalizeRouteResponse(result);
11916
+ };
11917
+ let execution;
11918
+ try {
11919
+ execution = typeof endpoint.__farmInvoke === "function" ? await endpoint.__farmInvoke(handlerContext) : {
11920
+ result: await farmHandler(handlerContext),
11921
+ context: handlerContext.context,
11922
+ handlerExecuted: true,
11923
+ invalidations: []
11924
+ };
11925
+ } catch (error) {
11926
+ if (isEndpointFailure(error)) {
11927
+ return createEndpointFailureResponse(error);
11928
+ }
11929
+ throw error;
11930
+ }
11931
+ const response = normalizeRouteResponse(execution.result);
11932
+ return attachEndpointInvalidations(response, execution.invalidations);
11233
11933
  }
11234
11934
  function normalizeRouteResponse(result) {
11235
11935
  if (isWebResponse2(result)) {
@@ -11246,6 +11946,38 @@ function normalizeRouteResponse(result) {
11246
11946
  function isWebResponse2(value) {
11247
11947
  return value instanceof Response || typeof value === "object" && value !== null && "headers" in value && "status" in value && typeof value.arrayBuffer === "function";
11248
11948
  }
11949
+ function attachEndpointInvalidations(response, keys) {
11950
+ const existing = decodeFarmCacheInvalidations(
11951
+ response.headers.get(FARM_CACHE_INVALIDATION_HEADER)
11952
+ );
11953
+ const encoded = encodeFarmCacheInvalidations([...existing, ...keys]);
11954
+ if (!encoded) return response;
11955
+ const headers = new Headers(response.headers);
11956
+ headers.set(FARM_CACHE_INVALIDATION_HEADER, encoded);
11957
+ return new Response(response.body, {
11958
+ status: response.status,
11959
+ statusText: response.statusText,
11960
+ headers
11961
+ });
11962
+ }
11963
+ function createEndpointFailureResponse(failure) {
11964
+ return new Response(
11965
+ JSON.stringify({
11966
+ error: {
11967
+ code: failure.code,
11968
+ message: failure.message,
11969
+ data: failure.data
11970
+ }
11971
+ }),
11972
+ {
11973
+ status: failure.status,
11974
+ headers: {
11975
+ "cache-control": "no-store",
11976
+ "content-type": "application/json"
11977
+ }
11978
+ }
11979
+ );
11980
+ }
11249
11981
  function isFarmContextHandler(endpoint) {
11250
11982
  if (typeof endpoint !== "function") {
11251
11983
  return false;
@@ -11256,16 +11988,46 @@ function isFarmContextHandler(endpoint) {
11256
11988
  const firstParameter = (arrowMatch?.[1] || arrowMatch?.[2] || functionMatch?.[1] || "").split(",")[0].trim();
11257
11989
  return firstParameter === "ctx" || firstParameter === "context" || firstParameter.startsWith("{");
11258
11990
  }
11259
- async function readJSONBody(request) {
11991
+ async function readRequestBody2(request) {
11992
+ const contentType = request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
11260
11993
  try {
11994
+ if (contentType === "multipart/form-data") {
11995
+ return formDataToObject(await request.clone().formData());
11996
+ }
11261
11997
  const text2 = await request.clone().text();
11262
- if (text2) {
11998
+ if (!text2) return void 0;
11999
+ if (contentType === "application/x-www-form-urlencoded") {
12000
+ return searchParamsToObject2(new URLSearchParams(text2));
12001
+ }
12002
+ if (contentType === "application/json" || contentType?.endsWith("+json")) {
11263
12003
  return JSON.parse(text2);
11264
12004
  }
12005
+ return JSON.parse(text2);
11265
12006
  } catch {
11266
12007
  }
11267
12008
  return void 0;
11268
12009
  }
12010
+ function formDataToObject(formData) {
12011
+ return entriesToObject(formData.entries());
12012
+ }
12013
+ function searchParamsToObject2(searchParams) {
12014
+ return entriesToObject(searchParams.entries());
12015
+ }
12016
+ function entriesToObject(entries) {
12017
+ const output = /* @__PURE__ */ Object.create(null);
12018
+ for (const [key, value] of entries) {
12019
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
12020
+ const current = output[key];
12021
+ if (current === void 0) {
12022
+ output[key] = value;
12023
+ } else if (Array.isArray(current)) {
12024
+ current.push(value);
12025
+ } else {
12026
+ output[key] = [current, value];
12027
+ }
12028
+ }
12029
+ return output;
12030
+ }
11269
12031
  function validateInput(schema, value, error) {
11270
12032
  if (!schema || typeof schema.parse !== "function") {
11271
12033
  return value;
@@ -11354,13 +12116,20 @@ var init_runtime2 = __esm({
11354
12116
  "src/api/runtime.ts"() {
11355
12117
  "use strict";
11356
12118
  init_request();
12119
+ init_cache_invalidation();
12120
+ init_endpoint();
11357
12121
  __name(matchAPIRoute, "matchAPIRoute");
11358
12122
  __name(invokeAPIRouteEndpoint, "invokeAPIRouteEndpoint");
11359
12123
  __name(invokeAPIRouteEndpointInContext, "invokeAPIRouteEndpointInContext");
11360
12124
  __name(normalizeRouteResponse, "normalizeRouteResponse");
11361
12125
  __name(isWebResponse2, "isWebResponse");
12126
+ __name(attachEndpointInvalidations, "attachEndpointInvalidations");
12127
+ __name(createEndpointFailureResponse, "createEndpointFailureResponse");
11362
12128
  __name(isFarmContextHandler, "isFarmContextHandler");
11363
- __name(readJSONBody, "readJSONBody");
12129
+ __name(readRequestBody2, "readRequestBody");
12130
+ __name(formDataToObject, "formDataToObject");
12131
+ __name(searchParamsToObject2, "searchParamsToObject");
12132
+ __name(entriesToObject, "entriesToObject");
11364
12133
  __name(validateInput, "validateInput");
11365
12134
  __name(matchRoutePath, "matchRoutePath");
11366
12135
  __name(getPathSegments, "getPathSegments");
@@ -12329,7 +13098,9 @@ function readManifest(contentDir) {
12329
13098
  const manifestPath = import_node_path4.default.join(contentDir, FARM_DOCS_LAST_MODIFIED_MANIFEST);
12330
13099
  if (!(0, import_node_fs2.existsSync)(manifestPath)) return null;
12331
13100
  try {
12332
- const parsed = JSON.parse((0, import_node_fs2.readFileSync)(manifestPath, "utf8"));
13101
+ const parsed = JSON.parse(
13102
+ (0, import_node_fs2.readFileSync)(manifestPath, "utf8")
13103
+ );
12333
13104
  if (parsed.version !== 1 || !parsed.pages || typeof parsed.pages !== "object") return null;
12334
13105
  const pages = Object.fromEntries(
12335
13106
  Object.entries(parsed.pages).filter(
@@ -13341,7 +14112,7 @@ var init_manager = __esm({
13341
14112
  if (!nestedMatch.matched) {
13342
14113
  return { matched: false };
13343
14114
  }
13344
- const params = { ...nestedMatch.params || {} };
14115
+ const params = { ...nestedMatch.params };
13345
14116
  delete params.__farmRest;
13346
14117
  return {
13347
14118
  matched: true,
@@ -13715,6 +14486,7 @@ async function generateRouteTypes(options) {
13715
14486
  absolute: false
13716
14487
  });
13717
14488
  for (const file of pageFiles) {
14489
+ if (parseRouteSlotFile(file)) continue;
13718
14490
  const route = parseRoutePath(file);
13719
14491
  if (route.type === "page") {
13720
14492
  const pattern = createRoutePattern(route);
@@ -13780,6 +14552,7 @@ var init_generate_route_types = __esm({
13780
14552
  path17 = __toESM(require("path"));
13781
14553
  fs5 = __toESM(require("fs"));
13782
14554
  init_utils();
14555
+ init_route_slots();
13783
14556
  init_routes_server();
13784
14557
  init_write_file_if_changed();
13785
14558
  __name(routeSegmentsToTsTypeLiteral, "routeSegmentsToTsTypeLiteral");
@@ -14144,6 +14917,24 @@ var init_type_artifacts = __esm({
14144
14917
  }
14145
14918
  });
14146
14919
 
14920
+ // package.json
14921
+ var version;
14922
+ var init_package = __esm({
14923
+ "package.json"() {
14924
+ version = "0.1.0-beta.4";
14925
+ }
14926
+ });
14927
+
14928
+ // src/version.ts
14929
+ var FARM_VERSION;
14930
+ var init_version = __esm({
14931
+ "src/version.ts"() {
14932
+ "use strict";
14933
+ init_package();
14934
+ FARM_VERSION = version;
14935
+ }
14936
+ });
14937
+
14147
14938
  // src/after.ts
14148
14939
  function getAfterStorage() {
14149
14940
  const runtime = globalThis;
@@ -20432,16 +21223,7 @@ __export(devtools_client_exports, {
20432
21223
  function parseShortcut(shortcut) {
20433
21224
  const tokens = shortcut.toLowerCase().split("+").map((token) => token.trim()).filter(Boolean);
20434
21225
  const key = tokens.find(
20435
- (token) => ![
20436
- "alt",
20437
- "ctrl",
20438
- "control",
20439
- "meta",
20440
- "cmd",
20441
- "command",
20442
- "mod",
20443
- "shift"
20444
- ].includes(token)
21226
+ (token) => !["alt", "ctrl", "control", "meta", "cmd", "command", "mod", "shift"].includes(token)
20445
21227
  );
20446
21228
  if (!key) {
20447
21229
  return null;
@@ -20872,7 +21654,7 @@ function isResolvedEnvScope(value) {
20872
21654
  );
20873
21655
  }
20874
21656
  function isPotentialProgrammaticRouteSourceFile(normalizedFile, srcDirSlug) {
20875
- 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");
21657
+ 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");
20876
21658
  }
20877
21659
  function fileContainsProgrammaticPageRoute(file) {
20878
21660
  if (!fs7.existsSync(file)) {
@@ -21109,7 +21891,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21109
21891
  }, "isProgrammaticRouteSourceFile");
21110
21892
  const isAppRuntimeFile = /* @__PURE__ */ __name((file) => {
21111
21893
  const normalized = file.replace(/\\/g, "/");
21112
- 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(
21894
+ 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(
21113
21895
  normalized
21114
21896
  );
21115
21897
  }, "isAppRuntimeFile");
@@ -21800,7 +22582,10 @@ function farmPlugin(options = {}, initialPluginManager) {
21800
22582
  method: req.method || "GET"
21801
22583
  });
21802
22584
  }
21803
- const match = routeManager3.matchRoute(targetPath);
22585
+ const interceptFromHeader = request.headers.get("x-farm-intercept-from") || void 0;
22586
+ const match = routeManager3.matchRoute(targetRequestUrl.pathname, {
22587
+ interceptFrom: interceptFromHeader
22588
+ });
21804
22589
  if (pm) {
21805
22590
  await pm.runHookParallel("afterRouteMatch", {
21806
22591
  pathname: targetPath,
@@ -21816,7 +22601,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21816
22601
  res.end(JSON.stringify({ error: "Route not found" }));
21817
22602
  return;
21818
22603
  }
21819
- const { route, params, layouts } = match;
22604
+ const { route, params, layouts, slots } = match;
21820
22605
  if (!route) {
21821
22606
  res.statusCode = 404;
21822
22607
  res.setHeader("Content-Type", "application/json");
@@ -21871,6 +22656,54 @@ function farmPlugin(options = {}, initialPluginManager) {
21871
22656
  search: searchParams,
21872
22657
  routePath: route.pattern
21873
22658
  });
22659
+ const routeSlots = await Promise.all(
22660
+ slots.map(async (slot) => {
22661
+ const slotModule = await routeManager3.loadRouteModule(slot.route.modulePath);
22662
+ const slotMetadata = getClientModuleMetadata(
22663
+ slot.route.modulePath,
22664
+ server.config.root
22665
+ );
22666
+ const slotContext = await resolveFarmRouteContext(farmApp.getConfig(), {
22667
+ request,
22668
+ params: slot.params,
22669
+ search: searchParams,
22670
+ path: targetUrl.pathname
22671
+ });
22672
+ const slotProps = await parseRouteModuleProps2(slotModule, {
22673
+ props: withFarmRouteContext(
22674
+ {
22675
+ params: slot.params,
22676
+ searchParams: Promise.resolve(searchParams),
22677
+ path: targetUrl.pathname
22678
+ },
22679
+ slotContext
22680
+ ),
22681
+ search: searchParams,
22682
+ routePath: slot.route.pattern
22683
+ });
22684
+ return {
22685
+ name: slot.name,
22686
+ ownerPattern: slot.ownerPattern,
22687
+ containerId: slot.containerId,
22688
+ interception: slot.interception,
22689
+ fallback: slot.fallback,
22690
+ modulePath: slot.route.modulePath,
22691
+ isClientComponent: slotMetadata.isClientComponent,
22692
+ shouldHydrate: slotMetadata.shouldHydrate,
22693
+ props: {
22694
+ params: slotProps.params,
22695
+ search: slotProps.search,
22696
+ searchParams: slotProps.search,
22697
+ ..."data" in slotProps ? { data: slotProps.data } : {},
22698
+ ...slotProps.__farmCanonicalPath ? {
22699
+ __farmCanonicalPath: slotProps.__farmCanonicalPath
22700
+ } : {},
22701
+ ...slotProps.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
22702
+ path: targetUrl.pathname
22703
+ }
22704
+ };
22705
+ })
22706
+ );
21874
22707
  const projectRoot = server.config.root;
21875
22708
  const toUrlPath = /* @__PURE__ */ __name((absolutePath) => {
21876
22709
  if (absolutePath.startsWith(projectRoot)) {
@@ -21891,13 +22724,21 @@ function farmPlugin(options = {}, initialPluginManager) {
21891
22724
  },
21892
22725
  canonicalPath: routeProps.__farmCanonicalPath,
21893
22726
  modulePath: toUrlPath(route.modulePath),
21894
- isClientComponent,
21895
- shouldHydrate,
22727
+ isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
22728
+ shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
21896
22729
  metadata: {
21897
22730
  title: mergedMetadata.title,
21898
22731
  description: mergedMetadata.description
21899
22732
  },
21900
22733
  layoutModules: layouts.map((l) => toUrlPath(l.modulePath)),
22734
+ routeSlots: routeSlots.map((slot) => ({
22735
+ ...slot,
22736
+ modulePath: toUrlPath(slot.modulePath)
22737
+ })),
22738
+ interception: routeSlots.some((slot) => slot.interception) ? {
22739
+ from: interceptFromHeader,
22740
+ slots: routeSlots.filter((slot) => slot.interception).map((slot) => slot.name)
22741
+ } : void 0,
21901
22742
  i18n: getFarmI18nClientSnapshot()
21902
22743
  };
21903
22744
  await sendWebResponse(
@@ -22233,6 +23074,7 @@ function farmPlugin(options = {}, initialPluginManager) {
22233
23074
  export const getManifest = () => ({
22234
23075
  routes: {},
22235
23076
  layouts: {},
23077
+ slots: [],
22236
23078
  clientEntry: "/@farm/client.js",
22237
23079
  sharedAssets: []
22238
23080
  });
@@ -22243,6 +23085,7 @@ export const getManifest = () => ({
22243
23085
  clientEntry: "/@farm/client.js",
22244
23086
  routes: {},
22245
23087
  layouts: {},
23088
+ slots: [],
22246
23089
  sharedAssets: [
22247
23090
  {
22248
23091
  tag: "link",
@@ -22272,6 +23115,13 @@ export const getManifest = () => ({
22272
23115
  assets: []
22273
23116
  };
22274
23117
  }
23118
+ for (const slot of manifest.slots) {
23119
+ fullManifest.slots.push({
23120
+ ...slot,
23121
+ preloads: [slot.modulePath],
23122
+ assets: []
23123
+ });
23124
+ }
22275
23125
  return `
22276
23126
  // Auto-generated manifest for SPA navigation (TanStack Start pattern)
22277
23127
  // This manifest is inlined in the server bundle - no file on disk
@@ -22908,7 +23758,7 @@ window.__FARM_WRAP_PROVIDERS__ = wrapWithIntegrationProviders;
22908
23758
 
22909
23759
  // Get manifest from window (inlined by server in HTML)
22910
23760
  // Fallback to empty manifest if not available yet
22911
- const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, clientEntry: '', sharedAssets: [] };
23761
+ const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, slots: [], clientEntry: '', sharedAssets: [] };
22912
23762
 
22913
23763
  // ====== CLIENT-SIDE ROUTE MATCHING ======
22914
23764
  // Matches URL to route using manifest (no server request!)
@@ -23247,6 +24097,9 @@ let LayoutComponent = null;
23247
24097
 
23248
24098
  // Track if we've taken over rendering from SSR
23249
24099
  let hasClientTakenOver = false;
24100
+ const routeSlotRoots = new Map();
24101
+ const routeSlotDefinitions = new Map();
24102
+ let activeRouteInterception = null;
23250
24103
 
23251
24104
  function normalizeServerProps(rawProps) {
23252
24105
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -23259,6 +24112,123 @@ function normalizeServerProps(rawProps) {
23259
24112
  return props;
23260
24113
  }
23261
24114
 
24115
+ function getRouteSlotKey(slot) {
24116
+ return slot.ownerPattern + ':' + slot.name;
24117
+ }
24118
+
24119
+ async function renderRouteSlot(slot, mode = 'render') {
24120
+ const container = document.getElementById(slot.containerId);
24121
+ if (!container || !slot.modulePath) return false;
24122
+ if (mode === 'intercept' && !slot.isClientComponent && !slot.shouldHydrate) {
24123
+ return false;
24124
+ }
24125
+
24126
+ let slotModule = pageModuleCache.get(slot.modulePath);
24127
+ if (!slotModule) {
24128
+ slotModule = await import(/* @vite-ignore */ slot.modulePath);
24129
+ pageModuleCache.set(slot.modulePath, slotModule);
24130
+ }
24131
+ const SlotComponent = slotModule?.default;
24132
+ if (!SlotComponent) return false;
24133
+
24134
+ const props = reviveDeferredData(
24135
+ normalizeServerProps(slot.props || {}),
24136
+ window.__FARM_DEFERRED_DATA__ || {},
24137
+ );
24138
+ const element = wrapWithIntegrationProviders(
24139
+ React.createElement(SlotComponent, props),
24140
+ );
24141
+ const key = getRouteSlotKey(slot);
24142
+ const existingRoot = routeSlotRoots.get(key);
24143
+
24144
+ if (mode === 'hydrate') {
24145
+ if (existingRoot) return true;
24146
+ const root = hydrateRoot(container, element);
24147
+ routeSlotRoots.set(key, root);
24148
+ } else {
24149
+ if (existingRoot) {
24150
+ try { existingRoot.unmount(); } catch (error) {}
24151
+ }
24152
+ const root = createRoot(container);
24153
+ root.render(element);
24154
+ routeSlotRoots.set(key, root);
24155
+ }
24156
+
24157
+ routeSlotDefinitions.set(key, slot);
24158
+ return true;
24159
+ }
24160
+
24161
+ async function hydrateInitialRouteSlots() {
24162
+ const slots = Array.isArray(window.__FARM_ROUTE_SLOTS__)
24163
+ ? window.__FARM_ROUTE_SLOTS__
24164
+ : [];
24165
+ let hydrated = false;
24166
+
24167
+ for (const slot of slots) {
24168
+ routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
24169
+ if (!slot.isClientComponent && !slot.shouldHydrate) continue;
24170
+ try {
24171
+ hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
24172
+ } catch (error) {
24173
+ console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
24174
+ }
24175
+ }
24176
+
24177
+ return hydrated;
24178
+ }
24179
+
24180
+ async function renderRouteInterception(pageData) {
24181
+ const slots = Array.isArray(pageData.routeSlots)
24182
+ ? pageData.routeSlots.filter((slot) => slot.interception)
24183
+ : [];
24184
+ if (!pageData.interception || slots.length === 0) return false;
24185
+
24186
+ const previous = new Map();
24187
+ for (const slot of slots) {
24188
+ const key = getRouteSlotKey(slot);
24189
+ previous.set(key, routeSlotDefinitions.get(key) || null);
24190
+ if (!(await renderRouteSlot(slot, 'intercept'))) {
24191
+ return false;
24192
+ }
24193
+ }
24194
+
24195
+ activeRouteInterception = {
24196
+ from: pageData.interception.from,
24197
+ slots,
24198
+ previous,
24199
+ };
24200
+ return true;
24201
+ }
24202
+
24203
+ async function clearRouteInterception(destination) {
24204
+ if (!activeRouteInterception) return false;
24205
+ const active = activeRouteInterception;
24206
+ activeRouteInterception = null;
24207
+
24208
+ for (const slot of active.slots) {
24209
+ const key = getRouteSlotKey(slot);
24210
+ const root = routeSlotRoots.get(key);
24211
+ if (root) {
24212
+ try { root.unmount(); } catch (error) {}
24213
+ routeSlotRoots.delete(key);
24214
+ }
24215
+
24216
+ const previous = active.previous.get(key);
24217
+ if (previous && (previous.isClientComponent || previous.shouldHydrate)) {
24218
+ await renderRouteSlot(previous, 'render');
24219
+ } else {
24220
+ const container = document.getElementById(slot.containerId);
24221
+ if (container) container.replaceChildren();
24222
+ if (previous) routeSlotDefinitions.set(key, previous);
24223
+ else routeSlotDefinitions.delete(key);
24224
+ }
24225
+ }
24226
+
24227
+ if (!active.from) return false;
24228
+ const background = new URL(active.from, window.location.origin);
24229
+ return destination === background.pathname + background.search;
24230
+ }
24231
+
23262
24232
  function applyCanonicalPathFromProps(props) {
23263
24233
  const canonicalPath = props && typeof props.__farmCanonicalPath === 'string'
23264
24234
  ? props.__farmCanonicalPath
@@ -23493,6 +24463,17 @@ async function tryHydrateImportedPage(
23493
24463
  async function renderPage(pageData) {
23494
24464
  const container = document.getElementById('root');
23495
24465
  if (!container) return;
24466
+ const destination = window.location.pathname + window.location.search;
24467
+
24468
+ if (pageData.interception) {
24469
+ if (await renderRouteInterception(pageData)) return;
24470
+ window.location.assign(destination);
24471
+ return;
24472
+ }
24473
+
24474
+ if (await clearRouteInterception(destination)) {
24475
+ return;
24476
+ }
23496
24477
 
23497
24478
  const route = {
23498
24479
  modulePath: pageData.modulePath,
@@ -23721,7 +24702,9 @@ async function hydrate() {
23721
24702
  window.__FARM_SHOULD_HYDRATE__ === true ||
23722
24703
  isClientComponent ||
23723
24704
  await moduleLooksClient(modulePath);
24705
+ const hydratedSlots = await hydrateInitialRouteSlots();
23724
24706
  if (!shouldHydrate) {
24707
+ if (hydratedSlots) replayPreHydrationClicks();
23725
24708
  console.log('[Farm.js] Server component - SPA router ready')
23726
24709
  return
23727
24710
  }
@@ -23983,7 +24966,7 @@ async function defineConfig2(config = {}) {
23983
24966
  const isExposed = hostConfig === true || hostConfig === "0.0.0.0";
23984
24967
  console.log("");
23985
24968
  console.log(
23986
- ` ${pc.bold(pc.green("Farm.js"))} ${pc.dim("v1.0.0")} ${pc.dim(`ready in ${elapsed}ms`)}`
24969
+ ` ${pc.bold(pc.green("Farm.js"))} ${pc.dim(`v${FARM_VERSION}`)} ${pc.dim(`ready in ${elapsed}ms`)}`
23987
24970
  );
23988
24971
  console.log("");
23989
24972
  console.log(
@@ -24107,6 +25090,7 @@ var init_vite = __esm({
24107
25090
  path23 = __toESM(require("path"));
24108
25091
  init_layers();
24109
25092
  init_environment_vite();
25093
+ init_version();
24110
25094
  init_deferred();
24111
25095
  init_after();
24112
25096
  init_deployment();
@@ -25322,14 +26306,12 @@ export default defineEventHandler(async (event: H3Event) => {
25322
26306
  ...preset === "cloudflare-module" && cloudflareCronConfig ? {
25323
26307
  cloudflare: cloudflareCronConfig
25324
26308
  } : {},
25325
- handlers: [
25326
- ...farmWorkflows.handlerPath ? [
25327
- {
25328
- route: `${config.workflows.route}/**`,
25329
- handler: farmWorkflows.handlerPath
25330
- }
25331
- ] : []
25332
- ],
26309
+ handlers: farmWorkflows.handlerPath ? [
26310
+ {
26311
+ route: `${config.workflows.route}/**`,
26312
+ handler: farmWorkflows.handlerPath
26313
+ }
26314
+ ] : [],
25333
26315
  // Route rules for Vercel
25334
26316
  routeRules: {
25335
26317
  "/api/**": {
@@ -25723,8 +26705,8 @@ function compareRuntimeRoutes(left, right) {
25723
26705
  __name(compareRuntimeRoutes, "compareRuntimeRoutes");
25724
26706
  function getPatternSpecificity(pattern) {
25725
26707
  return normalizePattern(pattern).split("/").filter(Boolean).reduce((score, segment) => {
25726
- if (segment === "**" || /^\[\[\.\.\./.test(segment)) return score + 1;
25727
- if (segment === "*" || /^\[\.\.\./.test(segment)) return score + 10;
26708
+ if (segment === "**" || segment.startsWith("[[...")) return score + 1;
26709
+ if (segment === "*" || segment.startsWith("[...")) return score + 10;
25728
26710
  if (/^\[.+\]$/.test(segment)) return score + 50;
25729
26711
  return score + 100;
25730
26712
  }, 0);
@@ -26235,6 +27217,18 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
26235
27217
  ...isFarmMarkdownPageFile(entry.modulePath) ? { source: (0, import_fs5.readFileSync)(entry.modulePath, "utf8") } : {}
26236
27218
  });
26237
27219
  }
27220
+ const routeSlots = Array.from(
27221
+ routeManager.getRouteSlots().values(),
27222
+ (entry) => ({
27223
+ name: entry.name,
27224
+ ownerPattern: entry.ownerPattern,
27225
+ pattern: entry.pattern,
27226
+ modulePath: entry.modulePath,
27227
+ containerId: entry.containerId,
27228
+ interception: entry.interception,
27229
+ fallback: entry.fallback
27230
+ })
27231
+ );
26238
27232
  logger.info(`\u{1F4CB} Found ${pageRoutes.length} page routes`);
26239
27233
  const fs8 = await import("fs/promises");
26240
27234
  const layoutRoutes = [];
@@ -26278,7 +27272,16 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
26278
27272
  logger.info("\u{1F4E6} Building client and SSR bundles in parallel...");
26279
27273
  const [clientBuildResult, ssrBuildResult] = await Promise.allSettled([
26280
27274
  // Client build (to disk)
26281
- buildClient(productionVite, config, root, srcDir, clientOutputDir, pageRoutes, layoutRoutes),
27275
+ buildClient(
27276
+ productionVite,
27277
+ config,
27278
+ root,
27279
+ srcDir,
27280
+ clientOutputDir,
27281
+ pageRoutes,
27282
+ layoutRoutes,
27283
+ routeSlots
27284
+ ),
26282
27285
  // SSR build (in memory)
26283
27286
  buildSSRInMemory(
26284
27287
  productionVite,
@@ -26289,7 +27292,8 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
26289
27292
  serverRenderer,
26290
27293
  preset,
26291
27294
  pageRoutes,
26292
- layoutRoutes
27295
+ layoutRoutes,
27296
+ routeSlots
26293
27297
  )
26294
27298
  ]);
26295
27299
  const [routeRuntimeManifestResult] = await routeRuntimeManifestResultPromise;
@@ -26309,7 +27313,8 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
26309
27313
  srcDir,
26310
27314
  clientOutputDir,
26311
27315
  pageRoutes,
26312
- layoutRoutes
27316
+ layoutRoutes,
27317
+ routeSlots
26313
27318
  );
26314
27319
  }
26315
27320
  const ssrResult = ssrBuildResult.value;
@@ -26412,7 +27417,7 @@ async function validateClientBuildOutput(root, srcDir, outputDir) {
26412
27417
  }
26413
27418
  }
26414
27419
  __name(validateClientBuildOutput, "validateClientBuildOutput");
26415
- async function buildClient(productionVite, config, root, srcDir, outputDir, pageRoutes, layoutRoutes = []) {
27420
+ async function buildClient(productionVite, config, root, srcDir, outputDir, pageRoutes, layoutRoutes = [], routeSlots = []) {
26416
27421
  const viteBuild = productionVite.build;
26417
27422
  const { farmPlugin: farmPlugin2 } = await Promise.resolve().then(() => (init_vite(), vite_exports));
26418
27423
  const { PluginManager: PluginManager2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
@@ -26439,8 +27444,16 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
26439
27444
  logger.warn(`\u26A0\uFE0F Could not inspect route file ${route.modulePath}: ${error}`);
26440
27445
  }
26441
27446
  }
27447
+ const clientRouteSlots = routeSlots.filter((slot) => {
27448
+ try {
27449
+ return getClientModuleMetadata(slot.modulePath, root).shouldHydrate;
27450
+ } catch (error) {
27451
+ logger.warn(`\u26A0\uFE0F Could not inspect route slot ${slot.modulePath}: ${error}`);
27452
+ return false;
27453
+ }
27454
+ });
26442
27455
  logger.info(
26443
- `\u{1F4F1} Total hydratable routes detected: ${clientPages.length} out of ${pageRoutes.length} pages`
27456
+ `\u{1F4F1} Total hydratable routes detected: ${clientPages.length} pages and ${clientRouteSlots.length} slots`
26444
27457
  );
26445
27458
  const clientEntryRoot = import_path12.default.join(root, ".farm", "tmp");
26446
27459
  await fs8.mkdir(clientEntryRoot, { recursive: true });
@@ -26449,6 +27462,7 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
26449
27462
  const clientHydrationCode = generateClientHydrationEntry(
26450
27463
  clientPages,
26451
27464
  layoutRoutes,
27465
+ clientRouteSlots,
26452
27466
  root,
26453
27467
  srcDir,
26454
27468
  isFarmDocsSearchEnabled(config.docs),
@@ -26781,7 +27795,7 @@ function generateUniversalRouterStateProperties() {
26781
27795
  `.trim();
26782
27796
  }
26783
27797
  __name(generateUniversalRouterStateProperties, "generateUniversalRouterStateProperties");
26784
- function generateClientHydrationEntry(clientPages, layoutRoutes, root, srcDir, docsSearchEnabled, docsSearchModuleId, i18nConfig = {
27798
+ function generateClientHydrationEntry(clientPages, layoutRoutes, clientRouteSlots, root, srcDir, docsSearchEnabled, docsSearchModuleId, i18nConfig = {
26785
27799
  enabled: false,
26786
27800
  locales: ["en"],
26787
27801
  defaultLocale: "en",
@@ -26849,7 +27863,7 @@ function isFarmLocaleDocumentChange() {
26849
27863
  return false;
26850
27864
  }
26851
27865
  `;
26852
- if (clientPages.length === 0) {
27866
+ if (clientPages.length === 0 && clientRouteSlots.length === 0) {
26853
27867
  return `
26854
27868
  // Farm.js Client Runtime (no client components)
26855
27869
  ${cssImport}
@@ -27099,11 +28113,25 @@ document.addEventListener("click", function(e) {
27099
28113
  }
27100
28114
  const imports = [];
27101
28115
  const routeEntries = [];
28116
+ const routeSlotEntries = [];
27102
28117
  clientPages.forEach((page2, index) => {
27103
28118
  const importPath = toImportPath(page2.modulePath);
27104
28119
  imports.push(`import Page${index} from "${importPath}";`);
27105
28120
  routeEntries.push(` { pattern: ${JSON.stringify(page2.pattern)}, Component: Page${index} }`);
27106
28121
  });
28122
+ clientRouteSlots.forEach((slot, index) => {
28123
+ const importPath = toImportPath(slot.modulePath);
28124
+ imports.push(`import RouteSlot${index} from "${importPath}";`);
28125
+ routeSlotEntries.push(` {
28126
+ name: ${JSON.stringify(slot.name)},
28127
+ ownerPattern: ${JSON.stringify(slot.ownerPattern)},
28128
+ pattern: ${JSON.stringify(slot.pattern)},
28129
+ containerId: ${JSON.stringify(slot.containerId)},
28130
+ interception: ${JSON.stringify(slot.interception)},
28131
+ fallback: ${JSON.stringify(slot.fallback)},
28132
+ Component: RouteSlot${index},
28133
+ }`);
28134
+ });
27107
28135
  return `
27108
28136
  // Farm.js Client Runtime - SPA with Hydration
27109
28137
  ${cssImport}
@@ -27125,6 +28153,10 @@ const clientRoutes = [
27125
28153
  ${routeEntries.join(",\n")}
27126
28154
  ];
27127
28155
 
28156
+ const clientRouteSlots = [
28157
+ ${routeSlotEntries.join(",\n")}
28158
+ ];
28159
+
27128
28160
  // Layout routes for wrapping client components
27129
28161
  const layoutRoutes = [
27130
28162
  ${layoutRegistrations.join(",\n")}
@@ -27179,6 +28211,146 @@ function matchRoute(pathname) {
27179
28211
  return null;
27180
28212
  }
27181
28213
 
28214
+ function matchesRoutePrefix(pathname, pattern) {
28215
+ if (pattern === "/") return true;
28216
+ const pathSegments = getFarmRoutePathname(pathname).split("/").filter(Boolean);
28217
+ const patternSegments = pattern.split("/").filter(Boolean);
28218
+ if (patternSegments.length > pathSegments.length) return false;
28219
+ const candidate = "/" + pathSegments.slice(0, patternSegments.length).join("/");
28220
+ return matchFarmRoute(pattern, candidate) !== null;
28221
+ }
28222
+
28223
+ function matchInterceptedRouteSlot(pathname, from) {
28224
+ for (const slot of clientRouteSlots) {
28225
+ if (!slot.interception || !matchesRoutePrefix(from, slot.ownerPattern)) continue;
28226
+ const params = matchFarmRoute(slot.pattern, getFarmRoutePathname(pathname));
28227
+ if (params !== null) return { slot: slot, params: params };
28228
+ }
28229
+ return null;
28230
+ }
28231
+
28232
+ function routeSlotKey(slot) {
28233
+ return slot.ownerPattern + ":" + slot.name;
28234
+ }
28235
+
28236
+ function findClientRouteSlot(slot) {
28237
+ return clientRouteSlots.find(function(candidate) {
28238
+ return candidate.name === slot.name &&
28239
+ candidate.ownerPattern === slot.ownerPattern &&
28240
+ candidate.pattern === slot.pattern &&
28241
+ candidate.interception === slot.interception &&
28242
+ candidate.fallback === slot.fallback;
28243
+ });
28244
+ }
28245
+
28246
+ function readRouteSlotPayload(doc) {
28247
+ const script = doc.getElementById("__farm_route_slots_data__");
28248
+ if (!script?.textContent) return [];
28249
+ try {
28250
+ const parsed = JSON.parse(script.textContent);
28251
+ return Array.isArray(parsed) ? parsed : [];
28252
+ } catch {
28253
+ return [];
28254
+ }
28255
+ }
28256
+
28257
+ const routeSlotRoots = new Map();
28258
+ let activeRouteInterception = null;
28259
+
28260
+ function resetRouteSlotRoots() {
28261
+ for (const root of routeSlotRoots.values()) {
28262
+ try { root.unmount(); } catch {}
28263
+ }
28264
+ routeSlotRoots.clear();
28265
+ activeRouteInterception = null;
28266
+ }
28267
+
28268
+ function renderClientRouteSlot(slot, registration, mode) {
28269
+ const container = document.getElementById(slot.containerId);
28270
+ if (!container || !registration?.Component) return false;
28271
+ const key = routeSlotKey(slot);
28272
+ const existingRoot = routeSlotRoots.get(key);
28273
+ const props = slot.props && typeof slot.props === "object" ? slot.props : {};
28274
+ const element = React.createElement(registration.Component, props);
28275
+
28276
+ if (mode === "hydrate") {
28277
+ if (existingRoot) return true;
28278
+ const root = hydrateRoot(container, element);
28279
+ routeSlotRoots.set(key, root);
28280
+ return true;
28281
+ }
28282
+
28283
+ if (existingRoot) {
28284
+ try { existingRoot.unmount(); } catch {}
28285
+ routeSlotRoots.delete(key);
28286
+ } else {
28287
+ container.replaceChildren();
28288
+ }
28289
+ const root = createRoot(container);
28290
+ root.render(element);
28291
+ routeSlotRoots.set(key, root);
28292
+ return true;
28293
+ }
28294
+
28295
+ function hydrateInitialRouteSlots() {
28296
+ const slots = Array.isArray(window.__FARM_ROUTE_SLOTS__)
28297
+ ? window.__FARM_ROUTE_SLOTS__
28298
+ : [];
28299
+ for (const slot of slots) {
28300
+ const registration = findClientRouteSlot(slot);
28301
+ if (!registration) continue;
28302
+ try {
28303
+ renderClientRouteSlot(slot, registration, "hydrate");
28304
+ } catch (error) {
28305
+ console.warn("[Farm.js] Could not hydrate route slot:", slot.name, error);
28306
+ }
28307
+ }
28308
+ }
28309
+
28310
+ function renderRouteInterception(slot, registration, from) {
28311
+ const container = document.getElementById(slot.containerId);
28312
+ if (!container) return false;
28313
+ const key = routeSlotKey(slot);
28314
+ const previousSlot = (window.__FARM_ROUTE_SLOTS__ || []).find(function(candidate) {
28315
+ return routeSlotKey(candidate) === key;
28316
+ }) || null;
28317
+ const previousHtml = container.innerHTML;
28318
+
28319
+ if (!renderClientRouteSlot(slot, registration, "render")) return false;
28320
+ activeRouteInterception = {
28321
+ from: from,
28322
+ key: key,
28323
+ slot: slot,
28324
+ previousSlot: previousSlot,
28325
+ previousHtml: previousHtml,
28326
+ };
28327
+ return true;
28328
+ }
28329
+
28330
+ function clearRouteInterception(destination) {
28331
+ if (!activeRouteInterception) return false;
28332
+ const active = activeRouteInterception;
28333
+ activeRouteInterception = null;
28334
+ const root = routeSlotRoots.get(active.key);
28335
+ if (root) {
28336
+ try { root.unmount(); } catch {}
28337
+ routeSlotRoots.delete(active.key);
28338
+ }
28339
+ const container = document.getElementById(active.slot.containerId);
28340
+ if (container) {
28341
+ container.innerHTML = active.previousHtml;
28342
+ if (active.previousSlot) {
28343
+ const registration = findClientRouteSlot(active.previousSlot);
28344
+ if (registration) {
28345
+ try {
28346
+ renderClientRouteSlot(active.previousSlot, registration, "hydrate");
28347
+ } catch {}
28348
+ }
28349
+ }
28350
+ }
28351
+ return destination === active.from;
28352
+ }
28353
+
27182
28354
  // State
27183
28355
  let reactRoot = null;
27184
28356
  let currentPathname = null;
@@ -27197,12 +28369,17 @@ function resetReactRoot() {
27197
28369
  async function hydrate() {
27198
28370
  const pathname = window.location.pathname;
27199
28371
  const matched = matchRoute(pathname);
27200
-
28372
+ hydrateInitialRouteSlots();
28373
+
27201
28374
  if (!matched) {
27202
28375
  return;
27203
28376
  }
27204
28377
 
27205
- const container = document.getElementById("root");
28378
+ const hasRouteSlots =
28379
+ Array.isArray(window.__FARM_ROUTE_SLOTS__) && window.__FARM_ROUTE_SLOTS__.length > 0;
28380
+ const container =
28381
+ (hasRouteSlots ? document.getElementById("__farm_page__") : null) ||
28382
+ document.getElementById("root");
27206
28383
  if (!container) {
27207
28384
  console.error("[Farm.js] No root element found");
27208
28385
  return;
@@ -27216,7 +28393,9 @@ async function hydrate() {
27216
28393
 
27217
28394
  // Create page element and wrap with layouts
27218
28395
  const pageElement = React.createElement(Component, props);
27219
- const wrappedElement = wrapWithLayouts(pageElement, pathname, params);
28396
+ const wrappedElement = hasRouteSlots
28397
+ ? pageElement
28398
+ : wrapWithLayouts(pageElement, pathname, params);
27220
28399
 
27221
28400
  const shouldHydrate = !isHydrated && Boolean(container.innerHTML.trim());
27222
28401
  const hydrationSession = await farmClientRuntime.beginHydration({
@@ -27281,6 +28460,57 @@ ${generateUniversalRouterStateProperties()}
27281
28460
  : undefined,
27282
28461
  });
27283
28462
 
28463
+ if (activeRouteInterception && clearRouteInterception(to)) {
28464
+ await farmClientRuntime.markNavigationLoaded(clientNavigation, {
28465
+ routeSlots: "restored",
28466
+ });
28467
+ await farmClientRuntime.resolveNavigation(clientNavigation);
28468
+ void farmClientRuntime.scheduleNavigationRendered(clientNavigation);
28469
+ this.currentPath = to;
28470
+ this.finishNavigation();
28471
+ return;
28472
+ }
28473
+ if (activeRouteInterception) {
28474
+ clearRouteInterception(to);
28475
+ }
28476
+
28477
+ const intercepted = matchInterceptedRouteSlot(pathname, from);
28478
+ if (intercepted) {
28479
+ const html = await this.fetchPage(to, from);
28480
+ const doc = new DOMParser().parseFromString(html, "text/html");
28481
+ const slotPayload = readRouteSlotPayload(doc);
28482
+ const selectedSlot = slotPayload.find(function(slot) {
28483
+ return slot.interception &&
28484
+ slot.name === intercepted.slot.name &&
28485
+ slot.ownerPattern === intercepted.slot.ownerPattern;
28486
+ });
28487
+
28488
+ if (
28489
+ selectedSlot &&
28490
+ renderRouteInterception(selectedSlot, intercepted.slot, from)
28491
+ ) {
28492
+ await farmClientRuntime.markNavigationLoaded(clientNavigation, {
28493
+ route: intercepted.slot.pattern,
28494
+ params: intercepted.params,
28495
+ });
28496
+ const historyState = createHistoryState(
28497
+ to,
28498
+ options.state,
28499
+ window.history.state,
28500
+ );
28501
+ if (action === "replace") {
28502
+ window.history.replaceState(historyState, "", url);
28503
+ } else if (action !== "pop") {
28504
+ window.history.pushState(historyState, "", url);
28505
+ }
28506
+ await farmClientRuntime.resolveNavigation(clientNavigation);
28507
+ void farmClientRuntime.scheduleNavigationRendered(clientNavigation);
28508
+ this.currentPath = to;
28509
+ this.finishNavigation();
28510
+ return;
28511
+ }
28512
+ }
28513
+
27284
28514
  if (matched) {
27285
28515
  const Component = matched.route.Component;
27286
28516
  const params = matched.params;
@@ -27357,21 +28587,28 @@ ${generateUniversalRouterStateProperties()}
27357
28587
  }
27358
28588
  },
27359
28589
 
27360
- fetchPage: async function(url) {
27361
- const cached = this.prefetchCache.get(url);
28590
+ fetchPage: async function(url, interceptFrom) {
28591
+ const cacheKey = interceptFrom ? url + "\\nintercept:" + interceptFrom : url;
28592
+ const cached = this.prefetchCache.get(cacheKey);
27362
28593
  if (cached) return cached;
27363
28594
 
27364
28595
  const response = await fetch(url, {
27365
- headers: { "Accept": "text/html" }
28596
+ headers: {
28597
+ "Accept": "text/html",
28598
+ ...(interceptFrom ? { "X-Farm-Intercept-From": interceptFrom } : {}),
28599
+ }
27366
28600
  });
27367
28601
  if (!response.ok) throw new Error("Failed to fetch page");
27368
- return response.text();
28602
+ const html = await response.text();
28603
+ this.prefetchCache.set(cacheKey, html);
28604
+ return html;
27369
28605
  },
27370
28606
 
27371
28607
  swapContent: function(html, targetPath) {
27372
28608
  const parser = new DOMParser();
27373
28609
  const doc = parser.parseFromString(html, "text/html");
27374
28610
  if (isFarmLocaleDocumentChange(doc)) return false;
28611
+ const nextRouteSlots = readRouteSlotPayload(doc);
27375
28612
 
27376
28613
  // Update title
27377
28614
  const newTitle = doc.querySelector("title");
@@ -27396,7 +28633,10 @@ ${generateUniversalRouterStateProperties()}
27396
28633
  const currentRoot = document.getElementById("root");
27397
28634
  if (!newRoot || !currentRoot) return this.swapDocument(doc);
27398
28635
  resetReactRoot();
28636
+ resetRouteSlotRoots();
27399
28637
  currentRoot.innerHTML = newRoot.innerHTML;
28638
+ window.__FARM_ROUTE_SLOTS__ = nextRouteSlots;
28639
+ hydrateInitialRouteSlots();
27400
28640
 
27401
28641
  // Check if new page has a client component
27402
28642
  const targetUrl = new URL(targetPath || window.location.href, window.location.origin);
@@ -27408,13 +28648,15 @@ ${generateUniversalRouterStateProperties()}
27408
28648
  const params = matched.params;
27409
28649
  const searchParams = Object.fromEntries(targetUrl.searchParams);
27410
28650
  const props = { params: params, searchParams: Promise.resolve(searchParams) };
27411
-
27412
- if (!reactRoot) {
27413
- reactRoot = createRoot(currentRoot);
27414
- }
27415
28651
  const pageElement = React.createElement(Component, props);
27416
- const wrappedElement = wrapWithLayouts(pageElement, newPathname, params);
27417
- reactRoot.render(wrappedElement);
28652
+ const hasRouteSlots = nextRouteSlots.length > 0;
28653
+ const pageContainer =
28654
+ (hasRouteSlots ? document.getElementById("__farm_page__") : null) || currentRoot;
28655
+ const wrappedElement = hasRouteSlots
28656
+ ? pageElement
28657
+ : wrapWithLayouts(pageElement, newPathname, params);
28658
+ reactRoot = hydrateRoot(pageContainer, wrappedElement);
28659
+ isHydrated = true;
27418
28660
  }
27419
28661
  return true;
27420
28662
  },
@@ -27423,6 +28665,7 @@ ${generateUniversalRouterStateProperties()}
27423
28665
  if (!doc.documentElement || !doc.body) return false;
27424
28666
 
27425
28667
  resetReactRoot();
28668
+ resetRouteSlotRoots();
27426
28669
 
27427
28670
  Array.from(document.documentElement.attributes).forEach(function(attr) {
27428
28671
  if (!doc.documentElement.hasAttribute(attr.name)) {
@@ -27456,10 +28699,12 @@ ${generateUniversalRouterStateProperties()}
27456
28699
  if (url.origin !== window.location.origin) return;
27457
28700
 
27458
28701
  const pathname = url.pathname + url.search;
27459
- if (this.prefetchCache.has(pathname)) return;
28702
+ const interceptFrom = this.currentPath;
28703
+ const cacheKey = pathname + "\\nintercept:" + interceptFrom;
28704
+ if (this.prefetchCache.has(cacheKey)) return;
27460
28705
 
27461
- this.fetchPage(pathname)
27462
- .then(function(html) { spaRouter.prefetchCache.set(pathname, html); })
28706
+ this.fetchPage(pathname, interceptFrom)
28707
+ .then(function(html) { spaRouter.prefetchCache.set(cacheKey, html); })
27463
28708
  .catch(function() {});
27464
28709
  },
27465
28710
 
@@ -27550,7 +28795,7 @@ if (isFarmDocsSearchPage()) {
27550
28795
  `.trim();
27551
28796
  }
27552
28797
  __name(generateClientHydrationEntry, "generateClientHydrationEntry");
27553
- async function buildSSRInMemory(productionVite, config, root, routeManager, apiRouteManager, serverRenderer, preset, collectedPageRoutes, collectedLayoutRoutes) {
28798
+ async function buildSSRInMemory(productionVite, config, root, routeManager, apiRouteManager, serverRenderer, preset, collectedPageRoutes, collectedLayoutRoutes, collectedRouteSlots) {
27554
28799
  const viteBuild = productionVite.build;
27555
28800
  const { farmPlugin: farmPlugin2 } = await Promise.resolve().then(() => (init_vite(), vite_exports));
27556
28801
  const { PluginManager: PluginManager2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
@@ -27583,6 +28828,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
27583
28828
  let ssrEntryFile;
27584
28829
  const pageRoutes = collectedPageRoutes.map((route) => ({ ...route }));
27585
28830
  const layoutRoutes = collectedLayoutRoutes.map((route) => ({ ...route }));
28831
+ const routeSlots = collectedRouteSlots.map((slot) => ({ ...slot }));
27586
28832
  const metadataImageRoutes = [];
27587
28833
  for (const entry of routeManager.getMetadataImages().values()) {
27588
28834
  if (entry.sourceType === "static") {
@@ -27683,6 +28929,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
27683
28929
  apiRoutes,
27684
28930
  pageRoutes,
27685
28931
  layoutRoutes,
28932
+ routeSlots,
27686
28933
  errorRoutes,
27687
28934
  metadataImageRoutes,
27688
28935
  middlewareRoutes,
@@ -27853,10 +29100,14 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
27853
29100
  }
27854
29101
  }
27855
29102
  }
27856
- return { bundle: ssrBundle, entryFile: ssrEntryFile, configuredHeaderRoutes };
29103
+ return {
29104
+ bundle: ssrBundle,
29105
+ entryFile: ssrEntryFile,
29106
+ configuredHeaderRoutes
29107
+ };
27857
29108
  }
27858
29109
  __name(buildSSRInMemory, "buildSSRInMemory");
27859
- function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRoutes, metadataImageRoutes, middlewareRoutes, redirectRoutes, configuredRewriteRoutes, configuredHeaderRoutes, notFoundPath, config, configModulePath, hasServerRuntimeIntegrations, hasRuntimeIntegrationConfig, hasConfiguredRuntimePlugins, preset, i18nCatalogs) {
29110
+ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, routeSlots, errorRoutes, metadataImageRoutes, middlewareRoutes, redirectRoutes, configuredRewriteRoutes, configuredHeaderRoutes, notFoundPath, config, configModulePath, hasServerRuntimeIntegrations, hasRuntimeIntegrationConfig, hasConfiguredRuntimePlugins, preset, i18nCatalogs) {
27860
29111
  const hasPluginRuntime = hasRuntimeIntegrationConfig || hasConfiguredRuntimePlugins;
27861
29112
  const hasMiddlewareRuntime = middlewareRoutes.length > 0 || hasFarmMiddlewareConfig(config.middleware);
27862
29113
  const farmDocsBaseConfig = config.docs?.enabled ? { ...config.docs, config: void 0 } : void 0;
@@ -27900,10 +29151,12 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRout
27900
29151
  return;
27901
29152
  }
27902
29153
  pageImports.push(`import * as ${varName} from "${route.modulePath}";`);
29154
+ const shouldHydrate = getClientModuleMetadata(route.modulePath, config.root).shouldHydrate;
27903
29155
  pageRegistrations.push(`
27904
29156
  {
27905
29157
  pattern: ${JSON.stringify(route.pattern)},
27906
29158
  module: ${varName},
29159
+ shouldHydrate: ${JSON.stringify(shouldHydrate)},
27907
29160
  }`);
27908
29161
  });
27909
29162
  const layoutImports = [];
@@ -27917,6 +29170,22 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRout
27917
29170
  module: ${varName},
27918
29171
  }`);
27919
29172
  });
29173
+ const routeSlotImports = [];
29174
+ const routeSlotRegistrations = [];
29175
+ routeSlots.forEach((slot, index) => {
29176
+ const varName = `routeSlot${index}`;
29177
+ routeSlotImports.push(`import * as ${varName} from "${slot.modulePath}";`);
29178
+ routeSlotRegistrations.push(`
29179
+ {
29180
+ name: ${JSON.stringify(slot.name)},
29181
+ ownerPattern: ${JSON.stringify(slot.ownerPattern)},
29182
+ pattern: ${JSON.stringify(slot.pattern)},
29183
+ containerId: ${JSON.stringify(slot.containerId)},
29184
+ interception: ${JSON.stringify(slot.interception)},
29185
+ fallback: ${JSON.stringify(slot.fallback)},
29186
+ module: ${varName},
29187
+ }`);
29188
+ });
27920
29189
  const errorImports = [];
27921
29190
  const errorRegistrations = [];
27922
29191
  errorRoutes.forEach((errorRoute, index) => {
@@ -27967,6 +29236,7 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRout
27967
29236
  _runWithMiddlewareData,
27968
29237
  addMetadataImageReference,
27969
29238
  applyProductionMiddlewareHeaders,
29239
+ configureFarmCache,
27970
29240
  configureFarmObservability,
27971
29241
  createFarmCacheKey,
27972
29242
  createFarmLocaleCookie,
@@ -28069,6 +29339,7 @@ async function handleAPIRequest(request) {
28069
29339
  ${apiImports.join("\n")}
28070
29340
  ${pageImports.join("\n")}
28071
29341
  ${layoutImports.join("\n")}
29342
+ ${routeSlotImports.join("\n")}
28072
29343
  ${errorImports.join("\n")}
28073
29344
  ${metadataImageImports.join("\n")}
28074
29345
  ${middlewareImports.join("\n")}
@@ -28146,6 +29417,7 @@ const farmObservabilityConfig = farmUserConfig?.observability ?? ${JSON.stringif
28146
29417
  config.observability ?? false
28147
29418
  )};
28148
29419
  configureFarmObservability(farmObservabilityConfig);
29420
+ configureFarmCache(farmResolvedRuntimeConfig.cache);
28149
29421
  const farmI18nConfig = ${JSON.stringify(config.i18n)};
28150
29422
  const farmI18nRuntime = ${config.i18n.enabled ? `createFarmI18nRuntime(farmI18nConfig, ${JSON.stringify(i18nCatalogs)})` : "null"};
28151
29423
  ${config.i18n.enabled ? "_setDefaultFarmI18nRuntime(farmI18nRuntime);" : ""}
@@ -28203,6 +29475,9 @@ for (const route of pageRoutes) {
28203
29475
  const layoutRoutes = [${layoutRegistrations.join(",")}
28204
29476
  ];
28205
29477
 
29478
+ const routeSlots = [${routeSlotRegistrations.join(",")}
29479
+ ];
29480
+
28206
29481
  // Route-level error boundaries bundled at build time.
28207
29482
  const errorRoutes = [${errorRegistrations.join(",")}
28208
29483
  ];
@@ -28237,18 +29512,21 @@ function serializeFarmInlineValue(value) {
28237
29512
  .replace(/\\u2029/g, "\\\\u2029");
28238
29513
  }
28239
29514
 
28240
- function renderFarmClientBootstrapScript(canonicalPath) {
29515
+ function renderFarmClientBootstrapScript(canonicalPath, selectedRouteSlots) {
28241
29516
  const integrationManifest = ${hasRuntimeIntegrationConfig ? "getRegisteredIntegrationAPIManifest()" : "{}"};
29517
+ const serializedRouteSlots = serializeFarmInlineValue(selectedRouteSlots || []);
28242
29518
  let source = 'window.__FARM_INTEGRATION_API_MANIFEST__=' +
28243
29519
  serializeFarmInlineValue(integrationManifest) +
28244
- ';';
29520
+ ';window.__FARM_ROUTE_SLOTS__=' + serializedRouteSlots + ';';
28245
29521
  if (typeof canonicalPath === "string" && canonicalPath.startsWith("/")) {
28246
29522
  const serializedPath = serializeFarmInlineValue(canonicalPath);
28247
29523
  source += 'if(location.pathname+location.search!==' + serializedPath + '){' +
28248
29524
  'history.replaceState(Object.assign({},history.state||{},{path:' + serializedPath + '}),"",' +
28249
29525
  serializedPath + ');}';
28250
29526
  }
28251
- return '<script>' + source + '</script>';
29527
+ return '<script id="__farm_route_slots_data__" type="application/json">' +
29528
+ serializedRouteSlots +
29529
+ '</script><script>' + source + '</script>';
28252
29530
  }
28253
29531
 
28254
29532
  function escapeFarmHtmlAttribute(value) {
@@ -28998,6 +30276,79 @@ function matchPageRoute(pathname) {
28998
30276
  return null;
28999
30277
  }
29000
30278
 
30279
+ function matchesRoutePrefix(pathname, pattern) {
30280
+ if (pattern === "/") return true;
30281
+ const pathSegments = normalizeRuntimePath(pathname).split("/").filter(Boolean);
30282
+ const patternSegments = normalizeRuntimePath(pattern).split("/").filter(Boolean);
30283
+ if (patternSegments.length > pathSegments.length) return false;
30284
+ const candidate = "/" + pathSegments.slice(0, patternSegments.length).join("/");
30285
+ return matchRuntimePathPattern(pattern, candidate) !== null;
30286
+ }
30287
+
30288
+ function routeSlotSpecificity(slot) {
30289
+ return slot.pattern.split("/").reduce(function(score, segment) {
30290
+ if (!segment) return score;
30291
+ if (segment.startsWith("[[...")) return score + 1;
30292
+ if (segment.startsWith("[...")) return score + 2;
30293
+ if (segment.startsWith("[")) return score + 3;
30294
+ return score + 10;
30295
+ }, 0);
30296
+ }
30297
+
30298
+ function matchRouteSlots(pathname, interceptFrom) {
30299
+ const groups = new Map();
30300
+ for (const slot of routeSlots) {
30301
+ if (!matchesRoutePrefix(pathname, slot.ownerPattern)) continue;
30302
+ const key = slot.ownerPattern + ":" + slot.name;
30303
+ const entries = groups.get(key) || [];
30304
+ entries.push(slot);
30305
+ groups.set(key, entries);
30306
+ }
30307
+
30308
+ const normalizedFrom =
30309
+ typeof interceptFrom === "string" && interceptFrom.startsWith("/")
30310
+ ? new URL(interceptFrom, "http://farm.local").pathname
30311
+ : null;
30312
+ const matches = [];
30313
+
30314
+ for (const entries of groups.values()) {
30315
+ const candidates = entries
30316
+ .filter(function(entry) { return !entry.fallback; })
30317
+ .filter(function(entry) {
30318
+ return !entry.interception ||
30319
+ (normalizedFrom && matchesRoutePrefix(normalizedFrom, entry.ownerPattern));
30320
+ })
30321
+ .map(function(entry) {
30322
+ return {
30323
+ entry: entry,
30324
+ params: matchRuntimePathPattern(entry.pattern, pathname),
30325
+ };
30326
+ })
30327
+ .filter(function(candidate) { return candidate.params !== null; })
30328
+ .sort(function(left, right) {
30329
+ if (left.entry.interception !== right.entry.interception) {
30330
+ return left.entry.interception ? -1 : 1;
30331
+ }
30332
+ return routeSlotSpecificity(right.entry) - routeSlotSpecificity(left.entry);
30333
+ });
30334
+
30335
+ if (candidates[0]) {
30336
+ matches.push({ ...candidates[0].entry, params: candidates[0].params });
30337
+ continue;
30338
+ }
30339
+
30340
+ const fallback = entries.find(function(entry) { return entry.fallback; });
30341
+ if (fallback) matches.push({ ...fallback, params: {} });
30342
+ }
30343
+
30344
+ return matches.sort(function(left, right) {
30345
+ const ownerDepth =
30346
+ left.ownerPattern.split("/").filter(Boolean).length -
30347
+ right.ownerPattern.split("/").filter(Boolean).length;
30348
+ return ownerDepth || left.name.localeCompare(right.name);
30349
+ });
30350
+ }
30351
+
29001
30352
  function hasLocalRequestRoute(request, routePathname) {
29002
30353
  const pathname = new URL(request.url).pathname;
29003
30354
  if (matchLocalAPIRequest(request) || matchLocalIntegrationRequest(request)) {
@@ -29148,6 +30499,7 @@ function getPPRShellBypassReason(request, middlewareData, middlewareContext) {
29148
30499
  if (method !== "GET" && method !== "HEAD") return "method";
29149
30500
  if (request.headers.get("cookie")) return "cookie";
29150
30501
  if (request.headers.get("authorization")) return "authorization";
30502
+ if (request.headers.get("x-farm-intercept-from")) return "route-interception";
29151
30503
  if (request.headers.get("x-farm-ppr-refresh")) return "refresh";
29152
30504
  if (hasConfiguredRouteContext) return "route-context";
29153
30505
  if (middlewareData?.size) return "middleware-data";
@@ -29201,8 +30553,8 @@ function getRouteSharedCacheControl(routeModule, pprCanCache) {
29201
30553
  return "public, s-maxage=" + revalidate + ", stale-while-revalidate=300";
29202
30554
  }
29203
30555
 
29204
- function getCachedPPRShell(cacheKey) {
29205
- const entry = pprShellCache.getEntry(cacheKey);
30556
+ async function getCachedPPRShell(cacheKey) {
30557
+ const entry = await pprShellCache.getEntryAsync(cacheKey);
29206
30558
  if (!entry) {
29207
30559
  return null;
29208
30560
  }
@@ -29417,7 +30769,7 @@ async function handleFarmRequestInContext(
29417
30769
  }
29418
30770
  }
29419
30771
  if (pprCacheKey) {
29420
- const cachedPPRShell = getCachedPPRShell(pprCacheKey);
30772
+ const cachedPPRShell = await getCachedPPRShell(pprCacheKey);
29421
30773
  if (cachedPPRShell) {
29422
30774
  emitFarmEvent({ type: "ppr.shell.hit", route: pathname, key: pprCacheKey });
29423
30775
  emitFarmEvent({
@@ -29445,6 +30797,10 @@ async function handleFarmRequestInContext(
29445
30797
 
29446
30798
  // Get applicable layouts for this page
29447
30799
  const applicableLayouts = getApplicableLayouts(routePathname);
30800
+ const selectedRouteSlots = matchRouteSlots(
30801
+ routePathname,
30802
+ request.headers.get("x-farm-intercept-from"),
30803
+ );
29448
30804
 
29449
30805
  if (PageComponent) {
29450
30806
  // Parse search params - make it a resolved Promise for async components
@@ -29532,6 +30888,82 @@ async function handleFarmRequestInContext(
29532
30888
  throw routeStateError;
29533
30889
  }
29534
30890
  }
30891
+
30892
+ const renderedRouteSlots = await Promise.all(
30893
+ selectedRouteSlots.map(async function(slot) {
30894
+ const slotContext = hasConfiguredRouteContext
30895
+ ? await resolveFarmRouteContext(farmResolvedRuntimeConfig, {
30896
+ request,
30897
+ params: slot.params,
30898
+ search: searchParamsObj,
30899
+ path: pathname,
30900
+ })
30901
+ : undefined;
30902
+ const rawSlotProps = withFarmRouteContext(
30903
+ {
30904
+ params: slot.params,
30905
+ searchParams: Promise.resolve(searchParamsObj),
30906
+ path: pathname,
30907
+ ...(middlewareData?.size ? { middleware: { data: middlewareData } } : {}),
30908
+ },
30909
+ slotContext,
30910
+ );
30911
+ if (slot.module.__farmResolveRouteProps && slotContext !== undefined) {
30912
+ Object.defineProperty(rawSlotProps, "context", {
30913
+ value: slotContext,
30914
+ enumerable: true,
30915
+ configurable: true,
30916
+ });
30917
+ }
30918
+
30919
+ const slotSchemas = slot.module.__farmRouteParsesProps
30920
+ ? {}
30921
+ : slot.module.__farmRouteSchemas || {};
30922
+ let slotProps = slot.module.__farmResolveRouteProps
30923
+ ? await slot.module.__farmResolveRouteProps(rawSlotProps)
30924
+ : {
30925
+ ...rawSlotProps,
30926
+ params: slotSchemas.params?.parse
30927
+ ? slotSchemas.params.parse(slot.params)
30928
+ : slot.params,
30929
+ search: slotSchemas.search?.parse
30930
+ ? slotSchemas.search.parse(searchParamsObj)
30931
+ : searchParamsObj,
30932
+ searchParams: Promise.resolve(
30933
+ slotSchemas.search?.parse
30934
+ ? slotSchemas.search.parse(searchParamsObj)
30935
+ : searchParamsObj,
30936
+ ),
30937
+ };
30938
+ if (
30939
+ slot.module.__farmResolveRouteProps &&
30940
+ slotContext !== undefined &&
30941
+ slotProps.context === slotContext
30942
+ ) {
30943
+ const { context: _slotContext, ...renderSlotProps } = slotProps;
30944
+ slotProps = renderSlotProps;
30945
+ }
30946
+
30947
+ return { ...slot, props: slotProps };
30948
+ }),
30949
+ );
30950
+ const routeSlotPayload = renderedRouteSlots.map(function(slot) {
30951
+ return {
30952
+ name: slot.name,
30953
+ ownerPattern: slot.ownerPattern,
30954
+ pattern: slot.pattern,
30955
+ containerId: slot.containerId,
30956
+ interception: slot.interception,
30957
+ fallback: slot.fallback,
30958
+ props: {
30959
+ params: slot.props.params,
30960
+ search: slot.props.search || searchParamsObj,
30961
+ searchParams: slot.props.search || searchParamsObj,
30962
+ ...("data" in slot.props ? { data: slot.props.data } : {}),
30963
+ path: pathname,
30964
+ },
30965
+ };
30966
+ });
29535
30967
 
29536
30968
  const renderPageElement = async () => {
29537
30969
  // First, render the page content
@@ -29559,6 +30991,14 @@ async function handleFarmRequestInContext(
29559
30991
  pageElement = React.createElement(PageComponent, pageProps);
29560
30992
  }
29561
30993
 
30994
+ if (route.shouldHydrate && renderedRouteSlots.length > 0) {
30995
+ pageElement = React.createElement(
30996
+ "div",
30997
+ { id: "__farm_page__", "data-farm-client": "true" },
30998
+ pageElement,
30999
+ );
31000
+ }
31001
+
29562
31002
  // Wrap with layouts (from innermost to outermost)
29563
31003
  // Layouts are sorted by depth (root first), so we process in reverse
29564
31004
  let wrappedElement = pageElement;
@@ -29566,9 +31006,23 @@ async function handleFarmRequestInContext(
29566
31006
  const layout = applicableLayouts[i];
29567
31007
  const LayoutComponent = layout.module.default;
29568
31008
  if (LayoutComponent) {
31009
+ const slotProps = {};
31010
+ for (const slot of renderedRouteSlots) {
31011
+ if (slot.ownerPattern !== layout.pattern) continue;
31012
+ slotProps[slot.name] = React.createElement(
31013
+ "div",
31014
+ {
31015
+ id: slot.containerId,
31016
+ "data-farm-route-slot": slot.name,
31017
+ "data-farm-slot-owner": slot.ownerPattern,
31018
+ },
31019
+ React.createElement(slot.module.default, slot.props),
31020
+ );
31021
+ }
29569
31022
  wrappedElement = React.createElement(LayoutComponent, {
29570
31023
  children: wrappedElement,
29571
31024
  params,
31025
+ ...slotProps,
29572
31026
  });
29573
31027
  }
29574
31028
  }
@@ -29620,6 +31074,7 @@ async function handleFarmRequestInContext(
29620
31074
  );
29621
31075
  const hasRequestScopedRender = Boolean(
29622
31076
  pageStatus >= 400 ||
31077
+ request.headers.get("x-farm-intercept-from") ||
29623
31078
  hasPrivateRequestHeaders ||
29624
31079
  hasConfiguredRouteContext ||
29625
31080
  middlewareData?.size ||
@@ -29659,7 +31114,10 @@ async function handleFarmRequestInContext(
29659
31114
  ' <link rel="modulepreload" href="/farm-client.js">\\n' +
29660
31115
  '</head>\\n<body>\\n <div id="root">';
29661
31116
  const streamSuffix = '</div>\\n' +
29662
- ' ' + renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath) + '\\n' +
31117
+ ' ' + renderFarmClientBootstrapScript(
31118
+ pageProps.__farmCanonicalPath,
31119
+ routeSlotPayload
31120
+ ) + '\\n' +
29663
31121
  ' <script type="module" src="/farm-client.js"></script>\\n' +
29664
31122
  '</body>\\n</html>';
29665
31123
  const streamedDocument = createFarmDocumentStream(
@@ -29716,7 +31174,10 @@ async function handleFarmRequestInContext(
29716
31174
  // Inject client script before closing body tag
29717
31175
  .replace(
29718
31176
  /<\\/body>/i,
29719
- ' ' + renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath) + '\\n' +
31177
+ ' ' + renderFarmClientBootstrapScript(
31178
+ pageProps.__farmCanonicalPath,
31179
+ routeSlotPayload
31180
+ ) + '\\n' +
29720
31181
  ' <script type="module" src="/farm-client.js"></script>\\n</body>',
29721
31182
  );
29722
31183
 
@@ -29737,7 +31198,7 @@ async function handleFarmRequestInContext(
29737
31198
  </head>
29738
31199
  <body>
29739
31200
  <div id="root">\${html}</div>
29740
- \${renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath)}
31201
+ \${renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath, routeSlotPayload)}
29741
31202
  <script type="module" src="/farm-client.js"></script>
29742
31203
  </body>
29743
31204
  </html>\`;
@@ -29745,7 +31206,7 @@ async function handleFarmRequestInContext(
29745
31206
  fullHtml = applyFarmI18nDocument(fullHtml, pathname, farmI18nSnapshot);
29746
31207
 
29747
31208
  if (pageStatus === 200 && pprCacheKey && request.method.toUpperCase() !== "HEAD") {
29748
- pprShellCache.set(
31209
+ await pprShellCache.setAsync(
29749
31210
  pprCacheKey,
29750
31211
  { html: fullHtml },
29751
31212
  {
@@ -30362,9 +31823,8 @@ async function buildNitroUniversal(config, routeManager, apiRouteManager, server
30362
31823
  const nitroEntryPath = import_path12.default.join(ssrOutputDir, "nitro-entry.mjs");
30363
31824
  const nitroEntryCode = `
30364
31825
  // Farm.js Nitro Entry
30365
- // This file imports h3 and the SSR handler, wrapping it for Nitro
31826
+ // This file adapts Farm's Web fetch handler to Nitro's event handler contract.
30366
31827
 
30367
- import { defineEventHandler } from 'h3'
30368
31828
  import handler from './${ssrEntryFile}'
30369
31829
 
30370
31830
  function mergeVaryHeaders(target, source) {
@@ -30402,8 +31862,8 @@ function createResponseFinishedHook(event) {
30402
31862
  }
30403
31863
  }
30404
31864
 
30405
- // Export the wrapped handler for Nitro
30406
- export default defineEventHandler(async (event) => {
31865
+ // Export the event handler for Nitro
31866
+ export default async function farmNitroEventHandler(event) {
30407
31867
  const response = await handler.fetch(event.req, {
30408
31868
  waitUntil: (promise) => event.waitUntil(promise),
30409
31869
  onResponseFinished: createResponseFinishedHook(event),
@@ -30414,7 +31874,7 @@ export default defineEventHandler(async (event) => {
30414
31874
  const farmVary = response.headers.get('Vary')
30415
31875
  if (farmVary) mergeVaryHeaders(event.res.headers, farmVary)
30416
31876
  return response
30417
- })
31877
+ }
30418
31878
  `.trim();
30419
31879
  await fs8.writeFile(nitroEntryPath, nitroEntryCode);
30420
31880
  const prebuiltSSRPlugin = createExternalSSRBundlePlugin(
@@ -30832,8 +32292,8 @@ async function copySharpRuntime(config, root, nitroFuncDir, fs8) {
30832
32292
  await fs8.mkdir(import_path12.default.dirname(targetDir), { recursive: true });
30833
32293
  const packageRequire = (0, import_module2.createRequire)(packageJsonPath);
30834
32294
  const dependencies = {
30835
- ...packageJson.dependencies || {},
30836
- ...packageJson.optionalDependencies || {}
32295
+ ...packageJson.dependencies,
32296
+ ...packageJson.optionalDependencies
30837
32297
  };
30838
32298
  await Promise.all([
30839
32299
  fs8.cp(packageDir, targetDir, {
@@ -30926,7 +32386,7 @@ init_environment_vite();
30926
32386
  // src/server/vite-config.ts
30927
32387
  function mergeNoExternal(farmValue, userValue) {
30928
32388
  if (farmValue === true || userValue === true) return true;
30929
- const normalize = /* @__PURE__ */ __name((value) => value == null ? [] : Array.isArray(value) ? value : [value], "normalize");
32389
+ const normalize = /* @__PURE__ */ __name((value) => value == null || value === true ? [] : Array.isArray(value) ? value : [value], "normalize");
30930
32390
  return Array.from(/* @__PURE__ */ new Set([...normalize(farmValue), ...normalize(userValue)]));
30931
32391
  }
30932
32392
  __name(mergeNoExternal, "mergeNoExternal");
@@ -30939,6 +32399,13 @@ function mergeFarmViteConfig(farmConfig, userConfig = {}) {
30939
32399
  ...farmConfig.server,
30940
32400
  ...userConfig.server
30941
32401
  },
32402
+ resolve: {
32403
+ ...farmConfig.resolve,
32404
+ ...userConfig.resolve,
32405
+ dedupe: Array.from(
32406
+ /* @__PURE__ */ new Set([...farmConfig.resolve?.dedupe || [], ...userConfig.resolve?.dedupe || []])
32407
+ )
32408
+ },
30942
32409
  optimizeDeps: {
30943
32410
  ...farmConfig.optimizeDeps,
30944
32411
  ...userConfig.optimizeDeps,