@stacksjs/bun-router 0.0.15 → 0.0.17

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 (83) hide show
  1. package/dist/auth.d.ts +22 -8
  2. package/dist/chunk-1ahs68ys.js +18 -0
  3. package/dist/{chunk-j0e7z7hd.js → chunk-cgptvjdf.js} +153 -75
  4. package/dist/chunk-g3ybefhg.js +1923 -0
  5. package/dist/cli.js +13 -11
  6. package/dist/container/container.d.ts +1 -0
  7. package/dist/container/contextual-binding.d.ts +0 -1
  8. package/dist/container/decorators.d.ts +2 -5
  9. package/dist/container/index.d.ts +16 -0
  10. package/dist/container/index.js +322 -0
  11. package/dist/file-serving/static-files.d.ts +16 -0
  12. package/dist/index.d.ts +17 -1
  13. package/dist/index.js +1086 -1270
  14. package/dist/middleware/cors.d.ts +9 -0
  15. package/dist/middleware/csrf.d.ts +7 -0
  16. package/dist/middleware/ddos_protection.d.ts +16 -1
  17. package/dist/middleware/rate_limit.d.ts +7 -1
  18. package/dist/middleware/request_tracer.d.ts +15 -9
  19. package/dist/middleware/response_cache.d.ts +2 -1
  20. package/dist/request/context.d.ts +12 -1
  21. package/dist/request/macros.d.ts +40 -2
  22. package/dist/response/macros.d.ts +3 -3
  23. package/dist/router/fluent-routing.d.ts +50 -13
  24. package/dist/router/handler-resolver.d.ts +8 -0
  25. package/dist/router/index.d.ts +2 -0
  26. package/dist/router/route-compiler.d.ts +8 -10
  27. package/dist/router/route-trie.d.ts +7 -1
  28. package/dist/router/router.d.ts +13 -2
  29. package/dist/router/validation-integration.d.ts +10 -4
  30. package/dist/types/middleware-types.d.ts +1 -1
  31. package/dist/types/route-inference.d.ts +9 -2
  32. package/dist/types.d.ts +62 -9
  33. package/dist/utils.d.ts +1 -1
  34. package/package.json +11 -1
  35. package/src/auth.ts +32 -12
  36. package/src/container/container.ts +58 -12
  37. package/src/container/contextual-binding.ts +4 -1
  38. package/src/container/decorators.ts +16 -7
  39. package/src/container/index.ts +49 -0
  40. package/src/errors/circuit-breaker.ts +3 -2
  41. package/src/errors/graceful-degradation.ts +2 -0
  42. package/src/file-serving/static-files.ts +144 -7
  43. package/src/index.ts +53 -1
  44. package/src/middleware/cors.ts +76 -8
  45. package/src/middleware/csrf.ts +80 -22
  46. package/src/middleware/ddos_protection.ts +27 -3
  47. package/src/middleware/input_validation.ts +10 -6
  48. package/src/middleware/performance_alerting.ts +2 -0
  49. package/src/middleware/performance_monitor.ts +2 -0
  50. package/src/middleware/rate_limit.ts +7 -1
  51. package/src/middleware/request_signing.ts +2 -0
  52. package/src/middleware/request_tracer.ts +34 -17
  53. package/src/middleware/response_cache.ts +48 -13
  54. package/src/middleware/session.ts +3 -3
  55. package/src/observability/metrics.ts +1 -1
  56. package/src/request/context.ts +50 -3
  57. package/src/request/enhanced-request.ts +20 -1
  58. package/src/request/macros.ts +183 -49
  59. package/src/response/macros.ts +3 -3
  60. package/src/router/file-based-routing.ts +17 -10
  61. package/src/router/fluent-routing.ts +117 -48
  62. package/src/router/group-organization.ts +17 -1
  63. package/src/router/handler-resolver.ts +36 -0
  64. package/src/router/http-methods.ts +33 -3
  65. package/src/router/index.ts +6 -0
  66. package/src/router/middleware.ts +3 -0
  67. package/src/router/optimized-route-matching.ts +58 -9
  68. package/src/router/route-compiler.ts +62 -73
  69. package/src/router/route-matching.ts +19 -0
  70. package/src/router/route-trie.ts +34 -48
  71. package/src/router/router.ts +72 -60
  72. package/src/router/server.ts +361 -139
  73. package/src/router/validation-integration.ts +11 -5
  74. package/src/testing/auth-testing.ts +6 -4
  75. package/src/types/middleware-types.ts +7 -4
  76. package/src/types/route-inference.ts +29 -14
  77. package/src/types.ts +63 -7
  78. package/src/url.ts +7 -2
  79. package/src/utils.ts +107 -39
  80. package/src/validation/validator.ts +7 -4
  81. package/src/websocket/clustering.ts +8 -1
  82. package/dist/router/fluent-router.d.ts +0 -315
  83. package/src/router/fluent-router.ts +0 -927
