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 = {};
|
|
@@ -432,6 +438,48 @@ function matchRouteBranch(branch, pathname, allowPartial = false) {
|
|
|
432
438
|
return matches;
|
|
433
439
|
}
|
|
434
440
|
/**
|
|
441
|
+
* Characters that `encodeURIComponent` escapes but that are valid literally in
|
|
442
|
+
* a URL path segment. Per RFC 3986 §3.3, a path segment is made of `pchar`:
|
|
443
|
+
*
|
|
444
|
+
* ```
|
|
445
|
+
* pchar = unreserved / pct-encoded / sub-delims / ":" / "@"
|
|
446
|
+
* sub-delims = "!" / "$" / "&" / "'" / "(" / ")" / "*" / "+" / "," / ";" / "="
|
|
447
|
+
* ```
|
|
448
|
+
*
|
|
449
|
+
* `encodeURIComponent` targets query-string values, where `$ & + , ; = : @`
|
|
450
|
+
* are delimiters and must be escaped — but in a path segment they carry no
|
|
451
|
+
* special meaning, and browsers keep them literal in `location.pathname`.
|
|
452
|
+
* (`! ' ( ) *` and the unreserved set are already left alone by
|
|
453
|
+
* `encodeURIComponent`, so they need no restoring.)
|
|
454
|
+
*/
|
|
455
|
+
const PATH_PARAM_OVERESCAPED = {
|
|
456
|
+
"%24": "$",
|
|
457
|
+
"%26": "&",
|
|
458
|
+
"%2B": "+",
|
|
459
|
+
"%2C": ",",
|
|
460
|
+
"%3A": ":",
|
|
461
|
+
"%3B": ";",
|
|
462
|
+
"%3D": "=",
|
|
463
|
+
"%40": "@"
|
|
464
|
+
};
|
|
465
|
+
/**
|
|
466
|
+
* Encodes a param value for interpolation into a single URL path segment.
|
|
467
|
+
*
|
|
468
|
+
* Escapes characters that would break the path (`/ ? # %`, whitespace,
|
|
469
|
+
* non-ASCII, …) while leaving characters that RFC 3986 permits literally in a
|
|
470
|
+
* path segment untouched. Escaping those would needlessly rewrite URLs — e.g.
|
|
471
|
+
* a semver build param `1.0.0+1` would become `1.0.0%2B1` even though browsers
|
|
472
|
+
* display and match the `+` literally in `location.pathname`.
|
|
473
|
+
*
|
|
474
|
+
* See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3))
|
|
475
|
+
*
|
|
476
|
+
* @param value The param value to encode.
|
|
477
|
+
* @returns The encoded value, safe for use as a single path segment.
|
|
478
|
+
*/
|
|
479
|
+
function encodePathParam(value) {
|
|
480
|
+
return encodeURIComponent(value).replace(/%(?:24|26|2B|2C|3A|3B|3D|40)/g, (match) => PATH_PARAM_OVERESCAPED[match]);
|
|
481
|
+
}
|
|
482
|
+
/**
|
|
435
483
|
* Performs pattern matching on a URL pathname and returns information about
|
|
436
484
|
* the match.
|
|
437
485
|
*
|
|
@@ -458,13 +506,13 @@ function matchPathImpl(pattern, pathname, matcher, compiledParams) {
|
|
|
458
506
|
let match = pathname.match(matcher);
|
|
459
507
|
if (!match) return null;
|
|
460
508
|
let matchedPathname = match[0];
|
|
461
|
-
let pathnameBase = matchedPathname
|
|
509
|
+
let pathnameBase = removeTrailingSlash(matchedPathname, 1);
|
|
462
510
|
let captureGroups = match.slice(1);
|
|
463
511
|
return {
|
|
464
512
|
params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
|
|
465
513
|
if (paramName === "*") {
|
|
466
514
|
let splatValue = captureGroups[index] || "";
|
|
467
|
-
pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length)
|
|
515
|
+
pathnameBase = removeTrailingSlash(matchedPathname.slice(0, matchedPathname.length - splatValue.length), 1);
|
|
468
516
|
}
|
|
469
517
|
const value = captureGroups[index];
|
|
470
518
|
if (isOptional && !value) memo[paramName] = void 0;
|
|
@@ -533,7 +581,7 @@ function resolvePath(to, fromPathname = "/") {
|
|
|
533
581
|
let pathname;
|
|
534
582
|
if (toPathname) {
|
|
535
583
|
toPathname = removeDoubleSlashes(toPathname);
|
|
536
|
-
if (toPathname.startsWith("/")) pathname = resolvePathname(toPathname.substring(1), "/");
|
|
584
|
+
if (toPathname.startsWith("/") || toPathname.startsWith("\\")) pathname = resolvePathname(toPathname.substring(1), "/");
|
|
537
585
|
else pathname = resolvePathname(toPathname, fromPathname);
|
|
538
586
|
} else pathname = fromPathname;
|
|
539
587
|
return {
|
|
@@ -594,7 +642,11 @@ function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = fal
|
|
|
594
642
|
}
|
|
595
643
|
const removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
|
|
596
644
|
const joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
|
|
597
|
-
|
|
645
|
+
function removeTrailingSlash(path, minLength = 0) {
|
|
646
|
+
let end = path.length;
|
|
647
|
+
while (end > minLength && path.charCodeAt(end - 1) === 47) end--;
|
|
648
|
+
return end === path.length ? path : path.slice(0, end);
|
|
649
|
+
}
|
|
598
650
|
const normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
|
|
599
651
|
const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
|
|
600
652
|
const normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
|
|
@@ -1759,7 +1811,7 @@ async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fe
|
|
|
1759
1811
|
});
|
|
1760
1812
|
try {
|
|
1761
1813
|
await Promise.all(matches.flatMap((m) => [m._lazyPromises?.handler, m._lazyPromises?.route]));
|
|
1762
|
-
} catch
|
|
1814
|
+
} catch {}
|
|
1763
1815
|
return results;
|
|
1764
1816
|
}
|
|
1765
1817
|
async function callLoaderOrAction({ request, path, pattern, match, lazyHandlerPromise, lazyRoutePromise, handlerOverride, scopedContext }) {
|
|
@@ -2160,14 +2212,19 @@ function getPathsWithAncestors(paths) {
|
|
|
2160
2212
|
function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
|
|
2161
2213
|
let originHeader = request.headers.get("origin");
|
|
2162
2214
|
let originDomain = null;
|
|
2215
|
+
let originUrl = null;
|
|
2163
2216
|
try {
|
|
2164
|
-
|
|
2217
|
+
if (typeof originHeader === "string" && originHeader !== "null") {
|
|
2218
|
+
originUrl = new URL(originHeader);
|
|
2219
|
+
originDomain = originUrl.host;
|
|
2220
|
+
} else originDomain = originHeader;
|
|
2165
2221
|
} catch {
|
|
2166
2222
|
throw new Error(`\`origin\` header is not a valid URL. Aborting the action.`);
|
|
2167
2223
|
}
|
|
2168
|
-
let
|
|
2169
|
-
|
|
2170
|
-
|
|
2224
|
+
let requestUrl = new URL(request.url);
|
|
2225
|
+
let originMatchesRequest = originUrl ? originUrl.origin === requestUrl.origin : originDomain === requestUrl.host;
|
|
2226
|
+
if (originDomain && !originMatchesRequest) {
|
|
2227
|
+
if (!isAllowedOrigin(originDomain, allowedActionOrigins)) throw new Error("The `request.url` origin does not match `origin` header from a forwarded action request. Aborting the action.");
|
|
2171
2228
|
}
|
|
2172
2229
|
}
|
|
2173
2230
|
function matchWildcardDomain(domain, pattern) {
|
|
@@ -2323,6 +2380,8 @@ const Await = (async ({ children, resolve, errorElement }) => {
|
|
|
2323
2380
|
* encoding the {@link unstable_RSCPayload}.
|
|
2324
2381
|
* @param opts.loadServerAction Your `react-server-dom-xyz/server`'s
|
|
2325
2382
|
* `loadServerAction` function, used to load a server action by ID.
|
|
2383
|
+
* @param opts.clientVersion A version derived from the client build output used
|
|
2384
|
+
* to detect stale clients during lazy route discovery.
|
|
2326
2385
|
* @param opts.onError An optional error handler that will be called with any
|
|
2327
2386
|
* errors that occur during the request processing.
|
|
2328
2387
|
* @param opts.request The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
|
|
@@ -2336,7 +2395,7 @@ const Await = (async ({ children, resolve, errorElement }) => {
|
|
|
2336
2395
|
* that contains the [RSC](https://react.dev/reference/rsc/server-components)
|
|
2337
2396
|
* data for hydration.
|
|
2338
2397
|
*/
|
|
2339
|
-
async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse }) {
|
|
2398
|
+
async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, clientVersion, onError, request, routes, generateResponse }) {
|
|
2340
2399
|
let url = new URL(request.url);
|
|
2341
2400
|
basename = basename || "/";
|
|
2342
2401
|
let normalizedPath = url.pathname;
|
|
@@ -2354,22 +2413,26 @@ async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryRefe
|
|
|
2354
2413
|
});
|
|
2355
2414
|
const temporaryReferences = createTemporaryReferenceSet();
|
|
2356
2415
|
const requestUrl = new URL(request.url);
|
|
2357
|
-
if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery);
|
|
2416
|
+
if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery, clientVersion);
|
|
2358
2417
|
let isDataRequest = isReactServerRequest(requestUrl);
|
|
2359
2418
|
let matches = matchRoutes(routes, url.pathname, basename);
|
|
2360
2419
|
if (matches) await Promise.all(matches.map((m) => explodeLazyRoute(m.route)));
|
|
2361
2420
|
const leafMatch = matches?.[matches.length - 1];
|
|
2362
2421
|
if (!isDataRequest && leafMatch && !leafMatch.route.Component && !leafMatch.route.ErrorBoundary) return generateResourceResponse(routerRequest, routes, basename, leafMatch.route.id, requestContext, onError);
|
|
2363
|
-
let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery);
|
|
2422
|
+
let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery, clientVersion);
|
|
2364
2423
|
response.headers.set("X-Remix-Response", "yes");
|
|
2365
2424
|
return response;
|
|
2366
2425
|
}
|
|
2367
|
-
async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery) {
|
|
2426
|
+
async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery, clientVersion) {
|
|
2368
2427
|
let url = new URL(request.url);
|
|
2369
2428
|
if (url.toString().length > 7680) return new Response(null, {
|
|
2370
2429
|
statusText: "Bad Request",
|
|
2371
2430
|
status: 400
|
|
2372
2431
|
});
|
|
2432
|
+
if (clientVersion !== void 0 && clientVersion !== url.searchParams.get("version")) return new Response(null, {
|
|
2433
|
+
status: 204,
|
|
2434
|
+
headers: { "X-Remix-Reload-Document": "true" }
|
|
2435
|
+
});
|
|
2373
2436
|
if (routeDiscovery?.mode === "initial") {
|
|
2374
2437
|
let payload = {
|
|
2375
2438
|
type: "manifest",
|
|
@@ -2518,7 +2581,7 @@ async function generateResourceResponse(request, routes, basename, routeId, requ
|
|
|
2518
2581
|
});
|
|
2519
2582
|
}
|
|
2520
2583
|
}
|
|
2521
|
-
async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery) {
|
|
2584
|
+
async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery, clientVersion) {
|
|
2522
2585
|
let statusCode = 200;
|
|
2523
2586
|
let url = new URL(request.url);
|
|
2524
2587
|
let isSubmission = isMutationMethod(request.method);
|
|
@@ -2539,29 +2602,39 @@ async function generateRenderResponse(request, routes, basename, isDataRequest,
|
|
|
2539
2602
|
let formState;
|
|
2540
2603
|
let skipRevalidation = false;
|
|
2541
2604
|
let potentialCSRFAttackError;
|
|
2542
|
-
if (isMutationMethod(request.method))
|
|
2543
|
-
|
|
2544
|
-
|
|
2545
|
-
|
|
2546
|
-
|
|
2547
|
-
|
|
2548
|
-
|
|
2549
|
-
|
|
2550
|
-
|
|
2551
|
-
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
|
|
2605
|
+
if (isMutationMethod(request.method)) {
|
|
2606
|
+
try {
|
|
2607
|
+
throwIfPotentialCSRFAttack(request, allowedActionOrigins);
|
|
2608
|
+
} catch (error) {
|
|
2609
|
+
onError?.(error);
|
|
2610
|
+
potentialCSRFAttackError = error;
|
|
2611
|
+
request = new Request(request.url, {
|
|
2612
|
+
method: "GET",
|
|
2613
|
+
headers: request.headers,
|
|
2614
|
+
signal: request.signal
|
|
2615
|
+
});
|
|
2616
|
+
}
|
|
2617
|
+
if (!potentialCSRFAttackError) {
|
|
2618
|
+
ctx.runningAction = true;
|
|
2619
|
+
let result = await processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences).finally(() => {
|
|
2620
|
+
ctx.runningAction = false;
|
|
2621
|
+
});
|
|
2622
|
+
if (isResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
|
|
2623
|
+
skipRevalidation = result?.skipRevalidation ?? false;
|
|
2624
|
+
actionResult = result?.actionResult;
|
|
2625
|
+
formState = result?.formState;
|
|
2626
|
+
request = result?.revalidationRequest ?? request;
|
|
2627
|
+
if (ctx.redirect) return generateRedirectResponse(ctx.redirect, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, void 0);
|
|
2628
|
+
}
|
|
2556
2629
|
}
|
|
2557
|
-
let staticContext = await query(request, skipRevalidation
|
|
2630
|
+
let staticContext = await query(request, skipRevalidation ? { filterMatchesToLoad: () => false } : void 0);
|
|
2558
2631
|
if (isResponse(staticContext)) return generateRedirectResponse(staticContext, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
|
|
2559
2632
|
if (potentialCSRFAttackError) {
|
|
2560
2633
|
staticContext.errors ??= {};
|
|
2561
2634
|
staticContext.errors[staticContext.matches[0].route.id] = potentialCSRFAttackError;
|
|
2562
2635
|
staticContext.statusCode = 400;
|
|
2563
2636
|
}
|
|
2564
|
-
return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery);
|
|
2637
|
+
return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery, clientVersion);
|
|
2565
2638
|
}
|
|
2566
2639
|
}));
|
|
2567
2640
|
if (isRedirectResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
|
|
@@ -2595,7 +2668,7 @@ function generateRedirectResponse(response, actionResult, basename, isDataReques
|
|
|
2595
2668
|
onError: defaultOnError
|
|
2596
2669
|
});
|
|
2597
2670
|
}
|
|
2598
|
-
async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery) {
|
|
2671
|
+
async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery, clientVersion) {
|
|
2599
2672
|
statusCode = staticContext.statusCode ?? statusCode;
|
|
2600
2673
|
if (staticContext.errors) staticContext.errors = Object.fromEntries(Object.entries(staticContext.errors).map(([key, error]) => [key, isRouteErrorResponse(error) ? Object.fromEntries(Object.entries(error)) : error]));
|
|
2601
2674
|
staticContext.matches.forEach((m) => {
|
|
@@ -2608,6 +2681,7 @@ async function generateStaticContextResponse(routes, basename, generateResponse,
|
|
|
2608
2681
|
const baseRenderPayload = {
|
|
2609
2682
|
type: "render",
|
|
2610
2683
|
basename: staticContext.basename,
|
|
2684
|
+
clientVersion,
|
|
2611
2685
|
routeDiscovery: routeDiscovery ?? { mode: "lazy" },
|
|
2612
2686
|
actionData: staticContext.actionData,
|
|
2613
2687
|
errors: staticContext.errors,
|
|
@@ -2803,14 +2877,30 @@ function stringify(p) {
|
|
|
2803
2877
|
return p == null ? "" : typeof p === "string" ? p : String(p);
|
|
2804
2878
|
}
|
|
2805
2879
|
/**
|
|
2806
|
-
Returns a resolved URL path for the specified route.
|
|
2807
|
-
|
|
2808
|
-
|
|
2809
|
-
|
|
2810
|
-
|
|
2811
|
-
|
|
2812
|
-
|
|
2813
|
-
|
|
2880
|
+
* Returns a resolved URL path for the specified route.
|
|
2881
|
+
*
|
|
2882
|
+
* Param values are percent-encoded for use in a path segment: characters that
|
|
2883
|
+
* would change the URL structure (`/`, `?`, `#`, `%`, whitespace, non-ASCII)
|
|
2884
|
+
* are escaped, while characters that RFC 3986 allows literally in a path
|
|
2885
|
+
* segment (`$ & + , ; = : @`) are kept as-is. Note this differs from query-string
|
|
2886
|
+
* encoding (`encodeURIComponent`/`URLSearchParams`), where those characters are
|
|
2887
|
+
* delimiters and must be escaped. Splat (`*`) values are encoded per segment,
|
|
2888
|
+
* preserving `/` separators.
|
|
2889
|
+
*
|
|
2890
|
+
* See [RFC 3986 §3.3](https://datatracker.ietf.org/doc/html/rfc3986#section-3.3)
|
|
2891
|
+
*
|
|
2892
|
+
* @example
|
|
2893
|
+
* const h = href("/:lang?/about", { lang: "en" })
|
|
2894
|
+
* // -> `/en/about`
|
|
2895
|
+
*
|
|
2896
|
+
* <Link to={href("/products/:id", { id: "abc123" })} />
|
|
2897
|
+
*
|
|
2898
|
+
* @public
|
|
2899
|
+
* @category Utils
|
|
2900
|
+
* @mode framework
|
|
2901
|
+
* @param path The route path to resolve
|
|
2902
|
+
* @param args The route params to use when resolving the path
|
|
2903
|
+
* @returns The resolved URL path
|
|
2814
2904
|
*/
|
|
2815
2905
|
function href(path, ...args) {
|
|
2816
2906
|
let params = args[0];
|
|
@@ -2818,11 +2908,11 @@ function href(path, ...args) {
|
|
|
2818
2908
|
const isRequired = questionMark === void 0;
|
|
2819
2909
|
const value = params?.[param];
|
|
2820
2910
|
if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
|
|
2821
|
-
return value == null ? "" : "/" +
|
|
2911
|
+
return value == null ? "" : "/" + encodePathParam(stringify(value));
|
|
2822
2912
|
});
|
|
2823
2913
|
if (path.endsWith("*")) {
|
|
2824
2914
|
const value = params?.["*"];
|
|
2825
|
-
if (value !== void 0) result += "/" + stringify(value).split("/").map(
|
|
2915
|
+
if (value !== void 0) result += "/" + stringify(value).split("/").map(encodePathParam).join("/");
|
|
2826
2916
|
}
|
|
2827
2917
|
return result || "/";
|
|
2828
2918
|
}
|
|
@@ -2858,7 +2948,7 @@ const unsign = async (cookie, secret) => {
|
|
|
2858
2948
|
try {
|
|
2859
2949
|
let signature = byteStringToUint8Array(atob(hash));
|
|
2860
2950
|
return await crypto.subtle.verify("HMAC", key, signature, data) ? value : false;
|
|
2861
|
-
} catch
|
|
2951
|
+
} catch {
|
|
2862
2952
|
return false;
|
|
2863
2953
|
}
|
|
2864
2954
|
};
|
|
@@ -2875,6 +2965,14 @@ function byteStringToUint8Array(byteString) {
|
|
|
2875
2965
|
//#region lib/server-runtime/cookies.ts
|
|
2876
2966
|
/**
|
|
2877
2967
|
* Creates a logical container for managing a browser cookie from the server.
|
|
2968
|
+
*
|
|
2969
|
+
* @public
|
|
2970
|
+
* @category Utils
|
|
2971
|
+
* @mode framework
|
|
2972
|
+
* @mode data
|
|
2973
|
+
* @param name The name of the cookie.
|
|
2974
|
+
* @param cookieOptions Options for parsing and serializing the cookie.
|
|
2975
|
+
* @returns A {@link Cookie} object for parsing and serializing the cookie.
|
|
2878
2976
|
*/
|
|
2879
2977
|
const createCookie = (name, cookieOptions = {}) => {
|
|
2880
2978
|
let { secrets = [], ...options } = {
|
|
@@ -2914,9 +3012,15 @@ const createCookie = (name, cookieOptions = {}) => {
|
|
|
2914
3012
|
};
|
|
2915
3013
|
};
|
|
2916
3014
|
/**
|
|
2917
|
-
* Returns true if
|
|
3015
|
+
* Returns `true` if a value is a React Router {@link Cookie} object.
|
|
2918
3016
|
*
|
|
2919
|
-
* @
|
|
3017
|
+
* @public
|
|
3018
|
+
* @category Utils
|
|
3019
|
+
* @mode framework
|
|
3020
|
+
* @mode data
|
|
3021
|
+
* @param object The value to check.
|
|
3022
|
+
* @returns `true` if the value is a React Router {@link Cookie} object;
|
|
3023
|
+
* otherwise, `false`.
|
|
2920
3024
|
*/
|
|
2921
3025
|
const isCookie = (object) => {
|
|
2922
3026
|
return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
|
|
@@ -2942,7 +3046,7 @@ function encodeData(value) {
|
|
|
2942
3046
|
function decodeData(value) {
|
|
2943
3047
|
try {
|
|
2944
3048
|
return JSON.parse(decodeURIComponent(myEscape(atob(value))));
|
|
2945
|
-
} catch
|
|
3049
|
+
} catch {
|
|
2946
3050
|
return {};
|
|
2947
3051
|
}
|
|
2948
3052
|
}
|
|
@@ -3006,6 +3110,12 @@ function flash(name) {
|
|
|
3006
3110
|
*
|
|
3007
3111
|
* Note: This function is typically not invoked directly by application code.
|
|
3008
3112
|
* Instead, use a `SessionStorage` object's `getSession` method.
|
|
3113
|
+
*
|
|
3114
|
+
* @category Utils
|
|
3115
|
+
* @param initialData The initial data for the session.
|
|
3116
|
+
* @param id The identifier for the session. Defaults to an empty string for a
|
|
3117
|
+
* new session.
|
|
3118
|
+
* @returns A new {@link Session} object.
|
|
3009
3119
|
*/
|
|
3010
3120
|
const createSession = (initialData = {}, id = "") => {
|
|
3011
3121
|
let map = new Map(Object.entries(initialData));
|
|
@@ -3040,9 +3150,15 @@ const createSession = (initialData = {}, id = "") => {
|
|
|
3040
3150
|
};
|
|
3041
3151
|
};
|
|
3042
3152
|
/**
|
|
3043
|
-
* Returns true if
|
|
3153
|
+
* Returns `true` if a value is a React Router {@link Session} object.
|
|
3044
3154
|
*
|
|
3045
|
-
* @
|
|
3155
|
+
* @public
|
|
3156
|
+
* @category Utils
|
|
3157
|
+
* @mode framework
|
|
3158
|
+
* @mode data
|
|
3159
|
+
* @param object The value to check.
|
|
3160
|
+
* @returns `true` if the value is a React Router {@link Session} object;
|
|
3161
|
+
* otherwise, `false`.
|
|
3046
3162
|
*/
|
|
3047
3163
|
const isSession = (object) => {
|
|
3048
3164
|
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";
|
|
@@ -3052,6 +3168,11 @@ const isSession = (object) => {
|
|
|
3052
3168
|
*
|
|
3053
3169
|
* Note: This is a low-level API that should only be used if none of the
|
|
3054
3170
|
* existing session storage options meet your requirements.
|
|
3171
|
+
*
|
|
3172
|
+
* @category Utils
|
|
3173
|
+
* @param strategy The strategy used to store session identifiers and data.
|
|
3174
|
+
* @returns A {@link SessionStorage} object that persists session data using the
|
|
3175
|
+
* provided strategy.
|
|
3055
3176
|
*/
|
|
3056
3177
|
function createSessionStorage({ cookie: cookieArg, createData, readData, updateData, deleteData }) {
|
|
3057
3178
|
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
|
|
@@ -3091,6 +3212,14 @@ function warnOnceAboutSigningSessionCookie(cookie) {
|
|
|
3091
3212
|
* needed, and can help to simplify some load-balanced scenarios. However, it
|
|
3092
3213
|
* also has the limitation that serialized session data may not exceed the
|
|
3093
3214
|
* browser's maximum cookie size. Trade-offs!
|
|
3215
|
+
*
|
|
3216
|
+
* @public
|
|
3217
|
+
* @category Utils
|
|
3218
|
+
* @mode framework
|
|
3219
|
+
* @mode data
|
|
3220
|
+
* @param options Options for creating the cookie-backed session storage.
|
|
3221
|
+
* @returns A {@link SessionStorage} object that stores all session data in its
|
|
3222
|
+
* cookie.
|
|
3094
3223
|
*/
|
|
3095
3224
|
function createCookieSessionStorage({ cookie: cookieArg } = {}) {
|
|
3096
3225
|
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
|
|
@@ -3116,18 +3245,24 @@ function createCookieSessionStorage({ cookie: cookieArg } = {}) {
|
|
|
3116
3245
|
//#endregion
|
|
3117
3246
|
//#region lib/server-runtime/sessions/memoryStorage.ts
|
|
3118
3247
|
/**
|
|
3119
|
-
* Creates and returns a simple in-memory SessionStorage object
|
|
3120
|
-
*
|
|
3248
|
+
* Creates and returns a simple in-memory SessionStorage object.
|
|
3249
|
+
*
|
|
3250
|
+
* Intended for local development and testing. It does not scale beyond a single
|
|
3251
|
+
* process, and all session data is lost when the server process stops/restarts.
|
|
3121
3252
|
*
|
|
3122
|
-
*
|
|
3123
|
-
*
|
|
3253
|
+
* @public
|
|
3254
|
+
* @category Utils
|
|
3255
|
+
* @mode framework
|
|
3256
|
+
* @mode data
|
|
3257
|
+
* @param options Options for creating the in-memory session storage.
|
|
3258
|
+
* @returns A {@link SessionStorage} object that stores session data in memory.
|
|
3124
3259
|
*/
|
|
3125
3260
|
function createMemorySessionStorage({ cookie } = {}) {
|
|
3126
3261
|
let map = /* @__PURE__ */ new Map();
|
|
3127
3262
|
return createSessionStorage({
|
|
3128
3263
|
cookie,
|
|
3129
3264
|
async createData(data, expires) {
|
|
3130
|
-
let id =
|
|
3265
|
+
let id = crypto.randomUUID();
|
|
3131
3266
|
map.set(id, {
|
|
3132
3267
|
data,
|
|
3133
3268
|
expires
|
package/dist/production/index.js
CHANGED
|
@@ -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 };
|