@eleven-am/pondsocket 0.1.126 → 0.1.127
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/README.md +5 -15
- package/abstracts/abstractRequest.test.js +42 -0
- package/abstracts/middleware.test.js +70 -0
- package/channel/channel.js +18 -20
- package/channel/channel.test.js +415 -0
- package/channel/eventRequest.test.js +30 -0
- package/channel/eventResponse.js +7 -7
- package/channel/eventResponse.test.js +197 -0
- package/endpoint/endpoint.js +16 -19
- package/endpoint/endpoint.test.js +297 -0
- package/endpoint/response.js +5 -6
- package/index.d.ts +2 -0
- package/lobby/JoinRequest.test.js +40 -0
- package/lobby/JoinResponse.test.js +145 -0
- package/lobby/joinResponse.js +9 -9
- package/lobby/lobby.js +3 -4
- package/matcher/matcher.test.js +90 -0
- package/package.json +32 -13
- package/presence/presence.js +8 -9
- package/presence/presenceEngine.test.js +128 -0
- package/schema.js +3 -3
- package/server/pondSocket.js +5 -4
- package/types.d.ts +0 -496
- package/types.js +2 -0
- package/client/channel.js +0 -305
- package/client.d.ts +0 -5
- package/client.js +0 -107
- package/enums.js +0 -56
- package/express.d.ts +0 -3
- package/express.js +0 -17
- package/misc/uuid.js +0 -12
- package/nest.d.ts +0 -19
- package/nest.js +0 -769
- package/node.d.ts +0 -3
- package/node.js +0 -30
- package/subjects/subject.js +0 -137
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const joinRequest_1 = require("./joinRequest");
|
|
4
|
+
const eventResponse_test_1 = require("../channel/eventResponse.test");
|
|
5
|
+
const createMockSocket = (params = {}) => {
|
|
6
|
+
const channelEngine = (0, eventResponse_test_1.createChannelEngine)();
|
|
7
|
+
const socket = {
|
|
8
|
+
clientId: 'sender',
|
|
9
|
+
assigns: { assign: 'assign' },
|
|
10
|
+
requestId: 'requestId',
|
|
11
|
+
channelName: 'channel',
|
|
12
|
+
socket: {
|
|
13
|
+
send: jest.fn(),
|
|
14
|
+
},
|
|
15
|
+
};
|
|
16
|
+
const request = new joinRequest_1.JoinRequest(socket, params, channelEngine);
|
|
17
|
+
return {
|
|
18
|
+
channelEngine,
|
|
19
|
+
socket,
|
|
20
|
+
request,
|
|
21
|
+
};
|
|
22
|
+
};
|
|
23
|
+
describe('JoinRequest', () => {
|
|
24
|
+
it('should create a new PondChannelResponse', () => {
|
|
25
|
+
const { request } = createMockSocket();
|
|
26
|
+
expect(request).toBeDefined();
|
|
27
|
+
});
|
|
28
|
+
it('should return the join params', () => {
|
|
29
|
+
const { request } = createMockSocket({ params: 'params' });
|
|
30
|
+
expect(request.joinParams).toEqual({ params: 'params' });
|
|
31
|
+
});
|
|
32
|
+
it('should return the user data', () => {
|
|
33
|
+
const { request } = createMockSocket();
|
|
34
|
+
expect(request.user).toEqual({
|
|
35
|
+
id: 'sender',
|
|
36
|
+
assigns: { assign: 'assign' },
|
|
37
|
+
presence: {},
|
|
38
|
+
});
|
|
39
|
+
});
|
|
40
|
+
});
|
|
@@ -0,0 +1,145 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
4
|
+
const joinResponse_1 = require("./joinResponse");
|
|
5
|
+
const eventResponse_test_1 = require("../channel/eventResponse.test");
|
|
6
|
+
const createPondResponse = () => {
|
|
7
|
+
const channelEngine = (0, eventResponse_test_1.createChannelEngine)();
|
|
8
|
+
const socket = {
|
|
9
|
+
clientId: 'sender',
|
|
10
|
+
assigns: { assign: 'assign' },
|
|
11
|
+
channelName: 'channel',
|
|
12
|
+
requestId: 'requestId',
|
|
13
|
+
socket: {
|
|
14
|
+
send: jest.fn(),
|
|
15
|
+
},
|
|
16
|
+
};
|
|
17
|
+
const response = new joinResponse_1.JoinResponse(socket, channelEngine);
|
|
18
|
+
return {
|
|
19
|
+
channelEngine,
|
|
20
|
+
socket,
|
|
21
|
+
response,
|
|
22
|
+
};
|
|
23
|
+
};
|
|
24
|
+
describe('JoinResponse', () => {
|
|
25
|
+
it('should create a new PondChannelResponse', () => {
|
|
26
|
+
const { response } = createPondResponse();
|
|
27
|
+
expect(response).toBeDefined();
|
|
28
|
+
});
|
|
29
|
+
it('should accept the request', () => {
|
|
30
|
+
const { response, channelEngine, socket } = createPondResponse();
|
|
31
|
+
// spy on the channelEngine to see if the user was added
|
|
32
|
+
jest.spyOn(channelEngine, 'addUser');
|
|
33
|
+
response.accept();
|
|
34
|
+
// check if the response was sent
|
|
35
|
+
expect(channelEngine.addUser).toHaveBeenCalledWith(socket.clientId, socket.assigns, expect.any(Function));
|
|
36
|
+
expect(channelEngine.getUserData(socket.clientId)).not.toBeNull();
|
|
37
|
+
});
|
|
38
|
+
it('should reject the request', () => {
|
|
39
|
+
const { response, channelEngine, socket } = createPondResponse();
|
|
40
|
+
// spy on the channelEngine to see if the user was added
|
|
41
|
+
jest.spyOn(channelEngine, 'addUser');
|
|
42
|
+
response.reject();
|
|
43
|
+
// check if the response was sent
|
|
44
|
+
expect(channelEngine.addUser).not.toHaveBeenCalled();
|
|
45
|
+
expect(channelEngine.getUserData(socket.clientId)).toBeUndefined();
|
|
46
|
+
// also check if the socket was sent a message
|
|
47
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
48
|
+
// @ts-expect-error - we are mocking the socket
|
|
49
|
+
const params = socket.socket.send.mock.calls[0][0];
|
|
50
|
+
expect(JSON.parse(params)).toEqual(expect.objectContaining({
|
|
51
|
+
event: pondsocket_common_1.ErrorTypes.UNAUTHORIZED_JOIN_REQUEST,
|
|
52
|
+
payload: {
|
|
53
|
+
message: 'Unauthorized connection',
|
|
54
|
+
code: 401,
|
|
55
|
+
},
|
|
56
|
+
channelName: 'test',
|
|
57
|
+
action: pondsocket_common_1.ServerActions.ERROR,
|
|
58
|
+
}));
|
|
59
|
+
});
|
|
60
|
+
it('should send a direct message', () => {
|
|
61
|
+
const { response, channelEngine, socket } = createPondResponse();
|
|
62
|
+
// spy on the channelEngine to see if the user was added
|
|
63
|
+
jest.spyOn(channelEngine, 'addUser');
|
|
64
|
+
response.send('POND_MESSAGE', { message: 'message' });
|
|
65
|
+
// check if the response was sent
|
|
66
|
+
expect(channelEngine.addUser).toHaveBeenCalled();
|
|
67
|
+
expect(channelEngine.getUserData(socket.clientId)).toStrictEqual({
|
|
68
|
+
assigns: {
|
|
69
|
+
assign: 'assign',
|
|
70
|
+
},
|
|
71
|
+
id: 'sender',
|
|
72
|
+
presence: {},
|
|
73
|
+
});
|
|
74
|
+
// also check if the socket was sent a message
|
|
75
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
76
|
+
// @ts-expect-error - we are mocking the socket
|
|
77
|
+
const first = socket.socket.send.mock.calls[0][0];
|
|
78
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
79
|
+
// @ts-expect-error - we are mocking the socket
|
|
80
|
+
const params = socket.socket.send.mock.calls[1][0];
|
|
81
|
+
expect(JSON.parse(params)).toEqual(expect.objectContaining({
|
|
82
|
+
channelName: 'test',
|
|
83
|
+
action: pondsocket_common_1.ServerActions.SYSTEM,
|
|
84
|
+
payload: {
|
|
85
|
+
message: 'message',
|
|
86
|
+
},
|
|
87
|
+
event: 'POND_MESSAGE',
|
|
88
|
+
}));
|
|
89
|
+
expect(JSON.parse(first)).toEqual(expect.objectContaining({
|
|
90
|
+
channelName: 'test',
|
|
91
|
+
action: pondsocket_common_1.ServerActions.SYSTEM,
|
|
92
|
+
payload: {},
|
|
93
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
94
|
+
}));
|
|
95
|
+
});
|
|
96
|
+
// auxiliary functions
|
|
97
|
+
it('should send messages to different users', () => {
|
|
98
|
+
const { response, channelEngine } = createPondResponse();
|
|
99
|
+
// spy on the channelEngine to see if any messages were published
|
|
100
|
+
const broadcast = jest.spyOn(channelEngine, 'sendMessage');
|
|
101
|
+
// add a second user to the channel
|
|
102
|
+
channelEngine.addUser('user2', { assign: 'assign' }, () => { });
|
|
103
|
+
// send a message to a single user
|
|
104
|
+
// this is because the sender does not exist in the channel yet
|
|
105
|
+
expect(() => response.sendToUsers('hello_everyone', { message: 'hello' }, ['user2'])).toThrow();
|
|
106
|
+
// clear the spy
|
|
107
|
+
broadcast.mockClear();
|
|
108
|
+
// add the sender to the channel by using the response.accept() method
|
|
109
|
+
response.accept().sendToUsers('hello_everyone', { message: 'hello' }, ['user2']);
|
|
110
|
+
// check if the message was sent
|
|
111
|
+
expect(broadcast).toHaveBeenCalledWith('requestId', 'sender', ['user2'], pondsocket_common_1.ServerActions.BROADCAST, 'hello_everyone', { message: 'hello' });
|
|
112
|
+
// clear the spy
|
|
113
|
+
broadcast.mockClear();
|
|
114
|
+
// send a message to all users
|
|
115
|
+
response.broadcast('hello_everyone', { message: 'hello' });
|
|
116
|
+
expect(broadcast).toHaveBeenCalledWith('requestId', 'sender', pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.BROADCAST, 'hello_everyone', { message: 'hello' });
|
|
117
|
+
// clear the spy
|
|
118
|
+
broadcast.mockClear();
|
|
119
|
+
// send a message to all users except the sender
|
|
120
|
+
response.broadcastFromUser('hello_everyone', { message: 'hello' });
|
|
121
|
+
expect(broadcast).toHaveBeenCalledWith('requestId', 'sender', pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER, pondsocket_common_1.ServerActions.BROADCAST, 'hello_everyone', { message: 'hello' });
|
|
122
|
+
});
|
|
123
|
+
it('should be able to track the presence of users', () => {
|
|
124
|
+
const { response, channelEngine } = createPondResponse();
|
|
125
|
+
// spy on the channelEngine to see if the trackPresence method was called
|
|
126
|
+
const trackPresence = jest.spyOn(channelEngine.presenceEngine, 'trackPresence');
|
|
127
|
+
// add a second user to the channel
|
|
128
|
+
response.accept()
|
|
129
|
+
.trackPresence({
|
|
130
|
+
status: 'online',
|
|
131
|
+
});
|
|
132
|
+
// check if the trackPresence method was called
|
|
133
|
+
expect(trackPresence).toHaveBeenCalledWith('sender', { status: 'online' });
|
|
134
|
+
});
|
|
135
|
+
it('should throw an error if accept, reject / send is called more than once', () => {
|
|
136
|
+
const { response, channelEngine, socket } = createPondResponse();
|
|
137
|
+
jest.spyOn(channelEngine, 'addUser');
|
|
138
|
+
expect(channelEngine.addUser).not.toHaveBeenCalled();
|
|
139
|
+
response.accept();
|
|
140
|
+
expect(channelEngine.addUser).toHaveBeenCalledWith(socket.clientId, socket.assigns, expect.any(Function));
|
|
141
|
+
expect(() => response.accept()).toThrow('Request to join channel test rejected: Request already executed');
|
|
142
|
+
expect(() => response.reject()).toThrow('Request to join channel test rejected: Request already executed');
|
|
143
|
+
expect(() => response.send('event', { payload: 'payload' })).toThrow('Request to join channel test rejected: Request already executed');
|
|
144
|
+
});
|
|
145
|
+
});
|
package/lobby/joinResponse.js
CHANGED
|
@@ -13,8 +13,8 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
13
13
|
var _JoinResponse_instances, _JoinResponse_user, _JoinResponse_engine, _JoinResponse_executed, _JoinResponse_performChecks;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.JoinResponse = void 0;
|
|
16
|
+
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
16
17
|
const abstractResponse_1 = require("../abstracts/abstractResponse");
|
|
17
|
-
const enums_1 = require("../enums");
|
|
18
18
|
const pondError_1 = require("../errors/pondError");
|
|
19
19
|
class JoinResponse extends abstractResponse_1.PondResponse {
|
|
20
20
|
constructor(user, engine) {
|
|
@@ -41,9 +41,9 @@ class JoinResponse extends abstractResponse_1.PondResponse {
|
|
|
41
41
|
__classPrivateFieldGet(this, _JoinResponse_instances, "m", _JoinResponse_performChecks).call(this);
|
|
42
42
|
const newAssigns = Object.assign(Object.assign({}, assigns || {}), __classPrivateFieldGet(this, _JoinResponse_user, "f").assigns);
|
|
43
43
|
const acknowledgement = {
|
|
44
|
-
action:
|
|
44
|
+
action: pondsocket_common_1.ServerActions.SYSTEM,
|
|
45
45
|
channelName: __classPrivateFieldGet(this, _JoinResponse_engine, "f").name,
|
|
46
|
-
event:
|
|
46
|
+
event: pondsocket_common_1.Events.ACKNOWLEDGE,
|
|
47
47
|
requestId: this.requestId,
|
|
48
48
|
payload: {},
|
|
49
49
|
};
|
|
@@ -61,13 +61,13 @@ class JoinResponse extends abstractResponse_1.PondResponse {
|
|
|
61
61
|
reject(message, errorCode) {
|
|
62
62
|
__classPrivateFieldGet(this, _JoinResponse_instances, "m", _JoinResponse_performChecks).call(this);
|
|
63
63
|
const errorMessage = {
|
|
64
|
-
event:
|
|
64
|
+
event: pondsocket_common_1.ErrorTypes.UNAUTHORIZED_JOIN_REQUEST,
|
|
65
65
|
payload: {
|
|
66
66
|
message: message || 'Unauthorized connection',
|
|
67
67
|
code: errorCode || 401,
|
|
68
68
|
},
|
|
69
69
|
channelName: __classPrivateFieldGet(this, _JoinResponse_engine, "f").name,
|
|
70
|
-
action:
|
|
70
|
+
action: pondsocket_common_1.ServerActions.ERROR,
|
|
71
71
|
requestId: this.requestId,
|
|
72
72
|
};
|
|
73
73
|
__classPrivateFieldGet(this, _JoinResponse_user, "f").socket.send(JSON.stringify(errorMessage));
|
|
@@ -81,7 +81,7 @@ class JoinResponse extends abstractResponse_1.PondResponse {
|
|
|
81
81
|
*/
|
|
82
82
|
send(event, payload, assigns) {
|
|
83
83
|
this.accept(assigns);
|
|
84
|
-
__classPrivateFieldGet(this, _JoinResponse_engine, "f").sendMessage(this.requestId,
|
|
84
|
+
__classPrivateFieldGet(this, _JoinResponse_engine, "f").sendMessage(this.requestId, pondsocket_common_1.SystemSender.CHANNEL, [__classPrivateFieldGet(this, _JoinResponse_user, "f").clientId], pondsocket_common_1.ServerActions.SYSTEM, event, payload);
|
|
85
85
|
return this;
|
|
86
86
|
}
|
|
87
87
|
/**
|
|
@@ -90,7 +90,7 @@ class JoinResponse extends abstractResponse_1.PondResponse {
|
|
|
90
90
|
* @param payload - the payload to send
|
|
91
91
|
*/
|
|
92
92
|
broadcast(event, payload) {
|
|
93
|
-
__classPrivateFieldGet(this, _JoinResponse_engine, "f").sendMessage(this.requestId, __classPrivateFieldGet(this, _JoinResponse_user, "f").clientId,
|
|
93
|
+
__classPrivateFieldGet(this, _JoinResponse_engine, "f").sendMessage(this.requestId, __classPrivateFieldGet(this, _JoinResponse_user, "f").clientId, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.BROADCAST, event, payload);
|
|
94
94
|
return this;
|
|
95
95
|
}
|
|
96
96
|
/**
|
|
@@ -99,7 +99,7 @@ class JoinResponse extends abstractResponse_1.PondResponse {
|
|
|
99
99
|
* @param payload - the payload to send
|
|
100
100
|
*/
|
|
101
101
|
broadcastFromUser(event, payload) {
|
|
102
|
-
__classPrivateFieldGet(this, _JoinResponse_engine, "f").sendMessage(this.requestId, __classPrivateFieldGet(this, _JoinResponse_user, "f").clientId,
|
|
102
|
+
__classPrivateFieldGet(this, _JoinResponse_engine, "f").sendMessage(this.requestId, __classPrivateFieldGet(this, _JoinResponse_user, "f").clientId, pondsocket_common_1.ChannelReceiver.ALL_EXCEPT_SENDER, pondsocket_common_1.ServerActions.BROADCAST, event, payload);
|
|
103
103
|
return this;
|
|
104
104
|
}
|
|
105
105
|
/**
|
|
@@ -109,7 +109,7 @@ class JoinResponse extends abstractResponse_1.PondResponse {
|
|
|
109
109
|
* @param userIds - the ids of the clients to send the message to
|
|
110
110
|
*/
|
|
111
111
|
sendToUsers(event, payload, userIds) {
|
|
112
|
-
__classPrivateFieldGet(this, _JoinResponse_engine, "f").sendMessage(this.requestId, __classPrivateFieldGet(this, _JoinResponse_user, "f").clientId, userIds,
|
|
112
|
+
__classPrivateFieldGet(this, _JoinResponse_engine, "f").sendMessage(this.requestId, __classPrivateFieldGet(this, _JoinResponse_user, "f").clientId, userIds, pondsocket_common_1.ServerActions.BROADCAST, event, payload);
|
|
113
113
|
return this;
|
|
114
114
|
}
|
|
115
115
|
/**
|
package/lobby/lobby.js
CHANGED
|
@@ -13,10 +13,9 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
13
13
|
var _LobbyEngine_channels, _LobbyEngine_middleware, _LobbyEngine_leaveCallback, _PondChannel_lobby;
|
|
14
14
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
15
15
|
exports.PondChannel = exports.LobbyEngine = void 0;
|
|
16
|
+
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
16
17
|
const middleware_1 = require("../abstracts/middleware");
|
|
17
18
|
const channel_1 = require("../channel/channel");
|
|
18
|
-
const enums_1 = require("../enums");
|
|
19
|
-
const uuid_1 = require("../misc/uuid");
|
|
20
19
|
class LobbyEngine {
|
|
21
20
|
constructor() {
|
|
22
21
|
_LobbyEngine_channels.set(this, void 0);
|
|
@@ -69,11 +68,11 @@ class LobbyEngine {
|
|
|
69
68
|
if (!channel) {
|
|
70
69
|
throw new Error(`GatewayEngine: Channel ${channelName} does not exist`);
|
|
71
70
|
}
|
|
72
|
-
channel.sendMessage((0,
|
|
71
|
+
channel.sendMessage((0, pondsocket_common_1.uuid)(), pondsocket_common_1.SystemSender.CHANNEL, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.SYSTEM, event, payload);
|
|
73
72
|
}
|
|
74
73
|
else {
|
|
75
74
|
__classPrivateFieldGet(this, _LobbyEngine_channels, "f").forEach((channel) => {
|
|
76
|
-
channel.sendMessage((0,
|
|
75
|
+
channel.sendMessage((0, pondsocket_common_1.uuid)(), pondsocket_common_1.SystemSender.CHANNEL, pondsocket_common_1.ChannelReceiver.ALL_USERS, pondsocket_common_1.ServerActions.SYSTEM, event, payload);
|
|
77
76
|
});
|
|
78
77
|
}
|
|
79
78
|
}
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const matcher_1 = require("./matcher");
|
|
4
|
+
describe('parseAddress', () => {
|
|
5
|
+
it('should return null if a string does not match a regex and {} if it does', () => {
|
|
6
|
+
const regex = new RegExp(/^test/);
|
|
7
|
+
expect((0, matcher_1.parseAddress)(regex, 'test')).toStrictEqual({
|
|
8
|
+
params: {},
|
|
9
|
+
query: {},
|
|
10
|
+
});
|
|
11
|
+
const regex2 = new RegExp(/^test2/);
|
|
12
|
+
expect((0, matcher_1.parseAddress)(regex2, 'test')).toBe(null);
|
|
13
|
+
});
|
|
14
|
+
it('should return the params of a string matching the pattern', () => {
|
|
15
|
+
const pattern = '/test/:id';
|
|
16
|
+
const secondPattern = '/test/:id/:id2';
|
|
17
|
+
const string = '/test/5';
|
|
18
|
+
const secondString = '/test/5/6';
|
|
19
|
+
expect((0, matcher_1.parseAddress)(pattern, string))
|
|
20
|
+
.toStrictEqual({
|
|
21
|
+
params: {
|
|
22
|
+
id: '5',
|
|
23
|
+
},
|
|
24
|
+
query: {},
|
|
25
|
+
});
|
|
26
|
+
expect((0, matcher_1.parseAddress)(secondPattern, secondString))
|
|
27
|
+
.toStrictEqual({
|
|
28
|
+
params: {
|
|
29
|
+
id: '5',
|
|
30
|
+
id2: '6',
|
|
31
|
+
},
|
|
32
|
+
query: {},
|
|
33
|
+
});
|
|
34
|
+
expect((0, matcher_1.parseAddress)(pattern, secondString))
|
|
35
|
+
.toBe(null);
|
|
36
|
+
expect((0, matcher_1.parseAddress)(secondPattern, string))
|
|
37
|
+
.toBe(null);
|
|
38
|
+
});
|
|
39
|
+
it('should not match colons without slashes', () => {
|
|
40
|
+
const pattern = '/test:id';
|
|
41
|
+
const string = '/test5';
|
|
42
|
+
expect((0, matcher_1.parseAddress)(pattern, string))
|
|
43
|
+
.toBe(null);
|
|
44
|
+
});
|
|
45
|
+
it('should match when there is a *', () => {
|
|
46
|
+
const pattern = '/test/:id/*';
|
|
47
|
+
const string = '/test/5/6';
|
|
48
|
+
expect((0, matcher_1.parseAddress)(pattern, string))
|
|
49
|
+
.toStrictEqual({
|
|
50
|
+
params: {
|
|
51
|
+
id: '5',
|
|
52
|
+
'*': '/6',
|
|
53
|
+
},
|
|
54
|
+
query: {},
|
|
55
|
+
});
|
|
56
|
+
const secondPattern = '*';
|
|
57
|
+
const secondString = '/test/5/6';
|
|
58
|
+
expect((0, matcher_1.parseAddress)(secondPattern, secondString))
|
|
59
|
+
.toStrictEqual({
|
|
60
|
+
params: {
|
|
61
|
+
'*': '/test/5/6',
|
|
62
|
+
},
|
|
63
|
+
query: {},
|
|
64
|
+
});
|
|
65
|
+
});
|
|
66
|
+
it('should return the query of string', () => {
|
|
67
|
+
const pattern = '/test/:id';
|
|
68
|
+
const string = '/test/5?test=5';
|
|
69
|
+
const secondString = '/test/5?test=5&test2=6';
|
|
70
|
+
expect((0, matcher_1.parseAddress)(pattern, string))
|
|
71
|
+
.toStrictEqual({
|
|
72
|
+
params: {
|
|
73
|
+
id: '5',
|
|
74
|
+
},
|
|
75
|
+
query: {
|
|
76
|
+
test: '5',
|
|
77
|
+
},
|
|
78
|
+
});
|
|
79
|
+
expect((0, matcher_1.parseAddress)(pattern, secondString))
|
|
80
|
+
.toStrictEqual({
|
|
81
|
+
params: {
|
|
82
|
+
id: '5',
|
|
83
|
+
},
|
|
84
|
+
query: {
|
|
85
|
+
test: '5',
|
|
86
|
+
test2: '6',
|
|
87
|
+
},
|
|
88
|
+
});
|
|
89
|
+
});
|
|
90
|
+
});
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@eleven-am/pondsocket",
|
|
3
|
-
"version": "0.1.
|
|
3
|
+
"version": "0.1.127",
|
|
4
4
|
"description": "PondSocket is a fast simple socket server",
|
|
5
5
|
"keywords": [
|
|
6
6
|
"socket",
|
|
@@ -16,28 +16,31 @@
|
|
|
16
16
|
"access": "public"
|
|
17
17
|
},
|
|
18
18
|
"scripts": {
|
|
19
|
-
"test": "jest --coverage --verbose
|
|
20
|
-
"build": "tsc
|
|
21
|
-
"
|
|
19
|
+
"test": "jest --coverage --verbose",
|
|
20
|
+
"build": "rimraf dist && tsc && npm run remove",
|
|
21
|
+
"lint": "eslint --ext .ts src",
|
|
22
|
+
"lint:fix": "eslint --fix --ext .ts src",
|
|
23
|
+
"remove": "rm dist/dist.js dist/distIndex.js",
|
|
24
|
+
"copy": "cp src/dist.ts dist/types.d.ts && cp src/distIndex.ts dist/index.d.ts && cp package.json dist && cp README.md dist && cp LICENSE dist",
|
|
25
|
+
"push": "npm version patch && npm run copy && cd dist && npm publish && cd ..",
|
|
26
|
+
"pipeline": "npm run lint && npm run test && npm run build && npm run push"
|
|
22
27
|
},
|
|
23
28
|
"author": "Roy OSSAI",
|
|
24
29
|
"license": "GPL-3.0",
|
|
25
|
-
"main": "index.js",
|
|
26
|
-
"types": "index.d.ts",
|
|
30
|
+
"main": "./index.js",
|
|
31
|
+
"types": "./index.d.ts",
|
|
27
32
|
"repository": {
|
|
28
33
|
"type": "git",
|
|
29
34
|
"url": "git+https://github.com/Eleven-am/pondSocket.git"
|
|
30
35
|
},
|
|
31
36
|
"dependencies": {
|
|
32
|
-
"
|
|
33
|
-
"ws": "^8.16.0"
|
|
37
|
+
"@eleven-am/pondsocket-common": "^0.0.5",
|
|
38
|
+
"ws": "^8.16.0",
|
|
39
|
+
"zod": "^3.22.4"
|
|
34
40
|
},
|
|
35
41
|
"devDependencies": {
|
|
36
|
-
"@nestjs/common": "^10.3.0",
|
|
37
|
-
"@nestjs/core": "^10.3.0",
|
|
38
|
-
"@types/express": "^4.17.21",
|
|
39
42
|
"@types/jest": "^29.5.11",
|
|
40
|
-
"@types/node": "^20.10.
|
|
43
|
+
"@types/node": "^20.10.6",
|
|
41
44
|
"@types/websocket": "^1.0.10",
|
|
42
45
|
"@types/ws": "^8.5.10",
|
|
43
46
|
"@typescript-eslint/eslint-plugin": "^6.16.0",
|
|
@@ -45,10 +48,26 @@
|
|
|
45
48
|
"eslint-plugin-file-progress": "^1.3.0",
|
|
46
49
|
"eslint-plugin-import": "^2.29.1",
|
|
47
50
|
"jest": "^29.7.0",
|
|
48
|
-
"reflect-metadata": "^0.1.14",
|
|
49
51
|
"superwstest": "^2.0.3",
|
|
50
52
|
"ts-jest": "^29.1.1",
|
|
51
53
|
"ts-node": "^10.9.2",
|
|
52
54
|
"typescript": "^5.3.3"
|
|
55
|
+
},
|
|
56
|
+
"jest": {
|
|
57
|
+
"moduleFileExtensions": [
|
|
58
|
+
"js",
|
|
59
|
+
"json",
|
|
60
|
+
"ts"
|
|
61
|
+
],
|
|
62
|
+
"rootDir": "src",
|
|
63
|
+
"testRegex": ".*\\.test\\.ts$",
|
|
64
|
+
"transform": {
|
|
65
|
+
"^.+\\.(t|j)s$": "ts-jest"
|
|
66
|
+
},
|
|
67
|
+
"collectCoverageFrom": [
|
|
68
|
+
"**/*.(t|j)s"
|
|
69
|
+
],
|
|
70
|
+
"coverageDirectory": "../coverage",
|
|
71
|
+
"testEnvironment": "node"
|
|
53
72
|
}
|
|
54
73
|
}
|
package/presence/presence.js
CHANGED
|
@@ -13,9 +13,8 @@ var __classPrivateFieldGet = (this && this.__classPrivateFieldGet) || function (
|
|
|
13
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
|
-
const
|
|
16
|
+
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
17
17
|
const pondError_1 = require("../errors/pondError");
|
|
18
|
-
const uuid_1 = require("../misc/uuid");
|
|
19
18
|
class PresenceEngine {
|
|
20
19
|
constructor(channel) {
|
|
21
20
|
_PresenceEngine_instances.add(this);
|
|
@@ -49,7 +48,7 @@ class PresenceEngine {
|
|
|
49
48
|
trackPresence(presenceKey, presence) {
|
|
50
49
|
if (!__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").has(presenceKey)) {
|
|
51
50
|
__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").set(presenceKey, presence);
|
|
52
|
-
__classPrivateFieldGet(this, _PresenceEngine_instances, "m", _PresenceEngine_publish).call(this,
|
|
51
|
+
__classPrivateFieldGet(this, _PresenceEngine_instances, "m", _PresenceEngine_publish).call(this, pondsocket_common_1.PresenceEventTypes.JOIN, {
|
|
53
52
|
changed: presence,
|
|
54
53
|
presence: Array.from(__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").values()),
|
|
55
54
|
});
|
|
@@ -57,7 +56,7 @@ class PresenceEngine {
|
|
|
57
56
|
else {
|
|
58
57
|
const code = 400;
|
|
59
58
|
const message = `PresenceEngine: Presence with key ${presenceKey} already exists`;
|
|
60
|
-
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f").name,
|
|
59
|
+
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f").name, pondsocket_common_1.PresenceEventTypes.JOIN);
|
|
61
60
|
}
|
|
62
61
|
}
|
|
63
62
|
/**
|
|
@@ -69,7 +68,7 @@ class PresenceEngine {
|
|
|
69
68
|
const presence = __classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").get(presenceKey);
|
|
70
69
|
if (presence) {
|
|
71
70
|
__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").delete(presenceKey);
|
|
72
|
-
__classPrivateFieldGet(this, _PresenceEngine_instances, "m", _PresenceEngine_publish).call(this,
|
|
71
|
+
__classPrivateFieldGet(this, _PresenceEngine_instances, "m", _PresenceEngine_publish).call(this, pondsocket_common_1.PresenceEventTypes.LEAVE, {
|
|
73
72
|
changed: presence,
|
|
74
73
|
presence: Array.from(__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").values()),
|
|
75
74
|
});
|
|
@@ -77,7 +76,7 @@ class PresenceEngine {
|
|
|
77
76
|
else if (!graceful) {
|
|
78
77
|
const code = 404;
|
|
79
78
|
const message = `PresenceEngine: Presence with key ${presenceKey} does not exist`;
|
|
80
|
-
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f").name,
|
|
79
|
+
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f").name, pondsocket_common_1.PresenceEventTypes.LEAVE);
|
|
81
80
|
}
|
|
82
81
|
}
|
|
83
82
|
/**
|
|
@@ -90,7 +89,7 @@ class PresenceEngine {
|
|
|
90
89
|
if (oldPresence) {
|
|
91
90
|
const newPresence = Object.assign(Object.assign({}, oldPresence), presence);
|
|
92
91
|
__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").set(presenceKey, newPresence);
|
|
93
|
-
__classPrivateFieldGet(this, _PresenceEngine_instances, "m", _PresenceEngine_publish).call(this,
|
|
92
|
+
__classPrivateFieldGet(this, _PresenceEngine_instances, "m", _PresenceEngine_publish).call(this, pondsocket_common_1.PresenceEventTypes.UPDATE, {
|
|
94
93
|
changed: newPresence,
|
|
95
94
|
presence: Array.from(__classPrivateFieldGet(this, _PresenceEngine_presenceMap, "f").values()),
|
|
96
95
|
});
|
|
@@ -98,7 +97,7 @@ class PresenceEngine {
|
|
|
98
97
|
else {
|
|
99
98
|
const code = 404;
|
|
100
99
|
const message = `PresenceEngine: Presence with key ${presenceKey} does not exist`;
|
|
101
|
-
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f").name,
|
|
100
|
+
throw new pondError_1.PresenceError(message, code, __classPrivateFieldGet(this, _PresenceEngine_channel, "f").name, pondsocket_common_1.PresenceEventTypes.UPDATE);
|
|
102
101
|
}
|
|
103
102
|
}
|
|
104
103
|
}
|
|
@@ -108,5 +107,5 @@ _PresenceEngine_presenceMap = new WeakMap(), _PresenceEngine_channel = new WeakM
|
|
|
108
107
|
if (recipients.length === 0) {
|
|
109
108
|
return;
|
|
110
109
|
}
|
|
111
|
-
__classPrivateFieldGet(this, _PresenceEngine_channel, "f").sendMessage((0,
|
|
110
|
+
__classPrivateFieldGet(this, _PresenceEngine_channel, "f").sendMessage((0, pondsocket_common_1.uuid)(), pondsocket_common_1.SystemSender.CHANNEL, recipients, pondsocket_common_1.ServerActions.PRESENCE, event, payload);
|
|
112
111
|
};
|
|
@@ -0,0 +1,128 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
const pondsocket_common_1 = require("@eleven-am/pondsocket-common");
|
|
4
|
+
const presence_1 = require("./presence");
|
|
5
|
+
const eventResponse_test_1 = require("../channel/eventResponse.test");
|
|
6
|
+
describe('PresenceEngine', () => {
|
|
7
|
+
let presenceEngine;
|
|
8
|
+
let presence;
|
|
9
|
+
let presenceKey;
|
|
10
|
+
let channel;
|
|
11
|
+
beforeEach(() => {
|
|
12
|
+
channel = (0, eventResponse_test_1.createChannelEngine)();
|
|
13
|
+
channel.addUser('presenceKey', { assign: 'assign' }, () => {
|
|
14
|
+
// do nothing
|
|
15
|
+
});
|
|
16
|
+
presenceEngine = new presence_1.PresenceEngine(channel);
|
|
17
|
+
presence = {
|
|
18
|
+
id: 'id',
|
|
19
|
+
name: 'name',
|
|
20
|
+
color: 'color',
|
|
21
|
+
type: 'type',
|
|
22
|
+
location: 'location',
|
|
23
|
+
};
|
|
24
|
+
presenceKey = 'presenceKey';
|
|
25
|
+
});
|
|
26
|
+
describe('trackPresence', () => {
|
|
27
|
+
it('should insert a presence into the presence engine', () => {
|
|
28
|
+
// spy on the channel sendMessage method
|
|
29
|
+
const sendMessage = jest.spyOn(channel, 'sendMessage');
|
|
30
|
+
presenceEngine.trackPresence(presenceKey, presence);
|
|
31
|
+
// get all the params
|
|
32
|
+
const params = sendMessage.mock.calls[0];
|
|
33
|
+
// remove the first element as it contains the request id which is random
|
|
34
|
+
params.shift();
|
|
35
|
+
expect(params).toEqual([
|
|
36
|
+
pondsocket_common_1.SystemSender.CHANNEL,
|
|
37
|
+
['presenceKey'],
|
|
38
|
+
pondsocket_common_1.ServerActions.PRESENCE,
|
|
39
|
+
pondsocket_common_1.PresenceEventTypes.JOIN,
|
|
40
|
+
{
|
|
41
|
+
changed: presence,
|
|
42
|
+
presence: [presence],
|
|
43
|
+
},
|
|
44
|
+
]);
|
|
45
|
+
});
|
|
46
|
+
it('should throw an error if the presence already exists', () => {
|
|
47
|
+
presenceEngine.trackPresence(presenceKey, presence);
|
|
48
|
+
expect(() => presenceEngine.trackPresence(presenceKey, presence)).toThrowError(`PresenceEngine: Presence with key ${presenceKey} already exists`);
|
|
49
|
+
});
|
|
50
|
+
});
|
|
51
|
+
describe('updatePresence', () => {
|
|
52
|
+
it('should update a presence', () => {
|
|
53
|
+
presenceEngine.trackPresence(presenceKey, presence);
|
|
54
|
+
const newPresence = {
|
|
55
|
+
id: 'id',
|
|
56
|
+
name: 'name',
|
|
57
|
+
color: 'color',
|
|
58
|
+
type: 'type',
|
|
59
|
+
location: 'location',
|
|
60
|
+
};
|
|
61
|
+
const sendMessage = jest.spyOn(channel, 'sendMessage');
|
|
62
|
+
presenceEngine.updatePresence(presenceKey, newPresence);
|
|
63
|
+
// get all the params
|
|
64
|
+
const params = sendMessage.mock.calls[0];
|
|
65
|
+
// remove the first element as it contains the request id which is random
|
|
66
|
+
params.shift();
|
|
67
|
+
expect(params).toEqual([
|
|
68
|
+
pondsocket_common_1.SystemSender.CHANNEL,
|
|
69
|
+
['presenceKey'],
|
|
70
|
+
pondsocket_common_1.ServerActions.PRESENCE,
|
|
71
|
+
pondsocket_common_1.PresenceEventTypes.UPDATE,
|
|
72
|
+
{
|
|
73
|
+
changed: Object.assign(Object.assign({}, presence), newPresence),
|
|
74
|
+
presence: [newPresence],
|
|
75
|
+
},
|
|
76
|
+
]);
|
|
77
|
+
});
|
|
78
|
+
it('should throw an error if the presence does not exist', () => {
|
|
79
|
+
expect(() => presenceEngine.updatePresence(presenceKey, presence)).toThrowError(`PresenceEngine: Presence with key ${presenceKey} does not exist`);
|
|
80
|
+
});
|
|
81
|
+
});
|
|
82
|
+
describe('removePresence', () => {
|
|
83
|
+
it('should remove a presence from the presence engine', () => {
|
|
84
|
+
const listener = jest.spyOn(channel, 'sendMessage');
|
|
85
|
+
// before we can track a presence, we need make sure the user is in the channel
|
|
86
|
+
channel.addUser('presenceKey2', { assign: 'assign' }, () => {
|
|
87
|
+
// do nothing
|
|
88
|
+
});
|
|
89
|
+
presenceEngine.trackPresence(presenceKey, presence);
|
|
90
|
+
presenceEngine.trackPresence('presenceKey2', Object.assign(Object.assign({}, presence), { key: 'presence2' }));
|
|
91
|
+
expect(listener).toHaveBeenCalledTimes(2);
|
|
92
|
+
// clear the mock
|
|
93
|
+
listener.mockClear();
|
|
94
|
+
presenceEngine.removePresence(presenceKey);
|
|
95
|
+
expect(listener).toHaveBeenCalledTimes(1);
|
|
96
|
+
// get all the params
|
|
97
|
+
const params = listener.mock.calls[0];
|
|
98
|
+
// remove the first element as it contains the request id which is random
|
|
99
|
+
params.shift();
|
|
100
|
+
expect(params).toEqual([
|
|
101
|
+
pondsocket_common_1.SystemSender.CHANNEL,
|
|
102
|
+
['presenceKey2'],
|
|
103
|
+
pondsocket_common_1.ServerActions.PRESENCE,
|
|
104
|
+
pondsocket_common_1.PresenceEventTypes.LEAVE,
|
|
105
|
+
{
|
|
106
|
+
changed: presence,
|
|
107
|
+
presence: [
|
|
108
|
+
Object.assign(Object.assign({}, presence), { key: 'presence2' }),
|
|
109
|
+
],
|
|
110
|
+
},
|
|
111
|
+
]);
|
|
112
|
+
listener.mockClear();
|
|
113
|
+
presenceEngine.removePresence('presenceKey2');
|
|
114
|
+
expect(listener).toHaveBeenCalledTimes(0);
|
|
115
|
+
});
|
|
116
|
+
it('should throw an error if the presence does not exist', () => {
|
|
117
|
+
expect(() => presenceEngine.removePresence(presenceKey)).toThrowError(`PresenceEngine: Presence with key ${presenceKey} does not exist`);
|
|
118
|
+
});
|
|
119
|
+
});
|
|
120
|
+
describe('getPresence', () => {
|
|
121
|
+
it('should return the presence', () => {
|
|
122
|
+
presenceEngine.trackPresence(presenceKey, presence);
|
|
123
|
+
const data = {};
|
|
124
|
+
data[presenceKey] = presence;
|
|
125
|
+
expect(presenceEngine.getPresence()).toEqual(data);
|
|
126
|
+
});
|
|
127
|
+
});
|
|
128
|
+
});
|