@eleven-am/pondsocket 0.1.200 → 0.1.201

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.
@@ -30,22 +30,31 @@ var __rest = (this && this.__rest) || function (s, e) {
30
30
  }
31
31
  return t;
32
32
  };
33
- var _ChannelEngine_instances, _ChannelEngine_presenceEngine, _ChannelEngine_assignsCache, _ChannelEngine_userSubscriptions, _ChannelEngine_publisher, _ChannelEngine_name, _ChannelEngine_buildSubscriber, _ChannelEngine_getOrCreatePresenceEngine, _ChannelEngine_getUsersFromRecipients;
33
+ var _ChannelEngine_instances, _ChannelEngine_endpointId, _ChannelEngine_backend, _ChannelEngine_presenceEngine, _ChannelEngine_assignsCache, _ChannelEngine_userSubscriptions, _ChannelEngine_publisher, _ChannelEngine_distributedSubscription, _ChannelEngine_name, _ChannelEngine_buildSubscriber, _ChannelEngine_getOrCreatePresenceEngine, _ChannelEngine_getUsersFromRecipients, _ChannelEngine_setupDistributedSubscription, _ChannelEngine_handleDistributedMessage, _ChannelEngine_requestChannelState, _ChannelEngine_handleStateRequest, _ChannelEngine_handleStateResponse, _ChannelEngine_handleRemoteUserJoined, _ChannelEngine_handleRemoteUserLeft, _ChannelEngine_handleRemoteMessage, _ChannelEngine_handleRemotePresenceUpdate, _ChannelEngine_handleRemotePresenceRemoved, _ChannelEngine_handleRemoteAssignsUpdate, _ChannelEngine_handleRemoteAssignsRemoved, _ChannelEngine_handleRemoteEvictUser, _ChannelEngine_broadcastToNodes;
34
34
  Object.defineProperty(exports, "__esModule", { value: true });
35
35
  exports.ChannelEngine = void 0;
36
36
  const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
37
37
  const presenceEngine_1 = require("./presenceEngine");
38
+ const types_1 = require("../abstracts/types");
38
39
  const httpError_1 = require("../errors/httpError");
39
40
  class ChannelEngine {
40
- constructor(parent, name) {
41
+ constructor(parent, name, backend = null) {
41
42
  _ChannelEngine_instances.add(this);
42
43
  this.parent = parent;
44
+ _ChannelEngine_endpointId.set(this, void 0);
45
+ _ChannelEngine_backend.set(this, void 0);
43
46
  _ChannelEngine_presenceEngine.set(this, null);
44
47
  _ChannelEngine_assignsCache.set(this, new Map());
45
48
  _ChannelEngine_userSubscriptions.set(this, new Map());
46
49
  _ChannelEngine_publisher.set(this, new pondsocket_common_1.Subject());
50
+ _ChannelEngine_distributedSubscription.set(this, null);
47
51
  _ChannelEngine_name.set(this, void 0);
48
52
  __classPrivateFieldSet(this, _ChannelEngine_name, name, "f");
53
+ __classPrivateFieldSet(this, _ChannelEngine_backend, backend, "f");
54
+ __classPrivateFieldSet(this, _ChannelEngine_endpointId, parent.parent.path, "f");
55
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
56
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupDistributedSubscription).call(this);
57
+ }
49
58
  }
50
59
  get name() {
51
60
  return __classPrivateFieldGet(this, _ChannelEngine_name, "f");
@@ -65,10 +74,22 @@ class ChannelEngine {
65
74
  const message = `ChannelEngine: User with id ${userId} already exists in channel ${this.name}`;
66
75
  throw new httpError_1.HttpError(400, message);
67
76
  }
68
- // Store user assigns
77
+ const isFirstUser = this.users.size === 0;
69
78
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(userId, assigns);
70
- // Subscribe user to messages
71
79
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_buildSubscriber).call(this, userId, onMessage);
80
+ if (isFirstUser && __classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
81
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_requestChannelState).call(this);
82
+ }
83
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
84
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
85
+ type: types_1.DistributedMessageType.USER_JOINED,
86
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
87
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
88
+ userId,
89
+ presence: {},
90
+ assigns,
91
+ });
92
+ }
72
93
  // Send acknowledgment
