react-router 8.2.0 → 8.3.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 (169) hide show
  1. package/CHANGELOG.md +67 -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 +106 -17
  5. package/dist/development/index-react-server.js +168 -42
  6. package/dist/development/index.js +1 -1
  7. package/dist/development/lib/actions.js +1 -1
  8. package/dist/development/lib/components.js +1 -1
  9. package/dist/development/lib/context.js +1 -1
  10. package/dist/development/lib/dom/dom.d.ts +24 -24
  11. package/dist/development/lib/dom/dom.js +25 -25
  12. package/dist/development/lib/dom/lib.d.ts +2 -1
  13. package/dist/development/lib/dom/lib.js +5 -4
  14. package/dist/development/lib/dom/server.js +1 -1
  15. package/dist/development/lib/dom/ssr/components.js +1 -1
  16. package/dist/development/lib/dom/ssr/data.js +1 -1
  17. package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
  18. package/dist/development/lib/dom/ssr/fallback.js +1 -1
  19. package/dist/development/lib/dom/ssr/fog-of-war.js +26 -21
  20. package/dist/development/lib/dom/ssr/hydration.js +1 -1
  21. package/dist/development/lib/dom/ssr/invariant.js +1 -1
  22. package/dist/development/lib/dom/ssr/links.js +1 -1
  23. package/dist/development/lib/dom/ssr/markup.js +1 -1
  24. package/dist/development/lib/dom/ssr/routeModules.js +1 -1
  25. package/dist/development/lib/dom/ssr/routes-test-stub.d.ts +13 -0
  26. package/dist/development/lib/dom/ssr/routes-test-stub.js +14 -1
  27. package/dist/development/lib/dom/ssr/routes.js +1 -1
  28. package/dist/development/lib/dom/ssr/server.js +1 -1
  29. package/dist/development/lib/dom/ssr/single-fetch.js +1 -1
  30. package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
  31. package/dist/development/lib/dom-export/hydrated-router.js +1 -1
  32. package/dist/development/lib/errors.js +1 -1
  33. package/dist/development/lib/hooks.js +1 -1
  34. package/dist/development/lib/href.d.ts +24 -8
  35. package/dist/development/lib/href.js +28 -11
  36. package/dist/development/lib/router/history.d.ts +6 -0
  37. package/dist/development/lib/router/history.js +7 -1
  38. package/dist/development/lib/router/instrumentation.js +1 -1
  39. package/dist/development/lib/router/router.js +1 -1
  40. package/dist/development/lib/router/url.js +1 -1
  41. package/dist/development/lib/router/utils.d.ts +12 -0
  42. package/dist/development/lib/router/utils.js +57 -3
  43. package/dist/development/lib/rsc/browser.js +34 -24
  44. package/dist/development/lib/rsc/errorBoundaries.js +1 -1
  45. package/dist/development/lib/rsc/html-stream/browser.js +1 -1
  46. package/dist/development/lib/rsc/html-stream/server.js +29 -10
  47. package/dist/development/lib/rsc/route-modules.js +1 -1
  48. package/dist/development/lib/rsc/server.rsc.d.ts +5 -1
  49. package/dist/development/lib/rsc/server.ssr.d.ts +29 -6
  50. package/dist/development/lib/rsc/server.ssr.js +27 -12
  51. package/dist/development/lib/server-runtime/cookies.d.ts +28 -2
  52. package/dist/development/lib/server-runtime/cookies.js +17 -3
  53. package/dist/development/lib/server-runtime/crypto.js +1 -1
  54. package/dist/development/lib/server-runtime/data.js +1 -1
  55. package/dist/development/lib/server-runtime/dev.js +1 -1
  56. package/dist/development/lib/server-runtime/entry.js +1 -1
  57. package/dist/development/lib/server-runtime/errors.js +1 -1
  58. package/dist/development/lib/server-runtime/headers.js +1 -1
  59. package/dist/development/lib/server-runtime/invariant.js +1 -1
  60. package/dist/development/lib/server-runtime/mode.js +1 -1
  61. package/dist/development/lib/server-runtime/routeMatching.js +1 -1
  62. package/dist/development/lib/server-runtime/routes.js +1 -1
  63. package/dist/development/lib/server-runtime/server.d.ts +12 -0
  64. package/dist/development/lib/server-runtime/server.js +13 -1
  65. package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
  66. package/dist/development/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  67. package/dist/development/lib/server-runtime/sessions/cookieStorage.js +9 -1
  68. package/dist/development/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  69. package/dist/development/lib/server-runtime/sessions/memoryStorage.js +12 -6
  70. package/dist/development/lib/server-runtime/sessions.d.ts +31 -2
  71. package/dist/development/lib/server-runtime/sessions.js +20 -3
  72. package/dist/development/lib/server-runtime/single-fetch.js +1 -1
  73. package/dist/development/lib/server-runtime/urls.js +1 -1
  74. package/dist/development/lib/server-runtime/warnings.js +1 -1
  75. package/dist/development/lib/types/internal.js +1 -1
  76. package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
  77. package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  78. package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
  79. package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
  80. package/dist/production/dom-export.js +1 -1
  81. package/dist/production/index-react-server-client.js +1 -1
  82. package/dist/production/index-react-server.d.ts +106 -17
  83. package/dist/production/index-react-server.js +168 -42
  84. package/dist/production/index.js +1 -1
  85. package/dist/production/lib/actions.js +1 -1
  86. package/dist/production/lib/components.js +1 -1
  87. package/dist/production/lib/context.js +1 -1
  88. package/dist/production/lib/dom/dom.d.ts +24 -24
  89. package/dist/production/lib/dom/dom.js +25 -25
  90. package/dist/production/lib/dom/lib.d.ts +2 -1
  91. package/dist/production/lib/dom/lib.js +5 -4
  92. package/dist/production/lib/dom/server.js +1 -1
  93. package/dist/production/lib/dom/ssr/components.js +1 -1
  94. package/dist/production/lib/dom/ssr/data.js +1 -1
  95. package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
  96. package/dist/production/lib/dom/ssr/fallback.js +1 -1
  97. package/dist/production/lib/dom/ssr/fog-of-war.js +26 -21
  98. package/dist/production/lib/dom/ssr/hydration.js +1 -1
  99. package/dist/production/lib/dom/ssr/invariant.js +1 -1
  100. package/dist/production/lib/dom/ssr/links.js +1 -1
  101. package/dist/production/lib/dom/ssr/markup.js +1 -1
  102. package/dist/production/lib/dom/ssr/routeModules.js +1 -1
  103. package/dist/production/lib/dom/ssr/routes-test-stub.d.ts +13 -0
  104. package/dist/production/lib/dom/ssr/routes-test-stub.js +14 -1
  105. package/dist/production/lib/dom/ssr/routes.js +1 -1
  106. package/dist/production/lib/dom/ssr/server.js +1 -1
  107. package/dist/production/lib/dom/ssr/single-fetch.js +1 -1
  108. package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
  109. package/dist/production/lib/dom-export/hydrated-router.js +1 -1
  110. package/dist/production/lib/errors.js +1 -1
  111. package/dist/production/lib/hooks.js +1 -1
  112. package/dist/production/lib/href.d.ts +24 -8
  113. package/dist/production/lib/href.js +28 -11
  114. package/dist/production/lib/router/history.d.ts +6 -0
  115. package/dist/production/lib/router/history.js +7 -1
  116. package/dist/production/lib/router/instrumentation.js +1 -1
  117. package/dist/production/lib/router/router.js +1 -1
  118. package/dist/production/lib/router/url.js +1 -1
  119. package/dist/production/lib/router/utils.d.ts +12 -0
  120. package/dist/production/lib/router/utils.js +57 -3
  121. package/dist/production/lib/rsc/browser.js +34 -24
  122. package/dist/production/lib/rsc/errorBoundaries.js +1 -1
  123. package/dist/production/lib/rsc/html-stream/browser.js +1 -1
  124. package/dist/production/lib/rsc/html-stream/server.js +29 -10
  125. package/dist/production/lib/rsc/route-modules.js +1 -1
  126. package/dist/production/lib/rsc/server.rsc.d.ts +5 -1
  127. package/dist/production/lib/rsc/server.ssr.d.ts +29 -6
  128. package/dist/production/lib/rsc/server.ssr.js +27 -12
  129. package/dist/production/lib/server-runtime/cookies.d.ts +28 -2
  130. package/dist/production/lib/server-runtime/cookies.js +17 -3
  131. package/dist/production/lib/server-runtime/crypto.js +1 -1
  132. package/dist/production/lib/server-runtime/data.js +1 -1
  133. package/dist/production/lib/server-runtime/dev.js +1 -1
  134. package/dist/production/lib/server-runtime/entry.js +1 -1
  135. package/dist/production/lib/server-runtime/errors.js +1 -1
  136. package/dist/production/lib/server-runtime/headers.js +1 -1
  137. package/dist/production/lib/server-runtime/invariant.js +1 -1
  138. package/dist/production/lib/server-runtime/mode.js +1 -1
  139. package/dist/production/lib/server-runtime/routeMatching.js +1 -1
  140. package/dist/production/lib/server-runtime/routes.js +1 -1
  141. package/dist/production/lib/server-runtime/server.d.ts +12 -0
  142. package/dist/production/lib/server-runtime/server.js +13 -1
  143. package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
  144. package/dist/production/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
  145. package/dist/production/lib/server-runtime/sessions/cookieStorage.js +9 -1
  146. package/dist/production/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
  147. package/dist/production/lib/server-runtime/sessions/memoryStorage.js +12 -6
  148. package/dist/production/lib/server-runtime/sessions.d.ts +31 -2
  149. package/dist/production/lib/server-runtime/sessions.js +20 -3
  150. package/dist/production/lib/server-runtime/single-fetch.js +1 -1
  151. package/dist/production/lib/server-runtime/urls.js +1 -1
  152. package/dist/production/lib/server-runtime/warnings.js +1 -1
  153. package/dist/production/lib/types/internal.js +1 -1
  154. package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
  155. package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +1 -1
  156. package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
  157. package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
  158. package/docs/explanation/sessions-and-cookies.md +13 -13
  159. package/docs/explanation/state-management.md +2 -2
  160. package/docs/how-to/presets.md +2 -2
  161. package/docs/how-to/react-server-components.md +53 -1
  162. package/docs/how-to/security.md +8 -1
  163. package/docs/how-to/server-bundles.md +2 -2
  164. package/docs/start/data/route-object.md +2 -2
  165. package/docs/start/data/routing.md +1 -1
  166. package/docs/start/framework/pending-ui.md +1 -1
  167. package/docs/start/framework/route-module.md +8 -8
  168. package/docs/start/framework/routing.md +1 -1
  169. package/package.json +1 -1
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -110,6 +110,29 @@ function getManifestPath(_manifestPath, basename) {
110
110
  return basename == null ? manifestPath : joinPaths([basename, manifestPath]);
111
111
  }
