UNPKG

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