react-router 8.3.0 → 8.3.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 (138) hide show
  1. package/CHANGELOG.md +13 -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 +8 -0
  5. package/dist/development/index-react-server.js +22 -13
  6. package/dist/development/index.js +1 -1
  7. package/dist/development/lib/actions.js +10 -5
  8. package/dist/development/lib/components.js +6 -3
  9. package/dist/development/lib/context.d.ts +1 -0
  10. package/dist/development/lib/context.js +1 -1
  11. package/dist/development/lib/dom/dom.js +2 -2
  12. package/dist/development/lib/dom/lib.js +20 -5
  13. package/dist/development/lib/dom/server.js +1 -1
  14. package/dist/development/lib/dom/ssr/components.js +2 -2
  15. package/dist/development/lib/dom/ssr/data.js +1 -1
  16. package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
  17. package/dist/development/lib/dom/ssr/fallback.js +1 -1
  18. package/dist/development/lib/dom/ssr/fog-of-war.js +2 -1
  19. package/dist/development/lib/dom/ssr/hydration.js +1 -1
  20. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  21. package/dist/development/lib/dom/ssr/links.js +1 -1
  22. package/dist/development/lib/dom/ssr/markup.js +1 -1
  23. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  24. package/dist/development/lib/dom/ssr/routes-test-stub.js +1 -1
  25. package/dist/development/lib/dom/ssr/routes.js +1 -1
  26. package/dist/development/lib/dom/ssr/server.js +1 -1
  27. package/dist/development/lib/dom/ssr/single-fetch.js +5 -5
  28. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  29. package/dist/development/lib/dom-export/hydrated-router.js +1 -1
  30. package/dist/development/lib/errors.js +1 -1
  31. package/dist/development/lib/hooks.js +6 -3
  32. package/dist/development/lib/href.js +1 -1
  33. package/dist/development/lib/router/history.js +2 -2
  34. package/dist/development/lib/router/instrumentation.js +1 -1
  35. package/dist/development/lib/router/navigation.js +45 -0
  36. package/dist/development/lib/router/router.d.ts +8 -0
  37. package/dist/development/lib/router/router.js +31 -17
  38. package/dist/development/lib/router/url.js +1 -1
  39. package/dist/development/lib/router/utils.js +10 -6
  40. package/dist/development/lib/rsc/browser.js +4 -1
  41. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  42. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  43. package/dist/development/lib/rsc/html-stream/server.js +2 -2
  44. package/dist/development/lib/rsc/route-modules.js +1 -1
  45. package/dist/development/lib/rsc/server.ssr.js +2 -2
  46. package/dist/development/lib/server-runtime/cookies.js +2 -2
  47. package/dist/development/lib/server-runtime/crypto.js +2 -2
  48. package/dist/development/lib/server-runtime/data.js +1 -1
  49. package/dist/development/lib/server-runtime/dev.js +2 -2
  50. package/dist/development/lib/server-runtime/entry.js +1 -1
  51. package/dist/development/lib/server-runtime/errors.js +1 -1
  52. package/dist/development/lib/server-runtime/headers.js +1 -1
  53. package/dist/development/lib/server-runtime/invariant.js +1 -1
  54. package/dist/development/lib/server-runtime/mode.js +1 -1
  55. package/dist/development/lib/server-runtime/routeMatching.js +1 -1
  56. package/dist/development/lib/server-runtime/routes.js +1 -1
  57. package/dist/development/lib/server-runtime/server.js +2 -2
  58. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  59. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +1 -1
  60. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +1 -1
  61. package/dist/development/lib/server-runtime/sessions.js +1 -1
  62. package/dist/development/lib/server-runtime/single-fetch.js +2 -2
  63. package/dist/development/lib/server-runtime/urls.js +1 -1
  64. package/dist/development/lib/server-runtime/warnings.js +1 -1
  65. package/dist/development/lib/types/internal.js +1 -1
  66. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  67. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +4 -4
  68. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  69. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  70. package/dist/production/dom-export.js +1 -1
  71. package/dist/production/index-react-server-client.js +1 -1
  72. package/dist/production/index-react-server.d.ts +8 -0
  73. package/dist/production/index-react-server.js +22 -13
  74. package/dist/production/index.js +1 -1
  75. package/dist/production/lib/actions.js +10 -5
  76. package/dist/production/lib/components.js +6 -3
  77. package/dist/production/lib/context.d.ts +1 -0
  78. package/dist/production/lib/context.js +1 -1
  79. package/dist/production/lib/dom/dom.js +2 -2
  80. package/dist/production/lib/dom/lib.js +20 -5
  81. package/dist/production/lib/dom/server.js +1 -1
  82. package/dist/production/lib/dom/ssr/components.js +2 -2
  83. package/dist/production/lib/dom/ssr/data.js +1 -1
  84. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  85. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  86. package/dist/production/lib/dom/ssr/fog-of-war.js +2 -1
  87. package/dist/production/lib/dom/ssr/hydration.js +1 -1
  88. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  89. package/dist/production/lib/dom/ssr/links.js +1 -1
  90. package/dist/production/lib/dom/ssr/markup.js +1 -1
  91. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  92. package/dist/production/lib/dom/ssr/routes-test-stub.js +1 -1
  93. package/dist/production/lib/dom/ssr/routes.js +1 -1
  94. package/dist/production/lib/dom/ssr/server.js +1 -1
  95. package/dist/production/lib/dom/ssr/single-fetch.js +5 -5
  96. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  97. package/dist/production/lib/dom-export/hydrated-router.js +1 -1
  98. package/dist/production/lib/errors.js +1 -1
  99. package/dist/production/lib/hooks.js +6 -3
  100. package/dist/production/lib/href.js +1 -1
  101. package/dist/production/lib/router/history.js +2 -2
  102. package/dist/production/lib/router/instrumentation.js +1 -1
  103. package/dist/production/lib/router/navigation.js +45 -0
  104. package/dist/production/lib/router/router.d.ts +8 -0
  105. package/dist/production/lib/router/router.js +31 -17
  106. package/dist/production/lib/router/url.js +1 -1
  107. package/dist/production/lib/router/utils.js +10 -6
  108. package/dist/production/lib/rsc/browser.js +4 -1
  109. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  110. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  111. package/dist/production/lib/rsc/html-stream/server.js +2 -2
  112. package/dist/production/lib/rsc/route-modules.js +1 -1
  113. package/dist/production/lib/rsc/server.ssr.js +2 -2
  114. package/dist/production/lib/server-runtime/cookies.js +2 -2
  115. package/dist/production/lib/server-runtime/crypto.js +2 -2
  116. package/dist/production/lib/server-runtime/data.js +1 -1
  117. package/dist/production/lib/server-runtime/dev.js +2 -2
  118. package/dist/production/lib/server-runtime/entry.js +1 -1
  119. package/dist/production/lib/server-runtime/errors.js +1 -1
  120. package/dist/production/lib/server-runtime/headers.js +1 -1
  121. package/dist/production/lib/server-runtime/invariant.js +1 -1
  122. package/dist/production/lib/server-runtime/mode.js +1 -1
  123. package/dist/production/lib/server-runtime/routeMatching.js +1 -1
  124. package/dist/production/lib/server-runtime/routes.js +1 -1
  125. package/dist/production/lib/server-runtime/server.js +2 -2
  126. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  127. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +1 -1
  128. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +1 -1
  129. package/dist/production/lib/server-runtime/sessions.js +1 -1
  130. package/dist/production/lib/server-runtime/single-fetch.js +2 -2
  131. package/dist/production/lib/server-runtime/urls.js +1 -1
  132. package/dist/production/lib/server-runtime/warnings.js +1 -1
  133. package/dist/production/lib/types/internal.js +1 -1
  134. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  135. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +4 -4
  136. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  137. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  138. package/package.json +1 -1
