UNPKG

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