@wular/pnext 0.0.4 → 0.0.7

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 (395) hide show
  1. package/README.md +22 -16
  2. package/bin/pnext +6 -1
  3. package/config/lint/base.js +7 -7
  4. package/config/ts/base.json +2 -4
  5. package/config/ts/react.json +2 -6
  6. package/package.json +23 -2
  7. package/reference/compat.md +87 -22
  8. package/reference/config.md +40 -93
  9. package/reference/css.md +42 -33
  10. package/reference/dev.md +26 -33
  11. package/reference/env.md +18 -20
  12. package/reference/getting-started.md +132 -0
  13. package/reference/metadata.md +31 -46
  14. package/reference/navigation.md +65 -105
  15. package/reference/performance.md +21 -142
  16. package/reference/rendering.md +78 -75
  17. package/reference/routing.md +97 -107
  18. package/reference/typegen.md +27 -29
  19. package/src/api/cache.ts +36 -37
  20. package/src/api/client-cache.ts +2 -2
  21. package/src/api/client-navigation.ts +121 -114
  22. package/src/api/dynamic.tsx +58 -55
  23. package/src/api/link.tsx +52 -53
  24. package/src/api/navigation.ts +59 -58
  25. package/src/api/server.ts +152 -144
  26. package/src/api/suspense.ts +4 -4
  27. package/src/cli/adapters/vercel-warm.ts +126 -121
  28. package/src/cli/adapters/vercel.ts +437 -443
  29. package/src/cli/analyze.ts +333 -144
  30. package/src/cli/{named-bin.ts → boot/named-bin.ts} +37 -37
  31. package/src/cli/{boot-trace.ts → boot/trace.ts} +10 -10
  32. package/src/cli/build.ts +976 -978
  33. package/src/cli/create.ts +72 -64
  34. package/src/cli/dev.ts +116 -116
  35. package/src/cli/index.ts +94 -85
  36. package/src/cli/migrate/package-json.ts +73 -71
  37. package/src/cli/migrate/report.ts +32 -33
  38. package/src/cli/migrate/{index.ts → run.ts} +48 -49
  39. package/src/cli/migrate/scan.ts +46 -46
  40. package/src/cli/migrate/spinner.ts +9 -9
  41. package/src/cli/migrate/tsconfig.ts +35 -35
  42. package/src/cli/{server-entry.ts → serve/entry.ts} +57 -57
  43. package/src/cli/{request-pipeline.ts → serve/pipeline.ts} +386 -394
  44. package/src/cli/{serve-ui.ts → serve/ui.ts} +47 -47
  45. package/src/cli/start.ts +65 -66
  46. package/src/{typegen.ts → cli/typegen.ts} +59 -59
  47. package/src/client/build.ts +802 -768
  48. package/src/client/chunk-fold.ts +245 -240
  49. package/src/client/{paths.ts → chunk-name.ts} +6 -6
  50. package/src/client/entry.ts +162 -147
  51. package/src/client/prebuilt.ts +231 -223
  52. package/src/client/profile.ts +29 -29
  53. package/src/client/react-compiler.ts +20 -15
  54. package/src/client/{compat-surface.ts → react-tier.ts} +64 -64
  55. package/src/client/reference-stub.ts +51 -51
  56. package/src/client/reference.ts +19 -19
  57. package/src/{api → client}/router/events.ts +32 -17
  58. package/src/{api → client}/router/history.ts +16 -16
  59. package/src/{api → client}/router/hub.ts +52 -53
  60. package/src/{api/router.ts → client/router/index.ts} +61 -60
  61. package/src/{api → client}/router/policies.ts +24 -24
  62. package/src/{api → client}/router/runtime.ts +2022 -1995
  63. package/src/{api → client}/router/types.ts +98 -98
  64. package/src/compat/actions/client-plugin.ts +42 -43
  65. package/src/compat/actions/client-stub.ts +14 -14
  66. package/src/compat/actions/{action-client.ts → client.ts} +194 -193
  67. package/src/compat/actions/config.ts +63 -64
  68. package/src/compat/actions/detect.ts +68 -68
  69. package/src/compat/actions/discovery.ts +105 -105
  70. package/src/compat/actions/{action-dispatch.ts → dispatch.ts} +277 -274
  71. package/src/compat/actions/early-submit.ts +1 -1
  72. package/src/compat/actions/endpoint.ts +198 -198
  73. package/src/compat/actions/extensions.ts +811 -0
  74. package/src/compat/actions/flight.ts +23 -23
  75. package/src/compat/actions/form-state.ts +34 -34
  76. package/src/compat/actions/hoist.ts +382 -236
  77. package/src/compat/actions/ids.ts +7 -12
  78. package/src/compat/actions/index.ts +8 -8
  79. package/src/compat/actions/instances.ts +46 -46
  80. package/src/compat/actions/origin.ts +47 -48
  81. package/src/compat/actions/protocol.ts +22 -22
  82. package/src/compat/actions/registry.ts +19 -19
  83. package/src/compat/{misc/action-return.ts → actions/return.ts} +60 -57
  84. package/src/compat/actions/rewrite.ts +125 -125
  85. package/src/compat/actions/{action-router.ts → router.ts} +10 -10
  86. package/src/compat/actions/serve.ts +132 -136
  87. package/src/compat/actions/server-tag.ts +4 -4
  88. package/src/compat/actions/{action-shared.ts → shared.ts} +34 -34
  89. package/src/compat/actions/unrecognized-error.ts +4 -4
  90. package/src/compat/{index.ts → aliases.ts} +112 -109
  91. package/src/compat/bundler/bun-externals.ts +18 -18
  92. package/src/compat/bundler/cjs-exports.ts +271 -223
  93. package/src/compat/bundler/config.ts +116 -111
  94. package/src/compat/bundler/externals.ts +12 -12
  95. package/src/compat/bundler/import-meta-url.ts +19 -19
  96. package/src/compat/bundler/modularize-imports.ts +36 -33
  97. package/src/compat/bundler/new-url-asset.ts +22 -24
  98. package/src/compat/bundler/optimize-package-imports.ts +111 -107
  99. package/src/compat/bundler/polyfill.ts +28 -28
  100. package/src/compat/bundler/react-compiler.ts +35 -29
  101. package/src/compat/bundler/react-profiler.tsx +11 -11
  102. package/src/compat/bundler/relay-transform.ts +48 -47
  103. package/src/compat/bundler/require-context.ts +119 -113
  104. package/src/compat/bundler/resolve-extensions.ts +19 -19
  105. package/src/compat/bundler/source-cache.ts +25 -25
  106. package/src/compat/bundler/static-imports.ts +7 -7
  107. package/src/compat/bundler/symlink-imports.ts +46 -46
  108. package/src/compat/bundler/tsconfig-paths.ts +13 -15
  109. package/src/compat/bundler/wasm.ts +58 -60
  110. package/src/compat/bundler/webpack-loaders.ts +254 -241
  111. package/src/compat/bundler/worker.ts +101 -104
  112. package/src/compat/cache/build-flags.ts +29 -29
  113. package/src/compat/cache/build-prerender-errors.ts +40 -44
  114. package/src/compat/cache/custom-handler.ts +60 -53
  115. package/src/compat/cache/fetch-patch.ts +240 -240
  116. package/src/compat/cache/handler.ts +27 -31
  117. package/src/compat/cache/modern-handler.ts +148 -126
  118. package/src/compat/cache/resume-data-cache.ts +47 -45
  119. package/src/compat/cache/revalidate.ts +233 -232
  120. package/src/compat/cache/runtime-error.ts +35 -35
  121. package/src/compat/cache/use-cache-transform.ts +442 -417
  122. package/src/compat/cache/use-cache.ts +650 -614
  123. package/src/compat/cache-control.ts +140 -142
  124. package/src/compat/client/base-path.ts +21 -20
  125. package/src/compat/client/css-order.ts +18 -18
  126. package/src/compat/client/errors/bare-boundary.ts +11 -11
  127. package/src/compat/client/errors/control-flow.ts +23 -23
  128. package/src/compat/client/errors/error-boundary.ts +61 -61
  129. package/src/compat/client/errors/global-error.ts +101 -91
  130. package/src/compat/client/errors/install.ts +71 -72
  131. package/src/compat/client/errors/lazy.ts +23 -23
  132. package/src/compat/client/errors/primitive-throw.ts +47 -45
  133. package/src/compat/client/errors/soft-refresh.ts +4 -4
  134. package/src/compat/client/link-status.ts +33 -33
  135. package/src/compat/client/{nav-compat-runtime.ts → nav-runtime.ts} +20 -20
  136. package/src/compat/client/{nav-compat.ts → nav.ts} +12 -13
  137. package/src/compat/client/navigation-scroll.ts +63 -63
  138. package/src/compat/client/optimistic-routing.ts +93 -88
  139. package/src/compat/client/prefetch-cache.ts +23 -24
  140. package/src/compat/client/route-announcer.ts +35 -35
  141. package/src/compat/client/segment-cache-policy.ts +20 -20
  142. package/src/compat/client/segment-cache.ts +309 -315
  143. package/src/compat/client/segment-prefetch.ts +127 -132
  144. package/src/compat/client/trailing-slash.ts +5 -4
  145. package/src/compat/css/chunking.ts +113 -116
  146. package/src/compat/css/inline-css.ts +21 -21
  147. package/src/compat/css/lightningcss.ts +37 -38
  148. package/src/compat/css/modules.ts +161 -175
  149. package/src/compat/css/nonce.ts +7 -7
  150. package/src/compat/css/sass-plugin.ts +18 -21
  151. package/src/compat/css/sass.ts +150 -152
  152. package/src/compat/css/styled-jsx-runtime.ts +27 -27
  153. package/src/compat/css/styled-jsx.ts +21 -21
  154. package/src/compat/edge-runtime.ts +27 -27
  155. package/src/compat/{adapter → export}/build-complete.ts +72 -75
  156. package/src/compat/export/client.ts +29 -31
  157. package/src/compat/export/{index.ts → emit.ts} +111 -110
  158. package/src/compat/export/standalone.ts +62 -54
  159. package/src/compat/image-optimizer/cache.ts +51 -49
  160. package/src/compat/image-optimizer/detect.ts +52 -52
  161. package/src/compat/image-optimizer/{index.ts → optimize.ts} +189 -194
  162. package/src/compat/image-optimizer/source.ts +77 -80
  163. package/src/compat/lifecycle/after-scope.ts +26 -26
  164. package/src/compat/lifecycle/after.ts +48 -45
  165. package/src/compat/lifecycle/error-funnel.ts +98 -102
  166. package/src/compat/lifecycle/error-serialize.ts +30 -26
  167. package/src/compat/lifecycle/error-ui.ts +67 -32
  168. package/src/compat/lifecycle/instrumentation-client.ts +36 -38
  169. package/src/compat/lifecycle/instrumentation.ts +85 -85
  170. package/src/compat/lifecycle/node-console.ts +11 -11
  171. package/src/compat/lifecycle/testmode.ts +160 -132
  172. package/src/compat/mdx/compile.ts +60 -57
  173. package/src/compat/mdx/plugin.ts +11 -11
  174. package/src/compat/mdx/{next-mdx-stub.ts → stub.ts} +5 -5
  175. package/src/compat/{metadata-route-artifacts.ts → metadata-artifacts.ts} +195 -191
  176. package/src/compat/metadata.ts +75 -81
  177. package/src/compat/next/cache.ts +68 -69
  178. package/src/compat/next/canonical-url.ts +9 -9
  179. package/src/compat/next/client-cache.ts +19 -19
  180. package/src/compat/next/client-navigation.ts +112 -116
  181. package/src/compat/next/client-only.ts +1 -1
  182. package/src/compat/next/client-script.tsx +99 -93
  183. package/src/compat/next/client-server.ts +10 -10
  184. package/src/compat/next/config-loader.ts +176 -173
  185. package/src/compat/next/config.ts +7 -7
  186. package/src/compat/next/constants.cjs +6 -6
  187. package/src/compat/next/constants.ts +6 -6
  188. package/src/compat/next/custom-server.ts +26 -24
  189. package/src/compat/next/dist/client/components/app-router-headers.ts +21 -21
  190. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +3 -4
  191. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -1
  192. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -1
  193. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -1
  194. package/src/compat/next/dynamic.tsx +21 -18
  195. package/src/compat/next/error.tsx +52 -52
  196. package/src/compat/next/font/cache.ts +74 -74
  197. package/src/compat/next/font/google.ts +2 -2
  198. package/src/compat/next/font/index.ts +1 -1
  199. package/src/compat/next/font/local.ts +3 -3
  200. package/src/compat/next/font/runtime-client.ts +17 -15
  201. package/src/compat/next/font/runtime.ts +443 -408
  202. package/src/compat/next/font/shared.ts +120 -108
  203. package/src/compat/next/form.tsx +63 -63
  204. package/src/compat/next/head.tsx +2 -2
  205. package/src/compat/next/headers.ts +103 -95
  206. package/src/compat/next/image/client.tsx +220 -0
  207. package/src/compat/next/image/config.ts +56 -58
  208. package/src/compat/next/image/optimizer.ts +40 -36
  209. package/src/compat/next/image/patterns.ts +37 -40
  210. package/src/compat/next/{image-props.ts → image/props.ts} +208 -202
  211. package/src/compat/next/image/shared.ts +65 -57
  212. package/src/compat/next/image/static-metadata.ts +98 -107
  213. package/src/compat/next/image/validate.ts +79 -88
  214. package/src/compat/next/image.tsx +19 -23
  215. package/src/compat/next/index.ts +1 -1
  216. package/src/compat/next/legacy-image.tsx +59 -60
  217. package/src/compat/next/{link-validation-transform.ts → link-transform.ts} +95 -96
  218. package/src/compat/next/link.tsx +158 -158
  219. package/src/compat/next/navigation.cjs +12 -3
  220. package/src/compat/next/navigation.ts +48 -50
  221. package/src/compat/next/offline.ts +27 -27
  222. package/src/compat/next/og.ts +121 -124
  223. package/src/compat/next/preferred-region.ts +13 -14
  224. package/src/compat/next/redirects.ts +58 -56
  225. package/src/compat/next/resource-hints.ts +73 -76
  226. package/src/compat/next/rewrites.ts +130 -133
  227. package/src/compat/next/root-params.ts +45 -45
  228. package/src/compat/next/{optimistic-route-state.ts → route-state.ts} +52 -52
  229. package/src/compat/next/router.cjs +4 -2
  230. package/src/compat/next/router.ts +58 -61
  231. package/src/compat/next/script.tsx +108 -108
  232. package/src/compat/next/server-only.ts +1 -1
  233. package/src/compat/next/server.ts +19 -19
  234. package/src/compat/next/svgr.ts +18 -17
  235. package/src/compat/next/telemetry.ts +24 -24
  236. package/src/compat/next/{image-usage.ts → usage.ts} +70 -39
  237. package/src/compat/next/user-agent.ts +53 -49
  238. package/src/compat/next/web-vitals.ts +22 -24
  239. package/src/compat/otel/api.ts +41 -41
  240. package/src/compat/otel/client-trace-metadata.ts +25 -27
  241. package/src/compat/otel/fetch-span.ts +29 -29
  242. package/src/compat/otel/tracer.ts +331 -331
  243. package/src/compat/pages/api.ts +456 -0
  244. package/src/compat/pages/client-plugin.ts +36 -36
  245. package/src/compat/pages/router-state.ts +34 -34
  246. package/src/compat/pages/{index.ts → router.ts} +130 -135
  247. package/src/compat/ppr/io.ts +12 -12
  248. package/src/compat/ppr/missing-root-params.ts +34 -38
  249. package/src/compat/ppr/root-params-scan.ts +66 -66
  250. package/src/compat/ppr/root-params-transform.ts +24 -26
  251. package/src/compat/ppr/root-params.ts +30 -30
  252. package/src/compat/ppr/segment-config-incompat.ts +6 -7
  253. package/src/compat/protocol.ts +71 -70
  254. package/src/compat/react/action-state.ts +47 -48
  255. package/src/compat/react/client-lite.ts +15 -15
  256. package/src/compat/react/client.ts +4 -4
  257. package/src/compat/react/compiler-runtime.ts +11 -11
  258. package/src/compat/react/dom-client.ts +44 -44
  259. package/src/compat/react/dom-react-server.ts +10 -16
  260. package/src/compat/react/dom-server.ts +10 -10
  261. package/src/compat/react/dom.ts +52 -52
  262. package/src/compat/react/hooks-extra.ts +34 -35
  263. package/src/compat/react/parity.ts +64 -61
  264. package/src/compat/react/preact.ts +81 -82
  265. package/src/compat/react/react-server.ts +28 -28
  266. package/src/compat/react/router-shim.ts +1 -1
  267. package/src/compat/react/server-component-use.ts +8 -8
  268. package/src/compat/react/server-inserted-html.ts +30 -31
  269. package/src/compat/react/server.ts +53 -55
  270. package/src/compat/react/use.ts +32 -32
  271. package/src/compat/react/view-transition.ts +20 -20
  272. package/src/compat/register/actions.ts +35 -824
  273. package/src/compat/register/boot.ts +41 -41
  274. package/src/compat/register/build-tier.ts +5 -5
  275. package/src/compat/register/build.ts +74 -70
  276. package/src/compat/register/bundler.ts +161 -156
  277. package/src/compat/register/cache.ts +20 -20
  278. package/src/compat/register/client-errors.ts +3 -3
  279. package/src/compat/register/config.ts +6 -6
  280. package/src/compat/register/css-extras.ts +30 -34
  281. package/src/compat/register/edge-runtime.ts +3 -3
  282. package/src/compat/register/errors.ts +10 -12
  283. package/src/compat/register/export.ts +16 -16
  284. package/src/compat/register/font.ts +11 -11
  285. package/src/compat/register/hooks.ts +2 -2
  286. package/src/compat/register/image.ts +40 -40
  287. package/src/compat/register/index.ts +59 -62
  288. package/src/compat/register/instrumentation-client.ts +10 -10
  289. package/src/compat/register/lifecycle.ts +25 -28
  290. package/src/compat/register/mdx.ts +10 -10
  291. package/src/compat/register/middleware.ts +226 -16
  292. package/src/compat/register/otel.ts +80 -88
  293. package/src/compat/register/pages-api.ts +10 -463
  294. package/src/compat/register/ppr.ts +16 -16
  295. package/src/compat/register/protocol.ts +17 -18
  296. package/src/compat/register/proxy.ts +49 -51
  297. package/src/compat/register/render.ts +79 -76
  298. package/src/compat/register/routing.ts +162 -159
  299. package/src/compat/register/segment.ts +24 -1897
  300. package/src/compat/register/static-image.ts +3 -3
  301. package/src/compat/register/{misc.ts → taint.ts} +9 -9
  302. package/src/compat/register/typed-routes.ts +14 -14
  303. package/src/compat/register/{usecache.ts → use-cache.ts} +39 -39
  304. package/src/compat/register/validation.ts +18 -18
  305. package/src/compat/segment/loading-boundary.ts +43 -45
  306. package/src/compat/segment/page-slot.ts +69 -69
  307. package/src/compat/segment/serve.ts +1884 -0
  308. package/src/compat/segment/tree.ts +113 -112
  309. package/src/compat/segment/vary-key.ts +38 -38
  310. package/src/compat/segment/vary-params.ts +138 -142
  311. package/src/compat/static-params.ts +14 -12
  312. package/src/compat/tsconfig-defaults.ts +87 -91
  313. package/src/compat/typecheck/{index.ts → check.ts} +234 -212
  314. package/src/compat/typecheck/worker.ts +15 -12
  315. package/src/compat/typed-routes/{index.ts → generate.ts} +36 -36
  316. package/src/compat/typed-routes/manifest.ts +174 -170
  317. package/src/compat/typed-routes/typegen.ts +127 -110
  318. package/src/compat/validation/errors.ts +16 -19
  319. package/src/compat/validation/prerender-diagnostics.ts +521 -504
  320. package/src/compat/validation/{index.ts → validate.ts} +647 -648
  321. package/src/compat-bootstrap.ts +16 -16
  322. package/src/config.ts +86 -69
  323. package/src/css/build.ts +230 -227
  324. package/src/css/postcss.ts +79 -80
  325. package/src/css/worker.ts +14 -15
  326. package/src/dev/client-actions.ts +10 -10
  327. package/src/dev/client-chunk-store.ts +27 -27
  328. package/src/dev/{client-key-cache.ts → restart/client-key.ts} +76 -76
  329. package/src/dev/{restart-cache.ts → restart/enabled.ts} +1 -1
  330. package/src/dev/{global-css-cache.ts → restart/global-css.ts} +91 -83
  331. package/src/dev/{node-module-bundle-cache.ts → restart/node-modules.ts} +24 -24
  332. package/src/dev/{route-bundle-key-cache.ts → restart/route-bundle-key.ts} +53 -53
  333. package/src/dev/{route-facts-cache.ts → restart/route-facts.ts} +82 -82
  334. package/src/dev/server.ts +804 -820
  335. package/src/env.ts +46 -43
  336. package/src/extensions.ts +491 -478
  337. package/src/index.ts +8 -8
  338. package/src/internal.ts +20 -23
  339. package/src/{islands → render}/boundary-error.ts +3 -3
  340. package/src/render/hooks.ts +71 -71
  341. package/src/render/island-context.ts +14 -14
  342. package/src/render/metadata.ts +310 -310
  343. package/src/{ppr-postpone.ts → render/postpone.ts} +5 -5
  344. package/src/{ppr.ts → render/ppr.ts} +244 -245
  345. package/src/render/renderer.ts +2087 -2082
  346. package/src/render/resource-hints.ts +16 -17
  347. package/src/render/slots.tsx +224 -235
  348. package/src/{islands → render}/static-children.ts +9 -12
  349. package/src/{islands → render}/static-slots.ts +37 -37
  350. package/src/{cache/context.ts → request/cache.ts} +20 -20
  351. package/src/request/context.ts +107 -107
  352. package/src/{dynamic/source.ts → resolve/dynamic.ts} +139 -139
  353. package/src/resolve/engine.ts +90 -77
  354. package/src/resolve/imports.ts +475 -463
  355. package/src/resolve/scan-facts.ts +318 -296
  356. package/src/resolve/source-text.ts +37 -37
  357. package/src/{dynamic → resolve}/tree-shake.ts +132 -128
  358. package/src/routing/forwarded.ts +19 -19
  359. package/src/routing/handler.ts +84 -91
  360. package/src/routing/href.ts +87 -70
  361. package/src/routing/{metadata.ts → metadata-files.ts} +403 -401
  362. package/src/{proxy.ts → routing/proxy.ts} +306 -312
  363. package/src/routing/{request-runtime.ts → request-environment.ts} +10 -10
  364. package/src/routing/routes.ts +827 -815
  365. package/src/routing/slots.ts +164 -160
  366. package/src/runtime/loader.ts +954 -0
  367. package/src/{dev → runtime}/module-cache.ts +309 -287
  368. package/src/{dev → runtime}/module-generations.ts +9 -9
  369. package/src/{dev → runtime}/module-transform.ts +81 -72
  370. package/src/{dev/imports.ts → runtime/modules.ts} +934 -847
  371. package/src/runtime/{server.ts → vendor-build.ts} +848 -1696
  372. package/src/runtime/vendor.ts +428 -406
  373. package/src/styles.d.ts +9 -0
  374. package/src/types.ts +324 -335
  375. package/src/utils/ansi.ts +5 -5
  376. package/src/utils/{source.ts → code.ts} +15 -12
  377. package/src/utils/content-type.ts +3 -3
  378. package/src/utils/decode.ts +2 -2
  379. package/src/utils/dev-profile.ts +13 -13
  380. package/src/utils/error-log.ts +6 -6
  381. package/src/utils/esbuild.ts +18 -18
  382. package/src/utils/fs-cache.ts +13 -13
  383. package/src/utils/fs.ts +57 -49
  384. package/src/utils/html.ts +20 -24
  385. package/src/utils/native-require.ts +8 -8
  386. package/src/utils/serialize.ts +139 -146
  387. package/src/utils/verbose.ts +18 -18
  388. package/reference/overview.md +0 -35
  389. package/src/cli/analyze-print.ts +0 -181
  390. package/src/compat/middleware/manifest.ts +0 -210
  391. package/src/compat/next/image-client.tsx +0 -215
  392. package/src/compat/next/link-usage.ts +0 -29
  393. package/src/css/index.ts +0 -2
  394. package/src/render/index.ts +0 -1
  395. package/src/style-modules.d.ts +0 -9
