vinext 0.1.1 → 0.1.3

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 (266) hide show
  1. package/README.md +2 -5
  2. package/dist/build/client-build-config.d.ts +7 -1
  3. package/dist/build/client-build-config.js +9 -1
  4. package/dist/build/prerender.d.ts +9 -1
  5. package/dist/build/prerender.js +41 -12
  6. package/dist/build/run-prerender.d.ts +10 -2
  7. package/dist/build/run-prerender.js +15 -1
  8. package/dist/check.js +4 -3
  9. package/dist/client/app-nav-failure-handler.d.ts +8 -0
  10. package/dist/client/app-nav-failure-handler.js +44 -0
  11. package/dist/client/navigation-runtime.d.ts +3 -2
  12. package/dist/client/vinext-next-data.d.ts +18 -1
  13. package/dist/client/window-next.d.ts +8 -5
  14. package/dist/client/window-next.js +12 -1
  15. package/dist/cloudflare/src/cache/cdn-adapter.runtime.js +6 -1
  16. package/dist/config/config-matchers.d.ts +11 -4
  17. package/dist/config/config-matchers.js +88 -16
  18. package/dist/config/next-config.d.ts +59 -4
  19. package/dist/config/next-config.js +149 -48
  20. package/dist/deploy.d.ts +30 -11
  21. package/dist/deploy.js +189 -101
  22. package/dist/entries/app-browser-entry.d.ts +9 -3
  23. package/dist/entries/app-browser-entry.js +21 -3
  24. package/dist/entries/app-rsc-entry.d.ts +2 -0
  25. package/dist/entries/app-rsc-entry.js +71 -6
  26. package/dist/entries/app-rsc-manifest.js +2 -0
  27. package/dist/entries/app-ssr-entry.js +1 -1
  28. package/dist/entries/pages-client-entry.js +54 -9
  29. package/dist/entries/pages-server-entry.js +48 -11
  30. package/dist/index.d.ts +0 -2
  31. package/dist/index.js +285 -139
  32. package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
  33. package/dist/plugins/dynamic-preload-metadata.js +415 -0
  34. package/dist/plugins/extensionless-dynamic-import.d.ts +6 -0
  35. package/dist/plugins/extensionless-dynamic-import.js +152 -0
  36. package/dist/plugins/og-assets.js +2 -2
  37. package/dist/plugins/optimize-imports.d.ts +10 -5
  38. package/dist/plugins/optimize-imports.js +27 -21
  39. package/dist/plugins/postcss.js +7 -7
  40. package/dist/plugins/sass.d.ts +53 -24
  41. package/dist/plugins/sass.js +249 -1
  42. package/dist/plugins/typeof-window.d.ts +14 -0
  43. package/dist/plugins/typeof-window.js +150 -0
  44. package/dist/plugins/wasm-module-import.d.ts +15 -0
  45. package/dist/plugins/wasm-module-import.js +50 -0
  46. package/dist/routing/app-route-graph.d.ts +25 -2
  47. package/dist/routing/app-route-graph.js +91 -22
  48. package/dist/routing/file-matcher.d.ts +10 -1
  49. package/dist/routing/file-matcher.js +23 -2
  50. package/dist/routing/pages-router.js +3 -3
  51. package/dist/routing/utils.d.ts +35 -6
  52. package/dist/routing/utils.js +59 -7
  53. package/dist/server/api-handler.d.ts +6 -1
  54. package/dist/server/api-handler.js +21 -15
  55. package/dist/server/app-browser-action-result.d.ts +19 -6
  56. package/dist/server/app-browser-action-result.js +19 -10
  57. package/dist/server/app-browser-entry.js +269 -297
  58. package/dist/server/app-browser-error.d.ts +10 -3
  59. package/dist/server/app-browser-error.js +47 -6
  60. package/dist/server/app-browser-history-controller.d.ts +104 -0
  61. package/dist/server/app-browser-history-controller.js +210 -0
  62. package/dist/server/app-browser-hydration.d.ts +2 -0
  63. package/dist/server/app-browser-hydration.js +1 -0
  64. package/dist/server/app-browser-navigation-controller.d.ts +7 -4
  65. package/dist/server/app-browser-navigation-controller.js +33 -9
  66. package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
  67. package/dist/server/app-browser-rsc-redirect.js +30 -8
  68. package/dist/server/app-browser-server-action-navigation.d.ts +6 -0
  69. package/dist/server/app-browser-server-action-navigation.js +9 -0
  70. package/dist/server/app-browser-state.js +4 -7
  71. package/dist/server/app-browser-stream.js +86 -43
  72. package/dist/server/app-browser-visible-commit.js +1 -1
  73. package/dist/server/app-elements-wire.d.ts +6 -1
  74. package/dist/server/app-elements-wire.js +14 -4
  75. package/dist/server/app-elements.d.ts +2 -2
  76. package/dist/server/app-elements.js +2 -2
  77. package/dist/server/app-fallback-renderer.d.ts +3 -1
  78. package/dist/server/app-fallback-renderer.js +6 -2
  79. package/dist/server/app-middleware.js +1 -0
  80. package/dist/server/app-optimistic-routing.js +24 -3
  81. package/dist/server/app-page-boundary-render.d.ts +3 -1
  82. package/dist/server/app-page-boundary-render.js +31 -16
  83. package/dist/server/app-page-cache-render.d.ts +53 -0
  84. package/dist/server/app-page-cache-render.js +91 -0
  85. package/dist/server/app-page-cache.d.ts +16 -2
  86. package/dist/server/app-page-cache.js +71 -8
  87. package/dist/server/app-page-dispatch.d.ts +34 -0
  88. package/dist/server/app-page-dispatch.js +167 -97
  89. package/dist/server/app-page-element-builder.d.ts +23 -2
  90. package/dist/server/app-page-element-builder.js +42 -10
  91. package/dist/server/app-page-execution.d.ts +7 -2
  92. package/dist/server/app-page-execution.js +53 -18
  93. package/dist/server/app-page-probe.d.ts +1 -0
  94. package/dist/server/app-page-probe.js +4 -0
  95. package/dist/server/app-page-render-observation.d.ts +3 -1
  96. package/dist/server/app-page-render-observation.js +17 -1
  97. package/dist/server/app-page-render.d.ts +13 -2
  98. package/dist/server/app-page-render.js +48 -17
  99. package/dist/server/app-page-request.d.ts +3 -0
  100. package/dist/server/app-page-request.js +5 -3
  101. package/dist/server/app-page-response.js +1 -1
  102. package/dist/server/app-page-route-wiring.d.ts +5 -1
  103. package/dist/server/app-page-route-wiring.js +21 -11
  104. package/dist/server/app-page-stream.d.ts +16 -9
  105. package/dist/server/app-page-stream.js +12 -9
  106. package/dist/server/app-pages-bridge.d.ts +18 -0
  107. package/dist/server/app-pages-bridge.js +22 -5
  108. package/dist/server/app-ppr-fallback-shell-render.d.ts +17 -0
  109. package/dist/server/app-ppr-fallback-shell-render.js +26 -0
  110. package/dist/server/app-ppr-fallback-shell.d.ts +13 -1
  111. package/dist/server/app-ppr-fallback-shell.js +8 -1
  112. package/dist/server/app-route-handler-dispatch.js +9 -2
  113. package/dist/server/app-route-handler-policy.d.ts +1 -0
  114. package/dist/server/app-route-handler-response.js +11 -10
  115. package/dist/server/app-route-handler-runtime.js +12 -1
  116. package/dist/server/app-router-entry.js +5 -0
  117. package/dist/server/app-rsc-cache-busting.js +2 -0
  118. package/dist/server/app-rsc-handler.d.ts +25 -0
  119. package/dist/server/app-rsc-handler.js +153 -53
  120. package/dist/server/app-rsc-response-finalizer.js +1 -1
  121. package/dist/server/app-rsc-route-matching.d.ts +3 -0
  122. package/dist/server/app-rsc-route-matching.js +2 -0
  123. package/dist/server/app-segment-config.d.ts +9 -1
  124. package/dist/server/app-segment-config.js +12 -3
  125. package/dist/server/app-server-action-execution.d.ts +12 -0
  126. package/dist/server/app-server-action-execution.js +47 -15
  127. package/dist/server/app-ssr-entry.d.ts +2 -0
  128. package/dist/server/app-ssr-entry.js +81 -8
  129. package/dist/server/app-ssr-stream.js +9 -1
  130. package/dist/server/cache-control.js +4 -0
  131. package/dist/server/dev-lockfile.js +2 -1
  132. package/dist/server/dev-server.d.ts +2 -2
  133. package/dist/server/dev-server.js +287 -63
  134. package/dist/server/headers.d.ts +8 -1
  135. package/dist/server/headers.js +8 -1
  136. package/dist/server/hybrid-route-priority.d.ts +22 -0
  137. package/dist/server/hybrid-route-priority.js +33 -0
  138. package/dist/server/image-optimization.d.ts +18 -9
  139. package/dist/server/image-optimization.js +37 -23
  140. package/dist/server/implicit-tags.d.ts +2 -1
  141. package/dist/server/implicit-tags.js +4 -1
  142. package/dist/server/instrumentation-runtime.d.ts +6 -0
  143. package/dist/server/instrumentation-runtime.js +8 -0
  144. package/dist/server/isr-decision.d.ts +79 -0
  145. package/dist/server/isr-decision.js +70 -0
  146. package/dist/server/metadata-route-response.js +5 -3
  147. package/dist/server/middleware-runtime.d.ts +13 -0
  148. package/dist/server/middleware-runtime.js +11 -7
  149. package/dist/server/middleware.js +1 -0
  150. package/dist/server/navigation-planner.d.ts +186 -22
  151. package/dist/server/navigation-planner.js +302 -0
  152. package/dist/server/navigation-trace.d.ts +18 -1
  153. package/dist/server/navigation-trace.js +18 -1
  154. package/dist/server/normalize-path.d.ts +0 -8
  155. package/dist/server/normalize-path.js +3 -1
  156. package/dist/server/otel-tracer-extension.d.ts +45 -0
  157. package/dist/server/otel-tracer-extension.js +89 -0
  158. package/dist/server/pages-api-route.d.ts +20 -3
  159. package/dist/server/pages-api-route.js +19 -3
  160. package/dist/server/pages-asset-tags.d.ts +16 -4
  161. package/dist/server/pages-asset-tags.js +22 -12
  162. package/dist/server/pages-data-route.d.ts +8 -1
  163. package/dist/server/pages-data-route.js +16 -3
  164. package/dist/server/pages-get-initial-props.d.ts +54 -4
  165. package/dist/server/pages-get-initial-props.js +43 -1
  166. package/dist/server/pages-node-compat.d.ts +3 -11
  167. package/dist/server/pages-node-compat.js +175 -122
  168. package/dist/server/pages-page-data.d.ts +39 -2
  169. package/dist/server/pages-page-data.js +261 -46
  170. package/dist/server/pages-page-handler.d.ts +5 -2
  171. package/dist/server/pages-page-handler.js +78 -25
  172. package/dist/server/pages-page-response.d.ts +47 -2
  173. package/dist/server/pages-page-response.js +73 -9
  174. package/dist/server/pages-readiness.d.ts +1 -1
  175. package/dist/server/pages-request-pipeline.d.ts +16 -1
  176. package/dist/server/pages-request-pipeline.js +96 -38
  177. package/dist/server/pregenerated-concrete-paths.d.ts +1 -17
  178. package/dist/server/pregenerated-concrete-paths.js +2 -19
  179. package/dist/server/prerender-manifest.d.ts +33 -0
  180. package/dist/server/prerender-manifest.js +54 -0
  181. package/dist/server/prerender-route-params.d.ts +1 -2
  182. package/dist/server/prod-server.d.ts +39 -1
  183. package/dist/server/prod-server.js +107 -37
  184. package/dist/server/request-pipeline.d.ts +3 -15
  185. package/dist/server/request-pipeline.js +58 -47
  186. package/dist/server/rsc-stream-hints.d.ts +5 -1
  187. package/dist/server/rsc-stream-hints.js +6 -1
  188. package/dist/server/seed-cache.js +10 -18
  189. package/dist/shims/app-router-scroll-state.d.ts +3 -1
  190. package/dist/shims/app-router-scroll-state.js +14 -2
  191. package/dist/shims/app-router-scroll.d.ts +3 -0
  192. package/dist/shims/app-router-scroll.js +28 -18
  193. package/dist/shims/cache-runtime.js +12 -4
  194. package/dist/shims/cache.d.ts +1 -0
  195. package/dist/shims/cache.js +1 -1
  196. package/dist/shims/cdn-cache.d.ts +5 -5
  197. package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
  198. package/dist/shims/dynamic-preload-chunks.js +79 -0
  199. package/dist/shims/dynamic.d.ts +4 -0
  200. package/dist/shims/dynamic.js +4 -2
  201. package/dist/shims/error-boundary.d.ts +6 -4
  202. package/dist/shims/error-boundary.js +7 -0
  203. package/dist/shims/error.js +38 -11
  204. package/dist/shims/error.react-server.d.ts +9 -0
  205. package/dist/shims/error.react-server.js +6 -0
  206. package/dist/shims/fetch-cache.d.ts +11 -1
  207. package/dist/shims/fetch-cache.js +55 -20
  208. package/dist/shims/hash-scroll.js +6 -1
  209. package/dist/shims/head.js +6 -1
  210. package/dist/shims/headers.d.ts +16 -2
  211. package/dist/shims/headers.js +66 -5
  212. package/dist/shims/image-config.js +7 -1
  213. package/dist/shims/internal/als-registry.js +28 -1
  214. package/dist/shims/internal/app-route-detection.d.ts +6 -3
  215. package/dist/shims/internal/app-route-detection.js +18 -23
  216. package/dist/shims/internal/app-router-context.d.ts +5 -0
  217. package/dist/shims/internal/hybrid-client-route-owner.d.ts +31 -0
  218. package/dist/shims/internal/hybrid-client-route-owner.js +143 -0
  219. package/dist/shims/internal/navigation-untracked.d.ts +35 -0
  220. package/dist/shims/internal/navigation-untracked.js +55 -0
  221. package/dist/shims/internal/pages-data-target.d.ts +7 -2
  222. package/dist/shims/internal/pages-data-target.js +17 -8
  223. package/dist/shims/internal/pages-router-accessor.d.ts +19 -0
  224. package/dist/shims/internal/pages-router-accessor.js +13 -0
  225. package/dist/shims/internal/router-context.d.ts +2 -1
  226. package/dist/shims/internal/router-context.js +3 -1
  227. package/dist/shims/link.js +12 -5
  228. package/dist/shims/metadata.d.ts +6 -2
  229. package/dist/shims/metadata.js +32 -14
  230. package/dist/shims/navigation.d.ts +14 -17
  231. package/dist/shims/navigation.js +93 -46
  232. package/dist/shims/ppr-fallback-shell.d.ts +5 -1
  233. package/dist/shims/ppr-fallback-shell.js +28 -7
  234. package/dist/shims/router.d.ts +13 -2
  235. package/dist/shims/router.js +434 -116
  236. package/dist/shims/script-nonce-context.d.ts +1 -1
  237. package/dist/shims/script-nonce-context.js +11 -3
  238. package/dist/shims/server.d.ts +33 -2
  239. package/dist/shims/server.js +75 -18
  240. package/dist/shims/slot.js +1 -1
  241. package/dist/shims/unified-request-context.js +2 -0
  242. package/dist/typegen.js +1 -0
  243. package/dist/utils/built-asset-url.d.ts +4 -0
  244. package/dist/utils/built-asset-url.js +11 -0
  245. package/dist/utils/client-build-manifest.js +15 -5
  246. package/dist/utils/client-runtime-metadata.d.ts +45 -0
  247. package/dist/utils/client-runtime-metadata.js +63 -0
  248. package/dist/utils/commonjs-loader.d.ts +16 -0
  249. package/dist/utils/commonjs-loader.js +100 -0
  250. package/dist/utils/deployment-id.d.ts +8 -0
  251. package/dist/utils/deployment-id.js +22 -0
  252. package/dist/utils/hash.d.ts +17 -1
  253. package/dist/utils/hash.js +36 -1
  254. package/dist/utils/html-limited-bots.d.ts +18 -1
  255. package/dist/utils/html-limited-bots.js +23 -1
  256. package/dist/utils/lazy-chunks.d.ts +27 -1
  257. package/dist/utils/lazy-chunks.js +65 -1
  258. package/dist/utils/manifest-paths.d.ts +20 -2
  259. package/dist/utils/manifest-paths.js +38 -3
  260. package/dist/utils/parse-cookie.d.ts +13 -0
  261. package/dist/utils/parse-cookie.js +52 -0
  262. package/dist/utils/path.d.ts +8 -1
  263. package/dist/utils/path.js +13 -1
  264. package/package.json +2 -2
  265. package/dist/shims/internal/parse-cookie-header.d.ts +0 -14
  266. package/dist/shims/internal/parse-cookie-header.js +0 -30
