@eleven-am/pondsocket 0.1.197 → 0.1.199
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 +194 -39
- package/engines/endpointEngine.js +27 -15
- package/engines/lobbyEngine.js +103 -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,133 @@ 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();
|
|
263
|
+
// Remove the channel from the parent
|
|
264
|
+
this.parent.deleteChannel(this.name);
|
|
130
265
|
}
|
|
131
266
|
}
|
|
132
267
|
exports.ChannelEngine = ChannelEngine;
|
|
133
|
-
|
|
268
|
+
_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) {
|
|
269
|
+
const subscription = __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").subscribe((_a) => __awaiter(this, void 0, void 0, function* () {
|
|
270
|
+
var { recipients } = _a, event = __rest(_a, ["recipients"]);
|
|
271
|
+
if (recipients.includes(userId)) {
|
|
272
|
+
if (event.action === pondsocket_common_1.ServerActions.SYSTEM) {
|
|
273
|
+
return onMessage(event);
|
|
274
|
+
}
|
|
275
|
+
const newEvent = yield this.parent.manageOutgoingEvents(event, userId, this);
|
|
276
|
+
if (newEvent) {
|
|
277
|
+
onMessage(newEvent);
|
|
278
|
+
}
|
|
279
|
+
}
|
|
280
|
+
}));
|
|
281
|
+
__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").set(userId, subscription);
|
|
282
|
+
}, _ChannelEngine_getOrCreatePresenceEngine = function _ChannelEngine_getOrCreatePresenceEngine() {
|
|
283
|
+
if (!__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
|
|
284
|
+
__classPrivateFieldSet(this, _ChannelEngine_presenceEngine, new presenceEngine_1.PresenceEngine(this.name), "f");
|
|
285
|
+
// Subscribe to presence events and publish them through the channel publisher
|
|
286
|
+
__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").subscribe((event) => {
|
|
287
|
+
__classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(event);
|
|
288
|
+
});
|
|
289
|
+
}
|
|
290
|
+
return __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f");
|
|
291
|
+
}, _ChannelEngine_getUsersFromRecipients = function _ChannelEngine_getUsersFromRecipients(recipients, sender) {
|
|
134
292
|
const allUsers = Array.from(this.users);
|
|
135
293
|
let users;
|
|
136
294
|
switch (recipients) {
|
|
@@ -140,21 +298,18 @@ _ChannelEngine_manager = new WeakMap(), _ChannelEngine_instances = new WeakSet()
|
|
|
140
298
|
case pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER:
|
|
141
299
|
if (sender === pondsocket_common_1.SystemSender.CHANNEL) {
|
|
142
300
|
const message = `ChannelEngine: Invalid sender ${sender} for recipients ${recipients}`;
|
|
143
|
-
|
|
144
|
-
throw new httpError_1.HttpError(code, message);
|
|
301
|
+
throw new httpError_1.HttpError(400, message);
|
|
145
302
|
}
|
|
146
303
|
users = allUsers.filter((user) => user !== sender);
|
|
147
304
|
break;
|
|
148
305
|
default:
|
|
149
306
|
if (!Array.isArray(recipients)) {
|
|
150
307
|
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);
|
|
308
|
+
throw new httpError_1.HttpError(400, message);
|
|
153
309
|
}
|
|
154
310
|
if (recipients.some((user) => !allUsers.includes(user))) {
|
|
155
311
|
const message = `ChannelEngine: Invalid user ids in recipients ${recipients}`;
|
|
156
|
-
|
|
157
|
-
throw new httpError_1.HttpError(code, message);
|
|
312
|
+
throw new httpError_1.HttpError(400, message);
|
|
158
313
|
}
|
|
159
314
|
users = recipients;
|
|
160
315
|
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,36 @@ 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
|
+
}
|
|
141
|
+
/**
|
|
142
|
+
* Broadcasts an event to all users in a channel
|
|
143
|
+
*/
|
|
144
|
+
deleteChannel(channelName) {
|
|
145
|
+
const channel = __classPrivateFieldGet(this, _LobbyEngine_channels, "f").get(channelName);
|
|
146
|
+
if (channel) {
|
|
147
|
+
__classPrivateFieldGet(this, _LobbyEngine_channels, "f").delete(channelName);
|
|
148
|
+
}
|
|
149
|
+
}
|
|
58
150
|
}
|
|
59
151
|
exports.LobbyEngine = LobbyEngine;
|
|
60
|
-
_LobbyEngine_channels = new WeakMap(), _LobbyEngine_instances = new WeakSet(), _LobbyEngine_getChannel = function _LobbyEngine_getChannel(channelName) {
|
|
152
|
+
_LobbyEngine_channels = new WeakMap(), _LobbyEngine_outGoingEvents = new WeakMap(), _LobbyEngine_instances = new WeakSet(), _LobbyEngine_getChannel = function _LobbyEngine_getChannel(channelName) {
|
|
61
153
|
return __classPrivateFieldGet(this, _LobbyEngine_channels, "f").get(channelName) || null;
|
|
62
154
|
}, _LobbyEngine_createChannel = function _LobbyEngine_createChannel(channelName) {
|
|
63
|
-
|
|
64
|
-
const
|
|
65
|
-
|
|
155
|
+
// Create the channel engine
|
|
156
|
+
const channel = new channelEngine_1.ChannelEngine(this, channelName);
|
|
157
|
+
// Add it to the channels map
|
|
66
158
|
__classPrivateFieldGet(this, _LobbyEngine_channels, "f").set(channelName, channel);
|
|
159
|
+
// Set up a way to remove the channel when it's closed
|
|
160
|
+
// (We'll handle this in the ChannelEngine's close method)
|
|
161
|
+
const handleChannelClosed = () => {
|
|
162
|
+
__classPrivateFieldGet(this, _LobbyEngine_channels, "f").delete(channelName);
|
|
163
|
+
};
|
|
164
|
+
// Return the new channel engine
|
|
67
165
|
return channel;
|
|
68
166
|
};
|