@wular/pnext 0.0.1

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 (371) hide show
  1. package/README.md +153 -0
  2. package/bin/pnext +67 -0
  3. package/config/lint/base.js +48 -0
  4. package/config/ts/base.json +26 -0
  5. package/config/ts/react.json +13 -0
  6. package/package.json +70 -0
  7. package/reference/compat.md +63 -0
  8. package/reference/config.md +120 -0
  9. package/reference/css.md +58 -0
  10. package/reference/dev.md +69 -0
  11. package/reference/env.md +40 -0
  12. package/reference/metadata.md +86 -0
  13. package/reference/navigation.md +149 -0
  14. package/reference/overview.md +35 -0
  15. package/reference/performance.md +97 -0
  16. package/reference/rendering.md +127 -0
  17. package/reference/routing.md +167 -0
  18. package/reference/typegen.md +64 -0
  19. package/src/api/cache.ts +80 -0
  20. package/src/api/client-cache.ts +9 -0
  21. package/src/api/client-navigation.ts +279 -0
  22. package/src/api/dynamic.tsx +102 -0
  23. package/src/api/link.tsx +119 -0
  24. package/src/api/navigation.ts +198 -0
  25. package/src/api/router/events.ts +53 -0
  26. package/src/api/router/history.ts +70 -0
  27. package/src/api/router/hub.ts +194 -0
  28. package/src/api/router/policies.ts +107 -0
  29. package/src/api/router/runtime.ts +5238 -0
  30. package/src/api/router/types.ts +299 -0
  31. package/src/api/router.ts +167 -0
  32. package/src/api/server.ts +323 -0
  33. package/src/api/suspense.ts +16 -0
  34. package/src/cache/context.ts +61 -0
  35. package/src/cli/adapters/vercel-warm.ts +375 -0
  36. package/src/cli/adapters/vercel.ts +1310 -0
  37. package/src/cli/analyze-print.ts +181 -0
  38. package/src/cli/analyze.ts +328 -0
  39. package/src/cli/boot-trace.ts +29 -0
  40. package/src/cli/build.ts +3114 -0
  41. package/src/cli/dev.ts +276 -0
  42. package/src/cli/index.ts +196 -0
  43. package/src/cli/named-bin.ts +119 -0
  44. package/src/cli/serve-ui.ts +160 -0
  45. package/src/cli/start.ts +1425 -0
  46. package/src/client/build.ts +2136 -0
  47. package/src/client/chunk-fold.ts +526 -0
  48. package/src/client/entry.ts +1525 -0
  49. package/src/client/paths.ts +22 -0
  50. package/src/client/prebuilt.ts +621 -0
  51. package/src/client/profile.ts +75 -0
  52. package/src/client/react-compiler.ts +94 -0
  53. package/src/client/reference-stub.ts +145 -0
  54. package/src/client/reference.ts +47 -0
  55. package/src/compat/actions/action-client.ts +531 -0
  56. package/src/compat/actions/action-dispatch.ts +676 -0
  57. package/src/compat/actions/action-router.ts +40 -0
  58. package/src/compat/actions/action-shared.ts +95 -0
  59. package/src/compat/actions/client-plugin.ts +135 -0
  60. package/src/compat/actions/client-stub.ts +65 -0
  61. package/src/compat/actions/config.ts +164 -0
  62. package/src/compat/actions/detect.ts +208 -0
  63. package/src/compat/actions/discovery.ts +244 -0
  64. package/src/compat/actions/early-submit.ts +40 -0
  65. package/src/compat/actions/endpoint.ts +602 -0
  66. package/src/compat/actions/flight.ts +52 -0
  67. package/src/compat/actions/form-state.ts +73 -0
  68. package/src/compat/actions/hoist.ts +485 -0
  69. package/src/compat/actions/ids.ts +39 -0
  70. package/src/compat/actions/index.ts +41 -0
  71. package/src/compat/actions/instances.ts +144 -0
  72. package/src/compat/actions/origin.ts +109 -0
  73. package/src/compat/actions/protocol.ts +125 -0
  74. package/src/compat/actions/registry.ts +74 -0
  75. package/src/compat/actions/rewrite.ts +282 -0
  76. package/src/compat/actions/serve.ts +414 -0
  77. package/src/compat/actions/server-tag.ts +21 -0
  78. package/src/compat/actions/unrecognized-error.ts +30 -0
  79. package/src/compat/adapter/build-complete.ts +257 -0
  80. package/src/compat/bundler/bun-externals.ts +53 -0
  81. package/src/compat/bundler/cjs-exports.ts +542 -0
  82. package/src/compat/bundler/config.ts +363 -0
  83. package/src/compat/bundler/externals.ts +34 -0
  84. package/src/compat/bundler/import-meta-url.ts +60 -0
  85. package/src/compat/bundler/modularize-imports.ts +119 -0
  86. package/src/compat/bundler/new-url-asset.ts +87 -0
  87. package/src/compat/bundler/optimize-package-imports.ts +273 -0
  88. package/src/compat/bundler/polyfill.ts +88 -0
  89. package/src/compat/bundler/react-compiler.ts +61 -0
  90. package/src/compat/bundler/react-profiler.tsx +25 -0
  91. package/src/compat/bundler/relay-transform.ts +116 -0
  92. package/src/compat/bundler/require-context.ts +281 -0
  93. package/src/compat/bundler/resolve-extensions.ts +75 -0
  94. package/src/compat/bundler/source-cache.ts +61 -0
  95. package/src/compat/bundler/static-imports.ts +25 -0
  96. package/src/compat/bundler/symlink-imports.ts +119 -0
  97. package/src/compat/bundler/tsconfig-paths.ts +50 -0
  98. package/src/compat/bundler/wasm.ts +153 -0
  99. package/src/compat/bundler/webpack-loaders.ts +685 -0
  100. package/src/compat/bundler/worker.ts +278 -0
  101. package/src/compat/cache/build-flags.ts +81 -0
  102. package/src/compat/cache/build-prerender-errors.ts +163 -0
  103. package/src/compat/cache/custom-handler.ts +159 -0
  104. package/src/compat/cache/fetch-patch.ts +745 -0
  105. package/src/compat/cache/handler.ts +100 -0
  106. package/src/compat/cache/modern-handler.ts +275 -0
  107. package/src/compat/cache/resume-data-cache.ts +143 -0
  108. package/src/compat/cache/revalidate.ts +759 -0
  109. package/src/compat/cache/runtime-error.ts +124 -0
  110. package/src/compat/cache/use-cache-transform.ts +961 -0
  111. package/src/compat/cache/use-cache.ts +1695 -0
  112. package/src/compat/cache-control.ts +269 -0
  113. package/src/compat/client/base-path.ts +64 -0
  114. package/src/compat/client/css-order.ts +36 -0
  115. package/src/compat/client/errors/control-flow.ts +92 -0
  116. package/src/compat/client/errors/error-boundary.ts +222 -0
  117. package/src/compat/client/errors/global-error.ts +238 -0
  118. package/src/compat/client/errors/install.ts +217 -0
  119. package/src/compat/client/errors/lazy.ts +53 -0
  120. package/src/compat/client/errors/primitive-throw.ts +126 -0
  121. package/src/compat/client/errors/soft-refresh.ts +14 -0
  122. package/src/compat/client/link-status.ts +86 -0
  123. package/src/compat/client/nav-compat-runtime.ts +57 -0
  124. package/src/compat/client/nav-compat.ts +42 -0
  125. package/src/compat/client/navigation-scroll.ts +154 -0
  126. package/src/compat/client/optimistic-routing.ts +206 -0
  127. package/src/compat/client/prefetch-cache.ts +111 -0
  128. package/src/compat/client/route-announcer.ts +72 -0
  129. package/src/compat/client/segment-cache-policy.ts +159 -0
  130. package/src/compat/client/segment-cache.ts +1077 -0
  131. package/src/compat/client/segment-prefetch.ts +375 -0
  132. package/src/compat/client/trailing-slash.ts +24 -0
  133. package/src/compat/css/chunking.ts +254 -0
  134. package/src/compat/css/inline-css.ts +73 -0
  135. package/src/compat/css/lightningcss.ts +90 -0
  136. package/src/compat/css/modules.ts +373 -0
  137. package/src/compat/css/nonce.ts +30 -0
  138. package/src/compat/css/sass-plugin.ts +65 -0
  139. package/src/compat/css/sass.ts +392 -0
  140. package/src/compat/css/styled-jsx-runtime.ts +80 -0
  141. package/src/compat/css/styled-jsx.ts +49 -0
  142. package/src/compat/edge-runtime.ts +71 -0
  143. package/src/compat/export/client.ts +112 -0
  144. package/src/compat/export/index.ts +272 -0
  145. package/src/compat/export/standalone.ts +207 -0
  146. package/src/compat/image-optimizer/cache.ts +119 -0
  147. package/src/compat/image-optimizer/detect.ts +143 -0
  148. package/src/compat/image-optimizer/index.ts +601 -0
  149. package/src/compat/image-optimizer/source.ts +243 -0
  150. package/src/compat/index.ts +458 -0
  151. package/src/compat/lifecycle/after-scope.ts +86 -0
  152. package/src/compat/lifecycle/after.ts +173 -0
  153. package/src/compat/lifecycle/error-funnel.ts +306 -0
  154. package/src/compat/lifecycle/error-serialize.ts +87 -0
  155. package/src/compat/lifecycle/error-ui.ts +167 -0
  156. package/src/compat/lifecycle/instrumentation-client.ts +138 -0
  157. package/src/compat/lifecycle/instrumentation.ts +277 -0
  158. package/src/compat/lifecycle/node-console.ts +19 -0
  159. package/src/compat/lifecycle/testmode.ts +263 -0
  160. package/src/compat/mdx/compile.ts +219 -0
  161. package/src/compat/mdx/next-mdx-stub.ts +46 -0
  162. package/src/compat/mdx/plugin.ts +37 -0
  163. package/src/compat/metadata-route-artifacts.ts +458 -0
  164. package/src/compat/metadata.ts +295 -0
  165. package/src/compat/middleware/manifest.ts +210 -0
  166. package/src/compat/misc/action-return.ts +173 -0
  167. package/src/compat/next/cache.ts +211 -0
  168. package/src/compat/next/canonical-url.ts +35 -0
  169. package/src/compat/next/client-cache.ts +57 -0
  170. package/src/compat/next/client-navigation.ts +313 -0
  171. package/src/compat/next/client-only.ts +3 -0
  172. package/src/compat/next/client-script.tsx +215 -0
  173. package/src/compat/next/client-server.ts +39 -0
  174. package/src/compat/next/config-loader.ts +569 -0
  175. package/src/compat/next/config.ts +29 -0
  176. package/src/compat/next/constants.cjs +6 -0
  177. package/src/compat/next/constants.ts +6 -0
  178. package/src/compat/next/custom-server.ts +236 -0
  179. package/src/compat/next/dist/client/components/app-router-headers.ts +32 -0
  180. package/src/compat/next/dist/server/app-render/work-unit-async-storage.external.cjs +38 -0
  181. package/src/compat/next/dist/server/web/spec-extension/revalidate.ts +1 -0
  182. package/src/compat/next/dist/server/web/spec-extension/unstable-cache.ts +1 -0
  183. package/src/compat/next/dist/server/web/spec-extension/unstable-no-store.ts +1 -0
  184. package/src/compat/next/dynamic.tsx +46 -0
  185. package/src/compat/next/error.tsx +148 -0
  186. package/src/compat/next/font/cache.ts +171 -0
  187. package/src/compat/next/font/google.ts +2 -0
  188. package/src/compat/next/font/index.ts +8 -0
  189. package/src/compat/next/font/local.ts +5 -0
  190. package/src/compat/next/font/runtime-client.ts +71 -0
  191. package/src/compat/next/font/runtime.ts +974 -0
  192. package/src/compat/next/font/shared.ts +281 -0
  193. package/src/compat/next/form.tsx +156 -0
  194. package/src/compat/next/head.tsx +10 -0
  195. package/src/compat/next/headers.ts +247 -0
  196. package/src/compat/next/image/config.ts +196 -0
  197. package/src/compat/next/image/optimizer.ts +96 -0
  198. package/src/compat/next/image/patterns.ts +103 -0
  199. package/src/compat/next/image/shared.ts +141 -0
  200. package/src/compat/next/image/static-metadata.ts +283 -0
  201. package/src/compat/next/image/validate.ts +269 -0
  202. package/src/compat/next/image-client.tsx +215 -0
  203. package/src/compat/next/image-props.ts +575 -0
  204. package/src/compat/next/image-usage.ts +102 -0
  205. package/src/compat/next/image.tsx +56 -0
  206. package/src/compat/next/index.ts +1 -0
  207. package/src/compat/next/legacy-image.tsx +97 -0
  208. package/src/compat/next/link-usage.ts +29 -0
  209. package/src/compat/next/link-validation-transform.ts +200 -0
  210. package/src/compat/next/link.tsx +466 -0
  211. package/src/compat/next/navigation.cjs +21 -0
  212. package/src/compat/next/navigation.ts +188 -0
  213. package/src/compat/next/offline.ts +51 -0
  214. package/src/compat/next/og.ts +324 -0
  215. package/src/compat/next/optimistic-route-state.ts +188 -0
  216. package/src/compat/next/preferred-region.ts +39 -0
  217. package/src/compat/next/redirects.ts +131 -0
  218. package/src/compat/next/resource-hints.ts +136 -0
  219. package/src/compat/next/rewrites.ts +350 -0
  220. package/src/compat/next/root-params.ts +142 -0
  221. package/src/compat/next/router.cjs +49 -0
  222. package/src/compat/next/router.ts +143 -0
  223. package/src/compat/next/script.tsx +355 -0
  224. package/src/compat/next/server-only.ts +3 -0
  225. package/src/compat/next/server.ts +28 -0
  226. package/src/compat/next/svgr.ts +58 -0
  227. package/src/compat/next/telemetry.ts +77 -0
  228. package/src/compat/next/user-agent.ts +100 -0
  229. package/src/compat/next/web-vitals.ts +56 -0
  230. package/src/compat/otel/api.ts +95 -0
  231. package/src/compat/otel/client-trace-metadata.ts +71 -0
  232. package/src/compat/otel/fetch-span.ts +77 -0
  233. package/src/compat/otel/tracer.ts +944 -0
  234. package/src/compat/pages/client-plugin.ts +108 -0
  235. package/src/compat/pages/index.ts +527 -0
  236. package/src/compat/pages/router-state.ts +94 -0
  237. package/src/compat/ppr/io.ts +38 -0
  238. package/src/compat/ppr/missing-root-params.ts +105 -0
  239. package/src/compat/ppr/root-params-scan.ts +164 -0
  240. package/src/compat/ppr/root-params-transform.ts +75 -0
  241. package/src/compat/ppr/root-params.ts +129 -0
  242. package/src/compat/ppr/segment-config-incompat.ts +34 -0
  243. package/src/compat/protocol.ts +202 -0
  244. package/src/compat/react/client.ts +59 -0
  245. package/src/compat/react/compiler-runtime.ts +60 -0
  246. package/src/compat/react/dom-client.ts +115 -0
  247. package/src/compat/react/dom-react-server.ts +20 -0
  248. package/src/compat/react/dom-server.ts +40 -0
  249. package/src/compat/react/dom.ts +154 -0
  250. package/src/compat/react/preact.ts +522 -0
  251. package/src/compat/react/react-server.ts +84 -0
  252. package/src/compat/react/router-shim.ts +26 -0
  253. package/src/compat/react/server-component-use.ts +48 -0
  254. package/src/compat/react/server-inserted-html.ts +87 -0
  255. package/src/compat/react/server.ts +156 -0
  256. package/src/compat/react/view-transition.ts +60 -0
  257. package/src/compat/register/actions.ts +875 -0
  258. package/src/compat/register/boot.ts +141 -0
  259. package/src/compat/register/build-tier.ts +11 -0
  260. package/src/compat/register/build.ts +182 -0
  261. package/src/compat/register/bundler.ts +587 -0
  262. package/src/compat/register/cache.ts +85 -0
  263. package/src/compat/register/client-errors.ts +18 -0
  264. package/src/compat/register/config.ts +17 -0
  265. package/src/compat/register/css-extras.ts +120 -0
  266. package/src/compat/register/edge-runtime.ts +6 -0
  267. package/src/compat/register/errors.ts +46 -0
  268. package/src/compat/register/export.ts +23 -0
  269. package/src/compat/register/font.ts +36 -0
  270. package/src/compat/register/hooks.ts +34 -0
  271. package/src/compat/register/image.ts +133 -0
  272. package/src/compat/register/index.ts +111 -0
  273. package/src/compat/register/instrumentation-client.ts +35 -0
  274. package/src/compat/register/lifecycle.ts +86 -0
  275. package/src/compat/register/mdx.ts +48 -0
  276. package/src/compat/register/middleware.ts +36 -0
  277. package/src/compat/register/misc.ts +44 -0
  278. package/src/compat/register/otel.ts +288 -0
  279. package/src/compat/register/pages-api.ts +473 -0
  280. package/src/compat/register/ppr.ts +56 -0
  281. package/src/compat/register/protocol.ts +57 -0
  282. package/src/compat/register/proxy.ts +127 -0
  283. package/src/compat/register/render.ts +268 -0
  284. package/src/compat/register/routing.ts +410 -0
  285. package/src/compat/register/segment.ts +1903 -0
  286. package/src/compat/register/static-image.ts +21 -0
  287. package/src/compat/register/typed-routes.ts +35 -0
  288. package/src/compat/register/usecache.ts +131 -0
  289. package/src/compat/register/validation.ts +56 -0
  290. package/src/compat/segment/loading-boundary.ts +113 -0
  291. package/src/compat/segment/page-slot.ts +200 -0
  292. package/src/compat/segment/tree.ts +481 -0
  293. package/src/compat/segment/vary-key.ts +102 -0
  294. package/src/compat/segment/vary-params.ts +551 -0
  295. package/src/compat/static-params.ts +33 -0
  296. package/src/compat/tsconfig-defaults.ts +301 -0
  297. package/src/compat/typecheck/index.ts +1481 -0
  298. package/src/compat/typecheck/worker.ts +26 -0
  299. package/src/compat/typed-routes/index.ts +92 -0
  300. package/src/compat/typed-routes/manifest.ts +356 -0
  301. package/src/compat/typed-routes/typegen.ts +566 -0
  302. package/src/compat/validation/errors.ts +159 -0
  303. package/src/compat/validation/index.ts +1770 -0
  304. package/src/compat/validation/prerender-diagnostics.ts +1508 -0
  305. package/src/compat-bootstrap.ts +67 -0
  306. package/src/config.ts +218 -0
  307. package/src/css/build.ts +697 -0
  308. package/src/css/index.ts +2 -0
  309. package/src/css/postcss.ts +236 -0
  310. package/src/css/worker.ts +34 -0
  311. package/src/dev/client-actions.ts +35 -0
  312. package/src/dev/client-chunk-store.ts +92 -0
  313. package/src/dev/client-key-cache.ts +178 -0
  314. package/src/dev/global-css-cache.ts +212 -0
  315. package/src/dev/imports.ts +2430 -0
  316. package/src/dev/module-cache.ts +721 -0
  317. package/src/dev/module-generations.ts +38 -0
  318. package/src/dev/module-transform.ts +188 -0
  319. package/src/dev/node-module-bundle-cache.ts +63 -0
  320. package/src/dev/restart-cache.ts +10 -0
  321. package/src/dev/route-bundle-key-cache.ts +154 -0
  322. package/src/dev/route-facts-cache.ts +223 -0
  323. package/src/dev/server.ts +1710 -0
  324. package/src/dynamic/source.ts +307 -0
  325. package/src/dynamic/tree-shake.ts +262 -0
  326. package/src/env.ts +92 -0
  327. package/src/extensions.ts +1898 -0
  328. package/src/index.ts +34 -0
  329. package/src/internal.ts +43 -0
  330. package/src/islands/boundary-error.ts +8 -0
  331. package/src/islands/static-children.ts +37 -0
  332. package/src/islands/static-slots.ts +106 -0
  333. package/src/ppr-postpone.ts +24 -0
  334. package/src/ppr.ts +784 -0
  335. package/src/proxy.ts +752 -0
  336. package/src/render/hooks.ts +384 -0
  337. package/src/render/index.ts +1 -0
  338. package/src/render/island-context.ts +47 -0
  339. package/src/render/metadata.ts +857 -0
  340. package/src/render/renderer.ts +7391 -0
  341. package/src/render/resource-hints.ts +44 -0
  342. package/src/render/slots.tsx +679 -0
  343. package/src/request/context.ts +396 -0
  344. package/src/resolve/engine.ts +219 -0
  345. package/src/resolve/imports.ts +1104 -0
  346. package/src/resolve/scan-facts.ts +474 -0
  347. package/src/resolve/source-text.ts +86 -0
  348. package/src/routing/forwarded.ts +41 -0
  349. package/src/routing/handler.ts +271 -0
  350. package/src/routing/href.ts +203 -0
  351. package/src/routing/metadata.ts +1018 -0
  352. package/src/routing/request-runtime.ts +43 -0
  353. package/src/routing/routes.ts +2560 -0
  354. package/src/routing/slots.ts +432 -0
  355. package/src/runtime/server.ts +3453 -0
  356. package/src/runtime/vendor.ts +1160 -0
  357. package/src/style-modules.d.ts +9 -0
  358. package/src/typegen.ts +151 -0
  359. package/src/types.ts +725 -0
  360. package/src/utils/ansi.ts +9 -0
  361. package/src/utils/content-type.ts +31 -0
  362. package/src/utils/decode.ts +7 -0
  363. package/src/utils/dev-profile.ts +31 -0
  364. package/src/utils/error-log.ts +29 -0
  365. package/src/utils/fs-cache.ts +31 -0
  366. package/src/utils/fs.ts +119 -0
  367. package/src/utils/html.ts +46 -0
  368. package/src/utils/serialize.ts +378 -0
  369. package/src/utils/source.ts +35 -0
  370. package/src/utils/verbose.ts +39 -0
  371. package/tsconfig.json +10 -0