@@ -11,12 +11,12 @@
11
11
  // Served from the request interceptor registered in ../register-image.ts.
12
12
  // ---------------------------------------------------------------------------
13
13
 
14
- import path from 'node:path';
15
- import type { ResolvedConfig } from '../../config';
16
- import { getNextConfig } from '../next/config-loader';
17
- import { getImagesConfig, type ResolvedImagesConfig } from '../next/image/config';
18
- import { hasLocalMatch, hasRemoteMatch } from '../next/image/patterns';
19
- import { cacheHandlerGet, cacheHandlerSet, hasCacheHandler } from '../cache/handler';
14
+ import path from 'node:path'
15
+ import type { ResolvedConfig } from '../../config'
16
+ import { getNextConfig } from '../next/config-loader'
17
+ import { getImagesConfig, type ResolvedImagesConfig } from '../next/image/config'
18
+ import { hasLocalMatch, hasRemoteMatch } from '../next/image/patterns'
19
+ import { cacheHandlerGet, cacheHandlerSet, hasCacheHandler } from '../cache/handler'
20
20
  import {
21
21
  ANIMATABLE_TYPES,
22
22
  AVIF,
@@ -28,7 +28,7 @@ import {
28
28
  extensionForType,
29
29
  isAnimated,
30
30
  typeForExtension,
31
- } from './detect';
31
+ } from './detect'
32
32
  import {
33
33
  IMAGE_CACHE_VERSION,
34
34
  getHash,
@@ -36,74 +36,74 @@ import {
36
36
  readCacheEntry,
37
37
  writeCacheEntry,
38
38
  type ImageCacheEntry,
39
- } from './cache';
39
+ } from './cache'
40
40
  import {
41
41
  ImageError,
42
42
  fetchExternalImage,
43
43
  fetchInternalImage,
44
44
  readPublicFile,
45
45
  type ImageUpstream,
46
- } from './source';
46
+ } from './source'
47
47
 
