react-router 8.3.1 → 8.4.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 (176) hide show
  1. package/CHANGELOG.md +33 -0
  2. package/dist/development/dom-export.js +1 -1
  3. package/dist/development/index-react-server-client.js +1 -1
  4. package/dist/development/index-react-server.d.ts +42 -48
  5. package/dist/development/index-react-server.js +75 -31
  6. package/dist/development/index.d.ts +2 -2
  7. package/dist/development/index.js +3 -3
  8. package/dist/development/lib/actions.js +1 -1
  9. package/dist/development/lib/components.js +45 -8
  10. package/dist/development/lib/context.d.ts +13 -5
  11. package/dist/development/lib/context.js +11 -3
  12. package/dist/development/lib/dom/dom.js +1 -1
  13. package/dist/development/lib/dom/lib.js +14 -32
  14. package/dist/development/lib/dom/server.d.ts +6 -1
  15. package/dist/development/lib/dom/server.js +40 -14
  16. package/dist/development/lib/dom/ssr/components.js +15 -23
  17. package/dist/development/lib/dom/ssr/data.js +1 -1
  18. package/dist/development/lib/dom/ssr/entry.d.ts +4 -0
  19. package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
  20. package/dist/development/lib/dom/ssr/fallback.js +1 -1
  21. package/dist/development/lib/dom/ssr/fog-of-war.js +33 -12
  22. package/dist/development/lib/dom/ssr/hydration.d.ts +3 -1
  23. package/dist/development/lib/dom/ssr/hydration.js +6 -4
  24. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  25. package/dist/development/lib/dom/ssr/links.js +1 -1
  26. package/dist/development/lib/dom/ssr/markup.js +5 -2
  27. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  28. package/dist/development/lib/dom/ssr/routes-test-stub.js +1 -1
  29. package/dist/development/lib/dom/ssr/routes.js +1 -1
  30. package/dist/development/lib/dom/ssr/server.js +2 -2
  31. package/dist/development/lib/dom/ssr/single-fetch.js +10 -19
  32. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  33. package/dist/development/lib/dom-export/hydrated-router.js +2 -1
  34. package/dist/development/lib/errors.js +1 -1
  35. package/dist/development/lib/hooks.js +49 -44
  36. package/dist/development/lib/href.js +1 -1
  37. package/dist/development/lib/router/history.js +1 -1
  38. package/dist/development/lib/router/instrumentation.js +1 -1
  39. package/dist/development/lib/router/matcher-route-pattern.d.ts +2 -0
  40. package/dist/development/lib/router/matcher-route-pattern.js +216 -0
  41. package/dist/development/lib/router/matcher-route-pattern.preload.d.ts +20 -0
  42. package/dist/development/lib/router/matcher-route-pattern.preload.js +36 -0
  43. package/dist/development/lib/router/matcher.d.ts +2 -0
  44. package/dist/development/lib/router/matcher.js +29 -0
  45. package/dist/development/lib/router/navigation.js +1 -1
  46. package/dist/development/lib/router/router.d.ts +13 -7
  47. package/dist/development/lib/router/router.js +81 -66
  48. package/dist/development/lib/router/url.js +1 -1
  49. package/dist/development/lib/router/utils.d.ts +6 -1
  50. package/dist/development/lib/router/utils.js +6 -4
  51. package/dist/development/lib/rsc/browser.js +2 -1
  52. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  53. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  54. package/dist/development/lib/rsc/html-stream/server.js +2 -4
  55. package/dist/development/lib/rsc/route-modules.js +1 -1
  56. package/dist/development/lib/rsc/server.rsc.d.ts +7 -0
  57. package/dist/development/lib/rsc/server.ssr.js +25 -21
  58. package/dist/development/lib/server-runtime/cookies.js +1 -1
  59. package/dist/development/lib/server-runtime/crypto.js +1 -1
  60. package/dist/development/lib/server-runtime/data.js +1 -1
  61. package/dist/development/lib/server-runtime/dev.js +1 -1
  62. package/dist/development/lib/server-runtime/entry.js +1 -1
  63. package/dist/development/lib/server-runtime/errors.js +1 -1
  64. package/dist/development/lib/server-runtime/headers.js +1 -1
  65. package/dist/development/lib/server-runtime/invariant.js +1 -1
  66. package/dist/development/lib/server-runtime/mode.js +1 -1
  67. package/dist/development/lib/server-runtime/routeMatching.js +3 -4
  68. package/dist/development/lib/server-runtime/routes.js +1 -1
  69. package/dist/development/lib/server-runtime/server.js +7 -6
  70. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  71. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
  72. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
  73. package/dist/development/lib/server-runtime/sessions.js +1 -1
  74. package/dist/development/lib/server-runtime/single-fetch.js +1 -1
  75. package/dist/development/lib/server-runtime/urls.js +1 -1
  76. package/dist/development/lib/server-runtime/warnings.js +1 -1
  77. package/dist/development/lib/types/internal.js +1 -1
  78. package/dist/development/route-pattern.d.ts +3 -0
  79. package/dist/development/route-pattern.js +12 -0
  80. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  81. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  82. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  83. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  84. package/dist/production/dom-export.js +1 -1
  85. package/dist/production/index-react-server-client.js +1 -1
  86. package/dist/production/index-react-server.d.ts +42 -48
  87. package/dist/production/index-react-server.js +75 -31
  88. package/dist/production/index.d.ts +2 -2
  89. package/dist/production/index.js +3 -3
  90. package/dist/production/lib/actions.js +1 -1
  91. package/dist/production/lib/components.js +45 -8
  92. package/dist/production/lib/context.d.ts +13 -5
  93. package/dist/production/lib/context.js +11 -3
  94. package/dist/production/lib/dom/dom.js +1 -1
  95. package/dist/production/lib/dom/lib.js +14 -32
  96. package/dist/production/lib/dom/server.d.ts +6 -1
  97. package/dist/production/lib/dom/server.js +40 -14
  98. package/dist/production/lib/dom/ssr/components.js +15 -23
  99. package/dist/production/lib/dom/ssr/data.js +1 -1
  100. package/dist/production/lib/dom/ssr/entry.d.ts +4 -0
  101. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  102. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  103. package/dist/production/lib/dom/ssr/fog-of-war.js +33 -12
  104. package/dist/production/lib/dom/ssr/hydration.d.ts +3 -1
  105. package/dist/production/lib/dom/ssr/hydration.js +6 -4
  106. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  107. package/dist/production/lib/dom/ssr/links.js +1 -1
  108. package/dist/production/lib/dom/ssr/markup.js +5 -2
  109. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  110. package/dist/production/lib/dom/ssr/routes-test-stub.js +1 -1
  111. package/dist/production/lib/dom/ssr/routes.js +1 -1
  112. package/dist/production/lib/dom/ssr/server.js +2 -2
  113. package/dist/production/lib/dom/ssr/single-fetch.js +10 -19
  114. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  115. package/dist/production/lib/dom-export/hydrated-router.js +2 -1
  116. package/dist/production/lib/errors.js +1 -1
  117. package/dist/production/lib/hooks.js +49 -44
  118. package/dist/production/lib/href.js +1 -1
  119. package/dist/production/lib/router/history.js +1 -1
  120. package/dist/production/lib/router/instrumentation.js +1 -1
  121. package/dist/production/lib/router/matcher-route-pattern.d.ts +2 -0
  122. package/dist/production/lib/router/matcher-route-pattern.js +216 -0
  123. package/dist/production/lib/router/matcher-route-pattern.preload.d.ts +20 -0
  124. package/dist/production/lib/router/matcher-route-pattern.preload.js +36 -0
  125. package/dist/production/lib/router/matcher.d.ts +2 -0
  126. package/dist/production/lib/router/matcher.js +29 -0
  127. package/dist/production/lib/router/navigation.js +1 -1
  128. package/dist/production/lib/router/router.d.ts +13 -7
  129. package/dist/production/lib/router/router.js +81 -66
  130. package/dist/production/lib/router/url.js +1 -1
  131. package/dist/production/lib/router/utils.d.ts +6 -1
  132. package/dist/production/lib/router/utils.js +6 -4
  133. package/dist/production/lib/rsc/browser.js +2 -1
  134. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  135. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  136. package/dist/production/lib/rsc/html-stream/server.js +2 -4
  137. package/dist/production/lib/rsc/route-modules.js +1 -1
  138. package/dist/production/lib/rsc/server.rsc.d.ts +7 -0
  139. package/dist/production/lib/rsc/server.ssr.js +25 -21
  140. package/dist/production/lib/server-runtime/cookies.js +1 -1
  141. package/dist/production/lib/server-runtime/crypto.js +1 -1
  142. package/dist/production/lib/server-runtime/data.js +1 -1
  143. package/dist/production/lib/server-runtime/dev.js +1 -1
  144. package/dist/production/lib/server-runtime/entry.js +1 -1
  145. package/dist/production/lib/server-runtime/errors.js +1 -1
  146. package/dist/production/lib/server-runtime/headers.js +1 -1
  147. package/dist/production/lib/server-runtime/invariant.js +1 -1
  148. package/dist/production/lib/server-runtime/mode.js +1 -1
  149. package/dist/production/lib/server-runtime/routeMatching.js +3 -4
  150. package/dist/production/lib/server-runtime/routes.js +1 -1
  151. package/dist/production/lib/server-runtime/server.js +7 -6
  152. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  153. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
  154. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
  155. package/dist/production/lib/server-runtime/sessions.js +1 -1
  156. package/dist/production/lib/server-runtime/single-fetch.js +1 -1
  157. package/dist/production/lib/server-runtime/urls.js +1 -1
  158. package/dist/production/lib/server-runtime/warnings.js +1 -1
  159. package/dist/production/lib/types/internal.js +1 -1
  160. package/dist/production/route-pattern.d.ts +3 -0
  161. package/dist/production/route-pattern.js +12 -0
  162. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  163. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  164. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  165. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  166. package/docs/how-to/fetchers.md +6 -1
  167. package/docs/how-to/middleware.md +3 -1
  168. package/docs/how-to/optimize-revalidation.md +178 -5
  169. package/docs/how-to/react-server-components.md +26 -3
  170. package/docs/how-to/resource-routes.md +3 -0
  171. package/docs/start/data/actions.md +2 -1
  172. package/docs/start/data/route-object.md +21 -6
  173. package/docs/start/framework/actions.md +4 -2
  174. package/docs/start/framework/route-module.md +32 -5
  175. package/docs/upgrading/future.md +68 -0
  176. package/package.json +11 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.1
