@farm.js/core 0.1.0-beta.97 → 0.1.0-beta.99

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 (269) hide show
  1. package/dist/after.cjs.map +1 -0
  2. package/dist/agent-runtime.cjs +7 -3
  3. package/dist/agent-runtime.cjs.map +1 -0
  4. package/dist/agent-runtime.d.mts +2 -2
  5. package/dist/agent-runtime.d.ts +2 -2
  6. package/dist/agent-runtime.mjs +2 -1
  7. package/dist/agent-runtime.mjs.map +1 -1
  8. package/dist/api/route-manager.cjs.map +1 -0
  9. package/dist/api/route-manager.d.mts +2 -2
  10. package/dist/api/route-manager.d.ts +2 -2
  11. package/dist/api/runtime.cjs.map +1 -0
  12. package/dist/api.cjs +26 -1
  13. package/dist/api.cjs.map +1 -0
  14. package/dist/api.d.mts +3 -3
  15. package/dist/api.d.ts +3 -3
  16. package/dist/api.mjs +2 -2
  17. package/dist/{app-CIOx3LvY.d.ts → app-CGjrKNoN.d.ts} +4 -4
  18. package/dist/{app-DBd6437H.d.mts → app-DOcLs_iq.d.mts} +4 -4
  19. package/dist/app-markdown.cjs +21 -0
  20. package/dist/app-markdown.cjs.map +1 -0
  21. package/dist/app-markdown.d.mts +19 -4
  22. package/dist/app-markdown.d.ts +19 -4
  23. package/dist/app-markdown.mjs +7 -1
  24. package/dist/cache.cjs.map +1 -0
  25. package/dist/{chunk-VMCHLH3Y.mjs → chunk-26BCDVJS.mjs} +19 -1
  26. package/dist/chunk-26BCDVJS.mjs.map +1 -0
  27. package/dist/{chunk-FP5Q67FO.mjs → chunk-2HYYZCCI.mjs} +308 -19
  28. package/dist/chunk-2HYYZCCI.mjs.map +1 -0
  29. package/dist/{chunk-WKDAA6V2.mjs → chunk-3BTAMIQE.mjs} +100 -41
  30. package/dist/chunk-3BTAMIQE.mjs.map +1 -0
  31. package/dist/{chunk-WD2EBM3I.mjs → chunk-3CQJV3NC.mjs} +2 -2
  32. package/dist/{chunk-I3OOUWO5.mjs → chunk-3GDRV7SG.mjs} +2 -2
  33. package/dist/{chunk-YIBV77KZ.mjs → chunk-4WLAVHCY.mjs} +2 -2
  34. package/dist/{chunk-KDP5ZSWP.mjs → chunk-7PS5NKOM.mjs} +26 -11
  35. package/dist/chunk-7PS5NKOM.mjs.map +1 -0
  36. package/dist/{chunk-X4SFHKFH.mjs → chunk-BU65A3LZ.mjs} +10 -6
  37. package/dist/chunk-BU65A3LZ.mjs.map +1 -0
  38. package/dist/{chunk-WMJGA32P.mjs → chunk-BXVOHG6B.mjs} +27 -2
  39. package/dist/{chunk-WMJGA32P.mjs.map → chunk-BXVOHG6B.mjs.map} +1 -1
  40. package/dist/{chunk-BZAIS65C.mjs → chunk-DIHNOVZZ.mjs} +40 -13
  41. package/dist/chunk-DIHNOVZZ.mjs.map +1 -0
  42. package/dist/{chunk-ISSFLGJ5.mjs → chunk-FDWECN3W.mjs} +8 -4
  43. package/dist/chunk-FDWECN3W.mjs.map +1 -0
  44. package/dist/chunk-FDWXXTWR.mjs +114 -0
  45. package/dist/chunk-FDWXXTWR.mjs.map +1 -0
  46. package/dist/{chunk-FSIXEIOB.mjs → chunk-L7FSTPGS.mjs} +2 -2
  47. package/dist/{chunk-J3MPQ4MQ.mjs → chunk-LG4XB6RN.mjs} +2 -2
  48. package/dist/{chunk-J3MPQ4MQ.mjs.map → chunk-LG4XB6RN.mjs.map} +1 -1
  49. package/dist/{chunk-AAC66ILR.mjs → chunk-LXXG3K2Y.mjs} +61 -2
  50. package/dist/chunk-LXXG3K2Y.mjs.map +1 -0
  51. package/dist/{chunk-GJ7W6LNK.mjs → chunk-N5RK4LK6.mjs} +3 -3
  52. package/dist/{chunk-D7F7YZ7L.mjs → chunk-NCVFPT67.mjs} +100 -40
  53. package/dist/{chunk-D7F7YZ7L.mjs.map → chunk-NCVFPT67.mjs.map} +1 -1
  54. package/dist/{chunk-UMUWR5XA.mjs → chunk-PURU7FXR.mjs} +2 -2
  55. package/dist/{chunk-2SKCXYLB.mjs → chunk-QQCCBMTH.mjs} +60 -6
  56. package/dist/chunk-QQCCBMTH.mjs.map +1 -0
  57. package/dist/chunk-RKUPOYQV.mjs +28 -0
  58. package/dist/chunk-RKUPOYQV.mjs.map +1 -0
  59. package/dist/{chunk-52BR7LTX.mjs → chunk-STSPSJUI.mjs} +3 -3
  60. package/dist/{chunk-EPDIF432.mjs → chunk-UOGX6UNO.mjs} +37 -17
  61. package/dist/chunk-UOGX6UNO.mjs.map +1 -0
  62. package/dist/{chunk-IZBX2TEU.mjs → chunk-VQG3QWWK.mjs} +2 -2
  63. package/dist/{chunk-35NPLKQJ.mjs → chunk-XWM2C267.mjs} +21 -105
  64. package/dist/chunk-XWM2C267.mjs.map +1 -0
  65. package/dist/{chunk-7ZVN3YZI.mjs → chunk-Z5ARESQG.mjs} +43 -1
  66. package/dist/chunk-Z5ARESQG.mjs.map +1 -0
  67. package/dist/client/lifecycle.cjs.map +1 -0
  68. package/dist/client/lifecycle.mjs +2 -2
  69. package/dist/{client-plugins-B6-07YmP.d.mts → client-plugins-BqtHoJ9C.d.mts} +91 -3
  70. package/dist/{client-plugins-By6MVUX-.d.ts → client-plugins-DYprkCKN.d.ts} +91 -3
  71. package/dist/client-plugins.cjs +1 -1
  72. package/dist/client-plugins.cjs.map +1 -0
  73. package/dist/client-plugins.d.mts +2 -2
  74. package/dist/client-plugins.d.ts +2 -2
  75. package/dist/client-plugins.mjs +2 -2
  76. package/dist/client.cjs +61 -16
  77. package/dist/client.cjs.map +1 -0
  78. package/dist/client.d.mts +5 -5
  79. package/dist/client.d.ts +5 -5
  80. package/dist/client.mjs +7 -7
  81. package/dist/config.cjs.map +1 -0
  82. package/dist/config.d.mts +2 -2
  83. package/dist/config.d.ts +2 -2
  84. package/dist/cron.cjs +48 -12
  85. package/dist/cron.cjs.map +1 -0
  86. package/dist/cron.mjs +2 -1
  87. package/dist/deferred.cjs.map +1 -0
  88. package/dist/deployment.cjs.map +1 -0
  89. package/dist/{devtools-XWZX6CCL.mjs → devtools-24N3U5IG.mjs} +9 -7
  90. package/dist/{devtools-XWZX6CCL.mjs.map → devtools-24N3U5IG.mjs.map} +1 -1
  91. package/dist/docs.cjs +7 -3
  92. package/dist/docs.cjs.map +1 -0
  93. package/dist/docs.mjs +9 -7
  94. package/dist/env-types.cjs.map +1 -0
  95. package/dist/env.cjs.map +1 -0
  96. package/dist/environment/vite.cjs.map +1 -0
  97. package/dist/environment.cjs.map +1 -0
  98. package/dist/font.cjs.map +1 -0
  99. package/dist/headers.cjs.map +1 -0
  100. package/dist/i18n/client.cjs.map +1 -0
  101. package/dist/i18n/index.cjs.map +1 -0
  102. package/dist/i18n/server.cjs.map +1 -0
  103. package/dist/image/server.cjs +99 -40
  104. package/dist/image/server.cjs.map +1 -0
  105. package/dist/image/server.d.mts +6 -0
  106. package/dist/image/server.d.ts +6 -0
  107. package/dist/image/server.mjs +1 -1
  108. package/dist/image/sharp.cjs +1 -0
  109. package/dist/image/sharp.cjs.map +1 -0
  110. package/dist/image/sharp.d.mts +1 -1
  111. package/dist/image/sharp.d.ts +1 -1
  112. package/dist/image/sharp.mjs +1 -1
  113. package/dist/image/sharp.mjs.map +1 -1
  114. package/dist/image.cjs.map +1 -0
  115. package/dist/index.cjs +943 -231
  116. package/dist/index.cjs.map +1 -0
  117. package/dist/index.d.mts +11 -11
  118. package/dist/index.d.ts +11 -11
  119. package/dist/index.mjs +38 -24
  120. package/dist/index.mjs.map +1 -1
  121. package/dist/instrumentation.cjs.map +1 -0
  122. package/dist/{integration-orm-LLW6WZNK.mjs → integration-orm-G5BPBT5X.mjs} +3 -3
  123. package/dist/integrations.cjs +168 -5
  124. package/dist/integrations.cjs.map +1 -0
  125. package/dist/integrations.d.mts +2 -2
  126. package/dist/integrations.d.ts +2 -2
  127. package/dist/integrations.mjs +10 -3
  128. package/dist/internal/build-runtime.cjs +745 -139
  129. package/dist/internal/build-runtime.cjs.map +1 -0
  130. package/dist/internal/build-runtime.d.mts +4 -4
  131. package/dist/internal/build-runtime.d.ts +4 -4
  132. package/dist/internal/build-runtime.mjs +16 -14
  133. package/dist/internal/client-runtime.cjs +7 -2
  134. package/dist/internal/client-runtime.cjs.map +1 -0
  135. package/dist/internal/client-runtime.mjs +8 -3
  136. package/dist/internal/client-runtime.mjs.map +1 -1
  137. package/dist/internal/config-runtime.cjs +59 -42
  138. package/dist/internal/config-runtime.cjs.map +1 -0
  139. package/dist/internal/config-runtime.d.mts +3 -3
  140. package/dist/internal/config-runtime.d.ts +3 -3
  141. package/dist/internal/config-runtime.mjs +8 -6
  142. package/dist/internal/isolated-boundary.cjs.map +1 -0
  143. package/dist/internal/metadata-image-runtime.cjs.map +1 -0
  144. package/dist/internal/product-telemetry-runtime.cjs +52 -5
  145. package/dist/internal/product-telemetry-runtime.cjs.map +1 -0
  146. package/dist/internal/product-telemetry-runtime.d.mts +12 -1
  147. package/dist/internal/product-telemetry-runtime.d.ts +12 -1
  148. package/dist/internal/product-telemetry-runtime.mjs +50 -5
  149. package/dist/internal/product-telemetry-runtime.mjs.map +1 -1
  150. package/dist/internal/production-node-env.cjs.map +1 -0
  151. package/dist/internal/production-runtime.cjs +56 -10
  152. package/dist/internal/production-runtime.cjs.map +1 -0
  153. package/dist/internal/production-runtime.d.mts +8 -8
  154. package/dist/internal/production-runtime.d.ts +8 -8
  155. package/dist/internal/production-runtime.mjs +2 -2
  156. package/dist/markdown.cjs.map +1 -0
  157. package/dist/{metadata-route-8_V3UonT.d.ts → metadata-route-D2be_0kC.d.ts} +17 -2
  158. package/dist/{metadata-route-CWP_HjGU.d.mts → metadata-route-Dw5CrDIZ.d.mts} +17 -2
  159. package/dist/middleware.cjs.map +1 -0
  160. package/dist/middleware.d.mts +4 -4
  161. package/dist/middleware.d.ts +4 -4
  162. package/dist/navigation.cjs.map +1 -0
  163. package/dist/{nitro-EGRNOB5P.mjs → nitro-YJEP3FWM.mjs} +4 -3
  164. package/dist/{nitro-EGRNOB5P.mjs.map → nitro-YJEP3FWM.mjs.map} +1 -1
  165. package/dist/observability.cjs.map +1 -0
  166. package/dist/plugin.cjs +599 -104
  167. package/dist/plugin.cjs.map +1 -0
  168. package/dist/plugin.d.mts +2 -2
  169. package/dist/plugin.d.ts +2 -2
  170. package/dist/plugin.mjs +13 -11
  171. package/dist/{production-runtime-CdmzsDax.d.ts → production-runtime-CTqpD1C8.d.ts} +2 -2
  172. package/dist/{production-runtime-BGRH1dKG.d.mts → production-runtime-HNqNZd4D.d.mts} +2 -2
  173. package/dist/query/client.cjs.map +1 -0
  174. package/dist/query/index.cjs.map +1 -0
  175. package/dist/query/parsers.cjs.map +1 -0
  176. package/dist/query/server.cjs.map +1 -0
  177. package/dist/{render-plan-DLxJYAA5.d.mts → render-plan-COscpW-i.d.mts} +1 -1
  178. package/dist/{render-plan-0GeHiGVZ.d.ts → render-plan-Z4kSxaT_.d.ts} +1 -1
  179. package/dist/renderer/react/client.cjs.map +1 -0
  180. package/dist/renderer/react/server.cjs.map +1 -0
  181. package/dist/renderer/react/vite.cjs.map +1 -0
  182. package/dist/renderer-client.cjs +26 -1
  183. package/dist/renderer-client.cjs.map +1 -0
  184. package/dist/renderer-client.d.mts +4 -4
  185. package/dist/renderer-client.d.ts +4 -4
  186. package/dist/renderer-client.mjs +2 -2
  187. package/dist/renderer.cjs.map +1 -0
  188. package/dist/{request-BstWbZ6O.d.ts → request-BEDb2jE9.d.ts} +1 -1
  189. package/dist/{request-BCNXSXLh.d.mts → request-CbIAqJY9.d.mts} +1 -1
  190. package/dist/request.cjs.map +1 -0
  191. package/dist/request.d.mts +3 -3
  192. package/dist/request.d.ts +3 -3
  193. package/dist/router.cjs.map +1 -0
  194. package/dist/routes.cjs.map +1 -0
  195. package/dist/routes.d.mts +2 -2
  196. package/dist/routes.d.ts +2 -2
  197. package/dist/server-action-security.cjs +117 -104
  198. package/dist/server-action-security.cjs.map +1 -0
  199. package/dist/server-action-security.mjs +2 -1
  200. package/dist/server-fn-client.cjs.map +1 -0
  201. package/dist/server-fn.cjs.map +1 -0
  202. package/dist/server-fn.mjs +3 -2
  203. package/dist/server-plugins.cjs.map +1 -0
  204. package/dist/server-plugins.d.mts +3 -3
  205. package/dist/server-plugins.d.ts +3 -3
  206. package/dist/server-query-client.cjs +26 -1
  207. package/dist/server-query-client.cjs.map +1 -0
  208. package/dist/server-query-client.mjs +3 -3
  209. package/dist/server-query.cjs.map +1 -0
  210. package/dist/server-query.mjs +4 -3
  211. package/dist/server.cjs +686 -170
  212. package/dist/server.cjs.map +1 -0
  213. package/dist/server.d.mts +8 -8
  214. package/dist/server.d.ts +8 -8
  215. package/dist/server.mjs +16 -14
  216. package/dist/server.mjs.map +1 -1
  217. package/dist/{spa-router-COgu4txb.d.ts → spa-router-CjeQites.d.ts} +1 -1
  218. package/dist/{spa-router-BeyAVSc6.d.mts → spa-router-FJbNou1a.d.mts} +1 -1
  219. package/dist/storage.cjs +7 -3
  220. package/dist/storage.cjs.map +1 -0
  221. package/dist/storage.mjs +1 -1
  222. package/dist/testing.cjs.map +1 -0
  223. package/dist/testing.d.mts +2 -2
  224. package/dist/testing.d.ts +2 -2
  225. package/dist/testing.mjs +2 -1
  226. package/dist/testing.mjs.map +1 -1
  227. package/dist/theme/client.cjs.map +1 -0
  228. package/dist/theme/index.cjs.map +1 -0
  229. package/dist/theme/runtime.cjs.map +1 -0
  230. package/dist/theme/server.cjs.map +1 -0
  231. package/dist/{utils-DL2VwDQl.d.mts → utils-BiMJv9Ys.d.mts} +1 -1
  232. package/dist/{utils-DkU2c_1B.d.ts → utils-BpnKRFv9.d.ts} +1 -1
  233. package/dist/version.cjs +1 -1
  234. package/dist/version.cjs.map +1 -0
  235. package/dist/version.mjs +1 -1
  236. package/dist/vite.cjs +599 -104
  237. package/dist/vite.cjs.map +1 -0
  238. package/dist/vite.d.mts +2 -2
  239. package/dist/vite.d.ts +2 -2
  240. package/dist/vite.mjs +13 -11
  241. package/dist/{workflows-ChyK_oTu.d.mts → workflows-BantWytQ.d.mts} +7 -0
  242. package/dist/{workflows-ChyK_oTu.d.ts → workflows-BantWytQ.d.ts} +7 -0
  243. package/dist/workflows.cjs +73 -5
  244. package/dist/workflows.cjs.map +1 -0
  245. package/dist/workflows.d.mts +1 -1
  246. package/dist/workflows.d.ts +1 -1
  247. package/dist/workflows.mjs +2 -1
  248. package/package.json +17 -17
  249. package/dist/chunk-2SKCXYLB.mjs.map +0 -1
  250. package/dist/chunk-35NPLKQJ.mjs.map +0 -1
  251. package/dist/chunk-7ZVN3YZI.mjs.map +0 -1
  252. package/dist/chunk-AAC66ILR.mjs.map +0 -1
  253. package/dist/chunk-BZAIS65C.mjs.map +0 -1
  254. package/dist/chunk-EPDIF432.mjs.map +0 -1
  255. package/dist/chunk-FP5Q67FO.mjs.map +0 -1
  256. package/dist/chunk-ISSFLGJ5.mjs.map +0 -1
  257. package/dist/chunk-KDP5ZSWP.mjs.map +0 -1
  258. package/dist/chunk-VMCHLH3Y.mjs.map +0 -1
  259. package/dist/chunk-WKDAA6V2.mjs.map +0 -1
  260. package/dist/chunk-X4SFHKFH.mjs.map +0 -1
  261. /package/dist/{chunk-WD2EBM3I.mjs.map → chunk-3CQJV3NC.mjs.map} +0 -0
  262. /package/dist/{chunk-I3OOUWO5.mjs.map → chunk-3GDRV7SG.mjs.map} +0 -0
  263. /package/dist/{chunk-YIBV77KZ.mjs.map → chunk-4WLAVHCY.mjs.map} +0 -0
  264. /package/dist/{chunk-FSIXEIOB.mjs.map → chunk-L7FSTPGS.mjs.map} +0 -0
  265. /package/dist/{chunk-GJ7W6LNK.mjs.map → chunk-N5RK4LK6.mjs.map} +0 -0
  266. /package/dist/{chunk-UMUWR5XA.mjs.map → chunk-PURU7FXR.mjs.map} +0 -0
  267. /package/dist/{chunk-52BR7LTX.mjs.map → chunk-STSPSJUI.mjs.map} +0 -0
  268. /package/dist/{chunk-IZBX2TEU.mjs.map → chunk-VQG3QWWK.mjs.map} +0 -0
  269. /package/dist/{integration-orm-LLW6WZNK.mjs.map → integration-orm-G5BPBT5X.mjs.map} +0 -0
