@eleven-am/pondsocket 0.1.214 → 0.1.216

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,46 @@ 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_closed, _ChannelEngine_distributedReady, _ChannelEngine_safeRemovePresence, _ChannelEngine_buildSubscriber, _ChannelEngine_getOrCreatePresenceEngine, _ChannelEngine_getUsersFromRecipients, _ChannelEngine_setupDistributedSubscription, _ChannelEngine_afterDistributedReady, _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);
64
+ _ChannelEngine_closed.set(this, false);
65
+ _ChannelEngine_distributedReady.set(this, null);
52
66
  __classPrivateFieldSet(this, _ChannelEngine_name, name, "f");
53
67
  __classPrivateFieldSet(this, _ChannelEngine_backend, backend, "f");
68
+ __classPrivateFieldSet(this, _ChannelEngine_nodeId, (0, pondsocket_common_1.uuid)(), "f");
54
69
  __classPrivateFieldSet(this, _ChannelEngine_endpointId, parent.parent.path, "f");
55
70
  if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
56
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupDistributedSubscription).call(this);
71
+ __classPrivateFieldSet(this, _ChannelEngine_distributedReady, __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupDistributedSubscription).call(this).catch(() => { }), "f");
72
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupHeartbeatTracking).call(this);
57
73
  }
58
74
  }
59
75
  get name() {
@@ -62,13 +78,6 @@ class ChannelEngine {
62
78
  get users() {
63
79
  return new Set(__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").keys());
64
80
  }
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
81
  addUser(userId, assigns, onMessage) {
73
82
  if (this.users.has(userId)) {
74
83
  const message = `ChannelEngine: User with id ${userId} already exists in channel ${this.name}`;
@@ -85,23 +94,18 @@ class ChannelEngine {
85
94
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(userId, assigns);
86
95
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_buildSubscriber).call(this, userId, onMessage);
87
96
  if (isFirstUser && __classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
88
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_requestChannelState).call(this);
89
- }
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
- });
97
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_afterDistributedReady).call(this, () => __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_requestChannelState).call(this));
99
98
  }
99
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
100
+ type: types_1.DistributedMessageType.USER_JOINED,
101
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
102
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
103
+ userId,
104
+ presence: {},
105
+ assigns,
106
+ });
100
107
  return () => this.removeUser(userId);
101
108
  }
102
- /**
103
- * Sends a message to recipients
104
- */
105
109
  sendMessage(sender, recipient, action, event, payload, requestId = (0, pondsocket_common_1.uuid)()) {
106
110
  if (!this.users.has(sender) && sender !== pondsocket_common_1.SystemSender.CHANNEL) {
107
111
  const message = `ChannelEngine: User with id ${sender} does not exist in channel ${this.name}`;
@@ -117,24 +121,17 @@ class ChannelEngine {
117
121
  const recipients = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getUsersFromRecipients).call(this, recipient, sender);
118
122
  const internalEvent = Object.assign(Object.assign({}, channelEvent), { recipients });
119
123
  __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
- }
124
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
125
+ type: types_1.DistributedMessageType.USER_MESSAGE,
126
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
127
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
128
+ fromUserId: sender,
129
+ event,
130
+ payload,
131
+ requestId,
132
+ recipientDescriptor: recipient,
133
+ });
132
134
  }
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
135
  broadcastMessage(userId, message) {
139
136
  if (!this.users.has(userId)) {
140
137
  const messageText = `ChannelEngine: User with id ${userId} does not exist in channel ${this.name}`;
@@ -148,80 +145,50 @@ class ChannelEngine {
148
145
  }, message.requestId);
149
146
  });
150
147
  }
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
148
  trackPresence(userId, presence) {
157
149
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
158
150
  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
- }
151
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
152
+ type: types_1.DistributedMessageType.PRESENCE_UPDATE,
153
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
154
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
155
+ userId,
156
+ presence,
157
+ });
168
158
  }
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
159
  updatePresence(userId, presence) {
175
160
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
176
161
  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
- }
162
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
163
+ type: types_1.DistributedMessageType.PRESENCE_UPDATE,
164
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
165
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
166
+ userId,
167
+ presence,
168
+ });
186
169
  }
