@eleven-am/pondsocket 0.1.11 → 0.1.12

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 (90) hide show
  1. package/dist/client/channel.js +200 -0
  2. package/dist/client/index.d.ts +122 -0
  3. package/{pondClient/socket.js → dist/client/index.js} +30 -44
  4. package/dist/express/index.d.ts +36 -0
  5. package/dist/express/index.js +16 -0
  6. package/dist/index.d.ts +340 -0
  7. package/dist/index.js +4 -0
  8. package/dist/server/abstracts/abstractRequest.js +40 -0
  9. package/dist/server/abstracts/abstractRequest.test.js +41 -0
  10. package/dist/server/abstracts/abstractResponse.js +6 -0
  11. package/dist/server/abstracts/middleware.js +38 -0
  12. package/dist/server/abstracts/middleware.test.js +70 -0
  13. package/dist/server/channel/channelEngine.js +279 -0
  14. package/dist/server/channel/channelEngine.test.js +377 -0
  15. package/dist/server/channel/channelRequest.test.js +29 -0
  16. package/dist/server/channel/channelResponse.test.js +134 -0
  17. package/dist/server/channel/eventRequest.js +18 -0
  18. package/dist/server/channel/eventResponse.js +141 -0
  19. package/dist/server/endpoint/connectionResponse.js +50 -0
  20. package/dist/server/endpoint/endpoint.js +269 -0
  21. package/dist/server/endpoint/endpoint.test.js +406 -0
  22. package/dist/server/endpoint/endpointResponse.test.js +43 -0
  23. package/dist/server/pondChannel/joinRequest.js +29 -0
  24. package/dist/server/pondChannel/joinResponse.js +96 -0
  25. package/dist/server/pondChannel/pondChannel.js +161 -0
  26. package/dist/server/pondChannel/pondChannelResponse.test.js +108 -0
  27. package/dist/server/presence/presenceEngine.js +112 -0
  28. package/dist/server/presence/presenceEngine.test.js +104 -0
  29. package/dist/server/server/pondSocket.js +122 -0
  30. package/{pondSocket → dist/server/server}/server.test.js +7 -21
  31. package/{pondBase/baseClass.js → dist/server/utils/matchPattern.js} +33 -43
  32. package/{pondBase/baseClass.test.js → dist/server/utils/matchPattern.test.js} +16 -25
  33. package/dist/server/utils/subjectUtils.js +68 -0
  34. package/package.json +28 -12
  35. package/.eslintrc.js +0 -28
  36. package/base.d.ts +0 -1
  37. package/base.js +0 -17
  38. package/client.d.ts +0 -1
  39. package/client.js +0 -17
  40. package/index.d.ts +0 -1
  41. package/index.js +0 -17
  42. package/jest.config.js +0 -11
  43. package/pondBase/baseClass.d.ts +0 -55
  44. package/pondBase/enums.d.ts +0 -9
  45. package/pondBase/enums.js +0 -14
  46. package/pondBase/index.d.ts +0 -6
  47. package/pondBase/index.js +0 -22
  48. package/pondBase/pondBase.d.ts +0 -41
  49. package/pondBase/pondBase.js +0 -60
  50. package/pondBase/pondBase.test.js +0 -101
  51. package/pondBase/pubSub.d.ts +0 -82
  52. package/pondBase/pubSub.js +0 -158
  53. package/pondBase/pubSub.test.js +0 -332
  54. package/pondBase/simpleBase.d.ts +0 -126
  55. package/pondBase/simpleBase.js +0 -211
  56. package/pondBase/simpleBase.test.js +0 -153
  57. package/pondBase/types.d.ts +0 -2
  58. package/pondBase/types.js +0 -2
  59. package/pondClient/channel.d.ts +0 -77
  60. package/pondClient/channel.js +0 -167
  61. package/pondClient/index.d.ts +0 -2
  62. package/pondClient/index.js +0 -18
  63. package/pondClient/socket.d.ts +0 -41
  64. package/pondSocket/channel.d.ts +0 -129
  65. package/pondSocket/channel.js +0 -287
  66. package/pondSocket/channel.test.js +0 -377
  67. package/pondSocket/channelMiddleWare.d.ts +0 -28
  68. package/pondSocket/channelMiddleWare.js +0 -36
  69. package/pondSocket/endpoint.d.ts +0 -90
  70. package/pondSocket/endpoint.js +0 -320
  71. package/pondSocket/endpoint.test.js +0 -490
  72. package/pondSocket/enums.d.ts +0 -19
  73. package/pondSocket/enums.js +0 -25
  74. package/pondSocket/index.d.ts +0 -7
  75. package/pondSocket/index.js +0 -23
  76. package/pondSocket/pondChannel.d.ts +0 -79
  77. package/pondSocket/pondChannel.js +0 -211
  78. package/pondSocket/pondChannel.test.js +0 -430
  79. package/pondSocket/pondResponse.d.ts +0 -25
  80. package/pondSocket/pondResponse.js +0 -120
  81. package/pondSocket/pondSocket.d.ts +0 -47
  82. package/pondSocket/pondSocket.js +0 -94
  83. package/pondSocket/socketMiddleWare.d.ts +0 -6
  84. package/pondSocket/socketMiddleWare.js +0 -32
  85. package/pondSocket/types.d.ts +0 -74
  86. package/pondSocket/types.js +0 -2
  87. package/socket.d.ts +0 -1
  88. package/socket.js +0 -17
  89. package/tsconfig.eslint.json +0 -5
  90. package/tsconfig.json +0 -90
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.createChannelEvent = exports.createChannelEngine = void 0;
4
+ const channelEngine_1 = require("./channelEngine");
5
+ const channelEngine_test_1 = require("./channelEngine.test");
6
+ const eventResponse_1 = require("./eventResponse");
7
+ const createChannelEngine = () => {
8
+ const parentEngine = (0, channelEngine_test_1.createParentEngine)();
9
+ return new channelEngine_1.ChannelEngine('test', parentEngine);
10
+ };
11
+ exports.createChannelEngine = createChannelEngine;
12
+ const createChannelEvent = () => {
13
+ const responseEvent = {
14
+ event: 'event',
15
+ payload: {
16
+ payload: 'payload',
17
+ },
18
+ sender: 'sender',
19
+ action: channelEngine_1.ServerActions.BROADCAST,
20
+ recipients: ['recipient'],
21
+ };
22
+ return responseEvent;
23
+ };
24
+ exports.createChannelEvent = createChannelEvent;
25
+ const createChannelResponse = () => {
26
+ const channelEngine = (0, exports.createChannelEngine)();
27
+ const event = (0, exports.createChannelEvent)();
28
+ channelEngine.addUser(event.sender, { assign: 'assign' }, () => { });
29
+ const response = new eventResponse_1.EventResponse(event, channelEngine);
30
+ return { channelEngine,
31
+ event,
32
+ response };
33
+ };
34
+ describe('ChannelResponse', () => {
35
+ it('should create a new ChannelResponse', () => {
36
+ const { response } = createChannelResponse();
37
+ expect(response).toBeDefined();
38
+ });
39
+ it('should return the responseSent', () => {
40
+ const { response } = createChannelResponse();
41
+ expect(response.responseSent).toEqual(false);
42
+ });
43
+ it('should accept the request', () => {
44
+ const { response } = createChannelResponse();
45
+ response.accept();
46
+ expect(response.responseSent).toEqual(true);
47
+ });
48
+ it('should reject the request', () => {
49
+ const { response, channelEngine, event } = createChannelResponse();
50
+ jest.spyOn(channelEngine, 'sendMessage');
51
+ response.reject();
52
+ expect(response.responseSent).toEqual(true);
53
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('channel', [event.sender], channelEngine_1.ServerActions.ERROR, 'error_channel', { message: 'Unauthorized request',
54
+ code: 403 });
55
+ });
56
+ it('should send a direct message', () => {
57
+ const { response, channelEngine, event } = createChannelResponse();
58
+ jest.spyOn(channelEngine, 'sendMessage');
59
+ response.send('event', { payload: 'payload' });
60
+ expect(response.responseSent).toEqual(true);
61
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('channel', [event.sender], channelEngine_1.ServerActions.SYSTEM, 'event', { payload: 'payload' });
62
+ });
63
+ it('should broadcast a message', () => {
64
+ const { response, channelEngine } = createChannelResponse();
65
+ jest.spyOn(channelEngine, 'sendMessage');
66
+ response.broadcast('event', { payload: 'payload' });
67
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', 'all_users', channelEngine_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
68
+ });
69
+ it('should broadcastFromUser a message', () => {
70
+ const { response, channelEngine } = createChannelResponse();
71
+ jest.spyOn(channelEngine, 'sendMessage');
72
+ response.broadcastFromUser('event', { payload: 'payload' });
73
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', 'all_except_sender', channelEngine_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
74
+ });
75
+ it('should sendToUsers a message', () => {
76
+ const { response, channelEngine } = createChannelResponse();
77
+ jest.spyOn(channelEngine, 'sendMessage');
78
+ channelEngine.addUser('recipient', { assign: 'assign' }, () => { });
79
+ response.sendToUsers('event', { payload: 'payload' }, ['recipient']);
80
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', ['recipient'], channelEngine_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
81
+ });
82
+ it('should track a trackPresence', () => {
83
+ const { response, channelEngine } = createChannelResponse();
84
+ jest.spyOn(channelEngine, 'trackPresence');
85
+ response.trackPresence({ status: 'online' });
86
+ expect(channelEngine.trackPresence).toHaveBeenCalledWith('sender', { status: 'online' });
87
+ });
88
+ it('should untrack a trackPresence', () => {
89
+ const { response, channelEngine } = createChannelResponse();
90
+ jest.spyOn(channelEngine, 'unTrackPresence');
91
+ response.unTrackPresence();
92
+ expect(channelEngine.unTrackPresence).toHaveBeenCalledWith('sender');
93
+ });
94
+ it('should broadcast an error if untrackPresence is called twice', () => {
95
+ const { response, channelEngine } = createChannelResponse();
96
+ jest.spyOn(channelEngine, 'sendMessage');
97
+ // because by default the user is not tracked and the presence engine only exists after a first trackPresence
98
+ // we need to call trackPresence first
99
+ response.trackPresence({ status: 'online' });
100
+ response.unTrackPresence();
101
+ response.unTrackPresence();
102
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('channel', ['sender'], channelEngine_1.ServerActions.ERROR, 'error_channel', {
103
+ message: 'PresenceEngine: Presence with key sender does not exist',
104
+ code: 500,
105
+ });
106
+ });
107
+ it('should updatePresence', () => {
108
+ const { response, channelEngine } = createChannelResponse();
109
+ jest.spyOn(channelEngine, 'updatePresence');
110
+ response.trackPresence({ status: 'online' });
111
+ response.updatePresence({ status: 'updated' });
112
+ expect(channelEngine.updatePresence).toHaveBeenCalledWith('sender', { status: 'updated' });
113
+ });
114
+ it('should update a users assign data', () => {
115
+ const { response, channelEngine } = createChannelResponse();
116
+ jest.spyOn(channelEngine, 'updateAssigns');
117
+ response.accept({ assign: 'updated' });
118
+ expect(channelEngine.updateAssigns).toHaveBeenCalledWith('sender', { assign: 'updated' });
119
+ });
120
+ it('should evict a user', () => {
121
+ const { response, channelEngine } = createChannelResponse();
122
+ jest.spyOn(channelEngine, 'kickUser');
123
+ response.evictUser('recipient');
124
+ expect(channelEngine.kickUser).toHaveBeenCalledWith('sender', 'recipient');
125
+ expect(response.responseSent).toEqual(true);
126
+ });
127
+ it('should destroy the channel', () => {
128
+ const { response, channelEngine } = createChannelResponse();
129
+ jest.spyOn(channelEngine, 'destroy');
130
+ response.closeChannel('recipient');
131
+ expect(channelEngine.destroy).toHaveBeenCalledWith('recipient');
132
+ expect(response.responseSent).toEqual(true);
133
+ });
134
+ });
@@ -0,0 +1,18 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventRequest = void 0;
4
+ const abstractRequest_1 = require("../abstracts/abstractRequest");
5
+ class EventRequest extends abstractRequest_1.AbstractRequest {
6
+ constructor(event, engine) {
7
+ super(event.event, engine, event.payload);
8
+ this._internalEvent = event;
9
+ }
10
+ get user() {
11
+ const assigns = this._engine.getUserData(this._internalEvent.sender);
12
+ if (!assigns) {
13
+ throw new Error(`ChannelRequest: User with id ${this._internalEvent.sender} does not exist in channel ${this._engine.name}`);
14
+ }
15
+ return assigns;
16
+ }
17
+ }
18
+ exports.EventRequest = EventRequest;
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.EventResponse = void 0;
4
+ const channelEngine_1 = require("./channelEngine");
5
+ const abstractResponse_1 = require("../abstracts/abstractResponse");
6
+ class EventResponse extends abstractResponse_1.PondResponse {
7
+ constructor(event, engine) {
8
+ super();
9
+ this._hasExecuted = false;
10
+ this._event = event;
11
+ this._engine = engine;
12
+ }
13
+ /**
14
+ * @desc Checks if the response has been sent
15
+ */
16
+ get responseSent() {
17
+ return this._hasExecuted;
18
+ }
19
+ /**
20
+ * @desc Accepts the request and optionally assigns data to the client
21
+ * @param assigns - the data to assign to the client
22
+ */
23
+ accept(assigns) {
24
+ this._manageAssigns(assigns);
25
+ this._hasExecuted = true;
26
+ return this;
27
+ }
28
+ /**
29
+ * @desc Rejects the request and optionally assigns data to the client
30
+ * @param message - the error message
31
+ * @param errorCode - the error code
32
+ * @param assigns - the data to assign to the client
33
+ */
34
+ reject(message, errorCode, assigns) {
35
+ this._manageAssigns(assigns);
36
+ const text = message || 'Unauthorized request';
37
+ this._engine.sendMessage('channel', [this._event.sender], channelEngine_1.ServerActions.ERROR, 'error_channel', { message: text,
38
+ code: errorCode || 403 });
39
+ this._hasExecuted = true;
40
+ return this;
41
+ }
42
+ /**
43
+ * @desc Emits a direct message to the client
44
+ * @param event - the event name
45
+ * @param payload - the payload to send
46
+ * @param assigns - the data to assign to the client
47
+ */
48
+ send(event, payload, assigns) {
49
+ this._engine.sendMessage('channel', [this._event.sender], channelEngine_1.ServerActions.SYSTEM, event, payload);
50
+ return this.accept(assigns);
51
+ }
52
+ /**
53
+ * @desc Sends a message to all clients in the channel
54
+ * @param event - the event to send
55
+ * @param payload - the payload to send
56
+ */
57
+ broadcast(event, payload) {
58
+ this._engine.sendMessage(this._event.sender, 'all_users', channelEngine_1.ServerActions.BROADCAST, event, payload);
59
+ return this;
60
+ }
61
+ /**
62
+ * @desc Sends a message to all clients in the channel except the client making the request
63
+ * @param event - the event to send
64
+ * @param payload - the payload to send
65
+ */
66
+ broadcastFromUser(event, payload) {
67
+ this._engine.sendMessage(this._event.sender, 'all_except_sender', channelEngine_1.ServerActions.BROADCAST, event, payload);
68
+ return this;
69
+ }
70
+ /**
71
+ * @desc Sends a message to a set of clients in the channel
72
+ * @param event - the event to send
73
+ * @param payload - the payload to send
74
+ * @param userIds - the ids of the clients to send the message to
75
+ */
76
+ sendToUsers(event, payload, userIds) {
77
+ this._engine.sendMessage(this._event.sender, userIds, channelEngine_1.ServerActions.BROADCAST, event, payload);
78
+ return this;
79
+ }
80
+ /**
81
+ * @desc Tracks a user's presence in the channel
82
+ * @param presence - the initial presence data
83
+ * @param userId - the id of the user to track
84
+ */
85
+ trackPresence(presence, userId) {
86
+ this._engine.trackPresence(userId || this._event.sender, presence);
87
+ return this;
88
+ }
89
+ /**
90
+ * @desc Updates a user's presence in the channel
91
+ * @param presence - the updated presence data
92
+ * @param userId - the id of the user to update
93
+ */
94
+ updatePresence(presence, userId) {
95
+ this._engine.updatePresence(userId || this._event.sender, presence);
96
+ return this;
97
+ }
98
+ /**
99
+ * @desc Removes a user's presence from the channel
100
+ * @param userId - the id of the user to remove
101
+ */
102
+ unTrackPresence(userId) {
103
+ userId = userId || this._event.sender;
104
+ try {
105
+ this._engine.unTrackPresence(userId);
106
+ }
107
+ catch (e) {
108
+ this._engine.sendMessage('channel', [userId], channelEngine_1.ServerActions.ERROR, 'error_channel', { message: e.message,
109
+ code: 500 });
110
+ }
111
+ return this;
112
+ }
113
+ /**
114
+ * @desc Evicts a user from the channel
115
+ * @param reason - the reason for the eviction
116
+ * @param userId - the id of the user to evict,
117
+ */
118
+ evictUser(reason, userId) {
119
+ this._engine.kickUser(userId || this._event.sender, reason);
120
+ this._hasExecuted = true;
121
+ }
122
+ /**
123
+ * @desc Closes the channel from the server side for all clients
124
+ * @param reason - the reason for closing the channel
125
+ */
126
+ closeChannel(reason) {
127
+ this._engine.destroy(reason);
128
+ this._hasExecuted = true;
129
+ }
130
+ /**
131
+ * @desc Gets the event that triggered the response
132
+ * @param assigns - the data to assign to the client
133
+ * @private
134
+ */
135
+ _manageAssigns(assigns) {
136
+ if (assigns) {
137
+ this._engine.updateAssigns(this._event.sender, assigns);
138
+ }
139
+ }
140
+ }
141
+ exports.EventResponse = EventResponse;
@@ -0,0 +1,50 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.ConnectionResponse = void 0;
4
+ const abstractResponse_1 = require("../abstracts/abstractResponse");
5
+ class ConnectionResponse extends abstractResponse_1.PondResponse {
6
+ constructor(handler) {
7
+ super();
8
+ this._hasExecuted = false;
9
+ this._handler = handler;
10
+ }
11
+ /**
12
+ * @desc Accepts the request and optionally assigns data to the client
13
+ * @param assigns - the data to assign to the client
14
+ */
15
+ accept(assigns) {
16
+ if (this._hasExecuted) {
17
+ throw new Error('EndpointResponse: Message has already been processed');
18
+ }
19
+ this._hasExecuted = true;
20
+ this._handler(assigns || {}, {});
21
+ }
22
+ /**
23
+ * @desc Rejects the request with the given error message
24
+ * @param message - the error message
25
+ * @param errorCode - the error code
26
+ */
27
+ reject(message, errorCode) {
28
+ if (this._hasExecuted) {
29
+ throw new Error('EndpointResponse: Message has already been processed');
30
+ }
31
+ this._hasExecuted = true;
32
+ this._handler({}, { error: { message: message || 'Message rejected',
33
+ code: errorCode || 403 } });
34
+ }
35
+ /**
36
+ * @desc Emits a direct message to the client
37
+ * @param event - the event name
38
+ * @param payload - the payload to send
39
+ * @param assigns - the data to assign to the client
40
+ */
41
+ send(event, payload, assigns) {
42
+ if (this._hasExecuted) {
43
+ throw new Error('EndpointResponse: Message has already been processed');
44
+ }
45
+ this._hasExecuted = true;
46
+ this._handler(assigns || {}, { message: { event,
47
+ payload } });
48
+ }
49
+ }
50
+ exports.ConnectionResponse = ConnectionResponse;
@@ -0,0 +1,269 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Endpoint = exports.ClientActions = void 0;
4
+ const connectionResponse_1 = require("./connectionResponse");
5
+ const channelEngine_1 = require("../channel/channelEngine");
6
+ const matchPattern_1 = require("../utils/matchPattern");
7
+ var ClientActions;
8
+ (function (ClientActions) {
9
+ ClientActions["JOIN_CHANNEL"] = "JOIN_CHANNEL";
10
+ ClientActions["LEAVE_CHANNEL"] = "LEAVE_CHANNEL";
11
+ ClientActions["BROADCAST"] = "BROADCAST";
12
+ })(ClientActions = exports.ClientActions || (exports.ClientActions = {}));
13
+ class Endpoint {
14
+ constructor(server) {
15
+ this._server = server;
16
+ this._channels = [];
17
+ this._sockets = new Set();
18
+ this._matcher = new matchPattern_1.MatchPattern();
19
+ }
20
+ /**
21
+ * @desc Adds a new PondChannel to this path on this endpoint
22
+ * @param path - The path to add the channel to
23
+ * @param channel - The channel to add
24
+ *
25
+ * @example
26
+ * endpoint.useChannel('/chat', pondChannelInstance);
27
+ */
28
+ useChannel(path, channel) {
29
+ const manager = channel._buildManager();
30
+ this._channels.push({ path,
31
+ manager });
32
+ }
33
+ /**
34
+ * @desc List all clients connected to this endpoint
35
+ */
36
+ listConnections() {
37
+ return [...this._sockets].map(({ clientId }) => clientId);
38
+ }
39
+ /**
40
+ * @desc Gets all clients connected to this endpoint
41
+ */
42
+ getClients() {
43
+ return [...this._sockets];
44
+ }
45
+ /**
46
+ * @desc Broadcasts a message to all clients connected to this endpoint
47
+ * @param event - The event to broadcast
48
+ * @param payload - The payload to broadcast
49
+ */
50
+ broadcast(event, payload) {
51
+ this._sockets.forEach(({ socket }) => {
52
+ const message = {
53
+ event,
54
+ payload,
55
+ action: channelEngine_1.ServerActions.BROADCAST,
56
+ channelName: 'SERVER',
57
+ };
58
+ this._sendMessage(socket, message);
59
+ });
60
+ }
61
+ /**
62
+ * @desc Closes specific clients connected to this endpoint
63
+ * @param clientIds - The id for the client / clients to close
64
+ */
65
+ closeConnection(clientIds) {
66
+ const clients = typeof clientIds === 'string' ? [clientIds] : clientIds;
67
+ this.getClients()
68
+ .forEach(({ clientId, socket }) => {
69
+ if (clients.includes(clientId)) {
70
+ socket.close();
71
+ }
72
+ });
73
+ }
74
+ /**
75
+ * @desc Authenticates the client to the endpoint
76
+ * @param request - Incoming request
77
+ * @param socket - Incoming socket
78
+ * @param head - Incoming head
79
+ * @param data - Incoming the data resolved from the handler
80
+ * @param handler - The handler to use to authenticate the client
81
+ */
82
+ _authoriseConnection(request, socket, head, data, handler) {
83
+ const clientId = this._generateClientId();
84
+ const req = Object.assign(Object.assign({ headers: request.headers }, data), { id: clientId });
85
+ const resolver = (assigns, data) => {
86
+ if (data.error) {
87
+ socket.write(`HTTP/1.1 ${data.error.code} ${data.error.message}\r\n\r\n`);
88
+ return socket.destroy();
89
+ }
90
+ this._server.handleUpgrade(request, socket, head, (ws) => {
91
+ this._server.emit('connection', ws);
92
+ const socketCache = {
93
+ socket: ws,
94
+ assigns,
95
+ clientId,
96
+ };
97
+ this._sockets.add(socketCache);
98
+ this._manageSocket(socketCache);
99
+ if (data.message) {
100
+ const newMessage = {
101
+ event: data.message.event,
102
+ channelName: 'SERVER',
103
+ payload: data.message.payload,
104
+ action: channelEngine_1.ServerActions.SYSTEM,
105
+ };
106
+ this._sendMessage(ws, newMessage);
107
+ }
108
+ });
109
+ };
110
+ const res = new connectionResponse_1.ConnectionResponse(resolver);
111
+ handler(req, res);
112
+ }
113
+ /**
114
+ * @desc Manages a new socket connection
115
+ * @param cache - The socket cache
116
+ * @private
117
+ */
118
+ _manageSocket(cache) {
119
+ const socket = cache.socket;
120
+ socket.addEventListener('message', (message) => {
121
+ this._readMessage(cache, message.data);
122
+ });
123
+ socket.addEventListener('close', () => {
124
+ this._channels
125
+ .forEach(({ manager }) => manager.removeUser(cache.clientId));
126
+ });
127
+ socket.addEventListener('error', () => {
128
+ this._channels
129
+ .forEach(({ manager }) => manager.removeUser(cache.clientId));
130
+ });
131
+ }
132
+ /**
133
+ * @desc Sends a message to a client
134
+ * @param socket - The socket to send the message to
135
+ * @param message - The message to send
136
+ */
137
+ _sendMessage(socket, message) {
138
+ socket.send(JSON.stringify(message));
139
+ }
140
+ /**
141
+ * @desc Adds a new client to a channel on this endpoint
142
+ * @param channel - The channel to add the client to
143
+ * @param socket - The client to add to the channel
144
+ * @param joinParams - The parameters to pass to the channel
145
+ * @private
146
+ */
147
+ _joinChannel(channel, socket, joinParams) {
148
+ for (const { path, manager } of this._channels) {
149
+ const match = this._matcher.parseEvent(path, channel);
150
+ if (match) {
151
+ const request = Object.assign(Object.assign(Object.assign({}, match), socket), { joinParams, channelName: channel });
152
+ return manager.addUser(request);
153
+ }
154
+ }
155
+ throw new Error(`GatewayEngine: Channel ${channel} does not exist`);
156
+ }
157
+ /**
158
+ * @desc Executes a function on a channel
159
+ * @param channel - The channel to execute the function on
160
+ * @param handler - The function to execute
161
+ * @private
162
+ */
163
+ _execute(channel, handler) {
164
+ for (const { manager } of this._channels) {
165
+ const isPresent = manager.listChannels().includes(channel);
166
+ if (isPresent) {
167
+ return manager.execute(channel, handler);
168
+ }
169
+ }
170
+ throw new Error(`GatewayEngine: Channel ${channel} does not exist`);
171
+ }
172
+ /**
173
+ * @desc Deals with a message sent from a client
174
+ * @param cache - The socket cache of the client
175
+ * @param message - The message to handle
176
+ */
177
+ _handleMessage(cache, message) {
178
+ switch (message.action) {
179
+ case ClientActions.JOIN_CHANNEL:
180
+ this._joinChannel(message.channelName, cache, message.payload);
181
+ break;
182
+ case ClientActions.LEAVE_CHANNEL:
183
+ this._execute(message.channelName, (channel) => {
184
+ channel.removeUser(cache.clientId);
185
+ });
186
+ break;
187
+ case ClientActions.BROADCAST:
188
+ this._execute(message.channelName, (channel) => {
189
+ channel.broadcastMessage(cache.clientId, message);
190
+ });
191
+ break;
192
+ default:
193
+ throw new Error(`GatewayEngine: Action ${message.action} does not exist`);
194
+ }
195
+ }
196
+ /**
197
+ * @desc Handles a message sent from a client
198
+ * @param cache - The socket cache of the client
199
+ * @param message - The message to handle
200
+ * @private
201
+ */
202
+ _readMessage(cache, message) {
203
+ const errorMessage = {
204
+ event: 'error',
205
+ action: channelEngine_1.ServerActions.ERROR,
206
+ channelName: 'ENDPOINT',
207
+ payload: {},
208
+ };
209
+ try {
210
+ const data = JSON.parse(message);
211
+ if (!data.action) {
212
+ errorMessage.payload = {
213
+ message: 'No action provided',
214
+ };
215
+ }
216
+ else if (!data.channelName) {
217
+ errorMessage.payload = {
218
+ message: 'No channel name provided',
219
+ };
220
+ }
221
+ else if (!data.payload) {
222
+ errorMessage.payload = {
223
+ message: 'No payload provided',
224
+ };
225
+ }
226
+ if (!this._isObjectEmpty(errorMessage.payload)) {
227
+ this._sendMessage(cache.socket, errorMessage);
228
+ }
229
+ else {
230
+ this._handleMessage(cache, data);
231
+ }
232
+ }
233
+ catch (e) {
234
+ if (e instanceof SyntaxError) {
235
+ errorMessage.payload = {
236
+ message: 'Invalid JSON',
237
+ };
238
+ this._sendMessage(cache.socket, errorMessage);
239
+ }
240
+ else if (e instanceof Error) {
241
+ errorMessage.payload = {
242
+ message: e.message,
243
+ };
244
+ this._sendMessage(cache.socket, errorMessage);
245
+ }
246
+ }
247
+ }
248
+ /**
249
+ * @desc Checks if an object is empty
250
+ * @param obj - The object to check
251
+ * @private
252
+ */
253
+ _isObjectEmpty(obj) {
254
+ return Object.keys(obj).length === 0;
255
+ }
256
+ /**
257
+ * @desc Generates a new client ID
258
+ * @private
259
+ */
260
+ _generateClientId() {
261
+ let id = '';
262
+ const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
263
+ for (let i = 0; i < 21; i++) {
264
+ id += chars.charAt(Math.floor(Math.random() * chars.length));
265
+ }
266
+ return id;
267
+ }
268
+ }
269
+ exports.Endpoint = Endpoint;