package/dist/index.cjs CHANGED
@@ -917,9 +917,13 @@ function getStorage(namespace) {
917
917
  const namespaced = (0, import_unstorage.prefixStorage)(globalStorage, base);
918
918
  return {
919
919
  ...namespaced,
920
- async clear() {
921
- const keys = await namespaced.getKeys();
922
- await Promise.all(keys.map((key) => namespaced.removeItem(key)));
920
+ // Scope the wipe to the namespace instead of the whole storage, and keep
921
+ // the caller's base so `clear("sessions")` cannot take unrelated keys with
922
+ // it. Dropping the argument silently widens a targeted clear into a
923
+ // namespace-wide delete.
924
+ async clear(base2, opts) {
925
+ const keys = await namespaced.getKeys(base2);
926
+ await Promise.all(keys.map((key) => namespaced.removeItem(key, opts)));
923
927
  }
924
928
  };
925
929
  }
@@ -1359,6 +1363,170 @@ var init_route_pattern = __esm({
1359
1363
  }
1360
1364
  });
1361
1365
 
1366
+ // src/request-origin.ts
1367
+ function getRequestSourceOrigin(request) {
1368
+ const origin = request.headers.get("origin")?.trim();
1369
+ if (origin) {
1370
+ return parseSourceOrigin(origin);
1371
+ }
1372
+ const referer = request.headers.get("referer")?.trim();
1373
+ if (referer) {
1374
+ return parseSourceOrigin(referer);
1375
+ }
1376
+ return { ok: true, origin: null };
1377
+ }
1378
+ function parseSourceOrigin(value) {
1379
+ if (value === "null") {
1380
+ return { ok: false, reason: "opaque-origin" };
1381
+ }
1382
+ try {
1383
+ const parsed = new URL(value);
1384
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
1385
+ return { ok: false, reason: "invalid-origin" };
1386
+ }
1387
+ return { ok: true, origin: parsed.origin };
1388
+ } catch {
1389
+ return { ok: false, reason: "invalid-origin" };
1390
+ }
1391
+ }
1392
+ function matchesHostHeader(sourceOrigin, request) {
1393
+ const host = request.headers.get("host")?.trim().toLowerCase();
1394
+ if (!host) return false;
1395
+ try {
1396
+ const source = new URL(sourceOrigin);
1397
+ const target = new URL(request.url);
1398
+ return source.protocol === target.protocol && source.host.toLowerCase() === host;
1399
+ } catch {
1400
+ return false;
1401
+ }
1402
+ }
1403
+ function matchesAllowedOrigin(sourceOrigin, pattern) {
1404
+ const source = new URL(sourceOrigin);
1405
+ if (!pattern.includes("*")) {
1406
+ return pattern.includes("://") ? source.origin === pattern : source.host === pattern;
1407
+ }
1408
+ const schemeEnd = pattern.indexOf("://");
1409
+ const scheme = schemeEnd === -1 ? null : pattern.slice(0, schemeEnd + 1);
1410
+ const hostPattern = pattern.slice(schemeEnd === -1 ? 0 : schemeEnd + 3);
1411
+ const [wildcardHost, port] = splitHostAndPort(hostPattern);
1412
+ const baseHost = wildcardHost.slice(2);
1413
+ if (scheme && source.protocol !== scheme) return false;
1414
+ if (port && getEffectivePort(source) !== port) return false;
1415
+ if (!port && source.port) return false;
1416
+ return source.hostname.endsWith(`.${baseHost}`) && source.hostname !== baseHost;
1417
+ }
1418
+ function normalizeAllowedOriginPattern(value, label) {
1419
+ const pattern = value.trim().toLowerCase();
1420
+ if (!pattern) {
1421
+ throw new TypeError(`${label} cannot contain empty values`);
1422
+ }
1423
+ if (pattern.includes("*")) {
1424
+ if (!/^(?:https?:\/\/)?\*\.[a-z0-9.-]+(?::\d+)?$/.test(pattern)) {
1425
+ throw new TypeError(`Invalid ${label} pattern: ${JSON.stringify(value)}`);
1426
+ }
1427
+ return pattern;
1428
+ }
1429
+ if (pattern.includes("://")) {
1430
+ let parsed;
1431
+ try {
1432
+ parsed = new URL(pattern);
1433
+ } catch {
1434
+ throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);
1435
+ }
1436
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
1437
+ throw new TypeError(`${label} must contain origins without paths: ${JSON.stringify(value)}`);
1438
+ }
1439
+ return parsed.origin;
1440
+ }
1441
+ if (/[/@?#]/.test(pattern)) {
1442
+ throw new TypeError(`${label} must contain origins or hosts: ${JSON.stringify(value)}`);
1443
+ }
1444
+ try {
1445
+ return new URL(`http://${pattern}`).host;
1446
+ } catch {
1447
+ throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);
1448
+ }
1449
+ }
1450
+ function splitHostAndPort(value) {
1451
+ const separator = value.lastIndexOf(":");
1452
+ if (separator === -1) return [value, null];
1453
+ return [value.slice(0, separator), value.slice(separator + 1)];
1454
+ }
1455
+ function getEffectivePort(url) {
1456
+ if (url.port) return url.port;
1457
+ if (url.protocol === "https:") return "443";
1458
+ if (url.protocol === "http:") return "80";
1459
+ return "";
1460
+ }
1461
+ var init_request_origin = __esm({
1462
+ "src/request-origin.ts"() {
1463
+ "use strict";
1464
+ __name(getRequestSourceOrigin, "getRequestSourceOrigin");
1465
+ __name(parseSourceOrigin, "parseSourceOrigin");
1466
+ __name(matchesHostHeader, "matchesHostHeader");
1467
+ __name(matchesAllowedOrigin, "matchesAllowedOrigin");
1468
+ __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
1469
+ __name(splitHostAndPort, "splitHostAndPort");
1470
+ __name(getEffectivePort, "getEffectivePort");
1471
+ }
1472
+ });
1473
+
1474
+ // src/integration-request-security.ts
1475
+ function resolveIntegrationAllowedOrigins(values, label) {
1476
+ return Object.freeze((values ?? []).map((value) => normalizeAllowedOriginPattern(value, label)));
1477
+ }
1478
+ function validateIntegrationRequestOrigin(request, policy = {}) {
1479
+ const allowedOrigins = policy.allowedOrigins ?? [];
1480
+ const requireOriginMetadata = policy.requireOriginMetadata ?? true;
1481
+ const fetchSite = request.headers.get("sec-fetch-site")?.trim().toLowerCase();
1482
+ const source = getRequestSourceOrigin(request);
1483
+ if (!source.ok) {
1484
+ return { ok: false, reason: source.reason };
1485
+ }
1486
+ if (source.origin === null) {
1487
+ if (fetchSite === "same-origin") {
1488
+ return { ok: true };
1489
+ }
1490
+ if (requireOriginMetadata) {
1491
+ return { ok: false, reason: fetchSite === "cross-site" ? "cross-site" : "missing-origin" };
1492
+ }
1493
+ return fetchSite === "cross-site" ? { ok: false, reason: "cross-site" } : { ok: true };
1494
+ }
1495
+ const requestUrl = new URL(request.url);
1496
+ const matchesConfiguredOrigin = allowedOrigins.some(
1497
+ (pattern) => matchesAllowedOrigin(source.origin, pattern)
1498
+ );
1499
+ const matchesRequest = source.origin === requestUrl.origin || matchesHostHeader(source.origin, request);
1500
+ if (!matchesRequest && !matchesConfiguredOrigin) {
1501
+ return { ok: false, reason: "cross-site" };
1502
+ }
1503
+ if (fetchSite === "cross-site" && !matchesConfiguredOrigin) {
1504
+ return { ok: false, reason: "cross-site" };
1505
+ }
1506
+ return { ok: true };
1507
+ }
1508
+ function describeIntegrationOriginRejection(reason) {
1509
+ switch (reason) {
1510
+ case "missing-origin":
1511
+ return "Request is missing same-origin metadata.";
1512
+ case "opaque-origin":
1513
+ return "Opaque origins are not allowed.";
1514
+ case "invalid-origin":
1515
+ return "Invalid request origin.";
1516
+ case "cross-site":
1517
+ return "Cross-site request was rejected.";
1518
+ }
1519
+ }
1520
+ var init_integration_request_security = __esm({
1521
+ "src/integration-request-security.ts"() {
1522
+ "use strict";
1523
+ init_request_origin();
1524
+ __name(resolveIntegrationAllowedOrigins, "resolveIntegrationAllowedOrigins");
1525
+ __name(validateIntegrationRequestOrigin, "validateIntegrationRequestOrigin");
1526
+ __name(describeIntegrationOriginRejection, "describeIntegrationOriginRejection");
1527
+ }
1528
+ });
1529
+
1362
1530
  // src/integration-api.ts
