@eleven-am/pondsocket 0.1.55 → 0.1.57

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.
Files changed (71) hide show
  1. package/.eslintrc.json +387 -0
  2. package/dist/LICENSE +674 -0
  3. package/dist/README.md +139 -0
  4. package/{channel → dist/channel}/channel.js +5 -7
  5. package/{lobby → dist/lobby}/joinResponse.js +2 -2
  6. package/dist/package.json +51 -0
  7. package/{presence → dist/presence}/presence.js +16 -18
  8. package/{server → dist/server}/pondSocket.js +1 -1
  9. package/{subjects → dist/subjects}/subject.js +44 -0
  10. package/{types.d.ts → dist/types.d.ts} +0 -5
  11. package/jest.config.js +11 -0
  12. package/package.json +3 -3
  13. package/src/abstracts/abstractRequest.test.ts +49 -0
  14. package/src/abstracts/abstractRequest.ts +56 -0
  15. package/src/abstracts/abstractResponse.ts +26 -0
  16. package/src/abstracts/middleware.test.ts +75 -0
  17. package/src/abstracts/middleware.ts +50 -0
  18. package/src/channel/channel.test.ts +501 -0
  19. package/src/channel/channel.ts +305 -0
  20. package/src/channel/eventRequest.test.ts +37 -0
  21. package/src/channel/eventRequest.ts +27 -0
  22. package/src/channel/eventResponse.test.ts +249 -0
  23. package/src/channel/eventResponse.ts +172 -0
  24. package/src/client/channel.test.ts +799 -0
  25. package/src/client/channel.ts +342 -0
  26. package/src/client.ts +124 -0
  27. package/src/endpoint/endpoint.test.ts +825 -0
  28. package/src/endpoint/endpoint.ts +304 -0
  29. package/src/endpoint/response.ts +106 -0
  30. package/src/enums.ts +52 -0
  31. package/src/errors/pondError.ts +32 -0
  32. package/src/express.ts +58 -0
  33. package/src/index.ts +3 -0
  34. package/src/lobby/JoinRequest.test.ts +48 -0
  35. package/src/lobby/JoinResponse.test.ts +162 -0
  36. package/src/lobby/joinRequest.ts +32 -0
  37. package/src/lobby/joinResponse.ts +146 -0
  38. package/src/lobby/lobby.ts +182 -0
  39. package/src/matcher/matcher.test.ts +103 -0
  40. package/src/matcher/matcher.ts +105 -0
  41. package/src/node.ts +33 -0
  42. package/src/presence/presence.ts +127 -0
  43. package/src/presence/presenceEngine.test.ts +143 -0
  44. package/src/server/pondSocket.ts +153 -0
  45. package/src/subjects/subject.test.ts +163 -0
  46. package/src/subjects/subject.ts +137 -0
  47. package/src/typedefs.d.ts +451 -0
  48. package/src/types.d.ts +89 -0
  49. package/tsconfig.build.json +7 -0
  50. package/tsconfig.json +12 -0
  51. /package/{abstracts → dist/abstracts}/abstractRequest.js +0 -0
  52. /package/{abstracts → dist/abstracts}/abstractResponse.js +0 -0
  53. /package/{abstracts → dist/abstracts}/middleware.js +0 -0
  54. /package/{channel → dist/channel}/eventRequest.js +0 -0
  55. /package/{channel → dist/channel}/eventResponse.js +0 -0
  56. /package/{client → dist/client}/channel.js +0 -0
  57. /package/{client.d.ts → dist/client.d.ts} +0 -0
  58. /package/{client.js → dist/client.js} +0 -0
  59. /package/{endpoint → dist/endpoint}/endpoint.js +0 -0
  60. /package/{endpoint → dist/endpoint}/response.js +0 -0
  61. /package/{enums.js → dist/enums.js} +0 -0
  62. /package/{errors → dist/errors}/pondError.js +0 -0
  63. /package/{express.d.ts → dist/express.d.ts} +0 -0
  64. /package/{express.js → dist/express.js} +0 -0
  65. /package/{index.d.ts → dist/index.d.ts} +0 -0
  66. /package/{index.js → dist/index.js} +0 -0
  67. /package/{lobby → dist/lobby}/joinRequest.js +0 -0
  68. /package/{lobby → dist/lobby}/lobby.js +0 -0
  69. /package/{matcher → dist/matcher}/matcher.js +0 -0
  70. /package/{node.d.ts → dist/node.d.ts} +0 -0
  71. /package/{node.js → dist/node.js} +0 -0
