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.
- package/example.ts +43 -22
- package/index.ts +47 -131
- 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
|
-
//
|
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
|
-
|
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
|
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
|
821
|
-
totalWsRoutes
|
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
|
-
|
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
|
-
|
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
|
|