react-router 8.2.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 (175) hide show
  1. package/CHANGELOG.md +78 -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 +114 -17
  5. package/dist/development/index-react-server.js +189 -54
  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.d.ts +24 -24
  12. package/dist/development/lib/dom/dom.js +26 -26
  13. package/dist/development/lib/dom/lib.d.ts +2 -1
  14. package/dist/development/lib/dom/lib.js +23 -7
  15. package/dist/development/lib/dom/server.js +1 -1
  16. package/dist/development/lib/dom/ssr/components.js +2 -2
  17. package/dist/development/lib/dom/ssr/data.js +1 -1
  18. package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
  19. package/dist/development/lib/dom/ssr/fallback.js +1 -1
  20. package/dist/development/lib/dom/ssr/fog-of-war.js +27 -21
  21. package/dist/development/lib/dom/ssr/hydration.js +1 -1
  22. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  23. package/dist/development/lib/dom/ssr/links.js +1 -1
  24. package/dist/development/lib/dom/ssr/markup.js +1 -1
  25. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  26. package/dist/development/lib/dom/ssr/routes-test-stub.d.ts +13 -0
  27. package/dist/development/lib/dom/ssr/routes-test-stub.js +14 -1
  28. package/dist/development/lib/dom/ssr/routes.js +1 -1
  29. package/dist/development/lib/dom/ssr/server.js +1 -1
  30. package/dist/development/lib/dom/ssr/single-fetch.js +5 -5
  31. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  32. package/dist/development/lib/dom-export/hydrated-router.js +1 -1
  33. package/dist/development/lib/errors.js +1 -1
  34. package/dist/development/lib/hooks.js +6 -3
  35. package/dist/development/lib/href.d.ts +24 -8
  36. package/dist/development/lib/href.js +28 -11
  37. package/dist/development/lib/router/history.d.ts +6 -0
  38. package/dist/development/lib/router/history.js +8 -2
  39. package/dist/development/lib/router/instrumentation.js +1 -1
  40. package/dist/development/lib/router/navigation.js +45 -0
  41. package/dist/development/lib/router/router.d.ts +8 -0
  42. package/dist/development/lib/router/router.js +31 -17
  43. package/dist/development/lib/router/url.js +1 -1
  44. package/dist/development/lib/router/utils.d.ts +12 -0
  45. package/dist/development/lib/router/utils.js +66 -8
  46. package/dist/development/lib/rsc/browser.js +37 -24
  47. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  48. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  49. package/dist/development/lib/rsc/html-stream/server.js +30 -11
  50. package/dist/development/lib/rsc/route-modules.js +1 -1
  51. package/dist/development/lib/rsc/server.rsc.d.ts +5 -1
  52. package/dist/development/lib/rsc/server.ssr.d.ts +29 -6
  53. package/dist/development/lib/rsc/server.ssr.js +28 -13
  54. package/dist/development/lib/server-runtime/cookies.d.ts +28 -2
  55. package/dist/development/lib/server-runtime/cookies.js +18 -4
  56. package/dist/development/lib/server-runtime/crypto.js +2 -2
  57. package/dist/development/lib/server-runtime/data.js +1 -1
  58. package/dist/development/lib/server-runtime/dev.js +2 -2
  59. package/dist/development/lib/server-runtime/entry.js +1 -1
  60. package/dist/development/lib/server-runtime/errors.js +1 -1
  61. package/dist/development/lib/server-runtime/headers.js +1 -1
  62. package/dist/development/lib/server-runtime/invariant.js +1 -1
  63. package/dist/development/lib/server-runtime/mode.js +1 -1
  64. package/dist/development/lib/server-runtime/routeMatching.js +1 -1
  65. package/dist/development/lib/server-runtime/routes.js +1 -1
  66. package/dist/development/lib/server-runtime/server.d.ts +12 -0
  67. package/dist/development/lib/server-runtime/server.js +14 -2
  68. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  69. package/dist/development/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  70. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +9 -1
  71. package/dist/development/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  72. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +12 -6
  73. package/dist/development/lib/server-runtime/sessions.d.ts +31 -2
  74. package/dist/development/lib/server-runtime/sessions.js +20 -3
  75. package/dist/development/lib/server-runtime/single-fetch.js +2 -2
  76. package/dist/development/lib/server-runtime/urls.js +1 -1
  77. package/dist/development/lib/server-runtime/warnings.js +1 -1
  78. package/dist/development/lib/types/internal.js +1 -1
  79. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  80. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +4 -4
  81. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  82. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  83. package/dist/production/dom-export.js +1 -1
  84. package/dist/production/index-react-server-client.js +1 -1
  85. package/dist/production/index-react-server.d.ts +114 -17
  86. package/dist/production/index-react-server.js +189 -54
  87. package/dist/production/index.js +1 -1
  88. package/dist/production/lib/actions.js +10 -5
  89. package/dist/production/lib/components.js +6 -3
  90. package/dist/production/lib/context.d.ts +1 -0
  91. package/dist/production/lib/context.js +1 -1
  92. package/dist/production/lib/dom/dom.d.ts +24 -24
  93. package/dist/production/lib/dom/dom.js +26 -26
  94. package/dist/production/lib/dom/lib.d.ts +2 -1
  95. package/dist/production/lib/dom/lib.js +23 -7
  96. package/dist/production/lib/dom/server.js +1 -1
  97. package/dist/production/lib/dom/ssr/components.js +2 -2
  98. package/dist/production/lib/dom/ssr/data.js +1 -1
  99. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  100. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  101. package/dist/production/lib/dom/ssr/fog-of-war.js +27 -21
  102. package/dist/production/lib/dom/ssr/hydration.js +1 -1
  103. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  104. package/dist/production/lib/dom/ssr/links.js +1 -1
  105. package/dist/production/lib/dom/ssr/markup.js +1 -1
  106. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  107. package/dist/production/lib/dom/ssr/routes-test-stub.d.ts +13 -0
  108. package/dist/production/lib/dom/ssr/routes-test-stub.js +14 -1
  109. package/dist/production/lib/dom/ssr/routes.js +1 -1
  110. package/dist/production/lib/dom/ssr/server.js +1 -1
  111. package/dist/production/lib/dom/ssr/single-fetch.js +5 -5
  112. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  113. package/dist/production/lib/dom-export/hydrated-router.js +1 -1
  114. package/dist/production/lib/errors.js +1 -1
  115. package/dist/production/lib/hooks.js +6 -3
  116. package/dist/production/lib/href.d.ts +24 -8
  117. package/dist/production/lib/href.js +28 -11
  118. package/dist/production/lib/router/history.d.ts +6 -0
  119. package/dist/production/lib/router/history.js +8 -2
  120. package/dist/production/lib/router/instrumentation.js +1 -1
  121. package/dist/production/lib/router/navigation.js +45 -0
  122. package/dist/production/lib/router/router.d.ts +8 -0
  123. package/dist/production/lib/router/router.js +31 -17
  124. package/dist/production/lib/router/url.js +1 -1
  125. package/dist/production/lib/router/utils.d.ts +12 -0
  126. package/dist/production/lib/router/utils.js +66 -8
  127. package/dist/production/lib/rsc/browser.js +37 -24
  128. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  129. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  130. package/dist/production/lib/rsc/html-stream/server.js +30 -11
  131. package/dist/production/lib/rsc/route-modules.js +1 -1
  132. package/dist/production/lib/rsc/server.rsc.d.ts +5 -1
  133. package/dist/production/lib/rsc/server.ssr.d.ts +29 -6
  134. package/dist/production/lib/rsc/server.ssr.js +28 -13
  135. package/dist/production/lib/server-runtime/cookies.d.ts +28 -2
  136. package/dist/production/lib/server-runtime/cookies.js +18 -4
  137. package/dist/production/lib/server-runtime/crypto.js +2 -2
  138. package/dist/production/lib/server-runtime/data.js +1 -1
  139. package/dist/production/lib/server-runtime/dev.js +2 -2
  140. package/dist/production/lib/server-runtime/entry.js +1 -1
  141. package/dist/production/lib/server-runtime/errors.js +1 -1
  142. package/dist/production/lib/server-runtime/headers.js +1 -1
  143. package/dist/production/lib/server-runtime/invariant.js +1 -1
  144. package/dist/production/lib/server-runtime/mode.js +1 -1
  145. package/dist/production/lib/server-runtime/routeMatching.js +1 -1
  146. package/dist/production/lib/server-runtime/routes.js +1 -1
  147. package/dist/production/lib/server-runtime/server.d.ts +12 -0
  148. package/dist/production/lib/server-runtime/server.js +14 -2
  149. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  150. package/dist/production/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  151. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +9 -1
  152. package/dist/production/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  153. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +12 -6
  154. package/dist/production/lib/server-runtime/sessions.d.ts +31 -2
  155. package/dist/production/lib/server-runtime/sessions.js +20 -3
  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/vendor/turbo-stream-v2/flatten.js +1 -1
  161. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +4 -4
  162. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  163. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  164. package/docs/explanation/sessions-and-cookies.md +13 -13
  165. package/docs/explanation/state-management.md +2 -2
  166. package/docs/how-to/presets.md +2 -2
  167. package/docs/how-to/react-server-components.md +53 -1
  168. package/docs/how-to/security.md +8 -1
  169. package/docs/how-to/server-bundles.md +2 -2
  170. package/docs/start/data/route-object.md +2 -2
  171. package/docs/start/data/routing.md +1 -1
  172. package/docs/start/framework/pending-ui.md +1 -1
  173. package/docs/start/framework/route-module.md +8 -8
  174. package/docs/start/framework/routing.md +1 -1
  175. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.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() {
@@ -231,7 +231,10 @@ function createLocation(current, to, state = null, key, mask) {
231
231
  /**
232
232
  * Creates a string URL path from the given pathname, search, and hash components.
233
233
  *
234
+ * @public
234
235
  * @category Utils
236
+ * @param path The pathname, search, and hash components to combine.
237
+ * @returns The combined URL path.
235
238
  */
236
239
  function createPath({ pathname = "/", search = "", hash = "" }) {
237
240
  if (search && search !== "?") pathname += search.charAt(0) === "?" ? search : "?" + search;
@@ -241,7 +244,10 @@ function createPath({ pathname = "/", search = "", hash = "" }) {
241
244
  /**
242
245
  * Parses a string URL path into its separate pathname, search, and hash components.
243
246
  *
247
+ * @public
244
248
  * @category Utils
249
+ * @param path The URL path to parse.
250
+ * @returns The parsed pathname, search, and hash components.
245
251
  */
246
252
  function parsePath(path) {
247
253
  let parsedPath = {};
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -0,0 +1,45 @@
1
+ /**
2
+ * react-router v8.3.1
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
+ //#region lib/router/navigation.ts
12
+ const DEFAULT_NAVIGATION_URL = new URL("http://localhost");
13
+ function getNavigatorCurrentUrl(navigator) {
14
+ if (navigator.createURL) return navigator.createURL("/");
15
+ try {
16
+ return new URL(navigator.createHref("/"), DEFAULT_NAVIGATION_URL);
17
+ } catch {
18
+ return DEFAULT_NAVIGATION_URL;
19
+ }
20
+ }
21
+ function isSameOrigin(a, b) {
22
+ return a.origin === b.origin && (a.origin !== "null" || a.protocol === b.protocol && a.host === b.host);
23
+ }
24
+ function isExplicitUrl(destination, target) {
25
+ if (destination.startsWith("//")) return true;
26
+ let protocol = target.protocol.toLowerCase();
27
+ if (!destination.toLowerCase().startsWith(protocol)) return false;
28
+ return target.host === "" || destination.slice(protocol.length).startsWith("//");
29
+ }
30
+ function validateNavigationTarget(original, resolved, currentUrl, externalPolicy) {
31
+ let originalUrl = null;
32
+ try {
33
+ originalUrl = original == null ? null : new URL(original, currentUrl);
34
+ } catch {}
35
+ let resolvedUrl = new URL(resolved, currentUrl);
36
+ let originalIsExternal = originalUrl != null && !isSameOrigin(originalUrl, currentUrl);
37
+ let resolvedIsExternal = !isSameOrigin(resolvedUrl, currentUrl);
38
+ if (externalPolicy === "reject") {
39
+ if (originalIsExternal || resolvedIsExternal) throw new Error("External navigation is not allowed");
40
+ } else if (resolvedIsExternal) {
41
+ if (originalUrl == null || !isExplicitUrl(original, originalUrl) || !isSameOrigin(originalUrl, resolvedUrl)) throw new Error("External navigation is not allowed");
42
+ }
43
+ }
44
+ //#endregion
45
+ export { getNavigatorCurrentUrl, validateNavigationTarget };
@@ -128,6 +128,14 @@ interface Router {
128
128
  * @param location
129
129
  */
130
130
  createHref(location: Location | URL): string;
131
+ /**
132
+ * @private
133
+ * PRIVATE - DO NOT USE
134
+ *
135
+ * Utility function to create a URL for the given location
136
+ * @param location
137
+ */
138
+ createURL?(to: To): URL;
131
139
  /**
132
140
  * @private
133
141
  * PRIVATE - DO NOT USE
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -12,6 +12,7 @@ import { PROTOCOL_RELATIVE_URL_REGEX, normalizeProtocolRelativeUrl } from "./url
12
12
  import { createBrowserURLImpl, createLocation, createPath, invariant, parsePath, warning } from "./history.js";
13
13
  import { ErrorResponseImpl, RouterContextProvider, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createDataFunctionUrl, flattenAndRankRoutes, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, matchRoutesImpl, prependBasename, removeDoubleSlashes, resolveTo, stripBasename } from "./utils.js";
14
14
  import { consumeInstrumentationClientResultMetaReceiver, getRouteInstrumentationUpdates, instrumentClientSideRouter } from "./instrumentation.js";
15
+ import { validateNavigationTarget } from "./navigation.js";
15
16
  //#region lib/router/router.ts
16
17
  const validMutationMethodsArr = [
17
18
  "POST",
@@ -412,21 +413,27 @@ function createRouter(init) {
412
413
  let instrumentationNavigateMetaReceiver = consumeInstrumentationClientResultMetaReceiver(router);
413
414
  let { path, submission, error } = normalizeNavigateOptions(false, normalizeTo(state.location, state.matches, basename, to, opts?.fromRouteId, opts?.relative), opts);
414
415
  let maskPath;
415
- if (opts?.mask) maskPath = {
416
- pathname: "",
417
- search: "",
418
- hash: "",
419
- ...typeof opts.mask === "string" ? parsePath(opts.mask) : {
416
+ if (opts?.mask) {
417
+ let partialPath = typeof opts.mask === "string" ? parsePath(opts.mask) : {
420
418
  ...state.location.mask,
421
419
  ...opts.mask
422
- }
423
- };
420
+ };
421
+ maskPath = {
422
+ pathname: partialPath.pathname ?? "",
423
+ search: partialPath.search ?? "",
424
+ hash: partialPath.hash ?? ""
425
+ };
426
+ if (PROTOCOL_RELATIVE_URL_REGEX.test(maskPath.pathname)) throw new Error("External navigation is not allowed");
427
+ else if (maskPath.pathname.startsWith("\\")) maskPath.pathname = maskPath.pathname.replace(/^\\+/, "/");
428
+ validateNavigationTarget(typeof opts.mask === "string" ? opts.mask : createPath(opts.mask), createPath(maskPath), init.history.createURL("/"), "reject");
429
+ }
424
430
  let currentLocation = state.location;
425
431
  let nextLocation = createLocation(currentLocation, path, opts && opts.state, void 0, maskPath);
426
432
  nextLocation = {
427
433
  ...nextLocation,
428
434
  ...init.history.encodeLocation(nextLocation)
429
435
  };
436
+ validateNavigationTarget(to == null ? init.history.createHref(state.location) : typeof to === "string" ? to : createPath(to), init.history.createHref(nextLocation.mask || nextLocation), init.history.createURL("/"), "reject");
430
437
  let userReplace = opts && opts.replace != null ? opts.replace : void 0;
431
438
  let historyAction = "PUSH";
432
439
  if (userReplace === true) historyAction = "REPLACE";
@@ -883,7 +890,10 @@ function createRouter(init) {
883
890
  let abortPendingFetchRevalidations = () => revalidatingFetchers.forEach((rf) => abortFetcher(rf.key));
884
891
  abortController.signal.addEventListener("abort", abortPendingFetchRevalidations);
885
892
  let { loaderResults, fetcherResults } = await callLoadersAndMaybeResolveData(dsMatches, revalidatingFetchers, revalidationRequest, nextLocation, scopedContext);
886
- if (abortController.signal.aborted) return;
893
+ if (abortController.signal.aborted) {
894
+ if (fetchReloadIds.get(key) === loadId) fetchReloadIds.delete(key);
895
+ return;
896
+ }
887
897
  abortController.signal.removeEventListener("abort", abortPendingFetchRevalidations);
888
898
  fetchReloadIds.delete(key);
889
899
  fetchControllers.delete(key);
@@ -1005,7 +1015,10 @@ function createRouter(init) {
1005
1015
  if (redirect.response.headers.has("X-Remix-Revalidate")) isRevalidationRequired = true;
1006
1016
  let location = redirect.response.headers.get("Location");
1007
1017
  invariant(location, "Expected a Location header on the redirect Response");
1008
- location = normalizeRedirectLocation(location, new URL(request.url), basename, init.history);
1018
+ let originalLocation = location;
1019
+ let currentUrl = new URL(request.url);
1020
+ location = normalizeRedirectLocation(location, currentUrl, basename, init.history);
1021
+ validateNavigationTarget(originalLocation, location, currentUrl, "allow-explicit");
1009
1022
  let redirectLocation = createLocation(state.location, location, { _isRedirect: true });
1010
1023
  if (isBrowser) {
1011
1024
  let isDocumentReload = false;
@@ -1357,6 +1370,7 @@ function createRouter(init) {
1357
1370
  fetch,
1358
1371
  revalidate,
1359
1372
  createHref: (to) => init.history.createHref(to),
1373
+ createURL: (to) => init.history.createURL(to),
1360
1374
  encodeLocation: (to) => init.history.encodeLocation(to),
1361
1375
  getFetcher,
1362
1376
  resetFetcher,
@@ -1876,7 +1890,7 @@ function normalizeNavigateOptions(isFetcher, path, opts) {
1876
1890
  text: void 0
1877
1891
  }
1878
1892
  };
1879
- } catch (e) {
1893
+ } catch {
1880
1894
  return getInvalidBodyError();
1881
1895
  }
1882
1896
  }
@@ -1899,7 +1913,7 @@ function normalizeNavigateOptions(isFetcher, path, opts) {
1899
1913
  } else try {
1900
1914
  searchParams = new URLSearchParams(opts.body);
1901
1915
  formData = convertSearchParamsToFormData(searchParams);
1902
- } catch (e) {
1916
+ } catch {
1903
1917
  return getInvalidBodyError();
1904
1918
  }
1905
1919
  let submission = {
@@ -2415,7 +2429,7 @@ async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fe
2415
2429
  });
2416
2430
  try {
2417
2431
  await Promise.all(matches.flatMap((m) => [m._lazyPromises?.handler, m._lazyPromises?.route]));
2418
- } catch (e) {}
2432
+ } catch {}
2419
2433
  return results;
2420
2434
  }
2421
2435
  async function callLoaderOrAction({ request, path, pattern, match, lazyHandlerPromise, lazyRoutePromise, handlerOverride, scopedContext }) {
@@ -2598,7 +2612,7 @@ function normalizeRedirectLocation(location, currentUrl, basename, historyInstan
2598
2612
  }
2599
2613
  try {
2600
2614
  if (hasInvalidProtocol(historyInstance.createURL(location).toString())) throw new Error("Invalid redirect location");
2601
- } catch (e) {}
2615
+ } catch {}
2602
2616
  return location;
2603
2617
  }
2604
2618
  function createClientSideRequest(history, location, signal, submission) {
@@ -2948,7 +2962,7 @@ function restoreAppliedTransitions(_window, transitions) {
2948
2962
  let json = JSON.parse(sessionPositions);
2949
2963
  for (let [k, v] of Object.entries(json || {})) if (v && Array.isArray(v)) transitions.set(k, new Set(v || []));
2950
2964
  }
2951
- } catch (e) {}
2965
+ } catch {}
2952
2966
  }
2953
2967
  function persistAppliedTransitions(_window, transitions) {
2954
2968
  if (transitions.size > 0) {
@@ -2969,13 +2983,13 @@ function createDeferred() {
2969
2983
  res(val);
2970
2984
  try {
2971
2985
  await promise;
2972
- } catch (e) {}
2986
+ } catch {}
2973
2987
  };
2974
2988
  reject = async (error) => {
2975
2989
  rej(error);
2976
2990
  try {
2977
2991
  await promise;
2978
- } catch (e) {}
2992
+ } catch {}
2979
2993
  };
2980
2994
  });
2981
2995
  return {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -686,10 +686,22 @@ interface RouteBranch<RouteObjectType extends RouteObject = RouteObject> {
686
686
  /**
687
687
  * Returns a path with params interpolated.
688
688
  *
689
+ * Param values are percent-encoded for use in a path segment: characters that
690
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
691
+ * are escaped, while characters that RFC 3986 allows literally in a path
692
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
693
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
694
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
695
+ * preserving `/` separators.
696
+ *
697
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
698
+ *
689
699
  * @example
690
700
  * import { generatePath } from "react-router";
691
701
  *
692
702
  * generatePath("/users/:id", { id: "123" }); // "/users/123"
703
+ * generatePath("/files/:name", { name: "a b" }); // "/files/a%20b"
704
+ * generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1"
693
705
  *
694
706
  * @public
695
707
  * @category Utils
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -375,12 +375,66 @@ function matchRouteBranch(branch, pathname, allowPartial = false) {
375
375
  return matches;
376
376
  }
377
377
  /**
378
+ * Characters that `encodeURIComponent` escapes but that are valid literally in
379
+ * a URL path segment. Per RFC 3986 §3.3, a path segment is made of `pchar`:
380
+ *
381
+ * ```
382
+ * pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
383
+ * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
384
+ * ```
385
+ *
386
+ * `encodeURIComponent` targets query-string values, where `$ & + , ; = : @`
387
+ * are delimiters and must be escaped — but in a path segment they carry no
388
+ * special meaning, and browsers keep them literal in `location.pathname`.
389
+ * (`! ' ( ) *` and the unreserved set are already left alone by
390
+ * `encodeURIComponent`, so they need no restoring.)
391
+ */
392
+ const PATH_PARAM_OVERESCAPED = {
393
+ "%24": "$",
394
+ "%26": "&",
395
+ "%2B": "+",
396
+ "%2C": ",",
397
+ "%3A": ":",
398
+ "%3B": ";",
399
+ "%3D": "=",
400
+ "%40": "@"
401
+ };
402
+ /**
403
+ * Encodes a param value for interpolation into a single URL path segment.
404
+ *
405
+ * Escapes characters that would break the path (`/ ? # %`, whitespace,
406
+ * non-ASCII, …) while leaving characters that RFC 3986 permits literally in a
407
+ * path segment untouched. Escaping those would needlessly rewrite URLs — e.g.
408
+ * a semver build param `1.0.0+1` would become `1.0.0%2B1` even though browsers
409
+ * display and match the `+` literally in `location.pathname`.
410
+ *
411
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3))
412
+ *
413
+ * @param value The param value to encode.
414
+ * @returns The encoded value, safe for use as a single path segment.
415
+ */
416
+ function encodePathParam(value) {
417
+ return encodeURIComponent(value).replace(/%(?:24|26|2B|2C|3A|3B|3D|40)/g, (match) => PATH_PARAM_OVERESCAPED[match]);
418
+ }
419
+ /**
378
420
  * Returns a path with params interpolated.
379
421
  *
422
+ * Param values are percent-encoded for use in a path segment: characters that
423
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
424
+ * are escaped, while characters that RFC 3986 allows literally in a path
425
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
426
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
427
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
428
+ * preserving `/` separators.
429
+ *
430
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
431
+ *
380
432
  * @example
381
433
  * import { generatePath } from "react-router";
382
434
  *
383
435
  * generatePath("/users/:id", { id: "123" }); // "/users/123"
436
+ * generatePath("/files/:name", { name: "a b" }); // "/files/a%20b"
437
+ * generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1"
384
438
  *
385
439
  * @public
386
440
  * @category Utils
@@ -403,7 +457,7 @@ function generatePath(originalPath, params = {}) {
403
457
  const [, key, optional, suffix] = keyMatch;
404
458
  let param = params[key];
405
459
  invariant(optional === "?" || param != null, `Missing ":${key}" param`);
406
- return encodeURIComponent(stringify(param)) + suffix;
460
+ return encodePathParam(stringify(param)) + suffix;
407
461
  }
408
462
  return segment.replace(/\?$/g, "");
409
463
  }).filter((segment) => !!segment).join("/");
@@ -435,13 +489,13 @@ function matchPathImpl(pattern, pathname, matcher, compiledParams) {
435
489
  let match = pathname.match(matcher);
436
490
  if (!match) return null;
437
491
  let matchedPathname = match[0];
438
- let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
492
+ let pathnameBase = removeTrailingSlash(matchedPathname, 1);
439
493
  let captureGroups = match.slice(1);
440
494
  return {
441
495
  params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
442
496
  if (paramName === "*") {
443
497
  let splatValue = captureGroups[index] || "";
444
- pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
498
+ pathnameBase = removeTrailingSlash(matchedPathname.slice(0, matchedPathname.length - splatValue.length), 1);
445
499
  }
446
500
  const value = captureGroups[index];
447
501
  if (isOptional && !value) memo[paramName] = void 0;
@@ -510,7 +564,7 @@ function resolvePath(to, fromPathname = "/") {
510
564
  let pathname;
511
565
  if (toPathname) {
512
566
  toPathname = removeDoubleSlashes(toPathname);
513
- if (toPathname.startsWith("/")) pathname = resolvePathname(toPathname.substring(1), "/");
567
+ if (toPathname.startsWith("/") || toPathname.startsWith("\\")) pathname = resolvePathname(toPathname.substring(1), "/");
514
568
  else pathname = resolvePathname(toPathname, fromPathname);
515
569
  } else pathname = fromPathname;
516
570
  return {
@@ -571,7 +625,11 @@ function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = fal
571
625
  }
572
626
  const removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
573
627
  const joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
574
- const removeTrailingSlash = (path) => path.replace(/\/+$/, "");
628
+ function removeTrailingSlash(path, minLength = 0) {
629
+ let end = path.length;
630
+ while (end > minLength && path.charCodeAt(end - 1) === 47) end--;
631
+ return end === path.length ? path : path.slice(0, end);
632
+ }
575
633
  const normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
576
634
  const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
577
635
  const normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
@@ -814,7 +872,7 @@ function parseToInfo(_to, basename) {
814
872
  let path = stripBasename(targetUrl.pathname, basename);
815
873
  if (targetUrl.origin === currentUrl.origin && path != null) to = path + targetUrl.search + targetUrl.hash;
816
874
  else isExternal = true;
817
- } catch (e) {
875
+ } catch {
818
876
  warning(false, `<Link to="${to}"> contains an invalid URL which will probably break when clicked - please update to a valid URL path.`);
819
877
  }
820
878
  return {
@@ -824,4 +882,4 @@ function parseToInfo(_to, basename) {
824
882
  };
825
883
  }
826
884
  //#endregion
827
- export { ErrorResponseImpl, RouterContextProvider, SUPPORTED_ERROR_TYPES, compilePath, convertRouteMatchToUiMatch, convertRoutesToDataRoutes, createContext, createDataFunctionUrl, data, decodePath, defaultMapRouteProperties, flattenAndRankRoutes, generatePath, getPathContributingMatches, getResolveToMatches, getRoutePattern, isAbsoluteUrl, isBrowser, isRouteErrorResponse, isUnsupportedLazyRouteFunctionKey, isUnsupportedLazyRouteObjectKey, joinPaths, matchPath, matchRoutes, matchRoutesImpl, parseToInfo, prependBasename, redirect, redirectDocument, removeDoubleSlashes, removeTrailingSlash, replace, resolvePath, resolveTo, stripBasename };
885
+ 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 };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -9,15 +9,16 @@
9
9
  * @license MIT
10
10
  */
11
11
  import { PROTOCOL_RELATIVE_URL_REGEX } from "../router/url.js";
12
- import { createBrowserHistory, invariant } from "../router/history.js";
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";
17
18
  import { createRequestInit } from "../dom/ssr/data.js";
18
19
  import { getSingleFetchDataStrategyImpl, singleFetchUrl, stripIndexParam } from "../dom/ssr/single-fetch.js";
19
20
  import { noActionDefinedError, shouldHydrateRouteLoader } from "../dom/ssr/routes.js";
20
- import { getPathsWithAncestors } from "../dom/ssr/fog-of-war.js";
21
+ import { getPathsWithAncestors, handleClientVersionMismatch } from "../dom/ssr/fog-of-war.js";
21
22
  import { FrameworkContext, setIsHydrated } from "../dom/ssr/components.js";
22
23
  import { RSCRouterGlobalErrorBoundary } from "./errorBoundaries.js";
23
24
  import { populateRSCRouteModules } from "./route-modules.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;
@@ -133,6 +136,7 @@ function createRouterFromPayload({ fetchImplementation, createFromReadableStream
133
136
  routeModules: globalVar.__reactRouterRouteModules
134
137
  };
135
138
  if (payload.type !== "render") throw new Error("Invalid payload type");
139
+ let { clientVersion } = payload;
136
140
  globalVar.__reactRouterRouteModules = globalVar.__reactRouterRouteModules ?? {};
137
141
  populateRSCRouteModules(globalVar.__reactRouterRouteModules, payload.matches);
138
142
  let routes = payload.matches.reduceRight((previous, match) => {
@@ -167,7 +171,7 @@ function createRouterFromPayload({ fetchImplementation, createFromReadableStream
167
171
  basename: payload.basename,
168
172
  isSpaMode: false
169
173
  }),
170
- async patchRoutesOnNavigation({ path, signal }) {
174
+ async patchRoutesOnNavigation({ path, signal, fetcherKey }) {
171
175
  if (payload.routeDiscovery.mode === "initial") {
172
176
  if (!applyPatchesPromise) applyPatchesPromise = (async () => {
173
177
  if (!payload.patches) return;
@@ -182,9 +186,10 @@ function createRouterFromPayload({ fetchImplementation, createFromReadableStream
182
186
  return;
183
187
  }
184
188
  if (discoveredPaths.has(path)) return;
185
- await fetchAndApplyManifestPatches([path], createFromReadableStream, fetchImplementation, signal);
189
+ let { state } = globalVar.__reactRouterDataRouter;
190
+ await fetchAndApplyManifestPatches([path], createFromReadableStream, fetchImplementation, clientVersion, fetcherKey ? window.location.href : createPath(state.navigation.location || state.location), signal);
186
191
  },
187
- dataStrategy: getRSCSingleFetchDataStrategy(() => globalVar.__reactRouterDataRouter, true, createFromReadableStream, fetchImplementation)
192
+ dataStrategy: getRSCSingleFetchDataStrategy(() => globalVar.__reactRouterDataRouter, true, createFromReadableStream, fetchImplementation, clientVersion)
188
193
  });
189
194
  if (globalVar.__reactRouterDataRouter.state.initialized) {
190
195
  globalVar.__routerInitialized = true;
@@ -237,19 +242,16 @@ function createRouterFromPayload({ fetchImplementation, createFromReadableStream
237
242
  };
238
243
  }
239
244
  const renderedRoutesContext = createContext();
240
- function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream, fetchImplementation) {
245
+ function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream, fetchImplementation, clientVersion) {
241
246
  let dataStrategy = getSingleFetchDataStrategyImpl(getRouter, (match) => {
242
247
  let M = match;
243
248
  return {
244
249
  hasLoader: M.route.hasLoader,
245
- hasClientLoader: M.route.hasClientLoader,
246
- hasComponent: M.route.hasComponent,
247
- hasAction: M.route.hasAction,
248
- hasClientAction: M.route.hasClientAction
250
+ hasClientLoader: M.route.hasClientLoader
249
251
  };
250
- }, getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation), ssr, (match) => {
252
+ }, getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion), ssr, (match) => {
251
253
  let M = match;
252
- return M.route.hasComponent && !M.route.element;
254
+ return !M.route.hasComponent || M.route.element != null;
253
255
  });
254
256
  return async (args) => args.runClientMiddleware(async () => {
255
257
  args.context.set(renderedRoutesContext, []);
@@ -268,7 +270,7 @@ function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream,
268
270
  return results;
269
271
  });
270
272
  }
271
- function getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation) {
273
+ function getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion) {
272
274
  return async (args, targetRoutes) => {
273
275
  let { request, context } = args;
274
276
  let url = singleFetchUrl(request.url, "rsc");
@@ -292,6 +294,7 @@ function getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation)
292
294
  } }
293
295
  };
294
296
  if (payload.type !== "render") throw new Error("Unexpected payload type");
297
+ if (clientVersion !== void 0 && await handleClientVersionMismatch(payload.clientVersion !== clientVersion, clientVersion, createPath(getRouter().state.navigation.location || getRouter().state.location))) return new Promise(() => {});
295
298
  context.get(renderedRoutesContext).push(...payload.matches);
296
299
  let results = { routes: {} };
297
300
  const dataKey = isMutationMethod(request.method) ? "actionData" : "loaderData";
@@ -347,7 +350,7 @@ function getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation)
347
350
  */
348
351
  function RSCHydratedRouter({ createFromReadableStream, fetch: fetchImplementation = fetch, payload, getContext }) {
349
352
  if (payload.type !== "render") throw new Error("Invalid payload type");
350
- let { routeDiscovery } = payload;
353
+ let { routeDiscovery, clientVersion } = payload;
351
354
  let { router, routeModules } = React$1.useMemo(() => createRouterFromPayload({
352
355
  payload,
353
356
  fetchImplementation,
@@ -413,7 +416,7 @@ function RSCHydratedRouter({ createFromReadableStream, fetch: fetchImplementatio
413
416
  });
414
417
  if (paths.length === 0) return;
415
418
  try {
416
- await fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation);
419
+ await fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, null);
417
420
  } catch (e) {
418
421
  console.error("Failed to fetch manifest patches", e);
419
422
  }
@@ -433,7 +436,8 @@ function RSCHydratedRouter({ createFromReadableStream, fetch: fetchImplementatio
433
436
  }, [
434
437
  routeDiscovery,
435
438
  createFromReadableStream,
436
- fetchImplementation
439
+ fetchImplementation,
440
+ clientVersion
437
441
  ]);
438
442
  const frameworkContext = {
439
443
  future: {},
@@ -506,6 +510,7 @@ function createRouteFromServerManifest(match, payload) {
506
510
  shouldRevalidate: match.shouldRevalidate,
507
511
  hasLoader: true,
508
512
  hasClientLoader: match.clientLoader != null,
513
+ hasComponent: match.hasComponent,
509
514
  hasAction: match.hasAction,
510
515
  hasClientAction: match.clientAction != null
511
516
  };
@@ -526,23 +531,31 @@ function preventInvalidServerHandlerCall(type, routeId, hasHandler) {
526
531
  const nextPaths = /* @__PURE__ */ new Set();
527
532
  const discoveredPathsMaxSize = 1e3;
528
533
  const discoveredPaths = /* @__PURE__ */ new Set();
529
- function getManifestUrl(paths) {
534
+ function getManifestUrl(paths, clientVersion) {
530
535
  if (paths.length === 0) return null;
531
- if (paths.length === 1) return new URL(`${paths[0]}.manifest`, window.location.origin);
532
- let basename = (window.__reactRouterDataRouter.basename ?? "").replace(/^\/|\/$/g, "");
533
- let url = new URL(`${basename}/.manifest`, window.location.origin);
534
- url.searchParams.set("paths", paths.sort().join(","));
536
+ let url;
537
+ if (paths.length === 1) url = new URL(`${paths[0]}.manifest`, window.location.origin);
538
+ else {
539
+ let basename = (window.__reactRouterDataRouter.basename ?? "").replace(/^\/|\/$/g, "");
540
+ url = new URL(`${basename}/.manifest`, window.location.origin);
541
+ url.searchParams.set("paths", paths.sort().join(","));
542
+ }
543
+ if (clientVersion !== void 0) url.searchParams.set("version", clientVersion);
535
544
  return url;
536
545
  }
537
- async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, signal) {
546
+ async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, errorReloadPath, signal) {
538
547
  paths = getPathsWithAncestors(paths);
539
- let url = getManifestUrl(paths);
548
+ let url = getManifestUrl(paths, clientVersion);
540
549
  if (url == null) return;
541
550
  if (url.toString().length > 7680) {
542
551
  nextPaths.clear();
543
552
  return;
544
553
  }
545
554
  let response = await fetchImplementation(new Request(url, { signal }));
555
+ if (clientVersion !== void 0 && response.status === 204 && response.headers.has("X-Remix-Reload-Document")) {
556
+ await handleClientVersionMismatch(true, clientVersion, errorReloadPath);
557
+ return;
558
+ }
546
559
  if (!response.body || response.status < 200 || response.status >= 300) throw new Error("Unable to fetch new route matches from the server");
547
560
  let payload = await createFromReadableStream(response.body, { temporaryReferences: void 0 });
548
561
  if (payload.type !== "manifest") throw new Error("Failed to patch routes");
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.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.2.0
2
+ * react-router v8.3.1
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *