vinext 1.0.0 → 1.1.0

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 (263) hide show
  1. package/README.md +72 -1
  2. package/THIRD_PARTY_LICENSES.md +26 -0
  3. package/dist/build/app-page-static-eligibility.d.ts +15 -0
  4. package/dist/build/app-page-static-eligibility.js +82 -0
  5. package/dist/build/lifecycle.js +10 -10
  6. package/dist/build/pages-client-assets-module.js +6 -1
  7. package/dist/build/precompress.js +1 -1
  8. package/dist/build/prerender-paths.js +6 -85
  9. package/dist/build/prerender.d.ts +8 -1
  10. package/dist/build/prerender.js +167 -46
  11. package/dist/build/run-prerender.js +4 -3
  12. package/dist/build/server-entry.d.ts +2 -0
  13. package/dist/build/server-entry.js +7 -1
  14. package/dist/build/server-manifest.d.ts +7 -0
  15. package/dist/build/server-manifest.js +11 -1
  16. package/dist/cache/cache-adapters-virtual.d.ts +7 -1
  17. package/dist/cache/cache-adapters-virtual.js +3 -3
  18. package/dist/client/browser-globals.d.ts +4 -0
  19. package/dist/client/browser-globals.js +5 -0
  20. package/dist/client/navigation-runtime.d.ts +11 -1
  21. package/dist/client/navigation-runtime.js +3 -3
  22. package/dist/config/config-matchers.js +3 -1
  23. package/dist/config/dotenv.js +14 -3
  24. package/dist/config/next-config.d.ts +7 -0
  25. package/dist/config/next-config.js +18 -3
  26. package/dist/entries/app-rsc-entry.d.ts +2 -0
  27. package/dist/entries/app-rsc-entry.js +67 -45
  28. package/dist/index.d.ts +4 -1
  29. package/dist/index.js +200 -100
  30. package/dist/init-cloudflare.d.ts +1 -1
  31. package/dist/init-cloudflare.js +6 -6
  32. package/dist/init-platform.js +1 -1
  33. package/dist/init.js +4 -2
  34. package/dist/plugins/cache-flight-codec.d.ts +19 -0
  35. package/dist/plugins/cache-flight-codec.js +68 -0
  36. package/dist/plugins/commonjs-esm-facade.d.ts +35 -0
  37. package/dist/plugins/commonjs-esm-facade.js +80 -0
  38. package/dist/plugins/commonjs-syntax.d.ts +16 -0
  39. package/dist/plugins/commonjs-syntax.js +22 -0
  40. package/dist/plugins/fonts.d.ts +8 -11
  41. package/dist/plugins/fonts.js +71 -34
  42. package/dist/plugins/ignore-dynamic-requests.d.ts +11 -0
  43. package/dist/plugins/ignore-dynamic-requests.js +54 -3
  44. package/dist/plugins/import-meta-url.js +6 -6
  45. package/dist/plugins/og-asset-ownership.d.ts +2 -0
  46. package/dist/plugins/og-asset-ownership.js +24 -13
  47. package/dist/plugins/og-assets.d.ts +18 -0
  48. package/dist/plugins/og-assets.js +50 -8
  49. package/dist/plugins/og-harfbuzz.js +38 -27
  50. package/dist/plugins/optimize-imports.js +2 -1
  51. package/dist/plugins/require-context.d.ts +1 -0
  52. package/dist/plugins/require-context.js +28 -1
  53. package/dist/plugins/scan-build-css.d.ts +12 -0
  54. package/dist/plugins/scan-build-css.js +39 -0
  55. package/dist/plugins/strip-server-exports.d.ts +2 -0
  56. package/dist/plugins/strip-server-exports.js +3 -1
  57. package/dist/plugins/transform-result.d.ts +18 -0
  58. package/dist/plugins/transform-result.js +23 -2
  59. package/dist/plugins/typeof-window.d.ts +10 -0
  60. package/dist/plugins/typeof-window.js +33 -9
  61. package/dist/routing/app-route-graph.d.ts +0 -1
  62. package/dist/routing/app-route-graph.js +2 -4
  63. package/dist/routing/app-route-ids.d.ts +3 -0
  64. package/dist/routing/app-route-ids.js +6 -0
  65. package/dist/routing/file-matcher.d.ts +1 -0
  66. package/dist/routing/file-matcher.js +1 -1
  67. package/dist/server/api-handler.js +17 -7
  68. package/dist/server/app-bfcache-id.d.ts +1 -0
  69. package/dist/server/app-bfcache-id.js +7 -1
  70. package/dist/server/app-bfcache-identity.js +1 -2
  71. package/dist/server/app-browser-entry.js +15 -9
  72. package/dist/server/app-browser-history-controller.d.ts +2 -0
  73. package/dist/server/app-browser-history-controller.js +8 -0
  74. package/dist/server/app-browser-hydration.d.ts +1 -1
  75. package/dist/server/app-browser-hydration.js +2 -2
  76. package/dist/server/app-browser-interception-context.js +26 -2
  77. package/dist/server/app-browser-navigation-controller.d.ts +7 -1
  78. package/dist/server/app-browser-navigation-controller.js +94 -11
  79. package/dist/server/app-browser-state.js +1 -1
  80. package/dist/server/app-browser-stream.d.ts +1 -1
  81. package/dist/server/app-fallback-renderer.d.ts +2 -0
  82. package/dist/server/app-fallback-renderer.js +1 -0
  83. package/dist/server/app-history-state.d.ts +3 -1
  84. package/dist/server/app-history-state.js +9 -5
  85. package/dist/server/app-middleware.js +2 -1
  86. package/dist/server/app-optimistic-routing.js +1 -1
  87. package/dist/server/app-page-boundary-render.d.ts +5 -0
  88. package/dist/server/app-page-boundary-render.js +6 -4
  89. package/dist/server/app-page-cache-finalizer.d.ts +11 -0
  90. package/dist/server/app-page-cache-finalizer.js +41 -7
  91. package/dist/server/app-page-cache-render.d.ts +18 -0
  92. package/dist/server/app-page-cache-render.js +65 -44
  93. package/dist/server/app-page-cache.d.ts +5 -0
  94. package/dist/server/app-page-cache.js +27 -7
  95. package/dist/server/app-page-dispatch.d.ts +9 -1
  96. package/dist/server/app-page-dispatch.js +47 -30
  97. package/dist/server/app-page-element-builder.d.ts +7 -0
  98. package/dist/server/app-page-element-builder.js +27 -42
  99. package/dist/server/app-page-execution.d.ts +15 -12
  100. package/dist/server/app-page-execution.js +30 -40
  101. package/dist/server/app-page-head.d.ts +5 -0
  102. package/dist/server/app-page-head.js +9 -3
  103. package/dist/server/app-page-probe.d.ts +0 -109
  104. package/dist/server/app-page-probe.js +3 -136
  105. package/dist/server/app-page-render-identity.d.ts +0 -1
  106. package/dist/server/app-page-render-identity.js +1 -1
  107. package/dist/server/app-page-render-observation.js +2 -2
  108. package/dist/server/app-page-render.d.ts +13 -5
  109. package/dist/server/app-page-render.js +219 -118
  110. package/dist/server/app-page-request.d.ts +0 -1
  111. package/dist/server/app-page-request.js +1 -2
  112. package/dist/server/app-page-response.d.ts +4 -3
  113. package/dist/server/app-page-response.js +4 -3
  114. package/dist/server/app-page-route-wiring.d.ts +17 -5
  115. package/dist/server/app-page-route-wiring.js +55 -33
  116. package/dist/server/app-page-rsc-render-status.d.ts +48 -0
  117. package/dist/server/app-page-rsc-render-status.js +54 -0
  118. package/dist/server/app-page-search-params-observation.js +1 -1
  119. package/dist/server/app-page-stream.d.ts +15 -8
  120. package/dist/server/app-page-stream.js +7 -1
  121. package/dist/server/app-page-tracing.js +2 -1
  122. package/dist/server/app-pages-bridge.js +3 -2
  123. package/dist/server/app-render-abort-error.d.ts +3 -0
  124. package/dist/server/app-render-abort-error.js +8 -0
  125. package/dist/server/app-request-stage-dispatch.js +1 -1
  126. package/dist/server/app-request-stage-independent-entry.js +8 -11
  127. package/dist/server/app-response-header-provenance.d.ts +2 -0
  128. package/dist/server/app-response-header-provenance.js +10 -4
  129. package/dist/server/app-route-handler-cache.d.ts +2 -1
  130. package/dist/server/app-route-handler-cache.js +11 -7
  131. package/dist/server/app-route-handler-dispatch.js +11 -4
  132. package/dist/server/app-route-handler-execution.d.ts +9 -1
  133. package/dist/server/app-route-handler-execution.js +28 -13
  134. package/dist/server/app-route-handler-policy.js +2 -2
  135. package/dist/server/app-route-handler-response.d.ts +1 -1
  136. package/dist/server/app-route-handler-response.js +14 -5
  137. package/dist/server/app-router-entry.d.ts +2 -1
  138. package/dist/server/app-router-entry.js +8 -12
  139. package/dist/server/app-rsc-errors.d.ts +0 -1
  140. package/dist/server/app-rsc-errors.js +2 -6
  141. package/dist/server/app-rsc-handler.d.ts +5 -0
  142. package/dist/server/app-rsc-handler.js +229 -102
  143. package/dist/server/app-rsc-request-normalization.d.ts +4 -2
  144. package/dist/server/app-rsc-request-normalization.js +7 -4
  145. package/dist/server/app-rsc-response-finalizer.js +4 -1
  146. package/dist/server/app-rsc-response-stage.d.ts +1 -1
  147. package/dist/server/app-rsc-response-stage.js +6 -4
  148. package/dist/server/app-rsc-route-matching.d.ts +9 -1
  149. package/dist/server/app-rsc-route-matching.js +37 -20
  150. package/dist/server/app-server-action-execution.d.ts +2 -1
  151. package/dist/server/app-server-action-execution.js +1 -1
  152. package/dist/server/app-ssr-entry.d.ts +3 -2
  153. package/dist/server/app-ssr-entry.js +9 -7
  154. package/dist/server/app-ssr-search-params-gate.js +1 -1
  155. package/dist/server/app-ssr-stream.d.ts +6 -1
  156. package/dist/server/app-ssr-stream.js +35 -16
  157. package/dist/server/app-worker-stages.d.ts +2 -2
  158. package/dist/server/app-worker-stages.js +3 -3
  159. package/dist/server/cache-control.js +3 -11
  160. package/dist/server/cacheability-request.js +56 -52
  161. package/dist/server/config-headers.js +3 -1
  162. package/dist/server/dev-server.js +23 -5
  163. package/dist/server/headers.d.ts +16 -0
  164. package/dist/server/headers.js +19 -1
  165. package/dist/server/http-error-responses.js +1 -0
  166. package/dist/server/image-optimization.d.ts +16 -0
  167. package/dist/server/image-optimization.js +26 -6
  168. package/dist/server/isr-cache.d.ts +2 -0
  169. package/dist/server/isr-cache.js +8 -3
  170. package/dist/server/metadata-route-response.js +94 -37
  171. package/dist/server/middleware-matcher-pattern.js +8 -4
  172. package/dist/server/middleware-response-headers.js +2 -0
  173. package/dist/server/multi-stage.d.ts +8 -0
  174. package/dist/server/navigation-planner.js +1 -1
  175. package/dist/server/node-response-signal.d.ts +16 -0
  176. package/dist/server/node-response-signal.js +26 -0
  177. package/dist/server/open-redirect.d.ts +47 -0
  178. package/dist/server/open-redirect.js +74 -1
  179. package/dist/server/pages-api-route.js +8 -2
  180. package/dist/server/pages-asset-tags.d.ts +1 -0
  181. package/dist/server/pages-asset-tags.js +73 -22
  182. package/dist/server/pages-client-assets.d.ts +2 -0
  183. package/dist/server/pages-page-data.d.ts +6 -1
  184. package/dist/server/pages-page-data.js +71 -65
  185. package/dist/server/pages-page-handler.js +50 -15
  186. package/dist/server/pages-page-response.js +3 -1
  187. package/dist/server/pages-request-pipeline.d.ts +2 -1
  188. package/dist/server/pages-request-pipeline.js +20 -5
  189. package/dist/server/pages-request-stage-entry.js +14 -8
  190. package/dist/server/prerender-manifest.d.ts +6 -0
  191. package/dist/server/prerender-manifest.js +1 -1
  192. package/dist/server/prod-server.d.ts +8 -7
  193. package/dist/server/prod-server.js +152 -78
  194. package/dist/server/request-pipeline.d.ts +20 -8
  195. package/dist/server/request-pipeline.js +21 -7
  196. package/dist/server/response-compression.d.ts +43 -0
  197. package/dist/server/response-compression.js +111 -0
  198. package/dist/server/response-stage-cacheability.js +1 -1
  199. package/dist/server/seed-cache.js +10 -7
  200. package/dist/server/socket-error-backstop.d.ts +14 -0
  201. package/dist/server/socket-error-backstop.js +27 -4
  202. package/dist/server/worker-revalidation-context.d.ts +2 -1
  203. package/dist/server/worker-revalidation-context.js +1 -0
  204. package/dist/server/workers-tracing.d.ts +17 -0
  205. package/dist/server/workers-tracing.js +48 -8
  206. package/dist/shims/cache-adapter-instantiate.d.ts +50 -0
  207. package/dist/shims/cache-adapter-instantiate.js +118 -0
  208. package/dist/shims/cache-callable-runtime.d.ts +2 -40
  209. package/dist/shims/cache-callable-runtime.js +50 -168
  210. package/dist/shims/cache-flight-arguments.d.ts +22 -0
  211. package/dist/shims/cache-flight-arguments.js +56 -0
  212. package/dist/shims/cache-request-state.d.ts +18 -14
  213. package/dist/shims/cache-request-state.js +33 -21
  214. package/dist/shims/cache-runtime.d.ts +6 -19
  215. package/dist/shims/cache-runtime.js +168 -226
  216. package/dist/shims/cache.d.ts +3 -2
  217. package/dist/shims/cache.js +8 -5
  218. package/dist/shims/cacheability-classification.d.ts +8 -1
  219. package/dist/shims/cacheability-classification.js +2 -2
  220. package/dist/shims/cdn-cache.d.ts +10 -1
  221. package/dist/shims/cdn-cache.js +16 -9
  222. package/dist/shims/error-boundary-navigation.d.ts +7 -0
  223. package/dist/shims/error-boundary-navigation.js +19 -1
  224. package/dist/shims/error-boundary.d.ts +10 -10
  225. package/dist/shims/error-boundary.js +26 -7
  226. package/dist/shims/error.d.ts +4 -18
  227. package/dist/shims/error.js +17 -10
  228. package/dist/shims/error.react-server.d.ts +4 -6
  229. package/dist/shims/error.react-server.js +4 -1
  230. package/dist/shims/fetch-cache.d.ts +6 -0
  231. package/dist/shims/fetch-cache.js +23 -4
  232. package/dist/shims/form.js +1 -1
  233. package/dist/shims/headers.d.ts +19 -69
  234. package/dist/shims/headers.js +82 -183
  235. package/dist/shims/internal/app-page-props-cache-key.d.ts +5 -2
  236. package/dist/shims/internal/app-page-props-cache-key.js +24 -15
  237. package/dist/shims/internal/cache-context-accessor.d.ts +16 -0
  238. package/dist/shims/internal/cache-context-accessor.js +15 -0
  239. package/dist/shims/internal/headers-state.d.ts +73 -0
  240. package/dist/shims/internal/headers-state.js +157 -0
  241. package/dist/shims/metadata.js +3 -3
  242. package/dist/shims/navigation-errors.d.ts +6 -0
  243. package/dist/shims/navigation-errors.js +11 -1
  244. package/dist/shims/navigation.d.ts +7 -0
  245. package/dist/shims/navigation.js +16 -6
  246. package/dist/shims/request-context.d.ts +9 -0
  247. package/dist/shims/request-state-types.d.ts +2 -1
  248. package/dist/shims/root-params.js +1 -1
  249. package/dist/shims/server.js +52 -2
  250. package/dist/shims/thenable-params.d.ts +4 -0
  251. package/dist/shims/thenable-params.js +25 -3
  252. package/dist/shims/unified-request-context.d.ts +1 -1
  253. package/dist/shims/unified-request-context.js +1 -0
  254. package/dist/utils/encode-cache-tag.d.ts +1 -0
  255. package/dist/utils/encode-cache-tag.js +11 -1
  256. package/dist/utils/mdx-scan.d.ts +5 -0
  257. package/dist/utils/mdx-scan.js +8 -1
  258. package/dist/utils/regex-safety.d.ts +10 -0
  259. package/dist/utils/regex-safety.js +70 -18
  260. package/dist/utils/vite-cli-invocation.js +8 -1
  261. package/package.json +8 -4
  262. package/dist/server/metadata-route-cache-policy.d.ts +0 -5
  263. package/dist/server/metadata-route-cache-policy.js +0 -6