@@ -8,17 +8,14 @@ import { isUnknownRecord } from "../utils/record.js";
8
8
  import { resolveRequestHost, resolveRequestProtocol, trustProxy, trustedHosts } from "./proxy-trust.js";
9
9
  import { DEFAULT_DEVICE_SIZES, DEFAULT_IMAGE_SIZES, isImageOptimizationPath, isSafeImageContentType, parseImageParams } from "./image-optimization.js";
10
10
  import { installSocketErrorBackstop } from "./socket-error-backstop.js";
11
- import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix, resolveAssetsDir } from "../utils/asset-prefix.js";
11
+ import { ASSET_PREFIX_URL_DIR, assetPrefixPathname, isAbsoluteAssetPrefix } from "../utils/asset-prefix.js";
12
12
  import { CONTENT_TYPES, StaticFileCache, etagFromFilenameHash } from "./static-file-cache.js";
13
13
  import { buildNextDataNotFoundResponse, isNextDataPathname, parseNextDataPathname } from "./pages-data-route.js";
14
14
  import { collectInlineCssManifest } from "../build/inline-css.js";
15
15
  import { mergeHeaders } from "./worker-utils.js";
16
- import { runPagesRequest } from "./pages-request-pipeline.js";
17
- import { manifestFileWithBase } from "../utils/manifest-paths.js";
16
+ import { runPagesRequest, wrapMiddlewareWithBasePath } from "./pages-request-pipeline.js";
18
17
  import { readTrustedPrerenderRouteParamsFromHeaders, serializePrerenderRouteParamsHeader } from "./prerender-route-params.js";
