@tanstack/router-core 1.167.5 → 1.168.1

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 (60) hide show
  1. package/dist/cjs/index.cjs +3 -0
  2. package/dist/cjs/index.d.cts +2 -0
  3. package/dist/cjs/load-matches.cjs +14 -9
  4. package/dist/cjs/load-matches.cjs.map +1 -1
  5. package/dist/cjs/router.cjs +135 -151
  6. package/dist/cjs/router.cjs.map +1 -1
  7. package/dist/cjs/router.d.cts +16 -10
  8. package/dist/cjs/scroll-restoration.cjs +5 -4
  9. package/dist/cjs/scroll-restoration.cjs.map +1 -1
  10. package/dist/cjs/ssr/createRequestHandler.cjs +2 -2
  11. package/dist/cjs/ssr/createRequestHandler.cjs.map +1 -1
  12. package/dist/cjs/ssr/ssr-client.cjs +14 -17
  13. package/dist/cjs/ssr/ssr-client.cjs.map +1 -1
  14. package/dist/cjs/ssr/ssr-server.cjs +1 -1
  15. package/dist/cjs/ssr/ssr-server.cjs.map +1 -1
  16. package/dist/cjs/stores.cjs +148 -0
  17. package/dist/cjs/stores.cjs.map +1 -0
  18. package/dist/cjs/stores.d.cts +70 -0
  19. package/dist/cjs/utils.cjs +7 -0
  20. package/dist/cjs/utils.cjs.map +1 -1
  21. package/dist/cjs/utils.d.cts +1 -0
  22. package/dist/esm/index.d.ts +2 -0
  23. package/dist/esm/index.js +2 -1
  24. package/dist/esm/load-matches.js +14 -9
  25. package/dist/esm/load-matches.js.map +1 -1
  26. package/dist/esm/router.d.ts +16 -10
  27. package/dist/esm/router.js +135 -151
  28. package/dist/esm/router.js.map +1 -1
  29. package/dist/esm/scroll-restoration.js +5 -4
  30. package/dist/esm/scroll-restoration.js.map +1 -1
  31. package/dist/esm/ssr/createRequestHandler.js +2 -2
  32. package/dist/esm/ssr/createRequestHandler.js.map +1 -1
  33. package/dist/esm/ssr/ssr-client.js +14 -17
  34. package/dist/esm/ssr/ssr-client.js.map +1 -1
  35. package/dist/esm/ssr/ssr-server.js +1 -1
  36. package/dist/esm/ssr/ssr-server.js.map +1 -1
  37. package/dist/esm/stores.d.ts +70 -0
  38. package/dist/esm/stores.js +146 -0
  39. package/dist/esm/stores.js.map +1 -0
  40. package/dist/esm/utils.d.ts +1 -0
  41. package/dist/esm/utils.js +7 -1
  42. package/dist/esm/utils.js.map +1 -1
  43. package/package.json +2 -2
  44. package/skills/router-core/search-params/SKILL.md +19 -25
  45. package/src/index.ts +11 -0
  46. package/src/load-matches.ts +23 -11
  47. package/src/router.ts +238 -252
  48. package/src/scroll-restoration.ts +6 -5
  49. package/src/ssr/createRequestHandler.ts +5 -4
  50. package/src/ssr/ssr-client.ts +17 -18
  51. package/src/ssr/ssr-server.ts +1 -1
  52. package/src/stores.ts +342 -0
  53. package/src/utils.ts +9 -0
  54. package/dist/cjs/utils/batch.cjs +0 -16
  55. package/dist/cjs/utils/batch.cjs.map +0 -1
  56. package/dist/cjs/utils/batch.d.cts +0 -1
  57. package/dist/esm/utils/batch.d.ts +0 -1
  58. package/dist/esm/utils/batch.js +0 -15
  59. package/dist/esm/utils/batch.js.map +0 -1
  60. package/src/utils/batch.ts +0 -18
