@stacksjs/bun-router 0.0.16 → 0.0.18

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 (176) hide show
  1. package/dist/auth.d.ts +22 -8
  2. package/dist/chunk-1ahs68ys.js +18 -0
  3. package/dist/{chunk-0145g1me.js → chunk-cgptvjdf.js} +147 -75
  4. package/dist/chunk-g3ybefhg.js +1923 -0
  5. package/dist/cli.js +3 -2
  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/container/service-provider.d.ts +3 -3
  12. package/dist/development/performance-profiler.d.ts +3 -3
  13. package/dist/development/route-debugger.d.ts +1 -1
  14. package/dist/development/route-inspector.d.ts +2 -2
  15. package/dist/file-serving/static-files.d.ts +16 -0
  16. package/dist/index.d.ts +17 -1
  17. package/dist/index.js +1159 -1380
  18. package/dist/middleware/cors.d.ts +9 -0
  19. package/dist/middleware/csrf.d.ts +7 -0
  20. package/dist/middleware/ddos_protection.d.ts +16 -1
  21. package/dist/middleware/rate_limit.d.ts +7 -1
  22. package/dist/middleware/request_tracer.d.ts +15 -9
  23. package/dist/middleware/response_cache.d.ts +2 -1
  24. package/dist/observability/integration.d.ts +1 -1
  25. package/dist/request/context.d.ts +12 -1
  26. package/dist/request/macros.d.ts +40 -2
  27. package/dist/response/macros.d.ts +4 -4
  28. package/dist/router/fluent-routing.d.ts +50 -13
  29. package/dist/router/handler-resolver.d.ts +8 -0
  30. package/dist/router/index.d.ts +9 -7
  31. package/dist/router/route-compiler.d.ts +8 -10
  32. package/dist/router/route-trie.d.ts +7 -1
  33. package/dist/router/router.d.ts +13 -34
  34. package/dist/router/validation-integration.d.ts +10 -4
  35. package/dist/testing/performance-testing.d.ts +2 -2
  36. package/dist/testing/test-client.d.ts +1 -1
  37. package/dist/testing/websocket-testing.d.ts +1 -1
  38. package/dist/types/middleware-types.d.ts +1 -1
  39. package/dist/types/route-inference.d.ts +9 -2
  40. package/dist/types.d.ts +62 -9
  41. package/dist/utils.d.ts +1 -1
  42. package/package.json +12 -3
  43. package/dist/router/fluent-router.d.ts +0 -315
  44. package/src/auth.ts +0 -469
  45. package/src/cache/lru-cache.ts +0 -457
  46. package/src/cache/middleware-memoization.ts +0 -531
  47. package/src/cache/route-cache-warmer.ts +0 -486
  48. package/src/cache/sqlite-cache.ts +0 -783
  49. package/src/cache/streaming-cache.ts +0 -572
  50. package/src/cli/colors.ts +0 -29
  51. package/src/cli/index.ts +0 -287
  52. package/src/cli/middleware.ts +0 -291
  53. package/src/cli/openapi.ts +0 -407
  54. package/src/cli/router.ts +0 -188
  55. package/src/cli/routes.ts +0 -265
  56. package/src/cli/utils.ts +0 -531
  57. package/src/cli.ts +0 -5
  58. package/src/config.ts +0 -322
  59. package/src/container/container.ts +0 -740
  60. package/src/container/contextual-binding.ts +0 -603
  61. package/src/container/decorators.ts +0 -359
  62. package/src/container/service-provider.ts +0 -596
  63. package/src/development/hot-reload.ts +0 -673
  64. package/src/development/index.ts +0 -499
  65. package/src/development/performance-profiler.ts +0 -717
  66. package/src/development/route-debugger.ts +0 -527
  67. package/src/development/route-inspector.ts +0 -749
  68. package/src/development/typescript-utilities.ts +0 -682
  69. package/src/docs.ts +0 -397
  70. package/src/errors/circuit-breaker.ts +0 -732
  71. package/src/errors/error-handler.ts +0 -569
  72. package/src/errors/error-reporting.ts +0 -672
  73. package/src/errors/exceptions.ts +0 -536
  74. package/src/errors/graceful-degradation.ts +0 -621
  75. package/src/errors/index.ts +0 -21
  76. package/src/errors/router-errors.ts +0 -632
  77. package/src/file-serving/static-files.ts +0 -581
  78. package/src/index.ts +0 -14
  79. package/src/middleware/auth.ts +0 -220
  80. package/src/middleware/content_security_policy.ts +0 -215
  81. package/src/middleware/cors.ts +0 -74
  82. package/src/middleware/csrf.ts +0 -118
  83. package/src/middleware/ddos_protection.ts +0 -255
  84. package/src/middleware/file_security.ts +0 -191
  85. package/src/middleware/file_upload.ts +0 -275
  86. package/src/middleware/helmet.ts +0 -268
  87. package/src/middleware/index.ts +0 -117
  88. package/src/middleware/input_validation.ts +0 -449
  89. package/src/middleware/json_body.ts +0 -37
  90. package/src/middleware/performance_alerting.ts +0 -538
  91. package/src/middleware/performance_dashboard.ts +0 -661
  92. package/src/middleware/performance_monitor.ts +0 -943
  93. package/src/middleware/pipeline.ts +0 -489
  94. package/src/middleware/rate_limit.ts +0 -245
  95. package/src/middleware/request_id.ts +0 -36
  96. package/src/middleware/request_signing.ts +0 -636
  97. package/src/middleware/request_tracer.ts +0 -636
  98. package/src/middleware/response_cache.ts +0 -743
  99. package/src/middleware/security.ts +0 -482
  100. package/src/middleware/security_suite.ts +0 -257
  101. package/src/middleware/session.ts +0 -91
  102. package/src/model-binding/index.ts +0 -18
  103. package/src/model-binding/model-middleware.ts +0 -425
  104. package/src/model-binding/model-registry.ts +0 -550
  105. package/src/model-binding.ts +0 -370
  106. package/src/model-resolver-factory.ts +0 -106
  107. package/src/observability/correlation.ts +0 -691
  108. package/src/observability/health-checks.ts +0 -729
  109. package/src/observability/index.ts +0 -184
  110. package/src/observability/integration.ts +0 -548
  111. package/src/observability/metrics.ts +0 -753
  112. package/src/observability/tracing.ts +0 -638
  113. package/src/optimization/bun-utilities.ts +0 -778
  114. package/src/query-builder-integration.ts +0 -137
  115. package/src/request/context.ts +0 -62
  116. package/src/request/enhanced-request.ts +0 -857
  117. package/src/request/macros.ts +0 -688
  118. package/src/response/macros.ts +0 -665
  119. package/src/response/response-factory.ts +0 -596
  120. package/src/router/api-routes.ts +0 -243
  121. package/src/router/file-based-routing.ts +0 -690
  122. package/src/router/file-streaming.ts +0 -383
  123. package/src/router/fluent-router.ts +0 -927
  124. package/src/router/fluent-routing.ts +0 -797
  125. package/src/router/group-organization.ts +0 -213
  126. package/src/router/handler-resolver.ts +0 -291
  127. package/src/router/http-methods.ts +0 -377
  128. package/src/router/index.ts +0 -187
  129. package/src/router/middleware-groups.ts +0 -222
  130. package/src/router/middleware-integration.ts +0 -399
  131. package/src/router/middleware.ts +0 -231
  132. package/src/router/model-binding.ts +0 -215
  133. package/src/router/optimized-route-matching.ts +0 -253
  134. package/src/router/route-building.ts +0 -221
  135. package/src/router/route-compiler.ts +0 -702
  136. package/src/router/route-matching.ts +0 -349
  137. package/src/router/route-trie.ts +0 -464
  138. package/src/router/router.ts +0 -1604
  139. package/src/router/server.ts +0 -462
  140. package/src/router/validation-integration.ts +0 -443
  141. package/src/router/view-rendering.ts +0 -233
  142. package/src/router/websocket.ts +0 -100
  143. package/src/routing/route-caching.ts +0 -402
  144. package/src/routing/route-throttling.ts +0 -469
  145. package/src/routing/subdomain-routing.ts +0 -492
  146. package/src/session/database-store.ts +0 -109
  147. package/src/session/file-store.ts +0 -148
  148. package/src/session/index.ts +0 -244
  149. package/src/session/memory-store.ts +0 -88
  150. package/src/session/redis-store.ts +0 -93
  151. package/src/streaming/index.ts +0 -17
  152. package/src/streaming/sse-handler.ts +0 -482
  153. package/src/streaming/stream-handler.ts +0 -552
  154. package/src/testing/auth-testing.ts +0 -446
  155. package/src/testing/file-upload-testing.ts +0 -543
  156. package/src/testing/index.ts +0 -10
  157. package/src/testing/middleware-testing.ts +0 -322
  158. package/src/testing/model-binding-testing.ts +0 -645
  159. package/src/testing/performance-testing.ts +0 -738
  160. package/src/testing/test-client.ts +0 -310
  161. package/src/testing/test-request.ts +0 -315
  162. package/src/testing/test-response.ts +0 -332
  163. package/src/testing/types.ts +0 -224
  164. package/src/testing/websocket-testing.ts +0 -590
  165. package/src/types/controller-types.ts +0 -385
  166. package/src/types/core.ts +0 -683
  167. package/src/types/middleware-types.ts +0 -419
  168. package/src/types/request-response-augmentation.ts +0 -489
  169. package/src/types/route-inference.ts +0 -357
  170. package/src/types.ts +0 -2067
  171. package/src/url.ts +0 -126
  172. package/src/utils/index.ts +0 -1
  173. package/src/utils/query-preservation.ts +0 -201
  174. package/src/utils.ts +0 -392
  175. package/src/validation/validator.ts +0 -685
  176. package/src/websocket/clustering.ts +0 -762
