@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,551 @@
1
+ // Render-time vary-params tracking (COMPAT - may import core freely). A port of Next's vary-params
2
+ // accumulator model.
3
+ //
4
+ // While a route renders, every `params` / `searchParams` read is recorded into an accumulator so the
5
+ // response can tell the client WHICH params a segment actually depends on. A segment whose vary set is
6
+ // empty is shareable across every param value of its route - the client segment cache then keys one entry
7
+ // for all of them.
8
+ //
9
+ // pnext's segment model is "whole route as one segment", so the accumulators collapse into three
10
+ // published sets per response:
11
+ // - `params` : union of every BODY segment's accesses (page + layouts) + root
12
+ // - `head` : generateMetadata/generateViewport accesses + root
13
+ // - `root` : next/root-params accesses, merged into both sets above
14
+ // plus a boolean `search` flag, the client-visible form of Next's `'?'` sentinel (searchParams have no
15
+ // fixed schema, so ANY access varies the whole query).
16
+ //
17
+ // Process-wide state (the ALS) is anchored on globalThis: a pnext module can be loaded twice (built copy
18
+ // vs original) and a split ALS would silently drop every access recorded by the other copy.
19
+
20
+ import { AsyncLocalStorage } from 'node:async_hooks';
21
+ import { SEARCH_PARAMS_SENTINEL } from './vary-key';
22
+
23
+ // Pure keying/encoding helpers live in ./vary-key (browser-safe — the client
24
+ // segment cache imports them); re-exported here for server callers.
25
+ export {
26
+ SEARCH_PARAMS_SENTINEL,
27
+ VARY_KEY_SEPARATOR,
28
+ encodeVarySet,
29
+ decodeVarySet,
30
+ segmentVaryCacheKey,
31
+ } from './vary-key';
32
+
33
+ /** The set of param names a segment read. `'?'` means "any searchParams access". */
34
+ export type VaryParams = Set<string>;
35
+
36
+ /**
37
+ * Which segment of the route an accumulator belongs to.
38
+ *
39
+ * The response used to UNION every body accumulator into one published set, which collapses Next's
40
+ * central per-segment property - a LAYOUT that reads `[category, item]` forced the PAGE, which read only
41
+ * `category`, to key on both, so two items of one category could never reuse the page. The accumulators
42
+ * now carry their segment kind so the response can publish the layout and page sets side by side and the
43
+ * client can re-fetch one frame while reusing the other.
44
+ */
45
+ export type VarySegmentKind = 'page' | 'layout' | 'head' | 'root';
46
+
47
+ /**
48
+ * Accumulates vary params for a single segment (or for metadata/rootParams). Mirrors Next's
49
+ * `VaryParamsAccumulator`, including the React thenable protocol fields (status/value/then). pnext has no
50
+ * Flight wire, so the thenable is never serialized - but keeping the shape identical means the Next port
51
+ * reads one-to-one and a future Flight upgrade needs no rework.
52
+ */
53
+ export interface VaryParamsAccumulator {
54
+ /** Which route segment this accumulator tracks (page / layout / head / root). */
55
+ kind: VarySegmentKind;
56
+ /** Mutable during render — accumulates param access. */
57
+ varyParams: VaryParams;
58
+ /** React thenable protocol: 'pending' until finishAccumulatingVaryParams. */
59
+ status: 'pending' | 'fulfilled';
60
+ value: VaryParams;
61
+ then(onfulfilled?: ((value: VaryParams) => unknown) | null): void;
62
+ /** Callbacks waiting for resolution. */
63
+ resolvers: ((value: VaryParams) => unknown)[];
64
+ }
65
+
66
+ /**
67
+ * Per-response accumulator bag. `segments` grows as each segment's props are
68
+ * created; `head` and `rootParams` are allocated up front.
69
+ */
70
+ export interface ResponseVaryParamsAccumulator {
71
+ /** metadata/viewport (the "head" segment). */
72
+ head: VaryParamsAccumulator;
73
+ /** `next/root-params` accesses; merged into every other segment's set. */
74
+ rootParams: VaryParamsAccumulator;
75
+ /** One accumulator per rendered body segment (page + layouts). */
76
+ segments: Set<VaryParamsAccumulator>;
77
+ }
78
+
79
+ export function createSegmentVaryParamsAccumulator(
80
+ kind: VarySegmentKind = 'page',
81
+ ): VaryParamsAccumulator {
82
+ const accumulator: VaryParamsAccumulator = {
83
+ kind,
84
+ varyParams: new Set<string>(),
85
+ status: 'pending',
86
+ value: new Set<string>(),
87
+ then(onfulfilled) {
88
+ if (!onfulfilled) return;
89
+ if (accumulator.status === 'pending') accumulator.resolvers.push(onfulfilled);
90
+ else onfulfilled(accumulator.value);
91
+ },
92
+ resolvers: [],
93
+ };
94
+ return accumulator;
95
+ }
96
+
97
+ const emptySet: VaryParams = new Set<string>();
98
+
99
+ /**
100
+ * A singleton accumulator already resolved to an empty set, for segments known
101
+ * up front to read no params (client components, segments with no user code).
102
+ */
103
+ export const emptyVaryParamsAccumulator: VaryParamsAccumulator = {
104
+ kind: 'page',
105
+ varyParams: emptySet,
106
+ status: 'fulfilled',
107
+ value: emptySet,
108
+ then(onfulfilled) {
109
+ if (onfulfilled) onfulfilled(emptySet);
110
+ },
111
+ resolvers: [],
112
+ };
113
+
114
+ export function createResponseVaryParamsAccumulator(): ResponseVaryParamsAccumulator {
115
+ return {
116
+ head: createSegmentVaryParamsAccumulator('head'),
117
+ rootParams: createSegmentVaryParamsAccumulator('root'),
118
+ segments: new Set<VaryParamsAccumulator>(),
119
+ };
120
+ }
121
+
122
+ // ---------------------------------------------------------------------------
123
+ // Response scope (ALS).
124
+ // ---------------------------------------------------------------------------
125
+
126
+ const VARY_PARAMS_GLOBALS = Symbol.for('pnext.compat.varyParamsGlobals');
127
+
128
+ function varyGlobal<T>(name: string, create: () => T): T {
129
+ const root = globalThis as Record<PropertyKey, unknown>;
130
+ const container = (root[VARY_PARAMS_GLOBALS] ??= {}) as Record<string, unknown>;
131
+ if (!(name in container)) container[name] = create();
132
+ return container[name] as T;
133
+ }
134
+
135
+ const varyStorage = varyGlobal(
136
+ 'storage',
137
+ () => new AsyncLocalStorage<ResponseVaryParamsAccumulator>(),
138
+ );
139
+
140
+ /** The response accumulator of the render in progress, or null outside one. */
141
+ export function currentResponseVaryParams(): ResponseVaryParamsAccumulator | null {
142
+ return varyStorage.getStore() ?? null;
143
+ }
144
+
145
+ /**
146
+ * Allocate a fresh body-segment accumulator inside the current response scope. Null when nothing is
147
+ * tracking (a build prerender outside the segment path, a `use cache` producer, a route handler) - callers
148
+ * then skip the proxy entirely.
149
+ */
150
+ export function createVaryParamsAccumulator(
151
+ kind: 'page' | 'layout' = 'page',
152
+ ): VaryParamsAccumulator | null {
153
+ const response = varyStorage.getStore();
154
+ if (!response) return null;
155
+ const accumulator = createSegmentVaryParamsAccumulator(kind);
156
+ response.segments.add(accumulator);
157
+ return accumulator;
158
+ }
159
+
160
+ /** The HEAD (metadata/viewport) accumulator of the current response, or null. */
161
+ export function getMetadataVaryParamsAccumulator(): VaryParamsAccumulator | null {
162
+ return varyStorage.getStore()?.head ?? null;
163
+ }
164
+
165
+ // metadata and viewport ship in one payload, so they share one accumulator.
166
+ export const getViewportVaryParamsAccumulator = getMetadataVaryParamsAccumulator;
167
+
168
+ /** The rootParams accumulator of the current response, or null. */
169
+ export function getRootParamsVaryParamsAccumulator(): VaryParamsAccumulator | null {
170
+ return varyStorage.getStore()?.rootParams ?? null;
171
+ }
172
+
173
+ /** Record that `paramName` was read by the segment owning `accumulator`. */
174
+ export function accumulateVaryParam(
175
+ accumulator: VaryParamsAccumulator,
176
+ paramName: string,
177
+ ): void {
178
+ // The shared empty accumulator is immutable — never widen it.
179
+ if (accumulator === emptyVaryParamsAccumulator) return;
180
+ accumulator.varyParams.add(paramName);
181
+ }
182
+
183
+ /** Record a `next/root-params` access (varies every segment of the response). */
184
+ export function accumulateRootVaryParam(paramName: string): void {
185
+ const accumulator = getRootParamsVaryParamsAccumulator();
186
+ if (accumulator !== null) accumulateVaryParam(accumulator, paramName);
187
+ }
188
+
189
+ // ---------------------------------------------------------------------------
190
+ // Tracking views over params / searchParams.
191
+ // ---------------------------------------------------------------------------
192
+
193
+ /**
194
+ * Marker exposing the ORIGINAL (untracked) object behind a tracking view, so a second consumer -
195
+ * `generateMetadata`, whose accesses belong to the HEAD segment, not the page's - can re-wrap the same
196
+ * data against its own accumulator instead of reading through, and widening, the page's set.
197
+ *
198
+ * A symbol key: it never enumerates, never serializes, and never collides with a param literally named
199
+ * `then`/`value`/`status`.
200
+ */
201
+ const VARY_TRACKING = Symbol.for('pnext.compat.varyTracking');
202
+
203
+ export interface VaryTrackingInfo {
204
+ raw: Record<string, unknown>;
205
+ kind: 'params' | 'searchParams';
206
+ optionalCatchAllParam: string | null;
207
+ }
208
+
209
+ function markVaryTracking(target: object, info: VaryTrackingInfo): void {
210
+ Object.defineProperty(target, VARY_TRACKING, {
211
+ value: info,
212
+ enumerable: false,
213
+ configurable: true,
214
+ });
215
+ }
216
+
217
+ /** The tracking info behind a params/searchParams view, or null when untracked. */
218
+ export function varyTrackingInfo(value: unknown): VaryTrackingInfo | null {
219
+ if (typeof value !== 'object' || value === null) return null;
220
+ const info = (value as Record<PropertyKey, unknown>)[VARY_TRACKING];
221
+ return (info as VaryTrackingInfo | undefined) ?? null;
222
+ }
223
+
224
+ /**
225
+ * A `params` object that records which params the render reads. Two strategies, exactly as Next:
226
+ * - With an optional catch-all param at its empty value the key is ABSENT from the object, so only a
227
+ * Proxy can observe reads of it. The traps also track enumeration (`ownKeys`) and `in` checks (`has`),
228
+ * because user code branching on "is the slug present?" depends on the param.
229
+ * - Otherwise every param is an own property, so per-property getters suffice - faster than Proxy traps,
230
+ * and `Object.keys()` alone does NOT vary, matching Next: enumerating names without reading values
231
+ * does not make the segment param-dependent.
232
+ *
233
+ * PROTOCOL-NAME SAFETY: a param literally named `then`, `value` or `status` must be delivered as plain
234
+ * data. Both strategies read through to the ORIGINAL params object, never to the accumulator, so the
235
+ * thenable protocol can never leak into user-visible params. Reads of a name that is NOT a param - which
236
+ * `await`/`Promise.resolve` probe for - are ignored, so awaiting the params promise never pollutes the
237
+ * vary set.
238
+ */
239
+ export function createVaryingParams<T extends Record<string, unknown>>(
240
+ accumulator: VaryParamsAccumulator,
241
+ params: T,
242
+ optionalCatchAllParamName: string | null = null,
243
+ ): T {
244
+ const originalParams = normalizeOptionalCatchAllParams(params, optionalCatchAllParamName);
245
+ const info: VaryTrackingInfo = {
246
+ raw: originalParams,
247
+ kind: 'params',
248
+ optionalCatchAllParam: optionalCatchAllParamName,
249
+ };
250
+ if (optionalCatchAllParamName !== null) {
251
+ return new Proxy(originalParams, {
252
+ get(target, prop, receiver) {
253
+ if (prop === VARY_TRACKING) return info;
254
+ if (
255
+ typeof prop === 'string' &&
256
+ (prop === optionalCatchAllParamName ||
257
+ Object.prototype.hasOwnProperty.call(target, prop))
258
+ ) {
259
+ accumulateVaryParam(accumulator, prop);
260
+ }
261
+ return Reflect.get(target, prop, receiver);
262
+ },
263
+ has(target, prop) {
264
+ if (prop === optionalCatchAllParamName) {
265
+ accumulateVaryParam(accumulator, optionalCatchAllParamName);
266
+ }
267
+ return Reflect.has(target, prop);
268
+ },
269
+ ownKeys(target) {
270
+ // Enumeration means user code may branch on which params exist, so the
271
+ // (possibly absent) optional param counts as read.
272
+ accumulateVaryParam(accumulator, optionalCatchAllParamName);
273
+ return Reflect.ownKeys(target);
274
+ },
275
+ });
276
+ }
277
+
278
+ const tracked = {} as Record<string, unknown>;
279
+ for (const paramName in originalParams) {
280
+ Object.defineProperty(tracked, paramName, {
281
+ get() {
282
+ accumulateVaryParam(accumulator, paramName);
283
+ return originalParams[paramName];
284
+ },
285
+ enumerable: true,
286
+ configurable: true,
287
+ });
288
+ }
289
+ markVaryTracking(tracked, info);
290
+ return tracked as T;
291
+ }
292
+
293
+ /**
294
+ * An optional catch-all at its EMPTY value carries no param in Next - `/optional-catchall` renders with
295
+ * `params` = `{}`, so `params.slug` is undefined and `'slug' in params` is false, which is how the
296
+ * fixtures distinguish "no slug" from a real slug. pnext's matcher fills the key with an empty array,
297
+ * which reads as a present-but-empty slug; strip it here, at the compat boundary that hands params to
298
+ * user code, so the page, generateMetadata and the tracking traps all see Next's shape.
299
+ *
300
+ * A copy is returned only when the key needs removing; otherwise the original object passes through,
301
+ * since identity matters for the tracking info's `raw`.
302
+ */
303
+ export function normalizeOptionalCatchAllParams<T extends Record<string, unknown>>(
304
+ params: T,
305
+ optionalCatchAllParamName: string | null,
306
+ ): T {
307
+ if (optionalCatchAllParamName === null) return params;
308
+ const value = params[optionalCatchAllParamName];
309
+ if (!Array.isArray(value) || value.length > 0) return params;
310
+ const { [optionalCatchAllParamName]: _empty, ...rest } = params;
311
+ return rest as unknown as T;
312
+ }
313
+
314
+ /**
315
+ * A `searchParams` object where ANY access - a read, an `in` check, or enumeration - records the `'?'`
316
+ * sentinel. Search params have no fixed schema, so the segment is keyed by the whole query string rather
317
+ * than per-key. A Proxy, not getters, is required so enumerating an EMPTY searchParams object still varies.
318
+ */
319
+ export function createVaryingSearchParams<T extends Record<string, unknown>>(
320
+ accumulator: VaryParamsAccumulator,
321
+ originalSearchParams: T,
322
+ ): T {
323
+ const info: VaryTrackingInfo = {
324
+ raw: originalSearchParams,
325
+ kind: 'searchParams',
326
+ optionalCatchAllParam: null,
327
+ };
328
+ return new Proxy(originalSearchParams, {
329
+ get(target, prop, receiver) {
330
+ if (prop === VARY_TRACKING) return info;
331
+ // `then` is probed by `await`/`Promise.resolve` on the wrapping promise;
332
+ // searchParams never legitimately carries it as a protocol member, but a
333
+ // real `?then=` query value must still read through as data. Only skip
334
+ // the sentinel when the key is absent (the probe case).
335
+ if (typeof prop === 'string' && !isPromiseProbe(target, prop)) {
336
+ accumulateVaryParam(accumulator, SEARCH_PARAMS_SENTINEL);
337
+ }
338
+ return Reflect.get(target, prop, receiver);
339
+ },
340
+ has(target, prop) {
341
+ if (typeof prop === 'string') accumulateVaryParam(accumulator, SEARCH_PARAMS_SENTINEL);
342
+ return Reflect.has(target, prop);
343
+ },
344
+ ownKeys(target) {
345
+ accumulateVaryParam(accumulator, SEARCH_PARAMS_SENTINEL);
346
+ return Reflect.ownKeys(target);
347
+ },
348
+ });
349
+ }
350
+
351
+ /** True for a `then` read on an object that has no such key (promise probing). */
352
+ function isPromiseProbe(target: object, prop: string): boolean {
353
+ return prop === 'then' && !Object.prototype.hasOwnProperty.call(target, prop);
354
+ }
355
+
356
+ // ---------------------------------------------------------------------------
357
+ // Finishing + publishing.
358
+ // ---------------------------------------------------------------------------
359
+
360
+ /**
361
+ * Resolve every accumulator, merging the response's root params into each.
362
+ * Call once rendering is complete. Accumulators left pending are treated as
363
+ * "unknown vary params" by the client (it then keys on the exact URL).
364
+ */
365
+ export function finishAccumulatingVaryParams(response: ResponseVaryParamsAccumulator): void {
366
+ const rootVaryParams = response.rootParams.varyParams;
367
+ finishSegmentAccumulator(response.head, rootVaryParams);
368
+ for (const segment of response.segments) finishSegmentAccumulator(segment, rootVaryParams);
369
+ finishSegmentAccumulator(response.rootParams, emptySet);
370
+ }
371
+
372
+ function finishSegmentAccumulator(
373
+ accumulator: VaryParamsAccumulator,
374
+ rootVaryParams: VaryParams,
375
+ ): void {
376
+ if (accumulator.status !== 'pending') return;
377
+ const merged = new Set<string>(accumulator.varyParams);
378
+ for (const param of rootVaryParams) merged.add(param);
379
+ accumulator.value = merged;
380
+ accumulator.status = 'fulfilled';
381
+ const resolvers = accumulator.resolvers;
382
+ accumulator.resolvers = [];
383
+ for (const resolve of resolvers) resolve(merged);
384
+ }
385
+
386
+ /**
387
+ * The published vary sets of a finished response. `search` is the `'?'` sentinel
388
+ * lifted out of the param lists (the client keys the query separately, so it
389
+ * never appears as a param name in the wire form).
390
+ */
391
+ export interface ResponseVaryParams {
392
+ /**
393
+ * Params the route BODY read, sorted - the UNION of page + layout. Kept as the whole-document key: an
394
+ * `/_index` response carries layout AND page markup, so it is only byte-correct for a URL that matches
395
+ * both sets. The split frames below are what let the client reuse one half of it.
396
+ */
397
+ params: string[];
398
+ /** Params the PAGE (leaf) segment read, sorted. */
399
+ page: string[];
400
+ /** Params the LAYOUT segments read, sorted (union across the layout chain). */
401
+ layout: string[];
402
+ /** Params `generateMetadata`/`generateViewport` read, sorted. */
403
+ head: string[];
404
+ /** Params read through `next/root-params`, sorted. */
405
+ root: string[];
406
+ /** True when the body read searchParams in any way. */
407
+ search: boolean;
408
+ /** True when the PAGE segment read searchParams. */
409
+ pageSearch: boolean;
410
+ /** True when a LAYOUT segment read searchParams. */
411
+ layoutSearch: boolean;
412
+ /** True when the head read searchParams in any way. */
413
+ headSearch: boolean;
414
+ }
415
+
416
+ export function collectResponseVaryParams(
417
+ response: ResponseVaryParamsAccumulator,
418
+ ): ResponseVaryParams {
419
+ finishAccumulatingVaryParams(response);
420
+ // w9-segment-split: the per-kind sets are collected SEPARATELY (no union at
421
+ // the accumulator level). `params` is still published as their union, but
422
+ // only as the whole-`/_index`-document key — the layout/page sets are the
423
+ // ones the split frames key on.
424
+ const body = new Set<string>();
425
+ const page = new Set<string>();
426
+ const layout = new Set<string>();
427
+ for (const segment of response.segments) {
428
+ const target = segment.kind === 'layout' ? layout : page;
429
+ for (const name of segment.value) {
430
+ body.add(name);
431
+ target.add(name);
432
+ }
433
+ }
434
+ const head = new Set<string>(response.head.value);
435
+ return {
436
+ params: sortedParamNames(body),
437
+ page: sortedParamNames(page),
438
+ layout: sortedParamNames(layout),
439
+ head: sortedParamNames(head),
440
+ root: sortedParamNames(response.rootParams.value),
441
+ search: body.has(SEARCH_PARAMS_SENTINEL),
442
+ pageSearch: page.has(SEARCH_PARAMS_SENTINEL),
443
+ layoutSearch: layout.has(SEARCH_PARAMS_SENTINEL),
444
+ headSearch: head.has(SEARCH_PARAMS_SENTINEL),
445
+ };
446
+ }
447
+
448
+ function sortedParamNames(set: VaryParams): string[] {
449
+ return [...set].filter(name => name !== SEARCH_PARAMS_SENTINEL).sort();
450
+ }
451
+
452
+ /**
453
+ * Run `produce` inside a fresh vary-params response scope and return its value
454
+ * alongside the collected vary sets. Nested calls reuse the outer scope's
455
+ * accumulator (a segment render nested inside another response must not start a
456
+ * second, disconnected tracking scope).
457
+ */
458
+ export async function withVaryParamsTracking<T>(
459
+ produce: () => Promise<T>,
460
+ ): Promise<{ value: T; vary: ResponseVaryParams }> {
461
+ const outer = varyStorage.getStore();
462
+ if (outer) return { value: await produce(), vary: collectResponseVaryParams(outer) };
463
+ const response = createResponseVaryParamsAccumulator();
464
+ const value = await varyStorage.run(response, produce);
465
+ return { value, vary: collectResponseVaryParams(response) };
466
+ }
467
+
468
+ // ---------------------------------------------------------------------------
469
+ // Wire format.
470
+ //
471
+ // A vary set travels as a comma-separated name list; the search sentinel rides
472
+ // as the literal `?` entry (mirroring Next, which keeps it inside the set). An
473
+ // EMPTY list means "shareable across every param value"; an ABSENT field means
474
+ // "unknown" and the client falls back to exact-URL keying.
475
+ // ---------------------------------------------------------------------------
476
+
477
+ /** The wire payload emitted alongside a segment/tree response. */
478
+ export interface SegmentVaryPayload {
479
+ /** Body-segment vary names (may include the `?` sentinel). */
480
+ params: string[];
481
+ /** Head-segment vary names (may include the `?` sentinel). */
482
+ head: string[];
483
+ /** Root-param vary names. */
484
+ root?: string[];
485
+ /** w9-segment-split: the PAGE frame's own vary names. */
486
+ page?: string[];
487
+ /** w9-segment-split: the LAYOUT frame's own vary names. */
488
+ layout?: string[];
489
+ }
490
+
491
+ export function toSegmentVaryPayload(vary: ResponseVaryParams): SegmentVaryPayload {
492
+ return {
493
+ params: encodeVaryNames(vary.params, vary.search),
494
+ head: encodeVaryNames(vary.head, vary.headSearch),
495
+ page: encodeVaryNames(vary.page, vary.pageSearch),
496
+ layout: encodeVaryNames(vary.layout, vary.layoutSearch),
497
+ ...(vary.root.length > 0 ? { root: vary.root } : {}),
498
+ };
499
+ }
500
+
501
+ function encodeVaryNames(names: readonly string[], search: boolean): string[] {
502
+ return search ? [...names, SEARCH_PARAMS_SENTINEL] : [...names];
503
+ }
504
+
505
+ /**
506
+ * Rebuild a `ResponseVaryParams` from the wire name lists a BAKED segment's
507
+ * `.meta` sidecar persisted. The inverse of `varyNamesFor`: the `?` sentinel
508
+ * comes back out as the search flag. Head/root sets are not persisted (the head
509
+ * segment publishes its own), so they come back empty.
510
+ */
511
+ export function responseVaryParamsFromWire(wire: {
512
+ vary?: string[];
513
+ layoutVary?: string[];
514
+ pageVary?: string[];
515
+ }): ResponseVaryParams {
516
+ const split = (names: readonly string[] | undefined): { names: string[]; search: boolean } => ({
517
+ names: (names ?? []).filter(name => name !== SEARCH_PARAMS_SENTINEL),
518
+ search: (names ?? []).includes(SEARCH_PARAMS_SENTINEL),
519
+ });
520
+ const body = split(wire.vary);
521
+ const layout = split(wire.layoutVary);
522
+ const page = split(wire.pageVary);
523
+ return {
524
+ params: body.names,
525
+ page: page.names,
526
+ layout: layout.names,
527
+ head: [],
528
+ root: [],
529
+ search: body.search,
530
+ pageSearch: page.search,
531
+ layoutSearch: layout.search,
532
+ headSearch: false,
533
+ };
534
+ }
535
+
536
+ /** The wire vary names for one segment kind of a finished response. */
537
+ export function varyNamesFor(
538
+ vary: ResponseVaryParams,
539
+ kind: 'body' | 'head' | 'page' | 'layout',
540
+ ): string[] {
541
+ switch (kind) {
542
+ case 'head':
543
+ return encodeVaryNames(vary.head, vary.headSearch);
544
+ case 'page':
545
+ return encodeVaryNames(vary.page, vary.pageSearch);
546
+ case 'layout':
547
+ return encodeVaryNames(vary.layout, vary.layoutSearch);
548
+ default:
549
+ return encodeVaryNames(vary.params, vary.search);
550
+ }
551
+ }
@@ -0,0 +1,33 @@
1
+ import { AsyncLocalStorage } from 'node:async_hooks';
2
+
3
+ // globalThis-anchored (this module can be loaded twice — original + built copy
4
+ // — and both must observe the same scope; see the use-cache module-duplication
5
+ // incident).
6
+ const GSP_SCOPE = Symbol.for('pnext.generateStaticParamsScope');
7
+ interface GspScope {
8
+ /** Params provided by parent generateStaticParams calls. */
9
+ provided: Record<string, unknown>;
10
+ }
11
+ const globals = globalThis as { [GSP_SCOPE]?: AsyncLocalStorage<GspScope> };
12
+ const gspScope = (globals[GSP_SCOPE] ??= new AsyncLocalStorage<GspScope>());
13
+
14
+ /** The active generateStaticParams scope, or undefined outside one. */
15
+ export function generateStaticParamsScope(): GspScope | undefined {
16
+ return gspScope.getStore();
17
+ }
18
+
19
+ export function aliasGenerateStaticParams(module: Record<string, unknown>): Record<string, unknown> {
20
+ if (typeof module.params === 'function' || typeof module.generateStaticParams !== 'function') {
21
+ return module;
22
+ }
23
+ const generate = module.generateStaticParams as (args?: {
24
+ params?: Record<string, unknown>;
25
+ }) => unknown;
26
+ // Wrap so root-params getters can tell they are running inside a
27
+ // generateStaticParams call (and which parent params are available) —
28
+ // reading a root param inside the generateStaticParams that defines it is a
29
+ // build error (app-root-params-getters/generate-static-params-error).
30
+ const params = (args?: { params?: Record<string, unknown> }) =>
31
+ gspScope.run({ provided: args?.params ?? {} }, () => generate(args));
32
+ return { ...module, params };
33
+ }