@eleven-am/pondsocket 0.1.0

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 (64) hide show
  1. package/.eslintrc.js +28 -0
  2. package/.idea/modules.xml +8 -0
  3. package/.idea/pondsocket.iml +12 -0
  4. package/LICENSE +674 -0
  5. package/base.d.ts +1 -0
  6. package/base.js +17 -0
  7. package/client.d.ts +1 -0
  8. package/client.js +17 -0
  9. package/index.d.ts +1 -0
  10. package/index.js +17 -0
  11. package/jest.config.js +11 -0
  12. package/package.json +48 -0
  13. package/pondBase/baseClass.d.ts +37 -0
  14. package/pondBase/baseClass.js +111 -0
  15. package/pondBase/baseClass.test.js +73 -0
  16. package/pondBase/enums.d.ts +9 -0
  17. package/pondBase/enums.js +14 -0
  18. package/pondBase/index.d.ts +6 -0
  19. package/pondBase/index.js +22 -0
  20. package/pondBase/pondBase.d.ts +41 -0
  21. package/pondBase/pondBase.js +60 -0
  22. package/pondBase/pondBase.test.js +101 -0
  23. package/pondBase/pubSub.d.ts +73 -0
  24. package/pondBase/pubSub.js +138 -0
  25. package/pondBase/pubSub.test.js +309 -0
  26. package/pondBase/simpleBase.d.ts +131 -0
  27. package/pondBase/simpleBase.js +211 -0
  28. package/pondBase/simpleBase.test.js +153 -0
  29. package/pondBase/types.d.ts +2 -0
  30. package/pondBase/types.js +2 -0
  31. package/pondClient/channel.d.ts +66 -0
  32. package/pondClient/channel.js +152 -0
  33. package/pondClient/index.d.ts +2 -0
  34. package/pondClient/index.js +18 -0
  35. package/pondClient/socket.d.ts +42 -0
  36. package/pondClient/socket.js +116 -0
  37. package/pondSocket/channel.d.ts +134 -0
  38. package/pondSocket/channel.js +287 -0
  39. package/pondSocket/channel.test.js +377 -0
  40. package/pondSocket/channelMiddleWare.d.ts +26 -0
  41. package/pondSocket/channelMiddleWare.js +36 -0
  42. package/pondSocket/endpoint.d.ts +90 -0
  43. package/pondSocket/endpoint.js +323 -0
  44. package/pondSocket/endpoint.test.js +513 -0
  45. package/pondSocket/enums.d.ts +19 -0
  46. package/pondSocket/enums.js +25 -0
  47. package/pondSocket/index.d.ts +7 -0
  48. package/pondSocket/index.js +23 -0
  49. package/pondSocket/pondChannel.d.ts +79 -0
  50. package/pondSocket/pondChannel.js +219 -0
  51. package/pondSocket/pondChannel.test.js +430 -0
  52. package/pondSocket/pondResponse.d.ts +25 -0
  53. package/pondSocket/pondResponse.js +120 -0
  54. package/pondSocket/pondSocket.d.ts +47 -0
  55. package/pondSocket/pondSocket.js +94 -0
  56. package/pondSocket/server.test.js +136 -0
  57. package/pondSocket/socketMiddleWare.d.ts +6 -0
  58. package/pondSocket/socketMiddleWare.js +32 -0
  59. package/pondSocket/types.d.ts +74 -0
  60. package/pondSocket/types.js +2 -0
  61. package/socket.d.ts +1 -0
  62. package/socket.js +17 -0
  63. package/tsconfig.eslint.json +5 -0
  64. package/tsconfig.json +90 -0
