@stacksjs/bun-router 0.0.5 → 0.0.7

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 (269) hide show
  1. package/dist/auth.d.ts +134 -0
  2. package/dist/cache/lru-cache.d.ts +139 -0
  3. package/dist/cache/middleware-memoization.d.ts +151 -0
  4. package/dist/cache/route-cache-warmer.d.ts +161 -0
  5. package/dist/cache/sqlite-cache.d.ts +209 -0
  6. package/dist/cache/streaming-cache.d.ts +132 -0
  7. package/dist/cli/colors.d.ts +15 -0
  8. package/dist/cli/index.d.ts +10 -0
  9. package/dist/cli/middleware.d.ts +31 -0
  10. package/dist/cli/openapi.d.ts +17 -0
  11. package/dist/cli/router.d.ts +15 -0
  12. package/dist/cli/routes.d.ts +29 -0
  13. package/dist/cli/utils.d.ts +74 -0
  14. package/dist/cli.d.ts +1 -1
  15. package/dist/cli.js +1 -1
  16. package/dist/config.d.ts +7 -0
  17. package/dist/container/container.d.ts +273 -0
  18. package/dist/container/contextual-binding.d.ts +240 -0
  19. package/dist/container/decorators.d.ts +141 -0
  20. package/dist/container/service-provider.d.ts +285 -0
  21. package/dist/development/hot-reload.d.ts +187 -0
  22. package/dist/development/index.d.ts +167 -0
  23. package/dist/development/performance-profiler.d.ts +217 -0
  24. package/dist/development/route-debugger.d.ts +154 -0
  25. package/dist/development/route-inspector.d.ts +211 -0
  26. package/dist/development/typescript-utilities.d.ts +209 -0
  27. package/dist/docs.d.ts +11 -0
  28. package/dist/errors/circuit-breaker.d.ts +195 -0
  29. package/dist/errors/error-handler.d.ts +88 -0
  30. package/dist/errors/error-reporting.d.ts +144 -0
  31. package/dist/errors/exceptions.d.ts +184 -0
  32. package/dist/errors/graceful-degradation.d.ts +154 -0
  33. package/dist/errors/index.d.ts +9 -0
  34. package/dist/errors/router-errors.d.ts +110 -0
  35. package/dist/file-serving/static-files.d.ts +143 -0
  36. package/dist/index.d.ts +2 -0
  37. package/dist/index.js +375 -227
  38. package/dist/middleware/auth.d.ts +63 -0
  39. package/dist/middleware/content_security_policy.d.ts +53 -0
  40. package/dist/middleware/cors.d.ts +5 -0
  41. package/dist/middleware/csrf.d.ts +7 -0
  42. package/dist/middleware/ddos_protection.d.ts +39 -0
  43. package/dist/middleware/file_security.d.ts +26 -0
  44. package/dist/middleware/file_upload.d.ts +38 -0
  45. package/dist/middleware/helmet.d.ts +54 -0
  46. package/dist/middleware/index.d.ts +44 -0
  47. package/dist/middleware/input_validation.d.ts +45 -0
  48. package/dist/middleware/json_body.d.ts +4 -0
  49. package/dist/middleware/performance_alerting.d.ts +87 -0
  50. package/dist/middleware/performance_dashboard.d.ts +87 -0
  51. package/dist/middleware/performance_monitor.d.ts +209 -0
  52. package/dist/middleware/pipeline.d.ts +131 -0
  53. package/dist/middleware/rate_limit.d.ts +36 -0
  54. package/dist/middleware/request_id.d.ts +4 -0
  55. package/dist/middleware/request_signing.d.ts +153 -0
  56. package/dist/middleware/request_tracer.d.ts +72 -0
  57. package/dist/middleware/response_cache.d.ts +97 -0
  58. package/dist/middleware/security.d.ts +69 -0
  59. package/dist/middleware/security_suite.d.ts +46 -0
  60. package/dist/middleware/session.d.ts +23 -0
  61. package/dist/model-binding/index.d.ts +2 -0
  62. package/dist/model-binding/model-middleware.d.ts +118 -0
  63. package/dist/model-binding/model-registry.d.ts +164 -0
  64. package/dist/model-binding.d.ts +185 -0
  65. package/dist/model-resolver-factory.d.ts +31 -0
  66. package/dist/observability/correlation.d.ts +187 -0
  67. package/dist/observability/health-checks.d.ts +186 -0
  68. package/dist/observability/index.d.ts +60 -0
  69. package/dist/observability/integration.d.ts +147 -0
  70. package/dist/observability/metrics.d.ts +184 -0
  71. package/dist/observability/tracing.d.ts +187 -0
  72. package/dist/optimization/bun-utilities.d.ts +224 -0
  73. package/dist/query-builder-integration.d.ts +36 -0
  74. package/dist/request/context.d.ts +34 -0
  75. package/dist/request/enhanced-request.d.ts +213 -0
  76. package/dist/request/macros.d.ts +300 -0
  77. package/dist/response/macros.d.ts +245 -0
  78. package/dist/response/response-factory.d.ts +133 -0
  79. package/dist/router/api-routes.d.ts +5 -0
  80. package/dist/router/file-based-routing.d.ts +118 -0
  81. package/dist/router/file-streaming.d.ts +5 -0
  82. package/dist/router/fluent-router.d.ts +315 -0
  83. package/dist/router/fluent-routing.d.ts +271 -0
  84. package/dist/router/group-organization.d.ts +5 -0
  85. package/dist/router/handler-resolver.d.ts +24 -0
  86. package/dist/router/http-methods.d.ts +10 -0
  87. package/dist/router/index.d.ts +113 -0
  88. package/dist/router/middleware-groups.d.ts +94 -0
  89. package/dist/router/middleware-integration.d.ts +112 -0
  90. package/dist/router/middleware.d.ts +5 -0
  91. package/dist/router/model-binding.d.ts +5 -0
  92. package/dist/router/optimized-route-matching.d.ts +6 -0
  93. package/dist/router/route-building.d.ts +5 -0
  94. package/dist/router/route-compiler.d.ts +153 -0
  95. package/dist/router/route-matching.d.ts +5 -0
  96. package/dist/router/route-trie.d.ts +114 -0
  97. package/dist/router/router.d.ts +276 -0
  98. package/dist/router/server.d.ts +5 -0
  99. package/dist/router/validation-integration.d.ts +170 -0
  100. package/dist/router/view-rendering.d.ts +5 -0
  101. package/dist/router/websocket.d.ts +5 -0
  102. package/dist/routing/route-caching.d.ts +129 -0
  103. package/dist/routing/route-throttling.d.ts +150 -0
  104. package/dist/routing/subdomain-routing.d.ts +206 -0
  105. package/dist/session/database-store.d.ts +51 -0
  106. package/dist/session/file-store.d.ts +22 -0
  107. package/dist/session/index.d.ts +106 -0
  108. package/dist/session/memory-store.d.ts +21 -0
  109. package/dist/session/redis-store.d.ts +31 -0
  110. package/dist/streaming/index.d.ts +3 -0
  111. package/dist/streaming/sse-handler.d.ts +138 -0
  112. package/dist/streaming/stream-handler.d.ts +114 -0
  113. package/dist/testing/auth-testing.d.ts +156 -0
  114. package/dist/testing/file-upload-testing.d.ts +190 -0
  115. package/dist/testing/index.d.ts +10 -0
  116. package/dist/testing/middleware-testing.d.ts +138 -0
  117. package/dist/testing/model-binding-testing.d.ts +186 -0
  118. package/dist/testing/performance-testing.d.ts +235 -0
  119. package/dist/testing/test-client.d.ts +117 -0
  120. package/dist/testing/test-request.d.ts +85 -0
  121. package/dist/testing/test-response.d.ts +90 -0
  122. package/dist/testing/types.d.ts +207 -0
  123. package/dist/testing/websocket-testing.d.ts +227 -0
  124. package/dist/types/controller-types.d.ts +208 -0
  125. package/dist/types/core.d.ts +543 -0
  126. package/dist/types/middleware-types.d.ts +227 -0
  127. package/dist/types/request-response-augmentation.d.ts +261 -0
  128. package/dist/types/route-inference.d.ts +168 -0
  129. package/dist/types.d.ts +1776 -0
  130. package/dist/url.d.ts +56 -0
  131. package/dist/utils/index.d.ts +1 -0
  132. package/dist/utils/query-preservation.d.ts +48 -0
  133. package/dist/utils.d.ts +69 -0
  134. package/dist/validation/validator.d.ts +140 -0
  135. package/dist/websocket/clustering.d.ts +185 -0
  136. package/package.json +3 -2
  137. package/src/auth.ts +469 -0
  138. package/src/cache/lru-cache.ts +457 -0
  139. package/src/cache/middleware-memoization.ts +531 -0
  140. package/src/cache/route-cache-warmer.ts +486 -0
  141. package/src/cache/sqlite-cache.ts +783 -0
  142. package/src/cache/streaming-cache.ts +572 -0
  143. package/src/cli/colors.ts +29 -0
  144. package/src/cli/index.ts +287 -0
  145. package/src/cli/middleware.ts +291 -0
  146. package/src/cli/openapi.ts +407 -0
  147. package/src/cli/router.ts +188 -0
  148. package/src/cli/routes.ts +265 -0
  149. package/src/cli/utils.ts +531 -0
  150. package/src/cli.ts +5 -0
  151. package/src/config.ts +322 -0
  152. package/src/container/container.ts +740 -0
  153. package/src/container/contextual-binding.ts +603 -0
  154. package/src/container/decorators.ts +359 -0
  155. package/src/container/service-provider.ts +596 -0
  156. package/src/development/hot-reload.ts +673 -0
  157. package/src/development/index.ts +499 -0
  158. package/src/development/performance-profiler.ts +717 -0
  159. package/src/development/route-debugger.ts +527 -0
  160. package/src/development/route-inspector.ts +749 -0
  161. package/src/development/typescript-utilities.ts +678 -0
  162. package/src/docs.ts +397 -0
  163. package/src/errors/circuit-breaker.ts +732 -0
  164. package/src/errors/error-handler.ts +569 -0
  165. package/src/errors/error-reporting.ts +672 -0
  166. package/src/errors/exceptions.ts +536 -0
  167. package/src/errors/graceful-degradation.ts +621 -0
  168. package/src/errors/index.ts +21 -0
  169. package/src/errors/router-errors.ts +632 -0
  170. package/src/file-serving/static-files.ts +581 -0
  171. package/src/index.ts +14 -0
  172. package/src/middleware/auth.ts +219 -0
  173. package/src/middleware/content_security_policy.ts +215 -0
  174. package/src/middleware/cors.ts +74 -0
  175. package/src/middleware/csrf.ts +108 -0
  176. package/src/middleware/ddos_protection.ts +255 -0
  177. package/src/middleware/file_security.ts +191 -0
  178. package/src/middleware/file_upload.ts +264 -0
  179. package/src/middleware/helmet.ts +268 -0
  180. package/src/middleware/index.ts +117 -0
  181. package/src/middleware/input_validation.ts +449 -0
  182. package/src/middleware/json_body.ts +37 -0
  183. package/src/middleware/performance_alerting.ts +538 -0
  184. package/src/middleware/performance_dashboard.ts +661 -0
  185. package/src/middleware/performance_monitor.ts +943 -0
  186. package/src/middleware/pipeline.ts +489 -0
  187. package/src/middleware/rate_limit.ts +245 -0
  188. package/src/middleware/request_id.ts +36 -0
  189. package/src/middleware/request_signing.ts +636 -0
  190. package/src/middleware/request_tracer.ts +636 -0
  191. package/src/middleware/response_cache.ts +743 -0
  192. package/src/middleware/security.ts +482 -0
  193. package/src/middleware/security_suite.ts +257 -0
  194. package/src/middleware/session.ts +91 -0
  195. package/src/model-binding/index.ts +18 -0
  196. package/src/model-binding/model-middleware.ts +425 -0
  197. package/src/model-binding/model-registry.ts +550 -0
  198. package/src/model-binding.ts +370 -0
  199. package/src/model-resolver-factory.ts +106 -0
  200. package/src/observability/correlation.ts +691 -0
  201. package/src/observability/health-checks.ts +729 -0
  202. package/src/observability/index.ts +184 -0
  203. package/src/observability/integration.ts +548 -0
  204. package/src/observability/metrics.ts +753 -0
  205. package/src/observability/tracing.ts +638 -0
  206. package/src/optimization/bun-utilities.ts +778 -0
  207. package/src/query-builder-integration.ts +137 -0
  208. package/src/request/context.ts +62 -0
  209. package/src/request/enhanced-request.ts +857 -0
  210. package/src/request/macros.ts +688 -0
  211. package/src/response/macros.ts +612 -0
  212. package/src/response/response-factory.ts +588 -0
  213. package/src/router/api-routes.ts +243 -0
  214. package/src/router/file-based-routing.ts +670 -0
  215. package/src/router/file-streaming.ts +372 -0
  216. package/src/router/fluent-router.ts +927 -0
  217. package/src/router/fluent-routing.ts +797 -0
  218. package/src/router/group-organization.ts +213 -0
  219. package/src/router/handler-resolver.ts +291 -0
  220. package/src/router/http-methods.ts +377 -0
  221. package/src/router/index.ts +185 -0
  222. package/src/router/middleware-groups.ts +222 -0
  223. package/src/router/middleware-integration.ts +399 -0
  224. package/src/router/middleware.ts +231 -0
  225. package/src/router/model-binding.ts +215 -0
  226. package/src/router/optimized-route-matching.ts +253 -0
  227. package/src/router/route-building.ts +212 -0
  228. package/src/router/route-compiler.ts +702 -0
  229. package/src/router/route-matching.ts +349 -0
  230. package/src/router/route-trie.ts +464 -0
  231. package/src/router/router.ts +1524 -0
  232. package/src/router/server.ts +429 -0
  233. package/src/router/validation-integration.ts +443 -0
  234. package/src/router/view-rendering.ts +233 -0
  235. package/src/router/websocket.ts +100 -0
  236. package/src/routing/route-caching.ts +402 -0
  237. package/src/routing/route-throttling.ts +469 -0
  238. package/src/routing/subdomain-routing.ts +492 -0
  239. package/src/session/database-store.ts +109 -0
  240. package/src/session/file-store.ts +148 -0
  241. package/src/session/index.ts +244 -0
  242. package/src/session/memory-store.ts +88 -0
  243. package/src/session/redis-store.ts +93 -0
  244. package/src/streaming/index.ts +17 -0
  245. package/src/streaming/sse-handler.ts +482 -0
  246. package/src/streaming/stream-handler.ts +552 -0
  247. package/src/testing/auth-testing.ts +446 -0
  248. package/src/testing/file-upload-testing.ts +543 -0
  249. package/src/testing/index.ts +10 -0
  250. package/src/testing/middleware-testing.ts +322 -0
  251. package/src/testing/model-binding-testing.ts +645 -0
  252. package/src/testing/performance-testing.ts +738 -0
  253. package/src/testing/test-client.ts +310 -0
  254. package/src/testing/test-request.ts +315 -0
  255. package/src/testing/test-response.ts +332 -0
  256. package/src/testing/types.ts +224 -0
  257. package/src/testing/websocket-testing.ts +590 -0
  258. package/src/types/controller-types.ts +385 -0
  259. package/src/types/core.ts +683 -0
  260. package/src/types/middleware-types.ts +419 -0
  261. package/src/types/request-response-augmentation.ts +489 -0
  262. package/src/types/route-inference.ts +357 -0
  263. package/src/types.ts +2049 -0
  264. package/src/url.ts +126 -0
  265. package/src/utils/index.ts +1 -0
  266. package/src/utils/query-preservation.ts +201 -0
  267. package/src/utils.ts +327 -0
  268. package/src/validation/validator.ts +685 -0
  269. package/src/websocket/clustering.ts +762 -0