187
- /**
188
- * Removes a user's presence
189
- * @param userId - The ID of the user to remove presence for
190
- */
191
170
  removePresence(userId) {
192
171
  if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
193
172
  __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
173
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
214
- type: types_1.DistributedMessageType.PRESENCE_UPDATE,
174
+ type: types_1.DistributedMessageType.PRESENCE_REMOVED,
215
175
  endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
216
176
  channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
217
177
  userId,
218
- presence,
219
178
  });
220
179
  }
221
180
  }
222
- /**
223
- * Updates a user's assigns
224
- */
181
+ upsertPresence(userId, presence) {
182
+ const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
183
+ presenceEngine.upsertPresence(userId, presence);
184
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
185
+ type: types_1.DistributedMessageType.PRESENCE_UPDATE,
186
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
187
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
188
+ userId,
189
+ presence,
190
+ });
191
+ }
225
192
  updateAssigns(userId, assigns) {
226
193
  if (!__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").has(userId)) {
227
194
  throw new httpError_1.HttpError(404, `User with id ${userId} does not exist in channel ${this.name}`);
@@ -229,92 +196,58 @@ class ChannelEngine {
229
196
  const currentAssigns = __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").get(userId) || {};
230
197
  const newAssigns = Object.assign(Object.assign({}, currentAssigns), assigns);
231
198
  __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
- }
199
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
200
+ type: types_1.DistributedMessageType.ASSIGNS_UPDATE,
201
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
202
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
203
+ userId,
204
+ assigns: newAssigns,
205
+ });
242
206
  }
243
- /**
244
- * Kicks a user from the channel
245
- */
246
207
  kickUser(userId, reason) {
247
208
  this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, [userId], pondsocket_common_1.ServerActions.SYSTEM, 'kicked_out', {
248
209
  message: reason,
249
210
  code: 403,
250
211
  });
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
212
  this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.SYSTEM, 'kicked', {
263
213
  userId,
264
214
  reason,
265
215
  });
216
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
217
+ type: types_1.DistributedMessageType.EVICT_USER,
218
+ endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
219
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
220
+ userId,
221
+ reason,
222
+ });
223
+ this.removeUser(userId, true);
266
224
  }
267
- /**
268
- * Gets all assigns as an object
269
- */
270
225
  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
- */
226
+ return mapToObject(__classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f"));
227
+ }
281
228
  getPresence() {
282
229
  if (!__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
283
230
  return {};
284
231
  }
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
- */
232
+ return mapToObject(__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").getAllPresence());
233
+ }
295
234
  destroy(reason) {
296
235
  this.sendMessage(pondsocket_common_1.SystemSender.CHANNEL, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.ERROR, 'destroyed', {
297
236
  message: reason !== null && reason !== void 0 ? reason : 'Channel has been destroyed',
298
237
  });
299
238
  this.close();
300
239
  }
301
- /**
302
- * Removes a user from the channel
303
- */
304
- removeUser(userId) {
240
+ removeUser(userId, skipDistributedBroadcast = false) {
305
241
  try {
306
242
  const userData = this.getUserData(userId);
307
243
  const unsubscribe = __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").get(userId);
308
244
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(userId);
309
- if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
310
- __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(userId);
311
- }
245
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, userId);
312
246
  if (unsubscribe) {
313
247
  unsubscribe();
314
248
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").delete(userId);
315
249
  }
316
- // Broadcast user left to other nodes
317
- if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
250
+ if (!skipDistributedBroadcast) {
318
251
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
319
252
  type: types_1.DistributedMessageType.USER_LEFT,
320
253
  endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
@@ -332,13 +265,10 @@ class ChannelEngine {
332
265
  this.close();
333
266
  }
334
267
  }
335
- catch (_a) {
336
- // Ignore cleanup errors
268
+ catch (_) {
269
+ void 0;
337
270
  }
338
271
  }
339
- /**
340
- * Gets user data by ID
341
- */
342
272
  getUserData(userId) {
343
273
  var _a;
344
274
  const assigns = __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").get(userId);
@@ -353,10 +283,8 @@ class ChannelEngine {
353
283
  id: userId,
354
284
  };
355
285
  }
356
- /**
357
- * Closes the channel and cleans up resources
358
- */
359
286
  close() {
287
+ __classPrivateFieldSet(this, _ChannelEngine_closed, true, "f");
360
288
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").forEach((unsubscribe) => unsubscribe());
361
289
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").clear();
362
290
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").clear();
@@ -368,12 +296,31 @@ class ChannelEngine {
368
296
  __classPrivateFieldGet(this, _ChannelEngine_distributedSubscription, "f").call(this);
369
297
  __classPrivateFieldSet(this, _ChannelEngine_distributedSubscription, null, "f");
370
298
  }
299
+ if (__classPrivateFieldGet(this, _ChannelEngine_heartbeatSubscription, "f")) {
300
+ __classPrivateFieldGet(this, _ChannelEngine_heartbeatSubscription, "f").call(this);
301
+ __classPrivateFieldSet(this, _ChannelEngine_heartbeatSubscription, null, "f");
302
+ }
303
+ if (__classPrivateFieldGet(this, _ChannelEngine_staleNodeTimer, "f")) {
304
+ clearInterval(__classPrivateFieldGet(this, _ChannelEngine_staleNodeTimer, "f"));
305
+ __classPrivateFieldSet(this, _ChannelEngine_staleNodeTimer, null, "f");
306
+ }
307
+ __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").clear();
308
+ __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").clear();
371
309
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").close();
372
310
  this.parent.deleteChannel(this.name);
373
311
  }
374
312
  }
375
313
  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) {
314
+ _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_closed = new WeakMap(), _ChannelEngine_distributedReady = new WeakMap(), _ChannelEngine_instances = new WeakSet(), _ChannelEngine_safeRemovePresence = function _ChannelEngine_safeRemovePresence(userId) {
315
+ if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
316
+ try {
317
+ __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(userId);
318
+ }
319
+ catch (_) {
320
+ void 0;
321
+ }
322
+ }
323
+ }, _ChannelEngine_buildSubscriber = function _ChannelEngine_buildSubscriber(userId, onMessage) {
377
324
  const subscription = __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").subscribe((_a) => __awaiter(this, void 0, void 0, function* () {
378
325
  var { recipients } = _a, event = __rest(_a, ["recipients"]);
379
326
  if (recipients.includes(userId)) {
@@ -389,10 +336,9 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
389
336
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").set(userId, subscription);
390
337
  }, _ChannelEngine_getOrCreatePresenceEngine = function _ChannelEngine_getOrCreatePresenceEngine() {
391
338
  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) => {
339
+ __classPrivateFieldSet(this, _ChannelEngine_presenceEngine, new presenceEngine_1.PresenceEngine(this.name, (event) => {
394
340
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(event);
395
- });
341
+ }), "f");
396
342
  }
