@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
@@ -410,6 +410,54 @@ var init_route_pattern = __esm({
410
410
  }
411
411
  });
412
412
 
413
+ // src/request-origin.ts
414
+ function normalizeAllowedOriginPattern(value, label) {
415
+ const pattern = value.trim().toLowerCase();
416
+ if (!pattern) {
417
+ throw new TypeError(`${label} cannot contain empty values`);
418
+ }
419
+ if (pattern.includes("*")) {
420
+ if (!/^(?:https?:\/\/)?\*\.[a-z0-9.-]+(?::\d+)?$/.test(pattern)) {
421
+ throw new TypeError(`Invalid ${label} pattern: ${JSON.stringify(value)}`);
422
+ }
423
+ return pattern;
424
+ }
425
+ if (pattern.includes("://")) {
426
+ let parsed;
427
+ try {
428
+ parsed = new URL(pattern);
429
+ } catch {
430
+ throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);
431
+ }
432
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
433
+ throw new TypeError(`${label} must contain origins without paths: ${JSON.stringify(value)}`);
434
+ }
435
+ return parsed.origin;
436
+ }
437
+ if (/[/@?#]/.test(pattern)) {
438
+ throw new TypeError(`${label} must contain origins or hosts: ${JSON.stringify(value)}`);
439
+ }
440
+ try {
441
+ return new URL(`http://${pattern}`).host;
442
+ } catch {
443
+ throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);
444
+ }
445
+ }
446
+ var init_request_origin = __esm({
447
+ "src/request-origin.ts"() {
448
+ "use strict";
449
+ __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
450
+ }
451
+ });
452
+
453
+ // src/integration-request-security.ts
454
+ var init_integration_request_security = __esm({
455
+ "src/integration-request-security.ts"() {
456
+ "use strict";
457
+ init_request_origin();
458
+ }
459
+ });
460
+
413
461
  // src/integration-api.ts
