bxo 0.0.1 → 0.0.2

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 (4) hide show
  1. package/README.md +570 -6
  2. package/example.ts +70 -24
  3. package/index.ts +177 -23
  4. package/package.json +1 -1
package/README.md CHANGED
@@ -1,15 +1,579 @@
1
- # XOXO
1
+ # 🦊 BXO - A Type-Safe Web Framework for Bun
2
2
 
3
- To install dependencies:
3
+ BXO is a fast, lightweight, and fully type-safe web framework built specifically for Bun runtime. Inspired by Elysia, it provides excellent developer experience with automatic type inference, Zod validation, and a powerful plugin system.
4
+
5
+ ## ✨ Features
6
+
7
+ - 🚀 **Built for Bun** - Leverages Bun's native HTTP server for maximum performance
8
+ - 🔒 **Type-Safe** - Full TypeScript support with automatic type inference
9
+ - 📋 **Zod Validation** - Built-in request/response validation using Zod schemas
10
+ - 🔌 **Plugin System** - Extensible architecture with lifecycle hooks
11
+ - 🎣 **Lifecycle Hooks** - Complete control over server and request/response lifecycle
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
17
+
18
+ ## 🚀 Quick Start
19
+
20
+ ### Installation
21
+
22
+ ```bash
23
+ bun add zod
24
+ ```
25
+
26
+ ### Basic Usage
27
+
28
+ ```typescript
29
+ import BXO, { z } from './index';
30
+
31
+ const app = new BXO()
32
+ .get('/', async (ctx) => {
33
+ return { message: 'Hello, BXO!' };
34
+ })
35
+ .start(3000);
36
+ ```
37
+
38
+ ## 📚 Documentation
39
+
40
+ ### HTTP Handlers
41
+
42
+ BXO supports all standard HTTP methods with fluent chaining:
43
+
44
+ ```typescript
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);
79
+ ```
80
+
81
+ ### Context Object
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
+ ```
100
+
101
+ ### Validation Configuration
102
+
103
+ Each route can specify validation schemas for different parts of the request:
104
+
105
+ ```typescript
106
+ const config = {
107
+ params: z.object({ id: z.string().uuid() }),
108
+ query: z.object({
109
+ page: z.coerce.number().default(1),
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);
127
+ ```
128
+
129
+ ## 🔌 Plugin System
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
136
+
137
+ ```typescript
138
+ import { cors } from './plugins';
139
+
140
+ app.use(cors({
141
+ origin: ['http://localhost:3000', 'https://example.com'],
142
+ methods: ['GET', 'POST', 'PUT', 'DELETE'],
143
+ allowedHeaders: ['Content-Type', 'Authorization'],
144
+ credentials: true,
145
+ maxAge: 86400
146
+ }));
147
+ ```
148
+
149
+ #### Logger Plugin
150
+
151
+ ```typescript
152
+ import { logger } from './plugins';
153
+
154
+ app.use(logger({
155
+ format: 'simple', // 'simple' | 'detailed' | 'json'
156
+ includeBody: false, // Log request/response bodies
157
+ includeHeaders: false // Log headers
158
+ }));
159
+ ```
160
+
161
+ #### Authentication Plugin
162
+
163
+ ```typescript
164
+ import { auth, createJWT } from './plugins';
165
+
166
+ app.use(auth({
167
+ type: 'jwt', // 'jwt' | 'bearer' | 'apikey'
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
+ );
182
+ ```
183
+
184
+ #### Rate Limiting Plugin
185
+
186
+ ```typescript
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
+ ```
200
+
201
+ ### Creating Custom Plugins
202
+
203
+ ```typescript
204
+ const customPlugin = {
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
+ };
219
+
220
+ app.use(customPlugin);
221
+ ```
222
+
223
+ ## 🎣 Lifecycle Hooks
224
+
225
+ BXO provides comprehensive lifecycle hooks with a consistent before/after pattern for both server and request lifecycle:
226
+
227
+ ### Server Lifecycle Hooks
228
+
229
+ ```typescript
230
+ app
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
252
+
253
+ ```typescript
254
+ app
255
+ .onRequest((ctx) => {
256
+ console.log(`📨 ${ctx.request.method} ${ctx.request.url}`);
257
+ })
258
+ .onResponse((ctx, response) => {
259
+ console.log(`📤 Response sent`);
260
+ return response; // Can modify response
261
+ })
262
+ .onError((ctx, error) => {
263
+ console.error(`💥 Error:`, error.message);
264
+ return { error: 'Something went wrong' }; // Can provide custom error response
265
+ });
266
+ ```
267
+
268
+ ## 🔄 Hot Reload & Server Management
269
+
270
+ BXO includes built-in hot reload and comprehensive server management capabilities:
271
+
272
+ ### Hot Reload
273
+
274
+ ```typescript
275
+ const app = new BXO();
276
+
277
+ // Enable hot reload - server will restart when files change
278
+ app.enableHotReload(['./']); // Watch current directory
279
+
280
+ // Hot reload will automatically restart the server when:
281
+ // - Any .ts or .js file changes in watched directories
282
+ // - Server lifecycle hooks are properly executed during restart
283
+ ```
284
+
285
+ ### Server Management
286
+
287
+ ```typescript
288
+ const app = new BXO();
289
+
290
+ // Start server
291
+ await app.start(3000, 'localhost');
292
+
293
+ // Check if server is running
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');
304
+
305
+ // Stop server gracefully
306
+ await app.stop();
307
+
308
+ // Backward compatibility - listen() still works
309
+ await app.listen(3000); // Same as app.start(3000)
310
+ ```
311
+
312
+ ### Development vs Production
313
+
314
+ ```typescript
315
+ const app = new BXO();
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
+ });
328
+
329
+ app.get('/dev/status', async (ctx) => {
330
+ return {
331
+ ...app.getServerInfo(),
332
+ uptime: process.uptime(),
333
+ memory: process.memoryUsage()
334
+ };
335
+ });
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
+
419
+ .post('/users', async (ctx) => {
420
+ return { created: ctx.body };
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
+ });
449
+
450
+ // Start server with hot reload
451
+ app.start(3000);
452
+ ```
453
+
454
+ ## 🧪 Testing Endpoints
455
+
456
+ With the example server running, test these endpoints:
4
457
 
5
458
  ```bash
6
- bun install
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
521
+
522
+ ```typescript
523
+ interface RouteConfig {
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
+ }
7
529
  ```
8
530
 
9
- To run:
531
+ ### Plugin Interface
532
+
533
+ ```typescript
534
+ interface Plugin {
535
+ name?: string;
536
+ onRequest?: (ctx: Context) => Promise<void> | void;
537
+ onResponse?: (ctx: Context, response: any) => Promise<any> | any;
538
+ onError?: (ctx: Context, error: Error) => Promise<any> | any;
539
+ }
540
+ ```
541
+
542
+ ## 🛠️ Development
543
+
544
+ ### Running the Example
10
545
 
11
546
  ```bash
12
- bun run index.ts
547
+ # Run with hot reload enabled
548
+ bun run example.ts
549
+
550
+ # The server will automatically restart when you edit any .ts/.js files!
551
+ ```
552
+
553
+ ### Project Structure
554
+
555
+ ```
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
13
567
  ```
14
568
 
15
- This project was created using `bun init` in bun v1.2.18. [Bun](https://bun.sh) is a fast all-in-one JavaScript runtime.
569
+ ## 🤝 Contributing
570
+
571
+ BXO is designed to be simple and extensible. Contributions are welcome!
572
+
573
+ ## 📄 License
574
+
575
+ MIT License - feel free to use BXO in your projects!
576
+
577
+ ---
578
+
579
+ Built with ❤️ for the Bun ecosystem
package/example.ts CHANGED
@@ -4,31 +4,46 @@ import { cors, logger, auth, rateLimit, createJWT } from './plugins';
4
4
  // Create the app instance
5
5
  const app = new BXO();
6
6
 
7
+ // Enable hot reload
8
+ app.enableHotReload(['./']); // Watch current directory
9
+
7
10
  // Add plugins
8
11
  app
9
12
  .use(logger({ format: 'simple' }))
10
- .use(cors({
13
+ .use(cors({
11
14
  origin: ['http://localhost:3000', 'https://example.com'],
12
- credentials: true
15
+ credentials: true
13
16
  }))
14
- .use(rateLimit({
15
- max: 100,
17
+ .use(rateLimit({
18
+ max: 100,
16
19
  window: 60, // 1 minute
17
- exclude: ['/health']
20
+ exclude: ['/health']
18
21
  }))
19
- .use(auth({
20
- type: 'jwt',
22
+ .use(auth({
23
+ type: 'jwt',
21
24
  secret: 'your-secret-key',
22
25
  exclude: ['/', '/login', '/health']
23
26
  }));
24
27
 
25
- // Add lifecycle hooks
28
+ // Add simplified lifecycle hooks
26
29
  app
27
- .onStart(() => {
28
- console.log('🚀 Server starting up...');
30
+ .onBeforeStart(() => {
31
+ console.log('🔧 Preparing to start server...');
32
+ })
33
+ .onAfterStart(() => {
34
+ console.log('✅ Server fully started and ready!');
35
+ })
36
+ .onBeforeStop(() => {
37
+ console.log('🔧 Preparing to stop server...');
38
+ })
39
+ .onAfterStop(() => {
40
+ console.log('✅ Server fully stopped!');
41
+ })
42
+ .onBeforeRestart(() => {
43
+ console.log('🔧 Preparing to restart server...');
29
44
  })
30
- .onStop(() => {
31
- console.log('🛑 Server shutting down...');
45
+ .onAfterRestart(() => {
46
+ console.log(' Server restart completed!');
32
47
  })
33
48
  .onRequest((ctx) => {
34
49
  console.log(`📨 Processing ${ctx.request.method} ${ctx.request.url}`);
@@ -48,7 +63,7 @@ app
48
63
  .get('/simple', async (ctx) => {
49
64
  return { message: 'Hello World' };
50
65
  })
51
-
66
+
52
67
  // Three arguments: path, handler, config
53
68
  .get('/users/:id', async (ctx) => {
54
69
  // ctx.params.id is fully typed as string (UUID)
@@ -58,7 +73,7 @@ app
58
73
  params: z.object({ id: z.string().uuid() }),
59
74
  query: z.object({ include: z.string().optional() })
60
75
  })
61
-
76
+
62
77
  .post('/users', async (ctx) => {
63
78
  // ctx.body is fully typed with name: string, email: string
64
79
  return { created: ctx.body };
@@ -71,18 +86,22 @@ app
71
86
 
72
87
  // Additional examples
73
88
  .get('/health', async (ctx) => {
74
- return { status: 'ok', timestamp: new Date().toISOString() };
89
+ return {
90
+ status: 'ok',
91
+ timestamp: new Date().toISOString(),
92
+ server: app.getServerInfo()
93
+ };
75
94
  })
76
95
 
77
96
  .post('/login', async (ctx) => {
78
97
  const { username, password } = ctx.body;
79
-
98
+
80
99
  // Simple auth check (in production, verify against database)
81
100
  if (username === 'admin' && password === 'password') {
82
101
  const token = createJWT({ username, role: 'admin' }, 'your-secret-key', 3600);
83
102
  return { token, user: { username, role: 'admin' } };
84
103
  }
85
-
104
+
86
105
  ctx.set.status = 401;
87
106
  return { error: 'Invalid credentials' };
88
107
  }, {
@@ -97,9 +116,24 @@ app
97
116
  return { message: 'This is protected', user: ctx.user };
98
117
  })
99
118
 
100
- .put('/users/:id', async (ctx) => {
119
+ // Server control endpoints
120
+ .post('/restart', async (ctx) => {
121
+ // Restart the server
122
+ setTimeout(() => app.restart(3000), 100);
123
+ return { message: 'Server restart initiated' };
124
+ })
125
+
126
+ .get('/status', async (ctx) => {
101
127
  return {
102
- updated: ctx.body,
128
+ ...app.getServerInfo(),
129
+ uptime: process.uptime(),
130
+ memory: process.memoryUsage()
131
+ };
132
+ })
133
+
134
+ .put('/users/:id', async (ctx) => {
135
+ return {
136
+ updated: ctx.body,
103
137
  id: ctx.params.id,
104
138
  version: ctx.headers['if-match']
105
139
  };
@@ -121,17 +155,29 @@ app
121
155
  params: z.object({ id: z.string().uuid() })
122
156
  });
123
157
 
124
- // Start the server
125
- app.listen(3000, 'localhost');
158
+ // Start the server (with hot reload enabled)
159
+ app.start(3000, 'localhost');
126
160
 
127
161
  console.log(`
128
- 🦊 BXO Framework Example
162
+ 🦊 BXO Framework with Hot Reload
129
163
 
130
- Try these endpoints:
164
+ Features Enabled:
165
+ - 🔄 Hot reload (edit any .ts/.js file to restart)
166
+ - 🎣 Full lifecycle hooks (before/after pattern)
167
+ - 🔒 JWT authentication
168
+ - 📊 Rate limiting
169
+ - 🌐 CORS support
170
+ - 📝 Request logging
171
+
172
+ 🧪 Try these endpoints:
131
173
  - GET /simple
132
174
  - GET /users/123e4567-e89b-12d3-a456-426614174000?include=profile
133
175
  - POST /users (with JSON body: {"name": "John", "email": "john@example.com"})
134
- - GET /health
176
+ - GET /health (shows server info)
135
177
  - POST /login (with JSON body: {"username": "admin", "password": "password"})
136
178
  - GET /protected (requires Bearer token from /login)
179
+ - GET /status (server statistics)
180
+ - POST /restart (restart server programmatically)
181
+
182
+ 💡 Edit this file and save to see hot reload in action!
137
183
  `);
package/index.ts CHANGED
@@ -50,8 +50,12 @@ interface Route {
50
50
 
51
51
  // Lifecycle hooks
52
52
  interface LifecycleHooks {
53
- onStart?: () => Promise<void> | void;
54
- onStop?: () => Promise<void> | void;
53
+ onBeforeStart?: () => Promise<void> | void;
54
+ onAfterStart?: () => Promise<void> | void;
55
+ onBeforeStop?: () => Promise<void> | void;
56
+ onAfterStop?: () => Promise<void> | void;
57
+ onBeforeRestart?: () => Promise<void> | void;
58
+ onAfterRestart?: () => Promise<void> | void;
55
59
  onRequest?: (ctx: Context) => Promise<void> | void;
56
60
  onResponse?: (ctx: Context, response: any) => Promise<any> | any;
57
61
  onError?: (ctx: Context, error: Error) => Promise<any> | any;
@@ -61,17 +65,41 @@ export default class BXO {
61
65
  private routes: Route[] = [];
62
66
  private plugins: Plugin[] = [];
63
67
  private hooks: LifecycleHooks = {};
68
+ private server?: any;
69
+ private isRunning: boolean = false;
70
+ private hotReloadEnabled: boolean = false;
71
+ private watchedFiles: Set<string> = new Set();
64
72
 
65
73
  constructor() {}
66
74
 
67
75
  // Lifecycle hook methods
68
- onStart(handler: () => Promise<void> | void): this {
69
- this.hooks.onStart = handler;
76
+ onBeforeStart(handler: () => Promise<void> | void): this {
77
+ this.hooks.onBeforeStart = handler;
70
78
  return this;
71
79
  }
72
80
 
73
- onStop(handler: () => Promise<void> | void): this {
74
- this.hooks.onStop = handler;
81
+ onAfterStart(handler: () => Promise<void> | void): this {
82
+ this.hooks.onAfterStart = handler;
83
+ return this;
84
+ }
85
+
86
+ onBeforeStop(handler: () => Promise<void> | void): this {
87
+ this.hooks.onBeforeStop = handler;
88
+ return this;
89
+ }
90
+
91
+ onAfterStop(handler: () => Promise<void> | void): this {
92
+ this.hooks.onAfterStop = handler;
93
+ return this;
94
+ }
95
+
96
+ onBeforeRestart(handler: () => Promise<void> | void): this {
97
+ this.hooks.onBeforeRestart = handler;
98
+ return this;
99
+ }
100
+
101
+ onAfterRestart(handler: () => Promise<void> | void): this {
102
+ this.hooks.onAfterRestart = handler;
75
103
  return this;
76
104
  }
77
105
 
@@ -375,28 +403,154 @@ export default class BXO {
375
403
  }
376
404
  }
377
405
 
378
- // Start the server
379
- async listen(port: number = 3000, hostname: string = 'localhost'): Promise<void> {
380
- if (this.hooks.onStart) {
381
- await this.hooks.onStart();
406
+ // Hot reload functionality
407
+ enableHotReload(watchPaths: string[] = ['./']): this {
408
+ this.hotReloadEnabled = true;
409
+ watchPaths.forEach(path => this.watchedFiles.add(path));
410
+ return this;
411
+ }
412
+
413
+ private async setupFileWatcher(port: number, hostname: string): Promise<void> {
414
+ if (!this.hotReloadEnabled) return;
415
+
416
+ const fs = require('fs');
417
+
418
+ for (const watchPath of this.watchedFiles) {
419
+ try {
420
+ fs.watch(watchPath, { recursive: true }, async (eventType: string, filename: string) => {
421
+ if (filename && (filename.endsWith('.ts') || filename.endsWith('.js'))) {
422
+ console.log(`🔄 File changed: ${filename}, restarting server...`);
423
+ await this.restart(port, hostname);
424
+ }
425
+ });
426
+ console.log(`👀 Watching ${watchPath} for changes...`);
427
+ } catch (error) {
428
+ console.warn(`⚠️ Could not watch ${watchPath}:`, error);
429
+ }
430
+ }
431
+ }
432
+
433
+ // Server management methods
434
+ async start(port: number = 3000, hostname: string = 'localhost'): Promise<void> {
435
+ if (this.isRunning) {
436
+ console.log('⚠️ Server is already running');
437
+ return;
438
+ }
439
+
440
+ try {
441
+ // Before start hook
442
+ if (this.hooks.onBeforeStart) {
443
+ await this.hooks.onBeforeStart();
444
+ }
445
+
446
+ this.server = Bun.serve({
447
+ port,
448
+ hostname,
449
+ fetch: (request) => this.handleRequest(request),
450
+ });
451
+
452
+ this.isRunning = true;
453
+
454
+ console.log(`🦊 BXO server running at http://${hostname}:${port}`);
455
+
456
+ // After start hook
457
+ if (this.hooks.onAfterStart) {
458
+ await this.hooks.onAfterStart();
459
+ }
460
+
461
+ // Setup hot reload
462
+ await this.setupFileWatcher(port, hostname);
463
+
464
+ // Handle graceful shutdown
465
+ const shutdownHandler = async () => {
466
+ await this.stop();
467
+ process.exit(0);
468
+ };
469
+
470
+ process.on('SIGINT', shutdownHandler);
471
+ process.on('SIGTERM', shutdownHandler);
472
+
473
+ } catch (error) {
474
+ console.error('❌ Failed to start server:', error);
475
+ throw error;
476
+ }
477
+ }
478
+
479
+ async stop(): Promise<void> {
480
+ if (!this.isRunning) {
481
+ console.log('⚠️ Server is not running');
482
+ return;
382
483
  }
383
484
 
384
- const server = Bun.serve({
385
- port,
386
- hostname,
387
- fetch: (request) => this.handleRequest(request),
388
- });
485
+ try {
486
+ // Before stop hook
487
+ if (this.hooks.onBeforeStop) {
488
+ await this.hooks.onBeforeStop();
489
+ }
490
+
491
+ if (this.server) {
492
+ this.server.stop();
493
+ this.server = null;
494
+ }
495
+
496
+ this.isRunning = false;
497
+
498
+ console.log('🛑 BXO server stopped');
499
+
500
+ // After stop hook
501
+ if (this.hooks.onAfterStop) {
502
+ await this.hooks.onAfterStop();
503
+ }
504
+
505
+ } catch (error) {
506
+ console.error('❌ Error stopping server:', error);
507
+ throw error;
508
+ }
509
+ }
389
510
 
390
- console.log(`🦊 BXO server running at http://${hostname}:${port}`);
511
+ async restart(port: number = 3000, hostname: string = 'localhost'): Promise<void> {
512
+ try {
513
+ // Before restart hook
514
+ if (this.hooks.onBeforeRestart) {
515
+ await this.hooks.onBeforeRestart();
516
+ }
391
517
 
392
- // Handle graceful shutdown
393
- process.on('SIGINT', async () => {
394
- if (this.hooks.onStop) {
395
- await this.hooks.onStop();
518
+ console.log('🔄 Restarting BXO server...');
519
+
520
+ await this.stop();
521
+
522
+ // Small delay to ensure cleanup
523
+ await new Promise(resolve => setTimeout(resolve, 100));
524
+
525
+ await this.start(port, hostname);
526
+
527
+ // After restart hook
528
+ if (this.hooks.onAfterRestart) {
529
+ await this.hooks.onAfterRestart();
396
530
  }
397
- server.stop();
398
- process.exit(0);
399
- });
531
+
532
+ } catch (error) {
533
+ console.error('❌ Error restarting server:', error);
534
+ throw error;
535
+ }
536
+ }
537
+
538
+ // Backward compatibility
539
+ async listen(port: number = 3000, hostname: string = 'localhost'): Promise<void> {
540
+ return this.start(port, hostname);
541
+ }
542
+
543
+ // Server status
544
+ isServerRunning(): boolean {
545
+ return this.isRunning;
546
+ }
547
+
548
+ getServerInfo(): { running: boolean; hotReload: boolean; watchedFiles: string[] } {
549
+ return {
550
+ running: this.isRunning,
551
+ hotReload: this.hotReloadEnabled,
552
+ watchedFiles: Array.from(this.watchedFiles)
553
+ };
400
554
  }
401
555
  }
402
556
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bxo",
3
3
  "module": "index.ts",
4
- "version": "0.0.1",
4
+ "version": "0.0.2",
5
5
  "description": "A simple and lightweight web framework for Bun",
6
6
  "type": "module",
7
7
  "devDependencies": {