1363
1531
  function defineIntegrationAPIOperation(operation2) {
1364
1532
  return {
@@ -4195,6 +4363,7 @@ var init_integrations = __esm({
4195
4363
  "src/integrations.ts"() {
4196
4364
  "use strict";
4197
4365
  init_route_pattern();
4366
+ init_integration_request_security();
4198
4367
  init_integration_api();
4199
4368
  init_plugin_integration_context();
4200
4369
  init_decode();
@@ -4660,6 +4829,29 @@ var init_markdown = __esm({
4660
4829
  }
4661
4830
  });
4662
4831
 
4832
+ // src/utils/runtime-env.ts
4833
+ function getFarmRuntimeBindings() {
4834
+ const runtimeBindings = globalThis.__env__;
4835
+ return runtimeBindings && typeof runtimeBindings === "object" ? runtimeBindings : void 0;
4836
+ }
4837
+ function readFarmEnvironmentValue(name2) {
4838
+ const runtimeValue = getFarmRuntimeBindings()?.[name2];
4839
+ if (typeof runtimeValue === "string") return runtimeValue;
4840
+ return typeof process !== "undefined" ? process.env?.[name2] : void 0;
4841
+ }
4842
+ function isFarmDeployedRuntime() {
4843
+ if (getFarmRuntimeBindings()) return true;
4844
+ return readFarmEnvironmentValue("NODE_ENV") === "production";
4845
+ }
4846
+ var init_runtime_env = __esm({
4847
+ "src/utils/runtime-env.ts"() {
4848
+ "use strict";
4849
+ __name(getFarmRuntimeBindings, "getFarmRuntimeBindings");
4850
+ __name(readFarmEnvironmentValue, "readFarmEnvironmentValue");
4851
+ __name(isFarmDeployedRuntime, "isFarmDeployedRuntime");
4852
+ }
4853
+ });
4854
+
4663
4855
  // src/workflows.ts
4664
4856
  var workflows_exports = {};
4665
4857
  __export(workflows_exports, {
@@ -4708,7 +4900,8 @@ function resolveWorkflowsConfig(workflows) {
4708
4900
  dirs,
4709
4901
  route: normalizeWorkflowRoute(options.route || DEFAULT_FARM_WORKFLOW_ROUTE),
4710
4902
  secretEnv: options.secretEnv || DEFAULT_FARM_WORKFLOW_SECRET_ENV,
4711
- secret: options.secret
4903
+ secret: options.secret,
4904
+ allowUnsecured: options.allowUnsecured === true
4712
4905
  };
4713
4906
  }
4714
4907
  async function discoverFarmWorkflows(config, options = {}) {
@@ -4834,8 +5027,11 @@ async function prepareFarmWorkflowsForNitro(config) {
4834
5027
  await fs16.mkdir(generatedDir, { recursive: true });
4835
5028
  const tasks = {};
4836
5029
  const scheduledTasks = createScheduledTasks(workflows);
5030
+ const wrapperNames = resolveWorkflowWrapperFileNames(workflows.map((workflow) => workflow.id));
4837
5031
  for (const workflow of workflows) {
4838
- const wrapperPath = toPosixPath(path43.join(generatedDir, `${safeFileName(workflow.id)}.mjs`));
5032
+ const wrapperPath = toPosixPath(
5033
+ path43.join(generatedDir, `${wrapperNames.get(workflow.id)}.mjs`)
5034
+ );
4839
5035
  await fs16.writeFile(wrapperPath, createNitroTaskWrapper(workflow), "utf8");
4840
5036
  tasks[workflow.id] = {
4841
5037
  handler: wrapperPath,
@@ -5073,6 +5269,7 @@ import {
5073
5269
  const route = ${JSON.stringify(config.route)};
5074
5270
  const secretEnv = ${JSON.stringify(config.secretEnv)};
5075
5271
  const inlineSecret = ${JSON.stringify(config.secret || "")};
5272
+ const allowUnsecured = ${JSON.stringify(config.allowUnsecured === true)};
5076
5273
  const bodySizeLimit = ${JSON.stringify(server.bodySizeLimit)};
5077
5274
  const workflows = ${JSON.stringify(workflows.map(toWorkflowMetadata))};
5078
5275
  const workflowIds = new Set(workflows.map((workflow) => workflow.id));
@@ -5102,7 +5299,10 @@ function getSecret() {
5102
5299
 
5103
5300
  function verifySecret(event) {
5104
5301
  const secret = getSecret();
5105
- if (!secret) return null;
5302
+ if (!secret) {
5303
+ if (allowUnsecured) return null;
5304
+ return json({ error: ${JSON.stringify(MISSING_FARM_WORKFLOW_SECRET_ERROR)} }, 401);
5305
+ }
5106
5306
  const authorization = getHeader(event, "authorization") || "";
5107
5307
  const headerSecret = getHeader(event, "x-farm-workflow-secret") || "";
5108
5308
  const bearer = authorization.match(/^Bearer\\s+(.+)$/i)?.[1] || "";
@@ -5196,8 +5396,16 @@ function readScheduledTime(payload) {
5196
5396
  return payload && typeof payload === "object" && "scheduledTime" in payload ? payload.scheduledTime : void 0;
5197
5397
  }
5198
5398
  function verifyWorkflowSecret(request, config) {
5199
- const secret = config.secret || process.env[config.secretEnv] || "";
5200
- if (!secret) return null;
5399
+ const secret = config.secret || readFarmEnvironmentValue(config.secretEnv) || "";
5400
+ if (!secret) {
5401
+ if (config.allowUnsecured === true || !isFarmDeployedRuntime()) return null;
5402
+ return Response.json(
5403
+ {
5404
+ error: MISSING_FARM_WORKFLOW_SECRET_ERROR
5405
+ },
5406
+ { status: 401 }
5407
+ );
5408
+ }
5201
5409
  const authorization = request.headers.get("authorization") || "";
5202
5410
  const bearer = authorization.match(/^Bearer\s+(.+)$/i)?.[1] || "";
5203
5411
  const headerSecret = request.headers.get("x-farm-workflow-secret") || "";
@@ -5210,18 +5418,50 @@ function joinRoute(...parts) {
5210
5418
  function trimSlashes(value) {
5211
5419
  return value.replace(/^\/+|\/+$/g, "");
5212
5420
  }
5421
+ function resolveWorkflowWrapperFileNames(ids) {
5422
+ const groups = /* @__PURE__ */ new Map();
5423
+ for (const id of ids) {
5424
+ const key = safeFileName(id).toLowerCase();
5425
+ groups.set(key, (groups.get(key) ?? 0) + 1);
5426
+ }
5427
+ const resolved = /* @__PURE__ */ new Map();
5428
+ const claimed = /* @__PURE__ */ new Map();
5429
+ for (const id of ids) {
5430
+ const base = safeFileName(id);
5431
+ const key = base.toLowerCase();
5432
+ const fileName = (groups.get(key) ?? 0) > 1 ? `${key}-${workflowIdFingerprint(id)}` : base;
5433
+ const claimedBy = claimed.get(fileName.toLowerCase());
5434
+ if (claimedBy !== void 0) {
5435
+ throw new Error(
5436
+ `Farm workflows ${JSON.stringify(claimedBy)} and ${JSON.stringify(id)} generate the same wrapper file ${JSON.stringify(`${fileName}.mjs`)}. Rename one of them.`
5437
+ );
5438
+ }
5439
+ claimed.set(fileName.toLowerCase(), id);
5440
+ resolved.set(id, fileName);
5441
+ }
5442
+ return resolved;
5443
+ }
5444
+ function workflowIdFingerprint(value) {
5445
+ let hash = 2166136261;
5446
+ for (let index = 0; index < value.length; index += 1) {
5447
+ hash ^= value.charCodeAt(index);
5448
+ hash = Math.imul(hash, 16777619);
5449
+ }
5450
+ return (hash >>> 0).toString(16).padStart(8, "0");
5451
+ }
5213
5452
  function safeFileName(value) {
5214
5453
  return value.replace(/[^a-zA-Z0-9._-]+/g, "-") || "workflow";
5215
5454
  }
5216
5455
  function relativePath(root, filePath) {
5217
5456
  return filePath.replace(/\\/g, "/").replace(`${root.replace(/\\/g, "/")}/`, "");
5218
5457
  }
5219
- var import_node_path2, DEFAULT_FARM_WORKFLOW_DIRS, DEFAULT_FARM_WORKFLOW_ROUTE, DEFAULT_FARM_WORKFLOW_SECRET_ENV;
5458
+ var import_node_path2, DEFAULT_FARM_WORKFLOW_DIRS, DEFAULT_FARM_WORKFLOW_ROUTE, DEFAULT_FARM_WORKFLOW_SECRET_ENV, MISSING_FARM_WORKFLOW_SECRET_ERROR;
5220
5459
  var init_workflows = __esm({
5221
5460
  "src/workflows.ts"() {
5222
5461
  "use strict";
5223
5462
  init_server_http();
5224
5463
  init_search_params();
5464
+ init_runtime_env();
5225
5465
  init_decode();
5226
5466
  init_utils();
5227
5467
  init_route_pattern();
@@ -5229,6 +5469,7 @@ var init_workflows = __esm({
5229
5469
  DEFAULT_FARM_WORKFLOW_DIRS = ["src/jobs", "src/workflows", "src/cron"];
5230
5470
  DEFAULT_FARM_WORKFLOW_ROUTE = "/api/_farm/workflows";
5231
5471
  DEFAULT_FARM_WORKFLOW_SECRET_ENV = "CRON_SECRET";
5472
+ MISSING_FARM_WORKFLOW_SECRET_ERROR = "Workflow route requires a secret. Set the CRON_SECRET environment variable, configure workflows.secret, or set workflows.allowUnsecured to true.";
5232
5473
  __name(defineWorkflow, "defineWorkflow");
5233
5474
  __name(defineTask, "defineTask");
5234
5475
  __name(defineCron, "defineCron");
@@ -5261,6 +5502,8 @@ var init_workflows = __esm({
5261
5502
  __name(verifyWorkflowSecret, "verifyWorkflowSecret");
5262
5503
  __name(joinRoute, "joinRoute");
5263
5504
  __name(trimSlashes, "trimSlashes");
5505
+ __name(resolveWorkflowWrapperFileNames, "resolveWorkflowWrapperFileNames");
5506
+ __name(workflowIdFingerprint, "workflowIdFingerprint");
5264
5507
  __name(safeFileName, "safeFileName");
5265
5508
  __name(relativePath, "relativePath");
5266
5509
  }
@@ -5312,9 +5555,10 @@ async function prepareFarmCronForNitro(config) {
5312
5555
  await fs16.rm(generatedDir, { recursive: true, force: true });
5313
5556
  await fs16.mkdir(generatedDir, { recursive: true });
5314
5557
  const tasks = {};
5558
+ const wrapperNames = resolveCronWrapperFileNames(cron.jobs.map((job) => job.name));
5315
5559
  for (const job of cron.jobs) {
5316
5560
  const taskName = getFarmCronTaskName(job.name);
5317
- const wrapperPath = toPosixPath(path43.join(generatedDir, `${safeFileName2(job.name)}.mjs`));
5561
+ const wrapperPath = toPosixPath(path43.join(generatedDir, `${wrapperNames.get(job.name)}.mjs`));
5318
5562
  await fs16.writeFile(wrapperPath, createNitroCronTaskWrapper(job, cron.secretEnv), "utf8");
5319
5563
  tasks[taskName] = {
5320
5564
  handler: wrapperPath,
@@ -5397,9 +5641,9 @@ function mergeScheduledTasks(...maps) {
5397
5641
  }
5398
5642
  function isCronRequestAuthorized(request, options = {}) {
5399
5643
  const secretEnv = options.secretEnv || DEFAULT_FARM_CRON_SECRET_ENV;
5400
- const secret = options.secret || readEnvironmentValue(secretEnv);
5644
+ const secret = options.secret || readFarmEnvironmentValue(secretEnv);
5401
5645
  if (!secret) {
5402
- return options.allowUnsecured === true || !getFarmRuntimeBindings() && readEnvironmentValue("NODE_ENV") !== "production";
5646
+ return options.allowUnsecured === true || !getFarmRuntimeBindings() && readFarmEnvironmentValue("NODE_ENV") !== "production";
5403
5647
  }
5404
5648
  const authorization = request.headers.get("authorization") || "";
5405
5649
  const bearer = /^Bearer (.*)$/i.exec(authorization)?.[1] || "";
@@ -5596,14 +5840,36 @@ export default defineTask({
5596
5840
  });
5597
5841
  `.trim();
5598
5842
  }
5599
- function readEnvironmentValue(name2) {
5600
- const runtimeValue = getFarmRuntimeBindings()?.[name2];
5601
- if (typeof runtimeValue === "string") return runtimeValue;
5602
- return typeof process !== "undefined" ? process.env?.[name2] : void 0;
5843
+ function resolveCronWrapperFileNames(names) {
5844
+ const groups = /* @__PURE__ */ new Map();
5845
+ for (const name2 of names) {
5846
+ const key = safeFileName2(name2).toLowerCase();
5847
+ groups.set(key, (groups.get(key) ?? 0) + 1);
5848
+ }
5849
+ const resolved = /* @__PURE__ */ new Map();
5850
+ const claimed = /* @__PURE__ */ new Map();
5851
+ for (const name2 of names) {
5852
+ const base = safeFileName2(name2);
5853
+ const key = base.toLowerCase();
5854
+ const fileName = (groups.get(key) ?? 0) > 1 ? `${key}-${cronNameFingerprint(name2)}` : base;
5855
+ const claimedBy = claimed.get(fileName.toLowerCase());
5856
+ if (claimedBy !== void 0) {
5857
+ throw new Error(
5858
+ `Farm cron jobs ${JSON.stringify(claimedBy)} and ${JSON.stringify(name2)} generate the same wrapper file ${JSON.stringify(`${fileName}.mjs`)}. Rename one of them.`
5859
+ );
5860
+ }
5861
+ claimed.set(fileName.toLowerCase(), name2);
5862
+ resolved.set(name2, fileName);
5863
+ }
5864
+ return resolved;
5603
5865
  }
5604
- function getFarmRuntimeBindings() {
5605
- const runtimeBindings = globalThis.__env__;
5606
- return runtimeBindings && typeof runtimeBindings === "object" ? runtimeBindings : void 0;
5866
+ function cronNameFingerprint(value) {
5867
+ let hash = 2166136261;
5868
+ for (let index = 0; index < value.length; index += 1) {
5869
+ hash ^= value.charCodeAt(index);
5870
+ hash = Math.imul(hash, 16777619);
5871
+ }
5872
+ return (hash >>> 0).toString(16).padStart(8, "0");
5607
5873
  }
5608
5874
  function safeFileName2(value) {
5609
5875
  return value.replace(/[^a-zA-Z0-9._-]+/g, "-") || "cron";
@@ -5613,6 +5879,7 @@ var init_cron = __esm({
5613
5879
  "src/cron.ts"() {
5614
5880
  "use strict";
5615
5881
  init_utils();
5882
+ init_runtime_env();
5616
5883
  DEFAULT_FARM_CRON_SECRET_ENV = "CRON_SECRET";
5617
5884
  FARM_CRON_MANIFEST = "cron-manifest.json";
5618
5885
  CRON_FIELD_RANGES = [
@@ -5643,8 +5910,8 @@ var init_cron = __esm({
5643
5910
  __name(mergeTaskNames, "mergeTaskNames");
5644
5911
  __name(getFarmCronTaskName, "getFarmCronTaskName");
5645
5912
  __name(createNitroCronTaskWrapper, "createNitroCronTaskWrapper");
5646
- __name(readEnvironmentValue, "readEnvironmentValue");
5647
- __name(getFarmRuntimeBindings, "getFarmRuntimeBindings");
5913
+ __name(resolveCronWrapperFileNames, "resolveCronWrapperFileNames");
5914
+ __name(cronNameFingerprint, "cronNameFingerprint");
5648
5915
  __name(safeFileName2, "safeFileName");
5649
5916
  }
5650
5917
  });
@@ -6715,7 +6982,9 @@ var init_server_fn_error = __esm({
6715
6982
 
6716
6983
  // src/server-action-security.ts
6717
6984
  function resolveServerActionsConfig(config) {
6718
- const allowedOrigins = (config?.allowedOrigins ?? []).map(normalizeAllowedOriginPattern);
6985
+ const allowedOrigins = (config?.allowedOrigins ?? []).map(
6986
+ (value) => normalizeAllowedOriginPattern(value, SERVER_ACTION_ALLOWED_ORIGINS_LABEL)
6987
+ );
6719
6988
  const bodySizeLimit = parseBodySizeLimit(
6720
6989
  config?.bodySizeLimit ?? DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT,
6721
6990
  "serverActions.bodySizeLimit"
@@ -6734,7 +7003,7 @@ function validateServerActionRequest(request, config) {
6734
7003
  );
6735
7004
  }
6736
7005
  const requestUrl = new URL(request.url);
6737
- const sourceOrigin = getRequestSourceOrigin(request);
7006
+ const sourceOrigin = resolveSourceOrigin(request);
6738
7007
  const fetchSite = request.headers.get("sec-fetch-site")?.trim().toLowerCase();
6739
7008
  if (!sourceOrigin) {
6740
7009
  if (fetchSite !== "same-origin") {
@@ -6850,103 +7119,16 @@ function getFallbackAbortController() {
6850
7119
  }
6851
7120
  return globalState2[FALLBACK_ABORT_CONTROLLER_KEY];
6852
7121
  }
6853
- function normalizeAllowedOriginPattern(value) {
6854
- const pattern = value.trim().toLowerCase();
6855
- if (!pattern) {
6856
- throw new TypeError("serverActions.allowedOrigins cannot contain empty values");
6857
- }
6858
- if (pattern.includes("*")) {
6859
- if (!/^(?:https?:\/\/)?\*\.[a-z0-9.-]+(?::\d+)?$/.test(pattern)) {
6860
- throw new TypeError(`Invalid serverActions.allowedOrigins pattern: ${JSON.stringify(value)}`);
6861
- }
6862
- return pattern;
6863
- }
6864
- if (pattern.includes("://")) {
6865
- let parsed;
6866
- try {
6867
- parsed = new URL(pattern);
6868
- } catch {
6869
- throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);
6870
- }
6871
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
6872
- throw new TypeError(
6873
- `serverActions.allowedOrigins must contain origins without paths: ${JSON.stringify(value)}`
6874
- );
6875
- }
6876
- return parsed.origin;
6877
- }
6878
- if (/[/@?#]/.test(pattern)) {
6879
- throw new TypeError(
6880
- `serverActions.allowedOrigins must contain origins or hosts: ${JSON.stringify(value)}`
7122
+ function resolveSourceOrigin(request) {
7123
+ const result = getRequestSourceOrigin(request);
7124
+ if (!result.ok) {
7125
+ throw new ServerActionRequestError(
7126
+ "INVALID_ORIGIN",
7127
+ 403,
7128
+ result.reason === "opaque-origin" ? "Opaque origins are not allowed" : "Invalid request origin"
6881
7129
  );
6882
7130
  }
6883
- try {
6884
- return new URL(`http://${pattern}`).host;
6885
- } catch {
6886
- throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);
6887
- }
6888
- }
6889
- function getRequestSourceOrigin(request) {
6890
- const origin = request.headers.get("origin")?.trim();
6891
- if (origin) {
6892
- return parseSourceOrigin(origin);
6893
- }
6894
- const referer = request.headers.get("referer")?.trim();
6895
- if (referer) {
6896
- return parseSourceOrigin(referer);
6897
- }
6898
- return null;
6899
- }
6900
- function parseSourceOrigin(value) {
6901
- if (value === "null") {
6902
- throw new ServerActionRequestError("INVALID_ORIGIN", 403, "Opaque origins are not allowed");
6903
- }
6904
- try {
6905
- const parsed = new URL(value);
6906
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
6907
- throw new Error("Unsupported protocol");
6908
- }
6909
- return parsed.origin;
6910
- } catch {
6911
- throw new ServerActionRequestError("INVALID_ORIGIN", 403, "Invalid request origin");
6912
- }
6913
- }
6914
- function matchesHostHeader(sourceOrigin, request) {
6915
- const host = request.headers.get("host")?.trim().toLowerCase();
6916
- if (!host) return false;
6917
- try {
6918
- const source = new URL(sourceOrigin);
6919
- const target = new URL(request.url);
6920
- return source.protocol === target.protocol && source.host.toLowerCase() === host;
6921
- } catch {
6922
- return false;
6923
- }
6924
- }
6925
- function matchesAllowedOrigin(sourceOrigin, pattern) {
6926
- const source = new URL(sourceOrigin);
6927
- if (!pattern.includes("*")) {
6928
- return pattern.includes("://") ? source.origin === pattern : source.host === pattern;
6929
- }
6930
- const schemeEnd = pattern.indexOf("://");
6931
- const scheme = schemeEnd === -1 ? null : pattern.slice(0, schemeEnd + 1);
6932
- const hostPattern = pattern.slice(schemeEnd === -1 ? 0 : schemeEnd + 3);
6933
- const [wildcardHost, port] = splitHostAndPort(hostPattern);
6934
- const baseHost = wildcardHost.slice(2);
6935
- if (scheme && source.protocol !== scheme) return false;
6936
- if (port && getEffectivePort(source) !== port) return false;
6937
- if (!port && source.port) return false;
6938
- return source.hostname.endsWith(`.${baseHost}`) && source.hostname !== baseHost;
6939
- }
6940
- function splitHostAndPort(value) {
6941
- const separator = value.lastIndexOf(":");
6942
- if (separator === -1) return [value, null];
6943
- return [value.slice(0, separator), value.slice(separator + 1)];
6944
- }
6945
- function getEffectivePort(url) {
6946
- if (url.port) return url.port;
6947
- if (url.protocol === "https:") return "443";
6948
- if (url.protocol === "http:") return "80";
6949
- return "";
7131
+ return result.origin;
6950
7132
  }
6951
7133
  function validateActionId(actionId) {
6952
7134
  if (!actionId || actionId.length > 4096 || hasControlCharacters(actionId)) {
@@ -7067,14 +7249,16 @@ function throwIfAborted2(signal) {
7067
7249
  if (signal.reason !== void 0) throw signal.reason;
7068
7250
  throw new DOMException("The operation was aborted", "AbortError");
7069
7251
  }
7070
- var import_node_async_hooks, DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT, _ServerActionRequestError, ServerActionRequestError, SERVER_ACTION_STORAGE_KEY, FALLBACK_ABORT_CONTROLLER_KEY, FORM_ACTION_CONTENT_TYPES, JAVASCRIPT_ACTION_CONTENT_TYPES;
7252
+ var import_node_async_hooks, SERVER_ACTION_ALLOWED_ORIGINS_LABEL, DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT, _ServerActionRequestError, ServerActionRequestError, SERVER_ACTION_STORAGE_KEY, FALLBACK_ABORT_CONTROLLER_KEY, FORM_ACTION_CONTENT_TYPES, JAVASCRIPT_ACTION_CONTENT_TYPES;
7071
7253
  var init_server_action_security = __esm({
7072
7254
  "src/server-action-security.ts"() {
7073
7255
  "use strict";
7074
7256
  import_node_async_hooks = require("async_hooks");
7075
7257
  init_cache_invalidation();
7258
+ init_request_origin();
7076
7259
  init_server_fn_error();
7077
7260
  init_server_http();
7261
+ SERVER_ACTION_ALLOWED_ORIGINS_LABEL = "serverActions.allowedOrigins";
7078
7262
  DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT = 1e6;
7079
7263
  _ServerActionRequestError = class _ServerActionRequestError extends Error {
7080
7264
  constructor(code, status, message) {
@@ -7115,13 +7299,7 @@ var init_server_action_security = __esm({
7115
7299
  });
7116
7300
  __name(getServerActionStorage, "getServerActionStorage");
7117
7301
  __name(getFallbackAbortController, "getFallbackAbortController");
7118
- __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
7119
- __name(getRequestSourceOrigin, "getRequestSourceOrigin");
7120
- __name(parseSourceOrigin, "parseSourceOrigin");
7121
- __name(matchesHostHeader, "matchesHostHeader");
7122
- __name(matchesAllowedOrigin, "matchesAllowedOrigin");
7123
- __name(splitHostAndPort, "splitHostAndPort");
7124
- __name(getEffectivePort, "getEffectivePort");
7302
+ __name(resolveSourceOrigin, "resolveSourceOrigin");
7125
7303
  __name(validateActionId, "validateActionId");
7126
7304
  __name(hasControlCharacters, "hasControlCharacters");
7127
7305
  __name(getSupportedContentType, "getSupportedContentType");
@@ -10233,6 +10411,18 @@ var init_routes_server = __esm({
10233
10411
  function isFarmMarkdownPageFile(filePath) {
10234
10412
  return /(^|[/\\])page\.mdx?$/i.test(filePath);
10235
10413
  }
10414
+ function farmRequestWantsMarkdown(pathname, accept) {
10415
+ return pathname.toLowerCase().endsWith(".md") || requestAcceptsMarkdown(accept);
10416
+ }
10417
+ function createFarmMarkdownErrorBody(status, pathname, homeHref = "/") {
10418
+ const heading = status === 404 ? "Page not found" : `Request failed (${status})`;
10419
+ return `# ${heading}
10420
+
10421
+ No page is available at \`${pathname}\`. The URL may be incorrect, or the page has not been published.
10422
+
10423
+ Browse [the site homepage](${homeHref}) to find available pages.
10424
+ `;
10425
+ }
10236
10426
  function normalizeFarmMarkdownRoutePath(pathname) {
10237
10427
  const withoutExtension = pathname.replace(/\.md$/i, "");
10238
10428
  const normalized = withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`;
@@ -10376,7 +10566,7 @@ async function createFarmMarkdownSourceResponse(options) {
10376
10566
  headers: headers2
10377
10567
  });
10378
10568
  }
10379
- var import_promises3, import_path3, import_url;
10569
+ var import_promises3, import_path3, import_url, FARM_MARKDOWN_CONTENT_TYPE;
10380
10570
  var init_app_markdown = __esm({
10381
10571
  "src/app-markdown.ts"() {
10382
10572
  "use strict";
@@ -10386,6 +10576,9 @@ var init_app_markdown = __esm({
10386
10576
  init_markdown();
10387
10577
  init_app_markdown_config();
10388
10578
  __name(isFarmMarkdownPageFile, "isFarmMarkdownPageFile");
10579
+ FARM_MARKDOWN_CONTENT_TYPE = "text/markdown; charset=utf-8";
10580
+ __name(farmRequestWantsMarkdown, "farmRequestWantsMarkdown");
10581
+ __name(createFarmMarkdownErrorBody, "createFarmMarkdownErrorBody");
10389
10582
  __name(normalizeFarmMarkdownRoutePath, "normalizeFarmMarkdownRoutePath");
10390
10583
  __name(parseMarkdownFrontmatter, "parseMarkdownFrontmatter");
10391
10584
  __name(titleFromMarkdown, "titleFromMarkdown");
@@ -10893,6 +11086,39 @@ function getStaticImportBindings(content) {
10893
11086
  }
10894
11087
  return bindings;
10895
11088
  }
11089
+ function findParserSensitiveJsxContainer(content, localBindings) {
11090
+ if (!content || localBindings.length === 0) return void 0;
11091
+ const names = new Set(localBindings);
11092
+ const tokens = tokenizeModuleSource(content);
11093
+ const openDepth = /* @__PURE__ */ new Map();
11094
+ const isSelfClosing = /* @__PURE__ */ __name((openingIndex) => {
11095
+ for (let index = openingIndex; index < tokens.length; index++) {
11096
+ if (tokens[index].value === ">") return tokens[index - 1]?.value === "/";
11097
+ }
11098
+ return false;
11099
+ }, "isSelfClosing");
11100
+ for (let index = 0; index < tokens.length - 1; index++) {
11101
+ if (tokens[index].value !== "<") continue;
11102
+ if (tokens[index + 1].value === "/") {
11103
+ const closing = tokens[index + 2]?.value;
11104
+ if (closing && openDepth.has(closing)) {
11105
+ openDepth.set(closing, Math.max(0, (openDepth.get(closing) ?? 0) - 1));
11106
+ }
11107
+ continue;
11108
+ }
11109
+ const tag = tokens[index + 1].value;
11110
+ if (names.has(tag)) {
11111
+ for (const [container, depth] of openDepth) {
11112
+ if (depth > 0) return container;
11113
+ }
11114
+ continue;
11115
+ }
11116
+ if (PARSER_SENSITIVE_JSX_CONTAINERS.has(tag) && !isSelfClosing(index)) {
11117
+ openDepth.set(tag, (openDepth.get(tag) ?? 0) + 1);
11118
+ }
11119
+ }
11120
+ return void 0;
11121
+ }
10896
11122
  function countStaticJsxUses(content, localBindings) {
10897
11123
  if (!content || localBindings.length === 0) return 0;
10898
11124
  const names = new Set(localBindings);
@@ -11001,6 +11227,36 @@ function getClientRenderingHelpers(tokens, clientBindings) {
11001
11227
  }
11002
11228
  return helpers;
11003
11229
  }
11230
+ function passesElementValuedProps(content, localBindings) {
11231
+ if (!content || localBindings.length === 0) return false;
11232
+ const names = new Set(localBindings);
11233
+ const tokens = tokenizeModuleSource(content);
11234
+ for (let index = 0; index < tokens.length - 1; index++) {
11235
+ if (tokens[index].value !== "<" || !names.has(tokens[index + 1].value)) continue;
11236
+ const name2 = tokens[index + 1].value;
11237
+ let cursor = index + 2;
11238
+ let braceDepth = 0;
11239
+ let selfClosing = false;
11240
+ for (; cursor < tokens.length; cursor++) {
11241
+ const value = tokens[cursor].value;
11242
+ if (value === "{") braceDepth++;
11243
+ else if (value === "}") braceDepth--;
11244
+ else if (value === "<" && braceDepth > 0) return true;
11245
+ else if (value === ">" && braceDepth === 0) {
11246
+ selfClosing = tokens[cursor - 1]?.value === "/";
11247
+ break;
11248
+ }
11249
+ }
11250
+ if (selfClosing || cursor >= tokens.length) continue;
11251
+ for (let child = cursor + 1; child < tokens.length; child++) {
11252
+ if (tokens[child].value !== "<") continue;
11253
+ const closesBoundary = tokens[child + 1]?.value === "/" && tokens[child + 2]?.value === name2;
11254
+ if (!closesBoundary) return true;
11255
+ break;
11256
+ }
11257
+ }
11258
+ return false;
11259
+ }
11004
11260
  function hasDynamicJsxCardinality(content, localBindings) {
11005
11261
  if (!content || localBindings.length === 0) return false;
11006
11262
  const clientBindings = new Set(localBindings);
@@ -11082,6 +11338,17 @@ function collectIsolatedClientBoundaries(ownerPath, root) {
11082
11338
  if (fallbackReason || importedBoundaryCount === 0) continue;
11083
11339
  const localBindings = importedBindings.get(specifier) ?? [];
11084
11340
  const staticUses = countStaticJsxUses(content, localBindings);
11341
+ if (staticUses > 0) {
11342
+ const container = findParserSensitiveJsxContainer(content, localBindings);
11343
+ if (container) {
11344
+ fallbackReason = `the client boundary imported from ${specifier} renders inside <${container}>, where the HTML parser relocates its hydration marker`;
11345
+ return 0;
11346
+ }
11347
+ if (passesElementValuedProps(content, localBindings)) {
11348
+ fallbackReason = `the client boundary imported from ${specifier} receives React elements, which cannot cross the boundary as serialized props`;
11349
+ return 0;
11350
+ }
11351
+ }
11085
11352
  if (staticUses > 0 && hasDynamicJsxCardinality(content, localBindings)) {
11086
11353
  fallbackReason = `the client boundary count imported from ${specifier} is data-dependent`;
11087
11354
  costGuardExceeded = true;
@@ -11378,7 +11645,7 @@ function resolveSourceCandidate(candidates) {
11378
11645
  }
11379
11646
  return null;
11380
11647
  }
11381
- var import_fs2, import_path4, import_url2, FARM_ISOLATED_HYDRATION_MAX_BOUNDARIES, RESOLVABLE_SOURCE_EXTENSIONS, REGEXP_PREFIX_PUNCTUATION, REGEXP_PREFIX_KEYWORDS;
11648
+ var import_fs2, import_path4, import_url2, FARM_ISOLATED_HYDRATION_MAX_BOUNDARIES, RESOLVABLE_SOURCE_EXTENSIONS, REGEXP_PREFIX_PUNCTUATION, REGEXP_PREFIX_KEYWORDS, PARSER_SENSITIVE_JSX_CONTAINERS;
11382
11649
  var init_client_component = __esm({
11383
11650
  "src/utils/client-component.ts"() {
11384
11651
  "use strict";
@@ -11456,11 +11723,23 @@ var init_client_component = __esm({
11456
11723
  __name(enforceFarmIsolatedHydrationRouteBudget, "enforceFarmIsolatedHydrationRouteBudget");
11457
11724
  __name(isIsolatableClientBoundarySource, "isIsolatableClientBoundarySource");
11458
11725
  __name(getStaticImportBindings, "getStaticImportBindings");
11726
+ PARSER_SENSITIVE_JSX_CONTAINERS = /* @__PURE__ */ new Set([
11727
+ "table",
11728
+ "thead",
11729
+ "tbody",
11730
+ "tfoot",
11731
+ "tr",
11732
+ "select",
11733
+ "optgroup",
11734
+ "svg"
11735
+ ]);
11736
+ __name(findParserSensitiveJsxContainer, "findParserSensitiveJsxContainer");
11459
11737
  __name(countStaticJsxUses, "countStaticJsxUses");
11460
11738
  __name(findMatchingToken, "findMatchingToken");
11461
11739
  __name(containsClientJsx, "containsClientJsx");
11462
11740
  __name(findStatementEnd, "findStatementEnd");
11463
11741
  __name(getClientRenderingHelpers, "getClientRenderingHelpers");
11742
+ __name(passesElementValuedProps, "passesElementValuedProps");
11464
11743
  __name(hasDynamicJsxCardinality, "hasDynamicJsxCardinality");
11465
11744
  __name(collectIsolatedClientBoundaries, "collectIsolatedClientBoundaries");
11466
11745
  __name(inspectClientModuleMetadata, "inspectClientModuleMetadata");
@@ -13071,6 +13350,51 @@ var init_not_found = __esm({
13071
13350
  }
13072
13351
  });
13073
13352
 
13353
+ // src/agent-config.ts
13354
+ function resolveFarmAgentConfig(input) {
13355
+ const jsonLd = input?.jsonLd;
13356
+ if (!jsonLd) return { jsonLd: false };
13357
+ return { jsonLd: jsonLd === true ? {} : jsonLd };
13358
+ }
13359
+ function pruneUndefined(object) {
13360
+ const result = {};
13361
+ for (const [key, value] of Object.entries(object)) {
13362
+ if (value !== void 0 && value !== null && value !== "") result[key] = value;
13363
+ }
13364
+ return result;
13365
+ }
13366
+ function serializeJsonLd(value) {
13367
+ return JSON.stringify(value).replace(/</g, "\\u003c");
13368
+ }
13369
+ function renderFarmAgentJsonLd(config, context2) {
13370
+ const base = pruneUndefined({
13371
+ name: config.name ?? context2.siteName ?? context2.title,
13372
+ url: config.url ?? context2.metadataBase,
13373
+ description: config.description ?? context2.description,
13374
+ logo: config.logo,
13375
+ sameAs: config.sameAs && config.sameAs.length > 0 ? config.sameAs : void 0
13376
+ });
13377
+ const object = {
13378
+ "@context": "https://schema.org",
13379
+ "@type": config.type || "Organization",
13380
+ ...base,
13381
+ ...config.properties || {}
13382
+ };
13383
+ if (Object.keys(object).length <= 2 && !config.properties) {
13384
+ return "";
13385
+ }
13386
+ return `<script type="application/ld+json">${serializeJsonLd(object)}</script>`;
13387
+ }
13388
+ var init_agent_config = __esm({
13389
+ "src/agent-config.ts"() {
13390
+ "use strict";
13391
+ __name(resolveFarmAgentConfig, "resolveFarmAgentConfig");
13392
+ __name(pruneUndefined, "pruneUndefined");
13393
+ __name(serializeJsonLd, "serializeJsonLd");
13394
+ __name(renderFarmAgentJsonLd, "renderFarmAgentJsonLd");
13395
+ }
13396
+ });
13397
+
13074
13398
  // src/metadata.ts
13075
13399
  function mergeMetadata(base, next) {
13076
13400
  if (!base) return next ? { ...next } : {};
@@ -13130,7 +13454,7 @@ function addMetadataImageReference(metadata, reference) {
13130
13454
  twitter
13131
13455
  };
13132
13456
  }
13133
- function renderMetadataHead(metadata) {
13457
+ function renderMetadataHead(metadata, options = {}) {
13134
13458
  const resolvedMetadata = metadata || {};
13135
13459
  const metadataBase = resolveMetadataBase(resolvedMetadata);
13136
13460
  const explicitTitle = resolveMetadataTitle(resolvedMetadata.title);
@@ -13151,14 +13475,16 @@ function renderMetadataHead(metadata) {
13151
13475
  appendMetaName(tags, "publisher", resolvedMetadata.publisher);
13152
13476
  appendMetaName(tags, "robots", normalizeRobots(resolvedMetadata.robots));
13153
13477
  const alternates = resolvedMetadata.alternates;
13154
- if (isRecord(alternates)) {
13155
- appendLink(tags, "canonical", resolveMetadataUrl(alternates.canonical, metadataBase));
13156
- if (isRecord(alternates.languages)) {
13157
- for (const [language, href] of Object.entries(alternates.languages)) {
13158
- appendLink(tags, "alternate", resolveMetadataUrl(href, metadataBase), {
13159
- hreflang: language
13160
- });
13161
- }
13478
+ const explicitCanonical = isRecord(alternates) ? alternates.canonical : void 0;
13479
+ const canonicalHref = explicitCanonical != null ? resolveMetadataUrl(explicitCanonical, metadataBase) : options.pathname ? resolveMetadataUrl(options.pathname, metadataBase) : void 0;
13480
+ if (canonicalHref) {
13481
+ appendLink(tags, "canonical", canonicalHref);
13482
+ }
13483
+ if (isRecord(alternates) && isRecord(alternates.languages)) {
13484
+ for (const [language, href] of Object.entries(alternates.languages)) {
13485
+ appendLink(tags, "alternate", resolveMetadataUrl(href, metadataBase), {
13486
+ hreflang: language
13487
+ });
13162
13488
  }
13163
13489
  }
13164
13490
  const hasFavicon = appendIcons(tags, resolvedMetadata.icons, metadataBase);
@@ -13171,6 +13497,16 @@ function renderMetadataHead(metadata) {
13171
13497
  }
13172
13498
  appendOpenGraph(tags, resolvedMetadata.openGraph, metadataBase);
13173
13499
  appendTwitter(tags, resolvedMetadata.twitter, metadataBase);
13500
+ if (options.jsonLd) {
13501
+ const jsonLdConfig = options.jsonLd === true ? {} : options.jsonLd;
13502
+ const jsonLdScript = renderFarmAgentJsonLd(jsonLdConfig, {
13503
+ metadataBase,
13504
+ siteName: isRecord(resolvedMetadata.openGraph) ? normalizeContent(resolvedMetadata.openGraph.siteName) : void 0,
13505
+ title: explicitTitle,
13506
+ description: normalizeContent(resolvedMetadata.description)
13507
+ });
13508
+ if (jsonLdScript) tags.push(jsonLdScript);
13509
+ }
13174
13510
  return {
13175
13511
  title: escapeText(title),
13176
13512
  tags: tags.length > 0 ? `
@@ -13194,7 +13530,7 @@ function appendOpenGraph(tags, openGraph, metadataBase) {
13194
13530
  appendMetaProperty(tags, "og:description", openGraph.description);
13195
13531
  appendMetaProperty(tags, "og:url", resolveMetadataUrl(openGraph.url, metadataBase));
13196
13532
  appendMetaProperty(tags, "og:site_name", openGraph.siteName);
13197
- appendMetaProperty(tags, "og:type", openGraph.type);
13533
+ appendMetaProperty(tags, "og:type", openGraph.type ?? "website");
13198
13534
  appendMetaProperty(tags, "og:locale", openGraph.locale);
13199
13535
  const images = normalizeMetadataImages(
13200
13536
  openGraph.images ?? openGraph.image,
@@ -13357,6 +13693,7 @@ function escapeAttribute(value) {
13357
13693
  var init_metadata = __esm({
13358
13694
  "src/metadata.ts"() {
13359
13695
  "use strict";
13696
+ init_agent_config();
13360
13697
  __name(mergeMetadata, "mergeMetadata");
13361
13698
  __name(mergeMetadataTitle, "mergeMetadataTitle");
13362
13699
  __name(applyTitleTemplate, "applyTitleTemplate");
@@ -17012,7 +17349,7 @@ var init_server3 = __esm({
17012
17349
  var version;
17013
17350
  var init_package = __esm({
17014
17351
  "package.json"() {
17015
- version = "0.1.0-beta.97";
17352
+ version = "0.1.0-beta.99";
17016
17353
  }
17017
17354
  });
17018
17355
 
@@ -17267,6 +17604,12 @@ function hasRequestHeader(req, name2) {
17267
17604
  const value = req.headers[name2.toLowerCase()];
17268
17605
  return Array.isArray(value) ? value.length > 0 : Boolean(value);
17269
17606
  }
17607
+ function getFarmMetadataPathname(req) {
17608
+ const raw = req.__FARM_ROUTE__ || req.url;
17609
+ if (typeof raw !== "string" || raw.length === 0) return void 0;
17610
+ const boundary = raw.search(/[?#]/);
17611
+ return boundary === -1 ? raw : raw.slice(0, boundary);
17612
+ }
17270
17613
  function serializeInlineValue2(value) {
17271
17614
  return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
17272
17615
  }
@@ -17415,6 +17758,7 @@ var init_renderer2 = __esm({
17415
17758
  init_server();
17416
17759
  init_request_context();
17417
17760
  init_ssg();
17761
+ init_app_markdown();
17418
17762
  init_integrations();
17419
17763
  init_request();
17420
17764
  init_cache();
@@ -17456,6 +17800,7 @@ var init_renderer2 = __esm({
17456
17800
  warnedFullDocumentLayout = false;
17457
17801
  __name(warnFarmFullDocumentLayout, "warnFarmFullDocumentLayout");
17458
17802
  __name(hasRequestHeader, "hasRequestHeader");
17803
+ __name(getFarmMetadataPathname, "getFarmMetadataPathname");
17459
17804
  __name(serializeInlineValue2, "serializeInlineValue");
17460
17805
  __name(escapeHtmlAttribute2, "escapeHtmlAttribute");
17461
17806
  __name(appendResponseHeader, "appendResponseHeader");
@@ -17855,7 +18200,21 @@ var init_renderer2 = __esm({
17855
18200
  const html = await this.rendererRuntime.renderToString(
17856
18201
  await this.wrapWithIntegrationProviders(pageElement)
17857
18202
  );
17858
- await this.cacheSSGPage(page2, html, { document: false });
18203
+ const mergedMetadata = await this.resolveRouteMetadata({
18204
+ layoutModules,
18205
+ routeModule: mod,
18206
+ pageProps,
18207
+ pathname: page2.urlPath
18208
+ });
18209
+ const { title, tags, hasFavicon } = renderMetadataHead(mergedMetadata);
18210
+ const metadataHead = `${hasFavicon ? "" : '<link rel="icon" href="data:,">\n '}<title>${title}</title>${tags}`;
18211
+ const fullDocument = this.createFullHTML(
18212
+ html,
18213
+ pageMetadata.shouldHydrate === true,
18214
+ page2.urlPath,
18215
+ metadataHead
18216
+ );
18217
+ await this.cacheSSGPage(page2, fullDocument, { document: true });
17859
18218
  logger.info(`ISR: Regenerated ${page2.urlPath}`);
17860
18219
  } catch (error) {
17861
18220
  logger.error(`ISR regeneration failed for ${page2.urlPath}: ${error}`);
@@ -18895,7 +19254,10 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
18895
19254
  tags: metaTags,
18896
19255
  hasFavicon,
18897
19256
  hasExplicitTitle
18898
- } = renderMetadataHead(req.__FARM_METADATA__);
19257
+ } = renderMetadataHead(req.__FARM_METADATA__, {
19258
+ pathname: getFarmMetadataPathname(req),
19259
+ jsonLd: this.config.agent?.jsonLd
19260
+ });
18899
19261
  const documentTitleTag = !hasExplicitTitle && /<title[\s>]/i.test(rendererHead) ? "" : `<title>${title}</title>`;
18900
19262
  const i18nSnapshot = getFarmI18nClientSnapshot();
18901
19263
  const alternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
@@ -19080,7 +19442,10 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
19080
19442
  title,
19081
19443
  tags: metaTags,
19082
19444
  hasFavicon
19083
- } = renderMetadataHead(req.__FARM_METADATA__);
19445
+ } = renderMetadataHead(req.__FARM_METADATA__, {
19446
+ pathname: getFarmMetadataPathname(req),
19447
+ jsonLd: this.config.agent?.jsonLd
19448
+ });
19084
19449
  const i18nSnapshot = getFarmI18nClientSnapshot();
19085
19450
  const i18nAlternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
19086
19451
  const fontHead = renderFarmFontDevHead(this.config.root || process.cwd());
@@ -19238,6 +19603,15 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
19238
19603
  const pathname = resolveFarmRequestURL(req, {
19239
19604
  trustProxy: this.config.server?.trustProxy
19240
19605
  }).pathname;
19606
+ const acceptHeader = req.headers.accept;
19607
+ const accept = Array.isArray(acceptHeader) ? acceptHeader.join(",") : acceptHeader;
19608
+ if (farmRequestWantsMarkdown(pathname, accept)) {
19609
+ res.setHeader("Content-Type", FARM_MARKDOWN_CONTENT_TYPE);
19610
+ res.setHeader("X-Farm-Markdown-Error", "404");
19611
+ res.setHeader("Cache-Control", "no-store");
19612
+ res.end(createFarmMarkdownErrorBody(404, pathname, this.config.basePath || "/"));
19613
+ return;
19614
+ }
19241
19615
  try {
19242
19616
  const appDir = path12.join(this.config.root, this.config.srcDir, "app");
19243
19617
  const notFoundExtensions = getFarmRendererComponentExtensions(this.config.renderer);
@@ -19323,7 +19697,7 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
19323
19697
  res.write(html);
19324
19698
  res.end();
19325
19699
  }
19326
- createFullHTML(content, isClientComponent = false, requestPath = "/", documentTitle = "Farm.js App") {
19700
+ createFullHTML(content, isClientComponent = false, requestPath = "/", metadataHead) {
19327
19701
  const i18nSnapshot = getFarmI18nClientSnapshot();
19328
19702
  const clientScript = isClientComponent ? ` <script type="module" src="/@farm/client.js"></script>` : "";
19329
19703
  const integrationManifestScript = `<script>
@@ -19347,6 +19721,7 @@ ${i18nSnapshot ? `window.__FARM_I18N__ = ${serializeInlineValue2(i18nSnapshot)};
19347
19721
  headAssets: [
19348
19722
  themeDocument.head,
19349
19723
  `<meta name="farm-deployment-id" content="${escapeHtmlAttribute2(this.getDeploymentId())}">`,
19724
+ metadataHead,
19350
19725
  alternateLinks,
19351
19726
  fontHead,
19352
19727
  `<link rel="stylesheet" href="/src/app/globals.css" />`,
@@ -19365,8 +19740,7 @@ ${i18nSnapshot ? `window.__FARM_I18N__ = ${serializeInlineValue2(i18nSnapshot)};
19365
19740
  <meta charset="utf-8">
19366
19741
  <meta name="viewport" content="width=device-width, initial-scale=1">
19367
19742
  <meta name="farm-deployment-id" content="${escapeHtmlAttribute2(this.getDeploymentId())}">
19368
- <link rel="icon" href="data:,">
19369
- <title>${escapeHtmlAttribute2(documentTitle)}</title>${alternateLinks}
19743
+ ${metadataHead ?? '<link rel="icon" href="data:,">\n <title>Farm.js App</title>'}${alternateLinks}
19370
19744
  ${fontHead}
19371
19745
  <link rel="stylesheet" href="/src/app/globals.css" />${this.collectDevStyleLinks().map((l) => `
19372
19746
  ${l}`).join("")}
@@ -20202,6 +20576,7 @@ var init_app = __esm({
20202
20576
  ppr: config.experimental?.ppr ?? false,
20203
20577
  ...config.experimental
20204
20578
  },
20579
+ agent: config.agent ?? {},
20205
20580
  vite: config.vite || {}
20206
20581
  };
20207
20582
  }
@@ -24924,6 +25299,116 @@ var init_server_query_boundary = __esm({
24924
25299
  }
24925
25300
  });
24926
25301
 
25302
+ // src/client-boundary-env.ts
25303
+ function shouldInspectClientBoundary(id, code) {
25304
+ if (id.startsWith("\0") || id.startsWith("virtual:") || id.includes("node_modules")) return false;
25305
+ return code.includes("process.env") || code.includes("node:");
25306
+ }
25307
+ function analyzeClientBoundary(program, publicKeys) {
25308
+ const envKeys = /* @__PURE__ */ new Set();
25309
+ const publicEnvKeys = /* @__PURE__ */ new Set();
25310
+ const builtinImports = /* @__PURE__ */ new Set();
25311
+ const visit = /* @__PURE__ */ __name((node, inFunction) => {
25312
+ if (!node || typeof node !== "object") return;
25313
+ if (Array.isArray(node)) {
25314
+ for (const entry of node) visit(entry, inFunction);
25315
+ return;
25316
+ }
25317
+ const estree = node;
25318
+ if (typeof estree.type !== "string") return;
25319
+ if (!inFunction) {
25320
+ const builtin = readNodeBuiltinSource(estree);
25321
+ if (builtin !== void 0) builtinImports.add(builtin);
25322
+ const key = readProcessEnvKey(estree);
25323
+ if (key !== void 0) {
25324
+ if (key !== "NODE_ENV") {
25325
+ (publicKeys.has(key) ? publicEnvKeys : envKeys).add(key);
25326
+ }
25327
+ return;
25328
+ }
25329
+ }
25330
+ const nextInFunction = inFunction || FUNCTION_BODY_TYPES.has(estree.type);
25331
+ for (const [childKey, value] of Object.entries(estree)) {
25332
+ if (childKey === "type") continue;
25333
+ visit(value, nextInFunction);
25334
+ }
25335
+ }, "visit");
25336
+ visit(program.body, false);
25337
+ return {
25338
+ envKeys: [...envKeys],
25339
+ publicEnvKeys: [...publicEnvKeys],
25340
+ builtinImports: [...builtinImports]
25341
+ };
25342
+ }
25343
+ function readNodeBuiltinSource(node) {
25344
+ if (node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ImportExpression") {
25345
+ const value = node.source?.value;
25346
+ return typeof value === "string" && value.startsWith("node:") ? value : void 0;
25347
+ }
25348
+ if (node.type === "CallExpression") {
25349
+ const callee = node.callee;
25350
+ if (callee?.type === "Identifier" && callee.name === "require") {
25351
+ const arg = node.arguments?.[0];
25352
+ const value = arg?.type === "Literal" ? arg.value : void 0;
25353
+ return typeof value === "string" && value.startsWith("node:") ? value : void 0;
25354
+ }
25355
+ }
25356
+ return void 0;
25357
+ }
25358
+ function readProcessEnvKey(node) {
25359
+ if (node.type !== "MemberExpression") return void 0;
25360
+ const object = node.object;
25361
+ if (!object || object.type !== "MemberExpression" || object.object?.type !== "Identifier" || object.object.name !== "process" || object.property?.name !== "env") {
25362
+ return void 0;
25363
+ }
25364
+ const property = node.property;
25365
+ if (!property) return void 0;
25366
+ if (!node.computed && property.type === "Identifier") {
25367
+ return property.name;
25368
+ }
25369
+ if (node.computed && property.type === "Literal") {
25370
+ const value = property.value;
25371
+ return typeof value === "string" ? value : void 0;
25372
+ }
25373
+ return void 0;
25374
+ }
25375
+ function formatClientBoundaryWarning(id, findings) {
25376
+ const { envKeys, publicEnvKeys, builtinImports } = findings;
25377
+ const lines = [`${id} is compiled for the client but uses server-only APIs:`];
25378
+ if (envKeys.length > 0) {
25379
+ lines.push(
25380
+ `- module-scope read of process.env.${envKeys.join(", process.env.")} \u2014 undefined in the browser. Move the read behind a server boundary, or expose it through env.public in farm.config.ts.`
25381
+ );
25382
+ }
25383
+ if (publicEnvKeys.length > 0) {
25384
+ lines.push(
25385
+ `- module-scope read of process.env.${publicEnvKeys.join(", process.env.")} \u2014 process.env is not populated in the browser even for public keys. Read it through publicEnv from "@farm.js/core/env".`
25386
+ );
25387
+ }
25388
+ if (builtinImports.length > 0) {
25389
+ lines.push(
25390
+ `- import of ${builtinImports.join(", ")} \u2014 stubbed with an empty object in the browser. Move the import into server-only code.`
25391
+ );
25392
+ }
25393
+ return lines.join("\n");
25394
+ }
25395
+ var FUNCTION_BODY_TYPES;
25396
+ var init_client_boundary_env = __esm({
25397
+ "src/client-boundary-env.ts"() {
25398
+ "use strict";
25399
+ FUNCTION_BODY_TYPES = /* @__PURE__ */ new Set([
25400
+ "FunctionDeclaration",
25401
+ "FunctionExpression",
25402
+ "ArrowFunctionExpression"
25403
+ ]);
25404
+ __name(shouldInspectClientBoundary, "shouldInspectClientBoundary");
25405
+ __name(analyzeClientBoundary, "analyzeClientBoundary");
25406
+ __name(readNodeBuiltinSource, "readNodeBuiltinSource");
25407
+ __name(readProcessEnvKey, "readProcessEnvKey");
25408
+ __name(formatClientBoundaryWarning, "formatClientBoundaryWarning");
25409
+ }
25410
+ });
25411
+
24927
25412
  // src/image-vite.ts
24928
25413
  function farmImageImportsPlugin() {
24929
25414
  return {
@@ -25037,6 +25522,7 @@ var init_font = __esm({
25037
25522
  function createFarmImageHandler(config, options) {
25038
25523
  const fetcher = options.fetch ?? globalThis.fetch;
25039
25524
  const cache = new FarmImageMemoryCache(options.cacheEntries ?? 100);
25525
+ const inflight = /* @__PURE__ */ new Map();
25040
25526
  const allowedWidths = /* @__PURE__ */ new Set([...config.deviceSizes, ...config.imageSizes]);
25041
25527
  const allowedQualities = new Set(config.qualities);
25042
25528
  return /* @__PURE__ */ __name(async function handleFarmImage(request) {
@@ -25065,50 +25551,55 @@ function createFarmImageHandler(config, options) {
25065
25551
  "quality"
25066
25552
  );
25067
25553
  const accept = request.headers.get("accept") ?? "";
25554
+ const negotiatedFormat = selectOutputFormat(accept, config.formats) ?? "";
25068
25555
  const cacheKey = `${sourceUrl.href}
25069
25556
  ${width}
25070
25557
  ${quality}
25071
- ${accept}`;
25558
+ ${negotiatedFormat}`;
25072
25559
  let optimized = cache.get(cacheKey);
25073
25560
  if (!optimized) {
25074
- const fetchedSource = await fetchImageSource(
25075
- sourceUrl,
25076
- requestUrl.origin,
25077
- config,
25078
- fetcher,
25079
- options.fetchRemote,
25080
- options.validateRemoteUrl,
25081
- request.signal
25082
- );
25083
- const source = await readResponseWithLimit(
25084
- fetchedSource.response,
25085
- config.maximumResponseBody
25086
- );
25087
- const sourceType = detectImageContentType(source);
25088
- validateSourceType(sourceType, config);
25089
- throwIfAborted3(request.signal);
25090
- const result = await options.transform({
25091
- source,
25092
- sourceUrl: fetchedSource.url,
25093
- sourceType,
25094
- width,
25095
- quality,
25096
- accept,
25097
- formats: config.formats,
25098
- signal: request.signal
25561
+ optimized = await runCoalesced(inflight, cacheKey, request.signal, async (signal) => {
25562
+ const fetchedSource = await fetchImageSource(
25563
+ sourceUrl,
25564
+ requestUrl.origin,
25565
+ config,
25566
+ fetcher,
25567
+ options.fetchRemote,
25568
+ options.validateRemoteUrl,
25569
+ signal
25570
+ );
25571
+ const source = await readResponseWithLimit(
25572
+ fetchedSource.response,
25573
+ config.maximumResponseBody
25574
+ );
25575
+ const sourceType = detectImageContentType(source);
25576
+ validateSourceType(sourceType, config);
25577
+ throwIfAborted3(signal);
25578
+ const result = await options.transform({
25579
+ source,
25580
+ sourceUrl: fetchedSource.url,
25581
+ sourceType,
25582
+ width,
25583
+ quality,
25584
+ accept,
25585
+ formats: config.formats,
25586
+ signal,
25587
+ maximumResponseBody: config.maximumResponseBody
25588
+ });
25589
+ throwIfAborted3(signal);
25590
+ validateTransformedResult(result, config);
25591
+ const entry = {
25592
+ ...result,
25593
+ etag: createImageEtag(result.body),
25594
+ cacheControl: `public, max-age=${config.minimumCacheTTL}, stale-while-revalidate=${Math.max(
25595
+ config.minimumCacheTTL,
25596
+ 60
25597
+ )}`,
25598
+ expiresAt: Date.now() + config.minimumCacheTTL * 1e3
25599
+ };
25600
+ cache.set(cacheKey, entry);
25601
+ return entry;
25099
25602
  });
25100
- throwIfAborted3(request.signal);
25101
- validateTransformedResult(result, config);
25102
- optimized = {
25103
- ...result,
25104
- etag: createImageEtag(result.body),
25105
- cacheControl: `public, max-age=${config.minimumCacheTTL}, stale-while-revalidate=${Math.max(
25106
- config.minimumCacheTTL,
25107
- 60
25108
- )}`,
25109
- expiresAt: Date.now() + config.minimumCacheTTL * 1e3
25110
- };
25111
- cache.set(cacheKey, optimized);
25112
25603
  }
25113
25604
  return createOptimizedImageResponse(request, optimized, config);
25114
25605
  } catch (error) {
@@ -25259,6 +25750,44 @@ async function fetchImageSource(initialUrl, requestOrigin, config, fetcher, fetc
25259
25750
  await validateImageSourceUrl(currentUrl, requestOrigin, config, validateRemoteUrl);
25260
25751
  }
25261
25752
  }
25753
+ async function runCoalesced(inflight, key, requestSignal, run) {
25754
+ let entry = inflight.get(key);
25755
+ if (!entry) {
25756
+ const controller = new AbortController();
25757
+ const created = {
25758
+ controller,
25759
+ waiters: 0,
25760
+ promise: void 0
25761
+ };
25762
+ created.promise = run(controller.signal).finally(() => {
25763
+ if (inflight.get(key) === created) inflight.delete(key);
25764
+ });
25765
+ created.promise.catch(() => {
25766
+ });
25767
+ inflight.set(key, created);
25768
+ entry = created;
25769
+ }
25770
+ const pending = entry;
25771
+ pending.waiters += 1;
25772
+ try {
25773
+ return await raceRequestAbort(pending.promise, requestSignal);
25774
+ } finally {
25775
+ pending.waiters -= 1;
25776
+ if (pending.waiters === 0 && inflight.get(key) === pending) {
25777
+ inflight.delete(key);
25778
+ pending.controller.abort();
25779
+ }
25780
+ }
25781
+ }
25782
+ function raceRequestAbort(promise, signal) {
25783
+ if (!signal) return promise;
25784
+ if (signal.aborted) return Promise.reject(signal.reason ?? new Error("Aborted"));
25785
+ return new Promise((resolve10, reject) => {
25786
+ const onAbort = /* @__PURE__ */ __name(() => reject(signal.reason ?? new Error("Aborted")), "onAbort");
25787
+ signal.addEventListener("abort", onAbort, { once: true });
25788
+ promise.then(resolve10, reject).finally(() => signal.removeEventListener("abort", onAbort));
25789
+ });
25790
+ }
25262
25791
  async function readResponseWithLimit(response, limit) {
25263
25792
  const contentLength = response.headers.get("content-length");
25264
25793
  if (contentLength && Number(contentLength) > limit) {
@@ -25426,7 +25955,7 @@ function throwIfAborted3(signal) {
25426
25955
  function isAbortError(error) {
25427
25956
  return error instanceof Error && error.name === "AbortError";
25428
25957
  }
25429
- var _FarmImageRequestError, FarmImageRequestError, _FarmImageMemoryCache, FarmImageMemoryCache;
25958
+ var _FarmImageRequestError, FarmImageRequestError, DEFAULT_IMAGE_TRANSFORM_BODY_LIMIT, _FarmImageMemoryCache, FarmImageMemoryCache;
25430
25959
  var init_image_server = __esm({
25431
25960
  "src/image-server.ts"() {
25432
25961
  "use strict";
@@ -25442,12 +25971,15 @@ var init_image_server = __esm({
25442
25971
  __name(_FarmImageRequestError, "FarmImageRequestError");
25443
25972
  FarmImageRequestError = _FarmImageRequestError;
25444
25973
  __name(createFarmImageHandler, "createFarmImageHandler");
25974
+ DEFAULT_IMAGE_TRANSFORM_BODY_LIMIT = 10 * 1024 * 1024;
25445
25975
  __name(selectOutputFormat, "selectOutputFormat");
25446
25976
  __name(isPrivateImageAddress, "isPrivateImageAddress");
25447
25977
  __name(parseAllowedInteger, "parseAllowedInteger");
25448
25978
  __name(resolveImageSourceUrl, "resolveImageSourceUrl");
25449
25979
  __name(validateImageSourceUrl, "validateImageSourceUrl");
25450
25980
  __name(fetchImageSource, "fetchImageSource");
25981
+ __name(runCoalesced, "runCoalesced");
25982
+ __name(raceRequestAbort, "raceRequestAbort");
25451
25983
  __name(readResponseWithLimit, "readResponseWithLimit");
25452
25984
  __name(cancelResponseBody, "cancelResponseBody");
25453
25985
  __name(detectImageContentType, "detectImageContentType");
@@ -28668,6 +29200,7 @@ async function resolveConfig(userConfig, mode) {
28668
29200
  const openapi = {
28669
29201
  enabled: false,
28670
29202
  route: "/docs/reference",
29203
+ specRoute: "/openapi.json",
28671
29204
  title: "API Documentation",
28672
29205
  description: "Auto-generated API documentation",
28673
29206
  version: "1.0.0",
@@ -28675,6 +29208,7 @@ async function resolveConfig(userConfig, mode) {
28675
29208
  ...userConfig.openapi
28676
29209
  };
28677
29210
  if (openapi.route !== void 0) validateConfigRouteSource(openapi.route, "openapi.route");
29211
+ if (openapi.specRoute) validateConfigRouteSource(openapi.specRoute, "openapi.specRoute");
28678
29212
  const resolved = {
28679
29213
  [FARM_RESOLVED_CUSTOM_CONTEXT]: typeof userConfig.context === "function",
28680
29214
  root,
@@ -28707,6 +29241,7 @@ async function resolveConfig(userConfig, mode) {
28707
29241
  ppr: false,
28708
29242
  ...userConfig.experimental
28709
29243
  },
29244
+ agent: resolveFarmAgentConfig(userConfig.agent),
28710
29245
  plugins: [...resolveIntegrationPlugins(integrations), ...userConfig.plugins || []],
28711
29246
  integrations,
28712
29247
  trailingSlash: userConfig.trailingSlash ?? false,
@@ -28815,6 +29350,7 @@ var init_config4 = __esm({
28815
29350
  init_security();
28816
29351
  init_navigation_errors();
28817
29352
  init_config2();
29353
+ init_agent_config();
28818
29354
  init_renderer();
28819
29355
  init_framework_detect();
28820
29356
  init_config3();
@@ -32669,6 +33205,22 @@ function isFarmConfigFile(file, root) {
32669
33205
  const relative6 = normalized.startsWith(`${rootSlug}/`) ? normalized.slice(rootSlug.length + 1) : normalized;
32670
33206
  return FARM_CONFIG_FILENAMES.has(relative6);
32671
33207
  }
33208
+ function warnClientBoundaryOnce(context2, id, code, config) {
33209
+ if (warnedClientBoundaryIds.has(id)) return;
33210
+ let program;
33211
+ try {
33212
+ program = context2.parse(code);
33213
+ } catch {
33214
+ return;
33215
+ }
33216
+ const publicKeys = new Set(Object.keys(config.env?.public ?? {}));
33217
+ const findings = analyzeClientBoundary(program, publicKeys);
33218
+ if (findings.envKeys.length === 0 && findings.publicEnvKeys.length === 0 && findings.builtinImports.length === 0) {
33219
+ return;
33220
+ }
33221
+ warnedClientBoundaryIds.add(id);
33222
+ logger.warn(formatClientBoundaryWarning(id, findings));
33223
+ }
32672
33224
  function farmPlugin(options = {}, initialPluginManager) {
32673
33225
  const imageImports = farmImageImportsPlugin();
32674
33226
  const fontImports = farmFontImportsPlugin({
@@ -33279,6 +33831,22 @@ window.__FARM_MANIFEST__ = ${inlineValue({
33279
33831
  }
33280
33832
  return;
33281
33833
  }
33834
+ if (openAPIManager && options.openapi?.specRoute && requestPathname === options.openapi.specRoute) {
33835
+ if (requestMethod !== "GET" && requestMethod !== "HEAD") {
33836
+ res.statusCode = 405;
33837
+ res.setHeader("Allow", "GET, HEAD");
33838
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
33839
+ res.end("Method Not Allowed");
33840
+ return;
33841
+ }
33842
+ const spec = await openAPIManager.getSpec();
33843
+ const body = JSON.stringify(spec);
33844
+ res.statusCode = 200;
33845
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
33846
+ res.setHeader("Cache-Control", "no-store");
33847
+ res.end(requestMethod === "HEAD" ? void 0 : body);
33848
+ return;
33849
+ }
33282
33850
  if (openAPIManager && requestPathname === options.openapi?.route) {
33283
33851
  const docsHandler = openAPIManager.getDocsRouteHandler();
33284
33852
  return docsHandler(req, res);
@@ -33289,6 +33857,11 @@ window.__FARM_MANIFEST__ = ${inlineValue({
33289
33857
  docsHeaders.set(key, Array.isArray(value) ? value.join(", ") : value);
33290
33858
  }
33291
33859
  }
33860
+ const runAppMiddlewareForContentRoute = /* @__PURE__ */ __name(async () => {
33861
+ if (!middlewareManager?.hasMiddleware()) return false;
33862
+ const middlewareRequest = createRequestFromNodeRequest(req, new URL(fullUrl));
33863
+ return farmApp.getServerRenderer().runWithRequestContext(middlewareRequest, () => middlewareManager.execute(req, res));
33864
+ }, "runAppMiddlewareForContentRoute");
33292
33865
  if (farmDocsHandler) {
33293
33866
  const docsRequest = new Request(fullUrl, {
33294
33867
  method: requestMethod,
@@ -33296,6 +33869,7 @@ window.__FARM_MANIFEST__ = ${inlineValue({
33296
33869
  });
33297
33870
  const docsResponse = await farmDocsHandler(docsRequest.clone());
33298
33871
  if (docsResponse) {
33872
+ if (await runAppMiddlewareForContentRoute()) return;
33299
33873
  await sendWebResponse(
33300
33874
  res,
33301
33875
  await wrapFarmDocsResponseWithLayouts(docsRequest, docsResponse)
@@ -33322,6 +33896,7 @@ window.__FARM_MANIFEST__ = ${inlineValue({
33322
33896
  }, "resolveSource")
33323
33897
  });
33324
33898
  if (markdownSourceResponse) {
33899
+ if (await runAppMiddlewareForContentRoute()) return;
33325
33900
  await sendWebResponse(res, markdownSourceResponse);
33326
33901
  return;
33327
33902
  }
@@ -33335,9 +33910,23 @@ window.__FARM_MANIFEST__ = ${inlineValue({
33335
33910
  renderPage: /* @__PURE__ */ __name(async (request) => fetch(request), "renderPage")
33336
33911
  });
33337
33912
  if (markdownResponse) {
33913
+ if (await runAppMiddlewareForContentRoute()) return;
33338
33914
  await sendWebResponse(res, markdownResponse);
33339
33915
  return;
33340
33916
  }
33917
+ if (farmRequestWantsMarkdown(requestPathname, req.headers.accept)) {
33918
+ const markdownRoute = farmApp.getRouteManager().matchRoute(normalizeFarmMarkdownRoutePath(requestPathname));
33919
+ if (!markdownRoute.route) {
33920
+ res.statusCode = 404;
33921
+ res.setHeader("Content-Type", FARM_MARKDOWN_CONTENT_TYPE);
33922
+ res.setHeader("X-Farm-Markdown-Error", "404");
33923
+ res.setHeader("Cache-Control", "no-store");
33924
+ res.end(
33925
+ createFarmMarkdownErrorBody(404, requestPathname, farmConfig.basePath || "/")
33926
+ );
33927
+ return;
33928
+ }
33929
+ }
33341
33930
  const markdownPageTarget = resolveMarkdownMirrorTarget(
33342
33931
  farmApp.getConfig().md,
33343
33932
  requestPathname,
@@ -34286,6 +34875,9 @@ export const manifest = getManifest();
34286
34875
  }
34287
34876
  }
34288
34877
  }
34878
+ if (!transformOptions?.ssr && shouldInspectClientBoundary(id, code)) {
34879
+ warnClientBoundaryOnce(this, id, code, farmApp?.getConfig() ?? options);
34880
+ }
34289
34881
  if (typeof imageImports.transform === "function") {
34290
34882
  const imageModule = await imageImports.transform.call(this, code, id, transformOptions);
34291
34883
  if (imageModule) return imageModule;
@@ -36625,7 +37217,7 @@ async function defineConfig2(config = {}) {
36625
37217
  }
36626
37218
  };
36627
37219
  }
36628
- var fs13, path34, import_node_url8, ALL_TYPE_ARTIFACTS, createEmptyTypeArtifactSelection, FARM_I18N_CLIENT_BRIDGE_ID, warnedDeprecatedDevtoolsDashboard, EMPTY_FARM_DOCS_SEARCH_CLIENT_RUNTIME, FARM_CONFIG_FILENAMES;
37220
+ var fs13, path34, import_node_url8, ALL_TYPE_ARTIFACTS, createEmptyTypeArtifactSelection, FARM_I18N_CLIENT_BRIDGE_ID, warnedDeprecatedDevtoolsDashboard, EMPTY_FARM_DOCS_SEARCH_CLIENT_RUNTIME, FARM_CONFIG_FILENAMES, warnedClientBoundaryIds;
36629
37221
  var init_vite2 = __esm({
36630
37222
  "src/vite.ts"() {
36631
37223
  "use strict";
@@ -36662,6 +37254,7 @@ var init_vite2 = __esm({
36662
37254
  init_server_context();
36663
37255
  init_dev_static();
36664
37256
  init_server_query_boundary();
37257
+ init_client_boundary_env();
36665
37258
  init_deployment();
36666
37259
  init_image_config();
36667
37260
  init_image_vite();
@@ -36761,6 +37354,8 @@ async function mountFarmDocsSearch() {
36761
37354
  __name(walkModuleAst, "walkModuleAst");
36762
37355
  __name(isFarmModuleAstNode, "isFarmModuleAstNode");
36763
37356
  __name(isFarmConfigFile, "isFarmConfigFile");
37357
+ warnedClientBoundaryIds = /* @__PURE__ */ new Set();
37358
+ __name(warnClientBoundaryOnce, "warnClientBoundaryOnce");
36764
37359
  __name(farmPlugin, "farmPlugin");
36765
37360
  __name(isFarmClientVirtualId, "isFarmClientVirtualId");
36766
37361
  __name(generateProgrammaticRouteModule, "generateProgrammaticRouteModule");
@@ -38129,6 +38724,7 @@ __export(src_exports, {
38129
38724
  FARM_DEVTOOLS_LAUNCH_PARAM: () => FARM_DEVTOOLS_LAUNCH_PARAM,
38130
38725
  FARM_DEVTOOLS_PATH: () => FARM_DEVTOOLS_PATH,
38131
38726
  FARM_INTEGRATION_INTERNAL_DISPATCH_CONTEXT_KEY: () => FARM_INTEGRATION_INTERNAL_DISPATCH_CONTEXT_KEY,
38727
+ FARM_MARKDOWN_CONTENT_TYPE: () => FARM_MARKDOWN_CONTENT_TYPE,
38132
38728
  FARM_SERVER_FN_FAILURE_SYMBOL: () => FARM_SERVER_FN_FAILURE_SYMBOL,
38133
38729
  FARM_SERVER_FN_SYMBOL: () => FARM_SERVER_FN_SYMBOL,
38134
38730
  FARM_SERVER_QUERY_SYMBOL: () => FARM_SERVER_QUERY_SYMBOL,
@@ -38219,6 +38815,7 @@ __export(src_exports, {
38219
38815
  createFarmDocsAdapterHandler: () => createFarmDocsAdapterHandler,
38220
38816
  createFarmDocsHandler: () => createFarmDocsHandler,
38221
38817
  createFarmImageTypeDeclarations: () => createFarmImageTypeDeclarations,
38818
+ createFarmMarkdownErrorBody: () => createFarmMarkdownErrorBody,
38222
38819
  createFarmMarkdownRouteModule: () => createFarmMarkdownRouteModule,
38223
38820
  createFarmMarkdownRouteModuleFromFile: () => createFarmMarkdownRouteModuleFromFile,
38224
38821
  createFarmMarkdownSourceResponse: () => createFarmMarkdownSourceResponse,
@@ -38279,6 +38876,7 @@ __export(src_exports, {
38279
38876
  defineStorageClient: () => defineStorageClient,
38280
38877
  defineTask: () => defineTask,
38281
38878
  defineWorkflow: () => defineWorkflow,
38879
+ describeIntegrationOriginRejection: () => describeIntegrationOriginRejection,
38282
38880
  discoverFarmDocsPages: () => discoverFarmDocsPages,
38283
38881
  discoverFarmWorkflows: () => discoverFarmWorkflows,
38284
38882
  dispatchIntegrationRequest: () => dispatchIntegrationRequest,
@@ -38291,6 +38889,7 @@ __export(src_exports, {
38291
38889
  farmApiPlugin: () => farmApiPlugin,
38292
38890
  farmIntegrationSchemaToOrmSchema: () => farmIntegrationSchemaToOrmSchema,
38293
38891
  farmMiddlewarePlugin: () => farmMiddlewarePlugin,
38892
+ farmRequestWantsMarkdown: () => farmRequestWantsMarkdown,
38294
38893
  farmRouteRuleMatches: () => farmRouteRuleMatches,
38295
38894
  fetchServerQuery: () => fetchServerQuery,
38296
38895
  fileExists: () => fileExists,
@@ -38475,6 +39074,7 @@ __export(src_exports, {
38475
39074
  resolveFarmRouteRuleRuntimeConfig: () => resolveFarmRouteRuleRuntimeConfig,
38476
39075
  resolveFarmRouteRuntimeConfig: () => resolveFarmRouteRuntimeConfig,
38477
39076
  resolveFarmThemeConfig: () => resolveFarmThemeConfig,
39077
+ resolveIntegrationAllowedOrigins: () => resolveIntegrationAllowedOrigins,
38478
39078
  resolveIntegrationOrmRuntimeClient: () => resolveIntegrationOrmRuntimeClient,
38479
39079
  resolveIntegrationPlugins: () => resolveIntegrationPlugins,
38480
39080
  resolveMarkdownConfig: () => resolveMarkdownConfig,
@@ -38535,6 +39135,7 @@ __export(src_exports, {
38535
39135
  useServerFn: () => useServerFn,
38536
39136
  useServerQuery: () => useServerQuery,
38537
39137
  useTheme: () => useTheme,
39138
+ validateIntegrationRequestOrigin: () => validateIntegrationRequestOrigin,
38538
39139
  validateServerActionRequest: () => validateServerActionRequest,
38539
39140
  vercelBlobStorage: () => vercelBlobStorage,
38540
39141
  vercelKVStorage: () => vercelKVStorage,
@@ -40247,6 +40848,7 @@ var _FarmClientDataCache = class _FarmClientDataCache {
40247
40848
  const watched = /* @__PURE__ */ new Set();
40248
40849
  for (const key of this.listeners.keys()) watched.add(this.resolveKey(key));
40249
40850
  let remaining = false;
40851
+ const swept = /* @__PURE__ */ new Set();
40250
40852
  for (const [key, entry] of this.entries) {
40251
40853
  if (entry.gcAt === void 0) continue;
40252
40854
  if (now2 < entry.gcAt || entry.fetching || this.inflight.has(key) || watched.has(key)) {
@@ -40255,9 +40857,28 @@ var _FarmClientDataCache = class _FarmClientDataCache {
40255
40857
  }
40256
40858
  this.entries.delete(key);
40257
40859
  this.persistence?.onDelete(key);
40860
+ swept.add(key);
40258
40861
  }
40862
+ if (swept.size > 0) this.sweepEntryMetadata(swept);
40259
40863
  if (remaining) this.scheduleGcSweep();
40260
40864
  }
40865
+ /**
40866
+ * Entry eviction alone leaves the per-key metadata behind. `invalidatedAt`
40867
+ * marks and provisional aliases are created per query invocation, so with
40868
+ * dynamic keys they accumulate for the lifetime of the page even though the
40869
+ * entries they describe are long gone.
40870
+ */
40871
+ sweepEntryMetadata(swept) {
40872
+ for (const key of swept) this.invalidatedAt.delete(key);
40873
+ for (const [alias, target] of this.aliases) {
40874
+ if (this.entries.has(alias) || this.listeners.has(alias)) continue;
40875
+ const resolved = this.resolveKey(target);
40876
+ if (!swept.has(resolved)) continue;
40877
+ if (this.entries.has(resolved) || this.listeners.has(resolved)) continue;
40878
+ this.aliases.delete(alias);
40879
+ this.invalidatedAt.delete(alias);
40880
+ }
40881
+ }
40261
40882
  emit(key, event) {
40262
40883
  this.notifyListeners(key, event);
40263
40884
  for (const [alias, target] of this.aliases) {
@@ -40268,7 +40889,12 @@ var _FarmClientDataCache = class _FarmClientDataCache {
40268
40889
  }
40269
40890
  notifyListeners(key, event) {
40270
40891
  for (const listener of this.listeners.get(key) ?? []) {
40271
- listener(event);
40892
+ try {
40893
+ listener(event);
40894
+ } catch (error) {
40895
+ const detail = error instanceof Error ? error.stack ?? error.message : String(error);
40896
+ console.warn(`[farm:client-cache] cache listener failed: ${detail}`);
40897
+ }
40272
40898
  }
40273
40899
  }
40274
40900
  };
@@ -48589,7 +49215,9 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
48589
49215
  }
48590
49216
  openAPIReference = {
48591
49217
  route: config.openapi.route,
48592
- html: renderOpenAPIReferenceHTML2(spec, config.openapi)
49218
+ html: renderOpenAPIReferenceHTML2(spec, config.openapi),
49219
+ spec,
49220
+ specRoute: config.openapi.specRoute || null
48593
49221
  };
48594
49222
  }
48595
49223
  const middlewareRoutes = await discoverMiddlewareRoutes(appDirs);
@@ -49211,6 +49839,7 @@ import { dirname as farmDocsDirname, join as farmDocsJoin } from "node:path";
49211
49839
  import { fileURLToPath as farmDocsFileURLToPath } from "node:url";` : "";
49212
49840
  const markdownHandlerImport = config.md?.enabled ? `import { applyMarkdownNegotiationHeaders, createMarkdownMirrorResponse } from "@farm.js/core/markdown";` : "";
49213
49841
  const appMarkdownImport = hasMarkdownPages ? `import { createFarmMarkdownRouteModule, createFarmMarkdownSourceResponse } from "@farm.js/core/app-markdown";` : "const createFarmMarkdownSourceResponse = null;";
49842
+ const markdownErrorImport = `import { FARM_MARKDOWN_CONTENT_TYPE, createFarmMarkdownErrorBody, farmRequestWantsMarkdown } from "@farm.js/core/app-markdown";`;
49214
49843
  const mdxComponentsPath = typeof config.mdx?.components === "string" ? import_path14.default.isAbsolute(config.mdx.components) ? config.mdx.components : import_path14.default.join(config.root, config.mdx.components) : null;
49215
49844
  const mdxComponentsImport = mdxComponentsPath ? `import * as FarmMdxComponentsModule from ${toVirtualEntryImportSpecifier(mdxComponentsPath)};` : "";
49216
49845
  const layerConfigPaths = (config.layers || []).map((layer) => layer.configFile).filter((configFile) => Boolean(configFile)).filter((configFile) => configFile !== configModulePath);
@@ -49353,6 +49982,7 @@ ${docsFontImport}
49353
49982
  ${docsRuntimeImport}
49354
49983
  ${markdownHandlerImport}
49355
49984
  ${appMarkdownImport}
49985
+ ${markdownErrorImport}
49356
49986
  ${mdxComponentsImport}
49357
49987
  ${integrationImports}
49358
49988
  ${providerServerImports}
@@ -50896,12 +51526,38 @@ async function handleFarmRequestInContext(
50896
51526
  }
50897
51527
  ` : ""}
50898
51528
 
51529
+ ${hasMiddlewareRuntime ? `
51530
+ const requestBeforeMiddleware = request;
51531
+ const middlewareResult = await farmMiddlewareRunner(request);
51532
+ if (middlewareResult.response) {
51533
+ return middlewareResult.response;
51534
+ }
51535
+ request = middlewareResult.request;
51536
+ const middlewareData = middlewareResult.data;
51537
+ const middlewareContext = middlewareResult.context;
51538
+ const middlewareHeaders = middlewareResult.headers;
51539
+ url = new URL(request.url);
51540
+ pathname = url.pathname;
51541
+ routePathname = getFarmRoutePathname(pathname);
51542
+
51543
+ // Middleware may replace the Request when it rewrites a URL. Re-enter the
51544
+ // request store so pages, layouts, and route context observe that rewritten
51545
+ // request instead of the original outer-handler value.
51546
+ const runResolvedRequest = async () => {
51547
+ ` : `
51548
+ const middlewareData = undefined;
51549
+ const middlewareContext = undefined;
51550
+ const middlewareHeaders = undefined;
51551
+ `}
51552
+
50899
51553
  ${config.docs?.enabled ? `
51554
+ // Docs responses are served after app middleware so route guards and
51555
+ // middleware headers apply to the docs engine like any other page content.
50900
51556
  if (farmDocsHandler) {
50901
51557
  const docsResponse = await farmDocsHandler(request.clone());
50902
51558
  if (docsResponse) {
50903
51559
  if (!docsResponse.headers.get("content-type")?.toLowerCase().includes("text/html")) {
50904
- return docsResponse;
51560
+ return applyProductionMiddlewareHeaders(docsResponse, middlewareHeaders);
50905
51561
  }
50906
51562
  const wrappedDocsResponse = await wrapFarmDocsResponseWithLayouts(
50907
51563
  request,
@@ -50909,16 +51565,18 @@ async function handleFarmRequestInContext(
50909
51565
  );
50910
51566
  const docsHeaders = new Headers(wrappedDocsResponse.headers);
50911
51567
  docsHeaders.set("x-farm-preload-buffered", "1");
50912
- return new Response(wrappedDocsResponse.body, {
51568
+ return applyProductionMiddlewareHeaders(new Response(wrappedDocsResponse.body, {
50913
51569
  status: wrappedDocsResponse.status,
50914
51570
  statusText: wrappedDocsResponse.statusText,
50915
51571
  headers: docsHeaders,
50916
- });
51572
+ }), middlewareHeaders);
50917
51573
  }
50918
51574
  }
50919
51575
  ` : ""}
50920
51576
 
50921
51577
  ${hasMarkdownPages ? `
51578
+ // The raw markdown source of a page route is the page's content in another
51579
+ // representation; middleware guarding the route must run before serving it.
50922
51580
  const markdownSourceResponse = await createFarmMarkdownSourceResponse?.({
50923
51581
  request: request.clone(),
50924
51582
  config: farmMdxConfig,
@@ -50928,7 +51586,7 @@ async function handleFarmRequestInContext(
50928
51586
  },
50929
51587
  });
50930
51588
  if (markdownSourceResponse) {
50931
- return markdownSourceResponse;
51589
+ return applyProductionMiddlewareHeaders(markdownSourceResponse, middlewareHeaders);
50932
51590
  }
50933
51591
  ` : ""}
50934
51592
 
@@ -50942,35 +51600,11 @@ async function handleFarmRequestInContext(
50942
51600
  renderPage: (targetRequest) => handleFarmRequest(targetRequest),
50943
51601
  });
50944
51602
  if (markdownResponse) {
50945
- return markdownResponse;
51603
+ return applyProductionMiddlewareHeaders(markdownResponse, middlewareHeaders);
50946
51604
  }
50947
51605
  }
50948
51606
  ` : ""}
50949
51607
 
50950
- ${hasMiddlewareRuntime ? `
50951
- const requestBeforeMiddleware = request;
50952
- const middlewareResult = await farmMiddlewareRunner(request);
50953
- if (middlewareResult.response) {
50954
- return middlewareResult.response;
50955
- }
50956
- request = middlewareResult.request;
50957
- const middlewareData = middlewareResult.data;
50958
- const middlewareContext = middlewareResult.context;
50959
- const middlewareHeaders = middlewareResult.headers;
50960
- url = new URL(request.url);
50961
- pathname = url.pathname;
50962
- routePathname = getFarmRoutePathname(pathname);
50963
-
50964
- // Middleware may replace the Request when it rewrites a URL. Re-enter the
50965
- // request store so pages, layouts, and route context observe that rewritten
50966
- // request instead of the original outer-handler value.
50967
- const runResolvedRequest = async () => {
50968
- ` : `
50969
- const middlewareData = undefined;
50970
- const middlewareContext = undefined;
50971
- const middlewareHeaders = undefined;
50972
- `}
50973
-
50974
51608
  ${apiRoutes.length > 0 ? `
50975
51609
  const apiResponse = await handleAPIRequest(request.clone());
50976
51610
  if (apiResponse) {
@@ -50978,6 +51612,31 @@ async function handleFarmRequestInContext(
50978
51612
  }
50979
51613
  ` : ""}
50980
51614
 
51615
+ if (
51616
+ farmOpenAPIReference &&
51617
+ farmOpenAPIReference.specRoute &&
51618
+ normalizeRuntimePath(pathname) === normalizeRuntimePath(farmOpenAPIReference.specRoute)
51619
+ ) {
51620
+ const method = request.method.toUpperCase();
51621
+ const specResponse = method === "GET" || method === "HEAD"
51622
+ ? new Response(method === "HEAD" ? null : JSON.stringify(farmOpenAPIReference.spec), {
51623
+ status: 200,
51624
+ headers: {
51625
+ "Content-Type": "application/json; charset=utf-8",
51626
+ "Cache-Control": "public, max-age=0, must-revalidate",
51627
+ "X-Content-Type-Options": "nosniff",
51628
+ },
51629
+ })
51630
+ : new Response("Method Not Allowed", {
51631
+ status: 405,
51632
+ headers: {
51633
+ "Allow": "GET, HEAD",
51634
+ "Content-Type": "text/plain; charset=utf-8",
51635
+ },
51636
+ });
51637
+ return applyProductionMiddlewareHeaders(specResponse, middlewareHeaders);
51638
+ }
51639
+
50981
51640
  if (
50982
51641
  farmOpenAPIReference &&
50983
51642
  normalizeRuntimePath(pathname) === normalizeRuntimePath(farmOpenAPIReference.route)
@@ -51450,7 +52109,7 @@ async function handleFarmRequestInContext(
51450
52109
  }
51451
52110
  }
51452
52111
 
51453
- const renderedMetadata = renderMetadataHead(mergedMetadata);
52112
+ const renderedMetadata = renderMetadataHead(mergedMetadata, { pathname, jsonLd: ${JSON.stringify(config.agent?.jsonLd ?? false)} });
51454
52113
  const title = renderedMetadata.title;
51455
52114
  const metaTags = renderedMetadata.tags;
51456
52115
  const hasFavicon = renderedMetadata.hasFavicon;
@@ -51826,6 +52485,30 @@ async function handleFarmRequestInContext(
51826
52485
 
51827
52486
  // 404 fallback - render proper HTML page
51828
52487
  emitFarmEvent({ type: "route.notFound", pathname });
52488
+
52489
+ // Agents that navigate in Markdown (a \`.md\` URL or \`Accept: text/markdown\`)
52490
+ // get a Markdown error body instead of the HTML not-found shell.
52491
+ if (farmRequestWantsMarkdown(pathname, request.headers.get("accept"))) {
52492
+ emitFarmEvent({
52493
+ type: "render.complete",
52494
+ route: pathname,
52495
+ pathname,
52496
+ status: 404,
52497
+ durationMs: Date.now() - requestStartTime,
52498
+ });
52499
+ return applyProductionMiddlewareHeaders(new Response(
52500
+ createFarmMarkdownErrorBody(404, pathname, applyFarmBasePath("/", farmResolvedRuntimeConfig.basePath)),
52501
+ {
52502
+ status: 404,
52503
+ headers: {
52504
+ "Content-Type": FARM_MARKDOWN_CONTENT_TYPE,
52505
+ "X-Farm-Markdown-Error": "404",
52506
+ "Cache-Control": "no-store",
52507
+ },
52508
+ }
52509
+ ), middlewareHeaders);
52510
+ }
52511
+
51829
52512
  try {
51830
52513
  const defaultNotFoundHomeHref = applyFarmBasePath("/", farmResolvedRuntimeConfig.basePath);
51831
52514
  // Default 404 page component
@@ -52065,6 +52748,9 @@ async function handleFarmFetch(request, context) {
52065
52748
  const healthResponse = await farmProductionLifecycle.handleHealthRequest(request);
52066
52749
  if (healthResponse) return healthResponse;
52067
52750
 
52751
+ const productionSiteAttestation = farmProductionSiteTelemetry?.handleAttestation(request);
52752
+ if (productionSiteAttestation) return productionSiteAttestation;
52753
+
52068
52754
  farmProductionSiteTelemetry?.report(
52069
52755
  request.url,
52070
52756
  typeof context?.waitUntil === "function" ? context.waitUntil.bind(context) : undefined,
@@ -53922,29 +54608,36 @@ __name(isFormData4, "isFormData");
53922
54608
  // src/cache-invalidation-broadcast.ts
53923
54609
  init_cache_invalidation();
53924
54610
  var FARM_CACHE_INVALIDATION_CHANNEL = "farm:cache-invalidation";
53925
- var activeBridge;
54611
+ var FARM_CACHE_INVALIDATION_BRIDGE = /* @__PURE__ */ Symbol.for("farm.cacheInvalidationBridge");
54612
+ var bridgeGlobal = globalThis;
53926
54613
  function enableCrossTabCacheInvalidation(options = {}) {
53927
54614
  if (typeof BroadcastChannel !== "function") return () => {
53928
54615
  };
53929
54616
  const channelName = options.channelName ?? FARM_CACHE_INVALIDATION_CHANNEL;
53930
- if (activeBridge) {
53931
- if (activeBridge.channelName !== channelName) {
54617
+ const existing = bridgeGlobal[FARM_CACHE_INVALIDATION_BRIDGE];
54618
+ let bridge;
54619
+ if (existing) {
54620
+ if (existing.channelName !== channelName) {
53932
54621
  throw new Error(
53933
- `Cross-tab cache invalidation is already enabled on channel "${activeBridge.channelName}".`
54622
+ `Cross-tab cache invalidation is already enabled on channel "${existing.channelName}".`
53934
54623
  );
53935
54624
  }
53936
- activeBridge.refCount += 1;
54625
+ existing.refCount += 1;
54626
+ bridge = existing;
53937
54627
  } else {
53938
- activeBridge = createBridge(channelName);
54628
+ bridge = createBridge(channelName);
54629
+ bridgeGlobal[FARM_CACHE_INVALIDATION_BRIDGE] = bridge;
53939
54630
  }
53940
54631
  let disposed = false;
53941
54632
  return () => {
53942
- if (disposed || !activeBridge) return;
54633
+ if (disposed) return;
53943
54634
  disposed = true;
53944
- activeBridge.refCount -= 1;
53945
- if (activeBridge.refCount === 0) {
53946
- activeBridge.teardown();
53947
- activeBridge = void 0;
54635
+ bridge.refCount -= 1;
54636
+ if (bridge.refCount === 0) {
54637
+ bridge.teardown();
54638
+ if (bridgeGlobal[FARM_CACHE_INVALIDATION_BRIDGE] === bridge) {
54639
+ bridgeGlobal[FARM_CACHE_INVALIDATION_BRIDGE] = void 0;
54640
+ }
53948
54641
  }
53949
54642
  };
53950
54643
  }
@@ -54152,7 +54845,7 @@ function createSink(engine) {
54152
54845
  scheduleFlush(engine);
54153
54846
  },
54154
54847
  onClear() {
54155
- if (engine.disabled) return;
54848
+ if (engine.disabled || engine.clearing) return;
54156
54849
  engine.pendingSets.clear();
54157
54850
  engine.pendingDeletes.clear();
54158
54851
  void engine.adapter.clear().catch((error) => disableEngine(engine, error));
@@ -54173,7 +54866,8 @@ function initPersistedClientCache(adapter, options = {}) {
54173
54866
  pendingDeletes: /* @__PURE__ */ new Set(),
54174
54867
  flushTimer: void 0,
54175
54868
  hydrating: false,
54176
- disabled: false
54869
+ disabled: false,
54870
+ clearing: false
54177
54871
  };
54178
54872
  activeEngine = engine;
54179
54873
  engine.cache.attachPersistence(createSink(engine));
@@ -54188,6 +54882,7 @@ function disposePersistedClientCache() {
54188
54882
  const engine = activeEngine;
54189
54883
  if (!engine) return;
54190
54884
  activeEngine = void 0;
54885
+ engine.disabled = true;
54191
54886
  engine.cache.attachPersistence(void 0);
54192
54887
  if (engine.flushTimer !== void 0) {
54193
54888
  clearTimeout(engine.flushTimer);
@@ -54199,7 +54894,18 @@ function disposePersistedClientCache() {
54199
54894
  __name(disposePersistedClientCache, "disposePersistedClientCache");
54200
54895
  async function clearPersistedCache() {
54201
54896
  const engine = activeEngine;
54202
- if (!engine) return;
54897
+ if (!engine) {
54898
+ getFarmClientDataCache().clear();
54899
+ return;
54900
+ }
54901
+ engine.pendingSets.clear();
54902
+ engine.pendingDeletes.clear();
54903
+ engine.clearing = true;
54904
+ try {
54905
+ engine.cache.clear();
54906
+ } finally {
54907
+ engine.clearing = false;
54908
+ }
54203
54909
  engine.pendingSets.clear();
54204
54910
  engine.pendingDeletes.clear();
54205
54911
  try {
@@ -57522,6 +58228,7 @@ __name(decodeCookiePart, "decodeCookiePart");
57522
58228
  FARM_DEVTOOLS_LAUNCH_PARAM,
57523
58229
  FARM_DEVTOOLS_PATH,
57524
58230
  FARM_INTEGRATION_INTERNAL_DISPATCH_CONTEXT_KEY,
58231
+ FARM_MARKDOWN_CONTENT_TYPE,
57525
58232
  FARM_SERVER_FN_FAILURE_SYMBOL,
57526
58233
  FARM_SERVER_FN_SYMBOL,
57527
58234
  FARM_SERVER_QUERY_SYMBOL,
@@ -57612,6 +58319,7 @@ __name(decodeCookiePart, "decodeCookiePart");
57612
58319
  createFarmDocsAdapterHandler,
57613
58320
  createFarmDocsHandler,
57614
58321
  createFarmImageTypeDeclarations,
58322
+ createFarmMarkdownErrorBody,
57615
58323
  createFarmMarkdownRouteModule,
57616
58324
  createFarmMarkdownRouteModuleFromFile,
57617
58325
  createFarmMarkdownSourceResponse,
@@ -57672,6 +58380,7 @@ __name(decodeCookiePart, "decodeCookiePart");
57672
58380
  defineStorageClient,
57673
58381
  defineTask,
57674
58382
  defineWorkflow,
58383
+ describeIntegrationOriginRejection,
57675
58384
  discoverFarmDocsPages,
57676
58385
  discoverFarmWorkflows,
57677
58386
  dispatchIntegrationRequest,
@@ -57684,6 +58393,7 @@ __name(decodeCookiePart, "decodeCookiePart");
57684
58393
  farmApiPlugin,
57685
58394
  farmIntegrationSchemaToOrmSchema,
57686
58395
  farmMiddlewarePlugin,
58396
+ farmRequestWantsMarkdown,
57687
58397
  farmRouteRuleMatches,
57688
58398
  fetchServerQuery,
57689
58399
  fileExists,
@@ -57868,6 +58578,7 @@ __name(decodeCookiePart, "decodeCookiePart");
57868
58578
  resolveFarmRouteRuleRuntimeConfig,
57869
58579
  resolveFarmRouteRuntimeConfig,
57870
58580
  resolveFarmThemeConfig,
58581
+ resolveIntegrationAllowedOrigins,
57871
58582
  resolveIntegrationOrmRuntimeClient,
57872
58583
  resolveIntegrationPlugins,
57873
58584
  resolveMarkdownConfig,
@@ -57928,6 +58639,7 @@ __name(decodeCookiePart, "decodeCookiePart");
57928
58639
  useServerFn,
57929
58640
  useServerQuery,
57930
58641
  useTheme,
58642
+ validateIntegrationRequestOrigin,
57931
58643
  validateServerActionRequest,
57932
58644
  vercelBlobStorage,
57933
58645
  vercelKVStorage,