414
462
  function defineIntegrationAPIOperation(operation) {
415
463
  return {
@@ -1426,9 +1474,13 @@ function getStorage(namespace) {
1426
1474
  const namespaced = (0, import_unstorage.prefixStorage)(globalStorage, base);
1427
1475
  return {
1428
1476
  ...namespaced,
1429
- async clear() {
1430
- const keys = await namespaced.getKeys();
1431
- await Promise.all(keys.map((key) => namespaced.removeItem(key)));
1477
+ // Scope the wipe to the namespace instead of the whole storage, and keep
1478
+ // the caller's base so `clear("sessions")` cannot take unrelated keys with
1479
+ // it. Dropping the argument silently widens a targeted clear into a
1480
+ // namespace-wide delete.
1481
+ async clear(base2, opts) {
1482
+ const keys = await namespaced.getKeys(base2);
1483
+ await Promise.all(keys.map((key) => namespaced.removeItem(key, opts)));
1432
1484
  }
1433
1485
  };
1434
1486
  }
@@ -3456,6 +3508,7 @@ var init_integrations = __esm({
3456
3508
  "src/integrations.ts"() {
3457
3509
  "use strict";
3458
3510
  init_route_pattern();
3511
+ init_integration_request_security();
3459
3512
  init_integration_api();
3460
3513
  init_plugin_integration_context();
3461
3514
  init_decode();
@@ -3885,6 +3938,29 @@ var init_search_params = __esm({
3885
3938
  }
3886
3939
  });
3887
3940
 
3941
+ // src/utils/runtime-env.ts
3942
+ function getFarmRuntimeBindings() {
3943
+ const runtimeBindings = globalThis.__env__;
3944
+ return runtimeBindings && typeof runtimeBindings === "object" ? runtimeBindings : void 0;
3945
+ }
3946
+ function readFarmEnvironmentValue(name2) {
3947
+ const runtimeValue = getFarmRuntimeBindings()?.[name2];
3948
+ if (typeof runtimeValue === "string") return runtimeValue;
3949
+ return typeof process !== "undefined" ? process.env?.[name2] : void 0;
3950
+ }
3951
+ function isFarmDeployedRuntime() {
3952
+ if (getFarmRuntimeBindings()) return true;
3953
+ return readFarmEnvironmentValue("NODE_ENV") === "production";
3954
+ }
3955
+ var init_runtime_env = __esm({
3956
+ "src/utils/runtime-env.ts"() {
3957
+ "use strict";
3958
+ __name(getFarmRuntimeBindings, "getFarmRuntimeBindings");
3959
+ __name(readFarmEnvironmentValue, "readFarmEnvironmentValue");
3960
+ __name(isFarmDeployedRuntime, "isFarmDeployedRuntime");
3961
+ }
3962
+ });
3963
+
3888
3964
  // src/utils.ts
3889
3965
  function parseRoutePath(filePath) {
3890
3966
  const segments = [];
@@ -4109,7 +4185,8 @@ function resolveWorkflowsConfig(workflows) {
4109
4185
  dirs,
4110
4186
  route: normalizeWorkflowRoute(options.route || DEFAULT_FARM_WORKFLOW_ROUTE),
4111
4187
  secretEnv: options.secretEnv || DEFAULT_FARM_WORKFLOW_SECRET_ENV,
4112
- secret: options.secret
4188
+ secret: options.secret,
4189
+ allowUnsecured: options.allowUnsecured === true
4113
4190
  };
4114
4191
  }
4115
4192
  async function discoverFarmWorkflows(config, options = {}) {
@@ -4235,8 +4312,11 @@ async function prepareFarmWorkflowsForNitro(config) {
4235
4312
  await fs15.mkdir(generatedDir, { recursive: true });
4236
4313
  const tasks = {};
4237
4314
  const scheduledTasks = createScheduledTasks(workflows);
4315
+ const wrapperNames = resolveWorkflowWrapperFileNames(workflows.map((workflow) => workflow.id));
4238
4316
  for (const workflow of workflows) {
4239
- const wrapperPath = toPosixPath(path42.join(generatedDir, `${safeFileName(workflow.id)}.mjs`));
4317
+ const wrapperPath = toPosixPath(
4318
+ path42.join(generatedDir, `${wrapperNames.get(workflow.id)}.mjs`)
4319
+ );
4240
4320
  await fs15.writeFile(wrapperPath, createNitroTaskWrapper(workflow), "utf8");
4241
4321
  tasks[workflow.id] = {
4242
4322
  handler: wrapperPath,
@@ -4474,6 +4554,7 @@ import {
4474
4554
  const route = ${JSON.stringify(config.route)};
4475
4555
  const secretEnv = ${JSON.stringify(config.secretEnv)};
4476
4556
  const inlineSecret = ${JSON.stringify(config.secret || "")};
4557
+ const allowUnsecured = ${JSON.stringify(config.allowUnsecured === true)};
4477
4558
  const bodySizeLimit = ${JSON.stringify(server.bodySizeLimit)};
4478
4559
  const workflows = ${JSON.stringify(workflows.map(toWorkflowMetadata))};
4479
4560
  const workflowIds = new Set(workflows.map((workflow) => workflow.id));
@@ -4503,7 +4584,10 @@ function getSecret() {
4503
4584
 
4504
4585
  function verifySecret(event) {
4505
4586
  const secret = getSecret();
4506
- if (!secret) return null;
4587
+ if (!secret) {
4588
+ if (allowUnsecured) return null;
4589
+ return json({ error: ${JSON.stringify(MISSING_FARM_WORKFLOW_SECRET_ERROR)} }, 401);
4590
+ }
4507
4591
  const authorization = getHeader(event, "authorization") || "";
4508
4592
  const headerSecret = getHeader(event, "x-farm-workflow-secret") || "";
4509
4593
  const bearer = authorization.match(/^Bearer\\s+(.+)$/i)?.[1] || "";
@@ -4597,8 +4681,16 @@ function readScheduledTime(payload) {
4597
4681
  return payload && typeof payload === "object" && "scheduledTime" in payload ? payload.scheduledTime : void 0;
4598
4682
  }
4599
4683
  function verifyWorkflowSecret(request, config) {
4600
- const secret = config.secret || process.env[config.secretEnv] || "";
4601
- if (!secret) return null;
4684
+ const secret = config.secret || readFarmEnvironmentValue(config.secretEnv) || "";
4685
+ if (!secret) {
4686
+ if (config.allowUnsecured === true || !isFarmDeployedRuntime()) return null;
4687
+ return Response.json(
4688
+ {
4689
+ error: MISSING_FARM_WORKFLOW_SECRET_ERROR
4690
+ },
4691
+ { status: 401 }
4692
+ );
4693
+ }
4602
4694
  const authorization = request.headers.get("authorization") || "";
4603
4695
  const bearer = authorization.match(/^Bearer\s+(.+)$/i)?.[1] || "";
4604
4696
  const headerSecret = request.headers.get("x-farm-workflow-secret") || "";
@@ -4611,18 +4703,50 @@ function joinRoute(...parts) {
4611
4703
  function trimSlashes(value) {
4612
4704
  return value.replace(/^\/+|\/+$/g, "");
4613
4705
  }
4706
+ function resolveWorkflowWrapperFileNames(ids) {
4707
+ const groups = /* @__PURE__ */ new Map();
4708
+ for (const id of ids) {
4709
+ const key = safeFileName(id).toLowerCase();
4710
+ groups.set(key, (groups.get(key) ?? 0) + 1);
4711
+ }
4712
+ const resolved = /* @__PURE__ */ new Map();
4713
+ const claimed = /* @__PURE__ */ new Map();
4714
+ for (const id of ids) {
4715
+ const base = safeFileName(id);
4716
+ const key = base.toLowerCase();
4717
+ const fileName = (groups.get(key) ?? 0) > 1 ? `${key}-${workflowIdFingerprint(id)}` : base;
4718
+ const claimedBy = claimed.get(fileName.toLowerCase());
4719
+ if (claimedBy !== void 0) {
4720
+ throw new Error(
4721
+ `Farm workflows ${JSON.stringify(claimedBy)} and ${JSON.stringify(id)} generate the same wrapper file ${JSON.stringify(`${fileName}.mjs`)}. Rename one of them.`
4722
+ );
4723
+ }
4724
+ claimed.set(fileName.toLowerCase(), id);
4725
+ resolved.set(id, fileName);
4726
+ }
4727
+ return resolved;
4728
+ }
4729
+ function workflowIdFingerprint(value) {
4730
+ let hash = 2166136261;
4731
+ for (let index = 0; index < value.length; index += 1) {
4732
+ hash ^= value.charCodeAt(index);
4733
+ hash = Math.imul(hash, 16777619);
4734
+ }
4735
+ return (hash >>> 0).toString(16).padStart(8, "0");
4736
+ }
4614
4737
  function safeFileName(value) {
4615
4738
  return value.replace(/[^a-zA-Z0-9._-]+/g, "-") || "workflow";
4616
4739
  }
4617
4740
  function relativePath(root, filePath) {
4618
4741
  return filePath.replace(/\\/g, "/").replace(`${root.replace(/\\/g, "/")}/`, "");
4619
4742
  }
4620
- var import_node_path, DEFAULT_FARM_WORKFLOW_DIRS, DEFAULT_FARM_WORKFLOW_ROUTE, DEFAULT_FARM_WORKFLOW_SECRET_ENV;
4743
+ var import_node_path, DEFAULT_FARM_WORKFLOW_DIRS, DEFAULT_FARM_WORKFLOW_ROUTE, DEFAULT_FARM_WORKFLOW_SECRET_ENV, MISSING_FARM_WORKFLOW_SECRET_ERROR;
4621
4744
  var init_workflows = __esm({
4622
4745
  "src/workflows.ts"() {
4623
4746
  "use strict";
4624
4747
  init_server_http();
4625
4748
  init_search_params();
4749
+ init_runtime_env();
4626
4750
  init_decode();
4627
4751
  init_utils();
4628
4752
  init_route_pattern();
@@ -4630,6 +4754,7 @@ var init_workflows = __esm({
4630
4754
  DEFAULT_FARM_WORKFLOW_DIRS = ["src/jobs", "src/workflows", "src/cron"];
4631
4755
  DEFAULT_FARM_WORKFLOW_ROUTE = "/api/_farm/workflows";
4632
4756
  DEFAULT_FARM_WORKFLOW_SECRET_ENV = "CRON_SECRET";
4757
+ 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.";
4633
4758
  __name(defineWorkflow, "defineWorkflow");
4634
4759
  __name(defineTask, "defineTask");
4635
4760
  __name(defineCron, "defineCron");
@@ -4662,6 +4787,8 @@ var init_workflows = __esm({
4662
4787
  __name(verifyWorkflowSecret, "verifyWorkflowSecret");
4663
4788
  __name(joinRoute, "joinRoute");
4664
4789
  __name(trimSlashes, "trimSlashes");
4790
+ __name(resolveWorkflowWrapperFileNames, "resolveWorkflowWrapperFileNames");
4791
+ __name(workflowIdFingerprint, "workflowIdFingerprint");
4665
4792
  __name(safeFileName, "safeFileName");
4666
4793
  __name(relativePath, "relativePath");
4667
4794
  }
@@ -4713,9 +4840,10 @@ async function prepareFarmCronForNitro(config) {
4713
4840
  await fs15.rm(generatedDir, { recursive: true, force: true });
4714
4841
  await fs15.mkdir(generatedDir, { recursive: true });
4715
4842
  const tasks = {};
4843
+ const wrapperNames = resolveCronWrapperFileNames(cron.jobs.map((job) => job.name));
4716
4844
  for (const job of cron.jobs) {
4717
4845
  const taskName = getFarmCronTaskName(job.name);
4718
- const wrapperPath = toPosixPath(path42.join(generatedDir, `${safeFileName2(job.name)}.mjs`));
4846
+ const wrapperPath = toPosixPath(path42.join(generatedDir, `${wrapperNames.get(job.name)}.mjs`));
4719
4847
  await fs15.writeFile(wrapperPath, createNitroCronTaskWrapper(job, cron.secretEnv), "utf8");
4720
4848
  tasks[taskName] = {
4721
4849
  handler: wrapperPath,
@@ -4977,6 +5105,37 @@ export default defineTask({
4977
5105
  });
4978
5106
  `.trim();
4979
5107
  }
5108
+ function resolveCronWrapperFileNames(names) {
5109
+ const groups = /* @__PURE__ */ new Map();
5110
+ for (const name2 of names) {
5111
+ const key = safeFileName2(name2).toLowerCase();
5112
+ groups.set(key, (groups.get(key) ?? 0) + 1);
5113
+ }
5114
+ const resolved = /* @__PURE__ */ new Map();
5115
+ const claimed = /* @__PURE__ */ new Map();
5116
+ for (const name2 of names) {
5117
+ const base = safeFileName2(name2);
5118
+ const key = base.toLowerCase();
5119
+ const fileName = (groups.get(key) ?? 0) > 1 ? `${key}-${cronNameFingerprint(name2)}` : base;
5120
+ const claimedBy = claimed.get(fileName.toLowerCase());
5121
+ if (claimedBy !== void 0) {
5122
+ throw new Error(
5123
+ `Farm cron jobs ${JSON.stringify(claimedBy)} and ${JSON.stringify(name2)} generate the same wrapper file ${JSON.stringify(`${fileName}.mjs`)}. Rename one of them.`
5124
+ );
5125
+ }
5126
+ claimed.set(fileName.toLowerCase(), name2);
5127
+ resolved.set(name2, fileName);
5128
+ }
5129
+ return resolved;
5130
+ }
5131
+ function cronNameFingerprint(value) {
5132
+ let hash = 2166136261;
5133
+ for (let index = 0; index < value.length; index += 1) {
5134
+ hash ^= value.charCodeAt(index);
5135
+ hash = Math.imul(hash, 16777619);
5136
+ }
5137
+ return (hash >>> 0).toString(16).padStart(8, "0");
5138
+ }
4980
5139
  function safeFileName2(value) {
4981
5140
  return value.replace(/[^a-zA-Z0-9._-]+/g, "-") || "cron";
4982
5141
  }
@@ -4985,6 +5144,7 @@ var init_cron = __esm({
4985
5144
  "src/cron.ts"() {
4986
5145
  "use strict";
4987
5146
  init_utils();
5147
+ init_runtime_env();
4988
5148
  DEFAULT_FARM_CRON_SECRET_ENV = "CRON_SECRET";
4989
5149
  FARM_CRON_MANIFEST = "cron-manifest.json";
4990
5150
  CRON_FIELD_RANGES = [
@@ -5013,6 +5173,8 @@ var init_cron = __esm({
5013
5173
  __name(mergeTaskNames, "mergeTaskNames");
5014
5174
  __name(getFarmCronTaskName, "getFarmCronTaskName");
5015
5175
  __name(createNitroCronTaskWrapper, "createNitroCronTaskWrapper");
5176
+ __name(resolveCronWrapperFileNames, "resolveCronWrapperFileNames");
5177
+ __name(cronNameFingerprint, "cronNameFingerprint");
5016
5178
  __name(safeFileName2, "safeFileName");
5017
5179
  }
5018
5180
  });
@@ -5604,7 +5766,9 @@ var init_server_fn_error = __esm({
5604
5766
 
5605
5767
  // src/server-action-security.ts
5606
5768
  function resolveServerActionsConfig(config) {
5607
- const allowedOrigins = (config?.allowedOrigins ?? []).map(normalizeAllowedOriginPattern);
5769
+ const allowedOrigins = (config?.allowedOrigins ?? []).map(
5770
+ (value) => normalizeAllowedOriginPattern(value, SERVER_ACTION_ALLOWED_ORIGINS_LABEL)
5771
+ );
5608
5772
  const bodySizeLimit = parseBodySizeLimit(
5609
5773
  config?.bodySizeLimit ?? DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT,
5610
5774
  "serverActions.bodySizeLimit"
@@ -5624,50 +5788,16 @@ function getServerActionStorage() {
5624
5788
  }
5625
5789
  return globalState2[SERVER_ACTION_STORAGE_KEY];
5626
5790
  }
5627
- function normalizeAllowedOriginPattern(value) {
5628
- const pattern = value.trim().toLowerCase();
5629
- if (!pattern) {
5630
- throw new TypeError("serverActions.allowedOrigins cannot contain empty values");
5631
- }
5632
- if (pattern.includes("*")) {
5633
- if (!/^(?:https?:\/\/)?\*\.[a-z0-9.-]+(?::\d+)?$/.test(pattern)) {
5634
- throw new TypeError(`Invalid serverActions.allowedOrigins pattern: ${JSON.stringify(value)}`);
5635
- }
5636
- return pattern;
5637
- }
5638
- if (pattern.includes("://")) {
5639
- let parsed;
5640
- try {
5641
- parsed = new URL(pattern);
5642
- } catch {
5643
- throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);
5644
- }
5645
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
5646
- throw new TypeError(
5647
- `serverActions.allowedOrigins must contain origins without paths: ${JSON.stringify(value)}`
5648
- );
5649
- }
5650
- return parsed.origin;
5651
- }
5652
- if (/[/@?#]/.test(pattern)) {
5653
- throw new TypeError(
5654
- `serverActions.allowedOrigins must contain origins or hosts: ${JSON.stringify(value)}`
5655
- );
5656
- }
5657
- try {
5658
- return new URL(`http://${pattern}`).host;
5659
- } catch {
5660
- throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);
5661
- }
5662
- }
5663
- var import_node_async_hooks, DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT, SERVER_ACTION_STORAGE_KEY;
5791
+ var import_node_async_hooks, SERVER_ACTION_ALLOWED_ORIGINS_LABEL, DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT, SERVER_ACTION_STORAGE_KEY;
5664
5792
  var init_server_action_security = __esm({
5665
5793
  "src/server-action-security.ts"() {
5666
5794
  "use strict";
5667
5795
  import_node_async_hooks = require("async_hooks");
5668
5796
  init_cache_invalidation();
5797
+ init_request_origin();
5669
5798
  init_server_fn_error();
5670
5799
  init_server_http();
5800
+ SERVER_ACTION_ALLOWED_ORIGINS_LABEL = "serverActions.allowedOrigins";
5671
5801
  DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT = 1e6;
5672
5802
  SERVER_ACTION_STORAGE_KEY = /* @__PURE__ */ Symbol.for("farm.serverActionStorage");
5673
5803
  __name(resolveServerActionsConfig, "resolveServerActionsConfig");
@@ -5679,7 +5809,6 @@ var init_server_action_security = __esm({
5679
5809
  getServerActionExecutionContext()?.cacheTasks.add(task);
5680
5810
  });
5681
5811
  __name(getServerActionStorage, "getServerActionStorage");
5682
- __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
5683
5812
  }
5684
5813
  });
5685
5814
 
@@ -7121,6 +7250,51 @@ var init_config2 = __esm({
7121
7250
  }
7122
7251
  });
7123
7252
 
7253
+ // src/agent-config.ts
7254
+ function resolveFarmAgentConfig(input) {
7255
+ const jsonLd = input?.jsonLd;
7256
+ if (!jsonLd) return { jsonLd: false };
7257
+ return { jsonLd: jsonLd === true ? {} : jsonLd };
7258
+ }
7259
+ function pruneUndefined(object) {
7260
+ const result = {};
7261
+ for (const [key, value] of Object.entries(object)) {
7262
+ if (value !== void 0 && value !== null && value !== "") result[key] = value;
7263
+ }
7264
+ return result;
7265
+ }
7266
+ function serializeJsonLd(value) {
7267
+ return JSON.stringify(value).replace(/</g, "\\u003c");
7268
+ }
7269
+ function renderFarmAgentJsonLd(config, context2) {
7270
+ const base = pruneUndefined({
7271
+ name: config.name ?? context2.siteName ?? context2.title,
7272
+ url: config.url ?? context2.metadataBase,
7273
+ description: config.description ?? context2.description,
7274
+ logo: config.logo,
7275
+ sameAs: config.sameAs && config.sameAs.length > 0 ? config.sameAs : void 0
7276
+ });
7277
+ const object = {
7278
+ "@context": "https://schema.org",
7279
+ "@type": config.type || "Organization",
7280
+ ...base,
7281
+ ...config.properties || {}
7282
+ };
7283
+ if (Object.keys(object).length <= 2 && !config.properties) {
7284
+ return "";
7285
+ }
7286
+ return `<script type="application/ld+json">${serializeJsonLd(object)}</script>`;
7287
+ }
7288
+ var init_agent_config = __esm({
7289
+ "src/agent-config.ts"() {
7290
+ "use strict";
7291
+ __name(resolveFarmAgentConfig, "resolveFarmAgentConfig");
7292
+ __name(pruneUndefined, "pruneUndefined");
7293
+ __name(serializeJsonLd, "serializeJsonLd");
7294
+ __name(renderFarmAgentJsonLd, "renderFarmAgentJsonLd");
7295
+ }
7296
+ });
7297
+
7124
7298
  // src/renderer.ts
7125
7299
  function getFarmRendererCapabilities(renderer) {
7126
7300
  return {
@@ -7886,6 +8060,7 @@ async function resolveConfig(userConfig, mode) {
7886
8060
  const openapi = {
7887
8061
  enabled: false,
7888
8062
  route: "/docs/reference",
8063
+ specRoute: "/openapi.json",
7889
8064
  title: "API Documentation",
7890
8065
  description: "Auto-generated API documentation",
7891
8066
  version: "1.0.0",
@@ -7893,6 +8068,7 @@ async function resolveConfig(userConfig, mode) {
7893
8068
  ...userConfig.openapi
7894
8069
  };
7895
8070
  if (openapi.route !== void 0) validateConfigRouteSource(openapi.route, "openapi.route");
8071
+ if (openapi.specRoute) validateConfigRouteSource(openapi.specRoute, "openapi.specRoute");
7896
8072
  const resolved = {
7897
8073
  [FARM_RESOLVED_CUSTOM_CONTEXT]: typeof userConfig.context === "function",
7898
8074
  root,
@@ -7925,6 +8101,7 @@ async function resolveConfig(userConfig, mode) {
7925
8101
  ppr: false,
7926
8102
  ...userConfig.experimental
7927
8103
  },
8104
+ agent: resolveFarmAgentConfig(userConfig.agent),
7928
8105
  plugins: [...resolveIntegrationPlugins(integrations), ...userConfig.plugins || []],
7929
8106
  integrations,
7930
8107
  trailingSlash: userConfig.trailingSlash ?? false,
@@ -8033,6 +8210,7 @@ var init_config4 = __esm({
8033
8210
  init_security();
8034
8211
  init_navigation_errors();
8035
8212
  init_config2();
8213
+ init_agent_config();
8036
8214
  init_renderer();
8037
8215
  init_framework_detect();
8038
8216
  init_config3();
@@ -10909,6 +11087,18 @@ var init_routes_server = __esm({
10909
11087
  function isFarmMarkdownPageFile(filePath) {
10910
11088
  return /(^|[/\\])page\.mdx?$/i.test(filePath);
10911
11089
  }
11090
+ function farmRequestWantsMarkdown(pathname, accept) {
11091
+ return pathname.toLowerCase().endsWith(".md") || requestAcceptsMarkdown(accept);
11092
+ }
11093
+ function createFarmMarkdownErrorBody(status, pathname, homeHref = "/") {
11094
+ const heading = status === 404 ? "Page not found" : `Request failed (${status})`;
11095
+ return `# ${heading}
11096
+
11097
+ No page is available at \`${pathname}\`. The URL may be incorrect, or the page has not been published.
11098
+
11099
+ Browse [the site homepage](${homeHref}) to find available pages.
11100
+ `;
11101
+ }
10912
11102
  function normalizeFarmMarkdownRoutePath(pathname) {
10913
11103
  const withoutExtension = pathname.replace(/\.md$/i, "");
10914
11104
  const normalized = withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`;
@@ -11052,7 +11242,7 @@ async function createFarmMarkdownSourceResponse(options) {
11052
11242
  headers
11053
11243
  });
11054
11244
  }
11055
- var import_promises4, import_path4, import_url;
11245
+ var import_promises4, import_path4, import_url, FARM_MARKDOWN_CONTENT_TYPE;
11056
11246
  var init_app_markdown = __esm({
11057
11247
  "src/app-markdown.ts"() {
11058
11248
  "use strict";
@@ -11062,6 +11252,9 @@ var init_app_markdown = __esm({
11062
11252
  init_markdown();
11063
11253
  init_app_markdown_config();
11064
11254
  __name(isFarmMarkdownPageFile, "isFarmMarkdownPageFile");
11255
+ FARM_MARKDOWN_CONTENT_TYPE = "text/markdown; charset=utf-8";
11256
+ __name(farmRequestWantsMarkdown, "farmRequestWantsMarkdown");
11257
+ __name(createFarmMarkdownErrorBody, "createFarmMarkdownErrorBody");
11065
11258
  __name(normalizeFarmMarkdownRoutePath, "normalizeFarmMarkdownRoutePath");
11066
11259
  __name(parseMarkdownFrontmatter, "parseMarkdownFrontmatter");
11067
11260
  __name(titleFromMarkdown, "titleFromMarkdown");
@@ -11569,6 +11762,39 @@ function getStaticImportBindings(content) {
11569
11762
  }
11570
11763
  return bindings;
11571
11764
  }
11765
+ function findParserSensitiveJsxContainer(content, localBindings) {
11766
+ if (!content || localBindings.length === 0) return void 0;
11767
+ const names = new Set(localBindings);
11768
+ const tokens = tokenizeModuleSource(content);
11769
+ const openDepth = /* @__PURE__ */ new Map();
11770
+ const isSelfClosing = /* @__PURE__ */ __name((openingIndex) => {
11771
+ for (let index = openingIndex; index < tokens.length; index++) {
11772
+ if (tokens[index].value === ">") return tokens[index - 1]?.value === "/";
11773
+ }
11774
+ return false;
11775
+ }, "isSelfClosing");
11776
+ for (let index = 0; index < tokens.length - 1; index++) {
11777
+ if (tokens[index].value !== "<") continue;
11778
+ if (tokens[index + 1].value === "/") {
11779
+ const closing = tokens[index + 2]?.value;
11780
+ if (closing && openDepth.has(closing)) {
11781
+ openDepth.set(closing, Math.max(0, (openDepth.get(closing) ?? 0) - 1));
11782
+ }
11783
+ continue;
11784
+ }
11785
+ const tag = tokens[index + 1].value;
11786
+ if (names.has(tag)) {
11787
+ for (const [container, depth] of openDepth) {
11788
+ if (depth > 0) return container;
11789
+ }
11790
+ continue;
11791
+ }
11792
+ if (PARSER_SENSITIVE_JSX_CONTAINERS.has(tag) && !isSelfClosing(index)) {
11793
+ openDepth.set(tag, (openDepth.get(tag) ?? 0) + 1);
11794
+ }
11795
+ }
11796
+ return void 0;
11797
+ }
11572
11798
  function countStaticJsxUses(content, localBindings) {
11573
11799
  if (!content || localBindings.length === 0) return 0;
11574
11800
  const names = new Set(localBindings);
@@ -11677,6 +11903,36 @@ function getClientRenderingHelpers(tokens, clientBindings) {
11677
11903
  }
11678
11904
  return helpers;
11679
11905
  }
11906
+ function passesElementValuedProps(content, localBindings) {
11907
+ if (!content || localBindings.length === 0) return false;
11908
+ const names = new Set(localBindings);
11909
+ const tokens = tokenizeModuleSource(content);
11910
+ for (let index = 0; index < tokens.length - 1; index++) {
11911
+ if (tokens[index].value !== "<" || !names.has(tokens[index + 1].value)) continue;
11912
+ const name2 = tokens[index + 1].value;
11913
+ let cursor = index + 2;
11914
+ let braceDepth = 0;
11915
+ let selfClosing = false;
11916
+ for (; cursor < tokens.length; cursor++) {
11917
+ const value = tokens[cursor].value;
11918
+ if (value === "{") braceDepth++;
11919
+ else if (value === "}") braceDepth--;
11920
+ else if (value === "<" && braceDepth > 0) return true;
11921
+ else if (value === ">" && braceDepth === 0) {
11922
+ selfClosing = tokens[cursor - 1]?.value === "/";
11923
+ break;
11924
+ }
11925
+ }
11926
+ if (selfClosing || cursor >= tokens.length) continue;
11927
+ for (let child = cursor + 1; child < tokens.length; child++) {
11928
+ if (tokens[child].value !== "<") continue;
11929
+ const closesBoundary = tokens[child + 1]?.value === "/" && tokens[child + 2]?.value === name2;
11930
+ if (!closesBoundary) return true;
11931
+ break;
11932
+ }
11933
+ }
11934
+ return false;
11935
+ }
11680
11936
  function hasDynamicJsxCardinality(content, localBindings) {
11681
11937
  if (!content || localBindings.length === 0) return false;
11682
11938
  const clientBindings = new Set(localBindings);
@@ -11758,6 +12014,17 @@ function collectIsolatedClientBoundaries(ownerPath, root) {
11758
12014
  if (fallbackReason || importedBoundaryCount === 0) continue;
11759
12015
  const localBindings = importedBindings.get(specifier) ?? [];
11760
12016
  const staticUses = countStaticJsxUses(content, localBindings);
12017
+ if (staticUses > 0) {
12018
+ const container = findParserSensitiveJsxContainer(content, localBindings);
12019
+ if (container) {
12020
+ fallbackReason = `the client boundary imported from ${specifier} renders inside <${container}>, where the HTML parser relocates its hydration marker`;
12021
+ return 0;
12022
+ }
12023
+ if (passesElementValuedProps(content, localBindings)) {
12024
+ fallbackReason = `the client boundary imported from ${specifier} receives React elements, which cannot cross the boundary as serialized props`;
12025
+ return 0;
12026
+ }
12027
+ }
11761
12028
  if (staticUses > 0 && hasDynamicJsxCardinality(content, localBindings)) {
11762
12029
  fallbackReason = `the client boundary count imported from ${specifier} is data-dependent`;
11763
12030
  costGuardExceeded = true;
@@ -12054,7 +12321,7 @@ function resolveSourceCandidate(candidates) {
12054
12321
  }
12055
12322
  return null;
12056
12323
  }
12057
- var import_fs2, import_path5, import_url2, FARM_ISOLATED_HYDRATION_MAX_BOUNDARIES, RESOLVABLE_SOURCE_EXTENSIONS, REGEXP_PREFIX_PUNCTUATION, REGEXP_PREFIX_KEYWORDS;
12324
+ var import_fs2, import_path5, import_url2, FARM_ISOLATED_HYDRATION_MAX_BOUNDARIES, RESOLVABLE_SOURCE_EXTENSIONS, REGEXP_PREFIX_PUNCTUATION, REGEXP_PREFIX_KEYWORDS, PARSER_SENSITIVE_JSX_CONTAINERS;
12058
12325
  var init_client_component = __esm({
12059
12326
  "src/utils/client-component.ts"() {
12060
12327
  "use strict";
@@ -12132,11 +12399,23 @@ var init_client_component = __esm({
12132
12399
  __name(enforceFarmIsolatedHydrationRouteBudget, "enforceFarmIsolatedHydrationRouteBudget");
12133
12400
  __name(isIsolatableClientBoundarySource, "isIsolatableClientBoundarySource");
12134
12401
  __name(getStaticImportBindings, "getStaticImportBindings");
12402
+ PARSER_SENSITIVE_JSX_CONTAINERS = /* @__PURE__ */ new Set([
12403
+ "table",
12404
+ "thead",
12405
+ "tbody",
12406
+ "tfoot",
12407
+ "tr",
12408
+ "select",
12409
+ "optgroup",
12410
+ "svg"
12411
+ ]);
12412
+ __name(findParserSensitiveJsxContainer, "findParserSensitiveJsxContainer");
12135
12413
  __name(countStaticJsxUses, "countStaticJsxUses");
12136
12414
  __name(findMatchingToken, "findMatchingToken");
12137
12415
  __name(containsClientJsx, "containsClientJsx");
12138
12416
  __name(findStatementEnd, "findStatementEnd");
12139
12417
  __name(getClientRenderingHelpers, "getClientRenderingHelpers");
12418
+ __name(passesElementValuedProps, "passesElementValuedProps");
12140
12419
  __name(hasDynamicJsxCardinality, "hasDynamicJsxCardinality");
12141
12420
  __name(collectIsolatedClientBoundaries, "collectIsolatedClientBoundaries");
12142
12421
  __name(inspectClientModuleMetadata, "inspectClientModuleMetadata");
@@ -13779,7 +14058,7 @@ function addMetadataImageReference(metadata, reference) {
13779
14058
  twitter
13780
14059
  };
13781
14060
  }
13782
- function renderMetadataHead(metadata) {
14061
+ function renderMetadataHead(metadata, options = {}) {
13783
14062
  const resolvedMetadata = metadata || {};
13784
14063
  const metadataBase = resolveMetadataBase(resolvedMetadata);
13785
14064
  const explicitTitle = resolveMetadataTitle(resolvedMetadata.title);
@@ -13800,14 +14079,16 @@ function renderMetadataHead(metadata) {
13800
14079
  appendMetaName(tags, "publisher", resolvedMetadata.publisher);
13801
14080
  appendMetaName(tags, "robots", normalizeRobots(resolvedMetadata.robots));
13802
14081
  const alternates = resolvedMetadata.alternates;
13803
- if (isRecord3(alternates)) {
13804
- appendLink(tags, "canonical", resolveMetadataUrl(alternates.canonical, metadataBase));
13805
- if (isRecord3(alternates.languages)) {
13806
- for (const [language, href] of Object.entries(alternates.languages)) {
13807
- appendLink(tags, "alternate", resolveMetadataUrl(href, metadataBase), {
13808
- hreflang: language
13809
- });
13810
- }
14082
+ const explicitCanonical = isRecord3(alternates) ? alternates.canonical : void 0;
14083
+ const canonicalHref = explicitCanonical != null ? resolveMetadataUrl(explicitCanonical, metadataBase) : options.pathname ? resolveMetadataUrl(options.pathname, metadataBase) : void 0;
14084
+ if (canonicalHref) {
14085
+ appendLink(tags, "canonical", canonicalHref);
14086
+ }
14087
+ if (isRecord3(alternates) && isRecord3(alternates.languages)) {
14088
+ for (const [language, href] of Object.entries(alternates.languages)) {
14089
+ appendLink(tags, "alternate", resolveMetadataUrl(href, metadataBase), {
14090
+ hreflang: language
14091
+ });
13811
14092
  }
13812
14093
  }
13813
14094
  const hasFavicon = appendIcons(tags, resolvedMetadata.icons, metadataBase);
@@ -13820,6 +14101,16 @@ function renderMetadataHead(metadata) {
13820
14101
  }
13821
14102
  appendOpenGraph(tags, resolvedMetadata.openGraph, metadataBase);
13822
14103
  appendTwitter(tags, resolvedMetadata.twitter, metadataBase);
14104
+ if (options.jsonLd) {
14105
+ const jsonLdConfig = options.jsonLd === true ? {} : options.jsonLd;
14106
+ const jsonLdScript = renderFarmAgentJsonLd(jsonLdConfig, {
14107
+ metadataBase,
14108
+ siteName: isRecord3(resolvedMetadata.openGraph) ? normalizeContent(resolvedMetadata.openGraph.siteName) : void 0,
14109
+ title: explicitTitle,
14110
+ description: normalizeContent(resolvedMetadata.description)
14111
+ });
14112
+ if (jsonLdScript) tags.push(jsonLdScript);
14113
+ }
13823
14114
  return {
13824
14115
  title: escapeText(title),
13825
14116
  tags: tags.length > 0 ? `
@@ -13843,7 +14134,7 @@ function appendOpenGraph(tags, openGraph, metadataBase) {
13843
14134
  appendMetaProperty(tags, "og:description", openGraph.description);
13844
14135
  appendMetaProperty(tags, "og:url", resolveMetadataUrl(openGraph.url, metadataBase));
13845
14136
  appendMetaProperty(tags, "og:site_name", openGraph.siteName);
13846
- appendMetaProperty(tags, "og:type", openGraph.type);
14137
+ appendMetaProperty(tags, "og:type", openGraph.type ?? "website");
13847
14138
  appendMetaProperty(tags, "og:locale", openGraph.locale);
13848
14139
  const images = normalizeMetadataImages(
13849
14140
  openGraph.images ?? openGraph.image,
@@ -14006,6 +14297,7 @@ function escapeAttribute(value) {
14006
14297
  var init_metadata = __esm({
14007
14298
  "src/metadata.ts"() {
14008
14299
  "use strict";
14300
+ init_agent_config();
14009
14301
  __name(mergeMetadata, "mergeMetadata");
14010
14302
  __name(mergeMetadataTitle, "mergeMetadataTitle");
14011
14303
  __name(applyTitleTemplate, "applyTitleTemplate");
@@ -17097,7 +17389,7 @@ var init_server3 = __esm({
17097
17389
  var version;
17098
17390
  var init_package = __esm({
17099
17391
  "package.json"() {
17100
- version = "0.1.0-beta.97";
17392
+ version = "0.1.0-beta.99";
17101
17393
  }
17102
17394
  });
17103
17395
 
@@ -17352,6 +17644,12 @@ function hasRequestHeader(req, name2) {
17352
17644
  const value = req.headers[name2.toLowerCase()];
17353
17645
  return Array.isArray(value) ? value.length > 0 : Boolean(value);
17354
17646
  }
17647
+ function getFarmMetadataPathname(req) {
17648
+ const raw = req.__FARM_ROUTE__ || req.url;
17649
+ if (typeof raw !== "string" || raw.length === 0) return void 0;
17650
+ const boundary = raw.search(/[?#]/);
17651
+ return boundary === -1 ? raw : raw.slice(0, boundary);
17652
+ }
17355
17653
  function serializeInlineValue2(value) {
17356
17654
  return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
17357
17655
  }
@@ -17500,6 +17798,7 @@ var init_renderer2 = __esm({
17500
17798
  init_server();
17501
17799
  init_request_context();
17502
17800
  init_ssg();
17801
+ init_app_markdown();
17503
17802
  init_integrations();
17504
17803
  init_request();
17505
17804
  init_cache();
@@ -17541,6 +17840,7 @@ var init_renderer2 = __esm({
17541
17840
  warnedFullDocumentLayout = false;
17542
17841
  __name(warnFarmFullDocumentLayout, "warnFarmFullDocumentLayout");
17543
17842
  __name(hasRequestHeader, "hasRequestHeader");
17843
+ __name(getFarmMetadataPathname, "getFarmMetadataPathname");
17544
17844
  __name(serializeInlineValue2, "serializeInlineValue");
17545
17845
  __name(escapeHtmlAttribute2, "escapeHtmlAttribute");
17546
17846
  __name(appendResponseHeader, "appendResponseHeader");
@@ -17940,7 +18240,21 @@ var init_renderer2 = __esm({
17940
18240
  const html = await this.rendererRuntime.renderToString(
17941
18241
  await this.wrapWithIntegrationProviders(pageElement)
17942
18242
  );
17943
- await this.cacheSSGPage(page2, html, { document: false });
18243
+ const mergedMetadata = await this.resolveRouteMetadata({
18244
+ layoutModules,
18245
+ routeModule: mod,
18246
+ pageProps,
18247
+ pathname: page2.urlPath
18248
+ });
18249
+ const { title, tags, hasFavicon } = renderMetadataHead(mergedMetadata);
18250
+ const metadataHead = `${hasFavicon ? "" : '<link rel="icon" href="data:,">\n '}<title>${title}</title>${tags}`;
18251
+ const fullDocument = this.createFullHTML(
18252
+ html,
18253
+ pageMetadata.shouldHydrate === true,
18254
+ page2.urlPath,
18255
+ metadataHead
18256
+ );
18257
+ await this.cacheSSGPage(page2, fullDocument, { document: true });
17944
18258
  logger.info(`ISR: Regenerated ${page2.urlPath}`);
17945
18259
  } catch (error) {
17946
18260
  logger.error(`ISR regeneration failed for ${page2.urlPath}: ${error}`);
@@ -18980,7 +19294,10 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
18980
19294
  tags: metaTags,
18981
19295
  hasFavicon,
18982
19296
  hasExplicitTitle
18983
- } = renderMetadataHead(req.__FARM_METADATA__);
19297
+ } = renderMetadataHead(req.__FARM_METADATA__, {
19298
+ pathname: getFarmMetadataPathname(req),
19299
+ jsonLd: this.config.agent?.jsonLd
19300
+ });
18984
19301
  const documentTitleTag = !hasExplicitTitle && /<title[\s>]/i.test(rendererHead) ? "" : `<title>${title}</title>`;
18985
19302
  const i18nSnapshot = getFarmI18nClientSnapshot();
18986
19303
  const alternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
@@ -19165,7 +19482,10 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
19165
19482
  title,
19166
19483
  tags: metaTags,
19167
19484
  hasFavicon
19168
- } = renderMetadataHead(req.__FARM_METADATA__);
19485
+ } = renderMetadataHead(req.__FARM_METADATA__, {
19486
+ pathname: getFarmMetadataPathname(req),
19487
+ jsonLd: this.config.agent?.jsonLd
19488
+ });
19169
19489
  const i18nSnapshot = getFarmI18nClientSnapshot();
19170
19490
  const i18nAlternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
19171
19491
  const fontHead = renderFarmFontDevHead(this.config.root || process.cwd());
@@ -19323,6 +19643,15 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
19323
19643
  const pathname = resolveFarmRequestURL(req, {
19324
19644
  trustProxy: this.config.server?.trustProxy
19325
19645
  }).pathname;
19646
+ const acceptHeader = req.headers.accept;
19647
+ const accept = Array.isArray(acceptHeader) ? acceptHeader.join(",") : acceptHeader;
19648
+ if (farmRequestWantsMarkdown(pathname, accept)) {
19649
+ res.setHeader("Content-Type", FARM_MARKDOWN_CONTENT_TYPE);
19650
+ res.setHeader("X-Farm-Markdown-Error", "404");
19651
+ res.setHeader("Cache-Control", "no-store");
19652
+ res.end(createFarmMarkdownErrorBody(404, pathname, this.config.basePath || "/"));
19653
+ return;
19654
+ }
19326
19655
  try {
19327
19656
  const appDir = path15.join(this.config.root, this.config.srcDir, "app");
19328
19657
  const notFoundExtensions = getFarmRendererComponentExtensions(this.config.renderer);
@@ -19408,7 +19737,7 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
19408
19737
  res.write(html);
19409
19738
  res.end();
19410
19739
  }
19411
- createFullHTML(content, isClientComponent = false, requestPath = "/", documentTitle = "Farm.js App") {
19740
+ createFullHTML(content, isClientComponent = false, requestPath = "/", metadataHead) {
19412
19741
  const i18nSnapshot = getFarmI18nClientSnapshot();
19413
19742
  const clientScript = isClientComponent ? ` <script type="module" src="/@farm/client.js"></script>` : "";
19414
19743
  const integrationManifestScript = `<script>
@@ -19432,6 +19761,7 @@ ${i18nSnapshot ? `window.__FARM_I18N__ = ${serializeInlineValue2(i18nSnapshot)};
19432
19761
  headAssets: [
19433
19762
  themeDocument.head,
19434
19763
  `<meta name="farm-deployment-id" content="${escapeHtmlAttribute2(this.getDeploymentId())}">`,
19764
+ metadataHead,
19435
19765
  alternateLinks,
19436
19766
  fontHead,
19437
19767
  `<link rel="stylesheet" href="/src/app/globals.css" />`,
@@ -19450,8 +19780,7 @@ ${i18nSnapshot ? `window.__FARM_I18N__ = ${serializeInlineValue2(i18nSnapshot)};
19450
19780
  <meta charset="utf-8">
19451
19781
  <meta name="viewport" content="width=device-width, initial-scale=1">
19452
19782
  <meta name="farm-deployment-id" content="${escapeHtmlAttribute2(this.getDeploymentId())}">
19453
- <link rel="icon" href="data:,">
19454
- <title>${escapeHtmlAttribute2(documentTitle)}</title>${alternateLinks}
19783
+ ${metadataHead ?? '<link rel="icon" href="data:,">\n <title>Farm.js App</title>'}${alternateLinks}
19455
19784
  ${fontHead}
19456
19785
  <link rel="stylesheet" href="/src/app/globals.css" />${this.collectDevStyleLinks().map((l) => `
19457
19786
  ${l}`).join("")}
@@ -19658,6 +19987,7 @@ var init_app = __esm({
19658
19987
  ppr: config.experimental?.ppr ?? false,
19659
19988
  ...config.experimental
19660
19989
  },
19990
+ agent: config.agent ?? {},
19661
19991
  vite: config.vite || {}
19662
19992
  };
19663
19993
  }
@@ -25109,6 +25439,116 @@ var init_server_query_boundary = __esm({
25109
25439
  }
25110
25440
  });
25111
25441
 
25442
+ // src/client-boundary-env.ts
25443
+ function shouldInspectClientBoundary(id, code) {
25444
+ if (id.startsWith("\0") || id.startsWith("virtual:") || id.includes("node_modules")) return false;
25445
+ return code.includes("process.env") || code.includes("node:");
25446
+ }
25447
+ function analyzeClientBoundary(program, publicKeys) {
25448
+ const envKeys = /* @__PURE__ */ new Set();
25449
+ const publicEnvKeys = /* @__PURE__ */ new Set();
25450
+ const builtinImports = /* @__PURE__ */ new Set();
25451
+ const visit = /* @__PURE__ */ __name((node, inFunction) => {
25452
+ if (!node || typeof node !== "object") return;
25453
+ if (Array.isArray(node)) {
25454
+ for (const entry of node) visit(entry, inFunction);
25455
+ return;
25456
+ }
25457
+ const estree = node;
25458
+ if (typeof estree.type !== "string") return;
25459
+ if (!inFunction) {
25460
+ const builtin = readNodeBuiltinSource(estree);
25461
+ if (builtin !== void 0) builtinImports.add(builtin);
25462
+ const key = readProcessEnvKey(estree);
25463
+ if (key !== void 0) {
25464
+ if (key !== "NODE_ENV") {
25465
+ (publicKeys.has(key) ? publicEnvKeys : envKeys).add(key);
25466
+ }
25467
+ return;
25468
+ }
25469
+ }
25470
+ const nextInFunction = inFunction || FUNCTION_BODY_TYPES.has(estree.type);
25471
+ for (const [childKey, value] of Object.entries(estree)) {
25472
+ if (childKey === "type") continue;
25473
+ visit(value, nextInFunction);
25474
+ }
25475
+ }, "visit");
25476
+ visit(program.body, false);
25477
+ return {
25478
+ envKeys: [...envKeys],
25479
+ publicEnvKeys: [...publicEnvKeys],
25480
+ builtinImports: [...builtinImports]
25481
+ };
25482
+ }
25483
+ function readNodeBuiltinSource(node) {
25484
+ if (node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ImportExpression") {
25485
+ const value = node.source?.value;
25486
+ return typeof value === "string" && value.startsWith("node:") ? value : void 0;
25487
+ }
25488
+ if (node.type === "CallExpression") {
25489
+ const callee = node.callee;
25490
+ if (callee?.type === "Identifier" && callee.name === "require") {
25491
+ const arg = node.arguments?.[0];
25492
+ const value = arg?.type === "Literal" ? arg.value : void 0;
25493
+ return typeof value === "string" && value.startsWith("node:") ? value : void 0;
25494
+ }
25495
+ }
25496
+ return void 0;
25497
+ }
25498
+ function readProcessEnvKey(node) {
25499
+ if (node.type !== "MemberExpression") return void 0;
25500
+ const object = node.object;
25501
+ if (!object || object.type !== "MemberExpression" || object.object?.type !== "Identifier" || object.object.name !== "process" || object.property?.name !== "env") {
25502
+ return void 0;
25503
+ }
25504
+ const property = node.property;
25505
+ if (!property) return void 0;
25506
+ if (!node.computed && property.type === "Identifier") {
25507
+ return property.name;
25508
+ }
25509
+ if (node.computed && property.type === "Literal") {
25510
+ const value = property.value;
25511
+ return typeof value === "string" ? value : void 0;
25512
+ }
25513
+ return void 0;
25514
+ }
25515
+ function formatClientBoundaryWarning(id, findings) {
25516
+ const { envKeys, publicEnvKeys, builtinImports } = findings;
25517
+ const lines = [`${id} is compiled for the client but uses server-only APIs:`];
25518
+ if (envKeys.length > 0) {
25519
+ lines.push(
25520
+ `- 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.`
25521
+ );
25522
+ }
25523
+ if (publicEnvKeys.length > 0) {
25524
+ lines.push(
25525
+ `- 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".`
25526
+ );
25527
+ }
25528
+ if (builtinImports.length > 0) {
25529
+ lines.push(
25530
+ `- import of ${builtinImports.join(", ")} \u2014 stubbed with an empty object in the browser. Move the import into server-only code.`
25531
+ );
25532
+ }
25533
+ return lines.join("\n");
25534
+ }
25535
+ var FUNCTION_BODY_TYPES;
25536
+ var init_client_boundary_env = __esm({
25537
+ "src/client-boundary-env.ts"() {
25538
+ "use strict";
25539
+ FUNCTION_BODY_TYPES = /* @__PURE__ */ new Set([
25540
+ "FunctionDeclaration",
25541
+ "FunctionExpression",
25542
+ "ArrowFunctionExpression"
25543
+ ]);
25544
+ __name(shouldInspectClientBoundary, "shouldInspectClientBoundary");
25545
+ __name(analyzeClientBoundary, "analyzeClientBoundary");
25546
+ __name(readNodeBuiltinSource, "readNodeBuiltinSource");
25547
+ __name(readProcessEnvKey, "readProcessEnvKey");
25548
+ __name(formatClientBoundaryWarning, "formatClientBoundaryWarning");
25549
+ }
25550
+ });
25551
+
25112
25552
  // src/image-vite.ts
25113
25553
  function farmImageImportsPlugin() {
25114
25554
  return {
@@ -25204,6 +25644,7 @@ var init_font = __esm({
25204
25644
  function createFarmImageHandler(config, options) {
25205
25645
  const fetcher = options.fetch ?? globalThis.fetch;
25206
25646
  const cache = new FarmImageMemoryCache(options.cacheEntries ?? 100);
25647
+ const inflight = /* @__PURE__ */ new Map();
25207
25648
  const allowedWidths = /* @__PURE__ */ new Set([...config.deviceSizes, ...config.imageSizes]);
25208
25649
  const allowedQualities = new Set(config.qualities);
25209
25650
  return /* @__PURE__ */ __name(async function handleFarmImage(request) {
@@ -25232,50 +25673,55 @@ function createFarmImageHandler(config, options) {
25232
25673
  "quality"
25233
25674
  );
25234
25675
  const accept = request.headers.get("accept") ?? "";
25676
+ const negotiatedFormat = selectOutputFormat(accept, config.formats) ?? "";
25235
25677
  const cacheKey = `${sourceUrl.href}
25236
25678
  ${width}
25237
25679
  ${quality}
25238
- ${accept}`;
25680
+ ${negotiatedFormat}`;
25239
25681
  let optimized = cache.get(cacheKey);
25240
25682
  if (!optimized) {
25241
- const fetchedSource = await fetchImageSource(
25242
- sourceUrl,
25243
- requestUrl.origin,
25244
- config,
25245
- fetcher,
25246
- options.fetchRemote,
25247
- options.validateRemoteUrl,
25248
- request.signal
25249
- );
25250
- const source = await readResponseWithLimit(
25251
- fetchedSource.response,
25252
- config.maximumResponseBody
25253
- );
25254
- const sourceType = detectImageContentType(source);
25255
- validateSourceType(sourceType, config);
25256
- throwIfAborted2(request.signal);
25257
- const result = await options.transform({
25258
- source,
25259
- sourceUrl: fetchedSource.url,
25260
- sourceType,
25261
- width,
25262
- quality,
25263
- accept,
25264
- formats: config.formats,
25265
- signal: request.signal
25683
+ optimized = await runCoalesced(inflight, cacheKey, request.signal, async (signal) => {
25684
+ const fetchedSource = await fetchImageSource(
25685
+ sourceUrl,
25686
+ requestUrl.origin,
25687
+ config,
25688
+ fetcher,
25689
+ options.fetchRemote,
25690
+ options.validateRemoteUrl,
25691
+ signal
25692
+ );
25693
+ const source = await readResponseWithLimit(
25694
+ fetchedSource.response,
25695
+ config.maximumResponseBody
25696
+ );
25697
+ const sourceType = detectImageContentType(source);
25698
+ validateSourceType(sourceType, config);
25699
+ throwIfAborted2(signal);
25700
+ const result = await options.transform({
25701
+ source,
25702
+ sourceUrl: fetchedSource.url,
25703
+ sourceType,
25704
+ width,
25705
+ quality,
25706
+ accept,
25707
+ formats: config.formats,
25708
+ signal,
25709
+ maximumResponseBody: config.maximumResponseBody
25710
+ });
25711
+ throwIfAborted2(signal);
25712
+ validateTransformedResult(result, config);
25713
+ const entry = {
25714
+ ...result,
25715
+ etag: createImageEtag(result.body),
25716
+ cacheControl: `public, max-age=${config.minimumCacheTTL}, stale-while-revalidate=${Math.max(
25717
+ config.minimumCacheTTL,
25718
+ 60
25719
+ )}`,
25720
+ expiresAt: Date.now() + config.minimumCacheTTL * 1e3
25721
+ };
25722
+ cache.set(cacheKey, entry);
25723
+ return entry;
25266
25724
  });
25267
- throwIfAborted2(request.signal);
25268
- validateTransformedResult(result, config);
25269
- optimized = {
25270
- ...result,
25271
- etag: createImageEtag(result.body),
25272
- cacheControl: `public, max-age=${config.minimumCacheTTL}, stale-while-revalidate=${Math.max(
25273
- config.minimumCacheTTL,
25274
- 60
25275
- )}`,
25276
- expiresAt: Date.now() + config.minimumCacheTTL * 1e3
25277
- };
25278
- cache.set(cacheKey, optimized);
25279
25725
  }
25280
25726
  return createOptimizedImageResponse(request, optimized, config);
25281
25727
  } catch (error) {
@@ -25426,6 +25872,44 @@ async function fetchImageSource(initialUrl, requestOrigin, config, fetcher, fetc
25426
25872
  await validateImageSourceUrl(currentUrl, requestOrigin, config, validateRemoteUrl);
25427
25873
  }
25428
25874
  }
25875
+ async function runCoalesced(inflight, key, requestSignal, run) {
25876
+ let entry = inflight.get(key);
25877
+ if (!entry) {
25878
+ const controller = new AbortController();
25879
+ const created = {
25880
+ controller,
25881
+ waiters: 0,
25882
+ promise: void 0
25883
+ };
25884
+ created.promise = run(controller.signal).finally(() => {
25885
+ if (inflight.get(key) === created) inflight.delete(key);
25886
+ });
25887
+ created.promise.catch(() => {
25888
+ });
25889
+ inflight.set(key, created);
25890
+ entry = created;
25891
+ }
25892
+ const pending = entry;
25893
+ pending.waiters += 1;
25894
+ try {
25895
+ return await raceRequestAbort(pending.promise, requestSignal);
25896
+ } finally {
25897
+ pending.waiters -= 1;
25898
+ if (pending.waiters === 0 && inflight.get(key) === pending) {
25899
+ inflight.delete(key);
25900
+ pending.controller.abort();
25901
+ }
25902
+ }
25903
+ }
25904
+ function raceRequestAbort(promise, signal) {
25905
+ if (!signal) return promise;
25906
+ if (signal.aborted) return Promise.reject(signal.reason ?? new Error("Aborted"));
25907
+ return new Promise((resolve10, reject) => {
25908
+ const onAbort = /* @__PURE__ */ __name(() => reject(signal.reason ?? new Error("Aborted")), "onAbort");
25909
+ signal.addEventListener("abort", onAbort, { once: true });
25910
+ promise.then(resolve10, reject).finally(() => signal.removeEventListener("abort", onAbort));
25911
+ });
25912
+ }
25429
25913
  async function readResponseWithLimit(response, limit) {
25430
25914
  const contentLength = response.headers.get("content-length");
25431
25915
  if (contentLength && Number(contentLength) > limit) {
@@ -25593,7 +26077,7 @@ function throwIfAborted2(signal) {
25593
26077
  function isAbortError(error) {
25594
26078
  return error instanceof Error && error.name === "AbortError";
25595
26079
  }
25596
- var _FarmImageRequestError, FarmImageRequestError, _FarmImageMemoryCache, FarmImageMemoryCache;
26080
+ var _FarmImageRequestError, FarmImageRequestError, DEFAULT_IMAGE_TRANSFORM_BODY_LIMIT, _FarmImageMemoryCache, FarmImageMemoryCache;
25597
26081
  var init_image_server = __esm({
25598
26082
  "src/image-server.ts"() {
25599
26083
  "use strict";
@@ -25609,12 +26093,15 @@ var init_image_server = __esm({
25609
26093
  __name(_FarmImageRequestError, "FarmImageRequestError");
25610
26094
  FarmImageRequestError = _FarmImageRequestError;
25611
26095
  __name(createFarmImageHandler, "createFarmImageHandler");
26096
+ DEFAULT_IMAGE_TRANSFORM_BODY_LIMIT = 10 * 1024 * 1024;
25612
26097
  __name(selectOutputFormat, "selectOutputFormat");
25613
26098
  __name(isPrivateImageAddress, "isPrivateImageAddress");
25614
26099
  __name(parseAllowedInteger, "parseAllowedInteger");
25615
26100
  __name(resolveImageSourceUrl, "resolveImageSourceUrl");
25616
26101
  __name(validateImageSourceUrl, "validateImageSourceUrl");
25617
26102
  __name(fetchImageSource, "fetchImageSource");
26103
+ __name(runCoalesced, "runCoalesced");
26104
+ __name(raceRequestAbort, "raceRequestAbort");
25618
26105
  __name(readResponseWithLimit, "readResponseWithLimit");
25619
26106
  __name(cancelResponseBody, "cancelResponseBody");
25620
26107
  __name(detectImageContentType, "detectImageContentType");
@@ -31478,6 +31965,22 @@ function isFarmConfigFile(file, root) {
31478
31965
  const relative6 = normalized.startsWith(`${rootSlug}/`) ? normalized.slice(rootSlug.length + 1) : normalized;
31479
31966
  return FARM_CONFIG_FILENAMES.has(relative6);
31480
31967
  }
31968
+ function warnClientBoundaryOnce(context2, id, code, config) {
31969
+ if (warnedClientBoundaryIds.has(id)) return;
31970
+ let program;
31971
+ try {
31972
+ program = context2.parse(code);
31973
+ } catch {
31974
+ return;
31975
+ }
31976
+ const publicKeys = new Set(Object.keys(config.env?.public ?? {}));
31977
+ const findings = analyzeClientBoundary(program, publicKeys);
31978
+ if (findings.envKeys.length === 0 && findings.publicEnvKeys.length === 0 && findings.builtinImports.length === 0) {
31979
+ return;
31980
+ }
31981
+ warnedClientBoundaryIds.add(id);
31982
+ logger.warn(formatClientBoundaryWarning(id, findings));
31983
+ }
31481
31984
  function farmPlugin(options = {}, initialPluginManager) {
31482
31985
  const imageImports = farmImageImportsPlugin();
31483
31986
  const fontImports = farmFontImportsPlugin({
@@ -32088,6 +32591,22 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32088
32591
  }
32089
32592
  return;
32090
32593
  }
32594
+ if (openAPIManager && options.openapi?.specRoute && requestPathname === options.openapi.specRoute) {
32595
+ if (requestMethod !== "GET" && requestMethod !== "HEAD") {
32596
+ res.statusCode = 405;
32597
+ res.setHeader("Allow", "GET, HEAD");
32598
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
32599
+ res.end("Method Not Allowed");
32600
+ return;
32601
+ }
32602
+ const spec = await openAPIManager.getSpec();
32603
+ const body = JSON.stringify(spec);
32604
+ res.statusCode = 200;
32605
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
32606
+ res.setHeader("Cache-Control", "no-store");
32607
+ res.end(requestMethod === "HEAD" ? void 0 : body);
32608
+ return;
32609
+ }
32091
32610
  if (openAPIManager && requestPathname === options.openapi?.route) {
32092
32611
  const docsHandler = openAPIManager.getDocsRouteHandler();
32093
32612
  return docsHandler(req, res);
@@ -32098,6 +32617,11 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32098
32617
  docsHeaders.set(key, Array.isArray(value) ? value.join(", ") : value);
32099
32618
  }
32100
32619
  }
32620
+ const runAppMiddlewareForContentRoute = /* @__PURE__ */ __name(async () => {
32621
+ if (!middlewareManager?.hasMiddleware()) return false;
32622
+ const middlewareRequest = createRequestFromNodeRequest(req, new URL(fullUrl));
32623
+ return farmApp.getServerRenderer().runWithRequestContext(middlewareRequest, () => middlewareManager.execute(req, res));
32624
+ }, "runAppMiddlewareForContentRoute");
32101
32625
  if (farmDocsHandler) {
32102
32626
  const docsRequest = new Request(fullUrl, {
32103
32627
  method: requestMethod,
@@ -32105,6 +32629,7 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32105
32629
  });
32106
32630
  const docsResponse = await farmDocsHandler(docsRequest.clone());
32107
32631
  if (docsResponse) {
32632
+ if (await runAppMiddlewareForContentRoute()) return;
32108
32633
  await sendWebResponse(
32109
32634
  res,
32110
32635
  await wrapFarmDocsResponseWithLayouts(docsRequest, docsResponse)
@@ -32131,6 +32656,7 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32131
32656
  }, "resolveSource")
32132
32657
  });
32133
32658
  if (markdownSourceResponse) {
32659
+ if (await runAppMiddlewareForContentRoute()) return;
32134
32660
  await sendWebResponse(res, markdownSourceResponse);
32135
32661
  return;
32136
32662
  }
@@ -32144,9 +32670,23 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32144
32670
  renderPage: /* @__PURE__ */ __name(async (request) => fetch(request), "renderPage")
32145
32671
  });
32146
32672
  if (markdownResponse) {
32673
+ if (await runAppMiddlewareForContentRoute()) return;
32147
32674
  await sendWebResponse(res, markdownResponse);
32148
32675
  return;
32149
32676
  }
32677
+ if (farmRequestWantsMarkdown(requestPathname, req.headers.accept)) {
32678
+ const markdownRoute = farmApp.getRouteManager().matchRoute(normalizeFarmMarkdownRoutePath(requestPathname));
32679
+ if (!markdownRoute.route) {
32680
+ res.statusCode = 404;
32681
+ res.setHeader("Content-Type", FARM_MARKDOWN_CONTENT_TYPE);
32682
+ res.setHeader("X-Farm-Markdown-Error", "404");
32683
+ res.setHeader("Cache-Control", "no-store");
32684
+ res.end(
32685
+ createFarmMarkdownErrorBody(404, requestPathname, farmConfig.basePath || "/")
32686
+ );
32687
+ return;
32688
+ }
32689
+ }
32150
32690
  const markdownPageTarget = resolveMarkdownMirrorTarget(
32151
32691
  farmApp.getConfig().md,
32152
32692
  requestPathname,
@@ -33095,6 +33635,9 @@ export const manifest = getManifest();
33095
33635
  }
33096
33636
  }
33097
33637
  }
33638
+ if (!transformOptions?.ssr && shouldInspectClientBoundary(id, code)) {
33639
+ warnClientBoundaryOnce(this, id, code, farmApp?.getConfig() ?? options);
33640
+ }
33098
33641
  if (typeof imageImports.transform === "function") {
33099
33642
  const imageModule = await imageImports.transform.call(this, code, id, transformOptions);
33100
33643
  if (imageModule) return imageModule;
@@ -35434,7 +35977,7 @@ async function defineConfig2(config = {}) {
35434
35977
  }
35435
35978
  };
35436
35979
  }
35437
- var fs14, path38, import_node_url9, ALL_TYPE_ARTIFACTS, createEmptyTypeArtifactSelection, FARM_I18N_CLIENT_BRIDGE_ID, warnedDeprecatedDevtoolsDashboard, EMPTY_FARM_DOCS_SEARCH_CLIENT_RUNTIME, FARM_CONFIG_FILENAMES;
35980
+ var fs14, path38, import_node_url9, ALL_TYPE_ARTIFACTS, createEmptyTypeArtifactSelection, FARM_I18N_CLIENT_BRIDGE_ID, warnedDeprecatedDevtoolsDashboard, EMPTY_FARM_DOCS_SEARCH_CLIENT_RUNTIME, FARM_CONFIG_FILENAMES, warnedClientBoundaryIds;
35438
35981
  var init_vite2 = __esm({
35439
35982
  "src/vite.ts"() {
35440
35983
  "use strict";
@@ -35471,6 +36014,7 @@ var init_vite2 = __esm({
35471
36014
  init_server_context();
35472
36015
  init_dev_static();
35473
36016
  init_server_query_boundary();
36017
+ init_client_boundary_env();
35474
36018
  init_deployment();
35475
36019
  init_image_config();
35476
36020
  init_image_vite();
@@ -35570,6 +36114,8 @@ async function mountFarmDocsSearch() {
35570
36114
  __name(walkModuleAst, "walkModuleAst");
35571
36115
  __name(isFarmModuleAstNode, "isFarmModuleAstNode");
35572
36116
  __name(isFarmConfigFile, "isFarmConfigFile");
36117
+ warnedClientBoundaryIds = /* @__PURE__ */ new Set();
36118
+ __name(warnClientBoundaryOnce, "warnClientBoundaryOnce");
35573
36119
  __name(farmPlugin, "farmPlugin");
35574
36120
  __name(isFarmClientVirtualId, "isFarmClientVirtualId");
35575
36121
  __name(generateProgrammaticRouteModule, "generateProgrammaticRouteModule");
@@ -40048,7 +40594,9 @@ async function buildSSRInMemory(productionVite, config, root, routeManager, apiR
40048
40594
  }
40049
40595
  openAPIReference = {
40050
40596
  route: config.openapi.route,
40051
- html: renderOpenAPIReferenceHTML2(spec, config.openapi)
40597
+ html: renderOpenAPIReferenceHTML2(spec, config.openapi),
40598
+ spec,
40599
+ specRoute: config.openapi.specRoute || null
40052
40600
  };
40053
40601
  }
40054
40602
  const middlewareRoutes = await discoverMiddlewareRoutes(appDirs);
@@ -40670,6 +41218,7 @@ import { dirname as farmDocsDirname, join as farmDocsJoin } from "node:path";
40670
41218
  import { fileURLToPath as farmDocsFileURLToPath } from "node:url";` : "";
40671
41219
  const markdownHandlerImport = config.md?.enabled ? `import { applyMarkdownNegotiationHeaders, createMarkdownMirrorResponse } from "@farm.js/core/markdown";` : "";
40672
41220
  const appMarkdownImport = hasMarkdownPages ? `import { createFarmMarkdownRouteModule, createFarmMarkdownSourceResponse } from "@farm.js/core/app-markdown";` : "const createFarmMarkdownSourceResponse = null;";
41221
+ const markdownErrorImport = `import { FARM_MARKDOWN_CONTENT_TYPE, createFarmMarkdownErrorBody, farmRequestWantsMarkdown } from "@farm.js/core/app-markdown";`;
40673
41222
  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;
40674
41223
  const mdxComponentsImport = mdxComponentsPath ? `import * as FarmMdxComponentsModule from ${toVirtualEntryImportSpecifier(mdxComponentsPath)};` : "";
40675
41224
  const layerConfigPaths = (config.layers || []).map((layer) => layer.configFile).filter((configFile) => Boolean(configFile)).filter((configFile) => configFile !== configModulePath);
@@ -40812,6 +41361,7 @@ ${docsFontImport}
40812
41361
  ${docsRuntimeImport}
40813
41362
  ${markdownHandlerImport}
40814
41363
  ${appMarkdownImport}
41364
+ ${markdownErrorImport}
40815
41365
  ${mdxComponentsImport}
40816
41366
  ${integrationImports}
40817
41367
  ${providerServerImports}
@@ -42355,12 +42905,38 @@ async function handleFarmRequestInContext(
42355
42905
  }
42356
42906
  ` : ""}
42357
42907
 
42908
+ ${hasMiddlewareRuntime ? `
42909
+ const requestBeforeMiddleware = request;
42910
+ const middlewareResult = await farmMiddlewareRunner(request);
42911
+ if (middlewareResult.response) {
42912
+ return middlewareResult.response;
42913
+ }
42914
+ request = middlewareResult.request;
42915
+ const middlewareData = middlewareResult.data;
42916
+ const middlewareContext = middlewareResult.context;
42917
+ const middlewareHeaders = middlewareResult.headers;
42918
+ url = new URL(request.url);
42919
+ pathname = url.pathname;
42920
+ routePathname = getFarmRoutePathname(pathname);
42921
+
42922
+ // Middleware may replace the Request when it rewrites a URL. Re-enter the
42923
+ // request store so pages, layouts, and route context observe that rewritten
42924
+ // request instead of the original outer-handler value.
42925
+ const runResolvedRequest = async () => {
42926
+ ` : `
42927
+ const middlewareData = undefined;
42928
+ const middlewareContext = undefined;
42929
+ const middlewareHeaders = undefined;
42930
+ `}
42931
+
42358
42932
  ${config.docs?.enabled ? `
42933
+ // Docs responses are served after app middleware so route guards and
42934
+ // middleware headers apply to the docs engine like any other page content.
42359
42935
  if (farmDocsHandler) {
42360
42936
  const docsResponse = await farmDocsHandler(request.clone());
42361
42937
  if (docsResponse) {
42362
42938
  if (!docsResponse.headers.get("content-type")?.toLowerCase().includes("text/html")) {
42363
- return docsResponse;
42939
+ return applyProductionMiddlewareHeaders(docsResponse, middlewareHeaders);
42364
42940
  }
42365
42941
  const wrappedDocsResponse = await wrapFarmDocsResponseWithLayouts(
42366
42942
  request,
@@ -42368,16 +42944,18 @@ async function handleFarmRequestInContext(
42368
42944
  );
42369
42945
  const docsHeaders = new Headers(wrappedDocsResponse.headers);
42370
42946
  docsHeaders.set("x-farm-preload-buffered", "1");
42371
- return new Response(wrappedDocsResponse.body, {
42947
+ return applyProductionMiddlewareHeaders(new Response(wrappedDocsResponse.body, {
42372
42948
  status: wrappedDocsResponse.status,
42373
42949
  statusText: wrappedDocsResponse.statusText,
42374
42950
  headers: docsHeaders,
42375
- });
42951
+ }), middlewareHeaders);
42376
42952
  }
42377
42953
  }
42378
42954
  ` : ""}
42379
42955
 
42380
42956
  ${hasMarkdownPages ? `
42957
+ // The raw markdown source of a page route is the page's content in another
42958
+ // representation; middleware guarding the route must run before serving it.
42381
42959
  const markdownSourceResponse = await createFarmMarkdownSourceResponse?.({
42382
42960
  request: request.clone(),
42383
42961
  config: farmMdxConfig,
@@ -42387,7 +42965,7 @@ async function handleFarmRequestInContext(
42387
42965
  },
42388
42966
  });
42389
42967
  if (markdownSourceResponse) {
42390
- return markdownSourceResponse;
42968
+ return applyProductionMiddlewareHeaders(markdownSourceResponse, middlewareHeaders);
42391
42969
  }
42392
42970
  ` : ""}
42393
42971
 
@@ -42401,35 +42979,11 @@ async function handleFarmRequestInContext(
42401
42979
  renderPage: (targetRequest) => handleFarmRequest(targetRequest),
42402
42980
  });
42403
42981
  if (markdownResponse) {
42404
- return markdownResponse;
42982
+ return applyProductionMiddlewareHeaders(markdownResponse, middlewareHeaders);
42405
42983
  }
42406
42984
  }
42407
42985
  ` : ""}
42408
42986
 
42409
- ${hasMiddlewareRuntime ? `
42410
- const requestBeforeMiddleware = request;
42411
- const middlewareResult = await farmMiddlewareRunner(request);
42412
- if (middlewareResult.response) {
42413
- return middlewareResult.response;
42414
- }
42415
- request = middlewareResult.request;
42416
- const middlewareData = middlewareResult.data;
42417
- const middlewareContext = middlewareResult.context;
42418
- const middlewareHeaders = middlewareResult.headers;
42419
- url = new URL(request.url);
42420
- pathname = url.pathname;
42421
- routePathname = getFarmRoutePathname(pathname);
42422
-
42423
- // Middleware may replace the Request when it rewrites a URL. Re-enter the
42424
- // request store so pages, layouts, and route context observe that rewritten
42425
- // request instead of the original outer-handler value.
42426
- const runResolvedRequest = async () => {
42427
- ` : `
42428
- const middlewareData = undefined;
42429
- const middlewareContext = undefined;
42430
- const middlewareHeaders = undefined;
42431
- `}
42432
-
42433
42987
  ${apiRoutes.length > 0 ? `
42434
42988
  const apiResponse = await handleAPIRequest(request.clone());
42435
42989
  if (apiResponse) {
@@ -42437,6 +42991,31 @@ async function handleFarmRequestInContext(
42437
42991
  }
42438
42992
  ` : ""}
42439
42993
 
42994
+ if (
42995
+ farmOpenAPIReference &&
42996
+ farmOpenAPIReference.specRoute &&
42997
+ normalizeRuntimePath(pathname) === normalizeRuntimePath(farmOpenAPIReference.specRoute)
42998
+ ) {
42999
+ const method = request.method.toUpperCase();
43000
+ const specResponse = method === "GET" || method === "HEAD"
43001
+ ? new Response(method === "HEAD" ? null : JSON.stringify(farmOpenAPIReference.spec), {
43002
+ status: 200,
43003
+ headers: {
43004
+ "Content-Type": "application/json; charset=utf-8",
43005
+ "Cache-Control": "public, max-age=0, must-revalidate",
43006
+ "X-Content-Type-Options": "nosniff",
43007
+ },
43008
+ })
43009
+ : new Response("Method Not Allowed", {
43010
+ status: 405,
43011
+ headers: {
43012
+ "Allow": "GET, HEAD",
43013
+ "Content-Type": "text/plain; charset=utf-8",
43014
+ },
43015
+ });
43016
+ return applyProductionMiddlewareHeaders(specResponse, middlewareHeaders);
43017
+ }
43018
+
42440
43019
  if (
42441
43020
  farmOpenAPIReference &&
42442
43021
  normalizeRuntimePath(pathname) === normalizeRuntimePath(farmOpenAPIReference.route)
@@ -42909,7 +43488,7 @@ async function handleFarmRequestInContext(
42909
43488
  }
42910
43489
  }
42911
43490
 
42912
- const renderedMetadata = renderMetadataHead(mergedMetadata);
43491
+ const renderedMetadata = renderMetadataHead(mergedMetadata, { pathname, jsonLd: ${JSON.stringify(config.agent?.jsonLd ?? false)} });
42913
43492
  const title = renderedMetadata.title;
42914
43493
  const metaTags = renderedMetadata.tags;
42915
43494
  const hasFavicon = renderedMetadata.hasFavicon;
@@ -43285,6 +43864,30 @@ async function handleFarmRequestInContext(
43285
43864
 
43286
43865
  // 404 fallback - render proper HTML page
43287
43866
  emitFarmEvent({ type: "route.notFound", pathname });
43867
+
43868
+ // Agents that navigate in Markdown (a \`.md\` URL or \`Accept: text/markdown\`)
43869
+ // get a Markdown error body instead of the HTML not-found shell.
43870
+ if (farmRequestWantsMarkdown(pathname, request.headers.get("accept"))) {
43871
+ emitFarmEvent({
43872
+ type: "render.complete",
43873
+ route: pathname,
43874
+ pathname,
43875
+ status: 404,
43876
+ durationMs: Date.now() - requestStartTime,
43877
+ });
43878
+ return applyProductionMiddlewareHeaders(new Response(
43879
+ createFarmMarkdownErrorBody(404, pathname, applyFarmBasePath("/", farmResolvedRuntimeConfig.basePath)),
43880
+ {
43881
+ status: 404,
43882
+ headers: {
43883
+ "Content-Type": FARM_MARKDOWN_CONTENT_TYPE,
43884
+ "X-Farm-Markdown-Error": "404",
43885
+ "Cache-Control": "no-store",
43886
+ },
43887
+ }
43888
+ ), middlewareHeaders);
43889
+ }
43890
+
43288
43891
  try {
43289
43892
  const defaultNotFoundHomeHref = applyFarmBasePath("/", farmResolvedRuntimeConfig.basePath);
43290
43893
  // Default 404 page component
@@ -43524,6 +44127,9 @@ async function handleFarmFetch(request, context) {
43524
44127
  const healthResponse = await farmProductionLifecycle.handleHealthRequest(request);
43525
44128
  if (healthResponse) return healthResponse;
43526
44129
 
44130
+ const productionSiteAttestation = farmProductionSiteTelemetry?.handleAttestation(request);
44131
+ if (productionSiteAttestation) return productionSiteAttestation;
44132
+
43527
44133
  farmProductionSiteTelemetry?.report(
43528
44134
  request.url,
43529
44135
  typeof context?.waitUntil === "function" ? context.waitUntil.bind(context) : undefined,