App Router dynamic-route params were being returned in reversed key
order (deepest segment first) because the recursive trie matcher
mutated result.params on the way back up the call stack.
Fix by collecting param entries into a shared stack during forward
traversal, then materializing the params object from that stack at
the terminal node. This preserves declaration order for Object.keys().
- Shared buildParams helper extracted to routing/utils.ts to unify
code between route-trie.ts and app-optimistic-routing.ts.
- Added tests verifying key order for [category]/[id], [a]/[b]/[c],
mixed static+dynamic, and backtracking-to-catch-all scenarios.
Fixes#1537
* fix(app-router): render optimistic loading shells for dynamic navigation
Dynamic App Router links currently skip automatic prefetch for route patterns with params, so a navigation to a sibling dynamic URL waits for fresh RSC data before committing anything. That diverges from Next.js, where automatic prefetch learns the loading shell for dynamic routes and the router can commit the shell immediately while the full payload is fetched.
The client prefetch path now requests a loading-shell render for dynamic app routes, keeps that entry out of the navigation cache, learns reusable optimistic route templates from settled shell prefetches, and commits a detached optimistic payload for matching dynamic navigations. The server emits shell-only RSC payloads under a separate cache variant so normal navigation data is not polluted.
Tests cover matching semantics, shell-only page wiring, cache key isolation, prefetch cache consumption, Link prefetch behavior, and the upstream Next.js optimistic-routing deploy-suite case.
* chore(app-router): address optimistic routing review