UNPKG

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