bxo 0.0.7 โ†’ 0.0.9

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.
@@ -9,19 +9,28 @@ 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, Search Params & Auth</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
+ <button onclick="connectWithParams()">Connect with Search Params</button>
21
+ <button onclick="connectWithAuth()">Connect with Auth</button>
20
22
  <button onclick="disconnect()">Disconnect</button>
23
+ <button onclick="setCookie()">Set Test Cookie</button>
21
24
 
22
25
  <script>
23
26
  let ws = null;
24
27
 
28
+ function setCookie() {
29
+ document.cookie = "testCookie=hello_from_client; path=/";
30
+ document.cookie = "sessionId=abc123; path=/";
31
+ addMessage('Cookies set! Refresh and reconnect to see them in server logs.');
32
+ }
33
+
25
34
  function connect() {
26
35
  ws = new WebSocket('ws://localhost:3000/ws');
27
36
 
@@ -42,6 +51,48 @@ async function main() {
42
51
  };
43
52
  }
44
53
 
54
+ function connectWithParams() {
55
+ ws = new WebSocket('ws://localhost:3000/ws?room=test&user=john&theme=dark');
56
+
57
+ ws.onopen = function() {
58
+ addMessage('Connected to WebSocket with search params');
59
+ };
60
+
61
+ ws.onmessage = function(event) {
62
+ addMessage('Received: ' + event.data);
63
+ };
64
+
65
+ ws.onclose = function() {
66
+ addMessage('Disconnected from WebSocket');
67
+ };
68
+
69
+ ws.onerror = function(error) {
70
+ addMessage('Error: ' + error);
71
+ };
72
+ }
73
+
74
+ function connectWithAuth() {
75
+ // Note: Browser WebSocket API doesn't support custom headers directly
76
+ // This would typically be done server-side or with a library
77
+ ws = new WebSocket('ws://localhost:3000/ws?token=abc123&auth=Bearer%20token123');
78
+
79
+ ws.onopen = function() {
80
+ addMessage('Connected to WebSocket with auth params');
81
+ };
82
+
83
+ ws.onmessage = function(event) {
84
+ addMessage('Received: ' + event.data);
85
+ };
86
+
87
+ ws.onclose = function() {
88
+ addMessage('Disconnected from WebSocket');
89
+ };
90
+
91
+ ws.onerror = function(error) {
92
+ addMessage('Error: ' + error);
93
+ };
94
+ }
95
+
45
96
  function disconnect() {
46
97
  if (ws) {
47
98
  ws.close();
@@ -73,60 +124,123 @@ async function main() {
73
124
  }
74
125
  });
75
126
  </script>
76
- `, 200, {
77
- "Content-Type": "text/html"
78
- });
127
+ `, 200);
79
128
  });
80
129
 
