@stacksjs/bun-router 0.0.13 → 0.0.14

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 (273) hide show
  1. package/CHANGELOG.md +770 -0
  2. package/README.md +584 -261
  3. package/package.json +43 -47
  4. package/bin/cli.ts +0 -9
  5. package/dist/auth.d.ts +0 -134
  6. package/dist/cache/lru-cache.d.ts +0 -139
  7. package/dist/cache/middleware-memoization.d.ts +0 -151
  8. package/dist/cache/route-cache-warmer.d.ts +0 -161
  9. package/dist/cache/sqlite-cache.d.ts +0 -209
  10. package/dist/cache/streaming-cache.d.ts +0 -132
  11. package/dist/chunk-1mvtv6jw.js +0 -13012
  12. package/dist/cli/colors.d.ts +0 -15
  13. package/dist/cli/index.d.ts +0 -10
  14. package/dist/cli/middleware.d.ts +0 -31
  15. package/dist/cli/openapi.d.ts +0 -17
  16. package/dist/cli/router.d.ts +0 -15
  17. package/dist/cli/routes.d.ts +0 -29
  18. package/dist/cli/utils.d.ts +0 -74
  19. package/dist/cli.d.ts +0 -1
  20. package/dist/cli.js +0 -3195
  21. package/dist/config.d.ts +0 -7
  22. package/dist/container/container.d.ts +0 -273
  23. package/dist/container/contextual-binding.d.ts +0 -240
  24. package/dist/container/decorators.d.ts +0 -141
  25. package/dist/container/service-provider.d.ts +0 -285
  26. package/dist/development/hot-reload.d.ts +0 -187
  27. package/dist/development/index.d.ts +0 -167
  28. package/dist/development/performance-profiler.d.ts +0 -217
  29. package/dist/development/route-debugger.d.ts +0 -154
  30. package/dist/development/route-inspector.d.ts +0 -211
  31. package/dist/development/typescript-utilities.d.ts +0 -209
  32. package/dist/docs.d.ts +0 -11
  33. package/dist/errors/circuit-breaker.d.ts +0 -195
  34. package/dist/errors/error-handler.d.ts +0 -88
  35. package/dist/errors/error-reporting.d.ts +0 -144
  36. package/dist/errors/exceptions.d.ts +0 -184
  37. package/dist/errors/graceful-degradation.d.ts +0 -154
  38. package/dist/errors/index.d.ts +0 -9
  39. package/dist/errors/router-errors.d.ts +0 -110
  40. package/dist/file-serving/static-files.d.ts +0 -143
  41. package/dist/index.d.ts +0 -14
  42. package/dist/index.js +0 -13933
  43. package/dist/middleware/auth.d.ts +0 -63
  44. package/dist/middleware/content_security_policy.d.ts +0 -53
  45. package/dist/middleware/cors.d.ts +0 -5
  46. package/dist/middleware/csrf.d.ts +0 -7
  47. package/dist/middleware/ddos_protection.d.ts +0 -39
  48. package/dist/middleware/file_security.d.ts +0 -26
  49. package/dist/middleware/file_upload.d.ts +0 -38
  50. package/dist/middleware/helmet.d.ts +0 -54
  51. package/dist/middleware/index.d.ts +0 -44
  52. package/dist/middleware/input_validation.d.ts +0 -45
  53. package/dist/middleware/json_body.d.ts +0 -4
  54. package/dist/middleware/performance_alerting.d.ts +0 -87
  55. package/dist/middleware/performance_dashboard.d.ts +0 -87
  56. package/dist/middleware/performance_monitor.d.ts +0 -209
  57. package/dist/middleware/pipeline.d.ts +0 -131
  58. package/dist/middleware/rate_limit.d.ts +0 -36
  59. package/dist/middleware/request_id.d.ts +0 -4
  60. package/dist/middleware/request_signing.d.ts +0 -153
  61. package/dist/middleware/request_tracer.d.ts +0 -72
  62. package/dist/middleware/response_cache.d.ts +0 -97
  63. package/dist/middleware/security.d.ts +0 -69
  64. package/dist/middleware/security_suite.d.ts +0 -46
  65. package/dist/middleware/session.d.ts +0 -23
  66. package/dist/model-binding/index.d.ts +0 -2
  67. package/dist/model-binding/model-middleware.d.ts +0 -118
  68. package/dist/model-binding/model-registry.d.ts +0 -164
  69. package/dist/model-binding.d.ts +0 -185
  70. package/dist/model-resolver-factory.d.ts +0 -31
  71. package/dist/observability/correlation.d.ts +0 -187
  72. package/dist/observability/health-checks.d.ts +0 -186
  73. package/dist/observability/index.d.ts +0 -60
  74. package/dist/observability/integration.d.ts +0 -147
  75. package/dist/observability/metrics.d.ts +0 -184
  76. package/dist/observability/tracing.d.ts +0 -187
  77. package/dist/optimization/bun-utilities.d.ts +0 -224
  78. package/dist/query-builder-integration.d.ts +0 -36
  79. package/dist/request/context.d.ts +0 -34
  80. package/dist/request/enhanced-request.d.ts +0 -213
  81. package/dist/request/macros.d.ts +0 -300
  82. package/dist/response/macros.d.ts +0 -245
  83. package/dist/response/response-factory.d.ts +0 -133
  84. package/dist/router/api-routes.d.ts +0 -5
  85. package/dist/router/file-based-routing.d.ts +0 -118
  86. package/dist/router/file-streaming.d.ts +0 -5
  87. package/dist/router/fluent-router.d.ts +0 -315
  88. package/dist/router/fluent-routing.d.ts +0 -271
  89. package/dist/router/group-organization.d.ts +0 -5
  90. package/dist/router/handler-resolver.d.ts +0 -24
  91. package/dist/router/http-methods.d.ts +0 -10
  92. package/dist/router/index.d.ts +0 -114
  93. package/dist/router/middleware-groups.d.ts +0 -94
  94. package/dist/router/middleware-integration.d.ts +0 -112
  95. package/dist/router/middleware.d.ts +0 -5
  96. package/dist/router/model-binding.d.ts +0 -5
  97. package/dist/router/optimized-route-matching.d.ts +0 -6
  98. package/dist/router/route-building.d.ts +0 -5
  99. package/dist/router/route-compiler.d.ts +0 -153
  100. package/dist/router/route-matching.d.ts +0 -5
  101. package/dist/router/route-trie.d.ts +0 -114
  102. package/dist/router/router.d.ts +0 -294
  103. package/dist/router/server.d.ts +0 -5
  104. package/dist/router/validation-integration.d.ts +0 -170
  105. package/dist/router/view-rendering.d.ts +0 -5
  106. package/dist/router/websocket.d.ts +0 -5
  107. package/dist/routing/route-caching.d.ts +0 -129
  108. package/dist/routing/route-throttling.d.ts +0 -150
  109. package/dist/routing/subdomain-routing.d.ts +0 -206
  110. package/dist/session/database-store.d.ts +0 -51
  111. package/dist/session/file-store.d.ts +0 -22
  112. package/dist/session/index.d.ts +0 -106
  113. package/dist/session/memory-store.d.ts +0 -21
  114. package/dist/session/redis-store.d.ts +0 -31
  115. package/dist/streaming/index.d.ts +0 -3
  116. package/dist/streaming/sse-handler.d.ts +0 -138
  117. package/dist/streaming/stream-handler.d.ts +0 -114
  118. package/dist/testing/auth-testing.d.ts +0 -156
  119. package/dist/testing/file-upload-testing.d.ts +0 -190
  120. package/dist/testing/index.d.ts +0 -10
  121. package/dist/testing/middleware-testing.d.ts +0 -138
  122. package/dist/testing/model-binding-testing.d.ts +0 -186
  123. package/dist/testing/performance-testing.d.ts +0 -235
  124. package/dist/testing/test-client.d.ts +0 -117
  125. package/dist/testing/test-request.d.ts +0 -85
  126. package/dist/testing/test-response.d.ts +0 -90
  127. package/dist/testing/types.d.ts +0 -207
  128. package/dist/testing/websocket-testing.d.ts +0 -227
  129. package/dist/types/controller-types.d.ts +0 -208
  130. package/dist/types/core.d.ts +0 -543
  131. package/dist/types/middleware-types.d.ts +0 -227
  132. package/dist/types/request-response-augmentation.d.ts +0 -261
  133. package/dist/types/route-inference.d.ts +0 -168
  134. package/dist/types.d.ts +0 -1782
  135. package/dist/url.d.ts +0 -56
  136. package/dist/utils/index.d.ts +0 -1
  137. package/dist/utils/query-preservation.d.ts +0 -48
  138. package/dist/utils.d.ts +0 -69
  139. package/dist/validation/validator.d.ts +0 -140
  140. package/dist/websocket/clustering.d.ts +0 -185
  141. package/src/auth.ts +0 -469
  142. package/src/cache/lru-cache.ts +0 -457
  143. package/src/cache/middleware-memoization.ts +0 -531
  144. package/src/cache/route-cache-warmer.ts +0 -486
  145. package/src/cache/sqlite-cache.ts +0 -783
  146. package/src/cache/streaming-cache.ts +0 -572
  147. package/src/cli/colors.ts +0 -29
  148. package/src/cli/index.ts +0 -287
  149. package/src/cli/middleware.ts +0 -291
  150. package/src/cli/openapi.ts +0 -407
  151. package/src/cli/router.ts +0 -188
  152. package/src/cli/routes.ts +0 -265
  153. package/src/cli/utils.ts +0 -531
  154. package/src/cli.ts +0 -5
  155. package/src/config.ts +0 -322
  156. package/src/container/container.ts +0 -740
  157. package/src/container/contextual-binding.ts +0 -603
  158. package/src/container/decorators.ts +0 -359
  159. package/src/container/service-provider.ts +0 -596
  160. package/src/development/hot-reload.ts +0 -673
  161. package/src/development/index.ts +0 -499
  162. package/src/development/performance-profiler.ts +0 -717
  163. package/src/development/route-debugger.ts +0 -527
  164. package/src/development/route-inspector.ts +0 -749
  165. package/src/development/typescript-utilities.ts +0 -682
  166. package/src/docs.ts +0 -397
  167. package/src/errors/circuit-breaker.ts +0 -732
  168. package/src/errors/error-handler.ts +0 -569
  169. package/src/errors/error-reporting.ts +0 -672
  170. package/src/errors/exceptions.ts +0 -536
  171. package/src/errors/graceful-degradation.ts +0 -621
  172. package/src/errors/index.ts +0 -21
  173. package/src/errors/router-errors.ts +0 -632
  174. package/src/file-serving/static-files.ts +0 -581
  175. package/src/index.ts +0 -14
  176. package/src/middleware/auth.ts +0 -219
  177. package/src/middleware/content_security_policy.ts +0 -215
  178. package/src/middleware/cors.ts +0 -74
  179. package/src/middleware/csrf.ts +0 -108
  180. package/src/middleware/ddos_protection.ts +0 -255
  181. package/src/middleware/file_security.ts +0 -191
  182. package/src/middleware/file_upload.ts +0 -264
  183. package/src/middleware/helmet.ts +0 -268
  184. package/src/middleware/index.ts +0 -117
  185. package/src/middleware/input_validation.ts +0 -449
  186. package/src/middleware/json_body.ts +0 -37
  187. package/src/middleware/performance_alerting.ts +0 -538
  188. package/src/middleware/performance_dashboard.ts +0 -661
  189. package/src/middleware/performance_monitor.ts +0 -943
  190. package/src/middleware/pipeline.ts +0 -489
  191. package/src/middleware/rate_limit.ts +0 -245
  192. package/src/middleware/request_id.ts +0 -36
  193. package/src/middleware/request_signing.ts +0 -636
  194. package/src/middleware/request_tracer.ts +0 -636
  195. package/src/middleware/response_cache.ts +0 -743
  196. package/src/middleware/security.ts +0 -482
  197. package/src/middleware/security_suite.ts +0 -257
  198. package/src/middleware/session.ts +0 -91
  199. package/src/model-binding/index.ts +0 -18
  200. package/src/model-binding/model-middleware.ts +0 -425
  201. package/src/model-binding/model-registry.ts +0 -550
  202. package/src/model-binding.ts +0 -370
  203. package/src/model-resolver-factory.ts +0 -106
  204. package/src/observability/correlation.ts +0 -691
  205. package/src/observability/health-checks.ts +0 -729
  206. package/src/observability/index.ts +0 -184
  207. package/src/observability/integration.ts +0 -548
  208. package/src/observability/metrics.ts +0 -753
  209. package/src/observability/tracing.ts +0 -638
  210. package/src/optimization/bun-utilities.ts +0 -778
  211. package/src/query-builder-integration.ts +0 -137
  212. package/src/request/context.ts +0 -62
  213. package/src/request/enhanced-request.ts +0 -857
  214. package/src/request/macros.ts +0 -688
  215. package/src/response/macros.ts +0 -612
  216. package/src/response/response-factory.ts +0 -596
  217. package/src/router/api-routes.ts +0 -243
  218. package/src/router/file-based-routing.ts +0 -670
  219. package/src/router/file-streaming.ts +0 -372
  220. package/src/router/fluent-router.ts +0 -927
  221. package/src/router/fluent-routing.ts +0 -797
  222. package/src/router/group-organization.ts +0 -213
  223. package/src/router/handler-resolver.ts +0 -291
  224. package/src/router/http-methods.ts +0 -377
  225. package/src/router/index.ts +0 -187
  226. package/src/router/middleware-groups.ts +0 -222
  227. package/src/router/middleware-integration.ts +0 -399
  228. package/src/router/middleware.ts +0 -231
  229. package/src/router/model-binding.ts +0 -215
  230. package/src/router/optimized-route-matching.ts +0 -253
  231. package/src/router/route-building.ts +0 -212
  232. package/src/router/route-compiler.ts +0 -702
  233. package/src/router/route-matching.ts +0 -349
  234. package/src/router/route-trie.ts +0 -464
  235. package/src/router/router.ts +0 -1581
  236. package/src/router/server.ts +0 -462
  237. package/src/router/validation-integration.ts +0 -443
  238. package/src/router/view-rendering.ts +0 -233
  239. package/src/router/websocket.ts +0 -100
  240. package/src/routing/route-caching.ts +0 -402
  241. package/src/routing/route-throttling.ts +0 -469
  242. package/src/routing/subdomain-routing.ts +0 -492
  243. package/src/session/database-store.ts +0 -109
  244. package/src/session/file-store.ts +0 -148
  245. package/src/session/index.ts +0 -244
  246. package/src/session/memory-store.ts +0 -88
  247. package/src/session/redis-store.ts +0 -93
  248. package/src/streaming/index.ts +0 -17
  249. package/src/streaming/sse-handler.ts +0 -482
  250. package/src/streaming/stream-handler.ts +0 -552
  251. package/src/testing/auth-testing.ts +0 -446
  252. package/src/testing/file-upload-testing.ts +0 -543
  253. package/src/testing/index.ts +0 -10
  254. package/src/testing/middleware-testing.ts +0 -322
  255. package/src/testing/model-binding-testing.ts +0 -645
  256. package/src/testing/performance-testing.ts +0 -738
  257. package/src/testing/test-client.ts +0 -310
  258. package/src/testing/test-request.ts +0 -315
  259. package/src/testing/test-response.ts +0 -332
  260. package/src/testing/types.ts +0 -224
  261. package/src/testing/websocket-testing.ts +0 -590
  262. package/src/types/controller-types.ts +0 -385
  263. package/src/types/core.ts +0 -683
  264. package/src/types/middleware-types.ts +0 -419
  265. package/src/types/request-response-augmentation.ts +0 -489
  266. package/src/types/route-inference.ts +0 -357
  267. package/src/types.ts +0 -2055
  268. package/src/url.ts +0 -126
  269. package/src/utils/index.ts +0 -1
  270. package/src/utils/query-preservation.ts +0 -201
  271. package/src/utils.ts +0 -327
  272. package/src/validation/validator.ts +0 -685
  273. package/src/websocket/clustering.ts +0 -762
@@ -1,927 +0,0 @@
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
- }