bxo 0.0.5-dev.4 → 0.0.5-dev.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.
Files changed (2) hide show
  1. package/index.ts +114 -3
  2. package/package.json +1 -1
package/index.ts CHANGED
@@ -52,6 +52,20 @@ interface Route {
52
52
  config?: RouteConfig;
53
53
  }
54
54
 
55
+ // WebSocket handler interface
56
+ interface WebSocketHandler {
57
+ onOpen?: (ws: any) => void;
58
+ onMessage?: (ws: any, message: string | Buffer) => void;
59
+ onClose?: (ws: any, code?: number, reason?: string) => void;
60
+ onError?: (ws: any, error: Error) => void;
61
+ }
62
+
63
+ // WebSocket route definition
64
+ interface WSRoute {
65
+ path: string;
66
+ handler: WebSocketHandler;
67
+ }
68
+
55
69
  // Lifecycle hooks
56
70
  interface LifecycleHooks {
57
71
  onBeforeStart?: () => Promise<void> | void;
@@ -67,6 +81,7 @@ interface LifecycleHooks {
67
81
 
68
82
  export default class BXO {
69
83
  private _routes: Route[] = [];
84
+ private _wsRoutes: WSRoute[] = [];
70
85
  private plugins: BXO[] = [];
71
86
  private hooks: LifecycleHooks = {};
72
87
  private server?: any;
@@ -222,6 +237,12 @@ export default class BXO {
222
237
  return this;
223
238
  }
224
239
 
240
+ // WebSocket route handler
241
+ ws(path: string, handler: WebSocketHandler): this {
242
+ this._wsRoutes.push({ path, handler });
243
+ return this;
244
+ }
245
+
225
246
  // Route matching utility
226
247
  private matchRoute(method: string, pathname: string): { route: Route; params: Record<string, string> } | null {
227
248
  for (const route of this._routes) {
@@ -261,6 +282,43 @@ export default class BXO {
261
282
  return null;
262
283
  }
263
284
 
285
+ // WebSocket route matching utility
286
+ private matchWSRoute(pathname: string): { route: WSRoute; params: Record<string, string> } | null {
287
+ for (const route of this._wsRoutes) {
288
+ const routeSegments = route.path.split('/').filter(Boolean);
289
+ const pathSegments = pathname.split('/').filter(Boolean);
290
+
291
+ if (routeSegments.length !== pathSegments.length) continue;
292
+
293
+ const params: Record<string, string> = {};
294
+ let isMatch = true;
295
+
296
+ for (let i = 0; i < routeSegments.length; i++) {
297
+ const routeSegment = routeSegments[i];
298
+ const pathSegment = pathSegments[i];
299
+
300
+ if (!routeSegment || !pathSegment) {
301
+ isMatch = false;
302
+ break;
303
+ }
304
+
305
+ if (routeSegment.startsWith(':')) {
306
+ const paramName = routeSegment.slice(1);
307
+ params[paramName] = decodeURIComponent(pathSegment);
308
+ } else if (routeSegment !== pathSegment) {
309
+ isMatch = false;
310
+ break;
311
+ }
312
+ }
313
+
314
+ if (isMatch) {
315
+ return { route, params };
316
+ }
317
+ }
318
+
319
+ return null;
320
+ }
321
+
264
322
  // Parse query string
265
323
  private parseQuery(searchParams: URLSearchParams): Record<string, string | undefined> {
266
324
  const query: Record<string, string | undefined> = {};
@@ -286,11 +344,30 @@ export default class BXO {
286
344
  }
287
345
 
288
346
  // Main request handler
289
- private async handleRequest(request: Request): Promise<Response> {
347
+ private async handleRequest(request: Request, server?: any): Promise<Response | undefined> {
290
348
  const url = new URL(request.url);
291
349
  const method = request.method;
292
350
  const pathname = url.pathname;
293
351
 
352
+ // Check for WebSocket upgrade
353
+ if (request.headers.get('upgrade') === 'websocket') {
354
+ const wsMatchResult = this.matchWSRoute(pathname);
355
+ if (wsMatchResult && server) {
356
+ const success = server.upgrade(request, {
357
+ data: {
358
+ handler: wsMatchResult.route.handler,
359
+ params: wsMatchResult.params,
360
+ pathname
361
+ }
362
+ });
363
+
364
+ if (success) {
365
+ return; // undefined response means upgrade was successful
366
+ }
367
+ }
368
+ return new Response('WebSocket upgrade failed', { status: 400 });
369
+ }
370
+
294
371
  const matchResult = this.matchRoute(method, pathname);
295
372
  if (!matchResult) {
296
373
  return new Response('Not Found', { status: 404 });
@@ -512,7 +589,27 @@ export default class BXO {
512
589
  this.server = Bun.serve({
513
590
  port,
514
591
  hostname,
515
- fetch: (request) => this.handleRequest(request),
592
+ fetch: (request, server) => this.handleRequest(request, server),
593
+ websocket: {
594
+ message: (ws: any, message: any) => {
595
+ const handler = ws.data?.handler;
596
+ if (handler?.onMessage) {
597
+ handler.onMessage(ws, message);
598
+ }
599
+ },
600
+ open: (ws: any) => {
601
+ const handler = ws.data?.handler;
602
+ if (handler?.onOpen) {
603
+ handler.onOpen(ws);
604
+ }
605
+ },
606
+ close: (ws: any, code?: number, reason?: string) => {
607
+ const handler = ws.data?.handler;
608
+ if (handler?.onClose) {
609
+ handler.onClose(ws, code, reason);
610
+ }
611
+ }
612
+ }
516
613
  });
517
614
 
518
615
  this.isRunning = true;
@@ -640,6 +737,7 @@ export default class BXO {
640
737
 
641
738
  // Application statistics
642
739
  totalRoutes: this._routes.length,
740
+ totalWsRoutes: this._wsRoutes.length,
643
741
  totalPlugins: this.plugins.length,
644
742
 
645
743
  // Hot reload configuration
@@ -664,6 +762,19 @@ export default class BXO {
664
762
  config: route.config || null
665
763
  }));
666
764
  }
765
+
766
+ // Get all WebSocket routes information
767
+ get wsRoutes() {
768
+ return this._wsRoutes.map((route: WSRoute) => ({
769
+ path: route.path,
770
+ hasHandlers: {
771
+ onOpen: !!route.handler.onOpen,
772
+ onMessage: !!route.handler.onMessage,
773
+ onClose: !!route.handler.onClose,
774
+ onError: !!route.handler.onError
775
+ }
776
+ }));
777
+ }
667
778
  }
668
779
 
669
780
  const error = (error: Error, status: number = 500) => {
@@ -674,4 +785,4 @@ const error = (error: Error, status: number = 500) => {
674
785
  export { z, error };
675
786
 
676
787
  // Export types for external use
677
- export type { RouteConfig, RouteDetail, Handler };
788
+ export type { RouteConfig, RouteDetail, Handler, WebSocketHandler, WSRoute };
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.4",
4
+ "version": "0.0.5-dev.5",
5
5
  "description": "A simple and lightweight web framework for Bun",
6
6
  "type": "module",
7
7
  "devDependencies": {