@@ -1,15 +1,18 @@
1
1
  import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { createValidFileMatcher, findFileWithExtensions } from "../routing/file-matcher.js";
3
+ import { isUnknownRecord } from "../utils/record.js";
3
4
  import { BLOCKED_PAGES } from "../shims/constants.js";
4
5
  import { VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_PRERENDER_SPECULATIVE_HEADER } from "../utils/protocol-headers.js";
5
6
  import { appRouteLayoutStaticParamsGroups } from "../routing/app-router.js";
6
- import { NEXT_CACHE_TAGS_HEADER, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER } from "../server/headers.js";
7
+ import { NEXT_CACHE_TAGS_HEADER, VINEXT_METADATA_ROUTE_CACHE_HEADER, VINEXT_PRERENDER_CACHE_LIFE_HEADER, VINEXT_PRERENDER_METADATA_ROUTES_PATH, VINEXT_PRERENDER_RENDER_ERROR_HEADER, VINEXT_PRERENDER_SPECIAL_ERROR_HEADER } from "../server/headers.js";
8
+ import { extractLocaleFromUrl } from "../server/pages-i18n.js";
7
9
  import { classifyAppRoute, classifyPagesRoute, getAppRouteRenderEntryPath } from "./report.js";
8
10
  import { resolveClientStaleTimeSeconds } from "../utils/cache-control-metadata.js";
