@eleven-am/pondsocket 0.1.0
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/.eslintrc.js +28 -0
- package/.idea/modules.xml +8 -0
- package/.idea/pondsocket.iml +12 -0
- package/LICENSE +674 -0
- package/base.d.ts +1 -0
- package/base.js +17 -0
- package/client.d.ts +1 -0
- package/client.js +17 -0
- package/index.d.ts +1 -0
- package/index.js +17 -0
- package/jest.config.js +11 -0
- package/package.json +48 -0
- package/pondBase/baseClass.d.ts +37 -0
- package/pondBase/baseClass.js +111 -0
- package/pondBase/baseClass.test.js +73 -0
- package/pondBase/enums.d.ts +9 -0
- package/pondBase/enums.js +14 -0
- package/pondBase/index.d.ts +6 -0
- package/pondBase/index.js +22 -0
- package/pondBase/pondBase.d.ts +41 -0
- package/pondBase/pondBase.js +60 -0
- package/pondBase/pondBase.test.js +101 -0
- package/pondBase/pubSub.d.ts +73 -0
- package/pondBase/pubSub.js +138 -0
- package/pondBase/pubSub.test.js +309 -0
- package/pondBase/simpleBase.d.ts +131 -0
- package/pondBase/simpleBase.js +211 -0
- package/pondBase/simpleBase.test.js +153 -0
- package/pondBase/types.d.ts +2 -0
- package/pondBase/types.js +2 -0
- package/pondClient/channel.d.ts +66 -0
- package/pondClient/channel.js +152 -0
- package/pondClient/index.d.ts +2 -0
- package/pondClient/index.js +18 -0
- package/pondClient/socket.d.ts +42 -0
- package/pondClient/socket.js +116 -0
- package/pondSocket/channel.d.ts +134 -0
- package/pondSocket/channel.js +287 -0
- package/pondSocket/channel.test.js +377 -0
- package/pondSocket/channelMiddleWare.d.ts +26 -0
- package/pondSocket/channelMiddleWare.js +36 -0
- package/pondSocket/endpoint.d.ts +90 -0
- package/pondSocket/endpoint.js +323 -0
- package/pondSocket/endpoint.test.js +513 -0
- package/pondSocket/enums.d.ts +19 -0
- package/pondSocket/enums.js +25 -0
- package/pondSocket/index.d.ts +7 -0
- package/pondSocket/index.js +23 -0
- package/pondSocket/pondChannel.d.ts +79 -0
- package/pondSocket/pondChannel.js +219 -0
- package/pondSocket/pondChannel.test.js +430 -0
- package/pondSocket/pondResponse.d.ts +25 -0
- package/pondSocket/pondResponse.js +120 -0
- package/pondSocket/pondSocket.d.ts +47 -0
- package/pondSocket/pondSocket.js +94 -0
- package/pondSocket/server.test.js +136 -0
- package/pondSocket/socketMiddleWare.d.ts +6 -0
- package/pondSocket/socketMiddleWare.js +32 -0
- package/pondSocket/types.d.ts +74 -0
- package/pondSocket/types.js +2 -0
- package/socket.d.ts +1 -0
- package/socket.js +17 -0
- package/tsconfig.eslint.json +5 -0
- package/tsconfig.json +90 -0
|
@@ -0,0 +1,323 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Endpoint = void 0;
|
|
4
|
+
const enums_1 = require("./enums");
|
|
5
|
+
const pondBase_1 = require("../pondBase");
|
|
6
|
+
const pondChannel_1 = require("./pondChannel");
|
|
7
|
+
const pondResponse_1 = require("./pondResponse");
|
|
8
|
+
class Endpoint extends pondBase_1.BaseClass {
|
|
9
|
+
constructor(server, handler) {
|
|
10
|
+
super();
|
|
11
|
+
this._channels = new pondBase_1.SimpleBase();
|
|
12
|
+
this._sockets = new pondBase_1.SimpleBase();
|
|
13
|
+
this._handler = handler;
|
|
14
|
+
this._server = server;
|
|
15
|
+
}
|
|
16
|
+
/**
|
|
17
|
+
* @desc Sends a message to a client
|
|
18
|
+
* @param socket - The socket to send the message to
|
|
19
|
+
* @param message - The message to send
|
|
20
|
+
*/
|
|
21
|
+
static _sendMessage(socket, message) {
|
|
22
|
+
socket.send(JSON.stringify(message));
|
|
23
|
+
}
|
|
24
|
+
/**
|
|
25
|
+
* @desc Accepts a new socket join request to the room provided using the handler function to authorise the socket
|
|
26
|
+
* @param path - the pattern to accept || can also be a regex
|
|
27
|
+
* @param handler - the handler function to authenticate the socket
|
|
28
|
+
*
|
|
29
|
+
* @example
|
|
30
|
+
* const channel = endpoint.createChannel('channel:*', (req, res) => {
|
|
31
|
+
* const isAdmin = req.clientAssigns.admin;
|
|
32
|
+
* if (!isAdmin)
|
|
33
|
+
* return res.reject("You are not an admin");
|
|
34
|
+
*
|
|
35
|
+
* res.accept({
|
|
36
|
+
* assign: {
|
|
37
|
+
* admin: true,
|
|
38
|
+
* joinedDate: new Date()
|
|
39
|
+
* },
|
|
40
|
+
* presence: {state: 'online'},
|
|
41
|
+
* channelData: {private: true}
|
|
42
|
+
* });
|
|
43
|
+
* });
|
|
44
|
+
*
|
|
45
|
+
* channel.on('ping', (req, res, channel) => {
|
|
46
|
+
* const users = channel.getPresence();
|
|
47
|
+
* res.assign({
|
|
48
|
+
* assign: {
|
|
49
|
+
* pingDate: new Date(),
|
|
50
|
+
* users: users.length
|
|
51
|
+
* }
|
|
52
|
+
* });
|
|
53
|
+
* })
|
|
54
|
+
*/
|
|
55
|
+
createChannel(path, handler) {
|
|
56
|
+
const pondChannel = new pondChannel_1.PondChannel(path, handler);
|
|
57
|
+
this._channels.set(path.toString(), pondChannel);
|
|
58
|
+
return pondChannel;
|
|
59
|
+
}
|
|
60
|
+
/**
|
|
61
|
+
* @desc Authenticates the client to the endpoint
|
|
62
|
+
* @param request - Incoming request
|
|
63
|
+
* @param socket - Incoming socket
|
|
64
|
+
* @param head - Incoming head
|
|
65
|
+
* @param data - Incoming the data resolved from the handler
|
|
66
|
+
*/
|
|
67
|
+
authoriseConnection(request, socket, head, data) {
|
|
68
|
+
const doc = this._sockets.createGenericDocument();
|
|
69
|
+
const req = {
|
|
70
|
+
headers: request.headers,
|
|
71
|
+
...data, clientId: doc.id
|
|
72
|
+
};
|
|
73
|
+
const resolver = (assigns, data) => {
|
|
74
|
+
if (data.error) {
|
|
75
|
+
socket.write(`HTTP/1.1 ${data.error.code} ${data.error.message}\r\n\r\n`);
|
|
76
|
+
return socket.destroy();
|
|
77
|
+
}
|
|
78
|
+
this._server.handleUpgrade(request, socket, head, (ws) => {
|
|
79
|
+
this._server.emit("connection", ws);
|
|
80
|
+
const socketCache = {
|
|
81
|
+
socket: ws,
|
|
82
|
+
assigns: assigns
|
|
83
|
+
};
|
|
84
|
+
doc.updateDoc(socketCache);
|
|
85
|
+
this._manageSocket(doc);
|
|
86
|
+
if (data.message) {
|
|
87
|
+
const newMessage = {
|
|
88
|
+
action: enums_1.ServerActions.MESSAGE,
|
|
89
|
+
event: data.message.event,
|
|
90
|
+
channelName: "SERVER",
|
|
91
|
+
payload: data.message.payload
|
|
92
|
+
};
|
|
93
|
+
Endpoint._sendMessage(ws, newMessage);
|
|
94
|
+
}
|
|
95
|
+
});
|
|
96
|
+
};
|
|
97
|
+
const res = new pondResponse_1.EndpointResponse(resolver);
|
|
98
|
+
this._handler(req, res, this);
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* @desc Closes a client connection to the endpoint.
|
|
102
|
+
* @param clientId - The id of the client to close the connection to.
|
|
103
|
+
*/
|
|
104
|
+
closeConnection(clientId) {
|
|
105
|
+
const message = {
|
|
106
|
+
action: enums_1.ServerActions.CLOSE,
|
|
107
|
+
channelName: "SERVER",
|
|
108
|
+
event: "CLOSED_FROM_SERVER", payload: {}
|
|
109
|
+
};
|
|
110
|
+
const stringifiedMessage = JSON.stringify(message);
|
|
111
|
+
const socketDoc = this._sockets.get(clientId);
|
|
112
|
+
if (socketDoc) {
|
|
113
|
+
socketDoc.doc.socket.send(stringifiedMessage);
|
|
114
|
+
socketDoc.doc.socket.close();
|
|
115
|
+
socketDoc.removeDoc();
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* @desc Sends a message to a client on the endpoint.
|
|
120
|
+
* @param clientId - The id of the client to send the message to.
|
|
121
|
+
* @param event - The event to send the message with.
|
|
122
|
+
* @param message - The message to send.
|
|
123
|
+
*/
|
|
124
|
+
send(clientId, event, message) {
|
|
125
|
+
const newMessage = {
|
|
126
|
+
action: enums_1.ServerActions.MESSAGE,
|
|
127
|
+
channelName: enums_1.PondSenders.ENDPOINT,
|
|
128
|
+
event, payload: message
|
|
129
|
+
};
|
|
130
|
+
const stringifiedMessage = JSON.stringify(newMessage);
|
|
131
|
+
const addresses = Array.isArray(clientId) ? clientId : [clientId];
|
|
132
|
+
addresses.forEach((address) => {
|
|
133
|
+
const socketDoc = this._sockets.get(address);
|
|
134
|
+
if (socketDoc)
|
|
135
|
+
socketDoc.doc.socket.send(stringifiedMessage);
|
|
136
|
+
});
|
|
137
|
+
}
|
|
138
|
+
/**
|
|
139
|
+
* @desc lists all the channels in the endpoint
|
|
140
|
+
*/
|
|
141
|
+
listChannels() {
|
|
142
|
+
return this._channels.map(channel => channel.info).flat();
|
|
143
|
+
}
|
|
144
|
+
/**
|
|
145
|
+
* @desc lists all the clients in the endpoint
|
|
146
|
+
*/
|
|
147
|
+
listConnections() {
|
|
148
|
+
return this._sockets.map(socket => socket.socket);
|
|
149
|
+
}
|
|
150
|
+
/**
|
|
151
|
+
* @desc Broadcasts a message to all clients in the endpoint.
|
|
152
|
+
* @param event - The event to broadcast.
|
|
153
|
+
* @param message - The message to broadcast.
|
|
154
|
+
*/
|
|
155
|
+
broadcast(event, message) {
|
|
156
|
+
const sockets = [...this._sockets.generator()];
|
|
157
|
+
const newMessage = {
|
|
158
|
+
action: enums_1.ServerActions.MESSAGE,
|
|
159
|
+
channelName: enums_1.PondSenders.ENDPOINT,
|
|
160
|
+
event, payload: message
|
|
161
|
+
};
|
|
162
|
+
const stringifiedMessage = JSON.stringify(newMessage);
|
|
163
|
+
sockets.forEach(doc => doc.doc.socket.send(stringifiedMessage));
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
166
|
+
* @desc Searches for a channel in the endpoint.
|
|
167
|
+
* @param name - The name of the channel to search for.
|
|
168
|
+
*/
|
|
169
|
+
_findChannel(name) {
|
|
170
|
+
const pond = this._findPondChannel(name);
|
|
171
|
+
if (pond) {
|
|
172
|
+
const channel = pond.doc.getChannel(name);
|
|
173
|
+
if (channel)
|
|
174
|
+
return channel;
|
|
175
|
+
}
|
|
176
|
+
return undefined;
|
|
177
|
+
}
|
|
178
|
+
/**
|
|
179
|
+
* @desc Manages a new socket connection
|
|
180
|
+
* @param cache - The socket cache
|
|
181
|
+
* @private
|
|
182
|
+
*/
|
|
183
|
+
_manageSocket(cache) {
|
|
184
|
+
const socket = cache.doc.socket;
|
|
185
|
+
socket.addEventListener("message", (message) => {
|
|
186
|
+
this._readMessage(cache, message.data);
|
|
187
|
+
});
|
|
188
|
+
socket.addEventListener("close", () => {
|
|
189
|
+
for (const channel of this._channels.generator())
|
|
190
|
+
channel.doc.removeUser(cache.id);
|
|
191
|
+
cache.removeDoc();
|
|
192
|
+
});
|
|
193
|
+
socket.addEventListener("error", () => {
|
|
194
|
+
for (const channel of this._channels.generator())
|
|
195
|
+
channel.doc.removeUser(cache.id);
|
|
196
|
+
cache.removeDoc();
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
/**
|
|
200
|
+
* @desc Finds a pond channel in the endpoint.
|
|
201
|
+
* @param channelName - The name of the channel to find.
|
|
202
|
+
* @private
|
|
203
|
+
*/
|
|
204
|
+
_findPondChannel(channelName) {
|
|
205
|
+
return this._channels.find(channel => this.generateEventRequest(channel.path, channelName) !== null);
|
|
206
|
+
}
|
|
207
|
+
/**
|
|
208
|
+
* @desc Handles a message sent from a client
|
|
209
|
+
* @param cache - The socket cache of the client
|
|
210
|
+
* @param message - The message to handle
|
|
211
|
+
* @private
|
|
212
|
+
*/
|
|
213
|
+
_readMessage(cache, message) {
|
|
214
|
+
const errorMessage = {
|
|
215
|
+
action: enums_1.ServerActions.ERROR,
|
|
216
|
+
event: "error",
|
|
217
|
+
channelName: enums_1.PondSenders.ENDPOINT,
|
|
218
|
+
payload: {}
|
|
219
|
+
};
|
|
220
|
+
try {
|
|
221
|
+
const data = JSON.parse(message);
|
|
222
|
+
if (!data.action)
|
|
223
|
+
errorMessage.payload = {
|
|
224
|
+
message: "No action provided"
|
|
225
|
+
};
|
|
226
|
+
else if (!data.channelName)
|
|
227
|
+
errorMessage.payload = {
|
|
228
|
+
message: "No channel name provided"
|
|
229
|
+
};
|
|
230
|
+
else if (!data.payload)
|
|
231
|
+
errorMessage.payload = {
|
|
232
|
+
message: "No payload provided"
|
|
233
|
+
};
|
|
234
|
+
else
|
|
235
|
+
this._handleMessage(cache, data);
|
|
236
|
+
if (!this.isObjectEmpty(errorMessage.payload))
|
|
237
|
+
Endpoint._sendMessage(cache.doc.socket, errorMessage);
|
|
238
|
+
}
|
|
239
|
+
catch (e) {
|
|
240
|
+
if (e instanceof SyntaxError) {
|
|
241
|
+
errorMessage.payload = {
|
|
242
|
+
message: "Invalid JSON"
|
|
243
|
+
};
|
|
244
|
+
Endpoint._sendMessage(cache.doc.socket, errorMessage);
|
|
245
|
+
}
|
|
246
|
+
else if (e instanceof Error) {
|
|
247
|
+
errorMessage.payload = {
|
|
248
|
+
message: e.message,
|
|
249
|
+
};
|
|
250
|
+
Endpoint._sendMessage(cache.doc.socket, errorMessage);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
/**
|
|
255
|
+
* @desc Deals with a message sent from a client
|
|
256
|
+
* @param cache - The socket cache of the client
|
|
257
|
+
* @param message - The message to handle
|
|
258
|
+
*/
|
|
259
|
+
_handleMessage(cache, message) {
|
|
260
|
+
switch (message.action) {
|
|
261
|
+
case "JOIN_CHANNEL":
|
|
262
|
+
const pond = this._findPondChannel(message.channelName);
|
|
263
|
+
if (pond) {
|
|
264
|
+
const user = {
|
|
265
|
+
clientId: cache.id,
|
|
266
|
+
socket: cache.doc.socket,
|
|
267
|
+
assigns: cache.doc.assigns
|
|
268
|
+
};
|
|
269
|
+
pond.doc.addUser(user, message.channelName, message.payload);
|
|
270
|
+
}
|
|
271
|
+
else
|
|
272
|
+
throw new Error(`Channel ${message.channelName} does not exist`);
|
|
273
|
+
break;
|
|
274
|
+
case "LEAVE_CHANNEL":
|
|
275
|
+
this._channelAction(message.channelName, "LEAVE_CHANNEL", channel => {
|
|
276
|
+
channel.removeUser(cache.id);
|
|
277
|
+
});
|
|
278
|
+
break;
|
|
279
|
+
case "BROADCAST_FROM":
|
|
280
|
+
this._channelAction(message.channelName, message.event, (channel) => {
|
|
281
|
+
channel.broadcastFrom(message.event, message.payload, cache.id);
|
|
282
|
+
});
|
|
283
|
+
break;
|
|
284
|
+
case "BROADCAST":
|
|
285
|
+
this._channelAction(message.channelName, message.event, (channel) => {
|
|
286
|
+
channel.broadcast(message.event, message.payload, cache.id);
|
|
287
|
+
});
|
|
288
|
+
break;
|
|
289
|
+
case "SEND_MESSAGE_TO_USER":
|
|
290
|
+
this._channelAction(message.channelName, message.event, (channel) => {
|
|
291
|
+
if (!message.addresses || message.addresses.length === 0)
|
|
292
|
+
throw new Error(`No addresses provided`);
|
|
293
|
+
channel.sendTo(message.event, message.payload, cache.id, message.addresses);
|
|
294
|
+
});
|
|
295
|
+
break;
|
|
296
|
+
case "UPDATE_PRESENCE":
|
|
297
|
+
this._channelAction(message.channelName, "UPDATE_PRESENCE", (channel) => {
|
|
298
|
+
var _a, _b;
|
|
299
|
+
channel.updateUser(cache.id, ((_a = message.payload) === null || _a === void 0 ? void 0 : _a.presence) || {}, ((_b = message.payload) === null || _b === void 0 ? void 0 : _b.assigns) || {});
|
|
300
|
+
});
|
|
301
|
+
break;
|
|
302
|
+
}
|
|
303
|
+
}
|
|
304
|
+
/**
|
|
305
|
+
* @desc Handles a channel action by finding the channel and executing the callback.
|
|
306
|
+
* @param channelName - The name of the channel to find.
|
|
307
|
+
* @param event - The event to execute.
|
|
308
|
+
* @param action - The action to execute.
|
|
309
|
+
* @private
|
|
310
|
+
*/
|
|
311
|
+
_channelAction(channelName, event, action) {
|
|
312
|
+
const channel = this._findChannel(channelName);
|
|
313
|
+
if (!channel)
|
|
314
|
+
throw new Error(`Channel ${channelName} does not exist`);
|
|
315
|
+
try {
|
|
316
|
+
return action(channel);
|
|
317
|
+
}
|
|
318
|
+
catch (e) {
|
|
319
|
+
throw new Error(`Error while executing event '${event}' on channel '${channelName}': ${e.message}`);
|
|
320
|
+
}
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
exports.Endpoint = Endpoint;
|