bxo 0.0.5-dev.10 โ†’ 0.0.5-dev.12

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 (3) hide show
  1. package/example.ts +43 -22
  2. package/index.ts +47 -131
  3. package/package.json +1 -1
package/example.ts CHANGED
@@ -1,13 +1,39 @@
1
1
  import BXO, { z } from './index';
2
2
  import { cors, logger, auth, rateLimit, createJWT } from './plugins';
3
3
 
4
+ // Create a simple API plugin that defines its own routes
5
+ function createApiPlugin(): BXO {
6
+ const apiPlugin = new BXO();
7
+
8
+ apiPlugin
9
+ .get('/api/info', async (ctx) => {
10
+ return {
11
+ name: 'BXO API Plugin',
12
+ version: '1.0.0',
13
+ endpoints: ['/api/info', '/api/ping', '/api/time']
14
+ };
15
+ })
16
+ .get('/api/ping', async (ctx) => {
17
+ return { ping: 'pong', timestamp: Date.now() };
18
+ })
19
+ .get('/api/time', async (ctx) => {
20
+ return { time: new Date().toISOString() };
21
+ })
22
+ .post('/api/echo', async (ctx) => {
23
+ return { echo: ctx.body };
24
+ }, {
25
+ body: z.object({
26
+ message: z.string()
27
+ })
28
+ });
29
+
30
+ return apiPlugin;
31
+ }
32
+
4
33
  // Create the app instance
5
34
  const app = new BXO();
6
35
 
7
- // Enable hot reload
8
- app.enableHotReload(['./']); // Watch current directory
9
-
10
- // Add plugins
36
+ // Add plugins (including our new API plugin)
11
37
  app
12
38
  .use(logger({ format: 'simple' }))
13
39
  .use(cors({
@@ -22,8 +48,9 @@ app
22
48
  .use(auth({
23
49
  type: 'jwt',
24
50
  secret: 'your-secret-key',
25
- exclude: ['/', '/login', '/health']
26
- }));
51
+ exclude: ['/', '/login', '/health', '/api/*']
52
+ }))
53
+ .use(createApiPlugin()); // Add our plugin with actual routes
27
54
 
28
55
  // Add simplified lifecycle hooks
29
56
  app
@@ -39,12 +66,6 @@ app
39
66
  .onAfterStop(() => {
40
67
  console.log('โœ… Server fully stopped!');
41
68
  })
42
- .onBeforeRestart(() => {
43
- console.log('๐Ÿ”ง Preparing to restart server...');
44
- })
45
- .onAfterRestart(() => {
46
- console.log('โœ… Server restart completed!');
47
- })
48
69
  .onRequest((ctx) => {
49
70
  console.log(`๐Ÿ“จ Processing ${ctx.request.method} ${ctx.request.url}`);
50
71
  })
@@ -116,13 +137,6 @@ app
116
137
  return { message: 'This is protected', user: ctx.user };
117
138
  })