73
94
  this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, [userId], pondsocket_common_1.ServerActions.SYSTEM, pondsocket_common_1.Events.ACKNOWLEDGE, {});
74
95
  // Return unsubscribe function
@@ -91,15 +112,31 @@ class ChannelEngine {
91
112
  };
92
113
  const recipients = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getUsersFromRecipients).call(this, recipient, sender);
93
114
  const internalEvent = Object.assign(Object.assign({}, channelEvent), { recipients });
115
+ // Publish locally
94
116
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(internalEvent);
117
+ // If distributed and this is a user message, broadcast to other nodes
118
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f") && action === pondsocket_common_1.ServerActions.BROADCAST && sender !== pondsocket_common_1.SystemSender.CHANNEL) {
119
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
120
+ type: types_1.DistributedMessageType.USER_MESSAGE,
121
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
122
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
123
+ fromUserId: sender,
124
+ event,
125
+ payload,
126
+ requestId,
127
+ recipients,
128
+ });
129
+ }
95
130
  }
96
131
  /**
97
132
  * Broadcasts a message from a user
133
+ * @param userId - The ID of the user sending the message
134
+ * @param message - The message to broadcast
98
135
  */
99
136
  broadcastMessage(userId, message) {
100
137
  if (!this.users.has(userId)) {
101
- const message = `ChannelEngine: User with id ${userId} does not exist in channel ${this.name}`;
102
- throw new httpError_1.HttpError(404, message);
138
+ const messageText = `ChannelEngine: User with id ${userId} does not exist in channel ${this.name}`;
139
+ throw new httpError_1.HttpError(404, messageText);
103
140
  }
104
141
  const responseEvent = Object.assign(Object.assign({}, message), { sender: userId, action: pondsocket_common_1.ServerActions.BROADCAST });
105
142
  this.parent.middleware.run(responseEvent, this, (error) => {
@@ -111,24 +148,58 @@ class ChannelEngine {
111
148
  }
112
149
  /**
113
150
  * Tracks a user's presence
151
+ * @param userId - The ID of the user
152
+ * @param presence - The presence data to track
114
153
  */
115
154
  trackPresence(userId, presence) {
116
155
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
117
156
  presenceEngine.trackPresence(userId, presence);
157
+ // Broadcast presence update to other nodes
158
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
159
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
160
+ type: types_1.DistributedMessageType.PRESENCE_UPDATE,
161
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
162
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
163
+ userId,
164
+ presence,
165
+ });
166
+ }
118
167
  }
119
168
  /**
120
169
  * Updates a user's presence
170
+ * @param userId - The ID of the user
171
+ * @param presence - The presence data to update
121
172
  */
122
173
  updatePresence(userId, presence) {
123
174
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
124
175
  presenceEngine.updatePresence(userId, presence);
176
+ // Broadcast presence update to other nodes
177
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
178
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
179
+ type: types_1.DistributedMessageType.PRESENCE_UPDATE,
180
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
181
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
182
+ userId,
183
+ presence,
184
+ });
185
+ }
125
186
  }
126
187
  /**
127
188
  * Removes a user's presence
189
+ * @param userId - The ID of the user to remove presence for
128
190
  */
129
191
  removePresence(userId) {
130
192
  if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
131
193
  __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(userId);
194
+ // Broadcast presence removal to other nodes
195
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
196
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
197
+ type: types_1.DistributedMessageType.PRESENCE_REMOVED,
198
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
199
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
200
+ userId,
201
+ });
202
+ }
132
203
  }
133
204
  }
