@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,4 +1,4 @@
1
- import { DEFAULT_PROTOCOL_ALLOWLIST, createControlledPromise, decodePath, deepEqual, encodePathLikeUrl, findLast, functionalUpdate, hasKeys, isDangerousProtocol, last, nullReplaceEqualDeep, replaceEqualDeep } from "./utils.js";
1
+ import { DEFAULT_PROTOCOL_ALLOWLIST, decodePath, deepEqual, encodePathLikeUrl, findLast, functionalUpdate, hasKeys, isDangerousProtocol, last, nullReplaceEqualDeep, replaceEqualDeep } from "./utils.js";
2
2
  import { createLRUCache } from "./lru-cache.js";
3
3
  import { buildRouteBranch, findFlatMatch, findRouteMatch, findSingleMatch, processRouteMasks, processRouteTree } from "./new-process-route-tree.js";
4
4
  import { cleanPath, compileDecodeCharMap, interpolatePath, resolvePath, trimPath, trimPathRight } from "./path.js";
@@ -6,13 +6,16 @@ import { isNotFound } from "./not-found.js";
6
6
  import { setupScrollRestoration } from "./scroll-restoration.js";
7
7
  import { defaultParseSearch, defaultStringifySearch } from "./searchParams.js";
8
8
  import { rootRouteId } from "./root.js";
9
- import { isRedirect, redirect } from "./redirect.js";
10
- import { loadMatches, loadRouteChunk, routeNeedsPreload } from "./load-matches.js";
9
+ import { isRedirect } from "./redirect.js";
10
+ import { loadClientRoute, loadRouteChunk, preloadClientRoute, refreshClientRoute, replaceRouteChunk, transferMatchResources } from "./load-client.js";
11
11
  import { composeRewrites, executeRewriteInput, executeRewriteOutput, rewriteBasepath } from "./rewrite.js";
12
12
  import { createRouterStores } from "./stores.js";
13
13
  import { createBrowserHistory, parseHref } from "@tanstack/history";
14
- import { isServer } from "@tanstack/router-core/isServer";
14
+ import { isServer, loadServerRoute } from "@tanstack/router-core/isServer";
15
15
  //#region src/router.ts
16
+ function routeNeedsLoad(route) {
17
+ return route.options.loader || route.options.beforeLoad || route.lazyFn || route.options.component?.preload || route.options.pendingComponent?.preload;
18
+ }
16
19
  /**
17
20
  * Convert an unknown error into a minimal, serializable object.
18
21
  * Includes name and message (and stack in development).
@@ -39,17 +42,34 @@ const trailingSlashOptions = {
39
42
  * resolved locations.
40
43
  */
41
44
  function getLocationChangeInfo(location, resolvedLocation) {
42
- const fromLocation = resolvedLocation;
43
- const toLocation = location;
44
45
  return {
45
- fromLocation,
46
- toLocation,
47
- pathChanged: fromLocation?.pathname !== toLocation.pathname,
48
- hrefChanged: fromLocation?.href !== toLocation.href,
49
- hashChanged: fromLocation?.hash !== toLocation.hash
46
+ fromLocation: resolvedLocation,
47
+ toLocation: location,
48
+ pathChanged: resolvedLocation?.pathname !== location.pathname,
49
+ hrefChanged: resolvedLocation?.href !== location.href,
50
+ hashChanged: resolvedLocation?.hash !== location.hash
50
51
  };
51
52
  }
52
53
  /**
54
+ * Return only state owned by the application, excluding volatile history
55
+ * bookkeeping. Mask payloads (`__tempLocation`/`__tempKey`) are kept: they
56
+ * distinguish otherwise-identical locations.
57
+ */
58
+ function _getUserHistoryState({ key: _key, __TSR_key: _tsrKey, __TSR_index: _tsrIndex, __hashScrollIntoViewOptions: _hashScroll, ...state }) {
59
+ return state;
60
+ }
61
+ /** Run route lifecycle callbacks in leave/enter/stay phases. */
62
+ function runRouteLifecycle(router, previous, matches, isCurrent) {
63
+ for (const match of previous) {
64
+ if (isCurrent?.() === false) return;
65
+ if (!matches.some((candidate) => candidate.routeId === match.routeId)) router.routesById[match.routeId].options.onLeave?.(match);
66
+ }
67
+ for (const match of matches) {
68
+ if (isCurrent?.() === false) return;
69
+ router.routesById[match.routeId].options[previous.some((candidate) => candidate.routeId === match.routeId) ? "onStay" : "onEnter"]?.(match);
70
+ }
71
+ }
72
+ /**
53
73
  * Core, framework-agnostic router engine that powers TanStack Router.
54
74
  *
55
75
  * Provides navigation, matching, loading, preloading, caching and event APIs
@@ -65,12 +85,15 @@ var RouterCore = class {
65
85
  constructor(options, getStoreConfig) {
66
86
  this.tempLocationKey = `${Math.round(Math.random() * 1e7)}`;
67
87
  this._scroll = { next: true };
68
- this.shouldViewTransition = void 0;
69
- this.isViewTransitionTypesSupported = void 0;
70
88
  this.subscribers = /* @__PURE__ */ new Set();
89
+ this._cache = /* @__PURE__ */ new Map();
90
+ this._committed = [];
71
91
  this.routeBranchCache = /* @__PURE__ */ new WeakMap();
72
92
  this.lightweightCache = /* @__PURE__ */ new WeakMap();
