bxo 0.0.5-dev.10 → 0.0.5-dev.11
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 +1 -1
- package/index.ts +47 -131
- package/package.json +1 -1
package/example.ts
CHANGED
@@ -5,7 +5,7 @@ import { cors, logger, auth, rateLimit, createJWT } from './plugins';
|
|
5
5
|
const app = new BXO();
|
6
6
|
|
7
7
|
// Enable hot reload
|
8
|
-
app.enableHotReload(['./']); // Watch current directory
|
8
|
+
app.enableHotReload([process.cwd(), './']); // Watch current directory
|
9
9
|
|
10
10
|
// Add plugins
|
11
11
|
app
|
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
|
|