vinext 1.2.1 → 1.3.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 (212) hide show
  1. package/README.md +12 -9
  2. package/dist/build/client-build-config.d.ts +64 -3
  3. package/dist/build/client-build-config.js +131 -4
  4. package/dist/build/client-reference-groups.d.ts +163 -0
  5. package/dist/build/client-reference-groups.js +534 -0
  6. package/dist/build/module-dependency-cache.d.ts +31 -0
  7. package/dist/build/module-dependency-cache.js +51 -1
  8. package/dist/build/next-client-runtime-manifests.d.ts +10 -1
  9. package/dist/build/next-client-runtime-manifests.js +19 -4
  10. package/dist/build/nitro-public-files.d.ts +24 -0
  11. package/dist/build/nitro-public-files.js +60 -0
  12. package/dist/build/pages-client-assets-module.js +1 -1
  13. package/dist/build/prerender-paths.js +5 -2
  14. package/dist/build/server-actions-encryption-key.d.ts +23 -0
  15. package/dist/build/server-actions-encryption-key.js +101 -0
  16. package/dist/build/ssr-manifest.d.ts +3 -0
  17. package/dist/build/ssr-manifest.js +10 -1
  18. package/dist/client/client-rewrite-params.d.ts +9 -0
  19. package/dist/client/client-rewrite-params.js +20 -0
  20. package/dist/client/dev-error-overlay.js +2 -1
  21. package/dist/client/navigation-runtime.d.ts +25 -2
  22. package/dist/client/navigation-runtime.js +42 -11
  23. package/dist/client/vinext-next-data.d.ts +0 -1
  24. package/dist/config/config-matchers.d.ts +15 -40
  25. package/dist/config/config-matchers.js +183 -255
  26. package/dist/config/config-source.d.ts +34 -0
  27. package/dist/config/config-source.js +141 -0
  28. package/dist/config/header-params.d.ts +29 -0
  29. package/dist/config/header-params.js +106 -0
  30. package/dist/config/next-config.d.ts +7 -5
  31. package/dist/config/next-config.js +120 -7
  32. package/dist/entries/app-browser-entry.d.ts +14 -1
  33. package/dist/entries/app-browser-entry.js +26 -36
  34. package/dist/entries/app-rsc-entry.d.ts +2 -0
  35. package/dist/entries/app-rsc-entry.js +92 -57
  36. package/dist/entries/app-rsc-manifest.d.ts +10 -0
  37. package/dist/entries/app-rsc-manifest.js +20 -1
  38. package/dist/entries/app-ssr-entry.d.ts +4 -1
  39. package/dist/entries/app-ssr-entry.js +9 -2
  40. package/dist/entries/pages-client-entry.d.ts +1 -0
  41. package/dist/entries/pages-client-entry.js +6 -6
  42. package/dist/entries/pages-server-entry.js +17 -14
  43. package/dist/index.d.ts +11 -0
  44. package/dist/index.js +239 -83
  45. package/dist/init.js +17 -0
  46. package/dist/plugins/optimize-imports.d.ts +25 -2
  47. package/dist/plugins/optimize-imports.js +106 -20
  48. package/dist/plugins/rsc-client-reference-loaders.d.ts +45 -1
  49. package/dist/plugins/rsc-client-reference-loaders.js +336 -18
  50. package/dist/plugins/use-cache-callable.d.ts +5 -0
  51. package/dist/plugins/use-cache-callable.js +30 -8
  52. package/dist/plugins/worker-image-imports.d.ts +1 -0
  53. package/dist/plugins/worker-image-imports.js +1 -1
  54. package/dist/routing/app-route-graph.d.ts +1 -1
  55. package/dist/routing/browser-route-manifest.d.ts +43 -0
  56. package/dist/routing/browser-route-manifest.js +195 -0
  57. package/dist/routing/hashed-route-patterns.d.ts +4 -0
  58. package/dist/routing/hashed-route-patterns.js +22 -0
  59. package/dist/routing/optimistic-route-trie.d.ts +27 -0
  60. package/dist/routing/optimistic-route-trie.js +102 -0
  61. package/dist/routing/route-validation.d.ts +6 -1
  62. package/dist/routing/route-validation.js +26 -10
  63. package/dist/server/app-action-forwarding.d.ts +8 -0
  64. package/dist/server/app-action-forwarding.js +15 -1
  65. package/dist/server/app-browser-entry.js +147 -60
  66. package/dist/server/app-browser-error.d.ts +3 -1
  67. package/dist/server/app-browser-error.js +7 -2
  68. package/dist/server/app-browser-interception-context.d.ts +2 -2
  69. package/dist/server/app-browser-mpa-navigation.d.ts +1 -0
  70. package/dist/server/app-browser-mpa-navigation.js +6 -5
  71. package/dist/server/app-browser-navigation-controller.d.ts +17 -6
  72. package/dist/server/app-browser-navigation-controller.js +88 -26
  73. package/dist/server/app-browser-state.d.ts +2 -2
  74. package/dist/server/app-browser-visible-commit.d.ts +4 -4
  75. package/dist/server/app-client-reference-preloader.d.ts +23 -0
  76. package/dist/server/app-client-reference-preloader.js +30 -7
  77. package/dist/server/app-fallback-renderer.js +2 -1
  78. package/dist/server/app-middleware.d.ts +6 -0
  79. package/dist/server/app-middleware.js +8 -10
  80. package/dist/server/app-optimistic-routing.d.ts +8 -10
  81. package/dist/server/app-optimistic-routing.js +4 -95
  82. package/dist/server/app-page-boundary-render.js +8 -1
  83. package/dist/server/app-page-cache-render.js +1 -0
  84. package/dist/server/app-page-dispatch.js +2 -0
  85. package/dist/server/app-page-render.js +3 -1
  86. package/dist/server/app-page-rsc-render-status.d.ts +1 -0
  87. package/dist/server/app-page-rsc-render-status.js +1 -0
  88. package/dist/server/app-page-search-params-observation.d.ts +1 -4
  89. package/dist/server/app-page-search-params-observation.js +8 -10
  90. package/dist/server/app-page-stream.d.ts +7 -0
  91. package/dist/server/app-page-stream.js +1 -0
  92. package/dist/server/app-pages-bridge.d.ts +2 -1
  93. package/dist/server/app-pages-bridge.js +11 -7
  94. package/dist/server/app-prerender-static-params.js +3 -2
  95. package/dist/server/app-render-dependency.js +42 -2
  96. package/dist/server/app-request-stage-independent-entry.js +2 -2
  97. package/dist/server/app-response-stage-entry.js +3 -3
  98. package/dist/server/app-route-handler-dispatch.js +3 -1
  99. package/dist/server/app-route-handler-execution.d.ts +2 -0
  100. package/dist/server/app-route-handler-execution.js +9 -2
  101. package/dist/server/app-route-handler-middleware-context.d.ts +2 -1
  102. package/dist/server/app-route-handler-middleware-context.js +3 -3
  103. package/dist/server/app-route-handler-response.d.ts +2 -1
  104. package/dist/server/app-route-handler-response.js +2 -2
  105. package/dist/server/app-route-handler-runtime.d.ts +2 -0
  106. package/dist/server/app-route-handler-runtime.js +20 -3
  107. package/dist/server/app-route-module-loader.js +7 -6
  108. package/dist/server/app-route-tree.d.ts +20 -0
  109. package/dist/server/app-route-tree.js +18 -0
  110. package/dist/server/app-router-entry.js +2 -2
  111. package/dist/server/app-rsc-combined-handler.js +8 -1
  112. package/dist/server/app-rsc-errors.d.ts +7 -0
  113. package/dist/server/app-rsc-errors.js +9 -2
  114. package/dist/server/app-rsc-handler.d.ts +4 -1
  115. package/dist/server/app-rsc-handler.js +146 -130
  116. package/dist/server/app-rsc-response-finalizer.d.ts +5 -0
  117. package/dist/server/app-rsc-response-finalizer.js +4 -1
  118. package/dist/server/app-rsc-response-stage.js +3 -1
  119. package/dist/server/app-rsc-route-matching.d.ts +1 -0
  120. package/dist/server/app-rsc-route-matching.js +1 -1
  121. package/dist/server/app-server-action-execution.d.ts +23 -6
  122. package/dist/server/app-server-action-execution.js +50 -56
  123. package/dist/server/app-ssr-entry.d.ts +6 -0
  124. package/dist/server/app-ssr-entry.js +16 -7
  125. package/dist/server/app-ssr-stream.d.ts +7 -0
  126. package/dist/server/app-ssr-stream.js +13 -1
  127. package/dist/server/config-headers.js +12 -3
  128. package/dist/server/isolate-shared-promise.d.ts +10 -0
  129. package/dist/server/isolate-shared-promise.js +33 -0
  130. package/dist/server/isr-cache.js +3 -2
  131. package/dist/server/middleware-matcher-pattern.d.ts +0 -1
  132. package/dist/server/middleware-matcher-pattern.js +11 -140
  133. package/dist/server/middleware-matcher.js +5 -6
  134. package/dist/server/middleware-response-headers.d.ts +31 -0
  135. package/dist/server/middleware-response-headers.js +57 -1
  136. package/dist/server/middleware-runtime.js +26 -13
  137. package/dist/server/navigation-planner.d.ts +2 -2
  138. package/dist/server/nitro-public-files-plugin.d.ts +38 -0
  139. package/dist/server/nitro-public-files-plugin.js +106 -0
  140. package/dist/server/nitro-public-files.d.ts +21 -0
  141. package/dist/server/nitro-public-files.js +39 -0
  142. package/dist/server/pages-asset-tags.d.ts +5 -0
  143. package/dist/server/pages-asset-tags.js +9 -2
  144. package/dist/server/pages-client-assets.d.ts +4 -1
  145. package/dist/server/pages-i18n.d.ts +18 -0
  146. package/dist/server/pages-i18n.js +18 -2
  147. package/dist/server/pages-node-compat.d.ts +5 -3
  148. package/dist/server/pages-node-compat.js +35 -16
  149. package/dist/server/pages-request-pipeline.d.ts +6 -1
  150. package/dist/server/pages-request-pipeline.js +31 -35
  151. package/dist/server/pages-request-stage-entry.js +16 -6
  152. package/dist/server/pages-response-stage-entry.js +1 -1
  153. package/dist/server/pages-revalidate.js +1 -0
  154. package/dist/server/pages-router-entry.js +1 -1
  155. package/dist/server/prod-server.d.ts +2 -2
  156. package/dist/server/prod-server.js +71 -33
  157. package/dist/server/proxy-trust.d.ts +6 -0
  158. package/dist/server/proxy-trust.js +12 -6
  159. package/dist/server/request-pipeline.d.ts +5 -0
  160. package/dist/server/request-pipeline.js +12 -1
  161. package/dist/server/server-reference-loader.js +10 -1
  162. package/dist/server/static-file-cache.js +15 -3
  163. package/dist/server/worker-utils.js +1 -1
  164. package/dist/shims/cache-handler.js +3 -2
  165. package/dist/shims/cache-request-state.d.ts +40 -3
  166. package/dist/shims/cache-request-state.js +70 -5
  167. package/dist/shims/cache-runtime.js +47 -19
  168. package/dist/shims/cache.d.ts +2 -2
  169. package/dist/shims/cache.js +17 -8
  170. package/dist/shims/cdn-purge-routes.d.ts +24 -0
  171. package/dist/shims/cdn-purge-routes.js +77 -0
  172. package/dist/shims/dynamic.js +17 -4
  173. package/dist/shims/form.js +8 -3
  174. package/dist/shims/image-props.d.ts +93 -0
  175. package/dist/shims/image-props.js +323 -0
  176. package/dist/shims/image.d.ts +4 -18
  177. package/dist/shims/image.js +4 -319
  178. package/dist/shims/internal/headers-state.d.ts +7 -0
  179. package/dist/shims/internal/headers-state.js +14 -2
  180. package/dist/shims/internal/hybrid-client-route-owner-direct.d.ts +4 -1
  181. package/dist/shims/internal/hybrid-client-route-owner-direct.js +31 -14
  182. package/dist/shims/internal/hybrid-client-route-owner.js +8 -8
  183. package/dist/shims/link.js +5 -3
  184. package/dist/shims/navigation.d.ts +1 -1
  185. package/dist/shims/navigation.js +17 -5
  186. package/dist/shims/og.js +35 -10
  187. package/dist/shims/request-context.d.ts +13 -0
  188. package/dist/shims/router.js +19 -40
  189. package/dist/shims/server.d.ts +31 -4
  190. package/dist/shims/server.js +58 -10
  191. package/dist/shims/thenable-params.d.ts +6 -1
  192. package/dist/shims/thenable-params.js +11 -2
  193. package/dist/shims/unified-request-context.d.ts +5 -0
  194. package/dist/shims/unified-request-context.js +2 -0
  195. package/dist/utils/asset-prefix.d.ts +7 -0
  196. package/dist/utils/asset-prefix.js +10 -1
  197. package/dist/utils/client-build-manifest.js +12 -5
  198. package/dist/utils/client-runtime-metadata.d.ts +1 -0
  199. package/dist/utils/client-runtime-metadata.js +15 -2
  200. package/dist/utils/lazy-chunks.d.ts +4 -0
  201. package/dist/utils/manifest-paths.d.ts +14 -0
  202. package/dist/utils/manifest-paths.js +22 -1
  203. package/dist/utils/path-to-regexp.d.ts +21 -0
  204. package/dist/{server/middleware-path-to-regexp.js → utils/path-to-regexp.js} +14 -11
  205. package/dist/utils/plugin-react-version.d.ts +15 -0
  206. package/dist/utils/plugin-react-version.js +64 -0
  207. package/dist/utils/public-routes.d.ts +2 -0
  208. package/dist/utils/public-routes.js +18 -7
  209. package/package.json +1 -1
  210. package/dist/server/middleware-path-to-regexp.d.ts +0 -13
  211. package/dist/utils/regex-safety.d.ts +0 -17
  212. package/dist/utils/regex-safety.js +0 -789
