@stacksjs/bun-router 0.0.14 → 0.0.15

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/README.md +261 -584
  2. package/bin/cli.ts +9 -0
  3. package/dist/auth.d.ts +134 -0
  4. package/dist/cache/lru-cache.d.ts +139 -0
  5. package/dist/cache/middleware-memoization.d.ts +151 -0
  6. package/dist/cache/route-cache-warmer.d.ts +161 -0
  7. package/dist/cache/sqlite-cache.d.ts +209 -0
  8. package/dist/cache/streaming-cache.d.ts +132 -0
  9. package/dist/chunk-j0e7z7hd.js +18053 -0
  10. package/dist/cli/colors.d.ts +15 -0
  11. package/dist/cli/index.d.ts +10 -0
  12. package/dist/cli/middleware.d.ts +31 -0
  13. package/dist/cli/openapi.d.ts +17 -0
  14. package/dist/cli/router.d.ts +15 -0
  15. package/dist/cli/routes.d.ts +29 -0
  16. package/dist/cli/utils.d.ts +74 -0
  17. package/dist/cli.d.ts +1 -0
  18. package/dist/cli.js +3360 -0
  19. package/dist/config.d.ts +7 -0
  20. package/dist/container/container.d.ts +273 -0
  21. package/dist/container/contextual-binding.d.ts +240 -0
  22. package/dist/container/decorators.d.ts +141 -0
  23. package/dist/container/service-provider.d.ts +285 -0
  24. package/dist/development/hot-reload.d.ts +187 -0
  25. package/dist/development/index.d.ts +167 -0
  26. package/dist/development/performance-profiler.d.ts +217 -0
  27. package/dist/development/route-debugger.d.ts +154 -0
  28. package/dist/development/route-inspector.d.ts +211 -0
  29. package/dist/development/typescript-utilities.d.ts +209 -0
  30. package/dist/docs.d.ts +11 -0
  31. package/dist/errors/circuit-breaker.d.ts +195 -0
  32. package/dist/errors/error-handler.d.ts +88 -0
  33. package/dist/errors/error-reporting.d.ts +144 -0
  34. package/dist/errors/exceptions.d.ts +184 -0
  35. package/dist/errors/graceful-degradation.d.ts +154 -0
  36. package/dist/errors/index.d.ts +9 -0
  37. package/dist/errors/router-errors.d.ts +110 -0
  38. package/dist/file-serving/static-files.d.ts +143 -0
  39. package/dist/index.d.ts +14 -0
  40. package/dist/index.js +13968 -0
  41. package/dist/middleware/auth.d.ts +63 -0
  42. package/dist/middleware/content_security_policy.d.ts +53 -0
  43. package/dist/middleware/cors.d.ts +5 -0
  44. package/dist/middleware/csrf.d.ts +7 -0
  45. package/dist/middleware/ddos_protection.d.ts +39 -0
  46. package/dist/middleware/file_security.d.ts +26 -0
  47. package/dist/middleware/file_upload.d.ts +38 -0
  48. package/dist/middleware/helmet.d.ts +54 -0
  49. package/dist/middleware/index.d.ts +44 -0
  50. package/dist/middleware/input_validation.d.ts +45 -0
  51. package/dist/middleware/json_body.d.ts +4 -0
  52. package/dist/middleware/performance_alerting.d.ts +87 -0
  53. package/dist/middleware/performance_dashboard.d.ts +87 -0
  54. package/dist/middleware/performance_monitor.d.ts +209 -0
  55. package/dist/middleware/pipeline.d.ts +131 -0
  56. package/dist/middleware/rate_limit.d.ts +36 -0
  57. package/dist/middleware/request_id.d.ts +4 -0
  58. package/dist/middleware/request_signing.d.ts +153 -0
  59. package/dist/middleware/request_tracer.d.ts +72 -0
  60. package/dist/middleware/response_cache.d.ts +97 -0
  61. package/dist/middleware/security.d.ts +69 -0
  62. package/dist/middleware/security_suite.d.ts +46 -0
  63. package/dist/middleware/session.d.ts +23 -0
  64. package/dist/model-binding/index.d.ts +2 -0
  65. package/dist/model-binding/model-middleware.d.ts +118 -0
  66. package/dist/model-binding/model-registry.d.ts +164 -0
  67. package/dist/model-binding.d.ts +185 -0
  68. package/dist/model-resolver-factory.d.ts +31 -0
  69. package/dist/observability/correlation.d.ts +187 -0
  70. package/dist/observability/health-checks.d.ts +186 -0
  71. package/dist/observability/index.d.ts +60 -0
  72. package/dist/observability/integration.d.ts +147 -0
  73. package/dist/observability/metrics.d.ts +184 -0
  74. package/dist/observability/tracing.d.ts +187 -0
  75. package/dist/optimization/bun-utilities.d.ts +224 -0
  76. package/dist/query-builder-integration.d.ts +36 -0
  77. package/dist/request/context.d.ts +34 -0
  78. package/dist/request/enhanced-request.d.ts +213 -0
  79. package/dist/request/macros.d.ts +300 -0
  80. package/dist/response/macros.d.ts +256 -0
  81. package/dist/response/response-factory.d.ts +133 -0
  82. package/dist/router/api-routes.d.ts +5 -0
  83. package/dist/router/file-based-routing.d.ts +118 -0
  84. package/dist/router/file-streaming.d.ts +5 -0
  85. package/dist/router/fluent-router.d.ts +315 -0
  86. package/dist/router/fluent-routing.d.ts +271 -0
  87. package/dist/router/group-organization.d.ts +5 -0
  88. package/dist/router/handler-resolver.d.ts +24 -0
  89. package/dist/router/http-methods.d.ts +10 -0
  90. package/dist/router/index.d.ts +114 -0
  91. package/dist/router/middleware-groups.d.ts +94 -0
  92. package/dist/router/middleware-integration.d.ts +112 -0
  93. package/dist/router/middleware.d.ts +5 -0
  94. package/dist/router/model-binding.d.ts +5 -0
  95. package/dist/router/optimized-route-matching.d.ts +6 -0
  96. package/dist/router/route-building.d.ts +5 -0
  97. package/dist/router/route-compiler.d.ts +153 -0
  98. package/dist/router/route-matching.d.ts +5 -0
  99. package/dist/router/route-trie.d.ts +114 -0
  100. package/dist/router/router.d.ts +317 -0
  101. package/dist/router/server.d.ts +5 -0
  102. package/dist/router/validation-integration.d.ts +170 -0
  103. package/dist/router/view-rendering.d.ts +5 -0
  104. package/dist/router/websocket.d.ts +5 -0
  105. package/dist/routing/route-caching.d.ts +129 -0
  106. package/dist/routing/route-throttling.d.ts +150 -0
  107. package/dist/routing/subdomain-routing.d.ts +206 -0
  108. package/dist/session/database-store.d.ts +51 -0
  109. package/dist/session/file-store.d.ts +22 -0
  110. package/dist/session/index.d.ts +106 -0
  111. package/dist/session/memory-store.d.ts +21 -0
  112. package/dist/session/redis-store.d.ts +31 -0
  113. package/dist/streaming/index.d.ts +3 -0
  114. package/dist/streaming/sse-handler.d.ts +138 -0
  115. package/dist/streaming/stream-handler.d.ts +114 -0
  116. package/dist/testing/auth-testing.d.ts +156 -0
  117. package/dist/testing/file-upload-testing.d.ts +190 -0
  118. package/dist/testing/index.d.ts +10 -0
  119. package/dist/testing/middleware-testing.d.ts +138 -0
  120. package/dist/testing/model-binding-testing.d.ts +186 -0
  121. package/dist/testing/performance-testing.d.ts +235 -0
  122. package/dist/testing/test-client.d.ts +117 -0
  123. package/dist/testing/test-request.d.ts +85 -0
  124. package/dist/testing/test-response.d.ts +90 -0
  125. package/dist/testing/types.d.ts +207 -0
  126. package/dist/testing/websocket-testing.d.ts +227 -0
  127. package/dist/types/controller-types.d.ts +208 -0
  128. package/dist/types/core.d.ts +543 -0
  129. package/dist/types/middleware-types.d.ts +227 -0
  130. package/dist/types/request-response-augmentation.d.ts +261 -0
  131. package/dist/types/route-inference.d.ts +168 -0
  132. package/dist/types.d.ts +1794 -0
  133. package/dist/url.d.ts +56 -0
  134. package/dist/utils/index.d.ts +1 -0
  135. package/dist/utils/query-preservation.d.ts +48 -0
  136. package/dist/utils.d.ts +69 -0
  137. package/dist/validation/validator.d.ts +140 -0
  138. package/dist/websocket/clustering.d.ts +185 -0
  139. package/package.json +45 -44
  140. package/src/auth.ts +469 -0
  141. package/src/cache/lru-cache.ts +457 -0
  142. package/src/cache/middleware-memoization.ts +531 -0
  143. package/src/cache/route-cache-warmer.ts +486 -0
  144. package/src/cache/sqlite-cache.ts +783 -0
  145. package/src/cache/streaming-cache.ts +572 -0
  146. package/src/cli/colors.ts +29 -0
  147. package/src/cli/index.ts +287 -0
  148. package/src/cli/middleware.ts +291 -0
  149. package/src/cli/openapi.ts +407 -0
  150. package/src/cli/router.ts +188 -0
  151. package/src/cli/routes.ts +265 -0
  152. package/src/cli/utils.ts +531 -0
  153. package/src/cli.ts +5 -0
  154. package/src/config.ts +322 -0
  155. package/src/container/container.ts +740 -0
  156. package/src/container/contextual-binding.ts +603 -0
  157. package/src/container/decorators.ts +359 -0
  158. package/src/container/service-provider.ts +596 -0
  159. package/src/development/hot-reload.ts +673 -0
  160. package/src/development/index.ts +499 -0
  161. package/src/development/performance-profiler.ts +717 -0
  162. package/src/development/route-debugger.ts +527 -0
  163. package/src/development/route-inspector.ts +749 -0
  164. package/src/development/typescript-utilities.ts +682 -0
  165. package/src/docs.ts +397 -0
  166. package/src/errors/circuit-breaker.ts +732 -0
  167. package/src/errors/error-handler.ts +569 -0
  168. package/src/errors/error-reporting.ts +672 -0
  169. package/src/errors/exceptions.ts +536 -0
  170. package/src/errors/graceful-degradation.ts +621 -0
  171. package/src/errors/index.ts +21 -0
  172. package/src/errors/router-errors.ts +632 -0
  173. package/src/file-serving/static-files.ts +581 -0
  174. package/src/index.ts +14 -0
  175. package/src/middleware/auth.ts +220 -0
  176. package/src/middleware/content_security_policy.ts +215 -0
  177. package/src/middleware/cors.ts +74 -0
  178. package/src/middleware/csrf.ts +108 -0
  179. package/src/middleware/ddos_protection.ts +255 -0
  180. package/src/middleware/file_security.ts +191 -0
  181. package/src/middleware/file_upload.ts +275 -0
  182. package/src/middleware/helmet.ts +268 -0
  183. package/src/middleware/index.ts +117 -0
  184. package/src/middleware/input_validation.ts +449 -0
  185. package/src/middleware/json_body.ts +37 -0
  186. package/src/middleware/performance_alerting.ts +538 -0
  187. package/src/middleware/performance_dashboard.ts +661 -0
  188. package/src/middleware/performance_monitor.ts +943 -0
  189. package/src/middleware/pipeline.ts +489 -0
  190. package/src/middleware/rate_limit.ts +245 -0
  191. package/src/middleware/request_id.ts +36 -0
  192. package/src/middleware/request_signing.ts +636 -0
  193. package/src/middleware/request_tracer.ts +636 -0
  194. package/src/middleware/response_cache.ts +743 -0
  195. package/src/middleware/security.ts +482 -0
  196. package/src/middleware/security_suite.ts +257 -0
  197. package/src/middleware/session.ts +91 -0
  198. package/src/model-binding/index.ts +18 -0
  199. package/src/model-binding/model-middleware.ts +425 -0
  200. package/src/model-binding/model-registry.ts +550 -0
  201. package/src/model-binding.ts +370 -0
  202. package/src/model-resolver-factory.ts +106 -0
  203. package/src/observability/correlation.ts +691 -0
  204. package/src/observability/health-checks.ts +729 -0
  205. package/src/observability/index.ts +184 -0
  206. package/src/observability/integration.ts +548 -0
  207. package/src/observability/metrics.ts +753 -0
  208. package/src/observability/tracing.ts +638 -0
  209. package/src/optimization/bun-utilities.ts +778 -0
  210. package/src/query-builder-integration.ts +137 -0
  211. package/src/request/context.ts +62 -0
  212. package/src/request/enhanced-request.ts +857 -0
  213. package/src/request/macros.ts +688 -0
  214. package/src/response/macros.ts +665 -0
  215. package/src/response/response-factory.ts +596 -0
  216. package/src/router/api-routes.ts +243 -0
  217. package/src/router/file-based-routing.ts +690 -0
  218. package/src/router/file-streaming.ts +383 -0
  219. package/src/router/fluent-router.ts +927 -0
  220. package/src/router/fluent-routing.ts +797 -0
  221. package/src/router/group-organization.ts +213 -0
  222. package/src/router/handler-resolver.ts +291 -0
  223. package/src/router/http-methods.ts +377 -0
  224. package/src/router/index.ts +187 -0
  225. package/src/router/middleware-groups.ts +222 -0
  226. package/src/router/middleware-integration.ts +399 -0
  227. package/src/router/middleware.ts +231 -0
  228. package/src/router/model-binding.ts +215 -0
  229. package/src/router/optimized-route-matching.ts +253 -0
  230. package/src/router/route-building.ts +221 -0
  231. package/src/router/route-compiler.ts +702 -0
  232. package/src/router/route-matching.ts +349 -0
  233. package/src/router/route-trie.ts +464 -0
  234. package/src/router/router.ts +1604 -0
  235. package/src/router/server.ts +462 -0
  236. package/src/router/validation-integration.ts +443 -0
  237. package/src/router/view-rendering.ts +233 -0
  238. package/src/router/websocket.ts +100 -0
  239. package/src/routing/route-caching.ts +402 -0
  240. package/src/routing/route-throttling.ts +469 -0
  241. package/src/routing/subdomain-routing.ts +492 -0
  242. package/src/session/database-store.ts +109 -0
  243. package/src/session/file-store.ts +148 -0
  244. package/src/session/index.ts +244 -0
  245. package/src/session/memory-store.ts +88 -0
  246. package/src/session/redis-store.ts +93 -0
  247. package/src/streaming/index.ts +17 -0
  248. package/src/streaming/sse-handler.ts +482 -0
  249. package/src/streaming/stream-handler.ts +552 -0
  250. package/src/testing/auth-testing.ts +446 -0
  251. package/src/testing/file-upload-testing.ts +543 -0
  252. package/src/testing/index.ts +10 -0
  253. package/src/testing/middleware-testing.ts +322 -0
  254. package/src/testing/model-binding-testing.ts +645 -0
  255. package/src/testing/performance-testing.ts +738 -0
  256. package/src/testing/test-client.ts +310 -0
  257. package/src/testing/test-request.ts +315 -0
  258. package/src/testing/test-response.ts +332 -0
  259. package/src/testing/types.ts +224 -0
  260. package/src/testing/websocket-testing.ts +590 -0
  261. package/src/types/controller-types.ts +385 -0
  262. package/src/types/core.ts +683 -0
  263. package/src/types/middleware-types.ts +419 -0
  264. package/src/types/request-response-augmentation.ts +489 -0
  265. package/src/types/route-inference.ts +357 -0
  266. package/src/types.ts +2067 -0
  267. package/src/url.ts +126 -0
  268. package/src/utils/index.ts +1 -0
  269. package/src/utils/query-preservation.ts +201 -0
  270. package/src/utils.ts +327 -0
  271. package/src/validation/validator.ts +685 -0
  272. package/src/websocket/clustering.ts +762 -0
  273. package/CHANGELOG.md +0 -770