48
- type XCacheHeader = 'MISS' | 'HIT' | 'STALE';
48
+ type XCacheHeader = 'MISS' | 'HIT' | 'STALE'
49
49
 
50
50
  interface ImageParams {
51
- href: string;
52
- isAbsolute: boolean;
53
- isStatic: boolean;
54
- width: number;
55
- quality: number;
56
- mimeType: string;
51
+ href: string
52
+ isAbsolute: boolean
53
+ isStatic: boolean
54
+ width: number
55
+ quality: number
56
+ mimeType: string
57
57
  }
58
58
 
59
59
  interface GeneratedImage {
60
- buffer: Buffer;
61
- contentType: string;
62
- maxAge: number;
63
- etag: string;
64
- upstreamEtag: string;
60
+ buffer: Buffer
61
+ contentType: string
62
+ maxAge: number
63
+ etag: string
64
+ upstreamEtag: string
65
65
  }
66
66
 
67
67
  // -- sharp ------------------------------------------------------------------
68
68
 
69
69
  interface SharpPipeline {
70
- timeout(options: { seconds: number }): SharpPipeline;
71
- rotate(): SharpPipeline;
70
+ timeout(options: { seconds: number }): SharpPipeline
71
+ rotate(): SharpPipeline
72
72
  resize(
73
73
  width: number | null,
74
74
  height?: number | null,
75
75
  options?: { withoutEnlargement?: boolean },
76
- ): SharpPipeline;
77
- webp(options: { quality: number }): SharpPipeline;
78
- avif(options: { quality: number; effort?: number }): SharpPipeline;
79
- png(options: { quality: number }): SharpPipeline;
80
- jpeg(options: { quality: number; mozjpeg?: boolean }): SharpPipeline;
81
- toBuffer(): Promise<Buffer>;
76
+ ): SharpPipeline
77
+ webp(options: { quality: number }): SharpPipeline
78
+ avif(options: { quality: number; effort?: number }): SharpPipeline
79
+ png(options: { quality: number }): SharpPipeline
80
+ jpeg(options: { quality: number; mozjpeg?: boolean }): SharpPipeline
81
+ toBuffer(): Promise<Buffer>
82
82
  }
