@eleven-am/pondsocket 0.1.199 → 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.
@@ -0,0 +1,156 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
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
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _JoinContext_instances, _JoinContext_options, _JoinContext_user, _JoinContext_newAssigns, _JoinContext_executed, _JoinContext_accepted, _JoinContext_sendMessage, _JoinContext_directMessage, _JoinContext_performChecks;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.JoinContext = void 0;
16
+ const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
17
+ const baseContext_1 = require("./baseContext");
18
+ const httpError_1 = require("../errors/httpError");
19
+ /**
20
+ * JoinContext combines the functionality of JoinRequest and JoinResponse
21
+ * to provide a unified interface for handling join events in a channel.
22
+ */
23
+ class JoinContext extends baseContext_1.BaseContext {
24
+ constructor(options, engine, user) {
25
+ super(engine, options.params, engine.name, options.joinParams, user.clientId);
26
+ _JoinContext_instances.add(this);
27
+ _JoinContext_options.set(this, void 0);
28
+ _JoinContext_user.set(this, void 0);
29
+ _JoinContext_newAssigns.set(this, void 0);
30
+ _JoinContext_executed.set(this, void 0);
31
+ _JoinContext_accepted.set(this, void 0);
32
+ __classPrivateFieldSet(this, _JoinContext_options, options, "f");
33
+ __classPrivateFieldSet(this, _JoinContext_user, user, "f");
34
+ __classPrivateFieldSet(this, _JoinContext_executed, false, "f");
35
+ __classPrivateFieldSet(this, _JoinContext_accepted, false, "f");
36
+ __classPrivateFieldSet(this, _JoinContext_newAssigns, Object.assign({}, user.assigns), "f");
37
+ }
38
+ /**
39
+ * The user who sent the request
40
+ */
41
+ get user() {
42
+ return {
43
+ id: __classPrivateFieldGet(this, _JoinContext_options, "f").clientId,
44
+ assigns: __classPrivateFieldGet(this, _JoinContext_options, "f").assigns,
45
+ presence: {},
46
+ };
47
+ }
48
+ /**
49
+ * The join parameters
50
+ */
51
+ get joinParams() {
52
+ return __classPrivateFieldGet(this, _JoinContext_options, "f").joinParams;
53
+ }
54
+ /**
55
+ * Whether the request has been handled
56
+ */
57
+ get hasResponded() {
58
+ return __classPrivateFieldGet(this, _JoinContext_executed, "f");
59
+ }
60
+ /**
61
+ * Accepts the join request
62
+ */
63
+ accept() {
64
+ __classPrivateFieldGet(this, _JoinContext_instances, "m", _JoinContext_performChecks).call(this);
65
+ const onMessage = this.engine.parent.parent.sendMessage.bind(this.engine.parent.parent, __classPrivateFieldGet(this, _JoinContext_user, "f").socket);
66
+ const subscription = this.engine.addUser(__classPrivateFieldGet(this, _JoinContext_user, "f").clientId, __classPrivateFieldGet(this, _JoinContext_newAssigns, "f"), onMessage);
67
+ __classPrivateFieldGet(this, _JoinContext_user, "f").subscriptions.add(subscription);
68
+ __classPrivateFieldSet(this, _JoinContext_accepted, true, "f");
69
+ return this;
70
+ }
71
+ /**
72
+ * Declines the join request
73
+ */
74
+ decline(message, errorCode) {
75
+ __classPrivateFieldGet(this, _JoinContext_instances, "m", _JoinContext_performChecks).call(this);
76
+ const errorMessage = {
77
+ event: pondsocket_common_1.ErrorTypes.UNAUTHORIZED_JOIN_REQUEST,
78
+ payload: {
79
+ message: message || 'Unauthorized connection',
80
+ code: errorCode || 401,
81
+ },
82
+ channelName: this.engine.name,
83
+ action: pondsocket_common_1.ServerActions.ERROR,
84
+ requestId: __classPrivateFieldGet(this, _JoinContext_user, "f").requestId,
85
+ };
86
+ __classPrivateFieldGet(this, _JoinContext_instances, "m", _JoinContext_directMessage).call(this, errorMessage);
87
+ return this;
88
+ }
89
+ /**
90
+ * Assigns data to the user
91
+ */
92
+ assign(assigns) {
93
+ if (__classPrivateFieldGet(this, _JoinContext_accepted, "f")) {
94
+ this.engine.updateAssigns(__classPrivateFieldGet(this, _JoinContext_user, "f").clientId, assigns);
95
+ }
96
+ else {
97
+ __classPrivateFieldSet(this, _JoinContext_newAssigns, Object.assign(Object.assign({}, __classPrivateFieldGet(this, _JoinContext_newAssigns, "f")), assigns), "f");
98
+ }
99
+ return this;
100
+ }
101
+ /**
102
+ * Sends a direct reply to the user
103
+ */
104
+ reply(event, payload) {
105
+ const message = {
106
+ action: pondsocket_common_1.ServerActions.SYSTEM,
107
+ channelName: this.engine.name,
108
+ requestId: __classPrivateFieldGet(this, _JoinContext_user, "f").requestId,
109
+ payload,
110
+ event,
111
+ };
112
+ __classPrivateFieldGet(this, _JoinContext_instances, "m", _JoinContext_directMessage).call(this, message);
113
+ return this;
114
+ }
115
+ /**
116
+ * Broadcasts a message to specific users
117
+ */
118
+ broadcastTo(event, payload, userIds) {
119
+ const ids = Array.isArray(userIds) ? userIds : [userIds];
120
+ __classPrivateFieldGet(this, _JoinContext_instances, "m", _JoinContext_sendMessage).call(this, ids, event, payload);
121
+ return this;
122
+ }
123
+ /**
124
+ * Broadcasts a message to all users in the channel
125
+ */
126
+ broadcast(event, payload) {
127
+ __classPrivateFieldGet(this, _JoinContext_instances, "m", _JoinContext_sendMessage).call(this, pondsocket_common_1.ChannelReceiver.ALL_USERS, event, payload);
128
+ return this;
129
+ }
130
+ /**
131
+ * Broadcasts a message to all users except the sender
132
+ */
133
+ broadcastFrom(event, payload) {
134
+ __classPrivateFieldGet(this, _JoinContext_instances, "m", _JoinContext_sendMessage).call(this, pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER, event, payload);
135
+ return this;
136
+ }
137
+ /**
138
+ * Tracks the user's presence
139
+ */
140
+ trackPresence(presence) {
141
+ this.engine.trackPresence(__classPrivateFieldGet(this, _JoinContext_user, "f").clientId, presence);
142
+ return this;
143
+ }
144
+ }
145
+ exports.JoinContext = JoinContext;
146
+ _JoinContext_options = new WeakMap(), _JoinContext_user = new WeakMap(), _JoinContext_newAssigns = new WeakMap(), _JoinContext_executed = new WeakMap(), _JoinContext_accepted = new WeakMap(), _JoinContext_instances = new WeakSet(), _JoinContext_sendMessage = function _JoinContext_sendMessage(recipient, event, payload) {
147
+ this.engine.sendMessage(__classPrivateFieldGet(this, _JoinContext_user, "f").clientId, recipient, pondsocket_common_1.ServerActions.BROADCAST, event, payload, __classPrivateFieldGet(this, _JoinContext_user, "f").requestId);
148
+ }, _JoinContext_directMessage = function _JoinContext_directMessage(event) {
149
+ this.engine.parent.parent.sendMessage(__classPrivateFieldGet(this, _JoinContext_user, "f").socket, event);
150
+ }, _JoinContext_performChecks = function _JoinContext_performChecks() {
151
+ if (__classPrivateFieldGet(this, _JoinContext_executed, "f")) {
152
+ const message = `Request to join channel ${this.engine.name} rejected: Request already executed`;
153
+ throw new httpError_1.HttpError(403, message);
154
+ }
155
+ __classPrivateFieldSet(this, _JoinContext_executed, true, "f");
156
+ };
@@ -0,0 +1,59 @@
1
+ "use strict";
2
+ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (receiver, state, value, kind, f) {
3
+ if (kind === "m") throw new TypeError("Private method is not writable");
4
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a setter");
5
+ if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot write private member to an object whose class did not declare it");
6
+ return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
7
+ };
8
+ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (receiver, state, kind, f) {
9
+ if (kind === "a" && !f) throw new TypeError("Private accessor was defined without a getter");
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
+ return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
+ };
13
+ var _OutgoingContext_payload, _OutgoingContext_isBlocked;
14
+ Object.defineProperty(exports, "__esModule", { value: true });
15
+ exports.OutgoingContext = void 0;
16
+ const baseContext_1 = require("./baseContext");
17
+ class OutgoingContext extends baseContext_1.BaseContext {
18
+ constructor(event, params, engine, userid) {
19
+ super(engine, params, event.event, event.payload, userid);
20
+ _OutgoingContext_payload.set(this, void 0);
21
+ _OutgoingContext_isBlocked.set(this, void 0);
22
+ __classPrivateFieldSet(this, _OutgoingContext_payload, event.payload, "f");
23
+ __classPrivateFieldSet(this, _OutgoingContext_isBlocked, false, "f");
24
+ }
25
+ get payload() {
26
+ return __classPrivateFieldGet(this, _OutgoingContext_payload, "f");
27
+ }
28
+ /**
29
+ * Blocks the outgoing context, preventing further processing of the event.
30
+ */
31
+ block() {
32
+ __classPrivateFieldSet(this, _OutgoingContext_isBlocked, true, "f");
33
+ return this;
34
+ }
35
+ /**
36
+ * Checks if the outgoing context is blocked.
37
+ * @returns {boolean} - True if blocked, false otherwise.
38
+ */
39
+ isBlocked() {
40
+ return __classPrivateFieldGet(this, _OutgoingContext_isBlocked, "f");
41
+ }
42
+ /**
43
+ * Transforms the outgoing context with a new payload.
44
+ * @param payload - The new payload to set for the context.
45
+ */
46
+ transform(payload) {
47
+ __classPrivateFieldSet(this, _OutgoingContext_payload, payload, "f");
48
+ return this;
49
+ }
50
+ /**
51
+ * Updates the parameters of the outgoing context.
52
+ * @param params - The new parameters to set for the context.
53
+ */
54
+ updateParams(params) {
55
+ super.updateParams(params);
56
+ }
57
+ }
58
+ exports.OutgoingContext = OutgoingContext;
59
+ _OutgoingContext_payload = new WeakMap(), _OutgoingContext_isBlocked = new WeakMap();
@@ -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
  }
