@eleven-am/pondsocket-nest 0.0.137 → 0.0.138

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.
Files changed (127) hide show
  1. package/README.md +141 -541
  2. package/dist/context/context.d.ts +41 -0
  3. package/dist/context/context.js +238 -0
  4. package/{decorators → dist/decorators}/onConnectionRequest.js +2 -2
  5. package/{decorators → dist/decorators}/onEvent.js +2 -2
  6. package/{decorators → dist/decorators}/onJoinRequest.js +2 -2
  7. package/{decorators → dist/decorators}/onLeave.js +3 -4
  8. package/{decorators → dist/decorators}/onOutgoingEvent.js +10 -2
  9. package/{helpers → dist/helpers}/createParamDecorator.js +4 -3
  10. package/{helpers → dist/helpers}/misc.d.ts +1 -1
  11. package/dist/helpers/misc.js +27 -0
  12. package/dist/index.d.ts +6 -0
  13. package/{index.js → dist/index.js} +1 -0
  14. package/dist/managers/outgoingEvent.d.ts +5 -0
  15. package/{modules → dist/modules}/pondSocket.js +4 -5
  16. package/dist/performers/action.d.ts +8 -0
  17. package/dist/performers/action.js +121 -0
  18. package/dist/performers/errors.d.ts +4 -0
  19. package/dist/performers/errors.js +43 -0
  20. package/dist/performers/response.d.ts +5 -0
  21. package/dist/performers/response.js +65 -0
  22. package/{services → dist/services}/pondSocket.d.ts +12 -4
  23. package/dist/services/pondSocket.js +288 -0
  24. package/dist/types.d.ts +113 -0
  25. package/package.json +57 -46
  26. package/context/context.d.ts +0 -27
  27. package/context/context.js +0 -103
  28. package/helpers/misc.js +0 -13
  29. package/index.d.ts +0 -5
  30. package/managers/outgoingEvent.d.ts +0 -5
  31. package/performers/action.d.ts +0 -5
  32. package/performers/action.js +0 -89
  33. package/performers/errors.d.ts +0 -2
  34. package/performers/errors.js +0 -37
  35. package/performers/narrow.d.ts +0 -7
  36. package/performers/narrow.js +0 -21
  37. package/performers/response.d.ts +0 -3
  38. package/performers/response.js +0 -62
  39. package/services/pondSocket.js +0 -190
  40. package/types.d.ts +0 -64
  41. /package/{constants.d.ts → dist/constants.d.ts} +0 -0
  42. /package/{constants.js → dist/constants.js} +0 -0
  43. /package/{decorators → dist/decorators}/channel.d.ts +0 -0
  44. /package/{decorators → dist/decorators}/channel.js +0 -0
  45. /package/{decorators → dist/decorators}/channelInstance.d.ts +0 -0
  46. /package/{decorators → dist/decorators}/channelInstance.js +0 -0
  47. /package/{decorators → dist/decorators}/endpoint.d.ts +0 -0
  48. /package/{decorators → dist/decorators}/endpoint.js +0 -0
  49. /package/{decorators → dist/decorators}/endpointInstance.d.ts +0 -0
  50. /package/{decorators → dist/decorators}/endpointInstance.js +0 -0
  51. /package/{decorators → dist/decorators}/getChannel.d.ts +0 -0
  52. /package/{decorators → dist/decorators}/getChannel.js +0 -0
  53. /package/{decorators → dist/decorators}/getConnectionContext.d.ts +0 -0
  54. /package/{decorators → dist/decorators}/getConnectionContext.js +0 -0
  55. /package/{decorators → dist/decorators}/getConnectionHeaders.d.ts +0 -0
  56. /package/{decorators → dist/decorators}/getConnectionHeaders.js +0 -0
  57. /package/{decorators → dist/decorators}/getContext.d.ts +0 -0
  58. /package/{decorators → dist/decorators}/getContext.js +0 -0
  59. /package/{decorators → dist/decorators}/getEventContext.d.ts +0 -0
  60. /package/{decorators → dist/decorators}/getEventContext.js +0 -0
  61. /package/{decorators → dist/decorators}/getEventParams.d.ts +0 -0
  62. /package/{decorators → dist/decorators}/getEventParams.js +0 -0
  63. /package/{decorators → dist/decorators}/getEventPayload.d.ts +0 -0
  64. /package/{decorators → dist/decorators}/getEventPayload.js +0 -0
  65. /package/{decorators → dist/decorators}/getEventQuery.d.ts +0 -0
  66. /package/{decorators → dist/decorators}/getEventQuery.js +0 -0
  67. /package/{decorators → dist/decorators}/getJoinContext.d.ts +0 -0
  68. /package/{decorators → dist/decorators}/getJoinContext.js +0 -0
  69. /package/{decorators → dist/decorators}/getJoinParams.d.ts +0 -0
  70. /package/{decorators → dist/decorators}/getJoinParams.js +0 -0
  71. /package/{decorators → dist/decorators}/getLeaveEvent.d.ts +0 -0
  72. /package/{decorators → dist/decorators}/getLeaveEvent.js +0 -0
  73. /package/{decorators → dist/decorators}/getUserAssigns.d.ts +0 -0
  74. /package/{decorators → dist/decorators}/getUserAssigns.js +0 -0
  75. /package/{decorators → dist/decorators}/getUserData.d.ts +0 -0
  76. /package/{decorators → dist/decorators}/getUserData.js +0 -0
  77. /package/{decorators → dist/decorators}/getUserPresence.d.ts +0 -0
  78. /package/{decorators → dist/decorators}/getUserPresence.js +0 -0
  79. /package/{decorators → dist/decorators}/index.d.ts +0 -0
  80. /package/{decorators → dist/decorators}/index.js +0 -0
  81. /package/{decorators → dist/decorators}/onConnectionRequest.d.ts +0 -0
  82. /package/{decorators → dist/decorators}/onEvent.d.ts +0 -0
  83. /package/{decorators → dist/decorators}/onJoinRequest.d.ts +0 -0
  84. /package/{decorators → dist/decorators}/onLeave.d.ts +0 -0
  85. /package/{decorators → dist/decorators}/onOutgoingEvent.d.ts +0 -0
  86. /package/{decorators → dist/decorators}/useGuards.d.ts +0 -0
  87. /package/{decorators → dist/decorators}/useGuards.js +0 -0
  88. /package/{decorators → dist/decorators}/usePipes.d.ts +0 -0
  89. /package/{decorators → dist/decorators}/usePipes.js +0 -0
  90. /package/{helpers → dist/helpers}/createContextParamDecorator.d.ts +0 -0
  91. /package/{helpers → dist/helpers}/createContextParamDecorator.js +0 -0
  92. /package/{helpers → dist/helpers}/createParamDecorator.d.ts +0 -0
  93. /package/{managers → dist/managers}/channel.d.ts +0 -0
  94. /package/{managers → dist/managers}/channel.js +0 -0
  95. /package/{managers → dist/managers}/channelInstance.d.ts +0 -0
  96. /package/{managers → dist/managers}/channelInstance.js +0 -0
  97. /package/{managers → dist/managers}/class.d.ts +0 -0
  98. /package/{managers → dist/managers}/class.js +0 -0
  99. /package/{managers → dist/managers}/connection.d.ts +0 -0
  100. /package/{managers → dist/managers}/connection.js +0 -0
  101. /package/{managers → dist/managers}/createMetadataManager.d.ts +0 -0
  102. /package/{managers → dist/managers}/createMetadataManager.js +0 -0
  103. /package/{managers → dist/managers}/endpoint.d.ts +0 -0
  104. /package/{managers → dist/managers}/endpoint.js +0 -0
  105. /package/{managers → dist/managers}/endpointInstance.d.ts +0 -0
  106. /package/{managers → dist/managers}/endpointInstance.js +0 -0
  107. /package/{managers → dist/managers}/event.d.ts +0 -0
  108. /package/{managers → dist/managers}/event.js +0 -0
  109. /package/{managers → dist/managers}/guards.d.ts +0 -0
  110. /package/{managers → dist/managers}/guards.js +0 -0
  111. /package/{managers → dist/managers}/handlers.d.ts +0 -0
  112. /package/{managers → dist/managers}/handlers.js +0 -0
  113. /package/{managers → dist/managers}/join.d.ts +0 -0
  114. /package/{managers → dist/managers}/join.js +0 -0
  115. /package/{managers → dist/managers}/leave.d.ts +0 -0
  116. /package/{managers → dist/managers}/leave.js +0 -0
  117. /package/{managers → dist/managers}/method.d.ts +0 -0
  118. /package/{managers → dist/managers}/method.js +0 -0
  119. /package/{managers → dist/managers}/outgoingEvent.js +0 -0
  120. /package/{managers → dist/managers}/parametres.d.ts +0 -0
  121. /package/{managers → dist/managers}/parametres.js +0 -0
  122. /package/{managers → dist/managers}/pipes.d.ts +0 -0
  123. /package/{managers → dist/managers}/pipes.js +0 -0
  124. /package/{managers → dist/managers}/property.d.ts +0 -0
  125. /package/{managers → dist/managers}/property.js +0 -0
  126. /package/{modules → dist/modules}/pondSocket.d.ts +0 -0
  127. /package/{types.js → dist/types.js} +0 -0
