UNPKG

24.2 kBJavaScriptView Raw
1/**
2 * react-router v8.4.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 { 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 future: {},
173 isSpaMode: false
174 }),
175 async patchRoutesOnNavigation({ path, signal, fetcherKey }) {
176 if (payload.routeDiscovery.mode === "initial") {
177 if (!applyPatchesPromise) applyPatchesPromise = (async () => {
178 if (!payload.patches) return;
179 let patches = await payload.patches;
180 React$1.startTransition(() => {
181 patches.forEach((p) => {
182 window.__reactRouterDataRouter.patchRoutes(p.parentId ?? null, [createRouteFromServerManifest(p)]);
183 });
184 });
185 })();
186 await applyPatchesPromise;
187 return;
188 }
189 if (discoveredPaths.has(path)) return;
190 let { state } = globalVar.__reactRouterDataRouter;
191 await fetchAndApplyManifestPatches([path], createFromReadableStream, fetchImplementation, clientVersion, fetcherKey ? window.location.href : createPath(state.navigation.location || state.location), signal);
192 },
193 dataStrategy: getRSCSingleFetchDataStrategy(() => globalVar.__reactRouterDataRouter, true, createFromReadableStream, fetchImplementation, clientVersion)
194 });
195 if (globalVar.__reactRouterDataRouter.state.initialized) {
196 globalVar.__routerInitialized = true;
197 globalVar.__reactRouterDataRouter.initialize();
198 } else globalVar.__routerInitialized = false;
199 let lastLoaderData = void 0;
200 globalVar.__reactRouterDataRouter.subscribe(({ loaderData, actionData }) => {
201 if (lastLoaderData !== loaderData) globalVar.__routerActionID = (globalVar.__routerActionID ??= 0) + 1;
202 });
203 globalVar.__reactRouterDataRouter._updateRoutesForHMR = (routeUpdateByRouteId) => {
204 const oldRoutes = window.__reactRouterDataRouter.routes;
205 const newRoutes = [];
206 function walkRoutes(routes, parentId) {
207 return routes.map((route) => {
208 const routeUpdate = routeUpdateByRouteId.get(route.id);
209 if (routeUpdate) {
210 const { routeModule, hasAction, hasComponent, hasLoader } = routeUpdate;
211 const newRoute = createRouteFromServerManifest({
212 clientAction: routeModule.clientAction,
213 clientLoader: routeModule.clientLoader,
214 element: route.element,
215 errorElement: route.errorElement,
216 handle: route.handle,
217 hasAction,
218 hasComponent,
219 hasLoader,
220 hydrateFallbackElement: route.hydrateFallbackElement,
221 id: route.id,
222 index: route.index,
223 links: routeModule.links,
224 meta: routeModule.meta,
225 parentId,
226 path: route.path,
227 shouldRevalidate: routeModule.shouldRevalidate
228 });
229 if (route.children) newRoute.children = walkRoutes(route.children, route.id);
230 return newRoute;
231 }
232 const updatedRoute = { ...route };
233 if (route.children) updatedRoute.children = walkRoutes(route.children, route.id);
234 return updatedRoute;
235 });
236 }
237 newRoutes.push(...walkRoutes(oldRoutes, void 0));
238 window.__reactRouterDataRouter._internalSetRoutes(newRoutes);
239 };
240 return {
241 router: globalVar.__reactRouterDataRouter,
242 routeModules: globalVar.__reactRouterRouteModules
243 };
244}
245const renderedRoutesContext = createContext();
246function getRSCSingleFetchDataStrategy(getRouter, ssr, createFromReadableStream, fetchImplementation, clientVersion) {
247 let dataStrategy = getSingleFetchDataStrategyImpl(getRouter, (match) => {
248 let M = match;
249 return {
250 hasLoader: M.route.hasLoader,
251 hasClientLoader: M.route.hasClientLoader
252 };
253 }, getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion), ssr, (match) => {
254 let M = match;
255 return !M.route.hasComponent || M.route.element != null;
256 });
257 return async (args) => args.runClientMiddleware(async () => {
258 args.context.set(renderedRoutesContext, []);
259 let results = await dataStrategy(args);
260 const renderedRoutesById = /* @__PURE__ */ new Map();
261 for (const route of args.context.get(renderedRoutesContext)) {
262 if (!renderedRoutesById.has(route.id)) renderedRoutesById.set(route.id, []);
263 renderedRoutesById.get(route.id).push(route);
264 }
265 React$1.startTransition(() => {
266 for (const match of args.matches) {
267 const renderedRoutes = renderedRoutesById.get(match.route.id);
268 if (renderedRoutes) for (const rendered of renderedRoutes) window.__reactRouterDataRouter.patchRoutes(rendered.parentId ?? null, [createRouteFromServerManifest(rendered)], true);
269 }
270 });
271 return results;
272 });
273}
274function getFetchAndDecodeViaRSC(getRouter, createFromReadableStream, fetchImplementation, clientVersion) {
275 return async (args, targetRoutes) => {
276 let { request, context } = args;
277 let url = singleFetchUrl(request.url, "rsc");
278 if (request.method === "GET") {
279 url = stripIndexParam(url);
280 if (targetRoutes) url.searchParams.set("_routes", targetRoutes.join(","));
281 }
282 let res = await fetchImplementation(new Request(url, await createRequestInit(request)));
283 if (res.status >= 400 && !res.headers.has("X-Remix-Response")) throw new ErrorResponseImpl(res.status, res.statusText, await res.text());
284 invariant(res.body, "No response body to decode");
285 try {
286 const payload = await createFromReadableStream(res.body, { temporaryReferences: void 0 });
287 if (payload.type === "redirect") return {
288 status: res.status,
289 data: { redirect: {
290 redirect: payload.location,
291 reload: payload.reload,
292 replace: payload.replace,
293 revalidate: false,
294 status: payload.status
295 } }
296 };
297 if (payload.type !== "render") throw new Error("Unexpected payload type");
298 if (clientVersion !== void 0 && await handleClientVersionMismatch(payload.clientVersion !== clientVersion, clientVersion, createPath(getRouter().state.navigation.location || getRouter().state.location))) return new Promise(() => {});
299 context.get(renderedRoutesContext).push(...payload.matches);
300 let results = { routes: {} };
301 const dataKey = isMutationMethod(request.method) ? "actionData" : "loaderData";
302 for (let [routeId, data] of Object.entries(payload[dataKey] || {})) results.routes[routeId] = { data };
303 if (payload.errors) for (let [routeId, error] of Object.entries(payload.errors)) results.routes[routeId] = { error };
304 return {
305 status: res.status,
306 data: results
307 };
308 } catch (cause) {
309 throw new Error("Unable to decode RSC response", { cause });
310 }
311 };
312}
313/**
314* Hydrates a server rendered {@link unstable_RSCPayload} in the browser.
315*
316* @example
317* import { startTransition, StrictMode } from "react";
318* import { hydrateRoot } from "react-dom/client";
319* import {
320* unstable_getRSCStream as getRSCStream,
321* unstable_RSCHydratedRouter as RSCHydratedRouter,
322* } from "react-router";
323* import type { unstable_RSCPayload as RSCPayload } from "react-router";
324*
325* createFromReadableStream(getRSCStream()).then((payload) =>
326* startTransition(async () => {
327* hydrateRoot(
328* document,
329* <StrictMode>
330* <RSCHydratedRouter
331* createFromReadableStream={createFromReadableStream}
332* payload={payload}
333* />
334* </StrictMode>,
335* { formState: await getFormState(payload) },
336* );
337* }),
338* );
339*
340* @name unstable_RSCHydratedRouter
341* @public
342* @category RSC
343* @mode data
344* @param props Props
345* @param {unstable_RSCHydratedRouterProps.createFromReadableStream} props.createFromReadableStream n/a
346* @param {unstable_RSCHydratedRouterProps.fetch} props.fetch n/a
347* @param {unstable_RSCHydratedRouterProps.getContext} props.getContext n/a
348* @param {unstable_RSCHydratedRouterProps.payload} props.payload n/a
349* @returns A hydrated {@link DataRouter} that can be used to navigate and
350* render routes.
351*/
352function RSCHydratedRouter({ createFromReadableStream, fetch: fetchImplementation = fetch, payload, getContext }) {
353 if (payload.type !== "render") throw new Error("Invalid payload type");
354 let { routeDiscovery, clientVersion } = payload;
355 let { router, routeModules } = React$1.useMemo(() => createRouterFromPayload({
356 payload,
357 fetchImplementation,
358 getContext,
359 createFromReadableStream
360 }), [
361 createFromReadableStream,
362 payload,
363 fetchImplementation,
364 getContext
365 ]);
366 React$1.useEffect(() => {
367 setIsHydrated();
368 }, []);
369 React$1.useLayoutEffect(() => {
370 const globalVar = window;
371 if (!globalVar.__routerInitialized) {
372 globalVar.__routerInitialized = true;
373 globalVar.__reactRouterDataRouter.initialize();
374 }
375 }, []);
376 let [{ routes, state }, setState] = React$1.useState(() => ({
377 routes: cloneRoutes(router.routes),
378 state: router.state
379 }));
380 React$1.useLayoutEffect(() => router.subscribe((newState) => {
381 if (diffRoutes(router.routes, routes)) React$1.startTransition(() => {
382 setState({
383 routes: cloneRoutes(router.routes),
384 state: newState
385 });
386 });
387 }), [
388 router.subscribe,
389 routes,
390 router
391 ]);
392 const transitionEnabledRouter = React$1.useMemo(() => ({
393 ...router,
394 state,
395 routes
396 }), [
397 router,
398 routes,
399 state
400 ]);
401 React$1.useEffect(() => {
402 if (routeDiscovery.mode === "initial" || window.navigator?.connection?.saveData === true) return;
403 function registerElement(el) {
404 let path = el.tagName === "FORM" ? el.getAttribute("action") : el.getAttribute("href");
405 if (!path) return;
406 let pathname = el.tagName === "A" ? el.pathname : new URL(path, window.location.origin).pathname;
407 if (!discoveredPaths.has(pathname)) nextPaths.add(pathname);
408 }
409 async function fetchPatches() {
410 document.querySelectorAll("a[data-discover], form[data-discover]").forEach(registerElement);
411 let paths = Array.from(nextPaths.keys()).filter((path) => {
412 if (discoveredPaths.has(path)) {
413 nextPaths.delete(path);
414 return false;
415 }
416 return true;
417 });
418 if (paths.length === 0) return;
419 try {
420 await fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, null);
421 } catch (e) {
422 console.error("Failed to fetch manifest patches", e);
423 }
424 }
425 let debouncedFetchPatches = debounce(fetchPatches, 100);
426 fetchPatches();
427 new MutationObserver(() => debouncedFetchPatches()).observe(document.documentElement, {
428 subtree: true,
429 childList: true,
430 attributes: true,
431 attributeFilter: [
432 "data-discover",
433 "href",
434 "action"
435 ]
436 });
437 }, [
438 routeDiscovery,
439 createFromReadableStream,
440 fetchImplementation,
441 clientVersion
442 ]);
443 const frameworkContext = {
444 future: {},
445 isSpaMode: false,
446 ssr: true,
447 criticalCss: "",
448 manifest: {
449 routes: {},
450 version: "1",
451 url: "",
452 entry: {
453 module: "",
454 imports: []
455 }
456 },
457 routeDiscovery: payload.routeDiscovery.mode === "initial" ? {
458 mode: "initial",
459 manifestPath: defaultManifestPath
460 } : {
461 mode: "lazy",
462 manifestPath: payload.routeDiscovery.manifestPath || defaultManifestPath
463 },
464 routeModules
465 };
466 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, {
467 router: transitionEnabledRouter,
468 flushSync: ReactDOM.flushSync
469 }))));
470}
471function createRouteFromServerManifest(match, payload) {
472 let hasInitialData = payload && match.id in payload.loaderData;
473 let initialData = payload?.loaderData[match.id];
474 let hasInitialError = payload?.errors && match.id in payload.errors;
475 let initialError = payload?.errors?.[match.id];
476 let isHydrationRequest = match.clientLoader?.hydrate === true || !match.hasLoader || match.hasComponent && !match.element;
477 invariant(window.__reactRouterRouteModules);
478 populateRSCRouteModules(window.__reactRouterRouteModules, match);
479 let dataRoute = {
480 id: match.id,
481 element: match.element,
482 errorElement: match.errorElement,
483 handle: match.handle,
484 hydrateFallbackElement: match.hydrateFallbackElement,
485 index: match.index,
486 loader: match.clientLoader ? async (args, singleFetch) => {
487 let _isHydrationRequest = isHydrationRequest;
488 isHydrationRequest = false;
489 return await match.clientLoader({
490 ...args,
491 serverLoader: () => {
492 preventInvalidServerHandlerCall("loader", match.id, match.hasLoader);
493 if (_isHydrationRequest) {
494 if (hasInitialData) return initialData;
495 if (hasInitialError) throw initialError;
496 }
497 return callSingleFetch(singleFetch);
498 }
499 });
500 } : (_, singleFetch) => callSingleFetch(singleFetch),
501 action: match.clientAction ? (args, singleFetch) => match.clientAction({
502 ...args,
503 serverAction: async () => {
504 preventInvalidServerHandlerCall("action", match.id, match.hasLoader);
505 return await callSingleFetch(singleFetch);
506 }
507 }) : match.hasAction ? (_, singleFetch) => callSingleFetch(singleFetch) : () => {
508 throw noActionDefinedError("action", match.id);
509 },
510 path: match.path,
511 shouldRevalidate: match.shouldRevalidate,
512 hasLoader: true,
513 hasClientLoader: match.clientLoader != null,
514 hasComponent: match.hasComponent,
515 hasAction: match.hasAction,
516 hasClientAction: match.clientAction != null
517 };
518 if (typeof dataRoute.loader === "function") dataRoute.loader.hydrate = shouldHydrateRouteLoader(match.id, match.clientLoader, match.hasLoader, false);
519 return dataRoute;
520}
521function callSingleFetch(singleFetch) {
522 invariant(typeof singleFetch === "function", "Invalid singleFetch parameter");
523 return singleFetch();
524}
525function preventInvalidServerHandlerCall(type, routeId, hasHandler) {
526 if (!hasHandler) {
527 let msg = `You are trying to call ${type === "action" ? "serverAction()" : "serverLoader()"} on a route that does not have a server ${type} (routeId: "${routeId}")`;
528 console.error(msg);
529 throw new ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
530 }
531}
532const nextPaths = /* @__PURE__ */ new Set();
533const discoveredPathsMaxSize = 1e3;
534const discoveredPaths = /* @__PURE__ */ new Set();
535function getManifestUrl(paths, clientVersion) {
536 if (paths.length === 0) return null;
537 let url;
538 if (paths.length === 1) url = new URL(`${paths[0]}.manifest`, window.location.origin);
539 else {
540 let basename = (window.__reactRouterDataRouter.basename ?? "").replace(/^\/|\/$/g, "");
541 url = new URL(`${basename}/.manifest`, window.location.origin);
542 url.searchParams.set("paths", paths.sort().join(","));
543 }
544 if (clientVersion !== void 0) url.searchParams.set("version", clientVersion);
545 return url;
546}
547async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, clientVersion, errorReloadPath, signal) {
548 paths = getPathsWithAncestors(paths);
549 let url = getManifestUrl(paths, clientVersion);
550 if (url == null) return;
551 if (url.toString().length > 7680) {
552 nextPaths.clear();
553 return;
554 }
555 let response = await fetchImplementation(new Request(url, { signal }));
556 if (clientVersion !== void 0 && response.status === 204 && response.headers.has("X-Remix-Reload-Document")) {
557 await handleClientVersionMismatch(true, clientVersion, errorReloadPath);
558 return;
559 }
560 if (!response.body || response.status < 200 || response.status >= 300) throw new Error("Unable to fetch new route matches from the server");
561 let payload = await createFromReadableStream(response.body, { temporaryReferences: void 0 });
562 if (payload.type !== "manifest") throw new Error("Failed to patch routes");
563 paths.forEach((p) => addToFifoQueue(p, discoveredPaths));
564 let patches = await payload.patches;
565 React$1.startTransition(() => {
566 patches.forEach((p) => {
567 window.__reactRouterDataRouter.patchRoutes(p.parentId ?? null, [createRouteFromServerManifest(p)]);
568 });
569 });
570}
571function addToFifoQueue(path, queue) {
572 if (queue.size >= discoveredPathsMaxSize) {
573 let first = queue.values().next().value;
574 if (typeof first === "string") queue.delete(first);
575 }
576 queue.add(path);
577}
578function debounce(callback, wait) {
579 let timeoutId;
580 return (...args) => {
581 window.clearTimeout(timeoutId);
582 timeoutId = window.setTimeout(() => callback(...args), wait);
583 };
584}
585function isExternalLocation(location) {
586 return new URL(location, window.location.href).origin !== window.location.origin;
587}
588function normalizeRedirectLocation(location) {
589 if (PROTOCOL_RELATIVE_URL_REGEX.test(location)) {
590 let path = resolvePath(location);
591 return path.pathname + path.search + path.hash;
592 }
593 return location;
594}
595function cloneRoutes(routes) {
596 if (!routes) return void 0;
597 return routes.map((route) => ({
598 ...route,
599 children: cloneRoutes(route.children)
600 }));
601}
602function diffRoutes(a, b) {
603 if (a.length !== b.length) return true;
604 return a.some((route, index) => {
605 if (route.element !== b[index].element) return true;
606 if (route.errorElement !== b[index].errorElement) return true;
607 if (route.hydrateFallbackElement !== b[index].hydrateFallbackElement) return true;
608 if (route.hasLoader !== b[index].hasLoader) return true;
609 if (route.hasClientLoader !== b[index].hasClientLoader) return true;
610 if (route.hasAction !== b[index].hasAction) return true;
611 if (route.hasClientAction !== b[index].hasClientAction) return true;
612 return diffRoutes(route.children || [], b[index].children || []);
613 });
614}
615//#endregion
616export { RSCHydratedRouter, createCallServer };