@@ -1,4 +1,3 @@
1
- import { batch as batch$1 } from "./utils/batch.js";
2
1
  import { DEFAULT_PROTOCOL_ALLOWLIST, createControlledPromise, decodePath, deepEqual, encodePathLikeUrl, findLast, functionalUpdate, isDangerousProtocol, last, nullReplaceEqualDeep, replaceEqualDeep } from "./utils.js";
3
2
  import { createLRUCache } from "./lru-cache.js";
4
3
  import { findFlatMatch, findRouteMatch, findSingleMatch, processRouteMasks, processRouteTree } from "./new-process-route-tree.js";
@@ -10,7 +9,7 @@ import { rootRouteId } from "./root.js";
10
9
  import { isRedirect, redirect } from "./redirect.js";
11
10
  import { loadMatches, loadRouteChunk, routeNeedsPreload } from "./load-matches.js";
12
11
  import { composeRewrites, executeRewriteInput, executeRewriteOutput, rewriteBasepath } from "./rewrite.js";
13
- import { createStore } from "@tanstack/store";
12
+ import { createRouterStores } from "./stores.js";
14
13
  import { createBrowserHistory, parseHref } from "@tanstack/history";
15
14
  import { isServer } from "@tanstack/router-core/isServer";
16
15
  //#region src/router.ts
@@ -37,11 +36,11 @@ var trailingSlashOptions = {
37
36
  };
38
37
  /**
39
38
  * Compute whether path, href or hash changed between previous and current
40
- * resolved locations in router state.
39
+ * resolved locations.
41
40
  */
