vinext 1.0.0-beta.1 → 1.0.0-beta.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (234) hide show
  1. package/README.md +10 -10
  2. package/dist/build/client-build-config.js +8 -1
  3. package/dist/build/report.d.ts +8 -1
  4. package/dist/build/report.js +12 -2
  5. package/dist/check.js +19 -2
  6. package/dist/cli.js +3 -2
  7. package/dist/config/config-matchers.d.ts +13 -37
  8. package/dist/config/config-matchers.js +35 -122
  9. package/dist/config/next-config.d.ts +15 -5
  10. package/dist/config/next-config.js +7 -1
  11. package/dist/config/request-context.d.ts +14 -0
  12. package/dist/config/request-context.js +26 -0
  13. package/dist/entries/app-browser-entry.d.ts +4 -10
  14. package/dist/entries/app-browser-entry.js +20 -4
  15. package/dist/entries/app-rsc-entry.js +30 -9
  16. package/dist/entries/app-rsc-manifest.js +36 -0
  17. package/dist/entries/pages-client-entry.js +0 -1
  18. package/dist/entries/pages-server-entry.js +13 -5
  19. package/dist/index.js +194 -101
  20. package/dist/init-cloudflare.d.ts +3 -1
  21. package/dist/init-cloudflare.js +59 -5
  22. package/dist/init.d.ts +6 -4
  23. package/dist/init.js +20 -22
  24. package/dist/plugins/extensionless-dynamic-import.js +23 -18
  25. package/dist/plugins/ignore-dynamic-requests.js +3 -1
  26. package/dist/plugins/import-meta-url.js +17 -6
  27. package/dist/plugins/og-assets.js +1 -1
  28. package/dist/plugins/require-context.js +20 -15
  29. package/dist/plugins/transform-cache.d.ts +19 -0
  30. package/dist/plugins/transform-cache.js +36 -0
  31. package/dist/routing/app-route-graph.d.ts +27 -11
  32. package/dist/routing/app-route-graph.js +158 -22
  33. package/dist/routing/app-router.js +12 -6
  34. package/dist/routing/file-matcher.d.ts +11 -22
  35. package/dist/routing/file-matcher.js +21 -33
  36. package/dist/routing/pages-router.js +23 -29
  37. package/dist/routing/route-matching.d.ts +14 -1
  38. package/dist/routing/route-matching.js +13 -1
  39. package/dist/routing/route-pattern.d.ts +2 -1
  40. package/dist/routing/route-pattern.js +7 -4
  41. package/dist/routing/route-trie.d.ts +5 -1
  42. package/dist/routing/route-trie.js +5 -2
  43. package/dist/server/api-handler.d.ts +2 -0
  44. package/dist/server/api-handler.js +3 -3
  45. package/dist/server/app-browser-entry.js +110 -37
  46. package/dist/server/app-browser-history-controller.d.ts +1 -1
  47. package/dist/server/app-browser-history-controller.js +3 -3
  48. package/dist/server/app-browser-navigation-controller.d.ts +23 -21
  49. package/dist/server/app-browser-navigation-controller.js +2 -2
  50. package/dist/server/app-browser-state.js +1 -1
  51. package/dist/server/app-browser-visible-commit.js +1 -1
  52. package/dist/server/app-fallback-renderer.d.ts +3 -0
  53. package/dist/server/app-fallback-renderer.js +4 -2
  54. package/dist/server/app-middleware.js +2 -1
  55. package/dist/server/app-page-boundary-render.d.ts +12 -1
  56. package/dist/server/app-page-boundary-render.js +84 -2
  57. package/dist/server/app-page-cache-finalizer.d.ts +1 -1
  58. package/dist/server/app-page-cache-finalizer.js +1 -1
  59. package/dist/server/app-page-cache.d.ts +2 -2
  60. package/dist/server/app-page-cache.js +16 -2
  61. package/dist/server/app-page-dispatch.d.ts +19 -2
  62. package/dist/server/app-page-dispatch.js +45 -23
  63. package/dist/server/app-page-element-builder.d.ts +9 -2
  64. package/dist/server/app-page-element-builder.js +144 -33
  65. package/dist/server/app-page-head.d.ts +56 -4
  66. package/dist/server/app-page-head.js +216 -85
  67. package/dist/server/app-page-http-access-fallback-metadata.d.ts +29 -0
  68. package/dist/server/app-page-http-access-fallback-metadata.js +114 -0
  69. package/dist/server/app-page-probe.d.ts +4 -0
  70. package/dist/server/app-page-probe.js +6 -2
  71. package/dist/server/app-page-render.d.ts +2 -2
  72. package/dist/server/app-page-render.js +1 -1
  73. package/dist/server/app-page-request.d.ts +15 -0
  74. package/dist/server/app-page-request.js +134 -14
  75. package/dist/server/app-page-response.js +1 -1
  76. package/dist/server/app-page-route-wiring.d.ts +16 -0
  77. package/dist/server/app-page-route-wiring.js +254 -52
  78. package/dist/server/app-pages-bridge.d.ts +1 -1
  79. package/dist/server/app-pages-bridge.js +2 -1
  80. package/dist/server/app-post-middleware-context.d.ts +1 -1
  81. package/dist/server/app-post-middleware-context.js +1 -1
  82. package/dist/server/app-route-handler-cache.js +4 -0
  83. package/dist/server/app-route-handler-dispatch.js +40 -20
  84. package/dist/server/app-route-handler-execution.d.ts +6 -1
  85. package/dist/server/app-route-handler-execution.js +42 -16
  86. package/dist/server/app-route-handler-policy.d.ts +2 -0
  87. package/dist/server/app-route-handler-policy.js +3 -3
  88. package/dist/server/app-route-handler-response.js +1 -1
  89. package/dist/server/app-route-module-loader.d.ts +9 -1
  90. package/dist/server/app-route-module-loader.js +4 -0
  91. package/dist/server/app-router-entry.js +6 -3
  92. package/dist/server/app-rsc-cache-busting.d.ts +1 -1
  93. package/dist/server/app-rsc-errors.d.ts +1 -1
  94. package/dist/server/app-rsc-errors.js +22 -2
  95. package/dist/server/app-rsc-handler.d.ts +10 -5
  96. package/dist/server/app-rsc-handler.js +73 -33
  97. package/dist/server/app-rsc-request-normalization.d.ts +3 -2
  98. package/dist/server/app-rsc-request-normalization.js +8 -2
  99. package/dist/server/app-rsc-response-finalizer.d.ts +2 -2
  100. package/dist/server/app-rsc-response-finalizer.js +5 -11
  101. package/dist/server/app-rsc-route-matching.d.ts +29 -0
  102. package/dist/server/app-rsc-route-matching.js +93 -11
  103. package/dist/server/app-segment-config.js +13 -3
  104. package/dist/server/app-server-action-execution.d.ts +3 -1
  105. package/dist/server/app-server-action-execution.js +9 -7
  106. package/dist/server/app-ssr-entry.js +6 -6
  107. package/dist/server/app-ssr-stream.d.ts +1 -7
  108. package/dist/server/app-ssr-stream.js +2 -10
  109. package/dist/server/cache-control.d.ts +2 -2
  110. package/dist/server/cache-control.js +2 -1
  111. package/dist/server/config-headers.d.ts +24 -0
  112. package/dist/server/config-headers.js +52 -0
  113. package/dist/server/dev-initial-server-error.d.ts +1 -1
  114. package/dist/server/dev-server.d.ts +3 -1
  115. package/dist/server/dev-server.js +170 -321
  116. package/dist/server/headers.d.ts +2 -2
  117. package/dist/server/headers.js +4 -3
  118. package/dist/server/isr-cache.d.ts +11 -34
  119. package/dist/server/isr-cache.js +33 -43
  120. package/dist/server/isr-decision.d.ts +2 -2
  121. package/dist/server/middleware-matcher-pattern.d.ts +22 -0
  122. package/dist/server/middleware-matcher-pattern.js +219 -0
  123. package/dist/server/middleware-matcher.d.ts +2 -7
  124. package/dist/server/middleware-matcher.js +13 -60
  125. package/dist/server/middleware-path-to-regexp.d.ts +14 -0
  126. package/dist/server/middleware-path-to-regexp.js +228 -0
  127. package/dist/server/middleware-runtime.js +4 -3
  128. package/dist/server/middleware.d.ts +1 -1
  129. package/dist/server/middleware.js +2 -1
  130. package/dist/server/pages-api-route.d.ts +2 -0
  131. package/dist/server/pages-api-route.js +4 -1
  132. package/dist/server/pages-data-route.d.ts +11 -1
  133. package/dist/server/pages-data-route.js +16 -1
  134. package/dist/server/pages-dev-hydration.js +0 -1
  135. package/dist/server/pages-get-initial-props.d.ts +16 -3
  136. package/dist/server/pages-get-initial-props.js +17 -10
  137. package/dist/server/pages-i18n.js +64 -16
  138. package/dist/server/pages-node-compat.d.ts +2 -0
  139. package/dist/server/pages-node-compat.js +7 -3
  140. package/dist/server/pages-page-data.d.ts +64 -10
  141. package/dist/server/pages-page-data.js +349 -80
  142. package/dist/server/pages-page-handler.d.ts +9 -3
  143. package/dist/server/pages-page-handler.js +131 -36
  144. package/dist/server/pages-page-response.d.ts +5 -3
  145. package/dist/server/pages-page-response.js +24 -27
  146. package/dist/server/pages-readiness.d.ts +3 -3
  147. package/dist/server/pages-request-pipeline.d.ts +8 -0
  148. package/dist/server/pages-request-pipeline.js +30 -12
  149. package/dist/server/pages-revalidate.d.ts +1 -1
  150. package/dist/server/pages-revalidate.js +54 -8
  151. package/dist/server/pages-router-entry.d.ts +1 -0
  152. package/dist/server/pages-router-entry.js +16 -4
  153. package/dist/server/prerender-route-params.js +2 -2
  154. package/dist/server/prod-server.d.ts +3 -5
  155. package/dist/server/prod-server.js +72 -35
  156. package/dist/server/request-pipeline.d.ts +15 -29
  157. package/dist/server/request-pipeline.js +24 -59
  158. package/dist/server/revalidation-host.d.ts +11 -0
  159. package/dist/server/revalidation-host.js +19 -0
  160. package/dist/server/rsc-stream-hints.js +122 -18
  161. package/dist/server/worker-revalidation-context.d.ts +13 -0
  162. package/dist/server/worker-revalidation-context.js +29 -0
  163. package/dist/shims/app.d.ts +1 -1
  164. package/dist/shims/cache-handler.d.ts +1 -1
  165. package/dist/shims/cache-handler.js +10 -7
  166. package/dist/shims/cache-request-state.d.ts +27 -1
  167. package/dist/shims/cache-request-state.js +60 -1
  168. package/dist/shims/cache-runtime.d.ts +18 -1
  169. package/dist/shims/cache-runtime.js +26 -8
  170. package/dist/shims/cache.d.ts +5 -5
  171. package/dist/shims/cache.js +27 -11
  172. package/dist/shims/document.d.ts +42 -83
  173. package/dist/shims/document.js +62 -53
  174. package/dist/shims/dynamic.d.ts +5 -25
  175. package/dist/shims/dynamic.js +29 -8
  176. package/dist/shims/error.d.ts +1 -1
  177. package/dist/shims/error.js +2 -2
  178. package/dist/shims/fetch-cache.js +13 -4
  179. package/dist/shims/font-google-base.d.ts +11 -4
  180. package/dist/shims/font-google-base.js +17 -8
  181. package/dist/shims/font-local.d.ts +10 -3
  182. package/dist/shims/font-local.js +41 -10
  183. package/dist/shims/head.d.ts +13 -2
  184. package/dist/shims/head.js +3 -3
  185. package/dist/shims/headers.d.ts +37 -43
  186. package/dist/shims/headers.js +183 -74
  187. package/dist/shims/image.d.ts +27 -37
  188. package/dist/shims/image.js +21 -14
  189. package/dist/shims/internal/cookie-serialize.d.ts +12 -9
  190. package/dist/shims/internal/cookie-serialize.js +10 -7
  191. package/dist/shims/internal/hybrid-client-route-owner.js +3 -1
  192. package/dist/shims/internal/interpolate-as.d.ts +15 -1
  193. package/dist/shims/internal/interpolate-as.js +27 -2
  194. package/dist/shims/internal/utils.d.ts +3 -3
  195. package/dist/shims/legacy-image.d.ts +3 -37
  196. package/dist/shims/legacy-image.js +6 -16
  197. package/dist/shims/link.d.ts +15 -22
  198. package/dist/shims/link.js +34 -13
  199. package/dist/shims/metadata.d.ts +7 -17
  200. package/dist/shims/metadata.js +74 -24
  201. package/dist/shims/navigation-context-state.d.ts +1 -1
  202. package/dist/shims/navigation.d.ts +7 -3
  203. package/dist/shims/navigation.js +25 -14
  204. package/dist/shims/navigation.react-server.js +1 -1
  205. package/dist/shims/next-shims-augmentations.d.ts +1 -0
  206. package/dist/shims/next-shims-public.d.ts +1 -0
  207. package/dist/shims/public-shim-map.json.js +103 -0
  208. package/dist/shims/readonly-url-search-params.d.ts +3 -3
  209. package/dist/shims/readonly-url-search-params.js +3 -3
  210. package/dist/shims/request-context.d.ts +4 -1
  211. package/dist/shims/request-state-types.d.ts +1 -1
  212. package/dist/shims/router.d.ts +55 -21
  213. package/dist/shims/router.js +180 -44
  214. package/dist/shims/script.d.ts +6 -10
  215. package/dist/shims/script.js +7 -4
  216. package/dist/shims/server.d.ts +50 -26
  217. package/dist/shims/server.js +193 -55
  218. package/dist/shims/unified-request-context.d.ts +22 -3
  219. package/dist/shims/unified-request-context.js +84 -1
  220. package/dist/shims/url-safety.d.ts +1 -1
  221. package/dist/shims/url-safety.js +2 -2
  222. package/dist/typegen.d.ts +8 -2
  223. package/dist/typegen.js +76 -15
  224. package/dist/utils/cache-control-metadata.d.ts +2 -1
  225. package/dist/utils/cache-control-metadata.js +5 -1
  226. package/dist/utils/domain-locale.d.ts +2 -2
  227. package/dist/utils/external-url.d.ts +5 -0
  228. package/dist/utils/external-url.js +7 -0
  229. package/dist/utils/html-limited-bots.js +1 -1
  230. package/dist/utils/protocol-headers.d.ts +7 -1
  231. package/dist/utils/protocol-headers.js +7 -1
  232. package/dist/utils/regex-safety.d.ts +8 -0
  233. package/dist/utils/regex-safety.js +737 -0
  234. package/package.json +13 -2
