@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.
Files changed (64) hide show
  1. package/.eslintrc.js +28 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/pondsocket.iml +12 -0
  4. package/LICENSE +674 -0
  5. package/base.d.ts +1 -0
  6. package/base.js +17 -0
  7. package/client.d.ts +1 -0
  8. package/client.js +17 -0
  9. package/index.d.ts +1 -0
  10. package/index.js +17 -0
  11. package/jest.config.js +11 -0
  12. package/package.json +48 -0
  13. package/pondBase/baseClass.d.ts +37 -0
  14. package/pondBase/baseClass.js +111 -0
  15. package/pondBase/baseClass.test.js +73 -0
  16. package/pondBase/enums.d.ts +9 -0
  17. package/pondBase/enums.js +14 -0
  18. package/pondBase/index.d.ts +6 -0
  19. package/pondBase/index.js +22 -0
  20. package/pondBase/pondBase.d.ts +41 -0
  21. package/pondBase/pondBase.js +60 -0
  22. package/pondBase/pondBase.test.js +101 -0
  23. package/pondBase/pubSub.d.ts +73 -0
  24. package/pondBase/pubSub.js +138 -0
  25. package/pondBase/pubSub.test.js +309 -0
  26. package/pondBase/simpleBase.d.ts +131 -0
  27. package/pondBase/simpleBase.js +211 -0
  28. package/pondBase/simpleBase.test.js +153 -0
  29. package/pondBase/types.d.ts +2 -0
  30. package/pondBase/types.js +2 -0
  31. package/pondClient/channel.d.ts +66 -0
  32. package/pondClient/channel.js +152 -0
  33. package/pondClient/index.d.ts +2 -0
  34. package/pondClient/index.js +18 -0
  35. package/pondClient/socket.d.ts +42 -0
  36. package/pondClient/socket.js +116 -0
  37. package/pondSocket/channel.d.ts +134 -0
  38. package/pondSocket/channel.js +287 -0
  39. package/pondSocket/channel.test.js +377 -0
  40. package/pondSocket/channelMiddleWare.d.ts +26 -0
  41. package/pondSocket/channelMiddleWare.js +36 -0
  42. package/pondSocket/endpoint.d.ts +90 -0
  43. package/pondSocket/endpoint.js +323 -0
  44. package/pondSocket/endpoint.test.js +513 -0
  45. package/pondSocket/enums.d.ts +19 -0
  46. package/pondSocket/enums.js +25 -0
  47. package/pondSocket/index.d.ts +7 -0
  48. package/pondSocket/index.js +23 -0
  49. package/pondSocket/pondChannel.d.ts +79 -0
  50. package/pondSocket/pondChannel.js +219 -0
  51. package/pondSocket/pondChannel.test.js +430 -0
  52. package/pondSocket/pondResponse.d.ts +25 -0
  53. package/pondSocket/pondResponse.js +120 -0
  54. package/pondSocket/pondSocket.d.ts +47 -0
  55. package/pondSocket/pondSocket.js +94 -0
  56. package/pondSocket/server.test.js +136 -0
  57. package/pondSocket/socketMiddleWare.d.ts +6 -0
  58. package/pondSocket/socketMiddleWare.js +32 -0
  59. package/pondSocket/types.d.ts +74 -0
  60. package/pondSocket/types.js +2 -0
  61. package/socket.d.ts +1 -0
  62. package/socket.js +17 -0
  63. package/tsconfig.eslint.json +5 -0
  64. 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;