bxo 0.0.7 ā 0.0.8
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/websocket-example.ts +59 -26
- package/package.json +1 -1
- package/src/index.ts +43 -13
|
@@ -9,19 +9,26 @@ async function main() {
|
|
|
9
9
|
<!DOCTYPE html>
|
|
10
10
|
<html>
|
|
11
11
|
<head>
|
|
12
|
-
<title>BXO WebSocket Example</title>
|
|
12
|
+
<title>BXO WebSocket Example with Client ID & Cookies</title>
|
|
13
13
|
</head>
|
|
14
14
|
<body>
|
|
15
|
-
<h1>BXO WebSocket Example</h1>
|
|
15
|
+
<h1>BXO WebSocket Example with Client ID & Cookies</h1>
|
|
16
16
|
<div id="messages"></div>
|
|
17
17
|
<input type="text" id="messageInput" placeholder="Type a message...">
|
|
18
18
|
<button onclick="sendMessage()">Send</button>
|
|
19
19
|
<button onclick="connect()">Connect</button>
|
|
20
20
|
<button onclick="disconnect()">Disconnect</button>
|
|
21
|
+
<button onclick="setCookie()">Set Test Cookie</button>
|
|
21
22
|
|
|
22
23
|
<script>
|
|
23
24
|
let ws = null;
|
|
24
25
|
|
|
26
|
+
function setCookie() {
|
|
27
|
+
document.cookie = "testCookie=hello_from_client; path=/";
|
|
28
|
+
document.cookie = "sessionId=abc123; path=/";
|
|
29
|
+
addMessage('Cookies set! Refresh and reconnect to see them in server logs.');
|
|
30
|
+
}
|
|
31
|
+
|
|
25
32
|
function connect() {
|
|
26
33
|
ws = new WebSocket('ws://localhost:3000/ws');
|
|
27
34
|
|
|
@@ -73,60 +80,86 @@ async function main() {
|
|
|
73
80
|
}
|
|
74
81
|
});
|
|
75
82
|
</script>
|
|
76
|
-
`, 200
|
|
77
|
-
"Content-Type": "text/html"
|
|
78
|
-
});
|
|
83
|
+
`, 200);
|
|
79
84
|
});
|
|
80
85
|
|
|
81
|
-
// WebSocket route
|
|
86
|
+
// WebSocket route with enhanced client identification
|
|
82
87
|
app.ws("/ws", {
|
|
83
88
|
open(ws) {
|
|
84
|
-
|
|
85
|
-
|
|
89
|
+
const clientInfo = ws.data;
|
|
90
|
+
console.log("š WebSocket connection opened:");
|
|
91
|
+
console.log(` š Client ID: ${clientInfo.id}`);
|
|
92
|
+
console.log(` š Connection ID: ${clientInfo.connectionId}`);
|
|
93
|
+
console.log(` š Client IP: ${clientInfo.ip}`);
|
|
94
|
+
console.log(` š„ļø User Agent: ${clientInfo.userAgent}`);
|
|
95
|
+
console.log(` šÆ Origin: ${clientInfo.origin}`);
|
|
96
|
+
console.log(` š Host: ${clientInfo.host}`);
|
|
97
|
+
console.log(` š Path: ${clientInfo.path}`);
|
|
98
|
+
console.log(` šŖ Cookies:`, clientInfo.cookies);
|
|
99
|
+
|
|
100
|
+
ws.send(`Welcome! Your WebSocket ID is: ${clientInfo.id}`);
|
|
86
101
|
},
|
|
87
102
|
|
|
88
103
|
message(ws, message) {
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
104
|
+
const clientInfo = ws.data;
|
|
105
|
+
console.log(`š¬ Message from ${clientInfo.id} (${clientInfo.ip}):`, message);
|
|
106
|
+
console.log(` šŖ Client cookies:`, clientInfo.cookies);
|
|
107
|
+
|
|
108
|
+
// Echo the message back with client ID
|
|
109
|
+
ws.send(`[${clientInfo.id}] Echo: ${message}`);
|
|
92
110
|
},
|
|
93
111
|
|
|
94
112
|
close(ws, code, reason) {
|
|
95
|
-
|
|
113
|
+
const clientInfo = ws.data;
|
|
114
|
+
console.log(`ā WebSocket connection closed for ${clientInfo.id}: ${code} ${reason}`);
|
|
96
115
|
},
|
|
97
116
|
|
|
98
117
|
ping(ws, data) {
|
|
99
|
-
|
|
118
|
+
const clientInfo = ws.data;
|
|
119
|
+
console.log(`š Ping received from ${clientInfo.id}:`, data);
|
|
100
120
|
},
|
|
101
121
|
|
|
102
122
|
pong(ws, data) {
|
|
103
|
-
|
|
123
|
+
const clientInfo = ws.data;
|
|
124
|
+
console.log(`š Pong received from ${clientInfo.id}:`, data);
|
|
104
125
|
}
|
|
105
126
|
});
|
|
106
127
|
|
|
107
|
-
//
|
|
128
|
+
// Chat room WebSocket with client identification
|
|
108
129
|
app.ws("/chat/:room", {
|
|
109
130
|
open(ws) {
|
|
110
|
-
|
|
111
|
-
|
|
131
|
+
const clientInfo = ws.data;
|
|
132
|
+
const room = clientInfo.path.split('/').pop() || 'unknown';
|
|
133
|
+
console.log(`š Chat room connection opened for room: ${room}`);
|
|
134
|
+
console.log(` š Client: ${clientInfo.id} (${clientInfo.ip})`);
|
|
135
|
+
console.log(` šŖ Cookies:`, clientInfo.cookies);
|
|
136
|
+
ws.send(`Welcome to chat room: ${room}! Your ID is: ${clientInfo.id}`);
|
|
112
137
|
},
|
|
113
138
|
|
|
114
139
|
message(ws, message) {
|
|
115
|
-
const
|
|
116
|
-
|
|
117
|
-
|
|
140
|
+
const clientInfo = ws.data;
|
|
141
|
+
const room = clientInfo.path.split('/').pop() || 'unknown';
|
|
142
|
+
console.log(`š¬ Message in room ${room} from ${clientInfo.id}:`, message);
|
|
143
|
+
console.log(` šŖ Client cookies:`, clientInfo.cookies);
|
|
144
|
+
ws.send(`[${room}] [${clientInfo.id}] Echo: ${message}`);
|
|
118
145
|
},
|
|
119
146
|
|
|
120
147
|
close(ws, code, reason) {
|
|
121
|
-
const
|
|
122
|
-
|
|
148
|
+
const clientInfo = ws.data;
|
|
149
|
+
const room = clientInfo.path.split('/').pop() || 'unknown';
|
|
150
|
+
console.log(`ā Chat room connection closed for room ${room} (${clientInfo.id}): ${code} ${reason}`);
|
|
123
151
|
}
|
|
124
152
|
});
|
|
125
153
|
|
|
126
154
|
app.start();
|
|
127
|
-
console.log(
|
|
128
|
-
console.log(
|
|
129
|
-
console.log(
|
|
155
|
+
console.log(`š Server is running on http://localhost:${app.server?.port}`);
|
|
156
|
+
console.log(`š WebSocket available at ws://localhost:${app.server?.port}/ws`);
|
|
157
|
+
console.log(`š¬ Chat WebSocket available at ws://localhost:${app.server?.port}/chat/:room`);
|
|
158
|
+
console.log(`\nš Features demonstrated:`);
|
|
159
|
+
console.log(` ⢠ws.id - Short, unique client identifier`);
|
|
160
|
+
console.log(` ⢠ws.data.connectionId - Detailed connection ID`);
|
|
161
|
+
console.log(` ⢠ws.data.cookies - Parsed cookies from handshake`);
|
|
162
|
+
console.log(` ⢠Client IP, User Agent, Origin tracking`);
|
|
130
163
|
}
|
|
131
164
|
|
|
132
|
-
main().catch(console.error);
|
|
165
|
+
main().catch(console.error);
|
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -92,6 +92,18 @@ type Handler<P extends string, S extends RouteSchema | undefined = undefined> =
|
|
|
92
92
|
app: BXO
|
|
93
93
|
) => Response | string | BunFile | Promise<Response | string | BunFile>
|
|
94
94
|
|
|
95
|
+
// WebSocket client information type
|
|
96
|
+
export type WebSocketClientInfo = {
|
|
97
|
+
id: string; // Short, unique identifier for the WebSocket connection
|
|
98
|
+
path: string;
|
|
99
|
+
ip: string;
|
|
100
|
+
userAgent: string;
|
|
101
|
+
origin: string;
|
|
102
|
+
host: string;
|
|
103
|
+
connectionId: string; // Longer, more detailed connection identifier
|
|
104
|
+
cookies: Record<string, string>; // Parsed cookies from the handshake
|
|
105
|
+
};
|
|
106
|
+
|
|
95
107
|
// WebSocket handler types
|
|
96
108
|
export type WebSocketHandler<T = any> = {
|
|
97
109
|
message?(ws: Bun.ServerWebSocket<T>, message: string | Buffer): void | Promise<void>;
|
|
@@ -436,22 +448,22 @@ export default class BXO {
|
|
|
436
448
|
|
|
437
449
|
// Create WebSocket configuration if we have WebSocket routes
|
|
438
450
|
const websocketConfig = hasWebSocketRoutes ? {
|
|
439
|
-
message: (ws: Bun.ServerWebSocket<
|
|
451
|
+
message: (ws: Bun.ServerWebSocket<WebSocketClientInfo>, message: string | Buffer) => {
|
|
440
452
|
this.handleWebSocketMessage(ws, message);
|
|
441
453
|
},
|
|
442
|
-
open: (ws: Bun.ServerWebSocket<
|
|
454
|
+
open: (ws: Bun.ServerWebSocket<WebSocketClientInfo>) => {
|
|
443
455
|
this.handleWebSocketOpen(ws);
|
|
444
456
|
},
|
|
445
|
-
close: (ws: Bun.ServerWebSocket<
|
|
457
|
+
close: (ws: Bun.ServerWebSocket<WebSocketClientInfo>, code: number, reason: string) => {
|
|
446
458
|
this.handleWebSocketClose(ws, code, reason);
|
|
447
459
|
},
|
|
448
|
-
drain: (ws: Bun.ServerWebSocket<
|
|
460
|
+
drain: (ws: Bun.ServerWebSocket<WebSocketClientInfo>) => {
|
|
449
461
|
this.handleWebSocketDrain(ws);
|
|
450
462
|
},
|
|
451
|
-
ping: (ws: Bun.ServerWebSocket<
|
|
463
|
+
ping: (ws: Bun.ServerWebSocket<WebSocketClientInfo>, data: Buffer) => {
|
|
452
464
|
this.handleWebSocketPing(ws, data);
|
|
453
465
|
},
|
|
454
|
-
pong: (ws: Bun.ServerWebSocket<
|
|
466
|
+
pong: (ws: Bun.ServerWebSocket<WebSocketClientInfo>, data: Buffer) => {
|
|
455
467
|
this.handleWebSocketPong(ws, data);
|
|
456
468
|
}
|
|
457
469
|
} : undefined;
|
|
@@ -467,8 +479,26 @@ export default class BXO {
|
|
|
467
479
|
const url = new URL(req.url);
|
|
468
480
|
const wsRoute = this.findWebSocketRoute(url.pathname);
|
|
469
481
|
if (wsRoute) {
|
|
482
|
+
// Capture client information during handshake
|
|
483
|
+
const shortId = Math.random().toString(36).substr(2, 8);
|
|
484
|
+
const connectionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
485
|
+
const cookieHeader = req.headers.get("cookie");
|
|
486
|
+
|
|
487
|
+
const clientInfo = {
|
|
488
|
+
id: shortId, // Short, easy-to-use ID
|
|
489
|
+
path: url.pathname,
|
|
490
|
+
ip: req.headers.get("x-forwarded-for") ||
|
|
491
|
+
req.headers.get("x-real-ip") ||
|
|
492
|
+
"unknown",
|
|
493
|
+
userAgent: req.headers.get("user-agent") || "unknown",
|
|
494
|
+
origin: req.headers.get("origin") || "unknown",
|
|
495
|
+
host: req.headers.get("host") || "unknown",
|
|
496
|
+
connectionId: connectionId, // Longer, more detailed ID
|
|
497
|
+
cookies: cookieHeader ? parseCookies(cookieHeader) : {}
|
|
498
|
+
};
|
|
499
|
+
|
|
470
500
|
const success = server.upgrade(req, {
|
|
471
|
-
data:
|
|
501
|
+
data: clientInfo
|
|
472
502
|
});
|
|
473
503
|
if (success) {
|
|
474
504
|
return; // WebSocket upgrade successful
|
|
@@ -533,7 +563,7 @@ export default class BXO {
|
|
|
533
563
|
return null;
|
|
534
564
|
}
|
|
535
565
|
|
|
536
|
-
private handleWebSocketMessage(ws: Bun.ServerWebSocket<
|
|
566
|
+
private handleWebSocketMessage(ws: Bun.ServerWebSocket<WebSocketClientInfo>, message: string | Buffer): void {
|
|
537
567
|
const route = this.findWebSocketRoute(ws.data?.path || "");
|
|
538
568
|
if (route?.websocketHandler?.message) {
|
|
539
569
|
try {
|
|
@@ -544,7 +574,7 @@ export default class BXO {
|
|
|
544
574
|
}
|
|
545
575
|
}
|
|
546
576
|
|
|
547
|
-
private handleWebSocketOpen(ws: Bun.ServerWebSocket<
|
|
577
|
+
private handleWebSocketOpen(ws: Bun.ServerWebSocket<WebSocketClientInfo>): void {
|
|
548
578
|
const route = this.findWebSocketRoute(ws.data?.path || "");
|
|
549
579
|
if (route?.websocketHandler?.open) {
|
|
550
580
|
try {
|
|
@@ -555,7 +585,7 @@ export default class BXO {
|
|
|
555
585
|
}
|
|
556
586
|
}
|
|
557
587
|
|
|
558
|
-
private handleWebSocketClose(ws: Bun.ServerWebSocket<
|
|
588
|
+
private handleWebSocketClose(ws: Bun.ServerWebSocket<WebSocketClientInfo>, code: number, reason: string): void {
|
|
559
589
|
const route = this.findWebSocketRoute(ws.data?.path || "");
|
|
560
590
|
if (route?.websocketHandler?.close) {
|
|
561
591
|
try {
|
|
@@ -566,7 +596,7 @@ export default class BXO {
|
|
|
566
596
|
}
|
|
567
597
|
}
|
|
568
598
|
|
|
569
|
-
private handleWebSocketDrain(ws: Bun.ServerWebSocket<
|
|
599
|
+
private handleWebSocketDrain(ws: Bun.ServerWebSocket<WebSocketClientInfo>): void {
|
|
570
600
|
const route = this.findWebSocketRoute(ws.data?.path || "");
|
|
571
601
|
if (route?.websocketHandler?.drain) {
|
|
572
602
|
try {
|
|
@@ -577,7 +607,7 @@ export default class BXO {
|
|
|
577
607
|
}
|
|
578
608
|
}
|
|
579
609
|
|
|
580
|
-
private handleWebSocketPing(ws: Bun.ServerWebSocket<
|
|
610
|
+
private handleWebSocketPing(ws: Bun.ServerWebSocket<WebSocketClientInfo>, data: Buffer): void {
|
|
581
611
|
const route = this.findWebSocketRoute(ws.data?.path || "");
|
|
582
612
|
if (route?.websocketHandler?.ping) {
|
|
583
613
|
try {
|
|
@@ -588,7 +618,7 @@ export default class BXO {
|
|
|
588
618
|
}
|
|
589
619
|
}
|
|
590
620
|
|
|
591
|
-
private handleWebSocketPong(ws: Bun.ServerWebSocket<
|
|
621
|
+
private handleWebSocketPong(ws: Bun.ServerWebSocket<WebSocketClientInfo>, data: Buffer): void {
|
|
592
622
|
const route = this.findWebSocketRoute(ws.data?.path || "");
|
|
593
623
|
if (route?.websocketHandler?.pong) {
|
|
594
624
|
try {
|