118
139
 
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
140
  .get('/status', async (ctx) => {
127
141
  return {
128
142
  ...app.getServerInfo(),
@@ -162,12 +176,12 @@ console.log(`
162
176
  ๐ŸฆŠ BXO Framework with Hot Reload
163
177
 
164
178
  โœจ Features Enabled:
165
- - ๐Ÿ”„ Hot reload (edit any .ts/.js file to restart)
166
179
  - ๐ŸŽฃ Full lifecycle hooks (before/after pattern)
167
180
  - ๐Ÿ”’ JWT authentication
168
181
  - ๐Ÿ“Š Rate limiting
169
182
  - ๐ŸŒ CORS support
170
183
  - ๐Ÿ“ Request logging
184
+ - ๐Ÿ”Œ API Plugin with routes
171
185
 
172
186
  ๐Ÿงช Try these endpoints:
173
187
  - GET /simple
@@ -177,7 +191,14 @@ console.log(`
177
191
  - POST /login (with JSON body: {"username": "admin", "password": "password"})
178
192
  - GET /protected (requires Bearer token from /login)
179
193
  - GET /status (server statistics)
180
- - POST /restart (restart server programmatically)
194
+
195
+ ๐Ÿ”Œ API Plugin endpoints:
196
+ - GET /api/info (plugin information)
197
+ - GET /api/ping (ping pong)
198
+ - GET /api/time (current time)
199
+ - POST /api/echo (echo message: {"message": "hello"})
181
200
 
182
201
  ๐Ÿ’ก Edit this file and save to see hot reload in action!
183
- `);
202
+ `);
203
+
204
+ console.log(app.routes)
package/index.ts CHANGED
@@ -73,8 +73,6 @@ interface LifecycleHooks {
73
73
  onAfterStart?: (instance: BXO) => Promise<void> | void;
74
74
  onBeforeStop?: (instance: BXO) => Promise<void> | void;
75
75
  onAfterStop?: (instance: BXO) => Promise<void> | void;
76
- onBeforeRestart?: (instance: BXO) => Promise<void> | void;
77
- onAfterRestart?: (instance: BXO) => Promise<void> | void;
78
76
  onRequest?: (ctx: Context, instance: BXO) => Promise<void> | void;
79
77
  onResponse?: (ctx: Context, response: any, instance: BXO) => Promise<any> | any;
80
78
  onError?: (ctx: Context, error: Error, instance: BXO) => Promise<any> | any;
@@ -87,9 +85,6 @@ export default class BXO {
87
85
  private hooks: LifecycleHooks = {};
88
86
  private server?: any;
89
87
  private isRunning: boolean = false;
90
- private hotReloadEnabled: boolean = false;
91
- private watchedFiles: Set<string> = new Set();
92
- private watchedExclude: Set<string> = new Set();
93
88
  private serverPort?: number;
94
89
  private serverHostname?: string;
95
90
 
@@ -116,15 +111,7 @@ export default class BXO {
116
111
  return this;
117
112
  }
118
113
 
119
- onBeforeRestart(handler: (instance: BXO) => Promise<void> | void): this {
120
- this.hooks.onBeforeRestart = handler;
121
- return this;
122
- }
123
114
 
124
- onAfterRestart(handler: (instance: BXO) => Promise<void> | void): this {
125
- this.hooks.onAfterRestart = handler;
126
- return this;
127
- }
128
115
 
129
116
  onRequest(handler: (ctx: Context, instance: BXO) => Promise<void> | void): this {
130
117
  this.hooks.onRequest = handler;
@@ -582,77 +569,7 @@ export default class BXO {
582
569
  }
583
570
  }
584
571
 
585
- // Hot reload functionality
586
- enableHotReload(watchPaths: string[] = ['./'], excludePatterns: string[] = []): this {
587
- this.hotReloadEnabled = true;
588
- watchPaths.forEach(path => this.watchedFiles.add(path));
589
- excludePatterns.forEach(pattern => this.watchedExclude.add(pattern));
590
- return this;
591
- }
592
-
593
- private shouldExcludeFile(filename: string): boolean {
594
- for (const pattern of this.watchedExclude) {
595
- // Handle exact match
596
- if (pattern === filename) {
597
- return true;
598
- }
599
572
 
600
- // Handle directory patterns (e.g., "node_modules/", "dist/")
601
- if (pattern.endsWith('/')) {
602
- if (filename.startsWith(pattern) || filename.includes(`/${pattern}`)) {
603
- return true;
604
- }
605
- }
606
-
607
- // Handle wildcard patterns (e.g., "*.log", "temp*")
608
- if (pattern.includes('*')) {
609
- const regex = new RegExp(pattern.replace(/\*/g, '.*'));
610
- if (regex.test(filename)) {
611
- return true;
612
- }
613
- }
614
-
615
- // Handle file extension patterns (e.g., ".log", ".tmp")
616
- if (pattern.startsWith('.') && filename.endsWith(pattern)) {
617
- return true;
618
- }
619
-
620
- // Handle substring matches for directories
621
- if (filename.includes(pattern)) {
622
- return true;
623
- }
624
- }
625
-
626
- return false;
627
- }
628
-
629
- private async setupFileWatcher(port: number, hostname: string): Promise<void> {
630
- if (!this.hotReloadEnabled) return;
631
-
632
- const fs = require('fs');
633
-
634
- for (const watchPath of this.watchedFiles) {
635
- try {
636
- fs.watch(watchPath, { recursive: true }, async (eventType: string, filename: string) => {
637
- if (filename && (filename.endsWith('.ts') || filename.endsWith('.js'))) {
638
- // Check if file should be excluded
639
- if (this.shouldExcludeFile(filename)) {
640
- return;
641
- }
642
-
643
- console.log(`๐Ÿ”„ File changed: ${filename}, restarting server...`);
644
- await this.restart(port, hostname);
645
- }
646
- });
647
- console.log(`๐Ÿ‘€ Watching ${watchPath} for changes...`);
648
- if (this.watchedExclude.size > 0) {
649
- console.log(`๐Ÿšซ Excluding patterns: ${Array.from(this.watchedExclude).join(', ')}`);
650
- }
651
- } catch (error) {
652
- console.warn(`โš ๏ธ Could not watch ${watchPath}:`, error);
653
- }
654
- }
655
- }
656
573
 
657
574
  // Server management methods
658
575
  async start(port: number = 3000, hostname: string = 'localhost'): Promise<void> {
@@ -697,16 +614,11 @@ export default class BXO {
697
614
  this.serverPort = port;
698
615
  this.serverHostname = hostname;
699
616
 
700
- console.log(`๐ŸฆŠ BXO server running at http://${hostname}:${port}`);
701
-
702
617
  // After start hook
703
618
  if (this.hooks.onAfterStart) {
704
619
  await this.hooks.onAfterStart(this);
705
620
  }
706
621
 
707
- // Setup hot reload
708
- await this.setupFileWatcher(port, hostname);
709
-
710
622
  // Handle graceful shutdown
711
623
  const shutdownHandler = async () => {
712
624
  await this.stop();
@@ -743,8 +655,6 @@ export default class BXO {
743
655
  this.serverPort = undefined;
744
656
  this.serverHostname = undefined;
745
657
 
746
- console.log('๐Ÿ›‘ BXO server stopped');
747
-
748
658
  // After stop hook
749
659
  if (this.hooks.onAfterStop) {
750
660
  await this.hooks.onAfterStop(this);
@@ -756,32 +666,7 @@ export default class BXO {
756
666
  }
757
667
  }
758
668
 
759
- async restart(port: number = 3000, hostname: string = 'localhost'): Promise<void> {
760
- try {
761
- // Before restart hook
762
- if (this.hooks.onBeforeRestart) {
763
- await this.hooks.onBeforeRestart(this);
764
- }
765
-
766
- console.log('๐Ÿ”„ Restarting BXO server...');
767
-
768
- await this.stop();
769
-
770
- // Small delay to ensure cleanup
771
- await new Promise(resolve => setTimeout(resolve, 100));
772
-
773
- await this.start(port, hostname);
774
669
 
775
- // After restart hook
776
- if (this.hooks.onAfterRestart) {
777
- await this.hooks.onAfterRestart(this);
778
- }
779
-
780
- } catch (error) {
781
- console.error('โŒ Error restarting server:', error);
782
- throw error;
783
- }
784
- }
785
670
 
786
671
  // Backward compatibility
787
672
  async listen(port: number = 3000, hostname: string = 'localhost'): Promise<void> {
@@ -793,17 +678,18 @@ export default class BXO {
793
678
  return this.isRunning;
794
679
  }
795
680
 
796
- getServerInfo(): { running: boolean; hotReload: boolean; watchedFiles: string[]; excludePatterns: string[] } {
681
+ getServerInfo(): { running: boolean } {
797
682
  return {
798
- running: this.isRunning,
799
- hotReload: this.hotReloadEnabled,
800
- watchedFiles: Array.from(this.watchedFiles),
801
- excludePatterns: Array.from(this.watchedExclude)
683
+ running: this.isRunning
802
684
  };
803
685
  }
804
686
 
805
687
  // Get server information (alias for getServerInfo)
806
688
  get info() {
689
+ // Calculate total routes including plugins
690
+ const totalRoutes = this._routes.length + this.plugins.reduce((total, plugin) => total + plugin._routes.length, 0);
691
+ const totalWsRoutes = this._wsRoutes.length + this.plugins.reduce((total, plugin) => total + plugin._wsRoutes.length, 0);
692
+
807
693
  return {
808
694
  // Server status
809
695
  running: this.isRunning,
@@ -817,15 +703,10 @@ export default class BXO {
817
703
  : null,
818
704
 
819
705
  // Application statistics
820
- totalRoutes: this._routes.length,
821
- totalWsRoutes: this._wsRoutes.length,
706
+ totalRoutes,
707
+ totalWsRoutes,
822
708
  totalPlugins: this.plugins.length,
823
709
 
824
- // Hot reload configuration
825
- hotReload: this.hotReloadEnabled,
826
- watchedFiles: Array.from(this.watchedFiles),
827
- excludePatterns: Array.from(this.watchedExclude),
828
-
829
710
  // System information
830
711
  runtime: 'Bun',
831
712
  version: typeof Bun !== 'undefined' ? Bun.version : 'unknown',
@@ -836,25 +717,60 @@ export default class BXO {
836
717
 
837
718
  // Get all routes information
838
719
  get routes() {
839
- return this._routes.map((route: Route) => ({
720
+ // Get routes from main instance
721
+ const mainRoutes = this._routes.map((route: Route) => ({
840
722
  method: route.method,
841
723
  path: route.path,
842
724
  hasConfig: !!route.config,
843
- config: route.config || null
725
+ config: route.config || null,
726
+ source: 'main' as const
844
727
  }));
728
+
729
+ // Get routes from all plugins
730
+ const pluginRoutes = this.plugins.flatMap((plugin, pluginIndex) =>
731
+ plugin._routes.map((route: Route) => ({
732
+ method: route.method,
733
+ path: route.path,
734
+ hasConfig: !!route.config,
735
+ config: route.config || null,
736
+ source: 'plugin' as const,
737
+ pluginIndex
738
+ }))
739
+ );
740
+
741
+ return [...mainRoutes, ...pluginRoutes];
845
742
  }
846
743
 
847
744
  // Get all WebSocket routes information
848
745
  get wsRoutes() {
849
- return this._wsRoutes.map((route: WSRoute) => ({
746
+ // Get WebSocket routes from main instance
747
+ const mainWsRoutes = this._wsRoutes.map((route: WSRoute) => ({
850
748
  path: route.path,
851
749
  hasHandlers: {
852
750
  onOpen: !!route.handler.onOpen,
853
751
  onMessage: !!route.handler.onMessage,
854
752
  onClose: !!route.handler.onClose,
855
753
  onError: !!route.handler.onError
856
- }
754
+ },
755
+ source: 'main' as const
857
756
  }));
757
+
758
+ // Get WebSocket routes from all plugins
759
+ const pluginWsRoutes = this.plugins.flatMap((plugin, pluginIndex) =>
760
+ plugin._wsRoutes.map((route: WSRoute) => ({
761
+ path: route.path,
762
+ hasHandlers: {
763
+ onOpen: !!route.handler.onOpen,
764
+ onMessage: !!route.handler.onMessage,
765
+ onClose: !!route.handler.onClose,
766
+ onError: !!route.handler.onError
767
+ },
768
+ source: 'plugin' as const,
769
+ pluginIndex
770
+ }))
771
+ );
772
+
773
+ return [...mainWsRoutes, ...pluginWsRoutes];
858
774
  }
859
775
  }
860
776
 
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "bxo",
3
3
  "module": "index.ts",
4
- "version": "0.0.5-dev.10",
4
+ "version": "0.0.5-dev.12",
5
5
  "description": "A simple and lightweight web framework for Bun",
6
6
  "type": "module",
7
7
  "devDependencies": {