UNPKG

24.2 kB JavaScript View Raw
1/**
2 * react-router v8.3.1
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import { PROTOCOL_RELATIVE_URL_REGEX } from "../router/url.js";
12import { createBrowserHistory, createPath, invariant } from "../router/history.js";
13import { ErrorResponseImpl, createContext, resolvePath } from "../router/utils.js";
14import { validateNavigationTarget } from "../router/navigation.js";
15import { createRouter, hasInvalidProtocol, isMutationMethod } from "../router/router.js";
16import { RSCRouterContext } from "../context.js";
17import { RouterProvider } from "../components.js";
18import { createRequestInit } from "../dom/ssr/data.js";
19import { getSingleFetchDataStrategyImpl, singleFetchUrl, stripIndexParam } from "../dom/ssr/single-fetch.js";
20import { noActionDefinedError, shouldHydrateRouteLoader } from "../dom/ssr/routes.js";
21import { getPathsWithAncestors, handleClientVersionMismatch } from "../dom/ssr/fog-of-war.js";
22import { FrameworkContext, setIsHydrated } from "../dom/ssr/components.js";
23import { RSCRouterGlobalErrorBoundary } from "./errorBoundaries.js";
24import { populateRSCRouteModules } from "./route-modules.js";
25import { getHydrationData } from "../dom/ssr/hydration.js";
26import * as React$1 from "react";
27import * as ReactDOM from "react-dom";
28//#region lib/rsc/browser.tsx
29const defaultManifestPath = "/__manifest";
30/**
31* Create a React `callServer` implementation for React Router.
32*
33* @example
34* import {
35* createFromReadableStream,
36* createTemporaryReferenceSet,
37* encodeReply,
38* setServerCallback,
39* } from "@vitejs/plugin-rsc/browser";
40* import { unstable_createCallServer as createCallServer } from "react-router";
41*
42* setServerCallback(
43* createCallServer({
44* createFromReadableStream,
45* createTemporaryReferenceSet,
46* encodeReply,
47* })
48* );
49*
50* @name unstable_createCallServer
51* @public
52* @category RSC
53* @mode data
54* @param opts Options
55* @param opts.createFromReadableStream Your `react-server-dom-xyz/client`'s
56* `createFromReadableStream`. Used to decode payloads from the server.
57* @param opts.createTemporaryReferenceSet A function that creates a temporary
58* reference set for the [RSC](https://react.dev/reference/rsc/server-components)
59* payload.
60* @param opts.encodeReply Your `react-server-dom-xyz/client`'s `encodeReply`.
61* Used when sending payloads to the server.
62* @param opts.fetch Optional [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
63* implementation. Defaults to global [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch).
64* @returns A function that can be used to call server actions.
65*/
66function createCallServer({ createFromReadableStream, createTemporaryReferenceSet, encodeReply, fetch: fetchImplementation = fetch }) {
67 const globalVar = window;
68 let landedActionId = 0;
69 return async (id, args) => {
70 let actionId = globalVar.__routerActionID = (globalVar.__routerActionID ??= 0) + 1;
71 const temporaryReferences = createTemporaryReferenceSet();
72 const payloadPromise = fetchImplementation(new Request(location.href, {
73 body: await encodeReply(args, { temporaryReferences }),
74 method: "POST",
75 headers: {
76 Accept: "text/x-component",
77 "rsc-action-id": id
78 }
79 })).then((response) => {
80 if (!response.body) throw new Error("No response body");
81 return createFromReadableStream(response.body, { temporaryReferences });
82 });
83 React$1.startTransition(() => Promise.resolve(payloadPromise).then(async (payload) => {
84 if (payload.type === "redirect") {
85 let location = normalizeRedirectLocation(payload.location);
86 validateNavigationTarget(payload.location, location, new URL(window.location.href), "allow-explicit");
87 if (payload.reload || isExternalLocation(location)) {
88 if (hasInvalidProtocol(location)) throw new Error("Invalid redirect location");
89 window.location.href = location;
90 return;
91 }
92 React$1.startTransition(() => {
93 globalVar.__reactRouterDataRouter.navigate(location, { replace: payload.replace });
94 });
95 return;
96 }
97 if (payload.type !== "action") throw new Error("Unexpected payload type");
98 const rerender = await payload.rerender;
99 if (rerender && landedActionId < actionId && globalVar.__routerActionID <= actionId) {
100 if (rerender.type === "redirect") {
101 let location = normalizeRedirectLocation(rerender.location);
102 validateNavigationTarget(rerender.location, location, new URL(window.location.href), "allow-explicit");
103 if (rerender.reload || isExternalLocation(location)) {
104 if (hasInvalidProtocol(location)) throw new Error("Invalid redirect location");
105 window.location.href = location;
106 return;
107 }
108 React$1.startTransition(() => {
109 globalVar.__reactRouterDataRouter.navigate(location, { replace: rerender.replace });
110 });
111 return;
112 }
113 React$1.startTransition(() => {
114 let lastMatch;
115 for (const match of rerender.matches) {
116 globalVar.__reactRouterDataRouter.patchRoutes(lastMatch?.id ?? null, [createRouteFromServerManifest(match)], true);
117 lastMatch = match;
118 }
119 window.__reactRouterDataRouter._internalSetStateDoNotUseOrYouWillBreakYourApp({
120 loaderData: Object.assign({}, globalVar.__reactRouterDataRouter.state.loaderData, rerender.loaderData),
121 errors: rerender.errors ? Object.assign({}, globalVar.__reactRouterDataRouter.state.errors, rerender.errors) : null
122 });
123 });
124 }
125 }).catch(() => {}));
126 return payloadPromise.then((payload) => {
127 if (payload.type !== "action" && payload.type !== "redirect") throw new Error("Unexpected payload type");
128 return payload.actionResult;
129 });
130 };
131}
132function createRouterFromPayload({ fetchImplementation, createFromReadableStream, getContext, payload }) {
133 const globalVar = window;
134 if (globalVar.__reactRouterDataRouter && globalVar.__reactRouterRouteModules) return {
135 router: globalVar.__reactRouterDataRouter,
136 routeModules: globalVar.__reactRouterRouteModules
137 };
138 if (payload.type !== "render") throw new Error("Invalid payload type");
139 let { clientVersion } = payload;
140 globalVar.__reactRouterRouteModules = globalVar.__reactRouterRouteModules ?? {};
141 populateRSCRouteModules(globalVar.__reactRouterRouteModules, payload.matches);
142 let routes = payload.matches.reduceRight((previous, match) => {
143 const route = createRouteFromServerManifest(match, payload);
144 if (previous.length > 0) route.children = previous;
145 else if (!route.index) route.children = [];
146 return [route];
147 }, []);
148 let applyPatchesPromise;
149 globalVar.__reactRouterDataRouter = createRouter({
150 routes,
151 getContext,
152 basename: payload.basename,
153 history: createBrowserHistory(),
154 hydrationData: getHydrationData({
155 state: {
156 loaderData: payload.loaderData,
157 actionData: payload.actionData,
158 errors: payload.errors
159 },
160 routes,
161 getRouteInfo: (routeId) => {
162 let match = payload.matches.find((m) => m.id === routeId);
163 invariant(match, "Route not found in payload");
164 return {
165 clientLoader: match.clientLoader,
166 hasLoader: match.hasLoader,
167 hasHydrateFallback: match.hydrateFallbackElement != null
168 };
169 },
170 location: payload.location,
171 basename: payload.basename,
172 isSpaMode: false
173 }),
174 async patchRoutesOnNavigation({ path, signal, fetcherKey }) {
175 if (payload.routeDiscovery.mode === "initial") {
176 if (!applyPatchesPromise) applyPatchesPromise = (async () => {
177 if (!payload.patches) return;
178 let patches = await payload.patches;
179 React$1.startTransition(() => {
180 patches.forEach((p) => {
181 window.__reactRouterDataRouter.patchRoutes(p.parentId ?? null, [createRouteFromServerManifest(p)]);
182 });
183 });
184 })();
185 await applyPatchesPromise;
186 return;
187 }
188 if (discoveredPaths.has(path)) return;
189 let { state } = globalVar.__reactRouterDataRouter;
190 await fetchAndApplyManifestPatches([path], createFromReadableStream, fetchImplementation, clientVersion, fetcherKey ? window.location.href : createPath(state.navigation.location || state.location), signal);
191 },
192 dataStrategy: getRSCSingleFetchDataStrategy(() => globalVar.__reactRouterDataRouter, true, createFromReadableStream, fetchImplementation, clientVersion)
193 });
194 if (globalVar.__reactRouterDataRouter.state.initialized) {
195 globalVar.__routerInitialized = true;
196 globalVar.__reactRouterDataRouter.initialize();
197 } else globalVar.__routerInitialized = false;
198 let lastLoaderData = void 0;
199 globalVar.__reactRouterDataRouter.subscribe(({ loaderData, actionData }) => {
200 if (lastLoaderData !== loaderData) globalVar.__routerActionID = (globalVar.__routerActionID ??= 0) + 1;
201 });
202 globalVar.__reactRouterDataRouter._updateRoutesForHMR = (routeUpdateByRouteId) => {
203 const oldRoutes = window.__reactRouterDataRouter.routes;
204 const newRoutes = [];
205 function walkRoutes(routes, parentId) {
206 return routes.map((route) => {
207 const routeUpdate = routeUpdateByRouteId.get(route.id);
208 if (routeUpdate) {
209 const { routeModule, hasAction, hasComponent, hasLoader } = routeUpdate;
210 const newRoute = createRouteFromServerManifest({
211 clientAction: routeModule.clientAction,
212 clientLoader: routeModule.clientLoader,
213 element: route.element,
214 errorElement: route.errorElement,
215 handle: route.handle,
216 hasAction,
217 hasComponent,
218 hasLoader,
219 hydrateFallbackElement: route.hydrateFallbackElement,
220 id: route.id,
221 index: route.index,
222 links: routeModule.links,
223 meta: routeModule.meta,
224 parentId,
225 path: route.path,
226 shouldRevalidate: routeModule.shouldRevalidate
227 });
228 if (route.children) newRoute.children = walkRoutes(route.children, route.id);
229 return newRoute;
230 }
231 const updatedRoute = { ...route };
232 if (route.children) updatedRoute.children = walkRoutes(route.children, route.id);
233 return updatedRoute;
234 });
235 }
236 newRoutes.push(...walkRoutes(oldRoutes, void 0));
237 window.__reactRouterDataRouter._internalSetRoutes(newRoutes);
238 };
239 return {
240 router: globalVar.__reactRouterDataRouter,
241 routeModules: globalVar.__reactRouterRouteModules
242 };
243}
244const renderedRoutesContext = createContext();
245function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream, fetchImplementation, clientVersion) {
246 let dataStrategy = getSingleFetchDataStrategyImpl(getRouter, (match) => {
247 let M = match;
248 return {
249 hasLoader: M.route.hasLoader,
250 hasClientLoader: M.route.hasClientLoader
251 };
252 }, getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion), ssr, (match) => {
253 let M = match;
254 return !M.route.hasComponent || M.route.element != null;
255 });
256 return async (args) => args.runClientMiddleware(async () => {
257 args.context.set(renderedRoutesContext, []);
258 let results = await dataStrategy(args);
259 const renderedRoutesById = /* @__PURE__ */ new Map();
260 for (const route of args.context.get(renderedRoutesContext)) {
261 if (!renderedRoutesById.has(route.id)) renderedRoutesById.set(route.id, []);
262 renderedRoutesById.get(route.id).push(route);
263 }
264 React$1.startTransition(() => {
265 for (const match of args.matches) {
266 const renderedRoutes = renderedRoutesById.get(match.route.id);
267 if (renderedRoutes) for (const rendered of renderedRoutes) window.__reactRouterDataRouter.patchRoutes(rendered.parentId ?? null, [createRouteFromServerManifest(rendered)], true);
268 }
269 });
270 return results;
271 });
272}
273function getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion) {
274 return async (args, targetRoutes) => {
275 let { request, context } = args;
276 let url = singleFetchUrl(request.url, "rsc");
277 if (request.method === "GET") {
278 url = stripIndexParam(url);
279 if (targetRoutes) url.searchParams.set("_routes", targetRoutes.join(","));
280 }
281 let res = await fetchImplementation(new Request(url, await createRequestInit(request)));
282 if (res.status >= 400 && !res.headers.has("X-Remix-Response")) throw new ErrorResponseImpl(res.status, res.statusText, await res.text());
283 invariant(res.body, "No response body to decode");
284 try {
285 const payload = await createFromReadableStream(res.body, { temporaryReferences: void 0 });
286 if (payload.type === "redirect") return {
287 status: res.status,
288 data: { redirect: {
289 redirect: payload.location,
290 reload: payload.reload,
291 replace: payload.replace,
292 revalidate: false,
293 status: payload.status
294 } }
295 };
296 if (payload.type !== "render") throw new Error("Unexpected payload type");
297 if (clientVersion !== void 0 && await handleClientVersionMismatch(payload.clientVersion !== clientVersion, clientVersion, createPath(getRouter().state.navigation.location || getRouter().state.location))) return new Promise(() => {});
298 context.get(renderedRoutesContext).push(...payload.matches);
299 let results = { routes: {} };
300 const dataKey = isMutationMethod(request.method) ? "actionData" : "loaderData";
301 for (let [routeId, data] of Object.entries(payload[dataKey] || {})) results.routes[routeId] = { data };
302 if (payload.errors) for (let [routeId, error] of Object.entries(payload.errors)) results.routes[routeId] = { error };
303 return {
304 status: res.status,
305 data: results
306 };
307 } catch (cause) {
308 throw new Error("Unable to decode RSC response", { cause });
309 }
310 };
311}
312/**
313* Hydrates a server rendered {@link unstable_RSCPayload} in the browser.
314*
315* @example
316* import { startTransition, StrictMode } from "react";
317* import { hydrateRoot } from "react-dom/client";
318* import {
319* unstable_getRSCStream as getRSCStream,
320* unstable_RSCHydratedRouter as RSCHydratedRouter,
321* } from "react-router";
322* import type { unstable_RSCPayload as RSCPayload } from "react-router";
323*
324* createFromReadableStream(getRSCStream()).then((payload) =>
325* startTransition(async () => {
326* hydrateRoot(
327* document,
328* <StrictMode>
329* <RSCHydratedRouter
330* createFromReadableStream={createFromReadableStream}
331* payload={payload}
332* />
333* </StrictMode>,
334* { formState: await getFormState(payload) },
335* );
336* }),
337* );
338*
339* @name unstable_RSCHydratedRouter
340* @public
341* @category RSC
342* @mode data
343* @param props Props
344* @param {unstable_RSCHydratedRouterProps.createFromReadableStream} props.createFromReadableStream n/a
345* @param {unstable_RSCHydratedRouterProps.fetch} props.fetch n/a
346* @param {unstable_RSCHydratedRouterProps.getContext} props.getContext n/a
347* @param {unstable_RSCHydratedRouterProps.payload} props.payload n/a
348* @returns A hydrated {@link DataRouter} that can be used to navigate and
349* render routes.
350*/
351function RSCHydratedRouter({ createFromReadableStream, fetch: fetchImplementation = fetch, payload, getContext }) {
352 if (payload.type !== "render") throw new Error("Invalid payload type");
353 let { routeDiscovery, clientVersion } = payload;
354 let { router, routeModules } = React$1.useMemo(() => createRouterFromPayload({
355 payload,
356 fetchImplementation,
357 getContext,
358 createFromReadableStream
359 }), [
360 createFromReadableStream,
361 payload,
362 fetchImplementation,
363 getContext
364 ]);
365 React$1.useEffect(() => {
366 setIsHydrated();
367 }, []);
368 React$1.useLayoutEffect(() => {
369 const globalVar = window;
370 if (!globalVar.__routerInitialized) {
371 globalVar.__routerInitialized = true;
372 globalVar.__reactRouterDataRouter.initialize();
373 }
374 }, []);
375 let [{ routes, state }, setState] = React$1.useState(() => ({
376 routes: cloneRoutes(router.routes),
377 state: router.state
378 }));
379 React$1.useLayoutEffect(() => router.subscribe((newState) => {
380 if (diffRoutes(router.routes, routes)) React$1.startTransition(() => {
381 setState({
382 routes: cloneRoutes(router.routes),
383 state: newState
384 });
385 });
386 }), [
387 router.subscribe,
388 routes,
389 router
390 ]);
391 const transitionEnabledRouter = React$1.useMemo(() => ({
392 ...router,
393 state,
394 routes
395 }), [
396 router,
397 routes,
398 state
399 ]);
400 React$1.useEffect(() => {
401 if (routeDiscovery.mode === "initial" || window.navigator?.connection?.saveData === true) return;
402 function registerElement(el) {
403 let path = el.tagName === "FORM" ? el.getAttribute("action") : el.getAttribute("href");
404 if (!path) return;
405 let pathname = el.tagName === "A" ? el.pathname : new URL(path, window.location.origin).pathname;
406 if (!discoveredPaths.has(pathname)) nextPaths.add(pathname);
407 }
408 async function fetchPatches() {
409 document.querySelectorAll("a[data-discover], form[data-discover]").forEach(registerElement);
410 let paths = Array.from(nextPaths.keys()).filter((path) => {
411 if (discoveredPaths.has(path)) {
412 nextPaths.delete(path);
413 return false;
414 }
415 return true;
416 });
417 if (paths.length === 0) return;
418 try {
419 await fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, null);
420 } catch (e) {
421 console.error("Failed to fetch manifest patches", e);
422 }
423 }
424 let debouncedFetchPatches = debounce(fetchPatches, 100);
425 fetchPatches();
426 new MutationObserver(() => debouncedFetchPatches()).observe(document.documentElement, {
427 subtree: true,
428 childList: true,
429 attributes: true,
430 attributeFilter: [
431 "data-discover",
432 "href",
433 "action"
434 ]
435 });
436 }, [
437 routeDiscovery,
438 createFromReadableStream,
439 fetchImplementation,
440 clientVersion
441 ]);
442 const frameworkContext = {
443 future: {},
444 isSpaMode: false,
445 ssr: true,
446 criticalCss: "",
447 manifest: {
448 routes: {},
449 version: "1",
450 url: "",
451 entry: {
452 module: "",
453 imports: []
454 }
455 },
456 routeDiscovery: payload.routeDiscovery.mode === "initial" ? {
457 mode: "initial",
458 manifestPath: defaultManifestPath
459 } : {
460 mode: "lazy",
461 manifestPath: payload.routeDiscovery.manifestPath || defaultManifestPath
462 },
463 routeModules
464 };
465 return /* @__PURE__ */ React$1.createElement(RSCRouterContext.Provider, { value: true }, /* @__PURE__ */ React$1.createElement(RSCRouterGlobalErrorBoundary, { location: state.location }, /* @__PURE__ */ React$1.createElement(FrameworkContext.Provider, { value: frameworkContext }, /* @__PURE__ */ React$1.createElement(RouterProvider, {
466 router: transitionEnabledRouter,
467 flushSync: ReactDOM.flushSync
468 }))));
469}
470function createRouteFromServerManifest(match, payload) {
471 let hasInitialData = payload && match.id in payload.loaderData;
472 let initialData = payload?.loaderData[match.id];
473 let hasInitialError = payload?.errors && match.id in payload.errors;
474 let initialError = payload?.errors?.[match.id];
475 let isHydrationRequest = match.clientLoader?.hydrate === true || !match.hasLoader || match.hasComponent && !match.element;
476 invariant(window.__reactRouterRouteModules);
477 populateRSCRouteModules(window.__reactRouterRouteModules, match);
478 let dataRoute = {
479 id: match.id,
480 element: match.element,
481 errorElement: match.errorElement,
482 handle: match.handle,
483 hydrateFallbackElement: match.hydrateFallbackElement,
484 index: match.index,
485 loader: match.clientLoader ? async (args, singleFetch) => {
486 let _isHydrationRequest = isHydrationRequest;
487 isHydrationRequest = false;
488 return await match.clientLoader({
489 ...args,
490 serverLoader: () => {
491 preventInvalidServerHandlerCall("loader", match.id, match.hasLoader);
492 if (_isHydrationRequest) {
493 if (hasInitialData) return initialData;
494 if (hasInitialError) throw initialError;
495 }
496 return callSingleFetch(singleFetch);
497 }
498 });
499 } : (_, singleFetch) => callSingleFetch(singleFetch),
500 action: match.clientAction ? (args, singleFetch) => match.clientAction({
501 ...args,
502 serverAction: async () => {
503 preventInvalidServerHandlerCall("action", match.id, match.hasLoader);
504 return await callSingleFetch(singleFetch);
505 }
506 }) : match.hasAction ? (_, singleFetch) => callSingleFetch(singleFetch) : () => {
507 throw noActionDefinedError("action", match.id);
508 },
509 path: match.path,
510 shouldRevalidate: match.shouldRevalidate,
511 hasLoader: true,
512 hasClientLoader: match.clientLoader != null,
513 hasComponent: match.hasComponent,
514 hasAction: match.hasAction,
515 hasClientAction: match.clientAction != null
516 };
517 if (typeof dataRoute.loader === "function") dataRoute.loader.hydrate = shouldHydrateRouteLoader(match.id, match.clientLoader, match.hasLoader, false);
518 return dataRoute;
519}
520function callSingleFetch(singleFetch) {
521 invariant(typeof singleFetch === "function", "Invalid singleFetch parameter");
522 return singleFetch();
523}
524function preventInvalidServerHandlerCall(type, routeId, hasHandler) {
525 if (!hasHandler) {
526 let msg = `You are trying to call ${type === "action" ? "serverAction()" : "serverLoader()"} on a route that does not have a server ${type} (routeId: "${routeId}")`;
527 console.error(msg);
528 throw new ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
529 }
530}
531const nextPaths = /* @__PURE__ */ new Set();
532const discoveredPathsMaxSize = 1e3;
533const discoveredPaths = /* @__PURE__ */ new Set();
534function getManifestUrl(paths, clientVersion) {
535 if (paths.length === 0) return null;
536 let url;
537 if (paths.length === 1) url = new URL(`${paths[0]}.manifest`, window.location.origin);
538 else {
539 let basename = (window.__reactRouterDataRouter.basename ?? "").replace(/^\/|\/$/g, "");
540 url = new URL(`${basename}/.manifest`, window.location.origin);
541 url.searchParams.set("paths", paths.sort().join(","));
542 }
543 if (clientVersion !== void 0) url.searchParams.set("version", clientVersion);
544 return url;
545}
546async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, errorReloadPath, signal) {
547 paths = getPathsWithAncestors(paths);
548 let url = getManifestUrl(paths, clientVersion);
549 if (url == null) return;
550 if (url.toString().length > 7680) {
551 nextPaths.clear();
552 return;
553 }
554 let response = await fetchImplementation(new Request(url, { signal }));
555 if (clientVersion !== void 0 && response.status === 204 && response.headers.has("X-Remix-Reload-Document")) {
556 await handleClientVersionMismatch(true, clientVersion, errorReloadPath);
557 return;
558 }
559 if (!response.body || response.status < 200 || response.status >= 300) throw new Error("Unable to fetch new route matches from the server");
560 let payload = await createFromReadableStream(response.body, { temporaryReferences: void 0 });
561 if (payload.type !== "manifest") throw new Error("Failed to patch routes");
562 paths.forEach((p) => addToFifoQueue(p, discoveredPaths));
563 let patches = await payload.patches;
564 React$1.startTransition(() => {
565 patches.forEach((p) => {
566 window.__reactRouterDataRouter.patchRoutes(p.parentId ?? null, [createRouteFromServerManifest(p)]);
567 });
568 });
569}
570function addToFifoQueue(path, queue) {
571 if (queue.size >= discoveredPathsMaxSize) {
572 let first = queue.values().next().value;
573 if (typeof first === "string") queue.delete(first);
574 }
575 queue.add(path);
576}
577function debounce(callback, wait) {
578 let timeoutId;
579 return (...args) => {
580 window.clearTimeout(timeoutId);
581 timeoutId = window.setTimeout(() => callback(...args), wait);
582 };
583}
584function isExternalLocation(location) {
585 return new URL(location, window.location.href).origin !== window.location.origin;
586}
587function normalizeRedirectLocation(location) {
588 if (PROTOCOL_RELATIVE_URL_REGEX.test(location)) {
589 let path = resolvePath(location);
590 return path.pathname + path.search + path.hash;
591 }
592 return location;
593}
594function cloneRoutes(routes) {
595 if (!routes) return void 0;
596 return routes.map((route) => ({
597 ...route,
598 children: cloneRoutes(route.children)
599 }));
600}
601function diffRoutes(a, b) {
602 if (a.length !== b.length) return true;
603 return a.some((route, index) => {
604 if (route.element !== b[index].element) return true;
605 if (route.errorElement !== b[index].errorElement) return true;
606 if (route.hydrateFallbackElement !== b[index].hydrateFallbackElement) return true;
607 if (route.hasLoader !== b[index].hasLoader) return true;
608 if (route.hasClientLoader !== b[index].hasClientLoader) return true;
609 if (route.hasAction !== b[index].hasAction) return true;
610 if (route.hasClientAction !== b[index].hasClientAction) return true;
611 return diffRoutes(route.children || [], b[index].children || []);
612 });
613}
614//#endregion
615export { RSCHydratedRouter, createCallServer };