@eleven-am/pondsocket 0.1.65 → 0.1.66

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 (3) hide show
  1. package/README.md +181 -7
  2. package/package.json +1 -1
  3. package/types.d.ts +19 -7
package/README.md CHANGED
@@ -33,7 +33,7 @@ Within each endpoint, sockets interact through channels. Channels provide an org
33
33
 
34
34
  ```javascript
35
35
  const channel = endpoint.createChannel('/channel/:id', (req, res) => {
36
- // Handle channel-specific events and actions
36
+ // Handle the join request, which is sent when a user attempts to join the channel
37
37
  });
38
38
  ```
39
39
 
@@ -74,9 +74,179 @@ This node client allows you to turn another server into a client, enabling easy
74
74
  - **Broadcasting**: PondSocket enables broadcasting messages to all users or specific groups within a channel, facilitating real-time updates.
75
75
  - **Typed and Well-documented**: The codebase is thoroughly documented and typed, providing a seamless development experience with improved IDE suggestions.
76
76
 
77
- ## License
77
+ ## Examples
78
+
79
+ ### Client-side Example with Authentication
80
+
81
+ To connect to the PondSocket server and send messages while associating a username with the client connection, follow the steps below:
82
+
83
+ ```javascript
84
+ import PondClient from "@eleven-am/pondsocket/client";
85
+
86
+ // Your server URL
87
+ const serverUrl = 'ws://your-server-url/api/socket';
88
+
89
+ // Your authenticated user's username (replace with actual username)
90
+ const authToken = 'your-auth-token';
91
+
92
+ // Your username (replace with actual username)
93
+ const username = 'user123';
94
+
95
+ // Create a new PondClient instance
96
+ const socket = new PondClient(serverUrl, { token: authToken });
97
+
98
+ // Connect to the server
99
+ socket.connect();
100
+
101
+ // Add event listeners to handle various scenarios
102
+ socket.onConnectionChange((connected) => {
103
+ if (connected) {
104
+ console.log('Connected to the server.');
105
+ } else {
106
+ console.log('Disconnected from the server.');
107
+ }
108
+ });
109
+
110
+ // Create a channel and join it
111
+ const channel = socket.createChannel('/channel/123', { username });
112
+ channel.join();
113
+
114
+ // Send a message to the server
115
+ const message = "Hello, PondSocket!";
116
+ channel.broadcast('message', { text: message });
117
+
118
+ // Handle received messages
119
+ channel.onMessage((event, message) => {
120
+ console.log(`Received message from server: ${message.text}`);
121
+ });
122
+ ```
123
+
124
+ The client will now connect to the server, and the server will receive the necessary headers automatically, including any authentication tokens or cookies, as required by the browser.
125
+
126
+ ### Server-side Example with Authentication and check for profanity before broadcasting
127
+
128
+ To create a PondSocket server that accepts authenticated connections and checks for profanity before broadcasting messages, follow the steps below:
129
+
130
+ ```javascript
131
+ import PondSocket from "@eleven-am/pondsocket";
132
+ import Filter from "bad-words";
78
133
 
79
- PondSocket is released under the MIT License. Please refer to the `LICENSE` file for detailed licensing information.
134
+ // Helper functions for token validation
135
+ function isValidToken(token) {
136
+ // Implement your token validation logic here
137
+ // Return true if the token is valid, false otherwise
138
+ return true;
139
+ }
140
+
141
+ function getRoleFromToken(token) {
142
+ // Implement the logic to extract the user's role from the token
143
+ // Return the user's role
144
+ return 'user';
145
+ }
146
+
147
+ // Create a PondChannel with profanity check
148
+ const profanityFilter = new Filter();
149
+
150
+ const pond = new PondSocket();
151
+
152
+ // Create an endpoint for handling socket connections
153
+ const endpoint = pond.createEndpoint('/api/socket', (req, res) => {
154
+ // Depending if the user already has cookies set, they can be accessed from the request headers or the request address
155
+ const token = req.query.token; // If the token is passed as a query parameter
156
+
157
+ // Perform token validation here
158
+ if (isValidToken(token)) {
159
+ // Extract the authenticated user's username
160
+ const role = getUsernameFromToken(token);
161
+
162
+ // Handle socket connection and authentication for valid users
163
+ res.accept({ role }); // Assign the user's role to the socket
164
+ } else {
165
+ // Reject the connection for invalid users or without a token
166
+ res.reject('Invalid token', 401);
167
+ }
168
+ });
169
+
170
+ const profanityChannel = endpoint.createChannel('/channel/:id', (req, res) => {
171
+ // When joining the channel, any joinParams passed from the client will be available in the request payload
172
+ // Also any previous assigns on the socket will be available in the request payload as well
173
+ const { role } = req.user.assigns;
174
+ const { username } = req.joinParams;
175
+
176
+ // Check if the user has the required role to join the channel
177
+ if (role === 'admin') {
178
+ // Accept the join request
179
+ res.accept({ username, profanityCount: 0 })
180
+ // optionally you can track the presence of the user in the channel
181
+ .trackPresence({
182
+ username,
183
+ role,
184
+ status: 'online',
185
+ onlineSince: Date.now(),
186
+ });
187
+ } else {
188
+ // Reject the join request
189
+ res.reject('You do not have the required role to join this channel', 403);
190
+ }
191
+ });
192
+
193
+ // Attach message event listener to the profanityChannel
194
+ profanityChannel.onEvent('message', (req, res) => {
195
+ const { text } = req.event.payload;
196
+
197
+ // Check for profanity
198
+ if (profanityFilter.isProfane(text)) {
199
+ const profanityCount = req.user.assigns.profanityCount + 1
200
+ // Reject the message if it contains profanity
201
+ res.reject('Profanity is not allowed', 400, {
202
+ profanityCount,
203
+ });
204
+
205
+ if (profanityCount >= 3) {
206
+ // Kick the user from the channel if they have used profanity more than 3 times
207
+ res.evictUser('You have been kicked from the channel for using profanity');
208
+ } else {
209
+ // you can broadcast a message to all users or In the channel that profanity is not allowed
210
+ res.broadcast('profanity-warning', { message: 'Profanity is not allowed' })
211
+ // or you can send a message to the user that profanity is not allowed
212
+ .send('profanity-warning', { message: `You have used profanity ${profanityCount} times. You will be kicked from the channel if you use profanity more than 3 times.` });
213
+ }
214
+ } else {
215
+ // Accept the message to allow broadcasting to other clients in the channel
216
+ res.accept();
217
+ }
218
+
219
+ // for more complete access to the channel, you can use the client object
220
+ // const channel = req.client;
221
+ });
222
+
223
+ profanityChannel.onEvent('presence/:presence', (req, res) => {
224
+ const { presence } = req.event.params;
225
+ const { username } = req.user.assigns;
226
+
227
+ // Handle presence events
228
+ res.updatePresence({
229
+ username,
230
+ role,
231
+ onlineSince: Date.now(),
232
+ status: presence,
233
+ });
234
+ });
235
+
236
+ profanityChannel.onLeave((req, res) => {
237
+ const { username } = req.user.assigns;
238
+
239
+ // When a user leaves the channel, PondSocket will automatically remove the user from the presence list and inform other users in the channel
240
+
241
+ // perform a cleanup operation here
242
+ });
243
+
244
+
245
+ // Start the server
246
+ pond.listen(3000, () => {
247
+ console.log('PondSocket server listening on port 3000');
248
+ });
249
+ ```
80
250
 
81
251
  ## API Documentation
82
252
 
@@ -172,6 +342,8 @@ The `PondChannel` class represents a Generic channel in the PondSocket server. I
172
342
 
173
343
  - `broadcast(event: string, payload: PondMessage, channelName?: string): void`: Broadcasts a message to all users in the channel with the specified event and payload. Optionally, a specific channel name can be provided to broadcast the message only to users in that channel.
174
344
 
345
+ - `onLeave(handler: (event: LeaveEvent) => void | Promise<void>): void`: Handles a leave event for the channel with the provided handler function when a user leaves the channel.
346
+
175
347
  ### EventRequest
176
348
 
177
349
  The `EventRequest` class represents the request object when an event is received from a client.
@@ -206,9 +378,7 @@ The `EventResponse` class represents the response object for handling events fro
206
378
 
207
379
  - `broadcastFromUser(event: string, payload: PondMessage): EventResponse`: Sends a message to all clients in the channel except the sender with the specified event and payload.
208
380
 
209
- - `sendToUsers(event: string, payload: PondMessage, userIds: string[]): EventResponse`: Sends
210
-
211
- a message to a specific set of clients identified by the provided userIds with the specified event and payload.
381
+ - `sendToUsers(event: string, payload: PondMessage, userIds: string[]): EventResponse`: Sends a message to a specific set of clients identified by the provided userIds with the specified event and payload.
212
382
 
213
383
  - `trackPresence(presence: PondPresence, userId?: string): EventResponse`: Tracks a user's presence in the channel.
214
384
 
@@ -264,7 +434,7 @@ The `PondClient` class represents a client that connects to the PondSocket serve
264
434
 
265
435
  ### Channel
266
436
 
267
- The `Channel` class represents a channel in the PondSocket server.
437
+ The `Channel` class represents a channel in the PondClient.
268
438
 
269
439
  **Methods:**
270
440
 
@@ -298,6 +468,10 @@ The `Channel` class represents a channel in the PondSocket server.
298
468
 
299
469
  - `onConnectionChange(callback: (connected: boolean) => void): Unsubscribe`: Monitors the connection state of the channel and calls the provided callback when the connection state changes.
300
470
 
471
+ ## License
472
+
473
+ PondSocket is released under the GPL-3.0 License. Please refer to the `LICENSE` file for detailed licensing information.
474
+
301
475
  ## Conclusion
302
476
 
303
477
  PondSocket is a powerful and versatile solution for building real-time applications that require efficient bidirectional communication between server and client components. Its minimalist design and comprehensive feature set make it an excellent choice for WebSocket-based projects, providing developers with a straightforward and reliable tool for building real-time communication systems. With the Node.js client, it also allows for easy communication between multiple server instances, expanding its capabilities even further.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eleven-am/pondsocket",
3
- "version": "0.1.65",
3
+ "version": "0.1.66",
4
4
  "description": "PondSocket is a fast simple socket server",
5
5
  "keywords": [
6
6
  "socket",
package/types.d.ts CHANGED
@@ -1,9 +1,11 @@
1
- import { Express } from 'express';
2
1
  import { Server as HTTPServer, IncomingHttpHeaders } from 'http';
2
+
3
+ import type { Express } from 'express';
3
4
  import { WebSocketServer } from 'ws';
4
5
 
5
6
  type Unsubscribe = () => void;
6
7
 
8
+ export type default_t<T = any> = Record<string, T>;
7
9
  type IsParam<Path> = Path extends `:${infer Param}` ? Param : never;
8
10
 
9
11
  type FilteredParams<Path> = Path extends `${infer First}/${infer Second}`
@@ -21,12 +23,7 @@ type EventParams<Path> = {
21
23
  params: Params<Path>;
22
24
  }
23
25
 
24
- type Primitives = number | string | boolean | null | undefined;
25
-
26
- type PondObject = {
27
- [key: string]: Primitives | PondObject | PondObject[];
28
- }
29
-
26
+ type PondObject = default_t;
30
27
  type PondPresence = PondObject;
31
28
  type PondMessage = PondObject;
32
29
  type PondAssigns = PondObject;
@@ -56,6 +53,13 @@ type IncomingConnection<Path> = EventParams<Path> & {
56
53
  address: string;
57
54
  }
58
55
 
56
+ interface LeaveEvent {
57
+ userId: string;
58
+ assigns: PondAssigns;
59
+ }
60
+
61
+ type LeaveCallback = (event: LeaveEvent) => void;
62
+
59
63
  interface UserData {
60
64
  assigns: PondAssigns;
61
65
  presence: PondPresence;
@@ -479,6 +483,12 @@ export declare class PondChannel {
479
483
  *});
480
484
  */
481
485
  broadcast (event: string, payload: PondMessage, channelName?: string): void;
486
+
487
+ /**
488
+ * @desc Handles the leave event for a user, can occur when a user disconnects or leaves a channel, use this to clean up any resources
489
+ * @param callback - The callback to execute when a user leaves
490
+ */
491
+ public onLeave (callback: LeaveCallback): void;
482
492
  }
483
493
 
484
494
  declare class PondSocket {
@@ -515,6 +525,8 @@ declare class PondSocket {
515
525
  createEndpoint<Path extends string> (path: PondPath<Path>, handler: (request: IncomingConnection<Path>, response: ConnectionResponse) => void | Promise<void>): Endpoint;
516
526
  }
517
527
 
528
+ // eslint-disable-next-line @typescript-eslint/ban-ts-comment
529
+ // @ts-ignore
518
530
  interface PondSocketExpressApp extends Express {
519
531
 
520
532
  /**