react-router 8.3.1 → 8.5.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 (218) hide show
  1. package/CHANGELOG.md +83 -0
  2. package/dist/development/dom-export.d.ts +2 -1
  3. package/dist/development/dom-export.js +1 -1
  4. package/dist/development/index-react-server-client.js +2 -2
  5. package/dist/development/index-react-server.d.ts +601 -189
  6. package/dist/development/index-react-server.js +100 -48
  7. package/dist/development/index.d.ts +10 -9
  8. package/dist/development/index.js +6 -5
  9. package/dist/development/lib/actions.js +1 -1
  10. package/dist/development/lib/components.js +66 -10
  11. package/dist/development/lib/context.d.ts +19 -6
  12. package/dist/development/lib/context.js +11 -3
  13. package/dist/development/lib/dom/dom.d.ts +3 -0
  14. package/dist/development/lib/dom/dom.js +1 -1
  15. package/dist/development/lib/dom/global.d.ts +35 -0
  16. package/dist/development/lib/dom/lib.d.ts +34 -9
  17. package/dist/development/lib/dom/lib.js +30 -37
  18. package/dist/development/lib/dom/server.d.ts +6 -1
  19. package/dist/development/lib/dom/server.js +41 -15
  20. package/dist/development/lib/dom/ssr/components.js +26 -31
  21. package/dist/development/lib/dom/ssr/data.js +1 -1
  22. package/dist/development/lib/dom/ssr/entry.d.ts +13 -2
  23. package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
  24. package/dist/development/lib/dom/ssr/fallback.js +1 -1
  25. package/dist/development/lib/dom/ssr/fog-of-war.d.ts +3 -2
  26. package/dist/development/lib/dom/ssr/fog-of-war.js +46 -22
  27. package/dist/development/lib/dom/ssr/hydration.d.ts +3 -1
  28. package/dist/development/lib/dom/ssr/hydration.js +6 -4
  29. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  30. package/dist/development/lib/dom/ssr/links.js +1 -1
  31. package/dist/development/lib/dom/ssr/markup.js +5 -2
  32. package/dist/development/lib/dom/ssr/route-discovery.d.ts +120 -0
  33. package/dist/development/lib/dom/ssr/route-discovery.js +415 -0
  34. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  35. package/dist/development/lib/dom/ssr/routes-test-stub.js +2 -2
  36. package/dist/development/lib/dom/ssr/routes.d.ts +2 -2
  37. package/dist/development/lib/dom/ssr/routes.js +8 -8
  38. package/dist/development/lib/dom/ssr/server.js +4 -2
  39. package/dist/development/lib/dom/ssr/single-fetch.d.ts +28 -2
  40. package/dist/development/lib/dom/ssr/single-fetch.js +80 -71
  41. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  42. package/dist/development/lib/dom-export/hydrated-router.d.ts +28 -0
  43. package/dist/development/lib/dom-export/hydrated-router.js +33 -12
  44. package/dist/development/lib/errors.js +1 -1
  45. package/dist/development/lib/hooks.d.ts +3 -3
  46. package/dist/development/lib/hooks.js +52 -47
  47. package/dist/development/lib/href.js +1 -1
  48. package/dist/development/lib/router/history.js +1 -1
  49. package/dist/development/lib/router/instrumentation.js +1 -1
  50. package/dist/development/lib/router/matcher-route-pattern.d.ts +2 -0
  51. package/dist/development/lib/router/matcher-route-pattern.js +216 -0
  52. package/dist/development/lib/router/matcher-route-pattern.preload.d.ts +20 -0
  53. package/dist/development/lib/router/matcher-route-pattern.preload.js +36 -0
  54. package/dist/development/lib/router/matcher.d.ts +2 -0
  55. package/dist/development/lib/router/matcher.js +29 -0
  56. package/dist/development/lib/router/navigation.js +1 -1
  57. package/dist/development/lib/router/router.d.ts +19 -7
  58. package/dist/development/lib/router/router.js +235 -128
  59. package/dist/development/lib/router/url.js +1 -1
  60. package/dist/development/lib/router/utils.d.ts +30 -5
  61. package/dist/development/lib/router/utils.js +8 -5
  62. package/dist/development/lib/rsc/browser.js +3 -2
  63. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  64. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  65. package/dist/development/lib/rsc/html-stream/server.js +25 -15
  66. package/dist/development/lib/rsc/route-modules.js +1 -1
  67. package/dist/development/lib/rsc/server.rsc.d.ts +10 -2
  68. package/dist/development/lib/rsc/server.ssr.js +25 -21
  69. package/dist/development/lib/server-runtime/build.d.ts +14 -0
  70. package/dist/development/lib/server-runtime/cookies.js +1 -1
  71. package/dist/development/lib/server-runtime/crypto.js +1 -1
  72. package/dist/development/lib/server-runtime/data.js +1 -1
  73. package/dist/development/lib/server-runtime/dev.js +1 -1
  74. package/dist/development/lib/server-runtime/entry.js +1 -1
  75. package/dist/development/lib/server-runtime/errors.js +1 -1
  76. package/dist/development/lib/server-runtime/headers.js +1 -1
  77. package/dist/development/lib/server-runtime/invariant.js +1 -1
  78. package/dist/development/lib/server-runtime/mode.js +1 -1
  79. package/dist/development/lib/server-runtime/routeMatching.js +3 -4
  80. package/dist/development/lib/server-runtime/routes.d.ts +1 -1
  81. package/dist/development/lib/server-runtime/routes.js +1 -1
  82. package/dist/development/lib/server-runtime/server.js +18 -9
  83. package/dist/development/lib/server-runtime/serverHandoff.d.ts +17 -0
  84. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  85. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
  86. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
  87. package/dist/development/lib/server-runtime/sessions.js +1 -1
  88. package/dist/development/lib/server-runtime/single-fetch.js +1 -1
  89. package/dist/development/lib/server-runtime/urls.js +1 -1
  90. package/dist/development/lib/server-runtime/warnings.js +1 -1
  91. package/dist/development/lib/types/internal.js +1 -1
  92. package/dist/development/lib/types/route-data.d.ts +2 -2
  93. package/dist/development/lib/types/route-module-annotations.d.ts +1 -1
  94. package/dist/development/route-pattern.d.ts +3 -0
  95. package/dist/development/route-pattern.js +12 -0
  96. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  97. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  98. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  99. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  100. package/dist/production/dom-export.d.ts +2 -1
  101. package/dist/production/dom-export.js +1 -1
  102. package/dist/production/index-react-server-client.js +2 -2
  103. package/dist/production/index-react-server.d.ts +601 -189
  104. package/dist/production/index-react-server.js +100 -48
  105. package/dist/production/index.d.ts +10 -9
  106. package/dist/production/index.js +6 -5
  107. package/dist/production/lib/actions.js +1 -1
  108. package/dist/production/lib/components.js +66 -10
  109. package/dist/production/lib/context.d.ts +19 -6
  110. package/dist/production/lib/context.js +11 -3
  111. package/dist/production/lib/dom/dom.d.ts +3 -0
  112. package/dist/production/lib/dom/dom.js +1 -1
  113. package/dist/production/lib/dom/global.d.ts +35 -0
  114. package/dist/production/lib/dom/lib.d.ts +34 -9
  115. package/dist/production/lib/dom/lib.js +30 -37
  116. package/dist/production/lib/dom/server.d.ts +6 -1
  117. package/dist/production/lib/dom/server.js +41 -15
  118. package/dist/production/lib/dom/ssr/components.js +26 -31
  119. package/dist/production/lib/dom/ssr/data.js +1 -1
  120. package/dist/production/lib/dom/ssr/entry.d.ts +13 -2
  121. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  122. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  123. package/dist/production/lib/dom/ssr/fog-of-war.d.ts +3 -2
  124. package/dist/production/lib/dom/ssr/fog-of-war.js +46 -22
  125. package/dist/production/lib/dom/ssr/hydration.d.ts +3 -1
  126. package/dist/production/lib/dom/ssr/hydration.js +6 -4
  127. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  128. package/dist/production/lib/dom/ssr/links.js +1 -1
  129. package/dist/production/lib/dom/ssr/markup.js +5 -2
  130. package/dist/production/lib/dom/ssr/route-discovery.d.ts +120 -0
  131. package/dist/production/lib/dom/ssr/route-discovery.js +415 -0
  132. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  133. package/dist/production/lib/dom/ssr/routes-test-stub.js +2 -2
  134. package/dist/production/lib/dom/ssr/routes.d.ts +2 -2
  135. package/dist/production/lib/dom/ssr/routes.js +8 -8
  136. package/dist/production/lib/dom/ssr/server.js +4 -2
  137. package/dist/production/lib/dom/ssr/single-fetch.d.ts +28 -2
  138. package/dist/production/lib/dom/ssr/single-fetch.js +80 -71
  139. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  140. package/dist/production/lib/dom-export/hydrated-router.d.ts +28 -0
  141. package/dist/production/lib/dom-export/hydrated-router.js +33 -12
  142. package/dist/production/lib/errors.js +1 -1
  143. package/dist/production/lib/hooks.d.ts +3 -3
  144. package/dist/production/lib/hooks.js +51 -46
  145. package/dist/production/lib/href.js +1 -1
  146. package/dist/production/lib/router/history.js +1 -1
  147. package/dist/production/lib/router/instrumentation.js +1 -1
  148. package/dist/production/lib/router/matcher-route-pattern.d.ts +2 -0
  149. package/dist/production/lib/router/matcher-route-pattern.js +216 -0
  150. package/dist/production/lib/router/matcher-route-pattern.preload.d.ts +20 -0
  151. package/dist/production/lib/router/matcher-route-pattern.preload.js +36 -0
  152. package/dist/production/lib/router/matcher.d.ts +2 -0
  153. package/dist/production/lib/router/matcher.js +29 -0
  154. package/dist/production/lib/router/navigation.js +1 -1
  155. package/dist/production/lib/router/router.d.ts +19 -7
  156. package/dist/production/lib/router/router.js +235 -128
  157. package/dist/production/lib/router/url.js +1 -1
  158. package/dist/production/lib/router/utils.d.ts +30 -5
  159. package/dist/production/lib/router/utils.js +8 -5
  160. package/dist/production/lib/rsc/browser.js +3 -2
  161. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  162. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  163. package/dist/production/lib/rsc/html-stream/server.js +25 -15
  164. package/dist/production/lib/rsc/route-modules.js +1 -1
  165. package/dist/production/lib/rsc/server.rsc.d.ts +10 -2
  166. package/dist/production/lib/rsc/server.ssr.js +25 -21
  167. package/dist/production/lib/server-runtime/build.d.ts +14 -0
  168. package/dist/production/lib/server-runtime/cookies.js +1 -1
  169. package/dist/production/lib/server-runtime/crypto.js +1 -1
  170. package/dist/production/lib/server-runtime/data.js +1 -1
  171. package/dist/production/lib/server-runtime/dev.js +1 -1
  172. package/dist/production/lib/server-runtime/entry.js +1 -1
  173. package/dist/production/lib/server-runtime/errors.js +1 -1
  174. package/dist/production/lib/server-runtime/headers.js +1 -1
  175. package/dist/production/lib/server-runtime/invariant.js +1 -1
  176. package/dist/production/lib/server-runtime/mode.js +1 -1
  177. package/dist/production/lib/server-runtime/routeMatching.js +3 -4
  178. package/dist/production/lib/server-runtime/routes.d.ts +1 -1
  179. package/dist/production/lib/server-runtime/routes.js +1 -1
  180. package/dist/production/lib/server-runtime/server.js +18 -9
  181. package/dist/production/lib/server-runtime/serverHandoff.d.ts +17 -0
  182. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  183. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
  184. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
  185. package/dist/production/lib/server-runtime/sessions.js +1 -1
  186. package/dist/production/lib/server-runtime/single-fetch.js +1 -1
  187. package/dist/production/lib/server-runtime/urls.js +1 -1
  188. package/dist/production/lib/server-runtime/warnings.js +1 -1
  189. package/dist/production/lib/types/internal.js +1 -1
  190. package/dist/production/lib/types/route-data.d.ts +2 -2
  191. package/dist/production/lib/types/route-module-annotations.d.ts +1 -1
  192. package/dist/production/route-pattern.d.ts +3 -0
  193. package/dist/production/route-pattern.js +12 -0
  194. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  195. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  196. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  197. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  198. package/docs/explanation/lazy-route-discovery.md +4 -0
  199. package/docs/explanation/sessions-and-cookies.md +108 -4
  200. package/docs/how-to/custom-fetch.md +218 -0
  201. package/docs/how-to/data-strategy.md +23 -1
  202. package/docs/how-to/fetchers.md +6 -1
  203. package/docs/how-to/middleware.md +4 -1
  204. package/docs/how-to/optimize-revalidation.md +178 -5
  205. package/docs/how-to/pre-rendering.md +23 -1
  206. package/docs/how-to/react-server-components.md +26 -3
  207. package/docs/how-to/resource-routes.md +3 -0
  208. package/docs/how-to/route-discovery.md +145 -0
  209. package/docs/how-to/spa.md +2 -0
  210. package/docs/how-to/view-transitions.md +80 -13
  211. package/docs/start/data/actions.md +2 -1
  212. package/docs/start/data/route-object.md +21 -6
  213. package/docs/start/framework/actions.md +4 -2
  214. package/docs/start/framework/navigating.md +5 -9
  215. package/docs/start/framework/rendering.md +37 -0
  216. package/docs/start/framework/route-module.md +32 -5
  217. package/docs/upgrading/future.md +68 -0
  218. package/package.json +19 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.1