@@ -0,0 +1,429 @@
1
+ import type { Server } from 'bun'
2
+ import type { EnhancedRequest, HTTPMethod, ServerOptions } from '../types'
3
+ import type { Router } from './router'
4
+ import { runWithRequest, setCurrentRequest } from '../request/context'
5
+
6
+ /**
7
+ * Server handling extension for Router class
8
+ */
9
+ export function registerServerHandling(RouterClass: typeof Router): void {
10
+ Object.defineProperties(RouterClass.prototype, {
11
+ /**
12
+ * Start the HTTP server
13
+ */
14
+ serve: {
15
+ async value(options: ServerOptions = {}): Promise<Server<any>> {
16
+ // Initialize automatic file-based routing (discovers views from src/views, views, etc.)
17
+ if (this._initFileRoutes) {
18
+ await this._initFileRoutes()
19
+ }
20
+
21
+ // Initialize automatic API route discovery (from routes/ directory)
22
+ if (this._initApiRoutes) {
23
+ await this._initApiRoutes()
24
+ }
25
+
26
+ // Invalidate route cache before starting server
27
+ this.invalidateCache()
28
+
29
+ // Create server options
30
+ const serverOptions: any = {
31
+ idleTimeout: 255, // Max allowed timeout (255 seconds)
32
+ ...options,
33
+ fetch: this.handleRequest.bind(this),
34
+ }
35
+
36
+ // Build Bun's static option from registered static Response routes.
37
+ // Static responses use Bun's native zero-allocation dispatch (~15% faster).
38
+ const staticRouteMap: Record<string, Response> = {}
39
+ if (this.staticResponses && this.staticResponses.size > 0) {
40
+ for (const [path, response] of this.staticResponses) {
41
+ staticRouteMap[path] = response
42
+ }
43
+ }
44
+ // Merge with user-provided static routes (user options take precedence)
45
+ if (options.static) {
46
+ Object.assign(staticRouteMap, options.static)
47
+ }
48
+ if (Object.keys(staticRouteMap).length > 0) {
49
+ serverOptions.static = staticRouteMap
50
+ }
51
+
52
+ // Forward development options for HMR and console streaming
53
+ if (options.development !== undefined) {
54
+ serverOptions.development = options.development
55
+ }
56
+
57
+ // Apply WebSocket configuration if provided
58
+ if (this.wsConfig) {
59
+ serverOptions.websocket = this.wsConfig
60
+ }
61
+
62
+ // Start the server
63
+ this.serverInstance = Bun.serve(serverOptions)
64
+
65
+ if (this.config.verbose) {
66
+ const port = this.serverInstance.port
67
+ const hostname = this.serverInstance.hostname
68
+ console.log(`\n🚀 Server running at http://${hostname}:${port}\n`)
69
+ }
70
+
71
+ return this.serverInstance
72
+ },
73
+ writable: true,
74
+ configurable: true,
75
+ },
76
+
77
+ /**
78
+ * Reload the HTTP server
79
+ */
80
+ reload: {
81
+ async value(): Promise<void> {
82
+ if (!this.serverInstance) {
83
+ throw new Error('Server not started, cannot reload')
84
+ }
85
+
86
+ // Invalidate route cache before reloading
87
+ this.invalidateCache()
88
+
89
+ // Save the current server port and hostname
90
+ const port = this.serverInstance.port
91
+ const hostname = this.serverInstance.hostname
92
+
93
+ // Close the current server
94
+ this.serverInstance.stop()
95
+
96
+ // Start a new server with the same configuration
97
+ this.serverInstance = Bun.serve({
98
+ port,
99
+ hostname,
100
+ fetch: this.handleRequest.bind(this),
101
+ websocket: this.wsConfig || undefined,
102
+ })
103
+
104
+ if (this.config.verbose) {
105
+ console.log(`🔄 Server reloaded at http://${hostname}:${port}`)
106
+ }
107
+ },
108
+ writable: true,
109
+ configurable: true,
110
+ },
111
+
112
+ /**
113
+ * Handle HTTP requests
114
+ */
115
+ handleRequest: {
116
+ async value(req: Request): Promise<Response> {
117
+ // The prototype is mutated via defineProperties; the static type doesn't see
118
+ // handleRequestImpl as a public method, so we cast through unknown.
119
+ const self = this as unknown as { handleRequestImpl: (r: Request) => Promise<Response> }
120
+ return runWithRequest(req as EnhancedRequest, () => self.handleRequestImpl(req))
121
+ },
122
+ writable: true,
123
+ configurable: true,
124
+ },
125
+
126
+ /**
127
+ * Internal: actual request handling, executed inside the request context scope.
128
+ */
129
+ handleRequestImpl: {
130
+ async value(req: Request): Promise<Response> {
131
+ try {
132
+ // Create URL for route matching
133
+ const url = new URL(req.url)
134
+
135
+ // Handle CORS preflight OPTIONS requests - but check for registered OPTIONS routes first
136
+ // This ensures explicitly registered OPTIONS routes work while still providing CORS support
137
+ if (req.method === 'OPTIONS') {
138
+ const hostname = url.hostname || req.headers.get('host')?.split(':')[0] || 'localhost'
139
+ const optionsMatch = this.matchRoute(url.pathname, 'OPTIONS', hostname)
140
+ if (!optionsMatch) {
141
+ // No explicit OPTIONS route - return generic CORS preflight response
142
+ return new Response(null, {
143
+ status: 204,
144
+ headers: {
145
+ 'Access-Control-Allow-Origin': '*',
146
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
147
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With, Accept, Origin',
148
+ 'Access-Control-Max-Age': '86400',
149
+ 'Access-Control-Allow-Credentials': 'true',
150
+ },
151
+ })
152
+ }
153
+ // Let the registered OPTIONS route handle it (fall through to normal route matching)
154
+ }
155
+
156
+ // Get domain from the host header
157
+ const hostname = url.hostname || req.headers.get('host')?.split(':')[0] || 'localhost'
158
+
159
+ // Find a matching route
160
+ const match = this.matchRoute(url.pathname, req.method as HTTPMethod, hostname)
161
+
162
+ // Enhance the request with params and other utilities
163
+ const enhancedReq = this.enhanceRequest(req, match?.params || {})
164
+ setCurrentRequest(enhancedReq)
165
+
166
+ if (match) {
167
+ // Add the matched route to the request
168
+ enhancedReq.route = match.route
169
+
170
+ // Collect all middleware to run
171
+ const middlewareStack = [...this.globalMiddleware]
172
+
173
+ // Add route-specific middleware
174
+ if (match.route.middleware && match.route.middleware.length > 0) {
175
+ middlewareStack.push(...match.route.middleware)
176
+ }
177
+
178
+ // Create a final middleware that executes the route handler
179
+ const routeHandlerMiddleware = async (req: EnhancedRequest, _next: any) => {
180
+ return await this.resolveHandler(match.route.handler, req)
181
+ }
182
+
183
+ // Add the route handler as the final middleware
184
+ middlewareStack.push(routeHandlerMiddleware)
185
+
186
+ // Run middleware stack with the route handler at the end
187
+ const response = await this.runMiddleware(enhancedReq, middlewareStack)
188
+
189
+ // Apply modified cookies to the response
190
+ if (response) {
191
+ return this.applyModifiedCookies(response, enhancedReq)
192
+ }
193
+
194
+ // This should not happen since we're always returning a response now
195
+ return new Response('No response from middleware chain', { status: 500 })
196
+ }
197
+
198
+ // No route found - check if the path exists with a different method (405 vs 404).
199
+ // The 404/405 responses below now (a) include path + method in the body so client
200
+ // debugging is one grep away, and (b) flow through globalMiddleware so cross-cutting
201
+ // concerns (X-Request-ID, Server-Timing, audit logging, custom CORS) can observe
202
+ // them. Previously these paths short-circuited entirely.
203
+ const allowedMethods = this.getAllowedMethods(url.pathname, hostname)
204
+ const corsHeaders = {
205
+ 'Content-Type': 'application/json',
206
+ 'Access-Control-Allow-Origin': '*',
207
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
208
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With, Accept, Origin',
209
+ }
210
+
211
+ if (allowedMethods.length > 0) {
212
+ const methodNotAllowedHandler = async (_req: EnhancedRequest, _next: any) => {
213
+ return new Response(JSON.stringify({
214
+ success: false,
215
+ message: 'Method Not Allowed',
216
+ path: url.pathname,
217
+ method: req.method,
218
+ allowed: allowedMethods,
219
+ }), {
220
+ status: 405,
221
+ headers: { ...corsHeaders, Allow: allowedMethods.join(', ') },
222
+ })
223
+ }
224
+ if (this.globalMiddleware.length > 0) {
225
+ const stack = [...this.globalMiddleware, methodNotAllowedHandler]
226
+ const response = await this.runMiddleware(enhancedReq, stack)
227
+ if (response) return this.applyModifiedCookies(response, enhancedReq)
228
+ }
229
+ return await methodNotAllowedHandler(enhancedReq, async () => null as any)
230
+ }
231
+
232
+ // No route found, try the fallback handler
233
+ if (this.fallbackHandler) {
234
+ const response = await this.resolveHandler(this.fallbackHandler, enhancedReq)
235
+ return this.applyModifiedCookies(response, enhancedReq)
236
+ }
237
+
238
+ // No fallback — emit a 404 enriched with path + method, also through
239
+ // globalMiddleware so user middleware sees it.
240
+ const notFoundHandler = async (_req: EnhancedRequest, _next: any) => {
241
+ return new Response(JSON.stringify({
242
+ success: false,
243
+ message: 'Not Found',
244
+ path: url.pathname,
245
+ method: req.method,
246
+ }), { status: 404, headers: corsHeaders })
247
+ }
248
+ if (this.globalMiddleware.length > 0) {
249
+ const stack = [...this.globalMiddleware, notFoundHandler]
250
+ const response = await this.runMiddleware(enhancedReq, stack)
251
+ if (response) return this.applyModifiedCookies(response, enhancedReq)
252
+ }
253
+ return await notFoundHandler(enhancedReq, async () => null as any)
254
+ }
255
+ catch (error) {
256
+ console.error('Error handling request:', error)
257
+
258
+ // Use custom error handler if available
259
+ if (this.errorHandler) {
260
+ return this.errorHandler(error as Error)
261
+ }
262
+
263
+ // Default error response with CORS headers
264
+ return new Response(JSON.stringify({
265
+ success: false,
266
+ message: 'Internal Server Error',
267
+ error: error instanceof Error ? error.message : String(error),
268
+ }), {
269
+ status: 500,
270
+ headers: {
271
+ 'Content-Type': 'application/json',
272
+ 'Access-Control-Allow-Origin': '*',
273
+ 'Access-Control-Allow-Methods': 'GET, POST, PUT, DELETE, PATCH, OPTIONS',
274
+ 'Access-Control-Allow-Headers': 'Content-Type, Authorization, X-Requested-With, Accept, Origin',
275
+ },
276
+ })
277
+ }
278
+ },
279
+ writable: true,
280
+ configurable: true,
281
+ },
282
+
283
+ /**
284
+ * Enhance a request with params and other utilities
285
+ */
286
+ enhanceRequest: {
287
+ value(req: Request, params: Record<string, string> = {}): EnhancedRequest {
288
+ // Lazy cookie parsing
289
+ let parsedCookies: Record<string, string> | null = null
290
+
291
+ const getCookies = () => {
292
+ if (parsedCookies === null) {
293
+ parsedCookies = {}
294
+ const cookieHeader = req.headers.get('cookie') || ''
295
+
296
+ cookieHeader.split(';').forEach((cookie) => {
297
+ const parts = cookie.trim().split('=')
298
+ if (parts.length >= 2) {
299
+ const name = parts[0].trim()
300
+ const value = parts.slice(1).join('=').trim()
301
+ parsedCookies![name] = decodeURIComponent(value)
302
+ }
303
+ })
304
+ }
305
+ return parsedCookies
306
+ }
307
+
308
+ // Create cookie utilities with lazy parsing
309
+ const cookies = {
310
+ get: (name: string) => getCookies()[name],
311
+ set: (name: string, value: string, options: any = {}) => {
312
+ const enhancedRequest = req as EnhancedRequest
313
+ if (!enhancedRequest._cookiesToSet) {
314
+ enhancedRequest._cookiesToSet = []
315
+ }
316
+ enhancedRequest._cookiesToSet.push({ name, value, options })
317
+ },
318
+ delete: (name: string, options: any = {}) => {
319
+ const enhancedRequest = req as EnhancedRequest
320
+ if (!enhancedRequest._cookiesToDelete) {
321
+ enhancedRequest._cookiesToDelete = []
322
+ }
323
+ enhancedRequest._cookiesToDelete.push({ name, options })
324
+ },
325
+ getAll: () => ({ ...getCookies() }),
326
+ }
327
+
328
+ // Create enhanced request
329
+ const enhancedReq = Object.assign(req, {
330
+ params,
331
+ cookies: getCookies(), // Set cookies as plain object for direct access
332
+ _cookiesToSet: [],
333
+ _cookiesToDelete: [],
334
+ }) as unknown as EnhancedRequest
335
+
336
+ // Add cookie methods to the request
337
+ Object.assign(enhancedReq, { cookies: { ...getCookies(), ...cookies } })
338
+
339
+ return enhancedReq
340
+ },
341
+ writable: true,
342
+ configurable: true,
343
+ },
344
+
345
+ /**
346
+ * Apply modified cookies to a response
347
+ */
348
+ applyModifiedCookies: {
349
+ value(response: Response, req: EnhancedRequest): Response {
350
+ // Clone the response to modify headers
351
+ const newResponse = new Response(response.body, {
352
+ status: response.status,
353
+ statusText: response.statusText,
354
+ headers: response.headers,
355
+ })
356
+
357
+ // Apply cookies to set
358
+ if (req._cookiesToSet && req._cookiesToSet.length > 0) {
359
+ for (const { name, value, options } of req._cookiesToSet) {
360
+ const cookieString = this.serializeCookie(name, value, options)
361
+ newResponse.headers.append('Set-Cookie', cookieString)
362
+ }
363
+ }
364
+
365
+ // Apply cookies to delete
366
+ if (req._cookiesToDelete && req._cookiesToDelete.length > 0) {
367
+ for (const { name, options } of req._cookiesToDelete) {
368
+ const deletionOptions = {
369
+ ...options,
370
+ expires: new Date(0), // Set expiration to past date
371
+ maxAge: 0,
372
+ }
373
+ const cookieString = this.serializeCookie(name, '', deletionOptions)
374
+ newResponse.headers.append('Set-Cookie', cookieString)
375
+ }
376
+ }
377
+
378
+ return newResponse
379
+ },
380
+ writable: true,
381
+ configurable: true,
382
+ },
383
+
384
+ /**
385
+ * Serialize a cookie for the Set-Cookie header
386
+ */
387
+ serializeCookie: {
388
+ value(name: string, value: string, options: any = {}): string {
389
+ let cookie = `${encodeURIComponent(name)}=${encodeURIComponent(value)}`
390
+
391
+ if (options.maxAge !== undefined) {
392
+ cookie += `; Max-Age=${options.maxAge}`
393
+ }
394
+
395
+ if (options.expires && options.expires instanceof Date) {
396
+ cookie += `; Expires=${options.expires.toUTCString()}`
397
+ }
398
+
399
+ if (options.path) {
400
+ cookie += `; Path=${options.path}`
401
+ }
402
+ else {
403
+ cookie += '; Path=/'
404
+ }
405
+
406
+ if (options.domain) {
407
+ cookie += `; Domain=${options.domain}`
408
+ }
409
+
410
+ if (options.secure) {
411
+ cookie += '; Secure'
412
+ }
413
+
414
+ if (options.httpOnly) {
415
+ cookie += '; HttpOnly'
416
+ }
417
+
418
+ if (options.sameSite) {
419
+ const sameSite = options.sameSite.toLowerCase()
420
+ cookie += `; SameSite=${sameSite.charAt(0).toUpperCase() + sameSite.slice(1)}`
421
+ }
422
+
423
+ return cookie
424
+ },
425
+ writable: true,
426
+ configurable: true,
427
+ },
428
+ })
429
+ }