@eleven-am/pondsocket 0.1.213 → 0.1.215

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,30 +30,44 @@ var __rest = (this && this.__rest) || function (s, e) {
30
30
  }
31
31
  return t;
32
32
  };
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;
33
+ var _ChannelEngine_instances, _ChannelEngine_endpointId, _ChannelEngine_backend, _ChannelEngine_nodeId, _ChannelEngine_presenceEngine, _ChannelEngine_assignsCache, _ChannelEngine_userSubscriptions, _ChannelEngine_publisher, _ChannelEngine_distributedSubscription, _ChannelEngine_heartbeatSubscription, _ChannelEngine_nodeLastSeen, _ChannelEngine_nodeUsers, _ChannelEngine_staleNodeTimer, _ChannelEngine_name, _ChannelEngine_safeRemovePresence, _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_handleRemoteEvictUser, _ChannelEngine_setupHeartbeatTracking, _ChannelEngine_cleanupStaleNodes, _ChannelEngine_trackNodeUser, _ChannelEngine_untrackNodeUser, _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");
39
38
  const httpError_1 = require("../errors/httpError");
39
+ const types_1 = require("../types");
40
+ function mapToObject(map) {
41
+ const result = {};
42
+ for (const [key, value] of map) {
43
+ result[key] = value;
44
+ }
45
+ return result;
46
+ }
40
47
  class ChannelEngine {
41
48
  constructor(parent, name, backend = null) {
42
49
  _ChannelEngine_instances.add(this);
43
50
  this.parent = parent;
44
51
  _ChannelEngine_endpointId.set(this, void 0);
45
52
  _ChannelEngine_backend.set(this, void 0);
53
+ _ChannelEngine_nodeId.set(this, void 0);
46
54
  _ChannelEngine_presenceEngine.set(this, null);
47
55
  _ChannelEngine_assignsCache.set(this, new Map());
48
56
  _ChannelEngine_userSubscriptions.set(this, new Map());
49
57
  _ChannelEngine_publisher.set(this, new pondsocket_common_1.Subject());
50
58
  _ChannelEngine_distributedSubscription.set(this, null);
59
+ _ChannelEngine_heartbeatSubscription.set(this, null);
60
+ _ChannelEngine_nodeLastSeen.set(this, new Map());
61
+ _ChannelEngine_nodeUsers.set(this, new Map());
62
+ _ChannelEngine_staleNodeTimer.set(this, null);
51
63
  _ChannelEngine_name.set(this, void 0);
52
64
  __classPrivateFieldSet(this, _ChannelEngine_name, name, "f");
53
65
  __classPrivateFieldSet(this, _ChannelEngine_backend, backend, "f");
66
+ __classPrivateFieldSet(this, _ChannelEngine_nodeId, (0, pondsocket_common_1.uuid)(), "f");
54
67
  __classPrivateFieldSet(this, _ChannelEngine_endpointId, parent.parent.path, "f");
55
68
  if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
56
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupDistributedSubscription).call(this);
69
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupDistributedSubscription).call(this).catch(() => { });
70
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupHeartbeatTracking).call(this);
57
71
  }
58
72
  }
59
73
  get name() {
@@ -62,13 +76,6 @@ class ChannelEngine {
62
76
  get users() {
63
77
  return new Set(__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").keys());
64
78
  }
65
- /**
66
- * Adds a user to the channel
67
- * @param userId - The user ID
68
- * @param assigns - The user's assigns
69
- * @param onMessage - Callback for messages
70
- * @returns Unsubscribe function
71
- */
72
79
  addUser(userId, assigns, onMessage) {
73
80
  if (this.users.has(userId)) {
74
81
  const message = `ChannelEngine: User with id ${userId} already exists in channel ${this.name}`;
@@ -87,21 +94,16 @@ class ChannelEngine {
87
94
  if (isFirstUser && __classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
88
95
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_requestChannelState).call(this);
89
96
  }
90
- if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
91
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
92
- type: types_1.DistributedMessageType.USER_JOINED,
93
- endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
94
- channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
95
- userId,
96
- presence: {},
97
- assigns,
98
- });
99
- }
97
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
98
+ type: types_1.DistributedMessageType.USER_JOINED,
99
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
100
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
101
+ userId,
102
+ presence: {},
103
+ assigns,
104
+ });
100
105
  return () => this.removeUser(userId);
