@hazeljs/websocket 0.2.0-beta.1
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/README.md +530 -0
- package/dist/decorators/realtime.decorator.d.ts +113 -0
- package/dist/decorators/realtime.decorator.d.ts.map +1 -0
- package/dist/decorators/realtime.decorator.js +202 -0
- package/dist/index.d.ts +10 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +32 -0
- package/dist/room/room.manager.d.ts +81 -0
- package/dist/room/room.manager.d.ts.map +1 -0
- package/dist/room/room.manager.js +209 -0
- package/dist/src/decorators/realtime.decorator.d.ts +113 -0
- package/dist/src/decorators/realtime.decorator.d.ts.map +1 -0
- package/dist/src/decorators/realtime.decorator.js +202 -0
- package/dist/src/index.d.ts +10 -0
- package/dist/src/index.d.ts.map +1 -0
- package/dist/src/index.js +32 -0
- package/dist/src/room/room.manager.d.ts +81 -0
- package/dist/src/room/room.manager.d.ts.map +1 -0
- package/dist/src/room/room.manager.js +209 -0
- package/dist/src/sse/sse.handler.d.ts +61 -0
- package/dist/src/sse/sse.handler.d.ts.map +1 -0
- package/dist/src/sse/sse.handler.js +209 -0
- package/dist/src/websocket.gateway.d.ts +94 -0
- package/dist/src/websocket.gateway.d.ts.map +1 -0
- package/dist/src/websocket.gateway.js +309 -0
- package/dist/src/websocket.module.d.ts +57 -0
- package/dist/src/websocket.module.d.ts.map +1 -0
- package/dist/src/websocket.module.js +88 -0
- package/dist/src/websocket.types.d.ts +258 -0
- package/dist/src/websocket.types.d.ts.map +1 -0
- package/dist/src/websocket.types.js +2 -0
- package/dist/sse/sse.handler.d.ts +61 -0
- package/dist/sse/sse.handler.d.ts.map +1 -0
- package/dist/sse/sse.handler.js +209 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/websocket.gateway.d.ts +79 -0
- package/dist/websocket.gateway.d.ts.map +1 -0
- package/dist/websocket.gateway.js +214 -0
- package/dist/websocket.module.d.ts +57 -0
- package/dist/websocket.module.d.ts.map +1 -0
- package/dist/websocket.module.js +88 -0
- package/dist/websocket.types.d.ts +258 -0
- package/dist/websocket.types.d.ts.map +1 -0
- package/dist/websocket.types.js +2 -0
- package/package.json +48 -0
package/README.md
ADDED
|
@@ -0,0 +1,530 @@
|
|
|
1
|
+
# @hazeljs/websocket
|
|
2
|
+
|
|
3
|
+
**WebSocket and Server-Sent Events Module for HazelJS**
|
|
4
|
+
|
|
5
|
+
Build real-time applications with WebSocket support, room management, and event-driven architecture.
|
|
6
|
+
|
|
7
|
+
[](https://www.npmjs.com/package/@hazeljs/websocket)
|
|
8
|
+
[](https://opensource.org/licenses/MIT)
|
|
9
|
+
|
|
10
|
+
## Features
|
|
11
|
+
|
|
12
|
+
- 🔌 **WebSocket Support** - Full-duplex real-time communication
|
|
13
|
+
- 🏠 **Room Management** - Group clients into rooms
|
|
14
|
+
- 📡 **Event-Driven** - Decorator-based event handling
|
|
15
|
+
- 🎨 **Decorator API** - `@WebSocketGateway`, `@OnMessage`, `@Subscribe`
|
|
16
|
+
- 🔐 **Authentication** - Integrate with auth guards
|
|
17
|
+
- 📊 **Broadcasting** - Send messages to multiple clients
|
|
18
|
+
- 🎯 **Namespaces** - Separate WebSocket endpoints
|
|
19
|
+
- 💾 **State Management** - Per-client state storage
|
|
20
|
+
|
|
21
|
+
## Installation
|
|
22
|
+
|
|
23
|
+
```bash
|
|
24
|
+
npm install @hazeljs/websocket
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Quick Start
|
|
28
|
+
|
|
29
|
+
### 1. Create WebSocket Gateway
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { Injectable } from '@hazeljs/core';
|
|
33
|
+
import {
|
|
34
|
+
WebSocketGateway,
|
|
35
|
+
OnConnect,
|
|
36
|
+
OnDisconnect,
|
|
37
|
+
OnMessage,
|
|
38
|
+
WebSocketClient,
|
|
39
|
+
Data,
|
|
40
|
+
} from '@hazeljs/websocket';
|
|
41
|
+
|
|
42
|
+
@Injectable()
|
|
43
|
+
@WebSocketGateway({ path: '/ws' })
|
|
44
|
+
export class ChatGateway {
|
|
45
|
+
@OnConnect()
|
|
46
|
+
handleConnection(client: WebSocketClient) {
|
|
47
|
+
console.log('Client connected:', client.id);
|
|
48
|
+
client.emit('welcome', { message: 'Welcome to the chat!' });
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
@OnDisconnect()
|
|
52
|
+
handleDisconnect(client: WebSocketClient) {
|
|
53
|
+
console.log('Client disconnected:', client.id);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
@OnMessage('message')
|
|
57
|
+
handleMessage(client: WebSocketClient, @Data() data: { text: string }) {
|
|
58
|
+
console.log('Received message:', data.text);
|
|
59
|
+
|
|
60
|
+
// Broadcast to all clients
|
|
61
|
+
client.broadcast('message', {
|
|
62
|
+
from: client.id,
|
|
63
|
+
text: data.text,
|
|
64
|
+
timestamp: new Date(),
|
|
65
|
+
});
|
|
66
|
+
}
|
|
67
|
+
}
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
### 2. Register Gateway
|
|
71
|
+
|
|
72
|
+
```typescript
|
|
73
|
+
import { HazelModule } from '@hazeljs/core';
|
|
74
|
+
import { WebSocketModule } from '@hazeljs/websocket';
|
|
75
|
+
import { ChatGateway } from './chat.gateway';
|
|
76
|
+
|
|
77
|
+
@HazelModule({
|
|
78
|
+
imports: [WebSocketModule],
|
|
79
|
+
providers: [ChatGateway],
|
|
80
|
+
})
|
|
81
|
+
export class AppModule {}
|
|
82
|
+
```
|
|
83
|
+
|
|
84
|
+
### 3. Connect from Client
|
|
85
|
+
|
|
86
|
+
```javascript
|
|
87
|
+
// Browser client
|
|
88
|
+
const ws = new WebSocket('ws://localhost:3000/ws');
|
|
89
|
+
|
|
90
|
+
ws.onopen = () => {
|
|
91
|
+
console.log('Connected');
|
|
92
|
+
ws.send(JSON.stringify({ event: 'message', data: { text: 'Hello!' } }));
|
|
93
|
+
};
|
|
94
|
+
|
|
95
|
+
ws.onmessage = (event) => {
|
|
96
|
+
const message = JSON.parse(event.data);
|
|
97
|
+
console.log('Received:', message);
|
|
98
|
+
};
|
|
99
|
+
```
|
|
100
|
+
|
|
101
|
+
## Decorators
|
|
102
|
+
|
|
103
|
+
### @WebSocketGateway()
|
|
104
|
+
|
|
105
|
+
Define a WebSocket gateway:
|
|
106
|
+
|
|
107
|
+
```typescript
|
|
108
|
+
@WebSocketGateway({
|
|
109
|
+
path: '/chat',
|
|
110
|
+
namespace: '/chat',
|
|
111
|
+
cors: {
|
|
112
|
+
origin: '*',
|
|
113
|
+
credentials: true,
|
|
114
|
+
},
|
|
115
|
+
})
|
|
116
|
+
export class ChatGateway {}
|
|
117
|
+
```
|
|
118
|
+
|
|
119
|
+
### @OnConnect()
|
|
120
|
+
|
|
121
|
+
Handle client connections:
|
|
122
|
+
|
|
123
|
+
```typescript
|
|
124
|
+
@OnConnect()
|
|
125
|
+
handleConnection(client: WebSocketClient) {
|
|
126
|
+
console.log('New client:', client.id);
|
|
127
|
+
|
|
128
|
+
// Send welcome message
|
|
129
|
+
client.emit('welcome', { message: 'Hello!' });
|
|
130
|
+
|
|
131
|
+
// Store client data
|
|
132
|
+
client.data.username = 'Guest';
|
|
133
|
+
}
|
|
134
|
+
```
|
|
135
|
+
|
|
136
|
+
### @OnDisconnect()
|
|
137
|
+
|
|
138
|
+
Handle client disconnections:
|
|
139
|
+
|
|
140
|
+
```typescript
|
|
141
|
+
@OnDisconnect()
|
|
142
|
+
handleDisconnect(client: WebSocketClient) {
|
|
143
|
+
console.log('Client left:', client.id);
|
|
144
|
+
|
|
145
|
+
// Notify others
|
|
146
|
+
client.broadcast('user-left', {
|
|
147
|
+
userId: client.id,
|
|
148
|
+
username: client.data.username,
|
|
149
|
+
});
|
|
150
|
+
}
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
### @OnMessage()
|
|
154
|
+
|
|
155
|
+
Handle specific messages:
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
@OnMessage('chat-message')
|
|
159
|
+
handleChatMessage(client: WebSocketClient, @Data() data: ChatMessage) {
|
|
160
|
+
console.log('Chat message from', client.id, ':', data.text);
|
|
161
|
+
|
|
162
|
+
// Broadcast to all
|
|
163
|
+
client.broadcast('chat-message', {
|
|
164
|
+
from: client.data.username,
|
|
165
|
+
text: data.text,
|
|
166
|
+
timestamp: new Date(),
|
|
167
|
+
});
|
|
168
|
+
}
|
|
169
|
+
```
|
|
170
|
+
|
|
171
|
+
### @Subscribe()
|
|
172
|
+
|
|
173
|
+
Subscribe to events:
|
|
174
|
+
|
|
175
|
+
```typescript
|
|
176
|
+
@Subscribe('join-room')
|
|
177
|
+
handleJoinRoom(client: WebSocketClient, @Data() data: { room: string }) {
|
|
178
|
+
client.join(data.room);
|
|
179
|
+
|
|
180
|
+
// Notify room members
|
|
181
|
+
client.to(data.room).emit('user-joined', {
|
|
182
|
+
userId: client.id,
|
|
183
|
+
username: client.data.username,
|
|
184
|
+
});
|
|
185
|
+
}
|
|
186
|
+
```
|
|
187
|
+
|
|
188
|
+
## Room Management
|
|
189
|
+
|
|
190
|
+
### Join Room
|
|
191
|
+
|
|
192
|
+
```typescript
|
|
193
|
+
@Subscribe('join-room')
|
|
194
|
+
handleJoinRoom(client: WebSocketClient, @Data() data: { room: string }) {
|
|
195
|
+
client.join(data.room);
|
|
196
|
+
client.emit('joined', { room: data.room });
|
|
197
|
+
}
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
### Leave Room
|
|
201
|
+
|
|
202
|
+
```typescript
|
|
203
|
+
@Subscribe('leave-room')
|
|
204
|
+
handleLeaveRoom(client: WebSocketClient, @Data() data: { room: string }) {
|
|
205
|
+
client.leave(data.room);
|
|
206
|
+
client.emit('left', { room: data.room });
|
|
207
|
+
}
|
|
208
|
+
```
|
|
209
|
+
|
|
210
|
+
### Send to Room
|
|
211
|
+
|
|
212
|
+
```typescript
|
|
213
|
+
@Subscribe('room-message')
|
|
214
|
+
handleRoomMessage(
|
|
215
|
+
client: WebSocketClient,
|
|
216
|
+
@Data() data: { room: string; text: string }
|
|
217
|
+
) {
|
|
218
|
+
// Send to all clients in room except sender
|
|
219
|
+
client.to(data.room).emit('room-message', {
|
|
220
|
+
from: client.id,
|
|
221
|
+
text: data.text,
|
|
222
|
+
});
|
|
223
|
+
}
|
|
224
|
+
```
|
|
225
|
+
|
|
226
|
+
### Broadcast to Room
|
|
227
|
+
|
|
228
|
+
```typescript
|
|
229
|
+
@Subscribe('room-announcement')
|
|
230
|
+
handleAnnouncement(
|
|
231
|
+
client: WebSocketClient,
|
|
232
|
+
@Data() data: { room: string; text: string }
|
|
233
|
+
) {
|
|
234
|
+
// Send to all clients in room including sender
|
|
235
|
+
client.in(data.room).emit('announcement', {
|
|
236
|
+
text: data.text,
|
|
237
|
+
});
|
|
238
|
+
}
|
|
239
|
+
```
|
|
240
|
+
|
|
241
|
+
## Broadcasting
|
|
242
|
+
|
|
243
|
+
### Broadcast to All
|
|
244
|
+
|
|
245
|
+
```typescript
|
|
246
|
+
@OnMessage('global-message')
|
|
247
|
+
handleGlobalMessage(client: WebSocketClient, @Data() data: any) {
|
|
248
|
+
// Send to all connected clients except sender
|
|
249
|
+
client.broadcast('global-message', data);
|
|
250
|
+
}
|
|
251
|
+
```
|
|
252
|
+
|
|
253
|
+
### Emit to All
|
|
254
|
+
|
|
255
|
+
```typescript
|
|
256
|
+
@OnMessage('system-message')
|
|
257
|
+
handleSystemMessage(client: WebSocketClient, @Data() data: any) {
|
|
258
|
+
// Send to all connected clients including sender
|
|
259
|
+
this.server.emit('system-message', data);
|
|
260
|
+
}
|
|
261
|
+
```
|
|
262
|
+
|
|
263
|
+
### Emit to Specific Client
|
|
264
|
+
|
|
265
|
+
```typescript
|
|
266
|
+
@Subscribe('private-message')
|
|
267
|
+
handlePrivateMessage(
|
|
268
|
+
client: WebSocketClient,
|
|
269
|
+
@Data() data: { to: string; text: string }
|
|
270
|
+
) {
|
|
271
|
+
const targetClient = this.server.getClient(data.to);
|
|
272
|
+
|
|
273
|
+
if (targetClient) {
|
|
274
|
+
targetClient.emit('private-message', {
|
|
275
|
+
from: client.id,
|
|
276
|
+
text: data.text,
|
|
277
|
+
});
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
```
|
|
281
|
+
|
|
282
|
+
## Authentication
|
|
283
|
+
|
|
284
|
+
### With Guards
|
|
285
|
+
|
|
286
|
+
```typescript
|
|
287
|
+
import { UseGuard } from '@hazeljs/core';
|
|
288
|
+
import { AuthGuard } from '@hazeljs/auth';
|
|
289
|
+
|
|
290
|
+
@WebSocketGateway({ path: '/secure' })
|
|
291
|
+
@UseGuard(AuthGuard)
|
|
292
|
+
export class SecureGateway {
|
|
293
|
+
@OnConnect()
|
|
294
|
+
handleConnection(client: WebSocketClient) {
|
|
295
|
+
// Only authenticated clients reach here
|
|
296
|
+
console.log('Authenticated user:', client.data.user);
|
|
297
|
+
}
|
|
298
|
+
}
|
|
299
|
+
```
|
|
300
|
+
|
|
301
|
+
### Custom Authentication
|
|
302
|
+
|
|
303
|
+
```typescript
|
|
304
|
+
@WebSocketGateway({ path: '/chat' })
|
|
305
|
+
export class ChatGateway {
|
|
306
|
+
@OnConnect()
|
|
307
|
+
async handleConnection(client: WebSocketClient) {
|
|
308
|
+
const token = client.handshake.query.token;
|
|
309
|
+
|
|
310
|
+
try {
|
|
311
|
+
const user = await this.authService.verifyToken(token);
|
|
312
|
+
client.data.user = user;
|
|
313
|
+
client.emit('authenticated', { user });
|
|
314
|
+
} catch (error) {
|
|
315
|
+
client.disconnect();
|
|
316
|
+
}
|
|
317
|
+
}
|
|
318
|
+
}
|
|
319
|
+
```
|
|
320
|
+
|
|
321
|
+
## Complete Example: Chat Application
|
|
322
|
+
|
|
323
|
+
```typescript
|
|
324
|
+
import { Injectable } from '@hazeljs/core';
|
|
325
|
+
import {
|
|
326
|
+
WebSocketGateway,
|
|
327
|
+
OnConnect,
|
|
328
|
+
OnDisconnect,
|
|
329
|
+
Subscribe,
|
|
330
|
+
WebSocketClient,
|
|
331
|
+
Data,
|
|
332
|
+
WebSocketServer,
|
|
333
|
+
} from '@hazeljs/websocket';
|
|
334
|
+
|
|
335
|
+
interface ChatMessage {
|
|
336
|
+
text: string;
|
|
337
|
+
room?: string;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
interface JoinRoomData {
|
|
341
|
+
room: string;
|
|
342
|
+
username: string;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
@Injectable()
|
|
346
|
+
@WebSocketGateway({ path: '/chat' })
|
|
347
|
+
export class ChatGateway {
|
|
348
|
+
@WebSocketServer()
|
|
349
|
+
server: WebSocketServer;
|
|
350
|
+
|
|
351
|
+
@OnConnect()
|
|
352
|
+
handleConnection(client: WebSocketClient) {
|
|
353
|
+
console.log(`Client connected: ${client.id}`);
|
|
354
|
+
|
|
355
|
+
client.emit('connected', {
|
|
356
|
+
clientId: client.id,
|
|
357
|
+
message: 'Welcome to the chat!',
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
|
|
361
|
+
@OnDisconnect()
|
|
362
|
+
handleDisconnect(client: WebSocketClient) {
|
|
363
|
+
console.log(`Client disconnected: ${client.id}`);
|
|
364
|
+
|
|
365
|
+
// Notify rooms
|
|
366
|
+
const rooms = client.rooms;
|
|
367
|
+
rooms.forEach(room => {
|
|
368
|
+
client.to(room).emit('user-left', {
|
|
369
|
+
userId: client.id,
|
|
370
|
+
username: client.data.username,
|
|
371
|
+
});
|
|
372
|
+
});
|
|
373
|
+
}
|
|
374
|
+
|
|
375
|
+
@Subscribe('join-room')
|
|
376
|
+
handleJoinRoom(client: WebSocketClient, @Data() data: JoinRoomData) {
|
|
377
|
+
client.join(data.room);
|
|
378
|
+
client.data.username = data.username;
|
|
379
|
+
|
|
380
|
+
// Notify room members
|
|
381
|
+
client.to(data.room).emit('user-joined', {
|
|
382
|
+
userId: client.id,
|
|
383
|
+
username: data.username,
|
|
384
|
+
room: data.room,
|
|
385
|
+
});
|
|
386
|
+
|
|
387
|
+
// Confirm to sender
|
|
388
|
+
client.emit('joined-room', {
|
|
389
|
+
room: data.room,
|
|
390
|
+
members: this.getRoomMembers(data.room),
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
@Subscribe('leave-room')
|
|
395
|
+
handleLeaveRoom(client: WebSocketClient, @Data() data: { room: string }) {
|
|
396
|
+
client.leave(data.room);
|
|
397
|
+
|
|
398
|
+
// Notify room
|
|
399
|
+
client.to(data.room).emit('user-left', {
|
|
400
|
+
userId: client.id,
|
|
401
|
+
username: client.data.username,
|
|
402
|
+
});
|
|
403
|
+
}
|
|
404
|
+
|
|
405
|
+
@Subscribe('chat-message')
|
|
406
|
+
handleMessage(client: WebSocketClient, @Data() data: ChatMessage) {
|
|
407
|
+
const message = {
|
|
408
|
+
from: client.id,
|
|
409
|
+
username: client.data.username,
|
|
410
|
+
text: data.text,
|
|
411
|
+
timestamp: new Date(),
|
|
412
|
+
};
|
|
413
|
+
|
|
414
|
+
if (data.room) {
|
|
415
|
+
// Send to specific room
|
|
416
|
+
client.to(data.room).emit('chat-message', message);
|
|
417
|
+
} else {
|
|
418
|
+
// Broadcast to all
|
|
419
|
+
client.broadcast('chat-message', message);
|
|
420
|
+
}
|
|
421
|
+
}
|
|
422
|
+
|
|
423
|
+
@Subscribe('typing')
|
|
424
|
+
handleTyping(client: WebSocketClient, @Data() data: { room?: string }) {
|
|
425
|
+
const typingData = {
|
|
426
|
+
userId: client.id,
|
|
427
|
+
username: client.data.username,
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
if (data.room) {
|
|
431
|
+
client.to(data.room).emit('typing', typingData);
|
|
432
|
+
} else {
|
|
433
|
+
client.broadcast('typing', typingData);
|
|
434
|
+
}
|
|
435
|
+
}
|
|
436
|
+
|
|
437
|
+
private getRoomMembers(room: string): any[] {
|
|
438
|
+
const clients = this.server.getClientsInRoom(room);
|
|
439
|
+
return clients.map(c => ({
|
|
440
|
+
id: c.id,
|
|
441
|
+
username: c.data.username,
|
|
442
|
+
}));
|
|
443
|
+
}
|
|
444
|
+
}
|
|
445
|
+
```
|
|
446
|
+
|
|
447
|
+
## Client State
|
|
448
|
+
|
|
449
|
+
Store per-client data:
|
|
450
|
+
|
|
451
|
+
```typescript
|
|
452
|
+
@OnConnect()
|
|
453
|
+
handleConnection(client: WebSocketClient) {
|
|
454
|
+
// Store custom data
|
|
455
|
+
client.data.username = 'Guest';
|
|
456
|
+
client.data.score = 0;
|
|
457
|
+
client.data.joinedAt = new Date();
|
|
458
|
+
}
|
|
459
|
+
|
|
460
|
+
@Subscribe('update-profile')
|
|
461
|
+
handleUpdateProfile(client: WebSocketClient, @Data() data: any) {
|
|
462
|
+
client.data.username = data.username;
|
|
463
|
+
client.data.avatar = data.avatar;
|
|
464
|
+
}
|
|
465
|
+
```
|
|
466
|
+
|
|
467
|
+
## Server-Sent Events (SSE)
|
|
468
|
+
|
|
469
|
+
```typescript
|
|
470
|
+
import { Controller, Get, Res } from '@hazeljs/core';
|
|
471
|
+
import { Response } from 'express';
|
|
472
|
+
|
|
473
|
+
@Controller('/events')
|
|
474
|
+
export class EventsController {
|
|
475
|
+
@Get('/stream')
|
|
476
|
+
streamEvents(@Res() res: Response) {
|
|
477
|
+
res.setHeader('Content-Type', 'text/event-stream');
|
|
478
|
+
res.setHeader('Cache-Control', 'no-cache');
|
|
479
|
+
res.setHeader('Connection', 'keep-alive');
|
|
480
|
+
|
|
481
|
+
// Send event every second
|
|
482
|
+
const interval = setInterval(() => {
|
|
483
|
+
res.write(`data: ${JSON.stringify({ time: new Date() })}\n\n`);
|
|
484
|
+
}, 1000);
|
|
485
|
+
|
|
486
|
+
// Cleanup on close
|
|
487
|
+
res.on('close', () => {
|
|
488
|
+
clearInterval(interval);
|
|
489
|
+
res.end();
|
|
490
|
+
});
|
|
491
|
+
}
|
|
492
|
+
}
|
|
493
|
+
```
|
|
494
|
+
|
|
495
|
+
## Best Practices
|
|
496
|
+
|
|
497
|
+
1. **Handle Disconnections** - Always implement `@OnDisconnect()`
|
|
498
|
+
2. **Validate Messages** - Validate incoming data
|
|
499
|
+
3. **Use Rooms** - Group related clients
|
|
500
|
+
4. **Authentication** - Verify clients on connection
|
|
501
|
+
5. **Error Handling** - Catch and handle errors gracefully
|
|
502
|
+
6. **Rate Limiting** - Prevent message flooding
|
|
503
|
+
7. **Heartbeat** - Implement ping/pong for connection health
|
|
504
|
+
8. **Clean Up** - Remove client data on disconnect
|
|
505
|
+
|
|
506
|
+
## Examples
|
|
507
|
+
|
|
508
|
+
See the [examples](../../example/src/websocket) directory for complete working examples.
|
|
509
|
+
|
|
510
|
+
## Testing
|
|
511
|
+
|
|
512
|
+
```bash
|
|
513
|
+
npm test
|
|
514
|
+
```
|
|
515
|
+
|
|
516
|
+
## Contributing
|
|
517
|
+
|
|
518
|
+
Contributions are welcome! Please read our [Contributing Guide](../../CONTRIBUTING.md) for details.
|
|
519
|
+
|
|
520
|
+
## License
|
|
521
|
+
|
|
522
|
+
MIT © [HazelJS](https://hazeljs.com)
|
|
523
|
+
|
|
524
|
+
## Links
|
|
525
|
+
|
|
526
|
+
- [Documentation](https://hazeljs.com/docs/packages/websocket)
|
|
527
|
+
- [WebSocket API](https://developer.mozilla.org/en-US/docs/Web/API/WebSocket)
|
|
528
|
+
- [GitHub](https://github.com/hazel-js/hazeljs)
|
|
529
|
+
- [Issues](https://github.com/hazeljs/hazel-js/issues)
|
|
530
|
+
- [Discord](https://discord.gg/hazeljs)
|
|
@@ -0,0 +1,113 @@
|
|
|
1
|
+
import 'reflect-metadata';
|
|
2
|
+
import { WebSocketGatewayOptions } from '../websocket.types';
|
|
3
|
+
/**
|
|
4
|
+
* Realtime decorator for WebSocket gateways
|
|
5
|
+
*
|
|
6
|
+
* @example
|
|
7
|
+
* ```typescript
|
|
8
|
+
* @Realtime('/notifications')
|
|
9
|
+
* export class NotificationGateway {
|
|
10
|
+
* // Gateway methods
|
|
11
|
+
* }
|
|
12
|
+
* ```
|
|
13
|
+
*/
|
|
14
|
+
export declare function Realtime(pathOrOptions?: string | WebSocketGatewayOptions): ClassDecorator;
|
|
15
|
+
/**
|
|
16
|
+
* Get realtime metadata from a class
|
|
17
|
+
*/
|
|
18
|
+
export declare function getRealtimeMetadata(target: object | (new (...args: unknown[]) => object)): WebSocketGatewayOptions | undefined;
|
|
19
|
+
/**
|
|
20
|
+
* Check if a class is a realtime gateway
|
|
21
|
+
*/
|
|
22
|
+
export declare function isRealtimeGateway(target: object | (new (...args: unknown[]) => object)): boolean;
|
|
23
|
+
/**
|
|
24
|
+
* Subscribe decorator for event handlers
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* @Subscribe('user-{userId}')
|
|
29
|
+
* onUserEvent(@Param('userId') userId: string, @Data() data: any) {
|
|
30
|
+
* // Handle event
|
|
31
|
+
* }
|
|
32
|
+
* ```
|
|
33
|
+
*/
|
|
34
|
+
export declare function Subscribe(event: string): MethodDecorator;
|
|
35
|
+
/**
|
|
36
|
+
* Get subscribe metadata from a method
|
|
37
|
+
*/
|
|
38
|
+
export declare function getSubscribeMetadata(target: object, propertyKey: string | symbol): string | undefined;
|
|
39
|
+
/**
|
|
40
|
+
* OnConnect decorator for connection handlers
|
|
41
|
+
*
|
|
42
|
+
* @example
|
|
43
|
+
* ```typescript
|
|
44
|
+
* @OnConnect()
|
|
45
|
+
* handleConnection(@Client() client: WebSocketClient) {
|
|
46
|
+
* console.log('Client connected:', client.id);
|
|
47
|
+
* }
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
export declare function OnConnect(): MethodDecorator;
|
|
51
|
+
/**
|
|
52
|
+
* Get OnConnect metadata
|
|
53
|
+
*/
|
|
54
|
+
export declare function getOnConnectMetadata(target: object, propertyKey: string | symbol): boolean;
|
|
55
|
+
/**
|
|
56
|
+
* OnDisconnect decorator for disconnection handlers
|
|
57
|
+
*
|
|
58
|
+
* @example
|
|
59
|
+
* ```typescript
|
|
60
|
+
* @OnDisconnect()
|
|
61
|
+
* handleDisconnect(@Client() client: WebSocketClient) {
|
|
62
|
+
* console.log('Client disconnected:', client.id);
|
|
63
|
+
* }
|
|
64
|
+
* ```
|
|
65
|
+
*/
|
|
66
|
+
export declare function OnDisconnect(): MethodDecorator;
|
|
67
|
+
/**
|
|
68
|
+
* Get OnDisconnect metadata
|
|
69
|
+
*/
|
|
70
|
+
export declare function getOnDisconnectMetadata(target: object, propertyKey: string | symbol): boolean;
|
|
71
|
+
/**
|
|
72
|
+
* OnMessage decorator for message handlers
|
|
73
|
+
*
|
|
74
|
+
* @example
|
|
75
|
+
* ```typescript
|
|
76
|
+
* @OnMessage('chat')
|
|
77
|
+
* handleMessage(@Client() client: WebSocketClient, @Data() data: any) {
|
|
78
|
+
* // Handle message
|
|
79
|
+
* }
|
|
80
|
+
* ```
|
|
81
|
+
*/
|
|
82
|
+
export declare function OnMessage(event: string): MethodDecorator;
|
|
83
|
+
/**
|
|
84
|
+
* Get OnMessage metadata
|
|
85
|
+
*/
|
|
86
|
+
export declare function getOnMessageMetadata(target: object, propertyKey: string | symbol): string | undefined;
|
|
87
|
+
/**
|
|
88
|
+
* Client parameter decorator
|
|
89
|
+
*
|
|
90
|
+
* @example
|
|
91
|
+
* ```typescript
|
|
92
|
+
* handleConnection(@Client() client: WebSocketClient) {
|
|
93
|
+
* // client is injected
|
|
94
|
+
* }
|
|
95
|
+
* ```
|
|
96
|
+
*/
|
|
97
|
+
export declare function Client(): ParameterDecorator;
|
|
98
|
+
/**
|
|
99
|
+
* Data parameter decorator
|
|
100
|
+
*
|
|
101
|
+
* @example
|
|
102
|
+
* ```typescript
|
|
103
|
+
* handleMessage(@Data() data: any) {
|
|
104
|
+
* // data is injected
|
|
105
|
+
* }
|
|
106
|
+
* ```
|
|
107
|
+
*/
|
|
108
|
+
export declare function Data(): ParameterDecorator;
|
|
109
|
+
/**
|
|
110
|
+
* Get parameter metadata
|
|
111
|
+
*/
|
|
112
|
+
export declare function getParameterMetadata(target: object, propertyKey: string | symbol): string[];
|
|
113
|
+
//# sourceMappingURL=realtime.decorator.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"realtime.decorator.d.ts","sourceRoot":"","sources":["../../src/decorators/realtime.decorator.ts"],"names":[],"mappings":"AAAA,OAAO,kBAAkB,CAAC;AAC1B,OAAO,EAAE,uBAAuB,EAAE,MAAM,oBAAoB,CAAC;AAS7D;;;;;;;;;;GAUG;AACH,wBAAgB,QAAQ,CAAC,aAAa,CAAC,EAAE,MAAM,GAAG,uBAAuB,GAAG,cAAc,CAmBzF;AAED;;GAEG;AACH,wBAAgB,mBAAmB,CACjC,MAAM,EAAE,MAAM,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,MAAM,CAAC,GACpD,uBAAuB,GAAG,SAAS,CAErC;AAED;;GAEG;AACH,wBAAgB,iBAAiB,CAAC,MAAM,EAAE,MAAM,GAAG,CAAC,KAAK,GAAG,IAAI,EAAE,OAAO,EAAE,KAAK,MAAM,CAAC,GAAG,OAAO,CAEhG;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAQxD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC3B,MAAM,GAAG,SAAS,CAEpB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,IAAI,eAAe,CAQ3C;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAE1F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,YAAY,IAAI,eAAe,CAQ9C;AAED;;GAEG;AACH,wBAAgB,uBAAuB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,OAAO,CAE7F;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,SAAS,CAAC,KAAK,EAAE,MAAM,GAAG,eAAe,CAQxD;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAClC,MAAM,EAAE,MAAM,EACd,WAAW,EAAE,MAAM,GAAG,MAAM,GAC3B,MAAM,GAAG,SAAS,CAEpB;AAED;;;;;;;;;GASG;AACH,wBAAgB,MAAM,IAAI,kBAAkB,CAM3C;AAED;;;;;;;;;GASG;AACH,wBAAgB,IAAI,IAAI,kBAAkB,CAMzC;AAED;;GAEG;AACH,wBAAgB,oBAAoB,CAAC,MAAM,EAAE,MAAM,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,CAE3F"}
|