@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.
- package/README.md +181 -7
- package/package.json +1 -1
- 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
|
|
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
|
-
##
|
|
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
|
-
|
|
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
|
|
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
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
|
|
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
|
/**
|