UNPKG

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