mirror of
https://github.com/vercel/next.js.git
synced 2026-09-20 02:25:18 +08:00
16eab3ac5d
Backports #97128 ("Fix: Optimistic routing bugs leading to repeated prefetch loops") to `next-16-3`. Fixes an infinite prefetch loop in optimistic routing. A proxy that rewrites every URL to inject a leading path segment — i18n with the default locale hidden from the URL, so `/one/two` is served as `/en/one/two` — combined with a fully dynamic target route like `/[locale]/[...pages]`, produces a prefetch that never settles. When a prefetch response came back, the client never checked whether the rendered tree matched the one it had requested. When the params baked into the request were wrong because of the rewrite, the task kept re-attempting to fulfill the missing data and looped forever. The prefetch path now records a dynamic-rewrite marker on the local route definition when the response's tree diverges from the requested one, which disables further optimistic resolution of that route and invalidates the entries derived from it. This is the same strategy normal navigation responses already used, now applied to the prefetch path. A second, related bug is also fixed: parallel routes with conflicting dynamic params at the same level (`@modal/[...catchAll]` next to `[username]`) can't be distinguished by the traversal algorithm, which assumes each segment resolves independently without inspecting sibling branches. When that scenario is detected, optimistic routing is now disabled for the conflicting route rather than binding one branch's URL parts to another branch's params. ### Verification on this branch Cherry-picked from `5942b37a42`. Three source files conflicted, all because `next-16-3` predates the post-16.3.0 refactor that split the client's response decoder into its own module: - `segment-cache/decode-server-response.ts` — this file does not exist on this branch; it was extracted out of `navigation.ts` after 16.3.0. Its change (tracking whether the decoded tree's segment identities diverge from the base request tree) was ported into `navigation.ts`, where the equivalent logic still lives. Canary decodes a compact transport tree and compares segment identities inline during the walk; this branch instead merges the response patch onto the base `FlightRouterState` before converting it to a `RouteTree`. The check is therefore re-expressed as a read-only walk in `convertServerPatchToFullTree` that compares each server patch against the base tree it is applied to, following canary's descent (`convertServerPatchToFullTreeImpl`) exactly and using the same comparison rules: segment identity via `matchSegment`, page segments matched modulo embedded search params, server-filled `__DEFAULT__` segments ignored, and inactive parallel-route branches (those carrying a refresh state in the base) skipped. The result is threaded out through `NavigationSeed.treeDivergedFromBase`. - `segment-cache/cache.ts` — the consuming block in `fetchSegmentPrefetchesUsingDynamicRequest` (mark-as-dynamic-rewrite, invalidate derived entries, reject with immediate expiry) is byte-identical to canary. The surrounding conflicts were only incidental: the `RouteTreeAccumulator` type gaining `treeDivergedFromBase`, and canary's transport-decoder call vs. this branch's `convertRootFlightRouterStateToRouteTree`. Resolved by keeping this branch's calls and adding the new field to the accumulators. Canary's inline `NavigationSeed` construction in `writeDynamicTreeResponseIntoCache` does not apply here because that function delegates to `convertServerPatchToFullTree`, which now carries the field. - `router-reducer/create-initial-router-state.ts` — a one-line accumulator initializer; kept this branch's `convertRootFlightRouterStateToRouteTree` call and added `treeDivergedFromBase: false`. The other touched source files applied cleanly and are byte-identical to canary: `segment-cache/optimistic-routes.ts` (the parallel-route conflict detection) and `route-params.ts` (exporting `canonicalizeURLPart`), along with `ppr-navigations.ts`, the small cleanly-applied hunk in `navigation.ts`, and every test fixture. `tsc` passes on the `next` package for all files touched here. Behavioral verification is left to CI on this branch; the change carries the `proxy-prefix-rewrite-prefetch-loop` and `modal-catchall-sibling-dynamic-prefetch-loop` end-to-end suites plus additions to the `optimistic-routing` suite. Opened as a draft because the `decode-server-response.ts` → `navigation.ts` port is a manual re-implementation across the refactor boundary rather than a mechanical cherry-pick. The divergence walk in `convertServerPatchToFullTree` is the part most worth a close review. <!-- NEXT_JS_LLM -->