@stacksjs/bun-router 0.0.4 → 0.0.6

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 (272) hide show
  1. package/README.md +2 -2
  2. package/dist/auth.d.ts +134 -0
  3. package/dist/cache/lru-cache.d.ts +139 -0
  4. package/dist/cache/middleware-memoization.d.ts +151 -0
  5. package/dist/cache/route-cache-warmer.d.ts +161 -0
  6. package/dist/cache/sqlite-cache.d.ts +209 -0
  7. package/dist/cache/streaming-cache.d.ts +132 -0
  8. package/dist/chunk-jhsk5c5b.js +15758 -0
  9. package/dist/cli/colors.d.ts +15 -0
  10. package/dist/cli/index.d.ts +10 -0
  11. package/dist/cli/middleware.d.ts +31 -0
  12. package/dist/cli/openapi.d.ts +17 -0
  13. package/dist/cli/router.d.ts +15 -0
  14. package/dist/cli/routes.d.ts +29 -0
  15. package/dist/cli/utils.d.ts +74 -0
  16. package/dist/cli.d.ts +1 -1
  17. package/dist/cli.js +2 -2
  18. package/dist/config.d.ts +7 -0
  19. package/dist/container/container.d.ts +273 -0
  20. package/dist/container/contextual-binding.d.ts +240 -0
  21. package/dist/container/decorators.d.ts +141 -0
  22. package/dist/container/service-provider.d.ts +285 -0
  23. package/dist/development/hot-reload.d.ts +187 -0
  24. package/dist/development/index.d.ts +167 -0
  25. package/dist/development/performance-profiler.d.ts +217 -0
  26. package/dist/development/route-debugger.d.ts +154 -0
  27. package/dist/development/route-inspector.d.ts +211 -0
  28. package/dist/development/typescript-utilities.d.ts +209 -0
  29. package/dist/docs.d.ts +11 -0
  30. package/dist/errors/circuit-breaker.d.ts +195 -0
  31. package/dist/errors/error-handler.d.ts +88 -0
  32. package/dist/errors/error-reporting.d.ts +144 -0
  33. package/dist/errors/exceptions.d.ts +184 -0
  34. package/dist/errors/graceful-degradation.d.ts +154 -0
  35. package/dist/errors/index.d.ts +9 -0
  36. package/dist/errors/router-errors.d.ts +110 -0
  37. package/dist/file-serving/static-files.d.ts +143 -0
  38. package/dist/index.d.ts +2 -0
  39. package/dist/index.js +320 -206
  40. package/dist/middleware/auth.d.ts +63 -0
  41. package/dist/middleware/content_security_policy.d.ts +53 -0
  42. package/dist/middleware/cors.d.ts +5 -0
  43. package/dist/middleware/csrf.d.ts +7 -0
  44. package/dist/middleware/ddos_protection.d.ts +39 -0
  45. package/dist/middleware/file_security.d.ts +26 -0
  46. package/dist/middleware/file_upload.d.ts +38 -0
  47. package/dist/middleware/helmet.d.ts +54 -0
  48. package/dist/middleware/index.d.ts +44 -0
  49. package/dist/middleware/input_validation.d.ts +45 -0
  50. package/dist/middleware/json_body.d.ts +4 -0
  51. package/dist/middleware/performance_alerting.d.ts +87 -0
  52. package/dist/middleware/performance_dashboard.d.ts +87 -0
  53. package/dist/middleware/performance_monitor.d.ts +209 -0
  54. package/dist/middleware/pipeline.d.ts +131 -0
  55. package/dist/middleware/rate_limit.d.ts +36 -0
  56. package/dist/middleware/request_id.d.ts +4 -0
  57. package/dist/middleware/request_signing.d.ts +153 -0
  58. package/dist/middleware/request_tracer.d.ts +72 -0
  59. package/dist/middleware/response_cache.d.ts +97 -0
  60. package/dist/middleware/security.d.ts +69 -0
  61. package/dist/middleware/security_suite.d.ts +46 -0
  62. package/dist/middleware/session.d.ts +23 -0
  63. package/dist/model-binding/index.d.ts +2 -0
  64. package/dist/model-binding/model-middleware.d.ts +118 -0
  65. package/dist/model-binding/model-registry.d.ts +164 -0
  66. package/dist/model-binding.d.ts +185 -0
  67. package/dist/model-resolver-factory.d.ts +31 -0
  68. package/dist/observability/correlation.d.ts +187 -0
  69. package/dist/observability/health-checks.d.ts +186 -0
  70. package/dist/observability/index.d.ts +60 -0
  71. package/dist/observability/integration.d.ts +147 -0
  72. package/dist/observability/metrics.d.ts +184 -0
  73. package/dist/observability/tracing.d.ts +187 -0
  74. package/dist/optimization/bun-utilities.d.ts +224 -0
  75. package/dist/query-builder-integration.d.ts +36 -0
  76. package/dist/request/context.d.ts +34 -0
  77. package/dist/request/enhanced-request.d.ts +213 -0
  78. package/dist/request/macros.d.ts +300 -0
  79. package/dist/response/macros.d.ts +245 -0
  80. package/dist/response/response-factory.d.ts +133 -0
  81. package/dist/router/api-routes.d.ts +5 -0
  82. package/dist/router/file-based-routing.d.ts +118 -0
  83. package/dist/router/file-streaming.d.ts +5 -0
  84. package/dist/router/fluent-router.d.ts +315 -0
  85. package/dist/router/fluent-routing.d.ts +271 -0
  86. package/dist/router/group-organization.d.ts +5 -0
  87. package/dist/router/handler-resolver.d.ts +24 -0
  88. package/dist/router/http-methods.d.ts +10 -0
  89. package/dist/router/index.d.ts +113 -0
  90. package/dist/router/middleware-groups.d.ts +94 -0
  91. package/dist/router/middleware-integration.d.ts +112 -0
  92. package/dist/router/middleware.d.ts +5 -0
  93. package/dist/router/model-binding.d.ts +5 -0
  94. package/dist/router/optimized-route-matching.d.ts +6 -0
  95. package/dist/router/route-building.d.ts +5 -0
  96. package/dist/router/route-compiler.d.ts +153 -0
  97. package/dist/router/route-matching.d.ts +5 -0
  98. package/dist/router/route-trie.d.ts +114 -0
  99. package/dist/router/router.d.ts +276 -0
  100. package/dist/router/server.d.ts +5 -0
  101. package/dist/router/validation-integration.d.ts +170 -0
  102. package/dist/router/view-rendering.d.ts +5 -0
  103. package/dist/router/websocket.d.ts +5 -0
  104. package/dist/routing/route-caching.d.ts +129 -0
  105. package/dist/routing/route-throttling.d.ts +150 -0
  106. package/dist/routing/subdomain-routing.d.ts +206 -0
  107. package/dist/session/database-store.d.ts +51 -0
  108. package/dist/session/file-store.d.ts +22 -0
  109. package/dist/session/index.d.ts +106 -0
  110. package/dist/session/memory-store.d.ts +21 -0
  111. package/dist/session/redis-store.d.ts +31 -0
  112. package/dist/streaming/index.d.ts +3 -0
  113. package/dist/streaming/sse-handler.d.ts +138 -0
  114. package/dist/streaming/stream-handler.d.ts +114 -0
  115. package/dist/testing/auth-testing.d.ts +156 -0
  116. package/dist/testing/file-upload-testing.d.ts +190 -0
  117. package/dist/testing/index.d.ts +10 -0
  118. package/dist/testing/middleware-testing.d.ts +138 -0
  119. package/dist/testing/model-binding-testing.d.ts +186 -0
  120. package/dist/testing/performance-testing.d.ts +235 -0
  121. package/dist/testing/test-client.d.ts +117 -0
  122. package/dist/testing/test-request.d.ts +85 -0
  123. package/dist/testing/test-response.d.ts +90 -0
  124. package/dist/testing/types.d.ts +207 -0
  125. package/dist/testing/websocket-testing.d.ts +227 -0
  126. package/dist/types/controller-types.d.ts +208 -0
  127. package/dist/types/core.d.ts +543 -0
  128. package/dist/types/middleware-types.d.ts +227 -0
  129. package/dist/types/request-response-augmentation.d.ts +261 -0
  130. package/dist/types/route-inference.d.ts +168 -0
  131. package/dist/types.d.ts +1776 -0
  132. package/dist/url.d.ts +56 -0
  133. package/dist/utils/index.d.ts +1 -0
  134. package/dist/utils/query-preservation.d.ts +48 -0
  135. package/dist/utils.d.ts +69 -0
  136. package/dist/validation/validator.d.ts +140 -0
  137. package/dist/websocket/clustering.d.ts +185 -0
  138. package/package.json +18 -4
  139. package/src/auth.ts +469 -0
  140. package/src/cache/lru-cache.ts +457 -0
  141. package/src/cache/middleware-memoization.ts +531 -0
  142. package/src/cache/route-cache-warmer.ts +486 -0
  143. package/src/cache/sqlite-cache.ts +783 -0
  144. package/src/cache/streaming-cache.ts +572 -0
  145. package/src/cli/colors.ts +29 -0
  146. package/src/cli/index.ts +287 -0
  147. package/src/cli/middleware.ts +291 -0
  148. package/src/cli/openapi.ts +407 -0
  149. package/src/cli/router.ts +188 -0
  150. package/src/cli/routes.ts +265 -0
  151. package/src/cli/utils.ts +531 -0
  152. package/src/cli.ts +5 -0
  153. package/src/config.ts +322 -0
  154. package/src/container/container.ts +740 -0
  155. package/src/container/contextual-binding.ts +603 -0
  156. package/src/container/decorators.ts +359 -0
  157. package/src/container/service-provider.ts +596 -0
  158. package/src/development/hot-reload.ts +673 -0
  159. package/src/development/index.ts +499 -0
  160. package/src/development/performance-profiler.ts +717 -0
  161. package/src/development/route-debugger.ts +527 -0
  162. package/src/development/route-inspector.ts +749 -0
  163. package/src/development/typescript-utilities.ts +678 -0
  164. package/src/docs.ts +397 -0
  165. package/src/errors/circuit-breaker.ts +732 -0
  166. package/src/errors/error-handler.ts +569 -0
  167. package/src/errors/error-reporting.ts +672 -0
  168. package/src/errors/exceptions.ts +536 -0
  169. package/src/errors/graceful-degradation.ts +621 -0
  170. package/src/errors/index.ts +21 -0
  171. package/src/errors/router-errors.ts +632 -0
  172. package/src/file-serving/static-files.ts +581 -0
  173. package/src/index.ts +14 -0
  174. package/src/middleware/auth.ts +219 -0
  175. package/src/middleware/content_security_policy.ts +215 -0
  176. package/src/middleware/cors.ts +74 -0
  177. package/src/middleware/csrf.ts +108 -0
  178. package/src/middleware/ddos_protection.ts +255 -0
  179. package/src/middleware/file_security.ts +191 -0
  180. package/src/middleware/file_upload.ts +264 -0
  181. package/src/middleware/helmet.ts +268 -0
  182. package/src/middleware/index.ts +117 -0
  183. package/src/middleware/input_validation.ts +449 -0
  184. package/src/middleware/json_body.ts +37 -0
  185. package/src/middleware/performance_alerting.ts +538 -0
  186. package/src/middleware/performance_dashboard.ts +661 -0
  187. package/src/middleware/performance_monitor.ts +943 -0
  188. package/src/middleware/pipeline.ts +489 -0
  189. package/src/middleware/rate_limit.ts +245 -0
  190. package/src/middleware/request_id.ts +36 -0
  191. package/src/middleware/request_signing.ts +636 -0
  192. package/src/middleware/request_tracer.ts +636 -0
  193. package/src/middleware/response_cache.ts +743 -0
  194. package/src/middleware/security.ts +482 -0
  195. package/src/middleware/security_suite.ts +257 -0
  196. package/src/middleware/session.ts +91 -0
  197. package/src/model-binding/index.ts +18 -0
  198. package/src/model-binding/model-middleware.ts +425 -0
  199. package/src/model-binding/model-registry.ts +550 -0
  200. package/src/model-binding.ts +370 -0
  201. package/src/model-resolver-factory.ts +106 -0
  202. package/src/observability/correlation.ts +691 -0
  203. package/src/observability/health-checks.ts +729 -0
  204. package/src/observability/index.ts +184 -0
  205. package/src/observability/integration.ts +548 -0
  206. package/src/observability/metrics.ts +753 -0
  207. package/src/observability/tracing.ts +638 -0
  208. package/src/optimization/bun-utilities.ts +778 -0
  209. package/src/query-builder-integration.ts +137 -0
  210. package/src/request/context.ts +62 -0
  211. package/src/request/enhanced-request.ts +857 -0
  212. package/src/request/macros.ts +688 -0
  213. package/src/response/macros.ts +612 -0
  214. package/src/response/response-factory.ts +588 -0
  215. package/src/router/api-routes.ts +243 -0
  216. package/src/router/file-based-routing.ts +670 -0
  217. package/src/router/file-streaming.ts +372 -0
  218. package/src/router/fluent-router.ts +927 -0
  219. package/src/router/fluent-routing.ts +797 -0
  220. package/src/router/group-organization.ts +213 -0
  221. package/src/router/handler-resolver.ts +291 -0
  222. package/src/router/http-methods.ts +377 -0
  223. package/src/router/index.ts +185 -0
  224. package/src/router/middleware-groups.ts +222 -0
  225. package/src/router/middleware-integration.ts +399 -0
  226. package/src/router/middleware.ts +231 -0
  227. package/src/router/model-binding.ts +215 -0
  228. package/src/router/optimized-route-matching.ts +253 -0
  229. package/src/router/route-building.ts +212 -0
  230. package/src/router/route-compiler.ts +702 -0
  231. package/src/router/route-matching.ts +349 -0
  232. package/src/router/route-trie.ts +464 -0
  233. package/src/router/router.ts +1508 -0
  234. package/src/router/server.ts +406 -0
  235. package/src/router/validation-integration.ts +443 -0
  236. package/src/router/view-rendering.ts +233 -0
  237. package/src/router/websocket.ts +100 -0
  238. package/src/routing/route-caching.ts +402 -0
  239. package/src/routing/route-throttling.ts +469 -0
  240. package/src/routing/subdomain-routing.ts +492 -0
  241. package/src/session/database-store.ts +109 -0
  242. package/src/session/file-store.ts +148 -0
  243. package/src/session/index.ts +244 -0
  244. package/src/session/memory-store.ts +88 -0
  245. package/src/session/redis-store.ts +93 -0
  246. package/src/streaming/index.ts +17 -0
  247. package/src/streaming/sse-handler.ts +482 -0
  248. package/src/streaming/stream-handler.ts +552 -0
  249. package/src/testing/auth-testing.ts +446 -0
  250. package/src/testing/file-upload-testing.ts +543 -0
  251. package/src/testing/index.ts +10 -0
  252. package/src/testing/middleware-testing.ts +322 -0
  253. package/src/testing/model-binding-testing.ts +645 -0
  254. package/src/testing/performance-testing.ts +738 -0
  255. package/src/testing/test-client.ts +310 -0
  256. package/src/testing/test-request.ts +315 -0
  257. package/src/testing/test-response.ts +332 -0
  258. package/src/testing/types.ts +224 -0
  259. package/src/testing/websocket-testing.ts +590 -0
  260. package/src/types/controller-types.ts +385 -0
  261. package/src/types/core.ts +683 -0
  262. package/src/types/middleware-types.ts +419 -0
  263. package/src/types/request-response-augmentation.ts +489 -0
  264. package/src/types/route-inference.ts +357 -0
  265. package/src/types.ts +2049 -0
  266. package/src/url.ts +126 -0
  267. package/src/utils/index.ts +1 -0
  268. package/src/utils/query-preservation.ts +201 -0
  269. package/src/utils.ts +327 -0
  270. package/src/validation/validator.ts +685 -0
  271. package/src/websocket/clustering.ts +762 -0
  272. package/dist/chunk-cjdekj87.js +0 -25886
