@tanstack/router-core 1.171.15 → 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.
Files changed (134) hide show
  1. package/dist/cjs/Matches.cjs.map +1 -1
  2. package/dist/cjs/Matches.d.cts +3 -12
  3. package/dist/cjs/await-signal.cjs +21 -0
  4. package/dist/cjs/await-signal.cjs.map +1 -0
  5. package/dist/cjs/await-signal.d.cts +1 -0
  6. package/dist/cjs/index.cjs +3 -1
  7. package/dist/cjs/index.d.cts +3 -2
  8. package/dist/cjs/isServer/client.cjs +2 -0
  9. package/dist/cjs/isServer/client.cjs.map +1 -1
  10. package/dist/cjs/isServer/client.d.cts +1 -0
  11. package/dist/cjs/isServer/development.cjs +2 -0
  12. package/dist/cjs/isServer/development.cjs.map +1 -1
  13. package/dist/cjs/isServer/development.d.cts +1 -0
  14. package/dist/cjs/isServer/server.cjs +2 -0
  15. package/dist/cjs/isServer/server.cjs.map +1 -1
  16. package/dist/cjs/isServer/server.d.cts +1 -0
  17. package/dist/cjs/load-client.cjs +1470 -0
  18. package/dist/cjs/load-client.cjs.map +1 -0
  19. package/dist/cjs/load-client.d.cts +147 -0
  20. package/dist/cjs/load-server.cjs +565 -0
  21. package/dist/cjs/load-server.cjs.map +1 -0
  22. package/dist/cjs/load-server.d.cts +16 -0
  23. package/dist/cjs/redirect.cjs.map +1 -1
  24. package/dist/cjs/redirect.d.cts +0 -1
  25. package/dist/cjs/route.cjs.map +1 -1
  26. package/dist/cjs/router.cjs +187 -442
  27. package/dist/cjs/router.cjs.map +1 -1
  28. package/dist/cjs/router.d.cts +61 -51
  29. package/dist/cjs/ssr/client.cjs +2 -2
  30. package/dist/cjs/ssr/createRequestHandler.cjs +60 -9
  31. package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
  32. package/dist/cjs/ssr/createRequestHandler.d.cts +1 -0
  33. package/dist/cjs/ssr/handlerCallback.cjs +49 -4
  34. package/dist/cjs/ssr/handlerCallback.cjs.map +1 -1
  35. package/dist/cjs/ssr/handlerCallback.d.cts +3 -0
  36. package/dist/cjs/ssr/server.cjs +4 -0
  37. package/dist/cjs/ssr/server.d.cts +2 -2
  38. package/dist/cjs/ssr/ssr-client.d.cts +1 -10
  39. package/dist/cjs/ssr/ssr-match-id.cjs +2 -2
  40. package/dist/cjs/ssr/ssr-match-id.cjs.map +1 -1
  41. package/dist/cjs/ssr/ssr-server.cjs +6 -6
  42. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  43. package/dist/cjs/ssr/transformStreamWithRouter.cjs +27 -1
  44. package/dist/cjs/ssr/transformStreamWithRouter.cjs.map +1 -1
  45. package/dist/cjs/ssr/transformStreamWithRouter.d.cts +2 -0
  46. package/dist/cjs/ssr/types.d.cts +0 -1
  47. package/dist/cjs/stores.cjs +30 -104
  48. package/dist/cjs/stores.cjs.map +1 -1
  49. package/dist/cjs/stores.d.cts +7 -32
  50. package/dist/cjs/utils.cjs +0 -4
  51. package/dist/cjs/utils.cjs.map +1 -1
  52. package/dist/esm/Matches.d.ts +3 -12
  53. package/dist/esm/Matches.js.map +1 -1
  54. package/dist/esm/await-signal.d.ts +1 -0
  55. package/dist/esm/await-signal.js +21 -0
  56. package/dist/esm/await-signal.js.map +1 -0
  57. package/dist/esm/index.d.ts +3 -2
  58. package/dist/esm/index.js +3 -2
  59. package/dist/esm/isServer/client.d.ts +1 -0
  60. package/dist/esm/isServer/client.js +2 -1
  61. package/dist/esm/isServer/client.js.map +1 -1
  62. package/dist/esm/isServer/development.d.ts +1 -0
  63. package/dist/esm/isServer/development.js +2 -1
  64. package/dist/esm/isServer/development.js.map +1 -1
  65. package/dist/esm/isServer/server.d.ts +1 -0
  66. package/dist/esm/isServer/server.js +2 -1
  67. package/dist/esm/isServer/server.js.map +1 -1
  68. package/dist/esm/load-client.d.ts +147 -0
  69. package/dist/esm/load-client.js +1462 -0
  70. package/dist/esm/load-client.js.map +1 -0
  71. package/dist/esm/load-server.d.ts +16 -0
  72. package/dist/esm/load-server.js +565 -0
  73. package/dist/esm/load-server.js.map +1 -0
  74. package/dist/esm/redirect.d.ts +0 -1
  75. package/dist/esm/redirect.js.map +1 -1
  76. package/dist/esm/route.js.map +1 -1
  77. package/dist/esm/router.d.ts +61 -51
  78. package/dist/esm/router.js +188 -444
  79. package/dist/esm/router.js.map +1 -1
  80. package/dist/esm/ssr/client.js +1 -1
  81. package/dist/esm/ssr/createRequestHandler.d.ts +1 -0
  82. package/dist/esm/ssr/createRequestHandler.js +61 -11
  83. package/dist/esm/ssr/createRequestHandler.js.map +1 -1
  84. package/dist/esm/ssr/handlerCallback.d.ts +3 -0
  85. package/dist/esm/ssr/handlerCallback.js +47 -5
  86. package/dist/esm/ssr/handlerCallback.js.map +1 -1
  87. package/dist/esm/ssr/server.d.ts +2 -2
  88. package/dist/esm/ssr/server.js +3 -3
  89. package/dist/esm/ssr/ssr-client.d.ts +1 -10
  90. package/dist/esm/ssr/ssr-match-id.js +2 -2
  91. package/dist/esm/ssr/ssr-match-id.js.map +1 -1
  92. package/dist/esm/ssr/ssr-server.js +6 -6
  93. package/dist/esm/ssr/ssr-server.js.map +1 -1
  94. package/dist/esm/ssr/transformStreamWithRouter.d.ts +2 -0
  95. package/dist/esm/ssr/transformStreamWithRouter.js +27 -1
  96. package/dist/esm/ssr/transformStreamWithRouter.js.map +1 -1
  97. package/dist/esm/ssr/types.d.ts +0 -1
  98. package/dist/esm/stores.d.ts +7 -32
  99. package/dist/esm/stores.js +30 -104
  100. package/dist/esm/stores.js.map +1 -1
  101. package/dist/esm/utils.js +1 -4
  102. package/dist/esm/utils.js.map +1 -1
  103. package/package.json +3 -3
  104. package/src/Matches.ts +6 -21
  105. package/src/await-signal.ts +27 -0
  106. package/src/index.ts +1 -3
  107. package/src/isServer/client.ts +1 -0
  108. package/src/isServer/development.ts +1 -0
  109. package/src/isServer/server.ts +1 -0
  110. package/src/load-client.ts +2701 -0
  111. package/src/load-server.ts +849 -0
  112. package/src/redirect.ts +0 -1
  113. package/src/route.ts +2 -11
  114. package/src/router.ts +379 -769
  115. package/src/ssr/createRequestHandler.ts +109 -16
  116. package/src/ssr/handlerCallback.ts +76 -10
  117. package/src/ssr/server.ts +4 -1
  118. package/src/ssr/ssr-client.ts +1 -310
  119. package/src/ssr/ssr-match-id.ts +11 -2
  120. package/src/ssr/ssr-server.ts +9 -8
  121. package/src/ssr/transformStreamWithRouter.ts +37 -1
  122. package/src/ssr/types.ts +0 -1
  123. package/src/stores.ts +46 -208
  124. package/dist/cjs/load-matches.cjs +0 -658
  125. package/dist/cjs/load-matches.cjs.map +0 -1
  126. package/dist/cjs/load-matches.d.cts +0 -18
  127. package/dist/cjs/ssr/ssr-client.cjs +0 -183
  128. package/dist/cjs/ssr/ssr-client.cjs.map +0 -1
  129. package/dist/esm/load-matches.d.ts +0 -18
  130. package/dist/esm/load-matches.js +0 -656
  131. package/dist/esm/load-matches.js.map +0 -1
  132. package/dist/esm/ssr/ssr-client.js +0 -183
  133. package/dist/esm/ssr/ssr-client.js.map +0 -1
  134. package/src/load-matches.ts +0 -1278