package/CHANGELOG.md CHANGED
@@ -1,5 +1,17 @@
1
1
  # `react-router`
2
2
 
3
+ ## v8.3.1
4
+
5
+ ### Patch Changes
6
+
7
+ - Fix `Expected fetcher: <key>` error thrown on navigation when a fetcher is aborted during its post-action revalidation ([#15365](https://github.com/remix-run/react-router/pull/15365))
8
+ - Fix lazy route discovery caching a path as discovered when the triggering navigation was aborted after the manifest response settled but before the route tree was patched, which permanently (for the session) shadowed the real route behind a catch-all or produced 404s on every subsequent visit ([#15399](https://github.com/remix-run/react-router/pull/15399))
9
+ - Improve route matching performance for long paths ([#15417](https://github.com/remix-run/react-router/pull/15417))
10
+ - Improve validation of action request origins ([#15419](https://github.com/remix-run/react-router/pull/15419))
11
+ - Fix `<ScrollRestoration>` leaving `history.scrollRestoration` set to `"auto"` after a bfcache restore, which let the browser restore scroll on subsequent history traversals before the destination route had rendered ([#15397](https://github.com/remix-run/react-router/pull/15397))
12
+ - Properly respect the `relative` option in `useSubmit`/`fetcher.submit` when resolivng the `action` path ([#15400](https://github.com/remix-run/react-router/pull/15400))
13
+ - Add additional URL validation on client side navigations/redirects ([#15445](https://github.com/remix-run/react-router/pull/15445))
14
+
3
15
  ## v8.3.0
4
16
 
5
17
  ### Patch Changes
@@ -17,7 +29,7 @@
17
29
 
18
30
  ### Unstable Changes
19
31
 
20
- ⚠️ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
32
+ ⚠️ _[Unstable features](https://reactrouter.com/community/api-development-strategy#unstable-flags) are not recommended for production use_
21
33
 
22
34
  - Detect stale RSC clients during lazy route discovery and reload the destination document ([#15318](https://github.com/remix-run/react-router/pull/15318))
23
35
 
@@ -35,7 +47,6 @@
35
47
  ```
36
48
 
37
49
  - Add CSP nonce support to RSC document rendering ([#15320](https://github.com/remix-run/react-router/pull/15320))
38
-
39
50
  - Add `nonce` options to `unstable_routeRSCServerRequest` and `unstable_RSCStaticRouter`
40
51
  - Forward the nonce to the HTML renderer and apply it to injected RSC payload scripts and nonce-aware framework components
41
52
 
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
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.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -1231,6 +1231,14 @@ interface Router$1 {
1231
1231
  * @param location
1232
1232
  */
1233
1233
  createHref(location: Location | URL): string;
1234
+ /**
1235
+ * @private
1236
+ * PRIVATE - DO NOT USE
1237
+ *
1238
+ * Utility function to create a URL for the given location
1239
+ * @param location
1240
+ */
1241
+ createURL?(to: To): URL;
1234
1242
  /**
1235
1243
  * @private
1236
1244
  * PRIVATE - DO NOT USE
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -24,7 +24,7 @@ function warning(cond, message) {
24
24
  if (typeof console !== "undefined") console.warn(message);
25
25
  try {
26
26
  throw new Error(message);
27
- } catch (e) {}
27
+ } catch {}
28
28
  }
29
29
  }
30
30
  function createKey$1() {
@@ -515,13 +515,13 @@ function matchPathImpl(pattern, pathname, matcher, compiledParams) {
515
515
  let match = pathname.match(matcher);
516
516
  if (!match) return null;
517
517
  let matchedPathname = match[0];
518
- let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
518
+ let pathnameBase = removeTrailingSlash(matchedPathname, 1);
519
519
  let captureGroups = match.slice(1);
520
520
  return {
521
521
  params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
522
522
  if (paramName === "*") {
523
523
  let splatValue = captureGroups[index] || "";
524
- pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
524
+ pathnameBase = removeTrailingSlash(matchedPathname.slice(0, matchedPathname.length - splatValue.length), 1);
525
525
  }
526
526
  const value = captureGroups[index];
527
527
  if (isOptional && !value) memo[paramName] = void 0;
@@ -590,7 +590,7 @@ function resolvePath(to, fromPathname = "/") {
590
590
  let pathname;
591
591
  if (toPathname) {
592
592
  toPathname = removeDoubleSlashes(toPathname);
593
- if (toPathname.startsWith("/")) pathname = resolvePathname(toPathname.substring(1), "/");
593
+ if (toPathname.startsWith("/") || toPathname.startsWith("\\")) pathname = resolvePathname(toPathname.substring(1), "/");
594
594
  else pathname = resolvePathname(toPathname, fromPathname);
595
595
  } else pathname = fromPathname;
596
596
  return {
@@ -651,7 +651,11 @@ function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = fal
651
651
  }
652
652
  const removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
653
653
  const joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
654
- const removeTrailingSlash = (path) => path.replace(/\/+$/, "");
654
+ function removeTrailingSlash(path, minLength = 0) {
655
+ let end = path.length;
656
+ while (end > minLength && path.charCodeAt(end - 1) === 47) end--;
657
+ return end === path.length ? path : path.slice(0, end);
658
+ }
655
659
  const normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
656
660
  const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
657
661
  const normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
@@ -1816,7 +1820,7 @@ async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fe
1816
1820
  });
1817
1821
  try {
1818
1822
  await Promise.all(matches.flatMap((m) => [m._lazyPromises?.handler, m._lazyPromises?.route]));
1819
- } catch (e) {}
1823
+ } catch {}
1820
1824
  return results;
1821
1825
  }
1822
1826
  async function callLoaderOrAction({ request, path, pattern, match, lazyHandlerPromise, lazyRoutePromise, handlerOverride, scopedContext }) {
@@ -2217,14 +2221,19 @@ function getPathsWithAncestors(paths) {
2217
2221
  function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
2218
2222
  let originHeader = request.headers.get("origin");
2219
2223
  let originDomain = null;
2224
+ let originUrl = null;
2220
2225
  try {
2221
- originDomain = typeof originHeader === "string" && originHeader !== "null" ? new URL(originHeader).host : originHeader;
2226
+ if (typeof originHeader === "string" && originHeader !== "null") {
2227
+ originUrl = new URL(originHeader);
2228
+ originDomain = originUrl.host;
2229
+ } else originDomain = originHeader;
2222
2230
  } catch {
2223
2231
  throw new Error(`\`origin\` header is not a valid URL. Aborting the action.`);
2224
2232
  }
2225
- let host = new URL(request.url).host;
2226
- if (originDomain && originDomain !== host) {
2227
- if (!isAllowedOrigin(originDomain, allowedActionOrigins)) throw new Error("The `request.url` host does not match `origin` header from a forwarded action request. Aborting the action.");
2233
+ let requestUrl = new URL(request.url);
2234
+ let originMatchesRequest = originUrl ? originUrl.origin === requestUrl.origin : originDomain === requestUrl.host;
2235
+ if (originDomain && !originMatchesRequest) {
2236
+ if (!isAllowedOrigin(originDomain, allowedActionOrigins)) throw new Error("The `request.url` origin does not match `origin` header from a forwarded action request. Aborting the action.");
2228
2237
  }
2229
2238
  }
2230
2239
  function matchWildcardDomain(domain, pattern) {
@@ -2948,7 +2957,7 @@ const unsign = async (cookie, secret) => {
2948
2957
  try {
2949
2958
  let signature = byteStringToUint8Array(atob(hash));
2950
2959
  return await crypto.subtle.verify("HMAC", key, signature, data) ? value : false;
2951
- } catch (e) {
2960
+ } catch {
2952
2961
  return false;
2953
2962
  }
2954
2963
  };
@@ -3046,7 +3055,7 @@ function encodeData(value) {
3046
3055
  function decodeData(value) {
3047
3056
  try {
3048
3057
  return JSON.parse(decodeURIComponent(myEscape(atob(value))));
3049
- } catch (e) {
3058
+ } catch {
3050
3059
  return {};
3051
3060
  }
3052
3061
  }
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
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.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -12,14 +12,19 @@
12
12
  function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
13
13
  let originHeader = request.headers.get("origin");
14
14
  let originDomain = null;
15
+ let originUrl = null;
15
16
  try {
16
- originDomain = typeof originHeader === "string" && originHeader !== "null" ? new URL(originHeader).host : originHeader;
17
+ if (typeof originHeader === "string" && originHeader !== "null") {
18
+ originUrl = new URL(originHeader);
19
+ originDomain = originUrl.host;
20
+ } else originDomain = originHeader;
17
21
  } catch {
18
22
  throw new Error(`\`origin\` header is not a valid URL. Aborting the action.`);
19
23
  }
20
- let host = new URL(request.url).host;
21
- if (originDomain && originDomain !== host) {
22
- if (!isAllowedOrigin(originDomain, allowedActionOrigins)) throw new Error("The `request.url` host does not match `origin` header from a forwarded action request. Aborting the action.");
24
+ let requestUrl = new URL(request.url);
25
+ let originMatchesRequest = originUrl ? originUrl.origin === requestUrl.origin : originDomain === requestUrl.host;
26
+ if (originDomain && !originMatchesRequest) {
27
+ if (!isAllowedOrigin(originDomain, allowedActionOrigins)) throw new Error("The `request.url` origin does not match `origin` header from a forwarded action request. Aborting the action.");
23
28
  }
24
29
  }
25
30
  function matchWildcardDomain(domain, pattern) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -8,8 +8,9 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import { createMemoryHistory, invariant, parsePath, warning } from "./router/history.js";
11
+ import { createMemoryHistory, createPath, invariant, parsePath, warning } from "./router/history.js";
12
12
  import { defaultMapRouteProperties, getResolveToMatches, getRoutePattern, resolveTo, stripBasename } from "./router/utils.js";
13
+ import { getNavigatorCurrentUrl, validateNavigationTarget } from "./router/navigation.js";
13
14
  import { createRouter } from "./router/router.js";
14
15
  import { AwaitContext, DataRouterContext, DataRouterStateContext, FetchersContext, LocationContext, NavigationContext, RouteContext, ViewTransitionContext, useIsRSCRouterContext } from "./context.js";
15
16
  import { _renderMatches, useActionData, useAsyncValue, useInRouterContext, useLoaderData, useLocation, useMatches, useNavigate, useOutlet, useParams, useRouteError, useRoutes, useRoutesImpl } from "./hooks.js";
@@ -256,6 +257,7 @@ function RouterProvider({ router, flushSync: reactDomFlushSyncImpl, onError, use
256
257
  let navigator = React$1.useMemo(() => {
257
258
  return {
258
259
  createHref: router.createHref,
260
+ createURL: router.createURL,
259
261
  encodeLocation: router.encodeLocation,
260
262
  go: (n) => router.navigate(n),
261
263
  push: (to, state, opts) => router.navigate(to, {
@@ -379,12 +381,13 @@ function MemoryRouter({ basename, children, initialEntries, initialIndex, useTra
379
381
  */
380
382
  function Navigate({ to, replace, state, relative }) {
381
383
  invariant(useInRouterContext(), `<Navigate> may be used only in the context of a <Router> component.`);
382
- let { static: isStatic } = React$1.useContext(NavigationContext);
384
+ let { static: isStatic, navigator } = React$1.useContext(NavigationContext);
383
385
  warning(!isStatic, "<Navigate> must not be used on the initial render in a <StaticRouter>. This is a no-op, but you should modify your code so the <Navigate> is only ever rendered in response to some user interaction or state change.");
384
386
  let { matches } = React$1.useContext(RouteContext);
385
387
  let { pathname: locationPathname } = useLocation();
386
388
  let navigate = useNavigate();
387
389
  let path = resolveTo(to, getResolveToMatches(matches), locationPathname, relative === "path");
390
+ validateNavigationTarget(typeof to === "string" ? to : createPath(to), navigator.createHref(path), getNavigatorCurrentUrl(navigator), "reject");
388
391
  let jsonPath = JSON.stringify(path);
389
392
  React$1.useEffect(() => {
390
393
  navigate(JSON.parse(jsonPath), {
@@ -55,6 +55,7 @@ interface NavigateOptions {
55
55
  */
56
56
  interface Navigator {
57
57
  createHref: History["createHref"];
58
+ createURL?: History["createURL"];
58
59
  encodeLocation?: History["encodeLocation"];
59
60
  go: History["go"];
60
61
  push(to: To, state?: any, opts?: NavigateOptions): void;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
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.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -75,7 +75,7 @@ function isFormDataSubmitterSupported() {
75
75
  if (_formDataSupportsSubmitter === null) try {
76
76
  new FormData(document.createElement("form"), 0);
77
77
  _formDataSupportsSubmitter = false;
78
- } catch (e) {
78
+ } catch {
79
79
  _formDataSupportsSubmitter = true;
80
80
  }
81
81
  return _formDataSupportsSubmitter;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -22,8 +22,8 @@ import * as React$1 from "react";
22
22
  //#region lib/dom/lib.tsx
23
23
  const isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
24
24
  try {
25
- if (isBrowser) window.__reactRouterVersion = "8.3.0";
26
- } catch (e) {}
25
+ if (isBrowser) window.__reactRouterVersion = "8";
26
+ } catch {}
27
27
  /**
28
28
  * Create a new {@link DataRouter| data router} that manages the application
29
29
  * path via [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)
@@ -123,7 +123,7 @@ function deserializeErrors(errors) {
123
123
  let error = new ErrorConstructor(val.message);
124
124
  error.stack = "";
125
125
  serialized[key] = error;
126
- } catch (e) {}
126
+ } catch {}
127
127
  }
128
128
  if (serialized[key] == null) {
129
129
  let error = new Error(val.message);
@@ -796,6 +796,7 @@ function useSubmit() {
796
796
  if (options.navigate === false) await routerFetch(options.fetcherKey || getUniqueFetcherId(), currentRouteId, options.action || action, {
797
797
  defaultShouldRevalidate: options.defaultShouldRevalidate,
798
798
  preventScrollReset: options.preventScrollReset,
799
+ relative: options.relative,
799
800
  formData,
800
801
  body,
801
802
  formMethod: options.method || method,
@@ -805,6 +806,7 @@ function useSubmit() {
805
806
  else await routerNavigate(options.action || action, {
806
807
  defaultShouldRevalidate: options.defaultShouldRevalidate,
807
808
  preventScrollReset: options.preventScrollReset,
809
+ relative: options.relative,
808
810
  formData,
809
811
  body,
810
812
  formMethod: options.method || method,
@@ -1087,6 +1089,9 @@ function useScrollRestoration({ getKey, storageKey } = {}) {
1087
1089
  window.history.scrollRestoration = "auto";
1088
1090
  };
1089
1091
  }, []);
1092
+ usePageShow(React$1.useCallback((event) => {
1093
+ if (event.persisted) window.history.scrollRestoration = "manual";
1094
+ }, []));
1090
1095
  usePageHide(React$1.useCallback(() => {
1091
1096
  if (navigation.state === "idle") {
1092
1097
  let key = getScrollRestorationKey(location, matches, basename, getKey);
@@ -1111,7 +1116,7 @@ function useScrollRestoration({ getKey, storageKey } = {}) {
1111
1116
  try {
1112
1117
  let sessionPositions = sessionStorage.getItem(storageKey || SCROLL_RESTORATION_STORAGE_KEY);
1113
1118
  if (sessionPositions) savedScrollPositions = JSON.parse(sessionPositions);
1114
- } catch (e) {}
1119
+ } catch {}
1115
1120
  }, [storageKey]);
1116
1121
  React$1.useLayoutEffect(() => {
1117
1122
  let disableScrollRestoration = router?.enableScrollRestoration(savedScrollPositions, () => window.scrollY, getKey ? (location, matches) => getScrollRestorationKey(location, matches, basename, getKey) : void 0);
@@ -1179,6 +1184,16 @@ function usePageHide(callback, options) {
1179
1184
  };
1180
1185
  }, [callback, capture]);
1181
1186
  }
1187
+ function usePageShow(callback, options) {
1188
+ let { capture } = options || {};
1189
+ React$1.useEffect(() => {
1190
+ let opts = capture != null ? { capture } : void 0;
1191
+ window.addEventListener("pageshow", callback, opts);
1192
+ return () => {
1193
+ window.removeEventListener("pageshow", callback, opts);
1194
+ };
1195
+ }, [callback, capture]);
1196
+ }
1182
1197
  /**
1183
1198
  * Wrapper around {@link useBlocker} to show a [`window.confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
1184
1199
  * prompt to users instead of building a custom UI with {@link useBlocker}.
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
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.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -425,7 +425,7 @@ function Meta() {
425
425
  type: "application/ld+json",
426
426
  dangerouslySetInnerHTML: { __html: escapeHtml(json) }
427
427
  });
428
- } catch (e) {
428
+ } catch {
429
429
  return null;
430
430
  }
431
431
  return /* @__PURE__ */ React$1.createElement("meta", {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
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.3.1
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.3.1
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.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -154,6 +154,7 @@ async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, ro
154
154
  if (signal?.aborted) return;
155
155
  throw e;
156
156
  }
157
+ if (signal?.aborted) return;
157
158
  let knownRoutes = new Set(Object.keys(manifest.routes));
158
159
  let patches = Object.values(serverPatches).reduce((acc, route) => {
159
160
  if (route && !knownRoutes.has(route.id)) acc[route.id] = route;
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
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.3.1
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.3.1
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.3.1
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.3.1
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.3.1
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.3.1
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.3.1
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.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -205,7 +205,7 @@ async function bubbleMiddlewareErrors(singleFetchPromise, matches, routesParams,
205
205
  if (middlewareError !== void 0) Array.from(routesParams.values()).forEach((routeId) => {
206
206
  if (results[routeId].result instanceof SingleFetchNoResultError) results[routeId].result = middlewareError;
207
207
  });
208
- } catch (e) {}
208
+ } catch {}
209
209
  }
210
210
  async function singleFetchLoaderFetcherStrategy(args, fetchAndDecode) {
211
211
  let fetcherMatch = args.matches.find((m) => m.shouldCallHandler());
@@ -277,7 +277,7 @@ async function fetchAndDecodeViaTurboStream(args, targetRoutes) {
277
277
  status: res.status,
278
278
  data
279
279
  };
280
- } catch (e) {
280
+ } catch {
281
281
  throw new Error("Unable to decode turbo-stream response");
282
282
  }
283
283
  }
@@ -326,13 +326,13 @@ function createDeferred() {
326
326
  res(val);
327
327
  try {
328
328
  await promise;
329
- } catch (e) {}
329
+ } catch {}
330
330
  };
331
331
  reject = async (error) => {
332
332
  rej(error);
333
333
  try {
334
334
  await promise;
335
- } catch (e) {}
335
+ } catch {}
336
336
  };
337
337
  });
338
338
  return {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
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.3.1
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.3.1
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.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -8,8 +8,9 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
- import { invariant, parsePath, warning } from "./router/history.js";
11
+ import { createPath, invariant, parsePath, warning } from "./router/history.js";
12
12
  import { convertRouteMatchToUiMatch, decodePath, getResolveToMatches, getRoutePattern, isBrowser, isRouteErrorResponse, joinPaths, matchPath, matchRoutes, parseToInfo, resolveTo, stripBasename } from "./router/utils.js";
13
+ import { getNavigatorCurrentUrl, validateNavigationTarget } from "./router/navigation.js";
13
14
  import { IDLE_BLOCKER, hasInvalidProtocol } from "./router/router.js";
14
15
  import { AwaitContext, DataRouterContext, DataRouterStateContext, LocationContext, NavigationContext, RSCRouterContext, RouteContext, RouteErrorContext } from "./context.js";
15
16
  import { decodeRedirectErrorDigest, decodeRouteErrorResponseDigest } from "./errors.js";
@@ -291,6 +292,7 @@ function useNavigateUnstable() {
291
292
  }
292
293
  let path = resolveTo(to, JSON.parse(routePathnamesJson), locationPathname, options.relative === "path");
293
294
  if (dataRouterContext == null && basename !== "/") path.pathname = path.pathname === "/" ? basename : joinPaths([basename, path.pathname]);
295
+ validateNavigationTarget(typeof to === "string" ? to : createPath(to), navigator.createHref(path), getNavigatorCurrentUrl(navigator), "reject");
294
296
  (!!options.replace ? navigator.replace : navigator.push)(path, options.state, options);
295
297
  }, [
296
298
  basename,
@@ -687,7 +689,7 @@ var RenderErrorBoundary = class extends React$1.Component {
687
689
  };
688
690
  const errorRedirectHandledMap = /* @__PURE__ */ new WeakMap();
689
691
  function RSCErrorHandler({ children, error }) {
690
- let { basename } = React$1.useContext(NavigationContext);
692
+ let { basename, navigator } = React$1.useContext(NavigationContext);
691
693
  if (typeof error === "object" && error && "digest" in error && typeof error.digest === "string") {
692
694
  let redirect = decodeRedirectErrorDigest(error.digest);
693
695
  if (redirect) {
@@ -695,6 +697,7 @@ function RSCErrorHandler({ children, error }) {
695
697
  if (existingRedirect) throw existingRedirect;
696
698
  let parsed = parseToInfo(redirect.location, basename);
697
699
  let target = parsed.absoluteURL || parsed.to;
700
+ validateNavigationTarget(redirect.location, target, getNavigatorCurrentUrl(navigator), "allow-explicit");
698
701
  if (hasInvalidProtocol(target)) throw new Error("Invalid redirect location");
699
702
  if (isBrowser && !errorRedirectHandledMap.get(error)) if (parsed.isExternal || redirect.reloadDocument) window.location.href = target;
700
703
  else {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
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.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -193,7 +193,7 @@ function warning(cond, message) {
193
193
  if (typeof console !== "undefined") console.warn(message);
194
194
  try {
195
195
  throw new Error(message);
196
- } catch (e) {}
196
+ } catch {}
197
197
  }
198
198
  }
199
199
  function createKey() {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.3.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *