@eleven-am/pondsocket 0.1.215 → 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.
@@ -19,12 +19,18 @@ var __classPrivateFieldSet = (this && this.__classPrivateFieldSet) || function (
19
19
  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");
20
20
  return (kind === "a" ? f.call(receiver, value) : f ? f.value = value : state.set(receiver, value)), value;
21
21
  };
22
- var _RedisDistributedBackend_instances, _RedisDistributedBackend_publishClient, _RedisDistributedBackend_subscribeClient, _RedisDistributedBackend_keyPrefix, _RedisDistributedBackend_heartbeatSubject, _RedisDistributedBackend_nodeId, _RedisDistributedBackend_heartbeatIntervalMs, _RedisDistributedBackend_heartbeatTimeoutMs, _RedisDistributedBackend_onError, _RedisDistributedBackend_isConnected, _RedisDistributedBackend_heartbeatTimer, _RedisDistributedBackend_attachEventHandlers, _RedisDistributedBackend_startHeartbeat, _RedisDistributedBackend_publishHeartbeat, _RedisDistributedBackend_handleHeartbeatMessage, _RedisDistributedBackend_isValidMessage, _RedisDistributedBackend_buildKey;
22
+ var _RedisDistributedBackend_instances, _RedisDistributedBackend_publishClient, _RedisDistributedBackend_subscribeClient, _RedisDistributedBackend_keyPrefix, _RedisDistributedBackend_namespace, _RedisDistributedBackend_heartbeatSubject, _RedisDistributedBackend_nodeId, _RedisDistributedBackend_heartbeatIntervalMs, _RedisDistributedBackend_heartbeatTimeoutMs, _RedisDistributedBackend_onError, _RedisDistributedBackend_isConnected, _RedisDistributedBackend_heartbeatTimer, _RedisDistributedBackend_attachEventHandlers, _RedisDistributedBackend_startHeartbeat, _RedisDistributedBackend_publishHeartbeat, _RedisDistributedBackend_handleHeartbeatMessage, _RedisDistributedBackend_isValidMessage, _RedisDistributedBackend_buildKey, _RedisDistributedBackend_normalizeTopicPart;
23
23
  Object.defineProperty(exports, "__esModule", { value: true });
24
24
  exports.RedisDistributedBackend = void 0;
25
25
  const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
26
26
  const redis_1 = require("redis");
27
27
  const types_1 = require("../types");
