UNPKG

71.2 kBJavaScriptView Raw
1/**
2 * react-router v7.10.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 {
12 ENABLE_DEV_WARNINGS,
13 ErrorResponseImpl,
14 FrameworkContext,
15 NO_BODY_STATUS_CODES,
16 Outlet,
17 RSCRouterContext,
18 RemixErrorBoundary,
19 RouterContextProvider,
20 RouterProvider,
21 SINGLE_FETCH_REDIRECT_STATUS,
22 SingleFetchRedirectSymbol,
23 StaticRouterProvider,
24 StreamTransfer,
25 convertRoutesToDataRoutes,
26 createMemoryRouter,
27 createServerRoutes,
28 createStaticHandler,
29 createStaticRouter,
30 decodeViaTurboStream,
31 encode,
32 escapeHtml,
33 getManifestPath,
34 getStaticContextFromError,
35 instrumentHandler,
36 isDataWithResponseInit,
37 isRedirectResponse,
38 isRedirectStatusCode,
39 isResponse,
40 isRouteErrorResponse,
41 matchRoutes,
42 redirect,
43 redirectDocument,
44 replace,
45 shouldHydrateRouteLoader,
46 stripBasename,
47 useRouteError,
48 warnOnce,
49 withComponentProps,
50 withErrorBoundaryProps,
51 withHydrateFallbackProps
52} from "./chunk-RNH4J7KQ.mjs";
53
54// lib/dom/ssr/server.tsx
55import * as React from "react";
56function ServerRouter({
57 context,
58 url,
59 nonce
60}) {
61 if (typeof url === "string") {
62 url = new URL(url);
63 }
64 let { manifest, routeModules, criticalCss, serverHandoffString } = context;
65 let routes = createServerRoutes(
66 manifest.routes,
67 routeModules,
68 context.future,
69 context.isSpaMode
70 );
71 context.staticHandlerContext.loaderData = {
72 ...context.staticHandlerContext.loaderData
73 };
74 for (let match of context.staticHandlerContext.matches) {
75 let routeId = match.route.id;
76 let route = routeModules[routeId];
77 let manifestRoute = context.manifest.routes[routeId];
78 if (route && manifestRoute && shouldHydrateRouteLoader(
79 routeId,
80 route.clientLoader,
81 manifestRoute.hasLoader,
82 context.isSpaMode
83 ) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
84 delete context.staticHandlerContext.loaderData[routeId];
85 }
86 }
87 let router = createStaticRouter(routes, context.staticHandlerContext);
88 return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
89 FrameworkContext.Provider,
90 {
91 value: {
92 manifest,
93 routeModules,
94 criticalCss,
95 serverHandoffString,
96 future: context.future,
97 ssr: context.ssr,
98 isSpaMode: context.isSpaMode,
99 routeDiscovery: context.routeDiscovery,
100 serializeError: context.serializeError,
101 renderMeta: context.renderMeta
102 }
103 },
104 /* @__PURE__ */ React.createElement(RemixErrorBoundary, { location: router.state.location }, /* @__PURE__ */ React.createElement(
105 StaticRouterProvider,
106 {
107 router,
108 context: context.staticHandlerContext,
109 hydrate: false
110 }
111 ))
112 ), context.serverHandoffStream ? /* @__PURE__ */ React.createElement(React.Suspense, null, /* @__PURE__ */ React.createElement(
113 StreamTransfer,
114 {
115 context,
116 identifier: 0,
117 reader: context.serverHandoffStream.getReader(),
118 textDecoder: new TextDecoder(),
119 nonce
120 }
121 )) : null);
122}
123
124// lib/dom/ssr/routes-test-stub.tsx
125import * as React2 from "react";
126function createRoutesStub(routes, _context) {
127 return function RoutesTestStub({
128 initialEntries,
129 initialIndex,
130 hydrationData,
131 future
132 }) {
133 let routerRef = React2.useRef();
134 let frameworkContextRef = React2.useRef();
135 if (routerRef.current == null) {
136 frameworkContextRef.current = {
137 future: {
138 unstable_subResourceIntegrity: future?.unstable_subResourceIntegrity === true,
139 v8_middleware: future?.v8_middleware === true
140 },
141 manifest: {
142 routes: {},
143 entry: { imports: [], module: "" },
144 url: "",
145 version: ""
146 },
147 routeModules: {},
148 ssr: false,
149 isSpaMode: false,
150 routeDiscovery: { mode: "lazy", manifestPath: "/__manifest" }
151 };
152 let patched = processRoutes(
153 // @ts-expect-error `StubRouteObject` is stricter about `loader`/`action`
154 // types compared to `AgnosticRouteObject`
155 convertRoutesToDataRoutes(routes, (r) => r),
156 _context !== void 0 ? _context : future?.v8_middleware ? new RouterContextProvider() : {},
157 frameworkContextRef.current.manifest,
158 frameworkContextRef.current.routeModules
159 );
160 routerRef.current = createMemoryRouter(patched, {
161 initialEntries,
162 initialIndex,
163 hydrationData
164 });
165 }
166 return /* @__PURE__ */ React2.createElement(FrameworkContext.Provider, { value: frameworkContextRef.current }, /* @__PURE__ */ React2.createElement(RouterProvider, { router: routerRef.current }));
167 };
168}
169function processRoutes(routes, context, manifest, routeModules, parentId) {
170 return routes.map((route) => {
171 if (!route.id) {
172 throw new Error(
173 "Expected a route.id in react-router processRoutes() function"
174 );
175 }
176 let newRoute = {
177 id: route.id,
178 path: route.path,
179 index: route.index,
180 Component: route.Component ? withComponentProps(route.Component) : void 0,
181 HydrateFallback: route.HydrateFallback ? withHydrateFallbackProps(route.HydrateFallback) : void 0,
182 ErrorBoundary: route.ErrorBoundary ? withErrorBoundaryProps(route.ErrorBoundary) : void 0,
183 action: route.action ? (args) => route.action({ ...args, context }) : void 0,
184 loader: route.loader ? (args) => route.loader({ ...args, context }) : void 0,
185 middleware: route.middleware ? route.middleware.map(
186 (mw) => (...args) => mw(
187 { ...args[0], context },
188 args[1]
189 )
190 ) : void 0,
191 handle: route.handle,
192 shouldRevalidate: route.shouldRevalidate
193 };
194 let entryRoute = {
195 id: route.id,
196 path: route.path,
197 index: route.index,
198 parentId,
199 hasAction: route.action != null,
200 hasLoader: route.loader != null,
201 // When testing routes, you should be stubbing loader/action/middleware,
202 // not trying to re-implement the full loader/clientLoader/SSR/hydration
203 // flow. That is better tested via E2E tests.
204 hasClientAction: false,
205 hasClientLoader: false,
206 hasClientMiddleware: false,
207 hasErrorBoundary: route.ErrorBoundary != null,
208 // any need for these?
209 module: "build/stub-path-to-module.js",
210 clientActionModule: void 0,
211 clientLoaderModule: void 0,
212 clientMiddlewareModule: void 0,
213 hydrateFallbackModule: void 0
214 };
215 manifest.routes[newRoute.id] = entryRoute;
216 routeModules[route.id] = {
217 default: newRoute.Component || Outlet,
218 ErrorBoundary: newRoute.ErrorBoundary || void 0,
219 handle: route.handle,
220 links: route.links,
221 meta: route.meta,
222 shouldRevalidate: route.shouldRevalidate
223 };
224 if (route.children) {
225 newRoute.children = processRoutes(
226 route.children,
227 context,
228 manifest,
229 routeModules,
230 newRoute.id
231 );
232 }
233 return newRoute;
234 });
235}
236
237// lib/server-runtime/cookies.ts
238import { parse, serialize } from "cookie";
239
240// lib/server-runtime/crypto.ts
241var encoder = /* @__PURE__ */ new TextEncoder();
242var sign = async (value, secret) => {
243 let data2 = encoder.encode(value);
244 let key = await createKey(secret, ["sign"]);
245 let signature = await crypto.subtle.sign("HMAC", key, data2);
246 let hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(
247 /=+$/,
248 ""
249 );
250 return value + "." + hash;
251};
252var unsign = async (cookie, secret) => {
253 let index = cookie.lastIndexOf(".");
254 let value = cookie.slice(0, index);
255 let hash = cookie.slice(index + 1);
256 let data2 = encoder.encode(value);
257 let key = await createKey(secret, ["verify"]);
258 try {
259 let signature = byteStringToUint8Array(atob(hash));
260 let valid = await crypto.subtle.verify("HMAC", key, signature, data2);
261 return valid ? value : false;
262 } catch (error) {
263 return false;
264 }
265};
266var createKey = async (secret, usages) => crypto.subtle.importKey(
267 "raw",
268 encoder.encode(secret),
269 { name: "HMAC", hash: "SHA-256" },
270 false,
271 usages
272);
273function byteStringToUint8Array(byteString) {
274 let array = new Uint8Array(byteString.length);
275 for (let i = 0; i < byteString.length; i++) {
276 array[i] = byteString.charCodeAt(i);
277 }
278 return array;
279}
280
281// lib/server-runtime/cookies.ts
282var createCookie = (name, cookieOptions = {}) => {
283 let { secrets = [], ...options } = {
284 path: "/",
285 sameSite: "lax",
286 ...cookieOptions
287 };
288 warnOnceAboutExpiresCookie(name, options.expires);
289 return {
290 get name() {
291 return name;
292 },
293 get isSigned() {
294 return secrets.length > 0;
295 },
296 get expires() {
297 return typeof options.maxAge !== "undefined" ? new Date(Date.now() + options.maxAge * 1e3) : options.expires;
298 },
299 async parse(cookieHeader, parseOptions) {
300 if (!cookieHeader) return null;
301 let cookies = parse(cookieHeader, { ...options, ...parseOptions });
302 if (name in cookies) {
303 let value = cookies[name];
304 if (typeof value === "string" && value !== "") {
305 let decoded = await decodeCookieValue(value, secrets);
306 return decoded;
307 } else {
308 return "";
309 }
310 } else {
311 return null;
312 }
313 },
314 async serialize(value, serializeOptions) {
315 return serialize(
316 name,
317 value === "" ? "" : await encodeCookieValue(value, secrets),
318 {
319 ...options,
320 ...serializeOptions
321 }
322 );
323 }
324 };
325};
326var isCookie = (object) => {
327 return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
328};
329async function encodeCookieValue(value, secrets) {
330 let encoded = encodeData(value);
331 if (secrets.length > 0) {
332 encoded = await sign(encoded, secrets[0]);
333 }
334 return encoded;
335}
336async function decodeCookieValue(value, secrets) {
337 if (secrets.length > 0) {
338 for (let secret of secrets) {
339 let unsignedValue = await unsign(value, secret);
340 if (unsignedValue !== false) {
341 return decodeData(unsignedValue);
342 }
343 }
344 return null;
345 }
346 return decodeData(value);
347}
348function encodeData(value) {
349 return btoa(myUnescape(encodeURIComponent(JSON.stringify(value))));
350}
351function decodeData(value) {
352 try {
353 return JSON.parse(decodeURIComponent(myEscape(atob(value))));
354 } catch (error) {
355 return {};
356 }
357}
358function myEscape(value) {
359 let str = value.toString();
360 let result = "";
361 let index = 0;
362 let chr, code;
363 while (index < str.length) {
364 chr = str.charAt(index++);
365 if (/[\w*+\-./@]/.exec(chr)) {
366 result += chr;
367 } else {
368 code = chr.charCodeAt(0);
369 if (code < 256) {
370 result += "%" + hex(code, 2);
371 } else {
372 result += "%u" + hex(code, 4).toUpperCase();
373 }
374 }
375 }
376 return result;
377}
378function hex(code, length) {
379 let result = code.toString(16);
380 while (result.length < length) result = "0" + result;
381 return result;
382}
383function myUnescape(value) {
384 let str = value.toString();
385 let result = "";
386 let index = 0;
387 let chr, part;
388 while (index < str.length) {
389 chr = str.charAt(index++);
390 if (chr === "%") {
391 if (str.charAt(index) === "u") {
392 part = str.slice(index + 1, index + 5);
393 if (/^[\da-f]{4}$/i.exec(part)) {
394 result += String.fromCharCode(parseInt(part, 16));
395 index += 5;
396 continue;
397 }
398 } else {
399 part = str.slice(index, index + 2);
400 if (/^[\da-f]{2}$/i.exec(part)) {
401 result += String.fromCharCode(parseInt(part, 16));
402 index += 2;
403 continue;
404 }
405 }
406 }
407 result += chr;
408 }
409 return result;
410}
411function warnOnceAboutExpiresCookie(name, expires) {
412 warnOnce(
413 !expires,
414 `The "${name}" cookie has an "expires" property set. This will cause the expires value to not be updated when the session is committed. Instead, you should set the expires value when serializing the cookie. You can use \`commitSession(session, { expires })\` if using a session storage object, or \`cookie.serialize("value", { expires })\` if you're using the cookie directly.`
415 );
416}
417
418// lib/server-runtime/entry.ts
419function createEntryRouteModules(manifest) {
420 return Object.keys(manifest).reduce((memo, routeId) => {
421 let route = manifest[routeId];
422 if (route) {
423 memo[routeId] = route.module;
424 }
425 return memo;
426 }, {});
427}
428
429// lib/server-runtime/mode.ts
430var ServerMode = /* @__PURE__ */ ((ServerMode2) => {
431 ServerMode2["Development"] = "development";
432 ServerMode2["Production"] = "production";
433 ServerMode2["Test"] = "test";
434 return ServerMode2;
435})(ServerMode || {});
436function isServerMode(value) {
437 return value === "development" /* Development */ || value === "production" /* Production */ || value === "test" /* Test */;
438}
439
440// lib/server-runtime/errors.ts
441function sanitizeError(error, serverMode) {
442 if (error instanceof Error && serverMode !== "development" /* Development */) {
443 let sanitized = new Error("Unexpected Server Error");
444 sanitized.stack = void 0;
445 return sanitized;
446 }
447 return error;
448}
449function sanitizeErrors(errors, serverMode) {
450 return Object.entries(errors).reduce((acc, [routeId, error]) => {
451 return Object.assign(acc, { [routeId]: sanitizeError(error, serverMode) });
452 }, {});
453}
454function serializeError(error, serverMode) {
455 let sanitized = sanitizeError(error, serverMode);
456 return {
457 message: sanitized.message,
458 stack: sanitized.stack
459 };
460}
461function serializeErrors(errors, serverMode) {
462 if (!errors) return null;
463 let entries = Object.entries(errors);
464 let serialized = {};
465 for (let [key, val] of entries) {
466 if (isRouteErrorResponse(val)) {
467 serialized[key] = { ...val, __type: "RouteErrorResponse" };
468 } else if (val instanceof Error) {
469 let sanitized = sanitizeError(val, serverMode);
470 serialized[key] = {
471 message: sanitized.message,
472 stack: sanitized.stack,
473 __type: "Error",
474 // If this is a subclass (i.e., ReferenceError), send up the type so we
475 // can re-create the same type during hydration. This will only apply
476 // in dev mode since all production errors are sanitized to normal
477 // Error instances
478 ...sanitized.name !== "Error" ? {
479 __subType: sanitized.name
480 } : {}
481 };
482 } else {
483 serialized[key] = val;
484 }
485 }
486 return serialized;
487}
488
489// lib/server-runtime/routeMatching.ts
490function matchServerRoutes(routes, pathname, basename) {
491 let matches = matchRoutes(
492 routes,
493 pathname,
494 basename
495 );
496 if (!matches) return null;
497 return matches.map((match) => ({
498 params: match.params,
499 pathname: match.pathname,
500 route: match.route
501 }));
502}
503
504// lib/server-runtime/data.ts
505async function callRouteHandler(handler, args) {
506 let result = await handler({
507 request: stripRoutesParam(stripIndexParam(args.request)),
508 params: args.params,
509 context: args.context,
510 unstable_pattern: args.unstable_pattern
511 });
512 if (isDataWithResponseInit(result) && result.init && result.init.status && isRedirectStatusCode(result.init.status)) {
513 throw new Response(null, result.init);
514 }
515 return result;
516}
517function stripIndexParam(request) {
518 let url = new URL(request.url);
519 let indexValues = url.searchParams.getAll("index");
520 url.searchParams.delete("index");
521 let indexValuesToKeep = [];
522 for (let indexValue of indexValues) {
523 if (indexValue) {
524 indexValuesToKeep.push(indexValue);
525 }
526 }
527 for (let toKeep of indexValuesToKeep) {
528 url.searchParams.append("index", toKeep);
529 }
530 let init = {
531 method: request.method,
532 body: request.body,
533 headers: request.headers,
534 signal: request.signal
535 };
536 if (init.body) {
537 init.duplex = "half";
538 }
539 return new Request(url.href, init);
540}
541function stripRoutesParam(request) {
542 let url = new URL(request.url);
543 url.searchParams.delete("_routes");
544 let init = {
545 method: request.method,
546 body: request.body,
547 headers: request.headers,
548 signal: request.signal
549 };
550 if (init.body) {
551 init.duplex = "half";
552 }
553 return new Request(url.href, init);
554}
555
556// lib/server-runtime/invariant.ts
557function invariant(value, message) {
558 if (value === false || value === null || typeof value === "undefined") {
559 console.error(
560 "The following error is a bug in React Router; please open an issue! https://github.com/remix-run/react-router/issues/new/choose"
561 );
562 throw new Error(message);
563 }
564}
565
566// lib/server-runtime/dev.ts
567var globalDevServerHooksKey = "__reactRouterDevServerHooks";
568function setDevServerHooks(devServerHooks) {
569 globalThis[globalDevServerHooksKey] = devServerHooks;
570}
571function getDevServerHooks() {
572 return globalThis[globalDevServerHooksKey];
573}
574function getBuildTimeHeader(request, headerName) {
575 if (typeof process !== "undefined") {
576 try {
577 if (process.env?.IS_RR_BUILD_REQUEST === "yes") {
578 return request.headers.get(headerName);
579 }
580 } catch (e) {
581 }
582 }
583 return null;
584}
585
586// lib/server-runtime/routes.ts
587function groupRoutesByParentId(manifest) {
588 let routes = {};
589 Object.values(manifest).forEach((route) => {
590 if (route) {
591 let parentId = route.parentId || "";
592 if (!routes[parentId]) {
593 routes[parentId] = [];
594 }
595 routes[parentId].push(route);
596 }
597 });
598 return routes;
599}
600function createRoutes(manifest, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
601 return (routesByParentId[parentId] || []).map((route) => ({
602 ...route,
603 children: createRoutes(manifest, route.id, routesByParentId)
604 }));
605}
606function createStaticHandlerDataRoutes(manifest, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
607 return (routesByParentId[parentId] || []).map((route) => {
608 let commonRoute = {
609 // Always include root due to default boundaries
610 hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
611 id: route.id,
612 path: route.path,
613 middleware: route.module.middleware,
614 // Need to use RR's version in the param typed here to permit the optional
615 // context even though we know it'll always be provided in remix
616 loader: route.module.loader ? async (args) => {
617 let preRenderedData = getBuildTimeHeader(
618 args.request,
619 "X-React-Router-Prerender-Data"
620 );
621 if (preRenderedData != null) {
622 let encoded = preRenderedData ? decodeURI(preRenderedData) : preRenderedData;
623 invariant(encoded, "Missing prerendered data for route");
624 let uint8array = new TextEncoder().encode(encoded);
625 let stream = new ReadableStream({
626 start(controller) {
627 controller.enqueue(uint8array);
628 controller.close();
629 }
630 });
631 let decoded = await decodeViaTurboStream(stream, global);
632 let data2 = decoded.value;
633 if (data2 && SingleFetchRedirectSymbol in data2) {
634 let result = data2[SingleFetchRedirectSymbol];
635 let init = { status: result.status };
636 if (result.reload) {
637 throw redirectDocument(result.redirect, init);
638 } else if (result.replace) {
639 throw replace(result.redirect, init);
640 } else {
641 throw redirect(result.redirect, init);
642 }
643 } else {
644 invariant(
645 data2 && route.id in data2,
646 "Unable to decode prerendered data"
647 );
648 let result = data2[route.id];
649 invariant(
650 "data" in result,
651 "Unable to process prerendered data"
652 );
653 return result.data;
654 }
655 }
656 let val = await callRouteHandler(route.module.loader, args);
657 return val;
658 } : void 0,
659 action: route.module.action ? (args) => callRouteHandler(route.module.action, args) : void 0,
660 handle: route.module.handle
661 };
662 return route.index ? {
663 index: true,
664 ...commonRoute
665 } : {
666 caseSensitive: route.caseSensitive,
667 children: createStaticHandlerDataRoutes(
668 manifest,
669 future,
670 route.id,
671 routesByParentId
672 ),
673 ...commonRoute
674 };
675 });
676}
677
678// lib/server-runtime/serverHandoff.ts
679function createServerHandoffString(serverHandoff) {
680 return escapeHtml(JSON.stringify(serverHandoff));
681}
682
683// lib/server-runtime/headers.ts
684import { splitCookiesString } from "set-cookie-parser";
685function getDocumentHeaders(context, build) {
686 return getDocumentHeadersImpl(context, (m) => {
687 let route = build.routes[m.route.id];
688 invariant(route, `Route with id "${m.route.id}" not found in build`);
689 return route.module.headers;
690 });
691}
692function getDocumentHeadersImpl(context, getRouteHeadersFn, _defaultHeaders) {
693 let boundaryIdx = context.errors ? context.matches.findIndex((m) => context.errors[m.route.id]) : -1;
694 let matches = boundaryIdx >= 0 ? context.matches.slice(0, boundaryIdx + 1) : context.matches;
695 let errorHeaders;
696 if (boundaryIdx >= 0) {
697 let { actionHeaders, actionData, loaderHeaders, loaderData } = context;
698 context.matches.slice(boundaryIdx).some((match) => {
699 let id = match.route.id;
700 if (actionHeaders[id] && (!actionData || !actionData.hasOwnProperty(id))) {
701 errorHeaders = actionHeaders[id];
702 } else if (loaderHeaders[id] && !loaderData.hasOwnProperty(id)) {
703 errorHeaders = loaderHeaders[id];
704 }
705 return errorHeaders != null;
706 });
707 }
708 const defaultHeaders = new Headers(_defaultHeaders);
709 return matches.reduce((parentHeaders, match, idx) => {
710 let { id } = match.route;
711 let loaderHeaders = context.loaderHeaders[id] || new Headers();
712 let actionHeaders = context.actionHeaders[id] || new Headers();
713 let includeErrorHeaders = errorHeaders != null && idx === matches.length - 1;
714 let includeErrorCookies = includeErrorHeaders && errorHeaders !== loaderHeaders && errorHeaders !== actionHeaders;
715 let headersFn = getRouteHeadersFn(match);
716 if (headersFn == null) {
717 let headers2 = new Headers(parentHeaders);
718 if (includeErrorCookies) {
719 prependCookies(errorHeaders, headers2);
720 }
721 prependCookies(actionHeaders, headers2);
722 prependCookies(loaderHeaders, headers2);
723 return headers2;
724 }
725 let headers = new Headers(
726 typeof headersFn === "function" ? headersFn({
727 loaderHeaders,
728 parentHeaders,
729 actionHeaders,
730 errorHeaders: includeErrorHeaders ? errorHeaders : void 0
731 }) : headersFn
732 );
733 if (includeErrorCookies) {
734 prependCookies(errorHeaders, headers);
735 }
736 prependCookies(actionHeaders, headers);
737 prependCookies(loaderHeaders, headers);
738 prependCookies(parentHeaders, headers);
739 return headers;
740 }, new Headers(defaultHeaders));
741}
742function prependCookies(parentHeaders, childHeaders) {
743 let parentSetCookieString = parentHeaders.get("Set-Cookie");
744 if (parentSetCookieString) {
745 let cookies = splitCookiesString(parentSetCookieString);
746 let childCookies = new Set(childHeaders.getSetCookie());
747 cookies.forEach((cookie) => {
748 if (!childCookies.has(cookie)) {
749 childHeaders.append("Set-Cookie", cookie);
750 }
751 });
752 }
753}
754
755// lib/server-runtime/single-fetch.ts
756var SERVER_NO_BODY_STATUS_CODES = /* @__PURE__ */ new Set([
757 ...NO_BODY_STATUS_CODES,
758 304
759]);
760async function singleFetchAction(build, serverMode, staticHandler, request, handlerUrl, loadContext, handleError) {
761 try {
762 let handlerRequest = new Request(handlerUrl, {
763 method: request.method,
764 body: request.body,
765 headers: request.headers,
766 signal: request.signal,
767 ...request.body ? { duplex: "half" } : void 0
768 });
769 let result = await staticHandler.query(handlerRequest, {
770 requestContext: loadContext,
771 skipLoaderErrorBubbling: true,
772 skipRevalidation: true,
773 generateMiddlewareResponse: build.future.v8_middleware ? async (query) => {
774 try {
775 let innerResult = await query(handlerRequest);
776 return handleQueryResult(innerResult);
777 } catch (error) {
778 return handleQueryError(error);
779 }
780 } : void 0
781 });
782 return handleQueryResult(result);
783 } catch (error) {
784 return handleQueryError(error);
785 }
786 function handleQueryResult(result) {
787 return isResponse(result) ? result : staticContextToResponse(result);
788 }
789 function handleQueryError(error) {
790 handleError(error);
791 return generateSingleFetchResponse(request, build, serverMode, {
792 result: { error },
793 headers: new Headers(),
794 status: 500
795 });
796 }
797 function staticContextToResponse(context) {
798 let headers = getDocumentHeaders(context, build);
799 if (isRedirectStatusCode(context.statusCode) && headers.has("Location")) {
800 return new Response(null, { status: context.statusCode, headers });
801 }
802 if (context.errors) {
803 Object.values(context.errors).forEach((err) => {
804 if (!isRouteErrorResponse(err) || err.error) {
805 handleError(err);
806 }
807 });
808 context.errors = sanitizeErrors(context.errors, serverMode);
809 }
810 let singleFetchResult;
811 if (context.errors) {
812 singleFetchResult = { error: Object.values(context.errors)[0] };
813 } else {
814 singleFetchResult = {
815 data: Object.values(context.actionData || {})[0]
816 };
817 }
818 return generateSingleFetchResponse(request, build, serverMode, {
819 result: singleFetchResult,
820 headers,
821 status: context.statusCode
822 });
823 }
824}
825async function singleFetchLoaders(build, serverMode, staticHandler, request, handlerUrl, loadContext, handleError) {
826 let routesParam = new URL(request.url).searchParams.get("_routes");
827 let loadRouteIds = routesParam ? new Set(routesParam.split(",")) : null;
828 try {
829 let handlerRequest = new Request(handlerUrl, {
830 headers: request.headers,
831 signal: request.signal
832 });
833 let result = await staticHandler.query(handlerRequest, {
834 requestContext: loadContext,
835 filterMatchesToLoad: (m) => !loadRouteIds || loadRouteIds.has(m.route.id),
836 skipLoaderErrorBubbling: true,
837 generateMiddlewareResponse: build.future.v8_middleware ? async (query) => {
838 try {
839 let innerResult = await query(handlerRequest);
840 return handleQueryResult(innerResult);
841 } catch (error) {
842 return handleQueryError(error);
843 }
844 } : void 0
845 });
846 return handleQueryResult(result);
847 } catch (error) {
848 return handleQueryError(error);
849 }
850 function handleQueryResult(result) {
851 return isResponse(result) ? result : staticContextToResponse(result);
852 }
853 function handleQueryError(error) {
854 handleError(error);
855 return generateSingleFetchResponse(request, build, serverMode, {
856 result: { error },
857 headers: new Headers(),
858 status: 500
859 });
860 }
861 function staticContextToResponse(context) {
862 let headers = getDocumentHeaders(context, build);
863 if (isRedirectStatusCode(context.statusCode) && headers.has("Location")) {
864 return new Response(null, { status: context.statusCode, headers });
865 }
866 if (context.errors) {
867 Object.values(context.errors).forEach((err) => {
868 if (!isRouteErrorResponse(err) || err.error) {
869 handleError(err);
870 }
871 });
872 context.errors = sanitizeErrors(context.errors, serverMode);
873 }
874 let results = {};
875 let loadedMatches = new Set(
876 context.matches.filter(
877 (m) => loadRouteIds ? loadRouteIds.has(m.route.id) : m.route.loader != null
878 ).map((m) => m.route.id)
879 );
880 if (context.errors) {
881 for (let [id, error] of Object.entries(context.errors)) {
882 results[id] = { error };
883 }
884 }
885 for (let [id, data2] of Object.entries(context.loaderData)) {
886 if (!(id in results) && loadedMatches.has(id)) {
887 results[id] = { data: data2 };
888 }
889 }
890 return generateSingleFetchResponse(request, build, serverMode, {
891 result: results,
892 headers,
893 status: context.statusCode
894 });
895 }
896}
897function generateSingleFetchResponse(request, build, serverMode, {
898 result,
899 headers,
900 status
901}) {
902 let resultHeaders = new Headers(headers);
903 resultHeaders.set("X-Remix-Response", "yes");
904 if (SERVER_NO_BODY_STATUS_CODES.has(status)) {
905 return new Response(null, { status, headers: resultHeaders });
906 }
907 resultHeaders.set("Content-Type", "text/x-script");
908 resultHeaders.delete("Content-Length");
909 return new Response(
910 encodeViaTurboStream(
911 result,
912 request.signal,
913 build.entry.module.streamTimeout,
914 serverMode
915 ),
916 {
917 status: status || 200,
918 headers: resultHeaders
919 }
920 );
921}
922function generateSingleFetchRedirectResponse(redirectResponse, request, build, serverMode) {
923 let redirect2 = getSingleFetchRedirect(
924 redirectResponse.status,
925 redirectResponse.headers,
926 build.basename
927 );
928 let headers = new Headers(redirectResponse.headers);
929 headers.delete("Location");
930 headers.set("Content-Type", "text/x-script");
931 return generateSingleFetchResponse(request, build, serverMode, {
932 result: request.method === "GET" ? { [SingleFetchRedirectSymbol]: redirect2 } : redirect2,
933 headers,
934 status: SINGLE_FETCH_REDIRECT_STATUS
935 });
936}
937function getSingleFetchRedirect(status, headers, basename) {
938 let redirect2 = headers.get("Location");
939 if (basename) {
940 redirect2 = stripBasename(redirect2, basename) || redirect2;
941 }
942 return {
943 redirect: redirect2,
944 status,
945 revalidate: (
946 // Technically X-Remix-Revalidate isn't needed here - that was an implementation
947 // detail of ?_data requests as our way to tell the front end to revalidate when
948 // we didn't have a response body to include that information in.
949 // With single fetch, we tell the front end via this revalidate boolean field.
950 // However, we're respecting it for now because it may be something folks have
951 // used in their own responses
952 // TODO(v3): Consider removing or making this official public API
953 headers.has("X-Remix-Revalidate") || headers.has("Set-Cookie")
954 ),
955 reload: headers.has("X-Remix-Reload-Document"),
956 replace: headers.has("X-Remix-Replace")
957 };
958}
959function encodeViaTurboStream(data2, requestSignal, streamTimeout, serverMode) {
960 let controller = new AbortController();
961 let timeoutId = setTimeout(
962 () => controller.abort(new Error("Server Timeout")),
963 typeof streamTimeout === "number" ? streamTimeout : 4950
964 );
965 requestSignal.addEventListener("abort", () => clearTimeout(timeoutId));
966 return encode(data2, {
967 signal: controller.signal,
968 plugins: [
969 (value) => {
970 if (value instanceof Error) {
971 let { name, message, stack } = serverMode === "production" /* Production */ ? sanitizeError(value, serverMode) : value;
972 return ["SanitizedError", name, message, stack];
973 }
974 if (value instanceof ErrorResponseImpl) {
975 let { data: data3, status, statusText } = value;
976 return ["ErrorResponse", data3, status, statusText];
977 }
978 if (value && typeof value === "object" && SingleFetchRedirectSymbol in value) {
979 return ["SingleFetchRedirect", value[SingleFetchRedirectSymbol]];
980 }
981 }
982 ],
983 postPlugins: [
984 (value) => {
985 if (!value) return;
986 if (typeof value !== "object") return;
987 return [
988 "SingleFetchClassInstance",
989 Object.fromEntries(Object.entries(value))
990 ];
991 },
992 () => ["SingleFetchFallback"]
993 ]
994 });
995}
996
997// lib/server-runtime/server.ts
998function derive(build, mode) {
999 let routes = createRoutes(build.routes);
1000 let dataRoutes = createStaticHandlerDataRoutes(build.routes, build.future);
1001 let serverMode = isServerMode(mode) ? mode : "production" /* Production */;
1002 let staticHandler = createStaticHandler(dataRoutes, {
1003 basename: build.basename,
1004 unstable_instrumentations: build.entry.module.unstable_instrumentations
1005 });
1006 let errorHandler = build.entry.module.handleError || ((error, { request }) => {
1007 if (serverMode !== "test" /* Test */ && !request.signal.aborted) {
1008 console.error(
1009 // @ts-expect-error This is "private" from users but intended for internal use
1010 isRouteErrorResponse(error) && error.error ? error.error : error
1011 );
1012 }
1013 });
1014 let requestHandler = async (request, initialContext) => {
1015 let params = {};
1016 let loadContext;
1017 let handleError = (error) => {
1018 if (mode === "development" /* Development */) {
1019 getDevServerHooks()?.processRequestError?.(error);
1020 }
1021 errorHandler(error, {
1022 context: loadContext,
1023 params,
1024 request
1025 });
1026 };
1027 if (build.future.v8_middleware) {
1028 if (initialContext && !(initialContext instanceof RouterContextProvider)) {
1029 let error = new Error(
1030 "Invalid `context` value provided to `handleRequest`. When middleware is enabled you must return an instance of `RouterContextProvider` from your `getLoadContext` function."
1031 );
1032 handleError(error);
1033 return returnLastResortErrorResponse(error, serverMode);
1034 }
1035 loadContext = initialContext || new RouterContextProvider();
1036 } else {
1037 loadContext = initialContext || {};
1038 }
1039 let url = new URL(request.url);
1040 let normalizedBasename = build.basename || "/";
1041 let normalizedPath = url.pathname;
1042 if (stripBasename(normalizedPath, normalizedBasename) === "/_root.data") {
1043 normalizedPath = normalizedBasename;
1044 } else if (normalizedPath.endsWith(".data")) {
1045 normalizedPath = normalizedPath.replace(/\.data$/, "");
1046 }
1047 if (stripBasename(normalizedPath, normalizedBasename) !== "/" && normalizedPath.endsWith("/")) {
1048 normalizedPath = normalizedPath.slice(0, -1);
1049 }
1050 let isSpaMode = getBuildTimeHeader(request, "X-React-Router-SPA-Mode") === "yes";
1051 if (!build.ssr) {
1052 let decodedPath = decodeURI(normalizedPath);
1053 if (normalizedBasename !== "/") {
1054 let strippedPath = stripBasename(decodedPath, normalizedBasename);
1055 if (strippedPath == null) {
1056 errorHandler(
1057 new ErrorResponseImpl(
1058 404,
1059 "Not Found",
1060 `Refusing to prerender the \`${decodedPath}\` path because it does not start with the basename \`${normalizedBasename}\``
1061 ),
1062 {
1063 context: loadContext,
1064 params,
1065 request
1066 }
1067 );
1068 return new Response("Not Found", {
1069 status: 404,
1070 statusText: "Not Found"
1071 });
1072 }
1073 decodedPath = strippedPath;
1074 }
1075 if (build.prerender.length === 0) {
1076 isSpaMode = true;
1077 } else if (!build.prerender.includes(decodedPath) && !build.prerender.includes(decodedPath + "/")) {
1078 if (url.pathname.endsWith(".data")) {
1079 errorHandler(
1080 new ErrorResponseImpl(
1081 404,
1082 "Not Found",
1083 `Refusing to SSR the path \`${decodedPath}\` because \`ssr:false\` is set and the path is not included in the \`prerender\` config, so in production the path will be a 404.`
1084 ),
1085 {
1086 context: loadContext,
1087 params,
1088 request
1089 }
1090 );
1091 return new Response("Not Found", {
1092 status: 404,
1093 statusText: "Not Found"
1094 });
1095 } else {
1096 isSpaMode = true;
1097 }
1098 }
1099 }
1100 let manifestUrl = getManifestPath(
1101 build.routeDiscovery.manifestPath,
1102 normalizedBasename
1103 );
1104 if (url.pathname === manifestUrl) {
1105 try {
1106 let res = await handleManifestRequest(build, routes, url);
1107 return res;
1108 } catch (e) {
1109 handleError(e);
1110 return new Response("Unknown Server Error", { status: 500 });
1111 }
1112 }
1113 let matches = matchServerRoutes(routes, normalizedPath, build.basename);
1114 if (matches && matches.length > 0) {
1115 Object.assign(params, matches[0].params);
1116 }
1117 let response;
1118 if (url.pathname.endsWith(".data")) {
1119 let handlerUrl = new URL(request.url);
1120 handlerUrl.pathname = normalizedPath;
1121 let singleFetchMatches = matchServerRoutes(
1122 routes,
1123 handlerUrl.pathname,
1124 build.basename
1125 );
1126 response = await handleSingleFetchRequest(
1127 serverMode,
1128 build,
1129 staticHandler,
1130 request,
1131 handlerUrl,
1132 loadContext,
1133 handleError
1134 );
1135 if (isRedirectResponse(response)) {
1136 response = generateSingleFetchRedirectResponse(
1137 response,
1138 request,
1139 build,
1140 serverMode
1141 );
1142 }
1143 if (build.entry.module.handleDataRequest) {
1144 response = await build.entry.module.handleDataRequest(response, {
1145 context: loadContext,
1146 params: singleFetchMatches ? singleFetchMatches[0].params : {},
1147 request
1148 });
1149 if (isRedirectResponse(response)) {
1150 response = generateSingleFetchRedirectResponse(
1151 response,
1152 request,
1153 build,
1154 serverMode
1155 );
1156 }
1157 }
1158 } else if (!isSpaMode && matches && matches[matches.length - 1].route.module.default == null && matches[matches.length - 1].route.module.ErrorBoundary == null) {
1159 response = await handleResourceRequest(
1160 serverMode,
1161 build,
1162 staticHandler,
1163 matches.slice(-1)[0].route.id,
1164 request,
1165 loadContext,
1166 handleError
1167 );
1168 } else {
1169 let { pathname } = url;
1170 let criticalCss = void 0;
1171 if (build.unstable_getCriticalCss) {
1172 criticalCss = await build.unstable_getCriticalCss({ pathname });
1173 } else if (mode === "development" /* Development */ && getDevServerHooks()?.getCriticalCss) {
1174 criticalCss = await getDevServerHooks()?.getCriticalCss?.(pathname);
1175 }
1176 response = await handleDocumentRequest(
1177 serverMode,
1178 build,
1179 staticHandler,
1180 request,
1181 loadContext,
1182 handleError,
1183 isSpaMode,
1184 criticalCss
1185 );
1186 }
1187 if (request.method === "HEAD") {
1188 return new Response(null, {
1189 headers: response.headers,
1190 status: response.status,
1191 statusText: response.statusText
1192 });
1193 }
1194 return response;
1195 };
1196 if (build.entry.module.unstable_instrumentations) {
1197 requestHandler = instrumentHandler(
1198 requestHandler,
1199 build.entry.module.unstable_instrumentations.map((i) => i.handler).filter(Boolean)
1200 );
1201 }
1202 return {
1203 routes,
1204 dataRoutes,
1205 serverMode,
1206 staticHandler,
1207 errorHandler,
1208 requestHandler
1209 };
1210}
1211var createRequestHandler = (build, mode) => {
1212 let _build;
1213 let routes;
1214 let serverMode;
1215 let staticHandler;
1216 let errorHandler;
1217 let _requestHandler;
1218 return async function requestHandler(request, initialContext) {
1219 _build = typeof build === "function" ? await build() : build;
1220 if (typeof build === "function") {
1221 let derived = derive(_build, mode);
1222 routes = derived.routes;
1223 serverMode = derived.serverMode;
1224 staticHandler = derived.staticHandler;
1225 errorHandler = derived.errorHandler;
1226 _requestHandler = derived.requestHandler;
1227 } else if (!routes || !serverMode || !staticHandler || !errorHandler || !_requestHandler) {
1228 let derived = derive(_build, mode);
1229 routes = derived.routes;
1230 serverMode = derived.serverMode;
1231 staticHandler = derived.staticHandler;
1232 errorHandler = derived.errorHandler;
1233 _requestHandler = derived.requestHandler;
1234 }
1235 return _requestHandler(request, initialContext);
1236 };
1237};
1238async function handleManifestRequest(build, routes, url) {
1239 if (build.assets.version !== url.searchParams.get("version")) {
1240 return new Response(null, {
1241 status: 204,
1242 headers: {
1243 "X-Remix-Reload-Document": "true"
1244 }
1245 });
1246 }
1247 let patches = {};
1248 if (url.searchParams.has("paths")) {
1249 let paths = /* @__PURE__ */ new Set();
1250 let pathParam = url.searchParams.get("paths") || "";
1251 let requestedPaths = pathParam.split(",").filter(Boolean);
1252 requestedPaths.forEach((path) => {
1253 if (!path.startsWith("/")) {
1254 path = `/${path}`;
1255 }
1256 let segments = path.split("/").slice(1);
1257 segments.forEach((_, i) => {
1258 let partialPath = segments.slice(0, i + 1).join("/");
1259 paths.add(`/${partialPath}`);
1260 });
1261 });
1262 for (let path of paths) {
1263 let matches = matchServerRoutes(routes, path, build.basename);
1264 if (matches) {
1265 for (let match of matches) {
1266 let routeId = match.route.id;
1267 let route = build.assets.routes[routeId];
1268 if (route) {
1269 patches[routeId] = route;
1270 }
1271 }
1272 }
1273 }
1274 return Response.json(patches, {
1275 headers: {
1276 "Cache-Control": "public, max-age=31536000, immutable"
1277 }
1278 });
1279 }
1280 return new Response("Invalid Request", { status: 400 });
1281}
1282async function handleSingleFetchRequest(serverMode, build, staticHandler, request, handlerUrl, loadContext, handleError) {
1283 let response = request.method !== "GET" ? await singleFetchAction(
1284 build,
1285 serverMode,
1286 staticHandler,
1287 request,
1288 handlerUrl,
1289 loadContext,
1290 handleError
1291 ) : await singleFetchLoaders(
1292 build,
1293 serverMode,
1294 staticHandler,
1295 request,
1296 handlerUrl,
1297 loadContext,
1298 handleError
1299 );
1300 return response;
1301}
1302async function handleDocumentRequest(serverMode, build, staticHandler, request, loadContext, handleError, isSpaMode, criticalCss) {
1303 try {
1304 let result = await staticHandler.query(request, {
1305 requestContext: loadContext,
1306 generateMiddlewareResponse: build.future.v8_middleware ? async (query) => {
1307 try {
1308 let innerResult = await query(request);
1309 if (!isResponse(innerResult)) {
1310 innerResult = await renderHtml(innerResult, isSpaMode);
1311 }
1312 return innerResult;
1313 } catch (error) {
1314 handleError(error);
1315 return new Response(null, { status: 500 });
1316 }
1317 } : void 0
1318 });
1319 if (!isResponse(result)) {
1320 result = await renderHtml(result, isSpaMode);
1321 }
1322 return result;
1323 } catch (error) {
1324 handleError(error);
1325 return new Response(null, { status: 500 });
1326 }
1327 async function renderHtml(context, isSpaMode2) {
1328 let headers = getDocumentHeaders(context, build);
1329 if (SERVER_NO_BODY_STATUS_CODES.has(context.statusCode)) {
1330 return new Response(null, { status: context.statusCode, headers });
1331 }
1332 if (context.errors) {
1333 Object.values(context.errors).forEach((err) => {
1334 if (!isRouteErrorResponse(err) || err.error) {
1335 handleError(err);
1336 }
1337 });
1338 context.errors = sanitizeErrors(context.errors, serverMode);
1339 }
1340 let state = {
1341 loaderData: context.loaderData,
1342 actionData: context.actionData,
1343 errors: serializeErrors(context.errors, serverMode)
1344 };
1345 let baseServerHandoff = {
1346 basename: build.basename,
1347 future: build.future,
1348 routeDiscovery: build.routeDiscovery,
1349 ssr: build.ssr,
1350 isSpaMode: isSpaMode2
1351 };
1352 let entryContext = {
1353 manifest: build.assets,
1354 routeModules: createEntryRouteModules(build.routes),
1355 staticHandlerContext: context,
1356 criticalCss,
1357 serverHandoffString: createServerHandoffString({
1358 ...baseServerHandoff,
1359 criticalCss
1360 }),
1361 serverHandoffStream: encodeViaTurboStream(
1362 state,
1363 request.signal,
1364 build.entry.module.streamTimeout,
1365 serverMode
1366 ),
1367 renderMeta: {},
1368 future: build.future,
1369 ssr: build.ssr,
1370 routeDiscovery: build.routeDiscovery,
1371 isSpaMode: isSpaMode2,
1372 serializeError: (err) => serializeError(err, serverMode)
1373 };
1374 let handleDocumentRequestFunction = build.entry.module.default;
1375 try {
1376 return await handleDocumentRequestFunction(
1377 request,
1378 context.statusCode,
1379 headers,
1380 entryContext,
1381 loadContext
1382 );
1383 } catch (error) {
1384 handleError(error);
1385 let errorForSecondRender = error;
1386 if (isResponse(error)) {
1387 try {
1388 let data2 = await unwrapResponse(error);
1389 errorForSecondRender = new ErrorResponseImpl(
1390 error.status,
1391 error.statusText,
1392 data2
1393 );
1394 } catch (e) {
1395 }
1396 }
1397 context = getStaticContextFromError(
1398 staticHandler.dataRoutes,
1399 context,
1400 errorForSecondRender
1401 );
1402 if (context.errors) {
1403 context.errors = sanitizeErrors(context.errors, serverMode);
1404 }
1405 let state2 = {
1406 loaderData: context.loaderData,
1407 actionData: context.actionData,
1408 errors: serializeErrors(context.errors, serverMode)
1409 };
1410 entryContext = {
1411 ...entryContext,
1412 staticHandlerContext: context,
1413 serverHandoffString: createServerHandoffString(baseServerHandoff),
1414 serverHandoffStream: encodeViaTurboStream(
1415 state2,
1416 request.signal,
1417 build.entry.module.streamTimeout,
1418 serverMode
1419 ),
1420 renderMeta: {}
1421 };
1422 try {
1423 return await handleDocumentRequestFunction(
1424 request,
1425 context.statusCode,
1426 headers,
1427 entryContext,
1428 loadContext
1429 );
1430 } catch (error2) {
1431 handleError(error2);
1432 return returnLastResortErrorResponse(error2, serverMode);
1433 }
1434 }
1435 }
1436}
1437async function handleResourceRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
1438 try {
1439 let result = await staticHandler.queryRoute(request, {
1440 routeId,
1441 requestContext: loadContext,
1442 generateMiddlewareResponse: build.future.v8_middleware ? async (queryRoute) => {
1443 try {
1444 let innerResult = await queryRoute(request);
1445 return handleQueryRouteResult(innerResult);
1446 } catch (error) {
1447 return handleQueryRouteError(error);
1448 }
1449 } : void 0
1450 });
1451 return handleQueryRouteResult(result);
1452 } catch (error) {
1453 return handleQueryRouteError(error);
1454 }
1455 function handleQueryRouteResult(result) {
1456 if (isResponse(result)) {
1457 return result;
1458 }
1459 if (typeof result === "string") {
1460 return new Response(result);
1461 }
1462 return Response.json(result);
1463 }
1464 function handleQueryRouteError(error) {
1465 if (isResponse(error)) {
1466 return error;
1467 }
1468 if (isRouteErrorResponse(error)) {
1469 handleError(error);
1470 return errorResponseToJson(error, serverMode);
1471 }
1472 if (error instanceof Error && error.message === "Expected a response from queryRoute") {
1473 let newError = new Error(
1474 "Expected a Response to be returned from resource route handler"
1475 );
1476 handleError(newError);
1477 return returnLastResortErrorResponse(newError, serverMode);
1478 }
1479 handleError(error);
1480 return returnLastResortErrorResponse(error, serverMode);
1481 }
1482}
1483function errorResponseToJson(errorResponse, serverMode) {
1484 return Response.json(
1485 serializeError(
1486 // @ts-expect-error This is "private" from users but intended for internal use
1487 errorResponse.error || new Error("Unexpected Server Error"),
1488 serverMode
1489 ),
1490 {
1491 status: errorResponse.status,
1492 statusText: errorResponse.statusText
1493 }
1494 );
1495}
1496function returnLastResortErrorResponse(error, serverMode) {
1497 let message = "Unexpected Server Error";
1498 if (serverMode !== "production" /* Production */) {
1499 message += `
1500
1501${String(error)}`;
1502 }
1503 return new Response(message, {
1504 status: 500,
1505 headers: {
1506 "Content-Type": "text/plain"
1507 }
1508 });
1509}
1510function unwrapResponse(response) {
1511 let contentType = response.headers.get("Content-Type");
1512 return contentType && /\bapplication\/json\b/.test(contentType) ? response.body == null ? null : response.json() : response.text();
1513}
1514
1515// lib/server-runtime/sessions.ts
1516function flash(name) {
1517 return `__flash_${name}__`;
1518}
1519var createSession = (initialData = {}, id = "") => {
1520 let map = new Map(Object.entries(initialData));
1521 return {
1522 get id() {
1523 return id;
1524 },
1525 get data() {
1526 return Object.fromEntries(map);
1527 },
1528 has(name) {
1529 return map.has(name) || map.has(flash(name));
1530 },
1531 get(name) {
1532 if (map.has(name)) return map.get(name);
1533 let flashName = flash(name);
1534 if (map.has(flashName)) {
1535 let value = map.get(flashName);
1536 map.delete(flashName);
1537 return value;
1538 }
1539 return void 0;
1540 },
1541 set(name, value) {
1542 map.set(name, value);
1543 },
1544 flash(name, value) {
1545 map.set(flash(name), value);
1546 },
1547 unset(name) {
1548 map.delete(name);
1549 }
1550 };
1551};
1552var isSession = (object) => {
1553 return object != null && typeof object.id === "string" && typeof object.data !== "undefined" && typeof object.has === "function" && typeof object.get === "function" && typeof object.set === "function" && typeof object.flash === "function" && typeof object.unset === "function";
1554};
1555function createSessionStorage({
1556 cookie: cookieArg,
1557 createData,
1558 readData,
1559 updateData,
1560 deleteData
1561}) {
1562 let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
1563 warnOnceAboutSigningSessionCookie(cookie);
1564 return {
1565 async getSession(cookieHeader, options) {
1566 let id = cookieHeader && await cookie.parse(cookieHeader, options);
1567 let data2 = id && await readData(id);
1568 return createSession(data2 || {}, id || "");
1569 },
1570 async commitSession(session, options) {
1571 let { id, data: data2 } = session;
1572 let expires = options?.maxAge != null ? new Date(Date.now() + options.maxAge * 1e3) : options?.expires != null ? options.expires : cookie.expires;
1573 if (id) {
1574 await updateData(id, data2, expires);
1575 } else {
1576 id = await createData(data2, expires);
1577 }
1578 return cookie.serialize(id, options);
1579 },
1580 async destroySession(session, options) {
1581 await deleteData(session.id);
1582 return cookie.serialize("", {
1583 ...options,
1584 maxAge: void 0,
1585 expires: /* @__PURE__ */ new Date(0)
1586 });
1587 }
1588 };
1589}
1590function warnOnceAboutSigningSessionCookie(cookie) {
1591 warnOnce(
1592 cookie.isSigned,
1593 `The "${cookie.name}" cookie is not signed, but session cookies should be signed to prevent tampering on the client before they are sent back to the server. See https://reactrouter.com/explanation/sessions-and-cookies#signing-cookies for more information.`
1594 );
1595}
1596
1597// lib/server-runtime/sessions/cookieStorage.ts
1598function createCookieSessionStorage({ cookie: cookieArg } = {}) {
1599 let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
1600 warnOnceAboutSigningSessionCookie(cookie);
1601 return {
1602 async getSession(cookieHeader, options) {
1603 return createSession(
1604 cookieHeader && await cookie.parse(cookieHeader, options) || {}
1605 );
1606 },
1607 async commitSession(session, options) {
1608 let serializedCookie = await cookie.serialize(session.data, options);
1609 if (serializedCookie.length > 4096) {
1610 throw new Error(
1611 "Cookie length will exceed browser maximum. Length: " + serializedCookie.length
1612 );
1613 }
1614 return serializedCookie;
1615 },
1616 async destroySession(_session, options) {
1617 return cookie.serialize("", {
1618 ...options,
1619 maxAge: void 0,
1620 expires: /* @__PURE__ */ new Date(0)
1621 });
1622 }
1623 };
1624}
1625
1626// lib/server-runtime/sessions/memoryStorage.ts
1627function createMemorySessionStorage({ cookie } = {}) {
1628 let map = /* @__PURE__ */ new Map();
1629 return createSessionStorage({
1630 cookie,
1631 async createData(data2, expires) {
1632 let id = Math.random().toString(36).substring(2, 10);
1633 map.set(id, { data: data2, expires });
1634 return id;
1635 },
1636 async readData(id) {
1637 if (map.has(id)) {
1638 let { data: data2, expires } = map.get(id);
1639 if (!expires || expires > /* @__PURE__ */ new Date()) {
1640 return data2;
1641 }
1642 if (expires) map.delete(id);
1643 }
1644 return null;
1645 },
1646 async updateData(id, data2, expires) {
1647 map.set(id, { data: data2, expires });
1648 },
1649 async deleteData(id) {
1650 map.delete(id);
1651 }
1652 });
1653}
1654
1655// lib/href.ts
1656function href(path, ...args) {
1657 let params = args[0];
1658 let result = trimTrailingSplat(path).replace(
1659 /\/:([\w-]+)(\?)?/g,
1660 // same regex as in .\router\utils.ts: compilePath().
1661 (_, param, questionMark) => {
1662 const isRequired = questionMark === void 0;
1663 const value = params?.[param];
1664 if (isRequired && value === void 0) {
1665 throw new Error(
1666 `Path '${path}' requires param '${param}' but it was not provided`
1667 );
1668 }
1669 return value === void 0 ? "" : "/" + value;
1670 }
1671 );
1672 if (path.endsWith("*")) {
1673 const value = params?.["*"];
1674 if (value !== void 0) {
1675 result += "/" + value;
1676 }
1677 }
1678 return result || "/";
1679}
1680function trimTrailingSplat(path) {
1681 let i = path.length - 1;
1682 let char = path[i];
1683 if (char !== "*" && char !== "/") return path;
1684 i--;
1685 for (; i >= 0; i--) {
1686 if (path[i] !== "/") break;
1687 }
1688 return path.slice(0, i + 1);
1689}
1690
1691// lib/rsc/server.ssr.tsx
1692import * as React4 from "react";
1693
1694// lib/rsc/html-stream/server.ts
1695var encoder2 = new TextEncoder();
1696var trailer = "</body></html>";
1697function injectRSCPayload(rscStream) {
1698 let decoder = new TextDecoder();
1699 let resolveFlightDataPromise;
1700 let flightDataPromise = new Promise(
1701 (resolve) => resolveFlightDataPromise = resolve
1702 );
1703 let startedRSC = false;
1704 let buffered = [];
1705 let timeout = null;
1706 function flushBufferedChunks(controller) {
1707 for (let chunk of buffered) {
1708 let buf = decoder.decode(chunk, { stream: true });
1709 if (buf.endsWith(trailer)) {
1710 buf = buf.slice(0, -trailer.length);
1711 }
1712 controller.enqueue(encoder2.encode(buf));
1713 }
1714 buffered.length = 0;
1715 timeout = null;
1716 }
1717 return new TransformStream({
1718 transform(chunk, controller) {
1719 buffered.push(chunk);
1720 if (timeout) {
1721 return;
1722 }
1723 timeout = setTimeout(async () => {
1724 flushBufferedChunks(controller);
1725 if (!startedRSC) {
1726 startedRSC = true;
1727 writeRSCStream(rscStream, controller).catch((err) => controller.error(err)).then(resolveFlightDataPromise);
1728 }
1729 }, 0);
1730 },
1731 async flush(controller) {
1732 await flightDataPromise;
1733 if (timeout) {
1734 clearTimeout(timeout);
1735 flushBufferedChunks(controller);
1736 }
1737 controller.enqueue(encoder2.encode("</body></html>"));
1738 }
1739 });
1740}
1741async function writeRSCStream(rscStream, controller) {
1742 let decoder = new TextDecoder("utf-8", { fatal: true });
1743 const reader = rscStream.getReader();
1744 try {
1745 let read;
1746 while ((read = await reader.read()) && !read.done) {
1747 const chunk = read.value;
1748 try {
1749 writeChunk(
1750 JSON.stringify(decoder.decode(chunk, { stream: true })),
1751 controller
1752 );
1753 } catch (err) {
1754 let base64 = JSON.stringify(btoa(String.fromCodePoint(...chunk)));
1755 writeChunk(
1756 `Uint8Array.from(atob(${base64}), m => m.codePointAt(0))`,
1757 controller
1758 );
1759 }
1760 }
1761 } finally {
1762 reader.releaseLock();
1763 }
1764 let remaining = decoder.decode();
1765 if (remaining.length) {
1766 writeChunk(JSON.stringify(remaining), controller);
1767 }
1768}
1769function writeChunk(chunk, controller) {
1770 controller.enqueue(
1771 encoder2.encode(
1772 `<script>${escapeScript(
1773 `(self.__FLIGHT_DATA||=[]).push(${chunk})`
1774 )}</script>`
1775 )
1776 );
1777}
1778function escapeScript(script) {
1779 return script.replace(/<!--/g, "<\\!--").replace(/<\/(script)/gi, "</\\$1");
1780}
1781
1782// lib/rsc/errorBoundaries.tsx
1783import React3 from "react";
1784var RSCRouterGlobalErrorBoundary = class extends React3.Component {
1785 constructor(props) {
1786 super(props);
1787 this.state = { error: null, location: props.location };
1788 }
1789 static getDerivedStateFromError(error) {
1790 return { error };
1791 }
1792 static getDerivedStateFromProps(props, state) {
1793 if (state.location !== props.location) {
1794 return { error: null, location: props.location };
1795 }
1796 return { error: state.error, location: state.location };
1797 }
1798 render() {
1799 if (this.state.error) {
1800 return /* @__PURE__ */ React3.createElement(
1801 RSCDefaultRootErrorBoundaryImpl,
1802 {
1803 error: this.state.error,
1804 renderAppShell: true
1805 }
1806 );
1807 } else {
1808 return this.props.children;
1809 }
1810 }
1811};
1812function ErrorWrapper({
1813 renderAppShell,
1814 title,
1815 children
1816}) {
1817 if (!renderAppShell) {
1818 return children;
1819 }
1820 return /* @__PURE__ */ React3.createElement("html", { lang: "en" }, /* @__PURE__ */ React3.createElement("head", null, /* @__PURE__ */ React3.createElement("meta", { charSet: "utf-8" }), /* @__PURE__ */ React3.createElement(
1821 "meta",
1822 {
1823 name: "viewport",
1824 content: "width=device-width,initial-scale=1,viewport-fit=cover"
1825 }
1826 ), /* @__PURE__ */ React3.createElement("title", null, title)), /* @__PURE__ */ React3.createElement("body", null, /* @__PURE__ */ React3.createElement("main", { style: { fontFamily: "system-ui, sans-serif", padding: "2rem" } }, children)));
1827}
1828function RSCDefaultRootErrorBoundaryImpl({
1829 error,
1830 renderAppShell
1831}) {
1832 console.error(error);
1833 let heyDeveloper = /* @__PURE__ */ React3.createElement(
1834 "script",
1835 {
1836 dangerouslySetInnerHTML: {
1837 __html: `
1838 console.log(
1839 "\u{1F4BF} Hey developer \u{1F44B}. You can provide a way better UX than this when your app throws errors. Check out https://reactrouter.com/how-to/error-boundary for more information."
1840 );
1841 `
1842 }
1843 }
1844 );
1845 if (isRouteErrorResponse(error)) {
1846 return /* @__PURE__ */ React3.createElement(
1847 ErrorWrapper,
1848 {
1849 renderAppShell,
1850 title: "Unhandled Thrown Response!"
1851 },
1852 /* @__PURE__ */ React3.createElement("h1", { style: { fontSize: "24px" } }, error.status, " ", error.statusText),
1853 ENABLE_DEV_WARNINGS ? heyDeveloper : null
1854 );
1855 }
1856 let errorInstance;
1857 if (error instanceof Error) {
1858 errorInstance = error;
1859 } else {
1860 let errorString = error == null ? "Unknown Error" : typeof error === "object" && "toString" in error ? error.toString() : JSON.stringify(error);
1861 errorInstance = new Error(errorString);
1862 }
1863 return /* @__PURE__ */ React3.createElement(ErrorWrapper, { renderAppShell, title: "Application Error!" }, /* @__PURE__ */ React3.createElement("h1", { style: { fontSize: "24px" } }, "Application Error"), /* @__PURE__ */ React3.createElement(
1864 "pre",
1865 {
1866 style: {
1867 padding: "2rem",
1868 background: "hsla(10, 50%, 50%, 0.1)",
1869 color: "red",
1870 overflow: "auto"
1871 }
1872 },
1873 errorInstance.stack
1874 ), heyDeveloper);
1875}
1876function RSCDefaultRootErrorBoundary({
1877 hasRootLayout
1878}) {
1879 let error = useRouteError();
1880 if (hasRootLayout === void 0) {
1881 throw new Error("Missing 'hasRootLayout' prop");
1882 }
1883 return /* @__PURE__ */ React3.createElement(
1884 RSCDefaultRootErrorBoundaryImpl,
1885 {
1886 renderAppShell: !hasRootLayout,
1887 error
1888 }
1889 );
1890}
1891
1892// lib/rsc/route-modules.ts
1893function createRSCRouteModules(payload) {
1894 const routeModules = {};
1895 for (const match of payload.matches) {
1896 populateRSCRouteModules(routeModules, match);
1897 }
1898 return routeModules;
1899}
1900function populateRSCRouteModules(routeModules, matches) {
1901 matches = Array.isArray(matches) ? matches : [matches];
1902 for (const match of matches) {
1903 routeModules[match.id] = {
1904 links: match.links,
1905 meta: match.meta,
1906 default: noopComponent
1907 };
1908 }
1909}
1910var noopComponent = () => null;
1911
1912// lib/rsc/server.ssr.tsx
1913var REACT_USE = "use";
1914var useImpl = React4[REACT_USE];
1915function useSafe(promise) {
1916 if (useImpl) {
1917 return useImpl(promise);
1918 }
1919 throw new Error("React Router v7 requires React 19+ for RSC features.");
1920}
1921async function routeRSCServerRequest({
1922 request,
1923 fetchServer,
1924 createFromReadableStream,
1925 renderHTML,
1926 hydrate = true
1927}) {
1928 const url = new URL(request.url);
1929 const isDataRequest = isReactServerRequest(url);
1930 const respondWithRSCPayload = isDataRequest || isManifestRequest(url) || request.headers.has("rsc-action-id");
1931 const serverResponse = await fetchServer(request);
1932 if (respondWithRSCPayload || serverResponse.headers.get("React-Router-Resource") === "true") {
1933 return serverResponse;
1934 }
1935 if (!serverResponse.body) {
1936 throw new Error("Missing body in server response");
1937 }
1938 const detectRedirectResponse = serverResponse.clone();
1939 let serverResponseB = null;
1940 if (hydrate) {
1941 serverResponseB = serverResponse.clone();
1942 }
1943 const body = serverResponse.body;
1944 let buffer;
1945 let streamControllers = [];
1946 const createStream = () => {
1947 if (!buffer) {
1948 buffer = [];
1949 return body.pipeThrough(
1950 new TransformStream({
1951 transform(chunk, controller) {
1952 buffer.push(chunk);
1953 controller.enqueue(chunk);
1954 streamControllers.forEach((c) => c.enqueue(chunk));
1955 },
1956 flush() {
1957 streamControllers.forEach((c) => c.close());
1958 streamControllers = [];
1959 }
1960 })
1961 );
1962 }
1963 return new ReadableStream({
1964 start(controller) {
1965 buffer.forEach((chunk) => controller.enqueue(chunk));
1966 streamControllers.push(controller);
1967 }
1968 });
1969 };
1970 let deepestRenderedBoundaryId = null;
1971 const getPayload = () => {
1972 const payloadPromise = Promise.resolve(
1973 createFromReadableStream(createStream())
1974 );
1975 return Object.defineProperties(payloadPromise, {
1976 _deepestRenderedBoundaryId: {
1977 get() {
1978 return deepestRenderedBoundaryId;
1979 },
1980 set(boundaryId) {
1981 deepestRenderedBoundaryId = boundaryId;
1982 }
1983 },
1984 formState: {
1985 get() {
1986 return payloadPromise.then(
1987 (payload) => payload.type === "render" ? payload.formState : void 0
1988 );
1989 }
1990 }
1991 });
1992 };
1993 try {
1994 if (!detectRedirectResponse.body) {
1995 throw new Error("Failed to clone server response");
1996 }
1997 const payload = await createFromReadableStream(
1998 detectRedirectResponse.body
1999 );
2000 if (serverResponse.status === SINGLE_FETCH_REDIRECT_STATUS && payload.type === "redirect") {
2001 const headers2 = new Headers(serverResponse.headers);
2002 headers2.delete("Content-Encoding");
2003 headers2.delete("Content-Length");
2004 headers2.delete("Content-Type");
2005 headers2.delete("X-Remix-Response");
2006 headers2.set("Location", payload.location);
2007 return new Response(serverResponseB?.body || "", {
2008 headers: headers2,
2009 status: payload.status,
2010 statusText: serverResponse.statusText
2011 });
2012 }
2013 const html = await renderHTML(getPayload);
2014 const headers = new Headers(serverResponse.headers);
2015 headers.set("Content-Type", "text/html; charset=utf-8");
2016 if (!hydrate) {
2017 return new Response(html, {
2018 status: serverResponse.status,
2019 headers
2020 });
2021 }
2022 if (!serverResponseB?.body) {
2023 throw new Error("Failed to clone server response");
2024 }
2025 const body2 = html.pipeThrough(injectRSCPayload(serverResponseB.body));
2026 return new Response(body2, {
2027 status: serverResponse.status,
2028 headers
2029 });
2030 } catch (reason) {
2031 if (reason instanceof Response) {
2032 return reason;
2033 }
2034 try {
2035 const status = isRouteErrorResponse(reason) ? reason.status : 500;
2036 const html = await renderHTML(() => {
2037 const decoded = Promise.resolve(
2038 createFromReadableStream(createStream())
2039 );
2040 const payloadPromise = decoded.then(
2041 (payload) => Object.assign(payload, {
2042 status,
2043 errors: deepestRenderedBoundaryId ? {
2044 [deepestRenderedBoundaryId]: reason
2045 } : {}
2046 })
2047 );
2048 return Object.defineProperties(payloadPromise, {
2049 _deepestRenderedBoundaryId: {
2050 get() {
2051 return deepestRenderedBoundaryId;
2052 },
2053 set(boundaryId) {
2054 deepestRenderedBoundaryId = boundaryId;
2055 }
2056 },
2057 formState: {
2058 get() {
2059 return payloadPromise.then(
2060 (payload) => payload.type === "render" ? payload.formState : void 0
2061 );
2062 }
2063 }
2064 });
2065 });
2066 const headers = new Headers(serverResponse.headers);
2067 headers.set("Content-Type", "text/html");
2068 if (!hydrate) {
2069 return new Response(html, {
2070 status,
2071 headers
2072 });
2073 }
2074 if (!serverResponseB?.body) {
2075 throw new Error("Failed to clone server response");
2076 }
2077 const body2 = html.pipeThrough(injectRSCPayload(serverResponseB.body));
2078 return new Response(body2, {
2079 status,
2080 headers
2081 });
2082 } catch {
2083 }
2084 throw reason;
2085 }
2086}
2087function RSCStaticRouter({ getPayload }) {
2088 const decoded = getPayload();
2089 const payload = useSafe(decoded);
2090 if (payload.type === "redirect") {
2091 throw new Response(null, {
2092 status: payload.status,
2093 headers: {
2094 Location: payload.location
2095 }
2096 });
2097 }
2098 if (payload.type !== "render") return null;
2099 let patchedLoaderData = { ...payload.loaderData };
2100 for (const match of payload.matches) {
2101 if (shouldHydrateRouteLoader(
2102 match.id,
2103 match.clientLoader,
2104 match.hasLoader,
2105 false
2106 ) && (match.hydrateFallbackElement || !match.hasLoader)) {
2107 delete patchedLoaderData[match.id];
2108 }
2109 }
2110 const context = {
2111 get _deepestRenderedBoundaryId() {
2112 return decoded._deepestRenderedBoundaryId ?? null;
2113 },
2114 set _deepestRenderedBoundaryId(boundaryId) {
2115 decoded._deepestRenderedBoundaryId = boundaryId;
2116 },
2117 actionData: payload.actionData,
2118 actionHeaders: {},
2119 basename: payload.basename,
2120 errors: payload.errors,
2121 loaderData: patchedLoaderData,
2122 loaderHeaders: {},
2123 location: payload.location,
2124 statusCode: 200,
2125 matches: payload.matches.map((match) => ({
2126 params: match.params,
2127 pathname: match.pathname,
2128 pathnameBase: match.pathnameBase,
2129 route: {
2130 id: match.id,
2131 action: match.hasAction || !!match.clientAction,
2132 handle: match.handle,
2133 hasErrorBoundary: match.hasErrorBoundary,
2134 loader: match.hasLoader || !!match.clientLoader,
2135 index: match.index,
2136 path: match.path,
2137 shouldRevalidate: match.shouldRevalidate
2138 }
2139 }))
2140 };
2141 const router = createStaticRouter(
2142 payload.matches.reduceRight((previous, match) => {
2143 const route = {
2144 id: match.id,
2145 action: match.hasAction || !!match.clientAction,
2146 element: match.element,
2147 errorElement: match.errorElement,
2148 handle: match.handle,
2149 hasErrorBoundary: !!match.errorElement,
2150 hydrateFallbackElement: match.hydrateFallbackElement,
2151 index: match.index,
2152 loader: match.hasLoader || !!match.clientLoader,
2153 path: match.path,
2154 shouldRevalidate: match.shouldRevalidate
2155 };
2156 if (previous.length > 0) {
2157 route.children = previous;
2158 }
2159 return [route];
2160 }, []),
2161 context
2162 );
2163 const frameworkContext = {
2164 future: {
2165 // These flags have no runtime impact so can always be false. If we add
2166 // flags that drive runtime behavior they'll need to be proxied through.
2167 v8_middleware: false,
2168 unstable_subResourceIntegrity: false
2169 },
2170 isSpaMode: false,
2171 ssr: true,
2172 criticalCss: "",
2173 manifest: {
2174 routes: {},
2175 version: "1",
2176 url: "",
2177 entry: {
2178 module: "",
2179 imports: []
2180 }
2181 },
2182 routeDiscovery: { mode: "lazy", manifestPath: "/__manifest" },
2183 routeModules: createRSCRouteModules(payload)
2184 };
2185 return /* @__PURE__ */ React4.createElement(RSCRouterContext.Provider, { value: true }, /* @__PURE__ */ React4.createElement(RSCRouterGlobalErrorBoundary, { location: payload.location }, /* @__PURE__ */ React4.createElement(FrameworkContext.Provider, { value: frameworkContext }, /* @__PURE__ */ React4.createElement(
2186 StaticRouterProvider,
2187 {
2188 context,
2189 router,
2190 hydrate: false,
2191 nonce: payload.nonce
2192 }
2193 ))));
2194}
2195function isReactServerRequest(url) {
2196 return url.pathname.endsWith(".rsc");
2197}
2198function isManifestRequest(url) {
2199 return url.pathname.endsWith(".manifest");
2200}
2201
2202// lib/dom/ssr/errors.ts
2203function deserializeErrors(errors) {
2204 if (!errors) return null;
2205 let entries = Object.entries(errors);
2206 let serialized = {};
2207 for (let [key, val] of entries) {
2208 if (val && val.__type === "RouteErrorResponse") {
2209 serialized[key] = new ErrorResponseImpl(
2210 val.status,
2211 val.statusText,
2212 val.data,
2213 val.internal === true
2214 );
2215 } else if (val && val.__type === "Error") {
2216 if (val.__subType) {
2217 let ErrorConstructor = window[val.__subType];
2218 if (typeof ErrorConstructor === "function") {
2219 try {
2220 let error = new ErrorConstructor(val.message);
2221 error.stack = val.stack;
2222 serialized[key] = error;
2223 } catch (e) {
2224 }
2225 }
2226 }
2227 if (serialized[key] == null) {
2228 let error = new Error(val.message);
2229 error.stack = val.stack;
2230 serialized[key] = error;
2231 }
2232 } else {
2233 serialized[key] = val;
2234 }
2235 }
2236 return serialized;
2237}
2238
2239// lib/dom/ssr/hydration.tsx
2240function getHydrationData({
2241 state,
2242 routes,
2243 getRouteInfo,
2244 location,
2245 basename,
2246 isSpaMode
2247}) {
2248 let hydrationData = {
2249 ...state,
2250 loaderData: { ...state.loaderData }
2251 };
2252 let initialMatches = matchRoutes(routes, location, basename);
2253 if (initialMatches) {
2254 for (let match of initialMatches) {
2255 let routeId = match.route.id;
2256 let routeInfo = getRouteInfo(routeId);
2257 if (shouldHydrateRouteLoader(
2258 routeId,
2259 routeInfo.clientLoader,
2260 routeInfo.hasLoader,
2261 isSpaMode
2262 ) && (routeInfo.hasHydrateFallback || !routeInfo.hasLoader)) {
2263 delete hydrationData.loaderData[routeId];
2264 } else if (!routeInfo.hasLoader) {
2265 hydrationData.loaderData[routeId] = null;
2266 }
2267 }
2268 }
2269 return hydrationData;
2270}
2271
2272export {
2273 ServerRouter,
2274 createRoutesStub,
2275 createCookie,
2276 isCookie,
2277 ServerMode,
2278 setDevServerHooks,
2279 createRequestHandler,
2280 createSession,
2281 isSession,
2282 createSessionStorage,
2283 createCookieSessionStorage,
2284 createMemorySessionStorage,
2285 href,
2286 RSCRouterGlobalErrorBoundary,
2287 RSCDefaultRootErrorBoundary,
2288 populateRSCRouteModules,
2289 routeRSCServerRequest,
2290 RSCStaticRouter,
2291 deserializeErrors,
2292 getHydrationData
2293};