81
- // WebSocket route
130
+ // WebSocket route with enhanced client identification
82
131
  app.ws("/ws", {
83
132
  open(ws) {
84
- console.log("WebSocket connection opened");
85
- ws.send("Welcome to BXO WebSocket!");
133
+ const clientInfo = ws.data;
134
+ console.log("๐Ÿ”Œ WebSocket connection opened:");
135
+ console.log(` ๐Ÿ†” Client ID: ${clientInfo.id}`);
136
+ console.log(` ๐Ÿ”— Connection ID: ${clientInfo.connectionId}`);
137
+ console.log(` ๐ŸŒ Client IP: ${clientInfo.ip}`);
138
+ console.log(` ๐Ÿ–ฅ๏ธ User Agent: ${clientInfo.userAgent}`);
139
+ console.log(` ๐ŸŽฏ Origin: ${clientInfo.origin}`);
140
+ console.log(` ๐Ÿ  Host: ${clientInfo.host}`);
141
+ console.log(` ๐Ÿ“ Path: ${clientInfo.path}`);
142
+ console.log(` ๐Ÿช Cookies:`, clientInfo.cookies);
143
+ console.log(` ๐Ÿ” Search Params:`, clientInfo.searchParams);
144
+ console.log(` ๐Ÿ” Authorization:`, clientInfo.authorization || 'None');
145
+
146
+ // Check for authentication
147
+ if (clientInfo.authorization) {
148
+ console.log(` โœ… Authenticated user detected`);
149
+ ws.send(`Welcome authenticated user! Your WebSocket ID is: ${clientInfo.id}`);
150
+ } else if (clientInfo.searchParams.token || clientInfo.searchParams.auth) {
151
+ console.log(` ๐Ÿ”‘ Token-based auth detected`);
152
+ ws.send(`Welcome! Token detected. Your WebSocket ID is: ${clientInfo.id}`);
153
+ } else {
154
+ ws.send(`Welcome! Your WebSocket ID is: ${clientInfo.id}`);
155
+ }
86
156
  },
87
157
 
88
158
  message(ws, message) {
89
- console.log("Received message:", message);
90
- // Echo the message back
91
- ws.send(`Echo: ${message}`);
159
+ const clientInfo = ws.data;
160
+ console.log(`๐Ÿ’ฌ Message from ${clientInfo.id} (${clientInfo.ip}):`, message);
161
+ console.log(` ๐Ÿช Client cookies:`, clientInfo.cookies);
162
+ console.log(` ๐Ÿ” Search params:`, clientInfo.searchParams);
163
+ console.log(` ๐Ÿ” Auth:`, clientInfo.authorization || 'None');
164
+
165
+ // Echo the message back with client ID and context
166
+ let response = `[${clientInfo.id}] Echo: ${message}`;
167
+
168
+ // Add context based on search params
169
+ if (clientInfo.searchParams.room) {
170
+ response += ` (Room: ${clientInfo.searchParams.room})`;
171
+ }
172
+ if (clientInfo.searchParams.user) {
173
+ response += ` (User: ${clientInfo.searchParams.user})`;
174
+ }
175
+
176
+ ws.send(response);
92
177
  },
93
178
 
94
179
  close(ws, code, reason) {
95
- console.log(`WebSocket connection closed: ${code} ${reason}`);
180
+ const clientInfo = ws.data;
181
+ console.log(`โŒ WebSocket connection closed for ${clientInfo.id}: ${code} ${reason}`);
96
182
  },
97
183
 
98
184
  ping(ws, data) {
99
- console.log("Ping received:", data);
185
+ const clientInfo = ws.data;
186
+ console.log(`๐Ÿ“ Ping received from ${clientInfo.id}:`, data);
100
187
  },
101
188
 
102
189
  pong(ws, data) {
103
- console.log("Pong received:", data);
190
+ const clientInfo = ws.data;
191
+ console.log(`๐Ÿ“ Pong received from ${clientInfo.id}:`, data);
104
192
  }
105
193
  });
106
194
 
107
- // Another WebSocket route with parameters
195
+ // Chat room WebSocket with client identification
108
196
  app.ws("/chat/:room", {
109
197
  open(ws) {
110
- console.log(`WebSocket connection opened for room: ${ws.data?.room || 'unknown'}`);
111
- ws.send(`Welcome to chat room: ${ws.data?.room || 'unknown'}`);
198
+ const clientInfo = ws.data;
199
+ const room = clientInfo.path.split('/').pop() || 'unknown';
200
+ console.log(`๐Ÿ  Chat room connection opened for room: ${room}`);
201
+ console.log(` ๐Ÿ†” Client: ${clientInfo.id} (${clientInfo.ip})`);
202
+ console.log(` ๐Ÿช Cookies:`, clientInfo.cookies);
203
+ console.log(` ๐Ÿ” Search Params:`, clientInfo.searchParams);
204
+ console.log(` ๐Ÿ” Authorization:`, clientInfo.authorization || 'None');
205
+
206
+ // Check for user info in search params
207
+ const username = clientInfo.searchParams.user || clientInfo.searchParams.username || 'Anonymous';
208
+ ws.send(`Welcome to chat room: ${room}! Your ID is: ${clientInfo.id}, Username: ${username}`);
112
209
  },
113
210
 
114
211
  message(ws, message) {
115
- const room = ws.data?.room || 'unknown';
116
- console.log(`Message in room ${room}:`, message);
117
- ws.send(`[${room}] Echo: ${message}`);
212
+ const clientInfo = ws.data;
213
+ const room = clientInfo.path.split('/').pop() || 'unknown';
214
+ const username = clientInfo.searchParams.user || clientInfo.searchParams.username || 'Anonymous';
215
+ console.log(`๐Ÿ’ฌ Message in room ${room} from ${clientInfo.id} (${username}):`, message);
216
+ console.log(` ๐Ÿช Client cookies:`, clientInfo.cookies);
217
+ console.log(` ๐Ÿ” Search params:`, clientInfo.searchParams);
218
+ ws.send(`[${room}] [${username}] [${clientInfo.id}] Echo: ${message}`);
118
219
  },
119
220
 
120
221
  close(ws, code, reason) {
121
- const room = ws.data?.room || 'unknown';
122
- console.log(`WebSocket connection closed for room ${room}: ${code} ${reason}`);
222
+ const clientInfo = ws.data;
223
+ const room = clientInfo.path.split('/').pop() || 'unknown';
224
+ console.log(`โŒ Chat room connection closed for room ${room} (${clientInfo.id}): ${code} ${reason}`);
123
225
  }
124
226
  });
125
227
 
126
228
  app.start();
127
- console.log(`Server is running on http://localhost:${app.server?.port}`);
128
- console.log(`WebSocket available at ws://localhost:${app.server?.port}/ws`);
129
- console.log(`Chat WebSocket available at ws://localhost:${app.server?.port}/chat/:room`);
229
+ console.log(`๐Ÿš€ Server is running on http://localhost:${app.server?.port}`);
230
+ console.log(`๐Ÿ”Œ WebSocket available at ws://localhost:${app.server?.port}/ws`);
231
+ console.log(`๐Ÿ’ฌ Chat WebSocket available at ws://localhost:${app.server?.port}/chat/:room`);
232
+ console.log(`\n๐Ÿ“ Features demonstrated:`);
233
+ console.log(` โ€ข ws.id - Short, unique client identifier`);
234
+ console.log(` โ€ข ws.data.connectionId - Detailed connection ID`);
235
+ console.log(` โ€ข ws.data.cookies - Parsed cookies from handshake`);
236
+ console.log(` โ€ข ws.data.searchParams - Query parameters from URL`);
237
+ console.log(` โ€ข ws.data.authorization - Authorization header`);
238
+ console.log(` โ€ข Client IP, User Agent, Origin tracking`);
239
+ console.log(`\n๐Ÿงช Test URLs:`);
240
+ console.log(` โ€ข Basic: ws://localhost:${app.server?.port}/ws`);
241
+ console.log(` โ€ข With params: ws://localhost:${app.server?.port}/ws?room=test&user=john&theme=dark`);
242
+ console.log(` โ€ข With auth: ws://localhost:${app.server?.port}/ws?token=abc123&auth=Bearer%20token123`);
243
+ console.log(` โ€ข Chat room: ws://localhost:${app.server?.port}/chat/general?user=alice&theme=light`);
130
244
  }
131
245
 
132
- main().catch(console.error);
246
+ main().catch(console.error);
package/package.json CHANGED
@@ -5,7 +5,7 @@
5
5
  ".": "./src/index.ts",
6
6
  "./plugins": "./plugins/index.ts"
7
7
  },
8
- "version": "0.0.7",
8
+ "version": "0.0.9",
9
9
  "type": "module",
10
10
  "devDependencies": {
11
11
  "@types/bun": "latest"
package/src/index.ts CHANGED
@@ -92,6 +92,20 @@ 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
+ searchParams: Record<string, string | string[]>; // Query parameters from the URL
106
+ authorization?: string; // Authorization header (Bearer token, Basic auth, etc.)
107
+ };
108
+
95
109
  // WebSocket handler types
