0http-bun 1.1.2 → 1.2.0
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/README.md +335 -71
- package/common.d.ts +37 -2
- package/lib/middleware/README.md +758 -0
- package/lib/middleware/body-parser.js +783 -0
- package/lib/middleware/cors.js +225 -0
- package/lib/middleware/index.d.ts +236 -0
- package/lib/middleware/index.js +45 -0
- package/lib/middleware/jwt-auth.js +406 -0
- package/lib/middleware/logger.js +310 -0
- package/lib/middleware/rate-limit.js +306 -0
- package/lib/router/sequential.js +10 -2
- package/package.json +8 -5
package/README.md
CHANGED
|
@@ -1,89 +1,353 @@
|
|
|
1
|
-
#
|
|
2
|
-
Experimental, bun-based HTTP framework inspired by [0http](https://0http.21no.de/#/)
|
|
1
|
+
# 0http-bun
|
|
3
2
|
|
|
4
|
-
|
|
5
|
-
> MacBook Pro (13-inch, 2020)
|
|
3
|
+
A high-performance, minimalist HTTP framework for [Bun](https://bun.sh/), inspired by [0http](https://0http.21no.de/#/). Built specifically to leverage Bun's native performance capabilities with a developer-friendly API.
|
|
6
4
|
|
|
7
|
-
##
|
|
8
|
-
```js
|
|
9
|
-
const http = require('0http-bun')
|
|
5
|
+
## Key Benefits
|
|
10
6
|
|
|
11
|
-
|
|
12
|
-
|
|
7
|
+
- **🚀 Bun-Native Performance**: Optimized for Bun's runtime with minimal overhead
|
|
8
|
+
- **🔧 TypeScript First**: Full TypeScript support with comprehensive type definitions
|
|
9
|
+
- **🎯 Minimalist API**: Clean, intuitive API that's easy to learn and use
|
|
10
|
+
- **🔄 Middleware Support**: Flexible middleware system with async/await support
|
|
11
|
+
- **📦 Tiny Footprint**: Lightweight framework focused on performance
|
|
12
|
+
- **🛡️ Web Standards**: Built on standard Web APIs (Request/Response)
|
|
13
|
+
|
|
14
|
+
## Installation
|
|
15
|
+
|
|
16
|
+
```bash
|
|
17
|
+
bun add 0http-bun
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Quick Start
|
|
21
|
+
|
|
22
|
+
### Basic Server
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
import http from '0http-bun'
|
|
26
|
+
|
|
27
|
+
const {router} = http()
|
|
28
|
+
|
|
29
|
+
router.get('/', () => {
|
|
30
|
+
return new Response('Hello World!')
|
|
13
31
|
})
|
|
14
|
-
router.use((req, next) => {
|
|
15
|
-
req.ctx = {
|
|
16
|
-
engine: 'bun'
|
|
17
|
-
}
|
|
18
32
|
|
|
19
|
-
|
|
33
|
+
router.get('/:id', (req) => {
|
|
34
|
+
return Response.json({id: req.params.id})
|
|
20
35
|
})
|
|
21
|
-
|
|
22
|
-
|
|
36
|
+
|
|
37
|
+
// Start the server
|
|
38
|
+
Bun.serve({
|
|
39
|
+
port: 3000,
|
|
40
|
+
fetch: router.fetch,
|
|
23
41
|
})
|
|
24
|
-
|
|
25
|
-
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
### With TypeScript Types
|
|
45
|
+
|
|
46
|
+
```typescript
|
|
47
|
+
import http, {ZeroRequest, StepFunction} from '0http-bun'
|
|
48
|
+
|
|
49
|
+
const {router} = http({
|
|
50
|
+
port: 3000,
|
|
51
|
+
errorHandler: (err: Error) => {
|
|
52
|
+
console.error('Server error:', err)
|
|
53
|
+
return new Response('Internal Server Error', {status: 500})
|
|
54
|
+
},
|
|
26
55
|
})
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
56
|
+
|
|
57
|
+
// Typed middleware
|
|
58
|
+
router.use((req: ZeroRequest, next: StepFunction) => {
|
|
59
|
+
req.ctx = {
|
|
60
|
+
startTime: Date.now(),
|
|
61
|
+
engine: 'bun',
|
|
62
|
+
}
|
|
63
|
+
return next()
|
|
64
|
+
})
|
|
65
|
+
|
|
66
|
+
// Typed route handlers
|
|
67
|
+
router.get('/:id', async (req: ZeroRequest) => {
|
|
68
|
+
return Response.json({
|
|
69
|
+
id: req.params.id,
|
|
70
|
+
context: req.ctx,
|
|
30
71
|
})
|
|
31
72
|
})
|
|
32
73
|
|
|
33
|
-
|
|
74
|
+
router.post('/users', async (req: ZeroRequest) => {
|
|
75
|
+
const body = await req.json()
|
|
76
|
+
return Response.json({created: true, data: body}, {status: 201})
|
|
77
|
+
})
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
## API Reference
|
|
81
|
+
|
|
82
|
+
### Router Configuration
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
interface IRouterConfig {
|
|
86
|
+
defaultRoute?: RequestHandler // Custom 404 handler
|
|
87
|
+
errorHandler?: (err: Error) => Response | Promise<Response> // Error handler
|
|
88
|
+
port?: number // Port number (for reference)
|
|
89
|
+
}
|
|
34
90
|
```
|
|
35
|
-
|
|
36
|
-
|
|
91
|
+
|
|
92
|
+
### Request Object
|
|
93
|
+
|
|
94
|
+
The `ZeroRequest` extends the standard `Request` with additional properties:
|
|
95
|
+
|
|
96
|
+
```typescript
|
|
97
|
+
type ZeroRequest = Request & {
|
|
98
|
+
params: Record<string, string> // URL parameters
|
|
99
|
+
query: Record<string, string> // Query string parameters
|
|
100
|
+
ctx?: Record<string, any> // Custom context (set by middleware)
|
|
101
|
+
}
|
|
102
|
+
```
|
|
103
|
+
|
|
104
|
+
### Route Methods
|
|
105
|
+
|
|
106
|
+
```typescript
|
|
107
|
+
// HTTP Methods
|
|
108
|
+
router.get(pattern, ...handlers)
|
|
109
|
+
router.post(pattern, ...handlers)
|
|
110
|
+
router.put(pattern, ...handlers)
|
|
111
|
+
router.patch(pattern, ...handlers)
|
|
112
|
+
router.delete(pattern, ...handlers)
|
|
113
|
+
router.head(pattern, ...handlers)
|
|
114
|
+
router.options(pattern, ...handlers)
|
|
115
|
+
router.connect(pattern, ...handlers)
|
|
116
|
+
router.trace(pattern, ...handlers)
|
|
117
|
+
|
|
118
|
+
// Generic method
|
|
119
|
+
router.on(method, pattern, ...handlers)
|
|
120
|
+
|
|
121
|
+
// All methods
|
|
122
|
+
router.all(pattern, ...handlers)
|
|
37
123
|
```
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
124
|
+
|
|
125
|
+
### Middleware
|
|
126
|
+
|
|
127
|
+
```typescript
|
|
128
|
+
// Global middleware
|
|
129
|
+
router.use((req, next) => {
|
|
130
|
+
// Middleware logic
|
|
131
|
+
return next()
|
|
132
|
+
})
|
|
133
|
+
|
|
134
|
+
// Path-specific middleware
|
|
135
|
+
router.use('/api/*', (req, next) => {
|
|
136
|
+
// API-specific middleware
|
|
137
|
+
return next()
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
// Multiple middlewares
|
|
141
|
+
router.use(authMiddleware, loggingMiddleware, (req, next) => next())
|
|
142
|
+
```
|
|
143
|
+
|
|
144
|
+
## Examples
|
|
145
|
+
|
|
146
|
+
### Complete REST API
|
|
147
|
+
|
|
148
|
+
```typescript
|
|
149
|
+
import http, {ZeroRequest, StepFunction} from '0http-bun'
|
|
150
|
+
|
|
151
|
+
const {router} = http({
|
|
152
|
+
errorHandler: (err: Error) => {
|
|
153
|
+
return Response.json({error: err.message}, {status: 500})
|
|
154
|
+
},
|
|
155
|
+
})
|
|
156
|
+
|
|
157
|
+
// Logging middleware
|
|
158
|
+
router.use((req: ZeroRequest, next: StepFunction) => {
|
|
159
|
+
console.log(`${req.method} ${req.url}`)
|
|
160
|
+
return next()
|
|
161
|
+
})
|
|
162
|
+
|
|
163
|
+
// JSON body parser middleware for POST/PUT
|
|
164
|
+
router.use('/api/*', async (req: ZeroRequest, next: StepFunction) => {
|
|
165
|
+
if (req.method === 'POST' || req.method === 'PUT') {
|
|
166
|
+
try {
|
|
167
|
+
req.ctx = {...req.ctx, body: await req.json()}
|
|
168
|
+
} catch (err) {
|
|
169
|
+
return Response.json({error: 'Invalid JSON'}, {status: 400})
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
return next()
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// Routes
|
|
176
|
+
router.get('/api/users', () => {
|
|
177
|
+
return Response.json([
|
|
178
|
+
{id: 1, name: 'John'},
|
|
179
|
+
{id: 2, name: 'Jane'},
|
|
180
|
+
])
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
router.get('/api/users/:id', (req: ZeroRequest) => {
|
|
184
|
+
const {id} = req.params
|
|
185
|
+
return Response.json({id: Number(id), name: 'User'})
|
|
186
|
+
})
|
|
187
|
+
|
|
188
|
+
router.post('/api/users', (req: ZeroRequest) => {
|
|
189
|
+
const userData = req.ctx?.body
|
|
190
|
+
return Response.json({id: Date.now(), ...userData}, {status: 201})
|
|
191
|
+
})
|
|
192
|
+
|
|
193
|
+
router.delete('/api/users/:id', (req: ZeroRequest) => {
|
|
194
|
+
const {id} = req.params
|
|
195
|
+
return Response.json({deleted: id})
|
|
196
|
+
})
|
|
197
|
+
|
|
198
|
+
// Start server
|
|
199
|
+
Bun.serve({
|
|
200
|
+
port: 3000,
|
|
201
|
+
fetch: router.fetch,
|
|
202
|
+
})
|
|
52
203
|
```
|
|
53
|
-
|
|
204
|
+
|
|
205
|
+
## Middleware Support
|
|
206
|
+
|
|
207
|
+
0http-bun includes a comprehensive middleware system with built-in middlewares for common use cases:
|
|
208
|
+
|
|
209
|
+
- **[Body Parser](./lib/middleware/README.md#body-parser)** - Automatic request body parsing (JSON, form data, text)
|
|
210
|
+
- **[CORS](./lib/middleware/README.md#cors)** - Cross-Origin Resource Sharing with flexible configuration
|
|
211
|
+
- **[JWT Authentication](./lib/middleware/README.md#jwt-authentication)** - JSON Web Token authentication and authorization
|
|
212
|
+
- **[Logger](./lib/middleware/README.md#logger)** - Request logging with multiple output formats
|
|
213
|
+
- **[Rate Limiting](./lib/middleware/README.md#rate-limiting)** - Flexible rate limiting with sliding window support
|
|
214
|
+
|
|
215
|
+
### Quick Example
|
|
216
|
+
|
|
217
|
+
```javascript
|
|
218
|
+
// Import middleware functions from the middleware module
|
|
219
|
+
const {
|
|
220
|
+
createCORS,
|
|
221
|
+
createLogger,
|
|
222
|
+
createBodyParser,
|
|
223
|
+
createJWTAuth,
|
|
224
|
+
createRateLimit,
|
|
225
|
+
} = require('0http-bun/lib/middleware')
|
|
226
|
+
|
|
227
|
+
const {router} = http()
|
|
228
|
+
|
|
229
|
+
// Apply middleware stack
|
|
230
|
+
router.use(createCORS()) // Enable CORS
|
|
231
|
+
router.use(createLogger()) // Request logging
|
|
232
|
+
router.use(createBodyParser()) // Parse request bodies
|
|
233
|
+
router.use(createRateLimit({max: 100})) // Rate limiting
|
|
234
|
+
|
|
235
|
+
// Protected routes
|
|
236
|
+
router.use('/api/*', createJWTAuth({secret: process.env.JWT_SECRET}))
|
|
54
237
|
```
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
238
|
+
|
|
239
|
+
📖 **[Complete Middleware Documentation](./lib/middleware/README.md)**
|
|
240
|
+
|
|
241
|
+
### Error Handling
|
|
242
|
+
|
|
243
|
+
```typescript
|
|
244
|
+
import http, {ZeroRequest} from '0http-bun'
|
|
245
|
+
|
|
246
|
+
const {router} = http({
|
|
247
|
+
errorHandler: (err: Error) => {
|
|
248
|
+
console.error('Application error:', err)
|
|
249
|
+
|
|
250
|
+
// Custom error responses based on error type
|
|
251
|
+
if (err.name === 'ValidationError') {
|
|
252
|
+
return Response.json(
|
|
253
|
+
{error: 'Validation failed', details: err.message},
|
|
254
|
+
{status: 400},
|
|
255
|
+
)
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
return Response.json({error: 'Internal server error'}, {status: 500})
|
|
259
|
+
},
|
|
260
|
+
defaultRoute: () => {
|
|
261
|
+
return Response.json({error: 'Route not found'}, {status: 404})
|
|
262
|
+
},
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
// Route that might throw an error
|
|
266
|
+
router.get('/api/risky', (req: ZeroRequest) => {
|
|
267
|
+
if (Math.random() > 0.5) {
|
|
268
|
+
const error = new Error('Random failure')
|
|
269
|
+
error.name = 'ValidationError'
|
|
270
|
+
throw error
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
return Response.json({success: true})
|
|
274
|
+
})
|
|
69
275
|
```
|
|
70
|
-
|
|
276
|
+
|
|
277
|
+
## Performance
|
|
278
|
+
|
|
279
|
+
0http-bun is designed for high performance with Bun's native capabilities:
|
|
280
|
+
|
|
281
|
+
- **Minimal overhead**: Direct use of Web APIs
|
|
282
|
+
- **Efficient routing**: Based on the proven `trouter` library
|
|
283
|
+
- **Fast parameter parsing**: Optimized URL parameter extraction with caching
|
|
284
|
+
- **Query string parsing**: Uses `fast-querystring` for optimal performance
|
|
285
|
+
- **Memory efficient**: Route caching and object reuse to minimize allocations
|
|
286
|
+
|
|
287
|
+
### Benchmark Results
|
|
288
|
+
|
|
289
|
+
Run benchmarks with:
|
|
290
|
+
|
|
291
|
+
```bash
|
|
292
|
+
bun run bench
|
|
71
293
|
```
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
294
|
+
|
|
295
|
+
_Performance characteristics will vary based on your specific use case and middleware stack._
|
|
296
|
+
|
|
297
|
+
## TypeScript Support
|
|
298
|
+
|
|
299
|
+
Full TypeScript support is included with comprehensive type definitions:
|
|
300
|
+
|
|
301
|
+
```typescript
|
|
302
|
+
// Main framework types
|
|
303
|
+
import {
|
|
304
|
+
ZeroRequest,
|
|
305
|
+
StepFunction,
|
|
306
|
+
RequestHandler,
|
|
307
|
+
IRouter,
|
|
308
|
+
IRouterConfig,
|
|
309
|
+
} from '0http-bun'
|
|
310
|
+
|
|
311
|
+
// Middleware-specific types
|
|
312
|
+
import {
|
|
313
|
+
LoggerOptions,
|
|
314
|
+
JWTAuthOptions,
|
|
315
|
+
APIKeyAuthOptions,
|
|
316
|
+
RateLimitOptions,
|
|
317
|
+
CORSOptions,
|
|
318
|
+
BodyParserOptions,
|
|
319
|
+
MemoryStore,
|
|
320
|
+
} from '0http-bun/lib/middleware'
|
|
321
|
+
|
|
322
|
+
// Example typed middleware
|
|
323
|
+
const customMiddleware: RequestHandler = (
|
|
324
|
+
req: ZeroRequest,
|
|
325
|
+
next: StepFunction,
|
|
326
|
+
) => {
|
|
327
|
+
req.ctx = req.ctx || {}
|
|
328
|
+
req.ctx.timestamp = Date.now()
|
|
329
|
+
return next()
|
|
330
|
+
}
|
|
331
|
+
|
|
332
|
+
// Example typed route handler
|
|
333
|
+
const typedHandler = (req: ZeroRequest): Response => {
|
|
334
|
+
return Response.json({
|
|
335
|
+
params: req.params,
|
|
336
|
+
query: req.query,
|
|
337
|
+
context: req.ctx,
|
|
338
|
+
})
|
|
339
|
+
}
|
|
86
340
|
```
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
341
|
+
|
|
342
|
+
## License
|
|
343
|
+
|
|
344
|
+
MIT
|
|
345
|
+
|
|
346
|
+
## Contributing
|
|
347
|
+
|
|
348
|
+
Contributions are welcome! Please feel free to submit a Pull Request.
|
|
349
|
+
|
|
350
|
+
## Related Projects
|
|
351
|
+
|
|
352
|
+
- [0http](https://0http.21no.de/#/) - The original inspiration
|
|
353
|
+
- [Bun](https://bun.sh/) - The JavaScript runtime this framework is built for
|
package/common.d.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import {Pattern, Methods} from 'trouter'
|
|
2
|
+
import {Logger} from 'pino'
|
|
2
3
|
|
|
3
4
|
export interface IRouterConfig {
|
|
4
5
|
defaultRoute?: RequestHandler
|
|
@@ -8,10 +9,44 @@ export interface IRouterConfig {
|
|
|
8
9
|
|
|
9
10
|
export type StepFunction = (error?: unknown) => Response | Promise<Response>
|
|
10
11
|
|
|
11
|
-
|
|
12
|
+
export interface ParsedFile {
|
|
13
|
+
name: string
|
|
14
|
+
size: number
|
|
15
|
+
type: string
|
|
16
|
+
data: File
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export type ZeroRequest = Request & {
|
|
12
20
|
params: Record<string, string>
|
|
13
21
|
query: Record<string, string>
|
|
14
|
-
|
|
22
|
+
// Legacy compatibility properties (mirrored from ctx)
|
|
23
|
+
user?: any
|
|
24
|
+
jwt?: {
|
|
25
|
+
payload: any
|
|
26
|
+
header: any
|
|
27
|
+
token: string
|
|
28
|
+
}
|
|
29
|
+
apiKey?: string
|
|
30
|
+
// Context object for middleware data
|
|
31
|
+
ctx?: {
|
|
32
|
+
log?: Logger
|
|
33
|
+
user?: any
|
|
34
|
+
jwt?: {
|
|
35
|
+
payload: any
|
|
36
|
+
header: any
|
|
37
|
+
token: string
|
|
38
|
+
}
|
|
39
|
+
apiKey?: string
|
|
40
|
+
rateLimit?: {
|
|
41
|
+
limit: number
|
|
42
|
+
used: number
|
|
43
|
+
remaining: number
|
|
44
|
+
resetTime: Date
|
|
45
|
+
}
|
|
46
|
+
body?: any
|
|
47
|
+
files?: Record<string, ParsedFile | ParsedFile[]>
|
|
48
|
+
[key: string]: any
|
|
49
|
+
}
|
|
15
50
|
}
|
|
16
51
|
|
|
17
52
|
export type RequestHandler = (
|