397
343
  return __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f");
398
344
  }, _ChannelEngine_getUsersFromRecipients = function _ChannelEngine_getUsersFromRecipients(recipients, sender) {
@@ -423,12 +369,29 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
423
369
  }
424
370
  return users;
425
371
  }, _ChannelEngine_setupDistributedSubscription = function _ChannelEngine_setupDistributedSubscription() {
426
- if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
372
+ return __awaiter(this, void 0, void 0, function* () {
373
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
374
+ return;
375
+ }
376
+ const unsubscribe = yield __classPrivateFieldGet(this, _ChannelEngine_backend, "f").subscribeToChannel(__classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"), __classPrivateFieldGet(this, _ChannelEngine_name, "f"), (message) => {
377
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleDistributedMessage).call(this, message);
378
+ });
379
+ if (__classPrivateFieldGet(this, _ChannelEngine_closed, "f")) {
380
+ unsubscribe();
381
+ return;
382
+ }
383
+ __classPrivateFieldSet(this, _ChannelEngine_distributedSubscription, unsubscribe, "f");
384
+ });
385
+ }, _ChannelEngine_afterDistributedReady = function _ChannelEngine_afterDistributedReady(callback) {
386
+ if (!__classPrivateFieldGet(this, _ChannelEngine_distributedReady, "f")) {
387
+ callback();
427
388
  return;
428
389
  }
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");
390
+ __classPrivateFieldGet(this, _ChannelEngine_distributedReady, "f").then(() => {
391
+ if (!__classPrivateFieldGet(this, _ChannelEngine_closed, "f")) {
392
+ return callback();
393
+ }
394
+ }).catch(() => { });
432
395
  }, _ChannelEngine_handleDistributedMessage = function _ChannelEngine_handleDistributedMessage(message) {
433
396
  switch (message.type) {
434
397
  case types_1.DistributedMessageType.STATE_REQUEST:
@@ -455,24 +418,20 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
455
418
  case types_1.DistributedMessageType.ASSIGNS_UPDATE:
456
419
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteAssignsUpdate).call(this, message);
457
420
  break;
458
- case types_1.DistributedMessageType.ASSIGNS_REMOVED:
459
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteAssignsRemoved).call(this, message);
460
- break;
461
421
  case types_1.DistributedMessageType.EVICT_USER:
