@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/server.cjs CHANGED
@@ -922,6 +922,29 @@ var init_search_params = __esm({
922
922
  }
923
923
  });
924
924
 
925
+ // src/utils/runtime-env.ts
926
+ function getFarmRuntimeBindings() {
927
+ const runtimeBindings = globalThis.__env__;
928
+ return runtimeBindings && typeof runtimeBindings === "object" ? runtimeBindings : void 0;
929
+ }
930
+ function readFarmEnvironmentValue(name2) {
931
+ const runtimeValue = getFarmRuntimeBindings()?.[name2];
932
+ if (typeof runtimeValue === "string") return runtimeValue;
933
+ return typeof process !== "undefined" ? process.env?.[name2] : void 0;
934
+ }
935
+ function isFarmDeployedRuntime() {
936
+ if (getFarmRuntimeBindings()) return true;
937
+ return readFarmEnvironmentValue("NODE_ENV") === "production";
938
+ }
939
+ var init_runtime_env = __esm({
940
+ "src/utils/runtime-env.ts"() {
941
+ "use strict";
942
+ __name(getFarmRuntimeBindings, "getFarmRuntimeBindings");
943
+ __name(readFarmEnvironmentValue, "readFarmEnvironmentValue");
944
+ __name(isFarmDeployedRuntime, "isFarmDeployedRuntime");
945
+ }
946
+ });
947
+
925
948
  // src/utils/decode.ts
