@aetherframework/websocket 1.0.1 → 1.0.3

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.
@@ -0,0 +1,95 @@
1
+ /**
2
+ * @file Basic WebSocket Server Example - Final Version
3
+ */
4
+
5
+ import { WebSocketFactory } from '../index.js';
6
+
7
+ async function main() {
8
+ try {
9
+ // Create WebSocket factory instance
10
+ const factory = new WebSocketFactory({
11
+ driver: 'http', // Use HTTP upgrade driver
12
+ port: process.env.WS_PORT || 8080,
13
+ host: process.env.WS_HOST || '0.0.0.0',
14
+ maxPayload: 1024 * 1024, // 1MB
15
+ pingInterval: 30000, // 30 seconds heartbeat
16
+ pingTimeout: 10000 // 10 seconds timeout
17
+ });
18
+
19
+ // Create server
20
+ const server = await factory.createServer();
21
+
22
+ console.log('āœ… WebSocket server starting...');
23
+
24
+ // Listen for connection events
25
+ factory.on('connection', (connection) => {
26
+ console.log(`šŸ”— Client connected: ${connection.id} from ${connection.remoteAddress || 'unknown'}`);
27
+
28
+ // Send welcome message
29
+ connection.send(JSON.stringify({
30
+ type: 'welcome',
31
+ message: 'Connected to WebSocket server',
32
+ connectionId: connection.id,
33
+ timestamp: Date.now()
34
+ }));
35
+
36
+ // Listen for message events
37
+ connection.on('message', (data, isBinary) => {
38
+ const message = isBinary ? '[Binary Data]' : data.toString();
39
+ console.log(`šŸ“Ø Received from ${connection.id}: ${message}`);
40
+
41
+ // Echo back the message
42
+ connection.send(`Echo: ${message}`);
43
+ });
44
+
45
+ // Listen for close events
46
+ connection.on('close', (code, reason) => {
47
+ console.log(`āŒ Client disconnected: ${connection.id}, code: ${code}, reason: ${reason}`);
48
+ });
49
+
50
+ // Listen for error events
51
+ connection.on('error', (error) => {
52
+ console.error(`āš ļø Connection error for ${connection.id}:`, error);
53
+ });
54
+ });
55
+
56
+ // Listen for server listening event
57
+ factory.on('server:listening', (address) => {
58
+ console.log(`šŸš€ Server listening on ws://${address.host}:${address.port}`);
59
+ console.log('šŸ“” Waiting for connections...');
60
+ console.log('šŸ›‘ Press Ctrl+C to stop the server');
61
+ });
62
+
63
+ // Listen for server error events
64
+ factory.on('error', (error) => {
65
+ console.error('šŸ’„ Server error:', error);
66
+ });
67
+
68
+ // Graceful shutdown handling
69
+ process.on('SIGINT', async () => {
70
+ console.log('\nšŸ”„ Shutting down WebSocket server...');
71
+ try {
72
+ await factory.close();
73
+ console.log('āœ… Server closed successfully');
74
+ process.exit(0);
75
+ } catch (error) {
76
+ console.error('āŒ Error closing server:', error);
77
+ process.exit(1);
78
+ }
79
+ });
80
+
81
+ // Keep process running
82
+ process.on('SIGTERM', async () => {
83
+ console.log('\nšŸ”„ Received SIGTERM, shutting down...');
84
+ await factory.close();
85
+ process.exit(0);
86
+ });
87
+
88
+ } catch (error) {
89
+ console.error('āŒ Failed to start server:', error);
90
+ process.exit(1);
91
+ }
92
+ }
93
+
94
+ // Start the server
95
+ main();
@@ -0,0 +1,416 @@
1
+ /**
2
+ * @file Chat Server Example using Rooms
3
+ * @description Complete chat server with room-based broadcasting with event fixes
4
+ */
5
+
6
+ import { WebSocketFactory } from '../index.js';
7
+ import BroadcastManager from '../src/middleware/broadcast-manager.js';
8
+
9
+ async function main() {
10
+ try {
11
+ console.log('šŸš€ Starting chat server...');
12
+ console.log(`šŸ“” Port: ${process.env.WS_PORT || 3000}`);
13
+ console.log(`šŸ  Host: ${process.env.WS_HOST || '0.0.0.0'}`);
14
+
15
+ // Create WebSocket factory instance
16
+ const factory = new WebSocketFactory({
17
+ driver: 'http',
18
+ port: process.env.WS_PORT || 3000,
19
+ host: process.env.WS_HOST || '0.0.0.0',
20
+ maxPayload: 1024 * 1024,
21
+ pingInterval: 30000,
22
+ pingTimeout: 10000
23
+ });
24
+
25
+ // Create broadcast manager
26
+ const broadcastMgr = new BroadcastManager();
27
+
28
+ // Track if server:listening event has been fired
29
+ let serverListeningEventFired = false;
30
+
31
+ // Listen for server listening event
32
+ factory.on('server:listening', (address) => {
33
+ serverListeningEventFired = true;
34
+ console.log(`\nšŸŽ‰ ============================================`);
35
+ console.log(`šŸš€ Chat server listening on ws://${address.host}:${address.port}`);
36
+ console.log(`šŸ’¬ Room-based chat server ready for connections`);
37
+ console.log(`šŸ›‘ Press Ctrl+C to stop the server`);
38
+ console.log(`============================================\n`);
39
+ });
40
+
41
+ // Listen for driver initialization (with duplicate prevention)
42
+ let driverInitialized = false;
43
+ factory.on('driver:initialized', (info) => {
44
+ if (!driverInitialized) {
45
+ driverInitialized = true;
46
+ console.log(`šŸ”§ Driver initialized: ${info.driver}`);
47
+ if (factory.connectionManager) {
48
+ console.log('šŸ”— Connection manager initialized');
49
+ }
50
+ }
51
+ });
52
+
53
+ // Listen for connection events
54
+ factory.on('connection', (connection) => {
55
+ console.log(`šŸ‘¤ User connected: ${connection.id}`);
56
+
57
+ // Apply broadcast manager middleware
58
+ broadcastMgr.middleware()(connection, () => {
59
+ console.log(`āœ… Broadcast middleware applied for ${connection.id}`);
60
+ });
61
+
62
+ // Send welcome message
63
+ connection.send(JSON.stringify({
64
+ type: 'system',
65
+ message: 'Welcome to Chat Server!',
66
+ commands: {
67
+ join: 'Send {"type":"join","room":"room-name"} to join a room',
68
+ chat: 'Send {"type":"chat","room":"room-name","message":"your message"} to send message',
69
+ leave: 'Send {"type":"leave","room":"room-name"} to leave a room',
70
+ list: 'Send {"type":"list"} to list all rooms'
71
+ },
72
+ connectionId: connection.id,
73
+ timestamp: Date.now()
74
+ }));
75
+
76
+ // Listen for message events
77
+ connection.on('message', (data, isBinary) => {
78
+ if (isBinary) {
79
+ connection.send(JSON.stringify({
80
+ type: 'error',
81
+ message: 'Binary messages not supported in chat'
82
+ }));
83
+ return;
84
+ }
85
+
86
+ try {
87
+ const message = JSON.parse(data.toString());
88
+ console.log(`šŸ“Ø Message from ${connection.id}:`, message);
89
+
90
+ switch (message.type) {
91
+ case 'join':
92
+ handleJoinRoom(connection, message);
93
+ break;
94
+
95
+ case 'leave':
96
+ handleLeaveRoom(connection, message);
97
+ break;
98
+
99
+ case 'chat':
100
+ handleChatMessage(connection, message);
101
+ break;
102
+
103
+ case 'list':
104
+ handleListRooms(connection);
105
+ break;
106
+
107
+ case 'users':
108
+ handleListUsers(connection, message);
109
+ break;
110
+
111
+ default:
112
+ connection.send(JSON.stringify({
113
+ type: 'error',
114
+ message: 'Unknown message type',
115
+ validTypes: ['join', 'leave', 'chat', 'list', 'users']
116
+ }));
117
+ }
118
+ } catch (error) {
119
+ console.error(`āŒ JSON parse error from ${connection.id}:`, error.message);
120
+ connection.send(JSON.stringify({
121
+ type: 'error',
122
+ message: 'Invalid JSON format',
123
+ example: '{"type":"join","room":"general"}'
124
+ }));
125
+ }
126
+ });
127
+
128
+ // Listen for close events
129
+ connection.on('close', (code, reason) => {
130
+ console.log(`šŸ‘‹ User disconnected: ${connection.id}, code: ${code}, reason: ${reason}`);
131
+
132
+ // Leave all rooms when user disconnects
133
+ const userRooms = connection.rooms ? connection.rooms() : [];
134
+ userRooms.forEach(room => {
135
+ connection.leave(room);
136
+ broadcastToRoom(room, {
137
+ type: 'system',
138
+ message: `User ${connection.id.substring(0, 8)} left the room`,
139
+ timestamp: Date.now()
140
+ });
141
+ });
142
+ });
143
+
144
+ // Listen for error events
145
+ connection.on('error', (error) => {
146
+ console.error(`āš ļø Connection error for ${connection.id}:`, error);
147
+ });
148
+ });
149
+
150
+ // Listen for server error events
151
+ factory.on('error', (error) => {
152
+ console.error('šŸ’„ Server error:', error);
153
+ });
154
+
155
+ // Create server
156
+ console.log('šŸ”„ Creating server...');
157
+ const server = await factory.createServer();
158
+ console.log('āœ… Server instance created:', server);
159
+
160
+ // Fallback: If server:listening event doesn't fire within 500ms, show server info manually
161
+ setTimeout(() => {
162
+ if (!serverListeningEventFired) {
163
+ console.log('\nāš ļø Server:listening event was not fired, checking server status manually...');
164
+
165
+ // Try to get server address directly
166
+ if (server && server.address) {
167
+ const address = typeof server.address === 'function' ? server.address() : server.address;
168
+ if (address && address.port) {
169
+ console.log(`šŸŽ‰ ============================================`);
170
+ console.log(`šŸš€ Chat server listening on ws://${address.address || '0.0.0.0'}:${address.port}`);
171
+ console.log(`šŸ’¬ Room-based chat server ready for connections`);
172
+ console.log(`šŸ›‘ Press Ctrl+C to stop the server`);
173
+ console.log(`============================================\n`);
174
+ } else {
175
+ console.log(`🌐 Server should be available at: ws://${process.env.WS_HOST || '0.0.0.0'}:${process.env.WS_PORT || 3000}`);
176
+ }
177
+ } else {
178
+ console.log(`🌐 Server should be available at: ws://${process.env.WS_HOST || '0.0.0.0'}:${process.env.WS_PORT || 3000}`);
179
+ }
180
+ }
181
+ }, 500);
182
+
183
+ // Display connection instructions
184
+ console.log('\nšŸ“‹ How to connect:');
185
+ console.log('1. Open browser developer tools (F12)');
186
+ console.log('2. In Console tab, run:');
187
+ console.log(`
188
+ const ws = new WebSocket('ws://localhost:${process.env.WS_PORT || 3000}');
189
+ ws.onopen = () => {
190
+ console.log('Connected!');
191
+ ws.send(JSON.stringify({type:"join",room:"general"}));
192
+ ws.send(JSON.stringify({type:"chat",room:"general",message:"Hello!"}));
193
+ };
194
+ ws.onmessage = (e) => console.log('Server:', JSON.parse(e.data));
195
+ `);
196
+
197
+ // Room handling functions
198
+ function handleJoinRoom(connection, message) {
199
+ if (!message.room) {
200
+ connection.send(JSON.stringify({
201
+ type: 'error',
202
+ message: 'Room name is required',
203
+ example: '{"type":"join","room":"general"}'
204
+ }));
205
+ return;
206
+ }
207
+
208
+ const roomName = message.room.trim();
209
+ if (roomName.length === 0) {
210
+ connection.send(JSON.stringify({
211
+ type: 'error',
212
+ message: 'Room name cannot be empty'
213
+ }));
214
+ return;
215
+ }
216
+
217
+ // Join room
218
+ connection.join(roomName);
219
+
220
+ console.log(`šŸ“„ ${connection.id} joined room: ${roomName}`);
221
+
222
+ // Send confirmation message
223
+ connection.send(JSON.stringify({
224
+ type: 'system',
225
+ message: `Joined room: ${roomName}`,
226
+ room: roomName,
227
+ timestamp: Date.now()
228
+ }));
229
+
230
+ // Broadcast to other users in the room
231
+ broadcastToRoom(roomName, {
232
+ type: 'system',
233
+ message: `New user ${connection.id.substring(0, 8)} joined the room`,
234
+ userCount: getRoomUserCount(roomName),
235
+ timestamp: Date.now()
236
+ }, connection.id); // Exclude self
237
+ }
238
+
239
+ function handleLeaveRoom(connection, message) {
240
+ if (!message.room) {
241
+ connection.send(JSON.stringify({
242
+ type: 'error',
243
+ message: 'Room name is required',
244
+ example: '{"type":"leave","room":"general"}'
245
+ }));
246
+ return;
247
+ }
248
+
249
+ const roomName = message.room.trim();
250
+ connection.leave(roomName);
251
+
252
+ console.log(`šŸ“¤ ${connection.id} left room: ${roomName}`);
253
+
254
+ connection.send(JSON.stringify({
255
+ type: 'system',
256
+ message: `Left room: ${roomName}`,
257
+ room: roomName,
258
+ timestamp: Date.now()
259
+ }));
260
+
261
+ // Notify other users in the room
262
+ broadcastToRoom(roomName, {
263
+ type: 'system',
264
+ message: `User ${connection.id.substring(0, 8)} left the room`,
265
+ userCount: getRoomUserCount(roomName),
266
+ timestamp: Date.now()
267
+ });
268
+ }
269
+
270
+ function handleChatMessage(connection, message) {
271
+ if (!message.room || !message.message) {
272
+ connection.send(JSON.stringify({
273
+ type: 'error',
274
+ message: 'Room and message are required',
275
+ example: '{"type":"chat","room":"general","message":"Hello!"}'
276
+ }));
277
+ return;
278
+ }
279
+
280
+ const roomName = message.room.trim();
281
+ const chatMessage = message.message.trim();
282
+
283
+ // Check if user is in the room
284
+ const userRooms = connection.rooms ? connection.rooms() : [];
285
+ if (!userRooms.includes(roomName)) {
286
+ connection.send(JSON.stringify({
287
+ type: 'error',
288
+ message: `You are not in room: ${roomName}`,
289
+ suggestion: 'Send {"type":"join","room":"room-name"} first'
290
+ }));
291
+ return;
292
+ }
293
+
294
+ console.log(`šŸ’¬ ${connection.id} -> ${roomName}: ${chatMessage}`);
295
+
296
+ // Broadcast message to room
297
+ broadcastToRoom(roomName, {
298
+ type: 'chat',
299
+ from: connection.id.substring(0, 8),
300
+ room: roomName,
301
+ message: chatMessage,
302
+ timestamp: Date.now()
303
+ }, connection.id); // Exclude sender
304
+
305
+ // Send confirmation to sender
306
+ connection.send(JSON.stringify({
307
+ type: 'sent',
308
+ room: roomName,
309
+ message: chatMessage,
310
+ timestamp: Date.now()
311
+ }));
312
+ }
313
+
314
+ function handleListRooms(connection) {
315
+ // Get all rooms (simplified implementation)
316
+ const roomInfo = {
317
+ type: 'rooms',
318
+ count: 0,
319
+ rooms: [],
320
+ timestamp: Date.now()
321
+ };
322
+
323
+ connection.send(JSON.stringify(roomInfo));
324
+ }
325
+
326
+ function handleListUsers(connection, message) {
327
+ if (!message.room) {
328
+ connection.send(JSON.stringify({
329
+ type: 'error',
330
+ message: 'Room name is required',
331
+ example: '{"type":"users","room":"general"}'
332
+ }));
333
+ return;
334
+ }
335
+
336
+ const roomName = message.room.trim();
337
+ const userInfo = {
338
+ type: 'users',
339
+ room: roomName,
340
+ count: 0,
341
+ users: [],
342
+ timestamp: Date.now()
343
+ };
344
+
345
+ connection.send(JSON.stringify(userInfo));
346
+ }
347
+
348
+ function broadcastToRoom(roomName, message, excludeConnectionId = null) {
349
+ // Simplified broadcast implementation
350
+ console.log(`šŸ“¢ Broadcasting to room ${roomName}:`, message);
351
+
352
+ // In a real implementation, use broadcast manager's room functionality
353
+ // For now, we'll use factory.broadcast with filtering
354
+ if (factory.broadcast) {
355
+ factory.broadcast(JSON.stringify(message), (conn) => {
356
+ // Exclude specific connection
357
+ if (excludeConnectionId && conn.id === excludeConnectionId) {
358
+ return false;
359
+ }
360
+
361
+ // Check if connection is in the room
362
+ const userRooms = conn.rooms ? conn.rooms() : [];
363
+ return userRooms.includes(roomName);
364
+ });
365
+ }
366
+ }
367
+
368
+ function getRoomUserCount(roomName) {
369
+ // Simplified implementation
370
+ return 1;
371
+ }
372
+
373
+ // Graceful shutdown handling
374
+ process.on('SIGINT', async () => {
375
+ console.log('\nšŸ”„ Shutting down chat server...');
376
+ try {
377
+ // Notify all users about server shutdown
378
+ if (factory.broadcast) {
379
+ factory.broadcast(JSON.stringify({
380
+ type: 'system',
381
+ message: 'Server is shutting down. Goodbye!',
382
+ timestamp: Date.now()
383
+ }));
384
+ }
385
+
386
+ await factory.close();
387
+ console.log('āœ… Chat server closed successfully');
388
+ process.exit(0);
389
+ } catch (error) {
390
+ console.error('āŒ Error closing server:', error);
391
+ process.exit(1);
392
+ }
393
+ });
394
+
395
+ process.on('SIGTERM', async () => {
396
+ console.log('\nšŸ”„ Received SIGTERM, shutting down...');
397
+ await factory.close();
398
+ process.exit(0);
399
+ });
400
+
401
+ // Keep the process running
402
+ console.log('ā³ Server is running and waiting for connections...');
403
+
404
+ } catch (error) {
405
+ console.error('āŒ Failed to start chat server:', error);
406
+ console.error('šŸ’” Possible solutions:');
407
+ console.error('1. Check if port 3000 is already in use');
408
+ console.error('2. Try running as administrator (if using port < 1024)');
409
+ console.error('3. Check firewall settings');
410
+ console.error('4. Verify WebSocketFactory implementation');
411
+ process.exit(1);
412
+ }
413
+ }
414
+
415
+ // Start the chat server
416
+ main();
@@ -0,0 +1,58 @@
1
+ /**
2
+ * @file client-test.js - Native WebSocket Stress Test Client
3
+ * @description Uses Node.js built-in WebSocket (no 'ws' package required)
4
+ */
5
+
6
+ // No import needed! Node.js 18+ has global WebSocket
7
+ const URL = 'ws://localhost:8081';
8
+
9
+ console.log('šŸš€ Starting stress test client using native WebSocket...');
10
+
11
+ const ws = new WebSocket(URL);
12
+
13
+ ws.onopen = () => {
14
+ console.log('āœ… Connected to server!');
15
+
16
+ // 1. Request initial stats
17
+ ws.send(JSON.stringify({ type: 'stats_request' }));
18
+
19
+ // 2. Start stress test sequence
20
+ // Sending 1000 messages with 512 bytes payload, no delay
21
+ console.log('šŸ“¤ Sending stress test command (1000 iterations)...');
22
+ ws.send(JSON.stringify({
23
+ type: 'stress_test',
24
+ iterations: 1000,
25
+ delay: 0,
26
+ payloadSize: 512
27
+ }));
28
+ };
29
+
30
+ ws.onmessage = (event) => {
31
+ const data = JSON.parse(event.data);
32
+
33
+ if (data.type === 'stats_response') {
34
+ console.log('šŸ“Š Server Stats Received:', {
35
+ activeConnections: data.stats.connections.active,
36
+ totalMessages: data.stats.messages.received
37
+ });
38
+ }
39
+ else if (data.type === 'stress_test_complete') {
40
+ console.log('\nšŸ ===== STRESS TEST COMPLETE =====');
41
+ console.log(`⚔ Speed: ${data.messagesPerSecond} msgs/sec`);
42
+ console.log(`ā±ļø Duration: ${data.duration} ms`);
43
+ console.log(`šŸ“¦ Total Iterations: ${data.iterations}`);
44
+ console.log('==================================\n');
45
+
46
+ // Close connection after test
47
+ ws.close();
48
+ }
49
+ };
50
+
51
+ ws.onerror = (error) => {
52
+ console.error('āŒ WebSocket Error:', error.message);
53
+ };
54
+
55
+ ws.onclose = () => {
56
+ console.log('šŸ”Œ Connection closed.');
57
+ process.exit(0);
58
+ };
@@ -0,0 +1,98 @@
1
+ /**
2
+ * @file Realtime API Example with Auth and Rate Limiting
3
+ */
4
+
5
+ import { WebSocketFactory } from '../index.js';
6
+ import AuthMiddleware from '../src/middleware/auth-middleware.js';
7
+ import RateLimiter from '../src/middleware/rate-limiter.js';
8
+
9
+ async function main() {
10
+ // Create WebSocket factory instance with HTTP driver
11
+ const factory = new WebSocketFactory({
12
+ driver: 'http',
13
+ port: 4000
14
+ });
15
+
16
+ // Create authentication middleware instance
17
+ const auth = new AuthMiddleware({
18
+ verify: async (token) => {
19
+ // Mock token verification logic
20
+ // In production, replace with actual JWT or OAuth verification
21
+ return token === 'secret-token' ? { userId: 'user_123' } : null;
22
+ }
23
+ });
24
+
25
+ // Create rate limiter middleware instance
26
+ const limiter = new RateLimiter({
27
+ windowMs: 10000, // Time window in milliseconds (10 seconds)
28
+ max: 50 // Maximum requests per window
29
+ });
30
+
31
+ // Create WebSocket server
32
+ const server = await factory.createServer();
33
+
34
+ // Listen for connection events
35
+ factory.on('connection', (ws) => {
36
+ // Chain middlewares: auth -> rate limiter -> connection handler
37
+ auth.middleware()(ws, () => {
38
+ limiter.middleware()(ws, () => {
39
+ console.log(`Authenticated user connected: ${ws.user?.userId}`);
40
+
41
+ // Listen for message events
42
+ ws.on('message', (data) => {
43
+ // Echo back with status confirmation
44
+ ws.send(JSON.stringify({
45
+ status: 'ok',
46
+ received: data,
47
+ timestamp: Date.now()
48
+ }));
49
+ });
50
+
51
+ // Listen for close events
52
+ ws.on('close', (code, reason) => {
53
+ console.log(`User ${ws.user?.userId} disconnected: ${code} - ${reason}`);
54
+ });
55
+
56
+ // Listen for error events
57
+ ws.on('error', (error) => {
58
+ console.error(`Connection error for user ${ws.user?.userId}:`, error);
59
+ });
60
+ });
61
+ });
62
+ });
63
+
64
+ // Listen for server listening event
65
+ factory.on('server:listening', (address) => {
66
+ console.log(`Secure Realtime API listening on ws://${address.host}:${address.port}`);
67
+ console.log('šŸ”’ Authentication required: Send token in connection header');
68
+ console.log('ā±ļø Rate limiting: 50 requests per 10 seconds');
69
+ console.log('šŸ›‘ Press Ctrl+C to stop the server');
70
+ });
71
+
72
+ // Listen for server error events
73
+ factory.on('error', (error) => {
74
+ console.error('Server error:', error);
75
+ });
76
+
77
+ // Graceful shutdown handling
78
+ process.on('SIGINT', async () => {
79
+ console.log('\nShutting down Realtime API server...');
80
+ try {
81
+ await factory.close();
82
+ console.log('Server closed successfully');
83
+ process.exit(0);
84
+ } catch (error) {
85
+ console.error('Error closing server:', error);
86
+ process.exit(1);
87
+ }
88
+ });
89
+
90
+ process.on('SIGTERM', async () => {
91
+ console.log('\nReceived SIGTERM, shutting down...');
92
+ await factory.close();
93
+ process.exit(0);
94
+ });
95
+ }
96
+
97
+ // Start the server
98
+ main().catch(console.error);