@@ -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;
@@ -104,7 +104,7 @@ export declare const ObservabilityIntegration: {
104
104
  manager: ObservabilityManager;
105
105
  middleware: (req: EnhancedRequest, next: () => Promise<Response>) => Promise<Response>;
106
106
  handlers: Record<string, (req: EnhancedRequest) => Promise<Response>>;
107
- status: ReturnType<ObservabilityManager["getStatus"]>;
107
+ status: ReturnType<ObservabilityManager['getStatus']>;
108
108
  };
109
109
  /**
110
110
  * Create observability-enabled route builder
@@ -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;
@@ -201,7 +201,7 @@ export declare const BuiltInResponseMacros: {
201
201
  /**
202
202
  * Health check response
203
203
  */
204
- health: (status?: "healthy" | "unhealthy" | "degraded", checks?: Record<string, any>) => Response;
204
+ health: (status?: 'healthy' | 'unhealthy' | 'degraded', checks?: Record<string, any>) => Response;
205
205
  };
206
206
  /**
207
207
  * Register all built-in macros
@@ -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>;
@@ -55,11 +55,11 @@ declare module './router' {
55
55
  clearAllCache: () => void;
56
56
  getCacheStats: () => any;
57
57
  };
58
- matchRoute: (path: string, method: string, domain?: string) => {
58
+ matchRoute(path: string, method: string, domain?: string): {
59
59
  route: Route;
60
60
  params: Record<string, string>;
61
61
  } | undefined;
62
- getAllowedMethods: (path: string, domain?: string) => string[];
62
+ getAllowedMethods(path: string, domain?: string): string[];
63
63
  clearRouteCache: () => void;
64
64
  getCacheStats: () => any;
65
65
  getRouteStats: () => any;
@@ -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,11 +99,12 @@ declare module './router' {
98
99
  create?: ActionHandler;
99
100
  edit?: ActionHandler;
100
101
  }) => Router;
101
- onError: (handler: (error: Error) => Response | Promise<Response>) => Router;
102
- redirect: (url: string, status?: 301 | 302 | 303 | 307 | 308) => Response;
103
- permanentRedirect: (url: string) => Response;
104
- fallback: (handler: ActionHandler) => Router;
105
- route: (name: string, params?: Record<string, string>) => string;
102
+ head: (path: string, handler: ActionHandler, type?: 'api' | 'web', name?: string, middleware?: any[]) => Router;
103
+ onError(handler: (error: Error) => Response | Promise<Response>): Router;
104
+ redirect(url: string, status?: 301 | 302 | 303 | 307 | 308): Response;
105
+ permanentRedirect(url: string): Response;
106
+ fallback(handler: ActionHandler): Router;
107
+ route(name: string, params?: Record<string, string>): string;
106
108
  }
107
109
  }
108
110
  export { Dependencies, FluentRouteBuilder, FluentRouter, globalMiddlewarePipeline, MiddlewareFactory, MiddlewarePipeline, RouteFactory, router, RouterUtils, SkipConditions };
@@ -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
  /**
@@ -153,26 +155,6 @@ export declare class Router {
153
155
  * for `TPath`-driven param narrowing on inline handlers.
154
156
  */