@@ -1,5 +1,14 @@
1
1
  import type { EnhancedRequest, NextFunction } from '../types';
2
2
  export default class Cors {
3
+ /**
4
+ * Resolve the `Access-Control-Allow-Origin` value for a request.
5
+ *
6
+ * Supports a string origin (`'*'` or an explicit origin) or an array of
7
+ * allowed origins. For an allowlist, the request `Origin` is reflected back
8
+ * only when it is present in the list. When credentials are enabled a literal
9
+ * `*` is never returned (it would be both spec-violating and dangerous).
10
+ */
11
+ private resolveAllowOrigin;
3
12
  private getCorsHeaders;
4
13
  handle(req: EnhancedRequest, next: NextFunction): Promise<Response>;
5
14
  }
@@ -2,6 +2,13 @@ import type { EnhancedRequest, NextFunction } from '../types';
2
2
  export default class Csrf {
3
3
  private static tokens;
4
4
  handle(req: EnhancedRequest, next: NextFunction): Promise<Response>;
5
+ /**
6
+ * Constant-time string comparison — a plain `!==` leaks how many leading
7
+ * characters of the token were correct through response timing.
8
+ */
9
+ private static safeCompare;
10
+ private static storeToken;
11
+ private static isTokenValid;
5
12
  private generateToken;
6
13
  private parseCookies;
7
14
  }
@@ -13,13 +13,27 @@ export interface DDoSProtectionOptions {
13
13
  skipSuccessfulRequests?: boolean;
14
14
  skipFailedRequests?: boolean;
15
15
  keyGenerator?: (req: EnhancedRequest) => string;
16
- onLimitReached?: (req: EnhancedRequest, rateLimitInfo: any) => Response | Promise<Response>;
16
+ onLimitReached?: (req: EnhancedRequest, rateLimitInfo: DDoSRateLimitInfo) => Response | Promise<Response>;
17
17
  store?: 'memory' | 'redis';
18
18
  redis?: {
19
19
  url: string;
20
20
  prefix?: string;
21
21
  };
22
22
  }
