@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,202 @@
1
+ // HTTP protocol conformance finalizers (COMPAT - may import core freely).
2
+ //
3
+ // Registered as core responseFinalizers when compat.next is on. Each runs before the first flush and
4
+ // mutates the outgoing headers in place to emit the Next wire-protocol headers pnext core deliberately
5
+ // does not know about:
6
+ // - `Vary` merge: append Next's router vary tokens to any existing Vary (user- or middleware-set)
7
+ // instead of overwriting.
8
+ // - `x-nextjs-rewritten-path` / `-query`: on responses whose URL a compat rewrite changed, so the
9
+ // client router can set the canonical URL.
10
+ //
11
+ // Core owns the generic "merge, do not overwrite" discipline; compat owns the exact Next header
12
+ // names and values. A pure-core app registers none of this.
13
+
14
+ import type { ResolvedConfig } from '../config';
15
+ import { getWorkUnit, type WorkUnit } from '../request/context';
16
+ import type {
17
+ RequestInterceptor,
18
+ ResponseFinalizer,
19
+ ResponseFinalizerContext,
20
+ } from '../extensions';
21
+ import { nextCompatEnabled } from '../compat';
22
+
23
+ // Next's router vary tokens appended to every app-router response so a CDN
24
+ // keys its cache on the RSC negotiation headers. Order + spelling match Next.
25
+ const ROUTER_VARY = [
26
+ 'rsc',
27
+ 'next-router-state-tree',
28
+ 'next-router-prefetch',
29
+ 'next-router-segment-prefetch',
30
+ ];
31
+
32
+ /** Rewrite tracking recorded per request by the compat rewrite interceptor. */
33
+ interface RewriteInfo {
34
+ /** Destination pathname, only when it differs from the requested one. */
35
+ path?: string;
36
+ /** Destination query (without leading `?`), only when non-empty. */
37
+ query?: string;
38
+ }
39
+
40
+ // Keyed on the request's work unit (stable for the whole response lifecycle);
41
+ // the interceptor writes it, the finalizer reads it. A WeakMap so completed
42
+ // requests are collected without any explicit teardown.
43
+ const rewrites = new WeakMap<WorkUnit, RewriteInfo>();
44
+
45
+ /**
46
+ * Record that a compat rewrite changed the request URL from `from` to `to`.
47
+ * The response finalizer emits `x-nextjs-rewritten-path/query` from this. A
48
+ * no-op outside a request work unit or when the URL did not actually change.
49
+ */
50
+ export function recordRewrite(from: URL, to: URL): void {
51
+ const unit = getWorkUnit();
52
+ if (!unit) return;
53
+ if (from.pathname === to.pathname && from.search === to.search) return;
54
+ const query = to.search.replace(/^\?/, '');
55
+ // Next reports the path header only when the pathname actually changed, and
56
+ // the query header only when the destination carries a query (a query-only
57
+ // rewrite reports query alone; a path-only rewrite reports path alone).
58
+ rewrites.set(unit, {
59
+ ...(to.pathname !== from.pathname ? { path: to.pathname } : {}),
60
+ ...(query ? { query } : {}),
61
+ });
62
+ }
63
+
64
+ /**
65
+ * Internal marker set by the segment responder's `_rsc` cache-busting 307 so
66
+ * this module's redirect normalization leaves it alone. Never reaches a client.
67
+ */
68
+ export const CACHE_BUSTING_REDIRECT_HEADER = 'x-pnext-cache-bust';
69
+
70
+ const APP_ROUTE_KINDS = new Set<ResponseFinalizerContext['routeKind']>([
71
+ 'html',
72
+ 'data',
73
+ 'route-handler',
74
+ ]);
75
+
76
+ const protocolFinalizer: ResponseFinalizer = ctx => {
77
+ if (!APP_ROUTE_KINDS.has(ctx.routeKind)) return;
78
+ mergeVary(ctx.headers, ROUTER_VARY);
79
+ normalizeRscRedirectStatus(ctx);
80
+ applyRewrittenHeaders(ctx);
81
+ applyEdgeRuntimeHeader(ctx);
82
+ };
83
+
84
+ // Next's edge SSR/route wrappers stamp `x-edge-runtime: 1` on every response
85
+ // from a route whose segment config selects the edge runtime (edge-ssr-app.ts).
86
+ // The matched route's runtime rides on the work-unit response hints.
87
+ function applyEdgeRuntimeHeader(ctx: ResponseFinalizerContext): void {
88
+ const runtime = ctx.hints?.runtime;
89
+ if (runtime === 'edge' || runtime === 'experimental-edge') {
90
+ ctx.headers.set('x-edge-runtime', '1');
91
+ }
92
+ }
93
+
94
+ // Append `values` to the response's Vary header, preserving any existing tokens
95
+ // (user- or middleware-set) and never duplicating (case-insensitive).
96
+ function mergeVary(headers: Headers, values: readonly string[]): void {
97
+ const existing = headers.get('vary');
98
+ const present = new Set(
99
+ (existing ?? '')
100
+ .split(',')
101
+ .map(token => token.trim().toLowerCase())
102
+ .filter(Boolean),
103
+ );
104
+ const merged = existing ? existing.split(',').map(token => token.trim()).filter(Boolean) : [];
105
+ for (const value of values) {
106
+ if (present.has(value.toLowerCase())) continue;
107
+ present.add(value.toLowerCase());
108
+ merged.push(value);
109
+ }
110
+ if (merged.length > 0) headers.set('vary', merged.join(', '));
111
+ }
112
+
113
+ // Emit x-nextjs-rewritten-path/query when a compat rewrite changed this
114
+ // request's URL. Next emits these only on RSC requests (the client router
115
+ // restores the canonical URL from them); plain HTML/navigation responses omit
116
+ // them. Each header is set only when the rewrite recorded that part. Absent on
117
+ // non-rewritten responses (the WeakMap has no entry).
118
+ function applyRewrittenHeaders(ctx: ResponseFinalizerContext): void {
119
+ if (ctx.request.headers.get('rsc') !== '1') return;
120
+ const unit = getWorkUnit();
121
+ if (!unit) return;
122
+ const info = rewrites.get(unit);
123
+ if (!info) return;
124
+ if (info.path !== undefined) ctx.headers.set('x-nextjs-rewritten-path', info.path);
125
+ if (info.query !== undefined) ctx.headers.set('x-nextjs-rewritten-query', info.query);
126
+ }
127
+
128
+ function normalizeRscRedirectStatus(ctx: ResponseFinalizerContext): void {
129
+ // A `_rsc` cache-busting bounce (register-segment) is a CDN key rotation, not
130
+ // an app redirect(): Next sends it as a real 307 that every client follows,
131
+ // so it must survive this normalization. The marker is internal — strip it.
132
+ if (ctx.headers.has(CACHE_BUSTING_REDIRECT_HEADER)) {
133
+ ctx.headers.delete(CACHE_BUSTING_REDIRECT_HEADER);
134
+ return;
135
+ }
136
+ if (ctx.routeKind !== 'html') return;
137
+ if (ctx.request.headers.get('rsc') !== '1') return;
138
+ // pnext's own soft-nav client sends rsc:1 but follows real redirects via
139
+ // fetch (it reads response.url as the final URL); rewriting 307/308 to 200
140
+ // would strand it on the source URL. The 200+Location wire form is only
141
+ // meaningful to Next's client, which never sends x-pnext-soft-nav.
142
+ if (ctx.request.headers.has('x-pnext-soft-nav')) return;
143
+ if (!ctx.headers.has('location')) return;
144
+ if (ctx.status !== 307 && ctx.status !== 308) return;
145
+ ctx.status = 200;
146
+ }
147
+
148
+ // next-router-state-tree validation (Next's parseAndValidateFlightRouterState).
149
+ //
150
+ // Next parses the RSC navigation's router state tree against a schema and lets a parse failure escape as
151
+ // a 500 - a malformed tree is a protocol violation, not something to render around. pnext's renderer
152
+ // ignores the header, so the check lives here, in front of route matching, and is deliberately narrow:
153
+ // only a tree that is unparseable, not an array, or whose parallel-routes slot is not an object is
154
+ // rejected. Anything Next's own client can send passes untouched.
155
+
156
+ function isValidFlightRouterState(value: unknown): boolean {
157
+ if (!Array.isArray(value)) return false;
158
+ const [segment, parallelRoutes] = value as unknown[];
159
+ if (typeof segment !== 'string' && !Array.isArray(segment)) return false;
160
+ if (typeof parallelRoutes !== 'object' || parallelRoutes === null) return false;
161
+ if (Array.isArray(parallelRoutes)) return false;
162
+ return Object.values(parallelRoutes as Record<string, unknown>).every(isValidFlightRouterState);
163
+ }
164
+
165
+ const routerStateTreeInterceptor: RequestInterceptor = request =>
166
+ Promise.resolve(validateRouterStateTree(request));
167
+
168
+ function validateRouterStateTree(request: Request): Response | undefined {
169
+ if (request.headers.get('rsc') !== '1') return undefined;
170
+ // pnext's own client mirrors its richer nav-state object into this header for
171
+ // wire compat (api/router.ts); that object is not a FlightRouterState and is
172
+ // never what this check is about.
173
+ if (request.headers.has('x-pnext-soft-nav')) return undefined;
174
+ const header = request.headers.get('next-router-state-tree');
175
+ if (header === null) return undefined;
176
+
177
+ let tree: unknown;
178
+ try {
179
+ tree = JSON.parse(decodeURIComponent(header));
180
+ } catch {
181
+ try {
182
+ tree = JSON.parse(header);
183
+ } catch {
184
+ return new Response('Internal Server Error', { status: 500 });
185
+ }
186
+ }
187
+ return isValidFlightRouterState(tree)
188
+ ? undefined
189
+ : new Response('Internal Server Error', { status: 500 });
190
+ }
191
+
192
+ /** The compat request interceptors this module owns, gated on compat.next. */
193
+ export function protocolInterceptors(config: ResolvedConfig): RequestInterceptor[] {
194
+ if (!nextCompatEnabled(config)) return [];
195
+ return [routerStateTreeInterceptor];
196
+ }
197
+
198
+ /** The compat protocol finalizers, gated on compat.next. */
199
+ export function protocolFinalizers(config: ResolvedConfig): ResponseFinalizer[] {
200
+ if (!nextCompatEnabled(config)) return [];
201
+ return [protocolFinalizer];
202
+ }
@@ -0,0 +1,59 @@
1
+ export {
2
+ Children,
3
+ Component,
4
+ Fragment,
5
+ PureComponent,
6
+ StrictMode,
7
+ Suspense,
8
+ SuspenseList,
9
+ cloneElement,
10
+ createContext,
11
+ createElement,
12
+ createFactory,
13
+ createPortal,
14
+ createRef,
15
+ default,
16
+ findDOMNode,
17
+ flushSync,
18
+ forwardRef,
19
+ hydrate,
20
+ isFragment,
21
+ isMemo,
22
+ isValidElement,
23
+ lazy,
24
+ memo,
25
+ render,
26
+ startTransition,
27
+ unmountComponentAtNode,
28
+ unstable_batchedUpdates,
29
+ useCallback,
30
+ useContext,
31
+ useDebugValue,
32
+ useDeferredValue,
33
+ useEffect,
34
+ useId,
35
+ useImperativeHandle,
36
+ useInsertionEffect,
37
+ useLayoutEffect,
38
+ useMemo,
39
+ useReducer,
40
+ useRef,
41
+ useState,
42
+ use,
43
+ useActionState,
44
+ useOptimistic,
45
+ useSyncExternalStore,
46
+ useTransition,
47
+ version,
48
+ } from './preact';
49
+ export { ViewTransition, addTransitionType } from './view-transition';
50
+
51
+ export function cache<Args extends unknown[], Result>(
52
+ fn: (...args: Args) => Result,
53
+ ): (...args: Args) => Result {
54
+ return (...args: Args) => fn(...args);
55
+ }
56
+
57
+ export function cacheSignal() {
58
+ return null;
59
+ }
@@ -0,0 +1,60 @@
1
+ // React Compiler runtime shim (COMPAT).
2
+ //
3
+ // React Compiler-compiled code - both the app's own source and pre-compiled node_modules libraries -
4
+ // imports the memo-cache helper `c` from `react/compiler-runtime` (React 19 built-in) or the standalone
5
+ // `react-compiler-runtime` npm package (React 17/18 target).
6
+ //
7
+ // The npm `react-compiler-runtime` does `require('react')` and calls `React.useMemo`. Under pnext that
8
+ // bare require resolves to the REAL react package, whose `useMemo` reads a null dispatcher during
9
+ // preact's SSR and throws. So BOTH runtime specifiers are aliased to this shim, which implements the
10
+ // identical memo-cache over preact's own `useMemo` - no real-react dependency, works in SSR and in the
11
+ // browser.
12
+ //
13
+ // `c(size)` returns a per-render array seeded with a sentinel, exactly the fallback path in the upstream
14
+ // runtime. The remaining exports are only emitted in development/validation builds; pnext ships the
15
+ // production runtime, so they are minimal stubs to keep any stray import resolvable.
16
+
17
+ import { useMemo } from 'preact/compat';
18
+
19
+ const $empty = Symbol.for('react.memo_cache_sentinel');
20
+
21
+ /** React Compiler memo-cache allocator (production fallback over preact useMemo). */
22
+ export function c(size: number): unknown[] {
23
+ return useMemo<unknown[]>(() => {
24
+ const cache: unknown[] = new Array<unknown>(size);
25
+ for (let index = 0; index < size; index++) cache[index] = $empty;
26
+ (cache as unknown as Record<symbol, boolean>)[$empty] = true;
27
+ return cache;
28
+ }, []);
29
+ }
30
+
31
+ /** Reset a memo-cache array back to sentinels (dev-only in upstream). */
32
+ export function $reset(cache: unknown[]): void {
33
+ for (let index = 0; index < cache.length; index++) cache[index] = $empty;
34
+ }
35
+
36
+ export function $makeReadOnly<T>(value: T): T {
37
+ return value;
38
+ }
39
+
40
+ // Dispatcher guards / structural check are dev-only validation hooks; ship as
41
+ // no-ops so a stray import in a mixed build stays resolvable.
42
+ export function $dispatcherGuard(_kind: number): void {
43
+ /* no-op: dev-only dispatcher guard */
44
+ }
45
+
46
+ export function $structuralCheck(): void {
47
+ /* no-op: dev-only structural equality check */
48
+ }
49
+
50
+ export const renderCounterRegistry = new Map<string, Set<{ count: number }>>();
51
+
52
+ export function clearRenderCounterRegistry(): void {
53
+ for (const counters of renderCounterRegistry.values()) {
54
+ for (const counter of counters) counter.count = 0;
55
+ }
56
+ }
57
+
58
+ export function useRenderCounter(_name: string): void {
59
+ /* no-op: dev-only render counter */
60
+ }
@@ -0,0 +1,115 @@
1
+ /**
2
+ * `react-dom` client-only compat surface.
3
+ *
4
+ * This variant omits any `request/context` dependency so esbuild can compile pure
5
+ * browser bundles. Resource hints are no-ops here; they are only meaningful in the
6
+ * server response-finalization pipeline.
7
+ */
8
+ import ReactCompat from 'preact/compat';
9
+ import { useActionState } from './preact';
10
+
11
+ const ReactDomCompat = Object.assign(ReactCompat, {
12
+ preload,
13
+ preconnect,
14
+ prefetchDNS,
15
+ preinit,
16
+ });
17
+
18
+ export default ReactDomCompat;
19
+
20
+ // The react-dom surface preact/compat provides (named, because preact/compat's
21
+ // types use `export =` which blocks `export *`).
22
+ export const render = ReactCompat.render;
23
+ export const hydrate = ReactCompat.hydrate;
24
+ export const createPortal = ReactCompat.createPortal;
25
+ export const findDOMNode = ReactCompat.findDOMNode;
26
+ export const flushSync = ReactCompat.flushSync;
27
+ export const unmountComponentAtNode = ReactCompat.unmountComponentAtNode;
28
+ export const unstable_batchedUpdates = ReactCompat.unstable_batchedUpdates;
29
+ export const version = ReactCompat.version;
30
+
31
+ function noOpRecordResourceHint() {
32
+ // intentionally empty: client bundles do not build response headers.
33
+ }
34
+
35
+ export function preload(_href: string, _options?: { as?: string; crossOrigin?: string }) {
36
+ noOpRecordResourceHint();
37
+ // no-op on client; header collection happens in server response finalizers.
38
+ }
39
+
40
+ export function preconnect(_href: string, _options?: { crossOrigin?: string }) {
41
+ noOpRecordResourceHint();
42
+ // no-op on client; header collection happens in server response finalizers.
43
+ }
44
+
45
+ export function prefetchDNS(_href: string) {
46
+ noOpRecordResourceHint();
47
+ // no-op on client; header collection happens in server response finalizers.
48
+ }
49
+
50
+ export function preinit(_href: string, _options?: { as?: string; crossOrigin?: string }) {
51
+ noOpRecordResourceHint();
52
+ // no-op on client; header collection happens in server response finalizers.
53
+ }
54
+
55
+ export const useFormState = useActionState;
56
+
57
+ export interface FormStatusNotPending {
58
+ pending: false;
59
+ data: null;
60
+ method: null;
61
+ action: null;
62
+ }
63
+
64
+ export interface FormStatusPending {
65
+ pending: true;
66
+ data: FormData;
67
+ method: string;
68
+ action: string | ((formData: FormData) => void | Promise<void>);
69
+ }
70
+
71
+ export type FormStatus = FormStatusPending | FormStatusNotPending;
72
+
73
+ const NOT_PENDING: FormStatusNotPending = {
74
+ pending: false,
75
+ data: null,
76
+ method: null,
77
+ action: null,
78
+ };
79
+
80
+ // Form-status store, fed by the client entry's function-form-action runtime.
81
+ const pendingStack: FormStatusPending[] = [];
82
+ const listeners = new Set<() => void>();
83
+ let storeVersion = 0;
84
+
85
+ /** Called by the client runtime when a function form action starts/finishes. */
86
+ export function __pnextPushFormStatus(status: FormStatusPending): () => void {
87
+ pendingStack.push(status);
88
+ notify();
89
+ return () => {
90
+ const index = pendingStack.indexOf(status);
91
+ if (index !== -1) pendingStack.splice(index, 1);
92
+ notify();
93
+ };
94
+ }
95
+
96
+ function notify() {
97
+ storeVersion += 1;
98
+ for (const listener of [...listeners]) listener();
99
+ }
100
+
101
+ function subscribe(listener: () => void) {
102
+ listeners.add(listener);
103
+ return () => {
104
+ listeners.delete(listener);
105
+ };
106
+ }
107
+
108
+ export function useFormStatus(): FormStatus {
109
+ ReactCompat.useSyncExternalStore(subscribe, () => storeVersion);
110
+ return pendingStack.length > 0 ? pendingStack[pendingStack.length - 1]! : NOT_PENDING;
111
+ }
112
+
113
+ export function requestFormReset(form: HTMLFormElement) {
114
+ if (form && typeof form.reset === 'function') form.reset();
115
+ }
@@ -0,0 +1,20 @@
1
+ import ReactDomCompat, {
2
+ preconnect,
3
+ prefetchDNS,
4
+ preinit,
5
+ preload,
6
+ version,
7
+ } from './dom';
8
+
9
+ const ReactDomServer = { ...ReactDomCompat } as typeof ReactDomCompat & {
10
+ useFormState?: unknown;
11
+ useFormStatus?: unknown;
12
+ requestFormReset?: unknown;
13
+ };
14
+
15
+ delete ReactDomServer.useFormState;
16
+ delete ReactDomServer.useFormStatus;
17
+ delete ReactDomServer.requestFormReset;
18
+
19
+ export { preconnect, prefetchDNS, preinit, preload, version };
20
+ export default ReactDomServer;
@@ -0,0 +1,40 @@
1
+ /**
2
+ * `react-dom/server` compat surface (also aliased for `react-dom/server.edge`
3
+ * and `react-dom/server.browser`). Next fixtures import react-dom/server inside
4
+ * client components for the synchronous string renderers (renderToStaticMarkup,
5
+ * renderToString). preact-render-to-string provides those; the streaming
6
+ * server APIs (renderToReadableStream / renderToPipeableStream) have no direct
7
+ * preact equivalent that fits a browser bundle, so they throw if invoked.
8
+ */
9
+ import ReactCompat from 'preact/compat';
10
+ import {
11
+ renderToStaticMarkup as preactRenderToStaticMarkup,
12
+ renderToString as preactRenderToString,
13
+ renderToStringAsync as preactRenderToStringAsync,
14
+ } from 'preact-render-to-string';
15
+
16
+ export const version = ReactCompat.version;
17
+ export const renderToString = preactRenderToString;
18
+ export const renderToStaticMarkup = preactRenderToStaticMarkup;
19
+ export const renderToStringAsync = preactRenderToStringAsync;
20
+
21
+ function unsupportedStream(name: string): never {
22
+ throw new Error(`react-dom/server ${name} is not supported under pnext's preact runtime`);
23
+ }
24
+
25
+ export function renderToReadableStream(): never {
26
+ return unsupportedStream('renderToReadableStream');
27
+ }
28
+
29
+ export function renderToPipeableStream(): never {
30
+ return unsupportedStream('renderToPipeableStream');
31
+ }
32
+
33
+ export default {
34
+ version,
35
+ renderToString,
36
+ renderToStaticMarkup,
37
+ renderToStringAsync,
38
+ renderToReadableStream,
39
+ renderToPipeableStream,
40
+ };
@@ -0,0 +1,154 @@
1
+ /**
2
+ * `react-dom` compat surface. preact/compat provides almost everything; this
3
+ * wrapper adds the form-action hooks React ships in react-dom:
4
+ *
5
+ * - useFormState: the deprecated alias of React.useActionState (identical
6
+ * signature); implemented in ./preact.
7
+ * - useFormStatus: pending status of the nearest enclosing <form action={fn}>.
8
+ * Approximated: the pnext client runtime reports in-flight function form
9
+ * actions to a module store and the hook subscribes to it. When exactly one
10
+ * submission is in flight (the overwhelmingly common case) the status is
11
+ * exact; concurrent submissions from different forms all read as pending.
12
+ * On the server it reports not-pending (matching React SSR).
13
+ * - requestFormReset: passthrough to form.reset(); the client runtime resets
14
+ * uncontrolled forms after a successful action itself.
15
+ */
16
+ import ReactCompat from 'preact/compat';
17
+ import { useActionState } from './preact';
18
+ import { registerResourceHint } from '../../render/resource-hints';
19
+
20
+ const ReactDomCompat = Object.assign(ReactCompat, {
21
+ preload,
22
+ preconnect,
23
+ prefetchDNS,
24
+ preinit,
25
+ });
26
+
27
+ export default ReactDomCompat;
28
+
29
+ // The react-dom surface preact/compat provides (named, because preact/compat's
30
+ // types use `export =` which blocks `export *`).
31
+ export const render = ReactCompat.render;
32
+ export const hydrate = ReactCompat.hydrate;
33
+ export const createPortal = ReactCompat.createPortal;
34
+ export const findDOMNode = ReactCompat.findDOMNode;
35
+ export const flushSync = ReactCompat.flushSync;
36
+ export const unmountComponentAtNode = ReactCompat.unmountComponentAtNode;
37
+ export const unstable_batchedUpdates = ReactCompat.unstable_batchedUpdates;
38
+ export const version = ReactCompat.version;
39
+
40
+ export function preload(
41
+ href: string,
42
+ options?: {
43
+ as?: string;
44
+ crossOrigin?: string;
45
+ type?: string;
46
+ fetchPriority?: string;
47
+ media?: string;
48
+ nonce?: string;
49
+ },
50
+ ) {
51
+ registerResourceHint({
52
+ rel: 'preload',
53
+ url: href,
54
+ ...(options?.as ? { as: options.as } : {}),
55
+ ...(options?.type ? { type: options.type } : {}),
56
+ ...(options?.fetchPriority
57
+ ? { fetchPriority: options.fetchPriority as 'high' | 'low' | 'auto' }
58
+ : {}),
59
+ ...(options?.media ? { media: options.media } : {}),
60
+ ...(options?.nonce ? { nonce: options.nonce } : {}),
61
+ ...(options?.crossOrigin
62
+ ? { crossOrigin: options.crossOrigin as 'anonymous' | 'use-credentials' | '' }
63
+ : {}),
64
+ });
65
+ }
66
+
67
+ export function preconnect(href: string, options?: { crossOrigin?: string }) {
68
+ registerResourceHint({
69
+ rel: 'preconnect',
70
+ url: href,
71
+ ...(options?.crossOrigin
72
+ ? { crossOrigin: options.crossOrigin as 'anonymous' | 'use-credentials' | '' }
73
+ : {}),
74
+ });
75
+ }
76
+
77
+ export function prefetchDNS(href: string) {
78
+ registerResourceHint({ rel: 'dns-prefetch', url: href });
79
+ }
80
+
81
+ export function preinit(href: string, options?: { as?: string; crossOrigin?: string }) {
82
+ registerResourceHint({
83
+ rel: 'preload',
84
+ url: href,
85
+ ...(options?.as ? { as: options.as } : {}),
86
+ ...(options?.crossOrigin
87
+ ? { crossOrigin: options.crossOrigin as 'anonymous' | 'use-credentials' | '' }
88
+ : {}),
89
+ });
90
+ }
91
+
92
+ export const useFormState = useActionState;
93
+
94
+ export interface FormStatusNotPending {
95
+ pending: false;
96
+ data: null;
97
+ method: null;
98
+ action: null;
99
+ }
100
+
101
+ export interface FormStatusPending {
102
+ pending: true;
103
+ data: FormData;
104
+ method: string;
105
+ action: string | ((formData: FormData) => void | Promise<void>);
106
+ }
107
+
108
+ export type FormStatus = FormStatusPending | FormStatusNotPending;
109
+
110
+ const NOT_PENDING: FormStatusNotPending = {
111
+ pending: false,
112
+ data: null,
113
+ method: null,
114
+ action: null,
115
+ };
116
+
117
+ // Form-status store, fed by the client entry's function-form-action runtime.
118
+ const pendingStack: FormStatusPending[] = [];
119
+ const listeners = new Set<() => void>();
120
+ let storeVersion = 0;
121
+
122
+ /** Called by the client runtime when a function form action starts/finishes. */
123
+ export function __pnextPushFormStatus(status: FormStatusPending): () => void {
124
+ pendingStack.push(status);
125
+ notify();
126
+ return () => {
127
+ const index = pendingStack.indexOf(status);
128
+ if (index !== -1) pendingStack.splice(index, 1);
129
+ notify();
130
+ };
131
+ }
132
+
133
+ function notify() {
134
+ storeVersion += 1;
135
+ for (const listener of [...listeners]) listener();
136
+ }
137
+
138
+ function subscribe(listener: () => void) {
139
+ listeners.add(listener);
140
+ return () => {
141
+ listeners.delete(listener);
142
+ };
143
+ }
144
+
145
+ export function useFormStatus(): FormStatus {
146
+ ReactCompat.useSyncExternalStore(subscribe, () => storeVersion);
147
+ return pendingStack.length > 0 ? pendingStack[pendingStack.length - 1]! : NOT_PENDING;
148
+ }
149
+
150
+ export function requestFormReset(form: HTMLFormElement) {
151
+ if (form && typeof form.reset === 'function') form.reset();
152
+ }
153
+
154
+ Object.assign(ReactDomCompat, { useFormState, useFormStatus, requestFormReset });