package/README.md CHANGED
@@ -1,720 +1,397 @@
1
- <p align="center"><img src=".github/art/cover.jpg" alt="Social Card of this repo"></p>
1
+ # @bun-router
2
2
 
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.
3
+ A Laravel-inspired router for Bun applications.
12
4
 
13
5
  ## Features
14
6
 
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
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
26
13
 
27
14
  ## Installation
28
15
 
29
16
  ```bash
30
- bun add bun-router
17
+ bun add @bun-router
31
18
  ```
32
19
 
33
20
  ## Basic Usage
34
21
 
35
22
  ```typescript
36
- import { Router } from 'bun-router'
23
+ import { route } from '@bun-router'
37
24
 
38
- // Create a router
39
- const router = new Router()
40
-
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 })
46
- })
25
+ // Basic route with inline handler
26
+ route.get('/', () => new Response('Hello World!'))
47
27
 
48
- // Start the server
49
- router.serve({
50
- port: 3000,
51
- })
52
- ```
53
-
54
- ## Route Options
55
-
56
- ```typescript
57
- // Route with path parameters
58
- router.get('/users/{id}', (req) => {
28
+ // Route with parameters
29
+ route.get('/users/{id}', (req) => {
59
30
  const { id } = req.params
60
- return Response.json({ id })
31
+ return Response.json({ userId: id })
61
32
  })
62
33
 
63
- // Named routes
64
- router.get('/users/{id}', getUserHandler, 'api', 'users.show')
65
-
66
- // Generate URL for named route
67
- const url = router.route('users.show', { id: '123' })
68
- ```
69
-
70
- ## Middleware
34
+ // Route with action class
35
+ route.post('/subscribe', 'Actions/SubscribeAction')
71
36
 
