@wular/pnext 0.1.5 → 0.1.7

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 (302) hide show
  1. package/bin/pnext +9 -2
  2. package/dist/server/_chunks/aliases-j47a10ce.js +44 -0
  3. package/dist/server/_chunks/analyze-arswqe6s.js +445 -0
  4. package/dist/server/_chunks/boot-af0mv3dy.js +36 -0
  5. package/dist/server/_chunks/build-spy3dxp4.js +84 -0
  6. package/dist/server/_chunks/build-t9h0hec3.js +3550 -0
  7. package/dist/server/_chunks/build-tier-m7yvqz61.js +2472 -0
  8. package/dist/server/_chunks/compile-53j02j27.js +22 -0
  9. package/dist/server/_chunks/config-1yzwexq6.js +27 -0
  10. package/dist/server/_chunks/config-n2z7yxps.js +22 -0
  11. package/dist/server/_chunks/create-meec9hs6.js +134 -0
  12. package/dist/server/_chunks/dev-fjmqg6en.js +220 -0
  13. package/dist/server/_chunks/esbuild-yyktfrtt.js +15 -0
  14. package/dist/server/_chunks/generate-n9sh6hzt.js +21 -0
  15. package/dist/server/_chunks/index-0374j2b9.js +66 -0
  16. package/dist/server/_chunks/index-06nyqntx.js +213 -0
  17. package/dist/server/_chunks/index-1011cscr.js +5263 -0
  18. package/dist/server/_chunks/index-11sfk4hb.js +170 -0
  19. package/dist/server/_chunks/index-20ppawv7.js +297 -0
  20. package/dist/server/_chunks/index-2dv4y4wd.js +1065 -0
  21. package/dist/server/_chunks/index-2vz0wzdn.js +59 -0
  22. package/dist/server/_chunks/index-2wfv4kvx.js +182 -0
  23. package/dist/server/_chunks/index-357na4a8.js +109 -0
  24. package/dist/server/_chunks/index-3b3at80h.js +109 -0
  25. package/dist/server/_chunks/index-3tf1xjsp.js +6166 -0
  26. package/dist/server/_chunks/index-3xbr4eyv.js +105 -0
  27. package/dist/server/_chunks/index-4399bvd2.js +18 -0
  28. package/dist/server/_chunks/index-4hgjrqh7.js +851 -0
  29. package/dist/server/_chunks/index-4q2gbfwk.js +2530 -0
  30. package/dist/server/_chunks/index-4t92hdxd.js +172 -0
  31. package/dist/server/_chunks/index-4zzebszd.js +10 -0
  32. package/dist/server/_chunks/index-5gyr4emz.js +107 -0
  33. package/dist/server/_chunks/index-5hr4062s.js +70 -0
  34. package/dist/server/_chunks/index-68m3mwbd.js +84 -0
  35. package/dist/server/_chunks/index-6b009mt0.js +56 -0
  36. package/dist/server/_chunks/index-6e4fqv30.js +588 -0
  37. package/dist/server/_chunks/index-6gex3pfa.js +15 -0
  38. package/dist/server/_chunks/index-6kp9r0d9.js +11 -0
  39. package/dist/server/_chunks/index-6q7zvrap.js +134 -0
  40. package/dist/server/_chunks/index-73z4esab.js +38 -0
  41. package/dist/server/_chunks/index-79ngvam8.js +60 -0
  42. package/dist/server/_chunks/index-7wc9n8be.js +25 -0
  43. package/dist/server/_chunks/index-8k7pfejn.js +1599 -0
  44. package/dist/server/_chunks/index-8kcxdfya.js +206 -0
  45. package/dist/server/_chunks/index-91x12ykq.js +274 -0
  46. package/dist/server/_chunks/index-9ch50vtq.js +18 -0
  47. package/dist/server/_chunks/index-9hx2641q.js +28 -0
  48. package/dist/server/_chunks/index-9n2914ww.js +535 -0
  49. package/dist/server/_chunks/index-9ych9ma8.js +207 -0
  50. package/dist/server/_chunks/index-b6wtkdp4.js +24 -0
  51. package/dist/server/_chunks/index-bcws5eh2.js +35 -0
  52. package/dist/server/_chunks/index-c0g2wf3z.js +92 -0
  53. package/dist/server/_chunks/index-cf4rvxv6.js +95 -0
  54. package/dist/server/_chunks/index-cjgqs13g.js +65 -0
  55. package/dist/server/_chunks/index-ckkdpht5.js +41 -0
  56. package/dist/server/_chunks/index-cyfednwb.js +87 -0
  57. package/dist/server/_chunks/index-danncfhy.js +311 -0
  58. package/dist/server/_chunks/index-dg0f05zs.js +199 -0
  59. package/dist/server/_chunks/index-e3a6yx84.js +18 -0
  60. package/dist/server/_chunks/index-e88tyv35.js +45 -0
  61. package/dist/server/_chunks/index-eajaspjk.js +1253 -0
  62. package/dist/server/_chunks/index-eeaanp2e.js +483 -0
  63. package/dist/server/_chunks/index-egykcqg3.js +101 -0
  64. package/dist/server/_chunks/index-en7n9f7m.js +22 -0
  65. package/dist/server/_chunks/index-ezgj0tr1.js +88 -0
  66. package/dist/server/_chunks/index-fkp000p1.js +590 -0
  67. package/dist/server/_chunks/index-fn1chkkd.js +214 -0
  68. package/dist/server/_chunks/index-fp8r0sqs.js +64 -0
  69. package/dist/server/_chunks/index-ggk2j965.js +9432 -0
  70. package/dist/server/_chunks/index-grta8ap3.js +238 -0
  71. package/dist/server/_chunks/index-h2a70tg4.js +7 -0
  72. package/dist/server/_chunks/index-h4xy648y.js +51 -0
  73. package/dist/server/_chunks/index-hkdfj58q.js +92 -0
  74. package/dist/server/_chunks/index-j2zrcp1k.js +66 -0
  75. package/dist/server/_chunks/index-jcsjq3d1.js +20 -0
  76. package/dist/server/_chunks/index-jepjvcrf.js +198 -0
  77. package/dist/server/_chunks/index-jks0x0a1.js +14 -0
  78. package/dist/server/_chunks/index-jx7bqvw1.js +57 -0
  79. package/dist/server/_chunks/index-k1g3epw0.js +1299 -0
  80. package/dist/server/_chunks/index-knp0s912.js +7 -0
  81. package/dist/server/_chunks/index-kq8kpzye.js +472 -0
  82. package/dist/server/_chunks/index-kqyy1d4f.js +50 -0
  83. package/dist/server/_chunks/index-kresp2n8.js +445 -0
  84. package/dist/server/_chunks/index-ms9v9tnx.js +31 -0
  85. package/dist/server/_chunks/index-n4hxpng2.js +11 -0
  86. package/dist/server/_chunks/index-ncnes8c6.js +17 -0
  87. package/dist/server/_chunks/index-nqh3xhdc.js +57 -0
  88. package/dist/server/_chunks/index-nyrba2kr.js +4130 -0
  89. package/dist/server/_chunks/index-p77b4kcx.js +296 -0
  90. package/dist/server/_chunks/index-pym1cezq.js +39 -0
  91. package/dist/server/_chunks/index-q738kkhj.js +100 -0
  92. package/dist/server/_chunks/index-qjj6gddn.js +54 -0
  93. package/dist/server/_chunks/index-qm6kmhqs.js +110 -0
  94. package/dist/server/_chunks/index-qn4z5s3c.js +819 -0
  95. package/dist/server/_chunks/index-qpwsgt3a.js +158 -0
  96. package/dist/server/_chunks/index-r97ptz1q.js +97 -0
  97. package/dist/server/_chunks/index-ras0sdab.js +26 -0
  98. package/dist/server/_chunks/index-rcs322t1.js +19 -0
  99. package/dist/server/_chunks/index-rn8dyr1f.js +126 -0
  100. package/dist/server/_chunks/index-rt9qq1va.js +4 -0
  101. package/dist/server/_chunks/index-rthrsed5.js +356 -0
  102. package/dist/server/_chunks/index-sctcgyvv.js +185 -0
  103. package/dist/server/_chunks/index-sjpckxqn.js +213 -0
  104. package/dist/server/_chunks/index-sq3kvz3g.js +200 -0
  105. package/dist/server/_chunks/index-stbk3jhn.js +31 -0
  106. package/dist/server/_chunks/index-sv8m6789.js +266 -0
  107. package/dist/server/_chunks/index-tgcakdxc.js +125 -0
  108. package/dist/server/_chunks/index-tgsfjmep.js +74 -0
  109. package/dist/server/_chunks/index-tq0pc7eg.js +154 -0
  110. package/dist/server/_chunks/index-tq1f96v7.js +51 -0
  111. package/dist/server/_chunks/index-tqn2qnnn.js +241 -0
  112. package/dist/server/_chunks/index-tvqkq6e6.js +128 -0
  113. package/dist/server/_chunks/index-v1ags1na.js +132 -0
  114. package/dist/server/_chunks/index-vvmmknm1.js +1431 -0
  115. package/dist/server/_chunks/index-vvq7rqtn.js +329 -0
  116. package/dist/server/_chunks/index-vz0km5qk.js +33 -0
  117. package/dist/server/_chunks/index-w6h8m6vp.js +138 -0
  118. package/dist/server/_chunks/index-web361b8.js +138 -0
  119. package/dist/server/_chunks/index-wpfhfbs4.js +194 -0
  120. package/dist/server/_chunks/index-x0y005nh.js +283 -0
  121. package/dist/server/_chunks/index-x3yn4ywm.js +22 -0
  122. package/dist/server/_chunks/index-x93hv0sc.js +271 -0
  123. package/dist/server/_chunks/index-xvj80c3a.js +30 -0
  124. package/dist/server/_chunks/index-xywjg2vc.js +100 -0
  125. package/dist/server/_chunks/index-y2jr2n72.js +152 -0
  126. package/dist/server/_chunks/index-y36a0ybw.js +57 -0
  127. package/dist/server/_chunks/index-ye9hmsvf.js +47 -0
  128. package/dist/server/_chunks/index-yhxkxghe.js +56 -0
  129. package/dist/server/_chunks/index-ym67zqxp.js +130 -0
  130. package/dist/server/_chunks/index-ytjvpbm2.js +736 -0
  131. package/dist/server/_chunks/index-yz54pxrh.js +960 -0
  132. package/dist/server/_chunks/index-z4z9zv3b.js +733 -0
  133. package/dist/server/_chunks/index-z84ab1w0.js +1270 -0
  134. package/dist/server/_chunks/index-z88qv16t.js +11 -0
  135. package/dist/server/_chunks/index-zqpqyg8p.js +666 -0
  136. package/dist/server/_chunks/loader-7yw0j98z.js +78 -0
  137. package/dist/server/_chunks/modules-jn8bmbn8.js +74 -0
  138. package/dist/server/_chunks/pipeline-yy9jhydf.js +832 -0
  139. package/dist/server/_chunks/production-jar9ztm3.js +44 -0
  140. package/dist/server/_chunks/proxy-config-j84dgksn.js +21 -0
  141. package/dist/server/_chunks/proxy-w1d0wmsn.js +33 -0
  142. package/dist/server/_chunks/router-k1reahfn.js +498 -0
  143. package/dist/server/_chunks/routes-2s277fc6.js +64 -0
  144. package/dist/server/_chunks/run-pvd8bs1t.js +696 -0
  145. package/dist/server/_chunks/server-mt4a994c.js +1835 -0
  146. package/dist/server/_chunks/tsconfig-defaults-1nb3308g.js +20 -0
  147. package/dist/server/_chunks/typegen-0qr0459b.js +35 -0
  148. package/dist/server/api/client-cache.js +14 -0
  149. package/dist/server/cli/index.js +378 -0
  150. package/dist/server/cli/start.js +137 -0
  151. package/dist/server/client/reference.js +16 -0
  152. package/dist/server/compat/next/cache.js +56 -0
  153. package/dist/server/compat/next/canonical-url.js +13 -0
  154. package/dist/server/compat/next/client-cache.js +57 -0
  155. package/dist/server/compat/next/client-navigation.js +232 -0
  156. package/dist/server/compat/next/client-only.js +1 -0
  157. package/dist/server/compat/next/client-script.js +178 -0
  158. package/dist/server/compat/next/client-server.js +34 -0
  159. package/dist/server/compat/next/config-headers.js +32 -0
  160. package/dist/server/compat/next/config-loader.js +25 -0
  161. package/dist/server/compat/next/config.js +10 -0
  162. package/dist/server/compat/next/constants.js +18 -0
  163. package/dist/server/compat/next/custom-server.js +10 -0
  164. package/dist/server/compat/next/dist/client/components/app-router-headers.js +48 -0
  165. package/dist/server/compat/next/dist/server/web/spec-extension/revalidate.js +27 -0
  166. package/dist/server/compat/next/dist/server/web/spec-extension/unstable-cache.js +25 -0
  167. package/dist/server/compat/next/dist/server/web/spec-extension/unstable-no-store.js +25 -0
  168. package/dist/server/compat/next/dynamic.js +32 -0
  169. package/dist/server/compat/next/error.js +112 -0
  170. package/dist/server/compat/next/font/cache.js +14 -0
  171. package/dist/server/compat/next/font/google.js +3 -0
  172. package/dist/server/compat/next/font/index.js +1 -0
  173. package/dist/server/compat/next/font/local.js +22 -0
  174. package/dist/server/compat/next/font/runtime-client.js +33 -0
  175. package/dist/server/compat/next/font/runtime.js +41 -0
  176. package/dist/server/compat/next/font/shared.js +22 -0
  177. package/dist/server/compat/next/form.js +129 -0
  178. package/dist/server/compat/next/head.js +10 -0
  179. package/dist/server/compat/next/headers.js +30 -0
  180. package/dist/server/compat/next/image/client.js +11 -0
  181. package/dist/server/compat/next/image/config.js +18 -0
  182. package/dist/server/compat/next/image/optimizer.js +25 -0
  183. package/dist/server/compat/next/image/patterns.js +12 -0
  184. package/dist/server/compat/next/image/props.js +16 -0
  185. package/dist/server/compat/next/image/shared.js +86 -0
  186. package/dist/server/compat/next/image/static-metadata.js +32 -0
  187. package/dist/server/compat/next/image/validate.js +8 -0
  188. package/dist/server/compat/next/image.js +59 -0
  189. package/dist/server/compat/next/index.js +1 -0
  190. package/dist/server/compat/next/legacy-image.js +78 -0
  191. package/dist/server/compat/next/legacy-request-apis.js +24 -0
  192. package/dist/server/compat/next/link-transform.js +14 -0
  193. package/dist/server/compat/next/link.js +14 -0
  194. package/dist/server/compat/next/navigation.js +190 -0
  195. package/dist/server/compat/next/offline.js +45 -0
  196. package/dist/server/compat/next/og.js +8 -0
  197. package/dist/server/compat/next/preferred-region.js +8 -0
  198. package/dist/server/compat/next/redirects.js +30 -0
  199. package/dist/server/compat/next/resource-hints.js +30 -0
  200. package/dist/server/compat/next/rewrites.js +41 -0
  201. package/dist/server/compat/next/root-params.js +121 -0
  202. package/dist/server/compat/next/route-state.js +23 -0
  203. package/dist/server/compat/next/router.js +103 -0
  204. package/dist/server/compat/next/script.js +37 -0
  205. package/dist/server/compat/next/server-only.js +1 -0
  206. package/dist/server/compat/next/server.js +131 -0
  207. package/dist/server/compat/next/svgr.js +14 -0
  208. package/dist/server/compat/next/telemetry.js +10 -0
  209. package/dist/server/compat/next/usage.js +13 -0
  210. package/dist/server/compat/next/user-agent.js +12 -0
  211. package/dist/server/compat/next/web-vitals.js +35 -0
  212. package/dist/server/compat/react/action-state.js +11 -0
  213. package/dist/server/compat/react/client-lite.js +83 -0
  214. package/dist/server/compat/react/client.js +131 -0
  215. package/dist/server/compat/react/compiler-runtime.js +42 -0
  216. package/dist/server/compat/react/dom-client.js +106 -0
  217. package/dist/server/compat/react/dom-react-server.js +37 -0
  218. package/dist/server/compat/react/dom-server.js +40 -0
  219. package/dist/server/compat/react/dom.js +52 -0
  220. package/dist/server/compat/react/hooks-extra.js +12 -0
  221. package/dist/server/compat/react/internals.js +14 -0
  222. package/dist/server/compat/react/parity.js +11 -0
  223. package/dist/server/compat/react/preact.js +113 -0
  224. package/dist/server/compat/react/react-server.js +133 -0
  225. package/dist/server/compat/react/router-shim.js +8 -0
  226. package/dist/server/compat/react/server-component-use.js +14 -0
  227. package/dist/server/compat/react/server-inserted-html.js +14 -0
  228. package/dist/server/compat/react/server.js +194 -0
  229. package/dist/server/compat/react/style-values.js +10 -0
  230. package/dist/server/compat/react/use.js +10 -0
  231. package/dist/server/compat/react/view-transition.js +12 -0
  232. package/dist/server/generation.json +1 -0
  233. package/package.json +4 -1
  234. package/src/api/dynamic.tsx +2 -0
  235. package/src/api/server.ts +24 -6
  236. package/src/cli/adapters/vercel-warm.ts +315 -61
  237. package/src/cli/adapters/vercel.ts +348 -71
  238. package/src/cli/build.ts +58 -33
  239. package/src/cli/index.ts +23 -10
  240. package/src/cli/serve/entry.ts +21 -159
  241. package/src/cli/serve/immutable.ts +31 -0
  242. package/src/cli/serve/pipeline.ts +82 -84
  243. package/src/cli/start.ts +3 -1
  244. package/src/client/chunk-fold.ts +19 -6
  245. package/src/client/react-tier.ts +3 -2
  246. package/src/client/reference-stub.ts +21 -2
  247. package/src/compat/actions/client-plugin.ts +4 -2
  248. package/src/compat/actions/config.ts +6 -5
  249. package/src/compat/actions/extensions.ts +2 -3
  250. package/src/compat/aliases.ts +8 -1
  251. package/src/compat/bundler/config.ts +88 -0
  252. package/src/compat/bundler/optimize-package-imports.ts +86 -26
  253. package/src/compat/bundler/relay-transform.ts +15 -3
  254. package/src/compat/cache-control.ts +16 -8
  255. package/src/compat/css/modules.ts +18 -5
  256. package/src/compat/lifecycle/error-funnel.ts +1 -1
  257. package/src/compat/lifecycle/instrumentation.ts +117 -24
  258. package/src/compat/lifecycle/testmode.ts +3 -2
  259. package/src/compat/next/config-loader.ts +137 -13
  260. package/src/compat/next/font/runtime.ts +22 -2
  261. package/src/compat/next/og.ts +3 -3
  262. package/src/compat/next/usage.ts +3 -2
  263. package/src/compat/pages/api.ts +83 -14
  264. package/src/compat/pages/router.ts +30 -14
  265. package/src/compat/register/bundler.ts +3 -1
  266. package/src/compat/register/middleware.ts +1 -1
  267. package/src/compat/register/otel.ts +5 -9
  268. package/src/compat/register/render.ts +3 -1
  269. package/src/compat/register/use-cache.ts +3 -1
  270. package/src/compat/release.ts +46 -0
  271. package/src/compat/segment/serve.ts +1 -1
  272. package/src/compat/validation/validate.ts +10 -7
  273. package/src/config.ts +51 -7
  274. package/src/css/assets.ts +213 -0
  275. package/src/css/build.ts +174 -238
  276. package/src/dev/server.ts +27 -8
  277. package/src/extensions.ts +4 -0
  278. package/src/render/hooks.ts +13 -0
  279. package/src/render/renderer.ts +52 -32
  280. package/src/request/context.ts +9 -0
  281. package/src/resolve/dynamic.ts +3 -1
  282. package/src/resolve/imports.ts +12 -52
  283. package/src/resolve/package-json.ts +56 -0
  284. package/src/resolve/scan-facts.ts +65 -0
  285. package/src/routing/href.ts +10 -3
  286. package/src/routing/match.ts +287 -0
  287. package/src/routing/metadata-files.ts +19 -5
  288. package/src/routing/proxy-config.ts +137 -0
  289. package/src/routing/proxy.ts +35 -134
  290. package/src/routing/routes.ts +22 -281
  291. package/src/runtime/fingerprint.ts +23 -8
  292. package/src/runtime/load.ts +170 -0
  293. package/src/runtime/loader.ts +215 -43
  294. package/src/runtime/module-cache.ts +13 -0
  295. package/src/runtime/modules.ts +370 -81
  296. package/src/runtime/prebundle.ts +81 -0
  297. package/src/runtime/production.ts +391 -0
  298. package/src/runtime/release-graph.ts +334 -0
  299. package/src/runtime/vendor-build.ts +724 -13
  300. package/src/runtime/vendor.ts +16 -3
  301. package/src/types.ts +2 -0
  302. package/src/utils/fs.ts +89 -3
