@stacksjs/bun-router 0.0.17 → 0.0.19

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 (153) hide show
  1. package/dist/chunk-2mx7fq49.js +4 -0
  2. package/dist/chunk-a8p83068.js +103 -0
  3. package/dist/{chunk-cgptvjdf.js → chunk-y3xq6wq6.js} +1 -1
  4. package/dist/cli.js +4 -3
  5. package/dist/container/index.js +1149 -211
  6. package/dist/container/service-provider.d.ts +3 -3
  7. package/dist/development/performance-profiler.d.ts +3 -3
  8. package/dist/development/route-debugger.d.ts +1 -1
  9. package/dist/development/route-inspector.d.ts +2 -2
  10. package/dist/index.js +369 -2209
  11. package/dist/observability/integration.d.ts +1 -1
  12. package/dist/response/macros.d.ts +1 -1
  13. package/dist/router/index.d.ts +7 -7
  14. package/dist/router/router.d.ts +0 -32
  15. package/dist/testing/performance-testing.d.ts +2 -2
  16. package/dist/testing/test-client.d.ts +1 -1
  17. package/dist/testing/websocket-testing.d.ts +1 -1
  18. package/package.json +7 -7
  19. package/dist/chunk-1ahs68ys.js +0 -18
  20. package/dist/chunk-g3ybefhg.js +0 -1923
  21. package/src/auth.ts +0 -489
  22. package/src/cache/lru-cache.ts +0 -457
  23. package/src/cache/middleware-memoization.ts +0 -531
  24. package/src/cache/route-cache-warmer.ts +0 -486
  25. package/src/cache/sqlite-cache.ts +0 -783
  26. package/src/cache/streaming-cache.ts +0 -572
  27. package/src/cli/colors.ts +0 -29
  28. package/src/cli/index.ts +0 -287
  29. package/src/cli/middleware.ts +0 -291
  30. package/src/cli/openapi.ts +0 -407
  31. package/src/cli/router.ts +0 -188
  32. package/src/cli/routes.ts +0 -265
  33. package/src/cli/utils.ts +0 -531
  34. package/src/cli.ts +0 -5
  35. package/src/config.ts +0 -322
  36. package/src/container/container.ts +0 -786
  37. package/src/container/contextual-binding.ts +0 -606
  38. package/src/container/decorators.ts +0 -368
  39. package/src/container/index.ts +0 -49
  40. package/src/container/service-provider.ts +0 -596
  41. package/src/development/hot-reload.ts +0 -673
  42. package/src/development/index.ts +0 -499
  43. package/src/development/performance-profiler.ts +0 -717
  44. package/src/development/route-debugger.ts +0 -527
  45. package/src/development/route-inspector.ts +0 -749
  46. package/src/development/typescript-utilities.ts +0 -682
  47. package/src/docs.ts +0 -397
  48. package/src/errors/circuit-breaker.ts +0 -733
  49. package/src/errors/error-handler.ts +0 -569
  50. package/src/errors/error-reporting.ts +0 -672
  51. package/src/errors/exceptions.ts +0 -536
  52. package/src/errors/graceful-degradation.ts +0 -623
  53. package/src/errors/index.ts +0 -21
  54. package/src/errors/router-errors.ts +0 -632
  55. package/src/file-serving/static-files.ts +0 -718
  56. package/src/index.ts +0 -66
  57. package/src/middleware/auth.ts +0 -220
  58. package/src/middleware/content_security_policy.ts +0 -215
  59. package/src/middleware/cors.ts +0 -142
  60. package/src/middleware/csrf.ts +0 -166
  61. package/src/middleware/ddos_protection.ts +0 -279
  62. package/src/middleware/file_security.ts +0 -191
  63. package/src/middleware/file_upload.ts +0 -275
  64. package/src/middleware/helmet.ts +0 -268
  65. package/src/middleware/index.ts +0 -117
  66. package/src/middleware/input_validation.ts +0 -453
  67. package/src/middleware/json_body.ts +0 -37
  68. package/src/middleware/performance_alerting.ts +0 -540
  69. package/src/middleware/performance_dashboard.ts +0 -661
  70. package/src/middleware/performance_monitor.ts +0 -945
  71. package/src/middleware/pipeline.ts +0 -489
  72. package/src/middleware/rate_limit.ts +0 -251
  73. package/src/middleware/request_id.ts +0 -36
  74. package/src/middleware/request_signing.ts +0 -638
  75. package/src/middleware/request_tracer.ts +0 -653
  76. package/src/middleware/response_cache.ts +0 -778
  77. package/src/middleware/security.ts +0 -482
  78. package/src/middleware/security_suite.ts +0 -257
  79. package/src/middleware/session.ts +0 -91
  80. package/src/model-binding/index.ts +0 -18
  81. package/src/model-binding/model-middleware.ts +0 -425
  82. package/src/model-binding/model-registry.ts +0 -550
  83. package/src/model-binding.ts +0 -370
  84. package/src/model-resolver-factory.ts +0 -106
  85. package/src/observability/correlation.ts +0 -691
  86. package/src/observability/health-checks.ts +0 -729
  87. package/src/observability/index.ts +0 -184
  88. package/src/observability/integration.ts +0 -548
  89. package/src/observability/metrics.ts +0 -753
  90. package/src/observability/tracing.ts +0 -638
  91. package/src/optimization/bun-utilities.ts +0 -778
  92. package/src/query-builder-integration.ts +0 -137
  93. package/src/request/context.ts +0 -109
  94. package/src/request/enhanced-request.ts +0 -876
  95. package/src/request/macros.ts +0 -822
  96. package/src/response/macros.ts +0 -665
  97. package/src/response/response-factory.ts +0 -596
  98. package/src/router/api-routes.ts +0 -243
  99. package/src/router/file-based-routing.ts +0 -697
  100. package/src/router/file-streaming.ts +0 -383
  101. package/src/router/fluent-routing.ts +0 -866
  102. package/src/router/group-organization.ts +0 -229
  103. package/src/router/handler-resolver.ts +0 -327
  104. package/src/router/http-methods.ts +0 -407
  105. package/src/router/index.ts +0 -193
  106. package/src/router/middleware-groups.ts +0 -222
  107. package/src/router/middleware-integration.ts +0 -399
  108. package/src/router/middleware.ts +0 -234
  109. package/src/router/model-binding.ts +0 -215
  110. package/src/router/optimized-route-matching.ts +0 -302
  111. package/src/router/route-building.ts +0 -221
  112. package/src/router/route-compiler.ts +0 -691
  113. package/src/router/route-matching.ts +0 -368
  114. package/src/router/route-trie.ts +0 -450
  115. package/src/router/router.ts +0 -1616
  116. package/src/router/server.ts +0 -684
  117. package/src/router/validation-integration.ts +0 -449
  118. package/src/router/view-rendering.ts +0 -233
  119. package/src/router/websocket.ts +0 -100
  120. package/src/routing/route-caching.ts +0 -402
  121. package/src/routing/route-throttling.ts +0 -469
  122. package/src/routing/subdomain-routing.ts +0 -492
  123. package/src/session/database-store.ts +0 -109
  124. package/src/session/file-store.ts +0 -148
  125. package/src/session/index.ts +0 -244
  126. package/src/session/memory-store.ts +0 -88
  127. package/src/session/redis-store.ts +0 -93
  128. package/src/streaming/index.ts +0 -17
  129. package/src/streaming/sse-handler.ts +0 -482
  130. package/src/streaming/stream-handler.ts +0 -552
  131. package/src/testing/auth-testing.ts +0 -448
  132. package/src/testing/file-upload-testing.ts +0 -543
  133. package/src/testing/index.ts +0 -10
  134. package/src/testing/middleware-testing.ts +0 -322
  135. package/src/testing/model-binding-testing.ts +0 -645
  136. package/src/testing/performance-testing.ts +0 -738
  137. package/src/testing/test-client.ts +0 -310
  138. package/src/testing/test-request.ts +0 -315
  139. package/src/testing/test-response.ts +0 -332
  140. package/src/testing/types.ts +0 -224
  141. package/src/testing/websocket-testing.ts +0 -590
  142. package/src/types/controller-types.ts +0 -385
  143. package/src/types/core.ts +0 -683
  144. package/src/types/middleware-types.ts +0 -422
  145. package/src/types/request-response-augmentation.ts +0 -489
  146. package/src/types/route-inference.ts +0 -372
  147. package/src/types.ts +0 -2123
  148. package/src/url.ts +0 -131
  149. package/src/utils/index.ts +0 -1
  150. package/src/utils/query-preservation.ts +0 -201
  151. package/src/utils.ts +0 -395
  152. package/src/validation/validator.ts +0 -688
  153. package/src/websocket/clustering.ts +0 -769