101
106
  }
102
- /**
103
- * Sends a message to recipients
104
- */
105
107
  sendMessage(sender, recipient, action, event, payload, requestId = (0, pondsocket_common_1.uuid)()) {
106
108
  if (!this.users.has(sender) && sender !== pondsocket_common_1.SystemSender.CHANNEL) {
107
109
  const message = `ChannelEngine: User with id ${sender} does not exist in channel ${this.name}`;
@@ -117,24 +119,17 @@ class ChannelEngine {
117
119
  const recipients = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getUsersFromRecipients).call(this, recipient, sender);
118
120
  const internalEvent = Object.assign(Object.assign({}, channelEvent), { recipients });
119
121
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(internalEvent);
120
- if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
121
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
122
- type: types_1.DistributedMessageType.USER_MESSAGE,
123
- endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
124
- channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
125
- fromUserId: sender,
126
- event,
127
- payload,
128
- requestId,
129
- recipients,
130
- });
131
- }
122
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
123
+ type: types_1.DistributedMessageType.USER_MESSAGE,
124
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
125
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
126
+ fromUserId: sender,
127
+ event,
128
+ payload,
129
+ requestId,
130
+ recipientDescriptor: recipient,
131
+ });
132
132
  }
133
- /**
134
- * Broadcasts a message from a user
135
- * @param userId - The ID of the user sending the message
136
- * @param message - The message to broadcast
137
- */
138
133
  broadcastMessage(userId, message) {
139
134
  if (!this.users.has(userId)) {
140
135
  const messageText = `ChannelEngine: User with id ${userId} does not exist in channel ${this.name}`;
@@ -148,80 +143,50 @@ class ChannelEngine {
148
143
  }, message.requestId);
149
144
  });
150
145
  }
151
- /**
152
- * Tracks a user's presence
153
- * @param userId - The ID of the user
154
- * @param presence - The presence data to track
155
- */
156
146
  trackPresence(userId, presence) {
157
147
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
158
148
  presenceEngine.trackPresence(userId, presence);
159
- if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
160
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
161
- type: types_1.DistributedMessageType.PRESENCE_UPDATE,
162
- endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
163
- channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
164
- userId,
165
- presence,
166
- });
167
- }
149
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
150
+ type: types_1.DistributedMessageType.PRESENCE_UPDATE,
151
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
152
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
153
+ userId,
154
+ presence,
155
+ });
168
156
  }
169
- /**
170
- * Updates a user's presence
171
- * @param userId - The ID of the user
172
- * @param presence - The presence data to update
173
- */
174
157
  updatePresence(userId, presence) {
175
158
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
176
159
  presenceEngine.updatePresence(userId, presence);
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
- }
160
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
161
+ type: types_1.DistributedMessageType.PRESENCE_UPDATE,
162
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
163
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
164
+ userId,
165
+ presence,
166
+ });
186
167
  }
187
- /**
188
- * Removes a user's presence
189
- * @param userId - The ID of the user to remove presence for
190
- */
191
168
  removePresence(userId) {
192
169
  if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
193
170
  __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
- }
203
- }
204
- }
205
- /**
206
- * Adds or updates a user's presence
207
- */
208
- upsertPresence(userId, presence) {
209
- const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
210
- presenceEngine.upsertPresence(userId, presence);
211
- // Broadcast presence update to other nodes
212
- if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
213
171
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
214
- type: types_1.DistributedMessageType.PRESENCE_UPDATE,
172
+ type: types_1.DistributedMessageType.PRESENCE_REMOVED,
215
173
  endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
216
174
  channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
217
175
  userId,
218
- presence,
219
176
  });
220
177
  }
221
178
  }