83
83
 
84
- type SharpModule = (input: Buffer | Uint8Array) => SharpPipeline;
84
+ type SharpModule = (input: Buffer | Uint8Array) => SharpPipeline
85
85
 
86
- let sharpPromise: Promise<SharpModule | undefined> | undefined;
86
+ let sharpPromise: Promise<SharpModule | undefined> | undefined
87
87
  function loadSharp(): Promise<SharpModule | undefined> {
88
88
  if (!sharpPromise) {
89
89
  sharpPromise = import('sharp')
90
90
  .then(m => ((m as { default?: unknown }).default ?? m) as SharpModule)
91
- .catch(() => undefined);
91
+ .catch(() => undefined)
92
92
  }
93
- return sharpPromise;
93
+ return sharpPromise
94
94
  }
95
95
 
96
96
  // -- helpers ----------------------------------------------------------------
97
97
 
98
- const warnedOnce = new Set<string>();
98
+ const warnedOnce = new Set<string>()
99
99
  function warnOnce(message: string): void {
100
- if (warnedOnce.has(message)) return;
101
- warnedOnce.add(message);
102
- console.warn(message);
100
+ if (warnedOnce.has(message)) return
101
+ warnedOnce.add(message)
102
+ console.warn(message)
103
103
  }
104
104
 