72
- ```typescript
73
- import { cors, jsonBody, Router } from 'bun-router'
74
-
75
- const router = new Router()
37
+ // Route grouping
38
+ route.group({ prefix: '/api' }, () => {
39
+ route.get('/users', 'Actions/User/IndexAction')
40
+ route.post('/users', 'Actions/User/StoreAction')
41
+ })
76
42
 
77
- // Use middleware globally
78
- router.use(jsonBody())
79
- router.use(cors())
43
+ // Health check route
44
+ route.health()
80
45
 
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: [] }))
46
+ // Start the server
47
+ route.serve({
48
+ port: 3000,
87
49
  })
88
50
  ```
89
51
 
90
- ### Built-in Middleware
52
+ ## Action Classes
91
53
 
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
54
+ Action classes provide a clean way to organize your route handlers. Create a class that implements a `handle` method:
100
55
 
101
56
  ```typescript
102
- import { EnhancedRequest, MiddlewareHandler, NextFunction } from 'bun-router'
103
-
104
- class LoggerMiddleware {
105
- async handle(req: EnhancedRequest, next: NextFunction): Promise<Response> {
106
- console.log(`${req.method} ${req.url}`)
107
- return next()
108
- }
109
- }
57
+ // actions/subscribe_action.ts
58
+ import type { EnhancedRequest } from '@bun-router'
110
59
 
111
- // Use custom middleware
112
- router.use(new LoggerMiddleware())
113
- ```
114
-
115
- ## WebSocket Support
60
+ export default class SubscribeAction {
61
+ async handle(request: EnhancedRequest): Promise<Response> {
62
+ const data = await request.json()
116
63
 
117
- bun-router provides seamless integration with Bun's high-performance WebSocket capabilities:
64
+ // Handle subscription logic
118
65
 
119
- ```typescript
120
- import type { ServerWebSocket } from 'bun'
121
- import { Router } from 'bun-router'
122
-
123
- // Define data type for WebSocket clients
124
- interface ClientData {
125
- userId: string
126
- room: string
66
+ return Response.json({
67
+ success: true,
68
+ message: 'Subscribed successfully'
69
+ })
70
+ }
127
71
  }
72
+ ```
128
73
 
129
- // Create a router
130
- const router = new Router<ClientData>() // Type-safe WebSockets
131
-
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
- },
148
-
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`)
166
- }
167
-
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
- },
74
+ ## Middleware
187
75
 
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)
76
+ Middleware allows you to run code before your route handlers. You can use middleware globally or for specific routes/groups.
193
77
 
194
- // Enable per-message compression
195
- perMessageDeflate: {
196
- compress: '16KB', // Use 16KB compression level
197
- decompress: true
198
- },
78
+ ### Creating Middleware
199
79
 
200
- sendPings: true, // Send ping frames to keep connection alive (default)
201
- publishToSelf: false // Don't send published messages to publisher (default)
202
- })
80
+ Create a middleware class that implements the `handle` method:
203
81
 
204
- // Start the server
205
- router.serve({ port: 3000 })
206
- ```
207
-
208
- ### WebSocket Utility Methods
82
+ ```typescript
83
+ // middleware/auth.ts
84
+ import type { EnhancedRequest, Middleware, NextFunction } from '@bun-router'
209
85
 
210
- The router provides utility methods for working with WebSockets:
86
+ export default class AuthMiddleware implements Middleware {
87
+ async handle(req: EnhancedRequest, next: NextFunction): Promise<Response> {
88
+ const authHeader = req.headers.get('Authorization')
211
89
 
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']
90
+ if (!authHeader) {
91
+ return new Response('Unauthorized', { status: 401 })
234
92
  }
235
- })
236
93
 
237
- if (!success) {
238
- return new Response('Failed to upgrade connection', { status: 400 })
94
+ // If auth passes, continue to next middleware or route handler
95
+ return next()
239
96
  }
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
- })
97
+ }
259
98
  ```
260
99
 
261
- ### WebSocket Patterns
100
+ ### Using Middleware
262
101
 
263
- Here are some common patterns for working with WebSockets:
102
+ You can use middleware in several ways:
264
103
 
265
- #### JSON Communication
104
+ 1. Global Middleware (applies to all routes):
266
105
 
267
106
  ```typescript
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
- }
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()
291
114
  })
292
115
  ```
293
116
 
294
- #### Room-Based Chat
117
+ 2. Group Middleware (applies to all routes in a group):
295
118
 
296
119
  ```typescript
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
- }
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')
327
126
  })
328
-
329
- function broadcastToRoom(room, message) {
330
- router.publish(room, message)
331
- }
332
127
  ```
333
128
 
334
- #### Handling Backpressure
129
+ 3. Inline Middleware:
335
130
 
336
131
  ```typescript
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 _/]
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
349
139
  }
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
- }
140
+ ]
141
+ }, () => {
142
+ route.get('/users', 'Actions/User/IndexAction')
359
143
  })
360
144
  ```
361
145
 
362
- ## Bun-Native Features
363
-
364
- bun-router fully integrates with Bun's native `Bun.serve()` API, leveraging the latest Bun features for optimal performance.
365
-
366
- ### Static Responses
146
+ ## Route Groups
367
147
 
368
- Define static routes without handler functions for optimal performance:
148
+ Group related routes with a common prefix and middleware:
369
149
 
370
150
  ```typescript
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
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
+ })
377
167
  ```
378
168
 
379
- ### Method-Specific Handlers
169
+ ## Route Parameters
380
170
 
381
- The router automatically organizes multiple methods for the same path into Bun's method-specific handlers:
171
+ Access route parameters through the `params` object:
382
172
 
383
173
  ```typescript
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
- // }
174
+ route.get('/users/{id}/posts/{postId}', (req) => {
175
+ const { id, postId } = req.params
176
+ return Response.json({ userId: id, postId })
177
+ })
398
178
  ```
399
179
 
400
- ### Hot Reloading
180
+ ## TypeScript Support
401
181
 
402
- Update routes without restarting the server:
182
+ The router is written in TypeScript and provides full type definitions:
403
183
 
404
184
  ```typescript
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
- ```
414
-
415
- ### Error Handling
416
-
417
- Add a global error handler for all routes:
185
+ import type { ActionHandler, EnhancedRequest, Middleware, NextFunction } from '@bun-router'
418
186
 
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
- })
187
+ // Type-safe request handling
188
+ const handler: ActionHandler = (req: EnhancedRequest) => {
189
+ const { id } = req.params
190
+ return Response.json({ id })
191
+ }
427
192
 
428
- // Define a route that might throw an error
429
- router.get('/api/risky', () => {
430
- throw new Error('Something went wrong')
431
- })
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
+ }
432
200
 
433
- // Serve with the error handler
434
- router.serve({ port: 3000 })
201
+ route.use(loggerMiddleware.handle)
202
+ route.get('/users/{id}', handler)
435
203
  ```
436
204
 
437
- ### Type-Safe Route Parameters
205
+ ## Streaming Support
438
206
 
439
- TypeScript automatically infers parameter types from route paths:
207
+ The router provides comprehensive streaming capabilities for modern web applications:
440
208
 
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
209
+ ### File Streaming
450
210
 
451
- Built-in support for working with cookies:
211
+ Stream files with automatic content-type detection:
452
212
 
453
213
  ```typescript
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 })
460
- })
461
-
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')
214
+ // Basic file streaming
215
+ route.get('/download/{filename}', (req) => {
216
+ const { filename } = req.params
217
+ return route.streamFile(`./uploads/${filename}`)
471
218
  })
472
219
 
473
- router.get('/logout', (req) => {
474
- // Delete cookies
475
- req.cookies.delete('user_id')
476
- return new Response('Logged out')
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)
477
225
  })
478
226
  ```
479
227
 
480
- ### Authentication Helper
228
+ ### Response Streaming
481
229
 
482
- Built-in methods for handling various authentication strategies:
230
+ Create streaming routes with clean, top-level API methods:
483
231
 
484
232
  ```typescript
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')
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))
491
238
  }
