@eleven-am/pondsocket 0.1.11 → 0.1.12
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/dist/client/channel.js +200 -0
- package/dist/client/index.d.ts +122 -0
- package/{pondClient/socket.js → dist/client/index.js} +30 -44
- package/dist/express/index.d.ts +36 -0
- package/dist/express/index.js +16 -0
- package/dist/index.d.ts +340 -0
- package/dist/index.js +4 -0
- package/dist/server/abstracts/abstractRequest.js +40 -0
- package/dist/server/abstracts/abstractRequest.test.js +41 -0
- package/dist/server/abstracts/abstractResponse.js +6 -0
- package/dist/server/abstracts/middleware.js +38 -0
- package/dist/server/abstracts/middleware.test.js +70 -0
- package/dist/server/channel/channelEngine.js +279 -0
- package/dist/server/channel/channelEngine.test.js +377 -0
- package/dist/server/channel/channelRequest.test.js +29 -0
- package/dist/server/channel/channelResponse.test.js +134 -0
- package/dist/server/channel/eventRequest.js +18 -0
- package/dist/server/channel/eventResponse.js +141 -0
- package/dist/server/endpoint/connectionResponse.js +50 -0
- package/dist/server/endpoint/endpoint.js +269 -0
- package/dist/server/endpoint/endpoint.test.js +406 -0
- package/dist/server/endpoint/endpointResponse.test.js +43 -0
- package/dist/server/pondChannel/joinRequest.js +29 -0
- package/dist/server/pondChannel/joinResponse.js +96 -0
- package/dist/server/pondChannel/pondChannel.js +161 -0
- package/dist/server/pondChannel/pondChannelResponse.test.js +108 -0
- package/dist/server/presence/presenceEngine.js +112 -0
- package/dist/server/presence/presenceEngine.test.js +104 -0
- package/dist/server/server/pondSocket.js +122 -0
- package/{pondSocket → dist/server/server}/server.test.js +7 -21
- package/{pondBase/baseClass.js → dist/server/utils/matchPattern.js} +33 -43
- package/{pondBase/baseClass.test.js → dist/server/utils/matchPattern.test.js} +16 -25
- package/dist/server/utils/subjectUtils.js +68 -0
- package/package.json +28 -12
- package/.eslintrc.js +0 -28
- package/base.d.ts +0 -1
- package/base.js +0 -17
- package/client.d.ts +0 -1
- package/client.js +0 -17
- package/index.d.ts +0 -1
- package/index.js +0 -17
- package/jest.config.js +0 -11
- package/pondBase/baseClass.d.ts +0 -55
- package/pondBase/enums.d.ts +0 -9
- package/pondBase/enums.js +0 -14
- package/pondBase/index.d.ts +0 -6
- package/pondBase/index.js +0 -22
- package/pondBase/pondBase.d.ts +0 -41
- package/pondBase/pondBase.js +0 -60
- package/pondBase/pondBase.test.js +0 -101
- package/pondBase/pubSub.d.ts +0 -82
- package/pondBase/pubSub.js +0 -158
- package/pondBase/pubSub.test.js +0 -332
- package/pondBase/simpleBase.d.ts +0 -126
- package/pondBase/simpleBase.js +0 -211
- package/pondBase/simpleBase.test.js +0 -153
- package/pondBase/types.d.ts +0 -2
- package/pondBase/types.js +0 -2
- package/pondClient/channel.d.ts +0 -77
- package/pondClient/channel.js +0 -167
- package/pondClient/index.d.ts +0 -2
- package/pondClient/index.js +0 -18
- package/pondClient/socket.d.ts +0 -41
- package/pondSocket/channel.d.ts +0 -129
- package/pondSocket/channel.js +0 -287
- package/pondSocket/channel.test.js +0 -377
- package/pondSocket/channelMiddleWare.d.ts +0 -28
- package/pondSocket/channelMiddleWare.js +0 -36
- package/pondSocket/endpoint.d.ts +0 -90
- package/pondSocket/endpoint.js +0 -320
- package/pondSocket/endpoint.test.js +0 -490
- package/pondSocket/enums.d.ts +0 -19
- package/pondSocket/enums.js +0 -25
- package/pondSocket/index.d.ts +0 -7
- package/pondSocket/index.js +0 -23
- package/pondSocket/pondChannel.d.ts +0 -79
- package/pondSocket/pondChannel.js +0 -211
- package/pondSocket/pondChannel.test.js +0 -430
- package/pondSocket/pondResponse.d.ts +0 -25
- package/pondSocket/pondResponse.js +0 -120
- package/pondSocket/pondSocket.d.ts +0 -47
- package/pondSocket/pondSocket.js +0 -94
- package/pondSocket/socketMiddleWare.d.ts +0 -6
- package/pondSocket/socketMiddleWare.js +0 -32
- package/pondSocket/types.d.ts +0 -74
- package/pondSocket/types.js +0 -2
- package/socket.d.ts +0 -1
- package/socket.js +0 -17
- package/tsconfig.eslint.json +0 -5
- package/tsconfig.json +0 -90
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.createChannelEvent = exports.createChannelEngine = void 0;
|
|
4
|
+
const channelEngine_1 = require("./channelEngine");
|
|
5
|
+
const channelEngine_test_1 = require("./channelEngine.test");
|
|
6
|
+
const eventResponse_1 = require("./eventResponse");
|
|
7
|
+
const createChannelEngine = () => {
|
|
8
|
+
const parentEngine = (0, channelEngine_test_1.createParentEngine)();
|
|
9
|
+
return new channelEngine_1.ChannelEngine('test', parentEngine);
|
|
10
|
+
};
|
|
11
|
+
exports.createChannelEngine = createChannelEngine;
|
|
12
|
+
const createChannelEvent = () => {
|
|
13
|
+
const responseEvent = {
|
|
14
|
+
event: 'event',
|
|
15
|
+
payload: {
|
|
16
|
+
payload: 'payload',
|
|
17
|
+
},
|
|
18
|
+
sender: 'sender',
|
|
19
|
+
action: channelEngine_1.ServerActions.BROADCAST,
|
|
20
|
+
recipients: ['recipient'],
|
|
21
|
+
};
|
|
22
|
+
return responseEvent;
|
|
23
|
+
};
|
|
24
|
+
exports.createChannelEvent = createChannelEvent;
|
|
25
|
+
const createChannelResponse = () => {
|
|
26
|
+
const channelEngine = (0, exports.createChannelEngine)();
|
|
27
|
+
const event = (0, exports.createChannelEvent)();
|
|
28
|
+
channelEngine.addUser(event.sender, { assign: 'assign' }, () => { });
|
|
29
|
+
const response = new eventResponse_1.EventResponse(event, channelEngine);
|
|
30
|
+
return { channelEngine,
|
|
31
|
+
event,
|
|
32
|
+
response };
|
|
33
|
+
};
|
|
34
|
+
describe('ChannelResponse', () => {
|
|
35
|
+
it('should create a new ChannelResponse', () => {
|
|
36
|
+
const { response } = createChannelResponse();
|
|
37
|
+
expect(response).toBeDefined();
|
|
38
|
+
});
|
|
39
|
+
it('should return the responseSent', () => {
|
|
40
|
+
const { response } = createChannelResponse();
|
|
41
|
+
expect(response.responseSent).toEqual(false);
|
|
42
|
+
});
|
|
43
|
+
it('should accept the request', () => {
|
|
44
|
+
const { response } = createChannelResponse();
|
|
45
|
+
response.accept();
|
|
46
|
+
expect(response.responseSent).toEqual(true);
|
|
47
|
+
});
|
|
48
|
+
it('should reject the request', () => {
|
|
49
|
+
const { response, channelEngine, event } = createChannelResponse();
|
|
50
|
+
jest.spyOn(channelEngine, 'sendMessage');
|
|
51
|
+
response.reject();
|
|
52
|
+
expect(response.responseSent).toEqual(true);
|
|
53
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('channel', [event.sender], channelEngine_1.ServerActions.ERROR, 'error_channel', { message: 'Unauthorized request',
|
|
54
|
+
code: 403 });
|
|
55
|
+
});
|
|
56
|
+
it('should send a direct message', () => {
|
|
57
|
+
const { response, channelEngine, event } = createChannelResponse();
|
|
58
|
+
jest.spyOn(channelEngine, 'sendMessage');
|
|
59
|
+
response.send('event', { payload: 'payload' });
|
|
60
|
+
expect(response.responseSent).toEqual(true);
|
|
61
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('channel', [event.sender], channelEngine_1.ServerActions.SYSTEM, 'event', { payload: 'payload' });
|
|
62
|
+
});
|
|
63
|
+
it('should broadcast a message', () => {
|
|
64
|
+
const { response, channelEngine } = createChannelResponse();
|
|
65
|
+
jest.spyOn(channelEngine, 'sendMessage');
|
|
66
|
+
response.broadcast('event', { payload: 'payload' });
|
|
67
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', 'all_users', channelEngine_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
|
|
68
|
+
});
|
|
69
|
+
it('should broadcastFromUser a message', () => {
|
|
70
|
+
const { response, channelEngine } = createChannelResponse();
|
|
71
|
+
jest.spyOn(channelEngine, 'sendMessage');
|
|
72
|
+
response.broadcastFromUser('event', { payload: 'payload' });
|
|
73
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', 'all_except_sender', channelEngine_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
|
|
74
|
+
});
|
|
75
|
+
it('should sendToUsers a message', () => {
|
|
76
|
+
const { response, channelEngine } = createChannelResponse();
|
|
77
|
+
jest.spyOn(channelEngine, 'sendMessage');
|
|
78
|
+
channelEngine.addUser('recipient', { assign: 'assign' }, () => { });
|
|
79
|
+
response.sendToUsers('event', { payload: 'payload' }, ['recipient']);
|
|
80
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('sender', ['recipient'], channelEngine_1.ServerActions.BROADCAST, 'event', { payload: 'payload' });
|
|
81
|
+
});
|
|
82
|
+
it('should track a trackPresence', () => {
|
|
83
|
+
const { response, channelEngine } = createChannelResponse();
|
|
84
|
+
jest.spyOn(channelEngine, 'trackPresence');
|
|
85
|
+
response.trackPresence({ status: 'online' });
|
|
86
|
+
expect(channelEngine.trackPresence).toHaveBeenCalledWith('sender', { status: 'online' });
|
|
87
|
+
});
|
|
88
|
+
it('should untrack a trackPresence', () => {
|
|
89
|
+
const { response, channelEngine } = createChannelResponse();
|
|
90
|
+
jest.spyOn(channelEngine, 'unTrackPresence');
|
|
91
|
+
response.unTrackPresence();
|
|
92
|
+
expect(channelEngine.unTrackPresence).toHaveBeenCalledWith('sender');
|
|
93
|
+
});
|
|
94
|
+
it('should broadcast an error if untrackPresence is called twice', () => {
|
|
95
|
+
const { response, channelEngine } = createChannelResponse();
|
|
96
|
+
jest.spyOn(channelEngine, 'sendMessage');
|
|
97
|
+
// because by default the user is not tracked and the presence engine only exists after a first trackPresence
|
|
98
|
+
// we need to call trackPresence first
|
|
99
|
+
response.trackPresence({ status: 'online' });
|
|
100
|
+
response.unTrackPresence();
|
|
101
|
+
response.unTrackPresence();
|
|
102
|
+
expect(channelEngine.sendMessage).toHaveBeenCalledWith('channel', ['sender'], channelEngine_1.ServerActions.ERROR, 'error_channel', {
|
|
103
|
+
message: 'PresenceEngine: Presence with key sender does not exist',
|
|
104
|
+
code: 500,
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
it('should updatePresence', () => {
|
|
108
|
+
const { response, channelEngine } = createChannelResponse();
|
|
109
|
+
jest.spyOn(channelEngine, 'updatePresence');
|
|
110
|
+
response.trackPresence({ status: 'online' });
|
|
111
|
+
response.updatePresence({ status: 'updated' });
|
|
112
|
+
expect(channelEngine.updatePresence).toHaveBeenCalledWith('sender', { status: 'updated' });
|
|
113
|
+
});
|
|
114
|
+
it('should update a users assign data', () => {
|
|
115
|
+
const { response, channelEngine } = createChannelResponse();
|
|
116
|
+
jest.spyOn(channelEngine, 'updateAssigns');
|
|
117
|
+
response.accept({ assign: 'updated' });
|
|
118
|
+
expect(channelEngine.updateAssigns).toHaveBeenCalledWith('sender', { assign: 'updated' });
|
|
119
|
+
});
|
|
120
|
+
it('should evict a user', () => {
|
|
121
|
+
const { response, channelEngine } = createChannelResponse();
|
|
122
|
+
jest.spyOn(channelEngine, 'kickUser');
|
|
123
|
+
response.evictUser('recipient');
|
|
124
|
+
expect(channelEngine.kickUser).toHaveBeenCalledWith('sender', 'recipient');
|
|
125
|
+
expect(response.responseSent).toEqual(true);
|
|
126
|
+
});
|
|
127
|
+
it('should destroy the channel', () => {
|
|
128
|
+
const { response, channelEngine } = createChannelResponse();
|
|
129
|
+
jest.spyOn(channelEngine, 'destroy');
|
|
130
|
+
response.closeChannel('recipient');
|
|
131
|
+
expect(channelEngine.destroy).toHaveBeenCalledWith('recipient');
|
|
132
|
+
expect(response.responseSent).toEqual(true);
|
|
133
|
+
});
|
|
134
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventRequest = void 0;
|
|
4
|
+
const abstractRequest_1 = require("../abstracts/abstractRequest");
|
|
5
|
+
class EventRequest extends abstractRequest_1.AbstractRequest {
|
|
6
|
+
constructor(event, engine) {
|
|
7
|
+
super(event.event, engine, event.payload);
|
|
8
|
+
this._internalEvent = event;
|
|
9
|
+
}
|
|
10
|
+
get user() {
|
|
11
|
+
const assigns = this._engine.getUserData(this._internalEvent.sender);
|
|
12
|
+
if (!assigns) {
|
|
13
|
+
throw new Error(`ChannelRequest: User with id ${this._internalEvent.sender} does not exist in channel ${this._engine.name}`);
|
|
14
|
+
}
|
|
15
|
+
return assigns;
|
|
16
|
+
}
|
|
17
|
+
}
|
|
18
|
+
exports.EventRequest = EventRequest;
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.EventResponse = void 0;
|
|
4
|
+
const channelEngine_1 = require("./channelEngine");
|
|
5
|
+
const abstractResponse_1 = require("../abstracts/abstractResponse");
|
|
6
|
+
class EventResponse extends abstractResponse_1.PondResponse {
|
|
7
|
+
constructor(event, engine) {
|
|
8
|
+
super();
|
|
9
|
+
this._hasExecuted = false;
|
|
10
|
+
this._event = event;
|
|
11
|
+
this._engine = engine;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* @desc Checks if the response has been sent
|
|
15
|
+
*/
|
|
16
|
+
get responseSent() {
|
|
17
|
+
return this._hasExecuted;
|
|
18
|
+
}
|
|
19
|
+
/**
|
|
20
|
+
* @desc Accepts the request and optionally assigns data to the client
|
|
21
|
+
* @param assigns - the data to assign to the client
|
|
22
|
+
*/
|
|
23
|
+
accept(assigns) {
|
|
24
|
+
this._manageAssigns(assigns);
|
|
25
|
+
this._hasExecuted = true;
|
|
26
|
+
return this;
|
|
27
|
+
}
|
|
28
|
+
/**
|
|
29
|
+
* @desc Rejects the request and optionally assigns data to the client
|
|
30
|
+
* @param message - the error message
|
|
31
|
+
* @param errorCode - the error code
|
|
32
|
+
* @param assigns - the data to assign to the client
|
|
33
|
+
*/
|
|
34
|
+
reject(message, errorCode, assigns) {
|
|
35
|
+
this._manageAssigns(assigns);
|
|
36
|
+
const text = message || 'Unauthorized request';
|
|
37
|
+
this._engine.sendMessage('channel', [this._event.sender], channelEngine_1.ServerActions.ERROR, 'error_channel', { message: text,
|
|
38
|
+
code: errorCode || 403 });
|
|
39
|
+
this._hasExecuted = true;
|
|
40
|
+
return this;
|
|
41
|
+
}
|
|
42
|
+
/**
|
|
43
|
+
* @desc Emits a direct message to the client
|
|
44
|
+
* @param event - the event name
|
|
45
|
+
* @param payload - the payload to send
|
|
46
|
+
* @param assigns - the data to assign to the client
|
|
47
|
+
*/
|
|
48
|
+
send(event, payload, assigns) {
|
|
49
|
+
this._engine.sendMessage('channel', [this._event.sender], channelEngine_1.ServerActions.SYSTEM, event, payload);
|
|
50
|
+
return this.accept(assigns);
|
|
51
|
+
}
|
|
52
|
+
/**
|
|
53
|
+
* @desc Sends a message to all clients in the channel
|
|
54
|
+
* @param event - the event to send
|
|
55
|
+
* @param payload - the payload to send
|
|
56
|
+
*/
|
|
57
|
+
broadcast(event, payload) {
|
|
58
|
+
this._engine.sendMessage(this._event.sender, 'all_users', channelEngine_1.ServerActions.BROADCAST, event, payload);
|
|
59
|
+
return this;
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* @desc Sends a message to all clients in the channel except the client making the request
|
|
63
|
+
* @param event - the event to send
|
|
64
|
+
* @param payload - the payload to send
|
|
65
|
+
*/
|
|
66
|
+
broadcastFromUser(event, payload) {
|
|
67
|
+
this._engine.sendMessage(this._event.sender, 'all_except_sender', channelEngine_1.ServerActions.BROADCAST, event, payload);
|
|
68
|
+
return this;
|
|
69
|
+
}
|
|
70
|
+
/**
|
|
71
|
+
* @desc Sends a message to a set of clients in the channel
|
|
72
|
+
* @param event - the event to send
|
|
73
|
+
* @param payload - the payload to send
|
|
74
|
+
* @param userIds - the ids of the clients to send the message to
|
|
75
|
+
*/
|
|
76
|
+
sendToUsers(event, payload, userIds) {
|
|
77
|
+
this._engine.sendMessage(this._event.sender, userIds, channelEngine_1.ServerActions.BROADCAST, event, payload);
|
|
78
|
+
return this;
|
|
79
|
+
}
|
|
80
|
+
/**
|
|
81
|
+
* @desc Tracks a user's presence in the channel
|
|
82
|
+
* @param presence - the initial presence data
|
|
83
|
+
* @param userId - the id of the user to track
|
|
84
|
+
*/
|
|
85
|
+
trackPresence(presence, userId) {
|
|
86
|
+
this._engine.trackPresence(userId || this._event.sender, presence);
|
|
87
|
+
return this;
|
|
88
|
+
}
|
|
89
|
+
/**
|
|
90
|
+
* @desc Updates a user's presence in the channel
|
|
91
|
+
* @param presence - the updated presence data
|
|
92
|
+
* @param userId - the id of the user to update
|
|
93
|
+
*/
|
|
94
|
+
updatePresence(presence, userId) {
|
|
95
|
+
this._engine.updatePresence(userId || this._event.sender, presence);
|
|
96
|
+
return this;
|
|
97
|
+
}
|
|
98
|
+
/**
|
|
99
|
+
* @desc Removes a user's presence from the channel
|
|
100
|
+
* @param userId - the id of the user to remove
|
|
101
|
+
*/
|
|
102
|
+
unTrackPresence(userId) {
|
|
103
|
+
userId = userId || this._event.sender;
|
|
104
|
+
try {
|
|
105
|
+
this._engine.unTrackPresence(userId);
|
|
106
|
+
}
|
|
107
|
+
catch (e) {
|
|
108
|
+
this._engine.sendMessage('channel', [userId], channelEngine_1.ServerActions.ERROR, 'error_channel', { message: e.message,
|
|
109
|
+
code: 500 });
|
|
110
|
+
}
|
|
111
|
+
return this;
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* @desc Evicts a user from the channel
|
|
115
|
+
* @param reason - the reason for the eviction
|
|
116
|
+
* @param userId - the id of the user to evict,
|
|
117
|
+
*/
|
|
118
|
+
evictUser(reason, userId) {
|
|
119
|
+
this._engine.kickUser(userId || this._event.sender, reason);
|
|
120
|
+
this._hasExecuted = true;
|
|
121
|
+
}
|
|
122
|
+
/**
|
|
123
|
+
* @desc Closes the channel from the server side for all clients
|
|
124
|
+
* @param reason - the reason for closing the channel
|
|
125
|
+
*/
|
|
126
|
+
closeChannel(reason) {
|
|
127
|
+
this._engine.destroy(reason);
|
|
128
|
+
this._hasExecuted = true;
|
|
129
|
+
}
|
|
130
|
+
/**
|
|
131
|
+
* @desc Gets the event that triggered the response
|
|
132
|
+
* @param assigns - the data to assign to the client
|
|
133
|
+
* @private
|
|
134
|
+
*/
|
|
135
|
+
_manageAssigns(assigns) {
|
|
136
|
+
if (assigns) {
|
|
137
|
+
this._engine.updateAssigns(this._event.sender, assigns);
|
|
138
|
+
}
|
|
139
|
+
}
|
|
140
|
+
}
|
|
141
|
+
exports.EventResponse = EventResponse;
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.ConnectionResponse = void 0;
|
|
4
|
+
const abstractResponse_1 = require("../abstracts/abstractResponse");
|
|
5
|
+
class ConnectionResponse extends abstractResponse_1.PondResponse {
|
|
6
|
+
constructor(handler) {
|
|
7
|
+
super();
|
|
8
|
+
this._hasExecuted = false;
|
|
9
|
+
this._handler = handler;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* @desc Accepts the request and optionally assigns data to the client
|
|
13
|
+
* @param assigns - the data to assign to the client
|
|
14
|
+
*/
|
|
15
|
+
accept(assigns) {
|
|
16
|
+
if (this._hasExecuted) {
|
|
17
|
+
throw new Error('EndpointResponse: Message has already been processed');
|
|
18
|
+
}
|
|
19
|
+
this._hasExecuted = true;
|
|
20
|
+
this._handler(assigns || {}, {});
|
|
21
|
+
}
|
|
22
|
+
/**
|
|
23
|
+
* @desc Rejects the request with the given error message
|
|
24
|
+
* @param message - the error message
|
|
25
|
+
* @param errorCode - the error code
|
|
26
|
+
*/
|
|
27
|
+
reject(message, errorCode) {
|
|
28
|
+
if (this._hasExecuted) {
|
|
29
|
+
throw new Error('EndpointResponse: Message has already been processed');
|
|
30
|
+
}
|
|
31
|
+
this._hasExecuted = true;
|
|
32
|
+
this._handler({}, { error: { message: message || 'Message rejected',
|
|
33
|
+
code: errorCode || 403 } });
|
|
34
|
+
}
|
|
35
|
+
/**
|
|
36
|
+
* @desc Emits a direct message to the client
|
|
37
|
+
* @param event - the event name
|
|
38
|
+
* @param payload - the payload to send
|
|
39
|
+
* @param assigns - the data to assign to the client
|
|
40
|
+
*/
|
|
41
|
+
send(event, payload, assigns) {
|
|
42
|
+
if (this._hasExecuted) {
|
|
43
|
+
throw new Error('EndpointResponse: Message has already been processed');
|
|
44
|
+
}
|
|
45
|
+
this._hasExecuted = true;
|
|
46
|
+
this._handler(assigns || {}, { message: { event,
|
|
47
|
+
payload } });
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
exports.ConnectionResponse = ConnectionResponse;
|
|
@@ -0,0 +1,269 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.Endpoint = exports.ClientActions = void 0;
|
|
4
|
+
const connectionResponse_1 = require("./connectionResponse");
|
|
5
|
+
const channelEngine_1 = require("../channel/channelEngine");
|
|
6
|
+
const matchPattern_1 = require("../utils/matchPattern");
|
|
7
|
+
var ClientActions;
|
|
8
|
+
(function (ClientActions) {
|
|
9
|
+
ClientActions["JOIN_CHANNEL"] = "JOIN_CHANNEL";
|
|
10
|
+
ClientActions["LEAVE_CHANNEL"] = "LEAVE_CHANNEL";
|
|
11
|
+
ClientActions["BROADCAST"] = "BROADCAST";
|
|
12
|
+
})(ClientActions = exports.ClientActions || (exports.ClientActions = {}));
|
|
13
|
+
class Endpoint {
|
|
14
|
+
constructor(server) {
|
|
15
|
+
this._server = server;
|
|
16
|
+
this._channels = [];
|
|
17
|
+
this._sockets = new Set();
|
|
18
|
+
this._matcher = new matchPattern_1.MatchPattern();
|
|
19
|
+
}
|
|
20
|
+
/**
|
|
21
|
+
* @desc Adds a new PondChannel to this path on this endpoint
|
|
22
|
+
* @param path - The path to add the channel to
|
|
23
|
+
* @param channel - The channel to add
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* endpoint.useChannel('/chat', pondChannelInstance);
|
|
27
|
+
*/
|
|
28
|
+
useChannel(path, channel) {
|
|
29
|
+
const manager = channel._buildManager();
|
|
30
|
+
this._channels.push({ path,
|
|
31
|
+
manager });
|
|
32
|
+
}
|
|
33
|
+
/**
|
|
34
|
+
* @desc List all clients connected to this endpoint
|
|
35
|
+
*/
|
|
36
|
+
listConnections() {
|
|
37
|
+
return [...this._sockets].map(({ clientId }) => clientId);
|
|
38
|
+
}
|
|
39
|
+
/**
|
|
40
|
+
* @desc Gets all clients connected to this endpoint
|
|
41
|
+
*/
|
|
42
|
+
getClients() {
|
|
43
|
+
return [...this._sockets];
|
|
44
|
+
}
|
|
45
|
+
/**
|
|
46
|
+
* @desc Broadcasts a message to all clients connected to this endpoint
|
|
47
|
+
* @param event - The event to broadcast
|
|
48
|
+
* @param payload - The payload to broadcast
|
|
49
|
+
*/
|
|
50
|
+
broadcast(event, payload) {
|
|
51
|
+
this._sockets.forEach(({ socket }) => {
|
|
52
|
+
const message = {
|
|
53
|
+
event,
|
|
54
|
+
payload,
|
|
55
|
+
action: channelEngine_1.ServerActions.BROADCAST,
|
|
56
|
+
channelName: 'SERVER',
|
|
57
|
+
};
|
|
58
|
+
this._sendMessage(socket, message);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
/**
|
|
62
|
+
* @desc Closes specific clients connected to this endpoint
|
|
63
|
+
* @param clientIds - The id for the client / clients to close
|
|
64
|
+
*/
|
|
65
|
+
closeConnection(clientIds) {
|
|
66
|
+
const clients = typeof clientIds === 'string' ? [clientIds] : clientIds;
|
|
67
|
+
this.getClients()
|
|
68
|
+
.forEach(({ clientId, socket }) => {
|
|
69
|
+
if (clients.includes(clientId)) {
|
|
70
|
+
socket.close();
|
|
71
|
+
}
|
|
72
|
+
});
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* @desc Authenticates the client to the endpoint
|
|
76
|
+
* @param request - Incoming request
|
|
77
|
+
* @param socket - Incoming socket
|
|
78
|
+
* @param head - Incoming head
|
|
79
|
+
* @param data - Incoming the data resolved from the handler
|
|
80
|
+
* @param handler - The handler to use to authenticate the client
|
|
81
|
+
*/
|
|
82
|
+
_authoriseConnection(request, socket, head, data, handler) {
|
|
83
|
+
const clientId = this._generateClientId();
|
|
84
|
+
const req = Object.assign(Object.assign({ headers: request.headers }, data), { id: clientId });
|
|
85
|
+
const resolver = (assigns, data) => {
|
|
86
|
+
if (data.error) {
|
|
87
|
+
socket.write(`HTTP/1.1 ${data.error.code} ${data.error.message}\r\n\r\n`);
|
|
88
|
+
return socket.destroy();
|
|
89
|
+
}
|
|
90
|
+
this._server.handleUpgrade(request, socket, head, (ws) => {
|
|
91
|
+
this._server.emit('connection', ws);
|
|
92
|
+
const socketCache = {
|
|
93
|
+
socket: ws,
|
|
94
|
+
assigns,
|
|
95
|
+
clientId,
|
|
96
|
+
};
|
|
97
|
+
this._sockets.add(socketCache);
|
|
98
|
+
this._manageSocket(socketCache);
|
|
99
|
+
if (data.message) {
|
|
100
|
+
const newMessage = {
|
|
101
|
+
event: data.message.event,
|
|
102
|
+
channelName: 'SERVER',
|
|
103
|
+
payload: data.message.payload,
|
|
104
|
+
action: channelEngine_1.ServerActions.SYSTEM,
|
|
105
|
+
};
|
|
106
|
+
this._sendMessage(ws, newMessage);
|
|
107
|
+
}
|
|
108
|
+
});
|
|
109
|
+
};
|
|
110
|
+
const res = new connectionResponse_1.ConnectionResponse(resolver);
|
|
111
|
+
handler(req, res);
|
|
112
|
+
}
|
|
113
|
+
/**
|
|
114
|
+
* @desc Manages a new socket connection
|
|
115
|
+
* @param cache - The socket cache
|
|
116
|
+
* @private
|
|
117
|
+
*/
|
|
118
|
+
_manageSocket(cache) {
|
|
119
|
+
const socket = cache.socket;
|
|
120
|
+
socket.addEventListener('message', (message) => {
|
|
121
|
+
this._readMessage(cache, message.data);
|
|
122
|
+
});
|
|
123
|
+
socket.addEventListener('close', () => {
|
|
124
|
+
this._channels
|
|
125
|
+
.forEach(({ manager }) => manager.removeUser(cache.clientId));
|
|
126
|
+
});
|
|
127
|
+
socket.addEventListener('error', () => {
|
|
128
|
+
this._channels
|
|
129
|
+
.forEach(({ manager }) => manager.removeUser(cache.clientId));
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
/**
|
|
133
|
+
* @desc Sends a message to a client
|
|
134
|
+
* @param socket - The socket to send the message to
|
|
135
|
+
* @param message - The message to send
|
|
136
|
+
*/
|
|
137
|
+
_sendMessage(socket, message) {
|
|
138
|
+
socket.send(JSON.stringify(message));
|
|
139
|
+
}
|
|
140
|
+
/**
|
|
141
|
+
* @desc Adds a new client to a channel on this endpoint
|
|
142
|
+
* @param channel - The channel to add the client to
|
|
143
|
+
* @param socket - The client to add to the channel
|
|
144
|
+
* @param joinParams - The parameters to pass to the channel
|
|
145
|
+
* @private
|
|
146
|
+
*/
|
|
147
|
+
_joinChannel(channel, socket, joinParams) {
|
|
148
|
+
for (const { path, manager } of this._channels) {
|
|
149
|
+
const match = this._matcher.parseEvent(path, channel);
|
|
150
|
+
if (match) {
|
|
151
|
+
const request = Object.assign(Object.assign(Object.assign({}, match), socket), { joinParams, channelName: channel });
|
|
152
|
+
return manager.addUser(request);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
throw new Error(`GatewayEngine: Channel ${channel} does not exist`);
|
|
156
|
+
}
|
|
157
|
+
/**
|
|
158
|
+
* @desc Executes a function on a channel
|
|
159
|
+
* @param channel - The channel to execute the function on
|
|
160
|
+
* @param handler - The function to execute
|
|
161
|
+
* @private
|
|
162
|
+
*/
|
|
163
|
+
_execute(channel, handler) {
|
|
164
|
+
for (const { manager } of this._channels) {
|
|
165
|
+
const isPresent = manager.listChannels().includes(channel);
|
|
166
|
+
if (isPresent) {
|
|
167
|
+
return manager.execute(channel, handler);
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
throw new Error(`GatewayEngine: Channel ${channel} does not exist`);
|
|
171
|
+
}
|
|
172
|
+
/**
|
|
173
|
+
* @desc Deals with a message sent from a client
|
|
174
|
+
* @param cache - The socket cache of the client
|
|
175
|
+
* @param message - The message to handle
|
|
176
|
+
*/
|
|
177
|
+
_handleMessage(cache, message) {
|
|
178
|
+
switch (message.action) {
|
|
179
|
+
case ClientActions.JOIN_CHANNEL:
|
|
180
|
+
this._joinChannel(message.channelName, cache, message.payload);
|
|
181
|
+
break;
|
|
182
|
+
case ClientActions.LEAVE_CHANNEL:
|
|
183
|
+
this._execute(message.channelName, (channel) => {
|
|
184
|
+
channel.removeUser(cache.clientId);
|
|
185
|
+
});
|
|
186
|
+
break;
|
|
187
|
+
case ClientActions.BROADCAST:
|
|
188
|
+
this._execute(message.channelName, (channel) => {
|
|
189
|
+
channel.broadcastMessage(cache.clientId, message);
|
|
190
|
+
});
|
|
191
|
+
break;
|
|
192
|
+
default:
|
|
193
|
+
throw new Error(`GatewayEngine: Action ${message.action} does not exist`);
|
|
194
|
+
}
|
|
195
|
+
}
|
|
196
|
+
/**
|
|
197
|
+
* @desc Handles a message sent from a client
|
|
198
|
+
* @param cache - The socket cache of the client
|
|
199
|
+
* @param message - The message to handle
|
|
200
|
+
* @private
|
|
201
|
+
*/
|
|
202
|
+
_readMessage(cache, message) {
|
|
203
|
+
const errorMessage = {
|
|
204
|
+
event: 'error',
|
|
205
|
+
action: channelEngine_1.ServerActions.ERROR,
|
|
206
|
+
channelName: 'ENDPOINT',
|
|
207
|
+
payload: {},
|
|
208
|
+
};
|
|
209
|
+
try {
|
|
210
|
+
const data = JSON.parse(message);
|
|
211
|
+
if (!data.action) {
|
|
212
|
+
errorMessage.payload = {
|
|
213
|
+
message: 'No action provided',
|
|
214
|
+
};
|
|
215
|
+
}
|
|
216
|
+
else if (!data.channelName) {
|
|
217
|
+
errorMessage.payload = {
|
|
218
|
+
message: 'No channel name provided',
|
|
219
|
+
};
|
|
220
|
+
}
|
|
221
|
+
else if (!data.payload) {
|
|
222
|
+
errorMessage.payload = {
|
|
223
|
+
message: 'No payload provided',
|
|
224
|
+
};
|
|
225
|
+
}
|
|
226
|
+
if (!this._isObjectEmpty(errorMessage.payload)) {
|
|
227
|
+
this._sendMessage(cache.socket, errorMessage);
|
|
228
|
+
}
|
|
229
|
+
else {
|
|
230
|
+
this._handleMessage(cache, data);
|
|
231
|
+
}
|
|
232
|
+
}
|
|
233
|
+
catch (e) {
|
|
234
|
+
if (e instanceof SyntaxError) {
|
|
235
|
+
errorMessage.payload = {
|
|
236
|
+
message: 'Invalid JSON',
|
|
237
|
+
};
|
|
238
|
+
this._sendMessage(cache.socket, errorMessage);
|
|
239
|
+
}
|
|
240
|
+
else if (e instanceof Error) {
|
|
241
|
+
errorMessage.payload = {
|
|
242
|
+
message: e.message,
|
|
243
|
+
};
|
|
244
|
+
this._sendMessage(cache.socket, errorMessage);
|
|
245
|
+
}
|
|
246
|
+
}
|
|
247
|
+
}
|
|
248
|
+
/**
|
|
249
|
+
* @desc Checks if an object is empty
|
|
250
|
+
* @param obj - The object to check
|
|
251
|
+
* @private
|
|
252
|
+
*/
|
|
253
|
+
_isObjectEmpty(obj) {
|
|
254
|
+
return Object.keys(obj).length === 0;
|
|
255
|
+
}
|
|
256
|
+
/**
|
|
257
|
+
* @desc Generates a new client ID
|
|
258
|
+
* @private
|
|
259
|
+
*/
|
|
260
|
+
_generateClientId() {
|
|
261
|
+
let id = '';
|
|
262
|
+
const chars = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
|
263
|
+
for (let i = 0; i < 21; i++) {
|
|
264
|
+
id += chars.charAt(Math.floor(Math.random() * chars.length));
|
|
265
|
+
}
|
|
266
|
+
return id;
|
|
267
|
+
}
|
|
268
|
+
}
|
|
269
|
+
exports.Endpoint = Endpoint;
|