9
11
  import { NoOpCacheHandler, getCacheHandler, setCacheHandler } from "../shims/cache-handler.js";
10
12
  import { headersContextFromRequest, runWithHeadersContext } from "../shims/headers.js";
11
13
  import { _consumeRequestScopedCacheLife } from "../shims/cache-request-state.js";
12
14
  import { normalizeStaticPathsEntry } from "../routing/route-pattern.js";
15
+ import { createRscRequestUrl } from "../server/app-rsc-cache-busting.js";
13
16
  import { readPrerenderSecret } from "./server-manifest.js";
14
17
  import { buildPregeneratedConcretePathTable } from "../server/prerender-manifest.js";
15
18
  import { concatUint8Arrays, decodeRscEmbeddedChunk } from "../server/app-rsc-embedded-chunks.js";
@@ -21,7 +24,6 @@ import { startProdServer } from "../server/prod-server.js";
21
24
  import { prerenderPoolAvailable, resolvePrerenderPoolSize, startPrerenderServerPool } from "./prerender-server-pool.js";
22
25
  import { enterPrerenderPhase } from "./prerender-phase.js";
23
26
  import { buildAppRouteCacheValue } from "../server/app-route-handler-response.js";
24
- import { isMetadataResponseCacheable } from "../server/metadata-route-cache-policy.js";
25
27
  import fs from "node:fs";
26
28
  import os from "node:os";
27
29
  //#region src/build/prerender.ts
