@stacksjs/bun-router 0.0.17 → 0.0.18

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (146) hide show
  1. package/dist/cli.js +1 -1
  2. package/dist/container/service-provider.d.ts +3 -3
  3. package/dist/development/performance-profiler.d.ts +3 -3
  4. package/dist/development/route-debugger.d.ts +1 -1
  5. package/dist/development/route-inspector.d.ts +2 -2
  6. package/dist/observability/integration.d.ts +1 -1
  7. package/dist/response/macros.d.ts +1 -1
  8. package/dist/router/index.d.ts +7 -7
  9. package/dist/router/router.d.ts +0 -32
  10. package/dist/testing/performance-testing.d.ts +2 -2
  11. package/dist/testing/test-client.d.ts +1 -1
  12. package/dist/testing/websocket-testing.d.ts +1 -1
  13. package/package.json +2 -3
  14. package/src/auth.ts +0 -489
  15. package/src/cache/lru-cache.ts +0 -457
  16. package/src/cache/middleware-memoization.ts +0 -531
  17. package/src/cache/route-cache-warmer.ts +0 -486
  18. package/src/cache/sqlite-cache.ts +0 -783
  19. package/src/cache/streaming-cache.ts +0 -572
  20. package/src/cli/colors.ts +0 -29
  21. package/src/cli/index.ts +0 -287
  22. package/src/cli/middleware.ts +0 -291
  23. package/src/cli/openapi.ts +0 -407
  24. package/src/cli/router.ts +0 -188
  25. package/src/cli/routes.ts +0 -265
  26. package/src/cli/utils.ts +0 -531
  27. package/src/cli.ts +0 -5
  28. package/src/config.ts +0 -322
  29. package/src/container/container.ts +0 -786
  30. package/src/container/contextual-binding.ts +0 -606
  31. package/src/container/decorators.ts +0 -368
  32. package/src/container/index.ts +0 -49
  33. package/src/container/service-provider.ts +0 -596
  34. package/src/development/hot-reload.ts +0 -673
  35. package/src/development/index.ts +0 -499
  36. package/src/development/performance-profiler.ts +0 -717
  37. package/src/development/route-debugger.ts +0 -527
  38. package/src/development/route-inspector.ts +0 -749
  39. package/src/development/typescript-utilities.ts +0 -682
  40. package/src/docs.ts +0 -397
  41. package/src/errors/circuit-breaker.ts +0 -733
  42. package/src/errors/error-handler.ts +0 -569
  43. package/src/errors/error-reporting.ts +0 -672
  44. package/src/errors/exceptions.ts +0 -536
  45. package/src/errors/graceful-degradation.ts +0 -623
  46. package/src/errors/index.ts +0 -21
  47. package/src/errors/router-errors.ts +0 -632
  48. package/src/file-serving/static-files.ts +0 -718
  49. package/src/index.ts +0 -66
  50. package/src/middleware/auth.ts +0 -220
  51. package/src/middleware/content_security_policy.ts +0 -215
  52. package/src/middleware/cors.ts +0 -142
  53. package/src/middleware/csrf.ts +0 -166
  54. package/src/middleware/ddos_protection.ts +0 -279
  55. package/src/middleware/file_security.ts +0 -191
  56. package/src/middleware/file_upload.ts +0 -275
  57. package/src/middleware/helmet.ts +0 -268
  58. package/src/middleware/index.ts +0 -117
  59. package/src/middleware/input_validation.ts +0 -453
  60. package/src/middleware/json_body.ts +0 -37
  61. package/src/middleware/performance_alerting.ts +0 -540
  62. package/src/middleware/performance_dashboard.ts +0 -661
  63. package/src/middleware/performance_monitor.ts +0 -945
  64. package/src/middleware/pipeline.ts +0 -489
  65. package/src/middleware/rate_limit.ts +0 -251
  66. package/src/middleware/request_id.ts +0 -36
  67. package/src/middleware/request_signing.ts +0 -638
  68. package/src/middleware/request_tracer.ts +0 -653
  69. package/src/middleware/response_cache.ts +0 -778
  70. package/src/middleware/security.ts +0 -482
  71. package/src/middleware/security_suite.ts +0 -257
  72. package/src/middleware/session.ts +0 -91
  73. package/src/model-binding/index.ts +0 -18
  74. package/src/model-binding/model-middleware.ts +0 -425
  75. package/src/model-binding/model-registry.ts +0 -550
  76. package/src/model-binding.ts +0 -370
  77. package/src/model-resolver-factory.ts +0 -106
  78. package/src/observability/correlation.ts +0 -691
  79. package/src/observability/health-checks.ts +0 -729
  80. package/src/observability/index.ts +0 -184
  81. package/src/observability/integration.ts +0 -548
  82. package/src/observability/metrics.ts +0 -753
  83. package/src/observability/tracing.ts +0 -638
  84. package/src/optimization/bun-utilities.ts +0 -778
  85. package/src/query-builder-integration.ts +0 -137
  86. package/src/request/context.ts +0 -109
  87. package/src/request/enhanced-request.ts +0 -876
  88. package/src/request/macros.ts +0 -822
  89. package/src/response/macros.ts +0 -665
  90. package/src/response/response-factory.ts +0 -596
  91. package/src/router/api-routes.ts +0 -243
  92. package/src/router/file-based-routing.ts +0 -697
  93. package/src/router/file-streaming.ts +0 -383
  94. package/src/router/fluent-routing.ts +0 -866
  95. package/src/router/group-organization.ts +0 -229
  96. package/src/router/handler-resolver.ts +0 -327
  97. package/src/router/http-methods.ts +0 -407
  98. package/src/router/index.ts +0 -193
  99. package/src/router/middleware-groups.ts +0 -222
  100. package/src/router/middleware-integration.ts +0 -399
  101. package/src/router/middleware.ts +0 -234
  102. package/src/router/model-binding.ts +0 -215
  103. package/src/router/optimized-route-matching.ts +0 -302
  104. package/src/router/route-building.ts +0 -221
  105. package/src/router/route-compiler.ts +0 -691
  106. package/src/router/route-matching.ts +0 -368
  107. package/src/router/route-trie.ts +0 -450
  108. package/src/router/router.ts +0 -1616
  109. package/src/router/server.ts +0 -684
  110. package/src/router/validation-integration.ts +0 -449
  111. package/src/router/view-rendering.ts +0 -233
  112. package/src/router/websocket.ts +0 -100
  113. package/src/routing/route-caching.ts +0 -402
  114. package/src/routing/route-throttling.ts +0 -469
  115. package/src/routing/subdomain-routing.ts +0 -492
  116. package/src/session/database-store.ts +0 -109
  117. package/src/session/file-store.ts +0 -148
  118. package/src/session/index.ts +0 -244
  119. package/src/session/memory-store.ts +0 -88
  120. package/src/session/redis-store.ts +0 -93
  121. package/src/streaming/index.ts +0 -17
  122. package/src/streaming/sse-handler.ts +0 -482
  123. package/src/streaming/stream-handler.ts +0 -552
  124. package/src/testing/auth-testing.ts +0 -448
  125. package/src/testing/file-upload-testing.ts +0 -543
  126. package/src/testing/index.ts +0 -10
  127. package/src/testing/middleware-testing.ts +0 -322
  128. package/src/testing/model-binding-testing.ts +0 -645
  129. package/src/testing/performance-testing.ts +0 -738
  130. package/src/testing/test-client.ts +0 -310
  131. package/src/testing/test-request.ts +0 -315
  132. package/src/testing/test-response.ts +0 -332
  133. package/src/testing/types.ts +0 -224
  134. package/src/testing/websocket-testing.ts +0 -590
  135. package/src/types/controller-types.ts +0 -385
  136. package/src/types/core.ts +0 -683
  137. package/src/types/middleware-types.ts +0 -422
  138. package/src/types/request-response-augmentation.ts +0 -489
  139. package/src/types/route-inference.ts +0 -372
  140. package/src/types.ts +0 -2123
  141. package/src/url.ts +0 -131
  142. package/src/utils/index.ts +0 -1
  143. package/src/utils/query-preservation.ts +0 -201
  144. package/src/utils.ts +0 -395
  145. package/src/validation/validator.ts +0 -688
  146. 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
- }