| 1 | |
| 2 | |
| 3 | |
| 4 | |
| 5 | |
| 6 | |
| 7 | |
| 8 | |
| 9 | |
| 10 |
|
| 11 | import { PROTOCOL_RELATIVE_URL_REGEX } from "../router/url.js";
|
| 12 | import { createBrowserHistory, invariant } from "../router/history.js";
|
| 13 | import { ErrorResponseImpl, createContext, resolvePath } from "../router/utils.js";
|
| 14 | import { createRouter, hasInvalidProtocol, isMutationMethod } from "../router/router.js";
|
| 15 | import { RSCRouterContext } from "../context.js";
|
| 16 | import { RouterProvider } from "../components.js";
|
| 17 | import { createRequestInit } from "../dom/ssr/data.js";
|
| 18 | import { getSingleFetchDataStrategyImpl, singleFetchUrl, stripIndexParam } from "../dom/ssr/single-fetch.js";
|
| 19 | import { noActionDefinedError, shouldHydrateRouteLoader } from "../dom/ssr/routes.js";
|
| 20 | import { getPathsWithAncestors } from "../dom/ssr/fog-of-war.js";
|
| 21 | import { FrameworkContext, setIsHydrated } from "../dom/ssr/components.js";
|
| 22 | import { RSCRouterGlobalErrorBoundary } from "./errorBoundaries.js";
|
| 23 | import { populateRSCRouteModules } from "./route-modules.js";
|
| 24 | import { getHydrationData } from "../dom/ssr/hydration.js";
|
| 25 | import * as React$1 from "react";
|
| 26 | import * as ReactDOM from "react-dom";
|
| 27 |
|
| 28 | const defaultManifestPath = "/__manifest";
|
| 29 | |
| 30 | |
| 31 | |
| 32 | |
| 33 | |
| 34 | |
| 35 | |
| 36 | |
| 37 | |
| 38 | |
| 39 | |
| 40 | |
| 41 | |
| 42 | |
| 43 | |
| 44 | |
| 45 | |
| 46 | |
| 47 | |
| 48 | |
| 49 | |
| 50 | |
| 51 | |
| 52 | |
| 53 | |
| 54 | |
| 55 | |
| 56 | |
| 57 | |
| 58 | |
| 59 | |
| 60 | |
| 61 | |
| 62 | |
| 63 | |
| 64 |
|
| 65 | function 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 | }
|
| 129 | function 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 | }
|
| 239 | const renderedRoutesContext = createContext();
|
| 240 | function 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 = 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 | }
|
| 272 | function 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 | |
| 312 | |
| 313 | |
| 314 | |
| 315 | |
| 316 | |
| 317 | |
| 318 | |
| 319 | |
| 320 | |
| 321 | |
| 322 | |
| 323 | |
| 324 | |
| 325 | |
| 326 | |
| 327 | |
| 328 | |
| 329 | |
| 330 | |
| 331 | |
| 332 | |
| 333 | |
| 334 | |
| 335 | |
| 336 | |
| 337 | |
| 338 | |
| 339 | |
| 340 | |
| 341 | |
| 342 | |
| 343 | |
| 344 | |
| 345 | |
| 346 | |
| 347 | |
| 348 |
|
| 349 | function 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 React$1.createElement(RSCRouterContext.Provider, { value: true }, React$1.createElement(RSCRouterGlobalErrorBoundary, { location: state.location }, React$1.createElement(FrameworkContext.Provider, { value: frameworkContext }, React$1.createElement(RouterProvider, {
|
| 463 | router: transitionEnabledRouter,
|
| 464 | flushSync: ReactDOM.flushSync
|
| 465 | }))));
|
| 466 | }
|
| 467 | function 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 | }
|
| 516 | function callSingleFetch(singleFetch) {
|
| 517 | invariant(typeof singleFetch === "function", "Invalid singleFetch parameter");
|
| 518 | return singleFetch();
|
| 519 | }
|
| 520 | function 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 | }
|
| 527 | const nextPaths = new Set();
|
| 528 | const discoveredPathsMaxSize = 1e3;
|
| 529 | const discoveredPaths = new Set();
|
| 530 | function 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 | }
|
| 538 | async 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 | }
|
| 558 | function 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 | }
|
| 565 | function debounce(callback, wait) {
|
| 566 | let timeoutId;
|
| 567 | return (...args) => {
|
| 568 | window.clearTimeout(timeoutId);
|
| 569 | timeoutId = window.setTimeout(() => callback(...args), wait);
|
| 570 | };
|
| 571 | }
|
| 572 | function isExternalLocation(location) {
|
| 573 | return new URL(location, window.location.href).origin !== window.location.origin;
|
| 574 | }
|
| 575 | function 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 | }
|
| 582 | function cloneRoutes(routes) {
|
| 583 | if (!routes) return void 0;
|
| 584 | return routes.map((route) => ({
|
| 585 | ...route,
|
| 586 | children: cloneRoutes(route.children)
|
| 587 | }));
|
| 588 | }
|
| 589 | function 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 |
|
| 603 | export { RSCHydratedRouter, createCallServer };
|