@stacksjs/bun-router 0.0.12 → 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 -48
  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
package/README.md CHANGED
@@ -1,397 +1,720 @@
1
- # @bun-router
1
+ <p align="center"><img src=".github/art/cover.jpg" alt="Social Card of this repo"></p>
2
2
 
3
- A Laravel-inspired router for Bun applications.
3
+ [![npm version][npm-version-src]][npm-version-href]
4
+ [![GitHub Actions][github-actions-src]][github-actions-href]
5
+ [![Commitizen friendly](https://img.shields.io/badge/commitizen-friendly-brightgreen.svg)](http://commitizen.github.io/cz-cli/)
6
+ <!-- [![npm downloads][npm-downloads-src]][npm-downloads-href] -->
7
+ <!-- [![Codecov][codecov-src]][codecov-href] -->
8
+
9
+ # bun-router
10
+
11
+ A high-performance, feature-rich router for Bun applications.
4
12
 
5
13
  ## Features
6
14
 
7
- - Laravel-style routing API
8
- - Support for route parameters
9
- - Route grouping
10
- - Action class support
11
- - Middleware support (global and route-specific)
12
- - Built on top of Bun's native HTTP server
15
+ - Fast and efficient routing system
16
+ - Support for all HTTP methods
17
+ - Path parameters and parameter constraints
18
+ - Middleware support with built-in middleware
19
+ - Group routing, resource routing, and nested routes
20
+ - Named routes and URL generation
21
+ - Domain and subdomain routing
22
+ - CSRF protection and session management
23
+ - Type-safe API
24
+ - Native Bun.serve() integration
25
+ - WebSocket support
13
26
 
14
27
  ## Installation
15
28
 
16
29
  ```bash
17
- bun add @bun-router
30
+ bun add bun-router
18
31
  ```
19
32
 
20
33
  ## Basic Usage
21
34
 
22
35
  ```typescript
23
- import { route } from '@bun-router'
36
+ import { Router } from 'bun-router'
24
37
 
25
- // Basic route with inline handler
26
- route.get('/', () => new Response('Hello World!'))
38
+ // Create a router
39
+ const router = new Router()
27
40
 
28
- // Route with parameters
29
- route.get('/users/{id}', (req) => {
30
- const { id } = req.params
31
- return Response.json({ userId: id })
41
+ // Define routes
42
+ router.get('/', () => new Response('Hello, World!'))
43
+ router.post('/users', async (req) => {
44
+ const data = await req.json()
45
+ return Response.json({ message: 'User created', data })
32
46
  })
33
47
 
34
- // Route with action class
35
- route.post('/subscribe', 'Actions/SubscribeAction')
36
-
37
- // Route grouping
38
- route.group({ prefix: '/api' }, () => {
39
- route.get('/users', 'Actions/User/IndexAction')
40
- route.post('/users', 'Actions/User/StoreAction')
41
- })
42
-
43
- // Health check route
44
- route.health()
45
-
46
48
  // Start the server
47
- route.serve({
49
+ router.serve({
48
50
  port: 3000,
49
51
  })
50
52
  ```
51
53
 
52
- ## Action Classes
54
+ ## Route Options
55
+
56
+ ```typescript
57
+ // Route with path parameters
58
+ router.get('/users/{id}', (req) => {
59
+ const { id } = req.params
60
+ return Response.json({ id })
61
+ })
62
+
63
+ // Named routes
64
+ router.get('/users/{id}', getUserHandler, 'api', 'users.show')
53
65
 
54
- Action classes provide a clean way to organize your route handlers. Create a class that implements a `handle` method:
66
+ // Generate URL for named route
67
+ const url = router.route('users.show', { id: '123' })
68
+ ```
69
+
70
+ ## Middleware
55
71
 
56
72
  ```typescript
57
- // actions/subscribe_action.ts
58
- import type { EnhancedRequest } from '@bun-router'
73
+ import { cors, jsonBody, Router } from 'bun-router'
74
+
75
+ const router = new Router()
59
76
 
60
- export default class SubscribeAction {
61
- async handle(request: EnhancedRequest): Promise<Response> {
62
- const data = await request.json()
77
+ // Use middleware globally
78
+ router.use(jsonBody())
79
+ router.use(cors())
63
80
 
64
- // Handle subscription logic
81
+ // Or apply to a group of routes
82
+ router.group({
83
+ prefix: '/api',
84
+ middleware: [jsonBody(), cors()]
85
+ }, () => {
86
+ router.get('/users', () => Response.json({ users: [] }))
87
+ })
88
+ ```
89
+
90
+ ### Built-in Middleware
91
+
92
+ - **Cors** - Handles Cross-Origin Resource Sharing
93
+ - **JsonBody** - Parses JSON request bodies into `req.jsonBody`
94
+ - **RequestId** - Adds unique IDs to requests with `X-Request-ID` header
95
+ - **Session** - Provides session management with `req.session`
96
+ - **Csrf** - Protects against cross-site request forgery
97
+ - **Auth** - Basic authentication middleware
98
+
99
+ ### Creating Custom Middleware
100
+
101
+ ```typescript
102
+ import { EnhancedRequest, MiddlewareHandler, NextFunction } from 'bun-router'
65
103
 
66
- return Response.json({
67
- success: true,
68
- message: 'Subscribed successfully'
69
- })
104
+ class LoggerMiddleware {
105
+ async handle(req: EnhancedRequest, next: NextFunction): Promise<Response> {
106
+ console.log(`${req.method} ${req.url}`)
107
+ return next()
70
108
  }
71
109
  }
110
+
111
+ // Use custom middleware
112
+ router.use(new LoggerMiddleware())
72
113
  ```
73
114
 
74
- ## Middleware
115
+ ## WebSocket Support
75
116
 
76
- Middleware allows you to run code before your route handlers. You can use middleware globally or for specific routes/groups.
117
+ bun-router provides seamless integration with Bun's high-performance WebSocket capabilities:
77
118
 
78
- ### Creating Middleware
119
+ ```typescript
120
+ import type { ServerWebSocket } from 'bun'
121
+ import { Router } from 'bun-router'
79
122
 
80
- Create a middleware class that implements the `handle` method:
123
+ // Define data type for WebSocket clients
124
+ interface ClientData {
125
+ userId: string
126
+ room: string
127
+ }
81
128
 
82
- ```typescript
83
- // middleware/auth.ts
84
- import type { EnhancedRequest, Middleware, NextFunction } from '@bun-router'
129
+ // Create a router
130
+ const router = new Router<ClientData>() // Type-safe WebSockets
85
131
 
86
- export default class AuthMiddleware implements Middleware {
87
- async handle(req: EnhancedRequest, next: NextFunction): Promise<Response> {
88
- const authHeader = req.headers.get('Authorization')
132
+ // Add a regular HTTP route
133
+ router.get('/', () => new Response('WebSocket Server'))
134
+
135
+ // Configure WebSocket handling
136
+ router.websocket({
137
+ // Handle new connections
138
+ open(ws) {
139
+ console.log('Client connected from:', ws.remoteAddress)
140
+
141
+ // Set client data (available in all handlers as ws.data)
142
+ ws.data = { userId: 'user_123', room: 'general' }
143
+
144
+ // Subscribe to topics for pub/sub messaging
145
+ ws.subscribe('general')
146
+ ws.send('Welcome to the server!')
147
+ },
89
148
 
90
- if (!authHeader) {
91
- return new Response('Unauthorized', { status: 401 })
149
+ // Handle incoming messages
150
+ message(ws, message) {
151
+ // Handle different message types (string, ArrayBuffer, Uint8Array)
152
+ const content = typeof message === 'string' ? message : 'Binary data received'
153
+ console.log(`Received from ${ws.data.userId}: ${content}`)
154
+
155
+ // Send response and check for backpressure
156
+ const sendResult = ws.send(`Echo: ${content}`)
157
+
158
+ if (sendResult === -1) {
159
+ console.log('Backpressure detected, message queued')
160
+ }
161
+ else if (sendResult === 0) {
162
+ console.log('Send failed, connection may be closed')
163
+ }
164
+ else {
165
+ console.log(`Sent ${sendResult} bytes`)
92
166
  }
93
167
 
94
- // If auth passes, continue to next middleware or route handler
95
- return next()
168
+ // Broadcast to all subscribers of a topic (except sender)
169
+ router.publish('general', `${ws.data.userId}: ${content}`)
170
+ },
171
+
172
+ // Handle disconnections
173
+ close(ws, code, reason) {
174
+ console.log(`Client ${ws.data.userId} disconnected: ${reason || 'No reason'} (${code})`)
175
+ ws.unsubscribe('general')
176
+ },
177
+
178
+ // Handle errors
179
+ error(ws, error) {
180
+ console.error(`Error for client ${ws.data.userId}:`, error)
181
+ },
182
+
183
+ // Handle backpressure relief
184
+ drain(ws) {
185
+ console.log(`Backpressure relieved for ${ws.data.userId}, socket ready for more data`)
186
+ },
187
+
188
+ // Advanced configuration options
189
+ maxPayloadLength: 16 _ 1024 _ 1024, // 16MB max message size (default)
190
+ idleTimeout: 120, // 2 minutes (default)
191
+ backpressureLimit: 1024 _ 1024, // 1MB (default)
192
+ closeOnBackpressureLimit: false, // Don't close on backpressure limit (default)
193
+
194
+ // Enable per-message compression
195
+ perMessageDeflate: {
196
+ compress: '16KB', // Use 16KB compression level
197
+ decompress: true
198
+ },
199
+
200
+ sendPings: true, // Send ping frames to keep connection alive (default)
201
+ publishToSelf: false // Don't send published messages to publisher (default)
202
+ })
203
+
204
+ // Start the server
205
+ router.serve({ port: 3000 })
206
+ ```
207
+
208
+ ### WebSocket Utility Methods
209
+
210
+ The router provides utility methods for working with WebSockets:
211
+
212
+ ```typescript
213
+ // Publish a message to all subscribers of a topic
214
+ // Returns: Number of bytes sent (or negative on error)
215
+ const result = router.publish('room-123', JSON.stringify({
216
+ type: 'message',
217
+ text: 'Hello!'
218
+ }), true) // Optional: enable compression
219
+
220
+ // Get the number of subscribers for a topic
221
+ const count = router.subscriberCount('room-123')
222
+
223
+ // Upgrade an HTTP request to a WebSocket connection
224
+ router.get('/custom-upgrade', (req) => {
225
+ const success = router.upgrade(req, {
226
+ // Optional custom headers for the 101 Switching Protocols response
227
+ headers: { 'X-Custom-Header': 'value' },
228
+
229
+ // Custom data to attach to the WebSocket
230
+ data: {
231
+ userId: '123',
232
+ authenticated: true,
233
+ permissions: ['read', 'write']
234
+ }
235
+ })
236
+
237
+ if (!success) {
238
+ return new Response('Failed to upgrade connection', { status: 400 })
96
239
  }
97
- }
240
+
241
+ // If upgrade is successful, this response is ignored
242
+ return new Response('Upgraded to WebSocket')
243
+ })
244
+
245
+ // Get client IP address
246
+ router.get('/ip', (req) => {
247
+ const ip = router.requestIP(req)
248
+ return Response.json(ip)
249
+ })
250
+
251
+ // Set custom timeout for a request
252
+ router.get('/long-operation', (req) => {
253
+ // Extend timeout to 5 minutes for this specific request
254
+ router.timeout(req, 300)
255
+
256
+ // Perform long operation...
257
+ return new Response('Operation completed')
258
+ })
98
259
  ```
99
260
 
100
- ### Using Middleware
261
+ ### WebSocket Patterns
101
262
 
102
- You can use middleware in several ways:
263
+ Here are some common patterns for working with WebSockets:
103
264
 
104
- 1. Global Middleware (applies to all routes):
265
+ #### JSON Communication
105
266
 
106
267
  ```typescript
107
- route.use('Middleware/Auth')
108
- route.use('Middleware/Logger')
109
-
110
- // Or with inline middleware
111
- route.use(async (req, next) => {
112
- console.log(`${req.method} ${req.url}`)
113
- return next()
268
+ // Client-side
269
+ const ws = new WebSocket('ws://localhost:3000/ws')
270
+ ws.send(JSON.stringify({ type: 'login', userId: '123' }))
271
+
272
+ // Server-side
273
+ router.websocket({
274
+ message(ws, message) {
275
+ try {
276
+ const data = JSON.parse(message.toString())
277
+
278
+ switch (data.type) {
279
+ case 'login':
280
+ handleLogin(ws, data.userId)
281
+ break
282
+ case 'message':
283
+ handleMessage(ws, data)
284
+ break
285
+ }
286
+ }
287
+ catch (e) {
288
+ ws.send(JSON.stringify({ error: 'Invalid JSON' }))
289
+ }
290
+ }
114
291
  })
115
292
  ```
116
293
 
117
- 2. Group Middleware (applies to all routes in a group):
294
+ #### Room-Based Chat
118
295
 
119
296
  ```typescript
120
- route.group({
121
- prefix: '/api',
122
- middleware: ['Middleware/Auth']
123
- }, () => {
124
- route.get('/users', 'Actions/User/IndexAction')
125
- route.post('/users', 'Actions/User/StoreAction')
297
+ router.websocket({
298
+ open(ws) {
299
+ ws.data = { userId: generateId(), room: 'lobby' }
300
+ ws.subscribe('lobby')
301
+ broadcastToRoom('lobby', `${ws.data.userId} joined the lobby`)
302
+ },
303
+
304
+ message(ws, message) {
305
+ const text = message.toString()
306
+
307
+ if (text.startsWith('/join ')) {
308
+ const newRoom = text.slice(6).trim()
309
+
310
+ // Leave current room
311
+ const oldRoom = ws.data.room
312
+ ws.unsubscribe(oldRoom)
313
+ broadcastToRoom(oldRoom, `${ws.data.userId} left the room`)
314
+
315
+ // Join new room
316
+ ws.data.room = newRoom
317
+ ws.subscribe(newRoom)
318
+ broadcastToRoom(newRoom, `${ws.data.userId} joined the room`)
319
+
320
+ ws.send(`You joined ${newRoom}`)
321
+ }
322
+ else {
323
+ // Regular message
324
+ broadcastToRoom(ws.data.room, `${ws.data.userId}: ${text}`)
325
+ }
326
+ }
126
327
  })
328
+
329
+ function broadcastToRoom(room, message) {
330
+ router.publish(room, message)
331
+ }
127
332
  ```
128
333
 
129
- 3. Inline Middleware:
334
+ #### Handling Backpressure
130
335
 
131
336
  ```typescript
132
- route.group({
133
- middleware: [
134
- async (req, next) => {
135
- console.log('Processing request...')
136
- const response = await next()
137
- console.log('Request complete')
138
- return response
337
+ router.websocket({
338
+ message(ws, message) {
339
+ // Send a large response
340
+ const largeData = generateLargeResponse()
341
+ const result = ws.send(largeData)
342
+
343
+ if (result === -1) {
344
+ // Message was queued due to backpressure
345
+ console.log('Backpressure detected, will process more in drain event')
346
+
347
+ // Store state to resume in drain handler
348
+ ws.data.pendingOperations = [/_ ...operations to complete _/]
139
349
  }
140
- ]
141
- }, () => {
142
- route.get('/users', 'Actions/User/IndexAction')
350
+ },
351
+
352
+ drain(ws) {
353
+ // Socket is ready to receive more data
354
+ if (ws.data.pendingOperations?.length) {
355
+ const nextOp = ws.data.pendingOperations.shift()
356
+ processOperation(ws, nextOp)
357
+ }
358
+ }
143
359
  })
144
360
  ```
145
361
 
146
- ## Route Groups
362
+ ## Bun-Native Features
147
363
 
148
- Group related routes with a common prefix and middleware:
364
+ bun-router fully integrates with Bun's native `Bun.serve()` API, leveraging the latest Bun features for optimal performance.
365
+
366
+ ### Static Responses
367
+
368
+ Define static routes without handler functions for optimal performance:
149
369
 
150
370
  ```typescript
151
- route.group({
152
- prefix: '/api/v1',
153
- middleware: ['Middleware/Auth', 'Middleware/RateLimit']
154
- }, () => {
155
- // All routes here will be prefixed with /api/v1
156
- route.get('/users', 'Actions/User/IndexAction')
157
- route.post('/users', 'Actions/User/StoreAction')
158
-
159
- // Nested groups
160
- route.group({
161
- prefix: '/admin',
162
- middleware: ['Middleware/AdminAuth']
163
- }, () => {
164
- route.get('/stats', 'Actions/Admin/StatsAction')
165
- })
166
- })
371
+ router.get('/health', () => new Response('OK'))
372
+ router.get('/ready', () => new Response('Ready', {
373
+ headers: { 'X-Ready': '1' }
374
+ }))
375
+
376
+ // These are automatically optimized to Bun's static routes internally
167
377
  ```
168
378
 
169
- ## Route Parameters
379
+ ### Method-Specific Handlers
170
380
 
171
- Access route parameters through the `params` object:
381
+ The router automatically organizes multiple methods for the same path into Bun's method-specific handlers:
172
382
 
173
383
  ```typescript
174
- route.get('/users/{id}/posts/{postId}', (req) => {
175
- const { id, postId } = req.params
176
- return Response.json({ userId: id, postId })
177
- })
384
+ router.get('/api/posts', getPosts)
385
+ router.post('/api/posts', createPost)
386
+ router.put('/api/posts/{id}', updatePost)
387
+ router.delete('/api/posts/{id}', deletePost)
388
+
389
+ // These will be organized into a more efficient format:
390
+ // '/api/posts': {
391
+ // GET: getPosts,
392
+ // POST: createPost
393
+ // },
394
+ // '/api/posts/{id}': {
395
+ // PUT: updatePost,
396
+ // DELETE: deletePost
397
+ // }
178
398
  ```
179
399
 
180
- ## TypeScript Support
400
+ ### Hot Reloading
181
401
 
182
- The router is written in TypeScript and provides full type definitions:
402
+ Update routes without restarting the server:
183
403
 
184
404
  ```typescript
185
- import type { ActionHandler, EnhancedRequest, Middleware, NextFunction } from '@bun-router'
405
+ // Initial setup
406
+ const router = new Router()
407
+ router.get('/api/version', () => Response.json({ version: '1.0.0' }))
408
+ const server = await router.serve({ port: 3000 })
409
+
410
+ // Later, update routes without downtime
411
+ router.get('/api/version', () => Response.json({ version: '2.0.0' }))
412
+ await router.reload()
413
+ ```
186
414
 
187
- // Type-safe request handling
188
- const handler: ActionHandler = (req: EnhancedRequest) => {
189
- const { id } = req.params
190
- return Response.json({ id })
191
- }
415
+ ### Error Handling
192
416
 
193
- // Type-safe middleware
194
- const loggerMiddleware: Middleware = {
195
- handle: async (req: EnhancedRequest, next: NextFunction) => {
196
- console.log(`${req.method} ${req.url}`)
197
- return next()
198
- }
199
- }
417
+ Add a global error handler for all routes:
418
+
419
+ ```typescript
420
+ router.onError((error) => {
421
+ console.error(error)
422
+ return new Response(`Server Error: ${error.message}`, {
423
+ status: 500,
424
+ headers: { 'Content-Type': 'text/plain' }
425
+ })
426
+ })
200
427
 
201
- route.use(loggerMiddleware.handle)
202
- route.get('/users/{id}', handler)
428
+ // Define a route that might throw an error
429
+ router.get('/api/risky', () => {
430
+ throw new Error('Something went wrong')
431
+ })
432
+
433
+ // Serve with the error handler
434
+ router.serve({ port: 3000 })
203
435
  ```
204
436
 
205
- ## Streaming Support
437
+ ### Type-Safe Route Parameters
206
438
 
207
- The router provides comprehensive streaming capabilities for modern web applications:
439
+ TypeScript automatically infers parameter types from route paths:
208
440
 
209
- ### File Streaming
441
+ ```typescript
442
+ router.get('/orgs/{orgId}/repos/{repoId}', (req) => {
443
+ // TypeScript knows the shape of req.params
444
+ const { orgId, repoId } = req.params
445
+ return Response.json({ orgId, repoId })
446
+ })
447
+ ```
448
+
449
+ ### Cookie Handling
210
450
 
211
- Stream files with automatic content-type detection:
451
+ Built-in support for working with cookies:
212
452
 
213
453
  ```typescript
214
- // Basic file streaming
215
- route.get('/download/{filename}', (req) => {
216
- const { filename } = req.params
217
- return route.streamFile(`./uploads/${filename}`)
454
+ router.get('/profile', (req) => {
455
+ // Read cookies
456
+ const userId = req.cookies.get('user_id')
457
+ const theme = req.cookies.get('theme') || 'light'
458
+
459
+ return Response.json({ userId, theme })
218
460
  })
219
461
 
220
- // File streaming with range support (for video/audio)
221
- route.get('/video/{id}', async (req) => {
222
- const { id } = req.params
223
- const videoPath = `./videos/${id}.mp4`
224
- return await route.streamFileWithRanges(videoPath, req)
462
+ router.get('/login', (req) => {
463
+ // Set cookies
464
+ req.cookies.set('user_id', '12345', {
465
+ httpOnly: true,
466
+ secure: true,
467
+ maxAge: 60 _ 60 * 24 // 1 day
468
+ })
469
+
470
+ return new Response('Logged in')
471
+ })
472
+
473
+ router.get('/logout', (req) => {
474
+ // Delete cookies
475
+ req.cookies.delete('user_id')
476
+ return new Response('Logged out')
225
477
  })
226
478
  ```
227
479
 
228
- ### Response Streaming
480
+ ### Authentication Helper
229
481
 
230
- Create streaming routes with clean, top-level API methods:
482
+ Built-in methods for handling various authentication strategies:
231
483
 
232
484
  ```typescript
233
- // Default streaming (uses Bun's async generator optimization)
234
- route.stream('/stream-data', async function* () {
235
- for (let i = 0; i < 100; i++) {
236
- yield `Chunk ${i}\n`
237
- await new Promise(resolve => setTimeout(resolve, 100))
485
+ // Basic Authentication
486
+ router.get('/api/protected', (req) => {
487
+ const auth = req.auth.basic()
488
+
489
+ if (!auth.isValid()) {
490
+ return auth.unauthorized('Protected area')
238
491
  }
492
+
493
+ const { username, password } = auth.credentials()
494
+ // Verify against your user database
495
+
496
+ return Response.json({ message: 'Authenticated' })
239
497
  })
240
498
 
241
- // Direct streaming for high-performance scenarios
242
- route.streamDirect('/stream-direct', async ({ write, close }) => {
243
- for (let i = 0; i < 1000; i++) {
244
- write(`Data chunk ${i}\n`)
245
- if (i % 100 === 0) {
246
- await new Promise(resolve => setTimeout(resolve, 10))
247
- }
499
+ // Bearer Token Authentication
500
+ router.get('/api/user-profile', (req) => {
501
+ const auth = req.auth.bearer()
502
+
503
+ if (!auth.isValid()) {
504
+ return auth.unauthorized('Invalid token')
248
505
  }
249
- close()
506
+
507
+ const token = auth.token()
508
+ // Verify token validity
509
+
510
+ return Response.json({ message: 'Valid token' })
250
511
  })
251
512
 
252
- // Buffered streaming using Bun.ArrayBufferSink
253
- route.streamBuffered('/stream-buffered', async ({ write, flush, end }) => {
254
- for (let i = 0; i < 1000; i++) {
255
- write(`Item ${i}\n`)
256
- if (i % 50 === 0) {
257
- flush() // Flush buffer every 50 items
258
- }
259
- }
260
- end()
261
- }, { highWaterMark: 1024 * 1024 }) // 1MB buffer
262
- ```
513
+ // JWT Authentication
514
+ router.post('/api/login', async (req) => {
515
+ const { username, password } = await req.json()
516
+ // Verify credentials
263
517
 
264
- ### Server-Sent Events (SSE)
518
+ const auth = req.auth.jwt()
519
+ const token = auth.sign({ userId: 123, role: 'admin' }, {
520
+ expiresIn: '1h',
521
+ secret: 'your-secret-key'
522
+ })
265
523
 
266
- Real-time data streaming to web clients:
524
+ return Response.json({ token })
525
+ })
267
526
 
268
- ```typescript
269
- // Basic SSE endpoint
270
- route.streamSSE('/events', async function* () {
271
- let counter = 0
272
- while (true) {
273
- yield {
274
- data: { timestamp: Date.now(), counter: counter++ },
275
- event: 'update',
276
- id: `msg-${counter}`
277
- }
278
- await new Promise(resolve => setTimeout(resolve, 1000))
527
+ router.get('/api/dashboard', (req) => {
528
+ const auth = req.auth.jwt()
529
+
530
+ if (!auth.verify({ secret: 'your-secret-key' })) {
531
+ return auth.unauthorized('Invalid JWT')
279
532
  }
533
+
534
+ const payload = auth.payload()
535
+ return Response.json({ user: payload })
280
536
  })
281
537
 
282
- // SSE with custom retry interval
283
- route.streamSSE('/notifications', async function* () {
284
- yield {
285
- data: 'Connection established',
286
- event: 'connected',
287
- retry: 5000 // Retry after 5 seconds if connection drops
538
+ // API Key Authentication
539
+ router.get('/api/data', (req) => {
540
+ const auth = req.auth.apiKey('x-api-key')
541
+
542
+ if (!auth.isValid()) {
543
+ return auth.unauthorized('Invalid API key')
288
544
  }
289
-
290
- // Stream notifications...
545
+
546
+ const apiKey = auth.key()
547
+ // Verify API key against database and check scopes
548
+
549
+ return Response.json({ data: 'Secure data' })
291
550
  })
292
- ```
293
551
 
294
- ### JSON Streaming (NDJSON)
552
+ // OAuth2 Authentication
553
+ router.get('/auth/github', (req) => {
554
+ const auth = req.auth.oauth2({
555
+ provider: 'github',
556
+ clientId: 'your-client-id',
557
+ redirectUri: 'http://localhost:3000/auth/callback'
558
+ })
295
559
 
296
- Stream JSON objects line by line:
560
+ return auth.redirect()
561
+ })
297
562
 
298
- ```typescript
299
- // Stream database results
300
- route.streamJSON('/users/stream', async function* () {
301
- const users = await getUsersFromDatabase()
302
- for (const user of users) {
303
- yield { id: user.id, name: user.name, email: user.email }
304
- }
563
+ router.get('/auth/callback', async (req) => {
564
+ const auth = req.auth.oauth2({
565
+ provider: 'github',
566
+ clientId: 'your-client-id',
567
+ clientSecret: 'your-client-secret',
568
+ redirectUri: 'http://localhost:3000/auth/callback'
569
+ })
570
+
571
+ const { accessToken, profile } = await auth.handleCallback(req)
572
+ // Create or update user record
573
+
574
+ return Response.redirect('/dashboard')
305
575
  })
306
576
 
307
- // Stream large datasets efficiently
308
- route.streamJSON('/analytics/data', async function* () {
309
- for (let page = 1; page <= 100; page++) {
310
- const data = await fetchAnalyticsPage(page)
311
- for (const record of data) {
312
- yield record
577
+ ### File Streaming
578
+
579
+ Easily stream files with range support:
580
+
581
+ ```typescript
582
+ router.get('/files/{filename}', async (req) => {
583
+ const filename = req.params.filename
584
+ const path = `./uploads/${filename}`
585
+
586
+ // Simple file streaming
587
+ return router.streamFile(path, {
588
+ headers: {
589
+ 'Content-Type': 'application/octet-stream',
590
+ 'Content-Disposition': `attachment; filename="${filename}"`
313
591
  }
314
- }
592
+ })
593
+ })
594
+
595
+ // With range support for video/audio streaming
596
+ router.get('/videos/{id}', async (req) => {
597
+ const videoPath = `./videos/${req.params.id}.mp4`
598
+ return router.streamFileWithRanges(videoPath, req)
315
599
  })
316
600
  ```
317
601
 
318
- ### Transform Streams
602
+ ## Route Groups
603
+
604
+ ```typescript
605
+ router.group({
606
+ prefix: '/api',
607
+ middleware: [jsonBody()]
608
+ }, () => {
609
+ router.get('/users', getUsersHandler)
610
+ router.post('/users', createUserHandler)
611
+ })
612
+ ```
319
613
 
320
- Process incoming request streams using Bun's TransformStream:
614
+ ## Parameter Constraints
321
615
 
322
616
  ```typescript
323
- // Transform uploaded data
324
- route.post('/process-upload', route.transformStream(
325
- (chunk) => {
326
- // Process each chunk (e.g., uppercase text)
327
- const text = new TextDecoder().decode(chunk)
328
- return text.toUpperCase()
329
- },
330
- { headers: { 'Content-Type': 'text/plain' } }
331
- ))
617
+ router.get('/users/{id}', getUserHandler)
618
+ .whereNumber('id')
619
+
620
+ router.get('/categories/{slug}', getCategoryHandler)
621
+ .whereAlpha('slug')
622
+
623
+ // Available constraints
624
+ router.whereNumber('id')
625
+ router.whereAlpha('name')
626
+ router.whereAlphaNumeric('username')
627
+ router.whereUuid('id')
628
+ router.whereIn('status', ['active', 'pending'])
332
629
  ```
333
630
 
334
- ### Advanced Streaming Features
631
+ ## RESTful Resources
632
+
633
+ ```typescript
634
+ // Creates all RESTful routes for 'posts'
635
+ router.resource('posts', 'PostsController')
636
+
637
+ // Equivalent to:
638
+ router.get('/posts', 'PostsController/index')
639
+ router.get('/posts/{id}', 'PostsController/show')
640
+ router.post('/posts', 'PostsController/store')
641
+ router.put('/posts/{id}', 'PostsController/update')
642
+ router.delete('/posts/{id}', 'PostsController/destroy')
643
+ ```
335
644
 
336
- Leverage Bun's performance optimizations:
645
+ ## Redirects
337
646
 
338
647
  ```typescript
339
- // Use direct ReadableStream for maximum performance
340
- route.streamDirect('/high-performance-stream', async ({ write, close }) => {
341
- // No queueing - data is written directly to the stream
342
- for (let i = 0; i < 10000; i++) {
343
- write(new Uint8Array([65 + (i % 26)])) // A-Z pattern
344
- }
345
- close()
346
- })
648
+ router.redirectRoute('/old-path', '/new-path')
649
+ router.permanentRedirectRoute('/very-old-path', '/new-path')
650
+ ```
347
651
 
348
- // Buffered streaming with Bun.ArrayBufferSink
349
- route.streamBuffered('/buffered-data', async ({ write, flush, end }) => {
350
- // Efficient incremental buffer building
351
- for (let batch = 0; batch < 10; batch++) {
352
- for (let i = 0; i < 100; i++) {
353
- write(`Batch ${batch}, Item ${i}\n`)
354
- }
355
- flush() // Periodically flush the buffer
356
- await new Promise(resolve => setTimeout(resolve, 100))
357
- }
358
- end()
359
- }, {
360
- highWaterMark: 512 * 1024, // 512KB buffer
361
- asUint8Array: true
362
- })
652
+ ## Domain Routing
363
653
 
364
- // Mix streaming with middleware and route groups
365
- route.group({ prefix: '/api/v1', middleware: ['Auth'] }, () => {
366
- route.streamJSON('/live-metrics', async function* () {
367
- while (true) {
368
- yield await getSystemMetrics()
369
- await new Promise(resolve => setTimeout(resolve, 1000))
370
- }
371
- })
372
-
373
- route.streamSSE('/notifications', async function* () {
374
- // Stream user-specific notifications
375
- const userId = getCurrentUserId()
376
- for await (const notification of watchNotifications(userId)) {
377
- yield { data: notification, event: 'notification' }
378
- }
654
+ ```typescript
655
+ router.domain('{account}.example.com', () => {
656
+ router.get('/', (req) => {
657
+ const account = req.params.account
658
+ return new Response(`Welcome to ${account}'s subdomain!`)
379
659
  })
380
660
  })
381
661
  ```
382
662
 
383
- ## Server Configuration
384
-
385
- The `serve` method accepts all Bun server options:
663
+ ## Configuration
386
664
 
387
665
  ```typescript
388
- route.serve({
389
- port: 3000,
390
- hostname: 'localhost',
391
- development: true,
666
+ const router = new Router({
667
+ verbose: true,
668
+ apiPrefix: '/api/v1',
669
+ defaultMiddleware: {
670
+ api: ['Middleware/Cors', 'Middleware/JsonBody'],
671
+ web: ['Middleware/Session', 'Middleware/Csrf']
672
+ }
392
673
  })
393
674
  ```
394
675
 
676
+ ## Changelog
677
+
678
+ Please see our [releases](https://github.com/stackjs/bun-router/releases) page for more information on what has changed recently.
679
+
395
680
  ## Contributing
396
681
 
397
- Contributions are welcome! Please feel free to submit a Pull Request.
682
+ Please see [CONTRIBUTING](.github/CONTRIBUTING.md) for details.
683
+
684
+ ## Community
685
+
686
+ For help, discussion about best practices, or any other conversation that would benefit from being searchable:
687
+
688
+ [Discussions on GitHub](https://github.com/stacksjs/bun-router/discussions)
689
+
690
+ For casual chit-chat with others using this package:
691
+
692
+ [Join the Stacks Discord Server](https://discord.gg/stacksjs)
693
+
694
+ ## Postcardware
695
+
696
+ "Software that is free, but hopes for a postcard." We love receiving postcards from around the world showing where Stacks is being used! We showcase them on our website too.
697
+
698
+ Our address: Stacks.js, 12665 Village Ln #2306, Playa Vista, CA 90094, United States 🌎
699
+
700
+ ## Sponsors
701
+
702
+ We would like to extend our thanks to the following sponsors for funding Stacks development. If you are interested in becoming a sponsor, please reach out to us.
703
+
704
+ - [JetBrains](https://www.jetbrains.com/)
705
+ - [The Solana Foundation](https://solana.com/)
706
+
707
+ ## License
708
+
709
+ The MIT License (MIT). Please see [LICENSE](LICENSE.md) for more information.
710
+
711
+ Made with 💙
712
+
713
+ <!-- Badges -->
714
+ [npm-version-src]: https://img.shields.io/npm/v/bun-router?style=flat-square
715
+ [npm-version-href]: https://npmjs.com/package/bun-router
716
+ [github-actions-src]: https://img.shields.io/github/actions/workflow/status/stacksjs/bun-router/ci.yml?style=flat-square&branch=main
717
+ [github-actions-href]: https://github.com/stacksjs/bun-router/actions?query=workflow%3Aci
718
+
719
+ <!-- [codecov-src]: https://img.shields.io/codecov/c/gh/stacksjs/bun-router/main?style=flat-square
720
+ [codecov-href]: https://codecov.io/gh/stacksjs/bun-router -->