package/README.md CHANGED
@@ -1,590 +1,190 @@
1
- # PondSocket NestJS Integration
1
+ # PondSocket NestJS
2
2
 
3
- This package provides a NestJS integration layer for PondSocket, making it easy to use PondSocket's real-time WebSocket
4
- functionality within NestJS applications.
3
+ NestJS integration for PondSocket endpoints and channels.
5
4
 
6
- ## Installation
5
+ ## Install
7
6
 
8
7
  ```bash
9
- npm install @eleven-am/pondsocket-nest
10
- ```
11
-
12
- ## Overview
13
-
14
- This package integrates PondSocket's powerful WebSocket capabilities with NestJS's architecture and dependency injection
15
- system. It provides decorators and services that make it natural to use WebSocket functionality in a NestJS application
16
- while maintaining all of PondSocket's features.
17
-
18
- ## Key Features
19
-
20
- - **NestJS Integration**: Seamless integration with NestJS's module system and dependency injection
21
- - **Decorator-based API**: Use familiar NestJS-style decorators for WebSocket endpoints and channels
22
- - **Guard Support**: Full integration with NestJS guards for authentication and authorization
23
- - **Pipe Support**: Use NestJS pipes for data transformation and validation
24
- - **Type Safety**: Complete TypeScript support with proper type definitions
25
- - **Distributed Support**: Maintains PondSocket's distributed backend capabilities
26
- - **Automatic Discovery**: Uses NestJS's discovery service to automatically find and manage WebSocket endpoints
27
-
28
- ## Basic Usage
29
-
30
- ### Module Setup
31
-
32
- ```typescript
33
- import {Module} from '@nestjs/common';
34
- import {PondSocketModule} from '@eleven-am/pondsocket-nest';
35
-
36
- @Module ({
37
- imports: [
38
- PondSocketModule.forRoot ({
39
- guards: [AuthGuard], // Optional: Global guards
40
- pipes: [ValidationPipe], // Optional: Global pipes
41
- isGlobal: true, // Optional: Make the module global
42
- })
43
- ]
44
- })
45
- export class AppModule {}
46
- ```
47
-
48
- ### Creating WebSocket Endpoints
49
-
50
- ```typescript
51
- import {Controller} from '@nestjs/common';
52
- import {PondSocketEndpoint, PondSocketConnection, Context} from '@eleven-am/pondsocket-nest';
53
-
54
- @Endpoint('/api/socket')
55
- export class SocketController {
56
- @OnConnection()
57
- async handleConnection (ctx: Context) {
58
- const token = ctx.request.query.token;
59
-
60
- if (isValidToken (token)) {
61
- const role = getRoleFromToken (token);
62
- ctx.accept ({role});
63
- } else {
64
- ctx.reject ('Invalid token', 401);
65
- }
66
- }
8
+ npm install @eleven-am/pondsocket @eleven-am/pondsocket-common @eleven-am/pondsocket-nest
9
+ ```
10
+
11
+ `@nestjs/common`, `@nestjs/core`, and `rxjs` are peer dependencies.
12
+
13
+ ## Shared schema
14
+
15
+ Define a channel once in code shared by the server and client. Schema use is optional; the legacy string decorators and channel APIs remain supported.
16
+
17
+ ```ts
18
+ import {
19
+ definePondChannel,
20
+ definePondEndpoint,
21
+ definePondSchema,
22
+ } from '@eleven-am/pondsocket-common';
23
+
24
+ interface ChatSchema {
25
+ events: {
26
+ 'message/:messageId': { text: string };
27
+ ping: [{ sentAt: number }, { receivedAt: number }];
28
+ };
29
+ presence: {
30
+ online: boolean;
31
+ };
32
+ assigns: {
33
+ role: 'admin' | 'member';
34
+ };
35
+ joinParams: {
36
+ token: string;
37
+ };
67
38
  }
68
- ```
69
39
 