462
422
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleRemoteEvictUser).call(this, message);
463
423
  break;
424
+ case types_1.DistributedMessageType.NODE_HEARTBEAT:
425
+ break;
464
426
  default:
465
427
  break;
466
428
  }
467
429
  }, _ChannelEngine_requestChannelState = function _ChannelEngine_requestChannelState() {
468
- if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
469
- return;
470
- }
471
430
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
472
431
  type: types_1.DistributedMessageType.STATE_REQUEST,
473
432
  endpointName: __classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"),
474
433
  channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
475
- fromNode: 'current-node',
434
+ fromNode: __classPrivateFieldGet(this, _ChannelEngine_nodeId, "f"),
476
435
  });
477
436
  }, _ChannelEngine_handleStateRequest = function _ChannelEngine_handleStateRequest(_message) {
478
437
  if (this.users.size === 0) {
@@ -499,9 +458,10 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
499
458
  message.users.forEach((user) => {
500
459
  if (!this.users.has(user.id)) {
501
460
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(user.id, user.assigns);
461
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_trackNodeUser).call(this, message.sourceNodeId, user.id);
502
462
  if (user.presence && Object.keys(user.presence).length > 0) {
503
463
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
504
- presenceEngine.trackPresence(user.id, user.presence);
464
+ presenceEngine.upsertPresence(user.id, user.presence);
505
465
  }
506
466
  }
507
467
  });
@@ -510,56 +470,147 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
510
470
  return;
511
471
  }
512
472
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, message.assigns);
473
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_trackNodeUser).call(this, message.sourceNodeId, message.userId);
513
474
  if (message.presence && Object.keys(message.presence).length > 0) {
514
475
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
515
476
  presenceEngine.trackPresence(message.userId, message.presence);
516
477
  }
517
478
  }, _ChannelEngine_handleRemoteUserLeft = function _ChannelEngine_handleRemoteUserLeft(message) {
518
479
  __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
- }
480
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_untrackNodeUser).call(this, message.sourceNodeId, message.userId);
481
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, message.userId);
522
482
  }, _ChannelEngine_handleRemoteMessage = function _ChannelEngine_handleRemoteMessage(message) {
483
+ const localUsers = Array.from(this.users);
484
+ let recipients;
485
+ if (message.recipientDescriptor === pondsocket_common_1.ChannelReceiver.ALL_USERS) {
486
+ recipients = localUsers;
487
+ }
488
+ else if (message.recipientDescriptor === pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER) {
489
+ recipients = localUsers.filter((u) => u !== message.fromUserId);
490
+ }
491
+ else if (Array.isArray(message.recipientDescriptor)) {
492
+ recipients = localUsers.filter((u) => message.recipientDescriptor.includes(u));
493
+ }
494
+ else {
495
+ return;
496
+ }
497
+ if (recipients.length === 0) {
498
+ return;
499
+ }
523
500
  const internalEvent = {
524
501
  channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
525
502
  requestId: message.requestId,
526
503
  action: pondsocket_common_1.ServerActions.BROADCAST,
527
504
  event: message.event,
528
505
  payload: message.payload,
529
- recipients: message.recipients,
506
+ recipients,
530
507
  };
531
508
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(internalEvent);
532
509
  }, _ChannelEngine_handleRemotePresenceUpdate = function _ChannelEngine_handleRemotePresenceUpdate(message) {
533
510
  const presenceEngine = __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_getOrCreatePresenceEngine).call(this);
534
511
  presenceEngine.upsertPresence(message.userId, message.presence);
535
512
  }, _ChannelEngine_handleRemotePresenceRemoved = function _ChannelEngine_handleRemotePresenceRemoved(message) {
536
- if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
537
- __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(message.userId);
538
- }
513
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, message.userId);
539
514
  }, _ChannelEngine_handleRemoteAssignsUpdate = function _ChannelEngine_handleRemoteAssignsUpdate(message) {
540
- __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, {});
515
+ const current = __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").get(message.userId);
516
+ if (!current) {
517
+ return;
518
+ }
519
+ if (message.assigns) {
520
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, message.assigns);
521
+ return;
522
+ }
523
+ if (message.key) {
524
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, Object.assign(Object.assign({}, current), { [message.key]: message.value }));
525
+ }
543
526
  }, _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);