222
- /**
223
- * Updates a user's assigns
224
- */
179
+ upsertPresence(userId, presence) {
180
+ const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
181
+ presenceEngine.upsertPresence(userId, presence);
182
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
183
+ type: types_1.DistributedMessageType.PRESENCE_UPDATE,
184
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
185
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
186
+ userId,
187
+ presence,
188
+ });
189
+ }
225
190
  updateAssigns(userId, assigns) {
226
191
  if (!__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").has(userId)) {
227
192
  throw new httpError_1.HttpError(404, `User with id ${userId} does not exist in channel ${this.name}`);
@@ -229,92 +194,58 @@ class ChannelEngine {
229
194
  const currentAssigns = __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").get(userId) || {};
230
195
  const newAssigns = Object.assign(Object.assign({}, currentAssigns), assigns);
231
196
  __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
- }
197
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
198
+ type: types_1.DistributedMessageType.ASSIGNS_UPDATE,
199
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
200
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
201
+ userId,
202
+ assigns: newAssigns,
203
+ });
242
204
  }
243
- /**
244
- * Kicks a user from the channel
245
- */
246
205
  kickUser(userId, reason) {
247
206
  this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, [userId], pondsocket_common_1.ServerActions.SYSTEM, 'kicked_out', {
248
207
  message: reason,
249
208
  code: 403,
250
209
  });
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
- }
261
- this.removeUser(userId);
262
210
  this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.SYSTEM, 'kicked', {
263
211
  userId,
264
212
  reason,
265
213
  });
214
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
215
+ type: types_1.DistributedMessageType.EVICT_USER,
216
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
217
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
218
+ userId,
219
+ reason,
220
+ });
221
+ this.removeUser(userId, true);
266
222
  }
