react-router 8.4.0 → 8.5.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (233) hide show
  1. package/CHANGELOG.md +94 -1
  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 +237 -165
  6. package/dist/development/index-react-server.js +69 -33
  7. package/dist/development/index.d.ts +9 -8
  8. package/dist/development/index.js +5 -4
  9. package/dist/development/lib/actions.js +1 -1
  10. package/dist/development/lib/await.js +58 -0
  11. package/dist/development/lib/components.d.ts +41 -5
  12. package/dist/development/lib/components.js +82 -5
  13. package/dist/development/lib/context.d.ts +8 -3
  14. package/dist/development/lib/context.js +1 -1
  15. package/dist/development/lib/dom/dom.d.ts +3 -0
  16. package/dist/development/lib/dom/dom.js +1 -1
  17. package/dist/development/lib/dom/lib.d.ts +34 -9
  18. package/dist/development/lib/dom/lib.js +17 -6
  19. package/dist/development/lib/dom/server.js +2 -2
  20. package/dist/development/lib/dom/ssr/components.js +12 -9
  21. package/dist/development/lib/dom/ssr/data.js +1 -1
  22. package/dist/development/lib/dom/ssr/entry.d.ts +9 -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 +25 -13
  27. package/dist/development/lib/dom/ssr/hydration.js +1 -1
  28. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  29. package/dist/development/lib/dom/ssr/links.js +1 -1
  30. package/dist/development/lib/dom/ssr/markup.js +1 -1
  31. package/dist/development/lib/dom/ssr/route-discovery.d.ts +120 -0
  32. package/dist/development/lib/dom/ssr/route-discovery.js +415 -0
  33. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  34. package/dist/development/lib/dom/ssr/routes-test-stub.js +2 -2
  35. package/dist/development/lib/dom/ssr/routes.d.ts +2 -2
  36. package/dist/development/lib/dom/ssr/routes.js +8 -8
  37. package/dist/development/lib/dom/ssr/server.js +3 -1
  38. package/dist/development/lib/dom/ssr/single-fetch.d.ts +28 -2
  39. package/dist/development/lib/dom/ssr/single-fetch.js +73 -55
  40. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  41. package/dist/development/lib/dom-export/hydrated-router.d.ts +28 -0
  42. package/dist/development/lib/dom-export/hydrated-router.js +32 -12
  43. package/dist/development/lib/errors.js +1 -1
  44. package/dist/development/lib/hooks.d.ts +3 -6
  45. package/dist/development/lib/hooks.js +8 -9
  46. package/dist/development/lib/href.js +1 -1
  47. package/dist/development/lib/router/history.js +1 -1
  48. package/dist/development/lib/router/instrumentation.js +1 -1
  49. package/dist/development/lib/router/matcher-route-pattern.js +1 -1
  50. package/dist/development/lib/router/matcher-route-pattern.preload.js +1 -1
  51. package/dist/development/lib/router/matcher.js +6 -1
  52. package/dist/development/lib/router/navigation.js +1 -1
  53. package/dist/development/lib/router/router.d.ts +8 -0
  54. package/dist/development/lib/router/router.js +213 -91
  55. package/dist/development/lib/router/url.js +1 -1
  56. package/dist/development/lib/router/utils.d.ts +31 -4
  57. package/dist/development/lib/router/utils.js +4 -3
  58. package/dist/development/lib/rsc/browser.js +4 -4
  59. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  60. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  61. package/dist/development/lib/rsc/html-stream/server.js +24 -12
  62. package/dist/development/lib/rsc/route-modules.js +1 -1
  63. package/dist/development/lib/rsc/server.rsc.d.ts +3 -2
  64. package/dist/development/lib/rsc/server.ssr.js +1 -1
  65. package/dist/development/lib/server-runtime/build.d.ts +14 -0
  66. package/dist/development/lib/server-runtime/cookies.js +1 -1
  67. package/dist/development/lib/server-runtime/crypto.js +1 -1
  68. package/dist/development/lib/server-runtime/data.js +1 -1
  69. package/dist/development/lib/server-runtime/dev.js +1 -1
  70. package/dist/development/lib/server-runtime/entry.js +1 -1
  71. package/dist/development/lib/server-runtime/errors.js +1 -1
  72. package/dist/development/lib/server-runtime/headers.js +1 -1
  73. package/dist/development/lib/server-runtime/invariant.js +1 -1
  74. package/dist/development/lib/server-runtime/mode.js +1 -1
  75. package/dist/development/lib/server-runtime/routeMatching.js +1 -1
  76. package/dist/development/lib/server-runtime/routes.d.ts +1 -1
  77. package/dist/development/lib/server-runtime/routes.js +1 -1
  78. package/dist/development/lib/server-runtime/server.js +24 -8
  79. package/dist/development/lib/server-runtime/serverHandoff.d.ts +17 -0
  80. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  81. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
  82. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
  83. package/dist/development/lib/server-runtime/sessions.js +2 -2
  84. package/dist/development/lib/server-runtime/single-fetch.js +1 -1
  85. package/dist/development/lib/server-runtime/urls.js +1 -1
  86. package/dist/development/lib/server-runtime/warnings.js +1 -1
  87. package/dist/development/lib/types/internal.js +1 -1
  88. package/dist/development/lib/types/route-data.d.ts +2 -2
  89. package/dist/development/lib/types/route-module-annotations.d.ts +1 -1
  90. package/dist/development/route-pattern.js +1 -1
  91. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  92. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  93. package/dist/development/vendor/turbo-stream-v2/unflatten.js +23 -3
  94. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  95. package/dist/production/dom-export.d.ts +2 -1
  96. package/dist/production/dom-export.js +1 -1
  97. package/dist/production/index-react-server-client.js +2 -2
  98. package/dist/production/index-react-server.d.ts +237 -165
  99. package/dist/production/index-react-server.js +69 -33
  100. package/dist/production/index.d.ts +9 -8
  101. package/dist/production/index.js +5 -4
  102. package/dist/production/lib/actions.js +1 -1
  103. package/dist/production/lib/await.js +58 -0
  104. package/dist/production/lib/components.d.ts +41 -5
  105. package/dist/production/lib/components.js +82 -5
  106. package/dist/production/lib/context.d.ts +8 -3
  107. package/dist/production/lib/context.js +1 -1
  108. package/dist/production/lib/dom/dom.d.ts +3 -0
  109. package/dist/production/lib/dom/dom.js +1 -1
  110. package/dist/production/lib/dom/lib.d.ts +34 -9
  111. package/dist/production/lib/dom/lib.js +17 -6
  112. package/dist/production/lib/dom/server.js +2 -2
  113. package/dist/production/lib/dom/ssr/components.js +12 -9
  114. package/dist/production/lib/dom/ssr/data.js +1 -1
  115. package/dist/production/lib/dom/ssr/entry.d.ts +9 -2
  116. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  117. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  118. package/dist/production/lib/dom/ssr/fog-of-war.d.ts +3 -2
  119. package/dist/production/lib/dom/ssr/fog-of-war.js +25 -13
  120. package/dist/production/lib/dom/ssr/hydration.js +1 -1
  121. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  122. package/dist/production/lib/dom/ssr/links.js +1 -1
  123. package/dist/production/lib/dom/ssr/markup.js +1 -1
  124. package/dist/production/lib/dom/ssr/route-discovery.d.ts +120 -0
  125. package/dist/production/lib/dom/ssr/route-discovery.js +415 -0
  126. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  127. package/dist/production/lib/dom/ssr/routes-test-stub.js +2 -2
  128. package/dist/production/lib/dom/ssr/routes.d.ts +2 -2
  129. package/dist/production/lib/dom/ssr/routes.js +8 -8
  130. package/dist/production/lib/dom/ssr/server.js +3 -1
  131. package/dist/production/lib/dom/ssr/single-fetch.d.ts +28 -2
  132. package/dist/production/lib/dom/ssr/single-fetch.js +73 -55
  133. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  134. package/dist/production/lib/dom-export/hydrated-router.d.ts +28 -0
  135. package/dist/production/lib/dom-export/hydrated-router.js +32 -12
  136. package/dist/production/lib/errors.js +1 -1
  137. package/dist/production/lib/hooks.d.ts +3 -6
  138. package/dist/production/lib/hooks.js +7 -8
  139. package/dist/production/lib/href.js +1 -1
  140. package/dist/production/lib/router/history.js +1 -1
  141. package/dist/production/lib/router/instrumentation.js +1 -1
  142. package/dist/production/lib/router/matcher-route-pattern.js +1 -1
  143. package/dist/production/lib/router/matcher-route-pattern.preload.js +1 -1
  144. package/dist/production/lib/router/matcher.js +6 -1
  145. package/dist/production/lib/router/navigation.js +1 -1
  146. package/dist/production/lib/router/router.d.ts +8 -0
  147. package/dist/production/lib/router/router.js +213 -91
  148. package/dist/production/lib/router/url.js +1 -1
  149. package/dist/production/lib/router/utils.d.ts +31 -4
  150. package/dist/production/lib/router/utils.js +4 -3
  151. package/dist/production/lib/rsc/browser.js +4 -4
  152. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  153. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  154. package/dist/production/lib/rsc/html-stream/server.js +24 -12
  155. package/dist/production/lib/rsc/route-modules.js +1 -1
  156. package/dist/production/lib/rsc/server.rsc.d.ts +3 -2
  157. package/dist/production/lib/rsc/server.ssr.js +1 -1
  158. package/dist/production/lib/server-runtime/build.d.ts +14 -0
  159. package/dist/production/lib/server-runtime/cookies.js +1 -1
  160. package/dist/production/lib/server-runtime/crypto.js +1 -1
  161. package/dist/production/lib/server-runtime/data.js +1 -1
  162. package/dist/production/lib/server-runtime/dev.js +1 -1
  163. package/dist/production/lib/server-runtime/entry.js +1 -1
  164. package/dist/production/lib/server-runtime/errors.js +1 -1
  165. package/dist/production/lib/server-runtime/headers.js +1 -1
  166. package/dist/production/lib/server-runtime/invariant.js +1 -1
  167. package/dist/production/lib/server-runtime/mode.js +1 -1
  168. package/dist/production/lib/server-runtime/routeMatching.js +1 -1
  169. package/dist/production/lib/server-runtime/routes.d.ts +1 -1
  170. package/dist/production/lib/server-runtime/routes.js +1 -1
  171. package/dist/production/lib/server-runtime/server.js +24 -8
  172. package/dist/production/lib/server-runtime/serverHandoff.d.ts +17 -0
  173. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  174. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
  175. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
  176. package/dist/production/lib/server-runtime/sessions.js +2 -2
  177. package/dist/production/lib/server-runtime/single-fetch.js +1 -1
  178. package/dist/production/lib/server-runtime/urls.js +1 -1
  179. package/dist/production/lib/server-runtime/warnings.js +1 -1
  180. package/dist/production/lib/types/internal.js +1 -1
  181. package/dist/production/lib/types/route-data.d.ts +2 -2
  182. package/dist/production/lib/types/route-module-annotations.d.ts +1 -1
  183. package/dist/production/route-pattern.js +1 -1
  184. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  185. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  186. package/dist/production/vendor/turbo-stream-v2/unflatten.js +23 -3
  187. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  188. package/docs/explanation/backend-for-frontend.md +0 -3
  189. package/docs/explanation/code-splitting.md +0 -3
  190. package/docs/explanation/concurrency.md +0 -3
  191. package/docs/explanation/hot-module-replacement.md +0 -3
  192. package/docs/explanation/lazy-route-discovery.md +4 -3
  193. package/docs/explanation/progressive-enhancement.md +0 -3
  194. package/docs/explanation/race-conditions.md +0 -3
  195. package/docs/explanation/react-transitions.md +0 -3
  196. package/docs/explanation/sessions-and-cookies.md +109 -5
  197. package/docs/explanation/state-management.md +0 -3
  198. package/docs/explanation/styling.md +0 -3
  199. package/docs/explanation/type-safety.md +0 -3
  200. package/docs/how-to/accessibility.md +0 -6
  201. package/docs/how-to/client-data.md +0 -3
  202. package/docs/how-to/custom-fetch.md +218 -0
  203. package/docs/how-to/data-strategy.md +23 -4
  204. package/docs/how-to/error-boundary.md +0 -3
  205. package/docs/how-to/error-reporting.md +0 -3
  206. package/docs/how-to/fetchers.md +0 -3
  207. package/docs/how-to/file-route-conventions.md +0 -3
  208. package/docs/how-to/file-uploads.md +0 -3
  209. package/docs/how-to/form-validation.md +0 -3
  210. package/docs/how-to/headers.md +0 -3
  211. package/docs/how-to/instrumentation.md +0 -3
  212. package/docs/how-to/middleware.md +7 -3
  213. package/docs/how-to/navigation-blocking.md +0 -3
  214. package/docs/how-to/optimize-revalidation.md +2 -5
  215. package/docs/how-to/pre-rendering.md +23 -4
  216. package/docs/how-to/presets.md +0 -3
  217. package/docs/how-to/react-server-components.md +0 -3
  218. package/docs/how-to/resource-routes.md +0 -3
  219. package/docs/how-to/route-discovery.md +147 -0
  220. package/docs/how-to/route-module-type-safety.md +0 -3
  221. package/docs/how-to/security.md +0 -3
  222. package/docs/how-to/server-bundles.md +0 -3
  223. package/docs/how-to/spa.md +2 -3
  224. package/docs/how-to/status.md +0 -3
  225. package/docs/how-to/suspense.md +22 -6
  226. package/docs/how-to/using-handle.md +0 -3
  227. package/docs/how-to/view-transitions.md +79 -15
  228. package/docs/start/framework/data-loading.md +9 -0
  229. package/docs/start/framework/navigating.md +5 -9
  230. package/docs/start/framework/rendering.md +37 -0
  231. package/docs/upgrading/future.md +0 -12
  232. package/docs/upgrading/v7.md +3 -33
  233. package/package.json +9 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.4.0