@@ -0,0 +1,927 @@
1
+ /**
2
+ * Fluent Routing Extensions
3
+ *
4
+ * Implements fluent routing patterns with full TypeScript support
5
+ */
6
+
7
+ import type {
8
+ ActionHandler,
9
+ EnhancedRequest,
10
+ MiddlewareHandler,
11
+ Route,
12
+ RouteHandler,
13
+ } from '../types'
14
+ import type { Router } from './router'
15
+ import { registerNamedRoute } from '../url'
16
+ import { matchPath } from '../utils'
17
+
18
+ // ============================================================================
19
+ // Types
20
+ // ============================================================================
21
+
22
+ /**
23
+ * Controller class interface
24
+ */
25
+ export interface ControllerClass {
26
+ new (): Controller
27
+ }
28
+
29
+ /**
30
+ * Base controller interface
31
+ */
32
+ export interface Controller {
33
+ [key: string]: RouteHandler | unknown
34
+ }
35
+
36
+ /**
37
+ * Resource controller methods
38
+ */
39
+ export type ResourceMethod = 'index' | 'create' | 'store' | 'show' | 'edit' | 'update' | 'destroy'
40
+
41
+ /**
42
+ * API resource methods (no create/edit views)
43
+ */
44
+ export type ApiResourceMethod = 'index' | 'store' | 'show' | 'update' | 'destroy'
45
+
46
+ /**
47
+ * Singleton resource methods
48
+ */
49
+ export type SingletonMethod = 'show' | 'edit' | 'update'
50
+
51
+ /**
52
+ * Resource configuration
53
+ */
54
+ export interface ResourceOptions {
55
+ only?: ResourceMethod[]
56
+ except?: ResourceMethod[]
57
+ middleware?: (string | MiddlewareHandler)[]
58
+ names?: Partial<Record<ResourceMethod, string>>
59
+ parameters?: Record<string, string>
60
+ shallow?: boolean
61
+ }
62
+
63
+ /**
64
+ * Route builder for fluent API
65
+ */
66
+ export interface FluentRoute {
67
+ name: (name: string) => FluentRoute
68
+ middleware: (...middleware: (string | MiddlewareHandler)[]) => FluentRoute
69
+ withoutMiddleware: (...middleware: string[]) => FluentRoute
70
+ where: (param: string, pattern: string | RegExp) => FluentRoute
71
+ whereNumber: (param: string) => FluentRoute
72
+ whereAlpha: (param: string) => FluentRoute
73
+ whereAlphaNumeric: (param: string) => FluentRoute
74
+ whereUuid: (param: string) => FluentRoute
75
+ whereSlug: (param: string) => FluentRoute
76
+ can: (ability: string, model?: string) => FluentRoute
77
+ missing: (handler: (req: EnhancedRequest) => Response) => FluentRoute
78
+ withTrashed: () => FluentRoute
79
+ scopeBindings: () => FluentRoute
80
+ }
81
+
82
+ /**
83
+ * Group builder for fluent API
84
+ */
85
+ export interface GroupBuilder {
86
+ prefix: (prefix: string) => GroupBuilder
87
+ name: (name: string) => GroupBuilder
88
+ middleware: (...middleware: (string | MiddlewareHandler)[]) => GroupBuilder
89
+ domain: (domain: string) => GroupBuilder
90
+ controller: (controller: ControllerClass) => GroupBuilder
91
+ routes: (callback: (router: FluentRouter) => void) => void
92
+ }
93
+
94
+ // ============================================================================
95
+ // Fluent Router Implementation
96
+ // ============================================================================
97
+
98
+ /**
99
+ * Fluent router with chainable API
100
+ */
101
+ export class FluentRouter {
102
+ private router: Router
103
+ private currentPrefix: string = ''
104
+ private currentNamePrefix: string = ''
105
+ private currentMiddleware: (string | MiddlewareHandler)[] = []
106
+ private currentDomain: string | null = null
107
+ private currentController: ControllerClass | null = null
108
+ private excludedMiddleware: Set<string> = new Set()
109
+
110
+ constructor(router: Router) {
111
+ this.router = router
112
+ }
113
+
114
+ // ============================================================================
115
+ // HTTP Methods
116
+ // ============================================================================
117
+
118
+ /**
119
+ * Register a GET route
120
+ */
121
+ get(path: string, handler: RouteHandler | string | Response): FluentRouteBuilder {
122
+ return this.addRoute('GET', path, handler)
123
+ }
124
+
125
+ /**
126
+ * Register a POST route
127
+ */
128
+ post(path: string, handler: RouteHandler | string): FluentRouteBuilder {
129
+ return this.addRoute('POST', path, handler)
130
+ }
131
+
132
+ /**
133
+ * Register a PUT route
134
+ */
135
+ put(path: string, handler: RouteHandler | string): FluentRouteBuilder {
136
+ return this.addRoute('PUT', path, handler)
137
+ }
138
+
139
+ /**
140
+ * Register a PATCH route
141
+ */
142
+ patch(path: string, handler: RouteHandler | string): FluentRouteBuilder {
143
+ return this.addRoute('PATCH', path, handler)
144
+ }
145
+
146
+ /**
147
+ * Register a DELETE route
148
+ */
149
+ delete(path: string, handler: RouteHandler | string): FluentRouteBuilder {
150
+ return this.addRoute('DELETE', path, handler)
151
+ }
152
+
153
+ /**
154
+ * Register an OPTIONS route
155
+ */
156
+ options(path: string, handler: RouteHandler | string): FluentRouteBuilder {
157
+ return this.addRoute('OPTIONS', path, handler)
158
+ }
159
+
160
+ /**
161
+ * Register a route for any HTTP method
162
+ */
163
+ any(path: string, handler: RouteHandler | string): FluentRouteBuilder {
164
+ return this.addRoute('*', path, handler)
165
+ }
166
+
167
+ /**
168
+ * Register a route for multiple HTTP methods
169
+ */
170
+ match(methods: string[], path: string, handler: RouteHandler | string): FluentRouteBuilder {
171
+ const fullPath = this.buildPath(path)
172
+ const resolvedHandler = this.resolveHandler(handler)
173
+ const middleware = this.buildMiddleware()
174
+
175
+ for (const method of methods) {
176
+ const route: Route = {
177
+ path: fullPath,
178
+ method: method.toUpperCase(),
179
+ handler: resolvedHandler,
180
+ middleware,
181
+ domain: this.currentDomain || undefined,
182
+ pattern: this.createPattern(fullPath),
183
+ }
184
+ // Use direct push to routes array to avoid conflict with http-methods.ts addRoute
185
+ this.router.routes.push(route)
186
+ }
187
+
188
+ return new FluentRouteBuilder(this.router, fullPath, methods[0])
189
+ }
190
+
191
+ // ============================================================================
192
+ // Resource Routes
193
+ // ============================================================================
194
+
195
+ /**
196
+ * Register a resource controller
197
+ */
198
+ resource(name: string, Controller: ControllerClass, options: ResourceOptions = {}): this {
199
+ const methods = this.getResourceMethods(options)
200
+ const paramName = options.parameters?.[name] || 'id'
201
+
202
+ const controllerInstance = new Controller()
203
+
204
+ for (const method of methods) {
205
+ const { httpMethod, path, handlerName } = this.getResourceRoute(name, method, paramName)
206
+ const handler = controllerInstance[handlerName] as RouteHandler | undefined
207
+
208
+ if (handler) {
209
+ const fullPath = this.buildPath(path)
210
+ const routeName = options.names?.[method] || `${name}.${method}`
211
+
212
+ const route: Route = {
213
+ path: fullPath,
214
+ method: httpMethod,
215
+ handler,
216
+ middleware: this.buildMiddleware(options.middleware),
217
+ name: this.currentNamePrefix + routeName,
218
+ domain: this.currentDomain || undefined,
219
+ pattern: this.createPattern(fullPath),
220
+ }
221
+ // Use direct push to routes array to avoid conflict with http-methods.ts addRoute
222
+ this.router.routes.push(route)
223
+ if (route.name) {
224
+ this.router.namedRoutes.set(route.name, route)
225
+ registerNamedRoute(route.name, route.path)
226
+ }
227
+ }
228
+ }
229
+
230
+ return this
231
+ }
232
+
233
+ /**
234
+ * Register an API resource controller (no create/edit views)
235
+ */
236
+ apiResource(name: string, controller: ControllerClass, options: ResourceOptions = {}): this {
237
+ const apiMethods: ApiResourceMethod[] = ['index', 'store', 'show', 'update', 'destroy']
238
+ const filteredOptions: ResourceOptions = {
239
+ ...options,
240
+ only: options.only?.filter((m): m is ResourceMethod => apiMethods.includes(m as ApiResourceMethod)) || apiMethods,
241
+ }
242
+ return this.resource(name, controller, filteredOptions)
243
+ }
244
+
245
+ /**
246
+ * Register a singleton resource (no index, create, store, destroy)
247
+ */
248
+ singleton(name: string, Controller: ControllerClass, options: ResourceOptions = {}): this {
249
+ const singletonMethods: SingletonMethod[] = ['show', 'edit', 'update']
250
+ const controllerInstance = new Controller()
251
+
252
+ for (const method of singletonMethods) {
253
+ if (options.except?.includes(method))
254
+ continue
255
+ if (options.only && !options.only.includes(method))
256
+ continue
257
+
258
+ const { httpMethod, handlerName } = this.getSingletonRoute(name, method)
259
+ const handler = controllerInstance[handlerName] as RouteHandler | undefined
260
+
261
+ if (handler) {
262
+ const fullPath = this.buildPath(`/${name}${method === 'edit' ? '/edit' : ''}`)
263
+ const routeName = options.names?.[method] || `${name}.${method}`
264
+
265
+ const route: Route = {
266
+ path: fullPath,
267
+ method: httpMethod,
268
+ handler,
269
+ middleware: this.buildMiddleware(options.middleware),
270
+ name: this.currentNamePrefix + routeName,
271
+ domain: this.currentDomain || undefined,
272
+ pattern: this.createPattern(fullPath),
273
+ }
274
+ // Use direct push to routes array to avoid conflict with http-methods.ts addRoute
275
+ this.router.routes.push(route)
276
+ if (route.name) {
277
+ this.router.namedRoutes.set(route.name, route)
278
+ registerNamedRoute(route.name, route.path)
279
+ }
280
+ }
281
+ }
282
+
283
+ return this
284
+ }
285
+
286
+ // ============================================================================
287
+ // Group Methods
288
+ // ============================================================================
289
+
290
+ /**
291
+ * Create a route group with prefix
292
+ */
293
+ prefix(prefix: string): GroupBuilderImpl {
294
+ return new GroupBuilderImpl(this).prefix(prefix)
295
+ }
296
+
297
+ /**
298
+ * Create a route group with name prefix
299
+ */
300
+ name(namePrefix: string): GroupBuilderImpl {
301
+ return new GroupBuilderImpl(this).name(namePrefix)
302
+ }
303
+
304
+ /**
305
+ * Create a route group with middleware
306
+ */
307
+ middleware(...middleware: (string | MiddlewareHandler)[]): GroupBuilderImpl {
308
+ return new GroupBuilderImpl(this).middleware(...middleware)
309
+ }
310
+
311
+ /**
312
+ * Create a route group for a domain
313
+ */
314
+ domain(domain: string): GroupBuilderImpl {
315
+ return new GroupBuilderImpl(this).domain(domain)
316
+ }
317
+
318
+ /**
319
+ * Create a route group with a controller
320
+ */
321
+ controller(controller: ControllerClass): GroupBuilderImpl {
322
+ return new GroupBuilderImpl(this).controller(controller)
323
+ }
324
+
325
+ /**
326
+ * Create a route group
327
+ */
328
+ group(options: GroupOptions, callback: (router: FluentRouter) => void): this {
329
+ const previousPrefix = this.currentPrefix
330
+ const previousNamePrefix = this.currentNamePrefix
331
+ const previousMiddleware = [...this.currentMiddleware]
332
+ const previousDomain = this.currentDomain
333
+ const previousController = this.currentController
334
+
335
+ if (options.prefix)
336
+ this.currentPrefix = this.buildPath(options.prefix)
337
+ if (options.as)
338
+ this.currentNamePrefix = this.currentNamePrefix + options.as
339
+ if (options.middleware)
340
+ this.currentMiddleware.push(...options.middleware)
341
+ if (options.domain)
342
+ this.currentDomain = options.domain
343
+ if (options.controller)
344
+ this.currentController = options.controller
345
+
346
+ callback(this)
347
+
348
+ this.currentPrefix = previousPrefix
349
+ this.currentNamePrefix = previousNamePrefix
350
+ this.currentMiddleware = previousMiddleware
351
+ this.currentDomain = previousDomain
352
+ this.currentController = previousController
353
+
354
+ return this
355
+ }
356
+
357
+ // ============================================================================
358
+ // Route Information
359
+ // ============================================================================
360
+
361
+ /**
362
+ * Check if a named route exists
363
+ */
364
+ has(name: string): boolean {
365
+ return this.router.namedRoutes.has(name)
366
+ }
367
+
368
+ /**
369
+ * Get the current route (from request context)
370
+ */
371
+ current(): Route | null {
372
+ // This would need to be set during request handling
373
+ return null
374
+ }
375
+
376
+ /**
377
+ * Get the current route name
378
+ */
379
+ currentRouteName(): string | null {
380
+ const route = this.current()
381
+ return route?.name || null
382
+ }
383
+
384
+ /**
385
+ * Get URL for a named route
386
+ */
387
+ route(name: string, params: Record<string, string | number> = {}): string {
388
+ const route = this.router.namedRoutes.get(name)
389
+ if (!route) {
390
+ throw new Error(`Route [${name}] not defined.`)
391
+ }
392
+
393
+ let url = route.path
394
+ for (const [key, value] of Object.entries(params)) {
395
+ url = url.replace(`{${key}}`, String(value))
396
+ url = url.replace(`:${key}`, String(value))
397
+ }
398
+
399
+ return url
400
+ }
401
+
402
+ // ============================================================================
403
+ // Fallback & Error Handling
404
+ // ============================================================================
405
+
406
+ /**
407
+ * Register a fallback route
408
+ */
409
+ fallback(handler: RouteHandler): this {
410
+ this.router.fallbackHandler = handler
411
+ return this
412
+ }
413
+
414
+ // ============================================================================
415
+ // Private Methods
416
+ // ============================================================================
417
+
418
+ private addRoute(method: string, path: string, handler: RouteHandler | string | Response): FluentRouteBuilder {
419
+ const fullPath = this.buildPath(path)
420
+
421
+ // If handler is a Response object, register for Bun's native static dispatch
422
+ if (handler instanceof Response) {
423
+ this.router.staticResponses.set(fullPath, handler)
424
+ }
425
+
426
+ const resolvedHandler = handler instanceof Response ? handler : this.resolveHandler(handler)
427
+ const middleware = this.buildMiddleware()
428
+
429
+ const route: Route = {
430
+ path: fullPath,
431
+ method: method.toUpperCase(),
432
+ handler: resolvedHandler,
433
+ middleware,
434
+ domain: this.currentDomain || undefined,
435
+ pattern: this.createPattern(fullPath),
436
+ }
437
+
438
+ // Use direct push to routes array to avoid conflict with http-methods.ts addRoute
439
+ this.router.routes.push(route)
440
+ if (route.name) {
441
+ this.router.namedRoutes.set(route.name, route)
442
+ registerNamedRoute(route.name, route.path)
443
+ }
444
+ return new FluentRouteBuilder(this.router, fullPath, method)
445
+ }
446
+
447
+ /**
448
+ * Create a pattern object for route matching
449
+ */
450
+ private createPattern(routePath: string): { exec: (url: URL) => { pathname: { groups: Record<string, string> } } | null } {
451
+ return {
452
+ exec: (url: URL): { pathname: { groups: Record<string, string> } } | null => {
453
+ const params: Record<string, string> = {}
454
+ const isMatch = matchPath(routePath, url.pathname, params)
455
+
456
+ if (!isMatch) {
457
+ return null
458
+ }
459
+
460
+ return {
461
+ pathname: {
462
+ groups: params,
463
+ },
464
+ }
465
+ },
466
+ }
467
+ }
468
+
469
+ private buildPath(path: string): string {
470
+ if (this.currentPrefix) {
471
+ return `${this.currentPrefix}${path.startsWith('/') ? path : `/${path}`}`
472
+ }
473
+ return path.startsWith('/') ? path : `/${path}`
474
+ }
475
+
476
+ private buildMiddleware(additional?: (string | MiddlewareHandler)[]): MiddlewareHandler[] {
477
+ const allMiddleware = [...this.currentMiddleware, ...(additional || [])]
478
+ return allMiddleware
479
+ .filter(m => typeof m !== 'string' || !this.excludedMiddleware.has(m))
480
+ .map(m => this.resolveMiddleware(m))
481
+ }
482
+
483
+ private resolveHandler(handler: RouteHandler | string): ActionHandler {
484
+ if (typeof handler === 'string') {
485
+ if (this.currentController) {
486
+ const ControllerClass = this.currentController
487
+ const controllerInstance = new ControllerClass()
488
+ const method = controllerInstance[handler] as RouteHandler | undefined
489
+ if (method) {
490
+ return method.bind(controllerInstance)
491
+ }
492
+ }
493
+ return handler as ActionHandler
494
+ }
495
+ return handler
496
+ }
497
+
498
+ private resolveMiddleware(middleware: string | MiddlewareHandler): MiddlewareHandler {
499
+ if (typeof middleware === 'function') {
500
+ return middleware
501
+ }
502
+ // String middleware would be resolved from named middleware registry
503
+ return async (_req, next) => next()
504
+ }
505
+
506
+ private getResourceMethods(options: ResourceOptions): ResourceMethod[] {
507
+ const allMethods: ResourceMethod[] = ['index', 'create', 'store', 'show', 'edit', 'update', 'destroy']
508
+
509
+ if (options.only) {
510
+ return options.only
511
+ }
512
+
513
+ if (options.except) {
514
+ return allMethods.filter(m => !options.except!.includes(m))
515
+ }
516
+
517
+ return allMethods
518
+ }
519
+
520
+ private getResourceRoute(name: string, method: ResourceMethod, paramName: string): { httpMethod: string, path: string, handlerName: string } {
521
+ const routes: Record<ResourceMethod, { httpMethod: string, path: string, handlerName: string }> = {
522
+ index: { httpMethod: 'GET', path: `/${name}`, handlerName: 'index' },
523
+ create: { httpMethod: 'GET', path: `/${name}/create`, handlerName: 'create' },
524
+ store: { httpMethod: 'POST', path: `/${name}`, handlerName: 'store' },
525
+ show: { httpMethod: 'GET', path: `/${name}/{${paramName}}`, handlerName: 'show' },
526
+ edit: { httpMethod: 'GET', path: `/${name}/{${paramName}}/edit`, handlerName: 'edit' },
527
+ update: { httpMethod: 'PUT', path: `/${name}/{${paramName}}`, handlerName: 'update' },
528
+ destroy: { httpMethod: 'DELETE', path: `/${name}/{${paramName}}`, handlerName: 'destroy' },
529
+ }
530
+
531
+ return routes[method]
532
+ }
533
+
534
+ private getSingletonRoute(name: string, method: SingletonMethod): { httpMethod: string, handlerName: string } {
535
+ const routes: Record<SingletonMethod, { httpMethod: string, handlerName: string }> = {
536
+ show: { httpMethod: 'GET', handlerName: 'show' },
537
+ edit: { httpMethod: 'GET', handlerName: 'edit' },
538
+ update: { httpMethod: 'PUT', handlerName: 'update' },
539
+ }
540
+
541
+ return routes[method]
542
+ }
543
+ }
544
+
545
+ // ============================================================================
546
+ // Fluent Route Builder
547
+ // ============================================================================
548
+
549
+ /**
550
+ * Fluent route builder for chaining route configuration
551
+ */
552
+ export class FluentRouteBuilder implements FluentRoute {
553
+ private router: Router
554
+ private path: string
555
+ private method: string
556
+ private routeName: string | null = null
557
+ private routeMiddleware: MiddlewareHandler[] = []
558
+ private excludedMiddleware: string[] = []
559
+ private constraints: Record<string, string | RegExp> = {}
560
+ private missingHandler: ((_req: EnhancedRequest) => Response) | null = null
561
+ private includeTrashed: boolean = false
562
+ private scopeBindingsEnabled: boolean = false
563
+ private ability: string | null = null
564
+ private abilityModel: string | null = null
565
+
566
+ constructor(router: Router, path: string, method: string) {
567
+ this.router = router
568
+ this.path = path
569
+ this.method = method
570
+ }
571
+
572
+ /**
573
+ * Set the route name
574
+ */
575
+ name(name: string): this {
576
+ this.routeName = name
577
+ // Update the route in the router
578
+ const route = this.findRoute()
579
+ if (route) {
580
+ route.name = name
581
+ this.router.namedRoutes.set(name, route)
582
+ registerNamedRoute(name, route.path)
583
+ }
584
+ return this
585
+ }
586
+
587
+ /**
588
+ * Add middleware to the route
589
+ */
590
+ middleware(...middleware: (string | MiddlewareHandler)[]): this {
591
+ const route = this.findRoute()
592
+ if (route) {
593
+ for (const m of middleware) {
594
+ if (typeof m === 'function') {
595
+ route.middleware.push(m)
596
+ }
597
+ }
598
+ }
599
+ return this
600
+ }
601
+
602
+ /**
603
+ * Exclude middleware from the route
604
+ */
605
+ withoutMiddleware(...middleware: string[]): this {
606
+ this.excludedMiddleware.push(...middleware)
607
+ const route = this.findRoute()
608
+ if (route) {
609
+ route.middleware = route.middleware.filter((m) => {
610
+ const name = m.name || ''
611
+ return !this.excludedMiddleware.includes(name)
612
+ })
613
+ }
614
+ return this
615
+ }
616
+
617
+ /**
618
+ * Add a parameter constraint
619
+ */
620
+ where(param: string, pattern: string | RegExp): this {
621
+ this.constraints[param] = pattern
622
+ const route = this.findRoute()
623
+ if (route) {
624
+ route.constraints = { ...route.constraints, [param]: String(pattern) }
625
+ }
626
+ return this
627
+ }
628
+
629
+ /**
630
+ * Constrain parameter to numbers
631
+ */
632
+ whereNumber(param: string): this {
633
+ return this.where(param, '[0-9]+')
634
+ }
635
+
636
+ /**
637
+ * Constrain parameter to letters
638
+ */
639
+ whereAlpha(param: string): this {
640
+ return this.where(param, '[a-zA-Z]+')
641
+ }
642
+
643
+ /**
644
+ * Constrain parameter to alphanumeric
645
+ */
646
+ whereAlphaNumeric(param: string): this {
647
+ return this.where(param, '[a-zA-Z0-9]+')
648
+ }
649
+
650
+ /**
651
+ * Constrain parameter to UUID
652
+ */
653
+ whereUuid(param: string): this {
654
+ return this.where(param, '[0-9a-fA-F]{8}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{4}-[0-9a-fA-F]{12}')
655
+ }
656
+
657
+ /**
658
+ * Constrain parameter to slug format
659
+ */
660
+ whereSlug(param: string): this {
661
+ return this.where(param, '[a-z0-9]+(?:-[a-z0-9]+)*')
662
+ }
663
+
664
+ /**
665
+ * Add authorization check
666
+ */
667
+ can(ability: string, model?: string): this {
668
+ this.ability = ability
669
+ this.abilityModel = model || null
670
+ // Add authorization middleware
671
+ const authMiddleware: MiddlewareHandler = async (req, next) => {
672
+ // Check if user can perform ability
673
+ const user = req.user
674
+ if (!user) {
675
+ return new Response('Unauthorized', { status: 401 })
676
+ }
677
+ // Authorization logic would go here
678
+ return next()
679
+ }
680
+ const route = this.findRoute()
681
+ if (route) {
682
+ route.middleware.push(authMiddleware)
683
+ }
684
+ return this
685
+ }
686
+
687
+ /**
688
+ * Handle missing model binding
689
+ */
690
+ missing(handler: (req: EnhancedRequest) => Response): this {
691
+ this.missingHandler = handler
692
+ return this
693
+ }
694
+
695
+ /**
696
+ * Include soft-deleted models
697
+ */
698
+ withTrashed(): this {
699
+ this.includeTrashed = true
700
+ return this
701
+ }
702
+
703
+ /**
704
+ * Enable scoped bindings
705
+ */
706
+ scopeBindings(): this {
707
+ this.scopeBindingsEnabled = true
708
+ return this
709
+ }
710
+
711
+ private findRoute(): Route | undefined {
712
+ return this.router.routes.find(r =>
713
+ r.path === this.path && r.method === this.method.toUpperCase(),
714
+ )
715
+ }
716
+ }
717
+
718
+ // ============================================================================
719
+ // Group Builder Implementation
720
+ // ============================================================================
721
+
722
+ interface GroupOptions {
723
+ prefix?: string
724
+ as?: string
725
+ middleware?: (string | MiddlewareHandler)[]
726
+ domain?: string
727
+ controller?: ControllerClass
728
+ }
729
+
730
+ class GroupBuilderImpl implements GroupBuilder {
731
+ private fluentRouter: FluentRouter
732
+ private options: GroupOptions = {}
733
+
734
+ constructor(router: FluentRouter) {
735
+ this.fluentRouter = router
736
+ }
737
+
738
+ prefix(prefix: string): this {
739
+ this.options.prefix = prefix
740
+ return this
741
+ }
742
+
743
+ name(name: string): this {
744
+ this.options.as = name
745
+ return this
746
+ }
747
+
748
+ middleware(...middleware: (string | MiddlewareHandler)[]): this {
749
+ this.options.middleware = [...(this.options.middleware || []), ...middleware]
750
+ return this
751
+ }
752
+
753
+ domain(domain: string): this {
754
+ this.options.domain = domain
755
+ return this
756
+ }
757
+
758
+ controller(controller: ControllerClass): this {
759
+ this.options.controller = controller
760
+ return this
761
+ }
762
+
763
+ routes(callback: (router: FluentRouter) => void): void {
764
+ this.fluentRouter.group(this.options, callback)
765
+ }
766
+ }
767
+
768
+ // ============================================================================
769
+ // Router Extension
770
+ // ============================================================================
771
+
772
+ /**
773
+ * Extend Router with fluent routing methods
774
+ */
775
+ export function registerFluentRouting(RouterClass: typeof import('./router').Router): void {
776
+ const proto = RouterClass.prototype as Router & {
777
+ fluent?: () => FluentRouter
778
+ resourceController?: (name: string, controller: ControllerClass, options?: ResourceOptions) => Router
779
+ apiResourceController?: (name: string, controller: ControllerClass, options?: ResourceOptions) => Router
780
+ singletonResource?: (name: string, controller: ControllerClass, options?: ResourceOptions) => Router
781
+ hasRoute?: (name: string) => boolean
782
+ currentRoute?: () => Route | null
783
+ currentRouteName?: () => string | null
784
+ routeUrl?: (name: string, params?: Record<string, string | number>) => string
785
+ }
786
+
787
+ /**
788
+ * Get fluent router instance
789
+ */
790
+ proto.fluent = function (this: Router): FluentRouter {
791
+ return new FluentRouter(this)
792
+ }
793
+
794
+ /**
795
+ * Register a controller-based resource
796
+ */
797
+ proto.resourceController = function (
798
+ this: Router,
799
+ name: string,
800
+ controller: ControllerClass,
801
+ options?: ResourceOptions,
802
+ ): Router {
803
+ new FluentRouter(this).resource(name, controller, options)
804
+ return this
805
+ }
806
+
807
+ /**
808
+ * Register an API resource controller
809
+ */
810
+ proto.apiResourceController = function (
811
+ this: Router,
812
+ name: string,
813
+ controller: ControllerClass,
814
+ options?: ResourceOptions,
815
+ ): Router {
816
+ new FluentRouter(this).apiResource(name, controller, options)
817
+ return this
818
+ }
819
+
820
+ /**
821
+ * Register a singleton resource
822
+ */
823
+ proto.singletonResource = function (
824
+ this: Router,
825
+ name: string,
826
+ controller: ControllerClass,
827
+ options?: ResourceOptions,
828
+ ): Router {
829
+ new FluentRouter(this).singleton(name, controller, options)
830
+ return this
831
+ }
832
+
833
+ /**
834
+ * Check if a named route exists
835
+ */
836
+ proto.hasRoute = function (this: Router, name: string): boolean {
837
+ return this.namedRoutes.has(name)
838
+ }
839
+
840
+ /**
841
+ * Get the current route
842
+ */
843
+ // eslint-disable-next-line pickier/no-unused-vars
844
+ proto.currentRoute = function (this: Router): Route | null {
845
+ // Would need request context
846
+ return null
847
+ }
848
+
849
+ /**
850
+ * Get the current route name
851
+ */
852
+ proto.currentRouteName = function (this: Router): string | null {
853
+ const route = this.currentRoute?.()
854
+ return route?.name || null
855
+ }
856
+
857
+ /**
858
+ * Get URL for a named route
859
+ */
860
+ proto.routeUrl = function (
861
+ this: Router,
862
+ name: string,
863
+ params: Record<string, string | number> = {},
864
+ ): string {
865
+ const route = this.namedRoutes.get(name)
866
+ if (!route) {
867
+ throw new Error(`Route [${name}] not defined.`)
868
+ }
869
+
870
+ let url = route.path
871
+ for (const [key, value] of Object.entries(params)) {
872
+ url = url.replace(`{${key}}`, String(value))
873
+ url = url.replace(`:${key}`, String(value))
874
+ }
875
+
876
+ return url
877
+ }
878
+ }
879
+
880
+ // ============================================================================
881
+ // Type Augmentation
882
+ // ============================================================================
883
+
884
+ declare module './router' {
885
+ interface Router {
886
+ /**
887
+ * Get fluent router instance
888
+ */
889
+ fluent: () => FluentRouter
890
+
891
+ /**
892
+ * Register a controller-based resource
893
+ * Note: Use `resourceController` to avoid conflict with existing `resource` method
894
+ */
895
+ resourceController: (name: string, controller: ControllerClass, options?: ResourceOptions) => Router
896
+
897
+ /**
898
+ * Register an API resource controller (no create/edit views)
899
+ */
900
+ apiResourceController: (name: string, controller: ControllerClass, options?: ResourceOptions) => Router
901
+
902
+ /**
903
+ * Register a singleton resource
904
+ */
905
+ singletonResource: (name: string, controller: ControllerClass, options?: ResourceOptions) => Router
906
+
907
+ /**
908
+ * Check if a named route exists
909
+ */
910
+ hasRoute: (name: string) => boolean
911
+
912
+ /**
913
+ * Get the current route
914
+ */
915
+ currentRoute: () => Route | null
916
+
917
+ /**
918
+ * Get the current route name
919
+ */
920
+ currentRouteName: () => string | null
921
+
922
+ /**
923
+ * Get URL for a named route
924
+ */
925
+ routeUrl: (name: string, params?: Record<string, string | number>) => string
926
+ }
927
+ }