@@ -337,6 +339,14 @@ function routeStaticParamSets(route, paramSets, options = {}) {
337
339
  function layoutOnlyParamSets(route, parentParamSets) {
338
340
  return parentParamSets.length > 0 && parentParamSets.every((params) => route.params.every((name) => name in params)) ? parentParamSets : null;
339
341
  }
342
+ function localizePagesPath(pathname, locale, i18n) {
343
+ if (!i18n || !locale) return pathname;
344
+ if (locale === i18n.defaultLocale) {
345
+ const firstSegment = pathname.split("/")[1]?.toLowerCase();
346
+ if (!i18n.locales.some((candidate) => candidate.toLowerCase() === firstSegment)) return pathname;
347
+ }
348
+ return pathname === "/" ? `/${locale}` : `/${locale}${pathname}`;
349
+ }
340
350
  /**
341
351
  * Run the prerender phase for Pages Router.
342
352
  *
@@ -379,6 +389,8 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
379
389
  port: 0,
380
390
  host: "127.0.0.1",
381
391
  outDir: path.dirname(path.dirname(pagesBundlePath)),
392
+ serverDir: path.dirname(pagesBundlePath),
393
+ serverEntryPath: pagesBundlePath,
382
394
  noCompression: true,
383
395
  purpose: "prerender"
384
396
  });
@@ -428,7 +440,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
428
440
  for (const route of bundlePageRoutes) {
429
441
  if (BLOCKED_PAGES.includes(route.pattern)) continue;
430
442
  if (route.pattern === "/404") continue;
431
- const { type, revalidate: classifiedRevalidate } = classifyPagesRoute(route.filePath);
443
+ const { type, revalidate: classifiedRevalidate, hasStaticProps = false } = classifyPagesRoute(route.filePath);
432
444
  if (type === "ssr") {
433
445
  if (mode === "export") results.push({
434
446
  route: route.pattern,
@@ -460,8 +472,8 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
460
472
  let pathsResult;
461
473
  try {
462
474
  pathsResult = await route.module.getStaticPaths({
463
- locales: [],
464
- defaultLocale: ""
475
+ locales: [...config.i18n?.locales ?? []],
476
+ defaultLocale: config.i18n?.defaultLocale ?? ""
465
477
  });
466
478
  } catch (e) {
467
479
  results.push({
@@ -483,21 +495,38 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
483
495
  }
484
496
  const paths = pathsResult?.paths ?? [];
485
497
  let entryError = null;
498
+ const seenPaths = /* @__PURE__ */ new Set();
486
499
  for (const item of paths) {
487
- const normalized = normalizeStaticPathsEntry(item, route.pattern);
500
+ let locale = config.i18n?.defaultLocale;
501
+ let pathEntry = item;
502
+ if (config.i18n && typeof item === "string") {
503
+ const localized = extractLocaleFromUrl(item, config.i18n);
504
+ locale = localized.locale;
505
+ pathEntry = localized.url;
506
+ } else if (item && typeof item === "object" && item.locale !== void 0) locale = item.locale;
507
+ const normalized = normalizeStaticPathsEntry(pathEntry, route.pattern);
488
508
  if ("error" in normalized) {
489
509
  entryError = normalized.error;
490
510
  break;
491
511
  }
512
+ if (locale !== void 0 && !config.i18n?.locales.includes(locale)) {
513
+ entryError = `Invalid locale returned from getStaticPaths for ${route.pattern}: ${locale}`;
514
+ break;
515
+ }
492
516
  const { params } = normalized;
493
517
  try {
494
- const urlPath = buildUrlFromParams(route.pattern, params);
495
- pagesToRender.push({
496
- route,
497
- urlPath,
498
- params,
499
- revalidate
500
- });
518
+ const urlPath = localizePagesPath(buildUrlFromParams(route.pattern, params), locale, config.i18n);
519
+ if (!seenPaths.has(urlPath)) {
520
+ seenPaths.add(urlPath);
521
+ pagesToRender.push({
522
+ route,
523
+ urlPath,
524
+ params,
525
+ revalidate,
526
+ hasStaticProps,
527
+ locale
528
+ });
529
+ }
501
530
  } catch (e) {
502
531
  entryError = e.message;
503
532
  break;
@@ -511,25 +540,58 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
511
540
  });
512
541
  continue;
513
542
  }
514
- } else pagesToRender.push({
543
+ } else for (const locale of config.i18n?.locales ?? [void 0]) pagesToRender.push({
515
544
  route,
516
- urlPath: route.pattern,
545
+ urlPath: localizePagesPath(route.pattern, locale, config.i18n),
517
546
  params: {},
518
- revalidate
547
+ revalidate,
548
+ hasStaticProps,
549
+ locale
519
550
  });
520
551
  }
521
552
  if (!options._prodServer && pagesBundlePath && prerenderPoolAvailable()) {
522
553
  const poolSize = resolvePrerenderPoolSize(pagesToRender.length, concurrency);
523
554
  if (poolSize > 1) {
524
- renderPool = await startOptionalPrerenderServerPool(path.dirname(path.dirname(pagesBundlePath)), poolSize);
555
+ renderPool = await startOptionalPrerenderServerPool(path.dirname(path.dirname(pagesBundlePath)), poolSize, void 0, path.dirname(pagesBundlePath));
525
556
  if (renderPool) renderPorts = renderPool.ports;
526
557
  }
527
558
  }
528
559
  let completed = 0;