2
+ * react-router v8.5.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -10,7 +10,7 @@
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, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, 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
15
  import { V6RegExMatcher } from "./matcher.js";
16
16
  import { getRoutePatternMatcher } from "./matcher-route-pattern.preload.js";
@@ -114,6 +114,7 @@ var DataRoutes = class {
114
114
  }
115
115
  }
116
116
  };
117
+ const clientErrors = /* @__PURE__ */ new WeakMap();
117
118
  /**
118
119
  * Create a router and listen to history POP navigations
119
120
  */
@@ -142,7 +143,6 @@ function createRouter(init) {
142
143
  let dataStrategyImpl = init.dataStrategy || defaultDataStrategyWithMiddleware;
143
144
  let unlistenHistory = null;
144
145
  let subscribers = /* @__PURE__ */ new Set();
145
- let bufferedInitialStateUpdate = null;
146
146
  let savedScrollPositions = null;
147
147
  let getScrollRestorationKey = null;
148
148
  let getScrollPosition = null;
@@ -213,6 +213,8 @@ function createRouter(init) {
213
213
  blockers: /* @__PURE__ */ new Map()
214
214
  };
215
215
  let pendingAction = "POP";
216
+ let pendingPopstateDelta = 0;
217
+ let pendingDiscoveryCancellation;
216
218
  let pendingPopstateNavigationDfd = null;
217
219
  let pendingPreventScrollReset = false;
218
220
  let pendingNavigationController;
@@ -273,6 +275,7 @@ function createRouter(init) {
273
275
  pendingPopstateNavigationDfd = null;
274
276
  return;
275
277
  }
278
+ if (future.unstable_customRouteDiscovery) pendingPopstateDelta += delta || 0;
276
279
  return startNavigation(historyAction, location);
277
280
  });
