react-router 8.2.0 → 8.3.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +78 -0
- package/dist/development/dom-export.js +1 -1
- package/dist/development/index-react-server-client.js +1 -1
- package/dist/development/index-react-server.d.ts +114 -17
- package/dist/development/index-react-server.js +189 -54
- package/dist/development/index.js +1 -1
- package/dist/development/lib/actions.js +10 -5
- package/dist/development/lib/components.js +6 -3
- package/dist/development/lib/context.d.ts +1 -0
- package/dist/development/lib/context.js +1 -1
- package/dist/development/lib/dom/dom.d.ts +24 -24
- package/dist/development/lib/dom/dom.js +26 -26
- package/dist/development/lib/dom/lib.d.ts +2 -1
- package/dist/development/lib/dom/lib.js +23 -7
- package/dist/development/lib/dom/server.js +1 -1
- package/dist/development/lib/dom/ssr/components.js +2 -2
- package/dist/development/lib/dom/ssr/data.js +1 -1
- package/dist/development/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/development/lib/dom/ssr/fallback.js +1 -1
- package/dist/development/lib/dom/ssr/fog-of-war.js +27 -21
- package/dist/development/lib/dom/ssr/hydration.js +1 -1
- package/dist/development/lib/dom/ssr/invariant.js +1 -1
- package/dist/development/lib/dom/ssr/links.js +1 -1
- package/dist/development/lib/dom/ssr/markup.js +1 -1
- package/dist/development/lib/dom/ssr/routeModules.js +1 -1
- package/dist/development/lib/dom/ssr/routes-test-stub.d.ts +13 -0
- package/dist/development/lib/dom/ssr/routes-test-stub.js +14 -1
- package/dist/development/lib/dom/ssr/routes.js +1 -1
- package/dist/development/lib/dom/ssr/server.js +1 -1
- package/dist/development/lib/dom/ssr/single-fetch.js +5 -5
- package/dist/development/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/development/lib/dom-export/hydrated-router.js +1 -1
- package/dist/development/lib/errors.js +1 -1
- package/dist/development/lib/hooks.js +6 -3
- package/dist/development/lib/href.d.ts +24 -8
- package/dist/development/lib/href.js +28 -11
- package/dist/development/lib/router/history.d.ts +6 -0
- package/dist/development/lib/router/history.js +8 -2
- package/dist/development/lib/router/instrumentation.js +1 -1
- package/dist/development/lib/router/navigation.js +45 -0
- package/dist/development/lib/router/router.d.ts +8 -0
- package/dist/development/lib/router/router.js +31 -17
- package/dist/development/lib/router/url.js +1 -1
- package/dist/development/lib/router/utils.d.ts +12 -0
- package/dist/development/lib/router/utils.js +66 -8
- package/dist/development/lib/rsc/browser.js +37 -24
- package/dist/development/lib/rsc/errorBoundaries.js +1 -1
- package/dist/development/lib/rsc/html-stream/browser.js +1 -1
- package/dist/development/lib/rsc/html-stream/server.js +30 -11
- package/dist/development/lib/rsc/route-modules.js +1 -1
- package/dist/development/lib/rsc/server.rsc.d.ts +5 -1
- package/dist/development/lib/rsc/server.ssr.d.ts +29 -6
- package/dist/development/lib/rsc/server.ssr.js +28 -13
- package/dist/development/lib/server-runtime/cookies.d.ts +28 -2
- package/dist/development/lib/server-runtime/cookies.js +18 -4
- package/dist/development/lib/server-runtime/crypto.js +2 -2
- package/dist/development/lib/server-runtime/data.js +1 -1
- package/dist/development/lib/server-runtime/dev.js +2 -2
- package/dist/development/lib/server-runtime/entry.js +1 -1
- package/dist/development/lib/server-runtime/errors.js +1 -1
- package/dist/development/lib/server-runtime/headers.js +1 -1
- package/dist/development/lib/server-runtime/invariant.js +1 -1
- package/dist/development/lib/server-runtime/mode.js +1 -1
- package/dist/development/lib/server-runtime/routeMatching.js +1 -1
- package/dist/development/lib/server-runtime/routes.js +1 -1
- package/dist/development/lib/server-runtime/server.d.ts +12 -0
- package/dist/development/lib/server-runtime/server.js +14 -2
- package/dist/development/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/development/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
- package/dist/development/lib/server-runtime/sessions/cookieStorage.js +9 -1
- package/dist/development/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
- package/dist/development/lib/server-runtime/sessions/memoryStorage.js +12 -6
- package/dist/development/lib/server-runtime/sessions.d.ts +31 -2
- package/dist/development/lib/server-runtime/sessions.js +20 -3
- package/dist/development/lib/server-runtime/single-fetch.js +2 -2
- package/dist/development/lib/server-runtime/urls.js +1 -1
- package/dist/development/lib/server-runtime/warnings.js +1 -1
- package/dist/development/lib/types/internal.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/turbo-stream.js +4 -4
- package/dist/development/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/development/vendor/turbo-stream-v2/utils.js +1 -1
- package/dist/production/dom-export.js +1 -1
- package/dist/production/index-react-server-client.js +1 -1
- package/dist/production/index-react-server.d.ts +114 -17
- package/dist/production/index-react-server.js +189 -54
- package/dist/production/index.js +1 -1
- package/dist/production/lib/actions.js +10 -5
- package/dist/production/lib/components.js +6 -3
- package/dist/production/lib/context.d.ts +1 -0
- package/dist/production/lib/context.js +1 -1
- package/dist/production/lib/dom/dom.d.ts +24 -24
- package/dist/production/lib/dom/dom.js +26 -26
- package/dist/production/lib/dom/lib.d.ts +2 -1
- package/dist/production/lib/dom/lib.js +23 -7
- package/dist/production/lib/dom/server.js +1 -1
- package/dist/production/lib/dom/ssr/components.js +2 -2
- package/dist/production/lib/dom/ssr/data.js +1 -1
- package/dist/production/lib/dom/ssr/errorBoundaries.js +1 -1
- package/dist/production/lib/dom/ssr/fallback.js +1 -1
- package/dist/production/lib/dom/ssr/fog-of-war.js +27 -21
- package/dist/production/lib/dom/ssr/hydration.js +1 -1
- package/dist/production/lib/dom/ssr/invariant.js +1 -1
- package/dist/production/lib/dom/ssr/links.js +1 -1
- package/dist/production/lib/dom/ssr/markup.js +1 -1
- package/dist/production/lib/dom/ssr/routeModules.js +1 -1
- package/dist/production/lib/dom/ssr/routes-test-stub.d.ts +13 -0
- package/dist/production/lib/dom/ssr/routes-test-stub.js +14 -1
- package/dist/production/lib/dom/ssr/routes.js +1 -1
- package/dist/production/lib/dom/ssr/server.js +1 -1
- package/dist/production/lib/dom/ssr/single-fetch.js +5 -5
- package/dist/production/lib/dom-export/dom-router-provider.js +1 -1
- package/dist/production/lib/dom-export/hydrated-router.js +1 -1
- package/dist/production/lib/errors.js +1 -1
- package/dist/production/lib/hooks.js +6 -3
- package/dist/production/lib/href.d.ts +24 -8
- package/dist/production/lib/href.js +28 -11
- package/dist/production/lib/router/history.d.ts +6 -0
- package/dist/production/lib/router/history.js +8 -2
- package/dist/production/lib/router/instrumentation.js +1 -1
- package/dist/production/lib/router/navigation.js +45 -0
- package/dist/production/lib/router/router.d.ts +8 -0
- package/dist/production/lib/router/router.js +31 -17
- package/dist/production/lib/router/url.js +1 -1
- package/dist/production/lib/router/utils.d.ts +12 -0
- package/dist/production/lib/router/utils.js +66 -8
- package/dist/production/lib/rsc/browser.js +37 -24
- package/dist/production/lib/rsc/errorBoundaries.js +1 -1
- package/dist/production/lib/rsc/html-stream/browser.js +1 -1
- package/dist/production/lib/rsc/html-stream/server.js +30 -11
- package/dist/production/lib/rsc/route-modules.js +1 -1
- package/dist/production/lib/rsc/server.rsc.d.ts +5 -1
- package/dist/production/lib/rsc/server.ssr.d.ts +29 -6
- package/dist/production/lib/rsc/server.ssr.js +28 -13
- package/dist/production/lib/server-runtime/cookies.d.ts +28 -2
- package/dist/production/lib/server-runtime/cookies.js +18 -4
- package/dist/production/lib/server-runtime/crypto.js +2 -2
- package/dist/production/lib/server-runtime/data.js +1 -1
- package/dist/production/lib/server-runtime/dev.js +2 -2
- package/dist/production/lib/server-runtime/entry.js +1 -1
- package/dist/production/lib/server-runtime/errors.js +1 -1
- package/dist/production/lib/server-runtime/headers.js +1 -1
- package/dist/production/lib/server-runtime/invariant.js +1 -1
- package/dist/production/lib/server-runtime/mode.js +1 -1
- package/dist/production/lib/server-runtime/routeMatching.js +1 -1
- package/dist/production/lib/server-runtime/routes.js +1 -1
- package/dist/production/lib/server-runtime/server.d.ts +12 -0
- package/dist/production/lib/server-runtime/server.js +14 -2
- package/dist/production/lib/server-runtime/serverHandoff.js +1 -1
- package/dist/production/lib/server-runtime/sessions/cookieStorage.d.ts +8 -0
- package/dist/production/lib/server-runtime/sessions/cookieStorage.js +9 -1
- package/dist/production/lib/server-runtime/sessions/memoryStorage.d.ts +10 -4
- package/dist/production/lib/server-runtime/sessions/memoryStorage.js +12 -6
- package/dist/production/lib/server-runtime/sessions.d.ts +31 -2
- package/dist/production/lib/server-runtime/sessions.js +20 -3
- package/dist/production/lib/server-runtime/single-fetch.js +2 -2
- package/dist/production/lib/server-runtime/urls.js +1 -1
- package/dist/production/lib/server-runtime/warnings.js +1 -1
- package/dist/production/lib/types/internal.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/flatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/turbo-stream.js +4 -4
- package/dist/production/vendor/turbo-stream-v2/unflatten.js +1 -1
- package/dist/production/vendor/turbo-stream-v2/utils.js +1 -1
- package/docs/explanation/sessions-and-cookies.md +13 -13
- package/docs/explanation/state-management.md +2 -2
- package/docs/how-to/presets.md +2 -2
- package/docs/how-to/react-server-components.md +53 -1
- package/docs/how-to/security.md +8 -1
- package/docs/how-to/server-bundles.md +2 -2
- package/docs/start/data/route-object.md +2 -2
- package/docs/start/data/routing.md +1 -1
- package/docs/start/framework/pending-ui.md +1 -1
- package/docs/start/framework/route-module.md +8 -8
- package/docs/start/framework/routing.md +1 -1
- package/package.json +1 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.3.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -24,7 +24,7 @@ function warning(cond, message) {
|
|
|
24
24
|
if (typeof console !== "undefined") console.warn(message);
|
|
25
25
|
try {
|
|
26
26
|
throw new Error(message);
|
|
27
|
-
} catch
|
|
27
|
+
} catch {}
|
|
28
28
|
}
|
|
29
29
|
}
|
|
30
30
|
function createKey$1() {
|
|
@@ -47,7 +47,10 @@ function createLocation(current, to, state = null, key, mask) {
|
|
|
47
47
|
/**
|
|
48
48
|
* Creates a string URL path from the given pathname, search, and hash components.
|
|
49
49
|
*
|
|
50
|
+
* @public
|
|
50
51
|
* @category Utils
|
|
52
|
+
* @param path The pathname, search, and hash components to combine.
|
|
53
|
+
* @returns The combined URL path.
|
|
51
54
|
*/
|
|
52
55
|
function createPath({ pathname = "/", search = "", hash = "" }) {
|
|
53
56
|
if (search && search !== "?") pathname += search.charAt(0) === "?" ? search : "?" + search;
|
|
@@ -57,7 +60,10 @@ function createPath({ pathname = "/", search = "", hash = "" }) {
|
|
|
57
60
|
/**
|
|
58
61
|
* Parses a string URL path into its separate pathname, search, and hash components.
|
|
59
62
|
*
|
|
63
|
+
* @public
|
|
60
64
|
* @category Utils
|
|
65
|
+
* @param path The URL path to parse.
|
|
66
|
+
* @returns The parsed pathname, search, and hash components.
|
|
61
67
|
*/
|
|
62
68
|
function parsePath(path) {
|
|
63
69
|
let parsedPath = {};
|
|
@@ -441,6 +447,48 @@ function matchRouteBranch(branch, pathname, allowPartial = false) {
|
|
|
441
447
|
return matches;
|
|
442
448
|
}
|
|
443
449
|
/**
|
|
450
|
+
* Characters that `encodeURIComponent` escapes but that are valid literally in
|
|
451
|
+
* a URL path segment. Per RFC 3986 §3.3, a path segment is made of `pchar`:
|
|
452
|
+
*
|
|
453
|
+
* ```
|
|
454
|
+
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
|
455
|
+
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
|
|
456
|
+
* ```
|
|
457
|
+
*
|
|
458
|
+
* `encodeURIComponent` targets query-string values, where `$ & + , ; = : @`
|
|
459
|
+
* are delimiters and must be escaped — but in a path segment they carry no
|
|
460
|
+
* special meaning, and browsers keep them literal in `location.pathname`.
|
|
461
|
+
* (`! ' ( ) *` and the unreserved set are already left alone by
|
|
462
|
+
* `encodeURIComponent`, so they need no restoring.)
|
|
463
|
+
*/
|
|
464
|
+
const PATH_PARAM_OVERESCAPED = {
|
|
465
|
+
"%24": "$",
|
|
466
|
+
"%26": "&",
|
|
467
|
+
"%2B": "+",
|
|
468
|
+
"%2C": ",",
|
|
469
|
+
"%3A": ":",
|
|
470
|
+
"%3B": ";",
|
|
471
|
+
"%3D": "=",
|
|
472
|
+
"%40": "@"
|
|
473
|
+
};
|
|
474
|
+
/**
|
|
475
|
+
* Encodes a param value for interpolation into a single URL path segment.
|
|
476
|
+
*
|
|
477
|
+
* Escapes characters that would break the path (`/ ? # %`, whitespace,
|
|
478
|
+
* non-ASCII, …) while leaving characters that RFC 3986 permits literally in a
|
|
479
|
+
* path segment untouched. Escaping those would needlessly rewrite URLs — e.g.
|
|
480
|
+
* a semver build param `1.0.0+1` would become `1.0.0%2B1` even though browsers
|
|
481
|
+
* display and match the `+` literally in `location.pathname`.
|
|
482
|
+
*
|
|
483
|
+
* See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3))
|
|
484
|
+
*
|
|
485
|
+
* @param value The param value to encode.
|
|
486
|
+
* @returns The encoded value, safe for use as a single path segment.
|
|
487
|
+
*/
|
|
488
|
+
function encodePathParam(value) {
|
|
489
|
+
return encodeURIComponent(value).replace(/%(?:24|26|2B|2C|3A|3B|3D|40)/g, (match) => PATH_PARAM_OVERESCAPED[match]);
|
|
490
|
+
}
|
|
491
|
+
/**
|
|
444
492
|
* Performs pattern matching on a URL pathname and returns information about
|
|
445
493
|
* the match.
|
|
446
494
|
*
|
|
@@ -467,13 +515,13 @@ function matchPathImpl(pattern, pathname, matcher, compiledParams) {
|
|
|
467
515
|
let match = pathname.match(matcher);
|
|
468
516
|
if (!match) return null;
|
|
469
517
|
let matchedPathname = match[0];
|
|
470
|
-
let pathnameBase = matchedPathname
|
|
518
|
+
let pathnameBase = removeTrailingSlash(matchedPathname, 1);
|
|
471
519
|
let captureGroups = match.slice(1);
|
|
472
520
|
return {
|
|
473
521
|
params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
|
|
474
522
|
if (paramName === "*") {
|
|
475
523
|
let splatValue = captureGroups[index] || "";
|
|
476
|
-
pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length)
|
|
524
|
+
pathnameBase = removeTrailingSlash(matchedPathname.slice(0, matchedPathname.length - splatValue.length), 1);
|
|
477
525
|
}
|
|
478
526
|
const value = captureGroups[index];
|
|
479
527
|
if (isOptional && !value) memo[paramName] = void 0;
|
|
@@ -542,7 +590,7 @@ function resolvePath(to, fromPathname = "/") {
|
|
|
542
590
|
let pathname;
|
|
543
591
|
if (toPathname) {
|
|
544
592
|
toPathname = removeDoubleSlashes(toPathname);
|
|
545
|
-
if (toPathname.startsWith("/")) pathname = resolvePathname(toPathname.substring(1), "/");
|
|
593
|
+
if (toPathname.startsWith("/") || toPathname.startsWith("\\")) pathname = resolvePathname(toPathname.substring(1), "/");
|
|
546
594
|
else pathname = resolvePathname(toPathname, fromPathname);
|
|
547
595
|
} else pathname = fromPathname;
|
|
548
596
|
return {
|
|
@@ -603,7 +651,11 @@ function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = fal
|
|
|
603
651
|
}
|
|
604
652
|
const removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
|
|
605
653
|
const joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
|
|
606
|
-
|
|
654
|
+
function removeTrailingSlash(path, minLength = 0) {
|
|
655
|
+
let end = path.length;
|
|
656
|
+
while (end > minLength && path.charCodeAt(end - 1) === 47) end--;
|
|
657
|
+
return end === path.length ? path : path.slice(0, end);
|
|
658
|
+
}
|
|
607
659
|
const normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
|
|
608
660
|
const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
|
|
609
661
|
const normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
|
|
@@ -1768,7 +1820,7 @@ async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fe
|
|
|
1768
1820
|
});
|
|
1769
1821
|
try {
|
|
1770
1822
|
await Promise.all(matches.flatMap((m) => [m._lazyPromises?.handler, m._lazyPromises?.route]));
|
|
1771
|
-
} catch
|
|
1823
|
+
} catch {}
|
|
1772
1824
|
return results;
|
|
1773
1825
|
}
|
|
1774
1826
|
async function callLoaderOrAction({ request, path, pattern, match, lazyHandlerPromise, lazyRoutePromise, handlerOverride, scopedContext }) {
|
|
@@ -2169,14 +2221,19 @@ function getPathsWithAncestors(paths) {
|
|
|
2169
2221
|
function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
|
|
2170
2222
|
let originHeader = request.headers.get("origin");
|
|
2171
2223
|
let originDomain = null;
|
|
2224
|
+
let originUrl = null;
|
|
2172
2225
|
try {
|
|
2173
|
-
|
|
2226
|
+
if (typeof originHeader === "string" && originHeader !== "null") {
|
|
2227
|
+
originUrl = new URL(originHeader);
|
|
2228
|
+
originDomain = originUrl.host;
|
|
2229
|
+
} else originDomain = originHeader;
|
|
2174
2230
|
} catch {
|
|
2175
2231
|
throw new Error(`\`origin\` header is not a valid URL. Aborting the action.`);
|
|
2176
2232
|
}
|
|
2177
|
-
let
|
|
2178
|
-
|
|
2179
|
-
|
|
2233
|
+
let requestUrl = new URL(request.url);
|
|
2234
|
+
let originMatchesRequest = originUrl ? originUrl.origin === requestUrl.origin : originDomain === requestUrl.host;
|
|
2235
|
+
if (originDomain && !originMatchesRequest) {
|
|
2236
|
+
if (!isAllowedOrigin(originDomain, allowedActionOrigins)) throw new Error("The `request.url` origin does not match `origin` header from a forwarded action request. Aborting the action.");
|
|
2180
2237
|
}
|
|
2181
2238
|
}
|
|
2182
2239
|
function matchWildcardDomain(domain, pattern) {
|
|
@@ -2332,6 +2389,8 @@ const Await = (async ({ children, resolve, errorElement }) => {
|
|
|
2332
2389
|
* encoding the {@link unstable_RSCPayload}.
|
|
2333
2390
|
* @param opts.loadServerAction Your `react-server-dom-xyz/server`'s
|
|
2334
2391
|
* `loadServerAction` function, used to load a server action by ID.
|
|
2392
|
+
* @param opts.clientVersion A version derived from the client build output used
|
|
2393
|
+
* to detect stale clients during lazy route discovery.
|
|
2335
2394
|
* @param opts.onError An optional error handler that will be called with any
|
|
2336
2395
|
* errors that occur during the request processing.
|
|
2337
2396
|
* @param opts.request The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
|
|
@@ -2345,7 +2404,7 @@ const Await = (async ({ children, resolve, errorElement }) => {
|
|
|
2345
2404
|
* that contains the [RSC](https://react.dev/reference/rsc/server-components)
|
|
2346
2405
|
* data for hydration.
|
|
2347
2406
|
*/
|
|
2348
|
-
async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse }) {
|
|
2407
|
+
async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, clientVersion, onError, request, routes, generateResponse }) {
|
|
2349
2408
|
let url = new URL(request.url);
|
|
2350
2409
|
basename = basename || "/";
|
|
2351
2410
|
let normalizedPath = url.pathname;
|
|
@@ -2363,22 +2422,26 @@ async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryRefe
|
|
|
2363
2422
|
});
|
|
2364
2423
|
const temporaryReferences = createTemporaryReferenceSet();
|
|
2365
2424
|
const requestUrl = new URL(request.url);
|
|
2366
|
-
if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery);
|
|
2425
|
+
if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery, clientVersion);
|
|
2367
2426
|
let isDataRequest = isReactServerRequest(requestUrl);
|
|
2368
2427
|
let matches = matchRoutes(routes, url.pathname, basename);
|
|
2369
2428
|
if (matches) await Promise.all(matches.map((m) => explodeLazyRoute(m.route)));
|
|
2370
2429
|
const leafMatch = matches?.[matches.length - 1];
|
|
2371
2430
|
if (!isDataRequest && leafMatch && !leafMatch.route.Component && !leafMatch.route.ErrorBoundary) return generateResourceResponse(routerRequest, routes, basename, leafMatch.route.id, requestContext, onError);
|
|
2372
|
-
let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery);
|
|
2431
|
+
let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery, clientVersion);
|
|
2373
2432
|
response.headers.set("X-Remix-Response", "yes");
|
|
2374
2433
|
return response;
|
|
2375
2434
|
}
|
|
2376
|
-
async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery) {
|
|
2435
|
+
async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery, clientVersion) {
|
|
2377
2436
|
let url = new URL(request.url);
|
|
2378
2437
|
if (url.toString().length > 7680) return new Response(null, {
|
|
2379
2438
|
statusText: "Bad Request",
|
|
2380
2439
|
status: 400
|
|
2381
2440
|
});
|
|
2441
|
+
if (clientVersion !== void 0 && clientVersion !== url.searchParams.get("version")) return new Response(null, {
|
|
2442
|
+
status: 204,
|
|
2443
|
+
headers: { "X-Remix-Reload-Document": "true" }
|
|
2444
|
+
});
|
|
2382
2445
|
if (routeDiscovery?.mode === "initial") {
|
|
2383
2446
|
let payload = {
|
|
2384
2447
|
type: "manifest",
|
|
@@ -2527,7 +2590,7 @@ async function generateResourceResponse(request, routes, basename, routeId, requ
|
|
|
2527
2590
|
});
|
|
2528
2591
|
}
|
|
2529
2592
|
}
|
|
2530
|
-
async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery) {
|
|
2593
|
+
async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery, clientVersion) {
|
|
2531
2594
|
let statusCode = 200;
|
|
2532
2595
|
let url = new URL(request.url);
|
|
2533
2596
|
let isSubmission = isMutationMethod(request.method);
|
|
@@ -2548,29 +2611,39 @@ async function generateRenderResponse(request, routes, basename, isDataRequest,
|
|
|
2548
2611
|
let formState;
|
|
2549
2612
|
let skipRevalidation = false;
|
|
2550
2613
|
let potentialCSRFAttackError;
|
|
2551
|
-
if (isMutationMethod(request.method))
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2556
|
-
|
|
2557
|
-
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
|
|
2562
|
-
|
|
2563
|
-
|
|
2564
|
-
|
|
2614
|
+
if (isMutationMethod(request.method)) {
|
|
2615
|
+
try {
|
|
2616
|
+
throwIfPotentialCSRFAttack(request, allowedActionOrigins);
|
|
2617
|
+
} catch (error) {
|
|
2618
|
+
onError?.(error);
|
|
2619
|
+
potentialCSRFAttackError = error;
|
|
2620
|
+
request = new Request(request.url, {
|
|
2621
|
+
method: "GET",
|
|
2622
|
+
headers: request.headers,
|
|
2623
|
+
signal: request.signal
|
|
2624
|
+
});
|
|
2625
|
+
}
|
|
2626
|
+
if (!potentialCSRFAttackError) {
|
|
2627
|
+
ctx.runningAction = true;
|
|
2628
|
+
let result = await processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences).finally(() => {
|
|
2629
|
+
ctx.runningAction = false;
|
|
2630
|
+
});
|
|
2631
|
+
if (isResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
|
|
2632
|
+
skipRevalidation = result?.skipRevalidation ?? false;
|
|
2633
|
+
actionResult = result?.actionResult;
|
|
2634
|
+
formState = result?.formState;
|
|
2635
|
+
request = result?.revalidationRequest ?? request;
|
|
2636
|
+
if (ctx.redirect) return generateRedirectResponse(ctx.redirect, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, void 0);
|
|
2637
|
+
}
|
|
2565
2638
|
}
|
|
2566
|
-
let staticContext = await query(request, skipRevalidation
|
|
2639
|
+
let staticContext = await query(request, skipRevalidation ? { filterMatchesToLoad: () => false } : void 0);
|
|
2567
2640
|
if (isResponse(staticContext)) return generateRedirectResponse(staticContext, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
|
|
2568
2641
|
if (potentialCSRFAttackError) {
|
|
2569
2642
|
staticContext.errors ??= {};
|
|
2570
2643
|
staticContext.errors[staticContext.matches[0].route.id] = potentialCSRFAttackError;
|
|
2571
2644
|
staticContext.statusCode = 400;
|
|
2572
2645
|
}
|
|
2573
|
-
return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery);
|
|
2646
|
+
return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery, clientVersion);
|
|
2574
2647
|
}
|
|
2575
2648
|
}));
|
|
2576
2649
|
if (isRedirectResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
|
|
@@ -2604,7 +2677,7 @@ function generateRedirectResponse(response, actionResult, basename, isDataReques
|
|
|
2604
2677
|
onError: defaultOnError
|
|
2605
2678
|
});
|
|
2606
2679
|
}
|
|
2607
|
-
async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery) {
|
|
2680
|
+
async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery, clientVersion) {
|
|
2608
2681
|
statusCode = staticContext.statusCode ?? statusCode;
|
|
2609
2682
|
if (staticContext.errors) staticContext.errors = Object.fromEntries(Object.entries(staticContext.errors).map(([key, error]) => [key, isRouteErrorResponse(error) ? Object.fromEntries(Object.entries(error)) : error]));
|
|
2610
2683
|
staticContext.matches.forEach((m) => {
|
|
@@ -2617,6 +2690,7 @@ async function generateStaticContextResponse(routes, basename, generateResponse,
|
|
|
2617
2690
|
const baseRenderPayload = {
|
|
2618
2691
|
type: "render",
|
|
2619
2692
|
basename: staticContext.basename,
|
|
2693
|
+
clientVersion,
|
|
2620
2694
|
routeDiscovery: routeDiscovery ?? { mode: "lazy" },
|
|
2621
2695
|
actionData: staticContext.actionData,
|
|
2622
2696
|
errors: staticContext.errors,
|
|
@@ -2812,14 +2886,30 @@ function stringify(p) {
|
|
|
2812
2886
|
return p == null ? "" : typeof p === "string" ? p : String(p);
|
|
2813
2887
|
}
|
|
2814
2888
|
/**
|
|
2815
|
-
Returns a resolved URL path for the specified route.
|
|
2816
|
-
|
|
2817
|
-
|
|
2818
|
-
|
|
2819
|
-
|
|
2820
|
-
|
|
2821
|
-
|
|
2822
|
-
|
|
2889
|
+
* Returns a resolved URL path for the specified route.
|
|
2890
|
+
*
|
|
2891
|
+
* Param values are percent-encoded for use in a path segment: characters that
|
|
2892
|
+
* would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
|
|
2893
|
+
* are escaped, while characters that RFC 3986 allows literally in a path
|
|
2894
|
+
* segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
|
|
2895
|
+
* encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
|
|
2896
|
+
* delimiters and must be escaped. Splat (`*`) values are encoded per segment,
|
|
2897
|
+
* preserving `/` separators.
|
|
2898
|
+
*
|
|
2899
|
+
* See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
|
|
2900
|
+
*
|
|
2901
|
+
* @example
|
|
2902
|
+
* const h = href("/:lang?/about", { lang: "en" })
|
|
2903
|
+
* // -> `/en/about`
|
|
2904
|
+
*
|
|
2905
|
+
* <Link to={href("/products/:id", { id: "abc123" })} />
|
|
2906
|
+
*
|
|
2907
|
+
* @public
|
|
2908
|
+
* @category Utils
|
|
2909
|
+
* @mode framework
|
|
2910
|
+
* @param path The route path to resolve
|
|
2911
|
+
* @param args The route params to use when resolving the path
|
|
2912
|
+
* @returns The resolved URL path
|
|
2823
2913
|
*/
|
|
2824
2914
|
function href(path, ...args) {
|
|
2825
2915
|
let params = args[0];
|
|
@@ -2827,11 +2917,11 @@ function href(path, ...args) {
|
|
|
2827
2917
|
const isRequired = questionMark === void 0;
|
|
2828
2918
|
const value = params?.[param];
|
|
2829
2919
|
if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
|
|
2830
|
-
return value == null ? "" : "/" +
|
|
2920
|
+
return value == null ? "" : "/" + encodePathParam(stringify(value));
|
|
2831
2921
|
});
|
|
2832
2922
|
if (path.endsWith("*")) {
|
|
2833
2923
|
const value = params?.["*"];
|
|
2834
|
-
if (value !== void 0) result += "/" + stringify(value).split("/").map(
|
|
2924
|
+
if (value !== void 0) result += "/" + stringify(value).split("/").map(encodePathParam).join("/");
|
|
2835
2925
|
}
|
|
2836
2926
|
return result || "/";
|
|
2837
2927
|
}
|
|
@@ -2867,7 +2957,7 @@ const unsign = async (cookie, secret) => {
|
|
|
2867
2957
|
try {
|
|
2868
2958
|
let signature = byteStringToUint8Array(atob(hash));
|
|
2869
2959
|
return await crypto.subtle.verify("HMAC", key, signature, data) ? value : false;
|
|
2870
|
-
} catch
|
|
2960
|
+
} catch {
|
|
2871
2961
|
return false;
|
|
2872
2962
|
}
|
|
2873
2963
|
};
|
|
@@ -2884,6 +2974,14 @@ function byteStringToUint8Array(byteString) {
|
|
|
2884
2974
|
//#region lib/server-runtime/cookies.ts
|
|
2885
2975
|
/**
|
|
2886
2976
|
* Creates a logical container for managing a browser cookie from the server.
|
|
2977
|
+
*
|
|
2978
|
+
* @public
|
|
2979
|
+
* @category Utils
|
|
2980
|
+
* @mode framework
|
|
2981
|
+
* @mode data
|
|
2982
|
+
* @param name The name of the cookie.
|
|
2983
|
+
* @param cookieOptions Options for parsing and serializing the cookie.
|
|
2984
|
+
* @returns A {@link Cookie} object for parsing and serializing the cookie.
|
|
2887
2985
|
*/
|
|
2888
2986
|
const createCookie = (name, cookieOptions = {}) => {
|
|
2889
2987
|
let { secrets = [], ...options } = {
|
|
@@ -2923,9 +3021,15 @@ const createCookie = (name, cookieOptions = {}) => {
|
|
|
2923
3021
|
};
|
|
2924
3022
|
};
|
|
2925
3023
|
/**
|
|
2926
|
-
* Returns true if
|
|
3024
|
+
* Returns `true` if a value is a React Router {@link Cookie} object.
|
|
2927
3025
|
*
|
|
2928
|
-
* @
|
|
3026
|
+
* @public
|
|
3027
|
+
* @category Utils
|
|
3028
|
+
* @mode framework
|
|
3029
|
+
* @mode data
|
|
3030
|
+
* @param object The value to check.
|
|
3031
|
+
* @returns `true` if the value is a React Router {@link Cookie} object;
|
|
3032
|
+
* otherwise, `false`.
|
|
2929
3033
|
*/
|
|
2930
3034
|
const isCookie = (object) => {
|
|
2931
3035
|
return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
|
|
@@ -2951,7 +3055,7 @@ function encodeData(value) {
|
|
|
2951
3055
|
function decodeData(value) {
|
|
2952
3056
|
try {
|
|
2953
3057
|
return JSON.parse(decodeURIComponent(myEscape(atob(value))));
|
|
2954
|
-
} catch
|
|
3058
|
+
} catch {
|
|
2955
3059
|
return {};
|
|
2956
3060
|
}
|
|
2957
3061
|
}
|
|
@@ -3015,6 +3119,12 @@ function flash(name) {
|
|
|
3015
3119
|
*
|
|
3016
3120
|
* Note: This function is typically not invoked directly by application code.
|
|
3017
3121
|
* Instead, use a `SessionStorage` object's `getSession` method.
|
|
3122
|
+
*
|
|
3123
|
+
* @category Utils
|
|
3124
|
+
* @param initialData The initial data for the session.
|
|
3125
|
+
* @param id The identifier for the session. Defaults to an empty string for a
|
|
3126
|
+
* new session.
|
|
3127
|
+
* @returns A new {@link Session} object.
|
|
3018
3128
|
*/
|
|
3019
3129
|
const createSession = (initialData = {}, id = "") => {
|
|
3020
3130
|
let map = new Map(Object.entries(initialData));
|
|
@@ -3049,9 +3159,15 @@ const createSession = (initialData = {}, id = "") => {
|
|
|
3049
3159
|
};
|
|
3050
3160
|
};
|
|
3051
3161
|
/**
|
|
3052
|
-
* Returns true if
|
|
3162
|
+
* Returns `true` if a value is a React Router {@link Session} object.
|
|
3053
3163
|
*
|
|
3054
|
-
* @
|
|
3164
|
+
* @public
|
|
3165
|
+
* @category Utils
|
|
3166
|
+
* @mode framework
|
|
3167
|
+
* @mode data
|
|
3168
|
+
* @param object The value to check.
|
|
3169
|
+
* @returns `true` if the value is a React Router {@link Session} object;
|
|
3170
|
+
* otherwise, `false`.
|
|
3055
3171
|
*/
|
|
3056
3172
|
const isSession = (object) => {
|
|
3057
3173
|
return object != null && typeof object.id === "string" && typeof object.data !== "undefined" && typeof object.has === "function" && typeof object.get === "function" && typeof object.set === "function" && typeof object.flash === "function" && typeof object.unset === "function";
|
|
@@ -3061,6 +3177,11 @@ const isSession = (object) => {
|
|
|
3061
3177
|
*
|
|
3062
3178
|
* Note: This is a low-level API that should only be used if none of the
|
|
3063
3179
|
* existing session storage options meet your requirements.
|
|
3180
|
+
*
|
|
3181
|
+
* @category Utils
|
|
3182
|
+
* @param strategy The strategy used to store session identifiers and data.
|
|
3183
|
+
* @returns A {@link SessionStorage} object that persists session data using the
|
|
3184
|
+
* provided strategy.
|
|
3064
3185
|
*/
|
|
3065
3186
|
function createSessionStorage({ cookie: cookieArg, createData, readData, updateData, deleteData }) {
|
|
3066
3187
|
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
|
|
@@ -3100,6 +3221,14 @@ function warnOnceAboutSigningSessionCookie(cookie) {
|
|
|
3100
3221
|
* needed, and can help to simplify some load-balanced scenarios. However, it
|
|
3101
3222
|
* also has the limitation that serialized session data may not exceed the
|
|
3102
3223
|
* browser's maximum cookie size. Trade-offs!
|
|
3224
|
+
*
|
|
3225
|
+
* @public
|
|
3226
|
+
* @category Utils
|
|
3227
|
+
* @mode framework
|
|
3228
|
+
* @mode data
|
|
3229
|
+
* @param options Options for creating the cookie-backed session storage.
|
|
3230
|
+
* @returns A {@link SessionStorage} object that stores all session data in its
|
|
3231
|
+
* cookie.
|
|
3103
3232
|
*/
|
|
3104
3233
|
function createCookieSessionStorage({ cookie: cookieArg } = {}) {
|
|
3105
3234
|
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
|
|
@@ -3125,18 +3254,24 @@ function createCookieSessionStorage({ cookie: cookieArg } = {}) {
|
|
|
3125
3254
|
//#endregion
|
|
3126
3255
|
//#region lib/server-runtime/sessions/memoryStorage.ts
|
|
3127
3256
|
/**
|
|
3128
|
-
* Creates and returns a simple in-memory SessionStorage object
|
|
3129
|
-
*
|
|
3257
|
+
* Creates and returns a simple in-memory SessionStorage object.
|
|
3258
|
+
*
|
|
3259
|
+
* Intended for local development and testing. It does not scale beyond a single
|
|
3260
|
+
* process, and all session data is lost when the server process stops/restarts.
|
|
3130
3261
|
*
|
|
3131
|
-
*
|
|
3132
|
-
*
|
|
3262
|
+
* @public
|
|
3263
|
+
* @category Utils
|
|
3264
|
+
* @mode framework
|
|
3265
|
+
* @mode data
|
|
3266
|
+
* @param options Options for creating the in-memory session storage.
|
|
3267
|
+
* @returns A {@link SessionStorage} object that stores session data in memory.
|
|
3133
3268
|
*/
|
|
3134
3269
|
function createMemorySessionStorage({ cookie } = {}) {
|
|
3135
3270
|
let map = /* @__PURE__ */ new Map();
|
|
3136
3271
|
return createSessionStorage({
|
|
3137
3272
|
cookie,
|
|
3138
3273
|
async createData(data, expires) {
|
|
3139
|
-
let id =
|
|
3274
|
+
let id = crypto.randomUUID();
|
|
3140
3275
|
map.set(id, {
|
|
3141
3276
|
data,
|
|
3142
3277
|
expires
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.3.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -12,14 +12,19 @@
|
|
|
12
12
|
function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
|
|
13
13
|
let originHeader = request.headers.get("origin");
|
|
14
14
|
let originDomain = null;
|
|
15
|
+
let originUrl = null;
|
|
15
16
|
try {
|
|
16
|
-
|
|
17
|
+
if (typeof originHeader === "string" && originHeader !== "null") {
|
|
18
|
+
originUrl = new URL(originHeader);
|
|
19
|
+
originDomain = originUrl.host;
|
|
20
|
+
} else originDomain = originHeader;
|
|
17
21
|
} catch {
|
|
18
22
|
throw new Error(`\`origin\` header is not a valid URL. Aborting the action.`);
|
|
19
23
|
}
|
|
20
|
-
let
|
|
21
|
-
|
|
22
|
-
|
|
24
|
+
let requestUrl = new URL(request.url);
|
|
25
|
+
let originMatchesRequest = originUrl ? originUrl.origin === requestUrl.origin : originDomain === requestUrl.host;
|
|
26
|
+
if (originDomain && !originMatchesRequest) {
|
|
27
|
+
if (!isAllowedOrigin(originDomain, allowedActionOrigins)) throw new Error("The `request.url` origin does not match `origin` header from a forwarded action request. Aborting the action.");
|
|
23
28
|
}
|
|
24
29
|
}
|
|
25
30
|
function matchWildcardDomain(domain, pattern) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/**
|
|
2
|
-
* react-router v8.
|
|
2
|
+
* react-router v8.3.1
|
|
3
3
|
*
|
|
4
4
|
* Copyright (c) Remix Software Inc.
|
|
5
5
|
*
|
|
@@ -8,8 +8,9 @@
|
|
|
8
8
|
*
|
|
9
9
|
* @license MIT
|
|
10
10
|
*/
|
|
11
|
-
import { createMemoryHistory, invariant, parsePath, warning } from "./router/history.js";
|
|
11
|
+
import { createMemoryHistory, createPath, invariant, parsePath, warning } from "./router/history.js";
|
|
12
12
|
import { defaultMapRouteProperties, getResolveToMatches, getRoutePattern, resolveTo, stripBasename } from "./router/utils.js";
|
|
13
|
+
import { getNavigatorCurrentUrl, validateNavigationTarget } from "./router/navigation.js";
|
|
13
14
|
import { createRouter } from "./router/router.js";
|
|
14
15
|
import { AwaitContext, DataRouterContext, DataRouterStateContext, FetchersContext, LocationContext, NavigationContext, RouteContext, ViewTransitionContext, useIsRSCRouterContext } from "./context.js";
|
|
15
16
|
import { _renderMatches, useActionData, useAsyncValue, useInRouterContext, useLoaderData, useLocation, useMatches, useNavigate, useOutlet, useParams, useRouteError, useRoutes, useRoutesImpl } from "./hooks.js";
|
|
@@ -256,6 +257,7 @@ function RouterProvider({ router, flushSync: reactDomFlushSyncImpl, onError, use
|
|
|
256
257
|
let navigator = React$1.useMemo(() => {
|
|
257
258
|
return {
|
|
258
259
|
createHref: router.createHref,
|
|
260
|
+
createURL: router.createURL,
|
|
259
261
|
encodeLocation: router.encodeLocation,
|
|
260
262
|
go: (n) => router.navigate(n),
|
|
261
263
|
push: (to, state, opts) => router.navigate(to, {
|
|
@@ -379,12 +381,13 @@ function MemoryRouter({ basename, children, initialEntries, initialIndex, useTra
|
|
|
379
381
|
*/
|
|
380
382
|
function Navigate({ to, replace, state, relative }) {
|
|
381
383
|
invariant(useInRouterContext(), `<Navigate> may be used only in the context of a <Router> component.`);
|
|
382
|
-
let { static: isStatic } = React$1.useContext(NavigationContext);
|
|
384
|
+
let { static: isStatic, navigator } = React$1.useContext(NavigationContext);
|
|
383
385
|
warning(!isStatic, "<Navigate> must not be used on the initial render in a <StaticRouter>. This is a no-op, but you should modify your code so the <Navigate> is only ever rendered in response to some user interaction or state change.");
|
|
384
386
|
let { matches } = React$1.useContext(RouteContext);
|
|
385
387
|
let { pathname: locationPathname } = useLocation();
|
|
386
388
|
let navigate = useNavigate();
|
|
387
389
|
let path = resolveTo(to, getResolveToMatches(matches), locationPathname, relative === "path");
|
|
390
|
+
validateNavigationTarget(typeof to === "string" ? to : createPath(to), navigator.createHref(path), getNavigatorCurrentUrl(navigator), "reject");
|
|
388
391
|
let jsonPath = JSON.stringify(path);
|
|
389
392
|
React$1.useEffect(() => {
|
|
390
393
|
navigate(JSON.parse(jsonPath), {
|
|
@@ -55,6 +55,7 @@ interface NavigateOptions {
|
|
|
55
55
|
*/
|
|
56
56
|
interface Navigator {
|
|
57
57
|
createHref: History["createHref"];
|
|
58
|
+
createURL?: History["createURL"];
|
|
58
59
|
encodeLocation?: History["encodeLocation"];
|
|
59
60
|
go: History["go"];
|
|
60
61
|
push(to: To, state?: any, opts?: NavigateOptions): void;
|
|
@@ -6,30 +6,30 @@ import { RelativeRoutingType } from "../router/router.js";
|
|
|
6
6
|
type ParamKeyValuePair = [string, string];
|
|
7
7
|
type URLSearchParamsInit = string | ParamKeyValuePair[] | Record<string, string | string[]> | URLSearchParams;
|
|
8
8
|
/**
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
9
|
+
* Creates a URLSearchParams object using the given initializer.
|
|
10
|
+
*
|
|
11
|
+
* This is identical to `new URLSearchParams(init)` except it also supports
|
|
12
|
+
* arrays as values in the object form of the initializer instead of just
|
|
13
|
+
* strings. This is convenient when you need multiple values for a given key,
|
|
14
|
+
* but don't want to use an array initializer.
|
|
15
|
+
*
|
|
16
|
+
* @example
|
|
17
|
+
* // Instead of:
|
|
18
|
+
* let searchParams = new URLSearchParams([
|
|
19
|
+
* ["sort", "name"],
|
|
20
|
+
* ["sort", "price"],
|
|
21
|
+
* ]);
|
|
22
|
+
*
|
|
23
|
+
* // You can do:
|
|
24
|
+
* let searchParams = createSearchParams({
|
|
25
|
+
* sort: ["name", "price"],
|
|
26
|
+
* });
|
|
27
|
+
*
|
|
28
|
+
* @public
|
|
29
|
+
* @category Utils
|
|
30
|
+
* @param init The value used to initialize the URL search parameters.
|
|
31
|
+
* @returns A URLSearchParams object containing the initialized search
|
|
32
|
+
* parameters.
|
|
33
33
|
*/
|
|
34
34
|
declare function createSearchParams(init?: URLSearchParamsInit): URLSearchParams;
|
|
35
35
|
type JsonObject = { [Key in string]: JsonValue } & { [Key in string]?: JsonValue | undefined };
|