@eleven-am/pondsocket 0.1.141 → 0.1.143

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.
@@ -6,8 +6,13 @@ const channel_1 = require("./channel");
6
6
  const channel_test_1 = require("./channel.test");
7
7
  const eventResponse_1 = require("./eventResponse");
8
8
  const createChannelEngine = () => {
9
- const parentEngine = (0, channel_test_1.createParentEngine)();
10
- return new channel_1.ChannelEngine('test', parentEngine);
9
+ const { parentEngine, socket } = (0, channel_test_1.createParentEngine)();
10
+ const channelEngine = new channel_1.ChannelEngine('test', parentEngine);
11
+ return {
12
+ channelEngine,
13
+ parentEngine,
14
+ socket,
15
+ };
11
16
  };
12
17
  exports.createChannelEngine = createChannelEngine;
13
18
  const createChannelEvent = (name) => {
@@ -18,7 +23,6 @@ const createChannelEvent = (name) => {
18
23
  },
19
24
  sender: 'sender',
20
25
  action: pondsocket_common_1.ServerActions.BROADCAST,
21
- recipients: ['recipient'],
22
26
  channelName: name,
23
27
  requestId: 'requestId',
24
28
  };
@@ -26,10 +30,11 @@ const createChannelEvent = (name) => {
26
30
  };
27
31
  exports.createChannelEvent = createChannelEvent;
28
32
  const createChannelResponse = () => {
29
- const channelEngine = (0, exports.createChannelEngine)();
33
+ const { channelEngine, socket } = (0, exports.createChannelEngine)();
30
34
  const event = (0, exports.createChannelEvent)(channelEngine.name);
31
- channelEngine.addUser(event.sender, { assign: 'assign' }, () => { });
32
- channelEngine.addUser(event.recipients[0], { assign: 'assign' }, () => { });
35
+ const unsub = channelEngine.addUser(event.sender, { assign: 'assign' }, () => { });
36
+ socket.subscriptions.set(channelEngine.name, unsub);
37
+ channelEngine.addUser('tester', { assign: 'assign' }, () => { });
33
38
  const response = new eventResponse_1.EventResponse(event, channelEngine);
34
39
  return {
35
40
  channelEngine,
@@ -42,52 +47,36 @@ describe('ChannelResponse', () => {
42
47
  const { response } = createChannelResponse();
43
48
  expect(response).toBeDefined();
44
49
  });
45
- it('should accept the request', () => {
46
- const { response, channelEngine } = createChannelResponse();
47
- jest.spyOn(channelEngine, 'sendMessage');
48
- response.accept();
49
- expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', 'sender', ['recipient'], pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
50
- });
51
- it('should reject the request', () => {
52
- const { response, channelEngine, event } = createChannelResponse();
53
- jest.spyOn(channelEngine, 'sendMessage');
54
- response.reject();
55
- expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', pondsocket_common_1.SystemSender.CHANNEL, [event.sender], pondsocket_common_1.ServerActions.ERROR, pondsocket_common_1.ErrorTypes.UNAUTHORIZED_BROADCAST, {
56
- message: 'Unauthorized request',
57
- code: 403,
58
- });
59
- });
60
50
  it('should send a direct message', () => {
61
51
  const { response, channelEngine, event } = createChannelResponse();
62
52
  jest.spyOn(channelEngine, 'sendMessage');
63
- response.send('event', { payload: 'payload' });
64
- expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', pondsocket_common_1.SystemSender.CHANNEL, [event.sender], pondsocket_common_1.ServerActions.SYSTEM, 'event', { payload: 'payload' });
53
+ response.reply('event', { payload: 'payload' });
54
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith(pondsocket_common_1.SystemSender.CHANNEL, [event.sender], pondsocket_common_1.ServerActions.SYSTEM, 'event', { payload: 'payload' }, 'requestId');
65
55
  });
66
56
  it('should broadcast a message', () => {
67
57
  const { response, channelEngine } = createChannelResponse();
68
58
  jest.spyOn(channelEngine, 'sendMessage');
69
59
  response.broadcast('event', { payload: 'payload' });
70
- expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', 'sender', pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
60
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' }, 'requestId');
71
61
  });