@@ -0,0 +1,287 @@
1
+ /**
2
+ * Request-time routing: matching a pathname against the built route table and finding an app
3
+ * directory's convention files. The route scan that builds the table lives in `./routes`.
4
+ */
5
+ import path from 'node:path'
6
+ import { readDirListing, toPosixPath } from '../utils/fs'
7
+ import { safeDecode } from '../utils/decode'
8
+ import { extraPageExtensions } from '../extensions'
9
+ import type { NavState, RouteManifestEntry, RouteParamValue } from '../types'
10
+
11
+ const BASE_PAGE_EXTENSIONS = ['tsx', 'ts', 'jsx', 'js', 'mjs'] as const
12
+
13
+ /** Base + compat-registered page extensions, de-duplicated in order. */
14
+ export function pageExtensions(): string[] {
15
+ return [...new Set([...BASE_PAGE_EXTENSIONS, ...extraPageExtensions()])]
16
+ }
17
+
18
+ const globalErrorBase = 'global-error'
19
+
20
+ /** Nearest existing file named `<base>.<ext>` for the convention extensions. */
21
+ export function conventionFileName(dir: string, base: string) {
22
+ const { files } = readDirListing(dir)
23
+ for (const extension of pageExtensions()) {
24
+ if (files.has(`${base}.${extension}`)) return path.join(dir, `${base}.${extension}`)
25
+ }
26
+ return undefined
27
+ }
28
+
29
+ export function findLayouts(appPath: string, routeFilePath: string) {
30
+ return findConventionFiles(appPath, routeFilePath, 'layout')
31
+ }
32
+
33
+ /**
34
+ * Walk from the route's directory up to `appPath`, returning one convention file per segment ordered
35
+ * root-to-leaf. `name` may be a bare base (`'layout'`) or carry an extension - a known extension is stripped
36
+ * so the full extension list is tried. Each segment resolves to the first existing `<base>.<ext>`; when none
37
+ * exists the `.tsx` candidate is returned so existing callers that filter by `existsSync` keep
38
+ * byte-identical behavior.
39
+ */
40
+ export function findConventionFiles(appPath: string, routeFilePath: string, name: string) {
41
+ const base = conventionBase(name)
42
+ const files: string[] = []
43
+ let dir = path.dirname(routeFilePath)
44
+
45
+ while (dir.startsWith(appPath)) {
46
+ files.push(conventionFileName(dir, base) ?? path.join(dir, `${base}.tsx`))
47
+ if (dir === appPath) break
48
+ dir = path.dirname(dir)
49
+ }
50
+
51
+ return files.reverse()
52
+ }
53
+
54
+ function conventionBase(name: string) {
55
+ const match = new RegExp(`\\.(${pageExtensions().join('|')})$`).exec(name)
56
+ return match ? name.slice(0, -match[0].length) : name
57
+ }
58
+
59
+ /** App-root `global-error.*` file, if present (BuildManifest.globalErrorFile). */
60
+ export function findGlobalError(appPath: string) {
61
+ return conventionFileName(appPath, globalErrorBase)
62
+ }
63
+
64
+ export function matchRoute(routes: RouteManifestEntry[], pathname: string) {
65
+ // Trailing-slash tolerance: '/route/' matches the same entry as '/route'
66
+ // (Next redirects these away; static file serving already accepts both).
67
+ const normalized = normalizePathname(pathname)
68
+ for (const route of routes) {
69
+ // Interception entries never match a plain (hard) request; they apply
70
+ // only through matchInterception on soft navigations.
71
+ if (route.interception) continue
72
+ const match = routeRegex(route).exec(normalized)
73
+ if (!match) continue
74
+ return { route, params: routeMatchParams(route, match) }
75
+ }
76
+ return null
77
+ }
78
+
79
+ export function normalizePathname(pathname: string) {
80
+ return pathname.length > 1 ? pathname.replace(/\/+$/, '') || '/' : pathname
81
+ }
82
+
83
+ function routeMatchParams(route: RouteManifestEntry, match: RegExpExecArray) {
84
+ const params: Record<string, RouteParamValue> = {}
85
+ route.params.forEach((name, index) => {
86
+ const decoded = safeDecode(match[index + 1] ?? '')
87
+ // A runtime request whose segment IS the dynamic placeholder for its own
88
+ // param (`[slug]`, whether sent raw or as `%5Bslug%5D`) is Next's "params
89
+ // placeholder": it must surface the ENCODED placeholder (`%5Bslug%5D`), not
90
+ // a decoded `[slug]`. A decoded `[slug]` reads as a fallback param and would
91
+ // trigger a fallback-shell render (failing without a parent Suspense
92
+ // boundary); Next keeps it URL-encoded to render at runtime instead.
93
+ // Fallback-shell generation injects its placeholder params through a
94
+ // separate build path, so this only affects live requests.
95
+ params[name] = decoded === `[${name}]` ? encodeURIComponent(decoded) : decoded
96
+ })
97
+ if (route.catchAll) {
98
+ params[route.catchAll] = (match[route.params.length + 1] ?? '')
99
+ .split('/')
100
+ .filter(Boolean)
101
+ .map(safeDecode)
102
+ }
103
+ return params
104
+ }
105
+
106
+ export interface InterceptionRouteMatch {
107
+ route: RouteManifestEntry
108
+ params: Record<string, RouteParamValue>
109
+ }
110
+
111
+ /**
112
+ * Match a soft navigation against interception entries: the entry's target
113
+ * pattern must match the destination and the navigation must originate at or
114
+ * below the interceptor's base (the children path the current document
115
+ * rendered from). The deepest base wins. A no-op when destination equals the
116
+ * origin (a refresh re-renders whatever produced the current document).
117
+ */
118
+ export function matchInterception(
119
+ routes: RouteManifestEntry[],
120
+ pathname: string,
121
+ fromPath: string | undefined,
122
+ ): InterceptionRouteMatch | null {
123
+ if (!fromPath) return null
124
+ const target = normalizePathname(pathname)
125
+ const from = normalizePathname(fromPath)
126
+ if (target === from) return null
127
+ let best: InterceptionRouteMatch | null = null
128
+ let bestDepth = -1
129
+ for (const route of routes) {
130
+ const interception = route.interception
131
+ if (!interception) continue
132
+ const match = routeRegex(route).exec(target)
133
+ if (!match) continue
134
+ const basePattern = interception.basePattern ? `/${interception.basePattern}` : ''
135
+ if (!new RegExp(`^${basePattern}(?:/.*)?$`).test(from)) continue
136
+ const depth = interception.base.split('/').filter(Boolean).length
137
+ if (depth <= bestDepth) continue
138
+ bestDepth = depth
139
+ best = { route, params: routeMatchParams(route, match) }
140
+ }
141
+ return best
142
+ }
143
+
144
+ export interface RouteRenderSelection {
145
+ route: RouteManifestEntry
146
+ params: Record<string, RouteParamValue>
147
+ /** Pathname the children tree renders from (normalized). */
148
+ childrenPath: string
149
+ /** Internal pathname after rewrites, used to resolve interception slots. */
150
+ targetPath: string
151
+ }
152
+
153
+ /**
154
+ * Pick the entry a page request renders. Hard requests match plainly. Soft navigations first consult
155
+ * interception entries; a slot interception (and a synthetic slot URL reached with known origin) renders the
156
+ * ORIGIN's entry as host - the current page stays while the slot content changes - with the children tree
157
+ * anchored at `childrenPath`.
158
+ */
159
+ export function selectRouteForRequest(
160
+ routes: RouteManifestEntry[],
161
+ pathname: string,
162
+ nav?: import('../types').NavState,
163
+ ): RouteRenderSelection | null {
164
+ const target = normalizePathname(pathname)
165
+ if (nav) {
166
+ const intercepted = matchInterception(routes, target, nav.children)
167
+ if (intercepted) {
168
+ if (!intercepted.route.interception?.slotDir) {
169
+ return {
170
+ route: intercepted.route,
171
+ params: intercepted.params,
172
+ childrenPath: target,
173
+ targetPath: target,
174
+ }
175
+ }
176
+ const host = hostMatch(routes, nav.children)
177
+ if (host) return { ...host, targetPath: target }
178
+ }
179
+ }
180
+ const matched = matchRoute(routes, target)
181
+ if (!matched) return null
182
+ if (nav && matched.route.synthetic && nav.children) {
183
+ const primary = matchRoute(
184
+ routes.filter(route => !route.synthetic),
185
+ target,
186
+ )
187
+ const syntheticOwner = matched.route.syntheticSlotDir
188
+ ? path.dirname(matched.route.syntheticSlotDir)
189
+ : undefined
190
+ const primaryFromOwner =
191
+ primary && syntheticOwner ? path.relative(syntheticOwner, primary.route.file) : undefined
192
+ if (
193
+ primary &&
194
+ primaryFromOwner !== undefined &&
195
+ primaryFromOwner !== '..' &&
196
+ !primaryFromOwner.startsWith(`..${path.sep}`)
197
+ ) {
198
+ return {
199
+ route: primary.route,
200
+ params: primary.params,
201
+ childrenPath: target,
202
+ targetPath: target,
203
+ }
204
+ }
205
+ const host = hostMatch(routes, nav.children)
206
+ if (
207
+ host &&
208
+ host.route !== matched.route &&
209
+ matched.route.syntheticSlotDir &&
210
+ host.route.slotDirs?.includes(matched.route.syntheticSlotDir)
211
+ ) {
212
+ return { ...host, targetPath: target }
213
+ }
214
+ }
215
+ return {
216
+ route: matched.route,
217
+ params: matched.params,
218
+ childrenPath: target,
219
+ targetPath: target,
220
+ }
221
+ }
222
+
223
+ /**
224
+ * Navigation headers from the client soft-nav runtime: `x-pnext-soft-nav`
225
+ * marks the fetch and `x-pnext-nav-state` carries the current document's
226
+ * parallel-route state (URI-encoded JSON).
227
+ */
228
+ export function parseNavState(request: Request): NavState | undefined {
229
+ if (!request.headers.get('x-pnext-soft-nav')) return undefined
230
+ const raw = request.headers.get('x-pnext-nav-state')
231
+ if (!raw) return {}
232
+ try {
233
+ const parsed = JSON.parse(decodeURIComponent(raw)) as unknown
234
+ if (parsed && typeof parsed === 'object') return parsed
235
+ } catch {
236
+ // Malformed state degrades to a plain soft navigation.
237
+ }
238
+ return {}
239
+ }
240
+
241
+ function hostMatch(
242
+ routes: RouteManifestEntry[],
243
+ fromPath: string | undefined,
244
+ ): RouteRenderSelection | null {
245
+ if (!fromPath) return null
246
+ const from = normalizePathname(fromPath)
247
+ const host = matchRoute(routes, from)
248
+ if (host?.route.kind !== 'page') return null
249
+ return { route: host.route, params: host.params, childrenPath: from, targetPath: from }
250
+ }
251
+
252
+ const routeRegexCache = new WeakMap<RouteManifestEntry, RegExp>()
253
+
254
+ function routeRegex(route: RouteManifestEntry) {
255
+ let regex = routeRegexCache.get(route)
256
+ if (!regex) {
257
+ regex = new RegExp(`^${route.pattern}$`)
258
+ routeRegexCache.set(route, regex)
259
+ }
260
+ return regex
261
+ }
262
+
263
+ export function serverActionsUnsupportedMessage(offender: {
264
+ file: string
265
+ route?: string
266
+ root?: string
267
+ }) {
268
+ const file = offender.root
269
+ ? toPosixPath(path.relative(offender.root, offender.file))
270
+ : offender.file
271
+ const reach = offender.route ? `, reachable from the route ${offender.route}` : ''
272
+ return [
273
+ `Server actions need next compat, and this app is pure core.`,
274
+ ``,
275
+ ` ${file} has a 'use server' directive${reach}.`,
276
+ ``,
277
+ `pnext will not build this app: core has no server-action registry and no`,
278
+ `dispatch endpoint, so the action could only render as a form that submits`,
279
+ `nowhere.`,
280
+ ``,
281
+ `Fix it one of two ways:`,
282
+ ` 1. Enable compat in pnext.config.ts:`,
283
+ ` export default { compat: { next: true } }`,
284
+ ` 2. Or delete the 'use server' directive from ${file}, if those exports are`,
285
+ ` only ever called on the server (in core the directive does nothing).`,
286
+ ].join('\n')
287
+ }
@@ -13,6 +13,7 @@ import type {
13
13
  } from '../types'
