fully-react
Version:
React Server for Vite
933 lines (872 loc) • 23.7 kB
text/typescript
import { ParsePathParams } from "./link";
import { AnyRouter, Router } from "./router";
import {
IsAny,
NoInfer,
PickRequired,
PickUnsafe,
UnionToIntersection,
} from "./utils";
import invariant from "tiny-invariant";
import { joinPaths, trimPath, trimPathRight } from "./path";
import { AnyRoutesInfo, DefaultRoutesInfo } from "./routeInfo";
import { RouteMatch } from "./routeMatch";
export const rootRouteId = "__root__" as const;
export type RootRouteId = typeof rootRouteId;
export type AnyPathParams = {};
export type AnySearchSchema = {};
export type AnyContext = {};
export interface RouteMeta {}
export interface RouteContext {}
export type RouteOptionsBase<TCustomId, TPath> =
| {
path: TPath;
}
| {
id: TCustomId;
};
export type RouteOptionsBaseIntersection<TCustomId, TPath> =
UnionToIntersection<RouteOptionsBase<TCustomId, TPath>>;
export type MetaOptions = keyof PickRequired<RouteMeta> extends never
? {
meta?: RouteMeta;
}
: {
meta: RouteMeta;
};
type GetContextFn<
TParentRoute,
TAllParams,
TFullSearchSchema,
TParentContext,
TAllParentContext,
TRouteContext,
> = (
opts: {
params: TAllParams;
search: TFullSearchSchema;
} & (TParentRoute extends undefined
? {
context?: TAllParentContext;
parentContext?: TParentContext;
}
: {
context: TAllParentContext;
parentContext: TParentContext;
}),
) => TRouteContext;
export type ContextOptions<
TParentRoute,
TAllParams,
TFullSearchSchema,
TParentContext,
TAllParentContext,
TRouteContext,
> = keyof PickRequired<RouteContext> extends never
? {
getContext?: GetContextFn<
TParentRoute,
TAllParams,
TFullSearchSchema,
TParentContext,
TAllParentContext,
TRouteContext
>;
}
: {
getContext: GetContextFn<
TParentRoute,
TAllParams,
TFullSearchSchema,
TParentContext,
TAllParentContext,
TRouteContext
>;
};
export type RouteProps<
TParentRoute extends AnyRoute,
TPath extends string,
TFullPath extends ResolveFullPath<
TParentRoute,
TPath,
RoutePrefix<TParentRoute["fullPath"], TPath>
>,
TCustomId extends string,
TId extends ResolveId<TParentRoute, TCustomId, TPath>,
TLoader,
TSearchSchema extends AnySearchSchema,
TFullSearchSchema extends AnySearchSchema,
TAllParams,
TParentContext,
TAllParentContext extends IsAny<
TParentRoute["__types"]["allParams"],
TParentContext,
TParentRoute["__types"]["allParams"] & TParentContext
>,
TRouteContext,
TContext,
TRouterContext extends AnyContext,
> = {
useMatch: () => RouteMatch<
AnyRoutesInfo,
Route<
TParentRoute,
TPath,
TFullPath,
TCustomId,
TId,
TLoader,
TSearchSchema,
TFullSearchSchema,
TParentContext,
TAllParentContext,
TRouteContext,
TContext,
TRouterContext,
any,
any
>
>;
useLoader: () => TLoader;
useSearch: <
TStrict extends boolean = true,
TSearch = TFullSearchSchema,
TSelected = TSearch,
>(opts?: {
strict?: TStrict;
track?: (search: TSearch) => TSelected;
}) => TStrict extends true ? TSelected : TSelected | undefined;
useParams: <
TDefaultSelected = TAllParams,
TSelected = TDefaultSelected,
>(opts?: {
track?: (search: TDefaultSelected) => TSelected;
}) => TSelected;
useContext: () => TContext;
// navigate: <T extends TFullPath, TTo extends string = ''>(
// opts?: MakeLinkOptions<T, TTo>,
// ) => Promise<void>
};
export type TypedRouteOptions<
TParentRoute extends AnyRoute = AnyRoute,
TPath extends string = string,
TSearchSchema extends AnySearchSchema = {},
TParams = Record<ParsePathParams<TPath>, any>,
TLoader = unknown,
TCustomId extends string = string,
TParentSearchSchema extends {} = {},
TFullSearchSchema extends AnySearchSchema = TSearchSchema,
TParentParams extends AnyPathParams = {},
TAllParams = TParams,
TParentContext extends AnyContext = AnyContext,
TAllParentContext extends IsAny<
TParentRoute["__types"]["allParams"],
TParentContext,
TParentRoute["__types"]["allParams"] & TParentContext
> = IsAny<
TParentRoute["__types"]["allParams"],
TParentContext,
TParentRoute["__types"]["allParams"] & TParentContext
>,
TRouteContext extends RouteContext = RouteContext,
TContext extends MergeFromParent<
TAllParentContext,
TRouteContext
> = MergeFromParent<TAllParentContext, TRouteContext>,
> = Omit<
RouteOptions<
TParentRoute,
TCustomId,
TPath,
TLoader,
TParentSearchSchema,
TSearchSchema,
TFullSearchSchema,
TParentParams,
TParams,
TAllParams,
TParentContext,
TAllParentContext,
TRouteContext
>,
"id" | "path" | "getParentRoute"
>;
export type RouteOptions<
TParentRoute extends AnyRoute = AnyRoute,
TCustomId extends string = string,
TPath extends string = string,
TLoader = unknown,
TParentSearchSchema extends {} = {},
TSearchSchema extends AnySearchSchema = {},
TFullSearchSchema extends AnySearchSchema = TSearchSchema,
TParentParams extends AnyPathParams = {},
TParams = Record<ParsePathParams<TPath>, any>,
TAllParams = TParams,
TParentContext extends AnyContext = AnyContext,
TAllParentContext extends IsAny<
TParentRoute["__types"]["allParams"],
TParentContext,
TParentRoute["__types"]["allParams"] & TParentContext
> = IsAny<
TParentRoute["__types"]["allParams"],
TParentContext,
TParentRoute["__types"]["allParams"] & TParentContext
>,
TRouteContext extends RouteContext = RouteContext,
TContext extends MergeFromParent<
TAllParentContext,
TRouteContext
> = MergeFromParent<TAllParentContext, TRouteContext>,
> = RouteOptionsBase<TCustomId, TPath> & {
getParentRoute: () => TParentRoute;
// If true, this route will be matched as case-sensitive
caseSensitive?: boolean;
// Filter functions that can manipulate search params *before* they are passed to links and navigate
// calls that match this route.
preSearchFilters?: SearchFilter<TFullSearchSchema>[];
// Filter functions that can manipulate search params *after* they are passed to links and navigate
// calls that match this route.
postSearchFilters?: SearchFilter<TFullSearchSchema>[];
// The content to be rendered when the route is matched. If no component is provided, defaults to `<Outlet />`
// component?: RouteComponent<
// RouteProps<
// TParentRoute,
// TPath,
// ResolveFullPath<TParentRoute, TPath>,
// TCustomId,
// ResolveId<TParentRoute, TCustomId, TPath>,
// TLoader,
// TSearchSchema,
// TFullSearchSchema,
// TAllParams,
// TParentContext,
// TAllParentContext,
// TRouteContext,
// TContext,
// NoInfer<TRouteContext>
// >
// >; //
// // The content to be rendered when the route encounters an error
// errorComponent?: RouteComponent<{
// error: Error;
// info: { componentStack: string };
// }>; //
// // If supported by your framework, the content to be rendered as the fallback content until the route is ready to render
// pendingComponent?: RouteComponent<
// RouteProps<
// TParentRoute,
// TPath,
// ResolveFullPath<TParentRoute, TPath>,
// TCustomId,
// ResolveId<TParentRoute, TCustomId, TPath>,
// TLoader,
// TSearchSchema,
// TFullSearchSchema,
// TAllParams,
// TParentContext,
// TAllParentContext,
// TRouteContext,
// TContext,
// NoInfer<TRouteContext>
// >
// >; //
wrapInSuspense?: boolean;
// This async function is called before a route is loaded.
// If an error is thrown here, the route's loader will not be called.
// If thrown during a navigation, the navigation will be cancelled and the error will be passed to the `onLoadError` function.
// If thrown during a preload event, the error will be logged to the console.
beforeLoad?: (opts: {
router: AnyRouter;
match: RouteMatch;
}) => Promise<void> | void;
// This function will be called if the route's loader throws an error **during an attempted navigation**.
// If you want to redirect due to an error, call `router.navigate()` from within this function.
onBeforeLoadError?: (err: any) => void;
validateSearch?: SearchSchemaValidator<TSearchSchema, TParentSearchSchema>;
// This function will be called if the route's validateSearch option throws an error **during an attempted validation**.
// If you want to redirect due to an error, call `router.navigate()` from within this function.
// If you want to display the errorComponent, rethrow the error
onValidateSearchError?: (err: any) => void;
// An asynchronous function responsible for preparing or fetching data for the route before it is rendered
loader?: OnLoadFn<
TLoader,
TSearchSchema,
TFullSearchSchema,
TAllParams,
NoInfer<TRouteContext>,
TContext
>;
onLoadError?: (err: any) => void;
onError?: (err: any) => void;
// This function is called
// when moving from an inactive state to an active one. Likewise, when moving from
// an active to an inactive state, the return function (if provided) is called.
onLoaded?: (matchContext: {
params: TAllParams;
search: TFullSearchSchema;
}) =>
| void
| undefined
| ((match: { params: TAllParams; search: TFullSearchSchema }) => void);
// This function is called when the route remains active from one transition to the next.
onTransition?: (match: {
params: TAllParams;
search: TFullSearchSchema;
}) => void;
} & MetaOptions &
ContextOptions<
TParentRoute,
TAllParams,
TFullSearchSchema,
TParentContext,
TAllParentContext,
TRouteContext
> &
(
| {
// Parse params optionally receives path params as strings and returns them in a parsed format (like a number or boolean)
parseParams?: (
rawParams: IsAny<TPath, any, Record<ParsePathParams<TPath>, string>>,
) => TParams extends Record<ParsePathParams<TPath>, any>
? TParams
: "parseParams must return an object";
stringifyParams?: (
params: NoInfer<TParams>,
) => Record<ParsePathParams<TPath>, string>;
}
| {
stringifyParams?: never;
parseParams?: never;
}
) &
(PickUnsafe<TParentParams, ParsePathParams<TPath>> extends never // Detect if an existing path param is being redefined
? { test?: PickUnsafe<TParentParams, ParsePathParams<TPath>> }
: "Cannot redefined path params in child routes!");
// The parse type here allows a zod schema to be passed directly to the validator
export type SearchSchemaValidator<TReturn, TParentSchema> =
| SearchSchemaValidatorObj<TReturn, TParentSchema>
| SearchSchemaValidatorFn<TReturn, TParentSchema>;
export type SearchSchemaValidatorObj<TReturn, TParentSchema> = {
parse?: SearchSchemaValidatorFn<TReturn, TParentSchema>;
};
export type SearchSchemaValidatorFn<TReturn, TParentSchema> = (
searchObj: Record<string, unknown>,
) => {} extends TParentSchema
? TReturn
: keyof TReturn extends keyof TParentSchema
? {
error: "Top level search params cannot be redefined by child routes!";
keys: keyof TReturn & keyof TParentSchema;
}
: TReturn;
export type DefinedPathParamWarning =
"Path params cannot be redefined by child routes!";
export type ParentParams<TParentParams> = AnyPathParams extends TParentParams
? {}
: {
[Key in keyof TParentParams]?: DefinedPathParamWarning;
};
export type OnLoadFn<
TLoader = unknown,
TSearchSchema extends AnySearchSchema = {},
TFullSearchSchema extends AnySearchSchema = {},
TAllParams = {},
TContext extends AnyContext = AnyContext,
TAllContext extends AnyContext = AnyContext,
> = (
loaderContext: LoaderContext<
TSearchSchema,
TFullSearchSchema,
TAllParams,
TContext,
TAllContext
>,
) => Promise<TLoader> | TLoader;
export interface LoaderContext<
TSearchSchema extends AnySearchSchema = {},
TFullSearchSchema extends AnySearchSchema = {},
TAllParams = {},
TContext extends AnyContext = AnyContext,
TAllContext extends AnyContext = AnyContext,
> {
params: TAllParams;
routeSearch: TSearchSchema;
search: TFullSearchSchema;
signal?: AbortSignal;
preload: boolean;
routeContext: TContext;
context: TAllContext;
}
export type UnloaderFn<TPath extends string> = (
routeMatch: RouteMatch<any, Route>,
) => void;
export type SearchFilter<T, U = T> = (prev: T) => U;
type ResolveId<
TParentRoute,
TCustomId extends string,
TPath extends string,
> = TParentRoute extends { id: infer TParentId extends string }
? RoutePrefix<TParentId, string extends TCustomId ? TPath : TCustomId>
: RootRouteId;
export type InferFullSearchSchema<TRoute> = TRoute extends {
isRoot: true;
__types: {
searchSchema: infer TSearchSchema;
};
}
? TSearchSchema
: TRoute extends {
__types: {
fullSearchSchema: infer TFullSearchSchema;
};
}
? TFullSearchSchema
: {};
export type ResolveFullSearchSchema<TParentRoute, TSearchSchema> =
InferFullSearchSchema<TParentRoute> & TSearchSchema;
export type AnyRoute = Route<
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any,
any
>;
type MergeFromParent<T, U> = IsAny<T, U, T & U>;
export type TypedRoute<
TParentRoute extends AnyRoute = AnyRoute,
TPath extends string = string,
TSearchSchema extends AnySearchSchema = {},
TParams extends Record<ParsePathParams<TPath>, any> = Record<
ParsePathParams<TPath>,
string
>,
TChildren extends unknown = unknown,
TFullPath extends ResolveFullPath<TParentRoute, TPath> = ResolveFullPath<
TParentRoute,
TPath
>,
TCustomId extends string = string,
TId extends ResolveId<TParentRoute, TCustomId, TPath> = ResolveId<
TParentRoute,
TCustomId,
TPath
>,
TLoader = unknown,
TFullSearchSchema extends AnySearchSchema = ResolveFullSearchSchema<
TParentRoute,
TSearchSchema
>,
TAllParams extends MergeFromParent<
TParentRoute["__types"]["allParams"],
TParams
> = MergeFromParent<TParentRoute["__types"]["allParams"], TParams>,
TParentContext extends TParentRoute["__types"]["routeContext"] = TParentRoute["__types"]["routeContext"],
TAllParentContext extends TParentRoute["__types"]["context"] = TParentRoute["__types"]["context"],
TRouteContext extends RouteContext = RouteContext,
TContext extends MergeFromParent<
TParentRoute["__types"]["context"],
TRouteContext
> = MergeFromParent<TParentRoute["__types"]["context"], TRouteContext>,
TRouterContext extends AnyContext = AnyContext,
TRoutesInfo extends DefaultRoutesInfo = DefaultRoutesInfo,
> = Route<
TParentRoute,
TPath,
TFullPath,
TCustomId,
TId,
TLoader,
TSearchSchema,
TFullSearchSchema,
TParams,
TAllParams,
TParentContext,
TAllParentContext,
TRouteContext,
TContext,
TRouterContext,
TChildren,
TRoutesInfo
>;
export class Route<
TParentRoute extends AnyRoute = AnyRoute,
TPath extends string = string,
TFullPath extends ResolveFullPath<TParentRoute, TPath> = ResolveFullPath<
TParentRoute,
TPath
>,
TCustomId extends string = string,
TId extends ResolveId<TParentRoute, TCustomId, TPath> = ResolveId<
TParentRoute,
TCustomId,
TPath
>,
TLoader = unknown,
TSearchSchema extends AnySearchSchema = {},
TFullSearchSchema extends AnySearchSchema = ResolveFullSearchSchema<
TParentRoute,
TSearchSchema
>,
TParams extends Record<ParsePathParams<TPath>, any> = Record<
ParsePathParams<TPath>,
string
>,
TAllParams extends MergeFromParent<
TParentRoute["__types"]["allParams"],
TParams
> = MergeFromParent<TParentRoute["__types"]["allParams"], TParams>,
TParentContext extends TParentRoute["__types"]["routeContext"] = TParentRoute["__types"]["routeContext"],
TAllParentContext extends TParentRoute["__types"]["context"] = TParentRoute["__types"]["context"],
TRouteContext extends RouteContext = RouteContext,
TContext extends MergeFromParent<
TParentRoute["__types"]["context"],
TRouteContext
> = MergeFromParent<TParentRoute["__types"]["context"], TRouteContext>,
TRouterContext extends AnyContext = AnyContext,
TChildren extends unknown = unknown,
TRoutesInfo extends DefaultRoutesInfo = DefaultRoutesInfo,
> {
__types!: {
parentRoute: TParentRoute;
path: TPath;
fullPath: TFullPath;
customId: TCustomId;
id: TId;
loader: TLoader;
searchSchema: TSearchSchema;
fullSearchSchema: TFullSearchSchema;
params: TParams;
allParams: TAllParams;
parentContext: TParentContext;
allParentContext: TAllParentContext;
routeContext: TRouteContext;
context: TContext;
children: TChildren;
routesInfo: TRoutesInfo;
routerContext: TRouterContext;
};
isRoot: TParentRoute extends Route<any> ? true : false;
options: RouteOptions<
TParentRoute,
TCustomId,
TPath,
TLoader,
InferFullSearchSchema<TParentRoute>,
TSearchSchema,
InferFullSearchSchema<TParentRoute> & TSearchSchema,
TParentRoute["__types"]["allParams"],
TParams,
TAllParams,
TParentContext,
TAllParentContext,
TRouteContext,
TContext
>;
// Set up in this.init()
parentRoute!: TParentRoute;
id!: TId;
// customId!: TCustomId
path!: TPath;
fullPath!: TFullPath;
// Optional
children?: TChildren;
originalIndex?: number;
router?: Router<TRoutesInfo["routeTree"], TRoutesInfo>;
constructor(
options: RouteOptions<
TParentRoute,
TCustomId,
TPath,
TLoader,
InferFullSearchSchema<TParentRoute>,
TSearchSchema,
TFullSearchSchema,
TParentRoute["__types"]["allParams"],
TParams,
TAllParams,
TParentContext,
TAllParentContext,
TRouteContext,
TContext
>,
) {
this.options = (options as any) || {};
this.isRoot = !options?.getParentRoute as any;
}
init = (opts: { originalIndex: number; router: AnyRouter }) => {
this.originalIndex = opts.originalIndex;
this.router = opts.router;
const allOptions = this.options as RouteOptions<
TParentRoute,
TCustomId,
TPath,
TLoader,
InferFullSearchSchema<TParentRoute>,
TSearchSchema,
TFullSearchSchema,
TParentRoute["__types"]["allParams"],
TParams,
TAllParams,
TParentContext,
TAllParentContext,
TRouteContext,
TContext
> &
RouteOptionsBaseIntersection<TCustomId, TPath>;
const isRoot = !allOptions?.path && !allOptions?.id;
this.parentRoute = this.options?.getParentRoute?.();
if (isRoot) {
this.path = rootRouteId as TPath;
} else {
invariant(
this.parentRoute,
`Child Route instances must pass a 'getParentRoute: () => ParentRoute' option that returns a Route instance.`,
);
}
let path: undefined | string = isRoot ? rootRouteId : allOptions.path;
// If the path is anything other than an index path, trim it up
if (path && path !== "/") {
path = trimPath(path);
}
const customId = allOptions?.id || path;
// Strip the parentId prefix from the first level of children
let id = isRoot
? rootRouteId
: joinPaths([
(this.parentRoute.id as any) === rootRouteId
? ""
: this.parentRoute.id,
customId,
]);
if (path === rootRouteId) {
path = "/";
}
if (id !== rootRouteId) {
id = joinPaths(["/", id]);
}
const fullPath =
id === rootRouteId
? "/"
: trimPathRight(joinPaths([this.parentRoute.fullPath, path]));
this.path = path as TPath;
this.id = id as TId;
// this.customId = customId as TCustomId
this.fullPath = fullPath as TFullPath;
};
addChildren = <TNewChildren extends AnyRoute[]>(
children: TNewChildren,
): Route<
TParentRoute,
TPath,
TFullPath,
TCustomId,
TId,
TLoader,
TSearchSchema,
TFullSearchSchema,
TParams,
TAllParams,
TParentContext,
TAllParentContext,
TRouteContext,
TContext,
TRouterContext,
TNewChildren,
TRoutesInfo
> => {
this.children = children as any;
return this as any;
};
}
export type AnyRootRoute = RootRoute<any, any, any, any>;
export type RouteWithChildren<
TRoute extends AnyRoute,
TNewChildren extends AnyRoute[],
> = Route<
TRoute["__types"]["parentRoute"],
TRoute["__types"]["path"],
TRoute["__types"]["fullPath"],
TRoute["__types"]["customId"],
TRoute["__types"]["id"],
TRoute["__types"]["loader"],
TRoute["__types"]["searchSchema"],
TRoute["__types"]["fullSearchSchema"],
TRoute["__types"]["params"],
TRoute["__types"]["allParams"],
TRoute["__types"]["parentContext"],
TRoute["__types"]["allParentContext"],
TRoute["__types"]["routeContext"],
TRoute["__types"]["context"],
TRoute["__types"]["routerContext"],
TNewChildren,
TRoute["__types"]["routesInfo"]
>;
// export type TypedRoute<
// TRoute extends AnyRoute,
// TPath extends string,
// TSearchSchema extends AnySearchSchema,
// > = Route<
// TRoute,
// TPath,
export class RootRoute<
TLoader = unknown,
TSearchSchema extends AnySearchSchema = {},
TContext extends RouteContext = RouteContext,
TRouterContext extends {} = {},
> extends Route<
any,
"/",
"/",
string,
RootRouteId,
TLoader,
TSearchSchema,
TSearchSchema,
{},
{},
TRouterContext,
TRouterContext,
MergeFromParent<TRouterContext, TContext>,
MergeFromParent<TRouterContext, TContext>,
TRouterContext,
any,
any
> {
constructor(
options?: Omit<
RouteOptions<
AnyRoute,
RootRouteId,
"",
TLoader,
{},
TSearchSchema,
NoInfer<TSearchSchema>,
{},
TRouterContext,
TRouterContext,
TContext,
NoInfer<TContext>
>,
| "path"
| "id"
| "getParentRoute"
| "caseSensitive"
| "parseParams"
| "stringifyParams"
>,
) {
super(options as any);
}
static withRouterContext = <TRouterContext extends {}>() => {
return <
TLoader = unknown,
TSearchSchema extends AnySearchSchema = {},
TContext extends {} = {},
>(
options?: Omit<
RouteOptions<
AnyRoute,
RootRouteId,
"",
TLoader,
{},
TSearchSchema,
NoInfer<TSearchSchema>,
{},
TRouterContext,
TRouterContext,
TContext,
TRouterContext & TContext
>,
"path" | "id" | "getParentRoute" | "caseSensitive"
>,
) =>
new RootRoute<TLoader, TSearchSchema, TContext, TRouterContext>(
options as any,
);
};
}
type ResolveFullPath<
TParentRoute extends AnyRoute,
TPath extends string,
TPrefixed extends RoutePrefix<TParentRoute["fullPath"], TPath> = RoutePrefix<
TParentRoute["fullPath"],
TPath
>,
> = TPrefixed extends RootRouteId ? "/" : TrimPathRight<`${TPrefixed}`>;
type RoutePrefix<
TPrefix extends string,
TId extends string,
> = string extends TId
? RootRouteId
: TId extends string
? TPrefix extends RootRouteId
? TId extends "/"
? "/"
: `/${TrimPath<TId>}`
: `${TPrefix}/${TId}` extends "/"
? "/"
: `/${TrimPathLeft<`${TrimPathRight<TPrefix>}/${TrimPath<TId>}`>}`
: never;
type TrimPath<T extends string> = "" extends T
? ""
: TrimPathRight<TrimPathLeft<T>>;
type TrimPathLeft<T extends string> = T extends `${RootRouteId}/${infer U}`
? TrimPathLeft<U>
: T extends `/${infer U}`
? TrimPathLeft<U>
: T;
type TrimPathRight<T extends string> = T extends "/"
? "/"
: T extends `${infer U}/`
? TrimPathRight<U>
: T;
// const rootRoute = new RootRoute({
// validateSearch: () => null as unknown as { root?: boolean },
// })
// const aRoute = new Route({
// getParentRoute: () => rootRoute,
// path: 'a',
// validateSearch: () => null as unknown as { a?: string },
// })
// const bRoute = new Route({
// getParentRoute: () => aRoute,
// path: 'b',
// })
// const rootIsRoot = rootRoute.isRoot
// // ^?
// const aIsRoot = aRoute.isRoot
// // ^?
// const rId = rootRoute.id
// // ^?
// const aId = aRoute.id
// // ^?
// const bId = bRoute.id
// // ^?
// const rPath = rootRoute.fullPath
// // ^?
// const aPath = aRoute.fullPath
// // ^?
// const bPath = bRoute.fullPath
// // ^?
// const rSearch = rootRoute.__types.fullSearchSchema
// // ^?
// const aSearch = aRoute.__types.fullSearchSchema
// // ^?
// const bSearch = bRoute.__types.fullSearchSchema
// // ^?
// const config = rootRoute.addChildren([aRoute.addChildren([bRoute])])
// // ^?