@eleven-am/pondsocket 0.1.141 → 0.1.142
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 +15 -15
- package/channel/channel.js +81 -61
- package/channel/channel.test.js +86 -61
- package/channel/eventRequest.test.js +3 -3
- package/channel/eventResponse.js +36 -61
- package/channel/eventResponse.test.js +31 -53
- package/endpoint/endpoint.test.js +30 -13
- package/endpoint/endpointEngine.js +321 -0
- package/endpoint/response.js +48 -14
- package/lobby/JoinRequest.test.js +17 -6
- package/lobby/JoinResponse.test.js +18 -23
- package/lobby/joinResponse.js +70 -31
- package/lobby/lobby.js +18 -22
- package/package.json +8 -8
- package/presence/presence.js +1 -1
- package/presence/presenceEngine.test.js +1 -4
- package/schema.js +0 -1
- package/server/pondSocket.js +9 -6
- package/types.d.ts +200 -138
- package/abstracts/abstractResponse.js +0 -9
- package/endpoint/endpoint.js +0 -239
|
@@ -6,8 +6,13 @@ const channel_1 = require("./channel");
|
|
|
6
6
|
const channel_test_1 = require("./channel.test");
|
|
7
7
|
const eventResponse_1 = require("./eventResponse");
|
|
8
8
|
const createChannelEngine = () => {
|
|
9
|
-
const parentEngine = (0, channel_test_1.createParentEngine)();
|
|
10
|
-
|
|
9
|
+
const { parentEngine, socket } = (0, channel_test_1.createParentEngine)();
|
|
10
|
+
const channelEngine = new channel_1.ChannelEngine('test', parentEngine);
|
|
11
|
+
return {
|
|
12
|
+
channelEngine,
|
|
13
|
+
parentEngine,
|
|
14
|
+
socket,
|
|
15
|
+
};
|
|
11
16
|
};
|
|
12
17
|
exports.createChannelEngine = createChannelEngine;
|
|
13
18
|
const createChannelEvent = (name) => {
|
|
@@ -18,7 +23,6 @@ const createChannelEvent = (name) => {
|
|
|
18
23
|
},
|
|
19
24
|
sender: 'sender',
|
|
20
25
|
action: pondsocket_common_1.ServerActions.BROADCAST,
|
|
21
|
-
recipients: ['recipient'],
|
|
22
26
|
channelName: name,
|
|
23
27
|
requestId: 'requestId',
|
|
24
28
|
};
|
|
@@ -26,10 +30,11 @@ const createChannelEvent = (name) => {
|
|
|
26
30
|
};
|
|
27
31
|
exports.createChannelEvent = createChannelEvent;
|
|
28
32
|
const createChannelResponse = () => {
|
|
29
|
-
const channelEngine = (0, exports.createChannelEngine)();
|
|
33
|
+
const { channelEngine, socket } = (0, exports.createChannelEngine)();
|
|
30
34
|
const event = (0, exports.createChannelEvent)(channelEngine.name);
|
|
31
|
-
channelEngine.addUser(event.sender, { assign: 'assign' }, () => { });
|
|
32
|
-
|
|
35
|
+
const unsub = channelEngine.addUser(event.sender, { assign: 'assign' }, () => { });
|
|
36
|
+
socket.subscriptions.set(channelEngine.name, unsub);
|
|
37
|
+
channelEngine.addUser('tester', { assign: 'assign' }, () => { });
|
|
33
38
|
const response = new eventResponse_1.EventResponse(event, channelEngine);
|
|
34
39
|
return {
|
|
35
40
|
channelEngine,
|
|
@@ -42,52 +47,36 @@ describe('ChannelResponse', () => {
|
|
|
42
47
|
const { response } = createChannelResponse();
|
|
43
48
|
expect(response).toBeDefined();
|
|
44
49
|
});
|
|
45
|
-
it('should accept the request', () => {
|
|
46
|
-
const { response, channelEngine } = createChannelResponse();
|
|
47
|
-
jest.spyOn(channelEngine, 'sendMessage');
|
|
48
|
-
response.accept();
|
|
49
|
-
expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', 'sender', ['recipient'], pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
|
|
50
|
-
});
|
|
51
|
-
it('should reject the request', () => {
|
|
52
|
-
const { response, channelEngine, event } = createChannelResponse();
|
|
53
|
-
jest.spyOn(channelEngine, 'sendMessage');
|
|
54
|
-
response.reject();
|
|
55
|
-
expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', pondsocket_common_1.SystemSender.CHANNEL, [event.sender], pondsocket_common_1.ServerActions.ERROR, pondsocket_common_1.ErrorTypes.UNAUTHORIZED_BROADCAST, {
|
|
56
|
-
message: 'Unauthorized request',
|
|
57
|
-
code: 403,
|
|
58
|
-
});
|
|
59
|
-
});
|
|
60
50
|
it('should send a direct message', () => {
|
|
61
51
|
const { response, channelEngine, event } = createChannelResponse();
|
|
62
52
|
jest.spyOn(channelEngine, 'sendMessage');
|
|
63
|
-
response.
|
|
64
|
-
expect(channelEngine.sendMessage).toHaveBeenCalledWith(
|
|
53
|
+
response.reply('event', { payload: 'payload' });
|
|
54
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith(pondsocket_common_1.SystemSender.CHANNEL, [event.sender], pondsocket_common_1.ServerActions.SYSTEM, 'event', { payload: 'payload' }, 'requestId');
|
|
65
55
|
});
|
|
66
56
|
it('should broadcast a message', () => {
|
|
67
57
|
const { response, channelEngine } = createChannelResponse();
|
|
68
58
|
jest.spyOn(channelEngine, 'sendMessage');
|
|
69
59
|
response.broadcast('event', { payload: 'payload' });
|
|
70
|
-
expect(channelEngine.sendMessage).toHaveBeenCalledWith('
|
|
60
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' }, 'requestId');
|
|
71
61
|
});
|
|
72
62
|
it('should broadcastFromUser a message', () => {
|
|
73
63
|
const { response, channelEngine } = createChannelResponse();
|
|
74
64
|
jest.spyOn(channelEngine, 'sendMessage');
|
|
75
|
-
response.
|
|
76
|
-
expect(channelEngine.sendMessage).toHaveBeenCalledWith('
|
|
65
|
+
response.broadcastFrom('event', { payload: 'payload' });
|
|
66
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER, pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' }, 'requestId');
|
|
77
67
|
});
|
|
78
68
|
it('should sendToUsers a message', () => {
|
|
79
69
|
const { response, channelEngine } = createChannelResponse();
|
|
80
70
|
jest.spyOn(channelEngine, 'sendMessage');
|
|
81
|
-
response.
|
|
82
|
-
expect(channelEngine.sendMessage).toHaveBeenCalledWith('
|
|
71
|
+
response.broadcastTo('event', { payload: 'payload' }, 'tester');
|
|
72
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', ['tester'], pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' }, 'requestId');
|
|
73
|
+
channelEngine.sendMessage.mockClear();
|
|
74
|
+
response.broadcastTo('event', { payload: 'payload' }, ['tester', 'sender']);
|
|
75
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', ['tester', 'sender'], pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' }, 'requestId');
|
|
83
76
|
});
|
|
84
77
|
it('should fail to send to non existing users', () => {
|
|
85
|
-
const {
|
|
86
|
-
|
|
87
|
-
event.recipients = ['non_existing_user'];
|
|
88
|
-
expect(() => response.accept()).toThrow('ChannelEngine: Invalid recipients non_existing_user some users do not exist in channel test');
|
|
89
|
-
expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', event.sender, ['non_existing_user'], pondsocket_common_1.ServerActions.BROADCAST, event.event, event.payload);
|
|
90
|
-
expect(() => response.sendToUsers('event', { payload: 'payload' }, ['non_existing_user']))
|
|
78
|
+
const { response } = createChannelResponse();
|
|
79
|
+
expect(() => response.broadcastTo('event', { payload: 'payload' }, ['non_existing_user']))
|
|
91
80
|
.toThrow('ChannelEngine: Invalid recipients non_existing_user some users do not exist in channel test');
|
|
92
81
|
});
|
|
93
82
|
it('should track a trackPresence', () => {
|
|
@@ -99,7 +88,7 @@ describe('ChannelResponse', () => {
|
|
|
99
88
|
it('should update a users assign data', () => {
|
|
100
89
|
const { response, channelEngine } = createChannelResponse();
|
|
101
90
|
jest.spyOn(channelEngine, 'updateAssigns');
|
|
102
|
-
response.
|
|
91
|
+
response.assign({ assign: 'updated' });
|
|
103
92
|
expect(channelEngine.updateAssigns).toHaveBeenCalledWith('sender', { assign: 'updated' });
|
|
104
93
|
});
|
|
105
94
|
it('should evict a user', () => {
|
|
@@ -109,7 +98,11 @@ describe('ChannelResponse', () => {
|
|
|
109
98
|
expect(channelEngine.kickUser).toHaveBeenCalledWith('sender', 'recipient');
|
|
110
99
|
});
|
|
111
100
|
it('should destroy the channel', () => {
|
|
112
|
-
const {
|
|
101
|
+
const { channelEngine, socket } = (0, exports.createChannelEngine)();
|
|
102
|
+
const event = (0, exports.createChannelEvent)(channelEngine.name);
|
|
103
|
+
const unsub = channelEngine.addUser(event.sender, { assign: 'assign' }, () => { });
|
|
104
|
+
socket.subscriptions.set(channelEngine.name, unsub);
|
|
105
|
+
const response = new eventResponse_1.EventResponse(event, channelEngine);
|
|
113
106
|
jest.spyOn(channelEngine, 'destroy');
|
|
114
107
|
response.closeChannel('recipient');
|
|
115
108
|
expect(channelEngine.destroy).toHaveBeenCalledWith('recipient');
|
|
@@ -118,7 +111,6 @@ describe('ChannelResponse', () => {
|
|
|
118
111
|
const { response, channelEngine } = createChannelResponse();
|
|
119
112
|
jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
|
|
120
113
|
expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
|
|
121
|
-
response.accept();
|
|
122
114
|
response.trackPresence({ status: 'online' });
|
|
123
115
|
expect(channelEngine.presenceEngine.trackPresence).toHaveBeenCalledWith('sender', { status: 'online' });
|
|
124
116
|
});
|
|
@@ -126,7 +118,6 @@ describe('ChannelResponse', () => {
|
|
|
126
118
|
const { response, channelEngine } = createChannelResponse();
|
|
127
119
|
jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
|
|
128
120
|
expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
|
|
129
|
-
response.accept();
|
|
130
121
|
response.trackPresence({ status: 'online' });
|
|
131
122
|
expect(channelEngine.presenceEngine.trackPresence).toHaveBeenCalledWith('sender', { status: 'online' });
|
|
132
123
|
expect(() => response.trackPresence({ status: 'online' })).toThrow('PresenceEngine: Presence with key sender already exists');
|
|
@@ -135,7 +126,6 @@ describe('ChannelResponse', () => {
|
|
|
135
126
|
const { response, channelEngine } = createChannelResponse();
|
|
136
127
|
jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
|
|
137
128
|
expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
|
|
138
|
-
response.accept();
|
|
139
129
|
expect(() => response.trackPresence({ status: 'online' }, 'non_existent_user'))
|
|
140
130
|
.toThrow('ChannelEngine: Invalid recipients non_existent_user some users do not exist in channel test');
|
|
141
131
|
});
|
|
@@ -144,7 +134,6 @@ describe('ChannelResponse', () => {
|
|
|
144
134
|
const { response, channelEngine } = createChannelResponse();
|
|
145
135
|
jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
|
|
146
136
|
expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
|
|
147
|
-
response.accept();
|
|
148
137
|
response.trackPresence({ status: 'online' });
|
|
149
138
|
expect(channelEngine.presenceEngine.trackPresence).toHaveBeenCalledWith('sender', { status: 'online' });
|
|
150
139
|
expect((_a = channelEngine.presenceEngine) === null || _a === void 0 ? void 0 : _a.getPresence()).toEqual({
|
|
@@ -159,7 +148,6 @@ describe('ChannelResponse', () => {
|
|
|
159
148
|
const { response, channelEngine } = createChannelResponse();
|
|
160
149
|
jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
|
|
161
150
|
expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
|
|
162
|
-
response.accept();
|
|
163
151
|
response.trackPresence({ status: 'online' });
|
|
164
152
|
expect(() => response.updatePresence({ status: 'online' }, 'non_existent_user'))
|
|
165
153
|
.toThrow('PresenceEngine: Presence with key non_existent_user does not exist');
|
|
@@ -171,7 +159,7 @@ describe('ChannelResponse', () => {
|
|
|
171
159
|
expect((_a = channelEngine.presenceEngine) === null || _a === void 0 ? void 0 : _a.getPresence()).toEqual({
|
|
172
160
|
sender: { status: 'online' },
|
|
173
161
|
});
|
|
174
|
-
response.
|
|
162
|
+
response.removePresence();
|
|
175
163
|
expect((_b = channelEngine.presenceEngine) === null || _b === void 0 ? void 0 : _b.getPresence()).toEqual({});
|
|
176
164
|
});
|
|
177
165
|
it('should throw an error if unTrackPresence is called for a non existing user', () => {
|
|
@@ -181,17 +169,7 @@ describe('ChannelResponse', () => {
|
|
|
181
169
|
expect((_a = channelEngine.presenceEngine) === null || _a === void 0 ? void 0 : _a.getPresence()).toEqual({
|
|
182
170
|
sender: { status: 'online' },
|
|
183
171
|
});
|
|
184
|
-
expect(() => response.
|
|
172
|
+
expect(() => response.removePresence('non_existent_user'))
|
|
185
173
|
.toThrow('PresenceEngine: Presence with key non_existent_user does not exist');
|
|
186
174
|
});
|
|
187
|
-
it('should throw an error if accept, reject / send is called more than once', () => {
|
|
188
|
-
const { response, channelEngine } = createChannelResponse();
|
|
189
|
-
jest.spyOn(channelEngine, 'sendMessage');
|
|
190
|
-
expect(channelEngine.sendMessage).not.toHaveBeenCalled();
|
|
191
|
-
response.accept();
|
|
192
|
-
expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', 'sender', ['recipient'], pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
|
|
193
|
-
expect(() => response.accept()).toThrow('Event response has already been executed');
|
|
194
|
-
expect(() => response.reject()).toThrow('Event response has already been executed');
|
|
195
|
-
expect(() => response.send('event', { payload: 'payload' })).toThrow('Event response has already been executed');
|
|
196
|
-
});
|
|
197
175
|
});
|
|
@@ -12,19 +12,36 @@ 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
|
+
exports.createEndpointEngine = void 0;
|
|
15
16
|
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
16
17
|
const superwstest_1 = __importDefault(require("superwstest"));
|
|
17
18
|
const pondSocket_1 = require("../server/pondSocket");
|
|
18
19
|
/* eslint-disable line-comment-position, no-inline-comments */
|
|
20
|
+
const createEndpointEngine = (socket) => ({
|
|
21
|
+
createChannel: jest.fn(),
|
|
22
|
+
listConnections: jest.fn(),
|
|
23
|
+
getClients: jest.fn(),
|
|
24
|
+
broadcast: jest.fn(),
|
|
25
|
+
closeConnection: jest.fn(),
|
|
26
|
+
manageSocket: jest.fn(),
|
|
27
|
+
getUser: () => socket,
|
|
28
|
+
subscribeTo: jest.fn(),
|
|
29
|
+
unsubscribeFrom: jest.fn(),
|
|
30
|
+
});
|
|
31
|
+
exports.createEndpointEngine = createEndpointEngine;
|
|
19
32
|
describe('endpoint', () => {
|
|
20
33
|
let socket;
|
|
21
34
|
let server;
|
|
22
35
|
beforeEach((done) => {
|
|
23
36
|
socket = new pondSocket_1.PondSocket();
|
|
24
|
-
server = socket.listen(3000, 'localhost',
|
|
37
|
+
server = socket.listen(3000, 'localhost', () => {
|
|
38
|
+
done();
|
|
39
|
+
});
|
|
25
40
|
});
|
|
26
41
|
afterEach((done) => {
|
|
27
|
-
server.close(
|
|
42
|
+
server.close(() => {
|
|
43
|
+
done();
|
|
44
|
+
});
|
|
28
45
|
});
|
|
29
46
|
it('should be able to close a single socket', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
30
47
|
let count = 0;
|
|
@@ -37,7 +54,7 @@ describe('endpoint', () => {
|
|
|
37
54
|
}, 1000);
|
|
38
55
|
}
|
|
39
56
|
else {
|
|
40
|
-
res.
|
|
57
|
+
res.decline();
|
|
41
58
|
}
|
|
42
59
|
});
|
|
43
60
|
yield (0, superwstest_1.default)(server)
|
|
@@ -51,11 +68,11 @@ describe('endpoint', () => {
|
|
|
51
68
|
const endpoint = socket.createEndpoint('/api/:room', (req, res) => {
|
|
52
69
|
if (req.params.room === 'socket') {
|
|
53
70
|
res.accept();
|
|
54
|
-
const connections = endpoint.
|
|
71
|
+
const connections = endpoint.getClients();
|
|
55
72
|
expect(connections).toHaveLength(1);
|
|
56
73
|
}
|
|
57
74
|
else {
|
|
58
|
-
res.
|
|
75
|
+
res.decline();
|
|
59
76
|
}
|
|
60
77
|
});
|
|
61
78
|
yield (0, superwstest_1.default)(server)
|
|
@@ -67,7 +84,7 @@ describe('endpoint', () => {
|
|
|
67
84
|
socket.createEndpoint('/api/:path', (req, res) => {
|
|
68
85
|
count++;
|
|
69
86
|
expect(req.params.path).toBe('socket');
|
|
70
|
-
res.
|
|
87
|
+
res.decline();
|
|
71
88
|
});
|
|
72
89
|
yield (0, superwstest_1.default)(server)
|
|
73
90
|
.ws('/api/socket')
|
|
@@ -88,12 +105,12 @@ describe('endpoint', () => {
|
|
|
88
105
|
const endpoint = socket.createEndpoint('/api/:room', (req, res) => {
|
|
89
106
|
if (req.params.room === 'socket') {
|
|
90
107
|
res.accept();
|
|
91
|
-
const connections = endpoint.
|
|
108
|
+
const connections = endpoint.getClients();
|
|
92
109
|
expect(connections).toHaveLength(1);
|
|
93
110
|
endpoint.broadcast('TEST', { test: 'test' });
|
|
94
111
|
}
|
|
95
112
|
else {
|
|
96
|
-
res.
|
|
113
|
+
res.decline();
|
|
97
114
|
}
|
|
98
115
|
});
|
|
99
116
|
yield (0, superwstest_1.default)(server)
|
|
@@ -120,12 +137,12 @@ describe('endpoint', () => {
|
|
|
120
137
|
res.accept();
|
|
121
138
|
}
|
|
122
139
|
else {
|
|
123
|
-
res.
|
|
140
|
+
res.decline();
|
|
124
141
|
}
|
|
125
142
|
});
|
|
126
143
|
endpoint.createChannel('/test/:room', (req, res) => {
|
|
127
144
|
expect(req.event.params.room).toBeDefined();
|
|
128
|
-
res.accept({
|
|
145
|
+
res.accept().assign({
|
|
129
146
|
assigns: {
|
|
130
147
|
status: 'online',
|
|
131
148
|
},
|
|
@@ -162,7 +179,7 @@ describe('endpoint', () => {
|
|
|
162
179
|
});
|
|
163
180
|
endpoint.createChannel('/test/:room', (req, res) => {
|
|
164
181
|
expect(req.event.params.room).toBeDefined();
|
|
165
|
-
res.
|
|
182
|
+
res.decline('Something went wrong');
|
|
166
183
|
});
|
|
167
184
|
yield (0, superwstest_1.default)(server)
|
|
168
185
|
.ws('/api/socket')
|
|
@@ -212,7 +229,7 @@ describe('endpoint', () => {
|
|
|
212
229
|
}));
|
|
213
230
|
it('should throw an error if accept, reject / send is called more than once', () => __awaiter(void 0, void 0, void 0, function* () {
|
|
214
231
|
socket.createEndpoint('/api/:room', (_, res) => {
|
|
215
|
-
res.
|
|
232
|
+
res.reply('hello', {
|
|
216
233
|
test: 'test',
|
|
217
234
|
});
|
|
218
235
|
expect(() => res.accept()).toThrowError('Cannot execute response more than once');
|
|
@@ -244,7 +261,7 @@ describe('endpoint', () => {
|
|
|
244
261
|
res.accept();
|
|
245
262
|
});
|
|
246
263
|
channel.onEvent('echo', (req, res) => {
|
|
247
|
-
res.
|
|
264
|
+
res.reply('echo', req.event.payload);
|
|
248
265
|
});
|
|
249
266
|
channel.onEvent('broadcast', (req) => {
|
|
250
267
|
channel.broadcast('broadcast', Object.assign(Object.assign({}, req.event.payload), { broadcast: true }));
|
|
@@ -0,0 +1,321 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
|
+
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
5
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
|
|
6
|
+
return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
|
|
7
|
+
};
|
|
8
|
+
var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
|
|
9
|
+
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
|
|
10
|
+
if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
|
|
11
|
+
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
|
+
};
|
|
13
|
+
var _EndpointEngine_instances, _EndpointEngine_middleware, _EndpointEngine_channels, _EndpointEngine_sockets, _EndpointEngine_parentEngine, _EndpointEngine_sendMessage, _EndpointEngine_joinChannel, _EndpointEngine_execute, _EndpointEngine_retrieveChannel, _EndpointEngine_handleMessage, _EndpointEngine_readMessage, _EndpointEngine_buildError, _EndpointEngine_leaveChannel, _Endpoint_engine;
|
|
14
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
|
+
exports.Endpoint = exports.EndpointEngine = void 0;
|
|
16
|
+
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
17
|
+
const zod_1 = require("zod");
|
|
18
|
+
const middleware_1 = require("../abstracts/middleware");
|
|
19
|
+
const pondError_1 = require("../errors/pondError");
|
|
20
|
+
const joinRequest_1 = require("../lobby/joinRequest");
|
|
21
|
+
const joinResponse_1 = require("../lobby/joinResponse");
|
|
22
|
+
const lobby_1 = require("../lobby/lobby");
|
|
23
|
+
const matcher_1 = require("../matcher/matcher");
|
|
24
|
+
const schema_1 = require("../schema");
|
|
25
|
+
class EndpointEngine {
|
|
26
|
+
constructor(parent) {
|
|
27
|
+
_EndpointEngine_instances.add(this);
|
|
28
|
+
_EndpointEngine_middleware.set(this, void 0);
|
|
29
|
+
_EndpointEngine_channels.set(this, void 0);
|
|
30
|
+
_EndpointEngine_sockets.set(this, void 0);
|
|
31
|
+
_EndpointEngine_parentEngine.set(this, void 0);
|
|
32
|
+
__classPrivateFieldSet(this, _EndpointEngine_sockets, new Map(), "f");
|
|
33
|
+
__classPrivateFieldSet(this, _EndpointEngine_middleware, new middleware_1.Middleware(), "f");
|
|
34
|
+
__classPrivateFieldSet(this, _EndpointEngine_channels, new Set(), "f");
|
|
35
|
+
__classPrivateFieldSet(this, _EndpointEngine_parentEngine, parent, "f");
|
|
36
|
+
}
|
|
37
|
+
get parent() {
|
|
38
|
+
return __classPrivateFieldGet(this, _EndpointEngine_parentEngine, "f");
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* @desc Adds a new PondChannel to this path on this endpoint
|
|
42
|
+
* @param path - The path to add the channel to
|
|
43
|
+
* @param handler - The handler to use to authenticate the client
|
|
44
|
+
*
|
|
45
|
+
* @example
|
|
46
|
+
* const channel = endpoint.createChannel('/chat', (request, response) => {
|
|
47
|
+
* if (request.user.assigns.admin)
|
|
48
|
+
* response.accept();
|
|
49
|
+
*
|
|
50
|
+
* else
|
|
51
|
+
* response.reject('You are not an admin', 403);
|
|
52
|
+
* });
|
|
53
|
+
*/
|
|
54
|
+
createChannel(path, handler) {
|
|
55
|
+
const pondChannel = new lobby_1.LobbyEngine(this);
|
|
56
|
+
__classPrivateFieldGet(this, _EndpointEngine_middleware, "f").use((user, joinParams, next) => {
|
|
57
|
+
const event = (0, matcher_1.parseAddress)(path, user.channelName);
|
|
58
|
+
if (event) {
|
|
59
|
+
const newChannel = pondChannel.getChannel(user.channelName) || pondChannel.createChannel(user.channelName);
|
|
60
|
+
const request = new joinRequest_1.JoinRequest(user, joinParams, newChannel);
|
|
61
|
+
const response = new joinResponse_1.JoinResponse(user, newChannel);
|
|
62
|
+
if (request._parseQueries(path)) {
|
|
63
|
+
return handler(request, response);
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
next();
|
|
67
|
+
});
|
|
68
|
+
__classPrivateFieldGet(this, _EndpointEngine_channels, "f").add(pondChannel);
|
|
69
|
+
return new lobby_1.PondChannel(pondChannel);
|
|
70
|
+
}
|
|
71
|
+
/**
|
|
72
|
+
* @desc Gets all clients connected to this endpoint
|
|
73
|
+
*/
|
|
74
|
+
getClients() {
|
|
75
|
+
return [...__classPrivateFieldGet(this, _EndpointEngine_sockets, "f").values()];
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* @desc Broadcasts a message to all clients connected to this endpoint
|
|
79
|
+
* @param event - The event to broadcast
|
|
80
|
+
* @param payload - The payload to broadcast
|
|
81
|
+
*/
|
|
82
|
+
broadcast(event, payload) {
|
|
83
|
+
__classPrivateFieldGet(this, _EndpointEngine_sockets, "f").forEach(({ socket }) => {
|
|
84
|
+
const message = {
|
|
85
|
+
event,
|
|
86
|
+
payload,
|
|
87
|
+
requestId: (0, pondsocket_common_1.uuid)(),
|
|
88
|
+
action: pondsocket_common_1.ServerActions.BROADCAST,
|
|
89
|
+
channelName: pondsocket_common_1.SystemSender.ENDPOINT,
|
|
90
|
+
};
|
|
91
|
+
__classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_sendMessage).call(this, socket, message);
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
/**
|
|
95
|
+
* @desc Closes specific clients connected to this endpoint
|
|
96
|
+
* @param clientIds - The id for the client / clients to close
|
|
97
|
+
*/
|
|
98
|
+
closeConnection(clientIds) {
|
|
99
|
+
const clients = typeof clientIds === 'string' ? [clientIds] : clientIds;
|
|
100
|
+
this.getClients()
|
|
101
|
+
.forEach(({ clientId, socket }) => {
|
|
102
|
+
if (clients.includes(clientId)) {
|
|
103
|
+
socket.close();
|
|
104
|
+
}
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
/**
|
|
108
|
+
* @desc Manages a new socket connection
|
|
109
|
+
* @param cache - The socket cache
|
|
110
|
+
*/
|
|
111
|
+
manageSocket(cache) {
|
|
112
|
+
__classPrivateFieldGet(this, _EndpointEngine_sockets, "f").set(cache.clientId, cache);
|
|
113
|
+
const socket = cache.socket;
|
|
114
|
+
socket.addEventListener('message', (message) => {
|
|
115
|
+
__classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_readMessage).call(this, cache, message.data);
|
|
116
|
+
});
|
|
117
|
+
socket.addEventListener('close', () => {
|
|
118
|
+
cache.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
119
|
+
});
|
|
120
|
+
socket.addEventListener('error', () => {
|
|
121
|
+
cache.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
/**
|
|
125
|
+
* @desc Retrieves a user from the endpoint
|
|
126
|
+
* @param clientId - The id of the user to retrieve
|
|
127
|
+
* @private
|
|
128
|
+
*/
|
|
129
|
+
getUser(clientId) {
|
|
130
|
+
const user = __classPrivateFieldGet(this, _EndpointEngine_sockets, "f").get(clientId);
|
|
131
|
+
if (!user) {
|
|
132
|
+
throw new pondError_1.EndpointError(`GatewayEngine: User ${clientId} does not exist`, 404);
|
|
133
|
+
}
|
|
134
|
+
return user;
|
|
135
|
+
}
|
|
136
|
+
/**
|
|
137
|
+
* @desc Subscribes a user to a channel, Will join the channel if it exists or add to pending subscriptions
|
|
138
|
+
* @param userId - The id of the user to subscribe
|
|
139
|
+
* @param channel - The name of the channel to subscribe to
|
|
140
|
+
*/
|
|
141
|
+
subscribeTo(userId, channel) {
|
|
142
|
+
const user = this.getUser(userId);
|
|
143
|
+
const channelEngine = __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_retrieveChannel).call(this, channel);
|
|
144
|
+
if (!channelEngine) {
|
|
145
|
+
user.pendingSubscriptions.add(channel);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
user.subscriptions.set(channel, channelEngine.addUser(userId, user.assigns, (event) => {
|
|
149
|
+
user.socket.send(JSON.stringify(event));
|
|
150
|
+
}));
|
|
151
|
+
}
|
|
152
|
+
}
|
|
153
|
+
/**
|
|
154
|
+
* @desc Unsubscribes a user from a channel
|
|
155
|
+
* @param userId - The id of the user to unsubscribe
|
|
156
|
+
* @param channel - The name of the channel to unsubscribe from
|
|
157
|
+
*/
|
|
158
|
+
unsubscribeFrom(userId, channel) {
|
|
159
|
+
const user = this.getUser(userId);
|
|
160
|
+
const unsubscribe = user.subscriptions.get(channel);
|
|
161
|
+
if (unsubscribe) {
|
|
162
|
+
unsubscribe();
|
|
163
|
+
user.subscriptions.delete(channel);
|
|
164
|
+
}
|
|
165
|
+
else if (user.pendingSubscriptions.has(channel)) {
|
|
166
|
+
user.pendingSubscriptions.delete(channel);
|
|
167
|
+
}
|
|
168
|
+
else {
|
|
169
|
+
throw new pondError_1.EndpointError(`GatewayEngine: User ${userId} is not subscribed to ${channel}`, 404);
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
subscribePendingUsers(channel) {
|
|
173
|
+
const users = [...__classPrivateFieldGet(this, _EndpointEngine_sockets, "f").values()]
|
|
174
|
+
.filter(({ pendingSubscriptions }) => pendingSubscriptions.has(channel.name));
|
|
175
|
+
users.forEach(({ clientId, pendingSubscriptions, subscriptions, socket, assigns }) => {
|
|
176
|
+
const unsubscribe = channel.addUser(clientId, assigns, (event) => {
|
|
177
|
+
socket.send(JSON.stringify(event));
|
|
178
|
+
});
|
|
179
|
+
subscriptions.set(channel.name, unsubscribe);
|
|
180
|
+
pendingSubscriptions.delete(channel.name);
|
|
181
|
+
});
|
|
182
|
+
}
|
|
183
|
+
}
|
|
184
|
+
exports.EndpointEngine = EndpointEngine;
|
|
185
|
+
_EndpointEngine_middleware = new WeakMap(), _EndpointEngine_channels = new WeakMap(), _EndpointEngine_sockets = new WeakMap(), _EndpointEngine_parentEngine = new WeakMap(), _EndpointEngine_instances = new WeakSet(), _EndpointEngine_sendMessage = function _EndpointEngine_sendMessage(socket, message) {
|
|
186
|
+
socket.send(JSON.stringify(message));
|
|
187
|
+
}, _EndpointEngine_joinChannel = function _EndpointEngine_joinChannel(channel, socket, joinParams, requestId) {
|
|
188
|
+
const cache = Object.assign(Object.assign({}, socket), { requestId, channelName: channel });
|
|
189
|
+
__classPrivateFieldGet(this, _EndpointEngine_middleware, "f").run(cache, joinParams, () => {
|
|
190
|
+
throw new pondError_1.EndpointError(`GatewayEngine: Channel ${channel} does not exist`, 404);
|
|
191
|
+
});
|
|
192
|
+
}, _EndpointEngine_execute = function _EndpointEngine_execute(channel, handler) {
|
|
193
|
+
for (const manager of __classPrivateFieldGet(this, _EndpointEngine_channels, "f")) {
|
|
194
|
+
const isPresent = manager.listChannels()
|
|
195
|
+
.includes(channel);
|
|
196
|
+
if (isPresent) {
|
|
197
|
+
return manager.execute(channel, handler);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
throw new Error(`GatewayEngine: Channel ${channel} does not exist`);
|
|
201
|
+
}, _EndpointEngine_retrieveChannel = function _EndpointEngine_retrieveChannel(channel) {
|
|
202
|
+
for (const manager of __classPrivateFieldGet(this, _EndpointEngine_channels, "f")) {
|
|
203
|
+
const isPresent = manager.listChannels()
|
|
204
|
+
.includes(channel);
|
|
205
|
+
if (isPresent) {
|
|
206
|
+
return manager.getChannel(channel);
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
}, _EndpointEngine_handleMessage = function _EndpointEngine_handleMessage(cache, message) {
|
|
210
|
+
switch (message.action) {
|
|
211
|
+
case pondsocket_common_1.ClientActions.JOIN_CHANNEL:
|
|
212
|
+
__classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_joinChannel).call(this, message.channelName, cache, message.payload, message.requestId);
|
|
213
|
+
break;
|
|
214
|
+
case pondsocket_common_1.ClientActions.LEAVE_CHANNEL:
|
|
215
|
+
__classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_leaveChannel).call(this, message.channelName, cache);
|
|
216
|
+
break;
|
|
217
|
+
case pondsocket_common_1.ClientActions.BROADCAST:
|
|
218
|
+
__classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_execute).call(this, message.channelName, (channel) => {
|
|
219
|
+
channel.broadcastMessage(cache.clientId, message);
|
|
220
|
+
});
|
|
221
|
+
break;
|
|
222
|
+
default:
|
|
223
|
+
throw new Error(`GatewayEngine: Action ${message.action} does not exist`);
|
|
224
|
+
}
|
|
225
|
+
}, _EndpointEngine_readMessage = function _EndpointEngine_readMessage(cache, message) {
|
|
226
|
+
try {
|
|
227
|
+
const data = JSON.parse(message);
|
|
228
|
+
const result = schema_1.clientMessageSchema.parse(data);
|
|
229
|
+
__classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_handleMessage).call(this, cache, result);
|
|
230
|
+
}
|
|
231
|
+
catch (e) {
|
|
232
|
+
__classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_sendMessage).call(this, cache.socket, __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_buildError).call(this, e));
|
|
233
|
+
}
|
|
234
|
+
}, _EndpointEngine_buildError = function _EndpointEngine_buildError(error) {
|
|
235
|
+
const event = {
|
|
236
|
+
event: pondsocket_common_1.ErrorTypes.INVALID_MESSAGE,
|
|
237
|
+
action: pondsocket_common_1.ServerActions.ERROR,
|
|
238
|
+
channelName: pondsocket_common_1.SystemSender.ENDPOINT,
|
|
239
|
+
requestId: (0, pondsocket_common_1.uuid)(),
|
|
240
|
+
payload: {},
|
|
241
|
+
};
|
|
242
|
+
if (error instanceof SyntaxError) {
|
|
243
|
+
event.payload = {
|
|
244
|
+
message: 'Invalid JSON',
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
else if (error instanceof Error) {
|
|
248
|
+
event.event = pondsocket_common_1.ErrorTypes.INTERNAL_SERVER_ERROR;
|
|
249
|
+
event.payload = {
|
|
250
|
+
message: error.message,
|
|
251
|
+
};
|
|
252
|
+
}
|
|
253
|
+
else if (error instanceof pondError_1.PresenceError) {
|
|
254
|
+
event.event = pondsocket_common_1.ErrorTypes.PRESENCE_ERROR;
|
|
255
|
+
event.channelName = error.channel;
|
|
256
|
+
event.payload = {
|
|
257
|
+
message: error.message,
|
|
258
|
+
code: error.code,
|
|
259
|
+
action: error.event,
|
|
260
|
+
};
|
|
261
|
+
}
|
|
262
|
+
else if (error instanceof pondError_1.ChannelError) {
|
|
263
|
+
event.event = pondsocket_common_1.ErrorTypes.CHANNEL_ERROR;
|
|
264
|
+
event.channelName = error.channel;
|
|
265
|
+
event.payload = {
|
|
266
|
+
message: error.message,
|
|
267
|
+
code: error.code,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
else if (error instanceof pondError_1.EndpointError) {
|
|
271
|
+
event.event = pondsocket_common_1.ErrorTypes.ENDPOINT_ERROR;
|
|
272
|
+
event.payload = {
|
|
273
|
+
message: error.message,
|
|
274
|
+
code: error.code,
|
|
275
|
+
};
|
|
276
|
+
}
|
|
277
|
+
else if (error instanceof zod_1.ZodError) {
|
|
278
|
+
const message = error.errors.map((error) => {
|
|
279
|
+
if ('expected' in error && 'received' in error) {
|
|
280
|
+
return `Expected ${error.path.join('.')} to be ${error.expected}, but received ${error.received}`;
|
|
281
|
+
}
|
|
282
|
+
return `${error.path.join('.')} ${error.message}`;
|
|
283
|
+
}).join(', ');
|
|
284
|
+
event.payload = {
|
|
285
|
+
message,
|
|
286
|
+
code: 400,
|
|
287
|
+
};
|
|
288
|
+
}
|
|
289
|
+
else {
|
|
290
|
+
event.payload = {
|
|
291
|
+
message: 'Unknown error',
|
|
292
|
+
};
|
|
293
|
+
}
|
|
294
|
+
return event;
|
|
295
|
+
}, _EndpointEngine_leaveChannel = function _EndpointEngine_leaveChannel(channel, socket) {
|
|
296
|
+
const unsubscribe = socket.subscriptions.get(channel);
|
|
297
|
+
if (!unsubscribe) {
|
|
298
|
+
throw new pondError_1.EndpointError(`GatewayEngine: Channel ${channel} does not exist`, 404);
|
|
299
|
+
}
|
|
300
|
+
unsubscribe();
|
|
301
|
+
};
|
|
302
|
+
class Endpoint {
|
|
303
|
+
constructor(engine) {
|
|
304
|
+
_Endpoint_engine.set(this, void 0);
|
|
305
|
+
__classPrivateFieldSet(this, _Endpoint_engine, engine, "f");
|
|
306
|
+
}
|
|
307
|
+
createChannel(path, handler) {
|
|
308
|
+
return __classPrivateFieldGet(this, _Endpoint_engine, "f").createChannel(path, handler);
|
|
309
|
+
}
|
|
310
|
+
broadcast(event, payload) {
|
|
311
|
+
__classPrivateFieldGet(this, _Endpoint_engine, "f").broadcast(event, payload);
|
|
312
|
+
}
|
|
313
|
+
closeConnection(clientIds) {
|
|
314
|
+
__classPrivateFieldGet(this, _Endpoint_engine, "f").closeConnection(clientIds);
|
|
315
|
+
}
|
|
316
|
+
getClients() {
|
|
317
|
+
return __classPrivateFieldGet(this, _Endpoint_engine, "f").getClients().map(({ socket }) => socket);
|
|
318
|
+
}
|
|
319
|
+
}
|
|
320
|
+
exports.Endpoint = Endpoint;
|
|
321
|
+
_Endpoint_engine = new WeakMap();
|