@@ -0,0 +1,24 @@
1
+ //#region src/build/nitro-public-files.d.ts
2
+ /** The Nitro options this integration reads and updates. */
3
+ export type NitroPublicFilesOptions = {
4
+ dev?: boolean;
5
+ preset?: string;
6
+ baseURL?: string;
7
+ serveStatic?: boolean | string;
8
+ plugins?: string[];
9
+ virtual?: Record<string, string | (() => string)>;
10
+ output?: {
11
+ publicDir?: string;
12
+ };
13
+ };
14
+ /**
15
+ * Register vinext's Nitro runtime plugin on presets whose server serves the
16
+ * static output, so requests for public files run through vinext, including
17
+ * its middleware, and vinext can serve the public files its router resolves.
18
+ *
19
+ * Nitro serves the static output from its root, so this applies only when
20
+ * Nitro's `baseURL` is the root; vinext applies its own `basePath`. Returns
21
+ * whether the plugin is registered.
22
+ */
23
+ export declare function registerNitroPublicFiles(options: NitroPublicFilesOptions, pluginModule: string, getPublicFiles: () => string[]): boolean;
24
+ //#endregion
@@ -0,0 +1,60 @@
1
+ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
+ import fs from "node:fs";
3
+ //#region src/build/nitro-public-files.ts
4
+ const NITRO_PUBLIC_FILES_PLUGIN = "#vinext/nitro-public-files";
5
+ const CDN_STATIC_PRESETS = /* @__PURE__ */ new Set(["aws-amplify"]);
6
+ const COMPRESSED_EXTENSIONS = [
7
+ ".br",
8
+ ".gz",
9
+ ".zst"
10
+ ];
11
+ function toNitroAssetId(relativePath) {
12
+ let decoded = relativePath;
13
+ try {
14
+ decoded = decodeURIComponent(relativePath);
15
+ } catch {}
16
+ return "/" + decoded.replace(/^\.?\//, "");
17
+ }
18
+ /**
19
+ * List the static files Nitro serves for the files in `public/`, keyed as
20
+ * Nitro's static handler keys them: each file that Nitro copied to its output
21
+ * directory, and the compressed copies of it that Nitro wrote there.
22
+ */
23
+ function listNitroPublicAssets(publicFiles, outputPublicDir) {
24
+ if (!outputPublicDir) return [];
25
+ const assets = [];
26
+ for (const file of publicFiles) {
27
+ const relativePath = file.slice(1);
28
+ for (const extension of ["", ...COMPRESSED_EXTENSIONS]) if (fs.statSync(path.join(outputPublicDir, relativePath + extension), { throwIfNoEntry: false })?.isFile()) assets.push(toNitroAssetId(relativePath + extension));
29
+ }
30
+ return [...new Set(assets)].sort();
31
+ }
32
+ /**
33
+ * Register vinext's Nitro runtime plugin on presets whose server serves the
34
+ * static output, so requests for public files run through vinext, including
35
+ * its middleware, and vinext can serve the public files its router resolves.
36
+ *
37
+ * Nitro serves the static output from its root, so this applies only when
38
+ * Nitro's `baseURL` is the root; vinext applies its own `basePath`. Returns
39
+ * whether the plugin is registered.
40
+ */
41
+ function registerNitroPublicFiles(options, pluginModule, getPublicFiles) {
42
+ if (options.dev || !options.serveStatic) return false;
43
+ if (options.preset && CDN_STATIC_PRESETS.has(options.preset.replaceAll("_", "-"))) return false;
44
+ if ((options.baseURL ?? "/") !== "/") return false;
45
+ options.virtual ??= {};
46
+ options.virtual[NITRO_PUBLIC_FILES_PLUGIN] = () => [
47
+ "import { fetchViteEnv } from \"nitro/vite/runtime\";",
48
+ `import { createNitroPublicFilesPlugin } from ${JSON.stringify(pluginModule)};`,
49
+ "export default createNitroPublicFilesPlugin({",
50
+ ` publicAssets: ${JSON.stringify(listNitroPublicAssets(getPublicFiles(), options.output?.publicDir))},`,
51
+ " fetchApp: (request) => fetchViteEnv(\"ssr\", request),",
52
+ "});",
53
+ ""
54
+ ].join("\n");
55
+ options.plugins ??= [];
56
+ if (!options.plugins.includes(NITRO_PUBLIC_FILES_PLUGIN)) options.plugins.push(NITRO_PUBLIC_FILES_PLUGIN);
57
+ return true;
58
+ }
59
+ //#endregion
60
+ export { registerNitroPublicFiles };
@@ -6,7 +6,7 @@ import fs from "node:fs";
6
6
  //#region src/build/pages-client-assets-module.ts
7
7
  const PAGES_CLIENT_ASSETS_MODULE = "vinext-client-assets.js";
8
8
  function buildPagesClientAssetsModule(assets) {
9
- const prepared = assets.ssrManifest ? {
9
+ const prepared = assets.ssrManifest && !assets.sharedChunks ? {
10
10
  ...assets,
11
11
  sharedChunks: getSharedChunkFiles(assets.ssrManifest)
12
12
  } : assets;
@@ -9,7 +9,7 @@ import { loadNextConfig, resolveNextConfig } from "../config/next-config.js";
9
9
  import { appRouteHasMainTreeLoadingBoundary, appRouter, matchAppRoute } from "../routing/app-router.js";
10
10
  import { apiRouter, matchRoute, pagesRouter } from "../routing/pages-router.js";
11
11
  import { matchesMiddlewarePathname } from "../server/middleware-matcher.js";
12
- import { extractLocaleFromUrl, normalizeDefaultLocalePathname } from "../server/pages-i18n.js";
12
+ import { extractLocaleFromUrl, normalizeDefaultLocalePathname, shouldRestoreLocaleRootTrailingSlash } from "../server/pages-i18n.js";
13
13
  import { classifyAppRoute, classifyAppRouteHandler, classifyPagesRoute, extractExportConstString, extractMiddlewareMatcherConfig, getAppRouteRenderEntryPath } from "./report.js";
14
14
  import { normalizeStaticPathname, normalizeStaticPathsEntry } from "../routing/route-pattern.js";
15
15
  import { buildPagesDataHref } from "../shims/internal/pages-data-url.js";
@@ -578,7 +578,10 @@ async function resolveAppWarmPaths(options) {
578
578
  function cachePolicyRuleMatchesWarmPath(pathname, rule, config) {
579
579
  const canonicalPathname = normalizePathTrailingSlash(pathname, config.trailingSlash);
580
580
  return [void 0, ...config.i18n?.domains?.map((domain) => domain.domain) ?? []].some((hostname) => {
581
- const matchPathname = normalizeDefaultLocalePathname(canonicalPathname, config.i18n, { hostname });
581
+ const matchPathname = normalizeDefaultLocalePathname(canonicalPathname, config.i18n, {
582
+ hostname,
583
+ restoreRootTrailingSlash: shouldRestoreLocaleRootTrailingSlash(canonicalPathname, config)
584
+ });
582
585
  let sourceMatched = false;
583
586
  matchHeaders(matchPathname, [rule], {
584
587
  cookies: {},
@@ -0,0 +1,23 @@
1
+ //#region src/build/server-actions-encryption-key.d.ts
2
+ type LoadEncryptionKeyOptions = {
3
+ root: string;
4
+ isBuild: boolean;
5
+ /** Defaults to `process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY`. */
6
+ providedKey?: string;
7
+ now?: number;
8
+ /** Defaults to Docker detection; Docker filesystems are likely ephemeral. */
9
+ hasPersistentStorage?: boolean;
10
+ };
11
+ /**
12
+ * Return NEXT_SERVER_ACTIONS_ENCRYPTION_KEY when set, otherwise a generated
13
+ * key that is cached in `.vinext/cache` for 14 days.
14
+ */
15
+ export declare function loadOrGenerateServerActionsEncryptionKey(options: LoadEncryptionKeyOptions): string;
16
+ /**
17
+ * The cached key lives inside the project root, which the dev server serves.
18
+ * Return the `server.fs.deny` entries that keep it private, preserving Vite's
19
+ * defaults and any configured entries. Like `.env` files, it is only exposed
20
+ * when `server.fs.strict` is false, which turns off Vite's file restrictions.
21
+ */
22
+ export declare function getServerActionsKeyCacheFsDeny(configuredDeny: string[] | undefined): string[];
23
+ //#endregion
@@ -0,0 +1,101 @@
1
+ import path from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
+ import fs from "node:fs";
3
+ import { randomBytes } from "node:crypto";
4
+ //#region src/build/server-actions-encryption-key.ts
5
+ /**
6
+ * Ported from Next.js: packages/next/src/server/app-render/encryption-utils-server.ts
7
+ * https://github.com/vercel/next.js/blob/canary/packages/next/src/server/app-render/encryption-utils-server.ts
8
+ *
9
+ * Next salts server-reference IDs with this key, so keeping it stable keeps
10
+ * client chunk names stable across rebuilds of identical source. Next caches
11
+ * a generated key under `<distDir>/cache` for builds and `<distDir>/dev/cache`
12
+ * for dev; vinext uses `<root>/.vinext/cache` and `<root>/.vinext/dev/cache`
13
+ * because `dist/` is wiped by every build.
14
+ */
15
+ const CONFIG_FILE = ".rscinfo";
16
+ const ENCRYPTION_KEY = "encryption.key";
17
+ const ENCRYPTION_EXPIRE_AT = "encryption.expire_at";
18
+ const EXPIRATION = 12096e5;
19
+ const VITE_DEFAULT_FS_DENY = [
20
+ ".env",
21
+ ".env.*",
22
+ "*.{crt,pem,key,p12,pfx,cer,der}",
23
+ ".npmrc",
24
+ ".yarnrc.yml",
25
+ "**/.git/**"
26
+ ];
27
+ const KEY_CACHE_FS_DENY = `**/.vinext/**/${CONFIG_FILE}`;
28
+ let isDockerResult;
29
+ function isDocker() {
30
+ if (isDockerResult === void 0) {
31
+ let hasDockerEnv = false;
32
+ try {
33
+ fs.statSync("/.dockerenv");
34
+ hasDockerEnv = true;
35
+ } catch {}
36
+ let hasDockerCGroup = false;
37
+ if (!hasDockerEnv) try {
38
+ hasDockerCGroup = fs.readFileSync("/proc/self/cgroup", "utf8").includes("docker");
39
+ } catch {}
40
+ isDockerResult = hasDockerEnv || hasDockerCGroup;
41
+ }
42
+ return isDockerResult;
43
+ }
44
+ function readCachedKey(configPath, isBuild, providedKey, now) {
45
+ if (!fs.existsSync(configPath)) return void 0;
46
+ try {
47
+ const config = JSON.parse(fs.readFileSync(configPath, "utf8"));
48
+ if (typeof config !== "object" || config === null) return void 0;
49
+ const cachedKey = config[ENCRYPTION_KEY];
50
+ const expireAt = config[ENCRYPTION_EXPIRE_AT];
51
+ if (typeof cachedKey !== "string" || typeof expireAt !== "number") return void 0;
52
+ if (isBuild && expireAt < now) return void 0;
53
+ if (cachedKey && providedKey && cachedKey !== providedKey) return void 0;
54
+ return cachedKey;
55
+ } catch {
56
+ return;
57
+ }
58
+ }
59
+ /**
60
+ * Return NEXT_SERVER_ACTIONS_ENCRYPTION_KEY when set, otherwise a generated
61
+ * key that is cached in `.vinext/cache` for 14 days.
62
+ */
63
+ function loadOrGenerateServerActionsEncryptionKey(options) {
64
+ const providedKey = "providedKey" in options ? options.providedKey : process.env.NEXT_SERVER_ACTIONS_ENCRYPTION_KEY;
65
+ const generateKey = () => providedKey || randomBytes(32).toString("base64");
66
+ if (!(options.hasPersistentStorage ?? !isDocker())) return generateKey();
67
+ const now = options.now ?? Date.now();
68
+ const cacheDir = options.isBuild ? path.join(options.root, ".vinext", "cache") : path.join(options.root, ".vinext", "dev", "cache");
69
+ const configPath = path.join(cacheDir, CONFIG_FILE);
70
+ const cachedKey = readCachedKey(configPath, options.isBuild, providedKey, now);
71
+ if (cachedKey !== void 0) {
72
+ restrictKeyCacheMode(configPath);
73
+ return cachedKey;
74
+ }
75
+ const key = generateKey();
76
+ try {
77
+ fs.mkdirSync(cacheDir, { recursive: true });
78
+ fs.writeFileSync(configPath, JSON.stringify({
79
+ [ENCRYPTION_KEY]: key,
80
+ [ENCRYPTION_EXPIRE_AT]: now + EXPIRATION
81
+ }), { mode: 384 });
82
+ restrictKeyCacheMode(configPath);
83
+ } catch {}
84
+ return key;
85
+ }
86
+ function restrictKeyCacheMode(configPath) {
87
+ try {
88
+ fs.chmodSync(configPath, 384);
89
+ } catch {}
90
+ }
91
+ /**
92
+ * The cached key lives inside the project root, which the dev server serves.
93
+ * Return the `server.fs.deny` entries that keep it private, preserving Vite's
94
+ * defaults and any configured entries. Like `.env` files, it is only exposed
95
+ * when `server.fs.strict` is false, which turns off Vite's file restrictions.
96
+ */
97
+ function getServerActionsKeyCacheFsDeny(configuredDeny) {
98
+ return configuredDeny ? [KEY_CACHE_FS_DENY] : [...VITE_DEFAULT_FS_DENY, KEY_CACHE_FS_DENY];
99
+ }
100
+ //#endregion
101
+ export { getServerActionsKeyCacheFsDeny, loadOrGenerateServerActionsEncryptionKey };
@@ -2,6 +2,9 @@
2
2
  export type BundleBackfillChunk = {
3
3
  type: "chunk";
4
4
  fileName: string;
5
+ name?: string;
6
+ isEntry?: boolean;
7
+ facadeModuleId?: string | null;
5
8
  imports?: string[];
6
9
  modules?: Record<string, unknown>;
7
10
  viteMetadata?: {
@@ -1,7 +1,13 @@
1
1
  import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
- import { collapseDuplicateBase, manifestFileWithBase } from "../utils/manifest-paths.js";
2
+ import { SSR_MANIFEST_SHARED_CHUNKS_KEY, collapseDuplicateBase, isSharedClientChunkName, manifestFileWithBase } from "../utils/manifest-paths.js";
3
3
  import fs from "node:fs";
4
4
  //#region src/build/ssr-manifest.ts
5
+ const PAGES_CLIENT_ENTRY_ID = "virtual:vinext-client-entry";
6
+ function isPagesClientEntryFacade(facadeModuleId) {
7
+ if (!facadeModuleId) return false;
8
+ const id = toSlash(facadeModuleId.startsWith("\0") ? facadeModuleId.slice(1) : facadeModuleId);
9
+ return id === PAGES_CLIENT_ENTRY_ID || id.endsWith(`/${PAGES_CLIENT_ENTRY_ID}`);
10
+ }
5
11
  /**
6
12
  * Realpath with NATIVE separators (backslashes on Windows). Callers apply
7
13
  * `toSlash` where the result becomes a module id / manifest key.
@@ -50,6 +56,7 @@ function normalizeManifestModuleId(moduleId, root) {
50
56
  }
51
57
  function augmentSsrManifestFromBundle(ssrManifest, bundle, root, base = "/") {
52
58
  const nextManifest = {};
59
+ const sharedChunkFiles = /* @__PURE__ */ new Set();
53
60
  for (const [key, files] of Object.entries(ssrManifest)) {
54
61
  const normalizedKey = normalizeManifestModuleId(key, root);
55
62
  if (!nextManifest[normalizedKey]) nextManifest[normalizedKey] = /* @__PURE__ */ new Set();
@@ -58,6 +65,7 @@ function augmentSsrManifestFromBundle(ssrManifest, bundle, root, base = "/") {
58
65
  for (const item of Object.values(bundle)) {
59
66
  if (item.type !== "chunk") continue;
60
67
  const chunk = item;
68
+ if (chunk.fileName.endsWith(".js") && (isSharedClientChunkName(chunk.name) || chunk.isEntry && isPagesClientEntryFacade(chunk.facadeModuleId))) sharedChunkFiles.add(manifestFileWithBase(chunk.fileName, base));
61
69
  const files = /* @__PURE__ */ new Set();
62
70
  files.add(manifestFileWithBase(chunk.fileName, base));
63
71
  for (const importedFile of chunk.imports ?? []) files.add(manifestFileWithBase(importedFile, base));
@@ -71,6 +79,7 @@ function augmentSsrManifestFromBundle(ssrManifest, bundle, root, base = "/") {
71
79
  for (const file of files) nextManifest[key].add(file);
72
80
  }
73
81
  }
82
+ if (sharedChunkFiles.size > 0) nextManifest[SSR_MANIFEST_SHARED_CHUNKS_KEY] = sharedChunkFiles;
74
83
  return Object.fromEntries(Object.entries(nextManifest).map(([key, files]) => [key, [...files]]));
75
84
  }
76
85
  //#endregion
@@ -0,0 +1,9 @@
1
+ //#region src/client/client-rewrite-params.d.ts
2
+ /**
3
+ * Whether a rewrite to `destination` appends the source `params` to its
4
+ * query. Next.js does that when the destination's path, host and hash use
5
+ * none of them, for each param its query does not already name; the Pages
6
+ * router's sync rewrite path does not, so it defers these rewrites.
7
+ */
8
+ export declare function clientRewriteAppendsParams(destination: string, params: Record<string, string>): boolean;
9
+ //#endregion
@@ -0,0 +1,20 @@
1
+ //#region src/client/client-rewrite-params.ts
2
+ /**
3
+ * Whether a rewrite to `destination` appends the source `params` to its
4
+ * query. Next.js does that when the destination's path, host and hash use
5
+ * none of them, for each param its query does not already name; the Pages
6
+ * router's sync rewrite path does not, so it defers these rewrites.
7
+ */
8
+ function clientRewriteAppendsParams(destination, params) {
9
+ const keys = Object.keys(params).filter((key) => key !== "nextInternalLocale");
10
+ if (keys.length === 0) return false;
11
+ const hashIndex = destination.indexOf("#");
12
+ const beforeHash = hashIndex === -1 ? destination : destination.slice(0, hashIndex);
13
+ const queryIndex = beforeHash.indexOf("?");
14
+ const withoutQuery = (queryIndex === -1 ? beforeHash : beforeHash.slice(0, queryIndex)) + (hashIndex === -1 ? "" : destination.slice(hashIndex));
15
+ if (keys.some((key) => new RegExp(`:${key}(?![A-Za-z0-9_])`).test(withoutQuery))) return false;
16
+ const queryKeys = new Set(queryIndex === -1 ? [] : new URLSearchParams(beforeHash.slice(queryIndex + 1)).keys());
17
+ return keys.some((key) => !queryKeys.has(key));
18
+ }
19
+ //#endregion
20
+ export { clientRewriteAppendsParams };
@@ -26,11 +26,12 @@ function installDevErrorOverlay() {
26
26
  installed = true;
27
27
  window.addEventListener("error", (event) => {
28
28
  const err = event.error;
29
+ if (!err) return;
29
30
  if (isNavigationSignalError(err)) return;
30
31
  if (err instanceof Error) {
31
32
  if (alreadyReported(err)) return;
32
33
  reportDevError(err, { source: "window-error" });
33
- } else if (event.message) reportDevError(new Error(event.message), { source: "window-error" });
34
+ } else reportDevError(event.message ? new Error(event.message) : err, { source: "window-error" });
34
35
  });
35
36
  window.addEventListener("unhandledrejection", (event) => {
36
37
  const reason = event.reason;
@@ -1,4 +1,4 @@
1
- import { RouteManifest } from "../routing/app-route-graph.js";
1
+ import { BrowserRouteManifest, BrowserRouteManifestWire } from "../routing/browser-route-manifest.js";
2
2
  import { AppRouterScrollIntent } from "../shims/app-router-scroll-state.js";
3
3
  //#region src/client/navigation-runtime.d.ts
4
4
  type NavigationRuntimeSnapshot = {
@@ -7,6 +7,8 @@ type NavigationRuntimeSnapshot = {
7
7
  };
8
8
  export type NavigationRuntimeRscChunk = string | [3, string];
9
9
  export type NavigationRuntimeRscBootstrap = {
10
+ /** The server's App Router RSC compatibility ID for this page. */
11
+ compatibilityId?: string;
10
12
  done?: boolean;
11
13
  dynamicStaleTimeSeconds?: number;
12
14
  initialCacheKind?: "dynamic" | "static";
@@ -68,9 +70,15 @@ export type NavigationRuntimeFunctions = {
68
70
  * committing writes its URL first, as Next.js does from an insertion effect.
69
71
  */
70
72
  flushCommittingNavigationUrl?: () => void;
73
+ /**
74
+ * Starts loading the browser RouteManifest chunk, or joins the load in
75
+ * progress. Returns null once the manifest is registered. Created by
76
+ * createNavigationRuntimeRouteManifestLoader.
77
+ */
78
+ loadRouteManifest?: () => Promise<boolean> | null;
71
79
  };
72
80
  export type NavigationRuntimeBootstrap = {
73
- routeManifest: RouteManifest | null;
81
+ routeManifest: BrowserRouteManifest | null;
74
82
  rsc: NavigationRuntimeRscBootstrap | undefined;
75
83
  };
76
84
  export type NavigationRuntime = {
@@ -82,6 +90,21 @@ export declare function getNavigationRuntime(): NavigationRuntime | null;
82
90
  export declare function registerNavigationRuntimeBootstrap(bootstrap: Partial<NavigationRuntimeBootstrap>): NavigationRuntime;
83
91
  export declare function registerNavigationRuntimeFunctions(functions: Partial<NavigationRuntimeFunctions>): NavigationRuntime;
84
92
  export declare function ensureNavigationRuntimeRscBootstrap(): NavigationRuntimeRscBootstrap;
93
+ /**
94
+ * The browser entry does not inline the RouteManifest: the manifest grows with
95
+ * every route of the app, so it lives in its own chunk that loads alongside
96
+ * hydration. The navigation planner needs it to classify commits (root layout
97
+ * changes, intercepted payloads, preserved slots), so navigations, commits and
98
+ * prefetches that read it wait for this load first.
99
+ */
100
+ export declare function createNavigationRuntimeRouteManifestLoader(importManifest: () => Promise<{
101
+ default: BrowserRouteManifestWire | null;
102
+ }>): () => Promise<boolean> | null;
103
+ /**
104
+ * Starts or joins the RouteManifest load. Resolves whether it loaded, and
105
+ * returns null once it has (or when no App Router runtime registered a loader).
106
+ */
107
+ export declare function loadNavigationRuntimeRouteManifest(): Promise<boolean> | null;
85
108
  export declare function hasAppNavigationRuntime(): boolean;
86
109
  /**
87
110
  * True when the App Router has installed its runtime bootstrap on `window`,
@@ -1,20 +1,15 @@
1
1
  import { isUnknownRecord } from "../utils/record.js";
2
+ import { decodeBrowserRouteManifest } from "../routing/browser-route-manifest.js";
2
3
  import { NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION } from "./browser-globals.js";
3
4
  //#region src/client/navigation-runtime.ts
4
5
  const NAVIGATION_RUNTIME_KEY = Symbol.for(NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION);
5
6
  const ROUTE_MANIFEST_SEGMENT_GRAPH_MAP_KEYS = [
6
- "boundaries",
7
- "defaults",
8
7
  "interceptions",
9
8
  "interceptionsBySlotId",
10
9
  "layouts",
11
- "pages",
12
10
  "rootBoundaries",
13
- "routeHandlers",
14
11
  "routes",
15
- "slotBindings",
16
- "slots",
17
- "templates"
12
+ "slotBindings"
18
13
  ];
19
14
  function createNavigationRuntime() {
20
15
  return {
@@ -31,7 +26,7 @@ function readRuntimeWindow() {
31
26
  }
32
27
  function isNavigationRuntimeFunctions(value) {
33
28
  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")) && isOptionalRuntimeFunction(Reflect.get(value, "discardPendingNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "flushCommittingNavigationUrl"));
29
+ 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")) && isOptionalRuntimeFunction(Reflect.get(value, "loadRouteManifest"));
35
30
  }
36
31
  function isNavigationRuntimeRscChunk(value) {
37
32
  if (typeof value === "string") return true;
@@ -49,6 +44,7 @@ function isNavigationRuntimeParams(value) {
49
44
  }
50
45
  function isNavigationRuntimeRscBootstrap(value) {
51
46
  if (!isUnknownRecord(value)) return false;
47
+ const compatibilityId = Reflect.get(value, "compatibilityId");
52
48
  const done = Reflect.get(value, "done");
53
49
  const dynamicStaleTimeSeconds = Reflect.get(value, "dynamicStaleTimeSeconds");
54
50
  const initialCacheKind = Reflect.get(value, "initialCacheKind");
@@ -58,7 +54,7 @@ function isNavigationRuntimeRscBootstrap(value) {
58
54
  const renderedSearch = Reflect.get(value, "renderedSearch");
59
55
  const rsc = Reflect.get(value, "rsc");
60
56
  const staleTimeSeconds = Reflect.get(value, "staleTimeSeconds");
61
- return (done === void 0 || typeof done === "boolean") && isOptionalStaleTimeSeconds(dynamicStaleTimeSeconds) && (initialCacheKind === void 0 || initialCacheKind === "dynamic" || initialCacheKind === "static") && (searchParamsFromBrowser === void 0 || typeof searchParamsFromBrowser === "boolean") && (nav === void 0 || isNavigationRuntimeSnapshot(nav)) && (params === void 0 || isNavigationRuntimeParams(params)) && (renderedSearch === void 0 || typeof renderedSearch === "string") && Array.isArray(rsc) && rsc.every(isNavigationRuntimeRscChunk) && isOptionalStaleTimeSeconds(staleTimeSeconds);
57
+ return (compatibilityId === void 0 || typeof compatibilityId === "string") && (done === void 0 || typeof done === "boolean") && isOptionalStaleTimeSeconds(dynamicStaleTimeSeconds) && (initialCacheKind === void 0 || initialCacheKind === "dynamic" || initialCacheKind === "static") && (searchParamsFromBrowser === void 0 || typeof searchParamsFromBrowser === "boolean") && (nav === void 0 || isNavigationRuntimeSnapshot(nav)) && (params === void 0 || isNavigationRuntimeParams(params)) && (renderedSearch === void 0 || typeof renderedSearch === "string") && Array.isArray(rsc) && rsc.every(isNavigationRuntimeRscChunk) && isOptionalStaleTimeSeconds(staleTimeSeconds);
62
58
  }
63
59
  function isOptionalStaleTimeSeconds(value) {
64
60
  return value === void 0 || typeof value === "number" && Number.isFinite(value) && value >= 0;
@@ -71,7 +67,7 @@ function isNullableString(value) {
71
67
  }
72
68
  function isNavigationRuntimeInterception(value) {
73
69
  if (!isUnknownRecord(value)) return false;
74
- return typeof value.id === "string" && typeof value.sourcePattern === "string" && isReadonlyStringArray(value.sourcePatternParts) && typeof value.targetPattern === "string" && isReadonlyStringArray(value.targetPatternParts) && typeof value.slotId === "string" && isNullableString(value.ownerLayoutId) && isNullableString(value.interceptingRouteId) && isNullableString(value.targetRouteId);
70
+ return typeof value.id === "string" && isReadonlyStringArray(value.sourcePatternParts) && isReadonlyStringArray(value.targetPatternParts) && typeof value.slotId === "string" && isNullableString(value.ownerLayoutId) && isNullableString(value.targetRouteId);
75
71
  }
76
72
  function isNavigationRuntimeInterceptionArray(value) {
77
73
  return Array.isArray(value) && value.every(isNavigationRuntimeInterception);
@@ -150,6 +146,41 @@ function ensureNavigationRuntimeRscBootstrapForRuntime(runtime) {
150
146
  }
151
147
  return rscBootstrap;
152
148
  }
149
+ /**
150
+ * The browser entry does not inline the RouteManifest: the manifest grows with
151
+ * every route of the app, so it lives in its own chunk that loads alongside
152
+ * hydration. The navigation planner needs it to classify commits (root layout
153
+ * changes, intercepted payloads, preserved slots), so navigations, commits and
154
+ * prefetches that read it wait for this load first.
155
+ */
156
+ function createNavigationRuntimeRouteManifestLoader(importManifest) {
157
+ let loaded = false;
158
+ let reportedFailure = false;
159
+ let pending = null;
160
+ return () => {
161
+ if (loaded) return null;
162
+ pending ??= importManifest().then(({ default: wire }) => {
163
+ registerNavigationRuntimeBootstrap({ routeManifest: wire === null ? null : decodeBrowserRouteManifest(wire) });
164
+ loaded = true;
165
+ return true;
166
+ }).catch((error) => {
167
+ pending = null;
168
+ if (!reportedFailure) {
169
+ reportedFailure = true;
170
+ console.error("[vinext] Failed to load the App Router route manifest:", error);
171
+ }
172
+ return false;
173
+ });
174
+ return pending;
175
+ };
176
+ }
177
+ /**
178
+ * Starts or joins the RouteManifest load. Resolves whether it loaded, and
179
+ * returns null once it has (or when no App Router runtime registered a loader).
180
+ */
181
+ function loadNavigationRuntimeRouteManifest() {
182
+ return getNavigationRuntime()?.functions.loadRouteManifest?.() ?? null;
183
+ }
153
184
  function hasAppNavigationRuntime() {
154
185
  return typeof getNavigationRuntime()?.functions.navigate === "function";
155
186
  }
@@ -169,4 +200,4 @@ function hasAppNavigationRuntimeBootstrap() {
169
200
  return getNavigationRuntime() !== null;
170
201
  }
171
202
  //#endregion
172
- export { NAVIGATION_RUNTIME_KEY, ensureNavigationRuntimeRscBootstrap, getNavigationRuntime, hasAppNavigationRuntime, hasAppNavigationRuntimeBootstrap, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions };
203
+ export { NAVIGATION_RUNTIME_KEY, createNavigationRuntimeRouteManifestLoader, ensureNavigationRuntimeRscBootstrap, getNavigationRuntime, hasAppNavigationRuntime, hasAppNavigationRuntimeBootstrap, loadNavigationRuntimeRouteManifest, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions };
@@ -21,7 +21,6 @@ export type VinextLinkPrefetchRoute = {
21
21
  */
22
22
  export type VinextPagesLinkPrefetchRoute = {
23
23
  canPrefetchLoadingShell: false;
24
- documentOnly?: boolean;
25
24
  isDynamic: boolean;
26
25
  patternParts: string[];
27
26
  requiresDynamicNavigationRequest?: boolean;
@@ -2,34 +2,8 @@ import { HasCondition, NextHeader, NextI18nConfig, NextRedirect, NextRewrite } f
2
2
  import { RequestContext, normalizeHost, parseCookies, requestContextFromRequest } from "./request-context.js";
3
3
  import { RedirectDestinationQueryPart } from "./destination-params.js";
4
4
  import { isExternalUrl } from "../utils/external-url.js";
5
+ import { configSourceError, matchConfigPattern } from "./config-source.js";
5
6
  //#region src/config/config-matchers.d.ts
6
- /**
7
- * Detect regex patterns vulnerable to catastrophic backtracking (ReDoS).
8
- *
9
- * Uses the same deterministic structural analysis as middleware matcher
10
- * validation. Nested bounded repetition is accepted only when its repeated
11
- * language has fixed width and unambiguous branches; a fixed outer count can
12
- * otherwise still cause polynomially catastrophic backtracking on long near
13
- * misses.
14
- *
15
- * Returns true if the pattern appears safe, false if it's potentially dangerous.
16
- */
17
- export declare function isSafeRegex(pattern: string, flags?: string): boolean;
18
- /**
19
- * Compile a regex pattern safely. Returns the compiled RegExp or null if the
20
- * pattern is invalid or vulnerable to ReDoS.
21
- *
22
- * Logs a warning when a pattern is rejected so developers can fix their config.
23
- */
24
- export declare function safeRegExp(pattern: string, flags?: string): RegExp | null;
25
- /**
26
- * Convert a Next.js header/rewrite/redirect source pattern into a regex string.
27
- *
28
- * Regex groups in the source (e.g. `(\d+)`) are extracted first, the remaining
29
- * text is escaped/converted in a **single pass** (avoiding chained `.replace()`
30
- * which CodeQL flags as incomplete sanitization), then groups are restored.
31
- */
32
- export declare function escapeHeaderSource(source: string): string;
33
7
  /**
34
8
  * basePath gating state passed alongside the pathname to every matcher.
35
9
  *
@@ -75,7 +49,6 @@ export declare function applyMiddlewareRequestHeaders(middlewareHeaders: Record<
75
49
  postMwReqCtx: RequestContext;
76
50
  };
77
51
  export declare function checkHasConditions(has: HasCondition[] | undefined, missing: HasCondition[] | undefined, ctx: RequestContext): boolean;
78
- export declare function matchConfigPattern(pathname: string, pattern: string): Record<string, string> | null;
79
52
  /**
80
53
  * A matched config redirect. `destinationQuery` holds the destination query's
81
54
  * parts, split before params were substituted, for merging the request query.
@@ -220,17 +193,19 @@ export declare function matchHeaders(pathname: string, headers: NextHeader[], ct
220
193
  * starts with `/:nextInternalLocale(en|sv|nl)` so that locale-prefixed
221
194
  * URLs match. For redirects only, a second variant prefixed with
222
195
  * `/${defaultLocale}` is also emitted, matching Next.js exactly.
223
- * - **Vinext divergence**: we ALSO retain the original (unprefixed) source
224
- * so that requests for the default locale that arrive without a prefix
225
- * still match. Next.js solves this upstream by path-normalising every
226
- * incoming default-locale request to include the prefix
227
- * (`resolve-routes.ts` lines ~251-263); vinext currently does that
228
- * normalisation only inside the pages-server-entry route matcher, so
229
- * the rewrite/redirect matcher would otherwise miss unprefixed paths.
230
- * Keeping the unprefixed variant gives functionally identical behaviour
231
- * without requiring a server-wide path normalisation pass. The original
232
- * source is appended LAST so the locale-aware variants win when both
233
- * forms could match.
196
+ * - Headers get only that variant, exactly like Next.js. Every header
197
+ * matcher caller splices the (domain-aware) default locale into
198
+ * unprefixed request paths first (`normalizeDefaultLocalePathname`,
199
+ * mirroring `resolve-routes.ts`), so `/about` is matched as `/en/about`.
200
+ * `/_next/*` paths are left unprefixed and therefore only match
201
+ * `locale: false` rules, as in Next.js.
202
+ * - **Vinext divergence (redirects/rewrites only)**: we ALSO retain the
203
+ * original (unprefixed) source, appended LAST so the locale-aware
204
+ * variants win when both forms could match. Their callers normalise
205
+ * default-locale paths too, so this is a legacy fallback. Headers never
206
+ * get it: it would apply rules Next.js skips (`/((?!api).*)` would match
207
+ * `/fr/api/x` as `fr/api/x`), and default-locale requests would match
208
+ * both variants, appending additive headers such as `Set-Cookie` twice.
234
209
  *
235
210
  * Destinations that are local (start with `/`) are similarly rewritten with
236
211
  * `/:nextInternalLocale` for the locale-capture variant so the locale
@@ -243,4 +218,4 @@ export declare function applyLocaleToRoutes<T extends NextRedirect | NextRewrite
243
218
  trailingSlash?: boolean;
244
219
  }): T[];
245
220
  //#endregion
246
- export { type RequestContext, isExternalUrl, normalizeHost, parseCookies, requestContextFromRequest };
221
+ export { type RequestContext, configSourceError, isExternalUrl, matchConfigPattern, normalizeHost, parseCookies, requestContextFromRequest };