@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.
- package/CHANGELOG.md +770 -0
- package/README.md +584 -261
- package/package.json +43 -48
- package/bin/cli.ts +0 -9
- package/dist/auth.d.ts +0 -134
- package/dist/cache/lru-cache.d.ts +0 -139
- package/dist/cache/middleware-memoization.d.ts +0 -151
- package/dist/cache/route-cache-warmer.d.ts +0 -161
- package/dist/cache/sqlite-cache.d.ts +0 -209
- package/dist/cache/streaming-cache.d.ts +0 -132
- package/dist/chunk-1mvtv6jw.js +0 -13012
- package/dist/cli/colors.d.ts +0 -15
- package/dist/cli/index.d.ts +0 -10
- package/dist/cli/middleware.d.ts +0 -31
- package/dist/cli/openapi.d.ts +0 -17
- package/dist/cli/router.d.ts +0 -15
- package/dist/cli/routes.d.ts +0 -29
- package/dist/cli/utils.d.ts +0 -74
- package/dist/cli.d.ts +0 -1
- package/dist/cli.js +0 -3195
- package/dist/config.d.ts +0 -7
- package/dist/container/container.d.ts +0 -273
- package/dist/container/contextual-binding.d.ts +0 -240
- package/dist/container/decorators.d.ts +0 -141
- package/dist/container/service-provider.d.ts +0 -285
- package/dist/development/hot-reload.d.ts +0 -187
- package/dist/development/index.d.ts +0 -167
- package/dist/development/performance-profiler.d.ts +0 -217
- package/dist/development/route-debugger.d.ts +0 -154
- package/dist/development/route-inspector.d.ts +0 -211
- package/dist/development/typescript-utilities.d.ts +0 -209
- package/dist/docs.d.ts +0 -11
- package/dist/errors/circuit-breaker.d.ts +0 -195
- package/dist/errors/error-handler.d.ts +0 -88
- package/dist/errors/error-reporting.d.ts +0 -144
- package/dist/errors/exceptions.d.ts +0 -184
- package/dist/errors/graceful-degradation.d.ts +0 -154
- package/dist/errors/index.d.ts +0 -9
- package/dist/errors/router-errors.d.ts +0 -110
- package/dist/file-serving/static-files.d.ts +0 -143
- package/dist/index.d.ts +0 -14
- package/dist/index.js +0 -13933
- package/dist/middleware/auth.d.ts +0 -63
- package/dist/middleware/content_security_policy.d.ts +0 -53
- package/dist/middleware/cors.d.ts +0 -5
- package/dist/middleware/csrf.d.ts +0 -7
- package/dist/middleware/ddos_protection.d.ts +0 -39
- package/dist/middleware/file_security.d.ts +0 -26
- package/dist/middleware/file_upload.d.ts +0 -38
- package/dist/middleware/helmet.d.ts +0 -54
- package/dist/middleware/index.d.ts +0 -44
- package/dist/middleware/input_validation.d.ts +0 -45
- package/dist/middleware/json_body.d.ts +0 -4
- package/dist/middleware/performance_alerting.d.ts +0 -87
- package/dist/middleware/performance_dashboard.d.ts +0 -87
- package/dist/middleware/performance_monitor.d.ts +0 -209
- package/dist/middleware/pipeline.d.ts +0 -131
- package/dist/middleware/rate_limit.d.ts +0 -36
- package/dist/middleware/request_id.d.ts +0 -4
- package/dist/middleware/request_signing.d.ts +0 -153
- package/dist/middleware/request_tracer.d.ts +0 -72
- package/dist/middleware/response_cache.d.ts +0 -97
- package/dist/middleware/security.d.ts +0 -69
- package/dist/middleware/security_suite.d.ts +0 -46
- package/dist/middleware/session.d.ts +0 -23
- package/dist/model-binding/index.d.ts +0 -2
- package/dist/model-binding/model-middleware.d.ts +0 -118
- package/dist/model-binding/model-registry.d.ts +0 -164
- package/dist/model-binding.d.ts +0 -185
- package/dist/model-resolver-factory.d.ts +0 -31
- package/dist/observability/correlation.d.ts +0 -187
- package/dist/observability/health-checks.d.ts +0 -186
- package/dist/observability/index.d.ts +0 -60
- package/dist/observability/integration.d.ts +0 -147
- package/dist/observability/metrics.d.ts +0 -184
- package/dist/observability/tracing.d.ts +0 -187
- package/dist/optimization/bun-utilities.d.ts +0 -224
- package/dist/query-builder-integration.d.ts +0 -36
- package/dist/request/context.d.ts +0 -34
- package/dist/request/enhanced-request.d.ts +0 -213
- package/dist/request/macros.d.ts +0 -300
- package/dist/response/macros.d.ts +0 -245
- package/dist/response/response-factory.d.ts +0 -133
- package/dist/router/api-routes.d.ts +0 -5
- package/dist/router/file-based-routing.d.ts +0 -118
- package/dist/router/file-streaming.d.ts +0 -5
- package/dist/router/fluent-router.d.ts +0 -315
- package/dist/router/fluent-routing.d.ts +0 -271
- package/dist/router/group-organization.d.ts +0 -5
- package/dist/router/handler-resolver.d.ts +0 -24
- package/dist/router/http-methods.d.ts +0 -10
- package/dist/router/index.d.ts +0 -114
- package/dist/router/middleware-groups.d.ts +0 -94
- package/dist/router/middleware-integration.d.ts +0 -112
- package/dist/router/middleware.d.ts +0 -5
- package/dist/router/model-binding.d.ts +0 -5
- package/dist/router/optimized-route-matching.d.ts +0 -6
- package/dist/router/route-building.d.ts +0 -5
- package/dist/router/route-compiler.d.ts +0 -153
- package/dist/router/route-matching.d.ts +0 -5
- package/dist/router/route-trie.d.ts +0 -114
- package/dist/router/router.d.ts +0 -294
- package/dist/router/server.d.ts +0 -5
- package/dist/router/validation-integration.d.ts +0 -170
- package/dist/router/view-rendering.d.ts +0 -5
- package/dist/router/websocket.d.ts +0 -5
- package/dist/routing/route-caching.d.ts +0 -129
- package/dist/routing/route-throttling.d.ts +0 -150
- package/dist/routing/subdomain-routing.d.ts +0 -206
- package/dist/session/database-store.d.ts +0 -51
- package/dist/session/file-store.d.ts +0 -22
- package/dist/session/index.d.ts +0 -106
- package/dist/session/memory-store.d.ts +0 -21
- package/dist/session/redis-store.d.ts +0 -31
- package/dist/streaming/index.d.ts +0 -3
- package/dist/streaming/sse-handler.d.ts +0 -138
- package/dist/streaming/stream-handler.d.ts +0 -114
- package/dist/testing/auth-testing.d.ts +0 -156
- package/dist/testing/file-upload-testing.d.ts +0 -190
- package/dist/testing/index.d.ts +0 -10
- package/dist/testing/middleware-testing.d.ts +0 -138
- package/dist/testing/model-binding-testing.d.ts +0 -186
- package/dist/testing/performance-testing.d.ts +0 -235
- package/dist/testing/test-client.d.ts +0 -117
- package/dist/testing/test-request.d.ts +0 -85
- package/dist/testing/test-response.d.ts +0 -90
- package/dist/testing/types.d.ts +0 -207
- package/dist/testing/websocket-testing.d.ts +0 -227
- package/dist/types/controller-types.d.ts +0 -208
- package/dist/types/core.d.ts +0 -543
- package/dist/types/middleware-types.d.ts +0 -227
- package/dist/types/request-response-augmentation.d.ts +0 -261
- package/dist/types/route-inference.d.ts +0 -168
- package/dist/types.d.ts +0 -1782
- package/dist/url.d.ts +0 -56
- package/dist/utils/index.d.ts +0 -1
- package/dist/utils/query-preservation.d.ts +0 -48
- package/dist/utils.d.ts +0 -69
- package/dist/validation/validator.d.ts +0 -140
- package/dist/websocket/clustering.d.ts +0 -185
- package/src/auth.ts +0 -469
- package/src/cache/lru-cache.ts +0 -457
- package/src/cache/middleware-memoization.ts +0 -531
- package/src/cache/route-cache-warmer.ts +0 -486
- package/src/cache/sqlite-cache.ts +0 -783
- package/src/cache/streaming-cache.ts +0 -572
- package/src/cli/colors.ts +0 -29
- package/src/cli/index.ts +0 -287
- package/src/cli/middleware.ts +0 -291
- package/src/cli/openapi.ts +0 -407
- package/src/cli/router.ts +0 -188
- package/src/cli/routes.ts +0 -265
- package/src/cli/utils.ts +0 -531
- package/src/cli.ts +0 -5
- package/src/config.ts +0 -322
- package/src/container/container.ts +0 -740
- package/src/container/contextual-binding.ts +0 -603
- package/src/container/decorators.ts +0 -359
- package/src/container/service-provider.ts +0 -596
- package/src/development/hot-reload.ts +0 -673
- package/src/development/index.ts +0 -499
- package/src/development/performance-profiler.ts +0 -717
- package/src/development/route-debugger.ts +0 -527
- package/src/development/route-inspector.ts +0 -749
- package/src/development/typescript-utilities.ts +0 -682
- package/src/docs.ts +0 -397
- package/src/errors/circuit-breaker.ts +0 -732
- package/src/errors/error-handler.ts +0 -569
- package/src/errors/error-reporting.ts +0 -672
- package/src/errors/exceptions.ts +0 -536
- package/src/errors/graceful-degradation.ts +0 -621
- package/src/errors/index.ts +0 -21
- package/src/errors/router-errors.ts +0 -632
- package/src/file-serving/static-files.ts +0 -581
- package/src/index.ts +0 -14
- package/src/middleware/auth.ts +0 -219
- package/src/middleware/content_security_policy.ts +0 -215
- package/src/middleware/cors.ts +0 -74
- package/src/middleware/csrf.ts +0 -108
- package/src/middleware/ddos_protection.ts +0 -255
- package/src/middleware/file_security.ts +0 -191
- package/src/middleware/file_upload.ts +0 -264
- package/src/middleware/helmet.ts +0 -268
- package/src/middleware/index.ts +0 -117
- package/src/middleware/input_validation.ts +0 -449
- package/src/middleware/json_body.ts +0 -37
- package/src/middleware/performance_alerting.ts +0 -538
- package/src/middleware/performance_dashboard.ts +0 -661
- package/src/middleware/performance_monitor.ts +0 -943
- package/src/middleware/pipeline.ts +0 -489
- package/src/middleware/rate_limit.ts +0 -245
- package/src/middleware/request_id.ts +0 -36
- package/src/middleware/request_signing.ts +0 -636
- package/src/middleware/request_tracer.ts +0 -636
- package/src/middleware/response_cache.ts +0 -743
- package/src/middleware/security.ts +0 -482
- package/src/middleware/security_suite.ts +0 -257
- package/src/middleware/session.ts +0 -91
- package/src/model-binding/index.ts +0 -18
- package/src/model-binding/model-middleware.ts +0 -425
- package/src/model-binding/model-registry.ts +0 -550
- package/src/model-binding.ts +0 -370
- package/src/model-resolver-factory.ts +0 -106
- package/src/observability/correlation.ts +0 -691
- package/src/observability/health-checks.ts +0 -729
- package/src/observability/index.ts +0 -184
- package/src/observability/integration.ts +0 -548
- package/src/observability/metrics.ts +0 -753
- package/src/observability/tracing.ts +0 -638
- package/src/optimization/bun-utilities.ts +0 -778
- package/src/query-builder-integration.ts +0 -137
- package/src/request/context.ts +0 -62
- package/src/request/enhanced-request.ts +0 -857
- package/src/request/macros.ts +0 -688
- package/src/response/macros.ts +0 -612
- package/src/response/response-factory.ts +0 -596
- package/src/router/api-routes.ts +0 -243
- package/src/router/file-based-routing.ts +0 -670
- package/src/router/file-streaming.ts +0 -372
- package/src/router/fluent-router.ts +0 -927
- package/src/router/fluent-routing.ts +0 -797
- package/src/router/group-organization.ts +0 -213
- package/src/router/handler-resolver.ts +0 -291
- package/src/router/http-methods.ts +0 -377
- package/src/router/index.ts +0 -187
- package/src/router/middleware-groups.ts +0 -222
- package/src/router/middleware-integration.ts +0 -399
- package/src/router/middleware.ts +0 -231
- package/src/router/model-binding.ts +0 -215
- package/src/router/optimized-route-matching.ts +0 -253
- package/src/router/route-building.ts +0 -212
- package/src/router/route-compiler.ts +0 -702
- package/src/router/route-matching.ts +0 -349
- package/src/router/route-trie.ts +0 -464
- package/src/router/router.ts +0 -1581
- package/src/router/server.ts +0 -462
- package/src/router/validation-integration.ts +0 -443
- package/src/router/view-rendering.ts +0 -233
- package/src/router/websocket.ts +0 -100
- package/src/routing/route-caching.ts +0 -402
- package/src/routing/route-throttling.ts +0 -469
- package/src/routing/subdomain-routing.ts +0 -492
- package/src/session/database-store.ts +0 -109
- package/src/session/file-store.ts +0 -148
- package/src/session/index.ts +0 -244
- package/src/session/memory-store.ts +0 -88
- package/src/session/redis-store.ts +0 -93
- package/src/streaming/index.ts +0 -17
- package/src/streaming/sse-handler.ts +0 -482
- package/src/streaming/stream-handler.ts +0 -552
- package/src/testing/auth-testing.ts +0 -446
- package/src/testing/file-upload-testing.ts +0 -543
- package/src/testing/index.ts +0 -10
- package/src/testing/middleware-testing.ts +0 -322
- package/src/testing/model-binding-testing.ts +0 -645
- package/src/testing/performance-testing.ts +0 -738
- package/src/testing/test-client.ts +0 -310
- package/src/testing/test-request.ts +0 -315
- package/src/testing/test-response.ts +0 -332
- package/src/testing/types.ts +0 -224
- package/src/testing/websocket-testing.ts +0 -590
- package/src/types/controller-types.ts +0 -385
- package/src/types/core.ts +0 -683
- package/src/types/middleware-types.ts +0 -419
- package/src/types/request-response-augmentation.ts +0 -489
- package/src/types/route-inference.ts +0 -357
- package/src/types.ts +0 -2055
- package/src/url.ts +0 -126
- package/src/utils/index.ts +0 -1
- package/src/utils/query-preservation.ts +0 -201
- package/src/utils.ts +0 -327
- package/src/validation/validator.ts +0 -685
- package/src/websocket/clustering.ts +0 -762
package/README.md
CHANGED
|
@@ -1,397 +1,720 @@
|
|
|
1
|
-
|
|
1
|
+
<p align="center"><img src=".github/art/cover.jpg" alt="Social Card of this repo"></p>
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
[![npm version][npm-version-src]][npm-version-href]
|
|
4
|
+
[![GitHub Actions][github-actions-src]][github-actions-href]
|
|
5
|
+
[](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
|
-
-
|
|
8
|
-
- Support for
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
-
|
|
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
|
|
30
|
+
bun add bun-router
|
|
18
31
|
```
|
|
19
32
|
|
|
20
33
|
## Basic Usage
|
|
21
34
|
|
|
22
35
|
```typescript
|
|
23
|
-
import {
|
|
36
|
+
import { Router } from 'bun-router'
|
|
24
37
|
|
|
25
|
-
//
|
|
26
|
-
|
|
38
|
+
// Create a router
|
|
39
|
+
const router = new Router()
|
|
27
40
|
|
|
28
|
-
//
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
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
|
-
|
|
49
|
+
router.serve({
|
|
48
50
|
port: 3000,
|
|
49
51
|
})
|
|
50
52
|
```
|
|
51
53
|
|
|
52
|
-
##
|
|
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
|
-
|
|
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
|
-
|
|
58
|
-
|
|
73
|
+
import { cors, jsonBody, Router } from 'bun-router'
|
|
74
|
+
|
|
75
|
+
const router = new Router()
|
|
59
76
|
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
77
|
+
// Use middleware globally
|
|
78
|
+
router.use(jsonBody())
|
|
79
|
+
router.use(cors())
|
|
63
80
|
|
|
64
|
-
|
|
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
|
-
|
|
67
|
-
|
|
68
|
-
|
|
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
|
-
##
|
|
115
|
+
## WebSocket Support
|
|
75
116
|
|
|
76
|
-
|
|
117
|
+
bun-router provides seamless integration with Bun's high-performance WebSocket capabilities:
|
|
77
118
|
|
|
78
|
-
|
|
119
|
+
```typescript
|
|
120
|
+
import type { ServerWebSocket } from 'bun'
|
|
121
|
+
import { Router } from 'bun-router'
|
|
79
122
|
|
|
80
|
-
|
|
123
|
+
// Define data type for WebSocket clients
|
|
124
|
+
interface ClientData {
|
|
125
|
+
userId: string
|
|
126
|
+
room: string
|
|
127
|
+
}
|
|
81
128
|
|
|
82
|
-
|
|
83
|
-
//
|
|
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
|
-
|
|
87
|
-
|
|
88
|
-
|
|
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
|
-
|
|
91
|
-
|
|
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
|
-
//
|
|
95
|
-
|
|
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
|
-
###
|
|
261
|
+
### WebSocket Patterns
|
|
101
262
|
|
|
102
|
-
|
|
263
|
+
Here are some common patterns for working with WebSockets:
|
|
103
264
|
|
|
104
|
-
|
|
265
|
+
#### JSON Communication
|
|
105
266
|
|
|
106
267
|
```typescript
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
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
|
-
|
|
294
|
+
#### Room-Based Chat
|
|
118
295
|
|
|
119
296
|
```typescript
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
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
|
-
|
|
334
|
+
#### Handling Backpressure
|
|
130
335
|
|
|
131
336
|
```typescript
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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
|
-
|
|
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
|
-
##
|
|
362
|
+
## Bun-Native Features
|
|
147
363
|
|
|
148
|
-
|
|
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
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
}
|
|
155
|
-
|
|
156
|
-
|
|
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
|
-
|
|
379
|
+
### Method-Specific Handlers
|
|
170
380
|
|
|
171
|
-
|
|
381
|
+
The router automatically organizes multiple methods for the same path into Bun's method-specific handlers:
|
|
172
382
|
|
|
173
383
|
```typescript
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
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
|
-
|
|
400
|
+
### Hot Reloading
|
|
181
401
|
|
|
182
|
-
|
|
402
|
+
Update routes without restarting the server:
|
|
183
403
|
|
|
184
404
|
```typescript
|
|
185
|
-
|
|
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
|
-
|
|
188
|
-
const handler: ActionHandler = (req: EnhancedRequest) => {
|
|
189
|
-
const { id } = req.params
|
|
190
|
-
return Response.json({ id })
|
|
191
|
-
}
|
|
415
|
+
### Error Handling
|
|
192
416
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
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
|
|
202
|
-
|
|
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
|
-
|
|
437
|
+
### Type-Safe Route Parameters
|
|
206
438
|
|
|
207
|
-
|
|
439
|
+
TypeScript automatically infers parameter types from route paths:
|
|
208
440
|
|
|
209
|
-
|
|
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
|
-
|
|
451
|
+
Built-in support for working with cookies:
|
|
212
452
|
|
|
213
453
|
```typescript
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
const
|
|
217
|
-
|
|
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
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
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
|
-
###
|
|
480
|
+
### Authentication Helper
|
|
229
481
|
|
|
230
|
-
|
|
482
|
+
Built-in methods for handling various authentication strategies:
|
|
231
483
|
|
|
232
484
|
```typescript
|
|
233
|
-
//
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
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
|
-
//
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
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
|
-
|
|
506
|
+
|
|
507
|
+
const token = auth.token()
|
|
508
|
+
// Verify token validity
|
|
509
|
+
|
|
510
|
+
return Response.json({ message: 'Valid token' })
|
|
250
511
|
})
|
|
251
512
|
|
|
252
|
-
//
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
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
|
-
|
|
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
|
-
|
|
524
|
+
return Response.json({ token })
|
|
525
|
+
})
|
|
267
526
|
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
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
|
-
//
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
560
|
+
return auth.redirect()
|
|
561
|
+
})
|
|
297
562
|
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
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
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
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
|
-
|
|
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
|
-
|
|
614
|
+
## Parameter Constraints
|
|
321
615
|
|
|
322
616
|
```typescript
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
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
|
-
|
|
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
|
-
|
|
645
|
+
## Redirects
|
|
337
646
|
|
|
338
647
|
```typescript
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
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
|
-
|
|
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
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
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
|
-
##
|
|
384
|
-
|
|
385
|
-
The `serve` method accepts all Bun server options:
|
|
663
|
+
## Configuration
|
|
386
664
|
|
|
387
665
|
```typescript
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
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
|
-
|
|
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 -->
|