73
- this.startTransition = (fn) => fn();
93
+ this.startTransition = async (fn) => {
94
+ fn();
95
+ return false;
96
+ };
74
97
  this.update = (newOptions) => {
75
98
  if (process.env.NODE_ENV !== "production") {
76
99
  if (newOptions.notFoundRoute) console.warn("The notFoundRoute API is deprecated and will be removed in the next major version. See https://tanstack.com/router/v1/docs/framework/react/guide/not-found-errors#migrating-from-notfoundroute for more info.");
@@ -83,7 +106,7 @@ var RouterCore = class {
83
106
  ...prevOptions,
84
107
  ...newOptions
85
108
  };
86
- this.isServer = this.options.isServer ?? typeof document === "undefined";
109
+ this.isServer = this.options.isServer ?? isServer ?? typeof document === "undefined";
87
110
  this.protocolAllowlist = new Set(this.options.protocolAllowlist);
88
111
  if (this.options.pathParamsAllowedCharacters) this.pathParamsDecoder = compileDecodeCharMap(this.options.pathParamsAllowedCharacters);
89
112
  if (!this.history || this.options.history && this.options.history !== this.history) if (!this.options.history) {
@@ -114,10 +137,9 @@ var RouterCore = class {
114
137
  if (!this.stores && this.latestLocation) {
115
138
  const config = this.getStoreConfig(this);
116
139
  this.batch = config.batch;
117
- this.stores = createRouterStores(getInitialRouterState(this.latestLocation), config);
140
+ this.stores = createRouterStores(this.latestLocation, config);
118
141
  if (!(isServer ?? this.isServer)) setupScrollRestoration(this);
119
142
  }
120
- let needsLocationUpdate = false;
121
143
  const nextBasepath = this.options.basepath ?? "/";
122
144
  const nextRewriteOption = this.options.rewrite;
123
145
  if (basepathWasUnset || prevBasepath !== nextBasepath || prevRewriteOption !== nextRewriteOption) {
@@ -128,10 +150,8 @@ var RouterCore = class {
128
150
  if (nextRewriteOption) rewrites.push(nextRewriteOption);
129
151
  this.rewrite = rewrites.length === 0 ? void 0 : rewrites.length === 1 ? rewrites[0] : composeRewrites(rewrites);
130
152
  if (this.history) this.updateLatestLocation();
131
- needsLocationUpdate = true;
153
+ if (this.stores) this.stores.location.set(this.latestLocation);
132
154
  }
133
- if (needsLocationUpdate && this.stores) this.stores.location.set(this.latestLocation);
134
- if (typeof window !== "undefined" && "CSS" in window && typeof window.CSS?.supports === "function") this.isViewTransitionTypesSupported = window.CSS.supports("selector(:active-view-transition-type(a))");
135
155
  };
136
156
  this.updateLatestLocation = () => {
137
157
  this.latestLocation = this.parseLocation(this.history.location, this.latestLocation);
@@ -154,9 +174,11 @@ var RouterCore = class {
154
174
  };
155
175
  };
156
176
  this.emit = (routerEvent) => {
157
- this.subscribers.forEach((listener) => {
158
- if (listener.eventType === routerEvent.type) listener.fn(routerEvent);
159
- });
177
+ for (const listener of this.subscribers) if (listener.eventType === routerEvent.type) try {
178
+ listener.fn(routerEvent);
179
+ } catch (e) {
180
+ console.error(e);
181
+ }
160
182
  };
161
183
  this.parseLocation = (locationToParse, previousLocation) => {
162
184
  const parse = ({ pathname, search, hash, href, state }) => {
@@ -220,33 +242,18 @@ var RouterCore = class {
220
242
  return this.matchRoutesInternal(pathnameOrNext, locationSearchOrOpts);
221
243
  };
222
244
  this.getMatchedRoutes = (pathname) => {
223
- return getMatchedRoutes({
224
- pathname,
225
- routesById: this.routesById,
226
- processedTree: this.processedTree
227
- });
228
- };
229
- this.cancelMatch = (id) => {
230
- const match = this.getMatch(id);
231
- if (!match) return;
232
- match.abortController.abort();
233
- clearTimeout(match._nonReactive.pendingTimeout);
234
- match._nonReactive.pendingTimeout = void 0;
235
- };
236
- this.cancelMatches = () => {
237
- this.stores.pendingIds.get().forEach((matchId) => {
238
- this.cancelMatch(matchId);
239
- });
240
- this.stores.matchesId.get().forEach((matchId) => {
241
- if (this.stores.pendingMatchStores.has(matchId)) return;
242
- const match = this.stores.matchStores.get(matchId)?.get();
243
- if (!match) return;
244
- if (match.status === "pending" || match.isFetching === "loader") this.cancelMatch(matchId);
245
- });
245
+ const routeParams = Object.create(null);
246
+ const match = findRouteMatch(trimPathRight(pathname), this.processedTree, true);
247
+ if (match) Object.assign(routeParams, match.rawParams);
248
+ return {
249
+ matchedRoutes: match?.branch || [this.routesById["__root__"]],
250
+ routeParams,
251
+ foundRoute: match?.route
252
+ };
246
253
  };
247
254
  this.buildLocation = (opts) => {
248
255
  const build = (dest = {}) => {
249
- const currentLocation = dest._fromLocation || this.pendingBuiltLocation || this.latestLocation;
256
+ const currentLocation = dest._fromLocation || this._pendingLocation || this.latestLocation;
250
257
  const lightweightResult = this.matchRoutesLightweight(currentLocation);
251
258
  if (dest.from && process.env.NODE_ENV !== "production" && dest._isNavigate) {
252
259
  const allFromMatches = this.getMatchedRoutes(dest.from).matchedRoutes;
@@ -264,7 +271,7 @@ var RouterCore = class {
264
271
  const fromParams = Object.assign(Object.create(null), lightweightResult.params);
265
272
  const sourcePath = destTo?.charCodeAt(0) === 47 ? "/" : this.resolvePathWithBase(defaultedFromPath, ".");
266
273
  const nextTo = destTo ? this.resolvePathWithBase(sourcePath, destTo) : sourcePath;
267
- const nextParams = dest.params === false || dest.params === null ? Object.create(null) : (dest.params ?? true) === true ? fromParams : Object.assign(fromParams, functionalUpdate(dest.params, fromParams));
274
+ const nextParams = resolveNextParams(dest.params, fromParams);
268
275
  const destRoute = this.routesByPath[trimPathRight(nextTo)];
269
276
  let destRoutes;
270
277
  if (destRoute) destRoutes = this.getRouteBranch(destRoute);
@@ -303,12 +310,7 @@ var RouterCore = class {
303
310
  });
304
311
  nextSearch = validatedSearch;
305
312
  }
306
- nextSearch = applySearchMiddleware({
307
- search: nextSearch,
308
- dest,
309
- destRoutes,
310
- _includeValidateSearch: opts._includeValidateSearch
311
- });
313
+ nextSearch = applySearchMiddleware(nextSearch, dest, destRoutes, opts._includeValidateSearch);
312
314
  nextSearch = nullReplaceEqualDeep(fromSearch, nextSearch);
313
315
  const searchStr = this.options.stringifySearch(nextSearch);
314
316
  const hash = dest.hash === true ? currentLocation.hash : dest.hash ? functionalUpdate(dest.hash, currentLocation.hash) : void 0;
@@ -353,7 +355,7 @@ var RouterCore = class {
353
355
  if (match) {
354
356
  Object.assign(params, match.rawParams);
355
357
  const { from: _from, params: maskParams, ...maskProps } = match.route;
356
- const nextParams = maskParams === false || maskParams === null ? Object.create(null) : (maskParams ?? true) === true ? params : Object.assign(params, functionalUpdate(maskParams, params));
358
+ const nextParams = resolveNextParams(maskParams, params);
357
359
  maskedDest = {
358
360
  from: opts.from,
359
361
  ...maskProps,
@@ -374,29 +376,18 @@ var RouterCore = class {
374
376
  };
375
377
  this.commitLocation = async ({ viewTransition, ignoreBlocker, ...next }) => {
376
378
  let historyAction;
377
- const isSameState = () => {
378
- const ignoredProps = [
379
- "key",
380
- "__TSR_key",
381
- "__TSR_index",
382
- "__hashScrollIntoViewOptions"
383
- ];
384
- ignoredProps.forEach((prop) => {
385
- next.state[prop] = this.latestLocation.state[prop];
386
- });
387
- const isEqual = deepEqual(next.state, this.latestLocation.state);
388
- ignoredProps.forEach((prop) => {
389
- delete next.state[prop];
390
- });
391
- return isEqual;
392
- };
393
- const isSameUrl = trimPathRight(this.latestLocation.href) === trimPathRight(next.href);
394
- let previousCommitPromise = this.commitLocationPromise;
395
- this.commitLocationPromise = createControlledPromise(() => {
396
- previousCommitPromise?.resolve();
397
- previousCommitPromise = void 0;
379
+ const isSameLocation = trimPathRight(this.latestLocation.href) === trimPathRight(next.href) && deepEqual(_getUserHistoryState(next.state), _getUserHistoryState(this.latestLocation.state));
380
+ const previousCommitPromise = this._commitPromise;
381
+ let resolve;
382
+ const commitPromise = new Promise((done) => {
383
+ resolve = done;
398
384
  });
399
- if (isSameUrl && isSameState()) this.load();
385
+ commitPromise.resolve = () => {
386
+ resolve();
387
+ previousCommitPromise?.resolve();
388
+ };
389
+ this._commitPromise = commitPromise;
390
+ if (isSameLocation) this.load({ _dedupe: true });
400
391
  else {
401
392
  let { maskedLocation, hashScrollIntoView, ...nextHistory } = next;
402
393
  if (maskedLocation) {
@@ -424,12 +415,12 @@ var RouterCore = class {
424
415
  this.shouldViewTransition = viewTransition;
425
416
  historyAction = next.replace ? "REPLACE" : "PUSH";
426
417
  this.history[historyAction === "REPLACE" ? "replace" : "push"](nextHistory.publicHref, nextHistory.state, { ignoreBlocker });
418
+ if (!this.history.subscribers.size) this.load({ action: { type: historyAction } });
427
419
  }
428
420
  this._scroll.next = next.resetScroll ?? true;
429
- if (!this.history.subscribers.size) this.load(historyAction ? { action: { type: historyAction } } : void 0);
430
- return this.commitLocationPromise;
421
+ return this._commitPromise;
431
422
  };
432
- this.buildAndCommitLocation = ({ replace, resetScroll, hashScrollIntoView, viewTransition, ignoreBlocker, href, ...rest } = {}) => {
423
+ this.buildAndCommitLocation = ({ replace, resetScroll, hashScrollIntoView, viewTransition, ignoreBlocker, _redirects, href, ...rest } = {}) => {
433
424
  if (href) {
434
425
  const currentIndex = this.history.location.state.__TSR_index;
435
426
  const parsed = parseHref(href, { __TSR_index: replace ? currentIndex : currentIndex + 1 });
@@ -442,7 +433,8 @@ var RouterCore = class {
442
433
  ...rest,
443
434
  _includeValidateSearch: true
444
435
  });
445
- this.pendingBuiltLocation = location;
436
+ if (_redirects) location._redirects = _redirects;
437
+ this._pendingLocation = location;
446
438
  const commitPromise = this.commitLocation({
447
439
  ...location,
448
440
  viewTransition,
@@ -452,7 +444,7 @@ var RouterCore = class {
452
444
  ignoreBlocker
453
445
  });
454
446
  queueMicrotask(() => {
455
- if (this.pendingBuiltLocation === location) this.pendingBuiltLocation = void 0;
447
+ if (this._pendingLocation === location) this._pendingLocation = void 0;
456
448
  });
457
449
  return commitPromise;
458
450
  };
@@ -498,216 +490,63 @@ var RouterCore = class {
498
490
  _isNavigate: true
499
491
  });
500
492
  };
501
- this.beforeLoad = () => {
502
- this.cancelMatches();
503
- this.updateLatestLocation();
504
- if (isServer ?? this.isServer) {
505
- const nextLocation = this.buildLocation({
506
- to: this.latestLocation.pathname,
507
- search: true,
508
- params: true,
509
- hash: true,
510
- state: true,
511
- _includeValidateSearch: true
512
- });
513
- if (this.latestLocation.publicHref !== nextLocation.publicHref) {
514
- const href = this.getParsedLocationHref(nextLocation);
515
- if (nextLocation.external) throw redirect({ href });
516
- else throw redirect({
517
- href,
518
- _builtLocation: nextLocation
519
- });
520
- }
521
- }
522
- const pendingMatches = this.matchRoutes(this.latestLocation);
523
- const nextCachedMatches = this.stores.cachedMatches.get().filter((d) => !pendingMatches.some((e) => e.id === d.id));
524
- this.batch(() => {
525
- this.stores.status.set("pending");
526
- this.stores.statusCode.set(200);
527
- this.stores.isLoading.set(true);
528
- this.stores.location.set(this.latestLocation);
529
- this.stores.setPending(pendingMatches);
530
- this.stores.setCached(nextCachedMatches);
531
- });
532
- };
533
493
  this.load = async (opts) => {
534
- const historyAction = opts?.action?.type;
535
- let redirect;
536
- let notFound;
537
- let loadPromise;
538
- const previousLocation = this.stores.resolvedLocation.get() ?? this.stores.location.get();
539
- loadPromise = new Promise((resolve) => {
540
- this.startTransition(async () => {
541
- try {
542
- this.beforeLoad();
543
- if (historyAction) this._scroll.hash = historyAction === "PUSH" || historyAction === "REPLACE";
544
- const next = this.latestLocation;
545
- const locationChangeInfo = getLocationChangeInfo(next, this.stores.resolvedLocation.get());
546
- if (!this.stores.redirect.get()) this.emit({
547
- type: "onBeforeNavigate",
548
- ...locationChangeInfo
549
- });
550
- this.emit({
551
- type: "onBeforeLoad",
552
- ...locationChangeInfo
553
- });
554
- await loadMatches({
555
- router: this,
556
- sync: opts?.sync,
557
- forceStaleReload: previousLocation.href === next.href,
558
- matches: this.stores.pendingMatches.get(),
559
- location: next,
560
- updateMatch: this.updateMatch,
561
- onReady: async () => {
562
- this.startTransition(() => {
563
- this.startViewTransition(async () => {
564
- let exitingMatches = null;
565
- let hookExitingMatches = null;
566
- let hookEnteringMatches = null;
567
- let hookStayingMatches = null;
568
- this.batch(() => {
569
- const pendingMatches = this.stores.pendingMatches.get();
570
- const mountPending = pendingMatches.length;
571
- const currentMatches = this.stores.matches.get();
572
- exitingMatches = mountPending ? currentMatches.filter((match) => !this.stores.pendingMatchStores.has(match.id)) : null;
573
- const pendingRouteIds = /* @__PURE__ */ new Set();
574
- for (const s of this.stores.pendingMatchStores.values()) if (s.routeId) pendingRouteIds.add(s.routeId);
575
- const activeRouteIds = /* @__PURE__ */ new Set();
576
- for (const s of this.stores.matchStores.values()) if (s.routeId) activeRouteIds.add(s.routeId);
577
- hookExitingMatches = mountPending ? currentMatches.filter((match) => !pendingRouteIds.has(match.routeId)) : null;
578
- hookEnteringMatches = mountPending ? pendingMatches.filter((match) => !activeRouteIds.has(match.routeId)) : null;
579
- hookStayingMatches = mountPending ? pendingMatches.filter((match) => activeRouteIds.has(match.routeId)) : currentMatches;
580
- this.stores.isLoading.set(false);
581
- this.stores.loadedAt.set(Date.now());
582
- /**
583
- * When committing new matches, cache any exiting matches that are still usable.
584
- * Routes that resolved with `status: 'error'` or `status: 'notFound'` are
585
- * deliberately excluded from `cachedMatches` so that subsequent invalidations
586
- * or reloads re-run their loaders instead of reusing the failed/not-found data.
587
- */
588
- if (mountPending) {
589
- this.stores.setMatches(pendingMatches);
590
- this.stores.setPending([]);
591
- this.stores.setCached([...this.stores.cachedMatches.get(), ...exitingMatches.filter((d) => d.status !== "error" && d.status !== "notFound" && d.status !== "redirected")]);
592
- this.clearExpiredCache();
593
- }
594
- });
595
- for (const [matches, hook] of [
596
- [hookExitingMatches, "onLeave"],
597
- [hookEnteringMatches, "onEnter"],
598
- [hookStayingMatches, "onStay"]
599
- ]) {
600
- if (!matches) continue;
601
- for (const match of matches) this.looseRoutesById[match.routeId].options[hook]?.(match);
602
- }
603
- });
604
- });
605
- }
606
- });
607
- } catch (err) {
608
- if (isRedirect(err)) {
609
- redirect = err;
610
- if (!(isServer ?? this.isServer)) this.navigate({
611
- ...redirect.options,
612
- replace: true,
613
- ignoreBlocker: true
614
- });
615
- } else if (isNotFound(err)) notFound = err;
616
- const nextStatusCode = redirect ? redirect.status : notFound ? 404 : this.stores.matches.get().some((d) => d.status === "error") ? 500 : 200;
617
- this.batch(() => {
618
- this.stores.statusCode.set(nextStatusCode);
619
- this.stores.redirect.set(redirect);
620
- });
621
- }
622
- if (this.latestLoadPromise === loadPromise) {
623
- this.commitLocationPromise?.resolve();
624
- this.latestLoadPromise = void 0;
625
- this.commitLocationPromise = void 0;
626
- }
627
- resolve();
628
- });
629
- });
630
- this.latestLoadPromise = loadPromise;
631
- await loadPromise;
632
- while (this.latestLoadPromise && loadPromise !== this.latestLoadPromise) await this.latestLoadPromise;
633
- let newStatusCode = void 0;
634
- if (this.hasNotFoundMatch()) newStatusCode = 404;
635
- else if (this.stores.matches.get().some((d) => d.status === "error")) newStatusCode = 500;
636
- if (newStatusCode !== void 0) this.stores.statusCode.set(newStatusCode);
494
+ if (isServer ?? this.isServer) return loadServerRoute(this, opts);
495
+ this.updateLatestLocation();
496
+ if (opts?.action) this._scroll.hash = opts.action.type === "PUSH" || opts.action.type === "REPLACE";
497
+ await loadClientRoute(this, opts);
637
498
  };
638
499
  this.startViewTransition = (fn) => {
639
500
  const shouldViewTransition = this.shouldViewTransition ?? this.options.defaultViewTransition;
640
501
  this.shouldViewTransition = void 0;
641
- if (shouldViewTransition && typeof document !== "undefined" && "startViewTransition" in document && typeof document.startViewTransition === "function") {
502
+ if (shouldViewTransition && !(isServer ?? typeof document === "undefined") && typeof document.startViewTransition === "function") {
642
503
  let startViewTransitionParams;
643
- if (typeof shouldViewTransition === "object" && this.isViewTransitionTypesSupported) {
504
+ if (typeof shouldViewTransition === "object" && window.CSS?.supports?.("selector(:active-view-transition-type(a))")) {
644
505
  const next = this.latestLocation;
645
506
  const prevLocation = this.stores.resolvedLocation.get();
646
507
  const resolvedViewTransitionTypes = typeof shouldViewTransition.types === "function" ? shouldViewTransition.types(getLocationChangeInfo(next, prevLocation)) : shouldViewTransition.types;
647
- if (resolvedViewTransitionTypes === false) {
648
- fn();
649
- return;
650
- }
508
+ if (resolvedViewTransitionTypes === false) return fn();
651
509
  startViewTransitionParams = {
652
510
  update: fn,
653
511
  types: resolvedViewTransitionTypes
654
512
  };
655
513
  } else startViewTransitionParams = fn;
656
- document.startViewTransition(startViewTransitionParams);
657
- } else fn();
658
- };
659
- this.updateMatch = (id, updater) => {
660
- this.startTransition(() => {
661
- const pendingMatch = this.stores.pendingMatchStores.get(id);
662
- if (pendingMatch) {
663
- pendingMatch.set(updater);
664
- return;
665
- }
666
- const activeMatch = this.stores.matchStores.get(id);
667
- if (activeMatch) {
668
- activeMatch.set(updater);
669
- return;
670
- }
671
- const cachedMatch = this.stores.cachedMatchStores.get(id);
672
- if (cachedMatch) {
673
- const next = updater(cachedMatch.get());
674
- if (next.status === "redirected") {
675
- if (this.stores.cachedMatchStores.delete(id)) this.stores.cachedIds.set((prev) => prev.filter((matchId) => matchId !== id));
676
- } else cachedMatch.set(next);
677
- }
678
- });
679
- };
680
- this.getMatch = (matchId) => {
681
- return this.stores.cachedMatchStores.get(matchId)?.get() ?? this.stores.pendingMatchStores.get(matchId)?.get() ?? this.stores.matchStores.get(matchId)?.get();
514
+ return document.startViewTransition(startViewTransitionParams).updateCallbackDone;
515
+ }
516
+ return fn();
682
517
  };
683
518
  this.invalidate = (opts) => {
519
+ const committedMatches = this._committed;
520
+ const filter = opts?.filter;
521
+ const invalidIds = filter ? new Set([...committedMatches, ...this._cache.values()].filter((match) => filter(match)).map((match) => match.id)) : void 0;
684
522
  const invalidate = (d) => {
685
- if (opts?.filter?.(d) ?? true) return {
686
- ...d,
687
- invalid: true,
688
- ...opts?.forcePending || d.status === "error" || d.status === "notFound" ? {
689
- status: "pending",
690
- error: void 0
691
- } : void 0
692
- };
523
+ if (!invalidIds || invalidIds.has(d.id)) {
524
+ const route = this.routesById[d.routeId];
525
+ const next = {
526
+ ...d,
527
+ invalid: true,
528
+ ...(opts?.forcePending || d.status === "error" || d.status === "notFound") && routeNeedsLoad(route) ? {
529
+ status: "pending",
530
+ error: void 0
531
+ } : void 0
532
+ };
533
+ d._flight = void 0;
534
+ return next;
535
+ }
693
536
  return d;
694
537
  };
695
- this.batch(() => {
696
- this.stores.setMatches(this.stores.matches.get().map(invalidate));
697
- this.stores.setCached(this.stores.cachedMatches.get().map(invalidate));
698
- this.stores.setPending(this.stores.pendingMatches.get().map(invalidate));
699
- });
538
+ const committed = committedMatches.map(invalidate);
539
+ this._committed = committed;
540
+ const cache = /* @__PURE__ */ new Map();
541
+ for (const [id, match] of this._cache) cache.set(id, invalidate(match));
542
+ this._cache = cache;
700
543
  this.shouldViewTransition = false;
701
544
  return this.load({ sync: opts?.sync });
702
545
  };
703
- this.getParsedLocationHref = (location) => {
704
- return location.publicHref || "/";
705
- };
706
546
  this.resolveRedirect = (redirect) => {
707
547
  const locationHeader = redirect.headers.get("Location");
708
548
  if (!redirect.options.href || redirect.options._builtLocation) {
709
- const location = redirect.options._builtLocation ?? this.buildLocation(redirect.options);
710
- const href = this.getParsedLocationHref(location);
549
+ const href = (redirect.options._builtLocation ?? this.buildLocation(redirect.options)).publicHref || "/";
711
550
  redirect.options.href = href;
712
551
  redirect.headers.set("Location", href);
713
552
  } else if (locationHeader) try {
@@ -723,60 +562,26 @@ var RouterCore = class {
723
562
  return redirect;
724
563
  };
725
564
  this.clearCache = (opts) => {
565
+ const cached = this._cache;
566
+ const preloads = this._preloads;
726
567
  const filter = opts?.filter;
727
- if (filter !== void 0) this.stores.setCached(this.stores.cachedMatches.get().filter((m) => !filter(m)));
728
- else this.stores.setCached([]);
729
- };
730
- this.clearExpiredCache = () => {
731
- const now = Date.now();
732
- const filter = (d) => {
733
- const route = this.looseRoutesById[d.routeId];
734
- if (!route.options.loader) return true;
735
- const gcTime = (d.preload ? route.options.preloadGcTime ?? this.options.defaultPreloadGcTime : route.options.gcTime ?? this.options.defaultGcTime) ?? 300 * 1e3;
736
- if (d.status === "error") return true;
737
- return now - d.updatedAt >= gcTime;
738
- };
739
- this.clearCache({ filter });
568
+ const retained = /* @__PURE__ */ new Map();
569
+ const discarded = [];
570
+ for (const [id, match] of cached) if (filter && !filter(match)) retained.set(id, match);
571
+ else discarded.push(match);
572
+ const retainedPreloads = /* @__PURE__ */ new Map();
573
+ const discardedPreloads = [];
574
+ for (const [href, preload] of preloads ?? []) if (!filter || preload[0].some(filter)) {
575
+ discardedPreloads.push(preload);
576
+ discarded.push(...preload[0]);
577
+ } else retainedPreloads.set(href, preload);
578
+ this._cache = retained;
579
+ this._preloads = retainedPreloads;
580
+ transferMatchResources(this, discarded);
581
+ for (const preload of discardedPreloads) preload[1].abort();
740
582
  };
741
583
  this.loadRouteChunk = loadRouteChunk;
742
- this.preloadRoute = async (opts) => {
743
- const next = opts._builtLocation ?? this.buildLocation(opts);
744
- let matches = this.matchRoutes(next, {
745
- throwOnError: true,
746
- preload: true,
747
- dest: opts
748
- });
749
- const activeMatchIds = new Set([...this.stores.matchesId.get(), ...this.stores.pendingIds.get()]);
750
- const loadedMatchIds = new Set([...activeMatchIds, ...this.stores.cachedIds.get()]);
751
- const matchesToCache = matches.filter((match) => !loadedMatchIds.has(match.id));
752
- if (matchesToCache.length) {
753
- const cachedMatches = this.stores.cachedMatches.get();
754
- this.stores.setCached([...cachedMatches, ...matchesToCache]);
755
- }
756
- try {
757
- matches = await loadMatches({
758
- router: this,
759
- matches,
760
- location: next,
761
- preload: true,
762
- updateMatch: (id, updater) => {
763
- if (activeMatchIds.has(id)) matches = matches.map((d) => d.id === id ? updater(d) : d);
764
- else this.updateMatch(id, updater);
765
- }
766
- });
767
- return matches;
768
- } catch (err) {
769
- if (isRedirect(err)) {
770
- if (err.options.reloadDocument) return;
771
- return await this.preloadRoute({
772
- ...err.options,
773
- _fromLocation: next
774
- });
775
- }
776
- if (!isNotFound(err)) console.error(err);
777
- return;
778
- }
779
- };
584
+ this.preloadRoute = (opts) => preloadClientRoute(this, opts);
780
585
  this.matchRoute = (location, opts) => {
781
586
  const matchLocation = {
782
587
  ...location,
@@ -785,8 +590,9 @@ var RouterCore = class {
785
590
  leaveParams: true
786
591
  };
787
592
  const next = this.buildLocation(matchLocation);
788
- if (opts?.pending && this.stores.status.get() !== "pending") return false;
789
- const baseLocation = (opts?.pending === void 0 ? !this.stores.isLoading.get() : opts.pending) ? this.latestLocation : this.stores.resolvedLocation.get() || this.stores.location.get();
593
+ const isPending = this.stores.status.get() === "pending";
594
+ if (opts?.pending && !isPending) return false;
595
+ const baseLocation = opts?.pending ?? !isPending ? this.latestLocation : this.stores.resolvedLocation.get() || this.stores.location.get();
790
596
  const match = findSingleMatch(next.pathname, opts?.caseSensitive ?? false, opts?.fuzzy ?? false, baseLocation.pathname, this.processedTree);
791
597
  if (!match) return false;
792
598
  if (location.params) {
@@ -795,9 +601,6 @@ var RouterCore = class {
795
601
  if (opts?.includeSearch ?? true) return deepEqual(baseLocation.search, next.search, { partial: true }) ? match.rawParams : false;
796
602
  return match.rawParams;
797
603
  };
798
- this.hasNotFoundMatch = () => {
799
- return this.stores.matches.get().some((d) => d.status === "notFound" || d.globalNotFound);
800
- };
801
604
  this.getStoreConfig = getStoreConfig;
802
605
  this.update({
803
606
  defaultPreloadDelay: 50,
@@ -811,14 +614,11 @@ var RouterCore = class {
811
614
  parseSearch: options.parseSearch ?? defaultParseSearch,
812
615
  protocolAllowlist: options.protocolAllowlist ?? DEFAULT_PROTOCOL_ALLOWLIST
813
616
  });
814
- if (typeof document !== "undefined") self.__TSR_ROUTER__ = this;
617
+ if (!(isServer ?? typeof document === "undefined")) self.__TSR_ROUTER__ = this;
815
618
  }
816
619
  isShell() {
817
620
  return !!this.options.isShell;
818
621
  }
819
- isPrerendering() {
820
- return !!this.options.isPrerendering;
821
- }
822
622
  get state() {
823
623
  return this.stores.__store.get();
824
624
  }
@@ -840,12 +640,6 @@ var RouterCore = class {
840
640
  }
841
641
  return branch;
842
642
  }
843
- get looseRoutesById() {
844
- return this.routesById;
845
- }
846
- getParentContext(parentMatch) {
847
- return !parentMatch?.id ? this.options.context ?? void 0 : parentMatch.context ?? this.options.context ?? void 0;
848
- }
849
643
  matchRoutesInternal(next, opts) {
850
644
  const matchedRoutesResult = this.getMatchedRoutes(next.pathname);
851
645
  const { foundRoute, routeParams } = matchedRoutesResult;
@@ -853,10 +647,13 @@ var RouterCore = class {
853
647
  let isGlobalNotFound = false;
854
648
  if (foundRoute ? foundRoute.path !== "/" && routeParams["**"] : trimPathRight(next.pathname)) if (this.options.notFoundRoute) matchedRoutes = [...matchedRoutes, this.options.notFoundRoute];
855
649
  else isGlobalNotFound = true;
856
- const globalNotFoundRouteId = isGlobalNotFound ? findGlobalNotFoundRouteId(this.options.notFoundMode, matchedRoutes) : void 0;
650
+ const _notFoundRouteId = isGlobalNotFound ? findGlobalNotFoundRouteId(this.options.notFoundMode, matchedRoutes) : void 0;
857
651
  const matches = new Array(matchedRoutes.length);
858
- const previousActiveMatchesByRouteId = /* @__PURE__ */ new Map();
859
- for (const store of this.stores.matchStores.values()) if (store.routeId) previousActiveMatchesByRouteId.set(store.routeId, store.get());
652
+ const committed = this._committed;
653
+ const previousAt = (route, index) => {
654
+ const match = committed[index];
655
+ return match?.routeId === route.id ? match : route === this.options.notFoundRoute ? committed.find((candidate) => candidate.routeId === route.id) : void 0;
656
+ };
860
657
  for (let index = 0; index < matchedRoutes.length; index++) {
861
658
  const route = matchedRoutes[index];
862
659
  const parentMatch = matches[index - 1];
@@ -876,7 +673,6 @@ var RouterCore = class {
876
673
  ...parentStrictSearch,
877
674
  ...strictSearch
878
675
  };
879
- searchError = void 0;
880
676
  } catch (err) {
881
677
  let searchParamError = err;
882
678
  if (!(err instanceof SearchParamError)) searchParamError = new SearchParamError(err.message, { cause: err });
@@ -886,8 +682,15 @@ var RouterCore = class {
886
682
  searchError = searchParamError;
887
683
  }
888
684
  }
889
- const loaderDeps = route.options.loaderDeps?.({ search: preMatchSearch }) ?? "";
890
- const loaderDepsHash = loaderDeps ? JSON.stringify(loaderDeps) : "";
685
+ let loaderDeps = "";
686
+ let loaderDepsHash = "";
687
+ try {
688
+ loaderDeps = route.options.loaderDeps?.({ search: preMatchSearch }) ?? "";
689
+ loaderDepsHash = loaderDeps ? JSON.stringify(loaderDeps) || "" : "";
690
+ } catch (cause) {
691
+ if (opts?.throwOnError) throw cause;
692
+ searchError ??= cause;
693
+ }
891
694
  const { interpolatedPath, usedParams } = interpolatePath({
892
695
  path: route.fullPath,
893
696
  params: routeParams,
@@ -895,10 +698,10 @@ var RouterCore = class {
895
698
  server: this.isServer
896
699
  });
897
700
  const matchId = route.id + interpolatedPath + loaderDepsHash;
898
- const existingMatch = this.getMatch(matchId);
899
- const previousMatch = previousActiveMatchesByRouteId.get(route.id);
701
+ const previousMatch = previousAt(route, index);
702
+ const existingMatch = process.env.NODE_ENV !== "production" && opts?._rematerialize ? void 0 : this._cache.get(matchId) ?? (previousMatch?.id === matchId ? previousMatch : void 0);
900
703
  const strictParams = existingMatch?._strictParams ?? usedParams;
901
- let paramsError = void 0;
704
+ let paramsError;
902
705
  if (!existingMatch) try {
903
706
  extractStrictParams(route, strictParams);
904
707
  } catch (err) {
@@ -915,10 +718,11 @@ var RouterCore = class {
915
718
  params: previousMatch?.params ?? routeParams,
916
719
  _strictParams: strictParams,
917
720
  search: previousMatch ? nullReplaceEqualDeep(previousMatch.search, preMatchSearch) : nullReplaceEqualDeep(existingMatch.search, preMatchSearch),
918
- _strictSearch: strictMatchSearch
721
+ _strictSearch: strictMatchSearch,
722
+ searchError
919
723
  };
920
724
  else {
921
- const status = route.options.loader || route.options.beforeLoad || route.lazyFn || routeNeedsPreload(route) ? "pending" : "success";
725
+ const status = routeNeedsLoad(route) ? "pending" : "success";
922
726
  match = {
923
727
  id: matchId,
924
728
  ssr: isServer ?? this.isServer ? void 0 : route.options.ssr,
@@ -930,83 +734,42 @@ var RouterCore = class {
930
734
  updatedAt: Date.now(),
931
735
  search: previousMatch ? nullReplaceEqualDeep(previousMatch.search, preMatchSearch) : preMatchSearch,
932
736
  _strictSearch: strictMatchSearch,
933
- searchError: void 0,
737
+ searchError,
934
738
  status,
935
739
  isFetching: false,
936
740
  error: void 0,
937
741
  paramsError,
938
- __routeContext: void 0,
939
- _nonReactive: { loadPromise: createControlledPromise() },
940
- __beforeLoadContext: void 0,
941
742
  context: {},
942
- abortController: new AbortController(),
943
- fetchCount: 0,
743
+ abortController: opts?._controller ?? new AbortController(),
944
744
  cause,
945
745
  loaderDeps: previousMatch ? replaceEqualDeep(previousMatch.loaderDeps, loaderDeps) : loaderDeps,
946
746
  invalid: false,
947
747
  preload: false,
948
- links: void 0,
949
- scripts: void 0,
950
- headScripts: void 0,
951
- meta: void 0,
952
748
  staticData: route.options.staticData || {},
953
749
  fullPath: route.fullPath
954
750
  };
955
751
  }
956
- if (!opts?.preload) match.globalNotFound = globalNotFoundRouteId === route.id;
957
- match.searchError = searchError;
958
- const parentContext = this.getParentContext(parentMatch);
959
- match.context = {
960
- ...parentContext,
961
- ...match.__routeContext,
962
- ...match.__beforeLoadContext
963
- };
752
+ const _notFound = _notFoundRouteId === route.id;
753
+ if (match._notFound && !_notFound) match.error = void 0;
754
+ match._notFound = _notFound;
964
755
  matches[index] = match;
965
756
  }
966
757
  for (let index = 0; index < matches.length; index++) {
967
758
  const match = matches[index];
968
- const route = this.looseRoutesById[match.routeId];
969
- const existingMatch = this.getMatch(match.id);
970
- const previousMatch = previousActiveMatchesByRouteId.get(match.routeId);
971
- match.params = previousMatch ? nullReplaceEqualDeep(previousMatch.params, routeParams) : routeParams;
972
- if (!existingMatch) {
973
- const parentMatch = matches[index - 1];
974
- const parentContext = this.getParentContext(parentMatch);
975
- if (route.options.context) {
976
- const contextFnContext = {
977
- deps: match.loaderDeps,
978
- params: match.params,
979
- context: parentContext ?? {},
980
- location: next,
981
- navigate: (opts) => this.navigate({
982
- ...opts,
983
- _fromLocation: next
984
- }),
985
- buildLocation: this.buildLocation,
986
- cause: match.cause,
987
- abortController: match.abortController,
988
- preload: !!match.preload,
989
- matches,
990
- routeId: route.id
991
- };
992
- match.__routeContext = route.options.context(contextFnContext) ?? void 0;
993
- }
994
- match.context = {
995
- ...parentContext,
996
- ...match.__routeContext,
997
- ...match.__beforeLoadContext
998
- };
999
- }
759
+ match.params = match.cause === "stay" ? nullReplaceEqualDeep(match.params, routeParams) : routeParams;
760
+ if (opts?._controller) match.context = {};
1000
761
  }
1001
762
  return matches;
1002
763
  }
1003
764
  /**
1004
765
  * Lightweight route matching for buildLocation.
1005
766
  * Only computes fullPath, accumulated search, and params - skipping expensive
1006
- * operations like AbortController, ControlledPromise, loaderDeps, and full match objects.
767
+ * operations like AbortController, loaderDeps, and full match objects.
1007
768
  */
1008
769
  matchRoutesLightweight(location) {
1009
- const lastStateMatchId = last(this.stores.matchesId.get());
770
+ const lastRouteId = last(this.stores.ids.get());
771
+ const lastStateMatch = lastRouteId ? this.stores.byRoute.get(lastRouteId).get() : void 0;
772
+ const lastStateMatchId = lastStateMatch?.id;
1010
773
  const cached = this.lightweightCache.get(location);
1011
774
  if (cached && cached[0] === lastStateMatchId) return cached[1];
1012
775
  const { matchedRoutes, routeParams } = this.getMatchedRoutes(location.pathname);
@@ -1015,7 +778,6 @@ var RouterCore = class {
1015
778
  for (const route of matchedRoutes) try {
1016
779
  Object.assign(accumulatedSearch, validateSearch(route.options.validateSearch, accumulatedSearch));
1017
780
  } catch {}
1018
- const lastStateMatch = lastStateMatchId && this.stores.matchStores.get(lastStateMatchId)?.get();
1019
781
  const canReuseParams = lastStateMatch && lastStateMatch.routeId === lastRoute.id && lastStateMatch.pathname === location.pathname;
1020
782
  let params;
1021
783
  if (canReuseParams) params = lastStateMatch.params;
@@ -1036,6 +798,19 @@ var RouterCore = class {
1036
798
  return result;
1037
799
  }
1038
800
  };
801
+ /**
802
+ * In non-production environments,
803
+ * augment the RouterCore class with a `_refreshRoute` method
804
+ * dedicated to HMR.
805
+ */
806
+ if (process.env.NODE_ENV !== "production") {
807
+ RouterCore.prototype._replaceRouteChunk = replaceRouteChunk;
808
+ RouterCore.prototype._refreshRoute = async function() {
809
+ this._serverResult = void 0;
810
+ this.updateLatestLocation();
811
+ await refreshClientRoute(this);
812
+ };
813
+ }
1039
814
  /** Error thrown when search parameter validation fails. */
1040
815
  var SearchParamError = class extends Error {};
1041
816
  /** Error thrown when path parameter parsing/validation fails. */
@@ -1056,14 +831,11 @@ function lazyFn(fn, key) {
1056
831
  /** Create an initial RouterState from a parsed location. */
1057
832
  function getInitialRouterState(location) {
1058
833
  return {
1059
- loadedAt: 0,
1060
834
  isLoading: false,
1061
- isTransitioning: false,
1062
835
  status: "idle",
1063
836
  resolvedLocation: void 0,
1064
837
  location,
1065
- matches: [],
1066
- statusCode: 200
838
+ matches: []
1067
839
  };
1068
840
  }
1069
841
  function validateSearch(validateSearch, input) {
@@ -1078,35 +850,7 @@ function validateSearch(validateSearch, input) {
1078
850
  if (typeof validateSearch === "function") return validateSearch(input);
1079
851
  return {};
1080
852
  }
1081
- /**
1082
- * Build the matched route chain and extract params for a pathname.
1083
- * Falls back to the root route if no specific route is found.
1084
- */
1085
- function getMatchedRoutes({ pathname, routesById, processedTree }) {
1086
- const routeParams = Object.create(null);
1087
- const trimmedPath = trimPathRight(pathname);
1088
- let foundRoute = void 0;
1089
- const match = findRouteMatch(trimmedPath, processedTree, true);
1090
- if (match) {
1091
- foundRoute = match.route;
1092
- Object.assign(routeParams, match.rawParams);
1093
- }
1094
- return {
1095
- matchedRoutes: match?.branch || [routesById["__root__"]],
1096
- routeParams,
1097
- foundRoute
1098
- };
1099
- }
1100
- /**
1101
- * TODO: once caches are persisted across requests on the server,
1102
- * we can cache the built middleware chain using `last(destRoutes)` as the key
1103
- */
1104
- function applySearchMiddleware({ search, dest, destRoutes, _includeValidateSearch }) {
1105
- return buildMiddlewareChain(destRoutes)(search, dest, _includeValidateSearch ?? false);
1106
- }
1107
- function buildMiddlewareChain(destRoutes) {
1108
- let dest;
1109
- let includeValidateSearch;
853
+ function applySearchMiddleware(search, dest, destRoutes, includeValidateSearch) {
1110
854
  const middlewares = [];
1111
855
  for (const route of destRoutes) {
1112
856
  const routeOptions = route.options;
@@ -1162,28 +906,28 @@ function buildMiddlewareChain(destRoutes) {
1162
906
  meta
1163
907
  });
1164
908
  };
1165
- return function middleware(search, nextDest, _includeValidateSearch) {
1166
- dest = nextDest;
1167
- includeValidateSearch = _includeValidateSearch;
1168
- return applyNext(0, search);
1169
- };
909
+ return applyNext(0, search);
1170
910
  }
1171
911
  function findGlobalNotFoundRouteId(notFoundMode, routes) {
1172
- if (notFoundMode !== "root") for (let i = routes.length - 1; i >= 0; i--) {
1173
- const route = routes[i];
1174
- if (route.children) return route.id;
912
+ if (notFoundMode !== "root") {
913
+ let fallback;
914
+ for (let i = routes.length - 1; i >= 0; i--) {
915
+ const route = routes[i];
916
+ if (route.options.notFoundComponent) return route.id;
917
+ fallback ||= route.children && route.id;
918
+ }
919
+ if (fallback) return fallback;
1175
920
  }
1176
921
  return rootRouteId;
1177
922
  }
923
+ function resolveNextParams(spec, base) {
924
+ return spec === false || spec === null ? Object.create(null) : (spec ?? true) === true ? base : Object.assign(base, functionalUpdate(spec, base));
925
+ }
1178
926
  function extractStrictParams(route, accumulatedParams) {
1179
927
  const parseParams = route.options.params?.parse ?? route.options.parseParams;
1180
- if (parseParams) {
1181
- const result = parseParams(accumulatedParams);
1182
- if (result === false) throw new Error("Route params.parse returned false for a matched route");
1183
- Object.assign(accumulatedParams, result);
1184
- }
928
+ if (parseParams) Object.assign(accumulatedParams, parseParams(accumulatedParams));
1185
929
  }
1186
930
  //#endregion
1187
- export { PathParamError, RouterCore, SearchParamError, defaultSerializeError, getInitialRouterState, getLocationChangeInfo, getMatchedRoutes, lazyFn, trailingSlashOptions };
931
+ export { PathParamError, RouterCore, SearchParamError, _getUserHistoryState, defaultSerializeError, getInitialRouterState, getLocationChangeInfo, lazyFn, runRouteLifecycle, trailingSlashOptions };
1188
932
 
1189
933
  //# sourceMappingURL=router.js.map