@farm.js/core 0.1.0-beta.3 → 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 (192) hide show
  1. package/dist/agent-runtime.d.mts +2 -1
  2. package/dist/agent-runtime.d.ts +2 -1
  3. package/dist/api/route-manager.cjs +313 -10
  4. package/dist/api/route-manager.cjs.map +1 -1
  5. package/dist/api/route-manager.d.mts +3 -2
  6. package/dist/api/route-manager.d.ts +3 -2
  7. package/dist/api/route-manager.mjs +6 -5
  8. package/dist/api/runtime.cjs +313 -10
  9. package/dist/api/runtime.cjs.map +1 -1
  10. package/dist/api/runtime.d.mts +2 -1
  11. package/dist/api/runtime.d.ts +2 -1
  12. package/dist/api/runtime.mjs +4 -3
  13. package/dist/api.cjs +642 -57
  14. package/dist/api.cjs.map +1 -1
  15. package/dist/api.d.mts +3 -2
  16. package/dist/api.d.ts +3 -2
  17. package/dist/api.mjs +29 -9
  18. package/dist/{app-DOKxo-LH.d.ts → app-BjN-da13.d.ts} +38 -3
  19. package/dist/{app-BB3Iu3xh.d.mts → app-CakAr8Be.d.mts} +38 -3
  20. package/dist/app-markdown.d.mts +2 -1
  21. package/dist/app-markdown.d.ts +2 -1
  22. package/dist/cache.cjs +395 -12
  23. package/dist/cache.cjs.map +1 -1
  24. package/dist/cache.d.mts +100 -6
  25. package/dist/cache.d.ts +100 -6
  26. package/dist/cache.mjs +8 -2
  27. package/dist/{chunk-XUBZ7JI4.mjs → chunk-2CUKEMVR.mjs} +187 -16
  28. package/dist/chunk-2CUKEMVR.mjs.map +1 -0
  29. package/dist/{chunk-5QH4IKY3.mjs → chunk-3U3ZUQAQ.mjs} +2 -2
  30. package/dist/{chunk-6WXJ54TC.mjs → chunk-65A7QIKI.mjs} +5 -5
  31. package/dist/{chunk-6WXJ54TC.mjs.map → chunk-65A7QIKI.mjs.map} +1 -1
  32. package/dist/{chunk-OHC6VCDI.mjs → chunk-AJWRO5WE.mjs} +521 -42
  33. package/dist/chunk-AJWRO5WE.mjs.map +1 -0
  34. package/dist/chunk-ALCLXXTY.mjs +187 -0
  35. package/dist/chunk-ALCLXXTY.mjs.map +1 -0
  36. package/dist/{chunk-XJSHJN4M.mjs → chunk-AOBXKNS5.mjs} +593 -49
  37. package/dist/chunk-AOBXKNS5.mjs.map +1 -0
  38. package/dist/{chunk-CSU4KJJF.mjs → chunk-C2ROET2V.mjs} +2 -2
  39. package/dist/{chunk-BIOIOWXZ.mjs → chunk-HNIQFZKB.mjs} +2 -2
  40. package/dist/{chunk-G5I34UA6.mjs → chunk-JDEOUK5V.mjs} +4 -4
  41. package/dist/{chunk-T543TMFY.mjs → chunk-NM3CRAAL.mjs} +3 -2
  42. package/dist/chunk-NM3CRAAL.mjs.map +1 -0
  43. package/dist/{chunk-LWMGIEJA.mjs → chunk-PFE6GMNE.mjs} +47 -37
  44. package/dist/chunk-PFE6GMNE.mjs.map +1 -0
  45. package/dist/{chunk-6VS6CKAV.mjs → chunk-QM746KUO.mjs} +8 -1
  46. package/dist/chunk-QM746KUO.mjs.map +1 -0
  47. package/dist/{chunk-SHRJG6OG.mjs → chunk-RBRLLZL5.mjs} +18 -3
  48. package/dist/chunk-RBRLLZL5.mjs.map +1 -0
  49. package/dist/{chunk-MH6FKW2I.mjs → chunk-SADGIRS2.mjs} +3 -3
  50. package/dist/{chunk-NRRFGX45.mjs → chunk-TQAEEJB4.mjs} +3 -3
  51. package/dist/{chunk-45LKHSHJ.mjs → chunk-UB5UOSHB.mjs} +24 -12
  52. package/dist/chunk-UB5UOSHB.mjs.map +1 -0
  53. package/dist/chunk-V3XURYJO.mjs +756 -0
  54. package/dist/chunk-V3XURYJO.mjs.map +1 -0
  55. package/dist/{chunk-5FW6PQ3J.mjs → chunk-VVKWFZAH.mjs} +41 -16
  56. package/dist/chunk-VVKWFZAH.mjs.map +1 -0
  57. package/dist/{chunk-HZYXGV4K.mjs → chunk-VY2UYN5R.mjs} +24 -4
  58. package/dist/chunk-VY2UYN5R.mjs.map +1 -0
  59. package/dist/{chunk-C33RALN7.mjs → chunk-W2CRQFF6.mjs} +79 -15
  60. package/dist/chunk-W2CRQFF6.mjs.map +1 -0
  61. package/dist/{chunk-JNYVN7YP.mjs → chunk-W4UIVMVI.mjs} +2 -2
  62. package/dist/{chunk-JNYVN7YP.mjs.map → chunk-W4UIVMVI.mjs.map} +1 -1
  63. package/dist/{chunk-LG3YINQN.mjs → chunk-Z5CQMP54.mjs} +3 -3
  64. package/dist/{client-DiViK9yK.d.mts → client-BT1mFQas.d.mts} +23 -4
  65. package/dist/{client-DpEBJbts.d.ts → client-Nn0q4eO4.d.ts} +23 -4
  66. package/dist/{client-plugins-CTnsZz_A.d.ts → client-plugins-Cw4qUGsw.d.ts} +6 -0
  67. package/dist/{client-plugins-Cnr0D3kk.d.mts → client-plugins-DQQ1adXM.d.mts} +6 -0
  68. package/dist/client-plugins.d.mts +2 -1
  69. package/dist/client-plugins.d.ts +2 -1
  70. package/dist/client.cjs +665 -82
  71. package/dist/client.cjs.map +1 -1
  72. package/dist/client.d.mts +70 -4
  73. package/dist/client.d.ts +70 -4
  74. package/dist/client.mjs +10 -7
  75. package/dist/config.d.mts +2 -1
  76. package/dist/config.d.ts +2 -1
  77. package/dist/{devtools-HE4FTUDN.mjs → devtools-LVC3VB5P.mjs} +4 -4
  78. package/dist/docs.cjs +11 -1
  79. package/dist/docs.cjs.map +1 -1
  80. package/dist/docs.mjs +4 -4
  81. package/dist/index.cjs +2112 -197
  82. package/dist/index.cjs.map +1 -1
  83. package/dist/index.d.mts +12 -11
  84. package/dist/index.d.ts +12 -11
  85. package/dist/index.mjs +49 -21
  86. package/dist/index.mjs.map +1 -1
  87. package/dist/integrations.d.mts +2 -1
  88. package/dist/integrations.d.ts +2 -1
  89. package/dist/internal/build-runtime.cjs +1455 -90
  90. package/dist/internal/build-runtime.cjs.map +1 -1
  91. package/dist/internal/build-runtime.d.mts +4 -3
  92. package/dist/internal/build-runtime.d.ts +4 -3
  93. package/dist/internal/build-runtime.mjs +15 -14
  94. package/dist/internal/config-runtime.cjs +12 -1
  95. package/dist/internal/config-runtime.cjs.map +1 -1
  96. package/dist/internal/config-runtime.d.mts +3 -2
  97. package/dist/internal/config-runtime.d.ts +3 -2
  98. package/dist/internal/config-runtime.mjs +3 -3
  99. package/dist/internal/production-runtime.cjs +282 -6
  100. package/dist/internal/production-runtime.cjs.map +1 -1
  101. package/dist/internal/production-runtime.d.mts +4 -4
  102. package/dist/internal/production-runtime.d.ts +4 -4
  103. package/dist/internal/production-runtime.mjs +4 -2
  104. package/dist/{metadata-eoVmHoyv.d.ts → metadata-BAPg_Nmo.d.ts} +1 -1
  105. package/dist/{metadata-Dp8AdvsB.d.mts → metadata-_6i9oFG3.d.mts} +1 -1
  106. package/dist/middleware.d.mts +4 -3
  107. package/dist/middleware.d.ts +4 -3
  108. package/dist/navigation.cjs +40 -15
  109. package/dist/navigation.cjs.map +1 -1
  110. package/dist/navigation.mjs +2 -2
  111. package/dist/plugin.cjs +913 -53
  112. package/dist/plugin.cjs.map +1 -1
  113. package/dist/plugin.d.mts +2 -1
  114. package/dist/plugin.d.ts +2 -1
  115. package/dist/plugin.mjs +11 -10
  116. package/dist/{production-runtime-BqTnUTai.d.ts → production-runtime-BoJDc5mc.d.ts} +1 -1
  117. package/dist/{production-runtime-DtDdG9fc.d.mts → production-runtime-DPfgMV9Y.d.mts} +1 -1
  118. package/dist/routes.cjs +276 -6
  119. package/dist/routes.cjs.map +1 -1
  120. package/dist/routes.d.mts +1 -1
  121. package/dist/routes.d.ts +1 -1
  122. package/dist/routes.mjs +3 -3
  123. package/dist/{runtime-C1B7pSGT.d.mts → runtime-9nQjVM7X.d.mts} +8 -9
  124. package/dist/{runtime-CdPHt0bv.d.ts → runtime-DgeS2skB.d.ts} +8 -9
  125. package/dist/server-action-security.cjs +28 -10
  126. package/dist/server-action-security.cjs.map +1 -1
  127. package/dist/server-action-security.d.mts +2 -1
  128. package/dist/server-action-security.d.ts +2 -1
  129. package/dist/server-action-security.mjs +2 -2
  130. package/dist/server-fn-client.d.mts +1 -0
  131. package/dist/server-fn-client.d.ts +1 -0
  132. package/dist/server-fn.cjs +785 -3
  133. package/dist/server-fn.cjs.map +1 -1
  134. package/dist/server-fn.d.mts +10 -1
  135. package/dist/server-fn.d.ts +10 -1
  136. package/dist/server-fn.mjs +6 -3
  137. package/dist/server-plugins.d.mts +3 -2
  138. package/dist/server-plugins.d.ts +3 -2
  139. package/dist/server-query-client.cjs +278 -7
  140. package/dist/server-query-client.cjs.map +1 -1
  141. package/dist/server-query-client.mjs +4 -4
  142. package/dist/server-query.cjs +381 -10
  143. package/dist/server-query.cjs.map +1 -1
  144. package/dist/server-query.mjs +5 -5
  145. package/dist/server.cjs +1086 -76
  146. package/dist/server.cjs.map +1 -1
  147. package/dist/server.d.mts +8 -7
  148. package/dist/server.d.ts +8 -7
  149. package/dist/server.mjs +31 -16
  150. package/dist/server.mjs.map +1 -1
  151. package/dist/testing.cjs +341 -20
  152. package/dist/testing.cjs.map +1 -1
  153. package/dist/testing.d.mts +3 -2
  154. package/dist/testing.d.ts +3 -2
  155. package/dist/testing.mjs +7 -6
  156. package/dist/testing.mjs.map +1 -1
  157. package/dist/transport-BmbGTXA5.d.mts +49 -0
  158. package/dist/transport-BmbGTXA5.d.ts +49 -0
  159. package/dist/{utils-8USa5BiY.d.mts → utils-CHXlMDkg.d.mts} +1 -1
  160. package/dist/{utils-DKJb6K6G.d.ts → utils-Drsyz1Vj.d.ts} +1 -1
  161. package/dist/version.cjs +1 -1
  162. package/dist/version.cjs.map +1 -1
  163. package/dist/version.mjs +1 -1
  164. package/dist/vite.cjs +914 -54
  165. package/dist/vite.cjs.map +1 -1
  166. package/dist/vite.d.mts +2 -1
  167. package/dist/vite.d.ts +2 -1
  168. package/dist/vite.mjs +11 -10
  169. package/package.json +1 -1
  170. package/types/api.d.ts +44 -0
  171. package/types/client.d.ts +85 -4
  172. package/dist/chunk-45LKHSHJ.mjs.map +0 -1
  173. package/dist/chunk-5FW6PQ3J.mjs.map +0 -1
  174. package/dist/chunk-6VS6CKAV.mjs.map +0 -1
  175. package/dist/chunk-C33RALN7.mjs.map +0 -1
  176. package/dist/chunk-GZWT6VI5.mjs +0 -382
  177. package/dist/chunk-GZWT6VI5.mjs.map +0 -1
  178. package/dist/chunk-HZYXGV4K.mjs.map +0 -1
  179. package/dist/chunk-LWMGIEJA.mjs.map +0 -1
  180. package/dist/chunk-OHC6VCDI.mjs.map +0 -1
  181. package/dist/chunk-SHRJG6OG.mjs.map +0 -1
  182. package/dist/chunk-T543TMFY.mjs.map +0 -1
  183. package/dist/chunk-XJSHJN4M.mjs.map +0 -1
  184. package/dist/chunk-XUBZ7JI4.mjs.map +0 -1
  185. /package/dist/{chunk-5QH4IKY3.mjs.map → chunk-3U3ZUQAQ.mjs.map} +0 -0
  186. /package/dist/{chunk-CSU4KJJF.mjs.map → chunk-C2ROET2V.mjs.map} +0 -0
  187. /package/dist/{chunk-BIOIOWXZ.mjs.map → chunk-HNIQFZKB.mjs.map} +0 -0
  188. /package/dist/{chunk-G5I34UA6.mjs.map → chunk-JDEOUK5V.mjs.map} +0 -0
  189. /package/dist/{chunk-MH6FKW2I.mjs.map → chunk-SADGIRS2.mjs.map} +0 -0
  190. /package/dist/{chunk-NRRFGX45.mjs.map → chunk-TQAEEJB4.mjs.map} +0 -0
  191. /package/dist/{chunk-LG3YINQN.mjs.map → chunk-Z5CQMP54.mjs.map} +0 -0
  192. /package/dist/{devtools-HE4FTUDN.mjs.map → devtools-LVC3VB5P.mjs.map} +0 -0
