interface RouterContext { root: Node; static: Record | undefined>; } type ParamsIndexMap = Array<[Index: number, name: string | RegExp, optional: boolean]>; type MethodData = { data: T; paramsMap?: ParamsIndexMap; paramsRegexp: RegExp[]; }; interface Node { key: string; static?: Record>; param?: Node; wildcard?: Node; hasRegexParam?: boolean; methods?: Record[] | undefined>; } type MatchedRoute = { data: T; params?: Record; }; type ExtractWildcards = TPath extends `${string}**:${infer Rest}` ? Rest extends `${infer Param}/${infer Tail}` ? Param | ExtractWildcards : Rest : TPath extends `${string}*${infer Rest}` ? Rest extends `*` ? `_` : `${Count["length"]}` | ExtractWildcards : TPath extends `${string}/${infer Rest}` ? ExtractWildcards : never; type ExtractNamedParams = TPath extends `${infer _Start}:${infer Rest}` ? Rest extends `${infer Param}/${infer Tail}` ? Param | ExtractNamedParams<`/${Tail}`> : Rest extends `${infer Param}*${infer Tail}` ? Param | ExtractNamedParams<`/${Tail}`> : Rest : TPath extends `/${infer Rest}` ? ExtractNamedParams : never; type InferRouteParams = { [K in ExtractNamedParams | ExtractWildcards]: string }; /** * Create a new router context. */ declare function createRouter(): RouterContext; /** * Add a route to the router context. */ declare function addRoute(ctx: RouterContext, method: string | undefined, path: string, data?: T): void; /** * Find a route by path. */ declare function findRoute(ctx: RouterContext, method: string | undefined, path: string, opts?: { params?: boolean; normalize?: boolean; }): MatchedRoute | undefined; /** * Remove a route from the router context. */ declare function removeRoute(ctx: RouterContext, method: string, path: string): void; /** * Find all route patterns that match the given path. */ declare function findAllRoutes(ctx: RouterContext, method: string | undefined, path: string, opts?: { params?: boolean; normalize?: boolean; }): MatchedRoute[]; /** * How the match-sets of two route patterns relate. See {@link compareRoutes}. */ type RouteComparison = "disjoint" | "equal" | "superset" | "subset" | "partial"; /** * Whether two route patterns can match a common concrete path (their match-sets * intersect). Pure and router-free. * * Overlap means "there exists a concrete path matched by both patterns" — it is * *not* subset containment. Patterns are expanded through rou3's own pipeline, * so groups (`{s}?`), optional/repeat modifiers (`:x?`/`:x+`/`:x*`), escaping, * and wildcard segment-count rules match `findRoute`/`findAllRoutes` exactly. * * Segment-count rules: bare `**` matches zero-or-more segments, `**:name` one- * or-more, a trailing bare `*` zero-or-one, and mid-pattern `*` / `:name` * exactly one. * * Regex-constrained segments are handled precisely against static literals * (`/user/:id(\d+)` vs `/user/42`), but two dynamic segments where at least one * is constrained are over-approximated to "overlaps" (the safe conservative * default; exact regex intersection is undecidable). * * @example * routesOverlap("/**", "/protected/feed/**"); // true * routesOverlap("/a/**", "/b/**"); // false * routesOverlap("/a/**", "/a"); // true (`**` matches zero segments) */ declare function routesOverlap(patternA: string, patternB: string): boolean; /** * Compare two route patterns by the sets of concrete paths they match. Pure * and router-free, like {@link routesOverlap}, but answers containment as well * as intersection: * * - `"disjoint"` — no concrete path matches both (proven). * - `"equal"` — both match exactly the same paths (proven; param *names* * don't matter: `/a/:x` equals `/a/:y`, `/u/:id(\d+)` equals `/u/:x(\d+)`). * - `"superset"` — `patternA` provably matches every path `patternB` matches, * and the reverse could not be proven (strict unless equality is * undecidable). * - `"subset"` — the mirror image (`patternA` ⊆ `patternB`). * - `"partial"` — neither containment could be proven and the match-sets * *may* intersect. * * Every verdict's containment claims are proofs; what is *not* guaranteed is * exhaustiveness of the undecidable directions, which always degrade toward a * weaker verdict, never a wrong claim: * * - Two regex-constrained segments are only proven equal by source equality * (modulo param names), and a regex only proven to cover a literal via * `test()` — so `/u/:id(\d+)` vs `/u/:id([0-9]+)` reports `"partial"` even * though the sets are equal. * - Strictness of `"superset"`/`"subset"` is best-effort: when a pair is * actually equal but equality is only provable in one direction, the proven * containment is reported — `/u/:id(42)` vs `/u/42` is `"superset"`, not * `"equal"`. * - `"partial"`'s intersection half is over-approximated (like * {@link routesOverlap}): a `"partial"` pair of disjoint regex constraints, * e.g. `/u/:a(\d+)` vs `/u/:b([a-z]+)`, may in fact share no path. * - Containment of one multi-shape pattern (optional groups/modifiers) in * another is proven shape-by-shape, so a subset split across several of the * other pattern's alternatives may also degrade to `"partial"`. * * Patterns are expanded through rou3's own `addRoute` pipeline (groups, * modifiers, escaping), so the verdict is consistent with * `findRoute`/`findAllRoutes` by construction — e.g. `/a/:x?` is `"equal"` to * `/a/*` (both match `/a` and `/a/seg`). * * @example * compareRoutes("/api/**", "/api/admin/**"); // "superset" * compareRoutes("/a/:x/c", "/a/b/*"); // "partial" (ambiguous specificity) * compareRoutes("/a/**", "/b/**"); // "disjoint" */ declare function compareRoutes(patternA: string, patternB: string): RouteComparison; /** * Find every registered route whose match-set intersects the given pattern * (scope). Like {@link findAllRoutes}, but the query is a *pattern* instead of a * concrete path. * * Results are ordered least- to most-specific (same traversal order as * `findAllRoutes`) and method handling mirrors it (`method`, falling back to the * method-agnostic `""` bucket). Overlap semantics are identical to * {@link routesOverlap}. * * Returned matches carry only `data` — a pattern describes a whole scope rather * than one concrete path, so no `params` can be resolved. A route registered * with optional/group syntax expands into several tree entries that share one * `data` reference; those are collapsed to a single match. Distinct routes are * always reported separately, even when they carry an equal primitive `data` * value (or none — `addRoute` stores `null` when no data is given). */ declare function findOverlappingRoutes(ctx: RouterContext, method: string | undefined, pattern: string): MatchedRoute[]; /** * Convert a rou3 route pattern into an anchored {@link RegExp}. * * The generated source targets a **PCRE-compatible** flavor: named groups use * the `(?...)` form and no JS-only constructs are emitted, so the output * also compiles in PCRE2 engines (`grep -P`, `rg -P`, `pcre2grep`, PHP `preg_*`) * and Perl. Trailing optional groups (`{...}?`, `:name?`) are compiled inline as * `(?:...)?` rather than an alternation, so a param is never emitted as a * duplicate named group — which PCRE2 rejects unless `PCRE2_DUPNAMES` is set. * * Note: multi-group or mid-route optionals that cannot be inlined still fall * back to alternation and may contain duplicate named groups (valid in JS/Perl, * but requiring `PCRE2_DUPNAMES` for strict PCRE2 engines). * * @example * routeToRegExp("/users/:id(\\d+)"); // /^\/users\/(?\d+)\/?$/ * routeToRegExp("/blog/:id(\\d+){-:title}?"); // /^\/blog\/(?\d+)(?:-(?[^/]+))?\/?$/ */ declare function routeToRegExp(route?: string): RegExp; /** * Convert an anchored {@link RegExp} (or its source string) produced by * {@link routeToRegExp} back into a rou3 route pattern. * * @example * regExpToRoute(/^\/users\/(?<id>\d+)\/?$/); // "/users/:id(\\d+)" * regExpToRoute(/^\/path\/(?<param>[^/]+)\/?$/); // "/path/:param" * regExpToRoute(/^\/base\/?(?<path>.+)\/?$/); // "/base/**:path" */ declare function regExpToRoute(regexp: RegExp | string): string; declare const NullProtoObj: { new (): any; }; export { type InferRouteParams, type MatchedRoute, NullProtoObj, type RouteComparison, type RouterContext, addRoute, compareRoutes, createRouter, findAllRoutes, findOverlappingRoutes, findRoute, regExpToRoute, removeRoute, routeToRegExp, routesOverlap };