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

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 (311) hide show
  1. package/dist/after.cjs +1 -1
  2. package/dist/after.mjs +1 -1
  3. package/dist/agent-runtime.d.mts +3 -2
  4. package/dist/agent-runtime.d.ts +3 -2
  5. package/dist/api/route-manager.cjs +43 -4
  6. package/dist/api/route-manager.d.mts +4 -3
  7. package/dist/api/route-manager.d.ts +4 -3
  8. package/dist/api/route-manager.mjs +6 -6
  9. package/dist/api/runtime.cjs +43 -4
  10. package/dist/api/runtime.d.mts +1 -1
  11. package/dist/api/runtime.d.ts +1 -1
  12. package/dist/api/runtime.mjs +4 -4
  13. package/dist/api.cjs +239 -70
  14. package/dist/api.d.mts +7 -6
  15. package/dist/api.d.ts +7 -6
  16. package/dist/api.mjs +17 -11
  17. package/dist/{app-CIIEvA4r.d.ts → app-CIOx3LvY.d.ts} +4 -4
  18. package/dist/{app-O4AEC1OH.d.mts → app-DBd6437H.d.mts} +4 -4
  19. package/dist/app-markdown.d.mts +4 -3
  20. package/dist/app-markdown.d.ts +4 -3
  21. package/dist/cache.cjs +42 -4
  22. package/dist/cache.d.mts +68 -1
  23. package/dist/cache.d.ts +68 -1
  24. package/dist/cache.mjs +3 -3
  25. package/dist/{chunk-AXVJCMVF.mjs → chunk-2HDOV47R.mjs} +3 -3
  26. package/dist/chunk-2HDOV47R.mjs.map +1 -0
  27. package/dist/{chunk-RBRLLZL5.mjs → chunk-34IAPZBG.mjs} +16 -3
  28. package/dist/chunk-34IAPZBG.mjs.map +1 -0
  29. package/dist/{chunk-VV24CC42.mjs → chunk-35NPLKQJ.mjs} +2 -2
  30. package/dist/{chunk-MM46HTX6.mjs → chunk-3TWQERDE.mjs} +4 -3
  31. package/dist/chunk-3TWQERDE.mjs.map +1 -0
  32. package/dist/{chunk-RWYRFCLG.mjs → chunk-4X57GXQA.mjs} +2 -2
  33. package/dist/{chunk-3I55IGXU.mjs → chunk-52BR7LTX.mjs} +14 -15
  34. package/dist/chunk-52BR7LTX.mjs.map +1 -0
  35. package/dist/{chunk-RAGSHKDC.mjs → chunk-6EJHNHH4.mjs} +38 -3
  36. package/dist/chunk-6EJHNHH4.mjs.map +1 -0
  37. package/dist/{chunk-ZFNK77WN.mjs → chunk-6JNGOC37.mjs} +2 -2
  38. package/dist/{after.cjs.map → chunk-6JNGOC37.mjs.map} +1 -1
  39. package/dist/{chunk-MOW47IF5.mjs → chunk-6UYVU46O.mjs} +2 -2
  40. package/dist/{chunk-7MABZJIM.mjs → chunk-7PYVLJ4J.mjs} +1 -1
  41. package/dist/chunk-7PYVLJ4J.mjs.map +1 -0
  42. package/dist/{chunk-54XSGLKT.mjs → chunk-D7F7YZ7L.mjs} +93 -67
  43. package/dist/chunk-D7F7YZ7L.mjs.map +1 -0
  44. package/dist/{chunk-SCFX4UTD.mjs → chunk-EHTUKVSW.mjs} +1 -1
  45. package/dist/chunk-EHTUKVSW.mjs.map +1 -0
  46. package/dist/{chunk-LZTGDH3H.mjs → chunk-EPDIF432.mjs} +424 -10
  47. package/dist/chunk-EPDIF432.mjs.map +1 -0
  48. package/dist/{chunk-GZHKWHCH.mjs → chunk-FP5Q67FO.mjs} +167 -96
  49. package/dist/chunk-FP5Q67FO.mjs.map +1 -0
  50. package/dist/{chunk-HHZAOTQD.mjs → chunk-GJ7W6LNK.mjs} +7 -6
  51. package/dist/chunk-GJ7W6LNK.mjs.map +1 -0
  52. package/dist/{chunk-RLGUZ3HS.mjs → chunk-I3OOUWO5.mjs} +3 -3
  53. package/dist/{chunk-LKKMI7JG.mjs → chunk-IZBX2TEU.mjs} +153 -68
  54. package/dist/chunk-IZBX2TEU.mjs.map +1 -0
  55. package/dist/{chunk-74QPONAN.mjs → chunk-J3MPQ4MQ.mjs} +2 -2
  56. package/dist/{chunk-74QPONAN.mjs.map → chunk-J3MPQ4MQ.mjs.map} +1 -1
  57. package/dist/{chunk-ODJGAWDH.mjs → chunk-JV7JUD7Q.mjs} +4 -4
  58. package/dist/{chunk-Q3OX763A.mjs → chunk-LXDASSTE.mjs} +2 -2
  59. package/dist/{chunk-ZRBCBOEX.mjs → chunk-N2FY2QZ7.mjs} +28 -3
  60. package/dist/chunk-N2FY2QZ7.mjs.map +1 -0
  61. package/dist/{chunk-MRVJCLYQ.mjs → chunk-PSQ7N4XP.mjs} +4 -2
  62. package/dist/chunk-PSQ7N4XP.mjs.map +1 -0
  63. package/dist/{chunk-HVG3HLQZ.mjs → chunk-Q7WGOOAI.mjs} +3 -3
  64. package/dist/{chunk-WTYAPO5I.mjs → chunk-RCNWMEBT.mjs} +3 -2
  65. package/dist/{chunk-WTYAPO5I.mjs.map → chunk-RCNWMEBT.mjs.map} +1 -1
  66. package/dist/{chunk-3INDBMUU.mjs → chunk-TGDIACZT.mjs} +2 -2
  67. package/dist/{chunk-WB75GIX2.mjs → chunk-TQTB33JS.mjs} +14 -3
  68. package/dist/chunk-TQTB33JS.mjs.map +1 -0
  69. package/dist/{chunk-WMAYETRD.mjs → chunk-TT3E2JOF.mjs} +3 -3
  70. package/dist/{chunk-FVAM43KI.mjs → chunk-UMUWR5XA.mjs} +2 -2
  71. package/dist/{chunk-VOUJW4FL.mjs → chunk-WD2EBM3I.mjs} +70 -15
  72. package/dist/chunk-WD2EBM3I.mjs.map +1 -0
  73. package/dist/{chunk-ZPG6SNHH.mjs → chunk-WMJGA32P.mjs} +47 -5
  74. package/dist/chunk-WMJGA32P.mjs.map +1 -0
  75. package/dist/{chunk-TKQCJMY7.mjs → chunk-X4SFHKFH.mjs} +7 -4
  76. package/dist/{chunk-TKQCJMY7.mjs.map → chunk-X4SFHKFH.mjs.map} +1 -1
  77. package/dist/{chunk-Q7GPJZCO.mjs → chunk-YIBV77KZ.mjs} +26 -7
  78. package/dist/chunk-YIBV77KZ.mjs.map +1 -0
  79. package/dist/client/lifecycle.mjs +2 -2
  80. package/dist/{client-mcMC334J.d.mts → client-DWoLZ3QU.d.mts} +52 -5
  81. package/dist/{client-d5ONQ-Pd.d.ts → client-DuQuQC1h.d.ts} +52 -5
  82. package/dist/client-cache-BMxuFqwH.d.ts +18 -0
  83. package/dist/client-cache-CCK-fCe0.d.mts +18 -0
  84. package/dist/{client-plugins-BvClItiL.d.mts → client-plugins-B6-07YmP.d.mts} +52 -99
  85. package/dist/{client-plugins-B9Sq-iMg.d.ts → client-plugins-By6MVUX-.d.ts} +52 -99
  86. package/dist/client-plugins.cjs +1 -1
  87. package/dist/client-plugins.d.mts +3 -2
  88. package/dist/client-plugins.d.ts +3 -2
  89. package/dist/client-plugins.mjs +2 -2
  90. package/dist/client.cjs +757 -104
  91. package/dist/client.d.mts +124 -16
  92. package/dist/client.d.ts +124 -16
  93. package/dist/client.mjs +28 -14
  94. package/dist/config.d.mts +3 -2
  95. package/dist/config.d.ts +3 -2
  96. package/dist/{devtools-S52RRRMN.mjs → devtools-XWZX6CCL.mjs} +5 -5
  97. package/dist/{devtools-client-EOVDBPOI.mjs → devtools-client-Z7QQA3Q2.mjs} +2 -2
  98. package/dist/{devtools-ui-NJKFORTG.mjs → devtools-ui-2HG5M4EL.mjs} +3 -1
  99. package/dist/devtools-ui-2HG5M4EL.mjs.map +1 -0
  100. package/dist/docs.cjs +24 -5
  101. package/dist/docs.mjs +5 -5
  102. package/dist/i18n/server.cjs +13 -2
  103. package/dist/i18n/server.d.mts +2 -2
  104. package/dist/i18n/server.d.ts +2 -2
  105. package/dist/i18n/server.mjs +1 -1
  106. package/dist/index.cjs +1578 -772
  107. package/dist/index.d.mts +28 -20
  108. package/dist/index.d.ts +28 -20
  109. package/dist/index.mjs +53 -33
  110. package/dist/index.mjs.map +1 -1
  111. package/dist/integration-api-BT024nuJ.d.mts +97 -0
  112. package/dist/integration-api-BT024nuJ.d.ts +97 -0
  113. package/dist/integrations.d.mts +3 -2
  114. package/dist/integrations.d.ts +3 -2
  115. package/dist/internal/build-runtime.cjs +710 -532
  116. package/dist/internal/build-runtime.d.mts +5 -4
  117. package/dist/internal/build-runtime.d.ts +5 -4
  118. package/dist/internal/build-runtime.mjs +15 -15
  119. package/dist/internal/client-runtime.cjs +1 -1
  120. package/dist/internal/client-runtime.mjs +2 -2
  121. package/dist/internal/config-runtime.cjs +4 -1
  122. package/dist/internal/config-runtime.d.mts +4 -3
  123. package/dist/internal/config-runtime.d.ts +4 -3
  124. package/dist/internal/config-runtime.mjs +4 -4
  125. package/dist/internal/product-telemetry-runtime.cjs +1 -1
  126. package/dist/internal/product-telemetry-runtime.mjs +1 -1
  127. package/dist/internal/production-runtime.cjs +29 -3
  128. package/dist/internal/production-runtime.d.mts +8 -7
  129. package/dist/internal/production-runtime.d.ts +8 -7
  130. package/dist/internal/production-runtime.mjs +5 -5
  131. package/dist/{metadata-route-yV4U6X8h.d.ts → metadata-route-8_V3UonT.d.ts} +1 -1
  132. package/dist/{metadata-route-p4K1l92V.d.mts → metadata-route-CWP_HjGU.d.mts} +1 -1
  133. package/dist/middleware.cjs +28 -2
  134. package/dist/middleware.d.mts +5 -4
  135. package/dist/middleware.d.ts +5 -4
  136. package/dist/middleware.mjs +4 -4
  137. package/dist/navigation.cjs +3 -1
  138. package/dist/navigation.mjs +3 -3
  139. package/dist/observability.cjs +27 -2
  140. package/dist/observability.mjs +1 -1
  141. package/dist/plugin.cjs +474 -334
  142. package/dist/plugin.d.mts +3 -2
  143. package/dist/plugin.d.ts +3 -2
  144. package/dist/plugin.mjs +12 -12
  145. package/dist/{production-runtime-IkB0Qj4i.d.mts → production-runtime-BGRH1dKG.d.mts} +1 -1
  146. package/dist/{production-runtime-0JTch4_d.d.ts → production-runtime-CdmzsDax.d.ts} +1 -1
  147. package/dist/query/client.cjs +11 -3
  148. package/dist/query/client.mjs +12 -4
  149. package/dist/query/client.mjs.map +1 -1
  150. package/dist/{render-plan-Dc8CjNI7.d.ts → render-plan-0GeHiGVZ.d.ts} +1 -1
  151. package/dist/{render-plan-CU31j-Jk.d.mts → render-plan-DLxJYAA5.d.mts} +1 -1
  152. package/dist/renderer-client.cjs +140 -26
  153. package/dist/renderer-client.d.mts +7 -6
  154. package/dist/renderer-client.d.ts +7 -6
  155. package/dist/renderer-client.mjs +16 -19
  156. package/dist/renderer-client.mjs.map +1 -1
  157. package/dist/{request-BXa1KY8i.d.mts → request-BCNXSXLh.d.mts} +1 -1
  158. package/dist/{request-CUUBujZt.d.ts → request-BstWbZ6O.d.ts} +1 -1
  159. package/dist/request.d.mts +4 -3
  160. package/dist/request.d.ts +4 -3
  161. package/dist/{route-Y8lfz1wF.d.ts → route-4pd-EVtw.d.ts} +45 -50
  162. package/dist/{route-Cq3aObmj.d.mts → route-CvEr4Uc9.d.mts} +45 -50
  163. package/dist/routes.cjs +27 -2
  164. package/dist/routes.d.mts +3 -2
  165. package/dist/routes.d.ts +3 -2
  166. package/dist/routes.mjs +4 -4
  167. package/dist/{runtime-j1S3sDTh.d.ts → runtime-BgDQzBiu.d.ts} +1 -0
  168. package/dist/{runtime-DDwvDtS4.d.mts → runtime-CV2UCV-5.d.mts} +1 -0
  169. package/dist/server-action-security.mjs +2 -2
  170. package/dist/server-fn-client.cjs +34 -2
  171. package/dist/server-fn-client.d.mts +6 -0
  172. package/dist/server-fn-client.d.ts +6 -0
  173. package/dist/server-fn-client.mjs +1 -1
  174. package/dist/server-fn.cjs +42 -4
  175. package/dist/server-fn.mjs +5 -5
  176. package/dist/server-plugins.d.mts +4 -3
  177. package/dist/server-plugins.d.ts +4 -3
  178. package/dist/server-query-client.cjs +146 -27
  179. package/dist/server-query-client.d.mts +3 -3
  180. package/dist/server-query-client.d.ts +3 -3
  181. package/dist/server-query-client.mjs +7 -7
  182. package/dist/{server-query-runtime-3zAj77yv.d.ts → server-query-runtime-DXRNhX_3.d.ts} +2 -0
  183. package/dist/{server-query-runtime-B50KAcj5.d.mts → server-query-runtime-aP52iuL5.d.mts} +2 -0
  184. package/dist/server-query.cjs +44 -5
  185. package/dist/server-query.d.mts +4 -0
  186. package/dist/server-query.d.ts +4 -0
  187. package/dist/server-query.mjs +7 -7
  188. package/dist/server.cjs +499 -355
  189. package/dist/server.d.mts +11 -10
  190. package/dist/server.d.ts +11 -10
  191. package/dist/server.mjs +16 -16
  192. package/dist/{spa-router-CyLkwst6.d.mts → spa-router-BeyAVSc6.d.mts} +1 -1
  193. package/dist/{spa-router-BkqGXaV_.d.ts → spa-router-COgu4txb.d.ts} +1 -1
  194. package/dist/testing.cjs +27 -2
  195. package/dist/testing.d.mts +3 -2
  196. package/dist/testing.d.ts +3 -2
  197. package/dist/testing.mjs +8 -8
  198. package/dist/{utils-NKR6SoNv.d.mts → utils-DL2VwDQl.d.mts} +1 -1
  199. package/dist/{utils-B2OHrb9F.d.ts → utils-DkU2c_1B.d.ts} +1 -1
  200. package/dist/version.cjs +1 -1
  201. package/dist/version.mjs +1 -1
  202. package/dist/vite.cjs +482 -338
  203. package/dist/vite.d.mts +3 -2
  204. package/dist/vite.d.ts +3 -2
  205. package/dist/vite.mjs +16 -12
  206. package/package.json +17 -17
  207. package/types/client.d.ts +87 -1
  208. package/dist/agent-runtime.cjs.map +0 -1
  209. package/dist/api/route-manager.cjs.map +0 -1
  210. package/dist/api/runtime.cjs.map +0 -1
  211. package/dist/api.cjs.map +0 -1
  212. package/dist/app-markdown.cjs.map +0 -1
  213. package/dist/cache.cjs.map +0 -1
  214. package/dist/chunk-3I55IGXU.mjs.map +0 -1
  215. package/dist/chunk-54XSGLKT.mjs.map +0 -1
  216. package/dist/chunk-7MABZJIM.mjs.map +0 -1
  217. package/dist/chunk-AXVJCMVF.mjs.map +0 -1
  218. package/dist/chunk-GZHKWHCH.mjs.map +0 -1
  219. package/dist/chunk-HHZAOTQD.mjs.map +0 -1
  220. package/dist/chunk-LKKMI7JG.mjs.map +0 -1
  221. package/dist/chunk-LZTGDH3H.mjs.map +0 -1
  222. package/dist/chunk-MM46HTX6.mjs.map +0 -1
  223. package/dist/chunk-MRVJCLYQ.mjs.map +0 -1
  224. package/dist/chunk-Q7GPJZCO.mjs.map +0 -1
  225. package/dist/chunk-RAGSHKDC.mjs.map +0 -1
  226. package/dist/chunk-RBRLLZL5.mjs.map +0 -1
  227. package/dist/chunk-SCFX4UTD.mjs.map +0 -1
  228. package/dist/chunk-VOUJW4FL.mjs.map +0 -1
  229. package/dist/chunk-WB75GIX2.mjs.map +0 -1
  230. package/dist/chunk-ZFNK77WN.mjs.map +0 -1
  231. package/dist/chunk-ZPG6SNHH.mjs.map +0 -1
  232. package/dist/chunk-ZRBCBOEX.mjs.map +0 -1
  233. package/dist/client/lifecycle.cjs.map +0 -1
  234. package/dist/client-cache-CopR_MaI.d.mts +0 -6
  235. package/dist/client-cache-Djy_TlE8.d.ts +0 -6
  236. package/dist/client-plugins.cjs.map +0 -1
  237. package/dist/client.cjs.map +0 -1
  238. package/dist/config.cjs.map +0 -1
  239. package/dist/cron.cjs.map +0 -1
  240. package/dist/deferred.cjs.map +0 -1
  241. package/dist/deployment.cjs.map +0 -1
  242. package/dist/devtools-ui-NJKFORTG.mjs.map +0 -1
  243. package/dist/docs.cjs.map +0 -1
  244. package/dist/env-types.cjs.map +0 -1
  245. package/dist/env.cjs.map +0 -1
  246. package/dist/environment/vite.cjs.map +0 -1
  247. package/dist/environment.cjs.map +0 -1
  248. package/dist/font.cjs.map +0 -1
  249. package/dist/headers.cjs.map +0 -1
  250. package/dist/i18n/client.cjs.map +0 -1
  251. package/dist/i18n/index.cjs.map +0 -1
  252. package/dist/i18n/server.cjs.map +0 -1
  253. package/dist/image/server.cjs.map +0 -1
  254. package/dist/image/sharp.cjs.map +0 -1
  255. package/dist/image.cjs.map +0 -1
  256. package/dist/index.cjs.map +0 -1
  257. package/dist/instrumentation.cjs.map +0 -1
  258. package/dist/integrations.cjs.map +0 -1
  259. package/dist/internal/build-runtime.cjs.map +0 -1
  260. package/dist/internal/client-runtime.cjs.map +0 -1
  261. package/dist/internal/config-runtime.cjs.map +0 -1
  262. package/dist/internal/isolated-boundary.cjs.map +0 -1
  263. package/dist/internal/metadata-image-runtime.cjs.map +0 -1
  264. package/dist/internal/product-telemetry-runtime.cjs.map +0 -1
  265. package/dist/internal/production-node-env.cjs.map +0 -1
  266. package/dist/internal/production-runtime.cjs.map +0 -1
  267. package/dist/markdown.cjs.map +0 -1
  268. package/dist/middleware.cjs.map +0 -1
  269. package/dist/navigation.cjs.map +0 -1
  270. package/dist/observability.cjs.map +0 -1
  271. package/dist/plugin.cjs.map +0 -1
  272. package/dist/query/client.cjs.map +0 -1
  273. package/dist/query/index.cjs.map +0 -1
  274. package/dist/query/parsers.cjs.map +0 -1
  275. package/dist/query/server.cjs.map +0 -1
  276. package/dist/renderer/react/client.cjs.map +0 -1
  277. package/dist/renderer/react/server.cjs.map +0 -1
  278. package/dist/renderer/react/vite.cjs.map +0 -1
  279. package/dist/renderer-client.cjs.map +0 -1
  280. package/dist/renderer.cjs.map +0 -1
  281. package/dist/request.cjs.map +0 -1
  282. package/dist/router.cjs.map +0 -1
  283. package/dist/routes.cjs.map +0 -1
  284. package/dist/server-action-security.cjs.map +0 -1
  285. package/dist/server-fn-client.cjs.map +0 -1
  286. package/dist/server-fn.cjs.map +0 -1
  287. package/dist/server-plugins.cjs.map +0 -1
  288. package/dist/server-query-client.cjs.map +0 -1
  289. package/dist/server-query.cjs.map +0 -1
  290. package/dist/server.cjs.map +0 -1
  291. package/dist/storage.cjs.map +0 -1
  292. package/dist/testing.cjs.map +0 -1
  293. package/dist/theme/client.cjs.map +0 -1
  294. package/dist/theme/index.cjs.map +0 -1
  295. package/dist/theme/runtime.cjs.map +0 -1
  296. package/dist/theme/server.cjs.map +0 -1
  297. package/dist/version.cjs.map +0 -1
  298. package/dist/vite.cjs.map +0 -1
  299. package/dist/workflows.cjs.map +0 -1
  300. /package/dist/{chunk-VV24CC42.mjs.map → chunk-35NPLKQJ.mjs.map} +0 -0
  301. /package/dist/{chunk-RWYRFCLG.mjs.map → chunk-4X57GXQA.mjs.map} +0 -0
  302. /package/dist/{chunk-MOW47IF5.mjs.map → chunk-6UYVU46O.mjs.map} +0 -0
  303. /package/dist/{chunk-RLGUZ3HS.mjs.map → chunk-I3OOUWO5.mjs.map} +0 -0
  304. /package/dist/{chunk-ODJGAWDH.mjs.map → chunk-JV7JUD7Q.mjs.map} +0 -0
  305. /package/dist/{chunk-Q3OX763A.mjs.map → chunk-LXDASSTE.mjs.map} +0 -0
  306. /package/dist/{chunk-HVG3HLQZ.mjs.map → chunk-Q7WGOOAI.mjs.map} +0 -0
  307. /package/dist/{chunk-3INDBMUU.mjs.map → chunk-TGDIACZT.mjs.map} +0 -0
  308. /package/dist/{chunk-WMAYETRD.mjs.map → chunk-TT3E2JOF.mjs.map} +0 -0
  309. /package/dist/{chunk-FVAM43KI.mjs.map → chunk-UMUWR5XA.mjs.map} +0 -0
  310. /package/dist/{devtools-S52RRRMN.mjs.map → devtools-XWZX6CCL.mjs.map} +0 -0
  311. /package/dist/{devtools-client-EOVDBPOI.mjs.map → devtools-client-Z7QQA3Q2.mjs.map} +0 -0
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../../src/image-sharp.ts","../../src/image-server.ts"],"sourcesContent":["import type { ResolvedFarmImageConfig } from \"./image-config\";\nimport {\n FarmImageRequestError,\n isPrivateImageAddress,\n selectOutputFormat,\n type FarmImageTransformer,\n} from \"./image-server\";\n\ntype NodeImageDnsLookup = (\n hostname: string,\n options: { all: true; verbatim: true },\n callback: (\n error: NodeJS.ErrnoException | null,\n addresses: Array<{ address: string; family: number }>,\n ) => void,\n) => void;\n\nexport function createSharpImageTransformer(): FarmImageTransformer {\n return async ({ source, sourceType, width, quality, accept, formats, signal }) => {\n // Sharp is an optional native runtime. Load it only when an image request\n // actually needs a transform so disabled/unused image pipelines do not add\n // a startup dependency or native-module initialization cost.\n const { default: sharp } = await import(\"sharp\");\n throwIfAborted(signal);\n const outputFormat = selectOutputFormat(accept, formats);\n let pipeline = sharp(source, {\n animated: sourceType === \"image/gif\" || sourceType === \"image/webp\",\n failOn: \"warning\",\n limitInputPixels: 268_402_689,\n })\n .rotate()\n .resize({ width, fit: \"inside\", withoutEnlargement: true });\n\n // When the Accept header matches none of the configured formats, keep the\n // source's own format (svg rasterizes to png) instead of forcing JPEG —\n // that preserved neither transparency nor the truth of the content type.\n let encodedType: string;\n if (outputFormat === \"image/avif\") {\n pipeline = pipeline.avif({ quality });\n encodedType = \"image/avif\";\n } else if (outputFormat === \"image/webp\") {\n pipeline = pipeline.webp({ quality });\n encodedType = \"image/webp\";\n } else if (sourceType === \"image/png\" || sourceType === \"image/svg+xml\") {\n pipeline = pipeline.png();\n encodedType = \"image/png\";\n } else if (sourceType === \"image/gif\") {\n pipeline = pipeline.gif();\n encodedType = \"image/gif\";\n } else if (sourceType === \"image/webp\") {\n pipeline = pipeline.webp({ quality });\n encodedType = \"image/webp\";\n } else if (sourceType === \"image/avif\") {\n pipeline = pipeline.avif({ quality });\n encodedType = \"image/avif\";\n } else {\n pipeline = pipeline.jpeg({ quality });\n encodedType = \"image/jpeg\";\n }\n\n const body = await pipeline.toBuffer();\n throwIfAborted(signal);\n return {\n body,\n contentType: encodedType,\n };\n };\n}\n\nexport function createNodeImageUrlValidator(config: ResolvedFarmImageConfig) {\n return async function validateNodeImageUrl(url: URL): Promise<void> {\n if (config.dangerouslyAllowLocalIP) return;\n\n let addresses: Array<{ address: string; family: number }>;\n try {\n // DNS is only needed by remote image requests. Keeping it out of the\n // initial server module graph reduces normal page/API startup work.\n const { lookup } = await import(\"node:dns/promises\");\n addresses = await lookup(url.hostname, { all: true, verbatim: true });\n } catch {\n throw new FarmImageRequestError(\n \"PRIVATE_SOURCE\",\n 400,\n \"Could not safely resolve the image source\",\n );\n }\n if (addresses.length === 0 || addresses.some(({ address }) => isPrivateImageAddress(address))) {\n throw new FarmImageRequestError(\"PRIVATE_SOURCE\", 400, \"Private image source is not allowed\");\n }\n };\n}\n\n/**\n * Create a remote image fetcher whose socket lookup rejects private addresses.\n * Validation and connection share this lookup, closing the DNS-rebinding gap\n * left by resolving a hostname before a separate global fetch.\n */\nexport function createNodeImageFetcher(\n config: ResolvedFarmImageConfig,\n lookup?: NodeImageDnsLookup,\n): typeof globalThis.fetch {\n if (config.dangerouslyAllowLocalIP) return globalThis.fetch.bind(globalThis);\n\n return (async (input: RequestInfo | URL, init: RequestInit = {}) => {\n const inputRequest = input instanceof Request ? input : undefined;\n const url = input instanceof URL ? input : new URL(inputRequest?.url ?? String(input));\n if (url.protocol !== \"http:\" && url.protocol !== \"https:\") {\n throw new FarmImageRequestError(\"DISALLOWED_SOURCE\", 400, \"Unsupported image protocol\");\n }\n\n const [{ request }, { Readable }, dns] = await Promise.all([\n url.protocol === \"https:\" ? import(\"node:https\") : import(\"node:http\"),\n import(\"node:stream\"),\n lookup ? Promise.resolve(null) : import(\"node:dns\"),\n ]);\n const resolveHostname = lookup ?? (dns!.lookup as unknown as NodeImageDnsLookup);\n const headers = new Headers(inputRequest?.headers);\n new Headers(init.headers).forEach((value, name) => headers.set(name, value));\n if (!headers.has(\"accept-encoding\")) headers.set(\"accept-encoding\", \"identity\");\n\n return new Promise<Response>((resolve, reject) => {\n const nodeRequest = request(\n url,\n {\n method: init.method ?? inputRequest?.method ?? \"GET\",\n headers: Object.fromEntries(headers.entries()),\n signal: init.signal ?? inputRequest?.signal,\n lookup(hostname, options, callback) {\n resolveHostname(hostname, { all: true, verbatim: true }, (error, addresses) => {\n if (error) {\n callback(error, \"\", 4);\n return;\n }\n if (\n addresses.length === 0 ||\n addresses.some(({ address }) => isPrivateImageAddress(address))\n ) {\n callback(\n new FarmImageRequestError(\n \"PRIVATE_SOURCE\",\n 400,\n \"Private image source is not allowed\",\n ),\n \"\",\n 4,\n );\n return;\n }\n\n if (options.all) {\n (\n callback as unknown as (\n error: null,\n addresses: Array<{ address: string; family: number }>,\n ) => void\n )(null, addresses);\n return;\n }\n const address = addresses[0]!;\n callback(null, address.address, address.family);\n });\n },\n },\n (nodeResponse) => {\n const status = nodeResponse.statusCode ?? 500;\n if (status < 200 || status > 599) {\n nodeResponse.resume();\n reject(\n new FarmImageRequestError(\n \"UNSUPPORTED_IMAGE\",\n 502,\n \"Image source returned an invalid HTTP status\",\n ),\n );\n return;\n }\n const responseHeaders = new Headers();\n for (let index = 0; index < nodeResponse.rawHeaders.length; index += 2) {\n responseHeaders.append(\n nodeResponse.rawHeaders[index]!,\n nodeResponse.rawHeaders[index + 1]!,\n );\n }\n const body =\n status === 204 || status === 205 || status === 304\n ? null\n : (Readable.toWeb(nodeResponse) as ReadableStream);\n resolve(\n new Response(body, {\n status,\n statusText: nodeResponse.statusMessage,\n headers: responseHeaders,\n }),\n );\n },\n );\n nodeRequest.once(\"error\", reject);\n nodeRequest.end();\n });\n }) as typeof globalThis.fetch;\n}\n\nfunction throwIfAborted(signal: AbortSignal): void {\n if (signal.aborted) {\n throw signal.reason instanceof Error\n ? signal.reason\n : new DOMException(\"The image request was aborted\", \"AbortError\");\n }\n}\n","import type {\n FarmImageFormat,\n FarmImageLocalPattern,\n FarmImageRemotePattern,\n ResolvedFarmImageConfig,\n} from \"./image-config\";\nimport { matchesFarmIfNoneMatch } from \"./server-http\";\n\nexport interface FarmImageTransformInput {\n source: Uint8Array;\n sourceUrl: URL;\n sourceType: string;\n width: number;\n quality: number;\n accept: string;\n formats: readonly FarmImageFormat[];\n signal: AbortSignal;\n}\n\nexport interface FarmImageTransformResult {\n body: Uint8Array;\n contentType: string;\n}\n\nexport type FarmImageTransformer = (\n input: FarmImageTransformInput,\n) => Promise<FarmImageTransformResult>;\n\nexport interface CreateFarmImageHandlerOptions {\n fetch?: typeof globalThis.fetch;\n /** Node-only fetcher that validates the DNS result used for remote connections. @internal */\n fetchRemote?: typeof globalThis.fetch;\n transform: FarmImageTransformer;\n validateRemoteUrl?: (url: URL) => void | Promise<void>;\n onError?: (error: unknown, request: Request) => void;\n cacheEntries?: number;\n}\n\nexport type FarmImageHandler = (request: Request) => Promise<Response | null>;\n\ntype OptimizedImage = FarmImageTransformResult & {\n etag: string;\n cacheControl: string;\n expiresAt: number;\n};\n\ntype FarmImageRequestErrorCode =\n | \"BODY_TOO_LARGE\"\n | \"DISALLOWED_SOURCE\"\n | \"INVALID_METHOD\"\n | \"INVALID_PARAMETER\"\n | \"PRIVATE_SOURCE\"\n | \"TOO_MANY_REDIRECTS\"\n | \"UNSUPPORTED_IMAGE\";\n\nexport class FarmImageRequestError extends Error {\n readonly code: FarmImageRequestErrorCode;\n readonly status: number;\n\n constructor(code: FarmImageRequestErrorCode, status: number, message: string) {\n super(message);\n this.name = \"FarmImageRequestError\";\n this.code = code;\n this.status = status;\n }\n}\n\nexport function createFarmImageHandler(\n config: ResolvedFarmImageConfig,\n options: CreateFarmImageHandlerOptions,\n): FarmImageHandler {\n const fetcher = options.fetch ?? globalThis.fetch;\n const cache = new FarmImageMemoryCache(options.cacheEntries ?? 100);\n const allowedWidths = new Set([...config.deviceSizes, ...config.imageSizes]);\n const allowedQualities = new Set(config.qualities);\n\n return async function handleFarmImage(request): Promise<Response | null> {\n const requestUrl = new URL(request.url);\n if (requestUrl.pathname !== config.path) return null;\n\n try {\n if (config.provider === \"none\") {\n throw new FarmImageRequestError(\n \"DISALLOWED_SOURCE\",\n 404,\n \"The Farm image optimizer is disabled\",\n );\n }\n if (request.method !== \"GET\" && request.method !== \"HEAD\") {\n throw new FarmImageRequestError(\n \"INVALID_METHOD\",\n 405,\n \"The Farm image optimizer only accepts GET and HEAD\",\n );\n }\n\n const sourceUrl = await resolveImageSourceUrl(requestUrl, config, options.validateRemoteUrl);\n const width = parseAllowedInteger(requestUrl.searchParams.get(\"w\"), allowedWidths, \"width\");\n const quality = parseAllowedInteger(\n requestUrl.searchParams.get(\"q\"),\n allowedQualities,\n \"quality\",\n );\n const accept = request.headers.get(\"accept\") ?? \"\";\n const cacheKey = `${sourceUrl.href}\\n${width}\\n${quality}\\n${accept}`;\n let optimized = cache.get(cacheKey);\n\n if (!optimized) {\n const fetchedSource = await fetchImageSource(\n sourceUrl,\n requestUrl.origin,\n config,\n fetcher,\n options.fetchRemote,\n options.validateRemoteUrl,\n request.signal,\n );\n const source = await readResponseWithLimit(\n fetchedSource.response,\n config.maximumResponseBody,\n );\n const sourceType = detectImageContentType(source);\n validateSourceType(sourceType, config);\n throwIfAborted(request.signal);\n\n const result = await options.transform({\n source,\n sourceUrl: fetchedSource.url,\n sourceType,\n width,\n quality,\n accept,\n formats: config.formats,\n signal: request.signal,\n });\n throwIfAborted(request.signal);\n validateTransformedResult(result, config);\n\n optimized = {\n ...result,\n etag: createImageEtag(result.body),\n cacheControl: `public, max-age=${config.minimumCacheTTL}, stale-while-revalidate=${Math.max(\n config.minimumCacheTTL,\n 60,\n )}`,\n expiresAt: Date.now() + config.minimumCacheTTL * 1_000,\n };\n cache.set(cacheKey, optimized);\n }\n\n return createOptimizedImageResponse(request, optimized, config);\n } catch (error) {\n if (!(error instanceof FarmImageRequestError) && !isAbortError(error)) {\n try {\n options.onError?.(error, request);\n } catch {\n // Error reporting must not replace the optimizer's sanitized response.\n }\n }\n return createFarmImageErrorResponse(error);\n }\n };\n}\n\nexport function createCloudflareImageTransformer(\n fetcher: typeof globalThis.fetch = globalThis.fetch,\n): FarmImageTransformer {\n return async ({ sourceUrl, width, quality, accept, formats, signal }) => {\n const format = selectOutputFormat(accept, formats);\n const response = await fetcher(sourceUrl, {\n signal,\n headers: { accept: \"image/*\" },\n cf: {\n image: {\n fit: \"scale-down\",\n width,\n quality,\n ...(format ? { format: format === \"image/avif\" ? \"avif\" : \"webp\" } : {}),\n },\n },\n } as RequestInit);\n\n if (!response.ok) {\n throw new FarmImageRequestError(\n \"UNSUPPORTED_IMAGE\",\n response.status === 404 ? 404 : 502,\n \"Cloudflare could not transform the source image\",\n );\n }\n\n const body = new Uint8Array(await response.arrayBuffer());\n return {\n body,\n contentType:\n normalizeImageContentType(response.headers.get(\"content-type\")) ||\n detectImageContentType(body),\n };\n };\n}\n\nexport function selectOutputFormat(\n accept: string,\n formats: readonly FarmImageFormat[],\n): FarmImageFormat | undefined {\n const qualityByFormat = new Map<string, number>();\n\n for (const range of accept.split(\",\")) {\n const [rawType, ...parameters] = range.split(\";\");\n const type = rawType.trim().toLowerCase();\n if (!type) continue;\n\n let quality = 1;\n for (const parameter of parameters) {\n const [rawName, rawValue] = parameter.split(\"=\", 2);\n if (rawName.trim().toLowerCase() !== \"q\") continue;\n const parsed = Number(rawValue?.trim());\n quality = Number.isFinite(parsed) && parsed >= 0 && parsed <= 1 ? parsed : 0;\n break;\n }\n\n qualityByFormat.set(type, Math.max(qualityByFormat.get(type) ?? 0, quality));\n }\n\n let selected: FarmImageFormat | undefined;\n let selectedQuality = 0;\n for (const format of formats) {\n const quality = qualityByFormat.get(format) ?? 0;\n if (quality > selectedQuality) {\n selected = format;\n selectedQuality = quality;\n }\n }\n return selected;\n}\n\nexport function isPrivateImageAddress(address: string): boolean {\n const value = address.toLowerCase().replace(/^\\[|\\]$/g, \"\");\n if (value === \"::\" || value === \"::1\") return true;\n if (/^(?:fc|fd)[0-9a-f]{2}:/.test(value) || /^fe[89ab][0-9a-f]:/.test(value)) return true;\n if (value.startsWith(\"::ffff:\")) return isPrivateImageAddress(value.slice(7));\n\n const parts = value.split(\".\");\n if (parts.length !== 4 || parts.some((part) => !/^\\d{1,3}$/.test(part))) return false;\n const octets = parts.map(Number);\n if (octets.some((part) => part > 255)) return false;\n\n const [a, b] = octets;\n return (\n a === 0 ||\n a === 10 ||\n a === 127 ||\n (a === 100 && b >= 64 && b <= 127) ||\n (a === 169 && b === 254) ||\n (a === 172 && b >= 16 && b <= 31) ||\n (a === 192 && (b === 0 || b === 168)) ||\n (a === 198 && (b === 18 || b === 19)) ||\n a >= 224\n );\n}\n\nfunction parseAllowedInteger(\n raw: string | null,\n allowed: ReadonlySet<number>,\n name: string,\n): number {\n if (!raw || !/^\\d+$/.test(raw)) {\n throw new FarmImageRequestError(\n \"INVALID_PARAMETER\",\n 400,\n `Image ${name} must be an allowed integer`,\n );\n }\n const value = Number(raw);\n if (!allowed.has(value)) {\n throw new FarmImageRequestError(\"INVALID_PARAMETER\", 400, `Image ${name} is not configured`);\n }\n return value;\n}\n\nasync function resolveImageSourceUrl(\n requestUrl: URL,\n config: ResolvedFarmImageConfig,\n validateRemoteUrl: CreateFarmImageHandlerOptions[\"validateRemoteUrl\"],\n): Promise<URL> {\n const raw = requestUrl.searchParams.get(\"url\");\n if (!raw || raw.length > 4096 || raw.startsWith(\"//\")) {\n throw new FarmImageRequestError(\"INVALID_PARAMETER\", 400, \"Invalid image source URL\");\n }\n\n let sourceUrl: URL;\n try {\n sourceUrl = raw.startsWith(\"/\") ? new URL(raw, requestUrl.origin) : new URL(raw);\n } catch {\n throw new FarmImageRequestError(\"INVALID_PARAMETER\", 400, \"Invalid image source URL\");\n }\n\n await validateImageSourceUrl(sourceUrl, requestUrl.origin, config, validateRemoteUrl);\n return sourceUrl;\n}\n\nasync function validateImageSourceUrl(\n sourceUrl: URL,\n requestOrigin: string,\n config: ResolvedFarmImageConfig,\n validateRemoteUrl: CreateFarmImageHandlerOptions[\"validateRemoteUrl\"],\n): Promise<void> {\n if (sourceUrl.protocol !== \"http:\" && sourceUrl.protocol !== \"https:\") {\n throw new FarmImageRequestError(\"DISALLOWED_SOURCE\", 400, \"Unsupported image protocol\");\n }\n if (sourceUrl.username || sourceUrl.password || sourceUrl.hash) {\n throw new FarmImageRequestError(\"DISALLOWED_SOURCE\", 400, \"Unsafe image source URL\");\n }\n\n if (sourceUrl.origin === requestOrigin) {\n if (\n sourceUrl.pathname === config.path ||\n !matchesLocalPatterns(sourceUrl, config.localPatterns)\n ) {\n throw new FarmImageRequestError(\n \"DISALLOWED_SOURCE\",\n 400,\n \"Local image source is not allowed\",\n );\n }\n return;\n }\n\n if (!matchesRemoteSource(sourceUrl, config)) {\n throw new FarmImageRequestError(\"DISALLOWED_SOURCE\", 400, \"Remote image source is not allowed\");\n }\n if (!config.dangerouslyAllowLocalIP && isPrivateImageAddress(sourceUrl.hostname)) {\n throw new FarmImageRequestError(\"PRIVATE_SOURCE\", 400, \"Private image source is not allowed\");\n }\n if (!config.dangerouslyAllowLocalIP) {\n await validateRemoteUrl?.(sourceUrl);\n }\n}\n\nasync function fetchImageSource(\n initialUrl: URL,\n requestOrigin: string,\n config: ResolvedFarmImageConfig,\n fetcher: typeof globalThis.fetch,\n fetchRemote: typeof globalThis.fetch | undefined,\n validateRemoteUrl: CreateFarmImageHandlerOptions[\"validateRemoteUrl\"],\n signal: AbortSignal,\n): Promise<{ response: Response; url: URL }> {\n let currentUrl = initialUrl;\n\n for (let redirectCount = 0; ; redirectCount += 1) {\n throwIfAborted(signal);\n const sourceFetcher = currentUrl.origin === requestOrigin ? fetcher : (fetchRemote ?? fetcher);\n const response = await sourceFetcher(currentUrl, {\n method: \"GET\",\n redirect: \"manual\",\n signal,\n headers: {\n accept: \"image/avif,image/webp,image/*,*/*;q=0.8\",\n \"user-agent\": \"Farm.js Image Optimizer\",\n },\n });\n\n if (![301, 302, 303, 307, 308].includes(response.status)) {\n if (!response.ok) {\n await cancelResponseBody(response);\n throw new FarmImageRequestError(\n \"UNSUPPORTED_IMAGE\",\n response.status === 404 ? 404 : 502,\n \"Could not fetch source image\",\n );\n }\n return { response, url: currentUrl };\n }\n\n if (redirectCount >= config.maximumRedirects) {\n await cancelResponseBody(response);\n throw new FarmImageRequestError(\n \"TOO_MANY_REDIRECTS\",\n 400,\n \"Source image exceeded the redirect limit\",\n );\n }\n const location = response.headers.get(\"location\");\n if (!location) {\n await cancelResponseBody(response);\n throw new FarmImageRequestError(\"UNSUPPORTED_IMAGE\", 502, \"Invalid image redirect\");\n }\n await cancelResponseBody(response);\n currentUrl = new URL(location, currentUrl);\n await validateImageSourceUrl(currentUrl, requestOrigin, config, validateRemoteUrl);\n }\n}\n\nasync function readResponseWithLimit(response: Response, limit: number): Promise<Uint8Array> {\n const contentLength = response.headers.get(\"content-length\");\n if (contentLength && Number(contentLength) > limit) {\n // Cleanup (including an unread tee branch) must not delay the size rejection.\n void cancelResponseBody(response);\n throw new FarmImageRequestError(\"BODY_TOO_LARGE\", 413, \"Source image is too large\");\n }\n\n if (!response.body) return new Uint8Array();\n const reader = response.body.getReader();\n const chunks: Uint8Array[] = [];\n let byteLength = 0;\n\n try {\n while (true) {\n const { done, value } = await reader.read();\n if (done) break;\n byteLength += value.byteLength;\n if (byteLength > limit) {\n const error = new FarmImageRequestError(\"BODY_TOO_LARGE\", 413, \"Source image is too large\");\n void reader.cancel(error).catch(() => {});\n throw error;\n }\n chunks.push(value);\n }\n } finally {\n reader.releaseLock();\n }\n\n const result = new Uint8Array(byteLength);\n let offset = 0;\n for (const chunk of chunks) {\n result.set(chunk, offset);\n offset += chunk.byteLength;\n }\n return result;\n}\n\nasync function cancelResponseBody(response: Response): Promise<void> {\n try {\n await response.body?.cancel();\n } catch {\n // Cleanup must not replace the request error or redirect result.\n }\n}\n\nfunction detectImageContentType(bytes: Uint8Array): string {\n if (\n bytes.length >= 8 &&\n bytes[0] === 0x89 &&\n bytes[1] === 0x50 &&\n bytes[2] === 0x4e &&\n bytes[3] === 0x47\n ) {\n return \"image/png\";\n }\n if (bytes.length >= 3 && bytes[0] === 0xff && bytes[1] === 0xd8 && bytes[2] === 0xff) {\n return \"image/jpeg\";\n }\n if (bytes.length >= 6) {\n const signature = new TextDecoder().decode(bytes.slice(0, 6));\n if (signature === \"GIF87a\" || signature === \"GIF89a\") return \"image/gif\";\n }\n if (bytes.length >= 12) {\n const riff = new TextDecoder().decode(bytes.slice(0, 4));\n const webp = new TextDecoder().decode(bytes.slice(8, 12));\n if (riff === \"RIFF\" && webp === \"WEBP\") return \"image/webp\";\n const box = new TextDecoder().decode(bytes.slice(4, 12));\n if (box.startsWith(\"ftypavif\") || box.startsWith(\"ftypavis\")) return \"image/avif\";\n }\n\n const prefix = new TextDecoder().decode(bytes.slice(0, 512)).trimStart().toLowerCase();\n if (prefix.startsWith(\"<svg\") || (prefix.startsWith(\"<?xml\") && prefix.includes(\"<svg\"))) {\n return \"image/svg+xml\";\n }\n return \"\";\n}\n\nfunction validateSourceType(type: string, config: ResolvedFarmImageConfig): void {\n if (!type || (type === \"image/svg+xml\" && !config.dangerouslyAllowSVG)) {\n throw new FarmImageRequestError(\"UNSUPPORTED_IMAGE\", 415, \"Unsupported source image\");\n }\n}\n\nfunction validateTransformedResult(\n result: FarmImageTransformResult,\n config: ResolvedFarmImageConfig,\n): void {\n if (!(result.body instanceof Uint8Array) || result.body.byteLength === 0) {\n throw new Error(\"The image transformer returned an empty response\");\n }\n if (result.body.byteLength > config.maximumResponseBody) {\n throw new FarmImageRequestError(\"BODY_TOO_LARGE\", 413, \"Optimized image is too large\");\n }\n const contentType = normalizeImageContentType(result.contentType);\n if (!contentType || (contentType === \"image/svg+xml\" && !config.dangerouslyAllowSVG)) {\n throw new Error(\"The image transformer returned an unsupported content type\");\n }\n result.contentType = contentType;\n}\n\nfunction normalizeImageContentType(value: string | null): string {\n const type = value?.split(\";\", 1)[0].trim().toLowerCase() ?? \"\";\n return type.startsWith(\"image/\") ? type : \"\";\n}\n\nfunction matchesRemoteSource(url: URL, config: ResolvedFarmImageConfig): boolean {\n if (config.domains.includes(url.hostname.toLowerCase())) return true;\n return config.remotePatterns.some((pattern) => matchesRemotePattern(url, pattern));\n}\n\nfunction matchesRemotePattern(url: URL, pattern: FarmImageRemotePattern): boolean {\n return (\n (!pattern.protocol || url.protocol === `${pattern.protocol}:`) &&\n matchesHostname(url.hostname, pattern.hostname) &&\n (pattern.port === undefined || url.port === pattern.port) &&\n matchesGlob(url.pathname, pattern.pathname ?? \"/**\") &&\n (pattern.search === undefined || url.search === pattern.search)\n );\n}\n\nfunction matchesLocalPatterns(url: URL, patterns: readonly FarmImageLocalPattern[]): boolean {\n return patterns.some(\n (pattern) =>\n matchesGlob(url.pathname, pattern.pathname) &&\n (pattern.search === undefined || url.search === pattern.search),\n );\n}\n\nfunction matchesHostname(hostname: string, pattern: string): boolean {\n const normalizedHostname = hostname.toLowerCase();\n const normalizedPattern = pattern.toLowerCase();\n if (normalizedPattern.startsWith(\"**.\")) {\n const suffix = normalizedPattern.slice(3);\n return normalizedHostname === suffix || normalizedHostname.endsWith(`.${suffix}`);\n }\n if (normalizedPattern.startsWith(\"*.\")) {\n const suffix = normalizedPattern.slice(2);\n const prefix = normalizedHostname.slice(0, -(suffix.length + 1));\n return normalizedHostname.endsWith(`.${suffix}`) && !!prefix && !prefix.includes(\".\");\n }\n return normalizedHostname === normalizedPattern;\n}\n\nfunction matchesGlob(value: string, pattern: string): boolean {\n const escaped = pattern.replace(/[.+?^${}()|[\\]\\\\]/g, \"\\\\$&\");\n const source = escaped.replace(/\\*\\*/g, \"\\0\").replace(/\\*/g, \"[^/]*\").replace(/\\0/g, \".*\");\n return new RegExp(`^${source}$`).test(value);\n}\n\nfunction createOptimizedImageResponse(\n request: Request,\n image: OptimizedImage,\n config: ResolvedFarmImageConfig,\n): Response {\n const headers = new Headers({\n \"cache-control\": image.cacheControl,\n \"content-type\": image.contentType,\n \"content-length\": String(image.body.byteLength),\n \"content-disposition\": \"inline\",\n etag: image.etag,\n vary: \"Accept\",\n \"x-content-type-options\": \"nosniff\",\n });\n if (image.contentType === \"image/svg+xml\" && config.dangerouslyAllowSVG) {\n headers.set(\"content-security-policy\", \"default-src 'none'; sandbox\");\n }\n if (matchesFarmIfNoneMatch(request.headers.get(\"if-none-match\"), image.etag)) {\n headers.delete(\"content-length\");\n return new Response(null, { status: 304, headers });\n }\n const body =\n request.method === \"HEAD\"\n ? null\n : image.body.buffer.slice(\n image.body.byteOffset,\n image.body.byteOffset + image.body.byteLength,\n );\n return new Response(body as ArrayBuffer | null, { status: 200, headers });\n}\n\nfunction createFarmImageErrorResponse(error: unknown): Response {\n const status =\n error instanceof FarmImageRequestError ? error.status : isAbortError(error) ? 499 : 500;\n const headers = new Headers({\n \"cache-control\": \"no-store\",\n \"content-type\": \"text/plain; charset=utf-8\",\n \"x-content-type-options\": \"nosniff\",\n });\n if (status === 405) headers.set(\"allow\", \"GET, HEAD\");\n\n const message =\n status === 400\n ? \"Invalid image request\"\n : status === 404\n ? \"Image not found\"\n : status === 405\n ? \"Method not allowed\"\n : status === 413\n ? \"Image is too large\"\n : status === 415\n ? \"Unsupported image\"\n : status === 499\n ? \"Image request cancelled\"\n : \"Image optimization failed\";\n return new Response(message, { status, headers });\n}\n\nfunction createImageEtag(bytes: Uint8Array): string {\n let hash = 0x811c9dc5;\n for (const byte of bytes) {\n hash ^= byte;\n hash = Math.imul(hash, 0x01000193);\n }\n return `W/\"farm-${bytes.byteLength.toString(16)}-${(hash >>> 0).toString(16)}\"`;\n}\n\nfunction throwIfAborted(signal: AbortSignal): void {\n if (signal.aborted) {\n throw signal.reason instanceof Error\n ? signal.reason\n : new DOMException(\"The image request was aborted\", \"AbortError\");\n }\n}\n\nfunction isAbortError(error: unknown): boolean {\n return error instanceof Error && error.name === \"AbortError\";\n}\n\nclass FarmImageMemoryCache {\n private readonly entries = new Map<string, OptimizedImage>();\n\n constructor(private readonly capacity: number) {}\n\n get(key: string): OptimizedImage | undefined {\n const value = this.entries.get(key);\n if (!value) return undefined;\n if (value.expiresAt <= Date.now()) {\n this.entries.delete(key);\n return undefined;\n }\n this.entries.delete(key);\n this.entries.set(key, value);\n return value;\n }\n\n set(key: string, value: OptimizedImage): void {\n if (this.capacity <= 0) return;\n this.entries.delete(key);\n this.entries.set(key, value);\n while (this.entries.size > this.capacity) {\n const oldestKey = this.entries.keys().next().value as string | undefined;\n if (oldestKey === undefined) break;\n this.entries.delete(oldestKey);\n }\n }\n}\n"],"mappings":"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","names":["address"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["../src/image.tsx","../src/image-config.ts"],"sourcesContent":["import { forwardRef, type CSSProperties, type ImgHTMLAttributes, type SyntheticEvent } from \"react\";\nimport * as ReactDOM from \"react-dom\";\nimport {\n DEFAULT_FARM_IMAGE_DEVICE_SIZES,\n DEFAULT_FARM_IMAGE_FORMATS,\n DEFAULT_FARM_IMAGE_PATH,\n DEFAULT_FARM_IMAGE_QUALITIES,\n DEFAULT_FARM_IMAGE_SIZES,\n type PublicFarmImageConfig,\n} from \"./image-config\";\n\ndeclare const __FARM_IMAGE_CONFIG__: PublicFarmImageConfig | undefined;\n\nexport interface StaticImageData {\n src: string;\n width: number;\n height: number;\n blurDataURL?: string;\n}\n\nexport interface FarmImageLoaderProps {\n src: string;\n width: number;\n quality: number;\n}\n\nexport type FarmImageLoader = (props: FarmImageLoaderProps) => string;\n\nexport interface ImageProps extends Omit<\n ImgHTMLAttributes<HTMLImageElement>,\n \"src\" | \"srcSet\" | \"width\" | \"height\" | \"loading\" | \"placeholder\"\n> {\n src: string | StaticImageData;\n alt: string;\n width?: number | `${number}`;\n height?: number | `${number}`;\n fill?: boolean;\n quality?: number;\n sizes?: string;\n loading?: \"eager\" | \"lazy\";\n preload?: boolean;\n placeholder?: \"empty\" | \"blur\";\n blurDataURL?: string;\n unoptimized?: boolean;\n loader?: FarmImageLoader;\n onLoad?: (event: SyntheticEvent<HTMLImageElement>) => void;\n}\n\nexport interface ImagePropsResult {\n props: ImgHTMLAttributes<HTMLImageElement>;\n}\n\nconst FALLBACK_CONFIG: PublicFarmImageConfig = {\n provider: \"auto\",\n path: DEFAULT_FARM_IMAGE_PATH,\n deviceSizes: DEFAULT_FARM_IMAGE_DEVICE_SIZES,\n imageSizes: DEFAULT_FARM_IMAGE_SIZES,\n qualities: DEFAULT_FARM_IMAGE_QUALITIES,\n formats: DEFAULT_FARM_IMAGE_FORMATS,\n};\n\nfunction getImageConfig(): PublicFarmImageConfig {\n return typeof __FARM_IMAGE_CONFIG__ !== \"undefined\" && __FARM_IMAGE_CONFIG__\n ? __FARM_IMAGE_CONFIG__\n : FALLBACK_CONFIG;\n}\n\nfunction defaultImageLoader(config: PublicFarmImageConfig): FarmImageLoader {\n return ({ src, width, quality }) => {\n const query = new URLSearchParams({\n url: src,\n w: String(width),\n q: String(quality),\n });\n return `${config.path}?${query}`;\n };\n}\n\nfunction getClosestQuality(quality: number | undefined, allowed: readonly number[]): number {\n const requested = quality ?? 75;\n return allowed.reduce((closest, candidate) =>\n Math.abs(candidate - requested) < Math.abs(closest - requested) ? candidate : closest,\n );\n}\n\nfunction getCandidateWidths(\n config: PublicFarmImageConfig,\n width: number | undefined,\n sizes: string | undefined,\n fill: boolean,\n): { widths: number[]; descriptor: \"w\" | \"x\" } {\n const allSizes = [...new Set([...config.imageSizes, ...config.deviceSizes])].sort(\n (left, right) => left - right,\n );\n\n if (fill || sizes) {\n const viewportPercentages = [...(sizes?.matchAll(/(^|\\s)(\\d{1,3})vw/g) ?? [])].map((match) =>\n Number(match[2]),\n );\n if (viewportPercentages.length > 0) {\n const smallestRatio = Math.min(...viewportPercentages) / 100;\n const minimum = config.deviceSizes[0] * smallestRatio;\n return { widths: allSizes.filter((candidate) => candidate >= minimum), descriptor: \"w\" };\n }\n return { widths: allSizes, descriptor: \"w\" };\n }\n\n if (!width) return { widths: config.deviceSizes.slice(), descriptor: \"w\" };\n\n const requested = [width, width * 2];\n const widths = requested.map(\n (target) => allSizes.find((candidate) => candidate >= target) ?? allSizes.at(-1)!,\n );\n return { widths: [...new Set(widths)], descriptor: \"x\" };\n}\n\nfunction isStaticImageData(value: ImageProps[\"src\"]): value is StaticImageData {\n return typeof value === \"object\" && value !== null && typeof value.src === \"string\";\n}\n\nfunction shouldSkipOptimization(src: string, config: PublicFarmImageConfig): boolean {\n return (\n config.provider === \"none\" ||\n src.startsWith(\"data:\") ||\n src.startsWith(\"blob:\") ||\n /\\.svg(?:$|[?#])/i.test(src)\n );\n}\n\nexport function getImageProps(input: ImageProps): ImagePropsResult {\n const {\n src: srcInput,\n alt,\n width: widthInput,\n height: heightInput,\n fill = false,\n quality: qualityInput,\n sizes: sizesInput,\n loading,\n preload = false,\n placeholder = \"empty\",\n blurDataURL: blurDataURLInput,\n unoptimized: unoptimizedInput = false,\n loader: loaderInput,\n style,\n ...rest\n } = input;\n const config = getImageConfig();\n const staticImage = isStaticImageData(srcInput) ? srcInput : undefined;\n const src = typeof srcInput === \"string\" ? srcInput : srcInput.src;\n const width = Number(widthInput ?? staticImage?.width) || undefined;\n const height = Number(heightInput ?? staticImage?.height) || undefined;\n const blurDataURL = blurDataURLInput ?? staticImage?.blurDataURL;\n\n if (!fill && (!width || !height)) {\n throw new TypeError(\n \"Farm Image requires width and height unless src is a static import or fill is enabled\",\n );\n }\n if (fill && (widthInput !== undefined || heightInput !== undefined)) {\n throw new TypeError(\"Farm Image cannot use width or height together with fill\");\n }\n if (placeholder === \"blur\" && !blurDataURL) {\n throw new TypeError(\"Farm Image placeholder=blur requires a static import or blurDataURL\");\n }\n\n const unoptimized = unoptimizedInput || shouldSkipOptimization(src, config);\n const quality = getClosestQuality(qualityInput, config.qualities);\n const loader = loaderInput ?? defaultImageLoader(config);\n const sizes = sizesInput ?? (fill ? \"100vw\" : undefined);\n const candidates = getCandidateWidths(config, width, sizes, fill);\n const srcSet = unoptimized\n ? undefined\n : candidates.widths\n .map((candidate, index) => {\n const descriptor = candidates.descriptor === \"w\" ? `${candidate}w` : `${index + 1}x`;\n return `${loader({ src, width: candidate, quality })} ${descriptor}`;\n })\n .join(\", \");\n const renderedSrc = unoptimized\n ? src\n : loader({ src, width: candidates.widths.at(-1) ?? width ?? config.deviceSizes[0], quality });\n const fillStyle: CSSProperties | undefined = fill\n ? { position: \"absolute\", inset: 0, width: \"100%\", height: \"100%\" }\n : undefined;\n const placeholderStyle: CSSProperties | undefined =\n placeholder === \"blur\"\n ? {\n backgroundImage: `url(\"${blurDataURL}\")`,\n backgroundPosition: \"50% 50%\",\n backgroundRepeat: \"no-repeat\",\n backgroundSize: \"cover\",\n }\n : undefined;\n\n return {\n props: {\n ...rest,\n alt,\n src: renderedSrc,\n srcSet,\n sizes,\n width: fill ? undefined : width,\n height: fill ? undefined : height,\n loading: preload ? \"eager\" : (loading ?? \"lazy\"),\n fetchPriority: preload ? \"high\" : rest.fetchPriority,\n decoding: rest.decoding ?? \"async\",\n style: { ...fillStyle, ...placeholderStyle, ...style },\n },\n };\n}\n\nexport const Image = forwardRef<HTMLImageElement, ImageProps>(function Image(input, ref) {\n const { props } = getImageProps(input);\n\n if (input.preload && typeof (ReactDOM as any).preload === \"function\") {\n (ReactDOM as any).preload(props.src as string, {\n as: \"image\",\n imageSrcSet: props.srcSet,\n imageSizes: props.sizes,\n fetchPriority: \"high\",\n });\n }\n\n return <img {...props} ref={ref} />;\n});\n\nImage.displayName = \"Image\";\n\nexport default Image;\n\nexport type {\n FarmImageConfig,\n FarmImageFormat,\n FarmImageLocalPattern,\n FarmImageProvider,\n FarmImageRemotePattern,\n ResolvedFarmImageConfig,\n} from \"./image-config\";\n","export const DEFAULT_FARM_IMAGE_PATH = \"/_farm/image\";\nexport const DEFAULT_FARM_IMAGE_DEVICE_SIZES = [\n 640, 750, 828, 1080, 1200, 1920, 2048, 3840,\n] as const;\nexport const DEFAULT_FARM_IMAGE_SIZES = [16, 32, 48, 64, 96, 128, 256, 384] as const;\nexport const DEFAULT_FARM_IMAGE_QUALITIES = [75] as const;\nexport const DEFAULT_FARM_IMAGE_FORMATS = [\"image/webp\"] as const;\n\nexport type FarmImageFormat = \"image/avif\" | \"image/webp\";\nexport type FarmImageProvider = \"auto\" | \"node\" | \"cloudflare\" | \"none\";\n\nexport interface FarmImageRemotePattern {\n protocol?: \"http\" | \"https\";\n hostname: string;\n port?: string;\n pathname?: string;\n search?: string;\n}\n\nexport interface FarmImageLocalPattern {\n pathname: string;\n search?: string;\n}\n\nexport interface FarmImageConfig {\n /** Runtime optimizer. Auto selects Cloudflare Images on Cloudflare and Sharp elsewhere. */\n provider?: FarmImageProvider;\n /** Public optimizer endpoint. */\n path?: string;\n /** @deprecated Prefer remotePatterns, which also restricts protocol, path, port, and query. */\n domains?: readonly string[];\n remotePatterns?: readonly FarmImageRemotePattern[];\n localPatterns?: readonly FarmImageLocalPattern[];\n deviceSizes?: readonly number[];\n imageSizes?: readonly number[];\n qualities?: readonly number[];\n formats?: readonly FarmImageFormat[];\n minimumCacheTTL?: number;\n maximumResponseBody?: number | string;\n maximumRedirects?: number;\n dangerouslyAllowSVG?: boolean;\n dangerouslyAllowLocalIP?: boolean;\n}\n\nexport interface ResolvedFarmImageConfig {\n provider: FarmImageProvider;\n path: string;\n domains: readonly string[];\n remotePatterns: readonly FarmImageRemotePattern[];\n localPatterns: readonly FarmImageLocalPattern[];\n deviceSizes: readonly number[];\n imageSizes: readonly number[];\n qualities: readonly number[];\n formats: readonly FarmImageFormat[];\n minimumCacheTTL: number;\n maximumResponseBody: number;\n maximumRedirects: number;\n dangerouslyAllowSVG: boolean;\n dangerouslyAllowLocalIP: boolean;\n}\n\nexport type PublicFarmImageConfig = Pick<\n ResolvedFarmImageConfig,\n \"provider\" | \"path\" | \"deviceSizes\" | \"imageSizes\" | \"qualities\" | \"formats\"\n>;\n\nconst SIZE_UNITS: Record<string, number> = {\n b: 1,\n kb: 1_000,\n mb: 1_000_000,\n gb: 1_000_000_000,\n kib: 1_024,\n mib: 1_048_576,\n gib: 1_073_741_824,\n};\n\nexport function resolveFarmImageConfig(\n config: FarmImageConfig | undefined,\n): ResolvedFarmImageConfig {\n const path = normalizeImagePath(config?.path ?? DEFAULT_FARM_IMAGE_PATH);\n const deviceSizes = normalizeIntegerList(\n config?.deviceSizes ?? DEFAULT_FARM_IMAGE_DEVICE_SIZES,\n \"images.deviceSizes\",\n );\n const imageSizes = normalizeIntegerList(\n config?.imageSizes ?? DEFAULT_FARM_IMAGE_SIZES,\n \"images.imageSizes\",\n );\n const qualities = normalizeIntegerList(\n config?.qualities ?? DEFAULT_FARM_IMAGE_QUALITIES,\n \"images.qualities\",\n 100,\n );\n const formats = [...new Set(config?.formats ?? DEFAULT_FARM_IMAGE_FORMATS)];\n\n if (formats.some((format) => format !== \"image/avif\" && format !== \"image/webp\")) {\n throw new TypeError('images.formats only supports \"image/avif\" and \"image/webp\"');\n }\n\n const minimumCacheTTL = normalizeNonNegativeInteger(\n config?.minimumCacheTTL ?? 60,\n \"images.minimumCacheTTL\",\n );\n const maximumRedirects = normalizeNonNegativeInteger(\n config?.maximumRedirects ?? 3,\n \"images.maximumRedirects\",\n );\n\n return Object.freeze({\n provider: config?.provider ?? \"auto\",\n path,\n domains: Object.freeze([\n ...new Set(\n (config?.domains ?? []).map((domain) => domain.trim().toLowerCase()).filter(Boolean),\n ),\n ]),\n remotePatterns: Object.freeze(\n (config?.remotePatterns ?? []).map((pattern) => normalizeRemotePattern(pattern)),\n ),\n localPatterns: Object.freeze(\n (config?.localPatterns ?? [{ pathname: \"/**\" }]).map((pattern) =>\n normalizeLocalPattern(pattern),\n ),\n ),\n deviceSizes: Object.freeze(deviceSizes),\n imageSizes: Object.freeze(imageSizes),\n qualities: Object.freeze(qualities),\n formats: Object.freeze(formats),\n minimumCacheTTL,\n maximumResponseBody: parseSize(\n config?.maximumResponseBody ?? \"10mb\",\n \"images.maximumResponseBody\",\n ),\n maximumRedirects,\n dangerouslyAllowSVG: config?.dangerouslyAllowSVG ?? false,\n dangerouslyAllowLocalIP: config?.dangerouslyAllowLocalIP ?? false,\n });\n}\n\nexport function getPublicFarmImageConfig(\n config: Pick<ResolvedFarmImageConfig, keyof PublicFarmImageConfig>,\n): PublicFarmImageConfig {\n return {\n provider: config.provider,\n path: config.path,\n deviceSizes: config.deviceSizes,\n imageSizes: config.imageSizes,\n qualities: config.qualities,\n formats: config.formats,\n };\n}\n\nfunction normalizeImagePath(value: string): string {\n const hasUnstableCharacters = (candidate: string) =>\n candidate.includes(\"\\\\\") ||\n Array.from(candidate).some((character) => {\n const code = character.charCodeAt(0);\n return code <= 31 || (code >= 127 && code <= 159);\n });\n\n if (hasUnstableCharacters(value)) {\n throw new TypeError(\"images.path cannot contain backslashes or control characters\");\n }\n\n const path = value.trim().replace(/\\/+$/, \"\") || \"/\";\n if (!path.startsWith(\"/\") || path.startsWith(\"//\") || path.includes(\"?\") || path.includes(\"#\")) {\n throw new TypeError(\"images.path must be an absolute pathname without a query or hash\");\n }\n\n for (const segment of path.split(\"/\")) {\n let decoded = segment;\n try {\n decoded = decodeURIComponent(segment);\n } catch {\n // Malformed escapes remain literal and cannot conceal a path separator\n // or dot segment.\n }\n if (hasUnstableCharacters(decoded)) {\n throw new TypeError(\"images.path cannot contain backslashes or control characters\");\n }\n if (decoded.includes(\"/\")) {\n throw new TypeError(\"images.path cannot contain percent-encoded path separators\");\n }\n if (decoded === \".\" || decoded === \"..\") {\n throw new TypeError('images.path cannot contain \".\" or \"..\" path segments');\n }\n }\n\n return path;\n}\n\nfunction normalizeIntegerList(values: readonly number[], name: string, max?: number): number[] {\n if (values.length === 0) {\n throw new TypeError(`${name} must contain at least one value`);\n }\n\n const normalized = [...new Set(values)].sort((left, right) => left - right);\n for (const value of normalized) {\n if (!Number.isSafeInteger(value) || value <= 0 || (max !== undefined && value > max)) {\n throw new TypeError(\n `${name} must contain positive integers${max ? ` no greater than ${max}` : \"\"}`,\n );\n }\n }\n return normalized;\n}\n\nfunction normalizeNonNegativeInteger(value: number, name: string): number {\n if (!Number.isSafeInteger(value) || value < 0) {\n throw new TypeError(`${name} must be a non-negative safe integer`);\n }\n return value;\n}\n\nfunction parseSize(value: number | string, name: string): number {\n if (typeof value === \"number\") {\n if (!Number.isSafeInteger(value) || value <= 0) {\n throw new TypeError(`${name} must be a positive safe integer`);\n }\n return value;\n }\n\n const match = value\n .trim()\n .toLowerCase()\n .match(/^(\\d+(?:\\.\\d+)?)\\s*(b|kb|mb|gb|kib|mib|gib)$/);\n if (!match) {\n throw new TypeError(`${name} must be bytes or a size string such as \"5mb\"`);\n }\n\n const bytes = Number(match[1]) * SIZE_UNITS[match[2]];\n if (!Number.isSafeInteger(bytes) || bytes <= 0) {\n throw new TypeError(`${name} must resolve to a positive safe integer`);\n }\n return bytes;\n}\n\nfunction normalizeRemotePattern(pattern: FarmImageRemotePattern): FarmImageRemotePattern {\n const hostname = pattern.hostname.trim().toLowerCase();\n if (!hostname || hostname.includes(\"/\") || hostname.includes(\":\")) {\n throw new TypeError(\"images.remotePatterns[].hostname must be a hostname or wildcard hostname\");\n }\n if (pattern.pathname && !pattern.pathname.startsWith(\"/\")) {\n throw new TypeError(\"images.remotePatterns[].pathname must start with /\");\n }\n if (pattern.search && !pattern.search.startsWith(\"?\")) {\n throw new TypeError(\"images.remotePatterns[].search must start with ?\");\n }\n return Object.freeze({ ...pattern, hostname });\n}\n\nfunction normalizeLocalPattern(pattern: FarmImageLocalPattern): FarmImageLocalPattern {\n if (!pattern.pathname.startsWith(\"/\")) {\n throw new TypeError(\"images.localPatterns[].pathname must start with /\");\n }\n if (pattern.search && !pattern.search.startsWith(\"?\")) {\n throw new TypeError(\"images.localPatterns[].search must start with ?\");\n }\n return Object.freeze({ ...pattern });\n}\n"],"mappings":"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","names":["preload","Image"]}