14
14
  import { escapeHtml, stringifyHtmlValue } from '../utils/html'
15
15
  import { toPosixPath } from '../utils/fs'
16
+ import { servedAppFiles, servedRelease } from '../runtime/production'
16
17
 
17
18
  export type StaticMetadataKind =
18
19
  | 'favicon'
@@ -65,6 +66,8 @@ export function clearMetadataFileCaches() {
65
66
  }
66
67
 
67
68
  export function discoverStaticMetadataFiles(appPath: string): StaticMetadataFile[] {
69
+ const served = servedRelease()
70
+ if (served) return served.staticMetadataFiles ?? []
68
71
  const cached = staticMetadataFiles.get(appPath)
69
72
  if (cached) return cached
70
73
  const files = existsSync(appPath)
@@ -343,15 +346,26 @@ function metadataAppliesToRoute(file: StaticMetadataFile, routeDir: string) {
343
346
  return routeDir === dir || routeDir.startsWith(`${dir}/`)
344
347
  }
345
348
 
349
+ // A release's tree is immutable: its inventory is derived once per release.
350
+ const releasedDynamicMetadataFiles = new WeakMap<object, DynamicMetadataFile[]>()
351
+
346
352
  function discoverDynamicMetadataFiles(appPath: string): DynamicMetadataFile[] {
347
- const cached = dynamicMetadataFiles.get(appPath)
353
+ const served = servedRelease()?.conventions
354
+ const cached = served
355
+ ? releasedDynamicMetadataFiles.get(served)
356
+ : dynamicMetadataFiles.get(appPath)
348
357
  if (cached) return cached
349
- const files = existsSync(appPath)
350
- ? listFilesSync(appPath)
358
+ const files = served
359
+ ? servedAppFiles(served)
351
360
  .flatMap(file => dynamicMetadataFile(appPath, file) ?? [])
352
361
  .sort((a, b) => a.relative.localeCompare(b.relative))
353
- : []
354
- dynamicMetadataFiles.set(appPath, files)
362
+ : existsSync(appPath)
363
+ ? listFilesSync(appPath)
364
+ .flatMap(file => dynamicMetadataFile(appPath, file) ?? [])
365
+ .sort((a, b) => a.relative.localeCompare(b.relative))
366
+ : []
367
+ if (served) releasedDynamicMetadataFiles.set(served, files)
368
+ else dynamicMetadataFiles.set(appPath, files)
355
369
  return files
356
370
  }
357
371
 
@@ -0,0 +1,137 @@
1
+ /** The proxy's literal `config` read off its source: build and dev only, a release carries it. */
2
+ import { readFileSync } from 'node:fs'
3
+ import path from 'node:path'
4
+ import type { ResolvedConfig } from '../config'
5
+ import { loadNative } from '../utils/native-require'
6
+ import { findProxyFile, type ProxyConfig } from './proxy'
7
+
8
+ // Lazy: the oxc-parser native binding costs ~12.6 MB RSS; load it only when a parse happens.
9
+ const parseSync: typeof import('oxc-parser').parseSync = (...args) =>
10
+ // eslint-disable-next-line @typescript-eslint/no-require-imports
11
+ loadNative(() => require('oxc-parser') as typeof import('oxc-parser')).parseSync(...args)
12
+
13
+ /** The proxy a production release serves: its source and, when wholly literal, its matcher config. */
14
+ export function releasedProxy(config: ResolvedConfig) {
15
+ const file = findProxyFile(config)
16
+ if (!file) return undefined
17
+ try {
18
+ return { file, config: staticProxyConfig(file, readFileSync(file, 'utf8')) }
19
+ } catch {
20
+ return { file }
21
+ }
22
+ }
23
+
24
+ export function staticProxyConfig(file: string, source: string): ProxyConfig | undefined {
25
+ const result = parseSync(file, source, { lang: parserLang(file) })
26
+ // A recovered parse may have dropped the very declaration we are reading.
27
+ if (result.errors.length > 0) return undefined
28
+ for (const statement of result.program.body as StaticNode[]) {
29
+ if (statement.type !== 'ExportNamedDeclaration') continue
30
+ const declaration = statement.declaration
31
+ if (declaration?.type !== 'VariableDeclaration') continue
32
+ for (const declarator of declaration.declarations ?? []) {
33
+ if (declarator.id?.type !== 'Identifier' || declarator.id.name !== 'config') continue
34
+ const value = staticValue(declarator.init)
35
+ return isProxyConfigShape(value) ? (value as ProxyConfig) : undefined
36
+ }
37
+ }
38
+ return undefined
39
+ }
40
+
41
+ function parserLang(file: string) {
42
+ const ext = path.extname(file)
43
+ if (ext === '.tsx') return 'tsx'
44
+ if (ext === '.jsx') return 'jsx'
45
+ return ext === '.js' || ext === '.mjs' ? 'js' : 'ts'
46
+ }
47
+
48
+ /** Structural view of the oxc AST nodes this file reads — no full type import. */
49
+ interface StaticNode {
50
+ type: string
51
+ name?: string
52
+ value?: unknown
53
+ computed?: boolean
54
+ shorthand?: boolean
55
+ key?: StaticNode
56
+ init?: StaticNode
57
+ id?: StaticNode
58
+ declaration?: StaticNode
59
+ declarations?: StaticNode[]
60
+ expression?: StaticNode
61
+ properties?: StaticNode[]
62
+ elements?: (StaticNode | null)[]
63
+ expressions?: StaticNode[]
64
+ quasis?: { value?: { cooked?: string } }[]
65
+ }
66
+
67
+ /** Literal-only evaluation; `undefined` marks "not statically known". */
68
+ function staticValue(node: StaticNode | undefined | null): unknown {
69
+ if (!node) return undefined
70
+ switch (node.type) {
71
+ // `… as const` / `… satisfies ProxyConfig` wrap the literal, they never change it.
72
+ case 'TSAsExpression':
73
+ case 'TSSatisfiesExpression':
74
+ case 'TSNonNullExpression':
75
+ return staticValue(node.expression)
76
+ case 'Literal':
77
+ case 'StringLiteral':
78
+ case 'NumericLiteral':
79
+ case 'BooleanLiteral':
80
+ // A regex literal reports `value: null` here; it is not a matcher shape.
81
+ return node.value === null ? undefined : node.value
82
+ case 'TemplateLiteral':
83
+ return node.expressions?.length === 0
84
+ ? (node.quasis?.[0]?.value?.cooked ?? undefined)
85
+ : undefined
86
+ case 'ArrayExpression': {
87
+ const items: unknown[] = []
88
+ for (const element of node.elements ?? []) {
89
+ const value = staticValue(element)
90
+ if (value === undefined) return undefined
91
+ items.push(value)
92
+ }
93
+ return items
94
+ }
95
+ case 'ObjectExpression': {
96
+ const object: Record<string, unknown> = {}
97
+ for (const property of node.properties ?? []) {
98
+ if (property.type !== 'Property' && property.type !== 'ObjectProperty') return undefined
99
+ if (property.computed) return undefined
100
+ const key =
101
+ property.key?.type === 'Identifier' ? property.key.name : (property.key?.value as string)
102
+ if (typeof key !== 'string') return undefined
103
+ const value = staticValue(property.value as StaticNode | undefined)
104
+ if (value === undefined) return undefined
105
+ object[key] = value
106
+ }
107
+ return object
108
+ }
109
+ default:
110
+ return undefined
111
+ }
112
+ }
113
+
114
+ /** Only a config whose `matcher` is exactly the documented shape may gate. */
115
+ function isProxyConfigShape(value: unknown): boolean {
116
+ if (typeof value !== 'object' || value === null) return false
117
+ const matcher = (value as { matcher?: unknown }).matcher
118
+ if (matcher === undefined) return true
119
+ const items = Array.isArray(matcher) ? matcher : [matcher]
120
+ return items.every(item => {
121
+ if (typeof item === 'string') return true
122
+ if (typeof item !== 'object' || item === null) return false
123
+ const { source, has, missing } = item as Record<string, unknown>
124
+ if (typeof source !== 'string') return false
125
+ return [has, missing].every(
126
+ list => list === undefined || (Array.isArray(list) && list.every(isConditionShape)),
127
+ )
128
+ })
129
+ }
130
+
131
+ function isConditionShape(value: unknown): boolean {
132
+ if (typeof value !== 'object' || value === null) return false
133
+ const { type, key, value: expected } = value as Record<string, unknown>
134
+ if (type !== 'header' && type !== 'query' && type !== 'cookie' && type !== 'host') return false
135
+ if (key !== undefined && typeof key !== 'string') return false
136
+ return expected === undefined || typeof expected === 'string'
137
+ }