Skip to content

Conversation

@wyattjoh
Copy link
Member

@wyattjoh wyattjoh commented Nov 5, 2025

What

Fixes a bug where interception routes in parallel slots could not be prerendered using generateStaticParams, causing 404 responses when these routes were accessed directly.

Why

The Problem:

Interception routes like app/@modal/(.)photo/[id]/page.tsx could not be prerendered even when they exported generateStaticParams. This was because the static path generation code only examined "children" segments in the loader tree, completely missing segments from parallel routes (like @modal) that actually contribute to the pathname.

Root Cause:

The previous implementation used childrenRouteParamSegments which only traversed the children branch of the loader tree:

// OLD: Only looked at children
const childrenRouteParamSegments = [...segments from children only...]

// This missed parallel routes like @modal that have dynamic segments

For a route structure like:

app/
  [username]/
    page.tsx
  @modal/
    (.)[username]/
      [id]/
        page.tsx  // ← This route's segments were MISSED

The build system couldn't discover the [id] parameter in the parallel route because it never traversed that branch of the tree.

How

Solution:

Introduces extractPathnameSegments() which properly traverses the ENTIRE loader tree (not just children) to find ALL segments that contribute to the pathname:

  1. BFS Traversal: Explores both children AND all parallel route slots (e.g., @modal, @sidebar)

  2. Depth Tracking: Correctly tracks URL depth by:

    • Skipping route groups (marketing) - not in URL
    • Skipping parallel markers @modal - not in URL
    • Including interception markers (.)photo - ARE in URL
  3. Prefix Validation: Ensures static segments match the target pathname before including dynamic segments

  4. Complete Parameter Discovery: Returns all segments that contribute to pathname construction, regardless of which tree branch they're in

Example:

For app/@modal/(.)photo/[id]/page.tsx:

  • Old: Missed the [id] parameter entirely
  • New: Discovers [id] and enables prerendering with generateStaticParams

Changes

New Module: extract-pathname-segments.ts (192 lines)

  • Core algorithm for traversing loader tree and extracting pathname segments
  • Handles complex cases: parallel routes, interception routes, route groups
  • Well-documented with examples and algorithm explanation

Comprehensive Tests: extract-pathname-segments.test.ts (897 lines)

  • Tests for simple cases, nested structures, parallel routes
  • Interception route handling in various configurations
  • Route group behavior and edge cases
  • Depth tracking validation

Integration: static-paths/app.ts

  • Replaced childrenRouteParamSegments with extractPathnameSegments()
  • Updated pathname construction to use segments from parallel routes
  • Maintained backward compatibility

E2E Test: Added test validating prerendering works for intercepted routes

Test Plan

The new E2E test verifies:

it('should prerender a dynamic intercepted route', async () => {
  // Verifies build output contains the prerendered interception route
  expect(next.cliOutput).toContain('/(.)john/1')
  // Verifies it doesn't generate the non-intercepted path
  expect(next.cliOutput).not.toContain('/john/1')
})

Impact

Before: Interception routes with dynamic segments returned 404 when accessed directly, even with generateStaticParams

After: These routes are properly prerendered at build time and return correct responses

@ijjk ijjk added created-by: Next.js team PRs by the Next.js team. tests Turbopack Related to Turbopack with Next.js. type: next labels Nov 5, 2025
@wyattjoh wyattjoh changed the base branch from canary to feat/build-time-route-validation November 5, 2025 22:19
Copy link
Member Author

wyattjoh commented Nov 5, 2025

@wyattjoh wyattjoh changed the base branch from feat/build-time-route-validation to graphite-base/85835 November 6, 2025 22:14
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch from cfe39e2 to 2fd93a8 Compare November 6, 2025 22:20
@wyattjoh wyattjoh force-pushed the graphite-base/85835 branch from 2ca55f9 to 6037878 Compare November 6, 2025 22:20
@wyattjoh wyattjoh changed the base branch from graphite-base/85835 to feat/build-time-route-validation November 6, 2025 22:20
@wyattjoh wyattjoh force-pushed the feat/build-time-route-validation branch from 6037878 to f51fb25 Compare November 6, 2025 22:32
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch from 2fd93a8 to 935136a Compare November 6, 2025 22:32
@ijjk
Copy link
Member

ijjk commented Nov 6, 2025

Failing test suites

Commit: 0940ad1 | About building and testing Next.js

pnpm test-start-turbo test/e2e/app-dir/app-prefetch/prefetching.stale-times.test.ts (turbopack) (job)

  • app dir - prefetching (custom staleTime) > should not re-fetch cached data when navigating back to a route group (DD)
Expand output

● app dir - prefetching (custom staleTime) › should not re-fetch cached data when navigating back to a route group

thrown: "Exceeded timeout of 120000 ms for a test.
Add a timeout value to this test to increase the timeout, if this is a long-running test. See https://jestjs.io/docs/api#testname-fn-timeout."

  128 |   })
  129 |
