react-router 8.3.0 → 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 +46 -2
  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 +50 -48
  5. package/dist/development/index-react-server.js +96 -43
  6. package/dist/development/index.d.ts +2 -2
  7. package/dist/development/index.js +3 -3
  8. package/dist/development/lib/actions.js +10 -5
  9. package/dist/development/lib/components.js +50 -10
  10. package/dist/development/lib/context.d.ts +14 -5
  11. package/dist/development/lib/context.js +11 -3
  12. package/dist/development/lib/dom/dom.js +2 -2
  13. package/dist/development/lib/dom/lib.js +33 -36
  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 +16 -24
  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 +34 -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 +13 -22
  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 +54 -46
  36. package/dist/development/lib/href.js +1 -1
  37. package/dist/development/lib/router/history.js +2 -2
  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 +45 -0
  46. package/dist/development/lib/router/router.d.ts +21 -7
  47. package/dist/development/lib/router/router.js +111 -82
  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 +15 -9
  51. package/dist/development/lib/rsc/browser.js +5 -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 +3 -5
  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 +26 -22
  58. package/dist/development/lib/server-runtime/cookies.js +2 -2
  59. package/dist/development/lib/server-runtime/crypto.js +2 -2
  60. package/dist/development/lib/server-runtime/data.js +1 -1
  61. package/dist/development/lib/server-runtime/dev.js +2 -2
  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 +8 -7
  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 +2 -2
  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 +4 -4
  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 +50 -48
  87. package/dist/production/index-react-server.js +96 -43
  88. package/dist/production/index.d.ts +2 -2
  89. package/dist/production/index.js +3 -3
  90. package/dist/production/lib/actions.js +10 -5
  91. package/dist/production/lib/components.js +50 -10
  92. package/dist/production/lib/context.d.ts +14 -5
  93. package/dist/production/lib/context.js +11 -3
  94. package/dist/production/lib/dom/dom.js +2 -2
  95. package/dist/production/lib/dom/lib.js +33 -36
  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 +16 -24
  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 +34 -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 +13 -22
  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 +54 -46
  118. package/dist/production/lib/href.js +1 -1
  119. package/dist/production/lib/router/history.js +2 -2
  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 +45 -0
  128. package/dist/production/lib/router/router.d.ts +21 -7
  129. package/dist/production/lib/router/router.js +111 -82
  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 +15 -9
  133. package/dist/production/lib/rsc/browser.js +5 -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 +3 -5
  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 +26 -22
  140. package/dist/production/lib/server-runtime/cookies.js +2 -2
  141. package/dist/production/lib/server-runtime/crypto.js +2 -2
  142. package/dist/production/lib/server-runtime/data.js +1 -1
  143. package/dist/production/lib/server-runtime/dev.js +2 -2
  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 +8 -7
  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 +2 -2
  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 +4 -4
  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.0
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);
@@ -480,13 +482,13 @@ function matchPathImpl(pattern, pathname, matcher, compiledParams) {
480
482
  let match = pathname.match(matcher);
481
483
  if (!match) return null;
482
484
  let matchedPathname = match[0];
483
- let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
485
+ let pathnameBase = removeTrailingSlash(matchedPathname, 1);
484
486
  let captureGroups = match.slice(1);
485
487
  return {
486
488
  params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
487
489
  if (paramName === "*") {
488
490
  let splatValue = captureGroups[index] || "";
489
- pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
491
+ pathnameBase = removeTrailingSlash(matchedPathname.slice(0, matchedPathname.length - splatValue.length), 1);
490
492
  }
491
493
  const value = captureGroups[index];
492
494
  if (isOptional && !value) memo[paramName] = void 0;
@@ -555,7 +557,7 @@ function resolvePath(to, fromPathname = "/") {
555
557
  let pathname;
556
558
  if (toPathname) {
557
559
  toPathname = removeDoubleSlashes(toPathname);
558
- if (toPathname.startsWith("/")) pathname = resolvePathname(toPathname.substring(1), "/");
560
+ if (toPathname.startsWith("/") || toPathname.startsWith("\\")) pathname = resolvePathname(toPathname.substring(1), "/");
559
561
  else pathname = resolvePathname(toPathname, fromPathname);
560
562
  } else pathname = fromPathname;
561
563
  return {
@@ -616,7 +618,11 @@ function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = fal
616
618
  }
617
619
  const removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
618
620
  const joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
619
- const removeTrailingSlash = (path) => path.replace(/\/+$/, "");
621
+ function removeTrailingSlash(path, minLength = 0) {
622
+ let end = path.length;
623
+ while (end > minLength && path.charCodeAt(end - 1) === 47) end--;
624
+ return end === path.length ? path : path.slice(0, end);
625
+ }
620
626
  const normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
621
627
  const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
622
628
  const normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
@@ -859,7 +865,7 @@ function parseToInfo(_to, basename) {
859
865
  let path = stripBasename(targetUrl.pathname, basename);
860
866
  if (targetUrl.origin === currentUrl.origin && path != null) to = path + targetUrl.search + targetUrl.hash;
861
867
  else isExternal = true;
862
- } catch (e) {
868
+ } catch {
863
869
  warning(false, `<Link to="${to}"> contains an invalid URL which will probably break when clicked - please update to a valid URL path.`);
864
870
  }
865
871
  return {
@@ -869,4 +875,4 @@ function parseToInfo(_to, basename) {
869
875
  };
870
876
  }
871
877
  //#endregion
872
- 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.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -11,6 +11,7 @@
11
11
  import { PROTOCOL_RELATIVE_URL_REGEX } from "../router/url.js";
12
12
  import { createBrowserHistory, createPath, invariant } from "../router/history.js";
13
13
  import { ErrorResponseImpl, createContext, resolvePath } from "../router/utils.js";
14
+ import { validateNavigationTarget } from "../router/navigation.js";
14
15
  import { createRouter, hasInvalidProtocol, isMutationMethod } from "../router/router.js";
15
16
  import { RSCRouterContext } from "../context.js";
16
17
  import { RouterProvider } from "../components.js";
@@ -82,6 +83,7 @@ function createCallServer({ createFromReadableStream, createTemporaryReferenceSe
82
83
  React$1.startTransition(() => Promise.resolve(payloadPromise).then(async (payload) => {
83
84
  if (payload.type === "redirect") {
84
85
  let location = normalizeRedirectLocation(payload.location);
86
+ validateNavigationTarget(payload.location, location, new URL(window.location.href), "allow-explicit");
85
87
  if (payload.reload || isExternalLocation(location)) {
86
88
  if (hasInvalidProtocol(location)) throw new Error("Invalid redirect location");
87
89
  window.location.href = location;
@@ -97,6 +99,7 @@ function createCallServer({ createFromReadableStream, createTemporaryReferenceSe
97
99
  if (rerender && landedActionId < actionId && globalVar.__routerActionID <= actionId) {
98
100
  if (rerender.type === "redirect") {
99
101
  let location = normalizeRedirectLocation(rerender.location);
102
+ validateNavigationTarget(rerender.location, location, new URL(window.location.href), "allow-explicit");
100
103
  if (rerender.reload || isExternalLocation(location)) {
101
104
  if (hasInvalidProtocol(location)) throw new Error("Invalid redirect location");
102
105
  window.location.href = location;
@@ -166,6 +169,7 @@ function createRouterFromPayload({ fetchImplementation, createFromReadableStream
166
169
  },
167
170
  location: payload.location,
168
171
  basename: payload.basename,
172
+ future: {},
169
173
  isSpaMode: false
170
174
  }),
171
175
  async patchRoutesOnNavigation({ path, signal, fetcherKey }) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
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.0
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.0
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>";
@@ -73,7 +74,7 @@ async function writeRSCStream(reader, controller, isCancelled, nonce) {
73
74
  const chunk = read.value;
74
75
  try {
75
76
  writeChunk(JSON.stringify(decoder.decode(chunk, { stream: true })), controller, nonce);
76
- } catch (e) {
77
+ } catch {
77
78
  writeChunk(`Uint8Array.from(atob(${JSON.stringify(btoa(String.fromCodePoint(...chunk)))}), m => m.codePointAt(0))`, controller, nonce);
78
79
  }
79
80
  }
@@ -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.0
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,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -9,10 +9,10 @@
9
9
  * @license MIT
10
10
  */
11
11
  import { isRouteErrorResponse } from "../router/utils.js";
12
- import { hasInvalidProtocol } from "../router/router.js";
12
+ import { createDataRouteMatcher, hasInvalidProtocol } from "../router/router.js";
13
13
  import { RSCRouterContext } from "../context.js";
14
14
  import { decodeRedirectErrorDigest, decodeRouteErrorResponseDigest } from "../errors.js";
15
- import { escapeHtml } from "../dom/ssr/markup.js";
15
+ import { escapeAttribute } from "../dom/ssr/markup.js";
16
16
  import { shouldHydrateRouteLoader } from "../dom/ssr/routes.js";
17
17
  import { FrameworkContext } from "../dom/ssr/components.js";
18
18
  import { StaticRouterProvider, createStaticRouter } from "../dom/server.js";
@@ -179,7 +179,7 @@ async function routeRSCServerRequest({ request, serverResponse, createFromReadab
179
179
  const redirectTransform = new TransformStream({ flush(controller) {
180
180
  if (renderRedirect) {
181
181
  if (hasInvalidProtocol(renderRedirect.location)) return;
182
- controller.enqueue(new TextEncoder().encode(`<meta http-equiv="refresh" content="0;url=${escapeHtml(renderRedirect.location)}"/>`));
182
+ controller.enqueue(new TextEncoder().encode(`<meta http-equiv="refresh" content="0;url=${escapeAttribute(renderRedirect.location)}"/>`));
183
183
  }
184
184
  } });
185
185
  if (!hydrate) return new Response(html.pipeThrough(redirectTransform), {
@@ -258,7 +258,7 @@ async function routeRSCServerRequest({ request, serverResponse, createFromReadab
258
258
  const retryRedirectTransform = new TransformStream({ flush(controller) {
259
259
  if (retryRedirect) {
260
260
  if (hasInvalidProtocol(retryRedirect.location)) return;
261
- controller.enqueue(new TextEncoder().encode(`<meta http-equiv="refresh" content="0;url=${escapeHtml(retryRedirect.location)}"/>`));
261
+ controller.enqueue(new TextEncoder().encode(`<meta http-equiv="refresh" content="0;url=${escapeAttribute(retryRedirect.location)}"/>`));
262
262
  }
263
263
  } });
264
264
  if (!hydrate) return new Response(html.pipeThrough(retryRedirectTransform), {
@@ -273,7 +273,7 @@ async function routeRSCServerRequest({ request, serverResponse, createFromReadab
273
273
  statusText,
274
274
  headers
275
275
  });
276
- } catch (error2) {}
276
+ } catch {}
277
277
  throw error;
278
278
  }
279
279
  }
@@ -333,6 +333,24 @@ function RSCStaticRouter({ getPayload, nonce }) {
333
333
  if (payload.type !== "render") return null;
334
334
  let patchedLoaderData = { ...payload.loaderData };
335
335
  for (const match of payload.matches) if (shouldHydrateRouteLoader(match.id, match.clientLoader, match.hasLoader, false) && (match.hydrateFallbackElement || !match.hasLoader)) delete patchedLoaderData[match.id];
336
+ const routes = payload.matches.reduceRight((previous, match) => {
337
+ const route = {
338
+ id: match.id,
339
+ action: match.hasAction || !!match.clientAction,
340
+ element: match.element,
341
+ errorElement: match.errorElement,
342
+ handle: match.handle,
343
+ hydrateFallbackElement: match.hydrateFallbackElement,
344
+ index: match.index,
345
+ loader: match.hasLoader || !!match.clientLoader,
346
+ path: match.path,
347
+ shouldRevalidate: match.shouldRevalidate
348
+ };
349
+ if (previous.length > 0) route.children = previous;
350
+ return [route];
351
+ }, []);
352
+ const dataRouteMatcher = createDataRouteMatcher({}, payload.basename || "/");
353
+ dataRouteMatcher.update(routes);
336
354
  const context = {
337
355
  get _deepestRenderedBoundaryId() {
338
356
  return decoded._deepestRenderedBoundaryId ?? null;
@@ -348,6 +366,7 @@ function RSCStaticRouter({ getPayload, nonce }) {
348
366
  loaderHeaders: {},
349
367
  location: payload.location,
350
368
  statusCode: 200,
369
+ _match: (locationArg) => dataRouteMatcher.match(locationArg),
351
370
  matches: payload.matches.map((match) => ({
352
371
  params: match.params,
353
372
  pathname: match.pathname,
@@ -363,22 +382,7 @@ function RSCStaticRouter({ getPayload, nonce }) {
363
382
  }
364
383
  }))
365
384
  };
366
- const router = createStaticRouter(payload.matches.reduceRight((previous, match) => {
367
- const route = {
368
- id: match.id,
369
- action: match.hasAction || !!match.clientAction,
370
- element: match.element,
371
- errorElement: match.errorElement,
372
- handle: match.handle,
373
- hydrateFallbackElement: match.hydrateFallbackElement,
374
- index: match.index,
375
- loader: match.hasLoader || !!match.clientLoader,
376
- path: match.path,
377
- shouldRevalidate: match.shouldRevalidate
378
- };
379
- if (previous.length > 0) route.children = previous;
380
- return [route];
381
- }, []), context);
385
+ const router = createStaticRouter(routes, context);
382
386
  const frameworkContext = {
383
387
  future: {},
384
388
  isSpaMode: false,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -95,7 +95,7 @@ function encodeData(value) {
95
95
  function decodeData(value) {
96
96
  try {
97
97
  return JSON.parse(decodeURIComponent(myEscape(atob(value))));
98
- } catch (e) {
98
+ } catch {
99
99
  return {};
100
100
  }
101
101
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -26,7 +26,7 @@ const unsign = async (cookie, secret) => {
26
26
  try {
27
27
  let signature = byteStringToUint8Array(atob(hash));
28
28
  return await crypto.subtle.verify("HMAC", key, signature, data) ? value : false;
29
- } catch (e) {
29
+ } catch {
30
30
  return false;
31
31
  }
32
32
  };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
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.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -19,7 +19,7 @@ function getDevServerHooks() {
19
19
  function getBuildTimeHeader(request, headerName) {
20
20
  if (typeof process !== "undefined") try {
21
21
  if (process.env.hasOwnProperty("IS_RR_BUILD_REQUEST") && process.env.IS_RR_BUILD_REQUEST === "yes") return request.headers.get(headerName);
22
- } catch (e) {}
22
+ } catch {}
23
23
  return null;
24
24
  }
25
25
  //#endregion
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
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.0
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.0
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.0
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.0
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.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -8,11 +8,10 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import { matchRoutesImpl } from "../router/utils.js";
12
11
  import invariant from "./invariant.js";
13
12
  //#region lib/server-runtime/routeMatching.ts
14
- function matchServerRoutes(manifest, dataRoutes, branches, pathname, basename) {
15
- let matches = matchRoutesImpl(dataRoutes, pathname, basename ?? "/", false, branches);
13
+ function matchServerRoutes(manifest, staticHandler, pathname) {
14
+ let matches = staticHandler.match(pathname);
16
15
  if (!matches) return null;
17
16
  return matches.map((match) => {
18
17
  let route = manifest[match.route.id];
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
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.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -90,12 +90,12 @@ function derive(build, mode) {
90
90
  }
91
91
  let manifestUrl = getManifestPath(build.routeDiscovery.manifestPath, build.basename);
92
92
  if (build.routeDiscovery.mode === "lazy" && requestUrl.pathname === manifestUrl) try {
93
- return await handleManifestRequest(build, staticHandler.dataRoutes, staticHandler._internalRouteBranches, requestUrl);
93
+ return await handleManifestRequest(build, staticHandler, requestUrl);
94
94
  } catch (e) {
95
95
  handleError(e);
96
96
  return new Response("Unknown Server Error", { status: 500 });
97
97
  }
98
- let matches = matchServerRoutes(build.routes, staticHandler.dataRoutes, staticHandler._internalRouteBranches, normalizedPathname, build.basename);
98
+ let matches = matchServerRoutes(build.routes, staticHandler, normalizedPathname);
99
99
  if (matches && matches.length > 0) Object.assign(params, matches[0].params);
100
100
  if (requestHandlerInstrumentations?.length) loadContext.set(instrumentationResultMetaContext, {
101
101
  url: createDataFunctionUrl(request, normalizedPath),
@@ -173,7 +173,7 @@ const createRequestHandler = (build, mode) => {
173
173
  return _requestHandler(request, initialContext);
174
174
  };
175
175
  };
176
- async function handleManifestRequest(build, dataRoutes, branches, url) {
176
+ async function handleManifestRequest(build, staticHandler, url) {
177
177
  if (url.toString().length > 7680) return new Response(null, {
178
178
  statusText: "Bad Request",
179
179
  status: 400
@@ -188,7 +188,7 @@ async function handleManifestRequest(build, dataRoutes, branches, url) {
188
188
  let paths = new Set(pathParam.split(",").filter(Boolean));
189
189
  for (let path of paths) {
190
190
  if (!path.startsWith("/")) path = `/${path}`;
191
- let matches = matchServerRoutes(build.routes, dataRoutes, branches, path, build.basename);
191
+ let matches = matchServerRoutes(build.routes, staticHandler, path);
192
192
  if (matches) for (let match of matches) {
193
193
  let routeId = match.route.id;
194
194
  let route = build.assets.routes[routeId];
@@ -256,7 +256,7 @@ async function handleDocumentRequest(serverMode, build, staticHandler, request,
256
256
  };
257
257
  let entryContext = {
258
258
  manifest: build.assets,
259
- branches: staticHandler._internalRouteBranches,
259
+ branches: [],
260
260
  routeModules: createEntryRouteModules(build.routes),
261
261
  staticHandlerContext: context,
262
262
  criticalCss,
@@ -281,7 +281,7 @@ async function handleDocumentRequest(serverMode, build, staticHandler, request,
281
281
  if (isResponse(error)) try {
282
282
  let data = await unwrapResponse(error);
283
283
  errorForSecondRender = new ErrorResponseImpl(error.status, error.statusText, data);
284
- } catch (e) {}
284
+ } catch {}
285
285
  context = getStaticContextFromError(staticHandler.dataRoutes, context, errorForSecondRender);
286
286
  if (context.errors) context.errors = sanitizeErrors(context.errors, serverMode);
287
287
  let state = {
@@ -291,6 +291,7 @@ async function handleDocumentRequest(serverMode, build, staticHandler, request,
291
291
  };
292
292
  entryContext = {
293
293
  ...entryContext,
294
+ branches: [],
294
295
  staticHandlerContext: context,
295
296
  serverHandoffString: createServerHandoffString(baseServerHandoff),
296
297
  serverHandoffStream: encodeViaTurboStream(state, request.signal, build.entry.module.streamTimeout, serverMode),
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
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.0
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.0
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.0
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.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -23,7 +23,7 @@ async function singleFetchAction(build, serverMode, staticHandler, request, load
23
23
  try {
24
24
  try {
25
25
  throwIfPotentialCSRFAttack(request, Array.isArray(build.allowedActionOrigins) ? build.allowedActionOrigins : []);
26
- } catch (e) {
26
+ } catch {
27
27
  return handleQueryError(/* @__PURE__ */ new Error("Bad Request"), 400);
28
28
  }
29
29
  return handleQueryResult(await staticHandler.query(request, {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
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.0
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.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -0,0 +1,3 @@
1
+
2
+ import { unstable_preloadRoutePattern } from "./lib/router/matcher-route-pattern.preload.js";
3
+ export { unstable_preloadRoutePattern };
@@ -0,0 +1,12 @@
1
+ /**
2
+ * react-router v8.4.0
3
+ *
4
+ * Copyright (c) Remix Software Inc.
5
+ *
6
+ * This source code is licensed under the MIT license found in the
7
+ * LICENSE.md file in the root directory of this source tree.
8
+ *
9
+ * @license MIT
10
+ */
11
+ import { unstable_preloadRoutePattern } from "./lib/router/matcher-route-pattern.preload.js";
12
+ export { unstable_preloadRoutePattern };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
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.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -41,7 +41,7 @@ async function decodeInitial(reader) {
41
41
  let line;
42
42
  try {
43
43
  line = JSON.parse(read.value);
44
- } catch (reason) {
44
+ } catch {
45
45
  throw new SyntaxError();
46
46
  }
47
47
  return {
@@ -64,7 +64,7 @@ async function decodeDeferred(reader) {
64
64
  let jsonLine;
65
65
  try {
66
66
  jsonLine = JSON.parse(lineData);
67
- } catch (reason) {
67
+ } catch {
68
68
  throw new SyntaxError();
69
69
  }
70
70
  const value = unflatten.call(this, jsonLine);
@@ -80,7 +80,7 @@ async function decodeDeferred(reader) {
80
80
  let jsonLine;
81
81
  try {
82
82
  jsonLine = JSON.parse(lineData);
83
- } catch (reason) {
83
+ } catch {
84
84
  throw new SyntaxError();
85
85
  }
86
86
  const value = unflatten.call(this, jsonLine);
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
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.0
2
+ * react-router v8.4.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -85,7 +85,7 @@ export default function Component() {
85
85
 
86
86
  ### 3. Submit the form
87
87
 
88
- If you submit the form now, the fetcher will call the action and revalidate the route data automatically.
88
+ If you submit the form now, the fetcher will call the action and revalidate the route data automatically. See [Revalidation Optimization](./optimize-revalidation) to skip that reload for a route or a single submission.
89
89
 
90
90
  ### 4. Render pending state
91
91
 
@@ -305,3 +305,8 @@ Fetchers can be submitted programmatically with `fetcher.submit`:
305
305
  ```
306
306
 
307
307
  Note the input event's form is passed as the first argument to `fetcher.submit`. The fetcher will use that form to submit the request, reading its attributes and serializing the data from its elements.
308
+
309
+ `fetcher.submit` accepts the same [`defaultShouldRevalidate`][fetcher-submit-options] option as [`<Form>`][form]. Pass `false` to skip loader revalidation for that submission. See [Revalidation Optimization](./optimize-revalidation) for how that interacts with route-level `shouldRevalidate`.
310
+
311
+ [fetcher-submit-options]: https://api.reactrouter.com/v8/interfaces/react-router.FetcherSubmitOptions.html#defaultshouldrevalidate
312
+ [form]: ../api/components/Form#defaultshouldrevalidate