19
- import { computeLazyChunks } from "../utils/lazy-chunks.js";
20
- import { findClientEntryFile, findPagesClientEntryFile, readClientBuildManifest } from "../utils/client-build-manifest.js";
21
- import { findClientEntryFileFromVinextManifest, findPagesClientEntryFileFromVinextManifest, readClientEntryManifest } from "../utils/client-entry-manifest.js";
18
+ import { computeClientRuntimeMetadata } from "../utils/client-runtime-metadata.js";
22
19
  import { readPrerenderSecret } from "../build/server-manifest.js";
23
20
  import { seedMemoryCacheFromPrerender } from "./seed-cache.js";
24
21
  import fs from "node:fs";
@@ -48,6 +45,68 @@ import { Readable, pipeline } from "node:stream";
48
45
  * - dist/server/index.js — RSC entry (default export: handler(Request) → Response)
49
46
  * - dist/server/ssr/index.js — SSR entry (imported by RSC entry at runtime)
50
47
  */
48
+ /**
49
+ * mtime of the build each bare (query-less) server-entry URL was first
50
+ * imported from in this process. Node's ESM cache pins a bare URL to that
51
+ * build forever, so rebuilds to the same path must be detected and loaded
52
+ * through a cache-busted URL instead.
53
+ */
54
+ const bareServerEntryMtimes = /* @__PURE__ */ new Map();
55
+ /**
56
+ * Import a built server entry module (App Router RSC entry or Pages Router
57
+ * server entry) by absolute file path.
58
+ *
59
+ * The first import of a given path uses the plain file:// URL with NO query
60
+ * string. This is load-bearing: code-split builds emit lazy chunks that
61
+ * import the entry back by bare specifier (default Vite builds on both
62
+ * supported majors — Rollup on Vite 7 and Rolldown on Vite 8 — hoist modules
63
+ * shared between the entry's static graph and lazy route chunks into the
64
+ * entry chunk, which the chunks then import as e.g. "../../index.js").
65
+ * Node keys its ESM cache on the full URL including the query string, so if
66
+ * the server imported the entry as `index.js?t=<mtime>`, a chunk's bare
67
+ * back-import would evaluate the entire server bundle a second time and
68
+ * module-level singletons (db pools, service registries) would silently
69
+ * diverge between the two copies. See
70
+ * https://github.com/cloudflare/vinext/issues/1923.
71
+ *
72
+ * A `?t=<mtime>` query string is appended only when the same path is
73
+ * imported again after a rebuild (different mtime) — e.g. test suites that
74
+ * rebuild a fixture to the same output path within one process — where the
75
+ * bare URL's cache entry would return the stale previous build. Note this
76
+ * rebuild branch trades the single-instance guarantee back: chunks that
77
+ * import the entry by bare path still resolve to the FIRST build's cache
78
+ * entry, so freshness and single-instance only hold together on the first
79
+ * import of a path. Production processes import each entry path exactly
80
+ * once and always get both.
81
+ *
82
+ * The entry is imported via its canonical real path: the bundler
83
+ * canonicalizes module ids with fs.realpathSync.native, so chunks evaluate
84
+ * under realpath-based URLs and their relative imports resolve to realpath
85
+ * URLs too. Importing the entry through a symlinked path (macOS /var/...
86
+ * tmpdirs, symlinked deploy directories) would otherwise create a second
87
+ * instance keyed on the symlinked URL.
88
+ *
89
+ * Exported for direct unit testing of the URL choice.
90
+ */
91
+ function resolveServerEntryImportUrl(entryPath) {
92
+ let canonicalEntryPath;
93
+ try {
94
+ canonicalEntryPath = fs.realpathSync.native(entryPath);
95
+ } catch {
96
+ canonicalEntryPath = entryPath;
97
+ }
98
+ const href = pathToFileURL(canonicalEntryPath).href;
99
+ const mtime = fs.statSync(canonicalEntryPath).mtimeMs;
100
+ const bareMtime = bareServerEntryMtimes.get(href);
101
+ if (bareMtime === void 0 || bareMtime === mtime) {
102
+ bareServerEntryMtimes.set(href, mtime);
103
+ return href;
104
+ }
105
+ return `${href}?t=${mtime}`;
106
+ }
107
+ async function importServerEntryModule(entryPath) {
108
+ return import(resolveServerEntryImportUrl(entryPath));
109
+ }
51
110
  /** Convert a Node.js IncomingMessage into a ReadableStream for Web Request body. */