267
- /**
268
- * Gets all assigns as an object
269
- */
270
223
  getAssigns() {
271
- return Array
272
- .from(__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").entries())
273
- .reduce((acc, [id, assigns]) => {
274
- acc[id] = assigns;
275
- return acc;
276
- }, {});
277
- }
278
- /**
279
- * Gets all presence data as an object
280
- */
224
+ return mapToObject(__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f"));
225
+ }
281
226
  getPresence() {
282
227
  if (!__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
283
228
  return {};
284
229
  }
285
- return Array
286
- .from(__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").getAllPresence().entries())
287
- .reduce((acc, [id, presence]) => {
288
- acc[id] = presence;
289
- return acc;
290
- }, {});
291
- }
292
- /**
293
- * Destroys the channel
294
- */
230
+ return mapToObject(__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").getAllPresence());
231
+ }
295
232
  destroy(reason) {
296
233
  this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.ERROR, 'destroyed', {
297
234
  message: reason !== null && reason !== void 0 ? reason : 'Channel has been destroyed',
298
235
  });
299
236
  this.close();
300
237
  }
301
- /**
302
- * Removes a user from the channel
303
- */
304
- removeUser(userId) {
238
+ removeUser(userId, skipDistributedBroadcast = false) {
305
239
  try {
306
240
  const userData = this.getUserData(userId);
307
241
  const unsubscribe = __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").get(userId);
308
242
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(userId);
309
- if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
310
- __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(userId);
311
- }
243
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, userId);
312
244
  if (unsubscribe) {
313
245
  unsubscribe();
314
246
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").delete(userId);
315
247
  }
316
- // Broadcast user left to other nodes
317
- if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
248
+ if (!skipDistributedBroadcast) {
318
249
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
319
250
  type: types_1.DistributedMessageType.USER_LEFT,
320
251
  endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
@@ -332,13 +263,10 @@ class ChannelEngine {
332
263
  this.close();
333
264
  }
334
265
  }
335
- catch (_a) {
336
- // Ignore cleanup errors
266
+ catch (_) {
267
+ void 0;
337
268
  }
338
269
  }
339
- /**
340
- * Gets user data by ID
341
- */
342
270
  getUserData(userId) {
343
271
  var _a;
344
272
  const assigns = __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").get(userId);
@@ -353,9 +281,6 @@ class ChannelEngine {
353
281
  id: userId,
354
282
  };
355
283
  }
356
- /**
357
- * Closes the channel and cleans up resources
358
- */
359
284
  close() {
360
285
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").forEach((unsubscribe) => unsubscribe());
361
286
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").clear();
@@ -368,12 +293,31 @@ class ChannelEngine {
368
293
  __classPrivateFieldGet(this, _ChannelEngine_distributedSubscription, "f").call(this);
369
294
  __classPrivateFieldSet(this, _ChannelEngine_distributedSubscription, null, "f");
370
295
  }
296
+ if (__classPrivateFieldGet(this, _ChannelEngine_heartbeatSubscription, "f")) {
297
+ __classPrivateFieldGet(this, _ChannelEngine_heartbeatSubscription, "f").call(this);
298
+ __classPrivateFieldSet(this, _ChannelEngine_heartbeatSubscription, null, "f");
299
+ }
300
+ if (__classPrivateFieldGet(this, _ChannelEngine_staleNodeTimer, "f")) {
301
+ clearInterval(__classPrivateFieldGet(this, _ChannelEngine_staleNodeTimer, "f"));
302
+ __classPrivateFieldSet(this, _ChannelEngine_staleNodeTimer, null, "f");
303
+ }
304
+ __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").clear();
305
+ __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").clear();
371
306
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").close();
372
307
  this.parent.deleteChannel(this.name);
373
308
  }
374
309
  }
375
310
  exports.ChannelEngine = ChannelEngine;
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) {
311
+ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(), _ChannelEngine_nodeId = new WeakMap(), _ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new WeakMap(), _ChannelEngine_userSubscriptions = new WeakMap(), _ChannelEngine_publisher = new WeakMap(), _ChannelEngine_distributedSubscription = new WeakMap(), _ChannelEngine_heartbeatSubscription = new WeakMap(), _ChannelEngine_nodeLastSeen = new WeakMap(), _ChannelEngine_nodeUsers = new WeakMap(), _ChannelEngine_staleNodeTimer = new WeakMap(), _ChannelEngine_name = new WeakMap(), _ChannelEngine_instances = new WeakSet(), _ChannelEngine_safeRemovePresence = function _ChannelEngine_safeRemovePresence(userId) {
312
+ if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
313
+ try {
314
+ __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(userId);
315
+ }
316
+ catch (_) {
317
+ void 0;
318
+ }
319
+ }
320
+ }, _ChannelEngine_buildSubscriber = function _ChannelEngine_buildSubscriber(userId, onMessage) {
377
321
  const subscription = __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").subscribe((_a) => __awaiter(this, void 0, void 0, function* () {
378
322
  var { recipients } = _a, event = __rest(_a, ["recipients"]);
379
323
  if (recipients.includes(userId)) {
@@ -389,10 +333,9 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
389
333
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").set(userId, subscription);
390
334
  }, _ChannelEngine_getOrCreatePresenceEngine = function _ChannelEngine_getOrCreatePresenceEngine() {
391
335
  if (!__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
392
- __classPrivateFieldSet(this, _ChannelEngine_presenceEngine, new presenceEngine_1.PresenceEngine(this.name), "f");
393
- __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").subscribe((event) => {
336
+ __classPrivateFieldSet(this, _ChannelEngine_presenceEngine, new presenceEngine_1.PresenceEngine(this.name, (event) => {
394
337
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(event);
395
- });
338
+ }), "f");
396
339
  }
397
340
  return __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f");
398
341
  }, _ChannelEngine_getUsersFromRecipients = function _ChannelEngine_getUsersFromRecipients(recipients, sender) {
@@ -423,12 +366,14 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
423
366
  }
424
367
  return users;
425
368
  }, _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");
369
+ return __awaiter(this, void 0, void 0, function* () {
370
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
371
+ return;
372
+ }
373
+ __classPrivateFieldSet(this, _ChannelEngine_distributedSubscription, yield __classPrivateFieldGet(this, _ChannelEngine_backend, "f").subscribeToChannel(__classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"), __classPrivateFieldGet(this, _ChannelEngine_name, "f"), (message) => {
374
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleDistributedMessage).call(this, message);
375
+ }), "f");
376
+ });
432
377
  }, _ChannelEngine_handleDistributedMessage = function _ChannelEngine_handleDistributedMessage(message) {
433
378
  switch (message.type) {
434
379
  case types_1.DistributedMessageType.STATE_REQUEST:
@@ -455,24 +400,20 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
455
400
  case types_1.DistributedMessageType.ASSIGNS_UPDATE:
456
401
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteAssignsUpdate).call(this, message);
457
402
  break;
458
- case types_1.DistributedMessageType.ASSIGNS_REMOVED:
459
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteAssignsRemoved).call(this, message);
460
- break;
461
403
  case types_1.DistributedMessageType.EVICT_USER:
462
404
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteEvictUser).call(this, message);
463
405
  break;
406
+ case types_1.DistributedMessageType.NODE_HEARTBEAT:
407
+ break;
464
408
  default:
465
409
  break;
466
410
  }
467
411
  }, _ChannelEngine_requestChannelState = function _ChannelEngine_requestChannelState() {
468
- if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
469
- return;
470
- }
471
412
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
472
413
  type: types_1.DistributedMessageType.STATE_REQUEST,
473
414
  endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
474
415
  channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
475
- fromNode: 'current-node',
416
+ fromNode: __classPrivateFieldGet(this, _ChannelEngine_nodeId, "f"),
476
417
  });
477
418
  }, _ChannelEngine_handleStateRequest = function _ChannelEngine_handleStateRequest(_message) {
478
419
  if (this.users.size === 0) {
@@ -499,9 +440,10 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
499
440
  message.users.forEach((user) => {
500
441
  if (!this.users.has(user.id)) {
501
442
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(user.id, user.assigns);
443
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_trackNodeUser).call(this, message.sourceNodeId, user.id);
502
444
  if (user.presence && Object.keys(user.presence).length > 0) {
503
445
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
504
- presenceEngine.trackPresence(user.id, user.presence);
446
+ presenceEngine.upsertPresence(user.id, user.presence);
505
447
  }
506
448
  }
507
449
  });
@@ -510,46 +452,124 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
510
452
  return;
511
453
  }
512
454
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, message.assigns);
455
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_trackNodeUser).call(this, message.sourceNodeId, message.userId);
513
456
  if (message.presence && Object.keys(message.presence).length > 0) {
514
457
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
515
458
  presenceEngine.trackPresence(message.userId, message.presence);
516
459
  }
517
460
  }, _ChannelEngine_handleRemoteUserLeft = function _ChannelEngine_handleRemoteUserLeft(message) {
518
461
  __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
- }
462
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_untrackNodeUser).call(this, message.sourceNodeId, message.userId);
463
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, message.userId);
522
464
  }, _ChannelEngine_handleRemoteMessage = function _ChannelEngine_handleRemoteMessage(message) {
465
+ const localUsers = Array.from(this.users);
466
+ let recipients;
467
+ if (message.recipientDescriptor === pondsocket_common_1.ChannelReceiver.ALL_USERS) {
468
+ recipients = localUsers;
469
+ }
470
+ else if (message.recipientDescriptor === pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER) {
471
+ recipients = localUsers.filter((u) => u !== message.fromUserId);
472
+ }
473
+ else if (Array.isArray(message.recipientDescriptor)) {
474
+ recipients = localUsers.filter((u) => message.recipientDescriptor.includes(u));
475
+ }
476
+ else {
477
+ return;
478
+ }
479
+ if (recipients.length === 0) {
480
+ return;
481
+ }
523
482
  const internalEvent = {
524
483
  channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
525
484
  requestId: message.requestId,
526
485
  action: pondsocket_common_1.ServerActions.BROADCAST,
527
486
  event: message.event,
528
487
  payload: message.payload,
529
- recipients: message.recipients,
488
+ recipients,
530
489
  };
531
490
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(internalEvent);
532
491
  }, _ChannelEngine_handleRemotePresenceUpdate = function _ChannelEngine_handleRemotePresenceUpdate(message) {
533
492
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
534
493
  presenceEngine.upsertPresence(message.userId, message.presence);
535
494
  }, _ChannelEngine_handleRemotePresenceRemoved = function _ChannelEngine_handleRemotePresenceRemoved(message) {
536
- if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
537
- __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(message.userId);
538
- }
495
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, message.userId);
539
496
  }, _ChannelEngine_handleRemoteAssignsUpdate = function _ChannelEngine_handleRemoteAssignsUpdate(message) {
540
497
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, message.assigns);
541
- }, _ChannelEngine_handleRemoteAssignsRemoved = function _ChannelEngine_handleRemoteAssignsRemoved(message) {
542
- __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, {});
543
498
  }, _ChannelEngine_handleRemoteEvictUser = function _ChannelEngine_handleRemoteEvictUser(message) {
544
- __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(message.userId);
545
- if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
546
- __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(message.userId);
499
+ if (__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").has(message.userId)) {
500
+ const kickedOutEvent = {
501
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
502
+ requestId: (0, pondsocket_common_1.uuid)(),
503
+ action: pondsocket_common_1.ServerActions.SYSTEM,
504
+ event: 'kicked_out',
505
+ payload: {
506
+ message: message.reason,
507
+ code: 403,
508
+ },
509
+ recipients: [message.userId],
510
+ };
511
+ __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(kickedOutEvent);
547
512
  }
513
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(message.userId);
514
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, message.userId);
548
515
  const unsubscribe = __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").get(message.userId);
