/** * Client-safe type-safe href function * * This is a compile-time only href that validates paths against registered routes. * No runtime route map lookup - just an identity function with TypeScript validation. * * @example * ```typescript * import { href } from "rsc-router/client"; * * href("/blog/my-post"); // ✓ matches /blog/:slug * href("/shop/product/widget"); // ✓ matches /shop/product/:slug * href("/invalid"); // ✗ TypeScript error * ``` */ import type { GetRegisteredRoutes } from "./types.js"; import type { ResponseEnvelope } from "./urls.js"; /** * Parse constraint values into a union type for paths * "a|b|c" → "a" | "b" | "c" */ type ParseConstraintPath = T extends `${infer First}|${infer Rest}` ? First | ParseConstraintPath : T; /** * Convert a route pattern to a template literal type * * Supports: * - Static: /about → "/about" * - Dynamic: /blog/:slug → `/blog/${string}` * - Optional: /:locale?/blog → "/blog" | `/${string}/blog` * - Constrained: /:locale(en|gb)/blog → "/en/blog" | "/gb/blog" * - Optional + Constrained: /:locale(en|gb)?/blog → "/blog" | "/en/blog" | "/gb/blog" * * @example * PatternToPath<"/blog/:slug"> = `/blog/${string}` * PatternToPath<"/:locale?/blog"> = "/blog" | `/${string}/blog` * PatternToPath<"/:locale(en|gb)/blog"> = "/en/blog" | "/gb/blog" * PatternToPath<"/:locale(en|gb)?/blog"> = "/blog" | "/en/blog" | "/gb/blog" */ export type PatternToPath = T extends `${infer Before}:${infer _Name}(${infer Constraint})?/${infer After}` ? PatternToPath<`${Before}${After}`> | `${Before}${ParseConstraintPath}/${PatternToPath}` : T extends `${infer Before}:${infer _Name}(${infer Constraint})?` ? Before | `${Before}${ParseConstraintPath}` : T extends `${infer Before}:${infer _Name}(${infer Constraint})/${infer After}` ? `${Before}${ParseConstraintPath}/${PatternToPath}` : T extends `${infer Before}:${infer _Name}(${infer Constraint})` ? `${Before}${ParseConstraintPath}` : T extends `${infer Before}:${infer _Param}?/${infer After}` ? PatternToPath<`${Before}${After}`> | `${Before}${string}/${PatternToPath}` : T extends `${infer Before}:${infer _Param}?` ? Before | `${Before}${string}` : T extends `${infer Before}:${infer _Param}/${infer After}` ? `${Before}${string}/${PatternToPath}` : T extends `${infer Before}:${infer _Param}` ? `${Before}${string}` : T; /** * Allow optional query string (?...) and/or hash fragment (#...) suffix * * @example * WithSuffix<"/about"> = "/about" | "/about?..." | "/about#..." | "/about?...#..." */ type WithSuffix = T | `${T}?${string}` | `${T}#${string}` | `${T}?${string}#${string}`; /** * Helper type to get pattern from routes, handling string values and { path, response } objects */ type RoutePattern = TRoutes[K] extends string ? TRoutes[K] : TRoutes[K] extends { readonly path: infer P extends string; } ? P : string; /** * Look up the response data type for a route pattern from RegisteredRoutes. * * Works by reverse-looking up the route name for the given pattern, * then extracting the response type from the route entry. * * For static routes (no params), pattern === path: * PathResponse<"/api/health"> → { status: string; timestamp: number } * * For dynamic routes, use the pattern: * PathResponse<"/api/products/:id"> → Product */ export type PathResponse = ResponseEnvelope<{ [K in keyof TRoutes]: RoutePattern extends TPattern ? TRoutes[K] extends { readonly response: infer R; } ? Exclude : never : never; }[keyof TRoutes]>; /** * Strip trailing slash from a path (e.g., "/blog/" -> "/blog" | "/blog/") * Allows navigation to include() prefixes without requiring trailing slash */ type OptionalTrailingSlash = T extends `${infer Base}/` ? (Base extends "" ? T : Base | T) : T; /** * Union of all valid paths from registered routes * * Generated from RSCRouter.RegisteredRoutes via module augmentation. * Allows optional query strings and hash fragments. */ export type ValidPaths = keyof TRoutes extends never ? `/${string}` : WithSuffix<{ [K in keyof TRoutes]: OptionalTrailingSlash>>; }[keyof TRoutes]>; /** * Type-safe href function for client-side use * * Without mount: identity function, validates absolute paths at compile time. * With mount: prepends mount path, for use with useMount() inside include() scopes. * * @param path - A valid path matching one of the registered route patterns * @param mount - Optional mount prefix from useMount() for include-scoped paths * @returns The resolved path * * @example * ```typescript * // Absolute paths (type-safe) * href("/blog/hello"); // "/blog/hello" * href("/shop/product/widget"); // "/shop/product/widget" * * // With mount (inside an include) * const mount = useMount(); // "/articles" * href("/", mount); // "/articles/" * href("/my-post", mount); // "/articles/my-post" * * // Query strings and hashes pass through * href("/blog/hello?page=1"); * href("/about#contact"); * ``` */ export declare function href(path: T, mount?: string): string; /** * Props shape returned by href.json() etc. for spreading on . * Sets data-external to trigger hard navigation (skips RSC fetch). */ export interface ResponseHrefProps { to: string; "data-external": ""; } type ResponseHrefFn = (path: T, mount?: string) => ResponseHrefProps; export declare namespace href { const json: ResponseHrefFn; const text: ResponseHrefFn; const html: ResponseHrefFn; const xml: ResponseHrefFn; const md: ResponseHrefFn; const image: ResponseHrefFn; const stream: ResponseHrefFn; const any: ResponseHrefFn; } export {}; //# sourceMappingURL=href-client.d.ts.map