package/README.md CHANGED
@@ -206,7 +206,10 @@ vinext works everywhere. It natively supports Cloudflare Workers (with `npx @vin
206
206
  vinext is a Vite plugin that reimplements the public Next.js API — routing, server rendering, `next/*` module imports, the CLI — so you can run Next.js applications on Vite instead of the Next.js compiler toolchain. It can be deployed anywhere: Cloudflare Workers is the first natively supported target, with other platforms available via Nitro. Native adapters for more platforms are [planned](https://github.com/cloudflare/vinext/issues/80).
207
207
 
208
208
  **Is this a fork of Next.js?**
209
- No. vinext is an alternative implementation of the Next.js API surface built on Vite. It does import some Next.js types and utilities, but the core is written from scratch. The goal is not to create a competing framework or add features beyond what Next.js offers; it is to provide the same well-defined API surface on Vite's toolchain.
209
+ No. vinext is an alternative implementation of the Next.js API surface built on Vite. The core is written from scratch. The goal is not to create a competing framework or add features beyond what Next.js offers; it is to provide the same well-defined API surface on Vite's toolchain.
210
+
211
+ **Does vinext require Next.js to be installed?**
212
+ No. vinext ships fallback declarations for the supported `next` and `next/*` APIs, so applications can run and type-check without the `next` package. If both packages are installed, vinext keeps using Next.js's authoritative types and adds only its own extensions. Compatibility features that consume Next.js internals, such as `styled-jsx`, may still require a matching Next.js installation when used.
210
213
 
211
214
  **How is this different from OpenNext?**
212
215
  [OpenNext](https://opennext.js.org/) adapts the _output_ of a standard `next build` to run on various platforms. Because it builds on Next.js's own output, it inherits broad API coverage and has been well-tested for much longer. vinext takes a different approach: it reimplements the Next.js APIs on Vite from scratch, which means faster builds and smaller bundles, but less coverage of the long tail of Next.js features. If you need a mature, well-tested way to run Next.js outside Vercel, OpenNext is the safer choice. If you want a lighter Vite-based toolchain and do not need every Next.js API, vinext may be a good fit.
@@ -342,24 +345,16 @@ Requires a custom domain (zone analytics are unavailable on `*.workers.dev`) and
342
345
 
343
346
  #### Custom Vite configuration
344
347
 
345
- If you need to customize the Vite config, create a `vite.config.ts`. vinext will merge its config with yours. This is required for Cloudflare Workers deployment with the App Router (RSC needs explicit plugin configuration):
348
+ If you need to customize the Vite config, create a `vite.config.ts`. vinext will merge its config with yours. For Cloudflare Workers deployment with the App Router, configure `@cloudflare/vite-plugin` so the RSC environment runs in workerd:
346
349
 
347
350
  ```ts
348
351
  import { defineConfig } from "vite";
349
352
  import vinext from "vinext";
350
- import rsc from "@vitejs/plugin-rsc";
351
353
  import { cloudflare } from "@cloudflare/vite-plugin";
352
354
 
353
355
  export default defineConfig({
354
356
  plugins: [
355
357
  vinext(),
356
- rsc({
357
- entries: {
358
- rsc: "virtual:vinext-rsc-entry",
359
- ssr: "virtual:vinext-app-ssr-entry",
360
- client: "virtual:vinext-app-browser-entry",
361
- },
362
- }),
363
358
  cloudflare({
364
359
  viteEnvironment: { name: "rsc", childEnvironments: ["ssr"] },
365
360
  }),
@@ -367,6 +362,11 @@ export default defineConfig({
367
362
  });
368
363
  ```
369
364
 
365
+ > **Do not register `@vitejs/plugin-rsc` yourself.** It is an optional peer dependency, so it must be
366
+ > _installed_ in your project, but vinext auto-registers it whenever an `app/` directory is detected.
367
+ > Adding an explicit `rsc()` call fails the build with `[vinext] Duplicate @vitejs/plugin-rsc detected`.
368
+ > Pass `rsc: false` to `vinext()` only if you want to own that registration.
369
+
370
370
  See the [examples](#live-examples) for complete working configurations.
371
371
 
372
372
  ### Other platforms (via Nitro)
@@ -6,6 +6,7 @@ const ROUTE_OWNED_CLIENT_SHIMS = /* @__PURE__ */ new Set([
6
6
  "dynamic-preload-chunks",
7
7
  "form",
8
8
  "image",
9
+ "internal/hybrid-client-route-owner",
9
10
  "layout-segment-context",
10
11
  "legacy-image",
11
12
  "link",
@@ -88,7 +89,13 @@ function createClientManualChunks(shimsDir, preserveRouteBoundaries = false) {
88
89
  if (id.includes("node_modules")) {
89
90
  const pkg = getPackageName(id);
90
91
  if (!pkg) return void 0;
91
- if (pkg === "react" || pkg === "react-dom" || pkg === "scheduler") return "framework";
92
+ if (pkg === "react-dom") {
93
+ const slashedId = toSlash(id);
94
+ const sub = slashedId.slice(slashedId.lastIndexOf("react-dom/") + 10);
95
+ if (sub.startsWith("server.") || sub.startsWith("static.") || sub.startsWith("cjs/react-dom-server")) return "react-dom-server";
96
+ return "framework";
97
+ }
98
+ if (pkg === "react" || pkg === "scheduler") return "framework";
92
99
  return;
93
100
  }
94
101
  const slashedId = toSlash(id);
@@ -44,6 +44,13 @@ declare function hasExportedName(code: string, name: string): boolean;
44
44
  */
45
45
  declare function extractExportConstString(code: string, name: string): string | null;
46
46
  declare function extractMiddlewareMatcherConfig(filePath: string): StaticMiddlewareMatcher | undefined;
47
+ /**
48
+ * Extract the statically analyzable `config.matcher` value without first
49
+ * narrowing it to vinext's runtime matcher type. Build validation needs the
50
+ * raw value so malformed matcher objects are rejected instead of disappearing
51
+ * as though no matcher had been configured.
52
+ */
53
+ declare function extractMiddlewareMatcherConfigValue(filePath: string): unknown;
47
54
  /**
48
55
  * Extracts the numeric value of `export const <name> = <number|false>`.
49
56
  * Supports integers, decimals, negative values, `Infinity`, and `false`.
@@ -137,4 +144,4 @@ declare function printBuildReport(options: {
137
144
  prerenderResult?: PrerenderResult;
138
145
  }): Promise<void>;
139
146
  //#endregion
140
- export { RouteRow, RouteType, StaticMiddlewareMatcher, buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
147
+ export { RouteRow, RouteType, StaticMiddlewareMatcher, buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, extractMiddlewareMatcherConfigValue, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
@@ -135,6 +135,16 @@ function extractStringFromConstInitializer(initializer) {
135
135
  return null;
136
136
  }
137
137
  function extractMiddlewareMatcherConfig(filePath) {
138
+ const value = extractMiddlewareMatcherConfigValue(filePath);
139
+ return isStaticMiddlewareMatcher(value) ? value : void 0;
140
+ }
141
+ /**
142
+ * Extract the statically analyzable `config.matcher` value without first
143
+ * narrowing it to vinext's runtime matcher type. Build validation needs the
144
+ * raw value so malformed matcher objects are rejected instead of disappearing
145
+ * as though no matcher had been configured.
146
+ */
147
+ function extractMiddlewareMatcherConfigValue(filePath) {
138
148
  let code;
139
149
  try {
140
150
  code = fs.readFileSync(filePath, "utf8");
@@ -148,7 +158,7 @@ function extractMiddlewareMatcherConfig(filePath) {
148
158
  const matcherExpression = objectPropertyValue(config, "matcher");
149
159
  if (!matcherExpression) return void 0;
150
160
  const value = extractStaticJsonValue(matcherExpression);
151
- return isStaticMiddlewareMatcher(value) ? value : void 0;
161
+ return value === UNSUPPORTED_STATIC_VALUE ? void 0 : value;
152
162
  }
153
163
  function objectPropertyValue(object, key) {
154
164
  for (const property of object.properties) {
@@ -596,4 +606,4 @@ async function printBuildReport(options) {
596
606
  }
597
607
  }
598
608
  //#endregion
599
- export { buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
609
+ export { buildReportRows, classifyAppRoute, classifyLayoutSegmentConfig, classifyPagesRoute, extractExportConstNumber, extractExportConstString, extractGetStaticPropsRevalidate, extractMiddlewareMatcherConfig, extractMiddlewareMatcherConfigValue, formatBuildReport, getAppRouteRenderEntryPath, hasExportedName, hasNamedExport, printBuildReport };
package/dist/check.js CHANGED
@@ -230,6 +230,10 @@ const CONFIG_SUPPORT = {
230
230
  status: "supported",
231
231
  detail: "server actions via 'use server' directive"
232
232
  },
233
+ "experimental.allowedRevalidateHeaderKeys": {
234
+ status: "supported",
235
+ detail: "forwards explicitly allowed request headers during Pages Router revalidation"
236
+ },
233
237
  "experimental.prefetchInlining": {
234
238
  status: "partial",
235
239
  detail: "config recognized; Link prefetch preserves pending/dedup semantics, but vinext does not implement per-segment cache storage"
@@ -390,6 +394,19 @@ function findSourceFiles(dir, extensions = [
390
394
  }
391
395
  return results;
392
396
  }
397
+ /**
398
+ * Find files that can contribute to the application compatibility surface.
399
+ * Test modules and test-runner configuration are executed by their own runners
400
+ * rather than bundled into the vinext application, so reporting their imports
401
+ * or CJS globals as migration blockers produces false positives.
402
+ */
403
+ function findRuntimeSourceFiles(root) {
404
+ return findSourceFiles(root).filter((file) => {
405
+ const basename = path.basename(file);
406
+ const isTestRunnerConfig = /^(?:jest|playwright|vitest)\.config\.[cm]?[jt]sx?$/.test(basename);
407
+ return !/\.(?:test|spec)\.[cm]?[jt]sx?$/.test(basename) && !isTestRunnerConfig;
408
+ });
409
+ }
393
410
  function isIdentStart(c) {
394
411
  return c >= "a" && c <= "z" || c >= "A" && c <= "Z" || c === "_" || c === "$";
395
412
  }
@@ -588,7 +605,7 @@ function hasFreeCjsGlobal(content) {
588
605
  * Scan source files for `import ... from 'next/...'` statements.
589
606
  */
590
607
  function scanImports(root) {
591
- const files = findSourceFiles(root);
608
+ const files = findRuntimeSourceFiles(root);
592
609
  const importUsage = /* @__PURE__ */ new Map();
593
610
  const importRegex = /(?:import\s+(?:[\w{},\s*]+\s+from\s+)?|require\s*\()['"]([^'"]+)['"]\)?/g;
594
611
  const typeOnlyImportRegex = /import\s+type\s+/;
@@ -911,7 +928,7 @@ function checkConventions(root) {
911
928
  detail: "required for Vite — vinext init will add it automatically"
912
929
  });
913
930
  }
914
- const allSourceFiles = findSourceFiles(root);
931
+ const allSourceFiles = findRuntimeSourceFiles(root);
915
932
  const viewTransitionRegex = /import\s+\{[^}]*\bViewTransition\b[^}]*\}\s+from\s+['"]react['"]/;
916
933
  const viewTransitionFiles = [];
917
934
  const cjsGlobalFiles = [];
package/dist/cli.js CHANGED
@@ -482,11 +482,12 @@ async function typegen() {
482
482
  root,
483
483
  mode: "production"
484
484
  });
485
- const outputPath = await generateRouteTypes({
485
+ const result = await generateRouteTypes({
486
486
  root,
487
487
  pageExtensions: (await resolveNextConfig(await loadNextConfig(root, PHASE_PRODUCTION_BUILD), root)).pageExtensions
488
488
  });
489
- console.log(`\n Generated route types at ${path.relative(root, outputPath)}\n`);
489
+ const nextEnvMessage = result.nextEnvStatus === "unchanged" ? `${path.relative(root, result.nextEnvPath)} is up to date` : `${result.nextEnvStatus === "created" ? "Created" : "Updated"} ${path.relative(root, result.nextEnvPath)}`;
490
+ console.log(`\n Generated route types at ${path.relative(root, result.routeTypesPath)}\n ${nextEnvMessage}\n`);
490
491
  }
491
492
  async function initCommand() {
492
493
  const parsed = parseArgs(rawArgs);
@@ -1,17 +1,20 @@
1
1
  import { HasCondition, NextHeader, NextI18nConfig, NextRedirect, NextRewrite } from "./next-config.js";
2
+ import { RequestContext, normalizeHost, parseCookies, requestContextFromRequest } from "./request-context.js";
3
+ import { isExternalUrl } from "../utils/external-url.js";
2
4
 
3
5
  //#region src/config/config-matchers.d.ts
4
6
  /**
5
7
  * Detect regex patterns vulnerable to catastrophic backtracking (ReDoS).
6
8
  *
7
- * Uses a lightweight heuristic: scans the pattern string for nested quantifiers
8
- * (a quantifier applied to a group that itself contains a quantifier). This
9
- * catches the most common pathological patterns like `(a+)+`, `(.*)*`,
10
- * `([^/]+)+`, `(a|a+)+` without needing a full regex parser.
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.
11
14
  *
12
15
  * Returns true if the pattern appears safe, false if it's potentially dangerous.
13
16
  */
14
- declare function isSafeRegex(pattern: string): boolean;
17
+ declare function isSafeRegex(pattern: string, flags?: string): boolean;
15
18
  /**
16
19
  * Compile a regex pattern safely. Returns the compiled RegExp or null if the
17
20
  * pattern is invalid or vulnerable to ReDoS.
@@ -27,16 +30,6 @@ declare function safeRegExp(pattern: string, flags?: string): RegExp | null;
27
30
  * which CodeQL flags as incomplete sanitization), then groups are restored.
28
31
  */
29
32
  declare function escapeHeaderSource(source: string): string;
30
- /**
31
- * Request context needed for evaluating has/missing conditions.
32
- * Callers extract the relevant parts from the incoming Request.
33
- */
34
- type RequestContext = {
35
- readonly headers: Headers;
36
- readonly cookies: Record<string, string>;
37
- readonly query: URLSearchParams;
38
- readonly host: string;
39
- };
40
33
  /**
41
34
  * basePath gating state passed alongside the pathname to every matcher.
42
35
  *
@@ -59,22 +52,6 @@ type BasePathMatchState = {
59
52
  */
60
53
  hadBasePath: boolean;
61
54
  };
62
- /**
63
- * Parse a Cookie header string into a key-value record.
64
- */
65
- declare function parseCookies(cookieHeader: string | null): Record<string, string>;
66
- /**
67
- * Build a RequestContext from a Web Request object.
68
- *
69
- * `cookies` and `query` are lazy memoized getters: they are consumed only by
70
- * `has`/`missing` condition evaluation (`checkHasConditions` /
71
- * `matchesRuleConditions`), and most apps configure no such conditions. The
72
- * cookie split and `searchParams` access are therefore deferred until first
73
- * read and computed at most once. Mirrors `headersContextFromRequest` in
74
- * `shims/headers.ts`.
75
- */
76
- declare function requestContextFromRequest(request: Request): RequestContext;
77
- declare function normalizeHost(hostHeader: string | null, fallbackHostname: string): string;
78
55
  /**
79
56
  * Unpack `x-middleware-request-*` headers from the collected middleware
80
57
  * response headers into the actual request, and strip all `x-middleware-*`
@@ -147,7 +124,7 @@ declare function matchRedirect(pathname: string, redirects: NextRedirect[], ctx:
147
124
  * to evaluate has/missing conditions. Next.js always has request context
148
125
  * when evaluating rewrites, so this parameter is required.
149
126
  */
150
- declare function matchRewrite(pathname: string, rewrites: NextRewrite[], ctx: RequestContext, basePathState?: BasePathMatchState): string | null;
127
+ declare function matchRewrite(pathname: string, rewrites: NextRewrite[], ctx: RequestContext, basePathState?: BasePathMatchState, paramsPathname?: string): string | null;
151
128
  /**
152
129
  * Check whether a rewrite source can match a pathname without evaluating its
153
130
  * request-dependent `has` / `missing` conditions.
@@ -175,7 +152,6 @@ declare function sanitizeDestination(dest: string): string;
175
152
  * Detects any URL scheme (http:, https:, data:, javascript:, blob:, etc.)
176
153
  * per RFC 3986, plus protocol-relative URLs (//).
177
154
  */
178
- declare function isExternalUrl(url: string): boolean;
179
155
  /**
180
156
  * Merge the original request's query params into a config-redirect
181
157
  * destination, preserving them on the resulting `Location`.
@@ -218,8 +194,8 @@ declare function matchHeaders(pathname: string, headers: NextHeader[], ctx: Requ
218
194
  value: string;
219
195
  }>;
220
196
  /**
221
- * Apply Next.js i18n locale-prefix transformation to a set of redirect or
222
- * rewrite rules. Mirrors the relevant slice of Next.js's `processRoutes`
197
+ * Apply Next.js i18n locale-prefix transformation to a set of redirect,
198
+ * rewrite, or header rules. Mirrors the relevant slice of Next.js's `processRoutes`
223
199
  * (load-custom-routes.ts) with one deliberate divergence noted below.
224
200
  *
225
201
  * For each rule:
@@ -250,8 +226,8 @@ declare function matchHeaders(pathname: string, headers: NextHeader[], ctx: Requ
250
226
  * Mirrors the Next.js reference in
251
227
  * packages/next/src/lib/load-custom-routes.ts — see `processRoutes`.
252
228
  */
253
- declare function applyLocaleToRoutes<T extends NextRedirect | NextRewrite>(routes: T[], i18n: NextI18nConfig | null | undefined, type: "redirect" | "rewrite", options?: {
229
+ declare function applyLocaleToRoutes<T extends NextRedirect | NextRewrite | NextHeader>(routes: T[], i18n: NextI18nConfig | null | undefined, type: "redirect" | "rewrite" | "header", options?: {
254
230
  trailingSlash?: boolean;
255
231
  }): T[];
256
232
  //#endregion
257
- export { BasePathMatchState, RequestContext, applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, escapeHeaderSource, isExternalUrl, isSafeRegex, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, matchesRewriteSource, normalizeHost, parseCookies, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, safeRegExp, sanitizeDestination };
233
+ export { BasePathMatchState, type RequestContext, applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, escapeHeaderSource, isExternalUrl, isSafeRegex, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, matchesRewriteSource, normalizeHost, parseCookies, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, safeRegExp, sanitizeDestination };
@@ -1,6 +1,8 @@
1
- import { VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER } from "../utils/protocol-headers.js";
1
+ import { PRERENDER_REVALIDATE_HEADER, PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER, VINEXT_MW_CTX_HEADER, VINEXT_PRERENDER_ROUTE_PARAMS_HEADER, VINEXT_PRERENDER_SECRET_HEADER, VINEXT_REVALIDATE_HOST_HEADER } from "../utils/protocol-headers.js";
2
2
  import { buildRequestHeadersFromMiddlewareResponse } from "../utils/middleware-request-headers.js";
3
- import { parseCookieHeader } from "../utils/parse-cookie.js";
3
+ import { analyzeRegexSafety } from "../utils/regex-safety.js";
4
+ import { normalizeHost, parseCookies, requestContextFromRequest } from "./request-context.js";
5
+ import { isExternalUrl } from "../utils/external-url.js";
4
6
  //#region src/config/config-matchers.ts
5
7
  /**
6
8
  * Cache for compiled regex patterns in matchConfigPattern.
@@ -191,75 +193,16 @@ function stripHopByHopRequestHeaders(headers) {
191
193
  /**
192
194
  * Detect regex patterns vulnerable to catastrophic backtracking (ReDoS).
193
195
  *
194
- * Uses a lightweight heuristic: scans the pattern string for nested quantifiers
195
- * (a quantifier applied to a group that itself contains a quantifier). This
196
- * catches the most common pathological patterns like `(a+)+`, `(.*)*`,
197
- * `([^/]+)+`, `(a|a+)+` without needing a full regex parser.
196
+ * Uses the same deterministic structural analysis as middleware matcher
197
+ * validation. Nested bounded repetition is accepted only when its repeated
198
+ * language has fixed width and unambiguous branches; a fixed outer count can
199
+ * otherwise still cause polynomially catastrophic backtracking on long near
200
+ * misses.
198
201
  *
199
202
  * Returns true if the pattern appears safe, false if it's potentially dangerous.
200
203
  */
201
- function isSafeRegex(pattern) {
202
- const quantifierAtDepth = [];
203
- let depth = 0;
204
- let i = 0;
205
- while (i < pattern.length) {
206
- const ch = pattern[i];
207
- if (ch === "\\") {
208
- i += 2;
209
- continue;
210
- }
211
- if (ch === "[") {
212
- i++;
213
- while (i < pattern.length && pattern[i] !== "]") {
214
- if (pattern[i] === "\\") i++;
215
- i++;
216
- }
217
- i++;
218
- continue;
219
- }
220
- if (ch === "(") {
221
- depth++;
222
- if (quantifierAtDepth.length <= depth) quantifierAtDepth.push(false);
223
- else quantifierAtDepth[depth] = false;
224
- i++;
225
- continue;
226
- }
227
- if (ch === ")") {
228
- const hadQuantifier = depth > 0 && quantifierAtDepth[depth];
229
- if (depth > 0) depth--;
230
- const next = pattern[i + 1];
231
- if (next === "+" || next === "*" || next === "{") {
232
- if (hadQuantifier) return false;
233
- if (depth >= 0 && depth < quantifierAtDepth.length) quantifierAtDepth[depth] = true;
234
- }
235
- i++;
236
- continue;
237
- }
238
- if (ch === "+" || ch === "*") {
239
- if (depth > 0) quantifierAtDepth[depth] = true;
240
- i++;
241
- continue;
242
- }
243
- if (ch === "?") {
244
- const prev = i > 0 ? pattern[i - 1] : "";
245
- if (prev !== "+" && prev !== "*" && prev !== "?" && prev !== "}") {
246
- if (depth > 0) quantifierAtDepth[depth] = true;
247
- }
248
- i++;
249
- continue;
250
- }
251
- if (ch === "{") {
252
- let j = i + 1;
253
- while (j < pattern.length && /[\d,]/.test(pattern[j])) j++;
254
- if (j < pattern.length && pattern[j] === "}" && j > i + 1) {
255
- if (depth > 0) quantifierAtDepth[depth] = true;
256
- i = j + 1;
257
- continue;
258
- }
259
- }
260
- i++;
261
- }
262
- return true;
204
+ function isSafeRegex(pattern, flags) {
205
+ return analyzeRegexSafety(pattern, { ignoreCase: flags?.includes("i") }) === null;
263
206
  }
264
207
  /**
265
208
  * Compile a regex pattern safely. Returns the compiled RegExp or null if the
@@ -268,8 +211,8 @@ function isSafeRegex(pattern) {
268
211
  * Logs a warning when a pattern is rejected so developers can fix their config.
269
212
  */
270
213
  function safeRegExp(pattern, flags) {
271
- if (!isSafeRegex(pattern)) {
272
- console.warn(`[vinext] Rejecting potentially unsafe regex pattern (ReDoS risk): ${pattern}\n Patterns with nested quantifiers (e.g. (a+)+) can cause catastrophic backtracking.\n Simplify the pattern to avoid nested repetition.`);
214
+ if (!isSafeRegex(pattern, flags)) {
215
+ console.warn(`[vinext] Rejecting potentially unsafe regex pattern (ReDoS risk): ${pattern}\n Nested or ambiguous repetition can cause catastrophic backtracking.\n Simplify the pattern to make repeated matches fixed and unambiguous.`);
273
216
  return null;
274
217
  }
275
218
  try {
@@ -342,40 +285,6 @@ function shouldEvaluateRule(ruleBasePath, state) {
342
285
  return ruleBasePath === false ? !state.hadBasePath : state.hadBasePath;
343
286
  }
344
287
  /**
345
- * Parse a Cookie header string into a key-value record.
346
- */
347
- function parseCookies(cookieHeader) {
348
- return parseCookieHeader(cookieHeader);
349
- }
350
- /**
351
- * Build a RequestContext from a Web Request object.
352
- *
353
- * `cookies` and `query` are lazy memoized getters: they are consumed only by
354
- * `has`/`missing` condition evaluation (`checkHasConditions` /
355
- * `matchesRuleConditions`), and most apps configure no such conditions. The
356
- * cookie split and `searchParams` access are therefore deferred until first
357
- * read and computed at most once. Mirrors `headersContextFromRequest` in
358
- * `shims/headers.ts`.
359
- */
360
- function requestContextFromRequest(request) {
361
- const url = new URL(request.url);
362
- let cookies;
363
- let query;
364
- return {
365
- headers: request.headers,
366
- get cookies() {
367
- return cookies ??= parseCookies(request.headers.get("cookie"));
368
- },
369
- get query() {
370
- return query ??= url.searchParams;
371
- },
372
- host: normalizeHost(request.headers.get("host"), url.hostname)
373
- };
374
- }
375
- function normalizeHost(hostHeader, fallbackHostname) {
376
- return (hostHeader ?? fallbackHostname).split(":", 1)[0].toLowerCase();
377
- }
378
- /**
379
288
  * Unpack `x-middleware-request-*` headers from the collected middleware
380
289
  * response headers into the actual request, and strip all `x-middleware-*`
381
290
  * internal signals so they never reach clients.
@@ -410,7 +319,7 @@ function _emptyParams() {
410
319
  return Object.create(null);
411
320
  }
412
321
  function _matchConditionValue(actualValue, expectedValue) {
413
- if (expectedValue === void 0) return _emptyParams();
322
+ if (!expectedValue) return actualValue ? _emptyParams() : null;
414
323
  const re = _cachedConditionRegex(expectedValue);
415
324
  if (re) {
416
325
  const match = re.exec(actualValue);
@@ -440,9 +349,10 @@ function matchSingleCondition(condition, ctx) {
440
349
  return _matchConditionValue(cookieValue, condition.value);
441
350
  }
442
351
  case "query": {
443
- const queryValue = ctx.query.get(condition.key);
444
- if (queryValue === null) return null;
445
- return _matchConditionValue(queryValue, condition.value);
352
+ const queryValues = ctx.query.getAll(condition.key);
353
+ if (queryValues.length === 0) return null;
354
+ if (!condition.value && queryValues.length > 1) return _emptyParams();
355
+ return _matchConditionValue(queryValues[queryValues.length - 1], condition.value);
446
356
  }
447
357
  case "host":
448
358
  if (condition.value !== void 0) return _matchConditionValue(ctx.host, condition.value);
@@ -710,11 +620,12 @@ function matchRedirect(pathname, redirects, ctx, basePathState = _BASEPATH_DEFAU
710
620
  * to evaluate has/missing conditions. Next.js always has request context
711
621
  * when evaluating rewrites, so this parameter is required.
712
622
  */
713
- function matchRewrite(pathname, rewrites, ctx, basePathState = _BASEPATH_DEFAULT) {
623
+ function matchRewrite(pathname, rewrites, ctx, basePathState = _BASEPATH_DEFAULT, paramsPathname = pathname) {
714
624
  for (const rewrite of rewrites) {
715
625
  if (!shouldEvaluateRule(rewrite.basePath, basePathState)) continue;
716
- const params = matchConfigPattern(pathname, rewrite.source);
717
- if (params) {
626
+ const matchedParams = matchConfigPattern(pathname, rewrite.source);
627
+ if (matchedParams) {
628
+ const params = paramsPathname === pathname ? matchedParams : matchConfigPattern(paramsPathname, rewrite.source) ?? matchedParams;
718
629
  const conditionParams = rewrite.has || rewrite.missing ? collectConditionParams(rewrite.has, rewrite.missing, ctx) : _emptyParams();
719
630
  if (!conditionParams) continue;
720
631
  const rewriteParams = {
@@ -868,9 +779,6 @@ function sanitizeDestination(dest) {
868
779
  * Detects any URL scheme (http:, https:, data:, javascript:, blob:, etc.)
869
780
  * per RFC 3986, plus protocol-relative URLs (//).
870
781
  */
871
- function isExternalUrl(url) {
872
- return /^[a-z][a-z0-9+.-]*:/i.test(url) || url.startsWith("//");
873
- }
874
782
  /**
875
783
  * Merge the original request's query params into a config-redirect
876
784
  * destination, preserving them on the resulting `Location`.
@@ -927,6 +835,9 @@ async function proxyExternalRequest(request, externalUrl) {
927
835
  for (const key of keysToDelete) headers.delete(key);
928
836
  headers.delete(VINEXT_PRERENDER_SECRET_HEADER);
929
837
  headers.delete(VINEXT_PRERENDER_ROUTE_PARAMS_HEADER);
838
+ headers.delete(PRERENDER_REVALIDATE_HEADER);
839
+ headers.delete(PRERENDER_REVALIDATE_ONLY_GENERATED_HEADER);
840
+ headers.delete(VINEXT_REVALIDATE_HOST_HEADER);
930
841
  headers.delete(VINEXT_MW_CTX_HEADER);
931
842
  const method = request.method;
932
843
  const hasBody = method !== "GET" && method !== "HEAD";
@@ -1004,8 +915,8 @@ function _escapeRegexString(value) {
1004
915
  return value.replace(/[|\\{}()[\]^$+*?.]/g, "\\$&");
1005
916
  }
1006
917
  /**
1007
- * Apply Next.js i18n locale-prefix transformation to a set of redirect or
1008
- * rewrite rules. Mirrors the relevant slice of Next.js's `processRoutes`
918
+ * Apply Next.js i18n locale-prefix transformation to a set of redirect,
919
+ * rewrite, or header rules. Mirrors the relevant slice of Next.js's `processRoutes`
1009
920
  * (load-custom-routes.ts) with one deliberate divergence noted below.
1010
921
  *
1011
922
  * For each rule:
@@ -1048,7 +959,8 @@ function applyLocaleToRoutes(routes, i18n, type, options = {}) {
1048
959
  out.push(r);
1049
960
  continue;
1050
961
  }
1051
- const isExternal = !!r.destination && !r.destination.startsWith("/");
962
+ const destination = "destination" in r ? r.destination : void 0;
963
+ const isExternal = !!destination && !destination.startsWith("/");
1052
964
  if (!isExternal) for (const locale of defaultLocales) {
1053
965
  const localizedSource = `/${locale}${suffixFor(r.source)}`;
1054
966
  out.push({
@@ -1057,13 +969,14 @@ function applyLocaleToRoutes(routes, i18n, type, options = {}) {
1057
969
  });
1058
970
  }
1059
971
  const internalSource = `${internalLocale}${suffixFor(r.source)}`;
1060
- let internalDestination = r.destination;
972
+ let internalDestination = destination;
1061
973
  if (internalDestination && internalDestination.startsWith("/") && !isExternal) internalDestination = `/:nextInternalLocale${internalDestination === "/" && !trailingSlash ? "" : internalDestination}`;
1062
- out.push({
974
+ const internalRoute = {
1063
975
  ...r,
1064
- source: internalSource,
1065
- destination: internalDestination
1066
- });
976
+ source: internalSource
977
+ };
978
+ if ("destination" in internalRoute && internalDestination !== void 0) internalRoute.destination = internalDestination;
979
+ out.push(internalRoute);
1067
980
  out.push(r);
1068
981
  }
1069
982
  return out;
@@ -54,7 +54,8 @@ type NextHeader = {
54
54
  key: string;
55
55
  value: string;
56
56
  }>; /** See {@link NextRedirect.basePath}. */
57
- basePath?: false;
57
+ basePath?: false; /** See {@link NextRedirect.locale}. */
58
+ locale?: false;
58
59
  };
59
60
  type NextI18nConfig = {
60
61
  /** List of supported locales */locales: string[]; /** The default locale (used when no locale prefix is in the URL) */
@@ -71,7 +72,7 @@ type NextI18nConfig = {
71
72
  domain: string;
72
73
  defaultLocale: string;
73
74
  locales?: string[];
74
- http?: boolean;
75
+ http?: true;
75
76
  }>;
76
77
  };
77
78
  /**
@@ -99,7 +100,11 @@ type NextConfig = {
99
100
  * @see https://nextjs.org/docs/app/api-reference/config/next-config-js/assetPrefix
100
101
  */
101
102
  assetPrefix?: string; /** Whether to add trailing slashes */
102
- trailingSlash?: boolean; /** Internationalization routing config */
103
+ trailingSlash?: boolean; /** TypeScript build settings. */
104
+ typescript?: {
105
+ /** Project-relative path to the TypeScript configuration file. */tsconfigPath?: string;
106
+ [key: string]: unknown;
107
+ }; /** Internationalization routing config */
103
108
  i18n?: NextI18nConfig; /** URL redirect rules */
104
109
  redirects?: () => Promise<NextRedirect[]> | NextRedirect[]; /** URL rewrite rules */
105
110
  rewrites?: () => Promise<NextRewrite[] | {
@@ -216,7 +221,8 @@ type NextConfig = {
216
221
  prefetchInlining?: boolean | {
217
222
  maxBundleSize?: number;
218
223
  maxSize?: number;
219
- };
224
+ }; /** Header names forwarded by Pages Router `res.revalidate()` internal requests. */
225
+ allowedRevalidateHeaderKeys?: string[];
220
226
  [key: string]: unknown;
221
227
  };
222
228
  /**
@@ -265,6 +271,9 @@ type ResolvedNextConfig = {
265
271
  */
266
272
  assetPrefix: string;
267
273
  trailingSlash: boolean;
274
+ typescript: {
275
+ tsconfigPath?: string;
276
+ };
268
277
  output: "" | "export" | "standalone";
269
278
  pageExtensions: string[];
270
279
  resolveExtensions: string[] | null;
@@ -294,7 +303,8 @@ type ResolvedNextConfig = {
294
303
  mdx: MdxOptions | null; /** Explicit module aliases preserved from wrapped next.config plugins. */
295
304
  aliases: Record<string, string>; /** Extra allowed origins for dev server access (from allowedDevOrigins). */
296
305
  allowedDevOrigins: string[]; /** Extra allowed origins for server action CSRF validation (from experimental.serverActions.allowedOrigins). */
297
- serverActionsAllowedOrigins: string[]; /** Packages whose barrel imports should be optimized (from experimental.optimizePackageImports). */
306
+ serverActionsAllowedOrigins: string[]; /** Header names forwarded by Pages Router `res.revalidate()` internal requests. */
307
+ allowedRevalidateHeaderKeys: string[]; /** Packages whose barrel imports should be optimized (from experimental.optimizePackageImports). */
298
308
  optimizePackageImports: string[]; /** Packages explicitly requested for server/client transpilation. */
299
309
  transpilePackages: string[]; /** Packages treated as application code by Turbopack's foreign-code condition. */
300
310
  turbopackTranspilePackages: string[]; /** Inline app CSS into production HTML (from experimental.inlineCss). */
@@ -1,6 +1,7 @@
1
1
  import path, { toSlash } from "../deps/.pnpm/pathslash@0.1.0/deps/pathslash/dist/index.js";
2
2
  import { normalizePageExtensions } from "../routing/file-matcher.js";
3
- import { applyLocaleToRoutes, isExternalUrl } from "./config-matchers.js";
3
+ import { isExternalUrl } from "../utils/external-url.js";
4
+ import { applyLocaleToRoutes } from "./config-matchers.js";
4
5
  import { isUnknownRecord } from "../utils/record.js";
5
6
  import { getHtmlLimitedBotRegex } from "../utils/html-limited-bots.js";
6
7
  import { flattenPluginOptions } from "../utils/plugin-options.js";
@@ -657,6 +658,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
657
658
  basePath: "",
658
659
  assetPrefix: "",
659
660
  trailingSlash: false,
661
+ typescript: {},
660
662
  output: "",
661
663
  pageExtensions: normalizePageExtensions(),
662
664
  resolveExtensions: null,
@@ -678,6 +680,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
678
680
  aliases: {},
679
681
  allowedDevOrigins: [],
680
682
  serverActionsAllowedOrigins: [],
683
+ allowedRevalidateHeaderKeys: [],
681
684
  optimizePackageImports: [],
682
685
  transpilePackages: [],
683
686
  turbopackTranspilePackages: [...DEFAULT_TRANSPILED_PACKAGES],
@@ -815,6 +818,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
815
818
  afterFiles: applyLocaleToRoutes(rewrites.afterFiles, i18n, "rewrite", opts),
816
819
  fallback: applyLocaleToRoutes(rewrites.fallback, i18n, "rewrite", opts)
817
820
  };
821
+ headers = applyLocaleToRoutes(headers, i18n, "header", opts);
818
822
  }
819
823
  const images = config.images ? {
820
824
  ...config.images,
@@ -831,6 +835,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
831
835
  basePath: config.basePath ?? "",
832
836
  assetPrefix: normalizeAssetPrefix(config.assetPrefix),
833
837
  trailingSlash: config.trailingSlash ?? false,
838
+ typescript: typeof config.typescript?.tsconfigPath === "string" ? { tsconfigPath: config.typescript.tsconfigPath } : {},
834
839
  output: output === "export" || output === "standalone" ? output : "",
835
840
  pageExtensions,
836
841
  resolveExtensions: resolveExtensions ?? webpackProbe.resolveExtensions,
@@ -849,6 +854,7 @@ async function resolveNextConfig(config, root = toSlash(process.cwd()), options
849
854
  aliases,
850
855
  allowedDevOrigins,
851
856
  serverActionsAllowedOrigins,
857
+ allowedRevalidateHeaderKeys: Array.isArray(experimental?.allowedRevalidateHeaderKeys) ? experimental.allowedRevalidateHeaderKeys.filter((value) => typeof value === "string").map((value) => value.toLowerCase()) : [],
852
858
  optimizePackageImports,
853
859
  transpilePackages,
854
860
  turbopackTranspilePackages,