134
205
  /**
@@ -137,6 +208,16 @@ class ChannelEngine {
137
208
  upsertPresence(userId, presence) {
138
209
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
139
210
  presenceEngine.upsertPresence(userId, presence);
211
+ // Broadcast presence update to other nodes
212
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
213
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
214
+ type: types_1.DistributedMessageType.PRESENCE_UPDATE,
215
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
216
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
217
+ userId,
218
+ presence,
219
+ });
220
+ }
140
221
  }
141
222
  /**
142
223
  * Updates a user's assigns
@@ -146,7 +227,18 @@ class ChannelEngine {
146
227
  throw new httpError_1.HttpError(404, `User with id ${userId} does not exist in channel ${this.name}`);
147
228
  }
148
229
  const currentAssigns = __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").get(userId) || {};
149
- __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(userId, Object.assign(Object.assign({}, currentAssigns), assigns));
230
+ const newAssigns = Object.assign(Object.assign({}, currentAssigns), assigns);
231
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(userId, newAssigns);
232
+ // Broadcast assigns update to other nodes
233
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
234
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
235
+ type: types_1.DistributedMessageType.ASSIGNS_UPDATE,
236
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
237
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
238
+ userId,
239
+ assigns: newAssigns,
240
+ });
241
+ }
150
242
  }
151
243
  /**
152
244
  * Kicks a user from the channel
@@ -156,6 +248,16 @@ class ChannelEngine {
156
248
  message: reason,
157
249
  code: 403,
158
250
  });
251
+ // Broadcast eviction to other nodes
252
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
253
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
254
+ type: types_1.DistributedMessageType.EVICT_USER,
255
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
256
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
257
+ userId,
258
+ reason,
259
+ });
260
+ }
159
261
  this.removeUser(userId);
160
262
  this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.SYSTEM, 'kicked', {
161
263
  userId,
@@ -211,19 +313,26 @@ class ChannelEngine {
211
313
  unsubscribe();
212
314
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").delete(userId);
213
315
  }
214
- // Trigger leave callback if defined
316
+ // Broadcast user left to other nodes
317
+ if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
318
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
319
+ type: types_1.DistributedMessageType.USER_LEFT,
320
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
321
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
322
+ userId,
323
+ });
324
+ }
215
325
  if (this.parent.leaveCallback) {
216
326
  this.parent.leaveCallback({
217
327
  user: userData,
218
328
  channel: this.parent.wrapChannel(this),
219
329
  });
220
330
  }
221
- // If no users left, close the channel
222
331
  if (this.users.size === 0) {
223
332
  this.close();
224
333
  }
225
334
  }
226
- catch (error) {
335
+ catch (_a) {
227
336
  // Ignore cleanup errors
228
337
  }
229
338
  }
@@ -255,19 +364,23 @@ class ChannelEngine {
255
364
  __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").close();
256
365
  __classPrivateFieldSet(this, _ChannelEngine_presenceEngine, null, "f");
257
366
  }
367
+ if (__classPrivateFieldGet(this, _ChannelEngine_distributedSubscription, "f")) {
368
+ __classPrivateFieldGet(this, _ChannelEngine_distributedSubscription, "f").call(this);
369
+ __classPrivateFieldSet(this, _ChannelEngine_distributedSubscription, null, "f");
370
+ }
258
371
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").close();
259
372
  this.parent.deleteChannel(this.name);
260
373
  }
261
374
  }
262
375
  exports.ChannelEngine = ChannelEngine;
263
- _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) {
376
+ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(), _ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new WeakMap(), _ChannelEngine_userSubscriptions = new WeakMap(), _ChannelEngine_publisher = new WeakMap(), _ChannelEngine_distributedSubscription = new WeakMap(), _ChannelEngine_name = new WeakMap(), _ChannelEngine_instances = new WeakSet(), _ChannelEngine_buildSubscriber = function _ChannelEngine_buildSubscriber(userId, onMessage) {
264
377
  const subscription = __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").subscribe((_a) => __awaiter(this, void 0, void 0, function* () {
265
378
  var { recipients } = _a, event = __rest(_a, ["recipients"]);
266
379
  if (recipients.includes(userId)) {
267
- if (event.action === pondsocket_common_1.ServerActions.SYSTEM) {
380
+ if (event.action === pondsocket_common_1.ServerActions.PRESENCE) {
268
381
  return onMessage(event);
269
382
  }
270
- const newEvent = yield this.parent.manageOutgoingEvents(event, userId, this);
383
+ const newEvent = yield this.parent.processOutgoingEvents(event, this, userId);
271
384
  if (newEvent) {
272
385
  onMessage(newEvent);
273
386
  }
@@ -277,7 +390,6 @@ _ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new
277
390
  }, _ChannelEngine_getOrCreatePresenceEngine = function _ChannelEngine_getOrCreatePresenceEngine() {
278
391
  if (!__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
279
392
  __classPrivateFieldSet(this, _ChannelEngine_presenceEngine, new presenceEngine_1.PresenceEngine(this.name), "f");
280
- // Subscribe to presence events and publish them through the channel publisher
281
393
  __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").subscribe((event) => {
282
394
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(event);
283
395
  });
@@ -310,4 +422,163 @@ _ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new
310
422
  break;
311
423
  }
312
424
  return users;
425
+ }, _ChannelEngine_setupDistributedSubscription = function _ChannelEngine_setupDistributedSubscription() {
426
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
427
+ return;
428
+ }
429
+ __classPrivateFieldSet(this, _ChannelEngine_distributedSubscription, __classPrivateFieldGet(this, _ChannelEngine_backend, "f").subscribe(__classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"), __classPrivateFieldGet(this, _ChannelEngine_name, "f"), (message) => {
430
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleDistributedMessage).call(this, message);
431
+ }), "f");
432
+ }, _ChannelEngine_handleDistributedMessage = function _ChannelEngine_handleDistributedMessage(message) {
433
+ switch (message.type) {
434
+ case types_1.DistributedMessageType.STATE_REQUEST:
435
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleStateRequest).call(this, message);
436
+ break;
437
+ case types_1.DistributedMessageType.STATE_RESPONSE:
438
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleStateResponse).call(this, message);
439
+ break;
440
+ case types_1.DistributedMessageType.USER_JOINED:
441
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteUserJoined).call(this, message);
442
+ break;
443
+ case types_1.DistributedMessageType.USER_LEFT:
444
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteUserLeft).call(this, message);
445
+ break;
446
+ case types_1.DistributedMessageType.USER_MESSAGE:
447
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteMessage).call(this, message);
448
+ break;
449
+ case types_1.DistributedMessageType.PRESENCE_UPDATE:
450
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemotePresenceUpdate).call(this, message);
451
+ break;
452
+ case types_1.DistributedMessageType.PRESENCE_REMOVED:
453
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemotePresenceRemoved).call(this, message);
454
+ break;
455
+ case types_1.DistributedMessageType.ASSIGNS_UPDATE:
456
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteAssignsUpdate).call(this, message);
457
+ break;
458
+ case types_1.DistributedMessageType.ASSIGNS_REMOVED:
459
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteAssignsRemoved).call(this, message);
460
+ break;
461
+ case types_1.DistributedMessageType.EVICT_USER:
462
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteEvictUser).call(this, message);
463
+ break;
464
+ default:
465
+ break;
466
+ }
467
+ }, _ChannelEngine_requestChannelState = function _ChannelEngine_requestChannelState() {
468
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
469
+ return;
470
+ }
471
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
472
+ type: types_1.DistributedMessageType.STATE_REQUEST,
473
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
474
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
475
+ fromNode: 'current-node',
476
+ });
477
+ }, _ChannelEngine_handleStateRequest = function _ChannelEngine_handleStateRequest(_message) {
478
+ if (this.users.size === 0) {
479
+ return;
480
+ }
481
+ const users = Array.from(__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").entries()).map(([id, assigns]) => {
482
+ var _a;
483
+ return ({
484
+ id,
485
+ assigns,
486
+ presence: ((_a = __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) === null || _a === void 0 ? void 0 : _a.getPresence(id)) || {},
487
+ });
488
+ });
489
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
490
+ type: types_1.DistributedMessageType.STATE_RESPONSE,
491
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
492
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
493
+ users,
494
+ });
495
+ }, _ChannelEngine_handleStateResponse = function _ChannelEngine_handleStateResponse(message) {
496
+ if (!message.users) {
497
+ return;
498
+ }
499
+ message.users.forEach((user) => {
500
+ if (!this.users.has(user.id)) {
501
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(user.id, user.assigns);
502
+ if (user.presence && Object.keys(user.presence).length > 0) {
503
+ const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
504
+ presenceEngine.trackPresence(user.id, user.presence);
505
+ }
506
+ }
507
+ });
508
+ }, _ChannelEngine_handleRemoteUserJoined = function _ChannelEngine_handleRemoteUserJoined(message) {
509
+ if (this.users.has(message.userId)) {
510
+ return;
511
+ }
512
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, message.assigns);
513
+ if (message.presence && Object.keys(message.presence).length > 0) {
514
+ const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
515
+ presenceEngine.trackPresence(message.userId, message.presence);
516
+ }
517
+ }, _ChannelEngine_handleRemoteUserLeft = function _ChannelEngine_handleRemoteUserLeft(message) {
518
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(message.userId);
519
+ if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
520
+ __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(message.userId);
521
+ }
522
+ }, _ChannelEngine_handleRemoteMessage = function _ChannelEngine_handleRemoteMessage(message) {
523
+ const localRecipients = message.recipients.filter((userId) => this.users.has(userId));
524
+ if (localRecipients.length === 0) {
525
+ return;
526
+ }
527
+ const internalEvent = {
528
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
529
+ requestId: message.requestId,
530
+ action: pondsocket_common_1.ServerActions.BROADCAST,
531
+ event: message.event,
532
+ payload: message.payload,
533
+ recipients: localRecipients,
534
+ };
535
+ __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(internalEvent);
536
+ }, _ChannelEngine_handleRemotePresenceUpdate = function _ChannelEngine_handleRemotePresenceUpdate(message) {
537
+ if (!this.users.has(message.userId)) {
538
+ return;
539
+ }
540
+ const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
541
+ presenceEngine.upsertPresence(message.userId, message.presence);
542
+ }, _ChannelEngine_handleRemotePresenceRemoved = function _ChannelEngine_handleRemotePresenceRemoved(message) {
543
+ if (!this.users.has(message.userId)) {
544
+ return;
545
+ }
546
+ if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
547
+ __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(message.userId);
548
+ }
549
+ }, _ChannelEngine_handleRemoteAssignsUpdate = function _ChannelEngine_handleRemoteAssignsUpdate(message) {
550
+ if (!this.users.has(message.userId)) {
551
+ return;
552
+ }
553
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, message.assigns);
554
+ }, _ChannelEngine_handleRemoteAssignsRemoved = function _ChannelEngine_handleRemoteAssignsRemoved(message) {
555
+ if (!this.users.has(message.userId)) {
556
+ return;
557
+ }
558
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, {});
559
+ }, _ChannelEngine_handleRemoteEvictUser = function _ChannelEngine_handleRemoteEvictUser(message) {
560
+ if (!this.users.has(message.userId)) {
561
+ return;
562
+ }
563
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(message.userId);
564
+ if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
565
+ __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(message.userId);
566
+ }
567
+ const unsubscribe = __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").get(message.userId);
568
+ if (unsubscribe) {
569
+ unsubscribe();
570
+ __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").delete(message.userId);
571
+ }
572
+ }, _ChannelEngine_broadcastToNodes = function _ChannelEngine_broadcastToNodes(message) {
573
+ return __awaiter(this, void 0, void 0, function* () {
574
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
575
+ return;
576
+ }
577
+ try {
578
+ yield __classPrivateFieldGet(this, _ChannelEngine_backend, "f").broadcast(__classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"), __classPrivateFieldGet(this, _ChannelEngine_name, "f"), message);
579
+ }
580
+ catch (_a) {
581
+ // Silently ignore broadcast errors to prevent cascading failures
582
+ }
583
+ });
313
584
  };
@@ -10,7 +10,7 @@ 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, _EndpointEngine_sockets, _EndpointEngine_middleware, _EndpointEngine_lobbyEngines, _EndpointEngine_handleSocketClose, _EndpointEngine_handleMessage, _EndpointEngine_joinChannel, _EndpointEngine_leaveChannel, _EndpointEngine_retrieveChannel, _EndpointEngine_buildError, _EndpointEngine_broadcastMessage, _EndpointEngine_readMessage;
13
+ var _EndpointEngine_instances, _EndpointEngine_sockets, _EndpointEngine_backend, _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");
@@ -21,20 +21,23 @@ const httpError_1 = require("../errors/httpError");
21
21
  const matcher_1 = require("../matcher/matcher");
22
22
  const pondChannel_1 = require("../wrappers/pondChannel");
23
23
  class EndpointEngine {
24
- constructor() {
24
+ constructor(path, backend) {
25
25
  _EndpointEngine_instances.add(this);
26
+ this.path = path;
26
27
  _EndpointEngine_sockets.set(this, void 0);
28
+ _EndpointEngine_backend.set(this, void 0);
27
29
  _EndpointEngine_middleware.set(this, void 0);
28
30
  _EndpointEngine_lobbyEngines.set(this, void 0);
29
31
  __classPrivateFieldSet(this, _EndpointEngine_sockets, new Map(), "f");
30
32
  __classPrivateFieldSet(this, _EndpointEngine_lobbyEngines, new Map(), "f");
31
33
  __classPrivateFieldSet(this, _EndpointEngine_middleware, new middleware_1.Middleware(), "f");
34
+ __classPrivateFieldSet(this, _EndpointEngine_backend, backend || null, "f");
32
35
  }
33
36
  /**
34
37
  * Creates a new channel on a specified path
35
38
  */
