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

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (254) hide show
  1. package/README.md +1 -1
  2. package/dist/agent-runtime.d.mts +2 -1
  3. package/dist/agent-runtime.d.ts +2 -1
  4. package/dist/api/route-manager.cjs +410 -15
  5. package/dist/api/route-manager.cjs.map +1 -1
  6. package/dist/api/route-manager.d.mts +4 -2
  7. package/dist/api/route-manager.d.ts +4 -2
  8. package/dist/api/route-manager.mjs +9 -8
  9. package/dist/api/runtime.cjs +748 -9
  10. package/dist/api/runtime.cjs.map +1 -1
  11. package/dist/api/runtime.d.mts +3 -16
  12. package/dist/api/runtime.d.ts +3 -16
  13. package/dist/api/runtime.mjs +8 -1
  14. package/dist/api.cjs +1243 -342
  15. package/dist/api.cjs.map +1 -1
  16. package/dist/api.d.mts +5 -147
  17. package/dist/api.d.ts +5 -147
  18. package/dist/api.mjs +51 -21
  19. package/dist/{app-CFRNJXP1.d.ts → app-BjN-da13.d.ts} +38 -3
  20. package/dist/{app-BiX4qb-d.d.mts → app-CakAr8Be.d.mts} +38 -3
  21. package/dist/app-markdown.cjs +1 -1
  22. package/dist/app-markdown.cjs.map +1 -1
  23. package/dist/app-markdown.d.mts +2 -1
  24. package/dist/app-markdown.d.ts +2 -1
  25. package/dist/app-markdown.mjs +1 -1
  26. package/dist/cache.cjs +414 -12
  27. package/dist/cache.cjs.map +1 -1
  28. package/dist/cache.d.mts +120 -6
  29. package/dist/cache.d.ts +120 -6
  30. package/dist/cache.mjs +11 -3
  31. package/dist/{chunk-Y7A27276.mjs → chunk-2CUKEMVR.mjs} +349 -15
  32. package/dist/chunk-2CUKEMVR.mjs.map +1 -0
  33. package/dist/{chunk-QF4PHCMA.mjs → chunk-2ED5Q2GH.mjs} +3 -3
  34. package/dist/chunk-2ED5Q2GH.mjs.map +1 -0
  35. package/dist/{chunk-GZP5W4K3.mjs → chunk-32W4QEJP.mjs} +1 -1
  36. package/dist/chunk-32W4QEJP.mjs.map +1 -0
  37. package/dist/{chunk-TRORP7E4.mjs → chunk-3U3ZUQAQ.mjs} +2 -2
  38. package/dist/{chunk-25D7G34K.mjs → chunk-65A7QIKI.mjs} +5 -5
  39. package/dist/{chunk-25D7G34K.mjs.map → chunk-65A7QIKI.mjs.map} +1 -1
  40. package/dist/{chunk-G3B5BRDY.mjs → chunk-AJWRO5WE.mjs} +539 -61
  41. package/dist/chunk-AJWRO5WE.mjs.map +1 -0
  42. package/dist/chunk-ALCLXXTY.mjs +187 -0
  43. package/dist/chunk-ALCLXXTY.mjs.map +1 -0
  44. package/dist/{chunk-CHFHNEMO.mjs → chunk-AOBXKNS5.mjs} +627 -84
  45. package/dist/chunk-AOBXKNS5.mjs.map +1 -0
  46. package/dist/{chunk-B6N4ZB2H.mjs → chunk-C2ROET2V.mjs} +3 -3
  47. package/dist/{chunk-JN3UU635.mjs → chunk-EJ7T4TPB.mjs} +2 -2
  48. package/dist/chunk-EJ7T4TPB.mjs.map +1 -0
  49. package/dist/{chunk-ZJQNATYV.mjs → chunk-FRPWCMMG.mjs} +3 -3
  50. package/dist/chunk-FRPWCMMG.mjs.map +1 -0
  51. package/dist/{chunk-QM6VV26B.mjs → chunk-GQVGIUSN.mjs} +3 -3
  52. package/dist/{chunk-QM6VV26B.mjs.map → chunk-GQVGIUSN.mjs.map} +1 -1
  53. package/dist/{chunk-UREJA4GZ.mjs → chunk-GXASWWJU.mjs} +1 -1
  54. package/dist/{chunk-UREJA4GZ.mjs.map → chunk-GXASWWJU.mjs.map} +1 -1
  55. package/dist/{chunk-3QJO55ZF.mjs → chunk-HNIQFZKB.mjs} +5 -3
  56. package/dist/{chunk-3QJO55ZF.mjs.map → chunk-HNIQFZKB.mjs.map} +1 -1
  57. package/dist/{chunk-PRNYWD7K.mjs → chunk-JDEOUK5V.mjs} +67 -239
  58. package/dist/chunk-JDEOUK5V.mjs.map +1 -0
  59. package/dist/{chunk-O3HADRW6.mjs → chunk-MIUW47JX.mjs} +2 -2
  60. package/dist/{chunk-O3HADRW6.mjs.map → chunk-MIUW47JX.mjs.map} +1 -1
  61. package/dist/{chunk-UHE2TTLK.mjs → chunk-NM3CRAAL.mjs} +9 -8
  62. package/dist/chunk-NM3CRAAL.mjs.map +1 -0
  63. package/dist/chunk-PFE6GMNE.mjs +603 -0
  64. package/dist/chunk-PFE6GMNE.mjs.map +1 -0
  65. package/dist/{chunk-S2XHZS7W.mjs → chunk-QGQX7SFC.mjs} +2 -2
  66. package/dist/chunk-QGQX7SFC.mjs.map +1 -0
  67. package/dist/{chunk-RKI6TN3A.mjs → chunk-QM746KUO.mjs} +9 -2
  68. package/dist/chunk-QM746KUO.mjs.map +1 -0
  69. package/dist/chunk-RBRLLZL5.mjs +81 -0
  70. package/dist/chunk-RBRLLZL5.mjs.map +1 -0
  71. package/dist/{chunk-HXP3ZSPC.mjs → chunk-SADGIRS2.mjs} +3 -3
  72. package/dist/{chunk-6XDUHOEM.mjs → chunk-TQAEEJB4.mjs} +3 -3
  73. package/dist/{chunk-AGS7UJ52.mjs → chunk-UB5UOSHB.mjs} +24 -12
  74. package/dist/chunk-UB5UOSHB.mjs.map +1 -0
  75. package/dist/{chunk-SXZTEK3V.mjs → chunk-UXUAWNO5.mjs} +2 -2
  76. package/dist/chunk-V3XURYJO.mjs +756 -0
  77. package/dist/chunk-V3XURYJO.mjs.map +1 -0
  78. package/dist/{chunk-TU4YJICB.mjs → chunk-VVKWFZAH.mjs} +41 -16
  79. package/dist/chunk-VVKWFZAH.mjs.map +1 -0
  80. package/dist/{chunk-JLWMCADZ.mjs → chunk-VY2UYN5R.mjs} +24 -4
  81. package/dist/chunk-VY2UYN5R.mjs.map +1 -0
  82. package/dist/{chunk-QFQRHRZS.mjs → chunk-W2CRQFF6.mjs} +151 -23
  83. package/dist/chunk-W2CRQFF6.mjs.map +1 -0
  84. package/dist/chunk-W4UIVMVI.mjs +10 -0
  85. package/dist/chunk-W4UIVMVI.mjs.map +1 -0
  86. package/dist/{chunk-Y2OJJD7R.mjs → chunk-XIVV5IWN.mjs} +7 -9
  87. package/dist/chunk-XIVV5IWN.mjs.map +1 -0
  88. package/dist/{chunk-OYXO65LI.mjs → chunk-YW7OBE6Q.mjs} +4 -4
  89. package/dist/chunk-YW7OBE6Q.mjs.map +1 -0
  90. package/dist/{chunk-YHU7SS6L.mjs → chunk-Z5CQMP54.mjs} +11 -11
  91. package/dist/{chunk-SOU3Q3ZA.mjs → chunk-ZZ57EH5Z.mjs} +1 -1
  92. package/dist/chunk-ZZ57EH5Z.mjs.map +1 -0
  93. package/dist/{client-D_yUjVhe.d.mts → client-BT1mFQas.d.mts} +55 -10
  94. package/dist/{client-Dkc03WP8.d.ts → client-Nn0q4eO4.d.ts} +55 -10
  95. package/dist/{client-plugins-BZnpeTGA.d.ts → client-plugins-Cw4qUGsw.d.ts} +7 -0
  96. package/dist/{client-plugins-g4bzuCmo.d.mts → client-plugins-DQQ1adXM.d.mts} +7 -0
  97. package/dist/client-plugins.d.mts +2 -1
  98. package/dist/client-plugins.d.ts +2 -1
  99. package/dist/client.cjs +922 -91
  100. package/dist/client.cjs.map +1 -1
  101. package/dist/client.d.mts +121 -3
  102. package/dist/client.d.ts +121 -3
  103. package/dist/client.mjs +23 -16
  104. package/dist/config.d.mts +2 -1
  105. package/dist/config.d.ts +2 -1
  106. package/dist/cron.cjs.map +1 -1
  107. package/dist/cron.mjs +1 -1
  108. package/dist/{devtools-AE4RVXCM.mjs → devtools-LVC3VB5P.mjs} +10 -10
  109. package/dist/{devtools-client-6WPY2AQU.mjs → devtools-client-3XRTBND5.mjs} +2 -11
  110. package/dist/devtools-client-3XRTBND5.mjs.map +1 -0
  111. package/dist/docs.cjs +14 -2
  112. package/dist/docs.cjs.map +1 -1
  113. package/dist/docs.mjs +10 -10
  114. package/dist/i18n/server.cjs +2 -2
  115. package/dist/i18n/server.cjs.map +1 -1
  116. package/dist/i18n/server.mjs +1 -1
  117. package/dist/index.cjs +2852 -463
  118. package/dist/index.cjs.map +1 -1
  119. package/dist/index.d.mts +13 -12
  120. package/dist/index.d.ts +13 -12
  121. package/dist/index.mjs +152 -109
  122. package/dist/index.mjs.map +1 -1
  123. package/dist/integrations.d.mts +2 -1
  124. package/dist/integrations.d.ts +2 -1
  125. package/dist/internal/build-runtime.cjs +1616 -149
  126. package/dist/internal/build-runtime.cjs.map +1 -1
  127. package/dist/internal/build-runtime.d.mts +4 -3
  128. package/dist/internal/build-runtime.d.ts +4 -3
  129. package/dist/internal/build-runtime.mjs +27 -25
  130. package/dist/internal/client-runtime.cjs.map +1 -1
  131. package/dist/internal/client-runtime.mjs +1 -1
  132. package/dist/internal/config-runtime.cjs +13 -2
  133. package/dist/internal/config-runtime.cjs.map +1 -1
  134. package/dist/internal/config-runtime.d.mts +3 -2
  135. package/dist/internal/config-runtime.d.ts +3 -2
  136. package/dist/internal/config-runtime.mjs +9 -9
  137. package/dist/internal/production-runtime.cjs +285 -9
  138. package/dist/internal/production-runtime.cjs.map +1 -1
  139. package/dist/internal/production-runtime.d.mts +4 -4
  140. package/dist/internal/production-runtime.d.ts +4 -4
  141. package/dist/internal/production-runtime.mjs +10 -8
  142. package/dist/{metadata-DAXRaOHx.d.ts → metadata-BAPg_Nmo.d.ts} +1 -1
  143. package/dist/{metadata-C9NZxMDW.d.mts → metadata-_6i9oFG3.d.mts} +1 -1
  144. package/dist/middleware.cjs +2 -2
  145. package/dist/middleware.cjs.map +1 -1
  146. package/dist/middleware.d.mts +4 -3
  147. package/dist/middleware.d.ts +4 -3
  148. package/dist/middleware.mjs +3 -3
  149. package/dist/navigation.cjs +40 -15
  150. package/dist/navigation.cjs.map +1 -1
  151. package/dist/navigation.mjs +3 -3
  152. package/dist/{nitro-KME62MWG.mjs → nitro-QO5LV6QK.mjs} +11 -13
  153. package/dist/{nitro-KME62MWG.mjs.map → nitro-QO5LV6QK.mjs.map} +1 -1
  154. package/dist/plugin.cjs +1032 -92
  155. package/dist/plugin.cjs.map +1 -1
  156. package/dist/plugin.d.mts +2 -1
  157. package/dist/plugin.d.ts +2 -1
  158. package/dist/plugin.mjs +23 -21
  159. package/dist/{production-runtime-DRkCHUvJ.d.ts → production-runtime-BoJDc5mc.d.ts} +1 -1
  160. package/dist/{production-runtime-flrjp_nC.d.mts → production-runtime-DPfgMV9Y.d.mts} +1 -1
  161. package/dist/routes.cjs +276 -6
  162. package/dist/routes.cjs.map +1 -1
  163. package/dist/routes.d.mts +1 -1
  164. package/dist/routes.d.ts +1 -1
  165. package/dist/routes.mjs +4 -4
  166. package/dist/runtime-9nQjVM7X.d.mts +206 -0
  167. package/dist/runtime-DgeS2skB.d.ts +206 -0
  168. package/dist/server-action-security.cjs +28 -10
  169. package/dist/server-action-security.cjs.map +1 -1
  170. package/dist/server-action-security.d.mts +2 -1
  171. package/dist/server-action-security.d.ts +2 -1
  172. package/dist/server-action-security.mjs +2 -2
  173. package/dist/server-fn-client.cjs.map +1 -1
  174. package/dist/server-fn-client.d.mts +1 -0
  175. package/dist/server-fn-client.d.ts +1 -0
  176. package/dist/server-fn-client.mjs +1 -1
  177. package/dist/server-fn.cjs +785 -3
  178. package/dist/server-fn.cjs.map +1 -1
  179. package/dist/server-fn.d.mts +10 -1
  180. package/dist/server-fn.d.ts +10 -1
  181. package/dist/server-fn.mjs +6 -3
  182. package/dist/server-plugins.d.mts +3 -2
  183. package/dist/server-plugins.d.ts +3 -2
  184. package/dist/server-query-client.cjs +278 -7
  185. package/dist/server-query-client.cjs.map +1 -1
  186. package/dist/server-query-client.mjs +4 -4
  187. package/dist/server-query.cjs +381 -10
  188. package/dist/server-query.cjs.map +1 -1
  189. package/dist/server-query.mjs +5 -5
  190. package/dist/server.cjs +1233 -118
  191. package/dist/server.cjs.map +1 -1
  192. package/dist/server.d.mts +8 -7
  193. package/dist/server.d.ts +8 -7
  194. package/dist/server.mjs +53 -33
  195. package/dist/server.mjs.map +1 -1
  196. package/dist/testing.cjs +436 -25
  197. package/dist/testing.cjs.map +1 -1
  198. package/dist/testing.d.mts +4 -3
  199. package/dist/testing.d.ts +4 -3
  200. package/dist/testing.mjs +19 -18
  201. package/dist/testing.mjs.map +1 -1
  202. package/dist/transport-BmbGTXA5.d.mts +49 -0
  203. package/dist/transport-BmbGTXA5.d.ts +49 -0
  204. package/dist/{utils-DOXXivjL.d.mts → utils-CHXlMDkg.d.mts} +1 -1
  205. package/dist/{utils-DA1yo54_.d.ts → utils-Drsyz1Vj.d.ts} +1 -1
  206. package/dist/version.cjs +36 -0
  207. package/dist/version.cjs.map +1 -0
  208. package/dist/version.d.mts +3 -0
  209. package/dist/version.d.ts +3 -0
  210. package/dist/version.mjs +8 -0
  211. package/dist/version.mjs.map +1 -0
  212. package/dist/vite.cjs +1039 -93
  213. package/dist/vite.cjs.map +1 -1
  214. package/dist/vite.d.mts +2 -1
  215. package/dist/vite.d.ts +2 -1
  216. package/dist/vite.mjs +23 -21
  217. package/dist/workflows.cjs +6 -8
  218. package/dist/workflows.cjs.map +1 -1
  219. package/dist/workflows.mjs +1 -1
  220. package/package.json +12 -5
  221. package/types/api.d.ts +44 -0
  222. package/types/client.d.ts +220 -16
  223. package/dist/chunk-4PTK6KT4.mjs +0 -364
  224. package/dist/chunk-4PTK6KT4.mjs.map +0 -1
  225. package/dist/chunk-AGS7UJ52.mjs.map +0 -1
  226. package/dist/chunk-CHFHNEMO.mjs.map +0 -1
  227. package/dist/chunk-EVGBEZDF.mjs +0 -215
  228. package/dist/chunk-EVGBEZDF.mjs.map +0 -1
  229. package/dist/chunk-G3B5BRDY.mjs.map +0 -1
  230. package/dist/chunk-GZP5W4K3.mjs.map +0 -1
  231. package/dist/chunk-JLWMCADZ.mjs.map +0 -1
  232. package/dist/chunk-JN3UU635.mjs.map +0 -1
  233. package/dist/chunk-OYXO65LI.mjs.map +0 -1
  234. package/dist/chunk-PRNYWD7K.mjs.map +0 -1
  235. package/dist/chunk-QF4PHCMA.mjs.map +0 -1
  236. package/dist/chunk-QFQRHRZS.mjs.map +0 -1
  237. package/dist/chunk-RKI6TN3A.mjs.map +0 -1
  238. package/dist/chunk-S2XHZS7W.mjs.map +0 -1
  239. package/dist/chunk-SOU3Q3ZA.mjs.map +0 -1
  240. package/dist/chunk-TU4YJICB.mjs.map +0 -1
  241. package/dist/chunk-TXCFGOHW.mjs +0 -42
  242. package/dist/chunk-TXCFGOHW.mjs.map +0 -1
  243. package/dist/chunk-UHE2TTLK.mjs.map +0 -1
  244. package/dist/chunk-Y2OJJD7R.mjs.map +0 -1
  245. package/dist/chunk-Y7A27276.mjs.map +0 -1
  246. package/dist/chunk-ZJQNATYV.mjs.map +0 -1
  247. package/dist/devtools-client-6WPY2AQU.mjs.map +0 -1
  248. /package/dist/{chunk-TRORP7E4.mjs.map → chunk-3U3ZUQAQ.mjs.map} +0 -0
  249. /package/dist/{chunk-B6N4ZB2H.mjs.map → chunk-C2ROET2V.mjs.map} +0 -0
  250. /package/dist/{chunk-HXP3ZSPC.mjs.map → chunk-SADGIRS2.mjs.map} +0 -0
  251. /package/dist/{chunk-6XDUHOEM.mjs.map → chunk-TQAEEJB4.mjs.map} +0 -0
  252. /package/dist/{chunk-SXZTEK3V.mjs.map → chunk-UXUAWNO5.mjs.map} +0 -0
  253. /package/dist/{chunk-YHU7SS6L.mjs.map → chunk-Z5CQMP54.mjs.map} +0 -0
  254. /package/dist/{devtools-AE4RVXCM.mjs.map → devtools-LVC3VB5P.mjs.map} +0 -0