@@ -0,0 +1,304 @@
1
+ import { WebSocket, WebSocketServer } from 'ws';
2
+
3
+ import { Middleware } from '../abstracts/middleware';
4
+ import { ChannelEngine } from '../channel/channel';
5
+ import { ServerActions, SystemSender, ErrorTypes, ClientActions } from '../enums';
6
+ import { EndpointError, PresenceError, ChannelError } from '../errors/pondError';
7
+ import { JoinRequest } from '../lobby/joinRequest';
8
+ import { JoinResponse } from '../lobby/joinResponse';
9
+ import { LobbyEngine, PondChannel } from '../lobby/lobby';
10
+ import { parseAddress } from '../matcher/matcher';
11
+ // eslint-disable-next-line import/no-unresolved
12
+ import { PondAssigns, ClientMessage, PondMessage, ChannelEvent, JoinParams, PondPath } from '../types';
13
+
14
+ type AuthorizationHandler<Event extends string> = (request: JoinRequest<Event>, response: JoinResponse) => void | Promise<void>;
15
+
16
+ export interface SocketCache {
17
+ clientId: string;
18
+ socket: WebSocket;
19
+ assigns: PondAssigns;
20
+ }
21
+
22
+ export interface RequestCache extends SocketCache {
23
+ channelName: string;
24
+ }
25
+
26
+ export class Endpoint {
27
+ readonly #middleware: Middleware<RequestCache, JoinParams>;
28
+
29
+ readonly #server: WebSocketServer;
30
+
31
+ readonly #channels: Set<LobbyEngine>;
32
+
33
+ readonly #sockets: Set<SocketCache>;
34
+
35
+ constructor (server: WebSocketServer) {
36
+ this.#server = server;
37
+ this.#sockets = new Set();
38
+ this.#middleware = new Middleware();
39
+ this.#channels = new Set();
40
+ }
41
+
42
+ /**
43
+ * @desc Adds a new PondChannel to this path on this endpoint
44
+ * @param path - The path to add the channel to
45
+ * @param handler - The handler to use to authenticate the client
46
+ *
47
+ * @example
48
+ * const channel = endpoint.createChannel('/chat', (request, response) => {
49
+ * if (request.user.assigns.admin)
50
+ * response.accept();
51
+ *
52
+ * else
53
+ * response.reject('You are not an admin', 403);
54
+ * });
55
+ */
56
+ public createChannel<Path extends string> (path: PondPath<Path>, handler: AuthorizationHandler<Path>) {
57
+ const pondChannel = new LobbyEngine();
58
+
59
+ this.#middleware.use((user, joinParams, next) => {
60
+ const event = parseAddress(path, user.channelName);
61
+
62
+ if (event) {
63
+ const newChannel = pondChannel.getChannel(user.channelName) || pondChannel.createChannel(user.channelName);
64
+ const request = new JoinRequest(user, joinParams, newChannel);
65
+ const response = new JoinResponse(user, newChannel);
66
+
67
+ if (request._parseQueries(path)) {
68
+ return handler(request as JoinRequest<Path>, response);
69
+ }
70
+ }
71
+
72
+ next();
73
+ });
74
+
75
+ this.#channels.add(pondChannel);
76
+
77
+ return new PondChannel(pondChannel);
78
+ }
79
+
80
+ /**
81
+ * @desc List all clients connected to this endpoint
82
+ */
83
+ public listConnections () {
84
+ return [...this.#sockets].map(({ clientId }) => clientId);
85
+ }
86
+
87
+ /**
88
+ * @desc Gets all clients connected to this endpoint
89
+ */
90
+ public getClients () {
91
+ return [...this.#sockets];
92
+ }
93
+
94
+ /**
95
+ * @desc Broadcasts a message to all clients connected to this endpoint
96
+ * @param event - The event to broadcast
97
+ * @param payload - The payload to broadcast
98
+ */
99
+ public broadcast (event: string, payload: PondMessage) {
100
+ this.#sockets.forEach(({ socket }) => {
101
+ const message: ChannelEvent = {
102
+ event,
103
+ payload,
104
+ action: ServerActions.BROADCAST,
105
+ channelName: SystemSender.ENDPOINT,
106
+ };
107
+
108
+ this.#sendMessage(socket, message);
109
+ });
110
+ }
111
+
112
+ /**
113
+ * @desc Closes specific clients connected to this endpoint
114
+ * @param clientIds - The id for the client / clients to close
115
+ */
116
+ public closeConnection (clientIds: string | string[]) {
117
+ const clients = typeof clientIds === 'string' ? [clientIds] : clientIds;
118
+
119
+ this.getClients()
120
+ .forEach(({ clientId, socket }) => {
121
+ if (clients.includes(clientId)) {
122
+ socket.close();
123
+ }
124
+ });
125
+ }
126
+
127
+ /**
128
+ * @desc Manages a new socket connection
129
+ * @param cache - The socket cache
130
+ */
131
+ public manageSocket (cache: SocketCache) {
132
+ this.#sockets.add(cache);
133
+ const socket = cache.socket;
134
+
135
+ socket.addEventListener('message', (message) => {
136
+ this.#readMessage(cache, message.data as string);
137
+ });
138
+
139
+ socket.addEventListener('close', () => {
140
+ this.#channels
141
+ .forEach((manager) => manager.removeUser(cache.clientId, true));
142
+ });
143
+
144
+ socket.addEventListener('error', () => {
145
+ this.#channels
146
+ .forEach((manager) => manager.removeUser(cache.clientId, true));
147
+ });
148
+ }
149
+
150
+ /**
151
+ * @desc Sends a message to a client
152
+ * @param socket - The socket to send the message to
153
+ * @param message - The message to send
154
+ */
155
+ #sendMessage (socket: WebSocket, message: ChannelEvent) {
156
+ socket.send(JSON.stringify(message));
157
+ }
158
+
159
+ /**
160
+ * @desc Adds a new client to a channel on this endpoint
161
+ * @param channel - The channel to add the client to
162
+ * @param socket - The client to add to the channel
163
+ * @param joinParams - The parameters to pass to the channel
164
+ * @private
165
+ */
166
+ #joinChannel (channel: string, socket: SocketCache, joinParams: Record<string, any>) {
167
+ const cache: RequestCache = {
168
+ ...socket,
169
+ channelName: channel,
170
+ };
171
+
172
+ this.#middleware.run(cache, joinParams, () => {
173
+ throw new EndpointError(`GatewayEngine: Channel ${channel} does not exist`, 404);
174
+ });
175
+ }
176
+
177
+ /**
178
+ * @desc Executes a function on a channel
179
+ * @param channel - The channel to execute the function on
180
+ * @param handler - The function to execute
181
+ * @private
182
+ */
183
+ #execute<ReturnType> (channel: string, handler: ((manager: ChannelEngine) => ReturnType)): ReturnType {
184
+ for (const manager of this.#channels) {
185
+ const isPresent = manager.listChannels()
186
+ .includes(channel);
187
+
188
+ if (isPresent) {
189
+ return manager.execute(channel, handler);
190
+ }
191
+ }
192
+
193
+ throw new Error(`GatewayEngine: Channel ${channel} does not exist`);
194
+ }
195
+
196
+ /**
197
+ * @desc Deals with a message sent from a client
198
+ * @param cache - The socket cache of the client
199
+ * @param message - The message to handle
200
+ */
201
+ #handleMessage (cache: SocketCache, message: ClientMessage) {
202
+ switch (message.action) {
203
+ case ClientActions.JOIN_CHANNEL:
204
+ this.#joinChannel(message.channelName, cache, message.payload);
205
+ break;
206
+
207
+ case ClientActions.LEAVE_CHANNEL:
208
+ this.#execute(message.channelName, (channel) => {
209
+ channel.removeUser(cache.clientId);
210
+ });
211
+ break;
212
+
213
+ case ClientActions.BROADCAST:
214
+ this.#execute(message.channelName, (channel) => {
215
+ channel.broadcastMessage(cache.clientId, message);
216
+ });
217
+ break;
218
+ default:
219
+ throw new Error(`GatewayEngine: Action ${message.action} does not exist`);
220
+ }
221
+ }
222
+
223
+ /**
224
+ * @desc Handles a message sent from a client
225
+ * @param cache - The socket cache of the client
226
+ * @param message - The message to handle
227
+ * @private
228
+ */
229
+ #readMessage (cache: SocketCache, message: string) {
230
+ const errorMessage: ChannelEvent = {
231
+ event: ErrorTypes.INVALID_MESSAGE,
232
+ action: ServerActions.ERROR,
233
+ channelName: SystemSender.ENDPOINT,
234
+ payload: {},
235
+ };
236
+
237
+ try {
238
+ const data = JSON.parse(message) as ClientMessage;
239
+
240
+ if (!data.action) {
241
+ errorMessage.payload = {
242
+ message: 'No action provided',
243
+ };
244
+ } else if (!data.channelName) {
245
+ errorMessage.payload = {
246
+ message: 'No channel name provided',
247
+ };
248
+ } else if (!data.payload) {
249
+ errorMessage.payload = {
250
+ message: 'No payload provided',
251
+ };
252
+ }
253
+
254
+ if (!this.#isObjectEmpty(errorMessage.payload)) {
255
+ this.#sendMessage(cache.socket, errorMessage);
256
+ } else {
257
+ this.#handleMessage(cache, data);
258
+ }
259
+ } catch (e) {
260
+ if (e instanceof SyntaxError) {
261
+ errorMessage.payload = {
262
+ message: 'Invalid JSON',
263
+ };
264
+ } else if (e instanceof Error) {
265
+ errorMessage.event = ErrorTypes.INTERNAL_SERVER_ERROR;
266
+ errorMessage.payload = {
267
+ message: e.message,
268
+ };
269
+ } else if (e instanceof PresenceError) {
270
+ errorMessage.event = ErrorTypes.PRESENCE_ERROR;
271
+ errorMessage.channelName = e.channel;
272
+ errorMessage.payload = {
273
+ message: e.message,
274
+ code: e.code,
275
+ action: e.event,
276
+ };
277
+ } else if (e instanceof ChannelError) {
278
+ errorMessage.event = ErrorTypes.CHANNEL_ERROR;
279
+ errorMessage.channelName = e.channel;
280
+ errorMessage.payload = {
281
+ message: e.message,
282
+ code: e.code,
283
+ };
284
+ } else if (e instanceof EndpointError) {
285
+ errorMessage.event = ErrorTypes.ENDPOINT_ERROR;
286
+ errorMessage.payload = {
287
+ message: e.message,
288
+ code: e.code,
289
+ };
290
+ }
291
+
292
+ this.#sendMessage(cache.socket, errorMessage);
293
+ }
294
+ }
295
+
296
+ /**
297
+ * @desc Checks if an object is empty
298
+ * @param obj - The object to check
299
+ * @private
300
+ */
301
+ #isObjectEmpty (obj: Record<string, any>) {
302
+ return Object.keys(obj).length === 0;
303
+ }
304
+ }
@@ -0,0 +1,106 @@
1
+ import { WebSocket } from 'ws';
2
+
3
+ import { Endpoint, SocketCache } from './endpoint';
4
+ import { PondResponse } from '../abstracts/abstractResponse';
5
+ import { ServerActions, ErrorTypes, SystemSender } from '../enums';
6
+ import { EndpointError } from '../errors/pondError';
7
+ // eslint-disable-next-line import/no-unresolved
8
+ import { PondAssigns, PondMessage } from '../types';
9
+
10
+ export class ConnectionResponse extends PondResponse {
11
+ readonly #webSocket: WebSocket;
12
+
13
+ readonly #engine: Endpoint;
14
+
15
+ readonly #clientId: string;
16
+
17
+ #executed: boolean;
18
+
19
+ constructor (webSocket: WebSocket, engine: Endpoint, clientId: string) {
20
+ super();
21
+ this.#webSocket = webSocket;
22
+ this.#engine = engine;
23
+ this.#clientId = clientId;
24
+ this.#executed = false;
25
+ }
26
+
27
+ /**
28
+ * @desc Accepts the request and optionally assigns data to the client
29
+ * @param assigns - the data to assign to the client
30
+ */
31
+ public accept (assigns?: PondAssigns) {
32
+ this.#performChecks();
33
+ const cache: SocketCache = {
34
+ clientId: this.#clientId,
35
+ socket: this.#webSocket,
36
+ assigns: assigns || {},
37
+ };
38
+
39
+ this.#engine.manageSocket(cache);
40
+ }
41
+
42
+ /**
43
+ * @desc Rejects the request with the given error message
44
+ * @param message - the error message
45
+ * @param errorCode - the error code
46
+ */
47
+ public reject (message?: string, errorCode?: number) {
48
+ this.#performChecks();
49
+ const payload = {
50
+ message: message || 'Unauthorized connection',
51
+ code: errorCode || 401,
52
+ };
53
+
54
+ this.#sendMessage(
55
+ ServerActions.ERROR,
56
+ ErrorTypes.UNAUTHORIZED_CONNECTION,
57
+ payload,
58
+ );
59
+
60
+ this.#webSocket.close();
61
+ }
62
+
63
+ /**
64
+ * @desc Emits a direct message to the client
65
+ * @param event - the event name
66
+ * @param payload - the payload to send
67
+ * @param assigns - the data to assign to the client
68
+ */
69
+ public send (event: string, payload: PondMessage, assigns?: PondAssigns) {
70
+ this.accept(assigns);
71
+ this.#sendMessage(ServerActions.BROADCAST, event, payload);
72
+ }
73
+
74
+ /**
75
+ * @desc Emits a direct message to the client
76
+ * @param action - the action to perform
77
+ * @param event - the event name
78
+ * @param payload - the payload to send
79
+ * @private
80
+ */
81
+ #sendMessage (action: ServerActions, event: string, payload: PondMessage) {
82
+ const message = {
83
+ action,
84
+ event,
85
+ payload,
86
+ channelName: SystemSender.ENDPOINT,
87
+ };
88
+
89
+ this.#webSocket.send(JSON.stringify(message));
90
+ }
91
+
92
+ /**
93
+ * @desc Performs checks to ensure the response has not been executed
94
+ * @private
95
+ */
96
+ #performChecks (): void {
97
+ if (this.#executed) {
98
+ const message = 'Cannot execute response more than once';
99
+ const code = 403;
100
+
101
+ throw new EndpointError(message, code);
102
+ }
103
+
104
+ this.#executed = true;
105
+ }
106
+ }
package/src/enums.ts ADDED
@@ -0,0 +1,52 @@
1
+ export enum PresenceEventTypes {
2
+ JOIN = 'JOIN',
3
+ LEAVE = 'LEAVE',
4
+ UPDATE = 'UPDATE'
5
+ }
6
+
7
+ export enum ServerActions {
8
+ PRESENCE = 'PRESENCE',
9
+ SYSTEM = 'SYSTEM',
10
+ BROADCAST = 'BROADCAST',
11
+ ERROR = 'ERROR',
12
+ }
13
+
14
+ export enum ClientActions {
15
+ JOIN_CHANNEL = 'JOIN_CHANNEL',
16
+ LEAVE_CHANNEL = 'LEAVE_CHANNEL',
17
+ BROADCAST = 'BROADCAST',
18
+ }
19
+
20
+ export enum ChannelState {
21
+ IDLE = 'IDLE',
22
+ JOINING = 'JOINING',
23
+ JOINED = 'JOINED',
24
+ STALLED = 'STALLED',
25
+ CLOSED = 'CLOSED',
26
+ }
27
+
28
+ export enum ErrorTypes {
29
+ UNAUTHORIZED_CONNECTION = 'UNAUTHORIZED_CONNECTION',
30
+ UNAUTHORIZED_JOIN_REQUEST = 'UNAUTHORIZED_JOIN_REQUEST',
31
+ UNAUTHORIZED_BROADCAST = 'UNAUTHORIZED_BROADCAST',
32
+ INVALID_MESSAGE = 'INVALID_MESSAGE',
33
+ HANDLER_NOT_FOUND = 'HANDLER_NOT_FOUND',
34
+ PRESENCE_ERROR = 'PRESENCE_ERROR',
35
+ CHANNEL_ERROR = 'CHANNEL_ERROR',
36
+ ENDPOINT_ERROR = 'ENDPOINT_ERROR',
37
+ INTERNAL_SERVER_ERROR = 'INTERNAL_SERVER_ERROR',
38
+ }
39
+
40
+ export enum SystemSender {
41
+ ENDPOINT = 'ENDPOINT',
42
+ CHANNEL = 'CHANNEL',
43
+ }
44
+
45
+ export enum ChannelReceiver {
46
+ ALL_USERS = 'ALL_USERS',
47
+ ALL_EXCEPT_SENDER = 'ALL_EXCEPT_SENDER',
48
+ }
49
+
50
+ export enum Events {
51
+ ACKNOWLEDGE = 'ACKNOWLEDGE',
52
+ }
@@ -0,0 +1,32 @@
1
+ import { PresenceEventTypes } from '../enums';
2
+
3
+ export class PondError {
4
+ public message: string;
5
+
6
+ public code: number;
7
+
8
+ constructor (message: string, code: number) {
9
+ this.code = code;
10
+ this.message = message;
11
+ }
12
+ }
13
+
14
+ export class EndpointError extends PondError {}
15
+
16
+ export class ChannelError extends EndpointError {
17
+ public channel: string;
18
+
19
+ constructor (message: string, code: number, channel: string) {
20
+ super(message, code);
21
+ this.channel = channel;
22
+ }
23
+ }
24
+
25
+ export class PresenceError extends ChannelError {
26
+ public event: PresenceEventTypes;
27
+
28
+ constructor (message: string, code: number, channel: string, event: PresenceEventTypes) {
29
+ super(message, code, channel);
30
+ this.event = event;
31
+ }
32
+ }
package/src/express.ts ADDED
@@ -0,0 +1,58 @@
1
+ import { createServer } from 'http';
2
+
3
+ // eslint-disable-next-line import/no-unresolved
4
+ import { Express } from 'express';
5
+
6
+ import { Endpoint } from './endpoint/endpoint';
7
+ import { ConnectionResponse } from './endpoint/response';
8
+ import { PondSocket } from './server/pondSocket';
9
+ // eslint-disable-next-line import/no-unresolved
10
+ import { PondPath, IncomingConnection } from './types';
11
+
12
+ declare global {
13
+ // eslint-disable-next-line @typescript-eslint/no-namespace
14
+ namespace Express {
15
+ export interface Application {
16
+ upgrade<Path extends string>(path: PondPath<Path>, handler: (request: IncomingConnection<Path>, response: ConnectionResponse) => void | Promise<void>): Endpoint;
17
+ }
18
+ }
19
+ }
20
+
21
+
22
+ interface PondSocketExpressApp extends Express {
23
+
24
+ /**
25
+ * @desc Accepts a new socket upgrade request on the provided endpoint using the handler function to authenticate the socket
26
+ * @param path - the pattern to accept || can also be a regex
27
+ * @param handler - the handler function to authenticate the socket
28
+ * @example
29
+ * const endpoint = pond.createEndpoint('/api/socket', (req, res) => {
30
+ * const token = req.query.token;
31
+ * if (!token)
32
+ * return res.reject("No token provided");
33
+ * res.accept({
34
+ * assign: {
35
+ * token
36
+ * }
37
+ * });
38
+ * })
39
+ */
40
+ upgrade<Path extends string>(path: PondPath<Path>, handler: (request: IncomingConnection<Path>, response: ConnectionResponse) => void | Promise<void>): Endpoint;
41
+ }
42
+
43
+ /**
44
+ * @desc Creates a pond socket server
45
+ * @param app - The Express app to be used by the server
46
+ * @constructor
47
+ */
48
+ const pondSocket = (app: Express): PondSocketExpressApp => {
49
+ const server = createServer(app);
50
+ const pondSocket = new PondSocket(server);
51
+
52
+ app.upgrade = (path, handler) => pondSocket.createEndpoint(path, handler);
53
+ app.listen = (...args: any[]) => pondSocket.listen(...args);
54
+
55
+ return app as PondSocketExpressApp;
56
+ };
57
+
58
+ export default pondSocket;
package/src/index.ts ADDED
@@ -0,0 +1,3 @@
1
+ import { PondSocket } from './server/pondSocket';
2
+
3
+ export default PondSocket;
@@ -0,0 +1,48 @@
1
+ import { JoinRequest } from './joinRequest';
2
+ import { createChannelEngine } from '../channel/eventResponse.test';
3
+ import { RequestCache } from '../endpoint/endpoint';
4
+
5
+ const createMockSocket = (params = {}) => {
6
+ const channelEngine = createChannelEngine();
7
+
8
+ const socket: RequestCache = {
9
+ clientId: 'sender',
10
+ assigns: { assign: 'assign' },
11
+ channelName: 'channel',
12
+ socket: {
13
+ send: jest.fn(),
14
+ } as any,
15
+ };
16
+
17
+ const request = new JoinRequest(socket, params, channelEngine);
18
+
19
+ return {
20
+ channelEngine,
21
+ socket,
22
+ request,
23
+ };
24
+ };
25
+
26
+ describe('JoinRequest', () => {
27
+ it('should create a new PondChannelResponse', () => {
28
+ const { request } = createMockSocket();
29
+
30
+ expect(request).toBeDefined();
31
+ });
32
+
33
+ it('should return the join params', () => {
34
+ const { request } = createMockSocket({ params: 'params' });
35
+
36
+ expect(request.joinParams).toEqual({ params: 'params' });
37
+ });
38
+
39
+ it('should return the user data', () => {
40
+ const { request } = createMockSocket();
41
+
42
+ expect(request.user).toEqual({
43
+ id: 'sender',
44
+ assigns: { assign: 'assign' },
45
+ presence: {},
46
+ });
47
+ });
48
+ });