28
+ const DISTRIBUTED_PROTOCOL = 'pondsocket.distributed';
29
+ const DISTRIBUTED_VERSION = 1;
30
+ function distributedId() {
31
+ return `${Date.now().toString(36)}-${Math.random().toString(36)
32
+ .slice(2)}`;
33
+ }
28
34
  class RedisDistributedBackend {
29
35
  get nodeId() {
30
36
  return __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f");
@@ -37,6 +43,7 @@ class RedisDistributedBackend {
37
43
  _RedisDistributedBackend_publishClient.set(this, void 0);
38
44
  _RedisDistributedBackend_subscribeClient.set(this, void 0);
39
45
  _RedisDistributedBackend_keyPrefix.set(this, void 0);
46
+ _RedisDistributedBackend_namespace.set(this, void 0);
40
47
  _RedisDistributedBackend_heartbeatSubject.set(this, void 0);
41
48
  _RedisDistributedBackend_nodeId.set(this, void 0);
42
49
  _RedisDistributedBackend_heartbeatIntervalMs.set(this, void 0);
@@ -44,8 +51,9 @@ class RedisDistributedBackend {
44
51
  _RedisDistributedBackend_onError.set(this, void 0);
45
52
  _RedisDistributedBackend_isConnected.set(this, false);
46
53
  _RedisDistributedBackend_heartbeatTimer.set(this, null);
47
- const { host = 'localhost', port = 6379, password, database = 0, url, keyPrefix = 'pondsocket', heartbeatIntervalMs = 30000, heartbeatTimeoutMs = 90000, onError = null, } = options;
54
+ const { host = 'localhost', port = 6379, password, database = 0, url, keyPrefix = 'pondsocket', namespace = 'default', heartbeatIntervalMs = 30000, heartbeatTimeoutMs = 90000, onError = null, } = options;
48
55
  __classPrivateFieldSet(this, _RedisDistributedBackend_keyPrefix, keyPrefix, "f");
56
+ __classPrivateFieldSet(this, _RedisDistributedBackend_namespace, namespace, "f");
49
57
  __classPrivateFieldSet(this, _RedisDistributedBackend_heartbeatIntervalMs, heartbeatIntervalMs, "f");
50
58
  __classPrivateFieldSet(this, _RedisDistributedBackend_heartbeatTimeoutMs, heartbeatTimeoutMs, "f");
51
59
  __classPrivateFieldSet(this, _RedisDistributedBackend_onError, onError, "f");
@@ -80,7 +88,7 @@ class RedisDistributedBackend {
80
88
  if (!__classPrivateFieldGet(this, _RedisDistributedBackend_instances, "m", _RedisDistributedBackend_isValidMessage).call(this, parsedMessage)) {
81
89
  return;
82
90
  }
83
- if (parsedMessage.nodeId !== __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f")) {
91
+ if (parsedMessage.sourceNodeId !== __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f")) {
84
92
  handler(parsedMessage);
85
93
  }
86
94
  }
@@ -99,11 +107,13 @@ class RedisDistributedBackend {
99
107
  }
100
108
  broadcast(endpointName, channelName, message) {
101
109
  return __awaiter(this, void 0, void 0, function* () {
110
+ var _a, _b;
102
111
  if (!__classPrivateFieldGet(this, _RedisDistributedBackend_isConnected, "f")) {
103
112
  throw new Error('Redis backend is not connected');
104
113
  }
105
114
  const key = __classPrivateFieldGet(this, _RedisDistributedBackend_instances, "m", _RedisDistributedBackend_buildKey).call(this, endpointName, channelName);
106
- const distributedMessage = Object.assign(Object.assign({}, message), { nodeId: __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f"), sourceNodeId: __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f") });
115
+ const normalizedEndpointName = __classPrivateFieldGet(this, _RedisDistributedBackend_instances, "m", _RedisDistributedBackend_normalizeTopicPart).call(this, endpointName);
116
+ const distributedMessage = Object.assign(Object.assign({}, message), { protocol: DISTRIBUTED_PROTOCOL, version: DISTRIBUTED_VERSION, messageId: (_a = message.messageId) !== null && _a !== void 0 ? _a : distributedId(), timestamp: (_b = message.timestamp) !== null && _b !== void 0 ? _b : Date.now(), sourceNodeId: __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f"), endpointName: normalizedEndpointName });
107
117
  const serializedMessage = JSON.stringify(distributedMessage);
108
118
  yield __classPrivateFieldGet(this, _RedisDistributedBackend_publishClient, "f").publish(key, serializedMessage);
109
119
  });
@@ -140,7 +150,7 @@ class RedisDistributedBackend {
140
150
  }
141
151
  }
142
152
  exports.RedisDistributedBackend = RedisDistributedBackend;
143
- _RedisDistributedBackend_publishClient = new WeakMap(), _RedisDistributedBackend_subscribeClient = new WeakMap(), _RedisDistributedBackend_keyPrefix = new WeakMap(), _RedisDistributedBackend_heartbeatSubject = new WeakMap(), _RedisDistributedBackend_nodeId = new WeakMap(), _RedisDistributedBackend_heartbeatIntervalMs = new WeakMap(), _RedisDistributedBackend_heartbeatTimeoutMs = new WeakMap(), _RedisDistributedBackend_onError = new WeakMap(), _RedisDistributedBackend_isConnected = new WeakMap(), _RedisDistributedBackend_heartbeatTimer = new WeakMap(), _RedisDistributedBackend_instances = new WeakSet(), _RedisDistributedBackend_attachEventHandlers = function _RedisDistributedBackend_attachEventHandlers(client, label) {
153
+ _RedisDistributedBackend_publishClient = new WeakMap(), _RedisDistributedBackend_subscribeClient = new WeakMap(), _RedisDistributedBackend_keyPrefix = new WeakMap(), _RedisDistributedBackend_namespace = new WeakMap(), _RedisDistributedBackend_heartbeatSubject = new WeakMap(), _RedisDistributedBackend_nodeId = new WeakMap(), _RedisDistributedBackend_heartbeatIntervalMs = new WeakMap(), _RedisDistributedBackend_heartbeatTimeoutMs = new WeakMap(), _RedisDistributedBackend_onError = new WeakMap(), _RedisDistributedBackend_isConnected = new WeakMap(), _RedisDistributedBackend_heartbeatTimer = new WeakMap(), _RedisDistributedBackend_instances = new WeakSet(), _RedisDistributedBackend_attachEventHandlers = function _RedisDistributedBackend_attachEventHandlers(client, label) {
144
154
  client.on('error', (err) => {
145
155
  __classPrivateFieldSet(this, _RedisDistributedBackend_isConnected, false, "f");
146
156
  if (__classPrivateFieldGet(this, _RedisDistributedBackend_onError, "f")) {
@@ -165,9 +175,14 @@ _RedisDistributedBackend_publishClient = new WeakMap(), _RedisDistributedBackend
165
175
  return;
166
176
  }
167
177
  const heartbeatMessage = {
178
+ protocol: DISTRIBUTED_PROTOCOL,
179
+ version: DISTRIBUTED_VERSION,
168
180
  type: types_1.DistributedMessageType.NODE_HEARTBEAT,
181
+ messageId: distributedId(),
169
182
  endpointName: '__heartbeat__',
170
183
  channelName: '__heartbeat__',
184
+ sourceNodeId: __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f"),
185
+ timestamp: Date.now(),
171
186
  nodeId: __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f"),
172
187
  };
173
188
  const key = __classPrivateFieldGet(this, _RedisDistributedBackend_instances, "m", _RedisDistributedBackend_buildKey).call(this, '__heartbeat__', '__heartbeat__');
@@ -179,8 +194,8 @@ _RedisDistributedBackend_publishClient = new WeakMap(), _RedisDistributedBackend
179
194
  }, _RedisDistributedBackend_handleHeartbeatMessage = function _RedisDistributedBackend_handleHeartbeatMessage(message) {
180
195
  try {
181
196
  const parsedMessage = JSON.parse(message);
182
- if (parsedMessage.type === types_1.DistributedMessageType.NODE_HEARTBEAT && parsedMessage.nodeId !== __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f")) {
183
- __classPrivateFieldGet(this, _RedisDistributedBackend_heartbeatSubject, "f").publish(parsedMessage.nodeId);
197
+ if (__classPrivateFieldGet(this, _RedisDistributedBackend_instances, "m", _RedisDistributedBackend_isValidMessage).call(this, parsedMessage) && parsedMessage.type === types_1.DistributedMessageType.NODE_HEARTBEAT && parsedMessage.sourceNodeId && parsedMessage.sourceNodeId !== __classPrivateFieldGet(this, _RedisDistributedBackend_nodeId, "f")) {
198
+ __classPrivateFieldGet(this, _RedisDistributedBackend_heartbeatSubject, "f").publish(parsedMessage.sourceNodeId);
184
199
  }
185
200
  }
186
201
  catch (_) {
@@ -188,10 +203,20 @@ _RedisDistributedBackend_publishClient = new WeakMap(), _RedisDistributedBackend
188
203
  }
189
204
  }, _RedisDistributedBackend_isValidMessage = function _RedisDistributedBackend_isValidMessage(message) {
190
205
  return (typeof message === 'object' &&
206
+ message.protocol === DISTRIBUTED_PROTOCOL &&
207
+ message.version === DISTRIBUTED_VERSION &&
191
208
  typeof message.type === 'string' &&
192
209
  Object.values(types_1.DistributedMessageType).includes(message.type) &&
210
+ typeof message.messageId === 'string' &&
193
211
  typeof message.endpointName === 'string' &&
194
- typeof message.channelName === 'string');
212
+ typeof message.channelName === 'string' &&
213
+ typeof message.sourceNodeId === 'string' &&
214
+ typeof message.timestamp === 'number');
195
215
  }, _RedisDistributedBackend_buildKey = function _RedisDistributedBackend_buildKey(endpointName, channelName) {
196
- return `${__classPrivateFieldGet(this, _RedisDistributedBackend_keyPrefix, "f")}:${endpointName}:${channelName}`;
216
+ if (endpointName === '__heartbeat__' && channelName === '__heartbeat__') {
217
+ return `${__classPrivateFieldGet(this, _RedisDistributedBackend_keyPrefix, "f")}:v1:${__classPrivateFieldGet(this, _RedisDistributedBackend_namespace, "f")}:__heartbeat__`;
218
+ }
219
+ return `${__classPrivateFieldGet(this, _RedisDistributedBackend_keyPrefix, "f")}:v1:${__classPrivateFieldGet(this, _RedisDistributedBackend_namespace, "f")}:${__classPrivateFieldGet(this, _RedisDistributedBackend_instances, "m", _RedisDistributedBackend_normalizeTopicPart).call(this, endpointName)}:${channelName}`;
220
+ }, _RedisDistributedBackend_normalizeTopicPart = function _RedisDistributedBackend_normalizeTopicPart(value) {
221
+ return value.replace(/^\/+/, '');
197
222
  };
@@ -32,6 +32,7 @@ export interface SocketCache {
32
32
  socket: WebSocket;
33
33
  assigns: PondAssigns;
34
34
  subscriptions: Set<Unsubscribe>;
35
+ channelSubscriptions?: Map<string, Unsubscribe>;
35
36
  }
36
37
  export interface ConnectionResponseOptions {
37
38
  engine: EndpointEngine;
@@ -121,6 +121,7 @@ class ConnectionContext {
121
121
  const cache = {
122
122
  clientId: __classPrivateFieldGet(this, _ConnectionContext_clientId, "f"),
123
123
  subscriptions: new Set(),
124
+ channelSubscriptions: new Map(),
124
125
  assigns: __classPrivateFieldGet(this, _ConnectionContext_assigns, "f"),
125
126
  socket: ws,
126
127
  };
@@ -45,10 +45,18 @@ class JoinContext extends baseContext_1.BaseContext {
45
45
  return __classPrivateFieldGet(this, _JoinContext_executed, "f");
46
46
  }
47
47
  accept() {
48
+ var _a;
48
49
  __classPrivateFieldGet(this, _JoinContext_instances, "m", _JoinContext_performChecks).call(this);
49
50
  const onMessage = this.engine.parent.parent.sendMessage.bind(this.engine.parent.parent, __classPrivateFieldGet(this, _JoinContext_user, "f").socket);
50
51
  const subscription = this.engine.addUser(__classPrivateFieldGet(this, _JoinContext_user, "f").clientId, __classPrivateFieldGet(this, _JoinContext_newAssigns, "f"), onMessage);
51
- __classPrivateFieldGet(this, _JoinContext_user, "f").subscriptions.add(subscription);
52
+ const wrappedSubscription = () => {
53
+ var _a;
54
+ subscription();
55
+ __classPrivateFieldGet(this, _JoinContext_user, "f").subscriptions.delete(wrappedSubscription);
56
+ (_a = __classPrivateFieldGet(this, _JoinContext_user, "f").channelSubscriptions) === null || _a === void 0 ? void 0 : _a.delete(this.engine.name);
57
+ };
58
+ __classPrivateFieldGet(this, _JoinContext_user, "f").subscriptions.add(wrappedSubscription);
59
+ (_a = __classPrivateFieldGet(this, _JoinContext_user, "f").channelSubscriptions) === null || _a === void 0 ? void 0 : _a.set(this.engine.name, wrappedSubscription);
52
60
  __classPrivateFieldSet(this, _JoinContext_accepted, true, "f");
53
61
  return this;
54
62
  }
@@ -30,7 +30,7 @@ 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_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;
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");
@@ -61,12 +61,14 @@ class ChannelEngine {
61
61
  _ChannelEngine_nodeUsers.set(this, new Map());
62
62
  _ChannelEngine_staleNodeTimer.set(this, null);
63
63
  _ChannelEngine_name.set(this, void 0);
64
+ _ChannelEngine_closed.set(this, false);
65
+ _ChannelEngine_distributedReady.set(this, null);
64
66
  __classPrivateFieldSet(this, _ChannelEngine_name, name, "f");
65
67
  __classPrivateFieldSet(this, _ChannelEngine_backend, backend, "f");
66
68
  __classPrivateFieldSet(this, _ChannelEngine_nodeId, (0, pondsocket_common_1.uuid)(), "f");
67
69
  __classPrivateFieldSet(this, _ChannelEngine_endpointId, parent.parent.path, "f");
68
70
  if (__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
69
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupDistributedSubscription).call(this).catch(() => { });
71
+ __classPrivateFieldSet(this, _ChannelEngine_distributedReady, __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupDistributedSubscription).call(this).catch(() => { }), "f");
70
72
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_setupHeartbeatTracking).call(this);
71
73
  }
72
74
  }
@@ -92,7 +94,7 @@ class ChannelEngine {
92
94
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(userId, assigns);
93
95
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_buildSubscriber).call(this, userId, onMessage);
94
96
  if (isFirstUser && __classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
95
- __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_requestChannelState).call(this);
97
+ __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_afterDistributedReady).call(this, () => __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_requestChannelState).call(this));
96
98
  }
97
99
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_broadcastToNodes).call(this, {
98
100
  type: types_1.DistributedMessageType.USER_JOINED,
@@ -282,6 +284,7 @@ class ChannelEngine {
282
284
  };
283
285
  }
284
286
  close() {
287
+ __classPrivateFieldSet(this, _ChannelEngine_closed, true, "f");
285
288
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").forEach((unsubscribe) => unsubscribe());
286
289
  __classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").clear();
287
290
  __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").clear();
@@ -308,7 +311,7 @@ class ChannelEngine {
308
311
  }
309
312
  }
310
313
  exports.ChannelEngine = ChannelEngine;
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) {
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) {
312
315
  if (__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) {
313
316
  try {
314
317
  __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").removePresence(userId);
@@ -370,10 +373,25 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
370
373
  if (!__classPrivateFieldGet(this, _ChannelEngine_backend, "f")) {
371
374
  return;
372
375
  }
373
- __classPrivateFieldSet(this, _ChannelEngine_distributedSubscription, yield __classPrivateFieldGet(this, _ChannelEngine_backend, "f").subscribeToChannel(__classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"), __classPrivateFieldGet(this, _ChannelEngine_name, "f"), (message) => {
376
+ const unsubscribe = yield __classPrivateFieldGet(this, _ChannelEngine_backend, "f").subscribeToChannel(__classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"), __classPrivateFieldGet(this, _ChannelEngine_name, "f"), (message) => {
374
377
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_handleDistributedMessage).call(this, message);
375
- }), "f");
378
+ });
379
+ if (__classPrivateFieldGet(this, _ChannelEngine_closed, "f")) {
380
+ unsubscribe();
381
+ return;
382
+ }
383
+ __classPrivateFieldSet(this, _ChannelEngine_distributedSubscription, unsubscribe, "f");
376
384
  });
385
+ }, _ChannelEngine_afterDistributedReady = function _ChannelEngine_afterDistributedReady(callback) {
386
+ if (!__classPrivateFieldGet(this, _ChannelEngine_distributedReady, "f")) {
387
+ callback();
388
+ return;
389
+ }
390
+ __classPrivateFieldGet(this, _ChannelEngine_distributedReady, "f").then(() => {
391
+ if (!__classPrivateFieldGet(this, _ChannelEngine_closed, "f")) {
392
+ return callback();
393
+ }
394
+ }).catch(() => { });
377
395
  }, _ChannelEngine_handleDistributedMessage = function _ChannelEngine_handleDistributedMessage(message) {
378
396
  switch (message.type) {
379
397
  case types_1.DistributedMessageType.STATE_REQUEST:
@@ -494,7 +512,17 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
494
512
  }, _ChannelEngine_handleRemotePresenceRemoved = function _ChannelEngine_handleRemotePresenceRemoved(message) {
495
513
  __classPrivateFieldGet(this, _ChannelEngine_instances, "m", _ChannelEngine_safeRemovePresence).call(this, message.userId);
496
514
  }, _ChannelEngine_handleRemoteAssignsUpdate = function _ChannelEngine_handleRemoteAssignsUpdate(message) {
497
- __classPrivateFieldGet(this, _ChannelEngine_assignsCache, "f").set(message.userId, message.assigns);
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
+ }
498
526
  }, _ChannelEngine_handleRemoteEvictUser = function _ChannelEngine_handleRemoteEvictUser(message) {
499
527
  if (__classPrivateFieldGet(this, _ChannelEngine_userSubscriptions, "f").has(message.userId)) {
500
528
  const kickedOutEvent = {
@@ -576,6 +604,9 @@ _ChannelEngine_endpointId = new WeakMap(), _ChannelEngine_backend = new WeakMap(
576
604
  return;
577
605
  }
578
606
  try {
607
+ if (__classPrivateFieldGet(this, _ChannelEngine_closed, "f")) {
608
+ return;
609
+ }
579
610
  yield __classPrivateFieldGet(this, _ChannelEngine_backend, "f").broadcast(__classPrivateFieldGet(this, _ChannelEngine_endpointId, "f"), __classPrivateFieldGet(this, _ChannelEngine_name, "f"), message);
580
611
  }
581
612
  catch (_) {
@@ -115,11 +115,13 @@ class EndpointEngine {
115
115
  }
116
116
  exports.EndpointEngine = EndpointEngine;
117
117
  _EndpointEngine_sockets = new WeakMap(), _EndpointEngine_backend = new WeakMap(), _EndpointEngine_middleware = new WeakMap(), _EndpointEngine_lobbyEngines = new WeakMap(), _EndpointEngine_maxMessageSize = new WeakMap(), _EndpointEngine_instances = new WeakSet(), _EndpointEngine_handleSocketClose = function _EndpointEngine_handleSocketClose(cache) {
118
+ var _a;
118
119
  try {
119
120
  __classPrivateFieldGet(this, _EndpointEngine_sockets, "f").delete(cache.clientId);
120
121
  cache.subscriptions.forEach((unsubscribe) => unsubscribe());
122
+ (_a = cache.channelSubscriptions) === null || _a === void 0 ? void 0 : _a.clear();
121
123
  }
122
- catch (_a) {
124
+ catch (_b) {
123
125
  void 0;
124
126
  }
125
127
  }, _EndpointEngine_handleMessage = function _EndpointEngine_handleMessage(cache, message) {
@@ -143,6 +145,12 @@ _EndpointEngine_sockets = new WeakMap(), _EndpointEngine_backend = new WeakMap()
143
145
  throw error;
144
146
  });
145
147
  }, _EndpointEngine_leaveChannel = function _EndpointEngine_leaveChannel(channel, socket) {
148
+ var _a;
149
+ const subscription = (_a = socket.channelSubscriptions) === null || _a === void 0 ? void 0 : _a.get(channel);
150
+ if (subscription) {
151
+ subscription();
152
+ return;
153
+ }
146
154
  const engine = __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_retrieveChannel).call(this, channel);
147
155
  engine.removeUser(socket.clientId);
148
156
  }, _EndpointEngine_retrieveChannel = function _EndpointEngine_retrieveChannel(channel) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@eleven-am/pondsocket",
3
- "version": "0.1.215",
3
+ "version": "0.1.216",
4
4
  "description": "PondSocket is a fast simple socket server",
5
5
  "keywords": [
6
6
  "socket",
@@ -43,21 +43,21 @@
43
43
  "url": "git+https://github.com/Eleven-am/pondSocket.git"
44
44
  },
45
45
  "dependencies": {
46
- "@eleven-am/pondsocket-common": "^0.0.37",
47
- "redis": "^5.11.0",
48
- "ws": "^8.19.0"
46
+ "@eleven-am/pondsocket-common": "^0.0.38",
47
+ "redis": "^5.12.1",
48
+ "ws": "^8.20.1"
49
49
  },
50
50
  "devDependencies": {
51
51
  "@eslint/compat": "^1.4.0",
52
52
  "@eslint/eslintrc": "^3.3.4",
53
- "@eslint/js": "^9.38.0",
53
+ "@eslint/js": "^9.39.4",
54
54
  "@stylistic/eslint-plugin-ts": "^4.4.1",
55
55
  "@types/jest": "^30.0.0",
56
56
  "@types/node": "^25.3.3",
57
57
  "@types/ws": "^8.18.1",
58
- "@typescript-eslint/eslint-plugin": "^8.56.1",
59
- "@typescript-eslint/parser": "^8.56.1",
60
- "eslint": "^9.38.0",
58
+ "@typescript-eslint/eslint-plugin": "^8.59.4",
59
+ "@typescript-eslint/parser": "^8.59.4",
60
+ "eslint": "^9.39.4",
61
61
  "eslint-config-prettier": "^10.1.8",
62
62
  "eslint-import-resolver-node": "^0.3.9",
63
63
  "eslint-plugin-file-progress": "^3.0.2",
@@ -65,11 +65,11 @@
65
65
  "eslint-plugin-prettier": "^5.5.5",
66
66
  "globals": "^16.4.0",
67
67
  "jest": "^30.2.0",
68
- "prettier": "^3.8.1",
68
+ "prettier": "^3.8.3",
69
69
  "source-map-support": "^0.5.21",
70
70
  "supertest": "^7.2.2",
71
- "ts-jest": "^29.4.6",
72
- "ts-loader": "^9.5.4",
71
+ "ts-jest": "^29.4.11",
72
+ "ts-loader": "^9.5.7",
73
73
  "ts-node": "^10.9.2",
74
74
  "tsconfig-paths": "^4.2.0",
75
75
  "typescript": "^5.9.3"
package/server/server.js CHANGED
@@ -10,7 +10,7 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
10
10
  if (typeof state === "function" ? receiver !== state || !f : !state.has(receiver)) throw new TypeError("Cannot read private member from an object whose class did not declare it");
11
11
  return kind === "m" ? f : kind === "a" ? f.call(receiver) : f ? f.value : state.get(receiver);
12
12
  };
13
- var _PondSocket_instances, _PondSocket_server, _PondSocket_exclusiveServer, _PondSocket_socketServer, _PondSocket_backend, _PondSocket_middleware, _PondSocket_maxMessageSize, _PondSocket_heartbeatMs, _PondSocket_heartbeatTimer, _PondSocket_handleUpgrade, _PondSocket_manageHeartbeat, _PondSocket_init, _PondSocket_getCookies;
13
+ var _PondSocket_instances, _PondSocket_server, _PondSocket_exclusiveServer, _PondSocket_socketServer, _PondSocket_backend, _PondSocket_middleware, _PondSocket_maxMessageSize, _PondSocket_heartbeatMs, _PondSocket_heartbeatTimer, _PondSocket_backendReady, _PondSocket_handleUpgrade, _PondSocket_manageHeartbeat, _PondSocket_init, _PondSocket_getCookies;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
15
  exports.PondSocket = void 0;
16
16
  const http_1 = require("http");
@@ -32,6 +32,7 @@ class PondSocket {
32
32
  _PondSocket_maxMessageSize.set(this, void 0);
33
33
  _PondSocket_heartbeatMs.set(this, void 0);
34
34
  _PondSocket_heartbeatTimer.set(this, null);
35
+ _PondSocket_backendReady.set(this, null);
35
36
  __classPrivateFieldSet(this, _PondSocket_middleware, new middleware_1.Middleware(), "f");
36
37
  __classPrivateFieldSet(this, _PondSocket_exclusiveServer, exclusiveServer, "f");
37
38
  __classPrivateFieldSet(this, _PondSocket_server, server !== null && server !== void 0 ? server : new http_1.Server(), "f");
@@ -51,6 +52,7 @@ class PondSocket {
51
52
  * Close the server
52
53
  */
53
54
  close(callback, timeout = 5000) {
55
+ var _a, _b;
54
56
  if (__classPrivateFieldGet(this, _PondSocket_heartbeatTimer, "f")) {
55
57
  clearInterval(__classPrivateFieldGet(this, _PondSocket_heartbeatTimer, "f"));
56
58
  __classPrivateFieldSet(this, _PondSocket_heartbeatTimer, null, "f");
@@ -71,6 +73,7 @@ class PondSocket {
71
73
  __classPrivateFieldGet(this, _PondSocket_socketServer, "f").close(() => {
72
74
  clearTimeout(forceClose);
73
75
  });
76
+ __classPrivateFieldSet(this, _PondSocket_backendReady, (_b = (_a = __classPrivateFieldGet(this, _PondSocket_backendReady, "f")) === null || _a === void 0 ? void 0 : _a.catch(() => { }).then(() => { var _a; return (_a = __classPrivateFieldGet(this, _PondSocket_backend, "f")) === null || _a === void 0 ? void 0 : _a.cleanup(); }).catch(() => { })) !== null && _b !== void 0 ? _b : null, "f");
74
77
  return __classPrivateFieldGet(this, _PondSocket_server, "f").close(callback);
75
78
  }
76
79
  /**
@@ -96,7 +99,7 @@ class PondSocket {
96
99
  }
97
100
  }
98
101
  exports.PondSocket = PondSocket;
99
- _PondSocket_server = new WeakMap(), _PondSocket_exclusiveServer = new WeakMap(), _PondSocket_socketServer = new WeakMap(), _PondSocket_backend = new WeakMap(), _PondSocket_middleware = new WeakMap(), _PondSocket_maxMessageSize = new WeakMap(), _PondSocket_heartbeatMs = new WeakMap(), _PondSocket_heartbeatTimer = new WeakMap(), _PondSocket_instances = new WeakSet(), _PondSocket_handleUpgrade = function _PondSocket_handleUpgrade(req, socket, head) {
102
+ _PondSocket_server = new WeakMap(), _PondSocket_exclusiveServer = new WeakMap(), _PondSocket_socketServer = new WeakMap(), _PondSocket_backend = new WeakMap(), _PondSocket_middleware = new WeakMap(), _PondSocket_maxMessageSize = new WeakMap(), _PondSocket_heartbeatMs = new WeakMap(), _PondSocket_heartbeatTimer = new WeakMap(), _PondSocket_backendReady = new WeakMap(), _PondSocket_instances = new WeakSet(), _PondSocket_handleUpgrade = function _PondSocket_handleUpgrade(req, socket, head) {
100
103
  const clientId = (0, pondsocket_common_1.uuid)();
101
104
  const request = {
102
105
  id: clientId,
@@ -137,7 +140,11 @@ _PondSocket_server = new WeakMap(), _PondSocket_exclusiveServer = new WeakMap(),
137
140
  });
138
141
  }, __classPrivateFieldGet(this, _PondSocket_heartbeatMs, "f"));
139
142
  }, _PondSocket_init = function _PondSocket_init() {
143
+ var _a, _b;
140
144
  __classPrivateFieldSet(this, _PondSocket_heartbeatTimer, __classPrivateFieldGet(this, _PondSocket_instances, "m", _PondSocket_manageHeartbeat).call(this), "f");
145
+ __classPrivateFieldSet(this, _PondSocket_backendReady, (_b = (_a = __classPrivateFieldGet(this, _PondSocket_backend, "f")) === null || _a === void 0 ? void 0 : _a.initialize().catch((error) => {
146
+ __classPrivateFieldGet(this, _PondSocket_server, "f").emit('error', error);
147
+ })) !== null && _b !== void 0 ? _b : null, "f");
141
148
  __classPrivateFieldGet(this, _PondSocket_server, "f").on('error', (error) => {
142
149
  if (__classPrivateFieldGet(this, _PondSocket_heartbeatTimer, "f")) {
143
150
  clearInterval(__classPrivateFieldGet(this, _PondSocket_heartbeatTimer, "f"));
package/types.d.ts CHANGED
@@ -8,6 +8,7 @@ export interface RedisDistributedBackendOptions {
8
8
  database?: number;
9
9
  url?: string;
10
10
  keyPrefix?: string;
11
+ namespace?: string;
11
12
  heartbeatIntervalMs?: number;
12
13
  heartbeatTimeoutMs?: number;
13
14
  onError?: (error: Error) => void;
@@ -30,10 +31,16 @@ export declare enum DistributedMessageType {
30
31
  PRESENCE_REMOVED = "PRESENCE_REMOVED",
31
32
  ASSIGNS_UPDATE = "ASSIGNS_UPDATE",
32
33
  EVICT_USER = "EVICT_USER",
34
+ USER_REMOVE = "USER_REMOVE",
35
+ USER_GET_REQUEST = "USER_GET_REQUEST",
36
+ USER_GET_RESPONSE = "USER_GET_RESPONSE",
33
37
  NODE_HEARTBEAT = "NODE_HEARTBEAT"
34
38
  }
35
39
  export interface DistributedMessage {
40
+ protocol?: 'pondsocket.distributed';
41
+ version?: 1;
36
42
  type: DistributedMessageType;
43
+ messageId?: string;
37
44
  endpointName: string;
38
45
  channelName: string;
39
46
  timestamp?: number;
@@ -81,19 +88,38 @@ export interface PresenceRemoved extends DistributedMessage {
81
88
  export interface AssignsUpdate extends DistributedMessage {
82
89
  type: DistributedMessageType.ASSIGNS_UPDATE;
83
90
  userId: string;
84
- assigns: PondAssigns;
91
+ assigns?: PondAssigns;
92
+ key?: string;
93
+ value?: unknown;
85
94
  }
86
95
  export interface EvictUser extends DistributedMessage {
87
96
  type: DistributedMessageType.EVICT_USER;
88
97
  userId: string;
89
98
  reason: string;
90
- fromNode?: string;
99
+ }
100
+ export interface UserRemove extends DistributedMessage {
101
+ type: DistributedMessageType.USER_REMOVE;
102
+ userId: string;
103
+ reason: string;
104
+ }
105
+ export interface UserGetRequest extends DistributedMessage {
106
+ type: DistributedMessageType.USER_GET_REQUEST;
107
+ userId: string;
108
+ lookupRequestId: string;
109
+ }
110
+ export interface UserGetResponse extends DistributedMessage {
111
+ type: DistributedMessageType.USER_GET_RESPONSE;
112
+ lookupRequestId: string;
113
+ userId: string;
114
+ found: boolean;
115
+ assigns?: PondAssigns;
116
+ presence?: PondPresence;
91
117
  }
92
118
  export interface NodeHeartbeat extends DistributedMessage {
93
119
  type: DistributedMessageType.NODE_HEARTBEAT;
94
120
  nodeId: string;
95
121
  }
96
- export type DistributedChannelMessage = StateRequest | StateResponse | UserJoined | UserLeft | UserMessage | PresenceUpdate | PresenceRemoved | AssignsUpdate | EvictUser | NodeHeartbeat;
122
+ export type DistributedChannelMessage = StateRequest | StateResponse | UserJoined | UserLeft | UserMessage | PresenceUpdate | PresenceRemoved | AssignsUpdate | EvictUser | UserRemove | UserGetRequest | UserGetResponse | NodeHeartbeat;
97
123
  export interface IDistributedBackend {
98
124
  readonly nodeId: string;
99
125
  readonly heartbeatTimeoutMs: number;
package/types.js CHANGED
@@ -12,5 +12,8 @@ var DistributedMessageType;
12
12
  DistributedMessageType["PRESENCE_REMOVED"] = "PRESENCE_REMOVED";
13
13
  DistributedMessageType["ASSIGNS_UPDATE"] = "ASSIGNS_UPDATE";
14
14
  DistributedMessageType["EVICT_USER"] = "EVICT_USER";
15
+ DistributedMessageType["USER_REMOVE"] = "USER_REMOVE";
16
+ DistributedMessageType["USER_GET_REQUEST"] = "USER_GET_REQUEST";
17
+ DistributedMessageType["USER_GET_RESPONSE"] = "USER_GET_RESPONSE";
15
18
  DistributedMessageType["NODE_HEARTBEAT"] = "NODE_HEARTBEAT";
16
19
  })(DistributedMessageType || (exports.DistributedMessageType = DistributedMessageType = {}));