BOC v1.0: RS256 auth, Ledger integration, Prometheus metrics, CI/CD, backup

This commit is contained in:
Bernt
2026-07-28 23:08:32 +00:00
parent 0b4f160af1
commit af874040ca
11541 changed files with 1654104 additions and 1103 deletions
+318
View File
@@ -0,0 +1,318 @@
import * as React from 'react';
import { R as RouterInit } from './instrumentation-Dkmpzd13.js';
import { L as Location, C as ClientActionFunction, a as ClientLoaderFunction, b as LinksFunction, M as MetaFunction, S as ShouldRevalidateFunction, P as Params, c as RouterContextProvider, A as ActionFunction, H as HeadersFunction, d as LoaderFunction } from './data-CjO11-hU.js';
declare function getRequest(): Request;
type RSCRouteConfigEntryBase = {
action?: ActionFunction;
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
ErrorBoundary?: React.ComponentType<any>;
handle?: any;
headers?: HeadersFunction;
HydrateFallback?: React.ComponentType<any>;
Layout?: React.ComponentType<any>;
links?: LinksFunction;
loader?: LoaderFunction;
meta?: MetaFunction;
shouldRevalidate?: ShouldRevalidateFunction;
};
type RSCRouteConfigEntry = RSCRouteConfigEntryBase & {
id: string;
path?: string;
Component?: React.ComponentType<any>;
lazy?: () => Promise<RSCRouteConfigEntryBase & ({
default?: React.ComponentType<any>;
Component?: never;
} | {
default?: never;
Component?: React.ComponentType<any>;
})>;
} & ({
index: true;
} | {
children?: RSCRouteConfigEntry[];
});
type RSCRouteConfig = Array<RSCRouteConfigEntry>;
type RSCRouteManifest = {
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
element?: React.ReactElement | false;
errorElement?: React.ReactElement;
handle?: any;
hasAction: boolean;
hasComponent: boolean;
hasErrorBoundary: boolean;
hasLoader: boolean;
hydrateFallbackElement?: React.ReactElement;
id: string;
index?: boolean;
links?: LinksFunction;
meta?: MetaFunction;
parentId?: string;
path?: string;
shouldRevalidate?: ShouldRevalidateFunction;
};
type RSCRouteMatch = RSCRouteManifest & {
params: Params;
pathname: string;
pathnameBase: string;
};
type RSCRenderPayload = {
type: "render";
actionData: Record<string, any> | null;
basename: string | undefined;
errors: Record<string, any> | null;
loaderData: Record<string, any>;
location: Location;
routeDiscovery: RouteDiscovery;
matches: RSCRouteMatch[];
patches?: Promise<RSCRouteManifest[]>;
nonce?: string;
formState?: unknown;
};
type RSCManifestPayload = {
type: "manifest";
patches: Promise<RSCRouteManifest[]>;
};
type RSCActionPayload = {
type: "action";
actionResult: Promise<unknown>;
rerender?: Promise<RSCRenderPayload | RSCRedirectPayload>;
};
type RSCRedirectPayload = {
type: "redirect";
status: number;
location: string;
replace: boolean;
reload: boolean;
actionResult?: Promise<unknown>;
};
type RSCPayload = RSCRenderPayload | RSCManifestPayload | RSCActionPayload | RSCRedirectPayload;
type RSCMatch = {
statusCode: number;
headers: Headers;
payload: RSCPayload;
};
type DecodeActionFunction = (formData: FormData) => Promise<() => Promise<unknown>>;
type DecodeFormStateFunction = (result: unknown, formData: FormData) => unknown;
type DecodeReplyFunction = (reply: FormData | string, options: {
temporaryReferences: unknown;
}) => Promise<unknown[]>;
type LoadServerActionFunction = (id: string) => Promise<Function>;
type RouteDiscovery = {
mode: "lazy";
manifestPath?: string | undefined;
} | {
mode: "initial";
};
/**
* Matches the given routes to a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* and returns an [RSC](https://react.dev/reference/rsc/server-components)
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* encoding an {@link unstable_RSCPayload} for consumption by an [RSC](https://react.dev/reference/rsc/server-components)
* enabled client router.
*
* @example
* import {
* createTemporaryReferenceSet,
* decodeAction,
* decodeReply,
* loadServerAction,
* renderToReadableStream,
* } from "@vitejs/plugin-rsc/rsc";
* import { unstable_matchRSCServerRequest as matchRSCServerRequest } from "react-router";
*
* matchRSCServerRequest({
* createTemporaryReferenceSet,
* decodeAction,
* decodeFormState,
* decodeReply,
* loadServerAction,
* request,
* routes: routes(),
* generateResponse(match) {
* return new Response(
* renderToReadableStream(match.payload),
* {
* status: match.statusCode,
* headers: match.headers,
* }
* );
* },
* });
*
* @name unstable_matchRSCServerRequest
* @public
* @category RSC
* @mode data
* @param opts Options
* @param opts.allowedActionOrigins Origin patterns that are allowed to execute actions.
* @param opts.basename The basename to use when matching the request.
* @param opts.createTemporaryReferenceSet A function that returns a temporary
* reference set for the request, used to track temporary references in the [RSC](https://react.dev/reference/rsc/server-components)
* stream.
* @param opts.decodeAction Your `react-server-dom-xyz/server`'s `decodeAction`
* function, responsible for loading a server action.
* @param opts.decodeFormState A function responsible for decoding form state for
* progressively enhanceable forms with React's [`useActionState`](https://react.dev/reference/react/useActionState)
* using your `react-server-dom-xyz/server`'s `decodeFormState`.
* @param opts.decodeReply Your `react-server-dom-xyz/server`'s `decodeReply`
* function, used to decode the server function's arguments and bind them to the
* implementation for invocation by the router.
* @param opts.generateResponse A function responsible for using your
* `renderToReadableStream` to generate a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* encoding the {@link unstable_RSCPayload}.
* @param opts.loadServerAction Your `react-server-dom-xyz/server`'s
* `loadServerAction` function, used to load a server action by ID.
* @param opts.onError An optional error handler that will be called with any
* errors that occur during the request processing.
* @param opts.request The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* to match against.
* @param opts.requestContext An instance of {@link RouterContextProvider}
* that should be created per request, to be passed to [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* @param opts.routeDiscovery The route discovery configuration, used to determine how the router should discover new routes during navigations.
* @param opts.routes Your {@link unstable_RSCRouteConfigEntry | route definitions}.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that contains the [RSC](https://react.dev/reference/rsc/server-components)
* data for hydration.
*/
declare function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse, }: {
allowedActionOrigins?: string[];
createTemporaryReferenceSet: () => unknown;
basename?: string;
decodeReply?: DecodeReplyFunction;
decodeAction?: DecodeActionFunction;
decodeFormState?: DecodeFormStateFunction;
requestContext?: RouterContextProvider;
loadServerAction?: LoadServerActionFunction;
onError?: (error: unknown) => void;
request: Request;
routes: RSCRouteConfigEntry[];
routeDiscovery?: RouteDiscovery;
generateResponse: (match: RSCMatch, { onError, temporaryReferences, }: {
onError(error: unknown): string | undefined;
temporaryReferences: unknown;
}) => Response;
}): Promise<Response>;
type BrowserCreateFromReadableStreamFunction = (body: ReadableStream<Uint8Array>, { temporaryReferences, }: {
temporaryReferences: unknown;
}) => Promise<unknown>;
type EncodeReplyFunction = (args: unknown[], options: {
temporaryReferences: unknown;
}) => Promise<BodyInit>;
/**
* Create a React `callServer` implementation for React Router.
*
* @example
* import {
* createFromReadableStream,
* createTemporaryReferenceSet,
* encodeReply,
* setServerCallback,
* } from "@vitejs/plugin-rsc/browser";
* import { unstable_createCallServer as createCallServer } from "react-router";
*
* setServerCallback(
* createCallServer({
* createFromReadableStream,
* createTemporaryReferenceSet,
* encodeReply,
* })
* );
*
* @name unstable_createCallServer
* @public
* @category RSC
* @mode data
* @param opts Options
* @param opts.createFromReadableStream Your `react-server-dom-xyz/client`'s
* `createFromReadableStream`. Used to decode payloads from the server.
* @param opts.createTemporaryReferenceSet A function that creates a temporary
* reference set for the [RSC](https://react.dev/reference/rsc/server-components)
* payload.
* @param opts.encodeReply Your `react-server-dom-xyz/client`'s `encodeReply`.
* Used when sending payloads to the server.
* @param opts.fetch Optional [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
* implementation. Defaults to global [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch).
* @returns A function that can be used to call server actions.
*/
declare function createCallServer({ createFromReadableStream, createTemporaryReferenceSet, encodeReply, fetch: fetchImplementation, }: {
createFromReadableStream: BrowserCreateFromReadableStreamFunction;
createTemporaryReferenceSet: () => unknown;
encodeReply: EncodeReplyFunction;
fetch?: (request: Request) => Promise<Response>;
}): (id: string, args: unknown[]) => Promise<unknown>;
/**
* Props for the {@link unstable_RSCHydratedRouter} component.
*
* @name unstable_RSCHydratedRouterProps
* @category Types
*/
interface RSCHydratedRouterProps {
/**
* Your `react-server-dom-xyz/client`'s `createFromReadableStream` function,
* used to decode payloads from the server.
*/
createFromReadableStream: BrowserCreateFromReadableStreamFunction;
/**
* Optional fetch implementation. Defaults to global [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch).
*/
fetch?: (request: Request) => Promise<Response>;
/**
* The decoded {@link unstable_RSCPayload} to hydrate.
*/
payload: RSCPayload;
/**
* A function that returns an {@link RouterContextProvider} instance
* which is provided as the `context` argument to client [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* This function is called to generate a fresh `context` instance on each
* navigation or fetcher call.
*/
getContext?: RouterInit["getContext"];
}
/**
* Hydrates a server rendered {@link unstable_RSCPayload} in the browser.
*
* @example
* import { startTransition, StrictMode } from "react";
* import { hydrateRoot } from "react-dom/client";
* import {
* unstable_getRSCStream as getRSCStream,
* unstable_RSCHydratedRouter as RSCHydratedRouter,
* } from "react-router";
* import type { unstable_RSCPayload as RSCPayload } from "react-router";
*
* createFromReadableStream(getRSCStream()).then((payload) =>
* startTransition(async () => {
* hydrateRoot(
* document,
* <StrictMode>
* <RSCHydratedRouter
* createFromReadableStream={createFromReadableStream}
* payload={payload}
* />
* </StrictMode>,
* { formState: await getFormState(payload) },
* );
* }),
* );
*
* @name unstable_RSCHydratedRouter
* @public
* @category RSC
* @mode data
* @param props Props
* @param {unstable_RSCHydratedRouterProps.createFromReadableStream} props.createFromReadableStream n/a
* @param {unstable_RSCHydratedRouterProps.fetch} props.fetch n/a
* @param {unstable_RSCHydratedRouterProps.getContext} props.getContext n/a
* @param {unstable_RSCHydratedRouterProps.payload} props.payload n/a
* @returns A hydrated {@link DataRouter} that can be used to navigate and
* render routes.
*/
declare function RSCHydratedRouter({ createFromReadableStream, fetch: fetchImplementation, payload, getContext, }: RSCHydratedRouterProps): React.JSX.Element;
export { type BrowserCreateFromReadableStreamFunction as B, type DecodeActionFunction as D, type EncodeReplyFunction as E, type LoadServerActionFunction as L, RSCHydratedRouter as R, type DecodeFormStateFunction as a, type DecodeReplyFunction as b, createCallServer as c, type RSCManifestPayload as d, type RSCPayload as e, type RSCRenderPayload as f, getRequest as g, type RSCHydratedRouterProps as h, type RSCMatch as i, type RSCRouteManifest as j, type RSCRouteMatch as k, type RSCRouteConfigEntry as l, matchRSCServerRequest as m, type RSCRouteConfig as n };
@@ -0,0 +1,318 @@
import * as React from 'react';
import { R as RouterInit } from './context-CeD5LmaF.mjs';
import { L as Location, C as ClientActionFunction, a as ClientLoaderFunction, b as LinksFunction, M as MetaFunction, S as ShouldRevalidateFunction, P as Params, c as RouterContextProvider, A as ActionFunction, H as HeadersFunction, d as LoaderFunction } from './data-DEjBmEfD.mjs';
declare function getRequest(): Request;
type RSCRouteConfigEntryBase = {
action?: ActionFunction;
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
ErrorBoundary?: React.ComponentType<any>;
handle?: any;
headers?: HeadersFunction;
HydrateFallback?: React.ComponentType<any>;
Layout?: React.ComponentType<any>;
links?: LinksFunction;
loader?: LoaderFunction;
meta?: MetaFunction;
shouldRevalidate?: ShouldRevalidateFunction;
};
type RSCRouteConfigEntry = RSCRouteConfigEntryBase & {
id: string;
path?: string;
Component?: React.ComponentType<any>;
lazy?: () => Promise<RSCRouteConfigEntryBase & ({
default?: React.ComponentType<any>;
Component?: never;
} | {
default?: never;
Component?: React.ComponentType<any>;
})>;
} & ({
index: true;
} | {
children?: RSCRouteConfigEntry[];
});
type RSCRouteConfig = Array<RSCRouteConfigEntry>;
type RSCRouteManifest = {
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
element?: React.ReactElement | false;
errorElement?: React.ReactElement;
handle?: any;
hasAction: boolean;
hasComponent: boolean;
hasErrorBoundary: boolean;
hasLoader: boolean;
hydrateFallbackElement?: React.ReactElement;
id: string;
index?: boolean;
links?: LinksFunction;
meta?: MetaFunction;
parentId?: string;
path?: string;
shouldRevalidate?: ShouldRevalidateFunction;
};
type RSCRouteMatch = RSCRouteManifest & {
params: Params;
pathname: string;
pathnameBase: string;
};
type RSCRenderPayload = {
type: "render";
actionData: Record<string, any> | null;
basename: string | undefined;
errors: Record<string, any> | null;
loaderData: Record<string, any>;
location: Location;
routeDiscovery: RouteDiscovery;
matches: RSCRouteMatch[];
patches?: Promise<RSCRouteManifest[]>;
nonce?: string;
formState?: unknown;
};
type RSCManifestPayload = {
type: "manifest";
patches: Promise<RSCRouteManifest[]>;
};
type RSCActionPayload = {
type: "action";
actionResult: Promise<unknown>;
rerender?: Promise<RSCRenderPayload | RSCRedirectPayload>;
};
type RSCRedirectPayload = {
type: "redirect";
status: number;
location: string;
replace: boolean;
reload: boolean;
actionResult?: Promise<unknown>;
};
type RSCPayload = RSCRenderPayload | RSCManifestPayload | RSCActionPayload | RSCRedirectPayload;
type RSCMatch = {
statusCode: number;
headers: Headers;
payload: RSCPayload;
};
type DecodeActionFunction = (formData: FormData) => Promise<() => Promise<unknown>>;
type DecodeFormStateFunction = (result: unknown, formData: FormData) => unknown;
type DecodeReplyFunction = (reply: FormData | string, options: {
temporaryReferences: unknown;
}) => Promise<unknown[]>;
type LoadServerActionFunction = (id: string) => Promise<Function>;
type RouteDiscovery = {
mode: "lazy";
manifestPath?: string | undefined;
} | {
mode: "initial";
};
/**
* Matches the given routes to a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* and returns an [RSC](https://react.dev/reference/rsc/server-components)
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* encoding an {@link unstable_RSCPayload} for consumption by an [RSC](https://react.dev/reference/rsc/server-components)
* enabled client router.
*
* @example
* import {
* createTemporaryReferenceSet,
* decodeAction,
* decodeReply,
* loadServerAction,
* renderToReadableStream,
* } from "@vitejs/plugin-rsc/rsc";
* import { unstable_matchRSCServerRequest as matchRSCServerRequest } from "react-router";
*
* matchRSCServerRequest({
* createTemporaryReferenceSet,
* decodeAction,
* decodeFormState,
* decodeReply,
* loadServerAction,
* request,
* routes: routes(),
* generateResponse(match) {
* return new Response(
* renderToReadableStream(match.payload),
* {
* status: match.statusCode,
* headers: match.headers,
* }
* );
* },
* });
*
* @name unstable_matchRSCServerRequest
* @public
* @category RSC
* @mode data
* @param opts Options
* @param opts.allowedActionOrigins Origin patterns that are allowed to execute actions.
* @param opts.basename The basename to use when matching the request.
* @param opts.createTemporaryReferenceSet A function that returns a temporary
* reference set for the request, used to track temporary references in the [RSC](https://react.dev/reference/rsc/server-components)
* stream.
* @param opts.decodeAction Your `react-server-dom-xyz/server`'s `decodeAction`
* function, responsible for loading a server action.
* @param opts.decodeFormState A function responsible for decoding form state for
* progressively enhanceable forms with React's [`useActionState`](https://react.dev/reference/react/useActionState)
* using your `react-server-dom-xyz/server`'s `decodeFormState`.
* @param opts.decodeReply Your `react-server-dom-xyz/server`'s `decodeReply`
* function, used to decode the server function's arguments and bind them to the
* implementation for invocation by the router.
* @param opts.generateResponse A function responsible for using your
* `renderToReadableStream` to generate a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* encoding the {@link unstable_RSCPayload}.
* @param opts.loadServerAction Your `react-server-dom-xyz/server`'s
* `loadServerAction` function, used to load a server action by ID.
* @param opts.onError An optional error handler that will be called with any
* errors that occur during the request processing.
* @param opts.request The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* to match against.
* @param opts.requestContext An instance of {@link RouterContextProvider}
* that should be created per request, to be passed to [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* @param opts.routeDiscovery The route discovery configuration, used to determine how the router should discover new routes during navigations.
* @param opts.routes Your {@link unstable_RSCRouteConfigEntry | route definitions}.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that contains the [RSC](https://react.dev/reference/rsc/server-components)
* data for hydration.
*/
declare function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse, }: {
allowedActionOrigins?: string[];
createTemporaryReferenceSet: () => unknown;
basename?: string;
decodeReply?: DecodeReplyFunction;
decodeAction?: DecodeActionFunction;
decodeFormState?: DecodeFormStateFunction;
requestContext?: RouterContextProvider;
loadServerAction?: LoadServerActionFunction;
onError?: (error: unknown) => void;
request: Request;
routes: RSCRouteConfigEntry[];
routeDiscovery?: RouteDiscovery;
generateResponse: (match: RSCMatch, { onError, temporaryReferences, }: {
onError(error: unknown): string | undefined;
temporaryReferences: unknown;
}) => Response;
}): Promise<Response>;
type BrowserCreateFromReadableStreamFunction = (body: ReadableStream<Uint8Array>, { temporaryReferences, }: {
temporaryReferences: unknown;
}) => Promise<unknown>;
type EncodeReplyFunction = (args: unknown[], options: {
temporaryReferences: unknown;
}) => Promise<BodyInit>;
/**
* Create a React `callServer` implementation for React Router.
*
* @example
* import {
* createFromReadableStream,
* createTemporaryReferenceSet,
* encodeReply,
* setServerCallback,
* } from "@vitejs/plugin-rsc/browser";
* import { unstable_createCallServer as createCallServer } from "react-router";
*
* setServerCallback(
* createCallServer({
* createFromReadableStream,
* createTemporaryReferenceSet,
* encodeReply,
* })
* );
*
* @name unstable_createCallServer
* @public
* @category RSC
* @mode data
* @param opts Options
* @param opts.createFromReadableStream Your `react-server-dom-xyz/client`'s
* `createFromReadableStream`. Used to decode payloads from the server.
* @param opts.createTemporaryReferenceSet A function that creates a temporary
* reference set for the [RSC](https://react.dev/reference/rsc/server-components)
* payload.
* @param opts.encodeReply Your `react-server-dom-xyz/client`'s `encodeReply`.
* Used when sending payloads to the server.
* @param opts.fetch Optional [Fetch API](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API)
* implementation. Defaults to global [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch).
* @returns A function that can be used to call server actions.
*/
declare function createCallServer({ createFromReadableStream, createTemporaryReferenceSet, encodeReply, fetch: fetchImplementation, }: {
createFromReadableStream: BrowserCreateFromReadableStreamFunction;
createTemporaryReferenceSet: () => unknown;
encodeReply: EncodeReplyFunction;
fetch?: (request: Request) => Promise<Response>;
}): (id: string, args: unknown[]) => Promise<unknown>;
/**
* Props for the {@link unstable_RSCHydratedRouter} component.
*
* @name unstable_RSCHydratedRouterProps
* @category Types
*/
interface RSCHydratedRouterProps {
/**
* Your `react-server-dom-xyz/client`'s `createFromReadableStream` function,
* used to decode payloads from the server.
*/
createFromReadableStream: BrowserCreateFromReadableStreamFunction;
/**
* Optional fetch implementation. Defaults to global [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch).
*/
fetch?: (request: Request) => Promise<Response>;
/**
* The decoded {@link unstable_RSCPayload} to hydrate.
*/
payload: RSCPayload;
/**
* A function that returns an {@link RouterContextProvider} instance
* which is provided as the `context` argument to client [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* This function is called to generate a fresh `context` instance on each
* navigation or fetcher call.
*/
getContext?: RouterInit["getContext"];
}
/**
* Hydrates a server rendered {@link unstable_RSCPayload} in the browser.
*
* @example
* import { startTransition, StrictMode } from "react";
* import { hydrateRoot } from "react-dom/client";
* import {
* unstable_getRSCStream as getRSCStream,
* unstable_RSCHydratedRouter as RSCHydratedRouter,
* } from "react-router";
* import type { unstable_RSCPayload as RSCPayload } from "react-router";
*
* createFromReadableStream(getRSCStream()).then((payload) =>
* startTransition(async () => {
* hydrateRoot(
* document,
* <StrictMode>
* <RSCHydratedRouter
* createFromReadableStream={createFromReadableStream}
* payload={payload}
* />
* </StrictMode>,
* { formState: await getFormState(payload) },
* );
* }),
* );
*
* @name unstable_RSCHydratedRouter
* @public
* @category RSC
* @mode data
* @param props Props
* @param {unstable_RSCHydratedRouterProps.createFromReadableStream} props.createFromReadableStream n/a
* @param {unstable_RSCHydratedRouterProps.fetch} props.fetch n/a
* @param {unstable_RSCHydratedRouterProps.getContext} props.getContext n/a
* @param {unstable_RSCHydratedRouterProps.payload} props.payload n/a
* @returns A hydrated {@link DataRouter} that can be used to navigate and
* render routes.
*/
declare function RSCHydratedRouter({ createFromReadableStream, fetch: fetchImplementation, payload, getContext, }: RSCHydratedRouterProps): React.JSX.Element;
export { type BrowserCreateFromReadableStreamFunction as B, type DecodeActionFunction as D, type EncodeReplyFunction as E, type LoadServerActionFunction as L, RSCHydratedRouter as R, type DecodeFormStateFunction as a, type DecodeReplyFunction as b, createCallServer as c, type RSCManifestPayload as d, type RSCPayload as e, type RSCRenderPayload as f, getRequest as g, type RSCHydratedRouterProps as h, type RSCMatch as i, type RSCRouteManifest as j, type RSCRouteMatch as k, type RSCRouteConfigEntry as l, matchRSCServerRequest as m, type RSCRouteConfig as n };
+188
View File
@@ -0,0 +1,188 @@
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
var _chunkSA4DP3SFjs = require('./chunk-SA4DP3SF.js');
// lib/dom/ssr/hydration.tsx
function getHydrationData({
state,
routes,
getRouteInfo,
location,
basename,
isSpaMode
}) {
let hydrationData = {
...state,
loaderData: { ...state.loaderData }
};
let initialMatches = _chunkSA4DP3SFjs.matchRoutes.call(void 0, routes, location, basename);
if (initialMatches) {
for (let match of initialMatches) {
let routeId = match.route.id;
let routeInfo = getRouteInfo(routeId);
if (_chunkSA4DP3SFjs.shouldHydrateRouteLoader.call(void 0,
routeId,
routeInfo.clientLoader,
routeInfo.hasLoader,
isSpaMode
) && (routeInfo.hasHydrateFallback || !routeInfo.hasLoader)) {
delete hydrationData.loaderData[routeId];
} else if (!routeInfo.hasLoader) {
hydrationData.loaderData[routeId] = null;
}
}
}
return hydrationData;
}
// lib/rsc/errorBoundaries.tsx
var _react = require('react'); var _react2 = _interopRequireDefault(_react);
var RSCRouterGlobalErrorBoundary = class extends _react2.default.Component {
constructor(props) {
super(props);
this.state = { error: null, location: props.location };
}
static getDerivedStateFromError(error) {
return { error };
}
static getDerivedStateFromProps(props, state) {
if (state.location !== props.location) {
return { error: null, location: props.location };
}
return { error: state.error, location: state.location };
}
render() {
if (this.state.error) {
return /* @__PURE__ */ _react2.default.createElement(
RSCDefaultRootErrorBoundaryImpl,
{
error: this.state.error,
renderAppShell: true
}
);
} else {
return this.props.children;
}
}
};
function ErrorWrapper({
renderAppShell,
title,
children
}) {
if (!renderAppShell) {
return children;
}
return /* @__PURE__ */ _react2.default.createElement("html", { lang: "en" }, /* @__PURE__ */ _react2.default.createElement("head", null, /* @__PURE__ */ _react2.default.createElement("meta", { charSet: "utf-8" }), /* @__PURE__ */ _react2.default.createElement(
"meta",
{
name: "viewport",
content: "width=device-width,initial-scale=1,viewport-fit=cover"
}
), /* @__PURE__ */ _react2.default.createElement("title", null, title)), /* @__PURE__ */ _react2.default.createElement("body", null, /* @__PURE__ */ _react2.default.createElement("main", { style: { fontFamily: "system-ui, sans-serif", padding: "2rem" } }, children)));
}
function RSCDefaultRootErrorBoundaryImpl({
error,
renderAppShell
}) {
console.error(error);
let heyDeveloper = /* @__PURE__ */ _react2.default.createElement(
"script",
{
dangerouslySetInnerHTML: {
__html: `
console.log(
"\u{1F4BF} Hey developer \u{1F44B}. You can provide a way better UX than this when your app throws errors. Check out https://reactrouter.com/how-to/error-boundary for more information."
);
`
}
}
);
if (_chunkSA4DP3SFjs.isRouteErrorResponse.call(void 0, error)) {
return /* @__PURE__ */ _react2.default.createElement(
ErrorWrapper,
{
renderAppShell,
title: "Unhandled Thrown Response!"
},
/* @__PURE__ */ _react2.default.createElement("h1", { style: { fontSize: "24px" } }, error.status, " ", error.statusText),
_chunkSA4DP3SFjs.ENABLE_DEV_WARNINGS ? heyDeveloper : null
);
}
let errorInstance;
if (error instanceof Error) {
errorInstance = error;
} else {
let errorString = error == null ? "Unknown Error" : typeof error === "object" && "toString" in error ? error.toString() : JSON.stringify(error);
errorInstance = new Error(errorString);
}
return /* @__PURE__ */ _react2.default.createElement(ErrorWrapper, { renderAppShell, title: "Application Error!" }, /* @__PURE__ */ _react2.default.createElement("h1", { style: { fontSize: "24px" } }, "Application Error"), /* @__PURE__ */ _react2.default.createElement(
"pre",
{
style: {
padding: "2rem",
background: "hsla(10, 50%, 50%, 0.1)",
color: "red",
overflow: "auto"
}
},
errorInstance.stack
), heyDeveloper);
}
function RSCDefaultRootErrorBoundary({
hasRootLayout
}) {
let error = _chunkSA4DP3SFjs.useRouteError.call(void 0, );
if (hasRootLayout === void 0) {
throw new Error("Missing 'hasRootLayout' prop");
}
return /* @__PURE__ */ _react2.default.createElement(
RSCDefaultRootErrorBoundaryImpl,
{
renderAppShell: !hasRootLayout,
error
}
);
}
// lib/rsc/route-modules.ts
function createRSCRouteModules(payload) {
const routeModules = {};
for (const match of payload.matches) {
populateRSCRouteModules(routeModules, match);
}
return routeModules;
}
function populateRSCRouteModules(routeModules, matches) {
matches = Array.isArray(matches) ? matches : [matches];
for (const match of matches) {
routeModules[match.id] = {
links: match.links,
meta: match.meta,
default: noopComponent
};
}
}
var noopComponent = () => null;
exports.getHydrationData = getHydrationData; exports.RSCRouterGlobalErrorBoundary = RSCRouterGlobalErrorBoundary; exports.RSCDefaultRootErrorBoundary = RSCDefaultRootErrorBoundary; exports.createRSCRouteModules = createRSCRouteModules; exports.populateRSCRouteModules = populateRSCRouteModules;
+1366
View File
@@ -0,0 +1,1366 @@
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
var _chunkSA4DP3SFjs = require('./chunk-SA4DP3SF.js');
// lib/dom/dom.ts
var defaultMethod = "get";
var defaultEncType = "application/x-www-form-urlencoded";
function isHtmlElement(object) {
return typeof HTMLElement !== "undefined" && object instanceof HTMLElement;
}
function isButtonElement(object) {
return isHtmlElement(object) && object.tagName.toLowerCase() === "button";
}
function isFormElement(object) {
return isHtmlElement(object) && object.tagName.toLowerCase() === "form";
}
function isInputElement(object) {
return isHtmlElement(object) && object.tagName.toLowerCase() === "input";
}
function isModifiedEvent(event) {
return !!(event.metaKey || event.altKey || event.ctrlKey || event.shiftKey);
}
function shouldProcessLinkClick(event, target) {
return event.button === 0 && // Ignore everything but left clicks
(!target || target === "_self") && // Let browser handle "target=_blank" etc.
!isModifiedEvent(event);
}
function createSearchParams(init = "") {
return new URLSearchParams(
typeof init === "string" || Array.isArray(init) || init instanceof URLSearchParams ? init : Object.keys(init).reduce((memo, key) => {
let value = init[key];
return memo.concat(
Array.isArray(value) ? value.map((v) => [key, v]) : [[key, value]]
);
}, [])
);
}
function getSearchParamsForLocation(locationSearch, defaultSearchParams) {
let searchParams = createSearchParams(locationSearch);
if (defaultSearchParams) {
defaultSearchParams.forEach((_, key) => {
if (!searchParams.has(key)) {
defaultSearchParams.getAll(key).forEach((value) => {
searchParams.append(key, value);
});
}
});
}
return searchParams;
}
var _formDataSupportsSubmitter = null;
function isFormDataSubmitterSupported() {
if (_formDataSupportsSubmitter === null) {
try {
new FormData(
document.createElement("form"),
// @ts-expect-error if FormData supports the submitter parameter, this will throw
0
);
_formDataSupportsSubmitter = false;
} catch (e) {
_formDataSupportsSubmitter = true;
}
}
return _formDataSupportsSubmitter;
}
var supportedFormEncTypes = /* @__PURE__ */ new Set([
"application/x-www-form-urlencoded",
"multipart/form-data",
"text/plain"
]);
function getFormEncType(encType) {
if (encType != null && !supportedFormEncTypes.has(encType)) {
_chunkSA4DP3SFjs.warning.call(void 0,
false,
`"${encType}" is not a valid \`encType\` for \`<Form>\`/\`<fetcher.Form>\` and will default to "${defaultEncType}"`
);
return null;
}
return encType;
}
function getFormSubmissionInfo(target, basename) {
let method;
let action;
let encType;
let formData;
let body;
if (isFormElement(target)) {
let attr = target.getAttribute("action");
action = attr ? _chunkSA4DP3SFjs.stripBasename.call(void 0, attr, basename) : null;
method = target.getAttribute("method") || defaultMethod;
encType = getFormEncType(target.getAttribute("enctype")) || defaultEncType;
formData = new FormData(target);
} else if (isButtonElement(target) || isInputElement(target) && (target.type === "submit" || target.type === "image")) {
let form = target.form;
if (form == null) {
throw new Error(
`Cannot submit a <button> or <input type="submit"> without a <form>`
);
}
let attr = target.getAttribute("formaction") || form.getAttribute("action");
action = attr ? _chunkSA4DP3SFjs.stripBasename.call(void 0, attr, basename) : null;
method = target.getAttribute("formmethod") || form.getAttribute("method") || defaultMethod;
encType = getFormEncType(target.getAttribute("formenctype")) || getFormEncType(form.getAttribute("enctype")) || defaultEncType;
formData = new FormData(form, target);
if (!isFormDataSubmitterSupported()) {
let { name, type, value } = target;
if (type === "image") {
let prefix = name ? `${name}.` : "";
formData.append(`${prefix}x`, "0");
formData.append(`${prefix}y`, "0");
} else if (name) {
formData.append(name, value);
}
}
} else if (isHtmlElement(target)) {
throw new Error(
`Cannot submit element that is not <form>, <button>, or <input type="submit|image">`
);
} else {
method = defaultMethod;
action = null;
encType = defaultEncType;
body = target;
}
if (formData && encType === "text/plain") {
body = formData;
formData = void 0;
}
return { action, method: method.toLowerCase(), encType, formData, body };
}
// lib/dom/lib.tsx
var _react = require('react'); var React = _interopRequireWildcard(_react); var React2 = _interopRequireWildcard(_react);
var isBrowser = typeof window !== "undefined" && typeof window.document !== "undefined" && typeof window.document.createElement !== "undefined";
try {
if (isBrowser) {
window.__reactRouterVersion = // @ts-expect-error
"7.18.1";
}
} catch (e) {
}
function createBrowserRouter(routes, opts) {
return _chunkSA4DP3SFjs.createRouter.call(void 0, {
basename: _optionalChain([opts, 'optionalAccess', _2 => _2.basename]),
getContext: _optionalChain([opts, 'optionalAccess', _3 => _3.getContext]),
future: _optionalChain([opts, 'optionalAccess', _4 => _4.future]),
history: _chunkSA4DP3SFjs.createBrowserHistory.call(void 0, { window: _optionalChain([opts, 'optionalAccess', _5 => _5.window]) }),
hydrationData: _optionalChain([opts, 'optionalAccess', _6 => _6.hydrationData]) || parseHydrationData(),
routes,
mapRouteProperties: _chunkSA4DP3SFjs.mapRouteProperties,
hydrationRouteProperties: _chunkSA4DP3SFjs.hydrationRouteProperties,
dataStrategy: _optionalChain([opts, 'optionalAccess', _7 => _7.dataStrategy]),
patchRoutesOnNavigation: _optionalChain([opts, 'optionalAccess', _8 => _8.patchRoutesOnNavigation]),
window: _optionalChain([opts, 'optionalAccess', _9 => _9.window]),
instrumentations: _optionalChain([opts, 'optionalAccess', _10 => _10.instrumentations])
}).initialize();
}
function createHashRouter(routes, opts) {
return _chunkSA4DP3SFjs.createRouter.call(void 0, {
basename: _optionalChain([opts, 'optionalAccess', _11 => _11.basename]),
getContext: _optionalChain([opts, 'optionalAccess', _12 => _12.getContext]),
future: _optionalChain([opts, 'optionalAccess', _13 => _13.future]),
history: _chunkSA4DP3SFjs.createHashHistory.call(void 0, { window: _optionalChain([opts, 'optionalAccess', _14 => _14.window]) }),
hydrationData: _optionalChain([opts, 'optionalAccess', _15 => _15.hydrationData]) || parseHydrationData(),
routes,
mapRouteProperties: _chunkSA4DP3SFjs.mapRouteProperties,
hydrationRouteProperties: _chunkSA4DP3SFjs.hydrationRouteProperties,
dataStrategy: _optionalChain([opts, 'optionalAccess', _16 => _16.dataStrategy]),
patchRoutesOnNavigation: _optionalChain([opts, 'optionalAccess', _17 => _17.patchRoutesOnNavigation]),
window: _optionalChain([opts, 'optionalAccess', _18 => _18.window]),
instrumentations: _optionalChain([opts, 'optionalAccess', _19 => _19.instrumentations])
}).initialize();
}
function parseHydrationData() {
let state = _optionalChain([window, 'optionalAccess', _20 => _20.__staticRouterHydrationData]);
if (state && state.errors) {
state = {
...state,
errors: deserializeErrors(state.errors)
};
}
return state;
}
function deserializeErrors(errors) {
if (!errors) return null;
let entries = Object.entries(errors);
let serialized = {};
for (let [key, val] of entries) {
if (val && val.__type === "RouteErrorResponse") {
serialized[key] = new (0, _chunkSA4DP3SFjs.ErrorResponseImpl)(
val.status,
val.statusText,
val.data,
val.internal === true
);
} else if (val && val.__type === "Error") {
if (typeof val.__subType === "string" && _chunkSA4DP3SFjs.SUPPORTED_ERROR_TYPES.includes(val.__subType)) {
let ErrorConstructor = window[val.__subType];
if (typeof ErrorConstructor === "function") {
try {
let error = new ErrorConstructor(val.message);
error.stack = "";
serialized[key] = error;
} catch (e) {
}
}
}
if (serialized[key] == null) {
let error = new Error(val.message);
error.stack = "";
serialized[key] = error;
}
} else {
serialized[key] = val;
}
}
return serialized;
}
function BrowserRouter({
basename,
children,
useTransitions,
window: window2
}) {
let historyRef = React.useRef();
if (historyRef.current == null) {
historyRef.current = _chunkSA4DP3SFjs.createBrowserHistory.call(void 0, { window: window2, v5Compat: true });
}
let history = historyRef.current;
let [state, setStateImpl] = React.useState({
action: history.action,
location: history.location
});
let setState = React.useCallback(
(newState) => {
if (useTransitions === false) {
setStateImpl(newState);
} else {
React.startTransition(() => setStateImpl(newState));
}
},
[useTransitions]
);
React.useLayoutEffect(() => history.listen(setState), [history, setState]);
return /* @__PURE__ */ React.createElement(
_chunkSA4DP3SFjs.Router,
{
basename,
children,
location: state.location,
navigationType: state.action,
navigator: history,
useTransitions
}
);
}
function HashRouter({
basename,
children,
useTransitions,
window: window2
}) {
let historyRef = React.useRef();
if (historyRef.current == null) {
historyRef.current = _chunkSA4DP3SFjs.createHashHistory.call(void 0, { window: window2, v5Compat: true });
}
let history = historyRef.current;
let [state, setStateImpl] = React.useState({
action: history.action,
location: history.location
});
let setState = React.useCallback(
(newState) => {
if (useTransitions === false) {
setStateImpl(newState);
} else {
React.startTransition(() => setStateImpl(newState));
}
},
[useTransitions]
);
React.useLayoutEffect(() => history.listen(setState), [history, setState]);
return /* @__PURE__ */ React.createElement(
_chunkSA4DP3SFjs.Router,
{
basename,
children,
location: state.location,
navigationType: state.action,
navigator: history,
useTransitions
}
);
}
function HistoryRouter({
basename,
children,
history,
useTransitions
}) {
let [state, setStateImpl] = React.useState({
action: history.action,
location: history.location
});
let setState = React.useCallback(
(newState) => {
if (useTransitions === false) {
setStateImpl(newState);
} else {
React.startTransition(() => setStateImpl(newState));
}
},
[useTransitions]
);
React.useLayoutEffect(() => history.listen(setState), [history, setState]);
return /* @__PURE__ */ React.createElement(
_chunkSA4DP3SFjs.Router,
{
basename,
children,
location: state.location,
navigationType: state.action,
navigator: history,
useTransitions
}
);
}
HistoryRouter.displayName = "unstable_HistoryRouter";
var Link = React.forwardRef(
function LinkWithRef({
onClick,
discover = "render",
prefetch = "none",
relative,
reloadDocument,
replace,
mask,
state,
target,
to,
preventScrollReset,
viewTransition,
defaultShouldRevalidate,
...rest
}, forwardedRef) {
let { basename, navigator, useTransitions } = React.useContext(_chunkSA4DP3SFjs.NavigationContext);
let isAbsolute = typeof to === "string" && _chunkSA4DP3SFjs.ABSOLUTE_URL_REGEX.test(to);
let parsed = _chunkSA4DP3SFjs.parseToInfo.call(void 0, to, basename);
to = parsed.to;
let href = _chunkSA4DP3SFjs.useHref.call(void 0, to, { relative });
let location = _chunkSA4DP3SFjs.useLocation.call(void 0, );
let maskedHref = null;
if (mask) {
let resolved = _chunkSA4DP3SFjs.resolveTo.call(void 0,
mask,
[],
location.mask ? location.mask.pathname : "/",
true
);
if (basename !== "/") {
resolved.pathname = resolved.pathname === "/" ? basename : _chunkSA4DP3SFjs.joinPaths.call(void 0, [basename, resolved.pathname]);
}
maskedHref = navigator.createHref(resolved);
}
let [shouldPrefetch, prefetchRef, prefetchHandlers] = _chunkSA4DP3SFjs.usePrefetchBehavior.call(void 0,
prefetch,
rest
);
let internalOnClick = useLinkClickHandler(to, {
replace,
mask,
state,
target,
preventScrollReset,
relative,
viewTransition,
defaultShouldRevalidate,
useTransitions
});
function handleClick(event) {
if (onClick) onClick(event);
if (!event.defaultPrevented) {
internalOnClick(event);
}
}
let isSpaLink = !(parsed.isExternal || reloadDocument);
let link = (
// eslint-disable-next-line jsx-a11y/anchor-has-content
/* @__PURE__ */ React.createElement(
"a",
{
...rest,
...prefetchHandlers,
href: (isSpaLink ? maskedHref : void 0) || parsed.absoluteURL || href,
onClick: isSpaLink ? handleClick : onClick,
ref: _chunkSA4DP3SFjs.mergeRefs.call(void 0, forwardedRef, prefetchRef),
target,
"data-discover": !isAbsolute && discover === "render" ? "true" : void 0
}
)
);
return shouldPrefetch && !isAbsolute ? /* @__PURE__ */ React.createElement(React.Fragment, null, link, /* @__PURE__ */ React.createElement(_chunkSA4DP3SFjs.PrefetchPageLinks, { page: href })) : link;
}
);
Link.displayName = "Link";
var NavLink = React.forwardRef(
function NavLinkWithRef({
"aria-current": ariaCurrentProp = "page",
caseSensitive = false,
className: classNameProp = "",
end = false,
style: styleProp,
to,
viewTransition,
children,
...rest
}, ref) {
let path = _chunkSA4DP3SFjs.useResolvedPath.call(void 0, to, { relative: rest.relative });
let location = _chunkSA4DP3SFjs.useLocation.call(void 0, );
let routerState = React.useContext(_chunkSA4DP3SFjs.DataRouterStateContext);
let { navigator, basename } = React.useContext(_chunkSA4DP3SFjs.NavigationContext);
let isTransitioning = routerState != null && // Conditional usage is OK here because the usage of a data router is static
// eslint-disable-next-line react-hooks/rules-of-hooks
useViewTransitionState(path) && viewTransition === true;
let toPathname = navigator.encodeLocation ? navigator.encodeLocation(path).pathname : path.pathname;
let locationPathname = location.pathname;
let nextLocationPathname = routerState && routerState.navigation && routerState.navigation.location ? routerState.navigation.location.pathname : null;
if (!caseSensitive) {
locationPathname = locationPathname.toLowerCase();
nextLocationPathname = nextLocationPathname ? nextLocationPathname.toLowerCase() : null;
toPathname = toPathname.toLowerCase();
}
if (nextLocationPathname && basename) {
nextLocationPathname = _chunkSA4DP3SFjs.stripBasename.call(void 0, nextLocationPathname, basename) || nextLocationPathname;
}
const endSlashPosition = toPathname !== "/" && toPathname.endsWith("/") ? toPathname.length - 1 : toPathname.length;
let isActive = locationPathname === toPathname || !end && locationPathname.startsWith(toPathname) && locationPathname.charAt(endSlashPosition) === "/";
let isPending = nextLocationPathname != null && (nextLocationPathname === toPathname || !end && nextLocationPathname.startsWith(toPathname) && nextLocationPathname.charAt(toPathname.length) === "/");
let renderProps = {
isActive,
isPending,
isTransitioning
};
let ariaCurrent = isActive ? ariaCurrentProp : void 0;
let className;
if (typeof classNameProp === "function") {
className = classNameProp(renderProps);
} else {
className = [
classNameProp,
isActive ? "active" : null,
isPending ? "pending" : null,
isTransitioning ? "transitioning" : null
].filter(Boolean).join(" ");
}
let style = typeof styleProp === "function" ? styleProp(renderProps) : styleProp;
return /* @__PURE__ */ React.createElement(
Link,
{
...rest,
"aria-current": ariaCurrent,
className,
ref,
style,
to,
viewTransition
},
typeof children === "function" ? children(renderProps) : children
);
}
);
NavLink.displayName = "NavLink";
var Form = React.forwardRef(
({
discover = "render",
fetcherKey,
navigate,
reloadDocument,
replace,
state,
method = defaultMethod,
action,
onSubmit,
relative,
preventScrollReset,
viewTransition,
defaultShouldRevalidate,
...props
}, forwardedRef) => {
let { useTransitions } = React.useContext(_chunkSA4DP3SFjs.NavigationContext);
let submit = useSubmit();
let formAction = useFormAction(action, { relative });
let formMethod = method.toLowerCase() === "get" ? "get" : "post";
let isAbsolute = typeof action === "string" && _chunkSA4DP3SFjs.ABSOLUTE_URL_REGEX.test(action);
let submitHandler = (event) => {
onSubmit && onSubmit(event);
if (event.defaultPrevented) return;
event.preventDefault();
let submitter = event.nativeEvent.submitter;
let submitMethod = _optionalChain([submitter, 'optionalAccess', _21 => _21.getAttribute, 'call', _22 => _22("formmethod")]) || method;
let doSubmit = () => submit(submitter || event.currentTarget, {
fetcherKey,
method: submitMethod,
navigate,
replace,
state,
relative,
preventScrollReset,
viewTransition,
defaultShouldRevalidate
});
if (useTransitions && navigate !== false) {
React.startTransition(() => doSubmit());
} else {
doSubmit();
}
};
return /* @__PURE__ */ React.createElement(
"form",
{
ref: forwardedRef,
method: formMethod,
action: formAction,
onSubmit: reloadDocument ? onSubmit : submitHandler,
...props,
"data-discover": !isAbsolute && discover === "render" ? "true" : void 0
}
);
}
);
Form.displayName = "Form";
function ScrollRestoration({
getKey,
storageKey,
...props
}) {
let remixContext = React.useContext(_chunkSA4DP3SFjs.FrameworkContext);
let { basename } = React.useContext(_chunkSA4DP3SFjs.NavigationContext);
let location = _chunkSA4DP3SFjs.useLocation.call(void 0, );
let matches = _chunkSA4DP3SFjs.useMatches.call(void 0, );
useScrollRestoration({ getKey, storageKey });
let ssrKey = React.useMemo(
() => {
if (!remixContext || !getKey) return null;
let userKey = getScrollRestorationKey(
location,
matches,
basename,
getKey
);
return userKey !== location.key ? userKey : null;
},
// Nah, we only need this the first time for the SSR render
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
if (!remixContext || remixContext.isSpaMode) {
return null;
}
let restoreScroll = ((storageKey2, restoreKey) => {
if (!window.history.state || !window.history.state.key) {
let key = Math.random().toString(32).slice(2);
window.history.replaceState({ key }, "");
}
try {
let positions = JSON.parse(sessionStorage.getItem(storageKey2) || "{}");
let storedY = positions[restoreKey || window.history.state.key];
if (typeof storedY === "number") {
window.scrollTo(0, storedY);
}
} catch (error) {
console.error(error);
sessionStorage.removeItem(storageKey2);
}
}).toString();
if (props.nonce == null && _optionalChain([remixContext, 'optionalAccess', _23 => _23.nonce])) {
props.nonce = remixContext.nonce;
}
return /* @__PURE__ */ React.createElement(
"script",
{
...props,
suppressHydrationWarning: true,
dangerouslySetInnerHTML: {
__html: `(${restoreScroll})(${_chunkSA4DP3SFjs.escapeHtml.call(void 0,
JSON.stringify(storageKey || SCROLL_RESTORATION_STORAGE_KEY)
)}, ${_chunkSA4DP3SFjs.escapeHtml.call(void 0, JSON.stringify(ssrKey))})`
}
}
);
}
ScrollRestoration.displayName = "ScrollRestoration";
function getDataRouterConsoleError(hookName) {
return `${hookName} must be used within a data router. See https://reactrouter.com/en/main/routers/picking-a-router.`;
}
function useDataRouterContext(hookName) {
let ctx = React.useContext(_chunkSA4DP3SFjs.DataRouterContext);
_chunkSA4DP3SFjs.invariant.call(void 0, ctx, getDataRouterConsoleError(hookName));
return ctx;
}
function useDataRouterState(hookName) {
let state = React.useContext(_chunkSA4DP3SFjs.DataRouterStateContext);
_chunkSA4DP3SFjs.invariant.call(void 0, state, getDataRouterConsoleError(hookName));
return state;
}
function useLinkClickHandler(to, {
target,
replace: replaceProp,
mask,
state,
preventScrollReset,
relative,
viewTransition,
defaultShouldRevalidate,
useTransitions
} = {}) {
let navigate = _chunkSA4DP3SFjs.useNavigate.call(void 0, );
let location = _chunkSA4DP3SFjs.useLocation.call(void 0, );
let path = _chunkSA4DP3SFjs.useResolvedPath.call(void 0, to, { relative });
return React.useCallback(
(event) => {
if (shouldProcessLinkClick(event, target)) {
event.preventDefault();
let replace = replaceProp !== void 0 ? replaceProp : _chunkSA4DP3SFjs.createPath.call(void 0, location) === _chunkSA4DP3SFjs.createPath.call(void 0, path);
let doNavigate = () => navigate(to, {
replace,
mask,
state,
preventScrollReset,
relative,
viewTransition,
defaultShouldRevalidate
});
if (useTransitions) {
React.startTransition(() => doNavigate());
} else {
doNavigate();
}
}
},
[
location,
navigate,
path,
replaceProp,
mask,
state,
target,
to,
preventScrollReset,
relative,
viewTransition,
defaultShouldRevalidate,
useTransitions
]
);
}
function useSearchParams(defaultInit) {
_chunkSA4DP3SFjs.warning.call(void 0,
typeof URLSearchParams !== "undefined",
`You cannot use the \`useSearchParams\` hook in a browser that does not support the URLSearchParams API. If you need to support Internet Explorer 11, we recommend you load a polyfill such as https://github.com/ungap/url-search-params.`
);
let defaultSearchParamsRef = React.useRef(createSearchParams(defaultInit));
let hasSetSearchParamsRef = React.useRef(false);
let location = _chunkSA4DP3SFjs.useLocation.call(void 0, );
let searchParams = React.useMemo(
() => (
// Only merge in the defaults if we haven't yet called setSearchParams.
// Once we call that we want those to take precedence, otherwise you can't
// remove a param with setSearchParams({}) if it has an initial value
getSearchParamsForLocation(
location.search,
hasSetSearchParamsRef.current ? null : defaultSearchParamsRef.current
)
),
[location.search]
);
let navigate = _chunkSA4DP3SFjs.useNavigate.call(void 0, );
let setSearchParams = React.useCallback(
(nextInit, navigateOptions) => {
const newSearchParams = createSearchParams(
typeof nextInit === "function" ? nextInit(new URLSearchParams(searchParams)) : nextInit
);
hasSetSearchParamsRef.current = true;
navigate("?" + newSearchParams, navigateOptions);
},
[navigate, searchParams]
);
return [searchParams, setSearchParams];
}
var fetcherId = 0;
var getUniqueFetcherId = () => `__${String(++fetcherId)}__`;
function useSubmit() {
let { router } = useDataRouterContext("useSubmit" /* UseSubmit */);
let { basename } = React.useContext(_chunkSA4DP3SFjs.NavigationContext);
let currentRouteId = _chunkSA4DP3SFjs.useRouteId.call(void 0, );
let routerFetch = router.fetch;
let routerNavigate = router.navigate;
return React.useCallback(
async (target, options = {}) => {
let { action, method, encType, formData, body } = getFormSubmissionInfo(
target,
basename
);
if (options.navigate === false) {
let key = options.fetcherKey || getUniqueFetcherId();
await routerFetch(key, currentRouteId, options.action || action, {
defaultShouldRevalidate: options.defaultShouldRevalidate,
preventScrollReset: options.preventScrollReset,
formData,
body,
formMethod: options.method || method,
formEncType: options.encType || encType,
flushSync: options.flushSync
});
} else {
await routerNavigate(options.action || action, {
defaultShouldRevalidate: options.defaultShouldRevalidate,
preventScrollReset: options.preventScrollReset,
formData,
body,
formMethod: options.method || method,
formEncType: options.encType || encType,
replace: options.replace,
state: options.state,
fromRouteId: currentRouteId,
flushSync: options.flushSync,
viewTransition: options.viewTransition
});
}
},
[routerFetch, routerNavigate, basename, currentRouteId]
);
}
function useFormAction(action, { relative } = {}) {
let { basename } = React.useContext(_chunkSA4DP3SFjs.NavigationContext);
let routeContext = React.useContext(_chunkSA4DP3SFjs.RouteContext);
_chunkSA4DP3SFjs.invariant.call(void 0, routeContext, "useFormAction must be used inside a RouteContext");
let [match] = routeContext.matches.slice(-1);
let path = { ..._chunkSA4DP3SFjs.useResolvedPath.call(void 0, action ? action : ".", { relative }) };
let location = _chunkSA4DP3SFjs.useLocation.call(void 0, );
if (action == null) {
path.search = location.search;
let params = new URLSearchParams(path.search);
let indexValues = params.getAll("index");
let hasNakedIndexParam = indexValues.some((v) => v === "");
if (hasNakedIndexParam) {
params.delete("index");
indexValues.filter((v) => v).forEach((v) => params.append("index", v));
let qs = params.toString();
path.search = qs ? `?${qs}` : "";
}
}
if ((!action || action === ".") && match.route.index) {
path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
}
if (basename !== "/") {
path.pathname = path.pathname === "/" ? basename : _chunkSA4DP3SFjs.joinPaths.call(void 0, [basename, path.pathname]);
}
return _chunkSA4DP3SFjs.createPath.call(void 0, path);
}
function useFetcher({
key
} = {}) {
let { router } = useDataRouterContext("useFetcher" /* UseFetcher */);
let state = useDataRouterState("useFetcher" /* UseFetcher */);
let fetcherData = React.useContext(_chunkSA4DP3SFjs.FetchersContext);
let route = React.useContext(_chunkSA4DP3SFjs.RouteContext);
let routeId = _optionalChain([route, 'access', _24 => _24.matches, 'access', _25 => _25[route.matches.length - 1], 'optionalAccess', _26 => _26.route, 'access', _27 => _27.id]);
_chunkSA4DP3SFjs.invariant.call(void 0, fetcherData, `useFetcher must be used inside a FetchersContext`);
_chunkSA4DP3SFjs.invariant.call(void 0, route, `useFetcher must be used inside a RouteContext`);
_chunkSA4DP3SFjs.invariant.call(void 0,
routeId != null,
`useFetcher can only be used on routes that contain a unique "id"`
);
let defaultKey = React.useId();
let [fetcherKey, setFetcherKey] = React.useState(key || defaultKey);
if (key && key !== fetcherKey) {
setFetcherKey(key);
}
let { deleteFetcher, getFetcher, resetFetcher, fetch: routerFetch } = router;
React.useEffect(() => {
getFetcher(fetcherKey);
return () => deleteFetcher(fetcherKey);
}, [deleteFetcher, getFetcher, fetcherKey]);
let load = React.useCallback(
async (href, opts) => {
_chunkSA4DP3SFjs.invariant.call(void 0, routeId, "No routeId available for fetcher.load()");
await routerFetch(fetcherKey, routeId, href, opts);
},
[fetcherKey, routeId, routerFetch]
);
let submitImpl = useSubmit();
let submit = React.useCallback(
async (target, opts) => {
await submitImpl(target, {
...opts,
navigate: false,
fetcherKey
});
},
[fetcherKey, submitImpl]
);
let reset = React.useCallback(
(opts) => resetFetcher(fetcherKey, opts),
[resetFetcher, fetcherKey]
);
let FetcherForm = React.useMemo(() => {
let FetcherForm2 = React.forwardRef(
(props, ref) => {
return /* @__PURE__ */ React.createElement(Form, { ...props, navigate: false, fetcherKey, ref });
}
);
FetcherForm2.displayName = "fetcher.Form";
return FetcherForm2;
}, [fetcherKey]);
let fetcher = state.fetchers.get(fetcherKey) || _chunkSA4DP3SFjs.IDLE_FETCHER;
let data = fetcherData.get(fetcherKey);
let fetcherWithComponents = React.useMemo(
() => ({
Form: FetcherForm,
submit,
load,
reset,
...fetcher,
data
}),
[FetcherForm, submit, load, reset, fetcher, data]
);
return fetcherWithComponents;
}
function useFetchers() {
let state = useDataRouterState("useFetchers" /* UseFetchers */);
return React.useMemo(
() => Array.from(state.fetchers.entries()).map(([key, fetcher]) => ({
...fetcher,
key
})),
[state.fetchers]
);
}
var SCROLL_RESTORATION_STORAGE_KEY = "react-router-scroll-positions";
var savedScrollPositions = {};
function getScrollRestorationKey(location, matches, basename, getKey) {
let key = null;
if (getKey) {
if (basename !== "/") {
key = getKey(
{
...location,
pathname: _chunkSA4DP3SFjs.stripBasename.call(void 0, location.pathname, basename) || location.pathname
},
matches
);
} else {
key = getKey(location, matches);
}
}
if (key == null) {
key = location.key;
}
return key;
}
function useScrollRestoration({
getKey,
storageKey
} = {}) {
let { router } = useDataRouterContext("useScrollRestoration" /* UseScrollRestoration */);
let { restoreScrollPosition, preventScrollReset } = useDataRouterState(
"useScrollRestoration" /* UseScrollRestoration */
);
let { basename } = React.useContext(_chunkSA4DP3SFjs.NavigationContext);
let location = _chunkSA4DP3SFjs.useLocation.call(void 0, );
let matches = _chunkSA4DP3SFjs.useMatches.call(void 0, );
let navigation = _chunkSA4DP3SFjs.useNavigation.call(void 0, );
React.useEffect(() => {
window.history.scrollRestoration = "manual";
return () => {
window.history.scrollRestoration = "auto";
};
}, []);
usePageHide(
React.useCallback(() => {
if (navigation.state === "idle") {
let key = getScrollRestorationKey(location, matches, basename, getKey);
savedScrollPositions[key] = window.scrollY;
}
try {
sessionStorage.setItem(
storageKey || SCROLL_RESTORATION_STORAGE_KEY,
JSON.stringify(savedScrollPositions)
);
} catch (error) {
_chunkSA4DP3SFjs.warning.call(void 0,
false,
`Failed to save scroll positions in sessionStorage, <ScrollRestoration /> will not work properly (${error}).`
);
}
window.history.scrollRestoration = "auto";
}, [navigation.state, getKey, basename, location, matches, storageKey])
);
if (typeof document !== "undefined") {
React.useLayoutEffect(() => {
try {
let sessionPositions = sessionStorage.getItem(
storageKey || SCROLL_RESTORATION_STORAGE_KEY
);
if (sessionPositions) {
savedScrollPositions = JSON.parse(sessionPositions);
}
} catch (e) {
}
}, [storageKey]);
React.useLayoutEffect(() => {
let disableScrollRestoration = _optionalChain([router, 'optionalAccess', _28 => _28.enableScrollRestoration, 'call', _29 => _29(
savedScrollPositions,
() => window.scrollY,
getKey ? (location2, matches2) => getScrollRestorationKey(location2, matches2, basename, getKey) : void 0
)]);
return () => disableScrollRestoration && disableScrollRestoration();
}, [router, basename, getKey]);
React.useLayoutEffect(() => {
if (restoreScrollPosition === false) {
return;
}
if (typeof restoreScrollPosition === "number") {
window.scrollTo(0, restoreScrollPosition);
return;
}
try {
if (location.hash) {
let el = document.getElementById(
decodeURIComponent(location.hash.slice(1))
);
if (el) {
el.scrollIntoView();
return;
}
}
} catch (e2) {
_chunkSA4DP3SFjs.warning.call(void 0,
false,
`"${location.hash.slice(
1
)}" is not a decodable element ID. The view will not scroll to it.`
);
}
if (preventScrollReset === true) {
return;
}
window.scrollTo(0, 0);
}, [location, restoreScrollPosition, preventScrollReset]);
}
}
function useBeforeUnload(callback, options) {
let { capture } = options || {};
React.useEffect(() => {
let opts = capture != null ? { capture } : void 0;
window.addEventListener("beforeunload", callback, opts);
return () => {
window.removeEventListener("beforeunload", callback, opts);
};
}, [callback, capture]);
}
function usePageHide(callback, options) {
let { capture } = options || {};
React.useEffect(() => {
let opts = capture != null ? { capture } : void 0;
window.addEventListener("pagehide", callback, opts);
return () => {
window.removeEventListener("pagehide", callback, opts);
};
}, [callback, capture]);
}
function usePrompt({
when,
message
}) {
let blocker = _chunkSA4DP3SFjs.useBlocker.call(void 0, when);
React.useEffect(() => {
if (blocker.state === "blocked") {
let proceed = window.confirm(message);
if (proceed) {
setTimeout(blocker.proceed, 0);
} else {
blocker.reset();
}
}
}, [blocker, message]);
React.useEffect(() => {
if (blocker.state === "blocked" && !when) {
blocker.reset();
}
}, [blocker, when]);
}
function useViewTransitionState(to, { relative } = {}) {
let vtContext = React.useContext(_chunkSA4DP3SFjs.ViewTransitionContext);
_chunkSA4DP3SFjs.invariant.call(void 0,
vtContext != null,
"`useViewTransitionState` must be used within `react-router-dom`'s `RouterProvider`. Did you accidentally import `RouterProvider` from `react-router`?"
);
let { basename } = useDataRouterContext(
"useViewTransitionState" /* useViewTransitionState */
);
let path = _chunkSA4DP3SFjs.useResolvedPath.call(void 0, to, { relative });
if (!vtContext.isTransitioning) {
return false;
}
let currentPath = _chunkSA4DP3SFjs.stripBasename.call(void 0, vtContext.currentLocation.pathname, basename) || vtContext.currentLocation.pathname;
let nextPath = _chunkSA4DP3SFjs.stripBasename.call(void 0, vtContext.nextLocation.pathname, basename) || vtContext.nextLocation.pathname;
return _chunkSA4DP3SFjs.matchPath.call(void 0, path.pathname, nextPath) != null || _chunkSA4DP3SFjs.matchPath.call(void 0, path.pathname, currentPath) != null;
}
// lib/dom/server.tsx
function StaticRouter({
basename,
children,
location: locationProp = "/"
}) {
if (typeof locationProp === "string") {
locationProp = _chunkSA4DP3SFjs.parsePath.call(void 0, locationProp);
}
let action = "POP" /* Pop */;
let location = {
pathname: locationProp.pathname || "/",
search: locationProp.search || "",
hash: locationProp.hash || "",
state: locationProp.state != null ? locationProp.state : null,
key: locationProp.key || "default",
mask: void 0
};
let staticNavigator = getStatelessNavigator();
return /* @__PURE__ */ React2.createElement(
_chunkSA4DP3SFjs.Router,
{
basename,
children,
location,
navigationType: action,
navigator: staticNavigator,
static: true,
useTransitions: false
}
);
}
function StaticRouterProvider({
context,
router,
hydrate = true,
nonce
}) {
_chunkSA4DP3SFjs.invariant.call(void 0,
router && context,
"You must provide `router` and `context` to <StaticRouterProvider>"
);
let dataRouterContext = {
router,
navigator: getStatelessNavigator(),
static: true,
staticContext: context,
basename: context.basename || "/"
};
let fetchersContext = /* @__PURE__ */ new Map();
let hydrateScript = "";
if (hydrate !== false) {
let data = {
loaderData: context.loaderData,
actionData: context.actionData,
errors: serializeErrors(context.errors)
};
let json = _chunkSA4DP3SFjs.escapeHtml.call(void 0, JSON.stringify(JSON.stringify(data)));
hydrateScript = `window.__staticRouterHydrationData = JSON.parse(${json});`;
}
let { state } = dataRouterContext.router;
return /* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(_chunkSA4DP3SFjs.DataRouterContext.Provider, { value: dataRouterContext }, /* @__PURE__ */ React2.createElement(_chunkSA4DP3SFjs.DataRouterStateContext.Provider, { value: state }, /* @__PURE__ */ React2.createElement(_chunkSA4DP3SFjs.FetchersContext.Provider, { value: fetchersContext }, /* @__PURE__ */ React2.createElement(_chunkSA4DP3SFjs.ViewTransitionContext.Provider, { value: { isTransitioning: false } }, /* @__PURE__ */ React2.createElement(
_chunkSA4DP3SFjs.Router,
{
basename: dataRouterContext.basename,
location: state.location,
navigationType: state.historyAction,
navigator: dataRouterContext.navigator,
static: dataRouterContext.static,
useTransitions: false
},
/* @__PURE__ */ React2.createElement(
_chunkSA4DP3SFjs.DataRoutes,
{
manifest: router.manifest,
routes: router.routes,
future: router.future,
state,
isStatic: true
}
)
))))), hydrateScript ? /* @__PURE__ */ React2.createElement(
"script",
{
suppressHydrationWarning: true,
nonce,
dangerouslySetInnerHTML: { __html: hydrateScript }
}
) : null);
}
function serializeErrors(errors) {
if (!errors) return null;
let entries = Object.entries(errors);
let serialized = {};
for (let [key, val] of entries) {
if (_chunkSA4DP3SFjs.isRouteErrorResponse.call(void 0, val)) {
serialized[key] = { ...val, __type: "RouteErrorResponse" };
} else if (val instanceof Error) {
serialized[key] = {
message: val.message,
__type: "Error",
// If this is a subclass (i.e., ReferenceError), send up the type so we
// can re-create the same type during hydration.
...val.name !== "Error" ? {
__subType: val.name
} : {}
};
} else {
serialized[key] = val;
}
}
return serialized;
}
function getStatelessNavigator() {
return {
createHref,
encodeLocation,
push(to) {
throw new Error(
`You cannot use navigator.push() on the server because it is a stateless environment. This error was probably triggered when you did a \`navigate(${JSON.stringify(to)})\` somewhere in your app.`
);
},
replace(to) {
throw new Error(
`You cannot use navigator.replace() on the server because it is a stateless environment. This error was probably triggered when you did a \`navigate(${JSON.stringify(to)}, { replace: true })\` somewhere in your app.`
);
},
go(delta) {
throw new Error(
`You cannot use navigator.go() on the server because it is a stateless environment. This error was probably triggered when you did a \`navigate(${delta})\` somewhere in your app.`
);
},
back() {
throw new Error(
`You cannot use navigator.back() on the server because it is a stateless environment.`
);
},
forward() {
throw new Error(
`You cannot use navigator.forward() on the server because it is a stateless environment.`
);
}
};
}
function createStaticHandler2(routes, opts) {
return _chunkSA4DP3SFjs.createStaticHandler.call(void 0, routes, {
...opts,
mapRouteProperties: _chunkSA4DP3SFjs.mapRouteProperties
});
}
function createStaticRouter(routes, context, opts = {}) {
let manifest = {};
let dataRoutes = _chunkSA4DP3SFjs.convertRoutesToDataRoutes.call(void 0,
routes,
_chunkSA4DP3SFjs.mapRouteProperties,
void 0,
manifest
);
let matches = context.matches.map((match) => {
let route = manifest[match.route.id] || match.route;
return {
...match,
route
};
});
let msg = (method) => `You cannot use router.${method}() on the server because it is a stateless environment`;
return {
get basename() {
return context.basename;
},
get future() {
return {
v8_middleware: false,
v8_passThroughRequests: false,
v8_trailingSlashAwareDataRequests: false,
..._optionalChain([opts, 'optionalAccess', _30 => _30.future])
};
},
get state() {
return {
historyAction: "POP" /* Pop */,
location: context.location,
matches,
loaderData: context.loaderData,
actionData: context.actionData,
errors: context.errors,
initialized: true,
renderFallback: false,
navigation: _chunkSA4DP3SFjs.IDLE_NAVIGATION,
restoreScrollPosition: null,
preventScrollReset: false,
revalidation: "idle",
fetchers: /* @__PURE__ */ new Map(),
blockers: /* @__PURE__ */ new Map()
};
},
get routes() {
return dataRoutes;
},
get branches() {
return opts.branches;
},
get manifest() {
return manifest;
},
get window() {
return void 0;
},
initialize() {
throw msg("initialize");
},
subscribe() {
throw msg("subscribe");
},
enableScrollRestoration() {
throw msg("enableScrollRestoration");
},
navigate() {
throw msg("navigate");
},
fetch() {
throw msg("fetch");
},
revalidate() {
throw msg("revalidate");
},
createHref,
encodeLocation,
getFetcher() {
return _chunkSA4DP3SFjs.IDLE_FETCHER;
},
deleteFetcher() {
throw msg("deleteFetcher");
},
resetFetcher() {
throw msg("resetFetcher");
},
dispose() {
throw msg("dispose");
},
getBlocker() {
return _chunkSA4DP3SFjs.IDLE_BLOCKER;
},
deleteBlocker() {
throw msg("deleteBlocker");
},
patchRoutes() {
throw msg("patchRoutes");
},
_internalFetchControllers: /* @__PURE__ */ new Map(),
_internalSetRoutes() {
throw msg("_internalSetRoutes");
},
_internalSetStateDoNotUseOrYouWillBreakYourApp() {
throw msg("_internalSetStateDoNotUseOrYouWillBreakYourApp");
}
};
}
function createHref(to) {
return typeof to === "string" ? to : _chunkSA4DP3SFjs.createPath.call(void 0, to);
}
function encodeLocation(to) {
let href = typeof to === "string" ? to : _chunkSA4DP3SFjs.createPath.call(void 0, to);
href = href.replace(/ $/, "%20");
let encoded = _chunkSA4DP3SFjs.ABSOLUTE_URL_REGEX.test(href) ? new URL(href) : new URL(href, "http://localhost");
return {
pathname: encoded.pathname,
search: encoded.search,
hash: encoded.hash
};
}
exports.createSearchParams = createSearchParams; exports.createBrowserRouter = createBrowserRouter; exports.createHashRouter = createHashRouter; exports.BrowserRouter = BrowserRouter; exports.HashRouter = HashRouter; exports.HistoryRouter = HistoryRouter; exports.Link = Link; exports.NavLink = NavLink; exports.Form = Form; exports.ScrollRestoration = ScrollRestoration; exports.useLinkClickHandler = useLinkClickHandler; exports.useSearchParams = useSearchParams; exports.useSubmit = useSubmit; exports.useFormAction = useFormAction; exports.useFetcher = useFetcher; exports.useFetchers = useFetchers; exports.useScrollRestoration = useScrollRestoration; exports.useBeforeUnload = useBeforeUnload; exports.usePrompt = usePrompt; exports.useViewTransitionState = useViewTransitionState; exports.StaticRouter = StaticRouter; exports.StaticRouterProvider = StaticRouterProvider; exports.createStaticHandler = createStaticHandler2; exports.createStaticRouter = createStaticRouter;
+2517
View File
@@ -0,0 +1,2517 @@
/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
import {
ENABLE_DEV_WARNINGS,
ErrorResponseImpl,
FrameworkContext,
NO_BODY_STATUS_CODES,
Outlet,
RSCRouterContext,
RemixErrorBoundary,
RouterContextProvider,
RouterProvider,
SINGLE_FETCH_REDIRECT_STATUS,
SingleFetchRedirectSymbol,
StaticRouterProvider,
StreamTransfer,
URL_LIMIT,
convertRoutesToDataRoutes,
createMemoryRouter,
createServerRoutes,
createStaticHandler,
createStaticRouter,
decodeRedirectErrorDigest,
decodeRouteErrorResponseDigest,
decodeViaTurboStream,
encode,
escapeHtml,
getManifestPath,
getStaticContextFromError,
hasInvalidProtocol,
instrumentHandler,
isDataWithResponseInit,
isMutationMethod,
isRedirectResponse,
isRedirectStatusCode,
isResponse,
isRouteErrorResponse,
matchRoutes,
matchRoutesImpl,
redirect,
redirectDocument,
replace,
shouldHydrateRouteLoader,
stripBasename,
useRouteError,
warnOnce,
withComponentProps,
withErrorBoundaryProps,
withHydrateFallbackProps
} from "./chunk-KS7C4IRE.mjs";
// lib/dom/ssr/server.tsx
import * as React from "react";
function ServerRouter({
context,
url,
nonce
}) {
if (typeof url === "string") {
url = new URL(url);
}
let { manifest, routeModules, criticalCss, serverHandoffString } = context;
let routes = createServerRoutes(
manifest.routes,
routeModules,
context.future,
context.isSpaMode
);
context.staticHandlerContext.loaderData = {
...context.staticHandlerContext.loaderData
};
for (let match of context.staticHandlerContext.matches) {
let routeId = match.route.id;
let route = routeModules[routeId];
let manifestRoute = context.manifest.routes[routeId];
if (route && manifestRoute && shouldHydrateRouteLoader(
routeId,
route.clientLoader,
manifestRoute.hasLoader,
context.isSpaMode
) && (route.HydrateFallback || !manifestRoute.hasLoader)) {
delete context.staticHandlerContext.loaderData[routeId];
}
}
let router = createStaticRouter(routes, context.staticHandlerContext, {
branches: context.branches
});
return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
FrameworkContext.Provider,
{
value: {
manifest,
routeModules,
criticalCss,
serverHandoffString,
future: context.future,
ssr: context.ssr,
isSpaMode: context.isSpaMode,
routeDiscovery: context.routeDiscovery,
nonce,
serializeError: context.serializeError,
renderMeta: context.renderMeta
}
},
/* @__PURE__ */ React.createElement(RemixErrorBoundary, { location: router.state.location }, /* @__PURE__ */ React.createElement(
StaticRouterProvider,
{
router,
context: context.staticHandlerContext,
hydrate: false
}
))
), context.serverHandoffStream ? /* @__PURE__ */ React.createElement(React.Suspense, null, /* @__PURE__ */ React.createElement(
StreamTransfer,
{
context,
identifier: 0,
reader: context.serverHandoffStream.getReader(),
textDecoder: new TextDecoder(),
nonce
}
)) : null);
}
// lib/dom/ssr/routes-test-stub.tsx
import * as React2 from "react";
function createRoutesStub(routes, _context) {
return function RoutesTestStub({
initialEntries,
initialIndex,
hydrationData,
future
}) {
let routerRef = React2.useRef();
let frameworkContextRef = React2.useRef();
if (routerRef.current == null) {
frameworkContextRef.current = {
future: {
v8_passThroughRequests: future?.v8_passThroughRequests === true,
v8_middleware: future?.v8_middleware === true,
v8_trailingSlashAwareDataRequests: future?.v8_trailingSlashAwareDataRequests === true
},
manifest: {
routes: {},
entry: { imports: [], module: "" },
url: "",
version: ""
},
routeModules: {},
ssr: false,
isSpaMode: false,
routeDiscovery: { mode: "lazy", manifestPath: "/__manifest" }
};
let patched = processRoutes(
// @ts-expect-error `StubRouteObject` is stricter about `loader`/`action`
// types compared to `RouteObject`
convertRoutesToDataRoutes(routes, (r) => r),
_context !== void 0 ? _context : future?.v8_middleware ? new RouterContextProvider() : {},
frameworkContextRef.current.manifest,
frameworkContextRef.current.routeModules
);
routerRef.current = createMemoryRouter(patched, {
initialEntries,
initialIndex,
hydrationData
});
}
return /* @__PURE__ */ React2.createElement(FrameworkContext.Provider, { value: frameworkContextRef.current }, /* @__PURE__ */ React2.createElement(RouterProvider, { router: routerRef.current }));
};
}
function processRoutes(routes, context, manifest, routeModules, parentId) {
return routes.map((route) => {
if (!route.id) {
throw new Error(
"Expected a route.id in react-router processRoutes() function"
);
}
let newRoute = {
id: route.id,
path: route.path,
index: route.index,
Component: route.Component ? withComponentProps(route.Component) : void 0,
HydrateFallback: route.HydrateFallback ? withHydrateFallbackProps(route.HydrateFallback) : void 0,
ErrorBoundary: route.ErrorBoundary ? withErrorBoundaryProps(route.ErrorBoundary) : void 0,
action: route.action ? (args) => route.action({ ...args, context }) : void 0,
loader: route.loader ? (args) => route.loader({ ...args, context }) : void 0,
middleware: route.middleware ? route.middleware.map(
(mw) => (...args) => mw(
{ ...args[0], context },
args[1]
)
) : void 0,
handle: route.handle,
shouldRevalidate: route.shouldRevalidate
};
let entryRoute = {
id: route.id,
path: route.path,
index: route.index,
parentId,
hasAction: route.action != null,
hasLoader: route.loader != null,
// When testing routes, you should be stubbing loader/action/middleware,
// not trying to re-implement the full loader/clientLoader/SSR/hydration
// flow. That is better tested via E2E tests.
hasClientAction: false,
hasClientLoader: false,
hasClientMiddleware: false,
hasErrorBoundary: route.ErrorBoundary != null,
// any need for these?
module: "build/stub-path-to-module.js",
clientActionModule: void 0,
clientLoaderModule: void 0,
clientMiddlewareModule: void 0,
hydrateFallbackModule: void 0
};
manifest.routes[newRoute.id] = entryRoute;
routeModules[route.id] = {
default: newRoute.Component || Outlet,
ErrorBoundary: newRoute.ErrorBoundary || void 0,
handle: route.handle,
links: route.links,
meta: route.meta,
shouldRevalidate: route.shouldRevalidate
};
if (route.children) {
newRoute.children = processRoutes(
route.children,
context,
manifest,
routeModules,
newRoute.id
);
}
return newRoute;
});
}
// lib/server-runtime/cookies.ts
import { parse, serialize } from "cookie";
// lib/server-runtime/crypto.ts
var encoder = /* @__PURE__ */ new TextEncoder();
var sign = async (value, secret) => {
let data2 = encoder.encode(value);
let key = await createKey(secret, ["sign"]);
let signature = await crypto.subtle.sign("HMAC", key, data2);
let hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(
/=+$/,
""
);
return value + "." + hash;
};
var unsign = async (cookie, secret) => {
let index = cookie.lastIndexOf(".");
let value = cookie.slice(0, index);
let hash = cookie.slice(index + 1);
let data2 = encoder.encode(value);
let key = await createKey(secret, ["verify"]);
try {
let signature = byteStringToUint8Array(atob(hash));
let valid = await crypto.subtle.verify("HMAC", key, signature, data2);
return valid ? value : false;
} catch (e) {
return false;
}
};
var createKey = async (secret, usages) => crypto.subtle.importKey(
"raw",
encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
usages
);
function byteStringToUint8Array(byteString) {
let array = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
array[i] = byteString.charCodeAt(i);
}
return array;
}
// lib/server-runtime/cookies.ts
var createCookie = (name, cookieOptions = {}) => {
let { secrets = [], ...options } = {
path: "/",
sameSite: "lax",
...cookieOptions
};
warnOnceAboutExpiresCookie(name, options.expires);
return {
get name() {
return name;
},
get isSigned() {
return secrets.length > 0;
},
get expires() {
return typeof options.maxAge !== "undefined" ? new Date(Date.now() + options.maxAge * 1e3) : options.expires;
},
async parse(cookieHeader, parseOptions) {
if (!cookieHeader) return null;
let cookies = parse(cookieHeader, { ...options, ...parseOptions });
if (name in cookies) {
let value = cookies[name];
if (typeof value === "string" && value !== "") {
let decoded = await decodeCookieValue(value, secrets);
return decoded;
} else {
return "";
}
} else {
return null;
}
},
async serialize(value, serializeOptions) {
return serialize(
name,
value === "" ? "" : await encodeCookieValue(value, secrets),
{
...options,
...serializeOptions
}
);
}
};
};
var isCookie = (object) => {
return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
};
async function encodeCookieValue(value, secrets) {
let encoded = encodeData(value);
if (secrets.length > 0) {
encoded = await sign(encoded, secrets[0]);
}
return encoded;
}
async function decodeCookieValue(value, secrets) {
if (secrets.length > 0) {
for (let secret of secrets) {
let unsignedValue = await unsign(value, secret);
if (unsignedValue !== false) {
return decodeData(unsignedValue);
}
}
return null;
}
return decodeData(value);
}
function encodeData(value) {
return btoa(myUnescape(encodeURIComponent(JSON.stringify(value))));
}
function decodeData(value) {
try {
return JSON.parse(decodeURIComponent(myEscape(atob(value))));
} catch (e) {
return {};
}
}
function myEscape(value) {
let str = value.toString();
let result = "";
let index = 0;
let chr, code;
while (index < str.length) {
chr = str.charAt(index++);
if (/[\w*+\-./@]/.exec(chr)) {
result += chr;
} else {
code = chr.charCodeAt(0);
if (code < 256) {
result += "%" + hex(code, 2);
} else {
result += "%u" + hex(code, 4).toUpperCase();
}
}
}
return result;
}
function hex(code, length) {
let result = code.toString(16);
while (result.length < length) result = "0" + result;
return result;
}
function myUnescape(value) {
let str = value.toString();
let result = "";
let index = 0;
let chr, part;
while (index < str.length) {
chr = str.charAt(index++);
if (chr === "%") {
if (str.charAt(index) === "u") {
part = str.slice(index + 1, index + 5);
if (/^[\da-f]{4}$/i.exec(part)) {
result += String.fromCharCode(parseInt(part, 16));
index += 5;
continue;
}
} else {
part = str.slice(index, index + 2);
if (/^[\da-f]{2}$/i.exec(part)) {
result += String.fromCharCode(parseInt(part, 16));
index += 2;
continue;
}
}
}
result += chr;
}
return result;
}
function warnOnceAboutExpiresCookie(name, expires) {
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.`
);
}
// lib/server-runtime/entry.ts
function createEntryRouteModules(manifest) {
return Object.keys(manifest).reduce((memo, routeId) => {
let route = manifest[routeId];
if (route) {
memo[routeId] = route.module;
}
return memo;
}, {});
}
// lib/server-runtime/mode.ts
var ServerMode = /* @__PURE__ */ ((ServerMode2) => {
ServerMode2["Development"] = "development";
ServerMode2["Production"] = "production";
ServerMode2["Test"] = "test";
return ServerMode2;
})(ServerMode || {});
function isServerMode(value) {
return value === "development" /* Development */ || value === "production" /* Production */ || value === "test" /* Test */;
}
// lib/server-runtime/errors.ts
function sanitizeError(error, serverMode) {
if (error instanceof Error && serverMode !== "development" /* Development */) {
let sanitized = new Error("Unexpected Server Error");
sanitized.stack = void 0;
return sanitized;
}
return error;
}
function sanitizeErrors(errors, serverMode) {
return Object.entries(errors).reduce((acc, [routeId, error]) => {
return Object.assign(acc, { [routeId]: sanitizeError(error, serverMode) });
}, {});
}
function serializeError(error, serverMode) {
let sanitized = sanitizeError(error, serverMode);
return {
message: sanitized.message,
stack: sanitized.stack
};
}
// lib/server-runtime/invariant.ts
function invariant(value, message) {
if (value === false || value === null || typeof value === "undefined") {
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"
);
throw new Error(message);
}
}
// lib/server-runtime/routeMatching.ts
function matchServerRoutes(manifest, dataRoutes, branches, pathname, basename) {
let matches = matchRoutesImpl(
dataRoutes,
pathname,
basename ?? "/",
false,
branches
);
if (!matches) return null;
return matches.map((match) => {
let route = manifest[match.route.id];
invariant(
route,
`Route with id "${match.route.id}" not found in manifest.`
);
return {
params: match.params,
pathname: match.pathname,
route
};
});
}
// lib/server-runtime/data.ts
async function callRouteHandler(handler, args, future) {
let result = await handler({
request: future.v8_passThroughRequests ? args.request : stripRoutesParam(stripIndexParam(args.request)),
url: args.url,
params: args.params,
context: args.context,
pattern: args.pattern
});
if (isDataWithResponseInit(result) && result.init && result.init.status && isRedirectStatusCode(result.init.status)) {
throw new Response(null, result.init);
}
return result;
}
function stripIndexParam(request) {
let url = new URL(request.url);
let indexValues = url.searchParams.getAll("index");
url.searchParams.delete("index");
let indexValuesToKeep = [];
for (let indexValue of indexValues) {
if (indexValue) {
indexValuesToKeep.push(indexValue);
}
}
for (let toKeep of indexValuesToKeep) {
url.searchParams.append("index", toKeep);
}
let init = {
method: request.method,
body: request.body,
headers: request.headers,
signal: request.signal
};
if (init.body) {
init.duplex = "half";
}
return new Request(url.href, init);
}
function stripRoutesParam(request) {
let url = new URL(request.url);
url.searchParams.delete("_routes");
let init = {
method: request.method,
body: request.body,
headers: request.headers,
signal: request.signal
};
if (init.body) {
init.duplex = "half";
}
return new Request(url.href, init);
}
// lib/server-runtime/dev.ts
var globalDevServerHooksKey = "__reactRouterDevServerHooks";
function setDevServerHooks(devServerHooks) {
globalThis[globalDevServerHooksKey] = devServerHooks;
}
function getDevServerHooks() {
return globalThis[globalDevServerHooksKey];
}
function getBuildTimeHeader(request, headerName) {
if (typeof process !== "undefined") {
try {
if (process.env.hasOwnProperty("IS_RR_BUILD_REQUEST") && process.env.IS_RR_BUILD_REQUEST === "yes") {
return request.headers.get(headerName);
}
} catch (e) {
}
}
return null;
}
// lib/server-runtime/routes.ts
function groupRoutesByParentId(manifest) {
let routes = {};
Object.values(manifest).forEach((route) => {
if (route) {
let parentId = route.parentId || "";
if (!routes[parentId]) {
routes[parentId] = [];
}
routes[parentId].push(route);
}
});
return routes;
}
function createStaticHandlerDataRoutes(manifest, future, parentId = "", routesByParentId = groupRoutesByParentId(manifest)) {
return (routesByParentId[parentId] || []).map((route) => {
let commonRoute = {
// Always include root due to default boundaries
hasErrorBoundary: route.id === "root" || route.module.ErrorBoundary != null,
id: route.id,
path: route.path,
middleware: route.module.middleware,
// Need to use RR's version in the param typed here to permit the optional
// context even though we know it'll always be provided in remix
loader: route.module.loader ? async (args) => {
let preRenderedData = getBuildTimeHeader(
args.request,
"X-React-Router-Prerender-Data"
);
if (preRenderedData != null) {
let encoded = preRenderedData ? decodeURI(preRenderedData) : preRenderedData;
invariant(encoded, "Missing prerendered data for route");
let uint8array = new TextEncoder().encode(encoded);
let stream = new ReadableStream({
start(controller) {
controller.enqueue(uint8array);
controller.close();
}
});
let decoded = await decodeViaTurboStream(stream, global);
let data2 = decoded.value;
if (data2 && SingleFetchRedirectSymbol in data2) {
let result = data2[SingleFetchRedirectSymbol];
let init = { status: result.status };
if (result.reload) {
throw redirectDocument(result.redirect, init);
} else if (result.replace) {
throw replace(result.redirect, init);
} else {
throw redirect(result.redirect, init);
}
} else {
invariant(
data2 && route.id in data2,
"Unable to decode prerendered data"
);
let result = data2[route.id];
invariant(
"data" in result,
"Unable to process prerendered data"
);
return result.data;
}
}
let val = await callRouteHandler(
route.module.loader,
args,
future
);
return val;
} : void 0,
action: route.module.action ? (args) => callRouteHandler(route.module.action, args, future) : void 0,
handle: route.module.handle
};
return route.index ? {
index: true,
...commonRoute
} : {
caseSensitive: route.caseSensitive,
children: createStaticHandlerDataRoutes(
manifest,
future,
route.id,
routesByParentId
),
...commonRoute
};
});
}
// lib/server-runtime/serverHandoff.ts
function createServerHandoffString(serverHandoff) {
return escapeHtml(JSON.stringify(serverHandoff));
}
// lib/server-runtime/headers.ts
import { splitCookiesString } from "set-cookie-parser";
function getDocumentHeaders(context, build) {
return getDocumentHeadersImpl(context, (m) => {
let route = build.routes[m.route.id];
invariant(route, `Route with id "${m.route.id}" not found in build`);
return route.module.headers;
});
}
function getDocumentHeadersImpl(context, getRouteHeadersFn, _defaultHeaders) {
let boundaryIdx = context.errors ? context.matches.findIndex((m) => context.errors[m.route.id]) : -1;
let matches = boundaryIdx >= 0 ? context.matches.slice(0, boundaryIdx + 1) : context.matches;
let errorHeaders;
if (boundaryIdx >= 0) {
let { actionHeaders, actionData, loaderHeaders, loaderData } = context;
context.matches.slice(boundaryIdx).some((match) => {
let id = match.route.id;
if (actionHeaders[id] && (!actionData || !actionData.hasOwnProperty(id))) {
errorHeaders = actionHeaders[id];
} else if (loaderHeaders[id] && !loaderData.hasOwnProperty(id)) {
errorHeaders = loaderHeaders[id];
}
return errorHeaders != null;
});
}
const defaultHeaders = new Headers(_defaultHeaders);
return matches.reduce((parentHeaders, match, idx) => {
let { id } = match.route;
let loaderHeaders = context.loaderHeaders[id] || new Headers();
let actionHeaders = context.actionHeaders[id] || new Headers();
let includeErrorHeaders = errorHeaders != null && idx === matches.length - 1;
let includeErrorCookies = includeErrorHeaders && errorHeaders !== loaderHeaders && errorHeaders !== actionHeaders;
let headersFn = getRouteHeadersFn(match);
if (headersFn == null) {
let headers2 = new Headers(parentHeaders);
if (includeErrorCookies) {
prependCookies(errorHeaders, headers2);
}
prependCookies(actionHeaders, headers2);
prependCookies(loaderHeaders, headers2);
return headers2;
}
let headers = new Headers(
typeof headersFn === "function" ? headersFn({
loaderHeaders,
parentHeaders,
actionHeaders,
errorHeaders: includeErrorHeaders ? errorHeaders : void 0
}) : headersFn
);
if (includeErrorCookies) {
prependCookies(errorHeaders, headers);
}
prependCookies(actionHeaders, headers);
prependCookies(loaderHeaders, headers);
prependCookies(parentHeaders, headers);
return headers;
}, new Headers(defaultHeaders));
}
function prependCookies(parentHeaders, childHeaders) {
let parentSetCookieString = parentHeaders.get("Set-Cookie");
if (parentSetCookieString) {
let cookies = splitCookiesString(parentSetCookieString);
let childCookies = new Set(childHeaders.getSetCookie());
cookies.forEach((cookie) => {
if (!childCookies.has(cookie)) {
childHeaders.append("Set-Cookie", cookie);
}
});
}
}
// lib/actions.ts
function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
let originHeader = request.headers.get("origin");
let originDomain = null;
try {
originDomain = typeof originHeader === "string" && originHeader !== "null" ? new URL(originHeader).host : originHeader;
} catch {
throw new Error(
`\`origin\` header is not a valid URL. Aborting the action.`
);
}
let host = new URL(request.url).host;
if (originDomain && originDomain !== host) {
if (!isAllowedOrigin(originDomain, allowedActionOrigins)) {
throw new Error(
"The `request.url` host does not match `origin` header from a forwarded action request. Aborting the action."
);
}
}
}
function matchWildcardDomain(domain, pattern) {
const domainParts = domain.split(".");
const patternParts = pattern.split(".");
if (patternParts.length < 1) {
return false;
}
if (domainParts.length < patternParts.length) {
return false;
}
while (patternParts.length) {
const patternPart = patternParts.pop();
const domainPart = domainParts.pop();
switch (patternPart) {
case "": {
return false;
}
case "*": {
if (domainPart) {
continue;
} else {
return false;
}
}
case "**": {
if (patternParts.length > 0) {
return false;
}
return domainPart !== void 0;
}
case void 0:
default: {
if (domainPart !== patternPart) {
return false;
}
}
}
}
return domainParts.length === 0;
}
function isAllowedOrigin(originDomain, allowedActionOrigins = []) {
return allowedActionOrigins.some(
(allowedOrigin) => allowedOrigin && (allowedOrigin === originDomain || matchWildcardDomain(originDomain, allowedOrigin))
);
}
// lib/server-runtime/urls.ts
function getNormalizedPath(request, basename, future) {
basename = basename || "/";
let url = new URL(request.url);
let pathname = url.pathname;
if (future?.v8_trailingSlashAwareDataRequests) {
if (pathname.endsWith("/_.data")) {
pathname = pathname.replace(/_\.data$/, "");
} else {
pathname = pathname.replace(/\.data$/, "");
}
} else {
if (stripBasename(pathname, basename) === "/_root.data") {
pathname = basename;
} else if (pathname.endsWith(".data")) {
pathname = pathname.replace(/\.data$/, "");
}
if (stripBasename(pathname, basename) !== "/" && pathname.endsWith("/")) {
pathname = pathname.slice(0, -1);
}
}
let searchParams = new URLSearchParams(url.search);
searchParams.delete("_routes");
let search = searchParams.toString();
if (search) {
search = `?${search}`;
}
return {
pathname,
search,
// No hashes on the server
hash: ""
};
}
// lib/server-runtime/single-fetch.ts
var SERVER_NO_BODY_STATUS_CODES = /* @__PURE__ */ new Set([
...NO_BODY_STATUS_CODES,
304
]);
async function singleFetchAction(build, serverMode, staticHandler, request, handlerUrl, loadContext, handleError) {
try {
try {
throwIfPotentialCSRFAttack(
request,
Array.isArray(build.allowedActionOrigins) ? build.allowedActionOrigins : []
);
} catch (e) {
return handleQueryError(new Error("Bad Request"), 400);
}
let handlerRequest = build.future.v8_passThroughRequests ? request : new Request(handlerUrl, {
method: request.method,
body: request.body,
headers: request.headers,
signal: request.signal,
...request.body ? { duplex: "half" } : void 0
});
let result = await staticHandler.query(handlerRequest, {
requestContext: loadContext,
skipLoaderErrorBubbling: true,
skipRevalidation: true,
generateMiddlewareResponse: build.future.v8_middleware ? async (query) => {
try {
let innerResult = await query(handlerRequest);
return handleQueryResult(innerResult);
} catch (error) {
return handleQueryError(error);
}
} : void 0,
normalizePath: (r) => getNormalizedPath(r, build.basename, build.future)
});
return handleQueryResult(result);
} catch (error) {
return handleQueryError(error);
}
function handleQueryResult(result) {
return isResponse(result) ? result : staticContextToResponse(result);
}
function handleQueryError(error, status = 500) {
handleError(error);
return generateSingleFetchResponse(request, build, serverMode, {
result: { error },
headers: new Headers(),
status
});
}
function staticContextToResponse(context) {
let headers = getDocumentHeaders(context, build);
if (isRedirectStatusCode(context.statusCode) && headers.has("Location")) {
return new Response(null, { status: context.statusCode, headers });
}
if (context.errors) {
Object.values(context.errors).forEach((err) => {
if (!isRouteErrorResponse(err) || err.error) {
handleError(err);
}
});
context.errors = sanitizeErrors(context.errors, serverMode);
}
let singleFetchResult;
if (context.errors) {
singleFetchResult = { error: Object.values(context.errors)[0] };
} else {
singleFetchResult = {
data: Object.values(context.actionData || {})[0]
};
}
return generateSingleFetchResponse(request, build, serverMode, {
result: singleFetchResult,
headers,
status: context.statusCode
});
}
}
async function singleFetchLoaders(build, serverMode, staticHandler, request, handlerUrl, loadContext, handleError) {
let routesParam = new URL(request.url).searchParams.get("_routes");
let loadRouteIds = routesParam ? new Set(routesParam.split(",")) : null;
try {
let handlerRequest = build.future.v8_passThroughRequests ? request : new Request(handlerUrl, {
headers: request.headers,
signal: request.signal
});
let result = await staticHandler.query(handlerRequest, {
requestContext: loadContext,
filterMatchesToLoad: (m) => !loadRouteIds || loadRouteIds.has(m.route.id),
skipLoaderErrorBubbling: true,
generateMiddlewareResponse: build.future.v8_middleware ? async (query) => {
try {
let innerResult = await query(handlerRequest);
return handleQueryResult(innerResult);
} catch (error) {
return handleQueryError(error);
}
} : void 0,
normalizePath: (r) => getNormalizedPath(r, build.basename, build.future)
});
return handleQueryResult(result);
} catch (error) {
return handleQueryError(error);
}
function handleQueryResult(result) {
return isResponse(result) ? result : staticContextToResponse(result);
}
function handleQueryError(error) {
handleError(error);
return generateSingleFetchResponse(request, build, serverMode, {
result: { error },
headers: new Headers(),
status: 500
});
}
function staticContextToResponse(context) {
let headers = getDocumentHeaders(context, build);
if (isRedirectStatusCode(context.statusCode) && headers.has("Location")) {
return new Response(null, { status: context.statusCode, headers });
}
if (context.errors) {
Object.values(context.errors).forEach((err) => {
if (!isRouteErrorResponse(err) || err.error) {
handleError(err);
}
});
context.errors = sanitizeErrors(context.errors, serverMode);
}
let results = {};
let loadedMatches = new Set(
context.matches.filter(
(m) => loadRouteIds ? loadRouteIds.has(m.route.id) : m.route.loader != null
).map((m) => m.route.id)
);
if (context.errors) {
for (let [id, error] of Object.entries(context.errors)) {
results[id] = { error };
}
}
for (let [id, data2] of Object.entries(context.loaderData)) {
if (!(id in results) && loadedMatches.has(id)) {
results[id] = { data: data2 };
}
}
return generateSingleFetchResponse(request, build, serverMode, {
result: results,
headers,
status: context.statusCode
});
}
}
function generateSingleFetchResponse(request, build, serverMode, {
result,
headers,
status
}) {
let resultHeaders = new Headers(headers);
resultHeaders.set("X-Remix-Response", "yes");
if (SERVER_NO_BODY_STATUS_CODES.has(status)) {
return new Response(null, { status, headers: resultHeaders });
}
resultHeaders.set("Content-Type", "text/x-script");
resultHeaders.delete("Content-Length");
return new Response(
encodeViaTurboStream(
result,
request.signal,
build.entry.module.streamTimeout,
serverMode
),
{
status: status || 200,
headers: resultHeaders
}
);
}
function generateSingleFetchRedirectResponse(redirectResponse, request, build, serverMode) {
let redirect2 = getSingleFetchRedirect(
redirectResponse.status,
redirectResponse.headers,
build.basename
);
let headers = new Headers(redirectResponse.headers);
headers.delete("Location");
headers.set("Content-Type", "text/x-script");
return generateSingleFetchResponse(request, build, serverMode, {
result: request.method === "GET" ? { [SingleFetchRedirectSymbol]: redirect2 } : redirect2,
headers,
status: SINGLE_FETCH_REDIRECT_STATUS
});
}
function getSingleFetchRedirect(status, headers, basename) {
let redirect2 = headers.get("Location");
if (basename) {
redirect2 = stripBasename(redirect2, basename) || redirect2;
}
return {
redirect: redirect2,
status,
revalidate: (
// Technically X-Remix-Revalidate isn't needed here - that was an implementation
// detail of ?_data requests as our way to tell the front end to revalidate when
// we didn't have a response body to include that information in.
// With single fetch, we tell the front end via this revalidate boolean field.
// However, we're respecting it for now because it may be something folks have
// used in their own responses
// TODO(v3): Consider removing or making this official public API
headers.has("X-Remix-Revalidate") || headers.has("Set-Cookie")
),
reload: headers.has("X-Remix-Reload-Document"),
replace: headers.has("X-Remix-Replace")
};
}
function encodeViaTurboStream(data2, requestSignal, streamTimeout, serverMode) {
let controller = new AbortController();
let timeoutId = setTimeout(
() => {
controller.abort(new Error("Server Timeout"));
cleanupCallbacks();
},
typeof streamTimeout === "number" ? streamTimeout : 4950
);
let abortControllerOnRequestAbort = () => {
controller.abort(requestSignal.reason);
cleanupCallbacks();
};
requestSignal.addEventListener("abort", abortControllerOnRequestAbort);
let cleanupCallbacks = () => {
clearTimeout(timeoutId);
requestSignal.removeEventListener("abort", abortControllerOnRequestAbort);
};
return encode(data2, {
signal: controller.signal,
onComplete: cleanupCallbacks,
plugins: [
(value) => {
if (value instanceof Error) {
let { name, message, stack } = serverMode === "production" /* Production */ ? sanitizeError(value, serverMode) : value;
return ["SanitizedError", name, message, stack];
}
if (value instanceof ErrorResponseImpl) {
let { data: data3, status, statusText } = value;
return ["ErrorResponse", data3, status, statusText];
}
if (value && typeof value === "object" && SingleFetchRedirectSymbol in value) {
return ["SingleFetchRedirect", value[SingleFetchRedirectSymbol]];
}
}
],
postPlugins: [
(value) => {
if (!value) return;
if (typeof value !== "object") return;
return [
"SingleFetchClassInstance",
Object.fromEntries(Object.entries(value))
];
},
() => ["SingleFetchFallback"]
]
});
}
// lib/server-runtime/server.ts
function derive(build, mode) {
let dataRoutes = createStaticHandlerDataRoutes(build.routes, build.future);
let serverMode = isServerMode(mode) ? mode : "production" /* Production */;
let staticHandler = createStaticHandler(dataRoutes, {
basename: build.basename,
instrumentations: build.entry.module.instrumentations,
future: build.future
});
let errorHandler = build.entry.module.handleError || ((error, { request }) => {
if (serverMode !== "test" /* Test */ && !request.signal.aborted) {
console.error(
// @ts-expect-error This is "private" from users but intended for internal use
isRouteErrorResponse(error) && error.error ? error.error : error
);
}
});
let requestHandler = async (request, initialContext) => {
let params = {};
let loadContext;
let handleError = (error) => {
if (mode === "development" /* Development */) {
getDevServerHooks()?.processRequestError?.(error);
}
errorHandler(error, {
context: loadContext,
params,
request
});
};
if (build.future.v8_middleware) {
if (initialContext && !(initialContext instanceof RouterContextProvider)) {
let error = new Error(
"Invalid `context` value provided to `handleRequest`. When middleware is enabled you must return an instance of `RouterContextProvider` from your `getLoadContext` function."
);
handleError(error);
return returnLastResortErrorResponse(error, serverMode);
}
loadContext = initialContext || new RouterContextProvider();
} else {
loadContext = initialContext || {};
}
let requestUrl = new URL(request.url);
let normalizedPathname = getNormalizedPath(
request,
build.basename,
build.future
).pathname;
let isSpaMode = getBuildTimeHeader(request, "X-React-Router-SPA-Mode") === "yes";
if (!build.ssr) {
let decodedPath = decodeURI(normalizedPathname);
if (build.basename && build.basename !== "/") {
let strippedPath = stripBasename(decodedPath, build.basename);
if (strippedPath == null) {
errorHandler(
new ErrorResponseImpl(
404,
"Not Found",
`Refusing to prerender the \`${decodedPath}\` path because it does not start with the basename \`${build.basename}\``
),
{
context: loadContext,
params,
request
}
);
return new Response("Not Found", {
status: 404,
statusText: "Not Found"
});
}
decodedPath = strippedPath;
}
if (build.prerender.length === 0) {
isSpaMode = true;
} else if (!build.prerender.includes(decodedPath.replace(/\/$/, "")) && !build.prerender.includes(decodedPath.replace(/[^/]$/, "/"))) {
if (requestUrl.pathname.endsWith(".data")) {
errorHandler(
new ErrorResponseImpl(
404,
"Not Found",
`Refusing to SSR the path \`${decodedPath}\` because \`ssr:false\` is set and the path is not included in the \`prerender\` config, so in production the path will be a 404.`
),
{
context: loadContext,
params,
request
}
);
return new Response("Not Found", {
status: 404,
statusText: "Not Found"
});
} else {
isSpaMode = true;
}
}
}
let manifestUrl = getManifestPath(
build.routeDiscovery.manifestPath,
build.basename
);
if (build.routeDiscovery.mode === "lazy" && requestUrl.pathname === manifestUrl) {
try {
let res = await handleManifestRequest(
build,
staticHandler.dataRoutes,
staticHandler._internalRouteBranches,
requestUrl
);
return res;
} catch (e) {
handleError(e);
return new Response("Unknown Server Error", { status: 500 });
}
}
let matches = matchServerRoutes(
build.routes,
staticHandler.dataRoutes,
staticHandler._internalRouteBranches,
normalizedPathname,
build.basename
);
if (matches && matches.length > 0) {
Object.assign(params, matches[0].params);
}
let response;
if (requestUrl.pathname.endsWith(".data")) {
response = await handleSingleFetchRequest(
serverMode,
build,
staticHandler,
request,
normalizedPathname,
loadContext,
handleError
);
if (isRedirectResponse(response)) {
response = generateSingleFetchRedirectResponse(
response,
request,
build,
serverMode
);
}
if (build.entry.module.handleDataRequest) {
response = await build.entry.module.handleDataRequest(response, {
context: loadContext,
params: matches ? matches[0].params : {},
request
});
if (isRedirectResponse(response)) {
response = generateSingleFetchRedirectResponse(
response,
request,
build,
serverMode
);
}
}
} else if (!isSpaMode && matches && matches[matches.length - 1].route.module.default == null && matches[matches.length - 1].route.module.ErrorBoundary == null) {
response = await handleResourceRequest(
serverMode,
build,
staticHandler,
matches.slice(-1)[0].route.id,
request,
loadContext,
handleError
);
} else {
let { pathname } = requestUrl;
let criticalCss = void 0;
if (build.unstable_getCriticalCss) {
criticalCss = await build.unstable_getCriticalCss({ pathname });
} else if (mode === "development" /* Development */ && getDevServerHooks()?.getCriticalCss) {
criticalCss = await getDevServerHooks()?.getCriticalCss?.(pathname);
}
response = await handleDocumentRequest(
serverMode,
build,
staticHandler,
request,
loadContext,
handleError,
isSpaMode,
criticalCss
);
}
if (request.method === "HEAD") {
return new Response(null, {
headers: response.headers,
status: response.status,
statusText: response.statusText
});
}
return response;
};
if (build.entry.module.instrumentations) {
requestHandler = instrumentHandler(
requestHandler,
build.entry.module.instrumentations.map((i) => i.handler).filter(Boolean)
);
}
return {
serverMode,
staticHandler,
errorHandler,
requestHandler
};
}
var createRequestHandler = (build, mode) => {
let _build;
let serverMode;
let staticHandler;
let errorHandler;
let _requestHandler;
return async function requestHandler(request, initialContext) {
_build = typeof build === "function" ? await build() : build;
if (typeof build === "function") {
let derived = derive(_build, mode);
serverMode = derived.serverMode;
staticHandler = derived.staticHandler;
errorHandler = derived.errorHandler;
_requestHandler = derived.requestHandler;
} else if (!serverMode || !staticHandler || !errorHandler || !_requestHandler) {
let derived = derive(_build, mode);
serverMode = derived.serverMode;
staticHandler = derived.staticHandler;
errorHandler = derived.errorHandler;
_requestHandler = derived.requestHandler;
}
return _requestHandler(request, initialContext);
};
};
async function handleManifestRequest(build, dataRoutes, branches, url) {
if (url.toString().length > URL_LIMIT) {
return new Response(null, {
statusText: "Bad Request",
status: 400
});
}
if (build.assets.version !== url.searchParams.get("version")) {
return new Response(null, {
status: 204,
headers: {
"X-Remix-Reload-Document": "true"
}
});
}
let patches = {};
if (url.searchParams.has("paths")) {
let pathParam = url.searchParams.get("paths") || "";
let paths = new Set(pathParam.split(",").filter(Boolean));
for (let path of paths) {
if (!path.startsWith("/")) {
path = `/${path}`;
}
let matches = matchServerRoutes(
build.routes,
dataRoutes,
branches,
path,
build.basename
);
if (matches) {
for (let match of matches) {
let routeId = match.route.id;
let route = build.assets.routes[routeId];
if (route) {
patches[routeId] = route;
}
}
}
}
return Response.json(patches, {
headers: {
"Cache-Control": "public, max-age=31536000, immutable"
}
});
}
return new Response("Invalid Request", { status: 400 });
}
async function handleSingleFetchRequest(serverMode, build, staticHandler, request, normalizedPath, loadContext, handleError) {
let handlerUrl = new URL(request.url);
handlerUrl.pathname = normalizedPath;
let response = isMutationMethod(request.method) ? await singleFetchAction(
build,
serverMode,
staticHandler,
request,
handlerUrl,
loadContext,
handleError
) : await singleFetchLoaders(
build,
serverMode,
staticHandler,
request,
handlerUrl,
loadContext,
handleError
);
return response;
}
async function handleDocumentRequest(serverMode, build, staticHandler, request, loadContext, handleError, isSpaMode, criticalCss) {
try {
if (isMutationMethod(request.method)) {
try {
throwIfPotentialCSRFAttack(
request,
Array.isArray(build.allowedActionOrigins) ? build.allowedActionOrigins : []
);
} catch (e) {
handleError(e);
return new Response("Bad Request", { status: 400 });
}
}
let result = await staticHandler.query(request, {
requestContext: loadContext,
generateMiddlewareResponse: build.future.v8_middleware ? async (query) => {
try {
let innerResult = await query(request);
if (!isResponse(innerResult)) {
innerResult = await renderHtml(innerResult, isSpaMode);
}
return innerResult;
} catch (error) {
handleError(error);
return new Response(null, { status: 500 });
}
} : void 0,
normalizePath: (r) => getNormalizedPath(r, build.basename, build.future)
});
if (!isResponse(result)) {
result = await renderHtml(result, isSpaMode);
}
return result;
} catch (error) {
handleError(error);
return new Response(null, { status: 500 });
}
async function renderHtml(context, isSpaMode2) {
let headers = getDocumentHeaders(context, build);
if (SERVER_NO_BODY_STATUS_CODES.has(context.statusCode)) {
return new Response(null, { status: context.statusCode, headers });
}
if (context.errors) {
Object.values(context.errors).forEach((err) => {
if (!isRouteErrorResponse(err) || err.error) {
handleError(err);
}
});
context.errors = sanitizeErrors(context.errors, serverMode);
}
let state = {
loaderData: context.loaderData,
actionData: context.actionData,
errors: context.errors
};
let baseServerHandoff = {
basename: build.basename,
future: build.future,
routeDiscovery: build.routeDiscovery,
ssr: build.ssr,
isSpaMode: isSpaMode2
};
let entryContext = {
manifest: build.assets,
branches: staticHandler._internalRouteBranches,
routeModules: createEntryRouteModules(build.routes),
staticHandlerContext: context,
criticalCss,
serverHandoffString: createServerHandoffString({
...baseServerHandoff,
criticalCss
}),
serverHandoffStream: encodeViaTurboStream(
state,
request.signal,
build.entry.module.streamTimeout,
serverMode
),
renderMeta: {},
future: build.future,
ssr: build.ssr,
routeDiscovery: build.routeDiscovery,
isSpaMode: isSpaMode2,
serializeError: (err) => serializeError(err, serverMode)
};
let handleDocumentRequestFunction = build.entry.module.default;
try {
return await handleDocumentRequestFunction(
request,
context.statusCode,
headers,
entryContext,
loadContext
);
} catch (error) {
handleError(error);
let errorForSecondRender = error;
if (isResponse(error)) {
try {
let data2 = await unwrapResponse(error);
errorForSecondRender = new ErrorResponseImpl(
error.status,
error.statusText,
data2
);
} catch (e) {
}
}
context = getStaticContextFromError(
staticHandler.dataRoutes,
context,
errorForSecondRender
);
if (context.errors) {
context.errors = sanitizeErrors(context.errors, serverMode);
}
let state2 = {
loaderData: context.loaderData,
actionData: context.actionData,
errors: context.errors
};
entryContext = {
...entryContext,
staticHandlerContext: context,
serverHandoffString: createServerHandoffString(baseServerHandoff),
serverHandoffStream: encodeViaTurboStream(
state2,
request.signal,
build.entry.module.streamTimeout,
serverMode
),
renderMeta: {}
};
try {
return await handleDocumentRequestFunction(
request,
context.statusCode,
headers,
entryContext,
loadContext
);
} catch (error2) {
handleError(error2);
return returnLastResortErrorResponse(error2, serverMode);
}
}
}
}
async function handleResourceRequest(serverMode, build, staticHandler, routeId, request, loadContext, handleError) {
try {
let result = await staticHandler.queryRoute(request, {
routeId,
requestContext: loadContext,
generateMiddlewareResponse: build.future.v8_middleware ? async (queryRoute) => {
try {
let innerResult = await queryRoute(request);
return handleQueryRouteResult(innerResult);
} catch (error) {
return handleQueryRouteError(error);
}
} : void 0,
normalizePath: (r) => getNormalizedPath(r, build.basename, build.future)
});
return handleQueryRouteResult(result);
} catch (error) {
return handleQueryRouteError(error);
}
function handleQueryRouteResult(result) {
if (isResponse(result)) {
return result;
}
if (typeof result === "string") {
return new Response(result);
}
return Response.json(result);
}
function handleQueryRouteError(error) {
if (isResponse(error)) {
return error;
}
if (isRouteErrorResponse(error)) {
handleError(error);
return errorResponseToJson(error, serverMode);
}
if (error instanceof Error && error.message === "Expected a response from queryRoute") {
let newError = new Error(
"Expected a Response to be returned from resource route handler"
);
handleError(newError);
return returnLastResortErrorResponse(newError, serverMode);
}
handleError(error);
return returnLastResortErrorResponse(error, serverMode);
}
}
function errorResponseToJson(errorResponse, serverMode) {
return Response.json(
serializeError(
// @ts-expect-error This is "private" from users but intended for internal use
errorResponse.error || new Error("Unexpected Server Error"),
serverMode
),
{
status: errorResponse.status,
statusText: errorResponse.statusText
}
);
}
function returnLastResortErrorResponse(error, serverMode) {
let message = "Unexpected Server Error";
if (serverMode !== "production" /* Production */) {
message += `
${String(error)}`;
}
return new Response(message, {
status: 500,
headers: {
"Content-Type": "text/plain"
}
});
}
function unwrapResponse(response) {
let contentType = response.headers.get("Content-Type");
return contentType && /\bapplication\/json\b/.test(contentType) ? response.body == null ? null : response.json() : response.text();
}
// lib/server-runtime/sessions.ts
function flash(name) {
return `__flash_${name}__`;
}
var createSession = (initialData = {}, id = "") => {
let map = new Map(Object.entries(initialData));
return {
get id() {
return id;
},
get data() {
return Object.fromEntries(map);
},
has(name) {
return map.has(name) || map.has(flash(name));
},
get(name) {
if (map.has(name)) return map.get(name);
let flashName = flash(name);
if (map.has(flashName)) {
let value = map.get(flashName);
map.delete(flashName);
return value;
}
return void 0;
},
set(name, value) {
map.set(name, value);
},
flash(name, value) {
map.set(flash(name), value);
},
unset(name) {
map.delete(name);
}
};
};
var isSession = (object) => {
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";
};
function createSessionStorage({
cookie: cookieArg,
createData,
readData,
updateData,
deleteData
}) {
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
warnOnceAboutSigningSessionCookie(cookie);
return {
async getSession(cookieHeader, options) {
let id = cookieHeader && await cookie.parse(cookieHeader, options);
let data2 = id && await readData(id);
return createSession(data2 || {}, id || "");
},
async commitSession(session, options) {
let { id, data: data2 } = session;
let expires = options?.maxAge != null ? new Date(Date.now() + options.maxAge * 1e3) : options?.expires != null ? options.expires : cookie.expires;
if (id) {
await updateData(id, data2, expires);
} else {
id = await createData(data2, expires);
}
return cookie.serialize(id, options);
},
async destroySession(session, options) {
await deleteData(session.id);
return cookie.serialize("", {
...options,
maxAge: void 0,
expires: /* @__PURE__ */ new Date(0)
});
}
};
}
function warnOnceAboutSigningSessionCookie(cookie) {
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.`
);
}
// lib/server-runtime/sessions/cookieStorage.ts
function createCookieSessionStorage({ cookie: cookieArg } = {}) {
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
warnOnceAboutSigningSessionCookie(cookie);
return {
async getSession(cookieHeader, options) {
return createSession(
cookieHeader && await cookie.parse(cookieHeader, options) || {}
);
},
async commitSession(session, options) {
let serializedCookie = await cookie.serialize(session.data, options);
if (serializedCookie.length > 4096) {
throw new Error(
"Cookie length will exceed browser maximum. Length: " + serializedCookie.length
);
}
return serializedCookie;
},
async destroySession(_session, options) {
return cookie.serialize("", {
...options,
maxAge: void 0,
expires: /* @__PURE__ */ new Date(0)
});
}
};
}
// lib/server-runtime/sessions/memoryStorage.ts
function createMemorySessionStorage({ cookie } = {}) {
let map = /* @__PURE__ */ new Map();
return createSessionStorage({
cookie,
async createData(data2, expires) {
let id = Math.random().toString(36).substring(2, 10);
map.set(id, { data: data2, expires });
return id;
},
async readData(id) {
if (map.has(id)) {
let { data: data2, expires } = map.get(id);
if (!expires || expires > /* @__PURE__ */ new Date()) {
return data2;
}
if (expires) map.delete(id);
}
return null;
},
async updateData(id, data2, expires) {
map.set(id, { data: data2, expires });
},
async deleteData(id) {
map.delete(id);
}
});
}
// lib/href.ts
function href(path, ...args) {
let params = args[0];
let result = trimTrailingSplat(path).replace(
/\/:([\w-]+)(\?)?/g,
// same regex as in .\router\utils.ts: compilePath().
(_, param, questionMark) => {
const isRequired = questionMark === void 0;
const value = params?.[param];
if (isRequired && value === void 0) {
throw new Error(
`Path '${path}' requires param '${param}' but it was not provided`
);
}
return value === void 0 ? "" : "/" + value;
}
);
if (path.endsWith("*")) {
const value = params?.["*"];
if (value !== void 0) {
result += "/" + value;
}
}
return result || "/";
}
function trimTrailingSplat(path) {
let i = path.length - 1;
let char = path[i];
if (char !== "*" && char !== "/") return path;
i--;
for (; i >= 0; i--) {
if (path[i] !== "/") break;
}
return path.slice(0, i + 1);
}
// lib/rsc/server.ssr.tsx
import * as React4 from "react";
// lib/rsc/html-stream/server.ts
var encoder2 = new TextEncoder();
var trailer = "</body></html>";
function injectRSCPayload(rscStream) {
let decoder = new TextDecoder();
let resolveFlightDataPromise;
let flightDataPromise = new Promise(
(resolve) => resolveFlightDataPromise = resolve
);
let startedRSC = false;
let buffered = [];
let timeout = null;
function flushBufferedChunks(controller) {
for (let chunk of buffered) {
let buf = decoder.decode(chunk, { stream: true });
if (buf.endsWith(trailer)) {
buf = buf.slice(0, -trailer.length);
}
controller.enqueue(encoder2.encode(buf));
}
buffered.length = 0;
timeout = null;
}
return new TransformStream({
transform(chunk, controller) {
buffered.push(chunk);
if (timeout) {
return;
}
timeout = setTimeout(async () => {
flushBufferedChunks(controller);
if (!startedRSC) {
startedRSC = true;
writeRSCStream(rscStream, controller).catch((err) => controller.error(err)).then(resolveFlightDataPromise);
}
}, 0);
},
async flush(controller) {
await flightDataPromise;
if (timeout) {
clearTimeout(timeout);
flushBufferedChunks(controller);
}
controller.enqueue(encoder2.encode("</body></html>"));
}
});
}
async function writeRSCStream(rscStream, controller) {
let decoder = new TextDecoder("utf-8", { fatal: true });
const reader = rscStream.getReader();
try {
let read;
while ((read = await reader.read()) && !read.done) {
const chunk = read.value;
try {
writeChunk(
JSON.stringify(decoder.decode(chunk, { stream: true })),
controller
);
} catch (e) {
let base64 = JSON.stringify(btoa(String.fromCodePoint(...chunk)));
writeChunk(
`Uint8Array.from(atob(${base64}), m => m.codePointAt(0))`,
controller
);
}
}
} finally {
reader.releaseLock();
}
let remaining = decoder.decode();
if (remaining.length) {
writeChunk(JSON.stringify(remaining), controller);
}
}
function writeChunk(chunk, controller) {
controller.enqueue(
encoder2.encode(
`<script>${escapeScript(
`(self.__FLIGHT_DATA||=[]).push(${chunk})`
)}</script>`
)
);
}
function escapeScript(script) {
return script.replace(/<!--/g, "<\\!--").replace(/<\/(script)/gi, "</\\$1");
}
// lib/rsc/errorBoundaries.tsx
import React3 from "react";
var RSCRouterGlobalErrorBoundary = class extends React3.Component {
constructor(props) {
super(props);
this.state = { error: null, location: props.location };
}
static getDerivedStateFromError(error) {
return { error };
}
static getDerivedStateFromProps(props, state) {
if (state.location !== props.location) {
return { error: null, location: props.location };
}
return { error: state.error, location: state.location };
}
render() {
if (this.state.error) {
return /* @__PURE__ */ React3.createElement(
RSCDefaultRootErrorBoundaryImpl,
{
error: this.state.error,
renderAppShell: true
}
);
} else {
return this.props.children;
}
}
};
function ErrorWrapper({
renderAppShell,
title,
children
}) {
if (!renderAppShell) {
return children;
}
return /* @__PURE__ */ React3.createElement("html", { lang: "en" }, /* @__PURE__ */ React3.createElement("head", null, /* @__PURE__ */ React3.createElement("meta", { charSet: "utf-8" }), /* @__PURE__ */ React3.createElement(
"meta",
{
name: "viewport",
content: "width=device-width,initial-scale=1,viewport-fit=cover"
}
), /* @__PURE__ */ React3.createElement("title", null, title)), /* @__PURE__ */ React3.createElement("body", null, /* @__PURE__ */ React3.createElement("main", { style: { fontFamily: "system-ui, sans-serif", padding: "2rem" } }, children)));
}
function RSCDefaultRootErrorBoundaryImpl({
error,
renderAppShell
}) {
console.error(error);
let heyDeveloper = /* @__PURE__ */ React3.createElement(
"script",
{
dangerouslySetInnerHTML: {
__html: `
console.log(
"\u{1F4BF} Hey developer \u{1F44B}. You can provide a way better UX than this when your app throws errors. Check out https://reactrouter.com/how-to/error-boundary for more information."
);
`
}
}
);
if (isRouteErrorResponse(error)) {
return /* @__PURE__ */ React3.createElement(
ErrorWrapper,
{
renderAppShell,
title: "Unhandled Thrown Response!"
},
/* @__PURE__ */ React3.createElement("h1", { style: { fontSize: "24px" } }, error.status, " ", error.statusText),
ENABLE_DEV_WARNINGS ? heyDeveloper : null
);
}
let errorInstance;
if (error instanceof Error) {
errorInstance = error;
} else {
let errorString = error == null ? "Unknown Error" : typeof error === "object" && "toString" in error ? error.toString() : JSON.stringify(error);
errorInstance = new Error(errorString);
}
return /* @__PURE__ */ React3.createElement(ErrorWrapper, { renderAppShell, title: "Application Error!" }, /* @__PURE__ */ React3.createElement("h1", { style: { fontSize: "24px" } }, "Application Error"), /* @__PURE__ */ React3.createElement(
"pre",
{
style: {
padding: "2rem",
background: "hsla(10, 50%, 50%, 0.1)",
color: "red",
overflow: "auto"
}
},
errorInstance.stack
), heyDeveloper);
}
function RSCDefaultRootErrorBoundary({
hasRootLayout
}) {
let error = useRouteError();
if (hasRootLayout === void 0) {
throw new Error("Missing 'hasRootLayout' prop");
}
return /* @__PURE__ */ React3.createElement(
RSCDefaultRootErrorBoundaryImpl,
{
renderAppShell: !hasRootLayout,
error
}
);
}
// lib/rsc/route-modules.ts
function createRSCRouteModules(payload) {
const routeModules = {};
for (const match of payload.matches) {
populateRSCRouteModules(routeModules, match);
}
return routeModules;
}
function populateRSCRouteModules(routeModules, matches) {
matches = Array.isArray(matches) ? matches : [matches];
for (const match of matches) {
routeModules[match.id] = {
links: match.links,
meta: match.meta,
default: noopComponent
};
}
}
var noopComponent = () => null;
// lib/rsc/server.ssr.tsx
var defaultManifestPath = "/__manifest";
var REACT_USE = "use";
var useImpl = React4[REACT_USE];
function useSafe(promise) {
if (useImpl) {
return useImpl(promise);
}
throw new Error("React Router v7 requires React 19+ for RSC features.");
}
async function routeRSCServerRequest({
request,
serverResponse,
createFromReadableStream,
renderHTML,
hydrate = true
}) {
const url = new URL(request.url);
const isDataRequest = isReactServerRequest(url);
const respondWithRSCPayload = isDataRequest || isManifestRequest(url) || request.headers.has("rsc-action-id");
if (respondWithRSCPayload || serverResponse.headers.get("React-Router-Resource") === "true") {
return serverResponse;
}
if (!serverResponse.body) {
throw new Error("Missing body in server response");
}
const detectRedirectResponse = serverResponse.clone();
let serverResponseB = null;
if (hydrate) {
serverResponseB = serverResponse.clone();
}
const body = serverResponse.body;
let buffer;
let streamControllers = [];
const createStream = () => {
if (!buffer) {
buffer = [];
return body.pipeThrough(
new TransformStream({
transform(chunk, controller) {
buffer.push(chunk);
controller.enqueue(chunk);
streamControllers.forEach((c) => c.enqueue(chunk));
},
flush() {
streamControllers.forEach((c) => c.close());
streamControllers = [];
}
})
);
}
return new ReadableStream({
start(controller) {
buffer.forEach((chunk) => controller.enqueue(chunk));
streamControllers.push(controller);
}
});
};
let deepestRenderedBoundaryId = null;
const getPayload = () => {
const payloadPromise = Promise.resolve(
createFromReadableStream(createStream())
);
return Object.defineProperties(payloadPromise, {
_deepestRenderedBoundaryId: {
get() {
return deepestRenderedBoundaryId;
},
set(boundaryId) {
deepestRenderedBoundaryId = boundaryId;
}
},
formState: {
get() {
return payloadPromise.then(
(payload) => payload.type === "render" ? payload.formState : void 0
);
}
}
});
};
let renderRedirect;
let renderError;
try {
if (!detectRedirectResponse.body) {
throw new Error("Failed to clone server response");
}
const payload = await createFromReadableStream(
detectRedirectResponse.body
);
if (serverResponse.status === SINGLE_FETCH_REDIRECT_STATUS && payload.type === "redirect") {
if (hasInvalidProtocol(payload.location)) {
throw new Error("Invalid redirect location");
}
const headers2 = new Headers(serverResponse.headers);
headers2.delete("Content-Encoding");
headers2.delete("Content-Length");
headers2.delete("Content-Type");
headers2.delete("X-Remix-Response");
headers2.set("Location", payload.location);
return new Response(serverResponseB?.body || "", {
headers: headers2,
status: payload.status,
statusText: serverResponse.statusText
});
}
let reactHeaders = new Headers();
let status = serverResponse.status;
let statusText = serverResponse.statusText;
let html = await renderHTML(getPayload, {
onError(error) {
if (typeof error === "object" && error && "digest" in error && typeof error.digest === "string") {
renderRedirect = decodeRedirectErrorDigest(error.digest);
if (renderRedirect) {
return error.digest;
}
let routeErrorResponse = decodeRouteErrorResponseDigest(error.digest);
if (routeErrorResponse) {
renderError = routeErrorResponse;
status = routeErrorResponse.status;
statusText = routeErrorResponse.statusText;
return error.digest;
}
}
},
onHeaders(headers2) {
for (const [key, value] of headers2) {
reactHeaders.append(key, value);
}
}
});
const headers = new Headers(reactHeaders);
for (const [key, value] of serverResponse.headers) {
headers.append(key, value);
}
headers.set("Content-Type", "text/html; charset=utf-8");
if (renderRedirect) {
if (hasInvalidProtocol(renderRedirect.location)) {
throw new Error("Invalid redirect location");
}
headers.set("Location", renderRedirect.location);
return new Response(html, {
status: renderRedirect.status,
headers
});
}
const redirectTransform = new TransformStream({
flush(controller) {
if (renderRedirect) {
if (hasInvalidProtocol(renderRedirect.location)) {
return;
}
controller.enqueue(
new TextEncoder().encode(
`<meta http-equiv="refresh" content="0;url=${escapeHtml(renderRedirect.location)}"/>`
)
);
}
}
});
if (!hydrate) {
return new Response(html.pipeThrough(redirectTransform), {
status,
statusText,
headers
});
}
if (!serverResponseB?.body) {
throw new Error("Failed to clone server response");
}
const body2 = html.pipeThrough(injectRSCPayload(serverResponseB.body)).pipeThrough(redirectTransform);
return new Response(body2, {
status,
statusText,
headers
});
} catch (error) {
if (error instanceof Response) {
return error;
}
if (renderRedirect) {
if (hasInvalidProtocol(renderRedirect.location)) {
throw new Error("Invalid redirect location");
}
return new Response(`Redirect: ${renderRedirect.location}`, {
status: renderRedirect.status,
headers: {
Location: renderRedirect.location
}
});
}
try {
let normalizedError = renderError ?? error;
let [status, statusText] = isRouteErrorResponse(normalizedError) ? [normalizedError.status, normalizedError.statusText] : [500, ""];
let retryRedirect;
let reactHeaders = new Headers();
const html = await renderHTML(
() => {
const decoded = Promise.resolve(
createFromReadableStream(createStream())
);
const payloadPromise = decoded.then(
(payload) => Object.assign(payload, {
status,
errors: deepestRenderedBoundaryId ? {
[deepestRenderedBoundaryId]: normalizedError
} : {}
})
);
return Object.defineProperties(payloadPromise, {
_deepestRenderedBoundaryId: {
get() {
return deepestRenderedBoundaryId;
},
set(boundaryId) {
deepestRenderedBoundaryId = boundaryId;
}
},
formState: {
get() {
return payloadPromise.then(
(payload) => payload.type === "render" ? payload.formState : void 0
);
}
}
});
},
{
onError(error2) {
if (typeof error2 === "object" && error2 && "digest" in error2 && typeof error2.digest === "string") {
retryRedirect = decodeRedirectErrorDigest(error2.digest);
if (retryRedirect) {
return error2.digest;
}
let routeErrorResponse = decodeRouteErrorResponseDigest(
error2.digest
);
if (routeErrorResponse) {
status = routeErrorResponse.status;
statusText = routeErrorResponse.statusText;
return error2.digest;
}
}
},
onHeaders(headers2) {
for (const [key, value] of headers2) {
reactHeaders.append(key, value);
}
}
}
);
const headers = new Headers(reactHeaders);
for (const [key, value] of serverResponse.headers) {
headers.append(key, value);
}
headers.set("Content-Type", "text/html; charset=utf-8");
if (retryRedirect) {
if (hasInvalidProtocol(retryRedirect.location)) {
throw new Error("Invalid redirect location");
}
headers.set("Location", retryRedirect.location);
return new Response(html, {
status: retryRedirect.status,
headers
});
}
const retryRedirectTransform = new TransformStream({
flush(controller) {
if (retryRedirect) {
if (hasInvalidProtocol(retryRedirect.location)) {
return;
}
controller.enqueue(
new TextEncoder().encode(
`<meta http-equiv="refresh" content="0;url=${escapeHtml(retryRedirect.location)}"/>`
)
);
}
}
});
if (!hydrate) {
return new Response(html.pipeThrough(retryRedirectTransform), {
status,
statusText,
headers
});
}
if (!serverResponseB?.body) {
throw new Error("Failed to clone server response");
}
const body2 = html.pipeThrough(injectRSCPayload(serverResponseB.body)).pipeThrough(retryRedirectTransform);
return new Response(body2, {
status,
statusText,
headers
});
} catch (error2) {
}
throw error;
}
}
function RSCStaticRouter({ getPayload }) {
const decoded = getPayload();
const payload = useSafe(decoded);
if (payload.type === "redirect") {
if (hasInvalidProtocol(payload.location)) {
throw new Error("Invalid redirect location");
}
throw new Response(null, {
status: payload.status,
headers: {
Location: payload.location
}
});
}
if (payload.type !== "render") return null;
let patchedLoaderData = { ...payload.loaderData };
for (const match of payload.matches) {
if (shouldHydrateRouteLoader(
match.id,
match.clientLoader,
match.hasLoader,
false
) && (match.hydrateFallbackElement || !match.hasLoader)) {
delete patchedLoaderData[match.id];
}
}
const context = {
get _deepestRenderedBoundaryId() {
return decoded._deepestRenderedBoundaryId ?? null;
},
set _deepestRenderedBoundaryId(boundaryId) {
decoded._deepestRenderedBoundaryId = boundaryId;
},
actionData: payload.actionData,
actionHeaders: {},
basename: payload.basename,
errors: payload.errors,
loaderData: patchedLoaderData,
loaderHeaders: {},
location: payload.location,
statusCode: 200,
matches: payload.matches.map((match) => ({
params: match.params,
pathname: match.pathname,
pathnameBase: match.pathnameBase,
route: {
id: match.id,
action: match.hasAction || !!match.clientAction,
handle: match.handle,
hasErrorBoundary: match.hasErrorBoundary,
loader: match.hasLoader || !!match.clientLoader,
index: match.index,
path: match.path,
shouldRevalidate: match.shouldRevalidate
}
}))
};
const router = createStaticRouter(
payload.matches.reduceRight((previous, match) => {
const route = {
id: match.id,
action: match.hasAction || !!match.clientAction,
element: match.element,
errorElement: match.errorElement,
handle: match.handle,
hasErrorBoundary: !!match.errorElement,
hydrateFallbackElement: match.hydrateFallbackElement,
index: match.index,
loader: match.hasLoader || !!match.clientLoader,
path: match.path,
shouldRevalidate: match.shouldRevalidate
};
if (previous.length > 0) {
route.children = previous;
}
return [route];
}, []),
context
);
const frameworkContext = {
future: {
// These flags have no runtime impact so can always be false. If we add
// flags that drive runtime behavior they'll need to be proxied through.
v8_middleware: false,
v8_trailingSlashAwareDataRequests: true,
// always on for RSC
v8_passThroughRequests: true
// always on for RSC
},
isSpaMode: false,
ssr: true,
criticalCss: "",
manifest: {
routes: {},
version: "1",
url: "",
entry: {
module: "",
imports: []
}
},
routeDiscovery: payload.routeDiscovery.mode === "initial" ? { mode: "initial", manifestPath: defaultManifestPath } : {
mode: "lazy",
manifestPath: payload.routeDiscovery.manifestPath || defaultManifestPath
},
routeModules: createRSCRouteModules(payload)
};
return /* @__PURE__ */ React4.createElement(RSCRouterContext.Provider, { value: true }, /* @__PURE__ */ React4.createElement(RSCRouterGlobalErrorBoundary, { location: payload.location }, /* @__PURE__ */ React4.createElement(FrameworkContext.Provider, { value: frameworkContext }, /* @__PURE__ */ React4.createElement(
StaticRouterProvider,
{
context,
router,
hydrate: false,
nonce: payload.nonce
}
))));
}
function isReactServerRequest(url) {
return url.pathname.endsWith(".rsc");
}
function isManifestRequest(url) {
return url.pathname.endsWith(".manifest");
}
// lib/dom/ssr/hydration.tsx
function getHydrationData({
state,
routes,
getRouteInfo,
location,
basename,
isSpaMode
}) {
let hydrationData = {
...state,
loaderData: { ...state.loaderData }
};
let initialMatches = matchRoutes(routes, location, basename);
if (initialMatches) {
for (let match of initialMatches) {
let routeId = match.route.id;
let routeInfo = getRouteInfo(routeId);
if (shouldHydrateRouteLoader(
routeId,
routeInfo.clientLoader,
routeInfo.hasLoader,
isSpaMode
) && (routeInfo.hasHydrateFallback || !routeInfo.hasLoader)) {
delete hydrationData.loaderData[routeId];
} else if (!routeInfo.hasLoader) {
hydrationData.loaderData[routeId] = null;
}
}
}
return hydrationData;
}
export {
ServerRouter,
createRoutesStub,
createCookie,
isCookie,
ServerMode,
setDevServerHooks,
createRequestHandler,
createSession,
isSession,
createSessionStorage,
createCookieSessionStorage,
createMemorySessionStorage,
href,
RSCRouterGlobalErrorBoundary,
RSCDefaultRootErrorBoundary,
populateRSCRouteModules,
routeRSCServerRequest,
RSCStaticRouter,
getHydrationData
};
File diff suppressed because it is too large Load Diff
File diff suppressed because one or more lines are too long
+1779
View File
@@ -0,0 +1,1779 @@
import { m as HTMLFormMethod, n as FormEncType, o as LoaderFunctionArgs, p as MiddlewareEnabled, c as RouterContextProvider, q as AppLoadContext, r as RouteObject, s as History, t as MaybePromise, u as MapRoutePropertiesFunction, v as Action, L as Location, w as DataRouteMatch, x as Submission, y as RouteData, z as DataStrategyFunction, B as PatchRoutesOnNavigationFunction, E as DataRouteObject, I as RouteBranch, J as RouteManifest, U as UIMatch, T as To, K as Path, P as Params, O as InitialEntry, Q as NonIndexRouteObject, V as LazyRouteFunction, W as IndexRouteObject, X as RouteMatch, Y as TrackedPromise } from './data-DEjBmEfD.mjs';
import * as React from 'react';
type ServerInstrumentation = {
handler?: InstrumentRequestHandlerFunction;
route?: InstrumentRouteFunction;
};
type ClientInstrumentation = {
router?: InstrumentRouterFunction;
route?: InstrumentRouteFunction;
};
type InstrumentRequestHandlerFunction = (handler: InstrumentableRequestHandler) => void;
type InstrumentRouterFunction = (router: InstrumentableRouter) => void;
type InstrumentRouteFunction = (route: InstrumentableRoute) => void;
type InstrumentationHandlerResult = {
status: "success";
error: undefined;
} | {
status: "error";
error: Error;
};
type InstrumentFunction<T> = (handler: () => Promise<InstrumentationHandlerResult>, info: T) => Promise<void>;
type ReadonlyRequest = {
method: string;
url: string;
headers: Pick<Headers, "get">;
};
type ReadonlyContext = MiddlewareEnabled extends true ? Pick<RouterContextProvider, "get"> : Readonly<AppLoadContext>;
type InstrumentableRoute = {
id: string;
index: boolean | undefined;
path: string | undefined;
instrument(instrumentations: RouteInstrumentations): void;
};
type RouteInstrumentations = {
lazy?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.loader"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.action"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.middleware"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
middleware?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
loader?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
action?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
};
type RouteLazyInstrumentationInfo = undefined;
type RouteHandlerInstrumentationInfo = Readonly<{
request: ReadonlyRequest;
params: LoaderFunctionArgs["params"];
pattern: string;
context: ReadonlyContext;
}>;
type InstrumentableRouter = {
instrument(instrumentations: RouterInstrumentations): void;
};
type RouterInstrumentations = {
navigate?: InstrumentFunction<RouterNavigationInstrumentationInfo>;
fetch?: InstrumentFunction<RouterFetchInstrumentationInfo>;
};
type RouterNavigationInstrumentationInfo = Readonly<{
to: string | number;
currentUrl: string;
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
formData?: FormData;
body?: any;
}>;
type RouterFetchInstrumentationInfo = Readonly<{
href: string;
currentUrl: string;
fetcherKey: string;
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
formData?: FormData;
body?: any;
}>;
type InstrumentableRequestHandler = {
instrument(instrumentations: RequestHandlerInstrumentations): void;
};
type RequestHandlerInstrumentations = {
request?: InstrumentFunction<RequestHandlerInstrumentationInfo>;
};
type RequestHandlerInstrumentationInfo = Readonly<{
request: ReadonlyRequest;
context: ReadonlyContext | undefined;
}>;
/**
* A Router instance manages all navigation and data loading/mutations
*/
interface Router$1 {
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the basename for the router
*/
get basename(): RouterInit["basename"];
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the future config for the router
*/
get future(): FutureConfig;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the current state of the router
*/
get state(): RouterState;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the routes for this router instance
*/
get routes(): DataRouteObject[];
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the route branches for this router instance
*/
get branches(): RouteBranch<DataRouteObject>[] | undefined;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the manifest for this router instance
*/
get manifest(): RouteManifest;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the window associated with the router
*/
get window(): RouterInit["window"];
/**
* @private
* PRIVATE - DO NOT USE
*
* Initialize the router, including adding history listeners and kicking off
* initial data fetches. Returns a function to cleanup listeners and abort
* any in-progress loads
*/
initialize(): Router$1;
/**
* @private
* PRIVATE - DO NOT USE
*
* Subscribe to router.state updates
*
* @param fn function to call with the new state
*/
subscribe(fn: RouterSubscriber): () => void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Enable scroll restoration behavior in the router
*
* @param savedScrollPositions Object that will manage positions, in case
* it's being restored from sessionStorage
* @param getScrollPosition Function to get the active Y scroll position
* @param getKey Function to get the key to use for restoration
*/
enableScrollRestoration(savedScrollPositions: Record<string, number>, getScrollPosition: GetScrollPositionFunction, getKey?: GetScrollRestorationKeyFunction): () => void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Navigate forward/backward in the history stack
* @param to Delta to move in the history stack
*/
navigate(to: number): Promise<void>;
/**
* Navigate to the given path
* @param to Path to navigate to
* @param opts Navigation options (method, submission, etc.)
*/
navigate(to: To | null, opts?: RouterNavigateOptions): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Trigger a fetcher load/submission
*
* @param key Fetcher key
* @param routeId Route that owns the fetcher
* @param href href to fetch
* @param opts Fetcher options, (method, submission, etc.)
*/
fetch(key: string, routeId: string, href: string | null, opts?: RouterFetchOptions): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Trigger a revalidation of all current route loaders and fetcher loads
*/
revalidate(): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Utility function to create an href for the given location
* @param location
*/
createHref(location: Location | URL): string;
/**
* @private
* PRIVATE - DO NOT USE
*
* Utility function to URL encode a destination path according to the internal
* history implementation
* @param to
*/
encodeLocation(to: To): Path;
/**
* @private
* PRIVATE - DO NOT USE
*
* Get/create a fetcher for the given key
* @param key
*/
getFetcher<TData = any>(key: string): Fetcher<TData>;
/**
* @internal
* PRIVATE - DO NOT USE
*
* Reset the fetcher for a given key
* @param key
*/
resetFetcher(key: string, opts?: {
reason?: unknown;
}): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Delete the fetcher for a given key
* @param key
*/
deleteFetcher(key: string): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Cleanup listeners and abort any in-progress loads
*/
dispose(): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Get a navigation blocker
* @param key The identifier for the blocker
* @param fn The blocker function implementation
*/
getBlocker(key: string, fn: BlockerFunction): Blocker;
/**
* @private
* PRIVATE - DO NOT USE
*
* Delete a navigation blocker
* @param key The identifier for the blocker
*/
deleteBlocker(key: string): void;
/**
* @private
* PRIVATE DO NOT USE
*
* Patch additional children routes into an existing parent route
* @param routeId The parent route id or a callback function accepting `patch`
* to perform batch patching
* @param children The additional children routes
* @param unstable_allowElementMutations Allow mutation or route elements on
* existing routes. Intended for RSC-usage
* only.
*/
patchRoutes(routeId: string | null, children: RouteObject[], unstable_allowElementMutations?: boolean): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* HMR needs to pass in-flight route updates to React Router
* TODO: Replace this with granular route update APIs (addRoute, updateRoute, deleteRoute)
*/
_internalSetRoutes(routes: RouteObject[]): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Cause subscribers to re-render. This is used to force a re-render.
*/
_internalSetStateDoNotUseOrYouWillBreakYourApp(state: Partial<RouterState>): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Internal fetch AbortControllers accessed by unit tests
*/
_internalFetchControllers: Map<string, AbortController>;
}
/**
* State maintained internally by the router. During a navigation, all states
* reflect the "old" location unless otherwise noted.
*/
interface RouterState {
/**
* The action of the most recent navigation
*/
historyAction: Action;
/**
* The current location reflected by the router
*/
location: Location;
/**
* The current set of route matches
*/
matches: DataRouteMatch[];
/**
* Tracks whether we've completed our initial data load
*/
initialized: boolean;
/**
* Tracks whether we should be rendering a HydrateFallback during hydration
*/
renderFallback: boolean;
/**
* Current scroll position we should start at for a new view
* - number -> scroll position to restore to
* - false -> do not restore scroll at all (used during submissions/revalidations)
* - null -> don't have a saved position, scroll to hash or top of page
*/
restoreScrollPosition: number | false | null;
/**
* Indicate whether this navigation should skip resetting the scroll position
* if we are unable to restore the scroll position
*/
preventScrollReset: boolean;
/**
* Tracks the state of the current navigation
*/
navigation: Navigation;
/**
* Tracks any in-progress revalidations
*/
revalidation: RevalidationState;
/**
* Data from the loaders for the current matches
*/
loaderData: RouteData;
/**
* Data from the action for the current matches
*/
actionData: RouteData | null;
/**
* Errors caught from loaders for the current matches
*/
errors: RouteData | null;
/**
* Map of current fetchers
*/
fetchers: Map<string, Fetcher>;
/**
* Map of current blockers
*/
blockers: Map<string, Blocker>;
}
/**
* Data that can be passed into hydrate a Router from SSR
*/
type HydrationState = Partial<Pick<RouterState, "loaderData" | "actionData" | "errors">>;
/**
* Future flags to toggle new feature behavior
*/
interface FutureConfig {
}
/**
* Initialization options for createRouter
*/
interface RouterInit {
routes: RouteObject[];
history: History;
basename?: string;
getContext?: () => MaybePromise<RouterContextProvider>;
instrumentations?: ClientInstrumentation[];
mapRouteProperties?: MapRoutePropertiesFunction;
future?: Partial<FutureConfig>;
hydrationRouteProperties?: string[];
hydrationData?: HydrationState;
window?: Window;
dataStrategy?: DataStrategyFunction;
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
}
/**
* State returned from a server-side query() call
*/
interface StaticHandlerContext {
basename: Router$1["basename"];
location: RouterState["location"];
matches: RouterState["matches"];
loaderData: RouterState["loaderData"];
actionData: RouterState["actionData"];
errors: RouterState["errors"];
statusCode: number;
loaderHeaders: Record<string, Headers>;
actionHeaders: Record<string, Headers>;
_deepestRenderedBoundaryId?: string | null;
}
/**
* A StaticHandler instance manages a singular SSR navigation/fetch event
*/
interface StaticHandler {
/**
* The set of data routes managed by this handler
*/
dataRoutes: DataRouteObject[];
/**
* @private
* PRIVATE - DO NOT USE
*
* The route branches derived from the data routes, used for internal route
* matching in Framework Mode
*/
_internalRouteBranches: RouteBranch<DataRouteObject>[];
/**
* Perform a query for a given request - executing all matched route
* loaders/actions. Used for document requests.
*
* @param request The request to query
* @param opts Optional query options
* @param opts.dataStrategy Alternate dataStrategy implementation
* @param opts.filterMatchesToLoad Predicate function to filter which matches should be loaded
* @param opts.generateMiddlewareResponse To enable middleware, provide a function
* to generate a response to bubble back up the middleware chain
* @param opts.requestContext Context object to pass to loaders/actions
* @param opts.skipLoaderErrorBubbling Skip loader error bubbling
* @param opts.skipRevalidation Skip revalidation after action submission
* @param opts.normalizePath Normalize the request path
*/
query(request: Request, opts?: {
requestContext?: unknown;
filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
skipLoaderErrorBubbling?: boolean;
skipRevalidation?: boolean;
dataStrategy?: DataStrategyFunction<unknown>;
generateMiddlewareResponse?: (query: (r: Request, args?: {
filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
}) => Promise<StaticHandlerContext | Response>) => MaybePromise<Response>;
normalizePath?: (request: Request) => Path;
}): Promise<StaticHandlerContext | Response>;
/**
* Perform a query for a specific route. Used for resource requests.
*
* @param request The request to query
* @param opts Optional queryRoute options
* @param opts.dataStrategy Alternate dataStrategy implementation
* @param opts.generateMiddlewareResponse To enable middleware, provide a function
* to generate a response to bubble back up the middleware chain
* @param opts.requestContext Context object to pass to loaders/actions
* @param opts.routeId The ID of the route to query
* @param opts.normalizePath Normalize the request path
*/
queryRoute(request: Request, opts?: {
routeId?: string;
requestContext?: unknown;
dataStrategy?: DataStrategyFunction<unknown>;
generateMiddlewareResponse?: (queryRoute: (r: Request) => Promise<Response>) => MaybePromise<Response>;
normalizePath?: (request: Request) => Path;
}): Promise<any>;
}
type ViewTransitionOpts = {
currentLocation: Location;
nextLocation: Location;
};
/**
* Subscriber function signature for changes to router state
*/
interface RouterSubscriber {
(state: RouterState, opts: {
deletedFetchers: string[];
newErrors: RouteData | null;
viewTransitionOpts?: ViewTransitionOpts;
flushSync: boolean;
}): void;
}
/**
* Function signature for determining the key to be used in scroll restoration
* for a given location
*/
interface GetScrollRestorationKeyFunction {
(location: Location, matches: UIMatch[]): string | null;
}
/**
* Function signature for determining the current scroll position
*/
interface GetScrollPositionFunction {
(): number;
}
/**
* - "route": relative to the route hierarchy so `..` means remove all segments
* of the current route even if it has many. For example, a `route("posts/:id")`
* would have both `:id` and `posts` removed from the url.
* - "path": relative to the pathname so `..` means remove one segment of the
* pathname. For example, a `route("posts/:id")` would have only `:id` removed
* from the url.
*/
type RelativeRoutingType = "route" | "path";
type BaseNavigateOrFetchOptions = {
preventScrollReset?: boolean;
relative?: RelativeRoutingType;
flushSync?: boolean;
defaultShouldRevalidate?: boolean;
};
type BaseNavigateOptions = BaseNavigateOrFetchOptions & {
replace?: boolean;
state?: any;
fromRouteId?: string;
viewTransition?: boolean;
mask?: To;
};
type BaseSubmissionOptions = {
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
} & ({
formData: FormData;
body?: undefined;
} | {
formData?: undefined;
body: any;
});
/**
* Options for a navigate() call for a normal (non-submission) navigation
*/
type LinkNavigateOptions = BaseNavigateOptions;
/**
* Options for a navigate() call for a submission navigation
*/
type SubmissionNavigateOptions = BaseNavigateOptions & BaseSubmissionOptions;
/**
* Options to pass to navigate() for a navigation
*/
type RouterNavigateOptions = LinkNavigateOptions | SubmissionNavigateOptions;
/**
* Options for a fetch() load
*/
type LoadFetchOptions = BaseNavigateOrFetchOptions;
/**
* Options for a fetch() submission
*/
type SubmitFetchOptions = BaseNavigateOrFetchOptions & BaseSubmissionOptions;
/**
* Options to pass to fetch()
*/
type RouterFetchOptions = LoadFetchOptions | SubmitFetchOptions;
/**
* Potential states for state.navigation
*/
type NavigationStates = {
Idle: {
state: "idle";
location: undefined;
matches: undefined;
historyAction: undefined;
formMethod: undefined;
formAction: undefined;
formEncType: undefined;
formData: undefined;
json: undefined;
text: undefined;
};
Loading: {
state: "loading";
location: Location;
matches: DataRouteMatch[];
historyAction: Action;
formMethod: Submission["formMethod"] | undefined;
formAction: Submission["formAction"] | undefined;
formEncType: Submission["formEncType"] | undefined;
formData: Submission["formData"] | undefined;
json: Submission["json"] | undefined;
text: Submission["text"] | undefined;
};
Submitting: {
state: "submitting";
location: Location;
matches: DataRouteMatch[];
historyAction: Action;
formMethod: Submission["formMethod"];
formAction: Submission["formAction"];
formEncType: Submission["formEncType"];
formData: Submission["formData"];
json: Submission["json"];
text: Submission["text"];
};
};
type Navigation = NavigationStates[keyof NavigationStates];
type RevalidationState = "idle" | "loading";
/**
* Potential states for fetchers
*/
type FetcherStates<TData = any> = {
/**
* The fetcher is not calling a loader or action
*
* ```tsx
* fetcher.state === "idle"
* ```
*/
Idle: {
state: "idle";
formMethod: undefined;
formAction: undefined;
formEncType: undefined;
text: undefined;
formData: undefined;
json: undefined;
/**
* If the fetcher has never been called, this will be undefined.
*/
data: TData | undefined;
};
/**
* The fetcher is loading data from a {@link LoaderFunction | loader} from a
* call to {@link FetcherWithComponents.load | `fetcher.load`}.
*
* ```tsx
* // somewhere
* <button onClick={() => fetcher.load("/some/route") }>Load</button>
*
* // the state will update
* fetcher.state === "loading"
* ```
*/
Loading: {
state: "loading";
formMethod: Submission["formMethod"] | undefined;
formAction: Submission["formAction"] | undefined;
formEncType: Submission["formEncType"] | undefined;
text: Submission["text"] | undefined;
formData: Submission["formData"] | undefined;
json: Submission["json"] | undefined;
data: TData | undefined;
};
/**
The fetcher is submitting to a {@link LoaderFunction} (GET) or {@link ActionFunction} (POST) from a {@link FetcherWithComponents.Form | `fetcher.Form`} or {@link FetcherWithComponents.submit | `fetcher.submit`}.
```tsx
// somewhere
<input
onChange={e => {
fetcher.submit(event.currentTarget.form, { method: "post" });
}}
/>
// the state will update
fetcher.state === "submitting"
// and formData will be available
fetcher.formData
```
*/
Submitting: {
state: "submitting";
formMethod: Submission["formMethod"];
formAction: Submission["formAction"];
formEncType: Submission["formEncType"];
text: Submission["text"];
formData: Submission["formData"];
json: Submission["json"];
data: TData | undefined;
};
};
type Fetcher<TData = any> = FetcherStates<TData>[keyof FetcherStates<TData>];
interface BlockerBlocked {
state: "blocked";
reset: () => void;
proceed: () => void;
location: Location;
}
interface BlockerUnblocked {
state: "unblocked";
reset: undefined;
proceed: undefined;
location: undefined;
}
interface BlockerProceeding {
state: "proceeding";
reset: undefined;
proceed: undefined;
location: Location;
}
type Blocker = BlockerUnblocked | BlockerBlocked | BlockerProceeding;
type BlockerFunction = (args: {
currentLocation: Location;
nextLocation: Location;
historyAction: Action;
}) => boolean;
declare const IDLE_NAVIGATION: NavigationStates["Idle"];
declare const IDLE_FETCHER: FetcherStates["Idle"];
declare const IDLE_BLOCKER: BlockerUnblocked;
/**
* Create a router and listen to history POP navigations
*/
declare function createRouter(init: RouterInit): Router$1;
interface CreateStaticHandlerOptions {
basename?: string;
mapRouteProperties?: MapRoutePropertiesFunction;
instrumentations?: Pick<ServerInstrumentation, "route">[];
future?: Partial<FutureConfig>;
}
declare function mapRouteProperties(route: RouteObject): Partial<RouteObject> & {
hasErrorBoundary: boolean;
};
declare const hydrationRouteProperties: (keyof RouteObject)[];
/**
* @category Data Routers
*/
interface MemoryRouterOpts {
/**
* Basename path for the application.
*/
basename?: string;
/**
* A function that returns an {@link RouterContextProvider} instance
* which is provided as the `context` argument to client [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* This function is called to generate a fresh `context` instance on each
* navigation or fetcher call.
*/
getContext?: RouterInit["getContext"];
/**
* Future flags to enable for the router.
*/
future?: Partial<FutureConfig>;
/**
* Hydration data to initialize the router with if you have already performed
* data loading on the server.
*/
hydrationData?: HydrationState;
/**
* Initial entries in the in-memory history stack
*/
initialEntries?: InitialEntry[];
/**
* Index of `initialEntries` the application should initialize to
*/
initialIndex?: number;
/**
* Array of instrumentation objects allowing you to instrument the router and
* individual routes prior to router initialization (and on any subsequently
* added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
* mostly useful for observability such as wrapping navigations, fetches,
* as well as route loaders/actions/middlewares with logging and/or performance
* tracing. See the [docs](../../how-to/instrumentation) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* instrumentations: [logging]
* });
*
*
* let logging = {
* router({ instrument }) {
* instrument({
* navigate: (impl, info) => logExecution(`navigate ${info.to}`, impl),
* fetch: (impl, info) => logExecution(`fetch ${info.to}`, impl)
* });
* },
* route({ instrument, id }) {
* instrument({
* middleware: (impl, info) => logExecution(
* `middleware ${info.request.url} (route ${id})`,
* impl
* ),
* loader: (impl, info) => logExecution(
* `loader ${info.request.url} (route ${id})`,
* impl
* ),
* action: (impl, info) => logExecution(
* `action ${info.request.url} (route ${id})`,
* impl
* ),
* })
* }
* };
*
* async function logExecution(label: string, impl: () => Promise<void>) {
* let start = performance.now();
* console.log(`start ${label}`);
* await impl();
* let duration = Math.round(performance.now() - start);
* console.log(`end ${label} (${duration}ms)`);
* }
* ```
*/
instrumentations?: ClientInstrumentation[];
/**
* Override the default data strategy of running loaders in parallel -
* see the [docs](../../how-to/data-strategy) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* async dataStrategy({
* matches,
* request,
* runClientMiddleware,
* }) {
* const matchesToLoad = matches.filter((m) =>
* m.shouldCallHandler(),
* );
*
* const results: Record<string, DataStrategyResult> = {};
* await runClientMiddleware(() =>
* Promise.all(
* matchesToLoad.map(async (match) => {
* results[match.route.id] = await match.resolve();
* }),
* ),
* );
* return results;
* },
* });
* ```
*/
dataStrategy?: DataStrategyFunction;
/**
* Lazily define portions of the route tree on navigations.
*/
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
}
/**
* Create a new {@link DataRouter} that manages the application path using an
* in-memory [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack. Useful for non-browser environments without a DOM API.
*
* Data Routers should not be held in React state. You should create your router
* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
*
* @public
* @category Data Routers
* @mode data
* @param routes Application routes
* @param opts Options
* @param {MemoryRouterOpts.basename} opts.basename n/a
* @param {MemoryRouterOpts.dataStrategy} opts.dataStrategy n/a
* @param {MemoryRouterOpts.future} opts.future n/a
* @param {MemoryRouterOpts.getContext} opts.getContext n/a
* @param {MemoryRouterOpts.hydrationData} opts.hydrationData n/a
* @param {MemoryRouterOpts.initialEntries} opts.initialEntries n/a
* @param {MemoryRouterOpts.initialIndex} opts.initialIndex n/a
* @param {MemoryRouterOpts.instrumentations} opts.instrumentations n/a
* @param {MemoryRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a
* @returns An initialized {@link DataRouter} to pass to {@link RouterProvider | `<RouterProvider>`}
*/
declare function createMemoryRouter(routes: RouteObject[], opts?: MemoryRouterOpts): Router$1;
/**
* Function signature for client side error handling for loader/actions errors
* and rendering errors via `componentDidCatch`
*/
interface ClientOnErrorFunction {
(error: unknown, info: {
location: Location;
params: Params;
pattern: string;
errorInfo?: React.ErrorInfo;
}): void;
}
/**
* @category Types
*/
interface RouterProviderProps {
/**
* The {@link DataRouter} instance to use for navigation and data fetching. The
* router prop should be a single router instance created outside of the React
* tree. Avoid creating new routers during React renders/re-renders.
*/
router: Router$1;
/**
* The [`ReactDOM.flushSync`](https://react.dev/reference/react-dom/flushSync)
* implementation to use for flushing updates.
*
* You usually don't have to worry about this:
* - The `RouterProvider` exported from `react-router/dom` handles this internally for you
* - If you are rendering in a non-DOM environment, you can import
* `RouterProvider` from `react-router` and ignore this prop
*/
flushSync?: (fn: () => unknown) => undefined;
/**
* An error handler function that will be called for any middleware, loader, action,
* or render errors that are encountered in your application. This is useful for
* logging or reporting errors instead of in the {@link ErrorBoundary} because it's not
* subject to re-rendering and will only run one time per error.
*
* The `errorInfo` parameter is passed along from
* [`componentDidCatch`](https://react.dev/reference/react/Component#componentdidcatch)
* and is only present for render errors.
*
* ```tsx
* <RouterProvider onError={(error, info) => {
* let { location, params, pattern, errorInfo } = info;
* console.error(error, location, errorInfo);
* reportToErrorService(error, location, errorInfo);
* }} />
* ```
*/
onError?: ClientOnErrorFunction;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all state updates are wrapped in
* `React.startTransition`
* - This can lead to buggy behaviors if you are wrapping your own
* navigations/fetchers in `startTransition`.
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and router state changes will be wrapped in
* `React.startTransition` and also sent through
* [`useOptimistic`](https://react.dev/reference/react/useOptimistic) to
* surface mid-navigation router state changes to the UI.
* - When set to `false`, the router will not leverage `React.startTransition` or
* `React.useOptimistic` on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* Render the UI for the given {@link DataRouter}. This component should
* typically be at the top of an app's element tree. The router prop should
* be a single router instance created outside of the React tree. Avoid
* creating new routers during React renders/re-renders.
*
* ```tsx
* import { createBrowserRouter } from "react-router";
* import { RouterProvider } from "react-router/dom";
* import { createRoot } from "react-dom/client";
*
* const router = createBrowserRouter(routes);
* createRoot(document.getElementById("root")).render(
* <RouterProvider router={router} />
* );
* ```
*
* <docs-info>Please note that this component is exported both from
* `react-router` and `react-router/dom` with the only difference being that the
* latter automatically wires up `react-dom`'s [`flushSync`](https://react.dev/reference/react-dom/flushSync)
* implementation. You _almost always_ want to use the version from
* `react-router/dom` unless you're running in a non-DOM environment.</docs-info>
*
*
* @public
* @category Data Routers
* @mode data
* @param props Props
* @param {RouterProviderProps.flushSync} props.flushSync n/a
* @param {RouterProviderProps.onError} props.onError n/a
* @param {RouterProviderProps.router} props.router n/a
* @param {RouterProviderProps.useTransitions} props.useTransitions n/a
* @returns React element for the rendered router
*/
declare function RouterProvider({ router, flushSync: reactDomFlushSyncImpl, onError, useTransitions, }: RouterProviderProps): React.ReactElement;
/**
* @category Types
*/
interface MemoryRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* Nested {@link Route} elements describing the route tree
*/
children?: React.ReactNode;
/**
* Initial entries in the in-memory history stack
*/
initialEntries?: InitialEntry[];
/**
* Index of `initialEntries` the application should initialize to
*/
initialIndex?: number;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* A declarative {@link Router | `<Router>`} that stores all entries in memory.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {MemoryRouterProps.basename} props.basename n/a
* @param {MemoryRouterProps.children} props.children n/a
* @param {MemoryRouterProps.initialEntries} props.initialEntries n/a
* @param {MemoryRouterProps.initialIndex} props.initialIndex n/a
* @param {MemoryRouterProps.useTransitions} props.useTransitions n/a
* @returns A declarative in-memory {@link Router | `<Router>`} for client-side
* routing.
*/
declare function MemoryRouter({ basename, children, initialEntries, initialIndex, useTransitions, }: MemoryRouterProps): React.ReactElement;
/**
* @category Types
*/
interface NavigateProps {
/**
* The path to navigate to. This can be a string or a {@link Path} object
*/
to: To;
/**
* Whether to replace the current entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack
*/
replace?: boolean;
/**
* State to pass to the new {@link Location} to store in [`history.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state).
*/
state?: any;
/**
* How to interpret relative routing in the `to` prop.
* See {@link RelativeRoutingType}.
*/
relative?: RelativeRoutingType;
}
/**
* A component-based version of {@link useNavigate} to use in a
* [`React.Component` class](https://react.dev/reference/react/Component) where
* hooks cannot be used.
*
* It's recommended to avoid using this component in favor of {@link useNavigate}.
*
* @example
* <Navigate to="/tasks" />
*
* @public
* @category Components
* @param props Props
* @param {NavigateProps.relative} props.relative n/a
* @param {NavigateProps.replace} props.replace n/a
* @param {NavigateProps.state} props.state n/a
* @param {NavigateProps.to} props.to n/a
* @returns {void}
*
*/
declare function Navigate({ to, replace, state, relative, }: NavigateProps): null;
/**
* @category Types
*/
interface OutletProps {
/**
* Provides a context value to the element tree below the outlet. Use when
* the parent route needs to provide values to child routes.
*
* ```tsx
* <Outlet context={myContextValue} />
* ```
*
* Access the context with {@link useOutletContext}.
*/
context?: unknown;
}
/**
* Renders the matching child route of a parent route or nothing if no child
* route matches.
*
* @example
* import { Outlet } from "react-router";
*
* export default function SomeParent() {
* return (
* <div>
* <h1>Parent Content</h1>
* <Outlet />
* </div>
* );
* }
*
* @public
* @category Components
* @param props Props
* @param {OutletProps.context} props.context n/a
* @returns React element for the rendered outlet or `null` if no child route matches.
*/
declare function Outlet(props: OutletProps): React.ReactElement | null;
/**
* @category Types
*/
interface PathRouteProps {
/**
* Whether the path should be case-sensitive. Defaults to `false`.
*/
caseSensitive?: NonIndexRouteObject["caseSensitive"];
/**
* The path pattern to match. If unspecified or empty, then this becomes a
* layout route.
*/
path?: NonIndexRouteObject["path"];
/**
* The unique identifier for this route (for use with {@link DataRouter}s)
*/
id?: NonIndexRouteObject["id"];
/**
* A function that returns a promise that resolves to the route object.
* Used for code-splitting routes.
* See [`lazy`](../../start/data/route-object#lazy).
*/
lazy?: LazyRouteFunction<NonIndexRouteObject>;
/**
* The route middleware.
* See [`middleware`](../../start/data/route-object#middleware).
*/
middleware?: NonIndexRouteObject["middleware"];
/**
* The route loader.
* See [`loader`](../../start/data/route-object#loader).
*/
loader?: NonIndexRouteObject["loader"];
/**
* The route action.
* See [`action`](../../start/data/route-object#action).
*/
action?: NonIndexRouteObject["action"];
hasErrorBoundary?: NonIndexRouteObject["hasErrorBoundary"];
/**
* The route shouldRevalidate function.
* See [`shouldRevalidate`](../../start/data/route-object#shouldRevalidate).
*/
shouldRevalidate?: NonIndexRouteObject["shouldRevalidate"];
/**
* The route handle.
*/
handle?: NonIndexRouteObject["handle"];
/**
* Whether this is an index route.
*/
index?: false;
/**
* Child Route components
*/
children?: React.ReactNode;
/**
* The React element to render when this Route matches.
* Mutually exclusive with `Component`.
*/
element?: React.ReactNode | null;
/**
* The React element to render while this router is loading data.
* Mutually exclusive with `HydrateFallback`.
*/
hydrateFallbackElement?: React.ReactNode | null;
/**
* The React element to render at this route if an error occurs.
* Mutually exclusive with `ErrorBoundary`.
*/
errorElement?: React.ReactNode | null;
/**
* The React Component to render when this route matches.
* Mutually exclusive with `element`.
*/
Component?: React.ComponentType | null;
/**
* The React Component to render while this router is loading data.
* Mutually exclusive with `hydrateFallbackElement`.
*/
HydrateFallback?: React.ComponentType | null;
/**
* The React Component to render at this route if an error occurs.
* Mutually exclusive with `errorElement`.
*/
ErrorBoundary?: React.ComponentType | null;
}
/**
* @category Types
*/
interface LayoutRouteProps extends PathRouteProps {
}
/**
* @category Types
*/
interface IndexRouteProps {
/**
* Whether the path should be case-sensitive. Defaults to `false`.
*/
caseSensitive?: IndexRouteObject["caseSensitive"];
/**
* The path pattern to match. If unspecified or empty, then this becomes a
* layout route.
*/
path?: IndexRouteObject["path"];
/**
* The unique identifier for this route (for use with {@link DataRouter}s)
*/
id?: IndexRouteObject["id"];
/**
* A function that returns a promise that resolves to the route object.
* Used for code-splitting routes.
* See [`lazy`](../../start/data/route-object#lazy).
*/
lazy?: LazyRouteFunction<IndexRouteObject>;
/**
* The route middleware.
* See [`middleware`](../../start/data/route-object#middleware).
*/
middleware?: IndexRouteObject["middleware"];
/**
* The route loader.
* See [`loader`](../../start/data/route-object#loader).
*/
loader?: IndexRouteObject["loader"];
/**
* The route action.
* See [`action`](../../start/data/route-object#action).
*/
action?: IndexRouteObject["action"];
hasErrorBoundary?: IndexRouteObject["hasErrorBoundary"];
/**
* The route shouldRevalidate function.
* See [`shouldRevalidate`](../../start/data/route-object#shouldRevalidate).
*/
shouldRevalidate?: IndexRouteObject["shouldRevalidate"];
/**
* The route handle.
*/
handle?: IndexRouteObject["handle"];
/**
* Whether this is an index route.
*/
index: true;
/**
* Child Route components
*/
children?: undefined;
/**
* The React element to render when this Route matches.
* Mutually exclusive with `Component`.
*/
element?: React.ReactNode | null;
/**
* The React element to render while this router is loading data.
* Mutually exclusive with `HydrateFallback`.
*/
hydrateFallbackElement?: React.ReactNode | null;
/**
* The React element to render at this route if an error occurs.
* Mutually exclusive with `ErrorBoundary`.
*/
errorElement?: React.ReactNode | null;
/**
* The React Component to render when this route matches.
* Mutually exclusive with `element`.
*/
Component?: React.ComponentType | null;
/**
* The React Component to render while this router is loading data.
* Mutually exclusive with `hydrateFallbackElement`.
*/
HydrateFallback?: React.ComponentType | null;
/**
* The React Component to render at this route if an error occurs.
* Mutually exclusive with `errorElement`.
*/
ErrorBoundary?: React.ComponentType | null;
}
/**
* @category Types
*/
type RouteProps = PathRouteProps | LayoutRouteProps | IndexRouteProps;
/**
* Configures an element to render when a pattern matches the current location.
* It must be rendered within a {@link Routes} element. Note that these routes
* do not participate in data loading, actions, code splitting, or any other
* route module features.
*
* @example
* // Usually used in a declarative router
* function App() {
* return (
* <BrowserRouter>
* <Routes>
* <Route index element={<StepOne />} />
* <Route path="step-2" element={<StepTwo />} />
* <Route path="step-3" element={<StepThree />} />
* </Routes>
* </BrowserRouter>
* );
* }
*
* // But can be used with a data router as well if you prefer the JSX notation
* const routes = createRoutesFromElements(
* <>
* <Route index loader={step1Loader} Component={StepOne} />
* <Route path="step-2" loader={step2Loader} Component={StepTwo} />
* <Route path="step-3" loader={step3Loader} Component={StepThree} />
* </>
* );
*
* const router = createBrowserRouter(routes);
*
* function App() {
* return <RouterProvider router={router} />;
* }
*
* @public
* @category Components
* @param props Props
* @param {PathRouteProps.action} props.action n/a
* @param {PathRouteProps.caseSensitive} props.caseSensitive n/a
* @param {PathRouteProps.Component} props.Component n/a
* @param {PathRouteProps.children} props.children n/a
* @param {PathRouteProps.element} props.element n/a
* @param {PathRouteProps.ErrorBoundary} props.ErrorBoundary n/a
* @param {PathRouteProps.errorElement} props.errorElement n/a
* @param {PathRouteProps.handle} props.handle n/a
* @param {PathRouteProps.HydrateFallback} props.HydrateFallback n/a
* @param {PathRouteProps.hydrateFallbackElement} props.hydrateFallbackElement n/a
* @param {PathRouteProps.id} props.id n/a
* @param {PathRouteProps.index} props.index n/a
* @param {PathRouteProps.lazy} props.lazy n/a
* @param {PathRouteProps.loader} props.loader n/a
* @param {PathRouteProps.path} props.path n/a
* @param {PathRouteProps.shouldRevalidate} props.shouldRevalidate n/a
* @returns {void}
*/
declare function Route(props: RouteProps): React.ReactElement | null;
/**
* @category Types
*/
interface RouterProps {
/**
* The base path for the application. This is prepended to all locations
*/
basename?: string;
/**
* Nested {@link Route} elements describing the route tree
*/
children?: React.ReactNode;
/**
* The location to match against. Defaults to the current location.
* This can be a string or a {@link Location} object.
*/
location: Partial<Location> | string;
/**
* The type of navigation that triggered this `location` change.
* Defaults to {@link NavigationType.Pop}.
*/
navigationType?: Action;
/**
* The navigator to use for navigation. This is usually a history object
* or a custom navigator that implements the {@link Navigator} interface.
*/
navigator: Navigator;
/**
* Whether this router is static or not (used for SSR). If `true`, the router
* will not be reactive to location changes.
*/
static?: boolean;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* Provides location context for the rest of the app.
*
* Note: You usually won't render a `<Router>` directly. Instead, you'll render a
* router that is more specific to your environment such as a {@link BrowserRouter}
* in web browsers or a {@link ServerRouter} for server rendering.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {RouterProps.basename} props.basename n/a
* @param {RouterProps.children} props.children n/a
* @param {RouterProps.location} props.location n/a
* @param {RouterProps.navigationType} props.navigationType n/a
* @param {RouterProps.navigator} props.navigator n/a
* @param {RouterProps.static} props.static n/a
* @param {RouterProps.useTransitions} props.useTransitions n/a
* @returns React element for the rendered router or `null` if the location does
* not match the {@link props.basename}
*/
declare function Router({ basename: basenameProp, children, location: locationProp, navigationType, navigator, static: staticProp, useTransitions, }: RouterProps): React.ReactElement | null;
/**
* @category Types
*/
interface RoutesProps {
/**
* Nested {@link Route} elements
*/
children?: React.ReactNode;
/**
* The {@link Location} to match against. Defaults to the current location.
*/
location?: Partial<Location> | string;
}
/**
* Renders a branch of {@link Route | `<Route>`s} that best matches the current
* location. Note that these routes do not participate in [data loading](../../start/framework/route-module#loader),
* [`action`](../../start/framework/route-module#action), code splitting, or
* any other [route module](../../start/framework/route-module) features.
*
* @example
* import { Route, Routes } from "react-router";
*
* <Routes>
* <Route index element={<StepOne />} />
* <Route path="step-2" element={<StepTwo />} />
* <Route path="step-3" element={<StepThree />} />
* </Routes>
*
* @public
* @category Components
* @param props Props
* @param {RoutesProps.children} props.children n/a
* @param {RoutesProps.location} props.location n/a
* @returns React element for the rendered routes or `null` if no route matches
*/
declare function Routes({ children, location, }: RoutesProps): React.ReactElement | null;
interface AwaitResolveRenderFunction<Resolve = any> {
(data: Awaited<Resolve>): React.ReactNode;
}
/**
* @category Types
*/
interface AwaitProps<Resolve> {
/**
* When using a function, the resolved value is provided as the parameter.
*
* ```tsx [2]
* <Await resolve={reviewsPromise}>
* {(resolvedReviews) => <Reviews items={resolvedReviews} />}
* </Await>
* ```
*
* When using React elements, {@link useAsyncValue} will provide the
* resolved value:
*
* ```tsx [2]
* <Await resolve={reviewsPromise}>
* <Reviews />
* </Await>
*
* function Reviews() {
* const resolvedReviews = useAsyncValue();
* return <div>...</div>;
* }
* ```
*/
children: React.ReactNode | AwaitResolveRenderFunction<Resolve>;
/**
* The error element renders instead of the `children` when the [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* rejects.
*
* ```tsx
* <Await
* errorElement={<div>Oops</div>}
* resolve={reviewsPromise}
* >
* <Reviews />
* </Await>
* ```
*
* To provide a more contextual error, you can use the {@link useAsyncError} in a
* child component
*
* ```tsx
* <Await
* errorElement={<ReviewsError />}
* resolve={reviewsPromise}
* >
* <Reviews />
* </Await>
*
* function ReviewsError() {
* const error = useAsyncError();
* return <div>Error loading reviews: {error.message}</div>;
* }
* ```
*
* If you do not provide an `errorElement`, the rejected value will bubble up
* to the nearest route-level [`ErrorBoundary`](../../start/framework/route-module#errorboundary)
* and be accessible via the {@link useRouteError} hook.
*/
errorElement?: React.ReactNode;
/**
* Takes a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* returned from a [`loader`](../../start/framework/route-module#loader) to be
* resolved and rendered.
*
* ```tsx
* import { Await, useLoaderData } from "react-router";
*
* export async function loader() {
* let reviews = getReviews(); // not awaited
* let book = await getBook();
* return {
* book,
* reviews, // this is a promise
* };
* }
*
* export default function Book() {
* const {
* book,
* reviews, // this is the same promise
* } = useLoaderData();
*
* return (
* <div>
* <h1>{book.title}</h1>
* <p>{book.description}</p>
* <React.Suspense fallback={<ReviewsSkeleton />}>
* <Await
* // and is the promise we pass to Await
* resolve={reviews}
* >
* <Reviews />
* </Await>
* </React.Suspense>
* </div>
* );
* }
* ```
*/
resolve: Resolve;
}
/**
* Used to render promise values with automatic error handling.
*
* **Note:** `<Await>` expects to be rendered inside a [`<React.Suspense>`](https://react.dev/reference/react/Suspense)
*
* @example
* import { Await, useLoaderData } from "react-router";
*
* export async function loader() {
* // not awaited
* const reviews = getReviews();
* // awaited (blocks the transition)
* const book = await fetch("/api/book").then((res) => res.json());
* return { book, reviews };
* }
*
* function Book() {
* const { book, reviews } = useLoaderData();
* return (
* <div>
* <h1>{book.title}</h1>
* <p>{book.description}</p>
* <React.Suspense fallback={<ReviewsSkeleton />}>
* <Await
* resolve={reviews}
* errorElement={
* <div>Could not load reviews 😬</div>
* }
* children={(resolvedReviews) => (
* <Reviews items={resolvedReviews} />
* )}
* />
* </React.Suspense>
* </div>
* );
* }
*
* @public
* @category Components
* @mode framework
* @mode data
* @param props Props
* @param {AwaitProps.children} props.children n/a
* @param {AwaitProps.errorElement} props.errorElement n/a
* @param {AwaitProps.resolve} props.resolve n/a
* @returns React element for the rendered awaited value
*/
declare function Await<Resolve>({ children, errorElement, resolve, }: AwaitProps<Resolve>): React.JSX.Element;
/**
* Creates a route config from a React "children" object, which is usually
* either a `<Route>` element or an array of them. Used internally by
* `<Routes>` to create a route config from its children.
*
* @category Utils
* @mode data
* @param children The React children to convert into a route config
* @param parentPath The path of the parent route, used to generate unique IDs.
* @returns An array of {@link RouteObject}s that can be used with a {@link DataRouter}
*/
declare function createRoutesFromChildren(children: React.ReactNode, parentPath?: number[]): RouteObject[];
/**
* Create route objects from JSX elements instead of arrays of objects.
*
* @example
* const routes = createRoutesFromElements(
* <>
* <Route index loader={step1Loader} Component={StepOne} />
* <Route path="step-2" loader={step2Loader} Component={StepTwo} />
* <Route path="step-3" loader={step3Loader} Component={StepThree} />
* </>
* );
*
* const router = createBrowserRouter(routes);
*
* function App() {
* return <RouterProvider router={router} />;
* }
*
* @name createRoutesFromElements
* @public
* @category Utils
* @mode data
* @param children The React children to convert into a route config
* @param parentPath The path of the parent route, used to generate unique IDs.
* This is used for internal recursion and is not intended to be used by the
* application developer.
* @returns An array of {@link RouteObject}s that can be used with a {@link DataRouter}
*/
declare const createRoutesFromElements: typeof createRoutesFromChildren;
/**
* Renders the result of {@link matchRoutes} into a React element.
*
* @public
* @category Utils
* @param matches The array of {@link RouteMatch | route matches} to render
* @returns A React element that renders the matched routes or `null` if no matches
*/
declare function renderMatches(matches: RouteMatch[] | null): React.ReactElement | null;
declare function useRouteComponentProps(): {
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
matches: UIMatch<unknown, unknown>[];
};
type RouteComponentProps = ReturnType<typeof useRouteComponentProps>;
type RouteComponentType = React.ComponentType<RouteComponentProps>;
declare function WithComponentProps({ children, }: {
children: React.ReactElement;
}): React.ReactElement<any, string | React.JSXElementConstructor<any>>;
declare function withComponentProps(Component: RouteComponentType): () => React.ReactElement<{
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
matches: UIMatch<unknown, unknown>[];
}, string | React.JSXElementConstructor<any>>;
declare function useHydrateFallbackProps(): {
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
};
type HydrateFallbackProps = ReturnType<typeof useHydrateFallbackProps>;
type HydrateFallbackType = React.ComponentType<HydrateFallbackProps>;
declare function WithHydrateFallbackProps({ children, }: {
children: React.ReactElement;
}): React.ReactElement<any, string | React.JSXElementConstructor<any>>;
declare function withHydrateFallbackProps(HydrateFallback: HydrateFallbackType): () => React.ReactElement<{
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
}, string | React.JSXElementConstructor<any>>;
declare function useErrorBoundaryProps(): {
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
error: unknown;
};
type ErrorBoundaryProps = ReturnType<typeof useErrorBoundaryProps>;
type ErrorBoundaryType = React.ComponentType<ErrorBoundaryProps>;
declare function WithErrorBoundaryProps({ children, }: {
children: React.ReactElement;
}): React.ReactElement<any, string | React.JSXElementConstructor<any>>;
declare function withErrorBoundaryProps(ErrorBoundary: ErrorBoundaryType): () => React.ReactElement<{
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
error: unknown;
}, string | React.JSXElementConstructor<any>>;
interface DataRouterContextObject extends Omit<NavigationContextObject, "future" | "useTransitions"> {
router: Router$1;
staticContext?: StaticHandlerContext;
onError?: ClientOnErrorFunction;
}
declare const DataRouterContext: React.Context<DataRouterContextObject | null>;
declare const DataRouterStateContext: React.Context<RouterState | null>;
type ViewTransitionContextObject = {
isTransitioning: false;
} | {
isTransitioning: true;
flushSync: boolean;
currentLocation: Location;
nextLocation: Location;
};
declare const ViewTransitionContext: React.Context<ViewTransitionContextObject>;
type FetchersContextObject = Map<string, any>;
declare const FetchersContext: React.Context<FetchersContextObject>;
declare const AwaitContext: React.Context<TrackedPromise | null>;
declare const AwaitContextProvider: (props: React.ComponentProps<typeof AwaitContext.Provider>) => React.FunctionComponentElement<React.ProviderProps<TrackedPromise | null>>;
interface NavigateOptions {
/** Replace the current entry in the history stack instead of pushing a new one */
replace?: boolean;
/** Masked URL */
mask?: To;
/** Adds persistent client side routing state to the next location */
state?: any;
/** If you are using {@link ScrollRestoration `<ScrollRestoration>`}, prevent the scroll position from being reset to the top of the window when navigating */
preventScrollReset?: boolean;
/** Defines the relative path behavior for the link. "route" will use the route hierarchy so ".." will remove all URL segments of the current route pattern while "path" will use the URL path so ".." will remove one URL segment. */
relative?: RelativeRoutingType;
/** Wraps the initial state update for this navigation in a {@link https://react.dev/reference/react-dom/flushSync ReactDOM.flushSync} call instead of the default {@link https://react.dev/reference/react/startTransition React.startTransition} */
flushSync?: boolean;
/** Enables a {@link https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API View Transition} for this navigation by wrapping the final state update in `document.startViewTransition()`. If you need to apply specific styles for this view transition, you will also need to leverage the {@link useViewTransitionState `useViewTransitionState()`} hook. */
viewTransition?: boolean;
/** Specifies the default revalidation behavior after this submission */
defaultShouldRevalidate?: boolean;
}
/**
* A Navigator is a "location changer"; it's how you get to different locations.
*
* Every history instance conforms to the Navigator interface, but the
* distinction is useful primarily when it comes to the low-level `<Router>` API
* where both the location and a navigator must be provided separately in order
* to avoid "tearing" that may occur in a suspense-enabled app if the action
* and/or location were to be read directly from the history instance.
*/
interface Navigator {
createHref: History["createHref"];
encodeLocation?: History["encodeLocation"];
go: History["go"];
push(to: To, state?: any, opts?: NavigateOptions): void;
replace(to: To, state?: any, opts?: NavigateOptions): void;
}
interface NavigationContextObject {
basename: string;
navigator: Navigator;
static: boolean;
useTransitions: boolean | undefined;
future: {};
}
declare const NavigationContext: React.Context<NavigationContextObject>;
interface LocationContextObject {
location: Location;
navigationType: Action;
}
declare const LocationContext: React.Context<LocationContextObject>;
interface RouteContextObject {
outlet: React.ReactElement | null;
matches: RouteMatch[];
isDataRoute: boolean;
}
declare const RouteContext: React.Context<RouteContextObject>;
export { createRoutesFromElements as $, AwaitContextProvider as A, type BlockerFunction as B, type ClientInstrumentation as C, type RouteProps as D, type RouterProps as E, type Fetcher as F, type GetScrollPositionFunction as G, type HydrationState as H, type InstrumentRequestHandlerFunction as I, type RoutesProps as J, Await as K, type LayoutRouteProps as L, type MemoryRouterOpts as M, type NavigateOptions as N, type OutletProps as O, type PathRouteProps as P, MemoryRouter as Q, type RouterInit as R, type StaticHandler as S, Navigate as T, Outlet as U, Route as V, Router as W, RouterProvider as X, Routes as Y, createMemoryRouter as Z, createRoutesFromChildren as _, type RouterProviderProps as a, renderMatches as a0, createRouter as a1, DataRouterContext as a2, DataRouterStateContext as a3, FetchersContext as a4, LocationContext as a5, NavigationContext as a6, RouteContext as a7, ViewTransitionContext as a8, hydrationRouteProperties as a9, mapRouteProperties as aa, WithComponentProps as ab, withComponentProps as ac, WithHydrateFallbackProps as ad, withHydrateFallbackProps as ae, WithErrorBoundaryProps as af, withErrorBoundaryProps as ag, type FutureConfig as ah, type CreateStaticHandlerOptions as ai, type ClientOnErrorFunction as b, type Router$1 as c, type NavigationStates as d, type Blocker as e, type RelativeRoutingType as f, type GetScrollRestorationKeyFunction as g, type StaticHandlerContext as h, type Navigation as i, type RouterState as j, type RouterSubscriber as k, type RouterNavigateOptions as l, type RouterFetchOptions as m, type RevalidationState as n, type ServerInstrumentation as o, type InstrumentRouterFunction as p, type InstrumentRouteFunction as q, type InstrumentationHandlerResult as r, IDLE_NAVIGATION as s, IDLE_FETCHER as t, IDLE_BLOCKER as u, type Navigator as v, type AwaitProps as w, type IndexRouteProps as x, type MemoryRouterProps as y, type NavigateProps as z };
+1740
View File
@@ -0,0 +1,1740 @@
import * as React from 'react';
import { ComponentType, ReactElement } from 'react';
/**
* An augmentable interface users can modify in their app-code to opt into
* future-flag-specific types
*/
interface Future {
}
type MiddlewareEnabled = Future extends {
v8_middleware: infer T extends boolean;
} ? T : false;
/**
* Actions represent the type of change to a location value.
*/
declare enum Action {
/**
* A POP indicates a change to an arbitrary index in the history stack, such
* as a back or forward navigation. It does not describe the direction of the
* navigation, only that the current index changed.
*
* Note: This is the default action for newly created history objects.
*/
Pop = "POP",
/**
* A PUSH indicates a new entry being added to the history stack, such as when
* a link is clicked and a new page loads. When this happens, all subsequent
* entries in the stack are lost.
*/
Push = "PUSH",
/**
* A REPLACE indicates the entry at the current index in the history stack
* being replaced by a new one.
*/
Replace = "REPLACE"
}
/**
* The pathname, search, and hash values of a URL.
*/
interface Path {
/**
* A URL pathname, beginning with a /.
*/
pathname: string;
/**
* A URL search string, beginning with a ?.
*/
search: string;
/**
* A URL fragment identifier, beginning with a #.
*/
hash: string;
}
/**
* An entry in a history stack. A location contains information about the
* URL path, as well as possibly some arbitrary state and a key.
*/
interface Location<State = any> extends Path {
/**
* A value of arbitrary data associated with this location.
*/
state: State;
/**
* A unique string associated with this location. May be used to safely store
* and retrieve data in some other storage API, like `localStorage`.
*
* Note: This value is always "default" on the initial location.
*/
key: string;
/**
* The masked location displayed in the URL bar, which differs from the URL the
* router is operating on
*/
mask?: Path;
}
/**
* A change to the current location.
*/
interface Update {
/**
* The action that triggered the change.
*/
action: Action;
/**
* The new location.
*/
location: Location;
/**
* The delta between this location and the former location in the history stack
*/
delta: number | null;
}
/**
* A function that receives notifications about location changes.
*/
interface Listener {
(update: Update): void;
}
/**
* Describes a location that is the destination of some navigation used in
* {@link Link}, {@link useNavigate}, etc.
*/
type To = string | Partial<Path>;
/**
* A history is an interface to the navigation stack. The history serves as the
* source of truth for the current location, as well as provides a set of
* methods that may be used to change it.
*
* It is similar to the DOM's `window.history` object, but with a smaller, more
* focused API.
*/
interface History {
/**
* The last action that modified the current location. This will always be
* Action.Pop when a history instance is first created. This value is mutable.
*/
readonly action: Action;
/**
* The current location. This value is mutable.
*/
readonly location: Location;
/**
* Returns a valid href for the given `to` value that may be used as
* the value of an <a href> attribute.
*
* @param to - The destination URL
*/
createHref(to: To): string;
/**
* Returns a URL for the given `to` value
*
* @param to - The destination URL
*/
createURL(to: To): URL;
/**
* Encode a location the same way window.history would do (no-op for memory
* history) so we ensure our PUSH/REPLACE navigations for data routers
* behave the same as POP
*
* @param to Unencoded path
*/
encodeLocation(to: To): Path;
/**
* Pushes a new location onto the history stack, increasing its length by one.
* If there were any entries in the stack after the current one, they are
* lost.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
push(to: To, state?: any): void;
/**
* Replaces the current location in the history stack with a new one. The
* location that was replaced will no longer be available.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
replace(to: To, state?: any): void;
/**
* Navigates `n` entries backward/forward in the history stack relative to the
* current index. For example, a "back" navigation would use go(-1).
*
* @param delta - The delta in the stack index
*/
go(delta: number): void;
/**
* Sets up a listener that will be called whenever the current location
* changes.
*
* @param listener - A function that will be called when the location changes
* @returns unlisten - A function that may be used to stop listening
*/
listen(listener: Listener): () => void;
}
/**
* A user-supplied object that describes a location. Used when providing
* entries to `createMemoryHistory` via its `initialEntries` option.
*/
type InitialEntry = string | Partial<Location>;
type MemoryHistoryOptions = {
initialEntries?: InitialEntry[];
initialIndex?: number;
v5Compat?: boolean;
};
/**
* A memory history stores locations in memory. This is useful in stateful
* environments where there is no web browser, such as node tests or React
* Native.
*/
interface MemoryHistory extends History {
/**
* The current index in the history stack.
*/
readonly index: number;
}
/**
* Memory history stores the current location in memory. It is designed for use
* in stateful non-browser environments like tests and React Native.
*/
declare function createMemoryHistory(options?: MemoryHistoryOptions): MemoryHistory;
/**
* A browser history stores the current location in regular URLs in a web
* browser environment. This is the standard for most web apps and provides the
* cleanest URLs the browser's address bar.
*
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#browserhistory
*/
interface BrowserHistory extends UrlHistory {
}
type BrowserHistoryOptions = UrlHistoryOptions;
/**
* Browser history stores the location in regular URLs. This is the standard for
* most web apps, but it requires some configuration on the server to ensure you
* serve the same app at multiple URLs.
*
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory
*/
declare function createBrowserHistory(options?: BrowserHistoryOptions): BrowserHistory;
/**
* A hash history stores the current location in the fragment identifier portion
* of the URL in a web browser environment.
*
* This is ideal for apps that do not control the server for some reason
* (because the fragment identifier is never sent to the server), including some
* shared hosting environments that do not provide fine-grained controls over
* which pages are served at which URLs.
*
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#hashhistory
*/
interface HashHistory extends UrlHistory {
}
type HashHistoryOptions = UrlHistoryOptions;
/**
* Hash history stores the location in window.location.hash. This makes it ideal
* for situations where you don't want to send the location to the server for
* some reason, either because you do cannot configure it or the URL space is
* reserved for something else.
*
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createhashhistory
*/
declare function createHashHistory(options?: HashHistoryOptions): HashHistory;
/**
* @private
*/
declare function invariant(value: boolean, message?: string): asserts value;
declare function invariant<T>(value: T | null | undefined, message?: string): asserts value is T;
/**
* Creates a string URL path from the given pathname, search, and hash components.
*
* @category Utils
*/
declare function createPath({ pathname, search, hash, }: Partial<Path>): string;
/**
* Parses a string URL path into its separate pathname, search, and hash components.
*
* @category Utils
*/
declare function parsePath(path: string): Partial<Path>;
interface UrlHistory extends History {
}
type UrlHistoryOptions = {
window?: Window;
v5Compat?: boolean;
};
type MaybePromise<T> = T | Promise<T>;
/**
* Map of routeId -> data returned from a loader/action/error
*/
interface RouteData {
[routeId: string]: any;
}
type LowerCaseFormMethod = "get" | "post" | "put" | "patch" | "delete";
type UpperCaseFormMethod = Uppercase<LowerCaseFormMethod>;
/**
* Users can specify either lowercase or uppercase form methods on `<Form>`,
* useSubmit(), `<fetcher.Form>`, etc.
*/
type HTMLFormMethod = LowerCaseFormMethod | UpperCaseFormMethod;
/**
* Active navigation/fetcher form methods are exposed in uppercase on the
* RouterState. This is to align with the normalization done via fetch().
*/
type FormMethod = UpperCaseFormMethod;
type FormEncType = "application/x-www-form-urlencoded" | "multipart/form-data" | "application/json" | "text/plain";
type JsonObject = {
[Key in string]: JsonValue;
} & {
[Key in string]?: JsonValue | undefined;
};
type JsonArray = JsonValue[] | readonly JsonValue[];
type JsonPrimitive = string | number | boolean | null;
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
/**
* @private
* Internal interface to pass around for action submissions, not intended for
* external consumption
*/
type Submission = {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: FormData;
json: undefined;
text: undefined;
} | {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: undefined;
json: JsonValue;
text: undefined;
} | {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: undefined;
json: undefined;
text: string;
};
/**
* A context instance used as the key for the `get`/`set` methods of a
* {@link RouterContextProvider}. Accepts an optional default
* value to be returned if no value has been set.
*/
interface RouterContext<T = unknown> {
defaultValue?: T;
}
/**
* Creates a type-safe {@link RouterContext} object that can be used to
* store and retrieve arbitrary values in [`action`](../../start/framework/route-module#action)s,
* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
* Similar to React's [`createContext`](https://react.dev/reference/react/createContext),
* but specifically designed for React Router's request/response lifecycle.
*
* If a `defaultValue` is provided, it will be returned from `context.get()`
* when no value has been set for the context. Otherwise, reading this context
* when no value has been set will throw an error.
*
* ```tsx filename=app/context.ts
* import { createContext } from "react-router";
*
* // Create a context for user data
* export const userContext =
* createContext<User | null>(null);
* ```
*
* ```tsx filename=app/middleware/auth.ts
* import { getUserFromSession } from "~/auth.server";
* import { userContext } from "~/context";
*
* export const authMiddleware = async ({
* context,
* request,
* }) => {
* const user = await getUserFromSession(request);
* context.set(userContext, user);
* };
* ```
*
* ```tsx filename=app/routes/profile.tsx
* import { userContext } from "~/context";
*
* export async function loader({
* context,
* }: Route.LoaderArgs) {
* const user = context.get(userContext);
*
* if (!user) {
* throw new Response("Unauthorized", { status: 401 });
* }
*
* return { user };
* }
* ```
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param defaultValue An optional default value for the context. This value
* will be returned if no value has been set for this context.
* @returns A {@link RouterContext} object that can be used with
* `context.get()` and `context.set()` in [`action`](../../start/framework/route-module#action)s,
* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
*/
declare function createContext<T>(defaultValue?: T): RouterContext<T>;
/**
* Provides methods for writing/reading values in application context in a
* type-safe way. Primarily for usage with [middleware](../../how-to/middleware).
*
* @example
* import {
* createContext,
* RouterContextProvider
* } from "react-router";
*
* const userContext = createContext<User | null>(null);
* const contextProvider = new RouterContextProvider();
* contextProvider.set(userContext, getUser());
* // ^ Type-safe
* const user = contextProvider.get(userContext);
* // ^ User
*
* @public
* @category Utils
* @mode framework
* @mode data
*/
declare class RouterContextProvider {
#private;
/**
* Create a new `RouterContextProvider` instance
* @param init An optional initial context map to populate the provider with
*/
constructor(init?: Map<RouterContext, unknown>);
/**
* Access a value from the context. If no value has been set for the context,
* it will return the context's `defaultValue` if provided, or throw an error
* if no `defaultValue` was set.
* @param context The context to get the value for
* @returns The value for the context, or the context's `defaultValue` if no
* value was set
*/
get<T>(context: RouterContext<T>): T;
/**
* Set a value for the context. If the context already has a value set, this
* will overwrite it.
*
* @param context The context to set the value for
* @param value The value to set for the context
* @returns {void}
*/
set<C extends RouterContext>(context: C, value: C extends RouterContext<infer T> ? T : never): void;
}
type DefaultContext = MiddlewareEnabled extends true ? Readonly<RouterContextProvider> : any;
/**
* @private
* Arguments passed to route loader/action functions. Same for now but we keep
* this as a private implementation detail in case they diverge in the future.
*/
interface DataFunctionArgs<Context> {
/** A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read headers (like cookies, and {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams URLSearchParams} from the request. */
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function loader({
* params,
* }: Route.LoaderArgs) {
* params.teamId;
* // ^ string
* }
*/
params: Params;
/**
* This is the context passed in to your server adapter's getLoadContext() function.
* It's a way to bridge the gap between the adapter's request/response API with your React Router app.
* It is only applicable if you are using a custom server adapter.
*/
context: Context;
}
/**
* Route middleware `next` function to call downstream handlers and then complete
* middlewares from the bottom-up
*/
interface MiddlewareNextFunction<Result = unknown> {
(): Promise<Result>;
}
/**
* Route middleware function signature. Receives the same "data" arguments as a
* `loader`/`action` (`request`, `params`, `context`) as the first parameter and
* a `next` function as the second parameter which will call downstream handlers
* and then complete middlewares from the bottom-up
*/
type MiddlewareFunction<Result = unknown> = (args: DataFunctionArgs<Readonly<RouterContextProvider>>, next: MiddlewareNextFunction<Result>) => MaybePromise<Result | void>;
/**
* Arguments passed to loader functions
*/
interface LoaderFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
}
/**
* Arguments passed to action functions
*/
interface ActionFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
}
/**
* Loaders and actions can return anything
*/
type DataFunctionValue = unknown;
type DataFunctionReturnValue = MaybePromise<DataFunctionValue>;
/**
* Route loader function signature
*/
type LoaderFunction<Context = DefaultContext> = {
(args: LoaderFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue;
} & {
hydrate?: boolean;
};
/**
* Route action function signature
*/
interface ActionFunction<Context = DefaultContext> {
(args: ActionFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue;
}
/**
* Arguments passed to shouldRevalidate function
*/
interface ShouldRevalidateFunctionArgs {
/** This is the url the navigation started from. You can compare it with `nextUrl` to decide if you need to revalidate this route's data. */
currentUrl: URL;
/** These are the {@link https://reactrouter.com/start/framework/routing#dynamic-segments dynamic route params} from the URL that can be compared to the `nextParams` to decide if you need to reload or not. Perhaps you're using only a partial piece of the param for data loading, you don't need to revalidate if a superfluous part of the param changed. */
currentParams: DataRouteMatch["params"];
/** In the case of navigation, this the URL the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentUrl. */
nextUrl: URL;
/** In the case of navigation, these are the {@link https://reactrouter.com/start/framework/routing#dynamic-segments dynamic route params} from the next location the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentParams. */
nextParams: DataRouteMatch["params"];
/** The method (probably `"GET"` or `"POST"`) used in the form submission that triggered the revalidation. */
formMethod?: Submission["formMethod"];
/** The form action (`<Form action="/somewhere">`) that triggered the revalidation. */
formAction?: Submission["formAction"];
/** The form encType (`<Form encType="application/x-www-form-urlencoded">) used in the form submission that triggered the revalidation*/
formEncType?: Submission["formEncType"];
/** The form submission data when the form's encType is `text/plain` */
text?: Submission["text"];
/** The form submission data when the form's encType is `application/x-www-form-urlencoded` or `multipart/form-data` */
formData?: Submission["formData"];
/** The form submission data when the form's encType is `application/json` */
json?: Submission["json"];
/** The status code of the action response */
actionStatus?: number;
/**
* When a submission causes the revalidation this will be the result of the action—either action data or an error if the action failed. It's common to include some information in the action result to instruct shouldRevalidate to revalidate or not.
*
* @example
* export async function action() {
* await saveSomeStuff();
* return { ok: true };
* }
*
* export function shouldRevalidate({
* actionResult,
* }) {
* if (actionResult?.ok) {
* return false;
* }
* return true;
* }
*/
actionResult?: any;
/**
* By default, React Router doesn't call every loader all the time. There are reliable optimizations it can make by default. For example, only loaders with changing params are called. Consider navigating from the following URL to the one below it:
*
* /projects/123/tasks/abc
* /projects/123/tasks/def
* React Router will only call the loader for tasks/def because the param for projects/123 didn't change.
*
* It's safest to always return defaultShouldRevalidate after you've done your specific optimizations that return false, otherwise your UI might get out of sync with your data on the server.
*/
defaultShouldRevalidate: boolean;
}
/**
* Route shouldRevalidate function signature. This runs after any submission
* (navigation or fetcher), so we flatten the navigation/fetcher submission
* onto the arguments. It shouldn't matter whether it came from a navigation
* or a fetcher, what really matters is the URLs and the formData since loaders
* have to re-run based on the data models that were potentially mutated.
*/
interface ShouldRevalidateFunction {
(args: ShouldRevalidateFunctionArgs): boolean;
}
interface DataStrategyMatch extends RouteMatch<string, DataRouteObject> {
/**
* @private
*/
_lazyPromises?: {
middleware: Promise<void> | undefined;
handler: Promise<void> | undefined;
route: Promise<void> | undefined;
};
/**
* @deprecated Deprecated in favor of `shouldCallHandler`
*
* A boolean value indicating whether this route handler should be called in
* this pass.
*
* The `matches` array always includes _all_ matched routes even when only
* _some_ route handlers need to be called so that things like middleware can
* be implemented.
*
* `shouldLoad` is usually only interesting if you are skipping the route
* handler entirely and implementing custom handler logic - since it lets you
* determine if that custom logic should run for this route or not.
*
* For example:
* - If you are on `/parent/child/a` and you navigate to `/parent/child/b` -
* you'll get an array of three matches (`[parent, child, b]`), but only `b`
* will have `shouldLoad=true` because the data for `parent` and `child` is
* already loaded
* - If you are on `/parent/child/a` and you submit to `a`'s [`action`](https://reactrouter.com/docs/start/data/route-object#action),
* then only `a` will have `shouldLoad=true` for the action execution of
* `dataStrategy`
* - After the [`action`](https://reactrouter.com/docs/start/data/route-object#action),
* `dataStrategy` will be called again for the [`loader`](https://reactrouter.com/docs/start/data/route-object#loader)
* revalidation, and all matches will have `shouldLoad=true` (assuming no
* custom `shouldRevalidate` implementations)
*/
shouldLoad: boolean;
/**
* Arguments passed to the `shouldRevalidate` function for this `loader` execution.
* Will be `null` if this is not a revalidating loader {@link DataStrategyMatch}.
*/
shouldRevalidateArgs: ShouldRevalidateFunctionArgs | null;
/**
* Determine if this route's handler should be called during this `dataStrategy`
* execution. Calling it with no arguments will leverage the default revalidation
* behavior. You can pass your own `defaultShouldRevalidate` value if you wish
* to change the default revalidation behavior with your `dataStrategy`.
*
* @param defaultShouldRevalidate `defaultShouldRevalidate` override value (optional)
*/
shouldCallHandler(defaultShouldRevalidate?: boolean): boolean;
/**
* An async function that will resolve any `route.lazy` implementations and
* execute the route's handler (if necessary), returning a {@link DataStrategyResult}
*
* - Calling `match.resolve` does not mean you're calling the
* [`action`](https://reactrouter.com/docs/start/data/route-object#action)/[`loader`](https://reactrouter.com/docs/start/data/route-object#loader)
* (the "handler") - `resolve` will only call the `handler` internally if
* needed _and_ if you don't pass your own `handlerOverride` function parameter
* - It is safe to call `match.resolve` for all matches, even if they have
* `shouldLoad=false`, and it will no-op if no loading is required
* - You should generally always call `match.resolve()` for `shouldLoad:true`
* routes to ensure that any `route.lazy` implementations are processed
* - See the examples below for how to implement custom handler execution via
* `match.resolve`
*/
resolve: (handlerOverride?: (handler: (ctx?: unknown) => DataFunctionReturnValue) => DataFunctionReturnValue) => Promise<DataStrategyResult>;
}
interface DataStrategyFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
/**
* Matches for this route extended with Data strategy APIs
*/
matches: DataStrategyMatch[];
runClientMiddleware: (cb: DataStrategyFunction<Context>) => Promise<Record<string, DataStrategyResult>>;
/**
* The key of the fetcher we are calling `dataStrategy` for, otherwise `null`
* for navigational executions
*/
fetcherKey: string | null;
}
/**
* Result from a loader or action called via dataStrategy
*/
interface DataStrategyResult {
type: "data" | "error";
result: unknown;
}
interface DataStrategyFunction<Context = DefaultContext> {
(args: DataStrategyFunctionArgs<Context>): Promise<Record<string, DataStrategyResult>>;
}
type PatchRoutesOnNavigationFunctionArgs = {
signal: AbortSignal;
path: string;
matches: RouteMatch[];
fetcherKey: string | undefined;
patch: (routeId: string | null, children: RouteObject[]) => void;
};
type PatchRoutesOnNavigationFunction = (opts: PatchRoutesOnNavigationFunctionArgs) => MaybePromise<void>;
/**
* Function provided to set route-specific properties from route objects
*/
interface MapRoutePropertiesFunction {
(route: DataRouteObject): {
hasErrorBoundary: boolean;
} & Record<string, any>;
}
/**
* Keys we cannot change from within a lazy object. We spread all other keys
* onto the route. Either they're meaningful to the router, or they'll get
* ignored.
*/
type UnsupportedLazyRouteObjectKey = "lazy" | "caseSensitive" | "path" | "id" | "index" | "children";
/**
* Keys we cannot change from within a lazy() function. We spread all other keys
* onto the route. Either they're meaningful to the router, or they'll get
* ignored.
*/
type UnsupportedLazyRouteFunctionKey = UnsupportedLazyRouteObjectKey | "middleware";
/**
* lazy object to load route properties, which can add non-matching
* related properties to a route
*/
type LazyRouteObject<R extends RouteObject> = {
[K in keyof R as K extends UnsupportedLazyRouteObjectKey ? never : K]?: () => Promise<R[K] | null | undefined>;
};
/**
* lazy() function to load a route definition, which can add non-matching
* related properties to a route
*/
interface LazyRouteFunction<R extends RouteObject> {
(): Promise<Omit<R, UnsupportedLazyRouteFunctionKey> & Partial<Record<UnsupportedLazyRouteFunctionKey, never>>>;
}
type LazyRouteDefinition<R extends RouteObject> = LazyRouteObject<R> | LazyRouteFunction<R>;
/**
* Base RouteObject with common props shared by all types of routes
* @internal
*/
type BaseRouteObject = {
/**
* Whether the path should be case-sensitive. Defaults to `false`.
*/
caseSensitive?: boolean;
/**
* The path pattern to match. If unspecified or empty, then this becomes a
* layout route.
*/
path?: string;
/**
* The unique identifier for this route (for use with {@link DataRouter}s)
*/
id?: string;
/**
* The route middleware.
* See [`middleware`](../../start/data/route-object#middleware).
*/
middleware?: MiddlewareFunction[];
/**
* The route loader.
* See [`loader`](../../start/data/route-object#loader).
*/
loader?: LoaderFunction | boolean;
/**
* The route action.
* See [`action`](../../start/data/route-object#action).
*/
action?: ActionFunction | boolean;
hasErrorBoundary?: boolean;
/**
* The route shouldRevalidate function.
* See [`shouldRevalidate`](../../start/data/route-object#shouldRevalidate).
*/
shouldRevalidate?: ShouldRevalidateFunction;
/**
* The route handle.
*/
handle?: any;
/**
* A function that returns a promise that resolves to the route object.
* Used for code-splitting routes.
* See [`lazy`](../../start/data/route-object#lazy).
*/
lazy?: LazyRouteDefinition<BaseRouteObject>;
/**
* The React Component to render when this route matches.
* Mutually exclusive with `element`.
*/
Component?: React.ComponentType | null;
/**
* The React element to render when this Route matches.
* Mutually exclusive with `Component`.
*/
element?: React.ReactNode | null;
/**
* The React Component to render at this route if an error occurs.
* Mutually exclusive with `errorElement`.
*/
ErrorBoundary?: React.ComponentType | null;
/**
* The React element to render at this route if an error occurs.
* Mutually exclusive with `ErrorBoundary`.
*/
errorElement?: React.ReactNode | null;
/**
* The React Component to render while this router is loading data.
* Mutually exclusive with `hydrateFallbackElement`.
*/
HydrateFallback?: React.ComponentType | null;
/**
* The React element to render while this router is loading data.
* Mutually exclusive with `HydrateFallback`.
*/
hydrateFallbackElement?: React.ReactNode | null;
};
/**
* Index routes must not have children
*/
type IndexRouteObject = BaseRouteObject & {
/**
* Child Route objects - not valid on index routes.
*/
children?: undefined;
/**
* Whether this is an index route.
*/
index: true;
};
/**
* Non-index routes may have children, but cannot have `index` set to `true`.
*/
type NonIndexRouteObject = BaseRouteObject & {
/**
* Child Route objects.
*/
children?: RouteObject[];
/**
* Whether this is an index route - must be `false` or undefined on non-index routes.
*/
index?: false;
};
/**
* A route object represents a logical route, with (optionally) its child
* routes organized in a tree-like structure.
*/
type RouteObject = IndexRouteObject | NonIndexRouteObject;
type DataIndexRouteObject = IndexRouteObject & {
id: string;
};
type DataNonIndexRouteObject = NonIndexRouteObject & {
children?: DataRouteObject[];
id: string;
};
/**
* A data route object, which is just a RouteObject with a required unique ID
*/
type DataRouteObject = DataIndexRouteObject | DataNonIndexRouteObject;
type RouteManifest<R = DataRouteObject> = Record<string, R | undefined>;
type Regex_az = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
type Regex_AZ = Uppercase<Regex_az>;
type Regex_09 = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
type Regex_w = Regex_az | Regex_AZ | Regex_09 | "_";
/** Emulates Regex `+` operator */
type RegexMatchPlus<char extends string, T extends string> = _RegexMatchPlus<char, T> extends infer result extends string ? result extends '' ? never : result : never;
type _RegexMatchPlus<char extends string, T extends string> = T extends `${infer head extends char}${infer rest}` ? `${head}${_RegexMatchPlus<char, rest>}` : '';
type ParamNameChar = Regex_w | "-";
type Simplify<T> = {
[K in keyof T]: T[K];
} & {};
type GeneratePathParams<path extends string> = Simplify<ParseParams<path> & {
[key in string]: string | null | undefined;
}>;
type ParseParams<path extends string> = path extends '*' ? {
'*': string;
} : path extends `${infer rest}/*` ? {
'*': string;
} & ParseParams<rest> : _ParseParams<path>;
type _ParseParams<path extends string> = path extends `${infer left}/${infer right}` ? _ParseParams<left> & _ParseParams<right> : path extends `:${infer param}?${string}` ? {
[key in RegexMatchPlus<ParamNameChar, param>]?: string | null | undefined;
} : path extends `:${infer param}` ? {
[key in RegexMatchPlus<ParamNameChar, param>]: string;
} : {};
type PathParam<path extends string> = (keyof ParseParams<path>) & string;
type ParamParseKey<Segment extends string> = [
PathParam<Segment>
] extends [never] ? string : PathParam<Segment>;
/**
* The parameters that were parsed from the URL path.
*/
type Params<Key extends string = string> = {
readonly [key in Key]: string | undefined;
};
/**
* A RouteMatch contains info about how a route matched a URL.
*/
interface RouteMatch<ParamKey extends string = string, RouteObjectType extends RouteObject = RouteObject> {
/**
* The names and values of dynamic parameters in the URL.
*/
params: Params<ParamKey>;
/**
* The portion of the URL pathname that was matched.
*/
pathname: string;
/**
* The portion of the URL pathname that was matched before child routes.
*/
pathnameBase: string;
/**
* The route object that was used to match.
*/
route: RouteObjectType;
}
interface DataRouteMatch extends RouteMatch<string, DataRouteObject> {
}
/**
* Matches the given routes to a location and returns the match data.
*
* @example
* import { matchRoutes } from "react-router";
*
* let routes = [{
* path: "/",
* Component: Root,
* children: [{
* path: "dashboard",
* Component: Dashboard,
* }]
* }];
*
* matchRoutes(routes, "/dashboard"); // [rootMatch, dashboardMatch]
*
* @public
* @category Utils
* @param routes The array of route objects to match against.
* @param locationArg The location to match against, either a string path or a
* partial {@link Location} object
* @param basename Optional base path to strip from the location before matching.
* Defaults to `/`.
* @returns An array of matched routes, or `null` if no matches were found.
*/
declare function matchRoutes<RouteObjectType extends RouteObject = RouteObject>(routes: RouteObjectType[], locationArg: Partial<Location> | string, basename?: string): RouteMatch<string, RouteObjectType>[] | null;
interface UIMatch<Data = unknown, Handle = unknown> {
id: string;
pathname: string;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the matched route.
*/
params: RouteMatch["params"];
/**
* The return value from the matched route's loader or clientLoader. This might
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
* an error and we're currently displaying an `ErrorBoundary`.
*
* @deprecated Use `UIMatch.loaderData` instead
*/
data: Data | undefined;
/**
* The return value from the matched route's loader or clientLoader. This might
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
* an error and we're currently displaying an `ErrorBoundary`.
*/
loaderData: Data | undefined;
/**
* The {@link https://reactrouter.com/start/framework/route-module#handle handle object}
* exported from the matched route module
*/
handle: Handle;
}
interface RouteMeta<RouteObjectType extends RouteObject = RouteObject> {
relativePath: string;
caseSensitive: boolean;
childrenIndex: number;
route: RouteObjectType;
matcher?: RegExp;
compiledParams?: CompiledPathParam[];
}
/**
* @private
* PRIVATE - DO NOT USE
*
* A "branch" of routes that match a given route pattern.
* This is an internal interface not intended for direct external usage.
*/
interface RouteBranch<RouteObjectType extends RouteObject = RouteObject> {
path: string;
score: number;
routesMeta: RouteMeta<RouteObjectType>[];
}
/**
* Returns a path with params interpolated.
*
* @example
* import { generatePath } from "react-router";
*
* generatePath("/users/:id", { id: "123" }); // "/users/123"
*
* @public
* @category Utils
* @param originalPath The original path to generate.
* @param params The parameters to interpolate into the path.
* @returns The generated path with parameters interpolated.
*/
declare function generatePath<Path extends string>(originalPath: Path, params?: GeneratePathParams<Path>): string;
/**
* Used to match on some portion of a URL pathname.
*/
interface PathPattern<Path extends string = string> {
/**
* A string to match against a URL pathname. May contain `:id`-style segments
* to indicate placeholders for dynamic parameters. It May also end with `/*`
* to indicate matching the rest of the URL pathname.
*/
path: Path;
/**
* Should be `true` if the static portions of the `path` should be matched in
* the same case.
*/
caseSensitive?: boolean;
/**
* Should be `true` if this pattern should match the entire URL pathname.
*/
end?: boolean;
}
/**
* Contains info about how a {@link PathPattern} matched on a URL pathname.
*/
interface PathMatch<ParamKey extends string = string> {
/**
* The names and values of dynamic parameters in the URL.
*/
params: Params<ParamKey>;
/**
* The portion of the URL pathname that was matched.
*/
pathname: string;
/**
* The portion of the URL pathname that was matched before child routes.
*/
pathnameBase: string;
/**
* The pattern that was used to match.
*/
pattern: PathPattern;
}
/**
* Performs pattern matching on a URL pathname and returns information about
* the match.
*
* @public
* @category Utils
* @param pattern The pattern to match against the URL pathname. This can be a
* string or a {@link PathPattern} object. If a string is provided, it will be
* treated as a pattern with `caseSensitive` set to `false` and `end` set to
* `true`.
* @param pathname The URL pathname to match against the pattern.
* @returns A path match object if the pattern matches the pathname,
* or `null` if it does not match.
*/
declare function matchPath<Path extends string>(pattern: PathPattern<Path> | Path, pathname: string): PathMatch<ParamParseKey<Path>> | null;
type CompiledPathParam = {
paramName: string;
isOptional?: boolean;
};
/**
* Returns a resolved {@link Path} object relative to the given pathname.
*
* @public
* @category Utils
* @param to The path to resolve, either a string or a partial {@link Path}
* object.
* @param fromPathname The pathname to resolve the path from. Defaults to `/`.
* @returns A {@link Path} object with the resolved pathname, search, and hash.
*/
declare function resolvePath(to: To, fromPathname?: string): Path;
declare class DataWithResponseInit<D> {
type: string;
data: D;
init: ResponseInit | null;
constructor(data: D, init?: ResponseInit);
}
/**
* Create "responses" that contain `headers`/`status` without forcing
* serialization into an actual [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
*
* @example
* import { data } from "react-router";
*
* export async function action({ request }: Route.ActionArgs) {
* let formData = await request.formData();
* let item = await createItem(formData);
* return data(item, {
* headers: { "X-Custom-Header": "value" }
* status: 201,
* });
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param data The data to be included in the response.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A {@link DataWithResponseInit} instance containing the data and
* response init.
*/
declare function data<D>(data: D, init?: number | ResponseInit): DataWithResponseInit<D>;
interface TrackedPromise extends Promise<any> {
_tracked?: boolean;
_data?: any;
_error?: any;
}
type RedirectFunction = (url: string, init?: number | ResponseInit) => Response;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
* Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* This utility accepts absolute URLs and can navigate to external domains, so
* the application should validate any user-supplied inputs to redirects.
*
* @example
* import { redirect } from "react-router";
*
* export async function loader({ request }: Route.LoaderArgs) {
* if (!isLoggedIn(request))
* throw redirect("/login");
* }
*
* // ...
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const redirect: RedirectFunction;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that will force a document reload to the new location. Sets the status code
* and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* This utility accepts absolute URLs and can navigate to external domains, so
* the application should validate any user-supplied inputs to redirects.
*
* ```tsx filename=routes/logout.tsx
* import { redirectDocument } from "react-router";
*
* import { destroySession } from "../sessions.server";
*
* export async function action({ request }: Route.ActionArgs) {
* let session = await getSession(request.headers.get("Cookie"));
* return redirectDocument("/", {
* headers: { "Set-Cookie": await destroySession(session) }
* });
* }
* ```
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const redirectDocument: RedirectFunction;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that will perform a [`history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState)
* instead of a [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)
* for client-side navigation redirects. Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* @example
* import { replace } from "react-router";
*
* export async function loader() {
* return replace("/new-location");
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const replace: RedirectFunction;
type ErrorResponse = {
status: number;
statusText: string;
data: any;
};
declare class ErrorResponseImpl implements ErrorResponse {
status: number;
statusText: string;
data: any;
private error?;
private internal;
constructor(status: number, statusText: string | undefined, data: any, internal?: boolean);
}
/**
* Check if the given error is an {@link ErrorResponse} generated from a 4xx/5xx
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* thrown from an [`action`](../../start/framework/route-module#action) or
* [`loader`](../../start/framework/route-module#loader) function.
*
* @example
* import { isRouteErrorResponse } from "react-router";
*
* export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
* if (isRouteErrorResponse(error)) {
* return (
* <>
* <p>Error: `${error.status}: ${error.statusText}`</p>
* <p>{error.data}</p>
* </>
* );
* }
*
* return (
* <p>Error: {error instanceof Error ? error.message : "Unknown Error"}</p>
* );
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param error The error to check.
* @returns `true` if the error is an {@link ErrorResponse}, `false` otherwise.
*/
declare function isRouteErrorResponse(error: any): error is ErrorResponse;
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>);
interface HtmlLinkProps {
/**
* Address of the hyperlink
*/
href?: string;
/**
* How the element handles crossorigin requests
*/
crossOrigin?: "anonymous" | "use-credentials";
/**
* Relationship between the document containing the hyperlink and the destination resource
*/
rel: LiteralUnion<"alternate" | "dns-prefetch" | "icon" | "manifest" | "modulepreload" | "next" | "pingback" | "preconnect" | "prefetch" | "preload" | "prerender" | "search" | "stylesheet", string>;
/**
* Applicable media: "screen", "print", "(max-width: 764px)"
*/
media?: string;
/**
* Integrity metadata used in Subresource Integrity checks
*/
integrity?: string;
/**
* Language of the linked resource
*/
hrefLang?: string;
/**
* Hint for the type of the referenced resource
*/
type?: string;
/**
* Referrer policy for fetches initiated by the element
*/
referrerPolicy?: "" | "no-referrer" | "no-referrer-when-downgrade" | "same-origin" | "origin" | "strict-origin" | "origin-when-cross-origin" | "strict-origin-when-cross-origin" | "unsafe-url";
/**
* Sizes of the icons (for rel="icon")
*/
sizes?: string;
/**
* Potential destination for a preload request (for rel="preload" and rel="modulepreload")
*/
as?: LiteralUnion<"audio" | "audioworklet" | "document" | "embed" | "fetch" | "font" | "frame" | "iframe" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt", string>;
/**
* Color to use when customizing a site's icon (for rel="mask-icon")
*/
color?: string;
/**
* Whether the link is disabled
*/
disabled?: boolean;
/**
* The title attribute has special semantics on this element: Title of the link; CSS style sheet set name.
*/
title?: string;
/**
* Images to use in different situations, e.g., high-resolution displays,
* small monitors, etc. (for rel="preload")
*/
imageSrcSet?: string;
/**
* Image sizes for different page layouts (for rel="preload")
*/
imageSizes?: string;
}
interface HtmlLinkPreloadImage extends HtmlLinkProps {
/**
* Relationship between the document containing the hyperlink and the destination resource
*/
rel: "preload";
/**
* Potential destination for a preload request (for rel="preload" and rel="modulepreload")
*/
as: "image";
/**
* Address of the hyperlink
*/
href?: string;
/**
* Images to use in different situations, e.g., high-resolution displays,
* small monitors, etc. (for rel="preload")
*/
imageSrcSet: string;
/**
* Image sizes for different page layouts (for rel="preload")
*/
imageSizes?: string;
}
/**
* Represents a `<link>` element.
*
* WHATWG Specification: https://html.spec.whatwg.org/multipage/semantics.html#the-link-element
*/
type HtmlLinkDescriptor = (HtmlLinkProps & Pick<Required<HtmlLinkProps>, "href">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "imageSizes">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "href"> & {
imageSizes?: never;
});
interface PageLinkDescriptor extends Omit<HtmlLinkDescriptor, "href" | "rel" | "type" | "sizes" | "imageSrcSet" | "imageSizes" | "as" | "color" | "title"> {
/**
* A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* element. If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*/
nonce?: string | undefined;
/**
* The absolute path of the page to prefetch, e.g. `/absolute/path`.
*/
page: string;
}
type LinkDescriptor = HtmlLinkDescriptor | PageLinkDescriptor;
type Serializable = undefined | null | boolean | string | symbol | number | Array<Serializable> | {
[key: PropertyKey]: Serializable;
} | bigint | Date | URL | RegExp | Error | Map<Serializable, Serializable> | Set<Serializable> | Promise<Serializable>;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;
type IsAny<T> = 0 extends 1 & T ? true : false;
type Func = (...args: any[]) => unknown;
type Pretty<T> = {
[K in keyof T]: T[K];
} & {};
type Normalize<T> = _Normalize<UnionKeys<T>, T>;
type _Normalize<Key extends keyof any, T> = T extends infer U ? Pretty<{
[K in Key as K extends keyof U ? undefined extends U[K] ? never : K : never]: K extends keyof U ? U[K] : never;
} & {
[K in Key as K extends keyof U ? undefined extends U[K] ? K : never : never]?: K extends keyof U ? U[K] : never;
} & {
[K in Key as K extends keyof U ? never : K]?: undefined;
}> : never;
type UnionKeys<T> = T extends any ? keyof T : never;
type RouteModule$1 = {
meta?: Func;
links?: Func;
headers?: Func;
loader?: Func;
clientLoader?: Func;
action?: Func;
clientAction?: Func;
HydrateFallback?: Func;
default?: Func;
ErrorBoundary?: Func;
[key: string]: unknown;
};
/**
* A brand that can be applied to a type to indicate that it will serialize
* to a specific type when transported to the client from a loader.
* Only use this if you have additional serialization/deserialization logic
* in your application.
*/
type unstable_SerializesTo<T> = {
unstable__ReactRouter_SerializesTo: [T];
};
type Serialize<T> = T extends unstable_SerializesTo<infer To> ? To : T extends Serializable ? T : T extends (...args: any[]) => unknown ? undefined : T extends Promise<infer U> ? Promise<Serialize<U>> : T extends Map<infer K, infer V> ? Map<Serialize<K>, Serialize<V>> : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<Serialize<K>, Serialize<V>> : T extends Set<infer U> ? Set<Serialize<U>> : T extends ReadonlySet<infer U> ? ReadonlySet<Serialize<U>> : T extends [] ? [] : T extends readonly [infer F, ...infer R] ? [Serialize<F>, ...Serialize<R>] : T extends Array<infer U> ? Array<Serialize<U>> : T extends readonly unknown[] ? readonly Serialize<T[number]>[] : T extends Record<any, any> ? {
[K in keyof T]: Serialize<T[K]>;
} : undefined;
type VoidToUndefined<T> = Equal<T, void> extends true ? undefined : T;
type DataFrom<T> = IsAny<T> extends true ? undefined : T extends Func ? VoidToUndefined<Awaited<ReturnType<T>>> : undefined;
type ClientData<T> = T extends Response ? never : T extends DataWithResponseInit<infer U> ? U : T;
type ServerData<T> = T extends Response ? never : T extends DataWithResponseInit<infer U> ? Serialize<U> : Serialize<T>;
type ServerDataFrom<T> = ServerData<DataFrom<T>>;
type ClientDataFrom<T> = ClientData<DataFrom<T>>;
type ClientDataFunctionArgs<Params> = {
/**
* A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read the URL, the method, the "content-type" header, and the request body from the request.
*
* @note Because client data functions are called before a network request is made, the Request object does not include the headers which the browser automatically adds. React Router infers the "content-type" header from the enc-type of the form that performed the submission.
**/
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function clientLoader({
* params,
* }: Route.ClientLoaderArgs) {
* params.teamId;
* // ^ string
* }
**/
params: Params;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* When `future.v8_middleware` is not enabled, this is undefined.
*
* When `future.v8_middleware` is enabled, this is an instance of
* `RouterContextProvider` and can be used to access context values
* from your route middlewares. You may pass in initial context values in your
* `<HydratedRouter getContext>` prop
*/
context: Readonly<RouterContextProvider>;
};
type ServerDataFunctionArgs<Params> = {
/** A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read the url, method, headers (such as cookies), and request body from the request. */
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function loader({
* params,
* }: Route.LoaderArgs) {
* params.teamId;
* // ^ string
* }
**/
params: Params;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* Without `future.v8_middleware` enabled, this is the context passed in
* to your server adapter's `getLoadContext` function. It's a way to bridge the
* gap between the adapter's request/response API with your React Router app.
* It is only applicable if you are using a custom server adapter.
*
* With `future.v8_middleware` enabled, this is an instance of
* `RouterContextProvider` and can be used for type-safe access to
* context value set in your route middlewares. If you are using a custom
* server adapter, you may provide an initial set of context values from your
* `getLoadContext` function.
*/
context: MiddlewareEnabled extends true ? Readonly<RouterContextProvider> : AppLoadContext;
};
type SerializeFrom<T> = T extends (...args: infer Args) => unknown ? Args extends [
ClientLoaderFunctionArgs | ClientActionFunctionArgs | ClientDataFunctionArgs<unknown>
] ? ClientDataFrom<T> : ServerDataFrom<T> : T;
type IsDefined<T> = Equal<T, undefined> extends true ? false : true;
type IsHydrate<ClientLoader> = ClientLoader extends {
hydrate: true;
} ? true : ClientLoader extends {
hydrate: false;
} ? false : false;
type GetLoaderData<T extends RouteModule$1> = _DataLoaderData<ServerDataFrom<T["loader"]>, ClientDataFrom<T["clientLoader"]>, IsHydrate<T["clientLoader"]>, T extends {
HydrateFallback: Func;
} ? true : false>;
type _DataLoaderData<ServerLoaderData, ClientLoaderData, ClientLoaderHydrate extends boolean, HasHydrateFallback> = [
HasHydrateFallback,
ClientLoaderHydrate
] extends [true, true] ? IsDefined<ClientLoaderData> extends true ? ClientLoaderData : undefined : [
IsDefined<ClientLoaderData>,
IsDefined<ServerLoaderData>
] extends [true, true] ? ServerLoaderData | ClientLoaderData : IsDefined<ClientLoaderData> extends true ? ClientLoaderData : IsDefined<ServerLoaderData> extends true ? ServerLoaderData : undefined;
type GetActionData<T extends RouteModule$1> = _DataActionData<ServerDataFrom<T["action"]>, ClientDataFrom<T["clientAction"]>>;
type _DataActionData<ServerActionData, ClientActionData> = Awaited<[
IsDefined<ServerActionData>,
IsDefined<ClientActionData>
] extends [true, true] ? ServerActionData | ClientActionData : IsDefined<ClientActionData> extends true ? ClientActionData : IsDefined<ServerActionData> extends true ? ServerActionData : undefined>;
interface RouteModules {
[routeId: string]: RouteModule | undefined;
}
/**
* The shape of a route module shipped to the client
*/
interface RouteModule {
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
clientMiddleware?: MiddlewareFunction<Record<string, DataStrategyResult>>[];
ErrorBoundary?: ErrorBoundaryComponent;
HydrateFallback?: HydrateFallbackComponent;
Layout?: LayoutComponent;
default: RouteComponent;
handle?: RouteHandle;
links?: LinksFunction;
meta?: MetaFunction;
shouldRevalidate?: ShouldRevalidateFunction;
}
/**
* The shape of a route module on the server
*/
interface ServerRouteModule extends RouteModule {
action?: ActionFunction;
headers?: HeadersFunction | {
[name: string]: string;
};
loader?: LoaderFunction;
middleware?: MiddlewareFunction<Response>[];
}
/**
* A function that handles data mutations for a route on the client
*/
type ClientActionFunction = (args: ClientActionFunctionArgs) => ReturnType<ActionFunction>;
/**
* Arguments passed to a route `clientAction` function
*/
type ClientActionFunctionArgs = ActionFunctionArgs & {
serverAction: <T = unknown>() => Promise<SerializeFrom<T>>;
};
/**
* A function that loads data for a route on the client
*/
type ClientLoaderFunction = ((args: ClientLoaderFunctionArgs) => ReturnType<LoaderFunction>) & {
hydrate?: boolean;
};
/**
* Arguments passed to a route `clientLoader` function
*/
type ClientLoaderFunctionArgs = LoaderFunctionArgs & {
serverLoader: <T = unknown>() => Promise<SerializeFrom<T>>;
};
/**
* ErrorBoundary to display for this route
*/
type ErrorBoundaryComponent = ComponentType;
type HeadersArgs = {
loaderHeaders: Headers;
parentHeaders: Headers;
actionHeaders: Headers;
errorHeaders: Headers | undefined;
};
/**
* A function that returns HTTP headers to be used for a route. These headers
* will be merged with (and take precedence over) headers from parent routes.
*/
interface HeadersFunction {
(args: HeadersArgs): Headers | HeadersInit;
}
/**
* `<Route HydrateFallback>` component to render on initial loads
* when client loaders are present
*/
type HydrateFallbackComponent = ComponentType;
/**
* Optional, root-only `<Route Layout>` component to wrap the root content in.
* Useful for defining the <html>/<head>/<body> document shell shared by the
* Component, HydrateFallback, and ErrorBoundary
*/
type LayoutComponent = ComponentType<{
children: ReactElement<unknown, ErrorBoundaryComponent | HydrateFallbackComponent | RouteComponent>;
}>;
/**
* A function that defines `<link>` tags to be inserted into the `<head>` of
* the document on route transitions.
*
* @see https://reactrouter.com/start/framework/route-module#meta
*/
interface LinksFunction {
(): LinkDescriptor[];
}
interface MetaMatch<RouteId extends string = string, Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown> {
id: RouteId;
pathname: DataRouteMatch["pathname"];
/** @deprecated Use `MetaMatch.loaderData` instead */
data: Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown;
loaderData: Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown;
handle?: RouteHandle;
params: DataRouteMatch["params"];
meta: MetaDescriptor[];
error?: unknown;
}
type MetaMatches<MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> = Array<{
[K in keyof MatchLoaders]: MetaMatch<Exclude<K, number | symbol>, MatchLoaders[K]>;
}[keyof MatchLoaders]>;
interface MetaArgs<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> {
/** @deprecated Use `MetaArgs.loaderData` instead */
data: (Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown) | undefined;
loaderData: (Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown) | undefined;
params: Params;
location: Location;
matches: MetaMatches<MatchLoaders>;
error?: unknown;
}
/**
* A function that returns an array of data objects to use for rendering
* metadata HTML tags in a route. These tags are not rendered on descendant
* routes in the route hierarchy. In other words, they will only be rendered on
* the route in which they are exported.
*
* @param Loader - The type of the current route's loader function
* @param MatchLoaders - Mapping from a parent route's filepath to its loader
* function type
*
* Note that parent route filepaths are relative to the `app/` directory.
*
* For example, if this meta function is for `/sales/customers/$customerId`:
*
* ```ts
* // app/root.tsx
* const loader = () => ({ hello: "world" })
* export type Loader = typeof loader
*
* // app/routes/sales.tsx
* const loader = () => ({ salesCount: 1074 })
* export type Loader = typeof loader
*
* // app/routes/sales/customers.tsx
* const loader = () => ({ customerCount: 74 })
* export type Loader = typeof loader
*
* // app/routes/sales/customers/$customersId.tsx
* import type { Loader as RootLoader } from "../../../root"
* import type { Loader as SalesLoader } from "../../sales"
* import type { Loader as CustomersLoader } from "../../sales/customers"
*
* const loader = () => ({ name: "Customer name" })
*
* const meta: MetaFunction<typeof loader, {
* "root": RootLoader,
* "routes/sales": SalesLoader,
* "routes/sales/customers": CustomersLoader,
* }> = ({ data, matches }) => {
* const { name } = data
* // ^? string
* const { customerCount } = matches.find((match) => match.id === "routes/sales/customers").data
* // ^? number
* const { salesCount } = matches.find((match) => match.id === "routes/sales").data
* // ^? number
* const { hello } = matches.find((match) => match.id === "root").data
* // ^? "world"
* }
* ```
*/
interface MetaFunction<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> {
(args: MetaArgs<Loader, MatchLoaders>): MetaDescriptor[] | undefined;
}
type MetaDescriptor = {
charSet: "utf-8";
} | {
title: string;
} | {
name: string;
content: string;
} | {
property: string;
content: string;
} | {
httpEquiv: string;
content: string;
} | {
"script:ld+json": LdJsonObject | LdJsonObject[];
} | {
tagName: "meta" | "link";
[name: string]: string;
} | {
[name: string]: unknown;
};
type LdJsonObject = {
[Key in string]: LdJsonValue;
} & {
[Key in string]?: LdJsonValue | undefined;
};
type LdJsonArray = LdJsonValue[] | readonly LdJsonValue[];
type LdJsonPrimitive = string | number | boolean | null;
type LdJsonValue = LdJsonPrimitive | LdJsonObject | LdJsonArray;
/**
* A React component that is rendered for a route.
*/
type RouteComponent = ComponentType<{}>;
/**
* An arbitrary object that is associated with a route.
*
* @see https://reactrouter.com/how-to/using-handle
*/
type RouteHandle = unknown;
/**
* An object of unknown type for route loaders and actions provided by the
* server's `getLoadContext()` function. This is defined as an empty interface
* specifically so apps can leverage declaration merging to augment this type
* globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
*/
interface AppLoadContext {
[key: string]: unknown;
}
export { type Equal as $, type ActionFunction as A, type MiddlewareNextFunction as B, type ClientActionFunction as C, type DataRouteMatch as D, type ClientDataFunctionArgs as E, type FormEncType as F, type GetLoaderData as G, type HeadersFunction as H, type DataStrategyResult as I, type ServerDataFrom as J, type GetActionData as K, type Location as L, type MetaFunction as M, type Normalize as N, type RouteModules as O, type Params as P, type SerializeFrom as Q, type RouteModule$1 as R, type ShouldRevalidateFunction as S, type To as T, type UIMatch as U, type PathPattern as V, type PathMatch as W, type ParamParseKey as X, type InitialEntry as Y, type IndexRouteObject as Z, type NonIndexRouteObject as _, type ClientLoaderFunction as a, type ActionFunctionArgs as a0, type BaseRouteObject as a1, type DataStrategyFunctionArgs as a2, type DataStrategyMatch as a3, DataWithResponseInit as a4, type ErrorResponse as a5, type FormMethod as a6, type LazyRouteFunction as a7, type MiddlewareFunction as a8, type PatchRoutesOnNavigationFunctionArgs as a9, createBrowserHistory as aA, createHashHistory as aB, invariant as aC, ErrorResponseImpl as aD, type ServerRouteModule as aE, type TrackedPromise as aF, type PathParam as aa, type RedirectFunction as ab, type RouteMatch as ac, type RouterContext as ad, type ShouldRevalidateFunctionArgs as ae, createContext as af, createPath as ag, parsePath as ah, data as ai, generatePath as aj, isRouteErrorResponse as ak, matchPath as al, matchRoutes as am, redirect as an, redirectDocument as ao, replace as ap, resolvePath as aq, type ClientActionFunctionArgs as ar, type ClientLoaderFunctionArgs as as, type HeadersArgs as at, type MetaArgs as au, type PageLinkDescriptor as av, type HtmlLinkDescriptor as aw, type Future as ax, type unstable_SerializesTo as ay, createMemoryHistory as az, type LinksFunction as b, RouterContextProvider as c, type LoaderFunction as d, type RouteObject as e, type History as f, type MaybePromise as g, type MapRoutePropertiesFunction as h, Action as i, type Submission as j, type RouteData as k, type DataStrategyFunction as l, type PatchRoutesOnNavigationFunction as m, type DataRouteObject as n, type RouteBranch as o, type RouteManifest as p, type HTMLFormMethod as q, type Path as r, type LoaderFunctionArgs as s, type MiddlewareEnabled as t, type AppLoadContext as u, type LinkDescriptor as v, type Func as w, type Pretty as x, type MetaDescriptor as y, type ServerDataFunctionArgs as z };
+1740
View File
@@ -0,0 +1,1740 @@
import * as React from 'react';
import { ComponentType, ReactElement } from 'react';
/**
* Actions represent the type of change to a location value.
*/
declare enum Action {
/**
* A POP indicates a change to an arbitrary index in the history stack, such
* as a back or forward navigation. It does not describe the direction of the
* navigation, only that the current index changed.
*
* Note: This is the default action for newly created history objects.
*/
Pop = "POP",
/**
* A PUSH indicates a new entry being added to the history stack, such as when
* a link is clicked and a new page loads. When this happens, all subsequent
* entries in the stack are lost.
*/
Push = "PUSH",
/**
* A REPLACE indicates the entry at the current index in the history stack
* being replaced by a new one.
*/
Replace = "REPLACE"
}
/**
* The pathname, search, and hash values of a URL.
*/
interface Path {
/**
* A URL pathname, beginning with a /.
*/
pathname: string;
/**
* A URL search string, beginning with a ?.
*/
search: string;
/**
* A URL fragment identifier, beginning with a #.
*/
hash: string;
}
/**
* An entry in a history stack. A location contains information about the
* URL path, as well as possibly some arbitrary state and a key.
*/
interface Location<State = any> extends Path {
/**
* A value of arbitrary data associated with this location.
*/
state: State;
/**
* A unique string associated with this location. May be used to safely store
* and retrieve data in some other storage API, like `localStorage`.
*
* Note: This value is always "default" on the initial location.
*/
key: string;
/**
* The masked location displayed in the URL bar, which differs from the URL the
* router is operating on
*/
mask?: Path;
}
/**
* A change to the current location.
*/
interface Update {
/**
* The action that triggered the change.
*/
action: Action;
/**
* The new location.
*/
location: Location;
/**
* The delta between this location and the former location in the history stack
*/
delta: number | null;
}
/**
* A function that receives notifications about location changes.
*/
interface Listener {
(update: Update): void;
}
/**
* Describes a location that is the destination of some navigation used in
* {@link Link}, {@link useNavigate}, etc.
*/
type To = string | Partial<Path>;
/**
* A history is an interface to the navigation stack. The history serves as the
* source of truth for the current location, as well as provides a set of
* methods that may be used to change it.
*
* It is similar to the DOM's `window.history` object, but with a smaller, more
* focused API.
*/
interface History {
/**
* The last action that modified the current location. This will always be
* Action.Pop when a history instance is first created. This value is mutable.
*/
readonly action: Action;
/**
* The current location. This value is mutable.
*/
readonly location: Location;
/**
* Returns a valid href for the given `to` value that may be used as
* the value of an <a href> attribute.
*
* @param to - The destination URL
*/
createHref(to: To): string;
/**
* Returns a URL for the given `to` value
*
* @param to - The destination URL
*/
createURL(to: To): URL;
/**
* Encode a location the same way window.history would do (no-op for memory
* history) so we ensure our PUSH/REPLACE navigations for data routers
* behave the same as POP
*
* @param to Unencoded path
*/
encodeLocation(to: To): Path;
/**
* Pushes a new location onto the history stack, increasing its length by one.
* If there were any entries in the stack after the current one, they are
* lost.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
push(to: To, state?: any): void;
/**
* Replaces the current location in the history stack with a new one. The
* location that was replaced will no longer be available.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
replace(to: To, state?: any): void;
/**
* Navigates `n` entries backward/forward in the history stack relative to the
* current index. For example, a "back" navigation would use go(-1).
*
* @param delta - The delta in the stack index
*/
go(delta: number): void;
/**
* Sets up a listener that will be called whenever the current location
* changes.
*
* @param listener - A function that will be called when the location changes
* @returns unlisten - A function that may be used to stop listening
*/
listen(listener: Listener): () => void;
}
/**
* A user-supplied object that describes a location. Used when providing
* entries to `createMemoryHistory` via its `initialEntries` option.
*/
type InitialEntry = string | Partial<Location>;
type MemoryHistoryOptions = {
initialEntries?: InitialEntry[];
initialIndex?: number;
v5Compat?: boolean;
};
/**
* A memory history stores locations in memory. This is useful in stateful
* environments where there is no web browser, such as node tests or React
* Native.
*/
interface MemoryHistory extends History {
/**
* The current index in the history stack.
*/
readonly index: number;
}
/**
* Memory history stores the current location in memory. It is designed for use
* in stateful non-browser environments like tests and React Native.
*/
declare function createMemoryHistory(options?: MemoryHistoryOptions): MemoryHistory;
/**
* A browser history stores the current location in regular URLs in a web
* browser environment. This is the standard for most web apps and provides the
* cleanest URLs the browser's address bar.
*
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#browserhistory
*/
interface BrowserHistory extends UrlHistory {
}
type BrowserHistoryOptions = UrlHistoryOptions;
/**
* Browser history stores the location in regular URLs. This is the standard for
* most web apps, but it requires some configuration on the server to ensure you
* serve the same app at multiple URLs.
*
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createbrowserhistory
*/
declare function createBrowserHistory(options?: BrowserHistoryOptions): BrowserHistory;
/**
* A hash history stores the current location in the fragment identifier portion
* of the URL in a web browser environment.
*
* This is ideal for apps that do not control the server for some reason
* (because the fragment identifier is never sent to the server), including some
* shared hosting environments that do not provide fine-grained controls over
* which pages are served at which URLs.
*
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#hashhistory
*/
interface HashHistory extends UrlHistory {
}
type HashHistoryOptions = UrlHistoryOptions;
/**
* Hash history stores the location in window.location.hash. This makes it ideal
* for situations where you don't want to send the location to the server for
* some reason, either because you do cannot configure it or the URL space is
* reserved for something else.
*
* @see https://github.com/remix-run/history/tree/main/docs/api-reference.md#createhashhistory
*/
declare function createHashHistory(options?: HashHistoryOptions): HashHistory;
/**
* @private
*/
declare function invariant(value: boolean, message?: string): asserts value;
declare function invariant<T>(value: T | null | undefined, message?: string): asserts value is T;
/**
* Creates a string URL path from the given pathname, search, and hash components.
*
* @category Utils
*/
declare function createPath({ pathname, search, hash, }: Partial<Path>): string;
/**
* Parses a string URL path into its separate pathname, search, and hash components.
*
* @category Utils
*/
declare function parsePath(path: string): Partial<Path>;
interface UrlHistory extends History {
}
type UrlHistoryOptions = {
window?: Window;
v5Compat?: boolean;
};
/**
* An augmentable interface users can modify in their app-code to opt into
* future-flag-specific types
*/
interface Future {
}
type MiddlewareEnabled = Future extends {
v8_middleware: infer T extends boolean;
} ? T : false;
type MaybePromise<T> = T | Promise<T>;
/**
* Map of routeId -> data returned from a loader/action/error
*/
interface RouteData {
[routeId: string]: any;
}
type LowerCaseFormMethod = "get" | "post" | "put" | "patch" | "delete";
type UpperCaseFormMethod = Uppercase<LowerCaseFormMethod>;
/**
* Users can specify either lowercase or uppercase form methods on `<Form>`,
* useSubmit(), `<fetcher.Form>`, etc.
*/
type HTMLFormMethod = LowerCaseFormMethod | UpperCaseFormMethod;
/**
* Active navigation/fetcher form methods are exposed in uppercase on the
* RouterState. This is to align with the normalization done via fetch().
*/
type FormMethod = UpperCaseFormMethod;
type FormEncType = "application/x-www-form-urlencoded" | "multipart/form-data" | "application/json" | "text/plain";
type JsonObject = {
[Key in string]: JsonValue;
} & {
[Key in string]?: JsonValue | undefined;
};
type JsonArray = JsonValue[] | readonly JsonValue[];
type JsonPrimitive = string | number | boolean | null;
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
/**
* @private
* Internal interface to pass around for action submissions, not intended for
* external consumption
*/
type Submission = {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: FormData;
json: undefined;
text: undefined;
} | {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: undefined;
json: JsonValue;
text: undefined;
} | {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: undefined;
json: undefined;
text: string;
};
/**
* A context instance used as the key for the `get`/`set` methods of a
* {@link RouterContextProvider}. Accepts an optional default
* value to be returned if no value has been set.
*/
interface RouterContext<T = unknown> {
defaultValue?: T;
}
/**
* Creates a type-safe {@link RouterContext} object that can be used to
* store and retrieve arbitrary values in [`action`](../../start/framework/route-module#action)s,
* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
* Similar to React's [`createContext`](https://react.dev/reference/react/createContext),
* but specifically designed for React Router's request/response lifecycle.
*
* If a `defaultValue` is provided, it will be returned from `context.get()`
* when no value has been set for the context. Otherwise, reading this context
* when no value has been set will throw an error.
*
* ```tsx filename=app/context.ts
* import { createContext } from "react-router";
*
* // Create a context for user data
* export const userContext =
* createContext<User | null>(null);
* ```
*
* ```tsx filename=app/middleware/auth.ts
* import { getUserFromSession } from "~/auth.server";
* import { userContext } from "~/context";
*
* export const authMiddleware = async ({
* context,
* request,
* }) => {
* const user = await getUserFromSession(request);
* context.set(userContext, user);
* };
* ```
*
* ```tsx filename=app/routes/profile.tsx
* import { userContext } from "~/context";
*
* export async function loader({
* context,
* }: Route.LoaderArgs) {
* const user = context.get(userContext);
*
* if (!user) {
* throw new Response("Unauthorized", { status: 401 });
* }
*
* return { user };
* }
* ```
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param defaultValue An optional default value for the context. This value
* will be returned if no value has been set for this context.
* @returns A {@link RouterContext} object that can be used with
* `context.get()` and `context.set()` in [`action`](../../start/framework/route-module#action)s,
* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
*/
declare function createContext<T>(defaultValue?: T): RouterContext<T>;
/**
* Provides methods for writing/reading values in application context in a
* type-safe way. Primarily for usage with [middleware](../../how-to/middleware).
*
* @example
* import {
* createContext,
* RouterContextProvider
* } from "react-router";
*
* const userContext = createContext<User | null>(null);
* const contextProvider = new RouterContextProvider();
* contextProvider.set(userContext, getUser());
* // ^ Type-safe
* const user = contextProvider.get(userContext);
* // ^ User
*
* @public
* @category Utils
* @mode framework
* @mode data
*/
declare class RouterContextProvider {
#private;
/**
* Create a new `RouterContextProvider` instance
* @param init An optional initial context map to populate the provider with
*/
constructor(init?: Map<RouterContext, unknown>);
/**
* Access a value from the context. If no value has been set for the context,
* it will return the context's `defaultValue` if provided, or throw an error
* if no `defaultValue` was set.
* @param context The context to get the value for
* @returns The value for the context, or the context's `defaultValue` if no
* value was set
*/
get<T>(context: RouterContext<T>): T;
/**
* Set a value for the context. If the context already has a value set, this
* will overwrite it.
*
* @param context The context to set the value for
* @param value The value to set for the context
* @returns {void}
*/
set<C extends RouterContext>(context: C, value: C extends RouterContext<infer T> ? T : never): void;
}
type DefaultContext = MiddlewareEnabled extends true ? Readonly<RouterContextProvider> : any;
/**
* @private
* Arguments passed to route loader/action functions. Same for now but we keep
* this as a private implementation detail in case they diverge in the future.
*/
interface DataFunctionArgs<Context> {
/** A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read headers (like cookies, and {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams URLSearchParams} from the request. */
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function loader({
* params,
* }: Route.LoaderArgs) {
* params.teamId;
* // ^ string
* }
*/
params: Params;
/**
* This is the context passed in to your server adapter's getLoadContext() function.
* It's a way to bridge the gap between the adapter's request/response API with your React Router app.
* It is only applicable if you are using a custom server adapter.
*/
context: Context;
}
/**
* Route middleware `next` function to call downstream handlers and then complete
* middlewares from the bottom-up
*/
interface MiddlewareNextFunction<Result = unknown> {
(): Promise<Result>;
}
/**
* Route middleware function signature. Receives the same "data" arguments as a
* `loader`/`action` (`request`, `params`, `context`) as the first parameter and
* a `next` function as the second parameter which will call downstream handlers
* and then complete middlewares from the bottom-up
*/
type MiddlewareFunction<Result = unknown> = (args: DataFunctionArgs<Readonly<RouterContextProvider>>, next: MiddlewareNextFunction<Result>) => MaybePromise<Result | void>;
/**
* Arguments passed to loader functions
*/
interface LoaderFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
}
/**
* Arguments passed to action functions
*/
interface ActionFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
}
/**
* Loaders and actions can return anything
*/
type DataFunctionValue = unknown;
type DataFunctionReturnValue = MaybePromise<DataFunctionValue>;
/**
* Route loader function signature
*/
type LoaderFunction<Context = DefaultContext> = {
(args: LoaderFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue;
} & {
hydrate?: boolean;
};
/**
* Route action function signature
*/
interface ActionFunction<Context = DefaultContext> {
(args: ActionFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue;
}
/**
* Arguments passed to shouldRevalidate function
*/
interface ShouldRevalidateFunctionArgs {
/** This is the url the navigation started from. You can compare it with `nextUrl` to decide if you need to revalidate this route's data. */
currentUrl: URL;
/** These are the {@link https://reactrouter.com/start/framework/routing#dynamic-segments dynamic route params} from the URL that can be compared to the `nextParams` to decide if you need to reload or not. Perhaps you're using only a partial piece of the param for data loading, you don't need to revalidate if a superfluous part of the param changed. */
currentParams: DataRouteMatch["params"];
/** In the case of navigation, this the URL the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentUrl. */
nextUrl: URL;
/** In the case of navigation, these are the {@link https://reactrouter.com/start/framework/routing#dynamic-segments dynamic route params} from the next location the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentParams. */
nextParams: DataRouteMatch["params"];
/** The method (probably `"GET"` or `"POST"`) used in the form submission that triggered the revalidation. */
formMethod?: Submission["formMethod"];
/** The form action (`<Form action="/somewhere">`) that triggered the revalidation. */
formAction?: Submission["formAction"];
/** The form encType (`<Form encType="application/x-www-form-urlencoded">) used in the form submission that triggered the revalidation*/
formEncType?: Submission["formEncType"];
/** The form submission data when the form's encType is `text/plain` */
text?: Submission["text"];
/** The form submission data when the form's encType is `application/x-www-form-urlencoded` or `multipart/form-data` */
formData?: Submission["formData"];
/** The form submission data when the form's encType is `application/json` */
json?: Submission["json"];
/** The status code of the action response */
actionStatus?: number;
/**
* When a submission causes the revalidation this will be the result of the action—either action data or an error if the action failed. It's common to include some information in the action result to instruct shouldRevalidate to revalidate or not.
*
* @example
* export async function action() {
* await saveSomeStuff();
* return { ok: true };
* }
*
* export function shouldRevalidate({
* actionResult,
* }) {
* if (actionResult?.ok) {
* return false;
* }
* return true;
* }
*/
actionResult?: any;
/**
* By default, React Router doesn't call every loader all the time. There are reliable optimizations it can make by default. For example, only loaders with changing params are called. Consider navigating from the following URL to the one below it:
*
* /projects/123/tasks/abc
* /projects/123/tasks/def
* React Router will only call the loader for tasks/def because the param for projects/123 didn't change.
*
* It's safest to always return defaultShouldRevalidate after you've done your specific optimizations that return false, otherwise your UI might get out of sync with your data on the server.
*/
defaultShouldRevalidate: boolean;
}
/**
* Route shouldRevalidate function signature. This runs after any submission
* (navigation or fetcher), so we flatten the navigation/fetcher submission
* onto the arguments. It shouldn't matter whether it came from a navigation
* or a fetcher, what really matters is the URLs and the formData since loaders
* have to re-run based on the data models that were potentially mutated.
*/
interface ShouldRevalidateFunction {
(args: ShouldRevalidateFunctionArgs): boolean;
}
interface DataStrategyMatch extends RouteMatch<string, DataRouteObject> {
/**
* @private
*/
_lazyPromises?: {
middleware: Promise<void> | undefined;
handler: Promise<void> | undefined;
route: Promise<void> | undefined;
};
/**
* @deprecated Deprecated in favor of `shouldCallHandler`
*
* A boolean value indicating whether this route handler should be called in
* this pass.
*
* The `matches` array always includes _all_ matched routes even when only
* _some_ route handlers need to be called so that things like middleware can
* be implemented.
*
* `shouldLoad` is usually only interesting if you are skipping the route
* handler entirely and implementing custom handler logic - since it lets you
* determine if that custom logic should run for this route or not.
*
* For example:
* - If you are on `/parent/child/a` and you navigate to `/parent/child/b` -
* you'll get an array of three matches (`[parent, child, b]`), but only `b`
* will have `shouldLoad=true` because the data for `parent` and `child` is
* already loaded
* - If you are on `/parent/child/a` and you submit to `a`'s [`action`](https://reactrouter.com/docs/start/data/route-object#action),
* then only `a` will have `shouldLoad=true` for the action execution of
* `dataStrategy`
* - After the [`action`](https://reactrouter.com/docs/start/data/route-object#action),
* `dataStrategy` will be called again for the [`loader`](https://reactrouter.com/docs/start/data/route-object#loader)
* revalidation, and all matches will have `shouldLoad=true` (assuming no
* custom `shouldRevalidate` implementations)
*/
shouldLoad: boolean;
/**
* Arguments passed to the `shouldRevalidate` function for this `loader` execution.
* Will be `null` if this is not a revalidating loader {@link DataStrategyMatch}.
*/
shouldRevalidateArgs: ShouldRevalidateFunctionArgs | null;
/**
* Determine if this route's handler should be called during this `dataStrategy`
* execution. Calling it with no arguments will leverage the default revalidation
* behavior. You can pass your own `defaultShouldRevalidate` value if you wish
* to change the default revalidation behavior with your `dataStrategy`.
*
* @param defaultShouldRevalidate `defaultShouldRevalidate` override value (optional)
*/
shouldCallHandler(defaultShouldRevalidate?: boolean): boolean;
/**
* An async function that will resolve any `route.lazy` implementations and
* execute the route's handler (if necessary), returning a {@link DataStrategyResult}
*
* - Calling `match.resolve` does not mean you're calling the
* [`action`](https://reactrouter.com/docs/start/data/route-object#action)/[`loader`](https://reactrouter.com/docs/start/data/route-object#loader)
* (the "handler") - `resolve` will only call the `handler` internally if
* needed _and_ if you don't pass your own `handlerOverride` function parameter
* - It is safe to call `match.resolve` for all matches, even if they have
* `shouldLoad=false`, and it will no-op if no loading is required
* - You should generally always call `match.resolve()` for `shouldLoad:true`
* routes to ensure that any `route.lazy` implementations are processed
* - See the examples below for how to implement custom handler execution via
* `match.resolve`
*/
resolve: (handlerOverride?: (handler: (ctx?: unknown) => DataFunctionReturnValue) => DataFunctionReturnValue) => Promise<DataStrategyResult>;
}
interface DataStrategyFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
/**
* Matches for this route extended with Data strategy APIs
*/
matches: DataStrategyMatch[];
runClientMiddleware: (cb: DataStrategyFunction<Context>) => Promise<Record<string, DataStrategyResult>>;
/**
* The key of the fetcher we are calling `dataStrategy` for, otherwise `null`
* for navigational executions
*/
fetcherKey: string | null;
}
/**
* Result from a loader or action called via dataStrategy
*/
interface DataStrategyResult {
type: "data" | "error";
result: unknown;
}
interface DataStrategyFunction<Context = DefaultContext> {
(args: DataStrategyFunctionArgs<Context>): Promise<Record<string, DataStrategyResult>>;
}
type PatchRoutesOnNavigationFunctionArgs = {
signal: AbortSignal;
path: string;
matches: RouteMatch[];
fetcherKey: string | undefined;
patch: (routeId: string | null, children: RouteObject[]) => void;
};
type PatchRoutesOnNavigationFunction = (opts: PatchRoutesOnNavigationFunctionArgs) => MaybePromise<void>;
/**
* Function provided to set route-specific properties from route objects
*/
interface MapRoutePropertiesFunction {
(route: DataRouteObject): {
hasErrorBoundary: boolean;
} & Record<string, any>;
}
/**
* Keys we cannot change from within a lazy object. We spread all other keys
* onto the route. Either they're meaningful to the router, or they'll get
* ignored.
*/
type UnsupportedLazyRouteObjectKey = "lazy" | "caseSensitive" | "path" | "id" | "index" | "children";
/**
* Keys we cannot change from within a lazy() function. We spread all other keys
* onto the route. Either they're meaningful to the router, or they'll get
* ignored.
*/
type UnsupportedLazyRouteFunctionKey = UnsupportedLazyRouteObjectKey | "middleware";
/**
* lazy object to load route properties, which can add non-matching
* related properties to a route
*/
type LazyRouteObject<R extends RouteObject> = {
[K in keyof R as K extends UnsupportedLazyRouteObjectKey ? never : K]?: () => Promise<R[K] | null | undefined>;
};
/**
* lazy() function to load a route definition, which can add non-matching
* related properties to a route
*/
interface LazyRouteFunction<R extends RouteObject> {
(): Promise<Omit<R, UnsupportedLazyRouteFunctionKey> & Partial<Record<UnsupportedLazyRouteFunctionKey, never>>>;
}
type LazyRouteDefinition<R extends RouteObject> = LazyRouteObject<R> | LazyRouteFunction<R>;
/**
* Base RouteObject with common props shared by all types of routes
* @internal
*/
type BaseRouteObject = {
/**
* Whether the path should be case-sensitive. Defaults to `false`.
*/
caseSensitive?: boolean;
/**
* The path pattern to match. If unspecified or empty, then this becomes a
* layout route.
*/
path?: string;
/**
* The unique identifier for this route (for use with {@link DataRouter}s)
*/
id?: string;
/**
* The route middleware.
* See [`middleware`](../../start/data/route-object#middleware).
*/
middleware?: MiddlewareFunction[];
/**
* The route loader.
* See [`loader`](../../start/data/route-object#loader).
*/
loader?: LoaderFunction | boolean;
/**
* The route action.
* See [`action`](../../start/data/route-object#action).
*/
action?: ActionFunction | boolean;
hasErrorBoundary?: boolean;
/**
* The route shouldRevalidate function.
* See [`shouldRevalidate`](../../start/data/route-object#shouldRevalidate).
*/
shouldRevalidate?: ShouldRevalidateFunction;
/**
* The route handle.
*/
handle?: any;
/**
* A function that returns a promise that resolves to the route object.
* Used for code-splitting routes.
* See [`lazy`](../../start/data/route-object#lazy).
*/
lazy?: LazyRouteDefinition<BaseRouteObject>;
/**
* The React Component to render when this route matches.
* Mutually exclusive with `element`.
*/
Component?: React.ComponentType | null;
/**
* The React element to render when this Route matches.
* Mutually exclusive with `Component`.
*/
element?: React.ReactNode | null;
/**
* The React Component to render at this route if an error occurs.
* Mutually exclusive with `errorElement`.
*/
ErrorBoundary?: React.ComponentType | null;
/**
* The React element to render at this route if an error occurs.
* Mutually exclusive with `ErrorBoundary`.
*/
errorElement?: React.ReactNode | null;
/**
* The React Component to render while this router is loading data.
* Mutually exclusive with `hydrateFallbackElement`.
*/
HydrateFallback?: React.ComponentType | null;
/**
* The React element to render while this router is loading data.
* Mutually exclusive with `HydrateFallback`.
*/
hydrateFallbackElement?: React.ReactNode | null;
};
/**
* Index routes must not have children
*/
type IndexRouteObject = BaseRouteObject & {
/**
* Child Route objects - not valid on index routes.
*/
children?: undefined;
/**
* Whether this is an index route.
*/
index: true;
};
/**
* Non-index routes may have children, but cannot have `index` set to `true`.
*/
type NonIndexRouteObject = BaseRouteObject & {
/**
* Child Route objects.
*/
children?: RouteObject[];
/**
* Whether this is an index route - must be `false` or undefined on non-index routes.
*/
index?: false;
};
/**
* A route object represents a logical route, with (optionally) its child
* routes organized in a tree-like structure.
*/
type RouteObject = IndexRouteObject | NonIndexRouteObject;
type DataIndexRouteObject = IndexRouteObject & {
id: string;
};
type DataNonIndexRouteObject = NonIndexRouteObject & {
children?: DataRouteObject[];
id: string;
};
/**
* A data route object, which is just a RouteObject with a required unique ID
*/
type DataRouteObject = DataIndexRouteObject | DataNonIndexRouteObject;
type RouteManifest<R = DataRouteObject> = Record<string, R | undefined>;
type Regex_az = "a" | "b" | "c" | "d" | "e" | "f" | "g" | "h" | "i" | "j" | "k" | "l" | "m" | "n" | "o" | "p" | "q" | "r" | "s" | "t" | "u" | "v" | "w" | "x" | "y" | "z";
type Regex_AZ = Uppercase<Regex_az>;
type Regex_09 = "0" | "1" | "2" | "3" | "4" | "5" | "6" | "7" | "8" | "9";
type Regex_w = Regex_az | Regex_AZ | Regex_09 | "_";
/** Emulates Regex `+` operator */
type RegexMatchPlus<char extends string, T extends string> = _RegexMatchPlus<char, T> extends infer result extends string ? result extends '' ? never : result : never;
type _RegexMatchPlus<char extends string, T extends string> = T extends `${infer head extends char}${infer rest}` ? `${head}${_RegexMatchPlus<char, rest>}` : '';
type ParamNameChar = Regex_w | "-";
type Simplify<T> = {
[K in keyof T]: T[K];
} & {};
type GeneratePathParams<path extends string> = Simplify<ParseParams<path> & {
[key in string]: string | null | undefined;
}>;
type ParseParams<path extends string> = path extends '*' ? {
'*': string;
} : path extends `${infer rest}/*` ? {
'*': string;
} & ParseParams<rest> : _ParseParams<path>;
type _ParseParams<path extends string> = path extends `${infer left}/${infer right}` ? _ParseParams<left> & _ParseParams<right> : path extends `:${infer param}?${string}` ? {
[key in RegexMatchPlus<ParamNameChar, param>]?: string | null | undefined;
} : path extends `:${infer param}` ? {
[key in RegexMatchPlus<ParamNameChar, param>]: string;
} : {};
type PathParam<path extends string> = (keyof ParseParams<path>) & string;
type ParamParseKey<Segment extends string> = [
PathParam<Segment>
] extends [never] ? string : PathParam<Segment>;
/**
* The parameters that were parsed from the URL path.
*/
type Params<Key extends string = string> = {
readonly [key in Key]: string | undefined;
};
/**
* A RouteMatch contains info about how a route matched a URL.
*/
interface RouteMatch<ParamKey extends string = string, RouteObjectType extends RouteObject = RouteObject> {
/**
* The names and values of dynamic parameters in the URL.
*/
params: Params<ParamKey>;
/**
* The portion of the URL pathname that was matched.
*/
pathname: string;
/**
* The portion of the URL pathname that was matched before child routes.
*/
pathnameBase: string;
/**
* The route object that was used to match.
*/
route: RouteObjectType;
}
interface DataRouteMatch extends RouteMatch<string, DataRouteObject> {
}
/**
* Matches the given routes to a location and returns the match data.
*
* @example
* import { matchRoutes } from "react-router";
*
* let routes = [{
* path: "/",
* Component: Root,
* children: [{
* path: "dashboard",
* Component: Dashboard,
* }]
* }];
*
* matchRoutes(routes, "/dashboard"); // [rootMatch, dashboardMatch]
*
* @public
* @category Utils
* @param routes The array of route objects to match against.
* @param locationArg The location to match against, either a string path or a
* partial {@link Location} object
* @param basename Optional base path to strip from the location before matching.
* Defaults to `/`.
* @returns An array of matched routes, or `null` if no matches were found.
*/
declare function matchRoutes<RouteObjectType extends RouteObject = RouteObject>(routes: RouteObjectType[], locationArg: Partial<Location> | string, basename?: string): RouteMatch<string, RouteObjectType>[] | null;
interface UIMatch<Data = unknown, Handle = unknown> {
id: string;
pathname: string;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the matched route.
*/
params: RouteMatch["params"];
/**
* The return value from the matched route's loader or clientLoader. This might
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
* an error and we're currently displaying an `ErrorBoundary`.
*
* @deprecated Use `UIMatch.loaderData` instead
*/
data: Data | undefined;
/**
* The return value from the matched route's loader or clientLoader. This might
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
* an error and we're currently displaying an `ErrorBoundary`.
*/
loaderData: Data | undefined;
/**
* The {@link https://reactrouter.com/start/framework/route-module#handle handle object}
* exported from the matched route module
*/
handle: Handle;
}
interface RouteMeta<RouteObjectType extends RouteObject = RouteObject> {
relativePath: string;
caseSensitive: boolean;
childrenIndex: number;
route: RouteObjectType;
matcher?: RegExp;
compiledParams?: CompiledPathParam[];
}
/**
* @private
* PRIVATE - DO NOT USE
*
* A "branch" of routes that match a given route pattern.
* This is an internal interface not intended for direct external usage.
*/
interface RouteBranch<RouteObjectType extends RouteObject = RouteObject> {
path: string;
score: number;
routesMeta: RouteMeta<RouteObjectType>[];
}
/**
* Returns a path with params interpolated.
*
* @example
* import { generatePath } from "react-router";
*
* generatePath("/users/:id", { id: "123" }); // "/users/123"
*
* @public
* @category Utils
* @param originalPath The original path to generate.
* @param params The parameters to interpolate into the path.
* @returns The generated path with parameters interpolated.
*/
declare function generatePath<Path extends string>(originalPath: Path, params?: GeneratePathParams<Path>): string;
/**
* Used to match on some portion of a URL pathname.
*/
interface PathPattern<Path extends string = string> {
/**
* A string to match against a URL pathname. May contain `:id`-style segments
* to indicate placeholders for dynamic parameters. It May also end with `/*`
* to indicate matching the rest of the URL pathname.
*/
path: Path;
/**
* Should be `true` if the static portions of the `path` should be matched in
* the same case.
*/
caseSensitive?: boolean;
/**
* Should be `true` if this pattern should match the entire URL pathname.
*/
end?: boolean;
}
/**
* Contains info about how a {@link PathPattern} matched on a URL pathname.
*/
interface PathMatch<ParamKey extends string = string> {
/**
* The names and values of dynamic parameters in the URL.
*/
params: Params<ParamKey>;
/**
* The portion of the URL pathname that was matched.
*/
pathname: string;
/**
* The portion of the URL pathname that was matched before child routes.
*/
pathnameBase: string;
/**
* The pattern that was used to match.
*/
pattern: PathPattern;
}
/**
* Performs pattern matching on a URL pathname and returns information about
* the match.
*
* @public
* @category Utils
* @param pattern The pattern to match against the URL pathname. This can be a
* string or a {@link PathPattern} object. If a string is provided, it will be
* treated as a pattern with `caseSensitive` set to `false` and `end` set to
* `true`.
* @param pathname The URL pathname to match against the pattern.
* @returns A path match object if the pattern matches the pathname,
* or `null` if it does not match.
*/
declare function matchPath<Path extends string>(pattern: PathPattern<Path> | Path, pathname: string): PathMatch<ParamParseKey<Path>> | null;
type CompiledPathParam = {
paramName: string;
isOptional?: boolean;
};
/**
* Returns a resolved {@link Path} object relative to the given pathname.
*
* @public
* @category Utils
* @param to The path to resolve, either a string or a partial {@link Path}
* object.
* @param fromPathname The pathname to resolve the path from. Defaults to `/`.
* @returns A {@link Path} object with the resolved pathname, search, and hash.
*/
declare function resolvePath(to: To, fromPathname?: string): Path;
declare class DataWithResponseInit<D> {
type: string;
data: D;
init: ResponseInit | null;
constructor(data: D, init?: ResponseInit);
}
/**
* Create "responses" that contain `headers`/`status` without forcing
* serialization into an actual [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
*
* @example
* import { data } from "react-router";
*
* export async function action({ request }: Route.ActionArgs) {
* let formData = await request.formData();
* let item = await createItem(formData);
* return data(item, {
* headers: { "X-Custom-Header": "value" }
* status: 201,
* });
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param data The data to be included in the response.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A {@link DataWithResponseInit} instance containing the data and
* response init.
*/
declare function data<D>(data: D, init?: number | ResponseInit): DataWithResponseInit<D>;
interface TrackedPromise extends Promise<any> {
_tracked?: boolean;
_data?: any;
_error?: any;
}
type RedirectFunction = (url: string, init?: number | ResponseInit) => Response;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
* Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* This utility accepts absolute URLs and can navigate to external domains, so
* the application should validate any user-supplied inputs to redirects.
*
* @example
* import { redirect } from "react-router";
*
* export async function loader({ request }: Route.LoaderArgs) {
* if (!isLoggedIn(request))
* throw redirect("/login");
* }
*
* // ...
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const redirect: RedirectFunction;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that will force a document reload to the new location. Sets the status code
* and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* This utility accepts absolute URLs and can navigate to external domains, so
* the application should validate any user-supplied inputs to redirects.
*
* ```tsx filename=routes/logout.tsx
* import { redirectDocument } from "react-router";
*
* import { destroySession } from "../sessions.server";
*
* export async function action({ request }: Route.ActionArgs) {
* let session = await getSession(request.headers.get("Cookie"));
* return redirectDocument("/", {
* headers: { "Set-Cookie": await destroySession(session) }
* });
* }
* ```
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const redirectDocument: RedirectFunction;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that will perform a [`history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState)
* instead of a [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)
* for client-side navigation redirects. Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* @example
* import { replace } from "react-router";
*
* export async function loader() {
* return replace("/new-location");
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const replace: RedirectFunction;
type ErrorResponse = {
status: number;
statusText: string;
data: any;
};
declare class ErrorResponseImpl implements ErrorResponse {
status: number;
statusText: string;
data: any;
private error?;
private internal;
constructor(status: number, statusText: string | undefined, data: any, internal?: boolean);
}
/**
* Check if the given error is an {@link ErrorResponse} generated from a 4xx/5xx
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* thrown from an [`action`](../../start/framework/route-module#action) or
* [`loader`](../../start/framework/route-module#loader) function.
*
* @example
* import { isRouteErrorResponse } from "react-router";
*
* export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
* if (isRouteErrorResponse(error)) {
* return (
* <>
* <p>Error: `${error.status}: ${error.statusText}`</p>
* <p>{error.data}</p>
* </>
* );
* }
*
* return (
* <p>Error: {error instanceof Error ? error.message : "Unknown Error"}</p>
* );
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param error The error to check.
* @returns `true` if the error is an {@link ErrorResponse}, `false` otherwise.
*/
declare function isRouteErrorResponse(error: any): error is ErrorResponse;
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>);
interface HtmlLinkProps {
/**
* Address of the hyperlink
*/
href?: string;
/**
* How the element handles crossorigin requests
*/
crossOrigin?: "anonymous" | "use-credentials";
/**
* Relationship between the document containing the hyperlink and the destination resource
*/
rel: LiteralUnion<"alternate" | "dns-prefetch" | "icon" | "manifest" | "modulepreload" | "next" | "pingback" | "preconnect" | "prefetch" | "preload" | "prerender" | "search" | "stylesheet", string>;
/**
* Applicable media: "screen", "print", "(max-width: 764px)"
*/
media?: string;
/**
* Integrity metadata used in Subresource Integrity checks
*/
integrity?: string;
/**
* Language of the linked resource
*/
hrefLang?: string;
/**
* Hint for the type of the referenced resource
*/
type?: string;
/**
* Referrer policy for fetches initiated by the element
*/
referrerPolicy?: "" | "no-referrer" | "no-referrer-when-downgrade" | "same-origin" | "origin" | "strict-origin" | "origin-when-cross-origin" | "strict-origin-when-cross-origin" | "unsafe-url";
/**
* Sizes of the icons (for rel="icon")
*/
sizes?: string;
/**
* Potential destination for a preload request (for rel="preload" and rel="modulepreload")
*/
as?: LiteralUnion<"audio" | "audioworklet" | "document" | "embed" | "fetch" | "font" | "frame" | "iframe" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt", string>;
/**
* Color to use when customizing a site's icon (for rel="mask-icon")
*/
color?: string;
/**
* Whether the link is disabled
*/
disabled?: boolean;
/**
* The title attribute has special semantics on this element: Title of the link; CSS style sheet set name.
*/
title?: string;
/**
* Images to use in different situations, e.g., high-resolution displays,
* small monitors, etc. (for rel="preload")
*/
imageSrcSet?: string;
/**
* Image sizes for different page layouts (for rel="preload")
*/
imageSizes?: string;
}
interface HtmlLinkPreloadImage extends HtmlLinkProps {
/**
* Relationship between the document containing the hyperlink and the destination resource
*/
rel: "preload";
/**
* Potential destination for a preload request (for rel="preload" and rel="modulepreload")
*/
as: "image";
/**
* Address of the hyperlink
*/
href?: string;
/**
* Images to use in different situations, e.g., high-resolution displays,
* small monitors, etc. (for rel="preload")
*/
imageSrcSet: string;
/**
* Image sizes for different page layouts (for rel="preload")
*/
imageSizes?: string;
}
/**
* Represents a `<link>` element.
*
* WHATWG Specification: https://html.spec.whatwg.org/multipage/semantics.html#the-link-element
*/
type HtmlLinkDescriptor = (HtmlLinkProps & Pick<Required<HtmlLinkProps>, "href">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "imageSizes">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "href"> & {
imageSizes?: never;
});
interface PageLinkDescriptor extends Omit<HtmlLinkDescriptor, "href" | "rel" | "type" | "sizes" | "imageSrcSet" | "imageSizes" | "as" | "color" | "title"> {
/**
* A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* element. If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*/
nonce?: string | undefined;
/**
* The absolute path of the page to prefetch, e.g. `/absolute/path`.
*/
page: string;
}
type LinkDescriptor = HtmlLinkDescriptor | PageLinkDescriptor;
type Serializable = undefined | null | boolean | string | symbol | number | Array<Serializable> | {
[key: PropertyKey]: Serializable;
} | bigint | Date | URL | RegExp | Error | Map<Serializable, Serializable> | Set<Serializable> | Promise<Serializable>;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;
type IsAny<T> = 0 extends 1 & T ? true : false;
type Func = (...args: any[]) => unknown;
type Pretty<T> = {
[K in keyof T]: T[K];
} & {};
type Normalize<T> = _Normalize<UnionKeys<T>, T>;
type _Normalize<Key extends keyof any, T> = T extends infer U ? Pretty<{
[K in Key as K extends keyof U ? undefined extends U[K] ? never : K : never]: K extends keyof U ? U[K] : never;
} & {
[K in Key as K extends keyof U ? undefined extends U[K] ? K : never : never]?: K extends keyof U ? U[K] : never;
} & {
[K in Key as K extends keyof U ? never : K]?: undefined;
}> : never;
type UnionKeys<T> = T extends any ? keyof T : never;
type RouteModule$1 = {
meta?: Func;
links?: Func;
headers?: Func;
loader?: Func;
clientLoader?: Func;
action?: Func;
clientAction?: Func;
HydrateFallback?: Func;
default?: Func;
ErrorBoundary?: Func;
[key: string]: unknown;
};
/**
* A brand that can be applied to a type to indicate that it will serialize
* to a specific type when transported to the client from a loader.
* Only use this if you have additional serialization/deserialization logic
* in your application.
*/
type unstable_SerializesTo<T> = {
unstable__ReactRouter_SerializesTo: [T];
};
type Serialize<T> = T extends unstable_SerializesTo<infer To> ? To : T extends Serializable ? T : T extends (...args: any[]) => unknown ? undefined : T extends Promise<infer U> ? Promise<Serialize<U>> : T extends Map<infer K, infer V> ? Map<Serialize<K>, Serialize<V>> : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<Serialize<K>, Serialize<V>> : T extends Set<infer U> ? Set<Serialize<U>> : T extends ReadonlySet<infer U> ? ReadonlySet<Serialize<U>> : T extends [] ? [] : T extends readonly [infer F, ...infer R] ? [Serialize<F>, ...Serialize<R>] : T extends Array<infer U> ? Array<Serialize<U>> : T extends readonly unknown[] ? readonly Serialize<T[number]>[] : T extends Record<any, any> ? {
[K in keyof T]: Serialize<T[K]>;
} : undefined;
type VoidToUndefined<T> = Equal<T, void> extends true ? undefined : T;
type DataFrom<T> = IsAny<T> extends true ? undefined : T extends Func ? VoidToUndefined<Awaited<ReturnType<T>>> : undefined;
type ClientData<T> = T extends Response ? never : T extends DataWithResponseInit<infer U> ? U : T;
type ServerData<T> = T extends Response ? never : T extends DataWithResponseInit<infer U> ? Serialize<U> : Serialize<T>;
type ServerDataFrom<T> = ServerData<DataFrom<T>>;
type ClientDataFrom<T> = ClientData<DataFrom<T>>;
type ClientDataFunctionArgs<Params> = {
/**
* A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read the URL, the method, the "content-type" header, and the request body from the request.
*
* @note Because client data functions are called before a network request is made, the Request object does not include the headers which the browser automatically adds. React Router infers the "content-type" header from the enc-type of the form that performed the submission.
**/
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function clientLoader({
* params,
* }: Route.ClientLoaderArgs) {
* params.teamId;
* // ^ string
* }
**/
params: Params;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* When `future.v8_middleware` is not enabled, this is undefined.
*
* When `future.v8_middleware` is enabled, this is an instance of
* `RouterContextProvider` and can be used to access context values
* from your route middlewares. You may pass in initial context values in your
* `<HydratedRouter getContext>` prop
*/
context: Readonly<RouterContextProvider>;
};
type ServerDataFunctionArgs<Params> = {
/** A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read the url, method, headers (such as cookies), and request body from the request. */
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function loader({
* params,
* }: Route.LoaderArgs) {
* params.teamId;
* // ^ string
* }
**/
params: Params;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* Without `future.v8_middleware` enabled, this is the context passed in
* to your server adapter's `getLoadContext` function. It's a way to bridge the
* gap between the adapter's request/response API with your React Router app.
* It is only applicable if you are using a custom server adapter.
*
* With `future.v8_middleware` enabled, this is an instance of
* `RouterContextProvider` and can be used for type-safe access to
* context value set in your route middlewares. If you are using a custom
* server adapter, you may provide an initial set of context values from your
* `getLoadContext` function.
*/
context: MiddlewareEnabled extends true ? Readonly<RouterContextProvider> : AppLoadContext;
};
type SerializeFrom<T> = T extends (...args: infer Args) => unknown ? Args extends [
ClientLoaderFunctionArgs | ClientActionFunctionArgs | ClientDataFunctionArgs<unknown>
] ? ClientDataFrom<T> : ServerDataFrom<T> : T;
type IsDefined<T> = Equal<T, undefined> extends true ? false : true;
type IsHydrate<ClientLoader> = ClientLoader extends {
hydrate: true;
} ? true : ClientLoader extends {
hydrate: false;
} ? false : false;
type GetLoaderData<T extends RouteModule$1> = _DataLoaderData<ServerDataFrom<T["loader"]>, ClientDataFrom<T["clientLoader"]>, IsHydrate<T["clientLoader"]>, T extends {
HydrateFallback: Func;
} ? true : false>;
type _DataLoaderData<ServerLoaderData, ClientLoaderData, ClientLoaderHydrate extends boolean, HasHydrateFallback> = [
HasHydrateFallback,
ClientLoaderHydrate
] extends [true, true] ? IsDefined<ClientLoaderData> extends true ? ClientLoaderData : undefined : [
IsDefined<ClientLoaderData>,
IsDefined<ServerLoaderData>
] extends [true, true] ? ServerLoaderData | ClientLoaderData : IsDefined<ClientLoaderData> extends true ? ClientLoaderData : IsDefined<ServerLoaderData> extends true ? ServerLoaderData : undefined;
type GetActionData<T extends RouteModule$1> = _DataActionData<ServerDataFrom<T["action"]>, ClientDataFrom<T["clientAction"]>>;
type _DataActionData<ServerActionData, ClientActionData> = Awaited<[
IsDefined<ServerActionData>,
IsDefined<ClientActionData>
] extends [true, true] ? ServerActionData | ClientActionData : IsDefined<ClientActionData> extends true ? ClientActionData : IsDefined<ServerActionData> extends true ? ServerActionData : undefined>;
interface RouteModules {
[routeId: string]: RouteModule | undefined;
}
/**
* The shape of a route module shipped to the client
*/
interface RouteModule {
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
clientMiddleware?: MiddlewareFunction<Record<string, DataStrategyResult>>[];
ErrorBoundary?: ErrorBoundaryComponent;
HydrateFallback?: HydrateFallbackComponent;
Layout?: LayoutComponent;
default: RouteComponent;
handle?: RouteHandle;
links?: LinksFunction;
meta?: MetaFunction;
shouldRevalidate?: ShouldRevalidateFunction;
}
/**
* The shape of a route module on the server
*/
interface ServerRouteModule extends RouteModule {
action?: ActionFunction;
headers?: HeadersFunction | {
[name: string]: string;
};
loader?: LoaderFunction;
middleware?: MiddlewareFunction<Response>[];
}
/**
* A function that handles data mutations for a route on the client
*/
type ClientActionFunction = (args: ClientActionFunctionArgs) => ReturnType<ActionFunction>;
/**
* Arguments passed to a route `clientAction` function
*/
type ClientActionFunctionArgs = ActionFunctionArgs & {
serverAction: <T = unknown>() => Promise<SerializeFrom<T>>;
};
/**
* A function that loads data for a route on the client
*/
type ClientLoaderFunction = ((args: ClientLoaderFunctionArgs) => ReturnType<LoaderFunction>) & {
hydrate?: boolean;
};
/**
* Arguments passed to a route `clientLoader` function
*/
type ClientLoaderFunctionArgs = LoaderFunctionArgs & {
serverLoader: <T = unknown>() => Promise<SerializeFrom<T>>;
};
/**
* ErrorBoundary to display for this route
*/
type ErrorBoundaryComponent = ComponentType;
type HeadersArgs = {
loaderHeaders: Headers;
parentHeaders: Headers;
actionHeaders: Headers;
errorHeaders: Headers | undefined;
};
/**
* A function that returns HTTP headers to be used for a route. These headers
* will be merged with (and take precedence over) headers from parent routes.
*/
interface HeadersFunction {
(args: HeadersArgs): Headers | HeadersInit;
}
/**
* `<Route HydrateFallback>` component to render on initial loads
* when client loaders are present
*/
type HydrateFallbackComponent = ComponentType;
/**
* Optional, root-only `<Route Layout>` component to wrap the root content in.
* Useful for defining the <html>/<head>/<body> document shell shared by the
* Component, HydrateFallback, and ErrorBoundary
*/
type LayoutComponent = ComponentType<{
children: ReactElement<unknown, ErrorBoundaryComponent | HydrateFallbackComponent | RouteComponent>;
}>;
/**
* A function that defines `<link>` tags to be inserted into the `<head>` of
* the document on route transitions.
*
* @see https://reactrouter.com/start/framework/route-module#meta
*/
interface LinksFunction {
(): LinkDescriptor[];
}
interface MetaMatch<RouteId extends string = string, Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown> {
id: RouteId;
pathname: DataRouteMatch["pathname"];
/** @deprecated Use `MetaMatch.loaderData` instead */
data: Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown;
loaderData: Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown;
handle?: RouteHandle;
params: DataRouteMatch["params"];
meta: MetaDescriptor[];
error?: unknown;
}
type MetaMatches<MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> = Array<{
[K in keyof MatchLoaders]: MetaMatch<Exclude<K, number | symbol>, MatchLoaders[K]>;
}[keyof MatchLoaders]>;
interface MetaArgs<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> {
/** @deprecated Use `MetaArgs.loaderData` instead */
data: (Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown) | undefined;
loaderData: (Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown) | undefined;
params: Params;
location: Location;
matches: MetaMatches<MatchLoaders>;
error?: unknown;
}
/**
* A function that returns an array of data objects to use for rendering
* metadata HTML tags in a route. These tags are not rendered on descendant
* routes in the route hierarchy. In other words, they will only be rendered on
* the route in which they are exported.
*
* @param Loader - The type of the current route's loader function
* @param MatchLoaders - Mapping from a parent route's filepath to its loader
* function type
*
* Note that parent route filepaths are relative to the `app/` directory.
*
* For example, if this meta function is for `/sales/customers/$customerId`:
*
* ```ts
* // app/root.tsx
* const loader = () => ({ hello: "world" })
* export type Loader = typeof loader
*
* // app/routes/sales.tsx
* const loader = () => ({ salesCount: 1074 })
* export type Loader = typeof loader
*
* // app/routes/sales/customers.tsx
* const loader = () => ({ customerCount: 74 })
* export type Loader = typeof loader
*
* // app/routes/sales/customers/$customersId.tsx
* import type { Loader as RootLoader } from "../../../root"
* import type { Loader as SalesLoader } from "../../sales"
* import type { Loader as CustomersLoader } from "../../sales/customers"
*
* const loader = () => ({ name: "Customer name" })
*
* const meta: MetaFunction<typeof loader, {
* "root": RootLoader,
* "routes/sales": SalesLoader,
* "routes/sales/customers": CustomersLoader,
* }> = ({ data, matches }) => {
* const { name } = data
* // ^? string
* const { customerCount } = matches.find((match) => match.id === "routes/sales/customers").data
* // ^? number
* const { salesCount } = matches.find((match) => match.id === "routes/sales").data
* // ^? number
* const { hello } = matches.find((match) => match.id === "root").data
* // ^? "world"
* }
* ```
*/
interface MetaFunction<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> {
(args: MetaArgs<Loader, MatchLoaders>): MetaDescriptor[] | undefined;
}
type MetaDescriptor = {
charSet: "utf-8";
} | {
title: string;
} | {
name: string;
content: string;
} | {
property: string;
content: string;
} | {
httpEquiv: string;
content: string;
} | {
"script:ld+json": LdJsonObject | LdJsonObject[];
} | {
tagName: "meta" | "link";
[name: string]: string;
} | {
[name: string]: unknown;
};
type LdJsonObject = {
[Key in string]: LdJsonValue;
} & {
[Key in string]?: LdJsonValue | undefined;
};
type LdJsonArray = LdJsonValue[] | readonly LdJsonValue[];
type LdJsonPrimitive = string | number | boolean | null;
type LdJsonValue = LdJsonPrimitive | LdJsonObject | LdJsonArray;
/**
* A React component that is rendered for a route.
*/
type RouteComponent = ComponentType<{}>;
/**
* An arbitrary object that is associated with a route.
*
* @see https://reactrouter.com/how-to/using-handle
*/
type RouteHandle = unknown;
/**
* An object of unknown type for route loaders and actions provided by the
* server's `getLoadContext()` function. This is defined as an empty interface
* specifically so apps can leverage declaration merging to augment this type
* globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
*/
interface AppLoadContext {
[key: string]: unknown;
}
export { type PathPattern as $, type ActionFunction as A, type PatchRoutesOnNavigationFunction as B, type ClientActionFunction as C, type DataStrategyResult as D, type DataRouteObject as E, type Func as F, type GetLoaderData as G, type HeadersFunction as H, type RouteBranch as I, type RouteManifest as J, type Path as K, type Location as L, type MetaFunction as M, type Normalize as N, type InitialEntry as O, type Params as P, type NonIndexRouteObject as Q, type RouteModule$1 as R, type ShouldRevalidateFunction as S, type To as T, type UIMatch as U, type LazyRouteFunction as V, type IndexRouteObject as W, type RouteMatch as X, type TrackedPromise as Y, type RouteModules as Z, type SerializeFrom as _, type ClientLoaderFunction as a, type PathMatch as a0, type ParamParseKey as a1, type Equal as a2, type ActionFunctionArgs as a3, type BaseRouteObject as a4, type DataStrategyFunctionArgs as a5, type DataStrategyMatch as a6, DataWithResponseInit as a7, type ErrorResponse as a8, type FormMethod as a9, createMemoryHistory as aA, createBrowserHistory as aB, createHashHistory as aC, invariant as aD, ErrorResponseImpl as aE, type ServerRouteModule as aF, type MiddlewareFunction as aa, type PatchRoutesOnNavigationFunctionArgs as ab, type PathParam as ac, type RedirectFunction as ad, type RouterContext as ae, type ShouldRevalidateFunctionArgs as af, createContext as ag, createPath as ah, parsePath as ai, data as aj, generatePath as ak, isRouteErrorResponse as al, matchPath as am, matchRoutes as an, redirect as ao, redirectDocument as ap, replace as aq, resolvePath as ar, type ClientActionFunctionArgs as as, type ClientLoaderFunctionArgs as at, type HeadersArgs as au, type MetaArgs as av, type PageLinkDescriptor as aw, type HtmlLinkDescriptor as ax, type Future as ay, type unstable_SerializesTo as az, type LinksFunction as b, RouterContextProvider as c, type LoaderFunction as d, type LinkDescriptor as e, type Pretty as f, type MetaDescriptor as g, type ServerDataFunctionArgs as h, type MiddlewareNextFunction as i, type ClientDataFunctionArgs as j, type ServerDataFrom as k, type GetActionData as l, type HTMLFormMethod as m, type FormEncType as n, type LoaderFunctionArgs as o, type MiddlewareEnabled as p, type AppLoadContext as q, type RouteObject as r, type History as s, type MaybePromise as t, type MapRoutePropertiesFunction as u, Action as v, type DataRouteMatch as w, type Submission as x, type RouteData as y, type DataStrategyFunction as z };
+172
View File
@@ -0,0 +1,172 @@
import * as React from 'react';
import { a as RouterProviderProps$1, R as RouterInit, C as ClientInstrumentation, b as ClientOnErrorFunction } from './context-CeD5LmaF.mjs';
export { D as unstable_DecodeActionFunction, a as unstable_DecodeFormStateFunction, b as unstable_DecodeReplyFunction, R as unstable_RSCHydratedRouter, d as unstable_RSCManifestPayload, e as unstable_RSCPayload, f as unstable_RSCRenderPayload, c as unstable_createCallServer } from './browser-DBmQ1yAR.mjs';
import './data-DEjBmEfD.mjs';
type RouterProviderProps = Omit<RouterProviderProps$1, "flushSync">;
declare function RouterProvider(props: RouterProviderProps): React.JSX.Element;
/**
* Props for the {@link dom.HydratedRouter} component.
*
* @category Types
*/
interface HydratedRouterProps {
/**
* Context factory function to be passed through to {@link createBrowserRouter}.
* This function will be called to create a fresh `context` instance on each
* navigation/fetch and made available to
* [`clientAction`](../../start/framework/route-module#clientAction)/[`clientLoader`](../../start/framework/route-module#clientLoader)
* functions.
*/
getContext?: RouterInit["getContext"];
/**
* Array of instrumentation objects allowing you to instrument the router and
* individual routes prior to router initialization (and on any subsequently
* added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
* mostly useful for observability such as wrapping navigations, fetches,
* as well as route loaders/actions/middlewares with logging and/or performance
* tracing. See the [docs](../../how-to/instrumentation) for more information.
*
* ```tsx
* const logging = {
* router({ instrument }) {
* instrument({
* navigate: (impl, { to }) => logExecution(`navigate ${to}`, impl),
* fetch: (impl, { to }) => logExecution(`fetch ${to}`, impl)
* });
* },
* route({ instrument, id }) {
* instrument({
* middleware: (impl, { request }) => logExecution(
* `middleware ${request.url} (route ${id})`,
* impl
* ),
* loader: (impl, { request }) => logExecution(
* `loader ${request.url} (route ${id})`,
* impl
* ),
* action: (impl, { request }) => logExecution(
* `action ${request.url} (route ${id})`,
* impl
* ),
* })
* }
* };
*
* async function logExecution(label: string, impl: () => Promise<void>) {
* let start = performance.now();
* console.log(`start ${label}`);
* await impl();
* let duration = Math.round(performance.now() - start);
* console.log(`end ${label} (${duration}ms)`);
* }
*
* startTransition(() => {
* hydrateRoot(
* document,
* <HydratedRouter instrumentations={[logging]} />
* );
* });
* ```
*/
instrumentations?: ClientInstrumentation[];
/**
* An error handler function that will be called for any middleware, loader, action,
* or render errors that are encountered in your application. This is useful for
* logging or reporting errors instead of in the {@link ErrorBoundary} because it's not
* subject to re-rendering and will only run one time per error.
*
* The `errorInfo` parameter is passed along from
* [`componentDidCatch`](https://react.dev/reference/react/Component#componentdidcatch)
* and is only present for render errors.
*
* ```tsx
* <HydratedRouter onError={(error, info) => {
* let { location, params, pattern, errorInfo } = info;
* console.error(error, location, errorInfo);
* reportToErrorService(error, location, errorInfo);
* }} />
* ```
*/
onError?: ClientOnErrorFunction;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all state updates are wrapped in
* `React.startTransition`
* - This can lead to buggy behaviors if you are wrapping your own
* navigations/fetchers in `startTransition`.
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and router state changes will be wrapped in
* `React.startTransition` and also sent through
* [`useOptimistic`](https://react.dev/reference/react/useOptimistic) to
* surface mid-navigation router state changes to the UI.
* - When set to `false`, the router will not leverage `React.startTransition` or
* `React.useOptimistic` on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* Framework-mode router component to be used to hydrate a router from a
* {@link ServerRouter}. See [`entry.client.tsx`](../framework-conventions/entry.client.tsx).
*
* @public
* @category Framework Routers
* @mode framework
* @param props Props
* @param {dom.HydratedRouterProps.getContext} props.getContext n/a
* @param {dom.HydratedRouterProps.onError} props.onError n/a
* @returns A React element that represents the hydrated application.
*/
declare function HydratedRouter(props: HydratedRouterProps): React.JSX.Element;
declare global {
interface Window {
__FLIGHT_DATA: any[];
}
}
/**
* Get the prerendered [RSC](https://react.dev/reference/rsc/server-components)
* stream for hydration. Usually passed directly to your
* `react-server-dom-xyz/client`'s `createFromReadableStream`.
*
* @example
* import { startTransition, StrictMode } from "react";
* import { hydrateRoot } from "react-dom/client";
* import {
* unstable_getRSCStream as getRSCStream,
* unstable_RSCHydratedRouter as RSCHydratedRouter,
* } from "react-router";
* import type { unstable_RSCPayload as RSCPayload } from "react-router";
*
* createFromReadableStream(getRSCStream()).then(
* (payload: RSCServerPayload) => {
* startTransition(async () => {
* hydrateRoot(
* document,
* <StrictMode>
* <RSCHydratedRouter {...props} />
* </StrictMode>,
* {
* // Options
* }
* );
* });
* }
* );
*
* @name unstable_getRSCStream
* @public
* @category RSC
* @mode data
* @returns A [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
* that contains the [RSC](https://react.dev/reference/rsc/server-components)
* data for hydration.
*/
declare function getRSCStream(): ReadableStream;
export { HydratedRouter, type HydratedRouterProps, RouterProvider, type RouterProviderProps, getRSCStream as unstable_getRSCStream };
+173
View File
@@ -0,0 +1,173 @@
import * as React from 'react';
import { RouterProviderProps as RouterProviderProps$1, RouterInit, ClientOnErrorFunction } from 'react-router';
import { C as ClientInstrumentation } from './instrumentation-Dkmpzd13.js';
export { D as unstable_DecodeActionFunction, a as unstable_DecodeFormStateFunction, b as unstable_DecodeReplyFunction, R as unstable_RSCHydratedRouter, d as unstable_RSCManifestPayload, e as unstable_RSCPayload, f as unstable_RSCRenderPayload, c as unstable_createCallServer } from './browser-B2PdsXXH.js';
import './data-CjO11-hU.js';
type RouterProviderProps = Omit<RouterProviderProps$1, "flushSync">;
declare function RouterProvider(props: RouterProviderProps): React.JSX.Element;
/**
* Props for the {@link dom.HydratedRouter} component.
*
* @category Types
*/
interface HydratedRouterProps {
/**
* Context factory function to be passed through to {@link createBrowserRouter}.
* This function will be called to create a fresh `context` instance on each
* navigation/fetch and made available to
* [`clientAction`](../../start/framework/route-module#clientAction)/[`clientLoader`](../../start/framework/route-module#clientLoader)
* functions.
*/
getContext?: RouterInit["getContext"];
/**
* Array of instrumentation objects allowing you to instrument the router and
* individual routes prior to router initialization (and on any subsequently
* added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
* mostly useful for observability such as wrapping navigations, fetches,
* as well as route loaders/actions/middlewares with logging and/or performance
* tracing. See the [docs](../../how-to/instrumentation) for more information.
*
* ```tsx
* const logging = {
* router({ instrument }) {
* instrument({
* navigate: (impl, { to }) => logExecution(`navigate ${to}`, impl),
* fetch: (impl, { to }) => logExecution(`fetch ${to}`, impl)
* });
* },
* route({ instrument, id }) {
* instrument({
* middleware: (impl, { request }) => logExecution(
* `middleware ${request.url} (route ${id})`,
* impl
* ),
* loader: (impl, { request }) => logExecution(
* `loader ${request.url} (route ${id})`,
* impl
* ),
* action: (impl, { request }) => logExecution(
* `action ${request.url} (route ${id})`,
* impl
* ),
* })
* }
* };
*
* async function logExecution(label: string, impl: () => Promise<void>) {
* let start = performance.now();
* console.log(`start ${label}`);
* await impl();
* let duration = Math.round(performance.now() - start);
* console.log(`end ${label} (${duration}ms)`);
* }
*
* startTransition(() => {
* hydrateRoot(
* document,
* <HydratedRouter instrumentations={[logging]} />
* );
* });
* ```
*/
instrumentations?: ClientInstrumentation[];
/**
* An error handler function that will be called for any middleware, loader, action,
* or render errors that are encountered in your application. This is useful for
* logging or reporting errors instead of in the {@link ErrorBoundary} because it's not
* subject to re-rendering and will only run one time per error.
*
* The `errorInfo` parameter is passed along from
* [`componentDidCatch`](https://react.dev/reference/react/Component#componentdidcatch)
* and is only present for render errors.
*
* ```tsx
* <HydratedRouter onError={(error, info) => {
* let { location, params, pattern, errorInfo } = info;
* console.error(error, location, errorInfo);
* reportToErrorService(error, location, errorInfo);
* }} />
* ```
*/
onError?: ClientOnErrorFunction;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all state updates are wrapped in
* `React.startTransition`
* - This can lead to buggy behaviors if you are wrapping your own
* navigations/fetchers in `startTransition`.
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and router state changes will be wrapped in
* `React.startTransition` and also sent through
* [`useOptimistic`](https://react.dev/reference/react/useOptimistic) to
* surface mid-navigation router state changes to the UI.
* - When set to `false`, the router will not leverage `React.startTransition` or
* `React.useOptimistic` on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* Framework-mode router component to be used to hydrate a router from a
* {@link ServerRouter}. See [`entry.client.tsx`](../framework-conventions/entry.client.tsx).
*
* @public
* @category Framework Routers
* @mode framework
* @param props Props
* @param {dom.HydratedRouterProps.getContext} props.getContext n/a
* @param {dom.HydratedRouterProps.onError} props.onError n/a
* @returns A React element that represents the hydrated application.
*/
declare function HydratedRouter(props: HydratedRouterProps): React.JSX.Element;
declare global {
interface Window {
__FLIGHT_DATA: any[];
}
}
/**
* Get the prerendered [RSC](https://react.dev/reference/rsc/server-components)
* stream for hydration. Usually passed directly to your
* `react-server-dom-xyz/client`'s `createFromReadableStream`.
*
* @example
* import { startTransition, StrictMode } from "react";
* import { hydrateRoot } from "react-dom/client";
* import {
* unstable_getRSCStream as getRSCStream,
* unstable_RSCHydratedRouter as RSCHydratedRouter,
* } from "react-router";
* import type { unstable_RSCPayload as RSCPayload } from "react-router";
*
* createFromReadableStream(getRSCStream()).then(
* (payload: RSCServerPayload) => {
* startTransition(async () => {
* hydrateRoot(
* document,
* <StrictMode>
* <RSCHydratedRouter {...props} />
* </StrictMode>,
* {
* // Options
* }
* );
* });
* }
* );
*
* @name unstable_getRSCStream
* @public
* @category RSC
* @mode data
* @returns A [`ReadableStream`](https://developer.mozilla.org/en-US/docs/Web/API/ReadableStream)
* that contains the [RSC](https://react.dev/reference/rsc/server-components)
* data for hydration.
*/
declare function getRSCStream(): ReadableStream;
export { HydratedRouter, type HydratedRouterProps, RouterProvider, type RouterProviderProps, getRSCStream as unstable_getRSCStream };
+1018
View File
@@ -0,0 +1,1018 @@
"use strict";Object.defineProperty(exports, "__esModule", {value: true}); function _interopRequireWildcard(obj) { if (obj && obj.__esModule) { return obj; } else { var newObj = {}; if (obj != null) { for (var key in obj) { if (Object.prototype.hasOwnProperty.call(obj, key)) { newObj[key] = obj[key]; } } } newObj.default = obj; return newObj; } } function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } function _optionalChain(ops) { let lastAccessLHS = undefined; let value = ops[0]; let i = 1; while (i < ops.length) { const op = ops[i]; const fn = ops[i + 1]; i += 2; if ((op === 'optionalAccess' || op === 'optionalCall') && value == null) { return undefined; } if (op === 'access' || op === 'optionalAccess') { lastAccessLHS = value; value = fn(value); } else if (op === 'call' || op === 'optionalCall') { value = fn((...args) => value.call(lastAccessLHS, ...args)); lastAccessLHS = undefined; } } return value; }/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
"use client";
var _chunkEVX4J2F5js = require('./chunk-EVX4J2F5.js');
var _chunkSA4DP3SFjs = require('./chunk-SA4DP3SF.js');
// lib/dom-export/dom-router-provider.tsx
var _react = require('react'); var React = _interopRequireWildcard(_react); var React2 = _interopRequireWildcard(_react); var React3 = _interopRequireWildcard(_react);
var _reactdom = require('react-dom'); var ReactDOM = _interopRequireWildcard(_reactdom); var ReactDOM2 = _interopRequireWildcard(_reactdom);
var _reactrouter = require('react-router');
function RouterProvider2(props) {
return /* @__PURE__ */ React.createElement(_reactrouter.RouterProvider, { flushSync: ReactDOM.flushSync, ...props });
}
// lib/dom-export/hydrated-router.tsx
var ssrInfo = null;
var router = null;
function initSsrInfo() {
if (!ssrInfo && window.__reactRouterContext && window.__reactRouterManifest && window.__reactRouterRouteModules) {
if (window.__reactRouterManifest.sri === true) {
const importMap = document.querySelector("script[rr-importmap]");
if (_optionalChain([importMap, 'optionalAccess', _2 => _2.textContent])) {
try {
window.__reactRouterManifest.sri = JSON.parse(
importMap.textContent
).integrity;
} catch (err) {
console.error("Failed to parse import map", err);
}
}
}
ssrInfo = {
context: window.__reactRouterContext,
manifest: window.__reactRouterManifest,
routeModules: window.__reactRouterRouteModules,
stateDecodingPromise: void 0,
router: void 0,
routerInitialized: false
};
}
}
function createHydratedRouter({
getContext,
instrumentations
}) {
initSsrInfo();
if (!ssrInfo) {
throw new Error(
"You must be using the SSR features of React Router in order to skip passing a `router` prop to `<RouterProvider>`"
);
}
let localSsrInfo = ssrInfo;
if (!ssrInfo.stateDecodingPromise) {
let stream = ssrInfo.context.stream;
_reactrouter.UNSAFE_invariant.call(void 0, stream, "No stream found for single fetch decoding");
ssrInfo.context.stream = void 0;
ssrInfo.stateDecodingPromise = _reactrouter.UNSAFE_decodeViaTurboStream.call(void 0, stream, window).then((value) => {
ssrInfo.context.state = value.value;
localSsrInfo.stateDecodingPromise.value = true;
}).catch((e) => {
localSsrInfo.stateDecodingPromise.error = e;
});
}
if (ssrInfo.stateDecodingPromise.error) {
throw ssrInfo.stateDecodingPromise.error;
}
if (!ssrInfo.stateDecodingPromise.value) {
throw ssrInfo.stateDecodingPromise;
}
let routes = _reactrouter.UNSAFE_createClientRoutes.call(void 0,
ssrInfo.manifest.routes,
ssrInfo.routeModules,
ssrInfo.context.state,
ssrInfo.context.ssr,
ssrInfo.context.isSpaMode
);
let hydrationData = void 0;
if (ssrInfo.context.isSpaMode) {
let { loaderData } = ssrInfo.context.state;
if (_optionalChain([ssrInfo, 'access', _3 => _3.manifest, 'access', _4 => _4.routes, 'access', _5 => _5.root, 'optionalAccess', _6 => _6.hasLoader]) && loaderData && "root" in loaderData) {
hydrationData = {
loaderData: {
root: loaderData.root
}
};
}
} else {
hydrationData = _reactrouter.UNSAFE_getHydrationData.call(void 0, {
state: ssrInfo.context.state,
routes,
getRouteInfo: (routeId) => ({
clientLoader: _optionalChain([ssrInfo, 'access', _7 => _7.routeModules, 'access', _8 => _8[routeId], 'optionalAccess', _9 => _9.clientLoader]),
hasLoader: _optionalChain([ssrInfo, 'access', _10 => _10.manifest, 'access', _11 => _11.routes, 'access', _12 => _12[routeId], 'optionalAccess', _13 => _13.hasLoader]) === true,
hasHydrateFallback: _optionalChain([ssrInfo, 'access', _14 => _14.routeModules, 'access', _15 => _15[routeId], 'optionalAccess', _16 => _16.HydrateFallback]) != null
}),
location: window.location,
basename: _optionalChain([window, 'access', _17 => _17.__reactRouterContext, 'optionalAccess', _18 => _18.basename]),
isSpaMode: ssrInfo.context.isSpaMode
});
}
if (window.history.state && window.history.state.masked) {
window.history.replaceState(
{ ...window.history.state, masked: void 0 },
""
);
}
let router2 = _reactrouter.UNSAFE_createRouter.call(void 0, {
routes,
history: _reactrouter.UNSAFE_createBrowserHistory.call(void 0, ),
basename: ssrInfo.context.basename,
getContext,
hydrationData,
hydrationRouteProperties: _reactrouter.UNSAFE_hydrationRouteProperties,
instrumentations,
mapRouteProperties: _reactrouter.UNSAFE_mapRouteProperties,
future: {
v8_passThroughRequests: ssrInfo.context.future.v8_passThroughRequests
},
dataStrategy: _reactrouter.UNSAFE_getTurboStreamSingleFetchDataStrategy.call(void 0,
() => router2,
ssrInfo.manifest,
ssrInfo.routeModules,
ssrInfo.context.ssr,
ssrInfo.context.basename,
ssrInfo.context.future.v8_trailingSlashAwareDataRequests
),
patchRoutesOnNavigation: _reactrouter.UNSAFE_getPatchRoutesOnNavigationFunction.call(void 0,
() => router2,
ssrInfo.manifest,
ssrInfo.routeModules,
ssrInfo.context.ssr,
ssrInfo.context.routeDiscovery,
ssrInfo.context.isSpaMode,
ssrInfo.context.basename
)
});
ssrInfo.router = router2;
if (router2.state.initialized) {
ssrInfo.routerInitialized = true;
router2.initialize();
}
router2.createRoutesForHMR = /* spacer so ts-ignore does not affect the right hand of the assignment */
_reactrouter.UNSAFE_createClientRoutesWithHMRRevalidationOptOut;
window.__reactRouterDataRouter = router2;
return router2;
}
function HydratedRouter(props) {
if (!router) {
router = createHydratedRouter({
getContext: props.getContext,
instrumentations: props.instrumentations
});
}
let [criticalCss, setCriticalCss] = React2.useState(
process.env.NODE_ENV === "development" ? _optionalChain([ssrInfo, 'optionalAccess', _19 => _19.context, 'access', _20 => _20.criticalCss]) : void 0
);
React2.useEffect(() => {
if (process.env.NODE_ENV === "development") {
setCriticalCss(void 0);
}
}, []);
React2.useEffect(() => {
if (process.env.NODE_ENV === "development" && criticalCss === void 0) {
document.querySelectorAll(`[${_chunkSA4DP3SFjs.CRITICAL_CSS_DATA_ATTRIBUTE}]`).forEach((element) => element.remove());
}
}, [criticalCss]);
let [location2, setLocation] = React2.useState(router.state.location);
React2.useLayoutEffect(() => {
if (ssrInfo && ssrInfo.router && !ssrInfo.routerInitialized) {
ssrInfo.routerInitialized = true;
ssrInfo.router.initialize();
}
}, []);
React2.useLayoutEffect(() => {
if (ssrInfo && ssrInfo.router) {
return ssrInfo.router.subscribe((newState) => {
if (newState.location !== location2) {
setLocation(newState.location);
}
});
}
}, [location2]);
_reactrouter.UNSAFE_invariant.call(void 0, ssrInfo, "ssrInfo unavailable for HydratedRouter");
_reactrouter.UNSAFE_useFogOFWarDiscovery.call(void 0,
router,
ssrInfo.manifest,
ssrInfo.routeModules,
ssrInfo.context.ssr,
ssrInfo.context.routeDiscovery,
ssrInfo.context.isSpaMode
);
return (
// This fragment is important to ensure we match the <ServerRouter> JSX
// structure so that useId values hydrate correctly
/* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(
_reactrouter.UNSAFE_FrameworkContext.Provider,
{
value: {
manifest: ssrInfo.manifest,
routeModules: ssrInfo.routeModules,
future: ssrInfo.context.future,
criticalCss,
ssr: ssrInfo.context.ssr,
isSpaMode: ssrInfo.context.isSpaMode,
routeDiscovery: ssrInfo.context.routeDiscovery
}
},
/* @__PURE__ */ React2.createElement(_reactrouter.UNSAFE_RemixErrorBoundary, { location: location2 }, /* @__PURE__ */ React2.createElement(
RouterProvider2,
{
router,
useTransitions: props.useTransitions,
onError: props.onError
}
))
), /* @__PURE__ */ React2.createElement(React2.Fragment, null))
);
}
// lib/rsc/browser.tsx
var defaultManifestPath = "/__manifest";
function createCallServer({
createFromReadableStream,
createTemporaryReferenceSet,
encodeReply,
fetch: fetchImplementation = fetch
}) {
const globalVar = window;
let landedActionId = 0;
return async (id, args) => {
let actionId = globalVar.__routerActionID = (_nullishCoalesce(globalVar.__routerActionID, () => ( (globalVar.__routerActionID = 0)))) + 1;
const temporaryReferences = createTemporaryReferenceSet();
const payloadPromise = fetchImplementation(
new Request(location.href, {
body: await encodeReply(args, { temporaryReferences }),
method: "POST",
headers: {
Accept: "text/x-component",
"rsc-action-id": id
}
})
).then((response) => {
if (!response.body) {
throw new Error("No response body");
}
return createFromReadableStream(response.body, {
temporaryReferences
});
});
React3.startTransition(
() => (
// @ts-expect-error - Needs React 19 types
Promise.resolve(payloadPromise).then(async (payload) => {
if (payload.type === "redirect") {
let location2 = normalizeRedirectLocation(payload.location);
if (payload.reload || isExternalLocation(location2)) {
if (_chunkSA4DP3SFjs.hasInvalidProtocol.call(void 0, location2)) {
throw new Error("Invalid redirect location");
}
window.location.href = location2;
return;
}
React3.startTransition(() => {
globalVar.__reactRouterDataRouter.navigate(location2, {
replace: payload.replace
});
});
return;
}
if (payload.type !== "action") {
throw new Error("Unexpected payload type");
}
const rerender = await payload.rerender;
if (rerender && landedActionId < actionId && globalVar.__routerActionID <= actionId) {
if (rerender.type === "redirect") {
let location2 = normalizeRedirectLocation(rerender.location);
if (rerender.reload || isExternalLocation(location2)) {
if (_chunkSA4DP3SFjs.hasInvalidProtocol.call(void 0, location2)) {
throw new Error("Invalid redirect location");
}
window.location.href = location2;
return;
}
React3.startTransition(() => {
globalVar.__reactRouterDataRouter.navigate(location2, {
replace: rerender.replace
});
});
return;
}
React3.startTransition(() => {
let lastMatch;
for (const match of rerender.matches) {
globalVar.__reactRouterDataRouter.patchRoutes(
_nullishCoalesce(_optionalChain([lastMatch, 'optionalAccess', _21 => _21.id]), () => ( null)),
[createRouteFromServerManifest(match)],
true
);
lastMatch = match;
}
window.__reactRouterDataRouter._internalSetStateDoNotUseOrYouWillBreakYourApp(
{
loaderData: Object.assign(
{},
globalVar.__reactRouterDataRouter.state.loaderData,
rerender.loaderData
),
errors: rerender.errors ? Object.assign(
{},
globalVar.__reactRouterDataRouter.state.errors,
rerender.errors
) : null
}
);
});
}
}).catch(() => {
})
)
);
return payloadPromise.then((payload) => {
if (payload.type !== "action" && payload.type !== "redirect") {
throw new Error("Unexpected payload type");
}
return payload.actionResult;
});
};
}
function createRouterFromPayload({
fetchImplementation,
createFromReadableStream,
getContext,
payload
}) {
const globalVar = window;
if (globalVar.__reactRouterDataRouter && globalVar.__reactRouterRouteModules)
return {
router: globalVar.__reactRouterDataRouter,
routeModules: globalVar.__reactRouterRouteModules
};
if (payload.type !== "render") throw new Error("Invalid payload type");
globalVar.__reactRouterRouteModules = _nullishCoalesce(globalVar.__reactRouterRouteModules, () => ( {}));
_chunkEVX4J2F5js.populateRSCRouteModules.call(void 0, globalVar.__reactRouterRouteModules, payload.matches);
let routes = payload.matches.reduceRight((previous, match) => {
const route = createRouteFromServerManifest(
match,
payload
);
if (previous.length > 0) {
route.children = previous;
} else if (!route.index) {
route.children = [];
}
return [route];
}, []);
let applyPatchesPromise;
globalVar.__reactRouterDataRouter = _chunkSA4DP3SFjs.createRouter.call(void 0, {
routes,
getContext,
basename: payload.basename,
history: _chunkSA4DP3SFjs.createBrowserHistory.call(void 0, ),
hydrationData: _chunkEVX4J2F5js.getHydrationData.call(void 0, {
state: {
loaderData: payload.loaderData,
actionData: payload.actionData,
errors: payload.errors
},
routes,
getRouteInfo: (routeId) => {
let match = payload.matches.find((m) => m.id === routeId);
_chunkSA4DP3SFjs.invariant.call(void 0, match, "Route not found in payload");
return {
clientLoader: match.clientLoader,
hasLoader: match.hasLoader,
hasHydrateFallback: match.hydrateFallbackElement != null
};
},
location: payload.location,
basename: payload.basename,
isSpaMode: false
}),
async patchRoutesOnNavigation({ path, signal }) {
if (payload.routeDiscovery.mode === "initial") {
if (!applyPatchesPromise) {
applyPatchesPromise = (async () => {
if (!payload.patches) return;
let patches = await payload.patches;
React3.startTransition(() => {
patches.forEach((p) => {
window.__reactRouterDataRouter.patchRoutes(_nullishCoalesce(p.parentId, () => ( null)), [
createRouteFromServerManifest(p)
]);
});
});
})();
}
await applyPatchesPromise;
return;
}
if (discoveredPaths.has(path)) {
return;
}
await fetchAndApplyManifestPatches(
[path],
createFromReadableStream,
fetchImplementation,
signal
);
},
// FIXME: Pass `build.ssr` into this function
dataStrategy: getRSCSingleFetchDataStrategy(
() => globalVar.__reactRouterDataRouter,
true,
payload.basename,
createFromReadableStream,
fetchImplementation
)
});
if (globalVar.__reactRouterDataRouter.state.initialized) {
globalVar.__routerInitialized = true;
globalVar.__reactRouterDataRouter.initialize();
} else {
globalVar.__routerInitialized = false;
}
let lastLoaderData = void 0;
globalVar.__reactRouterDataRouter.subscribe(({ loaderData, actionData }) => {
if (lastLoaderData !== loaderData) {
globalVar.__routerActionID = (_nullishCoalesce(globalVar.__routerActionID, () => ( (globalVar.__routerActionID = 0)))) + 1;
}
});
globalVar.__reactRouterDataRouter._updateRoutesForHMR = (routeUpdateByRouteId) => {
const oldRoutes = window.__reactRouterDataRouter.routes;
const newRoutes = [];
function walkRoutes(routes2, parentId) {
return routes2.map((route) => {
const routeUpdate = routeUpdateByRouteId.get(route.id);
if (routeUpdate) {
const {
routeModule,
hasAction,
hasComponent,
hasErrorBoundary,
hasLoader
} = routeUpdate;
const newRoute = createRouteFromServerManifest({
clientAction: routeModule.clientAction,
clientLoader: routeModule.clientLoader,
element: route.element,
errorElement: route.errorElement,
handle: route.handle,
hasAction,
hasComponent,
hasErrorBoundary,
hasLoader,
hydrateFallbackElement: route.hydrateFallbackElement,
id: route.id,
index: route.index,
links: routeModule.links,
meta: routeModule.meta,
parentId,
path: route.path,
shouldRevalidate: routeModule.shouldRevalidate
});
if (route.children) {
newRoute.children = walkRoutes(route.children, route.id);
}
return newRoute;
}
const updatedRoute = { ...route };
if (route.children) {
updatedRoute.children = walkRoutes(route.children, route.id);
}
return updatedRoute;
});
}
newRoutes.push(
...walkRoutes(oldRoutes, void 0)
);
window.__reactRouterDataRouter._internalSetRoutes(newRoutes);
};
return {
router: globalVar.__reactRouterDataRouter,
routeModules: globalVar.__reactRouterRouteModules
};
}
var renderedRoutesContext = _chunkSA4DP3SFjs.createContext.call(void 0, );
function getRSCSingleFetchDataStrategy(getRouter, ssr, basename, createFromReadableStream, fetchImplementation) {
let dataStrategy = _chunkSA4DP3SFjs.getSingleFetchDataStrategyImpl.call(void 0,
getRouter,
(match) => {
let M = match;
return {
hasLoader: M.route.hasLoader,
hasClientLoader: M.route.hasClientLoader,
hasComponent: M.route.hasComponent,
hasAction: M.route.hasAction,
hasClientAction: M.route.hasClientAction
};
},
// pass map into fetchAndDecode so it can add payloads
getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation),
ssr,
basename,
// .rsc requests are always trailing slash aware
true,
// If the route has a component but we don't have an element, we need to hit
// the server loader flow regardless of whether the client loader calls
// `serverLoader` or not, otherwise we'll have nothing to render.
(match) => {
let M = match;
return M.route.hasComponent && !M.route.element;
}
);
return async (args) => args.runClientMiddleware(async () => {
let context = args.context;
context.set(renderedRoutesContext, []);
let results = await dataStrategy(args);
const renderedRoutesById = /* @__PURE__ */ new Map();
for (const route of context.get(renderedRoutesContext)) {
if (!renderedRoutesById.has(route.id)) {
renderedRoutesById.set(route.id, []);
}
renderedRoutesById.get(route.id).push(route);
}
React3.startTransition(() => {
for (const match of args.matches) {
const renderedRoutes = renderedRoutesById.get(match.route.id);
if (renderedRoutes) {
for (const rendered of renderedRoutes) {
window.__reactRouterDataRouter.patchRoutes(
_nullishCoalesce(rendered.parentId, () => ( null)),
[createRouteFromServerManifest(rendered)],
true
);
}
}
}
});
return results;
});
}
function getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation) {
return async (args, basename, trailingSlashAware, targetRoutes) => {
let { request, context } = args;
let url = _chunkSA4DP3SFjs.singleFetchUrl.call(void 0, request.url, basename, trailingSlashAware, "rsc");
if (request.method === "GET") {
url = _chunkSA4DP3SFjs.stripIndexParam.call(void 0, url);
if (targetRoutes) {
url.searchParams.set("_routes", targetRoutes.join(","));
}
}
let res = await fetchImplementation(
new Request(url, await _chunkSA4DP3SFjs.createRequestInit.call(void 0, request))
);
if (res.status >= 400 && !res.headers.has("X-Remix-Response")) {
throw new (0, _chunkSA4DP3SFjs.ErrorResponseImpl)(res.status, res.statusText, await res.text());
}
_chunkSA4DP3SFjs.invariant.call(void 0, res.body, "No response body to decode");
try {
const payload = await createFromReadableStream(res.body, {
temporaryReferences: void 0
});
if (payload.type === "redirect") {
return {
status: res.status,
data: {
redirect: {
redirect: payload.location,
reload: payload.reload,
replace: payload.replace,
revalidate: false,
status: payload.status
}
}
};
}
if (payload.type !== "render") {
throw new Error("Unexpected payload type");
}
context.get(renderedRoutesContext).push(...payload.matches);
let results = { routes: {} };
const dataKey = _chunkSA4DP3SFjs.isMutationMethod.call(void 0, request.method) ? "actionData" : "loaderData";
for (let [routeId, data] of Object.entries(payload[dataKey] || {})) {
results.routes[routeId] = { data };
}
if (payload.errors) {
for (let [routeId, error] of Object.entries(payload.errors)) {
results.routes[routeId] = { error };
}
}
return { status: res.status, data: results };
} catch (cause) {
throw new Error("Unable to decode RSC response", { cause });
}
};
}
function RSCHydratedRouter({
createFromReadableStream,
fetch: fetchImplementation = fetch,
payload,
getContext
}) {
if (payload.type !== "render") throw new Error("Invalid payload type");
let { routeDiscovery } = payload;
let { router: router2, routeModules } = React3.useMemo(
() => createRouterFromPayload({
payload,
fetchImplementation,
getContext,
createFromReadableStream
}),
[createFromReadableStream, payload, fetchImplementation, getContext]
);
React3.useEffect(() => {
_chunkSA4DP3SFjs.setIsHydrated.call(void 0, );
}, []);
React3.useLayoutEffect(() => {
const globalVar = window;
if (!globalVar.__routerInitialized) {
globalVar.__routerInitialized = true;
globalVar.__reactRouterDataRouter.initialize();
}
}, []);
let [{ routes, state }, setState] = React3.useState(() => ({
routes: cloneRoutes(router2.routes),
state: router2.state
}));
React3.useLayoutEffect(
() => router2.subscribe((newState) => {
if (diffRoutes(router2.routes, routes))
React3.startTransition(() => {
setState({
routes: cloneRoutes(router2.routes),
state: newState
});
});
}),
[router2.subscribe, routes, router2]
);
const transitionEnabledRouter = React3.useMemo(
() => ({
...router2,
state,
routes
}),
[router2, routes, state]
);
React3.useEffect(() => {
if (routeDiscovery.mode === "initial" || // @ts-expect-error - TS doesn't know about this yet
_optionalChain([window, 'access', _22 => _22.navigator, 'optionalAccess', _23 => _23.connection, 'optionalAccess', _24 => _24.saveData]) === true) {
return;
}
function registerElement(el) {
let path = el.tagName === "FORM" ? el.getAttribute("action") : el.getAttribute("href");
if (!path) {
return;
}
let pathname = el.tagName === "A" ? el.pathname : new URL(path, window.location.origin).pathname;
if (!discoveredPaths.has(pathname)) {
nextPaths.add(pathname);
}
}
async function fetchPatches() {
document.querySelectorAll("a[data-discover], form[data-discover]").forEach(registerElement);
let paths = Array.from(nextPaths.keys()).filter((path) => {
if (discoveredPaths.has(path)) {
nextPaths.delete(path);
return false;
}
return true;
});
if (paths.length === 0) {
return;
}
try {
await fetchAndApplyManifestPatches(
paths,
createFromReadableStream,
fetchImplementation
);
} catch (e) {
console.error("Failed to fetch manifest patches", e);
}
}
let debouncedFetchPatches = debounce(fetchPatches, 100);
fetchPatches();
let observer = new MutationObserver(() => debouncedFetchPatches());
observer.observe(document.documentElement, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ["data-discover", "href", "action"]
});
}, [routeDiscovery, createFromReadableStream, fetchImplementation]);
const frameworkContext = {
future: {
// These flags have no runtime impact so can always be false. If we add
// flags that drive runtime behavior they'll need to be proxied through.
v8_middleware: false,
v8_trailingSlashAwareDataRequests: true,
// always on for RSC
v8_passThroughRequests: true
// always on for RSC
},
isSpaMode: false,
ssr: true,
criticalCss: "",
manifest: {
routes: {},
version: "1",
url: "",
entry: {
module: "",
imports: []
}
},
routeDiscovery: payload.routeDiscovery.mode === "initial" ? { mode: "initial", manifestPath: defaultManifestPath } : {
mode: "lazy",
manifestPath: payload.routeDiscovery.manifestPath || defaultManifestPath
},
routeModules
};
return /* @__PURE__ */ React3.createElement(_chunkSA4DP3SFjs.RSCRouterContext.Provider, { value: true }, /* @__PURE__ */ React3.createElement(_chunkEVX4J2F5js.RSCRouterGlobalErrorBoundary, { location: state.location }, /* @__PURE__ */ React3.createElement(_chunkSA4DP3SFjs.FrameworkContext.Provider, { value: frameworkContext }, /* @__PURE__ */ React3.createElement(
_chunkSA4DP3SFjs.RouterProvider,
{
router: transitionEnabledRouter,
flushSync: ReactDOM2.flushSync
}
))));
}
function createRouteFromServerManifest(match, payload) {
let hasInitialData = payload && match.id in payload.loaderData;
let initialData = _optionalChain([payload, 'optionalAccess', _25 => _25.loaderData, 'access', _26 => _26[match.id]]);
let hasInitialError = _optionalChain([payload, 'optionalAccess', _27 => _27.errors]) && match.id in payload.errors;
let initialError = _optionalChain([payload, 'optionalAccess', _28 => _28.errors, 'optionalAccess', _29 => _29[match.id]]);
let isHydrationRequest = _optionalChain([match, 'access', _30 => _30.clientLoader, 'optionalAccess', _31 => _31.hydrate]) === true || !match.hasLoader || // If the route has a component but we don't have an element, we need to hit
// the server loader flow regardless of whether the client loader calls
// `serverLoader` or not, otherwise we'll have nothing to render.
match.hasComponent && !match.element;
_chunkSA4DP3SFjs.invariant.call(void 0, window.__reactRouterRouteModules);
_chunkEVX4J2F5js.populateRSCRouteModules.call(void 0, window.__reactRouterRouteModules, match);
let dataRoute = {
id: match.id,
element: match.element,
errorElement: match.errorElement,
handle: match.handle,
hasErrorBoundary: match.hasErrorBoundary,
hydrateFallbackElement: match.hydrateFallbackElement,
index: match.index,
loader: match.clientLoader ? async (args, singleFetch) => {
let _isHydrationRequest = isHydrationRequest;
isHydrationRequest = false;
let result = await match.clientLoader({
...args,
serverLoader: () => {
preventInvalidServerHandlerCall(
"loader",
match.id,
match.hasLoader
);
if (_isHydrationRequest) {
if (hasInitialData) {
return initialData;
}
if (hasInitialError) {
throw initialError;
}
}
return callSingleFetch(singleFetch);
}
});
return result;
} : (
// We always make the call in this RSC world since even if we don't
// have a `loader` we may need to get the `element` implementation
(_, singleFetch) => callSingleFetch(singleFetch)
),
action: match.clientAction ? (args, singleFetch) => match.clientAction({
...args,
serverAction: async () => {
preventInvalidServerHandlerCall(
"action",
match.id,
match.hasLoader
);
return await callSingleFetch(singleFetch);
}
}) : match.hasAction ? (_, singleFetch) => callSingleFetch(singleFetch) : () => {
throw _chunkSA4DP3SFjs.noActionDefinedError.call(void 0, "action", match.id);
},
path: match.path,
shouldRevalidate: match.shouldRevalidate,
// We always have a "loader" in this RSC world since even if we don't
// have a `loader` we may need to get the `element` implementation
hasLoader: true,
hasClientLoader: match.clientLoader != null,
hasAction: match.hasAction,
hasClientAction: match.clientAction != null
};
if (typeof dataRoute.loader === "function") {
dataRoute.loader.hydrate = _chunkSA4DP3SFjs.shouldHydrateRouteLoader.call(void 0,
match.id,
match.clientLoader,
match.hasLoader,
false
);
}
return dataRoute;
}
function callSingleFetch(singleFetch) {
_chunkSA4DP3SFjs.invariant.call(void 0, typeof singleFetch === "function", "Invalid singleFetch parameter");
return singleFetch();
}
function preventInvalidServerHandlerCall(type, routeId, hasHandler) {
if (!hasHandler) {
let fn = type === "action" ? "serverAction()" : "serverLoader()";
let msg = `You are trying to call ${fn} on a route that does not have a server ${type} (routeId: "${routeId}")`;
console.error(msg);
throw new (0, _chunkSA4DP3SFjs.ErrorResponseImpl)(400, "Bad Request", new Error(msg), true);
}
}
var nextPaths = /* @__PURE__ */ new Set();
var discoveredPathsMaxSize = 1e3;
var discoveredPaths = /* @__PURE__ */ new Set();
function getManifestUrl(paths) {
if (paths.length === 0) {
return null;
}
if (paths.length === 1) {
return new URL(`${paths[0]}.manifest`, window.location.origin);
}
const globalVar = window;
let basename = (_nullishCoalesce(globalVar.__reactRouterDataRouter.basename, () => ( ""))).replace(
/^\/|\/$/g,
""
);
let url = new URL(`${basename}/.manifest`, window.location.origin);
url.searchParams.set("paths", paths.sort().join(","));
return url;
}
async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, signal) {
paths = _chunkSA4DP3SFjs.getPathsWithAncestors.call(void 0, paths);
let url = getManifestUrl(paths);
if (url == null) {
return;
}
if (url.toString().length > _chunkSA4DP3SFjs.URL_LIMIT) {
nextPaths.clear();
return;
}
let response = await fetchImplementation(new Request(url, { signal }));
if (!response.body || response.status < 200 || response.status >= 300) {
throw new Error("Unable to fetch new route matches from the server");
}
let payload = await createFromReadableStream(response.body, {
temporaryReferences: void 0
});
if (payload.type !== "manifest") {
throw new Error("Failed to patch routes");
}
paths.forEach((p) => addToFifoQueue(p, discoveredPaths));
let patches = await payload.patches;
React3.startTransition(() => {
patches.forEach((p) => {
window.__reactRouterDataRouter.patchRoutes(
_nullishCoalesce(p.parentId, () => ( null)),
[createRouteFromServerManifest(p)]
);
});
});
}
function addToFifoQueue(path, queue) {
if (queue.size >= discoveredPathsMaxSize) {
let first = queue.values().next().value;
if (typeof first === "string") queue.delete(first);
}
queue.add(path);
}
function debounce(callback, wait) {
let timeoutId;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => callback(...args), wait);
};
}
function isExternalLocation(location2) {
const newLocation = new URL(location2, window.location.href);
return newLocation.origin !== window.location.origin;
}
function normalizeRedirectLocation(location2) {
if (_chunkSA4DP3SFjs.PROTOCOL_RELATIVE_URL_REGEX.test(location2)) {
let path = _chunkSA4DP3SFjs.resolvePath.call(void 0, location2);
return path.pathname + path.search + path.hash;
}
return location2;
}
function cloneRoutes(routes) {
if (!routes) return void 0;
return routes.map((route) => ({
...route,
children: cloneRoutes(route.children)
}));
}
function diffRoutes(a, b) {
if (a.length !== b.length) return true;
return a.some((route, index) => {
if (route.element !== b[index].element) return true;
if (route.errorElement !== b[index].errorElement)
return true;
if (route.hydrateFallbackElement !== b[index].hydrateFallbackElement)
return true;
if (route.hasErrorBoundary !== b[index].hasErrorBoundary)
return true;
if (route.hasLoader !== b[index].hasLoader) return true;
if (route.hasClientLoader !== b[index].hasClientLoader)
return true;
if (route.hasAction !== b[index].hasAction) return true;
if (route.hasClientAction !== b[index].hasClientAction)
return true;
return diffRoutes(route.children || [], b[index].children || []);
});
}
// lib/rsc/html-stream/browser.ts
function getRSCStream() {
let encoder = new TextEncoder();
let streamController = null;
let rscStream = new ReadableStream({
start(controller) {
if (typeof window === "undefined") {
return;
}
let handleChunk = (chunk) => {
if (typeof chunk === "string") {
controller.enqueue(encoder.encode(chunk));
} else {
controller.enqueue(chunk);
}
};
window.__FLIGHT_DATA || (window.__FLIGHT_DATA = []);
window.__FLIGHT_DATA.forEach(handleChunk);
window.__FLIGHT_DATA.push = (chunk) => {
handleChunk(chunk);
return 0;
};
streamController = controller;
}
});
if (typeof document !== "undefined" && document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
_optionalChain([streamController, 'optionalAccess', _32 => _32.close, 'call', _33 => _33()]);
});
} else {
_optionalChain([streamController, 'optionalAccess', _34 => _34.close, 'call', _35 => _35()]);
}
return rscStream;
}
exports.HydratedRouter = HydratedRouter; exports.RouterProvider = RouterProvider2; exports.unstable_RSCHydratedRouter = RSCHydratedRouter; exports.unstable_createCallServer = createCallServer; exports.unstable_getRSCStream = getRSCStream;
+1010
View File
@@ -0,0 +1,1010 @@
/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
"use client";
import {
RSCRouterGlobalErrorBoundary,
getHydrationData,
populateRSCRouteModules
} from "./chunk-IJF3QNGC.mjs";
import {
CRITICAL_CSS_DATA_ATTRIBUTE,
ErrorResponseImpl,
FrameworkContext,
PROTOCOL_RELATIVE_URL_REGEX,
RSCRouterContext,
RemixErrorBoundary,
RouterProvider,
URL_LIMIT,
createBrowserHistory,
createClientRoutes,
createClientRoutesWithHMRRevalidationOptOut,
createContext,
createRequestInit,
createRouter,
decodeViaTurboStream,
getPatchRoutesOnNavigationFunction,
getPathsWithAncestors,
getSingleFetchDataStrategyImpl,
getTurboStreamSingleFetchDataStrategy,
hasInvalidProtocol,
hydrationRouteProperties,
invariant,
isMutationMethod,
mapRouteProperties,
noActionDefinedError,
resolvePath,
setIsHydrated,
shouldHydrateRouteLoader,
singleFetchUrl,
stripIndexParam,
useFogOFWarDiscovery
} from "./chunk-KS7C4IRE.mjs";
// lib/dom-export/dom-router-provider.tsx
import * as React from "react";
import * as ReactDOM from "react-dom";
function RouterProvider2(props) {
return /* @__PURE__ */ React.createElement(RouterProvider, { flushSync: ReactDOM.flushSync, ...props });
}
// lib/dom-export/hydrated-router.tsx
import * as React2 from "react";
var ssrInfo = null;
var router = null;
function initSsrInfo() {
if (!ssrInfo && window.__reactRouterContext && window.__reactRouterManifest && window.__reactRouterRouteModules) {
if (window.__reactRouterManifest.sri === true) {
const importMap = document.querySelector("script[rr-importmap]");
if (importMap?.textContent) {
try {
window.__reactRouterManifest.sri = JSON.parse(
importMap.textContent
).integrity;
} catch (err) {
console.error("Failed to parse import map", err);
}
}
}
ssrInfo = {
context: window.__reactRouterContext,
manifest: window.__reactRouterManifest,
routeModules: window.__reactRouterRouteModules,
stateDecodingPromise: void 0,
router: void 0,
routerInitialized: false
};
}
}
function createHydratedRouter({
getContext,
instrumentations
}) {
initSsrInfo();
if (!ssrInfo) {
throw new Error(
"You must be using the SSR features of React Router in order to skip passing a `router` prop to `<RouterProvider>`"
);
}
let localSsrInfo = ssrInfo;
if (!ssrInfo.stateDecodingPromise) {
let stream = ssrInfo.context.stream;
invariant(stream, "No stream found for single fetch decoding");
ssrInfo.context.stream = void 0;
ssrInfo.stateDecodingPromise = decodeViaTurboStream(stream, window).then((value) => {
ssrInfo.context.state = value.value;
localSsrInfo.stateDecodingPromise.value = true;
}).catch((e) => {
localSsrInfo.stateDecodingPromise.error = e;
});
}
if (ssrInfo.stateDecodingPromise.error) {
throw ssrInfo.stateDecodingPromise.error;
}
if (!ssrInfo.stateDecodingPromise.value) {
throw ssrInfo.stateDecodingPromise;
}
let routes = createClientRoutes(
ssrInfo.manifest.routes,
ssrInfo.routeModules,
ssrInfo.context.state,
ssrInfo.context.ssr,
ssrInfo.context.isSpaMode
);
let hydrationData = void 0;
if (ssrInfo.context.isSpaMode) {
let { loaderData } = ssrInfo.context.state;
if (ssrInfo.manifest.routes.root?.hasLoader && loaderData && "root" in loaderData) {
hydrationData = {
loaderData: {
root: loaderData.root
}
};
}
} else {
hydrationData = getHydrationData({
state: ssrInfo.context.state,
routes,
getRouteInfo: (routeId) => ({
clientLoader: ssrInfo.routeModules[routeId]?.clientLoader,
hasLoader: ssrInfo.manifest.routes[routeId]?.hasLoader === true,
hasHydrateFallback: ssrInfo.routeModules[routeId]?.HydrateFallback != null
}),
location: window.location,
basename: window.__reactRouterContext?.basename,
isSpaMode: ssrInfo.context.isSpaMode
});
}
if (window.history.state && window.history.state.masked) {
window.history.replaceState(
{ ...window.history.state, masked: void 0 },
""
);
}
let router2 = createRouter({
routes,
history: createBrowserHistory(),
basename: ssrInfo.context.basename,
getContext,
hydrationData,
hydrationRouteProperties,
instrumentations,
mapRouteProperties,
future: {
v8_passThroughRequests: ssrInfo.context.future.v8_passThroughRequests
},
dataStrategy: getTurboStreamSingleFetchDataStrategy(
() => router2,
ssrInfo.manifest,
ssrInfo.routeModules,
ssrInfo.context.ssr,
ssrInfo.context.basename,
ssrInfo.context.future.v8_trailingSlashAwareDataRequests
),
patchRoutesOnNavigation: getPatchRoutesOnNavigationFunction(
() => router2,
ssrInfo.manifest,
ssrInfo.routeModules,
ssrInfo.context.ssr,
ssrInfo.context.routeDiscovery,
ssrInfo.context.isSpaMode,
ssrInfo.context.basename
)
});
ssrInfo.router = router2;
if (router2.state.initialized) {
ssrInfo.routerInitialized = true;
router2.initialize();
}
router2.createRoutesForHMR = /* spacer so ts-ignore does not affect the right hand of the assignment */
createClientRoutesWithHMRRevalidationOptOut;
window.__reactRouterDataRouter = router2;
return router2;
}
function HydratedRouter(props) {
if (!router) {
router = createHydratedRouter({
getContext: props.getContext,
instrumentations: props.instrumentations
});
}
let [criticalCss, setCriticalCss] = React2.useState(
process.env.NODE_ENV === "development" ? ssrInfo?.context.criticalCss : void 0
);
React2.useEffect(() => {
if (process.env.NODE_ENV === "development") {
setCriticalCss(void 0);
}
}, []);
React2.useEffect(() => {
if (process.env.NODE_ENV === "development" && criticalCss === void 0) {
document.querySelectorAll(`[${CRITICAL_CSS_DATA_ATTRIBUTE}]`).forEach((element) => element.remove());
}
}, [criticalCss]);
let [location2, setLocation] = React2.useState(router.state.location);
React2.useLayoutEffect(() => {
if (ssrInfo && ssrInfo.router && !ssrInfo.routerInitialized) {
ssrInfo.routerInitialized = true;
ssrInfo.router.initialize();
}
}, []);
React2.useLayoutEffect(() => {
if (ssrInfo && ssrInfo.router) {
return ssrInfo.router.subscribe((newState) => {
if (newState.location !== location2) {
setLocation(newState.location);
}
});
}
}, [location2]);
invariant(ssrInfo, "ssrInfo unavailable for HydratedRouter");
useFogOFWarDiscovery(
router,
ssrInfo.manifest,
ssrInfo.routeModules,
ssrInfo.context.ssr,
ssrInfo.context.routeDiscovery,
ssrInfo.context.isSpaMode
);
return (
// This fragment is important to ensure we match the <ServerRouter> JSX
// structure so that useId values hydrate correctly
/* @__PURE__ */ React2.createElement(React2.Fragment, null, /* @__PURE__ */ React2.createElement(
FrameworkContext.Provider,
{
value: {
manifest: ssrInfo.manifest,
routeModules: ssrInfo.routeModules,
future: ssrInfo.context.future,
criticalCss,
ssr: ssrInfo.context.ssr,
isSpaMode: ssrInfo.context.isSpaMode,
routeDiscovery: ssrInfo.context.routeDiscovery
}
},
/* @__PURE__ */ React2.createElement(RemixErrorBoundary, { location: location2 }, /* @__PURE__ */ React2.createElement(
RouterProvider2,
{
router,
useTransitions: props.useTransitions,
onError: props.onError
}
))
), /* @__PURE__ */ React2.createElement(React2.Fragment, null))
);
}
// lib/rsc/browser.tsx
import * as React3 from "react";
import * as ReactDOM2 from "react-dom";
var defaultManifestPath = "/__manifest";
function createCallServer({
createFromReadableStream,
createTemporaryReferenceSet,
encodeReply,
fetch: fetchImplementation = fetch
}) {
const globalVar = window;
let landedActionId = 0;
return async (id, args) => {
let actionId = globalVar.__routerActionID = (globalVar.__routerActionID ?? (globalVar.__routerActionID = 0)) + 1;
const temporaryReferences = createTemporaryReferenceSet();
const payloadPromise = fetchImplementation(
new Request(location.href, {
body: await encodeReply(args, { temporaryReferences }),
method: "POST",
headers: {
Accept: "text/x-component",
"rsc-action-id": id
}
})
).then((response) => {
if (!response.body) {
throw new Error("No response body");
}
return createFromReadableStream(response.body, {
temporaryReferences
});
});
React3.startTransition(
() => (
// @ts-expect-error - Needs React 19 types
Promise.resolve(payloadPromise).then(async (payload) => {
if (payload.type === "redirect") {
let location2 = normalizeRedirectLocation(payload.location);
if (payload.reload || isExternalLocation(location2)) {
if (hasInvalidProtocol(location2)) {
throw new Error("Invalid redirect location");
}
window.location.href = location2;
return;
}
React3.startTransition(() => {
globalVar.__reactRouterDataRouter.navigate(location2, {
replace: payload.replace
});
});
return;
}
if (payload.type !== "action") {
throw new Error("Unexpected payload type");
}
const rerender = await payload.rerender;
if (rerender && landedActionId < actionId && globalVar.__routerActionID <= actionId) {
if (rerender.type === "redirect") {
let location2 = normalizeRedirectLocation(rerender.location);
if (rerender.reload || isExternalLocation(location2)) {
if (hasInvalidProtocol(location2)) {
throw new Error("Invalid redirect location");
}
window.location.href = location2;
return;
}
React3.startTransition(() => {
globalVar.__reactRouterDataRouter.navigate(location2, {
replace: rerender.replace
});
});
return;
}
React3.startTransition(() => {
let lastMatch;
for (const match of rerender.matches) {
globalVar.__reactRouterDataRouter.patchRoutes(
lastMatch?.id ?? null,
[createRouteFromServerManifest(match)],
true
);
lastMatch = match;
}
window.__reactRouterDataRouter._internalSetStateDoNotUseOrYouWillBreakYourApp(
{
loaderData: Object.assign(
{},
globalVar.__reactRouterDataRouter.state.loaderData,
rerender.loaderData
),
errors: rerender.errors ? Object.assign(
{},
globalVar.__reactRouterDataRouter.state.errors,
rerender.errors
) : null
}
);
});
}
}).catch(() => {
})
)
);
return payloadPromise.then((payload) => {
if (payload.type !== "action" && payload.type !== "redirect") {
throw new Error("Unexpected payload type");
}
return payload.actionResult;
});
};
}
function createRouterFromPayload({
fetchImplementation,
createFromReadableStream,
getContext,
payload
}) {
const globalVar = window;
if (globalVar.__reactRouterDataRouter && globalVar.__reactRouterRouteModules)
return {
router: globalVar.__reactRouterDataRouter,
routeModules: globalVar.__reactRouterRouteModules
};
if (payload.type !== "render") throw new Error("Invalid payload type");
globalVar.__reactRouterRouteModules = globalVar.__reactRouterRouteModules ?? {};
populateRSCRouteModules(globalVar.__reactRouterRouteModules, payload.matches);
let routes = payload.matches.reduceRight((previous, match) => {
const route = createRouteFromServerManifest(
match,
payload
);
if (previous.length > 0) {
route.children = previous;
} else if (!route.index) {
route.children = [];
}
return [route];
}, []);
let applyPatchesPromise;
globalVar.__reactRouterDataRouter = createRouter({
routes,
getContext,
basename: payload.basename,
history: createBrowserHistory(),
hydrationData: getHydrationData({
state: {
loaderData: payload.loaderData,
actionData: payload.actionData,
errors: payload.errors
},
routes,
getRouteInfo: (routeId) => {
let match = payload.matches.find((m) => m.id === routeId);
invariant(match, "Route not found in payload");
return {
clientLoader: match.clientLoader,
hasLoader: match.hasLoader,
hasHydrateFallback: match.hydrateFallbackElement != null
};
},
location: payload.location,
basename: payload.basename,
isSpaMode: false
}),
async patchRoutesOnNavigation({ path, signal }) {
if (payload.routeDiscovery.mode === "initial") {
if (!applyPatchesPromise) {
applyPatchesPromise = (async () => {
if (!payload.patches) return;
let patches = await payload.patches;
React3.startTransition(() => {
patches.forEach((p) => {
window.__reactRouterDataRouter.patchRoutes(p.parentId ?? null, [
createRouteFromServerManifest(p)
]);
});
});
})();
}
await applyPatchesPromise;
return;
}
if (discoveredPaths.has(path)) {
return;
}
await fetchAndApplyManifestPatches(
[path],
createFromReadableStream,
fetchImplementation,
signal
);
},
// FIXME: Pass `build.ssr` into this function
dataStrategy: getRSCSingleFetchDataStrategy(
() => globalVar.__reactRouterDataRouter,
true,
payload.basename,
createFromReadableStream,
fetchImplementation
)
});
if (globalVar.__reactRouterDataRouter.state.initialized) {
globalVar.__routerInitialized = true;
globalVar.__reactRouterDataRouter.initialize();
} else {
globalVar.__routerInitialized = false;
}
let lastLoaderData = void 0;
globalVar.__reactRouterDataRouter.subscribe(({ loaderData, actionData }) => {
if (lastLoaderData !== loaderData) {
globalVar.__routerActionID = (globalVar.__routerActionID ?? (globalVar.__routerActionID = 0)) + 1;
}
});
globalVar.__reactRouterDataRouter._updateRoutesForHMR = (routeUpdateByRouteId) => {
const oldRoutes = window.__reactRouterDataRouter.routes;
const newRoutes = [];
function walkRoutes(routes2, parentId) {
return routes2.map((route) => {
const routeUpdate = routeUpdateByRouteId.get(route.id);
if (routeUpdate) {
const {
routeModule,
hasAction,
hasComponent,
hasErrorBoundary,
hasLoader
} = routeUpdate;
const newRoute = createRouteFromServerManifest({
clientAction: routeModule.clientAction,
clientLoader: routeModule.clientLoader,
element: route.element,
errorElement: route.errorElement,
handle: route.handle,
hasAction,
hasComponent,
hasErrorBoundary,
hasLoader,
hydrateFallbackElement: route.hydrateFallbackElement,
id: route.id,
index: route.index,
links: routeModule.links,
meta: routeModule.meta,
parentId,
path: route.path,
shouldRevalidate: routeModule.shouldRevalidate
});
if (route.children) {
newRoute.children = walkRoutes(route.children, route.id);
}
return newRoute;
}
const updatedRoute = { ...route };
if (route.children) {
updatedRoute.children = walkRoutes(route.children, route.id);
}
return updatedRoute;
});
}
newRoutes.push(
...walkRoutes(oldRoutes, void 0)
);
window.__reactRouterDataRouter._internalSetRoutes(newRoutes);
};
return {
router: globalVar.__reactRouterDataRouter,
routeModules: globalVar.__reactRouterRouteModules
};
}
var renderedRoutesContext = createContext();
function getRSCSingleFetchDataStrategy(getRouter, ssr, basename, createFromReadableStream, fetchImplementation) {
let dataStrategy = getSingleFetchDataStrategyImpl(
getRouter,
(match) => {
let M = match;
return {
hasLoader: M.route.hasLoader,
hasClientLoader: M.route.hasClientLoader,
hasComponent: M.route.hasComponent,
hasAction: M.route.hasAction,
hasClientAction: M.route.hasClientAction
};
},
// pass map into fetchAndDecode so it can add payloads
getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation),
ssr,
basename,
// .rsc requests are always trailing slash aware
true,
// If the route has a component but we don't have an element, we need to hit
// the server loader flow regardless of whether the client loader calls
// `serverLoader` or not, otherwise we'll have nothing to render.
(match) => {
let M = match;
return M.route.hasComponent && !M.route.element;
}
);
return async (args) => args.runClientMiddleware(async () => {
let context = args.context;
context.set(renderedRoutesContext, []);
let results = await dataStrategy(args);
const renderedRoutesById = /* @__PURE__ */ new Map();
for (const route of context.get(renderedRoutesContext)) {
if (!renderedRoutesById.has(route.id)) {
renderedRoutesById.set(route.id, []);
}
renderedRoutesById.get(route.id).push(route);
}
React3.startTransition(() => {
for (const match of args.matches) {
const renderedRoutes = renderedRoutesById.get(match.route.id);
if (renderedRoutes) {
for (const rendered of renderedRoutes) {
window.__reactRouterDataRouter.patchRoutes(
rendered.parentId ?? null,
[createRouteFromServerManifest(rendered)],
true
);
}
}
}
});
return results;
});
}
function getFetchAndDecodeViaRSC(createFromReadableStream, fetchImplementation) {
return async (args, basename, trailingSlashAware, targetRoutes) => {
let { request, context } = args;
let url = singleFetchUrl(request.url, basename, trailingSlashAware, "rsc");
if (request.method === "GET") {
url = stripIndexParam(url);
if (targetRoutes) {
url.searchParams.set("_routes", targetRoutes.join(","));
}
}
let res = await fetchImplementation(
new Request(url, await createRequestInit(request))
);
if (res.status >= 400 && !res.headers.has("X-Remix-Response")) {
throw new ErrorResponseImpl(res.status, res.statusText, await res.text());
}
invariant(res.body, "No response body to decode");
try {
const payload = await createFromReadableStream(res.body, {
temporaryReferences: void 0
});
if (payload.type === "redirect") {
return {
status: res.status,
data: {
redirect: {
redirect: payload.location,
reload: payload.reload,
replace: payload.replace,
revalidate: false,
status: payload.status
}
}
};
}
if (payload.type !== "render") {
throw new Error("Unexpected payload type");
}
context.get(renderedRoutesContext).push(...payload.matches);
let results = { routes: {} };
const dataKey = isMutationMethod(request.method) ? "actionData" : "loaderData";
for (let [routeId, data] of Object.entries(payload[dataKey] || {})) {
results.routes[routeId] = { data };
}
if (payload.errors) {
for (let [routeId, error] of Object.entries(payload.errors)) {
results.routes[routeId] = { error };
}
}
return { status: res.status, data: results };
} catch (cause) {
throw new Error("Unable to decode RSC response", { cause });
}
};
}
function RSCHydratedRouter({
createFromReadableStream,
fetch: fetchImplementation = fetch,
payload,
getContext
}) {
if (payload.type !== "render") throw new Error("Invalid payload type");
let { routeDiscovery } = payload;
let { router: router2, routeModules } = React3.useMemo(
() => createRouterFromPayload({
payload,
fetchImplementation,
getContext,
createFromReadableStream
}),
[createFromReadableStream, payload, fetchImplementation, getContext]
);
React3.useEffect(() => {
setIsHydrated();
}, []);
React3.useLayoutEffect(() => {
const globalVar = window;
if (!globalVar.__routerInitialized) {
globalVar.__routerInitialized = true;
globalVar.__reactRouterDataRouter.initialize();
}
}, []);
let [{ routes, state }, setState] = React3.useState(() => ({
routes: cloneRoutes(router2.routes),
state: router2.state
}));
React3.useLayoutEffect(
() => router2.subscribe((newState) => {
if (diffRoutes(router2.routes, routes))
React3.startTransition(() => {
setState({
routes: cloneRoutes(router2.routes),
state: newState
});
});
}),
[router2.subscribe, routes, router2]
);
const transitionEnabledRouter = React3.useMemo(
() => ({
...router2,
state,
routes
}),
[router2, routes, state]
);
React3.useEffect(() => {
if (routeDiscovery.mode === "initial" || // @ts-expect-error - TS doesn't know about this yet
window.navigator?.connection?.saveData === true) {
return;
}
function registerElement(el) {
let path = el.tagName === "FORM" ? el.getAttribute("action") : el.getAttribute("href");
if (!path) {
return;
}
let pathname = el.tagName === "A" ? el.pathname : new URL(path, window.location.origin).pathname;
if (!discoveredPaths.has(pathname)) {
nextPaths.add(pathname);
}
}
async function fetchPatches() {
document.querySelectorAll("a[data-discover], form[data-discover]").forEach(registerElement);
let paths = Array.from(nextPaths.keys()).filter((path) => {
if (discoveredPaths.has(path)) {
nextPaths.delete(path);
return false;
}
return true;
});
if (paths.length === 0) {
return;
}
try {
await fetchAndApplyManifestPatches(
paths,
createFromReadableStream,
fetchImplementation
);
} catch (e) {
console.error("Failed to fetch manifest patches", e);
}
}
let debouncedFetchPatches = debounce(fetchPatches, 100);
fetchPatches();
let observer = new MutationObserver(() => debouncedFetchPatches());
observer.observe(document.documentElement, {
subtree: true,
childList: true,
attributes: true,
attributeFilter: ["data-discover", "href", "action"]
});
}, [routeDiscovery, createFromReadableStream, fetchImplementation]);
const frameworkContext = {
future: {
// These flags have no runtime impact so can always be false. If we add
// flags that drive runtime behavior they'll need to be proxied through.
v8_middleware: false,
v8_trailingSlashAwareDataRequests: true,
// always on for RSC
v8_passThroughRequests: true
// always on for RSC
},
isSpaMode: false,
ssr: true,
criticalCss: "",
manifest: {
routes: {},
version: "1",
url: "",
entry: {
module: "",
imports: []
}
},
routeDiscovery: payload.routeDiscovery.mode === "initial" ? { mode: "initial", manifestPath: defaultManifestPath } : {
mode: "lazy",
manifestPath: payload.routeDiscovery.manifestPath || defaultManifestPath
},
routeModules
};
return /* @__PURE__ */ React3.createElement(RSCRouterContext.Provider, { value: true }, /* @__PURE__ */ React3.createElement(RSCRouterGlobalErrorBoundary, { location: state.location }, /* @__PURE__ */ React3.createElement(FrameworkContext.Provider, { value: frameworkContext }, /* @__PURE__ */ React3.createElement(
RouterProvider,
{
router: transitionEnabledRouter,
flushSync: ReactDOM2.flushSync
}
))));
}
function createRouteFromServerManifest(match, payload) {
let hasInitialData = payload && match.id in payload.loaderData;
let initialData = payload?.loaderData[match.id];
let hasInitialError = payload?.errors && match.id in payload.errors;
let initialError = payload?.errors?.[match.id];
let isHydrationRequest = match.clientLoader?.hydrate === true || !match.hasLoader || // If the route has a component but we don't have an element, we need to hit
// the server loader flow regardless of whether the client loader calls
// `serverLoader` or not, otherwise we'll have nothing to render.
match.hasComponent && !match.element;
invariant(window.__reactRouterRouteModules);
populateRSCRouteModules(window.__reactRouterRouteModules, match);
let dataRoute = {
id: match.id,
element: match.element,
errorElement: match.errorElement,
handle: match.handle,
hasErrorBoundary: match.hasErrorBoundary,
hydrateFallbackElement: match.hydrateFallbackElement,
index: match.index,
loader: match.clientLoader ? async (args, singleFetch) => {
let _isHydrationRequest = isHydrationRequest;
isHydrationRequest = false;
let result = await match.clientLoader({
...args,
serverLoader: () => {
preventInvalidServerHandlerCall(
"loader",
match.id,
match.hasLoader
);
if (_isHydrationRequest) {
if (hasInitialData) {
return initialData;
}
if (hasInitialError) {
throw initialError;
}
}
return callSingleFetch(singleFetch);
}
});
return result;
} : (
// We always make the call in this RSC world since even if we don't
// have a `loader` we may need to get the `element` implementation
(_, singleFetch) => callSingleFetch(singleFetch)
),
action: match.clientAction ? (args, singleFetch) => match.clientAction({
...args,
serverAction: async () => {
preventInvalidServerHandlerCall(
"action",
match.id,
match.hasLoader
);
return await callSingleFetch(singleFetch);
}
}) : match.hasAction ? (_, singleFetch) => callSingleFetch(singleFetch) : () => {
throw noActionDefinedError("action", match.id);
},
path: match.path,
shouldRevalidate: match.shouldRevalidate,
// We always have a "loader" in this RSC world since even if we don't
// have a `loader` we may need to get the `element` implementation
hasLoader: true,
hasClientLoader: match.clientLoader != null,
hasAction: match.hasAction,
hasClientAction: match.clientAction != null
};
if (typeof dataRoute.loader === "function") {
dataRoute.loader.hydrate = shouldHydrateRouteLoader(
match.id,
match.clientLoader,
match.hasLoader,
false
);
}
return dataRoute;
}
function callSingleFetch(singleFetch) {
invariant(typeof singleFetch === "function", "Invalid singleFetch parameter");
return singleFetch();
}
function preventInvalidServerHandlerCall(type, routeId, hasHandler) {
if (!hasHandler) {
let fn = type === "action" ? "serverAction()" : "serverLoader()";
let msg = `You are trying to call ${fn} on a route that does not have a server ${type} (routeId: "${routeId}")`;
console.error(msg);
throw new ErrorResponseImpl(400, "Bad Request", new Error(msg), true);
}
}
var nextPaths = /* @__PURE__ */ new Set();
var discoveredPathsMaxSize = 1e3;
var discoveredPaths = /* @__PURE__ */ new Set();
function getManifestUrl(paths) {
if (paths.length === 0) {
return null;
}
if (paths.length === 1) {
return new URL(`${paths[0]}.manifest`, window.location.origin);
}
const globalVar = window;
let basename = (globalVar.__reactRouterDataRouter.basename ?? "").replace(
/^\/|\/$/g,
""
);
let url = new URL(`${basename}/.manifest`, window.location.origin);
url.searchParams.set("paths", paths.sort().join(","));
return url;
}
async function fetchAndApplyManifestPatches(paths, createFromReadableStream, fetchImplementation, signal) {
paths = getPathsWithAncestors(paths);
let url = getManifestUrl(paths);
if (url == null) {
return;
}
if (url.toString().length > URL_LIMIT) {
nextPaths.clear();
return;
}
let response = await fetchImplementation(new Request(url, { signal }));
if (!response.body || response.status < 200 || response.status >= 300) {
throw new Error("Unable to fetch new route matches from the server");
}
let payload = await createFromReadableStream(response.body, {
temporaryReferences: void 0
});
if (payload.type !== "manifest") {
throw new Error("Failed to patch routes");
}
paths.forEach((p) => addToFifoQueue(p, discoveredPaths));
let patches = await payload.patches;
React3.startTransition(() => {
patches.forEach((p) => {
window.__reactRouterDataRouter.patchRoutes(
p.parentId ?? null,
[createRouteFromServerManifest(p)]
);
});
});
}
function addToFifoQueue(path, queue) {
if (queue.size >= discoveredPathsMaxSize) {
let first = queue.values().next().value;
if (typeof first === "string") queue.delete(first);
}
queue.add(path);
}
function debounce(callback, wait) {
let timeoutId;
return (...args) => {
window.clearTimeout(timeoutId);
timeoutId = window.setTimeout(() => callback(...args), wait);
};
}
function isExternalLocation(location2) {
const newLocation = new URL(location2, window.location.href);
return newLocation.origin !== window.location.origin;
}
function normalizeRedirectLocation(location2) {
if (PROTOCOL_RELATIVE_URL_REGEX.test(location2)) {
let path = resolvePath(location2);
return path.pathname + path.search + path.hash;
}
return location2;
}
function cloneRoutes(routes) {
if (!routes) return void 0;
return routes.map((route) => ({
...route,
children: cloneRoutes(route.children)
}));
}
function diffRoutes(a, b) {
if (a.length !== b.length) return true;
return a.some((route, index) => {
if (route.element !== b[index].element) return true;
if (route.errorElement !== b[index].errorElement)
return true;
if (route.hydrateFallbackElement !== b[index].hydrateFallbackElement)
return true;
if (route.hasErrorBoundary !== b[index].hasErrorBoundary)
return true;
if (route.hasLoader !== b[index].hasLoader) return true;
if (route.hasClientLoader !== b[index].hasClientLoader)
return true;
if (route.hasAction !== b[index].hasAction) return true;
if (route.hasClientAction !== b[index].hasClientAction)
return true;
return diffRoutes(route.children || [], b[index].children || []);
});
}
// lib/rsc/html-stream/browser.ts
function getRSCStream() {
let encoder = new TextEncoder();
let streamController = null;
let rscStream = new ReadableStream({
start(controller) {
if (typeof window === "undefined") {
return;
}
let handleChunk = (chunk) => {
if (typeof chunk === "string") {
controller.enqueue(encoder.encode(chunk));
} else {
controller.enqueue(chunk);
}
};
window.__FLIGHT_DATA || (window.__FLIGHT_DATA = []);
window.__FLIGHT_DATA.forEach(handleChunk);
window.__FLIGHT_DATA.push = (chunk) => {
handleChunk(chunk);
return 0;
};
streamController = controller;
}
});
if (typeof document !== "undefined" && document.readyState === "loading") {
document.addEventListener("DOMContentLoaded", () => {
streamController?.close();
});
} else {
streamController?.close();
}
return rscStream;
}
export {
HydratedRouter,
RouterProvider2 as RouterProvider,
RSCHydratedRouter as unstable_RSCHydratedRouter,
createCallServer as unstable_createCallServer,
getRSCStream as unstable_getRSCStream
};
@@ -0,0 +1,3677 @@
import { l as ServerInstrumentation, H as HydrationState, e as StaticHandlerContext, c as RelativeRoutingType, a as Router$1, g as RouterState, R as RouterInit, t as FutureConfig$1, C as ClientInstrumentation, d as GetScrollRestorationKeyFunction, F as Fetcher, B as BlockerFunction, u as CreateStaticHandlerOptions$1, S as StaticHandler } from './instrumentation-Dkmpzd13.js';
import * as React from 'react';
import { p as RouteManifest, aE as ServerRouteModule, t as MiddlewareEnabled, c as RouterContextProvider, u as AppLoadContext, s as LoaderFunctionArgs, a0 as ActionFunctionArgs, O as RouteModules, n as DataRouteObject, a as ClientLoaderFunction, o as RouteBranch, aF as TrackedPromise, f as History, T as To, L as Location, i as Action, ac as RouteMatch, Y as InitialEntry, _ as NonIndexRouteObject, a7 as LazyRouteFunction, Z as IndexRouteObject, P as Params, l as DataStrategyFunction, m as PatchRoutesOnNavigationFunction, e as RouteObject, U as UIMatch, q as HTMLFormMethod, F as FormEncType, av as PageLinkDescriptor, Q as SerializeFrom } from './data-CjO11-hU.js';
type ServerRouteManifest = RouteManifest<Omit<ServerRoute, "children">>;
interface ServerRoute extends Route$1 {
children: ServerRoute[];
module: ServerRouteModule;
}
type OptionalCriticalCss = CriticalCss | undefined;
/**
* The output of the compiler for the server build.
*/
interface ServerBuild {
entry: {
module: ServerEntryModule;
};
routes: ServerRouteManifest;
assets: AssetsManifest;
basename?: string;
publicPath: string;
assetsBuildDirectory: string;
future: FutureConfig;
ssr: boolean;
unstable_getCriticalCss?: (args: {
pathname: string;
}) => OptionalCriticalCss | Promise<OptionalCriticalCss>;
/**
* @deprecated This is now done via a custom header during prerendering
*/
isSpaMode: boolean;
prerender: string[];
routeDiscovery: {
mode: "lazy" | "initial";
manifestPath: string;
};
allowedActionOrigins?: string[] | false;
}
interface HandleDocumentRequestFunction {
(request: Request, responseStatusCode: number, responseHeaders: Headers, context: EntryContext, loadContext: MiddlewareEnabled extends true ? RouterContextProvider : AppLoadContext): Promise<Response> | Response;
}
interface HandleDataRequestFunction {
(response: Response, args: {
request: LoaderFunctionArgs["request"] | ActionFunctionArgs["request"];
context: LoaderFunctionArgs["context"] | ActionFunctionArgs["context"];
params: LoaderFunctionArgs["params"] | ActionFunctionArgs["params"];
}): Promise<Response> | Response;
}
interface HandleErrorFunction {
(error: unknown, args: {
request: LoaderFunctionArgs["request"] | ActionFunctionArgs["request"];
context: LoaderFunctionArgs["context"] | ActionFunctionArgs["context"];
params: LoaderFunctionArgs["params"] | ActionFunctionArgs["params"];
}): void;
}
/**
* A module that serves as the entry point for a Remix app during server
* rendering.
*/
interface ServerEntryModule {
default: HandleDocumentRequestFunction;
handleDataRequest?: HandleDataRequestFunction;
handleError?: HandleErrorFunction;
instrumentations?: ServerInstrumentation[];
streamTimeout?: number;
}
interface Route$1 {
index?: boolean;
caseSensitive?: boolean;
id: string;
parentId?: string;
path?: string;
}
interface EntryRoute extends Route$1 {
hasAction: boolean;
hasLoader: boolean;
hasClientAction: boolean;
hasClientLoader: boolean;
hasClientMiddleware: boolean;
hasErrorBoundary: boolean;
imports?: string[];
css?: string[];
module: string;
clientActionModule: string | undefined;
clientLoaderModule: string | undefined;
clientMiddlewareModule: string | undefined;
hydrateFallbackModule: string | undefined;
parentId?: string;
}
declare function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation: Set<string>, manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState, ssr: boolean, isSpaMode: boolean): DataRouteObject[];
declare function createClientRoutes(manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState | null, ssr: boolean, isSpaMode: boolean, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>, needsRevalidation?: Set<string>): DataRouteObject[];
declare function shouldHydrateRouteLoader(routeId: string, clientLoader: ClientLoaderFunction | undefined, hasLoader: boolean, isSpaMode: boolean): boolean;
type SerializedError = {
message: string;
stack?: string;
};
interface FrameworkContextObject {
manifest: AssetsManifest;
routeModules: RouteModules;
criticalCss?: CriticalCss;
serverHandoffString?: string;
future: FutureConfig;
ssr: boolean;
isSpaMode: boolean;
routeDiscovery: ServerBuild["routeDiscovery"];
nonce?: string;
serializeError?(error: Error): SerializedError;
renderMeta?: {
didRenderScripts?: boolean;
streamCache?: Record<number, Promise<void> & {
result?: {
done: boolean;
value: string;
};
error?: unknown;
}>;
};
}
interface EntryContext extends FrameworkContextObject {
branches: RouteBranch<DataRouteObject>[];
staticHandlerContext: StaticHandlerContext;
serverHandoffStream?: ReadableStream<Uint8Array>;
}
interface FutureConfig {
v8_passThroughRequests: boolean;
v8_trailingSlashAwareDataRequests: boolean;
v8_middleware: boolean;
}
type CriticalCss = string | {
rel: "stylesheet";
href: string;
};
interface AssetsManifest {
entry: {
imports: string[];
module: string;
};
routes: RouteManifest<EntryRoute>;
url: string;
version: string;
hmr?: {
timestamp?: number;
runtime: string;
};
sri?: Record<string, string> | true;
}
interface DataRouterContextObject extends Omit<NavigationContextObject, "future" | "useTransitions"> {
router: Router$1;
staticContext?: StaticHandlerContext;
onError?: ClientOnErrorFunction;
}
declare const DataRouterContext: React.Context<DataRouterContextObject | null>;
declare const DataRouterStateContext: React.Context<RouterState | null>;
type ViewTransitionContextObject = {
isTransitioning: false;
} | {
isTransitioning: true;
flushSync: boolean;
currentLocation: Location;
nextLocation: Location;
};
declare const ViewTransitionContext: React.Context<ViewTransitionContextObject>;
type FetchersContextObject = Map<string, any>;
declare const FetchersContext: React.Context<FetchersContextObject>;
declare const AwaitContext: React.Context<TrackedPromise | null>;
declare const AwaitContextProvider: (props: React.ComponentProps<typeof AwaitContext.Provider>) => React.FunctionComponentElement<React.ProviderProps<TrackedPromise | null>>;
interface NavigateOptions {
/** Replace the current entry in the history stack instead of pushing a new one */
replace?: boolean;
/** Masked URL */
mask?: To;
/** Adds persistent client side routing state to the next location */
state?: any;
/** If you are using {@link ScrollRestoration `<ScrollRestoration>`}, prevent the scroll position from being reset to the top of the window when navigating */
preventScrollReset?: boolean;
/** Defines the relative path behavior for the link. "route" will use the route hierarchy so ".." will remove all URL segments of the current route pattern while "path" will use the URL path so ".." will remove one URL segment. */
relative?: RelativeRoutingType;
/** Wraps the initial state update for this navigation in a {@link https://react.dev/reference/react-dom/flushSync ReactDOM.flushSync} call instead of the default {@link https://react.dev/reference/react/startTransition React.startTransition} */
flushSync?: boolean;
/** Enables a {@link https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API View Transition} for this navigation by wrapping the final state update in `document.startViewTransition()`. If you need to apply specific styles for this view transition, you will also need to leverage the {@link useViewTransitionState `useViewTransitionState()`} hook. */
viewTransition?: boolean;
/** Specifies the default revalidation behavior after this submission */
defaultShouldRevalidate?: boolean;
}
/**
* A Navigator is a "location changer"; it's how you get to different locations.
*
* Every history instance conforms to the Navigator interface, but the
* distinction is useful primarily when it comes to the low-level `<Router>` API
* where both the location and a navigator must be provided separately in order
* to avoid "tearing" that may occur in a suspense-enabled app if the action
* and/or location were to be read directly from the history instance.
*/
interface Navigator {
createHref: History["createHref"];
encodeLocation?: History["encodeLocation"];
go: History["go"];
push(to: To, state?: any, opts?: NavigateOptions): void;
replace(to: To, state?: any, opts?: NavigateOptions): void;
}
interface NavigationContextObject {
basename: string;
navigator: Navigator;
static: boolean;
useTransitions: boolean | undefined;
future: {};
}
declare const NavigationContext: React.Context<NavigationContextObject>;
interface LocationContextObject {
location: Location;
navigationType: Action;
}
declare const LocationContext: React.Context<LocationContextObject>;
interface RouteContextObject {
outlet: React.ReactElement | null;
matches: RouteMatch[];
isDataRoute: boolean;
}
declare const RouteContext: React.Context<RouteContextObject>;
declare function mapRouteProperties(route: RouteObject): Partial<RouteObject> & {
hasErrorBoundary: boolean;
};
declare const hydrationRouteProperties: (keyof RouteObject)[];
/**
* @category Data Routers
*/
interface MemoryRouterOpts {
/**
* Basename path for the application.
*/
basename?: string;
/**
* A function that returns an {@link RouterContextProvider} instance
* which is provided as the `context` argument to client [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* This function is called to generate a fresh `context` instance on each
* navigation or fetcher call.
*/
getContext?: RouterInit["getContext"];
/**
* Future flags to enable for the router.
*/
future?: Partial<FutureConfig$1>;
/**
* Hydration data to initialize the router with if you have already performed
* data loading on the server.
*/
hydrationData?: HydrationState;
/**
* Initial entries in the in-memory history stack
*/
initialEntries?: InitialEntry[];
/**
* Index of `initialEntries` the application should initialize to
*/
initialIndex?: number;
/**
* Array of instrumentation objects allowing you to instrument the router and
* individual routes prior to router initialization (and on any subsequently
* added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
* mostly useful for observability such as wrapping navigations, fetches,
* as well as route loaders/actions/middlewares with logging and/or performance
* tracing. See the [docs](../../how-to/instrumentation) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* instrumentations: [logging]
* });
*
*
* let logging = {
* router({ instrument }) {
* instrument({
* navigate: (impl, info) => logExecution(`navigate ${info.to}`, impl),
* fetch: (impl, info) => logExecution(`fetch ${info.to}`, impl)
* });
* },
* route({ instrument, id }) {
* instrument({
* middleware: (impl, info) => logExecution(
* `middleware ${info.request.url} (route ${id})`,
* impl
* ),
* loader: (impl, info) => logExecution(
* `loader ${info.request.url} (route ${id})`,
* impl
* ),
* action: (impl, info) => logExecution(
* `action ${info.request.url} (route ${id})`,
* impl
* ),
* })
* }
* };
*
* async function logExecution(label: string, impl: () => Promise<void>) {
* let start = performance.now();
* console.log(`start ${label}`);
* await impl();
* let duration = Math.round(performance.now() - start);
* console.log(`end ${label} (${duration}ms)`);
* }
* ```
*/
instrumentations?: ClientInstrumentation[];
/**
* Override the default data strategy of running loaders in parallel -
* see the [docs](../../how-to/data-strategy) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* async dataStrategy({
* matches,
* request,
* runClientMiddleware,
* }) {
* const matchesToLoad = matches.filter((m) =>
* m.shouldCallHandler(),
* );
*
* const results: Record<string, DataStrategyResult> = {};
* await runClientMiddleware(() =>
* Promise.all(
* matchesToLoad.map(async (match) => {
* results[match.route.id] = await match.resolve();
* }),
* ),
* );
* return results;
* },
* });
* ```
*/
dataStrategy?: DataStrategyFunction;
/**
* Lazily define portions of the route tree on navigations.
*/
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
}
/**
* Create a new {@link DataRouter} that manages the application path using an
* in-memory [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack. Useful for non-browser environments without a DOM API.
*
* Data Routers should not be held in React state. You should create your router
* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
*
* @public
* @category Data Routers
* @mode data
* @param routes Application routes
* @param opts Options
* @param {MemoryRouterOpts.basename} opts.basename n/a
* @param {MemoryRouterOpts.dataStrategy} opts.dataStrategy n/a
* @param {MemoryRouterOpts.future} opts.future n/a
* @param {MemoryRouterOpts.getContext} opts.getContext n/a
* @param {MemoryRouterOpts.hydrationData} opts.hydrationData n/a
* @param {MemoryRouterOpts.initialEntries} opts.initialEntries n/a
* @param {MemoryRouterOpts.initialIndex} opts.initialIndex n/a
* @param {MemoryRouterOpts.instrumentations} opts.instrumentations n/a
* @param {MemoryRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a
* @returns An initialized {@link DataRouter} to pass to {@link RouterProvider | `<RouterProvider>`}
*/
declare function createMemoryRouter(routes: RouteObject[], opts?: MemoryRouterOpts): Router$1;
/**
* Function signature for client side error handling for loader/actions errors
* and rendering errors via `componentDidCatch`
*/
interface ClientOnErrorFunction {
(error: unknown, info: {
location: Location;
params: Params;
pattern: string;
errorInfo?: React.ErrorInfo;
}): void;
}
/**
* @category Types
*/
interface RouterProviderProps {
/**
* The {@link DataRouter} instance to use for navigation and data fetching. The
* router prop should be a single router instance created outside of the React
* tree. Avoid creating new routers during React renders/re-renders.
*/
router: Router$1;
/**
* The [`ReactDOM.flushSync`](https://react.dev/reference/react-dom/flushSync)
* implementation to use for flushing updates.
*
* You usually don't have to worry about this:
* - The `RouterProvider` exported from `react-router/dom` handles this internally for you
* - If you are rendering in a non-DOM environment, you can import
* `RouterProvider` from `react-router` and ignore this prop
*/
flushSync?: (fn: () => unknown) => undefined;
/**
* An error handler function that will be called for any middleware, loader, action,
* or render errors that are encountered in your application. This is useful for
* logging or reporting errors instead of in the {@link ErrorBoundary} because it's not
* subject to re-rendering and will only run one time per error.
*
* The `errorInfo` parameter is passed along from
* [`componentDidCatch`](https://react.dev/reference/react/Component#componentdidcatch)
* and is only present for render errors.
*
* ```tsx
* <RouterProvider onError={(error, info) => {
* let { location, params, pattern, errorInfo } = info;
* console.error(error, location, errorInfo);
* reportToErrorService(error, location, errorInfo);
* }} />
* ```
*/
onError?: ClientOnErrorFunction;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all state updates are wrapped in
* `React.startTransition`
* - This can lead to buggy behaviors if you are wrapping your own
* navigations/fetchers in `startTransition`.
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and router state changes will be wrapped in
* `React.startTransition` and also sent through
* [`useOptimistic`](https://react.dev/reference/react/useOptimistic) to
* surface mid-navigation router state changes to the UI.
* - When set to `false`, the router will not leverage `React.startTransition` or
* `React.useOptimistic` on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* Render the UI for the given {@link DataRouter}. This component should
* typically be at the top of an app's element tree. The router prop should
* be a single router instance created outside of the React tree. Avoid
* creating new routers during React renders/re-renders.
*
* ```tsx
* import { createBrowserRouter } from "react-router";
* import { RouterProvider } from "react-router/dom";
* import { createRoot } from "react-dom/client";
*
* const router = createBrowserRouter(routes);
* createRoot(document.getElementById("root")).render(
* <RouterProvider router={router} />
* );
* ```
*
* <docs-info>Please note that this component is exported both from
* `react-router` and `react-router/dom` with the only difference being that the
* latter automatically wires up `react-dom`'s [`flushSync`](https://react.dev/reference/react-dom/flushSync)
* implementation. You _almost always_ want to use the version from
* `react-router/dom` unless you're running in a non-DOM environment.</docs-info>
*
*
* @public
* @category Data Routers
* @mode data
* @param props Props
* @param {RouterProviderProps.flushSync} props.flushSync n/a
* @param {RouterProviderProps.onError} props.onError n/a
* @param {RouterProviderProps.router} props.router n/a
* @param {RouterProviderProps.useTransitions} props.useTransitions n/a
* @returns React element for the rendered router
*/
declare function RouterProvider({ router, flushSync: reactDomFlushSyncImpl, onError, useTransitions, }: RouterProviderProps): React.ReactElement;
/**
* @category Types
*/
interface MemoryRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* Nested {@link Route} elements describing the route tree
*/
children?: React.ReactNode;
/**
* Initial entries in the in-memory history stack
*/
initialEntries?: InitialEntry[];
/**
* Index of `initialEntries` the application should initialize to
*/
initialIndex?: number;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* A declarative {@link Router | `<Router>`} that stores all entries in memory.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {MemoryRouterProps.basename} props.basename n/a
* @param {MemoryRouterProps.children} props.children n/a
* @param {MemoryRouterProps.initialEntries} props.initialEntries n/a
* @param {MemoryRouterProps.initialIndex} props.initialIndex n/a
* @param {MemoryRouterProps.useTransitions} props.useTransitions n/a
* @returns A declarative in-memory {@link Router | `<Router>`} for client-side
* routing.
*/
declare function MemoryRouter({ basename, children, initialEntries, initialIndex, useTransitions, }: MemoryRouterProps): React.ReactElement;
/**
* @category Types
*/
interface NavigateProps {
/**
* The path to navigate to. This can be a string or a {@link Path} object
*/
to: To;
/**
* Whether to replace the current entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack
*/
replace?: boolean;
/**
* State to pass to the new {@link Location} to store in [`history.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state).
*/
state?: any;
/**
* How to interpret relative routing in the `to` prop.
* See {@link RelativeRoutingType}.
*/
relative?: RelativeRoutingType;
}
/**
* A component-based version of {@link useNavigate} to use in a
* [`React.Component` class](https://react.dev/reference/react/Component) where
* hooks cannot be used.
*
* It's recommended to avoid using this component in favor of {@link useNavigate}.
*
* @example
* <Navigate to="/tasks" />
*
* @public
* @category Components
* @param props Props
* @param {NavigateProps.relative} props.relative n/a
* @param {NavigateProps.replace} props.replace n/a
* @param {NavigateProps.state} props.state n/a
* @param {NavigateProps.to} props.to n/a
* @returns {void}
*
*/
declare function Navigate({ to, replace, state, relative, }: NavigateProps): null;
/**
* @category Types
*/
interface OutletProps {
/**
* Provides a context value to the element tree below the outlet. Use when
* the parent route needs to provide values to child routes.
*
* ```tsx
* <Outlet context={myContextValue} />
* ```
*
* Access the context with {@link useOutletContext}.
*/
context?: unknown;
}
/**
* Renders the matching child route of a parent route or nothing if no child
* route matches.
*
* @example
* import { Outlet } from "react-router";
*
* export default function SomeParent() {
* return (
* <div>
* <h1>Parent Content</h1>
* <Outlet />
* </div>
* );
* }
*
* @public
* @category Components
* @param props Props
* @param {OutletProps.context} props.context n/a
* @returns React element for the rendered outlet or `null` if no child route matches.
*/
declare function Outlet(props: OutletProps): React.ReactElement | null;
/**
* @category Types
*/
interface PathRouteProps {
/**
* Whether the path should be case-sensitive. Defaults to `false`.
*/
caseSensitive?: NonIndexRouteObject["caseSensitive"];
/**
* The path pattern to match. If unspecified or empty, then this becomes a
* layout route.
*/
path?: NonIndexRouteObject["path"];
/**
* The unique identifier for this route (for use with {@link DataRouter}s)
*/
id?: NonIndexRouteObject["id"];
/**
* A function that returns a promise that resolves to the route object.
* Used for code-splitting routes.
* See [`lazy`](../../start/data/route-object#lazy).
*/
lazy?: LazyRouteFunction<NonIndexRouteObject>;
/**
* The route middleware.
* See [`middleware`](../../start/data/route-object#middleware).
*/
middleware?: NonIndexRouteObject["middleware"];
/**
* The route loader.
* See [`loader`](../../start/data/route-object#loader).
*/
loader?: NonIndexRouteObject["loader"];
/**
* The route action.
* See [`action`](../../start/data/route-object#action).
*/
action?: NonIndexRouteObject["action"];
hasErrorBoundary?: NonIndexRouteObject["hasErrorBoundary"];
/**
* The route shouldRevalidate function.
* See [`shouldRevalidate`](../../start/data/route-object#shouldRevalidate).
*/
shouldRevalidate?: NonIndexRouteObject["shouldRevalidate"];
/**
* The route handle.
*/
handle?: NonIndexRouteObject["handle"];
/**
* Whether this is an index route.
*/
index?: false;
/**
* Child Route components
*/
children?: React.ReactNode;
/**
* The React element to render when this Route matches.
* Mutually exclusive with `Component`.
*/
element?: React.ReactNode | null;
/**
* The React element to render while this router is loading data.
* Mutually exclusive with `HydrateFallback`.
*/
hydrateFallbackElement?: React.ReactNode | null;
/**
* The React element to render at this route if an error occurs.
* Mutually exclusive with `ErrorBoundary`.
*/
errorElement?: React.ReactNode | null;
/**
* The React Component to render when this route matches.
* Mutually exclusive with `element`.
*/
Component?: React.ComponentType | null;
/**
* The React Component to render while this router is loading data.
* Mutually exclusive with `hydrateFallbackElement`.
*/
HydrateFallback?: React.ComponentType | null;
/**
* The React Component to render at this route if an error occurs.
* Mutually exclusive with `errorElement`.
*/
ErrorBoundary?: React.ComponentType | null;
}
/**
* @category Types
*/
interface LayoutRouteProps extends PathRouteProps {
}
/**
* @category Types
*/
interface IndexRouteProps {
/**
* Whether the path should be case-sensitive. Defaults to `false`.
*/
caseSensitive?: IndexRouteObject["caseSensitive"];
/**
* The path pattern to match. If unspecified or empty, then this becomes a
* layout route.
*/
path?: IndexRouteObject["path"];
/**
* The unique identifier for this route (for use with {@link DataRouter}s)
*/
id?: IndexRouteObject["id"];
/**
* A function that returns a promise that resolves to the route object.
* Used for code-splitting routes.
* See [`lazy`](../../start/data/route-object#lazy).
*/
lazy?: LazyRouteFunction<IndexRouteObject>;
/**
* The route middleware.
* See [`middleware`](../../start/data/route-object#middleware).
*/
middleware?: IndexRouteObject["middleware"];
/**
* The route loader.
* See [`loader`](../../start/data/route-object#loader).
*/
loader?: IndexRouteObject["loader"];
/**
* The route action.
* See [`action`](../../start/data/route-object#action).
*/
action?: IndexRouteObject["action"];
hasErrorBoundary?: IndexRouteObject["hasErrorBoundary"];
/**
* The route shouldRevalidate function.
* See [`shouldRevalidate`](../../start/data/route-object#shouldRevalidate).
*/
shouldRevalidate?: IndexRouteObject["shouldRevalidate"];
/**
* The route handle.
*/
handle?: IndexRouteObject["handle"];
/**
* Whether this is an index route.
*/
index: true;
/**
* Child Route components
*/
children?: undefined;
/**
* The React element to render when this Route matches.
* Mutually exclusive with `Component`.
*/
element?: React.ReactNode | null;
/**
* The React element to render while this router is loading data.
* Mutually exclusive with `HydrateFallback`.
*/
hydrateFallbackElement?: React.ReactNode | null;
/**
* The React element to render at this route if an error occurs.
* Mutually exclusive with `ErrorBoundary`.
*/
errorElement?: React.ReactNode | null;
/**
* The React Component to render when this route matches.
* Mutually exclusive with `element`.
*/
Component?: React.ComponentType | null;
/**
* The React Component to render while this router is loading data.
* Mutually exclusive with `hydrateFallbackElement`.
*/
HydrateFallback?: React.ComponentType | null;
/**
* The React Component to render at this route if an error occurs.
* Mutually exclusive with `errorElement`.
*/
ErrorBoundary?: React.ComponentType | null;
}
/**
* @category Types
*/
type RouteProps = PathRouteProps | LayoutRouteProps | IndexRouteProps;
/**
* Configures an element to render when a pattern matches the current location.
* It must be rendered within a {@link Routes} element. Note that these routes
* do not participate in data loading, actions, code splitting, or any other
* route module features.
*
* @example
* // Usually used in a declarative router
* function App() {
* return (
* <BrowserRouter>
* <Routes>
* <Route index element={<StepOne />} />
* <Route path="step-2" element={<StepTwo />} />
* <Route path="step-3" element={<StepThree />} />
* </Routes>
* </BrowserRouter>
* );
* }
*
* // But can be used with a data router as well if you prefer the JSX notation
* const routes = createRoutesFromElements(
* <>
* <Route index loader={step1Loader} Component={StepOne} />
* <Route path="step-2" loader={step2Loader} Component={StepTwo} />
* <Route path="step-3" loader={step3Loader} Component={StepThree} />
* </>
* );
*
* const router = createBrowserRouter(routes);
*
* function App() {
* return <RouterProvider router={router} />;
* }
*
* @public
* @category Components
* @param props Props
* @param {PathRouteProps.action} props.action n/a
* @param {PathRouteProps.caseSensitive} props.caseSensitive n/a
* @param {PathRouteProps.Component} props.Component n/a
* @param {PathRouteProps.children} props.children n/a
* @param {PathRouteProps.element} props.element n/a
* @param {PathRouteProps.ErrorBoundary} props.ErrorBoundary n/a
* @param {PathRouteProps.errorElement} props.errorElement n/a
* @param {PathRouteProps.handle} props.handle n/a
* @param {PathRouteProps.HydrateFallback} props.HydrateFallback n/a
* @param {PathRouteProps.hydrateFallbackElement} props.hydrateFallbackElement n/a
* @param {PathRouteProps.id} props.id n/a
* @param {PathRouteProps.index} props.index n/a
* @param {PathRouteProps.lazy} props.lazy n/a
* @param {PathRouteProps.loader} props.loader n/a
* @param {PathRouteProps.path} props.path n/a
* @param {PathRouteProps.shouldRevalidate} props.shouldRevalidate n/a
* @returns {void}
*/
declare function Route(props: RouteProps): React.ReactElement | null;
/**
* @category Types
*/
interface RouterProps {
/**
* The base path for the application. This is prepended to all locations
*/
basename?: string;
/**
* Nested {@link Route} elements describing the route tree
*/
children?: React.ReactNode;
/**
* The location to match against. Defaults to the current location.
* This can be a string or a {@link Location} object.
*/
location: Partial<Location> | string;
/**
* The type of navigation that triggered this `location` change.
* Defaults to {@link NavigationType.Pop}.
*/
navigationType?: Action;
/**
* The navigator to use for navigation. This is usually a history object
* or a custom navigator that implements the {@link Navigator} interface.
*/
navigator: Navigator;
/**
* Whether this router is static or not (used for SSR). If `true`, the router
* will not be reactive to location changes.
*/
static?: boolean;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* Provides location context for the rest of the app.
*
* Note: You usually won't render a `<Router>` directly. Instead, you'll render a
* router that is more specific to your environment such as a {@link BrowserRouter}
* in web browsers or a {@link ServerRouter} for server rendering.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {RouterProps.basename} props.basename n/a
* @param {RouterProps.children} props.children n/a
* @param {RouterProps.location} props.location n/a
* @param {RouterProps.navigationType} props.navigationType n/a
* @param {RouterProps.navigator} props.navigator n/a
* @param {RouterProps.static} props.static n/a
* @param {RouterProps.useTransitions} props.useTransitions n/a
* @returns React element for the rendered router or `null` if the location does
* not match the {@link props.basename}
*/
declare function Router({ basename: basenameProp, children, location: locationProp, navigationType, navigator, static: staticProp, useTransitions, }: RouterProps): React.ReactElement | null;
/**
* @category Types
*/
interface RoutesProps {
/**
* Nested {@link Route} elements
*/
children?: React.ReactNode;
/**
* The {@link Location} to match against. Defaults to the current location.
*/
location?: Partial<Location> | string;
}
/**
* Renders a branch of {@link Route | `<Route>`s} that best matches the current
* location. Note that these routes do not participate in [data loading](../../start/framework/route-module#loader),
* [`action`](../../start/framework/route-module#action), code splitting, or
* any other [route module](../../start/framework/route-module) features.
*
* @example
* import { Route, Routes } from "react-router";
*
* <Routes>
* <Route index element={<StepOne />} />
* <Route path="step-2" element={<StepTwo />} />
* <Route path="step-3" element={<StepThree />} />
* </Routes>
*
* @public
* @category Components
* @param props Props
* @param {RoutesProps.children} props.children n/a
* @param {RoutesProps.location} props.location n/a
* @returns React element for the rendered routes or `null` if no route matches
*/
declare function Routes({ children, location, }: RoutesProps): React.ReactElement | null;
interface AwaitResolveRenderFunction<Resolve = any> {
(data: Awaited<Resolve>): React.ReactNode;
}
/**
* @category Types
*/
interface AwaitProps<Resolve> {
/**
* When using a function, the resolved value is provided as the parameter.
*
* ```tsx [2]
* <Await resolve={reviewsPromise}>
* {(resolvedReviews) => <Reviews items={resolvedReviews} />}
* </Await>
* ```
*
* When using React elements, {@link useAsyncValue} will provide the
* resolved value:
*
* ```tsx [2]
* <Await resolve={reviewsPromise}>
* <Reviews />
* </Await>
*
* function Reviews() {
* const resolvedReviews = useAsyncValue();
* return <div>...</div>;
* }
* ```
*/
children: React.ReactNode | AwaitResolveRenderFunction<Resolve>;
/**
* The error element renders instead of the `children` when the [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* rejects.
*
* ```tsx
* <Await
* errorElement={<div>Oops</div>}
* resolve={reviewsPromise}
* >
* <Reviews />
* </Await>
* ```
*
* To provide a more contextual error, you can use the {@link useAsyncError} in a
* child component
*
* ```tsx
* <Await
* errorElement={<ReviewsError />}
* resolve={reviewsPromise}
* >
* <Reviews />
* </Await>
*
* function ReviewsError() {
* const error = useAsyncError();
* return <div>Error loading reviews: {error.message}</div>;
* }
* ```
*
* If you do not provide an `errorElement`, the rejected value will bubble up
* to the nearest route-level [`ErrorBoundary`](../../start/framework/route-module#errorboundary)
* and be accessible via the {@link useRouteError} hook.
*/
errorElement?: React.ReactNode;
/**
* Takes a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* returned from a [`loader`](../../start/framework/route-module#loader) to be
* resolved and rendered.
*
* ```tsx
* import { Await, useLoaderData } from "react-router";
*
* export async function loader() {
* let reviews = getReviews(); // not awaited
* let book = await getBook();
* return {
* book,
* reviews, // this is a promise
* };
* }
*
* export default function Book() {
* const {
* book,
* reviews, // this is the same promise
* } = useLoaderData();
*
* return (
* <div>
* <h1>{book.title}</h1>
* <p>{book.description}</p>
* <React.Suspense fallback={<ReviewsSkeleton />}>
* <Await
* // and is the promise we pass to Await
* resolve={reviews}
* >
* <Reviews />
* </Await>
* </React.Suspense>
* </div>
* );
* }
* ```
*/
resolve: Resolve;
}
/**
* Used to render promise values with automatic error handling.
*
* **Note:** `<Await>` expects to be rendered inside a [`<React.Suspense>`](https://react.dev/reference/react/Suspense)
*
* @example
* import { Await, useLoaderData } from "react-router";
*
* export async function loader() {
* // not awaited
* const reviews = getReviews();
* // awaited (blocks the transition)
* const book = await fetch("/api/book").then((res) => res.json());
* return { book, reviews };
* }
*
* function Book() {
* const { book, reviews } = useLoaderData();
* return (
* <div>
* <h1>{book.title}</h1>
* <p>{book.description}</p>
* <React.Suspense fallback={<ReviewsSkeleton />}>
* <Await
* resolve={reviews}
* errorElement={
* <div>Could not load reviews 😬</div>
* }
* children={(resolvedReviews) => (
* <Reviews items={resolvedReviews} />
* )}
* />
* </React.Suspense>
* </div>
* );
* }
*
* @public
* @category Components
* @mode framework
* @mode data
* @param props Props
* @param {AwaitProps.children} props.children n/a
* @param {AwaitProps.errorElement} props.errorElement n/a
* @param {AwaitProps.resolve} props.resolve n/a
* @returns React element for the rendered awaited value
*/
declare function Await<Resolve>({ children, errorElement, resolve, }: AwaitProps<Resolve>): React.JSX.Element;
/**
* Creates a route config from a React "children" object, which is usually
* either a `<Route>` element or an array of them. Used internally by
* `<Routes>` to create a route config from its children.
*
* @category Utils
* @mode data
* @param children The React children to convert into a route config
* @param parentPath The path of the parent route, used to generate unique IDs.
* @returns An array of {@link RouteObject}s that can be used with a {@link DataRouter}
*/
declare function createRoutesFromChildren(children: React.ReactNode, parentPath?: number[]): RouteObject[];
/**
* Create route objects from JSX elements instead of arrays of objects.
*
* @example
* const routes = createRoutesFromElements(
* <>
* <Route index loader={step1Loader} Component={StepOne} />
* <Route path="step-2" loader={step2Loader} Component={StepTwo} />
* <Route path="step-3" loader={step3Loader} Component={StepThree} />
* </>
* );
*
* const router = createBrowserRouter(routes);
*
* function App() {
* return <RouterProvider router={router} />;
* }
*
* @name createRoutesFromElements
* @public
* @category Utils
* @mode data
* @param children The React children to convert into a route config
* @param parentPath The path of the parent route, used to generate unique IDs.
* This is used for internal recursion and is not intended to be used by the
* application developer.
* @returns An array of {@link RouteObject}s that can be used with a {@link DataRouter}
*/
declare const createRoutesFromElements: typeof createRoutesFromChildren;
/**
* Renders the result of {@link matchRoutes} into a React element.
*
* @public
* @category Utils
* @param matches The array of {@link RouteMatch | route matches} to render
* @returns A React element that renders the matched routes or `null` if no matches
*/
declare function renderMatches(matches: RouteMatch[] | null): React.ReactElement | null;
declare function useRouteComponentProps(): {
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
matches: UIMatch<unknown, unknown>[];
};
type RouteComponentProps = ReturnType<typeof useRouteComponentProps>;
type RouteComponentType = React.ComponentType<RouteComponentProps>;
declare function WithComponentProps({ children, }: {
children: React.ReactElement;
}): React.ReactElement<any, string | React.JSXElementConstructor<any>>;
declare function withComponentProps(Component: RouteComponentType): () => React.ReactElement<{
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
matches: UIMatch<unknown, unknown>[];
}, string | React.JSXElementConstructor<any>>;
declare function useHydrateFallbackProps(): {
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
};
type HydrateFallbackProps = ReturnType<typeof useHydrateFallbackProps>;
type HydrateFallbackType = React.ComponentType<HydrateFallbackProps>;
declare function WithHydrateFallbackProps({ children, }: {
children: React.ReactElement;
}): React.ReactElement<any, string | React.JSXElementConstructor<any>>;
declare function withHydrateFallbackProps(HydrateFallback: HydrateFallbackType): () => React.ReactElement<{
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
}, string | React.JSXElementConstructor<any>>;
declare function useErrorBoundaryProps(): {
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
error: unknown;
};
type ErrorBoundaryProps = ReturnType<typeof useErrorBoundaryProps>;
type ErrorBoundaryType = React.ComponentType<ErrorBoundaryProps>;
declare function WithErrorBoundaryProps({ children, }: {
children: React.ReactElement;
}): React.ReactElement<any, string | React.JSXElementConstructor<any>>;
declare function withErrorBoundaryProps(ErrorBoundary: ErrorBoundaryType): () => React.ReactElement<{
params: Readonly<Params<string>>;
loaderData: any;
actionData: any;
error: unknown;
}, string | React.JSXElementConstructor<any>>;
type ParamKeyValuePair = [string, string];
type URLSearchParamsInit = string | ParamKeyValuePair[] | Record<string, string | string[]> | URLSearchParams;
/**
Creates a URLSearchParams object using the given initializer.
This is identical to `new URLSearchParams(init)` except it also
supports arrays as values in the object form of the initializer
instead of just strings. This is convenient when you need multiple
values for a given key, but don't want to use an array initializer.
For example, instead of:
```tsx
let searchParams = new URLSearchParams([
['sort', 'name'],
['sort', 'price']
]);
```
you can do:
```
let searchParams = createSearchParams({
sort: ['name', 'price']
});
```
@category Utils
*/
declare function createSearchParams(init?: URLSearchParamsInit): URLSearchParams;
type JsonObject = {
[Key in string]: JsonValue;
} & {
[Key in string]?: JsonValue | undefined;
};
type JsonArray = JsonValue[] | readonly JsonValue[];
type JsonPrimitive = string | number | boolean | null;
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
type SubmitTarget = HTMLFormElement | HTMLButtonElement | HTMLInputElement | FormData | URLSearchParams | JsonValue | null;
/**
* Submit options shared by both navigations and fetchers
*/
interface SharedSubmitOptions {
/**
* The HTTP method used to submit the form. Overrides `<form method>`.
* Defaults to "GET".
*/
method?: HTMLFormMethod;
/**
* The action URL path used to submit the form. Overrides `<form action>`.
* Defaults to the path of the current route.
*/
action?: string;
/**
* The encoding used to submit the form. Overrides `<form encType>`.
* Defaults to "application/x-www-form-urlencoded".
*/
encType?: FormEncType;
/**
* Determines whether the form action is relative to the route hierarchy or
* the pathname. Use this if you want to opt out of navigating the route
* hierarchy and want to instead route based on /-delimited URL segments
*/
relative?: RelativeRoutingType;
/**
* In browser-based environments, prevent resetting scroll after this
* navigation when using the <ScrollRestoration> component
*/
preventScrollReset?: boolean;
/**
* Enable flushSync for this submission's state updates
*/
flushSync?: boolean;
/**
* Specify the default revalidation behavior after this submission
*
* If no `shouldRevalidate` functions are present on the active routes, then this
* value will be used directly. Otherwise it will be passed into `shouldRevalidate`
* so the route can make the final determination on revalidation. This can be
* useful when updating search params and you don't want to trigger a revalidation.
*
* By default (when not specified), loaders will revalidate according to the routers
* standard revalidation behavior.
*/
defaultShouldRevalidate?: boolean;
}
/**
* Submit options available to fetchers
*/
interface FetcherSubmitOptions extends SharedSubmitOptions {
}
/**
* Submit options available to navigations
*/
interface SubmitOptions extends FetcherSubmitOptions {
/**
* Set `true` to replace the current entry in the browser's history stack
* instead of creating a new one (i.e. stay on "the same page"). Defaults
* to `false`.
*/
replace?: boolean;
/**
* State object to add to the history stack entry for this navigation
*/
state?: any;
/**
* Indicate a specific fetcherKey to use when using navigate=false
*/
fetcherKey?: string;
/**
* navigate=false will use a fetcher instead of a navigation
*/
navigate?: boolean;
/**
* Enable view transitions on this submission navigation
*/
viewTransition?: boolean;
}
declare const FrameworkContext: React.Context<FrameworkContextObject | undefined>;
/**
* Defines the [lazy route discovery](../../explanation/lazy-route-discovery)
* behavior of the link/form:
*
* - "render" - default, discover the route when the link renders
* - "none" - don't eagerly discover, only discover if the link is clicked
*/
type DiscoverBehavior = "render" | "none";
/**
* Defines the prefetching behavior of the link:
*
* - "none": Never fetched
* - "intent": Fetched when the user focuses or hovers the link
* - "render": Fetched when the link is rendered
* - "viewport": Fetched when the link is in the viewport
*/
type PrefetchBehavior = "intent" | "render" | "none" | "viewport";
/**
* Props for the {@link Links} component.
*
* @category Types
*/
interface LinksProps {
/**
* A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* element. If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*/
nonce?: string | undefined;
/**
* A [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)
* attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* element
*/
crossOrigin?: "anonymous" | "use-credentials";
}
/**
* Renders all the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags created by the route module's [`links`](../../start/framework/route-module#links)
* export. You should render it inside the [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
* of your document.
*
* @example
* import { Links } from "react-router";
*
* export default function Root() {
* return (
* <html>
* <head>
* <Links />
* </head>
* <body></body>
* </html>
* );
* }
*
* @public
* @category Components
* @mode framework
* @param props Props
* @param {LinksProps.nonce} props.nonce n/a
* @param {LinksProps.crossOrigin} props.crossOrigin n/a
* @returns A collection of React elements for [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags
*/
declare function Links({ nonce, crossOrigin }: LinksProps): React.JSX.Element;
/**
* Renders [`<link rel=prefetch|modulepreload>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/rel)
* tags for modules and data of another page to enable an instant navigation to
* that page. [`<Link prefetch>`](./Link#prefetch) uses this internally, but you
* can render it to prefetch a page for any other reason.
*
* For example, you may render one of this as the user types into a search field
* to prefetch search results before they click through to their selection.
*
* @example
* import { PrefetchPageLinks } from "react-router";
*
* <PrefetchPageLinks page="/absolute/path" />
*
* @public
* @category Components
* @mode framework
* @param props Props
* @param {PageLinkDescriptor.page} props.page n/a
* @param props.linkProps Additional props to spread onto the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags, such as [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/crossOrigin),
* [`integrity`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/integrity),
* [`rel`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/rel),
* etc.
* @returns A collection of React elements for [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags
*/
declare function PrefetchPageLinks({ page, ...linkProps }: PageLinkDescriptor): React.JSX.Element | null;
/**
* Renders all the [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
* tags created by the route module's [`meta`](../../start/framework/route-module#meta)
* export. You should render it inside the [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
* of your document.
*
* @example
* import { Meta } from "react-router";
*
* export default function Root() {
* return (
* <html>
* <head>
* <Meta />
* </head>
* </html>
* );
* }
*
* @public
* @category Components
* @mode framework
* @returns A collection of React elements for [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
* tags
*/
declare function Meta(): React.JSX.Element;
/**
* A couple common attributes:
*
* - `<Scripts crossOrigin>` for hosting your static assets on a different
* server than your app.
* - `<Scripts nonce>` to support a [content security policy for scripts](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src)
* with [nonce-sources](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources)
* for your [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tags.
*
* You cannot pass through attributes such as [`async`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/async),
* [`defer`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/defer),
* [`noModule`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/noModule),
* [`src`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/src),
* or [`type`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/type),
* because they are managed by React Router internally.
*
* @category Types
*/
type ScriptsProps = Omit<React.HTMLProps<HTMLScriptElement>, "async" | "children" | "dangerouslySetInnerHTML" | "defer" | "noModule" | "src" | "suppressHydrationWarning" | "type"> & {
/**
* A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* attribute to render on the [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* element. If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*/
nonce?: string | undefined;
};
/**
* Renders the client runtime of your app. It should be rendered inside the
* [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)
* of the document.
*
* If server rendering, you can omit `<Scripts/>` and the app will work as a
* traditional web app without JavaScript, relying solely on HTML and browser
* behaviors.
*
* @example
* import { Scripts } from "react-router";
*
* export default function Root() {
* return (
* <html>
* <head />
* <body>
* <Scripts />
* </body>
* </html>
* );
* }
*
* @public
* @category Components
* @mode framework
* @param scriptProps Additional props to spread onto the [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tags, such as [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/crossOrigin),
* [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce),
* etc.
* @returns A collection of React elements for [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tags
*/
declare function Scripts(scriptProps: ScriptsProps): React.JSX.Element | null;
/**
* @category Data Routers
*/
interface DOMRouterOpts {
/**
* Basename path for the application.
*/
basename?: string;
/**
* A function that returns an {@link RouterContextProvider} instance
* which is provided as the `context` argument to client [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* This function is called to generate a fresh `context` instance on each
* navigation or fetcher call.
*
* ```tsx
* import {
* createContext,
* RouterContextProvider,
* } from "react-router";
*
* const apiClientContext = createContext<APIClient>();
*
* function createBrowserRouter(routes, {
* getContext() {
* let context = new RouterContextProvider();
* context.set(apiClientContext, getApiClient());
* return context;
* }
* })
* ```
*/
getContext?: RouterInit["getContext"];
/**
* Future flags to enable for the router.
*/
future?: Partial<FutureConfig$1>;
/**
* When Server-Rendering and opting-out of automatic hydration, the
* `hydrationData` option allows you to pass in hydration data from your
* server-render. This will almost always be a subset of data from the
* {@link StaticHandlerContext} value you get back from the {@link StaticHandler}'s
* `query` method:
*
* ```tsx
* const router = createBrowserRouter(routes, {
* hydrationData: {
* loaderData: {
* // [routeId]: serverLoaderData
* },
* // may also include `errors` and/or `actionData`
* },
* });
* ```
*
* **Partial Hydration Data**
*
* You will almost always include a complete set of `loaderData` to hydrate a
* server-rendered app. But in advanced use-cases (such as Framework Mode's
* [`clientLoader`](../../start/framework/route-module#clientLoader)), you may
* want to include `loaderData` for only some routes that were loaded/rendered
* on the server. This allows you to hydrate _some_ of the routes (such as the
* app layout/shell) while showing a `HydrateFallback` component and running
* the [`loader`](../../start/data/route-object#loader)s for other routes
* during hydration.
*
* A route [`loader`](../../start/data/route-object#loader) will run during
* hydration in two scenarios:
*
* 1. No hydration data is provided
* In these cases the `HydrateFallback` component will render on initial
* hydration
* 2. The `loader.hydrate` property is set to `true`
* This allows you to run the [`loader`](../../start/data/route-object#loader)
* even if you did not render a fallback on initial hydration (i.e., to
* prime a cache with hydration data)
*
* ```tsx
* const router = createBrowserRouter(
* [
* {
* id: "root",
* loader: rootLoader,
* Component: Root,
* children: [
* {
* id: "index",
* loader: indexLoader,
* HydrateFallback: IndexSkeleton,
* Component: Index,
* },
* ],
* },
* ],
* {
* hydrationData: {
* loaderData: {
* root: "ROOT DATA",
* // No index data provided
* },
* },
* }
* );
* ```
*/
hydrationData?: HydrationState;
/**
* Array of instrumentation objects allowing you to instrument the router and
* individual routes prior to router initialization (and on any subsequently
* added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
* mostly useful for observability such as wrapping navigations, fetches,
* as well as route loaders/actions/middlewares with logging and/or performance
* tracing. See the [docs](../../how-to/instrumentation) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* instrumentations: [logging]
* });
*
*
* let logging = {
* router({ instrument }) {
* instrument({
* navigate: (impl, info) => logExecution(`navigate ${info.to}`, impl),
* fetch: (impl, info) => logExecution(`fetch ${info.to}`, impl)
* });
* },
* route({ instrument, id }) {
* instrument({
* middleware: (impl, info) => logExecution(
* `middleware ${info.request.url} (route ${id})`,
* impl
* ),
* loader: (impl, info) => logExecution(
* `loader ${info.request.url} (route ${id})`,
* impl
* ),
* action: (impl, info) => logExecution(
* `action ${info.request.url} (route ${id})`,
* impl
* ),
* })
* }
* };
*
* async function logExecution(label: string, impl: () => Promise<void>) {
* let start = performance.now();
* console.log(`start ${label}`);
* await impl();
* let duration = Math.round(performance.now() - start);
* console.log(`end ${label} (${duration}ms)`);
* }
* ```
*/
instrumentations?: ClientInstrumentation[];
/**
* Override the default data strategy of running loaders in parallel -
* see the [docs](../../how-to/data-strategy) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* async dataStrategy({
* matches,
* request,
* runClientMiddleware,
* }) {
* const matchesToLoad = matches.filter((m) =>
* m.shouldCallHandler(),
* );
*
* const results: Record<string, DataStrategyResult> = {};
* await runClientMiddleware(() =>
* Promise.all(
* matchesToLoad.map(async (match) => {
* results[match.route.id] = await match.resolve();
* }),
* ),
* );
* return results;
* },
* });
* ```
*/
dataStrategy?: DataStrategyFunction;
/**
* Lazily define portions of the route tree on navigations.
* See {@link PatchRoutesOnNavigationFunction}.
*
* By default, React Router wants you to provide a full route tree up front via
* `createBrowserRouter(routes)`. This allows React Router to perform synchronous
* route matching, execute loaders, and then render route components in the most
* optimistic manner without introducing waterfalls. The tradeoff is that your
* initial JS bundle is larger by definition — which may slow down application
* start-up times as your application grows.
*
* To combat this, we introduced [`route.lazy`](../../start/data/route-object#lazy)
* in [v6.9.0](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v690)
* which lets you lazily load the route _implementation_ ([`loader`](../../start/data/route-object#loader),
* [`Component`](../../start/data/route-object#Component), etc.) while still
* providing the route _definition_ aspects up front (`path`, `index`, etc.).
* This is a good middle ground. React Router still knows about your route
* definitions (the lightweight part) up front and can perform synchronous
* route matching, but then delay loading any of the route implementation
* aspects (the heavier part) until the route is actually navigated to.
*
* In some cases, even this doesn't go far enough. For huge applications,
* providing all route definitions up front can be prohibitively expensive.
* Additionally, it might not even be possible to provide all route definitions
* up front in certain Micro-Frontend or Module-Federation architectures.
*
* This is where `patchRoutesOnNavigation` comes in ([RFC](https://github.com/remix-run/react-router/discussions/11113)).
* This API is for advanced use-cases where you are unable to provide the full
* route tree up-front and need a way to lazily "discover" portions of the route
* tree at runtime. This feature is often referred to as ["Fog of War"](https://en.wikipedia.org/wiki/Fog_of_war),
* because similar to how video games expand the "world" as you move around -
* the router would be expanding its routing tree as the user navigated around
* the app - but would only ever end up loading portions of the tree that the
* user visited.
*
* `patchRoutesOnNavigation` will be called anytime React Router is unable to
* match a `path`. The arguments include the `path`, any partial `matches`,
* and a `patch` function you can call to patch new routes into the tree at a
* specific location. This method is executed during the `loading` portion of
* the navigation for `GET` requests and during the `submitting` portion of
* the navigation for non-`GET` requests.
*
* <details>
* <summary><b>Example <code>patchRoutesOnNavigation</code> Use Cases</b></summary>
*
* **Patching children into an existing route**
*
* ```tsx
* const router = createBrowserRouter(
* [
* {
* id: "root",
* path: "/",
* Component: RootComponent,
* },
* ],
* {
* async patchRoutesOnNavigation({ patch, path }) {
* if (path === "/a") {
* // Load/patch the `a` route as a child of the route with id `root`
* let route = await getARoute();
* // ^ { path: 'a', Component: A }
* patch("root", [route]);
* }
* },
* }
* );
* ```
*
* In the above example, if the user clicks a link to `/a`, React Router
* won't match any routes initially and will call `patchRoutesOnNavigation`
* with a `path = "/a"` and a `matches` array containing the root route
* match. By calling `patch('root', [route])`, the new route will be added
* to the route tree as a child of the `root` route and React Router will
* perform matching on the updated routes. This time it will successfully
* match the `/a` path and the navigation will complete successfully.
*
* **Patching new root-level routes**
*
* If you need to patch a new route to the top of the tree (i.e., it doesn't
* have a parent), you can pass `null` as the `routeId`:
*
* ```tsx
* const router = createBrowserRouter(
* [
* {
* id: "root",
* path: "/",
* Component: RootComponent,
* },
* ],
* {
* async patchRoutesOnNavigation({ patch, path }) {
* if (path === "/root-sibling") {
* // Load/patch the `/root-sibling` route as a sibling of the root route
* let route = await getRootSiblingRoute();
* // ^ { path: '/root-sibling', Component: RootSibling }
* patch(null, [route]);
* }
* },
* }
* );
* ```
*
* **Patching subtrees asynchronously**
*
* You can also perform asynchronous matching to lazily fetch entire sections
* of your application:
*
* ```tsx
* let router = createBrowserRouter(
* [
* {
* path: "/",
* Component: Home,
* },
* ],
* {
* async patchRoutesOnNavigation({ patch, path }) {
* if (path.startsWith("/dashboard")) {
* let children = await import("./dashboard");
* patch(null, children);
* }
* if (path.startsWith("/account")) {
* let children = await import("./account");
* patch(null, children);
* }
* },
* }
* );
* ```
*
* <docs-info>If in-progress execution of `patchRoutesOnNavigation` is
* interrupted by a later navigation, then any remaining `patch` calls in
* the interrupted execution will not update the route tree because the
* operation was cancelled.</docs-info>
*
* **Co-locating route discovery with route definition**
*
* If you don't wish to perform your own pseudo-matching, you can leverage
* the partial `matches` array and the [`handle`](../../start/data/route-object#handle)
* field on a route to keep the children definitions co-located:
*
* ```tsx
* let router = createBrowserRouter(
* [
* {
* path: "/",
* Component: Home,
* },
* {
* path: "/dashboard",
* children: [
* {
* // If we want to include /dashboard in the critical routes, we need to
* // also include it's index route since patchRoutesOnNavigation will not be
* // called on a navigation to `/dashboard` because it will have successfully
* // matched the `/dashboard` parent route
* index: true,
* // ...
* },
* ],
* handle: {
* lazyChildren: () => import("./dashboard"),
* },
* },
* {
* path: "/account",
* children: [
* {
* index: true,
* // ...
* },
* ],
* handle: {
* lazyChildren: () => import("./account"),
* },
* },
* ],
* {
* async patchRoutesOnNavigation({ matches, patch }) {
* let leafRoute = matches[matches.length - 1]?.route;
* if (leafRoute?.handle?.lazyChildren) {
* let children =
* await leafRoute.handle.lazyChildren();
* patch(leafRoute.id, children);
* }
* },
* }
* );
* ```
*
* **A note on routes with parameters**
*
* Because React Router uses ranked routes to find the best match for a
* given path, there is an interesting ambiguity introduced when only a
* partial route tree is known at any given point in time. If we match a
* fully static route such as `path: "/about/contact-us"` then we know we've
* found the right match since it's composed entirely of static URL segments.
* Thus, we do not need to bother asking for any other potentially
* higher-scoring routes.
*
* However, routes with parameters (dynamic or splat) can't make this
* assumption because there might be a not-yet-discovered route that scores
* higher. Consider a full route tree such as:
*
* ```tsx
* // Assume this is the full route tree for your app
* const routes = [
* {
* path: "/",
* Component: Home,
* },
* {
* id: "blog",
* path: "/blog",
* Component: BlogLayout,
* children: [
* { path: "new", Component: NewPost },
* { path: ":slug", Component: BlogPost },
* ],
* },
* ];
* ```
*
* And then assume we want to use `patchRoutesOnNavigation` to fill this in
* as the user navigates around:
*
* ```tsx
* // Start with only the index route
* const router = createBrowserRouter(
* [
* {
* path: "/",
* Component: Home,
* },
* ],
* {
* async patchRoutesOnNavigation({ patch, path }) {
* if (path === "/blog/new") {
* patch("blog", [
* {
* path: "new",
* Component: NewPost,
* },
* ]);
* } else if (path.startsWith("/blog")) {
* patch("blog", [
* {
* path: ":slug",
* Component: BlogPost,
* },
* ]);
* }
* },
* }
* );
* ```
*
* If the user were to a blog post first (i.e., `/blog/my-post`) we would
* patch in the `:slug` route. Then, if the user navigated to `/blog/new` to
* write a new post, we'd match `/blog/:slug` but it wouldn't be the _right_
* match! We need to call `patchRoutesOnNavigation` just in case there
* exists a higher-scoring route we've not yet discovered, which in this
* case there is.
*
* So, anytime React Router matches a path that contains at least one param,
* it will call `patchRoutesOnNavigation` and match routes again just to
* confirm it has found the best match.
*
* If your `patchRoutesOnNavigation` implementation is expensive or making
* side effect [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch)
* calls to a backend server, you may want to consider tracking previously
* seen routes to avoid over-fetching in cases where you know the proper
* route has already been found. This can usually be as simple as
* maintaining a small cache of prior `path` values for which you've already
* patched in the right routes:
*
* ```tsx
* let discoveredRoutes = new Set();
*
* const router = createBrowserRouter(routes, {
* async patchRoutesOnNavigation({ patch, path }) {
* if (discoveredRoutes.has(path)) {
* // We've seen this before so nothing to patch in and we can let the router
* // use the routes it already knows about
* return;
* }
*
* discoveredRoutes.add(path);
*
* // ... patch routes in accordingly
* },
* });
* ```
* </details>
*/
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
/**
* [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object
* override. Defaults to the global `window` instance.
*/
window?: Window;
}
/**
* Create a new {@link DataRouter| data router} that manages the application
* path via [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)
* and [`history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState).
*
* Data Routers should not be held in React state. You should create your router
* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
*
* @public
* @category Data Routers
* @mode data
* @param routes Application routes
* @param opts Options
* @param {DOMRouterOpts.basename} opts.basename n/a
* @param {DOMRouterOpts.dataStrategy} opts.dataStrategy n/a
* @param {DOMRouterOpts.future} opts.future n/a
* @param {DOMRouterOpts.getContext} opts.getContext n/a
* @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a
* @param {DOMRouterOpts.instrumentations} opts.instrumentations n/a
* @param {DOMRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a
* @param {DOMRouterOpts.window} opts.window n/a
* @returns An initialized {@link DataRouter| data router} to pass to {@link RouterProvider | `<RouterProvider>`}
*/
declare function createBrowserRouter(routes: RouteObject[], opts?: DOMRouterOpts): Router$1;
/**
* Create a new {@link DataRouter| data router} that manages the application
* path via the URL [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash).
*
* Data Routers should not be held in React state. You should create your router
* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
*
* @public
* @category Data Routers
* @mode data
* @param routes Application routes
* @param opts Options
* @param {DOMRouterOpts.basename} opts.basename n/a
* @param {DOMRouterOpts.future} opts.future n/a
* @param {DOMRouterOpts.getContext} opts.getContext n/a
* @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a
* @param {DOMRouterOpts.instrumentations} opts.instrumentations n/a
* @param {DOMRouterOpts.dataStrategy} opts.dataStrategy n/a
* @param {DOMRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a
* @param {DOMRouterOpts.window} opts.window n/a
* @returns An initialized {@link DataRouter| data router} to pass to {@link RouterProvider | `<RouterProvider>`}
*/
declare function createHashRouter(routes: RouteObject[], opts?: DOMRouterOpts): Router$1;
/**
* @category Types
*/
interface BrowserRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* {@link Route | `<Route>`} components describing your route configuration
*/
children?: React.ReactNode;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
/**
* [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object
* override. Defaults to the global `window` instance
*/
window?: Window;
}
/**
* A declarative {@link Router | `<Router>`} using the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* API for client-side routing.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {BrowserRouterProps.basename} props.basename n/a
* @param {BrowserRouterProps.children} props.children n/a
* @param {BrowserRouterProps.useTransitions} props.useTransitions n/a
* @param {BrowserRouterProps.window} props.window n/a
* @returns A declarative {@link Router | `<Router>`} using the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* API for client-side routing.
*/
declare function BrowserRouter({ basename, children, useTransitions, window, }: BrowserRouterProps): React.JSX.Element;
/**
* @category Types
*/
interface HashRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* {@link Route | `<Route>`} components describing your route configuration
*/
children?: React.ReactNode;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
/**
* [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object
* override. Defaults to the global `window` instance
*/
window?: Window;
}
/**
* A declarative {@link Router | `<Router>`} that stores the location in the
* [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash) portion
* of the URL so it is not sent to the server.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {HashRouterProps.basename} props.basename n/a
* @param {HashRouterProps.children} props.children n/a
* @param {HashRouterProps.useTransitions} props.useTransitions n/a
* @param {HashRouterProps.window} props.window n/a
* @returns A declarative {@link Router | `<Router>`} using the URL [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash)
* for client-side routing.
*/
declare function HashRouter({ basename, children, useTransitions, window, }: HashRouterProps): React.JSX.Element;
/**
* @category Types
*/
interface HistoryRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* {@link Route | `<Route>`} components describing your route configuration
*/
children?: React.ReactNode;
/**
* A {@link History} implementation for use by the router
*/
history: History;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* A declarative {@link Router | `<Router>`} that accepts a pre-instantiated
* `history` object.
* It's important to note that using your own `history` object is highly discouraged
* and may add two versions of the `history` library to your bundles unless you use
* the same version of the `history` library that React Router uses internally.
*
* @name unstable_HistoryRouter
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {HistoryRouterProps.basename} props.basename n/a
* @param {HistoryRouterProps.children} props.children n/a
* @param {HistoryRouterProps.history} props.history n/a
* @param {HistoryRouterProps.useTransitions} props.useTransitions n/a
* @returns A declarative {@link Router | `<Router>`} using the provided history
* implementation for client-side routing.
*/
declare function HistoryRouter({ basename, children, history, useTransitions, }: HistoryRouterProps): React.JSX.Element;
declare namespace HistoryRouter {
var displayName: string;
}
/**
* @category Types
*/
interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
/**
* Defines the link [lazy route discovery](../../explanation/lazy-route-discovery) behavior.
*
* - **render** — default, discover the route when the link renders
* - **none** — don't eagerly discover, only discover if the link is clicked
*
* ```tsx
* <Link /> // default ("render")
* <Link discover="render" />
* <Link discover="none" />
* ```
*/
discover?: DiscoverBehavior;
/**
* Defines the data and module prefetching behavior for the link.
*
* ```tsx
* <Link /> // default
* <Link prefetch="none" />
* <Link prefetch="intent" />
* <Link prefetch="render" />
* <Link prefetch="viewport" />
* ```
*
* - **none** — default, no prefetching
* - **intent** — prefetches when the user hovers or focuses the link
* - **render** — prefetches when the link renders
* - **viewport** — prefetches when the link is in the viewport, very useful for mobile
*
* Prefetching is done with HTML [`<link rel="prefetch">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags. They are inserted after the link.
*
* ```tsx
* <a href="..." />
* <a href="..." />
* <link rel="prefetch" /> // might conditionally render
* ```
*
* Because of this, if you are using `nav :last-child` you will need to use
* `nav :last-of-type` so the styles don't conditionally fall off your last link
* (and any other similar selectors).
*/
prefetch?: PrefetchBehavior;
/**
* Will use document navigation instead of client side routing when the link is
* clicked: the browser will handle the transition normally (as if it were an
* [`<a href>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)).
*
* ```tsx
* <Link to="/logout" reloadDocument />
* ```
*/
reloadDocument?: boolean;
/**
* Replaces the current entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack instead of pushing a new one onto it.
*
* ```tsx
* <Link replace />
* ```
*
* ```
* # with a history stack like this
* A -> B
*
* # normal link click pushes a new entry
* A -> B -> C
*
* # but with `replace`, B is replaced by C
* A -> C
* ```
*/
replace?: boolean;
/**
* Adds persistent client side routing state to the next location.
*
* ```tsx
* <Link to="/somewhere/else" state={{ some: "value" }} />
* ```
*
* The location state is accessed from the `location`.
*
* ```tsx
* function SomeComp() {
* const location = useLocation();
* location.state; // { some: "value" }
* }
* ```
*
* This state is inaccessible on the server as it is implemented on top of
* [`history.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state)
*/
state?: any;
/**
* Prevents the scroll position from being reset to the top of the window when
* the link is clicked and the app is using {@link ScrollRestoration}. This only
* prevents new locations resetting scroll to the top, scroll position will be
* restored for back/forward button navigation.
*
* ```tsx
* <Link to="?tab=one" preventScrollReset />
* ```
*/
preventScrollReset?: boolean;
/**
* Defines the relative path behavior for the link.
*
* ```tsx
* <Link to=".." /> // default: "route"
* <Link relative="route" />
* <Link relative="path" />
* ```
*
* Consider a route hierarchy where a parent route pattern is `"blog"` and a child
* route pattern is `"blog/:slug/edit"`.
*
* - **route** — default, resolves the link relative to the route pattern. In the
* example above, a relative link of `"..."` will remove both `:slug/edit` segments
* back to `"/blog"`.
* - **path** — relative to the path so `"..."` will only remove one URL segment up
* to `"/blog/:slug"`
*
* Note that index routes and layout routes do not have paths so they are not
* included in the relative path calculation.
*/
relative?: RelativeRoutingType;
/**
* Can be a string or a partial {@link Path}:
*
* ```tsx
* <Link to="/some/path" />
*
* <Link
* to={{
* pathname: "/some/path",
* search: "?query=string",
* hash: "#hash",
* }}
* />
* ```
*/
to: To;
/**
* Enables a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* for this navigation.
*
* ```jsx
* <Link to={to} viewTransition>
* Click me
* </Link>
* ```
*
* To apply specific styles for the transition, see {@link useViewTransitionState}
*/
viewTransition?: boolean;
/**
* Specify the default revalidation behavior for the navigation.
*
* ```tsx
* <Link to="/some/path" defaultShouldRevalidate={false} />
* ```
*
* If no `shouldRevalidate` functions are present on the active routes, then this
* value will be used directly. Otherwise it will be passed into `shouldRevalidate`
* so the route can make the final determination on revalidation. This can be
* useful when updating search params and you don't want to trigger a revalidation.
*
* By default (when not specified), loaders will revalidate according to the routers
* standard revalidation behavior.
*/
defaultShouldRevalidate?: boolean;
/**
* Masked path for this navigation, when you want to navigate the router to
* one location but display a separate location in the URL bar.
*
* This is useful for contextual navigations such as opening an image in a modal
* on top of a gallery while keeping the underlying gallery active. If a user
* shares the masked URL, or opens the link in a new tab, they will only load
* the masked location without the underlying contextual location.
*
* This feature relies on `history.state` and is thus only intended for SPA uses
* and SSR renders will not respect the masking.
*
* ```tsx
* // routes/gallery.tsx
* export function clientLoader({ request }: Route.LoaderArgs) {
* let sp = new URL(request.url).searchParams;
* return {
* images: getImages(),
* modalImage: sp.has("image") ? getImage(sp.get("image")!) : null,
* };
* }
*
* export default function Gallery({ loaderData }: Route.ComponentProps) {
* return (
* <>
* <GalleryGrid>
* {loaderData.images.map((image) => (
* <Link
* key={image.id}
* to={`/gallery?image=${image.id}`}
* mask={`/images/${image.id}`}
* >
* <img src={image.url} alt={image.alt} />
* </Link>
* ))}
* </GalleryGrid>
*
* {data.modalImage ? (
* <dialog open>
* <img src={data.modalImage.url} alt={data.modalImage.alt} />
* </dialog>
* ) : null}
* </>
* );
* }
* ```
*/
mask?: To;
}
/**
* A progressively enhanced [`<a href>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
* wrapper to enable navigation with client-side routing.
*
* @example
* import { Link } from "react-router";
*
* <Link to="/dashboard">Dashboard</Link>;
*
* <Link
* to={{
* pathname: "/some/path",
* search: "?query=string",
* hash: "#hash",
* }}
* />;
*
* @public
* @category Components
* @param {LinkProps.discover} props.discover [modes: framework] n/a
* @param {LinkProps.prefetch} props.prefetch [modes: framework] n/a
* @param {LinkProps.preventScrollReset} props.preventScrollReset [modes: framework, data] n/a
* @param {LinkProps.relative} props.relative n/a
* @param {LinkProps.reloadDocument} props.reloadDocument n/a
* @param {LinkProps.replace} props.replace n/a
* @param {LinkProps.state} props.state n/a
* @param {LinkProps.to} props.to n/a
* @param {LinkProps.viewTransition} props.viewTransition [modes: framework, data] n/a
* @param {LinkProps.defaultShouldRevalidate} props.defaultShouldRevalidate n/a
* @param {LinkProps.mask} props.mask [modes: framework, data] n/a
*/
declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>;
/**
* The object passed to {@link NavLink} `children`, `className`, and `style` prop
* callbacks to render and style the link based on its state.
*
* ```
* // className
* <NavLink
* to="/messages"
* className={({ isActive, isPending }) =>
* isPending ? "pending" : isActive ? "active" : ""
* }
* >
* Messages
* </NavLink>
*
* // style
* <NavLink
* to="/messages"
* style={({ isActive, isPending }) => {
* return {
* fontWeight: isActive ? "bold" : "",
* color: isPending ? "red" : "black",
* }
* )}
* />
*
* // children
* <NavLink to="/tasks">
* {({ isActive, isPending }) => (
* <span className={isActive ? "active" : ""}>Tasks</span>
* )}
* </NavLink>
* ```
*
*/
type NavLinkRenderProps = {
/**
* Indicates if the link's URL matches the current {@link Location}.
*/
isActive: boolean;
/**
* Indicates if the pending {@link Location} matches the link's URL. Only
* available in Framework/Data modes.
*/
isPending: boolean;
/**
* Indicates if a view transition to the link's URL is in progress.
* See {@link useViewTransitionState}
*/
isTransitioning: boolean;
};
/**
* @category Types
*/
interface NavLinkProps extends Omit<LinkProps, "className" | "style" | "children"> {
/**
* Can be regular React children or a function that receives an object with the
* `active` and `pending` states of the link.
*
* ```tsx
* <NavLink to="/tasks">
* {({ isActive }) => (
* <span className={isActive ? "active" : ""}>Tasks</span>
* )}
* </NavLink>
* ```
*/
children?: React.ReactNode | ((props: NavLinkRenderProps) => React.ReactNode);
/**
* Changes the matching logic to make it case-sensitive:
*
* | Link | URL | isActive |
* | -------------------------------------------- | ------------- | -------- |
* | `<NavLink to="/SpOnGe-bOB" />` | `/sponge-bob` | true |
* | `<NavLink to="/SpOnGe-bOB" caseSensitive />` | `/sponge-bob` | false |
*/
caseSensitive?: boolean;
/**
* Classes are automatically applied to `NavLink` that correspond to the state.
*
* ```css
* a.active {
* color: red;
* }
* a.pending {
* color: blue;
* }
* a.transitioning {
* view-transition-name: my-transition;
* }
* ```
*
* Or you can specify a function that receives {@link NavLinkRenderProps} and
* returns the `className`:
*
* ```tsx
* <NavLink className={({ isActive, isPending }) => (
* isActive ? "my-active-class" :
* isPending ? "my-pending-class" :
* ""
* )} />
* ```
*/
className?: string | ((props: NavLinkRenderProps) => string | undefined);
/**
* Changes the matching logic for the `active` and `pending` states to only match
* to the "end" of the {@link NavLinkProps.to}. If the URL is longer, it will no
* longer be considered active.
*
* | Link | URL | isActive |
* | ----------------------------- | ------------ | -------- |
* | `<NavLink to="/tasks" />` | `/tasks` | true |
* | `<NavLink to="/tasks" />` | `/tasks/123` | true |
* | `<NavLink to="/tasks" end />` | `/tasks` | true |
* | `<NavLink to="/tasks" end />` | `/tasks/123` | false |
*
* `<NavLink to="/">` is an exceptional case because _every_ URL matches `/`.
* To avoid this matching every single route by default, it effectively ignores
* the `end` prop and only matches when you're at the root route.
*/
end?: boolean;
/**
* Styles can also be applied dynamically via a function that receives
* {@link NavLinkRenderProps} and returns the styles:
*
* ```tsx
* <NavLink to="/tasks" style={{ color: "red" }} />
* <NavLink to="/tasks" style={({ isActive, isPending }) => ({
* color:
* isActive ? "red" :
* isPending ? "blue" : "black"
* })} />
* ```
*/
style?: React.CSSProperties | ((props: NavLinkRenderProps) => React.CSSProperties | undefined);
}
/**
* Wraps {@link Link | `<Link>`} with additional props for styling active and
* pending states.
*
* - Automatically applies classes to the link based on its `active` and `pending`
* states, see {@link NavLinkProps.className}
* - Note that `pending` is only available with Framework and Data modes.
* - Automatically applies `aria-current="page"` to the link when the link is active.
* See [`aria-current`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)
* on MDN.
* - States are additionally available through the className, style, and children
* render props. See {@link NavLinkRenderProps}.
*
* @example
* <NavLink to="/message">Messages</NavLink>
*
* // Using render props
* <NavLink
* to="/messages"
* className={({ isActive, isPending }) =>
* isPending ? "pending" : isActive ? "active" : ""
* }
* >
* Messages
* </NavLink>
*
* @public
* @category Components
* @param {NavLinkProps.caseSensitive} props.caseSensitive n/a
* @param {NavLinkProps.children} props.children n/a
* @param {NavLinkProps.className} props.className n/a
* @param {NavLinkProps.discover} props.discover [modes: framework] n/a
* @param {NavLinkProps.end} props.end n/a
* @param {NavLinkProps.prefetch} props.prefetch [modes: framework] n/a
* @param {NavLinkProps.preventScrollReset} props.preventScrollReset [modes: framework, data] n/a
* @param {NavLinkProps.relative} props.relative n/a
* @param {NavLinkProps.reloadDocument} props.reloadDocument n/a
* @param {NavLinkProps.replace} props.replace n/a
* @param {NavLinkProps.state} props.state n/a
* @param {NavLinkProps.style} props.style n/a
* @param {NavLinkProps.to} props.to n/a
* @param {NavLinkProps.viewTransition} props.viewTransition [modes: framework, data] n/a
*/
declare const NavLink: React.ForwardRefExoticComponent<NavLinkProps & React.RefAttributes<HTMLAnchorElement>>;
/**
* Form props shared by navigations and fetchers
*/
interface SharedFormProps extends React.FormHTMLAttributes<HTMLFormElement> {
/**
* The HTTP verb to use when the form is submitted. Supports `"delete"`,
* `"get"`, `"patch"`, `"post"`, and `"put"`.
*
* Native [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
* only supports `"get"` and `"post"`, avoid the other verbs if you'd like to
* support progressive enhancement
*/
method?: HTMLFormMethod;
/**
* The encoding type to use for the form submission.
*
* ```tsx
* <Form encType="application/x-www-form-urlencoded"/> // Default
* <Form encType="multipart/form-data"/>
* <Form encType="text/plain"/>
* ```
*/
encType?: "application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain";
/**
* The URL to submit the form data to. If `undefined`, this defaults to the
* closest route in context.
*/
action?: string;
/**
* Determines whether the form action is relative to the route hierarchy or
* the pathname. Use this if you want to opt out of navigating the route
* hierarchy and want to instead route based on slash-delimited URL segments.
* See {@link RelativeRoutingType}.
*/
relative?: RelativeRoutingType;
/**
* Prevent the scroll position from resetting to the top of the viewport on
* completion of the navigation when using the
* {@link ScrollRestoration | `<ScrollRestoration>`} component
*/
preventScrollReset?: boolean;
/**
* A function to call when the form is submitted. If you call
* [`event.preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
* then this form will not do anything.
*/
onSubmit?: React.FormEventHandler<HTMLFormElement>;
/**
* Specify the default revalidation behavior after this submission
*
* If no `shouldRevalidate` functions are present on the active routes, then this
* value will be used directly. Otherwise it will be passed into `shouldRevalidate`
* so the route can make the final determination on revalidation. This can be
* useful when updating search params and you don't want to trigger a revalidation.
*
* By default (when not specified), loaders will revalidate according to the routers
* standard revalidation behavior.
*/
defaultShouldRevalidate?: boolean;
}
/**
* Form props available to fetchers
* @category Types
*/
interface FetcherFormProps extends SharedFormProps {
}
/**
* Form props available to navigations
* @category Types
*/
interface FormProps extends SharedFormProps {
/**
* Defines the form [lazy route discovery](../../explanation/lazy-route-discovery) behavior.
*
* - **render** — default, discover the route when the form renders
* - **none** — don't eagerly discover, only discover if the form is submitted
*
* ```tsx
* <Form /> // default ("render")
* <Form discover="render" />
* <Form discover="none" />
* ```
*/
discover?: DiscoverBehavior;
/**
* Indicates a specific fetcherKey to use when using `navigate={false}` so you
* can pick up the fetcher's state in a different component in a {@link useFetcher}.
*/
fetcherKey?: string;
/**
* When `false`, skips the navigation and submits via a fetcher internally.
* This is essentially a shorthand for {@link useFetcher} + `<fetcher.Form>` where
* you don't care about the resulting data in this component.
*/
navigate?: boolean;
/**
* Forces a full document navigation instead of client side routing and data
* fetch.
*/
reloadDocument?: boolean;
/**
* Replaces the current entry in the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack when the form navigates. Use this if you don't want the user to be
* able to click "back" to the page with the form on it.
*/
replace?: boolean;
/**
* State object to add to the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack entry for this navigation
*/
state?: any;
/**
* Enables a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* for this navigation. To apply specific styles during the transition, see
* {@link useViewTransitionState}.
*/
viewTransition?: boolean;
}
/**
* A progressively enhanced HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
* that submits data to actions via [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch),
* activating pending states in {@link useNavigation} which enables advanced
* user interfaces beyond a basic HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form).
* After a form's `action` completes, all data on the page is automatically
* revalidated to keep the UI in sync with the data.
*
* Because it uses the HTML form API, server rendered pages are interactive at a
* basic level before JavaScript loads. Instead of React Router managing the
* submission, the browser manages the submission as well as the pending states
* (like the spinning favicon). After JavaScript loads, React Router takes over
* enabling web application user experiences.
*
* `Form` is most useful for submissions that should also change the URL or
* otherwise add an entry to the browser history stack. For forms that shouldn't
* manipulate the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack, use {@link FetcherWithComponents.Form | `<fetcher.Form>`}.
*
* @example
* import { Form } from "react-router";
*
* function NewEvent() {
* return (
* <Form action="/events" method="post">
* <input name="title" type="text" />
* <input name="description" type="text" />
* </Form>
* );
* }
*
* @public
* @category Components
* @mode framework
* @mode data
* @param {FormProps.action} action n/a
* @param {FormProps.discover} discover n/a
* @param {FormProps.encType} encType n/a
* @param {FormProps.fetcherKey} fetcherKey n/a
* @param {FormProps.method} method n/a
* @param {FormProps.navigate} navigate n/a
* @param {FormProps.onSubmit} onSubmit n/a
* @param {FormProps.preventScrollReset} preventScrollReset n/a
* @param {FormProps.relative} relative n/a
* @param {FormProps.reloadDocument} reloadDocument n/a
* @param {FormProps.replace} replace n/a
* @param {FormProps.state} state n/a
* @param {FormProps.viewTransition} viewTransition n/a
* @param {FormProps.defaultShouldRevalidate} defaultShouldRevalidate n/a
* @returns A progressively enhanced [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) component
*/
declare const Form: React.ForwardRefExoticComponent<FormProps & React.RefAttributes<HTMLFormElement>>;
type ScrollRestorationProps = ScriptsProps & {
/**
* A function that returns a key to use for scroll restoration. This is useful
* for custom scroll restoration logic, such as using only the pathname so
* that later navigations to prior paths will restore the scroll. Defaults to
* `location.key`. See {@link GetScrollRestorationKeyFunction}.
*
* ```tsx
* <ScrollRestoration
* getKey={(location, matches) => {
* // Restore based on a unique location key (default behavior)
* return location.key
*
* // Restore based on pathname
* return location.pathname
* }}
* />
* ```
*/
getKey?: GetScrollRestorationKeyFunction;
/**
* The key to use for storing scroll positions in [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage).
* Defaults to `"react-router-scroll-positions"`.
*/
storageKey?: string;
};
/**
* Emulates the browser's scroll restoration on location changes. Apps should only render one of these, right before the {@link Scripts} component.
*
* ```tsx
* import { ScrollRestoration } from "react-router";
*
* export default function Root() {
* return (
* <html>
* <body>
* <ScrollRestoration />
* <Scripts />
* </body>
* </html>
* );
* }
* ```
*
* This component renders an inline `<script>` to prevent scroll flashing. The
* `nonce` prop will be passed down to the script tag to allow CSP nonce usage.
* If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*
* ```tsx
* <ScrollRestoration nonce={cspNonce} />
* ```
*
* @public
* @category Components
* @mode framework
* @mode data
* @param props Props
* @param {ScrollRestorationProps.getKey} props.getKey n/a
* @param {ScriptsProps.nonce} props.nonce n/a
* @param {ScrollRestorationProps.storageKey} props.storageKey n/a
* @returns A [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tag that restores scroll positions on navigation.
*/
declare function ScrollRestoration({ getKey, storageKey, ...props }: ScrollRestorationProps): React.JSX.Element | null;
declare namespace ScrollRestoration {
var displayName: string;
}
/**
* Handles the click behavior for router {@link Link | `<Link>`} components.This
* is useful if you need to create custom {@link Link | `<Link>`} components with
* the same click behavior we use in our exported {@link Link | `<Link>`}.
*
* @public
* @category Hooks
* @param to The URL to navigate to, can be a string or a partial {@link Path}.
* @param options Options
* @param options.preventScrollReset Whether to prevent the scroll position from
* being reset to the top of the viewport on completion of the navigation when
* using the {@link ScrollRestoration} component. Defaults to `false`.
* @param options.relative The {@link RelativeRoutingType | relative routing type}
* to use for the link. Defaults to `"route"`.
* @param options.replace Whether to replace the current [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* entry instead of pushing a new one. Defaults to `false`.
* @param options.state The state to add to the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* entry for this navigation. Defaults to `undefined`.
* @param options.target The target attribute for the link. Defaults to `undefined`.
* @param options.viewTransition Enables a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* for this navigation. To apply specific styles during the transition, see
* {@link useViewTransitionState}. Defaults to `false`.
* @param options.defaultShouldRevalidate Specify the default revalidation
* behavior for the navigation. Defaults to `true`.
* @param options.mask Masked location to display in the browser instead
* of the router location. Defaults to `undefined`.
* @param options.useTransitions Wraps the navigation in
* [`React.startTransition`](https://react.dev/reference/react/startTransition)
* for concurrent rendering. Defaults to `false`.
* @returns A click handler function that can be used in a custom {@link Link} component.
*/
declare function useLinkClickHandler<E extends Element = HTMLAnchorElement>(to: To, { target, replace: replaceProp, mask, state, preventScrollReset, relative, viewTransition, defaultShouldRevalidate, useTransitions, }?: {
target?: React.HTMLAttributeAnchorTarget;
replace?: boolean;
mask?: To;
state?: any;
preventScrollReset?: boolean;
relative?: RelativeRoutingType;
viewTransition?: boolean;
defaultShouldRevalidate?: boolean;
useTransitions?: boolean;
}): (event: React.MouseEvent<E, MouseEvent>) => void;
/**
* Returns a tuple of the current URL's [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
* and a function to update them. Setting the search params causes a navigation.
*
* ```tsx
* import { useSearchParams } from "react-router";
*
* export function SomeComponent() {
* const [searchParams, setSearchParams] = useSearchParams();
* // ...
* }
* ```
*
* ### `setSearchParams` function
*
* The second element of the tuple is a function that can be used to update the
* search params. It accepts the same types as `defaultInit` and will cause a
* navigation to the new URL.
*
* ```tsx
* let [searchParams, setSearchParams] = useSearchParams();
*
* // a search param string
* setSearchParams("?tab=1");
*
* // a shorthand object
* setSearchParams({ tab: "1" });
*
* // object keys can be arrays for multiple values on the key
* setSearchParams({ brand: ["nike", "reebok"] });
*
* // an array of tuples
* setSearchParams([["tab", "1"]]);
*
* // a `URLSearchParams` object
* setSearchParams(new URLSearchParams("?tab=1"));
* ```
*
* It also supports a function callback like React's
* [`setState`](https://react.dev/reference/react/useState#setstate):
*
* ```tsx
* setSearchParams((searchParams) => {
* searchParams.set("tab", "2");
* return searchParams;
* });
* ```
*
* <docs-warning>The function callback version of `setSearchParams` does not support
* the [queueing](https://react.dev/reference/react/useState#setstate-parameters)
* logic that React's `setState` implements. Multiple calls to `setSearchParams`
* in the same tick will not build on the prior value. If you need this behavior,
* you can use `setState` manually.</docs-warning>
*
* ### Notes
*
* Note that `searchParams` is a stable reference, so you can reliably use it
* as a dependency in React's [`useEffect`](https://react.dev/reference/react/useEffect)
* hooks.
*
* ```tsx
* useEffect(() => {
* console.log(searchParams.get("tab"));
* }, [searchParams]);
* ```
*
* However, this also means it's mutable. If you change the object without
* calling `setSearchParams`, its values will change between renders if some
* other state causes the component to re-render and URL will not reflect the
* values.
*
* @public
* @category Hooks
* @param defaultInit
* You can initialize the search params with a default value, though it **will
* not** change the URL on the first render.
*
* ```tsx
* // a search param string
* useSearchParams("?tab=1");
*
* // a shorthand object
* useSearchParams({ tab: "1" });
*
* // object keys can be arrays for multiple values on the key
* useSearchParams({ brand: ["nike", "reebok"] });
*
* // an array of tuples
* useSearchParams([["tab", "1"]]);
*
* // a `URLSearchParams` object
* useSearchParams(new URLSearchParams("?tab=1"));
* ```
* @returns A tuple of the current [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
* and a function to update them.
*/
declare function useSearchParams(defaultInit?: URLSearchParamsInit): [URLSearchParams, SetURLSearchParams];
/**
* Sets new search params and causes a navigation when called.
*
* ```tsx
* <button
* onClick={() => {
* const params = new URLSearchParams();
* params.set("someKey", "someValue");
* setSearchParams(params, {
* preventScrollReset: true,
* });
* }}
* />
* ```
*
* It also supports a function for setting new search params.
*
* ```tsx
* <button
* onClick={() => {
* setSearchParams((prev) => {
* prev.set("someKey", "someValue");
* return prev;
* });
* }}
* />
* ```
*/
type SetURLSearchParams = (nextInit?: URLSearchParamsInit | ((prev: URLSearchParams) => URLSearchParamsInit), navigateOpts?: NavigateOptions) => void;
/**
* Submits a HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
* to the server without reloading the page.
*/
interface SubmitFunction {
(
/**
* Can be multiple types of elements and objects
*
* **[`HTMLFormElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement)**
*
* ```tsx
* <Form
* onSubmit={(event) => {
* submit(event.currentTarget);
* }}
* />
* ```
*
* **[`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)**
*
* ```tsx
* const formData = new FormData();
* formData.append("myKey", "myValue");
* submit(formData, { method: "post" });
* ```
*
* **Plain object that will be serialized as [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)**
*
* ```tsx
* submit({ myKey: "myValue" }, { method: "post" });
* ```
*
* **Plain object that will be serialized as JSON**
*
* ```tsx
* submit(
* { myKey: "myValue" },
* { method: "post", encType: "application/json" }
* );
* ```
*/
target: SubmitTarget,
/**
* Options that override the [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)'s
* own attributes. Required when submitting arbitrary data without a backing
* [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form).
*/
options?: SubmitOptions): Promise<void>;
}
/**
* Submits a fetcher [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) to the server without reloading the page.
*/
interface FetcherSubmitFunction {
(
/**
* Can be multiple types of elements and objects
*
* **[`HTMLFormElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement)**
*
* ```tsx
* <fetcher.Form
* onSubmit={(event) => {
* fetcher.submit(event.currentTarget);
* }}
* />
* ```
*
* **[`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)**
*
* ```tsx
* const formData = new FormData();
* formData.append("myKey", "myValue");
* fetcher.submit(formData, { method: "post" });
* ```
*
* **Plain object that will be serialized as [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)**
*
* ```tsx
* fetcher.submit({ myKey: "myValue" }, { method: "post" });
* ```
*
* **Plain object that will be serialized as JSON**
*
* ```tsx
* fetcher.submit(
* { myKey: "myValue" },
* { method: "post", encType: "application/json" }
* );
* ```
*/
target: SubmitTarget, options?: FetcherSubmitOptions): Promise<void>;
}
/**
* The imperative version of {@link Form | `<Form>`} that lets you submit a form
* from code instead of a user interaction.
*
* @example
* import { useSubmit } from "react-router";
*
* function SomeComponent() {
* const submit = useSubmit();
* return (
* <Form onChange={(event) => submit(event.currentTarget)} />
* );
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns A function that can be called to submit a {@link Form} imperatively.
*/
declare function useSubmit(): SubmitFunction;
/**
* Resolves the URL to the closest route in the component hierarchy instead of
* the current URL of the app.
*
* This is used internally by {@link Form} to resolve the `action` to the closest
* route, but can be used generically as well.
*
* ```ts
* import { useFormAction } from "react-router";
*
* function SomeComponent() {
* // closest route URL
* let action = useFormAction();
*
* // closest route URL + "destroy"
* let destroyAction = useFormAction("destroy");
* }
* ```
*
* <docs-info>This hook adds a `basename` if your app specifies one, so that it
* can be used with raw `<form>` elements in a progressively enhanced way. If
* you are using this to provide an `action` to `<Form>` or `fetcher.submit`, you
* will need to remove the `basename` since both of those will prepend it
* internally.</docs-info>
*
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param action The action to append to the closest route URL. Defaults to the
* closest route URL.
* @param options Options
* @param options.relative The relative routing type to use when resolving the
* action. Defaults to `"route"`.
* @returns The resolved action URL.
*/
declare function useFormAction(action?: string, { relative }?: {
relative?: RelativeRoutingType;
}): string;
/**
* The return value {@link useFetcher} that keeps track of the state of a fetcher.
*
* ```tsx
* let fetcher = useFetcher();
* ```
*/
type FetcherWithComponents<TData> = Fetcher<TData> & {
/**
* Just like {@link Form} except it doesn't cause a navigation.
*
* ```tsx
* function SomeComponent() {
* const fetcher = useFetcher()
* return (
* <fetcher.Form method="post" action="/some/route">
* <input type="text" />
* </fetcher.Form>
* )
* }
* ```
*/
Form: React.ForwardRefExoticComponent<FetcherFormProps & React.RefAttributes<HTMLFormElement>>;
/**
* Loads data from a route. Useful for loading data imperatively inside user
* events outside a normal button or form, like a combobox or search input.
*
* ```tsx
* let fetcher = useFetcher()
*
* <input onChange={e => {
* fetcher.load(`/search?q=${e.target.value}`)
* }} />
* ```
*/
load: (href: string, opts?: {
/**
* Wraps the initial state update for this `fetcher.load` in a
* [`ReactDOM.flushSync`](https://react.dev/reference/react-dom/flushSync)
* call instead of the default [`React.startTransition`](https://react.dev/reference/react/startTransition).
* This allows you to perform synchronous DOM actions immediately after the
* update is flushed to the DOM.
*/
flushSync?: boolean;
}) => Promise<void>;
/**
* Reset a fetcher back to an empty/idle state.
*
* If the fetcher is currently in-flight, the
* [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
* will be aborted with the `reason`, if provided.
* @param opts Options for resetting the fetcher.
* @param opts.reason Optional `reason` to provide to [`AbortController.abort()`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort)
* @returns void
*/
reset: (opts?: {
reason?: unknown;
}) => void;
/**
* Submits form data to a route. While multiple nested routes can match a URL, only the leaf route will be called.
*
* The `formData` can be multiple types:
*
* - [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
* A `FormData` instance.
* - [`HTMLFormElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement)
* A [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) DOM element.
* - `Object`
* An object of key/value-pairs that will be converted to a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
* instance by default. You can pass a more complex object and serialize it
* as JSON by specifying `encType: "application/json"`. See
* {@link useSubmit} for more details.
*
* If the method is `GET`, then the route [`loader`](../../start/framework/route-module#loader)
* is being called and with the `formData` serialized to the url as [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams).
* If `DELETE`, `PATCH`, `POST`, or `PUT`, then the route [`action`](../../start/framework/route-module#action)
* is being called with `formData` as the body.
*
* ```tsx
* // Submit a FormData instance (GET request)
* const formData = new FormData();
* fetcher.submit(formData);
*
* // Submit the HTML form element
* fetcher.submit(event.currentTarget.form, {
* method: "POST",
* });
*
* // Submit key/value JSON as a FormData instance
* fetcher.submit(
* { serialized: "values" },
* { method: "POST" }
* );
*
* // Submit raw JSON
* fetcher.submit(
* {
* deeply: {
* nested: {
* json: "values",
* },
* },
* },
* {
* method: "POST",
* encType: "application/json",
* }
* );
* ```
*/
submit: FetcherSubmitFunction;
};
/**
* Useful for creating complex, dynamic user interfaces that require multiple,
* concurrent data interactions without causing a navigation.
*
* Fetchers track their own, independent state and can be used to load data, submit
* forms, and generally interact with [`action`](../../start/framework/route-module#action)
* and [`loader`](../../start/framework/route-module#loader) functions.
*
* @example
* import { useFetcher } from "react-router"
*
* function SomeComponent() {
* let fetcher = useFetcher()
*
* // states are available on the fetcher
* fetcher.state // "idle" | "loading" | "submitting"
* fetcher.data // the data returned from the action or loader
*
* // render a form
* <fetcher.Form method="post" />
*
* // load data
* fetcher.load("/some/route")
*
* // submit data
* fetcher.submit(someFormRef, { method: "post" })
* fetcher.submit(someData, {
* method: "post",
* encType: "application/json"
* })
*
* // reset fetcher
* fetcher.reset()
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param options Options
* @param options.key A unique key to identify the fetcher.
*
*
* By default, `useFetcher` generates a unique fetcher scoped to that component.
* If you want to identify a fetcher with your own key such that you can access
* it from elsewhere in your app, you can do that with the `key` option:
*
* ```tsx
* function SomeComp() {
* let fetcher = useFetcher({ key: "my-key" })
* // ...
* }
*
* // Somewhere else
* function AnotherComp() {
* // this will be the same fetcher, sharing the state across the app
* let fetcher = useFetcher({ key: "my-key" });
* // ...
* }
* ```
* @returns A {@link FetcherWithComponents} object that contains the fetcher's state, data, and components for submitting forms and loading data.
*/
declare function useFetcher<T = any>({ key, }?: {
key?: string;
}): FetcherWithComponents<SerializeFrom<T>>;
/**
* Returns an array of all in-flight {@link Fetcher}s. This is useful for components
* throughout the app that didn't create the fetchers but want to use their submissions
* to participate in optimistic UI.
*
* @example
* import { useFetchers } from "react-router";
*
* function SomeComponent() {
* const fetchers = useFetchers();
* fetchers[0].formData; // FormData
* fetchers[0].state; // etc.
* // ...
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns An array of all in-flight {@link Fetcher}s, each with a unique `key`
* property.
*/
declare function useFetchers(): (Fetcher & {
key: string;
})[];
/**
* When rendered inside a {@link RouterProvider}, will restore scroll positions
* on navigations
*
* <!--
* Not marked `@public` because we only export as UNSAFE_ and therefore we don't
* maintain an .md file for this hook
* -->
*
* @name UNSAFE_useScrollRestoration
* @category Hooks
* @mode framework
* @mode data
* @param options Options
* @param options.getKey A function that returns a key to use for scroll restoration.
* This is useful for custom scroll restoration logic, such as using only the pathname
* so that subsequent navigations to prior paths will restore the scroll. Defaults
* to `location.key`.
* @param options.storageKey The key to use for storing scroll positions in
* `sessionStorage`. Defaults to `"react-router-scroll-positions"`.
* @returns {void}
*/
declare function useScrollRestoration({ getKey, storageKey, }?: {
getKey?: GetScrollRestorationKeyFunction;
storageKey?: string;
}): void;
/**
* Set up a callback to be fired on [Window's `beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event).
*
* @public
* @category Hooks
* @param callback The callback to be called when the [`beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event)
* is fired.
* @param options Options
* @param options.capture If `true`, the event will be captured during the capture
* phase. Defaults to `false`.
* @returns {void}
*/
declare function useBeforeUnload(callback: (event: BeforeUnloadEvent) => any, options?: {
capture?: boolean;
}): void;
/**
* Wrapper around {@link useBlocker} to show a [`window.confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
* prompt to users instead of building a custom UI with {@link useBlocker}.
*
* The `unstable_` flag will not be removed because this technique has a lot of
* rough edges and behaves very differently (and incorrectly sometimes) across
* browsers if users click addition back/forward navigations while the
* confirmation is open. Use at your own risk.
*
* @example
* function ImportantForm() {
* let [value, setValue] = React.useState("");
*
* // Block navigating elsewhere when data has been entered into the input
* unstable_usePrompt({
* message: "Are you sure?",
* when: ({ currentLocation, nextLocation }) =>
* value !== "" &&
* currentLocation.pathname !== nextLocation.pathname,
* });
*
* return (
* <Form method="post">
* <label>
* Enter some important data:
* <input
* name="data"
* value={value}
* onChange={(e) => setValue(e.target.value)}
* />
* </label>
* <button type="submit">Save</button>
* </Form>
* );
* }
*
* @name unstable_usePrompt
* @public
* @category Hooks
* @mode framework
* @mode data
* @param options Options
* @param options.message The message to show in the confirmation dialog.
* @param options.when A boolean or a function that returns a boolean indicating
* whether to block the navigation. If a function is provided, it will receive an
* object with `currentLocation` and `nextLocation` properties.
* @returns {void}
*/
declare function usePrompt({ when, message, }: {
when: boolean | BlockerFunction;
message: string;
}): void;
/**
* This hook returns `true` when there is an active [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* and the specified location matches either side of the navigation (the URL you are
* navigating **to** or the URL you are navigating **from**). This can be used to apply finer-grained styles to
* elements to further customize the view transition. This requires that view
* transitions have been enabled for the given navigation via {@link LinkProps.viewTransition}
* (or the `Form`, `submit`, or `navigate` call)
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param to The {@link To} location to compare against the active transition's current
* and next URLs.
* @param options Options
* @param options.relative The relative routing type to use when resolving the
* `to` location, defaults to `"route"`. See {@link RelativeRoutingType} for
* more details.
* @returns `true` if there is an active [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* and the resolved path matches the transition's destination or source pathname, otherwise `false`.
*/
declare function useViewTransitionState(to: To, { relative }?: {
relative?: RelativeRoutingType;
}): boolean;
/**
* @category Types
*/
interface StaticRouterProps {
/**
* The base URL for the static router (default: `/`)
*/
basename?: string;
/**
* The child elements to render inside the static router
*/
children?: React.ReactNode;
/**
* The {@link Location} to render the static router at (default: `/`)
*/
location: Partial<Location> | string;
}
/**
* A {@link Router | `<Router>`} that may not navigate to any other {@link Location}.
* This is useful on the server where there is no stateful UI.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {StaticRouterProps.basename} props.basename n/a
* @param {StaticRouterProps.children} props.children n/a
* @param {StaticRouterProps.location} props.location n/a
* @returns A React element that renders the static {@link Router | `<Router>`}
*/
declare function StaticRouter({ basename, children, location: locationProp, }: StaticRouterProps): React.JSX.Element;
/**
* @category Types
*/
interface StaticRouterProviderProps {
/**
* The {@link StaticHandlerContext} returned from {@link StaticHandler}'s
* `query`
*/
context: StaticHandlerContext;
/**
* The static {@link DataRouter} from {@link createStaticRouter}
*/
router: Router$1;
/**
* Whether to hydrate the router on the client (default `true`)
*/
hydrate?: boolean;
/**
* The [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* to use for the hydration [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tag
*/
nonce?: string;
}
/**
* A {@link DataRouter} that may not navigate to any other {@link Location}.
* This is useful on the server where there is no stateful UI.
*
* @example
* export async function handleRequest(request: Request) {
* let { query, dataRoutes } = createStaticHandler(routes);
* let context = await query(request));
*
* if (context instanceof Response) {
* return context;
* }
*
* let router = createStaticRouter(dataRoutes, context);
* return new Response(
* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
* { headers: { "Content-Type": "text/html" } }
* );
* }
*
* @public
* @category Data Routers
* @mode data
* @param props Props
* @param {StaticRouterProviderProps.context} props.context n/a
* @param {StaticRouterProviderProps.hydrate} props.hydrate n/a
* @param {StaticRouterProviderProps.nonce} props.nonce n/a
* @param {StaticRouterProviderProps.router} props.router n/a
* @returns A React element that renders the static router provider
*/
declare function StaticRouterProvider({ context, router, hydrate, nonce, }: StaticRouterProviderProps): React.JSX.Element;
type CreateStaticHandlerOptions = Omit<CreateStaticHandlerOptions$1, "mapRouteProperties">;
/**
* Create a static handler to perform server-side data loading
*
* @example
* export async function handleRequest(request: Request) {
* let { query, dataRoutes } = createStaticHandler(routes);
* let context = await query(request);
*
* if (context instanceof Response) {
* return context;
* }
*
* let router = createStaticRouter(dataRoutes, context);
* return new Response(
* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
* { headers: { "Content-Type": "text/html" } }
* );
* }
*
* @public
* @category Data Routers
* @mode data
* @param routes The {@link RouteObject | route objects} to create a static
* handler for
* @param opts Options
* @param opts.basename The base URL for the static handler (default: `/`)
* @param opts.future Future flags for the static handler
* @returns A static handler that can be used to query data for the provided
* routes
*/
declare function createStaticHandler(routes: RouteObject[], opts?: CreateStaticHandlerOptions): StaticHandler;
/**
* Create a static {@link DataRouter} for server-side rendering
*
* @example
* export async function handleRequest(request: Request) {
* let { query, dataRoutes } = createStaticHandler(routes);
* let context = await query(request);
*
* if (context instanceof Response) {
* return context;
* }
*
* let router = createStaticRouter(dataRoutes, context);
* return new Response(
* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
* { headers: { "Content-Type": "text/html" } }
* );
* }
*
* @public
* @category Data Routers
* @mode data
* @param routes The route objects to create a static {@link DataRouter} for
* @param context The {@link StaticHandlerContext} returned from {@link StaticHandler}'s
* `query`
* @param opts Options
* @param opts.future Future flags for the static {@link DataRouter}
* @param opts.branches Optional pre-computed route branches
* @returns A static {@link DataRouter} that can be used to render the provided routes
*/
declare function createStaticRouter(routes: RouteObject[], context: StaticHandlerContext, opts?: {
branches?: RouteBranch<DataRouteObject>[];
future?: Partial<FutureConfig$1>;
}): Router$1;
export { Form as $, type AssetsManifest as A, type BrowserRouterProps as B, type ClientOnErrorFunction as C, type DOMRouterOpts as D, type EntryContext as E, type FutureConfig as F, type ScrollRestorationProps as G, type HashRouterProps as H, type IndexRouteProps as I, type SetURLSearchParams as J, type SubmitFunction as K, type LayoutRouteProps as L, type MemoryRouterOpts as M, type NavigateOptions as N, type OutletProps as O, type PathRouteProps as P, type FetcherSubmitFunction as Q, type RouteProps as R, type ServerBuild as S, type FetcherWithComponents as T, createBrowserRouter as U, createHashRouter as V, BrowserRouter as W, HashRouter as X, Link as Y, HistoryRouter as Z, NavLink as _, type Navigator as a, ScrollRestoration as a0, useLinkClickHandler as a1, useSearchParams as a2, useSubmit as a3, useFormAction as a4, useFetcher as a5, useFetchers as a6, useBeforeUnload as a7, usePrompt as a8, useViewTransitionState as a9, FetchersContext as aA, LocationContext as aB, NavigationContext as aC, RouteContext as aD, ViewTransitionContext as aE, hydrationRouteProperties as aF, mapRouteProperties as aG, WithComponentProps as aH, withComponentProps as aI, WithHydrateFallbackProps as aJ, withHydrateFallbackProps as aK, WithErrorBoundaryProps as aL, withErrorBoundaryProps as aM, FrameworkContext as aN, createClientRoutes as aO, createClientRoutesWithHMRRevalidationOptOut as aP, shouldHydrateRouteLoader as aQ, useScrollRestoration as aR, type FetcherSubmitOptions as aa, type ParamKeyValuePair as ab, type SubmitOptions as ac, type URLSearchParamsInit as ad, type SubmitTarget as ae, createSearchParams as af, type StaticRouterProps as ag, type StaticRouterProviderProps as ah, createStaticHandler as ai, createStaticRouter as aj, StaticRouter as ak, StaticRouterProvider as al, Meta as am, Links as an, Scripts as ao, PrefetchPageLinks as ap, type LinksProps as aq, type ScriptsProps as ar, type PrefetchBehavior as as, type DiscoverBehavior as at, type HandleDataRequestFunction as au, type HandleDocumentRequestFunction as av, type HandleErrorFunction as aw, type ServerEntryModule as ax, DataRouterContext as ay, DataRouterStateContext as az, AwaitContextProvider as b, type AwaitProps as c, type MemoryRouterProps as d, type NavigateProps as e, type RouterProps as f, type RouterProviderProps as g, type RoutesProps as h, Await as i, MemoryRouter as j, Navigate as k, Outlet as l, Route as m, Router as n, RouterProvider as o, Routes as p, createMemoryRouter as q, createRoutesFromChildren as r, createRoutesFromElements as s, renderMatches as t, type HistoryRouterProps as u, type LinkProps as v, type NavLinkProps as w, type NavLinkRenderProps as x, type FetcherFormProps as y, type FormProps as z };
@@ -0,0 +1,2614 @@
import { o as ServerInstrumentation, H as HydrationState, h as StaticHandlerContext, f as RelativeRoutingType, g as GetScrollRestorationKeyFunction, R as RouterInit, ah as FutureConfig$1, C as ClientInstrumentation, N as NavigateOptions, F as Fetcher, c as Router, B as BlockerFunction, ai as CreateStaticHandlerOptions$1, S as StaticHandler } from './context-CeD5LmaF.mjs';
import * as React from 'react';
import { J as RouteManifest, aF as ServerRouteModule, p as MiddlewareEnabled, c as RouterContextProvider, q as AppLoadContext, o as LoaderFunctionArgs, a3 as ActionFunctionArgs, Z as RouteModules, E as DataRouteObject, a as ClientLoaderFunction, I as RouteBranch, m as HTMLFormMethod, n as FormEncType, aw as PageLinkDescriptor, T as To, s as History, z as DataStrategyFunction, B as PatchRoutesOnNavigationFunction, r as RouteObject, _ as SerializeFrom, L as Location } from './data-DEjBmEfD.mjs';
type ServerRouteManifest = RouteManifest<Omit<ServerRoute, "children">>;
interface ServerRoute extends Route {
children: ServerRoute[];
module: ServerRouteModule;
}
type OptionalCriticalCss = CriticalCss | undefined;
/**
* The output of the compiler for the server build.
*/
interface ServerBuild {
entry: {
module: ServerEntryModule;
};
routes: ServerRouteManifest;
assets: AssetsManifest;
basename?: string;
publicPath: string;
assetsBuildDirectory: string;
future: FutureConfig;
ssr: boolean;
unstable_getCriticalCss?: (args: {
pathname: string;
}) => OptionalCriticalCss | Promise<OptionalCriticalCss>;
/**
* @deprecated This is now done via a custom header during prerendering
*/
isSpaMode: boolean;
prerender: string[];
routeDiscovery: {
mode: "lazy" | "initial";
manifestPath: string;
};
allowedActionOrigins?: string[] | false;
}
interface HandleDocumentRequestFunction {
(request: Request, responseStatusCode: number, responseHeaders: Headers, context: EntryContext, loadContext: MiddlewareEnabled extends true ? RouterContextProvider : AppLoadContext): Promise<Response> | Response;
}
interface HandleDataRequestFunction {
(response: Response, args: {
request: LoaderFunctionArgs["request"] | ActionFunctionArgs["request"];
context: LoaderFunctionArgs["context"] | ActionFunctionArgs["context"];
params: LoaderFunctionArgs["params"] | ActionFunctionArgs["params"];
}): Promise<Response> | Response;
}
interface HandleErrorFunction {
(error: unknown, args: {
request: LoaderFunctionArgs["request"] | ActionFunctionArgs["request"];
context: LoaderFunctionArgs["context"] | ActionFunctionArgs["context"];
params: LoaderFunctionArgs["params"] | ActionFunctionArgs["params"];
}): void;
}
/**
* A module that serves as the entry point for a Remix app during server
* rendering.
*/
interface ServerEntryModule {
default: HandleDocumentRequestFunction;
handleDataRequest?: HandleDataRequestFunction;
handleError?: HandleErrorFunction;
instrumentations?: ServerInstrumentation[];
streamTimeout?: number;
}
interface Route {
index?: boolean;
caseSensitive?: boolean;
id: string;
parentId?: string;
path?: string;
}
interface EntryRoute extends Route {
hasAction: boolean;
hasLoader: boolean;
hasClientAction: boolean;
hasClientLoader: boolean;
hasClientMiddleware: boolean;
hasErrorBoundary: boolean;
imports?: string[];
css?: string[];
module: string;
clientActionModule: string | undefined;
clientLoaderModule: string | undefined;
clientMiddlewareModule: string | undefined;
hydrateFallbackModule: string | undefined;
parentId?: string;
}
declare function createClientRoutesWithHMRRevalidationOptOut(needsRevalidation: Set<string>, manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState, ssr: boolean, isSpaMode: boolean): DataRouteObject[];
declare function createClientRoutes(manifest: RouteManifest<EntryRoute>, routeModulesCache: RouteModules, initialState: HydrationState | null, ssr: boolean, isSpaMode: boolean, parentId?: string, routesByParentId?: Record<string, Omit<EntryRoute, "children">[]>, needsRevalidation?: Set<string>): DataRouteObject[];
declare function shouldHydrateRouteLoader(routeId: string, clientLoader: ClientLoaderFunction | undefined, hasLoader: boolean, isSpaMode: boolean): boolean;
type SerializedError = {
message: string;
stack?: string;
};
interface FrameworkContextObject {
manifest: AssetsManifest;
routeModules: RouteModules;
criticalCss?: CriticalCss;
serverHandoffString?: string;
future: FutureConfig;
ssr: boolean;
isSpaMode: boolean;
routeDiscovery: ServerBuild["routeDiscovery"];
nonce?: string;
serializeError?(error: Error): SerializedError;
renderMeta?: {
didRenderScripts?: boolean;
streamCache?: Record<number, Promise<void> & {
result?: {
done: boolean;
value: string;
};
error?: unknown;
}>;
};
}
interface EntryContext extends FrameworkContextObject {
branches: RouteBranch<DataRouteObject>[];
staticHandlerContext: StaticHandlerContext;
serverHandoffStream?: ReadableStream<Uint8Array>;
}
interface FutureConfig {
v8_passThroughRequests: boolean;
v8_trailingSlashAwareDataRequests: boolean;
v8_middleware: boolean;
}
type CriticalCss = string | {
rel: "stylesheet";
href: string;
};
interface AssetsManifest {
entry: {
imports: string[];
module: string;
};
routes: RouteManifest<EntryRoute>;
url: string;
version: string;
hmr?: {
timestamp?: number;
runtime: string;
};
sri?: Record<string, string> | true;
}
type ParamKeyValuePair = [string, string];
type URLSearchParamsInit = string | ParamKeyValuePair[] | Record<string, string | string[]> | URLSearchParams;
/**
Creates a URLSearchParams object using the given initializer.
This is identical to `new URLSearchParams(init)` except it also
supports arrays as values in the object form of the initializer
instead of just strings. This is convenient when you need multiple
values for a given key, but don't want to use an array initializer.
For example, instead of:
```tsx
let searchParams = new URLSearchParams([
['sort', 'name'],
['sort', 'price']
]);
```
you can do:
```
let searchParams = createSearchParams({
sort: ['name', 'price']
});
```
@category Utils
*/
declare function createSearchParams(init?: URLSearchParamsInit): URLSearchParams;
type JsonObject = {
[Key in string]: JsonValue;
} & {
[Key in string]?: JsonValue | undefined;
};
type JsonArray = JsonValue[] | readonly JsonValue[];
type JsonPrimitive = string | number | boolean | null;
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
type SubmitTarget = HTMLFormElement | HTMLButtonElement | HTMLInputElement | FormData | URLSearchParams | JsonValue | null;
/**
* Submit options shared by both navigations and fetchers
*/
interface SharedSubmitOptions {
/**
* The HTTP method used to submit the form. Overrides `<form method>`.
* Defaults to "GET".
*/
method?: HTMLFormMethod;
/**
* The action URL path used to submit the form. Overrides `<form action>`.
* Defaults to the path of the current route.
*/
action?: string;
/**
* The encoding used to submit the form. Overrides `<form encType>`.
* Defaults to "application/x-www-form-urlencoded".
*/
encType?: FormEncType;
/**
* Determines whether the form action is relative to the route hierarchy or
* the pathname. Use this if you want to opt out of navigating the route
* hierarchy and want to instead route based on /-delimited URL segments
*/
relative?: RelativeRoutingType;
/**
* In browser-based environments, prevent resetting scroll after this
* navigation when using the <ScrollRestoration> component
*/
preventScrollReset?: boolean;
/**
* Enable flushSync for this submission's state updates
*/
flushSync?: boolean;
/**
* Specify the default revalidation behavior after this submission
*
* If no `shouldRevalidate` functions are present on the active routes, then this
* value will be used directly. Otherwise it will be passed into `shouldRevalidate`
* so the route can make the final determination on revalidation. This can be
* useful when updating search params and you don't want to trigger a revalidation.
*
* By default (when not specified), loaders will revalidate according to the routers
* standard revalidation behavior.
*/
defaultShouldRevalidate?: boolean;
}
/**
* Submit options available to fetchers
*/
interface FetcherSubmitOptions extends SharedSubmitOptions {
}
/**
* Submit options available to navigations
*/
interface SubmitOptions extends FetcherSubmitOptions {
/**
* Set `true` to replace the current entry in the browser's history stack
* instead of creating a new one (i.e. stay on "the same page"). Defaults
* to `false`.
*/
replace?: boolean;
/**
* State object to add to the history stack entry for this navigation
*/
state?: any;
/**
* Indicate a specific fetcherKey to use when using navigate=false
*/
fetcherKey?: string;
/**
* navigate=false will use a fetcher instead of a navigation
*/
navigate?: boolean;
/**
* Enable view transitions on this submission navigation
*/
viewTransition?: boolean;
}
declare const FrameworkContext: React.Context<FrameworkContextObject | undefined>;
/**
* Defines the [lazy route discovery](../../explanation/lazy-route-discovery)
* behavior of the link/form:
*
* - "render" - default, discover the route when the link renders
* - "none" - don't eagerly discover, only discover if the link is clicked
*/
type DiscoverBehavior = "render" | "none";
/**
* Defines the prefetching behavior of the link:
*
* - "none": Never fetched
* - "intent": Fetched when the user focuses or hovers the link
* - "render": Fetched when the link is rendered
* - "viewport": Fetched when the link is in the viewport
*/
type PrefetchBehavior = "intent" | "render" | "none" | "viewport";
/**
* Props for the {@link Links} component.
*
* @category Types
*/
interface LinksProps {
/**
* A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* element. If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*/
nonce?: string | undefined;
/**
* A [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/HTML/Attributes/crossorigin)
* attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* element
*/
crossOrigin?: "anonymous" | "use-credentials";
}
/**
* Renders all the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags created by the route module's [`links`](../../start/framework/route-module#links)
* export. You should render it inside the [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
* of your document.
*
* @example
* import { Links } from "react-router";
*
* export default function Root() {
* return (
* <html>
* <head>
* <Links />
* </head>
* <body></body>
* </html>
* );
* }
*
* @public
* @category Components
* @mode framework
* @param props Props
* @param {LinksProps.nonce} props.nonce n/a
* @param {LinksProps.crossOrigin} props.crossOrigin n/a
* @returns A collection of React elements for [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags
*/
declare function Links({ nonce, crossOrigin }: LinksProps): React.JSX.Element;
/**
* Renders [`<link rel=prefetch|modulepreload>`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/rel)
* tags for modules and data of another page to enable an instant navigation to
* that page. [`<Link prefetch>`](./Link#prefetch) uses this internally, but you
* can render it to prefetch a page for any other reason.
*
* For example, you may render one of this as the user types into a search field
* to prefetch search results before they click through to their selection.
*
* @example
* import { PrefetchPageLinks } from "react-router";
*
* <PrefetchPageLinks page="/absolute/path" />
*
* @public
* @category Components
* @mode framework
* @param props Props
* @param {PageLinkDescriptor.page} props.page n/a
* @param props.linkProps Additional props to spread onto the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags, such as [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/crossOrigin),
* [`integrity`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/integrity),
* [`rel`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLLinkElement/rel),
* etc.
* @returns A collection of React elements for [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags
*/
declare function PrefetchPageLinks({ page, ...linkProps }: PageLinkDescriptor): React.JSX.Element | null;
/**
* Renders all the [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
* tags created by the route module's [`meta`](../../start/framework/route-module#meta)
* export. You should render it inside the [`<head>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/head)
* of your document.
*
* @example
* import { Meta } from "react-router";
*
* export default function Root() {
* return (
* <html>
* <head>
* <Meta />
* </head>
* </html>
* );
* }
*
* @public
* @category Components
* @mode framework
* @returns A collection of React elements for [`<meta>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/meta)
* tags
*/
declare function Meta(): React.JSX.Element;
/**
* A couple common attributes:
*
* - `<Scripts crossOrigin>` for hosting your static assets on a different
* server than your app.
* - `<Scripts nonce>` to support a [content security policy for scripts](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/script-src)
* with [nonce-sources](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Content-Security-Policy/Sources#sources)
* for your [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tags.
*
* You cannot pass through attributes such as [`async`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/async),
* [`defer`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/defer),
* [`noModule`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/noModule),
* [`src`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/src),
* or [`type`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/type),
* because they are managed by React Router internally.
*
* @category Types
*/
type ScriptsProps = Omit<React.HTMLProps<HTMLScriptElement>, "async" | "children" | "dangerouslySetInnerHTML" | "defer" | "noModule" | "src" | "suppressHydrationWarning" | "type"> & {
/**
* A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* attribute to render on the [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* element. If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*/
nonce?: string | undefined;
};
/**
* Renders the client runtime of your app. It should be rendered inside the
* [`<body>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/body)
* of the document.
*
* If server rendering, you can omit `<Scripts/>` and the app will work as a
* traditional web app without JavaScript, relying solely on HTML and browser
* behaviors.
*
* @example
* import { Scripts } from "react-router";
*
* export default function Root() {
* return (
* <html>
* <head />
* <body>
* <Scripts />
* </body>
* </html>
* );
* }
*
* @public
* @category Components
* @mode framework
* @param scriptProps Additional props to spread onto the [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tags, such as [`crossOrigin`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLScriptElement/crossOrigin),
* [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce),
* etc.
* @returns A collection of React elements for [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tags
*/
declare function Scripts(scriptProps: ScriptsProps): React.JSX.Element | null;
/**
* @category Data Routers
*/
interface DOMRouterOpts {
/**
* Basename path for the application.
*/
basename?: string;
/**
* A function that returns an {@link RouterContextProvider} instance
* which is provided as the `context` argument to client [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* This function is called to generate a fresh `context` instance on each
* navigation or fetcher call.
*
* ```tsx
* import {
* createContext,
* RouterContextProvider,
* } from "react-router";
*
* const apiClientContext = createContext<APIClient>();
*
* function createBrowserRouter(routes, {
* getContext() {
* let context = new RouterContextProvider();
* context.set(apiClientContext, getApiClient());
* return context;
* }
* })
* ```
*/
getContext?: RouterInit["getContext"];
/**
* Future flags to enable for the router.
*/
future?: Partial<FutureConfig$1>;
/**
* When Server-Rendering and opting-out of automatic hydration, the
* `hydrationData` option allows you to pass in hydration data from your
* server-render. This will almost always be a subset of data from the
* {@link StaticHandlerContext} value you get back from the {@link StaticHandler}'s
* `query` method:
*
* ```tsx
* const router = createBrowserRouter(routes, {
* hydrationData: {
* loaderData: {
* // [routeId]: serverLoaderData
* },
* // may also include `errors` and/or `actionData`
* },
* });
* ```
*
* **Partial Hydration Data**
*
* You will almost always include a complete set of `loaderData` to hydrate a
* server-rendered app. But in advanced use-cases (such as Framework Mode's
* [`clientLoader`](../../start/framework/route-module#clientLoader)), you may
* want to include `loaderData` for only some routes that were loaded/rendered
* on the server. This allows you to hydrate _some_ of the routes (such as the
* app layout/shell) while showing a `HydrateFallback` component and running
* the [`loader`](../../start/data/route-object#loader)s for other routes
* during hydration.
*
* A route [`loader`](../../start/data/route-object#loader) will run during
* hydration in two scenarios:
*
* 1. No hydration data is provided
* In these cases the `HydrateFallback` component will render on initial
* hydration
* 2. The `loader.hydrate` property is set to `true`
* This allows you to run the [`loader`](../../start/data/route-object#loader)
* even if you did not render a fallback on initial hydration (i.e., to
* prime a cache with hydration data)
*
* ```tsx
* const router = createBrowserRouter(
* [
* {
* id: "root",
* loader: rootLoader,
* Component: Root,
* children: [
* {
* id: "index",
* loader: indexLoader,
* HydrateFallback: IndexSkeleton,
* Component: Index,
* },
* ],
* },
* ],
* {
* hydrationData: {
* loaderData: {
* root: "ROOT DATA",
* // No index data provided
* },
* },
* }
* );
* ```
*/
hydrationData?: HydrationState;
/**
* Array of instrumentation objects allowing you to instrument the router and
* individual routes prior to router initialization (and on any subsequently
* added routes via `route.lazy` or `patchRoutesOnNavigation`). This is
* mostly useful for observability such as wrapping navigations, fetches,
* as well as route loaders/actions/middlewares with logging and/or performance
* tracing. See the [docs](../../how-to/instrumentation) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* instrumentations: [logging]
* });
*
*
* let logging = {
* router({ instrument }) {
* instrument({
* navigate: (impl, info) => logExecution(`navigate ${info.to}`, impl),
* fetch: (impl, info) => logExecution(`fetch ${info.to}`, impl)
* });
* },
* route({ instrument, id }) {
* instrument({
* middleware: (impl, info) => logExecution(
* `middleware ${info.request.url} (route ${id})`,
* impl
* ),
* loader: (impl, info) => logExecution(
* `loader ${info.request.url} (route ${id})`,
* impl
* ),
* action: (impl, info) => logExecution(
* `action ${info.request.url} (route ${id})`,
* impl
* ),
* })
* }
* };
*
* async function logExecution(label: string, impl: () => Promise<void>) {
* let start = performance.now();
* console.log(`start ${label}`);
* await impl();
* let duration = Math.round(performance.now() - start);
* console.log(`end ${label} (${duration}ms)`);
* }
* ```
*/
instrumentations?: ClientInstrumentation[];
/**
* Override the default data strategy of running loaders in parallel -
* see the [docs](../../how-to/data-strategy) for more information.
*
* ```tsx
* let router = createBrowserRouter(routes, {
* async dataStrategy({
* matches,
* request,
* runClientMiddleware,
* }) {
* const matchesToLoad = matches.filter((m) =>
* m.shouldCallHandler(),
* );
*
* const results: Record<string, DataStrategyResult> = {};
* await runClientMiddleware(() =>
* Promise.all(
* matchesToLoad.map(async (match) => {
* results[match.route.id] = await match.resolve();
* }),
* ),
* );
* return results;
* },
* });
* ```
*/
dataStrategy?: DataStrategyFunction;
/**
* Lazily define portions of the route tree on navigations.
* See {@link PatchRoutesOnNavigationFunction}.
*
* By default, React Router wants you to provide a full route tree up front via
* `createBrowserRouter(routes)`. This allows React Router to perform synchronous
* route matching, execute loaders, and then render route components in the most
* optimistic manner without introducing waterfalls. The tradeoff is that your
* initial JS bundle is larger by definition — which may slow down application
* start-up times as your application grows.
*
* To combat this, we introduced [`route.lazy`](../../start/data/route-object#lazy)
* in [v6.9.0](https://github.com/remix-run/react-router/blob/main/CHANGELOG.md#v690)
* which lets you lazily load the route _implementation_ ([`loader`](../../start/data/route-object#loader),
* [`Component`](../../start/data/route-object#Component), etc.) while still
* providing the route _definition_ aspects up front (`path`, `index`, etc.).
* This is a good middle ground. React Router still knows about your route
* definitions (the lightweight part) up front and can perform synchronous
* route matching, but then delay loading any of the route implementation
* aspects (the heavier part) until the route is actually navigated to.
*
* In some cases, even this doesn't go far enough. For huge applications,
* providing all route definitions up front can be prohibitively expensive.
* Additionally, it might not even be possible to provide all route definitions
* up front in certain Micro-Frontend or Module-Federation architectures.
*
* This is where `patchRoutesOnNavigation` comes in ([RFC](https://github.com/remix-run/react-router/discussions/11113)).
* This API is for advanced use-cases where you are unable to provide the full
* route tree up-front and need a way to lazily "discover" portions of the route
* tree at runtime. This feature is often referred to as ["Fog of War"](https://en.wikipedia.org/wiki/Fog_of_war),
* because similar to how video games expand the "world" as you move around -
* the router would be expanding its routing tree as the user navigated around
* the app - but would only ever end up loading portions of the tree that the
* user visited.
*
* `patchRoutesOnNavigation` will be called anytime React Router is unable to
* match a `path`. The arguments include the `path`, any partial `matches`,
* and a `patch` function you can call to patch new routes into the tree at a
* specific location. This method is executed during the `loading` portion of
* the navigation for `GET` requests and during the `submitting` portion of
* the navigation for non-`GET` requests.
*
* <details>
* <summary><b>Example <code>patchRoutesOnNavigation</code> Use Cases</b></summary>
*
* **Patching children into an existing route**
*
* ```tsx
* const router = createBrowserRouter(
* [
* {
* id: "root",
* path: "/",
* Component: RootComponent,
* },
* ],
* {
* async patchRoutesOnNavigation({ patch, path }) {
* if (path === "/a") {
* // Load/patch the `a` route as a child of the route with id `root`
* let route = await getARoute();
* // ^ { path: 'a', Component: A }
* patch("root", [route]);
* }
* },
* }
* );
* ```
*
* In the above example, if the user clicks a link to `/a`, React Router
* won't match any routes initially and will call `patchRoutesOnNavigation`
* with a `path = "/a"` and a `matches` array containing the root route
* match. By calling `patch('root', [route])`, the new route will be added
* to the route tree as a child of the `root` route and React Router will
* perform matching on the updated routes. This time it will successfully
* match the `/a` path and the navigation will complete successfully.
*
* **Patching new root-level routes**
*
* If you need to patch a new route to the top of the tree (i.e., it doesn't
* have a parent), you can pass `null` as the `routeId`:
*
* ```tsx
* const router = createBrowserRouter(
* [
* {
* id: "root",
* path: "/",
* Component: RootComponent,
* },
* ],
* {
* async patchRoutesOnNavigation({ patch, path }) {
* if (path === "/root-sibling") {
* // Load/patch the `/root-sibling` route as a sibling of the root route
* let route = await getRootSiblingRoute();
* // ^ { path: '/root-sibling', Component: RootSibling }
* patch(null, [route]);
* }
* },
* }
* );
* ```
*
* **Patching subtrees asynchronously**
*
* You can also perform asynchronous matching to lazily fetch entire sections
* of your application:
*
* ```tsx
* let router = createBrowserRouter(
* [
* {
* path: "/",
* Component: Home,
* },
* ],
* {
* async patchRoutesOnNavigation({ patch, path }) {
* if (path.startsWith("/dashboard")) {
* let children = await import("./dashboard");
* patch(null, children);
* }
* if (path.startsWith("/account")) {
* let children = await import("./account");
* patch(null, children);
* }
* },
* }
* );
* ```
*
* <docs-info>If in-progress execution of `patchRoutesOnNavigation` is
* interrupted by a later navigation, then any remaining `patch` calls in
* the interrupted execution will not update the route tree because the
* operation was cancelled.</docs-info>
*
* **Co-locating route discovery with route definition**
*
* If you don't wish to perform your own pseudo-matching, you can leverage
* the partial `matches` array and the [`handle`](../../start/data/route-object#handle)
* field on a route to keep the children definitions co-located:
*
* ```tsx
* let router = createBrowserRouter(
* [
* {
* path: "/",
* Component: Home,
* },
* {
* path: "/dashboard",
* children: [
* {
* // If we want to include /dashboard in the critical routes, we need to
* // also include it's index route since patchRoutesOnNavigation will not be
* // called on a navigation to `/dashboard` because it will have successfully
* // matched the `/dashboard` parent route
* index: true,
* // ...
* },
* ],
* handle: {
* lazyChildren: () => import("./dashboard"),
* },
* },
* {
* path: "/account",
* children: [
* {
* index: true,
* // ...
* },
* ],
* handle: {
* lazyChildren: () => import("./account"),
* },
* },
* ],
* {
* async patchRoutesOnNavigation({ matches, patch }) {
* let leafRoute = matches[matches.length - 1]?.route;
* if (leafRoute?.handle?.lazyChildren) {
* let children =
* await leafRoute.handle.lazyChildren();
* patch(leafRoute.id, children);
* }
* },
* }
* );
* ```
*
* **A note on routes with parameters**
*
* Because React Router uses ranked routes to find the best match for a
* given path, there is an interesting ambiguity introduced when only a
* partial route tree is known at any given point in time. If we match a
* fully static route such as `path: "/about/contact-us"` then we know we've
* found the right match since it's composed entirely of static URL segments.
* Thus, we do not need to bother asking for any other potentially
* higher-scoring routes.
*
* However, routes with parameters (dynamic or splat) can't make this
* assumption because there might be a not-yet-discovered route that scores
* higher. Consider a full route tree such as:
*
* ```tsx
* // Assume this is the full route tree for your app
* const routes = [
* {
* path: "/",
* Component: Home,
* },
* {
* id: "blog",
* path: "/blog",
* Component: BlogLayout,
* children: [
* { path: "new", Component: NewPost },
* { path: ":slug", Component: BlogPost },
* ],
* },
* ];
* ```
*
* And then assume we want to use `patchRoutesOnNavigation` to fill this in
* as the user navigates around:
*
* ```tsx
* // Start with only the index route
* const router = createBrowserRouter(
* [
* {
* path: "/",
* Component: Home,
* },
* ],
* {
* async patchRoutesOnNavigation({ patch, path }) {
* if (path === "/blog/new") {
* patch("blog", [
* {
* path: "new",
* Component: NewPost,
* },
* ]);
* } else if (path.startsWith("/blog")) {
* patch("blog", [
* {
* path: ":slug",
* Component: BlogPost,
* },
* ]);
* }
* },
* }
* );
* ```
*
* If the user were to a blog post first (i.e., `/blog/my-post`) we would
* patch in the `:slug` route. Then, if the user navigated to `/blog/new` to
* write a new post, we'd match `/blog/:slug` but it wouldn't be the _right_
* match! We need to call `patchRoutesOnNavigation` just in case there
* exists a higher-scoring route we've not yet discovered, which in this
* case there is.
*
* So, anytime React Router matches a path that contains at least one param,
* it will call `patchRoutesOnNavigation` and match routes again just to
* confirm it has found the best match.
*
* If your `patchRoutesOnNavigation` implementation is expensive or making
* side effect [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch)
* calls to a backend server, you may want to consider tracking previously
* seen routes to avoid over-fetching in cases where you know the proper
* route has already been found. This can usually be as simple as
* maintaining a small cache of prior `path` values for which you've already
* patched in the right routes:
*
* ```tsx
* let discoveredRoutes = new Set();
*
* const router = createBrowserRouter(routes, {
* async patchRoutesOnNavigation({ patch, path }) {
* if (discoveredRoutes.has(path)) {
* // We've seen this before so nothing to patch in and we can let the router
* // use the routes it already knows about
* return;
* }
*
* discoveredRoutes.add(path);
*
* // ... patch routes in accordingly
* },
* });
* ```
* </details>
*/
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
/**
* [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object
* override. Defaults to the global `window` instance.
*/
window?: Window;
}
/**
* Create a new {@link DataRouter| data router} that manages the application
* path via [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)
* and [`history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState).
*
* Data Routers should not be held in React state. You should create your router
* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
*
* @public
* @category Data Routers
* @mode data
* @param routes Application routes
* @param opts Options
* @param {DOMRouterOpts.basename} opts.basename n/a
* @param {DOMRouterOpts.dataStrategy} opts.dataStrategy n/a
* @param {DOMRouterOpts.future} opts.future n/a
* @param {DOMRouterOpts.getContext} opts.getContext n/a
* @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a
* @param {DOMRouterOpts.instrumentations} opts.instrumentations n/a
* @param {DOMRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a
* @param {DOMRouterOpts.window} opts.window n/a
* @returns An initialized {@link DataRouter| data router} to pass to {@link RouterProvider | `<RouterProvider>`}
*/
declare function createBrowserRouter(routes: RouteObject[], opts?: DOMRouterOpts): Router;
/**
* Create a new {@link DataRouter| data router} that manages the application
* path via the URL [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash).
*
* Data Routers should not be held in React state. You should create your router
* once outside of the React tree and pass it to {@link RouterProvider | `<RouterProvider>`}.
* You can use `patchRoutesOnNavigation` to add additional routes programmatically.
*
* @public
* @category Data Routers
* @mode data
* @param routes Application routes
* @param opts Options
* @param {DOMRouterOpts.basename} opts.basename n/a
* @param {DOMRouterOpts.future} opts.future n/a
* @param {DOMRouterOpts.getContext} opts.getContext n/a
* @param {DOMRouterOpts.hydrationData} opts.hydrationData n/a
* @param {DOMRouterOpts.instrumentations} opts.instrumentations n/a
* @param {DOMRouterOpts.dataStrategy} opts.dataStrategy n/a
* @param {DOMRouterOpts.patchRoutesOnNavigation} opts.patchRoutesOnNavigation n/a
* @param {DOMRouterOpts.window} opts.window n/a
* @returns An initialized {@link DataRouter| data router} to pass to {@link RouterProvider | `<RouterProvider>`}
*/
declare function createHashRouter(routes: RouteObject[], opts?: DOMRouterOpts): Router;
/**
* @category Types
*/
interface BrowserRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* {@link Route | `<Route>`} components describing your route configuration
*/
children?: React.ReactNode;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
/**
* [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object
* override. Defaults to the global `window` instance
*/
window?: Window;
}
/**
* A declarative {@link Router | `<Router>`} using the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* API for client-side routing.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {BrowserRouterProps.basename} props.basename n/a
* @param {BrowserRouterProps.children} props.children n/a
* @param {BrowserRouterProps.useTransitions} props.useTransitions n/a
* @param {BrowserRouterProps.window} props.window n/a
* @returns A declarative {@link Router | `<Router>`} using the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* API for client-side routing.
*/
declare function BrowserRouter({ basename, children, useTransitions, window, }: BrowserRouterProps): React.JSX.Element;
/**
* @category Types
*/
interface HashRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* {@link Route | `<Route>`} components describing your route configuration
*/
children?: React.ReactNode;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
/**
* [`Window`](https://developer.mozilla.org/en-US/docs/Web/API/Window) object
* override. Defaults to the global `window` instance
*/
window?: Window;
}
/**
* A declarative {@link Router | `<Router>`} that stores the location in the
* [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash) portion
* of the URL so it is not sent to the server.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {HashRouterProps.basename} props.basename n/a
* @param {HashRouterProps.children} props.children n/a
* @param {HashRouterProps.useTransitions} props.useTransitions n/a
* @param {HashRouterProps.window} props.window n/a
* @returns A declarative {@link Router | `<Router>`} using the URL [`hash`](https://developer.mozilla.org/en-US/docs/Web/API/URL/hash)
* for client-side routing.
*/
declare function HashRouter({ basename, children, useTransitions, window, }: HashRouterProps): React.JSX.Element;
/**
* @category Types
*/
interface HistoryRouterProps {
/**
* Application basename
*/
basename?: string;
/**
* {@link Route | `<Route>`} components describing your route configuration
*/
children?: React.ReactNode;
/**
* A {@link History} implementation for use by the router
*/
history: History;
/**
* Control whether router state updates are internally wrapped in
* [`React.startTransition`](https://react.dev/reference/react/startTransition).
*
* - When left `undefined`, all router state updates are wrapped in
* `React.startTransition`
* - When set to `true`, {@link Link} and {@link Form} navigations will be wrapped
* in `React.startTransition` and all router state updates are wrapped in
* `React.startTransition`
* - When set to `false`, the router will not leverage `React.startTransition`
* on any navigations or state changes.
*
* For more information, please see the [docs](../../explanation/react-transitions).
*/
useTransitions?: boolean;
}
/**
* A declarative {@link Router | `<Router>`} that accepts a pre-instantiated
* `history` object.
* It's important to note that using your own `history` object is highly discouraged
* and may add two versions of the `history` library to your bundles unless you use
* the same version of the `history` library that React Router uses internally.
*
* @name unstable_HistoryRouter
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {HistoryRouterProps.basename} props.basename n/a
* @param {HistoryRouterProps.children} props.children n/a
* @param {HistoryRouterProps.history} props.history n/a
* @param {HistoryRouterProps.useTransitions} props.useTransitions n/a
* @returns A declarative {@link Router | `<Router>`} using the provided history
* implementation for client-side routing.
*/
declare function HistoryRouter({ basename, children, history, useTransitions, }: HistoryRouterProps): React.JSX.Element;
declare namespace HistoryRouter {
var displayName: string;
}
/**
* @category Types
*/
interface LinkProps extends Omit<React.AnchorHTMLAttributes<HTMLAnchorElement>, "href"> {
/**
* Defines the link [lazy route discovery](../../explanation/lazy-route-discovery) behavior.
*
* - **render** — default, discover the route when the link renders
* - **none** — don't eagerly discover, only discover if the link is clicked
*
* ```tsx
* <Link /> // default ("render")
* <Link discover="render" />
* <Link discover="none" />
* ```
*/
discover?: DiscoverBehavior;
/**
* Defines the data and module prefetching behavior for the link.
*
* ```tsx
* <Link /> // default
* <Link prefetch="none" />
* <Link prefetch="intent" />
* <Link prefetch="render" />
* <Link prefetch="viewport" />
* ```
*
* - **none** — default, no prefetching
* - **intent** — prefetches when the user hovers or focuses the link
* - **render** — prefetches when the link renders
* - **viewport** — prefetches when the link is in the viewport, very useful for mobile
*
* Prefetching is done with HTML [`<link rel="prefetch">`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* tags. They are inserted after the link.
*
* ```tsx
* <a href="..." />
* <a href="..." />
* <link rel="prefetch" /> // might conditionally render
* ```
*
* Because of this, if you are using `nav :last-child` you will need to use
* `nav :last-of-type` so the styles don't conditionally fall off your last link
* (and any other similar selectors).
*/
prefetch?: PrefetchBehavior;
/**
* Will use document navigation instead of client side routing when the link is
* clicked: the browser will handle the transition normally (as if it were an
* [`<a href>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)).
*
* ```tsx
* <Link to="/logout" reloadDocument />
* ```
*/
reloadDocument?: boolean;
/**
* Replaces the current entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack instead of pushing a new one onto it.
*
* ```tsx
* <Link replace />
* ```
*
* ```
* # with a history stack like this
* A -> B
*
* # normal link click pushes a new entry
* A -> B -> C
*
* # but with `replace`, B is replaced by C
* A -> C
* ```
*/
replace?: boolean;
/**
* Adds persistent client side routing state to the next location.
*
* ```tsx
* <Link to="/somewhere/else" state={{ some: "value" }} />
* ```
*
* The location state is accessed from the `location`.
*
* ```tsx
* function SomeComp() {
* const location = useLocation();
* location.state; // { some: "value" }
* }
* ```
*
* This state is inaccessible on the server as it is implemented on top of
* [`history.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state)
*/
state?: any;
/**
* Prevents the scroll position from being reset to the top of the window when
* the link is clicked and the app is using {@link ScrollRestoration}. This only
* prevents new locations resetting scroll to the top, scroll position will be
* restored for back/forward button navigation.
*
* ```tsx
* <Link to="?tab=one" preventScrollReset />
* ```
*/
preventScrollReset?: boolean;
/**
* Defines the relative path behavior for the link.
*
* ```tsx
* <Link to=".." /> // default: "route"
* <Link relative="route" />
* <Link relative="path" />
* ```
*
* Consider a route hierarchy where a parent route pattern is `"blog"` and a child
* route pattern is `"blog/:slug/edit"`.
*
* - **route** — default, resolves the link relative to the route pattern. In the
* example above, a relative link of `"..."` will remove both `:slug/edit` segments
* back to `"/blog"`.
* - **path** — relative to the path so `"..."` will only remove one URL segment up
* to `"/blog/:slug"`
*
* Note that index routes and layout routes do not have paths so they are not
* included in the relative path calculation.
*/
relative?: RelativeRoutingType;
/**
* Can be a string or a partial {@link Path}:
*
* ```tsx
* <Link to="/some/path" />
*
* <Link
* to={{
* pathname: "/some/path",
* search: "?query=string",
* hash: "#hash",
* }}
* />
* ```
*/
to: To;
/**
* Enables a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* for this navigation.
*
* ```jsx
* <Link to={to} viewTransition>
* Click me
* </Link>
* ```
*
* To apply specific styles for the transition, see {@link useViewTransitionState}
*/
viewTransition?: boolean;
/**
* Specify the default revalidation behavior for the navigation.
*
* ```tsx
* <Link to="/some/path" defaultShouldRevalidate={false} />
* ```
*
* If no `shouldRevalidate` functions are present on the active routes, then this
* value will be used directly. Otherwise it will be passed into `shouldRevalidate`
* so the route can make the final determination on revalidation. This can be
* useful when updating search params and you don't want to trigger a revalidation.
*
* By default (when not specified), loaders will revalidate according to the routers
* standard revalidation behavior.
*/
defaultShouldRevalidate?: boolean;
/**
* Masked path for this navigation, when you want to navigate the router to
* one location but display a separate location in the URL bar.
*
* This is useful for contextual navigations such as opening an image in a modal
* on top of a gallery while keeping the underlying gallery active. If a user
* shares the masked URL, or opens the link in a new tab, they will only load
* the masked location without the underlying contextual location.
*
* This feature relies on `history.state` and is thus only intended for SPA uses
* and SSR renders will not respect the masking.
*
* ```tsx
* // routes/gallery.tsx
* export function clientLoader({ request }: Route.LoaderArgs) {
* let sp = new URL(request.url).searchParams;
* return {
* images: getImages(),
* modalImage: sp.has("image") ? getImage(sp.get("image")!) : null,
* };
* }
*
* export default function Gallery({ loaderData }: Route.ComponentProps) {
* return (
* <>
* <GalleryGrid>
* {loaderData.images.map((image) => (
* <Link
* key={image.id}
* to={`/gallery?image=${image.id}`}
* mask={`/images/${image.id}`}
* >
* <img src={image.url} alt={image.alt} />
* </Link>
* ))}
* </GalleryGrid>
*
* {data.modalImage ? (
* <dialog open>
* <img src={data.modalImage.url} alt={data.modalImage.alt} />
* </dialog>
* ) : null}
* </>
* );
* }
* ```
*/
mask?: To;
}
/**
* A progressively enhanced [`<a href>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/a)
* wrapper to enable navigation with client-side routing.
*
* @example
* import { Link } from "react-router";
*
* <Link to="/dashboard">Dashboard</Link>;
*
* <Link
* to={{
* pathname: "/some/path",
* search: "?query=string",
* hash: "#hash",
* }}
* />;
*
* @public
* @category Components
* @param {LinkProps.discover} props.discover [modes: framework] n/a
* @param {LinkProps.prefetch} props.prefetch [modes: framework] n/a
* @param {LinkProps.preventScrollReset} props.preventScrollReset [modes: framework, data] n/a
* @param {LinkProps.relative} props.relative n/a
* @param {LinkProps.reloadDocument} props.reloadDocument n/a
* @param {LinkProps.replace} props.replace n/a
* @param {LinkProps.state} props.state n/a
* @param {LinkProps.to} props.to n/a
* @param {LinkProps.viewTransition} props.viewTransition [modes: framework, data] n/a
* @param {LinkProps.defaultShouldRevalidate} props.defaultShouldRevalidate n/a
* @param {LinkProps.mask} props.mask [modes: framework, data] n/a
*/
declare const Link: React.ForwardRefExoticComponent<LinkProps & React.RefAttributes<HTMLAnchorElement>>;
/**
* The object passed to {@link NavLink} `children`, `className`, and `style` prop
* callbacks to render and style the link based on its state.
*
* ```
* // className
* <NavLink
* to="/messages"
* className={({ isActive, isPending }) =>
* isPending ? "pending" : isActive ? "active" : ""
* }
* >
* Messages
* </NavLink>
*
* // style
* <NavLink
* to="/messages"
* style={({ isActive, isPending }) => {
* return {
* fontWeight: isActive ? "bold" : "",
* color: isPending ? "red" : "black",
* }
* )}
* />
*
* // children
* <NavLink to="/tasks">
* {({ isActive, isPending }) => (
* <span className={isActive ? "active" : ""}>Tasks</span>
* )}
* </NavLink>
* ```
*
*/
type NavLinkRenderProps = {
/**
* Indicates if the link's URL matches the current {@link Location}.
*/
isActive: boolean;
/**
* Indicates if the pending {@link Location} matches the link's URL. Only
* available in Framework/Data modes.
*/
isPending: boolean;
/**
* Indicates if a view transition to the link's URL is in progress.
* See {@link useViewTransitionState}
*/
isTransitioning: boolean;
};
/**
* @category Types
*/
interface NavLinkProps extends Omit<LinkProps, "className" | "style" | "children"> {
/**
* Can be regular React children or a function that receives an object with the
* `active` and `pending` states of the link.
*
* ```tsx
* <NavLink to="/tasks">
* {({ isActive }) => (
* <span className={isActive ? "active" : ""}>Tasks</span>
* )}
* </NavLink>
* ```
*/
children?: React.ReactNode | ((props: NavLinkRenderProps) => React.ReactNode);
/**
* Changes the matching logic to make it case-sensitive:
*
* | Link | URL | isActive |
* | -------------------------------------------- | ------------- | -------- |
* | `<NavLink to="/SpOnGe-bOB" />` | `/sponge-bob` | true |
* | `<NavLink to="/SpOnGe-bOB" caseSensitive />` | `/sponge-bob` | false |
*/
caseSensitive?: boolean;
/**
* Classes are automatically applied to `NavLink` that correspond to the state.
*
* ```css
* a.active {
* color: red;
* }
* a.pending {
* color: blue;
* }
* a.transitioning {
* view-transition-name: my-transition;
* }
* ```
*
* Or you can specify a function that receives {@link NavLinkRenderProps} and
* returns the `className`:
*
* ```tsx
* <NavLink className={({ isActive, isPending }) => (
* isActive ? "my-active-class" :
* isPending ? "my-pending-class" :
* ""
* )} />
* ```
*/
className?: string | ((props: NavLinkRenderProps) => string | undefined);
/**
* Changes the matching logic for the `active` and `pending` states to only match
* to the "end" of the {@link NavLinkProps.to}. If the URL is longer, it will no
* longer be considered active.
*
* | Link | URL | isActive |
* | ----------------------------- | ------------ | -------- |
* | `<NavLink to="/tasks" />` | `/tasks` | true |
* | `<NavLink to="/tasks" />` | `/tasks/123` | true |
* | `<NavLink to="/tasks" end />` | `/tasks` | true |
* | `<NavLink to="/tasks" end />` | `/tasks/123` | false |
*
* `<NavLink to="/">` is an exceptional case because _every_ URL matches `/`.
* To avoid this matching every single route by default, it effectively ignores
* the `end` prop and only matches when you're at the root route.
*/
end?: boolean;
/**
* Styles can also be applied dynamically via a function that receives
* {@link NavLinkRenderProps} and returns the styles:
*
* ```tsx
* <NavLink to="/tasks" style={{ color: "red" }} />
* <NavLink to="/tasks" style={({ isActive, isPending }) => ({
* color:
* isActive ? "red" :
* isPending ? "blue" : "black"
* })} />
* ```
*/
style?: React.CSSProperties | ((props: NavLinkRenderProps) => React.CSSProperties | undefined);
}
/**
* Wraps {@link Link | `<Link>`} with additional props for styling active and
* pending states.
*
* - Automatically applies classes to the link based on its `active` and `pending`
* states, see {@link NavLinkProps.className}
* - Note that `pending` is only available with Framework and Data modes.
* - Automatically applies `aria-current="page"` to the link when the link is active.
* See [`aria-current`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/Attributes/aria-current)
* on MDN.
* - States are additionally available through the className, style, and children
* render props. See {@link NavLinkRenderProps}.
*
* @example
* <NavLink to="/message">Messages</NavLink>
*
* // Using render props
* <NavLink
* to="/messages"
* className={({ isActive, isPending }) =>
* isPending ? "pending" : isActive ? "active" : ""
* }
* >
* Messages
* </NavLink>
*
* @public
* @category Components
* @param {NavLinkProps.caseSensitive} props.caseSensitive n/a
* @param {NavLinkProps.children} props.children n/a
* @param {NavLinkProps.className} props.className n/a
* @param {NavLinkProps.discover} props.discover [modes: framework] n/a
* @param {NavLinkProps.end} props.end n/a
* @param {NavLinkProps.prefetch} props.prefetch [modes: framework] n/a
* @param {NavLinkProps.preventScrollReset} props.preventScrollReset [modes: framework, data] n/a
* @param {NavLinkProps.relative} props.relative n/a
* @param {NavLinkProps.reloadDocument} props.reloadDocument n/a
* @param {NavLinkProps.replace} props.replace n/a
* @param {NavLinkProps.state} props.state n/a
* @param {NavLinkProps.style} props.style n/a
* @param {NavLinkProps.to} props.to n/a
* @param {NavLinkProps.viewTransition} props.viewTransition [modes: framework, data] n/a
*/
declare const NavLink: React.ForwardRefExoticComponent<NavLinkProps & React.RefAttributes<HTMLAnchorElement>>;
/**
* Form props shared by navigations and fetchers
*/
interface SharedFormProps extends React.FormHTMLAttributes<HTMLFormElement> {
/**
* The HTTP verb to use when the form is submitted. Supports `"delete"`,
* `"get"`, `"patch"`, `"post"`, and `"put"`.
*
* Native [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
* only supports `"get"` and `"post"`, avoid the other verbs if you'd like to
* support progressive enhancement
*/
method?: HTMLFormMethod;
/**
* The encoding type to use for the form submission.
*
* ```tsx
* <Form encType="application/x-www-form-urlencoded"/> // Default
* <Form encType="multipart/form-data"/>
* <Form encType="text/plain"/>
* ```
*/
encType?: "application/x-www-form-urlencoded" | "multipart/form-data" | "text/plain";
/**
* The URL to submit the form data to. If `undefined`, this defaults to the
* closest route in context.
*/
action?: string;
/**
* Determines whether the form action is relative to the route hierarchy or
* the pathname. Use this if you want to opt out of navigating the route
* hierarchy and want to instead route based on slash-delimited URL segments.
* See {@link RelativeRoutingType}.
*/
relative?: RelativeRoutingType;
/**
* Prevent the scroll position from resetting to the top of the viewport on
* completion of the navigation when using the
* {@link ScrollRestoration | `<ScrollRestoration>`} component
*/
preventScrollReset?: boolean;
/**
* A function to call when the form is submitted. If you call
* [`event.preventDefault()`](https://developer.mozilla.org/en-US/docs/Web/API/Event/preventDefault)
* then this form will not do anything.
*/
onSubmit?: React.FormEventHandler<HTMLFormElement>;
/**
* Specify the default revalidation behavior after this submission
*
* If no `shouldRevalidate` functions are present on the active routes, then this
* value will be used directly. Otherwise it will be passed into `shouldRevalidate`
* so the route can make the final determination on revalidation. This can be
* useful when updating search params and you don't want to trigger a revalidation.
*
* By default (when not specified), loaders will revalidate according to the routers
* standard revalidation behavior.
*/
defaultShouldRevalidate?: boolean;
}
/**
* Form props available to fetchers
* @category Types
*/
interface FetcherFormProps extends SharedFormProps {
}
/**
* Form props available to navigations
* @category Types
*/
interface FormProps extends SharedFormProps {
/**
* Defines the form [lazy route discovery](../../explanation/lazy-route-discovery) behavior.
*
* - **render** — default, discover the route when the form renders
* - **none** — don't eagerly discover, only discover if the form is submitted
*
* ```tsx
* <Form /> // default ("render")
* <Form discover="render" />
* <Form discover="none" />
* ```
*/
discover?: DiscoverBehavior;
/**
* Indicates a specific fetcherKey to use when using `navigate={false}` so you
* can pick up the fetcher's state in a different component in a {@link useFetcher}.
*/
fetcherKey?: string;
/**
* When `false`, skips the navigation and submits via a fetcher internally.
* This is essentially a shorthand for {@link useFetcher} + `<fetcher.Form>` where
* you don't care about the resulting data in this component.
*/
navigate?: boolean;
/**
* Forces a full document navigation instead of client side routing and data
* fetch.
*/
reloadDocument?: boolean;
/**
* Replaces the current entry in the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack when the form navigates. Use this if you don't want the user to be
* able to click "back" to the page with the form on it.
*/
replace?: boolean;
/**
* State object to add to the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack entry for this navigation
*/
state?: any;
/**
* Enables a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* for this navigation. To apply specific styles during the transition, see
* {@link useViewTransitionState}.
*/
viewTransition?: boolean;
}
/**
* A progressively enhanced HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
* that submits data to actions via [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/fetch),
* activating pending states in {@link useNavigation} which enables advanced
* user interfaces beyond a basic HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form).
* After a form's `action` completes, all data on the page is automatically
* revalidated to keep the UI in sync with the data.
*
* Because it uses the HTML form API, server rendered pages are interactive at a
* basic level before JavaScript loads. Instead of React Router managing the
* submission, the browser manages the submission as well as the pending states
* (like the spinning favicon). After JavaScript loads, React Router takes over
* enabling web application user experiences.
*
* `Form` is most useful for submissions that should also change the URL or
* otherwise add an entry to the browser history stack. For forms that shouldn't
* manipulate the browser [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack, use {@link FetcherWithComponents.Form | `<fetcher.Form>`}.
*
* @example
* import { Form } from "react-router";
*
* function NewEvent() {
* return (
* <Form action="/events" method="post">
* <input name="title" type="text" />
* <input name="description" type="text" />
* </Form>
* );
* }
*
* @public
* @category Components
* @mode framework
* @mode data
* @param {FormProps.action} action n/a
* @param {FormProps.discover} discover n/a
* @param {FormProps.encType} encType n/a
* @param {FormProps.fetcherKey} fetcherKey n/a
* @param {FormProps.method} method n/a
* @param {FormProps.navigate} navigate n/a
* @param {FormProps.onSubmit} onSubmit n/a
* @param {FormProps.preventScrollReset} preventScrollReset n/a
* @param {FormProps.relative} relative n/a
* @param {FormProps.reloadDocument} reloadDocument n/a
* @param {FormProps.replace} replace n/a
* @param {FormProps.state} state n/a
* @param {FormProps.viewTransition} viewTransition n/a
* @param {FormProps.defaultShouldRevalidate} defaultShouldRevalidate n/a
* @returns A progressively enhanced [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) component
*/
declare const Form: React.ForwardRefExoticComponent<FormProps & React.RefAttributes<HTMLFormElement>>;
type ScrollRestorationProps = ScriptsProps & {
/**
* A function that returns a key to use for scroll restoration. This is useful
* for custom scroll restoration logic, such as using only the pathname so
* that later navigations to prior paths will restore the scroll. Defaults to
* `location.key`. See {@link GetScrollRestorationKeyFunction}.
*
* ```tsx
* <ScrollRestoration
* getKey={(location, matches) => {
* // Restore based on a unique location key (default behavior)
* return location.key
*
* // Restore based on pathname
* return location.pathname
* }}
* />
* ```
*/
getKey?: GetScrollRestorationKeyFunction;
/**
* The key to use for storing scroll positions in [`sessionStorage`](https://developer.mozilla.org/en-US/docs/Web/API/Window/sessionStorage).
* Defaults to `"react-router-scroll-positions"`.
*/
storageKey?: string;
};
/**
* Emulates the browser's scroll restoration on location changes. Apps should only render one of these, right before the {@link Scripts} component.
*
* ```tsx
* import { ScrollRestoration } from "react-router";
*
* export default function Root() {
* return (
* <html>
* <body>
* <ScrollRestoration />
* <Scripts />
* </body>
* </html>
* );
* }
* ```
*
* This component renders an inline `<script>` to prevent scroll flashing. The
* `nonce` prop will be passed down to the script tag to allow CSP nonce usage.
* If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*
* ```tsx
* <ScrollRestoration nonce={cspNonce} />
* ```
*
* @public
* @category Components
* @mode framework
* @mode data
* @param props Props
* @param {ScrollRestorationProps.getKey} props.getKey n/a
* @param {ScriptsProps.nonce} props.nonce n/a
* @param {ScrollRestorationProps.storageKey} props.storageKey n/a
* @returns A [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tag that restores scroll positions on navigation.
*/
declare function ScrollRestoration({ getKey, storageKey, ...props }: ScrollRestorationProps): React.JSX.Element | null;
declare namespace ScrollRestoration {
var displayName: string;
}
/**
* Handles the click behavior for router {@link Link | `<Link>`} components.This
* is useful if you need to create custom {@link Link | `<Link>`} components with
* the same click behavior we use in our exported {@link Link | `<Link>`}.
*
* @public
* @category Hooks
* @param to The URL to navigate to, can be a string or a partial {@link Path}.
* @param options Options
* @param options.preventScrollReset Whether to prevent the scroll position from
* being reset to the top of the viewport on completion of the navigation when
* using the {@link ScrollRestoration} component. Defaults to `false`.
* @param options.relative The {@link RelativeRoutingType | relative routing type}
* to use for the link. Defaults to `"route"`.
* @param options.replace Whether to replace the current [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* entry instead of pushing a new one. Defaults to `false`.
* @param options.state The state to add to the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* entry for this navigation. Defaults to `undefined`.
* @param options.target The target attribute for the link. Defaults to `undefined`.
* @param options.viewTransition Enables a [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* for this navigation. To apply specific styles during the transition, see
* {@link useViewTransitionState}. Defaults to `false`.
* @param options.defaultShouldRevalidate Specify the default revalidation
* behavior for the navigation. Defaults to `true`.
* @param options.mask Masked location to display in the browser instead
* of the router location. Defaults to `undefined`.
* @param options.useTransitions Wraps the navigation in
* [`React.startTransition`](https://react.dev/reference/react/startTransition)
* for concurrent rendering. Defaults to `false`.
* @returns A click handler function that can be used in a custom {@link Link} component.
*/
declare function useLinkClickHandler<E extends Element = HTMLAnchorElement>(to: To, { target, replace: replaceProp, mask, state, preventScrollReset, relative, viewTransition, defaultShouldRevalidate, useTransitions, }?: {
target?: React.HTMLAttributeAnchorTarget;
replace?: boolean;
mask?: To;
state?: any;
preventScrollReset?: boolean;
relative?: RelativeRoutingType;
viewTransition?: boolean;
defaultShouldRevalidate?: boolean;
useTransitions?: boolean;
}): (event: React.MouseEvent<E, MouseEvent>) => void;
/**
* Returns a tuple of the current URL's [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
* and a function to update them. Setting the search params causes a navigation.
*
* ```tsx
* import { useSearchParams } from "react-router";
*
* export function SomeComponent() {
* const [searchParams, setSearchParams] = useSearchParams();
* // ...
* }
* ```
*
* ### `setSearchParams` function
*
* The second element of the tuple is a function that can be used to update the
* search params. It accepts the same types as `defaultInit` and will cause a
* navigation to the new URL.
*
* ```tsx
* let [searchParams, setSearchParams] = useSearchParams();
*
* // a search param string
* setSearchParams("?tab=1");
*
* // a shorthand object
* setSearchParams({ tab: "1" });
*
* // object keys can be arrays for multiple values on the key
* setSearchParams({ brand: ["nike", "reebok"] });
*
* // an array of tuples
* setSearchParams([["tab", "1"]]);
*
* // a `URLSearchParams` object
* setSearchParams(new URLSearchParams("?tab=1"));
* ```
*
* It also supports a function callback like React's
* [`setState`](https://react.dev/reference/react/useState#setstate):
*
* ```tsx
* setSearchParams((searchParams) => {
* searchParams.set("tab", "2");
* return searchParams;
* });
* ```
*
* <docs-warning>The function callback version of `setSearchParams` does not support
* the [queueing](https://react.dev/reference/react/useState#setstate-parameters)
* logic that React's `setState` implements. Multiple calls to `setSearchParams`
* in the same tick will not build on the prior value. If you need this behavior,
* you can use `setState` manually.</docs-warning>
*
* ### Notes
*
* Note that `searchParams` is a stable reference, so you can reliably use it
* as a dependency in React's [`useEffect`](https://react.dev/reference/react/useEffect)
* hooks.
*
* ```tsx
* useEffect(() => {
* console.log(searchParams.get("tab"));
* }, [searchParams]);
* ```
*
* However, this also means it's mutable. If you change the object without
* calling `setSearchParams`, its values will change between renders if some
* other state causes the component to re-render and URL will not reflect the
* values.
*
* @public
* @category Hooks
* @param defaultInit
* You can initialize the search params with a default value, though it **will
* not** change the URL on the first render.
*
* ```tsx
* // a search param string
* useSearchParams("?tab=1");
*
* // a shorthand object
* useSearchParams({ tab: "1" });
*
* // object keys can be arrays for multiple values on the key
* useSearchParams({ brand: ["nike", "reebok"] });
*
* // an array of tuples
* useSearchParams([["tab", "1"]]);
*
* // a `URLSearchParams` object
* useSearchParams(new URLSearchParams("?tab=1"));
* ```
* @returns A tuple of the current [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams)
* and a function to update them.
*/
declare function useSearchParams(defaultInit?: URLSearchParamsInit): [URLSearchParams, SetURLSearchParams];
/**
* Sets new search params and causes a navigation when called.
*
* ```tsx
* <button
* onClick={() => {
* const params = new URLSearchParams();
* params.set("someKey", "someValue");
* setSearchParams(params, {
* preventScrollReset: true,
* });
* }}
* />
* ```
*
* It also supports a function for setting new search params.
*
* ```tsx
* <button
* onClick={() => {
* setSearchParams((prev) => {
* prev.set("someKey", "someValue");
* return prev;
* });
* }}
* />
* ```
*/
type SetURLSearchParams = (nextInit?: URLSearchParamsInit | ((prev: URLSearchParams) => URLSearchParamsInit), navigateOpts?: NavigateOptions) => void;
/**
* Submits a HTML [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)
* to the server without reloading the page.
*/
interface SubmitFunction {
(
/**
* Can be multiple types of elements and objects
*
* **[`HTMLFormElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement)**
*
* ```tsx
* <Form
* onSubmit={(event) => {
* submit(event.currentTarget);
* }}
* />
* ```
*
* **[`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)**
*
* ```tsx
* const formData = new FormData();
* formData.append("myKey", "myValue");
* submit(formData, { method: "post" });
* ```
*
* **Plain object that will be serialized as [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)**
*
* ```tsx
* submit({ myKey: "myValue" }, { method: "post" });
* ```
*
* **Plain object that will be serialized as JSON**
*
* ```tsx
* submit(
* { myKey: "myValue" },
* { method: "post", encType: "application/json" }
* );
* ```
*/
target: SubmitTarget,
/**
* Options that override the [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form)'s
* own attributes. Required when submitting arbitrary data without a backing
* [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form).
*/
options?: SubmitOptions): Promise<void>;
}
/**
* Submits a fetcher [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) to the server without reloading the page.
*/
interface FetcherSubmitFunction {
(
/**
* Can be multiple types of elements and objects
*
* **[`HTMLFormElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement)**
*
* ```tsx
* <fetcher.Form
* onSubmit={(event) => {
* fetcher.submit(event.currentTarget);
* }}
* />
* ```
*
* **[`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)**
*
* ```tsx
* const formData = new FormData();
* formData.append("myKey", "myValue");
* fetcher.submit(formData, { method: "post" });
* ```
*
* **Plain object that will be serialized as [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)**
*
* ```tsx
* fetcher.submit({ myKey: "myValue" }, { method: "post" });
* ```
*
* **Plain object that will be serialized as JSON**
*
* ```tsx
* fetcher.submit(
* { myKey: "myValue" },
* { method: "post", encType: "application/json" }
* );
* ```
*/
target: SubmitTarget, options?: FetcherSubmitOptions): Promise<void>;
}
/**
* The imperative version of {@link Form | `<Form>`} that lets you submit a form
* from code instead of a user interaction.
*
* @example
* import { useSubmit } from "react-router";
*
* function SomeComponent() {
* const submit = useSubmit();
* return (
* <Form onChange={(event) => submit(event.currentTarget)} />
* );
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns A function that can be called to submit a {@link Form} imperatively.
*/
declare function useSubmit(): SubmitFunction;
/**
* Resolves the URL to the closest route in the component hierarchy instead of
* the current URL of the app.
*
* This is used internally by {@link Form} to resolve the `action` to the closest
* route, but can be used generically as well.
*
* ```ts
* import { useFormAction } from "react-router";
*
* function SomeComponent() {
* // closest route URL
* let action = useFormAction();
*
* // closest route URL + "destroy"
* let destroyAction = useFormAction("destroy");
* }
* ```
*
* <docs-info>This hook adds a `basename` if your app specifies one, so that it
* can be used with raw `<form>` elements in a progressively enhanced way. If
* you are using this to provide an `action` to `<Form>` or `fetcher.submit`, you
* will need to remove the `basename` since both of those will prepend it
* internally.</docs-info>
*
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param action The action to append to the closest route URL. Defaults to the
* closest route URL.
* @param options Options
* @param options.relative The relative routing type to use when resolving the
* action. Defaults to `"route"`.
* @returns The resolved action URL.
*/
declare function useFormAction(action?: string, { relative }?: {
relative?: RelativeRoutingType;
}): string;
/**
* The return value {@link useFetcher} that keeps track of the state of a fetcher.
*
* ```tsx
* let fetcher = useFetcher();
* ```
*/
type FetcherWithComponents<TData> = Fetcher<TData> & {
/**
* Just like {@link Form} except it doesn't cause a navigation.
*
* ```tsx
* function SomeComponent() {
* const fetcher = useFetcher()
* return (
* <fetcher.Form method="post" action="/some/route">
* <input type="text" />
* </fetcher.Form>
* )
* }
* ```
*/
Form: React.ForwardRefExoticComponent<FetcherFormProps & React.RefAttributes<HTMLFormElement>>;
/**
* Loads data from a route. Useful for loading data imperatively inside user
* events outside a normal button or form, like a combobox or search input.
*
* ```tsx
* let fetcher = useFetcher()
*
* <input onChange={e => {
* fetcher.load(`/search?q=${e.target.value}`)
* }} />
* ```
*/
load: (href: string, opts?: {
/**
* Wraps the initial state update for this `fetcher.load` in a
* [`ReactDOM.flushSync`](https://react.dev/reference/react-dom/flushSync)
* call instead of the default [`React.startTransition`](https://react.dev/reference/react/startTransition).
* This allows you to perform synchronous DOM actions immediately after the
* update is flushed to the DOM.
*/
flushSync?: boolean;
}) => Promise<void>;
/**
* Reset a fetcher back to an empty/idle state.
*
* If the fetcher is currently in-flight, the
* [`AbortController`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController)
* will be aborted with the `reason`, if provided.
* @param opts Options for resetting the fetcher.
* @param opts.reason Optional `reason` to provide to [`AbortController.abort()`](https://developer.mozilla.org/en-US/docs/Web/API/AbortController/abort)
* @returns void
*/
reset: (opts?: {
reason?: unknown;
}) => void;
/**
* Submits form data to a route. While multiple nested routes can match a URL, only the leaf route will be called.
*
* The `formData` can be multiple types:
*
* - [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
* A `FormData` instance.
* - [`HTMLFormElement`](https://developer.mozilla.org/en-US/docs/Web/API/HTMLFormElement)
* A [`<form>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/form) DOM element.
* - `Object`
* An object of key/value-pairs that will be converted to a [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
* instance by default. You can pass a more complex object and serialize it
* as JSON by specifying `encType: "application/json"`. See
* {@link useSubmit} for more details.
*
* If the method is `GET`, then the route [`loader`](../../start/framework/route-module#loader)
* is being called and with the `formData` serialized to the url as [`URLSearchParams`](https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams).
* If `DELETE`, `PATCH`, `POST`, or `PUT`, then the route [`action`](../../start/framework/route-module#action)
* is being called with `formData` as the body.
*
* ```tsx
* // Submit a FormData instance (GET request)
* const formData = new FormData();
* fetcher.submit(formData);
*
* // Submit the HTML form element
* fetcher.submit(event.currentTarget.form, {
* method: "POST",
* });
*
* // Submit key/value JSON as a FormData instance
* fetcher.submit(
* { serialized: "values" },
* { method: "POST" }
* );
*
* // Submit raw JSON
* fetcher.submit(
* {
* deeply: {
* nested: {
* json: "values",
* },
* },
* },
* {
* method: "POST",
* encType: "application/json",
* }
* );
* ```
*/
submit: FetcherSubmitFunction;
};
/**
* Useful for creating complex, dynamic user interfaces that require multiple,
* concurrent data interactions without causing a navigation.
*
* Fetchers track their own, independent state and can be used to load data, submit
* forms, and generally interact with [`action`](../../start/framework/route-module#action)
* and [`loader`](../../start/framework/route-module#loader) functions.
*
* @example
* import { useFetcher } from "react-router"
*
* function SomeComponent() {
* let fetcher = useFetcher()
*
* // states are available on the fetcher
* fetcher.state // "idle" | "loading" | "submitting"
* fetcher.data // the data returned from the action or loader
*
* // render a form
* <fetcher.Form method="post" />
*
* // load data
* fetcher.load("/some/route")
*
* // submit data
* fetcher.submit(someFormRef, { method: "post" })
* fetcher.submit(someData, {
* method: "post",
* encType: "application/json"
* })
*
* // reset fetcher
* fetcher.reset()
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param options Options
* @param options.key A unique key to identify the fetcher.
*
*
* By default, `useFetcher` generates a unique fetcher scoped to that component.
* If you want to identify a fetcher with your own key such that you can access
* it from elsewhere in your app, you can do that with the `key` option:
*
* ```tsx
* function SomeComp() {
* let fetcher = useFetcher({ key: "my-key" })
* // ...
* }
*
* // Somewhere else
* function AnotherComp() {
* // this will be the same fetcher, sharing the state across the app
* let fetcher = useFetcher({ key: "my-key" });
* // ...
* }
* ```
* @returns A {@link FetcherWithComponents} object that contains the fetcher's state, data, and components for submitting forms and loading data.
*/
declare function useFetcher<T = any>({ key, }?: {
key?: string;
}): FetcherWithComponents<SerializeFrom<T>>;
/**
* Returns an array of all in-flight {@link Fetcher}s. This is useful for components
* throughout the app that didn't create the fetchers but want to use their submissions
* to participate in optimistic UI.
*
* @example
* import { useFetchers } from "react-router";
*
* function SomeComponent() {
* const fetchers = useFetchers();
* fetchers[0].formData; // FormData
* fetchers[0].state; // etc.
* // ...
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns An array of all in-flight {@link Fetcher}s, each with a unique `key`
* property.
*/
declare function useFetchers(): (Fetcher & {
key: string;
})[];
/**
* When rendered inside a {@link RouterProvider}, will restore scroll positions
* on navigations
*
* <!--
* Not marked `@public` because we only export as UNSAFE_ and therefore we don't
* maintain an .md file for this hook
* -->
*
* @name UNSAFE_useScrollRestoration
* @category Hooks
* @mode framework
* @mode data
* @param options Options
* @param options.getKey A function that returns a key to use for scroll restoration.
* This is useful for custom scroll restoration logic, such as using only the pathname
* so that subsequent navigations to prior paths will restore the scroll. Defaults
* to `location.key`.
* @param options.storageKey The key to use for storing scroll positions in
* `sessionStorage`. Defaults to `"react-router-scroll-positions"`.
* @returns {void}
*/
declare function useScrollRestoration({ getKey, storageKey, }?: {
getKey?: GetScrollRestorationKeyFunction;
storageKey?: string;
}): void;
/**
* Set up a callback to be fired on [Window's `beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event).
*
* @public
* @category Hooks
* @param callback The callback to be called when the [`beforeunload` event](https://developer.mozilla.org/en-US/docs/Web/API/Window/beforeunload_event)
* is fired.
* @param options Options
* @param options.capture If `true`, the event will be captured during the capture
* phase. Defaults to `false`.
* @returns {void}
*/
declare function useBeforeUnload(callback: (event: BeforeUnloadEvent) => any, options?: {
capture?: boolean;
}): void;
/**
* Wrapper around {@link useBlocker} to show a [`window.confirm`](https://developer.mozilla.org/en-US/docs/Web/API/Window/confirm)
* prompt to users instead of building a custom UI with {@link useBlocker}.
*
* The `unstable_` flag will not be removed because this technique has a lot of
* rough edges and behaves very differently (and incorrectly sometimes) across
* browsers if users click addition back/forward navigations while the
* confirmation is open. Use at your own risk.
*
* @example
* function ImportantForm() {
* let [value, setValue] = React.useState("");
*
* // Block navigating elsewhere when data has been entered into the input
* unstable_usePrompt({
* message: "Are you sure?",
* when: ({ currentLocation, nextLocation }) =>
* value !== "" &&
* currentLocation.pathname !== nextLocation.pathname,
* });
*
* return (
* <Form method="post">
* <label>
* Enter some important data:
* <input
* name="data"
* value={value}
* onChange={(e) => setValue(e.target.value)}
* />
* </label>
* <button type="submit">Save</button>
* </Form>
* );
* }
*
* @name unstable_usePrompt
* @public
* @category Hooks
* @mode framework
* @mode data
* @param options Options
* @param options.message The message to show in the confirmation dialog.
* @param options.when A boolean or a function that returns a boolean indicating
* whether to block the navigation. If a function is provided, it will receive an
* object with `currentLocation` and `nextLocation` properties.
* @returns {void}
*/
declare function usePrompt({ when, message, }: {
when: boolean | BlockerFunction;
message: string;
}): void;
/**
* This hook returns `true` when there is an active [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* and the specified location matches either side of the navigation (the URL you are
* navigating **to** or the URL you are navigating **from**). This can be used to apply finer-grained styles to
* elements to further customize the view transition. This requires that view
* transitions have been enabled for the given navigation via {@link LinkProps.viewTransition}
* (or the `Form`, `submit`, or `navigate` call)
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param to The {@link To} location to compare against the active transition's current
* and next URLs.
* @param options Options
* @param options.relative The relative routing type to use when resolving the
* `to` location, defaults to `"route"`. See {@link RelativeRoutingType} for
* more details.
* @returns `true` if there is an active [View Transition](https://developer.mozilla.org/en-US/docs/Web/API/View_Transitions_API)
* and the resolved path matches the transition's destination or source pathname, otherwise `false`.
*/
declare function useViewTransitionState(to: To, { relative }?: {
relative?: RelativeRoutingType;
}): boolean;
/**
* @category Types
*/
interface StaticRouterProps {
/**
* The base URL for the static router (default: `/`)
*/
basename?: string;
/**
* The child elements to render inside the static router
*/
children?: React.ReactNode;
/**
* The {@link Location} to render the static router at (default: `/`)
*/
location: Partial<Location> | string;
}
/**
* A {@link Router | `<Router>`} that may not navigate to any other {@link Location}.
* This is useful on the server where there is no stateful UI.
*
* @public
* @category Declarative Routers
* @mode declarative
* @param props Props
* @param {StaticRouterProps.basename} props.basename n/a
* @param {StaticRouterProps.children} props.children n/a
* @param {StaticRouterProps.location} props.location n/a
* @returns A React element that renders the static {@link Router | `<Router>`}
*/
declare function StaticRouter({ basename, children, location: locationProp, }: StaticRouterProps): React.JSX.Element;
/**
* @category Types
*/
interface StaticRouterProviderProps {
/**
* The {@link StaticHandlerContext} returned from {@link StaticHandler}'s
* `query`
*/
context: StaticHandlerContext;
/**
* The static {@link DataRouter} from {@link createStaticRouter}
*/
router: Router;
/**
* Whether to hydrate the router on the client (default `true`)
*/
hydrate?: boolean;
/**
* The [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* to use for the hydration [`<script>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/script)
* tag
*/
nonce?: string;
}
/**
* A {@link DataRouter} that may not navigate to any other {@link Location}.
* This is useful on the server where there is no stateful UI.
*
* @example
* export async function handleRequest(request: Request) {
* let { query, dataRoutes } = createStaticHandler(routes);
* let context = await query(request));
*
* if (context instanceof Response) {
* return context;
* }
*
* let router = createStaticRouter(dataRoutes, context);
* return new Response(
* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
* { headers: { "Content-Type": "text/html" } }
* );
* }
*
* @public
* @category Data Routers
* @mode data
* @param props Props
* @param {StaticRouterProviderProps.context} props.context n/a
* @param {StaticRouterProviderProps.hydrate} props.hydrate n/a
* @param {StaticRouterProviderProps.nonce} props.nonce n/a
* @param {StaticRouterProviderProps.router} props.router n/a
* @returns A React element that renders the static router provider
*/
declare function StaticRouterProvider({ context, router, hydrate, nonce, }: StaticRouterProviderProps): React.JSX.Element;
type CreateStaticHandlerOptions = Omit<CreateStaticHandlerOptions$1, "mapRouteProperties">;
/**
* Create a static handler to perform server-side data loading
*
* @example
* export async function handleRequest(request: Request) {
* let { query, dataRoutes } = createStaticHandler(routes);
* let context = await query(request);
*
* if (context instanceof Response) {
* return context;
* }
*
* let router = createStaticRouter(dataRoutes, context);
* return new Response(
* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
* { headers: { "Content-Type": "text/html" } }
* );
* }
*
* @public
* @category Data Routers
* @mode data
* @param routes The {@link RouteObject | route objects} to create a static
* handler for
* @param opts Options
* @param opts.basename The base URL for the static handler (default: `/`)
* @param opts.future Future flags for the static handler
* @returns A static handler that can be used to query data for the provided
* routes
*/
declare function createStaticHandler(routes: RouteObject[], opts?: CreateStaticHandlerOptions): StaticHandler;
/**
* Create a static {@link DataRouter} for server-side rendering
*
* @example
* export async function handleRequest(request: Request) {
* let { query, dataRoutes } = createStaticHandler(routes);
* let context = await query(request);
*
* if (context instanceof Response) {
* return context;
* }
*
* let router = createStaticRouter(dataRoutes, context);
* return new Response(
* ReactDOMServer.renderToString(<StaticRouterProvider ... />),
* { headers: { "Content-Type": "text/html" } }
* );
* }
*
* @public
* @category Data Routers
* @mode data
* @param routes The route objects to create a static {@link DataRouter} for
* @param context The {@link StaticHandlerContext} returned from {@link StaticHandler}'s
* `query`
* @param opts Options
* @param opts.future Future flags for the static {@link DataRouter}
* @param opts.branches Optional pre-computed route branches
* @returns A static {@link DataRouter} that can be used to render the provided routes
*/
declare function createStaticRouter(routes: RouteObject[], context: StaticHandlerContext, opts?: {
branches?: RouteBranch<DataRouteObject>[];
future?: Partial<FutureConfig$1>;
}): Router;
export { type ScriptsProps as $, type AssetsManifest as A, type BrowserRouterProps as B, useViewTransitionState as C, type DOMRouterOpts as D, type EntryContext as E, type FutureConfig as F, type FetcherSubmitOptions as G, type HashRouterProps as H, type SubmitOptions as I, type SubmitTarget as J, createSearchParams as K, type LinkProps as L, type StaticRouterProps as M, type NavLinkProps as N, type StaticRouterProviderProps as O, type ParamKeyValuePair as P, createStaticHandler as Q, createStaticRouter as R, type ServerBuild as S, StaticRouter as T, type URLSearchParamsInit as U, StaticRouterProvider as V, Meta as W, Links as X, Scripts as Y, PrefetchPageLinks as Z, type LinksProps as _, type HistoryRouterProps as a, type PrefetchBehavior as a0, type DiscoverBehavior as a1, type HandleDataRequestFunction as a2, type HandleDocumentRequestFunction as a3, type HandleErrorFunction as a4, type ServerEntryModule as a5, FrameworkContext as a6, createClientRoutes as a7, createClientRoutesWithHMRRevalidationOptOut as a8, shouldHydrateRouteLoader as a9, useScrollRestoration as aa, type NavLinkRenderProps as b, type FetcherFormProps as c, type FormProps as d, type ScrollRestorationProps as e, type SetURLSearchParams as f, type SubmitFunction as g, type FetcherSubmitFunction as h, type FetcherWithComponents as i, createBrowserRouter as j, createHashRouter as k, BrowserRouter as l, HashRouter as m, Link as n, HistoryRouter as o, NavLink as p, Form as q, ScrollRestoration as r, useSearchParams as s, useSubmit as t, useLinkClickHandler as u, useFormAction as v, useFetcher as w, useFetchers as x, useBeforeUnload as y, usePrompt as z };
@@ -0,0 +1,4 @@
export { Q as MemoryRouter, T as Navigate, U as Outlet, V as Route, W as Router, X as RouterProvider, Y as Routes, A as UNSAFE_AwaitContextProvider, ab as UNSAFE_WithComponentProps, af as UNSAFE_WithErrorBoundaryProps, ad as UNSAFE_WithHydrateFallbackProps } from './context-CeD5LmaF.mjs';
export { l as BrowserRouter, q as Form, m as HashRouter, n as Link, X as Links, W as Meta, p as NavLink, r as ScrollRestoration, T as StaticRouter, V as StaticRouterProvider, o as unstable_HistoryRouter } from './index-react-server-client-CACgcj2J.mjs';
import './data-DEjBmEfD.mjs';
import 'react';
@@ -0,0 +1,4 @@
export { W as BrowserRouter, $ as Form, X as HashRouter, Y as Link, an as Links, j as MemoryRouter, am as Meta, _ as NavLink, k as Navigate, l as Outlet, m as Route, n as Router, o as RouterProvider, p as Routes, a0 as ScrollRestoration, ak as StaticRouter, al as StaticRouterProvider, b as UNSAFE_AwaitContextProvider, aH as UNSAFE_WithComponentProps, aL as UNSAFE_WithErrorBoundaryProps, aJ as UNSAFE_WithHydrateFallbackProps, Z as unstable_HistoryRouter } from './index-react-server-client-3ykjivgQ.js';
import './instrumentation-Dkmpzd13.js';
import './data-CjO11-hU.js';
import 'react';
@@ -0,0 +1,61 @@
"use strict";Object.defineProperty(exports, "__esModule", {value: true});/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
"use client";
var _chunkG5KIBF6Ujs = require('./chunk-G5KIBF6U.js');
var _chunkSA4DP3SFjs = require('./chunk-SA4DP3SF.js');
exports.BrowserRouter = _chunkG5KIBF6Ujs.BrowserRouter; exports.Form = _chunkG5KIBF6Ujs.Form; exports.HashRouter = _chunkG5KIBF6Ujs.HashRouter; exports.Link = _chunkG5KIBF6Ujs.Link; exports.Links = _chunkSA4DP3SFjs.Links; exports.MemoryRouter = _chunkSA4DP3SFjs.MemoryRouter; exports.Meta = _chunkSA4DP3SFjs.Meta; exports.NavLink = _chunkG5KIBF6Ujs.NavLink; exports.Navigate = _chunkSA4DP3SFjs.Navigate; exports.Outlet = _chunkSA4DP3SFjs.Outlet; exports.Route = _chunkSA4DP3SFjs.Route; exports.Router = _chunkSA4DP3SFjs.Router; exports.RouterProvider = _chunkSA4DP3SFjs.RouterProvider; exports.Routes = _chunkSA4DP3SFjs.Routes; exports.ScrollRestoration = _chunkG5KIBF6Ujs.ScrollRestoration; exports.StaticRouter = _chunkG5KIBF6Ujs.StaticRouter; exports.StaticRouterProvider = _chunkG5KIBF6Ujs.StaticRouterProvider; exports.UNSAFE_AwaitContextProvider = _chunkSA4DP3SFjs.AwaitContextProvider; exports.UNSAFE_WithComponentProps = _chunkSA4DP3SFjs.WithComponentProps; exports.UNSAFE_WithErrorBoundaryProps = _chunkSA4DP3SFjs.WithErrorBoundaryProps; exports.UNSAFE_WithHydrateFallbackProps = _chunkSA4DP3SFjs.WithHydrateFallbackProps; exports.unstable_HistoryRouter = _chunkG5KIBF6Ujs.HistoryRouter;
@@ -0,0 +1,59 @@
/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
"use client";
import {
AwaitContextProvider,
BrowserRouter,
Form,
HashRouter,
HistoryRouter,
Link,
Links,
MemoryRouter,
Meta,
NavLink,
Navigate,
Outlet,
Route,
Router,
RouterProvider,
Routes,
ScrollRestoration,
StaticRouter,
StaticRouterProvider,
WithComponentProps,
WithErrorBoundaryProps,
WithHydrateFallbackProps
} from "./chunk-KS7C4IRE.mjs";
export {
BrowserRouter,
Form,
HashRouter,
Link,
Links,
MemoryRouter,
Meta,
NavLink,
Navigate,
Outlet,
Route,
Router,
RouterProvider,
Routes,
ScrollRestoration,
StaticRouter,
StaticRouterProvider,
AwaitContextProvider as UNSAFE_AwaitContextProvider,
WithComponentProps as UNSAFE_WithComponentProps,
WithErrorBoundaryProps as UNSAFE_WithErrorBoundaryProps,
WithHydrateFallbackProps as UNSAFE_WithHydrateFallbackProps,
HistoryRouter as unstable_HistoryRouter
};
@@ -0,0 +1,2711 @@
import * as React from 'react';
export { BrowserRouter, Form, HashRouter, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Outlet, Route, Router, RouterProvider, Routes, ScrollRestoration, StaticRouter, StaticRouterProvider, unstable_HistoryRouter } from 'react-router/internal/react-server-client';
import { ParseOptions, SerializeOptions } from 'cookie';
export { ParseOptions as CookieParseOptions, SerializeOptions as CookieSerializeOptions } from 'cookie';
/**
* Actions represent the type of change to a location value.
*/
declare enum Action {
/**
* A POP indicates a change to an arbitrary index in the history stack, such
* as a back or forward navigation. It does not describe the direction of the
* navigation, only that the current index changed.
*
* Note: This is the default action for newly created history objects.
*/
Pop = "POP",
/**
* A PUSH indicates a new entry being added to the history stack, such as when
* a link is clicked and a new page loads. When this happens, all subsequent
* entries in the stack are lost.
*/
Push = "PUSH",
/**
* A REPLACE indicates the entry at the current index in the history stack
* being replaced by a new one.
*/
Replace = "REPLACE"
}
/**
* The pathname, search, and hash values of a URL.
*/
interface Path {
/**
* A URL pathname, beginning with a /.
*/
pathname: string;
/**
* A URL search string, beginning with a ?.
*/
search: string;
/**
* A URL fragment identifier, beginning with a #.
*/
hash: string;
}
/**
* An entry in a history stack. A location contains information about the
* URL path, as well as possibly some arbitrary state and a key.
*/
interface Location<State = any> extends Path {
/**
* A value of arbitrary data associated with this location.
*/
state: State;
/**
* A unique string associated with this location. May be used to safely store
* and retrieve data in some other storage API, like `localStorage`.
*
* Note: This value is always "default" on the initial location.
*/
key: string;
/**
* The masked location displayed in the URL bar, which differs from the URL the
* router is operating on
*/
mask?: Path;
}
/**
* A change to the current location.
*/
interface Update {
/**
* The action that triggered the change.
*/
action: Action;
/**
* The new location.
*/
location: Location;
/**
* The delta between this location and the former location in the history stack
*/
delta: number | null;
}
/**
* A function that receives notifications about location changes.
*/
interface Listener {
(update: Update): void;
}
/**
* Describes a location that is the destination of some navigation used in
* {@link Link}, {@link useNavigate}, etc.
*/
type To = string | Partial<Path>;
/**
* A history is an interface to the navigation stack. The history serves as the
* source of truth for the current location, as well as provides a set of
* methods that may be used to change it.
*
* It is similar to the DOM's `window.history` object, but with a smaller, more
* focused API.
*/
interface History {
/**
* The last action that modified the current location. This will always be
* Action.Pop when a history instance is first created. This value is mutable.
*/
readonly action: Action;
/**
* The current location. This value is mutable.
*/
readonly location: Location;
/**
* Returns a valid href for the given `to` value that may be used as
* the value of an <a href> attribute.
*
* @param to - The destination URL
*/
createHref(to: To): string;
/**
* Returns a URL for the given `to` value
*
* @param to - The destination URL
*/
createURL(to: To): URL;
/**
* Encode a location the same way window.history would do (no-op for memory
* history) so we ensure our PUSH/REPLACE navigations for data routers
* behave the same as POP
*
* @param to Unencoded path
*/
encodeLocation(to: To): Path;
/**
* Pushes a new location onto the history stack, increasing its length by one.
* If there were any entries in the stack after the current one, they are
* lost.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
push(to: To, state?: any): void;
/**
* Replaces the current location in the history stack with a new one. The
* location that was replaced will no longer be available.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
replace(to: To, state?: any): void;
/**
* Navigates `n` entries backward/forward in the history stack relative to the
* current index. For example, a "back" navigation would use go(-1).
*
* @param delta - The delta in the stack index
*/
go(delta: number): void;
/**
* Sets up a listener that will be called whenever the current location
* changes.
*
* @param listener - A function that will be called when the location changes
* @returns unlisten - A function that may be used to stop listening
*/
listen(listener: Listener): () => void;
}
/**
* An augmentable interface users can modify in their app-code to opt into
* future-flag-specific types
*/
interface Future {
}
type MiddlewareEnabled = Future extends {
v8_middleware: infer T extends boolean;
} ? T : false;
type MaybePromise<T> = T | Promise<T>;
/**
* Map of routeId -> data returned from a loader/action/error
*/
interface RouteData {
[routeId: string]: any;
}
type LowerCaseFormMethod = "get" | "post" | "put" | "patch" | "delete";
type UpperCaseFormMethod = Uppercase<LowerCaseFormMethod>;
/**
* Users can specify either lowercase or uppercase form methods on `<Form>`,
* useSubmit(), `<fetcher.Form>`, etc.
*/
type HTMLFormMethod = LowerCaseFormMethod | UpperCaseFormMethod;
/**
* Active navigation/fetcher form methods are exposed in uppercase on the
* RouterState. This is to align with the normalization done via fetch().
*/
type FormMethod = UpperCaseFormMethod;
type FormEncType = "application/x-www-form-urlencoded" | "multipart/form-data" | "application/json" | "text/plain";
type JsonObject = {
[Key in string]: JsonValue;
} & {
[Key in string]?: JsonValue | undefined;
};
type JsonArray = JsonValue[] | readonly JsonValue[];
type JsonPrimitive = string | number | boolean | null;
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
/**
* @private
* Internal interface to pass around for action submissions, not intended for
* external consumption
*/
type Submission = {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: FormData;
json: undefined;
text: undefined;
} | {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: undefined;
json: JsonValue;
text: undefined;
} | {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: undefined;
json: undefined;
text: string;
};
/**
* A context instance used as the key for the `get`/`set` methods of a
* {@link RouterContextProvider}. Accepts an optional default
* value to be returned if no value has been set.
*/
interface RouterContext<T = unknown> {
defaultValue?: T;
}
/**
* Creates a type-safe {@link RouterContext} object that can be used to
* store and retrieve arbitrary values in [`action`](../../start/framework/route-module#action)s,
* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
* Similar to React's [`createContext`](https://react.dev/reference/react/createContext),
* but specifically designed for React Router's request/response lifecycle.
*
* If a `defaultValue` is provided, it will be returned from `context.get()`
* when no value has been set for the context. Otherwise, reading this context
* when no value has been set will throw an error.
*
* ```tsx filename=app/context.ts
* import { createContext } from "react-router";
*
* // Create a context for user data
* export const userContext =
* createContext<User | null>(null);
* ```
*
* ```tsx filename=app/middleware/auth.ts
* import { getUserFromSession } from "~/auth.server";
* import { userContext } from "~/context";
*
* export const authMiddleware = async ({
* context,
* request,
* }) => {
* const user = await getUserFromSession(request);
* context.set(userContext, user);
* };
* ```
*
* ```tsx filename=app/routes/profile.tsx
* import { userContext } from "~/context";
*
* export async function loader({
* context,
* }: Route.LoaderArgs) {
* const user = context.get(userContext);
*
* if (!user) {
* throw new Response("Unauthorized", { status: 401 });
* }
*
* return { user };
* }
* ```
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param defaultValue An optional default value for the context. This value
* will be returned if no value has been set for this context.
* @returns A {@link RouterContext} object that can be used with
* `context.get()` and `context.set()` in [`action`](../../start/framework/route-module#action)s,
* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
*/
declare function createContext<T>(defaultValue?: T): RouterContext<T>;
/**
* Provides methods for writing/reading values in application context in a
* type-safe way. Primarily for usage with [middleware](../../how-to/middleware).
*
* @example
* import {
* createContext,
* RouterContextProvider
* } from "react-router";
*
* const userContext = createContext<User | null>(null);
* const contextProvider = new RouterContextProvider();
* contextProvider.set(userContext, getUser());
* // ^ Type-safe
* const user = contextProvider.get(userContext);
* // ^ User
*
* @public
* @category Utils
* @mode framework
* @mode data
*/
declare class RouterContextProvider {
#private;
/**
* Create a new `RouterContextProvider` instance
* @param init An optional initial context map to populate the provider with
*/
constructor(init?: Map<RouterContext, unknown>);
/**
* Access a value from the context. If no value has been set for the context,
* it will return the context's `defaultValue` if provided, or throw an error
* if no `defaultValue` was set.
* @param context The context to get the value for
* @returns The value for the context, or the context's `defaultValue` if no
* value was set
*/
get<T>(context: RouterContext<T>): T;
/**
* Set a value for the context. If the context already has a value set, this
* will overwrite it.
*
* @param context The context to set the value for
* @param value The value to set for the context
* @returns {void}
*/
set<C extends RouterContext>(context: C, value: C extends RouterContext<infer T> ? T : never): void;
}
type DefaultContext = MiddlewareEnabled extends true ? Readonly<RouterContextProvider> : any;
/**
* @private
* Arguments passed to route loader/action functions. Same for now but we keep
* this as a private implementation detail in case they diverge in the future.
*/
interface DataFunctionArgs<Context> {
/** A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read headers (like cookies, and {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams URLSearchParams} from the request. */
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function loader({
* params,
* }: Route.LoaderArgs) {
* params.teamId;
* // ^ string
* }
*/
params: Params;
/**
* This is the context passed in to your server adapter's getLoadContext() function.
* It's a way to bridge the gap between the adapter's request/response API with your React Router app.
* It is only applicable if you are using a custom server adapter.
*/
context: Context;
}
/**
* Route middleware `next` function to call downstream handlers and then complete
* middlewares from the bottom-up
*/
interface MiddlewareNextFunction<Result = unknown> {
(): Promise<Result>;
}
/**
* Route middleware function signature. Receives the same "data" arguments as a
* `loader`/`action` (`request`, `params`, `context`) as the first parameter and
* a `next` function as the second parameter which will call downstream handlers
* and then complete middlewares from the bottom-up
*/
type MiddlewareFunction<Result = unknown> = (args: DataFunctionArgs<Readonly<RouterContextProvider>>, next: MiddlewareNextFunction<Result>) => MaybePromise<Result | void>;
/**
* Arguments passed to loader functions
*/
interface LoaderFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
}
/**
* Arguments passed to action functions
*/
interface ActionFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
}
/**
* Loaders and actions can return anything
*/
type DataFunctionValue = unknown;
type DataFunctionReturnValue = MaybePromise<DataFunctionValue>;
/**
* Route loader function signature
*/
type LoaderFunction<Context = DefaultContext> = {
(args: LoaderFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue;
} & {
hydrate?: boolean;
};
/**
* Route action function signature
*/
interface ActionFunction<Context = DefaultContext> {
(args: ActionFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue;
}
/**
* Arguments passed to shouldRevalidate function
*/
interface ShouldRevalidateFunctionArgs {
/** This is the url the navigation started from. You can compare it with `nextUrl` to decide if you need to revalidate this route's data. */
currentUrl: URL;
/** These are the {@link https://reactrouter.com/start/framework/routing#dynamic-segments dynamic route params} from the URL that can be compared to the `nextParams` to decide if you need to reload or not. Perhaps you're using only a partial piece of the param for data loading, you don't need to revalidate if a superfluous part of the param changed. */
currentParams: DataRouteMatch["params"];
/** In the case of navigation, this the URL the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentUrl. */
nextUrl: URL;
/** In the case of navigation, these are the {@link https://reactrouter.com/start/framework/routing#dynamic-segments dynamic route params} from the next location the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentParams. */
nextParams: DataRouteMatch["params"];
/** The method (probably `"GET"` or `"POST"`) used in the form submission that triggered the revalidation. */
formMethod?: Submission["formMethod"];
/** The form action (`<Form action="/somewhere">`) that triggered the revalidation. */
formAction?: Submission["formAction"];
/** The form encType (`<Form encType="application/x-www-form-urlencoded">) used in the form submission that triggered the revalidation*/
formEncType?: Submission["formEncType"];
/** The form submission data when the form's encType is `text/plain` */
text?: Submission["text"];
/** The form submission data when the form's encType is `application/x-www-form-urlencoded` or `multipart/form-data` */
formData?: Submission["formData"];
/** The form submission data when the form's encType is `application/json` */
json?: Submission["json"];
/** The status code of the action response */
actionStatus?: number;
/**
* When a submission causes the revalidation this will be the result of the action—either action data or an error if the action failed. It's common to include some information in the action result to instruct shouldRevalidate to revalidate or not.
*
* @example
* export async function action() {
* await saveSomeStuff();
* return { ok: true };
* }
*
* export function shouldRevalidate({
* actionResult,
* }) {
* if (actionResult?.ok) {
* return false;
* }
* return true;
* }
*/
actionResult?: any;
/**
* By default, React Router doesn't call every loader all the time. There are reliable optimizations it can make by default. For example, only loaders with changing params are called. Consider navigating from the following URL to the one below it:
*
* /projects/123/tasks/abc
* /projects/123/tasks/def
* React Router will only call the loader for tasks/def because the param for projects/123 didn't change.
*
* It's safest to always return defaultShouldRevalidate after you've done your specific optimizations that return false, otherwise your UI might get out of sync with your data on the server.
*/
defaultShouldRevalidate: boolean;
}
/**
* Route shouldRevalidate function signature. This runs after any submission
* (navigation or fetcher), so we flatten the navigation/fetcher submission
* onto the arguments. It shouldn't matter whether it came from a navigation
* or a fetcher, what really matters is the URLs and the formData since loaders
* have to re-run based on the data models that were potentially mutated.
*/
interface ShouldRevalidateFunction {
(args: ShouldRevalidateFunctionArgs): boolean;
}
interface DataStrategyMatch extends RouteMatch<string, DataRouteObject> {
/**
* @private
*/
_lazyPromises?: {
middleware: Promise<void> | undefined;
handler: Promise<void> | undefined;
route: Promise<void> | undefined;
};
/**
* @deprecated Deprecated in favor of `shouldCallHandler`
*
* A boolean value indicating whether this route handler should be called in
* this pass.
*
* The `matches` array always includes _all_ matched routes even when only
* _some_ route handlers need to be called so that things like middleware can
* be implemented.
*
* `shouldLoad` is usually only interesting if you are skipping the route
* handler entirely and implementing custom handler logic - since it lets you
* determine if that custom logic should run for this route or not.
*
* For example:
* - If you are on `/parent/child/a` and you navigate to `/parent/child/b` -
* you'll get an array of three matches (`[parent, child, b]`), but only `b`
* will have `shouldLoad=true` because the data for `parent` and `child` is
* already loaded
* - If you are on `/parent/child/a` and you submit to `a`'s [`action`](https://reactrouter.com/docs/start/data/route-object#action),
* then only `a` will have `shouldLoad=true` for the action execution of
* `dataStrategy`
* - After the [`action`](https://reactrouter.com/docs/start/data/route-object#action),
* `dataStrategy` will be called again for the [`loader`](https://reactrouter.com/docs/start/data/route-object#loader)
* revalidation, and all matches will have `shouldLoad=true` (assuming no
* custom `shouldRevalidate` implementations)
*/
shouldLoad: boolean;
/**
* Arguments passed to the `shouldRevalidate` function for this `loader` execution.
* Will be `null` if this is not a revalidating loader {@link DataStrategyMatch}.
*/
shouldRevalidateArgs: ShouldRevalidateFunctionArgs | null;
/**
* Determine if this route's handler should be called during this `dataStrategy`
* execution. Calling it with no arguments will leverage the default revalidation
* behavior. You can pass your own `defaultShouldRevalidate` value if you wish
* to change the default revalidation behavior with your `dataStrategy`.
*
* @param defaultShouldRevalidate `defaultShouldRevalidate` override value (optional)
*/
shouldCallHandler(defaultShouldRevalidate?: boolean): boolean;
/**
* An async function that will resolve any `route.lazy` implementations and
* execute the route's handler (if necessary), returning a {@link DataStrategyResult}
*
* - Calling `match.resolve` does not mean you're calling the
* [`action`](https://reactrouter.com/docs/start/data/route-object#action)/[`loader`](https://reactrouter.com/docs/start/data/route-object#loader)
* (the "handler") - `resolve` will only call the `handler` internally if
* needed _and_ if you don't pass your own `handlerOverride` function parameter
* - It is safe to call `match.resolve` for all matches, even if they have
* `shouldLoad=false`, and it will no-op if no loading is required
* - You should generally always call `match.resolve()` for `shouldLoad:true`
* routes to ensure that any `route.lazy` implementations are processed
* - See the examples below for how to implement custom handler execution via
* `match.resolve`
*/
resolve: (handlerOverride?: (handler: (ctx?: unknown) => DataFunctionReturnValue) => DataFunctionReturnValue) => Promise<DataStrategyResult>;
}
interface DataStrategyFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
/**
* Matches for this route extended with Data strategy APIs
*/
matches: DataStrategyMatch[];
runClientMiddleware: (cb: DataStrategyFunction<Context>) => Promise<Record<string, DataStrategyResult>>;
/**
* The key of the fetcher we are calling `dataStrategy` for, otherwise `null`
* for navigational executions
*/
fetcherKey: string | null;
}
/**
* Result from a loader or action called via dataStrategy
*/
interface DataStrategyResult {
type: "data" | "error";
result: unknown;
}
interface DataStrategyFunction<Context = DefaultContext> {
(args: DataStrategyFunctionArgs<Context>): Promise<Record<string, DataStrategyResult>>;
}
type PatchRoutesOnNavigationFunctionArgs = {
signal: AbortSignal;
path: string;
matches: RouteMatch[];
fetcherKey: string | undefined;
patch: (routeId: string | null, children: RouteObject[]) => void;
};
type PatchRoutesOnNavigationFunction = (opts: PatchRoutesOnNavigationFunctionArgs) => MaybePromise<void>;
/**
* Function provided to set route-specific properties from route objects
*/
interface MapRoutePropertiesFunction {
(route: DataRouteObject): {
hasErrorBoundary: boolean;
} & Record<string, any>;
}
/**
* Keys we cannot change from within a lazy object. We spread all other keys
* onto the route. Either they're meaningful to the router, or they'll get
* ignored.
*/
type UnsupportedLazyRouteObjectKey = "lazy" | "caseSensitive" | "path" | "id" | "index" | "children";
/**
* Keys we cannot change from within a lazy() function. We spread all other keys
* onto the route. Either they're meaningful to the router, or they'll get
* ignored.
*/
type UnsupportedLazyRouteFunctionKey = UnsupportedLazyRouteObjectKey | "middleware";
/**
* lazy object to load route properties, which can add non-matching
* related properties to a route
*/
type LazyRouteObject<R extends RouteObject> = {
[K in keyof R as K extends UnsupportedLazyRouteObjectKey ? never : K]?: () => Promise<R[K] | null | undefined>;
};
/**
* lazy() function to load a route definition, which can add non-matching
* related properties to a route
*/
interface LazyRouteFunction<R extends RouteObject> {
(): Promise<Omit<R, UnsupportedLazyRouteFunctionKey> & Partial<Record<UnsupportedLazyRouteFunctionKey, never>>>;
}
type LazyRouteDefinition<R extends RouteObject> = LazyRouteObject<R> | LazyRouteFunction<R>;
/**
* Base RouteObject with common props shared by all types of routes
* @internal
*/
type BaseRouteObject = {
/**
* Whether the path should be case-sensitive. Defaults to `false`.
*/
caseSensitive?: boolean;
/**
* The path pattern to match. If unspecified or empty, then this becomes a
* layout route.
*/
path?: string;
/**
* The unique identifier for this route (for use with {@link DataRouter}s)
*/
id?: string;
/**
* The route middleware.
* See [`middleware`](../../start/data/route-object#middleware).
*/
middleware?: MiddlewareFunction[];
/**
* The route loader.
* See [`loader`](../../start/data/route-object#loader).
*/
loader?: LoaderFunction | boolean;
/**
* The route action.
* See [`action`](../../start/data/route-object#action).
*/
action?: ActionFunction | boolean;
hasErrorBoundary?: boolean;
/**
* The route shouldRevalidate function.
* See [`shouldRevalidate`](../../start/data/route-object#shouldRevalidate).
*/
shouldRevalidate?: ShouldRevalidateFunction;
/**
* The route handle.
*/
handle?: any;
/**
* A function that returns a promise that resolves to the route object.
* Used for code-splitting routes.
* See [`lazy`](../../start/data/route-object#lazy).
*/
lazy?: LazyRouteDefinition<BaseRouteObject>;
/**
* The React Component to render when this route matches.
* Mutually exclusive with `element`.
*/
Component?: React.ComponentType | null;
/**
* The React element to render when this Route matches.
* Mutually exclusive with `Component`.
*/
element?: React.ReactNode | null;
/**
* The React Component to render at this route if an error occurs.
* Mutually exclusive with `errorElement`.
*/
ErrorBoundary?: React.ComponentType | null;
/**
* The React element to render at this route if an error occurs.
* Mutually exclusive with `ErrorBoundary`.
*/
errorElement?: React.ReactNode | null;
/**
* The React Component to render while this router is loading data.
* Mutually exclusive with `hydrateFallbackElement`.
*/
HydrateFallback?: React.ComponentType | null;
/**
* The React element to render while this router is loading data.
* Mutually exclusive with `HydrateFallback`.
*/
hydrateFallbackElement?: React.ReactNode | null;
};
/**
* Index routes must not have children
*/
type IndexRouteObject = BaseRouteObject & {
/**
* Child Route objects - not valid on index routes.
*/
children?: undefined;
/**
* Whether this is an index route.
*/
index: true;
};
/**
* Non-index routes may have children, but cannot have `index` set to `true`.
*/
type NonIndexRouteObject = BaseRouteObject & {
/**
* Child Route objects.
*/
children?: RouteObject[];
/**
* Whether this is an index route - must be `false` or undefined on non-index routes.
*/
index?: false;
};
/**
* A route object represents a logical route, with (optionally) its child
* routes organized in a tree-like structure.
*/
type RouteObject = IndexRouteObject | NonIndexRouteObject;
type DataIndexRouteObject = IndexRouteObject & {
id: string;
};
type DataNonIndexRouteObject = NonIndexRouteObject & {
children?: DataRouteObject[];
id: string;
};
/**
* A data route object, which is just a RouteObject with a required unique ID
*/
type DataRouteObject = DataIndexRouteObject | DataNonIndexRouteObject;
type RouteManifest<R = DataRouteObject> = Record<string, R | undefined>;
/**
* The parameters that were parsed from the URL path.
*/
type Params<Key extends string = string> = {
readonly [key in Key]: string | undefined;
};
/**
* A RouteMatch contains info about how a route matched a URL.
*/
interface RouteMatch<ParamKey extends string = string, RouteObjectType extends RouteObject = RouteObject> {
/**
* The names and values of dynamic parameters in the URL.
*/
params: Params<ParamKey>;
/**
* The portion of the URL pathname that was matched.
*/
pathname: string;
/**
* The portion of the URL pathname that was matched before child routes.
*/
pathnameBase: string;
/**
* The route object that was used to match.
*/
route: RouteObjectType;
}
interface DataRouteMatch extends RouteMatch<string, DataRouteObject> {
}
/**
* Matches the given routes to a location and returns the match data.
*
* @example
* import { matchRoutes } from "react-router";
*
* let routes = [{
* path: "/",
* Component: Root,
* children: [{
* path: "dashboard",
* Component: Dashboard,
* }]
* }];
*
* matchRoutes(routes, "/dashboard"); // [rootMatch, dashboardMatch]
*
* @public
* @category Utils
* @param routes The array of route objects to match against.
* @param locationArg The location to match against, either a string path or a
* partial {@link Location} object
* @param basename Optional base path to strip from the location before matching.
* Defaults to `/`.
* @returns An array of matched routes, or `null` if no matches were found.
*/
declare function matchRoutes<RouteObjectType extends RouteObject = RouteObject>(routes: RouteObjectType[], locationArg: Partial<Location> | string, basename?: string): RouteMatch<string, RouteObjectType>[] | null;
interface UIMatch<Data = unknown, Handle = unknown> {
id: string;
pathname: string;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the matched route.
*/
params: RouteMatch["params"];
/**
* The return value from the matched route's loader or clientLoader. This might
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
* an error and we're currently displaying an `ErrorBoundary`.
*
* @deprecated Use `UIMatch.loaderData` instead
*/
data: Data | undefined;
/**
* The return value from the matched route's loader or clientLoader. This might
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
* an error and we're currently displaying an `ErrorBoundary`.
*/
loaderData: Data | undefined;
/**
* The {@link https://reactrouter.com/start/framework/route-module#handle handle object}
* exported from the matched route module
*/
handle: Handle;
}
interface RouteMeta<RouteObjectType extends RouteObject = RouteObject> {
relativePath: string;
caseSensitive: boolean;
childrenIndex: number;
route: RouteObjectType;
matcher?: RegExp;
compiledParams?: CompiledPathParam[];
}
/**
* @private
* PRIVATE - DO NOT USE
*
* A "branch" of routes that match a given route pattern.
* This is an internal interface not intended for direct external usage.
*/
interface RouteBranch<RouteObjectType extends RouteObject = RouteObject> {
path: string;
score: number;
routesMeta: RouteMeta<RouteObjectType>[];
}
type CompiledPathParam = {
paramName: string;
isOptional?: boolean;
};
declare class DataWithResponseInit<D> {
type: string;
data: D;
init: ResponseInit | null;
constructor(data: D, init?: ResponseInit);
}
/**
* Create "responses" that contain `headers`/`status` without forcing
* serialization into an actual [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
*
* @example
* import { data } from "react-router";
*
* export async function action({ request }: Route.ActionArgs) {
* let formData = await request.formData();
* let item = await createItem(formData);
* return data(item, {
* headers: { "X-Custom-Header": "value" }
* status: 201,
* });
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param data The data to be included in the response.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A {@link DataWithResponseInit} instance containing the data and
* response init.
*/
declare function data<D>(data: D, init?: number | ResponseInit): DataWithResponseInit<D>;
type RedirectFunction = (url: string, init?: number | ResponseInit) => Response;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
* Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* This utility accepts absolute URLs and can navigate to external domains, so
* the application should validate any user-supplied inputs to redirects.
*
* @example
* import { redirect } from "react-router";
*
* export async function loader({ request }: Route.LoaderArgs) {
* if (!isLoggedIn(request))
* throw redirect("/login");
* }
*
* // ...
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const redirect$1: RedirectFunction;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that will force a document reload to the new location. Sets the status code
* and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* This utility accepts absolute URLs and can navigate to external domains, so
* the application should validate any user-supplied inputs to redirects.
*
* ```tsx filename=routes/logout.tsx
* import { redirectDocument } from "react-router";
*
* import { destroySession } from "../sessions.server";
*
* export async function action({ request }: Route.ActionArgs) {
* let session = await getSession(request.headers.get("Cookie"));
* return redirectDocument("/", {
* headers: { "Set-Cookie": await destroySession(session) }
* });
* }
* ```
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const redirectDocument$1: RedirectFunction;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that will perform a [`history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState)
* instead of a [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)
* for client-side navigation redirects. Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* @example
* import { replace } from "react-router";
*
* export async function loader() {
* return replace("/new-location");
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const replace$1: RedirectFunction;
type ErrorResponse = {
status: number;
statusText: string;
data: any;
};
/**
* Check if the given error is an {@link ErrorResponse} generated from a 4xx/5xx
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* thrown from an [`action`](../../start/framework/route-module#action) or
* [`loader`](../../start/framework/route-module#loader) function.
*
* @example
* import { isRouteErrorResponse } from "react-router";
*
* export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
* if (isRouteErrorResponse(error)) {
* return (
* <>
* <p>Error: `${error.status}: ${error.statusText}`</p>
* <p>{error.data}</p>
* </>
* );
* }
*
* return (
* <p>Error: {error instanceof Error ? error.message : "Unknown Error"}</p>
* );
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param error The error to check.
* @returns `true` if the error is an {@link ErrorResponse}, `false` otherwise.
*/
declare function isRouteErrorResponse(error: any): error is ErrorResponse;
/**
* An object of unknown type for route loaders and actions provided by the
* server's `getLoadContext()` function. This is defined as an empty interface
* specifically so apps can leverage declaration merging to augment this type
* globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
*/
interface AppLoadContext {
[key: string]: unknown;
}
type ServerInstrumentation = {
handler?: InstrumentRequestHandlerFunction;
route?: InstrumentRouteFunction;
};
type ClientInstrumentation = {
router?: InstrumentRouterFunction;
route?: InstrumentRouteFunction;
};
type InstrumentRequestHandlerFunction = (handler: InstrumentableRequestHandler) => void;
type InstrumentRouterFunction = (router: InstrumentableRouter) => void;
type InstrumentRouteFunction = (route: InstrumentableRoute) => void;
type InstrumentationHandlerResult = {
status: "success";
error: undefined;
} | {
status: "error";
error: Error;
};
type InstrumentFunction<T> = (handler: () => Promise<InstrumentationHandlerResult>, info: T) => Promise<void>;
type ReadonlyRequest = {
method: string;
url: string;
headers: Pick<Headers, "get">;
};
type ReadonlyContext = MiddlewareEnabled extends true ? Pick<RouterContextProvider, "get"> : Readonly<AppLoadContext>;
type InstrumentableRoute = {
id: string;
index: boolean | undefined;
path: string | undefined;
instrument(instrumentations: RouteInstrumentations): void;
};
type RouteInstrumentations = {
lazy?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.loader"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.action"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.middleware"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
middleware?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
loader?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
action?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
};
type RouteLazyInstrumentationInfo = undefined;
type RouteHandlerInstrumentationInfo = Readonly<{
request: ReadonlyRequest;
params: LoaderFunctionArgs["params"];
pattern: string;
context: ReadonlyContext;
}>;
type InstrumentableRouter = {
instrument(instrumentations: RouterInstrumentations): void;
};
type RouterInstrumentations = {
navigate?: InstrumentFunction<RouterNavigationInstrumentationInfo>;
fetch?: InstrumentFunction<RouterFetchInstrumentationInfo>;
};
type RouterNavigationInstrumentationInfo = Readonly<{
to: string | number;
currentUrl: string;
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
formData?: FormData;
body?: any;
}>;
type RouterFetchInstrumentationInfo = Readonly<{
href: string;
currentUrl: string;
fetcherKey: string;
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
formData?: FormData;
body?: any;
}>;
type InstrumentableRequestHandler = {
instrument(instrumentations: RequestHandlerInstrumentations): void;
};
type RequestHandlerInstrumentations = {
request?: InstrumentFunction<RequestHandlerInstrumentationInfo>;
};
type RequestHandlerInstrumentationInfo = Readonly<{
request: ReadonlyRequest;
context: ReadonlyContext | undefined;
}>;
/**
* A Router instance manages all navigation and data loading/mutations
*/
interface Router {
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the basename for the router
*/
get basename(): RouterInit["basename"];
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the future config for the router
*/
get future(): FutureConfig;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the current state of the router
*/
get state(): RouterState;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the routes for this router instance
*/
get routes(): DataRouteObject[];
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the route branches for this router instance
*/
get branches(): RouteBranch<DataRouteObject>[] | undefined;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the manifest for this router instance
*/
get manifest(): RouteManifest;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the window associated with the router
*/
get window(): RouterInit["window"];
/**
* @private
* PRIVATE - DO NOT USE
*
* Initialize the router, including adding history listeners and kicking off
* initial data fetches. Returns a function to cleanup listeners and abort
* any in-progress loads
*/
initialize(): Router;
/**
* @private
* PRIVATE - DO NOT USE
*
* Subscribe to router.state updates
*
* @param fn function to call with the new state
*/
subscribe(fn: RouterSubscriber): () => void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Enable scroll restoration behavior in the router
*
* @param savedScrollPositions Object that will manage positions, in case
* it's being restored from sessionStorage
* @param getScrollPosition Function to get the active Y scroll position
* @param getKey Function to get the key to use for restoration
*/
enableScrollRestoration(savedScrollPositions: Record<string, number>, getScrollPosition: GetScrollPositionFunction, getKey?: GetScrollRestorationKeyFunction): () => void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Navigate forward/backward in the history stack
* @param to Delta to move in the history stack
*/
navigate(to: number): Promise<void>;
/**
* Navigate to the given path
* @param to Path to navigate to
* @param opts Navigation options (method, submission, etc.)
*/
navigate(to: To | null, opts?: RouterNavigateOptions): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Trigger a fetcher load/submission
*
* @param key Fetcher key
* @param routeId Route that owns the fetcher
* @param href href to fetch
* @param opts Fetcher options, (method, submission, etc.)
*/
fetch(key: string, routeId: string, href: string | null, opts?: RouterFetchOptions): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Trigger a revalidation of all current route loaders and fetcher loads
*/
revalidate(): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Utility function to create an href for the given location
* @param location
*/
createHref(location: Location | URL): string;
/**
* @private
* PRIVATE - DO NOT USE
*
* Utility function to URL encode a destination path according to the internal
* history implementation
* @param to
*/
encodeLocation(to: To): Path;
/**
* @private
* PRIVATE - DO NOT USE
*
* Get/create a fetcher for the given key
* @param key
*/
getFetcher<TData = any>(key: string): Fetcher<TData>;
/**
* @internal
* PRIVATE - DO NOT USE
*
* Reset the fetcher for a given key
* @param key
*/
resetFetcher(key: string, opts?: {
reason?: unknown;
}): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Delete the fetcher for a given key
* @param key
*/
deleteFetcher(key: string): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Cleanup listeners and abort any in-progress loads
*/
dispose(): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Get a navigation blocker
* @param key The identifier for the blocker
* @param fn The blocker function implementation
*/
getBlocker(key: string, fn: BlockerFunction): Blocker;
/**
* @private
* PRIVATE - DO NOT USE
*
* Delete a navigation blocker
* @param key The identifier for the blocker
*/
deleteBlocker(key: string): void;
/**
* @private
* PRIVATE DO NOT USE
*
* Patch additional children routes into an existing parent route
* @param routeId The parent route id or a callback function accepting `patch`
* to perform batch patching
* @param children The additional children routes
* @param unstable_allowElementMutations Allow mutation or route elements on
* existing routes. Intended for RSC-usage
* only.
*/
patchRoutes(routeId: string | null, children: RouteObject[], unstable_allowElementMutations?: boolean): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* HMR needs to pass in-flight route updates to React Router
* TODO: Replace this with granular route update APIs (addRoute, updateRoute, deleteRoute)
*/
_internalSetRoutes(routes: RouteObject[]): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Cause subscribers to re-render. This is used to force a re-render.
*/
_internalSetStateDoNotUseOrYouWillBreakYourApp(state: Partial<RouterState>): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Internal fetch AbortControllers accessed by unit tests
*/
_internalFetchControllers: Map<string, AbortController>;
}
/**
* State maintained internally by the router. During a navigation, all states
* reflect the "old" location unless otherwise noted.
*/
interface RouterState {
/**
* The action of the most recent navigation
*/
historyAction: Action;
/**
* The current location reflected by the router
*/
location: Location;
/**
* The current set of route matches
*/
matches: DataRouteMatch[];
/**
* Tracks whether we've completed our initial data load
*/
initialized: boolean;
/**
* Tracks whether we should be rendering a HydrateFallback during hydration
*/
renderFallback: boolean;
/**
* Current scroll position we should start at for a new view
* - number -> scroll position to restore to
* - false -> do not restore scroll at all (used during submissions/revalidations)
* - null -> don't have a saved position, scroll to hash or top of page
*/
restoreScrollPosition: number | false | null;
/**
* Indicate whether this navigation should skip resetting the scroll position
* if we are unable to restore the scroll position
*/
preventScrollReset: boolean;
/**
* Tracks the state of the current navigation
*/
navigation: Navigation;
/**
* Tracks any in-progress revalidations
*/
revalidation: RevalidationState;
/**
* Data from the loaders for the current matches
*/
loaderData: RouteData;
/**
* Data from the action for the current matches
*/
actionData: RouteData | null;
/**
* Errors caught from loaders for the current matches
*/
errors: RouteData | null;
/**
* Map of current fetchers
*/
fetchers: Map<string, Fetcher>;
/**
* Map of current blockers
*/
blockers: Map<string, Blocker>;
}
/**
* Data that can be passed into hydrate a Router from SSR
*/
type HydrationState = Partial<Pick<RouterState, "loaderData" | "actionData" | "errors">>;
/**
* Future flags to toggle new feature behavior
*/
interface FutureConfig {
}
/**
* Initialization options for createRouter
*/
interface RouterInit {
routes: RouteObject[];
history: History;
basename?: string;
getContext?: () => MaybePromise<RouterContextProvider>;
instrumentations?: ClientInstrumentation[];
mapRouteProperties?: MapRoutePropertiesFunction;
future?: Partial<FutureConfig>;
hydrationRouteProperties?: string[];
hydrationData?: HydrationState;
window?: Window;
dataStrategy?: DataStrategyFunction;
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
}
/**
* State returned from a server-side query() call
*/
interface StaticHandlerContext {
basename: Router["basename"];
location: RouterState["location"];
matches: RouterState["matches"];
loaderData: RouterState["loaderData"];
actionData: RouterState["actionData"];
errors: RouterState["errors"];
statusCode: number;
loaderHeaders: Record<string, Headers>;
actionHeaders: Record<string, Headers>;
_deepestRenderedBoundaryId?: string | null;
}
/**
* A StaticHandler instance manages a singular SSR navigation/fetch event
*/
interface StaticHandler {
/**
* The set of data routes managed by this handler
*/
dataRoutes: DataRouteObject[];
/**
* @private
* PRIVATE - DO NOT USE
*
* The route branches derived from the data routes, used for internal route
* matching in Framework Mode
*/
_internalRouteBranches: RouteBranch<DataRouteObject>[];
/**
* Perform a query for a given request - executing all matched route
* loaders/actions. Used for document requests.
*
* @param request The request to query
* @param opts Optional query options
* @param opts.dataStrategy Alternate dataStrategy implementation
* @param opts.filterMatchesToLoad Predicate function to filter which matches should be loaded
* @param opts.generateMiddlewareResponse To enable middleware, provide a function
* to generate a response to bubble back up the middleware chain
* @param opts.requestContext Context object to pass to loaders/actions
* @param opts.skipLoaderErrorBubbling Skip loader error bubbling
* @param opts.skipRevalidation Skip revalidation after action submission
* @param opts.normalizePath Normalize the request path
*/
query(request: Request, opts?: {
requestContext?: unknown;
filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
skipLoaderErrorBubbling?: boolean;
skipRevalidation?: boolean;
dataStrategy?: DataStrategyFunction<unknown>;
generateMiddlewareResponse?: (query: (r: Request, args?: {
filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
}) => Promise<StaticHandlerContext | Response>) => MaybePromise<Response>;
normalizePath?: (request: Request) => Path;
}): Promise<StaticHandlerContext | Response>;
/**
* Perform a query for a specific route. Used for resource requests.
*
* @param request The request to query
* @param opts Optional queryRoute options
* @param opts.dataStrategy Alternate dataStrategy implementation
* @param opts.generateMiddlewareResponse To enable middleware, provide a function
* to generate a response to bubble back up the middleware chain
* @param opts.requestContext Context object to pass to loaders/actions
* @param opts.routeId The ID of the route to query
* @param opts.normalizePath Normalize the request path
*/
queryRoute(request: Request, opts?: {
routeId?: string;
requestContext?: unknown;
dataStrategy?: DataStrategyFunction<unknown>;
generateMiddlewareResponse?: (queryRoute: (r: Request) => Promise<Response>) => MaybePromise<Response>;
normalizePath?: (request: Request) => Path;
}): Promise<any>;
}
type ViewTransitionOpts = {
currentLocation: Location;
nextLocation: Location;
};
/**
* Subscriber function signature for changes to router state
*/
interface RouterSubscriber {
(state: RouterState, opts: {
deletedFetchers: string[];
newErrors: RouteData | null;
viewTransitionOpts?: ViewTransitionOpts;
flushSync: boolean;
}): void;
}
/**
* Function signature for determining the key to be used in scroll restoration
* for a given location
*/
interface GetScrollRestorationKeyFunction {
(location: Location, matches: UIMatch[]): string | null;
}
/**
* Function signature for determining the current scroll position
*/
interface GetScrollPositionFunction {
(): number;
}
/**
* - "route": relative to the route hierarchy so `..` means remove all segments
* of the current route even if it has many. For example, a `route("posts/:id")`
* would have both `:id` and `posts` removed from the url.
* - "path": relative to the pathname so `..` means remove one segment of the
* pathname. For example, a `route("posts/:id")` would have only `:id` removed
* from the url.
*/
type RelativeRoutingType = "route" | "path";
type BaseNavigateOrFetchOptions = {
preventScrollReset?: boolean;
relative?: RelativeRoutingType;
flushSync?: boolean;
defaultShouldRevalidate?: boolean;
};
type BaseNavigateOptions = BaseNavigateOrFetchOptions & {
replace?: boolean;
state?: any;
fromRouteId?: string;
viewTransition?: boolean;
mask?: To;
};
type BaseSubmissionOptions = {
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
} & ({
formData: FormData;
body?: undefined;
} | {
formData?: undefined;
body: any;
});
/**
* Options for a navigate() call for a normal (non-submission) navigation
*/
type LinkNavigateOptions = BaseNavigateOptions;
/**
* Options for a navigate() call for a submission navigation
*/
type SubmissionNavigateOptions = BaseNavigateOptions & BaseSubmissionOptions;
/**
* Options to pass to navigate() for a navigation
*/
type RouterNavigateOptions = LinkNavigateOptions | SubmissionNavigateOptions;
/**
* Options for a fetch() load
*/
type LoadFetchOptions = BaseNavigateOrFetchOptions;
/**
* Options for a fetch() submission
*/
type SubmitFetchOptions = BaseNavigateOrFetchOptions & BaseSubmissionOptions;
/**
* Options to pass to fetch()
*/
type RouterFetchOptions = LoadFetchOptions | SubmitFetchOptions;
/**
* Potential states for state.navigation
*/
type NavigationStates = {
Idle: {
state: "idle";
location: undefined;
matches: undefined;
historyAction: undefined;
formMethod: undefined;
formAction: undefined;
formEncType: undefined;
formData: undefined;
json: undefined;
text: undefined;
};
Loading: {
state: "loading";
location: Location;
matches: DataRouteMatch[];
historyAction: Action;
formMethod: Submission["formMethod"] | undefined;
formAction: Submission["formAction"] | undefined;
formEncType: Submission["formEncType"] | undefined;
formData: Submission["formData"] | undefined;
json: Submission["json"] | undefined;
text: Submission["text"] | undefined;
};
Submitting: {
state: "submitting";
location: Location;
matches: DataRouteMatch[];
historyAction: Action;
formMethod: Submission["formMethod"];
formAction: Submission["formAction"];
formEncType: Submission["formEncType"];
formData: Submission["formData"];
json: Submission["json"];
text: Submission["text"];
};
};
type Navigation = NavigationStates[keyof NavigationStates];
type RevalidationState = "idle" | "loading";
/**
* Potential states for fetchers
*/
type FetcherStates<TData = any> = {
/**
* The fetcher is not calling a loader or action
*
* ```tsx
* fetcher.state === "idle"
* ```
*/
Idle: {
state: "idle";
formMethod: undefined;
formAction: undefined;
formEncType: undefined;
text: undefined;
formData: undefined;
json: undefined;
/**
* If the fetcher has never been called, this will be undefined.
*/
data: TData | undefined;
};
/**
* The fetcher is loading data from a {@link LoaderFunction | loader} from a
* call to {@link FetcherWithComponents.load | `fetcher.load`}.
*
* ```tsx
* // somewhere
* <button onClick={() => fetcher.load("/some/route") }>Load</button>
*
* // the state will update
* fetcher.state === "loading"
* ```
*/
Loading: {
state: "loading";
formMethod: Submission["formMethod"] | undefined;
formAction: Submission["formAction"] | undefined;
formEncType: Submission["formEncType"] | undefined;
text: Submission["text"] | undefined;
formData: Submission["formData"] | undefined;
json: Submission["json"] | undefined;
data: TData | undefined;
};
/**
The fetcher is submitting to a {@link LoaderFunction} (GET) or {@link ActionFunction} (POST) from a {@link FetcherWithComponents.Form | `fetcher.Form`} or {@link FetcherWithComponents.submit | `fetcher.submit`}.
```tsx
// somewhere
<input
onChange={e => {
fetcher.submit(event.currentTarget.form, { method: "post" });
}}
/>
// the state will update
fetcher.state === "submitting"
// and formData will be available
fetcher.formData
```
*/
Submitting: {
state: "submitting";
formMethod: Submission["formMethod"];
formAction: Submission["formAction"];
formEncType: Submission["formEncType"];
text: Submission["text"];
formData: Submission["formData"];
json: Submission["json"];
data: TData | undefined;
};
};
type Fetcher<TData = any> = FetcherStates<TData>[keyof FetcherStates<TData>];
interface BlockerBlocked {
state: "blocked";
reset: () => void;
proceed: () => void;
location: Location;
}
interface BlockerUnblocked {
state: "unblocked";
reset: undefined;
proceed: undefined;
location: undefined;
}
interface BlockerProceeding {
state: "proceeding";
reset: undefined;
proceed: undefined;
location: Location;
}
type Blocker = BlockerUnblocked | BlockerBlocked | BlockerProceeding;
type BlockerFunction = (args: {
currentLocation: Location;
nextLocation: Location;
historyAction: Action;
}) => boolean;
interface CreateStaticHandlerOptions {
basename?: string;
mapRouteProperties?: MapRoutePropertiesFunction;
instrumentations?: Pick<ServerInstrumentation, "route">[];
future?: Partial<FutureConfig>;
}
declare function createStaticHandler(routes: RouteObject[], opts?: CreateStaticHandlerOptions): StaticHandler;
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>);
interface HtmlLinkProps {
/**
* Address of the hyperlink
*/
href?: string;
/**
* How the element handles crossorigin requests
*/
crossOrigin?: "anonymous" | "use-credentials";
/**
* Relationship between the document containing the hyperlink and the destination resource
*/
rel: LiteralUnion<"alternate" | "dns-prefetch" | "icon" | "manifest" | "modulepreload" | "next" | "pingback" | "preconnect" | "prefetch" | "preload" | "prerender" | "search" | "stylesheet", string>;
/**
* Applicable media: "screen", "print", "(max-width: 764px)"
*/
media?: string;
/**
* Integrity metadata used in Subresource Integrity checks
*/
integrity?: string;
/**
* Language of the linked resource
*/
hrefLang?: string;
/**
* Hint for the type of the referenced resource
*/
type?: string;
/**
* Referrer policy for fetches initiated by the element
*/
referrerPolicy?: "" | "no-referrer" | "no-referrer-when-downgrade" | "same-origin" | "origin" | "strict-origin" | "origin-when-cross-origin" | "strict-origin-when-cross-origin" | "unsafe-url";
/**
* Sizes of the icons (for rel="icon")
*/
sizes?: string;
/**
* Potential destination for a preload request (for rel="preload" and rel="modulepreload")
*/
as?: LiteralUnion<"audio" | "audioworklet" | "document" | "embed" | "fetch" | "font" | "frame" | "iframe" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt", string>;
/**
* Color to use when customizing a site's icon (for rel="mask-icon")
*/
color?: string;
/**
* Whether the link is disabled
*/
disabled?: boolean;
/**
* The title attribute has special semantics on this element: Title of the link; CSS style sheet set name.
*/
title?: string;
/**
* Images to use in different situations, e.g., high-resolution displays,
* small monitors, etc. (for rel="preload")
*/
imageSrcSet?: string;
/**
* Image sizes for different page layouts (for rel="preload")
*/
imageSizes?: string;
}
interface HtmlLinkPreloadImage extends HtmlLinkProps {
/**
* Relationship between the document containing the hyperlink and the destination resource
*/
rel: "preload";
/**
* Potential destination for a preload request (for rel="preload" and rel="modulepreload")
*/
as: "image";
/**
* Address of the hyperlink
*/
href?: string;
/**
* Images to use in different situations, e.g., high-resolution displays,
* small monitors, etc. (for rel="preload")
*/
imageSrcSet: string;
/**
* Image sizes for different page layouts (for rel="preload")
*/
imageSizes?: string;
}
/**
* Represents a `<link>` element.
*
* WHATWG Specification: https://html.spec.whatwg.org/multipage/semantics.html#the-link-element
*/
type HtmlLinkDescriptor = (HtmlLinkProps & Pick<Required<HtmlLinkProps>, "href">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "imageSizes">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "href"> & {
imageSizes?: never;
});
interface PageLinkDescriptor extends Omit<HtmlLinkDescriptor, "href" | "rel" | "type" | "sizes" | "imageSrcSet" | "imageSizes" | "as" | "color" | "title"> {
/**
* A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* element. If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*/
nonce?: string | undefined;
/**
* The absolute path of the page to prefetch, e.g. `/absolute/path`.
*/
page: string;
}
type LinkDescriptor = HtmlLinkDescriptor | PageLinkDescriptor;
type Serializable = undefined | null | boolean | string | symbol | number | Array<Serializable> | {
[key: PropertyKey]: Serializable;
} | bigint | Date | URL | RegExp | Error | Map<Serializable, Serializable> | Set<Serializable> | Promise<Serializable>;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;
type IsAny<T> = 0 extends 1 & T ? true : false;
type Func = (...args: any[]) => unknown;
/**
* A brand that can be applied to a type to indicate that it will serialize
* to a specific type when transported to the client from a loader.
* Only use this if you have additional serialization/deserialization logic
* in your application.
*/
type unstable_SerializesTo<T> = {
unstable__ReactRouter_SerializesTo: [T];
};
type Serialize<T> = T extends unstable_SerializesTo<infer To> ? To : T extends Serializable ? T : T extends (...args: any[]) => unknown ? undefined : T extends Promise<infer U> ? Promise<Serialize<U>> : T extends Map<infer K, infer V> ? Map<Serialize<K>, Serialize<V>> : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<Serialize<K>, Serialize<V>> : T extends Set<infer U> ? Set<Serialize<U>> : T extends ReadonlySet<infer U> ? ReadonlySet<Serialize<U>> : T extends [] ? [] : T extends readonly [infer F, ...infer R] ? [Serialize<F>, ...Serialize<R>] : T extends Array<infer U> ? Array<Serialize<U>> : T extends readonly unknown[] ? readonly Serialize<T[number]>[] : T extends Record<any, any> ? {
[K in keyof T]: Serialize<T[K]>;
} : undefined;
type VoidToUndefined<T> = Equal<T, void> extends true ? undefined : T;
type DataFrom<T> = IsAny<T> extends true ? undefined : T extends Func ? VoidToUndefined<Awaited<ReturnType<T>>> : undefined;
type ClientData<T> = T extends Response ? never : T extends DataWithResponseInit<infer U> ? U : T;
type ServerData<T> = T extends Response ? never : T extends DataWithResponseInit<infer U> ? Serialize<U> : Serialize<T>;
type ServerDataFrom<T> = ServerData<DataFrom<T>>;
type ClientDataFrom<T> = ClientData<DataFrom<T>>;
type ClientDataFunctionArgs<Params> = {
/**
* A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read the URL, the method, the "content-type" header, and the request body from the request.
*
* @note Because client data functions are called before a network request is made, the Request object does not include the headers which the browser automatically adds. React Router infers the "content-type" header from the enc-type of the form that performed the submission.
**/
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function clientLoader({
* params,
* }: Route.ClientLoaderArgs) {
* params.teamId;
* // ^ string
* }
**/
params: Params;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* When `future.v8_middleware` is not enabled, this is undefined.
*
* When `future.v8_middleware` is enabled, this is an instance of
* `RouterContextProvider` and can be used to access context values
* from your route middlewares. You may pass in initial context values in your
* `<HydratedRouter getContext>` prop
*/
context: Readonly<RouterContextProvider>;
};
type SerializeFrom<T> = T extends (...args: infer Args) => unknown ? Args extends [
ClientLoaderFunctionArgs | ClientActionFunctionArgs | ClientDataFunctionArgs<unknown>
] ? ClientDataFrom<T> : ServerDataFrom<T> : T;
/**
* A function that handles data mutations for a route on the client
*/
type ClientActionFunction = (args: ClientActionFunctionArgs) => ReturnType<ActionFunction>;
/**
* Arguments passed to a route `clientAction` function
*/
type ClientActionFunctionArgs = ActionFunctionArgs & {
serverAction: <T = unknown>() => Promise<SerializeFrom<T>>;
};
/**
* A function that loads data for a route on the client
*/
type ClientLoaderFunction = ((args: ClientLoaderFunctionArgs) => ReturnType<LoaderFunction>) & {
hydrate?: boolean;
};
/**
* Arguments passed to a route `clientLoader` function
*/
type ClientLoaderFunctionArgs = LoaderFunctionArgs & {
serverLoader: <T = unknown>() => Promise<SerializeFrom<T>>;
};
type HeadersArgs = {
loaderHeaders: Headers;
parentHeaders: Headers;
actionHeaders: Headers;
errorHeaders: Headers | undefined;
};
/**
* A function that returns HTTP headers to be used for a route. These headers
* will be merged with (and take precedence over) headers from parent routes.
*/
interface HeadersFunction {
(args: HeadersArgs): Headers | HeadersInit;
}
/**
* A function that defines `<link>` tags to be inserted into the `<head>` of
* the document on route transitions.
*
* @see https://reactrouter.com/start/framework/route-module#meta
*/
interface LinksFunction {
(): LinkDescriptor[];
}
interface MetaMatch<RouteId extends string = string, Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown> {
id: RouteId;
pathname: DataRouteMatch["pathname"];
/** @deprecated Use `MetaMatch.loaderData` instead */
data: Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown;
loaderData: Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown;
handle?: RouteHandle;
params: DataRouteMatch["params"];
meta: MetaDescriptor[];
error?: unknown;
}
type MetaMatches<MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> = Array<{
[K in keyof MatchLoaders]: MetaMatch<Exclude<K, number | symbol>, MatchLoaders[K]>;
}[keyof MatchLoaders]>;
interface MetaArgs<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> {
/** @deprecated Use `MetaArgs.loaderData` instead */
data: (Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown) | undefined;
loaderData: (Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown) | undefined;
params: Params;
location: Location;
matches: MetaMatches<MatchLoaders>;
error?: unknown;
}
/**
* A function that returns an array of data objects to use for rendering
* metadata HTML tags in a route. These tags are not rendered on descendant
* routes in the route hierarchy. In other words, they will only be rendered on
* the route in which they are exported.
*
* @param Loader - The type of the current route's loader function
* @param MatchLoaders - Mapping from a parent route's filepath to its loader
* function type
*
* Note that parent route filepaths are relative to the `app/` directory.
*
* For example, if this meta function is for `/sales/customers/$customerId`:
*
* ```ts
* // app/root.tsx
* const loader = () => ({ hello: "world" })
* export type Loader = typeof loader
*
* // app/routes/sales.tsx
* const loader = () => ({ salesCount: 1074 })
* export type Loader = typeof loader
*
* // app/routes/sales/customers.tsx
* const loader = () => ({ customerCount: 74 })
* export type Loader = typeof loader
*
* // app/routes/sales/customers/$customersId.tsx
* import type { Loader as RootLoader } from "../../../root"
* import type { Loader as SalesLoader } from "../../sales"
* import type { Loader as CustomersLoader } from "../../sales/customers"
*
* const loader = () => ({ name: "Customer name" })
*
* const meta: MetaFunction<typeof loader, {
* "root": RootLoader,
* "routes/sales": SalesLoader,
* "routes/sales/customers": CustomersLoader,
* }> = ({ data, matches }) => {
* const { name } = data
* // ^? string
* const { customerCount } = matches.find((match) => match.id === "routes/sales/customers").data
* // ^? number
* const { salesCount } = matches.find((match) => match.id === "routes/sales").data
* // ^? number
* const { hello } = matches.find((match) => match.id === "root").data
* // ^? "world"
* }
* ```
*/
interface MetaFunction<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> {
(args: MetaArgs<Loader, MatchLoaders>): MetaDescriptor[] | undefined;
}
type MetaDescriptor = {
charSet: "utf-8";
} | {
title: string;
} | {
name: string;
content: string;
} | {
property: string;
content: string;
} | {
httpEquiv: string;
content: string;
} | {
"script:ld+json": LdJsonObject | LdJsonObject[];
} | {
tagName: "meta" | "link";
[name: string]: string;
} | {
[name: string]: unknown;
};
type LdJsonObject = {
[Key in string]: LdJsonValue;
} & {
[Key in string]?: LdJsonValue | undefined;
};
type LdJsonArray = LdJsonValue[] | readonly LdJsonValue[];
type LdJsonPrimitive = string | number | boolean | null;
type LdJsonValue = LdJsonPrimitive | LdJsonObject | LdJsonArray;
/**
* An arbitrary object that is associated with a route.
*
* @see https://reactrouter.com/how-to/using-handle
*/
type RouteHandle = unknown;
interface AwaitResolveRenderFunction<Resolve = any> {
(data: Awaited<Resolve>): React.ReactNode;
}
/**
* @category Types
*/
interface AwaitProps<Resolve> {
/**
* When using a function, the resolved value is provided as the parameter.
*
* ```tsx [2]
* <Await resolve={reviewsPromise}>
* {(resolvedReviews) => <Reviews items={resolvedReviews} />}
* </Await>
* ```
*
* When using React elements, {@link useAsyncValue} will provide the
* resolved value:
*
* ```tsx [2]
* <Await resolve={reviewsPromise}>
* <Reviews />
* </Await>
*
* function Reviews() {
* const resolvedReviews = useAsyncValue();
* return <div>...</div>;
* }
* ```
*/
children: React.ReactNode | AwaitResolveRenderFunction<Resolve>;
/**
* The error element renders instead of the `children` when the [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* rejects.
*
* ```tsx
* <Await
* errorElement={<div>Oops</div>}
* resolve={reviewsPromise}
* >
* <Reviews />
* </Await>
* ```
*
* To provide a more contextual error, you can use the {@link useAsyncError} in a
* child component
*
* ```tsx
* <Await
* errorElement={<ReviewsError />}
* resolve={reviewsPromise}
* >
* <Reviews />
* </Await>
*
* function ReviewsError() {
* const error = useAsyncError();
* return <div>Error loading reviews: {error.message}</div>;
* }
* ```
*
* If you do not provide an `errorElement`, the rejected value will bubble up
* to the nearest route-level [`ErrorBoundary`](../../start/framework/route-module#errorboundary)
* and be accessible via the {@link useRouteError} hook.
*/
errorElement?: React.ReactNode;
/**
* Takes a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* returned from a [`loader`](../../start/framework/route-module#loader) to be
* resolved and rendered.
*
* ```tsx
* import { Await, useLoaderData } from "react-router";
*
* export async function loader() {
* let reviews = getReviews(); // not awaited
* let book = await getBook();
* return {
* book,
* reviews, // this is a promise
* };
* }
*
* export default function Book() {
* const {
* book,
* reviews, // this is the same promise
* } = useLoaderData();
*
* return (
* <div>
* <h1>{book.title}</h1>
* <p>{book.description}</p>
* <React.Suspense fallback={<ReviewsSkeleton />}>
* <Await
* // and is the promise we pass to Await
* resolve={reviews}
* >
* <Reviews />
* </Await>
* </React.Suspense>
* </div>
* );
* }
* ```
*/
resolve: Resolve;
}
/**
* Used to render promise values with automatic error handling.
*
* **Note:** `<Await>` expects to be rendered inside a [`<React.Suspense>`](https://react.dev/reference/react/Suspense)
*
* @example
* import { Await, useLoaderData } from "react-router";
*
* export async function loader() {
* // not awaited
* const reviews = getReviews();
* // awaited (blocks the transition)
* const book = await fetch("/api/book").then((res) => res.json());
* return { book, reviews };
* }
*
* function Book() {
* const { book, reviews } = useLoaderData();
* return (
* <div>
* <h1>{book.title}</h1>
* <p>{book.description}</p>
* <React.Suspense fallback={<ReviewsSkeleton />}>
* <Await
* resolve={reviews}
* errorElement={
* <div>Could not load reviews 😬</div>
* }
* children={(resolvedReviews) => (
* <Reviews items={resolvedReviews} />
* )}
* />
* </React.Suspense>
* </div>
* );
* }
*
* @public
* @category Components
* @mode framework
* @mode data
* @param props Props
* @param {AwaitProps.children} props.children n/a
* @param {AwaitProps.errorElement} props.errorElement n/a
* @param {AwaitProps.resolve} props.resolve n/a
* @returns React element for the rendered awaited value
*/
declare function Await$1<Resolve>({ children, errorElement, resolve, }: AwaitProps<Resolve>): React.JSX.Element;
declare function getRequest(): Request;
declare const redirect: typeof redirect$1;
declare const redirectDocument: typeof redirectDocument$1;
declare const replace: typeof replace$1;
declare const Await: typeof Await$1;
type RSCRouteConfigEntryBase = {
action?: ActionFunction;
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
ErrorBoundary?: React.ComponentType<any>;
handle?: any;
headers?: HeadersFunction;
HydrateFallback?: React.ComponentType<any>;
Layout?: React.ComponentType<any>;
links?: LinksFunction;
loader?: LoaderFunction;
meta?: MetaFunction;
shouldRevalidate?: ShouldRevalidateFunction;
};
type RSCRouteConfigEntry = RSCRouteConfigEntryBase & {
id: string;
path?: string;
Component?: React.ComponentType<any>;
lazy?: () => Promise<RSCRouteConfigEntryBase & ({
default?: React.ComponentType<any>;
Component?: never;
} | {
default?: never;
Component?: React.ComponentType<any>;
})>;
} & ({
index: true;
} | {
children?: RSCRouteConfigEntry[];
});
type RSCRouteConfig = Array<RSCRouteConfigEntry>;
type RSCRouteManifest = {
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
element?: React.ReactElement | false;
errorElement?: React.ReactElement;
handle?: any;
hasAction: boolean;
hasComponent: boolean;
hasErrorBoundary: boolean;
hasLoader: boolean;
hydrateFallbackElement?: React.ReactElement;
id: string;
index?: boolean;
links?: LinksFunction;
meta?: MetaFunction;
parentId?: string;
path?: string;
shouldRevalidate?: ShouldRevalidateFunction;
};
type RSCRouteMatch = RSCRouteManifest & {
params: Params;
pathname: string;
pathnameBase: string;
};
type RSCRenderPayload = {
type: "render";
actionData: Record<string, any> | null;
basename: string | undefined;
errors: Record<string, any> | null;
loaderData: Record<string, any>;
location: Location;
routeDiscovery: RouteDiscovery;
matches: RSCRouteMatch[];
patches?: Promise<RSCRouteManifest[]>;
nonce?: string;
formState?: unknown;
};
type RSCManifestPayload = {
type: "manifest";
patches: Promise<RSCRouteManifest[]>;
};
type RSCActionPayload = {
type: "action";
actionResult: Promise<unknown>;
rerender?: Promise<RSCRenderPayload | RSCRedirectPayload>;
};
type RSCRedirectPayload = {
type: "redirect";
status: number;
location: string;
replace: boolean;
reload: boolean;
actionResult?: Promise<unknown>;
};
type RSCPayload = RSCRenderPayload | RSCManifestPayload | RSCActionPayload | RSCRedirectPayload;
type RSCMatch = {
statusCode: number;
headers: Headers;
payload: RSCPayload;
};
type DecodeActionFunction = (formData: FormData) => Promise<() => Promise<unknown>>;
type DecodeFormStateFunction = (result: unknown, formData: FormData) => unknown;
type DecodeReplyFunction = (reply: FormData | string, options: {
temporaryReferences: unknown;
}) => Promise<unknown[]>;
type LoadServerActionFunction = (id: string) => Promise<Function>;
type RouteDiscovery = {
mode: "lazy";
manifestPath?: string | undefined;
} | {
mode: "initial";
};
/**
* Matches the given routes to a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* and returns an [RSC](https://react.dev/reference/rsc/server-components)
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* encoding an {@link unstable_RSCPayload} for consumption by an [RSC](https://react.dev/reference/rsc/server-components)
* enabled client router.
*
* @example
* import {
* createTemporaryReferenceSet,
* decodeAction,
* decodeReply,
* loadServerAction,
* renderToReadableStream,
* } from "@vitejs/plugin-rsc/rsc";
* import { unstable_matchRSCServerRequest as matchRSCServerRequest } from "react-router";
*
* matchRSCServerRequest({
* createTemporaryReferenceSet,
* decodeAction,
* decodeFormState,
* decodeReply,
* loadServerAction,
* request,
* routes: routes(),
* generateResponse(match) {
* return new Response(
* renderToReadableStream(match.payload),
* {
* status: match.statusCode,
* headers: match.headers,
* }
* );
* },
* });
*
* @name unstable_matchRSCServerRequest
* @public
* @category RSC
* @mode data
* @param opts Options
* @param opts.allowedActionOrigins Origin patterns that are allowed to execute actions.
* @param opts.basename The basename to use when matching the request.
* @param opts.createTemporaryReferenceSet A function that returns a temporary
* reference set for the request, used to track temporary references in the [RSC](https://react.dev/reference/rsc/server-components)
* stream.
* @param opts.decodeAction Your `react-server-dom-xyz/server`'s `decodeAction`
* function, responsible for loading a server action.
* @param opts.decodeFormState A function responsible for decoding form state for
* progressively enhanceable forms with React's [`useActionState`](https://react.dev/reference/react/useActionState)
* using your `react-server-dom-xyz/server`'s `decodeFormState`.
* @param opts.decodeReply Your `react-server-dom-xyz/server`'s `decodeReply`
* function, used to decode the server function's arguments and bind them to the
* implementation for invocation by the router.
* @param opts.generateResponse A function responsible for using your
* `renderToReadableStream` to generate a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* encoding the {@link unstable_RSCPayload}.
* @param opts.loadServerAction Your `react-server-dom-xyz/server`'s
* `loadServerAction` function, used to load a server action by ID.
* @param opts.onError An optional error handler that will be called with any
* errors that occur during the request processing.
* @param opts.request The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* to match against.
* @param opts.requestContext An instance of {@link RouterContextProvider}
* that should be created per request, to be passed to [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* @param opts.routeDiscovery The route discovery configuration, used to determine how the router should discover new routes during navigations.
* @param opts.routes Your {@link unstable_RSCRouteConfigEntry | route definitions}.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that contains the [RSC](https://react.dev/reference/rsc/server-components)
* data for hydration.
*/
declare function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse, }: {
allowedActionOrigins?: string[];
createTemporaryReferenceSet: () => unknown;
basename?: string;
decodeReply?: DecodeReplyFunction;
decodeAction?: DecodeActionFunction;
decodeFormState?: DecodeFormStateFunction;
requestContext?: RouterContextProvider;
loadServerAction?: LoadServerActionFunction;
onError?: (error: unknown) => void;
request: Request;
routes: RSCRouteConfigEntry[];
routeDiscovery?: RouteDiscovery;
generateResponse: (match: RSCMatch, { onError, temporaryReferences, }: {
onError(error: unknown): string | undefined;
temporaryReferences: unknown;
}) => Response;
}): Promise<Response>;
/**
* Apps can use this interface to "register" app-wide types for React Router via interface declaration merging and module augmentation.
* React Router should handle this for you via type generation.
*
* For more on declaration merging and module augmentation, see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation .
*/
interface Register {
}
type AnyParams = Record<string, string | undefined>;
type AnyPages = Record<string, {
params: AnyParams;
}>;
type Pages = Register extends {
pages: infer Registered extends AnyPages;
} ? Registered : AnyPages;
type Args = {
[K in keyof Pages]: ToArgs<Pages[K]["params"]>;
};
type ToArgs<Params extends Record<string, string | undefined>> = Equal<Params, {}> extends true ? [] : Partial<Params> extends Params ? [Params] | [] : [
Params
];
/**
Returns a resolved URL path for the specified route.
```tsx
const h = href("/:lang?/about", { lang: "en" })
// -> `/en/about`
<Link to={href("/products/:id", { id: "abc123" })} />
```
*/
declare function href<Path extends keyof Args>(path: Path, ...args: Args[Path]): string;
interface CookieSignatureOptions {
/**
* An array of secrets that may be used to sign/unsign the value of a cookie.
*
* The array makes it easy to rotate secrets. New secrets should be added to
* the beginning of the array. `cookie.serialize()` will always use the first
* value in the array, but `cookie.parse()` may use any of them so that
* cookies that were signed with older secrets still work.
*/
secrets?: string[];
}
type CookieOptions = ParseOptions & SerializeOptions & CookieSignatureOptions;
/**
* A HTTP cookie.
*
* A Cookie is a logical container for metadata about a HTTP cookie; its name
* and options. But it doesn't contain a value. Instead, it has `parse()` and
* `serialize()` methods that allow a single instance to be reused for
* parsing/encoding multiple different values.
*
* @see https://remix.run/utils/cookies#cookie-api
*/
interface Cookie {
/**
* The name of the cookie, used in the `Cookie` and `Set-Cookie` headers.
*/
readonly name: string;
/**
* True if this cookie uses one or more secrets for verification.
*/
readonly isSigned: boolean;
/**
* The Date this cookie expires.
*
* Note: This is calculated at access time using `maxAge` when no `expires`
* option is provided to `createCookie()`.
*/
readonly expires?: Date;
/**
* Parses a raw `Cookie` header and returns the value of this cookie or
* `null` if it's not present.
*/
parse(cookieHeader: string | null, options?: ParseOptions): Promise<any>;
/**
* Serializes the given value to a string and returns the `Set-Cookie`
* header.
*/
serialize(value: any, options?: SerializeOptions): Promise<string>;
}
/**
* Creates a logical container for managing a browser cookie from the server.
*/
declare const createCookie: (name: string, cookieOptions?: CookieOptions) => Cookie;
type IsCookieFunction = (object: any) => object is Cookie;
/**
* Returns true if an object is a Remix cookie container.
*
* @see https://remix.run/utils/cookies#iscookie
*/
declare const isCookie: IsCookieFunction;
/**
* An object of name/value pairs to be used in the session.
*/
interface SessionData {
[name: string]: any;
}
/**
* Session persists data across HTTP requests.
*
* @see https://reactrouter.com/explanation/sessions-and-cookies#sessions
*/
interface Session<Data = SessionData, FlashData = Data> {
/**
* A unique identifier for this session.
*
* Note: This will be the empty string for newly created sessions and
* sessions that are not backed by a database (i.e. cookie-based sessions).
*/
readonly id: string;
/**
* The raw data contained in this session.
*
* This is useful mostly for SessionStorage internally to access the raw
* session data to persist.
*/
readonly data: FlashSessionData<Data, FlashData>;
/**
* Returns `true` if the session has a value for the given `name`, `false`
* otherwise.
*/
has(name: (keyof Data | keyof FlashData) & string): boolean;
/**
* Returns the value for the given `name` in this session.
*/
get<Key extends (keyof Data | keyof FlashData) & string>(name: Key): (Key extends keyof Data ? Data[Key] : undefined) | (Key extends keyof FlashData ? FlashData[Key] : undefined) | undefined;
/**
* Sets a value in the session for the given `name`.
*/
set<Key extends keyof Data & string>(name: Key, value: Data[Key]): void;
/**
* Sets a value in the session that is only valid until the next `get()`.
* This can be useful for temporary values, like error messages.
*/
flash<Key extends keyof FlashData & string>(name: Key, value: FlashData[Key]): void;
/**
* Removes a value from the session.
*/
unset(name: keyof Data & string): void;
}
type FlashSessionData<Data, FlashData> = Partial<Data & {
[Key in keyof FlashData as FlashDataKey<Key & string>]: FlashData[Key];
}>;
type FlashDataKey<Key extends string> = `__flash_${Key}__`;
type CreateSessionFunction = <Data = SessionData, FlashData = Data>(initialData?: Data, id?: string) => Session<Data, FlashData>;
/**
* Creates a new Session object.
*
* Note: This function is typically not invoked directly by application code.
* Instead, use a `SessionStorage` object's `getSession` method.
*/
declare const createSession: CreateSessionFunction;
type IsSessionFunction = (object: any) => object is Session;
/**
* Returns true if an object is a React Router session.
*
* @see https://reactrouter.com/api/utils/isSession
*/
declare const isSession: IsSessionFunction;
/**
* SessionStorage stores session data between HTTP requests and knows how to
* parse and create cookies.
*
* A SessionStorage creates Session objects using a `Cookie` header as input.
* Then, later it generates the `Set-Cookie` header to be used in the response.
*/
interface SessionStorage<Data = SessionData, FlashData = Data> {
/**
* Parses a Cookie header from a HTTP request and returns the associated
* Session. If there is no session associated with the cookie, this will
* return a new Session with no data.
*/
getSession: (cookieHeader?: string | null, options?: ParseOptions) => Promise<Session<Data, FlashData>>;
/**
* Stores all data in the Session and returns the Set-Cookie header to be
* used in the HTTP response.
*/
commitSession: (session: Session<Data, FlashData>, options?: SerializeOptions) => Promise<string>;
/**
* Deletes all data associated with the Session and returns the Set-Cookie
* header to be used in the HTTP response.
*/
destroySession: (session: Session<Data, FlashData>, options?: SerializeOptions) => Promise<string>;
}
/**
* SessionIdStorageStrategy is designed to allow anyone to easily build their
* own SessionStorage using `createSessionStorage(strategy)`.
*
* This strategy describes a common scenario where the session id is stored in
* a cookie but the actual session data is stored elsewhere, usually in a
* database or on disk. A set of create, read, update, and delete operations
* are provided for managing the session data.
*/
interface SessionIdStorageStrategy<Data = SessionData, FlashData = Data> {
/**
* The Cookie used to store the session id, or options used to automatically
* create one.
*/
cookie?: Cookie | (CookieOptions & {
name?: string;
});
/**
* Creates a new record with the given data and returns the session id.
*/
createData: (data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<string>;
/**
* Returns data for a given session id, or `null` if there isn't any.
*/
readData: (id: string) => Promise<FlashSessionData<Data, FlashData> | null>;
/**
* Updates data for the given session id.
*/
updateData: (id: string, data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<void>;
/**
* Deletes data for a given session id from the data store.
*/
deleteData: (id: string) => Promise<void>;
}
/**
* Creates a SessionStorage object using a SessionIdStorageStrategy.
*
* Note: This is a low-level API that should only be used if none of the
* existing session storage options meet your requirements.
*/
declare function createSessionStorage<Data = SessionData, FlashData = Data>({ cookie: cookieArg, createData, readData, updateData, deleteData, }: SessionIdStorageStrategy<Data, FlashData>): SessionStorage<Data, FlashData>;
interface CookieSessionStorageOptions {
/**
* The Cookie used to store the session data on the client, or options used
* to automatically create one.
*/
cookie?: SessionIdStorageStrategy["cookie"];
}
/**
* Creates and returns a SessionStorage object that stores all session data
* directly in the session cookie itself.
*
* This has the advantage that no database or other backend services are
* needed, and can help to simplify some load-balanced scenarios. However, it
* also has the limitation that serialized session data may not exceed the
* browser's maximum cookie size. Trade-offs!
*/
declare function createCookieSessionStorage<Data = SessionData, FlashData = Data>({ cookie: cookieArg }?: CookieSessionStorageOptions): SessionStorage<Data, FlashData>;
interface MemorySessionStorageOptions {
/**
* The Cookie used to store the session id on the client, or options used
* to automatically create one.
*/
cookie?: SessionIdStorageStrategy["cookie"];
}
/**
* Creates and returns a simple in-memory SessionStorage object, mostly useful
* for testing and as a reference implementation.
*
* Note: This storage does not scale beyond a single process, so it is not
* suitable for most production scenarios.
*/
declare function createMemorySessionStorage<Data = SessionData, FlashData = Data>({ cookie }?: MemorySessionStorageOptions): SessionStorage<Data, FlashData>;
export { Await, type Cookie, type CookieOptions, type CookieSignatureOptions, type FlashSessionData, type IsCookieFunction, type IsSessionFunction, type MiddlewareFunction, type MiddlewareNextFunction, type RouterContext, RouterContextProvider, type Session, type SessionData, type SessionIdStorageStrategy, type SessionStorage, createContext, createCookie, createCookieSessionStorage, createMemorySessionStorage, createSession, createSessionStorage, createStaticHandler, data, href, isCookie, isRouteErrorResponse, isSession, matchRoutes, redirect, redirectDocument, replace, type DecodeActionFunction as unstable_DecodeActionFunction, type DecodeFormStateFunction as unstable_DecodeFormStateFunction, type DecodeReplyFunction as unstable_DecodeReplyFunction, type LoadServerActionFunction as unstable_LoadServerActionFunction, type RSCManifestPayload as unstable_RSCManifestPayload, type RSCMatch as unstable_RSCMatch, type RSCPayload as unstable_RSCPayload, type RSCRenderPayload as unstable_RSCRenderPayload, type RSCRouteConfig as unstable_RSCRouteConfig, type RSCRouteConfigEntry as unstable_RSCRouteConfigEntry, type RSCRouteManifest as unstable_RSCRouteManifest, type RSCRouteMatch as unstable_RSCRouteMatch, getRequest as unstable_getRequest, matchRSCServerRequest as unstable_matchRSCServerRequest };
+2711
View File
@@ -0,0 +1,2711 @@
import * as React from 'react';
export { BrowserRouter, Form, HashRouter, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Outlet, Route, Router, RouterProvider, Routes, ScrollRestoration, StaticRouter, StaticRouterProvider, unstable_HistoryRouter } from 'react-router/internal/react-server-client';
import { ParseOptions, SerializeOptions } from 'cookie';
export { ParseOptions as CookieParseOptions, SerializeOptions as CookieSerializeOptions } from 'cookie';
/**
* Actions represent the type of change to a location value.
*/
declare enum Action {
/**
* A POP indicates a change to an arbitrary index in the history stack, such
* as a back or forward navigation. It does not describe the direction of the
* navigation, only that the current index changed.
*
* Note: This is the default action for newly created history objects.
*/
Pop = "POP",
/**
* A PUSH indicates a new entry being added to the history stack, such as when
* a link is clicked and a new page loads. When this happens, all subsequent
* entries in the stack are lost.
*/
Push = "PUSH",
/**
* A REPLACE indicates the entry at the current index in the history stack
* being replaced by a new one.
*/
Replace = "REPLACE"
}
/**
* The pathname, search, and hash values of a URL.
*/
interface Path {
/**
* A URL pathname, beginning with a /.
*/
pathname: string;
/**
* A URL search string, beginning with a ?.
*/
search: string;
/**
* A URL fragment identifier, beginning with a #.
*/
hash: string;
}
/**
* An entry in a history stack. A location contains information about the
* URL path, as well as possibly some arbitrary state and a key.
*/
interface Location<State = any> extends Path {
/**
* A value of arbitrary data associated with this location.
*/
state: State;
/**
* A unique string associated with this location. May be used to safely store
* and retrieve data in some other storage API, like `localStorage`.
*
* Note: This value is always "default" on the initial location.
*/
key: string;
/**
* The masked location displayed in the URL bar, which differs from the URL the
* router is operating on
*/
mask?: Path;
}
/**
* A change to the current location.
*/
interface Update {
/**
* The action that triggered the change.
*/
action: Action;
/**
* The new location.
*/
location: Location;
/**
* The delta between this location and the former location in the history stack
*/
delta: number | null;
}
/**
* A function that receives notifications about location changes.
*/
interface Listener {
(update: Update): void;
}
/**
* Describes a location that is the destination of some navigation used in
* {@link Link}, {@link useNavigate}, etc.
*/
type To = string | Partial<Path>;
/**
* A history is an interface to the navigation stack. The history serves as the
* source of truth for the current location, as well as provides a set of
* methods that may be used to change it.
*
* It is similar to the DOM's `window.history` object, but with a smaller, more
* focused API.
*/
interface History {
/**
* The last action that modified the current location. This will always be
* Action.Pop when a history instance is first created. This value is mutable.
*/
readonly action: Action;
/**
* The current location. This value is mutable.
*/
readonly location: Location;
/**
* Returns a valid href for the given `to` value that may be used as
* the value of an <a href> attribute.
*
* @param to - The destination URL
*/
createHref(to: To): string;
/**
* Returns a URL for the given `to` value
*
* @param to - The destination URL
*/
createURL(to: To): URL;
/**
* Encode a location the same way window.history would do (no-op for memory
* history) so we ensure our PUSH/REPLACE navigations for data routers
* behave the same as POP
*
* @param to Unencoded path
*/
encodeLocation(to: To): Path;
/**
* Pushes a new location onto the history stack, increasing its length by one.
* If there were any entries in the stack after the current one, they are
* lost.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
push(to: To, state?: any): void;
/**
* Replaces the current location in the history stack with a new one. The
* location that was replaced will no longer be available.
*
* @param to - The new URL
* @param state - Data to associate with the new location
*/
replace(to: To, state?: any): void;
/**
* Navigates `n` entries backward/forward in the history stack relative to the
* current index. For example, a "back" navigation would use go(-1).
*
* @param delta - The delta in the stack index
*/
go(delta: number): void;
/**
* Sets up a listener that will be called whenever the current location
* changes.
*
* @param listener - A function that will be called when the location changes
* @returns unlisten - A function that may be used to stop listening
*/
listen(listener: Listener): () => void;
}
/**
* An augmentable interface users can modify in their app-code to opt into
* future-flag-specific types
*/
interface Future {
}
type MiddlewareEnabled = Future extends {
v8_middleware: infer T extends boolean;
} ? T : false;
type MaybePromise<T> = T | Promise<T>;
/**
* Map of routeId -> data returned from a loader/action/error
*/
interface RouteData {
[routeId: string]: any;
}
type LowerCaseFormMethod = "get" | "post" | "put" | "patch" | "delete";
type UpperCaseFormMethod = Uppercase<LowerCaseFormMethod>;
/**
* Users can specify either lowercase or uppercase form methods on `<Form>`,
* useSubmit(), `<fetcher.Form>`, etc.
*/
type HTMLFormMethod = LowerCaseFormMethod | UpperCaseFormMethod;
/**
* Active navigation/fetcher form methods are exposed in uppercase on the
* RouterState. This is to align with the normalization done via fetch().
*/
type FormMethod = UpperCaseFormMethod;
type FormEncType = "application/x-www-form-urlencoded" | "multipart/form-data" | "application/json" | "text/plain";
type JsonObject = {
[Key in string]: JsonValue;
} & {
[Key in string]?: JsonValue | undefined;
};
type JsonArray = JsonValue[] | readonly JsonValue[];
type JsonPrimitive = string | number | boolean | null;
type JsonValue = JsonPrimitive | JsonObject | JsonArray;
/**
* @private
* Internal interface to pass around for action submissions, not intended for
* external consumption
*/
type Submission = {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: FormData;
json: undefined;
text: undefined;
} | {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: undefined;
json: JsonValue;
text: undefined;
} | {
formMethod: FormMethod;
formAction: string;
formEncType: FormEncType;
formData: undefined;
json: undefined;
text: string;
};
/**
* A context instance used as the key for the `get`/`set` methods of a
* {@link RouterContextProvider}. Accepts an optional default
* value to be returned if no value has been set.
*/
interface RouterContext<T = unknown> {
defaultValue?: T;
}
/**
* Creates a type-safe {@link RouterContext} object that can be used to
* store and retrieve arbitrary values in [`action`](../../start/framework/route-module#action)s,
* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
* Similar to React's [`createContext`](https://react.dev/reference/react/createContext),
* but specifically designed for React Router's request/response lifecycle.
*
* If a `defaultValue` is provided, it will be returned from `context.get()`
* when no value has been set for the context. Otherwise, reading this context
* when no value has been set will throw an error.
*
* ```tsx filename=app/context.ts
* import { createContext } from "react-router";
*
* // Create a context for user data
* export const userContext =
* createContext<User | null>(null);
* ```
*
* ```tsx filename=app/middleware/auth.ts
* import { getUserFromSession } from "~/auth.server";
* import { userContext } from "~/context";
*
* export const authMiddleware = async ({
* context,
* request,
* }) => {
* const user = await getUserFromSession(request);
* context.set(userContext, user);
* };
* ```
*
* ```tsx filename=app/routes/profile.tsx
* import { userContext } from "~/context";
*
* export async function loader({
* context,
* }: Route.LoaderArgs) {
* const user = context.get(userContext);
*
* if (!user) {
* throw new Response("Unauthorized", { status: 401 });
* }
*
* return { user };
* }
* ```
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param defaultValue An optional default value for the context. This value
* will be returned if no value has been set for this context.
* @returns A {@link RouterContext} object that can be used with
* `context.get()` and `context.set()` in [`action`](../../start/framework/route-module#action)s,
* [`loader`](../../start/framework/route-module#loader)s, and [middleware](../../how-to/middleware).
*/
declare function createContext<T>(defaultValue?: T): RouterContext<T>;
/**
* Provides methods for writing/reading values in application context in a
* type-safe way. Primarily for usage with [middleware](../../how-to/middleware).
*
* @example
* import {
* createContext,
* RouterContextProvider
* } from "react-router";
*
* const userContext = createContext<User | null>(null);
* const contextProvider = new RouterContextProvider();
* contextProvider.set(userContext, getUser());
* // ^ Type-safe
* const user = contextProvider.get(userContext);
* // ^ User
*
* @public
* @category Utils
* @mode framework
* @mode data
*/
declare class RouterContextProvider {
#private;
/**
* Create a new `RouterContextProvider` instance
* @param init An optional initial context map to populate the provider with
*/
constructor(init?: Map<RouterContext, unknown>);
/**
* Access a value from the context. If no value has been set for the context,
* it will return the context's `defaultValue` if provided, or throw an error
* if no `defaultValue` was set.
* @param context The context to get the value for
* @returns The value for the context, or the context's `defaultValue` if no
* value was set
*/
get<T>(context: RouterContext<T>): T;
/**
* Set a value for the context. If the context already has a value set, this
* will overwrite it.
*
* @param context The context to set the value for
* @param value The value to set for the context
* @returns {void}
*/
set<C extends RouterContext>(context: C, value: C extends RouterContext<infer T> ? T : never): void;
}
type DefaultContext = MiddlewareEnabled extends true ? Readonly<RouterContextProvider> : any;
/**
* @private
* Arguments passed to route loader/action functions. Same for now but we keep
* this as a private implementation detail in case they diverge in the future.
*/
interface DataFunctionArgs<Context> {
/** A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read headers (like cookies, and {@link https://developer.mozilla.org/en-US/docs/Web/API/URLSearchParams URLSearchParams} from the request. */
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function loader({
* params,
* }: Route.LoaderArgs) {
* params.teamId;
* // ^ string
* }
*/
params: Params;
/**
* This is the context passed in to your server adapter's getLoadContext() function.
* It's a way to bridge the gap between the adapter's request/response API with your React Router app.
* It is only applicable if you are using a custom server adapter.
*/
context: Context;
}
/**
* Route middleware `next` function to call downstream handlers and then complete
* middlewares from the bottom-up
*/
interface MiddlewareNextFunction<Result = unknown> {
(): Promise<Result>;
}
/**
* Route middleware function signature. Receives the same "data" arguments as a
* `loader`/`action` (`request`, `params`, `context`) as the first parameter and
* a `next` function as the second parameter which will call downstream handlers
* and then complete middlewares from the bottom-up
*/
type MiddlewareFunction<Result = unknown> = (args: DataFunctionArgs<Readonly<RouterContextProvider>>, next: MiddlewareNextFunction<Result>) => MaybePromise<Result | void>;
/**
* Arguments passed to loader functions
*/
interface LoaderFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
}
/**
* Arguments passed to action functions
*/
interface ActionFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
}
/**
* Loaders and actions can return anything
*/
type DataFunctionValue = unknown;
type DataFunctionReturnValue = MaybePromise<DataFunctionValue>;
/**
* Route loader function signature
*/
type LoaderFunction<Context = DefaultContext> = {
(args: LoaderFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue;
} & {
hydrate?: boolean;
};
/**
* Route action function signature
*/
interface ActionFunction<Context = DefaultContext> {
(args: ActionFunctionArgs<Context>, handlerCtx?: unknown): DataFunctionReturnValue;
}
/**
* Arguments passed to shouldRevalidate function
*/
interface ShouldRevalidateFunctionArgs {
/** This is the url the navigation started from. You can compare it with `nextUrl` to decide if you need to revalidate this route's data. */
currentUrl: URL;
/** These are the {@link https://reactrouter.com/start/framework/routing#dynamic-segments dynamic route params} from the URL that can be compared to the `nextParams` to decide if you need to reload or not. Perhaps you're using only a partial piece of the param for data loading, you don't need to revalidate if a superfluous part of the param changed. */
currentParams: DataRouteMatch["params"];
/** In the case of navigation, this the URL the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentUrl. */
nextUrl: URL;
/** In the case of navigation, these are the {@link https://reactrouter.com/start/framework/routing#dynamic-segments dynamic route params} from the next location the user is requesting. Some revalidations are not navigation, so it will simply be the same as currentParams. */
nextParams: DataRouteMatch["params"];
/** The method (probably `"GET"` or `"POST"`) used in the form submission that triggered the revalidation. */
formMethod?: Submission["formMethod"];
/** The form action (`<Form action="/somewhere">`) that triggered the revalidation. */
formAction?: Submission["formAction"];
/** The form encType (`<Form encType="application/x-www-form-urlencoded">) used in the form submission that triggered the revalidation*/
formEncType?: Submission["formEncType"];
/** The form submission data when the form's encType is `text/plain` */
text?: Submission["text"];
/** The form submission data when the form's encType is `application/x-www-form-urlencoded` or `multipart/form-data` */
formData?: Submission["formData"];
/** The form submission data when the form's encType is `application/json` */
json?: Submission["json"];
/** The status code of the action response */
actionStatus?: number;
/**
* When a submission causes the revalidation this will be the result of the action—either action data or an error if the action failed. It's common to include some information in the action result to instruct shouldRevalidate to revalidate or not.
*
* @example
* export async function action() {
* await saveSomeStuff();
* return { ok: true };
* }
*
* export function shouldRevalidate({
* actionResult,
* }) {
* if (actionResult?.ok) {
* return false;
* }
* return true;
* }
*/
actionResult?: any;
/**
* By default, React Router doesn't call every loader all the time. There are reliable optimizations it can make by default. For example, only loaders with changing params are called. Consider navigating from the following URL to the one below it:
*
* /projects/123/tasks/abc
* /projects/123/tasks/def
* React Router will only call the loader for tasks/def because the param for projects/123 didn't change.
*
* It's safest to always return defaultShouldRevalidate after you've done your specific optimizations that return false, otherwise your UI might get out of sync with your data on the server.
*/
defaultShouldRevalidate: boolean;
}
/**
* Route shouldRevalidate function signature. This runs after any submission
* (navigation or fetcher), so we flatten the navigation/fetcher submission
* onto the arguments. It shouldn't matter whether it came from a navigation
* or a fetcher, what really matters is the URLs and the formData since loaders
* have to re-run based on the data models that were potentially mutated.
*/
interface ShouldRevalidateFunction {
(args: ShouldRevalidateFunctionArgs): boolean;
}
interface DataStrategyMatch extends RouteMatch<string, DataRouteObject> {
/**
* @private
*/
_lazyPromises?: {
middleware: Promise<void> | undefined;
handler: Promise<void> | undefined;
route: Promise<void> | undefined;
};
/**
* @deprecated Deprecated in favor of `shouldCallHandler`
*
* A boolean value indicating whether this route handler should be called in
* this pass.
*
* The `matches` array always includes _all_ matched routes even when only
* _some_ route handlers need to be called so that things like middleware can
* be implemented.
*
* `shouldLoad` is usually only interesting if you are skipping the route
* handler entirely and implementing custom handler logic - since it lets you
* determine if that custom logic should run for this route or not.
*
* For example:
* - If you are on `/parent/child/a` and you navigate to `/parent/child/b` -
* you'll get an array of three matches (`[parent, child, b]`), but only `b`
* will have `shouldLoad=true` because the data for `parent` and `child` is
* already loaded
* - If you are on `/parent/child/a` and you submit to `a`'s [`action`](https://reactrouter.com/docs/start/data/route-object#action),
* then only `a` will have `shouldLoad=true` for the action execution of
* `dataStrategy`
* - After the [`action`](https://reactrouter.com/docs/start/data/route-object#action),
* `dataStrategy` will be called again for the [`loader`](https://reactrouter.com/docs/start/data/route-object#loader)
* revalidation, and all matches will have `shouldLoad=true` (assuming no
* custom `shouldRevalidate` implementations)
*/
shouldLoad: boolean;
/**
* Arguments passed to the `shouldRevalidate` function for this `loader` execution.
* Will be `null` if this is not a revalidating loader {@link DataStrategyMatch}.
*/
shouldRevalidateArgs: ShouldRevalidateFunctionArgs | null;
/**
* Determine if this route's handler should be called during this `dataStrategy`
* execution. Calling it with no arguments will leverage the default revalidation
* behavior. You can pass your own `defaultShouldRevalidate` value if you wish
* to change the default revalidation behavior with your `dataStrategy`.
*
* @param defaultShouldRevalidate `defaultShouldRevalidate` override value (optional)
*/
shouldCallHandler(defaultShouldRevalidate?: boolean): boolean;
/**
* An async function that will resolve any `route.lazy` implementations and
* execute the route's handler (if necessary), returning a {@link DataStrategyResult}
*
* - Calling `match.resolve` does not mean you're calling the
* [`action`](https://reactrouter.com/docs/start/data/route-object#action)/[`loader`](https://reactrouter.com/docs/start/data/route-object#loader)
* (the "handler") - `resolve` will only call the `handler` internally if
* needed _and_ if you don't pass your own `handlerOverride` function parameter
* - It is safe to call `match.resolve` for all matches, even if they have
* `shouldLoad=false`, and it will no-op if no loading is required
* - You should generally always call `match.resolve()` for `shouldLoad:true`
* routes to ensure that any `route.lazy` implementations are processed
* - See the examples below for how to implement custom handler execution via
* `match.resolve`
*/
resolve: (handlerOverride?: (handler: (ctx?: unknown) => DataFunctionReturnValue) => DataFunctionReturnValue) => Promise<DataStrategyResult>;
}
interface DataStrategyFunctionArgs<Context = DefaultContext> extends DataFunctionArgs<Context> {
/**
* Matches for this route extended with Data strategy APIs
*/
matches: DataStrategyMatch[];
runClientMiddleware: (cb: DataStrategyFunction<Context>) => Promise<Record<string, DataStrategyResult>>;
/**
* The key of the fetcher we are calling `dataStrategy` for, otherwise `null`
* for navigational executions
*/
fetcherKey: string | null;
}
/**
* Result from a loader or action called via dataStrategy
*/
interface DataStrategyResult {
type: "data" | "error";
result: unknown;
}
interface DataStrategyFunction<Context = DefaultContext> {
(args: DataStrategyFunctionArgs<Context>): Promise<Record<string, DataStrategyResult>>;
}
type PatchRoutesOnNavigationFunctionArgs = {
signal: AbortSignal;
path: string;
matches: RouteMatch[];
fetcherKey: string | undefined;
patch: (routeId: string | null, children: RouteObject[]) => void;
};
type PatchRoutesOnNavigationFunction = (opts: PatchRoutesOnNavigationFunctionArgs) => MaybePromise<void>;
/**
* Function provided to set route-specific properties from route objects
*/
interface MapRoutePropertiesFunction {
(route: DataRouteObject): {
hasErrorBoundary: boolean;
} & Record<string, any>;
}
/**
* Keys we cannot change from within a lazy object. We spread all other keys
* onto the route. Either they're meaningful to the router, or they'll get
* ignored.
*/
type UnsupportedLazyRouteObjectKey = "lazy" | "caseSensitive" | "path" | "id" | "index" | "children";
/**
* Keys we cannot change from within a lazy() function. We spread all other keys
* onto the route. Either they're meaningful to the router, or they'll get
* ignored.
*/
type UnsupportedLazyRouteFunctionKey = UnsupportedLazyRouteObjectKey | "middleware";
/**
* lazy object to load route properties, which can add non-matching
* related properties to a route
*/
type LazyRouteObject<R extends RouteObject> = {
[K in keyof R as K extends UnsupportedLazyRouteObjectKey ? never : K]?: () => Promise<R[K] | null | undefined>;
};
/**
* lazy() function to load a route definition, which can add non-matching
* related properties to a route
*/
interface LazyRouteFunction<R extends RouteObject> {
(): Promise<Omit<R, UnsupportedLazyRouteFunctionKey> & Partial<Record<UnsupportedLazyRouteFunctionKey, never>>>;
}
type LazyRouteDefinition<R extends RouteObject> = LazyRouteObject<R> | LazyRouteFunction<R>;
/**
* Base RouteObject with common props shared by all types of routes
* @internal
*/
type BaseRouteObject = {
/**
* Whether the path should be case-sensitive. Defaults to `false`.
*/
caseSensitive?: boolean;
/**
* The path pattern to match. If unspecified or empty, then this becomes a
* layout route.
*/
path?: string;
/**
* The unique identifier for this route (for use with {@link DataRouter}s)
*/
id?: string;
/**
* The route middleware.
* See [`middleware`](../../start/data/route-object#middleware).
*/
middleware?: MiddlewareFunction[];
/**
* The route loader.
* See [`loader`](../../start/data/route-object#loader).
*/
loader?: LoaderFunction | boolean;
/**
* The route action.
* See [`action`](../../start/data/route-object#action).
*/
action?: ActionFunction | boolean;
hasErrorBoundary?: boolean;
/**
* The route shouldRevalidate function.
* See [`shouldRevalidate`](../../start/data/route-object#shouldRevalidate).
*/
shouldRevalidate?: ShouldRevalidateFunction;
/**
* The route handle.
*/
handle?: any;
/**
* A function that returns a promise that resolves to the route object.
* Used for code-splitting routes.
* See [`lazy`](../../start/data/route-object#lazy).
*/
lazy?: LazyRouteDefinition<BaseRouteObject>;
/**
* The React Component to render when this route matches.
* Mutually exclusive with `element`.
*/
Component?: React.ComponentType | null;
/**
* The React element to render when this Route matches.
* Mutually exclusive with `Component`.
*/
element?: React.ReactNode | null;
/**
* The React Component to render at this route if an error occurs.
* Mutually exclusive with `errorElement`.
*/
ErrorBoundary?: React.ComponentType | null;
/**
* The React element to render at this route if an error occurs.
* Mutually exclusive with `ErrorBoundary`.
*/
errorElement?: React.ReactNode | null;
/**
* The React Component to render while this router is loading data.
* Mutually exclusive with `hydrateFallbackElement`.
*/
HydrateFallback?: React.ComponentType | null;
/**
* The React element to render while this router is loading data.
* Mutually exclusive with `HydrateFallback`.
*/
hydrateFallbackElement?: React.ReactNode | null;
};
/**
* Index routes must not have children
*/
type IndexRouteObject = BaseRouteObject & {
/**
* Child Route objects - not valid on index routes.
*/
children?: undefined;
/**
* Whether this is an index route.
*/
index: true;
};
/**
* Non-index routes may have children, but cannot have `index` set to `true`.
*/
type NonIndexRouteObject = BaseRouteObject & {
/**
* Child Route objects.
*/
children?: RouteObject[];
/**
* Whether this is an index route - must be `false` or undefined on non-index routes.
*/
index?: false;
};
/**
* A route object represents a logical route, with (optionally) its child
* routes organized in a tree-like structure.
*/
type RouteObject = IndexRouteObject | NonIndexRouteObject;
type DataIndexRouteObject = IndexRouteObject & {
id: string;
};
type DataNonIndexRouteObject = NonIndexRouteObject & {
children?: DataRouteObject[];
id: string;
};
/**
* A data route object, which is just a RouteObject with a required unique ID
*/
type DataRouteObject = DataIndexRouteObject | DataNonIndexRouteObject;
type RouteManifest<R = DataRouteObject> = Record<string, R | undefined>;
/**
* The parameters that were parsed from the URL path.
*/
type Params<Key extends string = string> = {
readonly [key in Key]: string | undefined;
};
/**
* A RouteMatch contains info about how a route matched a URL.
*/
interface RouteMatch<ParamKey extends string = string, RouteObjectType extends RouteObject = RouteObject> {
/**
* The names and values of dynamic parameters in the URL.
*/
params: Params<ParamKey>;
/**
* The portion of the URL pathname that was matched.
*/
pathname: string;
/**
* The portion of the URL pathname that was matched before child routes.
*/
pathnameBase: string;
/**
* The route object that was used to match.
*/
route: RouteObjectType;
}
interface DataRouteMatch extends RouteMatch<string, DataRouteObject> {
}
/**
* Matches the given routes to a location and returns the match data.
*
* @example
* import { matchRoutes } from "react-router";
*
* let routes = [{
* path: "/",
* Component: Root,
* children: [{
* path: "dashboard",
* Component: Dashboard,
* }]
* }];
*
* matchRoutes(routes, "/dashboard"); // [rootMatch, dashboardMatch]
*
* @public
* @category Utils
* @param routes The array of route objects to match against.
* @param locationArg The location to match against, either a string path or a
* partial {@link Location} object
* @param basename Optional base path to strip from the location before matching.
* Defaults to `/`.
* @returns An array of matched routes, or `null` if no matches were found.
*/
declare function matchRoutes<RouteObjectType extends RouteObject = RouteObject>(routes: RouteObjectType[], locationArg: Partial<Location> | string, basename?: string): RouteMatch<string, RouteObjectType>[] | null;
interface UIMatch<Data = unknown, Handle = unknown> {
id: string;
pathname: string;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the matched route.
*/
params: RouteMatch["params"];
/**
* The return value from the matched route's loader or clientLoader. This might
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
* an error and we're currently displaying an `ErrorBoundary`.
*
* @deprecated Use `UIMatch.loaderData` instead
*/
data: Data | undefined;
/**
* The return value from the matched route's loader or clientLoader. This might
* be `undefined` if this route's `loader` (or a deeper route's `loader`) threw
* an error and we're currently displaying an `ErrorBoundary`.
*/
loaderData: Data | undefined;
/**
* The {@link https://reactrouter.com/start/framework/route-module#handle handle object}
* exported from the matched route module
*/
handle: Handle;
}
interface RouteMeta<RouteObjectType extends RouteObject = RouteObject> {
relativePath: string;
caseSensitive: boolean;
childrenIndex: number;
route: RouteObjectType;
matcher?: RegExp;
compiledParams?: CompiledPathParam[];
}
/**
* @private
* PRIVATE - DO NOT USE
*
* A "branch" of routes that match a given route pattern.
* This is an internal interface not intended for direct external usage.
*/
interface RouteBranch<RouteObjectType extends RouteObject = RouteObject> {
path: string;
score: number;
routesMeta: RouteMeta<RouteObjectType>[];
}
type CompiledPathParam = {
paramName: string;
isOptional?: boolean;
};
declare class DataWithResponseInit<D> {
type: string;
data: D;
init: ResponseInit | null;
constructor(data: D, init?: ResponseInit);
}
/**
* Create "responses" that contain `headers`/`status` without forcing
* serialization into an actual [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
*
* @example
* import { data } from "react-router";
*
* export async function action({ request }: Route.ActionArgs) {
* let formData = await request.formData();
* let item = await createItem(formData);
* return data(item, {
* headers: { "X-Custom-Header": "value" }
* status: 201,
* });
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param data The data to be included in the response.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A {@link DataWithResponseInit} instance containing the data and
* response init.
*/
declare function data<D>(data: D, init?: number | ResponseInit): DataWithResponseInit<D>;
type RedirectFunction = (url: string, init?: number | ResponseInit) => Response;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response).
* Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* This utility accepts absolute URLs and can navigate to external domains, so
* the application should validate any user-supplied inputs to redirects.
*
* @example
* import { redirect } from "react-router";
*
* export async function loader({ request }: Route.LoaderArgs) {
* if (!isLoggedIn(request))
* throw redirect("/login");
* }
*
* // ...
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const redirect$1: RedirectFunction;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that will force a document reload to the new location. Sets the status code
* and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* This utility accepts absolute URLs and can navigate to external domains, so
* the application should validate any user-supplied inputs to redirects.
*
* ```tsx filename=routes/logout.tsx
* import { redirectDocument } from "react-router";
*
* import { destroySession } from "../sessions.server";
*
* export async function action({ request }: Route.ActionArgs) {
* let session = await getSession(request.headers.get("Cookie"));
* return redirectDocument("/", {
* headers: { "Set-Cookie": await destroySession(session) }
* });
* }
* ```
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const redirectDocument$1: RedirectFunction;
/**
* A redirect [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that will perform a [`history.replaceState`](https://developer.mozilla.org/en-US/docs/Web/API/History/replaceState)
* instead of a [`history.pushState`](https://developer.mozilla.org/en-US/docs/Web/API/History/pushState)
* for client-side navigation redirects. Sets the status code and the [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header. Defaults to [`302 Found`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Status/302).
*
* @example
* import { replace } from "react-router";
*
* export async function loader() {
* return replace("/new-location");
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param url The URL to redirect to.
* @param init The status code or a `ResponseInit` object to be included in the
* response.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* object with the redirect status and [`Location`](https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Location)
* header.
*/
declare const replace$1: RedirectFunction;
type ErrorResponse = {
status: number;
statusText: string;
data: any;
};
/**
* Check if the given error is an {@link ErrorResponse} generated from a 4xx/5xx
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* thrown from an [`action`](../../start/framework/route-module#action) or
* [`loader`](../../start/framework/route-module#loader) function.
*
* @example
* import { isRouteErrorResponse } from "react-router";
*
* export function ErrorBoundary({ error }: Route.ErrorBoundaryProps) {
* if (isRouteErrorResponse(error)) {
* return (
* <>
* <p>Error: `${error.status}: ${error.statusText}`</p>
* <p>{error.data}</p>
* </>
* );
* }
*
* return (
* <p>Error: {error instanceof Error ? error.message : "Unknown Error"}</p>
* );
* }
*
* @public
* @category Utils
* @mode framework
* @mode data
* @param error The error to check.
* @returns `true` if the error is an {@link ErrorResponse}, `false` otherwise.
*/
declare function isRouteErrorResponse(error: any): error is ErrorResponse;
/**
* An object of unknown type for route loaders and actions provided by the
* server's `getLoadContext()` function. This is defined as an empty interface
* specifically so apps can leverage declaration merging to augment this type
* globally: https://www.typescriptlang.org/docs/handbook/declaration-merging.html
*/
interface AppLoadContext {
[key: string]: unknown;
}
type ServerInstrumentation = {
handler?: InstrumentRequestHandlerFunction;
route?: InstrumentRouteFunction;
};
type ClientInstrumentation = {
router?: InstrumentRouterFunction;
route?: InstrumentRouteFunction;
};
type InstrumentRequestHandlerFunction = (handler: InstrumentableRequestHandler) => void;
type InstrumentRouterFunction = (router: InstrumentableRouter) => void;
type InstrumentRouteFunction = (route: InstrumentableRoute) => void;
type InstrumentationHandlerResult = {
status: "success";
error: undefined;
} | {
status: "error";
error: Error;
};
type InstrumentFunction<T> = (handler: () => Promise<InstrumentationHandlerResult>, info: T) => Promise<void>;
type ReadonlyRequest = {
method: string;
url: string;
headers: Pick<Headers, "get">;
};
type ReadonlyContext = MiddlewareEnabled extends true ? Pick<RouterContextProvider, "get"> : Readonly<AppLoadContext>;
type InstrumentableRoute = {
id: string;
index: boolean | undefined;
path: string | undefined;
instrument(instrumentations: RouteInstrumentations): void;
};
type RouteInstrumentations = {
lazy?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.loader"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.action"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.middleware"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
middleware?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
loader?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
action?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
};
type RouteLazyInstrumentationInfo = undefined;
type RouteHandlerInstrumentationInfo = Readonly<{
request: ReadonlyRequest;
params: LoaderFunctionArgs["params"];
pattern: string;
context: ReadonlyContext;
}>;
type InstrumentableRouter = {
instrument(instrumentations: RouterInstrumentations): void;
};
type RouterInstrumentations = {
navigate?: InstrumentFunction<RouterNavigationInstrumentationInfo>;
fetch?: InstrumentFunction<RouterFetchInstrumentationInfo>;
};
type RouterNavigationInstrumentationInfo = Readonly<{
to: string | number;
currentUrl: string;
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
formData?: FormData;
body?: any;
}>;
type RouterFetchInstrumentationInfo = Readonly<{
href: string;
currentUrl: string;
fetcherKey: string;
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
formData?: FormData;
body?: any;
}>;
type InstrumentableRequestHandler = {
instrument(instrumentations: RequestHandlerInstrumentations): void;
};
type RequestHandlerInstrumentations = {
request?: InstrumentFunction<RequestHandlerInstrumentationInfo>;
};
type RequestHandlerInstrumentationInfo = Readonly<{
request: ReadonlyRequest;
context: ReadonlyContext | undefined;
}>;
/**
* A Router instance manages all navigation and data loading/mutations
*/
interface Router {
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the basename for the router
*/
get basename(): RouterInit["basename"];
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the future config for the router
*/
get future(): FutureConfig;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the current state of the router
*/
get state(): RouterState;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the routes for this router instance
*/
get routes(): DataRouteObject[];
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the route branches for this router instance
*/
get branches(): RouteBranch<DataRouteObject>[] | undefined;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the manifest for this router instance
*/
get manifest(): RouteManifest;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the window associated with the router
*/
get window(): RouterInit["window"];
/**
* @private
* PRIVATE - DO NOT USE
*
* Initialize the router, including adding history listeners and kicking off
* initial data fetches. Returns a function to cleanup listeners and abort
* any in-progress loads
*/
initialize(): Router;
/**
* @private
* PRIVATE - DO NOT USE
*
* Subscribe to router.state updates
*
* @param fn function to call with the new state
*/
subscribe(fn: RouterSubscriber): () => void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Enable scroll restoration behavior in the router
*
* @param savedScrollPositions Object that will manage positions, in case
* it's being restored from sessionStorage
* @param getScrollPosition Function to get the active Y scroll position
* @param getKey Function to get the key to use for restoration
*/
enableScrollRestoration(savedScrollPositions: Record<string, number>, getScrollPosition: GetScrollPositionFunction, getKey?: GetScrollRestorationKeyFunction): () => void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Navigate forward/backward in the history stack
* @param to Delta to move in the history stack
*/
navigate(to: number): Promise<void>;
/**
* Navigate to the given path
* @param to Path to navigate to
* @param opts Navigation options (method, submission, etc.)
*/
navigate(to: To | null, opts?: RouterNavigateOptions): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Trigger a fetcher load/submission
*
* @param key Fetcher key
* @param routeId Route that owns the fetcher
* @param href href to fetch
* @param opts Fetcher options, (method, submission, etc.)
*/
fetch(key: string, routeId: string, href: string | null, opts?: RouterFetchOptions): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Trigger a revalidation of all current route loaders and fetcher loads
*/
revalidate(): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Utility function to create an href for the given location
* @param location
*/
createHref(location: Location | URL): string;
/**
* @private
* PRIVATE - DO NOT USE
*
* Utility function to URL encode a destination path according to the internal
* history implementation
* @param to
*/
encodeLocation(to: To): Path;
/**
* @private
* PRIVATE - DO NOT USE
*
* Get/create a fetcher for the given key
* @param key
*/
getFetcher<TData = any>(key: string): Fetcher<TData>;
/**
* @internal
* PRIVATE - DO NOT USE
*
* Reset the fetcher for a given key
* @param key
*/
resetFetcher(key: string, opts?: {
reason?: unknown;
}): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Delete the fetcher for a given key
* @param key
*/
deleteFetcher(key: string): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Cleanup listeners and abort any in-progress loads
*/
dispose(): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Get a navigation blocker
* @param key The identifier for the blocker
* @param fn The blocker function implementation
*/
getBlocker(key: string, fn: BlockerFunction): Blocker;
/**
* @private
* PRIVATE - DO NOT USE
*
* Delete a navigation blocker
* @param key The identifier for the blocker
*/
deleteBlocker(key: string): void;
/**
* @private
* PRIVATE DO NOT USE
*
* Patch additional children routes into an existing parent route
* @param routeId The parent route id or a callback function accepting `patch`
* to perform batch patching
* @param children The additional children routes
* @param unstable_allowElementMutations Allow mutation or route elements on
* existing routes. Intended for RSC-usage
* only.
*/
patchRoutes(routeId: string | null, children: RouteObject[], unstable_allowElementMutations?: boolean): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* HMR needs to pass in-flight route updates to React Router
* TODO: Replace this with granular route update APIs (addRoute, updateRoute, deleteRoute)
*/
_internalSetRoutes(routes: RouteObject[]): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Cause subscribers to re-render. This is used to force a re-render.
*/
_internalSetStateDoNotUseOrYouWillBreakYourApp(state: Partial<RouterState>): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Internal fetch AbortControllers accessed by unit tests
*/
_internalFetchControllers: Map<string, AbortController>;
}
/**
* State maintained internally by the router. During a navigation, all states
* reflect the "old" location unless otherwise noted.
*/
interface RouterState {
/**
* The action of the most recent navigation
*/
historyAction: Action;
/**
* The current location reflected by the router
*/
location: Location;
/**
* The current set of route matches
*/
matches: DataRouteMatch[];
/**
* Tracks whether we've completed our initial data load
*/
initialized: boolean;
/**
* Tracks whether we should be rendering a HydrateFallback during hydration
*/
renderFallback: boolean;
/**
* Current scroll position we should start at for a new view
* - number -> scroll position to restore to
* - false -> do not restore scroll at all (used during submissions/revalidations)
* - null -> don't have a saved position, scroll to hash or top of page
*/
restoreScrollPosition: number | false | null;
/**
* Indicate whether this navigation should skip resetting the scroll position
* if we are unable to restore the scroll position
*/
preventScrollReset: boolean;
/**
* Tracks the state of the current navigation
*/
navigation: Navigation;
/**
* Tracks any in-progress revalidations
*/
revalidation: RevalidationState;
/**
* Data from the loaders for the current matches
*/
loaderData: RouteData;
/**
* Data from the action for the current matches
*/
actionData: RouteData | null;
/**
* Errors caught from loaders for the current matches
*/
errors: RouteData | null;
/**
* Map of current fetchers
*/
fetchers: Map<string, Fetcher>;
/**
* Map of current blockers
*/
blockers: Map<string, Blocker>;
}
/**
* Data that can be passed into hydrate a Router from SSR
*/
type HydrationState = Partial<Pick<RouterState, "loaderData" | "actionData" | "errors">>;
/**
* Future flags to toggle new feature behavior
*/
interface FutureConfig {
}
/**
* Initialization options for createRouter
*/
interface RouterInit {
routes: RouteObject[];
history: History;
basename?: string;
getContext?: () => MaybePromise<RouterContextProvider>;
instrumentations?: ClientInstrumentation[];
mapRouteProperties?: MapRoutePropertiesFunction;
future?: Partial<FutureConfig>;
hydrationRouteProperties?: string[];
hydrationData?: HydrationState;
window?: Window;
dataStrategy?: DataStrategyFunction;
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
}
/**
* State returned from a server-side query() call
*/
interface StaticHandlerContext {
basename: Router["basename"];
location: RouterState["location"];
matches: RouterState["matches"];
loaderData: RouterState["loaderData"];
actionData: RouterState["actionData"];
errors: RouterState["errors"];
statusCode: number;
loaderHeaders: Record<string, Headers>;
actionHeaders: Record<string, Headers>;
_deepestRenderedBoundaryId?: string | null;
}
/**
* A StaticHandler instance manages a singular SSR navigation/fetch event
*/
interface StaticHandler {
/**
* The set of data routes managed by this handler
*/
dataRoutes: DataRouteObject[];
/**
* @private
* PRIVATE - DO NOT USE
*
* The route branches derived from the data routes, used for internal route
* matching in Framework Mode
*/
_internalRouteBranches: RouteBranch<DataRouteObject>[];
/**
* Perform a query for a given request - executing all matched route
* loaders/actions. Used for document requests.
*
* @param request The request to query
* @param opts Optional query options
* @param opts.dataStrategy Alternate dataStrategy implementation
* @param opts.filterMatchesToLoad Predicate function to filter which matches should be loaded
* @param opts.generateMiddlewareResponse To enable middleware, provide a function
* to generate a response to bubble back up the middleware chain
* @param opts.requestContext Context object to pass to loaders/actions
* @param opts.skipLoaderErrorBubbling Skip loader error bubbling
* @param opts.skipRevalidation Skip revalidation after action submission
* @param opts.normalizePath Normalize the request path
*/
query(request: Request, opts?: {
requestContext?: unknown;
filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
skipLoaderErrorBubbling?: boolean;
skipRevalidation?: boolean;
dataStrategy?: DataStrategyFunction<unknown>;
generateMiddlewareResponse?: (query: (r: Request, args?: {
filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
}) => Promise<StaticHandlerContext | Response>) => MaybePromise<Response>;
normalizePath?: (request: Request) => Path;
}): Promise<StaticHandlerContext | Response>;
/**
* Perform a query for a specific route. Used for resource requests.
*
* @param request The request to query
* @param opts Optional queryRoute options
* @param opts.dataStrategy Alternate dataStrategy implementation
* @param opts.generateMiddlewareResponse To enable middleware, provide a function
* to generate a response to bubble back up the middleware chain
* @param opts.requestContext Context object to pass to loaders/actions
* @param opts.routeId The ID of the route to query
* @param opts.normalizePath Normalize the request path
*/
queryRoute(request: Request, opts?: {
routeId?: string;
requestContext?: unknown;
dataStrategy?: DataStrategyFunction<unknown>;
generateMiddlewareResponse?: (queryRoute: (r: Request) => Promise<Response>) => MaybePromise<Response>;
normalizePath?: (request: Request) => Path;
}): Promise<any>;
}
type ViewTransitionOpts = {
currentLocation: Location;
nextLocation: Location;
};
/**
* Subscriber function signature for changes to router state
*/
interface RouterSubscriber {
(state: RouterState, opts: {
deletedFetchers: string[];
newErrors: RouteData | null;
viewTransitionOpts?: ViewTransitionOpts;
flushSync: boolean;
}): void;
}
/**
* Function signature for determining the key to be used in scroll restoration
* for a given location
*/
interface GetScrollRestorationKeyFunction {
(location: Location, matches: UIMatch[]): string | null;
}
/**
* Function signature for determining the current scroll position
*/
interface GetScrollPositionFunction {
(): number;
}
/**
* - "route": relative to the route hierarchy so `..` means remove all segments
* of the current route even if it has many. For example, a `route("posts/:id")`
* would have both `:id` and `posts` removed from the url.
* - "path": relative to the pathname so `..` means remove one segment of the
* pathname. For example, a `route("posts/:id")` would have only `:id` removed
* from the url.
*/
type RelativeRoutingType = "route" | "path";
type BaseNavigateOrFetchOptions = {
preventScrollReset?: boolean;
relative?: RelativeRoutingType;
flushSync?: boolean;
defaultShouldRevalidate?: boolean;
};
type BaseNavigateOptions = BaseNavigateOrFetchOptions & {
replace?: boolean;
state?: any;
fromRouteId?: string;
viewTransition?: boolean;
mask?: To;
};
type BaseSubmissionOptions = {
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
} & ({
formData: FormData;
body?: undefined;
} | {
formData?: undefined;
body: any;
});
/**
* Options for a navigate() call for a normal (non-submission) navigation
*/
type LinkNavigateOptions = BaseNavigateOptions;
/**
* Options for a navigate() call for a submission navigation
*/
type SubmissionNavigateOptions = BaseNavigateOptions & BaseSubmissionOptions;
/**
* Options to pass to navigate() for a navigation
*/
type RouterNavigateOptions = LinkNavigateOptions | SubmissionNavigateOptions;
/**
* Options for a fetch() load
*/
type LoadFetchOptions = BaseNavigateOrFetchOptions;
/**
* Options for a fetch() submission
*/
type SubmitFetchOptions = BaseNavigateOrFetchOptions & BaseSubmissionOptions;
/**
* Options to pass to fetch()
*/
type RouterFetchOptions = LoadFetchOptions | SubmitFetchOptions;
/**
* Potential states for state.navigation
*/
type NavigationStates = {
Idle: {
state: "idle";
location: undefined;
matches: undefined;
historyAction: undefined;
formMethod: undefined;
formAction: undefined;
formEncType: undefined;
formData: undefined;
json: undefined;
text: undefined;
};
Loading: {
state: "loading";
location: Location;
matches: DataRouteMatch[];
historyAction: Action;
formMethod: Submission["formMethod"] | undefined;
formAction: Submission["formAction"] | undefined;
formEncType: Submission["formEncType"] | undefined;
formData: Submission["formData"] | undefined;
json: Submission["json"] | undefined;
text: Submission["text"] | undefined;
};
Submitting: {
state: "submitting";
location: Location;
matches: DataRouteMatch[];
historyAction: Action;
formMethod: Submission["formMethod"];
formAction: Submission["formAction"];
formEncType: Submission["formEncType"];
formData: Submission["formData"];
json: Submission["json"];
text: Submission["text"];
};
};
type Navigation = NavigationStates[keyof NavigationStates];
type RevalidationState = "idle" | "loading";
/**
* Potential states for fetchers
*/
type FetcherStates<TData = any> = {
/**
* The fetcher is not calling a loader or action
*
* ```tsx
* fetcher.state === "idle"
* ```
*/
Idle: {
state: "idle";
formMethod: undefined;
formAction: undefined;
formEncType: undefined;
text: undefined;
formData: undefined;
json: undefined;
/**
* If the fetcher has never been called, this will be undefined.
*/
data: TData | undefined;
};
/**
* The fetcher is loading data from a {@link LoaderFunction | loader} from a
* call to {@link FetcherWithComponents.load | `fetcher.load`}.
*
* ```tsx
* // somewhere
* <button onClick={() => fetcher.load("/some/route") }>Load</button>
*
* // the state will update
* fetcher.state === "loading"
* ```
*/
Loading: {
state: "loading";
formMethod: Submission["formMethod"] | undefined;
formAction: Submission["formAction"] | undefined;
formEncType: Submission["formEncType"] | undefined;
text: Submission["text"] | undefined;
formData: Submission["formData"] | undefined;
json: Submission["json"] | undefined;
data: TData | undefined;
};
/**
The fetcher is submitting to a {@link LoaderFunction} (GET) or {@link ActionFunction} (POST) from a {@link FetcherWithComponents.Form | `fetcher.Form`} or {@link FetcherWithComponents.submit | `fetcher.submit`}.
```tsx
// somewhere
<input
onChange={e => {
fetcher.submit(event.currentTarget.form, { method: "post" });
}}
/>
// the state will update
fetcher.state === "submitting"
// and formData will be available
fetcher.formData
```
*/
Submitting: {
state: "submitting";
formMethod: Submission["formMethod"];
formAction: Submission["formAction"];
formEncType: Submission["formEncType"];
text: Submission["text"];
formData: Submission["formData"];
json: Submission["json"];
data: TData | undefined;
};
};
type Fetcher<TData = any> = FetcherStates<TData>[keyof FetcherStates<TData>];
interface BlockerBlocked {
state: "blocked";
reset: () => void;
proceed: () => void;
location: Location;
}
interface BlockerUnblocked {
state: "unblocked";
reset: undefined;
proceed: undefined;
location: undefined;
}
interface BlockerProceeding {
state: "proceeding";
reset: undefined;
proceed: undefined;
location: Location;
}
type Blocker = BlockerUnblocked | BlockerBlocked | BlockerProceeding;
type BlockerFunction = (args: {
currentLocation: Location;
nextLocation: Location;
historyAction: Action;
}) => boolean;
interface CreateStaticHandlerOptions {
basename?: string;
mapRouteProperties?: MapRoutePropertiesFunction;
instrumentations?: Pick<ServerInstrumentation, "route">[];
future?: Partial<FutureConfig>;
}
declare function createStaticHandler(routes: RouteObject[], opts?: CreateStaticHandlerOptions): StaticHandler;
type Primitive = null | undefined | string | number | boolean | symbol | bigint;
type LiteralUnion<LiteralType, BaseType extends Primitive> = LiteralType | (BaseType & Record<never, never>);
interface HtmlLinkProps {
/**
* Address of the hyperlink
*/
href?: string;
/**
* How the element handles crossorigin requests
*/
crossOrigin?: "anonymous" | "use-credentials";
/**
* Relationship between the document containing the hyperlink and the destination resource
*/
rel: LiteralUnion<"alternate" | "dns-prefetch" | "icon" | "manifest" | "modulepreload" | "next" | "pingback" | "preconnect" | "prefetch" | "preload" | "prerender" | "search" | "stylesheet", string>;
/**
* Applicable media: "screen", "print", "(max-width: 764px)"
*/
media?: string;
/**
* Integrity metadata used in Subresource Integrity checks
*/
integrity?: string;
/**
* Language of the linked resource
*/
hrefLang?: string;
/**
* Hint for the type of the referenced resource
*/
type?: string;
/**
* Referrer policy for fetches initiated by the element
*/
referrerPolicy?: "" | "no-referrer" | "no-referrer-when-downgrade" | "same-origin" | "origin" | "strict-origin" | "origin-when-cross-origin" | "strict-origin-when-cross-origin" | "unsafe-url";
/**
* Sizes of the icons (for rel="icon")
*/
sizes?: string;
/**
* Potential destination for a preload request (for rel="preload" and rel="modulepreload")
*/
as?: LiteralUnion<"audio" | "audioworklet" | "document" | "embed" | "fetch" | "font" | "frame" | "iframe" | "image" | "manifest" | "object" | "paintworklet" | "report" | "script" | "serviceworker" | "sharedworker" | "style" | "track" | "video" | "worker" | "xslt", string>;
/**
* Color to use when customizing a site's icon (for rel="mask-icon")
*/
color?: string;
/**
* Whether the link is disabled
*/
disabled?: boolean;
/**
* The title attribute has special semantics on this element: Title of the link; CSS style sheet set name.
*/
title?: string;
/**
* Images to use in different situations, e.g., high-resolution displays,
* small monitors, etc. (for rel="preload")
*/
imageSrcSet?: string;
/**
* Image sizes for different page layouts (for rel="preload")
*/
imageSizes?: string;
}
interface HtmlLinkPreloadImage extends HtmlLinkProps {
/**
* Relationship between the document containing the hyperlink and the destination resource
*/
rel: "preload";
/**
* Potential destination for a preload request (for rel="preload" and rel="modulepreload")
*/
as: "image";
/**
* Address of the hyperlink
*/
href?: string;
/**
* Images to use in different situations, e.g., high-resolution displays,
* small monitors, etc. (for rel="preload")
*/
imageSrcSet: string;
/**
* Image sizes for different page layouts (for rel="preload")
*/
imageSizes?: string;
}
/**
* Represents a `<link>` element.
*
* WHATWG Specification: https://html.spec.whatwg.org/multipage/semantics.html#the-link-element
*/
type HtmlLinkDescriptor = (HtmlLinkProps & Pick<Required<HtmlLinkProps>, "href">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "imageSizes">) | (HtmlLinkPreloadImage & Pick<Required<HtmlLinkPreloadImage>, "href"> & {
imageSizes?: never;
});
interface PageLinkDescriptor extends Omit<HtmlLinkDescriptor, "href" | "rel" | "type" | "sizes" | "imageSrcSet" | "imageSizes" | "as" | "color" | "title"> {
/**
* A [`nonce`](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Global_attributes/nonce)
* attribute to render on the [`<link>`](https://developer.mozilla.org/en-US/docs/Web/HTML/Element/link)
* element. If not provided in Framework Mode, it will default to any
* {@link ServerRouter | `<ServerRouter nonce>`} prop.
*/
nonce?: string | undefined;
/**
* The absolute path of the page to prefetch, e.g. `/absolute/path`.
*/
page: string;
}
type LinkDescriptor = HtmlLinkDescriptor | PageLinkDescriptor;
type Serializable = undefined | null | boolean | string | symbol | number | Array<Serializable> | {
[key: PropertyKey]: Serializable;
} | bigint | Date | URL | RegExp | Error | Map<Serializable, Serializable> | Set<Serializable> | Promise<Serializable>;
type Equal<X, Y> = (<T>() => T extends X ? 1 : 2) extends (<T>() => T extends Y ? 1 : 2) ? true : false;
type IsAny<T> = 0 extends 1 & T ? true : false;
type Func = (...args: any[]) => unknown;
/**
* A brand that can be applied to a type to indicate that it will serialize
* to a specific type when transported to the client from a loader.
* Only use this if you have additional serialization/deserialization logic
* in your application.
*/
type unstable_SerializesTo<T> = {
unstable__ReactRouter_SerializesTo: [T];
};
type Serialize<T> = T extends unstable_SerializesTo<infer To> ? To : T extends Serializable ? T : T extends (...args: any[]) => unknown ? undefined : T extends Promise<infer U> ? Promise<Serialize<U>> : T extends Map<infer K, infer V> ? Map<Serialize<K>, Serialize<V>> : T extends ReadonlyMap<infer K, infer V> ? ReadonlyMap<Serialize<K>, Serialize<V>> : T extends Set<infer U> ? Set<Serialize<U>> : T extends ReadonlySet<infer U> ? ReadonlySet<Serialize<U>> : T extends [] ? [] : T extends readonly [infer F, ...infer R] ? [Serialize<F>, ...Serialize<R>] : T extends Array<infer U> ? Array<Serialize<U>> : T extends readonly unknown[] ? readonly Serialize<T[number]>[] : T extends Record<any, any> ? {
[K in keyof T]: Serialize<T[K]>;
} : undefined;
type VoidToUndefined<T> = Equal<T, void> extends true ? undefined : T;
type DataFrom<T> = IsAny<T> extends true ? undefined : T extends Func ? VoidToUndefined<Awaited<ReturnType<T>>> : undefined;
type ClientData<T> = T extends Response ? never : T extends DataWithResponseInit<infer U> ? U : T;
type ServerData<T> = T extends Response ? never : T extends DataWithResponseInit<infer U> ? Serialize<U> : Serialize<T>;
type ServerDataFrom<T> = ServerData<DataFrom<T>>;
type ClientDataFrom<T> = ClientData<DataFrom<T>>;
type ClientDataFunctionArgs<Params> = {
/**
* A {@link https://developer.mozilla.org/en-US/docs/Web/API/Request Fetch Request instance} which you can use to read the URL, the method, the "content-type" header, and the request body from the request.
*
* @note Because client data functions are called before a network request is made, the Request object does not include the headers which the browser automatically adds. React Router infers the "content-type" header from the enc-type of the form that performed the submission.
**/
request: Request;
/**
* A URL instance representing the application location being navigated to or
* fetched. By default, this matches `request.url`.
*
* In Framework mode with `future.v8_passThroughRequests` enabled, this is a
* normalized URL with React-Router-specific implementation details removed
* (`.data` suffixes, `index`/`_routes` search params).
*/
url: URL;
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function clientLoader({
* params,
* }: Route.ClientLoaderArgs) {
* params.teamId;
* // ^ string
* }
**/
params: Params;
/**
* Matched un-interpolated route pattern for the current path (i.e., /blog/:slug).
* Mostly useful as a identifier to aggregate on for logging/tracing/etc.
*/
pattern: string;
/**
* When `future.v8_middleware` is not enabled, this is undefined.
*
* When `future.v8_middleware` is enabled, this is an instance of
* `RouterContextProvider` and can be used to access context values
* from your route middlewares. You may pass in initial context values in your
* `<HydratedRouter getContext>` prop
*/
context: Readonly<RouterContextProvider>;
};
type SerializeFrom<T> = T extends (...args: infer Args) => unknown ? Args extends [
ClientLoaderFunctionArgs | ClientActionFunctionArgs | ClientDataFunctionArgs<unknown>
] ? ClientDataFrom<T> : ServerDataFrom<T> : T;
/**
* A function that handles data mutations for a route on the client
*/
type ClientActionFunction = (args: ClientActionFunctionArgs) => ReturnType<ActionFunction>;
/**
* Arguments passed to a route `clientAction` function
*/
type ClientActionFunctionArgs = ActionFunctionArgs & {
serverAction: <T = unknown>() => Promise<SerializeFrom<T>>;
};
/**
* A function that loads data for a route on the client
*/
type ClientLoaderFunction = ((args: ClientLoaderFunctionArgs) => ReturnType<LoaderFunction>) & {
hydrate?: boolean;
};
/**
* Arguments passed to a route `clientLoader` function
*/
type ClientLoaderFunctionArgs = LoaderFunctionArgs & {
serverLoader: <T = unknown>() => Promise<SerializeFrom<T>>;
};
type HeadersArgs = {
loaderHeaders: Headers;
parentHeaders: Headers;
actionHeaders: Headers;
errorHeaders: Headers | undefined;
};
/**
* A function that returns HTTP headers to be used for a route. These headers
* will be merged with (and take precedence over) headers from parent routes.
*/
interface HeadersFunction {
(args: HeadersArgs): Headers | HeadersInit;
}
/**
* A function that defines `<link>` tags to be inserted into the `<head>` of
* the document on route transitions.
*
* @see https://reactrouter.com/start/framework/route-module#meta
*/
interface LinksFunction {
(): LinkDescriptor[];
}
interface MetaMatch<RouteId extends string = string, Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown> {
id: RouteId;
pathname: DataRouteMatch["pathname"];
/** @deprecated Use `MetaMatch.loaderData` instead */
data: Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown;
loaderData: Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown;
handle?: RouteHandle;
params: DataRouteMatch["params"];
meta: MetaDescriptor[];
error?: unknown;
}
type MetaMatches<MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> = Array<{
[K in keyof MatchLoaders]: MetaMatch<Exclude<K, number | symbol>, MatchLoaders[K]>;
}[keyof MatchLoaders]>;
interface MetaArgs<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> {
/** @deprecated Use `MetaArgs.loaderData` instead */
data: (Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown) | undefined;
loaderData: (Loader extends LoaderFunction | ClientLoaderFunction ? SerializeFrom<Loader> : unknown) | undefined;
params: Params;
location: Location;
matches: MetaMatches<MatchLoaders>;
error?: unknown;
}
/**
* A function that returns an array of data objects to use for rendering
* metadata HTML tags in a route. These tags are not rendered on descendant
* routes in the route hierarchy. In other words, they will only be rendered on
* the route in which they are exported.
*
* @param Loader - The type of the current route's loader function
* @param MatchLoaders - Mapping from a parent route's filepath to its loader
* function type
*
* Note that parent route filepaths are relative to the `app/` directory.
*
* For example, if this meta function is for `/sales/customers/$customerId`:
*
* ```ts
* // app/root.tsx
* const loader = () => ({ hello: "world" })
* export type Loader = typeof loader
*
* // app/routes/sales.tsx
* const loader = () => ({ salesCount: 1074 })
* export type Loader = typeof loader
*
* // app/routes/sales/customers.tsx
* const loader = () => ({ customerCount: 74 })
* export type Loader = typeof loader
*
* // app/routes/sales/customers/$customersId.tsx
* import type { Loader as RootLoader } from "../../../root"
* import type { Loader as SalesLoader } from "../../sales"
* import type { Loader as CustomersLoader } from "../../sales/customers"
*
* const loader = () => ({ name: "Customer name" })
*
* const meta: MetaFunction<typeof loader, {
* "root": RootLoader,
* "routes/sales": SalesLoader,
* "routes/sales/customers": CustomersLoader,
* }> = ({ data, matches }) => {
* const { name } = data
* // ^? string
* const { customerCount } = matches.find((match) => match.id === "routes/sales/customers").data
* // ^? number
* const { salesCount } = matches.find((match) => match.id === "routes/sales").data
* // ^? number
* const { hello } = matches.find((match) => match.id === "root").data
* // ^? "world"
* }
* ```
*/
interface MetaFunction<Loader extends LoaderFunction | ClientLoaderFunction | unknown = unknown, MatchLoaders extends Record<string, LoaderFunction | ClientLoaderFunction | unknown> = Record<string, unknown>> {
(args: MetaArgs<Loader, MatchLoaders>): MetaDescriptor[] | undefined;
}
type MetaDescriptor = {
charSet: "utf-8";
} | {
title: string;
} | {
name: string;
content: string;
} | {
property: string;
content: string;
} | {
httpEquiv: string;
content: string;
} | {
"script:ld+json": LdJsonObject | LdJsonObject[];
} | {
tagName: "meta" | "link";
[name: string]: string;
} | {
[name: string]: unknown;
};
type LdJsonObject = {
[Key in string]: LdJsonValue;
} & {
[Key in string]?: LdJsonValue | undefined;
};
type LdJsonArray = LdJsonValue[] | readonly LdJsonValue[];
type LdJsonPrimitive = string | number | boolean | null;
type LdJsonValue = LdJsonPrimitive | LdJsonObject | LdJsonArray;
/**
* An arbitrary object that is associated with a route.
*
* @see https://reactrouter.com/how-to/using-handle
*/
type RouteHandle = unknown;
interface AwaitResolveRenderFunction<Resolve = any> {
(data: Awaited<Resolve>): React.ReactNode;
}
/**
* @category Types
*/
interface AwaitProps<Resolve> {
/**
* When using a function, the resolved value is provided as the parameter.
*
* ```tsx [2]
* <Await resolve={reviewsPromise}>
* {(resolvedReviews) => <Reviews items={resolvedReviews} />}
* </Await>
* ```
*
* When using React elements, {@link useAsyncValue} will provide the
* resolved value:
*
* ```tsx [2]
* <Await resolve={reviewsPromise}>
* <Reviews />
* </Await>
*
* function Reviews() {
* const resolvedReviews = useAsyncValue();
* return <div>...</div>;
* }
* ```
*/
children: React.ReactNode | AwaitResolveRenderFunction<Resolve>;
/**
* The error element renders instead of the `children` when the [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* rejects.
*
* ```tsx
* <Await
* errorElement={<div>Oops</div>}
* resolve={reviewsPromise}
* >
* <Reviews />
* </Await>
* ```
*
* To provide a more contextual error, you can use the {@link useAsyncError} in a
* child component
*
* ```tsx
* <Await
* errorElement={<ReviewsError />}
* resolve={reviewsPromise}
* >
* <Reviews />
* </Await>
*
* function ReviewsError() {
* const error = useAsyncError();
* return <div>Error loading reviews: {error.message}</div>;
* }
* ```
*
* If you do not provide an `errorElement`, the rejected value will bubble up
* to the nearest route-level [`ErrorBoundary`](../../start/framework/route-module#errorboundary)
* and be accessible via the {@link useRouteError} hook.
*/
errorElement?: React.ReactNode;
/**
* Takes a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* returned from a [`loader`](../../start/framework/route-module#loader) to be
* resolved and rendered.
*
* ```tsx
* import { Await, useLoaderData } from "react-router";
*
* export async function loader() {
* let reviews = getReviews(); // not awaited
* let book = await getBook();
* return {
* book,
* reviews, // this is a promise
* };
* }
*
* export default function Book() {
* const {
* book,
* reviews, // this is the same promise
* } = useLoaderData();
*
* return (
* <div>
* <h1>{book.title}</h1>
* <p>{book.description}</p>
* <React.Suspense fallback={<ReviewsSkeleton />}>
* <Await
* // and is the promise we pass to Await
* resolve={reviews}
* >
* <Reviews />
* </Await>
* </React.Suspense>
* </div>
* );
* }
* ```
*/
resolve: Resolve;
}
/**
* Used to render promise values with automatic error handling.
*
* **Note:** `<Await>` expects to be rendered inside a [`<React.Suspense>`](https://react.dev/reference/react/Suspense)
*
* @example
* import { Await, useLoaderData } from "react-router";
*
* export async function loader() {
* // not awaited
* const reviews = getReviews();
* // awaited (blocks the transition)
* const book = await fetch("/api/book").then((res) => res.json());
* return { book, reviews };
* }
*
* function Book() {
* const { book, reviews } = useLoaderData();
* return (
* <div>
* <h1>{book.title}</h1>
* <p>{book.description}</p>
* <React.Suspense fallback={<ReviewsSkeleton />}>
* <Await
* resolve={reviews}
* errorElement={
* <div>Could not load reviews 😬</div>
* }
* children={(resolvedReviews) => (
* <Reviews items={resolvedReviews} />
* )}
* />
* </React.Suspense>
* </div>
* );
* }
*
* @public
* @category Components
* @mode framework
* @mode data
* @param props Props
* @param {AwaitProps.children} props.children n/a
* @param {AwaitProps.errorElement} props.errorElement n/a
* @param {AwaitProps.resolve} props.resolve n/a
* @returns React element for the rendered awaited value
*/
declare function Await$1<Resolve>({ children, errorElement, resolve, }: AwaitProps<Resolve>): React.JSX.Element;
declare function getRequest(): Request;
declare const redirect: typeof redirect$1;
declare const redirectDocument: typeof redirectDocument$1;
declare const replace: typeof replace$1;
declare const Await: typeof Await$1;
type RSCRouteConfigEntryBase = {
action?: ActionFunction;
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
ErrorBoundary?: React.ComponentType<any>;
handle?: any;
headers?: HeadersFunction;
HydrateFallback?: React.ComponentType<any>;
Layout?: React.ComponentType<any>;
links?: LinksFunction;
loader?: LoaderFunction;
meta?: MetaFunction;
shouldRevalidate?: ShouldRevalidateFunction;
};
type RSCRouteConfigEntry = RSCRouteConfigEntryBase & {
id: string;
path?: string;
Component?: React.ComponentType<any>;
lazy?: () => Promise<RSCRouteConfigEntryBase & ({
default?: React.ComponentType<any>;
Component?: never;
} | {
default?: never;
Component?: React.ComponentType<any>;
})>;
} & ({
index: true;
} | {
children?: RSCRouteConfigEntry[];
});
type RSCRouteConfig = Array<RSCRouteConfigEntry>;
type RSCRouteManifest = {
clientAction?: ClientActionFunction;
clientLoader?: ClientLoaderFunction;
element?: React.ReactElement | false;
errorElement?: React.ReactElement;
handle?: any;
hasAction: boolean;
hasComponent: boolean;
hasErrorBoundary: boolean;
hasLoader: boolean;
hydrateFallbackElement?: React.ReactElement;
id: string;
index?: boolean;
links?: LinksFunction;
meta?: MetaFunction;
parentId?: string;
path?: string;
shouldRevalidate?: ShouldRevalidateFunction;
};
type RSCRouteMatch = RSCRouteManifest & {
params: Params;
pathname: string;
pathnameBase: string;
};
type RSCRenderPayload = {
type: "render";
actionData: Record<string, any> | null;
basename: string | undefined;
errors: Record<string, any> | null;
loaderData: Record<string, any>;
location: Location;
routeDiscovery: RouteDiscovery;
matches: RSCRouteMatch[];
patches?: Promise<RSCRouteManifest[]>;
nonce?: string;
formState?: unknown;
};
type RSCManifestPayload = {
type: "manifest";
patches: Promise<RSCRouteManifest[]>;
};
type RSCActionPayload = {
type: "action";
actionResult: Promise<unknown>;
rerender?: Promise<RSCRenderPayload | RSCRedirectPayload>;
};
type RSCRedirectPayload = {
type: "redirect";
status: number;
location: string;
replace: boolean;
reload: boolean;
actionResult?: Promise<unknown>;
};
type RSCPayload = RSCRenderPayload | RSCManifestPayload | RSCActionPayload | RSCRedirectPayload;
type RSCMatch = {
statusCode: number;
headers: Headers;
payload: RSCPayload;
};
type DecodeActionFunction = (formData: FormData) => Promise<() => Promise<unknown>>;
type DecodeFormStateFunction = (result: unknown, formData: FormData) => unknown;
type DecodeReplyFunction = (reply: FormData | string, options: {
temporaryReferences: unknown;
}) => Promise<unknown[]>;
type LoadServerActionFunction = (id: string) => Promise<Function>;
type RouteDiscovery = {
mode: "lazy";
manifestPath?: string | undefined;
} | {
mode: "initial";
};
/**
* Matches the given routes to a [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* and returns an [RSC](https://react.dev/reference/rsc/server-components)
* [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* encoding an {@link unstable_RSCPayload} for consumption by an [RSC](https://react.dev/reference/rsc/server-components)
* enabled client router.
*
* @example
* import {
* createTemporaryReferenceSet,
* decodeAction,
* decodeReply,
* loadServerAction,
* renderToReadableStream,
* } from "@vitejs/plugin-rsc/rsc";
* import { unstable_matchRSCServerRequest as matchRSCServerRequest } from "react-router";
*
* matchRSCServerRequest({
* createTemporaryReferenceSet,
* decodeAction,
* decodeFormState,
* decodeReply,
* loadServerAction,
* request,
* routes: routes(),
* generateResponse(match) {
* return new Response(
* renderToReadableStream(match.payload),
* {
* status: match.statusCode,
* headers: match.headers,
* }
* );
* },
* });
*
* @name unstable_matchRSCServerRequest
* @public
* @category RSC
* @mode data
* @param opts Options
* @param opts.allowedActionOrigins Origin patterns that are allowed to execute actions.
* @param opts.basename The basename to use when matching the request.
* @param opts.createTemporaryReferenceSet A function that returns a temporary
* reference set for the request, used to track temporary references in the [RSC](https://react.dev/reference/rsc/server-components)
* stream.
* @param opts.decodeAction Your `react-server-dom-xyz/server`'s `decodeAction`
* function, responsible for loading a server action.
* @param opts.decodeFormState A function responsible for decoding form state for
* progressively enhanceable forms with React's [`useActionState`](https://react.dev/reference/react/useActionState)
* using your `react-server-dom-xyz/server`'s `decodeFormState`.
* @param opts.decodeReply Your `react-server-dom-xyz/server`'s `decodeReply`
* function, used to decode the server function's arguments and bind them to the
* implementation for invocation by the router.
* @param opts.generateResponse A function responsible for using your
* `renderToReadableStream` to generate a [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* encoding the {@link unstable_RSCPayload}.
* @param opts.loadServerAction Your `react-server-dom-xyz/server`'s
* `loadServerAction` function, used to load a server action by ID.
* @param opts.onError An optional error handler that will be called with any
* errors that occur during the request processing.
* @param opts.request The [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* to match against.
* @param opts.requestContext An instance of {@link RouterContextProvider}
* that should be created per request, to be passed to [`action`](../../start/data/route-object#action)s,
* [`loader`](../../start/data/route-object#loader)s and [middleware](../../how-to/middleware).
* @param opts.routeDiscovery The route discovery configuration, used to determine how the router should discover new routes during navigations.
* @param opts.routes Your {@link unstable_RSCRouteConfigEntry | route definitions}.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that contains the [RSC](https://react.dev/reference/rsc/server-components)
* data for hydration.
*/
declare function matchRSCServerRequest({ allowedActionOrigins, createTemporaryReferenceSet, basename, decodeReply, requestContext, routeDiscovery, loadServerAction, decodeAction, decodeFormState, onError, request, routes, generateResponse, }: {
allowedActionOrigins?: string[];
createTemporaryReferenceSet: () => unknown;
basename?: string;
decodeReply?: DecodeReplyFunction;
decodeAction?: DecodeActionFunction;
decodeFormState?: DecodeFormStateFunction;
requestContext?: RouterContextProvider;
loadServerAction?: LoadServerActionFunction;
onError?: (error: unknown) => void;
request: Request;
routes: RSCRouteConfigEntry[];
routeDiscovery?: RouteDiscovery;
generateResponse: (match: RSCMatch, { onError, temporaryReferences, }: {
onError(error: unknown): string | undefined;
temporaryReferences: unknown;
}) => Response;
}): Promise<Response>;
/**
* Apps can use this interface to "register" app-wide types for React Router via interface declaration merging and module augmentation.
* React Router should handle this for you via type generation.
*
* For more on declaration merging and module augmentation, see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation .
*/
interface Register {
}
type AnyParams = Record<string, string | undefined>;
type AnyPages = Record<string, {
params: AnyParams;
}>;
type Pages = Register extends {
pages: infer Registered extends AnyPages;
} ? Registered : AnyPages;
type Args = {
[K in keyof Pages]: ToArgs<Pages[K]["params"]>;
};
type ToArgs<Params extends Record<string, string | undefined>> = Equal<Params, {}> extends true ? [] : Partial<Params> extends Params ? [Params] | [] : [
Params
];
/**
Returns a resolved URL path for the specified route.
```tsx
const h = href("/:lang?/about", { lang: "en" })
// -> `/en/about`
<Link to={href("/products/:id", { id: "abc123" })} />
```
*/
declare function href<Path extends keyof Args>(path: Path, ...args: Args[Path]): string;
interface CookieSignatureOptions {
/**
* An array of secrets that may be used to sign/unsign the value of a cookie.
*
* The array makes it easy to rotate secrets. New secrets should be added to
* the beginning of the array. `cookie.serialize()` will always use the first
* value in the array, but `cookie.parse()` may use any of them so that
* cookies that were signed with older secrets still work.
*/
secrets?: string[];
}
type CookieOptions = ParseOptions & SerializeOptions & CookieSignatureOptions;
/**
* A HTTP cookie.
*
* A Cookie is a logical container for metadata about a HTTP cookie; its name
* and options. But it doesn't contain a value. Instead, it has `parse()` and
* `serialize()` methods that allow a single instance to be reused for
* parsing/encoding multiple different values.
*
* @see https://remix.run/utils/cookies#cookie-api
*/
interface Cookie {
/**
* The name of the cookie, used in the `Cookie` and `Set-Cookie` headers.
*/
readonly name: string;
/**
* True if this cookie uses one or more secrets for verification.
*/
readonly isSigned: boolean;
/**
* The Date this cookie expires.
*
* Note: This is calculated at access time using `maxAge` when no `expires`
* option is provided to `createCookie()`.
*/
readonly expires?: Date;
/**
* Parses a raw `Cookie` header and returns the value of this cookie or
* `null` if it's not present.
*/
parse(cookieHeader: string | null, options?: ParseOptions): Promise<any>;
/**
* Serializes the given value to a string and returns the `Set-Cookie`
* header.
*/
serialize(value: any, options?: SerializeOptions): Promise<string>;
}
/**
* Creates a logical container for managing a browser cookie from the server.
*/
declare const createCookie: (name: string, cookieOptions?: CookieOptions) => Cookie;
type IsCookieFunction = (object: any) => object is Cookie;
/**
* Returns true if an object is a Remix cookie container.
*
* @see https://remix.run/utils/cookies#iscookie
*/
declare const isCookie: IsCookieFunction;
/**
* An object of name/value pairs to be used in the session.
*/
interface SessionData {
[name: string]: any;
}
/**
* Session persists data across HTTP requests.
*
* @see https://reactrouter.com/explanation/sessions-and-cookies#sessions
*/
interface Session<Data = SessionData, FlashData = Data> {
/**
* A unique identifier for this session.
*
* Note: This will be the empty string for newly created sessions and
* sessions that are not backed by a database (i.e. cookie-based sessions).
*/
readonly id: string;
/**
* The raw data contained in this session.
*
* This is useful mostly for SessionStorage internally to access the raw
* session data to persist.
*/
readonly data: FlashSessionData<Data, FlashData>;
/**
* Returns `true` if the session has a value for the given `name`, `false`
* otherwise.
*/
has(name: (keyof Data | keyof FlashData) & string): boolean;
/**
* Returns the value for the given `name` in this session.
*/
get<Key extends (keyof Data | keyof FlashData) & string>(name: Key): (Key extends keyof Data ? Data[Key] : undefined) | (Key extends keyof FlashData ? FlashData[Key] : undefined) | undefined;
/**
* Sets a value in the session for the given `name`.
*/
set<Key extends keyof Data & string>(name: Key, value: Data[Key]): void;
/**
* Sets a value in the session that is only valid until the next `get()`.
* This can be useful for temporary values, like error messages.
*/
flash<Key extends keyof FlashData & string>(name: Key, value: FlashData[Key]): void;
/**
* Removes a value from the session.
*/
unset(name: keyof Data & string): void;
}
type FlashSessionData<Data, FlashData> = Partial<Data & {
[Key in keyof FlashData as FlashDataKey<Key & string>]: FlashData[Key];
}>;
type FlashDataKey<Key extends string> = `__flash_${Key}__`;
type CreateSessionFunction = <Data = SessionData, FlashData = Data>(initialData?: Data, id?: string) => Session<Data, FlashData>;
/**
* Creates a new Session object.
*
* Note: This function is typically not invoked directly by application code.
* Instead, use a `SessionStorage` object's `getSession` method.
*/
declare const createSession: CreateSessionFunction;
type IsSessionFunction = (object: any) => object is Session;
/**
* Returns true if an object is a React Router session.
*
* @see https://reactrouter.com/api/utils/isSession
*/
declare const isSession: IsSessionFunction;
/**
* SessionStorage stores session data between HTTP requests and knows how to
* parse and create cookies.
*
* A SessionStorage creates Session objects using a `Cookie` header as input.
* Then, later it generates the `Set-Cookie` header to be used in the response.
*/
interface SessionStorage<Data = SessionData, FlashData = Data> {
/**
* Parses a Cookie header from a HTTP request and returns the associated
* Session. If there is no session associated with the cookie, this will
* return a new Session with no data.
*/
getSession: (cookieHeader?: string | null, options?: ParseOptions) => Promise<Session<Data, FlashData>>;
/**
* Stores all data in the Session and returns the Set-Cookie header to be
* used in the HTTP response.
*/
commitSession: (session: Session<Data, FlashData>, options?: SerializeOptions) => Promise<string>;
/**
* Deletes all data associated with the Session and returns the Set-Cookie
* header to be used in the HTTP response.
*/
destroySession: (session: Session<Data, FlashData>, options?: SerializeOptions) => Promise<string>;
}
/**
* SessionIdStorageStrategy is designed to allow anyone to easily build their
* own SessionStorage using `createSessionStorage(strategy)`.
*
* This strategy describes a common scenario where the session id is stored in
* a cookie but the actual session data is stored elsewhere, usually in a
* database or on disk. A set of create, read, update, and delete operations
* are provided for managing the session data.
*/
interface SessionIdStorageStrategy<Data = SessionData, FlashData = Data> {
/**
* The Cookie used to store the session id, or options used to automatically
* create one.
*/
cookie?: Cookie | (CookieOptions & {
name?: string;
});
/**
* Creates a new record with the given data and returns the session id.
*/
createData: (data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<string>;
/**
* Returns data for a given session id, or `null` if there isn't any.
*/
readData: (id: string) => Promise<FlashSessionData<Data, FlashData> | null>;
/**
* Updates data for the given session id.
*/
updateData: (id: string, data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<void>;
/**
* Deletes data for a given session id from the data store.
*/
deleteData: (id: string) => Promise<void>;
}
/**
* Creates a SessionStorage object using a SessionIdStorageStrategy.
*
* Note: This is a low-level API that should only be used if none of the
* existing session storage options meet your requirements.
*/
declare function createSessionStorage<Data = SessionData, FlashData = Data>({ cookie: cookieArg, createData, readData, updateData, deleteData, }: SessionIdStorageStrategy<Data, FlashData>): SessionStorage<Data, FlashData>;
interface CookieSessionStorageOptions {
/**
* The Cookie used to store the session data on the client, or options used
* to automatically create one.
*/
cookie?: SessionIdStorageStrategy["cookie"];
}
/**
* Creates and returns a SessionStorage object that stores all session data
* directly in the session cookie itself.
*
* This has the advantage that no database or other backend services are
* needed, and can help to simplify some load-balanced scenarios. However, it
* also has the limitation that serialized session data may not exceed the
* browser's maximum cookie size. Trade-offs!
*/
declare function createCookieSessionStorage<Data = SessionData, FlashData = Data>({ cookie: cookieArg }?: CookieSessionStorageOptions): SessionStorage<Data, FlashData>;
interface MemorySessionStorageOptions {
/**
* The Cookie used to store the session id on the client, or options used
* to automatically create one.
*/
cookie?: SessionIdStorageStrategy["cookie"];
}
/**
* Creates and returns a simple in-memory SessionStorage object, mostly useful
* for testing and as a reference implementation.
*
* Note: This storage does not scale beyond a single process, so it is not
* suitable for most production scenarios.
*/
declare function createMemorySessionStorage<Data = SessionData, FlashData = Data>({ cookie }?: MemorySessionStorageOptions): SessionStorage<Data, FlashData>;
export { Await, type Cookie, type CookieOptions, type CookieSignatureOptions, type FlashSessionData, type IsCookieFunction, type IsSessionFunction, type MiddlewareFunction, type MiddlewareNextFunction, type RouterContext, RouterContextProvider, type Session, type SessionData, type SessionIdStorageStrategy, type SessionStorage, createContext, createCookie, createCookieSessionStorage, createMemorySessionStorage, createSession, createSessionStorage, createStaticHandler, data, href, isCookie, isRouteErrorResponse, isSession, matchRoutes, redirect, redirectDocument, replace, type DecodeActionFunction as unstable_DecodeActionFunction, type DecodeFormStateFunction as unstable_DecodeFormStateFunction, type DecodeReplyFunction as unstable_DecodeReplyFunction, type LoadServerActionFunction as unstable_LoadServerActionFunction, type RSCManifestPayload as unstable_RSCManifestPayload, type RSCMatch as unstable_RSCMatch, type RSCPayload as unstable_RSCPayload, type RSCRenderPayload as unstable_RSCRenderPayload, type RSCRouteConfig as unstable_RSCRouteConfig, type RSCRouteConfigEntry as unstable_RSCRouteConfigEntry, type RSCRouteManifest as unstable_RSCRouteManifest, type RSCRouteMatch as unstable_RSCRouteMatch, getRequest as unstable_getRequest, matchRSCServerRequest as unstable_matchRSCServerRequest };
+3915
View File
@@ -0,0 +1,3915 @@
'use strict';
var node_async_hooks = require('node:async_hooks');
var React3 = require('react');
var setCookieParser = require('set-cookie-parser');
var reactServerClient = require('react-router/internal/react-server-client');
var cookie = require('cookie');
function _interopNamespace(e) {
if (e && e.__esModule) return e;
var n = Object.create(null);
if (e) {
Object.keys(e).forEach(function (k) {
if (k !== 'default') {
var d = Object.getOwnPropertyDescriptor(e, k);
Object.defineProperty(n, k, d.get ? d : {
enumerable: true,
get: function () { return e[k]; }
});
}
});
}
n.default = e;
return Object.freeze(n);
}
var React3__namespace = /*#__PURE__*/_interopNamespace(React3);
/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
var __typeError = (msg) => {
throw TypeError(msg);
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
// lib/router/url.ts
var ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i;
// lib/router/history.ts
function invariant(value, message) {
if (value === false || value === null || typeof value === "undefined") {
throw new Error(message);
}
}
function warning(cond, message) {
if (!cond) {
if (typeof console !== "undefined") console.warn(message);
try {
throw new Error(message);
} catch (e) {
}
}
}
function createKey() {
return Math.random().toString(36).substring(2, 10);
}
function createLocation(current, to, state = null, key, mask) {
let location = {
pathname: typeof current === "string" ? current : current.pathname,
search: "",
hash: "",
...typeof to === "string" ? parsePath(to) : to,
state,
// TODO: This could be cleaned up. push/replace should probably just take
// full Locations now and avoid the need to run through this flow at all
// But that's a pretty big refactor to the current test suite so going to
// keep as is for the time being and just let any incoming keys take precedence
key: to && to.key || key || createKey(),
mask
};
return location;
}
function createPath({
pathname = "/",
search = "",
hash = ""
}) {
if (search && search !== "?")
pathname += search.charAt(0) === "?" ? search : "?" + search;
if (hash && hash !== "#")
pathname += hash.charAt(0) === "#" ? hash : "#" + hash;
return pathname;
}
function parsePath(path) {
let parsedPath = {};
if (path) {
let hashIndex = path.indexOf("#");
if (hashIndex >= 0) {
parsedPath.hash = path.substring(hashIndex);
path = path.substring(0, hashIndex);
}
let searchIndex = path.indexOf("?");
if (searchIndex >= 0) {
parsedPath.search = path.substring(searchIndex);
path = path.substring(0, searchIndex);
}
if (path) {
parsedPath.pathname = path;
}
}
return parsedPath;
}
// lib/router/instrumentation.ts
var UninstrumentedSymbol = Symbol("Uninstrumented");
function getRouteInstrumentationUpdates(fns, route) {
let aggregated = {
lazy: [],
"lazy.loader": [],
"lazy.action": [],
"lazy.middleware": [],
middleware: [],
loader: [],
action: []
};
fns.forEach(
(fn) => fn({
id: route.id,
index: route.index,
path: route.path,
instrument(i) {
let keys = Object.keys(aggregated);
for (let key of keys) {
if (i[key]) {
aggregated[key].push(i[key]);
}
}
}
})
);
let updates = {};
if (typeof route.lazy === "function" && aggregated.lazy.length > 0) {
let instrumented = wrapImpl(aggregated.lazy, route.lazy, () => void 0);
if (instrumented) {
updates.lazy = instrumented;
}
}
if (typeof route.lazy === "object") {
let lazyObject = route.lazy;
["middleware", "loader", "action"].forEach((key) => {
let lazyFn = lazyObject[key];
let instrumentations = aggregated[`lazy.${key}`];
if (typeof lazyFn === "function" && instrumentations.length > 0) {
let instrumented = wrapImpl(instrumentations, lazyFn, () => void 0);
if (instrumented) {
updates.lazy = Object.assign(updates.lazy || {}, {
[key]: instrumented
});
}
}
});
}
["loader", "action"].forEach((key) => {
let handler = route[key];
if (typeof handler === "function" && aggregated[key].length > 0) {
let original = handler[UninstrumentedSymbol] ?? handler;
let instrumented = wrapImpl(
aggregated[key],
original,
(...args) => getHandlerInfo(args[0])
);
if (instrumented) {
if (key === "loader" && original.hydrate === true) {
instrumented.hydrate = true;
}
instrumented[UninstrumentedSymbol] = original;
updates[key] = instrumented;
}
}
});
if (route.middleware && route.middleware.length > 0 && aggregated.middleware.length > 0) {
updates.middleware = route.middleware.map((middleware) => {
let original = middleware[UninstrumentedSymbol] ?? middleware;
let instrumented = wrapImpl(
aggregated.middleware,
original,
(...args) => getHandlerInfo(args[0])
);
if (instrumented) {
instrumented[UninstrumentedSymbol] = original;
return instrumented;
}
return middleware;
});
}
return updates;
}
function wrapImpl(impls, handler, getInfo) {
if (impls.length === 0) {
return null;
}
return async (...args) => {
let result = await recurseRight(
impls,
getInfo(...args),
() => handler(...args),
impls.length - 1
);
if (result.type === "error") {
throw result.value;
}
return result.value;
};
}
async function recurseRight(impls, info, handler, index) {
let impl = impls[index];
let result;
if (!impl) {
try {
let value = await handler();
result = { type: "success", value };
} catch (e) {
result = { type: "error", value: e };
}
} else {
let handlerPromise = void 0;
let callHandler = async () => {
if (handlerPromise) {
console.error("You cannot call instrumented handlers more than once");
} else {
handlerPromise = recurseRight(impls, info, handler, index - 1);
}
result = await handlerPromise;
invariant(result, "Expected a result");
if (result.type === "error" && result.value instanceof Error) {
return { status: "error", error: result.value };
}
return { status: "success", error: void 0 };
};
try {
await impl(callHandler, info);
} catch (e) {
console.error("An instrumentation function threw an error:", e);
}
if (!handlerPromise) {
await callHandler();
}
await handlerPromise;
}
if (result) {
return result;
}
return {
type: "error",
value: new Error("No result assigned in instrumentation chain.")
};
}
function getHandlerInfo(args) {
let { request, context, params, pattern } = args;
return {
request: getReadonlyRequest(request),
params: { ...params },
pattern,
context: getReadonlyContext(context)
};
}
function getReadonlyRequest(request) {
return {
method: request.method,
url: request.url,
headers: {
get: (...args) => request.headers.get(...args)
}
};
}
function getReadonlyContext(context) {
if (isPlainObject(context)) {
let frozen = { ...context };
Object.freeze(frozen);
return frozen;
} else {
return {
get: (ctx) => context.get(ctx)
};
}
}
var objectProtoNames = Object.getOwnPropertyNames(Object.prototype).sort().join("\0");
function isPlainObject(thing) {
if (thing === null || typeof thing !== "object") {
return false;
}
const proto = Object.getPrototypeOf(thing);
return proto === Object.prototype || proto === null || Object.getOwnPropertyNames(proto).sort().join("\0") === objectProtoNames;
}
// lib/router/utils.ts
function createContext(defaultValue) {
return { defaultValue };
}
var _map;
var RouterContextProvider = class {
/**
* Create a new `RouterContextProvider` instance
* @param init An optional initial context map to populate the provider with
*/
constructor(init) {
__privateAdd(this, _map, /* @__PURE__ */ new Map());
if (init) {
for (let [context, value] of init) {
this.set(context, value);
}
}
}
/**
* Access a value from the context. If no value has been set for the context,
* it will return the context's `defaultValue` if provided, or throw an error
* if no `defaultValue` was set.
* @param context The context to get the value for
* @returns The value for the context, or the context's `defaultValue` if no
* value was set
*/
get(context) {
if (__privateGet(this, _map).has(context)) {
return __privateGet(this, _map).get(context);
}
if (context.defaultValue !== void 0) {
return context.defaultValue;
}
throw new Error("No value found for context");
}
/**
* Set a value for the context. If the context already has a value set, this
* will overwrite it.
*
* @param context The context to set the value for
* @param value The value to set for the context
* @returns {void}
*/
set(context, value) {
__privateGet(this, _map).set(context, value);
}
};
_map = new WeakMap();
var unsupportedLazyRouteObjectKeys = /* @__PURE__ */ new Set([
"lazy",
"caseSensitive",
"path",
"id",
"index",
"children"
]);
function isUnsupportedLazyRouteObjectKey(key) {
return unsupportedLazyRouteObjectKeys.has(
key
);
}
var unsupportedLazyRouteFunctionKeys = /* @__PURE__ */ new Set([
"lazy",
"caseSensitive",
"path",
"id",
"index",
"middleware",
"children"
]);
function isUnsupportedLazyRouteFunctionKey(key) {
return unsupportedLazyRouteFunctionKeys.has(
key
);
}
function isIndexRoute(route) {
return route.index === true;
}
function convertRoutesToDataRoutes(routes, mapRouteProperties, parentPath = [], manifest = {}, allowInPlaceMutations = false) {
return routes.map((route, index) => {
let treePath = [...parentPath, String(index)];
let id = typeof route.id === "string" ? route.id : treePath.join("-");
invariant(
route.index !== true || !route.children,
`Cannot specify children on an index route`
);
invariant(
allowInPlaceMutations || !manifest[id],
`Found a route id collision on id "${id}". Route id's must be globally unique within Data Router usages`
);
if (isIndexRoute(route)) {
let indexRoute = {
...route,
id
};
manifest[id] = mergeRouteUpdates(
indexRoute,
mapRouteProperties(indexRoute)
);
return indexRoute;
} else {
let pathOrLayoutRoute = {
...route,
id,
children: void 0
};
manifest[id] = mergeRouteUpdates(
pathOrLayoutRoute,
mapRouteProperties(pathOrLayoutRoute)
);
if (route.children) {
pathOrLayoutRoute.children = convertRoutesToDataRoutes(
route.children,
mapRouteProperties,
treePath,
manifest,
allowInPlaceMutations
);
}
return pathOrLayoutRoute;
}
});
}
function mergeRouteUpdates(route, updates) {
return Object.assign(route, {
...updates,
...typeof updates.lazy === "object" && updates.lazy != null ? {
lazy: {
...route.lazy,
...updates.lazy
}
} : {}
});
}
function matchRoutes(routes, locationArg, basename = "/") {
return matchRoutesImpl(routes, locationArg, basename, false);
}
function matchRoutesImpl(routes, locationArg, basename, allowPartial, precomputedBranches) {
let location = typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
let pathname = stripBasename(location.pathname || "/", basename);
if (pathname == null) {
return null;
}
let branches = precomputedBranches ?? flattenAndRankRoutes(routes);
let matches = null;
let decoded = decodePath(pathname);
for (let i = 0; matches == null && i < branches.length; ++i) {
matches = matchRouteBranch(
branches[i],
decoded,
allowPartial
);
}
return matches;
}
function convertRouteMatchToUiMatch(match, loaderData) {
let { route, pathname, params } = match;
return {
id: route.id,
pathname,
params,
data: loaderData[route.id],
loaderData: loaderData[route.id],
handle: route.handle
};
}
function flattenAndRankRoutes(routes) {
let branches = flattenRoutes(routes);
rankRouteBranches(branches);
return branches;
}
function flattenRoutes(routes, branches = [], parentsMeta = [], parentPath = "", _hasParentOptionalSegments = false) {
let flattenRoute = (route, index, hasParentOptionalSegments = _hasParentOptionalSegments, relativePath) => {
let meta = {
relativePath: relativePath === void 0 ? route.path || "" : relativePath,
caseSensitive: route.caseSensitive === true,
childrenIndex: index,
route
};
if (meta.relativePath.startsWith("/")) {
if (!meta.relativePath.startsWith(parentPath) && hasParentOptionalSegments) {
return;
}
invariant(
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.`
);
meta.relativePath = meta.relativePath.slice(parentPath.length);
}
let path = joinPaths([parentPath, meta.relativePath]);
let routesMeta = parentsMeta.concat(meta);
if (route.children && route.children.length > 0) {
invariant(
// Our types know better, but runtime JS may not!
// @ts-expect-error
route.index !== true,
`Index routes must not have child routes. Please remove all child routes from route path "${path}".`
);
flattenRoutes(
route.children,
branches,
routesMeta,
path,
hasParentOptionalSegments
);
}
if (route.path == null && !route.index) {
return;
}
branches.push({
path,
score: computeScore(path, route.index),
routesMeta: routesMeta.map((meta2, i) => {
let [matcher, params] = compilePath(
meta2.relativePath,
meta2.caseSensitive,
i === routesMeta.length - 1
);
return {
...meta2,
matcher,
compiledParams: params
};
})
});
};
routes.forEach((route, index) => {
if (route.path === "" || !route.path?.includes("?")) {
flattenRoute(route, index);
} else {
for (let exploded of explodeOptionalSegments(route.path)) {
flattenRoute(route, index, true, exploded);
}
}
});
return branches;
}
function explodeOptionalSegments(path) {
let segments = path.split("/");
if (segments.length === 0) return [];
let [first, ...rest] = segments;
let isOptional = first.endsWith("?");
let required = first.replace(/\?$/, "");
if (rest.length === 0) {
return isOptional ? [required, ""] : [required];
}
let restExploded = explodeOptionalSegments(rest.join("/"));
let result = [];
result.push(
...restExploded.map(
(subpath) => subpath === "" ? required : [required, subpath].join("/")
)
);
if (isOptional) {
result.push(...restExploded);
}
return result.map(
(exploded) => path.startsWith("/") && exploded === "" ? "/" : exploded
);
}
function rankRouteBranches(branches) {
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)
)
);
}
var paramRe = /^:[\w-]+$/;
var dynamicSegmentValue = 3;
var indexRouteValue = 2;
var emptySegmentValue = 1;
var staticSegmentValue = 10;
var splatPenalty = -2;
var isSplat = (s) => s === "*";
function computeScore(path, index) {
let segments = path.split("/");
let initialScore = segments.length;
if (segments.some(isSplat)) {
initialScore += splatPenalty;
}
if (index) {
initialScore += indexRouteValue;
}
return segments.filter((s) => !isSplat(s)).reduce(
(score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue),
initialScore
);
}
function compareIndexes(a, b) {
let siblings = a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]);
return siblings ? (
// If two routes are siblings, we should try to match the earlier sibling
// first. This allows people to have fine-grained control over the matching
// behavior by simply putting routes with identical paths in the order they
// want them tried.
a[a.length - 1] - b[b.length - 1]
) : (
// Otherwise, it doesn't really make sense to rank non-siblings by index,
// so they sort equally.
0
);
}
function matchRouteBranch(branch, pathname, allowPartial = false) {
let { routesMeta } = branch;
let matchedParams = {};
let matchedPathname = "/";
let matches = [];
for (let i = 0; i < routesMeta.length; ++i) {
let meta = routesMeta[i];
let end = i === routesMeta.length - 1;
let remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
let pattern = {
path: meta.relativePath,
caseSensitive: meta.caseSensitive,
end
};
let match = (
// Use precomputed matcher if it exists
meta.matcher && meta.compiledParams ? matchPathImpl(
pattern,
remainingPathname,
meta.matcher,
meta.compiledParams
) : matchPath(pattern, remainingPathname)
);
let route = meta.route;
if (!match && end && allowPartial && !routesMeta[routesMeta.length - 1].route.index) {
match = matchPath(
{
path: meta.relativePath,
caseSensitive: meta.caseSensitive,
end: false
},
remainingPathname
);
}
if (!match) {
return null;
}
Object.assign(matchedParams, match.params);
matches.push({
// TODO: Can this as be avoided?
params: matchedParams,
pathname: joinPaths([matchedPathname, match.pathname]),
pathnameBase: normalizePathname(
joinPaths([matchedPathname, match.pathnameBase])
),
route
});
if (match.pathnameBase !== "/") {
matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
}
}
return matches;
}
function matchPath(pattern, pathname) {
if (typeof pattern === "string") {
pattern = { path: pattern, caseSensitive: false, end: true };
}
let [matcher, compiledParams] = compilePath(
pattern.path,
pattern.caseSensitive,
pattern.end
);
return matchPathImpl(pattern, pathname, matcher, compiledParams);
}
function matchPathImpl(pattern, pathname, matcher, compiledParams) {
let match = pathname.match(matcher);
if (!match) return null;
let matchedPathname = match[0];
let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
let captureGroups = match.slice(1);
let params = compiledParams.reduce(
(memo, { paramName, isOptional }, index) => {
if (paramName === "*") {
let splatValue = captureGroups[index] || "";
pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
}
const value = captureGroups[index];
if (isOptional && !value) {
memo[paramName] = void 0;
} else {
memo[paramName] = (value || "").replace(/%2F/g, "/");
}
return memo;
},
{}
);
return {
params,
pathname: matchedPathname,
pathnameBase,
pattern
};
}
function compilePath(path, caseSensitive = false, end = true) {
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(/\*$/, "/*")}".`
);
let params = [];
let regexpSource = "^" + path.replace(/\/*\*?$/, "").replace(/^\/*/, "/").replace(/[\\.*+^${}|()[\]]/g, "\\$&").replace(
/\/:([\w-]+)(\?)?/g,
(match, paramName, isOptional, index, str) => {
params.push({ paramName, isOptional: isOptional != null });
if (isOptional) {
let nextChar = str.charAt(index + match.length);
if (nextChar && nextChar !== "/") {
return "/([^\\/]*)";
}
return "(?:/([^\\/]*))?";
}
return "/([^\\/]+)";
}
).replace(/\/([\w-]+)\?(\/|$)/g, "(/$1)?$2");
if (path.endsWith("*")) {
params.push({ paramName: "*" });
regexpSource += path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
} else if (end) {
regexpSource += "\\/*$";
} else if (path !== "" && path !== "/") {
regexpSource += "(?:(?=\\/|$))";
} else ;
let matcher = new RegExp(regexpSource, caseSensitive ? void 0 : "i");
return [matcher, params];
}
function decodePath(value) {
try {
return value.split("/").map((v) => decodeURIComponent(v).replace(/\//g, "%2F")).join("/");
} catch (error) {
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}).`
);
return value;
}
}
function stripBasename(pathname, basename) {
if (basename === "/") return pathname;
if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {
return null;
}
let startIndex = basename.endsWith("/") ? basename.length - 1 : basename.length;
let nextChar = pathname.charAt(startIndex);
if (nextChar && nextChar !== "/") {
return null;
}
return pathname.slice(startIndex) || "/";
}
function prependBasename({
basename,
pathname
}) {
return pathname === "/" ? basename : joinPaths([basename, pathname]);
}
var isAbsoluteUrl = (url) => ABSOLUTE_URL_REGEX.test(url);
function resolvePath(to, fromPathname = "/") {
let {
pathname: toPathname,
search = "",
hash = ""
} = typeof to === "string" ? parsePath(to) : to;
let pathname;
if (toPathname) {
toPathname = removeDoubleSlashes(toPathname);
if (toPathname.startsWith("/")) {
pathname = resolvePathname(toPathname.substring(1), "/");
} else {
pathname = resolvePathname(toPathname, fromPathname);
}
} else {
pathname = fromPathname;
}
return {
pathname,
search: normalizeSearch(search),
hash: normalizeHash(hash)
};
}
function resolvePathname(relativePath, fromPathname) {
let segments = removeTrailingSlash(fromPathname).split("/");
let relativeSegments = relativePath.split("/");
relativeSegments.forEach((segment) => {
if (segment === "..") {
if (segments.length > 1) segments.pop();
} else if (segment !== ".") {
segments.push(segment);
}
});
return segments.length > 1 ? segments.join("/") : "/";
}
function getInvalidPathError(char, field, dest, path) {
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.`;
}
function getPathContributingMatches(matches) {
return matches.filter(
(match, index) => index === 0 || match.route.path && match.route.path.length > 0
);
}
function getResolveToMatches(matches) {
let pathMatches = getPathContributingMatches(matches);
return pathMatches.map(
(match, idx) => idx === pathMatches.length - 1 ? match.pathname : match.pathnameBase
);
}
function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = false) {
let to;
if (typeof toArg === "string") {
to = parsePath(toArg);
} else {
to = { ...toArg };
invariant(
!to.pathname || !to.pathname.includes("?"),
getInvalidPathError("?", "pathname", "search", to)
);
invariant(
!to.pathname || !to.pathname.includes("#"),
getInvalidPathError("#", "pathname", "hash", to)
);
invariant(
!to.search || !to.search.includes("#"),
getInvalidPathError("#", "search", "hash", to)
);
}
let isEmptyPath = toArg === "" || to.pathname === "";
let toPathname = isEmptyPath ? "/" : to.pathname;
let from;
if (toPathname == null) {
from = locationPathname;
} else {
let routePathnameIndex = routePathnames.length - 1;
if (!isPathRelative && toPathname.startsWith("..")) {
let toSegments = toPathname.split("/");
while (toSegments[0] === "..") {
toSegments.shift();
routePathnameIndex -= 1;
}
to.pathname = toSegments.join("/");
}
from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
}
let path = resolvePath(to, from);
let hasExplicitTrailingSlash = toPathname && toPathname !== "/" && toPathname.endsWith("/");
let hasCurrentTrailingSlash = (isEmptyPath || toPathname === ".") && locationPathname.endsWith("/");
if (!path.pathname.endsWith("/") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) {
path.pathname += "/";
}
return path;
}
var removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
var joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
var removeTrailingSlash = (path) => path.replace(/\/+$/, "");
var normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
var normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
var normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
var DataWithResponseInit = class {
constructor(data2, init) {
this.type = "DataWithResponseInit";
this.data = data2;
this.init = init || null;
}
};
function data(data2, init) {
return new DataWithResponseInit(
data2,
typeof init === "number" ? { status: init } : init
);
}
var redirect = (url, init = 302) => {
let responseInit = init;
if (typeof responseInit === "number") {
responseInit = { status: responseInit };
} else if (typeof responseInit.status === "undefined") {
responseInit.status = 302;
}
let headers = new Headers(responseInit.headers);
headers.set("Location", url);
return new Response(null, { ...responseInit, headers });
};
var redirectDocument = (url, init) => {
let response = redirect(url, init);
response.headers.set("X-Remix-Reload-Document", "true");
return response;
};
var replace = (url, init) => {
let response = redirect(url, init);
response.headers.set("X-Remix-Replace", "true");
return response;
};
var ErrorResponseImpl = class {
constructor(status, statusText, data2, internal = false) {
this.status = status;
this.statusText = statusText || "";
this.internal = internal;
if (data2 instanceof Error) {
this.data = data2.toString();
this.error = data2;
} else {
this.data = data2;
}
}
};
function isRouteErrorResponse(error) {
return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error;
}
function getRoutePattern(matches) {
let parts = matches.map((m) => m.route.path).filter(Boolean);
return joinPaths(parts) || "/";
}
// lib/router/router.ts
var validMutationMethodsArr = [
"POST",
"PUT",
"PATCH",
"DELETE"
];
var validMutationMethods = new Set(
validMutationMethodsArr
);
var validRequestMethodsArr = [
"GET",
...validMutationMethodsArr
];
var validRequestMethods = new Set(validRequestMethodsArr);
var redirectStatusCodes = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
var defaultMapRouteProperties = (route) => ({
hasErrorBoundary: Boolean(route.hasErrorBoundary)
});
var ResetLoaderDataSymbol = Symbol("ResetLoaderData");
function createStaticHandler(routes, opts) {
invariant(
routes.length > 0,
"You must provide a non-empty routes array to createStaticHandler"
);
let manifest = {};
let basename = (opts ? opts.basename : null) || "/";
let _mapRouteProperties = opts?.mapRouteProperties || defaultMapRouteProperties;
let mapRouteProperties = _mapRouteProperties;
({
...opts?.future
});
if (opts?.instrumentations) {
let instrumentations = opts.instrumentations;
mapRouteProperties = (route) => {
return {
..._mapRouteProperties(route),
...getRouteInstrumentationUpdates(
instrumentations.map((i) => i.route).filter(Boolean),
route
)
};
};
}
let dataRoutes = convertRoutesToDataRoutes(
routes,
mapRouteProperties,
void 0,
manifest
);
let routeBranches = flattenAndRankRoutes(dataRoutes);
async function query(request, {
requestContext,
filterMatchesToLoad,
skipLoaderErrorBubbling,
skipRevalidation,
dataStrategy,
generateMiddlewareResponse,
normalizePath
} = {}) {
let normalizePathImpl = normalizePath || defaultNormalizePath;
let method = request.method;
let location = createLocation(
"",
normalizePathImpl(request),
null,
"default"
);
let matches = matchRoutesImpl(
dataRoutes,
location,
basename,
false,
routeBranches
);
requestContext = requestContext != null ? requestContext : new RouterContextProvider();
if (!isValidMethod(method) && method !== "HEAD") {
let error = getInternalRouterError(405, { method });
let { matches: methodNotAllowedMatches, route } = getShortCircuitMatches(dataRoutes);
let staticContext = {
basename,
location,
matches: methodNotAllowedMatches,
loaderData: {},
actionData: null,
errors: {
[route.id]: error
},
statusCode: error.status,
loaderHeaders: {},
actionHeaders: {}
};
return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
} else if (!matches) {
let error = getInternalRouterError(404, { pathname: location.pathname });
let { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes);
let staticContext = {
basename,
location,
matches: notFoundMatches,
loaderData: {},
actionData: null,
errors: {
[route.id]: error
},
statusCode: error.status,
loaderHeaders: {},
actionHeaders: {}
};
return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
}
if (generateMiddlewareResponse) {
invariant(
requestContext instanceof RouterContextProvider,
"When using middleware in `staticHandler.query()`, any provided `requestContext` must be an instance of `RouterContextProvider`"
);
try {
await loadLazyMiddlewareForMatches(
matches,
manifest,
mapRouteProperties
);
let renderedStaticContext;
let response = await runServerMiddlewarePipeline(
{
request,
url: createDataFunctionUrl(request, location),
pattern: getRoutePattern(matches),
matches,
params: matches[0].params,
// If we're calling middleware then it must be enabled so we can cast
// this to the proper type knowing it's not an `AppLoadContext`
context: requestContext
},
async () => {
let res = await generateMiddlewareResponse(
async (revalidationRequest, opts2 = {}) => {
let result2 = await queryImpl(
revalidationRequest,
location,
matches,
requestContext,
dataStrategy || null,
skipLoaderErrorBubbling === true,
null,
"filterMatchesToLoad" in opts2 ? opts2.filterMatchesToLoad ?? null : filterMatchesToLoad ?? null,
skipRevalidation === true
);
if (isResponse(result2)) {
return result2;
}
renderedStaticContext = { location, basename, ...result2 };
return renderedStaticContext;
}
);
return res;
},
async (error, routeId) => {
if (isRedirectResponse(error)) {
return error;
}
if (isResponse(error)) {
try {
error = new ErrorResponseImpl(
error.status,
error.statusText,
await parseResponseBody(error)
);
} catch (e) {
error = e;
}
}
if (isDataWithResponseInit(error)) {
error = dataWithResponseInitToErrorResponse(error);
}
if (renderedStaticContext) {
if (routeId in renderedStaticContext.loaderData) {
renderedStaticContext.loaderData[routeId] = void 0;
}
let staticContext = getStaticContextFromError(
dataRoutes,
renderedStaticContext,
error,
skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, routeId).route.id
);
return generateMiddlewareResponse(
() => Promise.resolve(staticContext)
);
} else {
let boundaryRouteId = skipLoaderErrorBubbling ? routeId : findNearestBoundary(
matches,
matches.find(
(m) => m.route.id === routeId || m.route.loader
)?.route.id || routeId
).route.id;
let staticContext = {
matches,
location,
basename,
loaderData: {},
actionData: null,
errors: {
[boundaryRouteId]: error
},
statusCode: isRouteErrorResponse(error) ? error.status : 500,
actionHeaders: {},
loaderHeaders: {}
};
return generateMiddlewareResponse(
() => Promise.resolve(staticContext)
);
}
}
);
invariant(isResponse(response), "Expected a response in query()");
return response;
} catch (e) {
if (isResponse(e)) {
return e;
}
throw e;
}
}
let result = await queryImpl(
request,
location,
matches,
requestContext,
dataStrategy || null,
skipLoaderErrorBubbling === true,
null,
filterMatchesToLoad || null,
skipRevalidation === true
);
if (isResponse(result)) {
return result;
}
return { location, basename, ...result };
}
async function queryRoute(request, {
routeId,
requestContext,
dataStrategy,
generateMiddlewareResponse,
normalizePath
} = {}) {
let normalizePathImpl = normalizePath || defaultNormalizePath;
let method = request.method;
let location = createLocation(
"",
normalizePathImpl(request),
null,
"default"
);
let matches = matchRoutesImpl(
dataRoutes,
location,
basename,
false,
routeBranches
);
requestContext = requestContext != null ? requestContext : new RouterContextProvider();
if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") {
throw getInternalRouterError(405, { method });
} else if (!matches) {
throw getInternalRouterError(404, { pathname: location.pathname });
}
let match = routeId ? matches.find((m) => m.route.id === routeId) : getTargetMatch(matches, location);
if (routeId && !match) {
throw getInternalRouterError(403, {
pathname: location.pathname,
routeId
});
} else if (!match) {
throw getInternalRouterError(404, { pathname: location.pathname });
}
if (generateMiddlewareResponse) {
invariant(
requestContext instanceof RouterContextProvider,
"When using middleware in `staticHandler.queryRoute()`, any provided `requestContext` must be an instance of `RouterContextProvider`"
);
await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
let response = await runServerMiddlewarePipeline(
{
request,
url: createDataFunctionUrl(request, location),
pattern: getRoutePattern(matches),
matches,
params: matches[0].params,
// If we're calling middleware then it must be enabled so we can cast
// this to the proper type knowing it's not an `AppLoadContext`
context: requestContext
},
async () => {
let res = await generateMiddlewareResponse(
async (innerRequest) => {
let result2 = await queryImpl(
innerRequest,
location,
matches,
requestContext,
dataStrategy || null,
false,
match,
null,
false
);
let processed = handleQueryResult(result2);
return isResponse(processed) ? processed : typeof processed === "string" ? new Response(processed) : Response.json(processed);
}
);
return res;
},
(error) => {
if (isDataWithResponseInit(error)) {
return Promise.resolve(dataWithResponseInitToResponse(error));
}
if (isResponse(error)) {
return Promise.resolve(error);
}
throw error;
}
);
return response;
}
let result = await queryImpl(
request,
location,
matches,
requestContext,
dataStrategy || null,
false,
match,
null,
false
);
return handleQueryResult(result);
function handleQueryResult(result2) {
if (isResponse(result2)) {
return result2;
}
let error = result2.errors ? Object.values(result2.errors)[0] : void 0;
if (error !== void 0) {
throw error;
}
if (result2.actionData) {
return Object.values(result2.actionData)[0];
}
if (result2.loaderData) {
return Object.values(result2.loaderData)[0];
}
return void 0;
}
}
async function queryImpl(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, skipRevalidation) {
invariant(
request.signal,
"query()/queryRoute() requests must contain an AbortController signal"
);
try {
if (isMutationMethod(request.method)) {
let result2 = await submit(
request,
location,
matches,
routeMatch || getTargetMatch(matches, location),
requestContext,
dataStrategy,
skipLoaderErrorBubbling,
routeMatch != null,
filterMatchesToLoad,
skipRevalidation
);
return result2;
}
let result = await loadRouteData(
request,
location,
matches,
requestContext,
dataStrategy,
skipLoaderErrorBubbling,
routeMatch,
filterMatchesToLoad
);
return isResponse(result) ? result : {
...result,
actionData: null,
actionHeaders: {}
};
} catch (e) {
if (isDataStrategyResult(e) && isResponse(e.result)) {
if (e.type === "error" /* error */) {
throw e.result;
}
return e.result;
}
if (isRedirectResponse(e)) {
return e;
}
throw e;
}
}
async function submit(request, location, matches, actionMatch, requestContext, dataStrategy, skipLoaderErrorBubbling, isRouteRequest, filterMatchesToLoad, skipRevalidation) {
let result;
if (!actionMatch.route.action && !actionMatch.route.lazy) {
let error = getInternalRouterError(405, {
method: request.method,
pathname: new URL(request.url).pathname,
routeId: actionMatch.route.id
});
if (isRouteRequest) {
throw error;
}
result = {
type: "error" /* error */,
error
};
} else {
let dsMatches = getTargetedDataStrategyMatches(
mapRouteProperties,
manifest,
request,
location,
matches,
actionMatch,
[],
requestContext
);
let results = await callDataStrategy(
request,
location,
dsMatches,
isRouteRequest,
requestContext,
dataStrategy
);
result = results[actionMatch.route.id];
if (request.signal.aborted) {
throwStaticHandlerAbortedError(request, isRouteRequest);
}
}
if (isRedirectResult(result)) {
throw new Response(null, {
status: result.response.status,
headers: {
Location: result.response.headers.get("Location")
}
});
}
if (isRouteRequest) {
if (isErrorResult(result)) {
throw result.error;
}
return {
matches: [actionMatch],
loaderData: {},
actionData: { [actionMatch.route.id]: result.data },
errors: null,
// Note: statusCode + headers are unused here since queryRoute will
// return the raw Response or value
statusCode: 200,
loaderHeaders: {},
actionHeaders: {}
};
}
if (skipRevalidation) {
if (isErrorResult(result)) {
let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
return {
statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
actionData: null,
actionHeaders: {
...result.headers ? { [actionMatch.route.id]: result.headers } : {}
},
matches,
loaderData: {},
errors: {
[boundaryMatch.route.id]: result.error
},
loaderHeaders: {}
};
} else {
return {
actionData: {
[actionMatch.route.id]: result.data
},
actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {},
matches,
loaderData: {},
errors: null,
statusCode: result.statusCode || 200,
loaderHeaders: {}
};
}
}
let loaderRequest = new Request(request.url, {
headers: request.headers,
redirect: request.redirect,
signal: request.signal
});
if (isErrorResult(result)) {
let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
let handlerContext2 = await loadRouteData(
loaderRequest,
location,
matches,
requestContext,
dataStrategy,
skipLoaderErrorBubbling,
null,
filterMatchesToLoad,
[boundaryMatch.route.id, result]
);
return {
...handlerContext2,
statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
actionData: null,
actionHeaders: {
...result.headers ? { [actionMatch.route.id]: result.headers } : {}
}
};
}
let handlerContext = await loadRouteData(
loaderRequest,
location,
matches,
requestContext,
dataStrategy,
skipLoaderErrorBubbling,
null,
filterMatchesToLoad
);
return {
...handlerContext,
actionData: {
[actionMatch.route.id]: result.data
},
// action status codes take precedence over loader status codes
...result.statusCode ? { statusCode: result.statusCode } : {},
actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {}
};
}
async function loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, pendingActionResult) {
let isRouteRequest = routeMatch != null;
if (isRouteRequest && !routeMatch?.route.loader && !routeMatch?.route.lazy) {
throw getInternalRouterError(400, {
method: request.method,
pathname: new URL(request.url).pathname,
routeId: routeMatch?.route.id
});
}
let dsMatches;
if (routeMatch) {
dsMatches = getTargetedDataStrategyMatches(
mapRouteProperties,
manifest,
request,
location,
matches,
routeMatch,
[],
requestContext
);
} else {
let maxIdx = pendingActionResult && isErrorResult(pendingActionResult[1]) ? (
// Up to but not including the boundary
matches.findIndex((m) => m.route.id === pendingActionResult[0]) - 1
) : void 0;
let pattern = getRoutePattern(matches);
dsMatches = matches.map((match, index) => {
if (maxIdx != null && index > maxIdx) {
return getDataStrategyMatch(
mapRouteProperties,
manifest,
request,
location,
pattern,
match,
[],
requestContext,
false
);
}
return getDataStrategyMatch(
mapRouteProperties,
manifest,
request,
location,
pattern,
match,
[],
requestContext,
(match.route.loader || match.route.lazy) != null && (!filterMatchesToLoad || filterMatchesToLoad(match))
);
});
}
if (!dataStrategy && !dsMatches.some((m) => m.shouldLoad)) {
return {
matches,
loaderData: {},
errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? {
[pendingActionResult[0]]: pendingActionResult[1].error
} : null,
statusCode: 200,
loaderHeaders: {}
};
}
let results = await callDataStrategy(
request,
location,
dsMatches,
isRouteRequest,
requestContext,
dataStrategy
);
if (request.signal.aborted) {
throwStaticHandlerAbortedError(request, isRouteRequest);
}
let handlerContext = processRouteLoaderData(
matches,
results,
pendingActionResult,
true,
skipLoaderErrorBubbling
);
return {
...handlerContext,
matches
};
}
async function callDataStrategy(request, location, matches, isRouteRequest, requestContext, dataStrategy) {
let results = await callDataStrategyImpl(
dataStrategy || defaultDataStrategy,
request,
location,
matches,
null,
requestContext);
let dataResults = {};
await Promise.all(
matches.map(async (match) => {
if (!(match.route.id in results)) {
return;
}
let result = results[match.route.id];
if (isRedirectDataStrategyResult(result)) {
let response = result.result;
throw normalizeRelativeRoutingRedirectResponse(
response,
request,
match.route.id,
matches,
basename
);
}
if (isRouteRequest) {
if (isResponse(result.result)) {
throw result;
} else if (isDataWithResponseInit(result.result)) {
throw dataWithResponseInitToResponse(result.result);
}
}
dataResults[match.route.id] = await convertDataStrategyResultToDataResult(result);
})
);
return dataResults;
}
return {
dataRoutes,
_internalRouteBranches: routeBranches,
query,
queryRoute
};
}
function getStaticContextFromError(routes, handlerContext, error, boundaryId) {
let errorBoundaryId = boundaryId || handlerContext._deepestRenderedBoundaryId || routes[0].id;
return {
...handlerContext,
statusCode: isRouteErrorResponse(error) ? error.status : 500,
errors: {
[errorBoundaryId]: error
}
};
}
function throwStaticHandlerAbortedError(request, isRouteRequest) {
if (request.signal.reason !== void 0) {
throw request.signal.reason;
}
let method = isRouteRequest ? "queryRoute" : "query";
throw new Error(
`${method}() call aborted without an \`AbortSignal.reason\`: ${request.method} ${request.url}`
);
}
function defaultNormalizePath(request) {
let url = new URL(request.url);
return {
pathname: url.pathname,
search: url.search,
hash: url.hash
};
}
function normalizeTo(location, matches, basename, to, fromRouteId, relative) {
let contextualMatches;
let activeRouteMatch;
{
contextualMatches = matches;
activeRouteMatch = matches[matches.length - 1];
}
let path = resolveTo(
to ? to : ".",
getResolveToMatches(contextualMatches),
stripBasename(location.pathname, basename) || location.pathname,
relative === "path"
);
if (to == null) {
path.search = location.search;
path.hash = location.hash;
}
if ((to == null || to === "" || to === ".") && activeRouteMatch) {
let nakedIndex = hasNakedIndexQuery(path.search);
if (activeRouteMatch.route.index && !nakedIndex) {
path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
} else if (!activeRouteMatch.route.index && nakedIndex) {
let params = new URLSearchParams(path.search);
let indexValues = params.getAll("index");
params.delete("index");
indexValues.filter((v) => v).forEach((v) => params.append("index", v));
let qs = params.toString();
path.search = qs ? `?${qs}` : "";
}
}
if (basename !== "/") {
path.pathname = prependBasename({ basename, pathname: path.pathname });
}
return createPath(path);
}
function shouldRevalidateLoader(loaderMatch, arg) {
if (loaderMatch.route.shouldRevalidate) {
let routeChoice = loaderMatch.route.shouldRevalidate(arg);
if (typeof routeChoice === "boolean") {
return routeChoice;
}
}
return arg.defaultShouldRevalidate;
}
var lazyRoutePropertyCache = /* @__PURE__ */ new WeakMap();
var loadLazyRouteProperty = ({
key,
route,
manifest,
mapRouteProperties
}) => {
let routeToUpdate = manifest[route.id];
invariant(routeToUpdate, "No route found in manifest");
if (!routeToUpdate.lazy || typeof routeToUpdate.lazy !== "object") {
return;
}
let lazyFn = routeToUpdate.lazy[key];
if (!lazyFn) {
return;
}
let cache2 = lazyRoutePropertyCache.get(routeToUpdate);
if (!cache2) {
cache2 = {};
lazyRoutePropertyCache.set(routeToUpdate, cache2);
}
let cachedPromise = cache2[key];
if (cachedPromise) {
return cachedPromise;
}
let propertyPromise = (async () => {
let isUnsupported = isUnsupportedLazyRouteObjectKey(key);
let staticRouteValue = routeToUpdate[key];
let isStaticallyDefined = staticRouteValue !== void 0 && key !== "hasErrorBoundary";
if (isUnsupported) {
warning(
!isUnsupported,
"Route property " + key + " is not a supported lazy route property. This property will be ignored."
);
cache2[key] = Promise.resolve();
} else if (isStaticallyDefined) {
warning(
false,
`Route "${routeToUpdate.id}" has a static property "${key}" defined. The lazy property will be ignored.`
);
} else {
let value = await lazyFn();
if (value != null) {
Object.assign(routeToUpdate, { [key]: value });
Object.assign(routeToUpdate, mapRouteProperties(routeToUpdate));
}
}
if (typeof routeToUpdate.lazy === "object") {
routeToUpdate.lazy[key] = void 0;
if (Object.values(routeToUpdate.lazy).every((value) => value === void 0)) {
routeToUpdate.lazy = void 0;
}
}
})();
cache2[key] = propertyPromise;
return propertyPromise;
};
var lazyRouteFunctionCache = /* @__PURE__ */ new WeakMap();
function loadLazyRoute(route, type, manifest, mapRouteProperties, lazyRoutePropertiesToSkip) {
let routeToUpdate = manifest[route.id];
invariant(routeToUpdate, "No route found in manifest");
if (!route.lazy) {
return {
lazyRoutePromise: void 0,
lazyHandlerPromise: void 0
};
}
if (typeof route.lazy === "function") {
let cachedPromise = lazyRouteFunctionCache.get(routeToUpdate);
if (cachedPromise) {
return {
lazyRoutePromise: cachedPromise,
lazyHandlerPromise: cachedPromise
};
}
let lazyRoutePromise2 = (async () => {
invariant(
typeof route.lazy === "function",
"No lazy route function found"
);
let lazyRoute = await route.lazy();
let routeUpdates = {};
for (let lazyRouteProperty in lazyRoute) {
let lazyValue = lazyRoute[lazyRouteProperty];
if (lazyValue === void 0) {
continue;
}
let isUnsupported = isUnsupportedLazyRouteFunctionKey(lazyRouteProperty);
let staticRouteValue = routeToUpdate[lazyRouteProperty];
let isStaticallyDefined = staticRouteValue !== void 0 && // This property isn't static since it should always be updated based
// on the route updates
lazyRouteProperty !== "hasErrorBoundary";
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."
);
} 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.`
);
} else {
routeUpdates[lazyRouteProperty] = lazyValue;
}
}
Object.assign(routeToUpdate, routeUpdates);
Object.assign(routeToUpdate, {
// To keep things framework agnostic, we use the provided `mapRouteProperties`
// function to set the framework-aware properties (`element`/`hasErrorBoundary`)
// since the logic will differ between frameworks.
...mapRouteProperties(routeToUpdate),
lazy: void 0
});
})();
lazyRouteFunctionCache.set(routeToUpdate, lazyRoutePromise2);
lazyRoutePromise2.catch(() => {
});
return {
lazyRoutePromise: lazyRoutePromise2,
lazyHandlerPromise: lazyRoutePromise2
};
}
let lazyKeys = Object.keys(route.lazy);
let lazyPropertyPromises = [];
let lazyHandlerPromise = void 0;
for (let key of lazyKeys) {
if (lazyRoutePropertiesToSkip && lazyRoutePropertiesToSkip.includes(key)) {
continue;
}
let promise = loadLazyRouteProperty({
key,
route,
manifest,
mapRouteProperties
});
if (promise) {
lazyPropertyPromises.push(promise);
if (key === type) {
lazyHandlerPromise = promise;
}
}
}
let lazyRoutePromise = lazyPropertyPromises.length > 0 ? Promise.all(lazyPropertyPromises).then(() => {
}) : void 0;
lazyRoutePromise?.catch(() => {
});
lazyHandlerPromise?.catch(() => {
});
return {
lazyRoutePromise,
lazyHandlerPromise
};
}
function isNonNullable(value) {
return value !== void 0;
}
function loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties) {
let promises = matches.map(({ route }) => {
if (typeof route.lazy !== "object" || !route.lazy.middleware) {
return void 0;
}
return loadLazyRouteProperty({
key: "middleware",
route,
manifest,
mapRouteProperties
});
}).filter(isNonNullable);
return promises.length > 0 ? Promise.all(promises) : void 0;
}
async function defaultDataStrategy(args) {
let matchesToLoad = args.matches.filter((m) => m.shouldLoad);
let keyedResults = {};
let results = await Promise.all(matchesToLoad.map((m) => m.resolve()));
results.forEach((result, i) => {
keyedResults[matchesToLoad[i].route.id] = result;
});
return keyedResults;
}
function runServerMiddlewarePipeline(args, handler, errorHandler) {
return runMiddlewarePipeline(
args,
handler,
processResult,
isResponse,
errorHandler
);
function processResult(result) {
return isDataWithResponseInit(result) ? dataWithResponseInitToResponse(result) : result;
}
}
async function runMiddlewarePipeline(args, handler, processResult, isResult, errorHandler) {
let { matches, ...dataFnArgs } = args;
let tuples = matches.flatMap(
(m) => m.route.middleware ? m.route.middleware.map((fn) => [m.route.id, fn]) : []
);
let result = await callRouteMiddleware(
dataFnArgs,
tuples,
handler,
processResult,
isResult,
errorHandler
);
return result;
}
async function callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx = 0) {
let { request } = args;
if (request.signal.aborted) {
throw request.signal.reason ?? new Error(`Request aborted: ${request.method} ${request.url}`);
}
let tuple = middlewares[idx];
if (!tuple) {
let result = await handler();
return result;
}
let [routeId, middleware] = tuple;
let nextResult;
let next = async () => {
if (nextResult) {
throw new Error("You may only call `next()` once per middleware");
}
try {
let result = await callRouteMiddleware(
args,
middlewares,
handler,
processResult,
isResult,
errorHandler,
idx + 1
);
nextResult = { value: result };
return nextResult.value;
} catch (error) {
nextResult = { value: await errorHandler(error, routeId, nextResult) };
return nextResult.value;
}
};
try {
let value = await middleware(args, next);
let result = value != null ? processResult(value) : void 0;
if (isResult(result)) {
return result;
} else if (nextResult) {
return result ?? nextResult.value;
} else {
nextResult = { value: await next() };
return nextResult.value;
}
} catch (error) {
let response = await errorHandler(error, routeId, nextResult);
return response;
}
}
function getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip) {
let lazyMiddlewarePromise = loadLazyRouteProperty({
key: "middleware",
route: match.route,
manifest,
mapRouteProperties
});
let lazyRoutePromises = loadLazyRoute(
match.route,
isMutationMethod(request.method) ? "action" : "loader",
manifest,
mapRouteProperties,
lazyRoutePropertiesToSkip
);
return {
middleware: lazyMiddlewarePromise,
route: lazyRoutePromises.lazyRoutePromise,
handler: lazyRoutePromises.lazyHandlerPromise
};
}
function getDataStrategyMatch(mapRouteProperties, manifest, request, path, pattern, match, lazyRoutePropertiesToSkip, scopedContext, shouldLoad, shouldRevalidateArgs = null, callSiteDefaultShouldRevalidate) {
let isUsingNewApi = false;
let _lazyPromises = getDataStrategyMatchLazyPromises(
mapRouteProperties,
manifest,
request,
match,
lazyRoutePropertiesToSkip
);
return {
...match,
_lazyPromises,
shouldLoad,
shouldRevalidateArgs,
shouldCallHandler(defaultShouldRevalidate) {
isUsingNewApi = true;
if (!shouldRevalidateArgs) {
return shouldLoad;
}
if (typeof defaultShouldRevalidate === "boolean") {
return shouldRevalidateLoader(match, {
...shouldRevalidateArgs,
defaultShouldRevalidate
});
}
return shouldRevalidateLoader(match, shouldRevalidateArgs);
},
resolve(handlerOverride) {
let { lazy, loader, middleware } = match.route;
let callHandler = isUsingNewApi || shouldLoad || handlerOverride && !isMutationMethod(request.method) && (lazy || loader);
let isMiddlewareOnlyRoute = middleware && middleware.length > 0 && !loader && !lazy;
if (callHandler && (isMutationMethod(request.method) || !isMiddlewareOnlyRoute)) {
return callLoaderOrAction({
request,
path,
pattern,
match,
lazyHandlerPromise: _lazyPromises?.handler,
lazyRoutePromise: _lazyPromises?.route,
handlerOverride,
scopedContext
});
}
return Promise.resolve({ type: "data" /* data */, result: void 0 });
}
};
}
function getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, path, matches, targetMatch, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateArgs = null) {
return matches.map((match) => {
if (match.route.id !== targetMatch.route.id) {
return {
...match,
shouldLoad: false,
shouldRevalidateArgs,
shouldCallHandler: () => false,
_lazyPromises: getDataStrategyMatchLazyPromises(
mapRouteProperties,
manifest,
request,
match,
lazyRoutePropertiesToSkip
),
resolve: () => Promise.resolve({ type: "data", result: void 0 })
};
}
return getDataStrategyMatch(
mapRouteProperties,
manifest,
request,
path,
getRoutePattern(matches),
match,
lazyRoutePropertiesToSkip,
scopedContext,
true,
shouldRevalidateArgs
);
});
}
async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, isStaticHandler) {
if (matches.some((m) => m._lazyPromises?.middleware)) {
await Promise.all(matches.map((m) => m._lazyPromises?.middleware));
}
let dataStrategyArgs = {
request,
url: createDataFunctionUrl(request, path),
pattern: getRoutePattern(matches),
params: matches[0].params,
context: scopedContext,
matches
};
let runClientMiddleware = () => {
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`"
);
} ;
let results = await dataStrategyImpl({
...dataStrategyArgs,
fetcherKey,
runClientMiddleware
});
try {
await Promise.all(
matches.flatMap((m) => [
m._lazyPromises?.handler,
m._lazyPromises?.route
])
);
} catch (e) {
}
return results;
}
async function callLoaderOrAction({
request,
path,
pattern,
match,
lazyHandlerPromise,
lazyRoutePromise,
handlerOverride,
scopedContext
}) {
let result;
let onReject;
let isAction = isMutationMethod(request.method);
let type = isAction ? "action" : "loader";
let runHandler = (handler) => {
let reject;
let abortPromise = new Promise((_, r) => reject = r);
onReject = () => reject();
request.signal.addEventListener("abort", onReject);
let actualHandler = (ctx) => {
if (typeof handler !== "function") {
return Promise.reject(
new Error(
`You cannot call the handler for a route which defines a boolean "${type}" [routeId: ${match.route.id}]`
)
);
}
return handler(
{
request,
url: createDataFunctionUrl(request, path),
pattern,
params: match.params,
context: scopedContext
},
...ctx !== void 0 ? [ctx] : []
);
};
let handlerPromise = (async () => {
try {
let val = await (handlerOverride ? handlerOverride((ctx) => actualHandler(ctx)) : actualHandler());
return { type: "data", result: val };
} catch (e) {
return { type: "error", result: e };
}
})();
return Promise.race([handlerPromise, abortPromise]);
};
try {
let handler = isAction ? match.route.action : match.route.loader;
if (lazyHandlerPromise || lazyRoutePromise) {
if (handler) {
let handlerError;
let [value] = await Promise.all([
// If the handler throws, don't let it immediately bubble out,
// since we need to let the lazy() execution finish so we know if this
// route has a boundary that can handle the error
runHandler(handler).catch((e) => {
handlerError = e;
}),
// Ensure all lazy route promises are resolved before continuing
lazyHandlerPromise,
lazyRoutePromise
]);
if (handlerError !== void 0) {
throw handlerError;
}
result = value;
} else {
await lazyHandlerPromise;
let handler2 = isAction ? match.route.action : match.route.loader;
if (handler2) {
[result] = await Promise.all([runHandler(handler2), lazyRoutePromise]);
} else if (type === "action") {
let url = new URL(request.url);
let pathname = url.pathname + url.search;
throw getInternalRouterError(405, {
method: request.method,
pathname,
routeId: match.route.id
});
} else {
return { type: "data" /* data */, result: void 0 };
}
}
} else if (!handler) {
let url = new URL(request.url);
let pathname = url.pathname + url.search;
throw getInternalRouterError(404, {
pathname
});
} else {
result = await runHandler(handler);
}
} catch (e) {
return { type: "error" /* error */, result: e };
} finally {
if (onReject) {
request.signal.removeEventListener("abort", onReject);
}
}
return result;
}
async function parseResponseBody(response) {
let contentType = response.headers.get("Content-Type");
if (contentType && /\bapplication\/json\b/.test(contentType)) {
return response.body == null ? null : response.json();
}
return response.text();
}
async function convertDataStrategyResultToDataResult(dataStrategyResult) {
let { result, type } = dataStrategyResult;
if (isResponse(result)) {
let data2;
try {
data2 = await parseResponseBody(result);
} catch (e) {
return { type: "error" /* error */, error: e };
}
if (type === "error" /* error */) {
return {
type: "error" /* error */,
error: new ErrorResponseImpl(result.status, result.statusText, data2),
statusCode: result.status,
headers: result.headers
};
}
return {
type: "data" /* data */,
data: data2,
statusCode: result.status,
headers: result.headers
};
}
if (type === "error" /* error */) {
if (isDataWithResponseInit(result)) {
if (result.data instanceof Error) {
return {
type: "error" /* error */,
error: result.data,
statusCode: result.init?.status,
headers: result.init?.headers ? new Headers(result.init.headers) : void 0
};
}
return {
type: "error" /* error */,
error: dataWithResponseInitToErrorResponse(result),
statusCode: isRouteErrorResponse(result) ? result.status : void 0,
headers: result.init?.headers ? new Headers(result.init.headers) : void 0
};
}
return {
type: "error" /* error */,
error: result,
statusCode: isRouteErrorResponse(result) ? result.status : void 0
};
}
if (isDataWithResponseInit(result)) {
return {
type: "data" /* data */,
data: result.data,
statusCode: result.init?.status,
headers: result.init?.headers ? new Headers(result.init.headers) : void 0
};
}
return { type: "data" /* data */, data: result };
}
function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename) {
let location = response.headers.get("Location");
invariant(
location,
"Redirects returned/thrown from loaders/actions must have a Location header"
);
if (!isAbsoluteUrl(location)) {
let trimmedMatches = matches.slice(
0,
matches.findIndex((m) => m.route.id === routeId) + 1
);
location = normalizeTo(
new URL(request.url),
trimmedMatches,
basename,
location
);
response.headers.set("Location", location);
}
return response;
}
function createDataFunctionUrl(request, path) {
let url = new URL(request.url);
let parsed = typeof path === "string" ? parsePath(path) : path;
url.pathname = parsed.pathname || "/";
if (parsed.search) {
let searchParams = new URLSearchParams(parsed.search);
let indexValues = searchParams.getAll("index");
searchParams.delete("index");
for (let value of indexValues.filter(Boolean)) {
searchParams.append("index", value);
}
url.search = searchParams.size ? `?${searchParams.toString()}` : "";
} else {
url.search = "";
}
url.hash = parsed.hash || "";
return url;
}
function processRouteLoaderData(matches, results, pendingActionResult, isStaticHandler = false, skipLoaderErrorBubbling = false) {
let loaderData = {};
let errors = null;
let statusCode;
let foundError = false;
let loaderHeaders = {};
let pendingError = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : void 0;
matches.forEach((match) => {
if (!(match.route.id in results)) {
return;
}
let id = match.route.id;
let result = results[id];
invariant(
!isRedirectResult(result),
"Cannot handle redirect results in processLoaderData"
);
if (isErrorResult(result)) {
let error = result.error;
if (pendingError !== void 0) {
error = pendingError;
pendingError = void 0;
}
errors = errors || {};
if (skipLoaderErrorBubbling) {
errors[id] = error;
} else {
let boundaryMatch = findNearestBoundary(matches, id);
if (errors[boundaryMatch.route.id] == null) {
errors[boundaryMatch.route.id] = error;
}
}
if (!isStaticHandler) {
loaderData[id] = ResetLoaderDataSymbol;
}
if (!foundError) {
foundError = true;
statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
}
if (result.headers) {
loaderHeaders[id] = result.headers;
}
} else {
loaderData[id] = result.data;
if (result.statusCode && result.statusCode !== 200 && !foundError) {
statusCode = result.statusCode;
}
if (result.headers) {
loaderHeaders[id] = result.headers;
}
}
});
if (pendingError !== void 0 && pendingActionResult) {
errors = { [pendingActionResult[0]]: pendingError };
if (pendingActionResult[2]) {
loaderData[pendingActionResult[2]] = void 0;
}
}
return {
loaderData,
errors,
statusCode: statusCode || 200,
loaderHeaders
};
}
function findNearestBoundary(matches, routeId) {
let eligibleMatches = routeId ? matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1) : [...matches];
return eligibleMatches.reverse().find((m) => m.route.hasErrorBoundary === true) || matches[0];
}
function getShortCircuitMatches(routes) {
let route = routes.length === 1 ? routes[0] : routes.find((r) => r.index || !r.path || r.path === "/") || {
id: `__shim-error-route__`
};
return {
matches: [
{
params: {},
pathname: "",
pathnameBase: "",
route
}
],
route
};
}
function getInternalRouterError(status, {
pathname,
routeId,
method,
type,
message
} = {}) {
let statusText = "Unknown Server Error";
let errorMessage = "Unknown @remix-run/router error";
if (status === 400) {
statusText = "Bad Request";
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.`;
} else if (type === "invalid-body") {
errorMessage = "Unable to encode submission body";
}
} else if (status === 403) {
statusText = "Forbidden";
errorMessage = `Route "${routeId}" does not match URL "${pathname}"`;
} else if (status === 404) {
statusText = "Not Found";
errorMessage = `No route matches URL "${pathname}"`;
} else if (status === 405) {
statusText = "Method Not Allowed";
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.`;
} else if (method) {
errorMessage = `Invalid request method "${method.toUpperCase()}"`;
}
}
return new ErrorResponseImpl(
status || 500,
statusText,
new Error(errorMessage),
true
);
}
function dataWithResponseInitToResponse(data2) {
return Response.json(data2.data, data2.init ?? void 0);
}
function dataWithResponseInitToErrorResponse(data2) {
return new ErrorResponseImpl(
data2.init?.status ?? 500,
data2.init?.statusText ?? "Internal Server Error",
data2.data
);
}
function isDataStrategyResult(result) {
return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === "data" /* data */ || result.type === "error" /* error */);
}
function isRedirectDataStrategyResult(result) {
return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
}
function isErrorResult(result) {
return result.type === "error" /* error */;
}
function isRedirectResult(result) {
return (result && result.type) === "redirect" /* redirect */;
}
function isDataWithResponseInit(value) {
return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
}
function isResponse(value) {
return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
}
function isRedirectStatusCode(statusCode) {
return redirectStatusCodes.has(statusCode);
}
function isRedirectResponse(result) {
return isResponse(result) && isRedirectStatusCode(result.status) && result.headers.has("Location");
}
function isValidMethod(method) {
return validRequestMethods.has(method.toUpperCase());
}
function isMutationMethod(method) {
return validMutationMethods.has(method.toUpperCase());
}
function hasNakedIndexQuery(search) {
return new URLSearchParams(search).getAll("index").some((v) => v === "");
}
function getTargetMatch(matches, location) {
let search = typeof location === "string" ? parsePath(location).search : location.search;
if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) {
return matches[matches.length - 1];
}
let pathMatches = getPathContributingMatches(matches);
return pathMatches[pathMatches.length - 1];
}
// lib/server-runtime/invariant.ts
function invariant2(value, message) {
if (value === false || value === null || typeof value === "undefined") {
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"
);
throw new Error(message);
}
}
// lib/server-runtime/headers.ts
function getDocumentHeadersImpl(context, getRouteHeadersFn, _defaultHeaders) {
let boundaryIdx = context.errors ? context.matches.findIndex((m) => context.errors[m.route.id]) : -1;
let matches = boundaryIdx >= 0 ? context.matches.slice(0, boundaryIdx + 1) : context.matches;
let errorHeaders;
if (boundaryIdx >= 0) {
let { actionHeaders, actionData, loaderHeaders, loaderData } = context;
context.matches.slice(boundaryIdx).some((match) => {
let id = match.route.id;
if (actionHeaders[id] && (!actionData || !actionData.hasOwnProperty(id))) {
errorHeaders = actionHeaders[id];
} else if (loaderHeaders[id] && !loaderData.hasOwnProperty(id)) {
errorHeaders = loaderHeaders[id];
}
return errorHeaders != null;
});
}
const defaultHeaders = new Headers(_defaultHeaders);
return matches.reduce((parentHeaders, match, idx) => {
let { id } = match.route;
let loaderHeaders = context.loaderHeaders[id] || new Headers();
let actionHeaders = context.actionHeaders[id] || new Headers();
let includeErrorHeaders = errorHeaders != null && idx === matches.length - 1;
let includeErrorCookies = includeErrorHeaders && errorHeaders !== loaderHeaders && errorHeaders !== actionHeaders;
let headersFn = getRouteHeadersFn(match);
if (headersFn == null) {
let headers2 = new Headers(parentHeaders);
if (includeErrorCookies) {
prependCookies(errorHeaders, headers2);
}
prependCookies(actionHeaders, headers2);
prependCookies(loaderHeaders, headers2);
return headers2;
}
let headers = new Headers(
typeof headersFn === "function" ? headersFn({
loaderHeaders,
parentHeaders,
actionHeaders,
errorHeaders: includeErrorHeaders ? errorHeaders : void 0
}) : headersFn
);
if (includeErrorCookies) {
prependCookies(errorHeaders, headers);
}
prependCookies(actionHeaders, headers);
prependCookies(loaderHeaders, headers);
prependCookies(parentHeaders, headers);
return headers;
}, new Headers(defaultHeaders));
}
function prependCookies(parentHeaders, childHeaders) {
let parentSetCookieString = parentHeaders.get("Set-Cookie");
if (parentSetCookieString) {
let cookies = setCookieParser.splitCookiesString(parentSetCookieString);
let childCookies = new Set(childHeaders.getSetCookie());
cookies.forEach((cookie) => {
if (!childCookies.has(cookie)) {
childHeaders.append("Set-Cookie", cookie);
}
});
}
}
var SINGLE_FETCH_REDIRECT_STATUS = 202;
// lib/server-runtime/warnings.ts
var alreadyWarned = {};
function warnOnce(condition, message) {
if (!condition && !alreadyWarned[message]) {
alreadyWarned[message] = true;
console.warn(message);
}
}
// lib/errors.ts
var ERROR_DIGEST_BASE = "REACT_ROUTER_ERROR";
var ERROR_DIGEST_REDIRECT = "REDIRECT";
var ERROR_DIGEST_ROUTE_ERROR_RESPONSE = "ROUTE_ERROR_RESPONSE";
function createRedirectErrorDigest(response) {
return `${ERROR_DIGEST_BASE}:${ERROR_DIGEST_REDIRECT}:${JSON.stringify({
status: response.status,
statusText: response.statusText,
location: response.headers.get("Location"),
reloadDocument: response.headers.get("X-Remix-Reload-Document") === "true",
replace: response.headers.get("X-Remix-Replace") === "true"
})}`;
}
function createRouteErrorResponseDigest(response) {
let status = 500;
let statusText = "";
let data2;
if (isDataWithResponseInit(response)) {
status = response.init?.status ?? status;
statusText = response.init?.statusText ?? statusText;
data2 = response.data;
} else {
status = response.status;
statusText = response.statusText;
data2 = void 0;
}
return `${ERROR_DIGEST_BASE}:${ERROR_DIGEST_ROUTE_ERROR_RESPONSE}:${JSON.stringify(
{
status,
statusText,
data: data2
}
)}`;
}
// lib/dom/ssr/fog-of-war.ts
var URL_LIMIT = 7680;
function getPathsWithAncestors(paths) {
let result = /* @__PURE__ */ new Set();
paths.forEach((path) => {
if (!path.startsWith("/")) {
path = `/${path}`;
}
for (let i = 1; i < path.length; i++) {
if (path[i] === "/") {
result.add(path.slice(0, i));
}
}
result.add(path);
});
return Array.from(result);
}
// lib/actions.ts
function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
let originHeader = request.headers.get("origin");
let originDomain = null;
try {
originDomain = typeof originHeader === "string" && originHeader !== "null" ? new URL(originHeader).host : originHeader;
} catch {
throw new Error(
`\`origin\` header is not a valid URL. Aborting the action.`
);
}
let host = new URL(request.url).host;
if (originDomain && originDomain !== host) {
if (!isAllowedOrigin(originDomain, allowedActionOrigins)) {
throw new Error(
"The `request.url` host does not match `origin` header from a forwarded action request. Aborting the action."
);
}
}
}
function matchWildcardDomain(domain, pattern) {
const domainParts = domain.split(".");
const patternParts = pattern.split(".");
if (patternParts.length < 1) {
return false;
}
if (domainParts.length < patternParts.length) {
return false;
}
while (patternParts.length) {
const patternPart = patternParts.pop();
const domainPart = domainParts.pop();
switch (patternPart) {
case "": {
return false;
}
case "*": {
if (domainPart) {
continue;
} else {
return false;
}
}
case "**": {
if (patternParts.length > 0) {
return false;
}
return domainPart !== void 0;
}
case void 0:
default: {
if (domainPart !== patternPart) {
return false;
}
}
}
}
return domainParts.length === 0;
}
function isAllowedOrigin(originDomain, allowedActionOrigins = []) {
return allowedActionOrigins.some(
(allowedOrigin) => allowedOrigin && (allowedOrigin === originDomain || matchWildcardDomain(originDomain, allowedOrigin))
);
}
// lib/server-runtime/urls.ts
function getNormalizedPath(request, basename, future) {
basename = basename || "/";
let url = new URL(request.url);
let pathname = url.pathname;
{
if (stripBasename(pathname, basename) === "/_root.data") {
pathname = basename;
} else if (pathname.endsWith(".data")) {
pathname = pathname.replace(/\.data$/, "");
}
if (stripBasename(pathname, basename) !== "/" && pathname.endsWith("/")) {
pathname = pathname.slice(0, -1);
}
}
let searchParams = new URLSearchParams(url.search);
searchParams.delete("_routes");
let search = searchParams.toString();
if (search) {
search = `?${search}`;
}
return {
pathname,
search,
// No hashes on the server
hash: ""
};
}
// lib/rsc/server.rsc.ts
var Outlet = reactServerClient.Outlet;
var WithComponentProps = reactServerClient.UNSAFE_WithComponentProps;
var WithErrorBoundaryProps = reactServerClient.UNSAFE_WithErrorBoundaryProps;
var WithHydrateFallbackProps = reactServerClient.UNSAFE_WithHydrateFallbackProps;
var globalVar = typeof globalThis !== "undefined" ? globalThis : global;
var ServerStorage = globalVar.___reactRouterServerStorage___ ?? (globalVar.___reactRouterServerStorage___ = new node_async_hooks.AsyncLocalStorage());
function getRequest() {
const ctx = ServerStorage.getStore();
if (!ctx)
throw new Error(
"getRequest must be called from within a React Server render context"
);
return ctx.request;
}
var redirect2 = (...args) => {
const response = redirect(...args);
const ctx = ServerStorage.getStore();
if (ctx && ctx.runningAction) {
ctx.redirect = response;
}
return response;
};
var redirectDocument2 = (...args) => {
const response = redirectDocument(...args);
const ctx = ServerStorage.getStore();
if (ctx && ctx.runningAction) {
ctx.redirect = response;
}
return response;
};
var replace2 = (...args) => {
const response = replace(...args);
const ctx = ServerStorage.getStore();
if (ctx && ctx.runningAction) {
ctx.redirect = response;
}
return response;
};
var cachedResolvePromise = (
// @ts-expect-error - on 18 types, requires 19.
React3__namespace.cache(async (resolve) => {
return Promise.allSettled([resolve]).then((r) => r[0]);
})
);
var Await = async ({
children,
resolve,
errorElement
}) => {
let promise = cachedResolvePromise(resolve);
let resolved = await promise;
if (resolved.status === "rejected" && !errorElement) {
throw resolved.reason;
}
if (resolved.status === "rejected") {
return React3__namespace.createElement(reactServerClient.UNSAFE_AwaitContextProvider, {
children: React3__namespace.createElement(React3__namespace.Fragment, null, errorElement),
value: { _tracked: true, _error: resolved.reason }
});
}
const toRender = typeof children === "function" ? children(resolved.value) : children;
return React3__namespace.createElement(reactServerClient.UNSAFE_AwaitContextProvider, {
children: toRender,
value: { _tracked: true, _data: resolved.value }
});
};
async function matchRSCServerRequest({
allowedActionOrigins,
createTemporaryReferenceSet,
basename,
decodeReply,
requestContext,
routeDiscovery,
loadServerAction,
decodeAction,
decodeFormState,
onError,
request,
routes,
generateResponse
}) {
let url = new URL(request.url);
basename = basename || "/";
let normalizedPath = url.pathname;
if (url.pathname.endsWith("/_.rsc")) {
normalizedPath = url.pathname.replace(/_\.rsc$/, "");
} else if (url.pathname.endsWith(".rsc")) {
normalizedPath = url.pathname.replace(/\.rsc$/, "");
}
if (stripBasename(normalizedPath, basename) !== "/" && normalizedPath.endsWith("/")) {
normalizedPath = normalizedPath.slice(0, -1);
}
url.pathname = normalizedPath;
basename = basename.length > normalizedPath.length ? normalizedPath : basename;
let routerRequest = new Request(url.toString(), {
method: request.method,
headers: request.headers,
body: request.body,
signal: request.signal,
duplex: request.body ? "half" : void 0
});
const temporaryReferences = createTemporaryReferenceSet();
const requestUrl = new URL(request.url);
if (isManifestRequest(requestUrl)) {
let response2 = await generateManifestResponse(
routes,
basename,
request,
generateResponse,
temporaryReferences,
routeDiscovery
);
return response2;
}
let isDataRequest = isReactServerRequest(requestUrl);
let matches = matchRoutes(routes, url.pathname, basename);
if (matches) {
await Promise.all(matches.map((m) => explodeLazyRoute(m.route)));
}
const leafMatch = matches?.[matches.length - 1];
if (!isDataRequest && leafMatch && !leafMatch.route.Component && !leafMatch.route.ErrorBoundary) {
return generateResourceResponse(
routerRequest,
routes,
basename,
leafMatch.route.id,
requestContext,
onError
);
}
let response = await generateRenderResponse(
routerRequest,
routes,
basename,
isDataRequest,
decodeReply,
requestContext,
loadServerAction,
decodeAction,
decodeFormState,
onError,
generateResponse,
temporaryReferences,
allowedActionOrigins,
routeDiscovery
);
response.headers.set("X-Remix-Response", "yes");
return response;
}
async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery) {
let url = new URL(request.url);
if (url.toString().length > URL_LIMIT) {
return new Response(null, {
statusText: "Bad Request",
status: 400
});
}
if (routeDiscovery?.mode === "initial") {
let payload2 = {
type: "manifest",
patches: getAllRoutePatches(routes)
};
return generateResponse(
{
statusCode: 200,
headers: new Headers({
"Content-Type": "text/x-component",
Vary: "Content-Type"
}),
payload: payload2
},
{ temporaryReferences, onError: defaultOnError }
);
}
let pathParam = url.searchParams.get("paths");
let pathnames = pathParam ? pathParam.split(",").filter(Boolean) : [url.pathname.replace(/\.manifest$/, "")];
let routeIds = /* @__PURE__ */ new Set();
let matchedRoutes = pathnames.flatMap((pathname) => {
let pathnameMatches = matchRoutes(routes, pathname, basename);
return pathnameMatches?.map((m, i) => ({
...m.route,
parentId: pathnameMatches[i - 1]?.route.id
})) ?? [];
}).filter((route) => {
if (!routeIds.has(route.id)) {
routeIds.add(route.id);
return true;
}
return false;
});
let payload = {
type: "manifest",
patches: Promise.all([
...matchedRoutes.map((route) => getManifestRoute(route)),
getAdditionalRoutePatches(
pathnames,
routes,
basename,
Array.from(routeIds)
)
]).then((r) => r.flat(1))
};
return generateResponse(
{
statusCode: 200,
headers: new Headers({
"Content-Type": "text/x-component"
}),
payload
},
{ temporaryReferences, onError: defaultOnError }
);
}
function prependBasenameToRedirectResponse(response, basename = "/") {
if (basename === "/") {
return response;
}
let redirect3 = response.headers.get("Location");
if (!redirect3 || isAbsoluteUrl(redirect3)) {
return response;
}
response.headers.set(
"Location",
prependBasename({ basename, pathname: redirect3 })
);
return response;
}
async function processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences) {
const getRevalidationRequest = () => new Request(request.url, {
method: "GET",
headers: request.headers,
signal: request.signal
});
const isFormRequest = canDecodeWithFormData(
request.headers.get("Content-Type")
);
const actionId = request.headers.get("rsc-action-id");
if (actionId) {
if (!decodeReply || !loadServerAction) {
throw new Error(
"Cannot handle enhanced server action without decodeReply and loadServerAction functions"
);
}
const reply = isFormRequest ? await request.formData() : await request.text();
const actionArgs = await decodeReply(reply, { temporaryReferences });
const action = await loadServerAction(actionId);
const serverAction = action.bind(null, ...actionArgs);
let actionResult = Promise.resolve(serverAction());
try {
await actionResult;
} catch (error) {
if (isResponse(error)) {
return error;
}
onError?.(error);
}
let maybeFormData = actionArgs.length === 1 ? actionArgs[0] : actionArgs[1];
let formData = maybeFormData && typeof maybeFormData === "object" && maybeFormData instanceof FormData ? maybeFormData : null;
let skipRevalidation = formData?.has("$SKIP_REVALIDATION") ?? false;
return {
actionResult,
revalidationRequest: getRevalidationRequest(),
skipRevalidation
};
} else if (isFormRequest) {
const formData = await request.clone().formData();
if (Array.from(formData.keys()).some((k) => k.startsWith("$ACTION_"))) {
if (!decodeAction) {
throw new Error(
"Cannot handle form actions without a decodeAction function"
);
}
const action = await decodeAction(formData);
let formState = void 0;
try {
let result = await action();
if (isRedirectResponse(result)) {
result = prependBasenameToRedirectResponse(result, basename);
}
formState = decodeFormState?.(result, formData);
} catch (error) {
if (isRedirectResponse(error)) {
return prependBasenameToRedirectResponse(error, basename);
}
if (isResponse(error)) {
return error;
}
onError?.(error);
}
return {
formState,
revalidationRequest: getRevalidationRequest(),
skipRevalidation: false
};
}
}
}
async function generateResourceResponse(request, routes, basename, routeId, requestContext, onError) {
try {
const staticHandler = createStaticHandler(routes, {
basename
});
let response = await staticHandler.queryRoute(request, {
routeId,
requestContext,
async generateMiddlewareResponse(queryRoute) {
try {
let response2 = await queryRoute(request);
return generateResourceResponse2(response2);
} catch (error) {
return generateErrorResponse(error);
}
},
normalizePath: (r) => getNormalizedPath(r, basename, null)
});
return response;
} catch (error) {
return generateErrorResponse(error);
}
function generateErrorResponse(error) {
let response;
if (isResponse(error)) {
response = error;
} else if (isRouteErrorResponse(error)) {
onError?.(error);
const errorMessage = typeof error.data === "string" ? error.data : error.statusText;
response = new Response(errorMessage, {
status: error.status,
statusText: error.statusText
});
} else {
onError?.(error);
response = new Response("Internal Server Error", { status: 500 });
}
return generateResourceResponse2(response);
}
function generateResourceResponse2(response) {
const headers = new Headers(response.headers);
headers.set("React-Router-Resource", "true");
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers
});
}
}
async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery) {
let statusCode = 200;
let url = new URL(request.url);
let isSubmission = isMutationMethod(request.method);
let routeIdsToLoad = !isSubmission && url.searchParams.has("_routes") ? url.searchParams.get("_routes").split(",") : null;
const staticHandler = createStaticHandler(routes, {
basename,
mapRouteProperties: (r) => ({
hasErrorBoundary: r.ErrorBoundary != null
})
});
let actionResult;
const ctx = {
request,
runningAction: false
};
const result = await ServerStorage.run(
ctx,
() => staticHandler.query(request, {
requestContext,
skipLoaderErrorBubbling: isDataRequest,
skipRevalidation: isSubmission,
...routeIdsToLoad ? { filterMatchesToLoad: (m) => routeIdsToLoad.includes(m.route.id) } : {},
normalizePath: (r) => getNormalizedPath(r, basename),
async generateMiddlewareResponse(query) {
let formState;
let skipRevalidation = false;
let potentialCSRFAttackError;
if (isMutationMethod(request.method)) {
try {
throwIfPotentialCSRFAttack(request, allowedActionOrigins);
ctx.runningAction = true;
let result2 = await processServerAction(
request,
basename,
decodeReply,
loadServerAction,
decodeAction,
decodeFormState,
onError,
temporaryReferences
).finally(() => {
ctx.runningAction = false;
});
if (isResponse(result2)) {
return generateRedirectResponse(
result2,
actionResult,
basename,
isDataRequest,
generateResponse,
temporaryReferences,
ctx.redirect?.headers
);
}
skipRevalidation = result2?.skipRevalidation ?? false;
actionResult = result2?.actionResult;
formState = result2?.formState;
request = result2?.revalidationRequest ?? request;
if (ctx.redirect) {
return generateRedirectResponse(
ctx.redirect,
actionResult,
basename,
isDataRequest,
generateResponse,
temporaryReferences,
void 0
);
}
} catch (error) {
potentialCSRFAttackError = error;
}
}
let staticContext = await query(
request,
skipRevalidation || !!potentialCSRFAttackError ? {
filterMatchesToLoad: () => false
} : void 0
);
if (isResponse(staticContext)) {
return generateRedirectResponse(
staticContext,
actionResult,
basename,
isDataRequest,
generateResponse,
temporaryReferences,
ctx.redirect?.headers
);
}
if (potentialCSRFAttackError) {
staticContext.errors ?? (staticContext.errors = {});
staticContext.errors[staticContext.matches[0].route.id] = potentialCSRFAttackError;
staticContext.statusCode = 400;
}
return generateStaticContextResponse(
routes,
basename,
generateResponse,
statusCode,
routeIdsToLoad,
isDataRequest,
isSubmission,
actionResult,
formState,
staticContext,
temporaryReferences,
skipRevalidation,
ctx.redirect?.headers,
routeDiscovery
);
}
})
);
if (isRedirectResponse(result)) {
return generateRedirectResponse(
result,
actionResult,
basename,
isDataRequest,
generateResponse,
temporaryReferences,
ctx.redirect?.headers
);
}
invariant2(isResponse(result), "Expected a response from query");
return result;
}
function generateRedirectResponse(response, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, sideEffectRedirectHeaders) {
let redirect3 = response.headers.get("Location");
if (isDataRequest && basename) {
redirect3 = stripBasename(redirect3, basename) || redirect3;
}
let payload = {
type: "redirect",
location: redirect3,
reload: response.headers.get("X-Remix-Reload-Document") === "true",
replace: response.headers.get("X-Remix-Replace") === "true",
status: response.status,
actionResult
};
let headers = new Headers(sideEffectRedirectHeaders);
for (const [key, value] of response.headers.entries()) {
headers.append(key, value);
}
headers.delete("Location");
headers.delete("X-Remix-Reload-Document");
headers.delete("X-Remix-Replace");
headers.delete("Content-Length");
headers.set("Content-Type", "text/x-component");
return generateResponse(
{
statusCode: SINGLE_FETCH_REDIRECT_STATUS,
headers,
payload
},
{ temporaryReferences, onError: defaultOnError }
);
}
async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery) {
statusCode = staticContext.statusCode ?? statusCode;
if (staticContext.errors) {
staticContext.errors = Object.fromEntries(
Object.entries(staticContext.errors).map(([key, error]) => [
key,
isRouteErrorResponse(error) ? Object.fromEntries(Object.entries(error)) : error
])
);
}
staticContext.matches.forEach((m) => {
const routeHasNoLoaderData = staticContext.loaderData[m.route.id] === void 0;
const routeHasError = Boolean(
staticContext.errors && m.route.id in staticContext.errors
);
if (routeHasNoLoaderData && !routeHasError) {
staticContext.loaderData[m.route.id] = null;
}
});
let headers = getDocumentHeadersImpl(
staticContext,
(match) => match.route.headers,
sideEffectRedirectHeaders
);
headers.delete("Content-Length");
const baseRenderPayload = {
type: "render",
basename: staticContext.basename,
routeDiscovery: routeDiscovery ?? { mode: "lazy" },
actionData: staticContext.actionData,
errors: staticContext.errors,
loaderData: staticContext.loaderData,
location: staticContext.location,
formState
};
const renderPayloadPromise = () => getRenderPayload(
baseRenderPayload,
routes,
basename,
routeIdsToLoad,
isDataRequest,
staticContext,
routeDiscovery
);
let payload;
if (actionResult) {
payload = {
type: "action",
actionResult,
rerender: skipRevalidation ? void 0 : renderPayloadPromise()
};
} else if (isSubmission && isDataRequest) {
payload = {
...baseRenderPayload,
matches: [],
patches: Promise.resolve([])
};
} else {
payload = await renderPayloadPromise();
}
return generateResponse(
{
statusCode,
headers,
payload
},
{ temporaryReferences, onError: defaultOnError }
);
}
async function getRenderPayload(baseRenderPayload, routes, basename, routeIdsToLoad, isDataRequest, staticContext, routeDiscovery) {
let deepestRenderedRouteIdx = staticContext.matches.length - 1;
let parentIds = {};
staticContext.matches.forEach((m, i) => {
if (i > 0) {
parentIds[m.route.id] = staticContext.matches[i - 1].route.id;
}
if (staticContext.errors && m.route.id in staticContext.errors && deepestRenderedRouteIdx > i) {
deepestRenderedRouteIdx = i;
}
});
let matchesPromise = Promise.all(
staticContext.matches.map((match, i) => {
let isBelowErrorBoundary = i > deepestRenderedRouteIdx;
let parentId = parentIds[match.route.id];
return getRSCRouteMatch({
staticContext,
match,
routeIdsToLoad,
isBelowErrorBoundary,
parentId
});
})
);
let patches = routeDiscovery?.mode === "initial" && !isDataRequest ? getAllRoutePatches(routes).then(
(patches2) => patches2.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)
);
return {
...baseRenderPayload,
matches: await matchesPromise,
patches
};
}
async function getRSCRouteMatch({
staticContext,
match,
isBelowErrorBoundary,
routeIdsToLoad,
parentId
}) {
const route = match.route;
await explodeLazyRoute(route);
const Layout = route.Layout || React3__namespace.Fragment;
const Component = route.Component;
const ErrorBoundary = route.ErrorBoundary;
const HydrateFallback = route.HydrateFallback;
const loaderData = staticContext.loaderData[route.id];
const actionData = staticContext.actionData?.[route.id];
const params = match.params;
let element = void 0;
let shouldLoadRoute = !routeIdsToLoad || routeIdsToLoad.includes(route.id);
if (Component && shouldLoadRoute) {
element = !isBelowErrorBoundary ? React3__namespace.createElement(
Layout,
null,
isClientReference(Component) ? React3__namespace.createElement(WithComponentProps, {
children: React3__namespace.createElement(Component)
}) : React3__namespace.createElement(Component, {
loaderData,
actionData,
params,
matches: staticContext.matches.map(
(match2) => convertRouteMatchToUiMatch(match2, staticContext.loaderData)
)
})
) : React3__namespace.createElement(Outlet);
}
let error = void 0;
if (ErrorBoundary && staticContext.errors) {
error = staticContext.errors[route.id];
}
const errorElement = ErrorBoundary ? React3__namespace.createElement(
Layout,
null,
isClientReference(ErrorBoundary) ? React3__namespace.createElement(WithErrorBoundaryProps, {
children: React3__namespace.createElement(ErrorBoundary)
}) : React3__namespace.createElement(ErrorBoundary, {
loaderData,
actionData,
params,
error
})
) : void 0;
const hydrateFallbackElement = HydrateFallback ? React3__namespace.createElement(
Layout,
null,
isClientReference(HydrateFallback) ? React3__namespace.createElement(WithHydrateFallbackProps, {
children: React3__namespace.createElement(HydrateFallback)
}) : React3__namespace.createElement(HydrateFallback, {
loaderData,
actionData,
params
})
) : void 0;
const hmrRoute = route;
return {
clientAction: route.clientAction,
clientLoader: route.clientLoader,
element,
errorElement,
handle: route.handle,
hasAction: !!route.action,
hasComponent: !!Component,
hasErrorBoundary: !!ErrorBoundary,
hasLoader: !!route.loader,
hydrateFallbackElement,
id: route.id,
index: "index" in route ? route.index : void 0,
links: route.links,
meta: route.meta,
params,
parentId,
path: route.path,
pathname: match.pathname,
pathnameBase: match.pathnameBase,
shouldRevalidate: route.shouldRevalidate,
// Add an unused client-only export (if present) so HMR can support
// switching between server-first and client-only routes during development
...hmrRoute.__ensureClientRouteModuleForHMR ? {
__ensureClientRouteModuleForHMR: hmrRoute.__ensureClientRouteModuleForHMR
} : {}
};
}
async function getManifestRoute(route) {
await explodeLazyRoute(route);
const Layout = route.Layout || React3__namespace.Fragment;
const errorElement = route.ErrorBoundary ? React3__namespace.createElement(
Layout,
null,
React3__namespace.createElement(route.ErrorBoundary)
) : void 0;
return {
clientAction: route.clientAction,
clientLoader: route.clientLoader,
handle: route.handle,
hasAction: !!route.action,
hasComponent: !!route.Component,
hasErrorBoundary: !!route.ErrorBoundary,
errorElement,
hasLoader: !!route.loader,
id: route.id,
parentId: route.parentId,
path: route.path,
index: "index" in route ? route.index : void 0,
links: route.links,
meta: route.meta
};
}
async function explodeLazyRoute(route) {
if ("lazy" in route && route.lazy) {
let {
default: lazyDefaultExport,
Component: lazyComponentExport,
...lazyProperties
} = await route.lazy();
let Component = lazyComponentExport || lazyDefaultExport;
if (Component && !route.Component) {
route.Component = Component;
}
for (let [k, v] of Object.entries(lazyProperties)) {
if (k !== "id" && k !== "path" && k !== "index" && k !== "children" && route[k] == null) {
route[k] = v;
}
}
route.lazy = void 0;
}
}
async function getAllRoutePatches(routes, basename) {
let patches = [];
async function traverse(route, parentId) {
let manifestRoute = await getManifestRoute({ ...route, parentId });
patches.push(manifestRoute);
if ("children" in route && route.children?.length) {
for (let child of route.children) {
await traverse(child, route.id);
}
}
}
for (let route of routes) {
await traverse(route, void 0);
}
return patches.filter((p) => !!p.parentId);
}
async function getAdditionalRoutePatches(pathnames, routes, basename, matchedRouteIds) {
let patchRouteMatches = /* @__PURE__ */ new Map();
let matchedPaths = /* @__PURE__ */ new Set();
for (const pathname of pathnames) {
if (matchedPaths.has(pathname)) {
continue;
}
matchedPaths.add(pathname);
let matches = matchRoutes(routes, pathname, basename) || [];
matches.forEach((m, i) => {
if (patchRouteMatches.get(m.route.id)) {
return;
}
patchRouteMatches.set(m.route.id, {
...m.route,
parentId: matches[i - 1]?.route.id
});
});
}
let patches = await Promise.all(
[...patchRouteMatches.values()].filter((route) => !matchedRouteIds.some((id) => id === route.id)).map((route) => getManifestRoute(route))
);
return patches;
}
function isReactServerRequest(url) {
return url.pathname.endsWith(".rsc");
}
function isManifestRequest(url) {
return url.pathname.endsWith(".manifest");
}
function defaultOnError(error) {
if (isRedirectResponse(error)) {
return createRedirectErrorDigest(error);
}
if (isResponse(error) || isDataWithResponseInit(error)) {
return createRouteErrorResponseDigest(error);
}
}
function isClientReference(x) {
try {
return x.$$typeof === Symbol.for("react.client.reference");
} catch {
return false;
}
}
function canDecodeWithFormData(contentType) {
if (!contentType) return false;
return contentType.match(/\bapplication\/x-www-form-urlencoded\b/) || contentType.match(/\bmultipart\/form-data\b/);
}
// lib/href.ts
function href(path, ...args) {
let params = args[0];
let result = trimTrailingSplat(path).replace(
/\/:([\w-]+)(\?)?/g,
// same regex as in .\router\utils.ts: compilePath().
(_, param, questionMark) => {
const isRequired = questionMark === void 0;
const value = params?.[param];
if (isRequired && value === void 0) {
throw new Error(
`Path '${path}' requires param '${param}' but it was not provided`
);
}
return value === void 0 ? "" : "/" + value;
}
);
if (path.endsWith("*")) {
const value = params?.["*"];
if (value !== void 0) {
result += "/" + value;
}
}
return result || "/";
}
function trimTrailingSplat(path) {
let i = path.length - 1;
let char = path[i];
if (char !== "*" && char !== "/") return path;
i--;
for (; i >= 0; i--) {
if (path[i] !== "/") break;
}
return path.slice(0, i + 1);
}
// lib/server-runtime/crypto.ts
var encoder = /* @__PURE__ */ new TextEncoder();
var sign = async (value, secret) => {
let data2 = encoder.encode(value);
let key = await createKey2(secret, ["sign"]);
let signature = await crypto.subtle.sign("HMAC", key, data2);
let hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(
/=+$/,
""
);
return value + "." + hash;
};
var unsign = async (cookie, secret) => {
let index = cookie.lastIndexOf(".");
let value = cookie.slice(0, index);
let hash = cookie.slice(index + 1);
let data2 = encoder.encode(value);
let key = await createKey2(secret, ["verify"]);
try {
let signature = byteStringToUint8Array(atob(hash));
let valid = await crypto.subtle.verify("HMAC", key, signature, data2);
return valid ? value : false;
} catch (e) {
return false;
}
};
var createKey2 = async (secret, usages) => crypto.subtle.importKey(
"raw",
encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
usages
);
function byteStringToUint8Array(byteString) {
let array = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
array[i] = byteString.charCodeAt(i);
}
return array;
}
// lib/server-runtime/cookies.ts
var createCookie = (name, cookieOptions = {}) => {
let { secrets = [], ...options } = {
path: "/",
sameSite: "lax",
...cookieOptions
};
warnOnceAboutExpiresCookie(name, options.expires);
return {
get name() {
return name;
},
get isSigned() {
return secrets.length > 0;
},
get expires() {
return typeof options.maxAge !== "undefined" ? new Date(Date.now() + options.maxAge * 1e3) : options.expires;
},
async parse(cookieHeader, parseOptions) {
if (!cookieHeader) return null;
let cookies = cookie.parse(cookieHeader, { ...options, ...parseOptions });
if (name in cookies) {
let value = cookies[name];
if (typeof value === "string" && value !== "") {
let decoded = await decodeCookieValue(value, secrets);
return decoded;
} else {
return "";
}
} else {
return null;
}
},
async serialize(value, serializeOptions) {
return cookie.serialize(
name,
value === "" ? "" : await encodeCookieValue(value, secrets),
{
...options,
...serializeOptions
}
);
}
};
};
var isCookie = (object) => {
return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
};
async function encodeCookieValue(value, secrets) {
let encoded = encodeData(value);
if (secrets.length > 0) {
encoded = await sign(encoded, secrets[0]);
}
return encoded;
}
async function decodeCookieValue(value, secrets) {
if (secrets.length > 0) {
for (let secret of secrets) {
let unsignedValue = await unsign(value, secret);
if (unsignedValue !== false) {
return decodeData(unsignedValue);
}
}
return null;
}
return decodeData(value);
}
function encodeData(value) {
return btoa(myUnescape(encodeURIComponent(JSON.stringify(value))));
}
function decodeData(value) {
try {
return JSON.parse(decodeURIComponent(myEscape(atob(value))));
} catch (e) {
return {};
}
}
function myEscape(value) {
let str = value.toString();
let result = "";
let index = 0;
let chr, code;
while (index < str.length) {
chr = str.charAt(index++);
if (/[\w*+\-./@]/.exec(chr)) {
result += chr;
} else {
code = chr.charCodeAt(0);
if (code < 256) {
result += "%" + hex(code, 2);
} else {
result += "%u" + hex(code, 4).toUpperCase();
}
}
}
return result;
}
function hex(code, length) {
let result = code.toString(16);
while (result.length < length) result = "0" + result;
return result;
}
function myUnescape(value) {
let str = value.toString();
let result = "";
let index = 0;
let chr, part;
while (index < str.length) {
chr = str.charAt(index++);
if (chr === "%") {
if (str.charAt(index) === "u") {
part = str.slice(index + 1, index + 5);
if (/^[\da-f]{4}$/i.exec(part)) {
result += String.fromCharCode(parseInt(part, 16));
index += 5;
continue;
}
} else {
part = str.slice(index, index + 2);
if (/^[\da-f]{2}$/i.exec(part)) {
result += String.fromCharCode(parseInt(part, 16));
index += 2;
continue;
}
}
}
result += chr;
}
return result;
}
function warnOnceAboutExpiresCookie(name, expires) {
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.`
);
}
// lib/server-runtime/sessions.ts
function flash(name) {
return `__flash_${name}__`;
}
var createSession = (initialData = {}, id = "") => {
let map = new Map(Object.entries(initialData));
return {
get id() {
return id;
},
get data() {
return Object.fromEntries(map);
},
has(name) {
return map.has(name) || map.has(flash(name));
},
get(name) {
if (map.has(name)) return map.get(name);
let flashName = flash(name);
if (map.has(flashName)) {
let value = map.get(flashName);
map.delete(flashName);
return value;
}
return void 0;
},
set(name, value) {
map.set(name, value);
},
flash(name, value) {
map.set(flash(name), value);
},
unset(name) {
map.delete(name);
}
};
};
var isSession = (object) => {
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";
};
function createSessionStorage({
cookie: cookieArg,
createData,
readData,
updateData,
deleteData
}) {
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
warnOnceAboutSigningSessionCookie(cookie);
return {
async getSession(cookieHeader, options) {
let id = cookieHeader && await cookie.parse(cookieHeader, options);
let data2 = id && await readData(id);
return createSession(data2 || {}, id || "");
},
async commitSession(session, options) {
let { id, data: data2 } = session;
let expires = options?.maxAge != null ? new Date(Date.now() + options.maxAge * 1e3) : options?.expires != null ? options.expires : cookie.expires;
if (id) {
await updateData(id, data2, expires);
} else {
id = await createData(data2, expires);
}
return cookie.serialize(id, options);
},
async destroySession(session, options) {
await deleteData(session.id);
return cookie.serialize("", {
...options,
maxAge: void 0,
expires: /* @__PURE__ */ new Date(0)
});
}
};
}
function warnOnceAboutSigningSessionCookie(cookie) {
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.`
);
}
// lib/server-runtime/sessions/cookieStorage.ts
function createCookieSessionStorage({ cookie: cookieArg } = {}) {
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
warnOnceAboutSigningSessionCookie(cookie);
return {
async getSession(cookieHeader, options) {
return createSession(
cookieHeader && await cookie.parse(cookieHeader, options) || {}
);
},
async commitSession(session, options) {
let serializedCookie = await cookie.serialize(session.data, options);
if (serializedCookie.length > 4096) {
throw new Error(
"Cookie length will exceed browser maximum. Length: " + serializedCookie.length
);
}
return serializedCookie;
},
async destroySession(_session, options) {
return cookie.serialize("", {
...options,
maxAge: void 0,
expires: /* @__PURE__ */ new Date(0)
});
}
};
}
// lib/server-runtime/sessions/memoryStorage.ts
function createMemorySessionStorage({ cookie } = {}) {
let map = /* @__PURE__ */ new Map();
return createSessionStorage({
cookie,
async createData(data2, expires) {
let id = Math.random().toString(36).substring(2, 10);
map.set(id, { data: data2, expires });
return id;
},
async readData(id) {
if (map.has(id)) {
let { data: data2, expires } = map.get(id);
if (!expires || expires > /* @__PURE__ */ new Date()) {
return data2;
}
if (expires) map.delete(id);
}
return null;
},
async updateData(id, data2, expires) {
map.set(id, { data: data2, expires });
},
async deleteData(id) {
map.delete(id);
}
});
}
Object.defineProperty(exports, "BrowserRouter", {
enumerable: true,
get: function () { return reactServerClient.BrowserRouter; }
});
Object.defineProperty(exports, "Form", {
enumerable: true,
get: function () { return reactServerClient.Form; }
});
Object.defineProperty(exports, "HashRouter", {
enumerable: true,
get: function () { return reactServerClient.HashRouter; }
});
Object.defineProperty(exports, "Link", {
enumerable: true,
get: function () { return reactServerClient.Link; }
});
Object.defineProperty(exports, "Links", {
enumerable: true,
get: function () { return reactServerClient.Links; }
});
Object.defineProperty(exports, "MemoryRouter", {
enumerable: true,
get: function () { return reactServerClient.MemoryRouter; }
});
Object.defineProperty(exports, "Meta", {
enumerable: true,
get: function () { return reactServerClient.Meta; }
});
Object.defineProperty(exports, "NavLink", {
enumerable: true,
get: function () { return reactServerClient.NavLink; }
});
Object.defineProperty(exports, "Navigate", {
enumerable: true,
get: function () { return reactServerClient.Navigate; }
});
Object.defineProperty(exports, "Outlet", {
enumerable: true,
get: function () { return reactServerClient.Outlet; }
});
Object.defineProperty(exports, "Route", {
enumerable: true,
get: function () { return reactServerClient.Route; }
});
Object.defineProperty(exports, "Router", {
enumerable: true,
get: function () { return reactServerClient.Router; }
});
Object.defineProperty(exports, "RouterProvider", {
enumerable: true,
get: function () { return reactServerClient.RouterProvider; }
});
Object.defineProperty(exports, "Routes", {
enumerable: true,
get: function () { return reactServerClient.Routes; }
});
Object.defineProperty(exports, "ScrollRestoration", {
enumerable: true,
get: function () { return reactServerClient.ScrollRestoration; }
});
Object.defineProperty(exports, "StaticRouter", {
enumerable: true,
get: function () { return reactServerClient.StaticRouter; }
});
Object.defineProperty(exports, "StaticRouterProvider", {
enumerable: true,
get: function () { return reactServerClient.StaticRouterProvider; }
});
Object.defineProperty(exports, "unstable_HistoryRouter", {
enumerable: true,
get: function () { return reactServerClient.unstable_HistoryRouter; }
});
exports.Await = Await;
exports.RouterContextProvider = RouterContextProvider;
exports.createContext = createContext;
exports.createCookie = createCookie;
exports.createCookieSessionStorage = createCookieSessionStorage;
exports.createMemorySessionStorage = createMemorySessionStorage;
exports.createSession = createSession;
exports.createSessionStorage = createSessionStorage;
exports.createStaticHandler = createStaticHandler;
exports.data = data;
exports.href = href;
exports.isCookie = isCookie;
exports.isRouteErrorResponse = isRouteErrorResponse;
exports.isSession = isSession;
exports.matchRoutes = matchRoutes;
exports.redirect = redirect2;
exports.redirectDocument = redirectDocument2;
exports.replace = replace2;
exports.unstable_getRequest = getRequest;
exports.unstable_matchRSCServerRequest = matchRSCServerRequest;
+3803
View File
@@ -0,0 +1,3803 @@
import { AsyncLocalStorage } from 'node:async_hooks';
import * as React3 from 'react';
import { splitCookiesString } from 'set-cookie-parser';
import { UNSAFE_AwaitContextProvider, UNSAFE_WithComponentProps, Outlet as Outlet$1, UNSAFE_WithErrorBoundaryProps, UNSAFE_WithHydrateFallbackProps } from 'react-router/internal/react-server-client';
export { BrowserRouter, Form, HashRouter, Link, Links, MemoryRouter, Meta, NavLink, Navigate, Outlet, Route, Router, RouterProvider, Routes, ScrollRestoration, StaticRouter, StaticRouterProvider, unstable_HistoryRouter } from 'react-router/internal/react-server-client';
import { serialize, parse } from 'cookie';
/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
var __typeError = (msg) => {
throw TypeError(msg);
};
var __accessCheck = (obj, member, msg) => member.has(obj) || __typeError("Cannot " + msg);
var __privateGet = (obj, member, getter) => (__accessCheck(obj, member, "read from private field"), getter ? getter.call(obj) : member.get(obj));
var __privateAdd = (obj, member, value) => member.has(obj) ? __typeError("Cannot add the same private member more than once") : member instanceof WeakSet ? member.add(obj) : member.set(obj, value);
// lib/router/url.ts
var ABSOLUTE_URL_REGEX = /^(?:[a-z][a-z0-9+.-]*:|[\\/]{2})/i;
// lib/router/history.ts
function invariant(value, message) {
if (value === false || value === null || typeof value === "undefined") {
throw new Error(message);
}
}
function warning(cond, message) {
if (!cond) {
if (typeof console !== "undefined") console.warn(message);
try {
throw new Error(message);
} catch (e) {
}
}
}
function createKey() {
return Math.random().toString(36).substring(2, 10);
}
function createLocation(current, to, state = null, key, mask) {
let location = {
pathname: typeof current === "string" ? current : current.pathname,
search: "",
hash: "",
...typeof to === "string" ? parsePath(to) : to,
state,
// TODO: This could be cleaned up. push/replace should probably just take
// full Locations now and avoid the need to run through this flow at all
// But that's a pretty big refactor to the current test suite so going to
// keep as is for the time being and just let any incoming keys take precedence
key: to && to.key || key || createKey(),
mask
};
return location;
}
function createPath({
pathname = "/",
search = "",
hash = ""
}) {
if (search && search !== "?")
pathname += search.charAt(0) === "?" ? search : "?" + search;
if (hash && hash !== "#")
pathname += hash.charAt(0) === "#" ? hash : "#" + hash;
return pathname;
}
function parsePath(path) {
let parsedPath = {};
if (path) {
let hashIndex = path.indexOf("#");
if (hashIndex >= 0) {
parsedPath.hash = path.substring(hashIndex);
path = path.substring(0, hashIndex);
}
let searchIndex = path.indexOf("?");
if (searchIndex >= 0) {
parsedPath.search = path.substring(searchIndex);
path = path.substring(0, searchIndex);
}
if (path) {
parsedPath.pathname = path;
}
}
return parsedPath;
}
// lib/router/instrumentation.ts
var UninstrumentedSymbol = Symbol("Uninstrumented");
function getRouteInstrumentationUpdates(fns, route) {
let aggregated = {
lazy: [],
"lazy.loader": [],
"lazy.action": [],
"lazy.middleware": [],
middleware: [],
loader: [],
action: []
};
fns.forEach(
(fn) => fn({
id: route.id,
index: route.index,
path: route.path,
instrument(i) {
let keys = Object.keys(aggregated);
for (let key of keys) {
if (i[key]) {
aggregated[key].push(i[key]);
}
}
}
})
);
let updates = {};
if (typeof route.lazy === "function" && aggregated.lazy.length > 0) {
let instrumented = wrapImpl(aggregated.lazy, route.lazy, () => void 0);
if (instrumented) {
updates.lazy = instrumented;
}
}
if (typeof route.lazy === "object") {
let lazyObject = route.lazy;
["middleware", "loader", "action"].forEach((key) => {
let lazyFn = lazyObject[key];
let instrumentations = aggregated[`lazy.${key}`];
if (typeof lazyFn === "function" && instrumentations.length > 0) {
let instrumented = wrapImpl(instrumentations, lazyFn, () => void 0);
if (instrumented) {
updates.lazy = Object.assign(updates.lazy || {}, {
[key]: instrumented
});
}
}
});
}
["loader", "action"].forEach((key) => {
let handler = route[key];
if (typeof handler === "function" && aggregated[key].length > 0) {
let original = handler[UninstrumentedSymbol] ?? handler;
let instrumented = wrapImpl(
aggregated[key],
original,
(...args) => getHandlerInfo(args[0])
);
if (instrumented) {
if (key === "loader" && original.hydrate === true) {
instrumented.hydrate = true;
}
instrumented[UninstrumentedSymbol] = original;
updates[key] = instrumented;
}
}
});
if (route.middleware && route.middleware.length > 0 && aggregated.middleware.length > 0) {
updates.middleware = route.middleware.map((middleware) => {
let original = middleware[UninstrumentedSymbol] ?? middleware;
let instrumented = wrapImpl(
aggregated.middleware,
original,
(...args) => getHandlerInfo(args[0])
);
if (instrumented) {
instrumented[UninstrumentedSymbol] = original;
return instrumented;
}
return middleware;
});
}
return updates;
}
function wrapImpl(impls, handler, getInfo) {
if (impls.length === 0) {
return null;
}
return async (...args) => {
let result = await recurseRight(
impls,
getInfo(...args),
() => handler(...args),
impls.length - 1
);
if (result.type === "error") {
throw result.value;
}
return result.value;
};
}
async function recurseRight(impls, info, handler, index) {
let impl = impls[index];
let result;
if (!impl) {
try {
let value = await handler();
result = { type: "success", value };
} catch (e) {
result = { type: "error", value: e };
}
} else {
let handlerPromise = void 0;
let callHandler = async () => {
if (handlerPromise) {
console.error("You cannot call instrumented handlers more than once");
} else {
handlerPromise = recurseRight(impls, info, handler, index - 1);
}
result = await handlerPromise;
invariant(result, "Expected a result");
if (result.type === "error" && result.value instanceof Error) {
return { status: "error", error: result.value };
}
return { status: "success", error: void 0 };
};
try {
await impl(callHandler, info);
} catch (e) {
console.error("An instrumentation function threw an error:", e);
}
if (!handlerPromise) {
await callHandler();
}
await handlerPromise;
}
if (result) {
return result;
}
return {
type: "error",
value: new Error("No result assigned in instrumentation chain.")
};
}
function getHandlerInfo(args) {
let { request, context, params, pattern } = args;
return {
request: getReadonlyRequest(request),
params: { ...params },
pattern,
context: getReadonlyContext(context)
};
}
function getReadonlyRequest(request) {
return {
method: request.method,
url: request.url,
headers: {
get: (...args) => request.headers.get(...args)
}
};
}
function getReadonlyContext(context) {
if (isPlainObject(context)) {
let frozen = { ...context };
Object.freeze(frozen);
return frozen;
} else {
return {
get: (ctx) => context.get(ctx)
};
}
}
var objectProtoNames = Object.getOwnPropertyNames(Object.prototype).sort().join("\0");
function isPlainObject(thing) {
if (thing === null || typeof thing !== "object") {
return false;
}
const proto = Object.getPrototypeOf(thing);
return proto === Object.prototype || proto === null || Object.getOwnPropertyNames(proto).sort().join("\0") === objectProtoNames;
}
// lib/router/utils.ts
function createContext(defaultValue) {
return { defaultValue };
}
var _map;
var RouterContextProvider = class {
/**
* Create a new `RouterContextProvider` instance
* @param init An optional initial context map to populate the provider with
*/
constructor(init) {
__privateAdd(this, _map, /* @__PURE__ */ new Map());
if (init) {
for (let [context, value] of init) {
this.set(context, value);
}
}
}
/**
* Access a value from the context. If no value has been set for the context,
* it will return the context's `defaultValue` if provided, or throw an error
* if no `defaultValue` was set.
* @param context The context to get the value for
* @returns The value for the context, or the context's `defaultValue` if no
* value was set
*/
get(context) {
if (__privateGet(this, _map).has(context)) {
return __privateGet(this, _map).get(context);
}
if (context.defaultValue !== void 0) {
return context.defaultValue;
}
throw new Error("No value found for context");
}
/**
* Set a value for the context. If the context already has a value set, this
* will overwrite it.
*
* @param context The context to set the value for
* @param value The value to set for the context
* @returns {void}
*/
set(context, value) {
__privateGet(this, _map).set(context, value);
}
};
_map = new WeakMap();
var unsupportedLazyRouteObjectKeys = /* @__PURE__ */ new Set([
"lazy",
"caseSensitive",
"path",
"id",
"index",
"children"
]);
function isUnsupportedLazyRouteObjectKey(key) {
return unsupportedLazyRouteObjectKeys.has(
key
);
}
var unsupportedLazyRouteFunctionKeys = /* @__PURE__ */ new Set([
"lazy",
"caseSensitive",
"path",
"id",
"index",
"middleware",
"children"
]);
function isUnsupportedLazyRouteFunctionKey(key) {
return unsupportedLazyRouteFunctionKeys.has(
key
);
}
function isIndexRoute(route) {
return route.index === true;
}
function convertRoutesToDataRoutes(routes, mapRouteProperties, parentPath = [], manifest = {}, allowInPlaceMutations = false) {
return routes.map((route, index) => {
let treePath = [...parentPath, String(index)];
let id = typeof route.id === "string" ? route.id : treePath.join("-");
invariant(
route.index !== true || !route.children,
`Cannot specify children on an index route`
);
invariant(
allowInPlaceMutations || !manifest[id],
`Found a route id collision on id "${id}". Route id's must be globally unique within Data Router usages`
);
if (isIndexRoute(route)) {
let indexRoute = {
...route,
id
};
manifest[id] = mergeRouteUpdates(
indexRoute,
mapRouteProperties(indexRoute)
);
return indexRoute;
} else {
let pathOrLayoutRoute = {
...route,
id,
children: void 0
};
manifest[id] = mergeRouteUpdates(
pathOrLayoutRoute,
mapRouteProperties(pathOrLayoutRoute)
);
if (route.children) {
pathOrLayoutRoute.children = convertRoutesToDataRoutes(
route.children,
mapRouteProperties,
treePath,
manifest,
allowInPlaceMutations
);
}
return pathOrLayoutRoute;
}
});
}
function mergeRouteUpdates(route, updates) {
return Object.assign(route, {
...updates,
...typeof updates.lazy === "object" && updates.lazy != null ? {
lazy: {
...route.lazy,
...updates.lazy
}
} : {}
});
}
function matchRoutes(routes, locationArg, basename = "/") {
return matchRoutesImpl(routes, locationArg, basename, false);
}
function matchRoutesImpl(routes, locationArg, basename, allowPartial, precomputedBranches) {
let location = typeof locationArg === "string" ? parsePath(locationArg) : locationArg;
let pathname = stripBasename(location.pathname || "/", basename);
if (pathname == null) {
return null;
}
let branches = precomputedBranches ?? flattenAndRankRoutes(routes);
let matches = null;
let decoded = decodePath(pathname);
for (let i = 0; matches == null && i < branches.length; ++i) {
matches = matchRouteBranch(
branches[i],
decoded,
allowPartial
);
}
return matches;
}
function convertRouteMatchToUiMatch(match, loaderData) {
let { route, pathname, params } = match;
return {
id: route.id,
pathname,
params,
data: loaderData[route.id],
loaderData: loaderData[route.id],
handle: route.handle
};
}
function flattenAndRankRoutes(routes) {
let branches = flattenRoutes(routes);
rankRouteBranches(branches);
return branches;
}
function flattenRoutes(routes, branches = [], parentsMeta = [], parentPath = "", _hasParentOptionalSegments = false) {
let flattenRoute = (route, index, hasParentOptionalSegments = _hasParentOptionalSegments, relativePath) => {
let meta = {
relativePath: relativePath === void 0 ? route.path || "" : relativePath,
caseSensitive: route.caseSensitive === true,
childrenIndex: index,
route
};
if (meta.relativePath.startsWith("/")) {
if (!meta.relativePath.startsWith(parentPath) && hasParentOptionalSegments) {
return;
}
invariant(
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.`
);
meta.relativePath = meta.relativePath.slice(parentPath.length);
}
let path = joinPaths([parentPath, meta.relativePath]);
let routesMeta = parentsMeta.concat(meta);
if (route.children && route.children.length > 0) {
invariant(
// Our types know better, but runtime JS may not!
// @ts-expect-error
route.index !== true,
`Index routes must not have child routes. Please remove all child routes from route path "${path}".`
);
flattenRoutes(
route.children,
branches,
routesMeta,
path,
hasParentOptionalSegments
);
}
if (route.path == null && !route.index) {
return;
}
branches.push({
path,
score: computeScore(path, route.index),
routesMeta: routesMeta.map((meta2, i) => {
let [matcher, params] = compilePath(
meta2.relativePath,
meta2.caseSensitive,
i === routesMeta.length - 1
);
return {
...meta2,
matcher,
compiledParams: params
};
})
});
};
routes.forEach((route, index) => {
if (route.path === "" || !route.path?.includes("?")) {
flattenRoute(route, index);
} else {
for (let exploded of explodeOptionalSegments(route.path)) {
flattenRoute(route, index, true, exploded);
}
}
});
return branches;
}
function explodeOptionalSegments(path) {
let segments = path.split("/");
if (segments.length === 0) return [];
let [first, ...rest] = segments;
let isOptional = first.endsWith("?");
let required = first.replace(/\?$/, "");
if (rest.length === 0) {
return isOptional ? [required, ""] : [required];
}
let restExploded = explodeOptionalSegments(rest.join("/"));
let result = [];
result.push(
...restExploded.map(
(subpath) => subpath === "" ? required : [required, subpath].join("/")
)
);
if (isOptional) {
result.push(...restExploded);
}
return result.map(
(exploded) => path.startsWith("/") && exploded === "" ? "/" : exploded
);
}
function rankRouteBranches(branches) {
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)
)
);
}
var paramRe = /^:[\w-]+$/;
var dynamicSegmentValue = 3;
var indexRouteValue = 2;
var emptySegmentValue = 1;
var staticSegmentValue = 10;
var splatPenalty = -2;
var isSplat = (s) => s === "*";
function computeScore(path, index) {
let segments = path.split("/");
let initialScore = segments.length;
if (segments.some(isSplat)) {
initialScore += splatPenalty;
}
if (index) {
initialScore += indexRouteValue;
}
return segments.filter((s) => !isSplat(s)).reduce(
(score, segment) => score + (paramRe.test(segment) ? dynamicSegmentValue : segment === "" ? emptySegmentValue : staticSegmentValue),
initialScore
);
}
function compareIndexes(a, b) {
let siblings = a.length === b.length && a.slice(0, -1).every((n, i) => n === b[i]);
return siblings ? (
// If two routes are siblings, we should try to match the earlier sibling
// first. This allows people to have fine-grained control over the matching
// behavior by simply putting routes with identical paths in the order they
// want them tried.
a[a.length - 1] - b[b.length - 1]
) : (
// Otherwise, it doesn't really make sense to rank non-siblings by index,
// so they sort equally.
0
);
}
function matchRouteBranch(branch, pathname, allowPartial = false) {
let { routesMeta } = branch;
let matchedParams = {};
let matchedPathname = "/";
let matches = [];
for (let i = 0; i < routesMeta.length; ++i) {
let meta = routesMeta[i];
let end = i === routesMeta.length - 1;
let remainingPathname = matchedPathname === "/" ? pathname : pathname.slice(matchedPathname.length) || "/";
let pattern = {
path: meta.relativePath,
caseSensitive: meta.caseSensitive,
end
};
let match = (
// Use precomputed matcher if it exists
meta.matcher && meta.compiledParams ? matchPathImpl(
pattern,
remainingPathname,
meta.matcher,
meta.compiledParams
) : matchPath(pattern, remainingPathname)
);
let route = meta.route;
if (!match && end && allowPartial && !routesMeta[routesMeta.length - 1].route.index) {
match = matchPath(
{
path: meta.relativePath,
caseSensitive: meta.caseSensitive,
end: false
},
remainingPathname
);
}
if (!match) {
return null;
}
Object.assign(matchedParams, match.params);
matches.push({
// TODO: Can this as be avoided?
params: matchedParams,
pathname: joinPaths([matchedPathname, match.pathname]),
pathnameBase: normalizePathname(
joinPaths([matchedPathname, match.pathnameBase])
),
route
});
if (match.pathnameBase !== "/") {
matchedPathname = joinPaths([matchedPathname, match.pathnameBase]);
}
}
return matches;
}
function matchPath(pattern, pathname) {
if (typeof pattern === "string") {
pattern = { path: pattern, caseSensitive: false, end: true };
}
let [matcher, compiledParams] = compilePath(
pattern.path,
pattern.caseSensitive,
pattern.end
);
return matchPathImpl(pattern, pathname, matcher, compiledParams);
}
function matchPathImpl(pattern, pathname, matcher, compiledParams) {
let match = pathname.match(matcher);
if (!match) return null;
let matchedPathname = match[0];
let pathnameBase = matchedPathname.replace(/(.)\/+$/, "$1");
let captureGroups = match.slice(1);
let params = compiledParams.reduce(
(memo, { paramName, isOptional }, index) => {
if (paramName === "*") {
let splatValue = captureGroups[index] || "";
pathnameBase = matchedPathname.slice(0, matchedPathname.length - splatValue.length).replace(/(.)\/+$/, "$1");
}
const value = captureGroups[index];
if (isOptional && !value) {
memo[paramName] = void 0;
} else {
memo[paramName] = (value || "").replace(/%2F/g, "/");
}
return memo;
},
{}
);
return {
params,
pathname: matchedPathname,
pathnameBase,
pattern
};
}
function compilePath(path, caseSensitive = false, end = true) {
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(/\*$/, "/*")}".`
);
let params = [];
let regexpSource = "^" + path.replace(/\/*\*?$/, "").replace(/^\/*/, "/").replace(/[\\.*+^${}|()[\]]/g, "\\$&").replace(
/\/:([\w-]+)(\?)?/g,
(match, paramName, isOptional, index, str) => {
params.push({ paramName, isOptional: isOptional != null });
if (isOptional) {
let nextChar = str.charAt(index + match.length);
if (nextChar && nextChar !== "/") {
return "/([^\\/]*)";
}
return "(?:/([^\\/]*))?";
}
return "/([^\\/]+)";
}
).replace(/\/([\w-]+)\?(\/|$)/g, "(/$1)?$2");
if (path.endsWith("*")) {
params.push({ paramName: "*" });
regexpSource += path === "*" || path === "/*" ? "(.*)$" : "(?:\\/(.+)|\\/*)$";
} else if (end) {
regexpSource += "\\/*$";
} else if (path !== "" && path !== "/") {
regexpSource += "(?:(?=\\/|$))";
} else ;
let matcher = new RegExp(regexpSource, caseSensitive ? void 0 : "i");
return [matcher, params];
}
function decodePath(value) {
try {
return value.split("/").map((v) => decodeURIComponent(v).replace(/\//g, "%2F")).join("/");
} catch (error) {
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}).`
);
return value;
}
}
function stripBasename(pathname, basename) {
if (basename === "/") return pathname;
if (!pathname.toLowerCase().startsWith(basename.toLowerCase())) {
return null;
}
let startIndex = basename.endsWith("/") ? basename.length - 1 : basename.length;
let nextChar = pathname.charAt(startIndex);
if (nextChar && nextChar !== "/") {
return null;
}
return pathname.slice(startIndex) || "/";
}
function prependBasename({
basename,
pathname
}) {
return pathname === "/" ? basename : joinPaths([basename, pathname]);
}
var isAbsoluteUrl = (url) => ABSOLUTE_URL_REGEX.test(url);
function resolvePath(to, fromPathname = "/") {
let {
pathname: toPathname,
search = "",
hash = ""
} = typeof to === "string" ? parsePath(to) : to;
let pathname;
if (toPathname) {
toPathname = removeDoubleSlashes(toPathname);
if (toPathname.startsWith("/")) {
pathname = resolvePathname(toPathname.substring(1), "/");
} else {
pathname = resolvePathname(toPathname, fromPathname);
}
} else {
pathname = fromPathname;
}
return {
pathname,
search: normalizeSearch(search),
hash: normalizeHash(hash)
};
}
function resolvePathname(relativePath, fromPathname) {
let segments = removeTrailingSlash(fromPathname).split("/");
let relativeSegments = relativePath.split("/");
relativeSegments.forEach((segment) => {
if (segment === "..") {
if (segments.length > 1) segments.pop();
} else if (segment !== ".") {
segments.push(segment);
}
});
return segments.length > 1 ? segments.join("/") : "/";
}
function getInvalidPathError(char, field, dest, path) {
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.`;
}
function getPathContributingMatches(matches) {
return matches.filter(
(match, index) => index === 0 || match.route.path && match.route.path.length > 0
);
}
function getResolveToMatches(matches) {
let pathMatches = getPathContributingMatches(matches);
return pathMatches.map(
(match, idx) => idx === pathMatches.length - 1 ? match.pathname : match.pathnameBase
);
}
function resolveTo(toArg, routePathnames, locationPathname, isPathRelative = false) {
let to;
if (typeof toArg === "string") {
to = parsePath(toArg);
} else {
to = { ...toArg };
invariant(
!to.pathname || !to.pathname.includes("?"),
getInvalidPathError("?", "pathname", "search", to)
);
invariant(
!to.pathname || !to.pathname.includes("#"),
getInvalidPathError("#", "pathname", "hash", to)
);
invariant(
!to.search || !to.search.includes("#"),
getInvalidPathError("#", "search", "hash", to)
);
}
let isEmptyPath = toArg === "" || to.pathname === "";
let toPathname = isEmptyPath ? "/" : to.pathname;
let from;
if (toPathname == null) {
from = locationPathname;
} else {
let routePathnameIndex = routePathnames.length - 1;
if (!isPathRelative && toPathname.startsWith("..")) {
let toSegments = toPathname.split("/");
while (toSegments[0] === "..") {
toSegments.shift();
routePathnameIndex -= 1;
}
to.pathname = toSegments.join("/");
}
from = routePathnameIndex >= 0 ? routePathnames[routePathnameIndex] : "/";
}
let path = resolvePath(to, from);
let hasExplicitTrailingSlash = toPathname && toPathname !== "/" && toPathname.endsWith("/");
let hasCurrentTrailingSlash = (isEmptyPath || toPathname === ".") && locationPathname.endsWith("/");
if (!path.pathname.endsWith("/") && (hasExplicitTrailingSlash || hasCurrentTrailingSlash)) {
path.pathname += "/";
}
return path;
}
var removeDoubleSlashes = (path) => path.replace(/[\\/]{2,}/g, "/");
var joinPaths = (paths) => removeDoubleSlashes(paths.join("/"));
var removeTrailingSlash = (path) => path.replace(/\/+$/, "");
var normalizePathname = (pathname) => removeTrailingSlash(pathname).replace(/^\/*/, "/");
var normalizeSearch = (search) => !search || search === "?" ? "" : search.startsWith("?") ? search : "?" + search;
var normalizeHash = (hash) => !hash || hash === "#" ? "" : hash.startsWith("#") ? hash : "#" + hash;
var DataWithResponseInit = class {
constructor(data2, init) {
this.type = "DataWithResponseInit";
this.data = data2;
this.init = init || null;
}
};
function data(data2, init) {
return new DataWithResponseInit(
data2,
typeof init === "number" ? { status: init } : init
);
}
var redirect = (url, init = 302) => {
let responseInit = init;
if (typeof responseInit === "number") {
responseInit = { status: responseInit };
} else if (typeof responseInit.status === "undefined") {
responseInit.status = 302;
}
let headers = new Headers(responseInit.headers);
headers.set("Location", url);
return new Response(null, { ...responseInit, headers });
};
var redirectDocument = (url, init) => {
let response = redirect(url, init);
response.headers.set("X-Remix-Reload-Document", "true");
return response;
};
var replace = (url, init) => {
let response = redirect(url, init);
response.headers.set("X-Remix-Replace", "true");
return response;
};
var ErrorResponseImpl = class {
constructor(status, statusText, data2, internal = false) {
this.status = status;
this.statusText = statusText || "";
this.internal = internal;
if (data2 instanceof Error) {
this.data = data2.toString();
this.error = data2;
} else {
this.data = data2;
}
}
};
function isRouteErrorResponse(error) {
return error != null && typeof error.status === "number" && typeof error.statusText === "string" && typeof error.internal === "boolean" && "data" in error;
}
function getRoutePattern(matches) {
let parts = matches.map((m) => m.route.path).filter(Boolean);
return joinPaths(parts) || "/";
}
// lib/router/router.ts
var validMutationMethodsArr = [
"POST",
"PUT",
"PATCH",
"DELETE"
];
var validMutationMethods = new Set(
validMutationMethodsArr
);
var validRequestMethodsArr = [
"GET",
...validMutationMethodsArr
];
var validRequestMethods = new Set(validRequestMethodsArr);
var redirectStatusCodes = /* @__PURE__ */ new Set([301, 302, 303, 307, 308]);
var defaultMapRouteProperties = (route) => ({
hasErrorBoundary: Boolean(route.hasErrorBoundary)
});
var ResetLoaderDataSymbol = Symbol("ResetLoaderData");
function createStaticHandler(routes, opts) {
invariant(
routes.length > 0,
"You must provide a non-empty routes array to createStaticHandler"
);
let manifest = {};
let basename = (opts ? opts.basename : null) || "/";
let _mapRouteProperties = opts?.mapRouteProperties || defaultMapRouteProperties;
let mapRouteProperties = _mapRouteProperties;
({
...opts?.future
});
if (opts?.instrumentations) {
let instrumentations = opts.instrumentations;
mapRouteProperties = (route) => {
return {
..._mapRouteProperties(route),
...getRouteInstrumentationUpdates(
instrumentations.map((i) => i.route).filter(Boolean),
route
)
};
};
}
let dataRoutes = convertRoutesToDataRoutes(
routes,
mapRouteProperties,
void 0,
manifest
);
let routeBranches = flattenAndRankRoutes(dataRoutes);
async function query(request, {
requestContext,
filterMatchesToLoad,
skipLoaderErrorBubbling,
skipRevalidation,
dataStrategy,
generateMiddlewareResponse,
normalizePath
} = {}) {
let normalizePathImpl = normalizePath || defaultNormalizePath;
let method = request.method;
let location = createLocation(
"",
normalizePathImpl(request),
null,
"default"
);
let matches = matchRoutesImpl(
dataRoutes,
location,
basename,
false,
routeBranches
);
requestContext = requestContext != null ? requestContext : new RouterContextProvider();
if (!isValidMethod(method) && method !== "HEAD") {
let error = getInternalRouterError(405, { method });
let { matches: methodNotAllowedMatches, route } = getShortCircuitMatches(dataRoutes);
let staticContext = {
basename,
location,
matches: methodNotAllowedMatches,
loaderData: {},
actionData: null,
errors: {
[route.id]: error
},
statusCode: error.status,
loaderHeaders: {},
actionHeaders: {}
};
return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
} else if (!matches) {
let error = getInternalRouterError(404, { pathname: location.pathname });
let { matches: notFoundMatches, route } = getShortCircuitMatches(dataRoutes);
let staticContext = {
basename,
location,
matches: notFoundMatches,
loaderData: {},
actionData: null,
errors: {
[route.id]: error
},
statusCode: error.status,
loaderHeaders: {},
actionHeaders: {}
};
return generateMiddlewareResponse ? generateMiddlewareResponse(() => Promise.resolve(staticContext)) : staticContext;
}
if (generateMiddlewareResponse) {
invariant(
requestContext instanceof RouterContextProvider,
"When using middleware in `staticHandler.query()`, any provided `requestContext` must be an instance of `RouterContextProvider`"
);
try {
await loadLazyMiddlewareForMatches(
matches,
manifest,
mapRouteProperties
);
let renderedStaticContext;
let response = await runServerMiddlewarePipeline(
{
request,
url: createDataFunctionUrl(request, location),
pattern: getRoutePattern(matches),
matches,
params: matches[0].params,
// If we're calling middleware then it must be enabled so we can cast
// this to the proper type knowing it's not an `AppLoadContext`
context: requestContext
},
async () => {
let res = await generateMiddlewareResponse(
async (revalidationRequest, opts2 = {}) => {
let result2 = await queryImpl(
revalidationRequest,
location,
matches,
requestContext,
dataStrategy || null,
skipLoaderErrorBubbling === true,
null,
"filterMatchesToLoad" in opts2 ? opts2.filterMatchesToLoad ?? null : filterMatchesToLoad ?? null,
skipRevalidation === true
);
if (isResponse(result2)) {
return result2;
}
renderedStaticContext = { location, basename, ...result2 };
return renderedStaticContext;
}
);
return res;
},
async (error, routeId) => {
if (isRedirectResponse(error)) {
return error;
}
if (isResponse(error)) {
try {
error = new ErrorResponseImpl(
error.status,
error.statusText,
await parseResponseBody(error)
);
} catch (e) {
error = e;
}
}
if (isDataWithResponseInit(error)) {
error = dataWithResponseInitToErrorResponse(error);
}
if (renderedStaticContext) {
if (routeId in renderedStaticContext.loaderData) {
renderedStaticContext.loaderData[routeId] = void 0;
}
let staticContext = getStaticContextFromError(
dataRoutes,
renderedStaticContext,
error,
skipLoaderErrorBubbling ? routeId : findNearestBoundary(matches, routeId).route.id
);
return generateMiddlewareResponse(
() => Promise.resolve(staticContext)
);
} else {
let boundaryRouteId = skipLoaderErrorBubbling ? routeId : findNearestBoundary(
matches,
matches.find(
(m) => m.route.id === routeId || m.route.loader
)?.route.id || routeId
).route.id;
let staticContext = {
matches,
location,
basename,
loaderData: {},
actionData: null,
errors: {
[boundaryRouteId]: error
},
statusCode: isRouteErrorResponse(error) ? error.status : 500,
actionHeaders: {},
loaderHeaders: {}
};
return generateMiddlewareResponse(
() => Promise.resolve(staticContext)
);
}
}
);
invariant(isResponse(response), "Expected a response in query()");
return response;
} catch (e) {
if (isResponse(e)) {
return e;
}
throw e;
}
}
let result = await queryImpl(
request,
location,
matches,
requestContext,
dataStrategy || null,
skipLoaderErrorBubbling === true,
null,
filterMatchesToLoad || null,
skipRevalidation === true
);
if (isResponse(result)) {
return result;
}
return { location, basename, ...result };
}
async function queryRoute(request, {
routeId,
requestContext,
dataStrategy,
generateMiddlewareResponse,
normalizePath
} = {}) {
let normalizePathImpl = normalizePath || defaultNormalizePath;
let method = request.method;
let location = createLocation(
"",
normalizePathImpl(request),
null,
"default"
);
let matches = matchRoutesImpl(
dataRoutes,
location,
basename,
false,
routeBranches
);
requestContext = requestContext != null ? requestContext : new RouterContextProvider();
if (!isValidMethod(method) && method !== "HEAD" && method !== "OPTIONS") {
throw getInternalRouterError(405, { method });
} else if (!matches) {
throw getInternalRouterError(404, { pathname: location.pathname });
}
let match = routeId ? matches.find((m) => m.route.id === routeId) : getTargetMatch(matches, location);
if (routeId && !match) {
throw getInternalRouterError(403, {
pathname: location.pathname,
routeId
});
} else if (!match) {
throw getInternalRouterError(404, { pathname: location.pathname });
}
if (generateMiddlewareResponse) {
invariant(
requestContext instanceof RouterContextProvider,
"When using middleware in `staticHandler.queryRoute()`, any provided `requestContext` must be an instance of `RouterContextProvider`"
);
await loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties);
let response = await runServerMiddlewarePipeline(
{
request,
url: createDataFunctionUrl(request, location),
pattern: getRoutePattern(matches),
matches,
params: matches[0].params,
// If we're calling middleware then it must be enabled so we can cast
// this to the proper type knowing it's not an `AppLoadContext`
context: requestContext
},
async () => {
let res = await generateMiddlewareResponse(
async (innerRequest) => {
let result2 = await queryImpl(
innerRequest,
location,
matches,
requestContext,
dataStrategy || null,
false,
match,
null,
false
);
let processed = handleQueryResult(result2);
return isResponse(processed) ? processed : typeof processed === "string" ? new Response(processed) : Response.json(processed);
}
);
return res;
},
(error) => {
if (isDataWithResponseInit(error)) {
return Promise.resolve(dataWithResponseInitToResponse(error));
}
if (isResponse(error)) {
return Promise.resolve(error);
}
throw error;
}
);
return response;
}
let result = await queryImpl(
request,
location,
matches,
requestContext,
dataStrategy || null,
false,
match,
null,
false
);
return handleQueryResult(result);
function handleQueryResult(result2) {
if (isResponse(result2)) {
return result2;
}
let error = result2.errors ? Object.values(result2.errors)[0] : void 0;
if (error !== void 0) {
throw error;
}
if (result2.actionData) {
return Object.values(result2.actionData)[0];
}
if (result2.loaderData) {
return Object.values(result2.loaderData)[0];
}
return void 0;
}
}
async function queryImpl(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, skipRevalidation) {
invariant(
request.signal,
"query()/queryRoute() requests must contain an AbortController signal"
);
try {
if (isMutationMethod(request.method)) {
let result2 = await submit(
request,
location,
matches,
routeMatch || getTargetMatch(matches, location),
requestContext,
dataStrategy,
skipLoaderErrorBubbling,
routeMatch != null,
filterMatchesToLoad,
skipRevalidation
);
return result2;
}
let result = await loadRouteData(
request,
location,
matches,
requestContext,
dataStrategy,
skipLoaderErrorBubbling,
routeMatch,
filterMatchesToLoad
);
return isResponse(result) ? result : {
...result,
actionData: null,
actionHeaders: {}
};
} catch (e) {
if (isDataStrategyResult(e) && isResponse(e.result)) {
if (e.type === "error" /* error */) {
throw e.result;
}
return e.result;
}
if (isRedirectResponse(e)) {
return e;
}
throw e;
}
}
async function submit(request, location, matches, actionMatch, requestContext, dataStrategy, skipLoaderErrorBubbling, isRouteRequest, filterMatchesToLoad, skipRevalidation) {
let result;
if (!actionMatch.route.action && !actionMatch.route.lazy) {
let error = getInternalRouterError(405, {
method: request.method,
pathname: new URL(request.url).pathname,
routeId: actionMatch.route.id
});
if (isRouteRequest) {
throw error;
}
result = {
type: "error" /* error */,
error
};
} else {
let dsMatches = getTargetedDataStrategyMatches(
mapRouteProperties,
manifest,
request,
location,
matches,
actionMatch,
[],
requestContext
);
let results = await callDataStrategy(
request,
location,
dsMatches,
isRouteRequest,
requestContext,
dataStrategy
);
result = results[actionMatch.route.id];
if (request.signal.aborted) {
throwStaticHandlerAbortedError(request, isRouteRequest);
}
}
if (isRedirectResult(result)) {
throw new Response(null, {
status: result.response.status,
headers: {
Location: result.response.headers.get("Location")
}
});
}
if (isRouteRequest) {
if (isErrorResult(result)) {
throw result.error;
}
return {
matches: [actionMatch],
loaderData: {},
actionData: { [actionMatch.route.id]: result.data },
errors: null,
// Note: statusCode + headers are unused here since queryRoute will
// return the raw Response or value
statusCode: 200,
loaderHeaders: {},
actionHeaders: {}
};
}
if (skipRevalidation) {
if (isErrorResult(result)) {
let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
return {
statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
actionData: null,
actionHeaders: {
...result.headers ? { [actionMatch.route.id]: result.headers } : {}
},
matches,
loaderData: {},
errors: {
[boundaryMatch.route.id]: result.error
},
loaderHeaders: {}
};
} else {
return {
actionData: {
[actionMatch.route.id]: result.data
},
actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {},
matches,
loaderData: {},
errors: null,
statusCode: result.statusCode || 200,
loaderHeaders: {}
};
}
}
let loaderRequest = new Request(request.url, {
headers: request.headers,
redirect: request.redirect,
signal: request.signal
});
if (isErrorResult(result)) {
let boundaryMatch = skipLoaderErrorBubbling ? actionMatch : findNearestBoundary(matches, actionMatch.route.id);
let handlerContext2 = await loadRouteData(
loaderRequest,
location,
matches,
requestContext,
dataStrategy,
skipLoaderErrorBubbling,
null,
filterMatchesToLoad,
[boundaryMatch.route.id, result]
);
return {
...handlerContext2,
statusCode: isRouteErrorResponse(result.error) ? result.error.status : result.statusCode != null ? result.statusCode : 500,
actionData: null,
actionHeaders: {
...result.headers ? { [actionMatch.route.id]: result.headers } : {}
}
};
}
let handlerContext = await loadRouteData(
loaderRequest,
location,
matches,
requestContext,
dataStrategy,
skipLoaderErrorBubbling,
null,
filterMatchesToLoad
);
return {
...handlerContext,
actionData: {
[actionMatch.route.id]: result.data
},
// action status codes take precedence over loader status codes
...result.statusCode ? { statusCode: result.statusCode } : {},
actionHeaders: result.headers ? { [actionMatch.route.id]: result.headers } : {}
};
}
async function loadRouteData(request, location, matches, requestContext, dataStrategy, skipLoaderErrorBubbling, routeMatch, filterMatchesToLoad, pendingActionResult) {
let isRouteRequest = routeMatch != null;
if (isRouteRequest && !routeMatch?.route.loader && !routeMatch?.route.lazy) {
throw getInternalRouterError(400, {
method: request.method,
pathname: new URL(request.url).pathname,
routeId: routeMatch?.route.id
});
}
let dsMatches;
if (routeMatch) {
dsMatches = getTargetedDataStrategyMatches(
mapRouteProperties,
manifest,
request,
location,
matches,
routeMatch,
[],
requestContext
);
} else {
let maxIdx = pendingActionResult && isErrorResult(pendingActionResult[1]) ? (
// Up to but not including the boundary
matches.findIndex((m) => m.route.id === pendingActionResult[0]) - 1
) : void 0;
let pattern = getRoutePattern(matches);
dsMatches = matches.map((match, index) => {
if (maxIdx != null && index > maxIdx) {
return getDataStrategyMatch(
mapRouteProperties,
manifest,
request,
location,
pattern,
match,
[],
requestContext,
false
);
}
return getDataStrategyMatch(
mapRouteProperties,
manifest,
request,
location,
pattern,
match,
[],
requestContext,
(match.route.loader || match.route.lazy) != null && (!filterMatchesToLoad || filterMatchesToLoad(match))
);
});
}
if (!dataStrategy && !dsMatches.some((m) => m.shouldLoad)) {
return {
matches,
loaderData: {},
errors: pendingActionResult && isErrorResult(pendingActionResult[1]) ? {
[pendingActionResult[0]]: pendingActionResult[1].error
} : null,
statusCode: 200,
loaderHeaders: {}
};
}
let results = await callDataStrategy(
request,
location,
dsMatches,
isRouteRequest,
requestContext,
dataStrategy
);
if (request.signal.aborted) {
throwStaticHandlerAbortedError(request, isRouteRequest);
}
let handlerContext = processRouteLoaderData(
matches,
results,
pendingActionResult,
true,
skipLoaderErrorBubbling
);
return {
...handlerContext,
matches
};
}
async function callDataStrategy(request, location, matches, isRouteRequest, requestContext, dataStrategy) {
let results = await callDataStrategyImpl(
dataStrategy || defaultDataStrategy,
request,
location,
matches,
null,
requestContext);
let dataResults = {};
await Promise.all(
matches.map(async (match) => {
if (!(match.route.id in results)) {
return;
}
let result = results[match.route.id];
if (isRedirectDataStrategyResult(result)) {
let response = result.result;
throw normalizeRelativeRoutingRedirectResponse(
response,
request,
match.route.id,
matches,
basename
);
}
if (isRouteRequest) {
if (isResponse(result.result)) {
throw result;
} else if (isDataWithResponseInit(result.result)) {
throw dataWithResponseInitToResponse(result.result);
}
}
dataResults[match.route.id] = await convertDataStrategyResultToDataResult(result);
})
);
return dataResults;
}
return {
dataRoutes,
_internalRouteBranches: routeBranches,
query,
queryRoute
};
}
function getStaticContextFromError(routes, handlerContext, error, boundaryId) {
let errorBoundaryId = boundaryId || handlerContext._deepestRenderedBoundaryId || routes[0].id;
return {
...handlerContext,
statusCode: isRouteErrorResponse(error) ? error.status : 500,
errors: {
[errorBoundaryId]: error
}
};
}
function throwStaticHandlerAbortedError(request, isRouteRequest) {
if (request.signal.reason !== void 0) {
throw request.signal.reason;
}
let method = isRouteRequest ? "queryRoute" : "query";
throw new Error(
`${method}() call aborted without an \`AbortSignal.reason\`: ${request.method} ${request.url}`
);
}
function defaultNormalizePath(request) {
let url = new URL(request.url);
return {
pathname: url.pathname,
search: url.search,
hash: url.hash
};
}
function normalizeTo(location, matches, basename, to, fromRouteId, relative) {
let contextualMatches;
let activeRouteMatch;
{
contextualMatches = matches;
activeRouteMatch = matches[matches.length - 1];
}
let path = resolveTo(
to ? to : ".",
getResolveToMatches(contextualMatches),
stripBasename(location.pathname, basename) || location.pathname,
relative === "path"
);
if (to == null) {
path.search = location.search;
path.hash = location.hash;
}
if ((to == null || to === "" || to === ".") && activeRouteMatch) {
let nakedIndex = hasNakedIndexQuery(path.search);
if (activeRouteMatch.route.index && !nakedIndex) {
path.search = path.search ? path.search.replace(/^\?/, "?index&") : "?index";
} else if (!activeRouteMatch.route.index && nakedIndex) {
let params = new URLSearchParams(path.search);
let indexValues = params.getAll("index");
params.delete("index");
indexValues.filter((v) => v).forEach((v) => params.append("index", v));
let qs = params.toString();
path.search = qs ? `?${qs}` : "";
}
}
if (basename !== "/") {
path.pathname = prependBasename({ basename, pathname: path.pathname });
}
return createPath(path);
}
function shouldRevalidateLoader(loaderMatch, arg) {
if (loaderMatch.route.shouldRevalidate) {
let routeChoice = loaderMatch.route.shouldRevalidate(arg);
if (typeof routeChoice === "boolean") {
return routeChoice;
}
}
return arg.defaultShouldRevalidate;
}
var lazyRoutePropertyCache = /* @__PURE__ */ new WeakMap();
var loadLazyRouteProperty = ({
key,
route,
manifest,
mapRouteProperties
}) => {
let routeToUpdate = manifest[route.id];
invariant(routeToUpdate, "No route found in manifest");
if (!routeToUpdate.lazy || typeof routeToUpdate.lazy !== "object") {
return;
}
let lazyFn = routeToUpdate.lazy[key];
if (!lazyFn) {
return;
}
let cache2 = lazyRoutePropertyCache.get(routeToUpdate);
if (!cache2) {
cache2 = {};
lazyRoutePropertyCache.set(routeToUpdate, cache2);
}
let cachedPromise = cache2[key];
if (cachedPromise) {
return cachedPromise;
}
let propertyPromise = (async () => {
let isUnsupported = isUnsupportedLazyRouteObjectKey(key);
let staticRouteValue = routeToUpdate[key];
let isStaticallyDefined = staticRouteValue !== void 0 && key !== "hasErrorBoundary";
if (isUnsupported) {
warning(
!isUnsupported,
"Route property " + key + " is not a supported lazy route property. This property will be ignored."
);
cache2[key] = Promise.resolve();
} else if (isStaticallyDefined) {
warning(
false,
`Route "${routeToUpdate.id}" has a static property "${key}" defined. The lazy property will be ignored.`
);
} else {
let value = await lazyFn();
if (value != null) {
Object.assign(routeToUpdate, { [key]: value });
Object.assign(routeToUpdate, mapRouteProperties(routeToUpdate));
}
}
if (typeof routeToUpdate.lazy === "object") {
routeToUpdate.lazy[key] = void 0;
if (Object.values(routeToUpdate.lazy).every((value) => value === void 0)) {
routeToUpdate.lazy = void 0;
}
}
})();
cache2[key] = propertyPromise;
return propertyPromise;
};
var lazyRouteFunctionCache = /* @__PURE__ */ new WeakMap();
function loadLazyRoute(route, type, manifest, mapRouteProperties, lazyRoutePropertiesToSkip) {
let routeToUpdate = manifest[route.id];
invariant(routeToUpdate, "No route found in manifest");
if (!route.lazy) {
return {
lazyRoutePromise: void 0,
lazyHandlerPromise: void 0
};
}
if (typeof route.lazy === "function") {
let cachedPromise = lazyRouteFunctionCache.get(routeToUpdate);
if (cachedPromise) {
return {
lazyRoutePromise: cachedPromise,
lazyHandlerPromise: cachedPromise
};
}
let lazyRoutePromise2 = (async () => {
invariant(
typeof route.lazy === "function",
"No lazy route function found"
);
let lazyRoute = await route.lazy();
let routeUpdates = {};
for (let lazyRouteProperty in lazyRoute) {
let lazyValue = lazyRoute[lazyRouteProperty];
if (lazyValue === void 0) {
continue;
}
let isUnsupported = isUnsupportedLazyRouteFunctionKey(lazyRouteProperty);
let staticRouteValue = routeToUpdate[lazyRouteProperty];
let isStaticallyDefined = staticRouteValue !== void 0 && // This property isn't static since it should always be updated based
// on the route updates
lazyRouteProperty !== "hasErrorBoundary";
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."
);
} 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.`
);
} else {
routeUpdates[lazyRouteProperty] = lazyValue;
}
}
Object.assign(routeToUpdate, routeUpdates);
Object.assign(routeToUpdate, {
// To keep things framework agnostic, we use the provided `mapRouteProperties`
// function to set the framework-aware properties (`element`/`hasErrorBoundary`)
// since the logic will differ between frameworks.
...mapRouteProperties(routeToUpdate),
lazy: void 0
});
})();
lazyRouteFunctionCache.set(routeToUpdate, lazyRoutePromise2);
lazyRoutePromise2.catch(() => {
});
return {
lazyRoutePromise: lazyRoutePromise2,
lazyHandlerPromise: lazyRoutePromise2
};
}
let lazyKeys = Object.keys(route.lazy);
let lazyPropertyPromises = [];
let lazyHandlerPromise = void 0;
for (let key of lazyKeys) {
if (lazyRoutePropertiesToSkip && lazyRoutePropertiesToSkip.includes(key)) {
continue;
}
let promise = loadLazyRouteProperty({
key,
route,
manifest,
mapRouteProperties
});
if (promise) {
lazyPropertyPromises.push(promise);
if (key === type) {
lazyHandlerPromise = promise;
}
}
}
let lazyRoutePromise = lazyPropertyPromises.length > 0 ? Promise.all(lazyPropertyPromises).then(() => {
}) : void 0;
lazyRoutePromise?.catch(() => {
});
lazyHandlerPromise?.catch(() => {
});
return {
lazyRoutePromise,
lazyHandlerPromise
};
}
function isNonNullable(value) {
return value !== void 0;
}
function loadLazyMiddlewareForMatches(matches, manifest, mapRouteProperties) {
let promises = matches.map(({ route }) => {
if (typeof route.lazy !== "object" || !route.lazy.middleware) {
return void 0;
}
return loadLazyRouteProperty({
key: "middleware",
route,
manifest,
mapRouteProperties
});
}).filter(isNonNullable);
return promises.length > 0 ? Promise.all(promises) : void 0;
}
async function defaultDataStrategy(args) {
let matchesToLoad = args.matches.filter((m) => m.shouldLoad);
let keyedResults = {};
let results = await Promise.all(matchesToLoad.map((m) => m.resolve()));
results.forEach((result, i) => {
keyedResults[matchesToLoad[i].route.id] = result;
});
return keyedResults;
}
function runServerMiddlewarePipeline(args, handler, errorHandler) {
return runMiddlewarePipeline(
args,
handler,
processResult,
isResponse,
errorHandler
);
function processResult(result) {
return isDataWithResponseInit(result) ? dataWithResponseInitToResponse(result) : result;
}
}
async function runMiddlewarePipeline(args, handler, processResult, isResult, errorHandler) {
let { matches, ...dataFnArgs } = args;
let tuples = matches.flatMap(
(m) => m.route.middleware ? m.route.middleware.map((fn) => [m.route.id, fn]) : []
);
let result = await callRouteMiddleware(
dataFnArgs,
tuples,
handler,
processResult,
isResult,
errorHandler
);
return result;
}
async function callRouteMiddleware(args, middlewares, handler, processResult, isResult, errorHandler, idx = 0) {
let { request } = args;
if (request.signal.aborted) {
throw request.signal.reason ?? new Error(`Request aborted: ${request.method} ${request.url}`);
}
let tuple = middlewares[idx];
if (!tuple) {
let result = await handler();
return result;
}
let [routeId, middleware] = tuple;
let nextResult;
let next = async () => {
if (nextResult) {
throw new Error("You may only call `next()` once per middleware");
}
try {
let result = await callRouteMiddleware(
args,
middlewares,
handler,
processResult,
isResult,
errorHandler,
idx + 1
);
nextResult = { value: result };
return nextResult.value;
} catch (error) {
nextResult = { value: await errorHandler(error, routeId, nextResult) };
return nextResult.value;
}
};
try {
let value = await middleware(args, next);
let result = value != null ? processResult(value) : void 0;
if (isResult(result)) {
return result;
} else if (nextResult) {
return result ?? nextResult.value;
} else {
nextResult = { value: await next() };
return nextResult.value;
}
} catch (error) {
let response = await errorHandler(error, routeId, nextResult);
return response;
}
}
function getDataStrategyMatchLazyPromises(mapRouteProperties, manifest, request, match, lazyRoutePropertiesToSkip) {
let lazyMiddlewarePromise = loadLazyRouteProperty({
key: "middleware",
route: match.route,
manifest,
mapRouteProperties
});
let lazyRoutePromises = loadLazyRoute(
match.route,
isMutationMethod(request.method) ? "action" : "loader",
manifest,
mapRouteProperties,
lazyRoutePropertiesToSkip
);
return {
middleware: lazyMiddlewarePromise,
route: lazyRoutePromises.lazyRoutePromise,
handler: lazyRoutePromises.lazyHandlerPromise
};
}
function getDataStrategyMatch(mapRouteProperties, manifest, request, path, pattern, match, lazyRoutePropertiesToSkip, scopedContext, shouldLoad, shouldRevalidateArgs = null, callSiteDefaultShouldRevalidate) {
let isUsingNewApi = false;
let _lazyPromises = getDataStrategyMatchLazyPromises(
mapRouteProperties,
manifest,
request,
match,
lazyRoutePropertiesToSkip
);
return {
...match,
_lazyPromises,
shouldLoad,
shouldRevalidateArgs,
shouldCallHandler(defaultShouldRevalidate) {
isUsingNewApi = true;
if (!shouldRevalidateArgs) {
return shouldLoad;
}
if (typeof defaultShouldRevalidate === "boolean") {
return shouldRevalidateLoader(match, {
...shouldRevalidateArgs,
defaultShouldRevalidate
});
}
return shouldRevalidateLoader(match, shouldRevalidateArgs);
},
resolve(handlerOverride) {
let { lazy, loader, middleware } = match.route;
let callHandler = isUsingNewApi || shouldLoad || handlerOverride && !isMutationMethod(request.method) && (lazy || loader);
let isMiddlewareOnlyRoute = middleware && middleware.length > 0 && !loader && !lazy;
if (callHandler && (isMutationMethod(request.method) || !isMiddlewareOnlyRoute)) {
return callLoaderOrAction({
request,
path,
pattern,
match,
lazyHandlerPromise: _lazyPromises?.handler,
lazyRoutePromise: _lazyPromises?.route,
handlerOverride,
scopedContext
});
}
return Promise.resolve({ type: "data" /* data */, result: void 0 });
}
};
}
function getTargetedDataStrategyMatches(mapRouteProperties, manifest, request, path, matches, targetMatch, lazyRoutePropertiesToSkip, scopedContext, shouldRevalidateArgs = null) {
return matches.map((match) => {
if (match.route.id !== targetMatch.route.id) {
return {
...match,
shouldLoad: false,
shouldRevalidateArgs,
shouldCallHandler: () => false,
_lazyPromises: getDataStrategyMatchLazyPromises(
mapRouteProperties,
manifest,
request,
match,
lazyRoutePropertiesToSkip
),
resolve: () => Promise.resolve({ type: "data", result: void 0 })
};
}
return getDataStrategyMatch(
mapRouteProperties,
manifest,
request,
path,
getRoutePattern(matches),
match,
lazyRoutePropertiesToSkip,
scopedContext,
true,
shouldRevalidateArgs
);
});
}
async function callDataStrategyImpl(dataStrategyImpl, request, path, matches, fetcherKey, scopedContext, isStaticHandler) {
if (matches.some((m) => m._lazyPromises?.middleware)) {
await Promise.all(matches.map((m) => m._lazyPromises?.middleware));
}
let dataStrategyArgs = {
request,
url: createDataFunctionUrl(request, path),
pattern: getRoutePattern(matches),
params: matches[0].params,
context: scopedContext,
matches
};
let runClientMiddleware = () => {
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`"
);
} ;
let results = await dataStrategyImpl({
...dataStrategyArgs,
fetcherKey,
runClientMiddleware
});
try {
await Promise.all(
matches.flatMap((m) => [
m._lazyPromises?.handler,
m._lazyPromises?.route
])
);
} catch (e) {
}
return results;
}
async function callLoaderOrAction({
request,
path,
pattern,
match,
lazyHandlerPromise,
lazyRoutePromise,
handlerOverride,
scopedContext
}) {
let result;
let onReject;
let isAction = isMutationMethod(request.method);
let type = isAction ? "action" : "loader";
let runHandler = (handler) => {
let reject;
let abortPromise = new Promise((_, r) => reject = r);
onReject = () => reject();
request.signal.addEventListener("abort", onReject);
let actualHandler = (ctx) => {
if (typeof handler !== "function") {
return Promise.reject(
new Error(
`You cannot call the handler for a route which defines a boolean "${type}" [routeId: ${match.route.id}]`
)
);
}
return handler(
{
request,
url: createDataFunctionUrl(request, path),
pattern,
params: match.params,
context: scopedContext
},
...ctx !== void 0 ? [ctx] : []
);
};
let handlerPromise = (async () => {
try {
let val = await (handlerOverride ? handlerOverride((ctx) => actualHandler(ctx)) : actualHandler());
return { type: "data", result: val };
} catch (e) {
return { type: "error", result: e };
}
})();
return Promise.race([handlerPromise, abortPromise]);
};
try {
let handler = isAction ? match.route.action : match.route.loader;
if (lazyHandlerPromise || lazyRoutePromise) {
if (handler) {
let handlerError;
let [value] = await Promise.all([
// If the handler throws, don't let it immediately bubble out,
// since we need to let the lazy() execution finish so we know if this
// route has a boundary that can handle the error
runHandler(handler).catch((e) => {
handlerError = e;
}),
// Ensure all lazy route promises are resolved before continuing
lazyHandlerPromise,
lazyRoutePromise
]);
if (handlerError !== void 0) {
throw handlerError;
}
result = value;
} else {
await lazyHandlerPromise;
let handler2 = isAction ? match.route.action : match.route.loader;
if (handler2) {
[result] = await Promise.all([runHandler(handler2), lazyRoutePromise]);
} else if (type === "action") {
let url = new URL(request.url);
let pathname = url.pathname + url.search;
throw getInternalRouterError(405, {
method: request.method,
pathname,
routeId: match.route.id
});
} else {
return { type: "data" /* data */, result: void 0 };
}
}
} else if (!handler) {
let url = new URL(request.url);
let pathname = url.pathname + url.search;
throw getInternalRouterError(404, {
pathname
});
} else {
result = await runHandler(handler);
}
} catch (e) {
return { type: "error" /* error */, result: e };
} finally {
if (onReject) {
request.signal.removeEventListener("abort", onReject);
}
}
return result;
}
async function parseResponseBody(response) {
let contentType = response.headers.get("Content-Type");
if (contentType && /\bapplication\/json\b/.test(contentType)) {
return response.body == null ? null : response.json();
}
return response.text();
}
async function convertDataStrategyResultToDataResult(dataStrategyResult) {
let { result, type } = dataStrategyResult;
if (isResponse(result)) {
let data2;
try {
data2 = await parseResponseBody(result);
} catch (e) {
return { type: "error" /* error */, error: e };
}
if (type === "error" /* error */) {
return {
type: "error" /* error */,
error: new ErrorResponseImpl(result.status, result.statusText, data2),
statusCode: result.status,
headers: result.headers
};
}
return {
type: "data" /* data */,
data: data2,
statusCode: result.status,
headers: result.headers
};
}
if (type === "error" /* error */) {
if (isDataWithResponseInit(result)) {
if (result.data instanceof Error) {
return {
type: "error" /* error */,
error: result.data,
statusCode: result.init?.status,
headers: result.init?.headers ? new Headers(result.init.headers) : void 0
};
}
return {
type: "error" /* error */,
error: dataWithResponseInitToErrorResponse(result),
statusCode: isRouteErrorResponse(result) ? result.status : void 0,
headers: result.init?.headers ? new Headers(result.init.headers) : void 0
};
}
return {
type: "error" /* error */,
error: result,
statusCode: isRouteErrorResponse(result) ? result.status : void 0
};
}
if (isDataWithResponseInit(result)) {
return {
type: "data" /* data */,
data: result.data,
statusCode: result.init?.status,
headers: result.init?.headers ? new Headers(result.init.headers) : void 0
};
}
return { type: "data" /* data */, data: result };
}
function normalizeRelativeRoutingRedirectResponse(response, request, routeId, matches, basename) {
let location = response.headers.get("Location");
invariant(
location,
"Redirects returned/thrown from loaders/actions must have a Location header"
);
if (!isAbsoluteUrl(location)) {
let trimmedMatches = matches.slice(
0,
matches.findIndex((m) => m.route.id === routeId) + 1
);
location = normalizeTo(
new URL(request.url),
trimmedMatches,
basename,
location
);
response.headers.set("Location", location);
}
return response;
}
function createDataFunctionUrl(request, path) {
let url = new URL(request.url);
let parsed = typeof path === "string" ? parsePath(path) : path;
url.pathname = parsed.pathname || "/";
if (parsed.search) {
let searchParams = new URLSearchParams(parsed.search);
let indexValues = searchParams.getAll("index");
searchParams.delete("index");
for (let value of indexValues.filter(Boolean)) {
searchParams.append("index", value);
}
url.search = searchParams.size ? `?${searchParams.toString()}` : "";
} else {
url.search = "";
}
url.hash = parsed.hash || "";
return url;
}
function processRouteLoaderData(matches, results, pendingActionResult, isStaticHandler = false, skipLoaderErrorBubbling = false) {
let loaderData = {};
let errors = null;
let statusCode;
let foundError = false;
let loaderHeaders = {};
let pendingError = pendingActionResult && isErrorResult(pendingActionResult[1]) ? pendingActionResult[1].error : void 0;
matches.forEach((match) => {
if (!(match.route.id in results)) {
return;
}
let id = match.route.id;
let result = results[id];
invariant(
!isRedirectResult(result),
"Cannot handle redirect results in processLoaderData"
);
if (isErrorResult(result)) {
let error = result.error;
if (pendingError !== void 0) {
error = pendingError;
pendingError = void 0;
}
errors = errors || {};
if (skipLoaderErrorBubbling) {
errors[id] = error;
} else {
let boundaryMatch = findNearestBoundary(matches, id);
if (errors[boundaryMatch.route.id] == null) {
errors[boundaryMatch.route.id] = error;
}
}
if (!isStaticHandler) {
loaderData[id] = ResetLoaderDataSymbol;
}
if (!foundError) {
foundError = true;
statusCode = isRouteErrorResponse(result.error) ? result.error.status : 500;
}
if (result.headers) {
loaderHeaders[id] = result.headers;
}
} else {
loaderData[id] = result.data;
if (result.statusCode && result.statusCode !== 200 && !foundError) {
statusCode = result.statusCode;
}
if (result.headers) {
loaderHeaders[id] = result.headers;
}
}
});
if (pendingError !== void 0 && pendingActionResult) {
errors = { [pendingActionResult[0]]: pendingError };
if (pendingActionResult[2]) {
loaderData[pendingActionResult[2]] = void 0;
}
}
return {
loaderData,
errors,
statusCode: statusCode || 200,
loaderHeaders
};
}
function findNearestBoundary(matches, routeId) {
let eligibleMatches = routeId ? matches.slice(0, matches.findIndex((m) => m.route.id === routeId) + 1) : [...matches];
return eligibleMatches.reverse().find((m) => m.route.hasErrorBoundary === true) || matches[0];
}
function getShortCircuitMatches(routes) {
let route = routes.length === 1 ? routes[0] : routes.find((r) => r.index || !r.path || r.path === "/") || {
id: `__shim-error-route__`
};
return {
matches: [
{
params: {},
pathname: "",
pathnameBase: "",
route
}
],
route
};
}
function getInternalRouterError(status, {
pathname,
routeId,
method,
type,
message
} = {}) {
let statusText = "Unknown Server Error";
let errorMessage = "Unknown @remix-run/router error";
if (status === 400) {
statusText = "Bad Request";
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.`;
} else if (type === "invalid-body") {
errorMessage = "Unable to encode submission body";
}
} else if (status === 403) {
statusText = "Forbidden";
errorMessage = `Route "${routeId}" does not match URL "${pathname}"`;
} else if (status === 404) {
statusText = "Not Found";
errorMessage = `No route matches URL "${pathname}"`;
} else if (status === 405) {
statusText = "Method Not Allowed";
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.`;
} else if (method) {
errorMessage = `Invalid request method "${method.toUpperCase()}"`;
}
}
return new ErrorResponseImpl(
status || 500,
statusText,
new Error(errorMessage),
true
);
}
function dataWithResponseInitToResponse(data2) {
return Response.json(data2.data, data2.init ?? void 0);
}
function dataWithResponseInitToErrorResponse(data2) {
return new ErrorResponseImpl(
data2.init?.status ?? 500,
data2.init?.statusText ?? "Internal Server Error",
data2.data
);
}
function isDataStrategyResult(result) {
return result != null && typeof result === "object" && "type" in result && "result" in result && (result.type === "data" /* data */ || result.type === "error" /* error */);
}
function isRedirectDataStrategyResult(result) {
return isResponse(result.result) && redirectStatusCodes.has(result.result.status);
}
function isErrorResult(result) {
return result.type === "error" /* error */;
}
function isRedirectResult(result) {
return (result && result.type) === "redirect" /* redirect */;
}
function isDataWithResponseInit(value) {
return typeof value === "object" && value != null && "type" in value && "data" in value && "init" in value && value.type === "DataWithResponseInit";
}
function isResponse(value) {
return value != null && typeof value.status === "number" && typeof value.statusText === "string" && typeof value.headers === "object" && typeof value.body !== "undefined";
}
function isRedirectStatusCode(statusCode) {
return redirectStatusCodes.has(statusCode);
}
function isRedirectResponse(result) {
return isResponse(result) && isRedirectStatusCode(result.status) && result.headers.has("Location");
}
function isValidMethod(method) {
return validRequestMethods.has(method.toUpperCase());
}
function isMutationMethod(method) {
return validMutationMethods.has(method.toUpperCase());
}
function hasNakedIndexQuery(search) {
return new URLSearchParams(search).getAll("index").some((v) => v === "");
}
function getTargetMatch(matches, location) {
let search = typeof location === "string" ? parsePath(location).search : location.search;
if (matches[matches.length - 1].route.index && hasNakedIndexQuery(search || "")) {
return matches[matches.length - 1];
}
let pathMatches = getPathContributingMatches(matches);
return pathMatches[pathMatches.length - 1];
}
// lib/server-runtime/invariant.ts
function invariant2(value, message) {
if (value === false || value === null || typeof value === "undefined") {
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"
);
throw new Error(message);
}
}
// lib/server-runtime/headers.ts
function getDocumentHeadersImpl(context, getRouteHeadersFn, _defaultHeaders) {
let boundaryIdx = context.errors ? context.matches.findIndex((m) => context.errors[m.route.id]) : -1;
let matches = boundaryIdx >= 0 ? context.matches.slice(0, boundaryIdx + 1) : context.matches;
let errorHeaders;
if (boundaryIdx >= 0) {
let { actionHeaders, actionData, loaderHeaders, loaderData } = context;
context.matches.slice(boundaryIdx).some((match) => {
let id = match.route.id;
if (actionHeaders[id] && (!actionData || !actionData.hasOwnProperty(id))) {
errorHeaders = actionHeaders[id];
} else if (loaderHeaders[id] && !loaderData.hasOwnProperty(id)) {
errorHeaders = loaderHeaders[id];
}
return errorHeaders != null;
});
}
const defaultHeaders = new Headers(_defaultHeaders);
return matches.reduce((parentHeaders, match, idx) => {
let { id } = match.route;
let loaderHeaders = context.loaderHeaders[id] || new Headers();
let actionHeaders = context.actionHeaders[id] || new Headers();
let includeErrorHeaders = errorHeaders != null && idx === matches.length - 1;
let includeErrorCookies = includeErrorHeaders && errorHeaders !== loaderHeaders && errorHeaders !== actionHeaders;
let headersFn = getRouteHeadersFn(match);
if (headersFn == null) {
let headers2 = new Headers(parentHeaders);
if (includeErrorCookies) {
prependCookies(errorHeaders, headers2);
}
prependCookies(actionHeaders, headers2);
prependCookies(loaderHeaders, headers2);
return headers2;
}
let headers = new Headers(
typeof headersFn === "function" ? headersFn({
loaderHeaders,
parentHeaders,
actionHeaders,
errorHeaders: includeErrorHeaders ? errorHeaders : void 0
}) : headersFn
);
if (includeErrorCookies) {
prependCookies(errorHeaders, headers);
}
prependCookies(actionHeaders, headers);
prependCookies(loaderHeaders, headers);
prependCookies(parentHeaders, headers);
return headers;
}, new Headers(defaultHeaders));
}
function prependCookies(parentHeaders, childHeaders) {
let parentSetCookieString = parentHeaders.get("Set-Cookie");
if (parentSetCookieString) {
let cookies = splitCookiesString(parentSetCookieString);
let childCookies = new Set(childHeaders.getSetCookie());
cookies.forEach((cookie) => {
if (!childCookies.has(cookie)) {
childHeaders.append("Set-Cookie", cookie);
}
});
}
}
var SINGLE_FETCH_REDIRECT_STATUS = 202;
// lib/server-runtime/warnings.ts
var alreadyWarned = {};
function warnOnce(condition, message) {
if (!condition && !alreadyWarned[message]) {
alreadyWarned[message] = true;
console.warn(message);
}
}
// lib/errors.ts
var ERROR_DIGEST_BASE = "REACT_ROUTER_ERROR";
var ERROR_DIGEST_REDIRECT = "REDIRECT";
var ERROR_DIGEST_ROUTE_ERROR_RESPONSE = "ROUTE_ERROR_RESPONSE";
function createRedirectErrorDigest(response) {
return `${ERROR_DIGEST_BASE}:${ERROR_DIGEST_REDIRECT}:${JSON.stringify({
status: response.status,
statusText: response.statusText,
location: response.headers.get("Location"),
reloadDocument: response.headers.get("X-Remix-Reload-Document") === "true",
replace: response.headers.get("X-Remix-Replace") === "true"
})}`;
}
function createRouteErrorResponseDigest(response) {
let status = 500;
let statusText = "";
let data2;
if (isDataWithResponseInit(response)) {
status = response.init?.status ?? status;
statusText = response.init?.statusText ?? statusText;
data2 = response.data;
} else {
status = response.status;
statusText = response.statusText;
data2 = void 0;
}
return `${ERROR_DIGEST_BASE}:${ERROR_DIGEST_ROUTE_ERROR_RESPONSE}:${JSON.stringify(
{
status,
statusText,
data: data2
}
)}`;
}
// lib/dom/ssr/fog-of-war.ts
var URL_LIMIT = 7680;
function getPathsWithAncestors(paths) {
let result = /* @__PURE__ */ new Set();
paths.forEach((path) => {
if (!path.startsWith("/")) {
path = `/${path}`;
}
for (let i = 1; i < path.length; i++) {
if (path[i] === "/") {
result.add(path.slice(0, i));
}
}
result.add(path);
});
return Array.from(result);
}
// lib/actions.ts
function throwIfPotentialCSRFAttack(request, allowedActionOrigins) {
let originHeader = request.headers.get("origin");
let originDomain = null;
try {
originDomain = typeof originHeader === "string" && originHeader !== "null" ? new URL(originHeader).host : originHeader;
} catch {
throw new Error(
`\`origin\` header is not a valid URL. Aborting the action.`
);
}
let host = new URL(request.url).host;
if (originDomain && originDomain !== host) {
if (!isAllowedOrigin(originDomain, allowedActionOrigins)) {
throw new Error(
"The `request.url` host does not match `origin` header from a forwarded action request. Aborting the action."
);
}
}
}
function matchWildcardDomain(domain, pattern) {
const domainParts = domain.split(".");
const patternParts = pattern.split(".");
if (patternParts.length < 1) {
return false;
}
if (domainParts.length < patternParts.length) {
return false;
}
while (patternParts.length) {
const patternPart = patternParts.pop();
const domainPart = domainParts.pop();
switch (patternPart) {
case "": {
return false;
}
case "*": {
if (domainPart) {
continue;
} else {
return false;
}
}
case "**": {
if (patternParts.length > 0) {
return false;
}
return domainPart !== void 0;
}
case void 0:
default: {
if (domainPart !== patternPart) {
return false;
}
}
}
}
return domainParts.length === 0;
}
function isAllowedOrigin(originDomain, allowedActionOrigins = []) {
return allowedActionOrigins.some(
(allowedOrigin) => allowedOrigin && (allowedOrigin === originDomain || matchWildcardDomain(originDomain, allowedOrigin))
);
}
// lib/server-runtime/urls.ts
function getNormalizedPath(request, basename, future) {
basename = basename || "/";
let url = new URL(request.url);
let pathname = url.pathname;
{
if (stripBasename(pathname, basename) === "/_root.data") {
pathname = basename;
} else if (pathname.endsWith(".data")) {
pathname = pathname.replace(/\.data$/, "");
}
if (stripBasename(pathname, basename) !== "/" && pathname.endsWith("/")) {
pathname = pathname.slice(0, -1);
}
}
let searchParams = new URLSearchParams(url.search);
searchParams.delete("_routes");
let search = searchParams.toString();
if (search) {
search = `?${search}`;
}
return {
pathname,
search,
// No hashes on the server
hash: ""
};
}
// lib/rsc/server.rsc.ts
var Outlet = Outlet$1;
var WithComponentProps = UNSAFE_WithComponentProps;
var WithErrorBoundaryProps = UNSAFE_WithErrorBoundaryProps;
var WithHydrateFallbackProps = UNSAFE_WithHydrateFallbackProps;
var globalVar = typeof globalThis !== "undefined" ? globalThis : global;
var ServerStorage = globalVar.___reactRouterServerStorage___ ?? (globalVar.___reactRouterServerStorage___ = new AsyncLocalStorage());
function getRequest() {
const ctx = ServerStorage.getStore();
if (!ctx)
throw new Error(
"getRequest must be called from within a React Server render context"
);
return ctx.request;
}
var redirect2 = (...args) => {
const response = redirect(...args);
const ctx = ServerStorage.getStore();
if (ctx && ctx.runningAction) {
ctx.redirect = response;
}
return response;
};
var redirectDocument2 = (...args) => {
const response = redirectDocument(...args);
const ctx = ServerStorage.getStore();
if (ctx && ctx.runningAction) {
ctx.redirect = response;
}
return response;
};
var replace2 = (...args) => {
const response = replace(...args);
const ctx = ServerStorage.getStore();
if (ctx && ctx.runningAction) {
ctx.redirect = response;
}
return response;
};
var cachedResolvePromise = (
// @ts-expect-error - on 18 types, requires 19.
React3.cache(async (resolve) => {
return Promise.allSettled([resolve]).then((r) => r[0]);
})
);
var Await = async ({
children,
resolve,
errorElement
}) => {
let promise = cachedResolvePromise(resolve);
let resolved = await promise;
if (resolved.status === "rejected" && !errorElement) {
throw resolved.reason;
}
if (resolved.status === "rejected") {
return React3.createElement(UNSAFE_AwaitContextProvider, {
children: React3.createElement(React3.Fragment, null, errorElement),
value: { _tracked: true, _error: resolved.reason }
});
}
const toRender = typeof children === "function" ? children(resolved.value) : children;
return React3.createElement(UNSAFE_AwaitContextProvider, {
children: toRender,
value: { _tracked: true, _data: resolved.value }
});
};
async function matchRSCServerRequest({
allowedActionOrigins,
createTemporaryReferenceSet,
basename,
decodeReply,
requestContext,
routeDiscovery,
loadServerAction,
decodeAction,
decodeFormState,
onError,
request,
routes,
generateResponse
}) {
let url = new URL(request.url);
basename = basename || "/";
let normalizedPath = url.pathname;
if (url.pathname.endsWith("/_.rsc")) {
normalizedPath = url.pathname.replace(/_\.rsc$/, "");
} else if (url.pathname.endsWith(".rsc")) {
normalizedPath = url.pathname.replace(/\.rsc$/, "");
}
if (stripBasename(normalizedPath, basename) !== "/" && normalizedPath.endsWith("/")) {
normalizedPath = normalizedPath.slice(0, -1);
}
url.pathname = normalizedPath;
basename = basename.length > normalizedPath.length ? normalizedPath : basename;
let routerRequest = new Request(url.toString(), {
method: request.method,
headers: request.headers,
body: request.body,
signal: request.signal,
duplex: request.body ? "half" : void 0
});
const temporaryReferences = createTemporaryReferenceSet();
const requestUrl = new URL(request.url);
if (isManifestRequest(requestUrl)) {
let response2 = await generateManifestResponse(
routes,
basename,
request,
generateResponse,
temporaryReferences,
routeDiscovery
);
return response2;
}
let isDataRequest = isReactServerRequest(requestUrl);
let matches = matchRoutes(routes, url.pathname, basename);
if (matches) {
await Promise.all(matches.map((m) => explodeLazyRoute(m.route)));
}
const leafMatch = matches?.[matches.length - 1];
if (!isDataRequest && leafMatch && !leafMatch.route.Component && !leafMatch.route.ErrorBoundary) {
return generateResourceResponse(
routerRequest,
routes,
basename,
leafMatch.route.id,
requestContext,
onError
);
}
let response = await generateRenderResponse(
routerRequest,
routes,
basename,
isDataRequest,
decodeReply,
requestContext,
loadServerAction,
decodeAction,
decodeFormState,
onError,
generateResponse,
temporaryReferences,
allowedActionOrigins,
routeDiscovery
);
response.headers.set("X-Remix-Response", "yes");
return response;
}
async function generateManifestResponse(routes, basename, request, generateResponse, temporaryReferences, routeDiscovery) {
let url = new URL(request.url);
if (url.toString().length > URL_LIMIT) {
return new Response(null, {
statusText: "Bad Request",
status: 400
});
}
if (routeDiscovery?.mode === "initial") {
let payload2 = {
type: "manifest",
patches: getAllRoutePatches(routes)
};
return generateResponse(
{
statusCode: 200,
headers: new Headers({
"Content-Type": "text/x-component",
Vary: "Content-Type"
}),
payload: payload2
},
{ temporaryReferences, onError: defaultOnError }
);
}
let pathParam = url.searchParams.get("paths");
let pathnames = pathParam ? pathParam.split(",").filter(Boolean) : [url.pathname.replace(/\.manifest$/, "")];
let routeIds = /* @__PURE__ */ new Set();
let matchedRoutes = pathnames.flatMap((pathname) => {
let pathnameMatches = matchRoutes(routes, pathname, basename);
return pathnameMatches?.map((m, i) => ({
...m.route,
parentId: pathnameMatches[i - 1]?.route.id
})) ?? [];
}).filter((route) => {
if (!routeIds.has(route.id)) {
routeIds.add(route.id);
return true;
}
return false;
});
let payload = {
type: "manifest",
patches: Promise.all([
...matchedRoutes.map((route) => getManifestRoute(route)),
getAdditionalRoutePatches(
pathnames,
routes,
basename,
Array.from(routeIds)
)
]).then((r) => r.flat(1))
};
return generateResponse(
{
statusCode: 200,
headers: new Headers({
"Content-Type": "text/x-component"
}),
payload
},
{ temporaryReferences, onError: defaultOnError }
);
}
function prependBasenameToRedirectResponse(response, basename = "/") {
if (basename === "/") {
return response;
}
let redirect3 = response.headers.get("Location");
if (!redirect3 || isAbsoluteUrl(redirect3)) {
return response;
}
response.headers.set(
"Location",
prependBasename({ basename, pathname: redirect3 })
);
return response;
}
async function processServerAction(request, basename, decodeReply, loadServerAction, decodeAction, decodeFormState, onError, temporaryReferences) {
const getRevalidationRequest = () => new Request(request.url, {
method: "GET",
headers: request.headers,
signal: request.signal
});
const isFormRequest = canDecodeWithFormData(
request.headers.get("Content-Type")
);
const actionId = request.headers.get("rsc-action-id");
if (actionId) {
if (!decodeReply || !loadServerAction) {
throw new Error(
"Cannot handle enhanced server action without decodeReply and loadServerAction functions"
);
}
const reply = isFormRequest ? await request.formData() : await request.text();
const actionArgs = await decodeReply(reply, { temporaryReferences });
const action = await loadServerAction(actionId);
const serverAction = action.bind(null, ...actionArgs);
let actionResult = Promise.resolve(serverAction());
try {
await actionResult;
} catch (error) {
if (isResponse(error)) {
return error;
}
onError?.(error);
}
let maybeFormData = actionArgs.length === 1 ? actionArgs[0] : actionArgs[1];
let formData = maybeFormData && typeof maybeFormData === "object" && maybeFormData instanceof FormData ? maybeFormData : null;
let skipRevalidation = formData?.has("$SKIP_REVALIDATION") ?? false;
return {
actionResult,
revalidationRequest: getRevalidationRequest(),
skipRevalidation
};
} else if (isFormRequest) {
const formData = await request.clone().formData();
if (Array.from(formData.keys()).some((k) => k.startsWith("$ACTION_"))) {
if (!decodeAction) {
throw new Error(
"Cannot handle form actions without a decodeAction function"
);
}
const action = await decodeAction(formData);
let formState = void 0;
try {
let result = await action();
if (isRedirectResponse(result)) {
result = prependBasenameToRedirectResponse(result, basename);
}
formState = decodeFormState?.(result, formData);
} catch (error) {
if (isRedirectResponse(error)) {
return prependBasenameToRedirectResponse(error, basename);
}
if (isResponse(error)) {
return error;
}
onError?.(error);
}
return {
formState,
revalidationRequest: getRevalidationRequest(),
skipRevalidation: false
};
}
}
}
async function generateResourceResponse(request, routes, basename, routeId, requestContext, onError) {
try {
const staticHandler = createStaticHandler(routes, {
basename
});
let response = await staticHandler.queryRoute(request, {
routeId,
requestContext,
async generateMiddlewareResponse(queryRoute) {
try {
let response2 = await queryRoute(request);
return generateResourceResponse2(response2);
} catch (error) {
return generateErrorResponse(error);
}
},
normalizePath: (r) => getNormalizedPath(r, basename, null)
});
return response;
} catch (error) {
return generateErrorResponse(error);
}
function generateErrorResponse(error) {
let response;
if (isResponse(error)) {
response = error;
} else if (isRouteErrorResponse(error)) {
onError?.(error);
const errorMessage = typeof error.data === "string" ? error.data : error.statusText;
response = new Response(errorMessage, {
status: error.status,
statusText: error.statusText
});
} else {
onError?.(error);
response = new Response("Internal Server Error", { status: 500 });
}
return generateResourceResponse2(response);
}
function generateResourceResponse2(response) {
const headers = new Headers(response.headers);
headers.set("React-Router-Resource", "true");
return new Response(response.body, {
status: response.status,
statusText: response.statusText,
headers
});
}
}
async function generateRenderResponse(request, routes, basename, isDataRequest, decodeReply, requestContext, loadServerAction, decodeAction, decodeFormState, onError, generateResponse, temporaryReferences, allowedActionOrigins, routeDiscovery) {
let statusCode = 200;
let url = new URL(request.url);
let isSubmission = isMutationMethod(request.method);
let routeIdsToLoad = !isSubmission && url.searchParams.has("_routes") ? url.searchParams.get("_routes").split(",") : null;
const staticHandler = createStaticHandler(routes, {
basename,
mapRouteProperties: (r) => ({
hasErrorBoundary: r.ErrorBoundary != null
})
});
let actionResult;
const ctx = {
request,
runningAction: false
};
const result = await ServerStorage.run(
ctx,
() => staticHandler.query(request, {
requestContext,
skipLoaderErrorBubbling: isDataRequest,
skipRevalidation: isSubmission,
...routeIdsToLoad ? { filterMatchesToLoad: (m) => routeIdsToLoad.includes(m.route.id) } : {},
normalizePath: (r) => getNormalizedPath(r, basename),
async generateMiddlewareResponse(query) {
let formState;
let skipRevalidation = false;
let potentialCSRFAttackError;
if (isMutationMethod(request.method)) {
try {
throwIfPotentialCSRFAttack(request, allowedActionOrigins);
ctx.runningAction = true;
let result2 = await processServerAction(
request,
basename,
decodeReply,
loadServerAction,
decodeAction,
decodeFormState,
onError,
temporaryReferences
).finally(() => {
ctx.runningAction = false;
});
if (isResponse(result2)) {
return generateRedirectResponse(
result2,
actionResult,
basename,
isDataRequest,
generateResponse,
temporaryReferences,
ctx.redirect?.headers
);
}
skipRevalidation = result2?.skipRevalidation ?? false;
actionResult = result2?.actionResult;
formState = result2?.formState;
request = result2?.revalidationRequest ?? request;
if (ctx.redirect) {
return generateRedirectResponse(
ctx.redirect,
actionResult,
basename,
isDataRequest,
generateResponse,
temporaryReferences,
void 0
);
}
} catch (error) {
potentialCSRFAttackError = error;
}
}
let staticContext = await query(
request,
skipRevalidation || !!potentialCSRFAttackError ? {
filterMatchesToLoad: () => false
} : void 0
);
if (isResponse(staticContext)) {
return generateRedirectResponse(
staticContext,
actionResult,
basename,
isDataRequest,
generateResponse,
temporaryReferences,
ctx.redirect?.headers
);
}
if (potentialCSRFAttackError) {
staticContext.errors ?? (staticContext.errors = {});
staticContext.errors[staticContext.matches[0].route.id] = potentialCSRFAttackError;
staticContext.statusCode = 400;
}
return generateStaticContextResponse(
routes,
basename,
generateResponse,
statusCode,
routeIdsToLoad,
isDataRequest,
isSubmission,
actionResult,
formState,
staticContext,
temporaryReferences,
skipRevalidation,
ctx.redirect?.headers,
routeDiscovery
);
}
})
);
if (isRedirectResponse(result)) {
return generateRedirectResponse(
result,
actionResult,
basename,
isDataRequest,
generateResponse,
temporaryReferences,
ctx.redirect?.headers
);
}
invariant2(isResponse(result), "Expected a response from query");
return result;
}
function generateRedirectResponse(response, actionResult, basename, isDataRequest, generateResponse, temporaryReferences, sideEffectRedirectHeaders) {
let redirect3 = response.headers.get("Location");
if (isDataRequest && basename) {
redirect3 = stripBasename(redirect3, basename) || redirect3;
}
let payload = {
type: "redirect",
location: redirect3,
reload: response.headers.get("X-Remix-Reload-Document") === "true",
replace: response.headers.get("X-Remix-Replace") === "true",
status: response.status,
actionResult
};
let headers = new Headers(sideEffectRedirectHeaders);
for (const [key, value] of response.headers.entries()) {
headers.append(key, value);
}
headers.delete("Location");
headers.delete("X-Remix-Reload-Document");
headers.delete("X-Remix-Replace");
headers.delete("Content-Length");
headers.set("Content-Type", "text/x-component");
return generateResponse(
{
statusCode: SINGLE_FETCH_REDIRECT_STATUS,
headers,
payload
},
{ temporaryReferences, onError: defaultOnError }
);
}
async function generateStaticContextResponse(routes, basename, generateResponse, statusCode, routeIdsToLoad, isDataRequest, isSubmission, actionResult, formState, staticContext, temporaryReferences, skipRevalidation, sideEffectRedirectHeaders, routeDiscovery) {
statusCode = staticContext.statusCode ?? statusCode;
if (staticContext.errors) {
staticContext.errors = Object.fromEntries(
Object.entries(staticContext.errors).map(([key, error]) => [
key,
isRouteErrorResponse(error) ? Object.fromEntries(Object.entries(error)) : error
])
);
}
staticContext.matches.forEach((m) => {
const routeHasNoLoaderData = staticContext.loaderData[m.route.id] === void 0;
const routeHasError = Boolean(
staticContext.errors && m.route.id in staticContext.errors
);
if (routeHasNoLoaderData && !routeHasError) {
staticContext.loaderData[m.route.id] = null;
}
});
let headers = getDocumentHeadersImpl(
staticContext,
(match) => match.route.headers,
sideEffectRedirectHeaders
);
headers.delete("Content-Length");
const baseRenderPayload = {
type: "render",
basename: staticContext.basename,
routeDiscovery: routeDiscovery ?? { mode: "lazy" },
actionData: staticContext.actionData,
errors: staticContext.errors,
loaderData: staticContext.loaderData,
location: staticContext.location,
formState
};
const renderPayloadPromise = () => getRenderPayload(
baseRenderPayload,
routes,
basename,
routeIdsToLoad,
isDataRequest,
staticContext,
routeDiscovery
);
let payload;
if (actionResult) {
payload = {
type: "action",
actionResult,
rerender: skipRevalidation ? void 0 : renderPayloadPromise()
};
} else if (isSubmission && isDataRequest) {
payload = {
...baseRenderPayload,
matches: [],
patches: Promise.resolve([])
};
} else {
payload = await renderPayloadPromise();
}
return generateResponse(
{
statusCode,
headers,
payload
},
{ temporaryReferences, onError: defaultOnError }
);
}
async function getRenderPayload(baseRenderPayload, routes, basename, routeIdsToLoad, isDataRequest, staticContext, routeDiscovery) {
let deepestRenderedRouteIdx = staticContext.matches.length - 1;
let parentIds = {};
staticContext.matches.forEach((m, i) => {
if (i > 0) {
parentIds[m.route.id] = staticContext.matches[i - 1].route.id;
}
if (staticContext.errors && m.route.id in staticContext.errors && deepestRenderedRouteIdx > i) {
deepestRenderedRouteIdx = i;
}
});
let matchesPromise = Promise.all(
staticContext.matches.map((match, i) => {
let isBelowErrorBoundary = i > deepestRenderedRouteIdx;
let parentId = parentIds[match.route.id];
return getRSCRouteMatch({
staticContext,
match,
routeIdsToLoad,
isBelowErrorBoundary,
parentId
});
})
);
let patches = routeDiscovery?.mode === "initial" && !isDataRequest ? getAllRoutePatches(routes).then(
(patches2) => patches2.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)
);
return {
...baseRenderPayload,
matches: await matchesPromise,
patches
};
}
async function getRSCRouteMatch({
staticContext,
match,
isBelowErrorBoundary,
routeIdsToLoad,
parentId
}) {
const route = match.route;
await explodeLazyRoute(route);
const Layout = route.Layout || React3.Fragment;
const Component = route.Component;
const ErrorBoundary = route.ErrorBoundary;
const HydrateFallback = route.HydrateFallback;
const loaderData = staticContext.loaderData[route.id];
const actionData = staticContext.actionData?.[route.id];
const params = match.params;
let element = void 0;
let shouldLoadRoute = !routeIdsToLoad || routeIdsToLoad.includes(route.id);
if (Component && shouldLoadRoute) {
element = !isBelowErrorBoundary ? React3.createElement(
Layout,
null,
isClientReference(Component) ? React3.createElement(WithComponentProps, {
children: React3.createElement(Component)
}) : React3.createElement(Component, {
loaderData,
actionData,
params,
matches: staticContext.matches.map(
(match2) => convertRouteMatchToUiMatch(match2, staticContext.loaderData)
)
})
) : React3.createElement(Outlet);
}
let error = void 0;
if (ErrorBoundary && staticContext.errors) {
error = staticContext.errors[route.id];
}
const errorElement = ErrorBoundary ? React3.createElement(
Layout,
null,
isClientReference(ErrorBoundary) ? React3.createElement(WithErrorBoundaryProps, {
children: React3.createElement(ErrorBoundary)
}) : React3.createElement(ErrorBoundary, {
loaderData,
actionData,
params,
error
})
) : void 0;
const hydrateFallbackElement = HydrateFallback ? React3.createElement(
Layout,
null,
isClientReference(HydrateFallback) ? React3.createElement(WithHydrateFallbackProps, {
children: React3.createElement(HydrateFallback)
}) : React3.createElement(HydrateFallback, {
loaderData,
actionData,
params
})
) : void 0;
const hmrRoute = route;
return {
clientAction: route.clientAction,
clientLoader: route.clientLoader,
element,
errorElement,
handle: route.handle,
hasAction: !!route.action,
hasComponent: !!Component,
hasErrorBoundary: !!ErrorBoundary,
hasLoader: !!route.loader,
hydrateFallbackElement,
id: route.id,
index: "index" in route ? route.index : void 0,
links: route.links,
meta: route.meta,
params,
parentId,
path: route.path,
pathname: match.pathname,
pathnameBase: match.pathnameBase,
shouldRevalidate: route.shouldRevalidate,
// Add an unused client-only export (if present) so HMR can support
// switching between server-first and client-only routes during development
...hmrRoute.__ensureClientRouteModuleForHMR ? {
__ensureClientRouteModuleForHMR: hmrRoute.__ensureClientRouteModuleForHMR
} : {}
};
}
async function getManifestRoute(route) {
await explodeLazyRoute(route);
const Layout = route.Layout || React3.Fragment;
const errorElement = route.ErrorBoundary ? React3.createElement(
Layout,
null,
React3.createElement(route.ErrorBoundary)
) : void 0;
return {
clientAction: route.clientAction,
clientLoader: route.clientLoader,
handle: route.handle,
hasAction: !!route.action,
hasComponent: !!route.Component,
hasErrorBoundary: !!route.ErrorBoundary,
errorElement,
hasLoader: !!route.loader,
id: route.id,
parentId: route.parentId,
path: route.path,
index: "index" in route ? route.index : void 0,
links: route.links,
meta: route.meta
};
}
async function explodeLazyRoute(route) {
if ("lazy" in route && route.lazy) {
let {
default: lazyDefaultExport,
Component: lazyComponentExport,
...lazyProperties
} = await route.lazy();
let Component = lazyComponentExport || lazyDefaultExport;
if (Component && !route.Component) {
route.Component = Component;
}
for (let [k, v] of Object.entries(lazyProperties)) {
if (k !== "id" && k !== "path" && k !== "index" && k !== "children" && route[k] == null) {
route[k] = v;
}
}
route.lazy = void 0;
}
}
async function getAllRoutePatches(routes, basename) {
let patches = [];
async function traverse(route, parentId) {
let manifestRoute = await getManifestRoute({ ...route, parentId });
patches.push(manifestRoute);
if ("children" in route && route.children?.length) {
for (let child of route.children) {
await traverse(child, route.id);
}
}
}
for (let route of routes) {
await traverse(route, void 0);
}
return patches.filter((p) => !!p.parentId);
}
async function getAdditionalRoutePatches(pathnames, routes, basename, matchedRouteIds) {
let patchRouteMatches = /* @__PURE__ */ new Map();
let matchedPaths = /* @__PURE__ */ new Set();
for (const pathname of pathnames) {
if (matchedPaths.has(pathname)) {
continue;
}
matchedPaths.add(pathname);
let matches = matchRoutes(routes, pathname, basename) || [];
matches.forEach((m, i) => {
if (patchRouteMatches.get(m.route.id)) {
return;
}
patchRouteMatches.set(m.route.id, {
...m.route,
parentId: matches[i - 1]?.route.id
});
});
}
let patches = await Promise.all(
[...patchRouteMatches.values()].filter((route) => !matchedRouteIds.some((id) => id === route.id)).map((route) => getManifestRoute(route))
);
return patches;
}
function isReactServerRequest(url) {
return url.pathname.endsWith(".rsc");
}
function isManifestRequest(url) {
return url.pathname.endsWith(".manifest");
}
function defaultOnError(error) {
if (isRedirectResponse(error)) {
return createRedirectErrorDigest(error);
}
if (isResponse(error) || isDataWithResponseInit(error)) {
return createRouteErrorResponseDigest(error);
}
}
function isClientReference(x) {
try {
return x.$$typeof === Symbol.for("react.client.reference");
} catch {
return false;
}
}
function canDecodeWithFormData(contentType) {
if (!contentType) return false;
return contentType.match(/\bapplication\/x-www-form-urlencoded\b/) || contentType.match(/\bmultipart\/form-data\b/);
}
// lib/href.ts
function href(path, ...args) {
let params = args[0];
let result = trimTrailingSplat(path).replace(
/\/:([\w-]+)(\?)?/g,
// same regex as in .\router\utils.ts: compilePath().
(_, param, questionMark) => {
const isRequired = questionMark === void 0;
const value = params?.[param];
if (isRequired && value === void 0) {
throw new Error(
`Path '${path}' requires param '${param}' but it was not provided`
);
}
return value === void 0 ? "" : "/" + value;
}
);
if (path.endsWith("*")) {
const value = params?.["*"];
if (value !== void 0) {
result += "/" + value;
}
}
return result || "/";
}
function trimTrailingSplat(path) {
let i = path.length - 1;
let char = path[i];
if (char !== "*" && char !== "/") return path;
i--;
for (; i >= 0; i--) {
if (path[i] !== "/") break;
}
return path.slice(0, i + 1);
}
// lib/server-runtime/crypto.ts
var encoder = /* @__PURE__ */ new TextEncoder();
var sign = async (value, secret) => {
let data2 = encoder.encode(value);
let key = await createKey2(secret, ["sign"]);
let signature = await crypto.subtle.sign("HMAC", key, data2);
let hash = btoa(String.fromCharCode(...new Uint8Array(signature))).replace(
/=+$/,
""
);
return value + "." + hash;
};
var unsign = async (cookie, secret) => {
let index = cookie.lastIndexOf(".");
let value = cookie.slice(0, index);
let hash = cookie.slice(index + 1);
let data2 = encoder.encode(value);
let key = await createKey2(secret, ["verify"]);
try {
let signature = byteStringToUint8Array(atob(hash));
let valid = await crypto.subtle.verify("HMAC", key, signature, data2);
return valid ? value : false;
} catch (e) {
return false;
}
};
var createKey2 = async (secret, usages) => crypto.subtle.importKey(
"raw",
encoder.encode(secret),
{ name: "HMAC", hash: "SHA-256" },
false,
usages
);
function byteStringToUint8Array(byteString) {
let array = new Uint8Array(byteString.length);
for (let i = 0; i < byteString.length; i++) {
array[i] = byteString.charCodeAt(i);
}
return array;
}
// lib/server-runtime/cookies.ts
var createCookie = (name, cookieOptions = {}) => {
let { secrets = [], ...options } = {
path: "/",
sameSite: "lax",
...cookieOptions
};
warnOnceAboutExpiresCookie(name, options.expires);
return {
get name() {
return name;
},
get isSigned() {
return secrets.length > 0;
},
get expires() {
return typeof options.maxAge !== "undefined" ? new Date(Date.now() + options.maxAge * 1e3) : options.expires;
},
async parse(cookieHeader, parseOptions) {
if (!cookieHeader) return null;
let cookies = parse(cookieHeader, { ...options, ...parseOptions });
if (name in cookies) {
let value = cookies[name];
if (typeof value === "string" && value !== "") {
let decoded = await decodeCookieValue(value, secrets);
return decoded;
} else {
return "";
}
} else {
return null;
}
},
async serialize(value, serializeOptions) {
return serialize(
name,
value === "" ? "" : await encodeCookieValue(value, secrets),
{
...options,
...serializeOptions
}
);
}
};
};
var isCookie = (object) => {
return object != null && typeof object.name === "string" && typeof object.isSigned === "boolean" && typeof object.parse === "function" && typeof object.serialize === "function";
};
async function encodeCookieValue(value, secrets) {
let encoded = encodeData(value);
if (secrets.length > 0) {
encoded = await sign(encoded, secrets[0]);
}
return encoded;
}
async function decodeCookieValue(value, secrets) {
if (secrets.length > 0) {
for (let secret of secrets) {
let unsignedValue = await unsign(value, secret);
if (unsignedValue !== false) {
return decodeData(unsignedValue);
}
}
return null;
}
return decodeData(value);
}
function encodeData(value) {
return btoa(myUnescape(encodeURIComponent(JSON.stringify(value))));
}
function decodeData(value) {
try {
return JSON.parse(decodeURIComponent(myEscape(atob(value))));
} catch (e) {
return {};
}
}
function myEscape(value) {
let str = value.toString();
let result = "";
let index = 0;
let chr, code;
while (index < str.length) {
chr = str.charAt(index++);
if (/[\w*+\-./@]/.exec(chr)) {
result += chr;
} else {
code = chr.charCodeAt(0);
if (code < 256) {
result += "%" + hex(code, 2);
} else {
result += "%u" + hex(code, 4).toUpperCase();
}
}
}
return result;
}
function hex(code, length) {
let result = code.toString(16);
while (result.length < length) result = "0" + result;
return result;
}
function myUnescape(value) {
let str = value.toString();
let result = "";
let index = 0;
let chr, part;
while (index < str.length) {
chr = str.charAt(index++);
if (chr === "%") {
if (str.charAt(index) === "u") {
part = str.slice(index + 1, index + 5);
if (/^[\da-f]{4}$/i.exec(part)) {
result += String.fromCharCode(parseInt(part, 16));
index += 5;
continue;
}
} else {
part = str.slice(index, index + 2);
if (/^[\da-f]{2}$/i.exec(part)) {
result += String.fromCharCode(parseInt(part, 16));
index += 2;
continue;
}
}
}
result += chr;
}
return result;
}
function warnOnceAboutExpiresCookie(name, expires) {
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.`
);
}
// lib/server-runtime/sessions.ts
function flash(name) {
return `__flash_${name}__`;
}
var createSession = (initialData = {}, id = "") => {
let map = new Map(Object.entries(initialData));
return {
get id() {
return id;
},
get data() {
return Object.fromEntries(map);
},
has(name) {
return map.has(name) || map.has(flash(name));
},
get(name) {
if (map.has(name)) return map.get(name);
let flashName = flash(name);
if (map.has(flashName)) {
let value = map.get(flashName);
map.delete(flashName);
return value;
}
return void 0;
},
set(name, value) {
map.set(name, value);
},
flash(name, value) {
map.set(flash(name), value);
},
unset(name) {
map.delete(name);
}
};
};
var isSession = (object) => {
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";
};
function createSessionStorage({
cookie: cookieArg,
createData,
readData,
updateData,
deleteData
}) {
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
warnOnceAboutSigningSessionCookie(cookie);
return {
async getSession(cookieHeader, options) {
let id = cookieHeader && await cookie.parse(cookieHeader, options);
let data2 = id && await readData(id);
return createSession(data2 || {}, id || "");
},
async commitSession(session, options) {
let { id, data: data2 } = session;
let expires = options?.maxAge != null ? new Date(Date.now() + options.maxAge * 1e3) : options?.expires != null ? options.expires : cookie.expires;
if (id) {
await updateData(id, data2, expires);
} else {
id = await createData(data2, expires);
}
return cookie.serialize(id, options);
},
async destroySession(session, options) {
await deleteData(session.id);
return cookie.serialize("", {
...options,
maxAge: void 0,
expires: /* @__PURE__ */ new Date(0)
});
}
};
}
function warnOnceAboutSigningSessionCookie(cookie) {
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.`
);
}
// lib/server-runtime/sessions/cookieStorage.ts
function createCookieSessionStorage({ cookie: cookieArg } = {}) {
let cookie = isCookie(cookieArg) ? cookieArg : createCookie(cookieArg?.name || "__session", cookieArg);
warnOnceAboutSigningSessionCookie(cookie);
return {
async getSession(cookieHeader, options) {
return createSession(
cookieHeader && await cookie.parse(cookieHeader, options) || {}
);
},
async commitSession(session, options) {
let serializedCookie = await cookie.serialize(session.data, options);
if (serializedCookie.length > 4096) {
throw new Error(
"Cookie length will exceed browser maximum. Length: " + serializedCookie.length
);
}
return serializedCookie;
},
async destroySession(_session, options) {
return cookie.serialize("", {
...options,
maxAge: void 0,
expires: /* @__PURE__ */ new Date(0)
});
}
};
}
// lib/server-runtime/sessions/memoryStorage.ts
function createMemorySessionStorage({ cookie } = {}) {
let map = /* @__PURE__ */ new Map();
return createSessionStorage({
cookie,
async createData(data2, expires) {
let id = Math.random().toString(36).substring(2, 10);
map.set(id, { data: data2, expires });
return id;
},
async readData(id) {
if (map.has(id)) {
let { data: data2, expires } = map.get(id);
if (!expires || expires > /* @__PURE__ */ new Date()) {
return data2;
}
if (expires) map.delete(id);
}
return null;
},
async updateData(id, data2, expires) {
map.set(id, { data: data2, expires });
},
async deleteData(id) {
map.delete(id);
}
});
}
export { Await, RouterContextProvider, createContext, createCookie, createCookieSessionStorage, createMemorySessionStorage, createSession, createSessionStorage, createStaticHandler, data, href, isCookie, isRouteErrorResponse, isSession, matchRoutes, redirect2 as redirect, redirectDocument2 as redirectDocument, replace2 as replace, getRequest as unstable_getRequest, matchRSCServerRequest as unstable_matchRSCServerRequest };
+1479
View File
@@ -0,0 +1,1479 @@
import { Z as RouteModules, z as DataStrategyFunction, p as MiddlewareEnabled, c as RouterContextProvider, q as AppLoadContext, T as To, L as Location, P as Params, U as UIMatch, v as Action, _ as SerializeFrom, $ as PathPattern, a0 as PathMatch, a1 as ParamParseKey, K as Path, r as RouteObject, G as GetLoaderData, l as GetActionData, O as InitialEntry, W as IndexRouteObject, d as LoaderFunction, A as ActionFunction, M as MetaFunction, b as LinksFunction, Q as NonIndexRouteObject, a2 as Equal, B as PatchRoutesOnNavigationFunction, E as DataRouteObject, a as ClientLoaderFunction } from './data-DEjBmEfD.mjs';
export { a3 as ActionFunctionArgs, a4 as BaseRouteObject, C as ClientActionFunction, as as ClientActionFunctionArgs, at as ClientLoaderFunctionArgs, w as DataRouteMatch, a5 as DataStrategyFunctionArgs, a6 as DataStrategyMatch, D as DataStrategyResult, a8 as ErrorResponse, n as FormEncType, a9 as FormMethod, ay as Future, m as HTMLFormMethod, au as HeadersArgs, H as HeadersFunction, ax as HtmlLinkDescriptor, V as LazyRouteFunction, e as LinkDescriptor, o as LoaderFunctionArgs, av as MetaArgs, g as MetaDescriptor, aa as MiddlewareFunction, aw as PageLinkDescriptor, ab as PatchRoutesOnNavigationFunctionArgs, ac as PathParam, ad as RedirectFunction, X as RouteMatch, ae as RouterContext, S as ShouldRevalidateFunction, af as ShouldRevalidateFunctionArgs, a7 as UNSAFE_DataWithResponseInit, aE as UNSAFE_ErrorResponseImpl, aB as UNSAFE_createBrowserHistory, aC as UNSAFE_createHashHistory, aA as UNSAFE_createMemoryHistory, aD as UNSAFE_invariant, ag as createContext, ah as createPath, aj as data, ak as generatePath, al as isRouteErrorResponse, am as matchPath, an as matchRoutes, ai as parsePath, ao as redirect, ap as redirectDocument, aq as replace, ar as resolvePath, az as unstable_SerializesTo } from './data-DEjBmEfD.mjs';
import { c as Router, N as NavigateOptions, d as NavigationStates, B as BlockerFunction, e as Blocker, f as RelativeRoutingType, H as HydrationState } from './context-CeD5LmaF.mjs';
export { K as Await, w as AwaitProps, C as ClientInstrumentation, b as ClientOnErrorFunction, F as Fetcher, G as GetScrollPositionFunction, g as GetScrollRestorationKeyFunction, u as IDLE_BLOCKER, t as IDLE_FETCHER, s as IDLE_NAVIGATION, x as IndexRouteProps, I as InstrumentRequestHandlerFunction, q as InstrumentRouteFunction, p as InstrumentRouterFunction, r as InstrumentationHandlerResult, L as LayoutRouteProps, Q as MemoryRouter, M as MemoryRouterOpts, y as MemoryRouterProps, T as Navigate, z as NavigateProps, i as Navigation, v as Navigator, U as Outlet, O as OutletProps, P as PathRouteProps, n as RevalidationState, V as Route, D as RouteProps, W as Router, m as RouterFetchOptions, R as RouterInit, l as RouterNavigateOptions, E as RouterProps, X as RouterProvider, a as RouterProviderProps, j as RouterState, k as RouterSubscriber, Y as Routes, J as RoutesProps, o as ServerInstrumentation, S as StaticHandler, h as StaticHandlerContext, A as UNSAFE_AwaitContextProvider, a2 as UNSAFE_DataRouterContext, a3 as UNSAFE_DataRouterStateContext, a4 as UNSAFE_FetchersContext, a5 as UNSAFE_LocationContext, a6 as UNSAFE_NavigationContext, a7 as UNSAFE_RouteContext, a8 as UNSAFE_ViewTransitionContext, ab as UNSAFE_WithComponentProps, af as UNSAFE_WithErrorBoundaryProps, ad as UNSAFE_WithHydrateFallbackProps, a1 as UNSAFE_createRouter, a9 as UNSAFE_hydrationRouteProperties, aa as UNSAFE_mapRouteProperties, ac as UNSAFE_withComponentProps, ag as UNSAFE_withErrorBoundaryProps, ae as UNSAFE_withHydrateFallbackProps, Z as createMemoryRouter, _ as createRoutesFromChildren, $ as createRoutesFromElements, a0 as renderMatches } from './context-CeD5LmaF.mjs';
import * as React from 'react';
import React__default, { ReactElement } from 'react';
import { a as RouteModules$1, P as Pages } from './register-CmkRspdl.mjs';
export { b as Register } from './register-CmkRspdl.mjs';
import { A as AssetsManifest, S as ServerBuild, E as EntryContext, F as FutureConfig } from './index-react-server-client-CACgcj2J.mjs';
export { l as BrowserRouter, B as BrowserRouterProps, D as DOMRouterOpts, a1 as DiscoverBehavior, c as FetcherFormProps, h as FetcherSubmitFunction, G as FetcherSubmitOptions, i as FetcherWithComponents, q as Form, d as FormProps, a2 as HandleDataRequestFunction, a3 as HandleDocumentRequestFunction, a4 as HandleErrorFunction, m as HashRouter, H as HashRouterProps, a as HistoryRouterProps, n as Link, L as LinkProps, X as Links, _ as LinksProps, W as Meta, p as NavLink, N as NavLinkProps, b as NavLinkRenderProps, P as ParamKeyValuePair, a0 as PrefetchBehavior, Z as PrefetchPageLinks, Y as Scripts, $ as ScriptsProps, r as ScrollRestoration, e as ScrollRestorationProps, a5 as ServerEntryModule, f as SetURLSearchParams, T as StaticRouter, M as StaticRouterProps, V as StaticRouterProvider, O as StaticRouterProviderProps, g as SubmitFunction, I as SubmitOptions, J as SubmitTarget, a6 as UNSAFE_FrameworkContext, a7 as UNSAFE_createClientRoutes, a8 as UNSAFE_createClientRoutesWithHMRRevalidationOptOut, a9 as UNSAFE_shouldHydrateRouteLoader, aa as UNSAFE_useScrollRestoration, U as URLSearchParamsInit, j as createBrowserRouter, k as createHashRouter, K as createSearchParams, Q as createStaticHandler, R as createStaticRouter, o as unstable_HistoryRouter, z as unstable_usePrompt, y as useBeforeUnload, w as useFetcher, x as useFetchers, v as useFormAction, u as useLinkClickHandler, s as useSearchParams, t as useSubmit, C as useViewTransitionState } from './index-react-server-client-CACgcj2J.mjs';
import { ParseOptions, SerializeOptions } from 'cookie';
export { ParseOptions as CookieParseOptions, SerializeOptions as CookieSerializeOptions } from 'cookie';
import { e as RSCPayload, g as getRequest, m as matchRSCServerRequest } from './browser-DBmQ1yAR.mjs';
export { B as unstable_BrowserCreateFromReadableStreamFunction, D as unstable_DecodeActionFunction, a as unstable_DecodeFormStateFunction, b as unstable_DecodeReplyFunction, E as unstable_EncodeReplyFunction, L as unstable_LoadServerActionFunction, h as unstable_RSCHydratedRouterProps, d as unstable_RSCManifestPayload, i as unstable_RSCMatch, f as unstable_RSCRenderPayload, n as unstable_RSCRouteConfig, l as unstable_RSCRouteConfigEntry, j as unstable_RSCRouteManifest, k as unstable_RSCRouteMatch } from './browser-DBmQ1yAR.mjs';
declare const SingleFetchRedirectSymbol: unique symbol;
declare function getTurboStreamSingleFetchDataStrategy(getRouter: () => Router, manifest: AssetsManifest, routeModules: RouteModules, ssr: boolean, basename: string | undefined, trailingSlashAware: boolean): DataStrategyFunction;
declare function decodeViaTurboStream(body: ReadableStream<Uint8Array>, global: Window | typeof globalThis): Promise<{
done: Promise<undefined>;
value: unknown;
}>;
/**
* The mode to use when running the server.
*/
declare enum ServerMode {
Development = "development",
Production = "production",
Test = "test"
}
type RequestHandler = (request: Request, loadContext?: MiddlewareEnabled extends true ? RouterContextProvider : AppLoadContext) => Promise<Response>;
type CreateRequestHandlerFunction = (build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>), mode?: string) => RequestHandler;
declare const createRequestHandler: CreateRequestHandlerFunction;
/**
* Resolves a URL against the current {@link Location}.
*
* @example
* import { useHref } from "react-router";
*
* function SomeComponent() {
* let href = useHref("some/where");
* // "/resolved/some/where"
* }
*
* @public
* @category Hooks
* @param to The path to resolve
* @param options Options
* @param options.relative Defaults to `"route"` so routing is relative to the
* route tree.
* Set to `"path"` to make relative routing operate against path segments.
* @returns The resolved href string
*/
declare function useHref(to: To, { relative }?: {
relative?: RelativeRoutingType;
}): string;
/**
* Returns `true` if this component is a descendant of a {@link Router}, useful
* to ensure a component is used within a {@link Router}.
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns Whether the component is within a {@link Router} context
*/
declare function useInRouterContext(): boolean;
/**
* Returns the current {@link Location}. This can be useful if you'd like to
* perform some side effect whenever it changes.
*
* @example
* import * as React from 'react'
* import { useLocation } from 'react-router'
*
* function SomeComponent() {
* let location = useLocation()
*
* React.useEffect(() => {
* // Google Analytics
* ga('send', 'pageview')
* }, [location]);
*
* return (
* // ...
* );
* }
*
* @public
* @category Hooks
* @returns The current {@link Location} object
*/
declare function useLocation(): Location;
/**
* Returns the current {@link Navigation} action which describes how the router
* came to the current {@link Location}, either by a pop, push, or replace on
* the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History) stack.
*
* @public
* @category Hooks
* @returns The current {@link NavigationType} (`"POP"`, `"PUSH"`, or `"REPLACE"`)
*/
declare function useNavigationType(): Action;
/**
* Returns a {@link PathMatch} object if the given pattern matches the current URL.
* This is useful for components that need to know "active" state, e.g.
* {@link NavLink | `<NavLink>`}.
*
* @public
* @category Hooks
* @param pattern The pattern to match against the current {@link Location}
* @returns The path match object if the pattern matches, `null` otherwise
*/
declare function useMatch<Path extends string>(pattern: PathPattern<Path> | Path): PathMatch<ParamParseKey<Path>> | null;
/**
* The interface for the `navigate` function returned from {@link useNavigate}.
*/
interface NavigateFunction {
(to: To, options?: NavigateOptions): void | Promise<void>;
(delta: number): void | Promise<void>;
}
/**
* Returns a function that lets you navigate programmatically in the browser in
* response to user interactions or effects.
*
* It's often better to use {@link redirect} in [`action`](../../start/framework/route-module#action)/[`loader`](../../start/framework/route-module#loader)
* functions than this hook.
*
* The returned function signature is `navigate(to, options?)`/`navigate(delta)` where:
*
* * `to` can be a string path, a {@link To} object, or a number (delta)
* * `options` contains options for modifying the navigation
* * These options work in all modes (Framework, Data, and Declarative):
* * `relative`: `"route"` or `"path"` to control relative routing logic
* * `replace`: Replace the current entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History) stack
* * `state`: Optional [`history.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state) to include with the new {@link Location}
* * These options only work in Framework and Data modes:
* * `flushSync`: Wrap the DOM updates in [`ReactDom.flushSync`](https://react.dev/reference/react-dom/flushSync)
* * `preventScrollReset`: Do not scroll back to the top of the page after navigation
* * `viewTransition`: Enable [`document.startViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/Document/startViewTransition) for this navigation
*
* @example
* import { useNavigate } from "react-router";
*
* function SomeComponent() {
* let navigate = useNavigate();
* return (
* <button onClick={() => navigate(-1)}>
* Go Back
* </button>
* );
* }
*
* @additionalExamples
* ### Navigate to another path
*
* ```tsx
* navigate("/some/route");
* navigate("/some/route?search=param");
* ```
*
* ### Navigate with a {@link To} object
*
* All properties are optional.
*
* ```tsx
* navigate({
* pathname: "/some/route",
* search: "?search=param",
* hash: "#hash",
* state: { some: "state" },
* });
* ```
*
* If you use `state`, that will be available on the {@link Location} object on
* the next page. Access it with `useLocation().state` (see {@link useLocation}).
*
* ### Navigate back or forward in the history stack
*
* ```tsx
* // back
* // often used to close modals
* navigate(-1);
*
* // forward
* // often used in a multistep wizard workflows
* navigate(1);
* ```
*
* Be cautious with `navigate(number)`. If your application can load up to a
* route that has a button that tries to navigate forward/back, there may not be
* a [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* entry to go back or forward to, or it can go somewhere you don't expect
* (like a different domain).
*
* Only use this if you're sure they will have an entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack to navigate to.
*
* ### Replace the current entry in the history stack
*
* This will remove the current entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack, replacing it with a new one, similar to a server side redirect.
*
* ```tsx
* navigate("/some/route", { replace: true });
* ```
*
* ### Prevent Scroll Reset
*
* [MODES: framework, data]
*
* <br/>
* <br/>
*
* To prevent {@link ScrollRestoration | `<ScrollRestoration>`} from resetting
* the scroll position, use the `preventScrollReset` option.
*
* ```tsx
* navigate("?some-tab=1", { preventScrollReset: true });
* ```
*
* For example, if you have a tab interface connected to search params in the
* middle of a page, and you don't want it to scroll to the top when a tab is
* clicked.
*
* ### Return Type Augmentation
*
* Internally, `useNavigate` uses a separate implementation when you are in
* Declarative mode versus Data/Framework mode - the primary difference being
* that the latter is able to return a stable reference that does not change
* identity across navigations. The implementation in Data/Framework mode also
* returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* that resolves when the navigation is completed. This means the return type of
* `useNavigate` is `void | Promise<void>`. This is accurate, but can lead to
* some red squigglies based on the union in the return value:
*
* - If you're using `typescript-eslint`, you may see errors from
* [`@typescript-eslint/no-floating-promises`](https://typescript-eslint.io/rules/no-floating-promises)
* - In Framework/Data mode, `React.use(navigate())` will show a false-positive
* `Argument of type 'void | Promise<void>' is not assignable to parameter of
* type 'Usable<void>'` error
*
* The easiest way to work around these issues is to augment the type based on the
* router you're using:
*
* ```ts
* // If using <BrowserRouter>
* declare module "react-router" {
* interface NavigateFunction {
* (to: To, options?: NavigateOptions): void;
* (delta: number): void;
* }
* }
*
* // If using <RouterProvider> or Framework mode
* declare module "react-router" {
* interface NavigateFunction {
* (to: To, options?: NavigateOptions): Promise<void>;
* (delta: number): Promise<void>;
* }
* }
* ```
*
* @public
* @category Hooks
* @returns A navigate function for programmatic navigation
*/
declare function useNavigate(): NavigateFunction;
/**
* Returns the parent route {@link Outlet | `<Outlet context>`}.
*
* Often parent routes manage state or other values you want shared with child
* routes. You can create your own [context provider](https://react.dev/learn/passing-data-deeply-with-context)
* if you like, but this is such a common situation that it's built-into
* {@link Outlet | `<Outlet>`}.
*
* ```tsx
* // Parent route
* function Parent() {
* const [count, setCount] = React.useState(0);
* return <Outlet context={[count, setCount]} />;
* }
* ```
*
* ```tsx
* // Child route
* import { useOutletContext } from "react-router";
*
* function Child() {
* const [count, setCount] = useOutletContext();
* const increment = () => setCount((c) => c + 1);
* return <button onClick={increment}>{count}</button>;
* }
* ```
*
* If you're using TypeScript, we recommend the parent component provide a
* custom hook for accessing the context value. This makes it easier for
* consumers to get nice typings, control consumers, and know who's consuming
* the context value.
*
* Here's a more realistic example:
*
* ```tsx filename=src/routes/dashboard.tsx lines=[14,20]
* import { useState } from "react";
* import { Outlet, useOutletContext } from "react-router";
*
* import type { User } from "./types";
*
* type ContextType = { user: User | null };
*
* export default function Dashboard() {
* const [user, setUser] = useState<User | null>(null);
*
* return (
* <div>
* <h1>Dashboard</h1>
* <Outlet context={{ user } satisfies ContextType} />
* </div>
* );
* }
*
* export function useUser() {
* return useOutletContext<ContextType>();
* }
* ```
*
* ```tsx filename=src/routes/dashboard/messages.tsx lines=[1,4]
* import { useUser } from "../dashboard";
*
* export default function DashboardMessages() {
* const { user } = useUser();
* return (
* <div>
* <h2>Messages</h2>
* <p>Hello, {user.name}!</p>
* </div>
* );
* }
* ```
*
* @public
* @category Hooks
* @returns The context value passed to the parent {@link Outlet} component
*/
declare function useOutletContext<Context = unknown>(): Context;
/**
* Returns the element for the child route at this level of the route
* hierarchy. Used internally by {@link Outlet | `<Outlet>`} to render child
* routes.
*
* @public
* @category Hooks
* @param context The context to pass to the outlet
* @returns The child route element or `null` if no child routes match
*/
declare function useOutlet(context?: unknown): React.ReactElement | null;
/**
* Returns an object of key/value-pairs of the dynamic params from the current
* URL that were matched by the routes. Child routes inherit all params from
* their parent routes.
*
* Assuming a route pattern like `/posts/:postId` is matched by `/posts/123`
* then `params.postId` will be `"123"`.
*
* @example
* import { useParams } from "react-router";
*
* function SomeComponent() {
* let params = useParams();
* params.postId;
* }
*
* @additionalExamples
* ### Basic Usage
*
* ```tsx
* import { useParams } from "react-router";
*
* // given a route like:
* <Route path="/posts/:postId" element={<Post />} />;
*
* // or a data route like:
* createBrowserRouter([
* {
* path: "/posts/:postId",
* component: Post,
* },
* ]);
*
* // or in routes.ts
* route("/posts/:postId", "routes/post.tsx");
* ```
*
* Access the params in a component:
*
* ```tsx
* import { useParams } from "react-router";
*
* export default function Post() {
* let params = useParams();
* return <h1>Post: {params.postId}</h1>;
* }
* ```
*
* ### Multiple Params
*
* Patterns can have multiple params:
*
* ```tsx
* "/posts/:postId/comments/:commentId";
* ```
*
* All will be available in the params object:
*
* ```tsx
* import { useParams } from "react-router";
*
* export default function Post() {
* let params = useParams();
* return (
* <h1>
* Post: {params.postId}, Comment: {params.commentId}
* </h1>
* );
* }
* ```
*
* ### Catchall Params
*
* Catchall params are defined with `*`:
*
* ```tsx
* "/files/*";
* ```
*
* The matched value will be available in the params object as follows:
*
* ```tsx
* import { useParams } from "react-router";
*
* export default function File() {
* let params = useParams();
* let catchall = params["*"];
* // ...
* }
* ```
*
* You can destructure the catchall param:
*
* ```tsx
* export default function File() {
* let { "*": catchall } = useParams();
* console.log(catchall);
* }
* ```
*
* @public
* @category Hooks
* @returns An object containing the dynamic route parameters
*/
declare function useParams<ParamsOrKey extends string | Record<string, string | undefined> = string>(): Readonly<[
ParamsOrKey
] extends [string] ? Params<ParamsOrKey> : Partial<ParamsOrKey>>;
/**
* Resolves the pathname of the given `to` value against the current
* {@link Location}. Similar to {@link useHref}, but returns a
* {@link Path} instead of a string.
*
* @example
* import { useResolvedPath } from "react-router";
*
* function SomeComponent() {
* // if the user is at /dashboard/profile
* let path = useResolvedPath("../accounts");
* path.pathname; // "/dashboard/accounts"
* path.search; // ""
* path.hash; // ""
* }
*
* @public
* @category Hooks
* @param to The path to resolve
* @param options Options
* @param options.relative Defaults to `"route"` so routing is relative to the route tree.
* Set to `"path"` to make relative routing operate against path segments.
* @returns The resolved {@link Path} object with `pathname`, `search`, and `hash`
*/
declare function useResolvedPath(to: To, { relative }?: {
relative?: RelativeRoutingType;
}): Path;
/**
* Hook version of {@link Routes | `<Routes>`} that uses objects instead of
* components. These objects have the same properties as the component props.
* The return value of `useRoutes` is either a valid React element you can use
* to render the route tree, or `null` if nothing matched.
*
* @example
* import { useRoutes } from "react-router";
*
* function App() {
* let element = useRoutes([
* {
* path: "/",
* element: <Dashboard />,
* children: [
* {
* path: "messages",
* element: <DashboardMessages />,
* },
* { path: "tasks", element: <DashboardTasks /> },
* ],
* },
* { path: "team", element: <AboutPage /> },
* ]);
*
* return element;
* }
*
* @public
* @category Hooks
* @param routes An array of {@link RouteObject}s that define the route hierarchy
* @param locationArg An optional {@link Location} object or pathname string to
* use instead of the current {@link Location}
* @returns A React element to render the matched route, or `null` if no routes matched
*/
declare function useRoutes(routes: RouteObject[], locationArg?: Partial<Location> | string): React.ReactElement | null;
type UseNavigationResult = UseNavigationResultStates[keyof UseNavigationResultStates];
type UseNavigationResultStates = {
Idle: Omit<NavigationStates["Idle"], "matches" | "historyAction">;
Loading: Omit<NavigationStates["Loading"], "matches" | "historyAction">;
Submitting: Omit<NavigationStates["Submitting"], "matches" | "historyAction">;
};
/**
* Returns the current {@link Navigation}, defaulting to an "idle" navigation
* when no navigation is in progress. You can use this to render pending UI
* (like a global spinner) or read [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
* from a form navigation.
*
* @example
* import { useNavigation } from "react-router";
*
* function SomeComponent() {
* let navigation = useNavigation();
* navigation.state;
* navigation.formData;
* // etc.
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The current {@link Navigation} object
*/
declare function useNavigation(): UseNavigationResult;
/**
* Revalidate the data on the page for reasons outside of normal data mutations
* like [`Window` focus](https://developer.mozilla.org/en-US/docs/Web/API/Window/focus_event)
* or polling on an interval.
*
* Note that page data is already revalidated automatically after actions.
* If you find yourself using this for normal CRUD operations on your data in
* response to user interactions, you're probably not taking advantage of the
* other APIs like {@link useFetcher}, {@link Form}, {@link useSubmit} that do
* this automatically.
*
* @example
* import { useRevalidator } from "react-router";
*
* function WindowFocusRevalidator() {
* const revalidator = useRevalidator();
*
* useFakeWindowFocus(() => {
* revalidator.revalidate();
* });
*
* return (
* <div hidden={revalidator.state === "idle"}>
* Revalidating...
* </div>
* );
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns An object with a `revalidate` function and the current revalidation
* `state`
*/
declare function useRevalidator(): {
revalidate: () => Promise<void>;
state: Router["state"]["revalidation"];
};
/**
* Returns the active route matches, useful for accessing `loaderData` for
* parent/child routes or the route [`handle`](../../start/framework/route-module#handle)
* property
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns An array of {@link UIMatch | UI matches} for the current route hierarchy
*/
declare function useMatches(): UIMatch[];
/**
* Returns the data from the closest route
* [`loader`](../../start/framework/route-module#loader) or
* [`clientLoader`](../../start/framework/route-module#clientloader).
*
* @example
* import { useLoaderData } from "react-router";
*
* export async function loader() {
* return await fakeDb.invoices.findAll();
* }
*
* export default function Invoices() {
* let invoices = useLoaderData<typeof loader>();
* // ...
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The data returned from the route's [`loader`](../../start/framework/route-module#loader) or [`clientLoader`](../../start/framework/route-module#clientloader) function
*/
declare function useLoaderData<T = any>(): SerializeFrom<T>;
/**
* Returns the [`loader`](../../start/framework/route-module#loader) data for a
* given route by route ID.
*
* Route IDs are created automatically. They are simply the path of the route file
* relative to the app folder without the extension.
*
* | Route Filename | Route ID |
* | ---------------------------- | ---------------------- |
* | `app/root.tsx` | `"root"` |
* | `app/routes/teams.tsx` | `"routes/teams"` |
* | `app/whatever/teams.$id.tsx` | `"whatever/teams.$id"` |
*
* @example
* import { useRouteLoaderData } from "react-router";
*
* function SomeComponent() {
* const { user } = useRouteLoaderData("root");
* }
*
* // You can also specify your own route ID's manually in your routes.ts file:
* route("/", "containers/app.tsx", { id: "app" })
* useRouteLoaderData("app");
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param routeId The ID of the route to return loader data from
* @returns The data returned from the specified route's [`loader`](../../start/framework/route-module#loader)
* function, or `undefined` if not found
*/
declare function useRouteLoaderData<T = any>(routeId: string): SerializeFrom<T> | undefined;
/**
* Returns the [`action`](../../start/framework/route-module#action) data from
* the most recent `POST` navigation form submission or `undefined` if there
* hasn't been one.
*
* @example
* import { Form, useActionData } from "react-router";
*
* export async function action({ request }) {
* const body = await request.formData();
* const name = body.get("visitorsName");
* return { message: `Hello, ${name}` };
* }
*
* export default function Invoices() {
* const data = useActionData();
* return (
* <Form method="post">
* <input type="text" name="visitorsName" />
* {data ? data.message : "Waiting..."}
* </Form>
* );
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The data returned from the route's [`action`](../../start/framework/route-module#action)
* function, or `undefined` if no [`action`](../../start/framework/route-module#action)
* has been called
*/
declare function useActionData<T = any>(): SerializeFrom<T> | undefined;
/**
* Accesses the error thrown during an
* [`action`](../../start/framework/route-module#action),
* [`loader`](../../start/framework/route-module#loader),
* or component render to be used in a route module
* [`ErrorBoundary`](../../start/framework/route-module#errorboundary).
*
* @example
* export function ErrorBoundary() {
* const error = useRouteError();
* return <div>{error.message}</div>;
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The error that was thrown during route [loading](../../start/framework/route-module#loader),
* [`action`](../../start/framework/route-module#action) execution, or rendering
*/
declare function useRouteError(): unknown;
/**
* Returns the resolved promise value from the closest {@link Await | `<Await>`}.
*
* @example
* function SomeDescendant() {
* const value = useAsyncValue();
* // ...
* }
*
* // somewhere in your app
* <Await resolve={somePromise}>
* <SomeDescendant />
* </Await>;
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The resolved value from the nearest {@link Await} component
*/
declare function useAsyncValue(): unknown;
/**
* Returns the rejection value from the closest {@link Await | `<Await>`}.
*
* @example
* import { Await, useAsyncError } from "react-router";
*
* function ErrorElement() {
* const error = useAsyncError();
* return (
* <p>Uh Oh, something went wrong! {error.message}</p>
* );
* }
*
* // somewhere in your app
* <Await
* resolve={promiseThatRejects}
* errorElement={<ErrorElement />}
* />;
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The error that was thrown in the nearest {@link Await} component
*/
declare function useAsyncError(): unknown;
/**
* Allow the application to block navigations within the SPA and present the
* user a confirmation dialog to confirm the navigation. Mostly used to avoid
* using half-filled form data. This does not handle hard-reloads or
* cross-origin navigations.
*
* The {@link Blocker} object returned by the hook has the following properties:
*
* - **`state`**
* - `unblocked` - the blocker is idle and has not prevented any navigation
* - `blocked` - the blocker has prevented a navigation
* - `proceeding` - the blocker is proceeding through from a blocked navigation
* - **`location`**
* - When in a `blocked` state, this represents the {@link Location} to which
* we blocked a navigation. When in a `proceeding` state, this is the
* location being navigated to after a `blocker.proceed()` call.
* - **`proceed()`**
* - When in a `blocked` state, you may call `blocker.proceed()` to proceed to
* the blocked location.
* - **`reset()`**
* - When in a `blocked` state, you may call `blocker.reset()` to return the
* blocker to an `unblocked` state and leave the user at the current
* location.
*
* @example
* // Boolean version
* let blocker = useBlocker(value !== "");
*
* // Function version
* let blocker = useBlocker(
* ({ currentLocation, nextLocation, historyAction }) =>
* value !== "" &&
* currentLocation.pathname !== nextLocation.pathname
* );
*
* @additionalExamples
* ```tsx
* import { useCallback, useState } from "react";
* import { BlockerFunction, useBlocker } from "react-router";
*
* export function ImportantForm() {
* const [value, setValue] = useState("");
*
* const shouldBlock = useCallback<BlockerFunction>(
* () => value !== "",
* [value]
* );
* const blocker = useBlocker(shouldBlock);
*
* return (
* <form
* onSubmit={(e) => {
* e.preventDefault();
* setValue("");
* if (blocker.state === "blocked") {
* blocker.proceed();
* }
* }}
* >
* <input
* name="data"
* value={value}
* onChange={(e) => setValue(e.target.value)}
* />
*
* <button type="submit">Save</button>
*
* {blocker.state === "blocked" ? (
* <>
* <p style={{ color: "red" }}>
* Blocked the last navigation to
* </p>
* <button
* type="button"
* onClick={() => blocker.proceed()}
* >
* Let me through
* </button>
* <button
* type="button"
* onClick={() => blocker.reset()}
* >
* Keep me here
* </button>
* </>
* ) : blocker.state === "proceeding" ? (
* <p style={{ color: "orange" }}>
* Proceeding through blocked navigation
* </p>
* ) : (
* <p style={{ color: "green" }}>
* Blocker is currently unblocked
* </p>
* )}
* </form>
* );
* }
* ```
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param shouldBlock Either a boolean or a function returning a boolean which
* indicates whether the navigation should be blocked. The function format
* receives a single object parameter containing the `currentLocation`,
* `nextLocation`, and `historyAction` of the potential navigation.
* @returns A {@link Blocker} object with state and reset functionality
*/
declare function useBlocker(shouldBlock: boolean | BlockerFunction): Blocker;
type UseRouteArgs = [] | [routeId: keyof RouteModules$1];
type UseRouteResult<Args extends UseRouteArgs> = Args extends [] ? UseRoute<unknown> : Args extends ["root"] ? UseRoute<"root"> : Args extends [infer RouteId extends keyof RouteModules$1] ? UseRoute<RouteId> | undefined : never;
type UseRoute<RouteId extends keyof RouteModules$1 | unknown> = {
handle: RouteId extends keyof RouteModules$1 ? RouteModules$1[RouteId] extends {
handle: infer handle;
} ? handle : unknown : unknown;
loaderData: RouteId extends keyof RouteModules$1 ? GetLoaderData<RouteModules$1[RouteId]> | undefined : unknown;
actionData: RouteId extends keyof RouteModules$1 ? GetActionData<RouteModules$1[RouteId]> | undefined : unknown;
};
declare function useRoute<Args extends UseRouteArgs>(...args: Args): UseRouteResult<Args>;
/**
* A single route match returned from {@link unstable_useRouterState}. Mirrors
* {@link UIMatch} minus the data-related fields (`data`, `loaderData`).
*/
type unstable_RouterStateMatch<Handle = unknown> = Omit<UIMatch<unknown, Handle>, "data" | "loaderData">;
/**
* The shape of the `active` variant returned from
* {@link unstable_useRouterState}.
*/
type unstable_RouterStateActiveVariant = {
location: Location;
searchParams: URLSearchParams;
params: Params;
matches: unstable_RouterStateMatch[];
type: Action;
};
/**
* The shape of the `pending` variant returned from
* {@link unstable_useRouterState}. Extends
* {@link unstable_RouterStateActiveVariant} with the navigation `state` and
* submission fields mirroring {@link useNavigation} — submission fields are
* populated when the in-flight navigation was triggered by a form submission,
* otherwise `undefined`.
*/
type unstable_RouterStatePendingVariant = unstable_RouterStatePendingVariants[keyof unstable_RouterStatePendingVariants];
type unstable_RouterStatePendingVariants = {
Loading: unstable_RouterStateActiveVariant & Omit<NavigationStates["Loading"], "matches" | "historyAction">;
Submitting: unstable_RouterStateActiveVariant & Omit<NavigationStates["Submitting"], "matches" | "historyAction">;
};
/**
* The return shape of {@link unstable_useRouterState}.
*
* `active` reflects the currently-committed location. `pending` reflects the
* in-flight navigation (if any).
*/
type unstable_RouterState = {
active: unstable_RouterStateActiveVariant;
pending: unstable_RouterStatePendingVariant | null;
};
/**
* A unified hook for reading router state: current (`active`) and in-flight
* (`pending`) locations, search params, params, matches, and navigation type.
*
* This hook consolidates the information you used to get from {@link useLocation},
* {@link useSearchParams}, {@link useParams}, {@link useMatches}, {@link useNavigation},
* and {@link useNavigationType} into a single hook.
*
*
* @example
* import { unstable_useRouterState as useRouterState } from "react-router";
*
* let { active, pending } = unstable_useRouterState();
*
* // Active is always populated with the current location
* active.location; // replaces `useLocation()`
* active.searchParams; // replaces `useSearchParams()[0]`
* active.params; // replaces `useParams()`
* active.matches; // replaces `useMatches()`
* active.type; // replaces `useNavigationType()`
*
* // Pending is only populated during a navigation
* pending.location; // replaces `useNavigation().location`
* pending.searchParams; // equivalent to `new URLSearchParams(useNavigation().search)`
* pending.params; // Not directly accessible today
* pending.matches; // Not directly accessible today
* pending.type; // Not directly accessible today
* pending.state; // replaces `useNavigation().state`
* pending.formMethod; // replaces useNavigation().formMethod
* pending.formAction; // replaces useNavigation().formAction
* pending.formEncType; // replaces useNavigation().formEncType
* pending.formData; // replaces useNavigation().formData
* pending.json; // replaces useNavigation().json
* pending.text; // replaces useNavigation().text
*
* @name unstable_useRouterState
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The current router state with `active` and `pending` variants
*/
declare function useRouterState(): unstable_RouterState;
/**
* @category Types
*/
interface ServerRouterProps {
/**
* The entry context containing the manifest, route modules, and other data
* needed for rendering.
*/
context: EntryContext;
/**
* The URL of the request being handled.
*/
url: string | URL;
/**
* An optional `nonce` for [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP)
* compliance. This is applied to inline scripts rendered by React Router and
* used as the default for nonce-aware components such as {@link Links | `<Links>`},
* {@link Scripts | `<Scripts>`}, and {@link ScrollRestoration | `<ScrollRestoration>`}
* when they do not provide their own `nonce`.
*/
nonce?: string;
}
/**
* The server entry point for a React Router app in Framework Mode. This
* component is used to generate the HTML in the response from the server. See
* [`entry.server.tsx`](../framework-conventions/entry.server.tsx).
*
* @public
* @category Framework Routers
* @mode framework
* @param props Props
* @param {ServerRouterProps.context} props.context n/a
* @param {ServerRouterProps.nonce} props.nonce n/a
* @param {ServerRouterProps.url} props.url n/a
* @returns A React element that represents the server-rendered application.
*/
declare function ServerRouter({ context, url, nonce, }: ServerRouterProps): ReactElement;
interface StubRouteExtensions {
Component?: React.ComponentType<any>;
HydrateFallback?: React.ComponentType<any>;
ErrorBoundary?: React.ComponentType<any>;
loader?: LoaderFunction;
action?: ActionFunction;
children?: StubRouteObject[];
meta?: MetaFunction;
links?: LinksFunction;
}
interface StubIndexRouteObject extends Omit<IndexRouteObject, "Component" | "HydrateFallback" | "ErrorBoundary" | "loader" | "action" | "element" | "errorElement" | "children">, StubRouteExtensions {
}
interface StubNonIndexRouteObject extends Omit<NonIndexRouteObject, "Component" | "HydrateFallback" | "ErrorBoundary" | "loader" | "action" | "element" | "errorElement" | "children">, StubRouteExtensions {
}
type StubRouteObject = StubIndexRouteObject | StubNonIndexRouteObject;
interface RoutesTestStubProps {
/**
* The initial entries in the history stack. This allows you to start a test with
* multiple locations already in the history stack (for testing a back navigation, etc.)
* The test will default to the last entry in initialEntries if no initialIndex is provided.
* e.g. initialEntries={["/home", "/about", "/contact"]}
*/
initialEntries?: InitialEntry[];
/**
* The initial index in the history stack to render. This allows you to start a test at a specific entry.
* It defaults to the last entry in initialEntries.
* e.g.
* initialEntries: ["/", "/events/123"]
* initialIndex: 1 // start at "/events/123"
*/
initialIndex?: number;
/**
* Used to set the route's initial loader and action data.
* e.g. hydrationData={{
* loaderData: { "/contact": { locale: "en-US" } },
* actionData: { "/login": { errors: { email: "invalid email" } }}
* }}
*/
hydrationData?: HydrationState;
/**
* Future flags mimicking the settings in react-router.config.ts
*/
future?: Partial<FutureConfig>;
}
/**
* @category Utils
*/
declare function createRoutesStub(routes: StubRouteObject[], _context?: AppLoadContext | RouterContextProvider): ({ initialEntries, initialIndex, hydrationData, future, }: RoutesTestStubProps) => React.JSX.Element;
interface CookieSignatureOptions {
/**
* An array of secrets that may be used to sign/unsign the value of a cookie.
*
* The array makes it easy to rotate secrets. New secrets should be added to
* the beginning of the array. `cookie.serialize()` will always use the first
* value in the array, but `cookie.parse()` may use any of them so that
* cookies that were signed with older secrets still work.
*/
secrets?: string[];
}
type CookieOptions = ParseOptions & SerializeOptions & CookieSignatureOptions;
/**
* A HTTP cookie.
*
* A Cookie is a logical container for metadata about a HTTP cookie; its name
* and options. But it doesn't contain a value. Instead, it has `parse()` and
* `serialize()` methods that allow a single instance to be reused for
* parsing/encoding multiple different values.
*
* @see https://remix.run/utils/cookies#cookie-api
*/
interface Cookie {
/**
* The name of the cookie, used in the `Cookie` and `Set-Cookie` headers.
*/
readonly name: string;
/**
* True if this cookie uses one or more secrets for verification.
*/
readonly isSigned: boolean;
/**
* The Date this cookie expires.
*
* Note: This is calculated at access time using `maxAge` when no `expires`
* option is provided to `createCookie()`.
*/
readonly expires?: Date;
/**
* Parses a raw `Cookie` header and returns the value of this cookie or
* `null` if it's not present.
*/
parse(cookieHeader: string | null, options?: ParseOptions): Promise<any>;
/**
* Serializes the given value to a string and returns the `Set-Cookie`
* header.
*/
serialize(value: any, options?: SerializeOptions): Promise<string>;
}
/**
* Creates a logical container for managing a browser cookie from the server.
*/
declare const createCookie: (name: string, cookieOptions?: CookieOptions) => Cookie;
type IsCookieFunction = (object: any) => object is Cookie;
/**
* Returns true if an object is a Remix cookie container.
*
* @see https://remix.run/utils/cookies#iscookie
*/
declare const isCookie: IsCookieFunction;
/**
* An object of name/value pairs to be used in the session.
*/
interface SessionData {
[name: string]: any;
}
/**
* Session persists data across HTTP requests.
*
* @see https://reactrouter.com/explanation/sessions-and-cookies#sessions
*/
interface Session<Data = SessionData, FlashData = Data> {
/**
* A unique identifier for this session.
*
* Note: This will be the empty string for newly created sessions and
* sessions that are not backed by a database (i.e. cookie-based sessions).
*/
readonly id: string;
/**
* The raw data contained in this session.
*
* This is useful mostly for SessionStorage internally to access the raw
* session data to persist.
*/
readonly data: FlashSessionData<Data, FlashData>;
/**
* Returns `true` if the session has a value for the given `name`, `false`
* otherwise.
*/
has(name: (keyof Data | keyof FlashData) & string): boolean;
/**
* Returns the value for the given `name` in this session.
*/
get<Key extends (keyof Data | keyof FlashData) & string>(name: Key): (Key extends keyof Data ? Data[Key] : undefined) | (Key extends keyof FlashData ? FlashData[Key] : undefined) | undefined;
/**
* Sets a value in the session for the given `name`.
*/
set<Key extends keyof Data & string>(name: Key, value: Data[Key]): void;
/**
* Sets a value in the session that is only valid until the next `get()`.
* This can be useful for temporary values, like error messages.
*/
flash<Key extends keyof FlashData & string>(name: Key, value: FlashData[Key]): void;
/**
* Removes a value from the session.
*/
unset(name: keyof Data & string): void;
}
type FlashSessionData<Data, FlashData> = Partial<Data & {
[Key in keyof FlashData as FlashDataKey<Key & string>]: FlashData[Key];
}>;
type FlashDataKey<Key extends string> = `__flash_${Key}__`;
type CreateSessionFunction = <Data = SessionData, FlashData = Data>(initialData?: Data, id?: string) => Session<Data, FlashData>;
/**
* Creates a new Session object.
*
* Note: This function is typically not invoked directly by application code.
* Instead, use a `SessionStorage` object's `getSession` method.
*/
declare const createSession: CreateSessionFunction;
type IsSessionFunction = (object: any) => object is Session;
/**
* Returns true if an object is a React Router session.
*
* @see https://reactrouter.com/api/utils/isSession
*/
declare const isSession: IsSessionFunction;
/**
* SessionStorage stores session data between HTTP requests and knows how to
* parse and create cookies.
*
* A SessionStorage creates Session objects using a `Cookie` header as input.
* Then, later it generates the `Set-Cookie` header to be used in the response.
*/
interface SessionStorage<Data = SessionData, FlashData = Data> {
/**
* Parses a Cookie header from a HTTP request and returns the associated
* Session. If there is no session associated with the cookie, this will
* return a new Session with no data.
*/
getSession: (cookieHeader?: string | null, options?: ParseOptions) => Promise<Session<Data, FlashData>>;
/**
* Stores all data in the Session and returns the Set-Cookie header to be
* used in the HTTP response.
*/
commitSession: (session: Session<Data, FlashData>, options?: SerializeOptions) => Promise<string>;
/**
* Deletes all data associated with the Session and returns the Set-Cookie
* header to be used in the HTTP response.
*/
destroySession: (session: Session<Data, FlashData>, options?: SerializeOptions) => Promise<string>;
}
/**
* SessionIdStorageStrategy is designed to allow anyone to easily build their
* own SessionStorage using `createSessionStorage(strategy)`.
*
* This strategy describes a common scenario where the session id is stored in
* a cookie but the actual session data is stored elsewhere, usually in a
* database or on disk. A set of create, read, update, and delete operations
* are provided for managing the session data.
*/
interface SessionIdStorageStrategy<Data = SessionData, FlashData = Data> {
/**
* The Cookie used to store the session id, or options used to automatically
* create one.
*/
cookie?: Cookie | (CookieOptions & {
name?: string;
});
/**
* Creates a new record with the given data and returns the session id.
*/
createData: (data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<string>;
/**
* Returns data for a given session id, or `null` if there isn't any.
*/
readData: (id: string) => Promise<FlashSessionData<Data, FlashData> | null>;
/**
* Updates data for the given session id.
*/
updateData: (id: string, data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<void>;
/**
* Deletes data for a given session id from the data store.
*/
deleteData: (id: string) => Promise<void>;
}
/**
* Creates a SessionStorage object using a SessionIdStorageStrategy.
*
* Note: This is a low-level API that should only be used if none of the
* existing session storage options meet your requirements.
*/
declare function createSessionStorage<Data = SessionData, FlashData = Data>({ cookie: cookieArg, createData, readData, updateData, deleteData, }: SessionIdStorageStrategy<Data, FlashData>): SessionStorage<Data, FlashData>;
interface CookieSessionStorageOptions {
/**
* The Cookie used to store the session data on the client, or options used
* to automatically create one.
*/
cookie?: SessionIdStorageStrategy["cookie"];
}
/**
* Creates and returns a SessionStorage object that stores all session data
* directly in the session cookie itself.
*
* This has the advantage that no database or other backend services are
* needed, and can help to simplify some load-balanced scenarios. However, it
* also has the limitation that serialized session data may not exceed the
* browser's maximum cookie size. Trade-offs!
*/
declare function createCookieSessionStorage<Data = SessionData, FlashData = Data>({ cookie: cookieArg }?: CookieSessionStorageOptions): SessionStorage<Data, FlashData>;
interface MemorySessionStorageOptions {
/**
* The Cookie used to store the session id on the client, or options used
* to automatically create one.
*/
cookie?: SessionIdStorageStrategy["cookie"];
}
/**
* Creates and returns a simple in-memory SessionStorage object, mostly useful
* for testing and as a reference implementation.
*
* Note: This storage does not scale beyond a single process, so it is not
* suitable for most production scenarios.
*/
declare function createMemorySessionStorage<Data = SessionData, FlashData = Data>({ cookie }?: MemorySessionStorageOptions): SessionStorage<Data, FlashData>;
type DevServerHooks = {
getCriticalCss?: (pathname: string) => Promise<string | undefined>;
processRequestError?: (error: unknown) => void;
};
declare function setDevServerHooks(devServerHooks: DevServerHooks): void;
type Args = {
[K in keyof Pages]: ToArgs<Pages[K]["params"]>;
};
type ToArgs<Params extends Record<string, string | undefined>> = Equal<Params, {}> extends true ? [] : Partial<Params> extends Params ? [Params] | [] : [
Params
];
/**
Returns a resolved URL path for the specified route.
```tsx
const h = href("/:lang?/about", { lang: "en" })
// -> `/en/about`
<Link to={href("/products/:id", { id: "abc123" })} />
```
*/
declare function href<Path extends keyof Args>(path: Path, ...args: Args[Path]): string;
type DecodedPayload = Promise<RSCPayload> & {
_deepestRenderedBoundaryId?: string | null;
formState: Promise<any>;
};
type SSRCreateFromReadableStreamFunction = (body: ReadableStream<Uint8Array>) => Promise<unknown>;
/**
* Routes the incoming [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* to the [RSC](https://react.dev/reference/rsc/server-components) server and
* appropriately proxies the server response for data / resource requests, or
* renders to HTML for a document request.
*
* @example
* import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
* import * as ReactDomServer from "react-dom/server.edge";
* import {
* unstable_RSCStaticRouter as RSCStaticRouter,
* unstable_routeRSCServerRequest as routeRSCServerRequest,
* } from "react-router";
*
* routeRSCServerRequest({
* request,
* serverResponse,
* createFromReadableStream,
* async renderHTML(getPayload) {
* const payload = getPayload();
*
* return await renderHTMLToReadableStream(
* <RSCStaticRouter getPayload={getPayload} />,
* {
* bootstrapScriptContent,
* formState: await payload.formState,
* }
* );
* },
* });
*
* @name unstable_routeRSCServerRequest
* @public
* @category RSC
* @mode data
* @param opts Options
* @param opts.createFromReadableStream Your `react-server-dom-xyz/client`'s
* `createFromReadableStream` function, used to decode payloads from the server.
* @param opts.serverResponse A Response or partial response generated by the [RSC](https://react.dev/reference/rsc/server-components) handler containing a serialized {@link unstable_RSCPayload}.
* @param opts.hydrate Whether to hydrate the server response with the RSC payload.
* Defaults to `true`.
* @param opts.renderHTML A function that renders the {@link unstable_RSCPayload} to
* HTML, usually using a {@link unstable_RSCStaticRouter | `<RSCStaticRouter>`}.
* @param opts.request The request to route.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that either contains the [RSC](https://react.dev/reference/rsc/server-components)
* payload for data requests, or renders the HTML for document requests.
*/
declare function routeRSCServerRequest({ request, serverResponse, createFromReadableStream, renderHTML, hydrate, }: {
request: Request;
serverResponse: Response;
createFromReadableStream: SSRCreateFromReadableStreamFunction;
renderHTML: (getPayload: () => DecodedPayload, options: {
onError(error: unknown): string | undefined;
onHeaders(headers: Headers): void;
}) => ReadableStream<Uint8Array> | Promise<ReadableStream<Uint8Array>>;
hydrate?: boolean;
}): Promise<Response>;
/**
* Props for the {@link unstable_RSCStaticRouter} component.
*
* @name unstable_RSCStaticRouterProps
* @category Types
*/
interface RSCStaticRouterProps {
/**
* A function that starts decoding of the {@link unstable_RSCPayload}. Usually passed
* through from {@link unstable_routeRSCServerRequest}'s `renderHTML`.
*/
getPayload: () => DecodedPayload;
}
/**
* Pre-renders an {@link unstable_RSCPayload} to HTML. Usually used in
* {@link unstable_routeRSCServerRequest}'s `renderHTML` callback.
*
* @example
* import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
* import * as ReactDomServer from "react-dom/server.edge";
* import {
* unstable_RSCStaticRouter as RSCStaticRouter,
* unstable_routeRSCServerRequest as routeRSCServerRequest,
* } from "react-router";
*
* routeRSCServerRequest({
* request,
* serverResponse,
* createFromReadableStream,
* async renderHTML(getPayload) {
* const payload = getPayload();
*
* return await renderHTMLToReadableStream(
* <RSCStaticRouter getPayload={getPayload} />,
* {
* bootstrapScriptContent,
* formState: await payload.formState,
* }
* );
* },
* });
*
* @name unstable_RSCStaticRouter
* @public
* @category RSC
* @mode data
* @param props Props
* @param {unstable_RSCStaticRouterProps.getPayload} props.getPayload n/a
* @returns A React component that renders the {@link unstable_RSCPayload} as HTML.
*/
declare function RSCStaticRouter({ getPayload }: RSCStaticRouterProps): React.JSX.Element | null;
declare function RSCDefaultRootErrorBoundary({ hasRootLayout, }: {
hasRootLayout: boolean;
}): React__default.JSX.Element;
type RemixErrorBoundaryProps = React.PropsWithChildren<{
location: Location;
isOutsideRemixApp?: boolean;
error?: Error;
}>;
type RemixErrorBoundaryState = {
error: null | Error;
location: Location;
};
declare class RemixErrorBoundary extends React.Component<RemixErrorBoundaryProps, RemixErrorBoundaryState> {
constructor(props: RemixErrorBoundaryProps);
static getDerivedStateFromError(error: Error): {
error: Error;
};
static getDerivedStateFromProps(props: RemixErrorBoundaryProps, state: RemixErrorBoundaryState): {
error: Error | null;
location: Location<any>;
};
render(): string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null | undefined;
}
declare function getPatchRoutesOnNavigationFunction(getRouter: () => Router, manifest: AssetsManifest, routeModules: RouteModules, ssr: boolean, routeDiscovery: ServerBuild["routeDiscovery"], isSpaMode: boolean, basename: string | undefined): PatchRoutesOnNavigationFunction | undefined;
declare function useFogOFWarDiscovery(router: Router, manifest: AssetsManifest, routeModules: RouteModules, ssr: boolean, routeDiscovery: ServerBuild["routeDiscovery"], isSpaMode: boolean): void;
declare function getHydrationData({ state, routes, getRouteInfo, location, basename, isSpaMode, }: {
state: {
loaderData?: Router["state"]["loaderData"];
actionData?: Router["state"]["actionData"];
errors?: Router["state"]["errors"];
};
routes: DataRouteObject[];
getRouteInfo: (routeId: string) => {
clientLoader: ClientLoaderFunction | undefined;
hasLoader: boolean;
hasHydrateFallback: boolean;
};
location: Path;
basename: string | undefined;
isSpaMode: boolean;
}): HydrationState;
/**
* @module index
* @mergeModuleWith react-router
*/
declare const unstable_getRequest: typeof getRequest;
declare const unstable_matchRSCServerRequest: typeof matchRSCServerRequest;
export { ActionFunction, AppLoadContext, Blocker, BlockerFunction, ClientLoaderFunction, type Cookie, type CookieOptions, type CookieSignatureOptions, type CreateRequestHandlerFunction, DataRouteObject, Router as DataRouter, DataStrategyFunction, EntryContext, type FlashSessionData, HydrationState, IndexRouteObject, InitialEntry, type IsCookieFunction, type IsSessionFunction, LinksFunction, LoaderFunction, Location, MetaFunction, type NavigateFunction, NavigateOptions, NavigationStates, Action as NavigationType, NonIndexRouteObject, ParamParseKey, Params, PatchRoutesOnNavigationFunction, Path, PathMatch, PathPattern, RelativeRoutingType, type RequestHandler, RouteObject, RouterContextProvider, type RoutesTestStubProps, ServerBuild, ServerRouter, type ServerRouterProps, type Session, type SessionData, type SessionIdStorageStrategy, type SessionStorage, To, UIMatch, AssetsManifest as UNSAFE_AssetsManifest, MiddlewareEnabled as UNSAFE_MiddlewareEnabled, RSCDefaultRootErrorBoundary as UNSAFE_RSCDefaultRootErrorBoundary, RemixErrorBoundary as UNSAFE_RemixErrorBoundary, RouteModules as UNSAFE_RouteModules, ServerMode as UNSAFE_ServerMode, SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol, decodeViaTurboStream as UNSAFE_decodeViaTurboStream, getHydrationData as UNSAFE_getHydrationData, getPatchRoutesOnNavigationFunction as UNSAFE_getPatchRoutesOnNavigationFunction, getTurboStreamSingleFetchDataStrategy as UNSAFE_getTurboStreamSingleFetchDataStrategy, useFogOFWarDiscovery as UNSAFE_useFogOFWarDiscovery, createCookie, createCookieSessionStorage, createMemorySessionStorage, createRequestHandler, createRoutesStub, createSession, createSessionStorage, href, isCookie, isSession, RSCPayload as unstable_RSCPayload, RSCStaticRouter as unstable_RSCStaticRouter, type RSCStaticRouterProps as unstable_RSCStaticRouterProps, type unstable_RouterState, type unstable_RouterStateActiveVariant, type unstable_RouterStatePendingVariant, type SSRCreateFromReadableStreamFunction as unstable_SSRCreateFromReadableStreamFunction, unstable_getRequest, unstable_matchRSCServerRequest, routeRSCServerRequest as unstable_routeRSCServerRequest, setDevServerHooks as unstable_setDevServerHooks, useRoute as unstable_useRoute, useRouterState as unstable_useRouterState, useActionData, useAsyncError, useAsyncValue, useBlocker, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes };
+1479
View File
@@ -0,0 +1,1479 @@
import { O as RouteModules, l as DataStrategyFunction, t as MiddlewareEnabled, c as RouterContextProvider, u as AppLoadContext, T as To, L as Location, P as Params, U as UIMatch, i as Action, Q as SerializeFrom, V as PathPattern, W as PathMatch, X as ParamParseKey, r as Path, e as RouteObject, G as GetLoaderData, K as GetActionData, Y as InitialEntry, Z as IndexRouteObject, d as LoaderFunction, A as ActionFunction, M as MetaFunction, b as LinksFunction, _ as NonIndexRouteObject, $ as Equal, m as PatchRoutesOnNavigationFunction, n as DataRouteObject, a as ClientLoaderFunction } from './data-CjO11-hU.js';
export { a0 as ActionFunctionArgs, a1 as BaseRouteObject, C as ClientActionFunction, ar as ClientActionFunctionArgs, as as ClientLoaderFunctionArgs, D as DataRouteMatch, a2 as DataStrategyFunctionArgs, a3 as DataStrategyMatch, I as DataStrategyResult, a5 as ErrorResponse, F as FormEncType, a6 as FormMethod, ax as Future, q as HTMLFormMethod, at as HeadersArgs, H as HeadersFunction, aw as HtmlLinkDescriptor, a7 as LazyRouteFunction, v as LinkDescriptor, s as LoaderFunctionArgs, au as MetaArgs, y as MetaDescriptor, a8 as MiddlewareFunction, av as PageLinkDescriptor, a9 as PatchRoutesOnNavigationFunctionArgs, aa as PathParam, ab as RedirectFunction, ac as RouteMatch, ad as RouterContext, S as ShouldRevalidateFunction, ae as ShouldRevalidateFunctionArgs, a4 as UNSAFE_DataWithResponseInit, aD as UNSAFE_ErrorResponseImpl, aA as UNSAFE_createBrowserHistory, aB as UNSAFE_createHashHistory, az as UNSAFE_createMemoryHistory, aC as UNSAFE_invariant, af as createContext, ag as createPath, ai as data, aj as generatePath, ak as isRouteErrorResponse, al as matchPath, am as matchRoutes, ah as parsePath, an as redirect, ao as redirectDocument, ap as replace, aq as resolvePath, ay as unstable_SerializesTo } from './data-CjO11-hU.js';
import { a as Router, N as NavigationStates, B as BlockerFunction, b as Blocker, c as RelativeRoutingType, H as HydrationState } from './instrumentation-Dkmpzd13.js';
export { C as ClientInstrumentation, F as Fetcher, G as GetScrollPositionFunction, d as GetScrollRestorationKeyFunction, r as IDLE_BLOCKER, q as IDLE_FETCHER, p as IDLE_NAVIGATION, I as InstrumentRequestHandlerFunction, n as InstrumentRouteFunction, m as InstrumentRouterFunction, o as InstrumentationHandlerResult, f as Navigation, k as RevalidationState, j as RouterFetchOptions, R as RouterInit, i as RouterNavigateOptions, g as RouterState, h as RouterSubscriber, l as ServerInstrumentation, S as StaticHandler, e as StaticHandlerContext, s as UNSAFE_createRouter } from './instrumentation-Dkmpzd13.js';
import { A as AssetsManifest, S as ServerBuild, N as NavigateOptions, E as EntryContext, F as FutureConfig } from './index-react-server-client-3ykjivgQ.js';
export { i as Await, c as AwaitProps, W as BrowserRouter, B as BrowserRouterProps, C as ClientOnErrorFunction, D as DOMRouterOpts, at as DiscoverBehavior, y as FetcherFormProps, Q as FetcherSubmitFunction, aa as FetcherSubmitOptions, T as FetcherWithComponents, $ as Form, z as FormProps, au as HandleDataRequestFunction, av as HandleDocumentRequestFunction, aw as HandleErrorFunction, X as HashRouter, H as HashRouterProps, u as HistoryRouterProps, I as IndexRouteProps, L as LayoutRouteProps, Y as Link, v as LinkProps, an as Links, aq as LinksProps, j as MemoryRouter, M as MemoryRouterOpts, d as MemoryRouterProps, am as Meta, _ as NavLink, w as NavLinkProps, x as NavLinkRenderProps, k as Navigate, e as NavigateProps, a as Navigator, l as Outlet, O as OutletProps, ab as ParamKeyValuePair, P as PathRouteProps, as as PrefetchBehavior, ap as PrefetchPageLinks, m as Route, R as RouteProps, n as Router, f as RouterProps, o as RouterProvider, g as RouterProviderProps, p as Routes, h as RoutesProps, ao as Scripts, ar as ScriptsProps, a0 as ScrollRestoration, G as ScrollRestorationProps, ax as ServerEntryModule, J as SetURLSearchParams, ak as StaticRouter, ag as StaticRouterProps, al as StaticRouterProvider, ah as StaticRouterProviderProps, K as SubmitFunction, ac as SubmitOptions, ae as SubmitTarget, b as UNSAFE_AwaitContextProvider, ay as UNSAFE_DataRouterContext, az as UNSAFE_DataRouterStateContext, aA as UNSAFE_FetchersContext, aN as UNSAFE_FrameworkContext, aB as UNSAFE_LocationContext, aC as UNSAFE_NavigationContext, aD as UNSAFE_RouteContext, aE as UNSAFE_ViewTransitionContext, aH as UNSAFE_WithComponentProps, aL as UNSAFE_WithErrorBoundaryProps, aJ as UNSAFE_WithHydrateFallbackProps, aO as UNSAFE_createClientRoutes, aP as UNSAFE_createClientRoutesWithHMRRevalidationOptOut, aF as UNSAFE_hydrationRouteProperties, aG as UNSAFE_mapRouteProperties, aQ as UNSAFE_shouldHydrateRouteLoader, aR as UNSAFE_useScrollRestoration, aI as UNSAFE_withComponentProps, aM as UNSAFE_withErrorBoundaryProps, aK as UNSAFE_withHydrateFallbackProps, ad as URLSearchParamsInit, U as createBrowserRouter, V as createHashRouter, q as createMemoryRouter, r as createRoutesFromChildren, s as createRoutesFromElements, af as createSearchParams, ai as createStaticHandler, aj as createStaticRouter, t as renderMatches, Z as unstable_HistoryRouter, a8 as unstable_usePrompt, a7 as useBeforeUnload, a5 as useFetcher, a6 as useFetchers, a4 as useFormAction, a1 as useLinkClickHandler, a2 as useSearchParams, a3 as useSubmit, a9 as useViewTransitionState } from './index-react-server-client-3ykjivgQ.js';
import * as React from 'react';
import React__default, { ReactElement } from 'react';
import { a as RouteModules$1, P as Pages } from './register-roq_0qYo.js';
export { b as Register } from './register-roq_0qYo.js';
import { ParseOptions, SerializeOptions } from 'cookie';
export { ParseOptions as CookieParseOptions, SerializeOptions as CookieSerializeOptions } from 'cookie';
import { e as RSCPayload, g as getRequest, m as matchRSCServerRequest } from './browser-B2PdsXXH.js';
export { B as unstable_BrowserCreateFromReadableStreamFunction, D as unstable_DecodeActionFunction, a as unstable_DecodeFormStateFunction, b as unstable_DecodeReplyFunction, E as unstable_EncodeReplyFunction, L as unstable_LoadServerActionFunction, h as unstable_RSCHydratedRouterProps, d as unstable_RSCManifestPayload, i as unstable_RSCMatch, f as unstable_RSCRenderPayload, n as unstable_RSCRouteConfig, l as unstable_RSCRouteConfigEntry, j as unstable_RSCRouteManifest, k as unstable_RSCRouteMatch } from './browser-B2PdsXXH.js';
declare const SingleFetchRedirectSymbol: unique symbol;
declare function getTurboStreamSingleFetchDataStrategy(getRouter: () => Router, manifest: AssetsManifest, routeModules: RouteModules, ssr: boolean, basename: string | undefined, trailingSlashAware: boolean): DataStrategyFunction;
declare function decodeViaTurboStream(body: ReadableStream<Uint8Array>, global: Window | typeof globalThis): Promise<{
done: Promise<undefined>;
value: unknown;
}>;
/**
* The mode to use when running the server.
*/
declare enum ServerMode {
Development = "development",
Production = "production",
Test = "test"
}
type RequestHandler = (request: Request, loadContext?: MiddlewareEnabled extends true ? RouterContextProvider : AppLoadContext) => Promise<Response>;
type CreateRequestHandlerFunction = (build: ServerBuild | (() => ServerBuild | Promise<ServerBuild>), mode?: string) => RequestHandler;
declare const createRequestHandler: CreateRequestHandlerFunction;
/**
* Resolves a URL against the current {@link Location}.
*
* @example
* import { useHref } from "react-router";
*
* function SomeComponent() {
* let href = useHref("some/where");
* // "/resolved/some/where"
* }
*
* @public
* @category Hooks
* @param to The path to resolve
* @param options Options
* @param options.relative Defaults to `"route"` so routing is relative to the
* route tree.
* Set to `"path"` to make relative routing operate against path segments.
* @returns The resolved href string
*/
declare function useHref(to: To, { relative }?: {
relative?: RelativeRoutingType;
}): string;
/**
* Returns `true` if this component is a descendant of a {@link Router}, useful
* to ensure a component is used within a {@link Router}.
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns Whether the component is within a {@link Router} context
*/
declare function useInRouterContext(): boolean;
/**
* Returns the current {@link Location}. This can be useful if you'd like to
* perform some side effect whenever it changes.
*
* @example
* import * as React from 'react'
* import { useLocation } from 'react-router'
*
* function SomeComponent() {
* let location = useLocation()
*
* React.useEffect(() => {
* // Google Analytics
* ga('send', 'pageview')
* }, [location]);
*
* return (
* // ...
* );
* }
*
* @public
* @category Hooks
* @returns The current {@link Location} object
*/
declare function useLocation(): Location;
/**
* Returns the current {@link Navigation} action which describes how the router
* came to the current {@link Location}, either by a pop, push, or replace on
* the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History) stack.
*
* @public
* @category Hooks
* @returns The current {@link NavigationType} (`"POP"`, `"PUSH"`, or `"REPLACE"`)
*/
declare function useNavigationType(): Action;
/**
* Returns a {@link PathMatch} object if the given pattern matches the current URL.
* This is useful for components that need to know "active" state, e.g.
* {@link NavLink | `<NavLink>`}.
*
* @public
* @category Hooks
* @param pattern The pattern to match against the current {@link Location}
* @returns The path match object if the pattern matches, `null` otherwise
*/
declare function useMatch<Path extends string>(pattern: PathPattern<Path> | Path): PathMatch<ParamParseKey<Path>> | null;
/**
* The interface for the `navigate` function returned from {@link useNavigate}.
*/
interface NavigateFunction {
(to: To, options?: NavigateOptions): void | Promise<void>;
(delta: number): void | Promise<void>;
}
/**
* Returns a function that lets you navigate programmatically in the browser in
* response to user interactions or effects.
*
* It's often better to use {@link redirect} in [`action`](../../start/framework/route-module#action)/[`loader`](../../start/framework/route-module#loader)
* functions than this hook.
*
* The returned function signature is `navigate(to, options?)`/`navigate(delta)` where:
*
* * `to` can be a string path, a {@link To} object, or a number (delta)
* * `options` contains options for modifying the navigation
* * These options work in all modes (Framework, Data, and Declarative):
* * `relative`: `"route"` or `"path"` to control relative routing logic
* * `replace`: Replace the current entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History) stack
* * `state`: Optional [`history.state`](https://developer.mozilla.org/en-US/docs/Web/API/History/state) to include with the new {@link Location}
* * These options only work in Framework and Data modes:
* * `flushSync`: Wrap the DOM updates in [`ReactDom.flushSync`](https://react.dev/reference/react-dom/flushSync)
* * `preventScrollReset`: Do not scroll back to the top of the page after navigation
* * `viewTransition`: Enable [`document.startViewTransition`](https://developer.mozilla.org/en-US/docs/Web/API/Document/startViewTransition) for this navigation
*
* @example
* import { useNavigate } from "react-router";
*
* function SomeComponent() {
* let navigate = useNavigate();
* return (
* <button onClick={() => navigate(-1)}>
* Go Back
* </button>
* );
* }
*
* @additionalExamples
* ### Navigate to another path
*
* ```tsx
* navigate("/some/route");
* navigate("/some/route?search=param");
* ```
*
* ### Navigate with a {@link To} object
*
* All properties are optional.
*
* ```tsx
* navigate({
* pathname: "/some/route",
* search: "?search=param",
* hash: "#hash",
* state: { some: "state" },
* });
* ```
*
* If you use `state`, that will be available on the {@link Location} object on
* the next page. Access it with `useLocation().state` (see {@link useLocation}).
*
* ### Navigate back or forward in the history stack
*
* ```tsx
* // back
* // often used to close modals
* navigate(-1);
*
* // forward
* // often used in a multistep wizard workflows
* navigate(1);
* ```
*
* Be cautious with `navigate(number)`. If your application can load up to a
* route that has a button that tries to navigate forward/back, there may not be
* a [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* entry to go back or forward to, or it can go somewhere you don't expect
* (like a different domain).
*
* Only use this if you're sure they will have an entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack to navigate to.
*
* ### Replace the current entry in the history stack
*
* This will remove the current entry in the [`History`](https://developer.mozilla.org/en-US/docs/Web/API/History)
* stack, replacing it with a new one, similar to a server side redirect.
*
* ```tsx
* navigate("/some/route", { replace: true });
* ```
*
* ### Prevent Scroll Reset
*
* [MODES: framework, data]
*
* <br/>
* <br/>
*
* To prevent {@link ScrollRestoration | `<ScrollRestoration>`} from resetting
* the scroll position, use the `preventScrollReset` option.
*
* ```tsx
* navigate("?some-tab=1", { preventScrollReset: true });
* ```
*
* For example, if you have a tab interface connected to search params in the
* middle of a page, and you don't want it to scroll to the top when a tab is
* clicked.
*
* ### Return Type Augmentation
*
* Internally, `useNavigate` uses a separate implementation when you are in
* Declarative mode versus Data/Framework mode - the primary difference being
* that the latter is able to return a stable reference that does not change
* identity across navigations. The implementation in Data/Framework mode also
* returns a [`Promise`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise)
* that resolves when the navigation is completed. This means the return type of
* `useNavigate` is `void | Promise<void>`. This is accurate, but can lead to
* some red squigglies based on the union in the return value:
*
* - If you're using `typescript-eslint`, you may see errors from
* [`@typescript-eslint/no-floating-promises`](https://typescript-eslint.io/rules/no-floating-promises)
* - In Framework/Data mode, `React.use(navigate())` will show a false-positive
* `Argument of type 'void | Promise<void>' is not assignable to parameter of
* type 'Usable<void>'` error
*
* The easiest way to work around these issues is to augment the type based on the
* router you're using:
*
* ```ts
* // If using <BrowserRouter>
* declare module "react-router" {
* interface NavigateFunction {
* (to: To, options?: NavigateOptions): void;
* (delta: number): void;
* }
* }
*
* // If using <RouterProvider> or Framework mode
* declare module "react-router" {
* interface NavigateFunction {
* (to: To, options?: NavigateOptions): Promise<void>;
* (delta: number): Promise<void>;
* }
* }
* ```
*
* @public
* @category Hooks
* @returns A navigate function for programmatic navigation
*/
declare function useNavigate(): NavigateFunction;
/**
* Returns the parent route {@link Outlet | `<Outlet context>`}.
*
* Often parent routes manage state or other values you want shared with child
* routes. You can create your own [context provider](https://react.dev/learn/passing-data-deeply-with-context)
* if you like, but this is such a common situation that it's built-into
* {@link Outlet | `<Outlet>`}.
*
* ```tsx
* // Parent route
* function Parent() {
* const [count, setCount] = React.useState(0);
* return <Outlet context={[count, setCount]} />;
* }
* ```
*
* ```tsx
* // Child route
* import { useOutletContext } from "react-router";
*
* function Child() {
* const [count, setCount] = useOutletContext();
* const increment = () => setCount((c) => c + 1);
* return <button onClick={increment}>{count}</button>;
* }
* ```
*
* If you're using TypeScript, we recommend the parent component provide a
* custom hook for accessing the context value. This makes it easier for
* consumers to get nice typings, control consumers, and know who's consuming
* the context value.
*
* Here's a more realistic example:
*
* ```tsx filename=src/routes/dashboard.tsx lines=[14,20]
* import { useState } from "react";
* import { Outlet, useOutletContext } from "react-router";
*
* import type { User } from "./types";
*
* type ContextType = { user: User | null };
*
* export default function Dashboard() {
* const [user, setUser] = useState<User | null>(null);
*
* return (
* <div>
* <h1>Dashboard</h1>
* <Outlet context={{ user } satisfies ContextType} />
* </div>
* );
* }
*
* export function useUser() {
* return useOutletContext<ContextType>();
* }
* ```
*
* ```tsx filename=src/routes/dashboard/messages.tsx lines=[1,4]
* import { useUser } from "../dashboard";
*
* export default function DashboardMessages() {
* const { user } = useUser();
* return (
* <div>
* <h2>Messages</h2>
* <p>Hello, {user.name}!</p>
* </div>
* );
* }
* ```
*
* @public
* @category Hooks
* @returns The context value passed to the parent {@link Outlet} component
*/
declare function useOutletContext<Context = unknown>(): Context;
/**
* Returns the element for the child route at this level of the route
* hierarchy. Used internally by {@link Outlet | `<Outlet>`} to render child
* routes.
*
* @public
* @category Hooks
* @param context The context to pass to the outlet
* @returns The child route element or `null` if no child routes match
*/
declare function useOutlet(context?: unknown): React.ReactElement | null;
/**
* Returns an object of key/value-pairs of the dynamic params from the current
* URL that were matched by the routes. Child routes inherit all params from
* their parent routes.
*
* Assuming a route pattern like `/posts/:postId` is matched by `/posts/123`
* then `params.postId` will be `"123"`.
*
* @example
* import { useParams } from "react-router";
*
* function SomeComponent() {
* let params = useParams();
* params.postId;
* }
*
* @additionalExamples
* ### Basic Usage
*
* ```tsx
* import { useParams } from "react-router";
*
* // given a route like:
* <Route path="/posts/:postId" element={<Post />} />;
*
* // or a data route like:
* createBrowserRouter([
* {
* path: "/posts/:postId",
* component: Post,
* },
* ]);
*
* // or in routes.ts
* route("/posts/:postId", "routes/post.tsx");
* ```
*
* Access the params in a component:
*
* ```tsx
* import { useParams } from "react-router";
*
* export default function Post() {
* let params = useParams();
* return <h1>Post: {params.postId}</h1>;
* }
* ```
*
* ### Multiple Params
*
* Patterns can have multiple params:
*
* ```tsx
* "/posts/:postId/comments/:commentId";
* ```
*
* All will be available in the params object:
*
* ```tsx
* import { useParams } from "react-router";
*
* export default function Post() {
* let params = useParams();
* return (
* <h1>
* Post: {params.postId}, Comment: {params.commentId}
* </h1>
* );
* }
* ```
*
* ### Catchall Params
*
* Catchall params are defined with `*`:
*
* ```tsx
* "/files/*";
* ```
*
* The matched value will be available in the params object as follows:
*
* ```tsx
* import { useParams } from "react-router";
*
* export default function File() {
* let params = useParams();
* let catchall = params["*"];
* // ...
* }
* ```
*
* You can destructure the catchall param:
*
* ```tsx
* export default function File() {
* let { "*": catchall } = useParams();
* console.log(catchall);
* }
* ```
*
* @public
* @category Hooks
* @returns An object containing the dynamic route parameters
*/
declare function useParams<ParamsOrKey extends string | Record<string, string | undefined> = string>(): Readonly<[
ParamsOrKey
] extends [string] ? Params<ParamsOrKey> : Partial<ParamsOrKey>>;
/**
* Resolves the pathname of the given `to` value against the current
* {@link Location}. Similar to {@link useHref}, but returns a
* {@link Path} instead of a string.
*
* @example
* import { useResolvedPath } from "react-router";
*
* function SomeComponent() {
* // if the user is at /dashboard/profile
* let path = useResolvedPath("../accounts");
* path.pathname; // "/dashboard/accounts"
* path.search; // ""
* path.hash; // ""
* }
*
* @public
* @category Hooks
* @param to The path to resolve
* @param options Options
* @param options.relative Defaults to `"route"` so routing is relative to the route tree.
* Set to `"path"` to make relative routing operate against path segments.
* @returns The resolved {@link Path} object with `pathname`, `search`, and `hash`
*/
declare function useResolvedPath(to: To, { relative }?: {
relative?: RelativeRoutingType;
}): Path;
/**
* Hook version of {@link Routes | `<Routes>`} that uses objects instead of
* components. These objects have the same properties as the component props.
* The return value of `useRoutes` is either a valid React element you can use
* to render the route tree, or `null` if nothing matched.
*
* @example
* import { useRoutes } from "react-router";
*
* function App() {
* let element = useRoutes([
* {
* path: "/",
* element: <Dashboard />,
* children: [
* {
* path: "messages",
* element: <DashboardMessages />,
* },
* { path: "tasks", element: <DashboardTasks /> },
* ],
* },
* { path: "team", element: <AboutPage /> },
* ]);
*
* return element;
* }
*
* @public
* @category Hooks
* @param routes An array of {@link RouteObject}s that define the route hierarchy
* @param locationArg An optional {@link Location} object or pathname string to
* use instead of the current {@link Location}
* @returns A React element to render the matched route, or `null` if no routes matched
*/
declare function useRoutes(routes: RouteObject[], locationArg?: Partial<Location> | string): React.ReactElement | null;
type UseNavigationResult = UseNavigationResultStates[keyof UseNavigationResultStates];
type UseNavigationResultStates = {
Idle: Omit<NavigationStates["Idle"], "matches" | "historyAction">;
Loading: Omit<NavigationStates["Loading"], "matches" | "historyAction">;
Submitting: Omit<NavigationStates["Submitting"], "matches" | "historyAction">;
};
/**
* Returns the current {@link Navigation}, defaulting to an "idle" navigation
* when no navigation is in progress. You can use this to render pending UI
* (like a global spinner) or read [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData)
* from a form navigation.
*
* @example
* import { useNavigation } from "react-router";
*
* function SomeComponent() {
* let navigation = useNavigation();
* navigation.state;
* navigation.formData;
* // etc.
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The current {@link Navigation} object
*/
declare function useNavigation(): UseNavigationResult;
/**
* Revalidate the data on the page for reasons outside of normal data mutations
* like [`Window` focus](https://developer.mozilla.org/en-US/docs/Web/API/Window/focus_event)
* or polling on an interval.
*
* Note that page data is already revalidated automatically after actions.
* If you find yourself using this for normal CRUD operations on your data in
* response to user interactions, you're probably not taking advantage of the
* other APIs like {@link useFetcher}, {@link Form}, {@link useSubmit} that do
* this automatically.
*
* @example
* import { useRevalidator } from "react-router";
*
* function WindowFocusRevalidator() {
* const revalidator = useRevalidator();
*
* useFakeWindowFocus(() => {
* revalidator.revalidate();
* });
*
* return (
* <div hidden={revalidator.state === "idle"}>
* Revalidating...
* </div>
* );
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns An object with a `revalidate` function and the current revalidation
* `state`
*/
declare function useRevalidator(): {
revalidate: () => Promise<void>;
state: Router["state"]["revalidation"];
};
/**
* Returns the active route matches, useful for accessing `loaderData` for
* parent/child routes or the route [`handle`](../../start/framework/route-module#handle)
* property
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns An array of {@link UIMatch | UI matches} for the current route hierarchy
*/
declare function useMatches(): UIMatch[];
/**
* Returns the data from the closest route
* [`loader`](../../start/framework/route-module#loader) or
* [`clientLoader`](../../start/framework/route-module#clientloader).
*
* @example
* import { useLoaderData } from "react-router";
*
* export async function loader() {
* return await fakeDb.invoices.findAll();
* }
*
* export default function Invoices() {
* let invoices = useLoaderData<typeof loader>();
* // ...
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The data returned from the route's [`loader`](../../start/framework/route-module#loader) or [`clientLoader`](../../start/framework/route-module#clientloader) function
*/
declare function useLoaderData<T = any>(): SerializeFrom<T>;
/**
* Returns the [`loader`](../../start/framework/route-module#loader) data for a
* given route by route ID.
*
* Route IDs are created automatically. They are simply the path of the route file
* relative to the app folder without the extension.
*
* | Route Filename | Route ID |
* | ---------------------------- | ---------------------- |
* | `app/root.tsx` | `"root"` |
* | `app/routes/teams.tsx` | `"routes/teams"` |
* | `app/whatever/teams.$id.tsx` | `"whatever/teams.$id"` |
*
* @example
* import { useRouteLoaderData } from "react-router";
*
* function SomeComponent() {
* const { user } = useRouteLoaderData("root");
* }
*
* // You can also specify your own route ID's manually in your routes.ts file:
* route("/", "containers/app.tsx", { id: "app" })
* useRouteLoaderData("app");
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param routeId The ID of the route to return loader data from
* @returns The data returned from the specified route's [`loader`](../../start/framework/route-module#loader)
* function, or `undefined` if not found
*/
declare function useRouteLoaderData<T = any>(routeId: string): SerializeFrom<T> | undefined;
/**
* Returns the [`action`](../../start/framework/route-module#action) data from
* the most recent `POST` navigation form submission or `undefined` if there
* hasn't been one.
*
* @example
* import { Form, useActionData } from "react-router";
*
* export async function action({ request }) {
* const body = await request.formData();
* const name = body.get("visitorsName");
* return { message: `Hello, ${name}` };
* }
*
* export default function Invoices() {
* const data = useActionData();
* return (
* <Form method="post">
* <input type="text" name="visitorsName" />
* {data ? data.message : "Waiting..."}
* </Form>
* );
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The data returned from the route's [`action`](../../start/framework/route-module#action)
* function, or `undefined` if no [`action`](../../start/framework/route-module#action)
* has been called
*/
declare function useActionData<T = any>(): SerializeFrom<T> | undefined;
/**
* Accesses the error thrown during an
* [`action`](../../start/framework/route-module#action),
* [`loader`](../../start/framework/route-module#loader),
* or component render to be used in a route module
* [`ErrorBoundary`](../../start/framework/route-module#errorboundary).
*
* @example
* export function ErrorBoundary() {
* const error = useRouteError();
* return <div>{error.message}</div>;
* }
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The error that was thrown during route [loading](../../start/framework/route-module#loader),
* [`action`](../../start/framework/route-module#action) execution, or rendering
*/
declare function useRouteError(): unknown;
/**
* Returns the resolved promise value from the closest {@link Await | `<Await>`}.
*
* @example
* function SomeDescendant() {
* const value = useAsyncValue();
* // ...
* }
*
* // somewhere in your app
* <Await resolve={somePromise}>
* <SomeDescendant />
* </Await>;
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The resolved value from the nearest {@link Await} component
*/
declare function useAsyncValue(): unknown;
/**
* Returns the rejection value from the closest {@link Await | `<Await>`}.
*
* @example
* import { Await, useAsyncError } from "react-router";
*
* function ErrorElement() {
* const error = useAsyncError();
* return (
* <p>Uh Oh, something went wrong! {error.message}</p>
* );
* }
*
* // somewhere in your app
* <Await
* resolve={promiseThatRejects}
* errorElement={<ErrorElement />}
* />;
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The error that was thrown in the nearest {@link Await} component
*/
declare function useAsyncError(): unknown;
/**
* Allow the application to block navigations within the SPA and present the
* user a confirmation dialog to confirm the navigation. Mostly used to avoid
* using half-filled form data. This does not handle hard-reloads or
* cross-origin navigations.
*
* The {@link Blocker} object returned by the hook has the following properties:
*
* - **`state`**
* - `unblocked` - the blocker is idle and has not prevented any navigation
* - `blocked` - the blocker has prevented a navigation
* - `proceeding` - the blocker is proceeding through from a blocked navigation
* - **`location`**
* - When in a `blocked` state, this represents the {@link Location} to which
* we blocked a navigation. When in a `proceeding` state, this is the
* location being navigated to after a `blocker.proceed()` call.
* - **`proceed()`**
* - When in a `blocked` state, you may call `blocker.proceed()` to proceed to
* the blocked location.
* - **`reset()`**
* - When in a `blocked` state, you may call `blocker.reset()` to return the
* blocker to an `unblocked` state and leave the user at the current
* location.
*
* @example
* // Boolean version
* let blocker = useBlocker(value !== "");
*
* // Function version
* let blocker = useBlocker(
* ({ currentLocation, nextLocation, historyAction }) =>
* value !== "" &&
* currentLocation.pathname !== nextLocation.pathname
* );
*
* @additionalExamples
* ```tsx
* import { useCallback, useState } from "react";
* import { BlockerFunction, useBlocker } from "react-router";
*
* export function ImportantForm() {
* const [value, setValue] = useState("");
*
* const shouldBlock = useCallback<BlockerFunction>(
* () => value !== "",
* [value]
* );
* const blocker = useBlocker(shouldBlock);
*
* return (
* <form
* onSubmit={(e) => {
* e.preventDefault();
* setValue("");
* if (blocker.state === "blocked") {
* blocker.proceed();
* }
* }}
* >
* <input
* name="data"
* value={value}
* onChange={(e) => setValue(e.target.value)}
* />
*
* <button type="submit">Save</button>
*
* {blocker.state === "blocked" ? (
* <>
* <p style={{ color: "red" }}>
* Blocked the last navigation to
* </p>
* <button
* type="button"
* onClick={() => blocker.proceed()}
* >
* Let me through
* </button>
* <button
* type="button"
* onClick={() => blocker.reset()}
* >
* Keep me here
* </button>
* </>
* ) : blocker.state === "proceeding" ? (
* <p style={{ color: "orange" }}>
* Proceeding through blocked navigation
* </p>
* ) : (
* <p style={{ color: "green" }}>
* Blocker is currently unblocked
* </p>
* )}
* </form>
* );
* }
* ```
*
* @public
* @category Hooks
* @mode framework
* @mode data
* @param shouldBlock Either a boolean or a function returning a boolean which
* indicates whether the navigation should be blocked. The function format
* receives a single object parameter containing the `currentLocation`,
* `nextLocation`, and `historyAction` of the potential navigation.
* @returns A {@link Blocker} object with state and reset functionality
*/
declare function useBlocker(shouldBlock: boolean | BlockerFunction): Blocker;
type UseRouteArgs = [] | [routeId: keyof RouteModules$1];
type UseRouteResult<Args extends UseRouteArgs> = Args extends [] ? UseRoute<unknown> : Args extends ["root"] ? UseRoute<"root"> : Args extends [infer RouteId extends keyof RouteModules$1] ? UseRoute<RouteId> | undefined : never;
type UseRoute<RouteId extends keyof RouteModules$1 | unknown> = {
handle: RouteId extends keyof RouteModules$1 ? RouteModules$1[RouteId] extends {
handle: infer handle;
} ? handle : unknown : unknown;
loaderData: RouteId extends keyof RouteModules$1 ? GetLoaderData<RouteModules$1[RouteId]> | undefined : unknown;
actionData: RouteId extends keyof RouteModules$1 ? GetActionData<RouteModules$1[RouteId]> | undefined : unknown;
};
declare function useRoute<Args extends UseRouteArgs>(...args: Args): UseRouteResult<Args>;
/**
* A single route match returned from {@link unstable_useRouterState}. Mirrors
* {@link UIMatch} minus the data-related fields (`data`, `loaderData`).
*/
type unstable_RouterStateMatch<Handle = unknown> = Omit<UIMatch<unknown, Handle>, "data" | "loaderData">;
/**
* The shape of the `active` variant returned from
* {@link unstable_useRouterState}.
*/
type unstable_RouterStateActiveVariant = {
location: Location;
searchParams: URLSearchParams;
params: Params;
matches: unstable_RouterStateMatch[];
type: Action;
};
/**
* The shape of the `pending` variant returned from
* {@link unstable_useRouterState}. Extends
* {@link unstable_RouterStateActiveVariant} with the navigation `state` and
* submission fields mirroring {@link useNavigation} — submission fields are
* populated when the in-flight navigation was triggered by a form submission,
* otherwise `undefined`.
*/
type unstable_RouterStatePendingVariant = unstable_RouterStatePendingVariants[keyof unstable_RouterStatePendingVariants];
type unstable_RouterStatePendingVariants = {
Loading: unstable_RouterStateActiveVariant & Omit<NavigationStates["Loading"], "matches" | "historyAction">;
Submitting: unstable_RouterStateActiveVariant & Omit<NavigationStates["Submitting"], "matches" | "historyAction">;
};
/**
* The return shape of {@link unstable_useRouterState}.
*
* `active` reflects the currently-committed location. `pending` reflects the
* in-flight navigation (if any).
*/
type unstable_RouterState = {
active: unstable_RouterStateActiveVariant;
pending: unstable_RouterStatePendingVariant | null;
};
/**
* A unified hook for reading router state: current (`active`) and in-flight
* (`pending`) locations, search params, params, matches, and navigation type.
*
* This hook consolidates the information you used to get from {@link useLocation},
* {@link useSearchParams}, {@link useParams}, {@link useMatches}, {@link useNavigation},
* and {@link useNavigationType} into a single hook.
*
*
* @example
* import { unstable_useRouterState as useRouterState } from "react-router";
*
* let { active, pending } = unstable_useRouterState();
*
* // Active is always populated with the current location
* active.location; // replaces `useLocation()`
* active.searchParams; // replaces `useSearchParams()[0]`
* active.params; // replaces `useParams()`
* active.matches; // replaces `useMatches()`
* active.type; // replaces `useNavigationType()`
*
* // Pending is only populated during a navigation
* pending.location; // replaces `useNavigation().location`
* pending.searchParams; // equivalent to `new URLSearchParams(useNavigation().search)`
* pending.params; // Not directly accessible today
* pending.matches; // Not directly accessible today
* pending.type; // Not directly accessible today
* pending.state; // replaces `useNavigation().state`
* pending.formMethod; // replaces useNavigation().formMethod
* pending.formAction; // replaces useNavigation().formAction
* pending.formEncType; // replaces useNavigation().formEncType
* pending.formData; // replaces useNavigation().formData
* pending.json; // replaces useNavigation().json
* pending.text; // replaces useNavigation().text
*
* @name unstable_useRouterState
* @public
* @category Hooks
* @mode framework
* @mode data
* @returns The current router state with `active` and `pending` variants
*/
declare function useRouterState(): unstable_RouterState;
/**
* @category Types
*/
interface ServerRouterProps {
/**
* The entry context containing the manifest, route modules, and other data
* needed for rendering.
*/
context: EntryContext;
/**
* The URL of the request being handled.
*/
url: string | URL;
/**
* An optional `nonce` for [Content Security Policy (CSP)](https://developer.mozilla.org/en-US/docs/Web/HTTP/Guides/CSP)
* compliance. This is applied to inline scripts rendered by React Router and
* used as the default for nonce-aware components such as {@link Links | `<Links>`},
* {@link Scripts | `<Scripts>`}, and {@link ScrollRestoration | `<ScrollRestoration>`}
* when they do not provide their own `nonce`.
*/
nonce?: string;
}
/**
* The server entry point for a React Router app in Framework Mode. This
* component is used to generate the HTML in the response from the server. See
* [`entry.server.tsx`](../framework-conventions/entry.server.tsx).
*
* @public
* @category Framework Routers
* @mode framework
* @param props Props
* @param {ServerRouterProps.context} props.context n/a
* @param {ServerRouterProps.nonce} props.nonce n/a
* @param {ServerRouterProps.url} props.url n/a
* @returns A React element that represents the server-rendered application.
*/
declare function ServerRouter({ context, url, nonce, }: ServerRouterProps): ReactElement;
interface StubRouteExtensions {
Component?: React.ComponentType<any>;
HydrateFallback?: React.ComponentType<any>;
ErrorBoundary?: React.ComponentType<any>;
loader?: LoaderFunction;
action?: ActionFunction;
children?: StubRouteObject[];
meta?: MetaFunction;
links?: LinksFunction;
}
interface StubIndexRouteObject extends Omit<IndexRouteObject, "Component" | "HydrateFallback" | "ErrorBoundary" | "loader" | "action" | "element" | "errorElement" | "children">, StubRouteExtensions {
}
interface StubNonIndexRouteObject extends Omit<NonIndexRouteObject, "Component" | "HydrateFallback" | "ErrorBoundary" | "loader" | "action" | "element" | "errorElement" | "children">, StubRouteExtensions {
}
type StubRouteObject = StubIndexRouteObject | StubNonIndexRouteObject;
interface RoutesTestStubProps {
/**
* The initial entries in the history stack. This allows you to start a test with
* multiple locations already in the history stack (for testing a back navigation, etc.)
* The test will default to the last entry in initialEntries if no initialIndex is provided.
* e.g. initialEntries={["/home", "/about", "/contact"]}
*/
initialEntries?: InitialEntry[];
/**
* The initial index in the history stack to render. This allows you to start a test at a specific entry.
* It defaults to the last entry in initialEntries.
* e.g.
* initialEntries: ["/", "/events/123"]
* initialIndex: 1 // start at "/events/123"
*/
initialIndex?: number;
/**
* Used to set the route's initial loader and action data.
* e.g. hydrationData={{
* loaderData: { "/contact": { locale: "en-US" } },
* actionData: { "/login": { errors: { email: "invalid email" } }}
* }}
*/
hydrationData?: HydrationState;
/**
* Future flags mimicking the settings in react-router.config.ts
*/
future?: Partial<FutureConfig>;
}
/**
* @category Utils
*/
declare function createRoutesStub(routes: StubRouteObject[], _context?: AppLoadContext | RouterContextProvider): ({ initialEntries, initialIndex, hydrationData, future, }: RoutesTestStubProps) => React.JSX.Element;
interface CookieSignatureOptions {
/**
* An array of secrets that may be used to sign/unsign the value of a cookie.
*
* The array makes it easy to rotate secrets. New secrets should be added to
* the beginning of the array. `cookie.serialize()` will always use the first
* value in the array, but `cookie.parse()` may use any of them so that
* cookies that were signed with older secrets still work.
*/
secrets?: string[];
}
type CookieOptions = ParseOptions & SerializeOptions & CookieSignatureOptions;
/**
* A HTTP cookie.
*
* A Cookie is a logical container for metadata about a HTTP cookie; its name
* and options. But it doesn't contain a value. Instead, it has `parse()` and
* `serialize()` methods that allow a single instance to be reused for
* parsing/encoding multiple different values.
*
* @see https://remix.run/utils/cookies#cookie-api
*/
interface Cookie {
/**
* The name of the cookie, used in the `Cookie` and `Set-Cookie` headers.
*/
readonly name: string;
/**
* True if this cookie uses one or more secrets for verification.
*/
readonly isSigned: boolean;
/**
* The Date this cookie expires.
*
* Note: This is calculated at access time using `maxAge` when no `expires`
* option is provided to `createCookie()`.
*/
readonly expires?: Date;
/**
* Parses a raw `Cookie` header and returns the value of this cookie or
* `null` if it's not present.
*/
parse(cookieHeader: string | null, options?: ParseOptions): Promise<any>;
/**
* Serializes the given value to a string and returns the `Set-Cookie`
* header.
*/
serialize(value: any, options?: SerializeOptions): Promise<string>;
}
/**
* Creates a logical container for managing a browser cookie from the server.
*/
declare const createCookie: (name: string, cookieOptions?: CookieOptions) => Cookie;
type IsCookieFunction = (object: any) => object is Cookie;
/**
* Returns true if an object is a Remix cookie container.
*
* @see https://remix.run/utils/cookies#iscookie
*/
declare const isCookie: IsCookieFunction;
/**
* An object of name/value pairs to be used in the session.
*/
interface SessionData {
[name: string]: any;
}
/**
* Session persists data across HTTP requests.
*
* @see https://reactrouter.com/explanation/sessions-and-cookies#sessions
*/
interface Session<Data = SessionData, FlashData = Data> {
/**
* A unique identifier for this session.
*
* Note: This will be the empty string for newly created sessions and
* sessions that are not backed by a database (i.e. cookie-based sessions).
*/
readonly id: string;
/**
* The raw data contained in this session.
*
* This is useful mostly for SessionStorage internally to access the raw
* session data to persist.
*/
readonly data: FlashSessionData<Data, FlashData>;
/**
* Returns `true` if the session has a value for the given `name`, `false`
* otherwise.
*/
has(name: (keyof Data | keyof FlashData) & string): boolean;
/**
* Returns the value for the given `name` in this session.
*/
get<Key extends (keyof Data | keyof FlashData) & string>(name: Key): (Key extends keyof Data ? Data[Key] : undefined) | (Key extends keyof FlashData ? FlashData[Key] : undefined) | undefined;
/**
* Sets a value in the session for the given `name`.
*/
set<Key extends keyof Data & string>(name: Key, value: Data[Key]): void;
/**
* Sets a value in the session that is only valid until the next `get()`.
* This can be useful for temporary values, like error messages.
*/
flash<Key extends keyof FlashData & string>(name: Key, value: FlashData[Key]): void;
/**
* Removes a value from the session.
*/
unset(name: keyof Data & string): void;
}
type FlashSessionData<Data, FlashData> = Partial<Data & {
[Key in keyof FlashData as FlashDataKey<Key & string>]: FlashData[Key];
}>;
type FlashDataKey<Key extends string> = `__flash_${Key}__`;
type CreateSessionFunction = <Data = SessionData, FlashData = Data>(initialData?: Data, id?: string) => Session<Data, FlashData>;
/**
* Creates a new Session object.
*
* Note: This function is typically not invoked directly by application code.
* Instead, use a `SessionStorage` object's `getSession` method.
*/
declare const createSession: CreateSessionFunction;
type IsSessionFunction = (object: any) => object is Session;
/**
* Returns true if an object is a React Router session.
*
* @see https://reactrouter.com/api/utils/isSession
*/
declare const isSession: IsSessionFunction;
/**
* SessionStorage stores session data between HTTP requests and knows how to
* parse and create cookies.
*
* A SessionStorage creates Session objects using a `Cookie` header as input.
* Then, later it generates the `Set-Cookie` header to be used in the response.
*/
interface SessionStorage<Data = SessionData, FlashData = Data> {
/**
* Parses a Cookie header from a HTTP request and returns the associated
* Session. If there is no session associated with the cookie, this will
* return a new Session with no data.
*/
getSession: (cookieHeader?: string | null, options?: ParseOptions) => Promise<Session<Data, FlashData>>;
/**
* Stores all data in the Session and returns the Set-Cookie header to be
* used in the HTTP response.
*/
commitSession: (session: Session<Data, FlashData>, options?: SerializeOptions) => Promise<string>;
/**
* Deletes all data associated with the Session and returns the Set-Cookie
* header to be used in the HTTP response.
*/
destroySession: (session: Session<Data, FlashData>, options?: SerializeOptions) => Promise<string>;
}
/**
* SessionIdStorageStrategy is designed to allow anyone to easily build their
* own SessionStorage using `createSessionStorage(strategy)`.
*
* This strategy describes a common scenario where the session id is stored in
* a cookie but the actual session data is stored elsewhere, usually in a
* database or on disk. A set of create, read, update, and delete operations
* are provided for managing the session data.
*/
interface SessionIdStorageStrategy<Data = SessionData, FlashData = Data> {
/**
* The Cookie used to store the session id, or options used to automatically
* create one.
*/
cookie?: Cookie | (CookieOptions & {
name?: string;
});
/**
* Creates a new record with the given data and returns the session id.
*/
createData: (data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<string>;
/**
* Returns data for a given session id, or `null` if there isn't any.
*/
readData: (id: string) => Promise<FlashSessionData<Data, FlashData> | null>;
/**
* Updates data for the given session id.
*/
updateData: (id: string, data: FlashSessionData<Data, FlashData>, expires?: Date) => Promise<void>;
/**
* Deletes data for a given session id from the data store.
*/
deleteData: (id: string) => Promise<void>;
}
/**
* Creates a SessionStorage object using a SessionIdStorageStrategy.
*
* Note: This is a low-level API that should only be used if none of the
* existing session storage options meet your requirements.
*/
declare function createSessionStorage<Data = SessionData, FlashData = Data>({ cookie: cookieArg, createData, readData, updateData, deleteData, }: SessionIdStorageStrategy<Data, FlashData>): SessionStorage<Data, FlashData>;
interface CookieSessionStorageOptions {
/**
* The Cookie used to store the session data on the client, or options used
* to automatically create one.
*/
cookie?: SessionIdStorageStrategy["cookie"];
}
/**
* Creates and returns a SessionStorage object that stores all session data
* directly in the session cookie itself.
*
* This has the advantage that no database or other backend services are
* needed, and can help to simplify some load-balanced scenarios. However, it
* also has the limitation that serialized session data may not exceed the
* browser's maximum cookie size. Trade-offs!
*/
declare function createCookieSessionStorage<Data = SessionData, FlashData = Data>({ cookie: cookieArg }?: CookieSessionStorageOptions): SessionStorage<Data, FlashData>;
interface MemorySessionStorageOptions {
/**
* The Cookie used to store the session id on the client, or options used
* to automatically create one.
*/
cookie?: SessionIdStorageStrategy["cookie"];
}
/**
* Creates and returns a simple in-memory SessionStorage object, mostly useful
* for testing and as a reference implementation.
*
* Note: This storage does not scale beyond a single process, so it is not
* suitable for most production scenarios.
*/
declare function createMemorySessionStorage<Data = SessionData, FlashData = Data>({ cookie }?: MemorySessionStorageOptions): SessionStorage<Data, FlashData>;
type DevServerHooks = {
getCriticalCss?: (pathname: string) => Promise<string | undefined>;
processRequestError?: (error: unknown) => void;
};
declare function setDevServerHooks(devServerHooks: DevServerHooks): void;
type Args = {
[K in keyof Pages]: ToArgs<Pages[K]["params"]>;
};
type ToArgs<Params extends Record<string, string | undefined>> = Equal<Params, {}> extends true ? [] : Partial<Params> extends Params ? [Params] | [] : [
Params
];
/**
Returns a resolved URL path for the specified route.
```tsx
const h = href("/:lang?/about", { lang: "en" })
// -> `/en/about`
<Link to={href("/products/:id", { id: "abc123" })} />
```
*/
declare function href<Path extends keyof Args>(path: Path, ...args: Args[Path]): string;
type DecodedPayload = Promise<RSCPayload> & {
_deepestRenderedBoundaryId?: string | null;
formState: Promise<any>;
};
type SSRCreateFromReadableStreamFunction = (body: ReadableStream<Uint8Array>) => Promise<unknown>;
/**
* Routes the incoming [`Request`](https://developer.mozilla.org/en-US/docs/Web/API/Request)
* to the [RSC](https://react.dev/reference/rsc/server-components) server and
* appropriately proxies the server response for data / resource requests, or
* renders to HTML for a document request.
*
* @example
* import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
* import * as ReactDomServer from "react-dom/server.edge";
* import {
* unstable_RSCStaticRouter as RSCStaticRouter,
* unstable_routeRSCServerRequest as routeRSCServerRequest,
* } from "react-router";
*
* routeRSCServerRequest({
* request,
* serverResponse,
* createFromReadableStream,
* async renderHTML(getPayload) {
* const payload = getPayload();
*
* return await renderHTMLToReadableStream(
* <RSCStaticRouter getPayload={getPayload} />,
* {
* bootstrapScriptContent,
* formState: await payload.formState,
* }
* );
* },
* });
*
* @name unstable_routeRSCServerRequest
* @public
* @category RSC
* @mode data
* @param opts Options
* @param opts.createFromReadableStream Your `react-server-dom-xyz/client`'s
* `createFromReadableStream` function, used to decode payloads from the server.
* @param opts.serverResponse A Response or partial response generated by the [RSC](https://react.dev/reference/rsc/server-components) handler containing a serialized {@link unstable_RSCPayload}.
* @param opts.hydrate Whether to hydrate the server response with the RSC payload.
* Defaults to `true`.
* @param opts.renderHTML A function that renders the {@link unstable_RSCPayload} to
* HTML, usually using a {@link unstable_RSCStaticRouter | `<RSCStaticRouter>`}.
* @param opts.request The request to route.
* @returns A [`Response`](https://developer.mozilla.org/en-US/docs/Web/API/Response)
* that either contains the [RSC](https://react.dev/reference/rsc/server-components)
* payload for data requests, or renders the HTML for document requests.
*/
declare function routeRSCServerRequest({ request, serverResponse, createFromReadableStream, renderHTML, hydrate, }: {
request: Request;
serverResponse: Response;
createFromReadableStream: SSRCreateFromReadableStreamFunction;
renderHTML: (getPayload: () => DecodedPayload, options: {
onError(error: unknown): string | undefined;
onHeaders(headers: Headers): void;
}) => ReadableStream<Uint8Array> | Promise<ReadableStream<Uint8Array>>;
hydrate?: boolean;
}): Promise<Response>;
/**
* Props for the {@link unstable_RSCStaticRouter} component.
*
* @name unstable_RSCStaticRouterProps
* @category Types
*/
interface RSCStaticRouterProps {
/**
* A function that starts decoding of the {@link unstable_RSCPayload}. Usually passed
* through from {@link unstable_routeRSCServerRequest}'s `renderHTML`.
*/
getPayload: () => DecodedPayload;
}
/**
* Pre-renders an {@link unstable_RSCPayload} to HTML. Usually used in
* {@link unstable_routeRSCServerRequest}'s `renderHTML` callback.
*
* @example
* import { createFromReadableStream } from "@vitejs/plugin-rsc/ssr";
* import * as ReactDomServer from "react-dom/server.edge";
* import {
* unstable_RSCStaticRouter as RSCStaticRouter,
* unstable_routeRSCServerRequest as routeRSCServerRequest,
* } from "react-router";
*
* routeRSCServerRequest({
* request,
* serverResponse,
* createFromReadableStream,
* async renderHTML(getPayload) {
* const payload = getPayload();
*
* return await renderHTMLToReadableStream(
* <RSCStaticRouter getPayload={getPayload} />,
* {
* bootstrapScriptContent,
* formState: await payload.formState,
* }
* );
* },
* });
*
* @name unstable_RSCStaticRouter
* @public
* @category RSC
* @mode data
* @param props Props
* @param {unstable_RSCStaticRouterProps.getPayload} props.getPayload n/a
* @returns A React component that renders the {@link unstable_RSCPayload} as HTML.
*/
declare function RSCStaticRouter({ getPayload }: RSCStaticRouterProps): React.JSX.Element | null;
declare function RSCDefaultRootErrorBoundary({ hasRootLayout, }: {
hasRootLayout: boolean;
}): React__default.JSX.Element;
type RemixErrorBoundaryProps = React.PropsWithChildren<{
location: Location;
isOutsideRemixApp?: boolean;
error?: Error;
}>;
type RemixErrorBoundaryState = {
error: null | Error;
location: Location;
};
declare class RemixErrorBoundary extends React.Component<RemixErrorBoundaryProps, RemixErrorBoundaryState> {
constructor(props: RemixErrorBoundaryProps);
static getDerivedStateFromError(error: Error): {
error: Error;
};
static getDerivedStateFromProps(props: RemixErrorBoundaryProps, state: RemixErrorBoundaryState): {
error: Error | null;
location: Location<any>;
};
render(): string | number | boolean | Iterable<React.ReactNode> | React.JSX.Element | null | undefined;
}
declare function getPatchRoutesOnNavigationFunction(getRouter: () => Router, manifest: AssetsManifest, routeModules: RouteModules, ssr: boolean, routeDiscovery: ServerBuild["routeDiscovery"], isSpaMode: boolean, basename: string | undefined): PatchRoutesOnNavigationFunction | undefined;
declare function useFogOFWarDiscovery(router: Router, manifest: AssetsManifest, routeModules: RouteModules, ssr: boolean, routeDiscovery: ServerBuild["routeDiscovery"], isSpaMode: boolean): void;
declare function getHydrationData({ state, routes, getRouteInfo, location, basename, isSpaMode, }: {
state: {
loaderData?: Router["state"]["loaderData"];
actionData?: Router["state"]["actionData"];
errors?: Router["state"]["errors"];
};
routes: DataRouteObject[];
getRouteInfo: (routeId: string) => {
clientLoader: ClientLoaderFunction | undefined;
hasLoader: boolean;
hasHydrateFallback: boolean;
};
location: Path;
basename: string | undefined;
isSpaMode: boolean;
}): HydrationState;
/**
* @module index
* @mergeModuleWith react-router
*/
declare const unstable_getRequest: typeof getRequest;
declare const unstable_matchRSCServerRequest: typeof matchRSCServerRequest;
export { ActionFunction, AppLoadContext, Blocker, BlockerFunction, ClientLoaderFunction, type Cookie, type CookieOptions, type CookieSignatureOptions, type CreateRequestHandlerFunction, DataRouteObject, Router as DataRouter, DataStrategyFunction, EntryContext, type FlashSessionData, HydrationState, IndexRouteObject, InitialEntry, type IsCookieFunction, type IsSessionFunction, LinksFunction, LoaderFunction, Location, MetaFunction, type NavigateFunction, NavigateOptions, NavigationStates, Action as NavigationType, NonIndexRouteObject, ParamParseKey, Params, PatchRoutesOnNavigationFunction, Path, PathMatch, PathPattern, RelativeRoutingType, type RequestHandler, RouteObject, RouterContextProvider, type RoutesTestStubProps, ServerBuild, ServerRouter, type ServerRouterProps, type Session, type SessionData, type SessionIdStorageStrategy, type SessionStorage, To, UIMatch, AssetsManifest as UNSAFE_AssetsManifest, MiddlewareEnabled as UNSAFE_MiddlewareEnabled, RSCDefaultRootErrorBoundary as UNSAFE_RSCDefaultRootErrorBoundary, RemixErrorBoundary as UNSAFE_RemixErrorBoundary, RouteModules as UNSAFE_RouteModules, ServerMode as UNSAFE_ServerMode, SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol, decodeViaTurboStream as UNSAFE_decodeViaTurboStream, getHydrationData as UNSAFE_getHydrationData, getPatchRoutesOnNavigationFunction as UNSAFE_getPatchRoutesOnNavigationFunction, getTurboStreamSingleFetchDataStrategy as UNSAFE_getTurboStreamSingleFetchDataStrategy, useFogOFWarDiscovery as UNSAFE_useFogOFWarDiscovery, createCookie, createCookieSessionStorage, createMemorySessionStorage, createRequestHandler, createRoutesStub, createSession, createSessionStorage, href, isCookie, isSession, RSCPayload as unstable_RSCPayload, RSCStaticRouter as unstable_RSCStaticRouter, type RSCStaticRouterProps as unstable_RSCStaticRouterProps, type unstable_RouterState, type unstable_RouterStateActiveVariant, type unstable_RouterStatePendingVariant, type SSRCreateFromReadableStreamFunction as unstable_SSRCreateFromReadableStreamFunction, unstable_getRequest, unstable_matchRSCServerRequest, routeRSCServerRequest as unstable_routeRSCServerRequest, setDevServerHooks as unstable_setDevServerHooks, useRoute as unstable_useRoute, useRouterState as unstable_useRouterState, useActionData, useAsyncError, useAsyncValue, useBlocker, useHref, useInRouterContext, useLoaderData, useLocation, useMatch, useMatches, useNavigate, useNavigation, useNavigationType, useOutlet, useOutletContext, useParams, useResolvedPath, useRevalidator, useRouteError, useRouteLoaderData, useRoutes };
File diff suppressed because one or more lines are too long
+275
View File
@@ -0,0 +1,275 @@
/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
"use client";
import {
RSCDefaultRootErrorBoundary,
RSCStaticRouter,
ServerMode,
ServerRouter,
createCookie,
createCookieSessionStorage,
createMemorySessionStorage,
createRequestHandler,
createRoutesStub,
createSession,
createSessionStorage,
getHydrationData,
href,
isCookie,
isSession,
routeRSCServerRequest,
setDevServerHooks
} from "./chunk-IJF3QNGC.mjs";
import {
Action,
Await,
AwaitContextProvider,
BrowserRouter,
DataRouterContext,
DataRouterStateContext,
ErrorResponseImpl,
FetchersContext,
Form,
FrameworkContext,
HashRouter,
HistoryRouter,
IDLE_BLOCKER,
IDLE_FETCHER,
IDLE_NAVIGATION,
Link,
Links,
LocationContext,
MemoryRouter,
Meta,
NavLink,
Navigate,
NavigationContext,
Outlet,
PrefetchPageLinks,
RemixErrorBoundary,
Route,
RouteContext,
Router,
RouterContextProvider,
RouterProvider,
Routes,
Scripts,
ScrollRestoration,
SingleFetchRedirectSymbol,
StaticRouter,
StaticRouterProvider,
ViewTransitionContext,
WithComponentProps,
WithErrorBoundaryProps,
WithHydrateFallbackProps,
createBrowserHistory,
createBrowserRouter,
createClientRoutes,
createClientRoutesWithHMRRevalidationOptOut,
createContext,
createHashHistory,
createHashRouter,
createMemoryHistory,
createMemoryRouter,
createPath,
createRouter,
createRoutesFromChildren,
createRoutesFromElements,
createSearchParams,
createStaticHandler2 as createStaticHandler,
createStaticRouter,
data,
decodeViaTurboStream,
generatePath,
getPatchRoutesOnNavigationFunction,
getTurboStreamSingleFetchDataStrategy,
hydrationRouteProperties,
invariant,
isRouteErrorResponse,
mapRouteProperties,
matchPath,
matchRoutes,
parsePath,
redirect,
redirectDocument,
renderMatches,
replace,
resolvePath,
shouldHydrateRouteLoader,
useActionData,
useAsyncError,
useAsyncValue,
useBeforeUnload,
useBlocker,
useFetcher,
useFetchers,
useFogOFWarDiscovery,
useFormAction,
useHref,
useInRouterContext,
useLinkClickHandler,
useLoaderData,
useLocation,
useMatch,
useMatches,
useNavigate,
useNavigation,
useNavigationType,
useOutlet,
useOutletContext,
useParams,
usePrompt,
useResolvedPath,
useRevalidator,
useRoute,
useRouteError,
useRouteLoaderData,
useRouterState,
useRoutes,
useScrollRestoration,
useSearchParams,
useSubmit,
useViewTransitionState,
withComponentProps,
withErrorBoundaryProps,
withHydrateFallbackProps
} from "./chunk-KS7C4IRE.mjs";
export {
Await,
BrowserRouter,
Form,
HashRouter,
IDLE_BLOCKER,
IDLE_FETCHER,
IDLE_NAVIGATION,
Link,
Links,
MemoryRouter,
Meta,
NavLink,
Navigate,
Action as NavigationType,
Outlet,
PrefetchPageLinks,
Route,
Router,
RouterContextProvider,
RouterProvider,
Routes,
Scripts,
ScrollRestoration,
ServerRouter,
StaticRouter,
StaticRouterProvider,
AwaitContextProvider as UNSAFE_AwaitContextProvider,
DataRouterContext as UNSAFE_DataRouterContext,
DataRouterStateContext as UNSAFE_DataRouterStateContext,
ErrorResponseImpl as UNSAFE_ErrorResponseImpl,
FetchersContext as UNSAFE_FetchersContext,
FrameworkContext as UNSAFE_FrameworkContext,
LocationContext as UNSAFE_LocationContext,
NavigationContext as UNSAFE_NavigationContext,
RSCDefaultRootErrorBoundary as UNSAFE_RSCDefaultRootErrorBoundary,
RemixErrorBoundary as UNSAFE_RemixErrorBoundary,
RouteContext as UNSAFE_RouteContext,
ServerMode as UNSAFE_ServerMode,
SingleFetchRedirectSymbol as UNSAFE_SingleFetchRedirectSymbol,
ViewTransitionContext as UNSAFE_ViewTransitionContext,
WithComponentProps as UNSAFE_WithComponentProps,
WithErrorBoundaryProps as UNSAFE_WithErrorBoundaryProps,
WithHydrateFallbackProps as UNSAFE_WithHydrateFallbackProps,
createBrowserHistory as UNSAFE_createBrowserHistory,
createClientRoutes as UNSAFE_createClientRoutes,
createClientRoutesWithHMRRevalidationOptOut as UNSAFE_createClientRoutesWithHMRRevalidationOptOut,
createHashHistory as UNSAFE_createHashHistory,
createMemoryHistory as UNSAFE_createMemoryHistory,
createRouter as UNSAFE_createRouter,
decodeViaTurboStream as UNSAFE_decodeViaTurboStream,
getHydrationData as UNSAFE_getHydrationData,
getPatchRoutesOnNavigationFunction as UNSAFE_getPatchRoutesOnNavigationFunction,
getTurboStreamSingleFetchDataStrategy as UNSAFE_getTurboStreamSingleFetchDataStrategy,
hydrationRouteProperties as UNSAFE_hydrationRouteProperties,
invariant as UNSAFE_invariant,
mapRouteProperties as UNSAFE_mapRouteProperties,
shouldHydrateRouteLoader as UNSAFE_shouldHydrateRouteLoader,
useFogOFWarDiscovery as UNSAFE_useFogOFWarDiscovery,
useScrollRestoration as UNSAFE_useScrollRestoration,
withComponentProps as UNSAFE_withComponentProps,
withErrorBoundaryProps as UNSAFE_withErrorBoundaryProps,
withHydrateFallbackProps as UNSAFE_withHydrateFallbackProps,
createBrowserRouter,
createContext,
createCookie,
createCookieSessionStorage,
createHashRouter,
createMemoryRouter,
createMemorySessionStorage,
createPath,
createRequestHandler,
createRoutesFromChildren,
createRoutesFromElements,
createRoutesStub,
createSearchParams,
createSession,
createSessionStorage,
createStaticHandler,
createStaticRouter,
data,
generatePath,
href,
isCookie,
isRouteErrorResponse,
isSession,
matchPath,
matchRoutes,
parsePath,
redirect,
redirectDocument,
renderMatches,
replace,
resolvePath,
HistoryRouter as unstable_HistoryRouter,
RSCStaticRouter as unstable_RSCStaticRouter,
routeRSCServerRequest as unstable_routeRSCServerRequest,
setDevServerHooks as unstable_setDevServerHooks,
usePrompt as unstable_usePrompt,
useRoute as unstable_useRoute,
useRouterState as unstable_useRouterState,
useActionData,
useAsyncError,
useAsyncValue,
useBeforeUnload,
useBlocker,
useFetcher,
useFetchers,
useFormAction,
useHref,
useInRouterContext,
useLinkClickHandler,
useLoaderData,
useLocation,
useMatch,
useMatches,
useNavigate,
useNavigation,
useNavigationType,
useOutlet,
useOutletContext,
useParams,
useResolvedPath,
useRevalidator,
useRouteError,
useRouteLoaderData,
useRoutes,
useSearchParams,
useSubmit,
useViewTransitionState
};
@@ -0,0 +1,715 @@
import { e as RouteObject, f as History, g as MaybePromise, c as RouterContextProvider, h as MapRoutePropertiesFunction, i as Action, L as Location, D as DataRouteMatch, j as Submission, k as RouteData, l as DataStrategyFunction, m as PatchRoutesOnNavigationFunction, n as DataRouteObject, o as RouteBranch, p as RouteManifest, U as UIMatch, T as To, q as HTMLFormMethod, F as FormEncType, r as Path, s as LoaderFunctionArgs, t as MiddlewareEnabled, u as AppLoadContext } from './data-CjO11-hU.js';
/**
* A Router instance manages all navigation and data loading/mutations
*/
interface Router {
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the basename for the router
*/
get basename(): RouterInit["basename"];
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the future config for the router
*/
get future(): FutureConfig;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the current state of the router
*/
get state(): RouterState;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the routes for this router instance
*/
get routes(): DataRouteObject[];
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the route branches for this router instance
*/
get branches(): RouteBranch<DataRouteObject>[] | undefined;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the manifest for this router instance
*/
get manifest(): RouteManifest;
/**
* @private
* PRIVATE - DO NOT USE
*
* Return the window associated with the router
*/
get window(): RouterInit["window"];
/**
* @private
* PRIVATE - DO NOT USE
*
* Initialize the router, including adding history listeners and kicking off
* initial data fetches. Returns a function to cleanup listeners and abort
* any in-progress loads
*/
initialize(): Router;
/**
* @private
* PRIVATE - DO NOT USE
*
* Subscribe to router.state updates
*
* @param fn function to call with the new state
*/
subscribe(fn: RouterSubscriber): () => void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Enable scroll restoration behavior in the router
*
* @param savedScrollPositions Object that will manage positions, in case
* it's being restored from sessionStorage
* @param getScrollPosition Function to get the active Y scroll position
* @param getKey Function to get the key to use for restoration
*/
enableScrollRestoration(savedScrollPositions: Record<string, number>, getScrollPosition: GetScrollPositionFunction, getKey?: GetScrollRestorationKeyFunction): () => void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Navigate forward/backward in the history stack
* @param to Delta to move in the history stack
*/
navigate(to: number): Promise<void>;
/**
* Navigate to the given path
* @param to Path to navigate to
* @param opts Navigation options (method, submission, etc.)
*/
navigate(to: To | null, opts?: RouterNavigateOptions): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Trigger a fetcher load/submission
*
* @param key Fetcher key
* @param routeId Route that owns the fetcher
* @param href href to fetch
* @param opts Fetcher options, (method, submission, etc.)
*/
fetch(key: string, routeId: string, href: string | null, opts?: RouterFetchOptions): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Trigger a revalidation of all current route loaders and fetcher loads
*/
revalidate(): Promise<void>;
/**
* @private
* PRIVATE - DO NOT USE
*
* Utility function to create an href for the given location
* @param location
*/
createHref(location: Location | URL): string;
/**
* @private
* PRIVATE - DO NOT USE
*
* Utility function to URL encode a destination path according to the internal
* history implementation
* @param to
*/
encodeLocation(to: To): Path;
/**
* @private
* PRIVATE - DO NOT USE
*
* Get/create a fetcher for the given key
* @param key
*/
getFetcher<TData = any>(key: string): Fetcher<TData>;
/**
* @internal
* PRIVATE - DO NOT USE
*
* Reset the fetcher for a given key
* @param key
*/
resetFetcher(key: string, opts?: {
reason?: unknown;
}): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Delete the fetcher for a given key
* @param key
*/
deleteFetcher(key: string): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Cleanup listeners and abort any in-progress loads
*/
dispose(): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Get a navigation blocker
* @param key The identifier for the blocker
* @param fn The blocker function implementation
*/
getBlocker(key: string, fn: BlockerFunction): Blocker;
/**
* @private
* PRIVATE - DO NOT USE
*
* Delete a navigation blocker
* @param key The identifier for the blocker
*/
deleteBlocker(key: string): void;
/**
* @private
* PRIVATE DO NOT USE
*
* Patch additional children routes into an existing parent route
* @param routeId The parent route id or a callback function accepting `patch`
* to perform batch patching
* @param children The additional children routes
* @param unstable_allowElementMutations Allow mutation or route elements on
* existing routes. Intended for RSC-usage
* only.
*/
patchRoutes(routeId: string | null, children: RouteObject[], unstable_allowElementMutations?: boolean): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* HMR needs to pass in-flight route updates to React Router
* TODO: Replace this with granular route update APIs (addRoute, updateRoute, deleteRoute)
*/
_internalSetRoutes(routes: RouteObject[]): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Cause subscribers to re-render. This is used to force a re-render.
*/
_internalSetStateDoNotUseOrYouWillBreakYourApp(state: Partial<RouterState>): void;
/**
* @private
* PRIVATE - DO NOT USE
*
* Internal fetch AbortControllers accessed by unit tests
*/
_internalFetchControllers: Map<string, AbortController>;
}
/**
* State maintained internally by the router. During a navigation, all states
* reflect the "old" location unless otherwise noted.
*/
interface RouterState {
/**
* The action of the most recent navigation
*/
historyAction: Action;
/**
* The current location reflected by the router
*/
location: Location;
/**
* The current set of route matches
*/
matches: DataRouteMatch[];
/**
* Tracks whether we've completed our initial data load
*/
initialized: boolean;
/**
* Tracks whether we should be rendering a HydrateFallback during hydration
*/
renderFallback: boolean;
/**
* Current scroll position we should start at for a new view
* - number -> scroll position to restore to
* - false -> do not restore scroll at all (used during submissions/revalidations)
* - null -> don't have a saved position, scroll to hash or top of page
*/
restoreScrollPosition: number | false | null;
/**
* Indicate whether this navigation should skip resetting the scroll position
* if we are unable to restore the scroll position
*/
preventScrollReset: boolean;
/**
* Tracks the state of the current navigation
*/
navigation: Navigation;
/**
* Tracks any in-progress revalidations
*/
revalidation: RevalidationState;
/**
* Data from the loaders for the current matches
*/
loaderData: RouteData;
/**
* Data from the action for the current matches
*/
actionData: RouteData | null;
/**
* Errors caught from loaders for the current matches
*/
errors: RouteData | null;
/**
* Map of current fetchers
*/
fetchers: Map<string, Fetcher>;
/**
* Map of current blockers
*/
blockers: Map<string, Blocker>;
}
/**
* Data that can be passed into hydrate a Router from SSR
*/
type HydrationState = Partial<Pick<RouterState, "loaderData" | "actionData" | "errors">>;
/**
* Future flags to toggle new feature behavior
*/
interface FutureConfig {
}
/**
* Initialization options for createRouter
*/
interface RouterInit {
routes: RouteObject[];
history: History;
basename?: string;
getContext?: () => MaybePromise<RouterContextProvider>;
instrumentations?: ClientInstrumentation[];
mapRouteProperties?: MapRoutePropertiesFunction;
future?: Partial<FutureConfig>;
hydrationRouteProperties?: string[];
hydrationData?: HydrationState;
window?: Window;
dataStrategy?: DataStrategyFunction;
patchRoutesOnNavigation?: PatchRoutesOnNavigationFunction;
}
/**
* State returned from a server-side query() call
*/
interface StaticHandlerContext {
basename: Router["basename"];
location: RouterState["location"];
matches: RouterState["matches"];
loaderData: RouterState["loaderData"];
actionData: RouterState["actionData"];
errors: RouterState["errors"];
statusCode: number;
loaderHeaders: Record<string, Headers>;
actionHeaders: Record<string, Headers>;
_deepestRenderedBoundaryId?: string | null;
}
/**
* A StaticHandler instance manages a singular SSR navigation/fetch event
*/
interface StaticHandler {
/**
* The set of data routes managed by this handler
*/
dataRoutes: DataRouteObject[];
/**
* @private
* PRIVATE - DO NOT USE
*
* The route branches derived from the data routes, used for internal route
* matching in Framework Mode
*/
_internalRouteBranches: RouteBranch<DataRouteObject>[];
/**
* Perform a query for a given request - executing all matched route
* loaders/actions. Used for document requests.
*
* @param request The request to query
* @param opts Optional query options
* @param opts.dataStrategy Alternate dataStrategy implementation
* @param opts.filterMatchesToLoad Predicate function to filter which matches should be loaded
* @param opts.generateMiddlewareResponse To enable middleware, provide a function
* to generate a response to bubble back up the middleware chain
* @param opts.requestContext Context object to pass to loaders/actions
* @param opts.skipLoaderErrorBubbling Skip loader error bubbling
* @param opts.skipRevalidation Skip revalidation after action submission
* @param opts.normalizePath Normalize the request path
*/
query(request: Request, opts?: {
requestContext?: unknown;
filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
skipLoaderErrorBubbling?: boolean;
skipRevalidation?: boolean;
dataStrategy?: DataStrategyFunction<unknown>;
generateMiddlewareResponse?: (query: (r: Request, args?: {
filterMatchesToLoad?: (match: DataRouteMatch) => boolean;
}) => Promise<StaticHandlerContext | Response>) => MaybePromise<Response>;
normalizePath?: (request: Request) => Path;
}): Promise<StaticHandlerContext | Response>;
/**
* Perform a query for a specific route. Used for resource requests.
*
* @param request The request to query
* @param opts Optional queryRoute options
* @param opts.dataStrategy Alternate dataStrategy implementation
* @param opts.generateMiddlewareResponse To enable middleware, provide a function
* to generate a response to bubble back up the middleware chain
* @param opts.requestContext Context object to pass to loaders/actions
* @param opts.routeId The ID of the route to query
* @param opts.normalizePath Normalize the request path
*/
queryRoute(request: Request, opts?: {
routeId?: string;
requestContext?: unknown;
dataStrategy?: DataStrategyFunction<unknown>;
generateMiddlewareResponse?: (queryRoute: (r: Request) => Promise<Response>) => MaybePromise<Response>;
normalizePath?: (request: Request) => Path;
}): Promise<any>;
}
type ViewTransitionOpts = {
currentLocation: Location;
nextLocation: Location;
};
/**
* Subscriber function signature for changes to router state
*/
interface RouterSubscriber {
(state: RouterState, opts: {
deletedFetchers: string[];
newErrors: RouteData | null;
viewTransitionOpts?: ViewTransitionOpts;
flushSync: boolean;
}): void;
}
/**
* Function signature for determining the key to be used in scroll restoration
* for a given location
*/
interface GetScrollRestorationKeyFunction {
(location: Location, matches: UIMatch[]): string | null;
}
/**
* Function signature for determining the current scroll position
*/
interface GetScrollPositionFunction {
(): number;
}
/**
* - "route": relative to the route hierarchy so `..` means remove all segments
* of the current route even if it has many. For example, a `route("posts/:id")`
* would have both `:id` and `posts` removed from the url.
* - "path": relative to the pathname so `..` means remove one segment of the
* pathname. For example, a `route("posts/:id")` would have only `:id` removed
* from the url.
*/
type RelativeRoutingType = "route" | "path";
type BaseNavigateOrFetchOptions = {
preventScrollReset?: boolean;
relative?: RelativeRoutingType;
flushSync?: boolean;
defaultShouldRevalidate?: boolean;
};
type BaseNavigateOptions = BaseNavigateOrFetchOptions & {
replace?: boolean;
state?: any;
fromRouteId?: string;
viewTransition?: boolean;
mask?: To;
};
type BaseSubmissionOptions = {
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
} & ({
formData: FormData;
body?: undefined;
} | {
formData?: undefined;
body: any;
});
/**
* Options for a navigate() call for a normal (non-submission) navigation
*/
type LinkNavigateOptions = BaseNavigateOptions;
/**
* Options for a navigate() call for a submission navigation
*/
type SubmissionNavigateOptions = BaseNavigateOptions & BaseSubmissionOptions;
/**
* Options to pass to navigate() for a navigation
*/
type RouterNavigateOptions = LinkNavigateOptions | SubmissionNavigateOptions;
/**
* Options for a fetch() load
*/
type LoadFetchOptions = BaseNavigateOrFetchOptions;
/**
* Options for a fetch() submission
*/
type SubmitFetchOptions = BaseNavigateOrFetchOptions & BaseSubmissionOptions;
/**
* Options to pass to fetch()
*/
type RouterFetchOptions = LoadFetchOptions | SubmitFetchOptions;
/**
* Potential states for state.navigation
*/
type NavigationStates = {
Idle: {
state: "idle";
location: undefined;
matches: undefined;
historyAction: undefined;
formMethod: undefined;
formAction: undefined;
formEncType: undefined;
formData: undefined;
json: undefined;
text: undefined;
};
Loading: {
state: "loading";
location: Location;
matches: DataRouteMatch[];
historyAction: Action;
formMethod: Submission["formMethod"] | undefined;
formAction: Submission["formAction"] | undefined;
formEncType: Submission["formEncType"] | undefined;
formData: Submission["formData"] | undefined;
json: Submission["json"] | undefined;
text: Submission["text"] | undefined;
};
Submitting: {
state: "submitting";
location: Location;
matches: DataRouteMatch[];
historyAction: Action;
formMethod: Submission["formMethod"];
formAction: Submission["formAction"];
formEncType: Submission["formEncType"];
formData: Submission["formData"];
json: Submission["json"];
text: Submission["text"];
};
};
type Navigation = NavigationStates[keyof NavigationStates];
type RevalidationState = "idle" | "loading";
/**
* Potential states for fetchers
*/
type FetcherStates<TData = any> = {
/**
* The fetcher is not calling a loader or action
*
* ```tsx
* fetcher.state === "idle"
* ```
*/
Idle: {
state: "idle";
formMethod: undefined;
formAction: undefined;
formEncType: undefined;
text: undefined;
formData: undefined;
json: undefined;
/**
* If the fetcher has never been called, this will be undefined.
*/
data: TData | undefined;
};
/**
* The fetcher is loading data from a {@link LoaderFunction | loader} from a
* call to {@link FetcherWithComponents.load | `fetcher.load`}.
*
* ```tsx
* // somewhere
* <button onClick={() => fetcher.load("/some/route") }>Load</button>
*
* // the state will update
* fetcher.state === "loading"
* ```
*/
Loading: {
state: "loading";
formMethod: Submission["formMethod"] | undefined;
formAction: Submission["formAction"] | undefined;
formEncType: Submission["formEncType"] | undefined;
text: Submission["text"] | undefined;
formData: Submission["formData"] | undefined;
json: Submission["json"] | undefined;
data: TData | undefined;
};
/**
The fetcher is submitting to a {@link LoaderFunction} (GET) or {@link ActionFunction} (POST) from a {@link FetcherWithComponents.Form | `fetcher.Form`} or {@link FetcherWithComponents.submit | `fetcher.submit`}.
```tsx
// somewhere
<input
onChange={e => {
fetcher.submit(event.currentTarget.form, { method: "post" });
}}
/>
// the state will update
fetcher.state === "submitting"
// and formData will be available
fetcher.formData
```
*/
Submitting: {
state: "submitting";
formMethod: Submission["formMethod"];
formAction: Submission["formAction"];
formEncType: Submission["formEncType"];
text: Submission["text"];
formData: Submission["formData"];
json: Submission["json"];
data: TData | undefined;
};
};
type Fetcher<TData = any> = FetcherStates<TData>[keyof FetcherStates<TData>];
interface BlockerBlocked {
state: "blocked";
reset: () => void;
proceed: () => void;
location: Location;
}
interface BlockerUnblocked {
state: "unblocked";
reset: undefined;
proceed: undefined;
location: undefined;
}
interface BlockerProceeding {
state: "proceeding";
reset: undefined;
proceed: undefined;
location: Location;
}
type Blocker = BlockerUnblocked | BlockerBlocked | BlockerProceeding;
type BlockerFunction = (args: {
currentLocation: Location;
nextLocation: Location;
historyAction: Action;
}) => boolean;
declare const IDLE_NAVIGATION: NavigationStates["Idle"];
declare const IDLE_FETCHER: FetcherStates["Idle"];
declare const IDLE_BLOCKER: BlockerUnblocked;
/**
* Create a router and listen to history POP navigations
*/
declare function createRouter(init: RouterInit): Router;
interface CreateStaticHandlerOptions {
basename?: string;
mapRouteProperties?: MapRoutePropertiesFunction;
instrumentations?: Pick<ServerInstrumentation, "route">[];
future?: Partial<FutureConfig>;
}
type ServerInstrumentation = {
handler?: InstrumentRequestHandlerFunction;
route?: InstrumentRouteFunction;
};
type ClientInstrumentation = {
router?: InstrumentRouterFunction;
route?: InstrumentRouteFunction;
};
type InstrumentRequestHandlerFunction = (handler: InstrumentableRequestHandler) => void;
type InstrumentRouterFunction = (router: InstrumentableRouter) => void;
type InstrumentRouteFunction = (route: InstrumentableRoute) => void;
type InstrumentationHandlerResult = {
status: "success";
error: undefined;
} | {
status: "error";
error: Error;
};
type InstrumentFunction<T> = (handler: () => Promise<InstrumentationHandlerResult>, info: T) => Promise<void>;
type ReadonlyRequest = {
method: string;
url: string;
headers: Pick<Headers, "get">;
};
type ReadonlyContext = MiddlewareEnabled extends true ? Pick<RouterContextProvider, "get"> : Readonly<AppLoadContext>;
type InstrumentableRoute = {
id: string;
index: boolean | undefined;
path: string | undefined;
instrument(instrumentations: RouteInstrumentations): void;
};
type RouteInstrumentations = {
lazy?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.loader"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.action"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
"lazy.middleware"?: InstrumentFunction<RouteLazyInstrumentationInfo>;
middleware?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
loader?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
action?: InstrumentFunction<RouteHandlerInstrumentationInfo>;
};
type RouteLazyInstrumentationInfo = undefined;
type RouteHandlerInstrumentationInfo = Readonly<{
request: ReadonlyRequest;
params: LoaderFunctionArgs["params"];
pattern: string;
context: ReadonlyContext;
}>;
type InstrumentableRouter = {
instrument(instrumentations: RouterInstrumentations): void;
};
type RouterInstrumentations = {
navigate?: InstrumentFunction<RouterNavigationInstrumentationInfo>;
fetch?: InstrumentFunction<RouterFetchInstrumentationInfo>;
};
type RouterNavigationInstrumentationInfo = Readonly<{
to: string | number;
currentUrl: string;
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
formData?: FormData;
body?: any;
}>;
type RouterFetchInstrumentationInfo = Readonly<{
href: string;
currentUrl: string;
fetcherKey: string;
formMethod?: HTMLFormMethod;
formEncType?: FormEncType;
formData?: FormData;
body?: any;
}>;
type InstrumentableRequestHandler = {
instrument(instrumentations: RequestHandlerInstrumentations): void;
};
type RequestHandlerInstrumentations = {
request?: InstrumentFunction<RequestHandlerInstrumentationInfo>;
};
type RequestHandlerInstrumentationInfo = Readonly<{
request: ReadonlyRequest;
context: ReadonlyContext | undefined;
}>;
export { type BlockerFunction as B, type ClientInstrumentation as C, type Fetcher as F, type GetScrollPositionFunction as G, type HydrationState as H, type InstrumentRequestHandlerFunction as I, type NavigationStates as N, type RouterInit as R, type StaticHandler as S, type Router as a, type Blocker as b, type RelativeRoutingType as c, type GetScrollRestorationKeyFunction as d, type StaticHandlerContext as e, type Navigation as f, type RouterState as g, type RouterSubscriber as h, type RouterNavigateOptions as i, type RouterFetchOptions as j, type RevalidationState as k, type ServerInstrumentation as l, type InstrumentRouterFunction as m, type InstrumentRouteFunction as n, type InstrumentationHandlerResult as o, IDLE_NAVIGATION as p, IDLE_FETCHER as q, IDLE_BLOCKER as r, createRouter as s, type FutureConfig as t, type CreateStaticHandlerOptions as u };
@@ -0,0 +1,184 @@
import { R as RouteModule, e as LinkDescriptor, L as Location, F as Func, f as Pretty, g as MetaDescriptor, G as GetLoaderData, h as ServerDataFunctionArgs, i as MiddlewareNextFunction, j as ClientDataFunctionArgs, D as DataStrategyResult, k as ServerDataFrom, N as Normalize, l as GetActionData } from '../../data-DEjBmEfD.mjs';
import { R as RouteFiles, P as Pages } from '../../register-CmkRspdl.mjs';
import 'react';
type MaybePromise<T> = T | Promise<T>;
type Props = {
params: unknown;
loaderData: unknown;
actionData: unknown;
};
type RouteInfo = Props & {
module: RouteModule;
matches: Array<MatchInfo>;
};
type MatchInfo = {
id: string;
module: RouteModule;
};
type MetaMatch<T extends MatchInfo> = Pretty<{
id: T["id"];
params: Record<string, string | undefined>;
pathname: string;
meta: MetaDescriptor[];
/** @deprecated Use `MetaMatch.loaderData` instead */
data: GetLoaderData<T["module"]>;
loaderData: GetLoaderData<T["module"]>;
handle?: unknown;
error?: unknown;
}>;
type MetaMatches<T extends Array<MatchInfo>> = T extends [infer F extends MatchInfo, ...infer R extends Array<MatchInfo>] ? [MetaMatch<F>, ...MetaMatches<R>] : Array<MetaMatch<MatchInfo> | undefined>;
type HasErrorBoundary<T extends RouteInfo> = T["module"] extends {
ErrorBoundary: Func;
} ? true : false;
type CreateMetaArgs<T extends RouteInfo> = {
/** This is the current router `Location` object. This is useful for generating tags for routes at specific paths or query parameters. */
location: Location;
/** {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route. */
params: T["params"];
/**
* The return value for this route's server loader function
*
* @deprecated Use `Route.MetaArgs.loaderData` instead
*/
data: T["loaderData"] | (HasErrorBoundary<T> extends true ? undefined : never);
/** The return value for this route's server loader function */
loaderData: T["loaderData"] | (HasErrorBoundary<T> extends true ? undefined : never);
/** Thrown errors that trigger error boundaries will be passed to the meta function. This is useful for generating metadata for error pages. */
error?: unknown;
/** An array of the current {@link https://api.reactrouter.com/v7/interfaces/react-router.UIMatch.html route matches}, including parent route matches. */
matches: MetaMatches<T["matches"]>;
};
type MetaDescriptors = MetaDescriptor[];
type HeadersArgs = {
loaderHeaders: Headers;
parentHeaders: Headers;
actionHeaders: Headers;
errorHeaders: Headers | undefined;
};
type CreateServerMiddlewareFunction<T extends RouteInfo> = (args: ServerDataFunctionArgs<T["params"]>, next: MiddlewareNextFunction<Response>) => MaybePromise<Response | void>;
type CreateClientMiddlewareFunction<T extends RouteInfo> = (args: ClientDataFunctionArgs<T["params"]>, next: MiddlewareNextFunction<Record<string, DataStrategyResult>>) => MaybePromise<Record<string, DataStrategyResult> | void>;
type CreateServerLoaderArgs<T extends RouteInfo> = ServerDataFunctionArgs<T["params"]>;
type CreateClientLoaderArgs<T extends RouteInfo> = ClientDataFunctionArgs<T["params"]> & {
/** This is an asynchronous function to get the data from the server loader for this route. On client-side navigations, this will make a {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API fetch} call to the React Router server loader. If you opt-into running your clientLoader on hydration, then this function will return the data that was already loaded on the server (via Promise.resolve). */
serverLoader: () => Promise<ServerDataFrom<T["module"]["loader"]>>;
};
type CreateServerActionArgs<T extends RouteInfo> = ServerDataFunctionArgs<T["params"]>;
type CreateClientActionArgs<T extends RouteInfo> = ClientDataFunctionArgs<T["params"]> & {
/** This is an asynchronous function that makes the {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API fetch} call to the React Router server action for this route. */
serverAction: () => Promise<ServerDataFrom<T["module"]["action"]>>;
};
type CreateHydrateFallbackProps<T extends RouteInfo, RSCEnabled extends boolean> = {
params: T["params"];
} & (RSCEnabled extends true ? {
/** The data returned from the `loader` */
loaderData?: ServerDataFrom<T["module"]["loader"]>;
/** The data returned from the `action` following an action submission. */
actionData?: ServerDataFrom<T["module"]["action"]>;
} : {
/** The data returned from the `loader` or `clientLoader` */
loaderData?: T["loaderData"];
/** The data returned from the `action` or `clientAction` following an action submission. */
actionData?: T["actionData"];
});
type Match<T extends MatchInfo> = Pretty<{
id: T["id"];
params: Record<string, string | undefined>;
pathname: string;
/** @deprecated Use `Match.loaderData` instead */
data: GetLoaderData<T["module"]>;
loaderData: GetLoaderData<T["module"]>;
handle: unknown;
}>;
type Matches<T extends Array<MatchInfo>> = T extends [infer F extends MatchInfo, ...infer R extends Array<MatchInfo>] ? [Match<F>, ...Matches<R>] : Array<Match<MatchInfo> | undefined>;
type CreateComponentProps<T extends RouteInfo, RSCEnabled extends boolean> = {
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export default function Component({
* params,
* }: Route.ComponentProps) {
* params.teamId;
* // ^ string
* }
**/
params: T["params"];
/** An array of the current {@link https://api.reactrouter.com/v7/interfaces/react-router.UIMatch.html route matches}, including parent route matches. */
matches: Matches<T["matches"]>;
} & (RSCEnabled extends true ? {
/** The data returned from the `loader` */
loaderData: ServerDataFrom<T["module"]["loader"]>;
/** The data returned from the `action` following an action submission. */
actionData?: ServerDataFrom<T["module"]["action"]>;
} : {
/** The data returned from the `loader` or `clientLoader` */
loaderData: T["loaderData"];
/** The data returned from the `action` or `clientAction` following an action submission. */
actionData?: T["actionData"];
});
type CreateErrorBoundaryProps<T extends RouteInfo, RSCEnabled extends boolean> = {
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function ErrorBoundary({
* params,
* }: Route.ErrorBoundaryProps) {
* params.teamId;
* // ^ string
* }
**/
params: T["params"];
error: unknown;
} & (RSCEnabled extends true ? {
/** The data returned from the `loader` */
loaderData?: ServerDataFrom<T["module"]["loader"]>;
/** The data returned from the `action` following an action submission. */
actionData?: ServerDataFrom<T["module"]["action"]>;
} : {
/** The data returned from the `loader` or `clientLoader` */
loaderData?: T["loaderData"];
/** The data returned from the `action` or `clientAction` following an action submission. */
actionData?: T["actionData"];
});
type GetAnnotations<Info extends RouteInfo> = {
LinkDescriptors: LinkDescriptor[];
LinksFunction: () => LinkDescriptor[];
MetaArgs: CreateMetaArgs<Info>;
MetaDescriptors: MetaDescriptors;
MetaFunction: (args: CreateMetaArgs<Info>) => MetaDescriptors;
HeadersArgs: HeadersArgs;
HeadersFunction: (args: HeadersArgs) => Headers | HeadersInit;
MiddlewareFunction: CreateServerMiddlewareFunction<Info>;
ClientMiddlewareFunction: CreateClientMiddlewareFunction<Info>;
LoaderArgs: CreateServerLoaderArgs<Info>;
ClientLoaderArgs: CreateClientLoaderArgs<Info>;
ActionArgs: CreateServerActionArgs<Info>;
ClientActionArgs: CreateClientActionArgs<Info>;
HydrateFallbackProps: CreateHydrateFallbackProps<Info, false>;
ServerHydrateFallbackProps: CreateHydrateFallbackProps<Info, true>;
ComponentProps: CreateComponentProps<Info, false>;
ServerComponentProps: CreateComponentProps<Info, true>;
ErrorBoundaryProps: CreateErrorBoundaryProps<Info, false>;
ServerErrorBoundaryProps: CreateErrorBoundaryProps<Info, true>;
};
type Params<RouteFile extends keyof RouteFiles> = Normalize<Pages[RouteFiles[RouteFile]["page"]]["params"]>;
type GetInfo<T extends {
file: keyof RouteFiles;
module: RouteModule;
}> = {
params: Params<T["file"]>;
loaderData: GetLoaderData<T["module"]>;
actionData: GetActionData<T["module"]>;
};
export type { GetAnnotations, GetInfo };
@@ -0,0 +1,184 @@
import { R as RouteModule, v as LinkDescriptor, L as Location, w as Func, x as Pretty, y as MetaDescriptor, G as GetLoaderData, z as ServerDataFunctionArgs, B as MiddlewareNextFunction, E as ClientDataFunctionArgs, I as DataStrategyResult, J as ServerDataFrom, N as Normalize, K as GetActionData } from '../../data-CjO11-hU.js';
import { R as RouteFiles, P as Pages } from '../../register-roq_0qYo.js';
import 'react';
type MaybePromise<T> = T | Promise<T>;
type Props = {
params: unknown;
loaderData: unknown;
actionData: unknown;
};
type RouteInfo = Props & {
module: RouteModule;
matches: Array<MatchInfo>;
};
type MatchInfo = {
id: string;
module: RouteModule;
};
type MetaMatch<T extends MatchInfo> = Pretty<{
id: T["id"];
params: Record<string, string | undefined>;
pathname: string;
meta: MetaDescriptor[];
/** @deprecated Use `MetaMatch.loaderData` instead */
data: GetLoaderData<T["module"]>;
loaderData: GetLoaderData<T["module"]>;
handle?: unknown;
error?: unknown;
}>;
type MetaMatches<T extends Array<MatchInfo>> = T extends [infer F extends MatchInfo, ...infer R extends Array<MatchInfo>] ? [MetaMatch<F>, ...MetaMatches<R>] : Array<MetaMatch<MatchInfo> | undefined>;
type HasErrorBoundary<T extends RouteInfo> = T["module"] extends {
ErrorBoundary: Func;
} ? true : false;
type CreateMetaArgs<T extends RouteInfo> = {
/** This is the current router `Location` object. This is useful for generating tags for routes at specific paths or query parameters. */
location: Location;
/** {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route. */
params: T["params"];
/**
* The return value for this route's server loader function
*
* @deprecated Use `Route.MetaArgs.loaderData` instead
*/
data: T["loaderData"] | (HasErrorBoundary<T> extends true ? undefined : never);
/** The return value for this route's server loader function */
loaderData: T["loaderData"] | (HasErrorBoundary<T> extends true ? undefined : never);
/** Thrown errors that trigger error boundaries will be passed to the meta function. This is useful for generating metadata for error pages. */
error?: unknown;
/** An array of the current {@link https://api.reactrouter.com/v7/interfaces/react-router.UIMatch.html route matches}, including parent route matches. */
matches: MetaMatches<T["matches"]>;
};
type MetaDescriptors = MetaDescriptor[];
type HeadersArgs = {
loaderHeaders: Headers;
parentHeaders: Headers;
actionHeaders: Headers;
errorHeaders: Headers | undefined;
};
type CreateServerMiddlewareFunction<T extends RouteInfo> = (args: ServerDataFunctionArgs<T["params"]>, next: MiddlewareNextFunction<Response>) => MaybePromise<Response | void>;
type CreateClientMiddlewareFunction<T extends RouteInfo> = (args: ClientDataFunctionArgs<T["params"]>, next: MiddlewareNextFunction<Record<string, DataStrategyResult>>) => MaybePromise<Record<string, DataStrategyResult> | void>;
type CreateServerLoaderArgs<T extends RouteInfo> = ServerDataFunctionArgs<T["params"]>;
type CreateClientLoaderArgs<T extends RouteInfo> = ClientDataFunctionArgs<T["params"]> & {
/** This is an asynchronous function to get the data from the server loader for this route. On client-side navigations, this will make a {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API fetch} call to the React Router server loader. If you opt-into running your clientLoader on hydration, then this function will return the data that was already loaded on the server (via Promise.resolve). */
serverLoader: () => Promise<ServerDataFrom<T["module"]["loader"]>>;
};
type CreateServerActionArgs<T extends RouteInfo> = ServerDataFunctionArgs<T["params"]>;
type CreateClientActionArgs<T extends RouteInfo> = ClientDataFunctionArgs<T["params"]> & {
/** This is an asynchronous function that makes the {@link https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API fetch} call to the React Router server action for this route. */
serverAction: () => Promise<ServerDataFrom<T["module"]["action"]>>;
};
type CreateHydrateFallbackProps<T extends RouteInfo, RSCEnabled extends boolean> = {
params: T["params"];
} & (RSCEnabled extends true ? {
/** The data returned from the `loader` */
loaderData?: ServerDataFrom<T["module"]["loader"]>;
/** The data returned from the `action` following an action submission. */
actionData?: ServerDataFrom<T["module"]["action"]>;
} : {
/** The data returned from the `loader` or `clientLoader` */
loaderData?: T["loaderData"];
/** The data returned from the `action` or `clientAction` following an action submission. */
actionData?: T["actionData"];
});
type Match<T extends MatchInfo> = Pretty<{
id: T["id"];
params: Record<string, string | undefined>;
pathname: string;
/** @deprecated Use `Match.loaderData` instead */
data: GetLoaderData<T["module"]>;
loaderData: GetLoaderData<T["module"]>;
handle: unknown;
}>;
type Matches<T extends Array<MatchInfo>> = T extends [infer F extends MatchInfo, ...infer R extends Array<MatchInfo>] ? [Match<F>, ...Matches<R>] : Array<Match<MatchInfo> | undefined>;
type CreateComponentProps<T extends RouteInfo, RSCEnabled extends boolean> = {
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export default function Component({
* params,
* }: Route.ComponentProps) {
* params.teamId;
* // ^ string
* }
**/
params: T["params"];
/** An array of the current {@link https://api.reactrouter.com/v7/interfaces/react-router.UIMatch.html route matches}, including parent route matches. */
matches: Matches<T["matches"]>;
} & (RSCEnabled extends true ? {
/** The data returned from the `loader` */
loaderData: ServerDataFrom<T["module"]["loader"]>;
/** The data returned from the `action` following an action submission. */
actionData?: ServerDataFrom<T["module"]["action"]>;
} : {
/** The data returned from the `loader` or `clientLoader` */
loaderData: T["loaderData"];
/** The data returned from the `action` or `clientAction` following an action submission. */
actionData?: T["actionData"];
});
type CreateErrorBoundaryProps<T extends RouteInfo, RSCEnabled extends boolean> = {
/**
* {@link https://reactrouter.com/start/framework/routing#dynamic-segments Dynamic route params} for the current route.
* @example
* // app/routes.ts
* route("teams/:teamId", "./team.tsx"),
*
* // app/team.tsx
* export function ErrorBoundary({
* params,
* }: Route.ErrorBoundaryProps) {
* params.teamId;
* // ^ string
* }
**/
params: T["params"];
error: unknown;
} & (RSCEnabled extends true ? {
/** The data returned from the `loader` */
loaderData?: ServerDataFrom<T["module"]["loader"]>;
/** The data returned from the `action` following an action submission. */
actionData?: ServerDataFrom<T["module"]["action"]>;
} : {
/** The data returned from the `loader` or `clientLoader` */
loaderData?: T["loaderData"];
/** The data returned from the `action` or `clientAction` following an action submission. */
actionData?: T["actionData"];
});
type GetAnnotations<Info extends RouteInfo> = {
LinkDescriptors: LinkDescriptor[];
LinksFunction: () => LinkDescriptor[];
MetaArgs: CreateMetaArgs<Info>;
MetaDescriptors: MetaDescriptors;
MetaFunction: (args: CreateMetaArgs<Info>) => MetaDescriptors;
HeadersArgs: HeadersArgs;
HeadersFunction: (args: HeadersArgs) => Headers | HeadersInit;
MiddlewareFunction: CreateServerMiddlewareFunction<Info>;
ClientMiddlewareFunction: CreateClientMiddlewareFunction<Info>;
LoaderArgs: CreateServerLoaderArgs<Info>;
ClientLoaderArgs: CreateClientLoaderArgs<Info>;
ActionArgs: CreateServerActionArgs<Info>;
ClientActionArgs: CreateClientActionArgs<Info>;
HydrateFallbackProps: CreateHydrateFallbackProps<Info, false>;
ServerHydrateFallbackProps: CreateHydrateFallbackProps<Info, true>;
ComponentProps: CreateComponentProps<Info, false>;
ServerComponentProps: CreateComponentProps<Info, true>;
ErrorBoundaryProps: CreateErrorBoundaryProps<Info, false>;
ServerErrorBoundaryProps: CreateErrorBoundaryProps<Info, true>;
};
type Params<RouteFile extends keyof RouteFiles> = Normalize<Pages[RouteFiles[RouteFile]["page"]]["params"]>;
type GetInfo<T extends {
file: keyof RouteFiles;
module: RouteModule;
}> = {
params: Params<T["file"]>;
loaderData: GetLoaderData<T["module"]>;
actionData: GetActionData<T["module"]>;
};
export type { GetAnnotations, GetInfo };
@@ -0,0 +1,10 @@
"use strict";/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
@@ -0,0 +1,10 @@
/**
* react-router v7.18.1
*
* Copyright (c) Remix Software Inc.
*
* This source code is licensed under the MIT license found in the
* LICENSE.md file in the root directory of this source tree.
*
* @license MIT
*/
@@ -0,0 +1,30 @@
import { R as RouteModule } from './data-DEjBmEfD.mjs';
/**
* Apps can use this interface to "register" app-wide types for React Router via interface declaration merging and module augmentation.
* React Router should handle this for you via type generation.
*
* For more on declaration merging and module augmentation, see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation .
*/
interface Register {
}
type AnyParams = Record<string, string | undefined>;
type AnyPages = Record<string, {
params: AnyParams;
}>;
type Pages = Register extends {
pages: infer Registered extends AnyPages;
} ? Registered : AnyPages;
type AnyRouteFiles = Record<string, {
id: string;
page: string;
}>;
type RouteFiles = Register extends {
routeFiles: infer Registered extends AnyRouteFiles;
} ? Registered : AnyRouteFiles;
type AnyRouteModules = Record<string, RouteModule>;
type RouteModules = Register extends {
routeModules: infer Registered extends AnyRouteModules;
} ? Registered : AnyRouteModules;
export type { Pages as P, RouteFiles as R, RouteModules as a, Register as b };
@@ -0,0 +1,30 @@
import { R as RouteModule } from './data-CjO11-hU.js';
/**
* Apps can use this interface to "register" app-wide types for React Router via interface declaration merging and module augmentation.
* React Router should handle this for you via type generation.
*
* For more on declaration merging and module augmentation, see https://www.typescriptlang.org/docs/handbook/declaration-merging.html#module-augmentation .
*/
interface Register {
}
type AnyParams = Record<string, string | undefined>;
type AnyPages = Record<string, {
params: AnyParams;
}>;
type Pages = Register extends {
pages: infer Registered extends AnyPages;
} ? Registered : AnyPages;
type AnyRouteFiles = Record<string, {
id: string;
page: string;
}>;
type RouteFiles = Register extends {
routeFiles: infer Registered extends AnyRouteFiles;
} ? Registered : AnyRouteFiles;
type AnyRouteModules = Record<string, RouteModule>;
type RouteModules = Register extends {
routeModules: infer Registered extends AnyRouteModules;
} ? Registered : AnyRouteModules;
export type { Pages as P, RouteFiles as R, RouteModules as a, Register as b };