> 130 |   it('should not re-fetch cached data when navigating back to a route group', async () => {
      |   ^
  131 |     let act: ReturnType<typeof createRouterAct>
  132 |     // Just installing so that the page doesn't automatically move past dynamic stale time
  133 |     createTimeController()

  at it (e2e/app-dir/app-prefetch/prefetching.stale-times.test.ts:130:3)
  at Object.describe (e2e/app-dir/app-prefetch/prefetching.stale-times.test.ts:6:1)

pnpm test-start-turbo test/production/next-server-nft/next-server-nft.test.ts (turbopack) (job)

  • next-server-nft > should not trace too many files in next-server.js.nft.json (DD)
Expand output

● next-server-nft › should not trace too many files in next-server.js.nft.json

expect(received).toMatchInlineSnapshot(snapshot)

Snapshot name: `next-server-nft should not trace too many files in next-server.js.nft.json 1`

- Snapshot  - 4
+ Received  + 1

@@ -5,12 +5,11 @@
    "/node_modules/@next/env/*",
    "/node_modules/@swc/helpers/*",
    "/node_modules/client-only/*",
    "/node_modules/detect-libc/*",
    "/node_modules/next/dist/build/output/log.js",
-   "/node_modules/next/dist/build/segment-config/app/app-segment-config.js",
-   "/node_modules/next/dist/build/segment-config/app/app-segments.js",
+   "/node_modules/next/dist/build/static-paths/app/extract-pathname-route-param-segments-from-loader-tree.js",
    "/node_modules/next/dist/build/static-paths/utils.js",
    "/node_modules/next/dist/client/*",
    "/node_modules/next/dist/compiled/@edge-runtime/cookies/index.js",
    "/node_modules/next/dist/compiled/@hapi/accept/index.js",
    "/node_modules/next/dist/compiled/@mswjs/interceptors/ClientRequest/index.js",
@@ -45,12 +44,10 @@
    "/node_modules/next/dist/compiled/stacktrace-parser/stack-trace-parser.cjs.js",
    "/node_modules/next/dist/compiled/string-hash/index.js",
    "/node_modules/next/dist/compiled/strip-ansi/index.js",
    "/node_modules/next/dist/compiled/superstruct/index.cjs",
    "/node_modules/next/dist/compiled/ws/index.js",
-   "/node_modules/next/dist/compiled/zod-validation-error/index.js",
-   "/node_modules/next/dist/compiled/zod/index.cjs",
    "/node_modules/next/dist/experimental/testmode/context.js",
    "/node_modules/next/dist/experimental/testmode/fetch.js",
    "/node_modules/next/dist/experimental/testmode/httpget.js",
    "/node_modules/next/dist/experimental/testmode/server-edge.js",
    "/node_modules/next/dist/experimental/testmode/server.js",

  114 |       ]
  115 |
> 116 |       expect(traceGrouped).toMatchInlineSnapshot(`
      |                            ^
  117 |        [
  118 |          "/node_modules/@img/colour/*",
  119 |          "/node_modules/@img/sharp-*/sharp-*.node",

  at Object.toMatchInlineSnapshot (production/next-server-nft/next-server-nft.test.ts:116:28)

@ijjk
Copy link
Member

ijjk commented Nov 6, 2025

Stats from current PR

Default Build (Increase detected ⚠️)
General
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
buildDuration 33.1s 31.3s N/A
buildDurationCached 29.5s 25s N/A
nodeModulesSize 455 MB 455 MB N/A
nextStartRea..uration (ms) 734ms 691ms N/A
Client Bundles (main, webpack) Overall increase ⚠️
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
436-HASH.js gzip 5.32 kB 5.32 kB N/A
4779.HASH.js gzip 169 B 169 B
9760-HASH.js gzip 52.3 kB 52.6 kB ⚠️ +338 B
c57d0559-HASH.js gzip 62.3 kB 62.3 kB N/A
framework-HASH.js gzip 59.8 kB 59.8 kB
main-app-HASH.js gzip 252 B 256 B N/A
main-HASH.js gzip 37.5 kB 38.3 kB ⚠️ +857 B
webpack-HASH.js gzip 1.69 kB 1.69 kB
Overall change 151 kB 153 kB ⚠️ +1.2 kB
Legacy Client Bundles (polyfills)
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
polyfills-HASH.js gzip 39.4 kB 39.4 kB
Overall change 39.4 kB 39.4 kB
Client Pages
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
_app-HASH.js gzip 193 B 194 B N/A
_error-HASH.js gzip 182 B 182 B
css-HASH.js gzip 334 B 334 B
dynamic-HASH.js gzip 1.81 kB 1.81 kB N/A
edge-ssr-HASH.js gzip 255 B 254 B N/A
head-HASH.js gzip 350 B 351 B N/A
hooks-HASH.js gzip 384 B 384 B
image-HASH.js gzip 4.78 kB 4.77 kB N/A
index-HASH.js gzip 260 B 259 B N/A
link-HASH.js gzip 2.5 kB 2.5 kB N/A
routerDirect..HASH.js gzip 316 B 320 B N/A
script-HASH.js gzip 388 B 388 B
withRouter-HASH.js gzip 316 B 314 B N/A
1afbb74e6ecf..834.css gzip 106 B 106 B
Overall change 1.39 kB 1.39 kB
Client Build Manifests
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
_buildManifest.js gzip 718 B 720 B N/A
Overall change 0 B 0 B
Rendered Page Sizes
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
index.html gzip 524 B 522 B N/A
link.html gzip 538 B 538 B
withRouter.html gzip 521 B 519 B N/A
Overall change 538 B 538 B
Edge SSR bundle Size
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
edge-ssr.js gzip 126 kB 126 kB N/A
page.js gzip 252 kB 234 kB N/A
Overall change 0 B 0 B
Middleware size Overall increase ⚠️
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
middleware-b..fest.js gzip 635 B 637 B N/A
middleware-r..fest.js gzip 156 B 156 B
middleware.js gzip 32.7 kB 32.9 kB ⚠️ +208 B
edge-runtime..pack.js gzip 846 B 846 B
Overall change 33.7 kB 33.9 kB ⚠️ +208 B
Next Runtimes Overall increase ⚠️
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
app-page-exp...dev.js gzip 296 kB 297 kB ⚠️ +749 B
app-page-exp..prod.js gzip 153 kB 153 kB ⚠️ +627 B
app-page-tur...dev.js gzip 296 kB 296 kB ⚠️ +750 B
app-page-tur..prod.js gzip 153 kB 153 kB ⚠️ +628 B
app-page-tur...dev.js gzip 293 kB 293 kB ⚠️ +747 B
app-page-tur..prod.js gzip 151 kB 151 kB ⚠️ +631 B
app-page.run...dev.js gzip 293 kB 293 kB ⚠️ +749 B
app-page.run..prod.js gzip 151 kB 151 kB ⚠️ +629 B
app-route-ex...dev.js gzip 68.3 kB 68.3 kB
app-route-ex..prod.js gzip 47.2 kB 47.2 kB
app-route-tu...dev.js gzip 68.3 kB 68.3 kB
app-route-tu..prod.js gzip 47.2 kB 47.2 kB
app-route-tu...dev.js gzip 68 kB 68 kB
app-route-tu..prod.js gzip 47 kB 47 kB
app-route.ru...dev.js gzip 68 kB 68 kB
app-route.ru..prod.js gzip 47 kB 47 kB
dist_client_...dev.js gzip 326 B 326 B
dist_client_...dev.js gzip 328 B 328 B
dist_client_...dev.js gzip 320 B 320 B
dist_client_...dev.js gzip 318 B 318 B
pages-api-tu...dev.js gzip 40.9 kB 40.9 kB
pages-api-tu..prod.js gzip 31 kB 31 kB
pages-api.ru...dev.js gzip 40.8 kB 40.8 kB
pages-api.ru..prod.js gzip 30.9 kB 30.9 kB
pages-turbo....dev.js gzip 50.4 kB 50.4 kB
pages-turbo...prod.js gzip 37.8 kB 37.8 kB
pages.runtim...dev.js gzip 50.3 kB 50.3 kB
pages.runtim..prod.js gzip 37.8 kB 37.8 kB
server.runti..prod.js gzip 76.8 kB 60.5 kB N/A
Overall change 2.57 MB 2.57 MB ⚠️ +5.51 kB
build cache
vercel/next.js canary vercel/next.js fix/interception-route-prerendering Change
0.pack gzip 3.23 MB 3.07 MB N/A
index.pack gzip 95.2 kB 93.7 kB N/A
Overall change 0 B 0 B
Diff details
Diff for page.js

Diff too large to display

Diff for middleware.js

Diff too large to display

Diff for edge-ssr.js

Diff too large to display

Diff for dynamic-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [2291],
   {
-    /***/ 1033: /***/ (
+    /***/ 431: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -9,7 +9,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/dynamic",
         function () {
-          return __webpack_require__(6490);
+          return __webpack_require__(8084);
         },
       ]);
       if (false) {
@@ -18,7 +18,7 @@
       /***/
     },
 
-    /***/ 5323: /***/ (
+    /***/ 2699: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -60,7 +60,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       const _react = /*#__PURE__*/ _interop_require_default._(
         __webpack_require__(2223)
       );
-      const _loadablecontextsharedruntime = __webpack_require__(9289);
+      const _loadablecontextsharedruntime = __webpack_require__(3785);
       function resolve(obj) {
         return obj && obj.default ? obj.default : obj;
       }
@@ -293,73 +293,34 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       /***/
     },
 
-    /***/ 6490: /***/ (
+    /***/ 3785: /***/ (
       __unused_webpack_module,
-      __webpack_exports__,
+      exports,
       __webpack_require__
     ) => {
       "use strict";
-      __webpack_require__.r(__webpack_exports__);
-      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
-        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
-        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
-        /* harmony export */
+      /* __next_internal_client_entry_do_not_use__  cjs */
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
       });
-      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
-        __webpack_require__(1503);
-      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(7320);
-      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1___default =
-        /*#__PURE__*/ __webpack_require__.n(
-          next_dynamic__WEBPACK_IMPORTED_MODULE_1__
-        );
-
-      const DynamicHello = next_dynamic__WEBPACK_IMPORTED_MODULE_1___default()(
-        () =>
-          __webpack_require__
-            .e(/* import() */ 4779)
-            .then(__webpack_require__.bind(__webpack_require__, 4779))
-            .then((mod) => mod.Hello),
-        {
-          loadableGenerated: {
-            webpack: () => [/*require.resolve*/ 4779],
-          },
-        }
+      Object.defineProperty(exports, "LoadableContext", {
+        enumerable: true,
+        get: function () {
+          return LoadableContext;
+        },
+      });
+      const _interop_require_default = __webpack_require__(1532);
+      const _react = /*#__PURE__*/ _interop_require_default._(
+        __webpack_require__(2223)
       );
-      const Page = () =>
-        /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(
-          react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragment,
-          {
-            children: [
-              /*#__PURE__*/ (0,
-              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
-                children: "testing next/dynamic size",
-              }),
-              /*#__PURE__*/ (0,
-              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
-                DynamicHello,
-                {}
-              ),
-            ],
-          }
-        );
-      var __N_SSP = true;
-      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = Page;
-
-      /***/
-    },
-
-    /***/ 7320: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(7340);
+      const LoadableContext = _react.default.createContext(null);
+      if (false) {
+      } //# sourceMappingURL=loadable-context.shared-runtime.js.map
 
       /***/
     },
 
-    /***/ 7340: /***/ (module, exports, __webpack_require__) => {
+    /***/ 6828: /***/ (module, exports, __webpack_require__) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -392,7 +353,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
         __webpack_require__(2223)
       );
       const _loadablesharedruntime = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(5323)
+        __webpack_require__(2699)
       );
       const isServerSide = "object" === "undefined";
       // Normalize loader to return the module as form { default: Component } for `React.lazy`.
@@ -492,29 +453,68 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
       /***/
     },
 
-    /***/ 9289: /***/ (
+    /***/ 7514: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(6828);
+
+      /***/
+    },
+
+    /***/ 8084: /***/ (
       __unused_webpack_module,
-      exports,
+      __webpack_exports__,
       __webpack_require__
     ) => {
       "use strict";
-      /* __next_internal_client_entry_do_not_use__  cjs */
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "LoadableContext", {
-        enumerable: true,
-        get: function () {
-          return LoadableContext;
-        },
+      __webpack_require__.r(__webpack_exports__);
+      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
+        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
+        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
+        /* harmony export */
       });
-      const _interop_require_default = __webpack_require__(1532);
-      const _react = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(2223)
+      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
+        __webpack_require__(1503);
+      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1__ =
+        __webpack_require__(7514);
+      /* harmony import */ var next_dynamic__WEBPACK_IMPORTED_MODULE_1___default =
+        /*#__PURE__*/ __webpack_require__.n(
+          next_dynamic__WEBPACK_IMPORTED_MODULE_1__
+        );
+
+      const DynamicHello = next_dynamic__WEBPACK_IMPORTED_MODULE_1___default()(
+        () =>
+          __webpack_require__
+            .e(/* import() */ 9573)
+            .then(__webpack_require__.bind(__webpack_require__, 9573))
+            .then((mod) => mod.Hello),
+        {
+          loadableGenerated: {
+            webpack: () => [/*require.resolve*/ 9573],
+          },
+        }
       );
-      const LoadableContext = _react.default.createContext(null);
-      if (false) {
-      } //# sourceMappingURL=loadable-context.shared-runtime.js.map
+      const Page = () =>
+        /*#__PURE__*/ (0, react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)(
+          react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.Fragment,
+          {
+            children: [
+              /*#__PURE__*/ (0,
+              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("p", {
+                children: "testing next/dynamic size",
+              }),
+              /*#__PURE__*/ (0,
+              react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
+                DynamicHello,
+                {}
+              ),
+            ],
+          }
+        );
+      var __N_SSP = true;
+      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = Page;
 
       /***/
     },
@@ -524,7 +524,7 @@ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(1033)
+      __webpack_exec__(431)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for edge-ssr-HASH.js
@@ -1,24 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [676],
   {
-    /***/ 1819: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/edge-ssr",
-        function () {
-          return __webpack_require__(7521);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 7521: /***/ (
+    /***/ 983: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -37,13 +20,30 @@
 
       /***/
     },
+
+    /***/ 985: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/edge-ssr",
+        function () {
+          return __webpack_require__(983);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(1819)
+      __webpack_exec__(985)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for head-HASH.js
@@ -1,34 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [5350],
   {
-    /***/ 619: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/head",
-        function () {
-          return __webpack_require__(9891);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 7997: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(6705);
-
-      /***/
-    },
-
-    /***/ 9891: /***/ (
+    /***/ 1417: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -43,7 +16,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1503);
       /* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(7997);
+        __webpack_require__(5171);
       /* harmony import */ var next_head__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_head__WEBPACK_IMPORTED_MODULE_1__
@@ -76,13 +49,40 @@
 
       /***/
     },
+
+    /***/ 1937: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/head",
+        function () {
+          return __webpack_require__(1417);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 5171: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(7505);
+
+      /***/
+    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(619)
+      __webpack_exec__(1937)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for hooks-HASH.js
@@ -1,24 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [9804],
   {
-    /***/ 1679: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/hooks",
-        function () {
-          return __webpack_require__(4655);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 4655: /***/ (
+    /***/ 1598: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -76,13 +59,30 @@
 
       /***/
     },
+
+    /***/ 3925: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/hooks",
+        function () {
+          return __webpack_require__(1598);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(1679)
+      __webpack_exec__(3925)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for image-HASH.js
@@ -1,24 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [2983],
   {
-    /***/ 797: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/image",
-        function () {
-          return __webpack_require__(5999);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 1713: /***/ (__unused_webpack_module, exports) => {
+    /***/ 881: /***/ (__unused_webpack_module, exports) => {
       "use strict";
       /**
        * A shared function, used on both client and server, to generate a SVG blur placeholder.
@@ -58,7 +41,7 @@
       /***/
     },
 
-    /***/ 2263: /***/ (module, exports, __webpack_require__) => {
+    /***/ 1511: /***/ (module, exports, __webpack_require__) => {
       "use strict";
 
       Object.defineProperty(exports, "__esModule", {
@@ -136,7 +119,137 @@
       /***/
     },
 
-    /***/ 2728: /***/ (module, exports, __webpack_require__) => {
+    /***/ 1744: /***/ (
+      __unused_webpack_module,
+      exports,
+      __webpack_require__
+    ) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "default", {
+        enumerable: true,
+        get: function () {
+          return _default;
+        },
+      });
+      const _findclosestquality = __webpack_require__(7054);
+      function defaultLoader({ config, src, width, quality }) {
+        if (
+          src.startsWith("/") &&
+          src.includes("?") &&
+          config.localPatterns?.length === 1 &&
+          config.localPatterns[0].pathname === "**" &&
+          config.localPatterns[0].search === ""
+        ) {
+          throw Object.defineProperty(
+            new Error(
+              `Image with src "${src}" is using a query string which is not configured in images.localPatterns.` +
+                `\nRead more: https://nextjs.org/docs/messages/next-image-unconfigured-localpatterns`
+            ),
+            "__NEXT_ERROR_CODE",
+            {
+              value: "E871",
+              enumerable: false,
+              configurable: true,
+            }
+          );
+        }
+        if (false) {
+        }
+        const q = (0, _findclosestquality.findClosestQuality)(quality, config);
+        return `${config.path}?url=${encodeURIComponent(
+          src
+        )}&w=${width}&q=${q}${
+          src.startsWith("/_next/static/media/") && false ? 0 : ""
+        }`;
+      }
+      // We use this to determine if the import is the default loader
+      // or a custom loader defined by the user in next.config.js
+      defaultLoader.__next_img_default = true;
+      const _default = defaultLoader; //# sourceMappingURL=image-loader.js.map
+
+      /***/
+    },
+
+    /***/ 2388: /***/ (
+      __unused_webpack_module,
+      __webpack_exports__,
+      __webpack_require__
+    ) => {
+      "use strict";
+      // ESM COMPAT FLAG
+      __webpack_require__.r(__webpack_exports__);
+
+      // EXPORTS
+      __webpack_require__.d(__webpack_exports__, {
+        __N_SSP: () => /* binding */ __N_SSP,
+        default: () => /* binding */ pages_image,
+      });
+
+      // EXTERNAL MODULE: ./node_modules/.pnpm/react@19.2.0/node_modules/react/jsx-runtime.js
+      var jsx_runtime = __webpack_require__(1503);
+      // EXTERNAL MODULE: ./node_modules/.pnpm/next@file+..+diff-repo+packages+next+next-packed.tgz_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/next/image.js
+      var next_image = __webpack_require__(3866);
+      var image_default = /*#__PURE__*/ __webpack_require__.n(next_image); // ./pages/nextjs.png
+      /* harmony default export */ const nextjs = {
+        src: "/_next/static/media/nextjs.cae0b805.png",
+        height: 1347,
+        width: 1626,
+        blurDataURL:
+          "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAHCAMAAAACh/xsAAAAD1BMVEX////x8fH6+vrb29vo6Oh8o70bAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAH0lEQVR4nGNgwARMjIyMjCAGCzMzMwsTRISJCcRABwAEcAAkLCQfgAAAAABJRU5ErkJggg==",
+        blurWidth: 8,
+        blurHeight: 7,
+      }; // ./pages/image.js
+      function ImagePage(props) {
+        return /*#__PURE__*/ (0, jsx_runtime.jsxs)(jsx_runtime.Fragment, {
+          children: [
+            /*#__PURE__*/ (0, jsx_runtime.jsx)("h1", {
+              children: "next/image example",
+            }),
+            /*#__PURE__*/ (0, jsx_runtime.jsx)(image_default(), {
+              src: nextjs,
+              placeholder: "blur",
+            }),
+          ],
+        });
+      }
+      var __N_SSP = true;
+      /* harmony default export */ const pages_image = ImagePage;
+
+      /***/
+    },
+
+    /***/ 3866: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(6888);
+
+      /***/
+    },
+
+    /***/ 4483: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/image",
+        function () {
+          return __webpack_require__(2388);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 6600: /***/ (module, exports, __webpack_require__) => {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -158,17 +271,17 @@
         __webpack_require__(9507)
       );
       const _head = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(6705)
+        __webpack_require__(7505)
       );
