@stacksjs/bun-router 0.0.15 → 0.0.17

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 (83) hide show
  1. package/dist/auth.d.ts +22 -8
  2. package/dist/chunk-1ahs68ys.js +18 -0
  3. package/dist/{chunk-j0e7z7hd.js → chunk-cgptvjdf.js} +153 -75
  4. package/dist/chunk-g3ybefhg.js +1923 -0
  5. package/dist/cli.js +13 -11
  6. package/dist/container/container.d.ts +1 -0
  7. package/dist/container/contextual-binding.d.ts +0 -1
  8. package/dist/container/decorators.d.ts +2 -5
  9. package/dist/container/index.d.ts +16 -0
  10. package/dist/container/index.js +322 -0
  11. package/dist/file-serving/static-files.d.ts +16 -0
  12. package/dist/index.d.ts +17 -1
  13. package/dist/index.js +1086 -1270
  14. package/dist/middleware/cors.d.ts +9 -0
  15. package/dist/middleware/csrf.d.ts +7 -0
  16. package/dist/middleware/ddos_protection.d.ts +16 -1
  17. package/dist/middleware/rate_limit.d.ts +7 -1
  18. package/dist/middleware/request_tracer.d.ts +15 -9
  19. package/dist/middleware/response_cache.d.ts +2 -1
  20. package/dist/request/context.d.ts +12 -1
  21. package/dist/request/macros.d.ts +40 -2
  22. package/dist/response/macros.d.ts +3 -3
  23. package/dist/router/fluent-routing.d.ts +50 -13
  24. package/dist/router/handler-resolver.d.ts +8 -0
  25. package/dist/router/index.d.ts +2 -0
  26. package/dist/router/route-compiler.d.ts +8 -10
  27. package/dist/router/route-trie.d.ts +7 -1
  28. package/dist/router/router.d.ts +13 -2
  29. package/dist/router/validation-integration.d.ts +10 -4
  30. package/dist/types/middleware-types.d.ts +1 -1
  31. package/dist/types/route-inference.d.ts +9 -2
  32. package/dist/types.d.ts +62 -9
  33. package/dist/utils.d.ts +1 -1
  34. package/package.json +11 -1
  35. package/src/auth.ts +32 -12
  36. package/src/container/container.ts +58 -12
  37. package/src/container/contextual-binding.ts +4 -1
  38. package/src/container/decorators.ts +16 -7
  39. package/src/container/index.ts +49 -0
  40. package/src/errors/circuit-breaker.ts +3 -2
  41. package/src/errors/graceful-degradation.ts +2 -0
  42. package/src/file-serving/static-files.ts +144 -7
  43. package/src/index.ts +53 -1
  44. package/src/middleware/cors.ts +76 -8
  45. package/src/middleware/csrf.ts +80 -22
  46. package/src/middleware/ddos_protection.ts +27 -3
  47. package/src/middleware/input_validation.ts +10 -6
  48. package/src/middleware/performance_alerting.ts +2 -0
  49. package/src/middleware/performance_monitor.ts +2 -0
  50. package/src/middleware/rate_limit.ts +7 -1
  51. package/src/middleware/request_signing.ts +2 -0
  52. package/src/middleware/request_tracer.ts +34 -17
  53. package/src/middleware/response_cache.ts +48 -13
  54. package/src/middleware/session.ts +3 -3
  55. package/src/observability/metrics.ts +1 -1
  56. package/src/request/context.ts +50 -3
  57. package/src/request/enhanced-request.ts +20 -1
  58. package/src/request/macros.ts +183 -49
  59. package/src/response/macros.ts +3 -3
  60. package/src/router/file-based-routing.ts +17 -10
  61. package/src/router/fluent-routing.ts +117 -48
  62. package/src/router/group-organization.ts +17 -1
  63. package/src/router/handler-resolver.ts +36 -0
  64. package/src/router/http-methods.ts +33 -3
  65. package/src/router/index.ts +6 -0
  66. package/src/router/middleware.ts +3 -0
  67. package/src/router/optimized-route-matching.ts +58 -9
  68. package/src/router/route-compiler.ts +62 -73
  69. package/src/router/route-matching.ts +19 -0
  70. package/src/router/route-trie.ts +34 -48
  71. package/src/router/router.ts +72 -60
  72. package/src/router/server.ts +361 -139
  73. package/src/router/validation-integration.ts +11 -5
  74. package/src/testing/auth-testing.ts +6 -4
  75. package/src/types/middleware-types.ts +7 -4
  76. package/src/types/route-inference.ts +29 -14
  77. package/src/types.ts +63 -7
  78. package/src/url.ts +7 -2
  79. package/src/utils.ts +107 -39
  80. package/src/validation/validator.ts +7 -4
  81. package/src/websocket/clustering.ts +8 -1
  82. package/dist/router/fluent-router.d.ts +0 -315
  83. package/src/router/fluent-router.ts +0 -927