549
516
  if (unsubscribe) {
550
517
  unsubscribe();
551
518
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").delete(message.userId);
552
519
  }
520
+ }, _ChannelEngine_setupHeartbeatTracking = function _ChannelEngine_setupHeartbeatTracking() {
521
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
522
+ return;
523
+ }
524
+ __classPrivateFieldSet(this, _ChannelEngine_heartbeatSubscription, __classPrivateFieldGet(this, _ChannelEngine_backend, "f").subscribeToHeartbeats((nodeId) => {
525
+ __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").set(nodeId, Date.now());
526
+ }), "f");
527
+ __classPrivateFieldSet(this, _ChannelEngine_staleNodeTimer, setInterval(() => {
528
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_cleanupStaleNodes).call(this);
529
+ }, __classPrivateFieldGet(this, _ChannelEngine_backend, "f").heartbeatTimeoutMs), "f");
530
+ }, _ChannelEngine_cleanupStaleNodes = function _ChannelEngine_cleanupStaleNodes() {
531
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
532
+ return;
533
+ }
534
+ const now = Date.now();
535
+ const timeout = __classPrivateFieldGet(this, _ChannelEngine_backend, "f").heartbeatTimeoutMs;
536
+ for (const [nodeId, lastSeen] of __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f")) {
537
+ if (now - lastSeen >= timeout) {
538
+ const users = __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").get(nodeId);
539
+ if (users) {
540
+ for (const userId of users) {
541
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(userId);
542
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, userId);
543
+ }
544
+ __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").delete(nodeId);
545
+ }
546
+ __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").delete(nodeId);
547
+ }
548
+ }
549
+ }, _ChannelEngine_trackNodeUser = function _ChannelEngine_trackNodeUser(sourceNodeId, userId) {
550
+ if (!sourceNodeId) {
551
+ return;
552
+ }
553
+ let users = __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").get(sourceNodeId);
554
+ if (!users) {
555
+ users = new Set();
556
+ __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").set(sourceNodeId, users);
557
+ }
558
+ users.add(userId);
559
+ if (!__classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").has(sourceNodeId)) {
560
+ __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").set(sourceNodeId, Date.now());
561
+ }
562
+ }, _ChannelEngine_untrackNodeUser = function _ChannelEngine_untrackNodeUser(sourceNodeId, userId) {
563
+ if (!sourceNodeId) {
564
+ return;
565
+ }
566
+ const users = __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").get(sourceNodeId);
567
+ if (users) {
568
+ users.delete(userId);
569
+ if (users.size === 0) {
570
+ __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").delete(sourceNodeId);
571
+ }
572
+ }
553
573
  }, _ChannelEngine_broadcastToNodes = function _ChannelEngine_broadcastToNodes(message) {
554
574
  return __awaiter(this, void 0, void 0, function* () {
555
575
  if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
@@ -558,8 +578,8 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
558
578
  try {
559
579
  yield __classPrivateFieldGet(this, _ChannelEngine_backend, "f").broadcast(__classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"), __classPrivateFieldGet(this, _ChannelEngine_name, "f"), message);
560
580
  }
561
- catch (_a) {
562
- // Silently ignore broadcast errors to prevent cascading failures
581
+ catch (_) {
582
+ void 0;
563
583
  }
564
584
  });
565
585
  };