UNPKG

@tanstack/router-core

Version:

Modern and scalable routing for React applications

1 lines 131 kB
{"version":3,"file":"router.cjs","names":[],"sources":["../../src/router.ts"],"sourcesContent":["import { createStore } from '@tanstack/store'\nimport { createBrowserHistory, parseHref } from '@tanstack/history'\nimport { isServer } from '@tanstack/router-core/isServer'\nimport { batch } from './utils/batch'\nimport {\n DEFAULT_PROTOCOL_ALLOWLIST,\n createControlledPromise,\n decodePath,\n deepEqual,\n encodePathLikeUrl,\n findLast,\n functionalUpdate,\n isDangerousProtocol,\n last,\n nullReplaceEqualDeep,\n replaceEqualDeep,\n} from './utils'\nimport {\n findFlatMatch,\n findRouteMatch,\n findSingleMatch,\n processRouteMasks,\n processRouteTree,\n} from './new-process-route-tree'\nimport {\n cleanPath,\n compileDecodeCharMap,\n interpolatePath,\n resolvePath,\n trimPath,\n trimPathRight,\n} from './path'\nimport { createLRUCache } from './lru-cache'\nimport { isNotFound } from './not-found'\nimport { setupScrollRestoration } from './scroll-restoration'\nimport { defaultParseSearch, defaultStringifySearch } from './searchParams'\nimport { rootRouteId } from './root'\nimport { isRedirect, redirect } from './redirect'\nimport { loadMatches, loadRouteChunk, routeNeedsPreload } from './load-matches'\nimport {\n composeRewrites,\n executeRewriteInput,\n executeRewriteOutput,\n rewriteBasepath,\n} from './rewrite'\nimport type { Store } from '@tanstack/store'\nimport type { LRUCache } from './lru-cache'\nimport type {\n ProcessRouteTreeResult,\n ProcessedTree,\n} from './new-process-route-tree'\nimport type { SearchParser, SearchSerializer } from './searchParams'\nimport type { AnyRedirect, ResolvedRedirect } from './redirect'\nimport type {\n HistoryLocation,\n HistoryState,\n ParsedHistoryState,\n RouterHistory,\n} from '@tanstack/history'\n\nimport type {\n Awaitable,\n Constrain,\n ControlledPromise,\n NoInfer,\n NonNullableUpdater,\n PickAsRequired,\n Updater,\n} from './utils'\nimport type { ParsedLocation } from './location'\nimport type {\n AnyContext,\n AnyRoute,\n AnyRouteWithContext,\n LoaderStaleReloadMode,\n MakeRemountDepsOptionsUnion,\n RouteContextOptions,\n RouteLike,\n RouteMask,\n SearchMiddleware,\n} from './route'\nimport type {\n FullSearchSchema,\n RouteById,\n RoutePaths,\n RoutesById,\n RoutesByPath,\n} from './routeInfo'\nimport type {\n AnyRouteMatch,\n MakeRouteMatch,\n MakeRouteMatchUnion,\n MatchRouteOptions,\n} from './Matches'\nimport type {\n BuildLocationFn,\n CommitLocationOptions,\n NavigateFn,\n} from './RouterProvider'\nimport type { Manifest, RouterManagedTag } from './manifest'\nimport type { AnySchema, AnyValidator } from './validators'\nimport type { NavigateOptions, ResolveRelativePath, ToOptions } from './link'\nimport type { NotFoundError } from './not-found'\nimport type {\n AnySerializationAdapter,\n ValidateSerializableInput,\n} from './ssr/serializer/transformer'\n// import type { AnyRouterConfig } from './config'\n\nexport type ControllablePromise<T = any> = Promise<T> & {\n resolve: (value: T) => void\n reject: (value?: any) => void\n}\n\nexport type InjectedHtmlEntry = Promise<string>\n\nexport interface Register {\n // Lots of things on here like...\n // router\n // config\n // ssr\n}\n\nexport type RegisteredSsr<TRegister = Register> = TRegister extends {\n ssr: infer TSSR\n}\n ? TSSR\n : false\n\nexport type RegisteredRouter<TRegister = Register> = TRegister extends {\n router: infer TRouter\n}\n ? TRouter\n : AnyRouter\n\nexport type RegisteredConfigType<TRegister, TKey> = TRegister extends {\n config: infer TConfig\n}\n ? TConfig extends {\n '~types': infer TTypes\n }\n ? TKey extends keyof TTypes\n ? TTypes[TKey]\n : unknown\n : unknown\n : unknown\n\nexport type DefaultRemountDepsFn<TRouteTree extends AnyRoute> = (\n opts: MakeRemountDepsOptionsUnion<TRouteTree>,\n) => any\n\nexport interface DefaultRouterOptionsExtensions {}\n\nexport interface RouterOptionsExtensions extends DefaultRouterOptionsExtensions {}\n\nexport type SSROption = boolean | 'data-only'\n\nexport interface RouterOptions<\n TRouteTree extends AnyRoute,\n TTrailingSlashOption extends TrailingSlashOption,\n TDefaultStructuralSharingOption extends boolean = false,\n TRouterHistory extends RouterHistory = RouterHistory,\n TDehydrated = undefined,\n> extends RouterOptionsExtensions {\n /**\n * The history object that will be used to manage the browser history.\n *\n * If not provided, a new createBrowserHistory instance will be created and used.\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#history-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/history-types)\n */\n history?: TRouterHistory\n /**\n * A function that will be used to stringify search params when generating links.\n *\n * @default defaultStringifySearch\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#stringifysearch-method)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization)\n */\n stringifySearch?: SearchSerializer\n /**\n * A function that will be used to parse search params when parsing the current location.\n *\n * @default defaultParseSearch\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#parsesearch-method)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/custom-search-param-serialization)\n */\n parseSearch?: SearchParser\n /**\n * If `false`, routes will not be preloaded by default in any way.\n *\n * If `'intent'`, routes will be preloaded by default when the user hovers over a link or a `touchstart` event is detected on a `<Link>`.\n *\n * If `'viewport'`, routes will be preloaded by default when they are within the viewport.\n *\n * @default false\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreload-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)\n */\n defaultPreload?: false | 'intent' | 'viewport' | 'render'\n /**\n * The delay in milliseconds that a route must be hovered over or touched before it is preloaded.\n *\n * @default 50\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreloaddelay-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading#preload-delay)\n */\n defaultPreloadDelay?: number\n /**\n * The default `preloadIntentProximity` a route should use if no preloadIntentProximity is provided.\n *\n * @default 0\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreloadintentproximity-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading#preload-intent-proximity)\n */\n defaultPreloadIntentProximity?: number\n /**\n * The default `pendingMs` a route should use if no pendingMs is provided.\n *\n * @default 1000\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpendingms-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#avoiding-pending-component-flash)\n */\n defaultPendingMs?: number\n /**\n * The default `pendingMinMs` a route should use if no pendingMinMs is provided.\n *\n * @default 500\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpendingminms-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#avoiding-pending-component-flash)\n */\n defaultPendingMinMs?: number\n /**\n * The default `staleTime` a route should use if no staleTime is provided. This is the time in milliseconds that a route will be considered fresh.\n *\n * @default 0\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultstaletime-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#key-options)\n */\n defaultStaleTime?: number\n /**\n * The default stale reload mode a route loader should use if no `loader.staleReloadMode` is provided.\n *\n * `'background'` preserves the current stale-while-revalidate behavior.\n * `'blocking'` waits for stale loader reloads to complete before resolving navigation.\n *\n * @default 'background'\n */\n defaultStaleReloadMode?: LoaderStaleReloadMode\n /**\n * The default `preloadStaleTime` a route should use if no preloadStaleTime is provided.\n *\n * @default 30_000 `(30 seconds)`\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreloadstaletime-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)\n */\n defaultPreloadStaleTime?: number\n /**\n * The default `defaultPreloadGcTime` a route should use if no preloadGcTime is provided.\n *\n * @default 1_800_000 `(30 minutes)`\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreloadgctime-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)\n */\n defaultPreloadGcTime?: number\n /**\n * If `true`, route navigations will called using `document.startViewTransition()`.\n *\n * If the browser does not support this api, this option will be ignored.\n *\n * See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Document/startViewTransition) for more information on how this function works.\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultviewtransition-property)\n */\n defaultViewTransition?: boolean | ViewTransitionOptions\n /**\n * The default `hashScrollIntoView` a route should use if no hashScrollIntoView is provided while navigating\n *\n * See [MDN](https://developer.mozilla.org/en-US/docs/Web/API/Element/scrollIntoView) for more information on `ScrollIntoViewOptions`.\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaulthashscrollintoview-property)\n */\n defaultHashScrollIntoView?: boolean | ScrollIntoViewOptions\n /**\n * @default 'fuzzy'\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#notfoundmode-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/not-found-errors#the-notfoundmode-option)\n */\n notFoundMode?: 'root' | 'fuzzy'\n /**\n * The default `gcTime` a route should use if no gcTime is provided.\n *\n * @default 1_800_000 `(30 minutes)`\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultgctime-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#key-options)\n */\n defaultGcTime?: number\n /**\n * If `true`, all routes will be matched as case-sensitive.\n *\n * @default false\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#casesensitive-property)\n */\n caseSensitive?: boolean\n /**\n *\n * The route tree that will be used to configure the router instance.\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#routetree-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/routing/route-trees)\n */\n routeTree?: TRouteTree\n /**\n * The basepath for then entire router. This is useful for mounting a router instance at a subpath.\n * ```\n * @default '/'\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#basepath-property)\n */\n basepath?: string\n /**\n * The root context that will be provided to all routes in the route tree.\n *\n * This can be used to provide a context to all routes in the tree without having to provide it to each route individually.\n *\n * Optional or required if the root route was created with [`createRootRouteWithContext()`](https://tanstack.com/router/latest/docs/framework/react/api/router/createRootRouteWithContextFunction).\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#context-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/router-context)\n */\n context?: InferRouterContext<TRouteTree>\n\n additionalContext?: any\n\n /**\n * A function that will be called when the router is dehydrated.\n *\n * The return value of this function will be serialized and stored in the router's dehydrated state.\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#dehydrate-method)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/external-data-loading#critical-dehydrationhydration)\n */\n dehydrate?: () => Constrain<\n TDehydrated,\n ValidateSerializableInput<Register, TDehydrated>\n >\n /**\n * A function that will be called when the router is hydrated.\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#hydrate-method)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/external-data-loading#critical-dehydrationhydration)\n */\n hydrate?: (dehydrated: TDehydrated) => Awaitable<void>\n /**\n * An array of route masks that will be used to mask routes in the route tree.\n *\n * Route masking is when you display a route at a different path than the one it is configured to match, like a modal popup that when shared will unmask to the modal's content instead of the modal's context.\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#routemasks-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/route-masking)\n */\n routeMasks?: Array<RouteMask<TRouteTree>>\n /**\n * If `true`, route masks will, by default, be removed when the page is reloaded.\n *\n * This can be overridden on a per-mask basis by setting the `unmaskOnReload` option on the mask, or on a per-navigation basis by setting the `unmaskOnReload` option in the `Navigate` options.\n *\n * @default false\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#unmaskonreload-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/route-masking#unmasking-on-page-reload)\n */\n unmaskOnReload?: boolean\n\n /**\n * Use `notFoundComponent` instead.\n *\n * @deprecated\n * See https://tanstack.com/router/v1/docs/guide/not-found-errors#migrating-from-notfoundroute for more info.\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#notfoundroute-property)\n */\n notFoundRoute?: AnyRoute\n /**\n * Configures how trailing slashes are treated.\n *\n * - `'always'` will add a trailing slash if not present\n * - `'never'` will remove the trailing slash if present\n * - `'preserve'` will not modify the trailing slash.\n *\n * @default 'never'\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#trailingslash-property)\n */\n trailingSlash?: TTrailingSlashOption\n /**\n * While usually automatic, sometimes it can be useful to force the router into a server-side state, e.g. when using the router in a non-browser environment that has access to a global.document object.\n *\n * @default typeof document !== 'undefined'\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#isserver-property)\n */\n isServer?: boolean\n\n /**\n * @default false\n */\n isShell?: boolean\n\n /**\n * @default false\n */\n isPrerendering?: boolean\n\n /**\n * The default `ssr` a route should use if no `ssr` is provided.\n *\n * @default true\n */\n defaultSsr?: SSROption\n\n search?: {\n /**\n * Configures how unknown search params (= not returned by any `validateSearch`) are treated.\n *\n * @default false\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#search.strict-property)\n */\n strict?: boolean\n }\n\n /**\n * Configures whether structural sharing is enabled by default for fine-grained selectors.\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultstructuralsharing-property)\n */\n defaultStructuralSharing?: TDefaultStructuralSharingOption\n\n /**\n * Configures which URI characters are allowed in path params that would ordinarily be escaped by encodeURIComponent.\n *\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#pathparamsallowedcharacters-property)\n * @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/path-params#allowed-characters)\n */\n pathParamsAllowedCharacters?: Array<\n ';' | ':' | '@' | '&' | '=' | '+' | '$' | ','\n >\n\n defaultRemountDeps?: DefaultRemountDepsFn<TRouteTree>\n\n /**\n * If `true`, scroll restoration will be enabled\n *\n * @default false\n */\n scrollRestoration?:\n | boolean\n | ((opts: { location: ParsedLocation }) => boolean)\n\n /**\n * A function that will be called to get the key for the scroll restoration cache.\n *\n * @default (location) => location.href\n */\n getScrollRestorationKey?: (location: ParsedLocation) => string\n /**\n * The default behavior for scroll restoration.\n *\n * @default 'auto'\n */\n scrollRestorationBehavior?: ScrollBehavior\n /**\n * An array of selectors that will be used to scroll to the top of the page in addition to `window`\n *\n * @default ['window']\n */\n scrollToTopSelectors?: Array<string | (() => Element | null | undefined)>\n\n /**\n * When `true`, disables the global catch boundary that normally wraps all route matches.\n * This allows unhandled errors to bubble up to top-level error handlers in the browser.\n *\n * Useful for testing tools (like Storybook Test Runner), error reporting services,\n * and debugging scenarios where you want errors to reach the browser's global error handlers.\n *\n * @default false\n */\n disableGlobalCatchBoundary?: boolean\n\n /**\n * An array of URL protocols to allow in links, redirects, and navigation.\n * Absolute URLs with protocols not in this list will be rejected.\n *\n * @default DEFAULT_PROTOCOL_ALLOWLIST (http:, https:, mailto:, tel:)\n * @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#protocolallowlist-property)\n */\n protocolAllowlist?: Array<string>\n\n serializationAdapters?: ReadonlyArray<AnySerializationAdapter>\n /**\n * Configures how the router will rewrite the location between the actual href and the internal href of the router.\n *\n * @default undefined\n * @description You can provide a custom rewrite pair (in/out).\n * This is useful for shifting data from the origin to the path (for things like subdomain routing), or other advanced use cases.\n */\n rewrite?: LocationRewrite\n origin?: string\n ssr?: {\n nonce?: string\n }\n}\n\nexport type LocationRewrite = {\n /**\n * A function that will be called to rewrite the URL before it is interpreted by the router from the history instance.\n *\n * @default undefined\n */\n input?: LocationRewriteFunction\n /**\n * A function that will be called to rewrite the URL before it is committed to the actual history instance from the router.\n *\n * @default undefined\n */\n output?: LocationRewriteFunction\n}\n\n/**\n * A function that will be called to rewrite the URL.\n *\n * @param url The URL to rewrite.\n * @returns The rewritten URL (as a URL instance or full href string) or undefined if no rewrite is needed.\n */\nexport type LocationRewriteFunction = ({\n url,\n}: {\n url: URL\n}) => undefined | string | URL\n\nexport interface RouterState<\n in out TRouteTree extends AnyRoute = AnyRoute,\n in out TRouteMatch = MakeRouteMatchUnion,\n> {\n status: 'pending' | 'idle'\n loadedAt: number\n isLoading: boolean\n isTransitioning: boolean\n matches: Array<TRouteMatch>\n pendingMatches?: Array<TRouteMatch>\n cachedMatches: Array<TRouteMatch>\n location: ParsedLocation<FullSearchSchema<TRouteTree>>\n resolvedLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>\n statusCode: number\n redirect?: AnyRedirect\n}\n\nexport interface BuildNextOptions {\n to?: string | number | null\n params?: true | Updater<unknown>\n search?: true | Updater<unknown>\n hash?: true | Updater<string>\n state?: true | NonNullableUpdater<ParsedHistoryState, HistoryState>\n mask?: {\n to?: string | number | null\n params?: true | Updater<unknown>\n search?: true | Updater<unknown>\n hash?: true | Updater<string>\n state?: true | NonNullableUpdater<ParsedHistoryState, HistoryState>\n unmaskOnReload?: boolean\n }\n from?: string\n href?: string\n _fromLocation?: ParsedLocation\n unsafeRelative?: 'path'\n _isNavigate?: boolean\n}\n\ntype NavigationEventInfo = {\n fromLocation?: ParsedLocation\n toLocation: ParsedLocation\n pathChanged: boolean\n hrefChanged: boolean\n hashChanged: boolean\n}\n\nexport interface RouterEvents {\n onBeforeNavigate: {\n type: 'onBeforeNavigate'\n } & NavigationEventInfo\n onBeforeLoad: {\n type: 'onBeforeLoad'\n } & NavigationEventInfo\n onLoad: {\n type: 'onLoad'\n } & NavigationEventInfo\n onResolved: {\n type: 'onResolved'\n } & NavigationEventInfo\n onBeforeRouteMount: {\n type: 'onBeforeRouteMount'\n } & NavigationEventInfo\n onRendered: {\n type: 'onRendered'\n } & NavigationEventInfo\n}\n\nexport type RouterEvent = RouterEvents[keyof RouterEvents]\n\nexport type ListenerFn<TEvent extends RouterEvent> = (event: TEvent) => void\n\nexport type RouterListener<TRouterEvent extends RouterEvent> = {\n eventType: TRouterEvent['type']\n fn: ListenerFn<TRouterEvent>\n}\n\nexport type SubscribeFn = <TType extends keyof RouterEvents>(\n eventType: TType,\n fn: ListenerFn<RouterEvents[TType]>,\n) => () => void\n\nexport interface MatchRoutesOpts {\n preload?: boolean\n throwOnError?: boolean\n dest?: BuildNextOptions\n}\n\nexport type InferRouterContext<TRouteTree extends AnyRoute> =\n TRouteTree['types']['routerContext']\n\nexport type RouterContextOptions<TRouteTree extends AnyRoute> =\n AnyContext extends InferRouterContext<TRouteTree>\n ? {\n context?: InferRouterContext<TRouteTree>\n }\n : {\n context: InferRouterContext<TRouteTree>\n }\n\nexport type RouterConstructorOptions<\n TRouteTree extends AnyRoute,\n TTrailingSlashOption extends TrailingSlashOption,\n TDefaultStructuralSharingOption extends boolean,\n TRouterHistory extends RouterHistory,\n TDehydrated extends Record<string, any>,\n> = Omit<\n RouterOptions<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory,\n TDehydrated\n >,\n 'context' | 'serializationAdapters' | 'defaultSsr'\n> &\n RouterContextOptions<TRouteTree>\n\nexport type PreloadRouteFn<\n TRouteTree extends AnyRoute,\n TTrailingSlashOption extends TrailingSlashOption,\n TDefaultStructuralSharingOption extends boolean,\n TRouterHistory extends RouterHistory,\n> = <\n TFrom extends RoutePaths<TRouteTree> | string = string,\n TTo extends string | undefined = undefined,\n TMaskFrom extends RoutePaths<TRouteTree> | string = TFrom,\n TMaskTo extends string = '',\n>(\n opts: NavigateOptions<\n RouterCore<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory\n >,\n TFrom,\n TTo,\n TMaskFrom,\n TMaskTo\n > & {\n /**\n * @internal\n * A **trusted** built location that can be used to redirect to.\n */\n _builtLocation?: ParsedLocation\n },\n) => Promise<Array<AnyRouteMatch> | undefined>\n\nexport type MatchRouteFn<\n TRouteTree extends AnyRoute,\n TTrailingSlashOption extends TrailingSlashOption,\n TDefaultStructuralSharingOption extends boolean,\n TRouterHistory extends RouterHistory,\n> = <\n TFrom extends RoutePaths<TRouteTree> = '/',\n TTo extends string | undefined = undefined,\n TResolved = ResolveRelativePath<TFrom, NoInfer<TTo>>,\n>(\n location: ToOptions<\n RouterCore<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory\n >,\n TFrom,\n TTo\n >,\n opts?: MatchRouteOptions,\n) => false | RouteById<TRouteTree, TResolved>['types']['allParams']\n\nexport type UpdateFn<\n TRouteTree extends AnyRoute,\n TTrailingSlashOption extends TrailingSlashOption,\n TDefaultStructuralSharingOption extends boolean,\n TRouterHistory extends RouterHistory,\n TDehydrated extends Record<string, any>,\n> = (\n newOptions: RouterConstructorOptions<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory,\n TDehydrated\n >,\n) => void\n\nexport type InvalidateFn<TRouter extends AnyRouter> = (opts?: {\n filter?: (d: MakeRouteMatchUnion<TRouter>) => boolean\n sync?: boolean\n forcePending?: boolean\n}) => Promise<void>\n\nexport type ParseLocationFn<TRouteTree extends AnyRoute> = (\n locationToParse: HistoryLocation,\n previousLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>,\n) => ParsedLocation<FullSearchSchema<TRouteTree>>\n\nexport type GetMatchRoutesFn = (pathname: string) => {\n matchedRoutes: ReadonlyArray<AnyRoute>\n /** exhaustive params, still in their string form */\n routeParams: Record<string, string>\n /** partial params, parsed from routeParams during matching */\n parsedParams: Record<string, unknown> | undefined\n foundRoute: AnyRoute | undefined\n parseError?: unknown\n}\n\nexport type EmitFn = (routerEvent: RouterEvent) => void\n\nexport type LoadFn = (opts?: { sync?: boolean }) => Promise<void>\n\nexport type CommitLocationFn = ({\n viewTransition,\n ignoreBlocker,\n ...next\n}: ParsedLocation & CommitLocationOptions) => Promise<void>\n\nexport type StartTransitionFn = (fn: () => void) => void\n\nexport interface MatchRoutesFn {\n (\n pathname: string,\n locationSearch?: AnySchema,\n opts?: MatchRoutesOpts,\n ): Array<MakeRouteMatchUnion>\n /**\n * @deprecated use the following signature instead\n */\n (next: ParsedLocation, opts?: MatchRoutesOpts): Array<AnyRouteMatch>\n (\n pathnameOrNext: string | ParsedLocation,\n locationSearchOrOpts?: AnySchema | MatchRoutesOpts,\n opts?: MatchRoutesOpts,\n ): Array<AnyRouteMatch>\n}\n\nexport type GetMatchFn = (matchId: string) => AnyRouteMatch | undefined\n\nexport type UpdateMatchFn = (\n id: string,\n updater: (match: AnyRouteMatch) => AnyRouteMatch,\n) => void\n\nexport type LoadRouteChunkFn = (route: AnyRoute) => Promise<Array<void>>\n\nexport type ResolveRedirect = (err: AnyRedirect) => ResolvedRedirect\n\nexport type ClearCacheFn<TRouter extends AnyRouter> = (opts?: {\n filter?: (d: MakeRouteMatchUnion<TRouter>) => boolean\n}) => void\n\nexport interface ServerSsr {\n /**\n * Injects HTML synchronously into the stream.\n * Emits an onInjectedHtml event that listeners can handle.\n * If no subscriber is listening, the HTML is buffered and can be retrieved via takeBufferedHtml().\n */\n injectHtml: (html: string) => void\n /**\n * Injects a script tag synchronously into the stream.\n */\n injectScript: (script: string) => void\n isDehydrated: () => boolean\n isSerializationFinished: () => boolean\n onRenderFinished: (listener: () => void) => void\n setRenderFinished: () => void\n cleanup: () => void\n onSerializationFinished: (listener: () => void) => void\n dehydrate: () => Promise<void>\n takeBufferedScripts: () => RouterManagedTag | undefined\n /**\n * Takes any buffered HTML that was injected.\n * Returns the buffered HTML string (which may include multiple script tags) or undefined if empty.\n */\n takeBufferedHtml: () => string | undefined\n liftScriptBarrier: () => void\n}\n\nexport type AnyRouterWithContext<TContext> = RouterCore<\n AnyRouteWithContext<TContext>,\n any,\n any,\n any,\n any\n>\n\nexport type AnyRouter = RouterCore<any, any, any, any, any>\n\nexport interface ViewTransitionOptions {\n types:\n | Array<string>\n | ((locationChangeInfo: {\n fromLocation?: ParsedLocation\n toLocation: ParsedLocation\n pathChanged: boolean\n hrefChanged: boolean\n hashChanged: boolean\n }) => Array<string> | false)\n}\n\n// TODO where is this used? can we remove this?\n/**\n * Convert an unknown error into a minimal, serializable object.\n * Includes name and message (and stack in development).\n */\nexport function defaultSerializeError(err: unknown) {\n if (err instanceof Error) {\n const obj = {\n name: err.name,\n message: err.message,\n }\n\n if (process.env.NODE_ENV === 'development') {\n ;(obj as any).stack = err.stack\n }\n\n return obj\n }\n\n return {\n data: err,\n }\n}\n\n/** Options for configuring trailing-slash behavior. */\nexport const trailingSlashOptions = {\n always: 'always',\n never: 'never',\n preserve: 'preserve',\n} as const\n\nexport type TrailingSlashOption =\n (typeof trailingSlashOptions)[keyof typeof trailingSlashOptions]\n\n/**\n * Compute whether path, href or hash changed between previous and current\n * resolved locations in router state.\n */\nexport function getLocationChangeInfo(routerState: {\n resolvedLocation?: ParsedLocation\n location: ParsedLocation\n}) {\n const fromLocation = routerState.resolvedLocation\n const toLocation = routerState.location\n const pathChanged = fromLocation?.pathname !== toLocation.pathname\n const hrefChanged = fromLocation?.href !== toLocation.href\n const hashChanged = fromLocation?.hash !== toLocation.hash\n return { fromLocation, toLocation, pathChanged, hrefChanged, hashChanged }\n}\n\nfunction filterRedirectedCachedMatches<T extends { status: string }>(\n matches: Array<T>,\n): Array<T> {\n const filtered = matches.filter((d) => d.status !== 'redirected')\n return filtered.length === matches.length ? matches : filtered\n}\n\nexport type CreateRouterFn = <\n TRouteTree extends AnyRoute,\n TTrailingSlashOption extends TrailingSlashOption = 'never',\n TDefaultStructuralSharingOption extends boolean = false,\n TRouterHistory extends RouterHistory = RouterHistory,\n TDehydrated extends Record<string, any> = Record<string, any>,\n>(\n options: undefined extends number\n ? 'strictNullChecks must be enabled in tsconfig.json'\n : RouterConstructorOptions<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory,\n TDehydrated\n >,\n) => RouterCore<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory,\n TDehydrated\n>\n\ndeclare global {\n // eslint-disable-next-line no-var\n var __TSR_CACHE__:\n | {\n routeTree: AnyRoute\n processRouteTreeResult: ProcessRouteTreeResult<AnyRoute>\n resolvePathCache: LRUCache<string, string>\n }\n | undefined\n}\n\n/**\n * Core, framework-agnostic router engine that powers TanStack Router.\n *\n * Provides navigation, matching, loading, preloading, caching and event APIs\n * used by framework adapters (React/Solid). Prefer framework helpers like\n * `createRouter` in app code.\n *\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/RouterType\n */\ntype RouterStateStore<TState> = {\n state: TState\n setState: (updater: (prev: TState) => TState) => void\n}\n\nfunction createServerStore<TState>(\n initialState: TState,\n): RouterStateStore<TState> {\n const store = {\n state: initialState,\n setState: (updater: (prev: TState) => TState) => {\n store.state = updater(store.state)\n },\n } as RouterStateStore<TState>\n\n return store\n}\n\nexport class RouterCore<\n in out TRouteTree extends AnyRoute,\n in out TTrailingSlashOption extends TrailingSlashOption,\n in out TDefaultStructuralSharingOption extends boolean,\n in out TRouterHistory extends RouterHistory = RouterHistory,\n in out TDehydrated extends Record<string, any> = Record<string, any>,\n> {\n // Option-independent properties\n tempLocationKey: string | undefined = `${Math.round(\n Math.random() * 10000000,\n )}`\n resetNextScroll = true\n shouldViewTransition?: boolean | ViewTransitionOptions = undefined\n isViewTransitionTypesSupported?: boolean = undefined\n subscribers = new Set<RouterListener<RouterEvent>>()\n viewTransitionPromise?: ControlledPromise<true>\n isScrollRestoring = false\n isScrollRestorationSetup = false\n\n // Must build in constructor\n __store!: Store<RouterState<TRouteTree>>\n options!: PickAsRequired<\n RouterOptions<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory,\n TDehydrated\n >,\n 'stringifySearch' | 'parseSearch' | 'context'\n >\n history!: TRouterHistory\n rewrite?: LocationRewrite\n origin?: string\n latestLocation!: ParsedLocation<FullSearchSchema<TRouteTree>>\n pendingBuiltLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>\n basepath!: string\n routeTree!: TRouteTree\n routesById!: RoutesById<TRouteTree>\n routesByPath!: RoutesByPath<TRouteTree>\n processedTree!: ProcessedTree<TRouteTree, any, any>\n resolvePathCache!: LRUCache<string, string>\n isServer!: boolean\n pathParamsDecoder?: (encoded: string) => string\n protocolAllowlist!: Set<string>\n\n /**\n * @deprecated Use the `createRouter` function instead\n */\n constructor(\n options: RouterConstructorOptions<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory,\n TDehydrated\n >,\n ) {\n this.update({\n defaultPreloadDelay: 50,\n defaultPendingMs: 1000,\n defaultPendingMinMs: 500,\n context: undefined!,\n ...options,\n caseSensitive: options.caseSensitive ?? false,\n notFoundMode: options.notFoundMode ?? 'fuzzy',\n stringifySearch: options.stringifySearch ?? defaultStringifySearch,\n parseSearch: options.parseSearch ?? defaultParseSearch,\n protocolAllowlist:\n options.protocolAllowlist ?? DEFAULT_PROTOCOL_ALLOWLIST,\n })\n\n if (typeof document !== 'undefined') {\n self.__TSR_ROUTER__ = this\n }\n }\n\n // This is a default implementation that can optionally be overridden\n // by the router provider once rendered. We provide this so that the\n // router can be used in a non-react environment if necessary\n startTransition: StartTransitionFn = (fn) => fn()\n\n isShell() {\n return !!this.options.isShell\n }\n\n isPrerendering() {\n return !!this.options.isPrerendering\n }\n\n update: UpdateFn<\n TRouteTree,\n TTrailingSlashOption,\n TDefaultStructuralSharingOption,\n TRouterHistory,\n TDehydrated\n > = (newOptions) => {\n if (process.env.NODE_ENV !== 'production') {\n if (newOptions.notFoundRoute) {\n console.warn(\n 'The notFoundRoute API is deprecated and will be removed in the next major version. See https://tanstack.com/router/v1/docs/framework/react/guide/not-found-errors#migrating-from-notfoundroute for more info.',\n )\n }\n }\n\n const prevOptions = this.options\n const prevBasepath = this.basepath ?? prevOptions?.basepath ?? '/'\n const basepathWasUnset = this.basepath === undefined\n const prevRewriteOption = prevOptions?.rewrite\n\n this.options = {\n ...prevOptions,\n ...newOptions,\n }\n\n this.isServer = this.options.isServer ?? typeof document === 'undefined'\n\n this.protocolAllowlist = new Set(this.options.protocolAllowlist)\n\n if (this.options.pathParamsAllowedCharacters)\n this.pathParamsDecoder = compileDecodeCharMap(\n this.options.pathParamsAllowedCharacters,\n )\n\n if (\n !this.history ||\n (this.options.history && this.options.history !== this.history)\n ) {\n if (!this.options.history) {\n if (!(isServer ?? this.isServer)) {\n this.history = createBrowserHistory() as TRouterHistory\n }\n } else {\n this.history = this.options.history\n }\n }\n\n this.origin = this.options.origin\n if (!this.origin) {\n if (\n !(isServer ?? this.isServer) &&\n window?.origin &&\n window.origin !== 'null'\n ) {\n this.origin = window.origin\n } else {\n // fallback for the server, can be overridden by calling router.update({origin}) on the server\n this.origin = 'http://localhost'\n }\n }\n\n if (this.history) {\n this.updateLatestLocation()\n }\n\n if (this.options.routeTree !== this.routeTree) {\n this.routeTree = this.options.routeTree as TRouteTree\n let processRouteTreeResult: ProcessRouteTreeResult<TRouteTree>\n if (\n (isServer ?? this.isServer) &&\n process.env.NODE_ENV !== 'development' &&\n globalThis.__TSR_CACHE__ &&\n globalThis.__TSR_CACHE__.routeTree === this.routeTree\n ) {\n const cached = globalThis.__TSR_CACHE__\n this.resolvePathCache = cached.resolvePathCache\n processRouteTreeResult = cached.processRouteTreeResult as any\n } else {\n this.resolvePathCache = createLRUCache(1000)\n processRouteTreeResult = this.buildRouteTree()\n // only cache if nothing else is cached yet\n if (\n (isServer ?? this.isServer) &&\n process.env.NODE_ENV !== 'development' &&\n globalThis.__TSR_CACHE__ === undefined\n ) {\n globalThis.__TSR_CACHE__ = {\n routeTree: this.routeTree,\n processRouteTreeResult: processRouteTreeResult as any,\n resolvePathCache: this.resolvePathCache,\n }\n }\n }\n this.setRoutes(processRouteTreeResult)\n }\n\n if (!this.__store && this.latestLocation) {\n if (isServer ?? this.isServer) {\n this.__store = createServerStore(\n getInitialRouterState(this.latestLocation),\n ) as unknown as Store<any>\n } else {\n this.__store = createStore(getInitialRouterState(this.latestLocation))\n\n setupScrollRestoration(this)\n }\n }\n\n let needsLocationUpdate = false\n const nextBasepath = this.options.basepath ?? '/'\n const nextRewriteOption = this.options.rewrite\n const basepathChanged = basepathWasUnset || prevBasepath !== nextBasepath\n const rewriteChanged = prevRewriteOption !== nextRewriteOption\n\n if (basepathChanged || rewriteChanged) {\n this.basepath = nextBasepath\n\n const rewrites: Array<LocationRewrite> = []\n const trimmed = trimPath(nextBasepath)\n if (trimmed && trimmed !== '/') {\n rewrites.push(\n rewriteBasepath({\n basepath: nextBasepath,\n }),\n )\n }\n if (nextRewriteOption) {\n rewrites.push(nextRewriteOption)\n }\n\n this.rewrite =\n rewrites.length === 0\n ? undefined\n : rewrites.length === 1\n ? rewrites[0]\n : composeRewrites(rewrites)\n\n if (this.history) {\n this.updateLatestLocation()\n }\n\n needsLocationUpdate = true\n }\n\n if (needsLocationUpdate && this.__store) {\n this.__store.setState((s) => ({\n ...s,\n location: this.latestLocation,\n }))\n }\n\n if (\n typeof window !== 'undefined' &&\n 'CSS' in window &&\n typeof window.CSS?.supports === 'function'\n ) {\n this.isViewTransitionTypesSupported = window.CSS.supports(\n 'selector(:active-view-transition-type(a)',\n )\n }\n }\n\n get state(): RouterState<TRouteTree> {\n return this.__store.state\n }\n\n updateLatestLocation = () => {\n this.latestLocation = this.parseLocation(\n this.history.location,\n this.latestLocation,\n )\n }\n\n buildRouteTree = () => {\n const result = processRouteTree(\n this.routeTree,\n this.options.caseSensitive,\n (route, i) => {\n route.init({\n originalIndex: i,\n })\n },\n )\n if (this.options.routeMasks) {\n processRouteMasks(this.options.routeMasks, result.processedTree)\n }\n\n return result\n }\n\n setRoutes({\n routesById,\n routesByPath,\n processedTree,\n }: ProcessRouteTreeResult<TRouteTree>) {\n this.routesById = routesById as RoutesById<TRouteTree>\n this.routesByPath = routesByPath as RoutesByPath<TRouteTree>\n this.processedTree = processedTree\n\n const notFoundRoute = this.options.notFoundRoute\n\n if (notFoundRoute) {\n notFoundRoute.init({\n originalIndex: 99999999999,\n })\n this.routesById[notFoundRoute.id] = notFoundRoute\n }\n }\n\n /**\n * Subscribe to router lifecycle events like `onBeforeNavigate`, `onLoad`,\n * `onResolved`, etc. Returns an unsubscribe function.\n *\n * @link https://tanstack.com/router/latest/docs/framework/react/api/router/RouterEventsType\n */\n subscribe: SubscribeFn = (eventType, fn) => {\n const listener: RouterListener<any> = {\n eventType,\n fn,\n }\n\n this.subscribers.add(listener)\n\n return () => {\n this.subscribers.delete(listener)\n }\n }\n\n emit: EmitFn = (routerEvent) => {\n this.subscribers.forEach((listener) => {\n if (listener.eventType === routerEvent.type) {\n listener.fn(routerEvent)\n }\n })\n }\n\n /**\n * Parse a HistoryLocation into a strongly-typed ParsedLocation using the\n * current router options, rewrite rules and search parser/stringifier.\n */\n parseLocation: ParseLocationFn<TRouteTree> = (\n locationToParse,\n previousLocation,\n ) => {\n const parse = ({\n pathname,\n search,\n hash,\n href,\n state,\n }: HistoryLocation): ParsedLocation<FullSearchSchema<TRouteTree>> => {\n // Fast path: no rewrite configured and pathname doesn't need encoding\n // Characters that need encoding: space, high unicode, control chars\n // eslint-disable-next-line no-control-regex\n if (!this.rewrite && !/[ \\x00-\\x1f\\x7f\\u0080-\\uffff]/.test(pathname)) {\n const parsedSearch = this.options.parseSearch(search)\n const searchStr = this.options.stringifySearch(parsedSearch)\n\n return {\n href: pathname + searchStr + hash,\n publicHref: href,\n pathname: decodePath(pathname).path,\n external: false,\n searchStr,\n search: nullReplaceEqualDeep(\n previousLocation?.search,\n parsedSearch,\n ) as any,\n hash: decodePath(hash.slice(1)).path,\n state: replaceEqualDeep(previousLocation?.state, state),\n }\n }\n\n // Before we do any processing, we need to allow rewrites to modify the URL\n // build up the full URL by combining the href from history with the router's origin\n const fullUrl = new URL(href, this.origin)\n\n const url = executeRewriteInput(this.rewrite, fullUrl)\n\n const parsedSearch = this.options.parseSearch(url.search)\n const searchStr = this.options.stringifySearch(parsedSearch)\n // Make sure our final url uses the re-stringified pathname, search, and has for consistency\n // (We were already doing this, so just keeping it for now)\n url.search = searchStr\n\n const fullPath = url.href.replace(url.origin, '')\n\n return {\n href: fullPath,\n publicHref: href,\n pathname: decodePath(url.pathname).path,\n external: !!this.rewrite && url.origin !== this.origin,\n searchStr,\n search: nullReplaceEqualDeep(\n previousLocation?.search,\n parsedSearch,\n ) as any,\n hash: decodePath(url.hash.slice(1)).path,\n state: replaceEqualDeep(previousLocation?.state, state),\n }\n }\n\n const location = parse(locationToParse)\n\n const { __tempLocation, __tempKey } = location.state\n\n if (__tempLocation && (!__tempKey || __tempKey === this.tempLocationKey)) {\n // Sync up the location keys\n const parsedTempLocation = parse(__tempLocation) as any\n parsedTempLocation.state.key = location.state.key // TODO: Remove in v2 - use __TSR_key instead\n parsedTempLocation.state.__TSR_key = location.state.__TSR_key\n\n delete parsedTempLocation.state.__tempLocation\n\n return {\n ...parsedTempLocation,\n maskedLocation: location,\n }\n }\n return location\n }\n\n /** Resolve a path against the router basepath and trailing-slash policy. */\n resolvePathWithBase = (from: string, path: string) => {\n const resolvedPath = resolvePath({\n base: from,\n to: cleanPath(path),\n trailingSlash: this.options.trailingSlash,\n cache: this.resolvePathCache,\n })\n return resolvedPath\n }\n\n get looseRoutesById() {\n return this.routesById as Record<string, AnyRoute>\n }\n\n matchRoutes: MatchRoutesFn = (\n pathnameOrNext: string | ParsedLocation,\n locationSearchOrOpts?: AnySchema | MatchRoutesOpts,\n opts?: MatchRoutesOpts,\n ) => {\n if (typeof pathnameOrNext === 'string') {\n return this.matchRoutesInternal(\n {\n pathname: pathnameOrNext,\n search: locationSearchOrOpts,\n } as ParsedLocation,\n opts,\n )\n }\n\n return this.matchRoutesInternal(pathnameOrNext, locationSearchOrOpts)\n }\n\n private getParentContext(parentMatch?: AnyRouteMatch) {\n const parentMatchId = parentMatch?.id\n\n const parentContext = !parentMatchId\n ? ((this.options.context as any) ?? undefined)\n : (parentMatch.context ?? this.options.context ?? undefined)\n\n return parentContext\n }\n\n private matchRoutesInternal(\n next: ParsedLocation,\n opts?: MatchRoutesOpts,\n ): Array<AnyRouteMatch> {\n const matchedRoutesResult = this.getMatchedRoutes(next.pathname)\n const { foundRoute, routeParams, parsedParams } = matchedRoutesResult\n let { matchedRoutes } = matchedRoutesResult\n let isGlobalNotFound = false\n\n // Check to see if the route needs a 404 entry\n if (\n // If we found a route, and it's not an index route and we have left over path\n foundRoute\n ? foundRoute.path !== '/' && routeParams['**']\n : // Or if we didn't find a route and we have left over path\n trimPathRight(next.pathname)\n ) {\n // If the user has defined an (old) 404 route, use it\n if (this.options.notFoundRoute) {\n matchedRoutes = [...matchedRoutes, this.options.notFoundRoute]\n } else {\n // If there is no routes found during path matching\n isGlobalNotFound = true\n }\n }\n\n const globalNotFoundRouteId = isGlobalNotFound\n ? findGlobalNotFoundRouteId(this.options.notFoundMode, matchedRoutes)\n : undefined\n\n const matches = new Array<AnyRouteMatch>(matchedRoutes.length)\n\n const previousMatchesByRouteId = new Map(\n this.state.matches.map((match) => [match.routeId, match]),\n )\n\n for (let index = 0; index < matchedRoutes.length; index++) {\n const route = matchedRoutes[index]!\n // Take each matched route and resolve + validate its search params\n // This has to happen serially because each route's search params\n // can depend on the parent route's search params\n // It must also happen before we create the match so that we can\n // pass the search params to the route's potential key function\n // which is used to uniquely identify the route match in state\n\n const parentMatch = matches[index - 1]\n\n let preMatchSearch: Record<string, any>\n let strictMatchSearch: Record<string, any>\n let searchError: any\n {\n // Validate the search params and stabilize them\n const parentSearch = parentMatch?.search ?? next.search\n const parentStrictSearch = parentMatch?._strictSearch ?? undefined\n\n try {\n const strictSearch =\n validateSearch(route.options.validateSearch, { ...parentSearch }) ??\n undefined\n\n preMatchSearch = {\n ...parentSearch,\n ...strictSearch,\n }\n strictMatchSearch = { ...parentStrictSearch, ...strictSearch }\n searchError = undefined\n } catch (err: any) {\n let searchParamError = err\n if (!(err instanceof SearchParamError)) {\n searchParamError = new SearchParamError(err.message, {\n cause: err,\n })\n }\n\n if (opts?.throwOnError) {\n thro