105
105
  function badRequest(message: string): Response {
106
- return new Response(message, { status: 400, headers: { 'content-type': 'text/plain' } });
106
+ return new Response(message, { status: 400, headers: { 'content-type': 'text/plain' } })
107
107
  }
108
108
 
109
109
  function errorResponse(error: unknown): Response {
@@ -111,47 +111,47 @@ function errorResponse(error: unknown): Response {
111
111
  return new Response(error.message, {
112
112
  status: error.statusCode,
113
113
  headers: { 'content-type': 'text/plain' },
114
- });
114
+ })
115
115
  }
116
- console.error('image optimizer failed', error);
117
- return new Response('Internal Server Error', { status: 500 });
116
+ console.error('image optimizer failed', error)
117
+ return new Response('Internal Server Error', { status: 500 })
118
118
  }
119
119
 
120
120
  function getMaxAge(cacheControl: string | null | undefined): number {
121
- if (!cacheControl) return 0;
121
+ if (!cacheControl) return 0
122
122
  for (const directive of cacheControl.split(',')) {
123
- const [key = '', rawValue] = directive.trim().split('=', 2);
124
- if (key.toLowerCase() !== 's-maxage' && key.toLowerCase() !== 'max-age') continue;
125
- let value = (rawValue ?? '').toLowerCase();
126
- if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1);
127
- const n = parseInt(value, 10);
128
- if (!isNaN(n)) return n;
123
+ const [key = '', rawValue] = directive.trim().split('=', 2)
124
+ if (key.toLowerCase() !== 's-maxage' && key.toLowerCase() !== 'max-age') continue
125
+ let value = (rawValue ?? '').toLowerCase()
126
+ if (value.startsWith('"') && value.endsWith('"')) value = value.slice(1, -1)
127
+ const n = parseInt(value, 10)
128
+ if (!isNaN(n)) return n
129
129
  }
130
- return 0;
130
+ return 0
131
131
  }
132
132
 
133
133
  /** First configured format the Accept header explicitly names, or '' (keep source format). */
134
134
  function getSupportedMimeType(formats: string[], accept: string): string {
135
135
  for (const format of formats) {
136
- if (accept.includes(format)) return format;
136
+ if (accept.includes(format)) return format
137
137
  }
138
- return '';
138
+ return ''
139
139
  }
140
140
 
141
141
  function getFileNameWithExtension(url: string, contentType: string | null): string {
142
- const [urlWithoutQueryParams = url] = url.split('?', 1);
143
- const fileNameWithExtension = urlWithoutQueryParams.split('/').pop();
144
- if (!contentType || !fileNameWithExtension) return 'image.bin';
145
- const [fileName = 'image'] = fileNameWithExtension.split('.', 1);
146
- const extension = extensionForType(contentType) ?? 'bin';
147
- return `${fileName}.${extension}`;
142
+ const [urlWithoutQueryParams = url] = url.split('?', 1)
143
+ const fileNameWithExtension = urlWithoutQueryParams.split('/').pop()
144
+ if (!contentType || !fileNameWithExtension) return 'image.bin'
145
+ const [fileName = 'image'] = fileNameWithExtension.split('.', 1)
146
+ const extension = extensionForType(contentType) ?? 'bin'
147
+ return `${fileName}.${extension}`
148
148
  }
149
149
 
150
150
  function contentDispositionHeader(type: string, fileName: string): string {
151
151
  if (/^[\x20-\x7e]*$/.test(fileName) && !fileName.includes('"')) {
152
- return `${type}; filename="${fileName}"`;
152
+ return `${type}; filename="${fileName}"`
153
153
  }
154
- return `${type}; filename*=UTF-8''${encodeURIComponent(fileName)}`;
154
+ return `${type}; filename*=UTF-8''${encodeURIComponent(fileName)}`
155
155
  }
156
156
 
157
157
  // -- validation -------------------------------------------------------------
