@tanstack/router-core 1.171.14 → 1.171.16-pre.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/Matches.cjs.map +1 -1
- package/dist/cjs/Matches.d.cts +3 -12
- package/dist/cjs/await-signal.cjs +21 -0
- package/dist/cjs/await-signal.cjs.map +1 -0
- package/dist/cjs/await-signal.d.cts +1 -0
- package/dist/cjs/index.cjs +3 -1
- package/dist/cjs/index.d.cts +3 -2
- package/dist/cjs/isServer/client.cjs +2 -0
- package/dist/cjs/isServer/client.cjs.map +1 -1
- package/dist/cjs/isServer/client.d.cts +1 -0
- package/dist/cjs/isServer/development.cjs +2 -0
- package/dist/cjs/isServer/development.cjs.map +1 -1
- package/dist/cjs/isServer/development.d.cts +1 -0
- package/dist/cjs/isServer/server.cjs +2 -0
- package/dist/cjs/isServer/server.cjs.map +1 -1
- package/dist/cjs/isServer/server.d.cts +1 -0
- package/dist/cjs/load-client.cjs +1470 -0
- package/dist/cjs/load-client.cjs.map +1 -0
- package/dist/cjs/load-client.d.cts +147 -0
- package/dist/cjs/load-server.cjs +565 -0
- package/dist/cjs/load-server.cjs.map +1 -0
- package/dist/cjs/load-server.d.cts +16 -0
- package/dist/cjs/redirect.cjs.map +1 -1
- package/dist/cjs/redirect.d.cts +0 -1
- package/dist/cjs/route.cjs.map +1 -1
- package/dist/cjs/router.cjs +187 -445
- package/dist/cjs/router.cjs.map +1 -1
- package/dist/cjs/router.d.cts +62 -52
- package/dist/cjs/scroll-restoration.cjs +27 -34
- package/dist/cjs/scroll-restoration.cjs.map +1 -1
- package/dist/cjs/ssr/client.cjs +2 -2
- package/dist/cjs/ssr/createRequestHandler.cjs +60 -9
- package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
- package/dist/cjs/ssr/createRequestHandler.d.cts +1 -0
- package/dist/cjs/ssr/handlerCallback.cjs +49 -4
- package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
- package/dist/cjs/ssr/handlerCallback.d.cts +3 -0
- package/dist/cjs/ssr/server.cjs +4 -0
- package/dist/cjs/ssr/server.d.cts +2 -2
- package/dist/cjs/ssr/ssr-client.d.cts +1 -10
- package/dist/cjs/ssr/ssr-match-id.cjs +2 -2
- package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
- package/dist/cjs/ssr/ssr-server.cjs +6 -6
- package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs +27 -1
- package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
- package/dist/cjs/ssr/transformStreamWithRouter.d.cts +2 -0
- package/dist/cjs/ssr/types.d.cts +0 -1
- package/dist/cjs/stores.cjs +30 -104
- package/dist/cjs/stores.cjs.map +1 -1
- package/dist/cjs/stores.d.cts +7 -32
- package/dist/cjs/utils.cjs +0 -4
- package/dist/cjs/utils.cjs.map +1 -1
- package/dist/esm/Matches.d.ts +3 -12
- package/dist/esm/Matches.js.map +1 -1
- package/dist/esm/await-signal.d.ts +1 -0
- package/dist/esm/await-signal.js +21 -0
- package/dist/esm/await-signal.js.map +1 -0
- package/dist/esm/index.d.ts +3 -2
- package/dist/esm/index.js +3 -2
- package/dist/esm/isServer/client.d.ts +1 -0
- package/dist/esm/isServer/client.js +2 -1
- package/dist/esm/isServer/client.js.map +1 -1
- package/dist/esm/isServer/development.d.ts +1 -0
- package/dist/esm/isServer/development.js +2 -1
- package/dist/esm/isServer/development.js.map +1 -1
- package/dist/esm/isServer/server.d.ts +1 -0
- package/dist/esm/isServer/server.js +2 -1
- package/dist/esm/isServer/server.js.map +1 -1
- package/dist/esm/load-client.d.ts +147 -0
- package/dist/esm/load-client.js +1462 -0
- package/dist/esm/load-client.js.map +1 -0
- package/dist/esm/load-server.d.ts +16 -0
- package/dist/esm/load-server.js +565 -0
- package/dist/esm/load-server.js.map +1 -0
- package/dist/esm/redirect.d.ts +0 -1
- package/dist/esm/redirect.js.map +1 -1
- package/dist/esm/route.js.map +1 -1
- package/dist/esm/router.d.ts +62 -52
- package/dist/esm/router.js +188 -446
- package/dist/esm/router.js.map +1 -1
- package/dist/esm/scroll-restoration.js +27 -34
- package/dist/esm/scroll-restoration.js.map +1 -1
- package/dist/esm/ssr/client.js +1 -1
- package/dist/esm/ssr/createRequestHandler.d.ts +1 -0
- package/dist/esm/ssr/createRequestHandler.js +61 -11
- package/dist/esm/ssr/createRequestHandler.js.map +1 -1
- package/dist/esm/ssr/handlerCallback.d.ts +3 -0
- package/dist/esm/ssr/handlerCallback.js +47 -5
- package/dist/esm/ssr/handlerCallback.js.map +1 -1
- package/dist/esm/ssr/server.d.ts +2 -2
- package/dist/esm/ssr/server.js +3 -3
- package/dist/esm/ssr/ssr-client.d.ts +1 -10
- package/dist/esm/ssr/ssr-match-id.js +2 -2
- package/dist/esm/ssr/ssr-match-id.js.map +1 -1
- package/dist/esm/ssr/ssr-server.js +6 -6
- package/dist/esm/ssr/ssr-server.js.map +1 -1
- package/dist/esm/ssr/transformStreamWithRouter.d.ts +2 -0
- package/dist/esm/ssr/transformStreamWithRouter.js +27 -1
- package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
- package/dist/esm/ssr/types.d.ts +0 -1
- package/dist/esm/stores.d.ts +7 -32
- package/dist/esm/stores.js +30 -104
- package/dist/esm/stores.js.map +1 -1
- package/dist/esm/utils.js +1 -4
- package/dist/esm/utils.js.map +1 -1
- package/package.json +3 -3
- package/src/Matches.ts +6 -21
- package/src/await-signal.ts +27 -0
- package/src/index.ts +1 -3
- package/src/isServer/client.ts +1 -0
- package/src/isServer/development.ts +1 -0
- package/src/isServer/server.ts +1 -0
- package/src/load-client.ts +2701 -0
- package/src/load-server.ts +849 -0
- package/src/redirect.ts +0 -1
- package/src/route.ts +2 -11
- package/src/router.ts +380 -774
- package/src/scroll-restoration.ts +40 -56
- package/src/ssr/createRequestHandler.ts +109 -16
- package/src/ssr/handlerCallback.ts +76 -10
- package/src/ssr/server.ts +4 -1
- package/src/ssr/ssr-client.ts +1 -310
- package/src/ssr/ssr-match-id.ts +11 -2
- package/src/ssr/ssr-server.ts +9 -8
- package/src/ssr/transformStreamWithRouter.ts +37 -1
- package/src/ssr/types.ts +0 -1
- package/src/stores.ts +46 -208
- package/dist/cjs/load-matches.cjs +0 -658
- package/dist/cjs/load-matches.cjs.map +0 -1
- package/dist/cjs/load-matches.d.cts +0 -18
- package/dist/cjs/ssr/ssr-client.cjs +0 -183
- package/dist/cjs/ssr/ssr-client.cjs.map +0 -1
- package/dist/esm/load-matches.d.ts +0 -18
- package/dist/esm/load-matches.js +0 -656
- package/dist/esm/load-matches.js.map +0 -1
- package/dist/esm/ssr/ssr-client.js +0 -183
- package/dist/esm/ssr/ssr-client.js.map +0 -1
- package/src/load-matches.ts +0 -1278
package/dist/cjs/router.d.cts
CHANGED
|
@@ -1,12 +1,13 @@
|
|
|
1
|
-
import { loadRouteChunk } from './load-
|
|
1
|
+
import { loadRouteChunk, ActivePreload, LoadTransaction, LoaderFlight, PendingSession } from './load-client.cjs';
|
|
2
2
|
import { LRUCache } from './lru-cache.cjs';
|
|
3
3
|
import { ProcessRouteTreeResult, ProcessedTree } from './new-process-route-tree.cjs';
|
|
4
4
|
import { SearchParser, SearchSerializer } from './searchParams.cjs';
|
|
5
5
|
import { AnyRedirect, ResolvedRedirect } from './redirect.cjs';
|
|
6
|
+
import { ServerLoadResult } from './load-server.cjs';
|
|
6
7
|
import { HistoryAction, HistoryLocation, HistoryState, ParsedHistoryState, RouterHistory } from '@tanstack/history';
|
|
7
|
-
import { Awaitable, Constrain,
|
|
8
|
+
import { Awaitable, Constrain, NoInfer, NonNullableUpdater, PickAsRequired, Updater } from './utils.cjs';
|
|
8
9
|
import { ParsedLocation } from './location.cjs';
|
|
9
|
-
import { AnyContext, AnyRoute, AnyRouteWithContext, LoaderStaleReloadMode, MakeRemountDepsOptionsUnion,
|
|
10
|
+
import { AnyContext, AnyRoute, AnyRouteWithContext, LoaderStaleReloadMode, MakeRemountDepsOptionsUnion, RouteMask } from './route.cjs';
|
|
10
11
|
import { FullSearchSchema, RouteById, RoutePaths, RoutesById, RoutesByPath } from './routeInfo.cjs';
|
|
11
12
|
import { AnyRouteMatch, MakeRouteMatchUnion, MatchRouteOptions } from './Matches.cjs';
|
|
12
13
|
import { BuildLocationFn, CommitLocationOptions, NavigateFn } from './RouterProvider.cjs';
|
|
@@ -135,9 +136,9 @@ export interface RouterOptions<TRouteTree extends AnyRoute, TTrailingSlashOption
|
|
|
135
136
|
*/
|
|
136
137
|
defaultPreloadStaleTime?: number;
|
|
137
138
|
/**
|
|
138
|
-
* The default `
|
|
139
|
+
* The default `preloadGcTime` a route should use if none is provided.
|
|
139
140
|
*
|
|
140
|
-
* @default
|
|
141
|
+
* @default 300_000 `(5 minutes)`
|
|
141
142
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultpreloadgctime-property)
|
|
142
143
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/preloading)
|
|
143
144
|
*/
|
|
@@ -169,7 +170,7 @@ export interface RouterOptions<TRouteTree extends AnyRoute, TTrailingSlashOption
|
|
|
169
170
|
/**
|
|
170
171
|
* The default `gcTime` a route should use if no gcTime is provided.
|
|
171
172
|
*
|
|
172
|
-
* @default
|
|
173
|
+
* @default 300_000 `(5 minutes)`
|
|
173
174
|
* @link [API Docs](https://tanstack.com/router/latest/docs/framework/react/api/router/RouterOptionsType#defaultgctime-property)
|
|
174
175
|
* @link [Guide](https://tanstack.com/router/latest/docs/framework/react/guide/data-loading#key-options)
|
|
175
176
|
*/
|
|
@@ -389,14 +390,10 @@ export type LocationRewriteFunction = ({ url, }: {
|
|
|
389
390
|
}) => undefined | string | URL;
|
|
390
391
|
export interface RouterState<in out TRouteTree extends AnyRoute = AnyRoute, in out TRouteMatch = MakeRouteMatchUnion> {
|
|
391
392
|
status: 'pending' | 'idle';
|
|
392
|
-
loadedAt: number;
|
|
393
393
|
isLoading: boolean;
|
|
394
|
-
isTransitioning: boolean;
|
|
395
394
|
matches: Array<TRouteMatch>;
|
|
396
395
|
location: ParsedLocation<FullSearchSchema<TRouteTree>>;
|
|
397
396
|
resolvedLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>;
|
|
398
|
-
statusCode: number;
|
|
399
|
-
redirect?: AnyRedirect;
|
|
400
397
|
}
|
|
401
398
|
export interface BuildNextOptions {
|
|
402
399
|
to?: string | number | null;
|
|
@@ -453,9 +450,7 @@ export type RouterListener<TRouterEvent extends RouterEvent> = {
|
|
|
453
450
|
};
|
|
454
451
|
export type SubscribeFn = <TType extends keyof RouterEvents>(eventType: TType, fn: ListenerFn<RouterEvents[TType]>) => () => void;
|
|
455
452
|
export interface MatchRoutesOpts {
|
|
456
|
-
preload?: boolean;
|
|
457
453
|
throwOnError?: boolean;
|
|
458
|
-
dest?: BuildNextOptions;
|
|
459
454
|
}
|
|
460
455
|
export type InferRouterContext<TRouteTree extends AnyRoute> = TRouteTree['types']['routerContext'];
|
|
461
456
|
export type RouterContextOptions<TRouteTree extends AnyRoute> = AnyContext extends InferRouterContext<TRouteTree> ? {
|
|
@@ -486,9 +481,11 @@ export type LoadFn = (opts?: {
|
|
|
486
481
|
action?: {
|
|
487
482
|
type: HistoryAction;
|
|
488
483
|
};
|
|
484
|
+
_signal?: AbortSignal;
|
|
485
|
+
_dedupe?: boolean;
|
|
489
486
|
}) => Promise<void>;
|
|
490
487
|
export type CommitLocationFn = ({ viewTransition, ignoreBlocker, ...next }: ParsedLocation & CommitLocationOptions) => Promise<void>;
|
|
491
|
-
export type StartTransitionFn = (fn: () => void) =>
|
|
488
|
+
export type StartTransitionFn = (fn: () => void, expected: Array<AnyRouteMatch>, urgent?: boolean) => Promise<boolean>;
|
|
492
489
|
export interface MatchRoutesFn {
|
|
493
490
|
(pathname: string, locationSearch?: AnySchema, opts?: MatchRoutesOpts): Array<MakeRouteMatchUnion>;
|
|
494
491
|
/**
|
|
@@ -497,8 +494,6 @@ export interface MatchRoutesFn {
|
|
|
497
494
|
(next: ParsedLocation, opts?: MatchRoutesOpts): Array<AnyRouteMatch>;
|
|
498
495
|
(pathnameOrNext: string | ParsedLocation, locationSearchOrOpts?: AnySchema | MatchRoutesOpts, opts?: MatchRoutesOpts): Array<AnyRouteMatch>;
|
|
499
496
|
}
|
|
500
|
-
export type GetMatchFn = (matchId: string) => AnyRouteMatch | undefined;
|
|
501
|
-
export type UpdateMatchFn = (id: string, updater: (match: AnyRouteMatch) => AnyRouteMatch) => void;
|
|
502
497
|
export type LoadRouteChunkFn = (route: AnyRoute) => Promise<Array<void>>;
|
|
503
498
|
export type ResolveRedirect = (err: AnyRedirect) => ResolvedRedirect;
|
|
504
499
|
export type ClearCacheFn<TRouter extends AnyRouter> = (opts?: {
|
|
@@ -585,7 +580,14 @@ export declare function getLocationChangeInfo(location: ParsedLocation, resolved
|
|
|
585
580
|
hrefChanged: boolean;
|
|
586
581
|
hashChanged: boolean;
|
|
587
582
|
};
|
|
588
|
-
|
|
583
|
+
/**
|
|
584
|
+
* Return only state owned by the application, excluding volatile history
|
|
585
|
+
* bookkeeping. Mask payloads (`__tempLocation`/`__tempKey`) are kept: they
|
|
586
|
+
* distinguish otherwise-identical locations.
|
|
587
|
+
*/
|
|
588
|
+
export declare function _getUserHistoryState({ key: _key, __TSR_key: _tsrKey, __TSR_index: _tsrIndex, __hashScrollIntoViewOptions: _hashScroll, ...state }: ParsedHistoryState): HistoryState;
|
|
589
|
+
/** Run route lifecycle callbacks in leave/enter/stay phases. */
|
|
590
|
+
export declare function runRouteLifecycle(router: AnyRouter, previous: Array<AnyRouteMatch>, matches: Array<AnyRouteMatch>, isCurrent?: () => boolean): void;
|
|
589
591
|
export type CreateRouterFn = <TRouteTree extends AnyRoute, TTrailingSlashOption extends TrailingSlashOption = 'never', TDefaultStructuralSharingOption extends boolean = false, TRouterHistory extends RouterHistory = RouterHistory, TDehydrated extends Record<string, any> = Record<string, any>>(options: undefined extends number ? 'strictNullChecks must be enabled in tsconfig.json' : RouterConstructorOptions<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory, TDehydrated>) => RouterCore<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory, TDehydrated>;
|
|
590
592
|
declare global {
|
|
591
593
|
var __TSR_CACHE__: {
|
|
@@ -594,6 +596,33 @@ declare global {
|
|
|
594
596
|
resolvePathCache: LRUCache<string, string>;
|
|
595
597
|
} | undefined;
|
|
596
598
|
}
|
|
599
|
+
export interface RouterCore<in out TRouteTree extends AnyRoute, in out TTrailingSlashOption extends TrailingSlashOption, in out TDefaultStructuralSharingOption extends boolean, in out TRouterHistory extends RouterHistory = RouterHistory, in out TDehydrated extends Record<string, any> = Record<string, any>> {
|
|
600
|
+
shouldViewTransition?: boolean | ViewTransitionOptions;
|
|
601
|
+
/** Current client load transaction and owner of navigation writes. */
|
|
602
|
+
_tx?: LoadTransaction;
|
|
603
|
+
/** Joinable in-flight loader generations keyed by match ID. */
|
|
604
|
+
_flights?: Map<string, LoaderFlight>;
|
|
605
|
+
/** Whole speculative lanes that an identical navigation may adopt. */
|
|
606
|
+
_preloads?: Map<string, ActivePreload>;
|
|
607
|
+
/** Owns cancellable work before a client transaction publishes. */
|
|
608
|
+
_preflight?: AbortController;
|
|
609
|
+
/** Transfers one reconstructed SSR prefix into its initial client load. */
|
|
610
|
+
_handoff?: HydrationHandoff;
|
|
611
|
+
/** Pending-boundary reveal and minimum-visible timing state. */
|
|
612
|
+
_pending?: PendingSession;
|
|
613
|
+
/** Result of the latest server load, used to render or redirect. */
|
|
614
|
+
_serverResult?: ServerLoadResult;
|
|
615
|
+
/** Framework callback that acknowledges an exact matches publication. */
|
|
616
|
+
_rendered?: (matches: Array<AnyRouteMatch>) => void;
|
|
617
|
+
/** Development-only HMR reload for a route and its descendants. */
|
|
618
|
+
_refreshRoute: (() => Promise<void>) | undefined;
|
|
619
|
+
/** Development-only replacement for a route's lazy chunk owner. */
|
|
620
|
+
_replaceRouteChunk: ((route: AnyRoute, lazyFn: AnyRoute['lazyFn']) => void) | undefined;
|
|
621
|
+
}
|
|
622
|
+
export type HydrationHandoff = [
|
|
623
|
+
claim: () => AbortController | undefined,
|
|
624
|
+
finish: (matches?: Array<AnyRouteMatch>) => number | undefined
|
|
625
|
+
];
|
|
597
626
|
/**
|
|
598
627
|
* Core, framework-agnostic router engine that powers TanStack Router.
|
|
599
628
|
*
|
|
@@ -607,14 +636,16 @@ export declare class RouterCore<in out TRouteTree extends AnyRoute, in out TTrai
|
|
|
607
636
|
tempLocationKey: string | undefined;
|
|
608
637
|
_scroll: {
|
|
609
638
|
next: boolean;
|
|
639
|
+
hash?: boolean;
|
|
610
640
|
restoring?: boolean;
|
|
611
641
|
restoration?: boolean;
|
|
612
642
|
reset?: boolean;
|
|
613
643
|
};
|
|
614
|
-
shouldViewTransition?: boolean | ViewTransitionOptions;
|
|
615
|
-
isViewTransitionTypesSupported?: boolean;
|
|
616
644
|
subscribers: Set<RouterListener<RouterEvent>>;
|
|
617
|
-
|
|
645
|
+
/** Accepted off-screen loader generations keyed by match ID. */
|
|
646
|
+
_cache: Map<string, AnyRouteMatch>;
|
|
647
|
+
/** Accepted semantic lane, excluding temporary pending presentation. */
|
|
648
|
+
_committed: Array<AnyRouteMatch>;
|
|
618
649
|
stores: RouterStores<TRouteTree>;
|
|
619
650
|
private getStoreConfig;
|
|
620
651
|
batch: (fn: () => void) => void;
|
|
@@ -623,7 +654,7 @@ export declare class RouterCore<in out TRouteTree extends AnyRoute, in out TTrai
|
|
|
623
654
|
rewrite?: LocationRewrite;
|
|
624
655
|
origin?: string;
|
|
625
656
|
latestLocation: ParsedLocation<FullSearchSchema<TRouteTree>>;
|
|
626
|
-
|
|
657
|
+
_pendingLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>;
|
|
627
658
|
basepath: string;
|
|
628
659
|
routeTree: TRouteTree;
|
|
629
660
|
routesById: RoutesById<TRouteTree>;
|
|
@@ -641,7 +672,6 @@ export declare class RouterCore<in out TRouteTree extends AnyRoute, in out TTrai
|
|
|
641
672
|
constructor(options: RouterConstructorOptions<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory, TDehydrated>, getStoreConfig: GetStoreConfig);
|
|
642
673
|
startTransition: StartTransitionFn;
|
|
643
674
|
isShell(): boolean;
|
|
644
|
-
isPrerendering(): boolean;
|
|
645
675
|
update: UpdateFn<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory, TDehydrated>;
|
|
646
676
|
get state(): RouterState<TRouteTree>;
|
|
647
677
|
updateLatestLocation: () => void;
|
|
@@ -663,19 +693,15 @@ export declare class RouterCore<in out TRouteTree extends AnyRoute, in out TTrai
|
|
|
663
693
|
/** Resolve a path using the router's trailing-slash policy. */
|
|
664
694
|
resolvePathWithBase: (from: string, path: string) => string;
|
|
665
695
|
private getRouteBranch;
|
|
666
|
-
get looseRoutesById(): Record<string, AnyRoute>;
|
|
667
696
|
matchRoutes: MatchRoutesFn;
|
|
668
|
-
private getParentContext;
|
|
669
697
|
private matchRoutesInternal;
|
|
670
698
|
getMatchedRoutes: GetMatchRoutesFn;
|
|
671
699
|
/**
|
|
672
700
|
* Lightweight route matching for buildLocation.
|
|
673
701
|
* Only computes fullPath, accumulated search, and params - skipping expensive
|
|
674
|
-
* operations like AbortController,
|
|
702
|
+
* operations like AbortController, loaderDeps, and full match objects.
|
|
675
703
|
*/
|
|
676
704
|
private matchRoutesLightweight;
|
|
677
|
-
cancelMatch: (id: string) => void;
|
|
678
|
-
cancelMatches: () => void;
|
|
679
705
|
/**
|
|
680
706
|
* Build the next ParsedLocation from navigation options without committing.
|
|
681
707
|
* Resolves `to`/`from`, params/search/hash/state, applies search validation
|
|
@@ -684,14 +710,18 @@ export declare class RouterCore<in out TRouteTree extends AnyRoute, in out TTrai
|
|
|
684
710
|
* @link https://tanstack.com/router/latest/docs/framework/react/api/router/RouterType#buildlocation-method
|
|
685
711
|
*/
|
|
686
712
|
buildLocation: BuildLocationFn;
|
|
687
|
-
|
|
713
|
+
_commitPromise: (Promise<void> & {
|
|
714
|
+
resolve: () => void;
|
|
715
|
+
}) | undefined;
|
|
688
716
|
/**
|
|
689
717
|
* Commit a previously built location to history (push/replace), optionally
|
|
690
718
|
* using view transitions and scroll restoration options.
|
|
691
719
|
*/
|
|
692
720
|
commitLocation: CommitLocationFn;
|
|
693
721
|
/** Convenience helper: build a location from options, then commit it. */
|
|
694
|
-
buildAndCommitLocation: ({ replace, resetScroll, hashScrollIntoView, viewTransition, ignoreBlocker, href, ...rest }?: BuildNextOptions & CommitLocationOptions
|
|
722
|
+
buildAndCommitLocation: ({ replace, resetScroll, hashScrollIntoView, viewTransition, ignoreBlocker, _redirects, href, ...rest }?: BuildNextOptions & CommitLocationOptions & {
|
|
723
|
+
_redirects?: number;
|
|
724
|
+
}) => Promise<void>;
|
|
695
725
|
/**
|
|
696
726
|
* Imperatively navigate using standard `NavigateOptions`. When `reloadDocument`
|
|
697
727
|
* or an absolute `href` is provided, performs a full document navigation.
|
|
@@ -700,25 +730,19 @@ export declare class RouterCore<in out TRouteTree extends AnyRoute, in out TTrai
|
|
|
700
730
|
* @link https://tanstack.com/router/latest/docs/framework/react/api/router/NavigateOptionsType
|
|
701
731
|
*/
|
|
702
732
|
navigate: NavigateFn;
|
|
703
|
-
latestLoadPromise: undefined | Promise<void>;
|
|
704
|
-
beforeLoad: () => void;
|
|
705
733
|
load: LoadFn;
|
|
706
|
-
startViewTransition: (fn: () => Promise<void>) =>
|
|
707
|
-
updateMatch: UpdateMatchFn;
|
|
708
|
-
getMatch: GetMatchFn;
|
|
734
|
+
startViewTransition: (fn: () => Promise<void>) => any;
|
|
709
735
|
/**
|
|
710
736
|
* Invalidate the current matches and optionally force them back into a pending state.
|
|
711
737
|
*
|
|
712
738
|
* - Marks all matches that pass the optional `filter` as `invalid: true`.
|
|
713
|
-
*
|
|
714
|
-
*
|
|
715
|
-
*
|
|
739
|
+
*
|
|
740
|
+
* The next load decides when to publish pending UI, so invalidation does not
|
|
741
|
+
* mutate the currently rendered status.
|
|
716
742
|
*/
|
|
717
743
|
invalidate: InvalidateFn<RouterCore<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory, TDehydrated>>;
|
|
718
|
-
getParsedLocationHref: (location: ParsedLocation) => string;
|
|
719
744
|
resolveRedirect: (redirect: AnyRedirect) => AnyRedirect;
|
|
720
745
|
clearCache: ClearCacheFn<this>;
|
|
721
|
-
clearExpiredCache: () => void;
|
|
722
746
|
loadRouteChunk: typeof loadRouteChunk;
|
|
723
747
|
preloadRoute: PreloadRouteFn<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory>;
|
|
724
748
|
matchRoute: MatchRouteFn<TRouteTree, TTrailingSlashOption, TDefaultStructuralSharingOption, TRouterHistory>;
|
|
@@ -727,7 +751,6 @@ export declare class RouterCore<in out TRouteTree extends AnyRoute, in out TTrai
|
|
|
727
751
|
};
|
|
728
752
|
serverSsr?: ServerSsr;
|
|
729
753
|
serverSsrLifecycle?: RouterSsrLifecycle;
|
|
730
|
-
hasNotFoundMatch: () => boolean;
|
|
731
754
|
}
|
|
732
755
|
/** Error thrown when search parameter validation fails. */
|
|
733
756
|
export declare class SearchParamError extends Error {
|
|
@@ -742,17 +765,4 @@ export declare class PathParamError extends Error {
|
|
|
742
765
|
export declare function lazyFn<T extends Record<string, (...args: Array<any>) => any>, TKey extends keyof T = 'default'>(fn: () => Promise<T>, key?: TKey): (...args: Parameters<T[TKey]>) => Promise<Awaited<ReturnType<T[TKey]>>>;
|
|
743
766
|
/** Create an initial RouterState from a parsed location. */
|
|
744
767
|
export declare function getInitialRouterState(location: ParsedLocation): RouterState<any>;
|
|
745
|
-
/**
|
|
746
|
-
* Build the matched route chain and extract params for a pathname.
|
|
747
|
-
* Falls back to the root route if no specific route is found.
|
|
748
|
-
*/
|
|
749
|
-
export declare function getMatchedRoutes<TRouteLike extends RouteLike>({ pathname, routesById, processedTree, }: {
|
|
750
|
-
pathname: string;
|
|
751
|
-
routesById: Record<string, TRouteLike>;
|
|
752
|
-
processedTree: ProcessedTree<any, any, any>;
|
|
753
|
-
}): {
|
|
754
|
-
matchedRoutes: readonly TRouteLike[];
|
|
755
|
-
routeParams: Record<string, string>;
|
|
756
|
-
foundRoute: TRouteLike | undefined;
|
|
757
|
-
};
|
|
758
768
|
export {};
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
const require_router = require("./router.cjs");
|
|
2
1
|
let _tanstack_router_core_isServer = require("@tanstack/router-core/isServer");
|
|
3
2
|
//#region src/scroll-restoration.ts
|
|
4
3
|
function getSafeSessionStorage() {
|
|
@@ -67,11 +66,11 @@ function getElement(selector) {
|
|
|
67
66
|
} catch {}
|
|
68
67
|
}
|
|
69
68
|
function getScrollToTopElements(scrollToTopSelectors) {
|
|
70
|
-
const elements =
|
|
69
|
+
const elements = /* @__PURE__ */ new Set();
|
|
71
70
|
for (const selector of scrollToTopSelectors) {
|
|
72
71
|
if (selector === windowScrollTarget) continue;
|
|
73
72
|
const element = getElement(selector);
|
|
74
|
-
if (element) elements.
|
|
73
|
+
if (element) elements.add(element);
|
|
75
74
|
}
|
|
76
75
|
return elements;
|
|
77
76
|
}
|
|
@@ -81,35 +80,29 @@ function setupScrollRestoration(router, force) {
|
|
|
81
80
|
if (shouldSetupScrollRestoration) scroll.restoring = true;
|
|
82
81
|
if (_tanstack_router_core_isServer.isServer ?? router.isServer) return;
|
|
83
82
|
const getKey = router.options.getScrollRestorationKey || defaultGetScrollRestorationKey;
|
|
84
|
-
const
|
|
85
|
-
const setTrackedScrollEntry = (target, scrollX, scrollY) => {
|
|
86
|
-
const entry = trackedScrollEntries.get(target) || {};
|
|
87
|
-
entry.scrollX = scrollX;
|
|
88
|
-
entry.scrollY = scrollY;
|
|
89
|
-
trackedScrollEntries.set(target, entry);
|
|
90
|
-
};
|
|
91
|
-
const onScroll = (event) => {
|
|
92
|
-
if (ignoreScroll || !scroll.restoring) return;
|
|
93
|
-
if (event.target === document) setTrackedScrollEntry(windowScrollTarget, scrollX, scrollY);
|
|
94
|
-
else {
|
|
95
|
-
const target = event.target;
|
|
96
|
-
setTrackedScrollEntry(target, target.scrollLeft, target.scrollTop);
|
|
97
|
-
}
|
|
98
|
-
};
|
|
83
|
+
const trackedScrollTargets = /* @__PURE__ */ new Set();
|
|
99
84
|
const snapshotCurrentScrollTargets = (restoreKey) => {
|
|
100
|
-
if (!scroll.restoring) return;
|
|
101
85
|
const keyEntry = scrollRestorationCache[restoreKey] ||= {};
|
|
102
|
-
for (const
|
|
103
|
-
|
|
86
|
+
for (const target of trackedScrollTargets) if (target === document) keyEntry[windowScrollTarget] = {
|
|
87
|
+
scrollX,
|
|
88
|
+
scrollY
|
|
89
|
+
};
|
|
90
|
+
else if (target.isConnected) keyEntry[getScrollRestorationSelector(target)] = {
|
|
91
|
+
scrollX: target.scrollLeft,
|
|
92
|
+
scrollY: target.scrollTop
|
|
93
|
+
};
|
|
104
94
|
};
|
|
105
95
|
if (shouldSetupScrollRestoration && !scroll.restoration) {
|
|
106
96
|
scroll.restoration = true;
|
|
107
97
|
ignoreScroll = false;
|
|
108
98
|
history.scrollRestoration = "manual";
|
|
109
|
-
document.addEventListener("scroll",
|
|
99
|
+
document.addEventListener("scroll", (event) => {
|
|
100
|
+
if (ignoreScroll) return;
|
|
101
|
+
trackedScrollTargets.add(event.target);
|
|
102
|
+
}, true);
|
|
110
103
|
router.subscribe("onBeforeLoad", (event) => {
|
|
111
104
|
if (event.fromLocation) snapshotCurrentScrollTargets(getKey(event.fromLocation));
|
|
112
|
-
|
|
105
|
+
trackedScrollTargets.clear();
|
|
113
106
|
});
|
|
114
107
|
addEventListener("pagehide", () => {
|
|
115
108
|
snapshotCurrentScrollTargets(getKey(router.stores.resolvedLocation.get() ?? router.stores.location.get()));
|
|
@@ -122,9 +115,11 @@ function setupScrollRestoration(router, force) {
|
|
|
122
115
|
const behavior = router.options.scrollRestorationBehavior;
|
|
123
116
|
const scrollToTopSelectors = router.options.scrollToTopSelectors;
|
|
124
117
|
const shouldResetScroll = scroll.next;
|
|
118
|
+
const hashNavigation = scroll.hash;
|
|
125
119
|
let scrollToTopElements;
|
|
126
|
-
|
|
127
|
-
|
|
120
|
+
trackedScrollTargets.clear();
|
|
121
|
+
scroll.next = true;
|
|
122
|
+
scroll.hash = false;
|
|
128
123
|
if (typeof router.options.scrollRestoration === "function" && !router.options.scrollRestoration({ location: router.latestLocation })) return;
|
|
129
124
|
const cacheKey = getKey(event.toLocation);
|
|
130
125
|
const fromCacheKey = event.fromLocation && getKey(event.fromLocation);
|
|
@@ -140,7 +135,7 @@ function setupScrollRestoration(router, force) {
|
|
|
140
135
|
if (!element) continue;
|
|
141
136
|
if (shouldResetScroll && scrollToTopSelectors) {
|
|
142
137
|
scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors);
|
|
143
|
-
if (scrollToTopElements.
|
|
138
|
+
if (scrollToTopElements.has(element)) continue;
|
|
144
139
|
}
|
|
145
140
|
}
|
|
146
141
|
if (!toElementEntries) toElementEntries = scrollRestorationCache[cacheKey] = {};
|
|
@@ -154,8 +149,8 @@ function setupScrollRestoration(router, force) {
|
|
|
154
149
|
const hashScrollIntoViewOptions = event.toLocation.state.__hashScrollIntoViewOptions ?? true;
|
|
155
150
|
let windowRestored = false;
|
|
156
151
|
if (shouldResetScroll) {
|
|
157
|
-
|
|
158
|
-
const skipWindowRestore = hash && hashScrollIntoViewOptions &&
|
|
152
|
+
if (!hash && scrollToTopSelectors) scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors);
|
|
153
|
+
const skipWindowRestore = hash && hashScrollIntoViewOptions && hashNavigation;
|
|
159
154
|
const elementEntries = scroll.restoring ? scrollRestorationCache[cacheKey] : void 0;
|
|
160
155
|
if (elementEntries) for (const elementSelector in elementEntries) {
|
|
161
156
|
const { scrollX, scrollY } = elementEntries[elementSelector];
|
|
@@ -172,20 +167,18 @@ function setupScrollRestoration(router, force) {
|
|
|
172
167
|
if (element) {
|
|
173
168
|
element.scrollLeft = scrollX;
|
|
174
169
|
element.scrollTop = scrollY;
|
|
170
|
+
scrollToTopElements?.delete(element);
|
|
175
171
|
}
|
|
176
172
|
}
|
|
177
173
|
}
|
|
178
|
-
if (!
|
|
174
|
+
if (!hash) {
|
|
179
175
|
const scrollOptions = {
|
|
180
176
|
top: 0,
|
|
181
177
|
left: 0,
|
|
182
178
|
behavior
|
|
183
179
|
};
|
|
184
|
-
scrollTo(scrollOptions);
|
|
185
|
-
if (
|
|
186
|
-
scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors);
|
|
187
|
-
for (const element of scrollToTopElements) element.scrollTo(scrollOptions);
|
|
188
|
-
}
|
|
180
|
+
if (!windowRestored) scrollTo(scrollOptions);
|
|
181
|
+
if (scrollToTopElements) for (const element of scrollToTopElements) element.scrollTo(scrollOptions);
|
|
189
182
|
}
|
|
190
183
|
}
|
|
191
184
|
if (!windowRestored && hash && hashScrollIntoViewOptions) document.getElementById(hash)?.scrollIntoView(hashScrollIntoViewOptions);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"scroll-restoration.cjs","names":[],"sources":["../../src/scroll-restoration.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport { locationHistoryActions } from './router'\nimport type { AnyRouter } from './router'\nimport type { ParsedLocation } from './location'\n\nexport type ScrollRestorationEntry = { scrollX: number; scrollY: number }\n\ntype ScrollRestorationByElement = Record<string, ScrollRestorationEntry>\n\ntype ScrollRestorationByKey = Record<string, ScrollRestorationByElement>\n\nexport type ScrollRestorationOptions = {\n getKey?: (location: ParsedLocation) => string\n scrollBehavior?: ScrollToOptions['behavior']\n}\n\nfunction getSafeSessionStorage() {\n try {\n // Accessing sessionStorage itself can throw SecurityError in locked-down\n // contexts, e.g. sandboxed/opaque origins or blocked storage policies.\n return sessionStorage\n } catch {\n return\n }\n}\n\n// SessionStorage key used to store scroll positions across navigations.\nexport const storageKey = 'tsr-scroll-restoration-v1_3'\nconst safeSessionStorage = getSafeSessionStorage()\n\nfunction createScrollRestorationCache() {\n try {\n return JSON.parse(\n safeSessionStorage?.getItem(storageKey) || '{}',\n ) as ScrollRestorationByKey\n } catch {\n // ignore invalid session storage payloads\n return {}\n }\n}\n\nfunction persistScrollRestorationCache() {\n try {\n safeSessionStorage?.setItem(\n storageKey,\n JSON.stringify(scrollRestorationCache),\n )\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[ts-router] Could not persist scroll restoration state to sessionStorage.',\n )\n }\n }\n}\n\nconst scrollRestorationCache = /* @__PURE__ */ createScrollRestorationCache()\nconst scrollRestorationIdAttribute = 'data-scroll-restoration-id'\n\n/**\n * The default `getKey` function for `useScrollRestoration`.\n * It returns the `key` from the location state or the `href` of the location.\n *\n * The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.\n */\nexport const defaultGetScrollRestorationKey = (location: ParsedLocation) => {\n return location.state.__TSR_key! || location.href\n}\n\nfunction getScrollRestorationSelector(element: Element): string {\n const attrId = element.getAttribute(scrollRestorationIdAttribute)\n if (attrId) {\n return `[${scrollRestorationIdAttribute}=\"${attrId}\"]`\n }\n\n let selector = ''\n let el: any = element\n let parent: HTMLElement\n\n while ((parent = el.parentNode)) {\n let index = 1\n let sibling = el\n while ((sibling = sibling.previousElementSibling)) {\n index++\n }\n\n const part = `${el.localName}:nth-child(${index})`\n selector = selector ? `${part} > ${selector}` : part\n el = parent\n }\n\n return selector\n}\n\nexport function getElementScrollRestorationEntry(\n router: AnyRouter,\n options: (\n | {\n id: string\n getElement?: () => Window | Element | undefined | null\n }\n | {\n id?: string\n getElement: () => Window | Element | undefined | null\n }\n ) & {\n getKey?: (location: ParsedLocation) => string\n },\n): ScrollRestorationEntry | undefined {\n const getKey = options.getKey || defaultGetScrollRestorationKey\n const restoreKey = getKey(router.latestLocation)\n const entries = scrollRestorationCache[restoreKey]\n\n if (!entries) {\n return\n }\n\n if (options.id) {\n return entries[`[${scrollRestorationIdAttribute}=\"${options.id}\"]`]\n }\n\n const element = options.getElement?.()\n if (!element) {\n return\n }\n\n return entries[\n element === window\n ? windowScrollTarget\n : getScrollRestorationSelector(element as Element)\n ]\n}\n\nlet ignoreScroll = false\nconst windowScrollTarget = 'window'\ntype ScrollTarget = typeof windowScrollTarget | Element\n\nfunction getElement(selector: string | (() => Element | null | undefined)) {\n try {\n return typeof selector === 'function'\n ? selector()\n : document.querySelector(selector)\n } catch {}\n return\n}\n\nfunction getScrollToTopElements(\n scrollToTopSelectors: NonNullable<\n AnyRouter['options']['scrollToTopSelectors']\n >,\n): Array<Element> {\n const elements: Array<Element> = []\n\n for (const selector of scrollToTopSelectors) {\n if (selector === windowScrollTarget) {\n continue\n }\n\n const element = getElement(selector)\n if (element) {\n elements.push(element)\n }\n }\n\n return elements\n}\n\nexport function setupScrollRestoration(router: AnyRouter, force?: boolean) {\n // Keep hash/top scrolling active even when sessionStorage is unavailable.\n const shouldSetupScrollRestoration = force ?? router.options.scrollRestoration\n const scroll = router._scroll\n\n if (shouldSetupScrollRestoration) {\n scroll.restoring = true\n }\n\n if (isServer ?? router.isServer) {\n return\n }\n\n const getKey =\n router.options.getScrollRestorationKey || defaultGetScrollRestorationKey\n const trackedScrollEntries = new Map<ScrollTarget, ScrollRestorationEntry>()\n const setTrackedScrollEntry = (\n target: ScrollTarget,\n scrollX: number,\n scrollY: number,\n ) => {\n const entry =\n trackedScrollEntries.get(target) || ({} as ScrollRestorationEntry)\n entry.scrollX = scrollX\n entry.scrollY = scrollY\n trackedScrollEntries.set(target, entry)\n }\n\n const onScroll = (event: Event) => {\n if (ignoreScroll || !scroll.restoring) {\n return\n }\n\n if (event.target === document) {\n setTrackedScrollEntry(windowScrollTarget, scrollX, scrollY)\n } else {\n const target = event.target as Element\n setTrackedScrollEntry(target, target.scrollLeft, target.scrollTop)\n }\n }\n\n // Snapshot the current page's tracked scroll targets before navigation or unload.\n const snapshotCurrentScrollTargets = (restoreKey: string) => {\n if (!scroll.restoring) {\n return\n }\n\n const keyEntry = (scrollRestorationCache[restoreKey] ||=\n {} as ScrollRestorationByElement)\n\n for (const [target, position] of trackedScrollEntries) {\n if (target === windowScrollTarget) {\n keyEntry[windowScrollTarget] = position\n } else if (target.isConnected) {\n keyEntry[getScrollRestorationSelector(target)] = position\n }\n }\n }\n\n if (shouldSetupScrollRestoration && !scroll.restoration) {\n scroll.restoration = true\n ignoreScroll = false\n\n history.scrollRestoration = 'manual'\n\n document.addEventListener('scroll', onScroll, true)\n router.subscribe('onBeforeLoad', (event) => {\n if (event.fromLocation) {\n snapshotCurrentScrollTargets(getKey(event.fromLocation))\n }\n trackedScrollEntries.clear()\n })\n addEventListener('pagehide', () => {\n snapshotCurrentScrollTargets(\n getKey(\n router.stores.resolvedLocation.get() ?? router.stores.location.get(),\n ),\n )\n persistScrollRestorationCache()\n })\n }\n\n if (scroll.reset) {\n return\n }\n\n scroll.reset = true\n\n // Restore destination scroll after the new route has rendered.\n router.subscribe('onRendered', (event) => {\n const behavior = router.options.scrollRestorationBehavior\n const scrollToTopSelectors = router.options.scrollToTopSelectors\n const shouldResetScroll = scroll.next\n let scrollToTopElements: Array<Element> | undefined\n trackedScrollEntries.clear()\n\n if (!shouldResetScroll) {\n scroll.next = true\n }\n\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return\n }\n\n const cacheKey = getKey(event.toLocation)\n const fromCacheKey = event.fromLocation && getKey(event.fromLocation)\n\n if (scroll.restoring && fromCacheKey && fromCacheKey !== cacheKey) {\n const fromElementEntries = scrollRestorationCache[fromCacheKey]\n\n if (fromElementEntries) {\n let toElementEntries = scrollRestorationCache[cacheKey]\n\n for (const elementSelector in fromElementEntries) {\n if (elementSelector === windowScrollTarget) {\n if (shouldResetScroll) {\n continue\n }\n } else {\n const element = getElement(elementSelector)\n if (!element) {\n continue\n }\n\n if (shouldResetScroll && scrollToTopSelectors) {\n scrollToTopElements ??=\n getScrollToTopElements(scrollToTopSelectors)\n if (scrollToTopElements.includes(element)) {\n continue\n }\n }\n }\n\n if (!toElementEntries) {\n toElementEntries = scrollRestorationCache[cacheKey] =\n {} as ScrollRestorationByElement\n }\n\n toElementEntries[elementSelector] ??=\n fromElementEntries[elementSelector]!\n }\n }\n }\n\n ignoreScroll = true\n\n try {\n const hash = event.toLocation.hash\n const hashScrollIntoViewOptions =\n event.toLocation.state.__hashScrollIntoViewOptions ?? true\n let windowRestored = false\n\n if (shouldResetScroll) {\n const action = locationHistoryActions.get(event.toLocation)\n const skipWindowRestore =\n hash &&\n hashScrollIntoViewOptions &&\n (action === 'PUSH' || action === 'REPLACE')\n\n const elementEntries = scroll.restoring\n ? scrollRestorationCache[cacheKey]\n : undefined\n\n if (elementEntries) {\n for (const elementSelector in elementEntries) {\n const { scrollX, scrollY } = elementEntries[elementSelector]!\n\n if (elementSelector === windowScrollTarget) {\n if (skipWindowRestore) {\n continue\n }\n\n scrollTo({\n top: scrollY,\n left: scrollX,\n behavior,\n })\n windowRestored = true\n } else {\n const element = getElement(elementSelector)\n if (element) {\n element.scrollLeft = scrollX\n element.scrollTop = scrollY\n }\n }\n }\n }\n\n if (!windowRestored && !hash) {\n const scrollOptions = {\n top: 0,\n left: 0,\n behavior,\n }\n\n scrollTo(scrollOptions)\n if (scrollToTopSelectors) {\n scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors)\n for (const element of scrollToTopElements) {\n element.scrollTo(scrollOptions)\n }\n }\n }\n }\n\n if (!windowRestored && hash && hashScrollIntoViewOptions) {\n document.getElementById(hash)?.scrollIntoView(hashScrollIntoViewOptions)\n }\n } finally {\n ignoreScroll = false\n }\n })\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"scroll-restoration.cjs","names":[],"sources":["../../src/scroll-restoration.ts"],"sourcesContent":["import { isServer } from '@tanstack/router-core/isServer'\nimport type { AnyRouter } from './router'\nimport type { ParsedLocation } from './location'\n\nexport type ScrollRestorationEntry = { scrollX: number; scrollY: number }\n\ntype ScrollRestorationByElement = Record<string, ScrollRestorationEntry>\n\ntype ScrollRestorationByKey = Record<string, ScrollRestorationByElement>\n\nexport type ScrollRestorationOptions = {\n getKey?: (location: ParsedLocation) => string\n scrollBehavior?: ScrollToOptions['behavior']\n}\n\nfunction getSafeSessionStorage() {\n try {\n // Accessing sessionStorage itself can throw SecurityError in locked-down\n // contexts, e.g. sandboxed/opaque origins or blocked storage policies.\n return sessionStorage\n } catch {\n return\n }\n}\n\n// SessionStorage key used to store scroll positions across navigations.\nexport const storageKey = 'tsr-scroll-restoration-v1_3'\nconst safeSessionStorage = getSafeSessionStorage()\n\nfunction createScrollRestorationCache() {\n try {\n return JSON.parse(\n safeSessionStorage?.getItem(storageKey) || '{}',\n ) as ScrollRestorationByKey\n } catch {\n // ignore invalid session storage payloads\n return {}\n }\n}\n\nfunction persistScrollRestorationCache() {\n try {\n safeSessionStorage?.setItem(\n storageKey,\n JSON.stringify(scrollRestorationCache),\n )\n } catch {\n if (process.env.NODE_ENV !== 'production') {\n console.warn(\n '[ts-router] Could not persist scroll restoration state to sessionStorage.',\n )\n }\n }\n}\n\nconst scrollRestorationCache = /* @__PURE__ */ createScrollRestorationCache()\nconst scrollRestorationIdAttribute = 'data-scroll-restoration-id'\n\n/**\n * The default `getKey` function for `useScrollRestoration`.\n * It returns the `key` from the location state or the `href` of the location.\n *\n * The `location.href` is used as a fallback to support the use case where the location state is not available like the initial render.\n */\nexport const defaultGetScrollRestorationKey = (location: ParsedLocation) => {\n return location.state.__TSR_key! || location.href\n}\n\nfunction getScrollRestorationSelector(element: Element): string {\n const attrId = element.getAttribute(scrollRestorationIdAttribute)\n if (attrId) {\n return `[${scrollRestorationIdAttribute}=\"${attrId}\"]`\n }\n\n let selector = ''\n let el: any = element\n let parent: HTMLElement\n\n while ((parent = el.parentNode)) {\n let index = 1\n let sibling = el\n while ((sibling = sibling.previousElementSibling)) {\n index++\n }\n\n const part = `${el.localName}:nth-child(${index})`\n selector = selector ? `${part} > ${selector}` : part\n el = parent\n }\n\n return selector\n}\n\nexport function getElementScrollRestorationEntry(\n router: AnyRouter,\n options: (\n | {\n id: string\n getElement?: () => Window | Element | undefined | null\n }\n | {\n id?: string\n getElement: () => Window | Element | undefined | null\n }\n ) & {\n getKey?: (location: ParsedLocation) => string\n },\n): ScrollRestorationEntry | undefined {\n const getKey = options.getKey || defaultGetScrollRestorationKey\n const restoreKey = getKey(router.latestLocation)\n const entries = scrollRestorationCache[restoreKey]\n\n if (!entries) {\n return\n }\n\n if (options.id) {\n return entries[`[${scrollRestorationIdAttribute}=\"${options.id}\"]`]\n }\n\n const element = options.getElement?.()\n if (!element) {\n return\n }\n\n return entries[\n element === window\n ? windowScrollTarget\n : getScrollRestorationSelector(element as Element)\n ]\n}\n\nlet ignoreScroll = false\nconst windowScrollTarget = 'window'\n\nfunction getElement(selector: string | (() => Element | null | undefined)) {\n try {\n return typeof selector === 'function'\n ? selector()\n : document.querySelector(selector)\n } catch {}\n return\n}\n\nfunction getScrollToTopElements(\n scrollToTopSelectors: NonNullable<\n AnyRouter['options']['scrollToTopSelectors']\n >,\n) {\n const elements = new Set<Element>()\n\n for (const selector of scrollToTopSelectors) {\n if (selector === windowScrollTarget) {\n continue\n }\n\n const element = getElement(selector)\n if (element) {\n elements.add(element)\n }\n }\n\n return elements\n}\n\nexport function setupScrollRestoration(router: AnyRouter, force?: boolean) {\n // Keep hash/top scrolling active even when sessionStorage is unavailable.\n const shouldSetupScrollRestoration = force ?? router.options.scrollRestoration\n const scroll = router._scroll\n\n if (shouldSetupScrollRestoration) {\n scroll.restoring = true\n }\n\n if (isServer ?? router.isServer) {\n return\n }\n\n const getKey =\n router.options.getScrollRestorationKey || defaultGetScrollRestorationKey\n const trackedScrollTargets = new Set<Document | Element>()\n\n // Snapshot the current page's tracked scroll targets before navigation or unload.\n const snapshotCurrentScrollTargets = (restoreKey: string) => {\n const keyEntry = (scrollRestorationCache[restoreKey] ||=\n {} as ScrollRestorationByElement)\n\n for (const target of trackedScrollTargets) {\n if (target === document) {\n keyEntry[windowScrollTarget] = { scrollX, scrollY }\n } else if ((target as Element).isConnected) {\n keyEntry[getScrollRestorationSelector(target as Element)] = {\n scrollX: (target as Element).scrollLeft,\n scrollY: (target as Element).scrollTop,\n }\n }\n }\n }\n\n if (shouldSetupScrollRestoration && !scroll.restoration) {\n scroll.restoration = true\n ignoreScroll = false\n\n history.scrollRestoration = 'manual'\n\n document.addEventListener(\n 'scroll',\n (event) => {\n if (ignoreScroll) {\n return\n }\n trackedScrollTargets.add(event.target as Document | Element)\n },\n true,\n )\n router.subscribe('onBeforeLoad', (event) => {\n if (event.fromLocation) {\n snapshotCurrentScrollTargets(getKey(event.fromLocation))\n }\n trackedScrollTargets.clear()\n })\n addEventListener('pagehide', () => {\n snapshotCurrentScrollTargets(\n getKey(\n router.stores.resolvedLocation.get() ?? router.stores.location.get(),\n ),\n )\n persistScrollRestorationCache()\n })\n }\n\n if (scroll.reset) {\n return\n }\n\n scroll.reset = true\n\n // Restore destination scroll after the new route has rendered.\n router.subscribe('onRendered', (event) => {\n const behavior = router.options.scrollRestorationBehavior\n const scrollToTopSelectors = router.options.scrollToTopSelectors\n const shouldResetScroll = scroll.next\n const hashNavigation = scroll.hash\n let scrollToTopElements: Set<Element> | undefined\n trackedScrollTargets.clear()\n scroll.next = true\n scroll.hash = false\n\n if (\n typeof router.options.scrollRestoration === 'function' &&\n !router.options.scrollRestoration({ location: router.latestLocation })\n ) {\n return\n }\n\n const cacheKey = getKey(event.toLocation)\n const fromCacheKey = event.fromLocation && getKey(event.fromLocation)\n\n if (scroll.restoring && fromCacheKey && fromCacheKey !== cacheKey) {\n const fromElementEntries = scrollRestorationCache[fromCacheKey]\n\n if (fromElementEntries) {\n let toElementEntries = scrollRestorationCache[cacheKey]\n\n for (const elementSelector in fromElementEntries) {\n if (elementSelector === windowScrollTarget) {\n if (shouldResetScroll) {\n continue\n }\n } else {\n const element = getElement(elementSelector)\n if (!element) {\n continue\n }\n\n if (shouldResetScroll && scrollToTopSelectors) {\n scrollToTopElements ??=\n getScrollToTopElements(scrollToTopSelectors)\n if (scrollToTopElements.has(element)) {\n continue\n }\n }\n }\n\n if (!toElementEntries) {\n toElementEntries = scrollRestorationCache[cacheKey] =\n {} as ScrollRestorationByElement\n }\n\n toElementEntries[elementSelector] ??=\n fromElementEntries[elementSelector]!\n }\n }\n }\n\n ignoreScroll = true\n\n try {\n const hash = event.toLocation.hash\n const hashScrollIntoViewOptions =\n event.toLocation.state.__hashScrollIntoViewOptions ?? true\n let windowRestored = false\n\n if (shouldResetScroll) {\n if (!hash && scrollToTopSelectors) {\n scrollToTopElements ??= getScrollToTopElements(scrollToTopSelectors)\n }\n\n const skipWindowRestore =\n hash && hashScrollIntoViewOptions && hashNavigation\n\n const elementEntries = scroll.restoring\n ? scrollRestorationCache[cacheKey]\n : undefined\n\n if (elementEntries) {\n for (const elementSelector in elementEntries) {\n const { scrollX, scrollY } = elementEntries[elementSelector]!\n\n if (elementSelector === windowScrollTarget) {\n if (skipWindowRestore) {\n continue\n }\n\n scrollTo({\n top: scrollY,\n left: scrollX,\n behavior,\n })\n windowRestored = true\n } else {\n const element = getElement(elementSelector)\n if (element) {\n element.scrollLeft = scrollX\n element.scrollTop = scrollY\n scrollToTopElements?.delete(element)\n }\n }\n }\n }\n\n if (!hash) {\n const scrollOptions = {\n top: 0,\n left: 0,\n behavior,\n }\n\n if (!windowRestored) {\n scrollTo(scrollOptions)\n }\n if (scrollToTopElements) {\n for (const element of scrollToTopElements) {\n element.scrollTo(scrollOptions)\n }\n }\n }\n }\n\n if (!windowRestored && hash && hashScrollIntoViewOptions) {\n document.getElementById(hash)?.scrollIntoView(hashScrollIntoViewOptions)\n }\n } finally {\n ignoreScroll = false\n }\n })\n}\n"],"mappings":"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"}
|
package/dist/cjs/ssr/client.cjs
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
Object.defineProperty(exports, Symbol.toStringTag, { value: "Module" });
|
|
2
|
+
const require_load_client = require("../load-client.cjs");
|
|
2
3
|
const require_headers = require("./headers.cjs");
|
|
3
4
|
const require_json = require("./json.cjs");
|
|
4
|
-
|
|
5
|
-
exports.hydrate = require_ssr_client.hydrate;
|
|
5
|
+
exports.hydrate = require_load_client.hydrate;
|
|
6
6
|
exports.json = require_json.json;
|
|
7
7
|
exports.mergeHeaders = require_headers.mergeHeaders;
|
|
@@ -1,16 +1,59 @@
|
|
|
1
|
+
const require_load_client = require("../load-client.cjs");
|
|
1
2
|
const require_headers = require("./headers.cjs");
|
|
2
3
|
const require_ssr_server = require("./ssr-server.cjs");
|
|
3
4
|
const require_handlerCallback = require("./handlerCallback.cjs");
|
|
4
5
|
let _tanstack_history = require("@tanstack/history");
|
|
5
6
|
//#region src/ssr/createRequestHandler.ts
|
|
7
|
+
const requestWaiters = /* @__PURE__ */ new WeakMap();
|
|
8
|
+
function removeRequestWaiter(waiters, index, reject) {
|
|
9
|
+
if (waiters[index] !== reject) return;
|
|
10
|
+
if (index !== waiters.length - 1) {
|
|
11
|
+
waiters[index] = void 0;
|
|
12
|
+
return;
|
|
13
|
+
}
|
|
14
|
+
waiters.pop();
|
|
15
|
+
while (waiters.length && waiters[waiters.length - 1] === void 0) waiters.pop();
|
|
16
|
+
}
|
|
17
|
+
function waitForRequest(value, signal, onLate) {
|
|
18
|
+
const promise = Promise.resolve(value);
|
|
19
|
+
if (signal.aborted) {
|
|
20
|
+
promise.then(onLate, () => {});
|
|
21
|
+
return Promise.reject(signal.reason);
|
|
22
|
+
}
|
|
23
|
+
return new Promise((resolve, reject) => {
|
|
24
|
+
let waiters = requestWaiters.get(signal);
|
|
25
|
+
let index;
|
|
26
|
+
if (waiters) index = waiters.push(reject) - 1;
|
|
27
|
+
else {
|
|
28
|
+
const newWaiters = [reject];
|
|
29
|
+
waiters = newWaiters;
|
|
30
|
+
index = 0;
|
|
31
|
+
requestWaiters.set(signal, newWaiters);
|
|
32
|
+
signal.addEventListener("abort", () => {
|
|
33
|
+
requestWaiters.delete(signal);
|
|
34
|
+
for (const rejectWaiter of newWaiters) rejectWaiter?.(signal.reason);
|
|
35
|
+
newWaiters.length = 0;
|
|
36
|
+
}, { once: true });
|
|
37
|
+
}
|
|
38
|
+
promise.then((result) => {
|
|
39
|
+
removeRequestWaiter(waiters, index, reject);
|
|
40
|
+
if (signal.aborted) onLate?.(result);
|
|
41
|
+
else resolve(result);
|
|
42
|
+
}, (error) => {
|
|
43
|
+
removeRequestWaiter(waiters, index, reject);
|
|
44
|
+
reject(error);
|
|
45
|
+
});
|
|
46
|
+
});
|
|
47
|
+
}
|
|
6
48
|
function createRequestHandler({ createRouter, request, getRouterManifest }) {
|
|
7
49
|
return async (cb) => {
|
|
50
|
+
request.signal.throwIfAborted();
|
|
8
51
|
const router = createRouter();
|
|
9
52
|
let responseOwnsCleanup = false;
|
|
10
53
|
try {
|
|
11
54
|
require_ssr_server.attachRouterServerSsrUtils({
|
|
12
55
|
router,
|
|
13
|
-
manifest: await getRouterManifest?.()
|
|
56
|
+
manifest: await waitForRequest(getRouterManifest?.(), request.signal)
|
|
14
57
|
});
|
|
15
58
|
const { url } = require_ssr_server.getNormalizedURL(request.url, "http://localhost");
|
|
16
59
|
const origin = require_ssr_server.getOrigin(request);
|
|
@@ -19,13 +62,22 @@ function createRequestHandler({ createRouter, request, getRouterManifest }) {
|
|
|
19
62
|
history,
|
|
20
63
|
origin: router.options.origin ?? origin
|
|
21
64
|
});
|
|
22
|
-
await router.load();
|
|
23
|
-
|
|
24
|
-
const
|
|
65
|
+
await router.load({ _signal: request.signal });
|
|
66
|
+
request.signal.throwIfAborted();
|
|
67
|
+
const result = router._serverResult;
|
|
68
|
+
if (result?.type === "redirect") return result.redirect;
|
|
69
|
+
await waitForRequest(router.serverSsr?.dehydrate(), request.signal);
|
|
70
|
+
request.signal.throwIfAborted();
|
|
71
|
+
const responseHeaders = getRequestHeaders({ router });
|
|
72
|
+
request.signal.throwIfAborted();
|
|
73
|
+
const ssrResponse = require_handlerCallback.bindSsrResponseToRequest(router, await waitForRequest(cb({
|
|
25
74
|
request,
|
|
26
75
|
router,
|
|
27
|
-
responseHeaders
|
|
28
|
-
}))
|
|
76
|
+
responseHeaders
|
|
77
|
+
}), request.signal, (late) => {
|
|
78
|
+
require_handlerCallback.disposeSsrResponseDetached(late, request.signal.reason);
|
|
79
|
+
}), request.signal);
|
|
80
|
+
request.signal.throwIfAborted();
|
|
29
81
|
responseOwnsCleanup = ssrResponse.serverSsrCleanup === "stream";
|
|
30
82
|
return ssrResponse.response;
|
|
31
83
|
} finally {
|
|
@@ -35,12 +87,11 @@ function createRequestHandler({ createRouter, request, getRouterManifest }) {
|
|
|
35
87
|
}
|
|
36
88
|
function getRequestHeaders(opts) {
|
|
37
89
|
const matchHeaders = [];
|
|
38
|
-
for (const match of opts.router.stores.matches.get()) matchHeaders.push(match.headers);
|
|
39
|
-
const redirect = opts.router.stores.redirect.get();
|
|
40
|
-
if (redirect) matchHeaders.push(redirect.headers);
|
|
90
|
+
for (const match of require_load_client._getRenderedMatches(opts.router.stores.matches.get())) matchHeaders.push(match.headers);
|
|
41
91
|
return require_headers.mergeHeaders({ "Content-Type": "text/html; charset=UTF-8" }, ...matchHeaders);
|
|
42
92
|
}
|
|
43
93
|
//#endregion
|
|
44
94
|
exports.createRequestHandler = createRequestHandler;
|
|
95
|
+
exports.waitForRequest = waitForRequest;
|
|
45
96
|
|
|
46
97
|
//# sourceMappingURL=createRequestHandler.cjs.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"createRequestHandler.cjs","names":[],"sources":["../../../src/ssr/createRequestHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport { mergeHeaders } from './headers'\nimport {\n attachRouterServerSsrUtils,\n getNormalizedURL,\n getOrigin,\n} from './ssr-server'\nimport {
|
|
1
|
+
{"version":3,"file":"createRequestHandler.cjs","names":[],"sources":["../../../src/ssr/createRequestHandler.ts"],"sourcesContent":["import { createMemoryHistory } from '@tanstack/history'\nimport { _getRenderedMatches } from '../load-client'\nimport { mergeHeaders } from './headers'\nimport {\n attachRouterServerSsrUtils,\n getNormalizedURL,\n getOrigin,\n} from './ssr-server'\nimport {\n bindSsrResponseToRequest,\n disposeSsrResponseDetached,\n} from './handlerCallback'\nimport type { HandlerCallback } from './handlerCallback'\nimport type { AnyHeaders } from './headers'\nimport type { AnyRouter } from '../router'\nimport type { ServerManifest } from '../manifest'\n\nexport type RequestHandler<TRouter extends AnyRouter> = (\n cb: HandlerCallback<TRouter>,\n) => Promise<Response>\n\ntype RequestWaiter = ((reason: unknown) => void) | undefined\n\nconst requestWaiters = new WeakMap<AbortSignal, Array<RequestWaiter>>()\n\nfunction removeRequestWaiter(\n waiters: Array<RequestWaiter>,\n index: number,\n reject: (reason: unknown) => void,\n) {\n if (waiters[index] !== reject) {\n return\n }\n if (index !== waiters.length - 1) {\n waiters[index] = undefined\n return\n }\n\n waiters.pop()\n while (waiters.length && waiters[waiters.length - 1] === undefined) {\n waiters.pop()\n }\n}\n\nexport function waitForRequest<T>(\n value: T | PromiseLike<T>,\n signal: AbortSignal,\n onLate?: (value: T) => void,\n): Promise<T> {\n const promise = Promise.resolve(value)\n if (signal.aborted) {\n void promise.then(onLate, () => {})\n return Promise.reject(signal.reason)\n }\n\n return new Promise<T>((resolve, reject) => {\n let waiters = requestWaiters.get(signal)\n let index: number\n if (waiters) {\n index = waiters.push(reject) - 1\n } else {\n const newWaiters: Array<RequestWaiter> = [reject]\n waiters = newWaiters\n index = 0\n requestWaiters.set(signal, newWaiters)\n signal.addEventListener(\n 'abort',\n () => {\n requestWaiters.delete(signal)\n for (const rejectWaiter of newWaiters) {\n rejectWaiter?.(signal.reason)\n }\n newWaiters.length = 0\n },\n { once: true },\n )\n }\n void promise.then(\n (result) => {\n removeRequestWaiter(waiters, index, reject)\n if (signal.aborted) {\n onLate?.(result)\n } else {\n resolve(result)\n }\n },\n (error) => {\n removeRequestWaiter(waiters, index, reject)\n reject(error)\n },\n )\n })\n}\n\nexport function createRequestHandler<TRouter extends AnyRouter>({\n createRouter,\n request,\n getRouterManifest,\n}: {\n createRouter: () => TRouter\n request: Request\n getRouterManifest?: () => ServerManifest | Promise<ServerManifest>\n}): RequestHandler<TRouter> {\n return async (cb) => {\n request.signal.throwIfAborted()\n const router = createRouter()\n let responseOwnsCleanup = false\n\n try {\n attachRouterServerSsrUtils({\n router,\n manifest: await waitForRequest(getRouterManifest?.(), request.signal),\n })\n\n // normalizing and sanitizing the pathname here for server, so we always deal with the same format during SSR.\n const { url } = getNormalizedURL(request.url, 'http://localhost')\n const origin = getOrigin(request)\n const href = url.href.replace(url.origin, '')\n\n // Create a history for the router\n const history = createMemoryHistory({\n initialEntries: [href],\n })\n\n // Update the router with the history and context\n router.update({\n history,\n origin: router.options.origin ?? origin,\n })\n\n await router.load({\n _signal: request.signal,\n })\n request.signal.throwIfAborted()\n\n const result = router._serverResult\n if (result?.type === 'redirect') {\n return result.redirect\n }\n\n await waitForRequest(router.serverSsr?.dehydrate(), request.signal)\n request.signal.throwIfAborted()\n\n const responseHeaders = getRequestHeaders({\n router,\n })\n\n request.signal.throwIfAborted()\n const response = await waitForRequest(\n cb({\n request,\n router,\n responseHeaders,\n }),\n request.signal,\n (late) => {\n disposeSsrResponseDetached(late, request.signal.reason)\n },\n )\n const ssrResponse = bindSsrResponseToRequest(\n router,\n response,\n request.signal,\n )\n request.signal.throwIfAborted()\n responseOwnsCleanup = ssrResponse.serverSsrCleanup === 'stream'\n return ssrResponse.response\n } finally {\n if (!responseOwnsCleanup) {\n // Clean up router SSR state if the callback won't handle it\n // (e.g., if an error occurred before the callback was invoked).\n // Transformed streaming response bodies clean up when consumed/cancelled.\n router.serverSsr?.cleanup()\n }\n }\n }\n}\n\nfunction getRequestHeaders(opts: { router: AnyRouter }): Headers {\n const matchHeaders: Array<AnyHeaders> = []\n for (const match of _getRenderedMatches(opts.router.stores.matches.get())) {\n matchHeaders.push(match.headers)\n }\n\n return mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=UTF-8',\n },\n ...matchHeaders,\n )\n}\n"],"mappings":"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"}
|
|
@@ -2,6 +2,7 @@ import { HandlerCallback } from './handlerCallback.cjs';
|
|
|
2
2
|
import { AnyRouter } from '../router.cjs';
|
|
3
3
|
import { ServerManifest } from '../manifest.cjs';
|
|
4
4
|
export type RequestHandler<TRouter extends AnyRouter> = (cb: HandlerCallback<TRouter>) => Promise<Response>;
|
|
5
|
+
export declare function waitForRequest<T>(value: T | PromiseLike<T>, signal: AbortSignal, onLate?: (value: T) => void): Promise<T>;
|
|
5
6
|
export declare function createRequestHandler<TRouter extends AnyRouter>({ createRouter, request, getRouterManifest, }: {
|
|
6
7
|
createRouter: () => TRouter;
|
|
7
8
|
request: Request;
|