bxo 0.0.5-dev.3 → 0.0.5
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/index.ts +10 -125
- package/package.json +1 -1
package/index.ts
CHANGED
@@ -9,7 +9,6 @@ interface RouteConfig {
|
|
9
9
|
query?: z.ZodSchema<any>;
|
10
10
|
body?: z.ZodSchema<any>;
|
11
11
|
headers?: z.ZodSchema<any>;
|
12
|
-
response?: z.ZodSchema<any>;
|
13
12
|
}
|
14
13
|
|
15
14
|
// Context type that's fully typed based on the route configuration
|
@@ -53,16 +52,13 @@ interface LifecycleHooks {
|
|
53
52
|
}
|
54
53
|
|
55
54
|
export default class BXO {
|
56
|
-
private
|
55
|
+
private routes: Route[] = [];
|
57
56
|
private plugins: BXO[] = [];
|
58
57
|
private hooks: LifecycleHooks = {};
|
59
58
|
private server?: any;
|
60
59
|
private isRunning: boolean = false;
|
61
60
|
private hotReloadEnabled: boolean = false;
|
62
61
|
private watchedFiles: Set<string> = new Set();
|
63
|
-
private watchedExclude: Set<string> = new Set();
|
64
|
-
private serverPort?: number;
|
65
|
-
private serverHostname?: string;
|
66
62
|
|
67
63
|
constructor() { }
|
68
64
|
|
@@ -133,7 +129,7 @@ export default class BXO {
|
|
133
129
|
handler: Handler<TConfig>,
|
134
130
|
config?: TConfig
|
135
131
|
): this {
|
136
|
-
this.
|
132
|
+
this.routes.push({ method: 'GET', path, handler, config });
|
137
133
|
return this;
|
138
134
|
}
|
139
135
|
|
@@ -151,7 +147,7 @@ export default class BXO {
|
|
151
147
|
handler: Handler<TConfig>,
|
152
148
|
config?: TConfig
|
153
149
|
): this {
|
154
|
-
this.
|
150
|
+
this.routes.push({ method: 'POST', path, handler, config });
|
155
151
|
return this;
|
156
152
|
}
|
157
153
|
|
@@ -169,7 +165,7 @@ export default class BXO {
|
|
169
165
|
handler: Handler<TConfig>,
|
170
166
|
config?: TConfig
|
171
167
|
): this {
|
172
|
-
this.
|
168
|
+
this.routes.push({ method: 'PUT', path, handler, config });
|
173
169
|
return this;
|
174
170
|
}
|
175
171
|
|
@@ -187,7 +183,7 @@ export default class BXO {
|
|
187
183
|
handler: Handler<TConfig>,
|
188
184
|
config?: TConfig
|
189
185
|
): this {
|
190
|
-
this.
|
186
|
+
this.routes.push({ method: 'DELETE', path, handler, config });
|
191
187
|
return this;
|
192
188
|
}
|
193
189
|
|
@@ -205,13 +201,13 @@ export default class BXO {
|
|
205
201
|
handler: Handler<TConfig>,
|
206
202
|
config?: TConfig
|
207
203
|
): this {
|
208
|
-
this.
|
204
|
+
this.routes.push({ method: 'PATCH', path, handler, config });
|
209
205
|
return this;
|
210
206
|
}
|
211
207
|
|
212
208
|
// Route matching utility
|
213
209
|
private matchRoute(method: string, pathname: string): { route: Route; params: Record<string, string> } | null {
|
214
|
-
for (const route of this.
|
210
|
+
for (const route of this.routes) {
|
215
211
|
if (route.method !== method) continue;
|
216
212
|
|
217
213
|
const routeSegments = route.path.split('/').filter(Boolean);
|
@@ -342,20 +338,6 @@ export default class BXO {
|
|
342
338
|
}
|
343
339
|
}
|
344
340
|
|
345
|
-
// Validate response against schema if provided
|
346
|
-
if (route.config?.response && !(response instanceof Response)) {
|
347
|
-
try {
|
348
|
-
response = this.validateData(route.config.response, response);
|
349
|
-
} catch (validationError) {
|
350
|
-
// Response validation failed
|
351
|
-
const errorMessage = validationError instanceof Error ? validationError.message : 'Response validation failed';
|
352
|
-
return new Response(JSON.stringify({ error: `Response validation error: ${errorMessage}` }), {
|
353
|
-
status: 500,
|
354
|
-
headers: { 'Content-Type': 'application/json' }
|
355
|
-
});
|
356
|
-
}
|
357
|
-
}
|
358
|
-
|
359
341
|
// Convert response to Response object
|
360
342
|
if (response instanceof Response) {
|
361
343
|
return response;
|
@@ -412,49 +394,12 @@ export default class BXO {
|
|
412
394
|
}
|
413
395
|
|
414
396
|
// Hot reload functionality
|
415
|
-
enableHotReload(watchPaths: string[] = ['./']
|
397
|
+
enableHotReload(watchPaths: string[] = ['./']): this {
|
416
398
|
this.hotReloadEnabled = true;
|
417
399
|
watchPaths.forEach(path => this.watchedFiles.add(path));
|
418
|
-
excludePatterns.forEach(pattern => this.watchedExclude.add(pattern));
|
419
400
|
return this;
|
420
401
|
}
|
421
402
|
|
422
|
-
private shouldExcludeFile(filename: string): boolean {
|
423
|
-
for (const pattern of this.watchedExclude) {
|
424
|
-
// Handle exact match
|
425
|
-
if (pattern === filename) {
|
426
|
-
return true;
|
427
|
-
}
|
428
|
-
|
429
|
-
// Handle directory patterns (e.g., "node_modules/", "dist/")
|
430
|
-
if (pattern.endsWith('/')) {
|
431
|
-
if (filename.startsWith(pattern) || filename.includes(`/${pattern}`)) {
|
432
|
-
return true;
|
433
|
-
}
|
434
|
-
}
|
435
|
-
|
436
|
-
// Handle wildcard patterns (e.g., "*.log", "temp*")
|
437
|
-
if (pattern.includes('*')) {
|
438
|
-
const regex = new RegExp(pattern.replace(/\*/g, '.*'));
|
439
|
-
if (regex.test(filename)) {
|
440
|
-
return true;
|
441
|
-
}
|
442
|
-
}
|
443
|
-
|
444
|
-
// Handle file extension patterns (e.g., ".log", ".tmp")
|
445
|
-
if (pattern.startsWith('.') && filename.endsWith(pattern)) {
|
446
|
-
return true;
|
447
|
-
}
|
448
|
-
|
449
|
-
// Handle substring matches for directories
|
450
|
-
if (filename.includes(pattern)) {
|
451
|
-
return true;
|
452
|
-
}
|
453
|
-
}
|
454
|
-
|
455
|
-
return false;
|
456
|
-
}
|
457
|
-
|
458
403
|
private async setupFileWatcher(port: number, hostname: string): Promise<void> {
|
459
404
|
if (!this.hotReloadEnabled) return;
|
460
405
|
|
@@ -464,19 +409,11 @@ export default class BXO {
|
|
464
409
|
try {
|
465
410
|
fs.watch(watchPath, { recursive: true }, async (eventType: string, filename: string) => {
|
466
411
|
if (filename && (filename.endsWith('.ts') || filename.endsWith('.js'))) {
|
467
|
-
// Check if file should be excluded
|
468
|
-
if (this.shouldExcludeFile(filename)) {
|
469
|
-
return;
|
470
|
-
}
|
471
|
-
|
472
412
|
console.log(`🔄 File changed: ${filename}, restarting server...`);
|
473
413
|
await this.restart(port, hostname);
|
474
414
|
}
|
475
415
|
});
|
476
416
|
console.log(`👀 Watching ${watchPath} for changes...`);
|
477
|
-
if (this.watchedExclude.size > 0) {
|
478
|
-
console.log(`🚫 Excluding patterns: ${Array.from(this.watchedExclude).join(', ')}`);
|
479
|
-
}
|
480
417
|
} catch (error) {
|
481
418
|
console.warn(`⚠️ Could not watch ${watchPath}:`, error);
|
482
419
|
}
|
@@ -503,8 +440,6 @@ export default class BXO {
|
|
503
440
|
});
|
504
441
|
|
505
442
|
this.isRunning = true;
|
506
|
-
this.serverPort = port;
|
507
|
-
this.serverHostname = hostname;
|
508
443
|
|
509
444
|
console.log(`🦊 BXO server running at http://${hostname}:${port}`);
|
510
445
|
|
@@ -549,8 +484,6 @@ export default class BXO {
|
|
549
484
|
}
|
550
485
|
|
551
486
|
this.isRunning = false;
|
552
|
-
this.serverPort = undefined;
|
553
|
-
this.serverHostname = undefined;
|
554
487
|
|
555
488
|
console.log('🛑 BXO server stopped');
|
556
489
|
|
@@ -602,61 +535,13 @@ export default class BXO {
|
|
602
535
|
return this.isRunning;
|
603
536
|
}
|
604
537
|
|
605
|
-
getServerInfo(): { running: boolean; hotReload: boolean; watchedFiles: string[]
|
538
|
+
getServerInfo(): { running: boolean; hotReload: boolean; watchedFiles: string[] } {
|
606
539
|
return {
|
607
540
|
running: this.isRunning,
|
608
541
|
hotReload: this.hotReloadEnabled,
|
609
|
-
watchedFiles: Array.from(this.watchedFiles)
|
610
|
-
excludePatterns: Array.from(this.watchedExclude)
|
542
|
+
watchedFiles: Array.from(this.watchedFiles)
|
611
543
|
};
|
612
544
|
}
|
613
|
-
|
614
|
-
// Get server information (alias for getServerInfo)
|
615
|
-
get info() {
|
616
|
-
return {
|
617
|
-
// Server status
|
618
|
-
running: this.isRunning,
|
619
|
-
server: this.server ? 'Bun' : null,
|
620
|
-
|
621
|
-
// Connection details
|
622
|
-
hostname: this.serverHostname,
|
623
|
-
port: this.serverPort,
|
624
|
-
url: this.isRunning && this.serverHostname && this.serverPort
|
625
|
-
? `http://${this.serverHostname}:${this.serverPort}`
|
626
|
-
: null,
|
627
|
-
|
628
|
-
// Application statistics
|
629
|
-
totalRoutes: this._routes.length,
|
630
|
-
totalPlugins: this.plugins.length,
|
631
|
-
|
632
|
-
// Hot reload configuration
|
633
|
-
hotReload: this.hotReloadEnabled,
|
634
|
-
watchedFiles: Array.from(this.watchedFiles),
|
635
|
-
excludePatterns: Array.from(this.watchedExclude),
|
636
|
-
|
637
|
-
// System information
|
638
|
-
runtime: 'Bun',
|
639
|
-
version: typeof Bun !== 'undefined' ? Bun.version : 'unknown',
|
640
|
-
pid: process.pid,
|
641
|
-
uptime: this.isRunning ? process.uptime() : 0
|
642
|
-
};
|
643
|
-
}
|
644
|
-
|
645
|
-
// Get all routes information
|
646
|
-
get routes() {
|
647
|
-
return this._routes.map((route: Route) => ({
|
648
|
-
method: route.method,
|
649
|
-
path: route.path,
|
650
|
-
hasConfig: !!route.config,
|
651
|
-
config: route.config ? {
|
652
|
-
hasParams: !!route.config.params,
|
653
|
-
hasQuery: !!route.config.query,
|
654
|
-
hasBody: !!route.config.body,
|
655
|
-
hasHeaders: !!route.config.headers,
|
656
|
-
hasResponse: !!route.config.response
|
657
|
-
} : null
|
658
|
-
}));
|
659
|
-
}
|
660
545
|
}
|
661
546
|
|
662
547
|
const error = (error: Error, status: number = 500) => {
|