52
111
  function readNodeStream(req) {
53
112
  return new ReadableStream({ start(controller) {
@@ -142,6 +201,7 @@ function toWebHeaders(headersRecord) {
142
201
  }
143
202
  function appendWebHeader(headers, key, value) {
144
203
  if (value === void 0) return;
204
+ if (key.startsWith(":")) return;
145
205
  if (Array.isArray(value)) {
146
206
  for (const item of value) headers.append(key, item);
147
207
  return;
@@ -158,8 +218,14 @@ const NO_BODY_RESPONSE_STATUSES = new Set([
158
218
  205,
159
219
  304
160
220
  ]);
161
- function omitHeadersCaseInsensitive(headersRecord, names) {
162
- const targets = new Set(names.map((name) => name.toLowerCase()));
221
+ const OMIT_BODY_HEADERS = new Set(["content-length", "content-type"]);
222
+ const OMIT_STATIC_RESPONSE_HEADERS = new Set([
223
+ VINEXT_STATIC_FILE_HEADER,
224
+ "content-encoding",
225
+ "content-length",
226
+ "content-type"
227
+ ]);
228
+ function omitHeadersCaseInsensitive(headersRecord, targets) {
163
229
  const filtered = {};
164
230
  for (const [key, value] of Object.entries(headersRecord)) {
165
231
  if (targets.has(key.toLowerCase())) continue;
@@ -172,6 +238,15 @@ function matchesIfNoneMatchHeader(ifNoneMatch, etag) {
172
238
  if (ifNoneMatch === "*") return true;
173
239
  return ifNoneMatch.split(",").map((value) => value.trim()).some((value) => value === etag);
174
240
  }
241
+ function installClientBuildManifestGlobals(clientDir, assetBase, assetPrefix) {
242
+ const metadata = computeClientRuntimeMetadata({
243
+ clientDir,
244
+ assetBase,
245
+ assetPrefix
246
+ });
247
+ globalThis.__VINEXT_LAZY_CHUNKS__ = metadata.lazyChunks;
248
+ globalThis.__VINEXT_DYNAMIC_PRELOADS__ = metadata.dynamicPreloads;
249
+ }
175
250
  function isNoBodyResponseStatus(status) {
176
251
  return NO_BODY_RESPONSE_STATUSES.has(status);
177
252
  }
@@ -212,7 +287,7 @@ function sendCompressed(req, res, body, contentType, statusCode, extraHeaders =
212
287
  const buf = typeof body === "string" ? Buffer.from(body) : body;
213
288
  const baseType = contentType.split(";")[0].trim();
214
289
  const encoding = compress ? negotiateEncoding(req) : null;
215
- const headersWithoutBodyHeaders = omitHeadersCaseInsensitive(extraHeaders, ["content-length", "content-type"]);
290
+ const headersWithoutBodyHeaders = omitHeadersCaseInsensitive(extraHeaders, OMIT_BODY_HEADERS);
216
291
  const writeHead = (headers) => {
217
292
  if (statusText) res.writeHead(statusCode, statusText, headers);
218
293
  else res.writeHead(statusCode, headers);
@@ -619,19 +694,15 @@ function readSsrManifest(clientDir) {
619
694
  function installPagesClientAssetGlobals(options) {
620
695
  const ssrManifest = readSsrManifest(options.clientDir);
621
696
  globalThis.__VINEXT_SSR_MANIFEST__ = Object.keys(ssrManifest).length > 0 ? ssrManifest : void 0;
622
- const buildManifest = readClientBuildManifest(path.join(options.clientDir, ".vite", "manifest.json"));
623
- const clientEntryManifest = readClientEntryManifest(options.clientDir);
624
- const entryOptions = {
697
+ const metadata = computeClientRuntimeMetadata({
625
698
  clientDir: options.clientDir,
626
- assetsSubdir: options.assetsSubdir,
627
699
  assetBase: options.assetBase,
628
- ...buildManifest ? { buildManifest } : {}
629
- };
630
- globalThis.__VINEXT_CLIENT_ENTRY__ = options.clientEntryLookup === "pages-client-entry" ? findPagesClientEntryFileFromVinextManifest(clientEntryManifest, options.assetBase) ?? findPagesClientEntryFile(entryOptions) : findClientEntryFileFromVinextManifest(clientEntryManifest, options.assetBase) ?? findClientEntryFile(entryOptions);
631
- if (buildManifest) {
632
- const lazyChunks = computeLazyChunks(buildManifest).map((file) => manifestFileWithBase(file, options.assetBase));
633
- globalThis.__VINEXT_LAZY_CHUNKS__ = lazyChunks.length > 0 ? lazyChunks : void 0;
634
- } else globalThis.__VINEXT_LAZY_CHUNKS__ = void 0;
700
+ assetPrefix: options.assetPrefix,
701
+ includeClientEntry: options.clientEntryLookup === "pages-client-entry" ? "pages-client-entry" : true
702
+ });
703
+ globalThis.__VINEXT_CLIENT_ENTRY__ = metadata.clientEntryFile;
704
+ globalThis.__VINEXT_LAZY_CHUNKS__ = metadata.lazyChunks;
705
+ globalThis.__VINEXT_DYNAMIC_PRELOADS__ = metadata.dynamicPreloads;
635
706
  return ssrManifest;
636
707
  }
637
708
  /**
@@ -659,8 +730,7 @@ async function startAppRouterServer(options) {
659
730
  imageConfig = JSON.parse(fs.readFileSync(imageConfigPath, "utf-8"));
660
731
  } catch {}
661
732
  const prerenderSecret = readPrerenderSecret(path.dirname(rscEntryPath));
662
- const rscMtime = fs.statSync(rscEntryPath).mtimeMs;
663
- const rscModule = await import(`${pathToFileURL(rscEntryPath).href}?t=${rscMtime}`);
733
+ const rscModule = await importServerEntryModule(rscEntryPath);
664
734
  const rscHandler = resolveAppRouterHandler(rscModule.default);
665
735
  const appRouterAssetPrefix = typeof rscModule.__assetPrefix === "string" ? rscModule.__assetPrefix : "";
666
736
  const appRouterBasePath = typeof rscModule.__basePath === "string" ? rscModule.__basePath : "";
@@ -671,10 +741,11 @@ async function startAppRouterServer(options) {
671
741
  const appAssetBase = appRouterBasePath ? `${appRouterBasePath}/` : "/";
672
742
  if (appRouterHasPagesDir) installPagesClientAssetGlobals({
673
743
  clientDir,
674
- assetsSubdir: resolveAssetsDir(appRouterAssetPrefix),
744
+ assetPrefix: appRouterAssetPrefix,
675
745
  assetBase: appAssetBase,
676
746
  clientEntryLookup: "pages-client-entry"
677
747
  });
748
+ else installClientBuildManifestGlobals(clientDir, appAssetBase, appRouterAssetPrefix);
678
749
  const seededRoutes = await resolveAppRouterPrerenderSeeder(rscModule)(path.dirname(rscEntryPath));
679
750
  if (seededRoutes > 0) console.log(`[vinext] Seeded ${seededRoutes} pre-rendered route${seededRoutes !== 1 ? "s" : ""} into memory cache`);
680
751
  const staticCache = await StaticFileCache.create(clientDir);
@@ -713,7 +784,7 @@ async function startAppRouterServer(options) {
713
784
  }
714
785
  }
715
786
  if (isImageOptimizationPath(pathname)) {
716
- const params = parseImageParams(new URL(rawUrl, "http://localhost"), [...DEFAULT_DEVICE_SIZES, ...DEFAULT_IMAGE_SIZES]);
787
+ const params = parseImageParams(new URL(rawUrl, "http://localhost"), [...imageConfig?.deviceSizes ?? DEFAULT_DEVICE_SIZES, ...imageConfig?.imageSizes ?? DEFAULT_IMAGE_SIZES], imageConfig?.qualities);
717
788
  if (!params) {
718
789
  res.writeHead(400);
719
790
  res.end("Bad Request");
@@ -745,12 +816,7 @@ async function startAppRouterServer(options) {
745
816
  } catch {
746
817
  staticFilePath = staticFileSignal;
747
818
  }
748
- const staticResponseHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, response), [
749
- VINEXT_STATIC_FILE_HEADER,
750
- "content-encoding",
751
- "content-length",
752
- "content-type"
753
- ]);
819
+ const staticResponseHeaders = omitHeadersCaseInsensitive(mergeResponseHeaders({}, response), OMIT_STATIC_RESPONSE_HEADERS);
754
820
  const served = await tryServeStatic(req, res, clientDir, staticFilePath, compress, staticCache, staticResponseHeaders, response.status);
755
821
  cancelResponseBody(response);
756
822
  if (served) return;
@@ -804,8 +870,7 @@ function readPagesServerEntryPageRoutes(value) {
804
870
  */
805
871
  async function startPagesRouterServer(options) {
806
872
  const { port, host, clientDir, serverEntryPath, compress, purpose } = options;
807
- const serverMtime = fs.statSync(serverEntryPath).mtimeMs;
808
- const serverEntry = await import(`${pathToFileURL(serverEntryPath).href}?t=${serverMtime}`);
873
+ const serverEntry = await importServerEntryModule(serverEntryPath);
809
874
  const { renderPage, handleApiRoute: handleApi, runMiddleware, vinextConfig, buildId: pagesBuildId } = serverEntry;
810
875
  const matchPageRoute = typeof serverEntry.matchPageRoute === "function" ? serverEntry.matchPageRoute : void 0;
811
876
  const pageRoutes = readPagesServerEntryPageRoutes(serverEntry.pageRoutes);
@@ -827,12 +892,13 @@ async function startPagesRouterServer(options) {
827
892
  const pagesImageConfig = vinextConfig?.images ? {
828
893
  dangerouslyAllowSVG: vinextConfig.images.dangerouslyAllowSVG,
829
894
  dangerouslyAllowLocalIP: vinextConfig.images.dangerouslyAllowLocalIP,
895
+ qualities: vinextConfig.images.qualities,
830
896
  contentDispositionType: vinextConfig.images.contentDispositionType,
831
897
  contentSecurityPolicy: vinextConfig.images.contentSecurityPolicy
832
898
  } : void 0;
833
899
  const ssrManifest = installPagesClientAssetGlobals({
834
900
  clientDir,
835
- assetsSubdir: resolveAssetsDir(assetPrefix),
901
+ assetPrefix,
836
902
  assetBase,
837
903
  clientEntryLookup: "any-client-entry"
838
904
  });
@@ -896,7 +962,7 @@ async function startPagesRouterServer(options) {
896
962
  return;
897
963
  }
898
964
  if (isImageOptimizationPath(pathname) || isImageOptimizationPath(staticLookupPath)) {
899
- const params = parseImageParams(new URL(rawUrl, "http://localhost"), allowedImageWidths);
965
+ const params = parseImageParams(new URL(rawUrl, "http://localhost"), allowedImageWidths, pagesImageConfig?.qualities);
900
966
  if (!params) {
901
967
  res.writeHead(400);
902
968
  res.end("Bad Request");
@@ -927,6 +993,7 @@ async function startPagesRouterServer(options) {
927
993
  }
928
994
  }
929
995
  let isDataReq = false;
996
+ const originalRenderUrl = url;
930
997
  if (isNextDataPathname(pathname)) {
931
998
  const dataMatch = pagesBuildId ? parseNextDataPathname(pathname, pagesBuildId) : null;
932
999
  if (!dataMatch) {
@@ -963,8 +1030,11 @@ async function startPagesRouterServer(options) {
963
1030
  ctx: void 0,
964
1031
  rawSearch: rawQs,
965
1032
  matchPageRoute: matchPageRoute ?? null,
966
- runMiddleware: typeof runMiddleware === "function" ? runMiddleware : null,
967
- renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, options) : null,
1033
+ runMiddleware: typeof runMiddleware === "function" ? wrapMiddlewareWithBasePath(runMiddleware, basePath, hadBasePath) : null,
1034
+ renderPage: typeof renderPage === "function" ? (request, resolvedUrl, options, stagedHeaders) => renderPage(request, resolvedUrl, ssrManifest, void 0, stagedHeaders, {
1035
+ ...options,
1036
+ originalUrl: originalRenderUrl
1037
+ }) : null,
968
1038
  handleApi: typeof handleApi === "function" ? (request, apiUrl) => handleApi(request, apiUrl, createNodeExecutionContext()) : null,
969
1039
  serveStaticFile: async (requestPathname, stagedHeaders) => {
970
1040
  if (requestPathname === "/" || requestPathname.startsWith("/api/") || requestPathname.startsWith(`/_next/static/`)) return false;
@@ -1018,4 +1088,4 @@ async function startPagesRouterServer(options) {
1018
1088
  };
1019
1089
  }
1020
1090
  //#endregion
1021
- export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
1091
+ export { COMPRESSIBLE_TYPES, COMPRESS_THRESHOLD, importServerEntryModule, mergeResponseHeaders, mergeWebResponse, negotiateEncoding, nodeToWebRequest, resolveAppRouterAssetPath, resolveAppRouterPrerenderSeeder, resolveRequestHost as resolveHost, resolveServerEntryImportUrl, sendCompressed, sendWebResponse, startProdServer, trustProxy, trustedHosts, tryServeStatic };
@@ -76,7 +76,8 @@ type ApplyConfigHeadersOptions = {
76
76
  * (basePath: true) rule for backward compatibility — callers that need to
77
77
  * support `basePath: false` headers must pass this in.
78
78
  */
79
- basePathState?: BasePathMatchState;
79
+ basePathState?: BasePathMatchState; /** Existing framework-generated headers that matching config rules may replace. */
80
+ overwriteExisting?: ReadonlySet<string>;
80
81
  };
81
82
  type StaticFileSignalContext = {
82
83
  headers: Headers | null;
@@ -157,19 +158,6 @@ declare function validateCsrfOrigin(request: Request, allowedOrigins?: string[])
157
158
  */
158
159
  declare function validateServerActionPayload(body: string | FormData): Promise<Response | null>;
159
160
  declare function isOriginAllowed(origin: string, allowed: string[]): boolean;
160
- /**
161
- * Validate an image optimization URL parameter.
162
- *
163
- * Ensures the URL is a relative path that doesn't escape the origin:
164
- * - Must start with "/" but not "//"
165
- * - Backslashes are normalized (browsers treat `\` as `/`)
166
- * - Origin validation as defense-in-depth
167
- *
168
- * @param rawUrl - The raw `url` query parameter value
169
- * @param requestUrl - The full request URL for origin comparison
170
- * @returns An error Response if validation fails, or the normalized image URL
171
- */
172
- declare function validateImageUrl(rawUrl: string | null, requestUrl: string): Response | string;
173
161
  /**
174
162
  * Strip internal `x-middleware-*` headers from a Headers object.
175
163
  *
@@ -219,4 +207,4 @@ declare function cloneRequestWithHeaders(request: Request, headers: Headers): Re
219
207
  */
220
208
  declare function cloneRequestWithUrl(request: Request, url: string): Request;
221
209
  //#endregion
222
- export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateImageUrl, validateServerActionPayload };
210
+ export { HeaderRecord, INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
@@ -122,7 +122,7 @@ function applyConfigHeadersToResponse(responseHeaders, options) {
122
122
  for (const header of matched) {
123
123
  const lowerName = header.key.toLowerCase();
124
124
  if (lowerName === "vary" || lowerName === "set-cookie") responseHeaders.append(header.key, header.value);
125
- else if (!responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
125
+ else if (options.overwriteExisting?.has(lowerName) || !responseHeaders.has(lowerName)) responseHeaders.set(header.key, header.value);
126
126
  }
127
127
  }
128
128
  /**
@@ -197,9 +197,10 @@ function normalizeTrailingSlash(pathname, basePath, trailingSlash, search) {
197
197
  if (isOpenRedirectShaped(pathname)) return notFoundResponse();
198
198
  const normalizedPathname = normalizeTrailingSlashPathname(pathname, trailingSlash);
199
199
  if (normalizedPathname === null) return null;
200
+ const encodedPathname = normalizedPathname.replace(/[^A-Za-z0-9\-._~!$&'()*+,;=:@/%]/gu, encodeURIComponent);
200
201
  return new Response(null, {
201
202
  status: 308,
202
- headers: { Location: basePath + normalizedPathname + search }
203
+ headers: { Location: basePath + encodedPathname + search }
203
204
  });
204
205
  }
205
206
  /**
@@ -246,45 +247,74 @@ function validateCsrfOrigin(request, allowedOrigins = []) {
246
247
  * Regular user form fields are ignored entirely.
247
248
  */
248
249
  async function validateServerActionPayload(body) {
249
- const containerRefRe = /"\$([QWi])(\d+)"/g;
250
+ const maxNumericFields = 4096;
251
+ const maxContainerReferences = 16384;
252
+ const maxContainerDepth = 1024;
253
+ const containerRefRe = /"\$([QWi])([0-9a-f]+)(?=[:"])/gi;
250
254
  const fieldRefs = /* @__PURE__ */ new Map();
255
+ let referenceCount = 0;
256
+ const invalidPayloadResponse = () => new Response("Invalid server action payload", {
257
+ status: 400,
258
+ headers: { "Content-Type": "text/plain" }
259
+ });
251
260
  const collectRefs = (fieldKey, text) => {
261
+ if (fieldRefs.has(fieldKey)) return true;
262
+ if (fieldRefs.size >= maxNumericFields) return false;
252
263
  const refs = /* @__PURE__ */ new Set();
253
264
  let match;
254
265
  containerRefRe.lastIndex = 0;
255
- while ((match = containerRefRe.exec(text)) !== null) refs.add(match[2]);
266
+ while ((match = containerRefRe.exec(text)) !== null) {
267
+ const previousSize = refs.size;
268
+ refs.add(String(Number.parseInt(match[2], 16)));
269
+ if (refs.size !== previousSize && ++referenceCount > maxContainerReferences) return false;
270
+ }
256
271
  fieldRefs.set(fieldKey, refs);
272
+ return true;
257
273
  };
258
- if (typeof body === "string") collectRefs("0", body);
259
- else for (const [key, value] of body.entries()) {
274
+ if (typeof body === "string") {
275
+ if (!collectRefs("0", body)) return invalidPayloadResponse();
276
+ } else for (const [key, value] of body.entries()) {
260
277
  if (!/^\d+$/.test(key)) continue;
261
278
  if (typeof value === "string") {
262
- collectRefs(key, value);
279
+ if (!collectRefs(key, value)) return invalidPayloadResponse();
263
280
  continue;
264
281
  }
265
- if (typeof value?.text === "function") collectRefs(key, await value.text());
282
+ if (typeof value?.text === "function") {
283
+ if (!collectRefs(key, await value.text())) return invalidPayloadResponse();
284
+ }
266
285
  }
267
286
  if (fieldRefs.size === 0) return null;
268
287
  const knownFields = new Set(fieldRefs.keys());
269
- for (const refs of fieldRefs.values()) for (const ref of refs) if (!knownFields.has(ref)) return new Response("Invalid server action payload", {
270
- status: 400,
271
- headers: { "Content-Type": "text/plain" }
272
- });
273
- const visited = /* @__PURE__ */ new Set();
274
- const stack = /* @__PURE__ */ new Set();
275
- const hasCycle = (node) => {
276
- if (stack.has(node)) return true;
277
- if (visited.has(node)) return false;
278
- visited.add(node);
279
- stack.add(node);
280
- for (const ref of fieldRefs.get(node) ?? []) if (hasCycle(ref)) return true;
281
- stack.delete(node);
282
- return false;
283
- };
284
- for (const node of fieldRefs.keys()) if (hasCycle(node)) return new Response("Invalid server action payload", {
285
- status: 400,
286
- headers: { "Content-Type": "text/plain" }
287
- });
288
+ for (const refs of fieldRefs.values()) for (const ref of refs) if (!knownFields.has(ref)) return invalidPayloadResponse();
289
+ const state = /* @__PURE__ */ new Map();
290
+ for (const root of fieldRefs.keys()) {
291
+ if (state.has(root)) continue;
292
+ const stack = [{
293
+ node: root,
294
+ refs: [...fieldRefs.get(root) ?? []],
295
+ nextRef: 0
296
+ }];
297
+ state.set(root, "visiting");
298
+ while (stack.length > 0) {
299
+ if (stack.length > maxContainerDepth) return invalidPayloadResponse();
300
+ const frame = stack[stack.length - 1];
301
+ const ref = frame.refs[frame.nextRef++];
302
+ if (ref === void 0) {
303
+ state.set(frame.node, "visited");
304
+ stack.pop();
305
+ continue;
306
+ }
307
+ const refState = state.get(ref);
308
+ if (refState === "visiting") return invalidPayloadResponse();
309
+ if (refState === "visited") continue;
310
+ state.set(ref, "visiting");
311
+ stack.push({
312
+ node: ref,
313
+ refs: [...fieldRefs.get(ref) ?? []],
314
+ nextRef: 0
315
+ });
316
+ }
317
+ }
288
318
  return null;
289
319
  }
290
320
  /**
@@ -329,25 +359,6 @@ function isOriginAllowed(origin, allowed) {
329
359
  return false;
330
360
  }
331
361
  /**
332
- * Validate an image optimization URL parameter.
333
- *
334
- * Ensures the URL is a relative path that doesn't escape the origin:
335
- * - Must start with "/" but not "//"
336
- * - Backslashes are normalized (browsers treat `\` as `/`)
337
- * - Origin validation as defense-in-depth
338
- *
339
- * @param rawUrl - The raw `url` query parameter value
340
- * @param requestUrl - The full request URL for origin comparison
341
- * @returns An error Response if validation fails, or the normalized image URL
342
- */
343
- function validateImageUrl(rawUrl, requestUrl) {
344
- const imgUrl = rawUrl?.replaceAll("\\", "/") ?? null;
345
- if (!imgUrl || !imgUrl.startsWith("/") || imgUrl.startsWith("//")) return new Response(!rawUrl ? "Missing url parameter" : "Only relative URLs allowed", { status: 400 });
346
- const url = new URL(requestUrl);
347
- if (new URL(imgUrl, url.origin).origin !== url.origin) return new Response("Only relative URLs allowed", { status: 400 });
348
- return imgUrl;
349
- }
350
- /**
351
362
  * Strip internal `x-middleware-*` headers from a Headers object.
352
363
  *
353
364
  * Middleware uses `x-middleware-*` headers as internal signals (e.g.
@@ -465,4 +476,4 @@ function cloneRequestWithUrl(request, url) {
465
476
  return cloned;
466
477
  }
467
478
  //#endregion
468
- export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateImageUrl, validateServerActionPayload };
479
+ export { INTERNAL_HEADERS, VINEXT_INTERNAL_HEADERS, applyConfigHeadersToHeaderRecord, applyConfigHeadersToResponse, cloneRequestWithHeaders, cloneRequestWithUrl, createStaticFileSignal, filterInternalHeaders, guardProtocolRelativeUrl, hasBasePath, isOpenRedirectShaped, isOriginAllowed, normalizeTrailingSlash, normalizeTrailingSlashPathname, processMiddlewareHeaders, resolvePublicFileRoute, stripBasePath, validateCsrfOrigin, validateServerActionPayload };
@@ -1,6 +1,10 @@
1
1
  //#region src/server/rsc-stream-hints.d.ts
2
2
  declare function normalizeReactFlightPreloadHints(stream: ReadableStream<Uint8Array>): ReadableStream<Uint8Array>;
3
3
  type RscRawRenderer = (model: unknown, options?: unknown) => ReadableStream<Uint8Array>;
4
+ type RscRawPrerenderer = (model: unknown, options?: unknown) => Promise<{
5
+ prelude: ReadableStream<Uint8Array>;
6
+ }>;
4
7
  declare function createRscRenderer(render: RscRawRenderer): RscRawRenderer;
8
+ declare function createRscPrerenderer(prerender: RscRawPrerenderer): RscRawPrerenderer;
5
9
  //#endregion
6
- export { createRscRenderer, normalizeReactFlightPreloadHints };
10
+ export { RscRawPrerenderer, RscRawRenderer, createRscPrerenderer, createRscRenderer, normalizeReactFlightPreloadHints };
@@ -32,5 +32,10 @@ function normalizeReactFlightPreloadHints(stream) {
32
32
  function createRscRenderer(render) {
33
33
  return (model, options) => normalizeReactFlightPreloadHints(render(model, options));
34
34
  }
35
+ function createRscPrerenderer(prerender) {
36
+ return async (model, options) => {
37
+ return { prelude: normalizeReactFlightPreloadHints((await prerender(model, options)).prelude) };
38
+ };
39
+ }
35
40
  //#endregion
36
- export { createRscRenderer, normalizeReactFlightPreloadHints };
41
+ export { createRscPrerenderer, createRscRenderer, normalizeReactFlightPreloadHints };
@@ -1,8 +1,8 @@
1
- import { normalizePathnameForRouteMatch } from "../routing/utils.js";
2
- import { normalizePath } from "./normalize-path.js";
3
1
  import { isrCacheKey, isrSetPrerenderedAppPage } from "./isr-cache.js";
4
2
  import { buildAppPageCacheTags } from "./app-page-cache.js";
5
3
  import { getOutputPath, getRscOutputPath } from "../utils/prerender-output-paths.js";
4
+ import { addPregeneratedConcretePath, clearPregeneratedConcretePaths, normalizePregeneratedPathname } from "./pregenerated-concrete-paths.js";
5
+ import { getRenderedAppRoutes, isFallbackShellArtifactPath, readPrerenderManifest } from "./prerender-manifest.js";
6
6
  import fs from "node:fs";
7
7
  import path from "node:path";
8
8
  //#region src/server/seed-cache.ts
@@ -46,26 +46,21 @@ import path from "node:path";
46
46
  * @returns The number of routes seeded (0 if no manifest or no renderable routes).
47
47
  */
48
48
  async function seedMemoryCacheFromPrerender(serverDir, options) {
49
- const manifestPath = path.join(serverDir, "vinext-prerender.json");
50
- if (!fs.existsSync(manifestPath)) return 0;
51
- let manifest;
52
- try {
53
- manifest = JSON.parse(fs.readFileSync(manifestPath, "utf-8"));
54
- } catch (err) {
55
- console.warn("[vinext] Failed to parse vinext-prerender.json, skipping cache seeding:", err);
56
- return 0;
57
- }
49
+ clearPregeneratedConcretePaths();
50
+ const manifest = readPrerenderManifest(path.join(serverDir, "vinext-prerender.json"));
51
+ if (!manifest) return 0;
58
52
  const { buildId, routes } = manifest;
59
53
  if (!buildId || !Array.isArray(routes)) return 0;
60
54
  const trailingSlash = manifest.trailingSlash ?? false;
61
55
  const prerenderDir = path.join(serverDir, "prerendered-routes");
62
56
  const writeAppPageEntry = options?.writeAppPageEntry ?? createDefaultAppPageEntryWriter();
63
57
  let seeded = 0;
64
- for (const route of routes) {
65
- if (route.status !== "rendered") continue;
66
- if (route.router !== "app") continue;
58
+ const appRoutes = getRenderedAppRoutes(routes);
59
+ for (const route of appRoutes) {
60
+ const concretePathname = route.path ?? route.route;
61
+ if (!isFallbackShellArtifactPath(concretePathname, route)) addPregeneratedConcretePath(route.route, concretePathname);
67
62
  const artifactPathname = route.path ?? route.route;
68
- const cachePathname = normalizePrerenderCachePathname(artifactPathname);
63
+ const cachePathname = normalizePregeneratedPathname(artifactPathname);
69
64
  const baseKey = isrCacheKey("app", cachePathname, buildId);
70
65
  const htmlKey = options?.buildAppPageHtmlKey?.(cachePathname) ?? baseKey + ":html";
71
66
  const rscKey = options?.buildAppPageRscKey?.(cachePathname) ?? baseKey + ":rsc";
@@ -79,9 +74,6 @@ async function seedMemoryCacheFromPrerender(serverDir, options) {
79
74
  }
80
75
  return seeded;
81
76
  }
82
- function normalizePrerenderCachePathname(pathname) {
83
- return normalizePath(normalizePathnameForRouteMatch(pathname));
84
- }
85
77
  function createDefaultAppPageEntryWriter() {
86
78
  return (key, data, metadata) => isrSetPrerenderedAppPage(key, data, metadata);
87
79
  }
@@ -3,11 +3,13 @@ type AppRouterScrollIntent = Readonly<{
3
3
  commitId: number | null;
4
4
  hash: string | null;
5
5
  id: number;
6
+ targetHoistedInHead: boolean;
6
7
  }>;
7
8
  declare function beginAppRouterScrollIntent(hash: string | null): AppRouterScrollIntent;
8
9
  declare function clearAppRouterScrollIntent(): void;
9
10
  declare function getPendingAppRouterScrollIntent(): AppRouterScrollIntent | null;
10
11
  declare function claimAppRouterScrollIntentForCommit(expected: AppRouterScrollIntent | null | undefined, commitId: number): void;
12
+ declare function markAppRouterScrollIntentHeadHoisted(expected: AppRouterScrollIntent | null | undefined, commitId: number): void;
11
13
  declare function consumeAppRouterScrollIntent(expected: AppRouterScrollIntent | null | undefined, commitId?: number): AppRouterScrollIntent | null;
12
14
  //#endregion
13
- export { AppRouterScrollIntent, beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent };
15
+ export { AppRouterScrollIntent, beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, markAppRouterScrollIntentHeadHoisted };
@@ -14,7 +14,8 @@ function beginAppRouterScrollIntent(hash) {
14
14
  const intent = {
15
15
  commitId: null,
16
16
  hash,
17
- id: store.nextId
17
+ id: store.nextId,
18
+ targetHoistedInHead: false
18
19
  };
19
20
  store.pending = intent;
20
21
  return intent;
@@ -35,6 +36,17 @@ function claimAppRouterScrollIntentForCommit(expected, commitId) {
35
36
  commitId
36
37
  };
37
38
  }
39
+ function markAppRouterScrollIntentHeadHoisted(expected, commitId) {
40
+ const store = getScrollIntentStore();
41
+ const intent = store.pending;
42
+ if (expected === null || expected === void 0 || intent === null) return;
43
+ if (intent.id !== expected.id) return;
44
+ if (intent.commitId !== commitId) return;
45
+ store.pending = {
46
+ ...intent,
47
+ targetHoistedInHead: true
48
+ };
49
+ }
38
50
  function consumeAppRouterScrollIntent(expected, commitId) {
39
51
  if (expected === null || expected === void 0) return null;
40
52
  const store = getScrollIntentStore();
@@ -46,4 +58,4 @@ function consumeAppRouterScrollIntent(expected, commitId) {
46
58
  return intent;
47
59
  }
48
60
  //#endregion
49
- export { beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent };
61
+ export { beginAppRouterScrollIntent, claimAppRouterScrollIntentForCommit, clearAppRouterScrollIntent, consumeAppRouterScrollIntent, getPendingAppRouterScrollIntent, markAppRouterScrollIntentHeadHoisted };
@@ -5,9 +5,12 @@ declare class AppRouterScrollTargetInner extends React$1.Component<{
5
5
  children: React$1.ReactNode;
6
6
  commitId: number | null;
7
7
  }> {
8
+ scheduledCommitId: number | null;
9
+ schedulePotentialScroll: () => void;
8
10
  handlePotentialScroll: () => void;
9
11
  componentDidMount(): void;
10
12
  componentDidUpdate(): void;
13
+ componentWillUnmount(): void;
11
14
  render(): React$1.ReactNode;
12
15
  }
13
16
  declare function AppRouterScrollCommitProvider({