* @returns Cleanup function that unregisters the bindings
*
* @example
* ```ts
* "use client";
* import { registerRouteResources } from "@lazarv/react-server/navigation";
* import { todos } from "./resources.client";
*
* registerRouteResources("/todos", [
* todos.from((_, search) => ({ filter: search.filter ?? "all" })),
* ]);
* ```
*/
export function registerRouteResources(
path: string,
resources: (RouteResourceBinding | RouteResource)[]
): () => void;
// ── Typed route definitions & hooks ──
import type {
RouteDescriptor,
RouteValidate,
RouteParse,
SearchParamsProps,
ExtractParams,
} from "../server/router";
// ── Client-safe createRoute (no element, no .Route) ──
interface ClientRouteOptions<TParams = any, TSearch = Record<string, string>> {
exact?: boolean;
validate?: RouteValidate<TParams, TSearch>;
parse?: RouteParse<TParams, TSearch>;
}
/**
* Client-safe route factory — returns a `RouteDescriptor` with `path`,
* `validate`, `href()`, `.Link`, `.useParams()`, and `.useSearchParams()`
* — but no `.Route`.
*
* Use this in shared route definition files that are imported by
* both server components and client components.
*
* @example
* ```ts
* // routes.ts (shared)
* import { createRoute } from "@lazarv/react-server/navigation";
* import { z } from "zod";
*
* export const user = createRoute("/user/[id]", {
* validate: { params: z.object({ id: z.string() }) },
* });
*
* // Client component:
* const params = user.useParams(); // typed!
* user.href({ id: "42" }); // → "/user/42"
* <user.Link params={{ id: "42" }}>User 42</user.Link>
* ```
*/
export function createRoute<
TPath extends string,
TParams = ExtractParams<TPath>,
TSearch = Record<string, string>,
>(
path: TPath,
options?: ClientRouteOptions<TParams, TSearch>
): RouteDescriptor<TPath, TParams, TSearch>;
export function createRoute(
path: "*",
options?: Omit<ClientRouteOptions, "exact">
): RouteDescriptor<"*", {}, {}>;
/** Create a scoped fallback descriptor — matches any URL under the given prefix */
export function createRoute(
path: `${string}/*`,
options?: Omit<ClientRouteOptions, "exact">
): RouteDescriptor<string, {}, {}>;
export function createRoute(
options?: Omit<ClientRouteOptions, "exact">
): RouteDescriptor<"*", {}, {}>;
export function createRoute(): RouteDescriptor<"*", {}, {}>;
/**
* Read typed, validated params for a route.
* Uses the route's `validate.params` schema (if provided) to parse the raw match.
*
* @param route - A route created by `createRoute`.
* @returns Parsed params, or `null` if the route doesn't match / validation fails.
*
* @example
* ```tsx
* import { useRouteParams } from "@lazarv/react-server/navigation";
* import { user } from "./routes";
*
* const { id } = useRouteParams(user); // id: string
* ```
*/
export function useRouteParams<TPath extends string, TParams, TSearch>(
route: RouteDescriptor<TPath, TParams, TSearch>
): TParams | null;
/**
* Test if a route matches the current pathname and return typed params (or null).
*
* @param route - A route created by `createRoute`.
* @returns Matched params or null.
*
* @example
* ```tsx
* import { useRouteMatch } from "@lazarv/react-server/navigation";
* import { user } from "./routes";
*
* const match = useRouteMatch(user);
* if (match) console.log(match.id);
* ```
*/
export function useRouteMatch<TPath extends string, TParams, TSearch>(
route: RouteDescriptor<TPath, TParams, TSearch>
): TParams | null;
/**
* Read typed, validated search params for a route.
* Uses the route's `validate.search` schema (if provided) to parse query params.
*
* @param route - A route created by `createRoute` with `validate.search`.
* @returns Parsed search params.
*
* @example
* ```tsx
* import { useRouteSearchParams } from "@lazarv/react-server/navigation";
* import { products } from "./routes";
*
* const { sort, page } = useRouteSearchParams(products);
* ```
*/
export function useRouteSearchParams<TPath extends string, TParams, TSearch>(
route: RouteDescriptor<TPath, TParams, TSearch>
): TSearch;
/** Options when navigating to a route descriptor */
export interface RouteNavigateOptions<TParams = {}, TSearch = {}> {
/** Path params — required when the route has dynamic segments */
params?: TParams;
/**
* Search params — merged with the current URL search params.
*
* Object form: values are merged on top of current params (null removes a key).
* Function form: receives decoded current search params, returns the next search object.
*/
search?: Partial<TSearch> | ((prev: TSearch) => TSearch);
outlet?: string;
push?: boolean;
rollback?: number;
signal?: AbortSignal;
fallback?: React.ReactNode;
Component?: React.ReactNode;
}
type NavigateToUrl = import("./index").ReactServerClientContext["navigate"];
/**
* Navigate function returned by `useNavigate()`.
*
* Accepts either:
* - A URL string + optional nav options (classic mode)
* - A route descriptor + options with typed `params` and `search` (typed mode)
*
* In typed mode, `search` is **merged** with the current URL search params.
*
* @example
* ```tsx
* const navigate = useNavigate();
*
* // Classic — plain URL
* navigate("/about");
*
* // Typed — route with search params (merged with current URL)
* navigate(products, { search: { sort: "price", page: 2 } });
*
* // Typed — route with path params + search
* navigate(user, { params: { id: "42" }, search: { tab: "posts" } });
* ```
*/
export interface NavigateFunction {
/** Navigate to a plain URL string */
(url: string, options?: Parameters<NavigateToUrl>[1]): Promise<void>;
/** Navigate to a typed route descriptor */
<TPath extends string, TParams, TSearch>(
route: RouteDescriptor<TPath, TParams, TSearch>,
options?: RouteNavigateOptions<TParams, TSearch>
): Promise<void>;
}
/**
* Returns a `navigate` function that accepts a URL string or a route descriptor.