70
- ### Creating Channels
71
-
72
- ```typescript
73
- import {Controller} from '@nestjs/common';
74
- import {PondSocketChannel, PondSocketJoin, Context} from '@eleven-am/pondsocket-nest';
75
-
76
- @Channel('/channel/:id')
77
- export class ChannelController {
78
- @OnJoin()
79
- async handleJoin (ctx: Context) {
80
- const {role} = ctx.user.assigns;
81
- const {username} = ctx.joinParams;
82
- const {id} = ctx.event.params;
83
-
84
- if (role === 'admin') {
85
- ctx.accept({username})
86
- .trackPresence({
87
- username,
88
- role,
89
- status: 'online',
90
- onlineSince: Date.now (),
91
- });
92
- } else {
93
- ctx.decline('Insufficient permissions', 403);
94
- }
95
- }
96
- }
40
+ export const Socket = definePondEndpoint('/v1/socket');
41
+ export const Chat = definePondChannel(
42
+ definePondSchema<ChatSchema>(),
43
+ '/chat/:roomId',
44
+ );
97
45
  ```
98
46
 
99
- ### Handling Channel Events
100
-
101
- ```typescript
102
- import {Controller} from '@nestjs/common';
103
- import {PondSocketEvent, Context} from '@eleven-am/pondsocket-nest';
104
-
105
- @Channel('/channel/:id')
106
- export class ChannelController {
107
- @OnEvent('message')
108
- async handleMessage (ctx: Context) {
109
- const {text} = ctx.event.payload;
110
-
111
- // Broadcast to all users in the channel
112
- ctx.broadcast('message', {text});
113
-
114
- // Broadcast to specific users
115
- ctx.broadcastTo(['user1', 'user2'], 'message', {text});
116
-
117
- // Broadcast to all except sender
118
- ctx.broadcastFrom('message', {text});
119
- }
120
- }
121
- ```
47
+ The optional second argument to `definePondChannel` is the runtime channel pattern. It is inferred as a literal type, so it is never repeated as a generic.
122
48
 
