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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (193) hide show
  1. package/dist/agent-runtime.d.mts +2 -1
  2. package/dist/agent-runtime.d.ts +2 -1
  3. package/dist/api/route-manager.cjs +313 -10
  4. package/dist/api/route-manager.cjs.map +1 -1
  5. package/dist/api/route-manager.d.mts +3 -2
  6. package/dist/api/route-manager.d.ts +3 -2
  7. package/dist/api/route-manager.mjs +6 -5
  8. package/dist/api/runtime.cjs +313 -10
  9. package/dist/api/runtime.cjs.map +1 -1
  10. package/dist/api/runtime.d.mts +2 -1
  11. package/dist/api/runtime.d.ts +2 -1
  12. package/dist/api/runtime.mjs +4 -3
  13. package/dist/api.cjs +642 -57
  14. package/dist/api.cjs.map +1 -1
  15. package/dist/api.d.mts +3 -2
  16. package/dist/api.d.ts +3 -2
  17. package/dist/api.mjs +29 -9
  18. package/dist/{app-DOKxo-LH.d.ts → app-BjN-da13.d.ts} +38 -3
  19. package/dist/{app-BB3Iu3xh.d.mts → app-CakAr8Be.d.mts} +38 -3
  20. package/dist/app-markdown.d.mts +2 -1
  21. package/dist/app-markdown.d.ts +2 -1
  22. package/dist/cache.cjs +395 -12
  23. package/dist/cache.cjs.map +1 -1
  24. package/dist/cache.d.mts +100 -6
  25. package/dist/cache.d.ts +100 -6
  26. package/dist/cache.mjs +8 -2
  27. package/dist/{chunk-XUBZ7JI4.mjs → chunk-2CUKEMVR.mjs} +187 -16
  28. package/dist/chunk-2CUKEMVR.mjs.map +1 -0
  29. package/dist/{chunk-OHC6VCDI.mjs → chunk-2NHCNRF6.mjs} +521 -42
  30. package/dist/chunk-2NHCNRF6.mjs.map +1 -0
  31. package/dist/{chunk-6WXJ54TC.mjs → chunk-65A7QIKI.mjs} +5 -5
  32. package/dist/{chunk-6WXJ54TC.mjs.map → chunk-65A7QIKI.mjs.map} +1 -1
  33. package/dist/{chunk-5QH4IKY3.mjs → chunk-7N62DNSO.mjs} +2 -2
  34. package/dist/{chunk-XJSHJN4M.mjs → chunk-A2LUXHD2.mjs} +593 -49
  35. package/dist/chunk-A2LUXHD2.mjs.map +1 -0
  36. package/dist/chunk-ALCLXXTY.mjs +187 -0
  37. package/dist/chunk-ALCLXXTY.mjs.map +1 -0
  38. package/dist/{chunk-CSU4KJJF.mjs → chunk-C2ROET2V.mjs} +2 -2
  39. package/dist/{chunk-JNYVN7YP.mjs → chunk-EEABSBKK.mjs} +2 -2
  40. package/dist/{chunk-JNYVN7YP.mjs.map → chunk-EEABSBKK.mjs.map} +1 -1
  41. package/dist/{chunk-BIOIOWXZ.mjs → chunk-HNIQFZKB.mjs} +2 -2
  42. package/dist/{chunk-MH6FKW2I.mjs → chunk-J5YWQVI5.mjs} +4 -4
  43. package/dist/chunk-J5YWQVI5.mjs.map +1 -0
  44. package/dist/{chunk-G5I34UA6.mjs → chunk-JDEOUK5V.mjs} +4 -4
  45. package/dist/{chunk-T543TMFY.mjs → chunk-NM3CRAAL.mjs} +3 -2
  46. package/dist/chunk-NM3CRAAL.mjs.map +1 -0
  47. package/dist/{chunk-LWMGIEJA.mjs → chunk-PFE6GMNE.mjs} +47 -37
  48. package/dist/chunk-PFE6GMNE.mjs.map +1 -0
  49. package/dist/{chunk-6VS6CKAV.mjs → chunk-QM746KUO.mjs} +8 -1
  50. package/dist/chunk-QM746KUO.mjs.map +1 -0
  51. package/dist/{chunk-5FW6PQ3J.mjs → chunk-QTZZ2ISQ.mjs} +41 -16
  52. package/dist/chunk-QTZZ2ISQ.mjs.map +1 -0
  53. package/dist/{chunk-SHRJG6OG.mjs → chunk-RBRLLZL5.mjs} +18 -3
  54. package/dist/chunk-RBRLLZL5.mjs.map +1 -0
  55. package/dist/{chunk-NRRFGX45.mjs → chunk-TQAEEJB4.mjs} +3 -3
  56. package/dist/{chunk-45LKHSHJ.mjs → chunk-UB5UOSHB.mjs} +24 -12
  57. package/dist/chunk-UB5UOSHB.mjs.map +1 -0
  58. package/dist/chunk-V3XURYJO.mjs +756 -0
  59. package/dist/chunk-V3XURYJO.mjs.map +1 -0
  60. package/dist/{chunk-HZYXGV4K.mjs → chunk-VY2UYN5R.mjs} +24 -4
  61. package/dist/chunk-VY2UYN5R.mjs.map +1 -0
  62. package/dist/{chunk-C33RALN7.mjs → chunk-W2CRQFF6.mjs} +79 -15
  63. package/dist/chunk-W2CRQFF6.mjs.map +1 -0
  64. package/dist/{chunk-LG3YINQN.mjs → chunk-Z5CQMP54.mjs} +3 -3
  65. package/dist/{client-DiViK9yK.d.mts → client-BT1mFQas.d.mts} +23 -4
  66. package/dist/{client-DpEBJbts.d.ts → client-Nn0q4eO4.d.ts} +23 -4
  67. package/dist/{client-plugins-CTnsZz_A.d.ts → client-plugins-Cw4qUGsw.d.ts} +6 -0
  68. package/dist/{client-plugins-Cnr0D3kk.d.mts → client-plugins-DQQ1adXM.d.mts} +6 -0
  69. package/dist/client-plugins.d.mts +2 -1
  70. package/dist/client-plugins.d.ts +2 -1
  71. package/dist/client.cjs +665 -82
  72. package/dist/client.cjs.map +1 -1
  73. package/dist/client.d.mts +71 -5
  74. package/dist/client.d.ts +71 -5
  75. package/dist/client.mjs +10 -7
  76. package/dist/config.d.mts +2 -1
  77. package/dist/config.d.ts +2 -1
  78. package/dist/{devtools-HE4FTUDN.mjs → devtools-LVC3VB5P.mjs} +4 -4
  79. package/dist/docs.cjs +11 -1
  80. package/dist/docs.cjs.map +1 -1
  81. package/dist/docs.mjs +4 -4
  82. package/dist/index.cjs +2113 -198
  83. package/dist/index.cjs.map +1 -1
  84. package/dist/index.d.mts +16 -16
  85. package/dist/index.d.ts +16 -16
  86. package/dist/index.mjs +49 -21
  87. package/dist/index.mjs.map +1 -1
  88. package/dist/integrations.d.mts +2 -1
  89. package/dist/integrations.d.ts +2 -1
  90. package/dist/internal/build-runtime.cjs +1455 -90
  91. package/dist/internal/build-runtime.cjs.map +1 -1
  92. package/dist/internal/build-runtime.d.mts +4 -3
  93. package/dist/internal/build-runtime.d.ts +4 -3
  94. package/dist/internal/build-runtime.mjs +15 -14
  95. package/dist/internal/config-runtime.cjs +12 -1
  96. package/dist/internal/config-runtime.cjs.map +1 -1
  97. package/dist/internal/config-runtime.d.mts +3 -2
  98. package/dist/internal/config-runtime.d.ts +3 -2
  99. package/dist/internal/config-runtime.mjs +3 -3
  100. package/dist/internal/production-runtime.cjs +282 -6
  101. package/dist/internal/production-runtime.cjs.map +1 -1
  102. package/dist/internal/production-runtime.d.mts +4 -4
  103. package/dist/internal/production-runtime.d.ts +4 -4
  104. package/dist/internal/production-runtime.mjs +4 -2
  105. package/dist/{metadata-eoVmHoyv.d.ts → metadata-BAPg_Nmo.d.ts} +1 -1
  106. package/dist/{metadata-Dp8AdvsB.d.mts → metadata-_6i9oFG3.d.mts} +1 -1
  107. package/dist/middleware.d.mts +4 -3
  108. package/dist/middleware.d.ts +4 -3
  109. package/dist/navigation.cjs +40 -15
  110. package/dist/navigation.cjs.map +1 -1
  111. package/dist/navigation.mjs +2 -2
  112. package/dist/plugin.cjs +913 -53
  113. package/dist/plugin.cjs.map +1 -1
  114. package/dist/plugin.d.mts +2 -1
  115. package/dist/plugin.d.ts +2 -1
  116. package/dist/plugin.mjs +11 -10
  117. package/dist/{production-runtime-BqTnUTai.d.ts → production-runtime-BoJDc5mc.d.ts} +1 -1
  118. package/dist/{production-runtime-DtDdG9fc.d.mts → production-runtime-DPfgMV9Y.d.mts} +1 -1
  119. package/dist/routes.cjs +276 -6
  120. package/dist/routes.cjs.map +1 -1
  121. package/dist/routes.d.mts +1 -1
  122. package/dist/routes.d.ts +1 -1
  123. package/dist/routes.mjs +3 -3
  124. package/dist/{runtime-C1B7pSGT.d.mts → runtime-9nQjVM7X.d.mts} +8 -9
  125. package/dist/{runtime-CdPHt0bv.d.ts → runtime-DgeS2skB.d.ts} +8 -9
  126. package/dist/server-action-security.cjs +28 -10
  127. package/dist/server-action-security.cjs.map +1 -1
  128. package/dist/server-action-security.d.mts +2 -1
  129. package/dist/server-action-security.d.ts +2 -1
  130. package/dist/server-action-security.mjs +2 -2
  131. package/dist/server-fn-client.d.mts +1 -0
  132. package/dist/server-fn-client.d.ts +1 -0
  133. package/dist/server-fn.cjs +785 -3
  134. package/dist/server-fn.cjs.map +1 -1
  135. package/dist/server-fn.d.mts +10 -1
  136. package/dist/server-fn.d.ts +10 -1
  137. package/dist/server-fn.mjs +6 -3
  138. package/dist/server-plugins.d.mts +3 -2
  139. package/dist/server-plugins.d.ts +3 -2
  140. package/dist/server-query-client.cjs +279 -8
  141. package/dist/server-query-client.cjs.map +1 -1
  142. package/dist/server-query-client.mjs +4 -4
  143. package/dist/server-query.cjs +381 -10
  144. package/dist/server-query.cjs.map +1 -1
  145. package/dist/server-query.mjs +5 -5
  146. package/dist/server.cjs +1086 -76
  147. package/dist/server.cjs.map +1 -1
  148. package/dist/server.d.mts +8 -7
  149. package/dist/server.d.ts +8 -7
  150. package/dist/server.mjs +31 -16
  151. package/dist/server.mjs.map +1 -1
  152. package/dist/testing.cjs +341 -20
  153. package/dist/testing.cjs.map +1 -1
  154. package/dist/testing.d.mts +3 -2
  155. package/dist/testing.d.ts +3 -2
  156. package/dist/testing.mjs +7 -6
  157. package/dist/testing.mjs.map +1 -1
  158. package/dist/transport-BmbGTXA5.d.mts +49 -0
  159. package/dist/transport-BmbGTXA5.d.ts +49 -0
  160. package/dist/{utils-8USa5BiY.d.mts → utils-CHXlMDkg.d.mts} +1 -1
  161. package/dist/{utils-DKJb6K6G.d.ts → utils-Drsyz1Vj.d.ts} +1 -1
  162. package/dist/version.cjs +1 -1
  163. package/dist/version.cjs.map +1 -1
  164. package/dist/version.mjs +1 -1
  165. package/dist/vite.cjs +914 -54
  166. package/dist/vite.cjs.map +1 -1
  167. package/dist/vite.d.mts +2 -1
  168. package/dist/vite.d.ts +2 -1
  169. package/dist/vite.mjs +11 -10
  170. package/package.json +1 -1
  171. package/types/api.d.ts +44 -0
  172. package/types/client.d.ts +85 -4
  173. package/dist/chunk-45LKHSHJ.mjs.map +0 -1
  174. package/dist/chunk-5FW6PQ3J.mjs.map +0 -1
  175. package/dist/chunk-6VS6CKAV.mjs.map +0 -1
  176. package/dist/chunk-C33RALN7.mjs.map +0 -1
  177. package/dist/chunk-GZWT6VI5.mjs +0 -382
  178. package/dist/chunk-GZWT6VI5.mjs.map +0 -1
  179. package/dist/chunk-HZYXGV4K.mjs.map +0 -1
  180. package/dist/chunk-LWMGIEJA.mjs.map +0 -1
  181. package/dist/chunk-MH6FKW2I.mjs.map +0 -1
  182. package/dist/chunk-OHC6VCDI.mjs.map +0 -1
  183. package/dist/chunk-SHRJG6OG.mjs.map +0 -1
  184. package/dist/chunk-T543TMFY.mjs.map +0 -1
  185. package/dist/chunk-XJSHJN4M.mjs.map +0 -1
  186. package/dist/chunk-XUBZ7JI4.mjs.map +0 -1
  187. /package/dist/{chunk-5QH4IKY3.mjs.map → chunk-7N62DNSO.mjs.map} +0 -0
  188. /package/dist/{chunk-CSU4KJJF.mjs.map → chunk-C2ROET2V.mjs.map} +0 -0
  189. /package/dist/{chunk-BIOIOWXZ.mjs.map → chunk-HNIQFZKB.mjs.map} +0 -0
  190. /package/dist/{chunk-G5I34UA6.mjs.map → chunk-JDEOUK5V.mjs.map} +0 -0
  191. /package/dist/{chunk-NRRFGX45.mjs.map → chunk-TQAEEJB4.mjs.map} +0 -0
  192. /package/dist/{chunk-LG3YINQN.mjs.map → chunk-Z5CQMP54.mjs.map} +0 -0
  193. /package/dist/{devtools-HE4FTUDN.mjs.map → devtools-LVC3VB5P.mjs.map} +0 -0
