@nextrush/router 4.0.0-beta.0 → 4.0.0-beta.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (38) hide show
  1. package/dist/index.d.ts +93 -1
  2. package/dist/index.js +294 -27
  3. package/dist/index.js.map +1 -1
  4. package/package.json +4 -4
  5. package/src/__tests__/allowed-methods.test.ts +2 -2
  6. package/src/__tests__/audit-fixes.test.ts +0 -12
  7. package/src/__tests__/canonical-path-security.test.ts +198 -0
  8. package/src/__tests__/canonicalize-memo.test.ts +108 -0
  9. package/src/__tests__/dispatch-deasync.test.ts +0 -20
  10. package/src/__tests__/find-node-differential.test.ts +6 -6
  11. package/src/__tests__/head-auto-registration.test.ts +197 -0
  12. package/src/__tests__/helpers/differential-corpus.ts +1 -3
  13. package/src/__tests__/match-node-indexed-unpooled.test.ts +83 -0
  14. package/src/__tests__/match-normalize-fastpath.test.ts +1 -3
  15. package/src/__tests__/match-prenormalized.test.ts +95 -0
  16. package/src/__tests__/match-safety.test.ts +53 -7
  17. package/src/__tests__/match-single-alloc.test.ts +1 -3
  18. package/src/__tests__/match-walk-pool-safety.test.ts +103 -0
  19. package/src/__tests__/public-surface.test.ts +14 -1
  20. package/src/__tests__/registration-max-depth.test.ts +56 -0
  21. package/src/__tests__/router-audit.test.ts +0 -11
  22. package/src/__tests__/router-edge-cases.test.ts +0 -5
  23. package/src/__tests__/router.test.ts +0 -5
  24. package/src/__tests__/static-map-reset.test.ts +1 -3
  25. package/src/__tests__/walk-pool-sizing.test.ts +72 -0
  26. package/src/__tests__/walk-pool-undersized-guard.test.ts +59 -0
  27. package/src/canonicalize.ts +137 -0
  28. package/src/composition.ts +4 -0
  29. package/src/constants.ts +24 -6
  30. package/src/dispatch.ts +34 -6
  31. package/src/index.ts +6 -0
  32. package/src/match-route.ts +82 -19
  33. package/src/matching.ts +20 -14
  34. package/src/registration.ts +62 -10
  35. package/src/router.ts +79 -1
  36. package/src/segment-trie.ts +8 -0
  37. package/src/state.ts +1 -0
  38. package/src/walk-pool.ts +208 -0
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"sources":["../src/router.ts","../src/segment-trie.ts","../src/group-router.ts","../src/redirect.ts","../src/constants.ts","../src/matching.ts","../src/match-route.ts","../src/composition.ts","../src/middleware-adapter.ts","../src/registration.ts","../src/route-metadata.ts","../src/find-node.ts","../src/dispatch.ts","../src/state.ts"],"sourcesContent":["/**\n * @nextrush/router - Router Implementation\n *\n * The public `Router` shell: a thin, chainable facade delegating registration,\n * matching, dispatch, composition, and grouping to focused sibling modules\n * (design.md D1/D2). Segment trie keyed by whole path segments, not a radix tree.\n *\n * @packageDocumentation\n */\n\nimport {\n HTTP_METHODS,\n type HttpMethod,\n type Middleware,\n type RouteDefinition,\n type RouteEntry,\n type RouteHandler,\n type RouteMatch,\n type RouterOptions,\n} from '@nextrush/types';\nimport { clearNode, createNode, type StaticRouteMap, type TrieNode } from './segment-trie';\nimport { type RedirectStatus } from './redirect';\nimport { runRouteGroup, type RouteGroup } from './group-router';\nimport { resolveMatch, type MatchState } from './match-route';\nimport { copyRoutes } from './composition';\nimport { sealRouterMiddleware as sealRouterMiddlewareImpl } from './middleware-adapter';\nimport {\n addRoute as addRouteImpl,\n normalizeRegistrationPath,\n pushAnyMethodDefinition,\n registerRedirect,\n type RegistrationState,\n} from './registration';\nimport { createAllowedMethodsMiddleware, createRoutesMiddleware } from './dispatch';\nimport { createRouterState, resolveRouterOptions } from './state';\n\n/** Inline route metadata declaration — re-exported from its own module (RT-3). */\nexport { endpoint } from './route-metadata';\n\n/**\n * High-performance segment-trie router: O(d) lookup by path segment, with a\n * static-route hash map for an O(1) fast path.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md | @nextrush/router README}\n */\nexport class Router {\n private readonly root: TrieNode;\n private readonly opts: Required<RouterOptions>;\n private readonly routerMiddleware: Middleware[] = [];\n\n /** Static-route fast path: method-nested map for O(1) lookup with no per-request key string (HP-9). */\n private readonly staticRoutes: StaticRouteMap = new Map();\n\n /**\n * Introspection registry, kept SEPARATE from the hot-path trie/staticRoutes\n * so request dispatch never reads metadata — only getRoutes() touches it.\n */\n private readonly routeDefinitions: RouteDefinition[] = [];\n\n /** Whether any routes have params or wildcards (disables static-only fast path) */\n private hasParamRoutes = false;\n\n /** Whether router-level middleware has already been sealed into executors (audit RT-7) */\n private _sealed = false;\n\n /** Memoized state the extracted registration/matching functions read (see {@link createRouterState}). */\n private readonly state: RegistrationState & MatchState;\n\n constructor(options: RouterOptions = {}) {\n this.root = createNode('');\n this.opts = resolveRouterOptions(options);\n this.state = createRouterState(\n this.root,\n this.opts,\n this.staticRoutes,\n this.routeDefinitions,\n this.routerMiddleware\n );\n }\n\n /**\n * Validate + normalize a raw path, then delegate trie insertion to the\n * extracted `addRoute` (design.md D2); flips `hasParamRoutes` from its return.\n */\n private addRoute(\n method: HttpMethod,\n path: string,\n entries: RouteEntry[],\n middleware: Middleware[] = [],\n recordIntrospection = true\n ): void {\n // Guard untyped-JS callers: a non-string path would coerce to a bogus literal route.\n const rawPath: unknown = path;\n if (typeof rawPath !== 'string') {\n throw new TypeError(\n `Route path must be a string, received ${rawPath === null ? 'null' : typeof rawPath}.`\n );\n }\n const normalized = normalizeRegistrationPath(path, this.opts.prefix, this.opts.strict);\n if (addRouteImpl(method, normalized, entries, middleware, this.state, recordIntrospection)) {\n this.hasParamRoutes = true;\n }\n }\n\n get(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('GET', path, entries);\n return this;\n }\n\n post(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('POST', path, entries);\n return this;\n }\n\n put(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('PUT', path, entries);\n return this;\n }\n\n delete(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('DELETE', path, entries);\n return this;\n }\n\n patch(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('PATCH', path, entries);\n return this;\n }\n\n head(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('HEAD', path, entries);\n return this;\n }\n\n options(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('OPTIONS', path, entries);\n return this;\n }\n\n /**\n * Register a route for every HTTP method under one consolidated `isAnyMethod`\n * introspection row (T016) — matching is unchanged (see {@link pushAnyMethodDefinition}).\n */\n all(path: string, ...entries: RouteEntry[]): this {\n // recordIntrospection=false: insert each per-method handler without its own\n // introspection row; the single consolidated row below replaces all 7.\n for (const method of HTTP_METHODS) {\n this.addRoute(method, path, entries, [], false);\n }\n pushAnyMethodDefinition(\n this.routeDefinitions,\n normalizeRegistrationPath(path, this.opts.prefix, this.opts.strict)\n );\n return this;\n }\n\n route(method: HttpMethod, path: string, ...entries: RouteEntry[]): this {\n this.addRoute(method, path, entries);\n return this;\n }\n\n /**\n * Every registered route as a read-only list, for renderers (`@nextrush/openapi`,\n * SDK/RPC generators). Doc-generation-time only — never on the request path.\n */\n getRoutes(): readonly RouteDefinition[] {\n return this.routeDefinitions;\n }\n\n /**\n * Register a redirect from one path to another (301 by default). 307/308\n * additionally register POST/PUT/PATCH/DELETE to preserve the method.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#redirects | README: Redirects}\n */\n redirect(from: string, to: string, status: RedirectStatus = 301): this {\n registerRedirect(from, to, status, (method, path, entries) => {\n this.addRoute(method, path, entries);\n });\n return this;\n }\n\n use(pathOrMiddleware: string | Middleware | Router, routerOrUndefined?: Router): this {\n if (typeof pathOrMiddleware === 'function') {\n this.routerMiddleware.push(pathOrMiddleware);\n } else if (typeof pathOrMiddleware === 'string' && routerOrUndefined instanceof Router) {\n this.mountRouter(pathOrMiddleware, routerOrUndefined);\n } else if (typeof pathOrMiddleware === 'string') {\n throw new Error(\n `router.use('${pathOrMiddleware}', ...) requires a Router instance as the second argument. ` +\n 'Use router.group(prefix, callback) for prefix-scoped middleware, ' +\n 'or router.use(middlewareFn) to register middleware without a prefix.'\n );\n } else if (pathOrMiddleware instanceof Router) {\n this.mountRouter('', pathOrMiddleware);\n }\n return this;\n }\n\n /**\n * Mount a sub-router at a path prefix (Hono-style) — the explicit, more\n * semantic equivalent of `router.use(path, subRouter)`.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#sub-router-mounting | README: Sub-Router Mounting}\n */\n mount(path: string, router: Router): this {\n this.mountRouter(path, router);\n return this;\n }\n\n /** Mount a sub-router, carrying its own `routerMiddleware` onto every copied route. */\n private mountRouter(prefix: string, router: Router): void {\n copyRoutes(router.root, prefix, [], router.routerMiddleware, this.addRoute.bind(this));\n }\n\n /** Match a request to a route — delegates to {@link resolveMatch} (design.md D1). */\n match(method: HttpMethod, path: string): RouteMatch | null {\n return resolveMatch(this.state, this.hasParamRoutes, method, path);\n }\n\n /**\n * Return the router's dispatch middleware — mount this on the application.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#routerroutes | README: router.routes()}\n */\n routes(): Middleware {\n // Seal router middleware into every executor once (audit RT-7 idempotency):\n // routes() may run more than once, so the _sealed guard prevents re-prepend.\n if (this.routerMiddleware.length > 0 && !this._sealed) {\n this._sealed = true;\n sealRouterMiddlewareImpl(this.root, this.staticRoutes, this.routerMiddleware);\n }\n return createRoutesMiddleware((method, path) => this.match(method, path));\n }\n\n /**\n * Generate allowed-methods middleware. Responds to OPTIONS with an `Allow`\n * header and returns 405 for a known path hit with an unregistered method.\n */\n allowedMethods(): Middleware {\n return createAllowedMethodsMiddleware(this.root, this.opts.caseSensitive, this.opts.strict);\n }\n\n /**\n * Create a route group with a shared prefix and middleware. The callback\n * receives a {@link RouteGroup} to register routes against.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#route-groups | README: Route Groups}\n */\n group(\n prefix: string,\n middlewareOrCallback: Middleware[] | ((router: RouteGroup) => void),\n callback?: (router: RouteGroup) => void\n ): this {\n runRouteGroup(this, prefix, middlewareOrCallback, callback);\n return this;\n }\n\n /**\n * Remove all routes and middleware, resetting the router to its initial\n * state — for test isolation or hot-reload re-registration.\n */\n reset(): void {\n clearNode(this.root);\n this.staticRoutes.clear();\n this.routerMiddleware.length = 0;\n this.hasParamRoutes = false;\n // Clear the introspection registry too, or getRoutes()/OpenAPI would emit\n // ghost routes after a reset (audit RT-1).\n this.routeDefinitions.length = 0;\n this._sealed = false;\n }\n\n /** Register a route on behalf of a {@link RouteGroup} (group context). @internal */\n _addGroupRoute(\n method: HttpMethod,\n path: string,\n handlers: RouteHandler[],\n groupMiddleware: Middleware[],\n recordIntrospection = true\n ): void {\n this.addRoute(method, path, handlers, groupMiddleware, recordIntrospection);\n }\n\n /**\n * Group-facing entry point to {@link pushAnyMethodDefinition} — a group's `.all()`\n * records its consolidated row here since group routes live on the parent. @internal\n */\n _pushAnyMethodRouteDefinition(path: string): void {\n pushAnyMethodDefinition(\n this.routeDefinitions,\n normalizeRegistrationPath(path, this.opts.prefix, this.opts.strict)\n );\n }\n}\n\n/**\n * Create a new {@link Router} instance.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#quick-start | README: Quick Start}\n */\nexport function createRouter(options?: RouterOptions): Router {\n return new Router(options);\n}\n","/**\n * @nextrush/router - Segment Trie Node\n *\n * Internal segment trie implementation for high-performance route matching.\n * Segments are split by `/` and matched one trie level per segment for O(k)\n * lookups where k is path length.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { Context, HttpMethod, Middleware, RouteHandler } from '@nextrush/types';\n\n/**\n * Node type enumeration\n */\nexport const enum NodeType {\n /** Static path segment: /users */\n STATIC = 0,\n /** Named parameter: /:id */\n PARAM = 1,\n /** Wildcard: /* */\n WILDCARD = 2,\n}\n\n/**\n * Segment trie node\n */\nexport interface TrieNode {\n /** Path segment for this node */\n segment: string;\n /** Node type */\n type: NodeType;\n /** Static child nodes keyed by whole path segment (e.g. `users`), not the first character */\n children: Map<string, TrieNode>;\n /** Parameter name if this is a param node */\n paramName?: string;\n /** Handlers keyed by HTTP method */\n handlers: Map<HttpMethod, HandlerEntry>;\n /** Wildcard child if any */\n wildcardChild?: TrieNode;\n /** Parameter child if any */\n paramChild?: TrieNode;\n}\n\n/**\n * Handler entry with middleware and pre-compiled executor\n */\nexport interface HandlerEntry {\n handler: RouteHandler;\n middleware: Middleware[];\n /** Pre-compiled executor for fast dispatch (no closure per request) */\n executor?: (ctx: Context) => Promise<void>;\n}\n\n/**\n * Method-nested static-route fast-path map (HP-9): the OUTER map selects an\n * inner map by HTTP method, the INNER map probes by the trailing-slash-\n * normalized path. This replaces the former `Map<\"METHOD path\", HandlerEntry>`\n * so a static lookup no longer builds a `` `${method} ${path}` `` key string per\n * request — it selects the inner map by `method` and probes by the raw path.\n */\nexport type StaticRouteMap = Map<HttpMethod, Map<string, HandlerEntry>>;\n\n/**\n * No-op next function - reusable, zero allocation\n * Caches the resolved Promise to avoid per-call allocation\n * @internal\n */\nconst RESOLVED_PROMISE = Promise.resolve();\nexport const NOOP_NEXT = (): Promise<void> => RESOLVED_PROMISE;\n\n/**\n * Compile an executor for a route handler with middleware\n * This creates the executor ONCE at registration time, not per-request\n * @internal\n */\nexport function compileExecutor(\n handler: RouteHandler,\n middleware: Middleware[]\n): (ctx: Context) => Promise<void> {\n const len = middleware.length;\n\n // FAST PATH: No middleware — direct handler call, no extra async frame (NF-1).\n if (len === 0) {\n return (ctx: Context): Promise<void> => {\n // `setNext(NOOP_NEXT)` is LOAD-BEARING, not redundant (NF-4a): it\n // terminates the middleware chain at the handler so a handler that calls\n // `ctx.next()` is a safe no-op and cannot leak into app-level middleware\n // mounted AFTER the router (the general `compose` dispatch wires ctx._next\n // to advance into that middleware before running the router).\n if (ctx.setNext) ctx.setNext(NOOP_NEXT);\n try {\n // `Promise.resolve(...)` — NOT `x instanceof Promise ? x : RESOLVED` — so\n // a non-Promise THENABLE return is adopted (its async work awaited), not\n // dropped: byte-identical to the former `await handler(...)`, minus the\n // async form's internal microtask hop. A native promise is returned as-is\n // (`Promise.resolve(p) === p`); a void return yields a resolved promise.\n return Promise.resolve(handler(ctx, NOOP_NEXT));\n } catch (err) {\n // Self-contained \"never throw synchronously\" contract, identical to the\n // len >= 1 branch: a sync throw becomes a rejection; a non-Error is wrapped.\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n };\n }\n\n // Middleware present: guarded recursive dispatch. This mirrors core `compose()`\n // so per-route middleware behave identically to application middleware:\n // - `ctx.next()` (modern) and the `(ctx, next)` argument advance the SAME chain\n // (ctx.setNext is wired before each layer),\n // - a synchronous throw is turned into a rejected promise (proper propagation),\n // - calling next() more than once in a layer rejects with a clear error.\n return (ctx: Context): Promise<void> => {\n let index = -1;\n\n const dispatch = (i: number): Promise<void> => {\n if (i <= index) {\n return Promise.reject(new Error('next() called multiple times'));\n }\n index = i;\n\n if (i < len) {\n const mw = middleware[i];\n if (mw === undefined) return Promise.reject(new Error('middleware length mismatch'));\n const next = (): Promise<void> => dispatch(i + 1);\n if (ctx.setNext) ctx.setNext(next);\n try {\n return Promise.resolve(mw(ctx, next));\n } catch (err) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n // Final handler. A handler calling next() is a safe no-op.\n if (ctx.setNext) ctx.setNext(NOOP_NEXT);\n try {\n return Promise.resolve(handler(ctx, NOOP_NEXT));\n } catch (err) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n };\n\n return dispatch(0);\n };\n}\n\n/**\n * Create a new segment trie node\n */\nexport function createNode(segment: string, type: NodeType = NodeType.STATIC): TrieNode {\n return {\n segment,\n type,\n children: new Map(),\n handlers: new Map(),\n };\n}\n\n/**\n * Reset a trie node to its empty state — clears children/handlers and drops the\n * param/wildcard branches. Used by `Router.reset()` for test isolation and\n * hot-reload re-registration.\n */\nexport function clearNode(node: TrieNode): void {\n node.children.clear();\n node.handlers.clear();\n node.paramChild = undefined;\n node.wildcardChild = undefined;\n}\n\n/**\n * Parse path segments\n * Splits path into segments and identifies param/wildcard types\n *\n * @param path - Route path to parse\n * @param caseSensitive - If false, lowercase static segments for case-insensitive matching\n */\nexport function parseSegments(path: string, caseSensitive = true): ParsedSegment[] {\n const normalized = path.startsWith('/') ? path.slice(1) : path;\n if (normalized === '') return [];\n\n const parts = normalized.split('/');\n const segments: ParsedSegment[] = [];\n\n for (const part of parts) {\n if (part.startsWith(':')) {\n // Preserve the original parameter name case\n const paramName = part.slice(1);\n segments.push({\n segment: part, // Preserve original case — param nodes match any segment\n type: NodeType.PARAM,\n paramName, // Keep original case\n });\n } else if (part === '*') {\n segments.push({\n segment: '*',\n type: NodeType.WILDCARD,\n });\n break; // Wildcard must be last\n } else {\n segments.push({\n segment: caseSensitive ? part : part.toLowerCase(),\n type: NodeType.STATIC,\n });\n }\n }\n\n return segments;\n}\n\n/**\n * Parsed segment structure\n */\nexport interface ParsedSegment {\n segment: string;\n type: NodeType;\n paramName?: string;\n}\n","/**\n * @nextrush/router - Route Groups\n *\n * A route group applies a shared path prefix and middleware to a set of routes\n * registered inside a callback. Extracted from `router.ts` (audit RT-3) and\n * given a real public type (audit RT-6): `router.group()` callbacks receive a\n * {@link RouteGroup}, not a mis-cast `Router`.\n *\n * @packageDocumentation\n */\n\nimport { HTTP_METHODS, type HttpMethod, type Middleware, type RouteHandler } from '@nextrush/types';\nimport { createRedirectHandler, type RedirectStatus } from './redirect';\n\n/**\n * The subset of the parent router a group needs to register routes into.\n *\n * @remarks\n * Declared as an interface (rather than importing `Router`) so `group-router.ts`\n * and `router.ts` don't form an import cycle. `Router` satisfies it structurally\n * via its `_addGroupRoute` method.\n */\nexport interface GroupRouterHost {\n _addGroupRoute(\n method: HttpMethod,\n path: string,\n handlers: RouteHandler[],\n groupMiddleware: Middleware[],\n recordIntrospection?: boolean\n ): void;\n /** Record a single any-method introspection row (T016) — see `Router._pushAnyMethodRouteDefinition`. */\n _pushAnyMethodRouteDefinition(path: string): void;\n}\n\n/**\n * The object passed to a `router.group(prefix, callback)` callback.\n *\n * @remarks\n * Exposes only the route-registration surface that is valid inside a group —\n * intentionally NOT the full {@link Router} (no `mount`/`use`/`reset`), which is\n * why the previous `as unknown as Router` cast was a lie (audit RT-6).\n */\nexport interface RouteGroup {\n get(path: string, ...handlers: RouteHandler[]): this;\n post(path: string, ...handlers: RouteHandler[]): this;\n put(path: string, ...handlers: RouteHandler[]): this;\n delete(path: string, ...handlers: RouteHandler[]): this;\n patch(path: string, ...handlers: RouteHandler[]): this;\n head(path: string, ...handlers: RouteHandler[]): this;\n options(path: string, ...handlers: RouteHandler[]): this;\n all(path: string, ...handlers: RouteHandler[]): this;\n redirect(from: string, to: string, status?: RedirectStatus): this;\n group(\n prefix: string,\n middlewareOrCallback: Middleware[] | ((router: RouteGroup) => void),\n callback?: (router: RouteGroup) => void\n ): this;\n}\n\n/**\n * Collects routes under a shared prefix + middleware and forwards them to the\n * parent router. Implements {@link RouteGroup}.\n */\nexport class GroupRouter implements RouteGroup {\n private readonly parent: GroupRouterHost;\n private readonly prefix: string;\n private readonly middleware: Middleware[];\n\n constructor(parent: GroupRouterHost, prefix: string, middleware: Middleware[]) {\n this.parent = parent;\n this.prefix = prefix;\n this.middleware = middleware;\n }\n\n private fullPath(path: string): string {\n // Handle root path in group\n if (path === '/' || path === '') {\n return this.prefix;\n }\n // Combine prefix and path\n const cleanPrefix = this.prefix.endsWith('/') ? this.prefix.slice(0, -1) : this.prefix;\n const cleanPath = path.startsWith('/') ? path : '/' + path;\n return cleanPrefix + cleanPath;\n }\n\n get(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('GET', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n post(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('POST', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n put(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('PUT', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n delete(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('DELETE', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n patch(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('PATCH', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n head(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('HEAD', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n options(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('OPTIONS', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n /**\n * Register a route matching every HTTP method under a single introspection\n * entry, mirroring `Router.all()` (T016) — a group's `.all()` must not\n * regress to the pre-T016 one-row-per-method shape just because\n * registration is routed through `_addGroupRoute` instead of `addRoute`\n * directly.\n */\n all(path: string, ...handlers: RouteHandler[]): this {\n for (const method of HTTP_METHODS) {\n this.parent._addGroupRoute(method, this.fullPath(path), handlers, this.middleware, false);\n }\n this.parent._pushAnyMethodRouteDefinition(this.fullPath(path));\n return this;\n }\n\n /**\n * Register a redirect within the group (uses the shared redirect handler,\n * audit RT-4 — no more naive replaceAll param substitution).\n */\n redirect(from: string, to: string, status: RedirectStatus = 301): this {\n const redirectHandler = createRedirectHandler(to, status);\n\n this.parent._addGroupRoute('GET', this.fullPath(from), [redirectHandler], this.middleware);\n this.parent._addGroupRoute('HEAD', this.fullPath(from), [redirectHandler], this.middleware);\n\n return this;\n }\n\n /**\n * Nested group support — combines this group's prefix + middleware with the\n * nested group's.\n */\n group(\n prefix: string,\n middlewareOrCallback: Middleware[] | ((router: RouteGroup) => void),\n callback?: (router: RouteGroup) => void\n ): this {\n runRouteGroup(\n this.parent,\n this.fullPath(prefix),\n middlewareOrCallback,\n callback,\n this.middleware\n );\n return this;\n }\n}\n\n/**\n * Resolve a `group()` call's `(middleware[], callback)` | `(callback)` overload\n * and run the callback against a fresh {@link GroupRouter} bound to `host`.\n *\n * @remarks\n * Shared by `Router.group()` and the nested `GroupRouter.group()` so the\n * overload handling and the \"throw when a middleware array has no callback\"\n * guard have a single definition.\n *\n * @param host - Parent router the group registers routes into.\n * @param prefix - Fully-resolved path prefix for the group.\n * @param middlewareOrCallback - Either the group middleware array or the callback.\n * @param callback - The callback, required when a middleware array is passed.\n * @param inheritedMiddleware - Middleware from an enclosing group, prepended\n * ahead of this group's own (empty for a top-level `Router.group()`).\n */\nexport function runRouteGroup(\n host: GroupRouterHost,\n prefix: string,\n middlewareOrCallback: Middleware[] | ((router: RouteGroup) => void),\n callback: ((router: RouteGroup) => void) | undefined,\n inheritedMiddleware: Middleware[] = []\n): void {\n let middleware: Middleware[] = [];\n let cb: (router: RouteGroup) => void;\n\n if (Array.isArray(middlewareOrCallback)) {\n middleware = middlewareOrCallback;\n if (!callback) {\n throw new Error('Callback function is required when providing middleware array');\n }\n cb = callback;\n } else {\n cb = middlewareOrCallback;\n }\n\n const combined =\n inheritedMiddleware.length > 0 ? [...inheritedMiddleware, ...middleware] : middleware;\n cb(new GroupRouter(host, prefix, combined));\n}\n","/**\n * @nextrush/router - Redirect Handler Compilation\n *\n * Single, shared redirect implementation used by both `Router.redirect()` and\n * route groups (audit RT-4). Previously the group variant used a naive\n * `replaceAll(':key', value)` that could mis-substitute overlapping param names\n * and re-scanned the string per request; this precompiles the target template\n * once and only substitutes route-style `:param` slots (a `:` at position 0 or\n * preceded by `/`), so `https://` and other literal colons are never touched.\n *\n * @packageDocumentation\n */\n\nimport type { Context, RouteHandler } from '@nextrush/types';\n\n/** HTTP redirect status codes supported by `redirect()`. */\nexport type RedirectStatus = 301 | 302 | 303 | 307 | 308;\n\n/**\n * Precompile a redirect target into alternating literal / param-name parts.\n *\n * @param to - Target path/URL, possibly containing `:param` placeholders.\n * @returns A parts array (`[literal, paramName, literal, …]`) when `to` has\n * route-style params, otherwise `undefined` (a static target).\n */\nexport function compileRedirectTarget(to: string): string[] | undefined {\n const parts: string[] = [];\n let pos = 0;\n let found = false;\n\n while (pos < to.length) {\n let idx = -1;\n for (let i = pos; i < to.length; i++) {\n if (to[i] === ':' && (i === 0 || to[i - 1] === '/') && i + 1 < to.length && to[i + 1] !== '/') {\n idx = i;\n break;\n }\n }\n if (idx === -1) break;\n\n found = true;\n parts.push(to.slice(pos, idx)); // literal before ':'\n const end = to.indexOf('/', idx + 1);\n if (end === -1) {\n parts.push(to.slice(idx + 1)); // param name (rest of string)\n pos = to.length;\n } else {\n parts.push(to.slice(idx + 1, end)); // param name\n pos = end;\n }\n }\n\n if (found) {\n parts.push(to.slice(pos)); // trailing literal\n return parts;\n }\n return undefined;\n}\n\n/**\n * Build the redirect route handler for a `to`/`status` pair. Substitutes any\n * `:param` placeholders from `ctx.params` using the precompiled template.\n *\n * @param to - Target path/URL.\n * @param status - Redirect status code.\n * @returns A {@link RouteHandler} that sets `Location` and the status.\n */\nexport function createRedirectHandler(to: string, status: RedirectStatus): RouteHandler {\n const compiledParts = compileRedirectTarget(to);\n\n return (ctx: Context) => {\n let targetPath: string;\n\n if (compiledParts) {\n const params = ctx.params;\n const head = compiledParts[0];\n if (head === undefined) {\n targetPath = to;\n } else {\n let result = head;\n for (let i = 1; i < compiledParts.length - 1; i += 2) {\n const paramKey = compiledParts[i];\n const tail = compiledParts[i + 1];\n if (paramKey === undefined || tail === undefined) break;\n result += (params[paramKey] ?? '') + tail;\n }\n targetPath = result;\n }\n } else {\n targetPath = to;\n }\n\n ctx.status = status;\n ctx.set('Location', targetPath);\n ctx.body = '';\n };\n}\n","/**\n * @nextrush/router - Shared internal constants\n *\n * A leaf module that imports nothing, so any router module can depend on it\n * without risking an import cycle. This is the resolution of the former\n * `EMPTY_PARAMS` duplication: the constant was previously copied across modules\n * to dodge a `router.ts` <-> `match-route.ts` cycle, which a dependency-free\n * leaf module makes impossible by construction.\n *\n * @packageDocumentation\n * @internal\n */\n\n/**\n * Frozen, null-prototype empty params object for matches with no path\n * parameters (static-map hits and successful walks that bound no `:param`/`*`).\n *\n * Shared as a single instance to avoid allocating a fresh object per request on\n * the hot path. Null-prototype so callers can never read an inherited\n * `Object.prototype` key as if it were a route param; frozen so the shared\n * instance can never be mutated by a handler.\n */\nexport const EMPTY_PARAMS: Record<string, string> = Object.freeze(\n Object.create(null) as Record<string, string>\n);\n","/**\n * @nextrush/router - Route Matching Engine\n *\n * The segment-trie lookup logic extracted from the `Router` class (T014,\n * design.md D1). These are standalone pure functions taking the trie root and\n * other needed state as explicit parameters — not methods — so the matching\n * engine has no implicit dependency on broader `Router` instance state beyond\n * what's passed in. `Router` becomes a thin delegating shell around these.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { HttpMethod } from '@nextrush/types';\nimport type { HandlerEntry, TrieNode } from './segment-trie';\n\n/**\n * Percent-decode an extracted param/wildcard value when `decode` is enabled.\n * Fast-paths values with no `%`, and falls back to the raw value on malformed\n * encoding (decodeURIComponent throws a URIError) so a bad request never crashes\n * routing.\n */\nexport function decodeParam(value: string, decode: boolean): string {\n if (!decode || !value.includes('%')) return value;\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\n/**\n * Return the path segment starting at `start`, up to the next `/` (or end).\n * Scalar — no tuple array (the iterative walk tracks the next position itself).\n * Used to recover an original-case param value from the original-case path at a\n * position already validated against the (folded) lookup path, and by the\n * {@link findNode} allowed-methods walk to scan segments off the lookup path.\n */\nexport function segmentAt(path: string, start: number): string {\n const slashPos = path.indexOf('/', start);\n return slashPos === -1 ? path.slice(start) : path.slice(start, slashPos);\n}\n\n/**\n * True only when `path.toLowerCase()` is provably a no-op: every character is\n * ASCII (`<= 0x7F`) and none is an ASCII uppercase letter (`A`–`Z`). Any\n * non-ASCII byte is treated as UNCERTAIN (it could be uppercase like `Ü`), so\n * this returns `false` and the caller folds — never skipping unicode case\n * folding (design.md D3). This lets `normalizePathForMatch` skip the\n * `toLowerCase()` allocation for the overwhelmingly common all-lowercase-ASCII\n * request path while staying byte-identical to always folding.\n */\nexport function isProvablyLowerAscii(path: string): boolean {\n for (let i = 0; i < path.length; i++) {\n const c = path.charCodeAt(i);\n if ((c >= 0x41 && c <= 0x5a) || c > 0x7f) return false;\n }\n return true;\n}\n\n/**\n * Structural match-time normalization WITHOUT case folding: collapse repeated\n * slashes and strip a single trailing slash (unless `strict`). Split out of\n * {@link normalizePathForMatch} so the case-fold decision and the structural\n * pass are separable — `matchRoute` reuses this to build the original-case path\n * only when a fold actually happened (HP-12), rather than running the full\n * normalize twice.\n */\nexport function collapseAndStrip(path: string, strict: boolean): string {\n let normalized = path;\n\n // Fast-path: skip the regex when there are no double slashes (99%+ of requests).\n if (normalized.includes('//')) {\n normalized = normalized.replace(/\\/+/g, '/');\n }\n\n // Non-strict mode treats a trailing slash as insignificant; strict keeps it.\n if (!strict && normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n\n return normalized;\n}\n\n/**\n * Normalize a request path for segment-trie matching: fold case (unless\n * `caseSensitive`), collapse repeated slashes, and strip a single trailing\n * slash (unless `strict`). This is the one definition of the match-time\n * normalization rules, shared by `matchRoute` (in `match-route.ts`) and\n * {@link findAllowedMethods}.\n *\n * The `toLowerCase()` call is skipped when the path is provably case-stable\n * ({@link isProvablyLowerAscii}) — byte-identical to always folding, but with\n * no throwaway string on the common all-lowercase-ASCII path (HP-12 / D3).\n *\n * Query-string removal is intentionally NOT done here — it is caller-specific:\n * `matchRoute` strips the query before calling, while `findAllowedMethods`\n * receives an already query-free `ctx.path`. Pass `caseSensitive: true` to\n * normalize while preserving case (used by `matchRoute` to build the\n * original-case path from which param values are extracted).\n *\n * NOTE: registration-time normalization (`Router.normalizePath`) is a separate\n * concern — it joins the router prefix, guarantees a leading slash, and never\n * folds case — so it deliberately does not share this helper.\n */\nexport function normalizePathForMatch(\n path: string,\n caseSensitive: boolean,\n strict: boolean\n): string {\n const folded = caseSensitive || isProvablyLowerAscii(path) ? path : path.toLowerCase();\n return collapseAndStrip(folded, strict);\n}\n\n/**\n * One node in the iterative walk's explicit stack. `stage` is a small state\n * machine (0 = extract + try static, 1 = try param, 2 = try wildcard/backtrack)\n * so a single frame can be revisited on backtrack without recursion. `bound`\n * records whether this frame pushed a deferred param binding, so backtracking\n * can pop it without an object-property delete.\n */\ninterface WalkFrame {\n node: TrieNode;\n pos: number;\n stage: 0 | 1 | 2;\n seg: string;\n next: number;\n bound: boolean;\n}\n\n/**\n * Iterative, index-based segment-trie match (HP-11 / HP-13, design.md D4).\n *\n * Walks the trie with an EXPLICIT stack instead of recursion, so a pathological\n * segment count cannot overflow the call stack (DoS safety) — behavior is\n * otherwise byte-identical to the former recursive matcher: precedence is\n * static > param > wildcard at each node, a partially-matching branch backtracks\n * cleanly, and the first accepted terminal wins.\n *\n * Param/wildcard bindings are DEFERRED onto the caller-owned `bindNames` /\n * `bindValues` stacks and popped on backtrack, so params are materialized ONCE\n * on the accepted terminal by the caller (no eager bind + backtrack\n * `Reflect.deleteProperty`, and no `Object.keys` post-loop — the caller reads\n * the bind count). `decode` runs strictly on the already-split segment/remainder\n * (design.md D9), so an encoded slash/dot decodes into the value only and can\n * never create new path segments.\n *\n * `originalPath` (present only when case-folding actually occurred) supplies the\n * original-case param value at the same position as the folded lookup path.\n */\nexport function matchNodeIndexed(\n root: TrieNode,\n path: string,\n startPos: number,\n bindNames: string[],\n bindValues: string[],\n method: HttpMethod,\n decode: boolean,\n originalPath?: string\n): HandlerEntry | null {\n const stack: WalkFrame[] = [\n { node: root, pos: startPos, stage: 0, seg: '', next: 0, bound: false },\n ];\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1];\n if (frame === undefined) break;\n\n // Stage 0 — first visit: terminal checks, then try the static child.\n if (frame.stage === 0) {\n if (frame.pos >= path.length) {\n const handler = frame.node.handlers.get(method);\n if (handler) return handler; // accepted — bind stacks hold this path's params\n stack.pop();\n continue;\n }\n const slashPos = path.indexOf('/', frame.pos);\n if (slashPos === -1) {\n frame.seg = path.slice(frame.pos);\n frame.next = path.length;\n } else {\n frame.seg = path.slice(frame.pos, slashPos);\n frame.next = slashPos + 1;\n }\n if (frame.seg === '') {\n const handler = frame.node.handlers.get(method);\n if (handler) return handler;\n stack.pop();\n continue;\n }\n frame.stage = 1;\n const staticChild = frame.node.children.get(frame.seg);\n if (staticChild) {\n stack.push({ node: staticChild, pos: frame.next, stage: 0, seg: '', next: 0, bound: false });\n }\n continue;\n }\n\n // Stage 1 — static child (if any) has failed: try the param child, deferring\n // its binding onto the shared stacks.\n if (frame.stage === 1) {\n frame.stage = 2;\n const paramChild = frame.node.paramChild;\n if (paramChild) {\n const paramName = paramChild.paramName;\n if (paramName === undefined) return null; // degenerate param node → whole walk fails (as before)\n const value =\n originalPath !== undefined\n ? decodeParam(segmentAt(originalPath, frame.pos), decode)\n : decodeParam(frame.seg, decode);\n bindNames.push(paramName);\n bindValues.push(value);\n frame.bound = true;\n stack.push({ node: paramChild, pos: frame.next, stage: 0, seg: '', next: 0, bound: false });\n }\n continue;\n }\n\n // Stage 2 — param branch (if taken) failed: undo its deferred bind, then try\n // the wildcard child (a terminal — it captures the original-case remainder).\n if (frame.bound) {\n bindNames.pop();\n bindValues.pop();\n frame.bound = false;\n }\n const wildcardChild = frame.node.wildcardChild;\n if (wildcardChild) {\n const src = originalPath ?? path;\n bindNames.push('*');\n bindValues.push(decodeParam(src.slice(frame.pos), decode));\n const handler = wildcardChild.handlers.get(method);\n if (handler) return handler;\n bindNames.pop();\n bindValues.pop();\n }\n stack.pop();\n }\n\n return null;\n}\n","/**\n * @nextrush/router - Top-Level Route Match Orchestration\n *\n * `matchRoute` was originally part of `matching.ts` (the lookup-primitives\n * module: `decodeParam`/`extractSegment`/`findNode`/`findAllowedMethods`/\n * `matchNodeIndexed`), but `matching.ts` was approaching the 300-line ceiling\n * itself once `matchRoute` moved in — this file separates the top-level\n * \"match a request\" orchestration (query-strip, normalize, static fast-path,\n * tree-walk delegation, param post-processing) from the lower-level lookup\n * primitives it calls into.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { HttpMethod, Middleware, RouteMatch } from '@nextrush/types';\nimport { EMPTY_PARAMS } from './constants';\nimport { matchNodeIndexed, collapseAndStrip, isProvablyLowerAscii } from './matching';\nimport type { StaticRouteMap, TrieNode } from './segment-trie';\n\n/**\n * Match a route and return the full {@link RouteMatch} in a SINGLE allocation\n * (design.md D1 / HP-10).\n *\n * `matchRoute` builds the final `RouteMatch` — including `middleware:\n * routerMiddleware` — directly at each return site, so `Router.match()` gets\n * one object per matched request instead of a bare result later re-wrapped by\n * `resolveMatch`. `routerMiddleware` is threaded in as a parameter (its\n * contents are never read here — only attached by reference), preserving the\n * \"no implicit `Router` state\" property of the earlier extraction while\n * removing the duplicate wrapper object.\n *\n * Every other piece of `Router` state it reads (`root`, `staticRoutes`,\n * `hasParamRoutes`, the option flags) is read-only here (no mutation, unlike\n * `addRoute` in `registration.ts`), so it is threaded as plain parameters.\n */\nexport function matchRoute(\n method: HttpMethod,\n rawPath: string,\n root: TrieNode,\n staticRoutes: StaticRouteMap,\n hasParamRoutes: boolean,\n caseSensitive: boolean,\n strict: boolean,\n decode: boolean,\n routerMiddleware: Middleware[]\n): RouteMatch | null {\n let path = rawPath;\n // Query string must not affect path matching (RFC 3986 §3.4). Strip it here,\n // before normalization, so both the lookup path and extracted param values\n // exclude it. This strip is caller-specific: `findAllowedMethods` receives an\n // already query-free `ctx.path`, so the shared `normalizePathForMatch` does\n // not strip — only `matchRoute` does.\n const queryIdx = path.indexOf('?');\n if (queryIdx !== -1) path = path.slice(0, queryIdx);\n\n // HP-12: decide case-stability ONCE. When the path is provably case-stable\n // (case-sensitive router, or an all-lowercase-ASCII path), folding is a no-op\n // and the original-case path equals the normalized one — so we skip both the\n // `toLowerCase()` allocation and the second original-case normalize pass.\n const caseStable = caseSensitive || isProvablyLowerAscii(path);\n const folded = caseStable ? path : path.toLowerCase();\n const normalized = collapseAndStrip(folded, strict);\n\n // FAST PATH: O(1) static route lookup (no tree traversal). Method-nested map\n // (HP-9): select the inner map by method, then probe by the normalized path —\n // no per-request `${method} ${path}` key-string allocation. For static routes\n // a trailing slash is irrelevant, so always strip it for the probe.\n const methodMap = staticRoutes.get(method);\n if (methodMap) {\n const staticKey =\n normalized.length > 1 && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n const staticEntry = methodMap.get(staticKey);\n if (staticEntry) {\n return {\n handler: staticEntry.handler,\n params: EMPTY_PARAMS,\n middleware: routerMiddleware,\n executor: staticEntry.executor,\n };\n }\n }\n\n // Only walk tree if we have param/wildcard routes\n if (!hasParamRoutes) return null;\n\n // Original-case (query-stripped) path so extracted param values keep their\n // casing while lookup uses the lowercased `normalized`. Needed ONLY when a\n // fold actually happened (HP-12): when case-stable, `normalized` already IS\n // the original-case structure, so the walk extracts from it and no second\n // normalize pass runs.\n const originalPath = caseStable ? undefined : collapseAndStrip(path, strict);\n\n // Deferred param binding (HP-11): the walk records the accepted path's\n // `:param`/`*` bindings onto these parallel stacks (pushed on descent, popped\n // on backtrack) so params are materialized ONCE here on the accepted terminal\n // — no eager bind + backtrack `Reflect.deleteProperty`.\n const bindNames: string[] = [];\n const bindValues: string[] = [];\n\n const entry = matchNodeIndexed(\n root,\n normalized,\n 1, // Start after leading '/'\n bindNames,\n bindValues,\n method,\n decode,\n originalPath\n );\n if (!entry) return null;\n\n // Materialize params once on a null-prototype object (design.md D8): a param\n // named `__proto__`/`constructor`/`prototype` binds as an OWN key with no\n // prototype mutation, and no inherited member is visible on `ctx.params`. The\n // bind count replaces the former `Object.keys` post-loop (HP-13); zero binds\n // returns the shared frozen `EMPTY_PARAMS`.\n const count = bindNames.length;\n let params: Record<string, string>;\n if (count === 0) {\n params = EMPTY_PARAMS;\n } else {\n params = Object.create(null) as Record<string, string>;\n for (let i = 0; i < count; i++) {\n const name = bindNames[i];\n const value = bindValues[i];\n if (name !== undefined && value !== undefined) params[name] = value;\n }\n }\n\n return {\n handler: entry.handler,\n params,\n middleware: routerMiddleware,\n executor: entry.executor,\n };\n}\n\n/**\n * Stable router state `resolveMatch` reads on every request. All fields are\n * fixed references for the router's lifetime, so the caller memoizes this once\n * rather than rebuilding it per request.\n */\nexport interface MatchState {\n readonly root: TrieNode;\n readonly staticRoutes: StaticRouteMap;\n readonly caseSensitive: boolean;\n readonly strict: boolean;\n readonly decode: boolean;\n readonly routerMiddleware: Middleware[];\n}\n\n/**\n * Resolve a request to a full {@link RouteMatch}. Thin delegator to\n * {@link matchRoute}, which now builds the final `RouteMatch` (incl.\n * `state.routerMiddleware`) in one allocation — so this no longer wraps a\n * result in a second object (HP-10). `Router.match()` is a one-line delegator\n * to this. `hasParamRoutes` is passed separately from `state` because it is\n * the one piece of router state that flips after construction.\n */\nexport function resolveMatch(\n state: MatchState,\n hasParamRoutes: boolean,\n method: HttpMethod,\n path: string\n): RouteMatch | null {\n return matchRoute(\n method,\n path,\n state.root,\n state.staticRoutes,\n hasParamRoutes,\n state.caseSensitive,\n state.strict,\n state.decode,\n state.routerMiddleware\n );\n}\n","/**\n * @nextrush/router - Router Composition\n *\n * Sub-router mounting/copying logic extracted from the `Router` class (T014,\n * design.md D3 — extracted after the matching-engine split left `router.ts`\n * still over the 300-line ceiling).\n *\n * `copyRoutes`'s tree walk is structurally pure (segments/prefix/middleware are\n * all explicit parameters), but its side effect — registering the copied route —\n * needs `Router.addRoute`, a private method. Rather than reach into `Router`\n * internals or promote `addRoute` to `public` (a public-API change out of scope\n * for this refactor), the effect is injected explicitly as an `addRoute`\n * callback parameter. `use`/`mount`/`mountRouter` stay on `Router` itself:\n * they return `this` for fluent chaining and read `Router` instance state\n * (`root`, `routerMiddleware`) that isn't naturally expressible as function\n * parameters without more ceremony than the win justifies.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { HttpMethod, Middleware, RouteHandler } from '@nextrush/types';\nimport type { TrieNode } from './segment-trie';\n\n/** Callback signature matching `Router['addRoute']` — injected, not imported. */\nexport type AddRouteFn = (\n method: HttpMethod,\n path: string,\n handlers: RouteHandler[],\n middleware: Middleware[]\n) => void;\n\n/**\n * Recursively copy routes from one router's trie into another via the\n * supplied `addRoute` callback.\n */\nexport function copyRoutes(\n node: TrieNode,\n prefix: string,\n segments: string[],\n subRouterMiddleware: Middleware[],\n addRoute: AddRouteFn\n): void {\n // Copy handlers at this node\n for (const [method, entry] of node.handlers) {\n const path = prefix + '/' + segments.join('/');\n // Prepend sub-router middleware so it runs before the route's own middleware\n const combined =\n subRouterMiddleware.length > 0\n ? [...subRouterMiddleware, ...entry.middleware]\n : entry.middleware;\n addRoute(method, path || '/', [entry.handler], combined);\n }\n\n // Copy static children\n for (const [, child] of node.children) {\n copyRoutes(child, prefix, [...segments, child.segment], subRouterMiddleware, addRoute);\n }\n\n // Copy param child\n if (node.paramChild) {\n copyRoutes(\n node.paramChild,\n prefix,\n [...segments, node.paramChild.segment],\n subRouterMiddleware,\n addRoute\n );\n }\n\n // Copy wildcard child\n if (node.wildcardChild) {\n copyRoutes(node.wildcardChild, prefix, [...segments, '*'], subRouterMiddleware, addRoute);\n }\n}\n","/**\n * @nextrush/router - Middleware Adaptation\n *\n * Router-level-middleware sealing logic extracted from the `Router` class\n * (T014, design.md D3 — extracted after the matching-engine and composition\n * splits left `router.ts` still over the 300-line ceiling).\n *\n * The tree-walk that re-compiles every handler's executor is structurally\n * pure (it only mutates the `TrieNode`/`HandlerEntry` structures passed in,\n * same shape as `copyRoutes` in `composition.ts`) — it doesn't touch\n * `Router`-only state like `_sealed`, so it extracts cleanly. `routes()` and\n * `allowedMethods()` stay on `Router`: both return closures that capture\n * `this` (for `this.match`, `this.opts`, `this.root`) and are the router's\n * actual public middleware-producing API, not internal tree mechanics.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport { compileExecutor, type StaticRouteMap, type TrieNode } from './segment-trie';\nimport type { Middleware } from '@nextrush/types';\n\n/**\n * Re-compile every route executor in the trie (plus the static-route hash\n * map) to include router-level middleware ahead of each route's own\n * middleware. Idempotency (guarding against a double seal) is the caller's\n * responsibility — this function always re-walks and re-compiles.\n */\nexport function sealRouterMiddleware(\n root: TrieNode,\n staticRoutes: StaticRouteMap,\n routerMiddleware: Middleware[]\n): void {\n const routerMw = [...routerMiddleware];\n\n const walk = (node: TrieNode): void => {\n for (const [method, entry] of node.handlers) {\n const combinedMw = [...routerMw, ...entry.middleware];\n entry.executor = compileExecutor(entry.handler, combinedMw);\n node.handlers.set(method, entry);\n }\n for (const [, child] of node.children) {\n walk(child);\n }\n if (node.paramChild) walk(node.paramChild);\n if (node.wildcardChild) walk(node.wildcardChild);\n };\n\n walk(root);\n\n // Also update static route entries (method-nested map, HP-9).\n for (const [, methodMap] of staticRoutes) {\n for (const [key, entry] of methodMap) {\n const combinedMw = [...routerMw, ...entry.middleware];\n entry.executor = compileExecutor(entry.handler, combinedMw);\n methodMap.set(key, entry);\n }\n }\n}\n","/**\n * @nextrush/router - Route Registration\n *\n * Trie-insertion logic extracted from the `Router` class (T014, design.md D2 —\n * D2 keeps HTTP-verb shortcuts (`get`/`post`/etc.) on `Router` itself, but\n * explicitly permits extracting their shared internal, `addRoute`, \"if\n * `addRoute` itself is large enough to warrant it.\" At 116 lines and the\n * highest cyclomatic/cognitive complexity in the file, it is.\n *\n * `addRoute` touches five pieces of `Router` instance state (the trie root,\n * `caseSensitive`, the `hasParamRoutes` flag, the static-route hash map, and\n * the introspection registry) as both reads and writes. All five are threaded\n * explicitly rather than read/written through an implicit `this`, so this\n * function has no hidden dependency on `Router` beyond what's passed in —\n * same principle as the matching-engine extraction (design.md D1).\n *\n * @packageDocumentation\n * @internal\n */\n\nimport { HTTP_METHODS } from '@nextrush/types';\nimport type {\n HttpMethod,\n MetadataContribution,\n Middleware,\n RouteDefinition,\n RouteEntry,\n RouteHandler,\n} from '@nextrush/types';\nimport {\n compileExecutor,\n createNode,\n NodeType,\n parseSegments,\n type HandlerEntry,\n type StaticRouteMap,\n type TrieNode,\n} from './segment-trie';\nimport { mergeContributions, readContribution } from './route-metadata';\nimport { createRedirectHandler, type RedirectStatus } from './redirect';\n\n/** Registration state `addRoute` reads, threaded explicitly. */\nexport interface RegistrationState {\n readonly root: TrieNode;\n readonly caseSensitive: boolean;\n readonly staticRoutes: StaticRouteMap;\n readonly routeDefinitions: RouteDefinition[];\n}\n\n/**\n * Normalize a route path at registration time: join the router prefix,\n * collapse duplicate slashes, drop a trailing slash in non-strict mode, and\n * guarantee a leading slash.\n *\n * @remarks\n * This is registration-time normalization — it joins the router `prefix` and\n * never case-folds (case handling belongs to trie insertion / matching). It is\n * a distinct concern from `normalizePathForMatch` in `matching.ts`, which\n * normalizes an *incoming request* path for lookup; both were extracted from\n * `Router` to keep each single-definition (design.md D2/D3).\n */\nexport function normalizeRegistrationPath(path: string, prefix: string, strict: boolean): string {\n // Handle prefix with trailing slash and path with leading slash\n let joinedPrefix = prefix;\n if (joinedPrefix.endsWith('/') && path.startsWith('/')) {\n joinedPrefix = joinedPrefix.slice(0, -1);\n }\n\n let normalized = joinedPrefix + path;\n\n // Fast-path: skip regex when no double slashes (99%+ of requests)\n if (normalized.includes('//')) {\n normalized = normalized.replace(/\\/+/g, '/');\n }\n\n // For non-strict mode during registration, remove trailing slash\n if (!strict && normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n\n return normalized.startsWith('/') ? normalized : '/' + normalized;\n}\n\n/**\n * Insert one route into the segment trie, updating every side structure\n * (static-route fast-path map, introspection registry) that\n * `Router.match`/`Router.getRoutes` depend on.\n *\n * @param normalized - Already-normalized path (caller applies `Router`'s own\n * prefix/strict-mode normalization before calling this — normalization\n * itself stays a `Router` method since it only touches `opts`, not the trie).\n * @param recordIntrospection - When `false`, skips pushing this call's row\n * into `state.routeDefinitions` while still inserting the concrete\n * per-method trie handler (matching is completely unaffected either way).\n * `Router.all()` (T016) sets this to `false` on each of its 7 per-method\n * `addRoute` calls, then pushes exactly one consolidated\n * `isAnyMethod: true` row itself — so an any-method route yields a single\n * `getRoutes()` entry instead of one row per enumerated method, without\n * touching how any individual method is matched. Every other call site\n * (`get`/`post`/etc., `redirect`, sub-router mounting) omits this parameter\n * and keeps the original one-row-per-call behavior unchanged.\n * @returns `true` if the registered route has a param or wildcard segment —\n * the caller (`Router.addRoute`) uses this to flip its own `hasParamRoutes`\n * flag. Returned rather than mutated through `state` because it's a\n * primitive: a struct field can't carry a boolean mutation back to the\n * caller by reference the way `root`/`staticRoutes`/`routeDefinitions` do.\n */\nexport function addRoute(\n method: HttpMethod,\n normalized: string,\n entries: RouteEntry[],\n middleware: Middleware[],\n state: RegistrationState,\n recordIntrospection = true\n): boolean {\n const segments = parseSegments(normalized, state.caseSensitive);\n\n let node = state.root;\n\n for (const seg of segments) {\n if (seg.type === NodeType.PARAM) {\n if (!node.paramChild) {\n node.paramChild = createNode(seg.segment, NodeType.PARAM);\n node.paramChild.paramName = seg.paramName;\n } else if (node.paramChild.paramName !== seg.paramName) {\n // Same position, different param names — this silently loses one name\n // at runtime (params[newName] is undefined), so fail fast at\n // registration rather than warn (audit RT-5). Also removes the\n // process.env / console.warn usage that was here.\n throw new Error(\n `Route param name conflict at \"${normalized}\": \":${String(seg.paramName)}\" ` +\n `conflicts with existing \":${String(node.paramChild.paramName)}\" at the same ` +\n `position. Use the same param name for this segment across all routes.`\n );\n }\n node = node.paramChild;\n } else if (seg.type === NodeType.WILDCARD) {\n node.wildcardChild ??= createNode('*', NodeType.WILDCARD);\n node = node.wildcardChild;\n break; // Wildcard must be last\n } else {\n const key = seg.segment;\n let child = node.children.get(key);\n if (!child) {\n child = createNode(seg.segment, NodeType.STATIC);\n node.children.set(key, child);\n }\n node = child;\n }\n }\n\n // Partition entries: functions are behavior (inline middleware + the final\n // handler); pure markers (endpoint()) contribute metadata only and never\n // execute. Every entry may also carry a metadata contribution (e.g. a\n // function like validate() that both runs and contributes its schema).\n const functions: Middleware[] = [];\n const contributions: MetadataContribution[] = [];\n for (const routeEntry of entries) {\n const contribution = readContribution(routeEntry);\n if (contribution) contributions.push(contribution);\n if (typeof routeEntry === 'function') functions.push(routeEntry);\n }\n\n // Combine functions into a single handler with inline middleware\n const combinedMiddleware = [...middleware];\n const finalHandler: RouteHandler | undefined = functions[functions.length - 1];\n\n if (!finalHandler) {\n throw new Error('At least one handler is required');\n }\n\n // Inline middleware = every function before the last\n for (let i = 0; i < functions.length - 1; i++) {\n const fn = functions[i];\n if (fn) combinedMiddleware.push(fn);\n }\n\n // Pre-compile executor at registration time (not per-request!)\n const executor = compileExecutor(finalHandler, combinedMiddleware);\n\n const handlerEntry: HandlerEntry = {\n handler: finalHandler,\n middleware: combinedMiddleware,\n executor,\n };\n\n // Detect duplicate route registration\n if (node.handlers.has(method)) {\n throw new Error(\n `Route conflict: ${method} ${normalized} is already registered. ` +\n 'Remove the duplicate or use a different path.'\n );\n }\n\n node.handlers.set(method, handlerEntry);\n\n // Populate static route hash map for O(1) lookup. Method-nested (HP-9): the\n // outer map is keyed by method, the inner by the (lowercased, unless\n // case-sensitive) path — so matching selects the inner map by method and\n // probes by the raw path with no per-request key-string concatenation.\n const hasParams = segments.some((s) => s.type === NodeType.PARAM || s.type === NodeType.WILDCARD);\n if (!hasParams) {\n const normalizedKey = state.caseSensitive ? normalized : normalized.toLowerCase();\n let methodMap = state.staticRoutes.get(method);\n if (!methodMap) {\n methodMap = new Map<string, HandlerEntry>();\n state.staticRoutes.set(method, methodMap);\n }\n methodMap.set(normalizedKey, handlerEntry);\n }\n\n // Record in the introspection registry (side structure — never touched by\n // request dispatch). key is the canonical `${METHOD} ${pathPattern}`. Skipped\n // when the caller is consolidating multiple addRoute calls into a single row\n // itself (Router.all(), T016) — the concrete trie handler above is still\n // inserted regardless, so matching for this method is unaffected.\n if (recordIntrospection) {\n state.routeDefinitions.push({\n key: `${method} ${normalized}`,\n method,\n path: normalized,\n metadata: mergeContributions(contributions),\n });\n }\n\n return hasParams;\n}\n\n/**\n * Callback signature matching `Router['addRoute']` (the validating/\n * normalizing wrapper, not the extracted `addRoute` above) — injected so\n * `registerRedirect` doesn't need `Router` instance access, same pattern as\n * `copyRoutes`'s `AddRouteFn` in `composition.ts`.\n */\nexport type RegisterRouteFn = (method: HttpMethod, path: string, entries: RouteEntry[]) => void;\n\n/**\n * Register a redirect route from one path to another across every HTTP\n * method the target status code requires (GET/HEAD always; POST/PUT/PATCH/\n * DELETE additionally for 307/308, which must preserve the original method).\n *\n * Extracted from `Router.redirect()` — thematically closer to \"register N\n * routes for one call\" than to composition or matching, and, like\n * `copyRoutes`, only needs `addRoute` injected rather than full `Router`\n * instance access.\n */\nexport function registerRedirect(\n from: string,\n to: string,\n status: RedirectStatus,\n addRoute: RegisterRouteFn\n): void {\n const redirectHandler = createRedirectHandler(to, status);\n\n // Register for common methods. 307/308 preserve the original method,\n // so register all standard methods for those status codes.\n addRoute('GET', from, [redirectHandler]);\n addRoute('HEAD', from, [redirectHandler]);\n if (status === 307 || status === 308) {\n addRoute('POST', from, [redirectHandler]);\n addRoute('PUT', from, [redirectHandler]);\n addRoute('PATCH', from, [redirectHandler]);\n addRoute('DELETE', from, [redirectHandler]);\n }\n}\n\n/**\n * Push a single consolidated any-method (`isAnyMethod: true`) introspection\n * row (T016) into the registry.\n *\n * @remarks\n * `Router.all()` / `GroupRouter.all()` insert one concrete per-method trie\n * handler each (so every method still matches) with `recordIntrospection`\n * off, then call this exactly once — so `getRoutes()` yields a single row per\n * `.all()`/`@All()` route instead of one row per enumerated HTTP method,\n * without changing how any individual method is matched.\n *\n * @param routeDefinitions - The router's introspection registry to append to.\n * @param normalized - The already-normalized route path.\n */\nexport function pushAnyMethodDefinition(\n routeDefinitions: RouteDefinition[],\n normalized: string\n): void {\n routeDefinitions.push({\n key: `${HTTP_METHODS[0]} ${normalized}`,\n method: HTTP_METHODS[0],\n path: normalized,\n isAnyMethod: true,\n });\n}\n","/**\n * @nextrush/router - Route Metadata Contributions\n *\n * Inline route metadata (`endpoint()`) and the registration-time merge of\n * contributions from `validate()`, `endpoint()`, etc. Extracted from `router.ts`\n * (audit RT-3). None of this is on the request hot path — it is read only at\n * registration and by `getRoutes()` at doc-generation time.\n *\n * @packageDocumentation\n */\n\nimport {\n ROUTE_METADATA,\n type MetadataContribution,\n type RouteEntry,\n type RouteMetadata,\n type RouteMetaMarker,\n} from '@nextrush/types';\n\n/**\n * Declare route metadata inline in a route's argument list. Returns a pure data\n * marker (not a middleware) — the router reads its metadata at registration and\n * never executes it per request.\n *\n * @example\n * ```typescript\n * router.post('/users',\n * validate(User),\n * endpoint({ summary: 'Create a user', responses: { 201: UserResponse } }),\n * handler,\n * );\n * ```\n */\nexport function endpoint(metadata: MetadataContribution): RouteMetaMarker {\n return { [ROUTE_METADATA]: metadata };\n}\n\n/** Mutable view of RouteMetadata, used only while merging contributions. */\ntype MutableRouteMetadata = { -readonly [K in keyof RouteMetadata]?: RouteMetadata[K] };\n\n/**\n * Merge metadata contributions (from `validate()`, `endpoint()`, etc.) in\n * registration order. Scalars and arrays are last-write-wins; the `request` and\n * `responses` maps merge per key. Returns `undefined` when nothing contributed\n * (an undocumented route).\n */\nexport function mergeContributions(\n contributions: readonly MetadataContribution[]\n): RouteMetadata | undefined {\n if (contributions.length === 0) return undefined;\n\n const meta: MutableRouteMetadata = {};\n for (const c of contributions) {\n if (c.summary !== undefined) meta.summary = c.summary;\n if (c.description !== undefined) meta.description = c.description;\n if (c.deprecated !== undefined) meta.deprecated = c.deprecated;\n if (c.visibility !== undefined) meta.visibility = c.visibility;\n if (c.tags !== undefined) meta.tags = c.tags;\n if (c.request) meta.request = { ...meta.request, ...c.request };\n if (c.responses) meta.responses = { ...meta.responses, ...c.responses };\n }\n return meta;\n}\n\n/** Read a route entry's metadata contribution, if it carries one. */\nexport function readContribution(entry: RouteEntry): MetadataContribution | undefined {\n return (entry as Partial<RouteMetaMarker>)[ROUTE_METADATA];\n}\n","/**\n * @nextrush/router - Method-agnostic trie walk (allowed-methods / 405 path)\n *\n * `findNode` + `findAllowedMethods` were split out of `matching.ts` when the\n * iterative rewrite of `findNode` (HP-17, OpenSpec change\n * `router-context-final-cleanup`) pushed that file past the 300-line ceiling.\n * They form one cohesive concern — resolving the *node* for a path regardless\n * of HTTP method, to answer OPTIONS/405 — distinct from `matching.ts`'s\n * method-aware handler match (`matchNodeIndexed`) and its normalization\n * primitives, which are reused here via import (`segmentAt`,\n * `normalizePathForMatch`).\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { HttpMethod } from '@nextrush/types';\nimport { normalizePathForMatch, segmentAt } from './matching';\nimport type { TrieNode } from './segment-trie';\n\n/**\n * One node in {@link findNode}'s explicit-stack walk. `stage` is a small state\n * machine (0 = extract segment + try static, 1 = try param, 2 = try\n * wildcard/backtrack) so a single frame can be revisited on backtrack without\n * recursion. `next` is the start position of the following segment, captured\n * once in stage 0 and reused when descending into the param branch.\n */\ninterface FindFrame {\n node: TrieNode;\n pos: number;\n stage: 0 | 1 | 2;\n next: number;\n}\n\n/**\n * Walk the trie to find the node matching a path (ignoring HTTP method), the\n * method-agnostic walker used by {@link findAllowedMethods} for the 405/OPTIONS\n * path (design.md D3 / HP-17).\n *\n * Walks with an EXPLICIT stack instead of recursion — mirroring\n * `matchNodeIndexed` — so a pathological segment count cannot overflow the call\n * stack (the same DoS class HP-11 closed for the match path). Behavior is\n * byte-identical to the former recursive walker: precedence is static > param >\n * wildcard at each node, a partially-matching branch backtracks cleanly, the\n * wildcard child is a terminal (it captures the remainder), and the first\n * accepted terminal wins. The scalar {@link segmentAt} scan is reused so the\n * traversal shares one segment-extraction helper rather than duplicating it.\n *\n * `path` is the already-normalized lookup path; `startPos` skips the leading\n * `/` (callers pass `1`, matching `matchNodeIndexed`).\n */\nexport function findNode(root: TrieNode, path: string, startPos: number): TrieNode | null {\n const stack: FindFrame[] = [{ node: root, pos: startPos, stage: 0, next: 0 }];\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1];\n if (frame === undefined) break;\n\n // Stage 0 — first visit: terminal check, then try the static child.\n if (frame.stage === 0) {\n // Whole path consumed at this node → it is the matching node.\n if (frame.pos >= path.length) {\n return frame.node;\n }\n const seg = segmentAt(path, frame.pos);\n // An empty segment means the path is exhausted here (e.g. a strict-mode\n // trailing slash) — treat this node as the terminal, as the recursive\n // walk did after `split('/').filter(Boolean)` dropped empty segments.\n if (seg === '') {\n return frame.node;\n }\n // `segmentAt` already scanned to the next `/`; derive the following\n // position from the segment length (no second indexOf) — a slash follows\n // iff the segment ended before the path did.\n const segEnd = frame.pos + seg.length;\n frame.next = segEnd < path.length ? segEnd + 1 : path.length;\n frame.stage = 1;\n const staticChild = frame.node.children.get(seg);\n if (staticChild) {\n stack.push({ node: staticChild, pos: frame.next, stage: 0, next: 0 });\n }\n continue;\n }\n\n // Stage 1 — static child (if any) failed: try the param child.\n if (frame.stage === 1) {\n frame.stage = 2;\n if (frame.node.paramChild) {\n stack.push({ node: frame.node.paramChild, pos: frame.next, stage: 0, next: 0 });\n }\n continue;\n }\n\n // Stage 2 — static and param branches exhausted: the wildcard child is a\n // terminal (matches the remainder). Otherwise this branch fails; backtrack.\n if (frame.node.wildcardChild) {\n return frame.node.wildcardChild;\n }\n stack.pop();\n }\n\n return null;\n}\n\n/**\n * Find all HTTP methods registered for a given path via a single tree walk.\n * `caseSensitive`/`strict` (formerly `this.opts.*`) and `root` (formerly\n * `this.root`) are threaded explicitly.\n */\nexport function findAllowedMethods(\n path: string,\n root: TrieNode,\n caseSensitive: boolean,\n strict: boolean\n): HttpMethod[] {\n const normalized = normalizePathForMatch(path, caseSensitive, strict);\n\n // Walk from position 1 to skip the leading '/', matching `matchNodeIndexed`'s\n // start offset. The iterative `findNode` scans segments off the path in place,\n // so no `split('/')` array is allocated here.\n const node = findNode(root, normalized, 1);\n if (!node || node.handlers.size === 0) return [];\n\n return Array.from(node.handlers.keys());\n}\n","/**\n * @nextrush/router - Dispatch Middleware Generation\n *\n * The two app-facing `Middleware` factories extracted from the `Router` class\n * (design.md D2 — finishing T014's split along the same seam: the composition,\n * matching-engine, and sealing clusters were already extracted; this is the\n * dispatch/allowed-methods generation cluster).\n *\n * Both closures are structurally pure — every value they read (the `match`\n * function, the trie root, the case-sensitivity/strict flags) is passed in\n * explicitly rather than captured off `this`, so they carry no hidden\n * dependency on `Router` beyond their parameters (same principle as the\n * matching-engine extraction, design.md D1). `Router.routes()` and\n * `Router.allowedMethods()` stay as thin public methods that supply that\n * state and return these closures.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { Context, HttpMethod, Middleware, RouteMatch } from '@nextrush/types';\nimport { NOOP_NEXT, type TrieNode } from './segment-trie';\nimport { findAllowedMethods } from './find-node';\n\n/**\n * Shared resolved promise for the no-`next` miss path (NF-1). Reused rather than\n * allocating a fresh `Promise.resolve()` per miss, mirroring the router's\n * existing `NOOP_NEXT`/`RESOLVED_PROMISE` sentinels.\n */\nconst RESOLVED: Promise<void> = Promise.resolve();\n\n/**\n * Build the router's primary dispatch middleware.\n *\n * On each request it resolves the route via the injected `match` function,\n * sets `ctx.params`, and runs the pre-compiled executor (which already bakes\n * in any router-level middleware). A miss sets `ctx.status = 404` and yields\n * to the next middleware so `allowedMethods()`/a 404 handler can act.\n *\n * @param match - Route resolver, supplied by `Router.match` so this factory\n * never touches `Router` internals directly.\n */\nexport function createRoutesMiddleware(\n match: (method: HttpMethod, path: string) => RouteMatch | null\n): Middleware {\n return (ctx: Context, next?: () => Promise<void>): Promise<void> => {\n const routeMatch = match(ctx.method, ctx.path);\n\n if (!routeMatch) {\n // No route matched — set 404 so allowedMethods()/notFoundHandler() can act,\n // then forward to the next middleware (the allowedMethods fall-through).\n ctx.status = 404;\n return next ? next() : RESOLVED;\n }\n\n ctx.params = routeMatch.params;\n\n // NF-1: forward the executor's promise DIRECTLY instead of `await`-ing it in\n // an extra `async` frame. The executor already returns a `Promise<void>`,\n // converts synchronous throws to rejections, and terminates the chain at the\n // handler, so ordering, rejection propagation, and the `setNext(NOOP_NEXT)`\n // guard are unchanged — one state machine + one microtask hop removed. A\n // synchronous throw from `match()` itself is still converted to a rejection\n // by the composer's `try/catch` that wraps this middleware call.\n return routeMatch.executor\n ? routeMatch.executor(ctx)\n : // Fallback (no pre-compiled executor — shouldn't happen): wrap so a void\n // or thenable return still yields a Promise<void> and never a sync throw.\n Promise.resolve(routeMatch.handler(ctx, NOOP_NEXT));\n };\n}\n\n/**\n * Build the allowed-methods middleware.\n *\n * Runs after the dispatch middleware: if the request was a 404, it does a\n * single tree walk to collect every method registered for the path. An\n * `OPTIONS` request gets a `200` with an `Allow` header; any other method\n * gets a `405` with `Allow`. If no method is registered for the path it\n * leaves the 404 untouched.\n *\n * @param root - Trie root to walk for allowed methods.\n * @param caseSensitive - Router case-sensitivity option.\n * @param strict - Router strict-trailing-slash option.\n */\nexport function createAllowedMethodsMiddleware(\n root: TrieNode,\n caseSensitive: boolean,\n strict: boolean\n): Middleware {\n return async (ctx: Context, next?: () => Promise<void>): Promise<void> => {\n if (next) {\n await next();\n }\n\n if (ctx.status !== 404) return;\n\n // Single tree walk to find all allowed methods instead of N×match()\n const allowed = findAllowedMethods(ctx.path, root, caseSensitive, strict);\n\n if (allowed.length === 0) return;\n\n const allowHeader = allowed.join(', ');\n\n // If OPTIONS request, respond with allowed methods\n if (ctx.method === 'OPTIONS') {\n ctx.status = 200;\n ctx.set('Allow', allowHeader);\n ctx.body = '';\n return;\n }\n\n // Otherwise, return 405 Method Not Allowed\n ctx.status = 405;\n ctx.set('Allow', allowHeader);\n };\n}\n","/**\n * @nextrush/router - Router State Construction\n *\n * Pure builders for the `Router`'s resolved options and its memoized state\n * struct, extracted from the constructor (design.md D2) so `Router` stays a\n * thin shell that delegates even its own initialization.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { Middleware, RouteDefinition, RouterOptions } from '@nextrush/types';\nimport type { StaticRouteMap, TrieNode } from './segment-trie';\nimport type { RegistrationState } from './registration';\nimport type { MatchState } from './match-route';\n\n/** Apply defaults to user-supplied router options. */\nexport function resolveRouterOptions(options: RouterOptions): Required<RouterOptions> {\n return {\n prefix: options.prefix ?? '',\n caseSensitive: options.caseSensitive ?? false,\n strict: options.strict ?? false,\n decode: options.decode ?? true,\n };\n}\n\n/**\n * Build the state struct the extracted registration/matching functions read.\n *\n * @remarks\n * Every field is a stable reference for the router's lifetime, so the caller\n * memoizes this once — `reset()` mutates the referenced structures in place, it\n * never reassigns them. A single struct satisfies both `RegistrationState`\n * (what `addRoute` reads) and `MatchState` (what `resolveMatch` reads).\n */\nexport function createRouterState(\n root: TrieNode,\n opts: Required<RouterOptions>,\n staticRoutes: StaticRouteMap,\n routeDefinitions: RouteDefinition[],\n routerMiddleware: Middleware[]\n): RegistrationState & MatchState {\n return {\n root,\n staticRoutes,\n routeDefinitions,\n caseSensitive: opts.caseSensitive,\n strict: opts.strict,\n decode: opts.decode,\n routerMiddleware,\n };\n}\n"],"mappings":"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","names":["HTTP_METHODS","NodeType","RESOLVED_PROMISE","Promise","resolve","NOOP_NEXT","compileExecutor","handler","middleware","len","length","ctx","setNext","err","reject","Error","String","index","dispatch","i","mw","undefined","next","createNode","segment","type","children","Map","handlers","clearNode","node","clear","paramChild","wildcardChild","parseSegments","path","caseSensitive","normalized","startsWith","slice","parts","split","segments","part","paramName","push","toLowerCase","HTTP_METHODS","compileRedirectTarget","to","parts","pos","found","length","idx","i","push","slice","end","indexOf","undefined","createRedirectHandler","status","compiledParts","ctx","targetPath","params","head","result","paramKey","tail","set","body","GroupRouter","parent","prefix","middleware","fullPath","path","cleanPrefix","endsWith","slice","cleanPath","startsWith","get","handlers","_addGroupRoute","post","put","delete","patch","head","options","all","method","HTTP_METHODS","_pushAnyMethodRouteDefinition","redirect","from","to","status","redirectHandler","createRedirectHandler","group","middlewareOrCallback","callback","runRouteGroup","host","inheritedMiddleware","cb","Array","isArray","Error","combined","length","EMPTY_PARAMS","Object","freeze","create","decodeParam","value","decode","includes","decodeURIComponent","segmentAt","path","start","slashPos","indexOf","slice","isProvablyLowerAscii","i","length","c","charCodeAt","collapseAndStrip","strict","normalized","replace","endsWith","normalizePathForMatch","caseSensitive","folded","toLowerCase","matchNodeIndexed","root","startPos","bindNames","bindValues","method","originalPath","stack","node","pos","stage","seg","next","bound","frame","undefined","handler","handlers","get","pop","staticChild","children","push","paramChild","paramName","wildcardChild","src","matchRoute","method","rawPath","root","staticRoutes","hasParamRoutes","caseSensitive","strict","decode","routerMiddleware","path","queryIdx","indexOf","slice","caseStable","isProvablyLowerAscii","folded","toLowerCase","normalized","collapseAndStrip","methodMap","get","staticKey","length","endsWith","staticEntry","handler","params","EMPTY_PARAMS","middleware","executor","originalPath","undefined","bindNames","bindValues","entry","matchNodeIndexed","count","Object","create","i","name","value","resolveMatch","state","copyRoutes","node","prefix","segments","subRouterMiddleware","addRoute","method","entry","handlers","path","join","combined","length","middleware","handler","child","children","segment","paramChild","wildcardChild","sealRouterMiddleware","root","staticRoutes","routerMiddleware","routerMw","walk","node","method","entry","handlers","combinedMw","middleware","executor","compileExecutor","handler","set","child","children","paramChild","wildcardChild","methodMap","key","HTTP_METHODS","ROUTE_METADATA","endpoint","metadata","ROUTE_METADATA","mergeContributions","contributions","length","undefined","meta","c","summary","description","deprecated","visibility","tags","request","responses","readContribution","entry","normalizeRegistrationPath","path","prefix","strict","joinedPrefix","endsWith","startsWith","slice","normalized","includes","replace","length","addRoute","method","entries","middleware","state","recordIntrospection","segments","parseSegments","caseSensitive","node","root","seg","type","NodeType","PARAM","paramChild","createNode","segment","paramName","Error","String","WILDCARD","wildcardChild","key","child","children","get","STATIC","set","functions","contributions","routeEntry","contribution","readContribution","push","combinedMiddleware","finalHandler","i","fn","executor","compileExecutor","handlerEntry","handler","handlers","has","hasParams","some","s","normalizedKey","toLowerCase","methodMap","staticRoutes","Map","routeDefinitions","metadata","mergeContributions","registerRedirect","from","to","status","redirectHandler","createRedirectHandler","pushAnyMethodDefinition","HTTP_METHODS","isAnyMethod","findNode","root","path","startPos","stack","node","pos","stage","next","length","frame","undefined","seg","segmentAt","segEnd","staticChild","children","get","push","paramChild","wildcardChild","pop","findAllowedMethods","caseSensitive","strict","normalized","normalizePathForMatch","handlers","size","Array","from","keys","RESOLVED","Promise","resolve","createRoutesMiddleware","match","ctx","next","routeMatch","method","path","status","params","executor","handler","NOOP_NEXT","createAllowedMethodsMiddleware","root","caseSensitive","strict","allowed","findAllowedMethods","length","allowHeader","join","set","body","resolveRouterOptions","options","prefix","caseSensitive","strict","decode","createRouterState","root","opts","staticRoutes","routeDefinitions","routerMiddleware","Router","root","opts","routerMiddleware","staticRoutes","Map","routeDefinitions","hasParamRoutes","_sealed","state","options","createNode","resolveRouterOptions","createRouterState","addRoute","method","path","entries","middleware","recordIntrospection","rawPath","TypeError","normalized","normalizeRegistrationPath","prefix","strict","addRouteImpl","get","post","put","delete","patch","head","all","HTTP_METHODS","pushAnyMethodDefinition","route","getRoutes","redirect","from","to","status","registerRedirect","use","pathOrMiddleware","routerOrUndefined","push","mountRouter","Error","mount","router","copyRoutes","bind","match","resolveMatch","routes","length","sealRouterMiddlewareImpl","createRoutesMiddleware","allowedMethods","createAllowedMethodsMiddleware","caseSensitive","group","middlewareOrCallback","callback","runRouteGroup","reset","clearNode","clear","_addGroupRoute","handlers","groupMiddleware","_pushAnyMethodRouteDefinition","createRouter"]}
1
+ {"version":3,"sources":["../src/router.ts","../src/segment-trie.ts","../src/group-router.ts","../src/redirect.ts","../src/constants.ts","../src/walk-pool.ts","../src/matching.ts","../src/match-route.ts","../src/composition.ts","../src/middleware-adapter.ts","../src/registration.ts","../src/route-metadata.ts","../src/find-node.ts","../src/canonicalize.ts","../src/dispatch.ts","../src/state.ts"],"sourcesContent":["/**\n * @nextrush/router - Router Implementation\n *\n * The public `Router` shell: a thin, chainable facade delegating registration,\n * matching, dispatch, composition, and grouping to focused sibling modules\n * (design.md D1/D2). Segment trie keyed by whole path segments, not a radix tree.\n *\n * @packageDocumentation\n */\n\nimport {\n HTTP_METHODS,\n type HttpMethod,\n type Middleware,\n type RouteDefinition,\n type RouteEntry,\n type RouteHandler,\n type RouteMatch,\n type RouterOptions,\n} from '@nextrush/types';\nimport { clearNode, createNode, type StaticRouteMap, type TrieNode } from './segment-trie';\nimport { type RedirectStatus } from './redirect';\nimport { runRouteGroup, type RouteGroup } from './group-router';\nimport { resolveMatch, type MatchState } from './match-route';\nimport { createWalkPool } from './walk-pool';\nimport { copyRoutes } from './composition';\nimport { sealRouterMiddleware as sealRouterMiddlewareImpl } from './middleware-adapter';\nimport {\n addRoute as addRouteImpl,\n normalizeRegistrationPath,\n pushAnyMethodDefinition,\n registerRedirect,\n type RegistrationState,\n} from './registration';\nimport { createAllowedMethodsMiddleware, createRoutesMiddleware } from './dispatch';\nimport { createRouterState, resolveRouterOptions } from './state';\nimport { canonicalizePath } from './canonicalize';\n\n/** '/'.charCodeAt(0) — used by {@link Router.matchesMountPrefix}'s boundary check. */\nconst SLASH_CHAR_CODE = 0x2f;\n\n/** Inline route metadata declaration — re-exported from its own module (RT-3). */\nexport { endpoint } from './route-metadata';\n\n/**\n * High-performance segment-trie router: O(d) lookup by path segment, with a\n * static-route hash map for an O(1) fast path.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md | @nextrush/router README}\n */\nexport class Router {\n private readonly root: TrieNode;\n private readonly opts: Required<RouterOptions>;\n private readonly routerMiddleware: Middleware[] = [];\n\n /** Static-route fast path: method-nested map for O(1) lookup with no per-request key string (HP-9). */\n private readonly staticRoutes: StaticRouteMap = new Map();\n\n /**\n * Introspection registry, kept SEPARATE from the hot-path trie/staticRoutes\n * so request dispatch never reads metadata — only getRoutes() touches it.\n */\n private readonly routeDefinitions: RouteDefinition[] = [];\n\n /** Whether any routes have params or wildcards (disables static-only fast path) */\n private hasParamRoutes = false;\n\n /** Whether router-level middleware has already been sealed into executors (audit RT-7) */\n private _sealed = false;\n\n /**\n * Single-entry memo for {@link matchesMountPrefix}'s canonicalization of the\n * mount prefix, which is fixed at registration time. Declared as two fields\n * rather than an object so a miss stores without allocating.\n */\n private _prefixMemoRaw: string | undefined = undefined;\n private _prefixMemoCanonical = '';\n\n /** Memoized state the extracted registration/matching functions read (see {@link createRouterState}). */\n private readonly state: RegistrationState & MatchState;\n\n constructor(options: RouterOptions = {}) {\n this.root = createNode('');\n this.opts = resolveRouterOptions(options);\n this.state = createRouterState(\n this.root,\n this.opts,\n this.staticRoutes,\n this.routeDefinitions,\n this.routerMiddleware\n );\n }\n\n /**\n * Validate + normalize a raw path, then delegate trie insertion to the\n * extracted `addRoute` (design.md D2); flips `hasParamRoutes` from its return.\n */\n private addRoute(\n method: HttpMethod,\n path: string,\n entries: RouteEntry[],\n middleware: Middleware[] = [],\n recordIntrospection = true\n ): void {\n // Guard untyped-JS callers: a non-string path would coerce to a bogus literal route.\n const rawPath: unknown = path;\n if (typeof rawPath !== 'string') {\n throw new TypeError(\n `Route path must be a string, received ${rawPath === null ? 'null' : typeof rawPath}.`\n );\n }\n const normalized = normalizeRegistrationPath(path, this.opts.prefix, this.opts.strict);\n const depthBefore = this.state.maxDepth;\n if (addRouteImpl(method, normalized, entries, middleware, this.state, recordIntrospection)) {\n this.hasParamRoutes = true;\n }\n // Rebuild the pool only when maxDepth actually grew (F-02) — a cheap,\n // registration-time-only check; never runs per-request.\n if (this.state.maxDepth > depthBefore) {\n this.state.walkPool = createWalkPool(this.state.maxDepth);\n }\n }\n\n get(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('GET', path, entries);\n return this;\n }\n\n post(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('POST', path, entries);\n return this;\n }\n\n put(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('PUT', path, entries);\n return this;\n }\n\n delete(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('DELETE', path, entries);\n return this;\n }\n\n patch(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('PATCH', path, entries);\n return this;\n }\n\n head(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('HEAD', path, entries);\n return this;\n }\n\n options(path: string, ...entries: RouteEntry[]): this {\n this.addRoute('OPTIONS', path, entries);\n return this;\n }\n\n /**\n * Register a route for every HTTP method under one consolidated `isAnyMethod`\n * introspection row (T016) — matching is unchanged (see {@link pushAnyMethodDefinition}).\n */\n all(path: string, ...entries: RouteEntry[]): this {\n // recordIntrospection=false: insert each per-method handler without its own\n // introspection row; the single consolidated row below replaces all 7.\n for (const method of HTTP_METHODS) {\n this.addRoute(method, path, entries, [], false);\n }\n pushAnyMethodDefinition(\n this.routeDefinitions,\n normalizeRegistrationPath(path, this.opts.prefix, this.opts.strict)\n );\n return this;\n }\n\n route(method: HttpMethod, path: string, ...entries: RouteEntry[]): this {\n this.addRoute(method, path, entries);\n return this;\n }\n\n /**\n * Every registered route as a read-only list, for renderers (`@nextrush/openapi`,\n * SDK/RPC generators). Doc-generation-time only — never on the request path.\n */\n getRoutes(): readonly RouteDefinition[] {\n return this.routeDefinitions;\n }\n\n /**\n * Register a redirect from one path to another (301 by default). 307/308\n * additionally register POST/PUT/PATCH/DELETE to preserve the method.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#redirects | README: Redirects}\n */\n redirect(from: string, to: string, status: RedirectStatus = 301): this {\n registerRedirect(from, to, status, (method, path, entries) => {\n this.addRoute(method, path, entries);\n });\n return this;\n }\n\n use(pathOrMiddleware: string | Middleware | Router, routerOrUndefined?: Router): this {\n if (typeof pathOrMiddleware === 'function') {\n this.routerMiddleware.push(pathOrMiddleware);\n } else if (typeof pathOrMiddleware === 'string' && routerOrUndefined instanceof Router) {\n this.mountRouter(pathOrMiddleware, routerOrUndefined);\n } else if (typeof pathOrMiddleware === 'string') {\n throw new Error(\n `router.use('${pathOrMiddleware}', ...) requires a Router instance as the second argument. ` +\n 'Use router.group(prefix, callback) for prefix-scoped middleware, ' +\n 'or router.use(middlewareFn) to register middleware without a prefix.'\n );\n } else if (pathOrMiddleware instanceof Router) {\n this.mountRouter('', pathOrMiddleware);\n }\n return this;\n }\n\n /**\n * Mount a sub-router at a path prefix (Hono-style) — the explicit, more\n * semantic equivalent of `router.use(path, subRouter)`.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#sub-router-mounting | README: Sub-Router Mounting}\n */\n mount(path: string, router: Router): this {\n this.mountRouter(path, router);\n return this;\n }\n\n /** Mount a sub-router, carrying its own `routerMiddleware` onto every copied route. */\n private mountRouter(prefix: string, router: Router): void {\n copyRoutes(router.root, prefix, [], router.routerMiddleware, this.addRoute.bind(this));\n }\n\n /** Match a request to a route — delegates to {@link resolveMatch} (design.md D1). */\n match(method: HttpMethod, path: string): RouteMatch | null {\n return resolveMatch(this.state, this.hasParamRoutes, method, path);\n }\n\n /**\n * Test whether `path` falls under `prefix` using this router's OWN\n * canonicalization (case folding per `caseSensitive`, structural\n * normalization) — the mount-boundary counterpart to {@link match}, so a\n * router mounted via `Application.route()` is tested with the identical\n * rule it dispatches with (RFC-029, task 3.8). Implements the optional\n * `Routable.matchesMountPrefix` contract from `@nextrush/core`.\n *\n * @param path - The full request path being tested for this mount.\n * @param prefix - The normalized mount prefix (leading `/`, no trailing `/`).\n * @returns The path's remainder past the prefix (e.g. `/users` for a\n * `/ADMIN/Users` request mounted at `/admin`), or `undefined` when `path`\n * is not under `prefix` per this router's canonicalization.\n */\n matchesMountPrefix(path: string, prefix: string): string | undefined {\n const canonical = canonicalizePath(path, this.opts.caseSensitive, this.opts.strict);\n if (canonical.rejected) return undefined;\n\n // The prefix is fixed at registration time, so canonicalizing it on every\n // request is pure waste — it was ~150 ns of the ~557 ns each mounted router\n // added to dispatch. Memoized rather than precomputed at `route()` time so\n // the `Routable.matchesMountPrefix` contract still accepts any prefix\n // string from any caller. One entry is enough: a router is normally mounted\n // once, and a second prefix only costs a miss.\n let canonicalPrefix: string;\n if (this._prefixMemoRaw === prefix) {\n canonicalPrefix = this._prefixMemoCanonical;\n } else {\n canonicalPrefix = canonicalizePath(prefix, this.opts.caseSensitive, this.opts.strict).path;\n this._prefixMemoRaw = prefix;\n this._prefixMemoCanonical = canonicalPrefix;\n }\n\n const prefixLen = canonicalPrefix.length;\n if (!canonical.path.startsWith(canonicalPrefix)) return undefined;\n\n const hasCharAfterPrefix = prefixLen < canonical.path.length;\n if (hasCharAfterPrefix && canonical.path.charCodeAt(prefixLen) !== SLASH_CHAR_CODE) {\n return undefined;\n }\n\n return canonical.path.slice(prefixLen) || '/';\n }\n\n /**\n * Return the router's dispatch middleware — mount this on the application.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#routerroutes | README: router.routes()}\n */\n routes(): Middleware {\n // Seal router middleware into every executor once (audit RT-7 idempotency):\n // routes() may run more than once, so the _sealed guard prevents re-prepend.\n if (this.routerMiddleware.length > 0 && !this._sealed) {\n this._sealed = true;\n sealRouterMiddlewareImpl(this.root, this.staticRoutes, this.routerMiddleware);\n }\n // F-10: the internal closure calls `resolveMatch` directly with\n // `preNormalized: true` rather than going through the public `this.match()`\n // — this is the one call path where the caller (`createRoutesMiddleware`)\n // has already run `canonicalizePath()` on `path`, so `matchRoute`'s own\n // fold+collapse re-derivation is skippable. `Router.match()` itself is\n // untouched and still defaults to `false` for every other caller.\n return createRoutesMiddleware(\n (method, path) => resolveMatch(this.state, this.hasParamRoutes, method, path, true),\n this.opts.caseSensitive,\n this.opts.strict\n );\n }\n\n /**\n * Generate allowed-methods middleware. Responds to OPTIONS with an `Allow`\n * header and returns 405 for a known path hit with an unregistered method.\n */\n allowedMethods(): Middleware {\n return createAllowedMethodsMiddleware(this.root, this.opts.caseSensitive, this.opts.strict);\n }\n\n /**\n * Create a route group with a shared prefix and middleware. The callback\n * receives a {@link RouteGroup} to register routes against.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#route-groups | README: Route Groups}\n */\n group(\n prefix: string,\n middlewareOrCallback: Middleware[] | ((router: RouteGroup) => void),\n callback?: (router: RouteGroup) => void\n ): this {\n runRouteGroup(this, prefix, middlewareOrCallback, callback);\n return this;\n }\n\n /**\n * Remove all routes and middleware, resetting the router to its initial\n * state — for test isolation or hot-reload re-registration.\n */\n reset(): void {\n clearNode(this.root);\n this.staticRoutes.clear();\n this.routerMiddleware.length = 0;\n this.hasParamRoutes = false;\n // Clear the introspection registry too, or getRoutes()/OpenAPI would emit\n // ghost routes after a reset (audit RT-1).\n this.routeDefinitions.length = 0;\n // Reset the walk-frame pool sizing too (F-02) — otherwise maxDepth would\n // keep reporting a since-cleared route's depth, and the pool would stay\n // needlessly oversized for whatever gets registered next.\n this.state.maxDepth = 0;\n this.state.walkPool = undefined;\n this._sealed = false;\n }\n\n /** Register a route on behalf of a {@link RouteGroup} (group context). @internal */\n _addGroupRoute(\n method: HttpMethod,\n path: string,\n handlers: RouteHandler[],\n groupMiddleware: Middleware[],\n recordIntrospection = true\n ): void {\n this.addRoute(method, path, handlers, groupMiddleware, recordIntrospection);\n }\n\n /**\n * Group-facing entry point to {@link pushAnyMethodDefinition} — a group's `.all()`\n * records its consolidated row here since group routes live on the parent. @internal\n */\n _pushAnyMethodRouteDefinition(path: string): void {\n pushAnyMethodDefinition(\n this.routeDefinitions,\n normalizeRegistrationPath(path, this.opts.prefix, this.opts.strict)\n );\n }\n}\n\n/**\n * Create a new {@link Router} instance.\n * @see {@link https://github.com/0xTanzim/nextRush/blob/main/packages/router/README.md#quick-start | README: Quick Start}\n */\nexport function createRouter(options?: RouterOptions): Router {\n return new Router(options);\n}\n","/**\n * @nextrush/router - Segment Trie Node\n *\n * Internal segment trie implementation for high-performance route matching.\n * Segments are split by `/` and matched one trie level per segment for O(k)\n * lookups where k is path length.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { Context, HttpMethod, Middleware, RouteHandler } from '@nextrush/types';\n\n/**\n * Node type enumeration\n */\nexport const enum NodeType {\n /** Static path segment: /users */\n STATIC = 0,\n /** Named parameter: /:id */\n PARAM = 1,\n /** Wildcard: /* */\n WILDCARD = 2,\n}\n\n/**\n * Segment trie node\n */\nexport interface TrieNode {\n /** Path segment for this node */\n segment: string;\n /** Node type */\n type: NodeType;\n /** Static child nodes keyed by whole path segment (e.g. `users`), not the first character */\n children: Map<string, TrieNode>;\n /** Parameter name if this is a param node */\n paramName?: string;\n /** Handlers keyed by HTTP method */\n handlers: Map<HttpMethod, HandlerEntry>;\n /** Wildcard child if any */\n wildcardChild?: TrieNode;\n /** Parameter child if any */\n paramChild?: TrieNode;\n}\n\n/**\n * Handler entry with middleware and pre-compiled executor\n */\nexport interface HandlerEntry {\n handler: RouteHandler;\n middleware: Middleware[];\n /** Pre-compiled executor for fast dispatch (no closure per request) */\n executor?: (ctx: Context) => Promise<void>;\n /**\n * `true` only for a `HEAD` entry derived from a `GET` registration\n * (RFC 9110 §9.3.2). A derived entry is replaced by an explicit `HEAD`\n * registration instead of reporting a route conflict, is absent from\n * `getRoutes()`, and is skipped when copying routes into a parent router —\n * which re-derives it from the `GET` it copies.\n */\n autoHead: boolean;\n}\n\n/**\n * Method-nested static-route fast-path map (HP-9): the OUTER map selects an\n * inner map by HTTP method, the INNER map probes by the trailing-slash-\n * normalized path. This replaces the former `Map<\"METHOD path\", HandlerEntry>`\n * so a static lookup no longer builds a `` `${method} ${path}` `` key string per\n * request — it selects the inner map by `method` and probes by the raw path.\n */\nexport type StaticRouteMap = Map<HttpMethod, Map<string, HandlerEntry>>;\n\n/**\n * No-op next function - reusable, zero allocation\n * Caches the resolved Promise to avoid per-call allocation\n * @internal\n */\nconst RESOLVED_PROMISE = Promise.resolve();\nexport const NOOP_NEXT = (): Promise<void> => RESOLVED_PROMISE;\n\n/**\n * Compile an executor for a route handler with middleware\n * This creates the executor ONCE at registration time, not per-request\n * @internal\n */\nexport function compileExecutor(\n handler: RouteHandler,\n middleware: Middleware[]\n): (ctx: Context) => Promise<void> {\n const len = middleware.length;\n\n // FAST PATH: No middleware — direct handler call, no extra async frame (NF-1).\n if (len === 0) {\n return (ctx: Context): Promise<void> => {\n // `setNext(NOOP_NEXT)` is LOAD-BEARING, not redundant (NF-4a): it\n // terminates the middleware chain at the handler so a handler that calls\n // `ctx.next()` is a safe no-op and cannot leak into app-level middleware\n // mounted AFTER the router (the general `compose` dispatch wires ctx._next\n // to advance into that middleware before running the router).\n if (ctx.setNext) ctx.setNext(NOOP_NEXT);\n try {\n // `Promise.resolve(...)` — NOT `x instanceof Promise ? x : RESOLVED` — so\n // a non-Promise THENABLE return is adopted (its async work awaited), not\n // dropped: byte-identical to the former `await handler(...)`, minus the\n // async form's internal microtask hop. A native promise is returned as-is\n // (`Promise.resolve(p) === p`); a void return yields a resolved promise.\n return Promise.resolve(handler(ctx, NOOP_NEXT));\n } catch (err) {\n // Self-contained \"never throw synchronously\" contract, identical to the\n // len >= 1 branch: a sync throw becomes a rejection; a non-Error is wrapped.\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n };\n }\n\n // Middleware present: guarded recursive dispatch. This mirrors core `compose()`\n // so per-route middleware behave identically to application middleware:\n // - `ctx.next()` (modern) and the `(ctx, next)` argument advance the SAME chain\n // (ctx.setNext is wired before each layer),\n // - a synchronous throw is turned into a rejected promise (proper propagation),\n // - calling next() more than once in a layer rejects with a clear error.\n return (ctx: Context): Promise<void> => {\n let index = -1;\n\n const dispatch = (i: number): Promise<void> => {\n if (i <= index) {\n return Promise.reject(new Error('next() called multiple times'));\n }\n index = i;\n\n if (i < len) {\n const mw = middleware[i];\n if (mw === undefined) return Promise.reject(new Error('middleware length mismatch'));\n const next = (): Promise<void> => dispatch(i + 1);\n if (ctx.setNext) ctx.setNext(next);\n try {\n return Promise.resolve(mw(ctx, next));\n } catch (err) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n }\n\n // Final handler. A handler calling next() is a safe no-op.\n if (ctx.setNext) ctx.setNext(NOOP_NEXT);\n try {\n return Promise.resolve(handler(ctx, NOOP_NEXT));\n } catch (err) {\n return Promise.reject(err instanceof Error ? err : new Error(String(err)));\n }\n };\n\n return dispatch(0);\n };\n}\n\n/**\n * Create a new segment trie node\n */\nexport function createNode(segment: string, type: NodeType = NodeType.STATIC): TrieNode {\n return {\n segment,\n type,\n children: new Map(),\n handlers: new Map(),\n };\n}\n\n/**\n * Reset a trie node to its empty state — clears children/handlers and drops the\n * param/wildcard branches. Used by `Router.reset()` for test isolation and\n * hot-reload re-registration.\n */\nexport function clearNode(node: TrieNode): void {\n node.children.clear();\n node.handlers.clear();\n node.paramChild = undefined;\n node.wildcardChild = undefined;\n}\n\n/**\n * Parse path segments\n * Splits path into segments and identifies param/wildcard types\n *\n * @param path - Route path to parse\n * @param caseSensitive - If false, lowercase static segments for case-insensitive matching\n */\nexport function parseSegments(path: string, caseSensitive = true): ParsedSegment[] {\n const normalized = path.startsWith('/') ? path.slice(1) : path;\n if (normalized === '') return [];\n\n const parts = normalized.split('/');\n const segments: ParsedSegment[] = [];\n\n for (const part of parts) {\n if (part.startsWith(':')) {\n // Preserve the original parameter name case\n const paramName = part.slice(1);\n segments.push({\n segment: part, // Preserve original case — param nodes match any segment\n type: NodeType.PARAM,\n paramName, // Keep original case\n });\n } else if (part === '*') {\n segments.push({\n segment: '*',\n type: NodeType.WILDCARD,\n });\n break; // Wildcard must be last\n } else {\n segments.push({\n segment: caseSensitive ? part : part.toLowerCase(),\n type: NodeType.STATIC,\n });\n }\n }\n\n return segments;\n}\n\n/**\n * Parsed segment structure\n */\nexport interface ParsedSegment {\n segment: string;\n type: NodeType;\n paramName?: string;\n}\n","/**\n * @nextrush/router - Route Groups\n *\n * A route group applies a shared path prefix and middleware to a set of routes\n * registered inside a callback. Extracted from `router.ts` (audit RT-3) and\n * given a real public type (audit RT-6): `router.group()` callbacks receive a\n * {@link RouteGroup}, not a mis-cast `Router`.\n *\n * @packageDocumentation\n */\n\nimport { HTTP_METHODS, type HttpMethod, type Middleware, type RouteHandler } from '@nextrush/types';\nimport { createRedirectHandler, type RedirectStatus } from './redirect';\n\n/**\n * The subset of the parent router a group needs to register routes into.\n *\n * @remarks\n * Declared as an interface (rather than importing `Router`) so `group-router.ts`\n * and `router.ts` don't form an import cycle. `Router` satisfies it structurally\n * via its `_addGroupRoute` method.\n */\nexport interface GroupRouterHost {\n _addGroupRoute(\n method: HttpMethod,\n path: string,\n handlers: RouteHandler[],\n groupMiddleware: Middleware[],\n recordIntrospection?: boolean\n ): void;\n /** Record a single any-method introspection row (T016) — see `Router._pushAnyMethodRouteDefinition`. */\n _pushAnyMethodRouteDefinition(path: string): void;\n}\n\n/**\n * The object passed to a `router.group(prefix, callback)` callback.\n *\n * @remarks\n * Exposes only the route-registration surface that is valid inside a group —\n * intentionally NOT the full {@link Router} (no `mount`/`use`/`reset`), which is\n * why the previous `as unknown as Router` cast was a lie (audit RT-6).\n */\nexport interface RouteGroup {\n get(path: string, ...handlers: RouteHandler[]): this;\n post(path: string, ...handlers: RouteHandler[]): this;\n put(path: string, ...handlers: RouteHandler[]): this;\n delete(path: string, ...handlers: RouteHandler[]): this;\n patch(path: string, ...handlers: RouteHandler[]): this;\n head(path: string, ...handlers: RouteHandler[]): this;\n options(path: string, ...handlers: RouteHandler[]): this;\n all(path: string, ...handlers: RouteHandler[]): this;\n redirect(from: string, to: string, status?: RedirectStatus): this;\n group(\n prefix: string,\n middlewareOrCallback: Middleware[] | ((router: RouteGroup) => void),\n callback?: (router: RouteGroup) => void\n ): this;\n}\n\n/**\n * Collects routes under a shared prefix + middleware and forwards them to the\n * parent router. Implements {@link RouteGroup}.\n */\nexport class GroupRouter implements RouteGroup {\n private readonly parent: GroupRouterHost;\n private readonly prefix: string;\n private readonly middleware: Middleware[];\n\n constructor(parent: GroupRouterHost, prefix: string, middleware: Middleware[]) {\n this.parent = parent;\n this.prefix = prefix;\n this.middleware = middleware;\n }\n\n private fullPath(path: string): string {\n // Handle root path in group\n if (path === '/' || path === '') {\n return this.prefix;\n }\n // Combine prefix and path\n const cleanPrefix = this.prefix.endsWith('/') ? this.prefix.slice(0, -1) : this.prefix;\n const cleanPath = path.startsWith('/') ? path : '/' + path;\n return cleanPrefix + cleanPath;\n }\n\n get(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('GET', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n post(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('POST', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n put(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('PUT', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n delete(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('DELETE', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n patch(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('PATCH', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n head(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('HEAD', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n options(path: string, ...handlers: RouteHandler[]): this {\n this.parent._addGroupRoute('OPTIONS', this.fullPath(path), handlers, this.middleware);\n return this;\n }\n\n /**\n * Register a route matching every HTTP method under a single introspection\n * entry, mirroring `Router.all()` (T016) — a group's `.all()` must not\n * regress to the pre-T016 one-row-per-method shape just because\n * registration is routed through `_addGroupRoute` instead of `addRoute`\n * directly.\n */\n all(path: string, ...handlers: RouteHandler[]): this {\n for (const method of HTTP_METHODS) {\n this.parent._addGroupRoute(method, this.fullPath(path), handlers, this.middleware, false);\n }\n this.parent._pushAnyMethodRouteDefinition(this.fullPath(path));\n return this;\n }\n\n /**\n * Register a redirect within the group (uses the shared redirect handler,\n * audit RT-4 — no more naive replaceAll param substitution).\n */\n redirect(from: string, to: string, status: RedirectStatus = 301): this {\n const redirectHandler = createRedirectHandler(to, status);\n\n this.parent._addGroupRoute('GET', this.fullPath(from), [redirectHandler], this.middleware);\n this.parent._addGroupRoute('HEAD', this.fullPath(from), [redirectHandler], this.middleware);\n\n return this;\n }\n\n /**\n * Nested group support — combines this group's prefix + middleware with the\n * nested group's.\n */\n group(\n prefix: string,\n middlewareOrCallback: Middleware[] | ((router: RouteGroup) => void),\n callback?: (router: RouteGroup) => void\n ): this {\n runRouteGroup(\n this.parent,\n this.fullPath(prefix),\n middlewareOrCallback,\n callback,\n this.middleware\n );\n return this;\n }\n}\n\n/**\n * Resolve a `group()` call's `(middleware[], callback)` | `(callback)` overload\n * and run the callback against a fresh {@link GroupRouter} bound to `host`.\n *\n * @remarks\n * Shared by `Router.group()` and the nested `GroupRouter.group()` so the\n * overload handling and the \"throw when a middleware array has no callback\"\n * guard have a single definition.\n *\n * @param host - Parent router the group registers routes into.\n * @param prefix - Fully-resolved path prefix for the group.\n * @param middlewareOrCallback - Either the group middleware array or the callback.\n * @param callback - The callback, required when a middleware array is passed.\n * @param inheritedMiddleware - Middleware from an enclosing group, prepended\n * ahead of this group's own (empty for a top-level `Router.group()`).\n */\nexport function runRouteGroup(\n host: GroupRouterHost,\n prefix: string,\n middlewareOrCallback: Middleware[] | ((router: RouteGroup) => void),\n callback: ((router: RouteGroup) => void) | undefined,\n inheritedMiddleware: Middleware[] = []\n): void {\n let middleware: Middleware[] = [];\n let cb: (router: RouteGroup) => void;\n\n if (Array.isArray(middlewareOrCallback)) {\n middleware = middlewareOrCallback;\n if (!callback) {\n throw new Error('Callback function is required when providing middleware array');\n }\n cb = callback;\n } else {\n cb = middlewareOrCallback;\n }\n\n const combined =\n inheritedMiddleware.length > 0 ? [...inheritedMiddleware, ...middleware] : middleware;\n cb(new GroupRouter(host, prefix, combined));\n}\n","/**\n * @nextrush/router - Redirect Handler Compilation\n *\n * Single, shared redirect implementation used by both `Router.redirect()` and\n * route groups (audit RT-4). Previously the group variant used a naive\n * `replaceAll(':key', value)` that could mis-substitute overlapping param names\n * and re-scanned the string per request; this precompiles the target template\n * once and only substitutes route-style `:param` slots (a `:` at position 0 or\n * preceded by `/`), so `https://` and other literal colons are never touched.\n *\n * @packageDocumentation\n */\n\nimport type { Context, RouteHandler } from '@nextrush/types';\n\n/** HTTP redirect status codes supported by `redirect()`. */\nexport type RedirectStatus = 301 | 302 | 303 | 307 | 308;\n\n/**\n * Precompile a redirect target into alternating literal / param-name parts.\n *\n * @param to - Target path/URL, possibly containing `:param` placeholders.\n * @returns A parts array (`[literal, paramName, literal, …]`) when `to` has\n * route-style params, otherwise `undefined` (a static target).\n */\nexport function compileRedirectTarget(to: string): string[] | undefined {\n const parts: string[] = [];\n let pos = 0;\n let found = false;\n\n while (pos < to.length) {\n let idx = -1;\n for (let i = pos; i < to.length; i++) {\n if (to[i] === ':' && (i === 0 || to[i - 1] === '/') && i + 1 < to.length && to[i + 1] !== '/') {\n idx = i;\n break;\n }\n }\n if (idx === -1) break;\n\n found = true;\n parts.push(to.slice(pos, idx)); // literal before ':'\n const end = to.indexOf('/', idx + 1);\n if (end === -1) {\n parts.push(to.slice(idx + 1)); // param name (rest of string)\n pos = to.length;\n } else {\n parts.push(to.slice(idx + 1, end)); // param name\n pos = end;\n }\n }\n\n if (found) {\n parts.push(to.slice(pos)); // trailing literal\n return parts;\n }\n return undefined;\n}\n\n/**\n * Build the redirect route handler for a `to`/`status` pair. Substitutes any\n * `:param` placeholders from `ctx.params` using the precompiled template.\n *\n * @param to - Target path/URL.\n * @param status - Redirect status code.\n * @returns A {@link RouteHandler} that sets `Location` and the status.\n */\nexport function createRedirectHandler(to: string, status: RedirectStatus): RouteHandler {\n const compiledParts = compileRedirectTarget(to);\n\n return (ctx: Context) => {\n let targetPath: string;\n\n if (compiledParts) {\n const params = ctx.params;\n const head = compiledParts[0];\n if (head === undefined) {\n targetPath = to;\n } else {\n let result = head;\n for (let i = 1; i < compiledParts.length - 1; i += 2) {\n const paramKey = compiledParts[i];\n const tail = compiledParts[i + 1];\n if (paramKey === undefined || tail === undefined) break;\n result += (params[paramKey] ?? '') + tail;\n }\n targetPath = result;\n }\n } else {\n targetPath = to;\n }\n\n ctx.status = status;\n ctx.set('Location', targetPath);\n ctx.body = '';\n };\n}\n","/**\n * @nextrush/router - Shared internal constants\n *\n * A leaf module that imports nothing, so any router module can depend on it\n * without risking an import cycle. This is the resolution of the former\n * `EMPTY_PARAMS` duplication: the constant was previously copied across modules\n * to dodge a `router.ts` <-> `match-route.ts` cycle, which a dependency-free\n * leaf module makes impossible by construction.\n *\n * @packageDocumentation\n * @internal\n */\n\n/**\n * Prototype for every per-request key/value bag the router hands to\n * application code (`ctx.params`).\n *\n * Built once at module load, so instances are created with\n * `Object.create(NULL_PROTO)` rather than `Object.create(null)`. Both satisfy\n * the security requirement — `Object.prototype` stays unreachable, so a param\n * named `__proto__`/`constructor`/`prototype` binds as an own key and no\n * inherited member is visible — but `Object.create(null)` additionally puts the\n * object into V8 DICTIONARY mode, where property loads cannot be inline-cached.\n * That cost is paid by every `ctx.params.id` read in every handler, forever.\n * Deriving from a null-prototype object instead keeps FAST properties.\n *\n * @see docs/adr/ADR-0021-fast-property-request-containers.md\n */\nexport const NULL_PROTO: object = Object.create(null) as object;\n\n/**\n * Frozen empty params object for matches with no path parameters (static-map\n * hits and successful walks that bound no `:param`/`*`).\n *\n * Shared as a single instance to avoid allocating a fresh object per request on\n * the hot path. Frozen so the shared instance can never be mutated by a\n * handler. Built on {@link NULL_PROTO} because it is *read* on every\n * static-route request — a dictionary-mode miss-read (`ctx.params.id` on a\n * route with no params) measured 2.2x slower than a fast-property one.\n */\nexport const EMPTY_PARAMS: Record<string, string> = Object.freeze(\n Object.create(NULL_PROTO) as Record<string, string>\n);\n","/**\n * @nextrush/router - Reused walk-frame pool (F-02, `reduce-router-match-allocations`)\n *\n * Split out of `matching.ts` once the pool implementation pushed it over the\n * 300-line file cap — same reasoning `match-route.ts`'s own extraction used.\n * Holds the pooled scratch structure and its indexed-cursor variant of the\n * tree walk; `matching.ts`'s `matchNodeIndexed` delegates here when a pool is\n * supplied and keeps its own fresh-allocation behavior otherwise.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { HttpMethod } from '@nextrush/types';\nimport type { HandlerEntry, TrieNode } from './segment-trie';\nimport { decodeParam, segmentAt } from './matching';\n\n/**\n * One node in the iterative walk's explicit stack. `stage` is a small state\n * machine (0 = extract + try static, 1 = try param, 2 = try wildcard/backtrack)\n * so a single frame can be revisited on backtrack without recursion. `bound`\n * records whether this frame pushed a deferred param binding, so backtracking\n * can pop it without an object-property delete.\n */\nexport interface WalkFrame {\n node: TrieNode;\n pos: number;\n stage: 0 | 1 | 2;\n seg: string;\n next: number;\n bound: boolean;\n}\n\n/**\n * Per-router-instance reused scratch space for the tree walk — the\n * `WalkFrame[]` stack and the `bindNames`/`bindValues` binding arrays,\n * pre-sized to the deepest currently registered route\n * (`RegistrationState.maxDepth`) instead of allocated fresh on every\n * `matchNodeIndexed` call.\n *\n * Safe under the router's synchronous-walk invariant only: a request path\n * can never make the walk descend past `maxDepth` (a mismatched segment\n * backtracks — the depth cursor decrements — rather than growing past the\n * pool), and the walk never awaits mid-frame, so no two in-flight matches on\n * the same router instance can observe each other's frames. See the\n * `router` capability's \"Reused internal walk state is never shared across\n * concurrent in-flight matches\" requirement.\n */\nexport interface WalkPool {\n frames: WalkFrame[];\n bindNames: string[];\n bindValues: string[];\n}\n\n/**\n * Build a `WalkPool` sized to hold `maxDepth` matched segments. The walk's\n * frame at index 0 always represents the trie ROOT itself (mirroring the\n * unpooled walk's `stack[0] = { node: root, ... }`), so a route with\n * `maxDepth` segments needs `maxDepth + 1` frames — one for the root plus one\n * per descended segment. Called once when a router's `maxDepth` grows\n * (registration time), never per-request.\n */\nexport function createWalkPool(maxDepth: number): WalkPool {\n const frames: WalkFrame[] = [];\n for (let i = 0; i < maxDepth + 1; i++) {\n frames.push({\n node: undefined as unknown as TrieNode,\n pos: 0,\n stage: 0,\n seg: '',\n next: 0,\n bound: false,\n });\n }\n return { frames, bindNames: [], bindValues: [] };\n}\n\n/**\n * Pooled variant of `matchNodeIndexed` — same stage-machine, same precedence,\n * same backtrack semantics, byte-identical results — but indexes into\n * `pool.frames` by depth (mutating each pre-allocated frame in place) instead\n * of `push`/`pop`-ing fresh frame objects onto a fresh array. `depth` is a\n * local cursor, never shared across calls; only the underlying frame OBJECTS\n * are reused. Safe only because the walk never awaits mid-frame and a\n * mismatched segment decrements `depth` (backtrack) rather than growing past\n * `pool.frames.length` (bounded by the router's `maxDepth` at registration\n * time — see {@link createWalkPool}).\n */\nexport function matchNodeIndexedPooled(\n root: TrieNode,\n path: string,\n startPos: number,\n bindNames: string[],\n bindValues: string[],\n method: HttpMethod,\n decode: boolean,\n pool: WalkPool,\n originalPath?: string\n): HandlerEntry | null {\n const { frames } = pool;\n let depth = 0;\n const first = frames[0];\n if (first === undefined) return null; // maxDepth 0 — no param routes registered, nothing to walk\n first.node = root;\n first.pos = startPos;\n first.stage = 0;\n first.seg = '';\n first.next = 0;\n first.bound = false;\n\n while (depth >= 0) {\n const frame = frames[depth];\n if (frame === undefined) break;\n\n if (frame.stage === 0) {\n if (frame.pos >= path.length) {\n const handler = frame.node.handlers.get(method);\n if (handler) return handler;\n depth--;\n continue;\n }\n const slashPos = path.indexOf('/', frame.pos);\n if (slashPos === -1) {\n frame.seg = path.slice(frame.pos);\n frame.next = path.length;\n } else {\n frame.seg = path.slice(frame.pos, slashPos);\n frame.next = slashPos + 1;\n }\n if (frame.seg === '') {\n const handler = frame.node.handlers.get(method);\n if (handler) return handler;\n depth--;\n continue;\n }\n frame.stage = 1;\n const staticChild = frame.node.children.get(frame.seg);\n if (staticChild) {\n depth++;\n const next = frames[depth];\n if (next === undefined) {\n // Should be unreachable — pool sized to maxDepth — but fail closed\n // (treat as a miss) rather than write past the pooled array.\n depth--;\n continue;\n }\n next.node = staticChild;\n next.pos = frame.next;\n next.stage = 0;\n next.seg = '';\n next.next = 0;\n next.bound = false;\n }\n continue;\n }\n\n if (frame.stage === 1) {\n frame.stage = 2;\n const paramChild = frame.node.paramChild;\n if (paramChild) {\n const paramName = paramChild.paramName;\n if (paramName === undefined) return null;\n const value =\n originalPath !== undefined\n ? decodeParam(segmentAt(originalPath, frame.pos), decode)\n : decodeParam(frame.seg, decode);\n bindNames.push(paramName);\n bindValues.push(value);\n frame.bound = true;\n depth++;\n const next = frames[depth];\n if (next === undefined) {\n bindNames.pop();\n bindValues.pop();\n frame.bound = false;\n depth--;\n continue;\n }\n next.node = paramChild;\n next.pos = frame.next;\n next.stage = 0;\n next.seg = '';\n next.next = 0;\n next.bound = false;\n }\n continue;\n }\n\n if (frame.bound) {\n bindNames.pop();\n bindValues.pop();\n frame.bound = false;\n }\n const wildcardChild = frame.node.wildcardChild;\n if (wildcardChild) {\n const src = originalPath ?? path;\n bindNames.push('*');\n bindValues.push(decodeParam(src.slice(frame.pos), decode));\n const handler = wildcardChild.handlers.get(method);\n if (handler) return handler;\n bindNames.pop();\n bindValues.pop();\n }\n depth--;\n }\n\n return null;\n}\n","/**\n * @nextrush/router - Route Matching Engine\n *\n * The segment-trie lookup logic extracted from the `Router` class (T014,\n * design.md D1). These are standalone pure functions taking the trie root and\n * other needed state as explicit parameters — not methods — so the matching\n * engine has no implicit dependency on broader `Router` instance state beyond\n * what's passed in. `Router` becomes a thin delegating shell around these.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { HttpMethod } from '@nextrush/types';\nimport type { HandlerEntry, TrieNode } from './segment-trie';\nimport { matchNodeIndexedPooled, type WalkFrame, type WalkPool } from './walk-pool';\n\nexport type { WalkFrame, WalkPool } from './walk-pool';\nexport { createWalkPool } from './walk-pool';\n\n/**\n * Percent-decode an extracted param/wildcard value when `decode` is enabled.\n * Fast-paths values with no `%`, and falls back to the raw value on malformed\n * encoding (decodeURIComponent throws a URIError) so a bad request never crashes\n * routing.\n */\nexport function decodeParam(value: string, decode: boolean): string {\n if (!decode || !value.includes('%')) return value;\n try {\n return decodeURIComponent(value);\n } catch {\n return value;\n }\n}\n\n/**\n * Return the path segment starting at `start`, up to the next `/` (or end).\n * Scalar — no tuple array (the iterative walk tracks the next position itself).\n * Used to recover an original-case param value from the original-case path at a\n * position already validated against the (folded) lookup path, and by the\n * {@link findNode} allowed-methods walk to scan segments off the lookup path.\n */\nexport function segmentAt(path: string, start: number): string {\n const slashPos = path.indexOf('/', start);\n return slashPos === -1 ? path.slice(start) : path.slice(start, slashPos);\n}\n\n/**\n * True only when `path.toLowerCase()` is provably a no-op: every character is\n * ASCII (`<= 0x7F`) and none is an ASCII uppercase letter (`A`–`Z`). Any\n * non-ASCII byte is treated as UNCERTAIN (it could be uppercase like `Ü`), so\n * this returns `false` and the caller folds — never skipping unicode case\n * folding (design.md D3). This lets `normalizePathForMatch` skip the\n * `toLowerCase()` allocation for the overwhelmingly common all-lowercase-ASCII\n * request path while staying byte-identical to always folding.\n */\nexport function isProvablyLowerAscii(path: string): boolean {\n for (let i = 0; i < path.length; i++) {\n const c = path.charCodeAt(i);\n if ((c >= 0x41 && c <= 0x5a) || c > 0x7f) return false;\n }\n return true;\n}\n\n/**\n * Structural match-time normalization WITHOUT case folding: collapse repeated\n * slashes and strip a single trailing slash (unless `strict`). Split out of\n * {@link normalizePathForMatch} so the case-fold decision and the structural\n * pass are separable — `matchRoute` reuses this to build the original-case path\n * only when a fold actually happened (HP-12), rather than running the full\n * normalize twice.\n */\nexport function collapseAndStrip(path: string, strict: boolean): string {\n let normalized = path;\n\n // Fast-path: skip the regex when there are no double slashes (99%+ of requests).\n if (normalized.includes('//')) {\n normalized = normalized.replace(/\\/+/g, '/');\n }\n\n // Non-strict mode treats a trailing slash as insignificant; strict keeps it.\n if (!strict && normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n\n return normalized;\n}\n\n/**\n * Normalize a request path for segment-trie matching: fold case (unless\n * `caseSensitive`), collapse repeated slashes, and strip a single trailing\n * slash (unless `strict`). This is the one definition of the match-time\n * normalization rules, shared by `matchRoute` (in `match-route.ts`) and\n * {@link findAllowedMethods}.\n *\n * The `toLowerCase()` call is skipped when the path is provably case-stable\n * ({@link isProvablyLowerAscii}) — byte-identical to always folding, but with\n * no throwaway string on the common all-lowercase-ASCII path (HP-12 / D3).\n *\n * Query-string removal is intentionally NOT done here — it is caller-specific:\n * `matchRoute` strips the query before calling, while `findAllowedMethods`\n * receives an already query-free `ctx.path`. Pass `caseSensitive: true` to\n * normalize while preserving case (used by `matchRoute` to build the\n * original-case path from which param values are extracted).\n *\n * NOTE: registration-time normalization (`Router.normalizePath`) is a separate\n * concern — it joins the router prefix, guarantees a leading slash, and never\n * folds case — so it deliberately does not share this helper.\n */\nexport function normalizePathForMatch(\n path: string,\n caseSensitive: boolean,\n strict: boolean\n): string {\n const folded = caseSensitive || isProvablyLowerAscii(path) ? path : path.toLowerCase();\n return collapseAndStrip(folded, strict);\n}\n\n/**\n * One node in the iterative walk's explicit stack — see the full doc comment\n * on {@link WalkFrame} in `walk-pool.ts` (re-exported here for callers that\n * only import from `matching.ts`).\n */\n\n/**\n * Iterative, index-based segment-trie match (HP-11 / HP-13, design.md D4).\n *\n * Walks the trie with an EXPLICIT stack instead of recursion, so a pathological\n * segment count cannot overflow the call stack (DoS safety) — behavior is\n * otherwise byte-identical to the former recursive matcher: precedence is\n * static > param > wildcard at each node, a partially-matching branch backtracks\n * cleanly, and the first accepted terminal wins.\n *\n * Param/wildcard bindings are DEFERRED onto the caller-owned `bindNames` /\n * `bindValues` stacks and popped on backtrack, so params are materialized ONCE\n * on the accepted terminal by the caller (no eager bind + backtrack\n * `Reflect.deleteProperty`, and no `Object.keys` post-loop — the caller reads\n * the bind count). `decode` runs strictly on the already-split segment/remainder\n * (design.md D9), so an encoded slash/dot decodes into the value only and can\n * never create new path segments.\n *\n * `originalPath` (present only when case-folding actually occurred) supplies the\n * original-case param value at the same position as the folded lookup path.\n */\nexport function matchNodeIndexed(\n root: TrieNode,\n path: string,\n startPos: number,\n bindNames: string[],\n bindValues: string[],\n method: HttpMethod,\n decode: boolean,\n originalPath?: string,\n /**\n * When provided, the walk indexes into `pool.frames` by depth (reusing\n * each pre-allocated frame object in place) instead of `push`/`pop`-ing\n * fresh frame literals onto a fresh array — see `walk-pool.ts`'s\n * `matchNodeIndexedPooled`. Omitted, the walk behaves exactly as before (a\n * fresh `stack: WalkFrame[]`) — every other caller of `matchNodeIndexed`\n * keeps its current behavior unchanged.\n */\n pool?: WalkPool\n): HandlerEntry | null {\n if (pool) {\n return matchNodeIndexedPooled(root, path, startPos, bindNames, bindValues, method, decode, pool, originalPath);\n }\n const stack: WalkFrame[] = [\n { node: root, pos: startPos, stage: 0, seg: '', next: 0, bound: false },\n ];\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1];\n if (frame === undefined) break;\n\n // Stage 0 — first visit: terminal checks, then try the static child.\n if (frame.stage === 0) {\n if (frame.pos >= path.length) {\n const handler = frame.node.handlers.get(method);\n if (handler) return handler; // accepted — bind stacks hold this path's params\n stack.pop();\n continue;\n }\n const slashPos = path.indexOf('/', frame.pos);\n if (slashPos === -1) {\n frame.seg = path.slice(frame.pos);\n frame.next = path.length;\n } else {\n frame.seg = path.slice(frame.pos, slashPos);\n frame.next = slashPos + 1;\n }\n if (frame.seg === '') {\n const handler = frame.node.handlers.get(method);\n if (handler) return handler;\n stack.pop();\n continue;\n }\n frame.stage = 1;\n const staticChild = frame.node.children.get(frame.seg);\n if (staticChild) {\n stack.push({ node: staticChild, pos: frame.next, stage: 0, seg: '', next: 0, bound: false });\n }\n continue;\n }\n\n // Stage 1 — static child (if any) has failed: try the param child, deferring\n // its binding onto the shared stacks.\n if (frame.stage === 1) {\n frame.stage = 2;\n const paramChild = frame.node.paramChild;\n if (paramChild) {\n const paramName = paramChild.paramName;\n if (paramName === undefined) return null; // degenerate param node → whole walk fails (as before)\n const value =\n originalPath !== undefined\n ? decodeParam(segmentAt(originalPath, frame.pos), decode)\n : decodeParam(frame.seg, decode);\n bindNames.push(paramName);\n bindValues.push(value);\n frame.bound = true;\n stack.push({ node: paramChild, pos: frame.next, stage: 0, seg: '', next: 0, bound: false });\n }\n continue;\n }\n\n // Stage 2 — param branch (if taken) failed: undo its deferred bind, then try\n // the wildcard child (a terminal — it captures the original-case remainder).\n if (frame.bound) {\n bindNames.pop();\n bindValues.pop();\n frame.bound = false;\n }\n const wildcardChild = frame.node.wildcardChild;\n if (wildcardChild) {\n const src = originalPath ?? path;\n bindNames.push('*');\n bindValues.push(decodeParam(src.slice(frame.pos), decode));\n const handler = wildcardChild.handlers.get(method);\n if (handler) return handler;\n bindNames.pop();\n bindValues.pop();\n }\n stack.pop();\n }\n\n return null;\n}\n","/**\n * @nextrush/router - Top-Level Route Match Orchestration\n *\n * `matchRoute` was originally part of `matching.ts` (the lookup-primitives\n * module: `decodeParam`/`extractSegment`/`findNode`/`findAllowedMethods`/\n * `matchNodeIndexed`), but `matching.ts` was approaching the 300-line ceiling\n * itself once `matchRoute` moved in — this file separates the top-level\n * \"match a request\" orchestration (query-strip, normalize, static fast-path,\n * tree-walk delegation, param post-processing) from the lower-level lookup\n * primitives it calls into.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { HttpMethod, Middleware, RouteMatch } from '@nextrush/types';\nimport { EMPTY_PARAMS, NULL_PROTO } from './constants';\nimport { matchNodeIndexed, collapseAndStrip, isProvablyLowerAscii } from './matching';\nimport type { WalkPool } from './walk-pool';\nimport type { StaticRouteMap, TrieNode } from './segment-trie';\n\n/**\n * Match a route and return the full {@link RouteMatch} in a SINGLE allocation\n * (design.md D1 / HP-10).\n *\n * `matchRoute` builds the final `RouteMatch` — including `middleware:\n * routerMiddleware` — directly at each return site, so `Router.match()` gets\n * one object per matched request instead of a bare result later re-wrapped by\n * `resolveMatch`. `routerMiddleware` is threaded in as a parameter (its\n * contents are never read here — only attached by reference), preserving the\n * \"no implicit `Router` state\" property of the earlier extraction while\n * removing the duplicate wrapper object.\n *\n * Every other piece of `Router` state it reads (`root`, `staticRoutes`,\n * `hasParamRoutes`, the option flags) is read-only here (no mutation, unlike\n * `addRoute` in `registration.ts`), so it is threaded as plain parameters.\n */\nexport function matchRoute(\n method: HttpMethod,\n rawPath: string,\n root: TrieNode,\n staticRoutes: StaticRouteMap,\n hasParamRoutes: boolean,\n caseSensitive: boolean,\n strict: boolean,\n decode: boolean,\n routerMiddleware: Middleware[],\n /**\n * When `true`, `rawPath` is trusted as already the output of\n * `canonicalizePath()` (same `caseSensitive`/`strict` options) — the fold\n * and structural-collapse steps below are skipped entirely rather than\n * re-derived. Only valid when the caller has actually run\n * `canonicalizePath()` on this exact input first (F-10); the router's own\n * `routes()` dispatch path is the only caller that does. Defaults to\n * `false`, preserving every other caller's existing behavior — including\n * `Router.match()`, which never canonicalizes first.\n */\n preNormalized = false,\n /**\n * When supplied, the param walk reuses this router instance's pooled\n * `WalkFrame[]`/binding-array scratch space instead of allocating fresh\n * arrays on this call (F-02, `reduce-router-match-allocations`). Omitted,\n * `matchRoute` behaves exactly as before.\n */\n walkPool?: WalkPool\n): RouteMatch | null {\n let path = rawPath;\n // Query string must not affect path matching (RFC 3986 §3.4). Strip it here,\n // before normalization, so both the lookup path and extracted param values\n // exclude it. This strip is caller-specific: `findAllowedMethods` receives an\n // already query-free `ctx.path`, so the shared `normalizePathForMatch` does\n // not strip — only `matchRoute` does.\n if (!preNormalized) {\n const queryIdx = path.indexOf('?');\n if (queryIdx !== -1) path = path.slice(0, queryIdx);\n }\n\n // HP-12: decide case-stability ONCE. When the path is provably case-stable\n // (case-sensitive router, or an all-lowercase-ASCII path), folding is a no-op\n // and the original-case path equals the normalized one — so we skip both the\n // `toLowerCase()` allocation and the second original-case normalize pass.\n //\n // F-10: when `preNormalized` is true, the caller already ran this exact\n // fold+collapse via `canonicalizePath()` — trust `path` as both the\n // normalized AND original-case string, skipping the fold+collapse\n // re-derivation entirely rather than repeating work already done upstream.\n let normalized: string;\n let caseStable: boolean;\n if (preNormalized) {\n normalized = path;\n caseStable = true;\n } else {\n caseStable = caseSensitive || isProvablyLowerAscii(path);\n const folded = caseStable ? path : path.toLowerCase();\n normalized = collapseAndStrip(folded, strict);\n }\n\n // FAST PATH: O(1) static route lookup (no tree traversal). Method-nested map\n // (HP-9): select the inner map by method, then probe by the normalized path —\n // no per-request `${method} ${path}` key-string allocation. For static routes\n // a trailing slash is irrelevant, so always strip it for the probe.\n const methodMap = staticRoutes.get(method);\n if (methodMap) {\n const staticKey =\n normalized.length > 1 && normalized.endsWith('/') ? normalized.slice(0, -1) : normalized;\n const staticEntry = methodMap.get(staticKey);\n if (staticEntry) {\n return {\n handler: staticEntry.handler,\n params: EMPTY_PARAMS,\n middleware: routerMiddleware,\n executor: staticEntry.executor,\n };\n }\n }\n\n // Only walk tree if we have param/wildcard routes\n if (!hasParamRoutes) return null;\n\n // Original-case (query-stripped) path so extracted param values keep their\n // casing while lookup uses the lowercased `normalized`. Needed ONLY when a\n // fold actually happened (HP-12): when case-stable, `normalized` already IS\n // the original-case structure, so the walk extracts from it and no second\n // normalize pass runs.\n const originalPath = caseStable ? undefined : collapseAndStrip(path, strict);\n\n // Deferred param binding (HP-11): the walk records the accepted path's\n // `:param`/`*` bindings onto these parallel stacks (pushed on descent, popped\n // on backtrack) so params are materialized ONCE here on the accepted terminal\n // — no eager bind + backtrack `Reflect.deleteProperty`. Reused from the\n // router's pool when supplied (F-02) instead of allocated fresh.\n const bindNames: string[] = walkPool ? walkPool.bindNames : [];\n const bindValues: string[] = walkPool ? walkPool.bindValues : [];\n // A pooled bindNames/bindValues array persists across calls — clear any\n // leftover entries from a prior match before this walk starts (the walk\n // itself pops everything it pushes on a clean miss or match, but a defensive\n // clear here costs nothing on the common empty case and closes any future\n // edit that might leave a stale entry behind).\n if (walkPool) {\n bindNames.length = 0;\n bindValues.length = 0;\n }\n\n const entry = matchNodeIndexed(\n root,\n normalized,\n 1, // Start after leading '/'\n bindNames,\n bindValues,\n method,\n decode,\n originalPath,\n walkPool\n );\n if (!entry) return null;\n\n // Materialize params once on a bag whose prototype chain excludes\n // `Object.prototype` (design.md D8): a param named\n // `__proto__`/`constructor`/`prototype` binds as an OWN key with no prototype\n // mutation, and no inherited member is visible on `ctx.params`. Derived from\n // `NULL_PROTO` rather than `Object.create(null)` so the object keeps V8 fast\n // properties and handler reads stay inline-cacheable. The bind count replaces\n // the former `Object.keys` post-loop (HP-13); zero binds returns the shared\n // frozen `EMPTY_PARAMS`.\n const count = bindNames.length;\n let params: Record<string, string>;\n if (count === 0) {\n params = EMPTY_PARAMS;\n } else {\n params = Object.create(NULL_PROTO) as Record<string, string>;\n for (let i = 0; i < count; i++) {\n const name = bindNames[i];\n const value = bindValues[i];\n if (name !== undefined && value !== undefined) params[name] = value;\n }\n }\n\n return {\n handler: entry.handler,\n params,\n middleware: routerMiddleware,\n executor: entry.executor,\n };\n}\n\n/**\n * Stable router state `resolveMatch` reads on every request. All fields are\n * fixed references for the router's lifetime, so the caller memoizes this once\n * rather than rebuilding it per request.\n *\n * `walkPool` is the one field that is itself mutable (its contents, not the\n * reference) — the reused `WalkFrame[]`/binding-array scratch space (F-02,\n * `reduce-router-match-allocations`). It is `undefined` until the router has\n * at least one param/wildcard route (no pool needed for a static-only router,\n * per `createWalkPool(0)` never being called) and is rebuilt whenever the\n * router's `maxDepth` grows past what the current pool was sized for — see\n * `Router`'s own wiring, not this interface, for when that rebuild happens.\n */\nexport interface MatchState {\n readonly root: TrieNode;\n readonly staticRoutes: StaticRouteMap;\n readonly caseSensitive: boolean;\n readonly strict: boolean;\n readonly decode: boolean;\n readonly routerMiddleware: Middleware[];\n walkPool?: WalkPool;\n}\n\n/**\n * Resolve a request to a full {@link RouteMatch}. Thin delegator to\n * {@link matchRoute}, which now builds the final `RouteMatch` (incl.\n * `state.routerMiddleware`) in one allocation — so this no longer wraps a\n * result in a second object (HP-10). `Router.match()` is a one-line delegator\n * to this. `hasParamRoutes` is passed separately from `state` because it is\n * the one piece of router state that flips after construction.\n */\nexport function resolveMatch(\n state: MatchState,\n hasParamRoutes: boolean,\n method: HttpMethod,\n path: string,\n /**\n * Forwarded to {@link matchRoute} — see its own doc comment for the\n * caller contract. Defaults to `false`.\n */\n preNormalized = false\n): RouteMatch | null {\n return matchRoute(\n method,\n path,\n state.root,\n state.staticRoutes,\n hasParamRoutes,\n state.caseSensitive,\n state.strict,\n state.decode,\n state.routerMiddleware,\n preNormalized,\n state.walkPool\n );\n}\n","/**\n * @nextrush/router - Router Composition\n *\n * Sub-router mounting/copying logic extracted from the `Router` class (T014,\n * design.md D3 — extracted after the matching-engine split left `router.ts`\n * still over the 300-line ceiling).\n *\n * `copyRoutes`'s tree walk is structurally pure (segments/prefix/middleware are\n * all explicit parameters), but its side effect — registering the copied route —\n * needs `Router.addRoute`, a private method. Rather than reach into `Router`\n * internals or promote `addRoute` to `public` (a public-API change out of scope\n * for this refactor), the effect is injected explicitly as an `addRoute`\n * callback parameter. `use`/`mount`/`mountRouter` stay on `Router` itself:\n * they return `this` for fluent chaining and read `Router` instance state\n * (`root`, `routerMiddleware`) that isn't naturally expressible as function\n * parameters without more ceremony than the win justifies.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { HttpMethod, Middleware, RouteHandler } from '@nextrush/types';\nimport type { TrieNode } from './segment-trie';\n\n/** Callback signature matching `Router['addRoute']` — injected, not imported. */\nexport type AddRouteFn = (\n method: HttpMethod,\n path: string,\n handlers: RouteHandler[],\n middleware: Middleware[]\n) => void;\n\n/**\n * Recursively copy routes from one router's trie into another via the\n * supplied `addRoute` callback.\n */\nexport function copyRoutes(\n node: TrieNode,\n prefix: string,\n segments: string[],\n subRouterMiddleware: Middleware[],\n addRoute: AddRouteFn\n): void {\n // Copy handlers at this node\n for (const [method, entry] of node.handlers) {\n // A derived HEAD entry is not copied — the parent re-derives it from the\n // GET registration below, which keeps the parent's own explicit-HEAD\n // precedence and introspection rows correct.\n if (entry.autoHead) continue;\n const path = prefix + '/' + segments.join('/');\n // Prepend sub-router middleware so it runs before the route's own middleware\n const combined =\n subRouterMiddleware.length > 0\n ? [...subRouterMiddleware, ...entry.middleware]\n : entry.middleware;\n addRoute(method, path || '/', [entry.handler], combined);\n }\n\n // Copy static children\n for (const [, child] of node.children) {\n copyRoutes(child, prefix, [...segments, child.segment], subRouterMiddleware, addRoute);\n }\n\n // Copy param child\n if (node.paramChild) {\n copyRoutes(\n node.paramChild,\n prefix,\n [...segments, node.paramChild.segment],\n subRouterMiddleware,\n addRoute\n );\n }\n\n // Copy wildcard child\n if (node.wildcardChild) {\n copyRoutes(node.wildcardChild, prefix, [...segments, '*'], subRouterMiddleware, addRoute);\n }\n}\n","/**\n * @nextrush/router - Middleware Adaptation\n *\n * Router-level-middleware sealing logic extracted from the `Router` class\n * (T014, design.md D3 — extracted after the matching-engine and composition\n * splits left `router.ts` still over the 300-line ceiling).\n *\n * The tree-walk that re-compiles every handler's executor is structurally\n * pure (it only mutates the `TrieNode`/`HandlerEntry` structures passed in,\n * same shape as `copyRoutes` in `composition.ts`) — it doesn't touch\n * `Router`-only state like `_sealed`, so it extracts cleanly. `routes()` and\n * `allowedMethods()` stay on `Router`: both return closures that capture\n * `this` (for `this.match`, `this.opts`, `this.root`) and are the router's\n * actual public middleware-producing API, not internal tree mechanics.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport { compileExecutor, type StaticRouteMap, type TrieNode } from './segment-trie';\nimport type { Middleware } from '@nextrush/types';\n\n/**\n * Re-compile every route executor in the trie (plus the static-route hash\n * map) to include router-level middleware ahead of each route's own\n * middleware. Idempotency (guarding against a double seal) is the caller's\n * responsibility — this function always re-walks and re-compiles.\n */\nexport function sealRouterMiddleware(\n root: TrieNode,\n staticRoutes: StaticRouteMap,\n routerMiddleware: Middleware[]\n): void {\n const routerMw = [...routerMiddleware];\n\n const walk = (node: TrieNode): void => {\n for (const [method, entry] of node.handlers) {\n const combinedMw = [...routerMw, ...entry.middleware];\n entry.executor = compileExecutor(entry.handler, combinedMw);\n node.handlers.set(method, entry);\n }\n for (const [, child] of node.children) {\n walk(child);\n }\n if (node.paramChild) walk(node.paramChild);\n if (node.wildcardChild) walk(node.wildcardChild);\n };\n\n walk(root);\n\n // Also update static route entries (method-nested map, HP-9).\n for (const [, methodMap] of staticRoutes) {\n for (const [key, entry] of methodMap) {\n const combinedMw = [...routerMw, ...entry.middleware];\n entry.executor = compileExecutor(entry.handler, combinedMw);\n methodMap.set(key, entry);\n }\n }\n}\n","/**\n * @nextrush/router - Route Registration\n *\n * Trie-insertion logic extracted from the `Router` class (T014, design.md D2 —\n * D2 keeps HTTP-verb shortcuts (`get`/`post`/etc.) on `Router` itself, but\n * explicitly permits extracting their shared internal, `addRoute`, \"if\n * `addRoute` itself is large enough to warrant it.\" At 116 lines and the\n * highest cyclomatic/cognitive complexity in the file, it is.\n *\n * `addRoute` touches five pieces of `Router` instance state (the trie root,\n * `caseSensitive`, the `hasParamRoutes` flag, the static-route hash map, and\n * the introspection registry) as both reads and writes. All five are threaded\n * explicitly rather than read/written through an implicit `this`, so this\n * function has no hidden dependency on `Router` beyond what's passed in —\n * same principle as the matching-engine extraction (design.md D1).\n *\n * @packageDocumentation\n * @internal\n */\n\nimport { HTTP_METHODS } from '@nextrush/types';\nimport type {\n HttpMethod,\n MetadataContribution,\n Middleware,\n RouteDefinition,\n RouteEntry,\n RouteHandler,\n} from '@nextrush/types';\nimport {\n compileExecutor,\n createNode,\n NodeType,\n parseSegments,\n type HandlerEntry,\n type StaticRouteMap,\n type TrieNode,\n} from './segment-trie';\nimport { mergeContributions, readContribution } from './route-metadata';\nimport { createRedirectHandler, type RedirectStatus } from './redirect';\n\n/**\n * Registration state `addRoute` reads and writes, threaded explicitly.\n *\n * `maxDepth` is the deepest registered route's segment count seen so far —\n * used to size the reused walk-frame pool (`reduce-router-match-allocations`)\n * without guessing a constant. It only ever grows: a request path can never\n * make the tree walk descend past the trie's own real depth (a mismatched\n * segment backtracks rather than pushing a new frame), so this stays a\n * registration-time-only figure, never influenced by request traffic.\n */\nexport interface RegistrationState {\n readonly root: TrieNode;\n readonly caseSensitive: boolean;\n readonly staticRoutes: StaticRouteMap;\n readonly routeDefinitions: RouteDefinition[];\n maxDepth: number;\n}\n\n/**\n * Normalize a route path at registration time: join the router prefix,\n * collapse duplicate slashes, drop a trailing slash in non-strict mode, and\n * guarantee a leading slash.\n *\n * @remarks\n * This is registration-time normalization — it joins the router `prefix` and\n * never case-folds (case handling belongs to trie insertion / matching). It is\n * a distinct concern from `normalizePathForMatch` in `matching.ts`, which\n * normalizes an *incoming request* path for lookup; both were extracted from\n * `Router` to keep each single-definition (design.md D2/D3).\n */\nexport function normalizeRegistrationPath(path: string, prefix: string, strict: boolean): string {\n // Handle prefix with trailing slash and path with leading slash\n let joinedPrefix = prefix;\n if (joinedPrefix.endsWith('/') && path.startsWith('/')) {\n joinedPrefix = joinedPrefix.slice(0, -1);\n }\n\n let normalized = joinedPrefix + path;\n\n // Fast-path: skip regex when no double slashes (99%+ of requests)\n if (normalized.includes('//')) {\n normalized = normalized.replace(/\\/+/g, '/');\n }\n\n // For non-strict mode during registration, remove trailing slash\n if (!strict && normalized.length > 1 && normalized.endsWith('/')) {\n normalized = normalized.slice(0, -1);\n }\n\n return normalized.startsWith('/') ? normalized : '/' + normalized;\n}\n\n/**\n * Insert one entry into the method-nested static-route fast-path map,\n * creating the per-method inner map on first use.\n */\nfunction setStaticEntry(\n staticRoutes: StaticRouteMap,\n method: HttpMethod,\n key: string,\n entry: HandlerEntry\n): void {\n let methodMap = staticRoutes.get(method);\n if (!methodMap) {\n methodMap = new Map<string, HandlerEntry>();\n staticRoutes.set(method, methodMap);\n }\n methodMap.set(key, entry);\n}\n\n/**\n * Insert one route into the segment trie, updating every side structure\n * (static-route fast-path map, introspection registry) that\n * `Router.match`/`Router.getRoutes` depend on.\n *\n * @param normalized - Already-normalized path (caller applies `Router`'s own\n * prefix/strict-mode normalization before calling this — normalization\n * itself stays a `Router` method since it only touches `opts`, not the trie).\n * @param recordIntrospection - When `false`, skips pushing this call's row\n * into `state.routeDefinitions` while still inserting the concrete\n * per-method trie handler (matching is completely unaffected either way).\n * `Router.all()` (T016) sets this to `false` on each of its 7 per-method\n * `addRoute` calls, then pushes exactly one consolidated\n * `isAnyMethod: true` row itself — so an any-method route yields a single\n * `getRoutes()` entry instead of one row per enumerated method, without\n * touching how any individual method is matched. Every other call site\n * (`get`/`post`/etc., `redirect`, sub-router mounting) omits this parameter\n * and keeps the original one-row-per-call behavior unchanged.\n * @returns `true` if the registered route has a param or wildcard segment —\n * the caller (`Router.addRoute`) uses this to flip its own `hasParamRoutes`\n * flag. Returned rather than mutated through `state` because it's a\n * primitive: a struct field can't carry a boolean mutation back to the\n * caller by reference the way `root`/`staticRoutes`/`routeDefinitions` do.\n */\nexport function addRoute(\n method: HttpMethod,\n normalized: string,\n entries: RouteEntry[],\n middleware: Middleware[],\n state: RegistrationState,\n recordIntrospection = true\n): boolean {\n const segments = parseSegments(normalized, state.caseSensitive);\n\n if (segments.length > state.maxDepth) {\n state.maxDepth = segments.length;\n }\n\n let node = state.root;\n\n for (const seg of segments) {\n if (seg.type === NodeType.PARAM) {\n if (!node.paramChild) {\n node.paramChild = createNode(seg.segment, NodeType.PARAM);\n node.paramChild.paramName = seg.paramName;\n } else if (node.paramChild.paramName !== seg.paramName) {\n // Same position, different param names — this silently loses one name\n // at runtime (params[newName] is undefined), so fail fast at\n // registration rather than warn (audit RT-5). Also removes the\n // process.env / console.warn usage that was here.\n throw new Error(\n `Route param name conflict at \"${normalized}\": \":${String(seg.paramName)}\" ` +\n `conflicts with existing \":${String(node.paramChild.paramName)}\" at the same ` +\n `position. Use the same param name for this segment across all routes.`\n );\n }\n node = node.paramChild;\n } else if (seg.type === NodeType.WILDCARD) {\n node.wildcardChild ??= createNode('*', NodeType.WILDCARD);\n node = node.wildcardChild;\n break; // Wildcard must be last\n } else {\n const key = seg.segment;\n let child = node.children.get(key);\n if (!child) {\n child = createNode(seg.segment, NodeType.STATIC);\n node.children.set(key, child);\n }\n node = child;\n }\n }\n\n // Partition entries: functions are behavior (inline middleware + the final\n // handler); pure markers (endpoint()) contribute metadata only and never\n // execute. Every entry may also carry a metadata contribution (e.g. a\n // function like validate() that both runs and contributes its schema).\n const functions: Middleware[] = [];\n const contributions: MetadataContribution[] = [];\n for (const routeEntry of entries) {\n const contribution = readContribution(routeEntry);\n if (contribution) contributions.push(contribution);\n if (typeof routeEntry === 'function') functions.push(routeEntry);\n }\n\n // Combine functions into a single handler with inline middleware\n const combinedMiddleware = [...middleware];\n const finalHandler: RouteHandler | undefined = functions[functions.length - 1];\n\n if (!finalHandler) {\n throw new Error('At least one handler is required');\n }\n\n // Inline middleware = every function before the last\n for (let i = 0; i < functions.length - 1; i++) {\n const fn = functions[i];\n if (fn) combinedMiddleware.push(fn);\n }\n\n // Pre-compile executor at registration time (not per-request!)\n const executor = compileExecutor(finalHandler, combinedMiddleware);\n\n const handlerEntry: HandlerEntry = {\n handler: finalHandler,\n middleware: combinedMiddleware,\n executor,\n autoHead: false,\n };\n\n // Detect duplicate route registration. A derived HEAD entry is not a\n // duplicate — an explicit `router.head()` replaces it, in either\n // registration order.\n const existing = node.handlers.get(method);\n if (existing && !(method === 'HEAD' && existing.autoHead)) {\n throw new Error(\n `Route conflict: ${method} ${normalized} is already registered. ` +\n 'Remove the duplicate or use a different path.'\n );\n }\n\n node.handlers.set(method, handlerEntry);\n\n // Populate static route hash map for O(1) lookup. Method-nested (HP-9): the\n // outer map is keyed by method, the inner by the (lowercased, unless\n // case-sensitive) path — so matching selects the inner map by method and\n // probes by the raw path with no per-request key-string concatenation.\n const hasParams = segments.some((s) => s.type === NodeType.PARAM || s.type === NodeType.WILDCARD);\n const staticKey = hasParams\n ? undefined\n : state.caseSensitive\n ? normalized\n : normalized.toLowerCase();\n if (staticKey !== undefined) {\n setStaticEntry(state.staticRoutes, method, staticKey, handlerEntry);\n }\n\n // RFC 9110 §9.3.2: HEAD is GET without a body, so a GET registration answers\n // HEAD too — matching Fastify/Express/Koa/Hono. Derived at registration time,\n // so request dispatch is unchanged. An explicit HEAD already registered for\n // this path wins and is never overwritten.\n if (method === 'GET' && !node.handlers.has('HEAD')) {\n const derived: HandlerEntry = {\n handler: finalHandler,\n middleware: combinedMiddleware,\n executor,\n autoHead: true,\n };\n node.handlers.set('HEAD', derived);\n if (staticKey !== undefined) {\n setStaticEntry(state.staticRoutes, 'HEAD', staticKey, derived);\n }\n }\n\n // Record in the introspection registry (side structure — never touched by\n // request dispatch). key is the canonical `${METHOD} ${pathPattern}`. Skipped\n // when the caller is consolidating multiple addRoute calls into a single row\n // itself (Router.all(), T016) — the concrete trie handler above is still\n // inserted regardless, so matching for this method is unaffected.\n if (recordIntrospection) {\n state.routeDefinitions.push({\n key: `${method} ${normalized}`,\n method,\n path: normalized,\n metadata: mergeContributions(contributions),\n });\n }\n\n return hasParams;\n}\n\n/**\n * Callback signature matching `Router['addRoute']` (the validating/\n * normalizing wrapper, not the extracted `addRoute` above) — injected so\n * `registerRedirect` doesn't need `Router` instance access, same pattern as\n * `copyRoutes`'s `AddRouteFn` in `composition.ts`.\n */\nexport type RegisterRouteFn = (method: HttpMethod, path: string, entries: RouteEntry[]) => void;\n\n/**\n * Register a redirect route from one path to another across every HTTP\n * method the target status code requires (GET/HEAD always; POST/PUT/PATCH/\n * DELETE additionally for 307/308, which must preserve the original method).\n *\n * Extracted from `Router.redirect()` — thematically closer to \"register N\n * routes for one call\" than to composition or matching, and, like\n * `copyRoutes`, only needs `addRoute` injected rather than full `Router`\n * instance access.\n */\nexport function registerRedirect(\n from: string,\n to: string,\n status: RedirectStatus,\n addRoute: RegisterRouteFn\n): void {\n const redirectHandler = createRedirectHandler(to, status);\n\n // Register for common methods. 307/308 preserve the original method,\n // so register all standard methods for those status codes.\n addRoute('GET', from, [redirectHandler]);\n addRoute('HEAD', from, [redirectHandler]);\n if (status === 307 || status === 308) {\n addRoute('POST', from, [redirectHandler]);\n addRoute('PUT', from, [redirectHandler]);\n addRoute('PATCH', from, [redirectHandler]);\n addRoute('DELETE', from, [redirectHandler]);\n }\n}\n\n/**\n * Push a single consolidated any-method (`isAnyMethod: true`) introspection\n * row (T016) into the registry.\n *\n * @remarks\n * `Router.all()` / `GroupRouter.all()` insert one concrete per-method trie\n * handler each (so every method still matches) with `recordIntrospection`\n * off, then call this exactly once — so `getRoutes()` yields a single row per\n * `.all()`/`@All()` route instead of one row per enumerated HTTP method,\n * without changing how any individual method is matched.\n *\n * @param routeDefinitions - The router's introspection registry to append to.\n * @param normalized - The already-normalized route path.\n */\nexport function pushAnyMethodDefinition(\n routeDefinitions: RouteDefinition[],\n normalized: string\n): void {\n routeDefinitions.push({\n key: `${HTTP_METHODS[0]} ${normalized}`,\n method: HTTP_METHODS[0],\n path: normalized,\n isAnyMethod: true,\n });\n}\n","/**\n * @nextrush/router - Route Metadata Contributions\n *\n * Inline route metadata (`endpoint()`) and the registration-time merge of\n * contributions from `validate()`, `endpoint()`, etc. Extracted from `router.ts`\n * (audit RT-3). None of this is on the request hot path — it is read only at\n * registration and by `getRoutes()` at doc-generation time.\n *\n * @packageDocumentation\n */\n\nimport {\n ROUTE_METADATA,\n type MetadataContribution,\n type RouteEntry,\n type RouteMetadata,\n type RouteMetaMarker,\n} from '@nextrush/types';\n\n/**\n * Declare route metadata inline in a route's argument list. Returns a pure data\n * marker (not a middleware) — the router reads its metadata at registration and\n * never executes it per request.\n *\n * @example\n * ```typescript\n * router.post('/users',\n * validate(User),\n * endpoint({ summary: 'Create a user', responses: { 201: UserResponse } }),\n * handler,\n * );\n * ```\n */\nexport function endpoint(metadata: MetadataContribution): RouteMetaMarker {\n return { [ROUTE_METADATA]: metadata };\n}\n\n/** Mutable view of RouteMetadata, used only while merging contributions. */\ntype MutableRouteMetadata = { -readonly [K in keyof RouteMetadata]?: RouteMetadata[K] };\n\n/**\n * Merge metadata contributions (from `validate()`, `endpoint()`, etc.) in\n * registration order. Scalars and arrays are last-write-wins; the `request` and\n * `responses` maps merge per key. Returns `undefined` when nothing contributed\n * (an undocumented route).\n */\nexport function mergeContributions(\n contributions: readonly MetadataContribution[]\n): RouteMetadata | undefined {\n if (contributions.length === 0) return undefined;\n\n const meta: MutableRouteMetadata = {};\n for (const c of contributions) {\n if (c.summary !== undefined) meta.summary = c.summary;\n if (c.description !== undefined) meta.description = c.description;\n if (c.deprecated !== undefined) meta.deprecated = c.deprecated;\n if (c.visibility !== undefined) meta.visibility = c.visibility;\n if (c.tags !== undefined) meta.tags = c.tags;\n if (c.request) meta.request = { ...meta.request, ...c.request };\n if (c.responses) meta.responses = { ...meta.responses, ...c.responses };\n }\n return meta;\n}\n\n/** Read a route entry's metadata contribution, if it carries one. */\nexport function readContribution(entry: RouteEntry): MetadataContribution | undefined {\n return (entry as Partial<RouteMetaMarker>)[ROUTE_METADATA];\n}\n","/**\n * @nextrush/router - Method-agnostic trie walk (allowed-methods / 405 path)\n *\n * `findNode` + `findAllowedMethods` were split out of `matching.ts` when the\n * iterative rewrite of `findNode` (HP-17, OpenSpec change\n * `router-context-final-cleanup`) pushed that file past the 300-line ceiling.\n * They form one cohesive concern — resolving the *node* for a path regardless\n * of HTTP method, to answer OPTIONS/405 — distinct from `matching.ts`'s\n * method-aware handler match (`matchNodeIndexed`) and its normalization\n * primitives, which are reused here via import (`segmentAt`,\n * `normalizePathForMatch`).\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { HttpMethod } from '@nextrush/types';\nimport { normalizePathForMatch, segmentAt } from './matching';\nimport type { TrieNode } from './segment-trie';\n\n/**\n * One node in {@link findNode}'s explicit-stack walk. `stage` is a small state\n * machine (0 = extract segment + try static, 1 = try param, 2 = try\n * wildcard/backtrack) so a single frame can be revisited on backtrack without\n * recursion. `next` is the start position of the following segment, captured\n * once in stage 0 and reused when descending into the param branch.\n */\ninterface FindFrame {\n node: TrieNode;\n pos: number;\n stage: 0 | 1 | 2;\n next: number;\n}\n\n/**\n * Walk the trie to find the node matching a path (ignoring HTTP method), the\n * method-agnostic walker used by {@link findAllowedMethods} for the 405/OPTIONS\n * path (design.md D3 / HP-17).\n *\n * Walks with an EXPLICIT stack instead of recursion — mirroring\n * `matchNodeIndexed` — so a pathological segment count cannot overflow the call\n * stack (the same DoS class HP-11 closed for the match path). Behavior is\n * byte-identical to the former recursive walker: precedence is static > param >\n * wildcard at each node, a partially-matching branch backtracks cleanly, the\n * wildcard child is a terminal (it captures the remainder), and the first\n * accepted terminal wins. The scalar {@link segmentAt} scan is reused so the\n * traversal shares one segment-extraction helper rather than duplicating it.\n *\n * `path` is the already-normalized lookup path; `startPos` skips the leading\n * `/` (callers pass `1`, matching `matchNodeIndexed`).\n */\nexport function findNode(root: TrieNode, path: string, startPos: number): TrieNode | null {\n const stack: FindFrame[] = [{ node: root, pos: startPos, stage: 0, next: 0 }];\n\n while (stack.length > 0) {\n const frame = stack[stack.length - 1];\n if (frame === undefined) break;\n\n // Stage 0 — first visit: terminal check, then try the static child.\n if (frame.stage === 0) {\n // Whole path consumed at this node → it is the matching node.\n if (frame.pos >= path.length) {\n return frame.node;\n }\n const seg = segmentAt(path, frame.pos);\n // An empty segment means the path is exhausted here (e.g. a strict-mode\n // trailing slash) — treat this node as the terminal, as the recursive\n // walk did after `split('/').filter(Boolean)` dropped empty segments.\n if (seg === '') {\n return frame.node;\n }\n // `segmentAt` already scanned to the next `/`; derive the following\n // position from the segment length (no second indexOf) — a slash follows\n // iff the segment ended before the path did.\n const segEnd = frame.pos + seg.length;\n frame.next = segEnd < path.length ? segEnd + 1 : path.length;\n frame.stage = 1;\n const staticChild = frame.node.children.get(seg);\n if (staticChild) {\n stack.push({ node: staticChild, pos: frame.next, stage: 0, next: 0 });\n }\n continue;\n }\n\n // Stage 1 — static child (if any) failed: try the param child.\n if (frame.stage === 1) {\n frame.stage = 2;\n if (frame.node.paramChild) {\n stack.push({ node: frame.node.paramChild, pos: frame.next, stage: 0, next: 0 });\n }\n continue;\n }\n\n // Stage 2 — static and param branches exhausted: the wildcard child is a\n // terminal (matches the remainder). Otherwise this branch fails; backtrack.\n if (frame.node.wildcardChild) {\n return frame.node.wildcardChild;\n }\n stack.pop();\n }\n\n return null;\n}\n\n/**\n * Find all HTTP methods registered for a given path via a single tree walk.\n * `caseSensitive`/`strict` (formerly `this.opts.*`) and `root` (formerly\n * `this.root`) are threaded explicitly.\n */\nexport function findAllowedMethods(\n path: string,\n root: TrieNode,\n caseSensitive: boolean,\n strict: boolean\n): HttpMethod[] {\n const normalized = normalizePathForMatch(path, caseSensitive, strict);\n\n // Walk from position 1 to skip the leading '/', matching `matchNodeIndexed`'s\n // start offset. The iterative `findNode` scans segments off the path in place,\n // so no `split('/')` array is allocated here.\n const node = findNode(root, normalized, 1);\n if (!node || node.handlers.size === 0) return [];\n\n return Array.from(node.handlers.keys());\n}\n","/**\n * @nextrush/router - Canonical Request Path\n *\n * The single normalization owner (RFC-029): every consumer that needs to know\n * \"what path does the router treat this request as\" — the router's own match,\n * a mounted-router prefix test, a CSRF exclude-path match, an adapter's\n * published `ctx.path` — calls {@link canonicalizePath} instead of hand-rolling\n * its own fold/collapse/strip, so there is exactly one definition of \"the same\n * path\" across the framework (SEC-02, SEC-09, SEC-15).\n *\n * @see docs/RFC/request-data/029-canonical-request-path.md\n * @packageDocumentation\n */\n\nimport { collapseAndStrip, isProvablyLowerAscii } from './matching';\n\n/** Result of {@link canonicalizePath}. */\nexport interface CanonicalPathResult {\n /**\n * `true` when the path contains a `.`/`..` path segment. A dot segment is\n * rejected outright (400), never resolved — resolving it locally would\n * diverge from how a front-end proxy already resolved (or didn't resolve)\n * the same segment before forwarding, reopening the desync this RFC closes.\n */\n readonly rejected: boolean;\n /**\n * The canonical path: query-stripped, dot-segment-free, case-folded (unless\n * `caseSensitive`), slash-collapsed, trailing-slash-stripped per `strict`.\n * Meaningless when {@link rejected} is `true`.\n */\n readonly path: string;\n}\n\nconst DOT = 0x2e; // '.'\nconst SLASH = 0x2f; // '/'\n\n/**\n * True when `path` (already query-stripped) contains a `.` or `..` segment —\n * a component that is exactly `.` or `..` between slash boundaries (or at the\n * start/end of the string). A single linear scan, no backtracking regex\n * (task 3.5): each character is visited at most once, so a pathological input\n * cannot degrade to worse than O(n).\n *\n * A dot as filename content (`archive.tar.gz`, `..hidden.txt`) is NOT a dot\n * segment — only a component whose entire text between slashes is `.` or `..`\n * counts.\n */\nexport function hasDotSegment(path: string): boolean {\n const len = path.length;\n let segStart = 0;\n\n for (let i = 0; i <= len; i++) {\n const atBoundary = i === len || path.charCodeAt(i) === SLASH;\n if (!atBoundary) continue;\n\n const segLen = i - segStart;\n if (segLen === 1 && path.charCodeAt(segStart) === DOT) return true;\n if (segLen === 2 && path.charCodeAt(segStart) === DOT && path.charCodeAt(segStart + 1) === DOT) {\n return true;\n }\n segStart = i + 1;\n }\n\n return false;\n}\n\n/**\n * Single-entry memo for {@link canonicalizePath}.\n *\n * Exists because a request falling through N prefix mounts asks for the\n * canonical form of the *same* path N times, once per mount — the O(mounts)\n * term that made a 10-module app spend most of its dispatch time deciding which\n * mount to enter. Each call also allocated its own result object, so the memo\n * removes an allocation as well as three string scans.\n *\n * Safe to share the result across callers: {@link CanonicalPathResult} declares\n * both fields `readonly`, and no caller mutates it (verified across all three\n * call sites — the mount test, the router's own dispatch, and the adapter).\n * Keyed on all three inputs, so two routers with different `caseSensitive` or\n * `strict` options can never read each other's answer. A single entry means\n * interleaved requests simply miss rather than getting a wrong answer.\n *\n * This is a pure-function memo, not shared state: it cannot make\n * `canonicalizePath` return anything other than what recomputing would return.\n */\nlet memoTarget: string | undefined = undefined;\nlet memoCaseSensitive = false;\nlet memoStrict = false;\nlet memoResult: CanonicalPathResult = { rejected: false, path: '/' };\n\n/**\n * Canonicalize a raw request target into the one path value every consumer\n * (router match, mount-prefix test, CSRF exclude match, published `ctx.path`)\n * treats as \"this request's path\" (RFC-029).\n *\n * Order: strip the query string, reject a dot segment before any further\n * normalization (a dot segment is a request-shape violation, not something to\n * fold case on first), then fold case (unless `caseSensitive`) and collapse\n * structure exactly as {@link import('./matching').normalizePathForMatch} does\n * — this function IS that normalization, extended with dot-segment rejection\n * and made a public, adapter-facing entry point.\n *\n * The most recent result is memoized; see the note above the memo fields for\n * why that is sound. Treat the returned object as immutable.\n *\n * @param rawTarget - The raw request target, may include a query string.\n * @param caseSensitive - Router case-sensitivity option.\n * @param strict - Router strict-trailing-slash option.\n */\nexport function canonicalizePath(\n rawTarget: string,\n caseSensitive: boolean,\n strict: boolean\n): CanonicalPathResult {\n if (rawTarget === memoTarget && caseSensitive === memoCaseSensitive && strict === memoStrict) {\n return memoResult;\n }\n\n const queryIdx = rawTarget.indexOf('?');\n const path = queryIdx === -1 ? rawTarget : rawTarget.slice(0, queryIdx);\n\n const result: CanonicalPathResult = hasDotSegment(path)\n ? { rejected: true, path }\n : {\n rejected: false,\n path: collapseAndStrip(\n caseSensitive || isProvablyLowerAscii(path) ? path : path.toLowerCase(),\n strict\n ),\n };\n\n memoTarget = rawTarget;\n memoCaseSensitive = caseSensitive;\n memoStrict = strict;\n memoResult = result;\n return result;\n}\n","/**\n * @nextrush/router - Dispatch Middleware Generation\n *\n * The two app-facing `Middleware` factories extracted from the `Router` class\n * (design.md D2 — finishing T014's split along the same seam: the composition,\n * matching-engine, and sealing clusters were already extracted; this is the\n * dispatch/allowed-methods generation cluster).\n *\n * Both closures are structurally pure — every value they read (the `match`\n * function, the trie root, the case-sensitivity/strict flags) is passed in\n * explicitly rather than captured off `this`, so they carry no hidden\n * dependency on `Router` beyond their parameters (same principle as the\n * matching-engine extraction, design.md D1). `Router.routes()` and\n * `Router.allowedMethods()` stay as thin public methods that supply that\n * state and return these closures.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { Context, HttpMethod, Middleware, RouteMatch } from '@nextrush/types';\nimport { NOOP_NEXT, type TrieNode } from './segment-trie';\nimport { findAllowedMethods } from './find-node';\nimport { canonicalizePath } from './canonicalize';\n\n/**\n * Shared resolved promise for the no-`next` miss path (NF-1). Reused rather than\n * allocating a fresh `Promise.resolve()` per miss, mirroring the router's\n * existing `NOOP_NEXT`/`RESOLVED_PROMISE` sentinels.\n */\nconst RESOLVED: Promise<void> = Promise.resolve();\n\n/**\n * Build the router's primary dispatch middleware.\n *\n * On each request it canonicalizes the path (RFC-029): a dot segment\n * (`.`/`..`) is rejected with 400 before any route match or path-based\n * middleware runs — resolving it locally would diverge from how a front-end\n * proxy already handled the same segment, so it is never resolved, only\n * rejected. Otherwise it resolves the route via the injected `match`\n * function, publishes the canonical path onto `ctx.path` (preserving the raw\n * target as `ctx.originalPath`), sets `ctx.params`, and runs the\n * pre-compiled executor (which already bakes in any router-level\n * middleware). A miss sets `ctx.status = 404` and yields to the next\n * middleware so `allowedMethods()`/a 404 handler can act.\n *\n * @param match - Route resolver, supplied by `Router.match` so this factory\n * never touches `Router` internals directly.\n * @param caseSensitive - Router case-sensitivity option, forwarded to\n * canonicalization so the published `ctx.path` matches what `match` used.\n * @param strict - Router strict-trailing-slash option, forwarded likewise.\n */\nexport function createRoutesMiddleware(\n match: (method: HttpMethod, path: string) => RouteMatch | null,\n caseSensitive: boolean,\n strict: boolean\n): Middleware {\n return (ctx: Context, next?: () => Promise<void>): Promise<void> => {\n const originalPath = ctx.path;\n const canonical = canonicalizePath(originalPath, caseSensitive, strict);\n\n if (canonical.rejected) {\n // A dot segment is a request-shape violation, not a 404 — it stops the\n // chain outright rather than falling through to a 404/allowedMethods\n // handler, which would still leak information about the un-normalized\n // target via ctx.path.\n ctx.status = 400;\n (ctx as { originalPath: string }).originalPath = originalPath;\n return RESOLVED;\n }\n\n (ctx as { path: string }).path = canonical.path;\n (ctx as { originalPath: string }).originalPath = originalPath;\n\n const routeMatch = match(ctx.method, canonical.path);\n\n if (!routeMatch) {\n // No route matched — set 404 so allowedMethods()/notFoundHandler() can act,\n // then forward to the next middleware (the allowedMethods fall-through).\n ctx.status = 404;\n return next ? next() : RESOLVED;\n }\n\n ctx.params = routeMatch.params;\n\n // NF-1: forward the executor's promise DIRECTLY instead of `await`-ing it in\n // an extra `async` frame. The executor already returns a `Promise<void>`,\n // converts synchronous throws to rejections, and terminates the chain at the\n // handler, so ordering, rejection propagation, and the `setNext(NOOP_NEXT)`\n // guard are unchanged — one state machine + one microtask hop removed. A\n // synchronous throw from `match()` itself is still converted to a rejection\n // by the composer's `try/catch` that wraps this middleware call.\n return routeMatch.executor\n ? routeMatch.executor(ctx)\n : // Fallback (no pre-compiled executor — shouldn't happen): wrap so a void\n // or thenable return still yields a Promise<void> and never a sync throw.\n Promise.resolve(routeMatch.handler(ctx, NOOP_NEXT));\n };\n}\n\n/**\n * Build the allowed-methods middleware.\n *\n * Runs after the dispatch middleware: if the request was a 404, it does a\n * single tree walk to collect every method registered for the path. An\n * `OPTIONS` request gets a `200` with an `Allow` header; any other method\n * gets a `405` with `Allow`. If no method is registered for the path it\n * leaves the 404 untouched.\n *\n * @param root - Trie root to walk for allowed methods.\n * @param caseSensitive - Router case-sensitivity option.\n * @param strict - Router strict-trailing-slash option.\n */\nexport function createAllowedMethodsMiddleware(\n root: TrieNode,\n caseSensitive: boolean,\n strict: boolean\n): Middleware {\n return async (ctx: Context, next?: () => Promise<void>): Promise<void> => {\n if (next) {\n await next();\n }\n\n if (ctx.status !== 404) return;\n\n // Single tree walk to find all allowed methods instead of N×match()\n const allowed = findAllowedMethods(ctx.path, root, caseSensitive, strict);\n\n if (allowed.length === 0) return;\n\n const allowHeader = allowed.join(', ');\n\n // If OPTIONS request, respond with allowed methods\n if (ctx.method === 'OPTIONS') {\n ctx.status = 200;\n ctx.set('Allow', allowHeader);\n ctx.body = '';\n return;\n }\n\n // Otherwise, return 405 Method Not Allowed\n ctx.status = 405;\n ctx.set('Allow', allowHeader);\n };\n}\n","/**\n * @nextrush/router - Router State Construction\n *\n * Pure builders for the `Router`'s resolved options and its memoized state\n * struct, extracted from the constructor (design.md D2) so `Router` stays a\n * thin shell that delegates even its own initialization.\n *\n * @packageDocumentation\n * @internal\n */\n\nimport type { Middleware, RouteDefinition, RouterOptions } from '@nextrush/types';\nimport type { StaticRouteMap, TrieNode } from './segment-trie';\nimport type { RegistrationState } from './registration';\nimport type { MatchState } from './match-route';\n\n/** Apply defaults to user-supplied router options. */\nexport function resolveRouterOptions(options: RouterOptions): Required<RouterOptions> {\n return {\n prefix: options.prefix ?? '',\n caseSensitive: options.caseSensitive ?? false,\n strict: options.strict ?? false,\n decode: options.decode ?? true,\n };\n}\n\n/**\n * Build the state struct the extracted registration/matching functions read.\n *\n * @remarks\n * Every field is a stable reference for the router's lifetime, so the caller\n * memoizes this once — `reset()` mutates the referenced structures in place, it\n * never reassigns them. A single struct satisfies both `RegistrationState`\n * (what `addRoute` reads) and `MatchState` (what `resolveMatch` reads).\n */\nexport function createRouterState(\n root: TrieNode,\n opts: Required<RouterOptions>,\n staticRoutes: StaticRouteMap,\n routeDefinitions: RouteDefinition[],\n routerMiddleware: Middleware[]\n): RegistrationState & MatchState {\n return {\n root,\n staticRoutes,\n routeDefinitions,\n caseSensitive: opts.caseSensitive,\n strict: opts.strict,\n decode: opts.decode,\n routerMiddleware,\n maxDepth: 0,\n };\n}\n"],"mappings":"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","names":["HTTP_METHODS","NodeType","RESOLVED_PROMISE","Promise","resolve","NOOP_NEXT","compileExecutor","handler","middleware","len","length","ctx","setNext","err","reject","Error","String","index","dispatch","i","mw","undefined","next","createNode","segment","type","children","Map","handlers","clearNode","node","clear","paramChild","wildcardChild","parseSegments","path","caseSensitive","normalized","startsWith","slice","parts","split","segments","part","paramName","push","toLowerCase","HTTP_METHODS","compileRedirectTarget","to","parts","pos","found","length","idx","i","push","slice","end","indexOf","undefined","createRedirectHandler","status","compiledParts","ctx","targetPath","params","head","result","paramKey","tail","set","body","GroupRouter","parent","prefix","middleware","fullPath","path","cleanPrefix","endsWith","slice","cleanPath","startsWith","get","handlers","_addGroupRoute","post","put","delete","patch","head","options","all","method","HTTP_METHODS","_pushAnyMethodRouteDefinition","redirect","from","to","status","redirectHandler","createRedirectHandler","group","middlewareOrCallback","callback","runRouteGroup","host","inheritedMiddleware","cb","Array","isArray","Error","combined","length","NULL_PROTO","Object","create","EMPTY_PARAMS","freeze","createWalkPool","maxDepth","frames","i","push","node","undefined","pos","stage","seg","next","bound","bindNames","bindValues","matchNodeIndexedPooled","root","path","startPos","method","decode","pool","originalPath","depth","first","frame","length","handler","handlers","get","slashPos","indexOf","slice","staticChild","children","paramChild","paramName","value","decodeParam","segmentAt","pop","wildcardChild","src","decodeParam","value","decode","includes","decodeURIComponent","segmentAt","path","start","slashPos","indexOf","slice","isProvablyLowerAscii","i","length","c","charCodeAt","collapseAndStrip","strict","normalized","replace","endsWith","normalizePathForMatch","caseSensitive","folded","toLowerCase","matchNodeIndexed","root","startPos","bindNames","bindValues","method","originalPath","pool","matchNodeIndexedPooled","stack","node","pos","stage","seg","next","bound","frame","undefined","handler","handlers","get","pop","staticChild","children","push","paramChild","paramName","wildcardChild","src","matchRoute","method","rawPath","root","staticRoutes","hasParamRoutes","caseSensitive","strict","decode","routerMiddleware","preNormalized","walkPool","path","queryIdx","indexOf","slice","normalized","caseStable","isProvablyLowerAscii","folded","toLowerCase","collapseAndStrip","methodMap","get","staticKey","length","endsWith","staticEntry","handler","params","EMPTY_PARAMS","middleware","executor","originalPath","undefined","bindNames","bindValues","entry","matchNodeIndexed","count","Object","create","NULL_PROTO","i","name","value","resolveMatch","state","copyRoutes","node","prefix","segments","subRouterMiddleware","addRoute","method","entry","handlers","autoHead","path","join","combined","length","middleware","handler","child","children","segment","paramChild","wildcardChild","sealRouterMiddleware","root","staticRoutes","routerMiddleware","routerMw","walk","node","method","entry","handlers","combinedMw","middleware","executor","compileExecutor","handler","set","child","children","paramChild","wildcardChild","methodMap","key","HTTP_METHODS","ROUTE_METADATA","endpoint","metadata","ROUTE_METADATA","mergeContributions","contributions","length","undefined","meta","c","summary","description","deprecated","visibility","tags","request","responses","readContribution","entry","normalizeRegistrationPath","path","prefix","strict","joinedPrefix","endsWith","startsWith","slice","normalized","includes","replace","length","setStaticEntry","staticRoutes","method","key","entry","methodMap","get","Map","set","addRoute","entries","middleware","state","recordIntrospection","segments","parseSegments","caseSensitive","maxDepth","node","root","seg","type","NodeType","PARAM","paramChild","createNode","segment","paramName","Error","String","WILDCARD","wildcardChild","child","children","STATIC","functions","contributions","routeEntry","contribution","readContribution","push","combinedMiddleware","finalHandler","i","fn","executor","compileExecutor","handlerEntry","handler","autoHead","existing","handlers","hasParams","some","s","staticKey","undefined","toLowerCase","has","derived","routeDefinitions","metadata","mergeContributions","registerRedirect","from","to","status","redirectHandler","createRedirectHandler","pushAnyMethodDefinition","HTTP_METHODS","isAnyMethod","findNode","root","path","startPos","stack","node","pos","stage","next","length","frame","undefined","seg","segmentAt","segEnd","staticChild","children","get","push","paramChild","wildcardChild","pop","findAllowedMethods","caseSensitive","strict","normalized","normalizePathForMatch","handlers","size","Array","from","keys","DOT","SLASH","hasDotSegment","path","len","length","segStart","i","atBoundary","charCodeAt","segLen","memoTarget","undefined","memoCaseSensitive","memoStrict","memoResult","rejected","canonicalizePath","rawTarget","caseSensitive","strict","queryIdx","indexOf","slice","result","collapseAndStrip","isProvablyLowerAscii","toLowerCase","RESOLVED","Promise","resolve","createRoutesMiddleware","match","caseSensitive","strict","ctx","next","originalPath","path","canonical","canonicalizePath","rejected","status","routeMatch","method","params","executor","handler","NOOP_NEXT","createAllowedMethodsMiddleware","root","allowed","findAllowedMethods","length","allowHeader","join","set","body","resolveRouterOptions","options","prefix","caseSensitive","strict","decode","createRouterState","root","opts","staticRoutes","routeDefinitions","routerMiddleware","maxDepth","SLASH_CHAR_CODE","Router","root","opts","routerMiddleware","staticRoutes","Map","routeDefinitions","hasParamRoutes","_sealed","_prefixMemoRaw","undefined","_prefixMemoCanonical","state","options","createNode","resolveRouterOptions","createRouterState","addRoute","method","path","entries","middleware","recordIntrospection","rawPath","TypeError","normalized","normalizeRegistrationPath","prefix","strict","depthBefore","maxDepth","addRouteImpl","walkPool","createWalkPool","get","post","put","delete","patch","head","all","HTTP_METHODS","pushAnyMethodDefinition","route","getRoutes","redirect","from","to","status","registerRedirect","use","pathOrMiddleware","routerOrUndefined","push","mountRouter","Error","mount","router","copyRoutes","bind","match","resolveMatch","matchesMountPrefix","canonical","canonicalizePath","caseSensitive","rejected","canonicalPrefix","prefixLen","length","startsWith","hasCharAfterPrefix","charCodeAt","slice","routes","sealRouterMiddlewareImpl","createRoutesMiddleware","allowedMethods","createAllowedMethodsMiddleware","group","middlewareOrCallback","callback","runRouteGroup","reset","clearNode","clear","_addGroupRoute","handlers","groupMiddleware","_pushAnyMethodRouteDefinition","createRouter"]}