278
281
  if (isBrowser) {
@@ -294,19 +297,16 @@ function createRouter(init) {
294
297
  }
295
298
  function subscribe(fn) {
296
299
  subscribers.add(fn);
297
- if (bufferedInitialStateUpdate) {
298
- let { newErrors } = bufferedInitialStateUpdate;
299
- bufferedInitialStateUpdate = null;
300
- fn(state, {
301
- deletedFetchers: [],
302
- newErrors,
303
- viewTransitionOpts: void 0,
304
- flushSync: false
305
- });
306
- }
307
300
  return () => subscribers.delete(fn);
308
301
  }
309
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
+ }
310
310
  if (newState.matches) newState.matches = newState.matches.map((m) => {
311
311
  let route = manifest[m.route.id];
312
312
  let matchRoute = m.route;
@@ -329,10 +329,9 @@ function createRouter(init) {
329
329
  fetchersQueuedForDeletion.forEach((key) => {
330
330
  if (!state.fetchers.has(key) && !fetchControllers.has(key)) unmountedFetchers.push(key);
331
331
  });
332
- if (subscribers.size === 0) bufferedInitialStateUpdate = { newErrors: newState.errors ?? null };
333
332
  [...subscribers].forEach((subscriber) => subscriber(state, {
334
333
  deletedFetchers: unmountedFetchers,
335
- newErrors: newState.errors ?? null,
334
+ newErrors,
336
335
  viewTransitionOpts: opts.viewTransitionOpts,
337
336
  flushSync: opts.flushSync === true
338
337
  }));
@@ -397,17 +396,22 @@ function createRouter(init) {
397
396
  viewTransitionOpts,
398
397
  flushSync: flushSync === true
399
398
  });
400
- pendingAction = "POP";
401
- pendingPreventScrollReset = false;
402
- pendingViewTransitionEnabled = false;
403
- isUninterruptedRevalidation = false;
404
- isRevalidationRequired = false;
399
+ resetPendingNavigation();
405
400
  pendingPopstateNavigationDfd?.resolve();
406
401
  pendingPopstateNavigationDfd = null;
407
402
  pendingRevalidationDfd?.resolve();
408
403
  pendingRevalidationDfd = null;
409
404
  }
405
+ function resetPendingNavigation() {
406
+ pendingPopstateDelta = 0;
407
+ pendingAction = "POP";
408
+ pendingPreventScrollReset = false;
409
+ pendingViewTransitionEnabled = false;
410
+ isUninterruptedRevalidation = false;
411
+ isRevalidationRequired = fetchReloadIds.size > 0;
412
+ }
410
413
  async function navigate(to, opts) {
414
+ if (pendingDiscoveryCancellation) await pendingDiscoveryCancellation;
411
415
  pendingPopstateNavigationDfd?.resolve();
412
416
  pendingPopstateNavigationDfd = null;
413
417
  if (typeof to === "number") {
@@ -442,8 +446,9 @@ function createRouter(init) {
442
446
  validateNavigationTarget(to == null ? init.history.createHref(state.location) : typeof to === "string" ? to : createPath(to), init.history.createHref(nextLocation.mask || nextLocation), init.history.createURL("/"), "reject");
443
447
  let userReplace = opts && opts.replace != null ? opts.replace : void 0;
444
448
  let historyAction = "PUSH";
449
+ let currentPath = state.location.pathname + state.location.search;
445
450
  if (userReplace === true) historyAction = "REPLACE";
446
- else if (userReplace === false) {} else if (submission != null && isMutationMethod(submission.formMethod) && submission.formAction === state.location.pathname + state.location.search) historyAction = "REPLACE";
451
+ else if (userReplace === false) {} else if (submission != null && isMutationMethod(submission.formMethod) && (submission.formAction === currentPath || nextLocation.pathname + nextLocation.search === currentPath)) historyAction = "REPLACE";
447
452
  let preventScrollReset = opts && "preventScrollReset" in opts ? opts.preventScrollReset === true : void 0;
448
453
  let flushSync = (opts && opts.flushSync) === true;
449
454
  let blockerKey = shouldBlockNavigation({
@@ -484,22 +489,28 @@ function createRouter(init) {
484
489
  });
485
490
  }
486
491
  function revalidate() {
492
+ if (pendingDiscoveryCancellation) return pendingDiscoveryCancellation.then(revalidate);
487
493
  if (!pendingRevalidationDfd) pendingRevalidationDfd = createDeferred();
488
494
  interruptActiveLoads();
489
495
  updateState({ revalidation: "loading" });
490
496
  let promise = pendingRevalidationDfd.promise;
491
497
  if (state.navigation.state === "submitting") return promise;
492
498
  if (state.navigation.state === "idle") {
493
- startNavigation(state.historyAction, state.location, { startUninterruptedRevalidation: true });
499
+ startNavigation(state.historyAction, state.location, {
500
+ startUninterruptedRevalidation: true,
501
+ dataStrategyInitiator: "revalidation"
502
+ });
494
503
  return promise;
495
504
  }
496
505
  startNavigation(pendingAction || state.historyAction, state.navigation.location, {
497
506
  overrideNavigation: state.navigation,
507
+ dataStrategyInitiator: "revalidation",
498
508
  enableViewTransition: pendingViewTransitionEnabled === true
499
509
  });
500
510
  return promise;
501
511
  }
502
512
  async function startNavigation(historyAction, location, opts) {
513
+ if (pendingDiscoveryCancellation) await pendingDiscoveryCancellation;
503
514
  pendingNavigationController && pendingNavigationController.abort();
504
515
  pendingNavigationController = null;
505
516
  pendingAction = historyAction;
@@ -535,6 +546,7 @@ function createRouter(init) {
535
546
  } : void 0;
536
547
  pendingNavigationController = new AbortController();
537
548
  let request = createClientSideRequest(init.history, location, pendingNavigationController.signal, opts && opts.submission);
549
+ let dataStrategyInitiator = opts?.initialHydration ? "initialization" : opts?.dataStrategyInitiator ?? "navigation";
538
550
  let scopedContext = init.getContext ? await init.getContext() : new RouterContextProvider();
539
551
  let pendingActionResult;
540
552
  if (opts && opts.pendingError) pendingActionResult = [findNearestBoundary(matches).route.id, {
@@ -542,7 +554,7 @@ function createRouter(init) {
542
554
  error: opts.pendingError
543
555
  }];
544
556
  else if (opts && opts.submission && isMutationMethod(opts.submission.formMethod)) {
545
- let actionResult = await handleAction(request, location, opts.submission, matches, historyAction, scopedContext, fogOfWar.active, opts && opts.initialHydration === true, {
557
+ let actionResult = await handleAction(request, dataStrategyInitiator, location, opts.submission, matches, historyAction, scopedContext, fogOfWar.active, opts && opts.initialHydration === true, {
546
558
  replace: opts.replace,
547
559
  flushSync
548
560
  });
@@ -566,7 +578,7 @@ function createRouter(init) {
566
578
  fogOfWar.active = false;
567
579
  request = createClientSideRequest(init.history, request.url, request.signal);
568
580
  }
569
- 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);
581
+ 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);
570
582
  if (shortCircuited) return;
571
583
  pendingNavigationController = null;
572
584
  completeNavigation(location, {
@@ -577,13 +589,57 @@ function createRouter(init) {
577
589
  ...workingFetchers ? { fetchers: workingFetchers } : {}
578
590
  });
579
591
  }
580
- async function handleAction(request, location, submission, matches, historyAction, scopedContext, isFogOfWar, initialHydration, opts = {}) {
581
- interruptActiveLoads();
592
+ function cancelDiscoveryNavigation() {
593
+ let cancellation = createDeferred();
594
+ pendingDiscoveryCancellation = cancellation.promise;
595
+ let delta = pendingPopstateDelta;
596
+ let popstateNavigationDfd = pendingPopstateNavigationDfd;
597
+ pendingPopstateNavigationDfd = null;
598
+ pendingNavigationController?.abort();
599
+ pendingNavigationController = null;
600
+ resetPendingNavigation();
601
+ pendingRevalidationDfd?.resolve();
602
+ pendingRevalidationDfd = null;
603
+ let blockers = new Map(state.blockers);
604
+ blockers.forEach((blocker, key) => {
605
+ if (blocker.state === "proceeding") blockers.set(key, IDLE_BLOCKER);
606
+ });
607
+ let fetchers = new Map(state.fetchers);
608
+ let didUpdateFetchers = markFetchRedirectsDone(fetchers);
609
+ fetchers.forEach((fetcher, key) => {
610
+ if (fetcher.state === "loading" && !fetchControllers.has(key)) {
611
+ fetchers.set(key, getDoneFetcher(fetcher.data));
612
+ didUpdateFetchers = true;
613
+ }
614
+ });
615
+ cancelledFetcherLoads.clear();
616
+ updateState({
617
+ navigation: IDLE_NAVIGATION,
618
+ revalidation: "idle",
619
+ blockers,
620
+ ...didUpdateFetchers ? { fetchers } : {}
621
+ });
622
+ function finishCancellation() {
623
+ pendingDiscoveryCancellation = void 0;
624
+ popstateNavigationDfd?.resolve();
625
+ cancellation.resolve();
626
+ }
627
+ if (delta) {
628
+ unblockBlockerHistoryUpdate = finishCancellation;
629
+ init.history.go(-delta);
630
+ } else finishCancellation();
631
+ return cancellation.promise;
632
+ }
633
+ async function handleAction(request, dataStrategyInitiator, location, submission, matches, historyAction, scopedContext, isFogOfWar, initialHydration, opts = {}) {
634
+ if (!future.unstable_customRouteDiscovery) interruptActiveLoads();
582
635
  updateState({ navigation: getSubmittingNavigation(location, matches, historyAction, submission) }, { flushSync: opts.flushSync === true });
583
636
  if (isFogOfWar) {
584
637
  let discoverResult = await discoverRoutes(matches, location.pathname, request.signal);
585
- if (discoverResult.type === "aborted") return { shortCircuited: true };
586
- else if (discoverResult.type === "error") {
638
+ if (future.unstable_customRouteDiscovery && request.signal.aborted || discoverResult.type === "aborted") return { shortCircuited: true };
639
+ else if (discoverResult.type === "cancelled") {
640
+ await cancelDiscoveryNavigation();
641
+ return { shortCircuited: true };
642
+ } else if (discoverResult.type === "error") {
587
643
  if (discoverResult.partialMatches.length === 0) {
588
644
  let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
589
645
  return {
@@ -613,6 +669,7 @@ function createRouter(init) {
613
669
  };
614
670
  } else matches = discoverResult.matches;
615
671
  }
672
+ if (future.unstable_customRouteDiscovery) interruptActiveLoads();
616
673
  let result;
617
674
  let actionMatch = getTargetMatch(matches, location);
618
675
  if (!actionMatch.route.action && !actionMatch.route.lazy) result = {
@@ -624,7 +681,7 @@ function createRouter(init) {
624
681
  })
625
682
  };
626
683
  else {
627
- let results = await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, initialHydration ? [] : hydrationRouteProperties, scopedContext), scopedContext, null);
684
+ let results = await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, initialHydration ? [] : hydrationRouteProperties, scopedContext), scopedContext, null, dataStrategyInitiator, historyAction);
628
685
  result = results[actionMatch.route.id];
629
686
  if (!result) {
630
687
  for (let match of matches) if (results[match.route.id]) {
@@ -638,7 +695,7 @@ function createRouter(init) {
638
695
  let replace;
639
696
  if (opts && opts.replace != null) replace = opts.replace;
640
697
  else replace = normalizeRedirectLocation(result.response.headers.get("Location"), new URL(request.url), basename, init.history) === state.location.pathname + state.location.search;
641
- await startRedirectNavigation(request, result, true, {
698
+ await startRedirectNavigation(request, result, true, dataStrategyInitiator, {
642
699
  submission,
643
700
  replace
644
701
  });
@@ -661,7 +718,7 @@ function createRouter(init) {
661
718
  pendingActionResult: [actionMatch.route.id, result]
662
719
  };
663
720
  }
664
- async function handleLoaders(request, location, matches, historyAction, scopedContext, isFogOfWar, overrideNavigation, submission, fetcherSubmission, replace, initialHydration, flushSync, pendingActionResult, callSiteDefaultShouldRevalidate) {
721
+ async function handleLoaders(request, dataStrategyInitiator, location, matches, historyAction, scopedContext, isFogOfWar, overrideNavigation, submission, fetcherSubmission, replace, initialHydration, flushSync, pendingActionResult, callSiteDefaultShouldRevalidate) {
665
722
  let loadingNavigation = overrideNavigation || getLoadingNavigation(location, matches, historyAction, submission);
666
723
  let activeSubmission = submission || fetcherSubmission || getSubmissionFromNavigation(loadingNavigation);
667
724
  let shouldUpdateNavigationState = !isUninterruptedRevalidation && !initialHydration;
@@ -674,8 +731,11 @@ function createRouter(init) {
674
731
  }, { flushSync });
675
732
  }
676
733
  let discoverResult = await discoverRoutes(matches, location.pathname, request.signal);
677
- if (discoverResult.type === "aborted") return { shortCircuited: true };
678
- else if (discoverResult.type === "error") {
734
+ if (future.unstable_customRouteDiscovery && request.signal.aborted || discoverResult.type === "aborted") return { shortCircuited: true };
735
+ else if (discoverResult.type === "cancelled") {
736
+ await cancelDiscoveryNavigation();
737
+ return { shortCircuited: true };
738
+ } else if (discoverResult.type === "error") {
679
739
  if (discoverResult.partialMatches.length === 0) {
680
740
  let { matches, route } = getShortCircuitMatches(dataRoutes.activeRoutes);
681
741
  return {
@@ -697,7 +757,10 @@ function createRouter(init) {
697
757
  loaderData: {},
698
758
  errors: { [route.id]: error }
699
759
  };
700
- } else matches = discoverResult.matches;
760
+ } else {
761
+ matches = discoverResult.matches;
762
+ if (initialHydration && !request.signal.aborted) updateState({ matches });
763
+ }
701
764
  }
702
765
  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);
703
766
  pendingNavigationLoadId = ++incrementingLoadId;
@@ -727,29 +790,37 @@ function createRouter(init) {
727
790
  abortFetcher(rf.key);
728
791
  if (rf.controller) fetchControllers.set(rf.key, rf.controller);
729
792
  });
730
- let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((f) => abortFetcher(f.key));
793
+ let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => {
794
+ if (fetchControllers.get(rf.key) === rf.controller) abortFetcher(rf.key);
795
+ });
731
796
  if (pendingNavigationController) pendingNavigationController.signal.addEventListener("abort", abortPendingFetchRevalidations);
732
- let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, request, location, scopedContext);
797
+ let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, request, location, scopedContext, dataStrategyInitiator, historyAction);
733
798
  if (request.signal.aborted) return { shortCircuited: true };
734
799
  if (pendingNavigationController) pendingNavigationController.signal.removeEventListener("abort", abortPendingFetchRevalidations);
735
- revalidatingFetchers.forEach((rf) => fetchControllers.delete(rf.key));
800
+ revalidatingFetchers.forEach((rf) => {
801
+ if (fetchControllers.get(rf.key) === rf.controller) fetchControllers.delete(rf.key);
802
+ });
736
803
  let redirect = findRedirect(loaderResults);
737
804
  if (redirect) {
738
- await startRedirectNavigation(request, redirect.result, true, { replace });
805
+ await startRedirectNavigation(request, redirect.result, true, dataStrategyInitiator, { replace });
739
806
  return { shortCircuited: true };
740
807
  }
741
808
  redirect = findRedirect(fetcherResults);
742
809
  if (redirect) {
743
810
  fetchRedirectIds.add(redirect.key);
744
- await startRedirectNavigation(request, redirect.result, true, { replace });
811
+ await startRedirectNavigation(request, redirect.result, true, dataStrategyInitiator, { replace });
745
812
  return { shortCircuited: true };
746
813
  }
747
814
  let workingFetchers = new Map(state.fetchers);
748
815
  let { loaderData, errors } = processLoaderData(state, matches, loaderResults, pendingActionResult, revalidatingFetchers, fetcherResults, workingFetchers);
749
- if (initialHydration && state.errors) errors = {
750
- ...state.errors,
751
- ...errors
752
- };
816
+ if (initialHydration && state.errors) {
817
+ let newClientErrors = errors ?? null;
818
+ errors = {
819
+ ...state.errors,
820
+ ...errors
821
+ };
822
+ clientErrors.set(errors, newClientErrors);
823
+ }
753
824
  let didUpdateFetcherRedirects = markFetchRedirectsDone(workingFetchers);
754
825
  let didAbortFetchLoads = abortStaleFetchLoads(pendingNavigationLoadId, workingFetchers);
755
826
  let shouldUpdateFetchers = didUpdateFetcherRedirects || didAbortFetchLoads || revalidatingFetchers.length > 0;
@@ -775,6 +846,7 @@ function createRouter(init) {
775
846
  return workingFetchers;
776
847
  }
777
848
  async function fetch(key, routeId, href, opts) {
849
+ if (pendingDiscoveryCancellation) await pendingDiscoveryCancellation;
778
850
  abortFetcher(key);
779
851
  let flushSync = (opts && opts.flushSync) === true;
780
852
  let instrumentationResultMetaReceiver = consumeInstrumentationClientResultMetaReceiver(router);
@@ -807,15 +879,22 @@ function createRouter(init) {
807
879
  await handleFetcherLoader(key, loadMatch, matches, scopedContext, flushSync, preventScrollReset, submission);
808
880
  }
809
881
  async function handleFetcherAction(key, routeId, path, requestMatches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission, callSiteDefaultShouldRevalidate) {
810
- interruptActiveLoads();
882
+ if (!future.unstable_customRouteDiscovery) interruptActiveLoads();
811
883
  fetchLoadMatches.delete(key);
812
- updateFetcherState(key, getSubmittingFetcher(submission, state.fetchers.get(key)), { flushSync });
884
+ let existingFetcher = state.fetchers.get(key);
885
+ updateFetcherState(key, getSubmittingFetcher(submission, existingFetcher), { flushSync });
813
886
  let abortController = new AbortController();
887
+ if (future.unstable_customRouteDiscovery) fetchControllers.set(key, abortController);
814
888
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal, submission);
815
889
  if (isFogOfWar) {
816
890
  let discoverResult = await discoverRoutes(requestMatches, new URL(fetchRequest.url).pathname, fetchRequest.signal, key);
817
- if (discoverResult.type === "aborted") return;
818
- else if (discoverResult.type === "error") {
891
+ if (future.unstable_customRouteDiscovery && fetchRequest.signal.aborted || discoverResult.type === "aborted") return;
892
+ else if (discoverResult.type === "cancelled") {
893
+ fetchControllers.delete(key);
894
+ fetchLoadMatches.delete(key);
895
+ updateFetcherState(key, getDoneFetcher(existingFetcher?.data), { flushSync });
896
+ return;
897
+ } else if (discoverResult.type === "error") {
819
898
  setFetcherError(key, routeId, discoverResult.error, { flushSync });
820
899
  return;
821
900
  } else if (!discoverResult.matches) {
@@ -832,10 +911,11 @@ function createRouter(init) {
832
911
  }), { flushSync });
833
912
  return;
834
913
  }
914
+ if (future.unstable_customRouteDiscovery) interruptActiveLoads();
835
915
  fetchControllers.set(key, abortController);
836
916
  let originatingLoadId = incrementingLoadId;
837
917
  let fetchMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, requestMatches, match, hydrationRouteProperties, scopedContext);
838
- let actionResults = await callDataStrategy(fetchRequest, path, fetchMatches, scopedContext, key);
918
+ let actionResults = await callDataStrategy(fetchRequest, path, fetchMatches, scopedContext, key, "fetcher");
839
919
  let actionResult = actionResults[match.route.id];
840
920
  if (!actionResult) {
841
921
  for (let match of fetchMatches) if (actionResults[match.route.id]) {
@@ -848,30 +928,39 @@ function createRouter(init) {
848
928
  return;
849
929
  }
850
930
  if (fetchersQueuedForDeletion.has(key)) {
851
- if (isRedirectResult(actionResult) || isErrorResult(actionResult)) {
931
+ if (isErrorResult(actionResult)) {
852
932
  updateFetcherState(key, getDoneFetcher(void 0));
853
933
  return;
854
934
  }
855
- } else {
856
935
  if (isRedirectResult(actionResult)) {
936
+ isRevalidationRequired = true;
937
+ actionResult = {
938
+ type: "data",
939
+ data: void 0
940
+ };
941
+ }
942
+ } else {
943
+ if (isRedirectResult(actionResult)) if (pendingNavigationLoadId > originatingLoadId) {
944
+ isRevalidationRequired = true;
945
+ actionResult = {
946
+ type: "data",
947
+ data: void 0
948
+ };
949
+ } else {
857
950
  fetchControllers.delete(key);
858
- if (pendingNavigationLoadId > originatingLoadId) {
859
- updateFetcherState(key, getDoneFetcher(void 0));
860
- return;
861
- } else {
862
- fetchRedirectIds.add(key);
863
- updateFetcherState(key, getLoadingFetcher(submission));
864
- return startRedirectNavigation(fetchRequest, actionResult, false, {
865
- fetcherSubmission: submission,
866
- preventScrollReset
867
- });
868
- }
951
+ fetchRedirectIds.add(key);
952
+ updateFetcherState(key, getLoadingFetcher(submission));
953
+ return startRedirectNavigation(fetchRequest, actionResult, false, "fetcher", {
954
+ fetcherSubmission: submission,
955
+ preventScrollReset
956
+ });
869
957
  }
870
958
  if (isErrorResult(actionResult)) {
871
959
  setFetcherError(key, routeId, actionResult.error);
872
960
  return;
873
961
  }
874
962
  }
963
+ isRevalidationRequired = true;
875
964
  let nextLocation = state.navigation.location || state.location;
876
965
  let revalidationRequest = createClientSideRequest(init.history, nextLocation, abortController.signal);
877
966
  let matches = state.navigation.state !== "idle" ? dataRouteMatcher.match(state.navigation.location) : state.matches;
@@ -891,9 +980,11 @@ function createRouter(init) {
891
980
  if (rf.controller) fetchControllers.set(staleKey, rf.controller);
892
981
  });
893
982
  updateState({ fetchers: workingFetchers });
894
- let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => abortFetcher(rf.key));
983
+ let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => {
984
+ if (fetchControllers.get(rf.key) === rf.controller) abortFetcher(rf.key);
985
+ });
895
986
  abortController.signal.addEventListener("abort", abortPendingFetchRevalidations);
896
- let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, revalidationRequest, nextLocation, scopedContext);
987
+ let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, revalidationRequest, nextLocation, scopedContext, "fetcher");
897
988
  if (abortController.signal.aborted) {
898
989
  if (fetchReloadIds.get(key) === loadId) fetchReloadIds.delete(key);
899
990
  return;
@@ -901,7 +992,9 @@ function createRouter(init) {
901
992
  abortController.signal.removeEventListener("abort", abortPendingFetchRevalidations);
902
993
  fetchReloadIds.delete(key);
903
994
  fetchControllers.delete(key);
904
- revalidatingFetchers.forEach((r) => fetchControllers.delete(r.key));
995
+ revalidatingFetchers.forEach((r) => {
996
+ if (fetchControllers.get(r.key) === r.controller) fetchControllers.delete(r.key);
997
+ });
905
998
  let fetcherIsMounted = state.fetchers.has(key);
906
999
  let getRedirectStateWithDoneFetcher = (s) => {
907
1000
  if (!fetcherIsMounted) return s;
@@ -915,13 +1008,13 @@ function createRouter(init) {
915
1008
  let redirect = findRedirect(loaderResults);
916
1009
  if (redirect) {
917
1010
  state = getRedirectStateWithDoneFetcher(state);
918
- return startRedirectNavigation(revalidationRequest, redirect.result, false, { preventScrollReset });
1011
+ return startRedirectNavigation(revalidationRequest, redirect.result, false, "fetcher", { preventScrollReset });
919
1012
  }
920
1013
  redirect = findRedirect(fetcherResults);
921
1014
  if (redirect) {
922
1015
  fetchRedirectIds.add(redirect.key);
923
1016
  state = getRedirectStateWithDoneFetcher(state);
924
- return startRedirectNavigation(revalidationRequest, redirect.result, false, { preventScrollReset });
1017
+ return startRedirectNavigation(revalidationRequest, redirect.result, false, "fetcher", { preventScrollReset });
925
1018
  }
926
1019
  let finalFetchers = new Map(state.fetchers);
927
1020
  if (fetcherIsMounted) finalFetchers.set(key, getDoneFetcher(actionResult.data));
@@ -930,6 +1023,7 @@ function createRouter(init) {
930
1023
  if (state.navigation.state === "loading" && loadId > pendingNavigationLoadId) {
931
1024
  invariant(pendingAction, "Expected pending action");
932
1025
  pendingNavigationController && pendingNavigationController.abort();
1026
+ markFetchRedirectsDone(finalFetchers);
933
1027
  completeNavigation(state.navigation.location, {
934
1028
  matches,
935
1029
  loaderData,
@@ -942,7 +1036,7 @@ function createRouter(init) {
942
1036
  loaderData: mergeLoaderData(state.loaderData, loaderData, matches, errors),
943
1037
  fetchers: finalFetchers
944
1038
  });
945
- isRevalidationRequired = false;
1039
+ isRevalidationRequired = fetchReloadIds.size > 0;
946
1040
  }
947
1041
  }
948
1042
  async function handleFetcherLoader(key, loadMatch, matches, scopedContext, flushSync, preventScrollReset, submission) {
@@ -950,11 +1044,17 @@ function createRouter(init) {
950
1044
  let existingFetcher = state.fetchers.get(key);
951
1045
  updateFetcherState(key, getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : void 0), { flushSync });
952
1046
  let abortController = new AbortController();
1047
+ if (future.unstable_customRouteDiscovery) fetchControllers.set(key, abortController);
953
1048
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
954
1049
  if (loadMatch.isDiscovering) {
955
1050
  let discoverResult = await discoverRoutes(matches, new URL(fetchRequest.url).pathname, fetchRequest.signal, key);
956
- if (discoverResult.type === "aborted") return;
957
- else if (discoverResult.type === "error") {
1051
+ if (future.unstable_customRouteDiscovery && fetchRequest.signal.aborted || discoverResult.type === "aborted") return;
1052
+ else if (discoverResult.type === "cancelled") {
1053
+ fetchControllers.delete(key);
1054
+ fetchLoadMatches.delete(key);
1055
+ updateFetcherState(key, getDoneFetcher(existingFetcher?.data), { flushSync });
1056
+ return;
1057
+ } else if (discoverResult.type === "error") {
958
1058
  setFetcherError(key, routeId, discoverResult.error, { flushSync });
959
1059
  return;
960
1060
  } else if (!discoverResult.matches) {
@@ -968,7 +1068,7 @@ function createRouter(init) {
968
1068
  let match = getTargetMatch(matches, path);
969
1069
  fetchControllers.set(key, abortController);
970
1070
  let originatingLoadId = incrementingLoadId;
971
- let results = await callDataStrategy(fetchRequest, path, getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, matches, match, hydrationRouteProperties, scopedContext), scopedContext, key);
1071
+ let results = await callDataStrategy(fetchRequest, path, getTargetedDataStrategyMatches(mapRouteProperties, manifest, fetchRequest, path, matches, match, hydrationRouteProperties, scopedContext), scopedContext, key, "fetcher");
972
1072
  let result = results[match.route.id];
973
1073
  if (!result) {
974
1074
  for (let match of matches) if (results[match.route.id]) {
@@ -987,7 +1087,7 @@ function createRouter(init) {
987
1087
  return;
988
1088
  } else {
989
1089
  fetchRedirectIds.add(key);
990
- await startRedirectNavigation(fetchRequest, result, false, { preventScrollReset });
1090
+ await startRedirectNavigation(fetchRequest, result, false, "fetcher", { preventScrollReset });
991
1091
  return;
992
1092
  }
993
1093
  if (isErrorResult(result)) {
@@ -1015,7 +1115,7 @@ function createRouter(init) {
1015
1115
  * actually touch history until we've processed redirects, so we just use
1016
1116
  * the history action from the original navigation (PUSH or REPLACE).
1017
1117
  */
1018
- async function startRedirectNavigation(request, redirect, isNavigation, { submission, fetcherSubmission, preventScrollReset, replace } = {}) {
1118
+ async function startRedirectNavigation(request, redirect, isNavigation, dataStrategyInitiator, { submission, fetcherSubmission, preventScrollReset, replace } = {}) {
1019
1119
  if (!isNavigation) {
1020
1120
  pendingPopstateNavigationDfd?.resolve();
1021
1121
  pendingPopstateNavigationDfd = null;
@@ -1052,20 +1152,22 @@ function createRouter(init) {
1052
1152
  formAction: location
1053
1153
  },
1054
1154
  preventScrollReset: preventScrollReset || pendingPreventScrollReset,
1055
- enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0
1155
+ enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0,
1156
+ dataStrategyInitiator
1056
1157
  });
1057
1158
  else await startNavigation(redirectNavigationType, redirectLocation, {
1058
1159
  overrideNavigation: getLoadingNavigation(redirectLocation, [], redirectNavigationType, submission),
1059
1160
  fetcherSubmission,
1060
1161
  preventScrollReset: preventScrollReset || pendingPreventScrollReset,
1061
- enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0
1162
+ enableViewTransition: isNavigation ? pendingViewTransitionEnabled : void 0,
1163
+ dataStrategyInitiator
1062
1164
  });
1063
1165
  }
1064
- async function callDataStrategy(request, path, matches, scopedContext, fetcherKey) {
1166
+ async function callDataStrategy(request, path, matches, scopedContext, fetcherKey, initiator, navigationType = null) {
1065
1167
  let results;
1066
1168
  let dataResults = {};
1067
1169
  try {
1068
- results = await callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, false);
1170
+ results = await callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, initiator, scopedContext, false, navigationType);
1069
1171
  } catch (e) {
1070
1172
  matches.filter((m) => m.shouldLoad).forEach((m) => {
1071
1173
  dataResults[m.route.id] = {
@@ -1092,11 +1194,11 @@ function createRouter(init) {
1092
1194
  } else dataResults[routeId] = await convertDataStrategyResultToDataResult(result);
1093
1195
  return dataResults;
1094
1196
  }
1095
- async function callLoadersAndMaybeResolveData(matches, fetchersToLoad, request, location, scopedContext) {
1096
- let loaderResultsPromise = callDataStrategy(request, location, matches, scopedContext, null);
1197
+ async function callLoadersAndMaybeResolveData(matches, fetchersToLoad, request, location, scopedContext, initiator, navigationType = null) {
1198
+ let loaderResultsPromise = callDataStrategy(request, location, matches, scopedContext, null, initiator, navigationType);
1097
1199
  let fetcherResultsPromise = Promise.all(fetchersToLoad.map(async (f) => {
1098
1200
  if (f.matches && f.match && f.request && f.controller) {
1099
- let result = (await callDataStrategy(f.request, f.path, f.matches, scopedContext, f.key))[f.match.route.id];
1201
+ let result = (await callDataStrategy(f.request, f.path, f.matches, scopedContext, f.key, initiator, navigationType))[f.match.route.id];
1100
1202
  return { [f.key]: result };
1101
1203
  } else return Promise.resolve({ [f.key]: {
1102
1204
  type: "error",
@@ -1110,7 +1212,8 @@ function createRouter(init) {
1110
1212
  }
1111
1213
  function interruptActiveLoads() {
1112
1214
  isRevalidationRequired = true;
1113
- fetchLoadMatches.forEach((_, key) => {
1215
+ fetchLoadMatches.forEach((loadMatch, key) => {
1216
+ if (future.unstable_customRouteDiscovery && loadMatch.isDiscovering) return;
1114
1217
  if (fetchControllers.has(key)) cancelledFetcherLoads.add(key);
1115
1218
  abortFetcher(key);
1116
1219
  });
@@ -1136,6 +1239,8 @@ function createRouter(init) {
1136
1239
  }
1137
1240
  function resetFetcher(key, opts) {
1138
1241
  abortFetcher(key, opts?.reason);
1242
+ fetchLoadMatches.delete(key);
1243
+ cancelledFetcherLoads.delete(key);
1139
1244
  updateFetcherState(key, getDoneFetcher(null));
1140
1245
  }
1141
1246
  function deleteFetcher(fetchers, key) {
@@ -1308,6 +1413,10 @@ function createRouter(init) {
1308
1413
  }
1309
1414
  });
1310
1415
  } catch (e) {
1416
+ if (future.unstable_customRouteDiscovery) {
1417
+ if (signal.aborted) return { type: "aborted" };
1418
+ if (e instanceof DiscoveryCancelledError) return { type: "cancelled" };
1419
+ }
1311
1420
  return {
1312
1421
  type: "error",
1313
1422
  error: e,
@@ -1768,7 +1877,7 @@ function createStaticHandler(routes, opts) {
1768
1877
  };
1769
1878
  }
1770
1879
  async function callDataStrategy(request, location, matches, isRouteRequest, requestContext, dataStrategy) {
1771
- let results = await callDataStrategyImpl(dataStrategy || defaultDataStrategy, request, location, matches, null, requestContext, true);
1880
+ let results = await callDataStrategyImpl(dataStrategy || defaultDataStrategy, request, location, matches, null, "static", requestContext, true);
1772
1881
  let dataResults = {};
1773
1882
  await Promise.all(matches.map(async (match) => {
1774
1883
  if (!(match.route.id in results)) return;
@@ -1787,6 +1896,9 @@ function createStaticHandler(routes, opts) {
1787
1896
  }
1788
1897
  return {
1789
1898
  dataRoutes,
1899
+ get _matchCost() {
1900
+ return dataRouteMatcher instanceof V6RegExMatcher ? dataRouteMatcher.matchCost : void 0;
1901
+ },
1790
1902
  match,
1791
1903
  query,
1792
1904
  queryRoute
@@ -2172,6 +2284,9 @@ const loadLazyRouteProperty = ({ key, route, manifest, mapRouteProperties }) =>
2172
2284
  }
2173
2285
  })();
2174
2286
  cache[key] = propertyPromise;
2287
+ propertyPromise.catch(() => {
2288
+ if (cache[key] === propertyPromise) delete cache[key];
2289
+ });
2175
2290
  return propertyPromise;
2176
2291
  };
2177
2292
  const lazyRouteFunctionCache = /* @__PURE__ */ new WeakMap();
@@ -2213,7 +2328,9 @@ function loadLazyRoute(route, type, manifest, mapRouteProperties, lazyRoutePrope
2213
2328
  });
2214
2329
  })();
2215
2330
  lazyRouteFunctionCache.set(routeToUpdate, lazyRoutePromise);
2216
- lazyRoutePromise.catch(() => {});
2331
+ lazyRoutePromise.catch(() => {
2332
+ if (lazyRouteFunctionCache.get(routeToUpdate) === lazyRoutePromise) lazyRouteFunctionCache.delete(routeToUpdate);
2333
+ });
2217
2334
  return {
2218
2335
  lazyRoutePromise,
2219
2336
  lazyHandlerPromise: lazyRoutePromise
@@ -2409,7 +2526,7 @@ function getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, p
2409
2526
  return getDataStrategyMatch(mapRouteProperties, manifest, request, path, getRoutePattern(matches), match, lazyRoutePropertiesToSkip, scopedContext, true, shouldRevalidateArgs);
2410
2527
  });
2411
2528
  }
2412
- async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, isStaticHandler) {
2529
+ async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, initiator, scopedContext, isStaticHandler, navigationType = null) {
2413
2530
  if (matches.some((m) => m._lazyPromises?.middleware)) await Promise.all(matches.map((m) => m._lazyPromises?.middleware));
2414
2531
  let dataStrategyArgs = {
2415
2532
  request,
@@ -2417,7 +2534,9 @@ async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fe
2417
2534
  pattern: getRoutePattern(matches),
2418
2535
  params: matches[0].params,
2419
2536
  context: scopedContext,
2420
- matches
2537
+ matches,
2538
+ unstable_initiator: initiator,
2539
+ unstable_navigationType: initiator === "navigation" ? navigationType : null
2421
2540
  };
2422
2541
  let runClientMiddleware = isStaticHandler ? () => {
2423
2542
  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`");
@@ -2503,10 +2622,13 @@ async function callLoaderOrAction({ request, path, pattern, match, lazyHandlerPr
2503
2622
  pathname,
2504
2623
  routeId: match.route.id
2505
2624
  });
2506
- } else return {
2507
- type: "data",
2508
- result: void 0
2509
- };
2625
+ } else {
2626
+ await lazyRoutePromise;
2627
+ return {
2628
+ type: "data",
2629
+ result: void 0
2630
+ };
2631
+ }
2510
2632
  }
2511
2633
  else if (!handler) {
2512
2634
  let url = new URL(request.url);
@@ -2701,12 +2823,12 @@ function processRouteLoaderData(matches, results, pendingActionResult, isStaticH
2701
2823
  function processLoaderData(state, matches, results, pendingActionResult, revalidatingFetchers, fetcherResults, workingFetchers) {
2702
2824
  let { loaderData, errors } = processRouteLoaderData(matches, results, pendingActionResult);
2703
2825
  revalidatingFetchers.filter((f) => !f.matches || f.matches.some((m) => m.shouldLoad)).forEach((rf) => {
2704
- let { key, match, controller } = rf;
2826
+ let { key, routeId, controller } = rf;
2705
2827
  if (controller && controller.signal.aborted) return;
2706
2828
  let result = fetcherResults[key];
2707
2829
  invariant(result, "Did not find corresponding fetcher result");
2708
2830
  if (isErrorResult(result)) {
2709
- let boundaryMatch = findNearestBoundary(state.matches, match?.route.id);
2831
+ let boundaryMatch = findNearestBoundary(state.matches, routeId);
2710
2832
  if (!(errors && errors[boundaryMatch.route.id])) errors = {
2711
2833
  ...errors,
2712
2834
  [boundaryMatch.route.id]: result.error
@@ -3014,4 +3136,4 @@ function createDeferred() {
3014
3136
  };
3015
3137
  }
3016
3138
  //#endregion
3017
- export { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, createDataRouteMatcher, createRouter, createStaticHandler, getStaticContextFromError, hasInvalidProtocol, isDataWithResponseInit, isMutationMethod, isRedirectResponse, isRedirectStatusCode, isResponse };
3139
+ export { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, clientErrors, createDataRouteMatcher, createRouter, createStaticHandler, getStaticContextFromError, hasInvalidProtocol, isDataWithResponseInit, isMutationMethod, isRedirectResponse, isRedirectStatusCode, isResponse };