23
+ interface RequestInfo {
24
+ count: number;
25
+ firstRequest: number;
26
+ lastRequest: number;
27
+ blocked: boolean;
28
+ blockExpires?: number;
29
+ }
30
+ /**
31
+ * Rate-limit details passed to `onLimitReached` when a client is blocked.
32
+ */
33
+ export interface DDoSRateLimitInfo extends RequestInfo {
34
+ /** Seconds until the block lifts (mirrors the Retry-After header) */
35
+ retryAfter: number;
36
+ }
23
37
  export default class DDoSProtection {
24
38
  private options;
25
39
  private requestStore;
@@ -37,3 +51,4 @@ export default class DDoSProtection {
37
51
  destroy(): void;
38
52
  }
39
53
  export declare function ddosProtection(options?: DDoSProtectionOptions): (req: EnhancedRequest, next: NextFunction) => Promise<Response>;
54
+ export {};
@@ -1,3 +1,4 @@
1
+ import type { StorageProvider } from 'ts-rate-limiter';
1
2
  import type { EnhancedRequest, Middleware, MiddlewareHandler, NextFunction } from '../types';
2
3
  export interface RateLimitOptions {
3
4
  windowMs?: number;
@@ -12,7 +13,12 @@ export interface RateLimitOptions {
12
13
  resetTime: number;
13
14
  }) => Response | Promise<Response>;
14
15
  skip?: (request: Request) => boolean | Promise<boolean>;
15
- storage?: any;
16
+ /**
17
+ * Custom storage backend (ts-rate-limiter `StorageProvider`), or the
18
+ * string `'redis'` to use the built-in Redis store configured via
19
+ * the `redis` option.
20
+ */
21
+ storage?: StorageProvider | 'redis';
16
22
  algorithm?: 'fixed-window' | 'sliding-window' | 'token-bucket';
17
23
  draftMode?: boolean;
18
24
  redis?: {
@@ -7,17 +7,28 @@ export interface TraceSpan {
7
7
  startTime: number;
8
8
  endTime?: number;
9
9
  duration?: number;
10
- tags: Record<string, any>;
10
+ tags: Record<string, unknown>;
11
11
  logs: Array<{
12
12
  timestamp: number;
13
13
  level: 'debug' | 'info' | 'warn' | 'error';
14
14
  message: string;
15
- fields?: Record<string, any>;
15
+ fields?: Record<string, unknown>;
16
16
  }>;
17
17
  status: 'ok' | 'error' | 'timeout';
18
18
  error?: string;
19
19
  stackTrace?: string[];
20
20
  }
21
+ /**
22
+ * Destination for exported spans. `endpoint`/`headers` configure the
23
+ * HTTP-based exporters (jaeger/zipkin/otlp); `customExporter` receives
24
+ * the spans directly.
25
+ */
26
+ export interface TraceExporterConfig {
27
+ type: 'console' | 'jaeger' | 'zipkin' | 'otlp' | 'custom';
28
+ endpoint?: string;
29
+ headers?: Record<string, string>;
30
+ customExporter?: (spans: TraceSpan[]) => Promise<void>;
31
+ }
21
32
  export interface TracingOptions {
22
33
  enabled?: boolean;
23
34
  sampleRate?: number;
@@ -27,12 +38,7 @@ export interface TracingOptions {
27
38
  includeRequestBody?: boolean;
28
39
  includeResponseBody?: boolean;
29
40
  maxBodySize?: number;
30
- exporters?: Array<{
31
- type: 'console' | 'jaeger' | 'zipkin' | 'otlp' | 'custom';
32
- endpoint?: string;
33
- headers?: Record<string, string>;
34
- customExporter?: (spans: TraceSpan[]) => Promise<void>;
35
- }>;
41
+ exporters?: TraceExporterConfig[];
36
42
  propagation?: {
37
43
  enabled?: boolean;
38
44
  headers?: string[];
@@ -63,7 +69,7 @@ export default class RequestTracer {
63
69
  getActiveSpans(): TraceSpan[];
64
70
  getSpanById(spanId: string): TraceSpan | undefined;
65
71
  createChildSpan(parentSpanId: string, operationName: string): string | null;
66
- addTag(spanId: string, key: string, value: any): void;
72
+ addTag(spanId: string, key: string, value: unknown): void;
67
73
  addLog(spanId: string, level: 'debug' | 'info' | 'warn' | 'error', message: string, fields?: Record<string, any>): void;
68
74
  finish(spanId: string, status?: 'ok' | 'error' | 'timeout', error?: string): void;
69
75
  flush(): Promise<void>;
@@ -5,7 +5,7 @@ export interface CacheEntry {
5
5
  status: number;
6
6
  statusText: string;
7
7
  headers: Record<string, string>;
8
- body: number[];
8
+ body: Uint8Array | number[];
9
9
  contentType: string;
10
10
  };
11
11
  createdAt: number;
@@ -54,6 +54,7 @@ export declare class ResponseCache implements Middleware {
54
54
  private memoryCache;
55
55
  private cacheStats;
56
56
  private cleanupInterval?;
57
+ private compiledInvalidationPatterns;
57
58
  constructor(options?: ResponseCacheOptions);
58
59
  private initializeStorage;
59
60
  private startCleanupInterval;
@@ -9,7 +9,14 @@
9
9
  */
10
10
  import type { EnhancedRequest } from '../types';
11
11
  /**
12
- * Run `fn` inside an AsyncLocalStorage scope where `request()` and
12
+ * Force AsyncLocalStorage-backed request context on for all subsequent
13
+ * requests. Calling `request()`/`getCurrentRequest()` enables it
14
+ * automatically — use this when the very first request must already be
15
+ * fully isolated under concurrent load.
16
+ */
17
+ export declare function enableRequestContext(): void;
18
+ /**
19
+ * Run `fn` inside a request-context scope where `request()` and
13
20
  * `getCurrentRequest()` resolve to `initial`. The Router calls this around
14
21
  * each request automatically.
15
22
  */
@@ -23,6 +30,10 @@ export declare function setCurrentRequest(req: EnhancedRequest): void;
23
30
  /**
24
31
  * Returns the active request, or `undefined` when called outside a request
25
32
  * scope. Prefer `request()` when the request is required.
33
+ *
34
+ * First use switches the router to AsyncLocalStorage-backed context for
35
+ * all subsequent requests; this call itself resolves via a synchronous
36
+ * fallback (see `enableRequestContext` for eager opt-in).
26
37
  */
27
38
  export declare function getCurrentRequest(): EnhancedRequest | undefined;
28
39
  /**
@@ -7,16 +7,25 @@ import type { EnhancedRequest } from '../types';
7
7
  export interface RequestMacro {
8
8
  name: string;
9
9
  handler: (this: EnhancedRequest, ...args: any[]) => any;
10
+ /**
11
+ * Accessor macros are installed as getters on the macro prototype —
12
+ * `req.<name>` invokes the handler instead of returning a function.
13
+ * Assignment shadows the accessor with an own property.
14
+ */
15
+ accessor?: boolean;
10
16
  }
17
+ export declare function getParsedURL(req: EnhancedRequest): URL;
18
+ export declare function getParsedCookies(req: EnhancedRequest): Record<string, string>;
11
19
  /**
12
20
  * Request macro registry
13
21
  */
14
22
  declare class RequestMacroRegistry {
15
23
  private macros;
24
+ private epoch;
16
25
  /**
17
26
  * Register a request macro
18
27
  */
19
- register(name: string, handler: (this: EnhancedRequest, ...args: any[]) => any): void;
28
+ register(name: string, handler: (this: EnhancedRequest, ...args: any[]) => any, accessor?: boolean): void;
20
29
  /**
21
30
  * Get a registered macro
22
31
  */
@@ -37,6 +46,10 @@ declare class RequestMacroRegistry {
37
46
  * Clear all macros
38
47
  */
39
48
  clear(): void;
49
+ /**
50
+ * Monotonic registry version (see `RequestWithMacros.applyMacros`)
51
+ */
52
+ getEpoch(): number;
40
53
  }
41
54
  /**
42
55
  * Global request macro registry
@@ -51,7 +64,21 @@ export declare class RequestWithMacros {
51
64
  */
52
65
  static macro(name: string, handler: (this: EnhancedRequest, ...args: any[]) => any): void;
53
66
  /**
54
- * Apply macros to a request object
67
+ * Register an accessor macro: `req.<name>` evaluates the getter
68
+ * (instead of exposing a callable). Assignment to the property
69
+ * shadows the accessor with an own value, so consumers that set
70
+ * `req.<name> = ...` keep working.
71
+ */
72
+ static macroAccessor(name: string, getter: (this: EnhancedRequest) => any): void;
73
+ /**
74
+ * Apply macros to a request object.
75
+ *
76
+ * Macros are attached via a shared prototype inserted between the
77
+ * request and its original prototype — one `setPrototypeOf` per
78
+ * request. The previous implementation looped over every macro and
79
+ * `bind`-assigned it, costing ~40 property writes and closures per
80
+ * request on the dispatch hot path. Macro handlers receive the
81
+ * request as `this`, so no per-request binding is needed.
55
82
  */
56
83
  static applyMacros(request: EnhancedRequest): EnhancedRequest;
57
84
  /**
@@ -119,6 +146,17 @@ export declare const BuiltInRequestMacros: {
119
146
  * Get authorization header
120
147
  */
121
148
  bearerToken(this: EnhancedRequest): string | null;
149
+ /**
150
+ * Get the raw, unparsed request body as a string.
151
+ *
152
+ * The body stream can only be consumed once, so this caches the result on
153
+ * `_rawBody`. Signature-verifying callbacks (Stripe/GitHub/Slack webhooks)
154
+ * need the exact bytes the client sent, before any JSON parsing — a parsed
155
+ * `jsonBody` re-serialized is NOT byte-identical and will fail HMAC checks.
156
+ * A framework body parser that has already read the body may populate
157
+ * `_rawBody` up front so this returns without re-reading.
158
+ */
159
+ rawBody(this: EnhancedRequest): Promise<string>;
122
160
  /**
123
161
  * Get basic auth credentials
124
162
  */
@@ -7,16 +7,16 @@ export interface ResponseMacro {
7
7
  name: string;
8
8
  handler: (...args: any[]) => Response;
9
9
  }
10
- export interface ApiResponse<T = any> {
10
+ export interface ApiResponse<T = unknown> {
11
11
  data?: T;
12
12
  message?: string;
13
13
  error?: string;
14
14
  errors?: Record<string, string[]>;
15
- meta?: Record<string, any>;
15
+ meta?: Record<string, unknown>;
16
16
  links?: Record<string, string>;
17
17
  timestamp?: string;
18
18
  }
19
- export interface PaginatedResponse<T = any> extends ApiResponse<T[]> {
19
+ export interface PaginatedResponse<T = unknown> extends ApiResponse<T[]> {
20
20
  meta: {
21
21
  current_page: number;
22
22
  per_page: number;
@@ -4,6 +4,30 @@ import type { ThrottleConfig } from '../routing/route-throttling';
4
4
  import type { EnhancedRequest, MiddlewareHandler, RouteHandler, ThrottlePattern } from '../types';
5
5
  import { RouteCacheFactory } from '../routing/route-caching';
6
6
  import { ThrottleFactory } from '../routing/route-throttling';
7
+ /**
8
+ * A route registered on a {@link FluentRouter}
9
+ */
10
+ export interface RegisteredFluentRoute {
11
+ method: string;
12
+ path: string;
13
+ handler: RouteHandler;
14
+ middleware: MiddlewareHandler[];
15
+ name?: string;
16
+ }
17
+ /**
18
+ * Laravel-style resource controller shape accepted by
19
+ * {@link FluentRouter.resource}. Every action is optional — only the
20
+ * actions present (and allowed by `only`/`except`) are registered.
21
+ */
22
+ export interface FluentResourceController {
23
+ index?: RouteHandler;
24
+ create?: RouteHandler;
25
+ store?: RouteHandler;
26
+ show?: RouteHandler;
27
+ edit?: RouteHandler;
28
+ update?: RouteHandler;
29
+ destroy?: RouteHandler;
30
+ }
7
31
  /**
8
32
  * Fluent route builder with chainable API
9
33
  */
@@ -122,6 +146,16 @@ export declare class FluentRouter {
122
146
  * Conditional middleware execution
123
147
  */
124
148
  when(condition: MiddlewareCondition): FluentConditionalBuilder;
149
+ /**
150
+ * Look up a named middleware factory.
151
+ * @internal Used by the fluent builders — avoids `as any` reach-ins.
152
+ */
153
+ resolveNamedMiddleware(name: string): ((params?: string) => MiddlewareHandler) | undefined;
154
+ /**
155
+ * Register conditional middleware, evaluated per request in {@link handle}.
156
+ * @internal Used by the fluent builders.
157
+ */
158
+ addConditionalMiddleware(conditional: ConditionalMiddleware): void;
125
159
  /**
126
160
  * Apply middleware with parameters
127
161
  */
@@ -192,7 +226,7 @@ export declare class FluentRouter {
192
226
  /**
193
227
  * Create resource routes (Laravel-style)
194
228
  */
195
- resource(name: string, controller: any, options?: {
229
+ resource(name: string, controller: FluentResourceController, options?: {
196
230
  only?: string[];
197
231
  except?: string[];
198
232
  model?: BunQueryBuilderModel;
@@ -200,23 +234,24 @@ export declare class FluentRouter {
200
234
  /**
201
235
  * Get all registered routes
202
236
  */
203
- getRoutes(): Array<{
204
- method: string;
205
- path: string;
206
- handler: RouteHandler;
207
- middleware: MiddlewareHandler[];
208
- name?: string;
209
- }>;
237
+ getRoutes(): RegisteredFluentRoute[];
210
238
  /**
211
239
  * Handle incoming request
212
240
  */
213
241
  handle(request: EnhancedRequest): Promise<Response | null>;
214
242
  /**
215
- * Check if route matches request
243
+ * Match a route against the request, extracting path parameters.
244
+ *
245
+ * Delegates to the same `matchPath` used by the main `Router`, so the
246
+ * fluent API has identical semantics for `{param}`, optional `{param?}`,
247
+ * and wildcard segments (the previous ad-hoc regex neither escaped
248
+ * static text nor extracted params at all).
249
+ *
250
+ * @returns the extracted params, or `null` when the route doesn't match
216
251
  */
217
- private matchesRoute;
252
+ private matchRouteParams;
218
253
  /**
219
- * Execute route with middleware
254
+ * Execute route with middleware (global → conditional → route-specific)
220
255
  */
221
256
  private executeRoute;
222
257
  }
@@ -249,7 +284,9 @@ export declare const RouteFactory: {
249
284
  */
250
285
  export declare const RouterUtils: {
251
286
  /**
252
- * Generate route URL with parameters
287
+ * Generate a URL for a named route. Resolves the path from the shared
288
+ * named-route registry (the same one `router.route()`/`url()` use);
289
+ * falls back to `/<name>` when the name was never registered.
253
290
  */
254
291
  route: (name: string, params?: Record<string, string>, query?: Record<string, string>) => string;
255
292
  /**
@@ -259,7 +296,7 @@ export declare const RouterUtils: {
259
296
  /**
260
297
  * JSON response
261
298
  */
262
- json: (data: any, status?: number) => Response;
299
+ json: (data: unknown, status?: number) => Response;
263
300
  };
264
301
  /**
265
302
  * Global router instance
@@ -22,3 +22,11 @@ export declare function resolveHandler(handler: unknown, req: EnhancedRequest, c
22
22
  * Creates a handler resolver bound to a specific config
23
23
  */
24
24
  export declare function createHandlerResolver(config: RouterConfig): (handler: unknown, req: EnhancedRequest) => Promise<Response>;
25
+ /**
26
+ * Precompile the dispatch branch for a handler whose shape never changes
27
+ * (a registered route's handler). `resolveHandler` re-sniffs the handler
28
+ * type — instanceof / typeof / prototype checks — on every request; this
29
+ * resolves the branch once and returns a specialized invoker for the
30
+ * per-request hot path.
31
+ */
32
+ export declare function createHandlerInvoker(handler: unknown, config: RouterConfig): (req: EnhancedRequest) => Promise<Response>;
@@ -82,6 +82,7 @@ declare module './router' {
82
82
  status?: number;
83
83
  headers?: Record<string, string>;
84
84
  }) => Router;
85
+ withoutNativeDispatch: () => Router;
85
86
  where: ((_param: string, _pattern: string | RegExp) => Router) & ((constraints: Record<string, string | RegExp>) => Router);
86
87
  whereNumber: (param: string) => Router;
87
88
  whereAlpha: (param: string) => Router;
@@ -98,6 +99,7 @@ declare module './router' {
98
99
  create?: ActionHandler;
99
100
  edit?: ActionHandler;
100
101
  }) => Router;
102
+ head: (path: string, handler: ActionHandler, type?: 'api' | 'web', name?: string, middleware?: any[]) => Router;
101
103
  onError: (handler: (error: Error) => Response | Promise<Response>) => Router;
102
104
  redirect: (url: string, status?: 301 | 302 | 303 | 307 | 308) => Response;
103
105
  permanentRedirect: (url: string) => Response;
@@ -9,6 +9,12 @@ export interface RouteCompilerOptions {
9
9
  enablePriorityOptimization: boolean;
10
10
  cacheSize: number;
11
11
  precompilePatterns: boolean;
12
+ /**
13
+ * Record per-match timing via `performance.now()`. Off by default —
14
+ * timing every request costs two clock reads per match on the hot path.
15
+ * Match/hit/miss counters are always collected (they're just integers).
16
+ */
17
+ enableProfiling: boolean;
12
18
  }
13
19
  /**
14
20
  * Route matching statistics for performance monitoring
@@ -27,7 +33,7 @@ export interface RouteMatchStats {
27
33
  export declare class RouteCompiler {
28
34
  private trie;
29
35
  private matchCache;
30
- private cacheKeys;
36
+ private routeKeys;
31
37
  private stats;
32
38
  private options;
33
39
  constructor(options?: Partial<RouteCompilerOptions>);
@@ -36,10 +42,6 @@ export declare class RouteCompiler {
36
42
  * @returns true if route was added, false if it's a duplicate
37
43
  */
38
44
  addRoute(route: Route): boolean;
39
- /**
40
- * Check if a route is an exact duplicate of an existing route
41
- */
42
- private checkForDuplicateRoute;
43
45
  /**
44
46
  * Pre-compile route patterns for faster matching
45
47
  */
@@ -67,11 +69,7 @@ export declare class RouteCompiler {
67
69
  */
68
70
  private addToCache;
69
71
  /**
70
- * Update LRU tracking for a cache key
71
- */
72
- private updateCacheLRU;
73
- /**
74
- * Update average match time statistics
72
+ * Update average match time statistics (only when profiling is enabled)
75
73
  */
76
74
  private updateMatchTime;
77
75
  /**
@@ -83,7 +83,13 @@ export declare class RouteTrie {
83
83
  */
84
84
  match(path: string, method: HTTPMethod): RouteMatch | null;
85
85
  /**
86
- * Recursively match path segments
86
+ * Recursively match path segments.
87
+ *
88
+ * Candidates are tried in specificity order — static, then parameter,
89
+ * then wildcard — and the first full match wins. This both fixes the
90
+ * old behavior (where a wildcard sibling could shadow a static route)
91
+ * and avoids allocating candidate arrays and params copies per segment:
92
+ * `params` is mutated in place and rolled back on backtrack.
87
93
  */
88
94
  private matchSegments;
89
95
  /**
@@ -47,6 +47,8 @@ export declare class Router {
47
47
  private namedMiddleware;
48
48
  private conditionalMiddleware;
49
49
  _middlewarePipeline?: MiddlewarePipeline;
50
+ _mwEpoch: number;
51
+ _allowedMethodsCache: Map<string, string[]>;
50
52
  config: RouterConfig;
51
53
  constructor(config?: Partial<RouterConfig>);
52
54
  /**
@@ -229,12 +231,21 @@ export declare class Router {
229
231
  */
230
232
  use(...middleware: (string | MiddlewareHandler)[]): Router;
231
233
  /**
232
- * Create a route group with prefix and middleware
234
+ * Create a route group with prefix and middleware.
235
+ *
236
+ * Synchronous callbacks return the router for chaining. Asynchronous
237
+ * callbacks return a promise that resolves once the callback (and any
238
+ * routes it registers) has finished — await it, or routes registered
239
+ * after an `await` inside the callback would lose the group prefix.
233
240
  */
234
241
  group(options: {
235
242
  prefix?: string;
236
243
  middleware?: (string | MiddlewareHandler)[];
237
- }, callback: () => void | Promise<void>): Router;
244
+ }, callback: () => Promise<void>): Promise<Router>;
245
+ group(options: {
246
+ prefix?: string;
247
+ middleware?: (string | MiddlewareHandler)[];
248
+ }, callback: () => void): Router;
238
249
  /**
239
250
  * Add route to the router
240
251
  */
@@ -2,7 +2,7 @@
2
2
  * Router Validation Integration
3
3
  *
4
4
  * Integration layer for validation and macros with the router.
5
- * Provides RouteBuilder and FluentRouter for building routes with validation support.
5
+ * Provides RouteBuilder and ValidationFluentRouter for building routes with validation support.
6
6
  */
7
7
  import type { EnhancedRequest, MiddlewareHandler, RouteHandler } from '../types';
8
8
  import type { ValidationRules, ValidatorConfig } from '../validation/validator';
@@ -31,9 +31,15 @@ export declare class RouteBuilder {
31
31
  };
32
32
  }
33
33
  /**
34
- * Fluent router for enhanced routing functionality
34
+ * Minimal route collector with validation support.
35
+ *
36
+ * Renamed from `FluentRouter`: this module used to declare a third class
37
+ * with that name. It was shadowed by the canonical `FluentRouter` exported
38
+ * from `fluent-routing.ts` (explicit exports win over `export *`), so
39
+ * `createFluentRouter()` silently returned a different class than the
40
+ * public `FluentRouter` symbol. The distinct name removes the ambiguity.
35
41
  */
36
- export declare class FluentRouter {
42
+ export declare class ValidationFluentRouter {
37
43
  private routes;
38
44
  /**
39
45
  * Create GET route
@@ -136,7 +142,7 @@ export declare const RouteHelpers: {
136
142
  /**
137
143
  * Fluent router factory
138
144
  */
139
- export declare function createFluentRouter(): FluentRouter;
145
+ export declare function createFluentRouter(): ValidationFluentRouter;
140
146
  /**
141
147
  * Middleware composition helpers
142
148
  */
@@ -4,7 +4,7 @@
4
4
  * Advanced TypeScript utilities for type-safe middleware with generic constraints
5
5
  */
6
6
  import type { RouteHandler, TypedRequest } from './route-inference';
7
- export interface TypedMiddleware<TInput = any, TOutput = TInput, _TContext = object, TNext = any> {
7
+ export interface TypedMiddleware<TInput = Request, TOutput = Response, _TContext = object, TNext = () => Promise<TOutput>> {
8
8
  (request: TInput, next: TNext): Promise<TOutput> | TOutput;
9
9
  }
10
10
  export type AugmentContext<TBase, TAddition> = TBase & TAddition;
@@ -3,7 +3,13 @@
3
3
  *
4
4
  * Advanced TypeScript utilities for inferring route parameter types from URL patterns
5
5
  */
6
- export type ExtractRouteParams<T extends string> = T extends `${infer _Start}:${infer Param}/${infer Rest}` ? {
6
+ /** Strip an inline constraint from a brace parameter: `id:[0-9]+` → `id` */
7
+ type StripInlineConstraint<T extends string> = T extends `${infer Name}:${string}` ? Name : T;
8
+ export type ExtractRouteParams<T extends string> = T extends `${string}{${infer Param}}${infer Rest}` ? (Param extends `${infer Inner}?` ? {
9
+ [K in StripInlineConstraint<Inner>]?: string;
10
+ } : {
11
+ [K in StripInlineConstraint<Param>]: string;
12
+ }) & ExtractRouteParams<Rest> : T extends `${infer _Start}:${infer Param}/${infer Rest}` ? {
7
13
  [K in Param]: string;
8
14
  } & ExtractRouteParams<`/${Rest}`> : T extends `${infer _Start}:${infer Param}?${infer Rest}` ? {
9
15
  [K in Param]?: string;
@@ -100,7 +106,7 @@ export interface RouteGroup<TPrefix extends string = '', TContext = object> {
100
106
  middleware?: TypedMiddleware<any, any, any, TContext>[];
101
107
  routes: TypedRoute<any, any, any, TContext>[];
102
108
  }
103
- export interface TypedMiddleware<TRequest = any, TResponse = any, TNext = any, _TContext = Record<string, never>> {
109
+ export interface TypedMiddleware<TRequest = Request, TResponse = Response, TNext = () => Promise<TResponse>, _TContext = Record<string, never>> {
104
110
  (request: TRequest, next: TNext): Promise<TResponse> | TResponse;
105
111
  }
106
112
  export interface RouteBuilder<TContext = object> {
@@ -166,3 +172,4 @@ export interface GenerateOpenAPISchema<T extends TypedRoute<any, any, any, any>>
166
172
  export type AssertEqual<T, U> = T extends U ? U extends T ? true : false : false;
167
173
  export type AssertExtends<T, U> = T extends U ? true : false;
168
174
  export type AssertNotEqual<T, U> = AssertEqual<T, U> extends true ? false : true;
175
+ export {};