@eleven-am/pondsocket 0.1.197 → 0.1.198
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/engines/channelEngine.js +192 -39
- package/engines/endpointEngine.js +27 -15
- package/engines/lobbyEngine.js +94 -5
- package/engines/presenceEngine.js +140 -0
- package/index.js +0 -3
- package/package.json +3 -3
- package/requests/eventRequest.js +19 -1
- package/responses/eventResponse.js +30 -0
- package/responses/joinResponse.js +29 -3
- package/server/server.js +21 -32
- package/types.d.ts +32 -24
- package/wrappers/channel.js +37 -1
- package/wrappers/pondChannel.js +4 -0
- package/abstracts/redisClient.js +0 -499
- package/errors/redisError.js +0 -10
- package/managers/distributedManager.js +0 -165
- package/managers/localManager.js +0 -41
- package/managers/manager.js +0 -172
- package/managers/managerFactory.js +0 -20
package/engines/channelEngine.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
12
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
13
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
@@ -10,38 +19,68 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
19
|
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
20
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
21
|
};
|
|
13
|
-
var
|
|
22
|
+
var __rest = (this && this.__rest) || function (s, e) {
|
|
23
|
+
var t = {};
|
|
24
|
+
for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
|
|
25
|
+
t[p] = s[p];
|
|
26
|
+
if (s != null && typeof Object.getOwnPropertySymbols === "function")
|
|
27
|
+
for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
|
|
28
|
+
if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
|
|
29
|
+
t[p[i]] = s[p[i]];
|
|
30
|
+
}
|
|
31
|
+
return t;
|
|
32
|
+
};
|
|
33
|
+
var _ChannelEngine_instances, _ChannelEngine_presenceEngine, _ChannelEngine_assignsCache, _ChannelEngine_userSubscriptions, _ChannelEngine_publisher, _ChannelEngine_name, _ChannelEngine_buildSubscriber, _ChannelEngine_getOrCreatePresenceEngine, _ChannelEngine_getUsersFromRecipients;
|
|
14
34
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
35
|
exports.ChannelEngine = void 0;
|
|
16
36
|
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
37
|
+
const presenceEngine_1 = require("./presenceEngine");
|
|
17
38
|
const httpError_1 = require("../errors/httpError");
|
|
18
|
-
const channel_1 = require("../wrappers/channel");
|
|
19
39
|
class ChannelEngine {
|
|
20
|
-
constructor(parent, name
|
|
40
|
+
constructor(parent, name) {
|
|
21
41
|
_ChannelEngine_instances.add(this);
|
|
22
42
|
this.parent = parent;
|
|
23
|
-
this
|
|
24
|
-
|
|
25
|
-
|
|
43
|
+
_ChannelEngine_presenceEngine.set(this, null);
|
|
44
|
+
_ChannelEngine_assignsCache.set(this, new Map());
|
|
45
|
+
_ChannelEngine_userSubscriptions.set(this, new Map());
|
|
46
|
+
_ChannelEngine_publisher.set(this, new pondsocket_common_1.Subject());
|
|
47
|
+
_ChannelEngine_name.set(this, void 0);
|
|
48
|
+
__classPrivateFieldSet(this, _ChannelEngine_name, name, "f");
|
|
49
|
+
}
|
|
50
|
+
get name() {
|
|
51
|
+
return __classPrivateFieldGet(this, _ChannelEngine_name, "f");
|
|
26
52
|
}
|
|
27
53
|
get users() {
|
|
28
|
-
return __classPrivateFieldGet(this,
|
|
54
|
+
return new Set(__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").keys());
|
|
29
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Adds a user to the channel
|
|
58
|
+
* @param userId - The user ID
|
|
59
|
+
* @param assigns - The user's assigns
|
|
60
|
+
* @param onMessage - Callback for messages
|
|
61
|
+
* @returns Unsubscribe function
|
|
62
|
+
*/
|
|
30
63
|
addUser(userId, assigns, onMessage) {
|
|
31
64
|
if (this.users.has(userId)) {
|
|
32
65
|
const message = `ChannelEngine: User with id ${userId} already exists in channel ${this.name}`;
|
|
33
|
-
|
|
34
|
-
throw new httpError_1.HttpError(code, message);
|
|
66
|
+
throw new httpError_1.HttpError(400, message);
|
|
35
67
|
}
|
|
36
|
-
|
|
68
|
+
// Store user assigns
|
|
69
|
+
__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(userId, assigns);
|
|
70
|
+
// Subscribe user to messages
|
|
71
|
+
__classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_buildSubscriber).call(this, userId, onMessage);
|
|
72
|
+
// Send acknowledgment
|
|
37
73
|
this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, [userId], pondsocket_common_1.ServerActions.SYSTEM, pondsocket_common_1.Events.ACKNOWLEDGE, {});
|
|
38
|
-
|
|
74
|
+
// Return unsubscribe function
|
|
75
|
+
return () => this.removeUser(userId);
|
|
39
76
|
}
|
|
77
|
+
/**
|
|
78
|
+
* Sends a message to recipients
|
|
79
|
+
*/
|
|
40
80
|
sendMessage(sender, recipient, action, event, payload, requestId = (0, pondsocket_common_1.uuid)()) {
|
|
41
81
|
if (!this.users.has(sender) && sender !== pondsocket_common_1.SystemSender.CHANNEL) {
|
|
42
82
|
const message = `ChannelEngine: User with id ${sender} does not exist in channel ${this.name}`;
|
|
43
|
-
|
|
44
|
-
throw new httpError_1.HttpError(code, message);
|
|
83
|
+
throw new httpError_1.HttpError(404, message);
|
|
45
84
|
}
|
|
46
85
|
const channelEvent = {
|
|
47
86
|
channelName: this.name,
|
|
@@ -52,13 +91,15 @@ class ChannelEngine {
|
|
|
52
91
|
};
|
|
53
92
|
const recipients = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getUsersFromRecipients).call(this, recipient, sender);
|
|
54
93
|
const internalEvent = Object.assign(Object.assign({}, channelEvent), { recipients });
|
|
55
|
-
__classPrivateFieldGet(this,
|
|
94
|
+
__classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(internalEvent);
|
|
56
95
|
}
|
|
96
|
+
/**
|
|
97
|
+
* Broadcasts a message from a user
|
|
98
|
+
*/
|
|
57
99
|
broadcastMessage(userId, message) {
|
|
58
100
|
if (!this.users.has(userId)) {
|
|
59
101
|
const message = `ChannelEngine: User with id ${userId} does not exist in channel ${this.name}`;
|
|
60
|
-
|
|
61
|
-
throw new httpError_1.HttpError(code, message);
|
|
102
|
+
throw new httpError_1.HttpError(404, message);
|
|
62
103
|
}
|
|
63
104
|
const responseEvent = Object.assign(Object.assign({}, message), { sender: userId, action: pondsocket_common_1.ServerActions.BROADCAST });
|
|
64
105
|
this.parent.middleware.run(responseEvent, this, (error) => {
|
|
@@ -68,21 +109,48 @@ class ChannelEngine {
|
|
|
68
109
|
}, message.requestId);
|
|
69
110
|
});
|
|
70
111
|
}
|
|
112
|
+
/**
|
|
113
|
+
* Tracks a user's presence
|
|
114
|
+
*/
|
|
71
115
|
trackPresence(userId, presence) {
|
|
72
|
-
|
|
116
|
+
const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
|
|
117
|
+
presenceEngine.trackPresence(userId, presence);
|
|
73
118
|
}
|
|
119
|
+
/**
|
|
120
|
+
* Updates a user's presence
|
|
121
|
+
*/
|
|
74
122
|
updatePresence(userId, presence) {
|
|
75
|
-
|
|
123
|
+
const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
|
|
124
|
+
presenceEngine.updatePresence(userId, presence);
|
|
76
125
|
}
|
|
126
|
+
/**
|
|
127
|
+
* Removes a user's presence
|
|
128
|
+
*/
|
|
77
129
|
removePresence(userId) {
|
|
78
|
-
|
|
130
|
+
if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
|
|
131
|
+
__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(userId);
|
|
132
|
+
}
|
|
79
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Adds or updates a user's presence
|
|
136
|
+
*/
|
|
80
137
|
upsertPresence(userId, presence) {
|
|
81
|
-
|
|
138
|
+
const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
|
|
139
|
+
presenceEngine.upsertPresence(userId, presence);
|
|
82
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Updates a user's assigns
|
|
143
|
+
*/
|
|
83
144
|
updateAssigns(userId, assigns) {
|
|
84
|
-
|
|
145
|
+
if (!__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").has(userId)) {
|
|
146
|
+
throw new httpError_1.HttpError(404, `User with id ${userId} does not exist in channel ${this.name}`);
|
|
147
|
+
}
|
|
148
|
+
const currentAssigns = __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").get(userId) || {};
|
|
149
|
+
__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(userId, Object.assign(Object.assign({}, currentAssigns), assigns));
|
|
85
150
|
}
|
|
151
|
+
/**
|
|
152
|
+
* Kicks a user from the channel
|
|
153
|
+
*/
|
|
86
154
|
kickUser(userId, reason) {
|
|
87
155
|
this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, [userId], pondsocket_common_1.ServerActions.SYSTEM, 'kicked_out', {
|
|
88
156
|
message: reason,
|
|
@@ -94,43 +162,131 @@ class ChannelEngine {
|
|
|
94
162
|
reason,
|
|
95
163
|
});
|
|
96
164
|
}
|
|
165
|
+
/**
|
|
166
|
+
* Gets all assigns as an object
|
|
167
|
+
*/
|
|
97
168
|
getAssigns() {
|
|
98
169
|
return Array
|
|
99
|
-
.from(__classPrivateFieldGet(this,
|
|
170
|
+
.from(__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").entries())
|
|
100
171
|
.reduce((acc, [id, assigns]) => {
|
|
101
172
|
acc[id] = assigns;
|
|
102
173
|
return acc;
|
|
103
174
|
}, {});
|
|
104
175
|
}
|
|
176
|
+
/**
|
|
177
|
+
* Gets all presence data as an object
|
|
178
|
+
*/
|
|
105
179
|
getPresence() {
|
|
180
|
+
if (!__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
|
|
181
|
+
return {};
|
|
182
|
+
}
|
|
106
183
|
return Array
|
|
107
|
-
.from(__classPrivateFieldGet(this,
|
|
184
|
+
.from(__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").getAllPresence().entries())
|
|
108
185
|
.reduce((acc, [id, presence]) => {
|
|
109
186
|
acc[id] = presence;
|
|
110
187
|
return acc;
|
|
111
188
|
}, {});
|
|
112
189
|
}
|
|
190
|
+
/**
|
|
191
|
+
* Destroys the channel
|
|
192
|
+
*/
|
|
113
193
|
destroy(reason) {
|
|
114
194
|
this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.ERROR, 'destroyed', {
|
|
115
195
|
message: reason !== null && reason !== void 0 ? reason : 'Channel has been destroyed',
|
|
116
196
|
});
|
|
117
|
-
|
|
197
|
+
this.close();
|
|
118
198
|
}
|
|
199
|
+
/**
|
|
200
|
+
* Removes a user from the channel
|
|
201
|
+
*/
|
|
119
202
|
removeUser(userId) {
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
this.
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
203
|
+
try {
|
|
204
|
+
const userData = this.getUserData(userId);
|
|
205
|
+
const unsubscribe = __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").get(userId);
|
|
206
|
+
__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(userId);
|
|
207
|
+
if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
|
|
208
|
+
__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(userId);
|
|
209
|
+
}
|
|
210
|
+
if (unsubscribe) {
|
|
211
|
+
unsubscribe();
|
|
212
|
+
__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").delete(userId);
|
|
213
|
+
}
|
|
214
|
+
// Trigger leave callback if defined
|
|
215
|
+
if (this.parent.leaveCallback) {
|
|
216
|
+
this.parent.leaveCallback({
|
|
217
|
+
user: userData,
|
|
218
|
+
channel: this.parent.wrapChannel(this),
|
|
219
|
+
});
|
|
220
|
+
}
|
|
221
|
+
// If no users left, close the channel
|
|
222
|
+
if (this.users.size === 0) {
|
|
223
|
+
this.close();
|
|
224
|
+
}
|
|
225
|
+
}
|
|
226
|
+
catch (error) {
|
|
227
|
+
// Ignore cleanup errors
|
|
126
228
|
}
|
|
127
229
|
}
|
|
128
|
-
|
|
129
|
-
|
|
230
|
+
/**
|
|
231
|
+
* Gets user data by ID
|
|
232
|
+
*/
|
|
233
|
+
getUserData(userId) {
|
|
234
|
+
var _a;
|
|
235
|
+
const assigns = __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").get(userId);
|
|
236
|
+
const presence = ((_a = __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) === null || _a === void 0 ? void 0 : _a.getPresence(userId)) || null;
|
|
237
|
+
if (!assigns && !presence) {
|
|
238
|
+
const message = `User with id ${userId} does not exist in the channel ${this.name}`;
|
|
239
|
+
throw new httpError_1.HttpError(404, message);
|
|
240
|
+
}
|
|
241
|
+
return {
|
|
242
|
+
assigns: assigns || {},
|
|
243
|
+
presence: presence || {},
|
|
244
|
+
id: userId,
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* Closes the channel and cleans up resources
|
|
249
|
+
*/
|
|
250
|
+
close() {
|
|
251
|
+
// Clear all user subscriptions
|
|
252
|
+
__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").forEach((unsubscribe) => unsubscribe());
|
|
253
|
+
__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").clear();
|
|
254
|
+
// Clear assigns
|
|
255
|
+
__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").clear();
|
|
256
|
+
// Close presence engine if exists
|
|
257
|
+
if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
|
|
258
|
+
__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").close();
|
|
259
|
+
__classPrivateFieldSet(this, _ChannelEngine_presenceEngine, null, "f");
|
|
260
|
+
}
|
|
261
|
+
// Close publisher
|
|
262
|
+
__classPrivateFieldGet(this, _ChannelEngine_publisher, "f").close();
|
|
130
263
|
}
|
|
131
264
|
}
|
|
132
265
|
exports.ChannelEngine = ChannelEngine;
|
|
133
|
-
|
|
266
|
+
_ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new WeakMap(), _ChannelEngine_userSubscriptions = new WeakMap(), _ChannelEngine_publisher = new WeakMap(), _ChannelEngine_name = new WeakMap(), _ChannelEngine_instances = new WeakSet(), _ChannelEngine_buildSubscriber = function _ChannelEngine_buildSubscriber(userId, onMessage) {
|
|
267
|
+
const subscription = __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").subscribe((_a) => __awaiter(this, void 0, void 0, function* () {
|
|
268
|
+
var { recipients } = _a, event = __rest(_a, ["recipients"]);
|
|
269
|
+
if (recipients.includes(userId)) {
|
|
270
|
+
if (event.action === pondsocket_common_1.ServerActions.SYSTEM) {
|
|
271
|
+
return onMessage(event);
|
|
272
|
+
}
|
|
273
|
+
const newEvent = yield this.parent.manageOutgoingEvents(event, userId, this);
|
|
274
|
+
if (newEvent) {
|
|
275
|
+
onMessage(newEvent);
|
|
276
|
+
}
|
|
277
|
+
}
|
|
278
|
+
}));
|
|
279
|
+
__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").set(userId, subscription);
|
|
280
|
+
}, _ChannelEngine_getOrCreatePresenceEngine = function _ChannelEngine_getOrCreatePresenceEngine() {
|
|
281
|
+
if (!__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
|
|
282
|
+
__classPrivateFieldSet(this, _ChannelEngine_presenceEngine, new presenceEngine_1.PresenceEngine(this.name), "f");
|
|
283
|
+
// Subscribe to presence events and publish them through the channel publisher
|
|
284
|
+
__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").subscribe((event) => {
|
|
285
|
+
__classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(event);
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
return __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f");
|
|
289
|
+
}, _ChannelEngine_getUsersFromRecipients = function _ChannelEngine_getUsersFromRecipients(recipients, sender) {
|
|
134
290
|
const allUsers = Array.from(this.users);
|
|
135
291
|
let users;
|
|
136
292
|
switch (recipients) {
|
|
@@ -140,21 +296,18 @@ _ChannelEngine_manager = new WeakMap(), _ChannelEngine_instances = new WeakSet()
|
|
|
140
296
|
case pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER:
|
|
141
297
|
if (sender === pondsocket_common_1.SystemSender.CHANNEL) {
|
|
142
298
|
const message = `ChannelEngine: Invalid sender ${sender} for recipients ${recipients}`;
|
|
143
|
-
|
|
144
|
-
throw new httpError_1.HttpError(code, message);
|
|
299
|
+
throw new httpError_1.HttpError(400, message);
|
|
145
300
|
}
|
|
146
301
|
users = allUsers.filter((user) => user !== sender);
|
|
147
302
|
break;
|
|
148
303
|
default:
|
|
149
304
|
if (!Array.isArray(recipients)) {
|
|
150
305
|
const message = `ChannelEngine: Invalid recipients ${recipients} must be an array of user ids or ${pondsocket_common_1.ChannelReceiver.ALL_USERS}`;
|
|
151
|
-
|
|
152
|
-
throw new httpError_1.HttpError(code, message);
|
|
306
|
+
throw new httpError_1.HttpError(400, message);
|
|
153
307
|
}
|
|
154
308
|
if (recipients.some((user) => !allUsers.includes(user))) {
|
|
155
309
|
const message = `ChannelEngine: Invalid user ids in recipients ${recipients}`;
|
|
156
|
-
|
|
157
|
-
throw new httpError_1.HttpError(code, message);
|
|
310
|
+
throw new httpError_1.HttpError(400, message);
|
|
158
311
|
}
|
|
159
312
|
users = recipients;
|
|
160
313
|
break;
|
|
@@ -10,32 +10,32 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
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
11
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
12
|
};
|
|
13
|
-
var _EndpointEngine_instances,
|
|
13
|
+
var _EndpointEngine_instances, _EndpointEngine_sockets, _EndpointEngine_middleware, _EndpointEngine_lobbyEngines, _EndpointEngine_handleSocketClose, _EndpointEngine_handleMessage, _EndpointEngine_joinChannel, _EndpointEngine_leaveChannel, _EndpointEngine_retrieveChannel, _EndpointEngine_buildError, _EndpointEngine_broadcastMessage, _EndpointEngine_readMessage;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.EndpointEngine = void 0;
|
|
16
16
|
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
17
17
|
const lobbyEngine_1 = require("./lobbyEngine");
|
|
18
18
|
const middleware_1 = require("../abstracts/middleware");
|
|
19
19
|
const httpError_1 = require("../errors/httpError");
|
|
20
|
-
const managerFactory_1 = require("../managers/managerFactory");
|
|
21
20
|
const matcher_1 = require("../matcher/matcher");
|
|
22
21
|
const joinRequest_1 = require("../requests/joinRequest");
|
|
23
22
|
const joinResponse_1 = require("../responses/joinResponse");
|
|
24
23
|
const pondChannel_1 = require("../wrappers/pondChannel");
|
|
25
24
|
class EndpointEngine {
|
|
26
|
-
constructor(
|
|
25
|
+
constructor() {
|
|
27
26
|
_EndpointEngine_instances.add(this);
|
|
28
|
-
_EndpointEngine_clientFactory.set(this, void 0);
|
|
29
27
|
_EndpointEngine_sockets.set(this, void 0);
|
|
30
28
|
_EndpointEngine_middleware.set(this, void 0);
|
|
31
29
|
_EndpointEngine_lobbyEngines.set(this, void 0);
|
|
32
30
|
__classPrivateFieldSet(this, _EndpointEngine_sockets, new Map(), "f");
|
|
33
31
|
__classPrivateFieldSet(this, _EndpointEngine_lobbyEngines, new Map(), "f");
|
|
34
32
|
__classPrivateFieldSet(this, _EndpointEngine_middleware, new middleware_1.Middleware(), "f");
|
|
35
|
-
__classPrivateFieldSet(this, _EndpointEngine_clientFactory, clientFactory, "f");
|
|
36
33
|
}
|
|
34
|
+
/**
|
|
35
|
+
* Creates a new channel on a specified path
|
|
36
|
+
*/
|
|
37
37
|
createChannel(path, handler) {
|
|
38
|
-
const
|
|
38
|
+
const lobbyEngine = new lobbyEngine_1.LobbyEngine(this);
|
|
39
39
|
__classPrivateFieldGet(this, _EndpointEngine_middleware, "f").use((user, joinParams, next) => {
|
|
40
40
|
const event = (0, matcher_1.parseAddress)(path, user.channelName);
|
|
41
41
|
if (event) {
|
|
@@ -45,19 +45,25 @@ class EndpointEngine {
|
|
|
45
45
|
params: event,
|
|
46
46
|
joinParams,
|
|
47
47
|
};
|
|
48
|
-
const channel =
|
|
48
|
+
const channel = lobbyEngine.getOrCreateChannel(user.channelName);
|
|
49
49
|
const request = new joinRequest_1.JoinRequest(options, channel);
|
|
50
50
|
const response = new joinResponse_1.JoinResponse(user, channel);
|
|
51
51
|
return handler(request, response, next);
|
|
52
52
|
}
|
|
53
53
|
next();
|
|
54
54
|
});
|
|
55
|
-
__classPrivateFieldGet(this, _EndpointEngine_lobbyEngines, "f").set(path,
|
|
56
|
-
return new pondChannel_1.PondChannel(
|
|
55
|
+
__classPrivateFieldGet(this, _EndpointEngine_lobbyEngines, "f").set(path, lobbyEngine);
|
|
56
|
+
return new pondChannel_1.PondChannel(lobbyEngine);
|
|
57
57
|
}
|
|
58
|
+
/**
|
|
59
|
+
* Gets all connected clients
|
|
60
|
+
*/
|
|
58
61
|
getClients() {
|
|
59
62
|
return [...__classPrivateFieldGet(this, _EndpointEngine_sockets, "f").values()];
|
|
60
63
|
}
|
|
64
|
+
/**
|
|
65
|
+
* Gets a specific user by client ID
|
|
66
|
+
*/
|
|
61
67
|
getUser(clientId) {
|
|
62
68
|
const user = __classPrivateFieldGet(this, _EndpointEngine_sockets, "f").get(clientId);
|
|
63
69
|
if (!user) {
|
|
@@ -65,6 +71,9 @@ class EndpointEngine {
|
|
|
65
71
|
}
|
|
66
72
|
return user;
|
|
67
73
|
}
|
|
74
|
+
/**
|
|
75
|
+
* Manages a new WebSocket connection
|
|
76
|
+
*/
|
|
68
77
|
manageSocket(cache) {
|
|
69
78
|
__classPrivateFieldGet(this, _EndpointEngine_sockets, "f").set(cache.clientId, cache);
|
|
70
79
|
cache.socket.on('message', (message) => __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_readMessage).call(this, cache, message));
|
|
@@ -79,12 +88,15 @@ class EndpointEngine {
|
|
|
79
88
|
};
|
|
80
89
|
this.sendMessage(cache.socket, event);
|
|
81
90
|
}
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
91
|
+
/**
|
|
92
|
+
* Sends a message to a WebSocket
|
|
93
|
+
*/
|
|
85
94
|
sendMessage(socket, message) {
|
|
86
95
|
socket.send(JSON.stringify(message));
|
|
87
96
|
}
|
|
97
|
+
/**
|
|
98
|
+
* Closes one or more client connections
|
|
99
|
+
*/
|
|
88
100
|
closeConnection(clientIds) {
|
|
89
101
|
const clients = typeof clientIds === 'string' ? [clientIds] : clientIds;
|
|
90
102
|
this.getClients()
|
|
@@ -96,7 +108,7 @@ class EndpointEngine {
|
|
|
96
108
|
}
|
|
97
109
|
}
|
|
98
110
|
exports.EndpointEngine = EndpointEngine;
|
|
99
|
-
|
|
111
|
+
_EndpointEngine_sockets = new WeakMap(), _EndpointEngine_middleware = new WeakMap(), _EndpointEngine_lobbyEngines = new WeakMap(), _EndpointEngine_instances = new WeakSet(), _EndpointEngine_handleSocketClose = function _EndpointEngine_handleSocketClose(cache) {
|
|
100
112
|
try {
|
|
101
113
|
__classPrivateFieldGet(this, _EndpointEngine_sockets, "f").delete(cache.clientId);
|
|
102
114
|
cache.subscriptions.forEach((unsubscribe) => unsubscribe());
|
|
@@ -128,10 +140,10 @@ _EndpointEngine_clientFactory = new WeakMap(), _EndpointEngine_sockets = new Wea
|
|
|
128
140
|
const engine = __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_retrieveChannel).call(this, channel);
|
|
129
141
|
engine.removeUser(socket.clientId);
|
|
130
142
|
}, _EndpointEngine_retrieveChannel = function _EndpointEngine_retrieveChannel(channel) {
|
|
131
|
-
for (const [path,
|
|
143
|
+
for (const [path, lobby] of __classPrivateFieldGet(this, _EndpointEngine_lobbyEngines, "f")) {
|
|
132
144
|
const event = (0, matcher_1.parseAddress)(path, channel);
|
|
133
145
|
if (event) {
|
|
134
|
-
return
|
|
146
|
+
return lobby.getChannel(channel);
|
|
135
147
|
}
|
|
136
148
|
}
|
|
137
149
|
throw new httpError_1.HttpError(404, `GatewayEngine: Channel ${channel} does not exist`);
|
package/engines/lobbyEngine.js
CHANGED
|
@@ -1,4 +1,13 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
|
|
3
|
+
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
|
|
4
|
+
return new (P || (P = Promise))(function (resolve, reject) {
|
|
5
|
+
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
|
|
6
|
+
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
|
|
7
|
+
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
|
|
8
|
+
step((generator = generator.apply(thisArg, _arguments || [])).next());
|
|
9
|
+
});
|
|
10
|
+
};
|
|
2
11
|
var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
|
|
3
12
|
if (kind === "m") throw new TypeError("Private method is not writable");
|
|
4
13
|
if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
|
|
@@ -10,7 +19,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
10
19
|
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
20
|
return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
|
|
12
21
|
};
|
|
13
|
-
var _LobbyEngine_instances, _LobbyEngine_channels, _LobbyEngine_getChannel, _LobbyEngine_createChannel;
|
|
22
|
+
var _LobbyEngine_instances, _LobbyEngine_channels, _LobbyEngine_outGoingEvents, _LobbyEngine_getChannel, _LobbyEngine_createChannel;
|
|
14
23
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
24
|
exports.LobbyEngine = void 0;
|
|
16
25
|
const channelEngine_1 = require("./channelEngine");
|
|
@@ -19,17 +28,26 @@ const httpError_1 = require("../errors/httpError");
|
|
|
19
28
|
const matcher_1 = require("../matcher/matcher");
|
|
20
29
|
const eventRequest_1 = require("../requests/eventRequest");
|
|
21
30
|
const eventResponse_1 = require("../responses/eventResponse");
|
|
31
|
+
const channel_1 = require("../wrappers/channel");
|
|
22
32
|
class LobbyEngine {
|
|
23
33
|
constructor(parent) {
|
|
24
34
|
_LobbyEngine_instances.add(this);
|
|
25
35
|
this.parent = parent;
|
|
26
36
|
_LobbyEngine_channels.set(this, void 0);
|
|
37
|
+
_LobbyEngine_outGoingEvents.set(this, void 0);
|
|
27
38
|
this.middleware = new middleware_1.Middleware();
|
|
28
39
|
__classPrivateFieldSet(this, _LobbyEngine_channels, new Map(), "f");
|
|
40
|
+
__classPrivateFieldSet(this, _LobbyEngine_outGoingEvents, [], "f");
|
|
29
41
|
}
|
|
42
|
+
/**
|
|
43
|
+
* Sets the callback for when users leave channels
|
|
44
|
+
*/
|
|
30
45
|
onLeave(callback) {
|
|
31
46
|
this.leaveCallback = callback;
|
|
32
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Attaches a handler for a specific event pattern
|
|
50
|
+
*/
|
|
33
51
|
onEvent(event, handler) {
|
|
34
52
|
this.middleware.use((requestEvent, channel, next) => {
|
|
35
53
|
const params = (0, matcher_1.parseAddress)(event, requestEvent.event);
|
|
@@ -41,6 +59,60 @@ class LobbyEngine {
|
|
|
41
59
|
return next();
|
|
42
60
|
});
|
|
43
61
|
}
|
|
62
|
+
/**
|
|
63
|
+
* Attaches a handler for outgoing events
|
|
64
|
+
*/
|
|
65
|
+
handleOutgoingEvent(event, handler) {
|
|
66
|
+
const handlerWrapper = (request, channel, userId) => __awaiter(this, void 0, void 0, function* () {
|
|
67
|
+
try {
|
|
68
|
+
const params = (0, matcher_1.parseAddress)(event, request.event);
|
|
69
|
+
if (!params) {
|
|
70
|
+
return true;
|
|
71
|
+
}
|
|
72
|
+
const userData = channel.getUserData(userId);
|
|
73
|
+
const newEvent = {
|
|
74
|
+
channel,
|
|
75
|
+
userData,
|
|
76
|
+
event: params,
|
|
77
|
+
payload: request.payload,
|
|
78
|
+
};
|
|
79
|
+
const response = yield handler(newEvent);
|
|
80
|
+
if (response) {
|
|
81
|
+
return response;
|
|
82
|
+
}
|
|
83
|
+
return false;
|
|
84
|
+
}
|
|
85
|
+
catch (error) {
|
|
86
|
+
return false;
|
|
87
|
+
}
|
|
88
|
+
});
|
|
89
|
+
__classPrivateFieldGet(this, _LobbyEngine_outGoingEvents, "f").push(handlerWrapper);
|
|
90
|
+
}
|
|
91
|
+
/**
|
|
92
|
+
* Internal method to handle outgoing events
|
|
93
|
+
* @param request - The request object
|
|
94
|
+
* @param userId - The user ID
|
|
95
|
+
* @param channel - The channel engine
|
|
96
|
+
*/
|
|
97
|
+
manageOutgoingEvents(request, userId, channel) {
|
|
98
|
+
return __awaiter(this, void 0, void 0, function* () {
|
|
99
|
+
if (!__classPrivateFieldGet(this, _LobbyEngine_outGoingEvents, "f").length) {
|
|
100
|
+
return request;
|
|
101
|
+
}
|
|
102
|
+
const promises = __classPrivateFieldGet(this, _LobbyEngine_outGoingEvents, "f").map((handler) => handler(request, this.wrapChannel(channel), userId));
|
|
103
|
+
const results = yield Promise.all(promises);
|
|
104
|
+
if (results.some((result) => result === false)) {
|
|
105
|
+
return null;
|
|
106
|
+
}
|
|
107
|
+
request.payload = results
|
|
108
|
+
.filter((result) => typeof result !== 'boolean')
|
|
109
|
+
.reduce((acc, result) => Object.assign(acc, result), {});
|
|
110
|
+
return request;
|
|
111
|
+
});
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* Gets or creates a channel by name
|
|
115
|
+
*/
|
|
44
116
|
getOrCreateChannel(channelName) {
|
|
45
117
|
const oldChannel = __classPrivateFieldGet(this, _LobbyEngine_instances, "m", _LobbyEngine_getChannel).call(this, channelName);
|
|
46
118
|
if (oldChannel) {
|
|
@@ -48,6 +120,10 @@ class LobbyEngine {
|
|
|
48
120
|
}
|
|
49
121
|
return __classPrivateFieldGet(this, _LobbyEngine_instances, "m", _LobbyEngine_createChannel).call(this, channelName);
|
|
50
122
|
}
|
|
123
|
+
/**
|
|
124
|
+
* Gets a channel by name
|
|
125
|
+
* @throws HttpError if channel not found
|
|
126
|
+
*/
|
|
51
127
|
getChannel(channelName) {
|
|
52
128
|
const channel = __classPrivateFieldGet(this, _LobbyEngine_channels, "f").get(channelName);
|
|
53
129
|
if (!channel) {
|
|
@@ -55,14 +131,27 @@ class LobbyEngine {
|
|
|
55
131
|
}
|
|
56
132
|
return channel;
|
|
57
133
|
}
|
|
134
|
+
/**
|
|
135
|
+
* Creates a Channel wrapper from a ChannelEngine
|
|
136
|
+
* Used by the channel engine for the leave callback
|
|
137
|
+
*/
|
|
138
|
+
wrapChannel(engine) {
|
|
139
|
+
return new channel_1.Channel(engine);
|
|
140
|
+
}
|
|
58
141
|
}
|
|
59
142
|
exports.LobbyEngine = LobbyEngine;
|
|
60
|
-
_LobbyEngine_channels = new WeakMap(), _LobbyEngine_instances = new WeakSet(), _LobbyEngine_getChannel = function _LobbyEngine_getChannel(channelName) {
|
|
143
|
+
_LobbyEngine_channels = new WeakMap(), _LobbyEngine_outGoingEvents = new WeakMap(), _LobbyEngine_instances = new WeakSet(), _LobbyEngine_getChannel = function _LobbyEngine_getChannel(channelName) {
|
|
61
144
|
return __classPrivateFieldGet(this, _LobbyEngine_channels, "f").get(channelName) || null;
|
|
62
145
|
}, _LobbyEngine_createChannel = function _LobbyEngine_createChannel(channelName) {
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
146
|
+
// Create the channel engine
|
|
147
|
+
const channel = new channelEngine_1.ChannelEngine(this, channelName);
|
|
148
|
+
// Add it to the channels map
|
|
66
149
|
__classPrivateFieldGet(this, _LobbyEngine_channels, "f").set(channelName, channel);
|
|
150
|
+
// Set up a way to remove the channel when it's closed
|
|
151
|
+
// (We'll handle this in the ChannelEngine's close method)
|
|
152
|
+
const handleChannelClosed = () => {
|
|
153
|
+
__classPrivateFieldGet(this, _LobbyEngine_channels, "f").delete(channelName);
|
|
154
|
+
};
|
|
155
|
+
// Return the new channel engine
|
|
67
156
|
return channel;
|
|
68
157
|
};
|