155
157
  any<TPath extends string>(path: TPath, handler: ActionHandler<TPath>, type?: 'api' | 'web', name?: string, middleware?: (string | MiddlewareHandler)[]): Router;
156
- /**
157
- * Set fallback handler for unmatched routes
158
- */
159
- fallback(handler: ActionHandler): Router;
160
- /**
161
- * Generate URL for a named route
162
- */
163
- route(name: string, params?: Record<string, string>): string;
164
- /**
165
- * Set error handler
166
- */
167
- onError(handler: (error: Error) => Response | Promise<Response>): Router;
168
- /**
169
- * Create redirect response
170
- */
171
- redirect(url: string, status?: 301 | 302 | 303 | 307 | 308): Response;
172
- /**
173
- * Create permanent redirect response
174
- */
175
- permanentRedirect(url: string): Response;
176
158
  /**
177
159
  * Register redirect route
178
160
  */
@@ -188,18 +170,6 @@ export declare class Router {
188
170
  * Handle an HTTP request
189
171
  */
190
172
  handleRequest(req: Request): Promise<Response>;
191
- /**
192
- * Get all allowed HTTP methods for a given path
193
- * Used to determine if a 405 Method Not Allowed should be returned instead of 404
194
- */
195
- getAllowedMethods(path: string, domain?: string): string[];
196
- /**
197
- * Match a route based on the path, method, and domain
198
- */
199
- matchRoute(path: string, method: string, domain?: string): {
200
- route: Route;
201
- params: Record<string, string>;
202
- } | undefined;
203
173
  /**
204
174
  * Enhance a request with params and other utilities
205
175
  */
@@ -229,12 +199,21 @@ export declare class Router {
229
199
  */
230
200
  use(...middleware: (string | MiddlewareHandler)[]): Router;
231
201
  /**
232
- * Create a route group with prefix and middleware
202
+ * Create a route group with prefix and middleware.
203
+ *
204
+ * Synchronous callbacks return the router for chaining. Asynchronous
205
+ * callbacks return a promise that resolves once the callback (and any
206
+ * routes it registers) has finished — await it, or routes registered
207
+ * after an `await` inside the callback would lose the group prefix.
233
208
  */
234
209
  group(options: {
235
210
  prefix?: string;
236
211
  middleware?: (string | MiddlewareHandler)[];
237
- }, callback: () => void | Promise<void>): Router;
212
+ }, callback: () => Promise<void>): Promise<Router>;
213
+ group(options: {
214
+ prefix?: string;
215
+ middleware?: (string | MiddlewareHandler)[];
216
+ }, callback: () => void): Router;
238
217
  /**
239
218
  * Add route to the router
240
219
  */
@@ -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
  */
@@ -219,11 +219,11 @@ export declare const performanceUtils: {
219
219
  /**
220
220
  * Get current memory usage
221
221
  */
222
- getCurrentMemoryUsage: () => PerformanceMetrics["memoryUsage"];
222
+ getCurrentMemoryUsage: () => PerformanceMetrics['memoryUsage'];
223
223
  /**
224
224
  * Get CPU usage
225
225
  */
226
- getCPUUsage: () => PerformanceMetrics["cpuUsage"];
226
+ getCPUUsage: () => PerformanceMetrics['cpuUsage'];
227
227
  /**
228
228
  * Create performance metric snapshot
229
229
  */
@@ -109,7 +109,7 @@ export declare const testClient: {
109
109
  /**
110
110
  * Create a test client with authentication
111
111
  */
112
- withAuth: (token: string, type?: "Bearer" | "Basic") => (router: Router) => TestClient;
112
+ withAuth: (token: string, type?: 'Bearer' | 'Basic') => (router: Router) => TestClient;
113
113
  /**
114
114
  * Create a test client with custom headers
115
115
  */