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.
- package/index.ts +114 -3
- 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 };
|