@@ -162,82 +162,82 @@ function validateParams(
162
162
  config: ResolvedImagesConfig,
163
163
  basePath: string,
164
164
  ): ImageParams | { errorMessage: string } {
165
- const url = searchParams.get('url');
166
- const w = searchParams.get('w');
167
- const q = searchParams.get('q');
165
+ const url = searchParams.get('url')
166
+ const w = searchParams.get('w')
167
+ const q = searchParams.get('q')
168
168
 
169
- if (!url) return { errorMessage: '"url" parameter is required' };
170
- if (url.length > 3072) return { errorMessage: '"url" parameter is too long' };
169
+ if (!url) return { errorMessage: '"url" parameter is required' }
170
+ if (url.length > 3072) return { errorMessage: '"url" parameter is too long' }
171
171
  if (url.startsWith('//')) {
172
- return { errorMessage: '"url" parameter cannot be a protocol-relative URL (//)' };
172
+ return { errorMessage: '"url" parameter cannot be a protocol-relative URL (//)' }
173
173
  }
174
174
 
175
- let href: string;
176
- let isAbsolute: boolean;
175
+ let href: string
176
+ let isAbsolute: boolean
177
177
  if (url.startsWith('/')) {
178
- href = url;
179
- isAbsolute = false;
180
- let pathname = url.split('?', 1)[0] ?? url;
178
+ href = url
179
+ isAbsolute = false
180
+ let pathname = url.split('?', 1)[0] ?? url
181
181
  try {
182
- pathname = decodeURIComponent(pathname);
182
+ pathname = decodeURIComponent(pathname)
183
183
  } catch {
184
184
  // keep the raw pathname; the regex below still guards the raw form
185
185
  }
186
186
  if (/\/_next\/image($|\/)/.test(pathname)) {
187
- return { errorMessage: '"url" parameter cannot be recursive' };
187
+ return { errorMessage: '"url" parameter cannot be recursive' }
188
188
  }
189
189
  if (!hasLocalMatch(config.localPatterns, new URL(url, 'http://n'))) {
190
- return { errorMessage: '"url" parameter is not allowed' };
190
+ return { errorMessage: '"url" parameter is not allowed' }
191
191
  }
192
192
  } else {
193
- let parsed: URL;
193
+ let parsed: URL
194
194
  try {
195
- parsed = new URL(url);
195
+ parsed = new URL(url)
196
196
  } catch {
197
- return { errorMessage: '"url" parameter is invalid' };
197
+ return { errorMessage: '"url" parameter is invalid' }
198
198
  }
199
- href = parsed.toString();
200
- isAbsolute = true;
199
+ href = parsed.toString()
200
+ isAbsolute = true
201
201
  if (!['http:', 'https:'].includes(parsed.protocol)) {
202
- return { errorMessage: '"url" parameter is invalid' };
202
+ return { errorMessage: '"url" parameter is invalid' }
203
203
  }
204
204
  if (!hasRemoteMatch(config.domains, config.remotePatterns, parsed)) {
205
- return { errorMessage: '"url" parameter is not allowed' };
205
+ return { errorMessage: '"url" parameter is not allowed' }
206
206
  }
207
207
  }
208
208
 
209
- if (!w) return { errorMessage: '"w" parameter (width) is required' };
209
+ if (!w) return { errorMessage: '"w" parameter (width) is required' }
210
210
  if (!/^[0-9]+$/.test(w)) {
211
- return { errorMessage: '"w" parameter (width) must be an integer greater than 0' };
211
+ return { errorMessage: '"w" parameter (width) must be an integer greater than 0' }
212
212
  }
213
- if (!q) return { errorMessage: '"q" parameter (quality) is required' };
213
+ if (!q) return { errorMessage: '"q" parameter (quality) is required' }
214
214
  if (!/^[0-9]+$/.test(q)) {
215
- return { errorMessage: '"q" parameter (quality) must be an integer between 1 and 100' };
215
+ return { errorMessage: '"q" parameter (quality) must be an integer between 1 and 100' }
216
216
  }
217
217
 
218
- const width = parseInt(w, 10);
218
+ const width = parseInt(w, 10)
219
219
  if (width <= 0 || isNaN(width)) {
220
- return { errorMessage: '"w" parameter (width) must be an integer greater than 0' };
220
+ return { errorMessage: '"w" parameter (width) must be an integer greater than 0' }
221
221
  }
222
- const sizes = [...config.deviceSizes, ...config.imageSizes];
222
+ const sizes = [...config.deviceSizes, ...config.imageSizes]
223
223
  if (!sizes.includes(width)) {
224
- return { errorMessage: `"w" parameter (width) of ${width} is not allowed` };
224
+ return { errorMessage: `"w" parameter (width) of ${width} is not allowed` }
225
225
  }
226
226
 
227
- const quality = parseInt(q, 10);
227
+ const quality = parseInt(q, 10)
228
228
  if (isNaN(quality) || quality < 1 || quality > 100) {
229
- return { errorMessage: '"q" parameter (quality) must be an integer between 1 and 100' };
229
+ return { errorMessage: '"q" parameter (quality) must be an integer between 1 and 100' }
230
230
  }
231
231
  if (config.qualities && !config.qualities.includes(quality)) {
232
- return { errorMessage: `"q" parameter (quality) of ${q} is not allowed` };
232
+ return { errorMessage: `"q" parameter (quality) of ${q} is not allowed` }
233
233
  }
234
234
 
235
- const mimeType = getSupportedMimeType(config.formats, request.headers.get('accept') ?? '');
235
+ const mimeType = getSupportedMimeType(config.formats, request.headers.get('accept') ?? '')
236
236
  const isStatic =
237
237
  url.startsWith(`${basePath}/_next/static/media`) ||
238
- url.startsWith(`${basePath}/_next/static/immutable/media`);
238
+ url.startsWith(`${basePath}/_next/static/immutable/media`)
239
239
 
240
- return { href, isAbsolute, isStatic, width, quality, mimeType };
240
+ return { href, isAbsolute, isStatic, width, quality, mimeType }
241
241
  }
242
242
 
243
243
  // -- optimization -----------------------------------------------------------
@@ -248,22 +248,22 @@ async function optimizeImage(
248
248
  width: number,
249
249
  quality: number,
250
250
  ): Promise<Buffer> {
251
- const sharp = await loadSharp();
252
- if (!sharp) throw new Error('sharp is not available');
251
+ const sharp = await loadSharp()
252
+ if (!sharp) throw new Error('sharp is not available')
253
253
  let transformer = sharp(buffer)
254
254
  .timeout({ seconds: 7 })
255
255
  .rotate()
256
- .resize(width, undefined, { withoutEnlargement: true });
256
+ .resize(width, undefined, { withoutEnlargement: true })
257
257
  if (contentType === AVIF) {
258
- transformer = transformer.avif({ quality: Math.max(quality - 20, 1), effort: 3 });
258
+ transformer = transformer.avif({ quality: Math.max(quality - 20, 1), effort: 3 })
259
259
  } else if (contentType === WEBP) {
260
- transformer = transformer.webp({ quality });
260
+ transformer = transformer.webp({ quality })
261
261
  } else if (contentType === PNG) {
262
- transformer = transformer.png({ quality });
262
+ transformer = transformer.png({ quality })
263
263
  } else if (contentType === JPEG) {
264
- transformer = transformer.jpeg({ quality, mozjpeg: true });
264
+ transformer = transformer.jpeg({ quality, mozjpeg: true })
265
265
  }
266
- return transformer.toBuffer();
266
+ return transformer.toBuffer()
267
267
  }
268
268
 
269
269
  async function generateImage(
@@ -271,33 +271,33 @@ async function generateImage(
271
271
  params: ImageParams,
272
272
  config: ResolvedImagesConfig,
273
273
  ): Promise<GeneratedImage> {
274
- const { href, width, quality, mimeType } = params;
275
- const upstreamBuffer = upstream.buffer;
276
- const upstreamEtag = upstream.etag;
277
- const maxAge = Math.max(config.minimumCacheTTL, getMaxAge(upstream.cacheControl));
274
+ const { href, width, quality, mimeType } = params
275
+ const upstreamBuffer = upstream.buffer
276
+ const upstreamEtag = upstream.etag
277
+ const maxAge = Math.max(config.minimumCacheTTL, getMaxAge(upstream.cacheControl))
278
278
 
279
- const upstreamType = detectContentType(upstreamBuffer);
279
+ const upstreamType = detectContentType(upstreamBuffer)
280
280
  if (!upstreamType || !upstreamType.startsWith('image/') || upstreamType.includes(',')) {
281
- console.error("The requested resource isn't a valid image for", href, 'received', upstreamType);
282
- throw new ImageError(400, "The requested resource isn't a valid image.");
281
+ console.error("The requested resource isn't a valid image for", href, 'received', upstreamType)
282
+ throw new ImageError(400, "The requested resource isn't a valid image.")
283
283
  }
284
284
  if (upstreamType.startsWith('image/svg') && !config.dangerouslyAllowSVG) {
285
285
  console.error(
286
286
  `The requested resource "${href}" has type "${upstreamType}" but dangerouslyAllowSVG is disabled. Consider adding the "unoptimized" property to the <Image>.`,
287
- );
288
- throw new ImageError(400, '"url" parameter is valid but image type is not allowed');
287
+ )
288
+ throw new ImageError(400, '"url" parameter is valid but image type is not allowed')
289
289
  }
290
290
  if (ANIMATABLE_TYPES.includes(upstreamType) && isAnimated(upstreamBuffer)) {
291
291
  warnOnce(
292
292
  `The requested resource "${href}" is an animated image so it will not be optimized. Consider adding the "unoptimized" property to the <Image>.`,
293
- );
293
+ )
294
294
  return {
295
295
  buffer: upstreamBuffer,
296
296
  contentType: upstreamType,
297
297
  maxAge,
298
298
  etag: upstreamEtag,
299
299
  upstreamEtag,
300
- };
300
+ }
301
301
  }
302
302
  if (BYPASS_TYPES.includes(upstreamType)) {
303
303
  return {
@@ -306,31 +306,27 @@ async function generateImage(
306
306
  maxAge,
307
307
  etag: upstreamEtag,
308
308
  upstreamEtag,
309
- };
309
+ }
310
310
  }