112
112
  const MANIFEST_VERSION_STORAGE_KEY = "react-router-manifest-version";
113
+ async function handleClientVersionMismatch(needsReload, version, errorReloadPath) {
114
+ if (!needsReload) {
115
+ try {
116
+ sessionStorage.removeItem(MANIFEST_VERSION_STORAGE_KEY);
117
+ } catch {}
118
+ return false;
119
+ }
120
+ if (!errorReloadPath) {
121
+ console.warn("Detected a manifest version mismatch during eager route discovery. The next navigation/fetch to an undiscovered route will result in a new document navigation to sync up with the latest manifest.");
122
+ return true;
123
+ }
124
+ try {
125
+ if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === version) {
126
+ console.error("Unable to discover routes due to manifest version mismatch.");
127
+ return true;
128
+ }
129
+ sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, version);
130
+ } catch {}
131
+ window.location.href = errorReloadPath;
132
+ console.warn("Detected manifest version mismatch, reloading...");
133
+ await new Promise(() => {});
134
+ return true;
135
+ }
113
136
  async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, routeModules, ssr, isSpaMode, basename, manifestPath, patchRoutes, signal) {
114
137
  paths = getPathsWithAncestors(paths);
115
138
  const searchParams = new URLSearchParams();
@@ -125,25 +148,7 @@ async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, ro
125
148
  try {
126
149
  let res = await fetch(url, { signal });
127
150
  if (!res.ok) throw new Error(`${res.status} ${res.statusText}`);
128
- else if (res.status === 204 && res.headers.has("X-Remix-Reload-Document")) {
129
- if (!errorReloadPath) {
130
- console.warn("Detected a manifest version mismatch during eager route discovery. The next navigation/fetch to an undiscovered route will result in a new document navigation to sync up with the latest manifest.");
131
- return;
132
- }
133
- try {
134
- if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === manifest.version) {
135
- console.error("Unable to discover routes due to manifest version mismatch.");
136
- return;
137
- }
138
- sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, manifest.version);
139
- } catch {}
140
- window.location.href = errorReloadPath;
141
- console.warn("Detected manifest version mismatch, reloading...");
142
- await new Promise(() => {});
143
- } else if (res.status >= 400) throw new Error(await res.text());
144
- try {
145
- sessionStorage.removeItem(MANIFEST_VERSION_STORAGE_KEY);
146
- } catch {}
151
+ if (await handleClientVersionMismatch(res.status === 204 && res.headers.has("X-Remix-Reload-Document"), manifest.version, errorReloadPath)) return;
147
152
  serverPatches = await res.json();
