@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
@@ -0,0 +1,271 @@
1
+ import { isNotFoundError, isRedirectError } from '../api/navigation';
2
+ import { getWorkUnit, runWithRequest } from '../request/context';
3
+ import { nextResponseMeta, toNextRequest } from '../api/server';
4
+ import { reportRequestError } from '../extensions';
5
+ import type { NextRequest, RouteManifestEntry, RouteParamValue } from '../types';
6
+
7
+ const routeMethods = ['GET', 'HEAD', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS'] as const;
8
+
9
+ type RouteMethod = (typeof routeMethods)[number];
10
+
11
+ // Methods that are valid HTTP verbs but may be unimplemented on a route (405),
12
+ // vs. tokens that are not HTTP methods at all (400). Next answers 400 for the
13
+ // latter (e.g. a `HEADER` request) and 405 for a real-but-missing verb.
14
+ const httpMethods = new Set([
15
+ 'GET',
16
+ 'HEAD',
17
+ 'POST',
18
+ 'PUT',
19
+ 'PATCH',
20
+ 'DELETE',
21
+ 'OPTIONS',
22
+ 'CONNECT',
23
+ 'TRACE',
24
+ ]);
25
+
26
+ interface RouteHandlerContext {
27
+ params?: Promise<Record<string, RouteParamValue>>;
28
+ }
29
+
30
+ interface HandleRouteOptions {
31
+ /** Absolute route file path, used in the "no response returned" CLI error. */
32
+ routeFile?: string;
33
+ }
34
+
35
+ export interface RouteHandlerModule {
36
+ params?: () => Record<string, RouteParamValue>[] | Promise<Record<string, RouteParamValue>[]>;
37
+ GET?: RouteHandler;
38
+ HEAD?: RouteHandler;
39
+ POST?: RouteHandler;
40
+ PUT?: RouteHandler;
41
+ PATCH?: RouteHandler;
42
+ DELETE?: RouteHandler;
43
+ OPTIONS?: RouteHandler;
44
+ }
45
+
46
+ type RouteHandler = (
47
+ request: NextRequest,
48
+ context: RouteHandlerContext,
49
+ ) => Response | undefined | void | Promise<Response | undefined | void>;
50
+
51
+ export async function handleRouteModule(
52
+ module: RouteHandlerModule,
53
+ request: Request,
54
+ params: Record<string, RouteParamValue>,
55
+ options: HandleRouteOptions = {},
56
+ ) {
57
+ // Response-headers channel for the handler's request scope: APIs like
58
+ // draftMode().enable() append set-cookie here while the handler runs, and
59
+ // the headers are merged onto whatever response goes out — including the
60
+ // synthesized redirect response when the handler throws redirect().
61
+ const responseHeaders = new Headers();
62
+ const method = request.method.toUpperCase();
63
+ try {
64
+ // A request method that is not a valid HTTP verb is a bad request, not an
65
+ // unimplemented one (Next returns 400 with an empty body).
66
+ if (!httpMethods.has(method)) return new Response(null, { status: 400 });
67
+
68
+ const handler = routeMethods.includes(method as RouteMethod)
69
+ ? module[method as RouteMethod]
70
+ : undefined;
71
+
72
+ if (!handler) {
73
+ if (method === 'HEAD' && module.GET) {
74
+ // Auto-HEAD from GET: run GET, validate it, then drop the body.
75
+ const response = finalizeResponse(
76
+ await callHandler(module.GET, request, params, responseHeaders),
77
+ request,
78
+ responseHeaders,
79
+ options,
80
+ );
81
+ return new Response(null, { status: response.status, headers: response.headers });
82
+ }
83
+ return missingMethodResponse(module, method);
84
+ }
85
+
86
+ return finalizeResponse(
87
+ await callHandler(handler, request, params, responseHeaders),
88
+ request,
89
+ responseHeaders,
90
+ options,
91
+ );
92
+ } catch (error) {
93
+ if (isRedirectError(error)) {
94
+ // POST submissions get a 303 (Next's behavior for redirect() and
95
+ // permanentRedirect() thrown in route handlers): the browser follows
96
+ // with a GET instead of replaying the POST against the target.
97
+ const status = method === 'POST' ? 303 : error.status;
98
+ return withResponseHeaders(
99
+ new Response(null, { status, headers: { location: error.location } }),
100
+ responseHeaders,
101
+ );
102
+ }
103
+ // notFound() thrown inside a handler is a 404 with an empty body.
104
+ if (isNotFoundError(error)) {
105
+ return withResponseHeaders(new Response(null, { status: 404 }), responseHeaders);
106
+ }
107
+ // Any other throw is a 500 with an empty body. Report it through the error
108
+ // funnel (onRequestError) here since core no longer sees it as uncaught.
109
+ await reportRequestError(
110
+ error,
111
+ { method, url: request.url, headers: request.headers },
112
+ { phase: 'handler', routeKind: 'route-handler', ...(getWorkUnit()?.responseHints ?? {}) },
113
+ );
114
+ console.error(error);
115
+ return new Response(null, { status: 500 });
116
+ }
117
+ }
118
+
119
+ /**
120
+ * Validate the handler's return value and apply the scope's collected headers.
121
+ * A handler must return a Response; `undefined` or a `NextResponse.next()`
122
+ * result is a 500 (with Next's contract-violation CLI error) rather than a
123
+ * passthrough.
124
+ */
125
+ function finalizeResponse(
126
+ response: Response | undefined | void,
127
+ request: Request,
128
+ responseHeaders: Headers,
129
+ options: HandleRouteOptions,
130
+ ) {
131
+ const method = request.method.toUpperCase();
132
+ if (!response) {
133
+ const file = options.routeFile ?? '<route>';
134
+ console.error(
135
+ `No response is returned from route handler '${file}'. Expected a Response object but ` +
136
+ `received '${typeof response}' (method: ${method}, url: ${request.url}). Ensure you ` +
137
+ 'return a `Response` or a `NextResponse` in all branches of your handler.',
138
+ );
139
+ return new Response(null, { status: 500 });
140
+ }
141
+ if (nextResponseMeta(response)?.kind === 'next') {
142
+ console.error('https://nextjs.org/docs/messages/next-response-next-in-app-route-handler');
143
+ return new Response(null, { status: 500 });
144
+ }
145
+ return withResponseHeaders(response, responseHeaders);
146
+ }
147
+
148
+ function callHandler(
149
+ handler: RouteHandler,
150
+ request: Request,
151
+ params: Record<string, RouteParamValue>,
152
+ responseHeaders: Headers,
153
+ ) {
154
+ const nextRequest = toNextRequest(request);
155
+ // Static routes (no dynamic segments) get no `params` in context — Next
156
+ // omits it, so `context.params` is `undefined` rather than a promise of `{}`.
157
+ const context: RouteHandlerContext =
158
+ Object.keys(params).length > 0 ? { params: Promise.resolve(params) } : {};
159
+ return runWithRequest(nextRequest, () => handler(nextRequest, context), params, {
160
+ responseHeaders,
161
+ });
162
+ }
163
+
164
+ /** Merge scope-collected headers (set-cookie appends, everything else sets) onto the response. */
165
+ function withResponseHeaders(response: Response, extra: Headers) {
166
+ const withSetCookie = extra as Headers & { getSetCookie?: () => string[] };
167
+ const scopeCookies = withSetCookie.getSetCookie?.() ?? [];
168
+ let hasOthers = false;
169
+ extra.forEach((_, key) => {
170
+ if (key !== 'set-cookie') hasOthers = true;
171
+ });
172
+ if (scopeCookies.length === 0 && !hasOthers) return response;
173
+
174
+ const apply = (headers: Headers) => {
175
+ extra.forEach((value, key) => {
176
+ if (key !== 'set-cookie') headers.set(key, value);
177
+ });
178
+ // Cookie-jar merge (Next's ResponseCookies semantics): scope cookies
179
+ // (cookies().set during the handler) first, the response's own Set-Cookie
180
+ // headers override same-name entries, and every cookie is normalized with
181
+ // a default Path=/.
182
+ const responseCookies =
183
+ (headers as Headers & { getSetCookie?: () => string[] }).getSetCookie?.() ?? [];
184
+ const jar = new Map<string, string>();
185
+ for (const cookie of [...scopeCookies, ...responseCookies]) {
186
+ jar.set(cookieName(cookie), normalizeSetCookie(cookie));
187
+ }
188
+ headers.delete('set-cookie');
189
+ for (const cookie of jar.values()) headers.append('set-cookie', cookie);
190
+ };
191
+ try {
192
+ apply(response.headers);
193
+ return response;
194
+ } catch {
195
+ // Immutable headers (e.g. a Response forwarded from fetch): re-wrap.
196
+ const headers = new Headers(response.headers);
197
+ apply(headers);
198
+ return new Response(response.body, {
199
+ status: response.status,
200
+ statusText: response.statusText,
201
+ headers,
202
+ });
203
+ }
204
+ }
205
+
206
+ function cookieName(setCookie: string) {
207
+ return (setCookie.split('=')[0] ?? '').trim();
208
+ }
209
+
210
+ function normalizeSetCookie(setCookie: string) {
211
+ return /;\s*path=/i.test(setCookie) ? setCookie : `${setCookie}; Path=/`;
212
+ }
213
+
214
+ export async function staticRouteParams(route: RouteManifestEntry, module: RouteHandlerModule) {
215
+ if (typeof module.GET !== 'function') return [];
216
+ // `dynamic = 'force-static'` and `dynamic = 'error'` both evaluate a static
217
+ // request; the latter lets request APIs fail at their call sites. A
218
+ // `revalidate` export opts a request-independent GET into static/ISR output.
219
+ const config = route.segmentConfig;
220
+ const optedStatic =
221
+ config?.dynamic === 'force-static' ||
222
+ config?.dynamic === 'error' ||
223
+ config?.revalidate === false ||
224
+ (typeof config?.revalidate === 'number' && config.revalidate > 0);
225
+ if (route.hasStaticParams) return module.params?.() ?? [];
226
+ if (route.usesRequest && !optedStatic) return [];
227
+ return route.mode === 'static' || optedStatic ? [{}] : [];
228
+ }
229
+
230
+ export function routeParamsFromPath(
231
+ route: Pick<RouteManifestEntry, 'catchAll' | 'params' | 'pattern'>,
232
+ pathname: string,
233
+ ) {
234
+ const match = new RegExp(`^${route.pattern}$`).exec(pathname);
235
+ const params: Record<string, RouteParamValue> = {};
236
+ if (!match) return params;
237
+
238
+ for (let index = 0; index < route.params.length; index += 1) {
239
+ params[route.params[index]!] = safeDecode(match[index + 1] ?? '');
240
+ }
241
+ if (route.catchAll) {
242
+ params[route.catchAll] = (match[route.params.length + 1] ?? '')
243
+ .split('/')
244
+ .filter(Boolean)
245
+ .map(safeDecode);
246
+ }
247
+ return params;
248
+ }
249
+
250
+ function missingMethodResponse(module: RouteHandlerModule, method: string) {
251
+ const headers = { Allow: allowedMethods(module).join(', ') };
252
+ if (method === 'OPTIONS') return new Response(null, { status: 204, headers });
253
+ return new Response(null, { status: 405, headers });
254
+ }
255
+
256
+ function allowedMethods(module: RouteHandlerModule) {
257
+ const allowed = new Set(routeMethods.filter(method => typeof module[method] === 'function'));
258
+ // GET implies auto-HEAD; every route gets auto-OPTIONS. The Allow header is
259
+ // sorted (Next emits e.g. `OPTIONS, POST`).
260
+ if (allowed.has('GET')) allowed.add('HEAD');
261
+ allowed.add('OPTIONS');
262
+ return [...allowed].sort();
263
+ }
264
+
265
+ function safeDecode(value: string) {
266
+ try {
267
+ return decodeURIComponent(value);
268
+ } catch {
269
+ return value;
270
+ }
271
+ }
@@ -0,0 +1,203 @@
1
+ import type { RouteParamValue } from '../types';
2
+
3
+ export type SearchValue = string | number | boolean | null | undefined;
4
+ export type SearchInput = URLSearchParams | Record<string, SearchValue | SearchValue[]>;
5
+
6
+ export interface HrefParts {
7
+ params?: Record<string, RouteParamValue>;
8
+ search?: SearchInput;
9
+ hash?: string;
10
+ }
11
+
12
+ export function routeHref(route: string, options: HrefParts = {}) {
13
+ const path = applyTrailingSlash(fillParams(route, options.params ?? {}));
14
+ const search = searchString(options.search);
15
+ const hash = hashString(options.hash);
16
+ return `${path}${search}${hash}`;
17
+ }
18
+
19
+ declare global {
20
+ interface Window {
21
+ __PNEXT_TRAILING_SLASH__?: boolean;
22
+ }
23
+ var __PNEXT_TRAILING_SLASH__: boolean | undefined;
24
+ }
25
+
26
+ // With `trailingSlash: true` the server 308s bare page URLs to their slashed
27
+ // form; Links render the canonical (slashed) href directly, like Next. Set
28
+ // once per process by the server runtime from the resolved config. On the
29
+ // client the value is read from the injected window global (register-render's
30
+ // trailingSlashScript), mirroring basePath / skipTrailingSlash.
31
+ let trailingSlashUrls = false;
32
+
33
+ export function setTrailingSlashUrls(enabled: boolean) {
34
+ trailingSlashUrls = enabled;
35
+ }
36
+
37
+ function isTrailingSlashEnabled(): boolean {
38
+ if (trailingSlashUrls) return true;
39
+ if (process.browser || typeof window !== 'undefined') return window.__PNEXT_TRAILING_SLASH__ === true;
40
+ return globalThis.__PNEXT_TRAILING_SLASH__ === true;
41
+ }
42
+
43
+ export function getTrailingSlashUrls() {
44
+ return isTrailingSlashEnabled();
45
+ }
46
+
47
+ // The configured basePath ('' when unset). Set once per process by the server
48
+ // runtime from the resolved config (registerServerRuntime), mirroring the
49
+ // trailing-slash seam. Core render code (metadata) reads it through
50
+ // withBasePath so file-convention asset hrefs (og-image, manifest) carry the
51
+ // prefix without a compat/next.config import in core.
52
+ let basePathPrefix = '';
53
+
54
+ export function setBasePathPrefix(value: string) {
55
+ basePathPrefix = value;
56
+ }
57
+
58
+ export function getBasePathPrefix() {
59
+ return basePathPrefix;
60
+ }
61
+
62
+ /**
63
+ * Prefix a root-relative in-app path with the configured basePath (Next's
64
+ * addBasePath). Absolute/protocol-relative URLs, non-root-relative strings, and
65
+ * paths already under the basePath are returned unchanged. A no-basePath app is
66
+ * always a no-op. Any query/hash on the path rides along untouched.
67
+ */
68
+ export function withBasePath(path: string) {
69
+ const base = basePathPrefix;
70
+ if (!base) return path;
71
+ if (!path.startsWith('/') || path.startsWith('//')) return path;
72
+ if (path === base || path.startsWith(`${base}/`)) return path;
73
+ return `${base}${path}`;
74
+ }
75
+
76
+ export function applyTrailingSlash(pathname: string) {
77
+ const [, path = '', rest = ''] = /^([^?#]*)([\s\S]*)$/.exec(pathname) ?? [];
78
+ if (!path.startsWith('/')) return pathname;
79
+ if (trailingSlashUrls) {
80
+ // Add the canonical trailing slash (skip root, already-slashed, and files).
81
+ if (path === '/' || path.endsWith('/') || /\.[^/]+$/.test(path)) return pathname;
82
+ return `${path}/${rest}`;
83
+ }
84
+ // trailingSlash:false — Next strips a trailing slash from hrefs so the anchor
85
+ // matches the canonical (unslashed) form and avoids a 308 on click. Root and
86
+ // file paths (`/a.txt/` is not a thing) are left alone.
87
+ if (path === '/' || !path.endsWith('/') || /\.[^/]+\/$/.test(path)) return pathname;
88
+ return `${path.replace(/\/+$/, '')}${rest}`;
89
+ }
90
+
91
+ function fillParams(route: string, params: Record<string, RouteParamValue>) {
92
+ const filled = route
93
+ // Optional catch-all `[[...slug]]` must be matched before the plain
94
+ // catch-all form so its doubled brackets aren't mis-parsed.
95
+ .replace(/\[\[\.\.\.([^\]]+)\]\]/g, (_match, key: string) => encodeParam(params[key], true))
96
+ .replace(/\[\.\.\.([^\]]+)\]/g, (_match, key: string) => encodeParam(params[key], true))
97
+ .replace(/\[([^\]]+)\]/g, (_match, key: string) => encodeParam(params[key], false));
98
+ // An absent optional catch-all (`[[...slug]]`) leaves a dangling `/`; drop it
99
+ // so /docs/:slug* with no params yields /docs, not /docs/.
100
+ return collapseTrailingSlash(filled);
101
+ }
102
+
103
+ function collapseTrailingSlash(pathname: string) {
104
+ if (pathname.length <= 1) return pathname;
105
+ return pathname.replace(/\/+$/, '') || '/';
106
+ }
107
+
108
+ function encodeParam(value: RouteParamValue | undefined, catchAll: boolean) {
109
+ if (Array.isArray(value)) {
110
+ const encoded = value.map(segment => encodeURIComponent(segment));
111
+ return catchAll ? encoded.join('/') : encoded[0] ?? '';
112
+ }
113
+ return encodeURIComponent(value ?? '');
114
+ }
115
+
116
+ function searchString(search: SearchInput | undefined) {
117
+ if (!search) return '';
118
+ const params = search instanceof URLSearchParams ? search : objectSearchParams(search);
119
+ const value = params.toString();
120
+ return value ? `?${value}` : '';
121
+ }
122
+
123
+ function objectSearchParams(search: Record<string, SearchValue | SearchValue[]>) {
124
+ const params = new URLSearchParams();
125
+ for (const [key, value] of Object.entries(search)) {
126
+ const values = Array.isArray(value) ? value : [value];
127
+ for (const item of values) {
128
+ if (item == null) continue;
129
+ params.append(key, String(item));
130
+ }
131
+ }
132
+ return params;
133
+ }
134
+
135
+ function hashString(hash: string | undefined) {
136
+ if (!hash) return '';
137
+ return hash.startsWith('#') ? hash : `#${hash}`;
138
+ }
139
+
140
+ /**
141
+ * Malformed percent-encoding in the request path (e.g. `/%2`, a lone `%` or an
142
+ * out-of-range escape) is a 400 "Bad Request" before any routing, matching
143
+ * Next. Returns the response when the path cannot be decoded, else null.
144
+ */
145
+ export function malformedUrlResponse(request: Request): Response | null {
146
+ let pathname: string;
147
+ try {
148
+ pathname = new URL(request.url).pathname;
149
+ } catch {
150
+ return new Response('Bad Request', { status: 400 });
151
+ }
152
+ if (!pathname.includes('%')) return null;
153
+ try {
154
+ decodeURIComponent(pathname);
155
+ return null;
156
+ } catch {
157
+ return new Response('Bad Request', { status: 400 });
158
+ }
159
+ }
160
+
161
+ /**
162
+ * The canonical form of `pathname` under the given `trailingSlash` setting:
163
+ * with it on, a bare page path gains a trailing slash; with it off, a slashed
164
+ * path loses it. Root (`/`) and file-ish paths (an extension in the last
165
+ * segment) are always left untouched. Returns the pathname unchanged when it is
166
+ * already canonical.
167
+ */
168
+ export function canonicalTrailingSlashPath(pathname: string, trailingSlash: boolean): string {
169
+ if (pathname === '/' || pathname === '') return pathname;
170
+ if (/\.[^/]+$/.test(pathname)) return pathname;
171
+ const slashed = pathname.endsWith('/');
172
+ if (trailingSlash ? slashed : !slashed) return pathname;
173
+ return trailingSlash
174
+ ? `${pathname.replace(/\/+$/, '')}/`
175
+ : pathname.replace(/\/+$/, '') || '/';
176
+ }
177
+
178
+ /**
179
+ * Next-style trailing-slash normalization: with `trailingSlash: true` page
180
+ * URLs 308-redirect to their slashed form; with it off (the default) slashed
181
+ * URLs redirect to the bare form. File-ish paths (extensions) and internal
182
+ * paths are untouched. Returns null when the URL is already canonical.
183
+ */
184
+ export function trailingSlashRedirect(
185
+ config: { trailingSlash?: boolean; skipTrailingSlashRedirect?: boolean },
186
+ url: URL,
187
+ method: string,
188
+ ): Response | null {
189
+ // skipTrailingSlashRedirect: the app serves both slashed and unslashed URLs
190
+ // as-is, so no canonical redirect is ever emitted (route matching already
191
+ // tolerates a trailing slash via normalizePathname).
192
+ if (config.skipTrailingSlashRedirect) return null;
193
+ if (method !== 'GET' && method !== 'HEAD') return null;
194
+ const pathname = url.pathname;
195
+ if (pathname === '/' || pathname === '') return null;
196
+ if (pathname.startsWith('/__pnext') || pathname.startsWith('/assets/')) return null;
197
+ const target = canonicalTrailingSlashPath(pathname, Boolean(config.trailingSlash));
198
+ if (target === pathname) return null;
199
+ return new Response(null, {
200
+ status: 308,
201
+ headers: { location: `${target}${url.search}` },
202
+ });
203
+ }