-      const _getimgprops = __webpack_require__(3556);
-      const _imageconfig = __webpack_require__(3157);
-      const _imageconfigcontextsharedruntime = __webpack_require__(9323);
-      const _warnonce = __webpack_require__(6173);
-      const _routercontextsharedruntime = __webpack_require__(6046);
+      const _getimgprops = __webpack_require__(9588);
+      const _imageconfig = __webpack_require__(2645);
+      const _imageconfigcontextsharedruntime = __webpack_require__(5451);
+      const _warnonce = __webpack_require__(7549);
+      const _routercontextsharedruntime = __webpack_require__(5470);
       const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3744)
+        __webpack_require__(1744)
       );
-      const _usemergedref = __webpack_require__(2263);
+      const _usemergedref = __webpack_require__(1511);
       // This is replaced by webpack define plugin
       const configEnv = {
         deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
@@ -495,7 +608,96 @@
       /***/
     },
 
-    /***/ 3556: /***/ (
+    /***/ 6888: /***/ (
+      __unused_webpack_module,
+      exports,
+      __webpack_require__
+    ) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      0 && 0;
+      function _export(target, all) {
+        for (var name in all)
+          Object.defineProperty(target, name, {
+            enumerable: true,
+            get: all[name],
+          });
+      }
+      _export(exports, {
+        default: function () {
+          return _default;
+        },
+        getImageProps: function () {
+          return getImageProps;
+        },
+      });
+      const _interop_require_default = __webpack_require__(1532);
+      const _getimgprops = __webpack_require__(9588);
+      const _imagecomponent = __webpack_require__(6600);
+      const _imageloader = /*#__PURE__*/ _interop_require_default._(
+        __webpack_require__(1744)
+      );
+      function getImageProps(imgProps) {
+        const { props } = (0, _getimgprops.getImgProps)(imgProps, {
+          defaultLoader: _imageloader.default,
+          // This is replaced by webpack define plugin
+          imgConf: {
+            deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
+            imageSizes: [32, 48, 64, 96, 128, 256, 384],
+            qualities: [75],
+            path: "/_next/image",
+            loader: "default",
+            dangerouslyAllowSVG: false,
+            unoptimized: false,
+          },
+        });
+        // Normally we don't care about undefined props because we pass to JSX,
+        // but this exported function could be used by the end user for anything
+        // so we delete undefined props to clean it up a little.
+        for (const [key, value] of Object.entries(props)) {
+          if (value === undefined) {
+            delete props[key];
+          }
+        }
+        return {
+          props,
+        };
+      }
+      const _default = _imagecomponent.Image; //# sourceMappingURL=image-external.js.map
+
+      /***/
+    },
+
+    /***/ 7054: /***/ (__unused_webpack_module, exports) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "findClosestQuality", {
+        enumerable: true,
+        get: function () {
+          return findClosestQuality;
+        },
+      });
+      function findClosestQuality(quality, config) {
+        const q = quality || 75;
+        if (!config?.qualities?.length) {
+          return q;
+        }
+        return config.qualities.reduce(
+          (prev, cur) => (Math.abs(cur - q) < Math.abs(prev - q) ? cur : prev),
+          0
+        );
+      } //# sourceMappingURL=find-closest-quality.js.map
+
+      /***/
+    },
+
+    /***/ 9588: /***/ (
       __unused_webpack_module,
       exports,
       __webpack_require__
@@ -511,9 +713,9 @@
           return getImgProps;
         },
       });