529
- const pageResults = await runWithConcurrency(pagesToRender, concurrency, async ({ route, urlPath, revalidate }) => {
560
+ const pageResults = await runWithConcurrency(pagesToRender, concurrency, async ({ route, urlPath, revalidate, hasStaticProps, locale }) => {
530
561
  let result;
531
562
  try {
532
563
  const response = await renderPage(urlPath);
564
+ if (mode === "default" && response.headers.get("x-vinext-prerender-rewritten") !== "0") {
565
+ response.body?.cancel().catch(() => {});
566
+ const skipped = {
567
+ route: route.pattern,
568
+ status: "skipped",
569
+ reason: "dynamic"
570
+ };
571
+ onProgress?.({
572
+ completed: ++completed,
573
+ total: pagesToRender.length,
574
+ route: urlPath,
575
+ status: skipped.status
576
+ });
577
+ return skipped;
578
+ }
579
+ const contentType = response.headers.get("content-type");
580
+ const isStaticPropsResponse = mode === "default" && hasStaticProps && response.headers.get("X-Vinext-Cache") === "MISS";
581
+ const notFound = isStaticPropsResponse && response.status === 404;
582
+ const redirect = isStaticPropsResponse && [
583
+ 301,
584
+ 302,
585
+ 303,
586
+ 307,
587
+ 308
588
+ ].includes(response.status);
589
+ let redirectProps;
590
+ if (redirect) {
591
+ const props = await response.json();
592
+ if (!isUnknownRecord(props) || !isUnknownRecord(props.pageProps) || typeof props.pageProps.__N_REDIRECT !== "string" || props.pageProps.__N_REDIRECT_STATUS !== response.status || props.pageProps.__N_REDIRECT_BASE_PATH !== void 0 && typeof props.pageProps.__N_REDIRECT_BASE_PATH !== "boolean") throw new Error(`Invalid prerendered Pages redirect props for ${urlPath}`);
593
+ redirectProps = props;
594
+ }
533
595
  const outputFiles = [];
534
596
  const htmlOutputPath = getOutputPath(urlPath, config.trailingSlash, mode === "export" ? config.basePath : "");
535
597
  const htmlFullPath = path.join(outDir, htmlOutputPath);
@@ -539,7 +601,7 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
539
601
  fs.writeFileSync(htmlFullPath, html, "utf-8");
540
602
  outputFiles.push(htmlOutputPath);
541
603
  } else {
542
- if (!response.ok) throw new Error(`renderPage returned ${response.status} for ${urlPath}`);
604
+ if (!response.ok && !notFound) throw new Error(`renderPage returned ${response.status} for ${urlPath}`);
543
605
  const html = await response.text();
544
606
  fs.mkdirSync(path.dirname(htmlFullPath), { recursive: true });
545
607
  fs.writeFileSync(htmlFullPath, html, "utf-8");
@@ -552,7 +614,19 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
552
614
  revalidate,
553
615
  ...typeof revalidate === "number" ? { expire: config.expireTime } : {},
554
616
  router: "pages",
555
- ...urlPath !== route.pattern ? { path: urlPath } : {}
617
+ ...urlPath !== route.pattern ? { path: urlPath } : {},
618
+ ...locale ? { locale } : {},
619
+ ...response.ok ? { responseStatus: response.status } : {},
620
+ ...response.ok && contentType ? { headers: { "content-type": contentType } } : {},
621
+ ...notFound ? {
622
+ notFound: true,
623
+ responseStatus: 404
624
+ } : {},
625
+ ...redirect ? {
626
+ responseStatus: response.status,
627
+ redirectProps,
628
+ headers: { location: response.headers.get("location") ?? "/" }
629
+ } : {}
556
630
  };
557
631
  } catch (e) {
558
632
  renderPool?.recordRenderError(e);
@@ -575,21 +649,41 @@ async function prerenderPages({ routes, apiRoutes, pagesDir, outDir, config, mod
575
649
  renderPool?.assertHealthy();
576
650
  const hasCustom404 = findFileWithExtensions(path.join(pagesDir, "404"), fileMatcher);
577
651
  const hasErrorPage = findFileWithExtensions(path.join(pagesDir, "_error"), fileMatcher);
578
- if (hasCustom404 || hasErrorPage) try {
579
- const notFoundRes = await renderPage(hasCustom404 ? "/404" : NOT_FOUND_SENTINEL_PATH);
580
- const contentType = notFoundRes.headers.get("content-type") ?? "";
581
- if (notFoundRes.status === 404 && contentType.includes("text/html")) {
582
- const html404 = await notFoundRes.text();
583
- results.push({
584
- route: "/404",
585
- status: "rendered",
586
- outputFiles: emitStatic404Files(outDir, html404, config.trailingSlash),
587
- revalidate: false,
588
- router: "pages"
589
- });
652
+ for (const locale of config.i18n?.locales ?? [void 0]) {
653
+ if (!hasCustom404 && !hasErrorPage) break;
654
+ try {
655
+ const notFoundRes = await renderPage(localizePagesPath(hasCustom404 ? "/404" : NOT_FOUND_SENTINEL_PATH, locale, config.i18n));
656
+ if (mode === "default" && notFoundRes.headers.get("x-vinext-prerender-rewritten") !== "0") {
657
+ notFoundRes.body?.cancel().catch(() => {});
658
+ continue;
659
+ }
660
+ const contentType = notFoundRes.headers.get("content-type") ?? "";
661
+ if (notFoundRes.status === 404 && contentType.includes("text/html")) {
662
+ const html404 = await notFoundRes.text();
663
+ const pathname = localizePagesPath("/404", locale, config.i18n);
664
+ let outputFiles;
665
+ if (pathname === "/404") outputFiles = emitStatic404Files(outDir, html404, config.trailingSlash);
666
+ else {
667
+ const outputPath = getOutputPath(pathname, config.trailingSlash);
668
+ const fullPath = path.join(outDir, outputPath);
669
+ fs.mkdirSync(path.dirname(fullPath), { recursive: true });
670
+ fs.writeFileSync(fullPath, html404, "utf8");
671
+ outputFiles = [outputPath];
672
+ }
673
+ results.push({
674
+ route: "/404",
675
+ status: "rendered",
676
+ outputFiles,
677
+ revalidate: false,
678
+ router: "pages",
679
+ headers: { "content-type": contentType },
680
+ ...pathname !== "/404" ? { path: pathname } : {},
681
+ ...locale ? { locale } : {}
682
+ });
683
+ }
684
+ } catch (e) {
685
+ renderPool?.recordRenderError(e);
590
686
  }
591
- } catch (e) {
592
- renderPool?.recordRenderError(e);
593
687
  }
594
688
  renderPool?.assertHealthy();
595
689
  if (!skipManifest) writePrerenderIndex(results, manifestDir, {
@@ -900,7 +994,8 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
900
994
  };
901
995
  }
902
996
  const cacheControl = response.headers.get("cache-control") ?? "";
903
- if (!isMetadataResponseCacheable(response)) {
997
+ const requestCacheLife = readPrerenderCacheLifeHeader(response.headers);
998
+ if (requestCacheLife?.revalidate === 0) {
904
999
  await response.body?.cancel();
905
1000
  return {
906
1001
  route: routePattern,
@@ -908,15 +1003,14 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
908
1003
  reason: "dynamic"
909
1004
  };
910
1005
  }
911
- const requestCacheLife = readPrerenderCacheLifeHeader(response.headers);
912
1006
  const collectedTags = readPrerenderCacheTagsHeader(response.headers);
913
- const cacheValue = await buildAppRouteCacheValue(response);
1007
+ const cacheValue = await buildAppRouteCacheValue(response, cacheControl);
914
1008
  cacheValue.headers[VINEXT_METADATA_ROUTE_CACHE_HEADER] = "1";
915
1009
  const outputPath = getAppRouteOutputPath(urlPath);
916
1010
  const fullPath = path.join(outDir, outputPath);
917
1011
  fs.mkdirSync(path.dirname(fullPath), { recursive: true });
918
1012
  fs.writeFileSync(fullPath, new Uint8Array(cacheValue.body));
919
- const renderedCacheControl = resolveRenderedCacheControl(requestCacheLife ?? {}, cacheControl, config.expireTime);
1013
+ const renderedCacheControl = resolveRenderedCacheControl(requestCacheLife ?? {}, "", config.expireTime);
920
1014
  const renderedRevalidate = renderedCacheControl.revalidate ?? false;
921
1015
  const renderedStale = resolveClientStaleTimeSeconds(requestCacheLife ?? void 0);
922
1016
  return {
@@ -948,17 +1042,22 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
948
1042
  const responseCacheLife = readPrerenderCacheLifeHeader(response.headers);
949
1043
  const cacheTags = readPrerenderCacheTagsHeader(response.headers);
950
1044
  const fatal = response.headers.get(VINEXT_PRERENDER_RENDER_ERROR_HEADER) === "1";
951
- if (!response.ok || cacheControl.includes("no-store")) {
1045
+ const specialError = mode === "export" ? null : readPrerenderSpecialErrorHeader(response.headers);
1046
+ const isSpecialError = specialError !== null;
1047
+ const ok = response.ok || isSpecialError;
1048
+ if (!ok || cacheControl.includes("no-store")) {
952
1049
  await response.body?.cancel();
953
1050
  return {
954
1051
  cacheControl,
955
1052
  linkHeader,
956
1053
  html: null,
957
- ok: response.ok,
1054
+ ok,
958
1055
  requestCacheLife: null,
959
1056
  tags: [],
960
1057
  status: response.status,
961
- fatal
1058
+ fatal,
1059
+ specialErrorLocation: null,
1060
+ isSpecialError
962
1061
  };
963
1062
  }
964
1063
  const html = await response.text();
@@ -971,7 +1070,9 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
971
1070
  requestCacheLife: responseCacheLife ?? processCacheLife,
972
1071
  status: response.status,
973
1072
  tags: cacheTags,
974
- fatal: false
1073
+ fatal: false,
1074
+ specialErrorLocation: specialError?.location ?? null,
1075
+ isSpecialError
975
1076
  };
976
1077
  });
977
1078
  const htmlCacheControl = htmlRender.cacheControl;
@@ -999,7 +1100,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
999
1100
  error: "RSC handler returned no prerender HTML"
1000
1101
  };
1001
1102
  const html = isFallback ? markAppPprDynamicFallbackShellHtml(htmlRender.html) : htmlRender.html;
1002
- let rscData = extractRscPayloadFromPrerenderedHtml(html);
1103
+ let rscData = htmlRender.isSpecialError ? null : extractRscPayloadFromPrerenderedHtml(html);
1003
1104
  if (rscData === null) {
1004
1105
  const rscHeaders = new Headers({
1005
1106
  Accept: "text/x-component",
@@ -1007,7 +1108,7 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
1007
1108
  });
1008
1109
  if (prerenderRouteParamsHeader !== null) rscHeaders.set(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, prerenderRouteParamsHeader);
1009
1110
  if (isSpeculative) rscHeaders.set(VINEXT_PRERENDER_SPECULATIVE_HEADER, "1");
1010
- const rscRequest = new Request(`http://localhost${requestPath}`, { headers: rscHeaders });
1111
+ const rscRequest = new Request(`http://localhost${await createRscRequestUrl(requestPath, rscHeaders)}`, { headers: rscHeaders });
1011
1112
  const rscRes = await runWithHeadersContext(headersContextFromRequest(rscRequest), () => rscHandler(rscRequest));
1012
1113
  if (!rscRes.ok) {
1013
1114
  await rscRes.body?.cancel();
@@ -1042,7 +1143,11 @@ async function prerenderApp({ routes, metadataRoutes = [], outDir, config, mode,
1042
1143
  ...renderedStale === void 0 ? {} : { stale: renderedStale },
1043
1144
  router: "app",
1044
1145
  ...htmlRender.tags.length > 0 ? { tags: htmlRender.tags } : {},
1045
- ...htmlRender.linkHeader ? { headers: { link: htmlRender.linkHeader } } : {},
1146
+ ...htmlRender.linkHeader || htmlRender.specialErrorLocation !== null ? { headers: {
1147
+ ...htmlRender.linkHeader ? { link: htmlRender.linkHeader } : {},
1148
+ ...htmlRender.specialErrorLocation !== null ? { location: htmlRender.specialErrorLocation } : {}
1149
+ } } : {},
1150
+ ...htmlRender.isSpecialError ? { responseStatus: htmlRender.status } : {},
1046
1151
  ...urlPath !== routePattern ? { path: urlPath } : {},
1047
1152
  ...isFallback ? { fallback: true } : {}
1048
1153
  };
@@ -1146,6 +1251,19 @@ function readPrerenderCacheLifeHeader(headers) {
1146
1251
  return null;
1147
1252
  }
1148
1253
  }
1254
+ /** Reads the special-error marker: the page's stored headers, or null. */
1255
+ function readPrerenderSpecialErrorHeader(headers) {
1256
+ const value = headers.get(VINEXT_PRERENDER_SPECIAL_ERROR_HEADER);
1257
+ if (!value) return null;
1258
+ try {
1259
+ const parsed = JSON.parse(value);
1260
+ if (typeof parsed !== "object" || parsed === null) return null;
1261
+ const location = parsed.location;
1262
+ return { location: typeof location === "string" ? location : null };
1263
+ } catch {
1264
+ return null;
1265
+ }
1266
+ }
1149
1267
  function readPrerenderCacheTagsHeader(headers) {
1150
1268
  const value = headers.get(NEXT_CACHE_TAGS_HEADER);
1151
1269
  if (!value) return [];
@@ -1187,6 +1305,9 @@ function writePrerenderIndex(routes, outDir, options) {
1187
1305
  ...r.routeSegments ? { routeSegments: r.routeSegments } : {},
1188
1306
  ...r.headers ? { headers: r.headers } : {},
1189
1307
  ...typeof r.responseStatus === "number" ? { responseStatus: r.responseStatus } : {},
1308
+ ...r.notFound ? { notFound: true } : {},
1309
+ ...r.redirectProps ? { redirectProps: r.redirectProps } : {},
1310
+ ...r.locale ? { locale: r.locale } : {},
1190
1311
  ...r.path ? { path: r.path } : {},
1191
1312
  ...r.fallback ? { fallback: true } : {}
1192
1313
  };
@@ -1210,4 +1331,4 @@ function writePrerenderIndex(routes, outDir, options) {
1210
1331
  fs.writeFileSync(path.join(outDir, "vinext-prerender.json"), JSON.stringify(index, null, 2), "utf-8");
1211
1332
  }
1212
1333
  //#endregion
1213
- export { buildUrlFromParams, extractRscPayloadFromPrerenderedHtml, layoutOnlyParamSets, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, routeStaticParamSets, validateDiscoveredParams, writePrerenderIndex };
1334
+ export { buildUrlFromParams, extractRscPayloadFromPrerenderedHtml, layoutOnlyParamSets, localizePagesPath, prerenderApp, prerenderPages, readPrerenderSecret, resolveParentParams, routeStaticParamSets, validateDiscoveredParams, writePrerenderIndex };
@@ -11,7 +11,7 @@ import { rememberCurrentServerEntryImportMtime, startProdServer } from "../serve
11
11
  import { enterPrerenderPhase } from "./prerender-phase.js";
12
12
  import { prerenderApp, prerenderPages, writePrerenderIndex } from "./prerender.js";
13
13
  import { injectPregeneratedConcretePaths } from "./inject-pregenerated-paths.js";
14
- import { resolveBuiltRscEntryPath } from "./server-entry.js";
14
+ import { resolveBuiltPagesEntryPath, resolveBuiltRscEntryPath } from "./server-entry.js";
15
15
  import fs from "node:fs";
16
16
  //#region src/build/run-prerender.ts
17
17
  /**
@@ -112,10 +112,11 @@ async function runPrerender(options) {
112
112
  const buildOutDir = path.resolve(root, "dist");
113
113
  const configuredRscServerDir = path.resolve(root, options.routeRootConfig?.rscOutDir ?? path.join("dist", "server"));
114
114
  const rscBundlePath = options.rscBundlePath ?? resolveBuiltRscEntryPath(configuredRscServerDir);
115
+ const pagesBundlePath = options.pagesBundlePath ?? resolveBuiltPagesEntryPath(path.resolve(root, options.routeRootConfig?.ssrOutDir ?? path.join("dist", "server")));
115
116
  const relativeEntryPath = path.relative(configuredRscServerDir, rscBundlePath);
116
117
  const serverDir = !relativeEntryPath.startsWith("../") && !path.isAbsolute(relativeEntryPath) ? configuredRscServerDir : path.dirname(rscBundlePath);
117
118
  const config = options.nextConfig ? { ...options.nextConfig } : { ...await resolveNextConfig(await loadNextConfig(root, PHASE_PRODUCTION_BUILD), root) };
118
- const builtBuildId = readBuiltBuildId(manifestDir) ?? readBuiltBuildId(serverDir);
119
+ const builtBuildId = readBuiltBuildId(manifestDir) ?? readBuiltBuildId(appDir ? serverDir : path.dirname(pagesBundlePath));
119
120
  if (builtBuildId) config.buildId = builtBuildId;
120
121
  const mode = config.output === "export" ? "export" : "default";
121
122
  const allRoutes = [];
@@ -183,7 +184,7 @@ async function runPrerender(options) {
183
184
  ...sharedProdServer ? {
184
185
  _prodServer: sharedProdServer,
185
186
  _prerenderSecret: sharedPrerenderSecret
186
- } : { pagesBundlePath: options.pagesBundlePath ?? path.join(manifestDir, "entry.js") },
187
+ } : { pagesBundlePath },
187
188
  onProgress: ({ total, route }) => {
188
189
  if (pagesTotal === 0) {
189
190
  pagesTotal = total;
@@ -1,4 +1,6 @@
1
1
  //#region src/build/server-entry.d.ts
2
+ /** Pages builds emit entry.js in Node or index.js when the host owns the Worker entry. */
3
+ export declare function resolveBuiltPagesEntryPath(serverDir: string): string;
2
4
  /** Resolve the built App handler even when a deployment host owns index.js. */
3
5
  export declare function resolveBuiltRscEntryPath(serverDir: string): string;
4
6
  //#endregion
@@ -2,6 +2,12 @@ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import fs from "node:fs";
3
3
  //#region src/build/server-entry.ts
4
4
  const RSC_ENTRY_MANIFEST_KEY = "virtual:vinext-rsc-entry";
5
+ /** Pages builds emit entry.js in Node or index.js when the host owns the Worker entry. */
6
+ function resolveBuiltPagesEntryPath(serverDir) {
7
+ const nodeEntryPath = path.join(serverDir, "entry.js");
8
+ const workerEntryPath = path.join(serverDir, "index.js");
9
+ return fs.existsSync(nodeEntryPath) || !fs.existsSync(workerEntryPath) ? nodeEntryPath : workerEntryPath;
10
+ }
5
11
  /** Resolve the built App handler even when a deployment host owns index.js. */
6
12
  function resolveBuiltRscEntryPath(serverDir) {
7
13
  const fallbackPath = path.join(serverDir, "index.js");
@@ -25,4 +31,4 @@ function resolveBuiltRscEntryPath(serverDir) {
25
31
  return fs.existsSync(entryPath) ? entryPath : fallbackPath;
26
32
  }
27
33
  //#endregion
28
- export { resolveBuiltRscEntryPath };
34
+ export { resolveBuiltPagesEntryPath, resolveBuiltRscEntryPath };
@@ -14,6 +14,13 @@
14
14
  * warn when this returns `undefined`.
15
15
  */
16
16
  export declare function readPrerenderSecret(serverDir: string): string | undefined;
17
+ /**
18
+ * Read next.config `compress` from `vinext-server.json` in `serverDir`.
19
+ *
20
+ * Like `next start`, compression stays on unless the build recorded an
21
+ * explicit `compress: false`, so builds without the field keep compressing.
22
+ */
23
+ export declare function readServerCompress(serverDir: string): boolean;
17
24
  /**
18
25
  * Read every server output root that contains a deployable response-stage
19
26
  * graph. Paths are stored relative to the project root so build artifacts stay
@@ -20,6 +20,16 @@ function readPrerenderSecret(serverDir) {
20
20
  return readJsonFile(manifestPath)?.prerenderSecret;
21
21
  }
22
22
  /**
23
+ * Read next.config `compress` from `vinext-server.json` in `serverDir`.
24
+ *
25
+ * Like `next start`, compression stays on unless the build recorded an
26
+ * explicit `compress: false`, so builds without the field keep compressing.
27
+ */
28
+ function readServerCompress(serverDir) {
29
+ const manifestPath = path.join(serverDir, "vinext-server.json");
30
+ return readJsonFile(manifestPath)?.compress !== false;
31
+ }
32
+ /**
23
33
  * Read every server output root that contains a deployable response-stage
24
34
  * graph. Paths are stored relative to the project root so build artifacts stay
25
35
  * relocatable, then resolved for post-build sidecar updates.
@@ -31,4 +41,4 @@ function readServerRuntimeOutputDirs(serverDir, root) {
31
41
  return manifest.runtimeOutputDirs.filter((outputDir) => typeof outputDir === "string").map((outputDir) => path.resolve(root, outputDir));
32
42
  }
33
43
  //#endregion
34
- export { readPrerenderSecret, readServerRuntimeOutputDirs };
44
+ export { readPrerenderSecret, readServerCompress, readServerRuntimeOutputDirs };
@@ -74,7 +74,13 @@ export type CacheAdapterBuildOutput = {
74
74
  export type CacheAdapterDescriptor<O extends Record<string, unknown> = Record<string, unknown>> = {
75
75
  /**
76
76
  * Module specifier (or absolute path, e.g. from `require.resolve(...)`) whose
77
- * default export is a cache adapter factory.
77
+ * default export creates the adapter from one `{ env, options }` argument:
78
+ * either a factory `({ env, options }) => adapter` or a class
79
+ * `new Adapter({ env, options })`. Class syntax is invoked with `new`; any
80
+ * other function is called. For a constructor that is not class syntax (an
81
+ * ES5-compiled, bound or Proxy-wrapped class), export
82
+ * `(args) => new Adapter(args)`. The result must be the adapter object
83
+ * itself, not a Promise.
78
84
  */
79
85
  adapter: string;
80
86
  /** JSON-serializable options forwarded to the factory at runtime. */
@@ -103,7 +103,7 @@ function generateCacheAdaptersModule(cache) {
103
103
  "export function registerConfiguredCacheAdapters() {}",
104
104
  ""
105
105
  ].join("\n");
106
- const lines = ["// vinext: generated from the `cache` option in your vinext() plugin config."];
106
+ const lines = ["// vinext: generated from the `cache` option in your vinext() plugin config.", `import { instantiateCacheAdapter } from "vinext/shims/cache-adapter-instantiate";`];
107
107
  if (data?.adapter) {
108
108
  lines.push(`import __vinextDataAdapterFactory from ${JSON.stringify(data.adapter)};`);
109
109
  lines.push(`import { registerDataCacheHandler } from "vinext/shims/cache-handler";`);
@@ -114,11 +114,11 @@ function generateCacheAdaptersModule(cache) {
114
114
  } else if (dataProvidesBuildIdentity) lines.push(`import { DefaultCdnCacheAdapter } from "vinext/shims/cdn-cache";`, `import { registerCdnCacheAdapter } from "vinext/shims/cdn-cache-state";`);
115
115
  lines.push("", "// A factory that throws (e.g. a missing binding on an incompatible runtime)", "// is logged and skipped so the default handler stays in place.", "function __vinextFormatAdapterError(error) {", " if (error instanceof Error && error.message) return error.message;", " try {", " return String(error);", " } catch {", " return '<unknown error>';", " }", "}", "", "let __vinextCacheAdaptersRegistered = false;", "", "export function registerConfiguredCacheAdapters(env) {", " if (typeof process !== 'undefined' && process.env?.__VINEXT_PRERENDER_PATH_DISCOVERY === '1') return;", " if (__vinextCacheAdaptersRegistered) return;", " __vinextCacheAdaptersRegistered = true;");
116
116
  if (data?.adapter) {
117
- lines.push(" try {", ` registerDataCacheHandler(() => __vinextDataAdapterFactory({ env, options: ${inlineOptions(data.adapter, data.options)} }));`);
117
+ lines.push(" try {", ` registerDataCacheHandler(() => instantiateCacheAdapter(__vinextDataAdapterFactory, { env, options: ${inlineOptions(data.adapter, data.options)} }, "data"));`);
118
118
  if (dataProvidesBuildIdentity) lines.push(" registerCdnCacheAdapter(() => new DefaultCdnCacheAdapter(", " process.env.__VINEXT_RSC_BUILD_IDENTITY || process.env.__VINEXT_BUILD_ID,", " ));");
119
119
  lines.push(" } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured data cache adapter; using the default handler.\\n\" + __vinextFormatAdapterError(error));", " }");
120
120
  }
121
- if (cdn?.adapter) lines.push(" try {", ` registerCdnCacheAdapter(() => __vinextCdnAdapterFactory({ env, options: ${inlineOptions(cdn.adapter, cdn.options)} }));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured CDN cache adapter; using the default adapter.\\n\" + __vinextFormatAdapterError(error));", " }");
121
+ if (cdn?.adapter) lines.push(" try {", ` registerCdnCacheAdapter(() => instantiateCacheAdapter(__vinextCdnAdapterFactory, { env, options: ${inlineOptions(cdn.adapter, cdn.options)} }, "cdn"));`, " } catch (error) {", " console.warn(\"[vinext] failed to initialize the configured CDN cache adapter; using the default adapter.\\n\" + __vinextFormatAdapterError(error));", " }");
122
122
  lines.push("}", "");
123
123
  return lines.join("\n");
124
124
  }
@@ -0,0 +1,4 @@
1
+ //#region src/client/browser-globals.d.ts
2
+ export declare const RSC_FORM_STATE_GLOBAL = "__VINEXT_RSC_FORM_STATE__";
3
+ export declare const NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION = "vinext.navigationRuntime";
4
+ //#endregion
@@ -0,0 +1,5 @@
1
+ //#region src/client/browser-globals.ts
2
+ const RSC_FORM_STATE_GLOBAL = "__VINEXT_RSC_FORM_STATE__";
3
+ const NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION = "vinext.navigationRuntime";
4
+ //#endregion
5
+ export { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION, RSC_FORM_STATE_GLOBAL };
@@ -57,6 +57,17 @@ export type NavigationRuntimeFunctions = {
57
57
  preparePrefetchResponse?: (response: Response) => Promise<unknown>;
58
58
  claimCurrentHistoryTreeSnapshot?: (historyUpdateMode: NavigationRuntimeHistoryUpdateMode, previousHistoryState: unknown) => void;
59
59
  commitAppOwnedHistoryStateWrite?: (historyUpdateMode: NavigationRuntimeHistoryUpdateMode, previousHistoryState: unknown) => void;
60
+ /**
61
+ * Called after a raw (non-app-owned) history.pushState/replaceState with a
62
+ * URL so an App Router navigation that has not committed yet is discarded,
63
+ * matching Next.js' ACTION_RESTORE.
64
+ */
65
+ discardPendingNavigation?: () => void;
66
+ /**
67
+ * Called before any history.pushState/replaceState so a navigation React is
68
+ * committing writes its URL first, as Next.js does from an insertion effect.
69
+ */
70
+ flushCommittingNavigationUrl?: () => void;
60
71
  };
61
72
  export type NavigationRuntimeBootstrap = {
62
73
  routeManifest: RouteManifest | null;
@@ -66,7 +77,6 @@ export type NavigationRuntime = {
66
77
  bootstrap: NavigationRuntimeBootstrap;
67
78
  functions: NavigationRuntimeFunctions;
68
79
  };
69
- export declare const NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION = "vinext.navigationRuntime";
70
80
  export declare const NAVIGATION_RUNTIME_KEY: unique symbol;
71
81
  export declare function getNavigationRuntime(): NavigationRuntime | null;
72
82
  export declare function registerNavigationRuntimeBootstrap(bootstrap: Partial<NavigationRuntimeBootstrap>): NavigationRuntime;
@@ -1,6 +1,6 @@
1
1
  import { isUnknownRecord } from "../utils/record.js";
2
+ import { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from "./browser-globals.js";
2
3
  //#region src/client/navigation-runtime.ts
3
- const NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION = "vinext.navigationRuntime";
4
4
  const NAVIGATION_RUNTIME_KEY = Symbol.for(NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION);
5
5
  const ROUTE_MANIFEST_SEGMENT_GRAPH_MAP_KEYS = [
6
6
  "boundaries",
@@ -31,7 +31,7 @@ function readRuntimeWindow() {
31
31
  }
32
32
  function isNavigationRuntimeFunctions(value) {
33
33
  if (!isUnknownRecord(value)) return false;
34
- return isOptionalRuntimeFunction(Reflect.get(value, "clearNavigationCaches")) && isOptionalRuntimeFunction(Reflect.get(value, "commitHashNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "navigateExternal")) && isOptionalRuntimeFunction(Reflect.get(value, "navigate")) && isOptionalRuntimeFunction(Reflect.get(value, "getPrefetchRouterState")) && isOptionalRuntimeFunction(Reflect.get(value, "notifyLinkNavigationStart")) && isOptionalRuntimeFunction(Reflect.get(value, "pingVisibleLinks")) && isOptionalRuntimeFunction(Reflect.get(value, "preparePrefetchResponse")) && isOptionalRuntimeFunction(Reflect.get(value, "claimCurrentHistoryTreeSnapshot")) && isOptionalRuntimeFunction(Reflect.get(value, "commitAppOwnedHistoryStateWrite"));
34
+ return isOptionalRuntimeFunction(Reflect.get(value, "clearNavigationCaches")) && isOptionalRuntimeFunction(Reflect.get(value, "commitHashNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "navigateExternal")) && isOptionalRuntimeFunction(Reflect.get(value, "navigate")) && isOptionalRuntimeFunction(Reflect.get(value, "getPrefetchRouterState")) && isOptionalRuntimeFunction(Reflect.get(value, "notifyLinkNavigationStart")) && isOptionalRuntimeFunction(Reflect.get(value, "pingVisibleLinks")) && isOptionalRuntimeFunction(Reflect.get(value, "preparePrefetchResponse")) && isOptionalRuntimeFunction(Reflect.get(value, "claimCurrentHistoryTreeSnapshot")) && isOptionalRuntimeFunction(Reflect.get(value, "commitAppOwnedHistoryStateWrite")) && isOptionalRuntimeFunction(Reflect.get(value, "discardPendingNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "flushCommittingNavigationUrl"));
35
35
  }
36
36
  function isNavigationRuntimeRscChunk(value) {
37
37
  if (typeof value === "string") return true;
@@ -169,4 +169,4 @@ function hasAppNavigationRuntimeBootstrap() {
169
169
  return getNavigationRuntime() !== null;
170
170
  }
171
171
  //#endregion
172
- export { NAVIGATION_RUNTIME_KEY, NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION, ensureNavigationRuntimeRscBootstrap, getNavigationRuntime, hasAppNavigationRuntime, hasAppNavigationRuntimeBootstrap, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions };
172
+ export { NAVIGATION_RUNTIME_KEY, ensureNavigationRuntimeRscBootstrap, getNavigationRuntime, hasAppNavigationRuntime, hasAppNavigationRuntimeBootstrap, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions };
@@ -307,6 +307,7 @@ function applyMiddlewareRequestHeaders(middlewareHeaders, request) {
307
307
  method: request.method,
308
308
  headers: nextHeaders,
309
309
  body: request.body,
310
+ signal: request.signal,
310
311
  duplex: request.body ? "half" : void 0
311
312
  });
312
313
  return {
@@ -859,9 +860,10 @@ async function proxyExternalRequest(request, externalUrl) {
859
860
  try {
860
861
  upstreamResponse = await fetch(targetUrl.href, {
861
862
  ...init,
862
- signal: controller.signal
863
+ signal: AbortSignal.any([request.signal, controller.signal])
863
864
  });
864
865
  } catch (e) {
866
+ if (request.signal.aborted && !controller.signal.aborted) return new Response(null, { status: 499 });
865
867
  if (e instanceof Error && e.name === "AbortError") {
866
868
  console.error("[vinext] External rewrite proxy timeout:", targetUrl.href);
867
869
  return new Response("Gateway Timeout", { status: 504 });