react-router
Advanced tools
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -9,26 +9,26 @@ | ||
| /** | ||
| Creates a URLSearchParams object using the given initializer. | ||
| This is identical to `new URLSearchParams(init)` except it also | ||
| supports arrays as values in the object form of the initializer | ||
| instead of just strings. This is convenient when you need multiple | ||
| values for a given key, but don't want to use an array initializer. | ||
| For example, instead of: | ||
| ```tsx | ||
| let searchParams = new URLSearchParams([ | ||
| ['sort', 'name'], | ||
| ['sort', 'price'] | ||
| ]); | ||
| ``` | ||
| you can do: | ||
| ``` | ||
| let searchParams = createSearchParams({ | ||
| sort: ['name', 'price'] | ||
| }); | ||
| ``` | ||
| @category Utils | ||
| * Creates a URLSearchParams object using the given initializer. | ||
| * | ||
| * This is identical to `new URLSearchParams(init)` except it also supports | ||
| * arrays as values in the object form of the initializer instead of just | ||
| * strings. This is convenient when you need multiple values for a given key, | ||
| * but don't want to use an array initializer. | ||
| * | ||
| * @example | ||
| * // Instead of: | ||
| * let searchParams = new URLSearchParams([ | ||
| * ["sort", "name"], | ||
| * ["sort", "price"], | ||
| * ]); | ||
| * | ||
| * // You can do: | ||
| * let searchParams = createSearchParams({ | ||
| * sort: ["name", "price"], | ||
| * }); | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param init The value used to initialize the URL search parameters. | ||
| * @returns A URLSearchParams object containing the initialized search | ||
| * parameters. | ||
| */ | ||
@@ -35,0 +35,0 @@ declare function createSearchParams(init?: URLSearchParamsInit): URLSearchParams; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -33,26 +33,26 @@ * Copyright (c) Remix Software Inc. | ||
| /** | ||
| Creates a URLSearchParams object using the given initializer. | ||
| This is identical to `new URLSearchParams(init)` except it also | ||
| supports arrays as values in the object form of the initializer | ||
| instead of just strings. This is convenient when you need multiple | ||
| values for a given key, but don't want to use an array initializer. | ||
| For example, instead of: | ||
| ```tsx | ||
| let searchParams = new URLSearchParams([ | ||
| ['sort', 'name'], | ||
| ['sort', 'price'] | ||
| ]); | ||
| ``` | ||
| you can do: | ||
| ``` | ||
| let searchParams = createSearchParams({ | ||
| sort: ['name', 'price'] | ||
| }); | ||
| ``` | ||
| @category Utils | ||
| * Creates a URLSearchParams object using the given initializer. | ||
| * | ||
| * This is identical to `new URLSearchParams(init)` except it also supports | ||
| * arrays as values in the object form of the initializer instead of just | ||
| * strings. This is convenient when you need multiple values for a given key, | ||
| * but don't want to use an array initializer. | ||
| * | ||
| * @example | ||
| * // Instead of: | ||
| * let searchParams = new URLSearchParams([ | ||
| * ["sort", "name"], | ||
| * ["sort", "price"], | ||
| * ]); | ||
| * | ||
| * // You can do: | ||
| * let searchParams = createSearchParams({ | ||
| * sort: ["name", "price"], | ||
| * }); | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param init The value used to initialize the URL search parameters. | ||
| * @returns A URLSearchParams object containing the initialized search | ||
| * parameters. | ||
| */ | ||
@@ -59,0 +59,0 @@ function createSearchParams(init = "") { |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -25,3 +25,3 @@ * Copyright (c) Remix Software Inc. | ||
| try { | ||
| if (isBrowser) window.__reactRouterVersion = "8.2.0"; | ||
| if (isBrowser) window.__reactRouterVersion = "8.3.0"; | ||
| } catch (e) {} | ||
@@ -390,3 +390,3 @@ /** | ||
| let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(endSlashPosition) === "/"; | ||
| let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(toPathname.length) === "/"); | ||
| let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(endSlashPosition) === "/"); | ||
| let renderProps = { | ||
@@ -614,3 +614,4 @@ isActive, | ||
| * @param options.defaultShouldRevalidate Specify the default revalidation | ||
| * behavior for the navigation. Defaults to `true`. | ||
| * behavior for the navigation. When not specified, loaders revalidate | ||
| * according to the router's standard revalidation behavior. | ||
| * @param options.mask Masked location to display in the browser instead | ||
@@ -617,0 +618,0 @@ * of the router location. Defaults to `undefined`. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -113,2 +113,25 @@ * Copyright (c) Remix Software Inc. | ||
| const MANIFEST_VERSION_STORAGE_KEY = "react-router-manifest-version"; | ||
| async function handleClientVersionMismatch(needsReload, version, errorReloadPath) { | ||
| if (!needsReload) { | ||
| try { | ||
| sessionStorage.removeItem(MANIFEST_VERSION_STORAGE_KEY); | ||
| } catch {} | ||
| return false; | ||
| } | ||
| if (!errorReloadPath) { | ||
| 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."); | ||
| return true; | ||
| } | ||
| try { | ||
| if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === version) { | ||
| console.error("Unable to discover routes due to manifest version mismatch."); | ||
| return true; | ||
| } | ||
| sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, version); | ||
| } catch {} | ||
| window.location.href = errorReloadPath; | ||
| console.warn("Detected manifest version mismatch, reloading..."); | ||
| await new Promise(() => {}); | ||
| return true; | ||
| } | ||
| async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, routeModules, ssr, isSpaMode, basename, manifestPath, patchRoutes, signal) { | ||
@@ -129,21 +152,3 @@ paths = getPathsWithAncestors(paths); | ||
| if (!res.ok) throw new Error(`${res.status} ${res.statusText}`); | ||
| else if (res.status === 204 && res.headers.has("X-Remix-Reload-Document")) { | ||
| if (!errorReloadPath) { | ||
| 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."); | ||
| return; | ||
| } | ||
| try { | ||
| if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === manifest.version) { | ||
| console.error("Unable to discover routes due to manifest version mismatch."); | ||
| return; | ||
| } | ||
| sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, manifest.version); | ||
| } catch {} | ||
| window.location.href = errorReloadPath; | ||
| console.warn("Detected manifest version mismatch, reloading..."); | ||
| await new Promise(() => {}); | ||
| } else if (res.status >= 400) throw new Error(await res.text()); | ||
| try { | ||
| sessionStorage.removeItem(MANIFEST_VERSION_STORAGE_KEY); | ||
| } catch {} | ||
| if (await handleClientVersionMismatch(res.status === 204 && res.headers.has("X-Remix-Reload-Document"), manifest.version, errorReloadPath)) return; | ||
| serverPatches = await res.json(); | ||
@@ -182,2 +187,2 @@ } catch (e) { | ||
| //#endregion | ||
| export { URL_LIMIT, getManifestPath, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, isFogOfWarEnabled, useFogOFWarDiscovery }; | ||
| export { URL_LIMIT, getManifestPath, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, handleClientVersionMismatch, isFogOfWarEnabled, useFogOFWarDiscovery }; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -53,3 +53,16 @@ | ||
| /** | ||
| * Creates a React component that renders the provided routes in a test-friendly | ||
| * React Router context. | ||
| * | ||
| * Use this to unit test components that rely on router context, such as | ||
| * `loaderData`, `actionData`, and route matches. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param routes The route objects to render in the test router. | ||
| * @param _context An optional {@link RouterContextProvider} for supplying | ||
| * application context values to route middleware, loaders, and actions. | ||
| * @returns A React component that renders the test router. | ||
| */ | ||
@@ -56,0 +69,0 @@ declare function createRoutesStub(routes: StubRouteObject[], _context?: RouterContextProvider): ({ |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -17,3 +17,16 @@ * Copyright (c) Remix Software Inc. | ||
| /** | ||
| * Creates a React component that renders the provided routes in a test-friendly | ||
| * React Router context. | ||
| * | ||
| * Use this to unit test components that rely on router context, such as | ||
| * `loaderData`, `actionData`, and route matches. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param routes The route objects to render in the test router. | ||
| * @param _context An optional {@link RouterContextProvider} for supplying | ||
| * application context values to route middleware, loaders, and actions. | ||
| * @returns A React component that renders the test router. | ||
| */ | ||
@@ -20,0 +33,0 @@ function createRoutesStub(routes, _context) { |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -9,10 +9,26 @@ | ||
| /** | ||
| Returns a resolved URL path for the specified route. | ||
| ```tsx | ||
| const h = href("/:lang?/about", { lang: "en" }) | ||
| // -> `/en/about` | ||
| <Link to={href("/products/:id", { id: "abc123" })} /> | ||
| ``` | ||
| * Returns a resolved URL path for the specified route. | ||
| * | ||
| * Param values are percent-encoded for use in a path segment: characters that | ||
| * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) | ||
| * are escaped, while characters that RFC 3986 allows literally in a path | ||
| * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string | ||
| * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are | ||
| * delimiters and must be escaped. Splat (`*`) values are encoded per segment, | ||
| * preserving `/` separators. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) | ||
| * | ||
| * @example | ||
| * const h = href("/:lang?/about", { lang: "en" }) | ||
| * // -> `/en/about` | ||
| * | ||
| * <Link to={href("/products/:id", { id: "abc123" })} /> | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @param path The route path to resolve | ||
| * @param args The route params to use when resolving the path | ||
| * @returns The resolved URL path | ||
| */ | ||
@@ -19,0 +35,0 @@ declare function href<Path extends keyof Args>(path: Path, ...args: Args[Path]): string; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -11,2 +11,3 @@ * Copyright (c) Remix Software Inc. | ||
| */ | ||
| import { encodePathParam } from "./router/utils.js"; | ||
| //#region lib/href.ts | ||
@@ -17,10 +18,26 @@ function stringify(p) { | ||
| /** | ||
| Returns a resolved URL path for the specified route. | ||
| ```tsx | ||
| const h = href("/:lang?/about", { lang: "en" }) | ||
| // -> `/en/about` | ||
| <Link to={href("/products/:id", { id: "abc123" })} /> | ||
| ``` | ||
| * Returns a resolved URL path for the specified route. | ||
| * | ||
| * Param values are percent-encoded for use in a path segment: characters that | ||
| * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) | ||
| * are escaped, while characters that RFC 3986 allows literally in a path | ||
| * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string | ||
| * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are | ||
| * delimiters and must be escaped. Splat (`*`) values are encoded per segment, | ||
| * preserving `/` separators. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) | ||
| * | ||
| * @example | ||
| * const h = href("/:lang?/about", { lang: "en" }) | ||
| * // -> `/en/about` | ||
| * | ||
| * <Link to={href("/products/:id", { id: "abc123" })} /> | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @param path The route path to resolve | ||
| * @param args The route params to use when resolving the path | ||
| * @returns The resolved URL path | ||
| */ | ||
@@ -33,7 +50,7 @@ function href(path, ...args) { | ||
| if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`); | ||
| return value == null ? "" : "/" + encodeURIComponent(stringify(value)); | ||
| return value == null ? "" : "/" + encodePathParam(stringify(value)); | ||
| }); | ||
| if (path.endsWith("*")) { | ||
| const value = params?.["*"]; | ||
| if (value !== void 0) result += "/" + stringify(value).split("/").map(encodeURIComponent).join("/"); | ||
| if (value !== void 0) result += "/" + stringify(value).split("/").map(encodePathParam).join("/"); | ||
| } | ||
@@ -40,0 +57,0 @@ return result || "/"; |
@@ -239,3 +239,6 @@ | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param path The pathname, search, and hash components to combine. | ||
| * @returns The combined URL path. | ||
| */ | ||
@@ -250,3 +253,6 @@ declare function createPath({ | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param path The URL path to parse. | ||
| * @returns The parsed pathname, search, and hash components. | ||
| */ | ||
@@ -253,0 +259,0 @@ declare function parsePath(path: string): Partial<Path>; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -234,3 +234,6 @@ * Copyright (c) Remix Software Inc. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param path The pathname, search, and hash components to combine. | ||
| * @returns The combined URL path. | ||
| */ | ||
@@ -245,3 +248,6 @@ function createPath({ pathname = "/", search = "", hash = "" }) { | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param path The URL path to parse. | ||
| * @returns The parsed pathname, search, and hash components. | ||
| */ | ||
@@ -248,0 +254,0 @@ function parsePath(path) { |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -689,2 +689,12 @@ | ||
| * | ||
| * Param values are percent-encoded for use in a path segment: characters that | ||
| * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) | ||
| * are escaped, while characters that RFC 3986 allows literally in a path | ||
| * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string | ||
| * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are | ||
| * delimiters and must be escaped. Splat (`*`) values are encoded per segment, | ||
| * preserving `/` separators. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) | ||
| * | ||
| * @example | ||
@@ -694,2 +704,4 @@ * import { generatePath } from "react-router"; | ||
| * generatePath("/users/:id", { id: "123" }); // "/users/123" | ||
| * generatePath("/files/:name", { name: "a b" }); // "/files/a%20b" | ||
| * generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1" | ||
| * | ||
@@ -696,0 +708,0 @@ * @public |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -378,4 +378,56 @@ * Copyright (c) Remix Software Inc. | ||
| /** | ||
| * Characters that `encodeURIComponent` escapes but that are valid literally in | ||
| * a URL path segment. Per RFC 3986 §3.3, a path segment is made of `pchar`: | ||
| * | ||
| * ``` | ||
| * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
| * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" | ||
| * ``` | ||
| * | ||
| * `encodeURIComponent` targets query-string values, where `$ & + , ; = : @` | ||
| * are delimiters and must be escaped — but in a path segment they carry no | ||
| * special meaning, and browsers keep them literal in `location.pathname`. | ||
| * (`! ' ( ) *` and the unreserved set are already left alone by | ||
| * `encodeURIComponent`, so they need no restoring.) | ||
| */ | ||
| const PATH_PARAM_OVERESCAPED = { | ||
| "%24": "$", | ||
| "%26": "&", | ||
| "%2B": "+", | ||
| "%2C": ",", | ||
| "%3A": ":", | ||
| "%3B": ";", | ||
| "%3D": "=", | ||
| "%40": "@" | ||
| }; | ||
| /** | ||
| * Encodes a param value for interpolation into a single URL path segment. | ||
| * | ||
| * Escapes characters that would break the path (`/ ? # %`, whitespace, | ||
| * non-ASCII, …) while leaving characters that RFC 3986 permits literally in a | ||
| * path segment untouched. Escaping those would needlessly rewrite URLs — e.g. | ||
| * a semver build param `1.0.0+1` would become `1.0.0%2B1` even though browsers | ||
| * display and match the `+` literally in `location.pathname`. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)) | ||
| * | ||
| * @param value The param value to encode. | ||
| * @returns The encoded value, safe for use as a single path segment. | ||
| */ | ||
| function encodePathParam(value) { | ||
| return encodeURIComponent(value).replace(/%(?:24|26|2B|2C|3A|3B|3D|40)/g, (match) => PATH_PARAM_OVERESCAPED[match]); | ||
| } | ||
| /** | ||
| * Returns a path with params interpolated. | ||
| * | ||
| * Param values are percent-encoded for use in a path segment: characters that | ||
| * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) | ||
| * are escaped, while characters that RFC 3986 allows literally in a path | ||
| * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string | ||
| * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are | ||
| * delimiters and must be escaped. Splat (`*`) values are encoded per segment, | ||
| * preserving `/` separators. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) | ||
| * | ||
| * @example | ||
@@ -385,2 +437,4 @@ * import { generatePath } from "react-router"; | ||
| * generatePath("/users/:id", { id: "123" }); // "/users/123" | ||
| * generatePath("/files/:name", { name: "a b" }); // "/files/a%20b" | ||
| * generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1" | ||
| * | ||
@@ -408,3 +462,3 @@ * @public | ||
| invariant(optional === "?" || param != null, `Missing ":${key}" param`); | ||
| return encodeURIComponent(stringify(param)) + suffix; | ||
| return encodePathParam(stringify(param)) + suffix; | ||
| } | ||
@@ -830,2 +884,2 @@ return segment.replace(/\?$/g, ""); | ||
| //#endregion | ||
| 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 }; | ||
| 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 }; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -12,3 +12,3 @@ * Copyright (c) Remix Software Inc. | ||
| import { PROTOCOL_RELATIVE_URL_REGEX } from "../router/url.js"; | ||
| import { createBrowserHistory, invariant } from "../router/history.js"; | ||
| import { createBrowserHistory, createPath, invariant } from "../router/history.js"; | ||
| import { ErrorResponseImpl, createContext, resolvePath } from "../router/utils.js"; | ||
@@ -21,3 +21,3 @@ import { createRouter, hasInvalidProtocol, isMutationMethod } from "../router/router.js"; | ||
| import { noActionDefinedError, shouldHydrateRouteLoader } from "../dom/ssr/routes.js"; | ||
| import { getPathsWithAncestors } from "../dom/ssr/fog-of-war.js"; | ||
| import { getPathsWithAncestors, handleClientVersionMismatch } from "../dom/ssr/fog-of-war.js"; | ||
| import { FrameworkContext, setIsHydrated } from "../dom/ssr/components.js"; | ||
@@ -138,2 +138,3 @@ import { RSCRouterGlobalErrorBoundary } from "./errorBoundaries.js"; | ||
| if (payload.type !== "render") throw new Error("Invalid payload type"); | ||
| let { clientVersion } = payload; | ||
| globalVar.__reactRouterRouteModules = globalVar.__reactRouterRouteModules ?? {}; | ||
@@ -173,3 +174,3 @@ populateRSCRouteModules(globalVar.__reactRouterRouteModules, payload.matches); | ||
| }), | ||
| async patchRoutesOnNavigation({ path, signal }) { | ||
| async patchRoutesOnNavigation({ path, signal, fetcherKey }) { | ||
| if (payload.routeDiscovery.mode === "initial") { | ||
@@ -189,5 +190,6 @@ if (!applyPatchesPromise) applyPatchesPromise = (async () => { | ||
| if (discoveredPaths.has(path)) return; | ||
| await fetchAndApplyManifestPatches([path], createFromReadableStream, fetchImplementation, signal); | ||
| let { state } = globalVar.__reactRouterDataRouter; | ||
| await fetchAndApplyManifestPatches([path], createFromReadableStream, fetchImplementation, clientVersion, fetcherKey ? window.location.href : createPath(state.navigation.location || state.location), signal); | ||
| }, | ||
| dataStrategy: getRSCSingleFetchDataStrategy(() => globalVar.__reactRouterDataRouter, true, createFromReadableStream, fetchImplementation) | ||
| dataStrategy: getRSCSingleFetchDataStrategy(() => globalVar.__reactRouterDataRouter, true, createFromReadableStream, fetchImplementation, clientVersion) | ||
| }); | ||
@@ -245,3 +247,3 @@ if (globalVar.__reactRouterDataRouter.state.initialized) { | ||
| const renderedRoutesContext = createContext(); | ||
| function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream, fetchImplementation) { | ||
| function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream, fetchImplementation, clientVersion) { | ||
| let dataStrategy = getSingleFetchDataStrategyImpl(getRouter, (match) => { | ||
@@ -251,10 +253,7 @@ let M = match; | ||
| hasLoader: M.route.hasLoader, | ||
| hasClientLoader: M.route.hasClientLoader, | ||
| hasComponent: M.route.hasComponent, | ||
| hasAction: M.route.hasAction, | ||
| hasClientAction: M.route.hasClientAction | ||
| hasClientLoader: M.route.hasClientLoader | ||
| }; | ||
| }, getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation), ssr, (match) => { | ||
| }, getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion), ssr, (match) => { | ||
| let M = match; | ||
| return M.route.hasComponent && !M.route.element; | ||
| return !M.route.hasComponent || M.route.element != null; | ||
| }); | ||
@@ -278,3 +277,3 @@ return async (args) => args.runClientMiddleware(async () => { | ||
| } | ||
| function getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation) { | ||
| function getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion) { | ||
| return async (args, targetRoutes) => { | ||
@@ -303,2 +302,3 @@ let { request, context } = args; | ||
| if (payload.type !== "render") throw new Error("Unexpected payload type"); | ||
| if (clientVersion !== void 0 && await handleClientVersionMismatch(payload.clientVersion !== clientVersion, clientVersion, createPath(getRouter().state.navigation.location || getRouter().state.location))) return new Promise(() => {}); | ||
| context.get(renderedRoutesContext).push(...payload.matches); | ||
@@ -359,3 +359,3 @@ let results = { routes: {} }; | ||
| if (payload.type !== "render") throw new Error("Invalid payload type"); | ||
| let { routeDiscovery } = payload; | ||
| let { routeDiscovery, clientVersion } = payload; | ||
| let { router, routeModules } = React$1.useMemo(() => createRouterFromPayload({ | ||
@@ -426,3 +426,3 @@ payload, | ||
| try { | ||
| await fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation); | ||
| await fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, null); | ||
| } catch (e) { | ||
@@ -447,3 +447,4 @@ console.error("Failed to fetch manifest patches", e); | ||
| createFromReadableStream, | ||
| fetchImplementation | ||
| fetchImplementation, | ||
| clientVersion | ||
| ]); | ||
@@ -521,2 +522,3 @@ const frameworkContext = { | ||
| hasClientLoader: match.clientLoader != null, | ||
| hasComponent: match.hasComponent, | ||
| hasAction: match.hasAction, | ||
@@ -542,13 +544,17 @@ hasClientAction: match.clientAction != null | ||
| const discoveredPaths = /* @__PURE__ */ new Set(); | ||
| function getManifestUrl(paths) { | ||
| function getManifestUrl(paths, clientVersion) { | ||
| if (paths.length === 0) return null; | ||
| if (paths.length === 1) return new URL(`${paths[0]}.manifest`, window.location.origin); | ||
| let basename = (window.__reactRouterDataRouter.basename ?? "").replace(/^\/|\/$/g, ""); | ||
| let url = new URL(`${basename}/.manifest`, window.location.origin); | ||
| url.searchParams.set("paths", paths.sort().join(",")); | ||
| let url; | ||
| if (paths.length === 1) url = new URL(`${paths[0]}.manifest`, window.location.origin); | ||
| else { | ||
| let basename = (window.__reactRouterDataRouter.basename ?? "").replace(/^\/|\/$/g, ""); | ||
| url = new URL(`${basename}/.manifest`, window.location.origin); | ||
| url.searchParams.set("paths", paths.sort().join(",")); | ||
| } | ||
| if (clientVersion !== void 0) url.searchParams.set("version", clientVersion); | ||
| return url; | ||
| } | ||
| async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, signal) { | ||
| async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, errorReloadPath, signal) { | ||
| paths = getPathsWithAncestors(paths); | ||
| let url = getManifestUrl(paths); | ||
| let url = getManifestUrl(paths, clientVersion); | ||
| if (url == null) return; | ||
@@ -560,2 +566,6 @@ if (url.toString().length > 7680) { | ||
| let response = await fetchImplementation(new Request(url, { signal })); | ||
| if (clientVersion !== void 0 && response.status === 204 && response.headers.has("X-Remix-Reload-Document")) { | ||
| await handleClientVersionMismatch(true, clientVersion, errorReloadPath); | ||
| return; | ||
| } | ||
| if (!response.body || response.status < 200 || response.status >= 300) throw new Error("Unable to fetch new route matches from the server"); | ||
@@ -562,0 +572,0 @@ let payload = await createFromReadableStream(response.body, { temporaryReferences: void 0 }); |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -14,3 +14,3 @@ * Copyright (c) Remix Software Inc. | ||
| const trailer = "</body></html>"; | ||
| function injectRSCPayload(rscStream) { | ||
| function injectRSCPayload(rscStream, { nonce } = {}) { | ||
| let decoder = new TextDecoder(); | ||
@@ -20,2 +20,4 @@ let resolveFlightDataPromise; | ||
| let startedRSC = false; | ||
| let cancelled = false; | ||
| let rscReader = null; | ||
| let buffered = []; | ||
@@ -37,6 +39,8 @@ let timeout = null; | ||
| timeout = setTimeout(async () => { | ||
| if (cancelled) return; | ||
| flushBufferedChunks(controller); | ||
| if (!startedRSC) { | ||
| startedRSC = true; | ||
| writeRSCStream(rscStream, controller).catch((err) => controller.error(err)).then(resolveFlightDataPromise); | ||
| rscReader = rscStream.getReader(); | ||
| writeRSCStream(rscReader, controller, () => cancelled, nonce).catch((err) => controller.error(err)).then(resolveFlightDataPromise); | ||
| } | ||
@@ -52,16 +56,27 @@ }, 0); | ||
| controller.enqueue(encoder.encode("</body></html>")); | ||
| }, | ||
| async cancel(reason) { | ||
| cancelled = true; | ||
| if (timeout) { | ||
| clearTimeout(timeout); | ||
| timeout = null; | ||
| } | ||
| buffered.length = 0; | ||
| if (rscReader) await rscReader.cancel(reason).catch(() => {}); | ||
| else await rscStream.cancel(reason).catch(() => {}); | ||
| resolveFlightDataPromise(); | ||
| } | ||
| }); | ||
| } | ||
| async function writeRSCStream(rscStream, controller) { | ||
| async function writeRSCStream(reader, controller, isCancelled, nonce) { | ||
| let decoder = new TextDecoder("utf-8", { fatal: true }); | ||
| const reader = rscStream.getReader(); | ||
| try { | ||
| let read; | ||
| while ((read = await reader.read()) && !read.done) { | ||
| if (isCancelled()) return; | ||
| const chunk = read.value; | ||
| try { | ||
| writeChunk(JSON.stringify(decoder.decode(chunk, { stream: true })), controller); | ||
| writeChunk(JSON.stringify(decoder.decode(chunk, { stream: true })), controller, nonce); | ||
| } catch (e) { | ||
| writeChunk(`Uint8Array.from(atob(${JSON.stringify(btoa(String.fromCodePoint(...chunk)))}), m => m.codePointAt(0))`, controller); | ||
| writeChunk(`Uint8Array.from(atob(${JSON.stringify(btoa(String.fromCodePoint(...chunk)))}), m => m.codePointAt(0))`, controller, nonce); | ||
| } | ||
@@ -73,7 +88,11 @@ } | ||
| let remaining = decoder.decode(); | ||
| if (remaining.length) writeChunk(JSON.stringify(remaining), controller); | ||
| if (remaining.length && !isCancelled()) writeChunk(JSON.stringify(remaining), controller, nonce); | ||
| } | ||
| function writeChunk(chunk, controller) { | ||
| controller.enqueue(encoder.encode(`<script>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}<\/script>`)); | ||
| function writeChunk(chunk, controller, nonce) { | ||
| let nonceAttr = nonce == null ? "" : ` nonce="${escapeAttribute(nonce)}"`; | ||
| controller.enqueue(encoder.encode(`<script${nonceAttr}>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}<\/script>`)); | ||
| } | ||
| function escapeAttribute(value) { | ||
| return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">"); | ||
| } | ||
| function escapeScript(script) { | ||
@@ -80,0 +99,0 @@ return script.replace(/<!--/g, "<\\!--").replace(/<\/(script)/gi, "</\\$1"); |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -68,2 +68,3 @@ | ||
| basename: string | undefined; | ||
| clientVersion?: string; | ||
| errors: Record<string, any> | null; | ||
@@ -75,3 +76,2 @@ loaderData: Record<string, any>; | ||
| patches?: Promise<RSCRouteManifest[]>; | ||
| nonce?: string; | ||
| formState?: ReactFormState; | ||
@@ -173,2 +173,4 @@ }; | ||
| * `loadServerAction` function, used to load a server action by ID. | ||
| * @param opts.clientVersion A version derived from the client build output used | ||
| * to detect stale clients during lazy route discovery. | ||
| * @param opts.onError An optional error handler that will be called with any | ||
@@ -197,2 +199,3 @@ * errors that occur during the request processing. | ||
| decodeFormState, | ||
| clientVersion, | ||
| onError, | ||
@@ -211,2 +214,3 @@ request, | ||
| loadServerAction?: LoadServerActionFunction; | ||
| clientVersion?: string; | ||
| onError?: (error: unknown) => void; | ||
@@ -213,0 +217,0 @@ request: Request; |
@@ -29,8 +29,13 @@ | ||
| * createFromReadableStream, | ||
| * async renderHTML(getPayload) { | ||
| * nonce, | ||
| * async renderHTML(getPayload, options) { | ||
| * const payload = getPayload(); | ||
| * | ||
| * return await renderHTMLToReadableStream( | ||
| * <RSCStaticRouter getPayload={getPayload} />, | ||
| * <RSCStaticRouter | ||
| * getPayload={getPayload} | ||
| * nonce={options.nonce} | ||
| * />, | ||
| * { | ||
| * ...options, | ||
| * bootstrapScriptContent, | ||
@@ -53,2 +58,4 @@ * formState: await payload.formState, | ||
| * Defaults to `true`. | ||
| * @param opts.nonce An optional [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce) | ||
| * for inline scripts generated while rendering the HTML document. | ||
| * @param opts.renderHTML A function that renders the {@link unstable_RSCPayload} to | ||
@@ -66,3 +73,4 @@ * HTML, usually using a {@link unstable_RSCStaticRouter | `<RSCStaticRouter>`}. | ||
| renderHTML, | ||
| hydrate | ||
| hydrate, | ||
| nonce | ||
| }: { | ||
@@ -73,2 +81,3 @@ request: Request; | ||
| renderHTML: (getPayload: () => DecodedPayload, options: { | ||
| nonce?: string; | ||
| onError(error: unknown): string | undefined; | ||
@@ -78,2 +87,3 @@ onHeaders(headers: Headers): void; | ||
| hydrate?: boolean; | ||
| nonce?: string; | ||
| }): Promise<Response>; | ||
@@ -92,2 +102,8 @@ /** | ||
| getPayload: () => DecodedPayload; | ||
| /** | ||
| * An optional [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce) | ||
| * used as the default for nonce-aware components such as `<Links>` and | ||
| * `<ScrollRestoration>`. | ||
| */ | ||
| nonce?: string; | ||
| } | ||
@@ -110,8 +126,13 @@ /** | ||
| * createFromReadableStream, | ||
| * async renderHTML(getPayload) { | ||
| * nonce, | ||
| * async renderHTML(getPayload, options) { | ||
| * const payload = getPayload(); | ||
| * | ||
| * return await renderHTMLToReadableStream( | ||
| * <RSCStaticRouter getPayload={getPayload} />, | ||
| * <RSCStaticRouter | ||
| * getPayload={getPayload} | ||
| * nonce={options.nonce} | ||
| * />, | ||
| * { | ||
| * ...options, | ||
| * bootstrapScriptContent, | ||
@@ -130,8 +151,10 @@ * formState: await payload.formState, | ||
| * @param {unstable_RSCStaticRouterProps.getPayload} props.getPayload n/a | ||
| * @param {unstable_RSCStaticRouterProps.nonce} props.nonce n/a | ||
| * @returns A React component that renders the {@link unstable_RSCPayload} as HTML. | ||
| */ | ||
| declare function RSCStaticRouter({ | ||
| getPayload | ||
| getPayload, | ||
| nonce | ||
| }: RSCStaticRouterProps): React$1.JSX.Element | null; | ||
| //#endregion | ||
| export { RSCStaticRouter, RSCStaticRouterProps, SSRCreateFromReadableStreamFunction, routeRSCServerRequest }; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -43,8 +43,13 @@ * Copyright (c) Remix Software Inc. | ||
| * createFromReadableStream, | ||
| * async renderHTML(getPayload) { | ||
| * nonce, | ||
| * async renderHTML(getPayload, options) { | ||
| * const payload = getPayload(); | ||
| * | ||
| * return await renderHTMLToReadableStream( | ||
| * <RSCStaticRouter getPayload={getPayload} />, | ||
| * <RSCStaticRouter | ||
| * getPayload={getPayload} | ||
| * nonce={options.nonce} | ||
| * />, | ||
| * { | ||
| * ...options, | ||
| * bootstrapScriptContent, | ||
@@ -67,2 +72,4 @@ * formState: await payload.formState, | ||
| * Defaults to `true`. | ||
| * @param opts.nonce An optional [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce) | ||
| * for inline scripts generated while rendering the HTML document. | ||
| * @param opts.renderHTML A function that renders the {@link unstable_RSCPayload} to | ||
@@ -75,3 +82,3 @@ * HTML, usually using a {@link unstable_RSCStaticRouter | `<RSCStaticRouter>`}. | ||
| */ | ||
| async function routeRSCServerRequest({ request, serverResponse, createFromReadableStream, renderHTML, hydrate = true }) { | ||
| async function routeRSCServerRequest({ request, serverResponse, createFromReadableStream, renderHTML, hydrate = true, nonce }) { | ||
| const url = new URL(request.url); | ||
@@ -146,2 +153,3 @@ if (isReactServerRequest(url) || isManifestRequest(url) || request.headers.has("rsc-action-id") || serverResponse.headers.get("React-Router-Resource") === "true") return serverResponse; | ||
| let html = await renderHTML(getPayload, { | ||
| nonce, | ||
| onError(error) { | ||
@@ -187,3 +195,3 @@ if (typeof error === "object" && error && "digest" in error && typeof error.digest === "string") { | ||
| if (!serverResponseB?.body) throw new Error("Failed to clone server response"); | ||
| const body = html.pipeThrough(injectRSCPayload(serverResponseB.body)).pipeThrough(redirectTransform); | ||
| const body = html.pipeThrough(injectRSCPayload(serverResponseB.body, { nonce })).pipeThrough(redirectTransform); | ||
| return new Response(body, { | ||
@@ -227,2 +235,3 @@ status, | ||
| }, { | ||
| nonce, | ||
| onError(error) { | ||
@@ -267,3 +276,3 @@ if (typeof error === "object" && error && "digest" in error && typeof error.digest === "string") { | ||
| if (!serverResponseB?.body) throw new Error("Failed to clone server response"); | ||
| const body = html.pipeThrough(injectRSCPayload(serverResponseB.body)).pipeThrough(retryRedirectTransform); | ||
| const body = html.pipeThrough(injectRSCPayload(serverResponseB.body, { nonce })).pipeThrough(retryRedirectTransform); | ||
| return new Response(body, { | ||
@@ -294,8 +303,13 @@ status, | ||
| * createFromReadableStream, | ||
| * async renderHTML(getPayload) { | ||
| * nonce, | ||
| * async renderHTML(getPayload, options) { | ||
| * const payload = getPayload(); | ||
| * | ||
| * return await renderHTMLToReadableStream( | ||
| * <RSCStaticRouter getPayload={getPayload} />, | ||
| * <RSCStaticRouter | ||
| * getPayload={getPayload} | ||
| * nonce={options.nonce} | ||
| * />, | ||
| * { | ||
| * ...options, | ||
| * bootstrapScriptContent, | ||
@@ -314,5 +328,6 @@ * formState: await payload.formState, | ||
| * @param {unstable_RSCStaticRouterProps.getPayload} props.getPayload n/a | ||
| * @param {unstable_RSCStaticRouterProps.nonce} props.nonce n/a | ||
| * @returns A React component that renders the {@link unstable_RSCPayload} as HTML. | ||
| */ | ||
| function RSCStaticRouter({ getPayload }) { | ||
| function RSCStaticRouter({ getPayload, nonce }) { | ||
| const decoded = getPayload(); | ||
@@ -397,3 +412,4 @@ const payload = React$1.use(decoded); | ||
| }, | ||
| routeModules: createRSCRouteModules(payload) | ||
| routeModules: createRSCRouteModules(payload), | ||
| nonce | ||
| }; | ||
@@ -403,4 +419,3 @@ return /* @__PURE__ */ React$1.createElement(RSCRouterContext.Provider, { value: true }, /* @__PURE__ */ React$1.createElement(RSCRouterGlobalErrorBoundary, { location: payload.location }, /* @__PURE__ */ React$1.createElement(FrameworkContext.Provider, { value: frameworkContext }, /* @__PURE__ */ React$1.createElement(StaticRouterProvider, { | ||
| router, | ||
| hydrate: false, | ||
| nonce: payload.nonce | ||
| hydrate: false | ||
| })))); | ||
@@ -407,0 +422,0 @@ } |
@@ -56,9 +56,35 @@ | ||
| * Creates a logical container for managing a browser cookie from the server. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param name The name of the cookie. | ||
| * @param cookieOptions Options for parsing and serializing the cookie. | ||
| * @returns A {@link Cookie} object for parsing and serializing the cookie. | ||
| */ | ||
| declare const createCookie: (name: string, cookieOptions?: CookieOptions) => Cookie; | ||
| /** | ||
| * A function that determines whether a value is a React Router {@link Cookie} | ||
| * object. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Cookie} object; | ||
| * otherwise, `false`. | ||
| */ | ||
| type IsCookieFunction = (object: any) => object is Cookie; | ||
| /** | ||
| * Returns true if an object is a Remix cookie container. | ||
| * Returns `true` if a value is a React Router {@link Cookie} object. | ||
| * | ||
| * @see https://remix.run/utils/cookies#iscookie | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Cookie} object; | ||
| * otherwise, `false`. | ||
| */ | ||
@@ -65,0 +91,0 @@ declare const isCookie: IsCookieFunction; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -17,2 +17,10 @@ * Copyright (c) Remix Software Inc. | ||
| * Creates a logical container for managing a browser cookie from the server. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param name The name of the cookie. | ||
| * @param cookieOptions Options for parsing and serializing the cookie. | ||
| * @returns A {@link Cookie} object for parsing and serializing the cookie. | ||
| */ | ||
@@ -57,5 +65,11 @@ const createCookie = (name, cookieOptions = {}) => { | ||
| /** | ||
| * Returns true if an object is a Remix cookie container. | ||
| * Returns `true` if a value is a React Router {@link Cookie} object. | ||
| * | ||
| * @see https://remix.run/utils/cookies#iscookie | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Cookie} object; | ||
| * otherwise, `false`. | ||
| */ | ||
@@ -62,0 +76,0 @@ const isCookie = (object) => { |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -8,4 +8,16 @@ | ||
| type CreateRequestHandlerFunction = (build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>), mode?: string) => RequestHandler; | ||
| /** | ||
| * Creates a request handler for a React Router server build. | ||
| * | ||
| * This is a low-level API used by server adapters to translate incoming | ||
| * requests into React Router responses. | ||
| * | ||
| * @category Utils | ||
| * @param build The server build, or a function that resolves to the server | ||
| * build, used to handle requests. | ||
| * @param mode The mode in which the server build is running. | ||
| * @returns A request handler that returns a response for each incoming request. | ||
| */ | ||
| declare const createRequestHandler: CreateRequestHandlerFunction; | ||
| //#endregion | ||
| export { CreateRequestHandlerFunction, RequestHandler, createRequestHandler }; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -140,2 +140,14 @@ * Copyright (c) Remix Software Inc. | ||
| } | ||
| /** | ||
| * Creates a request handler for a React Router server build. | ||
| * | ||
| * This is a low-level API used by server adapters to translate incoming | ||
| * requests into React Router responses. | ||
| * | ||
| * @category Utils | ||
| * @param build The server build, or a function that resolves to the server | ||
| * build, used to handle requests. | ||
| * @param mode The mode in which the server build is running. | ||
| * @returns A request handler that returns a response for each incoming request. | ||
| */ | ||
| const createRequestHandler = (build, mode) => { | ||
@@ -142,0 +154,0 @@ let _build; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -63,9 +63,33 @@ | ||
| * Instead, use a `SessionStorage` object's `getSession` method. | ||
| * | ||
| * @category Utils | ||
| * @param initialData The initial data for the session. | ||
| * @param id The identifier for the session. Defaults to an empty string for a | ||
| * new session. | ||
| * @returns A new {@link Session} object. | ||
| */ | ||
| declare const createSession: CreateSessionFunction; | ||
| /** | ||
| * A function that determines whether a value is a React Router {@link Session} | ||
| * object. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Session} object; | ||
| * otherwise, `false`. | ||
| */ | ||
| type IsSessionFunction = (object: any) => object is Session; | ||
| /** | ||
| * Returns true if an object is a React Router session. | ||
| * Returns `true` if a value is a React Router {@link Session} object. | ||
| * | ||
| * @see https://reactrouter.com/api/utils/isSession | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Session} object; | ||
| * otherwise, `false`. | ||
| */ | ||
@@ -137,2 +161,7 @@ declare const isSession: IsSessionFunction; | ||
| * existing session storage options meet your requirements. | ||
| * | ||
| * @category Utils | ||
| * @param strategy The strategy used to store session identifiers and data. | ||
| * @returns A {@link SessionStorage} object that persists session data using the | ||
| * provided strategy. | ||
| */ | ||
@@ -139,0 +168,0 @@ declare function createSessionStorage<Data = SessionData, FlashData = Data>({ |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -22,2 +22,8 @@ * Copyright (c) Remix Software Inc. | ||
| * Instead, use a `SessionStorage` object's `getSession` method. | ||
| * | ||
| * @category Utils | ||
| * @param initialData The initial data for the session. | ||
| * @param id The identifier for the session. Defaults to an empty string for a | ||
| * new session. | ||
| * @returns A new {@link Session} object. | ||
| */ | ||
@@ -57,5 +63,11 @@ const createSession = (initialData = {}, id = "") => { | ||
| /** | ||
| * Returns true if an object is a React Router session. | ||
| * Returns `true` if a value is a React Router {@link Session} object. | ||
| * | ||
| * @see https://reactrouter.com/api/utils/isSession | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Session} object; | ||
| * otherwise, `false`. | ||
| */ | ||
@@ -70,2 +82,7 @@ const isSession = (object) => { | ||
| * existing session storage options meet your requirements. | ||
| * | ||
| * @category Utils | ||
| * @param strategy The strategy used to store session identifiers and data. | ||
| * @returns A {@link SessionStorage} object that persists session data using the | ||
| * provided strategy. | ||
| */ | ||
@@ -72,0 +89,0 @@ function createSessionStorage({ cookie: cookieArg, createData, readData, updateData, deleteData }) { |
@@ -20,2 +20,10 @@ | ||
| * browser's maximum cookie size. Trade-offs! | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param options Options for creating the cookie-backed session storage. | ||
| * @returns A {@link SessionStorage} object that stores all session data in its | ||
| * cookie. | ||
| */ | ||
@@ -22,0 +30,0 @@ declare function createCookieSessionStorage<Data = SessionData, FlashData = Data>({ |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -22,2 +22,10 @@ * Copyright (c) Remix Software Inc. | ||
| * browser's maximum cookie size. Trade-offs! | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param options Options for creating the cookie-backed session storage. | ||
| * @returns A {@link SessionStorage} object that stores all session data in its | ||
| * cookie. | ||
| */ | ||
@@ -24,0 +32,0 @@ function createCookieSessionStorage({ cookie: cookieArg } = {}) { |
@@ -13,7 +13,13 @@ | ||
| /** | ||
| * Creates and returns a simple in-memory SessionStorage object, mostly useful | ||
| * for testing and as a reference implementation. | ||
| * Creates and returns a simple in-memory SessionStorage object. | ||
| * | ||
| * Note: This storage does not scale beyond a single process, so it is not | ||
| * suitable for most production scenarios. | ||
| * Intended for local development and testing. It does not scale beyond a single | ||
| * process, and all session data is lost when the server process stops/restarts. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param options Options for creating the in-memory session storage. | ||
| * @returns A {@link SessionStorage} object that stores session data in memory. | ||
| */ | ||
@@ -20,0 +26,0 @@ declare function createMemorySessionStorage<Data = SessionData, FlashData = Data>({ |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -14,7 +14,13 @@ * Copyright (c) Remix Software Inc. | ||
| /** | ||
| * Creates and returns a simple in-memory SessionStorage object, mostly useful | ||
| * for testing and as a reference implementation. | ||
| * Creates and returns a simple in-memory SessionStorage object. | ||
| * | ||
| * Note: This storage does not scale beyond a single process, so it is not | ||
| * suitable for most production scenarios. | ||
| * Intended for local development and testing. It does not scale beyond a single | ||
| * process, and all session data is lost when the server process stops/restarts. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param options Options for creating the in-memory session storage. | ||
| * @returns A {@link SessionStorage} object that stores session data in memory. | ||
| */ | ||
@@ -26,3 +32,3 @@ function createMemorySessionStorage({ cookie } = {}) { | ||
| async createData(data, expires) { | ||
| let id = Math.random().toString(36).substring(2, 10); | ||
| let id = crypto.randomUUID(); | ||
| map.set(id, { | ||
@@ -29,0 +35,0 @@ data, |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -9,26 +9,26 @@ | ||
| /** | ||
| Creates a URLSearchParams object using the given initializer. | ||
| This is identical to `new URLSearchParams(init)` except it also | ||
| supports arrays as values in the object form of the initializer | ||
| instead of just strings. This is convenient when you need multiple | ||
| values for a given key, but don't want to use an array initializer. | ||
| For example, instead of: | ||
| ```tsx | ||
| let searchParams = new URLSearchParams([ | ||
| ['sort', 'name'], | ||
| ['sort', 'price'] | ||
| ]); | ||
| ``` | ||
| you can do: | ||
| ``` | ||
| let searchParams = createSearchParams({ | ||
| sort: ['name', 'price'] | ||
| }); | ||
| ``` | ||
| @category Utils | ||
| * Creates a URLSearchParams object using the given initializer. | ||
| * | ||
| * This is identical to `new URLSearchParams(init)` except it also supports | ||
| * arrays as values in the object form of the initializer instead of just | ||
| * strings. This is convenient when you need multiple values for a given key, | ||
| * but don't want to use an array initializer. | ||
| * | ||
| * @example | ||
| * // Instead of: | ||
| * let searchParams = new URLSearchParams([ | ||
| * ["sort", "name"], | ||
| * ["sort", "price"], | ||
| * ]); | ||
| * | ||
| * // You can do: | ||
| * let searchParams = createSearchParams({ | ||
| * sort: ["name", "price"], | ||
| * }); | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param init The value used to initialize the URL search parameters. | ||
| * @returns A URLSearchParams object containing the initialized search | ||
| * parameters. | ||
| */ | ||
@@ -35,0 +35,0 @@ declare function createSearchParams(init?: URLSearchParamsInit): URLSearchParams; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -33,26 +33,26 @@ * Copyright (c) Remix Software Inc. | ||
| /** | ||
| Creates a URLSearchParams object using the given initializer. | ||
| This is identical to `new URLSearchParams(init)` except it also | ||
| supports arrays as values in the object form of the initializer | ||
| instead of just strings. This is convenient when you need multiple | ||
| values for a given key, but don't want to use an array initializer. | ||
| For example, instead of: | ||
| ```tsx | ||
| let searchParams = new URLSearchParams([ | ||
| ['sort', 'name'], | ||
| ['sort', 'price'] | ||
| ]); | ||
| ``` | ||
| you can do: | ||
| ``` | ||
| let searchParams = createSearchParams({ | ||
| sort: ['name', 'price'] | ||
| }); | ||
| ``` | ||
| @category Utils | ||
| * Creates a URLSearchParams object using the given initializer. | ||
| * | ||
| * This is identical to `new URLSearchParams(init)` except it also supports | ||
| * arrays as values in the object form of the initializer instead of just | ||
| * strings. This is convenient when you need multiple values for a given key, | ||
| * but don't want to use an array initializer. | ||
| * | ||
| * @example | ||
| * // Instead of: | ||
| * let searchParams = new URLSearchParams([ | ||
| * ["sort", "name"], | ||
| * ["sort", "price"], | ||
| * ]); | ||
| * | ||
| * // You can do: | ||
| * let searchParams = createSearchParams({ | ||
| * sort: ["name", "price"], | ||
| * }); | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param init The value used to initialize the URL search parameters. | ||
| * @returns A URLSearchParams object containing the initialized search | ||
| * parameters. | ||
| */ | ||
@@ -59,0 +59,0 @@ function createSearchParams(init = "") { |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -25,3 +25,3 @@ * Copyright (c) Remix Software Inc. | ||
| try { | ||
| if (isBrowser) window.__reactRouterVersion = "8.2.0"; | ||
| if (isBrowser) window.__reactRouterVersion = "8.3.0"; | ||
| } catch (e) {} | ||
@@ -390,3 +390,3 @@ /** | ||
| let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(endSlashPosition) === "/"; | ||
| let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(toPathname.length) === "/"); | ||
| let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(endSlashPosition) === "/"); | ||
| let renderProps = { | ||
@@ -614,3 +614,4 @@ isActive, | ||
| * @param options.defaultShouldRevalidate Specify the default revalidation | ||
| * behavior for the navigation. Defaults to `true`. | ||
| * behavior for the navigation. When not specified, loaders revalidate | ||
| * according to the router's standard revalidation behavior. | ||
| * @param options.mask Masked location to display in the browser instead | ||
@@ -617,0 +618,0 @@ * of the router location. Defaults to `undefined`. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -113,2 +113,25 @@ * Copyright (c) Remix Software Inc. | ||
| const MANIFEST_VERSION_STORAGE_KEY = "react-router-manifest-version"; | ||
| async function handleClientVersionMismatch(needsReload, version, errorReloadPath) { | ||
| if (!needsReload) { | ||
| try { | ||
| sessionStorage.removeItem(MANIFEST_VERSION_STORAGE_KEY); | ||
| } catch {} | ||
| return false; | ||
| } | ||
| if (!errorReloadPath) { | ||
| 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."); | ||
| return true; | ||
| } | ||
| try { | ||
| if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === version) { | ||
| console.error("Unable to discover routes due to manifest version mismatch."); | ||
| return true; | ||
| } | ||
| sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, version); | ||
| } catch {} | ||
| window.location.href = errorReloadPath; | ||
| console.warn("Detected manifest version mismatch, reloading..."); | ||
| await new Promise(() => {}); | ||
| return true; | ||
| } | ||
| async function fetchAndApplyManifestPatches(paths, errorReloadPath, manifest, routeModules, ssr, isSpaMode, basename, manifestPath, patchRoutes, signal) { | ||
@@ -129,21 +152,3 @@ paths = getPathsWithAncestors(paths); | ||
| if (!res.ok) throw new Error(`${res.status} ${res.statusText}`); | ||
| else if (res.status === 204 && res.headers.has("X-Remix-Reload-Document")) { | ||
| if (!errorReloadPath) { | ||
| 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."); | ||
| return; | ||
| } | ||
| try { | ||
| if (sessionStorage.getItem(MANIFEST_VERSION_STORAGE_KEY) === manifest.version) { | ||
| console.error("Unable to discover routes due to manifest version mismatch."); | ||
| return; | ||
| } | ||
| sessionStorage.setItem(MANIFEST_VERSION_STORAGE_KEY, manifest.version); | ||
| } catch {} | ||
| window.location.href = errorReloadPath; | ||
| console.warn("Detected manifest version mismatch, reloading..."); | ||
| await new Promise(() => {}); | ||
| } else if (res.status >= 400) throw new Error(await res.text()); | ||
| try { | ||
| sessionStorage.removeItem(MANIFEST_VERSION_STORAGE_KEY); | ||
| } catch {} | ||
| if (await handleClientVersionMismatch(res.status === 204 && res.headers.has("X-Remix-Reload-Document"), manifest.version, errorReloadPath)) return; | ||
| serverPatches = await res.json(); | ||
@@ -182,2 +187,2 @@ } catch (e) { | ||
| //#endregion | ||
| export { URL_LIMIT, getManifestPath, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, isFogOfWarEnabled, useFogOFWarDiscovery }; | ||
| export { URL_LIMIT, getManifestPath, getPartialManifest, getPatchRoutesOnNavigationFunction, getPathsWithAncestors, handleClientVersionMismatch, isFogOfWarEnabled, useFogOFWarDiscovery }; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -53,3 +53,16 @@ | ||
| /** | ||
| * Creates a React component that renders the provided routes in a test-friendly | ||
| * React Router context. | ||
| * | ||
| * Use this to unit test components that rely on router context, such as | ||
| * `loaderData`, `actionData`, and route matches. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param routes The route objects to render in the test router. | ||
| * @param _context An optional {@link RouterContextProvider} for supplying | ||
| * application context values to route middleware, loaders, and actions. | ||
| * @returns A React component that renders the test router. | ||
| */ | ||
@@ -56,0 +69,0 @@ declare function createRoutesStub(routes: StubRouteObject[], _context?: RouterContextProvider): ({ |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -17,3 +17,16 @@ * Copyright (c) Remix Software Inc. | ||
| /** | ||
| * Creates a React component that renders the provided routes in a test-friendly | ||
| * React Router context. | ||
| * | ||
| * Use this to unit test components that rely on router context, such as | ||
| * `loaderData`, `actionData`, and route matches. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param routes The route objects to render in the test router. | ||
| * @param _context An optional {@link RouterContextProvider} for supplying | ||
| * application context values to route middleware, loaders, and actions. | ||
| * @returns A React component that renders the test router. | ||
| */ | ||
@@ -20,0 +33,0 @@ function createRoutesStub(routes, _context) { |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -9,10 +9,26 @@ | ||
| /** | ||
| Returns a resolved URL path for the specified route. | ||
| ```tsx | ||
| const h = href("/:lang?/about", { lang: "en" }) | ||
| // -> `/en/about` | ||
| <Link to={href("/products/:id", { id: "abc123" })} /> | ||
| ``` | ||
| * Returns a resolved URL path for the specified route. | ||
| * | ||
| * Param values are percent-encoded for use in a path segment: characters that | ||
| * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) | ||
| * are escaped, while characters that RFC 3986 allows literally in a path | ||
| * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string | ||
| * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are | ||
| * delimiters and must be escaped. Splat (`*`) values are encoded per segment, | ||
| * preserving `/` separators. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) | ||
| * | ||
| * @example | ||
| * const h = href("/:lang?/about", { lang: "en" }) | ||
| * // -> `/en/about` | ||
| * | ||
| * <Link to={href("/products/:id", { id: "abc123" })} /> | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @param path The route path to resolve | ||
| * @param args The route params to use when resolving the path | ||
| * @returns The resolved URL path | ||
| */ | ||
@@ -19,0 +35,0 @@ declare function href<Path extends keyof Args>(path: Path, ...args: Args[Path]): string; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -11,2 +11,3 @@ * Copyright (c) Remix Software Inc. | ||
| */ | ||
| import { encodePathParam } from "./router/utils.js"; | ||
| //#region lib/href.ts | ||
@@ -17,10 +18,26 @@ function stringify(p) { | ||
| /** | ||
| Returns a resolved URL path for the specified route. | ||
| ```tsx | ||
| const h = href("/:lang?/about", { lang: "en" }) | ||
| // -> `/en/about` | ||
| <Link to={href("/products/:id", { id: "abc123" })} /> | ||
| ``` | ||
| * Returns a resolved URL path for the specified route. | ||
| * | ||
| * Param values are percent-encoded for use in a path segment: characters that | ||
| * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) | ||
| * are escaped, while characters that RFC 3986 allows literally in a path | ||
| * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string | ||
| * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are | ||
| * delimiters and must be escaped. Splat (`*`) values are encoded per segment, | ||
| * preserving `/` separators. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) | ||
| * | ||
| * @example | ||
| * const h = href("/:lang?/about", { lang: "en" }) | ||
| * // -> `/en/about` | ||
| * | ||
| * <Link to={href("/products/:id", { id: "abc123" })} /> | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @param path The route path to resolve | ||
| * @param args The route params to use when resolving the path | ||
| * @returns The resolved URL path | ||
| */ | ||
@@ -33,7 +50,7 @@ function href(path, ...args) { | ||
| if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`); | ||
| return value == null ? "" : "/" + encodeURIComponent(stringify(value)); | ||
| return value == null ? "" : "/" + encodePathParam(stringify(value)); | ||
| }); | ||
| if (path.endsWith("*")) { | ||
| const value = params?.["*"]; | ||
| if (value !== void 0) result += "/" + stringify(value).split("/").map(encodeURIComponent).join("/"); | ||
| if (value !== void 0) result += "/" + stringify(value).split("/").map(encodePathParam).join("/"); | ||
| } | ||
@@ -40,0 +57,0 @@ return result || "/"; |
@@ -239,3 +239,6 @@ | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param path The pathname, search, and hash components to combine. | ||
| * @returns The combined URL path. | ||
| */ | ||
@@ -250,3 +253,6 @@ declare function createPath({ | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param path The URL path to parse. | ||
| * @returns The parsed pathname, search, and hash components. | ||
| */ | ||
@@ -253,0 +259,0 @@ declare function parsePath(path: string): Partial<Path>; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -234,3 +234,6 @@ * Copyright (c) Remix Software Inc. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param path The pathname, search, and hash components to combine. | ||
| * @returns The combined URL path. | ||
| */ | ||
@@ -245,3 +248,6 @@ function createPath({ pathname = "/", search = "", hash = "" }) { | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @param path The URL path to parse. | ||
| * @returns The parsed pathname, search, and hash components. | ||
| */ | ||
@@ -248,0 +254,0 @@ function parsePath(path) { |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -689,2 +689,12 @@ | ||
| * | ||
| * Param values are percent-encoded for use in a path segment: characters that | ||
| * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) | ||
| * are escaped, while characters that RFC 3986 allows literally in a path | ||
| * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string | ||
| * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are | ||
| * delimiters and must be escaped. Splat (`*`) values are encoded per segment, | ||
| * preserving `/` separators. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) | ||
| * | ||
| * @example | ||
@@ -694,2 +704,4 @@ * import { generatePath } from "react-router"; | ||
| * generatePath("/users/:id", { id: "123" }); // "/users/123" | ||
| * generatePath("/files/:name", { name: "a b" }); // "/files/a%20b" | ||
| * generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1" | ||
| * | ||
@@ -696,0 +708,0 @@ * @public |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -369,4 +369,56 @@ * Copyright (c) Remix Software Inc. | ||
| /** | ||
| * Characters that `encodeURIComponent` escapes but that are valid literally in | ||
| * a URL path segment. Per RFC 3986 §3.3, a path segment is made of `pchar`: | ||
| * | ||
| * ``` | ||
| * pchar = unreserved / pct-encoded / sub-delims / ":" / "@" | ||
| * sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "=" | ||
| * ``` | ||
| * | ||
| * `encodeURIComponent` targets query-string values, where `$ & + , ; = : @` | ||
| * are delimiters and must be escaped — but in a path segment they carry no | ||
| * special meaning, and browsers keep them literal in `location.pathname`. | ||
| * (`! ' ( ) *` and the unreserved set are already left alone by | ||
| * `encodeURIComponent`, so they need no restoring.) | ||
| */ | ||
| const PATH_PARAM_OVERESCAPED = { | ||
| "%24": "$", | ||
| "%26": "&", | ||
| "%2B": "+", | ||
| "%2C": ",", | ||
| "%3A": ":", | ||
| "%3B": ";", | ||
| "%3D": "=", | ||
| "%40": "@" | ||
| }; | ||
| /** | ||
| * Encodes a param value for interpolation into a single URL path segment. | ||
| * | ||
| * Escapes characters that would break the path (`/ ? # %`, whitespace, | ||
| * non-ASCII, …) while leaving characters that RFC 3986 permits literally in a | ||
| * path segment untouched. Escaping those would needlessly rewrite URLs — e.g. | ||
| * a semver build param `1.0.0+1` would become `1.0.0%2B1` even though browsers | ||
| * display and match the `+` literally in `location.pathname`. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)) | ||
| * | ||
| * @param value The param value to encode. | ||
| * @returns The encoded value, safe for use as a single path segment. | ||
| */ | ||
| function encodePathParam(value) { | ||
| return encodeURIComponent(value).replace(/%(?:24|26|2B|2C|3A|3B|3D|40)/g, (match) => PATH_PARAM_OVERESCAPED[match]); | ||
| } | ||
| /** | ||
| * Returns a path with params interpolated. | ||
| * | ||
| * Param values are percent-encoded for use in a path segment: characters that | ||
| * would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII) | ||
| * are escaped, while characters that RFC 3986 allows literally in a path | ||
| * segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string | ||
| * encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are | ||
| * delimiters and must be escaped. Splat (`*`) values are encoded per segment, | ||
| * preserving `/` separators. | ||
| * | ||
| * See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3) | ||
| * | ||
| * @example | ||
@@ -376,2 +428,4 @@ * import { generatePath } from "react-router"; | ||
| * generatePath("/users/:id", { id: "123" }); // "/users/123" | ||
| * generatePath("/files/:name", { name: "a b" }); // "/files/a%20b" | ||
| * generatePath("/releases/:v", { v: "1.0.0+1" }); // "/releases/1.0.0+1" | ||
| * | ||
@@ -399,3 +453,3 @@ * @public | ||
| invariant(optional === "?" || param != null, `Missing ":${key}" param`); | ||
| return encodeURIComponent(stringify(param)) + suffix; | ||
| return encodePathParam(stringify(param)) + suffix; | ||
| } | ||
@@ -821,2 +875,2 @@ return segment.replace(/\?$/g, ""); | ||
| //#endregion | ||
| 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 }; | ||
| 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 }; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -12,3 +12,3 @@ * Copyright (c) Remix Software Inc. | ||
| import { PROTOCOL_RELATIVE_URL_REGEX } from "../router/url.js"; | ||
| import { createBrowserHistory, invariant } from "../router/history.js"; | ||
| import { createBrowserHistory, createPath, invariant } from "../router/history.js"; | ||
| import { ErrorResponseImpl, createContext, resolvePath } from "../router/utils.js"; | ||
@@ -21,3 +21,3 @@ import { createRouter, hasInvalidProtocol, isMutationMethod } from "../router/router.js"; | ||
| import { noActionDefinedError, shouldHydrateRouteLoader } from "../dom/ssr/routes.js"; | ||
| import { getPathsWithAncestors } from "../dom/ssr/fog-of-war.js"; | ||
| import { getPathsWithAncestors, handleClientVersionMismatch } from "../dom/ssr/fog-of-war.js"; | ||
| import { FrameworkContext, setIsHydrated } from "../dom/ssr/components.js"; | ||
@@ -138,2 +138,3 @@ import { RSCRouterGlobalErrorBoundary } from "./errorBoundaries.js"; | ||
| if (payload.type !== "render") throw new Error("Invalid payload type"); | ||
| let { clientVersion } = payload; | ||
| globalVar.__reactRouterRouteModules = globalVar.__reactRouterRouteModules ?? {}; | ||
@@ -173,3 +174,3 @@ populateRSCRouteModules(globalVar.__reactRouterRouteModules, payload.matches); | ||
| }), | ||
| async patchRoutesOnNavigation({ path, signal }) { | ||
| async patchRoutesOnNavigation({ path, signal, fetcherKey }) { | ||
| if (payload.routeDiscovery.mode === "initial") { | ||
@@ -189,5 +190,6 @@ if (!applyPatchesPromise) applyPatchesPromise = (async () => { | ||
| if (discoveredPaths.has(path)) return; | ||
| await fetchAndApplyManifestPatches([path], createFromReadableStream, fetchImplementation, signal); | ||
| let { state } = globalVar.__reactRouterDataRouter; | ||
| await fetchAndApplyManifestPatches([path], createFromReadableStream, fetchImplementation, clientVersion, fetcherKey ? window.location.href : createPath(state.navigation.location || state.location), signal); | ||
| }, | ||
| dataStrategy: getRSCSingleFetchDataStrategy(() => globalVar.__reactRouterDataRouter, true, createFromReadableStream, fetchImplementation) | ||
| dataStrategy: getRSCSingleFetchDataStrategy(() => globalVar.__reactRouterDataRouter, true, createFromReadableStream, fetchImplementation, clientVersion) | ||
| }); | ||
@@ -245,3 +247,3 @@ if (globalVar.__reactRouterDataRouter.state.initialized) { | ||
| const renderedRoutesContext = createContext(); | ||
| function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream, fetchImplementation) { | ||
| function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream, fetchImplementation, clientVersion) { | ||
| let dataStrategy = getSingleFetchDataStrategyImpl(getRouter, (match) => { | ||
@@ -251,10 +253,7 @@ let M = match; | ||
| hasLoader: M.route.hasLoader, | ||
| hasClientLoader: M.route.hasClientLoader, | ||
| hasComponent: M.route.hasComponent, | ||
| hasAction: M.route.hasAction, | ||
| hasClientAction: M.route.hasClientAction | ||
| hasClientLoader: M.route.hasClientLoader | ||
| }; | ||
| }, getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation), ssr, (match) => { | ||
| }, getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion), ssr, (match) => { | ||
| let M = match; | ||
| return M.route.hasComponent && !M.route.element; | ||
| return !M.route.hasComponent || M.route.element != null; | ||
| }); | ||
@@ -278,3 +277,3 @@ return async (args) => args.runClientMiddleware(async () => { | ||
| } | ||
| function getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation) { | ||
| function getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion) { | ||
| return async (args, targetRoutes) => { | ||
@@ -303,2 +302,3 @@ let { request, context } = args; | ||
| if (payload.type !== "render") throw new Error("Unexpected payload type"); | ||
| if (clientVersion !== void 0 && await handleClientVersionMismatch(payload.clientVersion !== clientVersion, clientVersion, createPath(getRouter().state.navigation.location || getRouter().state.location))) return new Promise(() => {}); | ||
| context.get(renderedRoutesContext).push(...payload.matches); | ||
@@ -359,3 +359,3 @@ let results = { routes: {} }; | ||
| if (payload.type !== "render") throw new Error("Invalid payload type"); | ||
| let { routeDiscovery } = payload; | ||
| let { routeDiscovery, clientVersion } = payload; | ||
| let { router, routeModules } = React$1.useMemo(() => createRouterFromPayload({ | ||
@@ -426,3 +426,3 @@ payload, | ||
| try { | ||
| await fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation); | ||
| await fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, null); | ||
| } catch (e) { | ||
@@ -447,3 +447,4 @@ console.error("Failed to fetch manifest patches", e); | ||
| createFromReadableStream, | ||
| fetchImplementation | ||
| fetchImplementation, | ||
| clientVersion | ||
| ]); | ||
@@ -521,2 +522,3 @@ const frameworkContext = { | ||
| hasClientLoader: match.clientLoader != null, | ||
| hasComponent: match.hasComponent, | ||
| hasAction: match.hasAction, | ||
@@ -542,13 +544,17 @@ hasClientAction: match.clientAction != null | ||
| const discoveredPaths = /* @__PURE__ */ new Set(); | ||
| function getManifestUrl(paths) { | ||
| function getManifestUrl(paths, clientVersion) { | ||
| if (paths.length === 0) return null; | ||
| if (paths.length === 1) return new URL(`${paths[0]}.manifest`, window.location.origin); | ||
| let basename = (window.__reactRouterDataRouter.basename ?? "").replace(/^\/|\/$/g, ""); | ||
| let url = new URL(`${basename}/.manifest`, window.location.origin); | ||
| url.searchParams.set("paths", paths.sort().join(",")); | ||
| let url; | ||
| if (paths.length === 1) url = new URL(`${paths[0]}.manifest`, window.location.origin); | ||
| else { | ||
| let basename = (window.__reactRouterDataRouter.basename ?? "").replace(/^\/|\/$/g, ""); | ||
| url = new URL(`${basename}/.manifest`, window.location.origin); | ||
| url.searchParams.set("paths", paths.sort().join(",")); | ||
| } | ||
| if (clientVersion !== void 0) url.searchParams.set("version", clientVersion); | ||
| return url; | ||
| } | ||
| async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, signal) { | ||
| async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, errorReloadPath, signal) { | ||
| paths = getPathsWithAncestors(paths); | ||
| let url = getManifestUrl(paths); | ||
| let url = getManifestUrl(paths, clientVersion); | ||
| if (url == null) return; | ||
@@ -560,2 +566,6 @@ if (url.toString().length > 7680) { | ||
| let response = await fetchImplementation(new Request(url, { signal })); | ||
| if (clientVersion !== void 0 && response.status === 204 && response.headers.has("X-Remix-Reload-Document")) { | ||
| await handleClientVersionMismatch(true, clientVersion, errorReloadPath); | ||
| return; | ||
| } | ||
| if (!response.body || response.status < 200 || response.status >= 300) throw new Error("Unable to fetch new route matches from the server"); | ||
@@ -562,0 +572,0 @@ let payload = await createFromReadableStream(response.body, { temporaryReferences: void 0 }); |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -14,3 +14,3 @@ * Copyright (c) Remix Software Inc. | ||
| const trailer = "</body></html>"; | ||
| function injectRSCPayload(rscStream) { | ||
| function injectRSCPayload(rscStream, { nonce } = {}) { | ||
| let decoder = new TextDecoder(); | ||
@@ -20,2 +20,4 @@ let resolveFlightDataPromise; | ||
| let startedRSC = false; | ||
| let cancelled = false; | ||
| let rscReader = null; | ||
| let buffered = []; | ||
@@ -37,6 +39,8 @@ let timeout = null; | ||
| timeout = setTimeout(async () => { | ||
| if (cancelled) return; | ||
| flushBufferedChunks(controller); | ||
| if (!startedRSC) { | ||
| startedRSC = true; | ||
| writeRSCStream(rscStream, controller).catch((err) => controller.error(err)).then(resolveFlightDataPromise); | ||
| rscReader = rscStream.getReader(); | ||
| writeRSCStream(rscReader, controller, () => cancelled, nonce).catch((err) => controller.error(err)).then(resolveFlightDataPromise); | ||
| } | ||
@@ -52,16 +56,27 @@ }, 0); | ||
| controller.enqueue(encoder.encode("</body></html>")); | ||
| }, | ||
| async cancel(reason) { | ||
| cancelled = true; | ||
| if (timeout) { | ||
| clearTimeout(timeout); | ||
| timeout = null; | ||
| } | ||
| buffered.length = 0; | ||
| if (rscReader) await rscReader.cancel(reason).catch(() => {}); | ||
| else await rscStream.cancel(reason).catch(() => {}); | ||
| resolveFlightDataPromise(); | ||
| } | ||
| }); | ||
| } | ||
| async function writeRSCStream(rscStream, controller) { | ||
| async function writeRSCStream(reader, controller, isCancelled, nonce) { | ||
| let decoder = new TextDecoder("utf-8", { fatal: true }); | ||
| const reader = rscStream.getReader(); | ||
| try { | ||
| let read; | ||
| while ((read = await reader.read()) && !read.done) { | ||
| if (isCancelled()) return; | ||
| const chunk = read.value; | ||
| try { | ||
| writeChunk(JSON.stringify(decoder.decode(chunk, { stream: true })), controller); | ||
| writeChunk(JSON.stringify(decoder.decode(chunk, { stream: true })), controller, nonce); | ||
| } catch (e) { | ||
| writeChunk(`Uint8Array.from(atob(${JSON.stringify(btoa(String.fromCodePoint(...chunk)))}), m => m.codePointAt(0))`, controller); | ||
| writeChunk(`Uint8Array.from(atob(${JSON.stringify(btoa(String.fromCodePoint(...chunk)))}), m => m.codePointAt(0))`, controller, nonce); | ||
| } | ||
@@ -73,7 +88,11 @@ } | ||
| let remaining = decoder.decode(); | ||
| if (remaining.length) writeChunk(JSON.stringify(remaining), controller); | ||
| if (remaining.length && !isCancelled()) writeChunk(JSON.stringify(remaining), controller, nonce); | ||
| } | ||
| function writeChunk(chunk, controller) { | ||
| controller.enqueue(encoder.encode(`<script>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}<\/script>`)); | ||
| function writeChunk(chunk, controller, nonce) { | ||
| let nonceAttr = nonce == null ? "" : ` nonce="${escapeAttribute(nonce)}"`; | ||
| controller.enqueue(encoder.encode(`<script${nonceAttr}>${escapeScript(`(self.__FLIGHT_DATA||=[]).push(${chunk})`)}<\/script>`)); | ||
| } | ||
| function escapeAttribute(value) { | ||
| return value.replace(/&/g, "&").replace(/"/g, """).replace(/</g, "<").replace(/>/g, ">"); | ||
| } | ||
| function escapeScript(script) { | ||
@@ -80,0 +99,0 @@ return script.replace(/<!--/g, "<\\!--").replace(/<\/(script)/gi, "</\\$1"); |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -68,2 +68,3 @@ | ||
| basename: string | undefined; | ||
| clientVersion?: string; | ||
| errors: Record<string, any> | null; | ||
@@ -75,3 +76,2 @@ loaderData: Record<string, any>; | ||
| patches?: Promise<RSCRouteManifest[]>; | ||
| nonce?: string; | ||
| formState?: ReactFormState; | ||
@@ -173,2 +173,4 @@ }; | ||
| * `loadServerAction` function, used to load a server action by ID. | ||
| * @param opts.clientVersion A version derived from the client build output used | ||
| * to detect stale clients during lazy route discovery. | ||
| * @param opts.onError An optional error handler that will be called with any | ||
@@ -197,2 +199,3 @@ * errors that occur during the request processing. | ||
| decodeFormState, | ||
| clientVersion, | ||
| onError, | ||
@@ -211,2 +214,3 @@ request, | ||
| loadServerAction?: LoadServerActionFunction; | ||
| clientVersion?: string; | ||
| onError?: (error: unknown) => void; | ||
@@ -213,0 +217,0 @@ request: Request; |
@@ -29,8 +29,13 @@ | ||
| * createFromReadableStream, | ||
| * async renderHTML(getPayload) { | ||
| * nonce, | ||
| * async renderHTML(getPayload, options) { | ||
| * const payload = getPayload(); | ||
| * | ||
| * return await renderHTMLToReadableStream( | ||
| * <RSCStaticRouter getPayload={getPayload} />, | ||
| * <RSCStaticRouter | ||
| * getPayload={getPayload} | ||
| * nonce={options.nonce} | ||
| * />, | ||
| * { | ||
| * ...options, | ||
| * bootstrapScriptContent, | ||
@@ -53,2 +58,4 @@ * formState: await payload.formState, | ||
| * Defaults to `true`. | ||
| * @param opts.nonce An optional [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce) | ||
| * for inline scripts generated while rendering the HTML document. | ||
| * @param opts.renderHTML A function that renders the {@link unstable_RSCPayload} to | ||
@@ -66,3 +73,4 @@ * HTML, usually using a {@link unstable_RSCStaticRouter | `<RSCStaticRouter>`}. | ||
| renderHTML, | ||
| hydrate | ||
| hydrate, | ||
| nonce | ||
| }: { | ||
@@ -73,2 +81,3 @@ request: Request; | ||
| renderHTML: (getPayload: () => DecodedPayload, options: { | ||
| nonce?: string; | ||
| onError(error: unknown): string | undefined; | ||
@@ -78,2 +87,3 @@ onHeaders(headers: Headers): void; | ||
| hydrate?: boolean; | ||
| nonce?: string; | ||
| }): Promise<Response>; | ||
@@ -92,2 +102,8 @@ /** | ||
| getPayload: () => DecodedPayload; | ||
| /** | ||
| * An optional [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce) | ||
| * used as the default for nonce-aware components such as `<Links>` and | ||
| * `<ScrollRestoration>`. | ||
| */ | ||
| nonce?: string; | ||
| } | ||
@@ -110,8 +126,13 @@ /** | ||
| * createFromReadableStream, | ||
| * async renderHTML(getPayload) { | ||
| * nonce, | ||
| * async renderHTML(getPayload, options) { | ||
| * const payload = getPayload(); | ||
| * | ||
| * return await renderHTMLToReadableStream( | ||
| * <RSCStaticRouter getPayload={getPayload} />, | ||
| * <RSCStaticRouter | ||
| * getPayload={getPayload} | ||
| * nonce={options.nonce} | ||
| * />, | ||
| * { | ||
| * ...options, | ||
| * bootstrapScriptContent, | ||
@@ -130,8 +151,10 @@ * formState: await payload.formState, | ||
| * @param {unstable_RSCStaticRouterProps.getPayload} props.getPayload n/a | ||
| * @param {unstable_RSCStaticRouterProps.nonce} props.nonce n/a | ||
| * @returns A React component that renders the {@link unstable_RSCPayload} as HTML. | ||
| */ | ||
| declare function RSCStaticRouter({ | ||
| getPayload | ||
| getPayload, | ||
| nonce | ||
| }: RSCStaticRouterProps): React$1.JSX.Element | null; | ||
| //#endregion | ||
| export { RSCStaticRouter, RSCStaticRouterProps, SSRCreateFromReadableStreamFunction, routeRSCServerRequest }; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -43,8 +43,13 @@ * Copyright (c) Remix Software Inc. | ||
| * createFromReadableStream, | ||
| * async renderHTML(getPayload) { | ||
| * nonce, | ||
| * async renderHTML(getPayload, options) { | ||
| * const payload = getPayload(); | ||
| * | ||
| * return await renderHTMLToReadableStream( | ||
| * <RSCStaticRouter getPayload={getPayload} />, | ||
| * <RSCStaticRouter | ||
| * getPayload={getPayload} | ||
| * nonce={options.nonce} | ||
| * />, | ||
| * { | ||
| * ...options, | ||
| * bootstrapScriptContent, | ||
@@ -67,2 +72,4 @@ * formState: await payload.formState, | ||
| * Defaults to `true`. | ||
| * @param opts.nonce An optional [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce) | ||
| * for inline scripts generated while rendering the HTML document. | ||
| * @param opts.renderHTML A function that renders the {@link unstable_RSCPayload} to | ||
@@ -75,3 +82,3 @@ * HTML, usually using a {@link unstable_RSCStaticRouter | `<RSCStaticRouter>`}. | ||
| */ | ||
| async function routeRSCServerRequest({ request, serverResponse, createFromReadableStream, renderHTML, hydrate = true }) { | ||
| async function routeRSCServerRequest({ request, serverResponse, createFromReadableStream, renderHTML, hydrate = true, nonce }) { | ||
| const url = new URL(request.url); | ||
@@ -146,2 +153,3 @@ if (isReactServerRequest(url) || isManifestRequest(url) || request.headers.has("rsc-action-id") || serverResponse.headers.get("React-Router-Resource") === "true") return serverResponse; | ||
| let html = await renderHTML(getPayload, { | ||
| nonce, | ||
| onError(error) { | ||
@@ -187,3 +195,3 @@ if (typeof error === "object" && error && "digest" in error && typeof error.digest === "string") { | ||
| if (!serverResponseB?.body) throw new Error("Failed to clone server response"); | ||
| const body = html.pipeThrough(injectRSCPayload(serverResponseB.body)).pipeThrough(redirectTransform); | ||
| const body = html.pipeThrough(injectRSCPayload(serverResponseB.body, { nonce })).pipeThrough(redirectTransform); | ||
| return new Response(body, { | ||
@@ -227,2 +235,3 @@ status, | ||
| }, { | ||
| nonce, | ||
| onError(error) { | ||
@@ -267,3 +276,3 @@ if (typeof error === "object" && error && "digest" in error && typeof error.digest === "string") { | ||
| if (!serverResponseB?.body) throw new Error("Failed to clone server response"); | ||
| const body = html.pipeThrough(injectRSCPayload(serverResponseB.body)).pipeThrough(retryRedirectTransform); | ||
| const body = html.pipeThrough(injectRSCPayload(serverResponseB.body, { nonce })).pipeThrough(retryRedirectTransform); | ||
| return new Response(body, { | ||
@@ -294,8 +303,13 @@ status, | ||
| * createFromReadableStream, | ||
| * async renderHTML(getPayload) { | ||
| * nonce, | ||
| * async renderHTML(getPayload, options) { | ||
| * const payload = getPayload(); | ||
| * | ||
| * return await renderHTMLToReadableStream( | ||
| * <RSCStaticRouter getPayload={getPayload} />, | ||
| * <RSCStaticRouter | ||
| * getPayload={getPayload} | ||
| * nonce={options.nonce} | ||
| * />, | ||
| * { | ||
| * ...options, | ||
| * bootstrapScriptContent, | ||
@@ -314,5 +328,6 @@ * formState: await payload.formState, | ||
| * @param {unstable_RSCStaticRouterProps.getPayload} props.getPayload n/a | ||
| * @param {unstable_RSCStaticRouterProps.nonce} props.nonce n/a | ||
| * @returns A React component that renders the {@link unstable_RSCPayload} as HTML. | ||
| */ | ||
| function RSCStaticRouter({ getPayload }) { | ||
| function RSCStaticRouter({ getPayload, nonce }) { | ||
| const decoded = getPayload(); | ||
@@ -397,3 +412,4 @@ const payload = React$1.use(decoded); | ||
| }, | ||
| routeModules: createRSCRouteModules(payload) | ||
| routeModules: createRSCRouteModules(payload), | ||
| nonce | ||
| }; | ||
@@ -403,4 +419,3 @@ return /* @__PURE__ */ React$1.createElement(RSCRouterContext.Provider, { value: true }, /* @__PURE__ */ React$1.createElement(RSCRouterGlobalErrorBoundary, { location: payload.location }, /* @__PURE__ */ React$1.createElement(FrameworkContext.Provider, { value: frameworkContext }, /* @__PURE__ */ React$1.createElement(StaticRouterProvider, { | ||
| router, | ||
| hydrate: false, | ||
| nonce: payload.nonce | ||
| hydrate: false | ||
| })))); | ||
@@ -407,0 +422,0 @@ } |
@@ -56,9 +56,35 @@ | ||
| * Creates a logical container for managing a browser cookie from the server. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param name The name of the cookie. | ||
| * @param cookieOptions Options for parsing and serializing the cookie. | ||
| * @returns A {@link Cookie} object for parsing and serializing the cookie. | ||
| */ | ||
| declare const createCookie: (name: string, cookieOptions?: CookieOptions) => Cookie; | ||
| /** | ||
| * A function that determines whether a value is a React Router {@link Cookie} | ||
| * object. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Cookie} object; | ||
| * otherwise, `false`. | ||
| */ | ||
| type IsCookieFunction = (object: any) => object is Cookie; | ||
| /** | ||
| * Returns true if an object is a Remix cookie container. | ||
| * Returns `true` if a value is a React Router {@link Cookie} object. | ||
| * | ||
| * @see https://remix.run/utils/cookies#iscookie | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Cookie} object; | ||
| * otherwise, `false`. | ||
| */ | ||
@@ -65,0 +91,0 @@ declare const isCookie: IsCookieFunction; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -17,2 +17,10 @@ * Copyright (c) Remix Software Inc. | ||
| * Creates a logical container for managing a browser cookie from the server. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param name The name of the cookie. | ||
| * @param cookieOptions Options for parsing and serializing the cookie. | ||
| * @returns A {@link Cookie} object for parsing and serializing the cookie. | ||
| */ | ||
@@ -57,5 +65,11 @@ const createCookie = (name, cookieOptions = {}) => { | ||
| /** | ||
| * Returns true if an object is a Remix cookie container. | ||
| * Returns `true` if a value is a React Router {@link Cookie} object. | ||
| * | ||
| * @see https://remix.run/utils/cookies#iscookie | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Cookie} object; | ||
| * otherwise, `false`. | ||
| */ | ||
@@ -62,0 +76,0 @@ const isCookie = (object) => { |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -8,4 +8,16 @@ | ||
| type CreateRequestHandlerFunction = (build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>), mode?: string) => RequestHandler; | ||
| /** | ||
| * Creates a request handler for a React Router server build. | ||
| * | ||
| * This is a low-level API used by server adapters to translate incoming | ||
| * requests into React Router responses. | ||
| * | ||
| * @category Utils | ||
| * @param build The server build, or a function that resolves to the server | ||
| * build, used to handle requests. | ||
| * @param mode The mode in which the server build is running. | ||
| * @returns A request handler that returns a response for each incoming request. | ||
| */ | ||
| declare const createRequestHandler: CreateRequestHandlerFunction; | ||
| //#endregion | ||
| export { CreateRequestHandlerFunction, RequestHandler, createRequestHandler }; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -140,2 +140,14 @@ * Copyright (c) Remix Software Inc. | ||
| } | ||
| /** | ||
| * Creates a request handler for a React Router server build. | ||
| * | ||
| * This is a low-level API used by server adapters to translate incoming | ||
| * requests into React Router responses. | ||
| * | ||
| * @category Utils | ||
| * @param build The server build, or a function that resolves to the server | ||
| * build, used to handle requests. | ||
| * @param mode The mode in which the server build is running. | ||
| * @returns A request handler that returns a response for each incoming request. | ||
| */ | ||
| const createRequestHandler = (build, mode) => { | ||
@@ -142,0 +154,0 @@ let _build; |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -63,9 +63,33 @@ | ||
| * Instead, use a `SessionStorage` object's `getSession` method. | ||
| * | ||
| * @category Utils | ||
| * @param initialData The initial data for the session. | ||
| * @param id The identifier for the session. Defaults to an empty string for a | ||
| * new session. | ||
| * @returns A new {@link Session} object. | ||
| */ | ||
| declare const createSession: CreateSessionFunction; | ||
| /** | ||
| * A function that determines whether a value is a React Router {@link Session} | ||
| * object. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Session} object; | ||
| * otherwise, `false`. | ||
| */ | ||
| type IsSessionFunction = (object: any) => object is Session; | ||
| /** | ||
| * Returns true if an object is a React Router session. | ||
| * Returns `true` if a value is a React Router {@link Session} object. | ||
| * | ||
| * @see https://reactrouter.com/api/utils/isSession | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Session} object; | ||
| * otherwise, `false`. | ||
| */ | ||
@@ -137,2 +161,7 @@ declare const isSession: IsSessionFunction; | ||
| * existing session storage options meet your requirements. | ||
| * | ||
| * @category Utils | ||
| * @param strategy The strategy used to store session identifiers and data. | ||
| * @returns A {@link SessionStorage} object that persists session data using the | ||
| * provided strategy. | ||
| */ | ||
@@ -139,0 +168,0 @@ declare function createSessionStorage<Data = SessionData, FlashData = Data>({ |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -22,2 +22,8 @@ * Copyright (c) Remix Software Inc. | ||
| * Instead, use a `SessionStorage` object's `getSession` method. | ||
| * | ||
| * @category Utils | ||
| * @param initialData The initial data for the session. | ||
| * @param id The identifier for the session. Defaults to an empty string for a | ||
| * new session. | ||
| * @returns A new {@link Session} object. | ||
| */ | ||
@@ -57,5 +63,11 @@ const createSession = (initialData = {}, id = "") => { | ||
| /** | ||
| * Returns true if an object is a React Router session. | ||
| * Returns `true` if a value is a React Router {@link Session} object. | ||
| * | ||
| * @see https://reactrouter.com/api/utils/isSession | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param object The value to check. | ||
| * @returns `true` if the value is a React Router {@link Session} object; | ||
| * otherwise, `false`. | ||
| */ | ||
@@ -70,2 +82,7 @@ const isSession = (object) => { | ||
| * existing session storage options meet your requirements. | ||
| * | ||
| * @category Utils | ||
| * @param strategy The strategy used to store session identifiers and data. | ||
| * @returns A {@link SessionStorage} object that persists session data using the | ||
| * provided strategy. | ||
| */ | ||
@@ -72,0 +89,0 @@ function createSessionStorage({ cookie: cookieArg, createData, readData, updateData, deleteData }) { |
@@ -20,2 +20,10 @@ | ||
| * browser's maximum cookie size. Trade-offs! | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param options Options for creating the cookie-backed session storage. | ||
| * @returns A {@link SessionStorage} object that stores all session data in its | ||
| * cookie. | ||
| */ | ||
@@ -22,0 +30,0 @@ declare function createCookieSessionStorage<Data = SessionData, FlashData = Data>({ |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -22,2 +22,10 @@ * Copyright (c) Remix Software Inc. | ||
| * browser's maximum cookie size. Trade-offs! | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param options Options for creating the cookie-backed session storage. | ||
| * @returns A {@link SessionStorage} object that stores all session data in its | ||
| * cookie. | ||
| */ | ||
@@ -24,0 +32,0 @@ function createCookieSessionStorage({ cookie: cookieArg } = {}) { |
@@ -13,7 +13,13 @@ | ||
| /** | ||
| * Creates and returns a simple in-memory SessionStorage object, mostly useful | ||
| * for testing and as a reference implementation. | ||
| * Creates and returns a simple in-memory SessionStorage object. | ||
| * | ||
| * Note: This storage does not scale beyond a single process, so it is not | ||
| * suitable for most production scenarios. | ||
| * Intended for local development and testing. It does not scale beyond a single | ||
| * process, and all session data is lost when the server process stops/restarts. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param options Options for creating the in-memory session storage. | ||
| * @returns A {@link SessionStorage} object that stores session data in memory. | ||
| */ | ||
@@ -20,0 +26,0 @@ declare function createMemorySessionStorage<Data = SessionData, FlashData = Data>({ |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -14,7 +14,13 @@ * Copyright (c) Remix Software Inc. | ||
| /** | ||
| * Creates and returns a simple in-memory SessionStorage object, mostly useful | ||
| * for testing and as a reference implementation. | ||
| * Creates and returns a simple in-memory SessionStorage object. | ||
| * | ||
| * Note: This storage does not scale beyond a single process, so it is not | ||
| * suitable for most production scenarios. | ||
| * Intended for local development and testing. It does not scale beyond a single | ||
| * process, and all session data is lost when the server process stops/restarts. | ||
| * | ||
| * @public | ||
| * @category Utils | ||
| * @mode framework | ||
| * @mode data | ||
| * @param options Options for creating the in-memory session storage. | ||
| * @returns A {@link SessionStorage} object that stores session data in memory. | ||
| */ | ||
@@ -26,3 +32,3 @@ function createMemorySessionStorage({ cookie } = {}) { | ||
| async createData(data, expires) { | ||
| let id = Math.random().toString(36).substring(2, 10); | ||
| let id = crypto.randomUUID(); | ||
| map.set(id, { | ||
@@ -29,0 +35,0 @@ data, |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
| /** | ||
| * react-router v8.2.0 | ||
| * react-router v8.3.0 | ||
| * | ||
@@ -4,0 +4,0 @@ * Copyright (c) Remix Software Inc. |
@@ -281,3 +281,3 @@ --- | ||
| - [`isSession`][is-session] | ||
| - [`createMemorySessionStorage`][create-memory-session-storage] | ||
| - [`createMemorySessionStorage`][create-memory-session-storage] (local dev and testing) | ||
| - [`createSession`][create-session] (custom storage) | ||
@@ -453,15 +453,15 @@ - [`createFileSessionStorage`][create-file-session-storage] (node) | ||
| [sessions]: #sessions | ||
| [session-storage]: https://api.reactrouter.com/v7/interfaces/react-router.SessionStorage | ||
| [session-api]: https://api.reactrouter.com/v7/interfaces/react-router.Session | ||
| [is-session]: https://api.reactrouter.com/v7/functions/react-router.isSession | ||
| [cookie-api]: https://api.reactrouter.com/v7/interfaces/react-router.Cookie | ||
| [create-session-storage]: https://api.reactrouter.com/v7/functions/react-router.createSessionStorage | ||
| [create-session]: https://api.reactrouter.com/v7/functions/react-router.createSession | ||
| [create-memory-session-storage]: https://api.reactrouter.com/v7/functions/react-router.createMemorySessionStorage | ||
| [create-file-session-storage]: https://api.reactrouter.com/v7/functions/_react-router_node.createFileSessionStorage | ||
| [create-workers-kv-session-storage]: https://api.reactrouter.com/v7/functions/_react-router_cloudflare.createWorkersKVSessionStorage | ||
| [create-arc-table-session-storage]: https://api.reactrouter.com/v7/functions/_react-router_architect.createArcTableSessionStorage | ||
| [session-storage]: https://api.reactrouter.com/v8/interfaces/react-router.SessionStorage | ||
| [session-api]: https://api.reactrouter.com/v8/interfaces/react-router.Session | ||
| [is-session]: https://api.reactrouter.com/v8/variables/react-router.isSession.html | ||
| [cookie-api]: https://api.reactrouter.com/v8/interfaces/react-router.Cookie | ||
| [create-session-storage]: https://api.reactrouter.com/v8/functions/react-router.createSessionStorage | ||
| [create-session]: https://api.reactrouter.com/v8/variables/react-router.createSession.html | ||
| [create-memory-session-storage]: https://api.reactrouter.com/v8/functions/react-router.createMemorySessionStorage | ||
| [create-file-session-storage]: https://api.reactrouter.com/v8/functions/_react-router_node.createFileSessionStorage | ||
| [create-workers-kv-session-storage]: https://api.reactrouter.com/v8/functions/_react-router_cloudflare.createWorkersKVSessionStorage | ||
| [create-arc-table-session-storage]: https://api.reactrouter.com/v8/functions/_react-router_architect.createArcTableSessionStorage | ||
| [cookie]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Cookies | ||
| [cookie-attrs]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Set-Cookie#attributes | ||
| [is-cookie]: https://api.reactrouter.com/v7/functions/react-router.isCookie | ||
| [create-cookie]: https://api.reactrouter.com/v7/functions/react-router.createCookie | ||
| [is-cookie]: https://api.reactrouter.com/v8/variables/react-router.isCookie.html | ||
| [create-cookie]: https://api.reactrouter.com/v8/functions/react-router.createCookie |
@@ -513,4 +513,4 @@ --- | ||
| [apollo]: https://www.apollographql.com/ | ||
| [use_navigation]: https://api.reactrouter.com/v7/functions/react-router.useNavigation | ||
| [use_fetcher]: https://api.reactrouter.com/v7/functions/react-router.useFetcher | ||
| [use_navigation]: https://api.reactrouter.com/v8/functions/react-router.useNavigation | ||
| [use_fetcher]: https://api.reactrouter.com/v8/functions/react-router.useFetcher | ||
| [loader_data]: ../start/framework/data-loading | ||
@@ -517,0 +517,0 @@ [action_data]: ../start/framework/actions |
@@ -101,4 +101,4 @@ --- | ||
| [react-router-config]: https://api.reactrouter.com/v7/types/_react-router_dev.config.Config.html | ||
| [preset-type]: https://api.reactrouter.com/v7/types/_react-router_dev.config.Preset.html | ||
| [react-router-config]: https://api.reactrouter.com/v8/types/_react-router_dev.config.Config.html | ||
| [preset-type]: https://api.reactrouter.com/v8/types/_react-router_dev.config.Preset.html | ||
| [server-bundles]: ./server-bundles |
@@ -388,3 +388,2 @@ --- | ||
| - `splitRouteModules` | ||
| - `subResourceIntegrity` | ||
@@ -872,3 +871,56 @@ ## RSC Data Mode | ||
| ## Content Security Policy nonces | ||
| A [Content Security Policy][csp] can use a per-response nonce to allow the inline scripts required for RSC hydration without allowing arbitrary inline scripts. The nonce is an HTML concern, so configure it in `entry.ssr.tsx`; it does not need to be passed to `matchRSCServerRequest` or included in the RSC payload. | ||
| In RSC Framework Mode, first run `react-router reveal entry.ssr` to create a custom SSR entry. In RSC Data Mode, update your existing SSR entry. Generate a fresh nonce for each document response, then pass it to `routeRSCServerRequest`, the `RSCStaticRouter`, and your CSP response header: | ||
| ```tsx filename=app/entry.ssr.tsx | ||
| export async function generateHTML( | ||
| request: Request, | ||
| serverResponse: Response, | ||
| ): Promise<Response> { | ||
| const nonce = crypto.randomUUID(); | ||
| const response = await routeRSCServerRequest({ | ||
| request, | ||
| serverResponse, | ||
| createFromReadableStream, | ||
| nonce, | ||
| async renderHTML(getPayload, options) { | ||
| const payload = getPayload(); | ||
| const bootstrapScriptContent = | ||
| await import.meta.viteRsc.loadBootstrapScriptContent( | ||
| "index", | ||
| ); | ||
| return renderHTMLToReadableStream( | ||
| <RSCStaticRouter | ||
| getPayload={getPayload} | ||
| nonce={options.nonce} | ||
| />, | ||
| { | ||
| ...options, | ||
| bootstrapScriptContent, | ||
| formState: await payload.formState, | ||
| signal: request.signal, | ||
| }, | ||
| ); | ||
| }, | ||
| }); | ||
| response.headers.set( | ||
| "Content-Security-Policy", | ||
| `script-src 'self' 'nonce-${nonce}'`, | ||
| ); | ||
| return response; | ||
| } | ||
| ``` | ||
| The `nonce` option on `routeRSCServerRequest` applies the nonce to the inline scripts that transfer the RSC payload into the HTML document. Spreading its `renderHTML` options into `renderHTMLToReadableStream` applies the same nonce to scripts generated by React. Passing it to `RSCStaticRouter` makes it the default for nonce-aware components such as `<Links>` and `<ScrollRestoration>`. | ||
| The default RSC Framework entry does not generate a nonce. Only generate one when your application also sends a matching CSP header. For statically prerendered pages, prefer CSP hashes or external scripts instead of a per-response nonce. | ||
| [picking-a-mode]: ../start/modes | ||
| [csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP | ||
| [react-server-components-doc]: https://react.dev/reference/rsc/server-components | ||
@@ -875,0 +927,0 @@ [react-server-functions-doc]: https://react.dev/reference/rsc/server-functions |
@@ -7,3 +7,3 @@ --- | ||
| [MODES: framework] | ||
| [MODES: framework, data] | ||
@@ -17,2 +17,4 @@ <br/> | ||
| ### Framework Mode without RSC | ||
| If you are implementing a [Content-Security-Policy (CSP)][csp] in your application, specifically one using the `unsafe-inline` directive, you will need to specify a [`nonce`][nonce] attribute on the inline `<script>` elements rendered in your HTML. | ||
@@ -27,2 +29,6 @@ | ||
| ### RSC Framework and RSC Data Mode | ||
| For RSC Framework and RSC Data Mode, generate the nonce in `entry.ssr.tsx` and pass it to `routeRSCServerRequest`, `RSCStaticRouter`, and the CSP response header. See the [RSC Content Security Policy nonce guide][rsc-csp]. The nonce is only needed while generating the HTML document; it should not be included in the RSC payload or passed to `matchRSCServerRequest`. | ||
| [csp]: https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP | ||
@@ -36,1 +42,2 @@ [entryserver]: ../api/framework-conventions/entry.server.tsx | ||
| [serverrouter]: ../api/framework-routers/ServerRouter | ||
| [rsc-csp]: ./react-server-components#content-security-policy-nonces |
@@ -65,3 +65,3 @@ --- | ||
| [react-router-config]: https://api.reactrouter.com/v7/types/_react-router_dev.config.Config.html | ||
| [server-bundles-function]: https://api.reactrouter.com/v7/types/_react-router_dev.config.ServerBundlesFunction.html | ||
| [react-router-config]: https://api.reactrouter.com/v8/types/_react-router_dev.config.Config.html | ||
| [server-bundles-function]: https://api.reactrouter.com/v8/types/_react-router_dev.config.ServerBundlesFunction.html |
@@ -219,3 +219,3 @@ --- | ||
| [`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v7/interfaces/react-router.ShouldRevalidateFunctionArgs.html) | ||
| [`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v8/interfaces/react-router.ShouldRevalidateFunctionArgs.html) | ||
@@ -267,4 +267,4 @@ Please note the default behavior is different in [Framework Mode](../modes). | ||
| [loader-params]: https://api.reactrouter.com/v7/interfaces/react-router.LoaderFunctionArgs | ||
| [loader-params]: https://api.reactrouter.com/v8/interfaces/react-router.LoaderFunctionArgs | ||
| [middleware]: ../../how-to/middleware | ||
| [use-matches]: ../../api/hooks/useMatches |
@@ -281,2 +281,2 @@ --- | ||
| [outlet]: https://api.reactrouter.com/v7/functions/react-router.Outlet.html | ||
| [outlet]: https://api.reactrouter.com/v8/functions/react-router.Outlet.html |
@@ -142,2 +142,2 @@ --- | ||
| [use_fetcher]: https://api.reactrouter.com/v7/functions/react-router.useFetcher.html | ||
| [use_fetcher]: https://api.reactrouter.com/v8/functions/react-router.useFetcher.html |
@@ -503,3 +503,3 @@ --- | ||
| [`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v7/interfaces/react-router.ShouldRevalidateFunctionArgs.html) | ||
| [`ShouldRevalidateFunctionArgs` Reference Documentation ↗](https://api.reactrouter.com/v8/interfaces/react-router.ShouldRevalidateFunctionArgs.html) | ||
@@ -510,9 +510,9 @@ --- | ||
| [middleware-params]: https://api.reactrouter.com/v7/types/react-router.MiddlewareFunction.html | ||
| [middleware-params]: https://api.reactrouter.com/v8/types/react-router.MiddlewareFunction.html | ||
| [middleware]: ../../how-to/middleware | ||
| [when-middleware-runs]: ../../how-to/middleware#when-middleware-runs | ||
| [loader-params]: https://api.reactrouter.com/v7/interfaces/react-router.LoaderFunctionArgs | ||
| [client-loader-params]: https://api.reactrouter.com/v7/types/react-router.ClientLoaderFunctionArgs | ||
| [action-params]: https://api.reactrouter.com/v7/interfaces/react-router.ActionFunctionArgs | ||
| [client-action-params]: https://api.reactrouter.com/v7/types/react-router.ClientActionFunctionArgs | ||
| [loader-params]: https://api.reactrouter.com/v8/interfaces/react-router.LoaderFunctionArgs | ||
| [client-loader-params]: https://api.reactrouter.com/v8/types/react-router.ClientLoaderFunctionArgs | ||
| [action-params]: https://api.reactrouter.com/v8/interfaces/react-router.ActionFunctionArgs | ||
| [client-action-params]: https://api.reactrouter.com/v8/types/react-router.ClientActionFunctionArgs | ||
| [use-route-error]: ../../api/hooks/useRouteError | ||
@@ -524,4 +524,4 @@ [is-route-error-response]: ../../api/utils/isRouteErrorResponse | ||
| [meta-element]: https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta | ||
| [meta-params]: https://api.reactrouter.com/v7/interfaces/react-router.MetaArgs | ||
| [meta-function]: https://api.reactrouter.com/v7/types/react-router.MetaDescriptor.html | ||
| [meta-params]: https://api.reactrouter.com/v8/interfaces/react-router.MetaArgs | ||
| [meta-function]: https://api.reactrouter.com/v8/types/react-router.MetaDescriptor.html | ||
| [data-mode-should-revalidate]: ../data/route-object#shouldrevalidate | ||
@@ -528,0 +528,0 @@ [spa-mode]: ../../how-to/spa |
@@ -362,2 +362,2 @@ --- | ||
| [file-route-conventions]: ../../how-to/file-route-conventions | ||
| [outlet]: https://api.reactrouter.com/v7/functions/react-router.Outlet.html | ||
| [outlet]: https://api.reactrouter.com/v8/functions/react-router.Outlet.html |
+1
-1
| { | ||
| "name": "react-router", | ||
| "type": "module", | ||
| "version": "8.2.0", | ||
| "version": "8.3.0", | ||
| "description": "Declarative routing for React", | ||
@@ -6,0 +6,0 @@ "keywords": [ |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is too big to display
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
URL strings
Supply chain riskPackage contains fragments of external URLs or IP addresses, which the package may be accessing at runtime.
2788503
1.83%58446
2.05%