@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,167 @@
1
+ # Routing
2
+
3
+ ## Files
4
+
5
+ - `app/**/page` defines a UI route.
6
+ - `app/**/layout` exports metadata and can wrap descendant pages.
7
+ - `app/**/route` defines HTTP handlers.
8
+ - `proxy` or `middleware` runs before route matching.
9
+ - `app/**/loading` defines a Suspense fallback for the segment.
10
+ - `app/**/error` defines fallback UI for server render failures in the segment.
11
+ - `app/**/not-found` defines fallback UI.
12
+
13
+ Convention files are matched across `.tsx`, `.ts`, `.jsx`, `.js`, and `.mjs`.
14
+ `.tsx`/`.ts` are tried first, so an all-`.tsx` app resolves on the first
15
+ candidate. Route handlers (`route`) match `.ts`, `.tsx`, `.js`, `.mjs`.
16
+
17
+ ## Segments
18
+
19
+ - `app/about/page.tsx` -> `/about`
20
+ - `app/users/[id]/page.tsx` -> `/users/:id`
21
+ - `app/docs/[...slug]/page.tsx` -> `/docs/*`
22
+ - `app/docs/[[...slug]]/page.tsx` -> matches `/docs` AND `/docs/*` (the base path is included)
23
+ - `app/(marketing)/pricing/page.tsx` -> `/pricing`
24
+ - `app/dashboard/@team/page.tsx` -> a `@slot` directory adds no path segment; it is rendered as a slot of `/dashboard`
25
+
26
+ ### Intercepting markers
27
+
28
+ A marker prefixes a segment to intercept a route from another level. The marker
29
+ is stripped from the resulting path. See [Intercepting routes](#intercepting-routes).
30
+
31
+ - `(.)segment` -> same level.
32
+ - `(..)segment` -> one level up.
33
+ - `(..)(..)segment` -> two levels up.
34
+ - `(...)segment` -> from the app root.
35
+
36
+ ## Page Props
37
+
38
+ Pages receive explicit props:
39
+
40
+ ```ts
41
+ type PageProps<P = Record<string, string | string[]>> = {
42
+ params: Promise<P>;
43
+ searchParams: Promise<Record<string, string | string[] | undefined>>;
44
+ request?: NextRequest;
45
+ };
46
+ ```
47
+
48
+ Request data is read from `props.request`.
49
+
50
+ ## Route Handlers
51
+
52
+ Route handlers use the Next-compatible `(request, context)` shape:
53
+
54
+ ```ts
55
+ import type { RouteContext } from '#gen/app/api/users/[id]/route';
56
+ import type { NextRequest } from '@wular/pnext/server';
57
+
58
+ export async function GET(request: NextRequest, { params }: RouteContext) {
59
+ const { id } = await params;
60
+ return Response.json({
61
+ id,
62
+ q: request.nextUrl.searchParams.get('q'),
63
+ });
64
+ }
65
+ ```
66
+
67
+ `request` is a `NextRequest` compatible object. It extends the standard `Request` with convenience access for cookies and `nextUrl`.
68
+
69
+ If `OPTIONS` is not exported, pnext returns an automatic `OPTIONS` response with an `Allow` header. Unsupported methods return `405 Method Not Allowed` with the same `Allow` header.
70
+
71
+ Static `GET` route handlers are written as files during `pnext build`. Dynamic route handlers can export `params()` to write selected param outputs at build time.
72
+
73
+ `generateStaticParams()` is supported in Next compat mode.
74
+
75
+ ## Route Rendering
76
+
77
+ pnext infers route rendering separately from hydration:
78
+
79
+ - `static`: no dynamic params and no request prop usage.
80
+ - `dynamic`: request prop usage or dynamic route segments.
81
+
82
+ Route segment config overrides the inference. Hydration is controlled by `"use client"`.
83
+
84
+ ## Route Segment Config
85
+
86
+ Pages, layouts, and route handlers can export segment config. Values must be literals — they are read from the source, not evaluated.
87
+
88
+ ```ts
89
+ export const dynamic = 'force-dynamic';
90
+ export const maxDuration = 60;
91
+ ```
92
+
93
+ Recognized: `dynamic` (`auto` | `force-dynamic` | `force-static` | `error`), `revalidate` (a non-negative number or `false`), `fetchCache`, `dynamicParams`, `runtime` (`edge` | `nodejs`), `prefetch`, and `maxDuration`.
94
+
95
+ Config merges across the route's own file and its layout chain, leaf-first: the nearest declaration wins, except `force-dynamic` (any segment forces the whole route) and `revalidate` (the lowest declared number wins).
96
+
97
+ When building with `pnext build --adapter vercel`, `maxDuration` is copied to the generated Vercel Node function metadata.
98
+
99
+ ## File Conventions
100
+
101
+ `proxy.ts` and `middleware.ts` run before static files, pages, and route handlers. Use `proxy.ts` for new code. `middleware.ts` is supported for compatibility. If both exist, `proxy.ts` wins.
102
+
103
+ ```ts
104
+ import { NextResponse, type NextRequest } from '@wular/pnext/server';
105
+
106
+ export const config = {
107
+ matcher: ['/admin/:path*'],
108
+ };
109
+
110
+ export function proxy(request: NextRequest) {
111
+ if (!request.cookies.has('session')) {
112
+ return NextResponse.redirect(new URL('/login', request.url));
113
+ }
114
+ return NextResponse.next();
115
+ }
116
+ ```
117
+
118
+ Supported responses:
119
+
120
+ - `NextResponse.next()` continues to the matched static file, page, or route handler.
121
+ - `NextResponse.redirect(url)` returns a redirect response.
122
+ - `NextResponse.rewrite(url)` serves another path without changing the browser URL.
123
+ - `new Response(...)` returns a custom response directly.
124
+
125
+ `config.matcher` takes a string, a string array, or objects with `source` plus `has`/`missing` conditions (`header`, `query`, `cookie`, `host`). Patterns support exact paths, `:param`, `:path*`, and regex. With no `matcher`, every request runs through the proxy.
126
+
127
+ `loading.tsx` is used as the fallback for automatically wrapped server Suspense boundaries. Static builds wait for final HTML; dev and server responses can stream the fallback first.
128
+
129
+ `not-found.tsx` is rendered when a route calls `notFound()` or when no route matches. `pnext build` also writes `404.html` when root `app/not-found.tsx` exists.
130
+
131
+ `global-error.tsx` at the app root replaces the entire document when an unhandled error reaches the root uncaught. It renders its own `<html>` and `<body>`, so pnext skips the normal document shell and serves its markup directly. It only takes over when no nearer `error.tsx` boundary catches first, and a `"use client"` directive opts the file out.
132
+
133
+ `redirect()` and `permanentRedirect()` stop rendering and return redirect responses. They can be called from pages, server components, route handlers, and server helpers used during rendering.
134
+ Validate redirect targets that come from user input.
135
+
136
+ `error.tsx` is rendered when server rendering throws. It receives `error`, `reset`, and `unstable_retry`; `reset()`/`unstable_retry()` refresh the current route in `compat.next` apps.
137
+
138
+ `template.tsx` wraps the segment's children like a layout, but the segment re-renders per request where a layout persists. Nesting order is layout > template > children. A segment without a layout still applies its template.
139
+
140
+ `default.tsx` is the fallback rendered for a parallel `@slot` that has no page match.
141
+
142
+ `forbidden.tsx` and `unauthorized.tsx` render at 403/401 when `forbidden()` / `unauthorized()` is thrown. These are COMPAT-only (require `compat.next`) and experimental (`authInterrupts`); without compat they fall through to the normal error page.
143
+
144
+ ### Parallel routes
145
+
146
+ A `@slot` directory (e.g. `@team`) contributes no path segment. Its content is rendered and passed to the owning segment's layout as a prop keyed by the slot name (without the `@`), alongside `children`. A slot uses its `page` when present, else its `default` fallback, else renders nothing. A slot's `loading` wraps its content in a Suspense boundary.
147
+
148
+ ### Intercepting routes
149
+
150
+ An interception marker (`(.)`, `(..)`, `(..)(..)`, `(...)`) makes the route respond to soft navigations toward the marked target from the intercepting level, usually rendered into a parallel `@slot`. Hard requests render the non-intercepted target. Intercepting routes are always dynamic and never take static params.
151
+
152
+ ## Static Params
153
+
154
+ Use `params()` on a dynamic route to choose params that `pnext build` should write as static HTML.
155
+ Requests outside the returned list keep using dynamic rendering.
156
+
157
+ ```tsx
158
+ import type { PageProps, StaticParams } from '#gen/app/users/[id]/page';
159
+
160
+ export function params(): StaticParams {
161
+ return [{ id: 'ada' }, { id: 'grace' }];
162
+ }
163
+
164
+ export default async function Page({ params }: PageProps) {
165
+ return <p>{(await params).id}</p>;
166
+ }
167
+ ```
@@ -0,0 +1,64 @@
1
+ # Type Safety
2
+
3
+ pnext generates route types from the `app/` tree. These types check route params, static params, route handlers, and typed navigation.
4
+
5
+ ## Route Types
6
+
7
+ Import generated types for the current route. pnext validates those exports against the route inferred from the file path, so a wrong param name, static param shape, or route handler context fails TypeScript.
8
+
9
+ ```tsx
10
+ // app/users/[id]/page.tsx
11
+ import type { PageProps, StaticParams } from '#gen/app/users/[id]/page';
12
+
13
+ export function params(): StaticParams {
14
+ return [{ id: 'ada' }];
15
+ }
16
+
17
+ export default async function Page({ params }: PageProps) {
18
+ return <p>{(await params).id}</p>;
19
+ }
20
+ ```
21
+
22
+ Route handlers use generated `RouteContext` for the second argument:
23
+
24
+ ```ts
25
+ // app/users/[id]/route.ts
26
+ import type { RouteContext } from '#gen/app/users/[id]/route';
27
+ import type { NextRequest } from '@wular/pnext/server';
28
+
29
+ export async function GET(request: NextRequest, { params }: RouteContext) {
30
+ return Response.json({ id: (await params).id });
31
+ }
32
+ ```
33
+
34
+ ## Links
35
+
36
+ Typed links use typed `href` and `params`:
37
+
38
+ ```tsx
39
+ import { Link } from '@wular/pnext/link';
40
+
41
+ export function Nav() {
42
+ return <Link href="/users/[id]" params={{ id: 'ada' }}>Ada</Link>;
43
+ }
44
+ ```
45
+
46
+ Use a normal `<a>` for external URLs or intentionally untyped links.
47
+
48
+ ## Generated Types
49
+
50
+ `pnext dev` and `pnext build` refresh generated types automatically. Use `pnext typegen` to refresh them manually.
51
+
52
+ Generated files live in `.pnext/types` and should not be edited. Include `.pnext/types/**/*.ts` in the app TypeScript project for editor and `tsc` visibility.
53
+
54
+ Use a TypeScript path alias for generated imports:
55
+
56
+ ```json
57
+ {
58
+ "compilerOptions": {
59
+ "paths": {
60
+ "#gen/*": ["./.pnext/types/*"]
61
+ }
62
+ }
63
+ }
64
+ ```
@@ -0,0 +1,80 @@
1
+ import { currentCacheScope, type CacheNode } from '../cache/context';
2
+
3
+ export function cache<Args extends unknown[], Result>(
4
+ fn: (...args: Args) => Result,
5
+ ): (...args: Args) => Result {
6
+ return (...args: Args) => {
7
+ const scope = currentCacheScope();
8
+ if (!scope) return fn(...args);
9
+
10
+ let root = scope.functions.get(fn);
11
+ if (!root) {
12
+ root = cacheNode();
13
+ scope.functions.set(fn, root);
14
+ }
15
+
16
+ const node = cacheNodeForArgs(root, args);
17
+ const record = node.record;
18
+ if (record) {
19
+ if (record.status === 'rejected') {
20
+ if (record.async) {
21
+ return Promise.resolve().then(() => {
22
+ throw record.error;
23
+ }) as Result;
24
+ }
25
+ throw record.error;
26
+ }
27
+ if (record.status === 'resolved' && record.async) return Promise.resolve(record.value) as Result;
28
+ return record.value as Result;
29
+ }
30
+
31
+ try {
32
+ const value = fn(...args);
33
+ if (isPromiseLike(value)) {
34
+ const promise = Promise.resolve(value).then(
35
+ result => {
36
+ node.record = { status: 'resolved', value: result, async: true };
37
+ return result;
38
+ },
39
+ error => {
40
+ node.record = { status: 'rejected', error, async: true };
41
+ throw error;
42
+ },
43
+ );
44
+ node.record = { status: 'pending', value: promise };
45
+ return promise as Result;
46
+ }
47
+
48
+ node.record = { status: 'resolved', value, async: false };
49
+ return value;
50
+ } catch (error) {
51
+ node.record = { status: 'rejected', error, async: false };
52
+ throw error;
53
+ }
54
+ };
55
+ }
56
+
57
+ function cacheNode(): CacheNode {
58
+ return { children: new Map() };
59
+ }
60
+
61
+ function cacheNodeForArgs(root: CacheNode, args: unknown[]) {
62
+ let node = root;
63
+ for (const arg of args) {
64
+ let child = node.children.get(arg);
65
+ if (!child) {
66
+ child = cacheNode();
67
+ node.children.set(arg, child);
68
+ }
69
+ node = child;
70
+ }
71
+ return node;
72
+ }
73
+
74
+ function isPromiseLike(value: unknown): value is PromiseLike<unknown> {
75
+ return (
76
+ (typeof value === 'object' || typeof value === 'function') &&
77
+ value !== null &&
78
+ 'then' in value
79
+ );
80
+ }
@@ -0,0 +1,9 @@
1
+ export function cache<Args extends unknown[], Result>(
2
+ fn: (...args: Args) => Result,
3
+ ): (...args: Args) => Result {
4
+ return (...args: Args) => fn(...args);
5
+ }
6
+
7
+ export function cacheSignal() {
8
+ return null;
9
+ }
@@ -0,0 +1,279 @@
1
+ import { useEffect, useState } from 'preact/hooks';
2
+ import { ReadonlyURLSearchParams, type HrefOptions } from './navigation';
3
+ import { blockJavascriptUrl, prefetchRoute, softNavigate } from './router';
4
+ import { seedHistoryEntry } from './router/history';
5
+ import {
6
+ emitLocationChange,
7
+ locationListeners,
8
+ scheduleNavigationScroll,
9
+ } from './router/events';
10
+ import { routeHref } from '../routing/href';
11
+ import type { RouteParams, RouteParamValue, RoutePath } from '../types';
12
+
13
+ export type NavigateOptions<Route extends RoutePath> = HrefOptions<Route> & {
14
+ scroll?: boolean;
15
+ };
16
+
17
+ export type PrefetchOptions<Route extends RoutePath> = HrefOptions<Route> & {
18
+ kind?: 'auto' | 'full';
19
+ onInvalidate?: () => void;
20
+ };
21
+
22
+ export interface PNextRouter {
23
+ push<Route extends RoutePath>(
24
+ route: Route,
25
+ ...options: RouteParams<Route> extends Record<string, never>
26
+ ? [options?: NavigateOptions<Route>]
27
+ : [options: NavigateOptions<Route>]
28
+ ): void;
29
+ replace<Route extends RoutePath>(
30
+ route: Route,
31
+ ...options: RouteParams<Route> extends Record<string, never>
32
+ ? [options?: NavigateOptions<Route>]
33
+ : [options: NavigateOptions<Route>]
34
+ ): void;
35
+ prefetch<Route extends RoutePath>(
36
+ route: Route,
37
+ ...options: RouteParams<Route> extends Record<string, never>
38
+ ? [options?: PrefetchOptions<Route>]
39
+ : [options: PrefetchOptions<Route>]
40
+ ): void;
41
+ refresh(): void;
42
+ back(): void;
43
+ forward(): void;
44
+ bfcacheId: string;
45
+ }
46
+
47
+ export { ReadonlyURLSearchParams };
48
+
49
+ export interface CurrentRoute<Route extends RoutePath = RoutePath> {
50
+ route: Route | null;
51
+ pathname: string;
52
+ params: RouteParams<Route>;
53
+ searchParams: ReadonlyURLSearchParams;
54
+ }
55
+
56
+ interface RouteState {
57
+ route?: string;
58
+ params?: Record<string, RouteParamValue>;
59
+ catchAllOptional?: boolean;
60
+ staticChildren?: string[];
61
+ bfcacheId?: string;
62
+ /** `prefetch = 'allow-runtime'`: this route's shell is request-sampled. */
63
+ runtimePrefetch?: boolean;
64
+ }
65
+
66
+ /**
67
+ * `__PNEXT_ROUTE__.route` is stored on the wire in bracket-free colon form
68
+ * (`/:teamSlug/:slug*`) so no `[x]`/`%5Bx%5D` placeholder ever reaches a
69
+ * response body. The public `useRoute()` contract exposes the familiar bracket
70
+ * pattern (`/[teamSlug]/[...slug]`), so convert back at the API boundary.
71
+ */
72
+ function bracketRoutePattern(state: RouteState): string | null {
73
+ const route = state.route;
74
+ if (route == null) return null;
75
+ return route
76
+ .replace(/:([\w$]+)\*/g, state.catchAllOptional ? '[[...$1]]' : '[...$1]')
77
+ .replace(/:([\w$]+)/g, '[$1]');
78
+ }
79
+
80
+ declare global {
81
+ interface Window {
82
+ __PNEXT_ROUTE__?: RouteState;
83
+ __PNEXT_HISTORY_PATCHED__?: boolean;
84
+ }
85
+ }
86
+
87
+ // This module is the reason the hard-loaded entry needs its history ids before first render:
88
+ // `bfcacheId` below reads them, and `patchHistory` carries them onto an app-level pushState. Seeding
89
+ // here rather than in the hub's installRouter keeps a page whose client graph never touches router
90
+ // state from paying for them.
91
+ seedHistoryEntry();
92
+
93
+ export function useRouter(): PNextRouter {
94
+ return router;
95
+ }
96
+
97
+ const router: PNextRouter = {
98
+ push(route, ...[options]) {
99
+ navigate(routerHref(route, options), 'push', options?.scroll);
100
+ },
101
+ replace(route, ...[options]) {
102
+ navigate(routerHref(route, options), 'replace', options?.scroll);
103
+ },
104
+ prefetch(route, ...[options]) {
105
+ void prefetchRoute(routerHref(route, options), {
106
+ strict: true,
107
+ full: options?.kind === 'full',
108
+ onInvalidate: options?.onInvalidate,
109
+ });
110
+ },
111
+ refresh() {
112
+ void softNavigate(window.location.href, { replace: true, scroll: false });
113
+ },
114
+ back() {
115
+ window.history.back();
116
+ },
117
+ forward() {
118
+ window.history.forward();
119
+ },
120
+ get bfcacheId() {
121
+ return routeState().bfcacheId ?? '';
122
+ },
123
+ };
124
+
125
+ function routerHref<Route extends RoutePath>(route: Route, options: HrefOptions<Route> | undefined) {
126
+ return hasHrefParts(options) ? routeHref(route, options) : String(route);
127
+ }
128
+
129
+ function hasHrefParts<Route extends RoutePath>(
130
+ options: HrefOptions<Route> | undefined,
131
+ ): options is HrefOptions<Route> {
132
+ return Boolean(options && ('params' in options || 'search' in options || 'hash' in options));
133
+ }
134
+
135
+ export function usePathname() {
136
+ return useLocation().pathname;
137
+ }
138
+
139
+ export function useSearchParams(): ReadonlyURLSearchParams {
140
+ return new ReadonlyURLSearchParams(useLocation().search);
141
+ }
142
+
143
+ export function useParams<Route extends RoutePath = RoutePath>() {
144
+ useLocation();
145
+ return (routeState().params ?? {}) as RouteParams<Route>;
146
+ }
147
+
148
+ export function useRoute<Route extends RoutePath = RoutePath>(): CurrentRoute<Route> {
149
+ const location = useLocation();
150
+ return {
151
+ route: bracketRoutePattern(routeState()) as Route | null,
152
+ pathname: location.pathname,
153
+ params: (routeState().params ?? {}) as RouteParams<Route>,
154
+ searchParams: new ReadonlyURLSearchParams(location.search),
155
+ };
156
+ }
157
+
158
+ export function useLinkStatus() {
159
+ return { pending: false };
160
+ }
161
+
162
+ function useLocation() {
163
+ const [location, setLocation] = useState(() => currentLocation());
164
+
165
+ useEffect(() => {
166
+ patchHistory();
167
+ const listener = () =>
168
+ setLocation(previous => {
169
+ const next = currentLocation();
170
+ return previous.pathname === next.pathname &&
171
+ previous.search === next.search &&
172
+ previous.hash === next.hash
173
+ ? previous
174
+ : next;
175
+ });
176
+ locationListeners().add(listener);
177
+ window.addEventListener('popstate', listener);
178
+ // Re-read once the listener is attached: the URL can move between the
179
+ // render that captured it and this effect (a history traversal landing
180
+ // while a swapped-in tree mounts), and nothing was listening for that one.
181
+ listener();
182
+ return () => {
183
+ locationListeners().delete(listener);
184
+ window.removeEventListener('popstate', listener);
185
+ };
186
+ }, []);
187
+
188
+ return location;
189
+ }
190
+
191
+ function currentLocation() {
192
+ if (!process.browser && typeof window === 'undefined') {
193
+ return {
194
+ pathname: '/',
195
+ search: '',
196
+ hash: '',
197
+ };
198
+ }
199
+ return {
200
+ pathname: window.location.pathname,
201
+ search: window.location.search,
202
+ hash: window.location.hash,
203
+ };
204
+ }
205
+
206
+ function routeState() {
207
+ if (!process.browser && typeof window === 'undefined') return {};
208
+ const state = (history.state as Record<string, unknown> | null) ?? {};
209
+ const routeState = window.__PNEXT_ROUTE__ ?? {};
210
+ const bfcacheId = typeof state.__pnextBfcacheId === 'string' ? state.__pnextBfcacheId : undefined;
211
+ return bfcacheId ? ({ ...routeState, bfcacheId } as RouteState) : routeState;
212
+ }
213
+
214
+ function navigate(url: string, mode: 'push' | 'replace', scroll = true) {
215
+ if (blockJavascriptUrl(url)) return;
216
+ const next = new URL(url, window.location.href);
217
+ if (next.origin !== window.location.origin) {
218
+ const holder = globalThis as { __pnextMpaNavigation?: number };
219
+ holder.__pnextMpaNavigation = (holder.__pnextMpaNavigation ?? 0) + 1;
220
+ requestAnimationFrame(() =>
221
+ requestAnimationFrame(() => {
222
+ if (mode === 'replace') window.location.replace(next.href);
223
+ else window.location.assign(next.href);
224
+ }),
225
+ );
226
+ return;
227
+ }
228
+
229
+ if (next.pathname !== window.location.pathname || next.search !== window.location.search) {
230
+ void softNavigate(next.href, { replace: mode === 'replace', scroll });
231
+ return;
232
+ }
233
+
234
+ if (mode === 'replace') window.history.replaceState(history.state, '', next);
235
+ else window.history.pushState(history.state, '', next);
236
+ scheduleNavigationScroll(next, { scroll });
237
+ }
238
+
239
+ // Exported so compat's link-status can guarantee raw history.pushState /
240
+ // replaceState calls broadcast a location change (ending a link's pending
241
+ // state) even when no useLocation-based hook has mounted yet.
242
+ export function patchHistory() {
243
+ if ((!process.browser && typeof window === 'undefined') || window.__PNEXT_HISTORY_PATCHED__) return;
244
+ window.__PNEXT_HISTORY_PATCHED__ = true;
245
+ const pushState = window.history.pushState.bind(window.history);
246
+ const replaceState = window.history.replaceState.bind(window.history);
247
+
248
+ // Broadcast only when the URL actually changes. The router itself calls replaceState with the
249
+ // CURRENT href for bookkeeping - including at the start of every soft navigation - and emitting for
250
+ // those would clear a just-clicked link's pending state.
251
+ //
252
+ // An app-level pushState/replaceState creates an entry that still shows THIS document (shallow
253
+ // routing). Carrying pnext's own entry state over marks the new entry as pointing at the rendered
254
+ // document, so a later traversal is a location broadcast rather than a fetch-and-swap of a URL that
255
+ // may not even exist on the server. Router-initiated calls already carry the keys.
256
+ function copyInternalState(data: unknown): unknown {
257
+ const current = (window.history.state as Record<string, unknown> | null) ?? {};
258
+ const carried: Record<string, unknown> = {};
259
+ for (const key of Object.keys(current)) {
260
+ if (key.startsWith('__pnext')) carried[key] = current[key];
261
+ }
262
+ if (Object.keys(carried).length === 0) return data;
263
+ const own = (data as Record<string, unknown> | null) ?? {};
264
+ if (Object.keys(own).some(key => key.startsWith('__pnext'))) return data;
265
+ return { ...own, ...carried };
266
+ }
267
+ window.history.pushState = function patchedPushState(data, unused, url) {
268
+ const before = window.location.href;
269
+ const result = pushState(copyInternalState(data), unused, url);
270
+ if (window.location.href !== before) emitLocationChange();
271
+ return result;
272
+ };
273
+ window.history.replaceState = function patchedReplaceState(data, unused, url) {
274
+ const before = window.location.href;
275
+ const result = replaceState(copyInternalState(data), unused, url);
276
+ if (window.location.href !== before) emitLocationChange();
277
+ return result;
278
+ };
279
+ }