@@ -81,6 +81,14 @@ export class Router {
81
81
  // Middleware pipeline for advanced features
82
82
  _middlewarePipeline?: MiddlewarePipeline
83
83
 
84
+ // Bumped whenever global middleware changes; per-route compiled chains
85
+ // cache against this epoch and rebuild when it moves
86
+ _mwEpoch = 0
87
+
88
+ // Memoized getAllowedMethods results (the 405-vs-404 scan), keyed by
89
+ // domain:pathname. Cleared whenever routes change.
90
+ _allowedMethodsCache: Map<string, string[]> = new Map()
91
+
84
92
  config: RouterConfig = {
85
93
  verbose: false,
86
94
  routesPath: 'routes',
@@ -233,6 +241,7 @@ export class Router {
233
241
  */
234
242
  invalidateCache(): void {
235
243
  this.routeCache.clear()
244
+ this._allowedMethodsCache.clear()
236
245
  }
237
246
 
238
247
  /**
@@ -758,61 +767,46 @@ export class Router {
758
767
  */
759
768
  getAllowedMethods(path: string, domain?: string): string[] {
760
769
  const url = new URL(path, 'http://localhost')
761
- const methods: Set<string> = new Set()
762
- const allMethods = ['GET', 'POST', 'PUT', 'PATCH', 'DELETE', 'OPTIONS', 'HEAD']
763
-
764
- for (const method of allMethods) {
765
- // Check static routes
766
- if (this.staticRoutes.has(method)) {
767
- const staticRoute = this.staticRoutes.get(method)!.get(url.pathname)
768
- if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
769
- methods.add(method)
770
- continue
771
- }
772
- }
773
770
 
774
- // Get potential routes
775
- const potentialRoutes: Route[] = domain && this.domains[domain]
776
- ? this.domains[domain]
777
- : this.routes
771
+ // Memoized per path: every unmatched request pays this scan for its
772
+ // 405-vs-404 decision, and 404 floods tend to hammer the same paths
773
+ const cacheKey = `${domain || ''}:${url.pathname}`
774
+ const cached = this._allowedMethodsCache.get(cacheKey)
775
+ if (cached) {
776
+ return cached
777
+ }
778
778
 
779
- // Filter routes to only those matching the HTTP method
780
- const methodRoutes = potentialRoutes.filter((route: Route) => route.method === method)
779
+ const methods: Set<string> = new Set()
781
780
 
782
- // Check exact match
783
- for (const route of methodRoutes) {
784
- if (route.path === url.pathname) {
785
- methods.add(method)
786
- break
787
- }
781
+ // Static routes: one map lookup per registered method
782
+ for (const [method, routesByPath] of this.staticRoutes) {
783
+ const staticRoute = routesByPath.get(url.pathname)
784
+ if (staticRoute && (!domain || !staticRoute.domain || staticRoute.domain === domain)) {
785
+ methods.add(method)
788
786
  }
787
+ }
789
788
 
790
- // Check pattern match if not already found
791
- if (!methods.has(method)) {
792
- for (const route of methodRoutes) {
793
- if (route.pattern) {
794
- const match = route.pattern.exec(url)
795
- if (match) {
796
- methods.add(method)
797
- break
798
- }
799
- }
800
- }
801
- }
789
+ // Dynamic routes: a single pass over the candidate pool covers exact,
790
+ // pattern, and wildcard matches for every method at once (the old
791
+ // implementation re-scanned the table once per HTTP method)
792
+ const potentialRoutes: Route[] = domain && this.domains[domain]
793
+ ? this.domains[domain]
794
+ : this.routes
802
795
 
803
- // Check wildcard routes if not already found
804
- if (!methods.has(method)) {
805
- const wildcardRoutes = potentialRoutes.filter((route: Route) =>
806
- route.method === method && route.path.endsWith('*'),
807
- )
808
-
809
- for (const route of wildcardRoutes) {
810
- const basePath = route.path.slice(0, -1)
811
- if (url.pathname.startsWith(basePath)) {
812
- methods.add(method)
813
- break
814
- }
815
- }
796
+ for (const route of potentialRoutes) {
797
+ if (methods.has(route.method)) {
798
+ continue
799
+ }
800
+ if (route.path === url.pathname) {
801
+ methods.add(route.method)
802
+ continue
803
+ }
804
+ if (route.pattern && route.pattern.exec(url)) {
805
+ methods.add(route.method)
806
+ continue
807
+ }
808
+ if (route.path.endsWith('*') && url.pathname.startsWith(route.path.slice(0, -1))) {
809
+ methods.add(route.method)
816
810
  }
817
811
  }
818
812
 
@@ -821,7 +815,12 @@ export class Router {
821
815
  methods.add('HEAD')
822
816
  }
823
817
 
824
- return Array.from(methods)
818
+ const result = Array.from(methods)
819
+ if (this._allowedMethodsCache.size >= 10_000) {
820
+ this._allowedMethodsCache.clear()
821
+ }
822
+ this._allowedMethodsCache.set(cacheKey, result)
823
+ return result
825
824
  }
826
825
 
827
826
  /**
@@ -1350,9 +1349,16 @@ export class Router {
1350
1349
  }
1351
1350
 
1352
1351
  /**
1353
- * Create a route group with prefix and middleware
1352
+ * Create a route group with prefix and middleware.
1353
+ *
1354
+ * Synchronous callbacks return the router for chaining. Asynchronous
1355
+ * callbacks return a promise that resolves once the callback (and any
1356
+ * routes it registers) has finished — await it, or routes registered
1357
+ * after an `await` inside the callback would lose the group prefix.
1354
1358
  */
1355
- group(options: { prefix?: string, middleware?: (string | MiddlewareHandler)[] }, callback: () => void | Promise<void>): Router {
1359
+ group(options: { prefix?: string, middleware?: (string | MiddlewareHandler)[] }, callback: () => Promise<void>): Promise<Router>
1360
+ group(options: { prefix?: string, middleware?: (string | MiddlewareHandler)[] }, callback: () => void): Router
1361
+ group(options: { prefix?: string, middleware?: (string | MiddlewareHandler)[] }, callback: () => void | Promise<void>): Router | Promise<Router> {
1356
1362
  // Save current group state
1357
1363
  const previousGroup = this.currentGroup
1358
1364
 
@@ -1374,17 +1380,23 @@ export class Router {
1374
1380
  // Execute callback
1375
1381
  const result = callback()
1376
1382
 
1377
- // Handle async callbacks
1383
+ // Async callbacks keep the group active until they settle
1378
1384
  if (result instanceof Promise) {
1379
- result.then(() => {
1380
- this.currentGroup = previousGroup
1381
- })
1382
- }
1383
- else {
1384
- // Restore previous group state
1385
- this.currentGroup = previousGroup
1385
+ return result.then(
1386
+ () => {
1387
+ this.currentGroup = previousGroup
1388
+ return this
1389
+ },
1390
+ (error: unknown) => {
1391
+ this.currentGroup = previousGroup
1392
+ throw error
1393
+ },
1394
+ )
1386
1395
  }
1387
1396
 
1397
+ // Restore previous group state
1398
+ this.currentGroup = previousGroup
1399
+
1388
1400
  return this
1389
1401
  }
1390
1402