import type { ComponentType, ReactNode } from "react"; import type { SerializedManifest } from "../debug.js"; import type { ReverseFunction } from "../reverse.js"; import type { UrlPatterns } from "../urls.js"; import type { UrlBuilder, EnvCompatible } from "../urls/pattern-types.js"; import type { EntryData } from "../server/context"; import type { ErrorInfo, MatchResult } from "../types"; import type { NonceProvider } from "../rsc/types.js"; import type { ExecutionContext } from "../server/request-context.js"; import type { SerializedSegmentData, SegmentHandleData, } from "../cache/types.js"; import type { MiddlewareEntry, MiddlewareFn } from "./middleware.js"; import { RSC_ROUTER_BRAND } from "./router-registry.js"; import type { RangoOptions, RootLayoutProps } from "./router-options.js"; import type { DefaultVars } from "../types/global-namespace.js"; import type { ResolvedTimeouts, OnTimeoutCallback } from "./timeout.js"; /** * Options passed to router.fetch(), router.match(), and other request entrypoints. * All entrypoints use this same shape for consistency. */ export interface RouterRequestInput { env?: TEnv; vars?: Partial; ctx?: ExecutionContext; } /** * Merge route patterns with response types into a single route map. * Routes with response types get { path, response } objects; others stay as strings. * Handles both plain string routes and { path, search } object routes. */ type MergeRoutesWithResponses< TRoutes extends Record, TResponses, > = { [K in keyof TRoutes]: K extends keyof NonNullable ? unknown extends NonNullable[K] ? TRoutes[K] // RSC route — TData defaults to unknown, keep as-is : TRoutes[K] extends { readonly path: infer P extends string } ? TRoutes[K] & { readonly response: NonNullable[K] } : { readonly path: TRoutes[K] & string; readonly response: NonNullable[K]; } : TRoutes[K]; }; /** * Public Rango router interface — the user-facing API surface. * * Users interact with this type when building and using routers. * Internal framework code uses RangoInternal (via toInternal()) to access * matching, build-time, and configuration members that are not part of the * public contract. * * TRoutes accumulates all registered route types through the builder chain. */ export interface Rango< TEnv = any, TRoutes extends Record = Record, > { /** * Unique identifier for this router instance. * Used to namespace static output and isolate route maps between routers. */ readonly id: string; /** * URL prefix applied to all routes. Undefined when no basename is configured. */ readonly basename: string | undefined; /** * Register routes using URL patterns from urls() or a builder function * * @example * ```typescript * // With urls() * createRouter({}).routes(urlpatterns) * * // With builder function (urls() is implicit) * createRouter({}).routes(({ path, layout }) => [ * layout(RootLayout, () => [ * path("/", HomePage), * ]), * ]) * ``` */ routes>( patterns: T & EnvCompatible, ): Rango< TEnv, TRoutes & (NonNullable extends Record ? MergeRoutesWithResponses, T["_responses"]> : Record) >; routes(builder: UrlBuilder): Rango; /** * Add global middleware that runs on all routes * * @example * ```typescript * createRouter({ document: RootLayout }) * .use(loggerMiddleware) // All routes * .use("/api/*", rateLimiter) // Pattern match * .routes(urlpatterns) * ``` */ use( patternOrMiddleware: string | MiddlewareFn, middleware?: MiddlewareFn, ): Rango; /** * Type-safe URL builder for registered routes * Types are inferred from the accumulated route registrations * * @example * ```typescript * router.reverse("cart"); // "/shop/cart" * router.reverse("detail", { slug: "widget" }); // "/shop/product/widget" * ``` */ reverse: ReverseFunction; /** * Accumulated route map for typeof extraction * Used for module augmentation: `type AppRoutes = typeof _router.routeMap` * * @example * ```typescript * const _router = createRouter({ * urls: urlpatterns, * }); * * type AppRoutes = typeof _router.routeMap; * * declare global { * namespace Rango { * interface RegisteredRoutes extends AppRoutes {} * } * } * ``` */ readonly routeMap: TRoutes; /** * Handle an RSC request. * * Uses the router's configuration (nonce, version, cache) automatically. * The handler is lazily created on first call. * * @example Cloudflare Workers * ```tsx * import { router } from "./router"; * * export default { fetch: router.fetch }; * ``` * * @example Direct export * ```tsx * const router = createRouter({ * document: Document, * urls: urlpatterns, * nonce: () => true, * }); * * export const fetch = router.fetch; * ``` */ fetch(request: Request, input?: RouterRequestInput): Promise; } /** * Internal Rango router interface — the full framework-facing API. * * This type includes all members used by the Vite plugin, RSC handler, * pre-rendering pipeline, and other framework internals. It is NOT exported * from the public package API. * * Use toInternal(router) to assert a public Rango into this type * at the boundary where framework code receives a user-provided router. */ export interface RangoInternal< TEnv = any, TRoutes extends Record = Record, > { /** * Brand marker for build-time discovery. * The Vite plugin uses this to identify router instances in module exports. */ readonly __brand: typeof RSC_ROUTER_BRAND; /** * Unique identifier for this router instance. * Used to namespace static output and isolate route maps between routers. */ readonly id: string; /** URL prefix applied to all routes. */ readonly basename: string | undefined; /** * Register routes using URL patterns from urls() or a builder function. * * Env compatibility is checked by EnvCompatible: an env-agnostic urls() block * (its env is `unknown` — e.g. a shared module, or an app that does not augment * `Rango.Env`) attaches to any router, while a urls() block carrying a * concrete env is accepted only when this router's `TEnv` satisfies it. So a * `urls<{ DB }>()` cannot be mounted on a `createRouter<{}>()`. */ routes>( patterns: T & EnvCompatible, ): Rango< TEnv, TRoutes & (NonNullable extends Record ? MergeRoutesWithResponses, T["_responses"]> : Record) >; routes(builder: UrlBuilder): Rango; /** * Add global middleware that runs on all routes */ use( patternOrMiddleware: string | MiddlewareFn, middleware?: MiddlewareFn, ): Rango; /** * Type-safe URL builder for registered routes */ reverse: ReverseFunction; /** * Accumulated route map for typeof extraction */ readonly routeMap: TRoutes; /** * Root layout component that wraps the entire application * Access this to pass to renderSegments */ readonly rootLayout?: ComponentType; /** * Error callback for monitoring/alerting * Called when errors occur in loaders, actions, or routes */ readonly onError?: RangoOptions["onError"]; /** * Cache configuration */ readonly cache?: RangoOptions["cache"]; /** * Not found component to render when no route matches */ readonly notFound?: RangoOptions["notFound"]; /** * Resolved theme configuration (null if theme not enabled) * Used by NavigationProvider to include ThemeProvider and by MetaTags to render theme script */ readonly themeConfig: import("../theme/types.js").ResolvedThemeConfig | null; /** * Cache profiles for "use cache" per-request resolution. * Always includes at least the "default" profile. */ readonly cacheProfiles: Record< string, import("../cache/profile-registry.js").CacheProfile >; /** * Cache-Control header value for prefetch responses. * False means no caching of prefetch responses. * Derived from prefetchCacheTTL. */ readonly prefetchCacheControl: string | false; /** * TTL in milliseconds for the client-side in-memory prefetch cache. * 0 means caching is disabled. */ readonly prefetchCacheTTL: number; /** * Whether connection warmup is enabled. * When true, the client sends HEAD /?_rsc_warmup after idle periods * and the server responds with 204 No Content. */ readonly warmupEnabled: boolean; /** * Whether router-wide performance debugging is enabled. * Used by the request handler to create metrics before middleware runs. */ readonly debugPerformance?: boolean; /** * Whether ?__debug_manifest is allowed in production. * Always enabled in development. */ readonly allowDebugManifest: boolean; /** * Resolved timeout configuration (merged from shorthand + structured). */ readonly timeouts: ResolvedTimeouts; /** * Custom timeout response handler. */ readonly onTimeout?: OnTimeoutCallback; /** * App-level middleware entries * These wrap the entire request/response cycle */ readonly middleware: MiddlewareEntry[]; /** * Nonce provider for CSP */ readonly nonce?: NonceProvider; /** * RSC version string */ readonly version?: string; /** * URL patterns reference for build-time manifest generation */ readonly urlpatterns?: UrlPatterns; /** * SSR configuration. resolveStreaming determines stream vs allReady * per HTML request (undefined = always stream). */ readonly ssr?: import("./router-options.js").SSROptions; /** * Cross-origin request protection configuration. * Default: true (enabled). */ readonly originCheck: import("../rsc/origin-guard.js").OriginCheckConfig; /** * Source file path where createRouter() was called. * Set via Error.stack parsing at construction time. * Used by the Vite plugin to write per-router named-routes.gen.ts files. */ readonly __sourceFile?: string; /** @internal basename for runtime manifest generation */ readonly __basename?: string; match( request: Request, input?: RouterRequestInput, ): Promise; /** * Build-time pre-render match. Resolves segments with a BuildContext * (no request/env/headers/cookies), skipping middleware and loaders. * Used by the Vite plugin to collect pre-render data at build time. */ matchForPrerender( pathname: string, params: Record, buildVars?: Record, isPassthroughRoute?: boolean, buildEnv?: any, devMode?: boolean, ): Promise<{ segments: SerializedSegmentData[]; handles: Record; routeName: string; params: Record; interceptSegments?: SerializedSegmentData[]; interceptHandles?: Record; passthrough?: true; } | null>; /** * Render a single Static handler at build time. * Returns the RSC-serialized component string and handle data, or null on failure. */ renderStaticSegment( handler: Function, handlerId: string, routeName?: string, buildEnv?: any, devMode?: boolean, ): Promise<{ encoded: string; handles: Record } | null>; /** * Preview match - returns route middleware without segment resolution. * Also returns responseType and handler for response routes (non-RSC short-circuit). */ previewMatch( request: Request, input?: RouterRequestInput, ): Promise<{ routeMiddleware?: Array<{ handler: import("./middleware.js").MiddlewareFn; params: Record; }>; responseType?: string; handler?: Function; params?: Record; negotiated?: boolean; manifestEntry?: EntryData; routeKey?: string; } | null>; matchPartial( request: Request, input?: RouterRequestInput, actionContext?: { actionId?: string; actionUrl?: URL; actionResult?: any; formData?: FormData; }, ): Promise; /** * Match an error to the nearest error boundary and return error segments * * Used when an action or other operation fails and we need to render * the error boundary UI. Finds the nearest errorBoundary in the route tree * for the current URL and renders it with the error info. * * @param request - The current request (used to match the route) * @param context - Environment context * @param error - The error that occurred * @param segmentType - Type of segment where error occurred (default: "route") * @returns MatchResult with error segment, or null if no error boundary found */ matchError( request: Request, input: RouterRequestInput | undefined, error: unknown, segmentType?: ErrorInfo["segmentType"], ): Promise; /** * Low-level route matching function. * Used by classifyRequest() for request classification without * entering the full match pipeline. */ findMatch(pathname: string, metricsStore?: any): any; /** * Debug utility to serialize the manifest for inspection * Returns a JSON-friendly representation of all routes and layouts */ debugManifest(): Promise; /** * Handle an RSC request. */ fetch(request: Request, input?: RouterRequestInput): Promise; } /** * Assert a public Rango into the internal type. * * Use this at the boundary where framework code receives a user-provided * router and needs access to internal members (match, config, build-time). * The cast is safe because createRouter() always produces an object that * satisfies RangoInternal; the public type is just a narrower view. */ export function toInternal< TEnv = any, TRoutes extends Record = Record, >(router: Rango): RangoInternal { return router as RangoInternal; }