UNPKG

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