123
- ## Advanced Features
49
+ ## Nest handlers
124
50
 
125
- ### Presence Management
51
+ Endpoint association uses the endpoint class. If an application has exactly one endpoint, `{ endpoint: SocketEndpoint }` may be omitted.
126
52
 
127
- ```typescript
53
+ ```ts
54
+ import type {
55
+ PondConnectionContext,
56
+ PondEventContext,
57
+ PondJoinContext,
58
+ PondOutgoingContext,
59
+ } from '@eleven-am/pondsocket-nest';
60
+ import { Chat, Socket } from './pond-definitions';
128
61
 
129
- @Channel ('/channel/:id')
130
- export class ChannelController {
131
- @OnEvent ('presence')
132
- async handlePresence (ctx: Context) {
133
- ctx.trackPresence ({
134
- username: ctx.user.assigns.username,
135
- status: 'online',
136
- lastSeen: Date.now ()
137
- });
138
- }
62
+ @Socket.Endpoint()
63
+ export class SocketEndpoint {
64
+ @Socket.OnConnection()
65
+ connect(@Socket.GetContext() ctx: PondConnectionContext<typeof Socket>) {
66
+ ctx.accept();
67
+ }
139
68
  }
140
- ```
141
-
142
- ### User Assigns
143
-
144
- ```typescript
145
69
 
146
- @Channel ('/channel/:id')
147
- export class ChannelController {
148
- @OnEvent ('update-profile')
149
- async handleProfileUpdate (ctx: Context) {
150
- ctx.assign ({
151
- ...ctx.user.assigns,
152
- profile: ctx.event.payload
153
- });
154
- }
70
+ @Chat.Channel({ endpoint: SocketEndpoint })
71
+ export class ChatController {
72
+ @Chat.OnJoin()
73
+ join(@Chat.GetContext() ctx: PondJoinContext<typeof Chat>) {
74
+ ctx.joinParams.token.toUpperCase();
75
+ ctx.params.roomId.toUpperCase();
76
+ ctx.accept({ role: 'member' }).trackPresence({ online: true });
77
+ }
78
+
79
+ @Chat.OnEvent('message/:messageId')
80
+ message(
81
+ @Chat.GetContext()
82
+ ctx: PondEventContext<typeof Chat, 'message/:messageId'>,
83
+ ) {
84
+ ctx.params.messageId.toUpperCase();
85
+ ctx.payload.text.toUpperCase();
86
+ ctx.broadcast(ctx.event.event, ctx.payload);
87
+ }
88
+
89
+ @Chat.OnEvent('ping')
90
+ ping(
91
+ @Chat.GetContext()
92
+ ctx: PondEventContext<typeof Chat, 'ping'>,
93
+ ) {
94
+ ctx.reply({ receivedAt: Date.now() });
95
+ }
96
+
97
+ @Chat.OnOutgoingEvent('message/:messageId')
98
+ outgoing(
99
+ @Chat.GetContext()
100
+ ctx: PondOutgoingContext<typeof Chat, 'message/:messageId'>,
101
+ ) {
102
+ ctx.transform({ text: ctx.payload.text.trim() });
103
+ }
155
104
  }
156
105
  ```
157
106
 
158
- ### Error Handling
159
-
160
- ```typescript
161
- import {PondChannel} from "@eleven-am/pondsocket/types";
162
- import {ChannelInstance} from "@eleven-am/pondsocke-nest";
163
-
164
- @Channel ('/channel/:id')
165
- export class ChannelController {
166
- @ChannelInstance()
167
- instancd: PondChannel // Instance of the channel
168
-
169
- @OnEvent ('message')
170
- async handleMessage (ctx: Context) {
171
- try {
172
- // Your logic here
173
- ctx.accept();
174
- } catch (error) {
175
- ctx.decline (error.message, 400);
176
- }
177
- }
178
- }
179
- ```
107
+ Every handler parameter must use a PondSocket parameter decorator. `@Chat.GetContext()` and `@Socket.GetContext()` explicitly inject the definition-bound context; the TypeScript annotation narrows it to the handler lifecycle and event. Extraction decorators such as `@GetEventPayload()`, `@GetEventParams()`, and custom decorators remain available for individual values.
180
108
 
181
- ## Distributed Deployment
109
+ Outgoing handlers run once per recipient. They must call `ctx.transform(payload)` or `ctx.block()` explicitly; return values are ignored. For request/response tuple events, `ctx.action` discriminates the payload: `BROADCAST` carries the request payload and `SYSTEM` carries the response payload.
182
110
 
183
- The package maintains PondSocket's distributed deployment capabilities:
111
+ ## Module
184
112
 
185
- ```typescript
186
- import {Module} from '@nestjs/common';
187
- import {PondSocketModule} from '@eleven-am/pondsocket-nest';
188
- import {RedisBackend} from '@eleven-am/pondsocket';
113
+ ```ts
114
+ import { Module } from '@nestjs/common';
115
+ import { PondSocketModule } from '@eleven-am/pondsocket-nest';
189
116
 
190
117
  @Module({
191
- imports: [
192
- PondSocketModule.forRoot({
193
- backend: new RedisBackend({
194
- host: 'localhost',
195
- port: 6379
196
- })
197
- })
198
- ]
118
+ imports: [
119
+ PondSocketModule.forRoot({
120
+ providers: [SocketEndpoint, ChatController],
121
+ isExclusiveSocketServer: false,
122
+ }),
123
+ ],
199
124
  })
200
- export class AppModule {
201
- }
202
- ```
203
-
204
- ### Distributed Mode Features
205
-
206
- The distributed mode enables you to scale your WebSocket application across multiple server instances while maintaining
207
- state synchronization. Here are the key features:
208
-
209
- 1. **State Synchronization**
210
- - Channel presence is synchronized across all instances
211
- - User assigns are shared between instances
212
- - Channel events are broadcasted to all instances
213
-
214
- 2. **Load Balancing**
215
- - Multiple server instances can handle WebSocket connections
216
- - Connections are distributed across available instances
217
- - Automatic failover if an instance goes down
218
-
219
- 3. **Backend Options**
220
- ```typescript
221
- // Redis Backend (Recommended for production)
222
- import { RedisBackend } from '@eleven-am/pondsocket';
223
-
224
- PondSocketModule.forRoot({
225
- backend: new RedisBackend({
226
- host: 'localhost',
227
- port: 6379,
228
- password: 'optional-password',
229
- db: 0,
230
- keyPrefix: 'pondsocket:', // Optional prefix for Redis keys
231
- })
232
- })
233
-
234
- // Memory Backend (For development/testing)
235
- import { MemoryBackend } from '@eleven-am/pondsocket';
236
-
237
- PondSocketModule.forRoot({
238
- backend: new MemoryBackend()
239
- })
240
- ```
241
-
242
- 4. **Configuration Options**
243
- ```typescript
244
- interface DistributedBackendOptions {
245
- // Redis specific options
246
- host?: string;
247
- port?: number;
248
- password?: string;
249
- db?: number;
250
- keyPrefix?: string;
251
-
252
- // General options
253
- reconnectInterval?: number; // Time between reconnection attempts
254
- maxRetries?: number; // Maximum number of reconnection attempts
255
- timeout?: number; // Operation timeout in milliseconds
256
- }
257
- ```
258
-
259
- 5. **Error Handling**
260
- ```typescript
261
- @Channel('/channel/:id')
262
- export class ChannelController {
263
- @OnEvent('message')
264
- async handleMessage(ctx: Context) {
265
- try {
266
- // Your logic here
267
- ctx.accept();
268
- } catch (error) {
269
- // Handle distributed backend errors
270
- if (error instanceof DistributedBackendError) {
271
- // Handle specific distributed backend errors
272
- ctx.decline('Backend error occurred', 500);
273
- } else {
274
- ctx.decline(error.message, 400);
275
- }
276
- }
277
- }
278
- }
279
- ```
280
-
281
- 6. **Health Checks**
282
- ```typescript
283
- import { PondSocketService } from '@eleven-am/pondsocket-nest';
284
-
285
- @Controller('health')
286
- export class HealthController {
287
- constructor(private readonly pondSocketService: PondSocketService) {}
288
-
289
- @Get('websocket')
290
- async checkWebSocketHealth() {
291
- const isHealthy = await this.pondSocketService.isHealthy();
292
- return {
293
- status: isHealthy ? 'healthy' : 'unhealthy',
294
- timestamp: new Date().toISOString()
295
- };
296
- }
297
- }
298
- ```
299
-
300
- 7. **Best Practices**
301
- - Use Redis backend in production environments
302
- - Implement proper error handling for distributed operations
303
- - Monitor backend connection health
304
- - Use appropriate Redis configuration for your scale
305
- - Consider using Redis Cluster for high availability
306
- - Implement proper logging for distributed operations
307
-
308
- 8. **Scaling Considerations**
309
- - Monitor Redis memory usage
310
- - Implement proper cleanup of stale data
311
- - Consider using Redis Cluster for larger deployments
312
- - Implement proper error handling and retry mechanisms
313
- - Monitor network latency between instances
314
- - Implement proper logging and monitoring
315
-
316
- ## Configuration Options
317
-
318
- The `PondSocketModule.forRoot()` method accepts the following options:
319
-
320
- ```typescript
321
- interface PondSocketOptions {
322
- guards?: any[]; // Global guards
323
- pipes?: any[]; // Global pipes
324
- providers?: any[]; // Additional providers
325
- imports?: any[]; // Additional imports
326
- exports?: any[]; // Additional exports
327
- isGlobal?: boolean; // Make the module global
328
- isExclusiveSocketServer?: boolean; // Use exclusive socket server
329
- backend?: IDistributedBackend; // Distributed backend
330
- }
125
+ export class AppModule {}
331
126
  ```
332
127
 
333
- ## Client Usage
128
+ `forRootAsync` accepts the same Nest module metadata plus `inject` and `useFactory`. Its factory can return `backend`, `maxMessageSize`, and `heartbeatInterval`.
334
129
 
335
- The client-side usage remains the same as the core PondSocket package:
336
-
337
- ```typescript
338
- import PondClient from "@eleven-am/pondsocket-client";
339
-
340
- const socket = new PondClient('ws://your-server/api/socket', {
341
- token: 'your-auth-token'
342
- });
130
+ ## Client
343
131
 
344
- socket.connect ();
132
+ ```ts
133
+ import { PondClient } from '@eleven-am/pondsocket-client';
134
+ import { Chat } from './pond-definitions';
345
135
 
346
- const channel = socket.createChannel('/channel/123', {
347
- username: 'user123'
136
+ const client = new PondClient('wss://example.com/v1/socket');
137
+ const channel = client.createChannel(Chat, {
138
+ params: { roomId: 'general' },
139
+ joinParams: { token: 'secret' },
348
140
  });
349
141
 
350
- channel.join ();
351
-
352
- channel.onMessage((event, message) => {
353
- console.log (`Received message: ${message.text}`);
142
+ channel.onMessageEvent('message/:messageId', (payload) => {
143
+ payload.text.toUpperCase();
354
144
  });
355
145
 
356
- channel.broadcast('message', {text: 'Hello, PondSocket!'});
357
- ```
358
-
359
- ## Contributing
360
-
361
- Contributions are welcome! Please feel free to submit a Pull Request.
362
-
363
- ## License
364
-
365
- This project is licensed under the GPL-3.0 License - see the LICENSE file for details.
366
-
367
- ## Return Type Functionality
368
-
369
- The NestJS integration provides a powerful return type system that allows you to declaratively specify actions to be
370
- taken when handling WebSocket events. Instead of using the context object directly, you can return an object with
371
- specific properties to trigger various actions.
372
-
373
- ### Return Type Interface
374
-
375
- ```typescript
376
- type NestFuncType<Event extends string, Payload extends PondMessage, Presence extends PondPresence, Assigns extends PondAssigns = PondAssigns> =
377
- {
378
- // Send an event to the user
379
- event?: Event;
380
-
381
- // Broadcast to all users in the channel
382
- broadcast?: Event;
383
-
384
- // Broadcast to all users except the sender
385
- broadcastFrom?: Event;
386
-
387
- // Update user assigns
388
- assigns?: Partial<Assigns>;
389
-
390
- // Update user presence
391
- presence?: Presence;
392
- }
393
- & Payload;
146
+ channel.onError((error) => console.error(error.code, error.status, error.message));
147
+ channel.join();
394
148
  ```
395
149
 
396
- ### Usage Examples
397
-
398
- #### Channel Join with Multiple Actions
399
-
400
- ```typescript
401
-
402
- @PondSocketChannel('/chat/:roomId')
403
- export class ChatController {
404
- @PondSocketJoin()
405
- async handleJoin (ctx: Context) {
406
- const {username} = ctx.joinParams;
407
-
408
- return {
409
- // Send welcome message to the joining user
410
- event: 'welcome',
411
- message: 'Welcome to the chat!',
412
-
413
- // Broadcast join notification to all users
414
- broadcast: 'user_joined',
415
- username,
416
- timestamp: Date.now(),
417
-
418
- // Update user's assigns
419
- assigns: {
420
- username,
421
- joinedAt: Date.now(),
422
- role: 'member'
423
- },
424
-
425
- // Update user's presence
426
- presence: {
427
- username,
428
- status: 'online',
429
- lastSeen: Date.now()
430
- }
431
- };
432
- }
433
- }
434
- ```
150
+ Required route and join parameters are derived from the definition. Presence, assigns, event names, event payloads, and request/response tuples are derived from the same schema.
435
151
 
436
- #### Message Handling
152
+ ## Responses and errors
437
153
 
438
- ```typescript
154
+ A handler can use the context directly or return a declarative response. The nested `payload` form avoids collisions with control fields:
439
155
 
440
- @PondSocketChannel('/chat/:roomId')
441
- export class ChatController {
442
- @PondSocketEvent('message')
443
- async handleMessage(ctx: Context) {
444
- const {text} = ctx.event.payload;
445
- const {username} = ctx.user.assigns;
446
-
447
- return {
448
- // Broadcast the message to all users
449
- broadcast: 'message',
450
- text,
451
- username,
452
- timestamp: Date.now(),
453
-
454
- // Update user's last message timestamp
455
- assigns: {
456
- lastMessageAt: Date.now()
457
- }
458
- };
459
- }
460
- }
156
+ ```ts
157
+ return {
158
+ event: 'message/:messageId',
159
+ eventParams: { messageId: 'message-1' },
160
+ payload: { text: 'saved' },
161
+ assigns: { role: 'member' },
162
+ presence: { online: true },
163
+ };
461
164
  ```
462
165
 
463
- #### Presence Updates
464
-
465
- ```typescript
166
+ Connection and join handlers can decline declaratively:
466
167
 
467
- @PondSocketChannel ('/chat/:roomId')
468
- export class ChatController {
469
- @PondSocketEvent('status')
470
- async handleStatus(ctx: Context) {
471
- const {status} = ctx.event.payload;
472
-
473
- return {
474
- // Update user's presence
475
- presence: {
476
- status,
477
- lastSeen: Date.now()
478
- },
479
-
480
- // Notify others about the status change
481
- broadcastFrom: 'status_change',
482
- username: ctx.user.assigns.username,
483
- status,
484
- timestamp: Date.now()
485
- };
486
- }
487
- }
168
+ ```ts
169
+ return { decline: { message: 'Invalid token', status: 401 } };
488
170
  ```
489
171
 
490
- ### Benefits
491
-
492
- 1. **Declarative Code**: Actions are clearly specified in the return object, making the code more readable and
493
- maintainable.
494
-
495
- 2. **Type Safety**: The return type is fully typed, providing excellent TypeScript support and IDE autocompletion.
172
+ Unhandled exceptions are logged through Nest's `Logger`. Event failures reply with `INTERNAL_SERVER_ERROR`; rejected event guards reply with `UNAUTHORIZED_BROADCAST`.
496
173
 
497
- 3. **Reduced Boilerplate**: No need to call multiple context methods; all actions are specified in a single return
498
- statement.
174
+ ## Operations
499
175
 
500
- 4. **Flexible Combinations**: You can combine multiple actions in a single return statement, making it easy to handle
501
- complex scenarios.
176
+ `PondSocketService` is exported and injectable. `pondSocket` exposes the underlying server and `isHealthy()` reports backend/server health. Module initialization waits for the distributed backend, and module shutdown awaits PondSocket cleanup without closing Nest's shared HTTP server.
502
177
 
503
- 5. **Automatic Handling**: The framework automatically processes the returned object and executes the specified actions
504
- in the correct order.
178
+ ```ts
179
+ constructor(private readonly sockets: PondSocketService) {}
505
180
 
506
- ## Decorators
507
-
508
- The PondSocket NestJS integration provides a comprehensive set of decorators to simplify WebSocket endpoint and channel development. These decorators are organized into several categories:
509
-
510
- ### Class Decorators
511
-
512
- | Decorator | Description |
513
- |-----------|-------------|
514
- | `@Channel(path?: string)` | Marks a class as a WebSocket channel handler for the specified path |
515
- | `@Endpoint(path?: string)` | Registers a class as a WebSocket endpoint at the specified path |
516
-
517
- ### Property Decorators
518
-
519
- | Decorator | Description |
520
- |-----------|-------------|
521
- | `@ChannelInstance(channel?: string)` | Injects the underlying PondSocket channel instance into a property |
522
- | `@EndpointInstance()` | Injects the endpoint instance into a property |
523
-
524
- ### Method Decorators
525
-
526
- | Decorator | Description |
527
- |-----------|-------------|
528
- | `@OnConnectionRequest()` | Marks a method to handle new socket connections (authentication, setup, etc.) |
529
- | `@OnEvent(event?: string)` | Marks a method to handle a specific event in a channel |
530
- | `@OnJoinRequest()` | Marks a method to handle when a user joins a channel |
531
- | `@OnLeave()` | Marks a method to handle when a user leaves a channel |
532
- | `@UseGuards(...guards)` | Attaches guard(s) to a class or method for authorization logic |
533
- | `@UsePipes(...pipes)` | Attaches pipe(s) (validators/transformers) to a class or method |
534
-
535
- ### Parameter Decorators
536
-
537
- These decorators inject request-specific data into handler method parameters:
538
-
539
- | Decorator | Description |
540
- |-----------|-------------|
541
- | `@GetChannel()` | Injects the current channel instance |
542
- | `@GetConnectionHeaders()` | Injects the current connection headers |
543
- | `@GetConnectionContext()` | Injects the current connection context |
544
- | `@GetContext()` | Injects the current request context |
545
- | `@GetEventParams()` | Injects the current event parameters |
546
- | `@GetEventPayload()` | Injects the current event payload |
547
- | `@GetEventQuery()` | Injects the current event query |
548
- | `@GetEventContext()` | Injects the current event context |
549
- | `@GetJoinParams()` | Injects the current join parameters |
550
- | `@GetJoinContext()` | Injects the current join context |
551
- | `@GetLeaveEvent()` | Injects the current leave event |
552
- | `@GetUserData()` | Injects the current user data |
553
- | `@GetUserAssigns()` | Injects the current user assigns |
554
- | `@GetUserPresence()` | Injects the current user presence |
181
+ health() {
182
+ return this.sockets.isHealthy();
183
+ }
184
+ ```
555
185
 
556
- ### Usage Example
186
+ Channel patterns use path syntax: `/jobs/:id` and whole-segment `*` are supported. Patterns such as `job:*` are rejected at startup. A valid join that matches no registered channel receives a correlated `CHANNEL_NOT_FOUND` error instead of remaining in `JOINING`.
557
187
 
558
- Here's an example showing how to use these decorators together:
188
+ ## Legacy API
559
189
 
560
- ```typescript
561
- @Channel('/chat/:roomId')
562
- export class ChatController {
563
- @ChannelInstance()
564
- private channel: PondChannel;
565
-
566
- @OnJoinRequest()
567
- async handleJoin(
568
- @GetContext() ctx: Context,
569
- @GetJoinParams() params: any
570
- ) {
571
- // Handle join request
572
- }
573
-
574
- @OnEvent('message')
575
- async handleMessage(
576
- @GetEventPayload() payload: any,
577
- @GetUserData() user: any
578
- ) {
579
- // Handle message event
580
- }
581
-
582
- @OnLeave()
583
- async handleLeave(
584
- @GetLeaveEvent() event: any,
585
- @GetUserPresence() presence: any
586
- ) {
587
- // Handle leave event
588
- }
589
- }
590
- ```
190
+ `@Endpoint(path)`, `@Channel(path)`, `@OnConnectionRequest()`, `@OnJoinRequest()`, `@OnEvent(event)`, `@OnOutgoingEvent(event)`, and `@OnLeave()` remain available. In applications with multiple endpoints, shared definitions are recommended because they provide explicit endpoint ownership and schema-derived types.