72
62
  it('should broadcastFromUser a message', () => {
73
63
  const { response, channelEngine } = createChannelResponse();
74
64
  jest.spyOn(channelEngine, 'sendMessage');
75
- response.broadcastFromUser('event', { payload: 'payload' });
76
- expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', 'sender', pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER, pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
65
+ response.broadcastFrom('event', { payload: 'payload' });
66
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER, pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' }, 'requestId');
77
67
  });
78
68
  it('should sendToUsers a message', () => {
79
69
  const { response, channelEngine } = createChannelResponse();
80
70
  jest.spyOn(channelEngine, 'sendMessage');
81
- response.sendToUsers('event', { payload: 'payload' }, ['recipient']);
82
- expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', 'sender', ['recipient'], pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
71
+ response.broadcastTo('event', { payload: 'payload' }, 'tester');
72
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', ['tester'], pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' }, 'requestId');
73
+ channelEngine.sendMessage.mockClear();
74
+ response.broadcastTo('event', { payload: 'payload' }, ['tester', 'sender']);
75
+ expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', ['tester', 'sender'], pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' }, 'requestId');
83
76
  });
84
77
  it('should fail to send to non existing users', () => {
85
- const { event, response, channelEngine } = createChannelResponse();
86
- jest.spyOn(channelEngine, 'sendMessage');
87
- event.recipients = ['non_existing_user'];
88
- expect(() => response.accept()).toThrow('ChannelEngine: Invalid recipients non_existing_user some users do not exist in channel test');
89
- expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', event.sender, ['non_existing_user'], pondsocket_common_1.ServerActions.BROADCAST, event.event, event.payload);
90
- expect(() => response.sendToUsers('event', { payload: 'payload' }, ['non_existing_user']))
78
+ const { response } = createChannelResponse();
79
+ expect(() => response.broadcastTo('event', { payload: 'payload' }, ['non_existing_user']))
91
80
  .toThrow('ChannelEngine: Invalid recipients non_existing_user some users do not exist in channel test');
92
81
  });