148
153
  } catch (e) {
149
154
  if (signal?.aborted) return;
@@ -177,4 +182,4 @@ function debounce(callback, wait) {
177
182
  };
178
183
  }
179
184
  //#endregion
180
- export { URL_LIMIT, getManifestPath, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, isFogOfWarEnabled, useFogOFWarDiscovery };
185
+ export { URL_LIMIT, getManifestPath, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, handleClientVersionMismatch, isFogOfWarEnabled, useFogOFWarDiscovery };
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
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.0
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.0
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.0
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.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -50,7 +50,20 @@ interface RoutesTestStubProps {
50
50
  future?: Partial<FutureConfig>;
51
51
  }
52
52
  /**
53
+ * Creates a React component that renders the provided routes in a test-friendly
54
+ * React Router context.
55
+ *
56
+ * Use this to unit test components that rely on router context, such as
57
+ * `loaderData`, `actionData`, and route matches.
58
+ *
59
+ * @public
53
60
  * @category Utils
61
+ * @mode framework
62
+ * @mode data
63
+ * @param routes The route objects to render in the test router.
64
+ * @param _context An optional {@link RouterContextProvider} for supplying
65
+ * application context values to route middleware, loaders, and actions.
66
+ * @returns A React component that renders the test router.
54
67
  */
