vinext 0.1.0 → 0.1.2

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 (205) hide show
  1. package/README.md +2 -5
  2. package/dist/build/assets-ignore.d.ts +32 -0
  3. package/dist/build/assets-ignore.js +48 -0
  4. package/dist/build/client-build-config.d.ts +33 -1
  5. package/dist/build/client-build-config.js +66 -1
  6. package/dist/check.js +4 -3
  7. package/dist/cli.js +2 -0
  8. package/dist/client/navigation-runtime.d.ts +11 -2
  9. package/dist/client/navigation-runtime.js +1 -1
  10. package/dist/client/vinext-next-data.d.ts +2 -1
  11. package/dist/client/window-next.d.ts +6 -4
  12. package/dist/config/config-matchers.d.ts +31 -5
  13. package/dist/config/config-matchers.js +50 -3
  14. package/dist/config/next-config.d.ts +29 -3
  15. package/dist/config/next-config.js +32 -2
  16. package/dist/deploy.js +47 -304
  17. package/dist/entries/app-rsc-entry.d.ts +8 -2
  18. package/dist/entries/app-rsc-entry.js +61 -5
  19. package/dist/entries/app-rsc-manifest.js +20 -2
  20. package/dist/entries/pages-client-entry.js +1 -1
  21. package/dist/entries/pages-server-entry.js +16 -7
  22. package/dist/index.d.ts +0 -2
  23. package/dist/index.js +233 -280
  24. package/dist/plugins/dynamic-preload-metadata.d.ts +13 -0
  25. package/dist/plugins/dynamic-preload-metadata.js +415 -0
  26. package/dist/plugins/og-assets.js +2 -2
  27. package/dist/plugins/optimize-imports.d.ts +8 -4
  28. package/dist/plugins/optimize-imports.js +16 -12
  29. package/dist/plugins/postcss.js +18 -14
  30. package/dist/plugins/require-context.d.ts +6 -0
  31. package/dist/plugins/require-context.js +184 -0
  32. package/dist/plugins/sass.d.ts +53 -24
  33. package/dist/plugins/sass.js +249 -1
  34. package/dist/plugins/wasm-module-import.d.ts +15 -0
  35. package/dist/plugins/wasm-module-import.js +50 -0
  36. package/dist/routing/app-route-graph.d.ts +35 -2
  37. package/dist/routing/app-route-graph.js +179 -8
  38. package/dist/routing/file-matcher.js +1 -1
  39. package/dist/routing/route-pattern.d.ts +2 -1
  40. package/dist/routing/route-pattern.js +16 -1
  41. package/dist/server/api-handler.js +4 -0
  42. package/dist/server/app-browser-entry.js +155 -215
  43. package/dist/server/app-browser-error.d.ts +4 -1
  44. package/dist/server/app-browser-error.js +7 -1
  45. package/dist/server/app-browser-history-controller.d.ts +104 -0
  46. package/dist/server/app-browser-history-controller.js +210 -0
  47. package/dist/server/app-browser-interception-context.d.ts +2 -1
  48. package/dist/server/app-browser-interception-context.js +15 -2
  49. package/dist/server/app-browser-navigation-controller.d.ts +13 -2
  50. package/dist/server/app-browser-navigation-controller.js +83 -4
  51. package/dist/server/app-browser-popstate.d.ts +12 -3
  52. package/dist/server/app-browser-popstate.js +19 -4
  53. package/dist/server/app-browser-rsc-redirect.d.ts +11 -2
  54. package/dist/server/app-browser-rsc-redirect.js +30 -8
  55. package/dist/server/app-browser-state.d.ts +3 -0
  56. package/dist/server/app-browser-state.js +10 -10
  57. package/dist/server/app-browser-visible-commit.js +10 -8
  58. package/dist/server/app-fallback-renderer.d.ts +2 -1
  59. package/dist/server/app-fallback-renderer.js +3 -1
  60. package/dist/server/app-history-state.d.ts +45 -1
  61. package/dist/server/app-history-state.js +109 -1
  62. package/dist/server/app-middleware.js +1 -0
  63. package/dist/server/app-optimistic-routing.js +22 -1
  64. package/dist/server/app-page-boundary-render.d.ts +2 -1
  65. package/dist/server/app-page-boundary-render.js +45 -21
  66. package/dist/server/app-page-cache.js +9 -7
  67. package/dist/server/app-page-dispatch.d.ts +14 -0
  68. package/dist/server/app-page-dispatch.js +21 -6
  69. package/dist/server/app-page-element-builder.d.ts +23 -2
  70. package/dist/server/app-page-element-builder.js +58 -17
  71. package/dist/server/app-page-execution.d.ts +1 -1
  72. package/dist/server/app-page-execution.js +32 -17
  73. package/dist/server/app-page-render.d.ts +7 -1
  74. package/dist/server/app-page-render.js +11 -16
  75. package/dist/server/app-page-request.d.ts +9 -6
  76. package/dist/server/app-page-request.js +14 -10
  77. package/dist/server/app-page-response.d.ts +2 -2
  78. package/dist/server/app-page-response.js +2 -2
  79. package/dist/server/app-page-route-wiring.d.ts +3 -1
  80. package/dist/server/app-page-route-wiring.js +10 -8
  81. package/dist/server/app-page-stream.d.ts +37 -7
  82. package/dist/server/app-page-stream.js +36 -6
  83. package/dist/server/app-pages-bridge.d.ts +16 -0
  84. package/dist/server/app-pages-bridge.js +23 -3
  85. package/dist/server/app-route-handler-cache.d.ts +1 -0
  86. package/dist/server/app-route-handler-cache.js +1 -0
  87. package/dist/server/app-route-handler-dispatch.d.ts +1 -0
  88. package/dist/server/app-route-handler-dispatch.js +2 -0
  89. package/dist/server/app-route-handler-execution.d.ts +1 -0
  90. package/dist/server/app-route-handler-execution.js +1 -0
  91. package/dist/server/app-route-handler-response.js +11 -10
  92. package/dist/server/app-route-handler-runtime.d.ts +1 -0
  93. package/dist/server/app-route-handler-runtime.js +15 -3
  94. package/dist/server/app-rsc-handler.d.ts +1 -0
  95. package/dist/server/app-rsc-handler.js +5 -4
  96. package/dist/server/app-rsc-response-finalizer.js +1 -1
  97. package/dist/server/app-rsc-route-matching.d.ts +20 -1
  98. package/dist/server/app-rsc-route-matching.js +29 -4
  99. package/dist/server/app-server-action-execution.d.ts +22 -1
  100. package/dist/server/app-server-action-execution.js +73 -12
  101. package/dist/server/app-ssr-entry.d.ts +6 -0
  102. package/dist/server/app-ssr-entry.js +19 -3
  103. package/dist/server/app-ssr-stream.js +9 -1
  104. package/dist/server/dev-lockfile.js +2 -1
  105. package/dist/server/dev-server.d.ts +1 -1
  106. package/dist/server/dev-server.js +97 -43
  107. package/dist/server/headers.d.ts +8 -1
  108. package/dist/server/headers.js +8 -1
  109. package/dist/server/instrumentation-runtime.d.ts +6 -0
  110. package/dist/server/instrumentation-runtime.js +8 -0
  111. package/dist/server/isr-cache.d.ts +37 -1
  112. package/dist/server/isr-cache.js +85 -1
  113. package/dist/server/isr-decision.d.ts +79 -0
  114. package/dist/server/isr-decision.js +70 -0
  115. package/dist/server/metadata-route-response.js +5 -3
  116. package/dist/server/middleware-runtime.d.ts +13 -0
  117. package/dist/server/middleware-runtime.js +11 -7
  118. package/dist/server/middleware.js +1 -0
  119. package/dist/server/navigation-planner.d.ts +62 -1
  120. package/dist/server/navigation-planner.js +193 -3
  121. package/dist/server/navigation-trace.d.ts +12 -2
  122. package/dist/server/navigation-trace.js +11 -1
  123. package/dist/server/normalize-path.d.ts +0 -8
  124. package/dist/server/normalize-path.js +3 -1
  125. package/dist/server/otel-tracer-extension.d.ts +45 -0
  126. package/dist/server/otel-tracer-extension.js +89 -0
  127. package/dist/server/pages-api-route.d.ts +14 -3
  128. package/dist/server/pages-api-route.js +6 -1
  129. package/dist/server/pages-asset-tags.d.ts +15 -4
  130. package/dist/server/pages-asset-tags.js +18 -12
  131. package/dist/server/pages-data-route.js +5 -1
  132. package/dist/server/pages-node-compat.d.ts +5 -11
  133. package/dist/server/pages-node-compat.js +175 -118
  134. package/dist/server/pages-page-data.d.ts +38 -7
  135. package/dist/server/pages-page-data.js +64 -18
  136. package/dist/server/pages-page-handler.d.ts +10 -2
  137. package/dist/server/pages-page-handler.js +49 -20
  138. package/dist/server/pages-page-response.d.ts +55 -2
  139. package/dist/server/pages-page-response.js +74 -6
  140. package/dist/server/pages-readiness.d.ts +36 -0
  141. package/dist/server/pages-readiness.js +21 -0
  142. package/dist/server/pages-request-pipeline.d.ts +113 -0
  143. package/dist/server/pages-request-pipeline.js +230 -0
  144. package/dist/server/pages-revalidate.d.ts +15 -0
  145. package/dist/server/pages-revalidate.js +19 -0
  146. package/dist/server/prod-server.d.ts +45 -3
  147. package/dist/server/prod-server.js +182 -234
  148. package/dist/server/socket-error-backstop.d.ts +19 -1
  149. package/dist/server/socket-error-backstop.js +77 -4
  150. package/dist/shims/app-router-scroll.js +22 -4
  151. package/dist/shims/cache-runtime.js +39 -2
  152. package/dist/shims/dynamic-preload-chunks.d.ts +8 -0
  153. package/dist/shims/dynamic-preload-chunks.js +77 -0
  154. package/dist/shims/dynamic.d.ts +4 -0
  155. package/dist/shims/dynamic.js +4 -2
  156. package/dist/shims/error-boundary.d.ts +17 -7
  157. package/dist/shims/error-boundary.js +8 -1
  158. package/dist/shims/error.js +37 -11
  159. package/dist/shims/fetch-cache.d.ts +22 -1
  160. package/dist/shims/fetch-cache.js +28 -1
  161. package/dist/shims/hash-scroll.d.ts +1 -0
  162. package/dist/shims/hash-scroll.js +3 -1
  163. package/dist/shims/head.js +6 -1
  164. package/dist/shims/headers.d.ts +16 -2
  165. package/dist/shims/headers.js +37 -1
  166. package/dist/shims/image-config.js +7 -1
  167. package/dist/shims/internal/app-route-detection.d.ts +6 -3
  168. package/dist/shims/internal/app-route-detection.js +10 -6
  169. package/dist/shims/internal/app-router-context.d.ts +5 -0
  170. package/dist/shims/internal/link-status-registry.d.ts +43 -0
  171. package/dist/shims/internal/link-status-registry.js +42 -0
  172. package/dist/shims/internal/route-pattern-for-warning.d.ts +27 -0
  173. package/dist/shims/internal/route-pattern-for-warning.js +40 -0
  174. package/dist/shims/internal/utils.d.ts +1 -0
  175. package/dist/shims/link.js +20 -6
  176. package/dist/shims/metadata.d.ts +6 -2
  177. package/dist/shims/metadata.js +32 -14
  178. package/dist/shims/navigation.d.ts +9 -18
  179. package/dist/shims/navigation.js +96 -23
  180. package/dist/shims/router-state.d.ts +1 -0
  181. package/dist/shims/router-state.js +2 -0
  182. package/dist/shims/router.d.ts +6 -3
  183. package/dist/shims/router.js +156 -22
  184. package/dist/shims/script-nonce-context.d.ts +1 -1
  185. package/dist/shims/script-nonce-context.js +11 -3
  186. package/dist/shims/server.d.ts +17 -1
  187. package/dist/shims/server.js +31 -6
  188. package/dist/shims/slot.js +1 -1
  189. package/dist/shims/unified-request-context.js +1 -0
  190. package/dist/typegen.js +1 -0
  191. package/dist/utils/client-build-manifest.d.ts +8 -1
  192. package/dist/utils/client-build-manifest.js +41 -6
  193. package/dist/utils/client-entry-manifest.d.ts +11 -0
  194. package/dist/utils/client-entry-manifest.js +29 -0
  195. package/dist/utils/client-runtime-metadata.d.ts +45 -0
  196. package/dist/utils/client-runtime-metadata.js +63 -0
  197. package/dist/utils/hash.d.ts +17 -1
  198. package/dist/utils/hash.js +36 -1
  199. package/dist/utils/lazy-chunks.d.ts +27 -1
  200. package/dist/utils/lazy-chunks.js +65 -1
  201. package/dist/utils/manifest-paths.d.ts +20 -2
  202. package/dist/utils/manifest-paths.js +38 -3
  203. package/dist/utils/path.d.ts +2 -1
  204. package/dist/utils/path.js +5 -1
  205. package/package.json +6 -2
package/README.md CHANGED
@@ -528,22 +528,19 @@ The cache is pluggable. The default `MemoryCacheHandler` works out of the box. S
528
528
 
529
529
  #### Configuring cache adapters from `vite.config`
530
530
 
531
- Instead of wiring up cache handlers imperatively from a worker entry, you can declare them in the `vinext()` plugin config. The `@vinext/cloudflare` package ships two Cloudflare adapters for this:
531
+ Instead of wiring up cache handlers imperatively from a worker entry, you can declare them in the `vinext()` plugin config. The `@vinext/cloudflare` package ships Cloudflare adapters for this:
532
532
 
533
533
  - **`kvDataAdapter()`** (`@vinext/cloudflare/cache/kv-data-adapter`) — backs the `"use cache"` data cache with a Workers KV namespace.
534
- - **`cdnAdapter()`** (`@vinext/cloudflare/cache/cdn-adapter`) — backs full-route CDN caching with the Workers Cache API.
535
534
 
536
535
  ```ts
537
536
  import { defineConfig } from "vite";
538
537
  import vinext from "vinext";
539
- import { cdnAdapter } from "@vinext/cloudflare/cache/cdn-adapter";
540
538
  import { kvDataAdapter } from "@vinext/cloudflare/cache/kv-data-adapter";
541
539
 
542
540
  export default defineConfig({
543
541
  plugins: [
544
542
  vinext({
545
543
  cache: {
546
- cdn: cdnAdapter(),
547
544
  data: kvDataAdapter(),
548
545
  },
549
546
  }),
@@ -559,7 +556,7 @@ The KV data adapter reads `env[binding]` at runtime, so add the matching KV name
559
556
  }
560
557
  ```
561
558
 
562
- `binding` defaults to `VINEXT_KV_CACHE`, so `kvDataAdapter()` with no options works as long as that's your binding name. Other options: `appPrefix` (namespace cache keys to isolate multiple apps in one KV namespace), `ttlSeconds` (default KV `expirationTtl`, default 30 days), and `tagCacheTtlMs` (in-memory tag-invalidation cache TTL, default 5s). `cdnAdapter()` takes no options — it just needs the Workers Cache, which is always available on Workers.
559
+ `binding` defaults to `VINEXT_KV_CACHE`, so `kvDataAdapter()` with no options works as long as that's your binding name. Other options: `appPrefix` (namespace cache keys to isolate multiple apps in one KV namespace), `ttlSeconds` (default KV `expirationTtl`, default 30 days), and `tagCacheTtlMs` (in-memory tag-invalidation cache TTL, default 5s).
563
560
 
564
561
  Each builder returns a plain, serializable `{ adapter, options }` descriptor — **it never touches the Workers runtime**, so nothing throws at build or dev time when bindings aren't available. The actual adapter (and its `env` binding lookup) is instantiated lazily on the first request.
565
562
 
@@ -0,0 +1,32 @@
1
+ //#region src/build/assets-ignore.d.ts
2
+ /**
3
+ * Build metadata that must never be served by the Cloudflare ASSETS binding.
4
+ *
5
+ * On Cloudflare Workers the ASSETS binding serves any uploaded file whose path
6
+ * matches the request BEFORE the Worker runs, so anything written into the
7
+ * client output directory (`dist/client`) is publicly fetchable unless excluded.
8
+ * Vite writes its build/SSR manifests under `dist/client/.vite/` (enabled for
9
+ * Cloudflare builds so the worker entry can compute lazy chunks), which would
10
+ * otherwise expose the full source-file → chunk mapping — including the paths of
11
+ * routes that are never linked from the UI.
12
+ *
13
+ * The Node production server already blocks `/.vite/` explicitly
14
+ * (see `server/static-file-cache.ts`); `.assetsignore` is the equivalent guard
15
+ * for the Cloudflare deployment target. wrangler matches `.assetsignore`
16
+ * patterns with `.gitignore` semantics (via the `ignore` package), so the bare
17
+ * `.vite` entry excludes the directory and everything beneath it.
18
+ */
19
+ declare const DEFAULT_VINEXT_ASSET_IGNORE_PATTERNS: readonly string[];
20
+ /**
21
+ * Ensure a `.assetsignore` in `clientDir` contains every pattern in `patterns`.
22
+ *
23
+ * Any pre-existing (user-authored) content is preserved verbatim and only the
24
+ * missing patterns are appended, so the result is idempotent across rebuilds
25
+ * and never clobbers a `.assetsignore` the developer wrote themselves.
26
+ *
27
+ * @returns `true` when the file was created or modified, `false` when it already
28
+ * covered every requested pattern.
29
+ */
30
+ declare function ensureAssetsIgnore(clientDir: string, patterns?: readonly string[]): boolean;
31
+ //#endregion
32
+ export { DEFAULT_VINEXT_ASSET_IGNORE_PATTERNS, ensureAssetsIgnore };
@@ -0,0 +1,48 @@
1
+ import fs from "node:fs";
2
+ import path from "node:path";
3
+ //#region src/build/assets-ignore.ts
4
+ /**
5
+ * Build metadata that must never be served by the Cloudflare ASSETS binding.
6
+ *
7
+ * On Cloudflare Workers the ASSETS binding serves any uploaded file whose path
8
+ * matches the request BEFORE the Worker runs, so anything written into the
9
+ * client output directory (`dist/client`) is publicly fetchable unless excluded.
10
+ * Vite writes its build/SSR manifests under `dist/client/.vite/` (enabled for
11
+ * Cloudflare builds so the worker entry can compute lazy chunks), which would
12
+ * otherwise expose the full source-file → chunk mapping — including the paths of
13
+ * routes that are never linked from the UI.
14
+ *
15
+ * The Node production server already blocks `/.vite/` explicitly
16
+ * (see `server/static-file-cache.ts`); `.assetsignore` is the equivalent guard
17
+ * for the Cloudflare deployment target. wrangler matches `.assetsignore`
18
+ * patterns with `.gitignore` semantics (via the `ignore` package), so the bare
19
+ * `.vite` entry excludes the directory and everything beneath it.
20
+ */
21
+ const DEFAULT_VINEXT_ASSET_IGNORE_PATTERNS = [".vite"];
22
+ /** wrangler reads asset-exclusion patterns from this file in the assets dir. */
23
+ const ASSETS_IGNORE_FILENAME = ".assetsignore";
24
+ const GENERATED_HEADER = "# Keep build metadata out of the deployed asset bundle (generated by vinext)";
25
+ /**
26
+ * Ensure a `.assetsignore` in `clientDir` contains every pattern in `patterns`.
27
+ *
28
+ * Any pre-existing (user-authored) content is preserved verbatim and only the
29
+ * missing patterns are appended, so the result is idempotent across rebuilds
30
+ * and never clobbers a `.assetsignore` the developer wrote themselves.
31
+ *
32
+ * @returns `true` when the file was created or modified, `false` when it already
33
+ * covered every requested pattern.
34
+ */
35
+ function ensureAssetsIgnore(clientDir, patterns = DEFAULT_VINEXT_ASSET_IGNORE_PATTERNS) {
36
+ const assetsIgnorePath = path.join(clientDir, ASSETS_IGNORE_FILENAME);
37
+ const existing = fs.existsSync(assetsIgnorePath) ? fs.readFileSync(assetsIgnorePath, "utf-8") : null;
38
+ const present = new Set((existing ?? "").split(/\r?\n/).map((line) => line.trim()).filter(Boolean));
39
+ const missing = patterns.filter((pattern) => !present.has(pattern));
40
+ if (missing.length === 0) return false;
41
+ const lines = existing && existing.trim().length > 0 ? [existing.replace(/\s*$/, "")] : [GENERATED_HEADER];
42
+ lines.push(...missing, "");
43
+ fs.mkdirSync(clientDir, { recursive: true });
44
+ fs.writeFileSync(assetsIgnorePath, lines.join("\n"));
45
+ return true;
46
+ }
47
+ //#endregion
48
+ export { DEFAULT_VINEXT_ASSET_IGNORE_PATTERNS, ensureAssetsIgnore };
@@ -51,10 +51,16 @@ declare function createClientManualChunks(shimsDir: string): (id: string) => str
51
51
  * compression efficiency — small files restart the compression dictionary,
52
52
  * adding ~5-15% wire overhead vs fewer larger chunks.
53
53
  */
54
+ declare function createClientFileNameConfig(assetsDir: string): {
55
+ entryFileNames: string;
56
+ chunkFileNames: string;
57
+ };
54
58
  declare function createClientOutputConfig(clientManualChunks: (id: string) => string | undefined, assetsDir: string): {
55
59
  assetFileNames: (assetInfo: ClientAssetFileNameInfo) => string;
56
60
  manualChunks: (id: string) => string | undefined;
57
61
  experimentalMinChunkSize: number;
62
+ entryFileNames: string;
63
+ chunkFileNames: string;
58
64
  };
59
65
  declare function createClientCodeSplittingConfig(clientManualChunks: (id: string) => string | undefined): {
60
66
  minSize: number;
@@ -62,6 +68,32 @@ declare function createClientCodeSplittingConfig(clientManualChunks: (id: string
62
68
  name(moduleId: string): string | null;
63
69
  }[];
64
70
  };
71
+ /**
72
+ * Regex matching any {@link FRAMEWORK_PACKAGES} package inside `node_modules`.
73
+ * Derived from the package list so the regex and {@link isRscFrameworkModule}
74
+ * predicate can't drift.
75
+ */
76
+ declare const RSC_FRAMEWORK_CHUNK_TEST: RegExp;
77
+ declare function isRscFrameworkModule(id: string): boolean;
78
+ /**
79
+ * Output config that isolates React (and the RSC flight runtime) into a
80
+ * dedicated "framework" chunk in the RSC server build. Returns the bundler-
81
+ * appropriate shape: rolldown's `codeSplitting` for Vite 8+, Rollup's
82
+ * `manualChunks` for Vite 7. See {@link RSC_FRAMEWORK_CHUNK_TEST} for the
83
+ * motivation (issue #1549).
84
+ */
85
+ declare function createRscFrameworkChunkOutputConfig(viteMajorVersion: number): {
86
+ codeSplitting: {
87
+ groups: {
88
+ name: string;
89
+ test: RegExp;
90
+ }[];
91
+ };
92
+ manualChunks?: undefined;
93
+ } | {
94
+ manualChunks(id: string): string | undefined;
95
+ codeSplitting?: undefined;
96
+ };
65
97
  /**
66
98
  * Rollup treeshake configuration for production client builds.
67
99
  *
@@ -129,4 +161,4 @@ type VinextBuildConfigWithLegacy = VinextBuildConfig & {
129
161
  declare function getBuildBundlerOptions(build: UserConfig["build"] | undefined): VinextBuildBundlerOptions | undefined;
130
162
  declare function withBuildBundlerOptions(viteMajorVersion: number, bundlerOptions: VinextBuildBundlerOptions): Partial<VinextBuildConfigWithLegacy>;
131
163
  //#endregion
132
- export { clientTreeshakeConfig, createClientAssetFileNames, createClientCodeSplittingConfig, createClientManualChunks, createClientOutputConfig, getBuildBundlerOptions, getClientTreeshakeConfigForVite, withBuildBundlerOptions };
164
+ export { RSC_FRAMEWORK_CHUNK_TEST, clientTreeshakeConfig, createClientAssetFileNames, createClientCodeSplittingConfig, createClientFileNameConfig, createClientManualChunks, createClientOutputConfig, createRscFrameworkChunkOutputConfig, getBuildBundlerOptions, getClientTreeshakeConfigForVite, isRscFrameworkModule, withBuildBundlerOptions };
@@ -89,8 +89,16 @@ function createClientManualChunks(shimsDir) {
89
89
  * compression efficiency — small files restart the compression dictionary,
90
90
  * adding ~5-15% wire overhead vs fewer larger chunks.
91
91
  */
92
+ function createClientFileNameConfig(assetsDir) {
93
+ const chunksDir = `${assetsDir}/chunks`;
94
+ return {
95
+ entryFileNames: `${chunksDir}/[name]-[hash].js`,
96
+ chunkFileNames: `${chunksDir}/[name]-[hash].js`
97
+ };
98
+ }
92
99
  function createClientOutputConfig(clientManualChunks, assetsDir) {
93
100
  return {
101
+ ...createClientFileNameConfig(assetsDir),
94
102
  assetFileNames: createClientAssetFileNames(assetsDir),
95
103
  manualChunks: clientManualChunks,
96
104
  experimentalMinChunkSize: 1e4
@@ -105,6 +113,63 @@ function createClientCodeSplittingConfig(clientManualChunks) {
105
113
  };
106
114
  }
107
115
  /**
116
+ * Matches React framework packages (and the RSC flight runtime) inside
117
+ * `node_modules`. Used to split them into a dedicated "framework" chunk in the
118
+ * RSC server build.
119
+ *
120
+ * Why the RSC build needs this: without an explicit framework chunk, the
121
+ * bundler colocates React into whichever chunk first reaches it — typically the
122
+ * RSC entry chunk, which also carries the root layout's CSS. Modules that only
123
+ * import React for runtime helpers (notably `app/global-not-found.tsx`, which
124
+ * replaces the root layout for route-miss 404s) then import that entry chunk
125
+ * and inherit the root layout's CSS in their `serverResources` metadata. The
126
+ * 404 document ends up linking the layout's stylesheet last, so the layout's
127
+ * rules win the cascade over global-not-found's — the bug tracked in
128
+ * https://github.com/cloudflare/vinext/issues/1549.
129
+ *
130
+ * Splitting React into its own (CSS-free) chunk means global-not-found imports
131
+ * the framework chunk instead of the layout-bearing entry chunk, so it no
132
+ * longer inherits the root layout's CSS. The match list mirrors the client
133
+ * build's `framework` chunk, plus `react-server-dom-webpack` for the RSC flight
134
+ * runtime that the server environment bundles.
135
+ *
136
+ * Uses `[\\/]` rather than `/` for the path separator so it matches on Windows
137
+ * too, per the rolldown `codeSplitting` docs.
138
+ */
139
+ const FRAMEWORK_PACKAGES = [
140
+ "react",
141
+ "react-dom",
142
+ "scheduler",
143
+ "react-server-dom-webpack"
144
+ ];
145
+ /**
146
+ * Regex matching any {@link FRAMEWORK_PACKAGES} package inside `node_modules`.
147
+ * Derived from the package list so the regex and {@link isRscFrameworkModule}
148
+ * predicate can't drift.
149
+ */
150
+ const RSC_FRAMEWORK_CHUNK_TEST = new RegExp(`[\\\\/]node_modules[\\\\/](${FRAMEWORK_PACKAGES.join("|")})[\\\\/]`);
151
+ function isRscFrameworkModule(id) {
152
+ if (!id.includes("node_modules")) return false;
153
+ const pkg = getPackageName(id);
154
+ return pkg !== null && FRAMEWORK_PACKAGES.includes(pkg);
155
+ }
156
+ /**
157
+ * Output config that isolates React (and the RSC flight runtime) into a
158
+ * dedicated "framework" chunk in the RSC server build. Returns the bundler-
159
+ * appropriate shape: rolldown's `codeSplitting` for Vite 8+, Rollup's
160
+ * `manualChunks` for Vite 7. See {@link RSC_FRAMEWORK_CHUNK_TEST} for the
161
+ * motivation (issue #1549).
162
+ */
163
+ function createRscFrameworkChunkOutputConfig(viteMajorVersion) {
164
+ if (viteMajorVersion >= 8) return { codeSplitting: { groups: [{
165
+ name: "framework",
166
+ test: RSC_FRAMEWORK_CHUNK_TEST
167
+ }] } };
168
+ return { manualChunks(id) {
169
+ return isRscFrameworkModule(id) ? "framework" : void 0;
170
+ } };
171
+ }
172
+ /**
108
173
  * Rollup treeshake configuration for production client builds.
109
174
  *
110
175
  * Uses the 'recommended' preset as a safe base, then overrides
@@ -171,4 +236,4 @@ function withBuildBundlerOptions(viteMajorVersion, bundlerOptions) {
171
236
  return viteMajorVersion >= 8 ? { rolldownOptions: bundlerOptions } : { rollupOptions: bundlerOptions };
172
237
  }
173
238
  //#endregion
174
- export { clientTreeshakeConfig, createClientAssetFileNames, createClientCodeSplittingConfig, createClientManualChunks, createClientOutputConfig, getBuildBundlerOptions, getClientTreeshakeConfigForVite, withBuildBundlerOptions };
239
+ export { RSC_FRAMEWORK_CHUNK_TEST, clientTreeshakeConfig, createClientAssetFileNames, createClientCodeSplittingConfig, createClientFileNameConfig, createClientManualChunks, createClientOutputConfig, createRscFrameworkChunkOutputConfig, getBuildBundlerOptions, getClientTreeshakeConfigForVite, isRscFrameworkModule, withBuildBundlerOptions };
package/dist/check.js CHANGED
@@ -1,4 +1,5 @@
1
1
  import { detectPackageManager } from "./utils/project.js";
2
+ import { normalizePathSeparators } from "./utils/path.js";
2
3
  import fs from "node:fs";
3
4
  import path from "node:path";
4
5
  import { parseAst } from "vite";
@@ -342,7 +343,7 @@ function findSourceFiles(dir, extensions = [
342
343
  if (!fs.existsSync(dir)) return results;
343
344
  const entries = fs.readdirSync(dir, { withFileTypes: true });
344
345
  for (const entry of entries) {
345
- const fullPath = path.join(dir, entry.name);
346
+ const fullPath = normalizePathSeparators(path.join(dir, entry.name));
346
347
  if (entry.isDirectory()) {
347
348
  if (entry.name === "node_modules" || entry.name === ".next" || entry.name === "dist" || entry.name === ".git") continue;
348
349
  results.push(...findSourceFiles(fullPath, extensions));
@@ -563,7 +564,7 @@ function scanImports(root) {
563
564
  if (mod.startsWith("next/") || mod === "next" || mod === "server-only" || mod === "client-only") {
564
565
  const normalized = mod === "next" ? "next" : mod;
565
566
  if (!importUsage.has(normalized)) importUsage.set(normalized, []);
566
- const relFile = path.relative(root, file);
567
+ const relFile = normalizePathSeparators(path.relative(root, file));
567
568
  const usedInFiles = importUsage.get(normalized) ?? [];
568
569
  if (!usedInFiles.includes(relFile)) usedInFiles.push(relFile);
569
570
  }
@@ -876,7 +877,7 @@ function checkConventions(root) {
876
877
  const cjsGlobalFiles = [];
877
878
  for (const file of allSourceFiles) {
878
879
  const content = fs.readFileSync(file, "utf-8");
879
- const rel = path.relative(root, file);
880
+ const rel = normalizePathSeparators(path.relative(root, file));
880
881
  if (viewTransitionRegex.test(content)) viewTransitionFiles.push(rel);
881
882
  if (hasFreeCjsGlobal(content)) cjsGlobalFiles.push(rel);
882
883
  }
package/dist/cli.js CHANGED
@@ -18,6 +18,7 @@ import { createRequire } from "node:module";
18
18
  import fs from "node:fs";
19
19
  import path from "node:path";
20
20
  import { pathToFileURL } from "node:url";
21
+ import { randomBytes } from "node:crypto";
21
22
  import { execFileSync } from "node:child_process";
22
23
  //#region src/cli.ts
23
24
  /**
@@ -263,6 +264,7 @@ async function buildApp() {
263
264
  const resolvedNextConfig = await resolveNextConfig(await loadNextConfig(root, PHASE_PRODUCTION_BUILD), root);
264
265
  process.env.__VINEXT_SHARED_BUILD_ID = resolvedNextConfig.buildId;
265
266
  process.env.__VINEXT_SHARED_RSC_COMPATIBILITY_ID = createRscCompatibilityId(resolvedNextConfig);
267
+ if (!process.env.__VINEXT_SHARED_REVALIDATE_SECRET) process.env.__VINEXT_SHARED_REVALIDATE_SECRET = randomBytes(32).toString("hex");
266
268
  const outputMode = resolvedNextConfig.output;
267
269
  const distDir = path.resolve(root, "dist");
268
270
  if (outputMode === "standalone") {
@@ -15,17 +15,26 @@ type NavigationRuntimeRscBootstrap = {
15
15
  };
16
16
  type NavigationRuntimeKind = "navigate" | "traverse" | "refresh";
17
17
  type NavigationRuntimeHistoryUpdateMode = "push" | "replace";
18
+ type NavigationRuntimeVisibleCommitMode = "transition" | "synchronous";
18
19
  type NavigationRuntimeTraversalIntent = {
19
20
  direction: "back" | "forward" | "unknown";
20
21
  historyState: unknown;
21
22
  targetHistoryIndex: number | null;
22
23
  };
23
- type NavigationRuntimeNavigate = (href: string, redirectDepth?: number, navigationKind?: NavigationRuntimeKind, historyUpdateMode?: NavigationRuntimeHistoryUpdateMode, previousNextUrlOverride?: string | null, programmaticTransition?: boolean, traversalIntent?: NavigationRuntimeTraversalIntent, scrollIntent?: AppRouterScrollIntent | null) => Promise<void>;
24
+ type NavigationRuntimeNavigate = (href: string, redirectDepth?: number, navigationKind?: NavigationRuntimeKind, historyUpdateMode?: NavigationRuntimeHistoryUpdateMode, previousNextUrlOverride?: string | null, programmaticTransition?: boolean, traversalIntent?: NavigationRuntimeTraversalIntent, scrollIntent?: AppRouterScrollIntent | null, visibleCommitMode?: NavigationRuntimeVisibleCommitMode) => Promise<void>;
24
25
  type NavigationRuntimeFunctions = {
25
26
  clearNavigationCaches?: () => void;
26
27
  commitHashNavigation?: (href: string, historyUpdateMode: NavigationRuntimeHistoryUpdateMode, scroll: boolean) => void;
27
28
  navigateExternal?: (href: string, historyUpdateMode: NavigationRuntimeHistoryUpdateMode) => Promise<void>;
28
29
  navigate?: NavigationRuntimeNavigate;
30
+ /**
31
+ * Called at the start of every App Router navigation so the <Link> shim can
32
+ * reset any link that is still showing a `useLinkStatus()` pending state but
33
+ * is not the one driving this navigation (e.g. a programmatic router.push or
34
+ * a shallow-routing transition). Registered by shims/link.tsx; decoupled
35
+ * through the runtime to avoid a circular import with shims/navigation.ts.
36
+ */
37
+ notifyLinkNavigationStart?: () => void;
29
38
  pingVisibleLinks?: () => void;
30
39
  };
31
40
  type NavigationRuntimeBootstrap = {
@@ -58,4 +67,4 @@ declare function hasAppNavigationRuntime(): boolean;
58
67
  */
59
68
  declare function hasAppNavigationRuntimeBootstrap(): boolean;
60
69
  //#endregion
61
- export { NAVIGATION_RUNTIME_KEY, NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION, NavigationRuntime, NavigationRuntimeBootstrap, NavigationRuntimeFunctions, NavigationRuntimeNavigate, NavigationRuntimeRscBootstrap, NavigationRuntimeRscChunk, NavigationRuntimeSnapshot, ensureNavigationRuntimeRscBootstrap, getNavigationRuntime, hasAppNavigationRuntime, hasAppNavigationRuntimeBootstrap, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions, subscribeNavigationRuntimeRscChunk };
70
+ export { NAVIGATION_RUNTIME_KEY, NAVIGATION_RUNTIME_SYMBOL_DESCRIPTION, NavigationRuntime, NavigationRuntimeBootstrap, NavigationRuntimeFunctions, NavigationRuntimeNavigate, NavigationRuntimeRscBootstrap, NavigationRuntimeRscChunk, NavigationRuntimeSnapshot, NavigationRuntimeVisibleCommitMode, ensureNavigationRuntimeRscBootstrap, getNavigationRuntime, hasAppNavigationRuntime, hasAppNavigationRuntimeBootstrap, registerNavigationRuntimeBootstrap, registerNavigationRuntimeFunctions, subscribeNavigationRuntimeRscChunk };
@@ -31,7 +31,7 @@ function readRuntimeWindow() {
31
31
  }
32
32
  function isNavigationRuntimeFunctions(value) {
33
33
  if (!isUnknownRecord(value)) return false;
34
- return isOptionalRuntimeFunction(Reflect.get(value, "clearNavigationCaches")) && isOptionalRuntimeFunction(Reflect.get(value, "commitHashNavigation")) && isOptionalRuntimeFunction(Reflect.get(value, "navigateExternal")) && isOptionalRuntimeFunction(Reflect.get(value, "navigate")) && isOptionalRuntimeFunction(Reflect.get(value, "pingVisibleLinks"));
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, "notifyLinkNavigationStart")) && isOptionalRuntimeFunction(Reflect.get(value, "pingVisibleLinks"));
35
35
  }
36
36
  function isNavigationRuntimeRscChunk(value) {
37
37
  if (typeof value === "string") return true;
@@ -10,7 +10,8 @@ type VinextNextData = {
10
10
  /** vinext-specific additions (not part of Next.js upstream). */__vinext?: {
11
11
  /** Absolute URL of the page module for dynamic import. */pageModuleUrl?: string; /** Absolute URL of the `_app` module for dynamic import. */
12
12
  appModuleUrl?: string; /** True when the Pages Router server has middleware/proxy configured. */
13
- hasMiddleware?: boolean;
13
+ hasMiddleware?: boolean; /** True when build-time rewrites can affect the initial Pages Router ready state. */
14
+ hasRewrites?: boolean;
14
15
  };
15
16
  } & NEXT_DATA;
16
17
  type BrowserVinextNextData = NonNullable<Window["__NEXT_DATA__"]> & VinextNextData;
@@ -40,10 +40,9 @@
40
40
  * `window.next.router`. Mirrors the `publicAppRouterInstance` shape from
41
41
  * `packages/next/src/client/components/app-router-instance.ts`.
42
42
  *
43
- * `hmrRefresh` and `experimental_gesturePush` are intentionally omitted —
44
- * vinext does not implement them. Library callers that branch on their
45
- * presence (`typeof router.hmrRefresh === "function"`) will skip the
46
- * branch, matching what they would do on a production Next.js build.
43
+ * `hmrRefresh` is intentionally omitted — vinext does not implement it.
44
+ * `experimental_gesturePush` is attached only when
45
+ * `experimental.gestureTransition` is enabled, matching Next.js.
47
46
  */
48
47
  type AppRouterPublicInstance = {
49
48
  push: (href: string, options?: {
@@ -57,6 +56,9 @@ type AppRouterPublicInstance = {
57
56
  refresh: () => void;
58
57
  prefetch: (href: string, options?: {
59
58
  onInvalidate?: () => void;
59
+ }) => void;
60
+ experimental_gesturePush?: (href: string, options?: {
61
+ scroll?: boolean;
60
62
  }) => void; /** Default placeholder, matches Next.js. */
61
63
  bfcacheId?: string;
62
64
  };
@@ -32,10 +32,10 @@ declare function escapeHeaderSource(source: string): string;
32
32
  * Callers extract the relevant parts from the incoming Request.
33
33
  */
34
34
  type RequestContext = {
35
- headers: Headers;
36
- cookies: Record<string, string>;
37
- query: URLSearchParams;
38
- host: string;
35
+ readonly headers: Headers;
36
+ readonly cookies: Record<string, string>;
37
+ readonly query: URLSearchParams;
38
+ readonly host: string;
39
39
  };
40
40
  /**
41
41
  * basePath gating state passed alongside the pathname to every matcher.
@@ -65,6 +65,13 @@ type BasePathMatchState = {
65
65
  declare function parseCookies(cookieHeader: string | null): Record<string, string>;
66
66
  /**
67
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`.
68
75
  */
69
76
  declare function requestContextFromRequest(request: Request): RequestContext;
70
77
  declare function normalizeHost(hostHeader: string | null, fallbackHostname: string): string;
@@ -159,6 +166,25 @@ declare function sanitizeDestination(dest: string): string;
159
166
  * per RFC 3986, plus protocol-relative URLs (//).
160
167
  */
161
168
  declare function isExternalUrl(url: string): boolean;
169
+ /**
170
+ * Merge the original request's query params into a config-redirect
171
+ * destination, preserving them on the resulting `Location`.
172
+ *
173
+ * Next.js carries the original request query across config redirects
174
+ * (`prepareDestination({ query: parsedUrl.query })` →
175
+ * `stringifyQuery(...)` in resolve-routes.ts). This matters for App Router
176
+ * RSC client navigations: the cache-busting `_rsc` query must survive the
177
+ * redirect so the browser's auto-followed request to the destination is
178
+ * still treated as an RSC fetch. Dropping it breaks RSC fetch semantics
179
+ * (issue #1529).
180
+ *
181
+ * Destination query params win — a request param is only carried over when
182
+ * the destination does not already specify that key. Mirrors the merge
183
+ * semantics in `proxyExternalRequest`. External destinations are returned
184
+ * untouched (a config redirect to another origin should not leak the
185
+ * original request's query).
186
+ */
187
+ declare function preserveRedirectDestinationQuery(destination: string, requestSearch: string): string;
162
188
  /**
163
189
  * Proxy an incoming request to an external URL and return the upstream response.
164
190
  *
@@ -218,4 +244,4 @@ declare function applyLocaleToRoutes<T extends NextRedirect | NextRewrite>(route
218
244
  trailingSlash?: boolean;
219
245
  }): T[];
220
246
  //#endregion
221
- export { BasePathMatchState, RequestContext, applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, escapeHeaderSource, isExternalUrl, isSafeRegex, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, normalizeHost, parseCookies, proxyExternalRequest, requestContextFromRequest, safeRegExp, sanitizeDestination };
247
+ export { BasePathMatchState, RequestContext, applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, escapeHeaderSource, isExternalUrl, isSafeRegex, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, normalizeHost, parseCookies, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, safeRegExp, sanitizeDestination };
@@ -356,13 +356,26 @@ function parseCookies(cookieHeader) {
356
356
  }
357
357
  /**
358
358
  * Build a RequestContext from a Web Request object.
359
+ *
360
+ * `cookies` and `query` are lazy memoized getters: they are consumed only by
361
+ * `has`/`missing` condition evaluation (`checkHasConditions` /
362
+ * `matchesRuleConditions`), and most apps configure no such conditions. The
363
+ * cookie split and `searchParams` access are therefore deferred until first
364
+ * read and computed at most once. Mirrors `headersContextFromRequest` in
365
+ * `shims/headers.ts`.
359
366
  */
360
367
  function requestContextFromRequest(request) {
361
368
  const url = new URL(request.url);
369
+ let cookies;
370
+ let query;
362
371
  return {
363
372
  headers: request.headers,
364
- cookies: parseCookies(request.headers.get("cookie")),
365
- query: url.searchParams,
373
+ get cookies() {
374
+ return cookies ??= parseCookies(request.headers.get("cookie"));
375
+ },
376
+ get query() {
377
+ return query ??= url.searchParams;
378
+ },
366
379
  host: normalizeHost(request.headers.get("host"), url.hostname)
367
380
  };
368
381
  }
@@ -764,6 +777,40 @@ function isExternalUrl(url) {
764
777
  return /^[a-z][a-z0-9+.-]*:/i.test(url) || url.startsWith("//");
765
778
  }
766
779
  /**
780
+ * Merge the original request's query params into a config-redirect
781
+ * destination, preserving them on the resulting `Location`.
782
+ *
783
+ * Next.js carries the original request query across config redirects
784
+ * (`prepareDestination({ query: parsedUrl.query })` →
785
+ * `stringifyQuery(...)` in resolve-routes.ts). This matters for App Router
786
+ * RSC client navigations: the cache-busting `_rsc` query must survive the
787
+ * redirect so the browser's auto-followed request to the destination is
788
+ * still treated as an RSC fetch. Dropping it breaks RSC fetch semantics
789
+ * (issue #1529).
790
+ *
791
+ * Destination query params win — a request param is only carried over when
792
+ * the destination does not already specify that key. Mirrors the merge
793
+ * semantics in `proxyExternalRequest`. External destinations are returned
794
+ * untouched (a config redirect to another origin should not leak the
795
+ * original request's query).
796
+ */
797
+ function preserveRedirectDestinationQuery(destination, requestSearch) {
798
+ if (requestSearch === "" || requestSearch === "?" || isExternalUrl(destination)) return destination;
799
+ const requestParams = new URLSearchParams(requestSearch);
800
+ if ([...requestParams.keys()].length === 0) return destination;
801
+ const hashIndex = destination.indexOf("#");
802
+ const hash = hashIndex === -1 ? "" : destination.slice(hashIndex);
803
+ const beforeHash = hashIndex === -1 ? destination : destination.slice(0, hashIndex);
804
+ const queryIndex = beforeHash.indexOf("?");
805
+ const pathPart = queryIndex === -1 ? beforeHash : beforeHash.slice(0, queryIndex);
806
+ const destQuery = queryIndex === -1 ? "" : beforeHash.slice(queryIndex + 1);
807
+ const merged = new URLSearchParams(destQuery);
808
+ const destKeys = new Set(merged.keys());
809
+ for (const [key, value] of requestParams) if (!destKeys.has(key)) merged.append(key, value);
810
+ const mergedQuery = merged.toString();
811
+ return mergedQuery === "" ? `${pathPart}${hash}` : `${pathPart}?${mergedQuery}${hash}`;
812
+ }
813
+ /**
767
814
  * Proxy an incoming request to an external URL and return the upstream response.
768
815
  *
769
816
  * Used for external rewrites (e.g. `/ph/:path*` → `https://us.i.posthog.com/:path*`).
@@ -925,4 +972,4 @@ function applyLocaleToRoutes(routes, i18n, type, options = {}) {
925
972
  return out;
926
973
  }
927
974
  //#endregion
928
- export { applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, escapeHeaderSource, isExternalUrl, isSafeRegex, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, normalizeHost, parseCookies, proxyExternalRequest, requestContextFromRequest, safeRegExp, sanitizeDestination };
975
+ export { applyLocaleToRoutes, applyMiddlewareRequestHeaders, checkHasConditions, escapeHeaderSource, isExternalUrl, isSafeRegex, matchConfigPattern, matchHeaders, matchRedirect, matchRewrite, normalizeHost, parseCookies, preserveRedirectDestinationQuery, proxyExternalRequest, requestContextFromRequest, safeRegExp, sanitizeDestination };
@@ -132,7 +132,14 @@ type NextConfig = {
132
132
  */
133
133
  instrumentationClientInject?: string[]; /** Extra origins allowed to access the dev server. */
134
134
  allowedDevOrigins?: string[]; /** Maximum age in seconds for stale ISR entries before blocking regeneration. */
135
- expireTime?: number; /** User agents that require blocking metadata in the initial head. */
135
+ expireTime?: number;
136
+ /**
137
+ * Maximum total length (in characters) of the preload `Link` header emitted
138
+ * during App Router SSR. React drops whole entries once the limit is
139
+ * exceeded; `0` disables emission entirely. Defaults to 6000.
140
+ * @see https://nextjs.org/docs/app/api-reference/config/next-config-js/reactMaxHeadersLength
141
+ */
142
+ reactMaxHeadersLength?: number; /** User agents that require blocking metadata in the initial head. */
136
143
  htmlLimitedBots?: RegExp | string;
137
144
  /**
138
145
  * Enable Cache Components (Next.js 16).
@@ -174,6 +181,14 @@ type NextConfig = {
174
181
  */
175
182
  defineServer?: Record<string, string | number | boolean>;
176
183
  };
184
+ experimental?: {
185
+ /**
186
+ * Enables the experimental App Router gesture transition API:
187
+ * `useRouter().experimental_gesturePush()`.
188
+ */
189
+ gestureTransition?: boolean;
190
+ [key: string]: unknown;
191
+ };
177
192
  /**
178
193
  * Path to a custom cache handler module (e.g., KV, Redis, DynamoDB).
179
194
  * Accepts relative paths, absolute paths, or file:// URLs from import.meta.resolve().
@@ -223,6 +238,11 @@ type ResolvedNextConfig = {
223
238
  pageExtensions: string[];
224
239
  instrumentationClientInject: string[];
225
240
  cacheComponents: boolean;
241
+ /**
242
+ * Enables the experimental App Router gesture transition API:
243
+ * `useRouter().experimental_gesturePush()`.
244
+ */
245
+ gestureTransition: boolean;
226
246
  /**
227
247
  * Whether `experimental.prefetchInlining` is configured. Next.js uses this
228
248
  * with the Segment Cache to fetch the route tree before the bundled inlined
@@ -244,8 +264,14 @@ type ResolvedNextConfig = {
244
264
  serverActionsAllowedOrigins: string[]; /** Packages whose barrel imports should be optimized (from experimental.optimizePackageImports). */
245
265
  optimizePackageImports: string[]; /** Inline app CSS into production HTML (from experimental.inlineCss). */
246
266
  inlineCss: boolean; /** Parsed body size limit for server actions in bytes (from experimental.serverActions.bodySizeLimit). Defaults to 1MB. */
247
- serverActionsBodySizeLimit: number; /** Route-level expire fallback in seconds for ISR entries with numeric revalidate. */
248
- expireTime: number; /** Serialized htmlLimitedBots regexp source from next.config. */
267
+ serverActionsBodySizeLimit: number; /** Verbatim body size limit config value (e.g. "2mb") for the "Body exceeded {limit} limit" error. Defaults to "1 MB". */
268
+ serverActionsBodySizeLimitLabel: string; /** Route-level expire fallback in seconds for ISR entries with numeric revalidate. */
269
+ expireTime: number;
270
+ /**
271
+ * Maximum total length (in characters) of the preload `Link` header emitted
272
+ * during App Router SSR. `0` disables emission. Defaults to 6000.
273
+ */
274
+ reactMaxHeadersLength: number; /** Serialized htmlLimitedBots regexp source from next.config. */
249
275
  htmlLimitedBots: string | undefined;
250
276
  /**
251
277
  * Packages that should be treated as server-external (not bundled by Vite).