@@ -4225,7 +4225,8 @@ var init_route_rules = __esm({
4225
4225
  // src/cache-invalidation.ts
4226
4226
  function getFarmCacheInvalidationState() {
4227
4227
  return globalState[FARM_CACHE_INVALIDATION_STATE] ?? (globalState[FARM_CACHE_INVALIDATION_STATE] = {
4228
- listeners: /* @__PURE__ */ new Set()
4228
+ listeners: /* @__PURE__ */ new Set(),
4229
+ taskListeners: /* @__PURE__ */ new Set()
4229
4230
  });
4230
4231
  }
4231
4232
  function encodeFarmCacheInvalidations(keys) {
@@ -4251,6 +4252,11 @@ function subscribeFarmCacheInvalidation(listener) {
4251
4252
  state.listeners.add(listener);
4252
4253
  return () => state.listeners.delete(listener);
4253
4254
  }
4255
+ function subscribeFarmCacheTask(listener) {
4256
+ const state = getFarmCacheInvalidationState();
4257
+ state.taskListeners.add(listener);
4258
+ return () => state.taskListeners.delete(listener);
4259
+ }
4254
4260
  var FARM_CACHE_INVALIDATION_HEADER, FARM_CACHE_INVALIDATION_STATE, globalState;
4255
4261
  var init_cache_invalidation = __esm({
4256
4262
  "src/cache-invalidation.ts"() {
@@ -4262,6 +4268,7 @@ var init_cache_invalidation = __esm({
4262
4268
  __name(encodeFarmCacheInvalidations, "encodeFarmCacheInvalidations");
4263
4269
  __name(decodeFarmCacheInvalidations, "decodeFarmCacheInvalidations");
4264
4270
  __name(subscribeFarmCacheInvalidation, "subscribeFarmCacheInvalidation");
4271
+ __name(subscribeFarmCacheTask, "subscribeFarmCacheTask");
4265
4272
  }
4266
4273
  });
4267
4274
 
@@ -4365,6 +4372,9 @@ var init_server_action_security = __esm({
4365
4372
  subscribeFarmCacheInvalidation((key) => {
4366
4373
  getServerActionExecutionContext()?.invalidations.add(key);
4367
4374
  });
4375
+ subscribeFarmCacheTask((task) => {
4376
+ getServerActionExecutionContext()?.cacheTasks.add(task);
4377
+ });
4368
4378
  __name(getServerActionStorage, "getServerActionStorage");
4369
4379
  __name(parseBodySizeLimit, "parseBodySizeLimit");
4370
4380
  __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
@@ -5522,6 +5532,7 @@ async function resolveConfig(userConfig, mode) {
5522
5532
  observability: userConfig.observability ?? false,
5523
5533
  devtools: resolveFarmDevtoolsConfig(userConfig.devtools, mode),
5524
5534
  storage: userConfig.storage || {},
5535
+ cache: userConfig.cache || {},
5525
5536
  suppressLintOnLink: userConfig.suppressLintOnLink ?? false,
5526
5537
  experimental: {
5527
5538
  serverComponents: false,
@@ -6316,6 +6327,9 @@ var init_bridge = __esm({
6316
6327
  function getFarmDataCache() {
6317
6328
  return sharedFarmDataCache;
6318
6329
  }
6330
+ function configureFarmCache(config) {
6331
+ sharedFarmDataCache.configure(config);
6332
+ }
6319
6333
  function createPathCacheTag(routePath) {
6320
6334
  return `path:${normalizeRevalidatePath(routePath)}`;
6321
6335
  }
@@ -6369,6 +6383,46 @@ function normalizeCacheTag(tag) {
6369
6383
  }
6370
6384
  return normalized;
6371
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
+ }
6372
6426
  function getFunctionCacheIdentity(fn) {
6373
6427
  if (fn.name) {
6374
6428
  return `name:${fn.name}`;
@@ -6429,11 +6483,51 @@ var init_cache = __esm({
6429
6483
  init_bridge();
6430
6484
  init_cache_invalidation();
6431
6485
  _FarmDataCache = class _FarmDataCache {
6432
- constructor() {
6486
+ constructor(config = {}) {
6433
6487
  this.entries = /* @__PURE__ */ new Map();
6434
6488
  this.inflight = /* @__PURE__ */ new Map();
6435
6489
  this.invalidatedTagVersions = /* @__PURE__ */ new Map();
6436
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;
6437
6531
  }
6438
6532
  get size() {
6439
6533
  return this.entries.size;
@@ -6469,6 +6563,45 @@ var init_cache = __esm({
6469
6563
  });
6470
6564
  return this.toPublicEntry(entry);
6471
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
+ }
6472
6605
  set(key, value, options = {}) {
6473
6606
  const tags = /* @__PURE__ */ new Set();
6474
6607
  for (const tag of options.tags ?? []) {
@@ -6481,6 +6614,7 @@ var init_cache = __esm({
6481
6614
  key,
6482
6615
  value,
6483
6616
  tags,
6617
+ tagVersions: void 0,
6484
6618
  createdAt: options.createdAt ?? Date.now(),
6485
6619
  createdVersion: ++this.version,
6486
6620
  revalidate: normalizeRevalidate(options.revalidate)
@@ -6494,11 +6628,45 @@ var init_cache = __esm({
6494
6628
  });
6495
6629
  return this.toPublicEntry(entry);
6496
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
+ }
6497
6657
  delete(key) {
6498
6658
  const deleted = this.entries.delete(key);
6499
6659
  emitFarmEvent({ type: "cache.delete", key, deleted });
6500
6660
  return deleted;
6501
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
+ }
6502
6670
  clear() {
6503
6671
  const count = this.entries.size;
6504
6672
  this.entries.clear();
@@ -6507,6 +6675,10 @@ var init_cache = __esm({
6507
6675
  this.version = 0;
6508
6676
  emitFarmEvent({ type: "cache.clear", count });
6509
6677
  }
6678
+ async clearAsync() {
6679
+ this.clear();
6680
+ await this.adapter?.clear?.();
6681
+ }
6510
6682
  isStale(entry, now = Date.now()) {
6511
6683
  if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now - entry.createdAt >= entry.revalidate * 1e3) {
6512
6684
  return true;
@@ -6519,6 +6691,12 @@ var init_cache = __esm({
6519
6691
  }
6520
6692
  return false;
6521
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
+ }
6522
6700
  revalidateTag(tag, options = {}) {
6523
6701
  const normalized = normalizeCacheTag(tag);
6524
6702
  const count = this.invalidateTag(normalized);
@@ -6527,6 +6705,15 @@ var init_cache = __esm({
6527
6705
  );
6528
6706
  return count;
6529
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
+ }
6530
6717
  revalidatePath(routePath) {
6531
6718
  const normalizedPath = normalizeRevalidatePath(routePath);
6532
6719
  const pathTag = createPathCacheTag(normalizedPath);
@@ -6543,8 +6730,25 @@ var init_cache = __esm({
6543
6730
  }
6544
6731
  return count;
6545
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
+ }
6546
6750
  async getOrSet(key, producer, options = {}) {
6547
- const cached = this.getEntry(key);
6751
+ const cached = await this.getEntryAsync(key);
6548
6752
  if (cached) {
6549
6753
  return cached.value;
6550
6754
  }
@@ -6553,10 +6757,8 @@ var init_cache = __esm({
6553
6757
  emitFarmEvent({ type: "cache.dedupe", key });
6554
6758
  return inflight;
6555
6759
  }
6556
- const promise = Promise.resolve().then(producer).then((value) => {
6557
- this.set(key, value, options);
6558
- return value;
6559
- }).catch((error) => {
6760
+ const tags = Array.from(normalizeCacheOptionsTags(options));
6761
+ const promise = this.fillCacheEntry(key, producer, options, tags).catch((error) => {
6560
6762
  emitFarmEvent({ type: "cache.error", key, operation: "set", error });
6561
6763
  throw error;
6562
6764
  }).finally(() => {
@@ -6565,6 +6767,40 @@ var init_cache = __esm({
6565
6767
  this.inflight.set(key, promise);
6566
6768
  return promise;
6567
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
+ }
6568
6804
  countEntriesForTag(tag) {
6569
6805
  let count = 0;
6570
6806
  for (const entry of this.entries.values()) {
@@ -6592,11 +6828,53 @@ var init_cache = __esm({
6592
6828
  key: entry.key,
6593
6829
  value: entry.value,
6594
6830
  tags: Array.from(entry.tags),
6831
+ tagVersions: entry.tagVersions,
6595
6832
  createdAt: entry.createdAt,
6596
6833
  createdVersion: entry.createdVersion,
6597
6834
  revalidate: entry.revalidate
6598
6835
  };
6599
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
+ }
6600
6878
  };
6601
6879
  __name(_FarmDataCache, "FarmDataCache");
6602
6880
  FarmDataCache = _FarmDataCache;
@@ -6606,6 +6884,7 @@ var init_cache = __esm({
6606
6884
  functionIds = /* @__PURE__ */ new WeakMap();
6607
6885
  nextFunctionId = 0;
6608
6886
  __name(getFarmDataCache, "getFarmDataCache");
6887
+ __name(configureFarmCache, "configureFarmCache");
6609
6888
  __name(createPathCacheTag, "createPathCacheTag");
6610
6889
  __name(createRouteDataCacheTag, "createRouteDataCacheTag");
6611
6890
  __name(createRouteDataCacheKey, "createRouteDataCacheKey");
@@ -6613,6 +6892,12 @@ var init_cache = __esm({
6613
6892
  __name(createFarmCacheKey, "createFarmCacheKey");
6614
6893
  __name(normalizeRevalidate, "normalizeRevalidate");
6615
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");
6616
6901
  __name(getFunctionCacheIdentity, "getFunctionCacheIdentity");
6617
6902
  __name(stableSerialize, "stableSerialize");
6618
6903
  }
@@ -7734,6 +8019,83 @@ var init_routing = __esm({
7734
8019
  }
7735
8020
  });
7736
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
+
7737
8099
  // src/routing/route-manager.ts
7738
8100
  function routeSpecificity(entry) {
7739
8101
  return entry.route.segments.reduce((score, segment) => {
@@ -7805,11 +8167,13 @@ var init_route_manager = __esm({
7805
8167
  init_static_metadata_image();
7806
8168
  init_route_runtime();
7807
8169
  init_routing();
8170
+ init_route_slots();
7808
8171
  __name(routeSpecificity, "routeSpecificity");
7809
8172
  _RouteManager = class _RouteManager {
7810
8173
  constructor(config, viteServer) {
7811
8174
  this.routes = /* @__PURE__ */ new Map();
7812
8175
  this.layouts = /* @__PURE__ */ new Map();
8176
+ this.routeSlots = /* @__PURE__ */ new Map();
7813
8177
  this.loadings = /* @__PURE__ */ new Map();
7814
8178
  this.errors = /* @__PURE__ */ new Map();
7815
8179
  this.metadataImages = /* @__PURE__ */ new Map();
@@ -7825,6 +8189,7 @@ var init_route_manager = __esm({
7825
8189
  async discoverRoutes() {
7826
8190
  this.routes.clear();
7827
8191
  this.layouts.clear();
8192
+ this.routeSlots.clear();
7828
8193
  this.loadings.clear();
7829
8194
  this.errors.clear();
7830
8195
  this.metadataImages.clear();
@@ -7835,6 +8200,13 @@ var init_route_manager = __esm({
7835
8200
  await this.discoverFileRoutes(source);
7836
8201
  await this.discoverProgrammaticRoutes(source);
7837
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
+ }
7838
8210
  this.routes = new Map(
7839
8211
  Array.from(this.routes.entries()).sort(
7840
8212
  ([, left], [, right]) => routeSpecificity(right) - routeSpecificity(left)
@@ -7850,7 +8222,7 @@ var init_route_manager = __esm({
7850
8222
  /**
7851
8223
  * Find matching route for a given URL path
7852
8224
  */
7853
- matchRoute(pathname) {
8225
+ matchRoute(pathname, options = {}) {
7854
8226
  const routePathname = this.toRoutePathname(pathname);
7855
8227
  const normalizedPath = routePathname === "/" ? "/" : routePathname.replace(/\/$/, "");
7856
8228
  let matchedRoute = null;
@@ -7864,10 +8236,12 @@ var init_route_manager = __esm({
7864
8236
  }
7865
8237
  }
7866
8238
  const layouts = this.findMatchingLayouts(normalizedPath);
8239
+ const slots = this.findMatchingRouteSlots(normalizedPath, options.interceptFrom);
7867
8240
  return {
7868
8241
  route: matchedRoute,
7869
8242
  params,
7870
- layouts
8243
+ layouts,
8244
+ slots
7871
8245
  };
7872
8246
  }
7873
8247
  /**
@@ -7882,6 +8256,9 @@ var init_route_manager = __esm({
7882
8256
  getLayouts() {
7883
8257
  return new Map(this.layouts);
7884
8258
  }
8259
+ getRouteSlots() {
8260
+ return new Map(this.routeSlots);
8261
+ }
7885
8262
  /** Resolve route rules, inherited layouts, and the page export in precedence order. */
7886
8263
  async resolveRouteRuntimeConfig(pattern) {
7887
8264
  const routeEntry = this.routes.get(pattern);
@@ -8040,7 +8417,22 @@ var init_route_manager = __esm({
8040
8417
  pattern: entry.pattern,
8041
8418
  modulePath: toUrlPath(entry.modulePath)
8042
8419
  }));
8043
- 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 };
8044
8436
  }
8045
8437
  /**
8046
8438
  * Load a route module dynamically
@@ -8117,6 +8509,7 @@ var init_route_manager = __esm({
8117
8509
  async discoverFileRoutes(source) {
8118
8510
  const appDir = resolveAppPath(source.root, source.srcDir, "app");
8119
8511
  const pageFiles = await safeGlobFiles("**/page.{ts,tsx,js,jsx,md,mdx}", appDir);
8512
+ const slotDefaultFiles = await safeGlobFiles("**/@*/**/default.{ts,tsx,js,jsx}", appDir);
8120
8513
  const layoutFiles = await safeGlobFiles("**/layout.{ts,tsx,js,jsx}", appDir);
8121
8514
  const loadingFiles = await safeGlobFiles("**/loading.{ts,tsx,js,jsx}", appDir);
8122
8515
  const errorFiles = await safeGlobFiles("**/error.{ts,tsx,js,jsx}", appDir);
@@ -8125,7 +8518,7 @@ var init_route_manager = __esm({
8125
8518
  appDir
8126
8519
  );
8127
8520
  for (const [kind, files, target] of [
8128
- ["page", pageFiles, this.routes],
8521
+ ["page", pageFiles.filter((file) => parseRouteSlotFile(file) === null), this.routes],
8129
8522
  ["layout", layoutFiles, this.layouts],
8130
8523
  ["loading", loadingFiles, this.loadings],
8131
8524
  ["error", errorFiles, this.errors]
@@ -8149,6 +8542,32 @@ var init_route_manager = __esm({
8149
8542
  });
8150
8543
  }
8151
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
+ }
8152
8571
  for (const file of metadataImageFiles) {
8153
8572
  const fileBase = import_path6.default.basename(file, import_path6.default.extname(file));
8154
8573
  const kind = fileBase === "twitter-image" ? "twitter" : "opengraph";
@@ -8274,6 +8693,71 @@ var init_route_manager = __esm({
8274
8693
  }
8275
8694
  return matchingLayouts;
8276
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
+ }
8277
8761
  findNearestBoundary(pathname, boundaries) {
8278
8762
  const normalizedPath = pathname === "/" ? "/" : pathname.replace(/\/$/, "");
8279
8763
  const pathSegments = normalizedPath.split("/").filter(Boolean);
@@ -9868,7 +10352,9 @@ var init_renderer = __esm({
9868
10352
  async runWithRequestContext(request, fn) {
9869
10353
  return _runWithCurrentRequest(
9870
10354
  request,
9871
- () => 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()
9872
10358
  );
9873
10359
  }
9874
10360
  async resolveRouteContext(input) {
@@ -9891,11 +10377,11 @@ var init_renderer = __esm({
9891
10377
  /**
9892
10378
  * Check if a path should be served from SSG cache
9893
10379
  */
9894
- shouldServeSSG(pathname) {
10380
+ async shouldServeSSG(pathname) {
9895
10381
  const ssgPage = matchSSGPage(pathname, this.ssgManifest);
9896
10382
  if (!ssgPage) return null;
9897
- const cached = this.getCachedSSGPage(pathname);
9898
- if (cached && this.dataCache.isStale(cached)) {
10383
+ const cached = await this.getCachedSSGPage(pathname);
10384
+ if (cached && await this.dataCache.isStaleAsync(cached)) {
9899
10385
  this.regenerateSSGPage(ssgPage);
9900
10386
  }
9901
10387
  return ssgPage;
@@ -9907,12 +10393,12 @@ var init_renderer = __esm({
9907
10393
  return createFarmCacheKey(["ppr", normalizeRevalidatePath(pathname), search]);
9908
10394
  }
9909
10395
  getCachedSSGPage(urlPath) {
9910
- return this.dataCache.getEntry(this.getSSGCacheKey(urlPath), {
10396
+ return this.dataCache.getEntryAsync(this.getSSGCacheKey(urlPath), {
9911
10397
  allowStale: true
9912
10398
  });
9913
10399
  }
9914
- cacheSSGPage(page2, html, options) {
9915
- this.dataCache.set(
10400
+ async cacheSSGPage(page2, html, options) {
10401
+ await this.dataCache.setAsync(
9916
10402
  this.getSSGCacheKey(page2.urlPath),
9917
10403
  { html, document: options.document },
9918
10404
  {
@@ -9924,11 +10410,11 @@ var init_renderer = __esm({
9924
10410
  );
9925
10411
  }
9926
10412
  getCachedPPRShell(pathname, search) {
9927
- return this.dataCache.getEntry(this.getPPRCacheKey(pathname, search));
10413
+ return this.dataCache.getEntryAsync(this.getPPRCacheKey(pathname, search));
9928
10414
  }
9929
- cachePPRShell(options, html) {
10415
+ async cachePPRShell(options, html) {
9930
10416
  const key = this.getPPRCacheKey(options.pathname, options.search);
9931
- this.dataCache.set(
10417
+ await this.dataCache.setAsync(
9932
10418
  key,
9933
10419
  { html },
9934
10420
  {
@@ -10026,7 +10512,7 @@ var init_renderer = __esm({
10026
10512
  }
10027
10513
  const { renderToString } = await import("react-dom/server");
10028
10514
  const html = renderToString(await this.wrapWithIntegrationProviders(pageElement));
10029
- this.cacheSSGPage(page2, html, { document: false });
10515
+ await this.cacheSSGPage(page2, html, { document: false });
10030
10516
  logger.info(`ISR: Regenerated ${page2.urlPath}`);
10031
10517
  } catch (error) {
10032
10518
  logger.error(`ISR regeneration failed for ${page2.urlPath}: ${error}`);
@@ -10040,7 +10526,7 @@ var init_renderer = __esm({
10040
10526
  * Serve a pre-rendered SSG page
10041
10527
  */
10042
10528
  async serveSSGPage(req, res, page2) {
10043
- const cached = this.getCachedSSGPage(page2.urlPath);
10529
+ const cached = await this.getCachedSSGPage(page2.urlPath);
10044
10530
  if (cached) {
10045
10531
  res.setHeader("Content-Type", "text/html; charset=utf-8");
10046
10532
  res.setHeader("X-Farm-SSG", "cached");
@@ -10058,9 +10544,12 @@ var init_renderer = __esm({
10058
10544
  if (fs2.existsSync(htmlPath)) {
10059
10545
  const stat = fs2.statSync(htmlPath);
10060
10546
  const html = fs2.readFileSync(htmlPath, "utf-8");
10061
- this.cacheSSGPage(page2, html, { document: true, createdAt: stat.mtimeMs });
10062
- const fileCacheEntry = this.getCachedSSGPage(page2.urlPath);
10063
- 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)) {
10064
10553
  this.regenerateSSGPage(page2);
10065
10554
  }
10066
10555
  res.setHeader("Content-Type", "text/html; charset=utf-8");
@@ -10106,6 +10595,7 @@ var init_renderer = __esm({
10106
10595
  let pathname = "/";
10107
10596
  let params = {};
10108
10597
  let layouts = [];
10598
+ let routeSlots = [];
10109
10599
  let searchParamsObject = {};
10110
10600
  let middlewareMap = /* @__PURE__ */ new Map();
10111
10601
  let middlewareContext = /* @__PURE__ */ new Map();
@@ -10138,7 +10628,7 @@ var init_renderer = __esm({
10138
10628
  }
10139
10629
  this.applyDeploymentHeaders(req, res);
10140
10630
  if (process.env.NODE_ENV === "production") {
10141
- const ssgPage = this.shouldServeSSG(pathname);
10631
+ const ssgPage = await this.shouldServeSSG(pathname);
10142
10632
  if (ssgPage) {
10143
10633
  const served = await this.serveSSGPage(req, res, ssgPage);
10144
10634
  if (served) {
@@ -10151,6 +10641,7 @@ var init_renderer = __esm({
10151
10641
  const route = match.route;
10152
10642
  params = match.params;
10153
10643
  layouts = match.layouts;
10644
+ routeSlots = match.slots ?? [];
10154
10645
  if (!route) {
10155
10646
  emitFarmEvent({ type: "route.notFound", pathname });
10156
10647
  await this.render404(req, res);
@@ -10167,7 +10658,9 @@ var init_renderer = __esm({
10167
10658
  errorBoundaryEntry = this.routeManager.getMatchingError(pathname);
10168
10659
  middlewareMap = toMiddlewareMap2(req.__FARM_MIDDLEWARE_DATA__);
10169
10660
  middlewareContext = toMiddlewareMap2(req.__FARM_MIDDLEWARE_CONTEXT__);
10170
- pluginExposedContext = getRequestContextSnapshot(req, { exposedOnly: true });
10661
+ pluginExposedContext = getRequestContextSnapshot(req, {
10662
+ exposedOnly: true
10663
+ });
10171
10664
  const currentRequest = createWebRequestFromFarmRequest(req);
10172
10665
  const routeContext = await this.resolveRouteContext({
10173
10666
  request: currentRequest,
@@ -10231,8 +10724,16 @@ var init_renderer = __esm({
10231
10724
  revalidate: renderingConfig.revalidate
10232
10725
  } : void 0;
10233
10726
  if (renderingConfig.ppr && pprBypassReason) {
10234
- emitFarmEvent({ type: "ppr.shell.bypass", route: pathname, reason: pprBypassReason });
10235
- 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
+ });
10236
10737
  if (pprBypassReason === "refresh") {
10237
10738
  pprRefreshRoute = pathname;
10238
10739
  emitFarmEvent({ type: "ppr.refresh.start", route: pathname });
@@ -10240,14 +10741,22 @@ var init_renderer = __esm({
10240
10741
  }
10241
10742
  if (pprShellOptions) {
10242
10743
  const pprCacheKey = this.getPPRCacheKey(pathname, url.search);
10243
- const cachedPPRShell = this.getCachedPPRShell(pathname, url.search);
10744
+ const cachedPPRShell = await this.getCachedPPRShell(pathname, url.search);
10244
10745
  if (cachedPPRShell) {
10245
- emitFarmEvent({ type: "ppr.shell.hit", route: pathname, key: pprCacheKey });
10746
+ emitFarmEvent({
10747
+ type: "ppr.shell.hit",
10748
+ route: pathname,
10749
+ key: pprCacheKey
10750
+ });
10246
10751
  this.serveCachedPPRShell(res, cachedPPRShell.value, renderingConfig.revalidate);
10247
10752
  completeRender(res.statusCode || 200, pathname);
10248
10753
  return;
10249
10754
  }
10250
- emitFarmEvent({ type: "ppr.shell.miss", route: pathname, key: pprCacheKey });
10755
+ emitFarmEvent({
10756
+ type: "ppr.shell.miss",
10757
+ route: pathname,
10758
+ key: pprCacheKey
10759
+ });
10251
10760
  }
10252
10761
  let LoadingFallbackComponent = null;
10253
10762
  if (loadingBoundaryEntry) {
@@ -10271,14 +10780,77 @@ var init_renderer = __esm({
10271
10780
  );
10272
10781
  const moduleMetadata = routeManifestEntry || getClientModuleMetadata(route.modulePath, this.config.root);
10273
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
+ );
10274
10823
  const shouldHydrate = moduleMetadata.shouldHydrate;
10824
+ const hasHydratableRouteSlots = renderedRouteSlots.some(
10825
+ (slot) => slot.isClientComponent || slot.shouldHydrate
10826
+ );
10275
10827
  req.__FARM_PAGE_PATH__ = route.modulePath;
10276
10828
  req.__FARM_ROUTE__ = pathname;
10277
10829
  req.__FARM_IS_CLIENT_COMPONENT__ = isClientComponent;
10278
10830
  req.__FARM_SHOULD_HYDRATE__ = shouldHydrate;
10831
+ req.__FARM_HAS_HYDRATABLE_ROUTE_SLOTS__ = hasHydratableRouteSlots;
10279
10832
  req.__FARM_LOADING_MODULE_PATH__ = loadingBoundaryEntry?.modulePath ? loadingBoundaryEntry.modulePath.substring(
10280
10833
  loadingBoundaryEntry.modulePath.indexOf("/src/app/")
10281
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
+ }));
10282
10854
  req.__FARM_PROPS__ = {
10283
10855
  params: pageProps.params,
10284
10856
  search: pageProps.search,
@@ -10338,12 +10910,32 @@ var init_renderer = __esm({
10338
10910
  let wrappedElement = pageElement;
10339
10911
  for (let i = layoutModules.length - 1; i >= 0; i--) {
10340
10912
  const layoutModule = layoutModules[i];
10913
+ const layoutEntry = layouts[i];
10341
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
+ }
10342
10933
  wrappedElement = import_react4.default.createElement(
10343
10934
  LayoutComponent,
10344
10935
  {
10345
10936
  children: wrappedElement,
10346
- params
10937
+ params,
10938
+ ...slotProps
10347
10939
  }
10348
10940
  );
10349
10941
  }
@@ -10380,7 +10972,10 @@ var init_renderer = __esm({
10380
10972
  routeManifest,
10381
10973
  captureStaticShell: Boolean(pprShellOptions),
10382
10974
  observabilityRoute: pathname,
10383
- 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,
10384
10979
  onComplete: pprShellOptions && req.method !== "HEAD" ? (html) => this.cachePPRShell(pprShellOptions, html) : void 0
10385
10980
  }
10386
10981
  );
@@ -10428,7 +11023,11 @@ var init_renderer = __esm({
10428
11023
  }
10429
11024
  emitFarmEvent({ type: "render.error", route: pathname, error });
10430
11025
  if (pprRefreshRoute) {
10431
- emitFarmEvent({ type: "ppr.refresh.error", route: pprRefreshRoute, error });
11026
+ emitFarmEvent({
11027
+ type: "ppr.refresh.error",
11028
+ route: pprRefreshRoute,
11029
+ error
11030
+ });
10432
11031
  }
10433
11032
  logger.error(`Error rendering page: ${error}`);
10434
11033
  if (res.headersSent || res.writableEnded) {
@@ -10481,7 +11080,9 @@ var init_renderer = __esm({
10481
11080
  if (typeof layoutModule.generateMetadata === "function") {
10482
11081
  metadata = mergeMetadata(
10483
11082
  metadata,
10484
- await layoutModule.generateMetadata({ params: options.pageProps.params })
11083
+ await layoutModule.generateMetadata({
11084
+ params: options.pageProps.params
11085
+ })
10485
11086
  );
10486
11087
  }
10487
11088
  }
@@ -10712,7 +11313,13 @@ var init_renderer = __esm({
10712
11313
  clientEntry: "/@farm/client.js",
10713
11314
  routes: {},
10714
11315
  layouts: {},
10715
- 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
+ ]
10716
11323
  };
10717
11324
  for (const routeEntry of manifest.routes) {
10718
11325
  clientManifest.routes[routeEntry.pattern] = {
@@ -10734,9 +11341,26 @@ var init_renderer = __esm({
10734
11341
  assets: []
10735
11342
  };
10736
11343
  }
10737
- 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
+ });
10738
11361
  const propsScript = `<script>
10739
- window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data)};
11362
+ window.__FARM_PROPS__ = ${serializeInlineValue(deferredProps.data.page)};
11363
+ window.__FARM_ROUTE_SLOTS__ = ${serializeInlineValue(deferredProps.data.slots)};
10740
11364
  window.__FARM_DEPLOYMENT_ID__ = ${serializeInlineValue(deploymentId)};
10741
11365
  window.__FARM_PATH__ = ${JSON.stringify(req.__FARM_ROUTE__ || req.url || "/")};
10742
11366
  window.__FARM_IS_CLIENT__ = ${JSON.stringify(isClientComponent)};
@@ -10749,7 +11373,7 @@ window.__FARM_MANIFEST__ = ${JSON.stringify(clientManifest)};
10749
11373
  window.__FARM_INTEGRATION_API_MANIFEST__ = ${JSON.stringify(getRegisteredIntegrationAPIManifest())};
10750
11374
  ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(getFarmI18nClientSnapshot())};` : ""}
10751
11375
  </script>`;
10752
- 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() : "";
10753
11377
  const {
10754
11378
  title,
10755
11379
  tags: metaTags,
@@ -10860,7 +11484,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
10860
11484
  didError = true;
10861
11485
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
10862
11486
  logger.error(`SSR shell error: ${error}`);
10863
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
11487
+ emitFarmEvent({
11488
+ type: "render.error",
11489
+ route: observabilityRoute,
11490
+ error
11491
+ });
10864
11492
  }
10865
11493
  if (clearMiddlewareData) {
10866
11494
  clearMiddlewareData();
@@ -10871,7 +11499,11 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue(g
10871
11499
  didError = true;
10872
11500
  if (!isFarmRedirectError(error) && !isFarmNotFoundError(error)) {
10873
11501
  logger.error(`SSR streaming error: ${error}`);
10874
- emitFarmEvent({ type: "render.error", route: observabilityRoute, error });
11502
+ emitFarmEvent({
11503
+ type: "render.error",
11504
+ route: observabilityRoute,
11505
+ error
11506
+ });
10875
11507
  }
10876
11508
  }
10877
11509
  });
@@ -11053,6 +11685,7 @@ var init_app = __esm({
11053
11685
  init_devtools_config();
11054
11686
  init_config();
11055
11687
  init_server2();
11688
+ init_cache();
11056
11689
  defaultDocsConfig = {
11057
11690
  enabled: false,
11058
11691
  entry: "/docs",
@@ -11074,6 +11707,7 @@ var init_app = __esm({
11074
11707
  logger.info("Initializing Farm.js application...");
11075
11708
  }
11076
11709
  await initStorage(this.config.storage);
11710
+ await configureFarmCache(this.config.cache);
11077
11711
  await this.i18nRuntime.initialize();
11078
11712
  await this.verifyAppStructure();
11079
11713
  await this.routeManager.discoverRoutes();
@@ -11105,6 +11739,7 @@ var init_app = __esm({
11105
11739
  preset: config.preset ?? "node-server",
11106
11740
  deploy: config.deploy || {},
11107
11741
  storage: config.storage || {},
11742
+ cache: config.cache || {},
11108
11743
  integrations: config.integrations || {},
11109
11744
  plugins: config.plugins || [],
11110
11745
  migrations: config.migrations || { commands: [] },
@@ -11178,6 +11813,13 @@ var init_app = __esm({
11178
11813
  }
11179
11814
  });
11180
11815
 
11816
+ // src/api/transport.ts
11817
+ var init_transport = __esm({
11818
+ "src/api/transport.ts"() {
11819
+ "use strict";
11820
+ }
11821
+ });
11822
+
11181
11823
  // src/api/endpoint.ts
11182
11824
  function isEndpointFailure(value) {
11183
11825
  return value instanceof EndpointFailure;
@@ -11188,6 +11830,7 @@ var init_endpoint = __esm({
11188
11830
  "use strict";
11189
11831
  import_better_call = require("better-call");
11190
11832
  init_cache();
11833
+ init_transport();
11191
11834
  _EndpointFailure = class _EndpointFailure extends Error {
11192
11835
  constructor(code, data, options) {
11193
11836
  super(options.message);
@@ -11247,7 +11890,7 @@ async function invokeAPIRouteEndpointInContext(endpoint, request, params) {
11247
11890
  });
11248
11891
  let body = void 0;
11249
11892
  if (request.method.toUpperCase() !== "GET" && request.method.toUpperCase() !== "HEAD") {
11250
- body = await readJSONBody(request);
11893
+ body = await readRequestBody2(request);
11251
11894
  }
11252
11895
  const headers = Object.fromEntries(request.headers.entries());
11253
11896
  const types = endpoint.__types || {};
@@ -11345,16 +11988,46 @@ function isFarmContextHandler(endpoint) {
11345
11988
  const firstParameter = (arrowMatch?.[1] || arrowMatch?.[2] || functionMatch?.[1] || "").split(",")[0].trim();
11346
11989
  return firstParameter === "ctx" || firstParameter === "context" || firstParameter.startsWith("{");
11347
11990
  }
11348
- async function readJSONBody(request) {
11991
+ async function readRequestBody2(request) {
11992
+ const contentType = request.headers.get("content-type")?.split(";", 1)[0]?.trim().toLowerCase();
11349
11993
  try {
11994
+ if (contentType === "multipart/form-data") {
11995
+ return formDataToObject(await request.clone().formData());
11996
+ }
11350
11997
  const text2 = await request.clone().text();
11351
- 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")) {
11352
12003
  return JSON.parse(text2);
11353
12004
  }
12005
+ return JSON.parse(text2);
11354
12006
  } catch {
11355
12007
  }
11356
12008
  return void 0;
11357
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
+ }
11358
12031
  function validateInput(schema, value, error) {
11359
12032
  if (!schema || typeof schema.parse !== "function") {
11360
12033
  return value;
@@ -11453,7 +12126,10 @@ var init_runtime2 = __esm({
11453
12126
  __name(attachEndpointInvalidations, "attachEndpointInvalidations");
11454
12127
  __name(createEndpointFailureResponse, "createEndpointFailureResponse");
11455
12128
  __name(isFarmContextHandler, "isFarmContextHandler");
11456
- __name(readJSONBody, "readJSONBody");
12129
+ __name(readRequestBody2, "readRequestBody");
12130
+ __name(formDataToObject, "formDataToObject");
12131
+ __name(searchParamsToObject2, "searchParamsToObject");
12132
+ __name(entriesToObject, "entriesToObject");
11457
12133
  __name(validateInput, "validateInput");
11458
12134
  __name(matchRoutePath, "matchRoutePath");
11459
12135
  __name(getPathSegments, "getPathSegments");
@@ -13810,6 +14486,7 @@ async function generateRouteTypes(options) {
13810
14486
  absolute: false
13811
14487
  });
13812
14488
  for (const file of pageFiles) {
14489
+ if (parseRouteSlotFile(file)) continue;
13813
14490
  const route = parseRoutePath(file);
13814
14491
  if (route.type === "page") {
13815
14492
  const pattern = createRoutePattern(route);
@@ -13875,6 +14552,7 @@ var init_generate_route_types = __esm({
13875
14552
  path17 = __toESM(require("path"));
13876
14553
  fs5 = __toESM(require("fs"));
13877
14554
  init_utils();
14555
+ init_route_slots();
13878
14556
  init_routes_server();
13879
14557
  init_write_file_if_changed();
13880
14558
  __name(routeSegmentsToTsTypeLiteral, "routeSegmentsToTsTypeLiteral");
@@ -14243,7 +14921,7 @@ var init_type_artifacts = __esm({
14243
14921
  var version;
14244
14922
  var init_package = __esm({
14245
14923
  "package.json"() {
14246
- version = "0.1.0-beta.3";
14924
+ version = "0.1.0-beta.5";
14247
14925
  }
14248
14926
  });
14249
14927
 
@@ -21213,7 +21891,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21213
21891
  }, "isProgrammaticRouteSourceFile");
21214
21892
  const isAppRuntimeFile = /* @__PURE__ */ __name((file) => {
21215
21893
  const normalized = file.replace(/\\/g, "/");
21216
- 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(
21217
21895
  normalized
21218
21896
  );
21219
21897
  }, "isAppRuntimeFile");
@@ -21904,7 +22582,10 @@ function farmPlugin(options = {}, initialPluginManager) {
21904
22582
  method: req.method || "GET"
21905
22583
  });
21906
22584
  }
21907
- 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
+ });
21908
22589
  if (pm) {
21909
22590
  await pm.runHookParallel("afterRouteMatch", {
21910
22591
  pathname: targetPath,
@@ -21920,7 +22601,7 @@ function farmPlugin(options = {}, initialPluginManager) {
21920
22601
  res.end(JSON.stringify({ error: "Route not found" }));
21921
22602
  return;
21922
22603
  }
21923
- const { route, params, layouts } = match;
22604
+ const { route, params, layouts, slots } = match;
21924
22605
  if (!route) {
21925
22606
  res.statusCode = 404;
21926
22607
  res.setHeader("Content-Type", "application/json");
@@ -21975,6 +22656,54 @@ function farmPlugin(options = {}, initialPluginManager) {
21975
22656
  search: searchParams,
21976
22657
  routePath: route.pattern
21977
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
+ );
21978
22707
  const projectRoot = server.config.root;
21979
22708
  const toUrlPath = /* @__PURE__ */ __name((absolutePath) => {
21980
22709
  if (absolutePath.startsWith(projectRoot)) {
@@ -21995,13 +22724,21 @@ function farmPlugin(options = {}, initialPluginManager) {
21995
22724
  },
21996
22725
  canonicalPath: routeProps.__farmCanonicalPath,
21997
22726
  modulePath: toUrlPath(route.modulePath),
21998
- isClientComponent,
21999
- shouldHydrate,
22727
+ isClientComponent: routeSlots.length > 0 ? false : isClientComponent,
22728
+ shouldHydrate: shouldHydrate || routeSlots.some((slot) => slot.isClientComponent || slot.shouldHydrate),
22000
22729
  metadata: {
22001
22730
  title: mergedMetadata.title,
22002
22731
  description: mergedMetadata.description
22003
22732
  },
22004
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,
22005
22742
  i18n: getFarmI18nClientSnapshot()
22006
22743
  };
22007
22744
  await sendWebResponse(
@@ -22337,6 +23074,7 @@ function farmPlugin(options = {}, initialPluginManager) {
22337
23074
  export const getManifest = () => ({
22338
23075
  routes: {},
22339
23076
  layouts: {},
23077
+ slots: [],
22340
23078
  clientEntry: "/@farm/client.js",
22341
23079
  sharedAssets: []
22342
23080
  });
@@ -22347,6 +23085,7 @@ export const getManifest = () => ({
22347
23085
  clientEntry: "/@farm/client.js",
22348
23086
  routes: {},
22349
23087
  layouts: {},
23088
+ slots: [],
22350
23089
  sharedAssets: [
22351
23090
  {
22352
23091
  tag: "link",
@@ -22376,6 +23115,13 @@ export const getManifest = () => ({
22376
23115
  assets: []
22377
23116
  };
22378
23117
  }
23118
+ for (const slot of manifest.slots) {
23119
+ fullManifest.slots.push({
23120
+ ...slot,
23121
+ preloads: [slot.modulePath],
23122
+ assets: []
23123
+ });
23124
+ }
22379
23125
  return `
22380
23126
  // Auto-generated manifest for SPA navigation (TanStack Start pattern)
22381
23127
  // This manifest is inlined in the server bundle - no file on disk
@@ -23012,7 +23758,7 @@ window.__FARM_WRAP_PROVIDERS__ = wrapWithIntegrationProviders;
23012
23758
 
23013
23759
  // Get manifest from window (inlined by server in HTML)
23014
23760
  // Fallback to empty manifest if not available yet
23015
- const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, clientEntry: '', sharedAssets: [] };
23761
+ const getManifest = () => window.__FARM_MANIFEST__ || { routes: {}, layouts: {}, slots: [], clientEntry: '', sharedAssets: [] };
23016
23762
 
23017
23763
  // ====== CLIENT-SIDE ROUTE MATCHING ======
23018
23764
  // Matches URL to route using manifest (no server request!)
@@ -23351,6 +24097,9 @@ let LayoutComponent = null;
23351
24097
 
23352
24098
  // Track if we've taken over rendering from SSR
23353
24099
  let hasClientTakenOver = false;
24100
+ const routeSlotRoots = new Map();
24101
+ const routeSlotDefinitions = new Map();
24102
+ let activeRouteInterception = null;
23354
24103
 
23355
24104
  function normalizeServerProps(rawProps) {
23356
24105
  const props = rawProps && typeof rawProps === 'object' ? { ...rawProps } : {};
@@ -23363,6 +24112,123 @@ function normalizeServerProps(rawProps) {
23363
24112
  return props;
23364
24113
  }
23365
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
+
23366
24232
  function applyCanonicalPathFromProps(props) {
23367
24233
  const canonicalPath = props && typeof props.__farmCanonicalPath === 'string'
23368
24234
  ? props.__farmCanonicalPath
@@ -23597,6 +24463,17 @@ async function tryHydrateImportedPage(
23597
24463
  async function renderPage(pageData) {
23598
24464
  const container = document.getElementById('root');
23599
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
+ }
23600
24477
 
23601
24478
  const route = {
23602
24479
  modulePath: pageData.modulePath,
@@ -23825,7 +24702,9 @@ async function hydrate() {
23825
24702
  window.__FARM_SHOULD_HYDRATE__ === true ||
23826
24703
  isClientComponent ||
23827
24704
  await moduleLooksClient(modulePath);
24705
+ const hydratedSlots = await hydrateInitialRouteSlots();
23828
24706
  if (!shouldHydrate) {
24707
+ if (hydratedSlots) replayPreHydrationClicks();
23829
24708
  console.log('[Farm.js] Server component - SPA router ready')
23830
24709
  return
23831
24710
  }
@@ -26338,6 +27217,18 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
26338
27217
  ...isFarmMarkdownPageFile(entry.modulePath) ? { source: (0, import_fs5.readFileSync)(entry.modulePath, "utf8") } : {}
26339
27218
  });
26340
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
+ );
26341
27232
  logger.info(`\u{1F4CB} Found ${pageRoutes.length} page routes`);
26342
27233
  const fs8 = await import("fs/promises");
26343
27234
  const layoutRoutes = [];
@@ -26381,7 +27272,16 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
26381
27272
  logger.info("\u{1F4E6} Building client and SSR bundles in parallel...");
26382
27273
  const [clientBuildResult, ssrBuildResult] = await Promise.allSettled([
26383
27274
  // Client build (to disk)
26384
- 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
+ ),
26385
27285
  // SSR build (in memory)
26386
27286
  buildSSRInMemory(
26387
27287
  productionVite,
@@ -26392,7 +27292,8 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
26392
27292
  serverRenderer,
26393
27293
  preset,
26394
27294
  pageRoutes,
26395
- layoutRoutes
27295
+ layoutRoutes,
27296
+ routeSlots
26396
27297
  )
26397
27298
  ]);
26398
27299
  const [routeRuntimeManifestResult] = await routeRuntimeManifestResultPromise;
@@ -26412,7 +27313,8 @@ async function buildUniversal(config, routeManager, apiRouteManager, serverRende
26412
27313
  srcDir,
26413
27314
  clientOutputDir,
26414
27315
  pageRoutes,
26415
- layoutRoutes
27316
+ layoutRoutes,
27317
+ routeSlots
26416
27318
  );
26417
27319
  }
26418
27320
  const ssrResult = ssrBuildResult.value;
@@ -26515,7 +27417,7 @@ async function validateClientBuildOutput(root, srcDir, outputDir) {
26515
27417
  }
26516
27418
  }
26517
27419
  __name(validateClientBuildOutput, "validateClientBuildOutput");
26518
- async function buildClient(productionVite, config, root, srcDir, outputDir, pageRoutes, layoutRoutes = []) {
27420
+ async function buildClient(productionVite, config, root, srcDir, outputDir, pageRoutes, layoutRoutes = [], routeSlots = []) {
26519
27421
  const viteBuild = productionVite.build;
26520
27422
  const { farmPlugin: farmPlugin2 } = await Promise.resolve().then(() => (init_vite(), vite_exports));
26521
27423
  const { PluginManager: PluginManager2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
@@ -26542,8 +27444,16 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
26542
27444
  logger.warn(`\u26A0\uFE0F Could not inspect route file ${route.modulePath}: ${error}`);
26543
27445
  }
26544
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
+ });
26545
27455
  logger.info(
26546
- `\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`
26547
27457
  );
26548
27458
  const clientEntryRoot = import_path12.default.join(root, ".farm", "tmp");
26549
27459
  await fs8.mkdir(clientEntryRoot, { recursive: true });
@@ -26552,6 +27462,7 @@ async function buildClient(productionVite, config, root, srcDir, outputDir, page
26552
27462
  const clientHydrationCode = generateClientHydrationEntry(
26553
27463
  clientPages,
26554
27464
  layoutRoutes,
27465
+ clientRouteSlots,
26555
27466
  root,
26556
27467
  srcDir,
26557
27468
  isFarmDocsSearchEnabled(config.docs),
@@ -26884,7 +27795,7 @@ function generateUniversalRouterStateProperties() {
26884
27795
  `.trim();
26885
27796
  }
26886
27797
  __name(generateUniversalRouterStateProperties, "generateUniversalRouterStateProperties");
26887
- function generateClientHydrationEntry(clientPages, layoutRoutes, root, srcDir, docsSearchEnabled, docsSearchModuleId, i18nConfig = {
27798
+ function generateClientHydrationEntry(clientPages, layoutRoutes, clientRouteSlots, root, srcDir, docsSearchEnabled, docsSearchModuleId, i18nConfig = {
26888
27799
  enabled: false,
26889
27800
  locales: ["en"],
26890
27801
  defaultLocale: "en",
@@ -26952,7 +27863,7 @@ function isFarmLocaleDocumentChange() {
26952
27863
  return false;
26953
27864
  }
26954
27865
  `;
26955
- if (clientPages.length === 0) {
27866
+ if (clientPages.length === 0 && clientRouteSlots.length === 0) {
26956
27867
  return `
26957
27868
  // Farm.js Client Runtime (no client components)
26958
27869
  ${cssImport}
@@ -27202,11 +28113,25 @@ document.addEventListener("click", function(e) {
27202
28113
  }
27203
28114
  const imports = [];
27204
28115
  const routeEntries = [];
28116
+ const routeSlotEntries = [];
27205
28117
  clientPages.forEach((page2, index) => {
27206
28118
  const importPath = toImportPath(page2.modulePath);
27207
28119
  imports.push(`import Page${index} from "${importPath}";`);
27208
28120
  routeEntries.push(` { pattern: ${JSON.stringify(page2.pattern)}, Component: Page${index} }`);
27209
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
+ });
27210
28135
  return `
27211
28136
  // Farm.js Client Runtime - SPA with Hydration
27212
28137
  ${cssImport}
@@ -27228,6 +28153,10 @@ const clientRoutes = [
27228
28153
  ${routeEntries.join(",\n")}
27229
28154
  ];
27230
28155
 
28156
+ const clientRouteSlots = [
28157
+ ${routeSlotEntries.join(",\n")}
28158
+ ];
28159
+
27231
28160
  // Layout routes for wrapping client components
27232
28161
  const layoutRoutes = [
27233
28162
  ${layoutRegistrations.join(",\n")}
@@ -27282,6 +28211,146 @@ function matchRoute(pathname) {
27282
28211
  return null;
27283
28212
  }
27284
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
+
27285
28354
  // State
27286
28355
  let reactRoot = null;
27287
28356
  let currentPathname = null;
@@ -27300,12 +28369,17 @@ function resetReactRoot() {
27300
28369
  async function hydrate() {
27301
28370
  const pathname = window.location.pathname;
27302
28371
  const matched = matchRoute(pathname);
27303
-
28372
+ hydrateInitialRouteSlots();
28373
+
27304
28374
  if (!matched) {
27305
28375
  return;
27306
28376
  }
27307
28377
 
27308
- 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");
27309
28383
  if (!container) {
27310
28384
  console.error("[Farm.js] No root element found");
27311
28385
  return;
@@ -27319,7 +28393,9 @@ async function hydrate() {
27319
28393
 
27320
28394
  // Create page element and wrap with layouts
27321
28395
  const pageElement = React.createElement(Component, props);
27322
- const wrappedElement = wrapWithLayouts(pageElement, pathname, params);
28396
+ const wrappedElement = hasRouteSlots
28397
+ ? pageElement
28398
+ : wrapWithLayouts(pageElement, pathname, params);
27323
28399
 
27324
28400
  const shouldHydrate = !isHydrated && Boolean(container.innerHTML.trim());
27325
28401
  const hydrationSession = await farmClientRuntime.beginHydration({
@@ -27384,6 +28460,57 @@ ${generateUniversalRouterStateProperties()}
27384
28460
  : undefined,
27385
28461
  });
27386
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
+
27387
28514
  if (matched) {
27388
28515
  const Component = matched.route.Component;
27389
28516
  const params = matched.params;
@@ -27460,21 +28587,28 @@ ${generateUniversalRouterStateProperties()}
27460
28587
  }
27461
28588
  },
27462
28589
 
27463
- fetchPage: async function(url) {
27464
- 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);
27465
28593
  if (cached) return cached;
27466
28594
 
27467
28595
  const response = await fetch(url, {
27468
- headers: { "Accept": "text/html" }
28596
+ headers: {
28597
+ "Accept": "text/html",
28598
+ ...(interceptFrom ? { "X-Farm-Intercept-From": interceptFrom } : {}),
28599
+ }
27469
28600
  });
27470
28601
  if (!response.ok) throw new Error("Failed to fetch page");
27471
- return response.text();
28602
+ const html = await response.text();
28603
+ this.prefetchCache.set(cacheKey, html);
28604
+ return html;
27472
28605
  },
27473
28606
 
27474
28607
  swapContent: function(html, targetPath) {
27475
28608
  const parser = new DOMParser();
27476
28609
  const doc = parser.parseFromString(html, "text/html");
27477
28610
  if (isFarmLocaleDocumentChange(doc)) return false;
28611
+ const nextRouteSlots = readRouteSlotPayload(doc);
27478
28612
 
27479
28613
  // Update title
27480
28614
  const newTitle = doc.querySelector("title");
@@ -27499,7 +28633,10 @@ ${generateUniversalRouterStateProperties()}
27499
28633
  const currentRoot = document.getElementById("root");
27500
28634
  if (!newRoot || !currentRoot) return this.swapDocument(doc);
27501
28635
  resetReactRoot();
28636
+ resetRouteSlotRoots();
27502
28637
  currentRoot.innerHTML = newRoot.innerHTML;
28638
+ window.__FARM_ROUTE_SLOTS__ = nextRouteSlots;
28639
+ hydrateInitialRouteSlots();
27503
28640
 
27504
28641
  // Check if new page has a client component
27505
28642
  const targetUrl = new URL(targetPath || window.location.href, window.location.origin);
@@ -27511,13 +28648,15 @@ ${generateUniversalRouterStateProperties()}
27511
28648
  const params = matched.params;
27512
28649
  const searchParams = Object.fromEntries(targetUrl.searchParams);
27513
28650
  const props = { params: params, searchParams: Promise.resolve(searchParams) };
27514
-
27515
- if (!reactRoot) {
27516
- reactRoot = createRoot(currentRoot);
27517
- }
27518
28651
  const pageElement = React.createElement(Component, props);
27519
- const wrappedElement = wrapWithLayouts(pageElement, newPathname, params);
27520
- 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;
27521
28660
  }
27522
28661
  return true;
27523
28662
  },
@@ -27526,6 +28665,7 @@ ${generateUniversalRouterStateProperties()}
27526
28665
  if (!doc.documentElement || !doc.body) return false;
27527
28666
 
27528
28667
  resetReactRoot();
28668
+ resetRouteSlotRoots();
27529
28669
 
27530
28670
  Array.from(document.documentElement.attributes).forEach(function(attr) {
27531
28671
  if (!doc.documentElement.hasAttribute(attr.name)) {
@@ -27559,10 +28699,12 @@ ${generateUniversalRouterStateProperties()}
27559
28699
  if (url.origin !== window.location.origin) return;
27560
28700
 
27561
28701
  const pathname = url.pathname + url.search;
27562
- if (this.prefetchCache.has(pathname)) return;
28702
+ const interceptFrom = this.currentPath;
28703
+ const cacheKey = pathname + "\\nintercept:" + interceptFrom;
28704
+ if (this.prefetchCache.has(cacheKey)) return;
27563
28705
 
27564
- this.fetchPage(pathname)
27565
- .then(function(html) { spaRouter.prefetchCache.set(pathname, html); })
28706
+ this.fetchPage(pathname, interceptFrom)
28707
+ .then(function(html) { spaRouter.prefetchCache.set(cacheKey, html); })
27566
28708
  .catch(function() {});
27567
28709
  },
27568
28710
 
@@ -27653,7 +28795,7 @@ if (isFarmDocsSearchPage()) {
27653
28795
  `.trim();
27654
28796
  }
27655
28797
  __name(generateClientHydrationEntry, "generateClientHydrationEntry");
27656
- 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) {
27657
28799
  const viteBuild = productionVite.build;
27658
28800
  const { farmPlugin: farmPlugin2 } = await Promise.resolve().then(() => (init_vite(), vite_exports));
27659
28801
  const { PluginManager: PluginManager2 } = await Promise.resolve().then(() => (init_plugin(), plugin_exports));
@@ -27686,6 +28828,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
27686
28828
  let ssrEntryFile;
27687
28829
  const pageRoutes = collectedPageRoutes.map((route) => ({ ...route }));
27688
28830
  const layoutRoutes = collectedLayoutRoutes.map((route) => ({ ...route }));
28831
+ const routeSlots = collectedRouteSlots.map((slot) => ({ ...slot }));
27689
28832
  const metadataImageRoutes = [];
27690
28833
  for (const entry of routeManager.getMetadataImages().values()) {
27691
28834
  if (entry.sourceType === "static") {
@@ -27786,6 +28929,7 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
27786
28929
  apiRoutes,
27787
28930
  pageRoutes,
27788
28931
  layoutRoutes,
28932
+ routeSlots,
27789
28933
  errorRoutes,
27790
28934
  metadataImageRoutes,
27791
28935
  middlewareRoutes,
@@ -27956,10 +29100,14 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
27956
29100
  }
27957
29101
  }
27958
29102
  }
27959
- return { bundle: ssrBundle, entryFile: ssrEntryFile, configuredHeaderRoutes };
29103
+ return {
29104
+ bundle: ssrBundle,
29105
+ entryFile: ssrEntryFile,
29106
+ configuredHeaderRoutes
29107
+ };
27960
29108
  }
27961
29109
  __name(buildSSRInMemory, "buildSSRInMemory");
27962
- 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) {
27963
29111
  const hasPluginRuntime = hasRuntimeIntegrationConfig || hasConfiguredRuntimePlugins;
27964
29112
  const hasMiddlewareRuntime = middlewareRoutes.length > 0 || hasFarmMiddlewareConfig(config.middleware);
27965
29113
  const farmDocsBaseConfig = config.docs?.enabled ? { ...config.docs, config: void 0 } : void 0;
@@ -28003,10 +29151,12 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRout
28003
29151
  return;
28004
29152
  }
28005
29153
  pageImports.push(`import * as ${varName} from "${route.modulePath}";`);
29154
+ const shouldHydrate = getClientModuleMetadata(route.modulePath, config.root).shouldHydrate;
28006
29155
  pageRegistrations.push(`
28007
29156
  {
28008
29157
  pattern: ${JSON.stringify(route.pattern)},
28009
29158
  module: ${varName},
29159
+ shouldHydrate: ${JSON.stringify(shouldHydrate)},
28010
29160
  }`);
28011
29161
  });
28012
29162
  const layoutImports = [];
@@ -28020,6 +29170,22 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRout
28020
29170
  module: ${varName},
28021
29171
  }`);
28022
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
+ });
28023
29189
  const errorImports = [];
28024
29190
  const errorRegistrations = [];
28025
29191
  errorRoutes.forEach((errorRoute, index) => {
@@ -28070,6 +29236,7 @@ function generateVirtualEntryCode(apiRoutes, pageRoutes, layoutRoutes, errorRout
28070
29236
  _runWithMiddlewareData,
28071
29237
  addMetadataImageReference,
28072
29238
  applyProductionMiddlewareHeaders,
29239
+ configureFarmCache,
28073
29240
  configureFarmObservability,
28074
29241
  createFarmCacheKey,
28075
29242
  createFarmLocaleCookie,
@@ -28172,6 +29339,7 @@ async function handleAPIRequest(request) {
28172
29339
  ${apiImports.join("\n")}
28173
29340
  ${pageImports.join("\n")}
28174
29341
  ${layoutImports.join("\n")}
29342
+ ${routeSlotImports.join("\n")}
28175
29343
  ${errorImports.join("\n")}
28176
29344
  ${metadataImageImports.join("\n")}
28177
29345
  ${middlewareImports.join("\n")}
@@ -28249,6 +29417,7 @@ const farmObservabilityConfig = farmUserConfig?.observability ?? ${JSON.stringif
28249
29417
  config.observability ?? false
28250
29418
  )};
28251
29419
  configureFarmObservability(farmObservabilityConfig);
29420
+ configureFarmCache(farmResolvedRuntimeConfig.cache);
28252
29421
  const farmI18nConfig = ${JSON.stringify(config.i18n)};
28253
29422
  const farmI18nRuntime = ${config.i18n.enabled ? `createFarmI18nRuntime(farmI18nConfig, ${JSON.stringify(i18nCatalogs)})` : "null"};
28254
29423
  ${config.i18n.enabled ? "_setDefaultFarmI18nRuntime(farmI18nRuntime);" : ""}
@@ -28306,6 +29475,9 @@ for (const route of pageRoutes) {
28306
29475
  const layoutRoutes = [${layoutRegistrations.join(",")}
28307
29476
  ];
28308
29477
 
29478
+ const routeSlots = [${routeSlotRegistrations.join(",")}
29479
+ ];
29480
+
28309
29481
  // Route-level error boundaries bundled at build time.
28310
29482
  const errorRoutes = [${errorRegistrations.join(",")}
28311
29483
  ];
@@ -28340,18 +29512,21 @@ function serializeFarmInlineValue(value) {
28340
29512
  .replace(/\\u2029/g, "\\\\u2029");
28341
29513
  }
28342
29514
 
28343
- function renderFarmClientBootstrapScript(canonicalPath) {
29515
+ function renderFarmClientBootstrapScript(canonicalPath, selectedRouteSlots) {
28344
29516
  const integrationManifest = ${hasRuntimeIntegrationConfig ? "getRegisteredIntegrationAPIManifest()" : "{}"};
29517
+ const serializedRouteSlots = serializeFarmInlineValue(selectedRouteSlots || []);
28345
29518
  let source = 'window.__FARM_INTEGRATION_API_MANIFEST__=' +
28346
29519
  serializeFarmInlineValue(integrationManifest) +
28347
- ';';
29520
+ ';window.__FARM_ROUTE_SLOTS__=' + serializedRouteSlots + ';';
28348
29521
  if (typeof canonicalPath === "string" && canonicalPath.startsWith("/")) {
28349
29522
  const serializedPath = serializeFarmInlineValue(canonicalPath);
28350
29523
  source += 'if(location.pathname+location.search!==' + serializedPath + '){' +
28351
29524
  'history.replaceState(Object.assign({},history.state||{},{path:' + serializedPath + '}),"",' +
28352
29525
  serializedPath + ');}';
28353
29526
  }
28354
- return '<script>' + source + '</script>';
29527
+ return '<script id="__farm_route_slots_data__" type="application/json">' +
29528
+ serializedRouteSlots +
29529
+ '</script><script>' + source + '</script>';
28355
29530
  }
28356
29531
 
28357
29532
  function escapeFarmHtmlAttribute(value) {
@@ -29101,6 +30276,79 @@ function matchPageRoute(pathname) {
29101
30276
  return null;
29102
30277
  }
29103
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
+
29104
30352
  function hasLocalRequestRoute(request, routePathname) {
29105
30353
  const pathname = new URL(request.url).pathname;
29106
30354
  if (matchLocalAPIRequest(request) || matchLocalIntegrationRequest(request)) {
@@ -29251,6 +30499,7 @@ function getPPRShellBypassReason(request, middlewareData, middlewareContext) {
29251
30499
  if (method !== "GET" && method !== "HEAD") return "method";
29252
30500
  if (request.headers.get("cookie")) return "cookie";
29253
30501
  if (request.headers.get("authorization")) return "authorization";
30502
+ if (request.headers.get("x-farm-intercept-from")) return "route-interception";
29254
30503
  if (request.headers.get("x-farm-ppr-refresh")) return "refresh";
29255
30504
  if (hasConfiguredRouteContext) return "route-context";
29256
30505
  if (middlewareData?.size) return "middleware-data";
@@ -29304,8 +30553,8 @@ function getRouteSharedCacheControl(routeModule, pprCanCache) {
29304
30553
  return "public, s-maxage=" + revalidate + ", stale-while-revalidate=300";
29305
30554
  }
29306
30555
 
29307
- function getCachedPPRShell(cacheKey) {
29308
- const entry = pprShellCache.getEntry(cacheKey);
30556
+ async function getCachedPPRShell(cacheKey) {
30557
+ const entry = await pprShellCache.getEntryAsync(cacheKey);
29309
30558
  if (!entry) {
29310
30559
  return null;
29311
30560
  }
@@ -29520,7 +30769,7 @@ async function handleFarmRequestInContext(
29520
30769
  }
29521
30770
  }
29522
30771
  if (pprCacheKey) {
29523
- const cachedPPRShell = getCachedPPRShell(pprCacheKey);
30772
+ const cachedPPRShell = await getCachedPPRShell(pprCacheKey);
29524
30773
  if (cachedPPRShell) {
29525
30774
  emitFarmEvent({ type: "ppr.shell.hit", route: pathname, key: pprCacheKey });
29526
30775
  emitFarmEvent({
@@ -29548,6 +30797,10 @@ async function handleFarmRequestInContext(
29548
30797
 
29549
30798
  // Get applicable layouts for this page
29550
30799
  const applicableLayouts = getApplicableLayouts(routePathname);
30800
+ const selectedRouteSlots = matchRouteSlots(
30801
+ routePathname,
30802
+ request.headers.get("x-farm-intercept-from"),
30803
+ );
29551
30804
 
29552
30805
  if (PageComponent) {
29553
30806
  // Parse search params - make it a resolved Promise for async components
@@ -29635,6 +30888,82 @@ async function handleFarmRequestInContext(
29635
30888
  throw routeStateError;
29636
30889
  }
29637
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
+ });
29638
30967
 
29639
30968
  const renderPageElement = async () => {
29640
30969
  // First, render the page content
@@ -29662,6 +30991,14 @@ async function handleFarmRequestInContext(
29662
30991
  pageElement = React.createElement(PageComponent, pageProps);
29663
30992
  }
29664
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
+
29665
31002
  // Wrap with layouts (from innermost to outermost)
29666
31003
  // Layouts are sorted by depth (root first), so we process in reverse
29667
31004
  let wrappedElement = pageElement;
@@ -29669,9 +31006,23 @@ async function handleFarmRequestInContext(
29669
31006
  const layout = applicableLayouts[i];
29670
31007
  const LayoutComponent = layout.module.default;
29671
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
+ }
29672
31022
  wrappedElement = React.createElement(LayoutComponent, {
29673
31023
  children: wrappedElement,
29674
31024
  params,
31025
+ ...slotProps,
29675
31026
  });
29676
31027
  }
29677
31028
  }
@@ -29723,6 +31074,7 @@ async function handleFarmRequestInContext(
29723
31074
  );
29724
31075
  const hasRequestScopedRender = Boolean(
29725
31076
  pageStatus >= 400 ||
31077
+ request.headers.get("x-farm-intercept-from") ||
29726
31078
  hasPrivateRequestHeaders ||
29727
31079
  hasConfiguredRouteContext ||
29728
31080
  middlewareData?.size ||
@@ -29762,7 +31114,10 @@ async function handleFarmRequestInContext(
29762
31114
  ' <link rel="modulepreload" href="/farm-client.js">\\n' +
29763
31115
  '</head>\\n<body>\\n <div id="root">';
29764
31116
  const streamSuffix = '</div>\\n' +
29765
- ' ' + renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath) + '\\n' +
31117
+ ' ' + renderFarmClientBootstrapScript(
31118
+ pageProps.__farmCanonicalPath,
31119
+ routeSlotPayload
31120
+ ) + '\\n' +
29766
31121
  ' <script type="module" src="/farm-client.js"></script>\\n' +
29767
31122
  '</body>\\n</html>';
29768
31123
  const streamedDocument = createFarmDocumentStream(
@@ -29819,7 +31174,10 @@ async function handleFarmRequestInContext(
29819
31174
  // Inject client script before closing body tag
29820
31175
  .replace(
29821
31176
  /<\\/body>/i,
29822
- ' ' + renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath) + '\\n' +
31177
+ ' ' + renderFarmClientBootstrapScript(
31178
+ pageProps.__farmCanonicalPath,
31179
+ routeSlotPayload
31180
+ ) + '\\n' +
29823
31181
  ' <script type="module" src="/farm-client.js"></script>\\n</body>',
29824
31182
  );
29825
31183
 
@@ -29840,7 +31198,7 @@ async function handleFarmRequestInContext(
29840
31198
  </head>
29841
31199
  <body>
29842
31200
  <div id="root">\${html}</div>
29843
- \${renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath)}
31201
+ \${renderFarmClientBootstrapScript(pageProps.__farmCanonicalPath, routeSlotPayload)}
29844
31202
  <script type="module" src="/farm-client.js"></script>
29845
31203
  </body>
29846
31204
  </html>\`;
@@ -29848,7 +31206,7 @@ async function handleFarmRequestInContext(
29848
31206
  fullHtml = applyFarmI18nDocument(fullHtml, pathname, farmI18nSnapshot);
29849
31207
 
29850
31208
  if (pageStatus === 200 && pprCacheKey && request.method.toUpperCase() !== "HEAD") {
29851
- pprShellCache.set(
31209
+ await pprShellCache.setAsync(
29852
31210
  pprCacheKey,
29853
31211
  { html: fullHtml },
29854
31212
  {
@@ -31041,6 +32399,13 @@ function mergeFarmViteConfig(farmConfig, userConfig = {}) {
31041
32399
  ...farmConfig.server,
31042
32400
  ...userConfig.server
31043
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
+ },
31044
32409
  optimizeDeps: {
31045
32410
  ...farmConfig.optimizeDeps,
31046
32411
  ...userConfig.optimizeDeps,