@@ -0,0 +1,116 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.PondClient = void 0;
4
+ const webSocket_1 = require("rxjs/webSocket");
5
+ const rxjs_1 = require("rxjs");
6
+ const channel_1 = require("./channel");
7
+ const operators_1 = require("rxjs/operators");
8
+ const pondBase_1 = require("../pondBase");
9
+ class PondClient {
10
+ constructor(endpoint, params) {
11
+ this.socketState = "CLOSED";
12
+ /**
13
+ * @desc A retry strategy for the socket.
14
+ * @param maxTries - The maximum number of retries.
15
+ * @param ms - The number of milliseconds to wait before retrying.
16
+ */
17
+ this._retryStrategy = (maxTries, ms) => {
18
+ return (0, rxjs_1.pipe)((0, operators_1.retryWhen)(attempts => {
19
+ const observableForRetries = (0, rxjs_1.zip)((0, rxjs_1.range)(1, maxTries), attempts)
20
+ .pipe((0, operators_1.map)(([elemFromRange]) => elemFromRange), (0, operators_1.map)(i => i * i), (0, rxjs_1.switchMap)(i => (0, rxjs_1.timer)(i * ms)));
21
+ const observableForFailure = (0, rxjs_1.throwError)(new Error("Could not connect to server"))
22
+ .pipe((0, rxjs_1.materialize)(), (0, rxjs_1.delay)(1000), (0, rxjs_1.dematerialize)());
23
+ return (0, rxjs_1.concat)(observableForRetries, observableForFailure);
24
+ }));
25
+ };
26
+ let address;
27
+ try {
28
+ address = new URL(endpoint);
29
+ }
30
+ catch (e) {
31
+ address = new URL(window.location.toString());
32
+ address.pathname = endpoint;
33
+ }
34
+ const query = new URLSearchParams(params);
35
+ address.search = query.toString();
36
+ const protocol = address.protocol === "https:" ? "wss:" : "ws:";
37
+ if (address.protocol !== "wss:" && address.protocol !== "ws:")
38
+ address.protocol = protocol;
39
+ this.address = address;
40
+ this.channels = new pondBase_1.PondBase();
41
+ }
42
+ /**
43
+ * @desc Connects to the server and returns the socket.
44
+ */
45
+ connect() {
46
+ if (this.socketState !== "CLOSED")
47
+ return;
48
+ this.socketState = "CONNECTING";
49
+ const socket = (0, webSocket_1.webSocket)({
50
+ url: this.address.toString(),
51
+ openObserver: {
52
+ next: () => {
53
+ this.socketState = "OPEN";
54
+ }
55
+ },
56
+ closeObserver: {
57
+ next: () => {
58
+ this.socketState = "CLOSED";
59
+ }
60
+ },
61
+ closingObserver: {
62
+ next: () => {
63
+ this.socketState = "CLOSING";
64
+ }
65
+ }
66
+ });
67
+ this.socket = socket;
68
+ this.subscription = socket.pipe(this._retryStrategy(100, 1000)).subscribe();
69
+ return this;
70
+ }
71
+ /**
72
+ * @desc Returns the current state of the socket.
73
+ */
74
+ getState() {
75
+ return this.socketState;
76
+ }
77
+ /**
78
+ * @desc Creates a channel with the given name and params.
79
+ * @param channel - The name of the channel.
80
+ * @param params - The params to send to the server.
81
+ */
82
+ createChannel(channel, params) {
83
+ const channelDoc = this.channels.get(channel);
84
+ if (channelDoc)
85
+ return channelDoc.doc;
86
+ if (this.socket) {
87
+ const newChannel = new channel_1.Channel(channel, params || {}, this.socket);
88
+ this.channels.set(channel, newChannel);
89
+ return newChannel;
90
+ }
91
+ return null;
92
+ }
93
+ /**
94
+ * @desc An event that is triggered when the socket receives a message.
95
+ * @param callback - The callback to be called when the event is triggered.
96
+ */
97
+ onMessage(callback) {
98
+ var _a;
99
+ (_a = this.socket) === null || _a === void 0 ? void 0 : _a.subscribe(data => {
100
+ if (data.event)
101
+ callback(data.event, data.payload);
102
+ });
103
+ }
104
+ /**
105
+ * @desc Disconnects the socket from the server.
106
+ */
107
+ disconnect() {
108
+ var _a, _b, _c;
109
+ (_a = this.socket) === null || _a === void 0 ? void 0 : _a.complete();
110
+ (_b = this.socket) === null || _b === void 0 ? void 0 : _b.unsubscribe();
111
+ (_c = this.subscription) === null || _c === void 0 ? void 0 : _c.unsubscribe();
112
+ this.socket = undefined;
113
+ this.channels = new pondBase_1.PondBase();
114
+ }
115
+ }
116
+ exports.PondClient = PondClient;
@@ -0,0 +1,134 @@
1
+ import {BaseClass, default_t, PondDocument, Subscription} from "../pondBase";
2
+ import {NewUser, PondAssigns, PondChannelData, PondMessage, PondPresence, ServerMessage} from "./types";
3
+ import {PondSenders, ServerActions} from "./enums";
4
+ import {ChannelHandler} from "./channelMiddleWare";
5
+
6
+ export interface ChannelInfo {
7
+ name: string;
8
+ channelData: PondChannelData;
9
+ presence: PondPresence[];
10
+ assigns: PondPresence[];
11
+ }
12
+
13
+ export interface PondUser {
14
+ presence: PondDocument<PondPresence>;
15
+ assigns: PondDocument<PondAssigns>;
16
+ }
17
+
18
+ export declare class Channel extends BaseClass {
19
+ readonly name: string;
20
+
21
+ /**
22
+ * @desc Returns the channel info
23
+ */
24
+ get info(): Readonly<ChannelInfo>;
25
+
26
+ /**
27
+ * @desc Gets the channel's data
28
+ */
29
+ get data(): Readonly<PondChannelData>;
30
+
31
+ /**
32
+ * @desc Sets the channel's data
33
+ * @param data
34
+ */
35
+ set data(data: PondChannelData);
36
+
37
+ /**
38
+ * @desc Gets the channel's presence
39
+ */
40
+ get presence(): PondPresence[];
41
+
42
+ /**
43
+ * @desc Gets the channel's assigns
44
+ */
45
+ get assigns(): PondAssigns[];
46
+
47
+ /**
48
+ * @desc Gets a user's information
49
+ * @param clientId - The clientId of the user
50
+ */
51
+ getUserInfo(clientId: string): {
52
+ presence: PondPresence;
53
+ assigns: PondAssigns;
54
+ } | null;
55
+
56
+ /**
57
+ * @desc Checks if a user exists in the channel
58
+ * @param clientId - The clientId of the user
59
+ */
60
+ hasUser(clientId: string): boolean;
61
+
62
+ /**
63
+ * @desc Adds a new user to the channel
64
+ * @param user - The user to add to the channel
65
+ */
66
+ addUser(user: NewUser): void;
67
+
68
+ /**
69
+ * @desc Removes a user or group of users from the channel
70
+ * @param clientIds - The clientIds of the users to remove
71
+ */
72
+ removeUser(clientIds: string | string[]): void;
73
+
74
+ /**
75
+ * @desc Subscribes to the presence changes occuring in the channel
76
+ * @param callback - The callback to call when a presence change occurs
77
+ */
78
+ onPresenceChange(callback: (message: ServerMessage) => void): Subscription;
79
+
80
+ /**
81
+ * @desc Subscribes to the message events occuring in the channel
82
+ * @param callback - The callback to call when a message event occurs
83
+ */
84
+ onMessage(callback: ChannelHandler): void;
85
+
86
+ /**
87
+ * @desc Updates the state of a user in the channel
88
+ * @param clientId - The clientId of the user to update
89
+ * @param presence - The new presence of the user
90
+ * @param assigns - The new assigns of the user
91
+ */
92
+ updateUser(clientId: string, presence: PondPresence, assigns: PondAssigns): void;
93
+
94
+ /**
95
+ * @desc Broadcasts a message to all users in the channel
96
+ * @param event - The event name
97
+ * @param message - The message to send
98
+ * @param sender - The sender of the message
99
+ */
100
+ broadcast(event: string, message: PondMessage, sender?: PondSenders | string): void;
101
+
102
+ /**
103
+ * @desc Broadcasts a message to all users in the channel except the sender
104
+ * @param event - The event name
105
+ * @param message - The message to send
106
+ * @param clientId - The client id of the sender
107
+ */
108
+ broadcastFrom(event: string, message: PondMessage, clientId: string): void;
109
+
110
+ /**
111
+ * @desc Sends a message to a specific user or group of users
112
+ * @param event - The event name
113
+ * @param clientId - The client id of the user to send the message to
114
+ * @param message - The message to send
115
+ * @param sender - The client id of the sender
116
+ */
117
+ sendTo(event: string, message: PondMessage, sender: string, clientId: string | string[]): void;
118
+
119
+ /**
120
+ * @desc Subscribes to a channel event, used only by the sockets to subscribe to all events
121
+ * @param clientId - The client id of the user to send the message to
122
+ * @param callback - The callback to call when a message is received
123
+ */
124
+ subscribeToMessages(clientId: string, callback: (message: ServerMessage) => void): Subscription;
125
+
126
+ /**
127
+ * @desc Sends a message to a specific user without running the middleware
128
+ * @param event - The event name
129
+ * @param message - The message to send
130
+ * @param client - The client id of the user to send the message to
131
+ * @param action - The action to send
132
+ */
133
+ respondToClient(event: string, message: default_t, client: string, action?: ServerActions): void;
134
+ }
@@ -0,0 +1,287 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.Channel = void 0;
4
+ const pondBase_1 = require("../pondBase");
5
+ const enums_1 = require("./enums");
6
+ const channelMiddleWare_1 = require("./channelMiddleWare");
7
+ class Channel extends pondBase_1.BaseClass {
8
+ constructor(name, middleWare, removeChannel) {
9
+ super();
10
+ this.name = name;
11
+ this._channelPresence = new pondBase_1.PondBase();
12
+ this._channelAssigns = new pondBase_1.SimpleBase();
13
+ this._channelData = {};
14
+ this._middleWare = new channelMiddleWare_1.ChannelMiddleware();
15
+ this.removeChannel = removeChannel;
16
+ this._messages = new pondBase_1.Broadcast();
17
+ this._middleWare.merge(middleWare);
18
+ }
19
+ /**
20
+ * @desc Returns the channel info
21
+ */
22
+ get info() {
23
+ const data = {
24
+ name: this.name, channelData: this.data, presence: this.presence, assigns: this.assigns
25
+ };
26
+ return Object.freeze(data);
27
+ }
28
+ /**
29
+ * @desc Gets the channel's data
30
+ */
31
+ get data() {
32
+ const result = { ...this._channelData };
33
+ return Object.freeze(result);
34
+ }
35
+ /**
36
+ * @desc Sets the channel's data
37
+ * @param data
38
+ */
39
+ set data(data) {
40
+ this._channelData = { ...this._channelData, ...data };
41
+ }
42
+ /**
43
+ * @desc Gets the channel's presence
44
+ */
45
+ get presence() {
46
+ return this._channelPresence.map(presence => presence);
47
+ }
48
+ /**
49
+ * @desc Gets the channel's assigns
50
+ */
51
+ get assigns() {
52
+ return this._channelAssigns.map(assigns => assigns);
53
+ }
54
+ /**
55
+ * @desc Gets a user's information
56
+ * @param clientId - The clientId of the user
57
+ */
58
+ getUserInfo(clientId) {
59
+ const client = this._retrieveUser(clientId);
60
+ if (!client)
61
+ return null;
62
+ return {
63
+ presence: client.presence.doc, assigns: client.assigns.doc
64
+ };
65
+ }
66
+ /**
67
+ * @desc Checks if a user exists in the channel
68
+ * @param clientId - The clientId of the user
69
+ */
70
+ hasUser(clientId) {
71
+ return this._channelPresence.has(clientId);
72
+ }
73
+ /**
74
+ * @desc Adds a new user to the channel
75
+ * @param user - The user to add to the channel
76
+ */
77
+ addUser(user) {
78
+ const clientId = user.client.clientId;
79
+ if (this.hasUser(clientId))
80
+ throw new Error(`User with clientId ${clientId} already exists in channel ${this.name}`);
81
+ this._channelPresence.set(clientId, { ...user.presence, id: clientId });
82
+ this._channelAssigns.set(clientId, user.assigns);
83
+ this._channelData = Object.assign({ ...this._channelData, ...user.channelData });
84
+ }
85
+ /**
86
+ * @desc Removes a user or group of users from the channel
87
+ * @param clientIds - The clientIds of the users to remove
88
+ */
89
+ removeUser(clientIds) {
90
+ const clients = Array.isArray(clientIds) ? clientIds : [clientIds];
91
+ clients.forEach(clientId => {
92
+ const client = this._retrieveUser(clientId);
93
+ if (client) {
94
+ client.presence.removeDoc();
95
+ client.assigns.removeDoc();
96
+ }
97
+ });
98
+ if (this._channelPresence.size === 0)
99
+ this.removeChannel();
100
+ }
101
+ /**
102
+ * @desc Subscribes to the presence changes occuring in the channel
103
+ * @param callback - The callback to call when a presence change occurs
104
+ */
105
+ onPresenceChange(callback) {
106
+ return this._channelPresence.subscribe((docs, change, action) => {
107
+ const message = {
108
+ action: enums_1.ServerActions.PRESENCE,
109
+ payload: { presence: docs, change },
110
+ event: action,
111
+ channelName: this.name,
112
+ };
113
+ callback(message);
114
+ });
115
+ }
116
+ /**
117
+ * @desc Subscribes to the message events occuring in the channel
118
+ * @param callback - The callback to call when a message event occurs
119
+ */
120
+ onMessage(callback) {
121
+ this._middleWare.use(callback);
122
+ }
123
+ /**
124
+ * @desc Updates the state of a user in the channel
125
+ * @param clientId - The clientId of the user to update
126
+ * @param presence - The new presence of the user
127
+ * @param assigns - The new assigns of the user
128
+ */
129
+ updateUser(clientId, presence, assigns) {
130
+ const client = this._retrieveUser(clientId);
131
+ if (client) {
132
+ client.assigns.updateDoc(Object.assign({ ...client.assigns.doc }, assigns));
133
+ const presenceDoc = Object.assign({ ...client.presence.doc }, presence);
134
+ if (!this.areEqual(presenceDoc, client.presence.doc))
135
+ client.presence.updateDoc(presenceDoc);
136
+ }
137
+ }
138
+ /**
139
+ * @desc Broadcasts a message to all users in the channel
140
+ * @param event - The event name
141
+ * @param message - The message to send
142
+ * @param sender - The sender of the message
143
+ */
144
+ broadcast(event, message, sender = enums_1.PondSenders.POND_CHANNEL) {
145
+ const client = this._retrieveUser(sender);
146
+ if (!client && !Object.values(enums_1.PondSenders).includes(sender))
147
+ throw new Error(`Client with clientId ${sender} does not exist in channel ${this.name}`);
148
+ const channelEvent = {
149
+ client: {
150
+ clientId: sender,
151
+ clientAssigns: client ? client.assigns.doc : {},
152
+ clientPresence: client ? client.presence.doc : {}
153
+ },
154
+ event: event,
155
+ payload: message,
156
+ channel: this,
157
+ };
158
+ const newMessage = {
159
+ action: enums_1.ServerActions.MESSAGE, payload: message, event: event, channelName: this.name,
160
+ };
161
+ void this._middleWare.run(channelEvent, hasErrored => {
162
+ if (!hasErrored)
163
+ this._sendToClients(this._channelAssigns.keys, newMessage);
164
+ });
165
+ }
166
+ /**
167
+ * @desc Broadcasts a message to all users in the channel except the sender
168
+ * @param event - The event name
169
+ * @param message - The message to send
170
+ * @param clientId - The client id of the sender
171
+ */
172
+ broadcastFrom(event, message, clientId) {
173
+ const client = this._retrieveUser(clientId);
174
+ if (!client)
175
+ throw new Error(`Client with clientId ${clientId} does not exist in channel ${this.name}`);
176
+ const newMessage = {
177
+ action: enums_1.ServerActions.MESSAGE, payload: message, event: event, channelName: this.name,
178
+ };
179
+ const channelEvent = {
180
+ client: {
181
+ clientId: clientId,
182
+ clientAssigns: client.assigns.doc,
183
+ clientPresence: client.presence.doc
184
+ },
185
+ event: event,
186
+ payload: message,
187
+ channel: this,
188
+ };
189
+ void this._middleWare.run(channelEvent, hasErrored => {
190
+ if (!hasErrored) {
191
+ const clientIds = this._channelAssigns.keys.filter(id => id !== clientId);
192
+ this._sendToClients(clientIds, newMessage);
193
+ }
194
+ });
195
+ }
196
+ /**
197
+ * @desc Sends a message to a specific user or group of users
198
+ * @param event - The event name
199
+ * @param clientId - The client id of the user to send the message to
200
+ * @param message - The message to send
201
+ * @param sender - The client id of the sender
202
+ */
203
+ sendTo(event, message, sender, clientId) {
204
+ const client = this._retrieveUser(sender);
205
+ if (!client && !Object.values(enums_1.PondSenders).includes(sender))
206
+ throw new Error('Client not found');
207
+ const clientIds = Array.isArray(clientId) ? clientId : [clientId];
208
+ const notFound = clientIds.filter(id => !this._channelAssigns.has(id));
209
+ if (notFound.length > 0)
210
+ throw new Error(`Client(s) with clientId(s) ${notFound.join(', ')} were not found in channel ${this.name}`);
211
+ const newMessage = {
212
+ action: enums_1.ServerActions.MESSAGE, payload: message, event: event, channelName: this.name,
213
+ };
214
+ const channelEvent = {
215
+ client: {
216
+ clientId: sender,
217
+ clientAssigns: client ? client.assigns.doc : {},
218
+ clientPresence: client ? client.presence.doc : {}
219
+ },
220
+ channel: this,
221
+ event: event,
222
+ payload: message,
223
+ };
224
+ void this._middleWare.run(channelEvent, hasErrored => {
225
+ if (!hasErrored)
226
+ this._sendToClients(clientIds, newMessage);
227
+ });
228
+ }
229
+ /**
230
+ * @desc Subscribes to a channel event, used only by the sockets to subscribe to all events
231
+ * @param clientId - The client id of the user to send the message to
232
+ * @param callback - The callback to call when a message is received
233
+ */
234
+ subscribeToMessages(clientId, callback) {
235
+ const sub1 = this._messages.subscribe(({ clients, message }) => {
236
+ if (clients.includes(clientId))
237
+ callback(message);
238
+ });
239
+ const sub2 = this.onPresenceChange(callback);
240
+ return {
241
+ unsubscribe: () => {
242
+ sub1.unsubscribe();
243
+ sub2.unsubscribe();
244
+ }
245
+ };
246
+ }
247
+ /**
248
+ * @desc Sends a message to a specific user without running the middleware
249
+ * @param event - The event name
250
+ * @param message - The message to send
251
+ * @param client - The client id of the user to send the message to
252
+ * @param action - The action to send
253
+ */
254
+ respondToClient(event, message, client, action = enums_1.ServerActions.MESSAGE) {
255
+ const newMessage = {
256
+ action: action,
257
+ payload: message, event: event,
258
+ channelName: this.name,
259
+ };
260
+ this._sendToClients([client], newMessage);
261
+ }
262
+ /**
263
+ * @desc Sends a message to a specific user or group of users except the sender
264
+ * @param clients - The client id of the user to send the message to
265
+ * @param message - The message to send
266
+ * @private
267
+ */
268
+ _sendToClients(clients, message) {
269
+ void this._messages.publish({ clients, message });
270
+ }
271
+ /**
272
+ * @desc Retrieves a user from the channel
273
+ * @param clientId - The client id of the user to retrieve
274
+ * @private
275
+ */
276
+ _retrieveUser(clientId) {
277
+ const assigns = this._channelAssigns.get(clientId);
278
+ const presence = this._channelPresence.get(clientId);
279
+ if (assigns && presence !== null) {
280
+ return {
281
+ assigns: assigns, presence: presence,
282
+ };
283
+ }
284
+ return null;
285
+ }
286
+ }
287
+ exports.Channel = Channel;