package/dist/client.cjs CHANGED
@@ -31,6 +31,7 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  // src/client.ts
32
32
  var client_exports = {};
33
33
  __export(client_exports, {
34
+ APIClientError: () => APIClientError,
34
35
  FarmClientPluginManager: () => FarmClientPluginManager,
35
36
  IntegrationClientError: () => IntegrationClientError,
36
37
  Link: () => Link,
@@ -63,6 +64,8 @@ __export(client_exports, {
63
64
  readPageState: () => readPageState,
64
65
  replaceState: () => replaceState,
65
66
  useBlocker: () => useBlocker,
67
+ useFetcher: () => useFetcher,
68
+ useMutation: () => useMutation,
66
69
  useNavigation: () => useNavigation,
67
70
  usePageState: () => usePageState,
68
71
  useRouter: () => useRouter,
@@ -180,7 +183,7 @@ function mergeIntegrationClientData(...values) {
180
183
  continue;
181
184
  }
182
185
  merged = {
183
- ...merged || {},
186
+ ...merged,
184
187
  ...data
185
188
  };
186
189
  }
@@ -981,14 +984,44 @@ function formatError(error) {
981
984
  __name(formatError, "formatError");
982
985
 
983
986
  // src/cache-invalidation.ts
987
+ var FARM_CACHE_INVALIDATION_HEADER = "x-farm-cache-invalidations";
984
988
  var FARM_CACHE_INVALIDATION_STATE = /* @__PURE__ */ Symbol.for("farm.cacheInvalidationState");
985
989
  var globalState = globalThis;
986
990
  function getFarmCacheInvalidationState() {
987
991
  return globalState[FARM_CACHE_INVALIDATION_STATE] ?? (globalState[FARM_CACHE_INVALIDATION_STATE] = {
988
- listeners: /* @__PURE__ */ new Set()
992
+ listeners: /* @__PURE__ */ new Set(),
993
+ taskListeners: /* @__PURE__ */ new Set()
989
994
  });
990
995
  }
991
996
  __name(getFarmCacheInvalidationState, "getFarmCacheInvalidationState");
997
+ function notifyFarmCacheInvalidation(key) {
998
+ if (typeof key !== "string" || key.length === 0) return;
999
+ for (const listener of getFarmCacheInvalidationState().listeners) {
1000
+ listener(key);
1001
+ }
1002
+ }
1003
+ __name(notifyFarmCacheInvalidation, "notifyFarmCacheInvalidation");
1004
+ function applyFarmCacheInvalidations(keys) {
1005
+ if (!Array.isArray(keys)) return;
1006
+ for (const key of keys) {
1007
+ if (typeof key === "string") {
1008
+ notifyFarmCacheInvalidation(key);
1009
+ }
1010
+ }
1011
+ }
1012
+ __name(applyFarmCacheInvalidations, "applyFarmCacheInvalidations");
1013
+ function decodeFarmCacheInvalidations(value) {
1014
+ if (!value) return [];
1015
+ try {
1016
+ const parsed = JSON.parse(decodeURIComponent(value));
1017
+ return Array.isArray(parsed) ? Array.from(
1018
+ new Set(parsed.filter((key) => typeof key === "string" && key.length > 0))
1019
+ ) : [];
1020
+ } catch {
1021
+ return [];
1022
+ }
1023
+ }
1024
+ __name(decodeFarmCacheInvalidations, "decodeFarmCacheInvalidations");
992
1025
  function subscribeFarmCacheInvalidation(listener) {
993
1026
  const state = getFarmCacheInvalidationState();
994
1027
  state.listeners.add(listener);
@@ -1008,11 +1041,51 @@ __name(getActiveFarmI18nSnapshot, "getActiveFarmI18nSnapshot");
1008
1041
 
1009
1042
  // src/cache.ts
1010
1043
  var _FarmDataCache = class _FarmDataCache {
1011
- constructor() {
1044
+ constructor(config = {}) {
1012
1045
  this.entries = /* @__PURE__ */ new Map();
1013
1046
  this.inflight = /* @__PURE__ */ new Map();
1014
1047
  this.invalidatedTagVersions = /* @__PURE__ */ new Map();
1015
1048
  this.version = 0;
1049
+ this.namespace = "farm";
1050
+ this.local = true;
1051
+ this.lease = {
1052
+ enabled: true,
1053
+ ttlMs: 1e4,
1054
+ waitTimeoutMs: 1e4,
1055
+ pollIntervalMs: 25
1056
+ };
1057
+ this.configure(config);
1058
+ }
1059
+ configure(config = {}) {
1060
+ this.adapter = config.adapter;
1061
+ this.namespace = normalizeCacheNamespace(config.namespace || "farm");
1062
+ this.local = !config.adapter;
1063
+ this.lease = config.lease === false ? { ...this.lease, enabled: false } : {
1064
+ enabled: true,
1065
+ ttlMs: normalizePositiveDuration(config.lease?.ttlMs, 1e4, "cache.lease.ttlMs"),
1066
+ waitTimeoutMs: normalizePositiveDuration(
1067
+ config.lease?.waitTimeoutMs,
1068
+ 1e4,
1069
+ "cache.lease.waitTimeoutMs"
1070
+ ),
1071
+ pollIntervalMs: normalizePositiveDuration(
1072
+ config.lease?.pollIntervalMs,
1073
+ 25,
1074
+ "cache.lease.pollIntervalMs"
1075
+ )
1076
+ };
1077
+ if (!this.local) {
1078
+ this.entries.clear();
1079
+ this.inflight.clear();
1080
+ this.invalidatedTagVersions.clear();
1081
+ this.version = 0;
1082
+ }
1083
+ }
1084
+ get adapterName() {
1085
+ return this.adapter?.name || (this.adapter ? "custom" : "memory");
1086
+ }
1087
+ get hasAdapter() {
1088
+ return this.adapter !== void 0;
1016
1089
  }
1017
1090
  get size() {
1018
1091
  return this.entries.size;
@@ -1048,6 +1121,45 @@ var _FarmDataCache = class _FarmDataCache {
1048
1121
  });
1049
1122
  return this.toPublicEntry(entry);
1050
1123
  }
1124
+ async getEntryAsync(key, options = {}) {
1125
+ if (this.local) {
1126
+ const localEntry = this.getEntry(key, options);
1127
+ if (localEntry) return localEntry;
1128
+ }
1129
+ if (!this.adapter) {
1130
+ return this.local ? void 0 : this.getEntry(key, options);
1131
+ }
1132
+ const entry = await this.adapter.get(this.createAdapterKey(key));
1133
+ if (!entry) {
1134
+ emitFarmEvent({ type: "cache.miss", key });
1135
+ return void 0;
1136
+ }
1137
+ assertFarmCacheEntry(entry, key);
1138
+ const stale = await this.isAdapterEntryStale(entry, options.now);
1139
+ if (stale) {
1140
+ emitFarmEvent({
1141
+ type: "cache.stale",
1142
+ key,
1143
+ tags: [...entry.tags],
1144
+ revalidate: entry.revalidate
1145
+ });
1146
+ if (!options.allowStale) {
1147
+ emitFarmEvent({ type: "cache.miss", key, reason: "stale" });
1148
+ return void 0;
1149
+ }
1150
+ }
1151
+ emitFarmEvent({
1152
+ type: "cache.hit",
1153
+ key,
1154
+ tags: [...entry.tags],
1155
+ revalidate: entry.revalidate,
1156
+ stale
1157
+ });
1158
+ if (this.local && !stale) {
1159
+ this.hydrateLocalEntry(entry);
1160
+ }
1161
+ return { ...entry, key };
1162
+ }
1051
1163
  set(key, value, options = {}) {
1052
1164
  const tags = /* @__PURE__ */ new Set();
1053
1165
  for (const tag of options.tags ?? []) {
@@ -1060,6 +1172,7 @@ var _FarmDataCache = class _FarmDataCache {
1060
1172
  key,
1061
1173
  value,
1062
1174
  tags,
1175
+ tagVersions: void 0,
1063
1176
  createdAt: options.createdAt ?? Date.now(),
1064
1177
  createdVersion: ++this.version,
1065
1178
  revalidate: normalizeRevalidate(options.revalidate)
@@ -1073,11 +1186,45 @@ var _FarmDataCache = class _FarmDataCache {
1073
1186
  });
1074
1187
  return this.toPublicEntry(entry);
1075
1188
  }
1189
+ async setAsync(key, value, options = {}, tagVersions) {
1190
+ const tags = normalizeCacheOptionsTags(options);
1191
+ const capturedVersions = tagVersions ?? await this.getAdapterTagVersions(Array.from(tags.values()));
1192
+ const entry = {
1193
+ key,
1194
+ value,
1195
+ tags: Array.from(tags),
1196
+ tagVersions: capturedVersions,
1197
+ createdAt: options.createdAt ?? Date.now(),
1198
+ createdVersion: ++this.version,
1199
+ revalidate: normalizeRevalidate(options.revalidate)
1200
+ };
1201
+ if (this.local) {
1202
+ this.hydrateLocalEntry(entry);
1203
+ }
1204
+ if (this.adapter) {
1205
+ await this.adapter.set(this.createAdapterKey(key), entry);
1206
+ }
1207
+ emitFarmEvent({
1208
+ type: "cache.set",
1209
+ key,
1210
+ tags: [...entry.tags],
1211
+ revalidate: entry.revalidate
1212
+ });
1213
+ return entry;
1214
+ }
1076
1215
  delete(key) {
1077
1216
  const deleted = this.entries.delete(key);
1078
1217
  emitFarmEvent({ type: "cache.delete", key, deleted });
1079
1218
  return deleted;
1080
1219
  }
1220
+ async deleteAsync(key) {
1221
+ const deleted = this.entries.delete(key);
1222
+ if (this.adapter) {
1223
+ await this.adapter.delete(this.createAdapterKey(key));
1224
+ }
1225
+ emitFarmEvent({ type: "cache.delete", key, deleted: this.adapter ? true : deleted });
1226
+ return this.adapter ? true : deleted;
1227
+ }
1081
1228
  clear() {
1082
1229
  const count = this.entries.size;
1083
1230
  this.entries.clear();
@@ -1086,6 +1233,10 @@ var _FarmDataCache = class _FarmDataCache {
1086
1233
  this.version = 0;
1087
1234
  emitFarmEvent({ type: "cache.clear", count });
1088
1235
  }
1236
+ async clearAsync() {
1237
+ this.clear();
1238
+ await this.adapter?.clear?.();
1239
+ }
1089
1240
  isStale(entry, now2 = Date.now()) {
1090
1241
  if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now2 - entry.createdAt >= entry.revalidate * 1e3) {
1091
1242
  return true;
@@ -1098,6 +1249,12 @@ var _FarmDataCache = class _FarmDataCache {
1098
1249
  }
1099
1250
  return false;
1100
1251
  }
1252
+ async isStaleAsync(entry, now2 = Date.now()) {
1253
+ if (this.adapter) {
1254
+ return this.isAdapterEntryStale(entry, now2);
1255
+ }
1256
+ return this.isStale(entry, now2);
1257
+ }
1101
1258
  revalidateTag(tag, options = {}) {
1102
1259
  const normalized = normalizeCacheTag(tag);
1103
1260
  const count = this.invalidateTag(normalized);
@@ -1106,6 +1263,15 @@ var _FarmDataCache = class _FarmDataCache {
1106
1263
  );
1107
1264
  return count;
1108
1265
  }
1266
+ async revalidateTagAsync(tag, options = {}) {
1267
+ const normalized = normalizeCacheTag(tag);
1268
+ const count = this.invalidateTag(normalized);
1269
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(normalized)]);
1270
+ emitFarmEvent(
1271
+ options.source === "updateTag" ? { type: "cache.updateTag", tag: normalized, count } : { type: "cache.revalidateTag", tag: normalized, profile: options.profile, count }
1272
+ );
1273
+ return count;
1274
+ }
1109
1275
  revalidatePath(routePath) {
1110
1276
  const normalizedPath = normalizeRevalidatePath(routePath);
1111
1277
  const pathTag = createPathCacheTag(normalizedPath);
@@ -1122,8 +1288,25 @@ var _FarmDataCache = class _FarmDataCache {
1122
1288
  }
1123
1289
  return count;
1124
1290
  }
1291
+ async revalidatePathAsync(routePath) {
1292
+ const normalizedPath = normalizeRevalidatePath(routePath);
1293
+ const pathTag = createPathCacheTag(normalizedPath);
1294
+ const pprCount = this.countEntriesForTags([pathTag, "ppr"]);
1295
+ const count = this.invalidateTag(pathTag);
1296
+ await this.adapter?.invalidateTags?.([this.createAdapterTag(pathTag)]);
1297
+ emitFarmEvent({ type: "cache.revalidatePath", path: normalizedPath, count });
1298
+ if (pprCount > 0) {
1299
+ emitFarmEvent({
1300
+ type: "ppr.shell.invalidated",
1301
+ route: normalizedPath,
1302
+ reason: "revalidatePath",
1303
+ count: pprCount
1304
+ });
1305
+ }
1306
+ return count;
1307
+ }
1125
1308
  async getOrSet(key, producer, options = {}) {
1126
- const cached = this.getEntry(key);
1309
+ const cached = await this.getEntryAsync(key);
1127
1310
  if (cached) {
1128
1311
  return cached.value;
1129
1312
  }
@@ -1132,10 +1315,8 @@ var _FarmDataCache = class _FarmDataCache {
1132
1315
  emitFarmEvent({ type: "cache.dedupe", key });
1133
1316
  return inflight;
1134
1317
  }
1135
- const promise = Promise.resolve().then(producer).then((value) => {
1136
- this.set(key, value, options);
1137
- return value;
1138
- }).catch((error) => {
1318
+ const tags = Array.from(normalizeCacheOptionsTags(options));
1319
+ const promise = this.fillCacheEntry(key, producer, options, tags).catch((error) => {
1139
1320
  emitFarmEvent({ type: "cache.error", key, operation: "set", error });
1140
1321
  throw error;
1141
1322
  }).finally(() => {
@@ -1144,6 +1325,40 @@ var _FarmDataCache = class _FarmDataCache {
1144
1325
  this.inflight.set(key, promise);
1145
1326
  return promise;
1146
1327
  }
1328
+ async fillCacheEntry(key, producer, options, tags) {
1329
+ const leaseKey = `${this.namespace}:lease:${key}`;
1330
+ let leaseToken;
1331
+ if (this.adapter?.acquireLease && this.adapter.releaseLease && this.lease.enabled) {
1332
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
1333
+ if (!leaseToken) {
1334
+ const shared = await this.waitForAdapterEntry(key);
1335
+ if (shared) {
1336
+ emitFarmEvent({ type: "cache.dedupe", key });
1337
+ return shared.value;
1338
+ }
1339
+ leaseToken = await this.adapter.acquireLease(leaseKey, this.lease.ttlMs);
1340
+ }
1341
+ }
1342
+ try {
1343
+ const initialTagVersions = await this.getAdapterTagVersions(tags);
1344
+ const value = await producer();
1345
+ await this.setAsync(key, value, options, initialTagVersions);
1346
+ return value;
1347
+ } finally {
1348
+ if (leaseToken && this.adapter?.releaseLease) {
1349
+ await this.adapter.releaseLease(leaseKey, leaseToken);
1350
+ }
1351
+ }
1352
+ }
1353
+ async waitForAdapterEntry(key) {
1354
+ const deadline = Date.now() + this.lease.waitTimeoutMs;
1355
+ while (Date.now() < deadline) {
1356
+ await delay(this.lease.pollIntervalMs);
1357
+ const entry = await this.getEntryAsync(key);
1358
+ if (entry) return entry;
1359
+ }
1360
+ return void 0;
1361
+ }
1147
1362
  countEntriesForTag(tag) {
1148
1363
  let count = 0;
1149
1364
  for (const entry of this.entries.values()) {
@@ -1171,11 +1386,53 @@ var _FarmDataCache = class _FarmDataCache {
1171
1386
  key: entry.key,
1172
1387
  value: entry.value,
1173
1388
  tags: Array.from(entry.tags),
1389
+ tagVersions: entry.tagVersions,
1174
1390
  createdAt: entry.createdAt,
1175
1391
  createdVersion: entry.createdVersion,
1176
1392
  revalidate: entry.revalidate
1177
1393
  };
1178
1394
  }
1395
+ hydrateLocalEntry(entry) {
1396
+ this.entries.set(entry.key, {
1397
+ key: entry.key,
1398
+ value: entry.value,
1399
+ tags: new Set(entry.tags.map(normalizeCacheTag)),
1400
+ tagVersions: entry.tagVersions,
1401
+ createdAt: entry.createdAt,
1402
+ createdVersion: entry.createdVersion ?? ++this.version,
1403
+ revalidate: normalizeRevalidate(entry.revalidate)
1404
+ });
1405
+ }
1406
+ createAdapterKey(key) {
1407
+ return `${this.namespace}:entry:${key}`;
1408
+ }
1409
+ createAdapterTag(tag) {
1410
+ return `${this.namespace}:tag:${tag}`;
1411
+ }
1412
+ async getAdapterTagVersions(tags) {
1413
+ if (!this.adapter?.getTagVersions || tags.length === 0) return {};
1414
+ const normalized = tags.map(normalizeCacheTag);
1415
+ const physicalTags = normalized.map((tag) => this.createAdapterTag(tag));
1416
+ const versions = await this.adapter.getTagVersions(physicalTags);
1417
+ return Object.fromEntries(
1418
+ normalized.map((tag, index) => [
1419
+ tag,
1420
+ normalizeAdapterVersion(versions[physicalTags[index]])
1421
+ ])
1422
+ );
1423
+ }
1424
+ async isAdapterEntryStale(entry, now2 = Date.now()) {
1425
+ if (typeof entry.revalidate === "number" && entry.revalidate > 0 && now2 - entry.createdAt >= entry.revalidate * 1e3) {
1426
+ return true;
1427
+ }
1428
+ const currentVersions = await this.getAdapterTagVersions(entry.tags);
1429
+ for (const tag of entry.tags) {
1430
+ if (normalizeAdapterVersion(currentVersions[tag]) > normalizeAdapterVersion(entry.tagVersions?.[tag])) {
1431
+ return true;
1432
+ }
1433
+ }
1434
+ return false;
1435
+ }
1179
1436
  };
1180
1437
  __name(_FarmDataCache, "FarmDataCache");
1181
1438
  var FarmDataCache = _FarmDataCache;
@@ -1240,6 +1497,52 @@ function normalizeCacheTag(tag) {
1240
1497
  return normalized;
1241
1498
  }
1242
1499
  __name(normalizeCacheTag, "normalizeCacheTag");
1500
+ function normalizeCacheNamespace(namespace) {
1501
+ if (typeof namespace !== "string") {
1502
+ throw new TypeError("Cache namespace must be a string.");
1503
+ }
1504
+ const normalized = namespace.trim().replace(/:+$/g, "");
1505
+ if (!normalized) {
1506
+ throw new Error("Cache namespace cannot be empty.");
1507
+ }
1508
+ return normalized;
1509
+ }
1510
+ __name(normalizeCacheNamespace, "normalizeCacheNamespace");
1511
+ function normalizeAdapterVersion(value) {
1512
+ return typeof value === "number" && Number.isFinite(value) && value > 0 ? value : 0;
1513
+ }
1514
+ __name(normalizeAdapterVersion, "normalizeAdapterVersion");
1515
+ function normalizePositiveDuration(value, fallback, name) {
1516
+ if (value === void 0) return fallback;
1517
+ if (!Number.isFinite(value) || value <= 0) {
1518
+ throw new TypeError(`${name} must be a positive number of milliseconds.`);
1519
+ }
1520
+ return Math.floor(value);
1521
+ }
1522
+ __name(normalizePositiveDuration, "normalizePositiveDuration");
1523
+ function delay(ms) {
1524
+ return new Promise((resolve) => setTimeout(resolve, ms));
1525
+ }
1526
+ __name(delay, "delay");
1527
+ function normalizeCacheOptionsTags(options) {
1528
+ const tags = /* @__PURE__ */ new Set();
1529
+ for (const tag of options.tags ?? []) {
1530
+ tags.add(normalizeCacheTag(tag));
1531
+ }
1532
+ for (const routePath of options.paths ?? []) {
1533
+ tags.add(createPathCacheTag(routePath));
1534
+ }
1535
+ return tags;
1536
+ }
1537
+ __name(normalizeCacheOptionsTags, "normalizeCacheOptionsTags");
1538
+ function assertFarmCacheEntry(entry, expectedKey) {
1539
+ if (!entry || typeof entry !== "object" || entry.key !== expectedKey || !Array.isArray(entry.tags) || typeof entry.createdAt !== "number") {
1540
+ throw new TypeError(
1541
+ `Cache adapter returned an invalid entry for ${JSON.stringify(expectedKey)}.`
1542
+ );
1543
+ }
1544
+ }
1545
+ __name(assertFarmCacheEntry, "assertFarmCacheEntry");
1243
1546
  function getFunctionCacheIdentity(fn) {
1244
1547
  if (fn.name) {
1245
1548
  return `name:${fn.name}`;
@@ -1436,7 +1739,86 @@ function normalizeFarmClientCacheKey(key) {
1436
1739
  }
1437
1740
  __name(normalizeFarmClientCacheKey, "normalizeFarmClientCacheKey");
1438
1741
 
1742
+ // src/api/transport.ts
1743
+ function isJSONStreamResponse(response) {
1744
+ const contentType = response.headers?.get?.("content-type")?.toLowerCase() ?? "";
1745
+ return contentType.includes("application/x-ndjson") || contentType.includes("application/ndjson");
1746
+ }
1747
+ __name(isJSONStreamResponse, "isJSONStreamResponse");
1748
+ function readJSONStream(response) {
1749
+ if (!response.body) {
1750
+ throw new TypeError("Cannot read a JSON stream response without a body");
1751
+ }
1752
+ const reader = response.body.getReader();
1753
+ const decoder = new TextDecoder();
1754
+ let buffer = "";
1755
+ let completed = false;
1756
+ let claimed = false;
1757
+ const iterator = {
1758
+ async next() {
1759
+ while (true) {
1760
+ const lineEnd = buffer.indexOf("\n");
1761
+ if (lineEnd >= 0) {
1762
+ const line = buffer.slice(0, lineEnd).trim();
1763
+ buffer = buffer.slice(lineEnd + 1);
1764
+ if (!line) continue;
1765
+ return { done: false, value: JSON.parse(line) };
1766
+ }
1767
+ if (completed) {
1768
+ const line = buffer.trim();
1769
+ buffer = "";
1770
+ if (!line) return { done: true, value: void 0 };
1771
+ return { done: false, value: JSON.parse(line) };
1772
+ }
1773
+ const chunk = await reader.read();
1774
+ completed = chunk.done;
1775
+ buffer += decoder.decode(chunk.value, { stream: !chunk.done });
1776
+ }
1777
+ },
1778
+ async return() {
1779
+ completed = true;
1780
+ buffer = "";
1781
+ await reader.cancel();
1782
+ return { done: true, value: void 0 };
1783
+ }
1784
+ };
1785
+ return {
1786
+ response,
1787
+ async cancel(reason) {
1788
+ completed = true;
1789
+ buffer = "";
1790
+ await reader.cancel(reason);
1791
+ },
1792
+ [Symbol.asyncIterator]() {
1793
+ if (claimed) {
1794
+ throw new TypeError("Farm API streams can only be consumed once");
1795
+ }
1796
+ claimed = true;
1797
+ return iterator;
1798
+ }
1799
+ };
1800
+ }
1801
+ __name(readJSONStream, "readJSONStream");
1802
+ function isFarmAPIStream(value) {
1803
+ return typeof value === "object" && value !== null && "response" in value && typeof value.cancel === "function" && typeof value[Symbol.asyncIterator] === "function";
1804
+ }
1805
+ __name(isFarmAPIStream, "isFarmAPIStream");
1806
+
1439
1807
  // src/api/client.ts
1808
+ var FARM_API_ROUTE_REF_SYMBOL = /* @__PURE__ */ Symbol.for("farm.api.route-ref");
1809
+ var FARM_API_ROUTE_META_SYMBOL = /* @__PURE__ */ Symbol.for("farm.api.route-meta");
1810
+ var _APIClientError = class _APIClientError extends Error {
1811
+ constructor(code, data, options) {
1812
+ super(options.message);
1813
+ this.name = "APIClientError";
1814
+ this.code = code;
1815
+ this.data = data;
1816
+ this.status = options.status;
1817
+ this.response = options.response;
1818
+ }
1819
+ };
1820
+ __name(_APIClientError, "APIClientError");
1821
+ var APIClientError = _APIClientError;
1440
1822
  function createAPIClient(options = {}) {
1441
1823
  options ?? (options = {});
1442
1824
  const baseURL = options.baseURL || (typeof window !== "undefined" ? window.location.origin : "http://localhost:3000");
@@ -1462,21 +1844,30 @@ function createAPIClient(options = {}) {
1462
1844
  }
1463
1845
  });
1464
1846
  }
1847
+ const headers = {
1848
+ "Content-Type": "application/json",
1849
+ ...options.headers,
1850
+ ...requestOptions.headers
1851
+ };
1465
1852
  const fetchOptions = {
1466
1853
  method: requestOptions.method || "GET",
1467
- headers: {
1468
- "Content-Type": "application/json",
1469
- ...options.headers,
1470
- ...requestOptions.headers
1471
- }
1854
+ headers
1472
1855
  };
1473
- if (requestOptions.body) {
1474
- fetchOptions.body = JSON.stringify(requestOptions.body);
1856
+ if (requestOptions.body !== void 0) {
1857
+ if (isFormData(requestOptions.body)) {
1858
+ deleteHeader(headers, "content-type");
1859
+ fetchOptions.body = requestOptions.body;
1860
+ } else {
1861
+ fetchOptions.body = JSON.stringify(requestOptions.body);
1862
+ }
1475
1863
  }
1476
1864
  const response = await fetch(url.toString(), fetchOptions);
1865
+ applyFarmCacheInvalidations(
1866
+ decodeFarmCacheInvalidations(response.headers?.get?.(FARM_CACHE_INVALIDATION_HEADER))
1867
+ );
1477
1868
  let data = void 0;
1478
1869
  if (response.status !== 204 && response.status !== 205) {
1479
- data = await response.json();
1870
+ data = isJSONStreamResponse(response) ? readJSONStream(response) : await response.json();
1480
1871
  }
1481
1872
  return { response, data };
1482
1873
  }, "fetchClient");
@@ -1622,16 +2013,16 @@ function createAPIClient(options = {}) {
1622
2013
  }
1623
2014
  }
1624
2015
  attempt += 1;
1625
- const delay = typeof clientOptions?.retry?.delay === "function" ? clientOptions.retry.delay(attempt) : clientOptions?.retry?.delay ?? 0;
1626
- if (delay > 0) {
1627
- await new Promise((resolve) => setTimeout(resolve, delay));
2016
+ const delay2 = typeof clientOptions?.retry?.delay === "function" ? clientOptions.retry.delay(attempt) : clientOptions?.retry?.delay ?? 0;
2017
+ if (delay2 > 0) {
2018
+ await new Promise((resolve) => setTimeout(resolve, delay2));
1628
2019
  }
1629
2020
  }
1630
2021
  })();
1631
2022
  inflightState.set(cacheKey, { promise, startedAt: now2 });
1632
2023
  try {
1633
2024
  const result2 = await promise;
1634
- if (!result2.error && isCacheEnabled) {
2025
+ if (!result2.error && isCacheEnabled && !isFarmAPIStream(result2.data)) {
1635
2026
  const updatedAt = Date.now();
1636
2027
  cacheState.set(cacheKey, {
1637
2028
  data: result2.data,
@@ -1704,19 +2095,48 @@ function createAPIClient(options = {}) {
1704
2095
  clientOptions?.onSettled?.(result.data, result.error);
1705
2096
  return result;
1706
2097
  }, "request");
1707
- return createNestedProxy([], request, routeMeta, baseURL, rootAliases);
2098
+ return createNestedProxy(
2099
+ [],
2100
+ request,
2101
+ routeMeta,
2102
+ baseURL,
2103
+ options.baseURL === void 0,
2104
+ rootAliases
2105
+ );
1708
2106
  }
1709
2107
  __name(createAPIClient, "createAPIClient");
1710
- function createNestedProxy(path, client, routeMeta, baseURL, rootAliases) {
2108
+ function createNestedProxy(path, client, routeMeta, baseURL, sameOrigin, rootAliases) {
1711
2109
  const target = /* @__PURE__ */ __name(() => {
1712
2110
  }, "target");
1713
2111
  const proxy = new Proxy(target, {
1714
2112
  // When accessing a property (api.hello)
1715
2113
  get(_target, prop) {
2114
+ if (prop === FARM_API_ROUTE_REF_SYMBOL) {
2115
+ return path.length > 0;
2116
+ }
2117
+ if (prop === FARM_API_ROUTE_META_SYMBOL) {
2118
+ const metadata = resolveRouteMeta({ path, baseURL });
2119
+ return Object.freeze({
2120
+ path: metadata.routePath,
2121
+ method: metadata.method,
2122
+ baseURL,
2123
+ sameOrigin
2124
+ });
2125
+ }
1716
2126
  if (path.length === 0 && typeof prop === "string" && rootAliases && prop in rootAliases) {
1717
2127
  return rootAliases[prop];
1718
2128
  }
1719
- return createNestedProxy([...path, prop], client, routeMeta, baseURL, rootAliases);
2129
+ if (typeof prop !== "string") {
2130
+ return Reflect.get(_target, prop);
2131
+ }
2132
+ return createNestedProxy(
2133
+ [...path, prop],
2134
+ client,
2135
+ routeMeta,
2136
+ baseURL,
2137
+ sameOrigin,
2138
+ rootAliases
2139
+ );
1720
2140
  },
1721
2141
  // When calling as a function
1722
2142
  apply(_target, _thisArg, args) {
@@ -1738,6 +2158,26 @@ function createNestedProxy(path, client, routeMeta, baseURL, rootAliases) {
1738
2158
  return proxy;
1739
2159
  }
1740
2160
  __name(createNestedProxy, "createNestedProxy");
2161
+ function isAPIRouteRef(value) {
2162
+ return typeof value === "function" && value[FARM_API_ROUTE_REF_SYMBOL] === true;
2163
+ }
2164
+ __name(isAPIRouteRef, "isAPIRouteRef");
2165
+ function getAPIRouteRefMetadata(value) {
2166
+ if (!isAPIRouteRef(value)) return null;
2167
+ const metadata = value[FARM_API_ROUTE_META_SYMBOL];
2168
+ if (!metadata || typeof metadata !== "object") return null;
2169
+ const candidate = metadata;
2170
+ if (typeof candidate.path !== "string" || typeof candidate.method !== "string" || typeof candidate.baseURL !== "string" || typeof candidate.sameOrigin !== "boolean") {
2171
+ return null;
2172
+ }
2173
+ return {
2174
+ path: candidate.path,
2175
+ method: candidate.method,
2176
+ baseURL: candidate.baseURL,
2177
+ sameOrigin: candidate.sameOrigin
2178
+ };
2179
+ }
2180
+ __name(getAPIRouteRefMetadata, "getAPIRouteRefMetadata");
1741
2181
  function createServerAPIClient(endpoints, options = {}) {
1742
2182
  if (options.integrations === false || Object.prototype.hasOwnProperty.call(endpoints, "integrations")) {
1743
2183
  return endpoints;
@@ -1804,7 +2244,10 @@ function resolveTargetKey(routeMeta, target, input, baseURL = "http://localhost:
1804
2244
  }
1805
2245
  if (Array.isArray(target)) {
1806
2246
  const [route2, routeInput] = target;
1807
- return resolveTargetKey(routeMeta, route2, routeInput, baseURL);
2247
+ if (typeof route2 === "function") {
2248
+ return resolveTargetKey(routeMeta, route2, routeInput, baseURL);
2249
+ }
2250
+ return normalizeFarmClientCacheKey(target);
1808
2251
  }
1809
2252
  if ("key" in target) return normalizeFarmClientCacheKey(target.key);
1810
2253
  if ("path" in target) {
@@ -1830,21 +2273,381 @@ function resolveRouteMeta(meta) {
1830
2273
  }
1831
2274
  __name(resolveRouteMeta, "resolveRouteMeta");
1832
2275
  function normalizeError(error) {
1833
- if (error instanceof Error) return error;
1834
- return new Error(typeof error === "string" ? error : "Unknown error");
2276
+ if (error instanceof APIClientError) return error;
2277
+ const normalized = new APIClientError("network_error", void 0, {
2278
+ status: 0,
2279
+ message: error instanceof Error ? error.message : typeof error === "string" ? error : "Network request failed"
2280
+ });
2281
+ normalized.cause = error;
2282
+ return normalized;
1835
2283
  }
1836
2284
  __name(normalizeError, "normalizeError");
2285
+ function isFormData(value) {
2286
+ return typeof value === "object" && value !== null && (typeof FormData !== "undefined" && value instanceof FormData || Object.prototype.toString.call(value) === "[object FormData]" && typeof value.entries === "function");
2287
+ }
2288
+ __name(isFormData, "isFormData");
2289
+ function deleteHeader(headers, name) {
2290
+ const normalized = name.toLowerCase();
2291
+ for (const key of Object.keys(headers)) {
2292
+ if (key.toLowerCase() === normalized) delete headers[key];
2293
+ }
2294
+ }
2295
+ __name(deleteHeader, "deleteHeader");
1837
2296
  function createResponseError2(response, data) {
1838
- const error = new Error(`HTTP ${response.status}: ${response.statusText}`);
1839
- error.status = response.status;
1840
- error.response = response;
1841
- error.data = data;
1842
- return error;
2297
+ const expected = readEndpointErrorEnvelope(data);
2298
+ if (expected) {
2299
+ return new APIClientError(expected.code, expected.data, {
2300
+ status: response.status,
2301
+ message: expected.message,
2302
+ response
2303
+ });
2304
+ }
2305
+ return new APIClientError("http_error", data, {
2306
+ status: response.status,
2307
+ message: `HTTP ${response.status}: ${response.statusText}`,
2308
+ response
2309
+ });
1843
2310
  }
1844
2311
  __name(createResponseError2, "createResponseError");
2312
+ function readEndpointErrorEnvelope(data) {
2313
+ if (!data || typeof data !== "object") return null;
2314
+ const error = data.error;
2315
+ if (!error || typeof error !== "object") return null;
2316
+ const code = error.code;
2317
+ const message = error.message;
2318
+ if (typeof code !== "string" || typeof message !== "string") return null;
2319
+ return {
2320
+ code,
2321
+ message,
2322
+ data: error.data
2323
+ };
2324
+ }
2325
+ __name(readEndpointErrorEnvelope, "readEndpointErrorEnvelope");
1845
2326
 
1846
- // src/server-query-client.ts
2327
+ // src/mutation-client.ts
1847
2328
  var import_react = require("react");
2329
+ function useMutation(target, options = {}) {
2330
+ const initialData = options.initialData ?? null;
2331
+ const optionsRef = (0, import_react.useRef)(options);
2332
+ const targetRef = (0, import_react.useRef)(target);
2333
+ const requestIdRef = (0, import_react.useRef)(0);
2334
+ const initialState = {
2335
+ pendingCount: 0,
2336
+ status: "idle",
2337
+ data: initialData,
2338
+ error: null,
2339
+ variables: void 0
2340
+ };
2341
+ const stateRef = (0, import_react.useRef)(initialState);
2342
+ const [state, setState] = (0, import_react.useState)(initialState);
2343
+ optionsRef.current = options;
2344
+ targetRef.current = target;
2345
+ const setMutationState = (0, import_react.useCallback)(
2346
+ (value) => {
2347
+ setState((current) => {
2348
+ const next = typeof value === "function" ? value(current) : value;
2349
+ stateRef.current = next;
2350
+ return next;
2351
+ });
2352
+ },
2353
+ []
2354
+ );
2355
+ const mutateAsync = (0, import_react.useCallback)(
2356
+ async (variables) => {
2357
+ const requestId = ++requestIdRef.current;
2358
+ const currentOptions = optionsRef.current;
2359
+ const previousData = stateRef.current.data;
2360
+ const optimisticData = currentOptions.optimistic?.({
2361
+ variables,
2362
+ current: previousData
2363
+ });
2364
+ const hasOptimisticData = optimisticData !== void 0;
2365
+ setMutationState((current) => ({
2366
+ pendingCount: current.pendingCount + 1,
2367
+ status: "pending",
2368
+ data: hasOptimisticData ? optimisticData : currentOptions.resetOnMutate === false ? current.data : null,
2369
+ error: null,
2370
+ variables
2371
+ }));
2372
+ try {
2373
+ const mutationTarget = targetRef.current;
2374
+ const rawResult = isAPIRouteRef(mutationTarget) ? await mutationTarget(variables, currentOptions.request) : await mutationTarget(variables);
2375
+ const data = unwrapMutationResult(rawResult, isAPIRouteRef(mutationTarget));
2376
+ const isLatestRequest = requestId === requestIdRef.current;
2377
+ setMutationState((current) => {
2378
+ const pendingCount = Math.max(0, current.pendingCount - 1);
2379
+ if (!isLatestRequest) {
2380
+ return {
2381
+ ...current,
2382
+ pendingCount,
2383
+ status: pendingCount > 0 ? "pending" : current.status
2384
+ };
2385
+ }
2386
+ return {
2387
+ pendingCount,
2388
+ status: "success",
2389
+ data,
2390
+ error: null,
2391
+ variables
2392
+ };
2393
+ });
2394
+ if (isLatestRequest) {
2395
+ currentOptions.onSuccess?.(data, variables);
2396
+ currentOptions.onSettled?.(data, null, variables);
2397
+ }
2398
+ return data;
2399
+ } catch (cause) {
2400
+ const error = normalizeMutationError(cause);
2401
+ const isLatestRequest = requestId === requestIdRef.current;
2402
+ setMutationState((current) => {
2403
+ const pendingCount = Math.max(0, current.pendingCount - 1);
2404
+ if (!isLatestRequest) {
2405
+ return {
2406
+ ...current,
2407
+ pendingCount,
2408
+ status: pendingCount > 0 ? "pending" : current.status
2409
+ };
2410
+ }
2411
+ return {
2412
+ pendingCount,
2413
+ status: "error",
2414
+ data: hasOptimisticData && currentOptions.rollbackOnError ? previousData : currentOptions.resetOnMutate === false ? current.data : null,
2415
+ error,
2416
+ variables
2417
+ };
2418
+ });
2419
+ if (isLatestRequest) {
2420
+ currentOptions.onError?.(error, variables);
2421
+ currentOptions.onSettled?.(null, error, variables);
2422
+ }
2423
+ throw error;
2424
+ }
2425
+ },
2426
+ [setMutationState]
2427
+ );
2428
+ const mutate = (0, import_react.useCallback)(
2429
+ (variables) => {
2430
+ void mutateAsync(variables).catch(() => {
2431
+ });
2432
+ },
2433
+ [mutateAsync]
2434
+ );
2435
+ const reset = (0, import_react.useCallback)(() => {
2436
+ requestIdRef.current += 1;
2437
+ setMutationState({
2438
+ pendingCount: 0,
2439
+ status: "idle",
2440
+ data: optionsRef.current.initialData ?? null,
2441
+ error: null,
2442
+ variables: void 0
2443
+ });
2444
+ }, [setMutationState]);
2445
+ return (0, import_react.useMemo)(
2446
+ () => ({
2447
+ pending: state.pendingCount > 0,
2448
+ status: state.status,
2449
+ data: state.data,
2450
+ error: state.error,
2451
+ variables: state.variables,
2452
+ mutate,
2453
+ mutateAsync,
2454
+ reset
2455
+ }),
2456
+ [
2457
+ mutate,
2458
+ mutateAsync,
2459
+ reset,
2460
+ state.data,
2461
+ state.error,
2462
+ state.pendingCount,
2463
+ state.status,
2464
+ state.variables
2465
+ ]
2466
+ );
2467
+ }
2468
+ __name(useMutation, "useMutation");
2469
+ function unwrapMutationResult(result, apiRoute) {
2470
+ if (!apiRoute) return result;
2471
+ const apiResult = result;
2472
+ if (apiResult.error) {
2473
+ throw apiResult.error;
2474
+ }
2475
+ return apiResult.data;
2476
+ }
2477
+ __name(unwrapMutationResult, "unwrapMutationResult");
2478
+ function normalizeMutationError(error) {
2479
+ if (error instanceof Error) return error;
2480
+ const normalized = new Error(typeof error === "string" ? error : "Mutation failed");
2481
+ normalized.cause = error;
2482
+ return normalized;
2483
+ }
2484
+ __name(normalizeMutationError, "normalizeMutationError");
2485
+
2486
+ // src/fetcher-client.tsx
2487
+ var import_react2 = require("react");
2488
+ var formDataContexts = /* @__PURE__ */ new WeakMap();
2489
+ function useFetcher(target, options = {}) {
2490
+ const targetRef = (0, import_react2.useRef)(target);
2491
+ const optionsRef = (0, import_react2.useRef)(options);
2492
+ const submissionIdRef = (0, import_react2.useRef)(0);
2493
+ const [formData, setFormData] = (0, import_react2.useState)(null);
2494
+ const mutation = useMutation(target, options);
2495
+ targetRef.current = target;
2496
+ optionsRef.current = options;
2497
+ const submitAsync = (0, import_react2.useCallback)(
2498
+ async (input) => {
2499
+ const submissionId = ++submissionIdRef.current;
2500
+ const submittedFormData = isFormData2(input) ? input : null;
2501
+ setFormData(submittedFormData);
2502
+ try {
2503
+ const variables = submittedFormData ? mapFetcherFormData(targetRef.current, submittedFormData, optionsRef.current) : input;
2504
+ return await mutation.mutateAsync(variables);
2505
+ } finally {
2506
+ if (submissionId === submissionIdRef.current) {
2507
+ setFormData(null);
2508
+ }
2509
+ }
2510
+ },
2511
+ [mutation.mutateAsync]
2512
+ );
2513
+ const submit = (0, import_react2.useCallback)(
2514
+ (input) => {
2515
+ void submitAsync(input).catch(() => {
2516
+ });
2517
+ },
2518
+ [submitAsync]
2519
+ );
2520
+ const Form = (0, import_react2.useMemo)(() => {
2521
+ const FetcherForm = /* @__PURE__ */ __name(({ action, method, encType, onSubmit, ...props }) => {
2522
+ const targetValue = targetRef.current;
2523
+ const metadata = getAPIRouteRefMetadata(targetValue);
2524
+ const nativeAction = action ?? resolveFetcherFormAction(targetValue, metadata);
2525
+ const nativeMethod = method ?? (metadata && (metadata.method === "GET" || metadata.method === "POST") ? metadata.method.toLowerCase() : metadata ? "post" : void 0);
2526
+ const functionAction = typeof nativeAction === "function";
2527
+ const handleSubmit = /* @__PURE__ */ __name((event) => {
2528
+ onSubmit?.(event);
2529
+ if (event.defaultPrevented) return;
2530
+ event.preventDefault();
2531
+ const submitter = getSubmitter(event);
2532
+ const nextFormData = createSubmitterFormData(event.currentTarget, submitter);
2533
+ formDataContexts.set(nextFormData, {
2534
+ form: event.currentTarget,
2535
+ submitter
2536
+ });
2537
+ void submitAsync(nextFormData).catch(() => {
2538
+ });
2539
+ }, "handleSubmit");
2540
+ return (0, import_react2.createElement)("form", {
2541
+ ...props,
2542
+ action: nativeAction,
2543
+ method: functionAction ? void 0 : nativeMethod,
2544
+ encType: functionAction ? void 0 : encType,
2545
+ onSubmit: handleSubmit
2546
+ });
2547
+ }, "FetcherForm");
2548
+ FetcherForm.displayName = "FetcherForm";
2549
+ return FetcherForm;
2550
+ }, [submitAsync]);
2551
+ const reset = (0, import_react2.useCallback)(() => {
2552
+ submissionIdRef.current += 1;
2553
+ setFormData(null);
2554
+ mutation.reset();
2555
+ }, [mutation.reset]);
2556
+ return (0, import_react2.useMemo)(
2557
+ () => ({
2558
+ state: mutation.pending ? "submitting" : "idle",
2559
+ status: mutation.status,
2560
+ pending: mutation.pending,
2561
+ data: mutation.data,
2562
+ error: mutation.error,
2563
+ variables: mutation.variables,
2564
+ formData,
2565
+ submit,
2566
+ submitAsync,
2567
+ Form,
2568
+ reset
2569
+ }),
2570
+ [
2571
+ Form,
2572
+ formData,
2573
+ mutation.data,
2574
+ mutation.error,
2575
+ mutation.pending,
2576
+ mutation.status,
2577
+ mutation.variables,
2578
+ reset,
2579
+ submit,
2580
+ submitAsync
2581
+ ]
2582
+ );
2583
+ }
2584
+ __name(useFetcher, "useFetcher");
2585
+ function mapFetcherFormData(target, formData, options) {
2586
+ const context = formDataContexts.get(formData) ?? { form: null, submitter: null };
2587
+ if (options.mapFormData) {
2588
+ return options.mapFormData(formData, context);
2589
+ }
2590
+ if (!isAPIRouteRef(target)) {
2591
+ return formData;
2592
+ }
2593
+ const values = formDataToObject(formData);
2594
+ const method = getAPIRouteRefMetadata(target)?.method;
2595
+ return method === "GET" || method === "HEAD" ? { query: values } : { body: values };
2596
+ }
2597
+ __name(mapFetcherFormData, "mapFetcherFormData");
2598
+ function resolveFetcherFormAction(target, metadata) {
2599
+ if (!metadata) return isProgressiveFormAction(target) ? target : void 0;
2600
+ if (metadata.sameOrigin) return metadata.path;
2601
+ return new URL(metadata.path, metadata.baseURL).href;
2602
+ }
2603
+ __name(resolveFetcherFormAction, "resolveFetcherFormAction");
2604
+ function formDataToObject(formData) {
2605
+ const output = /* @__PURE__ */ Object.create(null);
2606
+ for (const [key, value] of formData.entries()) {
2607
+ if (key === "__proto__" || key === "constructor" || key === "prototype") continue;
2608
+ const current = output[key];
2609
+ if (current === void 0) {
2610
+ output[key] = value;
2611
+ } else if (Array.isArray(current)) {
2612
+ current.push(value);
2613
+ } else {
2614
+ output[key] = [current, value];
2615
+ }
2616
+ }
2617
+ return output;
2618
+ }
2619
+ __name(formDataToObject, "formDataToObject");
2620
+ function createSubmitterFormData(form, submitter) {
2621
+ try {
2622
+ return new FormData(form, submitter);
2623
+ } catch {
2624
+ const formData = new FormData(form);
2625
+ const candidate = submitter;
2626
+ if (candidate && !candidate.disabled && candidate.name) {
2627
+ formData.append(candidate.name, candidate.value ?? "");
2628
+ }
2629
+ return formData;
2630
+ }
2631
+ }
2632
+ __name(createSubmitterFormData, "createSubmitterFormData");
2633
+ function getSubmitter(event) {
2634
+ const nativeEvent = event.nativeEvent;
2635
+ return nativeEvent.submitter instanceof HTMLElement ? nativeEvent.submitter : null;
2636
+ }
2637
+ __name(getSubmitter, "getSubmitter");
2638
+ function isProgressiveFormAction(target) {
2639
+ const candidate = target;
2640
+ return candidate.__farmServerFn === true || typeof candidate.$$FORM_ACTION === "function" || candidate.$$typeof === /* @__PURE__ */ Symbol.for("react.server.reference");
2641
+ }
2642
+ __name(isProgressiveFormAction, "isProgressiveFormAction");
2643
+ function isFormData2(value) {
2644
+ if (!value || typeof value !== "object") return false;
2645
+ return typeof FormData !== "undefined" && value instanceof FormData || Object.prototype.toString.call(value) === "[object FormData]" && typeof value.entries === "function";
2646
+ }
2647
+ __name(isFormData2, "isFormData");
2648
+
2649
+ // src/server-query-client.ts
2650
+ var import_react3 = require("react");
1848
2651
  var FARM_SERVER_QUERY_CLIENT_STATE = /* @__PURE__ */ Symbol.for("farm.serverQueryClientState");
1849
2652
  var serverQueryClientGlobal = globalThis;
1850
2653
  var serverQueryClientState = serverQueryClientGlobal[FARM_SERVER_QUERY_CLIENT_STATE] ?? (serverQueryClientGlobal[FARM_SERVER_QUERY_CLIENT_STATE] = {
@@ -1876,25 +2679,25 @@ __name(prefetchServerQuery, "prefetchServerQuery");
1876
2679
  function useServerQuery(query, input, options = {}) {
1877
2680
  const cache = getFarmClientDataCache();
1878
2681
  const key = createServerQueryCallKey(query, input);
1879
- const inputRef = (0, import_react.useRef)(input);
1880
- const optionsRef = (0, import_react.useRef)(options);
1881
- const mountedKeyRef = (0, import_react.useRef)();
2682
+ const inputRef = (0, import_react3.useRef)(input);
2683
+ const optionsRef = (0, import_react3.useRef)(options);
2684
+ const mountedKeyRef = (0, import_react3.useRef)();
1882
2685
  inputRef.current = input;
1883
2686
  optionsRef.current = options;
1884
- const subscribe = (0, import_react.useCallback)(
2687
+ const subscribe = (0, import_react3.useCallback)(
1885
2688
  (listener) => cache.subscribe(key, listener),
1886
2689
  [cache, key]
1887
2690
  );
1888
- const getSnapshot = (0, import_react.useCallback)(() => cache.get(key), [cache, key]);
1889
- const entry = (0, import_react.useSyncExternalStore)(subscribe, getSnapshot, () => void 0);
1890
- const run = (0, import_react.useCallback)(
2691
+ const getSnapshot = (0, import_react3.useCallback)(() => cache.get(key), [cache, key]);
2692
+ const entry = (0, import_react3.useSyncExternalStore)(subscribe, getSnapshot, () => void 0);
2693
+ const run = (0, import_react3.useCallback)(
1891
2694
  (force = false) => fetchServerQuery(query, inputRef.current, {
1892
2695
  ...optionsRef.current,
1893
2696
  force
1894
2697
  }),
1895
2698
  [query]
1896
2699
  );
1897
- (0, import_react.useEffect)(() => {
2700
+ (0, import_react3.useEffect)(() => {
1898
2701
  if (options.enabled === false) return;
1899
2702
  const firstReadForKey = mountedKeyRef.current !== key;
1900
2703
  if (firstReadForKey) mountedKeyRef.current = key;
@@ -1902,7 +2705,7 @@ function useServerQuery(query, input, options = {}) {
1902
2705
  void run().catch(() => void 0);
1903
2706
  }
1904
2707
  }, [cache, entry, key, options.enabled, run]);
1905
- (0, import_react.useEffect)(() => {
2708
+ (0, import_react3.useEffect)(() => {
1906
2709
  if (options.enabled === false || typeof window === "undefined") return;
1907
2710
  const refreshIfStale = /* @__PURE__ */ __name(() => {
1908
2711
  if (cache.isStale(key)) void run().catch(() => void 0);
@@ -2215,7 +3018,7 @@ var api = {
2215
3018
  var endpoint = api;
2216
3019
 
2217
3020
  // src/store.ts
2218
- var import_react2 = require("react");
3021
+ var import_react4 = require("react");
2219
3022
  var RESERVED_STORE_KEYS = /* @__PURE__ */ new Set([
2220
3023
  "use",
2221
3024
  "get",
@@ -2347,20 +3150,20 @@ function createStore(initialState, extend) {
2347
3150
  }
2348
3151
  return lastSlice;
2349
3152
  }, "getSnapshot");
2350
- return (0, import_react2.useSyncExternalStore)(
3153
+ return (0, import_react4.useSyncExternalStore)(
2351
3154
  (notify) => addSubscription(keys, () => notify()),
2352
3155
  getSnapshot,
2353
3156
  getSnapshot
2354
3157
  );
2355
3158
  }
2356
3159
  if (typeof keyOrKeys === "string") {
2357
- return (0, import_react2.useSyncExternalStore)(
3160
+ return (0, import_react4.useSyncExternalStore)(
2358
3161
  (notify) => addSubscription([keyOrKeys], () => notify()),
2359
3162
  () => state[keyOrKeys],
2360
3163
  () => state[keyOrKeys]
2361
3164
  );
2362
3165
  }
2363
- return (0, import_react2.useSyncExternalStore)(
3166
+ return (0, import_react4.useSyncExternalStore)(
2364
3167
  (notify) => addSubscription(null, () => notify()),
2365
3168
  () => state,
2366
3169
  () => state
@@ -2412,7 +3215,7 @@ function createStore(initialState, extend) {
2412
3215
  __name(createStore, "createStore");
2413
3216
 
2414
3217
  // src/client/link.tsx
2415
- var import_react3 = require("react");
3218
+ var import_react5 = require("react");
2416
3219
 
2417
3220
  // src/router.ts
2418
3221
  function createFarmRouter(routes) {
@@ -2812,9 +3615,9 @@ function LinkInner({
2812
3615
  onTouchStart,
2813
3616
  ...props
2814
3617
  }, ref) {
2815
- const elementRef = (0, import_react3.useRef)(null);
2816
- const intentTimeoutRef = (0, import_react3.useRef)(null);
2817
- const hasPrefetched = (0, import_react3.useRef)(false);
3618
+ const elementRef = (0, import_react5.useRef)(null);
3619
+ const intentTimeoutRef = (0, import_react5.useRef)(null);
3620
+ const hasPrefetched = (0, import_react5.useRef)(false);
2818
3621
  const { intent, viewport, render } = normalizePrefetch(prefetch2);
2819
3622
  const baseHref = resolveLinkHref(href, params, {
2820
3623
  query,
@@ -2823,17 +3626,17 @@ function LinkInner({
2823
3626
  });
2824
3627
  const resolvedHref = localizeActiveFarmHref(baseHref, locale);
2825
3628
  const isExternal = isExternalUrl(resolvedHref);
2826
- const doPrefetch = (0, import_react3.useCallback)(() => {
3629
+ const doPrefetch = (0, import_react5.useCallback)(() => {
2827
3630
  if (isExternal || hasPrefetched.current) return;
2828
3631
  const router = getRouter();
2829
3632
  if (!router) return;
2830
3633
  hasPrefetched.current = true;
2831
3634
  router.prefetch(resolvedHref);
2832
3635
  }, [resolvedHref, isExternal]);
2833
- (0, import_react3.useEffect)(() => {
3636
+ (0, import_react5.useEffect)(() => {
2834
3637
  hasPrefetched.current = false;
2835
3638
  }, [resolvedHref]);
2836
- (0, import_react3.useEffect)(() => {
3639
+ (0, import_react5.useEffect)(() => {
2837
3640
  const element = elementRef.current;
2838
3641
  if (!element || isExternal) return;
2839
3642
  const router = getRouter();
@@ -2843,7 +3646,7 @@ function LinkInner({
2843
3646
  return () => router.unobserveForPrefetch(element);
2844
3647
  }
2845
3648
  }, [resolvedHref, viewport, isExternal]);
2846
- (0, import_react3.useEffect)(() => {
3649
+ (0, import_react5.useEffect)(() => {
2847
3650
  if (!render || isExternal) return;
2848
3651
  let cancelled = false;
2849
3652
  let timeoutId = null;
@@ -2865,7 +3668,7 @@ function LinkInner({
2865
3668
  }
2866
3669
  };
2867
3670
  }, [render, isExternal, resolvedHref]);
2868
- (0, import_react3.useEffect)(() => {
3671
+ (0, import_react5.useEffect)(() => {
2869
3672
  return () => {
2870
3673
  if (intentTimeoutRef.current) {
2871
3674
  clearTimeout(intentTimeoutRef.current);
@@ -2873,13 +3676,13 @@ function LinkInner({
2873
3676
  }
2874
3677
  };
2875
3678
  }, []);
2876
- const cancelIntent = (0, import_react3.useCallback)(() => {
3679
+ const cancelIntent = (0, import_react5.useCallback)(() => {
2877
3680
  if (intentTimeoutRef.current) {
2878
3681
  clearTimeout(intentTimeoutRef.current);
2879
3682
  intentTimeoutRef.current = null;
2880
3683
  }
2881
3684
  }, []);
2882
- const scheduleIntentPrefetch = (0, import_react3.useCallback)(() => {
3685
+ const scheduleIntentPrefetch = (0, import_react5.useCallback)(() => {
2883
3686
  if (isExternal || !intent || hasPrefetched.current) return;
2884
3687
  cancelIntent();
2885
3688
  intentTimeoutRef.current = setTimeout(() => {
@@ -2887,42 +3690,42 @@ function LinkInner({
2887
3690
  doPrefetch();
2888
3691
  }, prefetchDelay);
2889
3692
  }, [intent, isExternal, prefetchDelay, doPrefetch, cancelIntent]);
2890
- const handleMouseEnter = (0, import_react3.useCallback)(
3693
+ const handleMouseEnter = (0, import_react5.useCallback)(
2891
3694
  (event) => {
2892
3695
  onMouseEnter?.(event);
2893
3696
  scheduleIntentPrefetch();
2894
3697
  },
2895
3698
  [onMouseEnter, scheduleIntentPrefetch]
2896
3699
  );
2897
- const handleMouseLeave = (0, import_react3.useCallback)(
3700
+ const handleMouseLeave = (0, import_react5.useCallback)(
2898
3701
  (event) => {
2899
3702
  onMouseLeave?.(event);
2900
3703
  cancelIntent();
2901
3704
  },
2902
3705
  [onMouseLeave, cancelIntent]
2903
3706
  );
2904
- const handleTouchStart = (0, import_react3.useCallback)(
3707
+ const handleTouchStart = (0, import_react5.useCallback)(
2905
3708
  (event) => {
2906
3709
  onTouchStart?.(event);
2907
3710
  scheduleIntentPrefetch();
2908
3711
  },
2909
3712
  [onTouchStart, scheduleIntentPrefetch]
2910
3713
  );
2911
- const handleFocus = (0, import_react3.useCallback)(
3714
+ const handleFocus = (0, import_react5.useCallback)(
2912
3715
  (event) => {
2913
3716
  onFocus?.(event);
2914
3717
  scheduleIntentPrefetch();
2915
3718
  },
2916
3719
  [onFocus, scheduleIntentPrefetch]
2917
3720
  );
2918
- const handleBlur = (0, import_react3.useCallback)(
3721
+ const handleBlur = (0, import_react5.useCallback)(
2919
3722
  (event) => {
2920
3723
  onBlur?.(event);
2921
3724
  cancelIntent();
2922
3725
  },
2923
3726
  [onBlur, cancelIntent]
2924
3727
  );
2925
- const handleClick = (0, import_react3.useCallback)(
3728
+ const handleClick = (0, import_react5.useCallback)(
2926
3729
  (event) => {
2927
3730
  onClick?.(event);
2928
3731
  if (event.defaultPrevented) return;
@@ -2947,7 +3750,7 @@ function LinkInner({
2947
3750
  },
2948
3751
  [resolvedHref, replace, scroll, viewTransition, target, isExternal, onClick]
2949
3752
  );
2950
- const setRefs = (0, import_react3.useCallback)(
3753
+ const setRefs = (0, import_react5.useCallback)(
2951
3754
  (node) => {
2952
3755
  elementRef.current = node;
2953
3756
  if (typeof ref === "function") ref(node);
@@ -2976,7 +3779,7 @@ function LinkInner({
2976
3779
  );
2977
3780
  }
2978
3781
  __name(LinkInner, "LinkInner");
2979
- var LinkWithRef = (0, import_react3.forwardRef)(LinkInner);
3782
+ var LinkWithRef = (0, import_react5.forwardRef)(LinkInner);
2980
3783
  LinkWithRef.displayName = "Link";
2981
3784
  var Link = LinkWithRef;
2982
3785
  function resolveLinkHref(href, params, options) {
@@ -3077,7 +3880,7 @@ function matchManifestRoute(pathname, route2) {
3077
3880
  __name(matchManifestRoute, "matchManifestRoute");
3078
3881
 
3079
3882
  // src/client/router.ts
3080
- var import_react4 = require("react");
3883
+ var import_react6 = require("react");
3081
3884
 
3082
3885
  // src/deferred.ts
3083
3886
  var FARM_DEFERRED_CONTENT_TYPE = "application/x-farm-deferred+json";
@@ -3296,6 +4099,7 @@ __name(createFarmDeploymentMismatchError, "createFarmDeploymentMismatchError");
3296
4099
 
3297
4100
  // src/client/spa-router.ts
3298
4101
  var FARM_PAGE_STATE_KEY = "__farmPageState";
4102
+ var FARM_INTERCEPT_FROM_KEY = "__farmInterceptFrom";
3299
4103
  var IDLE_NAVIGATION_STATE = {
3300
4104
  state: "idle",
3301
4105
  pending: false,
@@ -3366,7 +4170,11 @@ var _SPARouter = class _SPARouter {
3366
4170
  return;
3367
4171
  }
3368
4172
  const from = window.location.pathname + window.location.search;
3369
- if (await this.shouldBlockNavigation({ from, to: fullPath, action: replace ? "replace" : "push" })) {
4173
+ if (await this.shouldBlockNavigation({
4174
+ from,
4175
+ to: fullPath,
4176
+ action: replace ? "replace" : "push"
4177
+ })) {
3370
4178
  return;
3371
4179
  }
3372
4180
  if (this.options.scrollRestoration) {
@@ -3384,11 +4192,11 @@ var _SPARouter = class _SPARouter {
3384
4192
  to: url,
3385
4193
  action: replace ? "replace" : "push"
3386
4194
  });
3387
- const pageData = await this.fetchPageData(fullPath);
4195
+ const pageData = await this.fetchPageData(fullPath, true, from);
3388
4196
  if (clientNavigation) {
3389
4197
  await this.clientPlugins?.markNavigationLoaded(clientNavigation, pageData);
3390
4198
  }
3391
- if (pageData.isClientComponent === false) {
4199
+ if (pageData.isClientComponent === false && !pageData.interception) {
3392
4200
  this.finishNavigation();
3393
4201
  window.location.assign(pageData.canonicalPath || fullPath);
3394
4202
  return;
@@ -3426,11 +4234,12 @@ var _SPARouter = class _SPARouter {
3426
4234
  if (isFarmLocaleChangeHref(href)) return;
3427
4235
  const url = new URL(href, window.location.origin);
3428
4236
  const fullPath = url.pathname + url.search;
4237
+ const interceptFrom = window.location.pathname + window.location.search;
3429
4238
  if (this.prefetchingUrls.has(fullPath)) return;
3430
- if (this.isCached(fullPath)) return;
4239
+ if (this.isCached(fullPath, interceptFrom)) return;
3431
4240
  this.prefetchingUrls.add(fullPath);
3432
4241
  try {
3433
- await this.fetchPageData(fullPath, false);
4242
+ await this.fetchPageData(fullPath, false, interceptFrom);
3434
4243
  } catch (error) {
3435
4244
  console.warn("[Farm.js] Prefetch failed:", href, error);
3436
4245
  } finally {
@@ -3505,7 +4314,12 @@ var _SPARouter = class _SPARouter {
3505
4314
  }
3506
4315
  async commitNavigation(options) {
3507
4316
  const historyPath = options.pageData.canonicalPath || options.fullPath;
3508
- const historyState = createHistoryState(historyPath, options.state);
4317
+ const historyState = createHistoryState(
4318
+ historyPath,
4319
+ options.state,
4320
+ void 0,
4321
+ options.pageData.interception?.from ?? null
4322
+ );
3509
4323
  if (options.replace) {
3510
4324
  window.history.replaceState(historyState, "", historyPath);
3511
4325
  } else {
@@ -3560,15 +4374,17 @@ var _SPARouter = class _SPARouter {
3560
4374
  /**
3561
4375
  * Fetch page data from cache or server
3562
4376
  */
3563
- async fetchPageData(path, recover = true) {
3564
- const cached = this.cache.get(path);
4377
+ async fetchPageData(path, recover = true, interceptFrom) {
4378
+ const cacheKey = getPageDataCacheKey(path, interceptFrom);
4379
+ const cached = this.cache.get(cacheKey);
3565
4380
  if (cached && Date.now() - cached.timestamp < this.options.cacheMaxAge) {
3566
4381
  return cached.data;
3567
4382
  }
3568
4383
  const response = await fetch(`/__farm/page-data?path=${encodeURIComponent(path)}`, {
3569
4384
  headers: createFarmDeploymentRequestHeaders(this.options.deploymentId, {
3570
4385
  Accept: "application/x-farm-deferred+json, application/json",
3571
- "X-Farm-SPA": "1"
4386
+ "X-Farm-SPA": "1",
4387
+ ...interceptFrom ? { "X-Farm-Intercept-From": interceptFrom } : {}
3572
4388
  })
3573
4389
  });
3574
4390
  if (isFarmDeploymentMismatchResponse(response, this.options.deploymentId)) {
@@ -3586,7 +4402,7 @@ var _SPARouter = class _SPARouter {
3586
4402
  throw new Error(`Failed to fetch page data: ${response.status}`);
3587
4403
  }
3588
4404
  const data = await readDeferredDataResponse(response);
3589
- this.cache.set(path, {
4405
+ this.cache.set(cacheKey, {
3590
4406
  data,
3591
4407
  timestamp: Date.now()
3592
4408
  });
@@ -3617,7 +4433,8 @@ var _SPARouter = class _SPARouter {
3617
4433
  to: window.location.href,
3618
4434
  action: "pop"
3619
4435
  });
3620
- const pageData = await this.fetchPageData(path);
4436
+ const interceptFrom = typeof event.state?.[FARM_INTERCEPT_FROM_KEY] === "string" ? event.state[FARM_INTERCEPT_FROM_KEY] : void 0;
4437
+ const pageData = await this.fetchPageData(path, true, interceptFrom);
3621
4438
  if (clientNavigation) {
3622
4439
  await this.clientPlugins?.markNavigationLoaded(clientNavigation, pageData);
3623
4440
  }
@@ -3648,8 +4465,8 @@ var _SPARouter = class _SPARouter {
3648
4465
  /**
3649
4466
  * Check if URL is cached
3650
4467
  */
3651
- isCached(path) {
3652
- const cached = this.cache.get(path);
4468
+ isCached(path, interceptFrom) {
4469
+ const cached = this.cache.get(getPageDataCacheKey(path, interceptFrom));
3653
4470
  return cached !== void 0 && Date.now() - cached.timestamp < this.options.cacheMaxAge;
3654
4471
  }
3655
4472
  /**
@@ -3805,15 +4622,26 @@ function readPageState() {
3805
4622
  return state[FARM_PAGE_STATE_KEY] ?? null;
3806
4623
  }
3807
4624
  __name(readPageState, "readPageState");
3808
- function createHistoryState(path, pageState, currentState) {
4625
+ function createHistoryState(path, pageState, currentState, interceptFrom) {
3809
4626
  const base = currentState && typeof currentState === "object" ? { ...currentState } : {};
3810
- return {
4627
+ const nextState = {
3811
4628
  ...base,
3812
4629
  path,
3813
4630
  [FARM_PAGE_STATE_KEY]: pageState
3814
4631
  };
4632
+ if (interceptFrom === null) {
4633
+ delete nextState[FARM_INTERCEPT_FROM_KEY];
4634
+ } else if (interceptFrom) {
4635
+ nextState[FARM_INTERCEPT_FROM_KEY] = interceptFrom;
4636
+ }
4637
+ return nextState;
3815
4638
  }
3816
4639
  __name(createHistoryState, "createHistoryState");
4640
+ function getPageDataCacheKey(path, interceptFrom) {
4641
+ return interceptFrom ? `${path}
4642
+ intercept:${interceptFrom}` : path;
4643
+ }
4644
+ __name(getPageDataCacheKey, "getPageDataCacheKey");
3817
4645
  function createNavigationLocation(url) {
3818
4646
  return {
3819
4647
  href: url.href,
@@ -3833,14 +4661,14 @@ function useRouter(options = {}) {
3833
4661
  const basePath = options.basePath || "";
3834
4662
  const routes = options.routes;
3835
4663
  const routeKey = routes?.map((route2) => typeof route2 === "string" ? route2 : route2.path).join("\n") || "";
3836
- const routeMatcher = (0, import_react4.useMemo)(
4664
+ const routeMatcher = (0, import_react6.useMemo)(
3837
4665
  () => routeKey ? createFarmRouter(routeKey.split("\n")) : null,
3838
4666
  [routeKey]
3839
4667
  );
3840
- const [state, setState] = (0, import_react4.useState)(() => {
4668
+ const [state, setState] = (0, import_react6.useState)(() => {
3841
4669
  return readRouterState(basePath, routeMatcher);
3842
4670
  });
3843
- (0, import_react4.useEffect)(() => {
4671
+ (0, import_react6.useEffect)(() => {
3844
4672
  if (typeof window === "undefined") return;
3845
4673
  const handlePopState = /* @__PURE__ */ __name(() => {
3846
4674
  setState(readRouterState(basePath, routeMatcher));
@@ -3891,8 +4719,8 @@ function useRouter(options = {}) {
3891
4719
  }
3892
4720
  __name(useRouter, "useRouter");
3893
4721
  function usePageState() {
3894
- const [state, setState] = (0, import_react4.useState)(() => readPageState());
3895
- (0, import_react4.useEffect)(() => {
4722
+ const [state, setState] = (0, import_react6.useState)(() => readPageState());
4723
+ (0, import_react6.useEffect)(() => {
3896
4724
  if (typeof window === "undefined") return;
3897
4725
  const handlePopState = /* @__PURE__ */ __name(() => {
3898
4726
  setState(readPageState());
@@ -3905,10 +4733,10 @@ function usePageState() {
3905
4733
  }
3906
4734
  __name(usePageState, "usePageState");
3907
4735
  function useNavigation() {
3908
- const [state, setState] = (0, import_react4.useState)(
4736
+ const [state, setState] = (0, import_react6.useState)(
3909
4737
  () => getRouter2().getNavigationState()
3910
4738
  );
3911
- (0, import_react4.useEffect)(() => {
4739
+ (0, import_react6.useEffect)(() => {
3912
4740
  if (typeof window === "undefined") return;
3913
4741
  return getRouter2().subscribeNavigation(setState);
3914
4742
  }, []);
@@ -3916,10 +4744,10 @@ function useNavigation() {
3916
4744
  }
3917
4745
  __name(useNavigation, "useNavigation");
3918
4746
  function useBlocker(options) {
3919
- const optionsRef = (0, import_react4.useRef)(options);
4747
+ const optionsRef = (0, import_react6.useRef)(options);
3920
4748
  optionsRef.current = options;
3921
4749
  const active = Boolean(options.when);
3922
- (0, import_react4.useEffect)(() => {
4750
+ (0, import_react6.useEffect)(() => {
3923
4751
  if (typeof window === "undefined" || !active) {
3924
4752
  return;
3925
4753
  }
@@ -3940,8 +4768,8 @@ function useBlocker(options) {
3940
4768
  }
3941
4769
  __name(useBlocker, "useBlocker");
3942
4770
  function useScrollRestoration(key) {
3943
- const ref = (0, import_react4.useRef)(null);
3944
- (0, import_react4.useEffect)(() => {
4771
+ const ref = (0, import_react6.useRef)(null);
4772
+ (0, import_react6.useEffect)(() => {
3945
4773
  const element = ref.current;
3946
4774
  if (!element) return;
3947
4775
  return getRouter2().registerScrollElement(key, element);
@@ -4530,6 +5358,7 @@ function isObject(value) {
4530
5358
  __name(isObject, "isObject");
4531
5359
  // Annotate the CommonJS export names for ESM import in node:
4532
5360
  0 && (module.exports = {
5361
+ APIClientError,
4533
5362
  FarmClientPluginManager,
4534
5363
  IntegrationClientError,
4535
5364
  Link,
@@ -4562,6 +5391,8 @@ __name(isObject, "isObject");
4562
5391
  readPageState,
4563
5392
  replaceState,
4564
5393
  useBlocker,
5394
+ useFetcher,
5395
+ useMutation,
4565
5396
  useNavigation,
4566
5397
  usePageState,
4567
5398
  useRouter,