@eleven-am/pondsocket 0.1.11 → 0.1.13

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 +31 -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,161 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PondChannel = void 0;
4
+ const joinRequest_1 = require("./joinRequest");
5
+ const joinResponse_1 = require("./joinResponse");
6
+ const middleware_1 = require("../abstracts/middleware");
7
+ const channelEngine_1 = require("../channel/channelEngine");
8
+ class PondChannel {
9
+ constructor() {
10
+ this._authorize = undefined;
11
+ this._channels = new Set();
12
+ this._middleware = new middleware_1.Middleware();
13
+ }
14
+ /**
15
+ * @desc Authorize a user to join a channel
16
+ * @param handler - The handler to authorize the user
17
+ * @example
18
+ * const pond = new PondChannelEngine();
19
+ * pond.onJoinRequest((request, response) => {
20
+ * if (request.event.assigns.admin)
21
+ * response.accept();
22
+ * else
23
+ * response.reject('You are not an admin', 403);
24
+ * });
25
+ */
26
+ onJoinRequest(handler) {
27
+ this._authorize = handler;
28
+ }
29
+ /**
30
+ * @desc Handles an event request made by a user
31
+ * @param event - The event to listen for
32
+ * @param handler - The handler to execute when the event is received
33
+ * @example
34
+ * pond.onEvent('echo', (request, response) => {
35
+ * response.send('echo', {
36
+ * message: request.payload,
37
+ * });
38
+ * });
39
+ */
40
+ onEvent(event, handler) {
41
+ this._middleware.use((request, response, next) => {
42
+ if (request._parseQueries(event)) {
43
+ return handler(request, response);
44
+ }
45
+ next();
46
+ });
47
+ }
48
+ /**
49
+ * @desc Builds a PondChannelManager from the current engine
50
+ */
51
+ _buildManager() {
52
+ return {
53
+ addUser: this._addUser.bind(this),
54
+ execute: this._execute.bind(this),
55
+ removeUser: this._removeUser.bind(this),
56
+ getChannels: this._getChannels.bind(this),
57
+ listChannels: this._listChannels.bind(this),
58
+ };
59
+ }
60
+ /**
61
+ * @desc Adds a user to a channel
62
+ * @param user - The user to add
63
+ * @private
64
+ */
65
+ _addUser(user) {
66
+ const newChannel = this._getChannel(user.channelName) || this._createChannel(user.channelName);
67
+ if (this._authorize) {
68
+ const socketCache = {
69
+ clientId: user.clientId,
70
+ socket: user.socket,
71
+ assigns: user.assigns,
72
+ };
73
+ const request = new joinRequest_1.JoinRequest(user, newChannel);
74
+ const response = new joinResponse_1.JoinResponse(socketCache, newChannel);
75
+ void this._authorize(request, response);
76
+ if (!response.responseSent) {
77
+ throw new Error('PondChannelEngine: Response was not resolved');
78
+ }
79
+ }
80
+ else {
81
+ newChannel.addUser(user.clientId, user.assigns, (event) => {
82
+ user.socket.send(JSON.stringify(event));
83
+ });
84
+ }
85
+ }
86
+ /**
87
+ * @desc Removes a user from all channels
88
+ * @param clientId - The client id of the user to remove
89
+ * @private
90
+ */
91
+ _removeUser(clientId) {
92
+ this._channels.forEach((channel) => {
93
+ channel.removeUser(clientId, true);
94
+ });
95
+ }
96
+ /**
97
+ * @desc Creates a new channel
98
+ * @param channelName - The name of the channel to create
99
+ * @private
100
+ */
101
+ _createChannel(channelName) {
102
+ const destroyChannel = this._destroyChannel.bind(this, channelName);
103
+ const execute = this._middleware.run.bind(this._middleware);
104
+ const parentEngine = {
105
+ execute,
106
+ destroyChannel,
107
+ };
108
+ const newChannel = new channelEngine_1.ChannelEngine(channelName, parentEngine);
109
+ this._channels.add(newChannel);
110
+ return newChannel;
111
+ }
112
+ /**
113
+ * @desc Executes a function on a channel
114
+ * @param channelName - The name of the channel to execute the function on
115
+ * @param handler - The function to execute
116
+ * @private
117
+ */
118
+ _execute(channelName, handler) {
119
+ const newChannel = this._getChannel(channelName);
120
+ if (newChannel) {
121
+ return handler(newChannel);
122
+ }
123
+ throw new Error(`GatewayEngine: Channel ${channelName} does not exist`);
124
+ }
125
+ /**
126
+ * @desc Gets a channel by name
127
+ * @param channelName - The name of the channel to get
128
+ * @private
129
+ */
130
+ _getChannel(channelName) {
131
+ return Array.from(this._channels)
132
+ .find((channel) => channel.name === channelName);
133
+ }
134
+ /**
135
+ * @desc Destroys a channel
136
+ * @param channel - The name of the channel to destroy
137
+ * @private
138
+ */
139
+ _destroyChannel(channel) {
140
+ const newChannel = this._getChannel(channel);
141
+ if (newChannel) {
142
+ this._channels.delete(newChannel);
143
+ }
144
+ }
145
+ /**
146
+ * @desc Lists all channels
147
+ * @private
148
+ */
149
+ _listChannels() {
150
+ return Array.from(this._channels)
151
+ .map((channel) => channel.name);
152
+ }
153
+ /**
154
+ * @desc Gets all channels
155
+ * @private
156
+ */
157
+ _getChannels() {
158
+ return Array.from(this._channels);
159
+ }
160
+ }
161
+ exports.PondChannel = PondChannel;
@@ -0,0 +1,108 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const joinResponse_1 = require("./joinResponse");
4
+ const channelEngine_1 = require("../channel/channelEngine");
5
+ const channelResponse_test_1 = require("../channel/channelResponse.test");
6
+ const createPondResponse = () => {
7
+ const channelEngine = (0, channelResponse_test_1.createChannelEngine)();
8
+ const socket = {
9
+ clientId: 'sender',
10
+ assigns: { assign: 'assign' },
11
+ socket: {
12
+ send: jest.fn(),
13
+ },
14
+ };
15
+ const response = new joinResponse_1.JoinResponse(socket, channelEngine);
16
+ return { channelEngine,
17
+ socket,
18
+ response };
19
+ };
20
+ /* eslint-disable line-comment-position, no-inline-comments */
21
+ describe('pondChannelResponse', () => {
22
+ it('should create a new PondChannelResponse', () => {
23
+ const { response } = createPondResponse();
24
+ expect(response).toBeDefined();
25
+ });
26
+ it('should return the responseSent', () => {
27
+ const { response } = createPondResponse();
28
+ expect(response.responseSent).toEqual(false);
29
+ });
30
+ it('should accept the request', () => {
31
+ const { response, channelEngine, socket } = createPondResponse();
32
+ // spy on the channelEngine to see if the user was added
33
+ jest.spyOn(channelEngine, 'addUser');
34
+ response.accept();
35
+ // check if the response was sent
36
+ expect(response.responseSent).toEqual(true);
37
+ expect(response.responseSent).toEqual(true);
38
+ expect(channelEngine.addUser).toHaveBeenCalledWith(socket.clientId, socket.assigns, expect.any(Function));
39
+ expect(channelEngine.getUserData(socket.clientId)).not.toBeNull();
40
+ });
41
+ it('should reject the request', () => {
42
+ const { response, channelEngine, socket } = createPondResponse();
43
+ // spy on the channelEngine to see if the user was added
44
+ jest.spyOn(channelEngine, 'addUser');
45
+ response.reject();
46
+ // check if the response was sent
47
+ expect(response.responseSent).toEqual(true);
48
+ expect(channelEngine.addUser).not.toHaveBeenCalled();
49
+ expect(channelEngine.getUserData(socket.clientId)).toBeUndefined();
50
+ // also check if the socket was sent a message
51
+ expect(socket.socket.send).toHaveBeenCalledWith(JSON.stringify({
52
+ event: 'POND_ERROR',
53
+ payload: {
54
+ message: 'Request to join channel test rejected: Unauthorized request',
55
+ code: 403,
56
+ },
57
+ channelName: 'test',
58
+ action: channelEngine_1.ServerActions.ERROR,
59
+ }));
60
+ });
61
+ it('should send a direct message', () => {
62
+ const { response, channelEngine, socket } = createPondResponse();
63
+ // spy on the channelEngine to see if the user was added
64
+ jest.spyOn(channelEngine, 'addUser');
65
+ response.send('POND_MESSAGE', { message: 'message' });
66
+ // check if the response was sent
67
+ expect(response.responseSent).toEqual(true);
68
+ expect(channelEngine.addUser).toHaveBeenCalled();
69
+ expect(channelEngine.getUserData(socket.clientId)).toStrictEqual({ assigns: { assign: 'assign' },
70
+ id: 'sender',
71
+ presence: {} });
72
+ // also check if the socket was sent a message
73
+ expect(socket.socket.send).toHaveBeenCalledWith(JSON.stringify({
74
+ action: channelEngine_1.ServerActions.SYSTEM,
75
+ event: 'POND_MESSAGE',
76
+ payload: {
77
+ message: 'message',
78
+ },
79
+ channelName: 'test',
80
+ }));
81
+ });
82
+ // auxiliary functions
83
+ it('should send messages to different users', () => {
84
+ const { response, channelEngine } = createPondResponse();
85
+ // spy on the channelEngine to see if any messages were published
86
+ const broadcast = jest.spyOn(channelEngine, 'sendMessage');
87
+ // add a second user to the channel
88
+ channelEngine.addUser('user2', { assign: 'assign' }, () => { });
89
+ // send a message to a single user
90
+ expect(() => response.sendToUsers('hello_everyone', { message: 'hello' }, ['user2'])).toThrow(); // this is because the sender does not exist in the channel yet
91
+ // clear the spy
92
+ broadcast.mockClear();
93
+ // add the sender to the channel by using the response.accept() method
94
+ response.accept().sendToUsers('hello_everyone', { message: 'hello' }, ['user2']);
95
+ // check if the message was sent
96
+ expect(broadcast).toHaveBeenCalledWith('sender', ['user2'], channelEngine_1.ServerActions.BROADCAST, 'hello_everyone', { message: 'hello' });
97
+ // clear the spy
98
+ broadcast.mockClear();
99
+ // send a message to all users
100
+ response.broadcast('hello_everyone', { message: 'hello' });
101
+ expect(broadcast).toHaveBeenCalledWith('sender', 'all_users', channelEngine_1.ServerActions.BROADCAST, 'hello_everyone', { message: 'hello' });
102
+ // clear the spy
103
+ broadcast.mockClear();
104
+ // send a message to all users except the sender
105
+ response.broadcastFromUser('hello_everyone', { message: 'hello' });
106
+ expect(broadcast).toHaveBeenCalledWith('sender', 'all_except_sender', channelEngine_1.ServerActions.BROADCAST, 'hello_everyone', { message: 'hello' });
107
+ });
108
+ });
@@ -0,0 +1,112 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PresenceEngine = exports.PresenceEventTypes = void 0;
4
+ const subjectUtils_1 = require("../utils/subjectUtils");
5
+ var PresenceEventTypes;
6
+ (function (PresenceEventTypes) {
7
+ PresenceEventTypes["JOIN"] = "JOIN";
8
+ PresenceEventTypes["LEAVE"] = "LEAVE";
9
+ PresenceEventTypes["UPDATE"] = "UPDATE";
10
+ })(PresenceEventTypes = exports.PresenceEventTypes || (exports.PresenceEventTypes = {}));
11
+ class PresenceEngine {
12
+ constructor() {
13
+ this._presence = new subjectUtils_1.BehaviorSubject();
14
+ this._presenceMap = new Map();
15
+ }
16
+ /**
17
+ * @desc Lists all the presence of the users
18
+ */
19
+ getPresence() {
20
+ return Array.from(this._presenceMap.entries())
21
+ .reduce((acc, [key, value]) => {
22
+ acc[key] = value;
23
+ return acc;
24
+ }, {});
25
+ }
26
+ /**
27
+ * @desc Returns the presence of a user
28
+ * @param userId - The id of the user
29
+ */
30
+ getUserPresence(userId) {
31
+ return this._presenceMap.get(userId);
32
+ }
33
+ /**
34
+ * @desc Removes a presence from the presence engine
35
+ * @param presenceKey - The key of the presence
36
+ */
37
+ removePresence(presenceKey) {
38
+ const presence = this._presenceMap.get(presenceKey);
39
+ if (presence) {
40
+ this._presence.unsubscribe(presenceKey);
41
+ this._presenceMap.delete(presenceKey);
42
+ if (this._presenceMap.size > 0) {
43
+ this._presence.next({
44
+ type: PresenceEventTypes.LEAVE,
45
+ changed: presence,
46
+ presence: Array.from(this._presenceMap.values()),
47
+ });
48
+ }
49
+ }
50
+ else {
51
+ throw new Error(`PresenceEngine: Presence with key ${presenceKey} does not exist`);
52
+ }
53
+ }
54
+ /**
55
+ * @desc Updates a presence
56
+ * @param presenceKey - The key of the presence
57
+ * @param presence - The new presence
58
+ */
59
+ updatePresence(presenceKey, presence) {
60
+ const oldPresence = this._presenceMap.get(presenceKey);
61
+ if (oldPresence) {
62
+ this._presenceMap.set(presenceKey, presence);
63
+ this._presence.next({
64
+ type: PresenceEventTypes.UPDATE,
65
+ changed: Object.assign(Object.assign({}, oldPresence), presence),
66
+ presence: Array.from(this._presenceMap.values()),
67
+ });
68
+ }
69
+ else {
70
+ throw new Error(`PresenceEngine: Presence with key ${presenceKey} does not exist`);
71
+ }
72
+ }
73
+ /**
74
+ * @desc Tracks a presence
75
+ * @param presenceKey - The key of the presence
76
+ * @param presence - The presence
77
+ * @param onPresenceChange - The callback to be called when the presence changes
78
+ */
79
+ trackPresence(presenceKey, presence, onPresenceChange) {
80
+ this._insertPresence(presenceKey, presence);
81
+ return this._subscribe(presenceKey, onPresenceChange);
82
+ }
83
+ /**
84
+ * @desc Inserts a presence into the presence engine
85
+ * @param presenceKey - The key of the presence
86
+ * @param presence - The presence
87
+ * @private
88
+ */
89
+ _insertPresence(presenceKey, presence) {
90
+ if (!this._presenceMap.has(presenceKey)) {
91
+ this._presenceMap.set(presenceKey, presence);
92
+ this._presence.next({
93
+ type: PresenceEventTypes.JOIN,
94
+ changed: presence,
95
+ presence: Array.from(this._presenceMap.values()),
96
+ });
97
+ }
98
+ else {
99
+ throw new Error(`PresenceEngine: Presence with key ${presenceKey} already exists`);
100
+ }
101
+ }
102
+ /**
103
+ * @desc Subscribes to the presence engine
104
+ * @param identifier - The identifier of the observer
105
+ * @param observer - The observer
106
+ * @private
107
+ */
108
+ _subscribe(identifier, observer) {
109
+ return this._presence.subscribe(identifier, observer);
110
+ }
111
+ }
112
+ exports.PresenceEngine = PresenceEngine;
@@ -0,0 +1,104 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const presenceEngine_1 = require("./presenceEngine");
4
+ /* eslint-disable @typescript-eslint/ban-ts-comment */
5
+ describe('PresenceEngine', () => {
6
+ let presenceEngine;
7
+ let presence;
8
+ let presenceKey;
9
+ let onPresenceChange;
10
+ beforeEach(() => {
11
+ presenceEngine = new presenceEngine_1.PresenceEngine();
12
+ presence = {
13
+ id: 'id',
14
+ name: 'name',
15
+ color: 'color',
16
+ type: 'type',
17
+ location: 'location',
18
+ };
19
+ presenceKey = 'presenceKey';
20
+ onPresenceChange = jest.fn();
21
+ });
22
+ describe('trackPresence', () => {
23
+ it('should insert a presence into the presence engine', () => {
24
+ // spy on the private method _insertPresence
25
+ // @ts-ignore
26
+ jest.spyOn(presenceEngine, '_insertPresence');
27
+ presenceEngine.trackPresence(presenceKey, presence, onPresenceChange);
28
+ // @ts-ignore
29
+ expect(presenceEngine._insertPresence).toHaveBeenCalledWith(presenceKey, presence);
30
+ expect(onPresenceChange).toHaveBeenCalledWith({
31
+ type: presenceEngine_1.PresenceEventTypes.JOIN,
32
+ changed: presence,
33
+ presence: [presence],
34
+ });
35
+ });
36
+ it('should subscribe to the presence engine', () => {
37
+ // spy on the private method _subscribe
38
+ // @ts-ignore
39
+ jest.spyOn(presenceEngine, '_subscribe');
40
+ presenceEngine.trackPresence(presenceKey, presence, onPresenceChange);
41
+ // @ts-ignore
42
+ expect(presenceEngine._subscribe).toHaveBeenCalledWith(presenceKey, onPresenceChange);
43
+ expect(onPresenceChange).toHaveBeenCalledWith({
44
+ type: presenceEngine_1.PresenceEventTypes.JOIN,
45
+ changed: presence,
46
+ presence: [presence],
47
+ });
48
+ });
49
+ it('should throw an error if the presence already exists', () => {
50
+ presenceEngine.trackPresence(presenceKey, presence, onPresenceChange);
51
+ expect(() => presenceEngine.trackPresence(presenceKey, presence, onPresenceChange)).toThrowError(`PresenceEngine: Presence with key ${presenceKey} already exists`);
52
+ });
53
+ });
54
+ describe('updatePresence', () => {
55
+ it('should update a presence', () => {
56
+ presenceEngine.trackPresence(presenceKey, presence, onPresenceChange);
57
+ const newPresence = {
58
+ id: 'id',
59
+ name: 'name',
60
+ color: 'color',
61
+ type: 'type',
62
+ location: 'location',
63
+ };
64
+ presenceEngine.updatePresence(presenceKey, newPresence);
65
+ expect(onPresenceChange).toHaveBeenCalledWith({
66
+ type: presenceEngine_1.PresenceEventTypes.UPDATE,
67
+ changed: Object.assign(Object.assign({}, presence), newPresence),
68
+ presence: [newPresence],
69
+ });
70
+ });
71
+ it('should throw an error if the presence does not exist', () => {
72
+ expect(() => presenceEngine.updatePresence(presenceKey, presence)).toThrowError(`PresenceEngine: Presence with key ${presenceKey} does not exist`);
73
+ });
74
+ });
75
+ describe('removePresence', () => {
76
+ it('should remove a presence from the presence engine', () => {
77
+ presenceEngine.trackPresence(presenceKey, presence, onPresenceChange);
78
+ presenceEngine.trackPresence('presenceKey2', Object.assign(Object.assign({}, presence), { key: 'presence2' }), onPresenceChange);
79
+ presenceEngine.removePresence(presenceKey);
80
+ expect(onPresenceChange).toHaveBeenCalledWith({
81
+ type: presenceEngine_1.PresenceEventTypes.LEAVE,
82
+ changed: presence,
83
+ presence: [
84
+ Object.assign(Object.assign({}, presence), { key: 'presence2' }),
85
+ ],
86
+ });
87
+ // @ts-ignore
88
+ onPresenceChange.mockClear();
89
+ presenceEngine.removePresence('presenceKey2');
90
+ expect(onPresenceChange).not.toHaveBeenCalled();
91
+ });
92
+ it('should throw an error if the presence does not exist', () => {
93
+ expect(() => presenceEngine.removePresence(presenceKey)).toThrowError(`PresenceEngine: Presence with key ${presenceKey} does not exist`);
94
+ });
95
+ });
96
+ describe('getPresence', () => {
97
+ it('should return the presence', () => {
98
+ presenceEngine.trackPresence(presenceKey, presence, onPresenceChange);
99
+ const data = {};
100
+ data[presenceKey] = presence;
101
+ expect(presenceEngine.getPresence()).toEqual(data);
102
+ });
103
+ });
104
+ });
@@ -0,0 +1,122 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PondSocket = void 0;
4
+ const http_1 = require("http");
5
+ const ws_1 = require("ws");
6
+ const endpoint_1 = require("../endpoint/endpoint");
7
+ const matchPattern_1 = require("../utils/matchPattern");
8
+ class PondSocket {
9
+ constructor(server, socketServer) {
10
+ this._middleware = [];
11
+ this._matcher = new matchPattern_1.MatchPattern();
12
+ this._server = server || new http_1.Server();
13
+ this._socketServer = socketServer || new ws_1.WebSocketServer({ noServer: true });
14
+ this._init();
15
+ }
16
+ /**
17
+ * @desc Specifies the port to listen on
18
+ * @param port - the port to listen on
19
+ * @param callback - the callback to call when the server is listening
20
+ */
21
+ listen(port, callback) {
22
+ return this._server.listen(port, callback);
23
+ }
24
+ /**
25
+ * @desc Closes the server
26
+ * @param callback - the callback to call when the server is closed
27
+ */
28
+ close(callback) {
29
+ return this._server.close(callback);
30
+ }
31
+ /**
32
+ * @desc Accepts a new socket upgrade request on the provided endpoint using the handler function to authenticate the socket
33
+ * @param path - the pattern to accept || can also be a regex
34
+ * @param handler - the handler function to authenticate the socket
35
+ * @example
36
+ * const endpoint = pond.createEndpoint('/api/socket', (req, res) => {
37
+ * const token = req.query.token;
38
+ * if (!token)
39
+ * return res.reject("No token provided");
40
+ * res.accept({
41
+ * assign: {
42
+ * token
43
+ * }
44
+ * });
45
+ * })
46
+ */
47
+ createEndpoint(path, handler) {
48
+ const endpoint = new endpoint_1.Endpoint(this._socketServer);
49
+ this._middleware.push((req, socket, head, next) => {
50
+ const address = req.url || '';
51
+ const dataEndpoint = this._matcher.parseEvent(path, address);
52
+ if (!dataEndpoint) {
53
+ return next();
54
+ }
55
+ endpoint._authoriseConnection(req, socket, head, dataEndpoint, handler);
56
+ });
57
+ return endpoint;
58
+ }
59
+ /**
60
+ * @desc Adds a middleware function to the socket server
61
+ * @param middleware - the middleware function to add
62
+ */
63
+ use(middleware) {
64
+ this._middleware.push(middleware);
65
+ }
66
+ /**
67
+ * @desc managed the heartbeat of the socket server
68
+ * @private
69
+ */
70
+ _manageHeartbeat() {
71
+ this._socketServer.on('connection', (socket) => {
72
+ socket.on('pong', () => {
73
+ socket.isAlive = true;
74
+ });
75
+ });
76
+ const interval = setInterval(() => {
77
+ this._socketServer.clients.forEach((socket) => {
78
+ if (socket.isAlive === false) {
79
+ return socket.terminate();
80
+ }
81
+ socket.isAlive = false;
82
+ socket.ping(() => { });
83
+ });
84
+ }, 30000);
85
+ this._socketServer.on('close', () => clearInterval(interval));
86
+ }
87
+ /**
88
+ * @desc executes the middleware functions
89
+ * @param req - the request object
90
+ * @param socket - the socket object
91
+ * @param head - the head buffer
92
+ * @private
93
+ */
94
+ _execute(req, socket, head) {
95
+ const temp = this._middleware.concat();
96
+ const next = () => {
97
+ const middleware = temp.shift();
98
+ if (middleware) {
99
+ middleware(req, socket, head, next);
100
+ }
101
+ else {
102
+ socket.write('HTTP/1.1 400 Bad Request\r\n\r');
103
+ socket.destroy();
104
+ }
105
+ };
106
+ next();
107
+ }
108
+ /**
109
+ * @desc initialises the socket server
110
+ * @private
111
+ */
112
+ _init() {
113
+ this._manageHeartbeat();
114
+ this._server.on('error', (error) => {
115
+ throw new Error(error.message);
116
+ });
117
+ this._server.on('upgrade', (req, socket, head) => {
118
+ this._execute(req, socket, head);
119
+ });
120
+ }
121
+ }
122
+ exports.PondSocket = PondSocket;
@@ -12,24 +12,13 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
12
12
  return (mod && mod.__esModule) ? mod : { "default": mod };