527
+ if (__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").has(message.userId)) {
528
+ const kickedOutEvent = {
529
+ channelName: __classPrivateFieldGet(this, _ChannelEngine_name, "f"),
530
+ requestId: (0, pondsocket_common_1.uuid)(),
531
+ action: pondsocket_common_1.ServerActions.SYSTEM,
532
+ event: 'kicked_out',
533
+ payload: {
534
+ message: message.reason,
535
+ code: 403,
536
+ },
537
+ recipients: [message.userId],
538
+ };
539
+ __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(kickedOutEvent);
547
540
  }
541
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(message.userId);
542
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, message.userId);
548
543
  const unsubscribe = __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").get(message.userId);
549
544
  if (unsubscribe) {
550
545
  unsubscribe();
551
546
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").delete(message.userId);
552
547
  }
548
+ }, _ChannelEngine_setupHeartbeatTracking = function _ChannelEngine_setupHeartbeatTracking() {
549
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
550
+ return;
551
+ }
552
+ __classPrivateFieldSet(this, _ChannelEngine_heartbeatSubscription, __classPrivateFieldGet(this, _ChannelEngine_backend, "f").subscribeToHeartbeats((nodeId) => {
553
+ __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").set(nodeId, Date.now());
554
+ }), "f");
555
+ __classPrivateFieldSet(this, _ChannelEngine_staleNodeTimer, setInterval(() => {
556
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_cleanupStaleNodes).call(this);
557
+ }, __classPrivateFieldGet(this, _ChannelEngine_backend, "f").heartbeatTimeoutMs), "f");
558
+ }, _ChannelEngine_cleanupStaleNodes = function _ChannelEngine_cleanupStaleNodes() {
559
+ if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
560
+ return;
561
+ }
562
+ const now = Date.now();
563
+ const timeout = __classPrivateFieldGet(this, _ChannelEngine_backend, "f").heartbeatTimeoutMs;
564
+ for (const [nodeId, lastSeen] of __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f")) {
565
+ if (now - lastSeen >= timeout) {
566
+ const users = __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").get(nodeId);
567
+ if (users) {
568
+ for (const userId of users) {
569
+ __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").delete(userId);
570
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, userId);
571
+ }
572
+ __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").delete(nodeId);
573
+ }
574
+ __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").delete(nodeId);
575
+ }
576
+ }
577
+ }, _ChannelEngine_trackNodeUser = function _ChannelEngine_trackNodeUser(sourceNodeId, userId) {
578
+ if (!sourceNodeId) {
579
+ return;
580
+ }
581
+ let users = __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").get(sourceNodeId);
582
+ if (!users) {
583
+ users = new Set();
584
+ __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").set(sourceNodeId, users);
585
+ }
586
+ users.add(userId);
587
+ if (!__classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").has(sourceNodeId)) {
588
+ __classPrivateFieldGet(this, _ChannelEngine_nodeLastSeen, "f").set(sourceNodeId, Date.now());
589
+ }
590
+ }, _ChannelEngine_untrackNodeUser = function _ChannelEngine_untrackNodeUser(sourceNodeId, userId) {
591
+ if (!sourceNodeId) {
592
+ return;
593
+ }
594
+ const users = __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").get(sourceNodeId);
595
+ if (users) {
596
+ users.delete(userId);
597
+ if (users.size === 0) {
598
+ __classPrivateFieldGet(this, _ChannelEngine_nodeUsers, "f").delete(sourceNodeId);
599
+ }
600
+ }
553
601
  }, _ChannelEngine_broadcastToNodes = function _ChannelEngine_broadcastToNodes(message) {
554
602
  return __awaiter(this, void 0, void 0, function* () {
555
603
  if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
556
604
  return;
557
605
  }
558
606
  try {
607
+ if (__classPrivateFieldGet(this, _ChannelEngine_closed, "f")) {
608
+ return;
609
+ }
559
610
  yield __classPrivateFieldGet(this, _ChannelEngine_backend, "f").broadcast(__classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"), __classPrivateFieldGet(this, _ChannelEngine_name, "f"), message);
560
611
  }
561
- catch (_a) {
562
- // Silently ignore broadcast errors to prevent cascading failures
612
+ catch (_) {
613
+ void 0;
563
614
  }
564
615
  });
565
616
  };