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