311
311
 
312
- let contentType: string;
312
+ let contentType: string
313
313
  if (mimeType) {
314
- contentType = mimeType;
315
- } else if (
316
- extensionForType(upstreamType) &&
317
- upstreamType !== WEBP &&
318
- upstreamType !== AVIF
319
- ) {
320
- contentType = upstreamType;
314
+ contentType = mimeType
315
+ } else if (extensionForType(upstreamType) && upstreamType !== WEBP && upstreamType !== AVIF) {
316
+ contentType = upstreamType
321
317
  } else {
322
- contentType = JPEG;
318
+ contentType = JPEG
323
319
  }
324
320
 
325
321
  try {
326
- const optimizedBuffer = await optimizeImage(upstreamBuffer, contentType, width, quality);
322
+ const optimizedBuffer = await optimizeImage(upstreamBuffer, contentType, width, quality)
327
323
  return {
328
324
  buffer: optimizedBuffer,
329
325
  contentType,
330
326
  maxAge,
331
327
  etag: getHash([optimizedBuffer]),
332
328
  upstreamEtag,
333
- };
329
+ }
334
330
  } catch {
335
331
  // If optimization fails, fall back to the original image bytes.
336
332
  return {
@@ -339,31 +335,31 @@ async function generateImage(
339
335
  maxAge: config.minimumCacheTTL,
340
336
  etag: upstreamEtag,
341
337
  upstreamEtag,
342
- };
338
+ }
343
339
  }
344
340
  }
345
341
 
346
342
  // -- cache orchestration ----------------------------------------------------
347
343
 
348
344
  interface OptimizerImagesConfig extends ResolvedImagesConfig {
349
- maximumDiskCacheSize: number | undefined;
345
+ maximumDiskCacheSize: number | undefined
350
346
  }
351
347
 
352
348
  function resolveOptimizerConfig(): OptimizerImagesConfig {
353
- const base = getImagesConfig();
354
- const raw = (getNextConfig().images ?? {}) as Record<string, unknown>;
349
+ const base = getImagesConfig()
350
+ const raw = (getNextConfig().images ?? {}) as Record<string, unknown>
355
351
  return {
356
352
  ...base,
357
353
  maximumDiskCacheSize:
358
354
  typeof raw.maximumDiskCacheSize === 'number' ? raw.maximumDiskCacheSize : undefined,
359
- };
355
+ }
360
356
  }
361
357
 
362
358
  function diskCacheEnabled(config: OptimizerImagesConfig): boolean {
363
- if (config.customCacheHandler && hasCacheHandler()) return false;
364
- if (config.maximumDiskCacheSize === 0) return false;
365
- const experimental = (getNextConfig().experimental ?? {}) as Record<string, unknown>;
366
- return experimental.isrFlushToDisk !== false;
359
+ if (config.customCacheHandler && hasCacheHandler()) return false
360
+ if (config.maximumDiskCacheSize === 0) return false
361
+ const experimental = (getNextConfig().experimental ?? {}) as Record<string, unknown>
362
+ return experimental.isrFlushToDisk !== false
367
363
  }
368
364
 
369
365
  async function fetchUpstream(
@@ -378,7 +374,7 @@ async function fetchUpstream(
378
374
  config.dangerouslyAllowLocalIP,
379
375
  config.maximumResponseBody,
380
376
  config.maximumRedirects,
381
- );
377
+ )
382
378
  }
383
379
  const publicFile = await readPublicFile(
384
380
  [
@@ -387,16 +383,16 @@ async function fetchUpstream(
387
383
  path.join(coreConfig.root, 'public'),
388
384
  ],
389
385
  params.href,
390
- );
386
+ )
391
387
  if (publicFile) {
392
388
  return {
393
389
  buffer: publicFile,
394
390
  contentType: null,
395
391
  cacheControl: null,
396
392
  etag: getHash([publicFile]),
397
- };
393
+ }
398
394
  }
399
- return fetchInternalImage(params.href, origin, config.maximumResponseBody);
395
+ return fetchInternalImage(params.href, origin, config.maximumResponseBody)
400
396
  }
401
397
 
402
398
  async function generateAndStore(
@@ -406,8 +402,8 @@ async function generateAndStore(
406
402
  coreConfig: ResolvedConfig,
407
403
  origin: string,
408
404
  ): Promise<ImageCacheEntry & { contentType: string }> {
409
- const upstream = await fetchUpstream(params, config, coreConfig, origin);
410
- const image = await generateImage(upstream, params, config);
405
+ const upstream = await fetchUpstream(params, config, coreConfig, origin)
406
+ const image = await generateImage(upstream, params, config)
411
407
  const entry: ImageCacheEntry & { contentType: string } = {
412
408
  buffer: image.buffer,
413
409
  etag: image.etag,
@@ -416,9 +412,9 @@ async function generateAndStore(
416
412
  maxAge: image.maxAge,
417
413
  expireAt: Math.max(image.maxAge, config.minimumCacheTTL) * 1000 + Date.now(),
418
414
  contentType: image.contentType,
419
- };
415
+ }
420
416
  if (config.customCacheHandler && hasCacheHandler()) {
421
- const revalidate = Math.max(image.maxAge, config.minimumCacheTTL);
417
+ const revalidate = Math.max(image.maxAge, config.minimumCacheTTL)
422
418
  await cacheHandlerSet(
423
419
  cacheKey,
424
420
  {
@@ -430,23 +426,23 @@ async function generateAndStore(
430
426
  revalidate,
431
427
  },
432
428
  { kind: 'IMAGE' },
433
- );
429
+ )
434
430
  } else if (diskCacheEnabled(config)) {
435
431
  await writeCacheEntry(
436
432
  imageCacheDir(coreConfig.root),
437
433
  cacheKey,
438
434
  entry,
439
435
  config.maximumDiskCacheSize,
440
- );
436
+ )
441
437
  }
442
- return entry;
438
+ return entry
443
439
  }
444
440
 
445
441
  // Concurrent requests for the same (url, w, q, format) share one upstream
446
442
  // fetch + optimization; every waiter reports MISS, matching Next.
447
- const inFlight = new Map<string, Promise<ImageCacheEntry & { contentType: string }>>();
443
+ const inFlight = new Map<string, Promise<ImageCacheEntry & { contentType: string }>>()
448
444
  // One background stale-refresh per key at a time.
449
- const refreshing = new Set<string>();
445
+ const refreshing = new Set<string>()
450
446
 