93
82
  it('should track a trackPresence', () => {
@@ -99,7 +88,7 @@ describe('ChannelResponse', () => {
99
88
  it('should update a users assign data', () => {
100
89
  const { response, channelEngine } = createChannelResponse();
101
90
  jest.spyOn(channelEngine, 'updateAssigns');
102
- response.accept({ assign: 'updated' });
91
+ response.assign({ assign: 'updated' });
103
92
  expect(channelEngine.updateAssigns).toHaveBeenCalledWith('sender', { assign: 'updated' });
104
93
  });
105
94
  it('should evict a user', () => {
@@ -109,7 +98,11 @@ describe('ChannelResponse', () => {
109
98
  expect(channelEngine.kickUser).toHaveBeenCalledWith('sender', 'recipient');
110
99
  });
111
100
  it('should destroy the channel', () => {
112
- const { response, channelEngine } = createChannelResponse();
101
+ const { channelEngine, socket } = (0, exports.createChannelEngine)();
102
+ const event = (0, exports.createChannelEvent)(channelEngine.name);
103
+ const unsub = channelEngine.addUser(event.sender, { assign: 'assign' }, () => { });
104
+ socket.subscriptions.set(channelEngine.name, unsub);
105
+ const response = new eventResponse_1.EventResponse(event, channelEngine);
113
106
  jest.spyOn(channelEngine, 'destroy');
114
107
  response.closeChannel('recipient');
115
108
  expect(channelEngine.destroy).toHaveBeenCalledWith('recipient');
@@ -118,7 +111,6 @@ describe('ChannelResponse', () => {
118
111
  const { response, channelEngine } = createChannelResponse();
119
112
  jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
120
113
  expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
121
- response.accept();
122
114
  response.trackPresence({ status: 'online' });
123
115
  expect(channelEngine.presenceEngine.trackPresence).toHaveBeenCalledWith('sender', { status: 'online' });
124
116
  });
@@ -126,7 +118,6 @@ describe('ChannelResponse', () => {
126
118
  const { response, channelEngine } = createChannelResponse();
127
119
  jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
128
120
  expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
129
- response.accept();
130
121
  response.trackPresence({ status: 'online' });
131
122
  expect(channelEngine.presenceEngine.trackPresence).toHaveBeenCalledWith('sender', { status: 'online' });
132
123
  expect(() => response.trackPresence({ status: 'online' })).toThrow('PresenceEngine: Presence with key sender already exists');
@@ -135,7 +126,6 @@ describe('ChannelResponse', () => {
135
126
  const { response, channelEngine } = createChannelResponse();
136
127
  jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
137
128
  expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
138
- response.accept();
139
129
  expect(() => response.trackPresence({ status: 'online' }, 'non_existent_user'))
140
130
  .toThrow('ChannelEngine: Invalid recipients non_existent_user some users do not exist in channel test');
141
131
  });
@@ -144,7 +134,6 @@ describe('ChannelResponse', () => {
144
134
  const { response, channelEngine } = createChannelResponse();
145
135
  jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
146
136
  expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
147
- response.accept();
148
137
  response.trackPresence({ status: 'online' });
149
138
  expect(channelEngine.presenceEngine.trackPresence).toHaveBeenCalledWith('sender', { status: 'online' });
150
139
  expect((_a = channelEngine.presenceEngine) === null || _a === void 0 ? void 0 : _a.getPresence()).toEqual({
@@ -159,7 +148,6 @@ describe('ChannelResponse', () => {
159
148
  const { response, channelEngine } = createChannelResponse();
160
149
  jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
161
150
  expect(channelEngine.presenceEngine.trackPresence).not.toHaveBeenCalled();
162
- response.accept();
163
151
  response.trackPresence({ status: 'online' });
164
152
  expect(() => response.updatePresence({ status: 'online' }, 'non_existent_user'))
165
153
  .toThrow('PresenceEngine: Presence with key non_existent_user does not exist');
@@ -171,7 +159,7 @@ describe('ChannelResponse', () => {
171
159
  expect((_a = channelEngine.presenceEngine) === null || _a === void 0 ? void 0 : _a.getPresence()).toEqual({
172
160
  sender: { status: 'online' },
173
161
  });
174
- response.unTrackPresence();
162
+ response.removePresence();
175
163
  expect((_b = channelEngine.presenceEngine) === null || _b === void 0 ? void 0 : _b.getPresence()).toEqual({});
176
164
  });
177
165
  it('should throw an error if unTrackPresence is called for a non existing user', () => {
@@ -181,17 +169,7 @@ describe('ChannelResponse', () => {
181
169
  expect((_a = channelEngine.presenceEngine) === null || _a === void 0 ? void 0 : _a.getPresence()).toEqual({
182
170
  sender: { status: 'online' },
183
171
  });
184
- expect(() => response.unTrackPresence('non_existent_user'))
172
+ expect(() => response.removePresence('non_existent_user'))
185
173
  .toThrow('PresenceEngine: Presence with key non_existent_user does not exist');
186
174
  });
187
- it('should throw an error if accept, reject / send is called more than once', () => {
188
- const { response, channelEngine } = createChannelResponse();
189
- jest.spyOn(channelEngine, 'sendMessage');
190
- expect(channelEngine.sendMessage).not.toHaveBeenCalled();
191
- response.accept();
192
- expect(channelEngine.sendMessage).toHaveBeenCalledWith('requestId', 'sender', ['recipient'], pondsocket_common_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
193
- expect(() => response.accept()).toThrow('Event response has already been executed');
194
- expect(() => response.reject()).toThrow('Event response has already been executed');
195
- expect(() => response.send('event', { payload: 'payload' })).toThrow('Event response has already been executed');
196
- });
197
175
  });
@@ -10,9 +10,9 @@ 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 _Endpoint_instances, _Endpoint_middleware, _Endpoint_channels, _Endpoint_sockets, _Endpoint_sendMessage, _Endpoint_joinChannel, _Endpoint_execute, _Endpoint_handleMessage, _Endpoint_readMessage;
13
+ var _EndpointEngine_instances, _EndpointEngine_middleware, _EndpointEngine_channels, _EndpointEngine_sockets, _EndpointEngine_parentEngine, _EndpointEngine_sendMessage, _EndpointEngine_joinChannel, _EndpointEngine_execute, _EndpointEngine_retrieveChannel, _EndpointEngine_handleMessage, _EndpointEngine_readMessage, _EndpointEngine_buildError, _EndpointEngine_leaveChannel, _Endpoint_engine;
14
14
  Object.defineProperty(exports, "__esModule", { value: true });
15
- exports.Endpoint = void 0;
15
+ exports.Endpoint = exports.EndpointEngine = void 0;
16
16
  const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
17
17
  const zod_1 = require("zod");
18
18
  const middleware_1 = require("../abstracts/middleware");
@@ -22,15 +22,20 @@ const joinResponse_1 = require("../lobby/joinResponse");
22
22
  const lobby_1 = require("../lobby/lobby");
23
23
  const matcher_1 = require("../matcher/matcher");
24
24
  const schema_1 = require("../schema");
25
- class Endpoint {
26
- constructor() {
27
- _Endpoint_instances.add(this);
28
- _Endpoint_middleware.set(this, void 0);
29
- _Endpoint_channels.set(this, void 0);
30
- _Endpoint_sockets.set(this, void 0);
31
- __classPrivateFieldSet(this, _Endpoint_sockets, new Set(), "f");
32
- __classPrivateFieldSet(this, _Endpoint_middleware, new middleware_1.Middleware(), "f");
33
- __classPrivateFieldSet(this, _Endpoint_channels, new Set(), "f");
25
+ class EndpointEngine {
26
+ constructor(parent) {
27
+ _EndpointEngine_instances.add(this);
28
+ _EndpointEngine_middleware.set(this, void 0);
29
+ _EndpointEngine_channels.set(this, void 0);
30
+ _EndpointEngine_sockets.set(this, void 0);
31
+ _EndpointEngine_parentEngine.set(this, void 0);
32
+ __classPrivateFieldSet(this, _EndpointEngine_sockets, new Map(), "f");
33
+ __classPrivateFieldSet(this, _EndpointEngine_middleware, new middleware_1.Middleware(), "f");
34
+ __classPrivateFieldSet(this, _EndpointEngine_channels, new Set(), "f");
35
+ __classPrivateFieldSet(this, _EndpointEngine_parentEngine, parent, "f");
36
+ }
37
+ get parent() {
38
+ return __classPrivateFieldGet(this, _EndpointEngine_parentEngine, "f");
34
39
  }
35
40
  /**
36
41
  * @desc Adds a new PondChannel to this path on this endpoint
@@ -47,8 +52,8 @@ class Endpoint {
47
52
  * });
48
53
  */
49
54
  createChannel(path, handler) {
50
- const pondChannel = new lobby_1.LobbyEngine();
51
- __classPrivateFieldGet(this, _Endpoint_middleware, "f").use((user, joinParams, next) => {
55
+ const pondChannel = new lobby_1.LobbyEngine(this);
56
+ __classPrivateFieldGet(this, _EndpointEngine_middleware, "f").use((user, joinParams, next) => {
52
57
  const event = (0, matcher_1.parseAddress)(path, user.channelName);
53
58
  if (event) {
54
59
  const newChannel = pondChannel.getChannel(user.channelName) || pondChannel.createChannel(user.channelName);
@@ -60,20 +65,14 @@ class Endpoint {
60
65
  }
61
66
  next();
62
67
  });
63
- __classPrivateFieldGet(this, _Endpoint_channels, "f").add(pondChannel);
68
+ __classPrivateFieldGet(this, _EndpointEngine_channels, "f").add(pondChannel);
64
69
  return new lobby_1.PondChannel(pondChannel);
65
70
  }
66
- /**
67
- * @desc List all clients connected to this endpoint
68
- */
69
- listConnections() {
70
- return [...__classPrivateFieldGet(this, _Endpoint_sockets, "f")].map(({ clientId }) => clientId);
71
- }
72
71
  /**
73
72
  * @desc Gets all clients connected to this endpoint
74
73
  */
75
74
  getClients() {
76
- return [...__classPrivateFieldGet(this, _Endpoint_sockets, "f")];
75
+ return [...__classPrivateFieldGet(this, _EndpointEngine_sockets, "f").values()];
77
76
  }
78
77
  /**
79
78
  * @desc Broadcasts a message to all clients connected to this endpoint
@@ -81,7 +80,7 @@ class Endpoint {
81
80
  * @param payload - The payload to broadcast
82
81
  */
83
82
  broadcast(event, payload) {
84
- __classPrivateFieldGet(this, _Endpoint_sockets, "f").forEach(({ socket }) => {
83
+ __classPrivateFieldGet(this, _EndpointEngine_sockets, "f").forEach(({ socket }) => {
85
84
  const message = {
86
85
  event,
87
86
  payload,
@@ -89,7 +88,7 @@ class Endpoint {
89
88
  action: pondsocket_common_1.ServerActions.BROADCAST,
90
89
  channelName: pondsocket_common_1.SystemSender.ENDPOINT,
91
90
  };
92
- __classPrivateFieldGet(this, _Endpoint_instances, "m", _Endpoint_sendMessage).call(this, socket, message);
91
+ __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_sendMessage).call(this, socket, message);
93
92
  });
94
93
  }
95
94
  /**
@@ -110,98 +109,88 @@ class Endpoint {
110
109
  * @param cache - The socket cache
111
110
  */
112
111
  manageSocket(cache) {
113
- __classPrivateFieldGet(this, _Endpoint_sockets, "f").add(cache);
112
+ __classPrivateFieldGet(this, _EndpointEngine_sockets, "f").set(cache.clientId, cache);
114
113
  const socket = cache.socket;
115
114
  socket.addEventListener('message', (message) => {
116
- __classPrivateFieldGet(this, _Endpoint_instances, "m", _Endpoint_readMessage).call(this, cache, message.data);
115
+ __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_readMessage).call(this, cache, message.data);
117
116
  });
118
117
  socket.addEventListener('close', () => {
119
- __classPrivateFieldGet(this, _Endpoint_channels, "f")
120
- .forEach((manager) => manager.removeUser(cache.clientId));
118
+ cache.subscriptions.forEach((unsubscribe) => unsubscribe());
121
119
  });
122
120
  socket.addEventListener('error', () => {
123
- __classPrivateFieldGet(this, _Endpoint_channels, "f")
124
- .forEach((manager) => manager.removeUser(cache.clientId));
121
+ cache.subscriptions.forEach((unsubscribe) => unsubscribe());
125
122
  });
126
123
  }
127
124
  /**
128
- * @desc Builds an error message
129
- * @param error - The error to build
125
+ * @desc Retrieves a user from the endpoint
126
+ * @param clientId - The id of the user to retrieve
130
127
  * @private
131
128
  */
132
- buildError(error) {
133
- const event = {
134
- event: pondsocket_common_1.ErrorTypes.INVALID_MESSAGE,
135
- action: pondsocket_common_1.ServerActions.ERROR,
136
- channelName: pondsocket_common_1.SystemSender.ENDPOINT,
137
- requestId: (0, pondsocket_common_1.uuid)(),
138
- payload: {},
139
- };
140
- if (error instanceof SyntaxError) {
141
- event.payload = {
142
- message: 'Invalid JSON',
143
- };
144
- }
145
- else if (error instanceof Error) {
146
- event.event = pondsocket_common_1.ErrorTypes.INTERNAL_SERVER_ERROR;
147
- event.payload = {
148
- message: error.message,
149
- };
129
+ getUser(clientId) {
130
+ const user = __classPrivateFieldGet(this, _EndpointEngine_sockets, "f").get(clientId);
131
+ if (!user) {
132
+ throw new pondError_1.EndpointError(`GatewayEngine: User ${clientId} does not exist`, 404);
150
133
  }
151
- else if (error instanceof pondError_1.PresenceError) {
152
- event.event = pondsocket_common_1.ErrorTypes.PRESENCE_ERROR;
153
- event.channelName = error.channel;
154
- event.payload = {
155
- message: error.message,
156
- code: error.code,
157
- action: error.event,
158
- };
134
+ return user;
135
+ }
136
+ /**
137
+ * @desc Subscribes a user to a channel, Will join the channel if it exists or add to pending subscriptions
138
+ * @param userId - The id of the user to subscribe
139
+ * @param channel - The name of the channel to subscribe to
140
+ */
141
+ subscribeTo(userId, channel) {
142
+ const user = this.getUser(userId);
143
+ const channelEngine = __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_retrieveChannel).call(this, channel);
144
+ if (!channelEngine) {
145
+ user.pendingSubscriptions.add(channel);
159
146
  }
160
- else if (error instanceof pondError_1.ChannelError) {
161
- event.event = pondsocket_common_1.ErrorTypes.CHANNEL_ERROR;
162
- event.channelName = error.channel;
163
- event.payload = {
164
- message: error.message,
165
- code: error.code,
166
- };
147
+ else {
148
+ user.subscriptions.set(channel, channelEngine.addUser(userId, user.assigns, (event) => {
149
+ user.socket.send(JSON.stringify(event));
150
+ }));
167
151
  }
168
- else if (error instanceof pondError_1.EndpointError) {
169
- event.event = pondsocket_common_1.ErrorTypes.ENDPOINT_ERROR;
170
- event.payload = {
171
- message: error.message,
172
- code: error.code,
173
- };
152
+ }
153
+ /**
154
+ * @desc Unsubscribes a user from a channel
155
+ * @param userId - The id of the user to unsubscribe
156
+ * @param channel - The name of the channel to unsubscribe from
157
+ */
158
+ unsubscribeFrom(userId, channel) {
159
+ const user = this.getUser(userId);
160
+ const unsubscribe = user.subscriptions.get(channel);
161
+ if (unsubscribe) {
162
+ unsubscribe();
163
+ user.subscriptions.delete(channel);
174
164
  }
175
- else if (error instanceof zod_1.ZodError) {
176
- const message = error.errors.map((error) => {
177
- if ('expected' in error && 'received' in error) {
178
- return `Expected ${error.path.join('.')} to be ${error.expected}, but received ${error.received}`;
179
- }
180
- return `${error.path.join('.')} ${error.message}`;
181
- }).join(', ');
182
- event.payload = {
183
- message,
184
- code: 400,
185
- };
165
+ else if (user.pendingSubscriptions.has(channel)) {
166
+ user.pendingSubscriptions.delete(channel);
186
167
  }
187
168
  else {
188
- event.payload = {
189
- message: 'Unknown error',
190
- };
169
+ throw new pondError_1.EndpointError(`GatewayEngine: User ${userId} is not subscribed to ${channel}`, 404);
191
170
  }
192
- return event;
171
+ }
172
+ subscribePendingUsers(channel) {
173
+ const users = [...__classPrivateFieldGet(this, _EndpointEngine_sockets, "f").values()]
174
+ .filter(({ pendingSubscriptions }) => pendingSubscriptions.has(channel.name));
175
+ users.forEach(({ clientId, pendingSubscriptions, subscriptions, socket, assigns }) => {
176
+ const unsubscribe = channel.addUser(clientId, assigns, (event) => {
177
+ socket.send(JSON.stringify(event));
178
+ });
179
+ subscriptions.set(channel.name, unsubscribe);
180
+ pendingSubscriptions.delete(channel.name);
181
+ });
193
182
  }
194
183
  }
195
- exports.Endpoint = Endpoint;
196
- _Endpoint_middleware = new WeakMap(), _Endpoint_channels = new WeakMap(), _Endpoint_sockets = new WeakMap(), _Endpoint_instances = new WeakSet(), _Endpoint_sendMessage = function _Endpoint_sendMessage(socket, message) {
184
+ exports.EndpointEngine = EndpointEngine;
185
+ _EndpointEngine_middleware = new WeakMap(), _EndpointEngine_channels = new WeakMap(), _EndpointEngine_sockets = new WeakMap(), _EndpointEngine_parentEngine = new WeakMap(), _EndpointEngine_instances = new WeakSet(), _EndpointEngine_sendMessage = function _EndpointEngine_sendMessage(socket, message) {
197
186
  socket.send(JSON.stringify(message));
198
- }, _Endpoint_joinChannel = function _Endpoint_joinChannel(channel, socket, joinParams, requestId) {
187
+ }, _EndpointEngine_joinChannel = function _EndpointEngine_joinChannel(channel, socket, joinParams, requestId) {
199
188
  const cache = Object.assign(Object.assign({}, socket), { requestId, channelName: channel });
200
- __classPrivateFieldGet(this, _Endpoint_middleware, "f").run(cache, joinParams, () => {
189
+ __classPrivateFieldGet(this, _EndpointEngine_middleware, "f").run(cache, joinParams, () => {
201
190
  throw new pondError_1.EndpointError(`GatewayEngine: Channel ${channel} does not exist`, 404);
202
191
  });
203
- }, _Endpoint_execute = function _Endpoint_execute(channel, handler) {
204
- for (const manager of __classPrivateFieldGet(this, _Endpoint_channels, "f")) {
192
+ }, _EndpointEngine_execute = function _EndpointEngine_execute(channel, handler) {
193
+ for (const manager of __classPrivateFieldGet(this, _EndpointEngine_channels, "f")) {
205
194
  const isPresent = manager.listChannels()
206
195
  .includes(channel);
207
196
  if (isPresent) {
@@ -209,31 +198,124 @@ _Endpoint_middleware = new WeakMap(), _Endpoint_channels = new WeakMap(), _Endpo
209
198
  }
210
199
  }
211
200
  throw new Error(`GatewayEngine: Channel ${channel} does not exist`);
212
- }, _Endpoint_handleMessage = function _Endpoint_handleMessage(cache, message) {
201
+ }, _EndpointEngine_retrieveChannel = function _EndpointEngine_retrieveChannel(channel) {
202
+ for (const manager of __classPrivateFieldGet(this, _EndpointEngine_channels, "f")) {
203
+ const isPresent = manager.listChannels()
204
+ .includes(channel);
205
+ if (isPresent) {
206
+ return manager.getChannel(channel);
207
+ }
208
+ }
209
+ }, _EndpointEngine_handleMessage = function _EndpointEngine_handleMessage(cache, message) {
213
210
  switch (message.action) {
214
211
  case pondsocket_common_1.ClientActions.JOIN_CHANNEL:
215
- __classPrivateFieldGet(this, _Endpoint_instances, "m", _Endpoint_joinChannel).call(this, message.channelName, cache, message.payload, message.requestId);
212
+ __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_joinChannel).call(this, message.channelName, cache, message.payload, message.requestId);
216
213
  break;
217
214
  case pondsocket_common_1.ClientActions.LEAVE_CHANNEL:
218
- __classPrivateFieldGet(this, _Endpoint_instances, "m", _Endpoint_execute).call(this, message.channelName, (channel) => {
219
- channel.removeUser(cache.clientId, true);
220
- });
215
+ __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_leaveChannel).call(this, message.channelName, cache);
221
216
  break;
222
217
  case pondsocket_common_1.ClientActions.BROADCAST:
223
- __classPrivateFieldGet(this, _Endpoint_instances, "m", _Endpoint_execute).call(this, message.channelName, (channel) => {
218
+ __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_execute).call(this, message.channelName, (channel) => {
224
219
  channel.broadcastMessage(cache.clientId, message);
225
220
  });
226
221
  break;
227
222
  default:
228
223
  throw new Error(`GatewayEngine: Action ${message.action} does not exist`);
229
224
  }
230
- }, _Endpoint_readMessage = function _Endpoint_readMessage(cache, message) {
225
+ }, _EndpointEngine_readMessage = function _EndpointEngine_readMessage(cache, message) {
231
226
  try {
232
227
  const data = JSON.parse(message);
233
228
  const result = schema_1.clientMessageSchema.parse(data);
234
- __classPrivateFieldGet(this, _Endpoint_instances, "m", _Endpoint_handleMessage).call(this, cache, result);
229
+ __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_handleMessage).call(this, cache, result);
235
230
  }
236
231
  catch (e) {
237
- __classPrivateFieldGet(this, _Endpoint_instances, "m", _Endpoint_sendMessage).call(this, cache.socket, this.buildError(e));
232
+ __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_sendMessage).call(this, cache.socket, __classPrivateFieldGet(this, _EndpointEngine_instances, "m", _EndpointEngine_buildError).call(this, e));
233
+ }
234
+ }, _EndpointEngine_buildError = function _EndpointEngine_buildError(error) {
235
+ const event = {
236
+ event: pondsocket_common_1.ErrorTypes.INVALID_MESSAGE,
237
+ action: pondsocket_common_1.ServerActions.ERROR,
238
+ channelName: pondsocket_common_1.SystemSender.ENDPOINT,
239
+ requestId: (0, pondsocket_common_1.uuid)(),
240
+ payload: {},
241
+ };
242
+ if (error instanceof SyntaxError) {
243
+ event.payload = {
244
+ message: 'Invalid JSON',
245
+ };
246
+ }
247
+ else if (error instanceof Error) {
248
+ event.event = pondsocket_common_1.ErrorTypes.INTERNAL_SERVER_ERROR;
249
+ event.payload = {
250
+ message: error.message,
251
+ };
252
+ }
253
+ else if (error instanceof pondError_1.PresenceError) {
254
+ event.event = pondsocket_common_1.ErrorTypes.PRESENCE_ERROR;
255
+ event.channelName = error.channel;
256
+ event.payload = {
257
+ message: error.message,
258
+ code: error.code,
259
+ action: error.event,
260
+ };
261
+ }
262
+ else if (error instanceof pondError_1.ChannelError) {
263
+ event.event = pondsocket_common_1.ErrorTypes.CHANNEL_ERROR;
264
+ event.channelName = error.channel;
265
+ event.payload = {
266
+ message: error.message,
267
+ code: error.code,
268
+ };
269
+ }
270
+ else if (error instanceof pondError_1.EndpointError) {
271
+ event.event = pondsocket_common_1.ErrorTypes.ENDPOINT_ERROR;
272
+ event.payload = {
273
+ message: error.message,
274
+ code: error.code,
275
+ };
276
+ }
277
+ else if (error instanceof zod_1.ZodError) {
278
+ const message = error.errors.map((error) => {
279
+ if ('expected' in error && 'received' in error) {
280
+ return `Expected ${error.path.join('.')} to be ${error.expected}, but received ${error.received}`;
281
+ }
282
+ return `${error.path.join('.')} ${error.message}`;
283
+ }).join(', ');
284
+ event.payload = {
285
+ message,
286
+ code: 400,
287
+ };
288
+ }
289
+ else {
290
+ event.payload = {
291
+ message: 'Unknown error',
292
+ };
293
+ }
294
+ return event;
295
+ }, _EndpointEngine_leaveChannel = function _EndpointEngine_leaveChannel(channel, socket) {
296
+ const unsubscribe = socket.subscriptions.get(channel);
297
+ if (!unsubscribe) {
298
+ throw new pondError_1.EndpointError(`GatewayEngine: Channel ${channel} does not exist`, 404);
238
299
  }
300
+ unsubscribe();
239
301
  };
302
+ class Endpoint {
303
+ constructor(engine) {
304
+ _Endpoint_engine.set(this, void 0);
305
+ __classPrivateFieldSet(this, _Endpoint_engine, engine, "f");
306
+ }
307
+ createChannel(path, handler) {
308
+ return __classPrivateFieldGet(this, _Endpoint_engine, "f").createChannel(path, handler);
309
+ }
310
+ broadcast(event, payload) {
311
+ __classPrivateFieldGet(this, _Endpoint_engine, "f").broadcast(event, payload);
312
+ }
313
+ closeConnection(clientIds) {
314
+ __classPrivateFieldGet(this, _Endpoint_engine, "f").closeConnection(clientIds);
315
+ }
316
+ getClients() {
317
+ return __classPrivateFieldGet(this, _Endpoint_engine, "f").getClients().map(({ socket }) => socket);
318
+ }
319
+ }
320
+ exports.Endpoint = Endpoint;
321
+ _Endpoint_engine = new WeakMap();