55
68
  declare function createRoutesStub(routes: StubRouteObject[], _context?: RouterContextProvider): ({
56
69
  initialEntries,
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -14,7 +14,20 @@ import { FrameworkContext } from "./components.js";
14
14
  import * as React$1 from "react";
15
15
  //#region lib/dom/ssr/routes-test-stub.tsx
16
16
  /**
17
+ * Creates a React component that renders the provided routes in a test-friendly
18
+ * React Router context.
19
+ *
20
+ * Use this to unit test components that rely on router context, such as
21
+ * `loaderData`, `actionData`, and route matches.
22
+ *
23
+ * @public
17
24
  * @category Utils
25
+ * @mode framework
26
+ * @mode data
27
+ * @param routes The route objects to render in the test router.
28
+ * @param _context An optional {@link RouterContextProvider} for supplying
29
+ * application context values to route middleware, loaders, and actions.
30
+ * @returns A React component that renders the test router.
18
31
  */
19
32
  function createRoutesStub(routes, _context) {
20
33
  return function RoutesTestStub({ initialEntries, initialIndex, hydrationData, future }) {
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
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.0
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.0
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.0
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.0
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.0
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.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -6,14 +6,30 @@ import { Pages } from "./types/register.js";
6
6
  type Args = { [K in keyof Pages]: ToArgs<Pages[K]["params"]> };
7
7
  type ToArgs<Params extends Record<string, string | undefined>> = Equal<Params, {}> extends true ? [] : Partial<Params> extends Params ? [Params] | [] : [Params];
8
8
  /**
9
- Returns a resolved URL path for the specified route.
10
-
11
- ```tsx
12
- const h = href("/:lang?/about", { lang: "en" })
13
- // -> `/en/about`
14
-
15
- <Link to={href("/products/:id", { id: "abc123" })} />
16
- ```
9
+ * Returns a resolved URL path for the specified route.
10
+ *
11
+ * Param values are percent-encoded for use in a path segment: characters that
12
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
13
+ * are escaped, while characters that RFC 3986 allows literally in a path
14
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
15
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
16
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
17
+ * preserving `/` separators.
18
+ *
19
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
20
+ *
21
+ * @example
22
+ * const h = href("/:lang?/about", { lang: "en" })
23
+ * // -> `/en/about`
24
+ *
25
+ * <Link to={href("/products/:id", { id: "abc123" })} />
26
+ *
27
+ * @public
28
+ * @category Utils
29
+ * @mode framework
30
+ * @param path The route path to resolve
31
+ * @param args The route params to use when resolving the path
32
+ * @returns The resolved URL path
17
33
  */
18
34
  declare function href<Path extends keyof Args>(path: Path, ...args: Args[Path]): string;
19
35
  //#endregion
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -8,19 +8,36 @@
8
8
  *
9
9
  * @license MIT
10
10
  */
11
+ import { encodePathParam } from "./router/utils.js";
11
12
  //#region lib/href.ts
12
13
  function stringify(p) {
13
14
  return p == null ? "" : typeof p === "string" ? p : String(p);
14
15
  }
15
16
  /**
16
- Returns a resolved URL path for the specified route.
17
-
18
- ```tsx
19
- const h = href("/:lang?/about", { lang: "en" })
20
- // -> `/en/about`
21
-
22
- <Link to={href("/products/:id", { id: "abc123" })} />
23
- ```
17
+ * Returns a resolved URL path for the specified route.
18
+ *
19
+ * Param values are percent-encoded for use in a path segment: characters that
20
+ * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
21
+ * are escaped, while characters that RFC 3986 allows literally in a path
22
+ * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
23
+ * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
24
+ * delimiters and must be escaped. Splat (`*`) values are encoded per segment,
25
+ * preserving `/` separators.
26
+ *
27
+ * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
28
+ *
29
+ * @example
30
+ * const h = href("/:lang?/about", { lang: "en" })
31
+ * // -> `/en/about`
32
+ *
33
+ * <Link to={href("/products/:id", { id: "abc123" })} />
34
+ *
35
+ * @public
36
+ * @category Utils
37
+ * @mode framework
38
+ * @param path The route path to resolve
39
+ * @param args The route params to use when resolving the path
40
+ * @returns The resolved URL path
24
41
  */
25
42
  function href(path, ...args) {
26
43
  let params = args[0];
@@ -28,11 +45,11 @@ function href(path, ...args) {
28
45
  const isRequired = questionMark === void 0;
29
46
  const value = params?.[param];
30
47
  if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
31
- return value == null ? "" : "/" + encodeURIComponent(stringify(value));
48
+ return value == null ? "" : "/" + encodePathParam(stringify(value));
32
49
  });
33
50
  if (path.endsWith("*")) {
34
51
  const value = params?.["*"];
35
- if (value !== void 0) result += "/" + stringify(value).split("/").map(encodeURIComponent).join("/");
52
+ if (value !== void 0) result += "/" + stringify(value).split("/").map(encodePathParam).join("/");
36
53
  }
37
54
  return result || "/";
38
55
  }
@@ -236,7 +236,10 @@ declare function invariant<T>(value: T | null | undefined, message?: string): as
236
236
  /**
237
237
  * Creates a string URL path from the given pathname, search, and hash components.
238
238
  *
239
+ * @public
239
240
  * @category Utils
241
+ * @param path The pathname, search, and hash components to combine.
242
+ * @returns The combined URL path.
240
243
  */
241
244
  declare function createPath({
242
245
  pathname,
@@ -246,7 +249,10 @@ declare function createPath({
246
249
  /**
247
250
  * Parses a string URL path into its separate pathname, search, and hash components.
248
251
  *
252
+ * @public
249
253
  * @category Utils
254
+ * @param path The URL path to parse.
255
+ * @returns The parsed pathname, search, and hash components.
250
256
  */
251
257
  declare function parsePath(path: string): Partial<Path>;
252
258
  interface UrlHistory extends History {}
@@ -1,5 +1,5 @@
1
1
  /**
2
- * react-router v8.2.0
2
+ * react-router v8.3.0
3
3
  *
4
4
  * Copyright (c) Remix Software Inc.
5
5
  *
@@ -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.0
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.0
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.0
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.0
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("/");
@@ -824,4 +878,4 @@ function parseToInfo(_to, basename) {
824
878
  };
825
879
  }
826
880
  //#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 };
881
+ 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 };