bxo 0.0.5-dev.7 โ 0.0.5-dev.71
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 +145 -499
- package/example/cors-example.ts +49 -0
- package/example/index.html +5 -0
- package/example/index.ts +191 -0
- package/example/openapi-example.ts +132 -0
- package/package.json +8 -8
- package/plugins/cors.ts +123 -73
- package/plugins/index.ts +2 -11
- package/plugins/openapi.ts +203 -0
- package/src/index.ts +644 -0
- package/tsconfig.json +3 -5
- package/.cursor/rules/use-bun-instead-of-node-vite-npm-pnpm.mdc +0 -111
- package/example.ts +0 -183
- package/index.ts +0 -833
- package/plugins/auth.ts +0 -119
- package/plugins/logger.ts +0 -109
- package/plugins/ratelimit.ts +0 -140
package/README.md
CHANGED
|
@@ -1,579 +1,225 @@
|
|
|
1
|
-
#
|
|
1
|
+
# bxo
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
A fast, lightweight web framework for Bun with built-in Zod validation and lifecycle hooks.
|
|
4
4
|
|
|
5
|
-
##
|
|
5
|
+
## Features
|
|
6
6
|
|
|
7
|
-
-
|
|
8
|
-
-
|
|
9
|
-
-
|
|
10
|
-
-
|
|
11
|
-
-
|
|
12
|
-
- ๐ **Hot Reload** - Automatic server restart on file changes during development
|
|
13
|
-
- ๐ฎ **Server Management** - Programmatic start, stop, and restart capabilities
|
|
14
|
-
- ๐ **Status Monitoring** - Built-in server status and runtime statistics
|
|
15
|
-
- ๐ฆ **Zero Dependencies** - Only depends on Zod for validation
|
|
16
|
-
- โก **Fast** - Minimal overhead with efficient routing
|
|
7
|
+
- **Type-safe routing** with Zod schema validation
|
|
8
|
+
- **Lifecycle hooks** for middleware and plugins
|
|
9
|
+
- **Plugin system** for extending functionality
|
|
10
|
+
- **Built-in CORS support** via plugin
|
|
11
|
+
- **Fast performance** leveraging Bun's native HTTP server
|
|
17
12
|
|
|
18
|
-
##
|
|
19
|
-
|
|
20
|
-
### Installation
|
|
13
|
+
## Installation
|
|
21
14
|
|
|
22
15
|
```bash
|
|
23
|
-
bun
|
|
16
|
+
bun install
|
|
24
17
|
```
|
|
25
18
|
|
|
26
|
-
|
|
19
|
+
## Quick Start
|
|
27
20
|
|
|
28
21
|
```typescript
|
|
29
|
-
import BXO
|
|
30
|
-
|
|
31
|
-
const app = new BXO()
|
|
32
|
-
.get('/', async (ctx) => {
|
|
33
|
-
return { message: 'Hello, BXO!' };
|
|
34
|
-
})
|
|
35
|
-
.start(3000);
|
|
36
|
-
```
|
|
22
|
+
import BXO from "./src/index";
|
|
23
|
+
import { cors } from "./plugins";
|
|
37
24
|
|
|
38
|
-
|
|
25
|
+
const app = new BXO();
|
|
39
26
|
|
|
40
|
-
|
|
27
|
+
// Use CORS plugin
|
|
28
|
+
app.use(cors());
|
|
41
29
|
|
|
42
|
-
|
|
30
|
+
// Define routes
|
|
31
|
+
app.get("/", (ctx) => ctx.json({ message: "Hello World!" }));
|
|
43
32
|
|
|
44
|
-
|
|
45
|
-
const app = new BXO()
|
|
46
|
-
// Simple handler
|
|
47
|
-
.get('/simple', async (ctx) => {
|
|
48
|
-
return { message: 'Hello World' };
|
|
49
|
-
})
|
|
50
|
-
|
|
51
|
-
// With validation
|
|
52
|
-
.post('/users', async (ctx) => {
|
|
53
|
-
// ctx.body is fully typed
|
|
54
|
-
return { created: ctx.body };
|
|
55
|
-
}, {
|
|
56
|
-
body: z.object({
|
|
57
|
-
name: z.string(),
|
|
58
|
-
email: z.string().email()
|
|
59
|
-
})
|
|
60
|
-
})
|
|
61
|
-
|
|
62
|
-
// Path parameters
|
|
63
|
-
.get('/users/:id', async (ctx) => {
|
|
64
|
-
// ctx.params.id is typed as UUID string
|
|
65
|
-
return { user: { id: ctx.params.id } };
|
|
66
|
-
}, {
|
|
67
|
-
params: z.object({
|
|
68
|
-
id: z.string().uuid()
|
|
69
|
-
}),
|
|
70
|
-
query: z.object({
|
|
71
|
-
include: z.string().optional()
|
|
72
|
-
})
|
|
73
|
-
})
|
|
74
|
-
|
|
75
|
-
// All HTTP methods supported
|
|
76
|
-
.put('/users/:id', handler)
|
|
77
|
-
.delete('/users/:id', handler)
|
|
78
|
-
.patch('/users/:id', handler);
|
|
33
|
+
app.start();
|
|
79
34
|
```
|
|
80
35
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
The context object (`ctx`) provides access to request data and response configuration:
|
|
84
|
-
|
|
85
|
-
```typescript
|
|
86
|
-
interface Context<TConfig> {
|
|
87
|
-
params: InferredParamsType; // Path parameters
|
|
88
|
-
query: InferredQueryType; // Query string parameters
|
|
89
|
-
body: InferredBodyType; // Request body
|
|
90
|
-
headers: InferredHeadersType; // Request headers
|
|
91
|
-
request: Request; // Original Request object
|
|
92
|
-
set: { // Response configuration
|
|
93
|
-
status?: number;
|
|
94
|
-
headers?: Record<string, string>;
|
|
95
|
-
};
|
|
96
|
-
user?: any; // Added by auth plugin
|
|
97
|
-
[key: string]: any; // Extended by plugins
|
|
98
|
-
}
|
|
99
|
-
```
|
|
36
|
+
## Lifecycle Hooks
|
|
100
37
|
|
|
101
|
-
|
|
38
|
+
BXO provides powerful lifecycle hooks that allow you to intercept and modify requests and responses at different stages:
|
|
102
39
|
|
|
103
|
-
|
|
40
|
+
### beforeRequest
|
|
41
|
+
Runs before any route processing. Can modify the request or return a response to short-circuit processing.
|
|
104
42
|
|
|
105
43
|
```typescript
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
limit: z.coerce.number().max(100).default(10)
|
|
111
|
-
}),
|
|
112
|
-
body: z.object({
|
|
113
|
-
name: z.string().min(1),
|
|
114
|
-
email: z.string().email()
|
|
115
|
-
}),
|
|
116
|
-
headers: z.object({
|
|
117
|
-
'content-type': z.literal('application/json')
|
|
118
|
-
})
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
app.post('/api/users/:id', async (ctx) => {
|
|
122
|
-
// All properties are fully typed based on schemas
|
|
123
|
-
const { id } = ctx.params; // string (UUID)
|
|
124
|
-
const { page, limit } = ctx.query; // number, number
|
|
125
|
-
const { name, email } = ctx.body; // string, string
|
|
126
|
-
}, config);
|
|
44
|
+
app.beforeRequest(async (req) => {
|
|
45
|
+
console.log(`${req.method} ${req.url}`);
|
|
46
|
+
return req; // Continue with request
|
|
47
|
+
});
|
|
127
48
|
```
|
|
128
49
|
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
BXO has a powerful plugin system with lifecycle hooks. Plugins are separate modules imported from `./plugins`.
|
|
132
|
-
|
|
133
|
-
### Available Plugins
|
|
134
|
-
|
|
135
|
-
#### CORS Plugin
|
|
50
|
+
### afterRequest
|
|
51
|
+
Runs after route processing but before the response is sent. Can modify the final response.
|
|
136
52
|
|
|
137
53
|
```typescript
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
methods: ['GET', 'POST', 'PUT', 'DELETE'],
|
|
143
|
-
allowedHeaders: ['Content-Type', 'Authorization'],
|
|
144
|
-
credentials: true,
|
|
145
|
-
maxAge: 86400
|
|
146
|
-
}));
|
|
54
|
+
app.afterRequest(async (req, res) => {
|
|
55
|
+
res.headers.set("X-Response-Time", Date.now().toString());
|
|
56
|
+
return res;
|
|
57
|
+
});
|
|
147
58
|
```
|
|
148
59
|
|
|
149
|
-
|
|
60
|
+
### beforeResponse
|
|
61
|
+
Runs after the route handler but before response headers are merged. Useful for modifying response metadata.
|
|
150
62
|
|
|
151
63
|
```typescript
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
includeBody: false, // Log request/response bodies
|
|
157
|
-
includeHeaders: false // Log headers
|
|
158
|
-
}));
|
|
64
|
+
app.beforeResponse(async (res) => {
|
|
65
|
+
res.headers.set("X-Custom-Header", "value");
|
|
66
|
+
return res;
|
|
67
|
+
});
|
|
159
68
|
```
|
|
160
69
|
|
|
161
|
-
|
|
70
|
+
### onError
|
|
71
|
+
Runs when an error occurs during request processing. Can return a custom error response.
|
|
162
72
|
|
|
163
73
|
```typescript
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
secret: 'your-secret-key',
|
|
169
|
-
exclude: ['/login', '/health'], // Skip auth for these paths
|
|
170
|
-
verify: async (token, ctx) => {
|
|
171
|
-
// Custom token verification
|
|
172
|
-
return { user: 'data' };
|
|
173
|
-
}
|
|
174
|
-
}));
|
|
175
|
-
|
|
176
|
-
// Create JWT tokens
|
|
177
|
-
const token = createJWT(
|
|
178
|
-
{ userId: 123, role: 'admin' },
|
|
179
|
-
'secret',
|
|
180
|
-
3600 // expires in 1 hour
|
|
181
|
-
);
|
|
74
|
+
app.onError(async (error, req) => {
|
|
75
|
+
console.error(`Error: ${error.message}`);
|
|
76
|
+
return new Response("Internal Server Error", { status: 500 });
|
|
77
|
+
});
|
|
182
78
|
```
|
|
183
79
|
|
|
184
|
-
|
|
80
|
+
## Plugins
|
|
185
81
|
|
|
186
|
-
|
|
187
|
-
import { rateLimit } from './plugins';
|
|
188
|
-
|
|
189
|
-
app.use(rateLimit({
|
|
190
|
-
max: 100, // Max requests
|
|
191
|
-
window: 60, // Time window in seconds
|
|
192
|
-
exclude: ['/health'], // Skip rate limiting for these paths
|
|
193
|
-
keyGenerator: (ctx) => { // Custom key generation
|
|
194
|
-
return ctx.request.headers.get('x-api-key') || 'default';
|
|
195
|
-
},
|
|
196
|
-
message: 'Too many requests',
|
|
197
|
-
statusCode: 429
|
|
198
|
-
}));
|
|
199
|
-
```
|
|
82
|
+
### CORS Plugin
|
|
200
83
|
|
|
201
|
-
|
|
84
|
+
The CORS plugin provides comprehensive Cross-Origin Resource Sharing support:
|
|
202
85
|
|
|
203
86
|
```typescript
|
|
204
|
-
|
|
205
|
-
name: 'custom',
|
|
206
|
-
onRequest: async (ctx) => {
|
|
207
|
-
console.log('Before request processing');
|
|
208
|
-
ctx.startTime = Date.now();
|
|
209
|
-
},
|
|
210
|
-
onResponse: async (ctx, response) => {
|
|
211
|
-
console.log(`Request took ${Date.now() - ctx.startTime}ms`);
|
|
212
|
-
return response;
|
|
213
|
-
},
|
|
214
|
-
onError: async (ctx, error) => {
|
|
215
|
-
console.error('Request failed:', error.message);
|
|
216
|
-
return { error: 'Custom error response' };
|
|
217
|
-
}
|
|
218
|
-
};
|
|
87
|
+
import { cors } from "./plugins";
|
|
219
88
|
|
|
220
|
-
app.use(
|
|
89
|
+
app.use(cors({
|
|
90
|
+
origin: ["http://localhost:3000", "https://myapp.com"],
|
|
91
|
+
methods: ["GET", "POST", "PUT", "DELETE"],
|
|
92
|
+
credentials: true
|
|
93
|
+
}));
|
|
221
94
|
```
|
|
222
95
|
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
BXO provides comprehensive lifecycle hooks with a consistent before/after pattern for both server and request lifecycle:
|
|
96
|
+
### OpenAPI Plugin
|
|
226
97
|
|
|
227
|
-
|
|
98
|
+
The OpenAPI plugin automatically generates OpenAPI 3.0 documentation with support for tags, security schemes, and comprehensive route metadata:
|
|
228
99
|
|
|
229
100
|
```typescript
|
|
230
|
-
|
|
231
|
-
.onBeforeStart(() => {
|
|
232
|
-
console.log('๐ง Preparing to start server...');
|
|
233
|
-
})
|
|
234
|
-
.onAfterStart(() => {
|
|
235
|
-
console.log('โ
Server fully started and ready!');
|
|
236
|
-
})
|
|
237
|
-
.onBeforeStop(() => {
|
|
238
|
-
console.log('๐ง Preparing to stop server...');
|
|
239
|
-
})
|
|
240
|
-
.onAfterStop(() => {
|
|
241
|
-
console.log('โ
Server fully stopped!');
|
|
242
|
-
})
|
|
243
|
-
.onBeforeRestart(() => {
|
|
244
|
-
console.log('๐ง Preparing to restart server...');
|
|
245
|
-
})
|
|
246
|
-
.onAfterRestart(() => {
|
|
247
|
-
console.log('โ
Server restart completed!');
|
|
248
|
-
});
|
|
249
|
-
```
|
|
250
|
-
|
|
251
|
-
### Request Lifecycle Hooks
|
|
101
|
+
import { openapi } from "./plugins";
|
|
252
102
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
.
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
103
|
+
app.use(openapi({
|
|
104
|
+
path: "/docs", // Swagger UI endpoint
|
|
105
|
+
jsonPath: "/openapi.json", // OpenAPI JSON endpoint
|
|
106
|
+
defaultTags: ["API"], // Default tags for routes
|
|
107
|
+
securitySchemes: { // Define security schemes
|
|
108
|
+
bearerAuth: {
|
|
109
|
+
type: "http",
|
|
110
|
+
scheme: "bearer",
|
|
111
|
+
bearerFormat: "JWT",
|
|
112
|
+
description: "JWT token for authentication"
|
|
113
|
+
},
|
|
114
|
+
apiKeyAuth: {
|
|
115
|
+
type: "apiKey",
|
|
116
|
+
in: "header",
|
|
117
|
+
name: "X-API-Key",
|
|
118
|
+
description: "API key for authentication"
|
|
119
|
+
}
|
|
120
|
+
},
|
|
121
|
+
globalSecurity: [ // Global security requirements
|
|
122
|
+
{ bearerAuth: [] },
|
|
123
|
+
{ apiKeyAuth: [] }
|
|
124
|
+
],
|
|
125
|
+
openapiConfig: { // Additional OpenAPI config
|
|
126
|
+
info: {
|
|
127
|
+
title: "My API",
|
|
128
|
+
version: "1.0.0",
|
|
129
|
+
description: "API description"
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}));
|
|
266
133
|
```
|
|
267
134
|
|
|
268
|
-
|
|
135
|
+
#### Route Metadata
|
|
269
136
|
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
### Hot Reload
|
|
137
|
+
Routes can include detailed metadata for better OpenAPI documentation:
|
|
273
138
|
|
|
274
139
|
```typescript
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
//
|
|
281
|
-
|
|
282
|
-
|
|
140
|
+
app.get("/users/:id", (ctx) => {
|
|
141
|
+
const id = ctx.params.id
|
|
142
|
+
return { user: { id, name: "John Doe" } }
|
|
143
|
+
}, {
|
|
144
|
+
detail: {
|
|
145
|
+
tags: ["Users"], // Route tags for grouping
|
|
146
|
+
summary: "Get user by ID", // Operation summary
|
|
147
|
+
description: "Retrieve user details", // Detailed description
|
|
148
|
+
security: [{ bearerAuth: [] }], // Route-specific security
|
|
149
|
+
params: { // Parameter documentation
|
|
150
|
+
id: z.string().describe("User ID")
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
})
|
|
283
154
|
```
|
|
284
155
|
|
|
285
|
-
|
|
156
|
+
#### Supported Metadata Fields
|
|
286
157
|
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (app.isServerRunning()) {
|
|
295
|
-
console.log('Server is running!');
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
// Get server information
|
|
299
|
-
const info = app.getServerInfo();
|
|
300
|
-
console.log(info); // { running: true, hotReload: true, watchedFiles: ['./'] }
|
|
301
|
-
|
|
302
|
-
// Restart server programmatically
|
|
303
|
-
await app.restart(3000, 'localhost');
|
|
158
|
+
- `tags`: Array of tags for grouping operations
|
|
159
|
+
- `summary`: Short description of the operation
|
|
160
|
+
- `description`: Detailed description of the operation
|
|
161
|
+
- `security`: Security requirements for the route
|
|
162
|
+
- `params`: Path parameter schemas and descriptions
|
|
163
|
+
- `query`: Query parameter schemas
|
|
164
|
+
- `hidden`: Set to `true` to exclude from OpenAPI docs
|
|
304
165
|
|
|
305
|
-
|
|
306
|
-
await app.stop();
|
|
307
|
-
|
|
308
|
-
// Backward compatibility - listen() still works
|
|
309
|
-
await app.listen(3000); // Same as app.start(3000)
|
|
310
|
-
```
|
|
166
|
+
### Creating Custom Plugins
|
|
311
167
|
|
|
312
|
-
|
|
168
|
+
Plugins are just BXO instances with lifecycle hooks:
|
|
313
169
|
|
|
314
170
|
```typescript
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
if (process.env.NODE_ENV === 'development') {
|
|
318
|
-
// Enable hot reload in development
|
|
319
|
-
app.enableHotReload(['./src', './routes']);
|
|
320
|
-
}
|
|
321
|
-
|
|
322
|
-
// Add server management endpoints for development
|
|
323
|
-
if (process.env.NODE_ENV === 'development') {
|
|
324
|
-
app.post('/dev/restart', async (ctx) => {
|
|
325
|
-
setTimeout(() => app.restart(3000), 100);
|
|
326
|
-
return { message: 'Server restart initiated' };
|
|
327
|
-
});
|
|
171
|
+
function loggingPlugin() {
|
|
172
|
+
const plugin = new BXO();
|
|
328
173
|
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
uptime: process.uptime(),
|
|
333
|
-
memory: process.memoryUsage()
|
|
334
|
-
};
|
|
174
|
+
plugin.beforeRequest(async (req) => {
|
|
175
|
+
console.log(`[${new Date().toISOString()}] ${req.method} ${req.url}`);
|
|
176
|
+
return req;
|
|
335
177
|
});
|
|
336
|
-
}
|
|
337
|
-
```
|
|
338
|
-
|
|
339
|
-
## ๐ Complete Example
|
|
340
|
-
|
|
341
|
-
```typescript
|
|
342
|
-
import BXO, { z } from './index';
|
|
343
|
-
import { cors, logger, auth, rateLimit, createJWT } from './plugins';
|
|
344
|
-
|
|
345
|
-
const app = new BXO();
|
|
346
|
-
|
|
347
|
-
// Enable hot reload for development
|
|
348
|
-
app.enableHotReload(['./']);
|
|
349
|
-
|
|
350
|
-
// Add plugins
|
|
351
|
-
app
|
|
352
|
-
.use(logger({ format: 'simple' }))
|
|
353
|
-
.use(cors({
|
|
354
|
-
origin: ['http://localhost:3000'],
|
|
355
|
-
credentials: true
|
|
356
|
-
}))
|
|
357
|
-
.use(rateLimit({
|
|
358
|
-
max: 100,
|
|
359
|
-
window: 60,
|
|
360
|
-
exclude: ['/health']
|
|
361
|
-
}))
|
|
362
|
-
.use(auth({
|
|
363
|
-
type: 'jwt',
|
|
364
|
-
secret: 'your-secret-key',
|
|
365
|
-
exclude: ['/', '/login', '/health']
|
|
366
|
-
}));
|
|
367
|
-
|
|
368
|
-
// Comprehensive lifecycle hooks
|
|
369
|
-
app
|
|
370
|
-
.onBeforeStart(() => console.log('๐ง Preparing server startup...'))
|
|
371
|
-
.onAfterStart(() => console.log('โ
Server ready!'))
|
|
372
|
-
.onBeforeRestart(() => console.log('๐ Restarting server...'))
|
|
373
|
-
.onAfterRestart(() => console.log('โ
Server restarted!'))
|
|
374
|
-
.onError((ctx, error) => ({
|
|
375
|
-
error: 'Internal server error',
|
|
376
|
-
timestamp: new Date().toISOString()
|
|
377
|
-
}));
|
|
378
|
-
|
|
379
|
-
// Routes
|
|
380
|
-
app
|
|
381
|
-
.get('/health', async () => ({
|
|
382
|
-
status: 'ok',
|
|
383
|
-
timestamp: new Date().toISOString(),
|
|
384
|
-
server: app.getServerInfo()
|
|
385
|
-
}))
|
|
386
|
-
|
|
387
|
-
.post('/login', async (ctx) => {
|
|
388
|
-
const { username, password } = ctx.body;
|
|
389
|
-
|
|
390
|
-
if (username === 'admin' && password === 'password') {
|
|
391
|
-
const token = createJWT(
|
|
392
|
-
{ username, role: 'admin' },
|
|
393
|
-
'your-secret-key'
|
|
394
|
-
);
|
|
395
|
-
return { token, user: { username, role: 'admin' } };
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
ctx.set.status = 401;
|
|
399
|
-
return { error: 'Invalid credentials' };
|
|
400
|
-
}, {
|
|
401
|
-
body: z.object({
|
|
402
|
-
username: z.string(),
|
|
403
|
-
password: z.string()
|
|
404
|
-
})
|
|
405
|
-
})
|
|
406
|
-
|
|
407
|
-
.get('/users/:id', async (ctx) => {
|
|
408
|
-
return {
|
|
409
|
-
user: {
|
|
410
|
-
id: ctx.params.id,
|
|
411
|
-
include: ctx.query.include
|
|
412
|
-
}
|
|
413
|
-
};
|
|
414
|
-
}, {
|
|
415
|
-
params: z.object({ id: z.string().uuid() }),
|
|
416
|
-
query: z.object({ include: z.string().optional() })
|
|
417
|
-
})
|
|
418
178
|
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
}, {
|
|
422
|
-
body: z.object({
|
|
423
|
-
name: z.string(),
|
|
424
|
-
email: z.string().email()
|
|
425
|
-
})
|
|
426
|
-
})
|
|
427
|
-
|
|
428
|
-
.get('/protected', async (ctx) => {
|
|
429
|
-
// ctx.user available from auth plugin
|
|
430
|
-
return {
|
|
431
|
-
message: 'Protected resource',
|
|
432
|
-
user: ctx.user
|
|
433
|
-
};
|
|
434
|
-
})
|
|
435
|
-
|
|
436
|
-
// Server management endpoints
|
|
437
|
-
.post('/restart', async (ctx) => {
|
|
438
|
-
setTimeout(() => app.restart(3000), 100);
|
|
439
|
-
return { message: 'Server restart initiated' };
|
|
440
|
-
})
|
|
441
|
-
|
|
442
|
-
.get('/status', async (ctx) => {
|
|
443
|
-
return {
|
|
444
|
-
...app.getServerInfo(),
|
|
445
|
-
uptime: process.uptime(),
|
|
446
|
-
memory: process.memoryUsage()
|
|
447
|
-
};
|
|
448
|
-
});
|
|
179
|
+
return plugin;
|
|
180
|
+
}
|
|
449
181
|
|
|
450
|
-
|
|
451
|
-
app.start(3000);
|
|
182
|
+
app.use(loggingPlugin());
|
|
452
183
|
```
|
|
453
184
|
|
|
454
|
-
##
|
|
455
|
-
|
|
456
|
-
With the example server running, test these endpoints:
|
|
185
|
+
## Route Validation
|
|
457
186
|
|
|
458
|
-
|
|
459
|
-
# Health check
|
|
460
|
-
curl http://localhost:3000/health
|
|
461
|
-
|
|
462
|
-
# Login to get token
|
|
463
|
-
curl -X POST http://localhost:3000/login \
|
|
464
|
-
-H "Content-Type: application/json" \
|
|
465
|
-
-d '{"username": "admin", "password": "password"}'
|
|
466
|
-
|
|
467
|
-
# Create user
|
|
468
|
-
curl -X POST http://localhost:3000/users \
|
|
469
|
-
-H "Content-Type: application/json" \
|
|
470
|
-
-d '{"name": "John Doe", "email": "john@example.com"}'
|
|
471
|
-
|
|
472
|
-
# Get user with validation
|
|
473
|
-
curl "http://localhost:3000/users/123e4567-e89b-12d3-a456-426614174000?include=profile"
|
|
474
|
-
|
|
475
|
-
# Access protected route (use token from login)
|
|
476
|
-
curl http://localhost:3000/protected \
|
|
477
|
-
-H "Authorization: Bearer YOUR_JWT_TOKEN"
|
|
478
|
-
|
|
479
|
-
# Check server status
|
|
480
|
-
curl http://localhost:3000/status
|
|
481
|
-
|
|
482
|
-
# Restart server programmatically
|
|
483
|
-
curl -X POST http://localhost:3000/restart
|
|
484
|
-
```
|
|
485
|
-
|
|
486
|
-
## ๐ API Reference
|
|
487
|
-
|
|
488
|
-
### BXO Class Methods
|
|
489
|
-
|
|
490
|
-
#### HTTP Methods
|
|
491
|
-
- `get(path, handler, config?)` - Handle GET requests
|
|
492
|
-
- `post(path, handler, config?)` - Handle POST requests
|
|
493
|
-
- `put(path, handler, config?)` - Handle PUT requests
|
|
494
|
-
- `delete(path, handler, config?)` - Handle DELETE requests
|
|
495
|
-
- `patch(path, handler, config?)` - Handle PATCH requests
|
|
496
|
-
|
|
497
|
-
#### Plugins & Hooks
|
|
498
|
-
- `use(plugin)` - Add a plugin
|
|
499
|
-
- `onBeforeStart(handler)` - Before server start hook
|
|
500
|
-
- `onAfterStart(handler)` - After server start hook
|
|
501
|
-
- `onBeforeStop(handler)` - Before server stop hook
|
|
502
|
-
- `onAfterStop(handler)` - After server stop hook
|
|
503
|
-
- `onBeforeRestart(handler)` - Before server restart hook
|
|
504
|
-
- `onAfterRestart(handler)` - After server restart hook
|
|
505
|
-
- `onRequest(handler)` - Global request hook
|
|
506
|
-
- `onResponse(handler)` - Global response hook
|
|
507
|
-
- `onError(handler)` - Global error hook
|
|
508
|
-
|
|
509
|
-
#### Server Management
|
|
510
|
-
- `start(port?, hostname?)` - Start the server
|
|
511
|
-
- `stop()` - Stop the server gracefully
|
|
512
|
-
- `restart(port?, hostname?)` - Restart the server
|
|
513
|
-
- `listen(port?, hostname?)` - Start the server (backward compatibility)
|
|
514
|
-
- `isServerRunning()` - Check if server is running
|
|
515
|
-
- `getServerInfo()` - Get server status information
|
|
516
|
-
|
|
517
|
-
#### Hot Reload
|
|
518
|
-
- `enableHotReload(watchPaths?)` - Enable hot reload with file watching
|
|
519
|
-
|
|
520
|
-
### Route Configuration
|
|
187
|
+
Define Zod schemas for request validation:
|
|
521
188
|
|
|
522
189
|
```typescript
|
|
523
|
-
|
|
524
|
-
params?: z.ZodSchema<any>; // Path parameter validation
|
|
525
|
-
query?: z.ZodSchema<any>; // Query string validation
|
|
526
|
-
body?: z.ZodSchema<any>; // Request body validation
|
|
527
|
-
headers?: z.ZodSchema<any>; // Header validation
|
|
528
|
-
}
|
|
529
|
-
```
|
|
190
|
+
import { z } from "bxo";
|
|
530
191
|
|
|
531
|
-
|
|
192
|
+
const UserSchema = z.object({
|
|
193
|
+
name: z.string().min(1),
|
|
194
|
+
email: z.string().email()
|
|
195
|
+
});
|
|
532
196
|
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
}
|
|
197
|
+
app.post("/users", async (ctx) => {
|
|
198
|
+
const user = ctx.body; // Already validated by UserSchema
|
|
199
|
+
return ctx.json({ id: 1, ...user });
|
|
200
|
+
}, {
|
|
201
|
+
body: UserSchema
|
|
202
|
+
});
|
|
540
203
|
```
|
|
541
204
|
|
|
542
|
-
##
|
|
543
|
-
|
|
544
|
-
### Running the Example
|
|
205
|
+
## Running
|
|
545
206
|
|
|
546
207
|
```bash
|
|
547
|
-
|
|
548
|
-
bun run example.ts
|
|
549
|
-
|
|
550
|
-
# The server will automatically restart when you edit any .ts/.js files!
|
|
208
|
+
bun run ./src/index.ts
|
|
551
209
|
```
|
|
552
210
|
|
|
553
|
-
|
|
211
|
+
Or run the CORS example:
|
|
554
212
|
|
|
213
|
+
```bash
|
|
214
|
+
bun run ./example/cors-example.ts
|
|
555
215
|
```
|
|
556
|
-
bxo/
|
|
557
|
-
โโโ index.ts # Main BXO framework
|
|
558
|
-
โโโ plugins/
|
|
559
|
-
โ โโโ index.ts # Plugin exports
|
|
560
|
-
โ โโโ cors.ts # CORS plugin
|
|
561
|
-
โ โโโ logger.ts # Logger plugin
|
|
562
|
-
โ โโโ auth.ts # Authentication plugin
|
|
563
|
-
โ โโโ ratelimit.ts # Rate limiting plugin
|
|
564
|
-
โโโ example.ts # Usage example
|
|
565
|
-
โโโ package.json
|
|
566
|
-
โโโ README.md
|
|
567
|
-
```
|
|
568
|
-
|
|
569
|
-
## ๐ค Contributing
|
|
570
|
-
|
|
571
|
-
BXO is designed to be simple and extensible. Contributions are welcome!
|
|
572
216
|
|
|
573
|
-
##
|
|
217
|
+
## Examples
|
|
574
218
|
|
|
575
|
-
|
|
219
|
+
Check out the `example/` directory for more usage examples:
|
|
576
220
|
|
|
577
|
-
|
|
221
|
+
- `cors-example.ts` - Demonstrates CORS plugin and lifecycle hooks
|
|
222
|
+
- `openapi-example.ts` - Demonstrates OpenAPI plugin with tags and security
|
|
223
|
+
- `index.ts` - Basic routing example
|
|
578
224
|
|
|
579
|
-
|
|
225
|
+
This project was created using `bun init` in bun v1.2.3. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
|