@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.
- package/.eslintrc.json +387 -0
- package/dist/LICENSE +674 -0
- package/dist/README.md +139 -0
- package/{channel → dist/channel}/channel.js +5 -7
- package/{lobby → dist/lobby}/joinResponse.js +2 -2
- package/dist/package.json +51 -0
- package/{presence → dist/presence}/presence.js +16 -18
- package/{server → dist/server}/pondSocket.js +1 -1
- package/{subjects → dist/subjects}/subject.js +44 -0
- package/{types.d.ts → dist/types.d.ts} +0 -5
- package/jest.config.js +11 -0
- package/package.json +3 -3
- package/src/abstracts/abstractRequest.test.ts +49 -0
- package/src/abstracts/abstractRequest.ts +56 -0
- package/src/abstracts/abstractResponse.ts +26 -0
- package/src/abstracts/middleware.test.ts +75 -0
- package/src/abstracts/middleware.ts +50 -0
- package/src/channel/channel.test.ts +501 -0
- package/src/channel/channel.ts +305 -0
- package/src/channel/eventRequest.test.ts +37 -0
- package/src/channel/eventRequest.ts +27 -0
- package/src/channel/eventResponse.test.ts +249 -0
- package/src/channel/eventResponse.ts +172 -0
- package/src/client/channel.test.ts +799 -0
- package/src/client/channel.ts +342 -0
- package/src/client.ts +124 -0
- package/src/endpoint/endpoint.test.ts +825 -0
- package/src/endpoint/endpoint.ts +304 -0
- package/src/endpoint/response.ts +106 -0
- package/src/enums.ts +52 -0
- package/src/errors/pondError.ts +32 -0
- package/src/express.ts +58 -0
- package/src/index.ts +3 -0
- package/src/lobby/JoinRequest.test.ts +48 -0
- package/src/lobby/JoinResponse.test.ts +162 -0
- package/src/lobby/joinRequest.ts +32 -0
- package/src/lobby/joinResponse.ts +146 -0
- package/src/lobby/lobby.ts +182 -0
- package/src/matcher/matcher.test.ts +103 -0
- package/src/matcher/matcher.ts +105 -0
- package/src/node.ts +33 -0
- package/src/presence/presence.ts +127 -0
- package/src/presence/presenceEngine.test.ts +143 -0
- package/src/server/pondSocket.ts +153 -0
- package/src/subjects/subject.test.ts +163 -0
- package/src/subjects/subject.ts +137 -0
- package/src/typedefs.d.ts +451 -0
- package/src/types.d.ts +89 -0
- package/tsconfig.build.json +7 -0
- package/tsconfig.json +12 -0
- /package/{abstracts → dist/abstracts}/abstractRequest.js +0 -0
- /package/{abstracts → dist/abstracts}/abstractResponse.js +0 -0
- /package/{abstracts → dist/abstracts}/middleware.js +0 -0
- /package/{channel → dist/channel}/eventRequest.js +0 -0
- /package/{channel → dist/channel}/eventResponse.js +0 -0
- /package/{client → dist/client}/channel.js +0 -0
- /package/{client.d.ts → dist/client.d.ts} +0 -0
- /package/{client.js → dist/client.js} +0 -0
- /package/{endpoint → dist/endpoint}/endpoint.js +0 -0
- /package/{endpoint → dist/endpoint}/response.js +0 -0
- /package/{enums.js → dist/enums.js} +0 -0
- /package/{errors → dist/errors}/pondError.js +0 -0
- /package/{express.d.ts → dist/express.d.ts} +0 -0
- /package/{express.js → dist/express.js} +0 -0
- /package/{index.d.ts → dist/index.d.ts} +0 -0
- /package/{index.js → dist/index.js} +0 -0
- /package/{lobby → dist/lobby}/joinRequest.js +0 -0
- /package/{lobby → dist/lobby}/lobby.js +0 -0
- /package/{matcher → dist/matcher}/matcher.js +0 -0
- /package/{node.d.ts → dist/node.d.ts} +0 -0
- /package/{node.js → dist/node.js} +0 -0
package/dist/README.md
ADDED
|
@@ -0,0 +1,139 @@
|
|
|
1
|
+
|
|
2
|
+
# PondSocket
|
|
3
|
+
|
|
4
|
+
PondSocket is a fast, minimalist and bidirectional socket framework for NodeJS. Pond allows you to think of each action during a sockets lifetime as a request instead of a huge callback that exists inside the connection event.
|
|
5
|
+
## Documentation
|
|
6
|
+
|
|
7
|
+
This is a Node.js module available through the npm registry.
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @eleven-am/pondsocket
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
PondSocket usage depends on the environment in which it is being used.
|
|
14
|
+
|
|
15
|
+
#### On the server
|
|
16
|
+
|
|
17
|
+
When using PondSocket, an endpoint is created. The endpoint is the gateway by which sockets actually connect to the server.
|
|
18
|
+
Multiple endpoints can be created but every endpoint is independent of the other, ie sockets on one endpoint cannot communicate with sockets on another endpoint.
|
|
19
|
+
|
|
20
|
+
```js
|
|
21
|
+
import { PondSocket } from "@eleven-am/pondsocket";
|
|
22
|
+
|
|
23
|
+
const pond = new PondSocket();
|
|
24
|
+
|
|
25
|
+
const endpoint = pond.createEndpoint('/api/socket', (req, res, _endpoint) => {
|
|
26
|
+
const token = req.query.token;
|
|
27
|
+
if (!token)
|
|
28
|
+
return res.reject('No token provided');
|
|
29
|
+
res.accept({
|
|
30
|
+
assign: {
|
|
31
|
+
token
|
|
32
|
+
}
|
|
33
|
+
});
|
|
34
|
+
})
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
While sockets connect through the endpoint, communication between sockets cannot occur on the endpoint level. Sockets have to join a channel to communicate
|
|
38
|
+
between themselves.
|
|
39
|
+
|
|
40
|
+
```js
|
|
41
|
+
const channel = endpoint.createChannel(/^channel(.*?)/, (req, res, channel) => {
|
|
42
|
+
const isAdmin = req.clientAssigns.admin;
|
|
43
|
+
if (!isAdmin)
|
|
44
|
+
return res.reject('You are not an admin');
|
|
45
|
+
|
|
46
|
+
res.accept({
|
|
47
|
+
assign: {
|
|
48
|
+
admin: true,
|
|
49
|
+
joinedDate: new Date()
|
|
50
|
+
},
|
|
51
|
+
presence: {
|
|
52
|
+
state: 'online'
|
|
53
|
+
},
|
|
54
|
+
channelData: {
|
|
55
|
+
locked: true,
|
|
56
|
+
numberOfUsers: channel.presence.length
|
|
57
|
+
}
|
|
58
|
+
});
|
|
59
|
+
});
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
A user goes through the createChannel function to join a channel.
|
|
63
|
+
When a user joins a channel, some private information can be assigned to the user. This assign could be viewed as a cookie that is only available serverside.
|
|
64
|
+
The presence is the current state of the user. When you reassign a new presence information to a user, all other users connected to the same channel are informed of the change.
|
|
65
|
+
This could be used as *user is typing*, *user is away*, etc. The channelData is information that is stored on the channel and accessible from anywhere the channel is available.
|
|
66
|
+
It can be anything from a boolean to an instance of a class. This data cannot be accessed from another channel as it is private to the channel.
|
|
67
|
+
|
|
68
|
+
```js
|
|
69
|
+
channel.on('hello', (req, res, channel) => {
|
|
70
|
+
const users = channel.presence;
|
|
71
|
+
res.assign({
|
|
72
|
+
assign: {
|
|
73
|
+
pingDate: new Date(),
|
|
74
|
+
users: users.length
|
|
75
|
+
}
|
|
76
|
+
});
|
|
77
|
+
|
|
78
|
+
// res.reject('curse words are not allowed on a child friendly channel')
|
|
79
|
+
// channel.closeFromChannel(req.client.clientId);
|
|
80
|
+
})
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
When a message is sent on a channel by a user, an event is triggered. The *on* function can be used to listen for these events. If the function is specified, it is called when the message is received.
|
|
84
|
+
You can choose to decline the message being sent, or you can allow the message to be sent as usual. You can also do all the normal assigns to the channel, or user.
|
|
85
|
+
In case there is no *on* function, the message will be sent without any action being taken.
|
|
86
|
+
|
|
87
|
+
#### On the browser
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
import { PondClient } from "@eleven-am/pondsocket/client";
|
|
91
|
+
|
|
92
|
+
export const socket = new PondClient('/api/socket', {});
|
|
93
|
+
socket.connect();
|
|
94
|
+
```
|
|
95
|
+
|
|
96
|
+
The browser compatible package can be imported from @eleven-am/pondsocket/client.
|
|
97
|
+
AN url string is provided to the class along with other url params, like token.
|
|
98
|
+
|
|
99
|
+
Multiple classes can be created, but it is advised to use a single class throughout the application.
|
|
100
|
+
You can just create multiple channels and maintain the single socket connection.
|
|
101
|
+
|
|
102
|
+
```js
|
|
103
|
+
const channelTopic = 'channel:one';
|
|
104
|
+
const options = {
|
|
105
|
+
username: 'eleven-am'
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export const channel = socket.createChannel(channelTopic, options);
|
|
109
|
+
channel.join();
|
|
110
|
+
```
|
|
111
|
+
|
|
112
|
+
When connected to the channel you can subscribe to the events from the channel.
|
|
113
|
+
|
|
114
|
+
```js
|
|
115
|
+
const subscriptionPresence = channel.onPresenceUpdate(presence => {
|
|
116
|
+
// handle the presence changes of the channel
|
|
117
|
+
});
|
|
118
|
+
|
|
119
|
+
const subscriptionMessage = channel.onMessage((event, data) => {
|
|
120
|
+
// handle the message being received
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
// When done with the channel remember to unsubscribe from these listeners
|
|
124
|
+
subscriptionPresence.unsubscribe();
|
|
125
|
+
subscriptionMessage.unsubscribe();
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
There are many other features available on the channel object. Since the application is completely typed,
|
|
129
|
+
suggestions should be provided by your IDE.
|
|
130
|
+
|
|
131
|
+
```js
|
|
132
|
+
channel.broadcast('hello', {
|
|
133
|
+
name: 'eleven-am',
|
|
134
|
+
message: 'I am the man, man'
|
|
135
|
+
})
|
|
136
|
+
|
|
137
|
+
// channel.broadcastFrom broadcasts a message to everyone but the client that emitted the message
|
|
138
|
+
// channel.sendMessage sends a message to clients specified in the function
|
|
139
|
+
```
|
|
@@ -123,11 +123,12 @@ class ChannelEngine {
|
|
|
123
123
|
* @param userId - The id of the user to get
|
|
124
124
|
*/
|
|
125
125
|
getUserData(userId) {
|
|
126
|
+
const assigns = __classPrivateFieldGet(this, _ChannelEngine_users, "f").get(userId);
|
|
126
127
|
const presence = __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f") ? __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").getUserPresence(userId) : {};
|
|
127
|
-
if (
|
|
128
|
+
if (assigns) {
|
|
128
129
|
return {
|
|
130
|
+
assigns,
|
|
129
131
|
id: userId,
|
|
130
|
-
assigns: __classPrivateFieldGet(this, _ChannelEngine_users, "f").get(userId),
|
|
131
132
|
presence: presence || {},
|
|
132
133
|
};
|
|
133
134
|
}
|
|
@@ -201,11 +202,8 @@ class ChannelEngine {
|
|
|
201
202
|
if (!__classPrivateFieldGet(this, _ChannelEngine_users, "f").has(userId)) {
|
|
202
203
|
throw new pondError_1.ChannelError(`ChannelEngine: User with id ${userId} does not exist in channel ${this.name}`, 404, this.name);
|
|
203
204
|
}
|
|
204
|
-
__classPrivateFieldSet(this, _ChannelEngine_presenceEngine, (_a = __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) !== null && _a !== void 0 ? _a : new presence_1.PresenceEngine(this
|
|
205
|
-
__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").trackPresence(userId, presence
|
|
206
|
-
const { type } = change, rest = __rest(change, ["type"]);
|
|
207
|
-
this.sendMessage(enums_1.SystemSender.CHANNEL, [userId], enums_1.ServerActions.PRESENCE, type, rest);
|
|
208
|
-
});
|
|
205
|
+
__classPrivateFieldSet(this, _ChannelEngine_presenceEngine, (_a = __classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f")) !== null && _a !== void 0 ? _a : new presence_1.PresenceEngine(this), "f");
|
|
206
|
+
__classPrivateFieldGet(this, _ChannelEngine_presenceEngine, "f").trackPresence(userId, presence);
|
|
209
207
|
}
|
|
210
208
|
/**
|
|
211
209
|
* @desc Gets the presence engine for the channel
|
|
@@ -33,7 +33,7 @@ class JoinResponse extends abstractResponse_1.PondResponse {
|
|
|
33
33
|
*/
|
|
34
34
|
accept(assigns) {
|
|
35
35
|
__classPrivateFieldGet(this, _JoinResponse_instances, "m", _JoinResponse_performChecks).call(this);
|
|
36
|
-
|
|
36
|
+
const newAssigns = Object.assign(Object.assign({}, assigns || {}), __classPrivateFieldGet(this, _JoinResponse_user, "f").assigns);
|
|
37
37
|
const acknowledgement = {
|
|
38
38
|
action: enums_1.ServerActions.SYSTEM,
|
|
39
39
|
channelName: __classPrivateFieldGet(this, _JoinResponse_engine, "f").name,
|
|
@@ -41,7 +41,7 @@ class JoinResponse extends abstractResponse_1.PondResponse {
|
|
|
41
41
|
payload: {},
|
|
42
42
|
};
|
|
43
43
|
__classPrivateFieldGet(this, _JoinResponse_user, "f").socket.send(JSON.stringify(acknowledgement));
|
|
44
|
-
__classPrivateFieldGet(this, _JoinResponse_engine, "f").addUser(__classPrivateFieldGet(this, _JoinResponse_user, "f").clientId,
|
|
44
|
+
__classPrivateFieldGet(this, _JoinResponse_engine, "f").addUser(__classPrivateFieldGet(this, _JoinResponse_user, "f").clientId, newAssigns, (event) => {
|
|
45
45
|
__classPrivateFieldGet(this, _JoinResponse_user, "f").socket.send(JSON.stringify(event));
|
|
46
46
|
});
|
|
47
47
|
return this;
|
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@eleven-am/pondsocket",
|
|
3
|
+
"version": "0.1.56",
|
|
4
|
+
"description": "PondSocket is a fast simple socket server",
|
|
5
|
+
"keywords": [
|
|
6
|
+
"socket",
|
|
7
|
+
"server",
|
|
8
|
+
"ws",
|
|
9
|
+
"websocket",
|
|
10
|
+
"pubsub",
|
|
11
|
+
"presence",
|
|
12
|
+
"realtime",
|
|
13
|
+
"realtime server"
|
|
14
|
+
],
|
|
15
|
+
"publishConfig": {
|
|
16
|
+
"access": "public"
|
|
17
|
+
},
|
|
18
|
+
"scripts": {
|
|
19
|
+
"test": "jest --coverage --verbose --watchAll",
|
|
20
|
+
"build": "tsc --project tsconfig.build.json",
|
|
21
|
+
"publishToNpm": "npm version patch && npm publish"
|
|
22
|
+
},
|
|
23
|
+
"author": "Roy OSSAI",
|
|
24
|
+
"license": "GPL-3.0",
|
|
25
|
+
"main": "index.js",
|
|
26
|
+
"types": "index.d.ts",
|
|
27
|
+
"repository": {
|
|
28
|
+
"type": "git",
|
|
29
|
+
"url": "git+https://github.com/Eleven-am/pondSocket.git"
|
|
30
|
+
},
|
|
31
|
+
"dependencies": {
|
|
32
|
+
"websocket": "^1.0.34",
|
|
33
|
+
"ws": "^8.12.0"
|
|
34
|
+
},
|
|
35
|
+
"devDependencies": {
|
|
36
|
+
"@types/express": "^4.17.14",
|
|
37
|
+
"@types/jest": "^29.5.0",
|
|
38
|
+
"@types/node": "^16.10.3",
|
|
39
|
+
"@types/websocket": "^1.0.5",
|
|
40
|
+
"@types/ws": "^8.5.3",
|
|
41
|
+
"@typescript-eslint/eslint-plugin": "^5.58.0",
|
|
42
|
+
"eslint": "^8.38.0",
|
|
43
|
+
"eslint-plugin-file-progress": "^1.3.0",
|
|
44
|
+
"eslint-plugin-import": "^2.27.5",
|
|
45
|
+
"jest": "^29.0.1",
|
|
46
|
+
"superwstest": "^2.0.3",
|
|
47
|
+
"ts-jest": "^29.1.0",
|
|
48
|
+
"ts-node": "^10.9.1",
|
|
49
|
+
"typescript": "^4.9.4"
|
|
50
|
+
}
|
|
51
|
+
}
|
|
@@ -10,19 +10,17 @@ 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
|
|
13
|
+
var _PresenceEngine_instances, _PresenceEngine_presenceMap, _PresenceEngine_channel, _PresenceEngine_publish;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.PresenceEngine = void 0;
|
|
16
16
|
const enums_1 = require("../enums");
|
|
17
17
|
const pondError_1 = require("../errors/pondError");
|
|
18
|
-
const subject_1 = require("../subjects/subject");
|
|
19
18
|
class PresenceEngine {
|
|
20
19
|
constructor(channel) {
|
|
21
|
-
|
|
20
|
+
_PresenceEngine_instances.add(this);
|
|
22
21
|
_PresenceEngine_presenceMap.set(this, void 0);
|
|
23
22
|
_PresenceEngine_channel.set(this, void 0);
|
|
24
23
|
__classPrivateFieldSet(this, _PresenceEngine_channel, channel, "f");
|
|
25
|
-
__classPrivateFieldSet(this, _PresenceEngine_engine, new subject_1.BehaviorSubject(), "f");
|
|
26
24
|
__classPrivateFieldSet(this, _PresenceEngine_presenceMap, new Map(), "f");
|
|
27
25
|
}
|
|
28
26
|
/**
|
|
@@ -46,13 +44,11 @@ class PresenceEngine {
|
|
|
46
44
|
* @desc Tracks a presence
|
|
47
45
|
* @param presenceKey - The key of the presence
|
|
48
46
|
* @param presence - The presence
|
|
49
|
-
* @param onPresenceChange - The callback to be called when the presence changes
|
|
50
47
|
*/
|
|
51
|
-
trackPresence(presenceKey, presence
|
|
48
|
+
trackPresence(presenceKey, presence) {
|
|
52
49
|
if (!__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").has(presenceKey)) {
|
|
53
50
|
__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").set(presenceKey, presence);
|
|
54
|
-
__classPrivateFieldGet(this,
|
|
55
|
-
type: enums_1.PresenceEventTypes.JOIN,
|
|
51
|
+
__classPrivateFieldGet(this, _PresenceEngine_instances, "m", _PresenceEngine_publish).call(this, enums_1.PresenceEventTypes.JOIN, {
|
|
56
52
|
changed: presence,
|
|
57
53
|
presence: Array.from(__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").values()),
|
|
58
54
|
});
|
|
@@ -60,9 +56,8 @@ class PresenceEngine {
|
|
|
60
56
|
else {
|
|
61
57
|
const code = 400;
|
|
62
58
|
const message = `PresenceEngine: Presence with key ${presenceKey} already exists`;
|
|
63
|
-
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f"), enums_1.PresenceEventTypes.JOIN);
|
|
59
|
+
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f").name, enums_1.PresenceEventTypes.JOIN);
|
|
64
60
|
}
|
|
65
|
-
return __classPrivateFieldGet(this, _PresenceEngine_engine, "f").subscribeWith(presenceKey, onPresenceChange);
|
|
66
61
|
}
|
|
67
62
|
/**
|
|
68
63
|
* @desc Removes a presence from the presence engine
|
|
@@ -72,10 +67,8 @@ class PresenceEngine {
|
|
|
72
67
|
removePresence(presenceKey, graceful = false) {
|
|
73
68
|
const presence = __classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").get(presenceKey);
|
|
74
69
|
if (presence) {
|
|
75
|
-
__classPrivateFieldGet(this, _PresenceEngine_engine, "f").unsubscribe(presenceKey);
|
|
76
70
|
__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").delete(presenceKey);
|
|
77
|
-
__classPrivateFieldGet(this,
|
|
78
|
-
type: enums_1.PresenceEventTypes.LEAVE,
|
|
71
|
+
__classPrivateFieldGet(this, _PresenceEngine_instances, "m", _PresenceEngine_publish).call(this, enums_1.PresenceEventTypes.LEAVE, {
|
|
79
72
|
changed: presence,
|
|
80
73
|
presence: Array.from(__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").values()),
|
|
81
74
|
});
|
|
@@ -83,7 +76,7 @@ class PresenceEngine {
|
|
|
83
76
|
else if (!graceful) {
|
|
84
77
|
const code = 404;
|
|
85
78
|
const message = `PresenceEngine: Presence with key ${presenceKey} does not exist`;
|
|
86
|
-
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f"), enums_1.PresenceEventTypes.LEAVE);
|
|
79
|
+
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f").name, enums_1.PresenceEventTypes.LEAVE);
|
|
87
80
|
}
|
|
88
81
|
}
|
|
89
82
|
/**
|
|
@@ -96,8 +89,7 @@ class PresenceEngine {
|
|
|
96
89
|
if (oldPresence) {
|
|
97
90
|
const newPresence = Object.assign(Object.assign({}, oldPresence), presence);
|
|
98
91
|
__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").set(presenceKey, newPresence);
|
|
99
|
-
__classPrivateFieldGet(this,
|
|
100
|
-
type: enums_1.PresenceEventTypes.UPDATE,
|
|
92
|
+
__classPrivateFieldGet(this, _PresenceEngine_instances, "m", _PresenceEngine_publish).call(this, enums_1.PresenceEventTypes.UPDATE, {
|
|
101
93
|
changed: newPresence,
|
|
102
94
|
presence: Array.from(__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").values()),
|
|
103
95
|
});
|
|
@@ -105,9 +97,15 @@ class PresenceEngine {
|
|
|
105
97
|
else {
|
|
106
98
|
const code = 404;
|
|
107
99
|
const message = `PresenceEngine: Presence with key ${presenceKey} does not exist`;
|
|
108
|
-
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f"), enums_1.PresenceEventTypes.UPDATE);
|
|
100
|
+
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f").name, enums_1.PresenceEventTypes.UPDATE);
|
|
109
101
|
}
|
|
110
102
|
}
|
|
111
103
|
}
|
|
112
104
|
exports.PresenceEngine = PresenceEngine;
|
|
113
|
-
|
|
105
|
+
_PresenceEngine_presenceMap = new WeakMap(), _PresenceEngine_channel = new WeakMap(), _PresenceEngine_instances = new WeakSet(), _PresenceEngine_publish = function _PresenceEngine_publish(event, payload) {
|
|
106
|
+
const recipients = Array.from(__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").keys());
|
|
107
|
+
if (recipients.length === 0) {
|
|
108
|
+
return;
|
|
109
|
+
}
|
|
110
|
+
__classPrivateFieldGet(this, _PresenceEngine_channel, "f").sendMessage(enums_1.SystemSender.CHANNEL, recipients, enums_1.ServerActions.PRESENCE, event, payload);
|
|
111
|
+
};
|
|
@@ -88,7 +88,7 @@ _PondSocket_server = new WeakMap(), _PondSocket_socketServer = new WeakMap(), _P
|
|
|
88
88
|
return socket.terminate();
|
|
89
89
|
}
|
|
90
90
|
socket.isAlive = false;
|
|
91
|
-
socket.ping(
|
|
91
|
+
socket.ping();
|
|
92
92
|
});
|
|
93
93
|
}, 30000);
|
|
94
94
|
__classPrivateFieldGet(this, _PondSocket_socketServer, "f").on('close', () => clearInterval(interval));
|
|
@@ -18,13 +18,24 @@ class SimpleSubject {
|
|
|
18
18
|
_SimpleSubject_observers.set(this, void 0);
|
|
19
19
|
__classPrivateFieldSet(this, _SimpleSubject_observers, new Set(), "f");
|
|
20
20
|
}
|
|
21
|
+
/**
|
|
22
|
+
* @desc Subscribes to a subject
|
|
23
|
+
* @param observer - The observer to subscribe
|
|
24
|
+
*/
|
|
21
25
|
subscribe(observer) {
|
|
22
26
|
__classPrivateFieldGet(this, _SimpleSubject_observers, "f").add(observer);
|
|
23
27
|
return () => __classPrivateFieldGet(this, _SimpleSubject_observers, "f").delete(observer);
|
|
24
28
|
}
|
|
29
|
+
/**
|
|
30
|
+
* @desc Publishes a message to all subscribers
|
|
31
|
+
* @param message - The message to publish
|
|
32
|
+
*/
|
|
25
33
|
publish(message) {
|
|
26
34
|
__classPrivateFieldGet(this, _SimpleSubject_observers, "f").forEach((observer) => observer(message));
|
|
27
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* @desc Returns the number of subscribers
|
|
38
|
+
*/
|
|
28
39
|
get size() {
|
|
29
40
|
return __classPrivateFieldGet(this, _SimpleSubject_observers, "f").size;
|
|
30
41
|
}
|
|
@@ -37,13 +48,24 @@ class SimpleBehaviorSubject extends SimpleSubject {
|
|
|
37
48
|
_SimpleBehaviorSubject_lastMessage.set(this, void 0);
|
|
38
49
|
__classPrivateFieldSet(this, _SimpleBehaviorSubject_lastMessage, initialValue, "f");
|
|
39
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* @desc Returns the last message published
|
|
53
|
+
*/
|
|
40
54
|
get value() {
|
|
41
55
|
return __classPrivateFieldGet(this, _SimpleBehaviorSubject_lastMessage, "f");
|
|
42
56
|
}
|
|
57
|
+
/**
|
|
58
|
+
* @desc Publishes a message to all subscribers
|
|
59
|
+
* @param message - The message to publish
|
|
60
|
+
*/
|
|
43
61
|
publish(message) {
|
|
44
62
|
__classPrivateFieldSet(this, _SimpleBehaviorSubject_lastMessage, message, "f");
|
|
45
63
|
super.publish(message);
|
|
46
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* @desc Subscribes to a subject
|
|
67
|
+
* @param observer - The observer to subscribe
|
|
68
|
+
*/
|
|
47
69
|
subscribe(observer) {
|
|
48
70
|
if (__classPrivateFieldGet(this, _SimpleBehaviorSubject_lastMessage, "f")) {
|
|
49
71
|
observer(__classPrivateFieldGet(this, _SimpleBehaviorSubject_lastMessage, "f"));
|
|
@@ -58,14 +80,27 @@ class Subject extends SimpleSubject {
|
|
|
58
80
|
super(...arguments);
|
|
59
81
|
_Subject_subscriptions.set(this, {});
|
|
60
82
|
}
|
|
83
|
+
/**
|
|
84
|
+
* @desc Subscribes to a subject
|
|
85
|
+
* @param identifier - The identifier of the subscription
|
|
86
|
+
* @param observer - The observer to subscribe
|
|
87
|
+
*/
|
|
61
88
|
subscribeWith(identifier, observer) {
|
|
62
89
|
__classPrivateFieldGet(this, _Subject_subscriptions, "f")[identifier] = super.subscribe(observer);
|
|
63
90
|
}
|
|
91
|
+
/**
|
|
92
|
+
* @desc Unsubscribes from a subject
|
|
93
|
+
* @param identifier - The identifier of the subscription
|
|
94
|
+
*/
|
|
64
95
|
unsubscribe(identifier) {
|
|
65
96
|
var _a, _b;
|
|
66
97
|
(_b = (_a = __classPrivateFieldGet(this, _Subject_subscriptions, "f"))[identifier]) === null || _b === void 0 ? void 0 : _b.call(_a);
|
|
67
98
|
delete __classPrivateFieldGet(this, _Subject_subscriptions, "f")[identifier];
|
|
68
99
|
}
|
|
100
|
+
/**
|
|
101
|
+
* @desc Checks if a subscription exists
|
|
102
|
+
* @param identifier - The identifier of the subscription
|
|
103
|
+
*/
|
|
69
104
|
has(identifier) {
|
|
70
105
|
return Boolean(__classPrivateFieldGet(this, _Subject_subscriptions, "f")[identifier]);
|
|
71
106
|
}
|
|
@@ -78,12 +113,21 @@ class BehaviorSubject extends Subject {
|
|
|
78
113
|
_BehaviorSubject_lastMessage.set(this, void 0);
|
|
79
114
|
__classPrivateFieldSet(this, _BehaviorSubject_lastMessage, initialValue, "f");
|
|
80
115
|
}
|
|
116
|
+
/**
|
|
117
|
+
* @desc Subscribes to a subject
|
|
118
|
+
* @param identifier - The identifier of the subscription
|
|
119
|
+
* @param observer - The observer to subscribe
|
|
120
|
+
*/
|
|
81
121
|
subscribeWith(identifier, observer) {
|
|
82
122
|
if (__classPrivateFieldGet(this, _BehaviorSubject_lastMessage, "f")) {
|
|
83
123
|
observer(__classPrivateFieldGet(this, _BehaviorSubject_lastMessage, "f"));
|
|
84
124
|
}
|
|
85
125
|
super.subscribeWith(identifier, observer);
|
|
86
126
|
}
|
|
127
|
+
/**
|
|
128
|
+
* @desc Publishes a message to all subscribers
|
|
129
|
+
* @param message - The message to publish
|
|
130
|
+
*/
|
|
87
131
|
publish(message) {
|
|
88
132
|
__classPrivateFieldSet(this, _BehaviorSubject_lastMessage, message, "f");
|
|
89
133
|
super.publish(message);
|
|
@@ -188,9 +188,6 @@ declare class EventResponse extends PondResponse {
|
|
|
188
188
|
}
|
|
189
189
|
|
|
190
190
|
export declare class Channel {
|
|
191
|
-
/**
|
|
192
|
-
* @desc Gets the current connection state of the channel.
|
|
193
|
-
*/
|
|
194
191
|
channelState: ChannelState;
|
|
195
192
|
|
|
196
193
|
/**
|
|
@@ -323,8 +320,6 @@ declare class JoinRequest<Path extends string> extends AbstractRequest<Path> {
|
|
|
323
320
|
}
|
|
324
321
|
|
|
325
322
|
declare class JoinResponse extends PondResponse {
|
|
326
|
-
#private;
|
|
327
|
-
|
|
328
323
|
/**
|
|
329
324
|
* @desc Accepts the request and optionally assigns data to the client
|
|
330
325
|
* @param assigns - the data to assign to the client
|
package/jest.config.js
ADDED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eleven-am/pondsocket",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.57",
|
|
4
4
|
"description": "PondSocket is a fast simple socket server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"socket",
|
|
@@ -22,8 +22,8 @@
|
|
|
22
22
|
},
|
|
23
23
|
"author": "Roy OSSAI",
|
|
24
24
|
"license": "GPL-3.0",
|
|
25
|
-
"main": "index.js",
|
|
26
|
-
"types": "index.d.ts",
|
|
25
|
+
"main": "dist/index.js",
|
|
26
|
+
"types": "dist/index.d.ts",
|
|
27
27
|
"repository": {
|
|
28
28
|
"type": "git",
|
|
29
29
|
"url": "git+https://github.com/Eleven-am/pondSocket.git"
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
import { AbstractRequest } from './abstractRequest';
|
|
2
|
+
|
|
3
|
+
const createMockChannelEngine = () => ({
|
|
4
|
+
name: 'test',
|
|
5
|
+
getAssigns: () => ({}),
|
|
6
|
+
getPresence: () => ({}),
|
|
7
|
+
} as any);
|
|
8
|
+
|
|
9
|
+
describe('AbstractRequest', () => {
|
|
10
|
+
it('should be able to be instantiated', () => {
|
|
11
|
+
const request = new AbstractRequest('/test', createMockChannelEngine(), {});
|
|
12
|
+
|
|
13
|
+
expect(request).toBeTruthy();
|
|
14
|
+
expect(request.channelNme).toBe('test');
|
|
15
|
+
expect(request.assigns).toEqual({});
|
|
16
|
+
expect(request.presence).toEqual({});
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
it('should be able to parse queries', () => {
|
|
20
|
+
const request = new AbstractRequest('/1234?choke=balls', createMockChannelEngine(), {});
|
|
21
|
+
|
|
22
|
+
expect(() => request.event).toThrowError('Event was not parsed');
|
|
23
|
+
expect(request['_parseQueries']('/:id')).toBe(true);
|
|
24
|
+
expect(request.event).toEqual({
|
|
25
|
+
event: '/1234?choke=balls',
|
|
26
|
+
params: { id: '1234' },
|
|
27
|
+
query: { choke: 'balls' },
|
|
28
|
+
payload: {},
|
|
29
|
+
});
|
|
30
|
+
});
|
|
31
|
+
|
|
32
|
+
it('should be return the value of the payload', () => {
|
|
33
|
+
const request = new AbstractRequest('/test', createMockChannelEngine(), { test: 'test' });
|
|
34
|
+
|
|
35
|
+
expect(request['_parseQueries']('/:id')).toBe(true);
|
|
36
|
+
expect(request.event.payload).toEqual({ test: 'test' });
|
|
37
|
+
|
|
38
|
+
const request2 = new AbstractRequest('/test', createMockChannelEngine(), {
|
|
39
|
+
test: 'test',
|
|
40
|
+
test2: 'test2',
|
|
41
|
+
});
|
|
42
|
+
|
|
43
|
+
expect(request2['_parseQueries']('/:id')).toBe(true);
|
|
44
|
+
expect(request2.event.payload).toEqual({
|
|
45
|
+
test: 'test',
|
|
46
|
+
test2: 'test2',
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
});
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { ChannelEngine } from '../channel/channel';
|
|
2
|
+
import { parseAddress } from '../matcher/matcher';
|
|
3
|
+
// eslint-disable-next-line import/no-unresolved
|
|
4
|
+
import { PondMessage, UserPresences, UserAssigns, PondPath, PondEvent, EventParams } from '../types';
|
|
5
|
+
|
|
6
|
+
export class AbstractRequest<Path extends string> {
|
|
7
|
+
protected readonly _engine: ChannelEngine;
|
|
8
|
+
|
|
9
|
+
#eventObject: EventParams<Path> | null;
|
|
10
|
+
|
|
11
|
+
readonly #event: string;
|
|
12
|
+
|
|
13
|
+
readonly #payload: PondMessage;
|
|
14
|
+
|
|
15
|
+
constructor (event: string, engine: ChannelEngine, payload: PondMessage) {
|
|
16
|
+
this._engine = engine;
|
|
17
|
+
this.#event = event;
|
|
18
|
+
this.#eventObject = null;
|
|
19
|
+
this.#payload = payload;
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
public get event (): PondEvent<Path> {
|
|
23
|
+
if (this.#eventObject === null) {
|
|
24
|
+
throw new Error('Event was not parsed');
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
return {
|
|
28
|
+
event: this.#event,
|
|
29
|
+
params: this.#eventObject.params || {},
|
|
30
|
+
query: this.#eventObject.query || {},
|
|
31
|
+
payload: this.#payload,
|
|
32
|
+
};
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
public get channelNme (): string {
|
|
36
|
+
return this._engine.name;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
public get assigns (): UserAssigns {
|
|
40
|
+
return this._engine.getAssigns();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
public get presence (): UserPresences {
|
|
44
|
+
return this._engine.presenceEngine?.getPresence() || {};
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* @desc Parses the event and returns true if the event matches the path
|
|
49
|
+
* @param path - the path to match
|
|
50
|
+
*/
|
|
51
|
+
public _parseQueries (path: PondPath<Path>): boolean {
|
|
52
|
+
this.#eventObject = parseAddress(path, this.#event);
|
|
53
|
+
|
|
54
|
+
return this.#eventObject !== null;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
// eslint-disable-next-line import/no-unresolved
|
|
2
|
+
import { PondAssigns, PondMessage } from '../types';
|
|
3
|
+
|
|
4
|
+
export abstract class PondResponse {
|
|
5
|
+
/**
|
|
6
|
+
* @desc Rejects the request with the given error message
|
|
7
|
+
* @param message - the error message
|
|
8
|
+
* @param errorCode - the error code
|
|
9
|
+
* @param assigns - the data to assign to the client
|
|
10
|
+
*/
|
|
11
|
+
public abstract reject(message?: string, errorCode?: number, assigns?: PondAssigns): void
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* @desc Emits a direct message to the client
|
|
15
|
+
* @param event - the event name
|
|
16
|
+
* @param payload - the payload to send
|
|
17
|
+
* @param assigns - the data to assign to the client
|
|
18
|
+
*/
|
|
19
|
+
public abstract send(event: string, payload: PondMessage, assigns?: PondAssigns): void
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* @desc Accepts the request and optionally assigns data to the client
|
|
23
|
+
* @param assigns - the data to assign to the client
|
|
24
|
+
*/
|
|
25
|
+
public abstract accept(assigns?: PondAssigns): void
|
|
26
|
+
}
|