Fix medium-severity audit findings across router, hooks, and matcher#2
Merged
Conversation
… matcher - Fix useSearchParams subscription tearing by reading from UrlContext (React state) instead of mutable navigation.currentEntry - Fix setSearchParams dropping hash fragment by appending url.hash - Fix canGoBack/canGoForward not reactive by subscribing to currententrychange event in useBack and useForward hooks - Add redirect cycle detection in createRouter for static targets - Guard route builder against reuse after terminal methods - Throw on duplicate route registration in matcher - Add prefetch deduplication via per-matcher WeakMap cache - Fix NavigationContext typed as non-null but defaulting to null - Export UrlContext from barrel BREAKING CHANGE: NavigationContext now typed as Context<Navigation | null> instead of Context<Navigation>. Code using use(NavigationContext) directly must handle the null case. The useNavigation() hook already throws a descriptive error on null, so hook consumers are unaffected. BREAKING CHANGE: matcher.register() now throws on duplicate pattern registration instead of silently overwriting. This prevents accidental route shadowing that previously caused hard-to-debug routing bugs. BREAKING CHANGE: RouteBuilder methods now throw when called after a terminal method (.render(), .redirect(), .group()). Previously the builder could be silently reused, leading to confusing behavior.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
React Hooks
useSearchParamssubscription tearing by introducingUrlContext— search params now derive from React state instead of the mutablenavigation.currentEntry, preventing concurrent-mode tearingsetSearchParamssilently dropping the URL hash fragment when updating search parameterscanGoBack/canGoForwardnot being reactive —useBackanduseForwardnow subscribe to thecurrententrychangeevent and keep their boolean in React stateRoute Builder (
createRouter).render(),.redirect(),.group()) — calling any method on a consumed builder now throws a descriptive errorUrlContextfrom the barrelMatcher
WeakMap<Matcher, Set<string>>cache to prevent thundering-herd when many<Link>components point to the same destinationType Safety
NavigationContexttyped asContext<Navigation>(usingnull as unknown as Navigation) but actually defaulting tonull— now correctly typed asContext<Navigation | null>Tests
Breaking Changes
NavigationContexttype changed toContext<Navigation | null>What changed: The context default is now properly typed as nullable instead of using
null as unknown as Navigation.Migration: Code using
use(NavigationContext)directly must handle thenullcase. TheuseNavigation()hook already throws a descriptive error on null, so hook consumers are unaffected.Why: The previous typing masked a runtime null value, causing potential crashes when consumed outside a Router.
matcher.register()throws on duplicate patternsWhat changed: Registering the same path pattern twice now throws instead of silently overwriting.
Migration: Remove duplicate route registrations. If intentional overwriting is needed, create a new matcher.
Why: Silent overwriting caused hard-to-debug routing bugs where the last registration would win.
Route builder throws on reuse after terminal methods
What changed: Calling
.middleware(),.scroll(), etc. after.render()/.redirect()/.group()now throws.Migration: Create a new builder via
route('/path')for each route instead of reusing a consumed builder.Why: Reusing a consumed builder led to confusing behavior where configuration appeared to be set but was silently ignored.