96
110
  export type WebSocketHandler<T = any> = {
97
111
  message?(ws: Bun.ServerWebSocket<T>, message: string | Buffer): void | Promise<void>;
@@ -436,22 +450,22 @@ export default class BXO {
436
450
 
437
451
  // Create WebSocket configuration if we have WebSocket routes
438
452
  const websocketConfig = hasWebSocketRoutes ? {
439
- message: (ws: Bun.ServerWebSocket<{ path: string }>, message: string | Buffer) => {
453
+ message: (ws: Bun.ServerWebSocket<WebSocketClientInfo>, message: string | Buffer) => {
440
454
  this.handleWebSocketMessage(ws, message);
441
455
  },
442
- open: (ws: Bun.ServerWebSocket<{ path: string }>) => {
456
+ open: (ws: Bun.ServerWebSocket<WebSocketClientInfo>) => {
443
457
  this.handleWebSocketOpen(ws);
444
458
  },
445
- close: (ws: Bun.ServerWebSocket<{ path: string }>, code: number, reason: string) => {
459
+ close: (ws: Bun.ServerWebSocket<WebSocketClientInfo>, code: number, reason: string) => {
446
460
  this.handleWebSocketClose(ws, code, reason);
447
461
  },
448
- drain: (ws: Bun.ServerWebSocket<{ path: string }>) => {
462
+ drain: (ws: Bun.ServerWebSocket<WebSocketClientInfo>) => {
449
463
  this.handleWebSocketDrain(ws);
450
464
  },
451
- ping: (ws: Bun.ServerWebSocket<{ path: string }>, data: Buffer) => {
465
+ ping: (ws: Bun.ServerWebSocket<WebSocketClientInfo>, data: Buffer) => {
452
466
  this.handleWebSocketPing(ws, data);
453
467
  },
454
- pong: (ws: Bun.ServerWebSocket<{ path: string }>, data: Buffer) => {
468
+ pong: (ws: Bun.ServerWebSocket<WebSocketClientInfo>, data: Buffer) => {
455
469
  this.handleWebSocketPong(ws, data);
456
470
  }
457
471
  } : undefined;
@@ -467,8 +481,44 @@ export default class BXO {
467
481
  const url = new URL(req.url);
468
482
  const wsRoute = this.findWebSocketRoute(url.pathname);
469
483
  if (wsRoute) {
484
+ // Capture client information during handshake
485
+ const shortId = Math.random().toString(36).substr(2, 8);
486
+ const connectionId = `${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
487
+ const cookieHeader = req.headers.get("cookie");
488
+ const authHeader = req.headers.get("authorization");
489
+
490
+ // Parse search parameters from URL
491
+ const searchParams: Record<string, string | string[]> = {};
492
+ for (const [key, value] of url.searchParams.entries()) {
493
+ if (key in searchParams) {
494
+ const existing = searchParams[key];
495
+ if (Array.isArray(existing)) {
496
+ existing.push(value);
497
+ } else {
498
+ searchParams[key] = [existing as string, value];
499
+ }
500
+ } else {
501
+ searchParams[key] = value;
502
+ }
503
+ }
504
+
505
+ const clientInfo = {
506
+ id: shortId, // Short, easy-to-use ID
507
+ path: url.pathname,
508
+ ip: req.headers.get("x-forwarded-for") ||
509
+ req.headers.get("x-real-ip") ||
510
+ "unknown",
511
+ userAgent: req.headers.get("user-agent") || "unknown",
512
+ origin: req.headers.get("origin") || "unknown",
513
+ host: req.headers.get("host") || "unknown",
514
+ connectionId: connectionId, // Longer, more detailed ID
515
+ cookies: cookieHeader ? parseCookies(cookieHeader) : {},
516
+ searchParams: searchParams, // Query parameters from URL
517
+ authorization: authHeader || undefined // Authorization header
518
+ };
519
+
470
520
  const success = server.upgrade(req, {
471
- data: { path: url.pathname }
521
+ data: clientInfo
472
522
  });
473
523
  if (success) {
474
524
  return; // WebSocket upgrade successful
@@ -533,7 +583,7 @@ export default class BXO {
533
583
  return null;
534
584
  }
535
585
 
536
- private handleWebSocketMessage(ws: Bun.ServerWebSocket<{ path: string }>, message: string | Buffer): void {
586
+ private handleWebSocketMessage(ws: Bun.ServerWebSocket<WebSocketClientInfo>, message: string | Buffer): void {
537
587
  const route = this.findWebSocketRoute(ws.data?.path || "");
538
588
  if (route?.websocketHandler?.message) {
539
589
  try {
@@ -544,7 +594,7 @@ export default class BXO {
544
594
  }
545
595
  }
546
596
 
547
- private handleWebSocketOpen(ws: Bun.ServerWebSocket<{ path: string }>): void {
597
+ private handleWebSocketOpen(ws: Bun.ServerWebSocket<WebSocketClientInfo>): void {
548
598
  const route = this.findWebSocketRoute(ws.data?.path || "");
549
599
  if (route?.websocketHandler?.open) {
550
600
  try {
@@ -555,7 +605,7 @@ export default class BXO {
555
605
  }
556
606
  }
557
607
 
558
- private handleWebSocketClose(ws: Bun.ServerWebSocket<{ path: string }>, code: number, reason: string): void {
608
+ private handleWebSocketClose(ws: Bun.ServerWebSocket<WebSocketClientInfo>, code: number, reason: string): void {
559
609
  const route = this.findWebSocketRoute(ws.data?.path || "");
560
610
  if (route?.websocketHandler?.close) {
561
611
  try {
@@ -566,7 +616,7 @@ export default class BXO {
566
616
  }
567
617
  }
568
618
 
569
- private handleWebSocketDrain(ws: Bun.ServerWebSocket<{ path: string }>): void {
619
+ private handleWebSocketDrain(ws: Bun.ServerWebSocket<WebSocketClientInfo>): void {
570
620
  const route = this.findWebSocketRoute(ws.data?.path || "");
571
621
  if (route?.websocketHandler?.drain) {
572
622
  try {
@@ -577,7 +627,7 @@ export default class BXO {
577
627
  }
578
628
  }
579
629
 
580
- private handleWebSocketPing(ws: Bun.ServerWebSocket<{ path: string }>, data: Buffer): void {
630
+ private handleWebSocketPing(ws: Bun.ServerWebSocket<WebSocketClientInfo>, data: Buffer): void {
581
631
  const route = this.findWebSocketRoute(ws.data?.path || "");
582
632
  if (route?.websocketHandler?.ping) {
583
633
  try {
@@ -588,7 +638,7 @@ export default class BXO {
588
638
  }
589
639
  }
590
640
 
591
- private handleWebSocketPong(ws: Bun.ServerWebSocket<{ path: string }>, data: Buffer): void {
641
+ private handleWebSocketPong(ws: Bun.ServerWebSocket<WebSocketClientInfo>, data: Buffer): void {
592
642
  const route = this.findWebSocketRoute(ws.data?.path || "");
593
643
  if (route?.websocketHandler?.pong) {
594
644
  try {
@@ -0,0 +1,68 @@
1
+ // Comprehensive test for enhanced WebSocket client identification
2
+ console.log('๐Ÿงช Testing Enhanced WebSocket Client Identification...\n');
3
+
4
+ // Test 1: Basic WebSocket connection
5
+ console.log('1. Testing basic WebSocket connection');
6
+ const ws1 = new WebSocket('ws://localhost:3000/ws');
7
+
8
+ ws1.onopen = function() {
9
+ console.log('โœ“ Connected to basic WebSocket');
10
+ ws1.send('Hello from basic connection!');
11
+ };
12
+
13
+ ws1.onmessage = function(event) {
14
+ console.log('โœ“ Received:', event.data);
15
+ ws1.close();
16
+ };
17
+
18
+ ws1.onclose = function() {
19
+ console.log('โœ“ Basic connection closed\n');
20
+
21
+ // Test 2: WebSocket with search parameters
22
+ console.log('2. Testing WebSocket with search parameters');
23
+ const ws2 = new WebSocket('ws://localhost:3000/ws?room=test&user=john&theme=dark&token=abc123');
24
+
25
+ ws2.onopen = function() {
26
+ console.log('โœ“ Connected with search params');
27
+ ws2.send('Hello from connection with params!');
28
+ };
29
+
30
+ ws2.onmessage = function(event) {
31
+ console.log('โœ“ Received:', event.data);
32
+ ws2.close();
33
+ };
34
+
35
+ ws2.onclose = function() {
36
+ console.log('โœ“ Search params connection closed\n');
37
+
38
+ // Test 3: Chat room with search parameters
39
+ console.log('3. Testing chat room with search parameters');
40
+ const ws3 = new WebSocket('ws://localhost:3000/chat/general?user=alice&theme=light&role=moderator');
41
+
42
+ ws3.onopen = function() {
43
+ console.log('โœ“ Connected to chat room with params');
44
+ ws3.send('Hello from chat room!');
45
+ };
46
+
47
+ ws3.onmessage = function(event) {
48
+ console.log('โœ“ Received:', event.data);
49
+ ws3.close();
50
+ };
51
+
52
+ ws3.onclose = function() {
53
+ console.log('โœ“ Chat room connection closed');
54
+ console.log('\nโœ… All tests completed! Check the server logs to see:');
55
+ console.log(' โ€ข ws.id - Short client identifier');
56
+ console.log(' โ€ข ws.data.searchParams - Query parameters');
57
+ console.log(' โ€ข ws.data.cookies - Parsed cookies');
58
+ console.log(' โ€ข ws.data.authorization - Auth header');
59
+ console.log(' โ€ข Client IP and other identification info');
60
+ process.exit(0);
61
+ };
62
+ };
63
+ };
64
+
65
+ ws1.onerror = function(error) {
66
+ console.error('โŒ WebSocket error:', error);
67
+ process.exit(1);
68
+ };