451
447
  async function readCache(
452
448
  cacheKey: string,
@@ -455,14 +451,13 @@ async function readCache(
455
451
  ): Promise<(ImageCacheEntry & { isStale: boolean }) | undefined> {
456
452
  if (config.customCacheHandler && hasCacheHandler()) {
457
453
  const cached = (await cacheHandlerGet(cacheKey, { kind: 'IMAGE' })) as
458
- | { value?: Record<string, unknown>; lastModified?: number }
459
- | undefined;
460
- const value = cached?.value;
461
- if (value?.kind !== 'IMAGE' || typeof value.buffer !== 'string') return undefined;
454
+ { value?: Record<string, unknown>; lastModified?: number } | undefined
455
+ const value = cached?.value
456
+ if (value?.kind !== 'IMAGE' || typeof value.buffer !== 'string') return undefined
462
457
  const revalidate =
463
- typeof value.revalidate === 'number' ? value.revalidate : config.minimumCacheTTL;
464
- const lastModified = cached?.lastModified ?? Date.now();
465
- const expireAt = Math.max(revalidate, config.minimumCacheTTL) * 1000 + lastModified;
458
+ typeof value.revalidate === 'number' ? value.revalidate : config.minimumCacheTTL
459
+ const lastModified = cached?.lastModified ?? Date.now()
460
+ const expireAt = Math.max(revalidate, config.minimumCacheTTL) * 1000 + lastModified
466
461
  return {
467
462
  buffer: Buffer.from(value.buffer, 'base64'),
468
463
  etag: typeof value.etag === 'string' ? value.etag : getHash([value.buffer]),
@@ -471,12 +466,12 @@ async function readCache(
471
466
  maxAge: revalidate,
472
467
  expireAt,
473
468
  isStale: expireAt < Date.now(),
474
- };
469
+ }
475
470
  }
476
- if (!diskCacheEnabled(config)) return undefined;
477
- const entry = await readCacheEntry(imageCacheDir(coreConfig.root), cacheKey);
478
- if (!entry) return undefined;
479
- return { ...entry, isStale: Date.now() > entry.expireAt };
471
+ if (!diskCacheEnabled(config)) return undefined
472
+ const entry = await readCacheEntry(imageCacheDir(coreConfig.root), cacheKey)
473
+ if (!entry) return undefined
474
+ return { ...entry, isStale: Date.now() > entry.expireAt }
480
475
  }
481
476
 
482
477
  // -- response ---------------------------------------------------------------
@@ -491,34 +486,34 @@ function buildResponse(
491
486
  maxAge: number,
492
487
  xCache: XCacheHeader,
493
488
  ): Response {
494
- const headers = new Headers();
495
- headers.set('Vary', 'Accept');
489
+ const headers = new Headers()
490
+ headers.set('Vary', 'Accept')
496
491
  headers.set(
497
492
  'Cache-Control',
498
493
  params.isStatic
499
494
  ? 'public, max-age=315360000, immutable'
500
495
  : `public, max-age=${maxAge}, must-revalidate`,
501
- );
502
- headers.set('ETag', etag);
496
+ )
497
+ headers.set('ETag', etag)
503
498
 
504
- const ifNoneMatch = request.headers.get('if-none-match');
499
+ const ifNoneMatch = request.headers.get('if-none-match')
505
500
  if (ifNoneMatch && (ifNoneMatch === etag || ifNoneMatch === `"${etag}"`)) {
506
- headers.set('X-Nextjs-Cache', xCache);
507
- return new Response(null, { status: 304, headers });
501
+ headers.set('X-Nextjs-Cache', xCache)
502
+ return new Response(null, { status: 304, headers })
508
503
  }
509
504
 
510
- headers.set('Content-Type', contentType);
511
- const fileName = getFileNameWithExtension(params.href, contentType);
505
+ headers.set('Content-Type', contentType)
506
+ const fileName = getFileNameWithExtension(params.href, contentType)
512
507
  headers.set(
513
508
  'Content-Disposition',
514
509
  contentDispositionHeader(config.contentDispositionType, fileName),
515
- );
516
- headers.set('Content-Security-Policy', config.contentSecurityPolicy);
517
- headers.set('X-Nextjs-Cache', xCache);
518
- headers.set('Content-Length', String(buffer.byteLength));
510
+ )
511
+ headers.set('Content-Security-Policy', config.contentSecurityPolicy)
512
+ headers.set('X-Nextjs-Cache', xCache)
513
+ headers.set('Content-Length', String(buffer.byteLength))
519
514
 
520
- if (request.method === 'HEAD') return new Response(null, { status: 200, headers });
521
- return new Response(new Uint8Array(buffer), { status: 200, headers });
515
+ if (request.method === 'HEAD') return new Response(null, { status: 200, headers })
516
+ return new Response(new Uint8Array(buffer), { status: 200, headers })
522
517
  }
523
518
 
524
519
  // -- entry point ------------------------------------------------------------
@@ -527,13 +522,13 @@ export async function serveImageOptimizer(
527
522
  request: Request,
528
523
  coreConfig: ResolvedConfig,
529
524
  ): Promise<Response> {
530
- const config = resolveOptimizerConfig();
531
- const url = new URL(request.url);
532
- const basePath = '';
525
+ const config = resolveOptimizerConfig()
526
+ const url = new URL(request.url)
527
+ const basePath = ''
533
528
 
534
- const paramsResult = validateParams(request, url.searchParams, config, basePath);
535
- if ('errorMessage' in paramsResult) return badRequest(paramsResult.errorMessage);
536
- const params = paramsResult;
529
+ const paramsResult = validateParams(request, url.searchParams, config, basePath)
530
+ if ('errorMessage' in paramsResult) return badRequest(paramsResult.errorMessage)
531
+ const params = paramsResult
537
532
 
538
533
  const cacheKey = getHash([
539
534
  IMAGE_CACHE_VERSION,
@@ -541,11 +536,11 @@ export async function serveImageOptimizer(
541
536
  params.width,
542
537
  params.quality,
543
538
  params.mimeType,
544
- ]);
545
- const origin = url.origin;
539
+ ])
540
+ const origin = url.origin
546
541
 
547
542
  try {
548
- const cached = await readCache(cacheKey, config, coreConfig);
543
+ const cached = await readCache(cacheKey, config, coreConfig)
549
544
  if (cached && !cached.isStale) {
550
545
  return buildResponse(
551
546
  request,
@@ -556,15 +551,15 @@ export async function serveImageOptimizer(
556
551
  cached.etag,
557
552
  cached.maxAge,
558
553
  'HIT',
559
- );
554
+ )
560
555
  }
561
556
  if (cached) {
562
557
  // Serve the stale bytes now, refresh in the background.
563
558
  if (!refreshing.has(cacheKey)) {
564
- refreshing.add(cacheKey);
559
+ refreshing.add(cacheKey)
565
560
  void generateAndStore(cacheKey, params, config, coreConfig, origin)
566
561
  .catch(() => undefined)
567
- .finally(() => refreshing.delete(cacheKey));
562
+ .finally(() => refreshing.delete(cacheKey))
568
563
  }
569
564
  return buildResponse(
570
565
  request,
@@ -575,16 +570,16 @@ export async function serveImageOptimizer(
575
570
  cached.etag,
576
571
  cached.maxAge,
577
572
  'STALE',
578
- );
573
+ )
579
574
  }
580
575
 
581
- let pending = inFlight.get(cacheKey);
576
+ let pending = inFlight.get(cacheKey)
582
577
  if (!pending) {
583
- pending = generateAndStore(cacheKey, params, config, coreConfig, origin);
584
- inFlight.set(cacheKey, pending);
585
- void pending.catch(() => undefined).finally(() => inFlight.delete(cacheKey));
578
+ pending = generateAndStore(cacheKey, params, config, coreConfig, origin)
579
+ inFlight.set(cacheKey, pending)
580
+ void pending.catch(() => undefined).finally(() => inFlight.delete(cacheKey))
586
581
  }
587
- const entry = await pending;
582
+ const entry = await pending
588
583
  return buildResponse(
589
584
  request,
590
585
  params,
@@ -594,8 +589,8 @@ export async function serveImageOptimizer(
594
589
  entry.etag,
595
590
  entry.maxAge,
596
591
  'MISS',
597
- );
592
+ )
598
593
  } catch (error) {
599
- return errorResponse(error);
594
+ return errorResponse(error)
600
595
  }
601
596
  }