492
-
493
- const { username, password } = auth.credentials()
494
- // Verify against your user database
495
-
496
- return Response.json({ message: 'Authenticated' })
497
239
  })
498
240
 
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')
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
+ }
505
248
  }
506
-
507
- const token = auth.token()
508
- // Verify token validity
509
-
510
- return Response.json({ message: 'Valid token' })
249
+ close()
511
250
  })
512
251
 
513
- // JWT Authentication
514
- router.post('/api/login', async (req) => {
515
- const { username, password } = await req.json()
516
- // Verify credentials
517
-
518
- const auth = req.auth.jwt()
519
- const token = auth.sign({ userId: 123, role: 'admin' }, {
520
- expiresIn: '1h',
521
- secret: 'your-secret-key'
522
- })
523
-
524
- return Response.json({ token })
525
- })
526
-
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')
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
+ }
532
259
  }
260
+ end()
261
+ }, { highWaterMark: 1024 * 1024 }) // 1MB buffer
262
+ ```
533
263
 
534
- const payload = auth.payload()
535
- return Response.json({ user: payload })
536
- })
264
+ ### Server-Sent Events (SSE)
537
265
 
538
- // API Key Authentication
539
- router.get('/api/data', (req) => {
540
- const auth = req.auth.apiKey('x-api-key')
266
+ Real-time data streaming to web clients:
541
267
 
542
- if (!auth.isValid()) {
543
- return auth.unauthorized('Invalid API key')
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))
544
279
  }
545
-
546
- const apiKey = auth.key()
547
- // Verify API key against database and check scopes
548
-
549
- return Response.json({ data: 'Secure data' })
550
280
  })
551
281
 
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
- })
559
-
560
- return auth.redirect()
561
- })
562
-
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')
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
288
+ }
289
+
290
+ // Stream notifications...
575
291
  })
292
+ ```
576
293
 
577
- ### File Streaming
294
+ ### JSON Streaming (NDJSON)
578
295
 
579
- Easily stream files with range support:
296
+ Stream JSON objects line by line:
580
297
 
581
298
  ```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}"`
591
- }
592
- })
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
+ }
593
305
  })
594
306
 
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)
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
313
+ }
314
+ }
599
315
  })
600
316
  ```
601
317
 
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
- ```
318
+ ### Transform Streams
613
319
 
614
- ## Parameter Constraints
320
+ Process incoming request streams using Bun's TransformStream:
615
321
 
616
322
  ```typescript
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'])
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
+ ))
629
332
  ```
630
333
 
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
- ```
334
+ ### Advanced Streaming Features
644
335
 
645
- ## Redirects
336
+ Leverage Bun's performance optimizations:
646
337
 
647
338
  ```typescript
648
- router.redirectRoute('/old-path', '/new-path')
649
- router.permanentRedirectRoute('/very-old-path', '/new-path')
650
- ```
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
+ })
651
347
 
652
- ## Domain Routing
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
+ })
653
363
 
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!`)
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
+ }
659
379
  })
660
380
  })
661
381
  ```
662
382
 
663
- ## Configuration
383
+ ## Server Configuration
384
+
385
+ The `serve` method accepts all Bun server options:
664
386
 
665
387
  ```typescript
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
- }
388
+ route.serve({
389
+ port: 3000,
390
+ hostname: 'localhost',
391
+ development: true,
673
392
  })
674
393
  ```
675
394
 
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
-
680
395
  ## Contributing
681
396
 
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 -->
397
+ Contributions are welcome! Please feel free to submit a Pull Request.