42
- function getLocationChangeInfo(routerState) {
43
- const fromLocation = routerState.resolvedLocation;
44
- const toLocation = routerState.location;
41
+ function getLocationChangeInfo(location, resolvedLocation) {
42
+ const fromLocation = resolvedLocation;
43
+ const toLocation = location;
45
44
  return {
46
45
  fromLocation,
47
46
  toLocation,
@@ -50,24 +49,20 @@ function getLocationChangeInfo(routerState) {
50
49
  hashChanged: fromLocation?.hash !== toLocation.hash
51
50
  };
52
51
  }
53
- function filterRedirectedCachedMatches(matches) {
54
- const filtered = matches.filter((d) => d.status !== "redirected");
55
- return filtered.length === matches.length ? matches : filtered;
56
- }
57
- function createServerStore(initialState) {
58
- const store = {
59
- state: initialState,
60
- setState: (updater) => {
61
- store.state = updater(store.state);
62
- }
63
- };
64
- return store;
65
- }
52
+ /**
53
+ * Core, framework-agnostic router engine that powers TanStack Router.
54
+ *
55
+ * Provides navigation, matching, loading, preloading, caching and event APIs
56
+ * used by framework adapters (React/Solid). Prefer framework helpers like
57
+ * `createRouter` in app code.
58
+ *
59
+ * @link https://tanstack.com/router/latest/docs/framework/react/api/router/RouterType
60
+ */
66
61
  var RouterCore = class {
67
62
  /**
68
63
  * @deprecated Use the `createRouter` function instead
69
64
  */
70
- constructor(options) {
65
+ constructor(options, getStoreConfig) {
71
66
  this.tempLocationKey = `${Math.round(Math.random() * 1e7)}`;
72
67
  this.resetNextScroll = true;
73
68
  this.shouldViewTransition = void 0;
@@ -116,10 +111,11 @@ var RouterCore = class {
116
111
  }
117
112
  this.setRoutes(processRouteTreeResult);
118
113
  }
119
- if (!this.__store && this.latestLocation) if (isServer ?? this.isServer) this.__store = createServerStore(getInitialRouterState(this.latestLocation));
120
- else {
121
- this.__store = createStore(getInitialRouterState(this.latestLocation));
122
- setupScrollRestoration(this);
114
+ if (!this.stores && this.latestLocation) {
115
+ const config = this.getStoreConfig(this);
116
+ this.batch = config.batch;
117
+ this.stores = createRouterStores(getInitialRouterState(this.latestLocation), config);
118
+ if (!(isServer ?? this.isServer)) setupScrollRestoration(this);
123
119
  }
124
120
  let needsLocationUpdate = false;
125
121
  const nextBasepath = this.options.basepath ?? "/";
@@ -134,10 +130,7 @@ var RouterCore = class {
134
130
  if (this.history) this.updateLatestLocation();
135
131
  needsLocationUpdate = true;
136
132
  }
137
- if (needsLocationUpdate && this.__store) this.__store.setState((s) => ({
138
- ...s,
139
- location: this.latestLocation
140
- }));
133
+ if (needsLocationUpdate && this.stores) this.stores.location.setState(() => this.latestLocation);
141
134
  if (typeof window !== "undefined" && "CSS" in window && typeof window.CSS?.supports === "function") this.isViewTransitionTypesSupported = window.CSS.supports("selector(:active-view-transition-type(a)");
142
135
  };
143
136
  this.updateLatestLocation = () => {
@@ -241,14 +234,14 @@ var RouterCore = class {
241
234
  match._nonReactive.pendingTimeout = void 0;
242
235
  };
243
236
  this.cancelMatches = () => {
244
- const currentPendingMatches = this.state.matches.filter((match) => match.status === "pending");
245
- const currentLoadingMatches = this.state.matches.filter((match) => match.isFetching === "loader");
246
- new Set([
247
- ...this.state.pendingMatches ?? [],
248
- ...currentPendingMatches,
249
- ...currentLoadingMatches
250
- ]).forEach((match) => {
251
- this.cancelMatch(match.id);
237
+ this.stores.pendingMatchesId.state.forEach((matchId) => {
238
+ this.cancelMatch(matchId);
239
+ });
240
+ this.stores.matchesId.state.forEach((matchId) => {
241
+ if (this.stores.pendingMatchStoresById.has(matchId)) return;
242
+ const match = this.stores.activeMatchStoresById.get(matchId)?.state;
243
+ if (!match) return;
244
+ if (match.status === "pending" || match.isFetching === "loader") this.cancelMatch(matchId);
252
245
  });
253
246
  };
254
247
  this.buildLocation = (opts) => {
@@ -514,83 +507,85 @@ var RouterCore = class {
514
507
  }
515
508
  }
516
509
  const pendingMatches = this.matchRoutes(this.latestLocation);
517
- this.__store.setState((s) => ({
518
- ...s,
519
- status: "pending",
520
- statusCode: 200,
521
- isLoading: true,
522
- location: this.latestLocation,
523
- pendingMatches,
524
- cachedMatches: s.cachedMatches.filter((d) => !pendingMatches.some((e) => e.id === d.id))
525
- }));
510
+ const nextCachedMatches = this.stores.cachedMatchesSnapshot.state.filter((d) => !pendingMatches.some((e) => e.id === d.id));
511
+ this.batch(() => {
512
+ this.stores.status.setState(() => "pending");
513
+ this.stores.statusCode.setState(() => 200);
514
+ this.stores.isLoading.setState(() => true);
515
+ this.stores.location.setState(() => this.latestLocation);
516
+ this.stores.setPendingMatches(pendingMatches);
517
+ this.stores.setCachedMatches(nextCachedMatches);
518
+ });
526
519
  };
527
520
  this.load = async (opts) => {
528
521
  let redirect;
529
522
  let notFound;
530
523
  let loadPromise;
531
- const previousLocation = this.state.resolvedLocation ?? this.state.location;
524
+ const previousLocation = this.stores.resolvedLocation.state ?? this.stores.location.state;
532
525
  loadPromise = new Promise((resolve) => {
533
526
  this.startTransition(async () => {
534
527
  try {
535
528
  this.beforeLoad();
536
529
  const next = this.latestLocation;
537
- const prevLocation = this.state.resolvedLocation;
538
- if (!this.state.redirect) this.emit({
530
+ const prevLocation = this.stores.resolvedLocation.state;
531
+ const locationChangeInfo = getLocationChangeInfo(next, prevLocation);
532
+ if (!this.stores.redirect.state) this.emit({
539
533
  type: "onBeforeNavigate",
540
- ...getLocationChangeInfo({
541
- resolvedLocation: prevLocation,
542
- location: next
543
- })
534
+ ...locationChangeInfo
544
535
  });
545
536
  this.emit({
546
537
  type: "onBeforeLoad",
547
- ...getLocationChangeInfo({
548
- resolvedLocation: prevLocation,
549
- location: next
550
- })
538
+ ...locationChangeInfo
551
539
  });
552
540
  await loadMatches({
553
541
  router: this,
554
542
  sync: opts?.sync,
555
543
  forceStaleReload: previousLocation.href === next.href,
556
- matches: this.state.pendingMatches,
544
+ matches: this.stores.pendingMatchesSnapshot.state,
557
545
  location: next,
558
546
  updateMatch: this.updateMatch,
559
547
  onReady: async () => {
560
548
  this.startTransition(() => {
561
549
  this.startViewTransition(async () => {
562
- let exitingMatches = [];
563
- let hookExitingMatches = [];
564
- let hookEnteringMatches = [];
565
- let hookStayingMatches = [];
566
- batch$1(() => {
567
- this.__store.setState((s) => {
568
- const previousMatches = s.matches;
569
- const newMatches = s.pendingMatches || s.matches;
570
- exitingMatches = previousMatches.filter((match) => !newMatches.some((d) => d.id === match.id));
571
- hookExitingMatches = previousMatches.filter((match) => !newMatches.some((d) => d.routeId === match.routeId));
572
- hookEnteringMatches = newMatches.filter((match) => !previousMatches.some((d) => d.routeId === match.routeId));
573
- hookStayingMatches = newMatches.filter((match) => previousMatches.some((d) => d.routeId === match.routeId));
574
- return {
575
- ...s,
576
- isLoading: false,
577
- loadedAt: Date.now(),
578
- matches: newMatches,
579
- pendingMatches: void 0,
580
- cachedMatches: [...s.cachedMatches, ...exitingMatches.filter((d) => d.status !== "error" && d.status !== "notFound" && d.status !== "redirected")]
581
- };
582
- });
583
- this.clearExpiredCache();
550
+ let exitingMatches = null;
551
+ let hookExitingMatches = null;
552
+ let hookEnteringMatches = null;
553
+ let hookStayingMatches = null;
554
+ this.batch(() => {
555
+ const pendingMatches = this.stores.pendingMatchesSnapshot.state;
556
+ const mountPending = pendingMatches.length;
557
+ const currentMatches = this.stores.activeMatchesSnapshot.state;
558
+ exitingMatches = mountPending ? currentMatches.filter((match) => !this.stores.pendingMatchStoresById.has(match.id)) : null;
559
+ const pendingRouteIds = /* @__PURE__ */ new Set();
560
+ for (const s of this.stores.pendingMatchStoresById.values()) if (s.routeId) pendingRouteIds.add(s.routeId);
561
+ const activeRouteIds = /* @__PURE__ */ new Set();
562
+ for (const s of this.stores.activeMatchStoresById.values()) if (s.routeId) activeRouteIds.add(s.routeId);
563
+ hookExitingMatches = mountPending ? currentMatches.filter((match) => !pendingRouteIds.has(match.routeId)) : null;
564
+ hookEnteringMatches = mountPending ? pendingMatches.filter((match) => !activeRouteIds.has(match.routeId)) : null;
565
+ hookStayingMatches = mountPending ? pendingMatches.filter((match) => activeRouteIds.has(match.routeId)) : currentMatches;
566
+ this.stores.isLoading.setState(() => false);
567
+ this.stores.loadedAt.setState(() => Date.now());
568
+ /**
569
+ * When committing new matches, cache any exiting matches that are still usable.
570
+ * Routes that resolved with `status: 'error'` or `status: 'notFound'` are
571
+ * deliberately excluded from `cachedMatches` so that subsequent invalidations
572
+ * or reloads re-run their loaders instead of reusing the failed/not-found data.
573
+ */
574
+ if (mountPending) {
575
+ this.stores.setActiveMatches(pendingMatches);
576
+ this.stores.setPendingMatches([]);
577
+ this.stores.setCachedMatches([...this.stores.cachedMatchesSnapshot.state, ...exitingMatches.filter((d) => d.status !== "error" && d.status !== "notFound" && d.status !== "redirected")]);
578
+ this.clearExpiredCache();
579
+ }
584
580
  });
585
- [
581
+ for (const [matches, hook] of [
586
582
  [hookExitingMatches, "onLeave"],
587
583
  [hookEnteringMatches, "onEnter"],
588
584
  [hookStayingMatches, "onStay"]
589
- ].forEach(([matches, hook]) => {
590
- matches.forEach((match) => {
591
- this.looseRoutesById[match.routeId].options[hook]?.(match);
592
- });
593
- });
585
+ ]) {
586
+ if (!matches) continue;
587
+ for (const match of matches) this.looseRoutesById[match.routeId].options[hook]?.(match);
588
+ }
594
589
  });
595
590
  });
596
591
  }
@@ -604,11 +599,11 @@ var RouterCore = class {
604
599
  ignoreBlocker: true
605
600
  });
606
601
  } else if (isNotFound(err)) notFound = err;
607
- this.__store.setState((s) => ({
608
- ...s,
609
- statusCode: redirect ? redirect.status : notFound ? 404 : s.matches.some((d) => d.status === "error") ? 500 : 200,
610
- redirect
611
- }));
602
+ const nextStatusCode = redirect ? redirect.status : notFound ? 404 : this.stores.activeMatchesSnapshot.state.some((d) => d.status === "error") ? 500 : 200;
603
+ this.batch(() => {
604
+ this.stores.statusCode.setState(() => nextStatusCode);
605
+ this.stores.redirect.setState(() => redirect);
606
+ });
612
607
  }
613
608
  if (this.latestLoadPromise === loadPromise) {
614
609
  this.commitLocationPromise?.resolve();
@@ -623,11 +618,8 @@ var RouterCore = class {
623
618
  while (this.latestLoadPromise && loadPromise !== this.latestLoadPromise) await this.latestLoadPromise;
624
619
  let newStatusCode = void 0;
625
620
  if (this.hasNotFoundMatch()) newStatusCode = 404;
626
- else if (this.__store.state.matches.some((d) => d.status === "error")) newStatusCode = 500;
627
- if (newStatusCode !== void 0) this.__store.setState((s) => ({
628
- ...s,
629
- statusCode: newStatusCode
630
- }));
621
+ else if (this.stores.activeMatchesSnapshot.state.some((d) => d.status === "error")) newStatusCode = 500;
622
+ if (newStatusCode !== void 0) this.stores.statusCode.setState(() => newStatusCode);
631
623
  };
632
624
  this.startViewTransition = (fn) => {
633
625
  const shouldViewTransition = this.shouldViewTransition ?? this.options.defaultViewTransition;
@@ -636,11 +628,8 @@ var RouterCore = class {
636
628
  let startViewTransitionParams;
637
629
  if (typeof shouldViewTransition === "object" && this.isViewTransitionTypesSupported) {
638
630
  const next = this.latestLocation;
639
- const prevLocation = this.state.resolvedLocation;
640
- const resolvedViewTransitionTypes = typeof shouldViewTransition.types === "function" ? shouldViewTransition.types(getLocationChangeInfo({
641
- resolvedLocation: prevLocation,
642
- location: next
643
- })) : shouldViewTransition.types;
631
+ const prevLocation = this.stores.resolvedLocation.state;
632
+ const resolvedViewTransitionTypes = typeof shouldViewTransition.types === "function" ? shouldViewTransition.types(getLocationChangeInfo(next, prevLocation)) : shouldViewTransition.types;
644
633
  if (resolvedViewTransitionTypes === false) {
645
634
  fn();
646
635
  return;
@@ -655,20 +644,27 @@ var RouterCore = class {
655
644
  };
656
645
  this.updateMatch = (id, updater) => {
657
646
  this.startTransition(() => {
658
- const matchesKey = this.state.pendingMatches?.some((d) => d.id === id) ? "pendingMatches" : this.state.matches.some((d) => d.id === id) ? "matches" : this.state.cachedMatches.some((d) => d.id === id) ? "cachedMatches" : "";
659
- if (matchesKey) if (matchesKey === "cachedMatches") this.__store.setState((s) => ({
660
- ...s,
661
- cachedMatches: filterRedirectedCachedMatches(s.cachedMatches.map((d) => d.id === id ? updater(d) : d))
662
- }));
663
- else this.__store.setState((s) => ({
664
- ...s,
665
- [matchesKey]: s[matchesKey]?.map((d) => d.id === id ? updater(d) : d)
666
- }));
647
+ const pendingMatch = this.stores.pendingMatchStoresById.get(id);
648
+ if (pendingMatch) {
649
+ pendingMatch.setState(updater);
650
+ return;
651
+ }
652
+ const activeMatch = this.stores.activeMatchStoresById.get(id);
653
+ if (activeMatch) {
654
+ activeMatch.setState(updater);
655
+ return;
656
+ }
657
+ const cachedMatch = this.stores.cachedMatchStoresById.get(id);
658
+ if (cachedMatch) {
659
+ const next = updater(cachedMatch.state);
660
+ if (next.status === "redirected") {
661
+ if (this.stores.cachedMatchStoresById.delete(id)) this.stores.cachedMatchesId.setState((prev) => prev.filter((matchId) => matchId !== id));
662
+ } else cachedMatch.setState(() => next);
663
+ }
667
664
  });
668
665
  };
669
666
  this.getMatch = (matchId) => {
670
- const findFn = (d) => d.id === matchId;
671
- return this.state.cachedMatches.find(findFn) ?? this.state.pendingMatches?.find(findFn) ?? this.state.matches.find(findFn);
667
+ return this.stores.cachedMatchStoresById.get(matchId)?.state ?? this.stores.pendingMatchStoresById.get(matchId)?.state ?? this.stores.activeMatchStoresById.get(matchId)?.state;
672
668
  };
673
669
  this.invalidate = (opts) => {
674
670
  const invalidate = (d) => {
@@ -682,12 +678,11 @@ var RouterCore = class {
682
678
  };
683
679
  return d;
684
680
  };
685
- this.__store.setState((s) => ({
686
- ...s,
687
- matches: s.matches.map(invalidate),
688
- cachedMatches: s.cachedMatches.map(invalidate),
689
- pendingMatches: s.pendingMatches?.map(invalidate)
690
- }));
681
+ this.batch(() => {
682
+ this.stores.setActiveMatches(this.stores.activeMatchesSnapshot.state.map(invalidate));
683
+ this.stores.setCachedMatches(this.stores.cachedMatchesSnapshot.state.map(invalidate));
684
+ this.stores.setPendingMatches(this.stores.pendingMatchesSnapshot.state.map(invalidate));
685
+ });
691
686
  this.shouldViewTransition = false;
692
687
  return this.load({ sync: opts?.sync });
693
688
  };
@@ -715,26 +710,17 @@ var RouterCore = class {
715
710
  };
716
711
  this.clearCache = (opts) => {
717
712
  const filter = opts?.filter;
718
- if (filter !== void 0) this.__store.setState((s) => {
719
- return {
720
- ...s,
721
- cachedMatches: s.cachedMatches.filter((m) => !filter(m))
722
- };
723
- });
724
- else this.__store.setState((s) => {
725
- return {
726
- ...s,
727
- cachedMatches: []
728
- };
729
- });
713
+ if (filter !== void 0) this.stores.setCachedMatches(this.stores.cachedMatchesSnapshot.state.filter((m) => !filter(m)));
714
+ else this.stores.setCachedMatches([]);
730
715
  };
731
716
  this.clearExpiredCache = () => {
717
+ const now = Date.now();
732
718
  const filter = (d) => {
733
719
  const route = this.looseRoutesById[d.routeId];
734
720
  if (!route.options.loader) return true;
735
721
  const gcTime = (d.preload ? route.options.preloadGcTime ?? this.options.defaultPreloadGcTime : route.options.gcTime ?? this.options.defaultGcTime) ?? 300 * 1e3;
736
722
  if (d.status === "error") return true;
737
- return Date.now() - d.updatedAt >= gcTime;
723
+ return now - d.updatedAt >= gcTime;
738
724
  };
739
725
  this.clearCache({ filter });
740
726
  };
@@ -746,16 +732,13 @@ var RouterCore = class {
746
732
  preload: true,
747
733
  dest: opts
748
734
  });
749
- const activeMatchIds = new Set([...this.state.matches, ...this.state.pendingMatches ?? []].map((d) => d.id));
750
- const loadedMatchIds = new Set([...activeMatchIds, ...this.state.cachedMatches.map((d) => d.id)]);
751
- batch$1(() => {
752
- matches.forEach((match) => {
753
- if (!loadedMatchIds.has(match.id)) this.__store.setState((s) => ({
754
- ...s,
755
- cachedMatches: [...s.cachedMatches, match]
756
- }));
757
- });
758
- });
735
+ const activeMatchIds = new Set([...this.stores.matchesId.state, ...this.stores.pendingMatchesId.state]);
736
+ const loadedMatchIds = new Set([...activeMatchIds, ...this.stores.cachedMatchesId.state]);
737
+ const matchesToCache = matches.filter((match) => !loadedMatchIds.has(match.id));
738
+ if (matchesToCache.length) {
739
+ const cachedMatches = this.stores.cachedMatchesSnapshot.state;
740
+ this.stores.setCachedMatches([...cachedMatches, ...matchesToCache]);
741
+ }
759
742
  try {
760
743
  matches = await loadMatches({
761
744
  router: this,
@@ -788,8 +771,8 @@ var RouterCore = class {
788
771
  leaveParams: true
789
772
  };
790
773
  const next = this.buildLocation(matchLocation);
791
- if (opts?.pending && this.state.status !== "pending") return false;
792
- const baseLocation = (opts?.pending === void 0 ? !this.state.isLoading : opts.pending) ? this.latestLocation : this.state.resolvedLocation || this.state.location;
774
+ if (opts?.pending && this.stores.status.state !== "pending") return false;
775
+ const baseLocation = (opts?.pending === void 0 ? !this.stores.isLoading.state : opts.pending) ? this.latestLocation : this.stores.resolvedLocation.state || this.stores.location.state;
793
776
  const match = findSingleMatch(next.pathname, opts?.caseSensitive ?? false, opts?.fuzzy ?? false, baseLocation.pathname, this.processedTree);
794
777
  if (!match) return false;
795
778
  if (location.params) {
@@ -799,8 +782,9 @@ var RouterCore = class {
799
782
  return match.rawParams;
800
783
  };
801
784
  this.hasNotFoundMatch = () => {
802
- return this.__store.state.matches.some((d) => d.status === "notFound" || d.globalNotFound);
785
+ return this.stores.activeMatchesSnapshot.state.some((d) => d.status === "notFound" || d.globalNotFound);
803
786
  };
787
+ this.getStoreConfig = getStoreConfig;
804
788
  this.update({
805
789
  defaultPreloadDelay: 50,
806
790
  defaultPendingMs: 1e3,
@@ -822,7 +806,7 @@ var RouterCore = class {
822
806
  return !!this.options.isPrerendering;
823
807
  }
824
808
  get state() {
825
- return this.__store.state;
809
+ return this.stores.__store.state;
826
810
  }
827
811
  setRoutes({ routesById, routesByPath, processedTree }) {
828
812
  this.routesById = routesById;
@@ -849,7 +833,8 @@ var RouterCore = class {
849
833
  else isGlobalNotFound = true;
850
834
  const globalNotFoundRouteId = isGlobalNotFound ? findGlobalNotFoundRouteId(this.options.notFoundMode, matchedRoutes) : void 0;
851
835
  const matches = new Array(matchedRoutes.length);
852
- const previousMatchesByRouteId = new Map(this.state.matches.map((match) => [match.routeId, match]));
836
+ const previousActiveMatchesByRouteId = /* @__PURE__ */ new Map();
837
+ for (const store of this.stores.activeMatchStoresById.values()) if (store.routeId) previousActiveMatchesByRouteId.set(store.routeId, store.state);
853
838
  for (let index = 0; index < matchedRoutes.length; index++) {
854
839
  const route = matchedRoutes[index];
855
840
  const parentMatch = matches[index - 1];
@@ -889,7 +874,7 @@ var RouterCore = class {
889
874
  });
890
875
  const matchId = route.id + interpolatedPath + loaderDepsHash;
891
876
  const existingMatch = this.getMatch(matchId);
892
- const previousMatch = previousMatchesByRouteId.get(route.id);
877
+ const previousMatch = previousActiveMatchesByRouteId.get(route.id);
893
878
  const strictParams = existingMatch?._strictParams ?? usedParams;
894
879
  let paramsError = void 0;
895
880
  if (!existingMatch) try {
@@ -960,7 +945,7 @@ var RouterCore = class {
960
945
  const match = matches[index];
961
946
  const route = this.looseRoutesById[match.routeId];
962
947
  const existingMatch = this.getMatch(match.id);
963
- const previousMatch = previousMatchesByRouteId.get(match.routeId);
948
+ const previousMatch = previousActiveMatchesByRouteId.get(match.routeId);
964
949
  match.params = previousMatch ? nullReplaceEqualDeep(previousMatch.params, routeParams) : routeParams;
965
950
  if (!existingMatch) {
966
951
  const parentMatch = matches[index - 1];
@@ -1005,8 +990,9 @@ var RouterCore = class {
1005
990
  for (const route of matchedRoutes) try {
1006
991
  Object.assign(accumulatedSearch, validateSearch(route.options.validateSearch, accumulatedSearch));
1007
992
  } catch {}
1008
- const lastStateMatch = last(this.state.matches);
1009
- const canReuseParams = lastStateMatch && lastStateMatch.routeId === lastRoute.id && location.pathname === this.state.location.pathname;
993
+ const lastStateMatchId = last(this.stores.matchesId.state);
994
+ const lastStateMatch = lastStateMatchId && this.stores.activeMatchStoresById.get(lastStateMatchId)?.state;
995
+ const canReuseParams = lastStateMatch && lastStateMatch.routeId === lastRoute.id && lastStateMatch.pathname === location.pathname;
1010
996
  let params;
1011
997
  if (canReuseParams) params = lastStateMatch.params;
1012
998
  else {
@@ -1051,8 +1037,6 @@ function getInitialRouterState(location) {
1051
1037
  resolvedLocation: void 0,
1052
1038
  location,
1053
1039
  matches: [],
1054
- pendingMatches: [],
1055
- cachedMatches: [],
1056
1040
  statusCode: 200
1057
1041
  };
1058
1042
  }