-      const _warnonce = __webpack_require__(6173);
-      const _imageblursvg = __webpack_require__(1713);
-      const _imageconfig = __webpack_require__(3157);
+      const _warnonce = __webpack_require__(7549);
+      const _imageblursvg = __webpack_require__(881);
+      const _imageconfig = __webpack_require__(2645);
       const VALID_LOADING_VALUES =
         /* unused pure expression or super */ null && [
           "lazy",
@@ -943,215 +1145,13 @@
 
       /***/
     },
-
-    /***/ 3744: /***/ (
-      __unused_webpack_module,
-      exports,
-      __webpack_require__
-    ) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "default", {
-        enumerable: true,
-        get: function () {
-          return _default;
-        },
-      });
-      const _findclosestquality = __webpack_require__(8494);
-      function defaultLoader({ config, src, width, quality }) {
-        if (
-          src.startsWith("/") &&
-          src.includes("?") &&
-          config.localPatterns?.length === 1 &&
-          config.localPatterns[0].pathname === "**" &&
-          config.localPatterns[0].search === ""
-        ) {
-          throw Object.defineProperty(
-            new Error(
-              `Image with src "${src}" is using a query string which is not configured in images.localPatterns.` +
-                `\nRead more: https://nextjs.org/docs/messages/next-image-unconfigured-localpatterns`
-            ),
-            "__NEXT_ERROR_CODE",
-            {
-              value: "E871",
-              enumerable: false,
-              configurable: true,
-            }
-          );
-        }
-        if (false) {
-        }
-        const q = (0, _findclosestquality.findClosestQuality)(quality, config);
-        return `${config.path}?url=${encodeURIComponent(
-          src
-        )}&w=${width}&q=${q}${
-          src.startsWith("/_next/static/media/") && false ? 0 : ""
-        }`;
-      }
-      // We use this to determine if the import is the default loader
-      // or a custom loader defined by the user in next.config.js
-      defaultLoader.__next_img_default = true;
-      const _default = defaultLoader; //# sourceMappingURL=image-loader.js.map
-
-      /***/
-    },
-
-    /***/ 4292: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(7896);
-
-      /***/
-    },
-
-    /***/ 5999: /***/ (
-      __unused_webpack_module,
-      __webpack_exports__,
-      __webpack_require__
-    ) => {
-      "use strict";
-      // ESM COMPAT FLAG
-      __webpack_require__.r(__webpack_exports__);
-
-      // EXPORTS
-      __webpack_require__.d(__webpack_exports__, {
-        __N_SSP: () => /* binding */ __N_SSP,
-        default: () => /* binding */ pages_image,
-      });
-
-      // EXTERNAL MODULE: ./node_modules/.pnpm/react@19.2.0/node_modules/react/jsx-runtime.js
-      var jsx_runtime = __webpack_require__(1503);
-      // EXTERNAL MODULE: ./node_modules/.pnpm/next@file+..+main-repo+packages+next+next-packed.tgz_react-dom@19.2.0_react@19.2.0__react@19.2.0/node_modules/next/image.js
-      var next_image = __webpack_require__(4292);
-      var image_default = /*#__PURE__*/ __webpack_require__.n(next_image); // ./pages/nextjs.png
-      /* harmony default export */ const nextjs = {
-        src: "/_next/static/media/nextjs.cae0b805.png",
-        height: 1347,
-        width: 1626,
-        blurDataURL:
-          "data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAHCAMAAAACh/xsAAAAD1BMVEX////x8fH6+vrb29vo6Oh8o70bAAAACXBIWXMAAAsTAAALEwEAmpwYAAAAH0lEQVR4nGNgwARMjIyMjCAGCzMzMwsTRISJCcRABwAEcAAkLCQfgAAAAABJRU5ErkJggg==",
-        blurWidth: 8,
-        blurHeight: 7,
-      }; // ./pages/image.js
-      function ImagePage(props) {
-        return /*#__PURE__*/ (0, jsx_runtime.jsxs)(jsx_runtime.Fragment, {
-          children: [
-            /*#__PURE__*/ (0, jsx_runtime.jsx)("h1", {
-              children: "next/image example",
-            }),
-            /*#__PURE__*/ (0, jsx_runtime.jsx)(image_default(), {
-              src: nextjs,
-              placeholder: "blur",
-            }),
-          ],
-        });
-      }
-      var __N_SSP = true;
-      /* harmony default export */ const pages_image = ImagePage;
-
-      /***/
-    },
-
-    /***/ 7896: /***/ (
-      __unused_webpack_module,
-      exports,
-      __webpack_require__
-    ) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      0 && 0;
-      function _export(target, all) {
-        for (var name in all)
-          Object.defineProperty(target, name, {
-            enumerable: true,
-            get: all[name],
-          });
-      }
-      _export(exports, {
-        default: function () {
-          return _default;
-        },
-        getImageProps: function () {
-          return getImageProps;
-        },
-      });
-      const _interop_require_default = __webpack_require__(1532);
-      const _getimgprops = __webpack_require__(3556);
-      const _imagecomponent = __webpack_require__(2728);
-      const _imageloader = /*#__PURE__*/ _interop_require_default._(
-        __webpack_require__(3744)
-      );
-      function getImageProps(imgProps) {
-        const { props } = (0, _getimgprops.getImgProps)(imgProps, {
-          defaultLoader: _imageloader.default,
-          // This is replaced by webpack define plugin
-          imgConf: {
-            deviceSizes: [640, 750, 828, 1080, 1200, 1920, 2048, 3840],
-            imageSizes: [32, 48, 64, 96, 128, 256, 384],
-            qualities: [75],
-            path: "/_next/image",
-            loader: "default",
-            dangerouslyAllowSVG: false,
-            unoptimized: false,
-          },
-        });
-        // Normally we don't care about undefined props because we pass to JSX,
-        // but this exported function could be used by the end user for anything
-        // so we delete undefined props to clean it up a little.
-        for (const [key, value] of Object.entries(props)) {
-          if (value === undefined) {
-            delete props[key];
-          }
-        }
-        return {
-          props,
-        };
-      }
-      const _default = _imagecomponent.Image; //# sourceMappingURL=image-external.js.map
-
-      /***/
-    },
-
-    /***/ 8494: /***/ (__unused_webpack_module, exports) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "findClosestQuality", {
-        enumerable: true,
-        get: function () {
-          return findClosestQuality;
-        },
-      });
-      function findClosestQuality(quality, config) {
-        const q = quality || 75;
-        if (!config?.qualities?.length) {
-          return q;
-        }
-        return config.qualities.reduce(
-          (prev, cur) => (Math.abs(cur - q) < Math.abs(prev - q) ? cur : prev),
-          0
-        );
-      } //# sourceMappingURL=find-closest-quality.js.map
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(797)
+      __webpack_exec__(4483)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for link-HASH.js
@@ -1,7 +1,338 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [4672],
   {
-    /***/ 69: /***/ (module, exports, __webpack_require__) => {
+    /***/ 1511: /***/ (module, exports, __webpack_require__) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "useMergedRef", {
+        enumerable: true,
+        get: function () {
+          return useMergedRef;
+        },
+      });
+      const _react = __webpack_require__(2223);
+      function useMergedRef(refA, refB) {
+        const cleanupA = (0, _react.useRef)(null);
+        const cleanupB = (0, _react.useRef)(null);
+        // NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
+        // (this happens often if the user doesn't pass a ref to Link/Form/Image)
+        // But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
+        // and the user might pass that ref into ref-merging library that doesn't support cleanup refs
+        // (because it hasn't been updated for React 19)
+        // which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
+        // So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
+        return (0, _react.useCallback)(
+          (current) => {
+            if (current === null) {
+              const cleanupFnA = cleanupA.current;
+              if (cleanupFnA) {
+                cleanupA.current = null;
+                cleanupFnA();
+              }
+              const cleanupFnB = cleanupB.current;
+              if (cleanupFnB) {
+                cleanupB.current = null;
+                cleanupFnB();
+              }
+            } else {
+              if (refA) {
+                cleanupA.current = applyRef(refA, current);
+              }
+              if (refB) {
+                cleanupB.current = applyRef(refB, current);
+              }
+            }
+          },
+          [refA, refB]
+        );
+      }
+      function applyRef(refA, current) {
+        if (typeof refA === "function") {
+          const cleanup = refA(current);
+          if (typeof cleanup === "function") {
+            return cleanup;
+          } else {
+            return () => refA(null);
+          }
+        } else {
+          refA.current = current;
+          return () => {
+            refA.current = null;
+          };
+        }
+      }
+      if (
+        (typeof exports.default === "function" ||
+          (typeof exports.default === "object" && exports.default !== null)) &&
+        typeof exports.default.__esModule === "undefined"
+      ) {
+        Object.defineProperty(exports.default, "__esModule", {
+          value: true,
+        });
+        Object.assign(exports.default, exports);
+        module.exports = exports.default;
+      } //# sourceMappingURL=use-merged-ref.js.map
+
+      /***/
+    },
+
+    /***/ 2025: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/link",
+        function () {
+          return __webpack_require__(4591);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 3267: /***/ (module, exports, __webpack_require__) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "getDomainLocale", {
+        enumerable: true,
+        get: function () {
+          return getDomainLocale;
+        },
+      });
+      const _normalizetrailingslash = __webpack_require__(2371);
+      const basePath =
+        /* unused pure expression or super */ null && (false || "");
+      function getDomainLocale(path, locale, locales, domainLocales) {
+        if (false) {
+        } else {
+          return false;
+        }
+      }
+      if (
+        (typeof exports.default === "function" ||
+          (typeof exports.default === "object" && exports.default !== null)) &&
+        typeof exports.default.__esModule === "undefined"
+      ) {
+        Object.defineProperty(exports.default, "__esModule", {
+          value: true,
+        });
+        Object.assign(exports.default, exports);
+        module.exports = exports.default;
+      } //# sourceMappingURL=get-domain-locale.js.map
+
+      /***/
+    },
+
+    /***/ 4591: /***/ (
+      __unused_webpack_module,
+      __webpack_exports__,
+      __webpack_require__
+    ) => {
+      "use strict";
+      __webpack_require__.r(__webpack_exports__);
+      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
+        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
+        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
+        /* harmony export */
+      });
+      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
+        __webpack_require__(1503);
+      /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1__ =
+        __webpack_require__(6929);
+      /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1___default =
+        /*#__PURE__*/ __webpack_require__.n(
+          next_link__WEBPACK_IMPORTED_MODULE_1__
+        );
+
+      function aLink(props) {
+        return /*#__PURE__*/ (0,
+        react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
+          children: [
+            /*#__PURE__*/ (0,
+            react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h3", {
+              children: "A Link page!",
+            }),
+            /*#__PURE__*/ (0,
+            react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
+              next_link__WEBPACK_IMPORTED_MODULE_1___default(),
+              {
+                href: "/",
+                children: "Go to /",
+              }
+            ),
+          ],
+        });
+      }
+      var __N_SSP = true;
+      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = aLink;
+
+      /***/
+    },
+
+    /***/ 6929: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(8885);
+
+      /***/
+    },
+
+    /***/ 7686: /***/ (module, exports, __webpack_require__) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "useIntersection", {
+        enumerable: true,
+        get: function () {
+          return useIntersection;
+        },
+      });
+      const _react = __webpack_require__(2223);
+      const _requestidlecallback = __webpack_require__(901);
+      const hasIntersectionObserver =
+        typeof IntersectionObserver === "function";
+      const observers = new Map();
+      const idList = [];
+      function createObserver(options) {
+        const id = {
+          root: options.root || null,
+          margin: options.rootMargin || "",
+        };
+        const existing = idList.find(
+          (obj) => obj.root === id.root && obj.margin === id.margin
+        );
+        let instance;
+        if (existing) {
+          instance = observers.get(existing);
+          if (instance) {
+            return instance;
+          }
+        }
+        const elements = new Map();
+        const observer = new IntersectionObserver((entries) => {
+          entries.forEach((entry) => {
+            const callback = elements.get(entry.target);
+            const isVisible =
+              entry.isIntersecting || entry.intersectionRatio > 0;
+            if (callback && isVisible) {
+              callback(isVisible);
+            }
+          });
+        }, options);
+        instance = {
+          id,
+          observer,
+          elements,
+        };
+        idList.push(id);
+        observers.set(id, instance);
+        return instance;
+      }
+      function observe(element, callback, options) {
+        const { id, observer, elements } = createObserver(options);
+        elements.set(element, callback);
+        observer.observe(element);
+        return function unobserve() {
+          elements.delete(element);
+          observer.unobserve(element);
+          // Destroy observer when there's nothing left to watch:
+          if (elements.size === 0) {
+            observer.disconnect();
+            observers.delete(id);
+            const index = idList.findIndex(
+              (obj) => obj.root === id.root && obj.margin === id.margin
+            );
+            if (index > -1) {
+              idList.splice(index, 1);
+            }
+          }
+        };
+      }
+      function useIntersection({ rootRef, rootMargin, disabled }) {
+        const isDisabled = disabled || !hasIntersectionObserver;
+        const [visible, setVisible] = (0, _react.useState)(false);
+        const elementRef = (0, _react.useRef)(null);
+        const setElement = (0, _react.useCallback)((element) => {
+          elementRef.current = element;
+        }, []);
+        (0, _react.useEffect)(() => {
+          if (hasIntersectionObserver) {
+            if (isDisabled || visible) return;
+            const element = elementRef.current;
+            if (element && element.tagName) {
+              const unobserve = observe(
+                element,
+                (isVisible) => isVisible && setVisible(isVisible),
+                {
+                  root: rootRef?.current,
+                  rootMargin,
+                }
+              );
+              return unobserve;
+            }
+          } else {
+            if (!visible) {
+              const idleCallback = (0,
+              _requestidlecallback.requestIdleCallback)(() => setVisible(true));
+              return () =>
+                (0, _requestidlecallback.cancelIdleCallback)(idleCallback);
+            }
+          }
+          // eslint-disable-next-line react-hooks/exhaustive-deps
+        }, [isDisabled, rootMargin, rootRef, visible, elementRef.current]);
+        const resetVisible = (0, _react.useCallback)(() => {
+          setVisible(false);
+        }, []);
+        return [setElement, visible, resetVisible];
+      }
+      if (
+        (typeof exports.default === "function" ||
+          (typeof exports.default === "object" && exports.default !== null)) &&
+        typeof exports.default.__esModule === "undefined"
+      ) {
+        Object.defineProperty(exports.default, "__esModule", {
+          value: true,
+        });
+        Object.assign(exports.default, exports);
+        module.exports = exports.default;
+      } //# sourceMappingURL=use-intersection.js.map
+
+      /***/
+    },
+
+    /***/ 8101: /***/ (__unused_webpack_module, exports) => {
+      "use strict";
+
+      Object.defineProperty(exports, "__esModule", {
+        value: true,
+      });
+      Object.defineProperty(exports, "errorOnce", {
+        enumerable: true,
+        get: function () {
+          return errorOnce;
+        },
+      });
+      let errorOnce = (_) => {};
+      if (false) {
+      } //# sourceMappingURL=error-once.js.map
+
+      /***/
+    },
+
+    /***/ 8885: /***/ (module, exports, __webpack_require__) => {
       "use strict";
       /* __next_internal_client_entry_do_not_use__  cjs */
       Object.defineProperty(exports, "__esModule", {
@@ -28,17 +359,17 @@
       const _react = /*#__PURE__*/ _interop_require_wildcard._(
         __webpack_require__(2223)
       );
-      const _resolvehref = __webpack_require__(2275);
-      const _islocalurl = __webpack_require__(3179);
-      const _formaturl = __webpack_require__(5486);
-      const _utils = __webpack_require__(3708);
-      const _addlocale = __webpack_require__(8225);
-      const _routercontextsharedruntime = __webpack_require__(6046);
-      const _useintersection = __webpack_require__(2678);
-      const _getdomainlocale = __webpack_require__(4499);
-      const _addbasepath = __webpack_require__(7434);
-      const _usemergedref = __webpack_require__(2263);
-      const _erroronce = __webpack_require__(2197);
+      const _resolvehref = __webpack_require__(7379);
+      const _islocalurl = __webpack_require__(4843);
+      const _formaturl = __webpack_require__(9374);
+      const _utils = __webpack_require__(3116);
+      const _addlocale = __webpack_require__(8065);
+      const _routercontextsharedruntime = __webpack_require__(5470);
+      const _useintersection = __webpack_require__(7686);
+      const _getdomainlocale = __webpack_require__(3267);
+      const _addbasepath = __webpack_require__(1450);
+      const _usemergedref = __webpack_require__(1511);
+      const _erroronce = __webpack_require__(8101);
       const prefetched = new Set();
       function prefetch(router, href, as, options) {
         if (false) {
@@ -416,344 +747,13 @@
 
       /***/
     },
-
-    /***/ 2197: /***/ (__unused_webpack_module, exports) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "errorOnce", {
-        enumerable: true,
-        get: function () {
-          return errorOnce;
-        },
-      });
-      let errorOnce = (_) => {};
-      if (false) {
-      } //# sourceMappingURL=error-once.js.map
-
-      /***/
-    },
-
-    /***/ 2263: /***/ (module, exports, __webpack_require__) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "useMergedRef", {
-        enumerable: true,
-        get: function () {
-          return useMergedRef;
-        },
-      });
-      const _react = __webpack_require__(2223);
-      function useMergedRef(refA, refB) {
-        const cleanupA = (0, _react.useRef)(null);
-        const cleanupB = (0, _react.useRef)(null);
-        // NOTE: In theory, we could skip the wrapping if only one of the refs is non-null.
-        // (this happens often if the user doesn't pass a ref to Link/Form/Image)
-        // But this can cause us to leak a cleanup-ref into user code (previously via `<Link legacyBehavior>`),
-        // and the user might pass that ref into ref-merging library that doesn't support cleanup refs
-        // (because it hasn't been updated for React 19)
-        // which can then cause things to blow up, because a cleanup-returning ref gets called with `null`.
-        // So in practice, it's safer to be defensive and always wrap the ref, even on React 19.
-        return (0, _react.useCallback)(
-          (current) => {
-            if (current === null) {
-              const cleanupFnA = cleanupA.current;
-              if (cleanupFnA) {
-                cleanupA.current = null;
-                cleanupFnA();
-              }
-              const cleanupFnB = cleanupB.current;
-              if (cleanupFnB) {
-                cleanupB.current = null;
-                cleanupFnB();
-              }
-            } else {
-              if (refA) {
-                cleanupA.current = applyRef(refA, current);
-              }
-              if (refB) {
-                cleanupB.current = applyRef(refB, current);
-              }
-            }
-          },
-          [refA, refB]
-        );
-      }
-      function applyRef(refA, current) {
-        if (typeof refA === "function") {
-          const cleanup = refA(current);
-          if (typeof cleanup === "function") {
-            return cleanup;
-          } else {
-            return () => refA(null);
-          }
-        } else {
-          refA.current = current;
-          return () => {
-            refA.current = null;
-          };
-        }
-      }
-      if (
-        (typeof exports.default === "function" ||
-          (typeof exports.default === "object" && exports.default !== null)) &&
-        typeof exports.default.__esModule === "undefined"
-      ) {
-        Object.defineProperty(exports.default, "__esModule", {
-          value: true,
-        });
-        Object.assign(exports.default, exports);
-        module.exports = exports.default;
-      } //# sourceMappingURL=use-merged-ref.js.map
-
-      /***/
-    },
-
-    /***/ 2369: /***/ (
-      __unused_webpack_module,
-      __webpack_exports__,
-      __webpack_require__
-    ) => {
-      "use strict";
-      __webpack_require__.r(__webpack_exports__);
-      /* harmony export */ __webpack_require__.d(__webpack_exports__, {
-        /* harmony export */ __N_SSP: () => /* binding */ __N_SSP,
-        /* harmony export */ default: () => __WEBPACK_DEFAULT_EXPORT__,
-        /* harmony export */
-      });
-      /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
-        __webpack_require__(1503);
-      /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(6691);
-      /* harmony import */ var next_link__WEBPACK_IMPORTED_MODULE_1___default =
-        /*#__PURE__*/ __webpack_require__.n(
-          next_link__WEBPACK_IMPORTED_MODULE_1__
-        );
-
-      function aLink(props) {
-        return /*#__PURE__*/ (0,
-        react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsxs)("div", {
-          children: [
-            /*#__PURE__*/ (0,
-            react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)("h3", {
-              children: "A Link page!",
-            }),
-            /*#__PURE__*/ (0,
-            react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__.jsx)(
-              next_link__WEBPACK_IMPORTED_MODULE_1___default(),
-              {
-                href: "/",
-                children: "Go to /",
-              }
-            ),
-          ],
-        });
-      }
-      var __N_SSP = true;
-      /* harmony default export */ const __WEBPACK_DEFAULT_EXPORT__ = aLink;
-
-      /***/
-    },
-
-    /***/ 2678: /***/ (module, exports, __webpack_require__) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "useIntersection", {
-        enumerable: true,
-        get: function () {
-          return useIntersection;
-        },
-      });
-      const _react = __webpack_require__(2223);
-      const _requestidlecallback = __webpack_require__(4213);
-      const hasIntersectionObserver =
-        typeof IntersectionObserver === "function";
-      const observers = new Map();
-      const idList = [];
-      function createObserver(options) {
-        const id = {
-          root: options.root || null,
-          margin: options.rootMargin || "",
-        };
-        const existing = idList.find(
-          (obj) => obj.root === id.root && obj.margin === id.margin
-        );
-        let instance;
-        if (existing) {
-          instance = observers.get(existing);
-          if (instance) {
-            return instance;
-          }
-        }
-        const elements = new Map();
-        const observer = new IntersectionObserver((entries) => {
-          entries.forEach((entry) => {
-            const callback = elements.get(entry.target);
-            const isVisible =
-              entry.isIntersecting || entry.intersectionRatio > 0;
-            if (callback && isVisible) {
-              callback(isVisible);
-            }
-          });
-        }, options);
-        instance = {
-          id,
-          observer,
-          elements,
-        };
-        idList.push(id);
-        observers.set(id, instance);
-        return instance;
-      }
-      function observe(element, callback, options) {
-        const { id, observer, elements } = createObserver(options);
-        elements.set(element, callback);
-        observer.observe(element);
-        return function unobserve() {
-          elements.delete(element);
-          observer.unobserve(element);
-          // Destroy observer when there's nothing left to watch:
-          if (elements.size === 0) {
-            observer.disconnect();
-            observers.delete(id);
-            const index = idList.findIndex(
-              (obj) => obj.root === id.root && obj.margin === id.margin
-            );
-            if (index > -1) {
-              idList.splice(index, 1);
-            }
-          }
-        };
-      }
-      function useIntersection({ rootRef, rootMargin, disabled }) {
-        const isDisabled = disabled || !hasIntersectionObserver;
-        const [visible, setVisible] = (0, _react.useState)(false);
-        const elementRef = (0, _react.useRef)(null);
-        const setElement = (0, _react.useCallback)((element) => {
-          elementRef.current = element;
-        }, []);
-        (0, _react.useEffect)(() => {
-          if (hasIntersectionObserver) {
-            if (isDisabled || visible) return;
-            const element = elementRef.current;
-            if (element && element.tagName) {
-              const unobserve = observe(
-                element,
-                (isVisible) => isVisible && setVisible(isVisible),
-                {
-                  root: rootRef?.current,
-                  rootMargin,
-                }
-              );
-              return unobserve;
-            }
-          } else {
-            if (!visible) {
-              const idleCallback = (0,
-              _requestidlecallback.requestIdleCallback)(() => setVisible(true));
-              return () =>
-                (0, _requestidlecallback.cancelIdleCallback)(idleCallback);
-            }
-          }
-          // eslint-disable-next-line react-hooks/exhaustive-deps
-        }, [isDisabled, rootMargin, rootRef, visible, elementRef.current]);
-        const resetVisible = (0, _react.useCallback)(() => {
-          setVisible(false);
-        }, []);
-        return [setElement, visible, resetVisible];
-      }
-      if (
-        (typeof exports.default === "function" ||
-          (typeof exports.default === "object" && exports.default !== null)) &&
-        typeof exports.default.__esModule === "undefined"
-      ) {
-        Object.defineProperty(exports.default, "__esModule", {
-          value: true,
-        });
-        Object.assign(exports.default, exports);
-        module.exports = exports.default;
-      } //# sourceMappingURL=use-intersection.js.map
-
-      /***/
-    },
-
-    /***/ 4499: /***/ (module, exports, __webpack_require__) => {
-      "use strict";
-
-      Object.defineProperty(exports, "__esModule", {
-        value: true,
-      });
-      Object.defineProperty(exports, "getDomainLocale", {
-        enumerable: true,
-        get: function () {
-          return getDomainLocale;
-        },
-      });
-      const _normalizetrailingslash = __webpack_require__(1379);
-      const basePath =
-        /* unused pure expression or super */ null && (false || "");
-      function getDomainLocale(path, locale, locales, domainLocales) {
-        if (false) {
-        } else {
-          return false;
-        }
-      }
-      if (
-        (typeof exports.default === "function" ||
-          (typeof exports.default === "object" && exports.default !== null)) &&
-        typeof exports.default.__esModule === "undefined"
-      ) {
-        Object.defineProperty(exports.default, "__esModule", {
-          value: true,
-        });
-        Object.assign(exports.default, exports);
-        module.exports = exports.default;
-      } //# sourceMappingURL=get-domain-locale.js.map
-
-      /***/
-    },
-
-    /***/ 6691: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(69);
-
-      /***/
-    },
-
-    /***/ 6771: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/link",
-        function () {
-          return __webpack_require__(2369);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(6771)
+      __webpack_exec__(2025)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for routerDirect-HASH.js
@@ -1,7 +1,34 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [188],
   {
-    /***/ 97: /***/ (
+    /***/ 417: /***/ (
+      __unused_webpack_module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      (window.__NEXT_P = window.__NEXT_P || []).push([
+        "/routerDirect",
+        function () {
+          return __webpack_require__(5491);
+        },
+      ]);
+      if (false) {
+      }
+
+      /***/
+    },
+
+    /***/ 1840: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(3252);
+
+      /***/
+    },
+
+    /***/ 5491: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -16,7 +43,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1503);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(7798);
+        __webpack_require__(1840);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_router__WEBPACK_IMPORTED_MODULE_1__
@@ -35,40 +62,13 @@
 
       /***/
     },
