UNPKG

123 kBJavaScriptView Raw
1/**
2 * react-router v8.0.0
3 *
4 * Copyright (c) Remix Software Inc.
5 *
6 * This source code is licensed under the MIT license found in the
7 * LICENSE.md file in the root directory of this source tree.
8 *
9 * @license MIT
10 */
11import { AsyncLocalStorage } from "node:async_hooks";
12import * as React from "react";
13import { parse, serialize, splitSetCookieString } from "cookie-es";
14import { BrowserRouter, Form, HashRouter, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Outlet, Outlet as Outlet$1, Route, Router, RouterProvider, Routes, ScrollRestoration, StaticRouter, StaticRouterProvider, UNSAFE_AwaitContextProvider, UNSAFE_WithComponentProps, UNSAFE_WithErrorBoundaryProps, UNSAFE_WithHydrateFallbackProps, unstable_HistoryRouter } from "react-router/internal/react-server-client";
15//#region lib/router/url.ts
16const ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i;
17//#endregion
18//#region lib/router/history.ts
19function invariant$1(value, message) {
20 if (value === false || value === null || typeof value === "undefined") throw new Error(message);
21}
22function warning(cond, message) {
23 if (!cond) {
24 if (typeof console !== "undefined") console.warn(message);
25 try {
26 throw new Error(message);
27 } catch (e) {}
28 }
29}
30function createKey$1() {
31 return Math.random().toString(36).substring(2, 10);
32}
33/**
34* Creates a Location object with a unique key from the given Path
35*/
36function createLocation(current, to, state = null, key, mask) {
37 return {
38 pathname: typeof current === "string" ? current : current.pathname,
39 search: "",
40 hash: "",
41 ...typeof to === "string" ? parsePath(to) : to,
42 state,
43 key: to && to.key || key || createKey$1(),
44 mask
45 };
46}
47/**
48* Creates a string URL path from the given pathname, search, and hash components.
49*
50* @category Utils
51*/
52function createPath({ pathname = "/", search = "", hash = "" }) {
53 if (search && search !== "?") pathname += search.charAt(0) === "?" ? search : "?" + search;
54 if (hash && hash !== "#") pathname += hash.charAt(0) === "#" ? hash : "#" + hash;
55 return pathname;
56}
57/**
58* Parses a string URL path into its separate pathname, search, and hash components.
59*
60* @category Utils
61*/
62function parsePath(path) {
63 let parsedPath = {};
64 if (path) {
65 let hashIndex = path.indexOf("#");
66 if (hashIndex >= 0) {
67 parsedPath.hash = path.substring(hashIndex);
68 path = path.substring(0, hashIndex);
69 }
70 let searchIndex = path.indexOf("?");
71 if (searchIndex >= 0) {
72 parsedPath.search = path.substring(searchIndex);
73 path = path.substring(0, searchIndex);
74 }
75 if (path) parsedPath.pathname = path;
76 }
77 return parsedPath;
78}
79//#endregion
80//#region lib/router/instrumentation.ts
81const UninstrumentedSymbol = Symbol("Uninstrumented");
82function getRouteInstrumentationUpdates(fns, route) {
83 let aggregated = {
84 lazy: [],
85 "lazy.loader": [],
86 "lazy.action": [],
87 "lazy.middleware": [],
88 middleware: [],
89 loader: [],
90 action: []
91 };
92 fns.forEach((fn) => fn({
93 id: route.id,
94 index: route.index,
95 path: route.path,
96 instrument(i) {
97 let keys = Object.keys(aggregated);
98 for (let key of keys) if (i[key]) aggregated[key].push(i[key]);
99 }
100 }));
101 let updates = {};
102 if (typeof route.lazy === "function" && aggregated.lazy.length > 0) {
103 let instrumented = wrapImpl(aggregated.lazy, route.lazy, () => void 0);
104 if (instrumented) updates.lazy = instrumented;
105 }
106 if (typeof route.lazy === "object") {
107 let lazyObject = route.lazy;
108 [
109 "middleware",
110 "loader",
111 "action"
112 ].forEach((key) => {
113 let lazyFn = lazyObject[key];
114 let instrumentations = aggregated[`lazy.${key}`];
115 if (typeof lazyFn === "function" && instrumentations.length > 0) {
116 let instrumented = wrapImpl(instrumentations, lazyFn, () => void 0);
117 if (instrumented) updates.lazy = Object.assign(updates.lazy || {}, { [key]: instrumented });
118 }
119 });
120 }
121 ["loader", "action"].forEach((key) => {
122 let handler = route[key];
123 if (typeof handler === "function" && aggregated[key].length > 0) {
124 let original = handler[UninstrumentedSymbol] ?? handler;
125 let instrumented = wrapImpl(aggregated[key], original, (...args) => getHandlerInfo(args[0]));
126 if (instrumented) {
127 if (key === "loader" && original.hydrate === true) instrumented.hydrate = true;
128 instrumented[UninstrumentedSymbol] = original;
129 updates[key] = instrumented;
130 }
131 }
132 });
133 if (route.middleware && route.middleware.length > 0 && aggregated.middleware.length > 0) updates.middleware = route.middleware.map((middleware) => {
134 let original = middleware[UninstrumentedSymbol] ?? middleware;
135 let instrumented = wrapImpl(aggregated.middleware, original, (...args) => getHandlerInfo(args[0]));
136 if (instrumented) {
137 instrumented[UninstrumentedSymbol] = original;
138 return instrumented;
139 }
140 return middleware;
141 });
142 return updates;
143}
144function wrapImpl(impls, handler, getInfo) {
145 if (impls.length === 0) return null;
146 return async (...args) => {
147 let result = await recurseRight(impls, getInfo(...args), () => handler(...args), impls.length - 1);
148 if (result.type === "error") throw result.value;
149 return result.value;
150 };
151}
152async function recurseRight(impls, info, handler, index) {
153 let impl = impls[index];
154 let result;
155 if (!impl) try {
156 result = {
157 type: "success",
158 value: await handler()
159 };
160 } catch (e) {
161 result = {
162 type: "error",
163 value: e
164 };
165 }
166 else {
167 let handlerPromise = void 0;
168 let callHandler = async () => {
169 if (handlerPromise) console.error("You cannot call instrumented handlers more than once");
170 else handlerPromise = recurseRight(impls, info, handler, index - 1);
171 result = await handlerPromise;
172 invariant$1(result, "Expected a result");
173 if (result.type === "error" && result.value instanceof Error) return {
174 status: "error",
175 error: result.value
176 };
177 return {
178 status: "success",
179 error: void 0
180 };
181 };
182 try {
183 await impl(callHandler, info);
184 } catch (e) {
185 console.error("An instrumentation function threw an error:", e);
186 }
187 if (!handlerPromise) await callHandler();
188 await handlerPromise;
189 }
190 if (result) return result;
191 return {
192 type: "error",
193 value: /* @__PURE__ */ new Error("No result assigned in instrumentation chain.")
194 };
195}
196function getHandlerInfo(args) {
197 let { request, context, params, pattern } = args;
198 return {
199 request: getReadonlyRequest(request),
200 params: { ...params },
201 pattern,
202 context: getReadonlyContext(context)
203 };
204}
205function getReadonlyRequest(request) {
206 return {
207 method: request.method,
208 url: request.url,
209 headers: { get: (...args) => request.headers.get(...args) }
210 };
211}
212function getReadonlyContext(context) {
213 return { get: (ctx) => context.get(ctx) };
214}
215//#endregion
216//#region lib/router/utils.ts
217/**
218* Creates a type-safe {@link RouterContext} object that can be used to
219* store and retrieve arbitrary values in [`action`](../../start/framework/route-module#action)s,
220* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
221* Similar to React's [`createContext`](https://react.dev/reference/react/createContext),
222* but specifically designed for React Router's request/response lifecycle.
223*
224* If a `defaultValue` is provided, it will be returned from `context.get()`
225* when no value has been set for the context. Otherwise, reading this context
226* when no value has been set will throw an error.
227*
228* ```tsx filename=app/context.ts
229* import { createContext } from "react-router";
230*
231* // Create a context for user data
232* export const userContext =
233* createContext<User | null>(null);
234* ```
235*
236* ```tsx filename=app/middleware/auth.ts
237* import { getUserFromSession } from "~/auth.server";
238* import { userContext } from "~/context";
239*
240* export const authMiddleware = async ({
241* context,
242* request,
243* }) => {
244* const user = await getUserFromSession(request);
245* context.set(userContext, user);
246* };
247* ```
248*
249* ```tsx filename=app/routes/profile.tsx
250* import { userContext } from "~/context";
251*
252* export async function loader({
253* context,
254* }: Route.LoaderArgs) {
255* const user = context.get(userContext);
256*
257* if (!user) {
258* throw new Response("Unauthorized", { status: 401 });
259* }
260*
261* return { user };
262* }
263* ```
264*
265* @public
266* @category Utils
267* @mode framework
268* @mode data
269* @param defaultValue An optional default value for the context. This value
270* will be returned if no value has been set for this context.
271* @returns A {@link RouterContext} object that can be used with
272* `context.get()` and `context.set()` in [`action`](../../start/framework/route-module#action)s,
273* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
274*/
275function createContext(defaultValue) {
276 return { defaultValue };
277}
278/**
279* Provides methods for writing/reading values in application context in a
280* type-safe way. Primarily for usage with [middleware](../../how-to/middleware).
281*
282* @example
283* import {
284* createContext,
285* RouterContextProvider
286* } from "react-router";
287*
288* const userContext = createContext<User | null>(null);
289* const contextProvider = new RouterContextProvider();
290* contextProvider.set(userContext, getUser());
291* // ^ Type-safe
292* const user = contextProvider.get(userContext);
293* // ^ User
294*
295* @public
296* @category Utils
297* @mode framework
298* @mode data
299*/
300var RouterContextProvider = class {
301 #map = /* @__PURE__ */ new Map();
302 /**
303 * Create a new `RouterContextProvider` instance
304 * @param init An optional initial context map to populate the provider with
305 */
306 constructor(init) {
307 if (init) for (let [context, value] of init) this.set(context, value);
308 }
309 /**
310 * Access a value from the context. If no value has been set for the context,
311 * it will return the context's `defaultValue` if provided, or throw an error
312 * if no `defaultValue` was set.
313 * @param context The context to get the value for
314 * @returns The value for the context, or the context's `defaultValue` if no
315 * value was set
316 */
317 get(context) {
318 if (this.#map.has(context)) return this.#map.get(context);
319 if (context.defaultValue !== void 0) return context.defaultValue;
320 throw new Error("No value found for context");
321 }
322 /**
323 * Set a value for the context. If the context already has a value set, this
324 * will overwrite it.
325 *
326 * @param context The context to set the value for
327 * @param value The value to set for the context
328 * @returns {void}
329 */
330 set(context, value) {
331 this.#map.set(context, value);
332 }
333};
334const unsupportedLazyRouteObjectKeys = new Set([
335 "lazy",
336 "caseSensitive",
337 "path",
338 "id",
339 "index",
340 "children"
341]);
342function isUnsupportedLazyRouteObjectKey(key) {
343 return unsupportedLazyRouteObjectKeys.has(key);
344}
345const unsupportedLazyRouteFunctionKeys = new Set([
346 "lazy",
347 "caseSensitive",
348 "path",
349 "id",
350 "index",
351 "middleware",
352 "children"
353]);
354function isUnsupportedLazyRouteFunctionKey(key) {
355 return unsupportedLazyRouteFunctionKeys.has(key);
356}
357function isIndexRoute(route) {
358 return route.index === true;
359}
360function defaultMapRouteProperties(route) {
361 let updates = {};
362 if (route.Component) {
363 if (route.element) warning(false, "You should not include both `Component` and `element` on your route - `Component` will be used.");
364 Object.assign(updates, {
365 element: React.createElement(route.Component),
366 Component: void 0
367 });
368 }
369 if (route.HydrateFallback) {
370 if (route.hydrateFallbackElement) warning(false, "You should not include both `HydrateFallback` and `hydrateFallbackElement` on your route - `HydrateFallback` will be used.");
371 Object.assign(updates, {
372 hydrateFallbackElement: React.createElement(route.HydrateFallback),
373 HydrateFallback: void 0
374 });
375 }
376 if (route.ErrorBoundary) {
377 if (route.errorElement) warning(false, "You should not include both `ErrorBoundary` and `errorElement` on your route - `ErrorBoundary` will be used.");
378 Object.assign(updates, {
379 errorElement: React.createElement(route.ErrorBoundary),
380 ErrorBoundary: void 0
381 });
382 }
383 return updates;
384}
385function convertRoutesToDataRoutes(routes, mapRouteProperties = defaultMapRouteProperties, parentPath = [], manifest = {}, allowInPlaceMutations = false) {
386 return routes.map((route, index) => {
387 let treePath = [...parentPath, String(index)];
388 let id = typeof route.id === "string" ? route.id : treePath.join("-");
389 invariant$1(route.index !== true || !route.children, `Cannot specify children on an index route`);
390 invariant$1(allowInPlaceMutations || !manifest[id], `Found a route id collision on id "${id}". Route id's must be globally unique within Data Router usages`);
391 if (isIndexRoute(route)) {
392 let indexRoute = {
393 ...route,
394 id
395 };
396 manifest[id] = mergeRouteUpdates(indexRoute, mapRouteProperties(indexRoute));
397 return indexRoute;
398 } else {
399 let pathOrLayoutRoute = {
400 ...route,
401 id,
402 children: void 0
403 };
404 manifest[id] = mergeRouteUpdates(pathOrLayoutRoute, mapRouteProperties(pathOrLayoutRoute));
405 if (route.children) pathOrLayoutRoute.children = convertRoutesToDataRoutes(route.children, mapRouteProperties, treePath, manifest, allowInPlaceMutations);
406 return pathOrLayoutRoute;
407 }
408 });
409}
410function mergeRouteUpdates(route, updates) {
411 return Object.assign(route, {
412 ...updates,
413 ...typeof updates.lazy === "object" && updates.lazy != null ? { lazy: {
414 ...route.lazy,
415 ...updates.lazy
416 } } : {}
417 });
418}
419/**
420* Matches the given routes to a location and returns the match data.
421*
422* @example
423* import { matchRoutes } from "react-router";
424*
425* let routes = [{
426* path: "/",
427* Component: Root,
428* children: [{
429* path: "dashboard",
430* Component: Dashboard,
431* }]
432* }];
433*
434* matchRoutes(routes, "/dashboard"); // [rootMatch, dashboardMatch]
435*
436* @public
437* @category Utils
438* @param routes The array of route objects to match against.
439* @param locationArg The location to match against, either a string path or a
440* partial {@link Location} object
441* @param basename Optional base path to strip from the location before matching.
442* Defaults to `/`.
443* @returns An array of matched routes, or `null` if no matches were found.
444*/
445function matchRoutes(routes, locationArg, basename = "/") {
446 return matchRoutesImpl(routes, locationArg, basename, false);
447}
448function matchRoutesImpl(routes, locationArg, basename, allowPartial, precomputedBranches) {
449 let pathname = stripBasename((typeof locationArg === "string" ? parsePath(locationArg) : locationArg).pathname || "/", basename);
450 if (pathname == null) return null;
451 let branches = precomputedBranches ?? flattenAndRankRoutes(routes);
452 let matches = null;
453 let decoded = decodePath(pathname);
454 for (let i = 0; matches == null && i < branches.length; ++i) matches = matchRouteBranch(branches[i], decoded, allowPartial);
455 return matches;
456}
457function convertRouteMatchToUiMatch(match, loaderData) {
458 let { route, pathname, params } = match;
459 return {
460 id: route.id,
461 pathname,
462 params,
463 loaderData: loaderData[route.id],
464 handle: route.handle
465 };
466}
467function flattenAndRankRoutes(routes) {
468 let branches = flattenRoutes(routes);
469 rankRouteBranches(branches);
470 return branches;
471}
472function flattenRoutes(routes, branches = [], parentsMeta = [], parentPath = "", _hasParentOptionalSegments = false) {
473 let flattenRoute = (route, index, hasParentOptionalSegments = _hasParentOptionalSegments, relativePath) => {
474 let meta = {
475 relativePath: relativePath === void 0 ? route.path || "" : relativePath,
476 caseSensitive: route.caseSensitive === true,
477 childrenIndex: index,
478 route
479 };
480 if (meta.relativePath.startsWith("/")) {
481 if (!meta.relativePath.startsWith(parentPath) && hasParentOptionalSegments) return;
482 invariant$1(meta.relativePath.startsWith(parentPath), `Absolute route path "${meta.relativePath}" nested under path "${parentPath}" is not valid. An absolute child route path must start with the combined path of all its parent routes.`);
483 meta.relativePath = meta.relativePath.slice(parentPath.length);
484 }
485 let path = joinPaths([parentPath, meta.relativePath]);
486 let routesMeta = parentsMeta.concat(meta);
487 if (route.children && route.children.length > 0) {
488 invariant$1(route.index !== true, `Index routes must not have child routes. Please remove all child routes from route path "${path}".`);
489 flattenRoutes(route.children, branches, routesMeta, path, hasParentOptionalSegments);
490 }
491 if (route.path == null && !route.index) return;
492 branches.push({
493 path,
494 score: computeScore(path, route.index),
495 routesMeta: routesMeta.map((meta, i) => {
496 let [matcher, params] = compilePath(meta.relativePath, meta.caseSensitive, i === routesMeta.length - 1);
497 return {
498 ...meta,
499 matcher,
500 compiledParams: params
501 };
502 })
503 });
504 };
505 routes.forEach((route, index) => {
506 if (route.path === "" || !route.path?.includes("?")) flattenRoute(route, index);
507 else for (let exploded of explodeOptionalSegments(route.path)) flattenRoute(route, index, true, exploded);
508 });
509 return branches;
510}
511function explodeOptionalSegments(path) {
512 let segments = path.split("/");
513 if (segments.length === 0) return [];
514 let [first, ...rest] = segments;
515 let isOptional = first.endsWith("?");
516 let required = first.replace(/\?$/, "");
517 if (rest.length === 0) return isOptional ? [required, ""] : [required];
518 let restExploded = explodeOptionalSegments(rest.join("/"));
519 let result = [];
520 result.push(...restExploded.map((subpath) => subpath === "" ? required : [required, subpath].join("/")));
521 if (isOptional) result.push(...restExploded);
522 return result.map((exploded) => path.startsWith("/") && exploded === "" ? "/" : exploded);
523}
524function rankRouteBranches(branches) {
525 branches.sort((a, b) => a.score !== b.score ? b.score - a.score : compareIndexes(a.routesMeta.map((meta) => meta.childrenIndex), b.routesMeta.map((meta) => meta.childrenIndex)));
526}
527const paramRe = /^:[\w-]+$/;
528const dynamicSegmentValue = 3;
529const indexRouteValue = 2;
530const emptySegmentValue = 1;
531const staticSegmentValue = 10;
532const splatPenalty = -2;
533const isSplat = (s) => s === "*";
534function computeScore(path, index) {
535 let segments = path.split("/");
536 let initialScore = segments.length;
537 if (segments.some(isSplat)) initialScore += splatPenalty;
538 if (index) initialScore += indexRouteValue;
539 return segments.filter((s) => !isSplat(s)).reduce((score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue), initialScore);
540}
541function compareIndexes(a, b) {
542 return a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]) ? a[a.length - 1] - b[b.length - 1] : 0;
543}
544function matchRouteBranch(branch, pathname, allowPartial = false) {
545 let { routesMeta } = branch;
546 let matchedParams = {};
547 let matchedPathname = "/";
548 let matches = [];
549 for (let i = 0; i < routesMeta.length; ++i) {
550 let meta = routesMeta[i];
551 let end = i === routesMeta.length - 1;
552 let remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
553 let pattern = {
554 path: meta.relativePath,
555 caseSensitive: meta.caseSensitive,
556 end
557 };
558 let match = meta.matcher && meta.compiledParams ? matchPathImpl(pattern, remainingPathname, meta.matcher, meta.compiledParams) : matchPath(pattern, remainingPathname);
559 let route = meta.route;
560 if (!match && end && allowPartial && !routesMeta[routesMeta.length - 1].route.index) match = matchPath({
561 path: meta.relativePath,
562 caseSensitive: meta.caseSensitive,
563 end: false
564 }, remainingPathname);
565 if (!match) return null;
566 Object.assign(matchedParams, match.params);
567 matches.push({
568 params: matchedParams,
569 pathname: joinPaths([matchedPathname, match.pathname]),
570 pathnameBase: normalizePathname(joinPaths([matchedPathname, match.pathnameBase])),
571 route
572 });
573 if (match.pathnameBase !== "/") matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
574 }
575 return matches;
576}
577/**
578* Performs pattern matching on a URL pathname and returns information about
579* the match.
580*
581* @public
582* @category Utils
583* @param pattern The pattern to match against the URL pathname. This can be a
584* string or a {@link PathPattern} object. If a string is provided, it will be
585* treated as a pattern with `caseSensitive` set to `false` and `end` set to
586* `true`.
587* @param pathname The URL pathname to match against the pattern.
588* @returns A path match object if the pattern matches the pathname,
589* or `null` if it does not match.
590*/
591function matchPath(pattern, pathname) {
592 if (typeof pattern === "string") pattern = {
593 path: pattern,
594 caseSensitive: false,
595 end: true
596 };
597 let [matcher, compiledParams] = compilePath(pattern.path, pattern.caseSensitive, pattern.end);
598 return matchPathImpl(pattern, pathname, matcher, compiledParams);
599}
600function matchPathImpl(pattern, pathname, matcher, compiledParams) {
601 let match = pathname.match(matcher);
602 if (!match) return null;
603 let matchedPathname = match[0];
604 let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
605 let captureGroups = match.slice(1);
606 return {
607 params: compiledParams.reduce((memo, { paramName, isOptional }, index) => {
608 if (paramName === "*") {
609 let splatValue = captureGroups[index] || "";
610 pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
611 }
612 const value = captureGroups[index];
613 if (isOptional && !value) memo[paramName] = void 0;
614 else memo[paramName] = (value || "").replace(/%2F/g, "/");
615 return memo;
616 }, {}),
617 pathname: matchedPathname,
618 pathnameBase,
619 pattern
620 };
621}
622function compilePath(path, caseSensitive = false, end = true) {
623 warning(path === "*" || !path.endsWith("*") || path.endsWith("/*"), `Route path "${path}" will be treated as if it were "${path.replace(/\*$/, "/*")}" because the \`*\` character must always follow a \`/\` in the pattern. To get rid of this warning, please change the route path to "${path.replace(/\*$/, "/*")}".`);
624 let params = [];
625 let regexpSource = "^" + path.replace(/\/*\*?$/, "").replace(/^\/*/, "/").replace(/[\\.*+^${}|()[\]]/g, "\\$&").replace(/\/:([\w-]+)(\?)?/g, (match, paramName, isOptional, index, str) => {
626 params.push({
627 paramName,
628 isOptional: isOptional != null
629 });
630 if (isOptional) {
631 let nextChar = str.charAt(index + match.length);
632 if (nextChar && nextChar !== "/") return "/([^\\/]*)";
633 return "(?:/([^\\/]*))?";
634 }
635 return "/([^\\/]+)";
636 }).replace(/\/([\w-]+)\?(\/|$)/g, "(/$1)?$2");
637 if (path.endsWith("*")) {
638 params.push({ paramName: "*" });
639 regexpSource += path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
640 } else if (end) regexpSource += "\\/*$";
641 else if (path !== "" && path !== "/") regexpSource += "(?:(?=\\/|$))";
642 return [new RegExp(regexpSource, caseSensitive ? void 0 : "i"), params];
643}
644function decodePath(value) {
645 try {
646 return value.split("/").map((v) => decodeURIComponent(v).replace(/\//g, "%2F")).join("/");
647 } catch (error) {
648 warning(false, `The URL path "${value}" could not be decoded because it is a malformed URL segment. This is probably due to a bad percent encoding (${error}).`);
649 return value;
650 }
651}
652function stripBasename(pathname, basename) {
653 if (basename === "/") return pathname;
654 if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) return null;
655 let startIndex = basename.endsWith("/") ? basename.length - 1 : basename.length;
656 let nextChar = pathname.charAt(startIndex);
657 if (nextChar && nextChar !== "/") return null;
658 return pathname.slice(startIndex) || "/";
659}
660function prependBasename({ basename, pathname }) {
661 return pathname === "/" ? basename : joinPaths([basename, pathname]);
662}
663const isAbsoluteUrl = (url) => ABSOLUTE_URL_REGEX.test(url);
664/**
665* Returns a resolved {@link Path} object relative to the given pathname.
666*
667* @public
668* @category Utils
669* @param to The path to resolve, either a string or a partial {@link Path}
670* object.
671* @param fromPathname The pathname to resolve the path from. Defaults to `/`.
672* @returns A {@link Path} object with the resolved pathname, search, and hash.
673*/
674function resolvePath(to, fromPathname = "/") {
675 let { pathname: toPathname, search = "", hash = "" } = typeof to === "string" ? parsePath(to) : to;
676 let pathname;
677 if (toPathname) {
678 toPathname = removeDoubleSlashes(toPathname);
679 if (toPathname.startsWith("/")) pathname = resolvePathname(toPathname.substring(1), "/");
680 else pathname = resolvePathname(toPathname, fromPathname);
681 } else pathname = fromPathname;
682 return {
683 pathname,
684 search: normalizeSearch(search),
685 hash: normalizeHash(hash)
686 };
687}
688function resolvePathname(relativePath, fromPathname) {
689 let segments = removeTrailingSlash(fromPathname).split("/");
690 relativePath.split("/").forEach((segment) => {
691 if (segment === "..") {
692 if (segments.length > 1) segments.pop();
693 } else if (segment !== ".") segments.push(segment);
694 });
695 return segments.length > 1 ? segments.join("/") : "/";
696}
697function getInvalidPathError(char, field, dest, path) {
698 return `Cannot include a '${char}' character in a manually specified \`to.${field}\` field [${JSON.stringify(path)}]. Please separate it out to the \`to.${dest}\` field. Alternatively you may provide the full path as a string in <Link to="..."> and the router will parse it for you.`;
699}
700function getPathContributingMatches(matches) {
701 return matches.filter((match, index) => index === 0 || match.route.path && match.route.path.length > 0);
702}
703function getResolveToMatches(matches) {
704 let pathMatches = getPathContributingMatches(matches);
705 return pathMatches.map((match, idx) => idx === pathMatches.length - 1 ? match.pathname : match.pathnameBase);
706}
707function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = false) {
708 let to;
709 if (typeof toArg === "string") to = parsePath(toArg);
710 else {
711 to = { ...toArg };
712 invariant$1(!to.pathname || !to.pathname.includes("?"), getInvalidPathError("?", "pathname", "search", to));
713 invariant$1(!to.pathname || !to.pathname.includes("#"), getInvalidPathError("#", "pathname", "hash", to));
714 invariant$1(!to.search || !to.search.includes("#"), getInvalidPathError("#", "search", "hash", to));
715 }
716 let isEmptyPath = toArg === "" || to.pathname === "";
717 let toPathname = isEmptyPath ? "/" : to.pathname;
718 let from;
719 if (toPathname == null) from = locationPathname;
720 else {
721 let routePathnameIndex = routePathnames.length - 1;
722 if (!isPathRelative && toPathname.startsWith("..")) {
723 let toSegments = toPathname.split("/");
724 while (toSegments[0] === "..") {
725 toSegments.shift();
726 routePathnameIndex -= 1;
727 }
728 to.pathname = toSegments.join("/");
729 }
730 from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
731 }
732 let path = resolvePath(to, from);
733 let hasExplicitTrailingSlash = toPathname && toPathname !== "/" && toPathname.endsWith("/");
734 let hasCurrentTrailingSlash = (isEmptyPath || toPathname === ".") && locationPathname.endsWith("/");
735 if (!path.pathname.endsWith("/") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) path.pathname += "/";
736 return path;
737}
738const removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
739const joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
740const removeTrailingSlash = (path) => path.replace(/\/+$/, "");
741const normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
742const normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
743const normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
744var DataWithResponseInit = class {
745 type = "DataWithResponseInit";
746 data;
747 init;
748 constructor(data, init) {
749 this.data = data;
750 this.init = init || null;
751 }
752};
753/**
754* Create "responses" that contain `headers`/`status` without forcing
755* serialization into an actual [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
756*
757* @example
758* import { data } from "react-router";
759*
760* export async function action({ request }: Route.ActionArgs) {
761* let formData = await request.formData();
762* let item = await createItem(formData);
763* return data(item, {
764* headers: { "X-Custom-Header": "value" }
765* status: 201,
766* });
767* }
768*
769* @public
770* @category Utils
771* @mode framework
772* @mode data
773* @param data The data to be included in the response.
774* @param init The status code or a `ResponseInit` object to be included in the
775* response.
776* @returns A {@link DataWithResponseInit} instance containing the data and
777* response init.
778*/
779function data(data, init) {
780 return new DataWithResponseInit(data, typeof init === "number" ? { status: init } : init);
781}
782/**
783* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
784* Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
785* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
786*
787* This utility accepts absolute URLs and can navigate to external domains, so
788* the application should validate any user-supplied inputs to redirects.
789*
790* @example
791* import { redirect } from "react-router";
792*
793* export async function loader({ request }: Route.LoaderArgs) {
794* if (!isLoggedIn(request))
795* throw redirect("/login");
796* }
797*
798* // ...
799* }
800*
801* @public
802* @category Utils
803* @mode framework
804* @mode data
805* @param url The URL to redirect to.
806* @param init The status code or a `ResponseInit` object to be included in the
807* response.
808* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
809* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
810* header.
811*/
812const redirect$1 = (url, init = 302) => {
813 let responseInit = init;
814 if (typeof responseInit === "number") responseInit = { status: responseInit };
815 else if (typeof responseInit.status === "undefined") responseInit.status = 302;
816 let headers = new Headers(responseInit.headers);
817 headers.set("Location", url);
818 return new Response(null, {
819 ...responseInit,
820 headers
821 });
822};
823/**
824* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
825* that will force a document reload to the new location. Sets the status code
826* and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
827* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
828*
829* This utility accepts absolute URLs and can navigate to external domains, so
830* the application should validate any user-supplied inputs to redirects.
831*
832* ```tsx filename=routes/logout.tsx
833* import { redirectDocument } from "react-router";
834*
835* import { destroySession } from "../sessions.server";
836*
837* export async function action({ request }: Route.ActionArgs) {
838* let session = await getSession(request.headers.get("Cookie"));
839* return redirectDocument("/", {
840* headers: { "Set-Cookie": await destroySession(session) }
841* });
842* }
843* ```
844*
845* @public
846* @category Utils
847* @mode framework
848* @mode data
849* @param url The URL to redirect to.
850* @param init The status code or a `ResponseInit` object to be included in the
851* response.
852* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
853* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
854* header.
855*/
856const redirectDocument$1 = (url, init) => {
857 let response = redirect$1(url, init);
858 response.headers.set("X-Remix-Reload-Document", "true");
859 return response;
860};
861/**
862* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
863* that will perform a [`history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState)
864* instead of a [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)
865* for client-side navigation redirects. Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
866* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
867*
868* @example
869* import { replace } from "react-router";
870*
871* export async function loader() {
872* return replace("/new-location");
873* }
874*
875* @public
876* @category Utils
877* @mode framework
878* @mode data
879* @param url The URL to redirect to.
880* @param init The status code or a `ResponseInit` object to be included in the
881* response.
882* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
883* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
884* header.
885*/
886const replace$1 = (url, init) => {
887 let response = redirect$1(url, init);
888 response.headers.set("X-Remix-Replace", "true");
889 return response;
890};
891var ErrorResponseImpl = class {
892 status;
893 statusText;
894 data;
895 error;
896 internal;
897 constructor(status, statusText, data, internal = false) {
898 this.status = status;
899 this.statusText = statusText || "";
900 this.internal = internal;
901 if (data instanceof Error) {
902 this.data = data.toString();
903 this.error = data;
904 } else this.data = data;
905 }
906};
907/**
908* Check if the given error is an {@link ErrorResponse} generated from a 4xx/5xx
909* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
910* thrown from an [`action`](../../start/framework/route-module#action) or
911* [`loader`](../../start/framework/route-module#loader) function.
912*
913* @example
914* import { isRouteErrorResponse } from "react-router";
915*
916* export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
917* if (isRouteErrorResponse(error)) {
918* return (
919* <>
920* <p>Error: `${error.status}: ${error.statusText}`</p>
921* <p>{error.data}</p>
922* </>
923* );
924* }
925*
926* return (
927* <p>Error: {error instanceof Error ? error.message : "Unknown Error"}</p>
928* );
929* }
930*
931* @public
932* @category Utils
933* @mode framework
934* @mode data
935* @param error The error to check.
936* @returns `true` if the error is an {@link ErrorResponse}, `false` otherwise.
937*/
938function isRouteErrorResponse(error) {
939 return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error;
940}
941function getRoutePattern(matches) {
942 return joinPaths(matches.map((m) => m.route.path).filter(Boolean)) || "/";
943}
944typeof window !== "undefined" && typeof window.document !== "undefined" && window.document.createElement;
945//#endregion
946//#region lib/router/router.ts
947const validMutationMethodsArr = [
948 "POST",
949 "PUT",
950 "PATCH",
951 "DELETE"
952];
953const validMutationMethods = new Set(validMutationMethodsArr);
954const validRequestMethodsArr = ["GET", ...validMutationMethodsArr];
955const validRequestMethods = new Set(validRequestMethodsArr);
956const redirectStatusCodes = new Set([
957 301,
958 302,
959 303,
960 307,
961 308
962]);
963const ResetLoaderDataSymbol = Symbol("ResetLoaderData");
964/**
965* Create a static handler to perform server-side data loading
966*
967* @example
968* export async function handleRequest(request: Request) {
969* let { query, dataRoutes } = createStaticHandler(routes);
970* let context = await query(request);
971*
972* if (context instanceof Response) {
973* return context;
974* }
975*
976* let router = createStaticRouter(dataRoutes, context);
977* return new Response(
978* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
979* { headers: { "Content-Type": "text/html" } }
980* );
981* }
982*
983* @public
984* @category Data Routers
985* @mode data
986* @param routes The {@link RouteObject | route objects} to create a static
987* handler for
988* @param opts Options
989* @param opts.basename The base URL for the static handler (default: `/`)
990* @param opts.future Future flags for the static handler
991* @returns A static handler that can be used to query data for the provided
992* routes
993*/
994function createStaticHandler(routes, opts) {
995 invariant$1(routes.length > 0, "You must provide a non-empty routes array to createStaticHandler");
996 let manifest = {};
997 let basename = (opts ? opts.basename : null) || "/";
998 let _mapRouteProperties = opts?.mapRouteProperties;
999 let mapRouteProperties = _mapRouteProperties ? _mapRouteProperties : () => ({});
1000 ({ ...opts?.future });
1001 if (opts?.instrumentations) {
1002 let instrumentations = opts.instrumentations;
1003 mapRouteProperties = (route) => {
1004 return {
1005 ..._mapRouteProperties?.(route),
1006 ...getRouteInstrumentationUpdates(instrumentations.map((i) => i.route).filter(Boolean), route)
1007 };
1008 };
1009 }
1010 let dataRoutes = convertRoutesToDataRoutes(routes, mapRouteProperties, void 0, manifest);
1011 let routeBranches = flattenAndRankRoutes(dataRoutes);
1012 /**
1013 * The query() method is intended for document requests, in which we want to
1014 * call an optional action and potentially multiple loaders for all nested
1015 * routes. It returns a StaticHandlerContext object, which is very similar
1016 * to the router state (location, loaderData, actionData, errors, etc.) and
1017 * also adds SSR-specific information such as the statusCode and headers
1018 * from action/loaders Responses.
1019 *
1020 * It _should_ never throw and should report all errors through the
1021 * returned handlerContext.errors object, properly associating errors to
1022 * their error boundary. Additionally, it tracks _deepestRenderedBoundaryId
1023 * which can be used to emulate React error boundaries during SSR by performing
1024 * a second pass only down to the boundaryId.
1025 *
1026 * The one exception where we do not return a StaticHandlerContext is when a
1027 * redirect response is returned or thrown from any action/loader. We
1028 * propagate that out and return the raw Response so the HTTP server can
1029 * return it directly.
1030 *
1031 * - `opts.requestContext` is an optional server context that will be passed
1032 * to actions/loaders in the `context` parameter
1033 * - `opts.skipLoaderErrorBubbling` is an optional parameter that will prevent
1034 * the bubbling of errors which allows single-fetch-type implementations
1035 * where the client will handle the bubbling and we may need to return data
1036 * for the handling route
1037 */
1038 async function query(request, { requestContext, filterMatchesToLoad, skipLoaderErrorBubbling, skipRevalidation, dataStrategy, generateMiddlewareResponse, normalizePath } = {}) {
1039 let normalizePathImpl = normalizePath || defaultNormalizePath;
1040 let method = request.method;
1041 let location = createLocation("", normalizePathImpl(request), null, "default");
1042 let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
1043 requestContext = requestContext != null ? requestContext : new RouterContextProvider();
1044 if (!isValidMethod(method) && method !== "HEAD") {
1045 let error = getInternalRouterError(405, { method });
1046 let { matches: methodNotAllowedMatches, route } = getShortCircuitMatches(dataRoutes);
1047 let staticContext = {
1048 basename,
1049 location,
1050 matches: methodNotAllowedMatches,
1051 loaderData: {},
1052 actionData: null,
1053 errors: { [route.id]: error },
1054 statusCode: error.status,
1055 loaderHeaders: {},
1056 actionHeaders: {}
1057 };
1058 return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
1059 } else if (!matches) {
1060 let error = getInternalRouterError(404, { pathname: location.pathname });
1061 let { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes);
1062 let staticContext = {
1063 basename,
1064 location,
1065 matches: notFoundMatches,
1066 loaderData: {},
1067 actionData: null,
1068 errors: { [route.id]: error },
1069 statusCode: error.status,
1070 loaderHeaders: {},
1071 actionHeaders: {}
1072 };
1073 return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
1074 }
1075 if (generateMiddlewareResponse) {
1076 invariant$1(requestContext instanceof RouterContextProvider, "When using middleware in `staticHandler.query()`, any provided `requestContext` must be an instance of `RouterContextProvider`");
1077 try {
1078 await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
1079 let renderedStaticContext;
1080 let response = await runServerMiddlewarePipeline({
1081 request,
1082 url: createDataFunctionUrl(request, location),
1083 pattern: getRoutePattern(matches),
1084 matches,
1085 params: matches[0].params,
1086 context: requestContext
1087 }, async () => {
1088 return await generateMiddlewareResponse(async (revalidationRequest, opts = {}) => {
1089 let result = await queryImpl(revalidationRequest, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, "filterMatchesToLoad" in opts ? opts.filterMatchesToLoad ?? null : filterMatchesToLoad ?? null, skipRevalidation === true);
1090 if (isResponse(result)) return result;
1091 renderedStaticContext = {
1092 location,
1093 basename,
1094 ...result
1095 };
1096 return renderedStaticContext;
1097 });
1098 }, async (error, routeId) => {
1099 if (isRedirectResponse(error)) return error;
1100 if (isResponse(error)) try {
1101 error = new ErrorResponseImpl(error.status, error.statusText, await parseResponseBody(error));
1102 } catch (e) {
1103 error = e;
1104 }
1105 if (isDataWithResponseInit(error)) error = dataWithResponseInitToErrorResponse(error);
1106 if (renderedStaticContext) {
1107 if (routeId in renderedStaticContext.loaderData) renderedStaticContext.loaderData[routeId] = void 0;
1108 let staticContext = getStaticContextFromError(dataRoutes, renderedStaticContext, error, skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, routeId).route.id);
1109 return generateMiddlewareResponse(() => Promise.resolve(staticContext));
1110 } else {
1111 let staticContext = {
1112 matches,
1113 location,
1114 basename,
1115 loaderData: {},
1116 actionData: null,
1117 errors: { [skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, matches.find((m) => m.route.id === routeId || m.route.loader)?.route.id || routeId).route.id]: error },
1118 statusCode: isRouteErrorResponse(error) ? error.status : 500,
1119 actionHeaders: {},
1120 loaderHeaders: {}
1121 };
1122 return generateMiddlewareResponse(() => Promise.resolve(staticContext));
1123 }
1124 });
1125 invariant$1(isResponse(response), "Expected a response in query()");
1126 return response;
1127 } catch (e) {
1128 if (isResponse(e)) return e;
1129 throw e;
1130 }
1131 }
1132 let result = await queryImpl(request, location, matches, requestContext, dataStrategy || null, skipLoaderErrorBubbling === true, null, filterMatchesToLoad || null, skipRevalidation === true);
1133 if (isResponse(result)) return result;
1134 return {
1135 location,
1136 basename,
1137 ...result
1138 };
1139 }
1140 /**
1141 * The queryRoute() method is intended for targeted route requests, either
1142 * for fetch ?_data requests or resource route requests. In this case, we
1143 * are only ever calling a single action or loader, and we are returning the
1144 * returned value directly. In most cases, this will be a Response returned
1145 * from the action/loader, but it may be a primitive or other value as well -
1146 * and in such cases the calling context should handle that accordingly.
1147 *
1148 * We do respect the throw/return differentiation, so if an action/loader
1149 * throws, then this method will throw the value. This is important so we
1150 * can do proper boundary identification in Remix where a thrown Response
1151 * must go to the Catch Boundary but a returned Response is happy-path.
1152 *
1153 * One thing to note is that any Router-initiated Errors that make sense
1154 * to associate with a status code will be thrown as an ErrorResponse
1155 * instance which include the raw Error, such that the calling context can
1156 * serialize the error as they see fit while including the proper response
1157 * code. Examples here are 404 and 405 errors that occur prior to reaching
1158 * any user-defined loaders.
1159 *
1160 * - `opts.routeId` allows you to specify the specific route handler to call.
1161 * If not provided the handler will determine the proper route by matching
1162 * against `request.url`
1163 * - `opts.requestContext` is an optional server context that will be passed
1164 * to actions/loaders in the `context` parameter
1165 */
1166 async function queryRoute(request, { routeId, requestContext, dataStrategy, generateMiddlewareResponse, normalizePath } = {}) {
1167 let normalizePathImpl = normalizePath || defaultNormalizePath;
1168 let method = request.method;
1169 let location = createLocation("", normalizePathImpl(request), null, "default");
1170 let matches = matchRoutesImpl(dataRoutes, location, basename, false, routeBranches);
1171 requestContext = requestContext != null ? requestContext : new RouterContextProvider();
1172 if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") throw getInternalRouterError(405, { method });
1173 else if (!matches) throw getInternalRouterError(404, { pathname: location.pathname });
1174 let match = routeId ? matches.find((m) => m.route.id === routeId) : getTargetMatch(matches, location);
1175 if (routeId && !match) throw getInternalRouterError(403, {
1176 pathname: location.pathname,
1177 routeId
1178 });
1179 else if (!match) throw getInternalRouterError(404, { pathname: location.pathname });
1180 if (generateMiddlewareResponse) {
1181 invariant$1(requestContext instanceof RouterContextProvider, "When using middleware in `staticHandler.queryRoute()`, any provided `requestContext` must be an instance of `RouterContextProvider`");
1182 await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
1183 return await runServerMiddlewarePipeline({
1184 request,
1185 url: createDataFunctionUrl(request, location),
1186 pattern: getRoutePattern(matches),
1187 matches,
1188 params: matches[0].params,
1189 context: requestContext
1190 }, async () => {
1191 return await generateMiddlewareResponse(async (innerRequest) => {
1192 let processed = handleQueryResult(await queryImpl(innerRequest, location, matches, requestContext, dataStrategy || null, false, match, null, false));
1193 return isResponse(processed) ? processed : typeof processed === "string" ? new Response(processed) : Response.json(processed);
1194 });
1195 }, (error) => {
1196 if (isDataWithResponseInit(error)) return Promise.resolve(dataWithResponseInitToResponse(error));
1197 if (isResponse(error)) return Promise.resolve(error);
1198 throw error;
1199 });
1200 }
1201 return handleQueryResult(await queryImpl(request, location, matches, requestContext, dataStrategy || null, false, match, null, false));
1202 function handleQueryResult(result) {
1203 if (isResponse(result)) return result;
1204 let error = result.errors ? Object.values(result.errors)[0] : void 0;
1205 if (error !== void 0) throw error;
1206 if (result.actionData) return Object.values(result.actionData)[0];
1207 if (result.loaderData) return Object.values(result.loaderData)[0];
1208 }
1209 }
1210 async function queryImpl(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, skipRevalidation) {
1211 invariant$1(request.signal, "query()/queryRoute() requests must contain an AbortController signal");
1212 try {
1213 if (isMutationMethod(request.method)) return await submit(request, location, matches, routeMatch || getTargetMatch(matches, location), requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch != null, filterMatchesToLoad, skipRevalidation);
1214 let result = await loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad);
1215 return isResponse(result) ? result : {
1216 ...result,
1217 actionData: null,
1218 actionHeaders: {}
1219 };
1220 } catch (e) {
1221 if (isDataStrategyResult(e) && isResponse(e.result)) {
1222 if (e.type === "error") throw e.result;
1223 return e.result;
1224 }
1225 if (isRedirectResponse(e)) return e;
1226 throw e;
1227 }
1228 }
1229 async function submit(request, location, matches, actionMatch, requestContext, dataStrategy, skipLoaderErrorBubbling, isRouteRequest, filterMatchesToLoad, skipRevalidation) {
1230 let result;
1231 if (!actionMatch.route.action && !actionMatch.route.lazy) {
1232 let error = getInternalRouterError(405, {
1233 method: request.method,
1234 pathname: new URL(request.url).pathname,
1235 routeId: actionMatch.route.id
1236 });
1237 if (isRouteRequest) throw error;
1238 result = {
1239 type: "error",
1240 error
1241 };
1242 } else {
1243 result = (await callDataStrategy(request, location, getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, actionMatch, [], requestContext), isRouteRequest, requestContext, dataStrategy))[actionMatch.route.id];
1244 if (request.signal.aborted) throwStaticHandlerAbortedError(request, isRouteRequest);
1245 }
1246 if (isRedirectResult(result)) throw new Response(null, {
1247 status: result.response.status,
1248 headers: { Location: result.response.headers.get("Location") }
1249 });
1250 if (isRouteRequest) {
1251 if (isErrorResult(result)) throw result.error;
1252 return {
1253 matches: [actionMatch],
1254 loaderData: {},
1255 actionData: { [actionMatch.route.id]: result.data },
1256 errors: null,
1257 statusCode: 200,
1258 loaderHeaders: {},
1259 actionHeaders: {}
1260 };
1261 }
1262 if (skipRevalidation) if (isErrorResult(result)) {
1263 let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
1264 return {
1265 statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
1266 actionData: null,
1267 actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} },
1268 matches,
1269 loaderData: {},
1270 errors: { [boundaryMatch.route.id]: result.error },
1271 loaderHeaders: {}
1272 };
1273 } else return {
1274 actionData: { [actionMatch.route.id]: result.data },
1275 actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {},
1276 matches,
1277 loaderData: {},
1278 errors: null,
1279 statusCode: result.statusCode || 200,
1280 loaderHeaders: {}
1281 };
1282 let loaderRequest = new Request(request.url, {
1283 headers: request.headers,
1284 redirect: request.redirect,
1285 signal: request.signal
1286 });
1287 if (isErrorResult(result)) return {
1288 ...await loadRouteData(loaderRequest, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad, [(skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id)).route.id, result]),
1289 statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
1290 actionData: null,
1291 actionHeaders: { ...result.headers ? { [actionMatch.route.id]: result.headers } : {} }
1292 };
1293 return {
1294 ...await loadRouteData(loaderRequest, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, null, filterMatchesToLoad),
1295 actionData: { [actionMatch.route.id]: result.data },
1296 ...result.statusCode ? { statusCode: result.statusCode } : {},
1297 actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {}
1298 };
1299 }
1300 async function loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, pendingActionResult) {
1301 let isRouteRequest = routeMatch != null;
1302 if (isRouteRequest && !routeMatch?.route.loader && !routeMatch?.route.lazy) throw getInternalRouterError(400, {
1303 method: request.method,
1304 pathname: new URL(request.url).pathname,
1305 routeId: routeMatch?.route.id
1306 });
1307 let dsMatches;
1308 if (routeMatch) dsMatches = getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, location, matches, routeMatch, [], requestContext);
1309 else {
1310 let maxIdx = pendingActionResult && isErrorResult(pendingActionResult[1]) ? matches.findIndex((m) => m.route.id === pendingActionResult[0]) - 1 : void 0;
1311 let pattern = getRoutePattern(matches);
1312 dsMatches = matches.map((match, index) => {
1313 if (maxIdx != null && index > maxIdx) return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, [], requestContext, false);
1314 return getDataStrategyMatch(mapRouteProperties, manifest, request, location, pattern, match, [], requestContext, (match.route.loader || match.route.lazy) != null && (!filterMatchesToLoad || filterMatchesToLoad(match)));
1315 });
1316 }
1317 if (!dataStrategy && !dsMatches.some((m) => m.shouldLoad)) return {
1318 matches,
1319 loaderData: {},
1320 errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? { [pendingActionResult[0]]: pendingActionResult[1].error } : null,
1321 statusCode: 200,
1322 loaderHeaders: {}
1323 };
1324 let results = await callDataStrategy(request, location, dsMatches, isRouteRequest, requestContext, dataStrategy);
1325 if (request.signal.aborted) throwStaticHandlerAbortedError(request, isRouteRequest);
1326 return {
1327 ...processRouteLoaderData(matches, results, pendingActionResult, true, skipLoaderErrorBubbling),
1328 matches
1329 };
1330 }
1331 async function callDataStrategy(request, location, matches, isRouteRequest, requestContext, dataStrategy) {
1332 let results = await callDataStrategyImpl(dataStrategy || defaultDataStrategy, request, location, matches, null, requestContext, true);
1333 let dataResults = {};
1334 await Promise.all(matches.map(async (match) => {
1335 if (!(match.route.id in results)) return;
1336 let result = results[match.route.id];
1337 if (isRedirectDataStrategyResult(result)) {
1338 let response = result.result;
1339 throw normalizeRelativeRoutingRedirectResponse(response, request, match.route.id, matches, basename);
1340 }
1341 if (isRouteRequest) {
1342 if (isResponse(result.result)) throw result;
1343 else if (isDataWithResponseInit(result.result)) throw dataWithResponseInitToResponse(result.result);
1344 }
1345 dataResults[match.route.id] = await convertDataStrategyResultToDataResult(result);
1346 }));
1347 return dataResults;
1348 }
1349 return {
1350 dataRoutes,
1351 _internalRouteBranches: routeBranches,
1352 query,
1353 queryRoute
1354 };
1355}
1356/**
1357* Given an existing StaticHandlerContext and an error thrown at render time,
1358* provide an updated StaticHandlerContext suitable for a second SSR render
1359*
1360* @category Utils
1361*/
1362function getStaticContextFromError(routes, handlerContext, error, boundaryId) {
1363 let errorBoundaryId = boundaryId || handlerContext._deepestRenderedBoundaryId || routes[0].id;
1364 return {
1365 ...handlerContext,
1366 statusCode: isRouteErrorResponse(error) ? error.status : 500,
1367 errors: { [errorBoundaryId]: error }
1368 };
1369}
1370function throwStaticHandlerAbortedError(request, isRouteRequest) {
1371 if (request.signal.reason !== void 0) throw request.signal.reason;
1372 throw new Error(`${isRouteRequest ? "queryRoute" : "query"}() call aborted without an \`AbortSignal.reason\`: ${request.method} ${request.url}`);
1373}
1374function defaultNormalizePath(request) {
1375 let url = new URL(request.url);
1376 return {
1377 pathname: url.pathname,
1378 search: url.search,
1379 hash: url.hash
1380 };
1381}
1382function normalizeTo(location, matches, basename, to, fromRouteId, relative) {
1383 let contextualMatches;
1384 let activeRouteMatch;
1385 if (fromRouteId) {
1386 contextualMatches = [];
1387 for (let match of matches) {
1388 contextualMatches.push(match);
1389 if (match.route.id === fromRouteId) {
1390 activeRouteMatch = match;
1391 break;
1392 }
1393 }
1394 } else {
1395 contextualMatches = matches;
1396 activeRouteMatch = matches[matches.length - 1];
1397 }
1398 let path = resolveTo(to ? to : ".", getResolveToMatches(contextualMatches), stripBasename(location.pathname, basename) || location.pathname, relative === "path");
1399 if (to == null) {
1400 path.search = location.search;
1401 path.hash = location.hash;
1402 }
1403 if ((to == null || to === "" || to === ".") && activeRouteMatch) {
1404 let nakedIndex = hasNakedIndexQuery(path.search);
1405 if (activeRouteMatch.route.index && !nakedIndex) path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
1406 else if (!activeRouteMatch.route.index && nakedIndex) {
1407 let params = new URLSearchParams(path.search);
1408 let indexValues = params.getAll("index");
1409 params.delete("index");
1410 indexValues.filter((v) => v).forEach((v) => params.append("index", v));
1411 let qs = params.toString();
1412 path.search = qs ? `?${qs}` : "";
1413 }
1414 }
1415 if (basename !== "/") path.pathname = prependBasename({
1416 basename,
1417 pathname: path.pathname
1418 });
1419 return createPath(path);
1420}
1421function shouldRevalidateLoader(loaderMatch, arg) {
1422 if (loaderMatch.route.shouldRevalidate) {
1423 let routeChoice = loaderMatch.route.shouldRevalidate(arg);
1424 if (typeof routeChoice === "boolean") return routeChoice;
1425 }
1426 return arg.defaultShouldRevalidate;
1427}
1428const lazyRoutePropertyCache = /* @__PURE__ */ new WeakMap();
1429const loadLazyRouteProperty = ({ key, route, manifest, mapRouteProperties }) => {
1430 let routeToUpdate = manifest[route.id];
1431 invariant$1(routeToUpdate, "No route found in manifest");
1432 if (!routeToUpdate.lazy || typeof routeToUpdate.lazy !== "object") return;
1433 let lazyFn = routeToUpdate.lazy[key];
1434 if (!lazyFn) return;
1435 let cache = lazyRoutePropertyCache.get(routeToUpdate);
1436 if (!cache) {
1437 cache = {};
1438 lazyRoutePropertyCache.set(routeToUpdate, cache);
1439 }
1440 let cachedPromise = cache[key];
1441 if (cachedPromise) return cachedPromise;
1442 let propertyPromise = (async () => {
1443 let isUnsupported = isUnsupportedLazyRouteObjectKey(key);
1444 let isStaticallyDefined = routeToUpdate[key] !== void 0;
1445 if (isUnsupported) {
1446 warning(!isUnsupported, "Route property " + key + " is not a supported lazy route property. This property will be ignored.");
1447 cache[key] = Promise.resolve();
1448 } else if (isStaticallyDefined) warning(false, `Route "${routeToUpdate.id}" has a static property "${key}" defined. The lazy property will be ignored.`);
1449 else {
1450 let value = await lazyFn();
1451 if (value != null) {
1452 Object.assign(routeToUpdate, { [key]: value });
1453 Object.assign(routeToUpdate, mapRouteProperties(routeToUpdate));
1454 }
1455 }
1456 if (typeof routeToUpdate.lazy === "object") {
1457 routeToUpdate.lazy[key] = void 0;
1458 if (Object.values(routeToUpdate.lazy).every((value) => value === void 0)) routeToUpdate.lazy = void 0;
1459 }
1460 })();
1461 cache[key] = propertyPromise;
1462 return propertyPromise;
1463};
1464const lazyRouteFunctionCache = /* @__PURE__ */ new WeakMap();
1465/**
1466* Execute route.lazy functions to lazily load route modules (loader, action,
1467* shouldRevalidate) and update the routeManifest in place which shares objects
1468* with dataRoutes so those get updated as well.
1469*/
1470function loadLazyRoute(route, type, manifest, mapRouteProperties, lazyRoutePropertiesToSkip) {
1471 let routeToUpdate = manifest[route.id];
1472 invariant$1(routeToUpdate, "No route found in manifest");
1473 if (!route.lazy) return {
1474 lazyRoutePromise: void 0,
1475 lazyHandlerPromise: void 0
1476 };
1477 if (typeof route.lazy === "function") {
1478 let cachedPromise = lazyRouteFunctionCache.get(routeToUpdate);
1479 if (cachedPromise) return {
1480 lazyRoutePromise: cachedPromise,
1481 lazyHandlerPromise: cachedPromise
1482 };
1483 let lazyRoutePromise = (async () => {
1484 invariant$1(typeof route.lazy === "function", "No lazy route function found");
1485 let lazyRoute = await route.lazy();
1486 let routeUpdates = {};
1487 for (let lazyRouteProperty in lazyRoute) {
1488 let lazyValue = lazyRoute[lazyRouteProperty];
1489 if (lazyValue === void 0) continue;
1490 let isUnsupported = isUnsupportedLazyRouteFunctionKey(lazyRouteProperty);
1491 let isStaticallyDefined = routeToUpdate[lazyRouteProperty] !== void 0;
1492 if (isUnsupported) warning(!isUnsupported, "Route property " + lazyRouteProperty + " is not a supported property to be returned from a lazy route function. This property will be ignored.");
1493 else if (isStaticallyDefined) warning(!isStaticallyDefined, `Route "${routeToUpdate.id}" has a static property "${lazyRouteProperty}" defined but its lazy function is also returning a value for this property. The lazy route property "${lazyRouteProperty}" will be ignored.`);
1494 else routeUpdates[lazyRouteProperty] = lazyValue;
1495 }
1496 Object.assign(routeToUpdate, routeUpdates);
1497 Object.assign(routeToUpdate, {
1498 ...mapRouteProperties(routeToUpdate),
1499 lazy: void 0
1500 });
1501 })();
1502 lazyRouteFunctionCache.set(routeToUpdate, lazyRoutePromise);
1503 lazyRoutePromise.catch(() => {});
1504 return {
1505 lazyRoutePromise,
1506 lazyHandlerPromise: lazyRoutePromise
1507 };
1508 }
1509 let lazyKeys = Object.keys(route.lazy);
1510 let lazyPropertyPromises = [];
1511 let lazyHandlerPromise = void 0;
1512 for (let key of lazyKeys) {
1513 if (lazyRoutePropertiesToSkip && lazyRoutePropertiesToSkip.includes(key)) continue;
1514 let promise = loadLazyRouteProperty({
1515 key,
1516 route,
1517 manifest,
1518 mapRouteProperties
1519 });
1520 if (promise) {
1521 lazyPropertyPromises.push(promise);
1522 if (key === type) lazyHandlerPromise = promise;
1523 }
1524 }
1525 let lazyRoutePromise = lazyPropertyPromises.length > 0 ? Promise.all(lazyPropertyPromises).then(() => {}) : void 0;
1526 lazyRoutePromise?.catch(() => {});
1527 lazyHandlerPromise?.catch(() => {});
1528 return {
1529 lazyRoutePromise,
1530 lazyHandlerPromise
1531 };
1532}
1533function isNonNullable(value) {
1534 return value !== void 0;
1535}
1536function loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties) {
1537 let promises = matches.map(({ route }) => {
1538 if (typeof route.lazy !== "object" || !route.lazy.middleware) return;
1539 return loadLazyRouteProperty({
1540 key: "middleware",
1541 route,
1542 manifest,
1543 mapRouteProperties
1544 });
1545 }).filter(isNonNullable);
1546 return promises.length > 0 ? Promise.all(promises) : void 0;
1547}
1548async function defaultDataStrategy(args) {
1549 let matchesToLoad = args.matches.filter((m) => m.shouldLoad);
1550 let keyedResults = {};
1551 (await Promise.all(matchesToLoad.map((m) => m.resolve()))).forEach((result, i) => {
1552 keyedResults[matchesToLoad[i].route.id] = result;
1553 });
1554 return keyedResults;
1555}
1556function runServerMiddlewarePipeline(args, handler, errorHandler) {
1557 return runMiddlewarePipeline(args, handler, processResult, isResponse, errorHandler);
1558 function processResult(result) {
1559 return isDataWithResponseInit(result) ? dataWithResponseInitToResponse(result) : result;
1560 }
1561}
1562function runClientMiddlewarePipeline(args, handler) {
1563 return runMiddlewarePipeline(args, handler, (r) => {
1564 if (isRedirectResponse(r)) throw r;
1565 return r;
1566 }, isDataStrategyResults, errorHandler);
1567 async function errorHandler(error, routeId, nextResult) {
1568 if (nextResult) return Object.assign(nextResult.value, { [routeId]: {
1569 type: "error",
1570 result: error
1571 } });
1572 else {
1573 let { matches } = args;
1574 let maxBoundaryIdx = Math.min(Math.max(matches.findIndex((m) => m.route.id === routeId), 0), Math.max(matches.findIndex((m) => m.shouldCallHandler()), 0));
1575 let deepestRouteId = matches[maxBoundaryIdx].route.id;
1576 for (let match of matches.slice(0, maxBoundaryIdx + 1)) try {
1577 await match._lazyPromises?.route;
1578 } catch {
1579 deepestRouteId = match.route.id;
1580 break;
1581 }
1582 return { [findNearestBoundary(matches, deepestRouteId).route.id]: {
1583 type: "error",
1584 result: error
1585 } };
1586 }
1587 }
1588}
1589async function runMiddlewarePipeline(args, handler, processResult, isResult, errorHandler) {
1590 let { matches, ...dataFnArgs } = args;
1591 return await callRouteMiddleware(dataFnArgs, matches.flatMap((m) => m.route.middleware ? m.route.middleware.map((fn) => [m.route.id, fn]) : []), handler, processResult, isResult, errorHandler);
1592}
1593async function callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx = 0) {
1594 let { request } = args;
1595 if (request.signal.aborted) throw request.signal.reason ?? /* @__PURE__ */ new Error(`Request aborted: ${request.method} ${request.url}`);
1596 let tuple = middlewares[idx];
1597 if (!tuple) return await handler();
1598 let [routeId, middleware] = tuple;
1599 let nextResult;
1600 let next = async () => {
1601 if (nextResult) throw new Error("You may only call `next()` once per middleware");
1602 try {
1603 nextResult = { value: await callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx + 1) };
1604 return nextResult.value;
1605 } catch (error) {
1606 nextResult = { value: await errorHandler(error, routeId, nextResult) };
1607 return nextResult.value;
1608 }
1609 };
1610 try {
1611 let value = await middleware(args, next);
1612 let result = value != null ? processResult(value) : void 0;
1613 if (isResult(result)) return result;
1614 else if (nextResult) return result ?? nextResult.value;
1615 else {
1616 nextResult = { value: await next() };
1617 return nextResult.value;
1618 }
1619 } catch (error) {
1620 return await errorHandler(error, routeId, nextResult);
1621 }
1622}
1623function getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip) {
1624 let lazyMiddlewarePromise = loadLazyRouteProperty({
1625 key: "middleware",
1626 route: match.route,
1627 manifest,
1628 mapRouteProperties
1629 });
1630 let lazyRoutePromises = loadLazyRoute(match.route, isMutationMethod(request.method) ? "action" : "loader", manifest, mapRouteProperties, lazyRoutePropertiesToSkip);
1631 return {
1632 middleware: lazyMiddlewarePromise,
1633 route: lazyRoutePromises.lazyRoutePromise,
1634 handler: lazyRoutePromises.lazyHandlerPromise
1635 };
1636}
1637function getDataStrategyMatch(mapRouteProperties, manifest, request, path, pattern, match, lazyRoutePropertiesToSkip, scopedContext, shouldLoad, shouldRevalidateArgs = null, callSiteDefaultShouldRevalidate) {
1638 let isUsingNewApi = false;
1639 let _lazyPromises = getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip);
1640 return {
1641 ...match,
1642 _lazyPromises,
1643 shouldLoad,
1644 shouldRevalidateArgs,
1645 shouldCallHandler(defaultShouldRevalidate) {
1646 isUsingNewApi = true;
1647 if (!shouldRevalidateArgs) return shouldLoad;
1648 if (typeof callSiteDefaultShouldRevalidate === "boolean") return shouldRevalidateLoader(match, {
1649 ...shouldRevalidateArgs,
1650 defaultShouldRevalidate: callSiteDefaultShouldRevalidate
1651 });
1652 if (typeof defaultShouldRevalidate === "boolean") return shouldRevalidateLoader(match, {
1653 ...shouldRevalidateArgs,
1654 defaultShouldRevalidate
1655 });
1656 return shouldRevalidateLoader(match, shouldRevalidateArgs);
1657 },
1658 resolve(handlerOverride) {
1659 let { lazy, loader, middleware } = match.route;
1660 let callHandler = isUsingNewApi || shouldLoad || handlerOverride && !isMutationMethod(request.method) && (lazy || loader);
1661 let isMiddlewareOnlyRoute = middleware && middleware.length > 0 && !loader && !lazy;
1662 if (callHandler && (isMutationMethod(request.method) || !isMiddlewareOnlyRoute)) return callLoaderOrAction({
1663 request,
1664 path,
1665 pattern,
1666 match,
1667 lazyHandlerPromise: _lazyPromises?.handler,
1668 lazyRoutePromise: _lazyPromises?.route,
1669 handlerOverride,
1670 scopedContext
1671 });
1672 return Promise.resolve({
1673 type: "data",
1674 result: void 0
1675 });
1676 }
1677 };
1678}
1679function getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, path, matches, targetMatch, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateArgs = null) {
1680 return matches.map((match) => {
1681 if (match.route.id !== targetMatch.route.id) return {
1682 ...match,
1683 shouldLoad: false,
1684 shouldRevalidateArgs,
1685 shouldCallHandler: () => false,
1686 _lazyPromises: getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip),
1687 resolve: () => Promise.resolve({
1688 type: "data",
1689 result: void 0
1690 })
1691 };
1692 return getDataStrategyMatch(mapRouteProperties, manifest, request, path, getRoutePattern(matches), match, lazyRoutePropertiesToSkip, scopedContext, true, shouldRevalidateArgs);
1693 });
1694}
1695async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, isStaticHandler) {
1696 if (matches.some((m) => m._lazyPromises?.middleware)) await Promise.all(matches.map((m) => m._lazyPromises?.middleware));
1697 let dataStrategyArgs = {
1698 request,
1699 url: createDataFunctionUrl(request, path),
1700 pattern: getRoutePattern(matches),
1701 params: matches[0].params,
1702 context: scopedContext,
1703 matches
1704 };
1705 let runClientMiddleware = isStaticHandler ? () => {
1706 throw new Error("You cannot call `runClientMiddleware()` from a static handler `dataStrategy`. Middleware is run outside of `dataStrategy` during SSR in order to bubble up the Response. You can enable middleware via the `respond` API in `query`/`queryRoute`");
1707 } : (cb) => {
1708 let typedDataStrategyArgs = dataStrategyArgs;
1709 return runClientMiddlewarePipeline(typedDataStrategyArgs, () => {
1710 return cb({
1711 ...typedDataStrategyArgs,
1712 fetcherKey,
1713 runClientMiddleware: () => {
1714 throw new Error("Cannot call `runClientMiddleware()` from within an `runClientMiddleware` handler");
1715 }
1716 });
1717 });
1718 };
1719 let results = await dataStrategyImpl({
1720 ...dataStrategyArgs,
1721 fetcherKey,
1722 runClientMiddleware
1723 });
1724 try {
1725 await Promise.all(matches.flatMap((m) => [m._lazyPromises?.handler, m._lazyPromises?.route]));
1726 } catch (e) {}
1727 return results;
1728}
1729async function callLoaderOrAction({ request, path, pattern, match, lazyHandlerPromise, lazyRoutePromise, handlerOverride, scopedContext }) {
1730 let result;
1731 let onReject;
1732 let isAction = isMutationMethod(request.method);
1733 let type = isAction ? "action" : "loader";
1734 let runHandler = (handler) => {
1735 let reject;
1736 let abortPromise = new Promise((_, r) => reject = r);
1737 onReject = () => reject();
1738 request.signal.addEventListener("abort", onReject);
1739 let actualHandler = (ctx) => {
1740 if (typeof handler !== "function") return Promise.reject(/* @__PURE__ */ new Error(`You cannot call the handler for a route which defines a boolean "${type}" [routeId: ${match.route.id}]`));
1741 return handler({
1742 request,
1743 url: createDataFunctionUrl(request, path),
1744 pattern,
1745 params: match.params,
1746 context: scopedContext
1747 }, ...ctx !== void 0 ? [ctx] : []);
1748 };
1749 let handlerPromise = (async () => {
1750 try {
1751 return {
1752 type: "data",
1753 result: await (handlerOverride ? handlerOverride((ctx) => actualHandler(ctx)) : actualHandler())
1754 };
1755 } catch (e) {
1756 return {
1757 type: "error",
1758 result: e
1759 };
1760 }
1761 })();
1762 return Promise.race([handlerPromise, abortPromise]);
1763 };
1764 try {
1765 let handler = isAction ? match.route.action : match.route.loader;
1766 if (lazyHandlerPromise || lazyRoutePromise) if (handler) {
1767 let handlerError;
1768 let [value] = await Promise.all([
1769 runHandler(handler).catch((e) => {
1770 handlerError = e;
1771 }),
1772 lazyHandlerPromise,
1773 lazyRoutePromise
1774 ]);
1775 if (handlerError !== void 0) throw handlerError;
1776 result = value;
1777 } else {
1778 await lazyHandlerPromise;
1779 let handler = isAction ? match.route.action : match.route.loader;
1780 if (handler) [result] = await Promise.all([runHandler(handler), lazyRoutePromise]);
1781 else if (type === "action") {
1782 let url = new URL(request.url);
1783 let pathname = url.pathname + url.search;
1784 throw getInternalRouterError(405, {
1785 method: request.method,
1786 pathname,
1787 routeId: match.route.id
1788 });
1789 } else return {
1790 type: "data",
1791 result: void 0
1792 };
1793 }
1794 else if (!handler) {
1795 let url = new URL(request.url);
1796 throw getInternalRouterError(404, { pathname: url.pathname + url.search });
1797 } else result = await runHandler(handler);
1798 } catch (e) {
1799 return {
1800 type: "error",
1801 result: e
1802 };
1803 } finally {
1804 if (onReject) request.signal.removeEventListener("abort", onReject);
1805 }
1806 return result;
1807}
1808async function parseResponseBody(response) {
1809 let contentType = response.headers.get("Content-Type");
1810 if (contentType && /\bapplication\/json\b/.test(contentType)) return response.body == null ? null : response.json();
1811 return response.text();
1812}
1813async function convertDataStrategyResultToDataResult(dataStrategyResult) {
1814 let { result, type } = dataStrategyResult;
1815 if (isResponse(result)) {
1816 let data;
1817 try {
1818 data = await parseResponseBody(result);
1819 } catch (e) {
1820 return {
1821 type: "error",
1822 error: e
1823 };
1824 }
1825 if (type === "error") return {
1826 type: "error",
1827 error: new ErrorResponseImpl(result.status, result.statusText, data),
1828 statusCode: result.status,
1829 headers: result.headers
1830 };
1831 return {
1832 type: "data",
1833 data,
1834 statusCode: result.status,
1835 headers: result.headers
1836 };
1837 }
1838 if (type === "error") {
1839 if (isDataWithResponseInit(result)) {
1840 if (result.data instanceof Error) return {
1841 type: "error",
1842 error: result.data,
1843 statusCode: result.init?.status,
1844 headers: result.init?.headers ? new Headers(result.init.headers) : void 0
1845 };
1846 return {
1847 type: "error",
1848 error: dataWithResponseInitToErrorResponse(result),
1849 statusCode: isRouteErrorResponse(result) ? result.status : void 0,
1850 headers: result.init?.headers ? new Headers(result.init.headers) : void 0
1851 };
1852 }
1853 return {
1854 type: "error",
1855 error: result,
1856 statusCode: isRouteErrorResponse(result) ? result.status : void 0
1857 };
1858 }
1859 if (isDataWithResponseInit(result)) return {
1860 type: "data",
1861 data: result.data,
1862 statusCode: result.init?.status,
1863 headers: result.init?.headers ? new Headers(result.init.headers) : void 0
1864 };
1865 return {
1866 type: "data",
1867 data: result
1868 };
1869}
1870function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename) {
1871 let location = response.headers.get("Location");
1872 invariant$1(location, "Redirects returned/thrown from loaders/actions must have a Location header");
1873 if (!isAbsoluteUrl(location)) {
1874 let trimmedMatches = matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1);
1875 location = normalizeTo(new URL(request.url), trimmedMatches, basename, location);
1876 response.headers.set("Location", location);
1877 }
1878 return response;
1879}
1880function createDataFunctionUrl(request, path) {
1881 let url = new URL(request.url);
1882 let parsed = typeof path === "string" ? parsePath(path) : path;
1883 url.pathname = parsed.pathname || "/";
1884 if (parsed.search) {
1885 let searchParams = new URLSearchParams(parsed.search);
1886 let indexValues = searchParams.getAll("index");
1887 searchParams.delete("index");
1888 for (let value of indexValues.filter(Boolean)) searchParams.append("index", value);
1889 let search = searchParams.toString();
1890 url.search = search ? `?${search}` : "";
1891 } else url.search = "";
1892 url.hash = parsed.hash || "";
1893 return url;
1894}
1895function processRouteLoaderData(matches, results, pendingActionResult, isStaticHandler = false, skipLoaderErrorBubbling = false) {
1896 let loaderData = {};
1897 let errors = null;
1898 let statusCode;
1899 let foundError = false;
1900 let loaderHeaders = {};
1901 let pendingError = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : void 0;
1902 matches.forEach((match) => {
1903 if (!(match.route.id in results)) return;
1904 let id = match.route.id;
1905 let result = results[id];
1906 invariant$1(!isRedirectResult(result), "Cannot handle redirect results in processLoaderData");
1907 if (isErrorResult(result)) {
1908 let error = result.error;
1909 if (pendingError !== void 0) {
1910 error = pendingError;
1911 pendingError = void 0;
1912 }
1913 errors = errors || {};
1914 if (skipLoaderErrorBubbling) errors[id] = error;
1915 else {
1916 let boundaryMatch = findNearestBoundary(matches, id);
1917 if (errors[boundaryMatch.route.id] == null) errors[boundaryMatch.route.id] = error;
1918 }
1919 if (!isStaticHandler) loaderData[id] = ResetLoaderDataSymbol;
1920 if (!foundError) {
1921 foundError = true;
1922 statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
1923 }
1924 if (result.headers) loaderHeaders[id] = result.headers;
1925 } else {
1926 loaderData[id] = result.data;
1927 if (result.statusCode && result.statusCode !== 200 && !foundError) statusCode = result.statusCode;
1928 if (result.headers) loaderHeaders[id] = result.headers;
1929 }
1930 });
1931 if (pendingError !== void 0 && pendingActionResult) {
1932 errors = { [pendingActionResult[0]]: pendingError };
1933 if (pendingActionResult[2]) loaderData[pendingActionResult[2]] = void 0;
1934 }
1935 return {
1936 loaderData,
1937 errors,
1938 statusCode: statusCode || 200,
1939 loaderHeaders
1940 };
1941}
1942function findNearestBoundary(matches, routeId) {
1943 return (routeId ? matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1) : [...matches]).reverse().find((m) => m.route.ErrorBoundary != null || m.route.errorElement != null) || matches[0];
1944}
1945function getShortCircuitMatches(routes) {
1946 let route = routes.length === 1 ? routes[0] : routes.find((r) => r.index || !r.path || r.path === "/") || { id: `__shim-error-route__` };
1947 return {
1948 matches: [{
1949 params: {},
1950 pathname: "",
1951 pathnameBase: "",
1952 route
1953 }],
1954 route
1955 };
1956}
1957function getInternalRouterError(status, { pathname, routeId, method, type, message } = {}) {
1958 let statusText = "Unknown Server Error";
1959 let errorMessage = "Unknown @remix-run/router error";
1960 if (status === 400) {
1961 statusText = "Bad Request";
1962 if (method && pathname && routeId) errorMessage = `You made a ${method} request to "${pathname}" but did not provide a \`loader\` for route "${routeId}", so there is no way to handle the request.`;
1963 else if (type === "invalid-body") errorMessage = "Unable to encode submission body";
1964 } else if (status === 403) {
1965 statusText = "Forbidden";
1966 errorMessage = `Route "${routeId}" does not match URL "${pathname}"`;
1967 } else if (status === 404) {
1968 statusText = "Not Found";
1969 errorMessage = `No route matches URL "${pathname}"`;
1970 } else if (status === 405) {
1971 statusText = "Method Not Allowed";
1972 if (method && pathname && routeId) errorMessage = `You made a ${method.toUpperCase()} request to "${pathname}" but did not provide an \`action\` for route "${routeId}", so there is no way to handle the request.`;
1973 else if (method) errorMessage = `Invalid request method "${method.toUpperCase()}"`;
1974 }
1975 return new ErrorResponseImpl(status || 500, statusText, new Error(errorMessage), true);
1976}
1977function dataWithResponseInitToResponse(data) {
1978 return Response.json(data.data, data.init ?? void 0);
1979}
1980function dataWithResponseInitToErrorResponse(data) {
1981 return new ErrorResponseImpl(data.init?.status ?? 500, data.init?.statusText ?? "Internal Server Error", data.data);
1982}
1983function isDataStrategyResults(result) {
1984 return result != null && typeof result === "object" && Object.entries(result).every(([key, value]) => typeof key === "string" && isDataStrategyResult(value));
1985}
1986function isDataStrategyResult(result) {
1987 return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === "data" || result.type === "error");
1988}
1989function isRedirectDataStrategyResult(result) {
1990 return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
1991}
1992function isErrorResult(result) {
1993 return result.type === "error";
1994}
1995function isRedirectResult(result) {
1996 return (result && result.type) === "redirect";
1997}
1998function isDataWithResponseInit(value) {
1999 return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
2000}
2001function isResponse(value) {
2002 return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
2003}
2004function isRedirectStatusCode(statusCode) {
2005 return redirectStatusCodes.has(statusCode);
2006}
2007function isRedirectResponse(result) {
2008 return isResponse(result) && isRedirectStatusCode(result.status) && result.headers.has("Location");
2009}
2010function isValidMethod(method) {
2011 return validRequestMethods.has(method.toUpperCase());
2012}
2013function isMutationMethod(method) {
2014 return validMutationMethods.has(method.toUpperCase());
2015}
2016function hasNakedIndexQuery(search) {
2017 return new URLSearchParams(search).getAll("index").some((v) => v === "");
2018}
2019function getTargetMatch(matches, location) {
2020 let search = typeof location === "string" ? parsePath(location).search : location.search;
2021 if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) return matches[matches.length - 1];
2022 let pathMatches = getPathContributingMatches(matches);
2023 return pathMatches[pathMatches.length - 1];
2024}
2025//#endregion
2026//#region lib/server-runtime/invariant.ts
2027function invariant(value, message) {
2028 if (value === false || value === null || typeof value === "undefined") {
2029 console.error("The following error is a bug in React Router; please open an issue! https://github.com/remix-run/react-router/issues/new/choose");
2030 throw new Error(message);
2031 }
2032}
2033//#endregion
2034//#region lib/server-runtime/headers.ts
2035function getDocumentHeadersImpl(context, getRouteHeadersFn, _defaultHeaders) {
2036 let boundaryIdx = context.errors ? context.matches.findIndex((m) => context.errors[m.route.id]) : -1;
2037 let matches = boundaryIdx >= 0 ? context.matches.slice(0, boundaryIdx + 1) : context.matches;
2038 let errorHeaders;
2039 if (boundaryIdx >= 0) {
2040 let { actionHeaders, actionData, loaderHeaders, loaderData } = context;
2041 context.matches.slice(boundaryIdx).some((match) => {
2042 let id = match.route.id;
2043 if (actionHeaders[id] && (!actionData || !actionData.hasOwnProperty(id))) errorHeaders = actionHeaders[id];
2044 else if (loaderHeaders[id] && !loaderData.hasOwnProperty(id)) errorHeaders = loaderHeaders[id];
2045 return errorHeaders != null;
2046 });
2047 }
2048 const defaultHeaders = new Headers(_defaultHeaders);
2049 return matches.reduce((parentHeaders, match, idx) => {
2050 let { id } = match.route;
2051 let loaderHeaders = context.loaderHeaders[id] || new Headers();
2052 let actionHeaders = context.actionHeaders[id] || new Headers();
2053 let includeErrorHeaders = errorHeaders != null && idx === matches.length - 1;
2054 let includeErrorCookies = includeErrorHeaders && errorHeaders !== loaderHeaders && errorHeaders !== actionHeaders;
2055 let headersFn = getRouteHeadersFn(match);
2056 if (headersFn == null) {
2057 let headers = new Headers(parentHeaders);
2058 if (includeErrorCookies) prependCookies(errorHeaders, headers);
2059 prependCookies(actionHeaders, headers);
2060 prependCookies(loaderHeaders, headers);
2061 return headers;
2062 }
2063 let headers = new Headers(typeof headersFn === "function" ? headersFn({
2064 loaderHeaders,
2065 parentHeaders,
2066 actionHeaders,
2067 errorHeaders: includeErrorHeaders ? errorHeaders : void 0
2068 }) : headersFn);
2069 if (includeErrorCookies) prependCookies(errorHeaders, headers);
2070 prependCookies(actionHeaders, headers);
2071 prependCookies(loaderHeaders, headers);
2072 prependCookies(parentHeaders, headers);
2073 return headers;
2074 }, new Headers(defaultHeaders));
2075}
2076function prependCookies(parentHeaders, childHeaders) {
2077 let parentSetCookieString = parentHeaders.get("Set-Cookie");
2078 if (parentSetCookieString) {
2079 let cookies = splitSetCookieString(parentSetCookieString);
2080 let childCookies = new Set(childHeaders.getSetCookie());
2081 cookies.forEach((cookie) => {
2082 if (!childCookies.has(cookie)) childHeaders.append("Set-Cookie", cookie);
2083 });
2084 }
2085}
2086//#endregion
2087//#region lib/server-runtime/warnings.ts
2088const alreadyWarned = {};
2089function warnOnce(condition, message) {
2090 if (!condition && !alreadyWarned[message]) {
2091 alreadyWarned[message] = true;
2092 console.warn(message);
2093 }
2094}
2095//#endregion
2096//#region lib/errors.ts
2097const ERROR_DIGEST_BASE = "REACT_ROUTER_ERROR";
2098const ERROR_DIGEST_REDIRECT = "REDIRECT";
2099const ERROR_DIGEST_ROUTE_ERROR_RESPONSE = "ROUTE_ERROR_RESPONSE";
2100function createRedirectErrorDigest(response) {
2101 return `${ERROR_DIGEST_BASE}:${ERROR_DIGEST_REDIRECT}:${JSON.stringify({
2102 status: response.status,
2103 statusText: response.statusText,
2104 location: response.headers.get("Location"),
2105 reloadDocument: response.headers.get("X-Remix-Reload-Document") === "true",
2106 replace: response.headers.get("X-Remix-Replace") === "true"
2107 })}`;
2108}
2109function createRouteErrorResponseDigest(response) {
2110 let status = 500;
2111 let statusText = "";
2112 let data;
2113 if (isDataWithResponseInit(response)) {
2114 status = response.init?.status ?? status;
2115 statusText = response.init?.statusText ?? statusText;
2116 data = response.data;
2117 } else {
2118 status = response.status;
2119 statusText = response.statusText;
2120 data = void 0;
2121 }
2122 return `${ERROR_DIGEST_BASE}:${ERROR_DIGEST_ROUTE_ERROR_RESPONSE}:${JSON.stringify({
2123 status,
2124 statusText,
2125 data
2126 })}`;
2127}
2128function getPathsWithAncestors(paths) {
2129 let result = /* @__PURE__ */ new Set();
2130 paths.forEach((path) => {
2131 if (!path.startsWith("/")) path = `/${path}`;
2132 for (let i = 1; i < path.length; i++) if (path[i] === "/") result.add(path.slice(0, i));
2133 result.add(path);
2134 });
2135 return Array.from(result);
2136}
2137//#endregion
2138//#region lib/actions.ts
2139function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
2140 let originHeader = request.headers.get("origin");
2141 let originDomain = null;
2142 try {
2143 originDomain = typeof originHeader === "string" && originHeader !== "null" ? new URL(originHeader).host : originHeader;
2144 } catch {
2145 throw new Error(`\`origin\` header is not a valid URL. Aborting the action.`);
2146 }
2147 let host = new URL(request.url).host;
2148 if (originDomain && originDomain !== host) {
2149 if (!isAllowedOrigin(originDomain, allowedActionOrigins)) throw new Error("The `request.url` host does not match `origin` header from a forwarded action request. Aborting the action.");
2150 }
2151}
2152function matchWildcardDomain(domain, pattern) {
2153 const domainParts = domain.split(".");
2154 const patternParts = pattern.split(".");
2155 if (patternParts.length < 1) return false;
2156 if (domainParts.length < patternParts.length) return false;
2157 while (patternParts.length) {
2158 const patternPart = patternParts.pop();
2159 const domainPart = domainParts.pop();
2160 switch (patternPart) {
2161 case "": return false;
2162 case "*": if (domainPart) continue;
2163 else return false;
2164 case "**":
2165 if (patternParts.length > 0) return false;
2166 return domainPart !== void 0;
2167 case void 0:
2168 default: if (domainPart !== patternPart) return false;
2169 }
2170 }
2171 return domainParts.length === 0;
2172}
2173function isAllowedOrigin(originDomain, allowedActionOrigins = []) {
2174 return allowedActionOrigins.some((allowedOrigin) => allowedOrigin && (allowedOrigin === originDomain || matchWildcardDomain(originDomain, allowedOrigin)));
2175}
2176//#endregion
2177//#region lib/server-runtime/urls.ts
2178function getNormalizedPath(request) {
2179 let url = new URL(request.url);
2180 let pathname = url.pathname;
2181 if (pathname.endsWith("/_.data")) pathname = pathname.replace(/_\.data$/, "");
2182 else pathname = pathname.replace(/\.data$/, "");
2183 let searchParams = new URLSearchParams(url.search);
2184 searchParams.delete("_routes");
2185 let search = searchParams.toString();
2186 if (search) search = `?${search}`;
2187 return {
2188 pathname,
2189 search,
2190 hash: ""
2191 };
2192}
2193//#endregion
2194//#region lib/rsc/server.rsc.ts
2195const Outlet$2 = Outlet$1;
2196const WithComponentProps = UNSAFE_WithComponentProps;
2197const WithErrorBoundaryProps = UNSAFE_WithErrorBoundaryProps;
2198const WithHydrateFallbackProps = UNSAFE_WithHydrateFallbackProps;
2199const globalVar = typeof globalThis !== "undefined" ? globalThis : global;
2200const ServerStorage = globalVar.___reactRouterServerStorage___ ??= new AsyncLocalStorage();
2201function getRequest() {
2202 const ctx = ServerStorage.getStore();
2203 if (!ctx) throw new Error("getRequest must be called from within a React Server render context");
2204 return ctx.request;
2205}
2206const redirect = (...args) => {
2207 const response = redirect$1(...args);
2208 const ctx = ServerStorage.getStore();
2209 if (ctx && ctx.runningAction) ctx.redirect = response;
2210 return response;
2211};
2212const redirectDocument = (...args) => {
2213 const response = redirectDocument$1(...args);
2214 const ctx = ServerStorage.getStore();
2215 if (ctx && ctx.runningAction) ctx.redirect = response;
2216 return response;
2217};
2218const replace = (...args) => {
2219 const response = replace$1(...args);
2220 const ctx = ServerStorage.getStore();
2221 if (ctx && ctx.runningAction) ctx.redirect = response;
2222 return response;
2223};
2224const cachedResolvePromise = React.cache(async (resolve) => {
2225 return Promise.allSettled([resolve]).then((r) => r[0]);
2226});
2227const Await = (async ({ children, resolve, errorElement }) => {
2228 let resolved = await cachedResolvePromise(resolve);
2229 if (resolved.status === "rejected" && !errorElement) throw resolved.reason;
2230 if (resolved.status === "rejected") return React.createElement(UNSAFE_AwaitContextProvider, {
2231 children: React.createElement(React.Fragment, null, errorElement),
2232 value: {
2233 _tracked: true,
2234 _error: resolved.reason
2235 }
2236 });
2237 const toRender = typeof children === "function" ? children(resolved.value) : children;
2238 return React.createElement(UNSAFE_AwaitContextProvider, {
2239 children: toRender,
2240 value: {
2241 _tracked: true,
2242 _data: resolved.value
2243 }
2244 });
2245});
2246/**
2247* Matches the given routes to a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
2248* and returns an [RSC](https://react.dev/reference/rsc/server-components)
2249* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
2250* encoding an {@link unstable_RSCPayload} for consumption by an [RSC](https://react.dev/reference/rsc/server-components)
2251* enabled client router.
2252*
2253* @example
2254* import {
2255* createTemporaryReferenceSet,
2256* decodeAction,
2257* decodeReply,
2258* loadServerAction,
2259* renderToReadableStream,
2260* } from "@vitejs/plugin-rsc/rsc";
2261* import { unstable_matchRSCServerRequest as matchRSCServerRequest } from "react-router";
2262*
2263* matchRSCServerRequest({
2264* createTemporaryReferenceSet,
2265* decodeAction,
2266* decodeFormState,
2267* decodeReply,
2268* loadServerAction,
2269* request,
2270* routes: routes(),
2271* generateResponse(match) {
2272* return new Response(
2273* renderToReadableStream(match.payload),
2274* {
2275* status: match.statusCode,
2276* headers: match.headers,
2277* }
2278* );
2279* },
2280* });
2281*
2282* @name unstable_matchRSCServerRequest
2283* @public
2284* @category RSC
2285* @mode data
2286* @param opts Options
2287* @param opts.allowedActionOrigins Origin patterns that are allowed to execute actions.
2288* @param opts.basename The basename to use when matching the request.
2289* @param opts.createTemporaryReferenceSet A function that returns a temporary
2290* reference set for the request, used to track temporary references in the [RSC](https://react.dev/reference/rsc/server-components)
2291* stream.
2292* @param opts.decodeAction Your `react-server-dom-xyz/server`'s `decodeAction`
2293* function, responsible for loading a server action.
2294* @param opts.decodeFormState A function responsible for decoding form state for
2295* progressively enhanceable forms with React's [`useActionState`](https://react.dev/reference/react/useActionState)
2296* using your `react-server-dom-xyz/server`'s `decodeFormState`.
2297* @param opts.decodeReply Your `react-server-dom-xyz/server`'s `decodeReply`
2298* function, used to decode the server function's arguments and bind them to the
2299* implementation for invocation by the router.
2300* @param opts.generateResponse A function responsible for using your
2301* `renderToReadableStream` to generate a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
2302* encoding the {@link unstable_RSCPayload}.
2303* @param opts.loadServerAction Your `react-server-dom-xyz/server`'s
2304* `loadServerAction` function, used to load a server action by ID.
2305* @param opts.onError An optional error handler that will be called with any
2306* errors that occur during the request processing.
2307* @param opts.request The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
2308* to match against.
2309* @param opts.requestContext An instance of {@link RouterContextProvider}
2310* that should be created per request, to be passed to [`action`](../../start/data/route-object#action)s,
2311* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
2312* @param opts.routeDiscovery The route discovery configuration, used to determine how the router should discover new routes during navigations.
2313* @param opts.routes Your {@link unstable_RSCRouteConfigEntry | route definitions}.
2314* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
2315* that contains the [RSC](https://react.dev/reference/rsc/server-components)
2316* data for hydration.
2317*/
2318async function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse }) {
2319 let url = new URL(request.url);
2320 basename = basename || "/";
2321 let normalizedPath = url.pathname;
2322 if (url.pathname.endsWith("/_.rsc")) normalizedPath = url.pathname.replace(/_\.rsc$/, "");
2323 else if (url.pathname.endsWith(".rsc")) normalizedPath = url.pathname.replace(/\.rsc$/, "");
2324 if (stripBasename(normalizedPath, basename) !== "/" && normalizedPath.endsWith("/")) normalizedPath = normalizedPath.slice(0, -1);
2325 url.pathname = normalizedPath;
2326 basename = basename.length > normalizedPath.length ? normalizedPath : basename;
2327 let routerRequest = new Request(url.toString(), {
2328 method: request.method,
2329 headers: request.headers,
2330 body: request.body,
2331 signal: request.signal,
2332 duplex: request.body ? "half" : void 0
2333 });
2334 const temporaryReferences = createTemporaryReferenceSet();
2335 const requestUrl = new URL(request.url);
2336 if (isManifestRequest(requestUrl)) return await generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery);
2337 let isDataRequest = isReactServerRequest(requestUrl);
2338 let matches = matchRoutes(routes, url.pathname, basename);
2339 if (matches) await Promise.all(matches.map((m) => explodeLazyRoute(m.route)));
2340 const leafMatch = matches?.[matches.length - 1];
2341 if (!isDataRequest && leafMatch && !leafMatch.route.Component && !leafMatch.route.ErrorBoundary) return generateResourceResponse(routerRequest, routes, basename, leafMatch.route.id, requestContext, onError);
2342 let response = await generateRenderResponse(routerRequest, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery);
2343 response.headers.set("X-Remix-Response", "yes");
2344 return response;
2345}
2346async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery) {
2347 let url = new URL(request.url);
2348 if (url.toString().length > 7680) return new Response(null, {
2349 statusText: "Bad Request",
2350 status: 400
2351 });
2352 if (routeDiscovery?.mode === "initial") {
2353 let payload = {
2354 type: "manifest",
2355 patches: getAllRoutePatches(routes, basename)
2356 };
2357 return generateResponse({
2358 statusCode: 200,
2359 headers: new Headers({
2360 "Content-Type": "text/x-component",
2361 Vary: "Content-Type"
2362 }),
2363 payload
2364 }, {
2365 temporaryReferences,
2366 onError: defaultOnError
2367 });
2368 }
2369 let pathParam = url.searchParams.get("paths");
2370 let pathnames = pathParam ? pathParam.split(",").filter(Boolean) : [url.pathname.replace(/\.manifest$/, "")];
2371 let routeIds = /* @__PURE__ */ new Set();
2372 let matchedRoutes = pathnames.flatMap((pathname) => {
2373 let pathnameMatches = matchRoutes(routes, pathname, basename);
2374 return pathnameMatches?.map((m, i) => ({
2375 ...m.route,
2376 parentId: pathnameMatches[i - 1]?.route.id
2377 })) ?? [];
2378 }).filter((route) => {
2379 if (!routeIds.has(route.id)) {
2380 routeIds.add(route.id);
2381 return true;
2382 }
2383 return false;
2384 });
2385 let payload = {
2386 type: "manifest",
2387 patches: Promise.all([...matchedRoutes.map((route) => getManifestRoute(route)), getAdditionalRoutePatches(pathnames, routes, basename, Array.from(routeIds))]).then((r) => r.flat(1))
2388 };
2389 return generateResponse({
2390 statusCode: 200,
2391 headers: new Headers({ "Content-Type": "text/x-component" }),
2392 payload
2393 }, {
2394 temporaryReferences,
2395 onError: defaultOnError
2396 });
2397}
2398function prependBasenameToRedirectResponse(response, basename = "/") {
2399 if (basename === "/") return response;
2400 let redirect = response.headers.get("Location");
2401 if (!redirect || isAbsoluteUrl(redirect)) return response;
2402 response.headers.set("Location", prependBasename({
2403 basename,
2404 pathname: redirect
2405 }));
2406 return response;
2407}
2408async function processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences) {
2409 const getRevalidationRequest = () => new Request(request.url, {
2410 method: "GET",
2411 headers: request.headers,
2412 signal: request.signal
2413 });
2414 const isFormRequest = canDecodeWithFormData(request.headers.get("Content-Type"));
2415 const actionId = request.headers.get("rsc-action-id");
2416 if (actionId) {
2417 if (!decodeReply || !loadServerAction) throw new Error("Cannot handle enhanced server action without decodeReply and loadServerAction functions");
2418 const actionArgs = await decodeReply(isFormRequest ? await request.formData() : await request.text(), { temporaryReferences });
2419 const serverAction = (await loadServerAction(actionId)).bind(null, ...actionArgs);
2420 let actionResult = Promise.resolve(serverAction());
2421 try {
2422 await actionResult;
2423 } catch (error) {
2424 if (isResponse(error)) return error;
2425 onError?.(error);
2426 }
2427 let maybeFormData = actionArgs.length === 1 ? actionArgs[0] : actionArgs[1];
2428 let skipRevalidation = (maybeFormData && typeof maybeFormData === "object" && maybeFormData instanceof FormData ? maybeFormData : null)?.has("$SKIP_REVALIDATION") ?? false;
2429 return {
2430 actionResult,
2431 revalidationRequest: getRevalidationRequest(),
2432 skipRevalidation
2433 };
2434 } else if (isFormRequest) {
2435 const formData = await request.clone().formData();
2436 if (Array.from(formData.keys()).some((k) => k.startsWith("$ACTION_"))) {
2437 if (!decodeAction) throw new Error("Cannot handle form actions without a decodeAction function");
2438 const action = await decodeAction(formData);
2439 let formState = void 0;
2440 try {
2441 let result = await action();
2442 if (isRedirectResponse(result)) result = prependBasenameToRedirectResponse(result, basename);
2443 formState = decodeFormState?.(result, formData);
2444 } catch (error) {
2445 if (isRedirectResponse(error)) return prependBasenameToRedirectResponse(error, basename);
2446 if (isResponse(error)) return error;
2447 onError?.(error);
2448 }
2449 return {
2450 formState,
2451 revalidationRequest: getRevalidationRequest(),
2452 skipRevalidation: false
2453 };
2454 }
2455 }
2456}
2457async function generateResourceResponse(request, routes, basename, routeId, requestContext, onError) {
2458 try {
2459 return await createStaticHandler(routes, { basename }).queryRoute(request, {
2460 routeId,
2461 requestContext,
2462 async generateMiddlewareResponse(queryRoute) {
2463 try {
2464 return generateResourceResponse(await queryRoute(request));
2465 } catch (error) {
2466 return generateErrorResponse(error);
2467 }
2468 },
2469 normalizePath: (r) => getNormalizedPath(r)
2470 });
2471 } catch (error) {
2472 return generateErrorResponse(error);
2473 }
2474 function generateErrorResponse(error) {
2475 let response;
2476 if (isResponse(error)) response = error;
2477 else if (isRouteErrorResponse(error)) {
2478 onError?.(error);
2479 const errorMessage = typeof error.data === "string" ? error.data : error.statusText;
2480 response = new Response(errorMessage, {
2481 status: error.status,
2482 statusText: error.statusText
2483 });
2484 } else {
2485 onError?.(error);
2486 response = new Response("Internal Server Error", { status: 500 });
2487 }
2488 return generateResourceResponse(response);
2489 }
2490 function generateResourceResponse(response) {
2491 const headers = new Headers(response.headers);
2492 headers.set("React-Router-Resource", "true");
2493 return new Response(response.body, {
2494 status: response.status,
2495 statusText: response.statusText,
2496 headers
2497 });
2498 }
2499}
2500async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery) {
2501 let statusCode = 200;
2502 let url = new URL(request.url);
2503 let isSubmission = isMutationMethod(request.method);
2504 let routeIdsToLoad = !isSubmission && url.searchParams.has("_routes") ? url.searchParams.get("_routes").split(",") : null;
2505 const staticHandler = createStaticHandler(routes, { basename });
2506 let actionResult;
2507 const ctx = {
2508 request,
2509 runningAction: false
2510 };
2511 const result = await ServerStorage.run(ctx, () => staticHandler.query(request, {
2512 requestContext,
2513 skipLoaderErrorBubbling: isDataRequest,
2514 skipRevalidation: isSubmission,
2515 ...routeIdsToLoad ? { filterMatchesToLoad: (m) => routeIdsToLoad.includes(m.route.id) } : {},
2516 normalizePath: (r) => getNormalizedPath(r),
2517 async generateMiddlewareResponse(query) {
2518 let formState;
2519 let skipRevalidation = false;
2520 let potentialCSRFAttackError;
2521 if (isMutationMethod(request.method)) try {
2522 throwIfPotentialCSRFAttack(request, allowedActionOrigins);
2523 ctx.runningAction = true;
2524 let result = await processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences).finally(() => {
2525 ctx.runningAction = false;
2526 });
2527 if (isResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
2528 skipRevalidation = result?.skipRevalidation ?? false;
2529 actionResult = result?.actionResult;
2530 formState = result?.formState;
2531 request = result?.revalidationRequest ?? request;
2532 if (ctx.redirect) return generateRedirectResponse(ctx.redirect, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, void 0);
2533 } catch (error) {
2534 potentialCSRFAttackError = error;
2535 }
2536 let staticContext = await query(request, skipRevalidation || !!potentialCSRFAttackError ? { filterMatchesToLoad: () => false } : void 0);
2537 if (isResponse(staticContext)) return generateRedirectResponse(staticContext, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
2538 if (potentialCSRFAttackError) {
2539 staticContext.errors ??= {};
2540 staticContext.errors[staticContext.matches[0].route.id] = potentialCSRFAttackError;
2541 staticContext.statusCode = 400;
2542 }
2543 return generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, ctx.redirect?.headers, routeDiscovery);
2544 }
2545 }));
2546 if (isRedirectResponse(result)) return generateRedirectResponse(result, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, ctx.redirect?.headers);
2547 invariant(isResponse(result), "Expected a response from query");
2548 return result;
2549}
2550function generateRedirectResponse(response, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, sideEffectRedirectHeaders) {
2551 let redirect = response.headers.get("Location");
2552 if (isDataRequest && basename) redirect = stripBasename(redirect, basename) || redirect;
2553 let payload = {
2554 type: "redirect",
2555 location: redirect,
2556 reload: response.headers.get("X-Remix-Reload-Document") === "true",
2557 replace: response.headers.get("X-Remix-Replace") === "true",
2558 status: response.status,
2559 actionResult
2560 };
2561 let headers = new Headers(sideEffectRedirectHeaders);
2562 for (const [key, value] of response.headers.entries()) headers.append(key, value);
2563 headers.delete("Location");
2564 headers.delete("X-Remix-Reload-Document");
2565 headers.delete("X-Remix-Replace");
2566 headers.delete("Content-Length");
2567 headers.set("Content-Type", "text/x-component");
2568 return generateResponse({
2569 statusCode: 202,
2570 headers,
2571 payload
2572 }, {
2573 temporaryReferences,
2574 onError: defaultOnError
2575 });
2576}
2577async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery) {
2578 statusCode = staticContext.statusCode ?? statusCode;
2579 if (staticContext.errors) staticContext.errors = Object.fromEntries(Object.entries(staticContext.errors).map(([key, error]) => [key, isRouteErrorResponse(error) ? Object.fromEntries(Object.entries(error)) : error]));
2580 staticContext.matches.forEach((m) => {
2581 const routeHasNoLoaderData = staticContext.loaderData[m.route.id] === void 0;
2582 const routeHasError = Boolean(staticContext.errors && m.route.id in staticContext.errors);
2583 if (routeHasNoLoaderData && !routeHasError) staticContext.loaderData[m.route.id] = null;
2584 });
2585 let headers = getDocumentHeadersImpl(staticContext, (match) => match.route.headers, sideEffectRedirectHeaders);
2586 headers.delete("Content-Length");
2587 const baseRenderPayload = {
2588 type: "render",
2589 basename: staticContext.basename,
2590 routeDiscovery: routeDiscovery ?? { mode: "lazy" },
2591 actionData: staticContext.actionData,
2592 errors: staticContext.errors,
2593 loaderData: staticContext.loaderData,
2594 location: staticContext.location,
2595 formState
2596 };
2597 const renderPayloadPromise = () => getRenderPayload(baseRenderPayload, routes, basename, routeIdsToLoad, isDataRequest, staticContext, routeDiscovery);
2598 let payload;
2599 if (actionResult) payload = {
2600 type: "action",
2601 actionResult,
2602 rerender: skipRevalidation ? void 0 : renderPayloadPromise()
2603 };
2604 else if (isSubmission && isDataRequest) payload = {
2605 ...baseRenderPayload,
2606 matches: [],
2607 patches: Promise.resolve([])
2608 };
2609 else payload = await renderPayloadPromise();
2610 return generateResponse({
2611 statusCode,
2612 headers,
2613 payload
2614 }, {
2615 temporaryReferences,
2616 onError: defaultOnError
2617 });
2618}
2619async function getRenderPayload(baseRenderPayload, routes, basename, routeIdsToLoad, isDataRequest, staticContext, routeDiscovery) {
2620 let deepestRenderedRouteIdx = staticContext.matches.length - 1;
2621 let parentIds = {};
2622 staticContext.matches.forEach((m, i) => {
2623 if (i > 0) parentIds[m.route.id] = staticContext.matches[i - 1].route.id;
2624 if (staticContext.errors && m.route.id in staticContext.errors && deepestRenderedRouteIdx > i) deepestRenderedRouteIdx = i;
2625 });
2626 let matchesPromise = Promise.all(staticContext.matches.map((match, i) => {
2627 let isBelowErrorBoundary = i > deepestRenderedRouteIdx;
2628 let parentId = parentIds[match.route.id];
2629 return getRSCRouteMatch({
2630 staticContext,
2631 match,
2632 routeIdsToLoad,
2633 isBelowErrorBoundary,
2634 parentId
2635 });
2636 }));
2637 let patches = routeDiscovery?.mode === "initial" && !isDataRequest ? getAllRoutePatches(routes, basename).then((patches) => patches.filter((patch) => !staticContext.matches.some((m) => m.route.id === patch.id))) : getAdditionalRoutePatches(getPathsWithAncestors([staticContext.location.pathname]), routes, basename, staticContext.matches.map((m) => m.route.id));
2638 return {
2639 ...baseRenderPayload,
2640 matches: await matchesPromise,
2641 patches
2642 };
2643}
2644async function getRSCRouteMatch({ staticContext, match, isBelowErrorBoundary, routeIdsToLoad, parentId }) {
2645 const route = match.route;
2646 await explodeLazyRoute(route);
2647 const Layout = route.Layout || React.Fragment;
2648 const Component = route.Component;
2649 const ErrorBoundary = route.ErrorBoundary;
2650 const HydrateFallback = route.HydrateFallback;
2651 const loaderData = staticContext.loaderData[route.id];
2652 const actionData = staticContext.actionData?.[route.id];
2653 const params = match.params;
2654 let element = void 0;
2655 let shouldLoadRoute = !routeIdsToLoad || routeIdsToLoad.includes(route.id);
2656 if (Component && shouldLoadRoute) element = !isBelowErrorBoundary ? React.createElement(Layout, null, isClientReference(Component) ? React.createElement(WithComponentProps, { children: React.createElement(Component) }) : React.createElement(Component, {
2657 loaderData,
2658 actionData,
2659 params,
2660 matches: staticContext.matches.map((match) => convertRouteMatchToUiMatch(match, staticContext.loaderData))
2661 })) : React.createElement(Outlet$2);
2662 let error = void 0;
2663 if (ErrorBoundary && staticContext.errors) error = staticContext.errors[route.id];
2664 const errorElement = ErrorBoundary ? React.createElement(Layout, null, isClientReference(ErrorBoundary) ? React.createElement(WithErrorBoundaryProps, { children: React.createElement(ErrorBoundary) }) : React.createElement(ErrorBoundary, {
2665 loaderData,
2666 actionData,
2667 params,
2668 error
2669 })) : void 0;
2670 const hydrateFallbackElement = HydrateFallback ? React.createElement(Layout, null, isClientReference(HydrateFallback) ? React.createElement(WithHydrateFallbackProps, { children: React.createElement(HydrateFallback) }) : React.createElement(HydrateFallback, {
2671 loaderData,
2672 actionData,
2673 params
2674 })) : void 0;
2675 const hmrRoute = route;
2676 return {
2677 clientAction: route.clientAction,
2678 clientLoader: route.clientLoader,
2679 element,
2680 errorElement,
2681 handle: route.handle,
2682 hasAction: !!route.action,
2683 hasComponent: !!Component,
2684 hasLoader: !!route.loader,
2685 hydrateFallbackElement,
2686 id: route.id,
2687 index: "index" in route ? route.index : void 0,
2688 links: route.links,
2689 meta: route.meta,
2690 params,
2691 parentId,
2692 path: route.path,
2693 pathname: match.pathname,
2694 pathnameBase: match.pathnameBase,
2695 shouldRevalidate: route.shouldRevalidate,
2696 ...hmrRoute.__ensureClientRouteModuleForHMR ? { __ensureClientRouteModuleForHMR: hmrRoute.__ensureClientRouteModuleForHMR } : {}
2697 };
2698}
2699async function getManifestRoute(route) {
2700 await explodeLazyRoute(route);
2701 const Layout = route.Layout || React.Fragment;
2702 const errorElement = route.ErrorBoundary ? React.createElement(Layout, null, React.createElement(route.ErrorBoundary)) : void 0;
2703 return {
2704 clientAction: route.clientAction,
2705 clientLoader: route.clientLoader,
2706 handle: route.handle,
2707 hasAction: !!route.action,
2708 hasComponent: !!route.Component,
2709 errorElement,
2710 hasLoader: !!route.loader,
2711 id: route.id,
2712 parentId: route.parentId,
2713 path: route.path,
2714 index: "index" in route ? route.index : void 0,
2715 links: route.links,
2716 meta: route.meta
2717 };
2718}
2719async function explodeLazyRoute(route) {
2720 if ("lazy" in route && route.lazy) {
2721 let { default: lazyDefaultExport, Component: lazyComponentExport, ...lazyProperties } = await route.lazy();
2722 let Component = lazyComponentExport || lazyDefaultExport;
2723 if (Component && !route.Component) route.Component = Component;
2724 for (let [k, v] of Object.entries(lazyProperties)) if (k !== "id" && k !== "path" && k !== "index" && k !== "children" && route[k] == null) route[k] = v;
2725 route.lazy = void 0;
2726 }
2727}
2728async function getAllRoutePatches(routes, basename) {
2729 let patches = [];
2730 async function traverse(route, parentId) {
2731 let manifestRoute = await getManifestRoute({
2732 ...route,
2733 parentId
2734 });
2735 patches.push(manifestRoute);
2736 if ("children" in route && route.children?.length) for (let child of route.children) await traverse(child, route.id);
2737 }
2738 for (let route of routes) await traverse(route, void 0);
2739 return patches.filter((p) => !!p.parentId);
2740}
2741async function getAdditionalRoutePatches(pathnames, routes, basename, matchedRouteIds) {
2742 let patchRouteMatches = /* @__PURE__ */ new Map();
2743 let matchedPaths = /* @__PURE__ */ new Set();
2744 for (const pathname of pathnames) {
2745 if (matchedPaths.has(pathname)) continue;
2746 matchedPaths.add(pathname);
2747 let matches = matchRoutes(routes, pathname, basename) || [];
2748 matches.forEach((m, i) => {
2749 if (patchRouteMatches.get(m.route.id)) return;
2750 patchRouteMatches.set(m.route.id, {
2751 ...m.route,
2752 parentId: matches[i - 1]?.route.id
2753 });
2754 });
2755 }
2756 return await Promise.all([...patchRouteMatches.values()].filter((route) => !matchedRouteIds.some((id) => id === route.id)).map((route) => getManifestRoute(route)));
2757}
2758function isReactServerRequest(url) {
2759 return url.pathname.endsWith(".rsc");
2760}
2761function isManifestRequest(url) {
2762 return url.pathname.endsWith(".manifest");
2763}
2764function defaultOnError(error) {
2765 if (isRedirectResponse(error)) return createRedirectErrorDigest(error);
2766 if (isResponse(error) || isDataWithResponseInit(error)) return createRouteErrorResponseDigest(error);
2767}
2768function isClientReference(x) {
2769 try {
2770 return x.$$typeof === Symbol.for("react.client.reference");
2771 } catch {
2772 return false;
2773 }
2774}
2775function canDecodeWithFormData(contentType) {
2776 if (!contentType) return false;
2777 return contentType.match(/\bapplication\/x-www-form-urlencoded\b/) || contentType.match(/\bmultipart\/form-data\b/);
2778}
2779//#endregion
2780//#region lib/href.ts
2781/**
2782Returns a resolved URL path for the specified route.
2783
2784```tsx
2785const h = href("/:lang?/about", { lang: "en" })
2786// -> `/en/about`
2787
2788<Link to={href("/products/:id", { id: "abc123" })} />
2789```
2790*/
2791function href(path, ...args) {
2792 let params = args[0];
2793 let result = trimTrailingSplat(path).replace(/\/:([\w-]+)(\?)?/g, (_, param, questionMark) => {
2794 const isRequired = questionMark === void 0;
2795 const value = params?.[param];
2796 if (isRequired && value === void 0) throw new Error(`Path '${path}' requires param '${param}' but it was not provided`);
2797 return value === void 0 ? "" : "/" + value;
2798 });
2799 if (path.endsWith("*")) {
2800 const value = params?.["*"];
2801 if (value !== void 0) result += "/" + value;
2802 }
2803 return result || "/";
2804}
2805/**
2806* Removes a trailing splat and any number of slashes from the end of the path.
2807*
2808* Benchmarked to be faster than `path.replace(/\/*\*?$/, "")`, which backtracks.
2809*/
2810function trimTrailingSplat(path) {
2811 let i = path.length - 1;
2812 let char = path[i];
2813 if (char !== "*" && char !== "/") return path;
2814 i--;
2815 for (; i >= 0; i--) if (path[i] !== "/") break;
2816 return path.slice(0, i + 1);
2817}
2818//#endregion
2819//#region lib/server-runtime/crypto.ts
2820const encoder = /* @__PURE__ */ new TextEncoder();
2821const sign = async (value, secret) => {
2822 let data = encoder.encode(value);
2823 let key = await createKey(secret, ["sign"]);
2824 let signature = await crypto.subtle.sign("HMAC", key, data);
2825 let hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(/=+$/, "");
2826 return value + "." + hash;
2827};
2828const unsign = async (cookie, secret) => {
2829 let index = cookie.lastIndexOf(".");
2830 let value = cookie.slice(0, index);
2831 let hash = cookie.slice(index + 1);
2832 let data = encoder.encode(value);
2833 let key = await createKey(secret, ["verify"]);
2834 try {
2835 let signature = byteStringToUint8Array(atob(hash));
2836 return await crypto.subtle.verify("HMAC", key, signature, data) ? value : false;
2837 } catch (e) {
2838 return false;
2839 }
2840};
2841const createKey = async (secret, usages) => crypto.subtle.importKey("raw", encoder.encode(secret), {
2842 name: "HMAC",
2843 hash: "SHA-256"
2844}, false, usages);
2845function byteStringToUint8Array(byteString) {
2846 let array = new Uint8Array(byteString.length);
2847 for (let i = 0; i < byteString.length; i++) array[i] = byteString.charCodeAt(i);
2848 return array;
2849}
2850//#endregion
2851//#region lib/server-runtime/cookies.ts
2852/**
2853* Creates a logical container for managing a browser cookie from the server.
2854*/
2855const createCookie = (name, cookieOptions = {}) => {
2856 let { secrets = [], ...options } = {
2857 path: "/",
2858 sameSite: "lax",
2859 ...cookieOptions
2860 };
2861 warnOnceAboutExpiresCookie(name, options.expires);
2862 return {
2863 get name() {
2864 return name;
2865 },
2866 get isSigned() {
2867 return secrets.length > 0;
2868 },
2869 get expires() {
2870 return typeof options.maxAge !== "undefined" ? new Date(Date.now() + options.maxAge * 1e3) : options.expires;
2871 },
2872 async parse(cookieHeader, parseOptions) {
2873 if (!cookieHeader) return null;
2874 let cookies = parse(cookieHeader, {
2875 ...options,
2876 ...parseOptions
2877 });
2878 if (name in cookies) {
2879 let value = cookies[name];
2880 if (typeof value === "string" && value !== "") return await decodeCookieValue(value, secrets);
2881 else return "";
2882 } else return null;
2883 },
2884 async serialize(value, serializeOptions) {
2885 return serialize(name, value === "" ? "" : await encodeCookieValue(value, secrets), {
2886 ...options,
2887 ...serializeOptions
2888 });
2889 }
2890 };
2891};
2892/**
2893* Returns true if an object is a Remix cookie container.
2894*
2895* @see https://remix.run/utils/cookies#iscookie
2896*/
2897const isCookie = (object) => {
2898 return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
2899};
2900async function encodeCookieValue(value, secrets) {
2901 let encoded = encodeData(value);
2902 if (secrets.length > 0) encoded = await sign(encoded, secrets[0]);
2903 return encoded;
2904}
2905async function decodeCookieValue(value, secrets) {
2906 if (secrets.length > 0) {
2907 for (let secret of secrets) {
2908 let unsignedValue = await unsign(value, secret);
2909 if (unsignedValue !== false) return decodeData(unsignedValue);
2910 }
2911 return null;
2912 }
2913 return decodeData(value);
2914}
2915function encodeData(value) {
2916 return btoa(myUnescape(encodeURIComponent(JSON.stringify(value))));
2917}
2918function decodeData(value) {
2919 try {
2920 return JSON.parse(decodeURIComponent(myEscape(atob(value))));
2921 } catch (e) {
2922 return {};
2923 }
2924}
2925function myEscape(value) {
2926 let str = value.toString();
2927 let result = "";
2928 let index = 0;
2929 let chr, code;
2930 while (index < str.length) {
2931 chr = str.charAt(index++);
2932 if (/[\w*+\-./@]/.exec(chr)) result += chr;
2933 else {
2934 code = chr.charCodeAt(0);
2935 if (code < 256) result += "%" + hex(code, 2);
2936 else result += "%u" + hex(code, 4).toUpperCase();
2937 }
2938 }
2939 return result;
2940}
2941function hex(code, length) {
2942 let result = code.toString(16);
2943 while (result.length < length) result = "0" + result;
2944 return result;
2945}
2946function myUnescape(value) {
2947 let str = value.toString();
2948 let result = "";
2949 let index = 0;
2950 let chr, part;
2951 while (index < str.length) {
2952 chr = str.charAt(index++);
2953 if (chr === "%") if (str.charAt(index) === "u") {
2954 part = str.slice(index + 1, index + 5);
2955 if (/^[\da-f]{4}$/i.exec(part)) {
2956 result += String.fromCharCode(parseInt(part, 16));
2957 index += 5;
2958 continue;
2959 }
2960 } else {
2961 part = str.slice(index, index + 2);
2962 if (/^[\da-f]{2}$/i.exec(part)) {
2963 result += String.fromCharCode(parseInt(part, 16));
2964 index += 2;
2965 continue;
2966 }
2967 }
2968 result += chr;
2969 }
2970 return result;
2971}
2972function warnOnceAboutExpiresCookie(name, expires) {
2973 warnOnce(!expires, `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.`);
2974}
2975//#endregion
2976//#region lib/server-runtime/sessions.ts
2977function flash(name) {
2978 return `__flash_${name}__`;
2979}
2980/**
2981* Creates a new Session object.
2982*
2983* Note: This function is typically not invoked directly by application code.
2984* Instead, use a `SessionStorage` object's `getSession` method.
2985*/
2986const createSession = (initialData = {}, id = "") => {
2987 let map = new Map(Object.entries(initialData));
2988 return {
2989 get id() {
2990 return id;
2991 },
2992 get data() {
2993 return Object.fromEntries(map);
2994 },
2995 has(name) {
2996 return map.has(name) || map.has(flash(name));
2997 },
2998 get(name) {
2999 if (map.has(name)) return map.get(name);
3000 let flashName = flash(name);
3001 if (map.has(flashName)) {
3002 let value = map.get(flashName);
3003 map.delete(flashName);
3004 return value;
3005 }
3006 },
3007 set(name, value) {
3008 map.set(name, value);
3009 },
3010 flash(name, value) {
3011 map.set(flash(name), value);
3012 },
3013 unset(name) {
3014 map.delete(name);
3015 }
3016 };
3017};
3018/**
3019* Returns true if an object is a React Router session.
3020*
3021* @see https://reactrouter.com/api/utils/isSession
3022*/
3023const isSession = (object) => {
3024 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";
3025};
3026/**
3027* Creates a SessionStorage object using a SessionIdStorageStrategy.
3028*
3029* Note: This is a low-level API that should only be used if none of the
3030* existing session storage options meet your requirements.
3031*/
3032function createSessionStorage({ cookie: cookieArg, createData, readData, updateData, deleteData }) {
3033 let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
3034 warnOnceAboutSigningSessionCookie(cookie);
3035 return {
3036 async getSession(cookieHeader, options) {
3037 let id = cookieHeader && await cookie.parse(cookieHeader, options);
3038 return createSession(id && await readData(id) || {}, id || "");
3039 },
3040 async commitSession(session, options) {
3041 let { id, data } = session;
3042 let expires = options?.maxAge != null ? new Date(Date.now() + options.maxAge * 1e3) : options?.expires != null ? options.expires : cookie.expires;
3043 if (id) await updateData(id, data, expires);
3044 else id = await createData(data, expires);
3045 return cookie.serialize(id, options);
3046 },
3047 async destroySession(session, options) {
3048 await deleteData(session.id);
3049 return cookie.serialize("", {
3050 ...options,
3051 maxAge: void 0,
3052 expires: /* @__PURE__ */ new Date(0)
3053 });
3054 }
3055 };
3056}
3057function warnOnceAboutSigningSessionCookie(cookie) {
3058 warnOnce(cookie.isSigned, `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.`);
3059}
3060//#endregion
3061//#region lib/server-runtime/sessions/cookieStorage.ts
3062/**
3063* Creates and returns a SessionStorage object that stores all session data
3064* directly in the session cookie itself.
3065*
3066* This has the advantage that no database or other backend services are
3067* needed, and can help to simplify some load-balanced scenarios. However, it
3068* also has the limitation that serialized session data may not exceed the
3069* browser's maximum cookie size. Trade-offs!
3070*/
3071function createCookieSessionStorage({ cookie: cookieArg } = {}) {
3072 let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
3073 warnOnceAboutSigningSessionCookie(cookie);
3074 return {
3075 async getSession(cookieHeader, options) {
3076 return createSession(cookieHeader && await cookie.parse(cookieHeader, options) || {});
3077 },
3078 async commitSession(session, options) {
3079 let serializedCookie = await cookie.serialize(session.data, options);
3080 if (serializedCookie.length > 4096) throw new Error("Cookie length will exceed browser maximum. Length: " + serializedCookie.length);
3081 return serializedCookie;
3082 },
3083 async destroySession(_session, options) {
3084 return cookie.serialize("", {
3085 ...options,
3086 maxAge: void 0,
3087 expires: /* @__PURE__ */ new Date(0)
3088 });
3089 }
3090 };
3091}
3092//#endregion
3093//#region lib/server-runtime/sessions/memoryStorage.ts
3094/**
3095* Creates and returns a simple in-memory SessionStorage object, mostly useful
3096* for testing and as a reference implementation.
3097*
3098* Note: This storage does not scale beyond a single process, so it is not
3099* suitable for most production scenarios.
3100*/
3101function createMemorySessionStorage({ cookie } = {}) {
3102 let map = /* @__PURE__ */ new Map();
3103 return createSessionStorage({
3104 cookie,
3105 async createData(data, expires) {
3106 let id = Math.random().toString(36).substring(2, 10);
3107 map.set(id, {
3108 data,
3109 expires
3110 });
3111 return id;
3112 },
3113 async readData(id) {
3114 if (map.has(id)) {
3115 let { data, expires } = map.get(id);
3116 if (!expires || expires > /* @__PURE__ */ new Date()) return data;
3117 if (expires) map.delete(id);
3118 }
3119 return null;
3120 },
3121 async updateData(id, data, expires) {
3122 map.set(id, {
3123 data,
3124 expires
3125 });
3126 },
3127 async deleteData(id) {
3128 map.delete(id);
3129 }
3130 });
3131}
3132//#endregion
3133export { Await, BrowserRouter, Form, HashRouter, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Outlet, Route, Router, RouterContextProvider, RouterProvider, Routes, ScrollRestoration, StaticRouter, StaticRouterProvider, createContext, createCookie, createCookieSessionStorage, createMemorySessionStorage, createSession, createSessionStorage, createStaticHandler, data, href, isCookie, isRouteErrorResponse, isSession, matchRoutes, redirect, redirectDocument, replace, unstable_HistoryRouter, getRequest as unstable_getRequest, matchRSCServerRequest as unstable_matchRSCServerRequest };