2
+ * react-router v8.5.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -10,8 +10,10 @@
10
10
  */
11
11
  import { PROTOCOL_RELATIVE_URL_REGEX, normalizeProtocolRelativeUrl } from "./url.js";
12
12
  import { createBrowserURLImpl, createLocation, createPath, invariant, parsePath, warning } from "./history.js";
13
- import { ErrorResponseImpl, RouterContextProvider, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createDataFunctionUrl, flattenAndRankRoutes, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, matchRoutesImpl, prependBasename, removeDoubleSlashes, resolveTo, stripBasename } from "./utils.js";
13
+ import { DiscoveryCancelledError, ErrorResponseImpl, RouterContextProvider, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createDataFunctionUrl, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, prependBasename, removeDoubleSlashes, resolveTo, stripBasename } from "./utils.js";
14
14
  import { consumeInstrumentationClientResultMetaReceiver, getRouteInstrumentationUpdates, instrumentClientSideRouter } from "./instrumentation.js";
15
+ import { V6RegExMatcher } from "./matcher.js";
16
+ import { getRoutePatternMatcher } from "./matcher-route-pattern.preload.js";
15
17
  import { validateNavigationTarget } from "./navigation.js";
16
18
  //#region lib/router/router.ts
17
19
  const validMutationMethodsArr = [
@@ -61,18 +63,26 @@ const IDLE_BLOCKER = {
61
63
  };
62
64
  const TRANSITIONS_STORAGE_KEY = "remix-router-transitions";
63
65
  const ResetLoaderDataSymbol = Symbol("ResetLoaderData");
66
+ function createDataRouteMatcher(future, basename) {
67
+ if (future.unstable_routePatternMatching) {
68
+ let RoutePatternMatcher = getRoutePatternMatcher();
69
+ invariant(RoutePatternMatcher, "You must call unstable_preloadRoutePattern() from \"react-router/route-pattern\" before enabling future.unstable_routePatternMatching.");
70
+ return new RoutePatternMatcher(basename);
71
+ }
72
+ return new V6RegExMatcher(basename);
73
+ }
64
74
  /**
65
75
  * Encapsulates the stable and in-flight route trees together with their
66
76
  * pre-computed branch caches so the structures always stay in sync.
67
77
  */
68
78
  var DataRoutes = class {
69
79
  #routes;
70
- #branches;
71
80
  #hmrRoutes;
72
- #hmrBranches;
73
- constructor(routes) {
81
+ #matcher;
82
+ constructor(routes, matcher) {
74
83
  this.#routes = routes;
75
- this.#branches = flattenAndRankRoutes(routes);
84
+ this.#matcher = matcher;
85
+ this.#matcher.update(routes);
76
86
  }
77
87
  /** The stable route tree */
78
88
  get stableRoutes() {
@@ -82,33 +92,29 @@ var DataRoutes = class {
82
92
  get activeRoutes() {
83
93
  return this.#hmrRoutes ?? this.#routes;
84
94
  }
85
- /** Pre-computed branches */
86
- get branches() {
87
- return this.#hmrBranches ?? this.#branches;
88
- }
89
95
  get hasHMRRoutes() {
90
96
  return this.#hmrRoutes != null;
91
97
  }
92
98
  /** Replace the stable route tree and recompute its branches */
93
99
  setRoutes(routes) {
94
100
  this.#routes = routes;
95
- this.#branches = flattenAndRankRoutes(routes);
101
+ if (!this.#hmrRoutes) this.#matcher.update(routes);
96
102
  }
97
103
  /** Set a new in-flight route tree and recompute its branches */
98
104
  setHmrRoutes(routes) {
99
105
  this.#hmrRoutes = routes;
100
- this.#hmrBranches = flattenAndRankRoutes(routes);
106
+ this.#matcher.update(routes);
101
107
  }
102
108
  /** Commit in-flight routes/branches to the stable slot and clear in-flight */
103
109
  commitHmrRoutes() {
104
110
  if (this.#hmrRoutes) {
105
111
  this.#routes = this.#hmrRoutes;
106
- this.#branches = this.#hmrBranches;
107
112
  this.#hmrRoutes = void 0;
108
- this.#hmrBranches = void 0;
113
+ this.#matcher.update(this.#routes);
109
114
  }
110
115
  }
111
116
  };
117
+ const clientErrors = /* @__PURE__ */ new WeakMap();
112
118
  /**
113
119
  * Create a router and listen to history POP navigations
114
120
  */
@@ -128,20 +134,20 @@ function createRouter(init) {
128
134
  };
129
135
  };
130
136
  }
131
- let manifest = {};
132
- let dataRoutes = new DataRoutes(convertRoutesToDataRoutes(init.routes, mapRouteProperties, void 0, manifest));
137
+ let future = { ...init.future };
133
138
  let basename = init.basename || "/";
134
139
  if (!basename.startsWith("/")) basename = `/${basename}`;
140
+ let dataRouteMatcher = createDataRouteMatcher(future, basename);
141
+ let manifest = {};
142
+ let dataRoutes = new DataRoutes(convertRoutesToDataRoutes(init.routes, mapRouteProperties, void 0, manifest), dataRouteMatcher);
135
143
  let dataStrategyImpl = init.dataStrategy || defaultDataStrategyWithMiddleware;
136
- let future = { ...init.future };
137
144
  let unlistenHistory = null;
138
145
  let subscribers = /* @__PURE__ */ new Set();
139
- let bufferedInitialStateUpdate = null;
140
146
  let savedScrollPositions = null;
141
147
  let getScrollRestorationKey = null;
142
148
  let getScrollPosition = null;
143
149
  let initialScrollRestored = init.hydrationData != null;
144
- let initialMatches = matchRoutesImpl(dataRoutes.activeRoutes, init.history.location, basename, false, dataRoutes.branches);
150
+ let initialMatches = dataRouteMatcher.match(init.history.location);
145
151
  let initialMatchesIsFOW = false;
146
152
  let initialErrors = null;
147
153
  let initialized;
@@ -155,13 +161,13 @@ function createRouter(init) {
155
161
  initialErrors = { [route.id]: error };
156
162
  } else {
157
163
  if (initialMatches && !init.hydrationData) {
158
- if (checkFogOfWar(initialMatches, dataRoutes.activeRoutes, init.history.location.pathname).active) initialMatches = null;
164
+ if (checkFogOfWar(initialMatches, init.history.location.pathname).active) initialMatches = null;
159
165
  }
160
166
  if (!initialMatches) {
161
167
  initialized = false;
162
168
  renderFallback = !initialized;
163
169
  initialMatches = [];
164
- let fogOfWar = checkFogOfWar(null, dataRoutes.activeRoutes, init.history.location.pathname);
170
+ let fogOfWar = checkFogOfWar(null, init.history.location.pathname);
165
171
  if (fogOfWar.active && fogOfWar.matches) {
166
172
  initialMatchesIsFOW = true;
167
173
  initialMatches = fogOfWar.matches;
@@ -207,6 +213,8 @@ function createRouter(init) {
207
213
  blockers: /* @__PURE__ */ new Map()
208
214
  };
209
215
  let pendingAction = "POP";
216
+ let pendingPopstateDelta = 0;
217
+ let pendingDiscoveryCancellation;
210
218
  let pendingPopstateNavigationDfd = null;
211
219
  let pendingPreventScrollReset = false;
212
220
  let pendingNavigationController;
@@ -267,6 +275,7 @@ function createRouter(init) {
267
275
  pendingPopstateNavigationDfd = null;
268
276
  return;
269
277
  }
278
+ if (future.unstable_customRouteDiscovery) pendingPopstateDelta += delta || 0;
270
279
  return startNavigation(historyAction, location);
271
280
  });
272
281
  if (isBrowser) {
@@ -288,19 +297,16 @@ function createRouter(init) {
288
297
  }
289
298
  function subscribe(fn) {
290
299
  subscribers.add(fn);
291
- if (bufferedInitialStateUpdate) {
292
- let { newErrors } = bufferedInitialStateUpdate;
293
- bufferedInitialStateUpdate = null;
294
- fn(state, {
295
- deletedFetchers: [],
296
- newErrors,
297
- viewTransitionOpts: void 0,
298
- flushSync: false
299
- });
300
- }
301
300
  return () => subscribers.delete(fn);
302
301
  }
303
302
  function updateState(newState, opts = {}) {
303
+ let newErrors = newState.errors ?? null;
304
+ let cachedErrors = newErrors ? clientErrors.get(newErrors) : void 0;
305
+ if (cachedErrors !== void 0) newErrors = cachedErrors;
306
+ if (newState.errors !== void 0 || newState.initialized) {
307
+ if (state.errors) clientErrors.delete(state.errors);
308
+ if (newState.errors) clientErrors.set(newState.errors, newErrors);
309
+ }
304
310
  if (newState.matches) newState.matches = newState.matches.map((m) => {
305
311
  let route = manifest[m.route.id];
306
312
  let matchRoute = m.route;
@@ -323,10 +329,9 @@ function createRouter(init) {
323
329
  fetchersQueuedForDeletion.forEach((key) => {
324
330
  if (!state.fetchers.has(key) && !fetchControllers.has(key)) unmountedFetchers.push(key);
325
331
  });
326
- if (subscribers.size === 0) bufferedInitialStateUpdate = { newErrors: newState.errors ?? null };
327
332
  [...subscribers].forEach((subscriber) => subscriber(state, {
328
333
  deletedFetchers: unmountedFetchers,
329
- newErrors: newState.errors ?? null,
334
+ newErrors,
330
335
  viewTransitionOpts: opts.viewTransitionOpts,
331
336
  flushSync: opts.flushSync === true
332
337
  }));
@@ -352,7 +357,7 @@ function createRouter(init) {
352
357
  if (isUninterruptedRevalidation) {} else if (pendingAction === "POP") {} else if (pendingAction === "PUSH") init.history.push(location, location.state);
353
358
  else if (pendingAction === "REPLACE") init.history.replace(location, location.state);
354
359
  let viewTransitionOpts;
355
- if (pendingAction === "POP") {
360
+ if (pendingAction === "POP" && !isUninterruptedRevalidation && location !== state.location) {
356
361
  let priorPaths = appliedViewTransitions.get(state.location.pathname);
357
362
  if (priorPaths && priorPaths.has(location.pathname)) viewTransitionOpts = {
358
363
  currentLocation: state.location,
@@ -391,17 +396,22 @@ function createRouter(init) {
391
396
  viewTransitionOpts,
392
397
  flushSync: flushSync === true
393
398
  });
399
+ resetPendingNavigation();
400
+ pendingPopstateNavigationDfd?.resolve();
401
+ pendingPopstateNavigationDfd = null;
402
+ pendingRevalidationDfd?.resolve();
403
+ pendingRevalidationDfd = null;
404
+ }
405
+ function resetPendingNavigation() {
406
+ pendingPopstateDelta = 0;
394
407
  pendingAction = "POP";
395
408
  pendingPreventScrollReset = false;
396
409
  pendingViewTransitionEnabled = false;
397
410
  isUninterruptedRevalidation = false;
398
411
  isRevalidationRequired = false;
399
- pendingPopstateNavigationDfd?.resolve();
400
- pendingPopstateNavigationDfd = null;
401
- pendingRevalidationDfd?.resolve();
402
- pendingRevalidationDfd = null;
403
412
  }
404
413
  async function navigate(to, opts) {
414
+ if (pendingDiscoveryCancellation) await pendingDiscoveryCancellation;
405
415
  pendingPopstateNavigationDfd?.resolve();
406
416
  pendingPopstateNavigationDfd = null;
407
417
  if (typeof to === "number") {
@@ -478,22 +488,28 @@ function createRouter(init) {
478
488
  });
479
489
  }
480
490
  function revalidate() {
491
+ if (pendingDiscoveryCancellation) return pendingDiscoveryCancellation.then(revalidate);
481
492
  if (!pendingRevalidationDfd) pendingRevalidationDfd = createDeferred();
482
493
  interruptActiveLoads();
483
494
  updateState({ revalidation: "loading" });
484
495
  let promise = pendingRevalidationDfd.promise;
485
496
  if (state.navigation.state === "submitting") return promise;
486
497
  if (state.navigation.state === "idle") {
487
- startNavigation(state.historyAction, state.location, { startUninterruptedRevalidation: true });
498
+ startNavigation(state.historyAction, state.location, {
499
+ startUninterruptedRevalidation: true,
500
+ dataStrategyInitiator: "revalidation"
501
+ });
488
502
  return promise;
489
503
  }
490
504
  startNavigation(pendingAction || state.historyAction, state.navigation.location, {
491
505
  overrideNavigation: state.navigation,
506
+ dataStrategyInitiator: "revalidation",
492
507
  enableViewTransition: pendingViewTransitionEnabled === true
493
508
  });
494
509
  return promise;
495
510
  }
496
511
  async function startNavigation(historyAction, location, opts) {
512
+ if (pendingDiscoveryCancellation) await pendingDiscoveryCancellation;
497
513
  pendingNavigationController && pendingNavigationController.abort();
498
514
  pendingNavigationController = null;
499
515
  pendingAction = historyAction;
@@ -501,14 +517,13 @@ function createRouter(init) {
501
517
  saveScrollPosition(state.location, state.matches);
502
518
  pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
503
519
  pendingViewTransitionEnabled = (opts && opts.enableViewTransition) === true;
504
- let routesToUse = dataRoutes.activeRoutes;
505
- let matches = opts?.initialHydration && state.matches && state.matches.length > 0 && !initialMatchesIsFOW ? state.matches : matchRoutesImpl(routesToUse, location, basename, false, dataRoutes.branches);
520
+ let matches = opts?.initialHydration && state.matches && state.matches.length > 0 && !initialMatchesIsFOW ? state.matches : dataRouteMatcher.match(location);
506
521
  let flushSync = (opts && opts.flushSync) === true;
507
522
  if (matches && state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
508
523
  completeNavigation(location, { matches }, { flushSync });
509
524
  return;
510
525
  }
511
- let fogOfWar = checkFogOfWar(matches, routesToUse, location.pathname);
526
+ let fogOfWar = checkFogOfWar(matches, location.pathname);
512
527
  if (fogOfWar.active && fogOfWar.matches) matches = fogOfWar.matches;
513
528
  if (opts?.instrumentationNavigateMetaReceiver) {
514
529
  let meta = getInstrumentationNavigateMeta(init.history, location, matches);
@@ -530,6 +545,7 @@ function createRouter(init) {
530
545
  } : void 0;
531
546
  pendingNavigationController = new AbortController();
532
547
  let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
548
+ let dataStrategyInitiator = opts?.initialHydration ? "initialization" : opts?.dataStrategyInitiator ?? "navigation";
533
549
  let scopedContext = init.getContext ? await init.getContext() : new RouterContextProvider();
534
550
  let pendingActionResult;
535
551
  if (opts && opts.pendingError) pendingActionResult = [findNearestBoundary(matches).route.id, {
@@ -537,7 +553,7 @@ function createRouter(init) {
537
553
  error: opts.pendingError
538
554
  }];
539
555
  else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
540
- let actionResult = await handleAction(request, location, opts.submission, matches, historyAction, scopedContext, fogOfWar.active, opts && opts.initialHydration === true, {
556
+ let actionResult = await handleAction(request, dataStrategyInitiator, location, opts.submission, matches, historyAction, scopedContext, fogOfWar.active, opts && opts.initialHydration === true, {
541
557
  replace: opts.replace,
542
558
  flushSync
543
559
  });
@@ -561,7 +577,7 @@ function createRouter(init) {
561
577
  fogOfWar.active = false;
562
578
  request = createClientSideRequest(init.history, request.url, request.signal);
563
579
  }
564
- let { shortCircuited, matches: updatedMatches, loaderData, errors, workingFetchers } = await handleLoaders(request, location, matches, historyAction, scopedContext, fogOfWar.active, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, opts && opts.initialHydration === true, flushSync, pendingActionResult, opts && opts.callSiteDefaultShouldRevalidate);
580
+ let { shortCircuited, matches: updatedMatches, loaderData, errors, workingFetchers } = await handleLoaders(request, dataStrategyInitiator, location, matches, historyAction, scopedContext, fogOfWar.active, loadingNavigation, opts && opts.submission, opts && opts.fetcherSubmission, opts && opts.replace, opts && opts.initialHydration === true, flushSync, pendingActionResult, opts && opts.callSiteDefaultShouldRevalidate);
565
581
  if (shortCircuited) return;
566
582
  pendingNavigationController = null;
567
583
  completeNavigation(location, {
@@ -572,13 +588,57 @@ function createRouter(init) {
572
588
  ...workingFetchers ? { fetchers: workingFetchers } : {}
573
589
  });
574
590
  }
575
- async function handleAction(request, location, submission, matches, historyAction, scopedContext, isFogOfWar, initialHydration, opts = {}) {
576
- interruptActiveLoads();
591
+ function cancelDiscoveryNavigation() {
592
+ let cancellation = createDeferred();
593
+ pendingDiscoveryCancellation = cancellation.promise;
594
+ let delta = pendingPopstateDelta;
595
+ let popstateNavigationDfd = pendingPopstateNavigationDfd;
596
+ pendingPopstateNavigationDfd = null;
597
+ pendingNavigationController?.abort();
598
+ pendingNavigationController = null;
599
+ resetPendingNavigation();
600
+ pendingRevalidationDfd?.resolve();
601
+ pendingRevalidationDfd = null;
602
+ let blockers = new Map(state.blockers);
603
+ blockers.forEach((blocker, key) => {
604
+ if (blocker.state === "proceeding") blockers.set(key, IDLE_BLOCKER);
605
+ });
606
+ let fetchers = new Map(state.fetchers);
607
+ let didUpdateFetchers = markFetchRedirectsDone(fetchers);
608
+ fetchers.forEach((fetcher, key) => {
609
+ if (fetcher.state === "loading" && !fetchControllers.has(key)) {
610
+ fetchers.set(key, getDoneFetcher(fetcher.data));
611
+ didUpdateFetchers = true;
612
+ }
613
+ });
614
+ cancelledFetcherLoads.clear();
615
+ updateState({
616
+ navigation: IDLE_NAVIGATION,
617
+ revalidation: "idle",
618
+ blockers,
619
+ ...didUpdateFetchers ? { fetchers } : {}
620
+ });
621
+ function finishCancellation() {
622
+ pendingDiscoveryCancellation = void 0;
623
+ popstateNavigationDfd?.resolve();
624
+ cancellation.resolve();
625
+ }
626
+ if (delta) {
627
+ unblockBlockerHistoryUpdate = finishCancellation;
628
+ init.history.go(-delta);
629
+ } else finishCancellation();
630
+ return cancellation.promise;
631
+ }
632
+ async function handleAction(request, dataStrategyInitiator, location, submission, matches, historyAction, scopedContext, isFogOfWar, initialHydration, opts = {}) {
633
+ if (!future.unstable_customRouteDiscovery) interruptActiveLoads();
577
634
  updateState({ navigation: getSubmittingNavigation(location, matches, historyAction, submission) }, { flushSync: opts.flushSync === true });
578
635
  if (isFogOfWar) {
579
636
  let discoverResult = await discoverRoutes(matches, location.pathname, request.signal);
580
- if (discoverResult.type === "aborted") return { shortCircuited: true };
581
- else if (discoverResult.type === "error") {
637
+ if (future.unstable_customRouteDiscovery && request.signal.aborted || discoverResult.type === "aborted") return { shortCircuited: true };
638
+ else if (discoverResult.type === "cancelled") {
639
+ await cancelDiscoveryNavigation();
640
+ return { shortCircuited: true };
641
+ } else if (discoverResult.type === "error") {
582
642
  if (discoverResult.partialMatches.length === 0) {
583
643
  let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
584
644
  return {
@@ -608,6 +668,7 @@ function createRouter(init) {
608
668
  };
609
669
  } else matches = discoverResult.matches;
610
670
  }
671
+ if (future.unstable_customRouteDiscovery) interruptActiveLoads();
611
672
  let result;
612
673
  let actionMatch = getTargetMatch(matches, location);
613
674
  if (!actionMatch.route.action && !actionMatch.route.lazy) result = {
@@ -619,7 +680,7 @@ function createRouter(init) {
619
680
  })
620
681
  };
621
682
  else {
622
- let results = await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, initialHydration ? [] : hydrationRouteProperties, scopedContext), scopedContext, null);
683
+ let results = await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, initialHydration ? [] : hydrationRouteProperties, scopedContext), scopedContext, null, dataStrategyInitiator, historyAction);
623
684
  result = results[actionMatch.route.id];
624
685
  if (!result) {
625
686
  for (let match of matches) if (results[match.route.id]) {
@@ -633,7 +694,7 @@ function createRouter(init) {
633
694
  let replace;
634
695
  if (opts && opts.replace != null) replace = opts.replace;
635
696
  else replace = normalizeRedirectLocation(result.response.headers.get("Location"), new URL(request.url), basename, init.history) === state.location.pathname + state.location.search;
636
- await startRedirectNavigation(request, result, true, {
697
+ await startRedirectNavigation(request, result, true, dataStrategyInitiator, {
637
698
  submission,
638
699
  replace
639
700
  });
@@ -656,7 +717,7 @@ function createRouter(init) {
656
717
  pendingActionResult: [actionMatch.route.id, result]
657
718
  };
658
719
  }
659
- async function handleLoaders(request, location, matches, historyAction, scopedContext, isFogOfWar, overrideNavigation, submission, fetcherSubmission, replace, initialHydration, flushSync, pendingActionResult, callSiteDefaultShouldRevalidate) {
720
+ async function handleLoaders(request, dataStrategyInitiator, location, matches, historyAction, scopedContext, isFogOfWar, overrideNavigation, submission, fetcherSubmission, replace, initialHydration, flushSync, pendingActionResult, callSiteDefaultShouldRevalidate) {
660
721
  let loadingNavigation = overrideNavigation || getLoadingNavigation(location, matches, historyAction, submission);
661
722
  let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation);
662
723
  let shouldUpdateNavigationState = !isUninterruptedRevalidation && !initialHydration;
@@ -669,8 +730,11 @@ function createRouter(init) {
669
730
  }, { flushSync });
670
731
  }
671
732
  let discoverResult = await discoverRoutes(matches, location.pathname, request.signal);
672
- if (discoverResult.type === "aborted") return { shortCircuited: true };
673
- else if (discoverResult.type === "error") {
733
+ if (future.unstable_customRouteDiscovery && request.signal.aborted || discoverResult.type === "aborted") return { shortCircuited: true };
734
+ else if (discoverResult.type === "cancelled") {
735
+ await cancelDiscoveryNavigation();
736
+ return { shortCircuited: true };
737
+ } else if (discoverResult.type === "error") {
674
738
  if (discoverResult.partialMatches.length === 0) {
675
739
  let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
676
740
  return {
@@ -692,10 +756,12 @@ function createRouter(init) {
692
756
  loaderData: {},
693
757
  errors: { [route.id]: error }
694
758
  };
695
- } else matches = discoverResult.matches;
759
+ } else {
760
+ matches = discoverResult.matches;
761
+ if (initialHydration && !request.signal.aborted) updateState({ matches });
762
+ }
696
763
  }
697
- let routesToUse = dataRoutes.activeRoutes;
698
- let { dsMatches, revalidatingFetchers } = getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, init.history, state, matches, activeSubmission, location, initialHydration ? [] : hydrationRouteProperties, initialHydration === true, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, init.patchRoutesOnNavigation != null, dataRoutes.branches, pendingActionResult, callSiteDefaultShouldRevalidate);
764
+ let { dsMatches, revalidatingFetchers } = getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, init.history, state, matches, activeSubmission, location, initialHydration ? [] : hydrationRouteProperties, initialHydration === true, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, dataRouteMatcher, pendingActionResult, callSiteDefaultShouldRevalidate);
699
765
  pendingNavigationLoadId = ++incrementingLoadId;
700
766
  if (!init.dataStrategy && !dsMatches.some((m) => m.shouldLoad) && !dsMatches.some((m) => m.route.middleware && m.route.middleware.length > 0) && revalidatingFetchers.length === 0) {
701
767
  let workingFetchers = new Map(state.fetchers);
@@ -725,27 +791,31 @@ function createRouter(init) {
725
791
  });
726
792
  let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((f) => abortFetcher(f.key));
727
793
  if (pendingNavigationController) pendingNavigationController.signal.addEventListener("abort", abortPendingFetchRevalidations);
728
- let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, request, location, scopedContext);
794
+ let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, request, location, scopedContext, dataStrategyInitiator, historyAction);
729
795
  if (request.signal.aborted) return { shortCircuited: true };
730
796
  if (pendingNavigationController) pendingNavigationController.signal.removeEventListener("abort", abortPendingFetchRevalidations);
731
797
  revalidatingFetchers.forEach((rf) => fetchControllers.delete(rf.key));
732
798
  let redirect = findRedirect(loaderResults);
733
799
  if (redirect) {
734
- await startRedirectNavigation(request, redirect.result, true, { replace });
800
+ await startRedirectNavigation(request, redirect.result, true, dataStrategyInitiator, { replace });
735
801
  return { shortCircuited: true };
736
802
  }
737
803
  redirect = findRedirect(fetcherResults);
738
804
  if (redirect) {
739
805
  fetchRedirectIds.add(redirect.key);
740
- await startRedirectNavigation(request, redirect.result, true, { replace });
806
+ await startRedirectNavigation(request, redirect.result, true, dataStrategyInitiator, { replace });
741
807
  return { shortCircuited: true };
742
808
  }
743
809
  let workingFetchers = new Map(state.fetchers);
744
810
  let { loaderData, errors } = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults, workingFetchers);
745
- if (initialHydration && state.errors) errors = {
746
- ...state.errors,
747
- ...errors
748
- };
811
+ if (initialHydration && state.errors) {
812
+ let newClientErrors = errors ?? null;
813
+ errors = {
814
+ ...state.errors,
815
+ ...errors
816
+ };
817
+ clientErrors.set(errors, newClientErrors);
818
+ }
749
819
  let didUpdateFetcherRedirects = markFetchRedirectsDone(workingFetchers);
750
820
  let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId, workingFetchers);
751
821
  let shouldUpdateFetchers = didUpdateFetcherRedirects || didAbortFetchLoads || revalidatingFetchers.length > 0;
@@ -771,13 +841,13 @@ function createRouter(init) {
771
841
  return workingFetchers;
772
842
  }
773
843
  async function fetch(key, routeId, href, opts) {
844
+ if (pendingDiscoveryCancellation) await pendingDiscoveryCancellation;
774
845
  abortFetcher(key);
775
846
  let flushSync = (opts && opts.flushSync) === true;
776
847
  let instrumentationResultMetaReceiver = consumeInstrumentationClientResultMetaReceiver(router);
777
- let routesToUse = dataRoutes.activeRoutes;
778
848
  let normalizedPath = normalizeTo(state.location, state.matches, basename, href, routeId, opts?.relative);
779
- let matches = matchRoutesImpl(routesToUse, normalizedPath, basename, false, dataRoutes.branches);
780
- let fogOfWar = checkFogOfWar(matches, routesToUse, normalizedPath);
849
+ let matches = dataRouteMatcher.match(normalizedPath);
850
+ let fogOfWar = checkFogOfWar(matches, normalizedPath);
781
851
  if (fogOfWar.active && fogOfWar.matches) matches = fogOfWar.matches;
782
852
  if (instrumentationResultMetaReceiver) instrumentationResultMetaReceiver(getInstrumentationNavigateMeta(init.history, normalizedPath, matches));
783
853
  if (!matches) {
@@ -795,22 +865,31 @@ function createRouter(init) {
795
865
  await handleFetcherAction(key, routeId, path, matches, scopedContext, fogOfWar.active, flushSync, preventScrollReset, submission, opts && opts.defaultShouldRevalidate);
796
866
  return;
797
867
  }
798
- fetchLoadMatches.set(key, {
868
+ let loadMatch = {
799
869
  routeId,
800
- path
801
- });
802
- await handleFetcherLoader(key, routeId, path, matches, scopedContext, fogOfWar.active, flushSync, preventScrollReset, submission);
870
+ path,
871
+ isDiscovering: fogOfWar.active
872
+ };
873
+ fetchLoadMatches.set(key, loadMatch);
874
+ await handleFetcherLoader(key, loadMatch, matches, scopedContext, flushSync, preventScrollReset, submission);
803
875
  }
804
876
  async function handleFetcherAction(key, routeId, path, requestMatches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission, callSiteDefaultShouldRevalidate) {
805
- interruptActiveLoads();
877
+ if (!future.unstable_customRouteDiscovery) interruptActiveLoads();
806
878
  fetchLoadMatches.delete(key);
807
- updateFetcherState(key, getSubmittingFetcher(submission, state.fetchers.get(key)), { flushSync });
879
+ let existingFetcher = state.fetchers.get(key);
880
+ updateFetcherState(key, getSubmittingFetcher(submission, existingFetcher), { flushSync });
808
881
  let abortController = new AbortController();
882
+ if (future.unstable_customRouteDiscovery) fetchControllers.set(key, abortController);
809
883
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
810
884
  if (isFogOfWar) {
811
885
  let discoverResult = await discoverRoutes(requestMatches, new URL(fetchRequest.url).pathname, fetchRequest.signal, key);
812
- if (discoverResult.type === "aborted") return;
813
- else if (discoverResult.type === "error") {
886
+ if (future.unstable_customRouteDiscovery && fetchRequest.signal.aborted || discoverResult.type === "aborted") return;
887
+ else if (discoverResult.type === "cancelled") {
888
+ fetchControllers.delete(key);
889
+ fetchLoadMatches.delete(key);
890
+ updateFetcherState(key, getDoneFetcher(existingFetcher?.data), { flushSync });
891
+ return;
892
+ } else if (discoverResult.type === "error") {
814
893
  setFetcherError(key, routeId, discoverResult.error, { flushSync });
815
894
  return;
816
895
  } else if (!discoverResult.matches) {
@@ -827,10 +906,11 @@ function createRouter(init) {
827
906
  }), { flushSync });
828
907
  return;
829
908
  }
909
+ if (future.unstable_customRouteDiscovery) interruptActiveLoads();
830
910
  fetchControllers.set(key, abortController);
831
911
  let originatingLoadId = incrementingLoadId;
832
912
  let fetchMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, requestMatches, match, hydrationRouteProperties, scopedContext);
833
- let actionResults = await callDataStrategy(fetchRequest, path, fetchMatches, scopedContext, key);
913
+ let actionResults = await callDataStrategy(fetchRequest, path, fetchMatches, scopedContext, key, "fetcher");
834
914
  let actionResult = actionResults[match.route.id];
835
915
  if (!actionResult) {
836
916
  for (let match of fetchMatches) if (actionResults[match.route.id]) {
@@ -856,7 +936,7 @@ function createRouter(init) {
856
936
  } else {
857
937
  fetchRedirectIds.add(key);
858
938
  updateFetcherState(key, getLoadingFetcher(submission));
859
- return startRedirectNavigation(fetchRequest, actionResult, false, {
939
+ return startRedirectNavigation(fetchRequest, actionResult, false, "fetcher", {
860
940
  fetcherSubmission: submission,
861
941
  preventScrollReset
862
942
  });
@@ -869,12 +949,11 @@ function createRouter(init) {
869
949
  }
870
950
  let nextLocation = state.navigation.location || state.location;
871
951
  let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
872
- let routesToUse = dataRoutes.activeRoutes;
873
- let matches = state.navigation.state !== "idle" ? matchRoutesImpl(routesToUse, state.navigation.location, basename, false, dataRoutes.branches) : state.matches;
952
+ let matches = state.navigation.state !== "idle" ? dataRouteMatcher.match(state.navigation.location) : state.matches;
874
953
  invariant(matches, "Didn't find any matches after fetcher action");
875
954
  let loadId = ++incrementingLoadId;
876
955
  fetchReloadIds.set(key, loadId);
877
- let { dsMatches, revalidatingFetchers } = getMatchesToLoad(revalidationRequest, scopedContext, mapRouteProperties, manifest, init.history, state, matches, submission, nextLocation, hydrationRouteProperties, false, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, init.patchRoutesOnNavigation != null, dataRoutes.branches, [match.route.id, actionResult], callSiteDefaultShouldRevalidate);
956
+ let { dsMatches, revalidatingFetchers } = getMatchesToLoad(revalidationRequest, scopedContext, mapRouteProperties, manifest, init.history, state, matches, submission, nextLocation, hydrationRouteProperties, false, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, dataRouteMatcher, [match.route.id, actionResult], callSiteDefaultShouldRevalidate);
878
957
  let loadFetcher = getLoadingFetcher(submission, actionResult.data);
879
958
  let workingFetchers = new Map(state.fetchers);
880
959
  workingFetchers.set(key, loadFetcher);
@@ -889,7 +968,7 @@ function createRouter(init) {
889
968
  updateState({ fetchers: workingFetchers });
890
969
  let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => abortFetcher(rf.key));
891
970
  abortController.signal.addEventListener("abort", abortPendingFetchRevalidations);
892
- let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, revalidationRequest, nextLocation, scopedContext);
971
+ let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, revalidationRequest, nextLocation, scopedContext, "fetcher");
893
972
  if (abortController.signal.aborted) {
894
973
  if (fetchReloadIds.get(key) === loadId) fetchReloadIds.delete(key);
895
974
  return;
@@ -911,13 +990,13 @@ function createRouter(init) {
911
990
  let redirect = findRedirect(loaderResults);
912
991
  if (redirect) {
913
992
  state = getRedirectStateWithDoneFetcher(state);
914
- return startRedirectNavigation(revalidationRequest, redirect.result, false, { preventScrollReset });
993
+ return startRedirectNavigation(revalidationRequest, redirect.result, false, "fetcher", { preventScrollReset });
915
994
  }
916
995
  redirect = findRedirect(fetcherResults);
917
996
  if (redirect) {
918
997
  fetchRedirectIds.add(redirect.key);
919
998
  state = getRedirectStateWithDoneFetcher(state);
920
- return startRedirectNavigation(revalidationRequest, redirect.result, false, { preventScrollReset });
999
+ return startRedirectNavigation(revalidationRequest, redirect.result, false, "fetcher", { preventScrollReset });
921
1000
  }
922
1001
  let finalFetchers = new Map(state.fetchers);
923
1002
  if (fetcherIsMounted) finalFetchers.set(key, getDoneFetcher(actionResult.data));
@@ -941,26 +1020,36 @@ function createRouter(init) {
941
1020
  isRevalidationRequired = false;
942
1021
  }
943
1022
  }
944
- async function handleFetcherLoader(key, routeId, path, matches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission) {
1023
+ async function handleFetcherLoader(key, loadMatch, matches, scopedContext, flushSync, preventScrollReset, submission) {
1024
+ let { routeId, path } = loadMatch;
945
1025
  let existingFetcher = state.fetchers.get(key);
946
1026
  updateFetcherState(key, getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : void 0), { flushSync });
947
1027
  let abortController = new AbortController();
1028
+ if (future.unstable_customRouteDiscovery) fetchControllers.set(key, abortController);
948
1029
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
949
- if (isFogOfWar) {
1030
+ if (loadMatch.isDiscovering) {
950
1031
  let discoverResult = await discoverRoutes(matches, new URL(fetchRequest.url).pathname, fetchRequest.signal, key);
951
- if (discoverResult.type === "aborted") return;
952
- else if (discoverResult.type === "error") {
1032
+ if (future.unstable_customRouteDiscovery && fetchRequest.signal.aborted || discoverResult.type === "aborted") return;
1033
+ else if (discoverResult.type === "cancelled") {
1034
+ fetchControllers.delete(key);
1035
+ fetchLoadMatches.delete(key);
1036
+ updateFetcherState(key, getDoneFetcher(existingFetcher?.data), { flushSync });
1037
+ return;
1038
+ } else if (discoverResult.type === "error") {
953
1039
  setFetcherError(key, routeId, discoverResult.error, { flushSync });
954
1040
  return;
955
1041
  } else if (!discoverResult.matches) {
956
1042
  setFetcherError(key, routeId, getInternalRouterError(404, { pathname: path }), { flushSync });
957
1043
  return;
958
- } else matches = discoverResult.matches;
1044
+ } else {
1045
+ matches = discoverResult.matches;
1046
+ loadMatch.isDiscovering = false;
1047
+ }
959
1048
  }
960
1049
  let match = getTargetMatch(matches, path);
961
1050
  fetchControllers.set(key, abortController);
962
1051
  let originatingLoadId = incrementingLoadId;
963
- let results = await callDataStrategy(fetchRequest, path, getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, matches, match, hydrationRouteProperties, scopedContext), scopedContext, key);
1052
+ let results = await callDataStrategy(fetchRequest, path, getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, matches, match, hydrationRouteProperties, scopedContext), scopedContext, key, "fetcher");
964
1053
  let result = results[match.route.id];
965
1054
  if (!result) {
966
1055
  for (let match of matches) if (results[match.route.id]) {
@@ -979,7 +1068,7 @@ function createRouter(init) {
979
1068
  return;
980
1069
  } else {
981
1070
  fetchRedirectIds.add(key);
982
- await startRedirectNavigation(fetchRequest, result, false, { preventScrollReset });
1071
+ await startRedirectNavigation(fetchRequest, result, false, "fetcher", { preventScrollReset });
983
1072
  return;
984
1073
  }
985
1074
  if (isErrorResult(result)) {
@@ -1007,7 +1096,7 @@ function createRouter(init) {
1007
1096
  * actually touch history until we've processed redirects, so we just use
1008
1097
  * the history action from the original navigation (PUSH or REPLACE).
1009
1098
  */
1010
- async function startRedirectNavigation(request, redirect, isNavigation, { submission, fetcherSubmission, preventScrollReset, replace } = {}) {
1099
+ async function startRedirectNavigation(request, redirect, isNavigation, dataStrategyInitiator, { submission, fetcherSubmission, preventScrollReset, replace } = {}) {
1011
1100
  if (!isNavigation) {
1012
1101
  pendingPopstateNavigationDfd?.resolve();
1013
1102
  pendingPopstateNavigationDfd = null;
@@ -1044,20 +1133,22 @@ function createRouter(init) {
1044
1133
  formAction: location
1045
1134
  },
1046
1135
  preventScrollReset: preventScrollReset || pendingPreventScrollReset,
1047
- enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0
1136
+ enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0,
1137
+ dataStrategyInitiator
1048
1138
  });
1049
1139
  else await startNavigation(redirectNavigationType, redirectLocation, {
1050
1140
  overrideNavigation: getLoadingNavigation(redirectLocation, [], redirectNavigationType, submission),
1051
1141
  fetcherSubmission,
1052
1142
  preventScrollReset: preventScrollReset || pendingPreventScrollReset,
1053
- enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0
1143
+ enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0,
1144
+ dataStrategyInitiator
1054
1145
  });
1055
1146
  }
1056
- async function callDataStrategy(request, path, matches, scopedContext, fetcherKey) {
1147
+ async function callDataStrategy(request, path, matches, scopedContext, fetcherKey, initiator, navigationType = null) {
1057
1148
  let results;
1058
1149
  let dataResults = {};
1059
1150
  try {
1060
- results = await callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, false);
1151
+ results = await callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, initiator, scopedContext, false, navigationType);
1061
1152
  } catch (e) {
1062
1153
  matches.filter((m) => m.shouldLoad).forEach((m) => {
1063
1154
  dataResults[m.route.id] = {
@@ -1084,11 +1175,11 @@ function createRouter(init) {
1084
1175
  } else dataResults[routeId] = await convertDataStrategyResultToDataResult(result);
1085
1176
  return dataResults;
1086
1177
  }
1087
- async function callLoadersAndMaybeResolveData(matches, fetchersToLoad, request, location, scopedContext) {
1088
- let loaderResultsPromise = callDataStrategy(request, location, matches, scopedContext, null);
1178
+ async function callLoadersAndMaybeResolveData(matches, fetchersToLoad, request, location, scopedContext, initiator, navigationType = null) {
1179
+ let loaderResultsPromise = callDataStrategy(request, location, matches, scopedContext, null, initiator, navigationType);
1089
1180
  let fetcherResultsPromise = Promise.all(fetchersToLoad.map(async (f) => {
1090
1181
  if (f.matches && f.match && f.request && f.controller) {
1091
- let result = (await callDataStrategy(f.request, f.path, f.matches, scopedContext, f.key))[f.match.route.id];
1182
+ let result = (await callDataStrategy(f.request, f.path, f.matches, scopedContext, f.key, initiator, navigationType))[f.match.route.id];
1092
1183
  return { [f.key]: result };
1093
1184
  } else return Promise.resolve({ [f.key]: {
1094
1185
  type: "error",
@@ -1102,7 +1193,8 @@ function createRouter(init) {
1102
1193
  }
1103
1194
  function interruptActiveLoads() {
1104
1195
  isRevalidationRequired = true;
1105
- fetchLoadMatches.forEach((_, key) => {
1196
+ fetchLoadMatches.forEach((loadMatch, key) => {
1197
+ if (future.unstable_customRouteDiscovery && loadMatch.isDiscovering) return;
1106
1198
  if (fetchControllers.has(key)) cancelledFetcherLoads.add(key);
1107
1199
  abortFetcher(key);
1108
1200
  });
@@ -1128,6 +1220,8 @@ function createRouter(init) {
1128
1220
  }
1129
1221
  function resetFetcher(key, opts) {
1130
1222
  abortFetcher(key, opts?.reason);
1223
+ fetchLoadMatches.delete(key);
1224
+ cancelledFetcherLoads.delete(key);
1131
1225
  updateFetcherState(key, getDoneFetcher(null));
1132
1226
  }
1133
1227
  function deleteFetcher(fetchers, key) {
@@ -1264,16 +1358,15 @@ function createRouter(init) {
1264
1358
  }
1265
1359
  return null;
1266
1360
  }
1267
- function checkFogOfWar(matches, routesToUse, pathname) {
1361
+ function checkFogOfWar(matches, pathname) {
1268
1362
  if (init.patchRoutesOnNavigation) {
1269
- let activeBranches = dataRoutes.branches;
1270
1363
  if (!matches) return {
1271
1364
  active: true,
1272
- matches: matchRoutesImpl(routesToUse, pathname, basename, true, activeBranches) || []
1365
+ matches: dataRouteMatcher.match(pathname, true) || []
1273
1366
  };
1274
1367
  else if (Object.keys(matches[0].params).length > 0) return {
1275
1368
  active: true,
1276
- matches: matchRoutesImpl(routesToUse, pathname, basename, true, activeBranches)
1369
+ matches: dataRouteMatcher.match(pathname, true)
1277
1370
  };
1278
1371
  }
1279
1372
  return {
@@ -1301,6 +1394,10 @@ function createRouter(init) {
1301
1394
  }
1302
1395
  });
1303
1396
  } catch (e) {
1397
+ if (future.unstable_customRouteDiscovery) {
1398
+ if (signal.aborted) return { type: "aborted" };
1399
+ if (e instanceof DiscoveryCancelledError) return { type: "cancelled" };
1400
+ }
1304
1401
  return {
1305
1402
  type: "error",
1306
1403
  error: e,
@@ -1308,21 +1405,20 @@ function createRouter(init) {
1308
1405
  };
1309
1406
  }
1310
1407
  if (signal.aborted) return { type: "aborted" };
1311
- let activeBranches = dataRoutes.branches;
1312
- let newMatches = matchRoutesImpl(dataRoutes.activeRoutes, pathname, basename, false, activeBranches);
1408
+ let newMatches = dataRouteMatcher.match(pathname);
1313
1409
  let newPartialMatches = null;
1314
1410
  if (newMatches) if (Object.keys(newMatches[0].params).length === 0) return {
1315
1411
  type: "success",
1316
1412
  matches: newMatches
1317
1413
  };
1318
1414
  else {
1319
- newPartialMatches = matchRoutesImpl(dataRoutes.activeRoutes, pathname, basename, true, activeBranches);
1415
+ newPartialMatches = dataRouteMatcher.match(pathname, true);
1320
1416
  if (!(newPartialMatches && partialMatches.length < newPartialMatches.length && compareMatches(partialMatches, newPartialMatches.slice(0, partialMatches.length)))) return {
1321
1417
  type: "success",
1322
1418
  matches: newMatches
1323
1419
  };
1324
1420
  }
1325
- if (!newPartialMatches) newPartialMatches = matchRoutesImpl(dataRoutes.activeRoutes, pathname, basename, true, activeBranches);
1421
+ if (!newPartialMatches) newPartialMatches = dataRouteMatcher.match(pathname, true);
1326
1422
  if (!newPartialMatches || compareMatches(partialMatches, newPartialMatches)) return {
1327
1423
  type: "success",
1328
1424
  matches: null
@@ -1354,8 +1450,8 @@ function createRouter(init) {
1354
1450
  get routes() {
1355
1451
  return dataRoutes.stableRoutes;
1356
1452
  },
1357
- get branches() {
1358
- return dataRoutes.branches;
1453
+ match(locationArg) {
1454
+ return dataRouteMatcher.match(locationArg);
1359
1455
  },
1360
1456
  get manifest() {
1361
1457
  return manifest;
@@ -1424,7 +1520,7 @@ function createStaticHandler(routes, opts) {
1424
1520
  let basename = (opts ? opts.basename : null) || "/";
1425
1521
  let _mapRouteProperties = opts?.mapRouteProperties;
1426
1522
  let mapRouteProperties = _mapRouteProperties ? _mapRouteProperties : () => ({});
1427
- ({ ...opts?.future });
1523
+ let dataRouteMatcher = createDataRouteMatcher({ ...opts?.future }, basename);
1428
1524
  if (opts?.instrumentations) {
1429
1525
  let instrumentations = opts.instrumentations;
1430
1526
  mapRouteProperties = (route) => {
@@ -1435,7 +1531,8 @@ function createStaticHandler(routes, opts) {
1435
1531
  };
1436
1532
  }
1437
1533
  let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, void 0, manifest);
1438
- let routeBranches = flattenAndRankRoutes(dataRoutes);
1534
+ dataRouteMatcher.update(dataRoutes);
1535
+ let match = (locationArg) => dataRouteMatcher.match(locationArg);
1439
1536
  /**
1440
1537
  * The query() method is intended for document requests, in which we want to
1441
1538
  * call an optional action and potentially multiple loaders for all nested
@@ -1466,7 +1563,7 @@ function createStaticHandler(routes, opts) {
1466
1563
  let normalizePathImpl = normalizePath || defaultNormalizePath;
1467
1564
  let method = request.method;
1468
1565
  let location = createLocation("", normalizePathImpl(request), null, "default");
1469
- let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
1566
+ let matches = dataRouteMatcher.match(location);
1470
1567
  requestContext = requestContext != null ? requestContext : new RouterContextProvider();
1471
1568
  if (!isValidMethod(method) && method !== "HEAD") {
1472
1569
  let error = getInternalRouterError(405, { method });
@@ -1480,7 +1577,8 @@ function createStaticHandler(routes, opts) {
1480
1577
  errors: { [route.id]: error },
1481
1578
  statusCode: error.status,
1482
1579
  loaderHeaders: {},
1483
- actionHeaders: {}
1580
+ actionHeaders: {},
1581
+ _match: match
1484
1582
  };
1485
1583
  return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
1486
1584
  } else if (!matches) {
@@ -1495,7 +1593,8 @@ function createStaticHandler(routes, opts) {
1495
1593
  errors: { [route.id]: error },
1496
1594
  statusCode: error.status,
1497
1595
  loaderHeaders: {},
1498
- actionHeaders: {}
1596
+ actionHeaders: {},
1597
+ _match: match
1499
1598
  };
1500
1599
  return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
1501
1600
  }
@@ -1518,7 +1617,8 @@ function createStaticHandler(routes, opts) {
1518
1617
  renderedStaticContext = {
1519
1618
  location,
1520
1619
  basename,
1521
- ...result
1620
+ ...result,
1621
+ _match: match
1522
1622
  };
1523
1623
  return renderedStaticContext;
1524
1624
  });
@@ -1544,7 +1644,8 @@ function createStaticHandler(routes, opts) {
1544
1644
  errors: { [skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, matches.find((m) => m.route.id === routeId || m.route.loader)?.route.id || routeId).route.id]: error },
1545
1645
  statusCode: isRouteErrorResponse(error) ? error.status : 500,
1546
1646
  actionHeaders: {},
1547
- loaderHeaders: {}
1647
+ loaderHeaders: {},
1648
+ _match: match
1548
1649
  };
1549
1650
  return generateMiddlewareResponse(() => Promise.resolve(staticContext));
1550
1651
  }
@@ -1561,7 +1662,8 @@ function createStaticHandler(routes, opts) {
1561
1662
  return {
1562
1663
  location,
1563
1664
  basename,
1564
- ...result
1665
+ ...result,
1666
+ _match: match
1565
1667
  };
1566
1668
  }
1567
1669
  /**
@@ -1594,7 +1696,7 @@ function createStaticHandler(routes, opts) {
1594
1696
  let normalizePathImpl = normalizePath || defaultNormalizePath;
1595
1697
  let method = request.method;
1596
1698
  let location = createLocation("", normalizePathImpl(request), null, "default");
1597
- let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
1699
+ let matches = dataRouteMatcher.match(location);
1598
1700
  requestContext = requestContext != null ? requestContext : new RouterContextProvider();
1599
1701
  if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") throw getInternalRouterError(405, { method });
1600
1702
  else if (!matches) throw getInternalRouterError(404, { pathname: location.pathname });
@@ -1756,7 +1858,7 @@ function createStaticHandler(routes, opts) {
1756
1858
  };
1757
1859
  }
1758
1860
  async function callDataStrategy(request, location, matches, isRouteRequest, requestContext, dataStrategy) {
1759
- let results = await callDataStrategyImpl(dataStrategy || defaultDataStrategy, request, location, matches, null, requestContext, true);
1861
+ let results = await callDataStrategyImpl(dataStrategy || defaultDataStrategy, request, location, matches, null, "static", requestContext, true);
1760
1862
  let dataResults = {};
1761
1863
  await Promise.all(matches.map(async (match) => {
1762
1864
  if (!(match.route.id in results)) return;
@@ -1775,7 +1877,7 @@ function createStaticHandler(routes, opts) {
1775
1877
  }
1776
1878
  return {
1777
1879
  dataRoutes,
1778
- _internalRouteBranches: routeBranches,
1880
+ match,
1779
1881
  query,
1780
1882
  queryRoute
1781
1883
  };
@@ -1936,7 +2038,7 @@ function normalizeNavigateOptions(isFetcher, path, opts) {
1936
2038
  submission
1937
2039
  };
1938
2040
  }
1939
- function getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, history, state, matches, submission, location, lazyRoutePropertiesToSkip, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, routesToUse, basename, hasPatchRoutesOnNavigation, branches, pendingActionResult, callSiteDefaultShouldRevalidate) {
2041
+ function getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, history, state, matches, submission, location, lazyRoutePropertiesToSkip, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, dataRouteMatcher, pendingActionResult, callSiteDefaultShouldRevalidate) {
1940
2042
  let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : void 0;
1941
2043
  let currentUrl = history.createURL(state.location);
1942
2044
  let nextUrl = history.createURL(location);
@@ -1986,12 +2088,11 @@ function getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest,
1986
2088
  });
1987
2089
  let revalidatingFetchers = [];
1988
2090
  fetchLoadMatches.forEach((f, key) => {
1989
- if (initialHydration || !matches.some((m) => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) return;
2091
+ if (initialHydration || !matches.some((m) => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key) || f.isDiscovering) return;
1990
2092
  let fetcher = state.fetchers.get(key);
1991
2093
  let isMidInitialLoad = fetcher && fetcher.state !== "idle" && fetcher.data === void 0;
1992
- let fetcherMatches = matchRoutesImpl(routesToUse, f.path, basename ?? "/", false, branches);
2094
+ let fetcherMatches = dataRouteMatcher.match(f.path);
1993
2095
  if (!fetcherMatches) {
1994
- if (hasPatchRoutesOnNavigation && isMidInitialLoad) return;
1995
2096
  revalidatingFetchers.push({
1996
2097
  key,
1997
2098
  routeId: f.routeId,
@@ -2398,7 +2499,7 @@ function getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, p
2398
2499
  return getDataStrategyMatch(mapRouteProperties, manifest, request, path, getRoutePattern(matches), match, lazyRoutePropertiesToSkip, scopedContext, true, shouldRevalidateArgs);
2399
2500
  });
2400
2501
  }
2401
- async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, isStaticHandler) {
2502
+ async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, initiator, scopedContext, isStaticHandler, navigationType = null) {
2402
2503
  if (matches.some((m) => m._lazyPromises?.middleware)) await Promise.all(matches.map((m) => m._lazyPromises?.middleware));
2403
2504
  let dataStrategyArgs = {
2404
2505
  request,
@@ -2406,7 +2507,9 @@ async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fe
2406
2507
  pattern: getRoutePattern(matches),
2407
2508
  params: matches[0].params,
2408
2509
  context: scopedContext,
2409
- matches
2510
+ matches,
2511
+ unstable_initiator: initiator,
2512
+ unstable_navigationType: initiator === "navigation" ? navigationType : null
2410
2513
  };
2411
2514
  let runClientMiddleware = isStaticHandler ? () => {
2412
2515
  throw new Error("You cannot call `runClientMiddleware()` from a static handler `dataStrategy`. Middleware is run outside of `dataStrategy` during SSR in order to bubble up the Response. You can enable middleware via the `respond` API in `query`/`queryRoute`");
@@ -2717,12 +2820,16 @@ function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
2717
2820
  merged[k] = v;
2718
2821
  return merged;
2719
2822
  }, {});
2823
+ let preservedCount = 0;
2720
2824
  for (let match of matches) {
2721
2825
  let id = match.route.id;
2722
- if (!newLoaderData.hasOwnProperty(id) && loaderData.hasOwnProperty(id) && match.route.loader) mergedLoaderData[id] = loaderData[id];
2826
+ if (!newLoaderData.hasOwnProperty(id) && loaderData.hasOwnProperty(id) && match.route.loader) {
2827
+ mergedLoaderData[id] = loaderData[id];
2828
+ preservedCount++;
2829
+ }
2723
2830
  if (errors && errors.hasOwnProperty(id)) break;
2724
2831
  }
2725
- return mergedLoaderData;
2832
+ return Object.keys(newLoaderData).length === 0 && preservedCount === Object.keys(loaderData).length ? loaderData : mergedLoaderData;
2726
2833
  }
2727
2834
  function getActionDataForCommit(pendingActionResult) {
2728
2835
  if (!pendingActionResult) return {};
@@ -2999,4 +3106,4 @@ function createDeferred() {
2999
3106
  };
3000
3107
  }
3001
3108
  //#endregion
3002
- export { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, createRouter, createStaticHandler, getStaticContextFromError, hasInvalidProtocol, isDataWithResponseInit, isMutationMethod, isRedirectResponse, isRedirectStatusCode, isResponse };
3109
+ export { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, clientErrors, createDataRouteMatcher, createRouter, createStaticHandler, getStaticContextFromError, hasInvalidProtocol, isDataWithResponseInit, isMutationMethod, isRedirectResponse, isRedirectStatusCode, isResponse };