-
-    /***/ 4283: /***/ (
-      __unused_webpack_module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      (window.__NEXT_P = window.__NEXT_P || []).push([
-        "/routerDirect",
-        function () {
-          return __webpack_require__(97);
-        },
-      ]);
-      if (false) {
-      }
-
-      /***/
-    },
-
-    /***/ 7798: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(9300);
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(4283)
+      __webpack_exec__(417)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for script-HASH.js
@@ -1,17 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [1209],
   {
-    /***/ 5964: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(2010);
-
-      /***/
-    },
-
-    /***/ 7758: /***/ (
+    /***/ 1312: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -26,7 +16,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1503);
       /* harmony import */ var next_script__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(5964);
+        __webpack_require__(2398);
       /* harmony import */ var next_script__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_script__WEBPACK_IMPORTED_MODULE_1__
@@ -59,7 +49,17 @@
       /***/
     },
 
-    /***/ 8803: /***/ (
+    /***/ 2398: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(8954);
+
+      /***/
+    },
+
+    /***/ 4305: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -67,7 +67,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/script",
         function () {
-          return __webpack_require__(7758);
+          return __webpack_require__(1312);
         },
       ]);
       if (false) {
@@ -81,7 +81,7 @@
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(8803)
+      __webpack_exec__(4305)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for withRouter-HASH.js
@@ -1,7 +1,7 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [3263],
   {
-    /***/ 184: /***/ (
+    /***/ 358: /***/ (
       __unused_webpack_module,
       __webpack_exports__,
       __webpack_require__
@@ -16,7 +16,7 @@
       /* harmony import */ var react_jsx_runtime__WEBPACK_IMPORTED_MODULE_0__ =
         __webpack_require__(1503);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1__ =
-        __webpack_require__(7798);
+        __webpack_require__(1840);
       /* harmony import */ var next_router__WEBPACK_IMPORTED_MODULE_1___default =
         /*#__PURE__*/ __webpack_require__.n(
           next_router__WEBPACK_IMPORTED_MODULE_1__
@@ -35,7 +35,17 @@
       /***/
     },
 
-    /***/ 3163: /***/ (
+    /***/ 1840: /***/ (
+      module,
+      __unused_webpack_exports,
+      __webpack_require__
+    ) => {
+      module.exports = __webpack_require__(3252);
+
+      /***/
+    },
+
+    /***/ 4041: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
@@ -43,7 +53,7 @@
       (window.__NEXT_P = window.__NEXT_P || []).push([
         "/withRouter",
         function () {
-          return __webpack_require__(184);
+          return __webpack_require__(358);
         },
       ]);
       if (false) {
@@ -51,23 +61,13 @@
 
       /***/
     },
-
-    /***/ 7798: /***/ (
-      module,
-      __unused_webpack_exports,
-      __webpack_require__
-    ) => {
-      module.exports = __webpack_require__(9300);
-
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
     /******/ var __webpack_exec__ = (moduleId) =>
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(0, [636, 6593, 8792], () =>
-      __webpack_exec__(3163)
+      __webpack_exec__(4041)
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for 436-HASH.js

Diff too large to display

Diff for 9760-HASH.js
failed to diff
Diff for main-HASH.js

Diff too large to display

Diff for main-app-HASH.js
@@ -1,64 +1,64 @@
 (self["webpackChunk_N_E"] = self["webpackChunk_N_E"] || []).push([
   [4977],
   {
-    /***/ 6363: /***/ (
+    /***/ 2788: /***/ () => {
+      /* (ignored) */
+      /***/
+    },
+
+    /***/ 5671: /***/ (
       __unused_webpack_module,
       __unused_webpack_exports,
       __webpack_require__
     ) => {
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 7126, 23)
+        __webpack_require__.t.bind(__webpack_require__, 844, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 4031, 23)
+        __webpack_require__.t.bind(__webpack_require__, 4365, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 1117, 23)
+        __webpack_require__.t.bind(__webpack_require__, 3903, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 1522, 23)
+        __webpack_require__.t.bind(__webpack_require__, 3100, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 1798, 23)
+        __webpack_require__.t.bind(__webpack_require__, 7364, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 8366, 23)
+        __webpack_require__.t.bind(__webpack_require__, 5632, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 6571, 23)
+        __webpack_require__.t.bind(__webpack_require__, 8889, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 596, 23)
+        __webpack_require__.t.bind(__webpack_require__, 9958, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 1321, 23)
+        __webpack_require__.t.bind(__webpack_require__, 7839, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 724, 23)
+        __webpack_require__.t.bind(__webpack_require__, 1270, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 6999, 23)
+        __webpack_require__.t.bind(__webpack_require__, 1885, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.bind(__webpack_require__, 854)
+        __webpack_require__.bind(__webpack_require__, 4088)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 7752, 23)
+        __webpack_require__.t.bind(__webpack_require__, 9622, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 1201, 23)
+        __webpack_require__.t.bind(__webpack_require__, 995, 23)
       );
       Promise.resolve(/* import() eager */).then(
-        __webpack_require__.t.bind(__webpack_require__, 7823, 23)
+        __webpack_require__.t.bind(__webpack_require__, 8577, 23)
       );
 
       /***/
     },
-
-    /***/ 7854: /***/ () => {
-      /* (ignored) */
-      /***/
-    },
   },
   /******/ (__webpack_require__) => {
     // webpackRuntimeModules
@@ -66,8 +66,8 @@
       __webpack_require__((__webpack_require__.s = moduleId));
     /******/ __webpack_require__.O(
       0,
-      [9137, 9760],
-      () => (__webpack_exec__(4730), __webpack_exec__(6363))
+      [2494, 4936],
+      () => (__webpack_exec__(204), __webpack_exec__(5671))
     );
     /******/ var __webpack_exports__ = __webpack_require__.O();
     /******/ _N_E = __webpack_exports__;
Diff for app-page-exp..ntime.dev.js
failed to diff
Diff for app-page-exp..time.prod.js

Diff too large to display

Diff for app-page-tur..ntime.dev.js
failed to diff
Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page-tur..ntime.dev.js
failed to diff
Diff for app-page-tur..time.prod.js

Diff too large to display

Diff for app-page.runtime.dev.js
failed to diff
Diff for app-page.runtime.prod.js

Diff too large to display

Diff for server.runtime.prod.js

Diff too large to display

Commit: 0940ad1

@wyattjoh wyattjoh force-pushed the feat/build-time-route-validation branch from f51fb25 to 1c41cc6 Compare November 7, 2025 21:50
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch 7 times, most recently from 5085782 to e2400fc Compare November 8, 2025 11:33
@wyattjoh wyattjoh force-pushed the feat/build-time-route-validation branch from 1c41cc6 to 0d30dce Compare November 8, 2025 11:34
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch from e2400fc to eb1b80d Compare November 12, 2025 21:39
@wyattjoh wyattjoh force-pushed the feat/build-time-route-validation branch from 0d30dce to 79a7111 Compare November 12, 2025 21:39
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch from eb1b80d to 0135c5f Compare November 12, 2025 21:45
@wyattjoh wyattjoh force-pushed the feat/build-time-route-validation branch from 79a7111 to 5c7d7b4 Compare November 12, 2025 21:45
@wyattjoh wyattjoh force-pushed the feat/build-time-route-validation branch from cfd7e6a to bba1a78 Compare November 13, 2025 16:50
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch 2 times, most recently from 1f65bed to 01d8412 Compare November 13, 2025 16:58
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch 2 times, most recently from 45149bb to 7fdeaee Compare November 13, 2025 19:45
@wyattjoh wyattjoh force-pushed the feat/build-time-route-validation branch from 6a5b6df to 482ed70 Compare November 13, 2025 20:12
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch 2 times, most recently from 39400b6 to 1bce2ff Compare November 13, 2025 20:37
@wyattjoh wyattjoh force-pushed the feat/build-time-route-validation branch from 482ed70 to ff86a46 Compare November 13, 2025 20:52
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch from 1bce2ff to 36f2100 Compare November 13, 2025 20:52
@wyattjoh wyattjoh force-pushed the feat/build-time-route-validation branch from ff86a46 to b9e46b4 Compare November 13, 2025 21:47
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch 2 times, most recently from 2f07fd3 to 00c52a6 Compare November 13, 2025 22:48
Base automatically changed from feat/build-time-route-validation to canary November 14, 2025 05:28
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch 3 times, most recently from 6743bfb to 9aaac83 Compare November 15, 2025 00:14
…taticParams

Fixes a bug where interception routes in parallel slots could not be prerendered
using generateStaticParams. The previous implementation only examined "children"
segments when building static paths, missing segments from parallel routes that
contribute to the pathname.

Introduces extractPathnameSegments() which:
- Traverses the entire loader tree (children + parallel routes) using BFS
- Correctly tracks URL depth (skipping route groups/parallel markers, including
  interception markers which are actual URL components)
- Validates that static segment prefixes match the target pathname
- Returns ALL segments that contribute to pathname construction

This enables routes like app/@modal/(.)photo/[id] to be prerendered when they
export generateStaticParams, fixing 404 responses that occurred when these
routes were accessed directly.
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch 3 times, most recently from 6afab63 to c86d398 Compare November 17, 2025 18:56
@wyattjoh wyattjoh force-pushed the fix/interception-route-prerendering branch from c86d398 to 0940ad1 Compare November 17, 2025 19:00
Comment on lines +45 to +49
const params: Params = { param1: 'value1', param2: ['a', 'b'] }
const route = parseAppRoute('/some/path', true)
const fallbackRouteParams: FallbackRouteParam[] = []

resolveRouteParamsFromTree(loaderTree, params, route, fallbackRouteParams)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why isn't this actually an error? if we ever got into this state then something went wrong right?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Not an error technically. We don't have the code checking that the values of the parameters match the route, though we could add one, this is verifying that the parameters aren't parsed if they were already defined.

Comment on lines +116 to +120
if (route.dynamicSegments.length === 0) {
throw new InvariantError(
`Expected a dynamic route, but got a static route: ${pathname}`
)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is the code path for dynamic paths, those where generateStaticParams would be present!

@wyattjoh wyattjoh merged commit d7608f7 into canary Nov 18, 2025
404 of 421 checks passed
@wyattjoh wyattjoh deleted the fix/interception-route-prerendering branch November 18, 2025 16:15
Moroshima pushed a commit to Moroshima/next.js that referenced this pull request Nov 20, 2025
…rcel#85835)

## What

Fixes a bug where interception routes in parallel slots could not be
prerendered using `generateStaticParams`, causing 404 responses when
these routes were accessed directly.

## Why

**The Problem:**

Interception routes like `app/@modal/(.)photo/[id]/page.tsx` could not
be prerendered even when they exported `generateStaticParams`. This was
because the static path generation code only examined "children"
segments in the loader tree, completely missing segments from parallel
routes (like `@modal`) that actually contribute to the pathname.

**Root Cause:**

The previous implementation used `childrenRouteParamSegments` which only
traversed the `children` branch of the loader tree:

```typescript
// OLD: Only looked at children
const childrenRouteParamSegments = [...segments from children only...]

// This missed parallel routes like @modal that have dynamic segments
```

For a route structure like:
```
app/
  [username]/
    page.tsx
  @modal/
    (.)[username]/
      [id]/
        page.tsx  // ← This route's segments were MISSED
```

The build system couldn't discover the `[id]` parameter in the parallel
route because it never traversed that branch of the tree.

## How

**Solution:**

Introduces `extractPathnameSegments()` which properly traverses the
ENTIRE loader tree (not just children) to find ALL segments that
contribute to the pathname:

1. **BFS Traversal**: Explores both `children` AND all parallel route
slots (e.g., `@modal`, `@sidebar`)

2. **Depth Tracking**: Correctly tracks URL depth by:
   - Skipping route groups `(marketing)` - not in URL
   - Skipping parallel markers `@modal` - not in URL
   - Including interception markers `(.)photo` - ARE in URL

3. **Prefix Validation**: Ensures static segments match the target
pathname before including dynamic segments

4. **Complete Parameter Discovery**: Returns all segments that
contribute to pathname construction, regardless of which tree branch
they're in

**Example:**

For `app/@modal/(.)photo/[id]/page.tsx`:
- Old: Missed the `[id]` parameter entirely
- New: Discovers `[id]` and enables prerendering with
`generateStaticParams`

## Changes

**New Module**: `extract-pathname-segments.ts` (192 lines)
- Core algorithm for traversing loader tree and extracting pathname
segments
- Handles complex cases: parallel routes, interception routes, route
groups
- Well-documented with examples and algorithm explanation

**Comprehensive Tests**: `extract-pathname-segments.test.ts` (897 lines)
- Tests for simple cases, nested structures, parallel routes
- Interception route handling in various configurations
- Route group behavior and edge cases
- Depth tracking validation

**Integration**: `static-paths/app.ts`
- Replaced `childrenRouteParamSegments` with `extractPathnameSegments()`
- Updated pathname construction to use segments from parallel routes
- Maintained backward compatibility

**E2E Test**: Added test validating prerendering works for intercepted
routes

## Test Plan

The new E2E test verifies:
```typescript
it('should prerender a dynamic intercepted route', async () => {
  // Verifies build output contains the prerendered interception route
  expect(next.cliOutput).toContain('/(.)john/1')
  // Verifies it doesn't generate the non-intercepted path
  expect(next.cliOutput).not.toContain('/john/1')
})
```

## Impact

**Before**: Interception routes with dynamic segments returned 404 when
accessed directly, even with `generateStaticParams`

**After**: These routes are properly prerendered at build time and
return correct responses
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

created-by: Next.js team PRs by the Next.js team. tests Turbopack Related to Turbopack with Next.js. type: next

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants