@eleven-am/pondsocket 0.1.89 → 0.1.90
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 +107 -489
- package/lobby/lobby.js +11 -0
- package/nest.d.ts +3 -3
- package/nest.js +128 -31
- package/package.json +4 -4
- package/types.d.ts +23 -13
package/README.md
CHANGED
|
@@ -1,521 +1,139 @@
|
|
|
1
|
-
# PondSocket
|
|
2
1
|
|
|
3
|
-
PondSocket
|
|
2
|
+
# PondSocket
|
|
4
3
|
|
|
5
|
-
|
|
4
|
+
PondSocket is a fast, minimalist and bidirectional socket framework for NodeJS. Pond allows you to think of each action during a sockets lifetime as a request instead of a huge callback that exists inside the connection event.
|
|
5
|
+
## Documentation
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
This is a Node.js module available through the npm registry.
|
|
8
8
|
|
|
9
9
|
```bash
|
|
10
|
-
npm install @eleven-am/pondsocket
|
|
10
|
+
npm install @eleven-am/pondsocket
|
|
11
11
|
```
|
|
12
12
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
import pondSocket from "@eleven-am/pondsocket/express";
|
|
36
|
-
import express from "express";
|
|
37
|
-
|
|
38
|
-
const app = pondSocket(express());
|
|
39
|
-
|
|
40
|
-
const endpoint = app.upgrade('/api/socket', (req, res) => {
|
|
41
|
-
// Handle socket connection and authentication
|
|
42
|
-
});
|
|
43
|
-
|
|
44
|
-
app.listen(3000);
|
|
13
|
+
PondSocket usage depends on the environment in which it is being used.
|
|
14
|
+
|
|
15
|
+
#### On the server
|
|
16
|
+
|
|
17
|
+
When using PondSocket, an endpoint is created. The endpoint is the gateway by which sockets actually connect to the server.
|
|
18
|
+
Multiple endpoints can be created but every endpoint is independent of the other, ie sockets on one endpoint cannot communicate with sockets on another endpoint.
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
import { PondSocket } from "@eleven-am/pondsocket";
|
|
22
|
+
|
|
23
|
+
const pond = new PondSocket();
|
|
24
|
+
|
|
25
|
+
const endpoint = pond.createEndpoint('/api/socket', (req, res, _endpoint) => {
|
|
26
|
+
const token = req.query.token;
|
|
27
|
+
if (!token)
|
|
28
|
+
return res.reject('No token provided');
|
|
29
|
+
res.accept({
|
|
30
|
+
assign: {
|
|
31
|
+
token
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
})
|
|
45
35
|
```
|
|
46
36
|
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
37
|
+
While sockets connect through the endpoint, communication between sockets cannot occur on the endpoint level. Sockets have to join a channel to communicate
|
|
38
|
+
between themselves.
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
const channel = endpoint.createChannel(/^channel(.*?)/, (req, res, channel) => {
|
|
42
|
+
const isAdmin = req.clientAssigns.admin;
|
|
43
|
+
if (!isAdmin)
|
|
44
|
+
return res.reject('You are not an admin');
|
|
45
|
+
|
|
46
|
+
res.accept({
|
|
47
|
+
assign: {
|
|
48
|
+
admin: true,
|
|
49
|
+
joinedDate: new Date()
|
|
50
|
+
},
|
|
51
|
+
presence: {
|
|
52
|
+
state: 'online'
|
|
53
|
+
},
|
|
54
|
+
channelData: {
|
|
55
|
+
locked: true,
|
|
56
|
+
numberOfUsers: channel.presence.length
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
53
60
|
```
|
|
54
61
|
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
62
|
+
A user goes through the createChannel function to join a channel.
|
|
63
|
+
When a user joins a channel, some private information can be assigned to the user. This assign could be viewed as a cookie that is only available serverside.
|
|
64
|
+
The presence is the current state of the user. When you reassign a new presence information to a user, all other users connected to the same channel are informed of the change.
|
|
65
|
+
This could be used as *user is typing*, *user is away*, etc. The channelData is information that is stored on the channel and accessible from anywhere the channel is available.
|
|
66
|
+
It can be anything from a boolean to an instance of a class. This data cannot be accessed from another channel as it is private to the channel.
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
channel.on('hello', (req, res, channel) => {
|
|
70
|
+
const users = channel.presence;
|
|
71
|
+
res.assign({
|
|
72
|
+
assign: {
|
|
73
|
+
pingDate: new Date(),
|
|
74
|
+
users: users.length
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// res.reject('curse words are not allowed on a child friendly channel')
|
|
79
|
+
// channel.closeFromChannel(req.client.clientId);
|
|
80
|
+
})
|
|
64
81
|
```
|
|
65
82
|
|
|
66
|
-
|
|
83
|
+
When a message is sent on a channel by a user, an event is triggered. The *on* function can be used to listen for these events. If the function is specified, it is called when the message is received.
|
|
84
|
+
You can choose to decline the message being sent, or you can allow the message to be sent as usual. You can also do all the normal assigns to the channel, or user.
|
|
85
|
+
In case there is no *on* function, the message will be sent without any action being taken.
|
|
67
86
|
|
|
68
|
-
|
|
69
|
-
const channel = socket.createChannel('/channel/123');
|
|
70
|
-
channel.join();
|
|
71
|
-
```
|
|
72
|
-
|
|
73
|
-
### Node Client
|
|
87
|
+
#### On the browser
|
|
74
88
|
|
|
75
|
-
|
|
89
|
+
```js
|
|
90
|
+
import { PondClient } from "@eleven-am/pondsocket/client";
|
|
76
91
|
|
|
77
|
-
|
|
78
|
-
|
|
92
|
+
export const socket = new PondClient('/api/socket', {});
|
|
93
|
+
socket.connect();
|
|
79
94
|
```
|
|
80
95
|
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
## Key Features
|
|
84
|
-
|
|
85
|
-
- **Simple and Efficient API**: PondSocket offers an easy-to-use API, making WebSocket communication straightforward and hassle-free.
|
|
86
|
-
- **Organized Channels**: Channels provide a structured approach for grouping users and facilitating efficient communication.
|
|
87
|
-
- **Assigns**: PondSocket allows the storage of private information for users and channels, enhancing data security.
|
|
88
|
-
- **Presence**: The presence feature keeps track of users' current states and notifies other users about any changes.
|
|
89
|
-
- **Broadcasting**: PondSocket enables broadcasting messages to all users or specific groups within a channel, facilitating real-time updates.
|
|
90
|
-
- **Typed and Well-documented**: The codebase is thoroughly documented and typed, providing a seamless development experience with improved IDE suggestions.
|
|
91
|
-
|
|
92
|
-
## Examples
|
|
96
|
+
The browser compatible package can be imported from @eleven-am/pondsocket/client.
|
|
97
|
+
AN url string is provided to the class along with other url params, like token.
|
|
93
98
|
|
|
94
|
-
|
|
99
|
+
Multiple classes can be created, but it is advised to use a single class throughout the application.
|
|
100
|
+
You can just create multiple channels and maintain the single socket connection.
|
|
95
101
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
// Your server URL
|
|
102
|
-
const serverUrl = 'ws://your-server-url/api/socket';
|
|
103
|
-
|
|
104
|
-
// Your authenticated user's token (replace with actual token)
|
|
105
|
-
const authToken = 'your-auth-token';
|
|
106
|
-
|
|
107
|
-
// Your username (replace with actual username)
|
|
108
|
-
const username = 'user123';
|
|
109
|
-
|
|
110
|
-
// Create a new PondClient instance
|
|
111
|
-
const socket = new PondClient(serverUrl, { token: authToken });
|
|
112
|
-
|
|
113
|
-
// Connect to the server
|
|
114
|
-
socket.connect();
|
|
115
|
-
|
|
116
|
-
// Add event listeners to handle various scenarios
|
|
117
|
-
socket.onConnectionChange((connected) => {
|
|
118
|
-
if (connected) {
|
|
119
|
-
console.log('Connected to the server.');
|
|
120
|
-
} else {
|
|
121
|
-
console.log('Disconnected from the server.');
|
|
102
|
+
```js
|
|
103
|
+
const channelTopic = 'channel:one';
|
|
104
|
+
const options = {
|
|
105
|
+
username: 'eleven-am'
|
|
122
106
|
}
|
|
123
|
-
});
|
|
124
|
-
|
|
125
|
-
// Create a channel and join it
|
|
126
|
-
const channel = socket.createChannel('/channel/123', { username });
|
|
127
|
-
channel.join();
|
|
128
|
-
|
|
129
|
-
// Send a message to the server
|
|
130
|
-
const message = "Hello, PondSocket!";
|
|
131
|
-
channel.broadcast('message', { text: message });
|
|
132
107
|
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
const subscription = channel.onMessage((event, message) => {
|
|
136
|
-
console.log(`Received message from server: ${message.text}`);
|
|
137
|
-
});
|
|
138
|
-
|
|
139
|
-
// Unsubscribe from the event
|
|
140
|
-
subscription();
|
|
108
|
+
export const channel = socket.createChannel(channelTopic, options);
|
|
109
|
+
channel.join();
|
|
141
110
|
```
|
|
142
111
|
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
### Server-side Example with Authentication and check for profanity before broadcasting
|
|
146
|
-
|
|
147
|
-
To create a PondSocket server that accepts authenticated connections and checks for profanity before broadcasting messages, follow the steps below:
|
|
148
|
-
|
|
149
|
-
```javascript
|
|
150
|
-
import PondSocket from "@eleven-am/pondsocket";
|
|
151
|
-
|
|
152
|
-
// Helper functions for token validation
|
|
153
|
-
function isValidToken(token) {
|
|
154
|
-
// Implement your token validation logic here
|
|
155
|
-
// Return true if the token is valid, false otherwise
|
|
156
|
-
return true;
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
function getRoleFromToken(token) {
|
|
160
|
-
// Implement the logic to extract the user's role from the token
|
|
161
|
-
// Return the user's role
|
|
162
|
-
return 'user';
|
|
163
|
-
}
|
|
164
|
-
|
|
165
|
-
function isTextProfane(text) {
|
|
166
|
-
// Implement your profanity check logic here
|
|
167
|
-
// Return true if the text is profane, false otherwise
|
|
168
|
-
return false;
|
|
169
|
-
}
|
|
170
|
-
|
|
171
|
-
function getMessagesFromDatabase(channelId) {
|
|
172
|
-
// Implement your logic to retrieve messages from the database
|
|
173
|
-
// Return an array of messages
|
|
174
|
-
return [];
|
|
175
|
-
}
|
|
176
|
-
|
|
177
|
-
const pond = new PondSocket();
|
|
178
|
-
|
|
179
|
-
// Create an endpoint for handling socket connections
|
|
180
|
-
const endpoint = pond.createEndpoint('/api/socket', (req, res) => {
|
|
181
|
-
// Depending if the user already has cookies set, they can be accessed from the request headers or the request address
|
|
182
|
-
const token = req.query.token; // If the token is passed as a query parameter
|
|
183
|
-
|
|
184
|
-
// Perform token validation here
|
|
185
|
-
if (isValidToken(token)) {
|
|
186
|
-
// Extract the authenticated user's username
|
|
187
|
-
const role = getRoleFromToken(token);
|
|
188
|
-
|
|
189
|
-
// Handle socket connection and authentication for valid users
|
|
190
|
-
res.accept({ role }); // Assign the user's role to the socket
|
|
191
|
-
} else {
|
|
192
|
-
// Reject the connection for invalid users or without a token
|
|
193
|
-
res.reject('Invalid token', 401);
|
|
194
|
-
}
|
|
195
|
-
});
|
|
196
|
-
|
|
197
|
-
// Create a channel, providing a callback that is called when a user attempts to join the channel
|
|
198
|
-
const profanityChannel = endpoint.createChannel('/channel/:id', async (req, res) => {
|
|
199
|
-
// When joining the channel, any joinParams passed from the client will be available in the request payload
|
|
200
|
-
// Also any previous assigns on the socket will be available in the request payload as well
|
|
201
|
-
const { role } = req.user.assigns;
|
|
202
|
-
const { username } = req.joinParams;
|
|
203
|
-
const { id } = req.event.params;
|
|
204
|
-
|
|
205
|
-
// maybe retrieve the previous messages from the database
|
|
206
|
-
const messages = await getMessagesFromDatabase(id);
|
|
207
|
-
|
|
208
|
-
// Check if the user has the required role to join the channel
|
|
209
|
-
if (role === 'admin') {
|
|
210
|
-
// Accept the join request
|
|
211
|
-
res.accept({ username, profanityCount: 0 })
|
|
212
|
-
// optionally you can track the presence of the user in the channel
|
|
213
|
-
.trackPresence({
|
|
214
|
-
username,
|
|
215
|
-
role,
|
|
216
|
-
status: 'online',
|
|
217
|
-
onlineSince: Date.now(),
|
|
218
|
-
})
|
|
219
|
-
// and send the user the channel history
|
|
220
|
-
.sendToUsers('history', { messages }, [req.user.id]);
|
|
221
|
-
|
|
222
|
-
// Alternatively, you can also send messages to the user, NOTE that the user would be automatically subscribed to the channel.
|
|
223
|
-
// res.send('history', { messages }, { username, profanityCount: 0 })
|
|
224
|
-
// .trackPresence({
|
|
225
|
-
// username,
|
|
226
|
-
// role,
|
|
227
|
-
// status: 'online',
|
|
228
|
-
// onlineSince: Date.now(),
|
|
229
|
-
// });
|
|
230
|
-
} else {
|
|
231
|
-
// Reject the join request
|
|
232
|
-
res.reject('You do not have the required role to join this channel', 403);
|
|
233
|
-
}
|
|
234
|
-
});
|
|
235
|
-
|
|
236
|
-
// Attach message event listener to the profanityChannel
|
|
237
|
-
profanityChannel.onEvent('message', (req, res) => {
|
|
238
|
-
const { text } = req.event.payload;
|
|
239
|
-
|
|
240
|
-
// Check for profanity
|
|
241
|
-
if (isTextProfane(text)) {
|
|
242
|
-
// Reject the message if it contains profanity
|
|
243
|
-
res.reject('Profanity is not allowed', 400, {
|
|
244
|
-
profanityCount: req.user.assigns.profanityCount + 1
|
|
245
|
-
});
|
|
246
|
-
|
|
247
|
-
// note that profanityCount is updated so req.user.assigns.profanityCount will be updated
|
|
248
|
-
if (req.user.assigns.profanityCount >= 3) {
|
|
249
|
-
// Kick the user from the channel if they have used profanity more than 3 times
|
|
250
|
-
res.evictUser('You have been kicked from the channel for using profanity');
|
|
251
|
-
} else {
|
|
252
|
-
// you can broadcast a message to all users or In the channel that profanity is not allowed
|
|
253
|
-
res.broadcast('profanity-warning', { message: 'Profanity is not allowed' })
|
|
254
|
-
// or you can send a message to the user that profanity is not allowed
|
|
255
|
-
.sendToUsers('profanity-warning', { message: `You have used profanity ${profanityCount} times. You will be kicked from the channel if you use profanity more than 3 times.` }, [req.user.id]);
|
|
256
|
-
}
|
|
257
|
-
} else {
|
|
258
|
-
// Accept the message to allow broadcasting to other clients in the channel
|
|
259
|
-
res.accept();
|
|
260
|
-
}
|
|
261
|
-
|
|
262
|
-
// for more complete access to the channel, you can use the channel instance
|
|
263
|
-
// const channel = req.channel;
|
|
264
|
-
});
|
|
112
|
+
When connected to the channel you can subscribe to the events from the channel.
|
|
265
113
|
|
|
266
|
-
|
|
267
|
-
const
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
// Handle presence events
|
|
271
|
-
res.updatePresence({
|
|
272
|
-
username,
|
|
273
|
-
role,
|
|
274
|
-
onlineSince: Date.now(),
|
|
275
|
-
status: presence,
|
|
114
|
+
```js
|
|
115
|
+
const subscriptionPresence = channel.onPresenceUpdate(presence => {
|
|
116
|
+
// handle the presence changes of the channel
|
|
276
117
|
});
|
|
277
|
-
});
|
|
278
|
-
|
|
279
|
-
profanityChannel.onLeave((event) => {
|
|
280
|
-
const { username } = event.assigns;
|
|
281
|
-
|
|
282
|
-
// When a user leaves the channel, PondSocket will automatically remove the user from the presence list and inform other users in the channel
|
|
283
118
|
|
|
284
|
-
|
|
285
|
-
|
|
119
|
+
const subscriptionMessage = channel.onMessage((event, data) => {
|
|
120
|
+
// handle the message being received
|
|
121
|
+
});
|
|
286
122
|
|
|
287
|
-
//
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
});
|
|
123
|
+
// When done with the channel remember to unsubscribe from these listeners
|
|
124
|
+
subscriptionPresence.unsubscribe();
|
|
125
|
+
subscriptionMessage.unsubscribe();
|
|
291
126
|
```
|
|
292
127
|
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
### PondSocket
|
|
296
|
-
|
|
297
|
-
The `PondSocket` class is the core class that represents the socket server.
|
|
298
|
-
|
|
299
|
-
**Constructor:**
|
|
300
|
-
|
|
301
|
-
- `constructor(server?: HTTPServer, socketServer?: WebSocketServer)`: Creates a new instance of the PondSocket with an optional HTTP server and WebSocket server.
|
|
302
|
-
|
|
303
|
-
**Methods:**
|
|
304
|
-
|
|
305
|
-
- `listen(...args: any[]): HTTPServer`: Specifies the port to listen on with the provided arguments.
|
|
306
|
-
|
|
307
|
-
- `close(callback?: () => void): HTTPServer`: Closes the server, and an optional callback can be provided.
|
|
308
|
-
|
|
309
|
-
- `createEndpoint<Path extends string>(path: PondPath<Path>, handler: (request: IncomingConnection<Path>, response: ConnectionResponse) => void | Promise<void>): Endpoint`: Accepts a new socket upgrade request on the provided endpoint using the handler function to authenticate the socket.
|
|
310
|
-
|
|
311
|
-
### ConnectionResponse
|
|
312
|
-
|
|
313
|
-
The `ConnectionResponse` class represents the response object for the incoming connection.
|
|
314
|
-
|
|
315
|
-
**Methods:**
|
|
316
|
-
|
|
317
|
-
- `accept(assigns?: PondAssigns): void`: Accepts the request and optionally assigns data to the client.
|
|
318
|
-
|
|
319
|
-
- `reject(message?: string, errorCode?: number): void`: Rejects the request with the given error message and optional error code.
|
|
320
|
-
|
|
321
|
-
- `send(event: string, payload: PondMessage, assigns?: PondAssigns): void`: Emits a direct message to the client with the specified event and payload.
|
|
322
|
-
|
|
323
|
-
### Endpoint
|
|
324
|
-
|
|
325
|
-
The `Endpoint` class represents an endpoint in the PondSocket server where channels can be created.
|
|
326
|
-
|
|
327
|
-
**Methods:**
|
|
328
|
-
|
|
329
|
-
- `createChannel<Path extends string>(path: PondPath<Path>, handler: (request: JoinRequest<Path>, response: JoinResponse) => void | Promise<void>): PondChannel`: Adds a new PondChannel to this path on this endpoint with the provided handler function to authenticate the client.
|
|
330
|
-
|
|
331
|
-
- `broadcast(event: string, payload: PondMessage): void`: Broadcasts a message to all clients connected to this endpoint with the specified event and payload.
|
|
332
|
-
|
|
333
|
-
- `closeConnection(clientIds: string | string[]): void`: Closes specific clients connected to this endpoint identified by the provided clientIds.
|
|
334
|
-
|
|
335
|
-
### JoinRequest
|
|
336
|
-
|
|
337
|
-
The `JoinRequest` class represents the request object when a client joins a channel.
|
|
338
|
-
|
|
339
|
-
**Properties:**
|
|
340
|
-
|
|
341
|
-
- `event: PondEvent<Path>`: The event associated with the request.
|
|
342
|
-
|
|
343
|
-
- `channelName: string`: The name of the channel.
|
|
344
|
-
|
|
345
|
-
- `assigns: UserAssigns`: The assigns data for the client.
|
|
346
|
-
|
|
347
|
-
- `presence: UserPresences`: The presence data for the client.
|
|
348
|
-
|
|
349
|
-
- `joinParams: JoinParams`: The join parameters for the client.
|
|
128
|
+
There are many other features available on the channel object. Since the application is completely typed,
|
|
129
|
+
suggestions should be provided by your IDE.
|
|
350
130
|
|
|
351
|
-
|
|
131
|
+
```js
|
|
132
|
+
channel.broadcast('hello', {
|
|
133
|
+
name: 'eleven-am',
|
|
134
|
+
message: 'I am the man, man'
|
|
135
|
+
})
|
|
352
136
|
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
The `JoinResponse` class represents the response object for the join request.
|
|
358
|
-
|
|
359
|
-
**Methods:**
|
|
360
|
-
|
|
361
|
-
- `accept(assigns?: PondAssigns): JoinResponse`: Accepts the join request and optionally assigns data to the client.
|
|
362
|
-
|
|
363
|
-
- `reject(message?: string, errorCode?: number): JoinResponse`: Rejects the join request with the given error message and optional error code.
|
|
364
|
-
|
|
365
|
-
- `send(event: string, payload: PondMessage, assigns?: PondAssigns): JoinResponse`: Emits a direct message to the client with the specified event, payload, and optional assigns data.
|
|
366
|
-
|
|
367
|
-
- `broadcast(event: string, payload: PondMessage): JoinResponse`: Emits a message to all clients in the channel with the specified event and payload.
|
|
368
|
-
|
|
369
|
-
- `broadcastFromUser(event: string, payload: PondMessage): JoinResponse`: Emits a message to all clients in the channel except the sender with the specified event and payload.
|
|
370
|
-
|
|
371
|
-
- `sendToUsers(event: string, payload: PondMessage, userIds: string[]): JoinResponse`: Emits a message to a specific set of clients identified by the provided userIds with the specified event and payload.
|
|
372
|
-
|
|
373
|
-
- `trackPresence(presence: PondPresence): JoinResponse`: Tracks the presence of the client in the channel.
|
|
374
|
-
|
|
375
|
-
### PondChannel
|
|
376
|
-
|
|
377
|
-
The `PondChannel` class represents a Generic channel in the PondSocket server. It is used to create a channel whose path matches the provided PondPath.
|
|
378
|
-
|
|
379
|
-
**Methods:**
|
|
380
|
-
|
|
381
|
-
- `onEvent<Event extends string>(event: PondPath<Event>, handler: (request: EventRequest<Event>, response: EventResponse) => void | Promise<void>): void`: Handles an event request made by a user for the specified event with the provided handler function.
|
|
382
|
-
|
|
383
|
-
- `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.
|
|
384
|
-
|
|
385
|
-
- `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.
|
|
386
|
-
|
|
387
|
-
### EventRequest
|
|
388
|
-
|
|
389
|
-
The `EventRequest` class represents the request object when an event is received from a client.
|
|
390
|
-
|
|
391
|
-
**Properties:**
|
|
392
|
-
|
|
393
|
-
- `event: PondEvent<Path>`: The event associated with the request.
|
|
394
|
-
|
|
395
|
-
- `channelName: string`: The name of the channel.
|
|
396
|
-
|
|
397
|
-
- `assigns: UserAssigns`: The assigns data for the client.
|
|
398
|
-
|
|
399
|
-
- `presence: UserPresences`: The presence data for the client.
|
|
400
|
-
|
|
401
|
-
- `user: UserData`: The user data associated with the client.
|
|
402
|
-
|
|
403
|
-
- `channel: Channel`: The Channel instance associated with the request.
|
|
404
|
-
|
|
405
|
-
### EventResponse
|
|
406
|
-
|
|
407
|
-
The `EventResponse` class represents the response object for handling events from clients.
|
|
408
|
-
|
|
409
|
-
**Methods:**
|
|
410
|
-
|
|
411
|
-
- `accept(assigns?: PondAssigns): EventResponse`: Accepts the request and optionally assigns data to the client.
|
|
412
|
-
|
|
413
|
-
- `reject(message?: string, errorCode?: number, assigns?: PondAssigns): EventResponse`: Rejects the request with the given error message, optional error code, and optional assigns data.
|
|
414
|
-
|
|
415
|
-
- `send(event: string, payload: PondMessage, assigns?: PondAssigns): void`: Emits a direct message to the client with the specified event, payload, and optional assigns data.
|
|
416
|
-
|
|
417
|
-
- `broadcast(event: string, payload: PondMessage): EventResponse`: Sends a message to all clients in the channel with the specified event and payload.
|
|
418
|
-
|
|
419
|
-
- `broadcastFromUser(event: string, payload: PondMessage): EventResponse`: Sends a message to all clients in the channel except the sender with the specified event and payload.
|
|
420
|
-
|
|
421
|
-
- `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.
|
|
422
|
-
|
|
423
|
-
- `trackPresence(presence: PondPresence, userId?: string): EventResponse`: Tracks a user's presence in the channel.
|
|
424
|
-
|
|
425
|
-
- `updatePresence(presence: PondPresence, userId?: string): EventResponse`: Updates a user's presence in the channel.
|
|
426
|
-
|
|
427
|
-
- `unTrackPresence(userId?: string): EventResponse`: Removes a user's presence from the channel.
|
|
428
|
-
|
|
429
|
-
- `evictUser(reason: string, userId?: string): void`: Evicts a user from the channel.
|
|
430
|
-
|
|
431
|
-
- `closeChannel(reason: string): void`: Closes the channel from the server-side for all clients.
|
|
432
|
-
|
|
433
|
-
### Channel
|
|
434
|
-
|
|
435
|
-
The `Channel` class represents a single Channel created by the PondSocket server. Note that a PondChannel can have multiple channels associated with it.
|
|
436
|
-
|
|
437
|
-
**Methods:**
|
|
438
|
-
|
|
439
|
-
- `name: string`: The name of the channel.
|
|
440
|
-
|
|
441
|
-
- `getAssigns: UserAssigns`: Gets the current assign data for the client.
|
|
442
|
-
|
|
443
|
-
- `getUserData(userId: string): UserData`: Gets the assign data for a specific user identified by the provided `userId`.
|
|
444
|
-
|
|
445
|
-
- `broadcastMessage(event: string, payload: PondMessage): void`: Broadcasts a message to every client in the channel with the specified event and payload.
|
|
446
|
-
|
|
447
|
-
- `sendToUser(userId: string, event: string, payload: PondMessage): void`: Sends a message to a specific client in the channel identified by the provided `userId`, with the specified event and payload.
|
|
448
|
-
|
|
449
|
-
- `sendToUsers(userIdS: string[], event: string, payload: PondMessage): void`: Sends a message to a specific set of clients identified by the provided `userIdS`, with the specified event and payload.
|
|
450
|
-
|
|
451
|
-
- `evictUser(userId: string, reason?: string): void`: Bans a user from the channel identified by the provided `userId`. Optionally, you can provide a `reason` for the ban.
|
|
452
|
-
|
|
453
|
-
- `trackPresence(userId: string, presence: PondPresence): void`: Tracks a user's presence in the channel identified by the provided `userId`.
|
|
454
|
-
|
|
455
|
-
- `removePresence(userId: string): void`: Removes a user's presence from the channel identified by the provided `userId`.
|
|
456
|
-
|
|
457
|
-
- `updatePresence(userId: string, presence: PondPresence): void`: Updates a user's presence in the channel identified by the provided `userId`.
|
|
458
|
-
|
|
459
|
-
### PondClient
|
|
460
|
-
|
|
461
|
-
The `PondClient` class represents a client that connects to the PondSocket server.
|
|
462
|
-
|
|
463
|
-
**Constructor:**
|
|
464
|
-
|
|
465
|
-
- `constructor(endpoint: string, params?: Record<string, any>)`: Creates a new instance of the PondClient with the provided endpoint URL and optional parameters.
|
|
466
|
-
|
|
467
|
-
**Methods:**
|
|
468
|
-
|
|
469
|
-
- `connect(backoff?: number): void`: Connects to the server with an optional backoff time.
|
|
470
|
-
|
|
471
|
-
- `getState(): boolean`: Returns the current state of the socket.
|
|
472
|
-
|
|
473
|
-
- `disconnect(): void`: Disconnects the socket.
|
|
474
|
-
|
|
475
|
-
- `createChannel(name: string, params?: JoinParams): ClientChannel`: Creates a channel with the given name and optional join parameters.
|
|
476
|
-
|
|
477
|
-
- `onConnectionChange(callback: (state: boolean) => void): Unsubscribe`: Subscribes to the connection state changes and calls the provided callback when the state changes.
|
|
478
|
-
|
|
479
|
-
### ClientChannel
|
|
480
|
-
|
|
481
|
-
The `ClientChannel` class represents a channel in the PondClient.
|
|
482
|
-
|
|
483
|
-
**Methods:**
|
|
484
|
-
|
|
485
|
-
- `join(): void`: Connects to the channel.
|
|
486
|
-
|
|
487
|
-
- `leave(): void`: Disconnects from the channel.
|
|
488
|
-
|
|
489
|
-
- `onMessage(callback: (event: string, message: PondMessage) => void): Unsubscribe`: Monitors the channel for messages and calls the provided callback when a message is received.
|
|
490
|
-
|
|
491
|
-
- `onMessageEvent(event: string, callback: (message: PondMessage) => void): Unsubscribe`: Monitors the channel for messages with the specified event and calls the provided callback when a message is received.
|
|
492
|
-
|
|
493
|
-
- `onChannelStateChange(callback: (connected: ChannelState) => void): Unsubscribe`: Monitors the channel state of the channel and calls the provided callback when the connection state changes.
|
|
494
|
-
|
|
495
|
-
- `onJoin(callback: (presence: PondPresence) => void): Unsubscribe`: Detects when clients join the channel and calls the provided callback when a client joins the channel.
|
|
496
|
-
|
|
497
|
-
- `onLeave(callback: (presence: PondPresence) => void): Unsubscribe`: Detects when clients leave the channel and calls the provided callback when a client leaves the channel.
|
|
498
|
-
|
|
499
|
-
- `onPresenceChange(callback: (presence: PresencePayload) => void): Unsubscribe`: Detects when clients change their presence in the channel and calls the provided callback when a client changes their presence in the channel.
|
|
500
|
-
|
|
501
|
-
- `sendMessage(event: string, payload: PondMessage, recipient: string[]): void`: Sends a message to specific clients in the channel with the specified event, payload, and recipient.
|
|
502
|
-
|
|
503
|
-
- `broadcastFrom(event: string, payload: PondMessage): void`: Broadcasts a message to every other client in the channel except yourself with the specified event and payload.
|
|
504
|
-
|
|
505
|
-
- `broadcast(event: string, payload: PondMessage): void`: Broadcasts a message to the channel, including yourself, with the specified event and payload.
|
|
506
|
-
|
|
507
|
-
- `getPresence(): PondPresence[]`: Gets the current presence of the channel.
|
|
508
|
-
|
|
509
|
-
- `onUsersChange(callback: (users: PondPresence[]) => void): Unsubscribe`: Monitors the presence of the channel and calls the provided callback when the presence changes.
|
|
510
|
-
|
|
511
|
-
- `isConnected(): boolean`: Gets the current connection state of the channel.
|
|
512
|
-
|
|
513
|
-
- `onConnectionChange(callback: (connected: boolean) => void): Unsubscribe`: Monitors the connection state of the channel and calls the provided callback when the connection state changes.
|
|
514
|
-
|
|
515
|
-
## License
|
|
516
|
-
|
|
517
|
-
PondSocket is released under the GPL-3.0 License. Please refer to the `LICENSE` file for detailed licensing information.
|
|
518
|
-
|
|
519
|
-
## Conclusion
|
|
520
|
-
|
|
521
|
-
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.
|
|
137
|
+
// channel.broadcastFrom broadcasts a message to everyone but the client that emitted the message
|
|
138
|
+
// channel.sendMessage sends a message to clients specified in the function
|
|
139
|
+
```
|
package/lobby/lobby.js
CHANGED
|
@@ -188,6 +188,17 @@ class PondChannel {
|
|
|
188
188
|
onLeave(callback) {
|
|
189
189
|
__classPrivateFieldGet(this, _PondChannel_lobby, "f").onLeave(callback);
|
|
190
190
|
}
|
|
191
|
+
/**
|
|
192
|
+
* @desc Gets a channel by name
|
|
193
|
+
* @param channelName - The name of the channel to get
|
|
194
|
+
*/
|
|
195
|
+
getChannel(channelName) {
|
|
196
|
+
const channel = __classPrivateFieldGet(this, _PondChannel_lobby, "f").getChannel(channelName);
|
|
197
|
+
if (channel) {
|
|
198
|
+
return new channel_1.Channel(channel);
|
|
199
|
+
}
|
|
200
|
+
return null;
|
|
201
|
+
}
|
|
191
202
|
}
|
|
192
203
|
exports.PondChannel = PondChannel;
|
|
193
204
|
_PondChannel_lobby = new WeakMap();
|
package/nest.d.ts
CHANGED
|
@@ -2,7 +2,7 @@ import {
|
|
|
2
2
|
OnJoinRequest,
|
|
3
3
|
ChannelInstance,
|
|
4
4
|
DChannel as Channel,
|
|
5
|
-
Channels,
|
|
5
|
+
Channels,
|
|
6
6
|
OnConnectionRequest,
|
|
7
7
|
DEndpoint as Endpoint,
|
|
8
8
|
OnEvent, PondSocketModule,
|
|
@@ -19,9 +19,9 @@ import {
|
|
|
19
19
|
export {
|
|
20
20
|
OnJoinRequest,
|
|
21
21
|
ChannelInstance,
|
|
22
|
+
OnConnectionRequest,
|
|
22
23
|
Endpoint, PondSocketModule,
|
|
23
|
-
|
|
24
|
-
Channel, Channels, Endpoints,
|
|
24
|
+
OnEvent, Channel, Channels,
|
|
25
25
|
GetEventQuery, GetJoinParams,
|
|
26
26
|
GetEventParams, GetJoinResponse,
|
|
27
27
|
GetEventRequest, GetJoinRequest,
|
package/nest.js
CHANGED
|
@@ -20,9 +20,10 @@ var __rest = (this && this.__rest) || function (s, e) {
|
|
|
20
20
|
return t;
|
|
21
21
|
};
|
|
22
22
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
23
|
-
exports.PondSocketModule = exports.
|
|
23
|
+
exports.PondSocketModule = exports.Endpoint = exports.Channel = exports.EndpointInstance = exports.ChannelInstance = exports.OnConnectionRequest = exports.OnEvent = exports.OnLeaveEvent = exports.OnJoinRequest = exports.GetConnectionQuery = exports.GetConnectionHeaders = exports.GetConnectionParams = exports.GetConnectionRequestId = exports.GetConnectionResponse = exports.GetConnectionRequest = exports.GetEventRequest = exports.GetEventResponse = exports.GetEventQuery = exports.GetEventParams = exports.GetEventPayload = exports.GetUserPresences = exports.GetInternalChannel = exports.GetUserData = exports.GetJoinParams = exports.GetJoinResponse = exports.GetJoinRequest = void 0;
|
|
24
24
|
const http_1 = require("http");
|
|
25
25
|
const common_1 = require("@nestjs/common");
|
|
26
|
+
const core_1 = require("@nestjs/core");
|
|
26
27
|
require("reflect-metadata");
|
|
27
28
|
const pondSocket_1 = require("./server/pondSocket");
|
|
28
29
|
const joinRequestKey = Symbol('joinRequestKey');
|
|
@@ -46,10 +47,10 @@ const onJoinHandlerKey = Symbol('onJoinHandlerKey');
|
|
|
46
47
|
const onEventHandlerKey = Symbol('onEventHandlerKey');
|
|
47
48
|
const onConnectionHandlerKey = Symbol('onConnectionHandlerKey');
|
|
48
49
|
const channelInstanceKey = Symbol('channelInstanceKey');
|
|
50
|
+
const endpointInstanceKey = Symbol('endpointInstanceKey');
|
|
49
51
|
const channelClassKey = Symbol('channel');
|
|
50
52
|
const endpointClassKey = Symbol('endpoint');
|
|
51
53
|
const channelsClassKey = Symbol('channels');
|
|
52
|
-
const endpointsClassKey = Symbol('endpoints');
|
|
53
54
|
function isNotEmpty(value) {
|
|
54
55
|
return value !== null &&
|
|
55
56
|
value !== undefined &&
|
|
@@ -57,6 +58,7 @@ function isNotEmpty(value) {
|
|
|
57
58
|
Object.keys(value).length !== 0;
|
|
58
59
|
}
|
|
59
60
|
function createClassDecorator(key, value) {
|
|
61
|
+
// eslint-disable-next-line new-cap
|
|
60
62
|
return (0, common_1.applyDecorators)((0, common_1.Injectable)(), (0, common_1.SetMetadata)(key, value));
|
|
61
63
|
}
|
|
62
64
|
function getClassMetadata(key, target) {
|
|
@@ -107,8 +109,30 @@ function manageHandlers(key, target) {
|
|
|
107
109
|
},
|
|
108
110
|
};
|
|
109
111
|
}
|
|
110
|
-
function
|
|
111
|
-
|
|
112
|
+
function managePropertyData(key, target) {
|
|
113
|
+
function build(propertyKey, callback) {
|
|
114
|
+
Object.defineProperty(target, propertyKey, {
|
|
115
|
+
get() {
|
|
116
|
+
const value = Reflect.getMetadata(key, this);
|
|
117
|
+
if (callback) {
|
|
118
|
+
return callback(value);
|
|
119
|
+
}
|
|
120
|
+
return value;
|
|
121
|
+
},
|
|
122
|
+
set() {
|
|
123
|
+
throw new Error(`${propertyKey} is readonly`);
|
|
124
|
+
},
|
|
125
|
+
enumerable: true,
|
|
126
|
+
configurable: true,
|
|
127
|
+
});
|
|
128
|
+
}
|
|
129
|
+
function set(value) {
|
|
130
|
+
Reflect.defineMetadata(key, value, target);
|
|
131
|
+
}
|
|
132
|
+
return {
|
|
133
|
+
build,
|
|
134
|
+
set,
|
|
135
|
+
};
|
|
112
136
|
}
|
|
113
137
|
function manageJoinHandlers(target) {
|
|
114
138
|
return manageHandlers(onJoinHandlerKey, target);
|
|
@@ -119,6 +143,15 @@ function manageEventHandlers(target) {
|
|
|
119
143
|
function manageConnectionHandlers(target) {
|
|
120
144
|
return manageHandlers(onConnectionHandlerKey, target);
|
|
121
145
|
}
|
|
146
|
+
function manageOnLeaveHandlers(target) {
|
|
147
|
+
return manageHandlers(onConnectionHandlerKey, target);
|
|
148
|
+
}
|
|
149
|
+
function manageChannelInstance(target) {
|
|
150
|
+
return managePropertyData(channelInstanceKey, target);
|
|
151
|
+
}
|
|
152
|
+
function manageEndpointInstance(target) {
|
|
153
|
+
return managePropertyData(endpointInstanceKey, target);
|
|
154
|
+
}
|
|
122
155
|
function GetJoinRequest() {
|
|
123
156
|
return createParamDecorator(joinRequestKey, 'JoinRequest decorator already applied');
|
|
124
157
|
}
|
|
@@ -302,6 +335,35 @@ function resolveEventParameters(request, response, target, propertyKey) {
|
|
|
302
335
|
}
|
|
303
336
|
});
|
|
304
337
|
}
|
|
338
|
+
function resolveLeaveParameters(event, target, propertyKey) {
|
|
339
|
+
const userDataIndex = resolveParamDecorator(userDataKey, target, propertyKey);
|
|
340
|
+
const rejectedKeys = [
|
|
341
|
+
joinRequestKey,
|
|
342
|
+
joinResponseKey,
|
|
343
|
+
joinParamsKey,
|
|
344
|
+
connectionRequestKey,
|
|
345
|
+
connectionResponseKey,
|
|
346
|
+
connectionRequestIdKey,
|
|
347
|
+
connectionParamsKey,
|
|
348
|
+
connectionQueryKey,
|
|
349
|
+
eventParamsKey,
|
|
350
|
+
eventQueryKey,
|
|
351
|
+
eventPayloadKey,
|
|
352
|
+
eventResponseKey,
|
|
353
|
+
eventRequestKey,
|
|
354
|
+
userPresenceKey,
|
|
355
|
+
internalChannelKey,
|
|
356
|
+
]
|
|
357
|
+
.map((key) => resolveParamDecorator(key, target, propertyKey))
|
|
358
|
+
.filter((index) => typeof index === 'number');
|
|
359
|
+
if (rejectedKeys.length) {
|
|
360
|
+
throw new Error(`Invalid parameter decorators: ${rejectedKeys.join(', ')}`);
|
|
361
|
+
}
|
|
362
|
+
if (userDataIndex === null) {
|
|
363
|
+
return [];
|
|
364
|
+
}
|
|
365
|
+
return [event.assigns];
|
|
366
|
+
}
|
|
305
367
|
function resolveConnectionParameters(request, response, target, propertyKey) {
|
|
306
368
|
const connectionRequestIndex = resolveParamDecorator(connectionRequestKey, target, propertyKey);
|
|
307
369
|
const connectionResponseIndex = resolveParamDecorator(connectionResponseKey, target, propertyKey);
|
|
@@ -390,6 +452,23 @@ function OnJoinRequest() {
|
|
|
390
452
|
};
|
|
391
453
|
}
|
|
392
454
|
exports.OnJoinRequest = OnJoinRequest;
|
|
455
|
+
function OnLeaveEvent() {
|
|
456
|
+
return (target, propertyKey, descriptor) => {
|
|
457
|
+
const originalMethod = descriptor.value;
|
|
458
|
+
const { set } = manageOnLeaveHandlers(target);
|
|
459
|
+
set('', (instance, event) => __awaiter(this, void 0, void 0, function* () {
|
|
460
|
+
try {
|
|
461
|
+
yield originalMethod.apply(instance, resolveLeaveParameters(event, target, propertyKey));
|
|
462
|
+
}
|
|
463
|
+
catch (error) {
|
|
464
|
+
if (error instanceof Error) {
|
|
465
|
+
console.error(error.message);
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}));
|
|
469
|
+
};
|
|
470
|
+
}
|
|
471
|
+
exports.OnLeaveEvent = OnLeaveEvent;
|
|
393
472
|
function OnEvent(event = '*') {
|
|
394
473
|
return (target, propertyKey, descriptor) => {
|
|
395
474
|
const originalMethod = descriptor.value;
|
|
@@ -458,20 +537,25 @@ function OnConnectionRequest() {
|
|
|
458
537
|
};
|
|
459
538
|
}
|
|
460
539
|
exports.OnConnectionRequest = OnConnectionRequest;
|
|
461
|
-
function ChannelInstance() {
|
|
540
|
+
function ChannelInstance(name) {
|
|
462
541
|
return (target, propertyKey) => {
|
|
463
|
-
const {
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
return
|
|
467
|
-
}
|
|
468
|
-
|
|
469
|
-
throw new Error('ChannelInstance is readonly');
|
|
470
|
-
},
|
|
542
|
+
const { build } = manageChannelInstance(target);
|
|
543
|
+
build(propertyKey, (value) => {
|
|
544
|
+
if (name) {
|
|
545
|
+
return value.getChannel(name);
|
|
546
|
+
}
|
|
547
|
+
return value;
|
|
471
548
|
});
|
|
472
549
|
};
|
|
473
550
|
}
|
|
474
551
|
exports.ChannelInstance = ChannelInstance;
|
|
552
|
+
function EndpointInstance() {
|
|
553
|
+
return (target, propertyKey) => {
|
|
554
|
+
const { build } = manageEndpointInstance(target);
|
|
555
|
+
build(propertyKey);
|
|
556
|
+
};
|
|
557
|
+
}
|
|
558
|
+
exports.EndpointInstance = EndpointInstance;
|
|
475
559
|
const Channel = (path = '*') => createClassDecorator(channelClassKey, path);
|
|
476
560
|
exports.Channel = Channel;
|
|
477
561
|
const SetEndpoint = (path = '*') => createClassDecorator(endpointClassKey, path);
|
|
@@ -480,33 +564,20 @@ const getChannels = (target) => {
|
|
|
480
564
|
var _a;
|
|
481
565
|
return (_a = getClassMetadata(channelsClassKey, target)) !== null && _a !== void 0 ? _a : [];
|
|
482
566
|
};
|
|
567
|
+
// eslint-disable-next-line new-cap
|
|
483
568
|
const Endpoint = (metadata) => (0, common_1.applyDecorators)(SetChannels(metadata.channels), SetEndpoint(metadata.path));
|
|
484
569
|
exports.Endpoint = Endpoint;
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
return (0, common_1.applyDecorators)(
|
|
488
|
-
// eslint-disable-next-line new-cap
|
|
489
|
-
(0, common_1.SetMetadata)(endpointsClassKey, endpoints),
|
|
490
|
-
// eslint-disable-next-line new-cap
|
|
491
|
-
(0, common_1.Module)({
|
|
492
|
-
imports,
|
|
493
|
-
providers: [...providers, ...endpoints, ...channels],
|
|
494
|
-
exports: [...exports, ...channels],
|
|
495
|
-
}))(target);
|
|
496
|
-
};
|
|
497
|
-
exports.Endpoints = Endpoints;
|
|
498
|
-
class PondSocketModule {
|
|
499
|
-
constructor(moduleRef, adapterHost) {
|
|
500
|
-
var _a;
|
|
570
|
+
class PondSocketService {
|
|
571
|
+
constructor(moduleRef, adapterHost, endpoints) {
|
|
501
572
|
this.moduleRef = moduleRef;
|
|
502
573
|
this.adapterHost = adapterHost;
|
|
574
|
+
this.endpoints = endpoints;
|
|
503
575
|
const httpAdapter = this.adapterHost.httpAdapter;
|
|
504
|
-
const endpoints = (_a = getClassMetadata(endpointsClassKey, this.constructor)) !== null && _a !== void 0 ? _a : [];
|
|
505
576
|
httpAdapter.listen = (...args) => {
|
|
506
577
|
const app = httpAdapter.getInstance();
|
|
507
578
|
const server = (0, http_1.createServer)(app);
|
|
508
579
|
const socket = new pondSocket_1.PondSocket(server);
|
|
509
|
-
endpoints.forEach((endpoint) => this.manageEndpoint(socket, endpoint));
|
|
580
|
+
this.endpoints.forEach((endpoint) => this.manageEndpoint(socket, endpoint));
|
|
510
581
|
return socket.listen(...args);
|
|
511
582
|
};
|
|
512
583
|
}
|
|
@@ -516,6 +587,7 @@ class PondSocketModule {
|
|
|
516
587
|
return;
|
|
517
588
|
}
|
|
518
589
|
const instance = this.moduleRef.get(endpoint, { strict: false });
|
|
590
|
+
const { set } = manageEndpointInstance(instance);
|
|
519
591
|
const pondEndpoint = socket.createEndpoint(endpointMetadata, (request, response) => __awaiter(this, void 0, void 0, function* () {
|
|
520
592
|
const { get } = manageConnectionHandlers(instance);
|
|
521
593
|
const [handler] = get();
|
|
@@ -526,6 +598,7 @@ class PondSocketModule {
|
|
|
526
598
|
response.accept();
|
|
527
599
|
}
|
|
528
600
|
}));
|
|
601
|
+
set(pondEndpoint);
|
|
529
602
|
getChannels(endpoint).forEach((channel) => {
|
|
530
603
|
this.manageChannel(channel, pondEndpoint);
|
|
531
604
|
});
|
|
@@ -547,13 +620,37 @@ class PondSocketModule {
|
|
|
547
620
|
}
|
|
548
621
|
}));
|
|
549
622
|
const { get: getEventHandlers } = manageEventHandlers(instance);
|
|
623
|
+
const { get: getLeaveHandlers } = manageOnLeaveHandlers(instance);
|
|
550
624
|
const { set } = manageChannelInstance(instance);
|
|
551
625
|
getEventHandlers().forEach((handler) => {
|
|
552
626
|
channelInstance.onEvent(handler.path, (request, response) => __awaiter(this, void 0, void 0, function* () {
|
|
553
627
|
yield handler.value(instance, request, response);
|
|
554
628
|
}));
|
|
555
629
|
});
|
|
630
|
+
const [leaveHandler] = getLeaveHandlers();
|
|
631
|
+
if (leaveHandler) {
|
|
632
|
+
channelInstance.onLeave((event) => __awaiter(this, void 0, void 0, function* () {
|
|
633
|
+
yield leaveHandler.value(instance, event);
|
|
634
|
+
}));
|
|
635
|
+
}
|
|
556
636
|
set(channelInstance);
|
|
557
637
|
}
|
|
558
638
|
}
|
|
639
|
+
class PondSocketModule {
|
|
640
|
+
static forRoot({ endpoints, providers = [], imports = [], exports = [], isGlobal = false, }) {
|
|
641
|
+
const channels = endpoints.flatMap((endpoint) => getChannels(endpoint));
|
|
642
|
+
const pondSocketProvider = {
|
|
643
|
+
provide: PondSocketService,
|
|
644
|
+
useFactory: (moduleRef, adapterHost) => new PondSocketService(moduleRef, adapterHost, endpoints),
|
|
645
|
+
inject: [core_1.ModuleRef, core_1.HttpAdapterHost],
|
|
646
|
+
};
|
|
647
|
+
return {
|
|
648
|
+
module: PondSocketModule,
|
|
649
|
+
imports: [...imports, ...endpoints, ...channels],
|
|
650
|
+
providers: [...providers, ...endpoints, ...channels, pondSocketProvider],
|
|
651
|
+
exports: [...exports, ...channels],
|
|
652
|
+
global: isGlobal,
|
|
653
|
+
};
|
|
654
|
+
}
|
|
655
|
+
}
|
|
559
656
|
exports.PondSocketModule = PondSocketModule;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eleven-am/pondsocket",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.90",
|
|
4
4
|
"description": "PondSocket is a fast simple socket server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"socket",
|
|
@@ -29,13 +29,12 @@
|
|
|
29
29
|
"url": "git+https://github.com/Eleven-am/pondSocket.git"
|
|
30
30
|
},
|
|
31
31
|
"dependencies": {
|
|
32
|
-
"@nestjs/common": "^10.3.0",
|
|
33
|
-
"@nestjs/core": "^10.3.0",
|
|
34
|
-
"reflect-metadata": "^0.1.14",
|
|
35
32
|
"websocket": "^1.0.34",
|
|
36
33
|
"ws": "^8.15.1"
|
|
37
34
|
},
|
|
38
35
|
"devDependencies": {
|
|
36
|
+
"@nestjs/common": "^10.3.0",
|
|
37
|
+
"@nestjs/core": "^10.3.0",
|
|
39
38
|
"@types/express": "^4.17.21",
|
|
40
39
|
"@types/jest": "^29.5.11",
|
|
41
40
|
"@types/node": "^20.10.5",
|
|
@@ -46,6 +45,7 @@
|
|
|
46
45
|
"eslint-plugin-file-progress": "^1.3.0",
|
|
47
46
|
"eslint-plugin-import": "^2.29.1",
|
|
48
47
|
"jest": "^29.7.0",
|
|
48
|
+
"reflect-metadata": "^0.1.14",
|
|
49
49
|
"superwstest": "^2.0.3",
|
|
50
50
|
"ts-jest": "^29.1.1",
|
|
51
51
|
"ts-node": "^10.9.2",
|
package/types.d.ts
CHANGED
|
@@ -1,7 +1,6 @@
|
|
|
1
1
|
import { Server as HTTPServer, IncomingHttpHeaders } from 'http';
|
|
2
2
|
|
|
3
|
-
import { ModuleMetadata } from '@nestjs/common';
|
|
4
|
-
import type { ModuleRef, HttpAdapterHost } from '@nestjs/core';
|
|
3
|
+
import { ModuleMetadata, DynamicModule } from '@nestjs/common';
|
|
5
4
|
import type { Express } from 'express';
|
|
6
5
|
import { WebSocketServer } from 'ws';
|
|
7
6
|
|
|
@@ -550,6 +549,12 @@ export declare class PondChannel {
|
|
|
550
549
|
* @param callback - The callback to execute when a user leaves
|
|
551
550
|
*/
|
|
552
551
|
public onLeave (callback: LeaveCallback): void;
|
|
552
|
+
|
|
553
|
+
/**
|
|
554
|
+
* @desc Gets a channel by name
|
|
555
|
+
* @param channelName - The name of the channel to get
|
|
556
|
+
*/
|
|
557
|
+
public getChannel (channelName: string): Channel | null;
|
|
553
558
|
}
|
|
554
559
|
|
|
555
560
|
declare class PondSocket {
|
|
@@ -756,6 +761,11 @@ declare function OnJoinRequest(): MethodDecorator;
|
|
|
756
761
|
*/
|
|
757
762
|
declare function OnEvent(event?: string): MethodDecorator;
|
|
758
763
|
|
|
764
|
+
/**
|
|
765
|
+
* @desc Marks a method as a handler for Leave events. Useful for cleaning up resources when a user leaves.
|
|
766
|
+
*/
|
|
767
|
+
declare function OnLeaveEvent (): MethodDecorator;
|
|
768
|
+
|
|
759
769
|
/**
|
|
760
770
|
* @desc Marks a method as a handler for ConnectionRequest events. Throwing an error will reject the request with the error message.
|
|
761
771
|
*/
|
|
@@ -769,8 +779,15 @@ declare function DChannel(path?: string): ClassDecorator;
|
|
|
769
779
|
|
|
770
780
|
/**
|
|
771
781
|
* Decorator to retrieve the channel instance as a read-only property in a class.
|
|
782
|
+
* It may either return an instance of the PondChannel if no name is provided otherwise an instance of Channel or null (depending on if a client is connected)
|
|
783
|
+
* @param name - The name of the channel to return
|
|
772
784
|
*/
|
|
773
|
-
declare function ChannelInstance(): PropertyDecorator;
|
|
785
|
+
declare function ChannelInstance(name?: string): PropertyDecorator;
|
|
786
|
+
|
|
787
|
+
/**
|
|
788
|
+
* Decorator to retrieve the endpoint instance as a read-only property in a class.
|
|
789
|
+
*/
|
|
790
|
+
declare function EndpointInstance (): PropertyDecorator;
|
|
774
791
|
|
|
775
792
|
/**
|
|
776
793
|
* Decorator to mark a class as having multiple channels.
|
|
@@ -784,17 +801,10 @@ declare function Channels(channels: Constructor<NonNullable<unknown>>[]): ClassD
|
|
|
784
801
|
*/
|
|
785
802
|
declare function DEndpoint(metadata: EndpointMetadata): ClassDecorator;
|
|
786
803
|
|
|
787
|
-
/**
|
|
788
|
-
* Decorator to mark a class as having multiple endpoints.
|
|
789
|
-
* @param metadata - The metadata for the endpoints.
|
|
790
|
-
*/
|
|
791
|
-
declare function Endpoints(metadata: Metadata): ClassDecorator;
|
|
792
|
-
|
|
793
804
|
declare class PondSocketModule {
|
|
794
805
|
/**
|
|
795
|
-
* @desc
|
|
796
|
-
* @param
|
|
797
|
-
* @param adapterHost - The NestJS adapterHost
|
|
806
|
+
* @desc Creates a new PondSocketModule
|
|
807
|
+
* @param metadata - The metadata for the module
|
|
798
808
|
*/
|
|
799
|
-
|
|
809
|
+
static forRoot (metadata: Metadata): DynamicModule;
|
|
800
810
|
}
|