@@ -1,229 +0,0 @@
1
- import type { ActionHandler, RouteGroup } from '../types'
2
- import type { Router } from './router'
3
- import { joinPaths } from '../utils'
4
-
5
- /**
6
- * Route grouping and organization extension for Router class
7
- */
8
- export function registerGroupOrganization(RouterClass: typeof Router): void {
9
- Object.defineProperties(RouterClass.prototype, {
10
- /**
11
- * Create a route group with shared attributes
12
- */
13
- group: {
14
- value(options: RouteGroup, callback: () => void): Router {
15
- // Save the current group if there is one
16
- const previousGroup = this.currentGroup
17
-
18
- // Create a new group by merging with previous group
19
- const newGroup: RouteGroup = { ...previousGroup }
20
-
21
- // Handle prefix nesting
22
- if (options.prefix && previousGroup?.prefix) {
23
- newGroup.prefix = joinPaths(previousGroup.prefix, options.prefix)
24
- }
25
- else if (options.prefix) {
26
- newGroup.prefix = options.prefix
27
- }
28
-
29
- // Merge middleware
30
- if (options.middleware) {
31
- newGroup.middleware = [
32
- ...(previousGroup?.middleware || []),
33
- ...options.middleware,
34
- ]
35
- }
36
-
37
- // Set the new group
38
- this.currentGroup = newGroup
39
-
40
- // Execute the callback to register routes in this group
41
- const result = callback() as unknown
42
-
43
- // Async callbacks (e.g. `register()`'s dynamic import) register
44
- // their routes after this call returns — the group must stay
45
- // active until the promise settles, and callers must await.
46
- if (result instanceof Promise) {
47
- return result.then(
48
- () => {
49
- this.currentGroup = previousGroup
50
- return this
51
- },
52
- (error: unknown) => {
53
- this.currentGroup = previousGroup
54
- throw error
55
- },
56
- ) as unknown as Router
57
- }
58
-
59
- // Restore the previous group
60
- this.currentGroup = previousGroup
61
-
62
- return this
63
- },
64
- writable: true,
65
- configurable: true,
66
- },
67
-
68
- /**
69
- * Create a route group with a name prefix
70
- */
71
- name: {
72
- value(prefix: string, callback: () => void): Router {
73
- // Save previous group
74
- const previousGroup = this.currentGroup || {}
75
-
76
- // Create a new group with the name prefix
77
- const newGroup: RouteGroup = { ...previousGroup }
78
- this.currentGroup = newGroup
79
-
80
- // Keep track of routes before the callback
81
- const routeCountBefore = this.routes.length
82
-
83
- // Execute the callback to register routes
84
- callback()
85
-
86
- // Apply name prefix to newly added routes
87
- for (let i = routeCountBefore; i < this.routes.length; i++) {
88
- const route = this.routes[i]
89
- if (route.name) {
90
- route.name = `${prefix}.${route.name}`
91
- }
92
- }
93
-
94
- // Restore previous group
95
- this.currentGroup = previousGroup
96
-
97
- return this
98
- },
99
- writable: true,
100
- configurable: true,
101
- },
102
-
103
- /**
104
- * Create a route group with a controller
105
- */
106
- controller: {
107
- value(controller: string | (new () => any), callback: () => void): Router {
108
- // Save previous group
109
- const previousGroup = this.currentGroup || {}
110
-
111
- // Create a new group with the controller
112
- const newGroup: RouteGroup = { ...previousGroup, controller }
113
- this.currentGroup = newGroup
114
-
115
- // Execute the callback to register routes
116
- callback()
117
-
118
- // Restore previous group
119
- this.currentGroup = previousGroup
120
-
121
- return this
122
- },
123
- writable: true,
124
- configurable: true,
125
- },
126
-
127
- /**
128
- * Create a route group with a domain
129
- */
130
- domain: {
131
- value(domain: string, callback: () => void): Router {
132
- // Save previous domain
133
- const previousDomain = this.currentDomain
134
-
135
- // Set the new domain
136
- this.currentDomain = domain
137
-
138
- // Execute the callback to register routes
139
- callback()
140
-
141
- // Restore previous domain
142
- this.currentDomain = previousDomain
143
-
144
- return this
145
- },
146
- writable: true,
147
- configurable: true,
148
- },
149
-
150
- /**
151
- * Create a route group with a prefix
152
- */
153
- prefix: {
154
- value(prefix: string, callback: () => void): Router {
155
- return this.group({ prefix }, callback)
156
- },
157
- writable: true,
158
- configurable: true,
159
- },
160
-
161
- /**
162
- * Create RESTful resource routes
163
- */
164
- resource: {
165
- value(
166
- name: string,
167
- handler: string | { [key: string]: ActionHandler },
168
- type: 'api' | 'web' = 'api',
169
- ): Router {
170
- // Normalize the name to build paths
171
- const pluralName = name.endsWith('s') ? name : `${name}s`
172
- const singularName = name.endsWith('s') ? name.slice(0, -1) : name
173
-
174
- // Define the routes to create
175
- const routes = [
176
- { name: 'index', method: 'GET', path: `/${pluralName}`, action: 'index' },
177
- { name: 'create', method: 'GET', path: `/${pluralName}/create`, action: 'create' },
178
- { name: 'store', method: 'POST', path: `/${pluralName}`, action: 'store' },
179
- { name: 'show', method: 'GET', path: `/${pluralName}/{id}`, action: 'show' },
180
- { name: 'edit', method: 'GET', path: `/${pluralName}/{id}/edit`, action: 'edit' },
181
- { name: 'update', method: 'PUT', path: `/${pluralName}/{id}`, action: 'update' },
182
- { name: 'destroy', method: 'DELETE', path: `/${pluralName}/{id}`, action: 'destroy' },
183
- ]
184
-
185
- // Use the name prefix for route names
186
- this.name(singularName, () => {
187
- for (const route of routes) {
188
- let routeHandler: ActionHandler
189
-
190
- if (typeof handler === 'string') {
191
- // If handler is a string, use it as a prefix for the action
192
- routeHandler = `${handler}${route.action.charAt(0).toUpperCase() + route.action.slice(1)}Action` as any
193
- }
194
- else {
195
- // If handler is an object, get the action handler from it
196
- if (handler[route.action]) {
197
- routeHandler = handler[route.action]
198
- }
199
- else {
200
- // Skip this route if no handler is defined
201
- continue
202
- }
203
- }
204
-
205
- // Register the route with the appropriate HTTP method
206
- switch (route.method) {
207
- case 'GET':
208
- this.get(route.path, routeHandler, type, route.name)
209
- break
210
- case 'POST':
211
- this.post(route.path, routeHandler, type, route.name)
212
- break
213
- case 'PUT':
214
- this.put(route.path, routeHandler, type, route.name)
215
- break
216
- case 'DELETE':
217
- this.delete(route.path, routeHandler, type, route.name)
218
- break
219
- }
220
- }
221
- })
222
-
223
- return this
224
- },
225
- writable: true,
226
- configurable: true,
227
- },
228
- })
229
- }
@@ -1,327 +0,0 @@
1
- /**
2
- * Handler Resolver
3
- *
4
- * Resolves route handlers from various formats:
5
- * - Functions returning Response
6
- * - Functions returning strings/objects (auto-wrapped)
7
- * - String file paths like 'UserAction.ts' or './actions/UserAction'
8
- * - Controller@method patterns like 'UserController@index'
9
- * - Class constructors with handle() method
10
- * - Class instances with handle() method
11
- */
12
-
13
- import type { EnhancedRequest, RouterConfig } from '../types'
14
-
15
- /**
16
- * Wraps a value in a Response object if it's not already a Response
17
- */
18
- export function wrapResponse(value: unknown): Response {
19
- // Already a Response
20
- if (value instanceof Response) {
21
- return value
22
- }
23
-
24
- // Null or undefined - return 204 No Content
25
- if (value === null || value === undefined) {
26
- return new Response(null, { status: 204 })
27
- }
28
-
29
- // String - return as text/plain
30
- if (typeof value === 'string') {
31
- return new Response(value, {
32
- headers: { 'Content-Type': 'text/plain; charset=utf-8' },
33
- })
34
- }
35
-
36
- // Number or boolean - convert to string
37
- if (typeof value === 'number' || typeof value === 'boolean') {
38
- return new Response(String(value), {
39
- headers: { 'Content-Type': 'text/plain; charset=utf-8' },
40
- })
41
- }
42
-
43
- // ArrayBuffer or Uint8Array - return as binary
44
- if (value instanceof ArrayBuffer || value instanceof Uint8Array) {
45
- return new Response(value as any, {
46
- headers: { 'Content-Type': 'application/octet-stream' },
47
- })
48
- }
49
-
50
- // ReadableStream - return as stream
51
- if (value instanceof ReadableStream) {
52
- return new Response(value)
53
- }
54
-
55
- // Object or array - return as JSON
56
- if (typeof value === 'object') {
57
- return new Response(JSON.stringify(value), {
58
- headers: { 'Content-Type': 'application/json; charset=utf-8' },
59
- })
60
- }
61
-
62
- // Fallback - convert to string
63
- return new Response(String(value), {
64
- headers: { 'Content-Type': 'text/plain; charset=utf-8' },
65
- })
66
- }
67
-
68
- /**
69
- * Resolves a string handler path to an imported module
70
- */
71
- async function importHandler(handlerPath: string, config: RouterConfig): Promise<unknown> {
72
- const actionsPath = config.actionsPath || 'actions'
73
- const controllersPath = config.controllersPath || 'controllers'
74
-
75
- // Normalize the path - remove .ts/.js extension if present
76
- const normalizedPath = handlerPath.replace(/\.(ts|js)$/, '')
77
-
78
- // List of paths to try for importing
79
- const pathsToTry: string[] = []
80
-
81
- // Check if it's a Controller@method pattern
82
- if (normalizedPath.includes('@')) {
83
- const [controllerName, _methodName] = normalizedPath.split('@')
84
- pathsToTry.push(
85
- `${controllersPath}/${controllerName}`,
86
- `${controllersPath}/${controllerName}.ts`,
87
- `${controllersPath}/${controllerName}.js`,
88
- `./${controllersPath}/${controllerName}`,
89
- `../${controllersPath}/${controllerName}`,
90
- )
91
- }
92
- // Check if it's an Actions/ path pattern
93
- else if (normalizedPath.startsWith('Actions/') || normalizedPath.startsWith('actions/')) {
94
- pathsToTry.push(
95
- normalizedPath,
96
- `./${normalizedPath}`,
97
- `../${normalizedPath}`,
98
- )
99
- }
100
- // Check if it's a relative path
101
- else if (normalizedPath.startsWith('./') || normalizedPath.startsWith('../')) {
102
- pathsToTry.push(normalizedPath)
103
- }
104
- // Otherwise, try various locations
105
- else {
106
- pathsToTry.push(
107
- // Try actionsPath first
108
- `${actionsPath}/${normalizedPath}`,
109
- `./${actionsPath}/${normalizedPath}`,
110
- `../${actionsPath}/${normalizedPath}`,
111
- // Try direct import
112
- normalizedPath,
113
- `./${normalizedPath}`,
114
- `../${normalizedPath}`,
115
- )
116
- }
117
-
118
- let lastError: Error | null = null
119
-
120
- for (const path of pathsToTry) {
121
- try {
122
- const module = await import(path)
123
- return module
124
- }
125
- catch (error) {
126
- lastError = error as Error
127
- // Continue trying other paths
128
- }
129
- }
130
-
131
- throw new Error(
132
- `Failed to import handler "${handlerPath}".\n`
133
- + `Tried paths: ${pathsToTry.join(', ')}\n`
134
- + `Last error: ${lastError?.message || 'Unknown error'}`,
135
- )
136
- }
137
-
138
- /**
139
- * Check if a value is a callable function handler
140
- */
141
- function isCallableHandler(handler: unknown): handler is (req: EnhancedRequest) => unknown {
142
- return typeof handler === 'function' && !handler.prototype?.handle
143
- }
144
-
145
- /**
146
- * Resolves and executes a handler, returning a Response
147
- */
148
- export async function resolveHandler(
149
- handler: unknown,
150
- req: EnhancedRequest,
151
- config: RouterConfig,
152
- ): Promise<Response> {
153
- // 0. If it's already a Response (static route), clone and return it directly
154
- if (handler instanceof Response) {
155
- return handler.clone() as Response
156
- }
157
-
158
- // 1. If it's a function (not a class with handle method), call it and wrap the result
159
- if (isCallableHandler(handler)) {
160
- const result = await handler(req)
161
- return wrapResponse(result)
162
- }
163
-
164
- // 2. If it's a string, try to import and resolve
165
- if (typeof handler === 'string') {
166
- return resolveStringHandler(handler, req, config)
167
- }
168
-
169
- // 3. If it's a class constructor with handle method
170
- if (
171
- typeof handler === 'function'
172
- && handler.prototype
173
- && typeof handler.prototype.handle === 'function'
174
- ) {
175
- const HandlerClass = handler as new () => { handle: (req: EnhancedRequest) => unknown }
176
- const instance = new HandlerClass()
177
- const result = await instance.handle(req)
178
- return wrapResponse(result)
179
- }
180
-
181
- // 4. If it's an object with a handle method
182
- if (handler && typeof handler === 'object' && 'handle' in handler) {
183
- const handlerObj = handler as { handle: (req: EnhancedRequest) => unknown }
184
- if (typeof handlerObj.handle === 'function') {
185
- const result = await handlerObj.handle(req)
186
- return wrapResponse(result)
187
- }
188
- }
189
-
190
- throw new Error(`Invalid handler type: ${typeof handler}`)
191
- }
192
-
193
- /**
194
- * Resolves a string-based handler
195
- */
196
- async function resolveStringHandler(
197
- handlerPath: string,
198
- req: EnhancedRequest,
199
- config: RouterConfig,
200
- ): Promise<Response> {
201
- const controllersPath = config.controllersPath || 'controllers'
202
-
203
- // Check if it's a Controller@method pattern
204
- if (handlerPath.includes('@')) {
205
- const [controllerName, methodName] = handlerPath.split('@')
206
-
207
- // Import the controller
208
- const pathsToTry = [
209
- `${controllersPath}/${controllerName}`,
210
- `./${controllersPath}/${controllerName}`,
211
- `../${controllersPath}/${controllerName}`,
212
- controllerName,
213
- `./${controllerName}`,
214
- `../${controllerName}`,
215
- ]
216
-
217
- let module: unknown = null
218
- let lastError: Error | null = null
219
-
220
- for (const path of pathsToTry) {
221
- try {
222
- module = await import(path)
223
- break
224
- }
225
- catch (error) {
226
- lastError = error as Error
227
- }
228
- }
229
-
230
- if (!module) {
231
- throw new Error(
232
- `Failed to import controller "${controllerName}".\n`
233
- + `Tried paths: ${pathsToTry.join(', ')}\n`
234
- + `Last error: ${lastError?.message || 'Unknown error'}`,
235
- )
236
- }
237
-
238
- const Controller = (module as { default?: unknown }).default || module
239
-
240
- // Instantiate and call method
241
- if (typeof Controller === 'function') {
242
- const instance = new (Controller as new () => Record<string, unknown>)()
243
- const method = instance[methodName]
244
-
245
- if (typeof method !== 'function') {
246
- throw new TypeError(`Method "${methodName}" not found on controller "${controllerName}"`)
247
- }
248
-
249
- const result = await (method as (req: EnhancedRequest) => unknown).call(instance, req)
250
- return wrapResponse(result)
251
- }
252
-
253
- throw new Error(`Controller "${controllerName}" is not a valid class`)
254
- }
255
-
256
- // Import the action handler
257
- const module = await importHandler(handlerPath, config)
258
- const Handler = (module as { default?: unknown }).default || module
259
-
260
- // If it's a function, call it
261
- if (typeof Handler === 'function') {
262
- // Check if it's a class with handle method
263
- if (Handler.prototype && typeof Handler.prototype.handle === 'function') {
264
- const instance = new (Handler as new () => { handle: (req: EnhancedRequest) => unknown })()
265
- const result = await instance.handle(req)
266
- return wrapResponse(result)
267
- }
268
-
269
- // It's a regular function
270
- const result = await (Handler as (req: EnhancedRequest) => unknown)(req)
271
- return wrapResponse(result)
272
- }
273
-
274
- // If it's an object with handle method
275
- if (Handler && typeof Handler === 'object' && 'handle' in Handler) {
276
- const handlerObj = Handler as { handle: (req: EnhancedRequest) => unknown }
277
- if (typeof handlerObj.handle === 'function') {
278
- const result = await handlerObj.handle(req)
279
- return wrapResponse(result)
280
- }
281
- }
282
-
283
- throw new Error(`Handler "${handlerPath}" does not export a valid handler function or class`)
284
- }
285
-
286
- /**
287
- * Creates a handler resolver bound to a specific config
288
- */
289
- export function createHandlerResolver(config: RouterConfig): (handler: unknown, req: EnhancedRequest) => Promise<Response> {
290
- return (handler: unknown, req: EnhancedRequest) => resolveHandler(handler, req, config)
291
- }
292
-
293
- /**
294
- * Precompile the dispatch branch for a handler whose shape never changes
295
- * (a registered route's handler). `resolveHandler` re-sniffs the handler
296
- * type — instanceof / typeof / prototype checks — on every request; this
297
- * resolves the branch once and returns a specialized invoker for the
298
- * per-request hot path.
299
- */
300
- export function createHandlerInvoker(
301
- handler: unknown,
302
- config: RouterConfig,
303
- ): (req: EnhancedRequest) => Promise<Response> {
304
- // Static Response routes
305
- if (handler instanceof Response) {
306
- return async () => handler.clone() as Response
307
- }
308
-
309
- // Plain function handlers (the common case)
310
- if (isCallableHandler(handler)) {
311
- return async (req: EnhancedRequest) => wrapResponse(await handler(req))
312
- }
313
-
314
- // Class constructors with a handle() method
315
- if (
316
- typeof handler === 'function'
317
- && handler.prototype
318
- && typeof handler.prototype.handle === 'function'
319
- ) {
320
- const HandlerClass = handler as new () => { handle: (req: EnhancedRequest) => unknown }
321
- return async (req: EnhancedRequest) => wrapResponse(await new HandlerClass().handle(req))
322
- }
323
-
324
- // Strings (action paths, Controller@method) and anything exotic keep
325
- // the full resolution flow
326
- return (req: EnhancedRequest) => resolveHandler(handler, req, config)
327
- }