13
13
  };
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- const endpoint_1 = require("./endpoint");
16
- const pondSocket_1 = require("./pondSocket");
17
- const superwstest_1 = __importDefault(require("superwstest"));
18
- const enums_1 = require("./enums");
19
15
  const http_1 = require("http");
16
+ const superwstest_1 = __importDefault(require("superwstest"));
20
17
  const ws_1 = require("ws");
18
+ const pondSocket_1 = require("./pondSocket");
19
+ const channelEngine_1 = require("../channel/channelEngine");
20
+ const endpoint_1 = require("../endpoint/endpoint");
21
21
  describe('server', () => {
22
- it('should be defined', () => {
23
- expect(pondSocket_1.PondSocket).toBeDefined();
24
- });
25
- it('should be instantiable', () => {
26
- expect(new pondSocket_1.PondSocket()).toBeInstanceOf(pondSocket_1.PondSocket);
27
- });
28
- it('should ba able to create its own server and websocket server if none is provided', () => {
29
- const socket = new pondSocket_1.PondSocket();
30
- expect(socket['_server']).toBeDefined();
31
- expect(socket['_socketServer']).toBeDefined();
32
- });
33
22
  it('should take a server and websocket server if provided', () => {
34
23
  const server = (0, http_1.createServer)();
35
24
  const socketServer = new ws_1.Server({ noServer: true });
@@ -50,7 +39,6 @@ describe('server', () => {
50
39
  console.log('socket');
51
40
  });
52
41
  expect(endpoint).toBeInstanceOf(endpoint_1.Endpoint);
53
- expect(endpoint['_handler']).toEqual(expect.any(Function));
54
42
  });
55
43
  it('should be able to create multiple endpoints', () => {
56
44
  const server = (0, http_1.createServer)();
@@ -63,9 +51,7 @@ describe('server', () => {
63
51
  console.log('socket2');
64
52
  });
65
53
  expect(endpoint).toBeInstanceOf(endpoint_1.Endpoint);
66
- expect(endpoint['_handler']).toEqual(expect.any(Function));
67
54
  expect(endpoint2).toBeInstanceOf(endpoint_1.Endpoint);
68
- expect(endpoint2['_handler']).toEqual(expect.any(Function));
69
55
  });
70
56
  it('should be able to reject a socket', () => {
71
57
  const server = (0, http_1.createServer)();
@@ -99,7 +85,7 @@ describe('server', () => {
99
85
  });
100
86
  yield (0, superwstest_1.default)(server)
101
87
  .ws('/api/socket')
102
- .expectUpgrade(res => expect(res.statusCode).toBe(101))
88
+ .expectUpgrade((res) => expect(res.statusCode).toBe(101))
103
89
  .close()
104
90
  .expectClosed();
105
91
  server.close();
@@ -131,12 +117,12 @@ describe('server', () => {
131
117
  });
132
118
  yield (0, superwstest_1.default)(server)
133
119
  .ws('/api/socket')
134
- .expectUpgrade(res => expect(res.statusCode).toBe(101))
120
+ .expectUpgrade((res) => expect(res.statusCode).toBe(101))
135
121
  .expectJson({
136
- action: enums_1.ServerActions.MESSAGE,
137
122
  event: 'testEvent',
138
123
  channelName: 'SERVER',
139
124
  payload: { test: 'test' },
125
+ action: channelEngine_1.ServerActions.SYSTEM,
140
126
  })
141
127
  .close()
142
128
  .expectClosed();