926
949
  function decodeRouteSegment(segment) {
927
950
  try {
@@ -1491,7 +1514,8 @@ function resolveWorkflowsConfig(workflows) {
1491
1514
  dirs,
1492
1515
  route: normalizeWorkflowRoute(options.route || DEFAULT_FARM_WORKFLOW_ROUTE),
1493
1516
  secretEnv: options.secretEnv || DEFAULT_FARM_WORKFLOW_SECRET_ENV,
1494
- secret: options.secret
1517
+ secret: options.secret,
1518
+ allowUnsecured: options.allowUnsecured === true
1495
1519
  };
1496
1520
  }
1497
1521
  async function discoverFarmWorkflows(config, options = {}) {
@@ -1617,8 +1641,11 @@ async function prepareFarmWorkflowsForNitro(config) {
1617
1641
  await fs16.mkdir(generatedDir, { recursive: true });
1618
1642
  const tasks = {};
1619
1643
  const scheduledTasks = createScheduledTasks(workflows);
1644
+ const wrapperNames = resolveWorkflowWrapperFileNames(workflows.map((workflow) => workflow.id));
1620
1645
  for (const workflow of workflows) {
1621
- const wrapperPath = toPosixPath(path37.join(generatedDir, `${safeFileName(workflow.id)}.mjs`));
1646
+ const wrapperPath = toPosixPath(
1647
+ path37.join(generatedDir, `${wrapperNames.get(workflow.id)}.mjs`)
1648
+ );
1622
1649
  await fs16.writeFile(wrapperPath, createNitroTaskWrapper(workflow), "utf8");
1623
1650
  tasks[workflow.id] = {
1624
1651
  handler: wrapperPath,
@@ -1856,6 +1883,7 @@ import {
1856
1883
  const route = ${JSON.stringify(config.route)};
1857
1884
  const secretEnv = ${JSON.stringify(config.secretEnv)};
1858
1885
  const inlineSecret = ${JSON.stringify(config.secret || "")};
1886
+ const allowUnsecured = ${JSON.stringify(config.allowUnsecured === true)};
1859
1887
  const bodySizeLimit = ${JSON.stringify(server.bodySizeLimit)};
1860
1888
  const workflows = ${JSON.stringify(workflows.map(toWorkflowMetadata))};
1861
1889
  const workflowIds = new Set(workflows.map((workflow) => workflow.id));
@@ -1885,7 +1913,10 @@ function getSecret() {
1885
1913
 
1886
1914
  function verifySecret(event) {
1887
1915
  const secret = getSecret();
1888
- if (!secret) return null;
1916
+ if (!secret) {
1917
+ if (allowUnsecured) return null;
1918
+ return json({ error: ${JSON.stringify(MISSING_FARM_WORKFLOW_SECRET_ERROR)} }, 401);
1919
+ }
1889
1920
  const authorization = getHeader(event, "authorization") || "";
1890
1921
  const headerSecret = getHeader(event, "x-farm-workflow-secret") || "";
1891
1922
  const bearer = authorization.match(/^Bearer\\s+(.+)$/i)?.[1] || "";
@@ -1979,8 +2010,16 @@ function readScheduledTime(payload) {
1979
2010
  return payload && typeof payload === "object" && "scheduledTime" in payload ? payload.scheduledTime : void 0;
1980
2011
  }
1981
2012
  function verifyWorkflowSecret(request, config) {
1982
- const secret = config.secret || process.env[config.secretEnv] || "";
1983
- if (!secret) return null;
2013
+ const secret = config.secret || readFarmEnvironmentValue(config.secretEnv) || "";
2014
+ if (!secret) {
2015
+ if (config.allowUnsecured === true || !isFarmDeployedRuntime()) return null;
2016
+ return Response.json(
2017
+ {
2018
+ error: MISSING_FARM_WORKFLOW_SECRET_ERROR
2019
+ },
2020
+ { status: 401 }
2021
+ );
2022
+ }
1984
2023
  const authorization = request.headers.get("authorization") || "";
1985
2024
  const bearer = authorization.match(/^Bearer\s+(.+)$/i)?.[1] || "";
1986
2025
  const headerSecret = request.headers.get("x-farm-workflow-secret") || "";
@@ -1993,18 +2032,50 @@ function joinRoute(...parts) {
1993
2032
  function trimSlashes(value) {
1994
2033
  return value.replace(/^\/+|\/+$/g, "");
1995
2034
  }
2035
+ function resolveWorkflowWrapperFileNames(ids) {
2036
+ const groups = /* @__PURE__ */ new Map();
2037
+ for (const id of ids) {
2038
+ const key = safeFileName(id).toLowerCase();
2039
+ groups.set(key, (groups.get(key) ?? 0) + 1);
2040
+ }
2041
+ const resolved = /* @__PURE__ */ new Map();
2042
+ const claimed = /* @__PURE__ */ new Map();
2043
+ for (const id of ids) {
2044
+ const base = safeFileName(id);
2045
+ const key = base.toLowerCase();
2046
+ const fileName = (groups.get(key) ?? 0) > 1 ? `${key}-${workflowIdFingerprint(id)}` : base;
2047
+ const claimedBy = claimed.get(fileName.toLowerCase());
2048
+ if (claimedBy !== void 0) {
2049
+ throw new Error(
2050
+ `Farm workflows ${JSON.stringify(claimedBy)} and ${JSON.stringify(id)} generate the same wrapper file ${JSON.stringify(`${fileName}.mjs`)}. Rename one of them.`
2051
+ );
2052
+ }
2053
+ claimed.set(fileName.toLowerCase(), id);
2054
+ resolved.set(id, fileName);
2055
+ }
2056
+ return resolved;
2057
+ }
2058
+ function workflowIdFingerprint(value) {
2059
+ let hash = 2166136261;
2060
+ for (let index = 0; index < value.length; index += 1) {
2061
+ hash ^= value.charCodeAt(index);
2062
+ hash = Math.imul(hash, 16777619);
2063
+ }
2064
+ return (hash >>> 0).toString(16).padStart(8, "0");
2065
+ }
1996
2066
  function safeFileName(value) {
1997
2067
  return value.replace(/[^a-zA-Z0-9._-]+/g, "-") || "workflow";
1998
2068
  }
1999
2069
  function relativePath(root, filePath) {
2000
2070
  return filePath.replace(/\\/g, "/").replace(`${root.replace(/\\/g, "/")}/`, "");
2001
2071
  }
2002
- var import_node_path, DEFAULT_FARM_WORKFLOW_DIRS, DEFAULT_FARM_WORKFLOW_ROUTE, DEFAULT_FARM_WORKFLOW_SECRET_ENV;
2072
+ var import_node_path, DEFAULT_FARM_WORKFLOW_DIRS, DEFAULT_FARM_WORKFLOW_ROUTE, DEFAULT_FARM_WORKFLOW_SECRET_ENV, MISSING_FARM_WORKFLOW_SECRET_ERROR;
2003
2073
  var init_workflows = __esm({
2004
2074
  "src/workflows.ts"() {
2005
2075
  "use strict";
2006
2076
  init_server_http();
2007
2077
  init_search_params();
2078
+ init_runtime_env();
2008
2079
  init_decode();
2009
2080
  init_utils();
2010
2081
  init_route_pattern();
@@ -2012,6 +2083,7 @@ var init_workflows = __esm({
2012
2083
  DEFAULT_FARM_WORKFLOW_DIRS = ["src/jobs", "src/workflows", "src/cron"];
2013
2084
  DEFAULT_FARM_WORKFLOW_ROUTE = "/api/_farm/workflows";
2014
2085
  DEFAULT_FARM_WORKFLOW_SECRET_ENV = "CRON_SECRET";
2086
+ 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.";
2015
2087
  __name(defineWorkflow, "defineWorkflow");
2016
2088
  __name(defineTask, "defineTask");
2017
2089
  __name(defineCron, "defineCron");
@@ -2044,6 +2116,8 @@ var init_workflows = __esm({
2044
2116
  __name(verifyWorkflowSecret, "verifyWorkflowSecret");
2045
2117
  __name(joinRoute, "joinRoute");
2046
2118
  __name(trimSlashes, "trimSlashes");
2119
+ __name(resolveWorkflowWrapperFileNames, "resolveWorkflowWrapperFileNames");
2120
+ __name(workflowIdFingerprint, "workflowIdFingerprint");
2047
2121
  __name(safeFileName, "safeFileName");
2048
2122
  __name(relativePath, "relativePath");
2049
2123
  }
@@ -2194,6 +2268,7 @@ var init_cron = __esm({
2194
2268
  "src/cron.ts"() {
2195
2269
  "use strict";
2196
2270
  init_utils();
2271
+ init_runtime_env();
2197
2272
  DEFAULT_FARM_CRON_SECRET_ENV = "CRON_SECRET";
2198
2273
  CRON_FIELD_RANGES = [
2199
2274
  [0, 59, "minute"],
@@ -2574,9 +2649,13 @@ function getStorage(namespace) {
2574
2649
  const namespaced = (0, import_unstorage.prefixStorage)(globalStorage, base);
2575
2650
  return {
2576
2651
  ...namespaced,
2577
- async clear() {
2578
- const keys = await namespaced.getKeys();
2579
- await Promise.all(keys.map((key) => namespaced.removeItem(key)));
2652
+ // Scope the wipe to the namespace instead of the whole storage, and keep
2653
+ // the caller's base so `clear("sessions")` cannot take unrelated keys with
2654
+ // it. Dropping the argument silently widens a targeted clear into a
2655
+ // namespace-wide delete.
2656
+ async clear(base2, opts) {
2657
+ const keys = await namespaced.getKeys(base2);
2658
+ await Promise.all(keys.map((key) => namespaced.removeItem(key, opts)));
2580
2659
  }
2581
2660
  };
2582
2661
  }
@@ -3571,6 +3650,114 @@ var init_cache_invalidation = __esm({
3571
3650
  }
3572
3651
  });
3573
3652
 
3653
+ // src/request-origin.ts
3654
+ function getRequestSourceOrigin(request) {
3655
+ const origin = request.headers.get("origin")?.trim();
3656
+ if (origin) {
3657
+ return parseSourceOrigin(origin);
3658
+ }
3659
+ const referer = request.headers.get("referer")?.trim();
3660
+ if (referer) {
3661
+ return parseSourceOrigin(referer);
3662
+ }
3663
+ return { ok: true, origin: null };
3664
+ }
3665
+ function parseSourceOrigin(value) {
3666
+ if (value === "null") {
3667
+ return { ok: false, reason: "opaque-origin" };
3668
+ }
3669
+ try {
3670
+ const parsed = new URL(value);
3671
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
3672
+ return { ok: false, reason: "invalid-origin" };
3673
+ }
3674
+ return { ok: true, origin: parsed.origin };
3675
+ } catch {
3676
+ return { ok: false, reason: "invalid-origin" };
3677
+ }
3678
+ }
3679
+ function matchesHostHeader(sourceOrigin, request) {
3680
+ const host = request.headers.get("host")?.trim().toLowerCase();
3681
+ if (!host) return false;
3682
+ try {
3683
+ const source = new URL(sourceOrigin);
3684
+ const target = new URL(request.url);
3685
+ return source.protocol === target.protocol && source.host.toLowerCase() === host;
3686
+ } catch {
3687
+ return false;
3688
+ }
3689
+ }
3690
+ function matchesAllowedOrigin(sourceOrigin, pattern) {
3691
+ const source = new URL(sourceOrigin);
3692
+ if (!pattern.includes("*")) {
3693
+ return pattern.includes("://") ? source.origin === pattern : source.host === pattern;
3694
+ }
3695
+ const schemeEnd = pattern.indexOf("://");
3696
+ const scheme = schemeEnd === -1 ? null : pattern.slice(0, schemeEnd + 1);
3697
+ const hostPattern = pattern.slice(schemeEnd === -1 ? 0 : schemeEnd + 3);
3698
+ const [wildcardHost, port] = splitHostAndPort(hostPattern);
3699
+ const baseHost = wildcardHost.slice(2);
3700
+ if (scheme && source.protocol !== scheme) return false;
3701
+ if (port && getEffectivePort(source) !== port) return false;
3702
+ if (!port && source.port) return false;
3703
+ return source.hostname.endsWith(`.${baseHost}`) && source.hostname !== baseHost;
3704
+ }
3705
+ function normalizeAllowedOriginPattern(value, label) {
3706
+ const pattern = value.trim().toLowerCase();
3707
+ if (!pattern) {
3708
+ throw new TypeError(`${label} cannot contain empty values`);
3709
+ }
3710
+ if (pattern.includes("*")) {
3711
+ if (!/^(?:https?:\/\/)?\*\.[a-z0-9.-]+(?::\d+)?$/.test(pattern)) {
3712
+ throw new TypeError(`Invalid ${label} pattern: ${JSON.stringify(value)}`);
3713
+ }
3714
+ return pattern;
3715
+ }
3716
+ if (pattern.includes("://")) {
3717
+ let parsed;
3718
+ try {
3719
+ parsed = new URL(pattern);
3720
+ } catch {
3721
+ throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);
3722
+ }
3723
+ if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
3724
+ throw new TypeError(`${label} must contain origins without paths: ${JSON.stringify(value)}`);
3725
+ }
3726
+ return parsed.origin;
3727
+ }
3728
+ if (/[/@?#]/.test(pattern)) {
3729
+ throw new TypeError(`${label} must contain origins or hosts: ${JSON.stringify(value)}`);
3730
+ }
3731
+ try {
3732
+ return new URL(`http://${pattern}`).host;
3733
+ } catch {
3734
+ throw new TypeError(`Invalid ${label} value: ${JSON.stringify(value)}`);
3735
+ }
3736
+ }
3737
+ function splitHostAndPort(value) {
3738
+ const separator = value.lastIndexOf(":");
3739
+ if (separator === -1) return [value, null];
3740
+ return [value.slice(0, separator), value.slice(separator + 1)];
3741
+ }
3742
+ function getEffectivePort(url) {
3743
+ if (url.port) return url.port;
3744
+ if (url.protocol === "https:") return "443";
3745
+ if (url.protocol === "http:") return "80";
3746
+ return "";
3747
+ }
3748
+ var init_request_origin = __esm({
3749
+ "src/request-origin.ts"() {
3750
+ "use strict";
3751
+ __name(getRequestSourceOrigin, "getRequestSourceOrigin");
3752
+ __name(parseSourceOrigin, "parseSourceOrigin");
3753
+ __name(matchesHostHeader, "matchesHostHeader");
3754
+ __name(matchesAllowedOrigin, "matchesAllowedOrigin");
3755
+ __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
3756
+ __name(splitHostAndPort, "splitHostAndPort");
3757
+ __name(getEffectivePort, "getEffectivePort");
3758
+ }
3759
+ });
3760
+
3574
3761
  // src/server-fn-error.ts
3575
3762
  function isServerFnFailure(value) {
3576
3763
  if (!value || typeof value !== "object") return false;
@@ -3614,7 +3801,9 @@ var init_server_fn_error = __esm({
3614
3801
 
3615
3802
  // src/server-action-security.ts
3616
3803
  function resolveServerActionsConfig(config) {
3617
- const allowedOrigins = (config?.allowedOrigins ?? []).map(normalizeAllowedOriginPattern);
3804
+ const allowedOrigins = (config?.allowedOrigins ?? []).map(
3805
+ (value) => normalizeAllowedOriginPattern(value, SERVER_ACTION_ALLOWED_ORIGINS_LABEL)
3806
+ );
3618
3807
  const bodySizeLimit = parseBodySizeLimit(
3619
3808
  config?.bodySizeLimit ?? DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT,
3620
3809
  "serverActions.bodySizeLimit"
@@ -3633,7 +3822,7 @@ function validateServerActionRequest(request, config) {
3633
3822
  );
3634
3823
  }
3635
3824
  const requestUrl = new URL(request.url);
3636
- const sourceOrigin = getRequestSourceOrigin(request);
3825
+ const sourceOrigin = resolveSourceOrigin(request);
3637
3826
  const fetchSite = request.headers.get("sec-fetch-site")?.trim().toLowerCase();
3638
3827
  if (!sourceOrigin) {
3639
3828
  if (fetchSite !== "same-origin") {
@@ -3749,103 +3938,16 @@ function getFallbackAbortController() {
3749
3938
  }
3750
3939
  return globalState2[FALLBACK_ABORT_CONTROLLER_KEY];
3751
3940
  }
3752
- function normalizeAllowedOriginPattern(value) {
3753
- const pattern = value.trim().toLowerCase();
3754
- if (!pattern) {
3755
- throw new TypeError("serverActions.allowedOrigins cannot contain empty values");
3756
- }
3757
- if (pattern.includes("*")) {
3758
- if (!/^(?:https?:\/\/)?\*\.[a-z0-9.-]+(?::\d+)?$/.test(pattern)) {
3759
- throw new TypeError(`Invalid serverActions.allowedOrigins pattern: ${JSON.stringify(value)}`);
3760
- }
3761
- return pattern;
3762
- }
3763
- if (pattern.includes("://")) {
3764
- let parsed;
3765
- try {
3766
- parsed = new URL(pattern);
3767
- } catch {
3768
- throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);
3769
- }
3770
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:" || parsed.username || parsed.password || parsed.pathname !== "/" || parsed.search || parsed.hash) {
3771
- throw new TypeError(
3772
- `serverActions.allowedOrigins must contain origins without paths: ${JSON.stringify(value)}`
3773
- );
3774
- }
3775
- return parsed.origin;
3776
- }
3777
- if (/[/@?#]/.test(pattern)) {
3778
- throw new TypeError(
3779
- `serverActions.allowedOrigins must contain origins or hosts: ${JSON.stringify(value)}`
3941
+ function resolveSourceOrigin(request) {
3942
+ const result = getRequestSourceOrigin(request);
3943
+ if (!result.ok) {
3944
+ throw new ServerActionRequestError(
3945
+ "INVALID_ORIGIN",
3946
+ 403,
3947
+ result.reason === "opaque-origin" ? "Opaque origins are not allowed" : "Invalid request origin"
3780
3948
  );
3781
3949
  }
3782
- try {
3783
- return new URL(`http://${pattern}`).host;
3784
- } catch {
3785
- throw new TypeError(`Invalid serverActions.allowedOrigins value: ${JSON.stringify(value)}`);
3786
- }
3787
- }
3788
- function getRequestSourceOrigin(request) {
3789
- const origin = request.headers.get("origin")?.trim();
3790
- if (origin) {
3791
- return parseSourceOrigin(origin);
3792
- }
3793
- const referer = request.headers.get("referer")?.trim();
3794
- if (referer) {
3795
- return parseSourceOrigin(referer);
3796
- }
3797
- return null;
3798
- }
3799
- function parseSourceOrigin(value) {
3800
- if (value === "null") {
3801
- throw new ServerActionRequestError("INVALID_ORIGIN", 403, "Opaque origins are not allowed");
3802
- }
3803
- try {
3804
- const parsed = new URL(value);
3805
- if (parsed.protocol !== "http:" && parsed.protocol !== "https:") {
3806
- throw new Error("Unsupported protocol");
3807
- }
3808
- return parsed.origin;
3809
- } catch {
3810
- throw new ServerActionRequestError("INVALID_ORIGIN", 403, "Invalid request origin");
3811
- }
3812
- }
3813
- function matchesHostHeader(sourceOrigin, request) {
3814
- const host = request.headers.get("host")?.trim().toLowerCase();
3815
- if (!host) return false;
3816
- try {
3817
- const source = new URL(sourceOrigin);
3818
- const target = new URL(request.url);
3819
- return source.protocol === target.protocol && source.host.toLowerCase() === host;
3820
- } catch {
3821
- return false;
3822
- }
3823
- }
3824
- function matchesAllowedOrigin(sourceOrigin, pattern) {
3825
- const source = new URL(sourceOrigin);
3826
- if (!pattern.includes("*")) {
3827
- return pattern.includes("://") ? source.origin === pattern : source.host === pattern;
3828
- }
3829
- const schemeEnd = pattern.indexOf("://");
3830
- const scheme = schemeEnd === -1 ? null : pattern.slice(0, schemeEnd + 1);
3831
- const hostPattern = pattern.slice(schemeEnd === -1 ? 0 : schemeEnd + 3);
3832
- const [wildcardHost, port] = splitHostAndPort(hostPattern);
3833
- const baseHost = wildcardHost.slice(2);
3834
- if (scheme && source.protocol !== scheme) return false;
3835
- if (port && getEffectivePort(source) !== port) return false;
3836
- if (!port && source.port) return false;
3837
- return source.hostname.endsWith(`.${baseHost}`) && source.hostname !== baseHost;
3838
- }
3839
- function splitHostAndPort(value) {
3840
- const separator = value.lastIndexOf(":");
3841
- if (separator === -1) return [value, null];
3842
- return [value.slice(0, separator), value.slice(separator + 1)];
3843
- }
3844
- function getEffectivePort(url) {
3845
- if (url.port) return url.port;
3846
- if (url.protocol === "https:") return "443";
3847
- if (url.protocol === "http:") return "80";
3848
- return "";
3950
+ return result.origin;
3849
3951
  }
3850
3952
  function validateActionId(actionId) {
3851
3953
  if (!actionId || actionId.length > 4096 || hasControlCharacters(actionId)) {
@@ -3966,14 +4068,16 @@ function throwIfAborted2(signal) {
3966
4068
  if (signal.reason !== void 0) throw signal.reason;
3967
4069
  throw new DOMException("The operation was aborted", "AbortError");
3968
4070
  }
3969
- 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;
4071
+ 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;
3970
4072
  var init_server_action_security = __esm({
3971
4073
  "src/server-action-security.ts"() {
3972
4074
  "use strict";
3973
4075
  import_node_async_hooks = require("async_hooks");
3974
4076
  init_cache_invalidation();
4077
+ init_request_origin();
3975
4078
  init_server_fn_error();
3976
4079
  init_server_http();
4080
+ SERVER_ACTION_ALLOWED_ORIGINS_LABEL = "serverActions.allowedOrigins";
3977
4081
  DEFAULT_SERVER_ACTION_BODY_SIZE_LIMIT = 1e6;
3978
4082
  _ServerActionRequestError = class _ServerActionRequestError extends Error {
3979
4083
  constructor(code, status, message) {
@@ -4014,13 +4118,7 @@ var init_server_action_security = __esm({
4014
4118
  });
4015
4119
  __name(getServerActionStorage, "getServerActionStorage");
4016
4120
  __name(getFallbackAbortController, "getFallbackAbortController");
4017
- __name(normalizeAllowedOriginPattern, "normalizeAllowedOriginPattern");
4018
- __name(getRequestSourceOrigin, "getRequestSourceOrigin");
4019
- __name(parseSourceOrigin, "parseSourceOrigin");
4020
- __name(matchesHostHeader, "matchesHostHeader");
4021
- __name(matchesAllowedOrigin, "matchesAllowedOrigin");
4022
- __name(splitHostAndPort, "splitHostAndPort");
4023
- __name(getEffectivePort, "getEffectivePort");
4121
+ __name(resolveSourceOrigin, "resolveSourceOrigin");
4024
4122
  __name(validateActionId, "validateActionId");
4025
4123
  __name(hasControlCharacters, "hasControlCharacters");
4026
4124
  __name(getSupportedContentType, "getSupportedContentType");
@@ -6398,6 +6496,14 @@ var init_routes = __esm({
6398
6496
  }
6399
6497
  });
6400
6498
 
6499
+ // src/integration-request-security.ts
6500
+ var init_integration_request_security = __esm({
6501
+ "src/integration-request-security.ts"() {
6502
+ "use strict";
6503
+ init_request_origin();
6504
+ }
6505
+ });
6506
+
6401
6507
  // src/integration-api.ts
6402
6508
  function defineIntegrationAPIOperation(operation) {
6403
6509
  return {
@@ -8573,6 +8679,7 @@ var init_integrations = __esm({
8573
8679
  "src/integrations.ts"() {
8574
8680
  "use strict";
8575
8681
  init_route_pattern();
8682
+ init_integration_request_security();
8576
8683
  init_integration_api();
8577
8684
  init_plugin_integration_context();
8578
8685
  init_decode();
@@ -8779,6 +8886,51 @@ var init_renderer = __esm({
8779
8886
  }
8780
8887
  });
8781
8888
 
8889
+ // src/agent-config.ts
8890
+ function resolveFarmAgentConfig(input) {
8891
+ const jsonLd = input?.jsonLd;
8892
+ if (!jsonLd) return { jsonLd: false };
8893
+ return { jsonLd: jsonLd === true ? {} : jsonLd };
8894
+ }
8895
+ function pruneUndefined(object) {
8896
+ const result = {};
8897
+ for (const [key, value] of Object.entries(object)) {
8898
+ if (value !== void 0 && value !== null && value !== "") result[key] = value;
8899
+ }
8900
+ return result;
8901
+ }
8902
+ function serializeJsonLd(value) {
8903
+ return JSON.stringify(value).replace(/</g, "\\u003c");
8904
+ }
8905
+ function renderFarmAgentJsonLd(config, context2) {
8906
+ const base = pruneUndefined({
8907
+ name: config.name ?? context2.siteName ?? context2.title,
8908
+ url: config.url ?? context2.metadataBase,
8909
+ description: config.description ?? context2.description,
8910
+ logo: config.logo,
8911
+ sameAs: config.sameAs && config.sameAs.length > 0 ? config.sameAs : void 0
8912
+ });
8913
+ const object = {
8914
+ "@context": "https://schema.org",
8915
+ "@type": config.type || "Organization",
8916
+ ...base,
8917
+ ...config.properties || {}
8918
+ };
8919
+ if (Object.keys(object).length <= 2 && !config.properties) {
8920
+ return "";
8921
+ }
8922
+ return `<script type="application/ld+json">${serializeJsonLd(object)}</script>`;
8923
+ }
8924
+ var init_agent_config = __esm({
8925
+ "src/agent-config.ts"() {
8926
+ "use strict";
8927
+ __name(resolveFarmAgentConfig, "resolveFarmAgentConfig");
8928
+ __name(pruneUndefined, "pruneUndefined");
8929
+ __name(serializeJsonLd, "serializeJsonLd");
8930
+ __name(renderFarmAgentJsonLd, "renderFarmAgentJsonLd");
8931
+ }
8932
+ });
8933
+
8782
8934
  // src/deployment.ts
8783
8935
  function normalizeFarmDeploymentId(value) {
8784
8936
  if (typeof value !== "string" || value.trim() === "") {
@@ -10310,6 +10462,7 @@ export const apiRoutes = ${manifestSource} as const;
10310
10462
  function createFarmImageHandler(config, options) {
10311
10463
  const fetcher = options.fetch ?? globalThis.fetch;
10312
10464
  const cache = new FarmImageMemoryCache(options.cacheEntries ?? 100);
10465
+ const inflight = /* @__PURE__ */ new Map();
10313
10466
  const allowedWidths = /* @__PURE__ */ new Set([...config.deviceSizes, ...config.imageSizes]);
10314
10467
  const allowedQualities = new Set(config.qualities);
10315
10468
  return /* @__PURE__ */ __name(async function handleFarmImage(request) {
@@ -10338,50 +10491,55 @@ function createFarmImageHandler(config, options) {
10338
10491
  "quality"
10339
10492
  );
10340
10493
  const accept = request.headers.get("accept") ?? "";
10494
+ const negotiatedFormat = selectOutputFormat(accept, config.formats) ?? "";
10341
10495
  const cacheKey = `${sourceUrl.href}
10342
10496
  ${width}
10343
10497
  ${quality}
10344
- ${accept}`;
10498
+ ${negotiatedFormat}`;
10345
10499
  let optimized = cache.get(cacheKey);
10346
10500
  if (!optimized) {
10347
- const fetchedSource = await fetchImageSource(
10348
- sourceUrl,
10349
- requestUrl.origin,
10350
- config,
10351
- fetcher,
10352
- options.fetchRemote,
10353
- options.validateRemoteUrl,
10354
- request.signal
10355
- );
10356
- const source = await readResponseWithLimit(
10357
- fetchedSource.response,
10358
- config.maximumResponseBody
10359
- );
10360
- const sourceType = detectImageContentType(source);
10361
- validateSourceType(sourceType, config);
10362
- throwIfAborted3(request.signal);
10363
- const result = await options.transform({
10364
- source,
10365
- sourceUrl: fetchedSource.url,
10366
- sourceType,
10367
- width,
10368
- quality,
10369
- accept,
10370
- formats: config.formats,
10371
- signal: request.signal
10501
+ optimized = await runCoalesced(inflight, cacheKey, request.signal, async (signal) => {
10502
+ const fetchedSource = await fetchImageSource(
10503
+ sourceUrl,
10504
+ requestUrl.origin,
10505
+ config,
10506
+ fetcher,
10507
+ options.fetchRemote,
10508
+ options.validateRemoteUrl,
10509
+ signal
10510
+ );
10511
+ const source = await readResponseWithLimit(
10512
+ fetchedSource.response,
10513
+ config.maximumResponseBody
10514
+ );
10515
+ const sourceType = detectImageContentType(source);
10516
+ validateSourceType(sourceType, config);
10517
+ throwIfAborted3(signal);
10518
+ const result = await options.transform({
10519
+ source,
10520
+ sourceUrl: fetchedSource.url,
10521
+ sourceType,
10522
+ width,
10523
+ quality,
10524
+ accept,
10525
+ formats: config.formats,
10526
+ signal,
10527
+ maximumResponseBody: config.maximumResponseBody
10528
+ });
10529
+ throwIfAborted3(signal);
10530
+ validateTransformedResult(result, config);
10531
+ const entry = {
10532
+ ...result,
10533
+ etag: createImageEtag(result.body),
10534
+ cacheControl: `public, max-age=${config.minimumCacheTTL}, stale-while-revalidate=${Math.max(
10535
+ config.minimumCacheTTL,
10536
+ 60
10537
+ )}`,
10538
+ expiresAt: Date.now() + config.minimumCacheTTL * 1e3
10539
+ };
10540
+ cache.set(cacheKey, entry);
10541
+ return entry;
10372
10542
  });
10373
- throwIfAborted3(request.signal);
10374
- validateTransformedResult(result, config);
10375
- optimized = {
10376
- ...result,
10377
- etag: createImageEtag(result.body),
10378
- cacheControl: `public, max-age=${config.minimumCacheTTL}, stale-while-revalidate=${Math.max(
10379
- config.minimumCacheTTL,
10380
- 60
10381
- )}`,
10382
- expiresAt: Date.now() + config.minimumCacheTTL * 1e3
10383
- };
10384
- cache.set(cacheKey, optimized);
10385
10543
  }
10386
10544
  return createOptimizedImageResponse(request, optimized, config);
10387
10545
  } catch (error) {
@@ -10532,6 +10690,44 @@ async function fetchImageSource(initialUrl, requestOrigin, config, fetcher, fetc
10532
10690
  await validateImageSourceUrl(currentUrl, requestOrigin, config, validateRemoteUrl);
10533
10691
  }
10534
10692
  }
10693
+ async function runCoalesced(inflight, key, requestSignal, run) {
10694
+ let entry = inflight.get(key);
10695
+ if (!entry) {
10696
+ const controller = new AbortController();
10697
+ const created = {
10698
+ controller,
10699
+ waiters: 0,
10700
+ promise: void 0
10701
+ };
10702
+ created.promise = run(controller.signal).finally(() => {
10703
+ if (inflight.get(key) === created) inflight.delete(key);
10704
+ });
10705
+ created.promise.catch(() => {
10706
+ });
10707
+ inflight.set(key, created);
10708
+ entry = created;
10709
+ }
10710
+ const pending = entry;
10711
+ pending.waiters += 1;
10712
+ try {
10713
+ return await raceRequestAbort(pending.promise, requestSignal);
10714
+ } finally {
10715
+ pending.waiters -= 1;
10716
+ if (pending.waiters === 0 && inflight.get(key) === pending) {
10717
+ inflight.delete(key);
10718
+ pending.controller.abort();
10719
+ }
10720
+ }
10721
+ }
10722
+ function raceRequestAbort(promise, signal) {
10723
+ if (!signal) return promise;
10724
+ if (signal.aborted) return Promise.reject(signal.reason ?? new Error("Aborted"));
10725
+ return new Promise((resolve10, reject) => {
10726
+ const onAbort = /* @__PURE__ */ __name(() => reject(signal.reason ?? new Error("Aborted")), "onAbort");
10727
+ signal.addEventListener("abort", onAbort, { once: true });
10728
+ promise.then(resolve10, reject).finally(() => signal.removeEventListener("abort", onAbort));
10729
+ });
10730
+ }
10535
10731
  async function readResponseWithLimit(response, limit) {
10536
10732
  const contentLength = response.headers.get("content-length");
10537
10733
  if (contentLength && Number(contentLength) > limit) {
@@ -10699,7 +10895,7 @@ function throwIfAborted3(signal) {
10699
10895
  function isAbortError(error) {
10700
10896
  return error instanceof Error && error.name === "AbortError";
10701
10897
  }
10702
- var _FarmImageRequestError, FarmImageRequestError, _FarmImageMemoryCache, FarmImageMemoryCache;
10898
+ var _FarmImageRequestError, FarmImageRequestError, DEFAULT_IMAGE_TRANSFORM_BODY_LIMIT, _FarmImageMemoryCache, FarmImageMemoryCache;
10703
10899
  var init_image_server = __esm({
10704
10900
  "src/image-server.ts"() {
10705
10901
  "use strict";
@@ -10715,12 +10911,15 @@ var init_image_server = __esm({
10715
10911
  __name(_FarmImageRequestError, "FarmImageRequestError");
10716
10912
  FarmImageRequestError = _FarmImageRequestError;
10717
10913
  __name(createFarmImageHandler, "createFarmImageHandler");
10914
+ DEFAULT_IMAGE_TRANSFORM_BODY_LIMIT = 10 * 1024 * 1024;
10718
10915
  __name(selectOutputFormat, "selectOutputFormat");
10719
10916
  __name(isPrivateImageAddress, "isPrivateImageAddress");
10720
10917
  __name(parseAllowedInteger, "parseAllowedInteger");
10721
10918
  __name(resolveImageSourceUrl, "resolveImageSourceUrl");
10722
10919
  __name(validateImageSourceUrl, "validateImageSourceUrl");
10723
10920
  __name(fetchImageSource, "fetchImageSource");
10921
+ __name(runCoalesced, "runCoalesced");
10922
+ __name(raceRequestAbort, "raceRequestAbort");
10724
10923
  __name(readResponseWithLimit, "readResponseWithLimit");
10725
10924
  __name(cancelResponseBody, "cancelResponseBody");
10726
10925
  __name(detectImageContentType, "detectImageContentType");
@@ -13571,6 +13770,7 @@ async function resolveConfig(userConfig, mode) {
13571
13770
  const openapi = {
13572
13771
  enabled: false,
13573
13772
  route: "/docs/reference",
13773
+ specRoute: "/openapi.json",
13574
13774
  title: "API Documentation",
13575
13775
  description: "Auto-generated API documentation",
13576
13776
  version: "1.0.0",
@@ -13578,6 +13778,7 @@ async function resolveConfig(userConfig, mode) {
13578
13778
  ...userConfig.openapi
13579
13779
  };
13580
13780
  if (openapi.route !== void 0) validateConfigRouteSource(openapi.route, "openapi.route");
13781
+ if (openapi.specRoute) validateConfigRouteSource(openapi.specRoute, "openapi.specRoute");
13581
13782
  const resolved = {
13582
13783
  [FARM_RESOLVED_CUSTOM_CONTEXT]: typeof userConfig.context === "function",
13583
13784
  root,
@@ -13610,6 +13811,7 @@ async function resolveConfig(userConfig, mode) {
13610
13811
  ppr: false,
13611
13812
  ...userConfig.experimental
13612
13813
  },
13814
+ agent: resolveFarmAgentConfig(userConfig.agent),
13613
13815
  plugins: [...resolveIntegrationPlugins(integrations), ...userConfig.plugins || []],
13614
13816
  integrations,
13615
13817
  trailingSlash: userConfig.trailingSlash ?? false,
@@ -13718,6 +13920,7 @@ var init_config4 = __esm({
13718
13920
  init_security();
13719
13921
  init_navigation_errors();
13720
13922
  init_config2();
13923
+ init_agent_config();
13721
13924
  init_renderer();
13722
13925
  init_framework_detect();
13723
13926
  init_config3();
@@ -17865,6 +18068,21 @@ function isFarmMarkdownPageFile(filePath) {
17865
18068
  return /(^|[/\\])page\.mdx?$/i.test(filePath);
17866
18069
  }
17867
18070
  __name(isFarmMarkdownPageFile, "isFarmMarkdownPageFile");
18071
+ var FARM_MARKDOWN_CONTENT_TYPE = "text/markdown; charset=utf-8";
18072
+ function farmRequestWantsMarkdown(pathname, accept) {
18073
+ return pathname.toLowerCase().endsWith(".md") || requestAcceptsMarkdown(accept);
18074
+ }
18075
+ __name(farmRequestWantsMarkdown, "farmRequestWantsMarkdown");
18076
+ function createFarmMarkdownErrorBody(status, pathname, homeHref = "/") {
18077
+ const heading = status === 404 ? "Page not found" : `Request failed (${status})`;
18078
+ return `# ${heading}
18079
+
18080
+ No page is available at \`${pathname}\`. The URL may be incorrect, or the page has not been published.
18081
+
18082
+ Browse [the site homepage](${homeHref}) to find available pages.
18083
+ `;
18084
+ }
18085
+ __name(createFarmMarkdownErrorBody, "createFarmMarkdownErrorBody");
17868
18086
  function normalizeFarmMarkdownRoutePath(pathname) {
17869
18087
  const withoutExtension = pathname.replace(/\.md$/i, "");
17870
18088
  const normalized = withoutExtension.startsWith("/") ? withoutExtension : `/${withoutExtension}`;
@@ -18585,6 +18803,50 @@ function getStaticImportBindings(content) {
18585
18803
  return bindings;
18586
18804
  }
18587
18805
  __name(getStaticImportBindings, "getStaticImportBindings");
18806
+ var PARSER_SENSITIVE_JSX_CONTAINERS = /* @__PURE__ */ new Set([
18807
+ "table",
18808
+ "thead",
18809
+ "tbody",
18810
+ "tfoot",
18811
+ "tr",
18812
+ "select",
18813
+ "optgroup",
18814
+ "svg"
18815
+ ]);
18816
+ function findParserSensitiveJsxContainer(content, localBindings) {
18817
+ if (!content || localBindings.length === 0) return void 0;
18818
+ const names = new Set(localBindings);
18819
+ const tokens = tokenizeModuleSource(content);
18820
+ const openDepth = /* @__PURE__ */ new Map();
18821
+ const isSelfClosing = /* @__PURE__ */ __name((openingIndex) => {
18822
+ for (let index = openingIndex; index < tokens.length; index++) {
18823
+ if (tokens[index].value === ">") return tokens[index - 1]?.value === "/";
18824
+ }
18825
+ return false;
18826
+ }, "isSelfClosing");
18827
+ for (let index = 0; index < tokens.length - 1; index++) {
18828
+ if (tokens[index].value !== "<") continue;
18829
+ if (tokens[index + 1].value === "/") {
18830
+ const closing = tokens[index + 2]?.value;
18831
+ if (closing && openDepth.has(closing)) {
18832
+ openDepth.set(closing, Math.max(0, (openDepth.get(closing) ?? 0) - 1));
18833
+ }
18834
+ continue;
18835
+ }
18836
+ const tag = tokens[index + 1].value;
18837
+ if (names.has(tag)) {
18838
+ for (const [container, depth] of openDepth) {
18839
+ if (depth > 0) return container;
18840
+ }
18841
+ continue;
18842
+ }
18843
+ if (PARSER_SENSITIVE_JSX_CONTAINERS.has(tag) && !isSelfClosing(index)) {
18844
+ openDepth.set(tag, (openDepth.get(tag) ?? 0) + 1);
18845
+ }
18846
+ }
18847
+ return void 0;
18848
+ }
18849
+ __name(findParserSensitiveJsxContainer, "findParserSensitiveJsxContainer");
18588
18850
  function countStaticJsxUses(content, localBindings) {
18589
18851
  if (!content || localBindings.length === 0) return 0;
18590
18852
  const names = new Set(localBindings);
@@ -18698,6 +18960,37 @@ function getClientRenderingHelpers(tokens, clientBindings) {
18698
18960
  return helpers;
18699
18961
  }
18700
18962
  __name(getClientRenderingHelpers, "getClientRenderingHelpers");
18963
+ function passesElementValuedProps(content, localBindings) {
18964
+ if (!content || localBindings.length === 0) return false;
18965
+ const names = new Set(localBindings);
18966
+ const tokens = tokenizeModuleSource(content);
18967
+ for (let index = 0; index < tokens.length - 1; index++) {
18968
+ if (tokens[index].value !== "<" || !names.has(tokens[index + 1].value)) continue;
18969
+ const name2 = tokens[index + 1].value;
18970
+ let cursor = index + 2;
18971
+ let braceDepth = 0;
18972
+ let selfClosing = false;
18973
+ for (; cursor < tokens.length; cursor++) {
18974
+ const value = tokens[cursor].value;
18975
+ if (value === "{") braceDepth++;
18976
+ else if (value === "}") braceDepth--;
18977
+ else if (value === "<" && braceDepth > 0) return true;
18978
+ else if (value === ">" && braceDepth === 0) {
18979
+ selfClosing = tokens[cursor - 1]?.value === "/";
18980
+ break;
18981
+ }
18982
+ }
18983
+ if (selfClosing || cursor >= tokens.length) continue;
18984
+ for (let child = cursor + 1; child < tokens.length; child++) {
18985
+ if (tokens[child].value !== "<") continue;
18986
+ const closesBoundary = tokens[child + 1]?.value === "/" && tokens[child + 2]?.value === name2;
18987
+ if (!closesBoundary) return true;
18988
+ break;
18989
+ }
18990
+ }
18991
+ return false;
18992
+ }
18993
+ __name(passesElementValuedProps, "passesElementValuedProps");
18701
18994
  function hasDynamicJsxCardinality(content, localBindings) {
18702
18995
  if (!content || localBindings.length === 0) return false;
18703
18996
  const clientBindings = new Set(localBindings);
@@ -18780,6 +19073,17 @@ function collectIsolatedClientBoundaries(ownerPath, root) {
18780
19073
  if (fallbackReason || importedBoundaryCount === 0) continue;
18781
19074
  const localBindings = importedBindings.get(specifier) ?? [];
18782
19075
  const staticUses = countStaticJsxUses(content, localBindings);
19076
+ if (staticUses > 0) {
19077
+ const container = findParserSensitiveJsxContainer(content, localBindings);
19078
+ if (container) {
19079
+ fallbackReason = `the client boundary imported from ${specifier} renders inside <${container}>, where the HTML parser relocates its hydration marker`;
19080
+ return 0;
19081
+ }
19082
+ if (passesElementValuedProps(content, localBindings)) {
19083
+ fallbackReason = `the client boundary imported from ${specifier} receives React elements, which cannot cross the boundary as serialized props`;
19084
+ return 0;
19085
+ }
19086
+ }
18783
19087
  if (staticUses > 0 && hasDynamicJsxCardinality(content, localBindings)) {
18784
19088
  fallbackReason = `the client boundary count imported from ${specifier} is data-dependent`;
18785
19089
  costGuardExceeded = true;
@@ -20624,6 +20928,7 @@ init_observability();
20624
20928
  init_navigation_errors();
20625
20929
 
20626
20930
  // src/metadata.ts
20931
+ init_agent_config();
20627
20932
  function mergeMetadata(base, next) {
20628
20933
  if (!base) return next ? { ...next } : {};
20629
20934
  if (!next) return { ...base };
@@ -20686,7 +20991,7 @@ function addMetadataImageReference(metadata, reference) {
20686
20991
  };
20687
20992
  }
20688
20993
  __name(addMetadataImageReference, "addMetadataImageReference");
20689
- function renderMetadataHead(metadata) {
20994
+ function renderMetadataHead(metadata, options = {}) {
20690
20995
  const resolvedMetadata = metadata || {};
20691
20996
  const metadataBase = resolveMetadataBase(resolvedMetadata);
20692
20997
  const explicitTitle = resolveMetadataTitle(resolvedMetadata.title);
@@ -20707,14 +21012,16 @@ function renderMetadataHead(metadata) {
20707
21012
  appendMetaName(tags, "publisher", resolvedMetadata.publisher);
20708
21013
  appendMetaName(tags, "robots", normalizeRobots(resolvedMetadata.robots));
20709
21014
  const alternates = resolvedMetadata.alternates;
20710
- if (isRecord(alternates)) {
20711
- appendLink(tags, "canonical", resolveMetadataUrl(alternates.canonical, metadataBase));
20712
- if (isRecord(alternates.languages)) {
20713
- for (const [language, href] of Object.entries(alternates.languages)) {
20714
- appendLink(tags, "alternate", resolveMetadataUrl(href, metadataBase), {
20715
- hreflang: language
20716
- });
20717
- }
21015
+ const explicitCanonical = isRecord(alternates) ? alternates.canonical : void 0;
21016
+ const canonicalHref = explicitCanonical != null ? resolveMetadataUrl(explicitCanonical, metadataBase) : options.pathname ? resolveMetadataUrl(options.pathname, metadataBase) : void 0;
21017
+ if (canonicalHref) {
21018
+ appendLink(tags, "canonical", canonicalHref);
21019
+ }
21020
+ if (isRecord(alternates) && isRecord(alternates.languages)) {
21021
+ for (const [language, href] of Object.entries(alternates.languages)) {
21022
+ appendLink(tags, "alternate", resolveMetadataUrl(href, metadataBase), {
21023
+ hreflang: language
21024
+ });
20718
21025
  }
20719
21026
  }
20720
21027
  const hasFavicon = appendIcons(tags, resolvedMetadata.icons, metadataBase);
@@ -20727,6 +21034,16 @@ function renderMetadataHead(metadata) {
20727
21034
  }
20728
21035
  appendOpenGraph(tags, resolvedMetadata.openGraph, metadataBase);
20729
21036
  appendTwitter(tags, resolvedMetadata.twitter, metadataBase);
21037
+ if (options.jsonLd) {
21038
+ const jsonLdConfig = options.jsonLd === true ? {} : options.jsonLd;
21039
+ const jsonLdScript = renderFarmAgentJsonLd(jsonLdConfig, {
21040
+ metadataBase,
21041
+ siteName: isRecord(resolvedMetadata.openGraph) ? normalizeContent(resolvedMetadata.openGraph.siteName) : void 0,
21042
+ title: explicitTitle,
21043
+ description: normalizeContent(resolvedMetadata.description)
21044
+ });
21045
+ if (jsonLdScript) tags.push(jsonLdScript);
21046
+ }
20730
21047
  return {
20731
21048
  title: escapeText(title),
20732
21049
  tags: tags.length > 0 ? `
@@ -20752,7 +21069,7 @@ function appendOpenGraph(tags, openGraph, metadataBase) {
20752
21069
  appendMetaProperty(tags, "og:description", openGraph.description);
20753
21070
  appendMetaProperty(tags, "og:url", resolveMetadataUrl(openGraph.url, metadataBase));
20754
21071
  appendMetaProperty(tags, "og:site_name", openGraph.siteName);
20755
- appendMetaProperty(tags, "og:type", openGraph.type);
21072
+ appendMetaProperty(tags, "og:type", openGraph.type ?? "website");
20756
21073
  appendMetaProperty(tags, "og:locale", openGraph.locale);
20757
21074
  const images = normalizeMetadataImages(
20758
21075
  openGraph.images ?? openGraph.image,
@@ -23907,7 +24224,7 @@ _setFarmThemeServerSnapshotResolver(() => {
23907
24224
  });
23908
24225
 
23909
24226
  // package.json
23910
- var version = "0.1.0-beta.97";
24227
+ var version = "0.1.0-beta.99";
23911
24228
 
23912
24229
  // src/version.ts
23913
24230
  var FARM_VERSION = version;
@@ -24077,6 +24394,13 @@ function hasRequestHeader(req, name2) {
24077
24394
  return Array.isArray(value) ? value.length > 0 : Boolean(value);
24078
24395
  }
24079
24396
  __name(hasRequestHeader, "hasRequestHeader");
24397
+ function getFarmMetadataPathname(req) {
24398
+ const raw = req.__FARM_ROUTE__ || req.url;
24399
+ if (typeof raw !== "string" || raw.length === 0) return void 0;
24400
+ const boundary = raw.search(/[?#]/);
24401
+ return boundary === -1 ? raw : raw.slice(0, boundary);
24402
+ }
24403
+ __name(getFarmMetadataPathname, "getFarmMetadataPathname");
24080
24404
  function serializeInlineValue2(value) {
24081
24405
  return JSON.stringify(value).replace(/</g, "\\u003c").replace(/\u2028/g, "\\u2028").replace(/\u2029/g, "\\u2029");
24082
24406
  }
@@ -24610,7 +24934,21 @@ var _ServerRenderer = class _ServerRenderer {
24610
24934
  const html = await this.rendererRuntime.renderToString(
24611
24935
  await this.wrapWithIntegrationProviders(pageElement)
24612
24936
  );
24613
- await this.cacheSSGPage(page2, html, { document: false });
24937
+ const mergedMetadata = await this.resolveRouteMetadata({
24938
+ layoutModules,
24939
+ routeModule: mod,
24940
+ pageProps,
24941
+ pathname: page2.urlPath
24942
+ });
24943
+ const { title, tags, hasFavicon } = renderMetadataHead(mergedMetadata);
24944
+ const metadataHead = `${hasFavicon ? "" : '<link rel="icon" href="data:,">\n '}<title>${title}</title>${tags}`;
24945
+ const fullDocument = this.createFullHTML(
24946
+ html,
24947
+ pageMetadata.shouldHydrate === true,
24948
+ page2.urlPath,
24949
+ metadataHead
24950
+ );
24951
+ await this.cacheSSGPage(page2, fullDocument, { document: true });
24614
24952
  logger.info(`ISR: Regenerated ${page2.urlPath}`);
24615
24953
  } catch (error) {
24616
24954
  logger.error(`ISR regeneration failed for ${page2.urlPath}: ${error}`);
@@ -25650,7 +25988,10 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
25650
25988
  tags: metaTags,
25651
25989
  hasFavicon,
25652
25990
  hasExplicitTitle
25653
- } = renderMetadataHead(req.__FARM_METADATA__);
25991
+ } = renderMetadataHead(req.__FARM_METADATA__, {
25992
+ pathname: getFarmMetadataPathname(req),
25993
+ jsonLd: this.config.agent?.jsonLd
25994
+ });
25654
25995
  const documentTitleTag = !hasExplicitTitle && /<title[\s>]/i.test(rendererHead) ? "" : `<title>${title}</title>`;
25655
25996
  const i18nSnapshot = getFarmI18nClientSnapshot();
25656
25997
  const alternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
@@ -25835,7 +26176,10 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
25835
26176
  title,
25836
26177
  tags: metaTags,
25837
26178
  hasFavicon
25838
- } = renderMetadataHead(req.__FARM_METADATA__);
26179
+ } = renderMetadataHead(req.__FARM_METADATA__, {
26180
+ pathname: getFarmMetadataPathname(req),
26181
+ jsonLd: this.config.agent?.jsonLd
26182
+ });
25839
26183
  const i18nSnapshot = getFarmI18nClientSnapshot();
25840
26184
  const i18nAlternateTags = i18nSnapshot ? renderI18nAlternateLinks(req.__FARM_ROUTE__ || req.url || "/", i18nSnapshot) : "";
25841
26185
  const fontHead = renderFarmFontDevHead(this.config.root || process.cwd());
@@ -25993,6 +26337,15 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
25993
26337
  const pathname = resolveFarmRequestURL(req, {
25994
26338
  trustProxy: this.config.server?.trustProxy
25995
26339
  }).pathname;
26340
+ const acceptHeader = req.headers.accept;
26341
+ const accept = Array.isArray(acceptHeader) ? acceptHeader.join(",") : acceptHeader;
26342
+ if (farmRequestWantsMarkdown(pathname, accept)) {
26343
+ res.setHeader("Content-Type", FARM_MARKDOWN_CONTENT_TYPE);
26344
+ res.setHeader("X-Farm-Markdown-Error", "404");
26345
+ res.setHeader("Cache-Control", "no-store");
26346
+ res.end(createFarmMarkdownErrorBody(404, pathname, this.config.basePath || "/"));
26347
+ return;
26348
+ }
25996
26349
  try {
25997
26350
  const appDir = path12.join(this.config.root, this.config.srcDir, "app");
25998
26351
  const notFoundExtensions = getFarmRendererComponentExtensions(this.config.renderer);
@@ -26078,7 +26431,7 @@ ${getFarmI18nClientSnapshot() ? `window.__FARM_I18N__ = ${serializeInlineValue2(
26078
26431
  res.write(html);
26079
26432
  res.end();
26080
26433
  }
26081
- createFullHTML(content, isClientComponent = false, requestPath = "/", documentTitle = "Farm.js App") {
26434
+ createFullHTML(content, isClientComponent = false, requestPath = "/", metadataHead) {
26082
26435
  const i18nSnapshot = getFarmI18nClientSnapshot();
26083
26436
  const clientScript = isClientComponent ? ` <script type="module" src="/@farm/client.js"></script>` : "";
26084
26437
  const integrationManifestScript = `<script>
@@ -26102,6 +26455,7 @@ ${i18nSnapshot ? `window.__FARM_I18N__ = ${serializeInlineValue2(i18nSnapshot)};
26102
26455
  headAssets: [
26103
26456
  themeDocument.head,
26104
26457
  `<meta name="farm-deployment-id" content="${escapeHtmlAttribute2(this.getDeploymentId())}">`,
26458
+ metadataHead,
26105
26459
  alternateLinks,
26106
26460
  fontHead,
26107
26461
  `<link rel="stylesheet" href="/src/app/globals.css" />`,
@@ -26120,8 +26474,7 @@ ${i18nSnapshot ? `window.__FARM_I18N__ = ${serializeInlineValue2(i18nSnapshot)};
26120
26474
  <meta charset="utf-8">
26121
26475
  <meta name="viewport" content="width=device-width, initial-scale=1">
26122
26476
  <meta name="farm-deployment-id" content="${escapeHtmlAttribute2(this.getDeploymentId())}">
26123
- <link rel="icon" href="data:,">
26124
- <title>${escapeHtmlAttribute2(documentTitle)}</title>${alternateLinks}
26477
+ ${metadataHead ?? '<link rel="icon" href="data:,">\n <title>Farm.js App</title>'}${alternateLinks}
26125
26478
  ${fontHead}
26126
26479
  <link rel="stylesheet" href="/src/app/globals.css" />${this.collectDevStyleLinks().map((l) => `
26127
26480
  ${l}`).join("")}
@@ -26286,6 +26639,7 @@ var _FarmApp = class _FarmApp {
26286
26639
  ppr: config.experimental?.ppr ?? false,
26287
26640
  ...config.experimental
26288
26641
  },
26642
+ agent: config.agent ?? {},
26289
26643
  vite: config.vite || {}
26290
26644
  };
26291
26645
  }
@@ -30645,6 +30999,110 @@ function formatServerFnBoundaryError(violation, id) {
30645
30999
  }
30646
31000
  __name(formatServerFnBoundaryError, "formatServerFnBoundaryError");
30647
31001
 
31002
+ // src/client-boundary-env.ts
31003
+ var FUNCTION_BODY_TYPES = /* @__PURE__ */ new Set([
31004
+ "FunctionDeclaration",
31005
+ "FunctionExpression",
31006
+ "ArrowFunctionExpression"
31007
+ ]);
31008
+ function shouldInspectClientBoundary(id, code) {
31009
+ if (id.startsWith("\0") || id.startsWith("virtual:") || id.includes("node_modules")) return false;
31010
+ return code.includes("process.env") || code.includes("node:");
31011
+ }
31012
+ __name(shouldInspectClientBoundary, "shouldInspectClientBoundary");
31013
+ function analyzeClientBoundary(program, publicKeys) {
31014
+ const envKeys = /* @__PURE__ */ new Set();
31015
+ const publicEnvKeys = /* @__PURE__ */ new Set();
31016
+ const builtinImports = /* @__PURE__ */ new Set();
31017
+ const visit = /* @__PURE__ */ __name((node, inFunction) => {
31018
+ if (!node || typeof node !== "object") return;
31019
+ if (Array.isArray(node)) {
31020
+ for (const entry of node) visit(entry, inFunction);
31021
+ return;
31022
+ }
31023
+ const estree = node;
31024
+ if (typeof estree.type !== "string") return;
31025
+ if (!inFunction) {
31026
+ const builtin = readNodeBuiltinSource(estree);
31027
+ if (builtin !== void 0) builtinImports.add(builtin);
31028
+ const key = readProcessEnvKey(estree);
31029
+ if (key !== void 0) {
31030
+ if (key !== "NODE_ENV") {
31031
+ (publicKeys.has(key) ? publicEnvKeys : envKeys).add(key);
31032
+ }
31033
+ return;
31034
+ }
31035
+ }
31036
+ const nextInFunction = inFunction || FUNCTION_BODY_TYPES.has(estree.type);
31037
+ for (const [childKey, value] of Object.entries(estree)) {
31038
+ if (childKey === "type") continue;
31039
+ visit(value, nextInFunction);
31040
+ }
31041
+ }, "visit");
31042
+ visit(program.body, false);
31043
+ return {
31044
+ envKeys: [...envKeys],
31045
+ publicEnvKeys: [...publicEnvKeys],
31046
+ builtinImports: [...builtinImports]
31047
+ };
31048
+ }
31049
+ __name(analyzeClientBoundary, "analyzeClientBoundary");
31050
+ function readNodeBuiltinSource(node) {
31051
+ if (node.type === "ImportDeclaration" || node.type === "ExportNamedDeclaration" || node.type === "ExportAllDeclaration" || node.type === "ImportExpression") {
31052
+ const value = node.source?.value;
31053
+ return typeof value === "string" && value.startsWith("node:") ? value : void 0;
31054
+ }
31055
+ if (node.type === "CallExpression") {
31056
+ const callee = node.callee;
31057
+ if (callee?.type === "Identifier" && callee.name === "require") {
31058
+ const arg = node.arguments?.[0];
31059
+ const value = arg?.type === "Literal" ? arg.value : void 0;
31060
+ return typeof value === "string" && value.startsWith("node:") ? value : void 0;
31061
+ }
31062
+ }
31063
+ return void 0;
31064
+ }
31065
+ __name(readNodeBuiltinSource, "readNodeBuiltinSource");
31066
+ function readProcessEnvKey(node) {
31067
+ if (node.type !== "MemberExpression") return void 0;
31068
+ const object = node.object;
31069
+ if (!object || object.type !== "MemberExpression" || object.object?.type !== "Identifier" || object.object.name !== "process" || object.property?.name !== "env") {
31070
+ return void 0;
31071
+ }
31072
+ const property = node.property;
31073
+ if (!property) return void 0;
31074
+ if (!node.computed && property.type === "Identifier") {
31075
+ return property.name;
31076
+ }
31077
+ if (node.computed && property.type === "Literal") {
31078
+ const value = property.value;
31079
+ return typeof value === "string" ? value : void 0;
31080
+ }
31081
+ return void 0;
31082
+ }
31083
+ __name(readProcessEnvKey, "readProcessEnvKey");
31084
+ function formatClientBoundaryWarning(id, findings) {
31085
+ const { envKeys, publicEnvKeys, builtinImports } = findings;
31086
+ const lines = [`${id} is compiled for the client but uses server-only APIs:`];
31087
+ if (envKeys.length > 0) {
31088
+ lines.push(
31089
+ `- 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.`
31090
+ );
31091
+ }
31092
+ if (publicEnvKeys.length > 0) {
31093
+ lines.push(
31094
+ `- 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".`
31095
+ );
31096
+ }
31097
+ if (builtinImports.length > 0) {
31098
+ lines.push(
31099
+ `- import of ${builtinImports.join(", ")} \u2014 stubbed with an empty object in the browser. Move the import into server-only code.`
31100
+ );
31101
+ }
31102
+ return lines.join("\n");
31103
+ }
31104
+ __name(formatClientBoundaryWarning, "formatClientBoundaryWarning");
31105
+
30648
31106
  // src/vite.ts
30649
31107
  init_deployment();
30650
31108
  init_image_config();
@@ -31629,6 +32087,24 @@ function isFarmConfigFile(file, root) {
31629
32087
  return FARM_CONFIG_FILENAMES.has(relative6);
31630
32088
  }
31631
32089
  __name(isFarmConfigFile, "isFarmConfigFile");
32090
+ var warnedClientBoundaryIds = /* @__PURE__ */ new Set();
32091
+ function warnClientBoundaryOnce(context2, id, code, config) {
32092
+ if (warnedClientBoundaryIds.has(id)) return;
32093
+ let program;
32094
+ try {
32095
+ program = context2.parse(code);
32096
+ } catch {
32097
+ return;
32098
+ }
32099
+ const publicKeys = new Set(Object.keys(config.env?.public ?? {}));
32100
+ const findings = analyzeClientBoundary(program, publicKeys);
32101
+ if (findings.envKeys.length === 0 && findings.publicEnvKeys.length === 0 && findings.builtinImports.length === 0) {
32102
+ return;
32103
+ }
32104
+ warnedClientBoundaryIds.add(id);
32105
+ logger.warn(formatClientBoundaryWarning(id, findings));
32106
+ }
32107
+ __name(warnClientBoundaryOnce, "warnClientBoundaryOnce");
31632
32108
  function farmPlugin(options = {}, initialPluginManager) {
31633
32109
  const imageImports = farmImageImportsPlugin();
31634
32110
  const fontImports = farmFontImportsPlugin({
@@ -32239,6 +32715,22 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32239
32715
  }
32240
32716
  return;
32241
32717
  }
32718
+ if (openAPIManager && options.openapi?.specRoute && requestPathname === options.openapi.specRoute) {
32719
+ if (requestMethod !== "GET" && requestMethod !== "HEAD") {
32720
+ res.statusCode = 405;
32721
+ res.setHeader("Allow", "GET, HEAD");
32722
+ res.setHeader("Content-Type", "text/plain; charset=utf-8");
32723
+ res.end("Method Not Allowed");
32724
+ return;
32725
+ }
32726
+ const spec = await openAPIManager.getSpec();
32727
+ const body = JSON.stringify(spec);
32728
+ res.statusCode = 200;
32729
+ res.setHeader("Content-Type", "application/json; charset=utf-8");
32730
+ res.setHeader("Cache-Control", "no-store");
32731
+ res.end(requestMethod === "HEAD" ? void 0 : body);
32732
+ return;
32733
+ }
32242
32734
  if (openAPIManager && requestPathname === options.openapi?.route) {
32243
32735
  const docsHandler = openAPIManager.getDocsRouteHandler();
32244
32736
  return docsHandler(req, res);
@@ -32249,6 +32741,11 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32249
32741
  docsHeaders.set(key, Array.isArray(value) ? value.join(", ") : value);
32250
32742
  }
32251
32743
  }
32744
+ const runAppMiddlewareForContentRoute = /* @__PURE__ */ __name(async () => {
32745
+ if (!middlewareManager?.hasMiddleware()) return false;
32746
+ const middlewareRequest = createRequestFromNodeRequest(req, new URL(fullUrl));
32747
+ return farmApp.getServerRenderer().runWithRequestContext(middlewareRequest, () => middlewareManager.execute(req, res));
32748
+ }, "runAppMiddlewareForContentRoute");
32252
32749
  if (farmDocsHandler) {
32253
32750
  const docsRequest = new Request(fullUrl, {
32254
32751
  method: requestMethod,
@@ -32256,6 +32753,7 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32256
32753
  });
32257
32754
  const docsResponse = await farmDocsHandler(docsRequest.clone());
32258
32755
  if (docsResponse) {
32756
+ if (await runAppMiddlewareForContentRoute()) return;
32259
32757
  await sendWebResponse(
32260
32758
  res,
32261
32759
  await wrapFarmDocsResponseWithLayouts(docsRequest, docsResponse)
@@ -32282,6 +32780,7 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32282
32780
  }, "resolveSource")
32283
32781
  });
32284
32782
  if (markdownSourceResponse) {
32783
+ if (await runAppMiddlewareForContentRoute()) return;
32285
32784
  await sendWebResponse(res, markdownSourceResponse);
32286
32785
  return;
32287
32786
  }
@@ -32295,9 +32794,23 @@ window.__FARM_MANIFEST__ = ${inlineValue({
32295
32794
  renderPage: /* @__PURE__ */ __name(async (request) => fetch(request), "renderPage")
32296
32795
  });
32297
32796
  if (markdownResponse) {
32797
+ if (await runAppMiddlewareForContentRoute()) return;
32298
32798
  await sendWebResponse(res, markdownResponse);
32299
32799
  return;
32300
32800
  }
32801
+ if (farmRequestWantsMarkdown(requestPathname, req.headers.accept)) {
32802
+ const markdownRoute = farmApp.getRouteManager().matchRoute(normalizeFarmMarkdownRoutePath(requestPathname));
32803
+ if (!markdownRoute.route) {
32804
+ res.statusCode = 404;
32805
+ res.setHeader("Content-Type", FARM_MARKDOWN_CONTENT_TYPE);
32806
+ res.setHeader("X-Farm-Markdown-Error", "404");
32807
+ res.setHeader("Cache-Control", "no-store");
32808
+ res.end(
32809
+ createFarmMarkdownErrorBody(404, requestPathname, farmConfig.basePath || "/")
32810
+ );
32811
+ return;
32812
+ }
32813
+ }
32301
32814
  const markdownPageTarget = resolveMarkdownMirrorTarget(
32302
32815
  farmApp.getConfig().md,
32303
32816
  requestPathname,
@@ -33246,6 +33759,9 @@ export const manifest = getManifest();
33246
33759
  }
33247
33760
  }
33248
33761
  }
33762
+ if (!transformOptions?.ssr && shouldInspectClientBoundary(id, code)) {
33763
+ warnClientBoundaryOnce(this, id, code, farmApp?.getConfig() ?? options);
33764
+ }
33249
33765
  if (typeof imageImports.transform === "function") {
33250
33766
  const imageModule = await imageImports.transform.call(this, code, id, transformOptions);
33251
33767
  if (imageModule) return imageModule;