package/src/proxy.ts ADDED
@@ -0,0 +1,752 @@
1
+ import { existsSync, readFileSync, statSync } from 'node:fs';
2
+ import path from 'node:path';
3
+ import { parseSync } from 'oxc-parser';
4
+ import { registerServerRuntime } from './runtime/server';
5
+ import { pathToFileHref, type ResolvedConfig } from './config';
6
+ import { devServerModuleHref } from './dev/imports';
7
+ import { runWithRequest } from './request/context';
8
+ import {
9
+ copyMiddlewareHeaders,
10
+ forwardMiddlewareRequestHeaders,
11
+ getSetCookie,
12
+ NextResponse,
13
+ nextResponseMeta,
14
+ parseCookies,
15
+ requestCookieHeader,
16
+ toNextRequest,
17
+ } from './api/server';
18
+ import type { ExternalLoadTarget } from './resolve/imports';
19
+ import { getProxyExtensions, withRouteRuntime } from './extensions';
20
+ import { canonicalTrailingSlashPath } from './routing/href';
21
+
22
+ export interface ProxyModule {
23
+ default?: ProxyHandler;
24
+ proxy?: ProxyHandler;
25
+ config?: ProxyConfig;
26
+ }
27
+
28
+ export type ProxyHandler = (
29
+ request: import('./types').NextRequest,
30
+ event: import('./types').NextFetchEvent,
31
+ ) => Response | undefined | void | Promise<Response | undefined | void>;
32
+
33
+ export interface ProxyConfig {
34
+ matcher?: ProxyMatcher | ProxyMatcher[];
35
+ }
36
+
37
+ export interface ProxyRouteCondition {
38
+ type: 'header' | 'query' | 'cookie' | 'host';
39
+ key?: string;
40
+ value?: string;
41
+ }
42
+
43
+ type ProxyMatcher =
44
+ | string
45
+ | { source: string; has?: ProxyRouteCondition[]; missing?: ProxyRouteCondition[] };
46
+
47
+ interface CompiledMatcher {
48
+ regex: RegExp;
49
+ has?: ProxyRouteCondition[];
50
+ missing?: ProxyRouteCondition[];
51
+ }
52
+
53
+ export interface ProxyRunOptions {
54
+ dev?: boolean;
55
+ devImportVersion?: string;
56
+ }
57
+
58
+ export type ProxyRewriteObserver = (from: URL, to: URL) => void;
59
+
60
+ let proxyRewriteObserver: ProxyRewriteObserver | undefined;
61
+
62
+ export function setProxyRewriteObserver(observer: ProxyRewriteObserver | undefined): void {
63
+ proxyRewriteObserver = observer;
64
+ }
65
+
66
+ export type ProxyRunObserver = <T>(request: Request, fn: () => T) => T;
67
+
68
+ let proxyRunObserver: ProxyRunObserver | undefined;
69
+
70
+ export function setProxyRunObserver(observer: ProxyRunObserver | undefined): void {
71
+ proxyRunObserver = observer;
72
+ }
73
+
74
+ function wrapMiddleware<T>(request: Request, fn: () => T): T {
75
+ return proxyRunObserver ? proxyRunObserver(request, fn) : fn();
76
+ }
77
+
78
+ export interface ProxyNextResult {
79
+ request: Request;
80
+ response?: Response;
81
+ }
82
+
83
+ export interface ProxyRunner {
84
+ (request: Request, options?: ProxyRunOptions): Promise<ProxyNextResult | Response | undefined>;
85
+ /**
86
+ * Start compiling and importing the proxy module now. The proxy runs serially in front of every
87
+ * request, so leaving its first import to the first request puts the whole compile on the critical
88
+ * path of the first page; dev calls this at boot so it overlaps route scanning and the warmup.
89
+ */
90
+ warm(options?: ProxyRunOptions): void;
91
+ }
92
+
93
+ const proxyExtensions = ['.ts', '.tsx', '.js', '.jsx', '.mts'];
94
+
95
+ export function findProxyFile(config: ResolvedConfig) {
96
+ return findProxyFiles(config)[0];
97
+ }
98
+
99
+ export function proxyExternalLoadTarget(file: string): ExternalLoadTarget {
100
+ return path.basename(file).startsWith('proxy.') ? 'client-ssr' : 'edge';
101
+ }
102
+
103
+ export function findProxyFiles(config: ResolvedConfig, names = getProxyExtensions().names) {
104
+ const files: string[] = [];
105
+ for (const base of proxyBases(config)) {
106
+ for (const name of names) {
107
+ for (const extension of proxyExtensions) {
108
+ const file = path.join(base, `${name}${extension}`);
109
+ if (existsSync(file)) files.push(file);
110
+ }
111
+ }
112
+ }
113
+ return files;
114
+ }
115
+
116
+ export async function validateProxyFiles(config: ResolvedConfig) {
117
+ await getProxyExtensions().validateFiles(config);
118
+ }
119
+
120
+ export async function runProxy(
121
+ config: ResolvedConfig,
122
+ request: Request,
123
+ options: ProxyRunOptions = {},
124
+ ): Promise<ProxyNextResult | Response | undefined> {
125
+ const url = new URL(request.url);
126
+ if (url.pathname.startsWith('/__pnext/') || url.pathname.startsWith('/assets/')) {
127
+ return undefined;
128
+ }
129
+
130
+ return createProxyRunner(config)(request, options);
131
+ }
132
+
133
+ export interface ProxyRunnerOptions {
134
+ /**
135
+ * Pre-compiled proxy module from the production build. Raw Bun imports
136
+ * cannot resolve bare compat specifiers (next/server etc.), so prod must
137
+ * load the build-time bundle that has aliases baked in.
138
+ */
139
+ compiledModuleHref?: string;
140
+ }
141
+
142
+ export function createProxyRunner(
143
+ config: ResolvedConfig,
144
+ runnerOptions: ProxyRunnerOptions = {},
145
+ ): ProxyRunner {
146
+ const file = findProxyFile(config);
147
+ registerServerRuntime(config, file ? [file] : []);
148
+ let moduleKey: string | undefined;
149
+ let modulePromise: Promise<ProxyModule> | undefined;
150
+ let matchers: CompiledMatcher[] | undefined;
151
+
152
+ /** The module for `options`, started at most once per import version. */
153
+ function proxyModule(options: ProxyRunOptions) {
154
+ const key = proxyModuleKey(options);
155
+ if (key !== moduleKey) {
156
+ moduleKey = key;
157
+ modulePromise = importProxyModule(config, file!, options, runnerOptions);
158
+ matchers = undefined;
159
+ }
160
+ return (modulePromise ??= importProxyModule(config, file!, options, runnerOptions));
161
+ }
162
+
163
+ const runner: ProxyRunner = async (request, options = {}) => {
164
+ const url = new URL(request.url);
165
+ if (url.pathname.startsWith('/__pnext/') || url.pathname.startsWith('/assets/')) {
166
+ return undefined;
167
+ }
168
+ if (!file) return undefined;
169
+
170
+ // Matcher first, straight off the source: a request no matcher selects must not wait on the
171
+ // proxy's compile+import, which is otherwise the first serial link of a cold page.
172
+ if (!matchers && proxyMatcherGateEnabled()) {
173
+ const staticMatchers = staticProxyMatchers(file);
174
+ if (staticMatchers && !matchesProxy(staticMatchers, url, request)) return undefined;
175
+ }
176
+
177
+ const module = await proxyModule(options);
178
+ matchers ??= proxyMatchers(module.config);
179
+ if (!matchesProxy(matchers, url, request)) return undefined;
180
+
181
+ // A proxied request's body is buffered so both the proxy and the matched
182
+ // route can read it; buffering is capped at clientMaxBodySize (compat maps
183
+ // experimental.proxyClientMaxBodySize, default 10MB) — the excess is
184
+ // dropped with a warning, matching Next's router-server behavior.
185
+ request = await bufferProxyRequestBody(request, url.pathname);
186
+
187
+ const handlerExport = getProxyExtensions().handlerExport(module as Record<string, unknown>);
188
+ if (typeof handlerExport !== 'function')
189
+ throw new Error(`${file} must export proxy or default`);
190
+ const handler = handlerExport as ProxyHandler;
191
+
192
+ // Next normally hides the RSC cache key and Flight headers from proxy;
193
+ // skipProxyUrlNormalize exposes the original request for advanced routing.
194
+ const handlerRequest = getProxyExtensions().skipUrlNormalize()
195
+ ? request
196
+ : stripFlightHeaders(stripRscUnionQuery(request));
197
+ const nextRequest = toNextRequest(handlerRequest);
198
+ // Response-headers channel so request-scoped APIs invoked inside the proxy
199
+ // (notably draftMode().enable()/disable() from next/headers) can emit
200
+ // Set-Cookie even when the handler returns NextResponse.next() — otherwise
201
+ // the cookie would be dropped since it is not written onto that response.
202
+ const proxyResponseHeaders = new Headers();
203
+ let response = await runWithRequest(
204
+ nextRequest,
205
+ () =>
206
+ withRouteRuntime('edge', () =>
207
+ wrapMiddleware(nextRequest, () =>
208
+ handler(nextRequest, {
209
+ waitUntil(promise) {
210
+ void promise;
211
+ },
212
+ }),
213
+ ),
214
+ ),
215
+ {},
216
+ { responseHeaders: proxyResponseHeaders },
217
+ );
218
+
219
+ const scopedCookies = getSetCookie(proxyResponseHeaders);
220
+ if (scopedCookies.length > 0) {
221
+ // No explicit return means an implicit NextResponse.next(): synthesize one
222
+ // so the emitted Set-Cookie survives while the request still continues.
223
+ if (!response) response = NextResponse.next();
224
+ for (const cookie of scopedCookies) response.headers.append('set-cookie', cookie);
225
+ }
226
+
227
+ if (!response) return { request };
228
+
229
+ // Same-origin middleware redirects follow the app's trailing-slash rule,
230
+ // exactly like a direct navigation would (Next normalizes the Location).
231
+ // skipTrailingSlashRedirect and cross-origin targets are left untouched.
232
+ response = normalizeRedirectLocation(response, url, config);
233
+
234
+ const meta = nextResponseMeta(response);
235
+ if (!meta) return response;
236
+ const forwardedRequest = forwardMiddlewareRequestHeaders(
237
+ response,
238
+ applyProxyCookies(applyRequestOverride(request, meta.requestHeaders), response),
239
+ );
240
+ if (meta.kind === 'rewrite' && meta.url) {
241
+ let rewritten = rewriteRequest(forwardedRequest, meta.url);
242
+ const rewrittenUrl = new URL(rewritten.url);
243
+ // A rewrite to another origin is proxied: the response for THIS request
244
+ // comes from the external server (Next's router-server does the same for
245
+ // any middleware rewrite whose destination carries a protocol).
246
+ if (rewrittenUrl.origin !== url.origin) {
247
+ // An RSC fetch's `_rsc` cache-buster must ride along: the external
248
+ // server cannot recover it from the original URL, and the router keys
249
+ // its response on it. Only added when the destination lacks its own.
250
+ const rscHash = url.searchParams.get('_rsc');
251
+ if (rscHash !== null && !rewrittenUrl.searchParams.has('_rsc')) {
252
+ rewrittenUrl.searchParams.set('_rsc', rscHash);
253
+ rewritten = new Request(rewrittenUrl, rewritten);
254
+ }
255
+ getProxyExtensions().onExternalRewrite(rewritten);
256
+ // The forwarded request still carries the ORIGINAL Host header (the
257
+ // local server); Bun uses it for SNI/verification, so an https target
258
+ // fails certificate checks. Drop it and let fetch derive Host from the
259
+ // target URL, like Next's external-rewrite proxying.
260
+ const externalHeaders = new Headers(rewritten.headers);
261
+ externalHeaders.delete('host');
262
+ const external = await fetch(new Request(rewritten, { headers: externalHeaders }));
263
+ const headers = new Headers(external.headers);
264
+ headers.delete('content-encoding');
265
+ headers.delete('content-length');
266
+ headers.delete('transfer-encoding');
267
+ const proxied = new Response(external.body, {
268
+ status: external.status,
269
+ statusText: external.statusText,
270
+ headers,
271
+ });
272
+ copyMiddlewareHeaders(response, proxied);
273
+ return proxied;
274
+ }
275
+ if (rewrittenUrl.origin === url.origin) {
276
+ proxyRewriteObserver?.(url, rewrittenUrl);
277
+ if (request.headers.get('x-nextjs-data') === '1') {
278
+ response.headers.set('x-nextjs-rewrite', rewrittenUrl.href);
279
+ }
280
+ }
281
+ return { request: rewritten, response };
282
+ }
283
+
284
+ return { request: forwardedRequest, response };
285
+ };
286
+
287
+ runner.warm = (options = {}) => {
288
+ if (!file) return;
289
+ // A failure surfaces on the first request, which awaits this same promise;
290
+ // the swallow here only keeps the unawaited warm call from going unhandled.
291
+ void proxyModule(options).catch(() => undefined);
292
+ };
293
+
294
+ return runner;
295
+ }
296
+
297
+ // Apply the app's trailing-slash rule to a same-origin redirect emitted by the
298
+ // middleware (e.g. `NextResponse.redirect(new URL('/somewhere', req.url))` under
299
+ // `trailingSlash: true` must land on `/somewhere/`). Cross-origin targets, the
300
+ // skipTrailingSlashRedirect opt-out, and non-redirect responses pass through.
301
+ function normalizeRedirectLocation(response: Response, requestUrl: URL, config: ResolvedConfig) {
302
+ if (config.skipTrailingSlashRedirect || !config.trailingSlash) return response;
303
+ if (response.status < 300 || response.status >= 400) return response;
304
+ const location = response.headers.get('location');
305
+ if (!location) return response;
306
+ let target: URL;
307
+ try {
308
+ target = new URL(location, requestUrl);
309
+ } catch {
310
+ return response;
311
+ }
312
+ if (target.origin !== requestUrl.origin) return response;
313
+ const normalized = canonicalTrailingSlashPath(target.pathname, true);
314
+ if (normalized === target.pathname) return response;
315
+ target.pathname = normalized;
316
+ try {
317
+ response.headers.set('location', target.href);
318
+ return response;
319
+ } catch {
320
+ const headers = new Headers(response.headers);
321
+ headers.set('location', target.href);
322
+ return new Response(response.body, {
323
+ status: response.status,
324
+ statusText: response.statusText,
325
+ headers,
326
+ });
327
+ }
328
+ }
329
+
330
+ // Human label for the truncation warning ("10MB", "500KB") — whole units only,
331
+ // mirroring Next's log line ("Request body exceeded 10MB for /api/echo").
332
+ function formatBodyLimit(bytes: number): string {
333
+ if (bytes >= 1024 * 1024 && bytes % (1024 * 1024) === 0) return `${bytes / (1024 * 1024)}MB`;
334
+ if (bytes >= 1024 && bytes % 1024 === 0) return `${bytes / 1024}KB`;
335
+ return `${bytes}B`;
336
+ }
337
+
338
+ async function bufferProxyRequestBody(request: Request, pathname: string): Promise<Request> {
339
+ if (request.method === 'GET' || request.method === 'HEAD' || !request.body) return request;
340
+ const limit = getProxyExtensions().clientMaxBodySize();
341
+ const chunks: Uint8Array[] = [];
342
+ let total = 0;
343
+ let truncated = false;
344
+ const reader = request.body.getReader();
345
+ for (;;) {
346
+ const { done, value } = await reader.read();
347
+ if (done) break;
348
+ if (!truncated && total + value.byteLength > limit) {
349
+ const keep = limit - total;
350
+ if (keep > 0) chunks.push(value.subarray(0, keep));
351
+ total = limit;
352
+ truncated = true;
353
+ // Keep draining (and discarding) the rest of the upload — cancelling the
354
+ // stream mid-upload stalls the connection and the response never flushes.
355
+ continue;
356
+ }
357
+ if (!truncated) {
358
+ chunks.push(value);
359
+ total += value.byteLength;
360
+ }
361
+ }
362
+ if (truncated) {
363
+ console.warn(`Request body exceeded ${formatBodyLimit(limit)} for ${pathname}`);
364
+ }
365
+ const body = new Uint8Array(total);
366
+ let offset = 0;
367
+ for (const chunk of chunks) {
368
+ body.set(chunk, offset);
369
+ offset += chunk.byteLength;
370
+ }
371
+ const headers = new Headers(request.headers);
372
+ headers.set('content-length', String(total));
373
+ return new Request(request.url, {
374
+ method: request.method,
375
+ headers,
376
+ body,
377
+ // @ts-expect-error Bun/Node fetch option for streaming request bodies.
378
+ duplex: 'half',
379
+ });
380
+ }
381
+
382
+ export function applyProxyResponse(response: Response, proxyResponse?: Response) {
383
+ if (proxyResponse) copyMiddlewareHeaders(proxyResponse, response);
384
+ return response;
385
+ }
386
+
387
+ export function proxyRoutePatterns(config: ProxyConfig | undefined) {
388
+ const matcher = config?.matcher;
389
+ if (!matcher) return ['^/(.*)$'];
390
+ const matchers = Array.isArray(matcher) ? matcher : [matcher];
391
+ return matchers.map(item => `^${matcherPattern(matcherSource(item))}$`);
392
+ }
393
+
394
+ async function importProxyModule(
395
+ config: ResolvedConfig,
396
+ file: string,
397
+ options: ProxyRunOptions,
398
+ runnerOptions: ProxyRunnerOptions,
399
+ ) {
400
+ const href = options.dev
401
+ ? await devServerModuleHref(config, file, options.devImportVersion ?? 'dev', {
402
+ conditionTarget: 'edge',
403
+ externalLoadTarget: proxyExternalLoadTarget(file),
404
+ reactServerLayer: true,
405
+ })
406
+ : (runnerOptions.compiledModuleHref ?? pathToFileHref(file));
407
+ return import(href) as Promise<ProxyModule>;
408
+ }
409
+
410
+ function proxyBases(config: ResolvedConfig) {
411
+ return config.appPath === path.join(config.root, 'src', 'app')
412
+ ? [path.join(config.root, 'src'), config.root]
413
+ : [config.root];
414
+ }
415
+
416
+ /**
417
+ * `PNEXT_PROXY_MATCHER_GATE=0` restores the old order - every request awaits the proxy module before its
418
+ * matchers are consulted - so the gate can be measured on one tree at one machine load.
419
+ */
420
+ function proxyMatcherGateEnabled() {
421
+ // eslint-disable-next-line turbo/no-undeclared-env-vars
422
+ return process.env.PNEXT_PROXY_MATCHER_GATE !== '0';
423
+ }
424
+
425
+ /** Parsed matchers per proxy file, keyed on the source's size+mtime. */
426
+ const staticMatcherCache = new Map<string, { key: string; matchers?: CompiledMatcher[] }>();
427
+
428
+ /**
429
+ * Compile the matchers of `export const config` off the proxy source itself. Only a wholly literal config
430
+ * qualifies (Next requires a statically analyzable matcher anyway - it extracts one into
431
+ * middleware-manifest.json at build time); anything computed, any parse error, and any file without a
432
+ * literal `config` yields undefined, and the caller falls back to the imported module's config.
433
+ */
434
+ function staticProxyMatchers(file: string): CompiledMatcher[] | undefined {
435
+ let key: string;
436
+ try {
437
+ const stats = statSync(file);
438
+ key = `${stats.size}:${stats.mtimeMs}`;
439
+ } catch {
440
+ return undefined;
441
+ }
442
+ const cached = staticMatcherCache.get(file);
443
+ if (cached?.key === key) return cached.matchers;
444
+ let matchers: CompiledMatcher[] | undefined;
445
+ try {
446
+ const config = staticProxyConfig(file, readFileSync(file, 'utf8'));
447
+ if (config) matchers = proxyMatchers(config);
448
+ } catch {
449
+ matchers = undefined;
450
+ }
451
+ staticMatcherCache.set(file, { key, matchers });
452
+ return matchers;
453
+ }
454
+
455
+ function staticProxyConfig(file: string, source: string): ProxyConfig | undefined {
456
+ const result = parseSync(file, source, { lang: parserLang(file) });
457
+ // A recovered parse may have dropped the very declaration we are reading.
458
+ if (result.errors.length > 0) return undefined;
459
+ for (const statement of result.program.body as StaticNode[]) {
460
+ if (statement.type !== 'ExportNamedDeclaration') continue;
461
+ const declaration = statement.declaration;
462
+ if (declaration?.type !== 'VariableDeclaration') continue;
463
+ for (const declarator of declaration.declarations ?? []) {
464
+ if (declarator.id?.type !== 'Identifier' || declarator.id.name !== 'config') continue;
465
+ const value = staticValue(declarator.init);
466
+ return isProxyConfigShape(value) ? (value as ProxyConfig) : undefined;
467
+ }
468
+ }
469
+ return undefined;
470
+ }
471
+
472
+ function parserLang(file: string) {
473
+ const ext = path.extname(file);
474
+ if (ext === '.tsx') return 'tsx';
475
+ if (ext === '.jsx') return 'jsx';
476
+ return ext === '.js' || ext === '.mjs' ? 'js' : 'ts';
477
+ }
478
+
479
+ /** Structural view of the oxc AST nodes this file reads — no full type import. */
480
+ interface StaticNode {
481
+ type: string;
482
+ name?: string;
483
+ value?: unknown;
484
+ computed?: boolean;
485
+ shorthand?: boolean;
486
+ key?: StaticNode;
487
+ init?: StaticNode;
488
+ id?: StaticNode;
489
+ declaration?: StaticNode;
490
+ declarations?: StaticNode[];
491
+ expression?: StaticNode;
492
+ properties?: StaticNode[];
493
+ elements?: (StaticNode | null)[];
494
+ expressions?: StaticNode[];
495
+ quasis?: { value?: { cooked?: string } }[];
496
+ }
497
+
498
+ /** Literal-only evaluation; `undefined` marks "not statically known". */
499
+ function staticValue(node: StaticNode | undefined | null): unknown {
500
+ if (!node) return undefined;
501
+ switch (node.type) {
502
+ // `… as const` / `… satisfies ProxyConfig` wrap the literal, they never change it.
503
+ case 'TSAsExpression':
504
+ case 'TSSatisfiesExpression':
505
+ case 'TSNonNullExpression':
506
+ return staticValue(node.expression);
507
+ case 'Literal':
508
+ case 'StringLiteral':
509
+ case 'NumericLiteral':
510
+ case 'BooleanLiteral':
511
+ // A regex literal reports `value: null` here; it is not a matcher shape.
512
+ return node.value === null ? undefined : node.value;
513
+ case 'TemplateLiteral':
514
+ return node.expressions?.length === 0
515
+ ? (node.quasis?.[0]?.value?.cooked ?? undefined)
516
+ : undefined;
517
+ case 'ArrayExpression': {
518
+ const items: unknown[] = [];
519
+ for (const element of node.elements ?? []) {
520
+ const value = staticValue(element);
521
+ if (value === undefined) return undefined;
522
+ items.push(value);
523
+ }
524
+ return items;
525
+ }
526
+ case 'ObjectExpression': {
527
+ const object: Record<string, unknown> = {};
528
+ for (const property of node.properties ?? []) {
529
+ if (property.type !== 'Property' && property.type !== 'ObjectProperty') return undefined;
530
+ if (property.computed) return undefined;
531
+ const key =
532
+ property.key?.type === 'Identifier' ? property.key.name : (property.key?.value as string);
533
+ if (typeof key !== 'string') return undefined;
534
+ const value = staticValue(property.value as StaticNode | undefined);
535
+ if (value === undefined) return undefined;
536
+ object[key] = value;
537
+ }
538
+ return object;
539
+ }
540
+ default:
541
+ return undefined;
542
+ }
543
+ }
544
+
545
+ /** Only a config whose `matcher` is exactly the documented shape may gate. */
546
+ function isProxyConfigShape(value: unknown): boolean {
547
+ if (typeof value !== 'object' || value === null) return false;
548
+ const matcher = (value as { matcher?: unknown }).matcher;
549
+ if (matcher === undefined) return true;
550
+ const items = Array.isArray(matcher) ? matcher : [matcher];
551
+ return items.every(item => {
552
+ if (typeof item === 'string') return true;
553
+ if (typeof item !== 'object' || item === null) return false;
554
+ const { source, has, missing } = item as Record<string, unknown>;
555
+ if (typeof source !== 'string') return false;
556
+ return [has, missing].every(
557
+ list => list === undefined || (Array.isArray(list) && list.every(isConditionShape)),
558
+ );
559
+ });
560
+ }
561
+
562
+ function isConditionShape(value: unknown): boolean {
563
+ if (typeof value !== 'object' || value === null) return false;
564
+ const { type, key, value: expected } = value as Record<string, unknown>;
565
+ if (type !== 'header' && type !== 'query' && type !== 'cookie' && type !== 'host') return false;
566
+ if (key !== undefined && typeof key !== 'string') return false;
567
+ return expected === undefined || typeof expected === 'string';
568
+ }
569
+
570
+ function proxyMatchers(config: ProxyConfig | undefined): CompiledMatcher[] {
571
+ const matcher = config?.matcher;
572
+ if (!matcher) return [{ regex: /.*/ }];
573
+ const matchers = Array.isArray(matcher) ? matcher : [matcher];
574
+ return matchers.map(item => ({
575
+ regex: matcherRegex(matcherSource(item)),
576
+ ...(typeof item === 'object' && item.has ? { has: item.has } : {}),
577
+ ...(typeof item === 'object' && item.missing ? { missing: item.missing } : {}),
578
+ }));
579
+ }
580
+
581
+ function matchesProxy(matchers: CompiledMatcher[], url: URL, request: Request) {
582
+ const decoded = decodePathSeparators(url.pathname);
583
+ return matchers.some(
584
+ matcher =>
585
+ (matcher.regex.test(url.pathname) || matcher.regex.test(decoded)) &&
586
+ matchesConditions(matcher, url, request),
587
+ );
588
+ }
589
+
590
+ // A matcher applies only when every `has` condition is present and no `missing`
591
+ // condition is present (Next's route-matcher semantics). The proxy config's
592
+ // `missing: [{ type: 'header', key: 'Next-Router-Prefetch' }]` is how a proxy
593
+ // opts out of running for prefetch requests.
594
+ function matchesConditions(matcher: CompiledMatcher, url: URL, request: Request) {
595
+ if (matcher.has && !matcher.has.every(condition => hasCondition(condition, url, request))) {
596
+ return false;
597
+ }
598
+ if (matcher.missing?.some(condition => hasCondition(condition, url, request))) {
599
+ return false;
600
+ }
601
+ return true;
602
+ }
603
+
604
+ function hasCondition(condition: ProxyRouteCondition, url: URL, request: Request): boolean {
605
+ const actual = conditionValue(condition, url, request);
606
+ if (actual === null) return false;
607
+ if (condition.value === undefined) return true;
608
+ try {
609
+ return new RegExp(`^${condition.value}$`).test(actual);
610
+ } catch {
611
+ return condition.value === actual;
612
+ }
613
+ }
614
+
615
+ function conditionValue(
616
+ condition: ProxyRouteCondition,
617
+ url: URL,
618
+ request: Request,
619
+ ): string | null {
620
+ switch (condition.type) {
621
+ case 'header':
622
+ return condition.key ? request.headers.get(condition.key) : null;
623
+ case 'query':
624
+ return condition.key ? url.searchParams.get(condition.key) : null;
625
+ case 'cookie': {
626
+ if (!condition.key) return null;
627
+ const cookie = parseCookies(request.headers.get('cookie') ?? '').find(
628
+ item => item.name === condition.key,
629
+ );
630
+ return cookie ? cookie.value : null;
631
+ }
632
+ case 'host':
633
+ return url.host;
634
+ default:
635
+ return null;
636
+ }
637
+ }
638
+
639
+ function decodePathSeparators(pathname: string) {
640
+ return pathname.replace(/%2f/gi, '/');
641
+ }
642
+
643
+ function matcherSource(matcher: ProxyMatcher) {
644
+ return typeof matcher === 'string' ? matcher : matcher.source;
645
+ }
646
+
647
+ function matcherRegex(matcher: string) {
648
+ return new RegExp(`^${matcherPattern(matcher)}$`);
649
+ }
650
+
651
+ function matcherPattern(matcher: string) {
652
+ if (matcher.includes('(')) return matcher;
653
+ if (matcher === '/') return '/';
654
+
655
+ let source = '';
656
+ for (const segment of matcher.split('/').filter(Boolean)) {
657
+ if (/^:[^/]+\*$/.test(segment)) {
658
+ source += '(?:/.*)?';
659
+ } else if (/^:[^/]+$/.test(segment)) {
660
+ source += '/[^/]+';
661
+ } else {
662
+ source += `/${escapeRegex(segment)}`;
663
+ }
664
+ }
665
+ return source || '/';
666
+ }
667
+
668
+ function rewriteRequest(request: Request, url: string) {
669
+ return new Request(new URL(url, request.url), request);
670
+ }
671
+
672
+ // Drop `?_rsc=<hash>` from the request the middleware handler sees. GET/HEAD
673
+ // only ever carry it (router fetches), so re-wrapping the Request is body-safe.
674
+ function stripRscUnionQuery(request: Request): Request {
675
+ const url = new URL(request.url);
676
+ if (!url.searchParams.has('_rsc')) return request;
677
+ url.searchParams.delete('_rsc');
678
+ return new Request(url, request);
679
+ }
680
+
681
+ // The app-router flight headers Next removes from the request middleware sees
682
+ // (FLIGHT_HEADERS in next/dist/client/components/app-router-headers). They are
683
+ // only elided for the middleware view; the downstream render keeps them.
684
+ const FLIGHT_HEADERS = [
685
+ 'rsc',
686
+ 'next-router-state-tree',
687
+ 'next-router-prefetch',
688
+ 'next-hmr-refresh',
689
+ 'next-router-segment-prefetch',
690
+ ];
691
+
692
+ // Drop the flight headers from the request the middleware handler sees. GET/HEAD
693
+ // router fetches carry them, so re-wrapping the Request is body-safe.
694
+ function stripFlightHeaders(request: Request): Request {
695
+ if (!FLIGHT_HEADERS.some(header => request.headers.has(header))) return request;
696
+ const headers = new Headers(request.headers);
697
+ for (const header of FLIGHT_HEADERS) headers.delete(header);
698
+ return new Request(request, { headers });
699
+ }
700
+
701
+ function applyRequestOverride(request: Request, headers: Headers | undefined) {
702
+ if (!headers) return request;
703
+ return new Request(request, { headers });
704
+ }
705
+
706
+ function applyProxyCookies(request: Request, response: Response) {
707
+ const setCookies = getSetCookie(response.headers);
708
+ if (setCookies.length === 0) return request;
709
+
710
+ const cookies = parseCookies(request.headers.get('cookie') ?? '');
711
+ for (const header of setCookies) {
712
+ const cookie = parseSetCookie(header);
713
+ if (!cookie) continue;
714
+ const index = cookies.findIndex(item => item.name === cookie.name);
715
+ if (cookie.deleted) {
716
+ if (index !== -1) cookies.splice(index, 1);
717
+ continue;
718
+ }
719
+ if (index === -1) cookies.push({ name: cookie.name, value: cookie.value });
720
+ else cookies[index] = { name: cookie.name, value: cookie.value };
721
+ }
722
+
723
+ const headers = new Headers(request.headers);
724
+ const cookieHeader = requestCookieHeader(cookies);
725
+ if (cookieHeader) headers.set('cookie', cookieHeader);
726
+ else headers.delete('cookie');
727
+ return new Request(request, { headers });
728
+ }
729
+
730
+ function parseSetCookie(header: string) {
731
+ const [pair = '', ...attributes] = header.split(';').map(part => part.trim());
732
+ const [name = '', ...valueParts] = pair.split('=');
733
+ if (!name) return undefined;
734
+ const deleted = attributes.some(attribute => /^max-age=0$/i.test(attribute));
735
+ return { name: decodeCookiePart(name), value: decodeCookiePart(valueParts.join('=')), deleted };
736
+ }
737
+
738
+ function decodeCookiePart(value: string) {
739
+ try {
740
+ return decodeURIComponent(value);
741
+ } catch {
742
+ return value;
743
+ }
744
+ }
745
+
746
+ function escapeRegex(value: string) {
747
+ return value.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
748
+ }
749
+
750
+ function proxyModuleKey(options: ProxyRunOptions) {
751
+ return options.dev ? `dev:${options.devImportVersion ?? 'dev'}` : 'prod';
752
+ }