36
39
  createChannel(path, handler) {
37
- const lobbyEngine = new lobbyEngine_1.LobbyEngine(this);
40
+ const lobbyEngine = new lobbyEngine_1.LobbyEngine(this, __classPrivateFieldGet(this, _EndpointEngine_backend, "f"));
38
41
  __classPrivateFieldGet(this, _EndpointEngine_middleware, "f").use((user, joinParams, next) => {
39
42
  const event = (0, matcher_1.parseAddress)(path, user.channelName);
40
43
  if (event) {
@@ -106,7 +109,7 @@ class EndpointEngine {
106
109
  }
107
110
  }
108
111
  exports.EndpointEngine = EndpointEngine;
109
- _EndpointEngine_sockets = new WeakMap(), _EndpointEngine_middleware = new WeakMap(), _EndpointEngine_lobbyEngines = new WeakMap(), _EndpointEngine_instances = new WeakSet(), _EndpointEngine_handleSocketClose = function _EndpointEngine_handleSocketClose(cache) {
112
+ _EndpointEngine_sockets = new WeakMap(), _EndpointEngine_backend = new WeakMap(), _EndpointEngine_middleware = new WeakMap(), _EndpointEngine_lobbyEngines = new WeakMap(), _EndpointEngine_instances = new WeakSet(), _EndpointEngine_handleSocketClose = function _EndpointEngine_handleSocketClose(cache) {
110
113
  try {
111
114
  __classPrivateFieldGet(this, _EndpointEngine_sockets, "f").delete(cache.clientId);
112
115
  cache.subscriptions.forEach((unsubscribe) => unsubscribe());
@@ -19,24 +19,26 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
19
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");
20
20
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
21
21
  };
22
- var _LobbyEngine_instances, _LobbyEngine_channels, _LobbyEngine_outGoingEvents, _LobbyEngine_getChannel, _LobbyEngine_createChannel;
22
+ var _LobbyEngine_instances, _LobbyEngine_backend, _LobbyEngine_channels, _LobbyEngine_getChannel, _LobbyEngine_createChannel;
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.LobbyEngine = void 0;
25
25
  const channelEngine_1 = require("./channelEngine");
26
26
  const middleware_1 = require("../abstracts/middleware");
27
27
  const eventContext_1 = require("../contexts/eventContext");
28
+ const outgoingContext_1 = require("../contexts/outgoingContext");
28
29
  const httpError_1 = require("../errors/httpError");
29
30
  const matcher_1 = require("../matcher/matcher");
30
31
  const channel_1 = require("../wrappers/channel");
31
32
  class LobbyEngine {
32
- constructor(parent) {
33
+ constructor(parent, backend) {
33
34
  _LobbyEngine_instances.add(this);
34
35
  this.parent = parent;
36
+ _LobbyEngine_backend.set(this, void 0);
35
37
  _LobbyEngine_channels.set(this, void 0);
36
- _LobbyEngine_outGoingEvents.set(this, void 0);
37
38
  this.middleware = new middleware_1.Middleware();
39
+ this.outgoing = new middleware_1.Middleware();
38
40
  __classPrivateFieldSet(this, _LobbyEngine_channels, new Map(), "f");
39
- __classPrivateFieldSet(this, _LobbyEngine_outGoingEvents, [], "f");
41
+ __classPrivateFieldSet(this, _LobbyEngine_backend, backend, "f");
40
42
  }
41
43
  /**
42
44
  * Sets the callback for when users leave channels
@@ -61,51 +63,38 @@ class LobbyEngine {
61
63
  * Attaches a handler for outgoing events
62
64
  */
63
65
  handleOutgoingEvent(event, handler) {
64
- const handlerWrapper = (request, channel, userId) => __awaiter(this, void 0, void 0, function* () {
65
- try {
66
- const params = (0, matcher_1.parseAddress)(event, request.event);
67
- if (!params) {
68
- return true;
69
- }
70
- const userData = channel.getUserData(userId);
71
- const newEvent = {
72
- channel,
73
- userData,
74
- event: params,
75
- payload: request.payload,
76
- };
77
- const response = yield handler(newEvent);
78
- if (response) {
79
- return response;
80
- }
81
- return false;
66
+ this.outgoing.use((context, chEvent, next) => __awaiter(this, void 0, void 0, function* () {
67
+ const params = (0, matcher_1.parseAddress)(event, chEvent.event);
68
+ if (!params || context.isBlocked()) {
69
+ return next();
82
70
  }
83
- catch (error) {
84
- return false;
71
+ context.updateParams(params);
72
+ const payload = yield handler(context, next);
73
+ if (payload === undefined || payload === null) {
74
+ return;
85
75
  }
86
- });
87
- __classPrivateFieldGet(this, _LobbyEngine_outGoingEvents, "f").push(handlerWrapper);
76
+ context.transform(Object.assign(Object.assign({}, chEvent), { payload }));
77
+ return next();
78
+ }));
88
79
  }
89
80
  /**
90
- * Internal method to handle outgoing events
91
- * @param request - The request object
92
- * @param userId - The user ID
93
- * @param channel - The channel engine
81
+ * Processes an outgoing event, applying middleware and returning a new event
82
+ * @param event - The channel event to process
83
+ * @param engine - The channel engine handling the event
84
+ * @param userId - The ID of the user sending the event
85
+ * @returns A new ChannelEvent or undefined if blocked
94
86
  */
95
- manageOutgoingEvents(request, userId, channel) {
87
+ processOutgoingEvents(event, engine, userId) {
96
88
  return __awaiter(this, void 0, void 0, function* () {
97
- if (!__classPrivateFieldGet(this, _LobbyEngine_outGoingEvents, "f").length) {
98
- return request;
99
- }
100
- const promises = __classPrivateFieldGet(this, _LobbyEngine_outGoingEvents, "f").map((handler) => handler(request, this.wrapChannel(channel), userId));
101
- const results = yield Promise.all(promises);
102
- if (results.some((result) => result === false)) {
103
- return null;
89
+ const params = (0, matcher_1.parseAddress)('*', event.event);
90
+ const context = new outgoingContext_1.OutgoingContext(event, params, engine, userId);
91
+ yield this.outgoing.runAsync(context, event, () => {
92
+ // no-op
93
+ });
94
+ if (context.isBlocked()) {
95
+ return;
104
96
  }
105
- request.payload = results
106
- .filter((result) => typeof result !== 'boolean')
107
- .reduce((acc, result) => Object.assign(acc, result), {});
108
- return request;
97
+ return Object.assign(Object.assign({}, event), { payload: context.payload, event: context.event.event });
109
98
  });
110
99
  }
111
100
  /**
@@ -120,7 +109,7 @@ class LobbyEngine {
120
109
  }
121
110
  /**
122
111
  * Gets a channel by name
123
- * @throws HttpError if channel not found
112
+ * @throws HttpError if a channel not found
124
113
  */
125
114
  getChannel(channelName) {
126
115
  const channel = __classPrivateFieldGet(this, _LobbyEngine_channels, "f").get(channelName);
@@ -147,18 +136,10 @@ class LobbyEngine {
147
136
  }
148
137
  }
149
138
  exports.LobbyEngine = LobbyEngine;
150
- _LobbyEngine_channels = new WeakMap(), _LobbyEngine_outGoingEvents = new WeakMap(), _LobbyEngine_instances = new WeakSet(), _LobbyEngine_getChannel = function _LobbyEngine_getChannel(channelName) {
139
+ _LobbyEngine_backend = new WeakMap(), _LobbyEngine_channels = new WeakMap(), _LobbyEngine_instances = new WeakSet(), _LobbyEngine_getChannel = function _LobbyEngine_getChannel(channelName) {
151
140
  return __classPrivateFieldGet(this, _LobbyEngine_channels, "f").get(channelName) || null;
152
141
  }, _LobbyEngine_createChannel = function _LobbyEngine_createChannel(channelName) {
153
- // Create the channel engine
154
- const channel = new channelEngine_1.ChannelEngine(this, channelName);
155
- // Add it to the channels map
142
+ const channel = new channelEngine_1.ChannelEngine(this, channelName, __classPrivateFieldGet(this, _LobbyEngine_backend, "f"));
156
143
  __classPrivateFieldGet(this, _LobbyEngine_channels, "f").set(channelName, channel);
157
- // Set up a way to remove the channel when it's closed
158
- // (We'll handle this in the ChannelEngine's close method)
159
- const handleChannelClosed = () => {
160
- __classPrivateFieldGet(this, _LobbyEngine_channels, "f").delete(channelName);
161
- };
162
- // Return the new channel engine
163
144
  return channel;
164
145
  };
@@ -124,7 +124,6 @@ _PresenceEngine_presenceCache = new WeakMap(), _PresenceEngine_publisher = new W
124
124
  }
125
125
  const total = Array.from(__classPrivateFieldGet(this, _PresenceEngine_presenceCache, "f").values());
126
126
  const userIds = Array.from(__classPrivateFieldGet(this, _PresenceEngine_presenceCache, "f").keys());
127
- // Create the presence event
128
127
  const internalEvent = {
129
128
  event: action,
130
129
  requestId: (0, pondsocket_common_1.uuid)(),