package/dist/server.cjs CHANGED
@@ -1819,7 +1819,8 @@ var init_route_rules = __esm({
1819
1819
  // src/cache-invalidation.ts
1820
1820
  function getFarmCacheInvalidationState() {
1821
1821
  return globalState[FARM_CACHE_INVALIDATION_STATE] ?? (globalState[FARM_CACHE_INVALIDATION_STATE] = {
1822
- listeners: /* @__PURE__ */ new Set()
1822
+ listeners: /* @__PURE__ */ new Set(),
1823
+ taskListeners: /* @__PURE__ */ new Set()
1823
1824
  });
1824
1825
  }
1825
1826
  function notifyFarmCacheInvalidation(key) {
@@ -1828,6 +1829,11 @@ function notifyFarmCacheInvalidation(key) {
1828
1829
  listener(key);
1829
1830
  }
1830
1831
  }
1832
+ function notifyFarmCacheTask(task) {
1833
+ for (const listener of getFarmCacheInvalidationState().taskListeners) {
1834
+ listener(task);
1835
+ }
1836
+ }
1831
1837
  function applyFarmCacheInvalidations(keys) {
1832
1838
  if (!Array.isArray(keys)) return;
1833
1839
  for (const key of keys) {
@@ -1859,6 +1865,11 @@ function subscribeFarmCacheInvalidation(listener) {
1859
1865
  state.listeners.add(listener);
1860
1866
  return () => state.listeners.delete(listener);
1861
1867
  }
1868
+ function subscribeFarmCacheTask(listener) {
1869
+ const state = getFarmCacheInvalidationState();
1870
+ state.taskListeners.add(listener);
1871
+ return () => state.taskListeners.delete(listener);
1872
+ }
1862
1873
  var FARM_CACHE_INVALIDATION_HEADER, FARM_CACHE_INVALIDATION_STATE, globalState;
1863
1874
  var init_cache_invalidation = __esm({
1864
1875
  "src/cache-invalidation.ts"() {
@@ -1868,10 +1879,12 @@ var init_cache_invalidation = __esm({
1868
1879
  globalState = globalThis;
1869
1880
  __name(getFarmCacheInvalidationState, "getFarmCacheInvalidationState");
1870
1881
  __name(notifyFarmCacheInvalidation, "notifyFarmCacheInvalidation");
1882
+ __name(notifyFarmCacheTask, "notifyFarmCacheTask");
1871
1883
  __name(applyFarmCacheInvalidations, "applyFarmCacheInvalidations");
1872
1884
  __name(encodeFarmCacheInvalidations, "encodeFarmCacheInvalidations");
1873
1885
  __name(decodeFarmCacheInvalidations, "decodeFarmCacheInvalidations");
1874
1886
  __name(subscribeFarmCacheInvalidation, "subscribeFarmCacheInvalidation");
1887
+ __name(subscribeFarmCacheTask, "subscribeFarmCacheTask");
1875
1888
  }
1876
1889
  });
1877
1890
 
@@ -1967,16 +1980,24 @@ function sanitizeServerActionError(_error) {
1967
1980
  message: "Server function failed"
1968
1981
  };
1969
1982
  }
1970
- function runWithServerActionRequest(request, callback) {
1983
+ async function runWithServerActionRequest(request, callback) {
1971
1984
  throwIfAborted(request.signal);
1972
- return getServerActionStorage().run(
1973
- {
1974
- request,
1975
- signal: request.signal,
1976
- invalidations: /* @__PURE__ */ new Set()
1977
- },
1978
- callback
1979
- );
1985
+ const context = {
1986
+ request,
1987
+ signal: request.signal,
1988
+ invalidations: /* @__PURE__ */ new Set(),
1989
+ cacheTasks: /* @__PURE__ */ new Set()
1990
+ };
1991
+ return getServerActionStorage().run(context, async () => {
1992
+ try {
1993
+ const result = await callback();
1994
+ await Promise.all(context.cacheTasks);
1995
+ return result;
1996
+ } catch (error) {
1997
+ await Promise.allSettled(context.cacheTasks);
1998
+ throw error;
1999
+ }
2000
+ });
1980
2001
  }
1981
2002
  function getServerActionExecutionContext() {
1982
2003
  return getServerActionStorage().getStore();
@@ -2287,6 +2308,9 @@ var init_server_action_security = __esm({
2287
2308
  subscribeFarmCacheInvalidation((key) => {
2288
2309
  getServerActionExecutionContext()?.invalidations.add(key);
2289
2310
  });
2311
+ subscribeFarmCacheTask((task) => {
2312
+ getServerActionExecutionContext()?.cacheTasks.add(task);
2313
+ });
2290
2314
  __name(getServerActionStorage, "getServerActionStorage");
2291
2315
  __name(getFallbackAbortController, "getFallbackAbortController");
2292
2316
  __name(parseBodySizeLimit, "parseBodySizeLimit");
@@ -9467,6 +9491,7 @@ async function resolveConfig(userConfig, mode) {
9467
9491
  observability: userConfig.observability ?? false,
9468
9492
  devtools: resolveFarmDevtoolsConfig(userConfig.devtools, mode),
9469
9493
  storage: userConfig.storage || {},
9494
+ cache: userConfig.cache || {},
9470
9495
  suppressLintOnLink: userConfig.suppressLintOnLink ?? false,
9471
9496
  experimental: {
9472
9497
  serverComponents: false,
@@ -12721,6 +12746,7 @@ var init_image_sharp = __esm({
12721
12746
  // src/server.ts
12722
12747
  var server_exports = {};
12723
12748
  __export(server_exports, {
12749
+ DEFAULT_FARM_DEV_SERVER_PORT: () => DEFAULT_FARM_DEV_SERVER_PORT,
12724
12750
  DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT: () => DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT,
12725
12751
  FARM_SERVER_QUERY_SYMBOL: () => FARM_SERVER_QUERY_SYMBOL,
12726
12752
  FarmApp: () => FarmApp,
@@ -12733,7 +12759,9 @@ __export(server_exports, {
12733
12759
  _runWithAfterRequest: () => _runWithAfterRequest,
12734
12760
  _withAfterNodeMiddleware: () => _withAfterNodeMiddleware,
12735
12761
  after: () => after,
12762
+ applyFarmCacheInvalidationTargets: () => applyFarmCacheInvalidationTargets,
12736
12763
  applyFarmCacheInvalidations: () => applyFarmCacheInvalidations,
12764
+ configureFarmCache: () => configureFarmCache,
12737
12765
  createClientOnlyFn: () => createClientOnlyFn,
12738
12766
  createFarmApp: () => createFarmApp,
12739
12767
  createFarmCacheKey: () => createFarmCacheKey,
@@ -12799,6 +12827,7 @@ __export(server_exports, {
12799
12827
  segmentsToPattern: () => segmentsToPattern,
12800
12828
  sqliteStorage: () => sqliteStorage,
12801
12829
  startDevServer: () => startDevServer,
12830
+ storageCacheAdapter: () => storageCacheAdapter,
12802
12831
  toViteModuleId: () => toViteModuleId,
12803
12832
  unstable_cache: () => unstable_cache,
12804
12833
  updateTag: () => updateTag,
@@ -13502,12 +13531,92 @@ function defineCacheKey() {
13502
13531
  };
13503
13532
  }
13504
13533
  __name(defineCacheKey, "defineCacheKey");
13534
+ function storageCacheAdapter(storage, options = {}) {
13535
+ if (!storage || typeof storage.getItem !== "function" || typeof storage.setItem !== "function") {
13536
+ throw new TypeError("storageCacheAdapter expects a compatible storage client.");
13537
+ }
13538
+ const base = normalizeCacheNamespace(options.base || "farm-cache");
13539
+ const entryKey = /* @__PURE__ */ __name((key) => `${base}:entry:${key}`, "entryKey");
13540
+ const tagKey = /* @__PURE__ */ __name((tag) => `${base}:tag:${tag}`, "tagKey");
13541
+ return {
13542
+ name: "storage",
13543
+ get: /* @__PURE__ */ __name((key) => storage.getItem(entryKey(key)), "get"),
13544
+ set: /* @__PURE__ */ __name(async (key, entry) => {
13545
+ await storage.setItem(entryKey(key), entry);
13546
+ }, "set"),
13547
+ delete: /* @__PURE__ */ __name(async (key) => {
13548
+ await storage.removeItem(entryKey(key));
13549
+ }, "delete"),
13550
+ clear: storage.clear ? async () => {
13551
+ await storage.clear(base);
13552
+ } : void 0,
13553
+ getTagVersions: /* @__PURE__ */ __name(async (tags) => {
13554
+ const versions = await Promise.all(
13555
+ tags.map(async (tag) => {
13556
+ const value = await storage.getItem(tagKey(tag));
13557
+ return [tag, normalizeAdapterVersion(value)];
13558
+ })
13559
+ );
13560
+ return Object.fromEntries(versions);
13561
+ }, "getTagVersions"),
13562
+ invalidateTags: /* @__PURE__ */ __name(async (tags) => {
13563
+ await Promise.all(
13564
+ tags.map(async (tag) => {
13565
+ const key = tagKey(tag);
13566
+ const current = normalizeAdapterVersion(await storage.getItem(key));
13567
+ await storage.setItem(key, Math.max(Date.now(), current + 1));
13568
+ })
13569
+ );
13570
+ }, "invalidateTags")
13571
+ };
13572
+ }
13573
+ __name(storageCacheAdapter, "storageCacheAdapter");
13505
13574
  var _FarmDataCache = class _FarmDataCache {
13506
- constructor() {
13575
+ constructor(config = {}) {
13507
13576
  this.entries = /* @__PURE__ */ new Map();
13508
13577
  this.inflight = /* @__PURE__ */ new Map();
13509
13578
  this.invalidatedTagVersions = /* @__PURE__ */ new Map();
13510
13579
  this.version = 0;
13580
+ this.namespace = "farm";
13581
+ this.local = true;
13582
+ this.lease = {
13583
+ enabled: true,
13584
+ ttlMs: 1e4,
13585
+ waitTimeoutMs: 1e4,
13586
+ pollIntervalMs: 25
13587
+ };
13588
+ this.configure(config);
13589
+ }
13590
+ configure(config = {}) {
13591
+ this.adapter = config.adapter;
13592
+ this.namespace = normalizeCacheNamespace(config.namespace || "farm");
13593
+ this.local = !config.adapter;
13594
+ this.lease = config.lease === false ? { ...this.lease, enabled: false } : {
13595
+ enabled: true,
13596
+ ttlMs: normalizePositiveDuration(config.lease?.ttlMs, 1e4, "cache.lease.ttlMs"),
13597
+ waitTimeoutMs: normalizePositiveDuration(
13598
+ config.lease?.waitTimeoutMs,
13599
+ 1e4,
13600
+ "cache.lease.waitTimeoutMs"
13601
+ ),
13602
+ pollIntervalMs: normalizePositiveDuration(
13603
+ config.lease?.pollIntervalMs,
13604
+ 25,
13605
+ "cache.lease.pollIntervalMs"
13606
+ )
13607
+ };
13608
+ if (!this.local) {
13609
+ this.entries.clear();
13610
+ this.inflight.clear();
13611
+ this.invalidatedTagVersions.clear();
13612
+ this.version = 0;
13613
+ }
13614
+ }
13615
+ get adapterName() {
13616
+ return this.adapter?.name || (this.adapter ? "custom" : "memory");
13617
+ }
13618
+ get hasAdapter() {
13619
+ return this.adapter !== void 0;
13511
13620
  }
13512
13621
  get size() {
13513
13622
  return this.entries.size;
@@ -13543,6 +13652,45 @@ var _FarmDataCache = class _FarmDataCache {
13543
13652
  });
13544
13653
  return this.toPublicEntry(entry);
13545
13654
  }
13655
+ async getEntryAsync(key, options = {}) {
13656
+ if (this.local) {
13657
+ const localEntry = this.getEntry(key, options);
13658
+ if (localEntry) return localEntry;
13659
+ }
13660
+ if (!this.adapter) {
13661
+ return this.local ? void 0 : this.getEntry(key, options);
13662
+ }
13663
+ const entry = await this.adapter.get(this.createAdapterKey(key));
13664
+ if (!entry) {
13665
+ emitFarmEvent({ type: "cache.miss", key });
13666
+ return void 0;
13667
+ }
13668
+ assertFarmCacheEntry(entry, key);
13669
+ const stale = await this.isAdapterEntryStale(entry, options.now);
13670
+ if (stale) {
13671
+ emitFarmEvent({
13672
+ type: "cache.stale",
13673
+ key,
13674
+ tags: [...entry.tags],
13675
+ revalidate: entry.revalidate
13676
+ });
13677
+ if (!options.allowStale) {
13678
+ emitFarmEvent({ type: "cache.miss", key, reason: "stale" });
13679
+ return void 0;
13680
+ }
13681
+ }
13682
+ emitFarmEvent({
13683
+ type: "cache.hit",
13684
+ key,
13685
+ tags: [...entry.tags],
13686
+ revalidate: entry.revalidate,
13687
+ stale
13688
+ });
13689
+ if (this.local && !stale) {
13690
+ this.hydrateLocalEntry(entry);
13691
+ }
13692
+ return { ...entry, key };
13693
+ }
13546
13694
  set(key, value, options = {}) {
13547
13695
  const tags = /* @__PURE__ */ new Set();
13548
13696
  for (const tag of options.tags ?? []) {
@@ -13555,6 +13703,7 @@ var _FarmDataCache = class _FarmDataCache {
13555
13703
  key,
13556
13704
  value,
13557
13705
  tags,
13706
+ tagVersions: void 0,
13558
13707
  createdAt: options.createdAt ?? Date.now(),
13559
13708
  createdVersion: ++this.version,
13560
13709
  revalidate: normalizeRevalidate(options.revalidate)
@@ -13568,11 +13717,45 @@ var _FarmDataCache = class _FarmDataCache {
13568
13717
  });
13569
13718
  return this.toPublicEntry(entry);
13570
13719
  }
13720
+ async setAsync(key, value, options = {}, tagVersions) {
13721
+ const tags = normalizeCacheOptionsTags(options);
13722
+ const capturedVersions = tagVersions ?? await this.getAdapterTagVersions(Array.from(tags.values()));
13723
+ const entry = {
13724
+ key,
13725
+ value,
13726
+ tags: Array.from(tags),
13727
+ tagVersions: capturedVersions,
13728
+ createdAt: options.createdAt ?? Date.now(),
13729
+ createdVersion: ++this.version,
13730
+ revalidate: normalizeRevalidate(options.revalidate)
13731
+ };
13732
+ if (this.local) {
13733
+ this.hydrateLocalEntry(entry);
13734
+ }
13735
+ if (this.adapter) {
13736
+ await this.adapter.set(this.createAdapterKey(key), entry);
13737
+ }
13738
+ emitFarmEvent({
13739
+ type: "cache.set",
13740
+ key,
13741
+ tags: [...entry.tags],
13742
+ revalidate: entry.revalidate
13743
+ });
13744
+ return entry;
13745
+ }
13571
13746
  delete(key) {
13572
13747
  const deleted = this.entries.delete(key);
13573
13748
  emitFarmEvent({ type: "cache.delete", key, deleted });
13574
13749
  return deleted;
13575
13750
  }
13751
+ async deleteAsync(key) {
13752
+ const deleted = this.entries.delete(key);
13753
+ if (this.adapter) {
13754
+ await this.adapter.delete(this.createAdapterKey(key));
13755
+ }
13756
+ emitFarmEvent({ type: "cache.delete", key, deleted: this.adapter ? true : deleted });
13757
+ return this.adapter ? true : deleted;
13758
+ }
13576
13759
  clear() {
13577
13760
  const count = this.entries.size;
13578
13761
  this.entries.clear();
@@ -13581,6 +13764,10 @@ var _FarmDataCache = class _FarmDataCache {
13581
13764
  this.version = 0;
13582
13765
  emitFarmEvent({ type: "cache.clear", count });
13583
13766
  }
13767
+ async clearAsync() {
13768
+ this.clear();
13769
+ await this.adapter?.clear?.();
13770
+ }
13584
13771
  isStale(entry, now = Date.now()) {
13585
13772
  if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
13586
13773
  return true;
@@ -13593,6 +13780,12 @@ var _FarmDataCache = class _FarmDataCache {
13593
13780
  }
13594
13781
  return false;
13595
13782
  }
13783
+ async isStaleAsync(entry, now = Date.now()) {
13784
+ if (this.adapter) {
13785
+ return this.isAdapterEntryStale(entry, now);
13786
+ }
13787
+ return this.isStale(entry, now);
13788
+ }
13596
13789
  revalidateTag(tag, options = {}) {
13597
13790
  const normalized = normalizeCacheTag(tag);
13598
13791
  const count = this.invalidateTag(normalized);
@@ -13601,6 +13794,15 @@ var _FarmDataCache = class _FarmDataCache {
13601
13794
  );
13602
13795
  return count;
13603
13796
  }
13797
+ async revalidateTagAsync(tag, options = {}) {
13798
+ const normalized = normalizeCacheTag(tag);
13799
+ const count = this.invalidateTag(normalized);
13800
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(normalized)]);
13801
+ emitFarmEvent(
13802
+ options.source === "updateTag" ? { type: "cache.updateTag", tag: normalized, count } : { type: "cache.revalidateTag", tag: normalized, profile: options.profile, count }
13803
+ );
13804
+ return count;
13805
+ }
13604
13806
  revalidatePath(routePath) {
13605
13807
  const normalizedPath = normalizeRevalidatePath(routePath);
13606
13808
  const pathTag = createPathCacheTag(normalizedPath);
@@ -13617,8 +13819,25 @@ var _FarmDataCache = class _FarmDataCache {
13617
13819
  }
13618
13820
  return count;
13619
13821
  }
13822
+ async revalidatePathAsync(routePath) {
13823
+ const normalizedPath = normalizeRevalidatePath(routePath);
13824
+ const pathTag = createPathCacheTag(normalizedPath);
13825
+ const pprCount = this.countEntriesForTags([pathTag, "ppr"]);
13826
+ const count = this.invalidateTag(pathTag);
13827
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(pathTag)]);
13828
+ emitFarmEvent({ type: "cache.revalidatePath", path: normalizedPath, count });
13829
+ if (pprCount > 0) {
13830
+ emitFarmEvent({
13831
+ type: "ppr.shell.invalidated",
13832
+ route: normalizedPath,
13833
+ reason: "revalidatePath",
13834
+ count: pprCount
13835
+ });
13836
+ }
13837
+ return count;
13838
+ }
13620
13839
  async getOrSet(key, producer, options = {}) {
13621
- const cached = this.getEntry(key);
13840
+ const cached = await this.getEntryAsync(key);
13622
13841
  if (cached) {
13623
13842
  return cached.value;
13624
13843
  }
@@ -13627,10 +13846,8 @@ var _FarmDataCache = class _FarmDataCache {
13627
13846
  emitFarmEvent({ type: "cache.dedupe", key });
13628
13847
  return inflight;
13629
13848
  }
13630
- const promise = Promise.resolve().then(producer).then((value) => {
13631
- this.set(key, value, options);
13632
- return value;
13633
- }).catch((error) => {
13849
+ const tags = Array.from(normalizeCacheOptionsTags(options));
13850
+ const promise = this.fillCacheEntry(key, producer, options, tags).catch((error) => {
13634
13851
  emitFarmEvent({ type: "cache.error", key, operation: "set", error });
13635
13852
  throw error;
13636
13853
  }).finally(() => {
@@ -13639,6 +13856,40 @@ var _FarmDataCache = class _FarmDataCache {
13639
13856
  this.inflight.set(key, promise);
13640
13857
  return promise;
13641
13858
  }
13859
+ async fillCacheEntry(key, producer, options, tags) {
13860
+ const leaseKey = `${this.namespace}:lease:${key}`;
13861
+ let leaseToken;
13862
+ if (this.adapter?.acquireLease && this.adapter.releaseLease && this.lease.enabled) {
13863
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
13864
+ if (!leaseToken) {
13865
+ const shared = await this.waitForAdapterEntry(key);
13866
+ if (shared) {
13867
+ emitFarmEvent({ type: "cache.dedupe", key });
13868
+ return shared.value;
13869
+ }
13870
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
13871
+ }
13872
+ }
13873
+ try {
13874
+ const initialTagVersions = await this.getAdapterTagVersions(tags);
13875
+ const value = await producer();
13876
+ await this.setAsync(key, value, options, initialTagVersions);
13877
+ return value;
13878
+ } finally {
13879
+ if (leaseToken && this.adapter?.releaseLease) {
13880
+ await this.adapter.releaseLease(leaseKey, leaseToken);
13881
+ }
13882
+ }
13883
+ }
13884
+ async waitForAdapterEntry(key) {
13885
+ const deadline = Date.now() + this.lease.waitTimeoutMs;
13886
+ while (Date.now() < deadline) {
13887
+ await delay(this.lease.pollIntervalMs);
13888
+ const entry = await this.getEntryAsync(key);
13889
+ if (entry) return entry;
13890
+ }
13891
+ return void 0;
13892
+ }
13642
13893
  countEntriesForTag(tag) {
13643
13894
  let count = 0;
13644
13895
  for (const entry of this.entries.values()) {
@@ -13666,11 +13917,53 @@ var _FarmDataCache = class _FarmDataCache {
13666
13917
  key: entry.key,
13667
13918
  value: entry.value,
13668
13919
  tags: Array.from(entry.tags),
13920
+ tagVersions: entry.tagVersions,
13669
13921
  createdAt: entry.createdAt,
13670
13922
  createdVersion: entry.createdVersion,
13671
13923
  revalidate: entry.revalidate
13672
13924
  };
13673
13925
  }
13926
+ hydrateLocalEntry(entry) {
13927
+ this.entries.set(entry.key, {
13928
+ key: entry.key,
13929
+ value: entry.value,
13930
+ tags: new Set(entry.tags.map(normalizeCacheTag)),
13931
+ tagVersions: entry.tagVersions,
13932
+ createdAt: entry.createdAt,
13933
+ createdVersion: entry.createdVersion ?? ++this.version,
13934
+ revalidate: normalizeRevalidate(entry.revalidate)
13935
+ });
13936
+ }
13937
+ createAdapterKey(key) {
13938
+ return `${this.namespace}:entry:${key}`;
13939
+ }
13940
+ createAdapterTag(tag) {
13941
+ return `${this.namespace}:tag:${tag}`;
13942
+ }
13943
+ async getAdapterTagVersions(tags) {
13944
+ if (!this.adapter?.getTagVersions || tags.length === 0) return {};
13945
+ const normalized = tags.map(normalizeCacheTag);
13946
+ const physicalTags = normalized.map((tag) => this.createAdapterTag(tag));
13947
+ const versions = await this.adapter.getTagVersions(physicalTags);
13948
+ return Object.fromEntries(
13949
+ normalized.map((tag, index) => [
13950
+ tag,
13951
+ normalizeAdapterVersion(versions[physicalTags[index]])
13952
+ ])
13953
+ );
13954
+ }
13955
+ async isAdapterEntryStale(entry, now = Date.now()) {
13956
+ if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
13957
+ return true;
13958
+ }
13959
+ const currentVersions = await this.getAdapterTagVersions(entry.tags);
13960
+ for (const tag of entry.tags) {
13961
+ if (normalizeAdapterVersion(currentVersions[tag]) > normalizeAdapterVersion(entry.tagVersions?.[tag])) {
13962
+ return true;
13963
+ }
13964
+ }
13965
+ return false;
13966
+ }
13674
13967
  };
13675
13968
  __name(_FarmDataCache, "FarmDataCache");
13676
13969
  var FarmDataCache = _FarmDataCache;
@@ -13683,6 +13976,10 @@ function getFarmDataCache() {
13683
13976
  return sharedFarmDataCache;
13684
13977
  }
13685
13978
  __name(getFarmDataCache, "getFarmDataCache");
13979
+ function configureFarmCache(config) {
13980
+ sharedFarmDataCache.configure(config);
13981
+ }
13982
+ __name(configureFarmCache, "configureFarmCache");
13686
13983
  function unstable_cache(fn, keyParts = [], options = {}) {
13687
13984
  return async (...args) => {
13688
13985
  const locale = getActiveFarmI18nSnapshot()?.locale;
@@ -13698,30 +13995,71 @@ function unstable_cache(fn, keyParts = [], options = {}) {
13698
13995
  }
13699
13996
  __name(unstable_cache, "unstable_cache");
13700
13997
  function revalidateTag(_tag, _profile) {
13701
- getFarmDataCache().revalidateTag(_tag, {
13998
+ const cache = getFarmDataCache();
13999
+ if (cache.hasAdapter) {
14000
+ const task = cache.revalidateTagAsync(_tag, {
14001
+ source: "revalidateTag",
14002
+ profile: _profile
14003
+ }).then(() => void 0);
14004
+ notifyFarmCacheTask(task);
14005
+ return task;
14006
+ }
14007
+ cache.revalidateTag(_tag, {
13702
14008
  source: "revalidateTag",
13703
14009
  profile: _profile
13704
14010
  });
13705
14011
  }
13706
14012
  __name(revalidateTag, "revalidateTag");
13707
14013
  function updateTag(tag) {
13708
- getFarmDataCache().revalidateTag(tag, { source: "updateTag" });
14014
+ const cache = getFarmDataCache();
14015
+ if (cache.hasAdapter) {
14016
+ const task = cache.revalidateTagAsync(tag, { source: "updateTag" }).then(() => void 0);
14017
+ notifyFarmCacheTask(task);
14018
+ return task;
14019
+ }
14020
+ cache.revalidateTag(tag, { source: "updateTag" });
13709
14021
  }
13710
14022
  __name(updateTag, "updateTag");
13711
14023
  function revalidatePath(routePath) {
13712
- getFarmDataCache().revalidatePath(routePath);
14024
+ const cache = getFarmDataCache();
14025
+ if (cache.hasAdapter) {
14026
+ const task = cache.revalidatePathAsync(routePath).then(() => void 0);
14027
+ notifyFarmCacheTask(task);
14028
+ return task;
14029
+ }
14030
+ cache.revalidatePath(routePath);
13713
14031
  }
13714
14032
  __name(revalidatePath, "revalidatePath");
13715
14033
  function invalidate(key) {
13716
14034
  const clientKey = createRouteDataCacheKey(key);
13717
- getFarmDataCache().revalidateTag(createRouteDataCacheTag(key), { source: "updateTag" });
14035
+ const task = updateTag(createRouteDataCacheTag(key));
13718
14036
  notifyFarmCacheInvalidation(clientKey);
14037
+ return task;
13719
14038
  }
13720
14039
  __name(invalidate, "invalidate");
13721
14040
  function invalidateRouteData(key) {
13722
- invalidate(key);
14041
+ return invalidate(key);
13723
14042
  }
13724
14043
  __name(invalidateRouteData, "invalidateRouteData");
14044
+ async function applyFarmCacheInvalidationTargets(targets) {
14045
+ if (!Array.isArray(targets)) {
14046
+ throw new TypeError("Cache invalidations must resolve to an array of targets.");
14047
+ }
14048
+ const clientKeys = [];
14049
+ for (const target of targets) {
14050
+ assertFarmCacheInvalidationTarget(target);
14051
+ if ("key" in target) {
14052
+ await invalidate(target.key);
14053
+ clientKeys.push(createRouteDataCacheKey(target.key));
14054
+ } else if ("path" in target) {
14055
+ await revalidatePath(target.path);
14056
+ } else {
14057
+ await updateTag(target.tag);
14058
+ }
14059
+ }
14060
+ return Array.from(new Set(clientKeys));
14061
+ }
14062
+ __name(applyFarmCacheInvalidationTargets, "applyFarmCacheInvalidationTargets");
13725
14063
  function createPathCacheTag(routePath) {
13726
14064
  return `path:${normalizeRevalidatePath(routePath)}`;
13727
14065
  }
@@ -13782,6 +14120,67 @@ function normalizeCacheTag(tag) {
13782
14120
  return normalized;
13783
14121
  }
13784
14122
  __name(normalizeCacheTag, "normalizeCacheTag");
14123
+ function normalizeCacheNamespace(namespace) {
14124
+ if (typeof namespace !== "string") {
14125
+ throw new TypeError("Cache namespace must be a string.");
14126
+ }
14127
+ const normalized = namespace.trim().replace(/:+$/g, "");
14128
+ if (!normalized) {
14129
+ throw new Error("Cache namespace cannot be empty.");
14130
+ }
14131
+ return normalized;
14132
+ }
14133
+ __name(normalizeCacheNamespace, "normalizeCacheNamespace");
14134
+ function normalizeAdapterVersion(value) {
14135
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
14136
+ }
14137
+ __name(normalizeAdapterVersion, "normalizeAdapterVersion");
14138
+ function normalizePositiveDuration(value, fallback, name) {
14139
+ if (value === void 0) return fallback;
14140
+ if (!Number.isFinite(value) || value <= 0) {
14141
+ throw new TypeError(`${name} must be a positive number of milliseconds.`);
14142
+ }
14143
+ return Math.floor(value);
14144
+ }
14145
+ __name(normalizePositiveDuration, "normalizePositiveDuration");
14146
+ function delay(ms) {
14147
+ return new Promise((resolve5) => setTimeout(resolve5, ms));
14148
+ }
14149
+ __name(delay, "delay");
14150
+ function normalizeCacheOptionsTags(options) {
14151
+ const tags = /* @__PURE__ */ new Set();
14152
+ for (const tag of options.tags ?? []) {
14153
+ tags.add(normalizeCacheTag(tag));
14154
+ }
14155
+ for (const routePath of options.paths ?? []) {
14156
+ tags.add(createPathCacheTag(routePath));
14157
+ }
14158
+ return tags;
14159
+ }
14160
+ __name(normalizeCacheOptionsTags, "normalizeCacheOptionsTags");
14161
+ function assertFarmCacheEntry(entry, expectedKey) {
14162
+ if (!entry || typeof entry !== "object" || entry.key !== expectedKey || !Array.isArray(entry.tags) || typeof entry.createdAt !== "number") {
14163
+ throw new TypeError(
14164
+ `Cache adapter returned an invalid entry for ${JSON.stringify(expectedKey)}.`
14165
+ );
14166
+ }
14167
+ }
14168
+ __name(assertFarmCacheEntry, "assertFarmCacheEntry");
14169
+ function assertFarmCacheInvalidationTarget(target) {
14170
+ if (!target || typeof target !== "object") {
14171
+ throw new TypeError("Cache invalidation targets must be { key }, { path }, or { tag }.");
14172
+ }
14173
+ if ("key" in target) {
14174
+ const key = target.key;
14175
+ if (typeof key === "string" || Array.isArray(key)) return;
14176
+ } else if ("path" in target && typeof target.path === "string") {
14177
+ return;
14178
+ } else if ("tag" in target && typeof target.tag === "string") {
14179
+ return;
14180
+ }
14181
+ throw new TypeError("Cache invalidation targets must contain a string/array key, path, or tag.");
14182
+ }
14183
+ __name(assertFarmCacheInvalidationTarget, "assertFarmCacheInvalidationTarget");
13785
14184
  function getFunctionCacheIdentity(fn) {
13786
14185
  if (fn.name) {
13787
14186
  return `name:${fn.name}`;
@@ -14922,6 +15321,76 @@ function normalizePathname(pathname) {
14922
15321
  }
14923
15322
  __name(normalizePathname, "normalizePathname");
14924
15323
 
15324
+ // src/routing/route-slots.ts
15325
+ var SLOT_SEGMENT = /^@([A-Za-z][A-Za-z0-9_-]*)$/;
15326
+ function parseRouteSlotFile(filePath) {
15327
+ const normalized = filePath.replace(/\\/g, "/");
15328
+ const parts = normalized.split("/").filter(Boolean);
15329
+ const fileName = parts.at(-1) ?? "";
15330
+ const baseName = fileName.replace(/\.(tsx?|jsx?)$/, "");
15331
+ if (baseName !== "page" && baseName !== "default") return null;
15332
+ const slotIndex = parts.findIndex((part) => SLOT_SEGMENT.test(part));
15333
+ if (slotIndex < 0) return null;
15334
+ if (parts.slice(slotIndex + 1, -1).some((part) => SLOT_SEGMENT.test(part))) {
15335
+ throw new Error(`Nested route slots are not supported in "${filePath}"`);
15336
+ }
15337
+ const slotMatch = parts[slotIndex].match(SLOT_SEGMENT);
15338
+ const name = slotMatch?.[1];
15339
+ if (!name) return null;
15340
+ const ownerParts = parts.slice(0, slotIndex);
15341
+ const contentParts = parts.slice(slotIndex + 1, -1);
15342
+ const marker = parseInterceptionMarker(contentParts[0]);
15343
+ let targetBase = ownerParts;
15344
+ let targetParts = contentParts;
15345
+ if (marker) {
15346
+ targetBase = marker.root === true ? [] : ownerParts.slice(0, Math.max(0, ownerParts.length - marker.up));
15347
+ targetParts = [...marker.remainder ? [marker.remainder] : [], ...contentParts.slice(1)];
15348
+ }
15349
+ const ownerFile = [...ownerParts, "layout.tsx"].join("/");
15350
+ const routeFile = [
15351
+ ...baseName === "default" ? ownerParts : targetBase,
15352
+ ...baseName === "default" ? [] : targetParts,
15353
+ "page.tsx"
15354
+ ].join("/");
15355
+ return {
15356
+ name,
15357
+ ownerRoute: parseRoutePath(ownerFile || "layout.tsx"),
15358
+ route: parseRoutePath(routeFile || "page.tsx"),
15359
+ interception: marker !== null,
15360
+ fallback: baseName === "default"
15361
+ };
15362
+ }
15363
+ __name(parseRouteSlotFile, "parseRouteSlotFile");
15364
+ function createRouteSlotContainerId(name, ownerPattern) {
15365
+ const owner = ownerPattern === "/" ? "root" : ownerPattern.replace(/^\//, "").replace(/[^A-Za-z0-9_-]+/g, "-").replace(/^-+|-+$/g, "");
15366
+ return `__farm_slot_${name}_${owner || "root"}__`;
15367
+ }
15368
+ __name(createRouteSlotContainerId, "createRouteSlotContainerId");
15369
+ function parseInterceptionMarker(segment) {
15370
+ if (!segment) return null;
15371
+ if (segment.startsWith("(...)")) {
15372
+ return {
15373
+ up: 0,
15374
+ root: true,
15375
+ remainder: segment.slice(5)
15376
+ };
15377
+ }
15378
+ if (segment.startsWith("(.)")) {
15379
+ return {
15380
+ up: 0,
15381
+ remainder: segment.slice(3)
15382
+ };
15383
+ }
15384
+ let remainder = segment;
15385
+ let up = 0;
15386
+ while (remainder.startsWith("(..)")) {
15387
+ up += 1;
15388
+ remainder = remainder.slice(4);
15389
+ }
15390
+ return up > 0 ? { up, remainder } : null;
15391
+ }
15392
+ __name(parseInterceptionMarker, "parseInterceptionMarker");
15393
+
14925
15394
  // src/routing/route-manager.ts
14926
15395
  function routeSpecificity(entry) {
14927
15396
  return entry.route.segments.reduce((score, segment) => {
@@ -14935,6 +15404,7 @@ var _RouteManager = class _RouteManager {
14935
15404
  constructor(config, viteServer) {
14936
15405
  this.routes = /* @__PURE__ */ new Map();
14937
15406
  this.layouts = /* @__PURE__ */ new Map();
15407
+ this.routeSlots = /* @__PURE__ */ new Map();
14938
15408
  this.loadings = /* @__PURE__ */ new Map();
14939
15409
  this.errors = /* @__PURE__ */ new Map();
14940
15410
  this.metadataImages = /* @__PURE__ */ new Map();
@@ -14950,6 +15420,7 @@ var _RouteManager = class _RouteManager {
14950
15420
  async discoverRoutes() {
14951
15421
  this.routes.clear();
14952
15422
  this.layouts.clear();
15423
+ this.routeSlots.clear();
14953
15424
  this.loadings.clear();
14954
15425
  this.errors.clear();
14955
15426
  this.metadataImages.clear();
@@ -14960,6 +15431,13 @@ var _RouteManager = class _RouteManager {
14960
15431
  await this.discoverFileRoutes(source);
14961
15432
  await this.discoverProgrammaticRoutes(source);
14962
15433
  }
15434
+ for (const slot of this.routeSlots.values()) {
15435
+ if (slot.interception && !this.routes.has(slot.pattern)) {
15436
+ throw new Error(
15437
+ `Intercepting route slot "${slot.name}" targets "${slot.pattern}", but no canonical page exists for that URL`
15438
+ );
15439
+ }
15440
+ }
14963
15441
  this.routes = new Map(
14964
15442
  Array.from(this.routes.entries()).sort(
14965
15443
  ([, left], [, right]) => routeSpecificity(right) - routeSpecificity(left)
@@ -14975,7 +15453,7 @@ var _RouteManager = class _RouteManager {
14975
15453
  /**
14976
15454
  * Find matching route for a given URL path
14977
15455
  */
14978
- matchRoute(pathname) {
15456
+ matchRoute(pathname, options = {}) {
14979
15457
  const routePathname = this.toRoutePathname(pathname);
14980
15458
  const normalizedPath = routePathname === "/" ? "/" : routePathname.replace(/\/$/, "");
14981
15459
  let matchedRoute = null;
@@ -14989,10 +15467,12 @@ var _RouteManager = class _RouteManager {
14989
15467
  }
14990
15468
  }
14991
15469
  const layouts = this.findMatchingLayouts(normalizedPath);
15470
+ const slots = this.findMatchingRouteSlots(normalizedPath, options.interceptFrom);
14992
15471
  return {
14993
15472
  route: matchedRoute,
14994
15473
  params,
14995
- layouts
15474
+ layouts,
15475
+ slots
14996
15476
  };
14997
15477
  }
14998
15478
  /**
@@ -15007,6 +15487,9 @@ var _RouteManager = class _RouteManager {
15007
15487
  getLayouts() {
15008
15488
  return new Map(this.layouts);
15009
15489
  }
15490
+ getRouteSlots() {
15491
+ return new Map(this.routeSlots);
15492
+ }
15010
15493
  /** Resolve route rules, inherited layouts, and the page export in precedence order. */
15011
15494
  async resolveRouteRuntimeConfig(pattern) {
15012
15495
  const routeEntry = this.routes.get(pattern);
@@ -15165,7 +15648,22 @@ var _RouteManager = class _RouteManager {
15165
15648
  pattern: entry.pattern,
15166
15649
  modulePath: toUrlPath(entry.modulePath)
15167
15650
  }));
15168
- return { routes, layouts };
15651
+ const slots = Array.from(this.routeSlots.values()).map((entry) => {
15652
+ const metadata = getClientModuleMetadata(entry.modulePath, projectRoot);
15653
+ return {
15654
+ name: entry.name,
15655
+ ownerPattern: entry.ownerPattern,
15656
+ pattern: entry.pattern,
15657
+ modulePath: toUrlPath(entry.modulePath),
15658
+ containerId: entry.containerId,
15659
+ interception: entry.interception,
15660
+ fallback: entry.fallback,
15661
+ shouldHydrate: metadata.shouldHydrate,
15662
+ isClientComponent: metadata.isClientComponent,
15663
+ segments: entry.route.segments
15664
+ };
15665
+ });
15666
+ return { routes, layouts, slots };
15169
15667
  }
15170
15668
  /**
15171
15669
  * Load a route module dynamically
@@ -15242,6 +15740,7 @@ var _RouteManager = class _RouteManager {
15242
15740
  async discoverFileRoutes(source) {
15243
15741
  const appDir = resolveAppPath(source.root, source.srcDir, "app");
15244
15742
  const pageFiles = await safeGlobFiles("**/page.{ts,tsx,js,jsx,md,mdx}", appDir);
15743
+ const slotDefaultFiles = await safeGlobFiles("**/@*/**/default.{ts,tsx,js,jsx}", appDir);
15245
15744
  const layoutFiles = await safeGlobFiles("**/layout.{ts,tsx,js,jsx}", appDir);
15246
15745
  const loadingFiles = await safeGlobFiles("**/loading.{ts,tsx,js,jsx}", appDir);
15247
15746
  const errorFiles = await safeGlobFiles("**/error.{ts,tsx,js,jsx}", appDir);
@@ -15250,7 +15749,7 @@ var _RouteManager = class _RouteManager {
15250
15749
  appDir
15251
15750
  );
15252
15751
  for (const [kind, files, target] of [
15253
- ["page", pageFiles, this.routes],
15752
+ ["page", pageFiles.filter((file) => parseRouteSlotFile(file) === null), this.routes],
15254
15753
  ["layout", layoutFiles, this.layouts],
15255
15754
  ["loading", loadingFiles, this.loadings],
15256
15755
  ["error", errorFiles, this.errors]
@@ -15274,6 +15773,32 @@ var _RouteManager = class _RouteManager {
15274
15773
  });
15275
15774
  }
15276
15775
  }
15776
+ for (const file of [...pageFiles, ...slotDefaultFiles]) {
15777
+ const slot = parseRouteSlotFile(file);
15778
+ if (!slot) continue;
15779
+ const modulePath = import_path5.default.join(appDir, file);
15780
+ const pattern = this.createRoutePattern(slot.route);
15781
+ const ownerPattern = this.createRoutePattern(slot.ownerRoute);
15782
+ const key = `${ownerPattern}:${slot.name}:${slot.interception ? "intercept" : "slot"}:${slot.fallback ? "default" : pattern}`;
15783
+ const existing = this.routeSlots.get(key);
15784
+ if (existing?.sourceRoot === source.root) {
15785
+ throw new Error(
15786
+ `Duplicate route slot "${slot.name}" for "${pattern}". Found both ${existing.modulePath} and ${modulePath}.`
15787
+ );
15788
+ }
15789
+ this.routeSlots.set(key, {
15790
+ route: slot.route,
15791
+ modulePath,
15792
+ pattern,
15793
+ source: "file",
15794
+ sourceRoot: source.root,
15795
+ name: slot.name,
15796
+ ownerPattern,
15797
+ interception: slot.interception,
15798
+ fallback: slot.fallback,
15799
+ containerId: createRouteSlotContainerId(slot.name, ownerPattern)
15800
+ });
15801
+ }
15277
15802
  for (const file of metadataImageFiles) {
15278
15803
  const fileBase = import_path5.default.basename(file, import_path5.default.extname(file));
15279
15804
  const kind = fileBase === "twitter-image" ? "twitter" : "opengraph";
@@ -15399,6 +15924,71 @@ var _RouteManager = class _RouteManager {
15399
15924
  }
15400
15925
  return matchingLayouts;
15401
15926
  }
15927
+ findMatchingRouteSlots(pathname, interceptFrom) {
15928
+ const groups = /* @__PURE__ */ new Map();
15929
+ for (const entry of this.routeSlots.values()) {
15930
+ if (!this.matchesRoutePrefix(pathname, entry.ownerPattern)) continue;
15931
+ const key = `${entry.ownerPattern}:${entry.name}`;
15932
+ const entries = groups.get(key) ?? [];
15933
+ entries.push(entry);
15934
+ groups.set(key, entries);
15935
+ }
15936
+ const normalizedFrom = interceptFrom ? this.toRoutePathname(new URL(interceptFrom, "http://farm.local").pathname) : void 0;
15937
+ const matches = [];
15938
+ for (const entries of groups.values()) {
15939
+ const candidates = entries.filter((entry) => !entry.fallback).filter(
15940
+ (entry) => !entry.interception || normalizedFrom !== void 0 && this.matchesRoutePrefix(normalizedFrom, entry.ownerPattern)
15941
+ ).map((entry) => ({
15942
+ entry,
15943
+ match: matchRoute(pathname, entry.route.segments)
15944
+ })).filter((candidate) => candidate.match.matches).sort((left, right) => {
15945
+ if (left.entry.interception !== right.entry.interception) {
15946
+ return left.entry.interception ? -1 : 1;
15947
+ }
15948
+ return routeSpecificity(right.entry) - routeSpecificity(left.entry);
15949
+ });
15950
+ const selected = candidates[0];
15951
+ if (selected) {
15952
+ matches.push({
15953
+ name: selected.entry.name,
15954
+ ownerPattern: selected.entry.ownerPattern,
15955
+ containerId: selected.entry.containerId,
15956
+ interception: selected.entry.interception,
15957
+ fallback: false,
15958
+ route: selected.entry,
15959
+ params: selected.match.params
15960
+ });
15961
+ continue;
15962
+ }
15963
+ const fallback = entries.find((entry) => entry.fallback);
15964
+ if (fallback) {
15965
+ matches.push({
15966
+ name: fallback.name,
15967
+ ownerPattern: fallback.ownerPattern,
15968
+ containerId: fallback.containerId,
15969
+ interception: false,
15970
+ fallback: true,
15971
+ route: fallback,
15972
+ params: {}
15973
+ });
15974
+ }
15975
+ }
15976
+ return matches.sort((left, right) => {
15977
+ const ownerDifference = left.route.route.segments.length - right.route.route.segments.length;
15978
+ return ownerDifference || left.name.localeCompare(right.name);
15979
+ });
15980
+ }
15981
+ matchesRoutePrefix(pathname, pattern) {
15982
+ if (pattern === "/") return true;
15983
+ const patternSegments = parseRoutePath(`${pattern}/page.tsx`).segments;
15984
+ const pathSegments = pathname.split("/").filter(Boolean);
15985
+ if (patternSegments.length > pathSegments.length) return false;
15986
+ for (let index = 0; index < patternSegments.length; index++) {
15987
+ const segment = patternSegments[index];
15988
+ if (!segment.isDynamic && segment.segment !== pathSegments[index]) return false;
15989
+ }
15990
+ return true;
15991
+ }
15402
15992
  findNearestBoundary(pathname, boundaries) {
15403
15993
  const normalizedPath = pathname === "/" ? "/" : pathname.replace(/\/$/, "");
15404
15994
  const pathSegments = normalizedPath.split("/").filter(Boolean);
@@ -16941,7 +17531,9 @@ var _ServerRenderer = class _ServerRenderer {
16941
17531
  async runWithRequestContext(request, fn) {
16942
17532
  return _runWithCurrentRequest(
16943
17533
  request,
16944
- () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, { redirect: false }) : fn()
17534
+ () => this.i18nRuntime?.config.enabled ? _runWithFarmI18nRequest(this.i18nRuntime, request, fn, {
17535
+ redirect: false
17536
+ }) : fn()
16945
17537
  );
16946
17538
  }
16947
17539
  async resolveRouteContext(input) {
@@ -16964,11 +17556,11 @@ var _ServerRenderer = class _ServerRenderer {
16964
17556
  /**
16965
17557
  * Check if a path should be served from SSG cache
16966
17558
  */
16967
- shouldServeSSG(pathname) {
17559
+ async shouldServeSSG(pathname) {
16968
17560
  const ssgPage = matchSSGPage(pathname, this.ssgManifest);
16969
17561
  if (!ssgPage) return null;
16970
- const cached = this.getCachedSSGPage(pathname);
16971
- if (cached && this.dataCache.isStale(cached)) {
17562
+ const cached = await this.getCachedSSGPage(pathname);
17563
+ if (cached && await this.dataCache.isStaleAsync(cached)) {
16972
17564
  this.regenerateSSGPage(ssgPage);
16973
17565
  }
16974
17566
  return ssgPage;
@@ -16980,12 +17572,12 @@ var _ServerRenderer = class _ServerRenderer {
16980
17572
  return createFarmCacheKey(["ppr", normalizeRevalidatePath(pathname), search]);
16981
17573
  }
16982
17574
  getCachedSSGPage(urlPath) {
16983
- return this.dataCache.getEntry(this.getSSGCacheKey(urlPath), {
17575
+ return this.dataCache.getEntryAsync(this.getSSGCacheKey(urlPath), {
16984
17576
  allowStale: true
16985
17577
  });
16986
17578
  }
16987
- cacheSSGPage(page2, html, options) {
16988
- this.dataCache.set(
17579
+ async cacheSSGPage(page2, html, options) {
17580
+ await this.dataCache.setAsync(
16989
17581
  this.getSSGCacheKey(page2.urlPath),
16990
17582
  { html, document: options.document },
16991
17583
  {
@@ -16997,11 +17589,11 @@ var _ServerRenderer = class _ServerRenderer {
16997
17589
  );
16998
17590
  }
16999
17591
  getCachedPPRShell(pathname, search) {
17000
- return this.dataCache.getEntry(this.getPPRCacheKey(pathname, search));
17592
+ return this.dataCache.getEntryAsync(this.getPPRCacheKey(pathname, search));
17001
17593
  }
17002
- cachePPRShell(options, html) {
17594
+ async cachePPRShell(options, html) {
17003
17595
  const key = this.getPPRCacheKey(options.pathname, options.search);
17004
- this.dataCache.set(
17596
+ await this.dataCache.setAsync(
17005
17597
  key,
17006
17598
  { html },
17007
17599
  {
@@ -17099,7 +17691,7 @@ var _ServerRenderer = class _ServerRenderer {
17099
17691
  }
17100
17692
  const { renderToString } = await import("react-dom/server");
17101
17693
  const html = renderToString(await this.wrapWithIntegrationProviders(pageElement));
17102
- this.cacheSSGPage(page2, html, { document: false });
17694
+ await this.cacheSSGPage(page2, html, { document: false });
17103
17695
  logger.info(`ISR: Regenerated ${page2.urlPath}`);
17104
17696
  } catch (error) {
17105
17697
  logger.error(`ISR regeneration failed for ${page2.urlPath}: ${error}`);
@@ -17113,7 +17705,7 @@ var _ServerRenderer = class _ServerRenderer {
17113
17705
  * Serve a pre-rendered SSG page
17114
17706
  */
17115
17707
  async serveSSGPage(req, res, page2) {
17116
- const cached = this.getCachedSSGPage(page2.urlPath);
17708
+ const cached = await this.getCachedSSGPage(page2.urlPath);
17117
17709
  if (cached) {
17118
17710
  res.setHeader("Content-Type", "text/html; charset=utf-8");
17119
17711
  res.setHeader("X-Farm-SSG", "cached");
@@ -17131,9 +17723,12 @@ var _ServerRenderer = class _ServerRenderer {
17131
17723
  if (fs2.existsSync(htmlPath)) {
17132
17724
  const stat = fs2.statSync(htmlPath);
17133
17725
  const html = fs2.readFileSync(htmlPath, "utf-8");
17134
- this.cacheSSGPage(page2, html, { document: true, createdAt: stat.mtimeMs });
17135
- const fileCacheEntry = this.getCachedSSGPage(page2.urlPath);
17136
- if (fileCacheEntry && this.dataCache.isStale(fileCacheEntry)) {
17726
+ await this.cacheSSGPage(page2, html, {
17727
+ document: true,
17728
+ createdAt: stat.mtimeMs
17729
+ });
17730
+ const fileCacheEntry = await this.getCachedSSGPage(page2.urlPath);
17731
+ if (fileCacheEntry && await this.dataCache.isStaleAsync(fileCacheEntry)) {
17137
17732
  this.regenerateSSGPage(page2);
17138
17733
  }
17139
17734
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -17179,6 +17774,7 @@ var _ServerRenderer = class _ServerRenderer {
17179
17774
  let pathname = "/";
17180
17775
  let params = {};
17181
17776
  let layouts = [];
17777
+ let routeSlots = [];
17182
17778
  let searchParamsObject = {};
17183
17779
  let middlewareMap = /* @__PURE__ */ new Map();
17184
17780
  let middlewareContext = /* @__PURE__ */ new Map();
@@ -17211,7 +17807,7 @@ var _ServerRenderer = class _ServerRenderer {
17211
17807
  }
17212
17808
  this.applyDeploymentHeaders(req, res);
17213
17809
  if (process.env.NODE_ENV === "production") {
17214
- const ssgPage = this.shouldServeSSG(pathname);
17810
+ const ssgPage = await this.shouldServeSSG(pathname);
17215
17811
  if (ssgPage) {
17216
17812
  const served = await this.serveSSGPage(req, res, ssgPage);
17217
17813
  if (served) {
@@ -17224,6 +17820,7 @@ var _ServerRenderer = class _ServerRenderer {
17224
17820
  const route = match.route;
17225
17821
  params = match.params;
17226
17822
  layouts = match.layouts;
17823
+ routeSlots = match.slots ?? [];
17227
17824
  if (!route) {
17228
17825
  emitFarmEvent({ type: "route.notFound", pathname });
17229
17826
  await this.render404(req, res);
@@ -17240,7 +17837,9 @@ var _ServerRenderer = class _ServerRenderer {
17240
17837
  errorBoundaryEntry = this.routeManager.getMatchingError(pathname);
17241
17838
  middlewareMap = toMiddlewareMap2(req.__FARM_MIDDLEWARE_DATA__);
17242
17839
  middlewareContext = toMiddlewareMap2(req.__FARM_MIDDLEWARE_CONTEXT__);
17243
- pluginExposedContext = getRequestContextSnapshot(req, { exposedOnly: true });
17840
+ pluginExposedContext = getRequestContextSnapshot(req, {
17841
+ exposedOnly: true
17842
+ });
17244
17843
  const currentRequest = createWebRequestFromFarmRequest(req);
17245
17844
  const routeContext = await this.resolveRouteContext({
17246
17845
  request: currentRequest,
@@ -17304,8 +17903,16 @@ var _ServerRenderer = class _ServerRenderer {
17304
17903
  revalidate: renderingConfig.revalidate
17305
17904
  } : void 0;
17306
17905
  if (renderingConfig.ppr && pprBypassReason) {
17307
- emitFarmEvent({ type: "ppr.shell.bypass", route: pathname, reason: pprBypassReason });
17308
- emitFarmEvent({ type: "cache.bypass", route: pathname, reason: pprBypassReason });
17906
+ emitFarmEvent({
17907
+ type: "ppr.shell.bypass",
17908
+ route: pathname,
17909
+ reason: pprBypassReason
17910
+ });
17911
+ emitFarmEvent({
17912
+ type: "cache.bypass",
17913
+ route: pathname,
17914
+ reason: pprBypassReason
17915
+ });
17309
17916
  if (pprBypassReason === "refresh") {
17310
17917
  pprRefreshRoute = pathname;
17311
17918
  emitFarmEvent({ type: "ppr.refresh.start", route: pathname });
@@ -17313,14 +17920,22 @@ var _ServerRenderer = class _ServerRenderer {
17313
17920
  }
17314
17921
  if (pprShellOptions) {
17315
17922
  const pprCacheKey = this.getPPRCacheKey(pathname, url.search);
17316
- const cachedPPRShell = this.getCachedPPRShell(pathname, url.search);
17923
+ const cachedPPRShell = await this.getCachedPPRShell(pathname, url.search);
17317
17924
  if (cachedPPRShell) {
17318
- emitFarmEvent({ type: "ppr.shell.hit", route: pathname, key: pprCacheKey });
17925
+ emitFarmEvent({
17926
+ type: "ppr.shell.hit",
17927
+ route: pathname,
17928
+ key: pprCacheKey
17929
+ });
17319
17930
  this.serveCachedPPRShell(res, cachedPPRShell.value, renderingConfig.revalidate);
17320
17931
  completeRender(res.statusCode || 200, pathname);
17321
17932
  return;
17322
17933
  }
17323
- emitFarmEvent({ type: "ppr.shell.miss", route: pathname, key: pprCacheKey });
17934
+ emitFarmEvent({
17935
+ type: "ppr.shell.miss",
17936
+ route: pathname,
17937
+ key: pprCacheKey
17938
+ });
17324
17939
  }
17325
17940
  let LoadingFallbackComponent = null;
17326
17941
  if (loadingBoundaryEntry) {
@@ -17344,14 +17959,77 @@ var _ServerRenderer = class _ServerRenderer {
17344
17959
  );
17345
17960
  const moduleMetadata = routeManifestEntry || getClientModuleMetadata(route.modulePath, this.config.root);
17346
17961
  const isClientComponent = moduleMetadata.isClientComponent;
17962
+ const renderedRouteSlots = await Promise.all(
17963
+ routeSlots.map(async (slot) => {
17964
+ const slotModule = await this.routeManager.loadRouteModule(slot.route.modulePath);
17965
+ if (!slotModule.default) {
17966
+ throw new Error(
17967
+ `Route slot "${slot.name}" module ${slot.route.modulePath} does not export a default component`
17968
+ );
17969
+ }
17970
+ const slotContext = await this.resolveRouteContext({
17971
+ request: currentRequest,
17972
+ rawRequest: req,
17973
+ params: slot.params,
17974
+ search: searchParamsObject,
17975
+ path: pathname
17976
+ });
17977
+ const rawSlotProps = withFarmRouteContext(
17978
+ {
17979
+ params: slot.params,
17980
+ searchParams: Promise.resolve(searchParamsObject),
17981
+ path: pathname,
17982
+ middleware: middlewareMap.size > 0 ? { data: middlewareMap } : void 0,
17983
+ context: pluginExposedContext.size > 0 ? { data: pluginExposedContext } : void 0
17984
+ },
17985
+ slotContext
17986
+ );
17987
+ const slotProps = await parseRouteModuleProps(slotModule, {
17988
+ props: rawSlotProps,
17989
+ search: searchParamsObject,
17990
+ routePath: slot.route.pattern
17991
+ });
17992
+ const metadata = getClientModuleMetadata(slot.route.modulePath, this.config.root);
17993
+ return {
17994
+ ...slot,
17995
+ module: slotModule,
17996
+ props: slotProps,
17997
+ isClientComponent: metadata.isClientComponent,
17998
+ shouldHydrate: metadata.shouldHydrate
17999
+ };
18000
+ })
18001
+ );
17347
18002
  const shouldHydrate = moduleMetadata.shouldHydrate;
18003
+ const hasHydratableRouteSlots = renderedRouteSlots.some(
18004
+ (slot) => slot.isClientComponent || slot.shouldHydrate
18005
+ );
17348
18006
  req.__FARM_PAGE_PATH__ = route.modulePath;
17349
18007
  req.__FARM_ROUTE__ = pathname;
17350
18008
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
17351
18009
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
18010
+ req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
17352
18011
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
17353
18012
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
17354
18013
  ) : null;
18014
+ req.__FARM_ROUTE_SLOTS__ = renderedRouteSlots.map((slot) => ({
18015
+ name: slot.name,
18016
+ ownerPattern: slot.ownerPattern,
18017
+ containerId: slot.containerId,
18018
+ interception: slot.interception,
18019
+ fallback: slot.fallback,
18020
+ modulePath: slot.route.modulePath,
18021
+ isClientComponent: slot.isClientComponent,
18022
+ shouldHydrate: slot.shouldHydrate,
18023
+ props: {
18024
+ params: slot.props.params,
18025
+ search: slot.props.search,
18026
+ searchParams: slot.props.search,
18027
+ ..."data" in slot.props ? { data: slot.props.data } : {},
18028
+ ...slot.props.__farmCanonicalPath ? { __farmCanonicalPath: slot.props.__farmCanonicalPath } : {},
18029
+ ...slot.props.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
18030
+ path: pathname
18031
+ }
18032
+ }));
17355
18033
  req.__FARM_PROPS__ = {
17356
18034
  params: pageProps.params,
17357
18035
  search: pageProps.search,
@@ -17411,12 +18089,32 @@ var _ServerRenderer = class _ServerRenderer {
17411
18089
  let wrappedElement = pageElement;
17412
18090
  for (let i = layoutModules.length - 1; i >= 0; i--) {
17413
18091
  const layoutModule = layoutModules[i];
18092
+ const layoutEntry = layouts[i];
17414
18093
  const LayoutComponent = layoutModule.default;
18094
+ const slotProps = {};
18095
+ for (const slot of renderedRouteSlots) {
18096
+ if (slot.ownerPattern !== layoutEntry.pattern) continue;
18097
+ let slotElement = import_react4.default.createElement(
18098
+ slot.module.default,
18099
+ slot.props
18100
+ );
18101
+ slotElement = import_react4.default.createElement(
18102
+ "div",
18103
+ {
18104
+ id: slot.containerId,
18105
+ "data-farm-route-slot": slot.name,
18106
+ "data-farm-slot-owner": slot.ownerPattern
18107
+ },
18108
+ slotElement
18109
+ );
18110
+ slotProps[slot.name] = slotElement;
18111
+ }
17415
18112
  wrappedElement = import_react4.default.createElement(
17416
18113
  LayoutComponent,
17417
18114
  {
17418
18115
  children: wrappedElement,
17419
- params
18116
+ params,
18117
+ ...slotProps
17420
18118
  }
17421
18119
  );
17422
18120
  }
@@ -17453,7 +18151,10 @@ var _ServerRenderer = class _ServerRenderer {
17453
18151
  routeManifest,
17454
18152
  captureStaticShell: Boolean(pprShellOptions),
17455
18153
  observabilityRoute: pathname,
17456
- onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({ type: "ppr.suspense.holeDetected", route: pathname }) : void 0,
18154
+ onSuspenseHoleDetected: pprShellOptions ? () => emitFarmEvent({
18155
+ type: "ppr.suspense.holeDetected",
18156
+ route: pathname
18157
+ }) : void 0,
17457
18158
  onComplete: pprShellOptions && req.method !== "HEAD" ? (html) => this.cachePPRShell(pprShellOptions, html) : void 0
17458
18159
  }
17459
18160
  );
@@ -17501,7 +18202,11 @@ var _ServerRenderer = class _ServerRenderer {
17501
18202
  }
17502
18203
  emitFarmEvent({ type: "render.error", route: pathname, error });
17503
18204
  if (pprRefreshRoute) {
17504
- emitFarmEvent({ type: "ppr.refresh.error", route: pprRefreshRoute, error });
18205
+ emitFarmEvent({
18206
+ type: "ppr.refresh.error",
18207
+ route: pprRefreshRoute,
18208
+ error
18209
+ });
17505
18210
  }
17506
18211
  logger.error(`Error rendering page: ${error}`);
17507
18212
  if (res.headersSent || res.writableEnded) {
@@ -17554,7 +18259,9 @@ var _ServerRenderer = class _ServerRenderer {
17554
18259
  if (typeof layoutModule.generateMetadata === "function") {
17555
18260
  metadata = mergeMetadata(
17556
18261
  metadata,
17557
- await layoutModule.generateMetadata({ params: options.pageProps.params })
18262
+ await layoutModule.generateMetadata({
18263
+ params: options.pageProps.params
18264
+ })
17558
18265
  );
17559
18266
  }
17560
18267
  }
@@ -17785,7 +18492,13 @@ var _ServerRenderer = class _ServerRenderer {
17785
18492
  clientEntry: "/@farm/client.js",
17786
18493
  routes: {},
17787
18494
  layouts: {},
17788
- sharedAssets: [{ tag: "link", attrs: { rel: "stylesheet", href: "/src/app/globals.css" } }]
18495
+ slots: [],
18496
+ sharedAssets: [
18497
+ {
18498
+ tag: "link",
18499
+ attrs: { rel: "stylesheet", href: "/src/app/globals.css" }
18500
+ }
18501
+ ]
17789
18502
  };
17790
18503
  for (const routeEntry of manifest.routes) {
17791
18504
  clientManifest.routes[routeEntry.pattern] = {
@@ -17807,9 +18520,26 @@ var _ServerRenderer = class _ServerRenderer {
17807
18520
  assets: []
17808
18521
  };
17809
18522
  }
17810
- const deferredProps = prepareDeferredData(req.__FARM_PROPS__ || {});
18523
+ for (const slotEntry of manifest.slots ?? []) {
18524
+ clientManifest.slots.push({
18525
+ ...slotEntry,
18526
+ preloads: [slotEntry.modulePath],
18527
+ assets: []
18528
+ });
18529
+ }
18530
+ const routeSlotPayload = (req.__FARM_ROUTE_SLOTS__ || []).map(
18531
+ (slot) => ({
18532
+ ...slot,
18533
+ modulePath: typeof slot.modulePath === "string" && slot.modulePath.startsWith(this.config.root) ? slot.modulePath.slice(this.config.root.length) : slot.modulePath
18534
+ })
18535
+ );
18536
+ const deferredProps = prepareDeferredData({
18537
+ page: req.__FARM_PROPS__ || {},
18538
+ slots: routeSlotPayload
18539
+ });
17811
18540
  const propsScript = `<script>
17812
- window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data)};
18541
+ window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data.page)};
18542
+ window.__FARM_ROUTE_SLOTS__ = ${serializeInlineValue(deferredProps.data.slots)};
17813
18543
  window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
17814
18544
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
17815
18545
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
@@ -17822,7 +18552,7 @@ window.__FARM_MANIFEST__ = ${JSON.stringify(clientManifest)};
17822
18552
  window.__FARM_INTEGRATION_API_MANIFEST__ = ${JSON.stringify(getRegisteredIntegrationAPIManifest())};
17823
18553
  ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(getFarmI18nClientSnapshot())};` : ""}
17824
18554
  </script>`;
17825
- const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true ? createPreHydrationClickQueueScript() : "";
18555
+ const hydrationClickQueueScript = isClientComponent || req.__FARM_SHOULD_HYDRATE__ === true || req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ === true ? createPreHydrationClickQueueScript() : "";
17826
18556
  const {
17827
18557
  title,
17828
18558
  tags: metaTags,
@@ -17933,7 +18663,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
17933
18663
  didError = true;
17934
18664
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
17935
18665
  logger.error(`SSR shell error: ${error}`);
17936
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
18666
+ emitFarmEvent({
18667
+ type: "render.error",
18668
+ route: observabilityRoute,
18669
+ error
18670
+ });
17937
18671
  }
17938
18672
  if (clearMiddlewareData) {
17939
18673
  clearMiddlewareData();
@@ -17944,7 +18678,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
17944
18678
  didError = true;
17945
18679
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
17946
18680
  logger.error(`SSR streaming error: ${error}`);
17947
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
18681
+ emitFarmEvent({
18682
+ type: "render.error",
18683
+ route: observabilityRoute,
18684
+ error
18685
+ });
17948
18686
  }
17949
18687
  }
17950
18688
  });
@@ -18120,6 +18858,7 @@ var _FarmApp = class _FarmApp {
18120
18858
  logger.info("Initializing Farm.js application...");
18121
18859
  }
18122
18860
  await initStorage(this.config.storage);
18861
+ await configureFarmCache(this.config.cache);
18123
18862
  await this.i18nRuntime.initialize();
18124
18863
  await this.verifyAppStructure();
18125
18864
  await this.routeManager.discoverRoutes();
@@ -18151,6 +18890,7 @@ var _FarmApp = class _FarmApp {
18151
18890
  preset: config.preset ?? "node-server",
18152
18891
  deploy: config.deploy || {},
18153
18892
  storage: config.storage || {},
18893
+ cache: config.cache || {},
18154
18894
  integrations: config.integrations || {},
18155
18895
  plugins: config.plugins || [],
18156
18896
  migrations: config.migrations || { commands: [] },
@@ -18629,7 +19369,7 @@ async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
18629
19369
  });
18630
19370
  let body = void 0;
18631
19371
  if (request.method.toUpperCase() !== "GET" && request.method.toUpperCase() !== "HEAD") {
18632
- body = await readJSONBody(request);
19372
+ body = await readRequestBody2(request);
18633
19373
  }
18634
19374
  const headers = Object.fromEntries(request.headers.entries());
18635
19375
  const types = endpoint.__types || {};
@@ -18733,17 +19473,50 @@ function isFarmContextHandler(endpoint) {
18733
19473
  return firstParameter === "ctx" || firstParameter === "context" || firstParameter.startsWith("{");
18734
19474
  }
18735
19475
  __name(isFarmContextHandler, "isFarmContextHandler");
18736
- async function readJSONBody(request) {
19476
+ async function readRequestBody2(request) {
19477
+ const contentType = request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
18737
19478
  try {
19479
+ if (contentType === "multipart/form-data") {
19480
+ return formDataToObject(await request.clone().formData());
19481
+ }
18738
19482
  const text2 = await request.clone().text();
18739
- if (text2) {
19483
+ if (!text2) return void 0;
19484
+ if (contentType === "application/x-www-form-urlencoded") {
19485
+ return searchParamsToObject2(new URLSearchParams(text2));
19486
+ }
19487
+ if (contentType === "application/json" || contentType?.endsWith("+json")) {
18740
19488
  return JSON.parse(text2);
18741
19489
  }
19490
+ return JSON.parse(text2);
18742
19491
  } catch {
18743
19492
  }
18744
19493
  return void 0;
18745
19494
  }
18746
- __name(readJSONBody, "readJSONBody");
19495
+ __name(readRequestBody2, "readRequestBody");
19496
+ function formDataToObject(formData) {
19497
+ return entriesToObject(formData.entries());
19498
+ }
19499
+ __name(formDataToObject, "formDataToObject");
19500
+ function searchParamsToObject2(searchParams) {
19501
+ return entriesToObject(searchParams.entries());
19502
+ }
19503
+ __name(searchParamsToObject2, "searchParamsToObject");
19504
+ function entriesToObject(entries) {
19505
+ const output = /* @__PURE__ */ Object.create(null);
19506
+ for (const [key, value] of entries) {
19507
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
19508
+ const current = output[key];
19509
+ if (current === void 0) {
19510
+ output[key] = value;
19511
+ } else if (Array.isArray(current)) {
19512
+ current.push(value);
19513
+ } else {
19514
+ output[key] = [current, value];
19515
+ }
19516
+ }
19517
+ return output;
19518
+ }
19519
+ __name(entriesToObject, "entriesToObject");
18747
19520
  function validateInput(schema, value, error) {
18748
19521
  if (!schema || typeof schema.parse !== "function") {
18749
19522
  return value;
@@ -19898,6 +20671,7 @@ async function generateRouteTypes(options) {
19898
20671
  absolute: false
19899
20672
  });
19900
20673
  for (const file of pageFiles) {
20674
+ if (parseRouteSlotFile(file)) continue;
19901
20675
  const route = parseRoutePath(file);
19902
20676
  if (route.type === "page") {
19903
20677
  const pattern = createRoutePattern(route);
@@ -20633,7 +21407,7 @@ function transformError(id, message) {
20633
21407
  __name(transformError, "transformError");
20634
21408
 
20635
21409
  // package.json
20636
- var version = "0.1.0-beta.3";
21410
+ var version = "0.1.0-beta.4";
20637
21411
 
20638
21412
  // src/version.ts
20639
21413
  var FARM_VERSION = version;
@@ -21352,7 +22126,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21352
22126
  }, "isProgrammaticRouteSourceFile");
21353
22127
  const isAppRuntimeFile = /* @__PURE__ */ __name((file) => {
21354
22128
  const normalized = file.replace(/\\/g, "/");
21355
- 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(
22129
+ 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(
21356
22130
  normalized
21357
22131
  );
21358
22132
  }, "isAppRuntimeFile");
@@ -22043,7 +22817,10 @@ function farmPlugin(options = {}, initialPluginManager) {
22043
22817
  method: req.method || "GET"
22044
22818
  });
22045
22819
  }
22046
- const match = routeManager3.matchRoute(targetPath);
22820
+ const interceptFromHeader = request.headers.get("x-farm-intercept-from") || void 0;
22821
+ const match = routeManager3.matchRoute(targetRequestUrl.pathname, {
22822
+ interceptFrom: interceptFromHeader
22823
+ });
22047
22824
  if (pm) {
22048
22825
  await pm.runHookParallel("afterRouteMatch", {
22049
22826
  pathname: targetPath,
@@ -22059,7 +22836,7 @@ function farmPlugin(options = {}, initialPluginManager) {
22059
22836
  res.end(JSON.stringify({ error: "Route not found" }));
22060
22837
  return;
22061
22838
  }
22062
- const { route, params, layouts } = match;
22839
+ const { route, params, layouts, slots } = match;
22063
22840
  if (!route) {
22064
22841
  res.statusCode = 404;
22065
22842
  res.setHeader("Content-Type", "application/json");
@@ -22114,6 +22891,54 @@ function farmPlugin(options = {}, initialPluginManager) {
22114
22891
  search: searchParams,
22115
22892
  routePath: route.pattern
22116
22893
  });
22894
+ const routeSlots = await Promise.all(
22895
+ slots.map(async (slot) => {
22896
+ const slotModule = await routeManager3.loadRouteModule(slot.route.modulePath);
22897
+ const slotMetadata = getClientModuleMetadata(
22898
+ slot.route.modulePath,
22899
+ server.config.root
22900
+ );
22901
+ const slotContext = await resolveFarmRouteContext(farmApp.getConfig(), {
22902
+ request,
22903
+ params: slot.params,
22904
+ search: searchParams,
22905
+ path: targetUrl.pathname
22906
+ });
22907
+ const slotProps = await parseRouteModuleProps2(slotModule, {
22908
+ props: withFarmRouteContext(
22909
+ {
22910
+ params: slot.params,
22911
+ searchParams: Promise.resolve(searchParams),
22912
+ path: targetUrl.pathname
22913
+ },
22914
+ slotContext
22915
+ ),
22916
+ search: searchParams,
22917
+ routePath: slot.route.pattern
22918
+ });
22919
+ return {
22920
+ name: slot.name,
22921
+ ownerPattern: slot.ownerPattern,
22922
+ containerId: slot.containerId,
22923
+ interception: slot.interception,
22924
+ fallback: slot.fallback,
22925
+ modulePath: slot.route.modulePath,
22926
+ isClientComponent: slotMetadata.isClientComponent,
22927
+ shouldHydrate: slotMetadata.shouldHydrate,
22928
+ props: {
22929
+ params: slotProps.params,
22930
+ search: slotProps.search,
22931
+ searchParams: slotProps.search,
22932
+ ..."data" in slotProps ? { data: slotProps.data } : {},
22933
+ ...slotProps.__farmCanonicalPath ? {
22934
+ __farmCanonicalPath: slotProps.__farmCanonicalPath
22935
+ } : {},
22936
+ ...slotProps.__farmRoutePropsResolved ? { __farmRoutePropsResolved: true } : {},
22937
+ path: targetUrl.pathname
22938
+ }
22939
+ };
22940
+ })
22941
+ );
22117
22942
  const projectRoot = server.config.root;
22118
22943
  const toUrlPath = /* @__PURE__ */ __name((absolutePath) => {
22119
22944
  if (absolutePath.startsWith(projectRoot)) {
@@ -22134,13 +22959,21 @@ function farmPlugin(options = {}, initialPluginManager) {
22134
22959
  },
22135
22960
  canonicalPath: routeProps.__farmCanonicalPath,
22136
22961
  modulePath: toUrlPath(route.modulePath),
22137
- isClientComponent,
22138
- shouldHydrate,
22962
+ isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
22963
+ shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
22139
22964
  metadata: {
22140
22965
  title: mergedMetadata.title,
22141
22966
  description: mergedMetadata.description
22142
22967
  },
22143
22968
  layoutModules: layouts.map((l) => toUrlPath(l.modulePath)),
22969
+ routeSlots: routeSlots.map((slot) => ({
22970
+ ...slot,
22971
+ modulePath: toUrlPath(slot.modulePath)
22972
+ })),
22973
+ interception: routeSlots.some((slot) => slot.interception) ? {
22974
+ from: interceptFromHeader,
22975
+ slots: routeSlots.filter((slot) => slot.interception).map((slot) => slot.name)
22976
+ } : void 0,
22144
22977
  i18n: getFarmI18nClientSnapshot()
22145
22978
  };
22146
22979
  await sendWebResponse(
@@ -22476,6 +23309,7 @@ function farmPlugin(options = {}, initialPluginManager) {
22476
23309
  export const getManifest = () => ({
22477
23310
  routes: {},
22478
23311
  layouts: {},
23312
+ slots: [],
22479
23313
  clientEntry: "/@farm/client.js",
22480
23314
  sharedAssets: []
22481
23315
  });
@@ -22486,6 +23320,7 @@ export const getManifest = () => ({
22486
23320
  clientEntry: "/@farm/client.js",
22487
23321
  routes: {},
22488
23322
  layouts: {},
23323
+ slots: [],
22489
23324
  sharedAssets: [
22490
23325
  {
22491
23326
  tag: "link",
@@ -22515,6 +23350,13 @@ export const getManifest = () => ({
22515
23350
  assets: []
22516
23351
  };
22517
23352
  }
23353
+ for (const slot of manifest.slots) {
23354
+ fullManifest.slots.push({
23355
+ ...slot,
23356
+ preloads: [slot.modulePath],
23357
+ assets: []
23358
+ });
23359
+ }
22518
23360
  return `
22519
23361
  // Auto-generated manifest for SPA navigation (TanStack Start pattern)
22520
23362
  // This manifest is inlined in the server bundle - no file on disk
@@ -23157,7 +23999,7 @@ window.__FARM_WRAP_PROVIDERS__ = wrapWithIntegrationProviders;
23157
23999
 
23158
24000
  // Get manifest from window (inlined by server in HTML)
23159
24001
  // Fallback to empty manifest if not available yet
23160
- const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, clientEntry: '', sharedAssets: [] };
24002
+ const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, slots: [], clientEntry: '', sharedAssets: [] };
23161
24003
 
23162
24004
  // ====== CLIENT-SIDE ROUTE MATCHING ======
23163
24005
  // Matches URL to route using manifest (no server request!)
@@ -23496,6 +24338,9 @@ let LayoutComponent = null;
23496
24338
 
23497
24339
  // Track if we've taken over rendering from SSR
23498
24340
  let hasClientTakenOver = false;
24341
+ const routeSlotRoots = new Map();
24342
+ const routeSlotDefinitions = new Map();
24343
+ let activeRouteInterception = null;
23499
24344
 
23500
24345
  function normalizeServerProps(rawProps) {
23501
24346
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -23508,6 +24353,123 @@ function normalizeServerProps(rawProps) {
23508
24353
  return props;
23509
24354
  }
23510
24355
 
24356
+ function getRouteSlotKey(slot) {
24357
+ return slot.ownerPattern + ':' + slot.name;
24358
+ }
24359
+
24360
+ async function renderRouteSlot(slot, mode = 'render') {
24361
+ const container = document.getElementById(slot.containerId);
24362
+ if (!container || !slot.modulePath) return false;
24363
+ if (mode === 'intercept' && !slot.isClientComponent && !slot.shouldHydrate) {
24364
+ return false;
24365
+ }
24366
+
24367
+ let slotModule = pageModuleCache.get(slot.modulePath);
24368
+ if (!slotModule) {
24369
+ slotModule = await import(/* @vite-ignore */ slot.modulePath);
24370
+ pageModuleCache.set(slot.modulePath, slotModule);
24371
+ }
24372
+ const SlotComponent = slotModule?.default;
24373
+ if (!SlotComponent) return false;
24374
+
24375
+ const props = reviveDeferredData(
24376
+ normalizeServerProps(slot.props || {}),
24377
+ window.__FARM_DEFERRED_DATA__ || {},
24378
+ );
24379
+ const element = wrapWithIntegrationProviders(
24380
+ React.createElement(SlotComponent, props),
24381
+ );
24382
+ const key = getRouteSlotKey(slot);
24383
+ const existingRoot = routeSlotRoots.get(key);
24384
+
24385
+ if (mode === 'hydrate') {
24386
+ if (existingRoot) return true;
24387
+ const root = hydrateRoot(container, element);
24388
+ routeSlotRoots.set(key, root);
24389
+ } else {
24390
+ if (existingRoot) {
24391
+ try { existingRoot.unmount(); } catch (error) {}
24392
+ }
24393
+ const root = createRoot(container);
24394
+ root.render(element);
24395
+ routeSlotRoots.set(key, root);
24396
+ }
24397
+
24398
+ routeSlotDefinitions.set(key, slot);
24399
+ return true;
24400
+ }
24401
+
24402
+ async function hydrateInitialRouteSlots() {
24403
+ const slots = Array.isArray(window.__FARM_ROUTE_SLOTS__)
24404
+ ? window.__FARM_ROUTE_SLOTS__
24405
+ : [];
24406
+ let hydrated = false;
24407
+
24408
+ for (const slot of slots) {
24409
+ routeSlotDefinitions.set(getRouteSlotKey(slot), slot);
24410
+ if (!slot.isClientComponent && !slot.shouldHydrate) continue;
24411
+ try {
24412
+ hydrated = (await renderRouteSlot(slot, 'hydrate')) || hydrated;
24413
+ } catch (error) {
24414
+ console.warn('[Farm.js] Could not hydrate route slot:', slot.name, error);
24415
+ }
24416
+ }
24417
+
24418
+ return hydrated;
24419
+ }
24420
+
24421
+ async function renderRouteInterception(pageData) {
24422
+ const slots = Array.isArray(pageData.routeSlots)
24423
+ ? pageData.routeSlots.filter((slot) => slot.interception)
24424
+ : [];
24425
+ if (!pageData.interception || slots.length === 0) return false;
24426
+
24427
+ const previous = new Map();
24428
+ for (const slot of slots) {
24429
+ const key = getRouteSlotKey(slot);
24430
+ previous.set(key, routeSlotDefinitions.get(key) || null);
24431
+ if (!(await renderRouteSlot(slot, 'intercept'))) {
24432
+ return false;
24433
+ }
24434
+ }
24435
+
24436
+ activeRouteInterception = {
24437
+ from: pageData.interception.from,
24438
+ slots,
24439
+ previous,
24440
+ };
24441
+ return true;
24442
+ }
24443
+
24444
+ async function clearRouteInterception(destination) {
24445
+ if (!activeRouteInterception) return false;
24446
+ const active = activeRouteInterception;
24447
+ activeRouteInterception = null;
24448
+
24449
+ for (const slot of active.slots) {
24450
+ const key = getRouteSlotKey(slot);
24451
+ const root = routeSlotRoots.get(key);
24452
+ if (root) {
24453
+ try { root.unmount(); } catch (error) {}
24454
+ routeSlotRoots.delete(key);
24455
+ }
24456
+
24457
+ const previous = active.previous.get(key);
24458
+ if (previous && (previous.isClientComponent || previous.shouldHydrate)) {
24459
+ await renderRouteSlot(previous, 'render');
24460
+ } else {
24461
+ const container = document.getElementById(slot.containerId);
24462
+ if (container) container.replaceChildren();
24463
+ if (previous) routeSlotDefinitions.set(key, previous);
24464
+ else routeSlotDefinitions.delete(key);
24465
+ }
24466
+ }
24467
+
24468
+ if (!active.from) return false;
24469
+ const background = new URL(active.from, window.location.origin);
24470
+ return destination === background.pathname + background.search;
24471
+ }
24472
+
23511
24473
  function applyCanonicalPathFromProps(props) {
23512
24474
  const canonicalPath = props && typeof props.__farmCanonicalPath === 'string'
23513
24475
  ? props.__farmCanonicalPath
@@ -23742,6 +24704,17 @@ async function tryHydrateImportedPage(
23742
24704
  async function renderPage(pageData) {
23743
24705
  const container = document.getElementById('root');
23744
24706
  if (!container) return;
24707
+ const destination = window.location.pathname + window.location.search;
24708
+
24709
+ if (pageData.interception) {
24710
+ if (await renderRouteInterception(pageData)) return;
24711
+ window.location.assign(destination);
24712
+ return;
24713
+ }
24714
+
24715
+ if (await clearRouteInterception(destination)) {
24716
+ return;
24717
+ }
23745
24718
 
23746
24719
  const route = {
23747
24720
  modulePath: pageData.modulePath,
@@ -23970,7 +24943,9 @@ async function hydrate() {
23970
24943
  window.__FARM_SHOULD_HYDRATE__ === true ||
23971
24944
  isClientComponent ||
23972
24945
  await moduleLooksClient(modulePath);
24946
+ const hydratedSlots = await hydrateInitialRouteSlots();
23973
24947
  if (!shouldHydrate) {
24948
+ if (hydratedSlots) replayPreHydrationClicks();
23974
24949
  console.log('[Farm.js] Server component - SPA router ready')
23975
24950
  return
23976
24951
  }
@@ -24354,7 +25329,7 @@ function createServerFn(options) {
24354
25329
  const middleware = resolveServerFnMiddleware(options.middleware);
24355
25330
  const serverFn = /* @__PURE__ */ __name(async (value) => {
24356
25331
  const formData = isFormData(value) ? value : void 0;
24357
- const rawInput = formData ? formDataToObject(formData) : value;
25332
+ const rawInput = formData ? formDataToObject2(formData) : value;
24358
25333
  const input = await _parseServerFnSchema(options.input, rawInput, "input");
24359
25334
  const executionContext = getServerActionExecutionContext();
24360
25335
  const request = executionContext?.request ?? _resolveCurrentRequest();
@@ -24366,8 +25341,21 @@ function createServerFn(options) {
24366
25341
  request,
24367
25342
  signal
24368
25343
  };
24369
- const result = await runServerFnMiddleware(middleware, handlerContext, options.handler);
24370
- return _parseServerFnSchema(options.output, result, "output");
25344
+ let resolvedContext = Object.freeze(/* @__PURE__ */ Object.create(null));
25345
+ const result = await runServerFnMiddleware(middleware, handlerContext, async (context) => {
25346
+ resolvedContext = context.context;
25347
+ return options.handler(context);
25348
+ });
25349
+ const parsedResult = await _parseServerFnSchema(options.output, result, "output");
25350
+ if (options.invalidates) {
25351
+ const declaration = typeof options.invalidates === "function" ? await options.invalidates({
25352
+ ...handlerContext,
25353
+ context: resolvedContext,
25354
+ result: parsedResult
25355
+ }) : options.invalidates;
25356
+ await applyFarmCacheInvalidationTargets(declaration);
25357
+ }
25358
+ return parsedResult;
24371
25359
  }, "serverFn");
24372
25360
  Object.defineProperties(serverFn, {
24373
25361
  [FARM_SERVER_FN_SYMBOL]: {
@@ -24389,6 +25377,10 @@ function createServerFn(options) {
24389
25377
  __farmServerFnMiddleware: {
24390
25378
  value: middleware,
24391
25379
  enumerable: false
25380
+ },
25381
+ __farmServerFnInvalidates: {
25382
+ value: options.invalidates,
25383
+ enumerable: false
24392
25384
  }
24393
25385
  });
24394
25386
  return serverFn;
@@ -24486,7 +25478,7 @@ function unwrapSafeParseResult(result) {
24486
25478
  return result;
24487
25479
  }
24488
25480
  __name(unwrapSafeParseResult, "unwrapSafeParseResult");
24489
- function formDataToObject(formData) {
25481
+ function formDataToObject2(formData) {
24490
25482
  const output = /* @__PURE__ */ Object.create(null);
24491
25483
  for (const [key, value] of formData.entries()) {
24492
25484
  if (!isSafeFormKey(key)) continue;
@@ -24501,7 +25493,7 @@ function formDataToObject(formData) {
24501
25493
  }
24502
25494
  return output;
24503
25495
  }
24504
- __name(formDataToObject, "formDataToObject");
25496
+ __name(formDataToObject2, "formDataToObject");
24505
25497
  function isSafeFormKey(key) {
24506
25498
  return !key.startsWith("$ACTION_") && !UNSAFE_FORM_KEYS.has(key);
24507
25499
  }
@@ -24563,7 +25555,7 @@ function createServerQuery(options) {
24563
25555
  tags: [createRouteDataCacheTag(routeKey)],
24564
25556
  revalidate: staleTime === false ? false : Math.max(1, Math.ceil(staleTime / 1e3))
24565
25557
  });
24566
- updatedAt = cache.getEntry(cacheKey, { allowStale: true })?.createdAt ?? Date.now();
25558
+ updatedAt = (await cache.getEntryAsync(cacheKey, { allowStale: true }))?.createdAt ?? Date.now();
24567
25559
  }
24568
25560
  return createFarmServerQueryResult(data, {
24569
25561
  key: clientKey,
@@ -25412,6 +26404,13 @@ function mergeFarmViteConfig(farmConfig, userConfig = {}) {
25412
26404
  ...farmConfig.server,
25413
26405
  ...userConfig.server
25414
26406
  },
26407
+ resolve: {
26408
+ ...farmConfig.resolve,
26409
+ ...userConfig.resolve,
26410
+ dedupe: Array.from(
26411
+ /* @__PURE__ */ new Set([...farmConfig.resolve?.dedupe || [], ...userConfig.resolve?.dedupe || []])
26412
+ )
26413
+ },
25415
26414
  optimizeDeps: {
25416
26415
  ...farmConfig.optimizeDeps,
25417
26416
  ...userConfig.optimizeDeps,
@@ -25438,6 +26437,7 @@ function mergeFarmViteConfig(farmConfig, userConfig = {}) {
25438
26437
  __name(mergeFarmViteConfig, "mergeFarmViteConfig");
25439
26438
 
25440
26439
  // src/server/create-server.ts
26440
+ var DEFAULT_FARM_DEV_SERVER_PORT = 3e3;
25441
26441
  function createBrandingPlugin() {
25442
26442
  let serverStarted = false;
25443
26443
  let startTime = Date.now();
@@ -25599,7 +26599,13 @@ async function createServer(config = {}) {
25599
26599
  createBrandingPlugin()
25600
26600
  ],
25601
26601
  server: {
25602
- middlewareMode: false
26602
+ middlewareMode: false,
26603
+ port: DEFAULT_FARM_DEV_SERVER_PORT,
26604
+ strictPort: true
26605
+ },
26606
+ resolve: {
26607
+ // Keep framework and application modules on one React runtime.
26608
+ dedupe: ["react", "react-dom"]
25603
26609
  },
25604
26610
  optimizeDeps: {
25605
26611
  // Avoid Vite scanning server/native-only deps from framework internals.
@@ -25674,7 +26680,7 @@ async function createServer(config = {}) {
25674
26680
  }
25675
26681
  }
25676
26682
  __name(createServer, "createServer");
25677
- async function startDevServer(config = {}, port = 3e3) {
26683
+ async function startDevServer(config = {}, port) {
25678
26684
  const server = await createServer(config);
25679
26685
  await server.listen(port);
25680
26686
  const pluginManager = server.__farmPluginManager;
@@ -25755,6 +26761,7 @@ function assertFunction(value, label) {
25755
26761
  __name(assertFunction, "assertFunction");
25756
26762
  // Annotate the CommonJS export names for ESM import in node:
25757
26763
  0 && (module.exports = {
26764
+ DEFAULT_FARM_DEV_SERVER_PORT,
25758
26765
  DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT,
25759
26766
  FARM_SERVER_QUERY_SYMBOL,
25760
26767
  FarmApp,
@@ -25767,7 +26774,9 @@ __name(assertFunction, "assertFunction");
25767
26774
  _runWithAfterRequest,
25768
26775
  _withAfterNodeMiddleware,
25769
26776
  after,
26777
+ applyFarmCacheInvalidationTargets,
25770
26778
  applyFarmCacheInvalidations,
26779
+ configureFarmCache,
25771
26780
  createClientOnlyFn,
25772
26781
  createFarmApp,
25773
26782
  createFarmCacheKey,
@@ -25833,6 +26842,7 @@ __name(assertFunction, "assertFunction");
25833
26842
  segmentsToPattern,
25834
26843
  sqliteStorage,
25835
26844
  startDevServer,
26845
+ storageCacheAdapter,
25836
26846
  toViteModuleId,
25837
26847
  unstable_cache,
25838
26848
  updateTag,