@@ -248,31 +357,30 @@ class ChannelEngine {
248
357
  * Closes the channel and cleans up resources
249
358
  */
250
359
  close() {
251
- // Clear all user subscriptions
252
360
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").forEach((unsubscribe) => unsubscribe());
253
361
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").clear();
254
- // Clear assigns
255
362
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").clear();
256
- // Close presence engine if exists
257
363
  if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
258
364
  __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").close();
259
365
  __classPrivateFieldSet(this, _ChannelEngine_presenceEngine, null, "f");
260
366
  }
261
- // Close publisher
367
+ if (__classPrivateFieldGet(this, _ChannelEngine_distributedSubscription, "f")) {
368
+ __classPrivateFieldGet(this, _ChannelEngine_distributedSubscription, "f").call(this);
369
+ __classPrivateFieldSet(this, _ChannelEngine_distributedSubscription, null, "f");
370
+ }
262
371
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").close();
263
- // Remove the channel from the parent
264
372
  this.parent.deleteChannel(this.name);
265
373
  }
266
374
  }
267
375
  exports.ChannelEngine = ChannelEngine;
268
- _ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new WeakMap(), _ChannelEngine_userSubscriptions = new WeakMap(), _ChannelEngine_publisher = new WeakMap(), _ChannelEngine_name = new WeakMap(), _ChannelEngine_instances = new WeakSet(), _ChannelEngine_buildSubscriber = function _ChannelEngine_buildSubscriber(userId, onMessage) {
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) {
269
377
  const subscription = __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").subscribe((_a) => __awaiter(this, void 0, void 0, function* () {
270
378
  var { recipients } = _a, event = __rest(_a, ["recipients"]);
271
379
  if (recipients.includes(userId)) {
272
- if (event.action === pondsocket_common_1.ServerActions.SYSTEM) {
380
+ if (event.action === pondsocket_common_1.ServerActions.PRESENCE) {
273
381
  return onMessage(event);
274
382
  }
275
- const newEvent = yield this.parent.manageOutgoingEvents(event, userId, this);
383
+ const newEvent = yield this.parent.processOutgoingEvents(event, this, userId);
276
384
  if (newEvent) {
277
385
  onMessage(newEvent);
278
386
  }
@@ -282,7 +390,6 @@ _ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new
282
390
  }, _ChannelEngine_getOrCreatePresenceEngine = function _ChannelEngine_getOrCreatePresenceEngine() {
283
391
  if (!__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
284
392
  __classPrivateFieldSet(this, _ChannelEngine_presenceEngine, new presenceEngine_1.PresenceEngine(this.name), "f");
285
- // Subscribe to presence events and publish them through the channel publisher
286
393
  __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").subscribe((event) => {
287
394
  __classPrivateFieldGet(this, _ChannelEngine_publisher, "f").publish(event);
288
395
  });
@@ -315,4 +422,163 @@ _ChannelEngine_presenceEngine = new WeakMap(), _ChannelEngine_assignsCache = new
315
422
  break;
316
423
  }
317
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
+ });
318
584
  };