2
+ * react-router v8.4.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 { 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,30 +92,25 @@ 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
  };
@@ -128,12 +133,13 @@ function createRouter(init) {
128
133
  };
129
134
  };
130
135
  }
131
- let manifest = {};
132
- let dataRoutes = new DataRoutes(convertRoutesToDataRoutes(init.routes, mapRouteProperties, void 0, manifest));
136
+ let future = { ...init.future };
133
137
  let basename = init.basename || "/";
134
138
  if (!basename.startsWith("/")) basename = `/${basename}`;
139
+ let dataRouteMatcher = createDataRouteMatcher(future, basename);
140
+ let manifest = {};
141
+ let dataRoutes = new DataRoutes(convertRoutesToDataRoutes(init.routes, mapRouteProperties, void 0, manifest), dataRouteMatcher);
135
142
  let dataStrategyImpl = init.dataStrategy || defaultDataStrategyWithMiddleware;
136
- let future = { ...init.future };
137
143
  let unlistenHistory = null;
138
144
  let subscribers = /* @__PURE__ */ new Set();
139
145
  let bufferedInitialStateUpdate = null;
@@ -141,7 +147,7 @@ function createRouter(init) {
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;
@@ -352,7 +358,7 @@ function createRouter(init) {
352
358
  if (isUninterruptedRevalidation) {} else if (pendingAction === "POP") {} else if (pendingAction === "PUSH") init.history.push(location, location.state);
353
359
  else if (pendingAction === "REPLACE") init.history.replace(location, location.state);
354
360
  let viewTransitionOpts;
355
- if (pendingAction === "POP") {
361
+ if (pendingAction === "POP" && !isUninterruptedRevalidation && location !== state.location) {
356
362
  let priorPaths = appliedViewTransitions.get(state.location.pathname);
357
363
  if (priorPaths && priorPaths.has(location.pathname)) viewTransitionOpts = {
358
364
  currentLocation: state.location,
@@ -501,14 +507,13 @@ function createRouter(init) {
501
507
  saveScrollPosition(state.location, state.matches);
502
508
  pendingPreventScrollReset = (opts && opts.preventScrollReset) === true;
503
509
  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);
510
+ let matches = opts?.initialHydration && state.matches && state.matches.length > 0 && !initialMatchesIsFOW ? state.matches : dataRouteMatcher.match(location);
506
511
  let flushSync = (opts && opts.flushSync) === true;
507
512
  if (matches && state.initialized && !isRevalidationRequired && isHashChangeOnly(state.location, location) && !(opts && opts.submission && isMutationMethod(opts.submission.formMethod))) {
508
513
  completeNavigation(location, { matches }, { flushSync });
509
514
  return;
510
515
  }
511
- let fogOfWar = checkFogOfWar(matches, routesToUse, location.pathname);
516
+ let fogOfWar = checkFogOfWar(matches, location.pathname);
512
517
  if (fogOfWar.active && fogOfWar.matches) matches = fogOfWar.matches;
513
518
  if (opts?.instrumentationNavigateMetaReceiver) {
514
519
  let meta = getInstrumentationNavigateMeta(init.history, location, matches);
@@ -694,8 +699,7 @@ function createRouter(init) {
694
699
  };
695
700
  } else matches = discoverResult.matches;
696
701
  }
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);
702
+ 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
703
  pendingNavigationLoadId = ++incrementingLoadId;
700
704
  if (!init.dataStrategy && !dsMatches.some((m) => m.shouldLoad) && !dsMatches.some((m) => m.route.middleware && m.route.middleware.length > 0) && revalidatingFetchers.length === 0) {
701
705
  let workingFetchers = new Map(state.fetchers);
@@ -774,10 +778,9 @@ function createRouter(init) {
774
778
  abortFetcher(key);
775
779
  let flushSync = (opts && opts.flushSync) === true;
776
780
  let instrumentationResultMetaReceiver = consumeInstrumentationClientResultMetaReceiver(router);
777
- let routesToUse = dataRoutes.activeRoutes;
778
781
  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);
782
+ let matches = dataRouteMatcher.match(normalizedPath);
783
+ let fogOfWar = checkFogOfWar(matches, normalizedPath);
781
784
  if (fogOfWar.active && fogOfWar.matches) matches = fogOfWar.matches;
782
785
  if (instrumentationResultMetaReceiver) instrumentationResultMetaReceiver(getInstrumentationNavigateMeta(init.history, normalizedPath, matches));
783
786
  if (!matches) {
@@ -795,11 +798,13 @@ function createRouter(init) {
795
798
  await handleFetcherAction(key, routeId, path, matches, scopedContext, fogOfWar.active, flushSync, preventScrollReset, submission, opts && opts.defaultShouldRevalidate);
796
799
  return;
797
800
  }
798
- fetchLoadMatches.set(key, {
801
+ let loadMatch = {
799
802
  routeId,
800
- path
801
- });
802
- await handleFetcherLoader(key, routeId, path, matches, scopedContext, fogOfWar.active, flushSync, preventScrollReset, submission);
803
+ path,
804
+ isDiscovering: fogOfWar.active
805
+ };
806
+ fetchLoadMatches.set(key, loadMatch);
807
+ await handleFetcherLoader(key, loadMatch, matches, scopedContext, flushSync, preventScrollReset, submission);
803
808
  }
804
809
  async function handleFetcherAction(key, routeId, path, requestMatches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission, callSiteDefaultShouldRevalidate) {
805
810
  interruptActiveLoads();
@@ -869,12 +874,11 @@ function createRouter(init) {
869
874
  }
870
875
  let nextLocation = state.navigation.location || state.location;
871
876
  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;
877
+ let matches = state.navigation.state !== "idle" ? dataRouteMatcher.match(state.navigation.location) : state.matches;
874
878
  invariant(matches, "Didn't find any matches after fetcher action");
875
879
  let loadId = ++incrementingLoadId;
876
880
  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);
881
+ 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
882
  let loadFetcher = getLoadingFetcher(submission, actionResult.data);
879
883
  let workingFetchers = new Map(state.fetchers);
880
884
  workingFetchers.set(key, loadFetcher);
@@ -941,12 +945,13 @@ function createRouter(init) {
941
945
  isRevalidationRequired = false;
942
946
  }
943
947
  }
944
- async function handleFetcherLoader(key, routeId, path, matches, scopedContext, isFogOfWar, flushSync, preventScrollReset, submission) {
948
+ async function handleFetcherLoader(key, loadMatch, matches, scopedContext, flushSync, preventScrollReset, submission) {
949
+ let { routeId, path } = loadMatch;
945
950
  let existingFetcher = state.fetchers.get(key);
946
951
  updateFetcherState(key, getLoadingFetcher(submission, existingFetcher ? existingFetcher.data : void 0), { flushSync });
947
952
  let abortController = new AbortController();
948
953
  let fetchRequest = createClientSideRequest(init.history, path, abortController.signal);
949
- if (isFogOfWar) {
954
+ if (loadMatch.isDiscovering) {
950
955
  let discoverResult = await discoverRoutes(matches, new URL(fetchRequest.url).pathname, fetchRequest.signal, key);
951
956
  if (discoverResult.type === "aborted") return;
952
957
  else if (discoverResult.type === "error") {
@@ -955,7 +960,10 @@ function createRouter(init) {
955
960
  } else if (!discoverResult.matches) {
956
961
  setFetcherError(key, routeId, getInternalRouterError(404, { pathname: path }), { flushSync });
957
962
  return;
958
- } else matches = discoverResult.matches;
963
+ } else {
964
+ matches = discoverResult.matches;
965
+ loadMatch.isDiscovering = false;
966
+ }
959
967
  }
960
968
  let match = getTargetMatch(matches, path);
961
969
  fetchControllers.set(key, abortController);
@@ -1264,16 +1272,15 @@ function createRouter(init) {
1264
1272
  }
1265
1273
  return null;
1266
1274
  }
1267
- function checkFogOfWar(matches, routesToUse, pathname) {
1275
+ function checkFogOfWar(matches, pathname) {
1268
1276
  if (init.patchRoutesOnNavigation) {
1269
- let activeBranches = dataRoutes.branches;
1270
1277
  if (!matches) return {
1271
1278
  active: true,
1272
- matches: matchRoutesImpl(routesToUse, pathname, basename, true, activeBranches) || []
1279
+ matches: dataRouteMatcher.match(pathname, true) || []
1273
1280
  };
1274
1281
  else if (Object.keys(matches[0].params).length > 0) return {
1275
1282
  active: true,
1276
- matches: matchRoutesImpl(routesToUse, pathname, basename, true, activeBranches)
1283
+ matches: dataRouteMatcher.match(pathname, true)
1277
1284
  };
1278
1285
  }
1279
1286
  return {
@@ -1308,21 +1315,20 @@ function createRouter(init) {
1308
1315
  };
1309
1316
  }
1310
1317
  if (signal.aborted) return { type: "aborted" };
1311
- let activeBranches = dataRoutes.branches;
1312
- let newMatches = matchRoutesImpl(dataRoutes.activeRoutes, pathname, basename, false, activeBranches);
1318
+ let newMatches = dataRouteMatcher.match(pathname);
1313
1319
  let newPartialMatches = null;
1314
1320
  if (newMatches) if (Object.keys(newMatches[0].params).length === 0) return {
1315
1321
  type: "success",
1316
1322
  matches: newMatches
1317
1323
  };
1318
1324
  else {
1319
- newPartialMatches = matchRoutesImpl(dataRoutes.activeRoutes, pathname, basename, true, activeBranches);
1325
+ newPartialMatches = dataRouteMatcher.match(pathname, true);
1320
1326
  if (!(newPartialMatches && partialMatches.length < newPartialMatches.length && compareMatches(partialMatches, newPartialMatches.slice(0, partialMatches.length)))) return {
1321
1327
  type: "success",
1322
1328
  matches: newMatches
1323
1329
  };
1324
1330
  }
1325
- if (!newPartialMatches) newPartialMatches = matchRoutesImpl(dataRoutes.activeRoutes, pathname, basename, true, activeBranches);
1331
+ if (!newPartialMatches) newPartialMatches = dataRouteMatcher.match(pathname, true);
1326
1332
  if (!newPartialMatches || compareMatches(partialMatches, newPartialMatches)) return {
1327
1333
  type: "success",
1328
1334
  matches: null
@@ -1354,8 +1360,8 @@ function createRouter(init) {
1354
1360
  get routes() {
1355
1361
  return dataRoutes.stableRoutes;
1356
1362
  },
1357
- get branches() {
1358
- return dataRoutes.branches;
1363
+ match(locationArg) {
1364
+ return dataRouteMatcher.match(locationArg);
1359
1365
  },
1360
1366
  get manifest() {
1361
1367
  return manifest;
@@ -1424,7 +1430,7 @@ function createStaticHandler(routes, opts) {
1424
1430
  let basename = (opts ? opts.basename : null) || "/";
1425
1431
  let _mapRouteProperties = opts?.mapRouteProperties;
1426
1432
  let mapRouteProperties = _mapRouteProperties ? _mapRouteProperties : () => ({});
1427
- ({ ...opts?.future });
1433
+ let dataRouteMatcher = createDataRouteMatcher({ ...opts?.future }, basename);
1428
1434
  if (opts?.instrumentations) {
1429
1435
  let instrumentations = opts.instrumentations;
1430
1436
  mapRouteProperties = (route) => {
@@ -1435,7 +1441,8 @@ function createStaticHandler(routes, opts) {
1435
1441
  };
1436
1442
  }
1437
1443
  let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, void 0, manifest);
1438
- let routeBranches = flattenAndRankRoutes(dataRoutes);
1444
+ dataRouteMatcher.update(dataRoutes);
1445
+ let match = (locationArg) => dataRouteMatcher.match(locationArg);
1439
1446
  /**
1440
1447
  * The query() method is intended for document requests, in which we want to
1441
1448
  * call an optional action and potentially multiple loaders for all nested
@@ -1466,7 +1473,7 @@ function createStaticHandler(routes, opts) {
1466
1473
  let normalizePathImpl = normalizePath || defaultNormalizePath;
1467
1474
  let method = request.method;
1468
1475
  let location = createLocation("", normalizePathImpl(request), null, "default");
1469
- let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
1476
+ let matches = dataRouteMatcher.match(location);
1470
1477
  requestContext = requestContext != null ? requestContext : new RouterContextProvider();
1471
1478
  if (!isValidMethod(method) && method !== "HEAD") {
1472
1479
  let error = getInternalRouterError(405, { method });
@@ -1480,7 +1487,8 @@ function createStaticHandler(routes, opts) {
1480
1487
  errors: { [route.id]: error },
1481
1488
  statusCode: error.status,
1482
1489
  loaderHeaders: {},
1483
- actionHeaders: {}
1490
+ actionHeaders: {},
1491
+ _match: match
1484
1492
  };
1485
1493
  return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
1486
1494
  } else if (!matches) {
@@ -1495,7 +1503,8 @@ function createStaticHandler(routes, opts) {
1495
1503
  errors: { [route.id]: error },
1496
1504
  statusCode: error.status,
1497
1505
  loaderHeaders: {},
1498
- actionHeaders: {}
1506
+ actionHeaders: {},
1507
+ _match: match
1499
1508
  };
1500
1509
  return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
1501
1510
  }
@@ -1518,7 +1527,8 @@ function createStaticHandler(routes, opts) {
1518
1527
  renderedStaticContext = {
1519
1528
  location,
1520
1529
  basename,
1521
- ...result
1530
+ ...result,
1531
+ _match: match
1522
1532
  };
1523
1533
  return renderedStaticContext;
1524
1534
  });
@@ -1544,7 +1554,8 @@ function createStaticHandler(routes, opts) {
1544
1554
  errors: { [skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, matches.find((m) => m.route.id === routeId || m.route.loader)?.route.id || routeId).route.id]: error },
1545
1555
  statusCode: isRouteErrorResponse(error) ? error.status : 500,
1546
1556
  actionHeaders: {},
1547
- loaderHeaders: {}
1557
+ loaderHeaders: {},
1558
+ _match: match
1548
1559
  };
1549
1560
  return generateMiddlewareResponse(() => Promise.resolve(staticContext));
1550
1561
  }
@@ -1561,7 +1572,8 @@ function createStaticHandler(routes, opts) {
1561
1572
  return {
1562
1573
  location,
1563
1574
  basename,
1564
- ...result
1575
+ ...result,
1576
+ _match: match
1565
1577
  };
1566
1578
  }
1567
1579
  /**
@@ -1594,7 +1606,7 @@ function createStaticHandler(routes, opts) {
1594
1606
  let normalizePathImpl = normalizePath || defaultNormalizePath;
1595
1607
  let method = request.method;
1596
1608
  let location = createLocation("", normalizePathImpl(request), null, "default");
1597
- let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
1609
+ let matches = dataRouteMatcher.match(location);
1598
1610
  requestContext = requestContext != null ? requestContext : new RouterContextProvider();
1599
1611
  if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") throw getInternalRouterError(405, { method });
1600
1612
  else if (!matches) throw getInternalRouterError(404, { pathname: location.pathname });
@@ -1775,7 +1787,7 @@ function createStaticHandler(routes, opts) {
1775
1787
  }
1776
1788
  return {
1777
1789
  dataRoutes,
1778
- _internalRouteBranches: routeBranches,
1790
+ match,
1779
1791
  query,
1780
1792
  queryRoute
1781
1793
  };
@@ -1936,7 +1948,7 @@ function normalizeNavigateOptions(isFetcher, path, opts) {
1936
1948
  submission
1937
1949
  };
1938
1950
  }
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) {
1951
+ function getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest, history, state, matches, submission, location, lazyRoutePropertiesToSkip, initialHydration, isRevalidationRequired, cancelledFetcherLoads, fetchersQueuedForDeletion, fetchLoadMatches, fetchRedirectIds, dataRouteMatcher, pendingActionResult, callSiteDefaultShouldRevalidate) {
1940
1952
  let actionResult = pendingActionResult ? isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : pendingActionResult[1].data : void 0;
1941
1953
  let currentUrl = history.createURL(state.location);
1942
1954
  let nextUrl = history.createURL(location);
@@ -1986,12 +1998,11 @@ function getMatchesToLoad(request, scopedContext, mapRouteProperties, manifest,
1986
1998
  });
1987
1999
  let revalidatingFetchers = [];
1988
2000
  fetchLoadMatches.forEach((f, key) => {
1989
- if (initialHydration || !matches.some((m) => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key)) return;
2001
+ if (initialHydration || !matches.some((m) => m.route.id === f.routeId) || fetchersQueuedForDeletion.has(key) || f.isDiscovering) return;
1990
2002
  let fetcher = state.fetchers.get(key);
1991
2003
  let isMidInitialLoad = fetcher && fetcher.state !== "idle" && fetcher.data === void 0;
1992
- let fetcherMatches = matchRoutesImpl(routesToUse, f.path, basename ?? "/", false, branches);
2004
+ let fetcherMatches = dataRouteMatcher.match(f.path);
1993
2005
  if (!fetcherMatches) {
1994
- if (hasPatchRoutesOnNavigation && isMidInitialLoad) return;
1995
2006
  revalidatingFetchers.push({
1996
2007
  key,
1997
2008
  routeId: f.routeId,
@@ -2717,12 +2728,16 @@ function mergeLoaderData(loaderData, newLoaderData, matches, errors) {
2717
2728
  merged[k] = v;
2718
2729
  return merged;
2719
2730
  }, {});
2731
+ let preservedCount = 0;
2720
2732
  for (let match of matches) {
2721
2733
  let id = match.route.id;
2722
- if (!newLoaderData.hasOwnProperty(id) && loaderData.hasOwnProperty(id) && match.route.loader) mergedLoaderData[id] = loaderData[id];
2734
+ if (!newLoaderData.hasOwnProperty(id) && loaderData.hasOwnProperty(id) && match.route.loader) {
2735
+ mergedLoaderData[id] = loaderData[id];
2736
+ preservedCount++;
2737
+ }
2723
2738
  if (errors && errors.hasOwnProperty(id)) break;
2724
2739
  }
2725
- return mergedLoaderData;
2740
+ return Object.keys(newLoaderData).length === 0 && preservedCount === Object.keys(loaderData).length ? loaderData : mergedLoaderData;
2726
2741
  }
2727
2742
  function getActionDataForCommit(pendingActionResult) {
2728
2743
  if (!pendingActionResult) return {};
@@ -2999,4 +3014,4 @@ function createDeferred() {
2999
3014
  };
3000
3015
  }
3001
3016
  //#endregion
3002
- export { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, createRouter, createStaticHandler, getStaticContextFromError, hasInvalidProtocol, isDataWithResponseInit, isMutationMethod, isRedirectResponse, isRedirectStatusCode, isResponse };
3017
+ export { IDLE_BLOCKER, IDLE_FETCHER, IDLE_NAVIGATION, createDataRouteMatcher, createRouter, createStaticHandler, getStaticContextFromError, hasInvalidProtocol, isDataWithResponseInit, isMutationMethod, isRedirectResponse, isRedirectStatusCode, isResponse };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.1
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -430,7 +430,7 @@ interface MapRoutePropertiesFunction {
430
430
  * onto the route. Either they're meaningful to the router, or they'll get
431
431
  * ignored.
432
432
  */
433
- type UnsupportedLazyRouteObjectKey = "lazy" | "caseSensitive" | "path" | "id" | "index" | "children";
433
+ type UnsupportedLazyRouteObjectKey = "lazy" | "caseSensitive" | "path" | "id" | "index" | "children" | "unstable_validateParams";
434
434
  /**
435
435
  * Keys we cannot change from within a lazy() function. We spread all other keys
436
436
  * onto the route. Either they're meaningful to the router, or they'll get
@@ -488,6 +488,11 @@ type BaseRouteObject = {
488
488
  * See [`shouldRevalidate`](../../start/data/route-object#shouldRevalidate).
489
489
  */
490
490
  shouldRevalidate?: ShouldRevalidateFunction;
491
+ /**
492
+ * A map of route param names to regular expressions used to validate params
493
+ * after a route-pattern match.
494
+ */
495
+ unstable_validateParams?: Record<string, RegExp>;
491
496
  /**
492
497
  * The route handle.
493
498
  */
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.1
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -135,7 +135,8 @@ const unsupportedLazyRouteObjectKeys = new Set([
135
135
  "path",
136
136
  "id",
137
137
  "index",
138
- "children"
138
+ "children",
139
+ "unstable_validateParams"
139
140
  ]);
140
141
  function isUnsupportedLazyRouteObjectKey(key) {
141
142
  return unsupportedLazyRouteObjectKeys.has(key);
@@ -147,7 +148,8 @@ const unsupportedLazyRouteFunctionKeys = new Set([
147
148
  "id",
148
149
  "index",
149
150
  "middleware",
150
- "children"
151
+ "children",
152
+ "unstable_validateParams"
151
153
  ]);
152
154
  function isUnsupportedLazyRouteFunctionKey(key) {
153
155
  return unsupportedLazyRouteFunctionKeys.has(key);
@@ -873,4 +875,4 @@ function parseToInfo(_to, basename) {
873
875
  };
874
876
  }
875
877
  //#endregion
876
- export { ErrorResponseImpl, RouterContextProvider, SUPPORTED_ERROR_TYPES, compilePath, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createContext, createDataFunctionUrl, data, decodePath, defaultMapRouteProperties, encodePathParam, flattenAndRankRoutes, generatePath, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isBrowser, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, joinPaths, matchPath, matchRoutes, matchRoutesImpl, parseToInfo, prependBasename, redirect, redirectDocument, removeDoubleSlashes, removeTrailingSlash, replace, resolvePath, resolveTo, stripBasename };
878
+ export { ErrorResponseImpl, RouterContextProvider, SUPPORTED_ERROR_TYPES, compilePath, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createContext, createDataFunctionUrl, data, decodePath, defaultMapRouteProperties, encodePathParam, explodeOptionalSegments, flattenAndRankRoutes, generatePath, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isBrowser, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, joinPaths, matchPath, matchRoutes, matchRoutesImpl, normalizePathname, parseToInfo, prependBasename, redirect, redirectDocument, removeDoubleSlashes, removeTrailingSlash, replace, resolvePath, resolveTo, stripBasename };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.1
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -169,6 +169,7 @@ function createRouterFromPayload({ fetchImplementation, createFromReadableStream
169
169
  },
170
170
  location: payload.location,
171
171
  basename: payload.basename,
172
+ future: {},
172
173
  isSpaMode: false
173
174
  }),
174
175
  async patchRoutesOnNavigation({ path, signal, fetcherKey }) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.1
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.1
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.1
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -8,6 +8,7 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
+ import { escapeAttribute } from "../../dom/ssr/markup.js";
11
12
  //#region lib/rsc/html-stream/server.ts
12
13
  const encoder = new TextEncoder();
13
14
  const trailer = "</body></html>";
@@ -87,9 +88,6 @@ function writeChunk(chunk, controller, nonce) {
87
88
  let nonceAttr = nonce == null ? "" : ` nonce="${escapeAttribute(nonce)}"`;
88
89
  controller.enqueue(encoder.encode(`<script${nonceAttr}>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}<\/script>`));
89
90
  }
90
- function escapeAttribute(value) {
91
- return value.replace(/&/g, "&amp;").replace(/"/g, "&quot;").replace(/</g, "&lt;").replace(/>/g, "&gt;");
92
- }
93
91
  function escapeScript(script) {
94
92
  return script.replace(/<!--/g, "<\\!--").replace(/<\/(script)/gi, "</\\$1");
95
93
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.1
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -116,6 +116,13 @@ type RouteDiscovery = {
116
116
  * encoding an {@link unstable_RSCPayload} for consumption by an [RSC](https://react.dev/reference/rsc/server-components)
117
117
  * enabled client router.
118
118
  *
119
+ * Treat every React Server Function as a public endpoint. Server Functions are
120
+ * not inherently associated with a route. Any route middleware that runs is
121
+ * selected from the URL used to call a Server Function, but the client controls
122
+ * both the Server Function identifier and the URL. Perform all access control
123
+ * checks within each Server Function, or use a route action for
124
+ * middleware-driven access control.
125
+ *
119
126
  * @example
120
127
  * import {
121
128
  * createTemporaryReferenceSet,