@@ -1,12 +1,13 @@
1
- import { loadRouteChunk } from './load-matches.cjs';
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, ControlledPromise, NoInfer, NonNullableUpdater, PickAsRequired, Updater } from './utils.cjs';
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, RouteLike, RouteMask } from './route.cjs';
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 `defaultPreloadGcTime` a route should use if no preloadGcTime is provided.
139
+ * The default `preloadGcTime` a route should use if none is provided.
139
140
  *
140
- * @default 1_800_000 `(30 minutes)`
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 1_800_000 `(30 minutes)`
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) => 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,6 +580,14 @@ export declare function getLocationChangeInfo(location: ParsedLocation, resolved
585
580
  hrefChanged: boolean;
586
581
  hashChanged: boolean;
587
582
  };
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;
588
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>;
589
592
  declare global {
590
593
  var __TSR_CACHE__: {
@@ -593,6 +596,33 @@ declare global {
593
596
  resolvePathCache: LRUCache<string, string>;
594
597
  } | undefined;
595
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
+ ];
596
626
  /**
597
627
  * Core, framework-agnostic router engine that powers TanStack Router.
598
628
  *
@@ -611,10 +641,11 @@ export declare class RouterCore<in out TRouteTree extends AnyRoute, in out TTrai
611
641
  restoration?: boolean;
612
642
  reset?: boolean;
613
643
  };
614
- shouldViewTransition?: boolean | ViewTransitionOptions;
615
- isViewTransitionTypesSupported?: boolean;
616
644
  subscribers: Set<RouterListener<RouterEvent>>;
617
- viewTransitionPromise?: ControlledPromise<true>;
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
- pendingBuiltLocation?: ParsedLocation<FullSearchSchema<TRouteTree>>;
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, ControlledPromise, loaderDeps, and full match objects.
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
- commitLocationPromise: undefined | ControlledPromise<void>;
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) => Promise<void>;
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>) => 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
- * - If `forcePending` is true, or a match is currently in `'error'` or `'notFound'` status,
714
- * its status is reset to `'pending'` and its `error` cleared so that the loader is re-run
715
- * on the next `load()` call (eg. after HMR or a manual invalidation).
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,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
- const require_ssr_client = require("./ssr-client.cjs");
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
- await router.serverSsr?.dehydrate();
24
- const ssrResponse = require_handlerCallback.normalizeSsrResponse(await cb({
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: getRequestHeaders({ router })
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 { normalizeSsrResponse } 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\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 const router = createRouter()\n let responseOwnsCleanup = false\n\n try {\n attachRouterServerSsrUtils({\n router,\n manifest: await getRouterManifest?.(),\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\n await router.serverSsr?.dehydrate()\n\n const responseHeaders = getRequestHeaders({\n router,\n })\n\n const response = await cb({\n request,\n router,\n responseHeaders,\n })\n const ssrResponse = normalizeSsrResponse(response)\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 opts.router.stores.matches.get()) {\n matchHeaders.push(match.headers)\n }\n\n // Handle Redirects\n const redirect = opts.router.stores.redirect.get()\n if (redirect) {\n matchHeaders.push(redirect.headers)\n }\n\n return mergeHeaders(\n {\n 'Content-Type': 'text/html; charset=UTF-8',\n },\n ...matchHeaders,\n )\n}\n"],"mappings":";;;;;AAiBA,SAAgB,qBAAgD,EAC9D,cACA,SACA,qBAK0B;CAC1B,OAAO,OAAO,OAAO;EACnB,MAAM,SAAS,aAAa;EAC5B,IAAI,sBAAsB;EAE1B,IAAI;GACF,mBAAA,2BAA2B;IACzB;IACA,UAAU,MAAM,oBAAoB;GACtC,CAAC;GAGD,MAAM,EAAE,QAAQ,mBAAA,iBAAiB,QAAQ,KAAK,kBAAkB;GAChE,MAAM,SAAS,mBAAA,UAAU,OAAO;GAIhC,MAAM,WAAA,GAAA,kBAAA,qBAA8B,EAClC,gBAAgB,CAJL,IAAI,KAAK,QAAQ,IAAI,QAAQ,EAIvB,CAAI,EACvB,CAAC;GAGD,OAAO,OAAO;IACZ;IACA,QAAQ,OAAO,QAAQ,UAAU;GACnC,CAAC;GAED,MAAM,OAAO,KAAK;GAElB,MAAM,OAAO,WAAW,UAAU;GAWlC,MAAM,cAAc,wBAAA,qBAAqB,MALlB,GAAG;IACxB;IACA;IACA,iBAPsB,kBAAkB,EACxC,OACF,CAKE;GACF,CAAC,CACgD;GACjD,sBAAsB,YAAY,qBAAqB;GACvD,OAAO,YAAY;EACrB,UAAU;GACR,IAAI,CAAC,qBAIH,OAAO,WAAW,QAAQ;EAE9B;CACF;AACF;AAEA,SAAS,kBAAkB,MAAsC;CAC/D,MAAM,eAAkC,CAAC;CACzC,KAAK,MAAM,SAAS,KAAK,OAAO,OAAO,QAAQ,IAAI,GACjD,aAAa,KAAK,MAAM,OAAO;CAIjC,MAAM,WAAW,KAAK,OAAO,OAAO,SAAS,IAAI;CACjD,IAAI,UACF,aAAa,KAAK,SAAS,OAAO;CAGpC,OAAO,gBAAA,aACL,EACE,gBAAgB,2BAClB,GACA,GAAG,YACL;AACF"}
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":";;;;;;AAuBA,MAAM,iCAAiB,IAAI,QAA2C;AAEtE,SAAS,oBACP,SACA,OACA,QACA;CACA,IAAI,QAAQ,WAAW,QACrB;CAEF,IAAI,UAAU,QAAQ,SAAS,GAAG;EAChC,QAAQ,SAAS,KAAA;EACjB;CACF;CAEA,QAAQ,IAAI;CACZ,OAAO,QAAQ,UAAU,QAAQ,QAAQ,SAAS,OAAO,KAAA,GACvD,QAAQ,IAAI;AAEhB;AAEA,SAAgB,eACd,OACA,QACA,QACY;CACZ,MAAM,UAAU,QAAQ,QAAQ,KAAK;CACrC,IAAI,OAAO,SAAS;EAClB,QAAa,KAAK,cAAc,CAAC,CAAC;EAClC,OAAO,QAAQ,OAAO,OAAO,MAAM;CACrC;CAEA,OAAO,IAAI,SAAY,SAAS,WAAW;EACzC,IAAI,UAAU,eAAe,IAAI,MAAM;EACvC,IAAI;EACJ,IAAI,SACF,QAAQ,QAAQ,KAAK,MAAM,IAAI;OAC1B;GACL,MAAM,aAAmC,CAAC,MAAM;GAChD,UAAU;GACV,QAAQ;GACR,eAAe,IAAI,QAAQ,UAAU;GACrC,OAAO,iBACL,eACM;IACJ,eAAe,OAAO,MAAM;IAC5B,KAAK,MAAM,gBAAgB,YACzB,eAAe,OAAO,MAAM;IAE9B,WAAW,SAAS;GACtB,GACA,EAAE,MAAM,KAAK,CACf;EACF;EACA,QAAa,MACV,WAAW;GACV,oBAAoB,SAAS,OAAO,MAAM;GAC1C,IAAI,OAAO,SACT,SAAS,MAAM;QAEf,QAAQ,MAAM;EAElB,IACC,UAAU;GACT,oBAAoB,SAAS,OAAO,MAAM;GAC1C,OAAO,KAAK;EACd,CACF;CACF,CAAC;AACH;AAEA,SAAgB,qBAAgD,EAC9D,cACA,SACA,qBAK0B;CAC1B,OAAO,OAAO,OAAO;EACnB,QAAQ,OAAO,eAAe;EAC9B,MAAM,SAAS,aAAa;EAC5B,IAAI,sBAAsB;EAE1B,IAAI;GACF,mBAAA,2BAA2B;IACzB;IACA,UAAU,MAAM,eAAe,oBAAoB,GAAG,QAAQ,MAAM;GACtE,CAAC;GAGD,MAAM,EAAE,QAAQ,mBAAA,iBAAiB,QAAQ,KAAK,kBAAkB;GAChE,MAAM,SAAS,mBAAA,UAAU,OAAO;GAIhC,MAAM,WAAA,GAAA,kBAAA,qBAA8B,EAClC,gBAAgB,CAJL,IAAI,KAAK,QAAQ,IAAI,QAAQ,EAIvB,CAAI,EACvB,CAAC;GAGD,OAAO,OAAO;IACZ;IACA,QAAQ,OAAO,QAAQ,UAAU;GACnC,CAAC;GAED,MAAM,OAAO,KAAK,EAChB,SAAS,QAAQ,OACnB,CAAC;GACD,QAAQ,OAAO,eAAe;GAE9B,MAAM,SAAS,OAAO;GACtB,IAAI,QAAQ,SAAS,YACnB,OAAO,OAAO;GAGhB,MAAM,eAAe,OAAO,WAAW,UAAU,GAAG,QAAQ,MAAM;GAClE,QAAQ,OAAO,eAAe;GAE9B,MAAM,kBAAkB,kBAAkB,EACxC,OACF,CAAC;GAED,QAAQ,OAAO,eAAe;GAY9B,MAAM,cAAc,wBAAA,yBAClB,QACA,MAbqB,eACrB,GAAG;IACD;IACA;IACA;GACF,CAAC,GACD,QAAQ,SACP,SAAS;IACR,wBAAA,2BAA2B,MAAM,QAAQ,OAAO,MAAM;GACxD,CACF,GAIE,QAAQ,MACV;GACA,QAAQ,OAAO,eAAe;GAC9B,sBAAsB,YAAY,qBAAqB;GACvD,OAAO,YAAY;EACrB,UAAU;GACR,IAAI,CAAC,qBAIH,OAAO,WAAW,QAAQ;EAE9B;CACF;AACF;AAEA,SAAS,kBAAkB,MAAsC;CAC/D,MAAM,eAAkC,CAAC;CACzC,KAAK,MAAM,SAAS,oBAAA,oBAAoB,KAAK,OAAO,OAAO,QAAQ,IAAI,CAAC,GACtE,aAAa,KAAK,MAAM,OAAO;CAGjC,OAAO,gBAAA,aACL,EACE,gBAAgB,2BAClB,GACA,GAAG,YACL;AACF"}
@@ -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;
@@ -8,6 +8,26 @@ function normalizeSsrResponse(result) {
8
8
  serverSsrCleanup: "none"
9
9
  };
10
10
  }
11
+ function disposeSsrResponse(response, reason) {
12
+ if (response.serverSsrCleanup !== "stream") return Promise.resolve();
13
+ try {
14
+ return Promise.resolve(response.dispose(reason));
15
+ } catch (error) {
16
+ return Promise.reject(error);
17
+ }
18
+ }
19
+ function disposeSsrResponseDetached(result, reason, onError = console.error) {
20
+ const ssrResponse = normalizeSsrResponse(result);
21
+ if (ssrResponse.serverSsrCleanup === "stream") {
22
+ disposeSsrResponse(ssrResponse, reason).catch(onError);
23
+ return;
24
+ }
25
+ if (ssrResponse.response.body) try {
26
+ ssrResponse.response.body.cancel(reason).catch(onError);
27
+ } catch (error) {
28
+ onError(error);
29
+ }
30
+ }
11
31
  function createSsrStreamResponse(router, response) {
12
32
  if (!response.body) throw new Error("Invariant failed: SSR stream response requires a body");
13
33
  let disposed = false;
@@ -17,16 +37,38 @@ function createSsrStreamResponse(router, response) {
17
37
  async dispose(reason) {
18
38
  if (disposed) return;
19
39
  disposed = true;
40
+ router.serverSsr?.cleanup();
20
41
  try {
21
42
  await response.body.cancel(reason);
22
43
  } catch {}
23
- router.serverSsr?.cleanup();
24
44
  }
25
45
  };
26
46
  }
27
- async function replaceSsrResponse(result, response, reason) {
47
+ function bindSsrResponseToRequest(router, result, signal) {
28
48
  const ssrResponse = normalizeSsrResponse(result);
29
- if (ssrResponse.serverSsrCleanup === "stream") await ssrResponse.dispose(reason);
49
+ if (ssrResponse.serverSsrCleanup !== "stream") {
50
+ if (signal.aborted) disposeSsrResponseDetached(result, signal.reason);
51
+ return ssrResponse;
52
+ }
53
+ const failed = (error) => {
54
+ router?.serverSsr?.cleanup();
55
+ console.error(error);
56
+ };
57
+ const abort = () => {
58
+ disposeSsrResponseDetached(ssrResponse, signal.reason, failed);
59
+ };
60
+ if (signal.aborted) {
61
+ abort();
62
+ return ssrResponse;
63
+ }
64
+ signal.addEventListener("abort", abort, { once: true });
65
+ router?.serverSsr?.onCleanup(() => {
66
+ signal.removeEventListener("abort", abort);
67
+ });
68
+ return ssrResponse;
69
+ }
70
+ async function replaceSsrResponse(result, response, reason) {
71
+ await disposeSsrResponse(normalizeSsrResponse(result), reason);
30
72
  return {
31
73
  response,
32
74
  serverSsrCleanup: "none"
@@ -34,7 +76,7 @@ async function replaceSsrResponse(result, response, reason) {
34
76
  }
35
77
  async function stripSsrResponseBody(result, reason) {
36
78
  const ssrResponse = normalizeSsrResponse(result);
37
- if (ssrResponse.serverSsrCleanup === "stream") await ssrResponse.dispose(reason);
79
+ await disposeSsrResponse(ssrResponse, reason);
38
80
  return {
39
81
  response: new Response(null, ssrResponse.response),
40
82
  serverSsrCleanup: "none"
@@ -44,8 +86,11 @@ function defineHandlerCallback(handler) {
44
86
  return handler;
45
87
  }
46
88
  //#endregion
89
+ exports.bindSsrResponseToRequest = bindSsrResponseToRequest;
47
90
  exports.createSsrStreamResponse = createSsrStreamResponse;
48
91
  exports.defineHandlerCallback = defineHandlerCallback;
92
+ exports.disposeSsrResponse = disposeSsrResponse;
93
+ exports.disposeSsrResponseDetached = disposeSsrResponseDetached;
49
94
  exports.isSsrResponse = isSsrResponse;
50
95
  exports.normalizeSsrResponse = normalizeSsrResponse;
51
96
  exports.replaceSsrResponse = replaceSsrResponse;
@@ -1 +1 @@
1
- {"version":3,"file":"handlerCallback.cjs","names":[],"sources":["../../../src/ssr/handlerCallback.ts"],"sourcesContent":["import type { AnyRouter } from '../router'\n\nexport type SsrResponse =\n | {\n response: Response\n serverSsrCleanup: 'none'\n }\n | {\n response: Response\n serverSsrCleanup: 'stream'\n dispose: (reason?: unknown) => Promise<void>\n }\n\nexport type HandlerCallbackResult = Response | SsrResponse\n\nexport function isSsrResponse(value: unknown): value is SsrResponse {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'response' in value &&\n 'serverSsrCleanup' in value\n )\n}\n\nexport function normalizeSsrResponse(\n result: HandlerCallbackResult,\n): SsrResponse {\n return isSsrResponse(result)\n ? result\n : { response: result, serverSsrCleanup: 'none' }\n}\n\nexport function createSsrStreamResponse<TRouter extends AnyRouter>(\n router: TRouter,\n response: Response,\n): SsrResponse {\n if (!response.body) {\n throw new Error('Invariant failed: SSR stream response requires a body')\n }\n\n let disposed = false\n return {\n response,\n serverSsrCleanup: 'stream',\n async dispose(reason?: unknown) {\n if (disposed) return\n disposed = true\n\n try {\n await response.body!.cancel(reason)\n } catch {\n // ignore; fallback cleanup below still releases router SSR state\n }\n\n router.serverSsr?.cleanup()\n },\n }\n}\n\nexport async function replaceSsrResponse(\n result: HandlerCallbackResult,\n response: Response,\n reason?: unknown,\n): Promise<SsrResponse> {\n const ssrResponse = normalizeSsrResponse(result)\n if (ssrResponse.serverSsrCleanup === 'stream') {\n await ssrResponse.dispose(reason)\n }\n return { response, serverSsrCleanup: 'none' }\n}\n\nexport async function stripSsrResponseBody(\n result: HandlerCallbackResult,\n reason?: unknown,\n): Promise<SsrResponse> {\n const ssrResponse = normalizeSsrResponse(result)\n if (ssrResponse.serverSsrCleanup === 'stream') {\n await ssrResponse.dispose(reason)\n }\n return {\n response: new Response(null, ssrResponse.response),\n serverSsrCleanup: 'none',\n }\n}\n\nexport interface HandlerCallback<TRouter extends AnyRouter> {\n (ctx: {\n request: Request\n router: TRouter\n responseHeaders: Headers\n }): HandlerCallbackResult | Promise<HandlerCallbackResult>\n}\n\nexport function defineHandlerCallback<TRouter extends AnyRouter>(\n handler: HandlerCallback<TRouter>,\n): HandlerCallback<TRouter> {\n return handler\n}\n"],"mappings":";AAeA,SAAgB,cAAc,OAAsC;CAClE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACd,sBAAsB;AAE1B;AAEA,SAAgB,qBACd,QACa;CACb,OAAO,cAAc,MAAM,IACvB,SACA;EAAE,UAAU;EAAQ,kBAAkB;CAAO;AACnD;AAEA,SAAgB,wBACd,QACA,UACa;CACb,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,MAAM,uDAAuD;CAGzE,IAAI,WAAW;CACf,OAAO;EACL;EACA,kBAAkB;EAClB,MAAM,QAAQ,QAAkB;GAC9B,IAAI,UAAU;GACd,WAAW;GAEX,IAAI;IACF,MAAM,SAAS,KAAM,OAAO,MAAM;GACpC,QAAQ,CAER;GAEA,OAAO,WAAW,QAAQ;EAC5B;CACF;AACF;AAEA,eAAsB,mBACpB,QACA,UACA,QACsB;CACtB,MAAM,cAAc,qBAAqB,MAAM;CAC/C,IAAI,YAAY,qBAAqB,UACnC,MAAM,YAAY,QAAQ,MAAM;CAElC,OAAO;EAAE;EAAU,kBAAkB;CAAO;AAC9C;AAEA,eAAsB,qBACpB,QACA,QACsB;CACtB,MAAM,cAAc,qBAAqB,MAAM;CAC/C,IAAI,YAAY,qBAAqB,UACnC,MAAM,YAAY,QAAQ,MAAM;CAElC,OAAO;EACL,UAAU,IAAI,SAAS,MAAM,YAAY,QAAQ;EACjD,kBAAkB;CACpB;AACF;AAUA,SAAgB,sBACd,SAC0B;CAC1B,OAAO;AACT"}
1
+ {"version":3,"file":"handlerCallback.cjs","names":[],"sources":["../../../src/ssr/handlerCallback.ts"],"sourcesContent":["import type { AnyRouter } from '../router'\n\nexport type SsrResponse =\n | {\n response: Response\n serverSsrCleanup: 'none'\n }\n | {\n response: Response\n serverSsrCleanup: 'stream'\n dispose: (reason?: unknown) => Promise<void>\n }\n\nexport type HandlerCallbackResult = Response | SsrResponse\n\nexport function isSsrResponse(value: unknown): value is SsrResponse {\n return (\n typeof value === 'object' &&\n value !== null &&\n 'response' in value &&\n 'serverSsrCleanup' in value\n )\n}\n\nexport function normalizeSsrResponse(\n result: HandlerCallbackResult,\n): SsrResponse {\n return isSsrResponse(result)\n ? result\n : { response: result, serverSsrCleanup: 'none' }\n}\n\nexport function disposeSsrResponse(\n response: SsrResponse,\n reason?: unknown,\n): Promise<void> {\n if (response.serverSsrCleanup !== 'stream') {\n return Promise.resolve()\n }\n try {\n return Promise.resolve(response.dispose(reason))\n } catch (error) {\n return Promise.reject(error)\n }\n}\n\nexport function disposeSsrResponseDetached(\n result: HandlerCallbackResult,\n reason?: unknown,\n onError: (error: unknown) => void = console.error,\n): void {\n const ssrResponse = normalizeSsrResponse(result)\n if (ssrResponse.serverSsrCleanup === 'stream') {\n void disposeSsrResponse(ssrResponse, reason).catch(onError)\n return\n }\n\n if (ssrResponse.response.body) {\n try {\n void ssrResponse.response.body.cancel(reason).catch(onError)\n } catch (error) {\n onError(error)\n }\n }\n}\n\nexport function createSsrStreamResponse<TRouter extends AnyRouter>(\n router: TRouter,\n response: Response,\n): SsrResponse {\n if (!response.body) {\n throw new Error('Invariant failed: SSR stream response requires a body')\n }\n\n let disposed = false\n return {\n response,\n serverSsrCleanup: 'stream',\n async dispose(reason?: unknown) {\n if (disposed) {\n return\n }\n disposed = true\n\n // Sever router ownership before asking user/renderer stream machinery to\n // cancel. A custom stream is allowed to ignore cancellation forever.\n router.serverSsr?.cleanup()\n\n try {\n await response.body!.cancel(reason)\n } catch {\n // Cleanup above already released router SSR state.\n }\n },\n }\n}\n\nexport function bindSsrResponseToRequest(\n router: AnyRouter | undefined,\n result: HandlerCallbackResult,\n signal: AbortSignal,\n): SsrResponse {\n const ssrResponse = normalizeSsrResponse(result)\n if (ssrResponse.serverSsrCleanup !== 'stream') {\n if (signal.aborted) {\n disposeSsrResponseDetached(result, signal.reason)\n }\n return ssrResponse\n }\n\n const failed = (error: unknown) => {\n router?.serverSsr?.cleanup()\n console.error(error)\n }\n const abort = () => {\n disposeSsrResponseDetached(ssrResponse, signal.reason, failed)\n }\n if (signal.aborted) {\n abort()\n return ssrResponse\n }\n\n signal.addEventListener('abort', abort, { once: true })\n router?.serverSsr?.onCleanup(() => {\n signal.removeEventListener('abort', abort)\n })\n return ssrResponse\n}\n\nexport async function replaceSsrResponse(\n result: HandlerCallbackResult,\n response: Response,\n reason?: unknown,\n): Promise<SsrResponse> {\n const ssrResponse = normalizeSsrResponse(result)\n await disposeSsrResponse(ssrResponse, reason)\n return { response, serverSsrCleanup: 'none' }\n}\n\nexport async function stripSsrResponseBody(\n result: HandlerCallbackResult,\n reason?: unknown,\n): Promise<SsrResponse> {\n const ssrResponse = normalizeSsrResponse(result)\n await disposeSsrResponse(ssrResponse, reason)\n return {\n response: new Response(null, ssrResponse.response),\n serverSsrCleanup: 'none',\n }\n}\n\nexport interface HandlerCallback<TRouter extends AnyRouter> {\n (ctx: {\n request: Request\n router: TRouter\n responseHeaders: Headers\n }): HandlerCallbackResult | Promise<HandlerCallbackResult>\n}\n\nexport function defineHandlerCallback<TRouter extends AnyRouter>(\n handler: HandlerCallback<TRouter>,\n): HandlerCallback<TRouter> {\n return handler\n}\n"],"mappings":";AAeA,SAAgB,cAAc,OAAsC;CAClE,OACE,OAAO,UAAU,YACjB,UAAU,QACV,cAAc,SACd,sBAAsB;AAE1B;AAEA,SAAgB,qBACd,QACa;CACb,OAAO,cAAc,MAAM,IACvB,SACA;EAAE,UAAU;EAAQ,kBAAkB;CAAO;AACnD;AAEA,SAAgB,mBACd,UACA,QACe;CACf,IAAI,SAAS,qBAAqB,UAChC,OAAO,QAAQ,QAAQ;CAEzB,IAAI;EACF,OAAO,QAAQ,QAAQ,SAAS,QAAQ,MAAM,CAAC;CACjD,SAAS,OAAO;EACd,OAAO,QAAQ,OAAO,KAAK;CAC7B;AACF;AAEA,SAAgB,2BACd,QACA,QACA,UAAoC,QAAQ,OACtC;CACN,MAAM,cAAc,qBAAqB,MAAM;CAC/C,IAAI,YAAY,qBAAqB,UAAU;EAC7C,mBAAwB,aAAa,MAAM,EAAE,MAAM,OAAO;EAC1D;CACF;CAEA,IAAI,YAAY,SAAS,MACvB,IAAI;EACF,YAAiB,SAAS,KAAK,OAAO,MAAM,EAAE,MAAM,OAAO;CAC7D,SAAS,OAAO;EACd,QAAQ,KAAK;CACf;AAEJ;AAEA,SAAgB,wBACd,QACA,UACa;CACb,IAAI,CAAC,SAAS,MACZ,MAAM,IAAI,MAAM,uDAAuD;CAGzE,IAAI,WAAW;CACf,OAAO;EACL;EACA,kBAAkB;EAClB,MAAM,QAAQ,QAAkB;GAC9B,IAAI,UACF;GAEF,WAAW;GAIX,OAAO,WAAW,QAAQ;GAE1B,IAAI;IACF,MAAM,SAAS,KAAM,OAAO,MAAM;GACpC,QAAQ,CAER;EACF;CACF;AACF;AAEA,SAAgB,yBACd,QACA,QACA,QACa;CACb,MAAM,cAAc,qBAAqB,MAAM;CAC/C,IAAI,YAAY,qBAAqB,UAAU;EAC7C,IAAI,OAAO,SACT,2BAA2B,QAAQ,OAAO,MAAM;EAElD,OAAO;CACT;CAEA,MAAM,UAAU,UAAmB;EACjC,QAAQ,WAAW,QAAQ;EAC3B,QAAQ,MAAM,KAAK;CACrB;CACA,MAAM,cAAc;EAClB,2BAA2B,aAAa,OAAO,QAAQ,MAAM;CAC/D;CACA,IAAI,OAAO,SAAS;EAClB,MAAM;EACN,OAAO;CACT;CAEA,OAAO,iBAAiB,SAAS,OAAO,EAAE,MAAM,KAAK,CAAC;CACtD,QAAQ,WAAW,gBAAgB;EACjC,OAAO,oBAAoB,SAAS,KAAK;CAC3C,CAAC;CACD,OAAO;AACT;AAEA,eAAsB,mBACpB,QACA,UACA,QACsB;CAEtB,MAAM,mBADc,qBAAqB,MAChB,GAAa,MAAM;CAC5C,OAAO;EAAE;EAAU,kBAAkB;CAAO;AAC9C;AAEA,eAAsB,qBACpB,QACA,QACsB;CACtB,MAAM,cAAc,qBAAqB,MAAM;CAC/C,MAAM,mBAAmB,aAAa,MAAM;CAC5C,OAAO;EACL,UAAU,IAAI,SAAS,MAAM,YAAY,QAAQ;EACjD,kBAAkB;CACpB;AACF;AAUA,SAAgB,sBACd,SAC0B;CAC1B,OAAO;AACT"}
@@ -10,7 +10,10 @@ export type SsrResponse = {
10
10
  export type HandlerCallbackResult = Response | SsrResponse;
11
11
  export declare function isSsrResponse(value: unknown): value is SsrResponse;
12
12
  export declare function normalizeSsrResponse(result: HandlerCallbackResult): SsrResponse;
13
+ export declare function disposeSsrResponse(response: SsrResponse, reason?: unknown): Promise<void>;
14
+ export declare function disposeSsrResponseDetached(result: HandlerCallbackResult, reason?: unknown, onError?: (error: unknown) => void): void;
13
15
  export declare function createSsrStreamResponse<TRouter extends AnyRouter>(router: TRouter, response: Response): SsrResponse;
16
+ export declare function bindSsrResponseToRequest(router: AnyRouter | undefined, result: HandlerCallbackResult, signal: AbortSignal): SsrResponse;
14
17
  export declare function replaceSsrResponse(result: HandlerCallbackResult, response: Response, reason?: unknown): Promise<SsrResponse>;
15
18
  export declare function stripSsrResponseBody(result: HandlerCallbackResult, reason?: unknown): Promise<SsrResponse>;
16
19
  export interface HandlerCallback<TRouter extends AnyRouter> {
@@ -4,9 +4,12 @@ const require_handlerCallback = require("./handlerCallback.cjs");
4
4
  const require_createRequestHandler = require("./createRequestHandler.cjs");
5
5
  const require_transformStreamWithRouter = require("./transformStreamWithRouter.cjs");
6
6
  exports.attachRouterServerSsrUtils = require_ssr_server.attachRouterServerSsrUtils;
7
+ exports.bindSsrResponseToRequest = require_handlerCallback.bindSsrResponseToRequest;
7
8
  exports.createRequestHandler = require_createRequestHandler.createRequestHandler;
8
9
  exports.createSsrStreamResponse = require_handlerCallback.createSsrStreamResponse;
9
10
  exports.defineHandlerCallback = require_handlerCallback.defineHandlerCallback;
11
+ exports.disposeSsrResponse = require_handlerCallback.disposeSsrResponse;
12
+ exports.disposeSsrResponseDetached = require_handlerCallback.disposeSsrResponseDetached;
10
13
  exports.getNormalizedURL = require_ssr_server.getNormalizedURL;
11
14
  exports.getOrigin = require_ssr_server.getOrigin;
12
15
  exports.isSsrResponse = require_handlerCallback.isSsrResponse;
@@ -16,3 +19,4 @@ exports.stripSsrResponseBody = require_handlerCallback.stripSsrResponseBody;
16
19
  exports.transformPipeableStreamWithRouter = require_transformStreamWithRouter.transformPipeableStreamWithRouter;
17
20
  exports.transformReadableStreamWithRouter = require_transformStreamWithRouter.transformReadableStreamWithRouter;
18
21
  exports.transformStreamWithRouter = require_transformStreamWithRouter.transformStreamWithRouter;
22
+ exports.waitForRequest = require_createRequestHandler.waitForRequest;
@@ -1,6 +1,6 @@
1
- export { createRequestHandler } from './createRequestHandler.cjs';
1
+ export { createRequestHandler, waitForRequest } from './createRequestHandler.cjs';
2
2
  export type { RequestHandler } from './createRequestHandler.cjs';
3
- export { createSsrStreamResponse, defineHandlerCallback, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody, } from './handlerCallback.cjs';
3
+ export { bindSsrResponseToRequest, createSsrStreamResponse, defineHandlerCallback, disposeSsrResponse, disposeSsrResponseDetached, isSsrResponse, normalizeSsrResponse, replaceSsrResponse, stripSsrResponseBody, } from './handlerCallback.cjs';
4
4
  export type { HandlerCallback, HandlerCallbackResult, SsrResponse, } from './handlerCallback.cjs';
5
5
  export { transformPipeableStreamWithRouter, transformStreamWithRouter, transformReadableStreamWithRouter, } from './transformStreamWithRouter.cjs';
6
6
  export type { TransformStreamWithRouterOptions } from './transformStreamWithRouter.cjs';
@@ -1,10 +1 @@
1
- import { GLOBAL_SEROVAL, GLOBAL_TSR } from './constants.cjs';
2
- import { TsrSsrGlobal } from './types.cjs';
3
- import { AnyRouter } from '../router.cjs';
4
- declare global {
5
- interface Window {
6
- [GLOBAL_TSR]?: TsrSsrGlobal;
7
- [GLOBAL_SEROVAL]?: any;
8
- }
9
- }
10
- export declare function hydrate(router: AnyRouter): Promise<any>;
1
+ export { hydrate } from '../load-client.cjs';
@@ -1,9 +1,9 @@
1
1
  //#region src/ssr/ssr-match-id.ts
2
2
  function dehydrateSsrMatchId(id) {
3
- return id.replaceAll("/", "\0");
3
+ return id.replaceAll("~", "~~").replaceAll("\0", "~0").replaceAll("�", "~r").replaceAll("/", "\0");
4
4
  }
5
5
  function hydrateSsrMatchId(id) {
6
- return id.replaceAll("\0", "/").replaceAll("�", "/");
6
+ return id.replaceAll("\0", "/").replaceAll("�", "/").replace(/~([~0r])/g, (_, code) => code === "0" ? "\0" : code === "r" ? "�" : code);
7
7
  }
8
8
  //#endregion
9
9
  exports.dehydrateSsrMatchId = dehydrateSsrMatchId;
@@ -1 +1 @@
1
- {"version":3,"file":"ssr-match-id.cjs","names":[],"sources":["../../../src/ssr/ssr-match-id.ts"],"sourcesContent":["export function dehydrateSsrMatchId(id: string): string {\n return id.replaceAll('/', '\\0')\n}\n\nexport function hydrateSsrMatchId(id: string): string {\n return id.replaceAll('\\0', '/').replaceAll('\\uFFFD', '/')\n}\n"],"mappings":";AAAA,SAAgB,oBAAoB,IAAoB;CACtD,OAAO,GAAG,WAAW,KAAK,IAAI;AAChC;AAEA,SAAgB,kBAAkB,IAAoB;CACpD,OAAO,GAAG,WAAW,MAAM,GAAG,EAAE,WAAW,KAAU,GAAG;AAC1D"}
1
+ {"version":3,"file":"ssr-match-id.cjs","names":[],"sources":["../../../src/ssr/ssr-match-id.ts"],"sourcesContent":["export function dehydrateSsrMatchId(id: string): string {\n return id\n .replaceAll('~', '~~')\n .replaceAll('\\0', '~0')\n .replaceAll('\\uFFFD', '~r')\n .replaceAll('/', '\\0')\n}\n\nexport function hydrateSsrMatchId(id: string): string {\n return id\n .replaceAll('\\0', '/')\n .replaceAll('\\uFFFD', '/')\n .replace(/~([~0r])/g, (_, code) =>\n code === '0' ? '\\0' : code === 'r' ? '\\uFFFD' : code,\n )\n}\n"],"mappings":";AAAA,SAAgB,oBAAoB,IAAoB;CACtD,OAAO,GACJ,WAAW,KAAK,IAAI,EACpB,WAAW,MAAM,IAAI,EACrB,WAAW,KAAU,IAAI,EACzB,WAAW,KAAK,IAAI;AACzB;AAEA,SAAgB,kBAAkB,IAAoB;CACpD,OAAO,GACJ,WAAW,MAAM,GAAG,EACpB,WAAW,KAAU,GAAG,EACxB,QAAQ,cAAc,GAAG,SACxB,SAAS,MAAM,OAAO,SAAS,MAAM,MAAW,IAClD;AACJ"}