@soundworks/core 4.0.0-alpha.21 → 4.0.0-alpha.23
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/CHANGELOG.md +14 -3
- package/package.json +15 -15
- package/src/client/Client.js +233 -188
- package/src/client/Socket.js +84 -247
- package/src/client/index.js +6 -0
- package/src/common/constants.js +0 -1
- package/src/common/logger.js +15 -0
- package/src/common/sockets-utils.js +0 -63
- package/src/common/version.js +2 -0
- package/src/server/Plugin.js +22 -10
- package/src/server/Server.js +37 -36
- package/src/server/Socket.js +126 -250
- package/src/server/Sockets.js +103 -127
- package/src/server/audit-network-latency.worker.js +4 -3
- package/src/server/index.js +6 -0
- package/types/client/Client.d.ts +41 -8
- package/types/client/Client.d.ts.map +1 -1
- package/types/client/Context.d.ts +40 -14
- package/types/client/Context.d.ts.map +1 -1
- package/types/client/Plugin.d.ts +42 -41
- package/types/client/Plugin.d.ts.map +1 -1
- package/types/client/PluginManager.d.ts +26 -11
- package/types/client/PluginManager.d.ts.map +1 -1
- package/types/client/SharedState.d.ts +1 -1
- package/types/client/SharedState.d.ts.map +1 -1
- package/types/client/SharedStateCollection.d.ts +41 -0
- package/types/client/SharedStateCollection.d.ts.map +1 -0
- package/types/client/Socket.d.ts +41 -30
- package/types/client/Socket.d.ts.map +1 -1
- package/types/client/StateManager.d.ts +1 -1
- package/types/client/StateManager.d.ts.map +1 -1
- package/types/client/index.d.ts +0 -8
- package/types/common/BasePlugin.d.ts +24 -19
- package/types/common/BasePlugin.d.ts.map +1 -1
- package/types/common/BasePluginManager.d.ts +17 -5
- package/types/common/BasePluginManager.d.ts.map +1 -1
- package/types/common/BaseSharedState.d.ts +100 -29
- package/types/common/BaseSharedState.d.ts.map +1 -1
- package/types/common/BaseSharedStateCollection.d.ts +183 -0
- package/types/common/BaseSharedStateCollection.d.ts.map +1 -0
- package/types/common/BaseStateManager.d.ts +83 -18
- package/types/common/BaseStateManager.d.ts.map +1 -1
- package/types/common/BatchedTransport.d.ts +21 -0
- package/types/common/BatchedTransport.d.ts.map +1 -0
- package/types/common/ParameterBag.d.ts +61 -40
- package/types/common/ParameterBag.d.ts.map +1 -1
- package/types/common/PromiseStore.d.ts +11 -0
- package/types/common/PromiseStore.d.ts.map +1 -0
- package/types/common/SharedStatePrivate.d.ts +4 -3
- package/types/common/SharedStatePrivate.d.ts.map +1 -1
- package/types/common/constants.d.ts +10 -1
- package/types/common/constants.d.ts.map +1 -1
- package/types/common/logger.d.ts +1 -1
- package/types/common/sockets-utils.d.ts.map +1 -1
- package/types/common/version.d.ts +3 -0
- package/types/common/version.d.ts.map +1 -0
- package/types/server/Client.d.ts +5 -0
- package/types/server/Client.d.ts.map +1 -1
- package/types/server/Context.d.ts +162 -22
- package/types/server/Context.d.ts.map +1 -1
- package/types/server/ContextManager.d.ts +4 -4
- package/types/server/ContextManager.d.ts.map +1 -1
- package/types/server/Plugin.d.ts +56 -55
- package/types/server/Plugin.d.ts.map +1 -1
- package/types/server/PluginManager.d.ts +26 -6
- package/types/server/PluginManager.d.ts.map +1 -1
- package/types/server/Server.d.ts +207 -70
- package/types/server/Server.d.ts.map +1 -1
- package/types/server/SharedState.d.ts +1 -1
- package/types/server/SharedState.d.ts.map +1 -1
- package/types/server/SharedStateCollection.d.ts +41 -0
- package/types/server/SharedStateCollection.d.ts.map +1 -0
- package/types/server/Socket.d.ts +93 -57
- package/types/server/Socket.d.ts.map +1 -1
- package/types/server/Sockets.d.ts +32 -25
- package/types/server/Sockets.d.ts.map +1 -1
- package/types/server/StateManager.d.ts +242 -27
- package/types/server/StateManager.d.ts.map +1 -1
- package/types/server/audit-network-latency.worker.d.ts +2 -1
- package/types/server/audit-schema.d.ts +8 -8
- package/types/server/crypto.d.ts +3 -0
- package/types/server/crypto.d.ts.map +1 -0
- package/types/server/index.d.ts +0 -4
- package/types/common/promise-store.d.ts +0 -4
- package/types/common/promise-store.d.ts.map +0 -1
- package/types/common/utils.d.ts +0 -5
- package/types/common/utils.d.ts.map +0 -1
package/src/client/Socket.js
CHANGED
|
@@ -2,34 +2,16 @@ import { isBrowser } from '@ircam/sc-utils';
|
|
|
2
2
|
import WebSocket from 'isomorphic-ws';
|
|
3
3
|
|
|
4
4
|
import {
|
|
5
|
-
PING_INTERVAL,
|
|
6
|
-
PING_LATENCY_TOLERANCE,
|
|
7
5
|
PING_MESSAGE,
|
|
8
6
|
PONG_MESSAGE,
|
|
9
7
|
} from '../common/constants.js';
|
|
10
8
|
import logger from '../common/logger.js';
|
|
11
9
|
import {
|
|
12
|
-
packBinaryMessage,
|
|
13
|
-
unpackBinaryMessage,
|
|
14
10
|
packStringMessage,
|
|
15
11
|
unpackStringMessage,
|
|
16
12
|
} from '../common/sockets-utils.js';
|
|
17
13
|
|
|
18
|
-
|
|
19
|
-
//
|
|
20
|
-
// `close`:
|
|
21
|
-
// Fired when a connection with a websocket is closed.
|
|
22
|
-
// Also available via the onclose property
|
|
23
|
-
// `error`:
|
|
24
|
-
// Fired when a connection with a websocket has been closed because of an error,
|
|
25
|
-
// such as whensome data couldn't be sent.
|
|
26
|
-
// Also available via the onerror property.
|
|
27
|
-
// `message`:
|
|
28
|
-
// Fired when data is received through a websocket.
|
|
29
|
-
// Also available via the onmessage property.
|
|
30
|
-
// `open`:
|
|
31
|
-
// Fired when a connection with a websocket is opened.
|
|
32
|
-
// Also available via the onopen property.
|
|
14
|
+
export const kSocketTerminate = Symbol('soundworks:socket-terminate');
|
|
33
15
|
|
|
34
16
|
/**
|
|
35
17
|
* The Socket class is a simple publish / subscribe wrapper built on top of the
|
|
@@ -53,26 +35,10 @@ import {
|
|
|
53
35
|
* @hideconstructor
|
|
54
36
|
*/
|
|
55
37
|
class Socket {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
* @private
|
|
61
|
-
*/
|
|
62
|
-
this.ws = null;
|
|
63
|
-
/**
|
|
64
|
-
* WebSocket instance configured with `binaryType = 'arraybuffer'`.
|
|
65
|
-
*
|
|
66
|
-
* @private
|
|
67
|
-
*/
|
|
68
|
-
this.binaryWs = null;
|
|
69
|
-
/** @private */
|
|
70
|
-
this._stringListeners = new Map();
|
|
71
|
-
/** @private */
|
|
72
|
-
this._binaryListeners = new Map();
|
|
73
|
-
/** @private */
|
|
74
|
-
this._heartbeatId = null;
|
|
75
|
-
}
|
|
38
|
+
#socket = null;
|
|
39
|
+
#listeners = new Map();
|
|
40
|
+
|
|
41
|
+
constructor() {}
|
|
76
42
|
|
|
77
43
|
/**
|
|
78
44
|
* Initialize a websocket connection with the server. Automatically called
|
|
@@ -80,114 +46,97 @@ class Socket {
|
|
|
80
46
|
*
|
|
81
47
|
* @param {string} role - Role of the client (see {@link client.Client#role})
|
|
82
48
|
* @param {object} config - Configuration of the sockets
|
|
83
|
-
* @
|
|
84
|
-
* @ignore
|
|
49
|
+
* @private
|
|
85
50
|
*/
|
|
86
51
|
async init(role, config) {
|
|
87
|
-
// unique key that allows to associate the two sockets to the same client.
|
|
88
|
-
// note: the key is only used to pair to two sockets, so its usage is very
|
|
89
|
-
// limited in time therefore a random number should hopefully be sufficient.
|
|
90
|
-
const key = (Math.random() + '').replace(/^0\./, '');
|
|
91
|
-
|
|
92
|
-
// open web sockets
|
|
93
52
|
let { path } = config.env.websockets;
|
|
94
|
-
|
|
95
|
-
// @note: keep this check
|
|
96
|
-
// backward compatibility w/ assetsDomain and websocket old config way
|
|
97
53
|
// cf. https://github.com/collective-soundworks/soundworks/issues/35
|
|
98
54
|
if (config.env.subpath) {
|
|
99
55
|
path = `${config.env.subpath}/${path}`;
|
|
100
56
|
}
|
|
101
57
|
|
|
102
|
-
|
|
58
|
+
const protocol = config.env.useHttps ? 'wss:' : 'ws:';
|
|
59
|
+
const port = config.env.port;
|
|
60
|
+
let serverAddress;
|
|
103
61
|
let webSocketOptions;
|
|
104
62
|
|
|
105
63
|
if (isBrowser()) {
|
|
106
|
-
|
|
107
|
-
|
|
64
|
+
// if a server address is given in config, use it, else fallback to URL hostname
|
|
65
|
+
if (config.env.serverAddress !== '') {
|
|
66
|
+
serverAddress = config.env.serverAddress;
|
|
67
|
+
} else {
|
|
68
|
+
serverAddress = window.location.hostname;
|
|
69
|
+
}
|
|
108
70
|
|
|
109
|
-
url = `${protocol}//${hostname}:${port}/${path}`;
|
|
110
71
|
webSocketOptions = [];
|
|
111
72
|
} else {
|
|
112
|
-
|
|
113
|
-
const { serverAddress, port } = config.env;
|
|
73
|
+
serverAddress = config.env.serverAddress;
|
|
114
74
|
|
|
115
|
-
url = `${protocol}//${serverAddress}:${port}/${path}`;
|
|
116
75
|
webSocketOptions = {
|
|
117
76
|
rejectUnauthorized: false,
|
|
118
77
|
};
|
|
119
78
|
}
|
|
120
79
|
|
|
121
|
-
let queryParams = `role=${role}
|
|
80
|
+
let queryParams = `role=${role}`;
|
|
122
81
|
|
|
123
82
|
if (config.token) {
|
|
124
83
|
queryParams += `&token=${config.token}`;
|
|
125
84
|
}
|
|
126
85
|
|
|
86
|
+
const url = `${protocol}//${serverAddress}:${port}/${path}?${queryParams}`;
|
|
87
|
+
|
|
127
88
|
// ----------------------------------------------------------
|
|
128
|
-
//
|
|
89
|
+
// Init socket
|
|
129
90
|
// ----------------------------------------------------------
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
await new Promise(resolve => {
|
|
91
|
+
return new Promise(resolve => {
|
|
133
92
|
let connectionRefusedLogged = false;
|
|
134
93
|
|
|
135
94
|
const trySocket = async () => {
|
|
136
|
-
const ws = new WebSocket(
|
|
95
|
+
const ws = new WebSocket(url, webSocketOptions);
|
|
137
96
|
|
|
138
|
-
ws.addEventListener('open',
|
|
97
|
+
ws.addEventListener('open', openEvent => {
|
|
139
98
|
// parse incoming messages for pubsub
|
|
140
|
-
this
|
|
141
|
-
|
|
142
|
-
// Heartbeat ping / pong check, to detect if connection has been
|
|
143
|
-
// broken but not detected by the client
|
|
144
|
-
// @note (2024-04): unstable and creates more issues than it solves, disable
|
|
145
|
-
// for now and review later
|
|
146
|
-
// const heartbeat = () => {
|
|
147
|
-
// clearTimeout(this._heartbeatId);
|
|
148
|
-
|
|
149
|
-
// this._heartbeatId = setTimeout(() => {
|
|
150
|
-
// console.log('> HEARTBEAT missed, closing...');
|
|
151
|
-
// this.terminate();
|
|
152
|
-
// }, PING_INTERVAL + PING_LATENCY_TOLERANCE);
|
|
153
|
-
// };
|
|
99
|
+
this.#socket = ws;
|
|
154
100
|
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
this.ws.addEventListener('message', e => {
|
|
101
|
+
this.#socket.addEventListener('message', e => {
|
|
158
102
|
if (e.data === PING_MESSAGE) {
|
|
159
103
|
// heartbeat();
|
|
160
|
-
this.
|
|
104
|
+
this.#socket.send(PONG_MESSAGE);
|
|
161
105
|
// do not propagate ping / pong messages
|
|
162
106
|
return;
|
|
163
107
|
}
|
|
164
108
|
|
|
165
109
|
const [channel, args] = unpackStringMessage(e.data);
|
|
166
|
-
this
|
|
110
|
+
this.#dispatchEvent(channel, ...args);
|
|
167
111
|
});
|
|
168
112
|
|
|
169
|
-
//
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
113
|
+
// propagate "native" events
|
|
114
|
+
// `close`:
|
|
115
|
+
// Fired when a connection with a websocket is closed.
|
|
116
|
+
// Also available via the onclose property
|
|
117
|
+
// `error`:
|
|
118
|
+
// Fired when a connection with a websocket has been closed because of an error,
|
|
119
|
+
// such as whensome data couldn't be sent.
|
|
120
|
+
// Also available via the onerror property.
|
|
121
|
+
// `message`:
|
|
122
|
+
// Fired when data is received through a websocket.
|
|
123
|
+
// Also available via the onmessage property.
|
|
124
|
+
// `open`:
|
|
125
|
+
// Fired when a connection with a websocket is opened.
|
|
126
|
+
// Also available via the onopen property.
|
|
127
|
+
[
|
|
128
|
+
'close',
|
|
129
|
+
'error',
|
|
130
|
+
'upgrade',
|
|
131
|
+
'message',
|
|
132
|
+
].forEach(eventName => {
|
|
133
|
+
this.#socket.addEventListener(eventName, e => {
|
|
134
|
+
this.#dispatchEvent(eventName, e);
|
|
135
|
+
});
|
|
136
|
+
})
|
|
187
137
|
|
|
188
138
|
// forward open event
|
|
189
|
-
this
|
|
190
|
-
|
|
139
|
+
this.#dispatchEvent('open', openEvent);
|
|
191
140
|
// continue with raw socket
|
|
192
141
|
resolve();
|
|
193
142
|
});
|
|
@@ -203,11 +152,13 @@ class Socket {
|
|
|
203
152
|
|
|
204
153
|
// for node clients, retry connection
|
|
205
154
|
if (e.error && e.error.code === 'ECONNREFUSED') {
|
|
155
|
+
// we want to log the warning just once
|
|
206
156
|
if (!connectionRefusedLogged) {
|
|
207
157
|
logger.log('[soundworks.Socket] Connection refused, waiting for the server to start');
|
|
208
158
|
connectionRefusedLogged = true;
|
|
209
159
|
}
|
|
210
160
|
|
|
161
|
+
// retry in 1 second
|
|
211
162
|
setTimeout(trySocket, 1000);
|
|
212
163
|
}
|
|
213
164
|
}
|
|
@@ -216,39 +167,6 @@ class Socket {
|
|
|
216
167
|
|
|
217
168
|
trySocket();
|
|
218
169
|
});
|
|
219
|
-
|
|
220
|
-
// ----------------------------------------------------------
|
|
221
|
-
// init binary socket
|
|
222
|
-
// ----------------------------------------------------------
|
|
223
|
-
const binarySocketUrl = `${url}?binary=1&${queryParams}`;
|
|
224
|
-
|
|
225
|
-
await new Promise(resolve => {
|
|
226
|
-
const ws = new WebSocket(binarySocketUrl, webSocketOptions);
|
|
227
|
-
ws.binaryType = 'arraybuffer';
|
|
228
|
-
|
|
229
|
-
ws.addEventListener('open', connectEvent => {
|
|
230
|
-
// parse incoming messages for pubsub
|
|
231
|
-
this.binaryWs = ws;
|
|
232
|
-
this.binaryWs.addEventListener('message', e => {
|
|
233
|
-
const [channel, data] = unpackBinaryMessage(e.data);
|
|
234
|
-
this._emit(true, channel, data);
|
|
235
|
-
});
|
|
236
|
-
|
|
237
|
-
// broadcast all `WebSocket` native events
|
|
238
|
-
['close', 'error', 'upgrade', 'message'].forEach(eventName => {
|
|
239
|
-
this.binaryWs.addEventListener(eventName, (e) => {
|
|
240
|
-
this._emit(true, eventName, e);
|
|
241
|
-
});
|
|
242
|
-
});
|
|
243
|
-
|
|
244
|
-
// forward open event
|
|
245
|
-
this._emit(true, 'open', connectEvent);
|
|
246
|
-
resolve();
|
|
247
|
-
});
|
|
248
|
-
});
|
|
249
|
-
|
|
250
|
-
// wait for both sockets connected
|
|
251
|
-
return Promise.resolve();
|
|
252
170
|
}
|
|
253
171
|
|
|
254
172
|
/**
|
|
@@ -257,28 +175,18 @@ class Socket {
|
|
|
257
175
|
*
|
|
258
176
|
* Is also called when a disconnection is detected by the heartbeat (note that
|
|
259
177
|
* in this case, the launcher will call `client.stop()` but the listeners are
|
|
260
|
-
* already cleared so the event will be trigerred only once
|
|
261
|
-
*
|
|
262
|
-
* @private
|
|
178
|
+
* already cleared so the event will be trigerred only once.
|
|
263
179
|
*/
|
|
264
|
-
async
|
|
265
|
-
const closeListeners = this.
|
|
266
|
-
const closeBinaryListeners = this._binaryListeners.get('close');
|
|
267
|
-
|
|
180
|
+
async [kSocketTerminate]() {
|
|
181
|
+
const closeListeners = this.#listeners.get('close');
|
|
268
182
|
this.removeAllListeners();
|
|
269
|
-
this.removeAllBinaryListeners();
|
|
270
183
|
|
|
271
|
-
this.
|
|
272
|
-
this.binaryWs.close();
|
|
184
|
+
this.#socket.close();
|
|
273
185
|
|
|
274
186
|
if (closeListeners) {
|
|
275
187
|
closeListeners.forEach(listener => listener());
|
|
276
188
|
}
|
|
277
189
|
|
|
278
|
-
if (closeBinaryListeners) {
|
|
279
|
-
closeBinaryListeners.forEach(listener => listener());
|
|
280
|
-
}
|
|
281
|
-
|
|
282
190
|
return Promise.resolve();
|
|
283
191
|
}
|
|
284
192
|
|
|
@@ -286,63 +194,14 @@ class Socket {
|
|
|
286
194
|
* @param {boolean} binary - Emit to either the string or binary socket.
|
|
287
195
|
* @param {string} channel - Channel name.
|
|
288
196
|
* @param {...*} args - Content of the message.
|
|
289
|
-
* @private
|
|
290
197
|
*/
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
if (listeners.has(channel)) {
|
|
295
|
-
const callbacks = listeners.get(channel);
|
|
198
|
+
#dispatchEvent(channel, ...args) {
|
|
199
|
+
if (this.#listeners.has(channel)) {
|
|
200
|
+
const callbacks = this.#listeners.get(channel);
|
|
296
201
|
callbacks.forEach(callback => callback(...args));
|
|
297
202
|
}
|
|
298
203
|
}
|
|
299
204
|
|
|
300
|
-
/**
|
|
301
|
-
* @param {Function[]} listeners - List of listeners, either for the string or binary socket.
|
|
302
|
-
* @param {string} channel - Channel name.
|
|
303
|
-
* @param {Function} callback - The function to be added to the listeners.
|
|
304
|
-
* @private
|
|
305
|
-
*/
|
|
306
|
-
_addListener(listeners, channel, callback) {
|
|
307
|
-
if (!listeners.has(channel)) {
|
|
308
|
-
listeners.set(channel, new Set());
|
|
309
|
-
}
|
|
310
|
-
|
|
311
|
-
const callbacks = listeners.get(channel);
|
|
312
|
-
callbacks.add(callback);
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
/**
|
|
316
|
-
* @param {Function[]} listeners - List of listeners, either for the string or binary socket.
|
|
317
|
-
* @param {string} channel - Channel name.
|
|
318
|
-
* @param {Function} callback - The function to be removed from the listeners.
|
|
319
|
-
* @private
|
|
320
|
-
*/
|
|
321
|
-
_removeListener(listeners, channel, callback) {
|
|
322
|
-
if (listeners.has(channel)) {
|
|
323
|
-
const callbacks = listeners.get(channel);
|
|
324
|
-
callbacks.delete(callback);
|
|
325
|
-
|
|
326
|
-
if (callbacks.size === 0) {
|
|
327
|
-
listeners.delete(channel);
|
|
328
|
-
}
|
|
329
|
-
}
|
|
330
|
-
}
|
|
331
|
-
|
|
332
|
-
/**
|
|
333
|
-
* @param {Function[]} listeners - List of listeners, either for the string or binary socket.
|
|
334
|
-
* @param {string} [channel=null] - Channel name of the listeners to remove. If null
|
|
335
|
-
* all the listeners are cleared.
|
|
336
|
-
* @private
|
|
337
|
-
*/
|
|
338
|
-
_removeAllListeners(listeners, channel = null) {
|
|
339
|
-
if (channel === null) {
|
|
340
|
-
listeners.clear();
|
|
341
|
-
} else if (listeners.has(channel)) {
|
|
342
|
-
listeners.delete(channel);
|
|
343
|
-
}
|
|
344
|
-
}
|
|
345
|
-
|
|
346
205
|
/**
|
|
347
206
|
* Send messages with JSON compatible data types on a given channel.
|
|
348
207
|
*
|
|
@@ -351,8 +210,10 @@ class Socket {
|
|
|
351
210
|
* JSON compatible data types (i.e. string, number, boolean, object, array and null).
|
|
352
211
|
*/
|
|
353
212
|
send(channel, ...args) {
|
|
354
|
-
|
|
355
|
-
|
|
213
|
+
if (this.#socket.readyState === 1) {
|
|
214
|
+
const msg = packStringMessage(channel, ...args);
|
|
215
|
+
this.#socket.send(msg);
|
|
216
|
+
}
|
|
356
217
|
}
|
|
357
218
|
|
|
358
219
|
/**
|
|
@@ -364,7 +225,12 @@ class Socket {
|
|
|
364
225
|
* {@link server.Socket#send} method.
|
|
365
226
|
*/
|
|
366
227
|
addListener(channel, callback) {
|
|
367
|
-
this.
|
|
228
|
+
if (!this.#listeners.has(channel)) {
|
|
229
|
+
this.#listeners.set(channel, new Set());
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
const callbacks = this.#listeners.get(channel);
|
|
233
|
+
callbacks.add(callback);
|
|
368
234
|
}
|
|
369
235
|
|
|
370
236
|
/**
|
|
@@ -374,7 +240,14 @@ class Socket {
|
|
|
374
240
|
* @param {Function} callback - Callback to remove.
|
|
375
241
|
*/
|
|
376
242
|
removeListener(channel, callback) {
|
|
377
|
-
this.
|
|
243
|
+
if (this.#listeners.has(channel)) {
|
|
244
|
+
const callbacks = this.#listeners.get(channel);
|
|
245
|
+
callbacks.delete(callback);
|
|
246
|
+
|
|
247
|
+
if (callbacks.size === 0) {
|
|
248
|
+
this.#listeners.delete(channel);
|
|
249
|
+
}
|
|
250
|
+
}
|
|
378
251
|
}
|
|
379
252
|
|
|
380
253
|
/**
|
|
@@ -383,47 +256,11 @@ class Socket {
|
|
|
383
256
|
* @param {string} channel - Channel name.
|
|
384
257
|
*/
|
|
385
258
|
removeAllListeners(channel = null) {
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
*
|
|
392
|
-
* @param {string} channel - Channel name.
|
|
393
|
-
* @param {TypedArray} typedArray - Binary data to be sent.
|
|
394
|
-
*/
|
|
395
|
-
sendBinary(channel, typedArray) {
|
|
396
|
-
const msg = packBinaryMessage(channel, typedArray);
|
|
397
|
-
this.binaryWs.send(msg);
|
|
398
|
-
}
|
|
399
|
-
|
|
400
|
-
/**
|
|
401
|
-
* Listen binary messages on a given channel
|
|
402
|
-
*
|
|
403
|
-
* @param {string} channel - Channel name.
|
|
404
|
-
* @param {Function} callback - Callback to execute when a message is received.
|
|
405
|
-
*/
|
|
406
|
-
addBinaryListener(channel, callback) {
|
|
407
|
-
this._addListener(this._binaryListeners, channel, callback);
|
|
408
|
-
}
|
|
409
|
-
|
|
410
|
-
/**
|
|
411
|
-
* Remove a listener of binary compatible messages from a given channel
|
|
412
|
-
*
|
|
413
|
-
* @param {string} channel - Channel name.
|
|
414
|
-
* @param {Function} callback - Callback to remove.
|
|
415
|
-
*/
|
|
416
|
-
removeBinaryListener(channel, callback) {
|
|
417
|
-
this._removeListener(this._binaryListeners, channel, callback);
|
|
418
|
-
}
|
|
419
|
-
|
|
420
|
-
/**
|
|
421
|
-
* Remove all listeners of binary compatible messages on a given channel
|
|
422
|
-
*
|
|
423
|
-
* @param {string} channel - Channel of the message.
|
|
424
|
-
*/
|
|
425
|
-
removeAllBinaryListeners(channel = null) {
|
|
426
|
-
this._removeAllListeners(this._binaryListeners, channel);
|
|
259
|
+
if (channel === null) {
|
|
260
|
+
this.#listeners.clear();
|
|
261
|
+
} else if (this.#listeners.has(channel)) {
|
|
262
|
+
this.#listeners.delete(channel);
|
|
263
|
+
}
|
|
427
264
|
}
|
|
428
265
|
}
|
|
429
266
|
|
package/src/client/index.js
CHANGED
package/src/common/constants.js
CHANGED
package/src/common/logger.js
CHANGED
|
@@ -126,6 +126,21 @@ const logger = {
|
|
|
126
126
|
console.log(` ${name} ${chalk.red('errors')}`);
|
|
127
127
|
},
|
|
128
128
|
|
|
129
|
+
warnVersionDiscepancies(clientRole, clientVersion, serverVersion) {
|
|
130
|
+
console.warn(`
|
|
131
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
|
|
132
|
+
|
|
133
|
+
WARNING
|
|
134
|
+
|
|
135
|
+
Version discrepancies between server and "${clientRole}" client:
|
|
136
|
+
+ server: ${serverVersion} | client: ${clientVersion}
|
|
137
|
+
|
|
138
|
+
This might lead to unexpected behavior, you should consider to re-install your
|
|
139
|
+
dependencies on both your server and clients.
|
|
140
|
+
|
|
141
|
+
!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
|
|
142
|
+
},
|
|
143
|
+
|
|
129
144
|
log(msg) {
|
|
130
145
|
if (!this.verbose) {
|
|
131
146
|
return;
|
|
@@ -1,66 +1,3 @@
|
|
|
1
|
-
import 'fast-text-encoding';
|
|
2
|
-
|
|
3
|
-
const encoder = new TextEncoder('utf-8');
|
|
4
|
-
const decoder = new TextDecoder('utf-8');
|
|
5
|
-
|
|
6
|
-
const types = [
|
|
7
|
-
'Int8Array',
|
|
8
|
-
'Uint8Array',
|
|
9
|
-
'Uint8ClampedArray',
|
|
10
|
-
'Int16Array',
|
|
11
|
-
'Uint16Array',
|
|
12
|
-
'Int32Array',
|
|
13
|
-
'Uint32Array',
|
|
14
|
-
'Float32Array',
|
|
15
|
-
'Float64Array',
|
|
16
|
-
'BigInt64Array',
|
|
17
|
-
'BigUint64Array',
|
|
18
|
-
];
|
|
19
|
-
|
|
20
|
-
// @note - probably some room for optimizations
|
|
21
|
-
|
|
22
|
-
/** @private */
|
|
23
|
-
export function packBinaryMessage(channel, data) {
|
|
24
|
-
const channelBuffer = encoder.encode(channel);
|
|
25
|
-
const channelSize = channelBuffer.byteLength;
|
|
26
|
-
const startOffset = Math.ceil((channelSize + 3) / data.BYTES_PER_ELEMENT) * data.BYTES_PER_ELEMENT;
|
|
27
|
-
const bufferSize = startOffset + data.byteLength;
|
|
28
|
-
const typeName = data.constructor.name;
|
|
29
|
-
const typeIndex = types.indexOf(typeName);
|
|
30
|
-
|
|
31
|
-
if (typeIndex === -1) {
|
|
32
|
-
throw new Error(`Invalid TypedArray type: ${typeName}`);
|
|
33
|
-
}
|
|
34
|
-
|
|
35
|
-
const view = new Uint8Array(bufferSize);
|
|
36
|
-
view[0] = channelSize;
|
|
37
|
-
view[1] = typeIndex;
|
|
38
|
-
view[2] = startOffset;
|
|
39
|
-
|
|
40
|
-
view.set(channelBuffer, 3, channelSize);
|
|
41
|
-
view.set(new Uint8Array(data.buffer), startOffset, data.byteLength);
|
|
42
|
-
|
|
43
|
-
return view.buffer;
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
/** @private */
|
|
47
|
-
export function unpackBinaryMessage(buffer /* arraybuffer */) {
|
|
48
|
-
const infos = new Uint8Array(buffer, 0, 3);
|
|
49
|
-
const channelSize = infos[0];
|
|
50
|
-
const typeIndex = infos[1];
|
|
51
|
-
const startOffset = infos[2];
|
|
52
|
-
|
|
53
|
-
// need to slice as the library recreates a UInt8Array from the whole buffer
|
|
54
|
-
// @todo - see if this copy could be avoided (probably needs a pull request)
|
|
55
|
-
const channelBuffer = new Uint8Array(buffer.slice(3, 3 + channelSize));
|
|
56
|
-
const channel = decoder.decode(channelBuffer);
|
|
57
|
-
const type = types[typeIndex];
|
|
58
|
-
// slice (copy) the underlying ArrayBuffer to create a clean TypedArray
|
|
59
|
-
const data = new globalThis[type](buffer.slice(startOffset));
|
|
60
|
-
|
|
61
|
-
return [channel, data];
|
|
62
|
-
}
|
|
63
|
-
|
|
64
1
|
/** @private */
|
|
65
2
|
export function packStringMessage(channel, ...args) {
|
|
66
3
|
return JSON.stringify([channel, args]);
|
package/src/server/Plugin.js
CHANGED
|
@@ -33,6 +33,9 @@ import BasePlugin from '../common/BasePlugin.js';
|
|
|
33
33
|
* @inheritdoc
|
|
34
34
|
*/
|
|
35
35
|
class Plugin extends BasePlugin {
|
|
36
|
+
#server = null;
|
|
37
|
+
#clients = new Set();
|
|
38
|
+
|
|
36
39
|
/**
|
|
37
40
|
* @param {server.Server} server - The soundworks server instance.
|
|
38
41
|
* @param {string} id - User defined id of the plugin as defined in
|
|
@@ -40,16 +43,25 @@ class Plugin extends BasePlugin {
|
|
|
40
43
|
*/
|
|
41
44
|
constructor(server, id) {
|
|
42
45
|
super(id);
|
|
46
|
+
this.#server = server;
|
|
47
|
+
}
|
|
43
48
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
49
|
+
/**
|
|
50
|
+
* Instance of soundworks server.
|
|
51
|
+
* @type {server.Server}
|
|
52
|
+
* @see {@link server.Server}
|
|
53
|
+
*/
|
|
54
|
+
get server() {
|
|
55
|
+
return this.#server;
|
|
56
|
+
}
|
|
50
57
|
|
|
51
|
-
|
|
52
|
-
|
|
58
|
+
/**
|
|
59
|
+
* Set of the clients registered in the plugin.
|
|
60
|
+
* @type {Set<server.Client>}
|
|
61
|
+
* @see {@link server.Client}
|
|
62
|
+
*/
|
|
63
|
+
get clients() {
|
|
64
|
+
return this.#clients;
|
|
53
65
|
}
|
|
54
66
|
|
|
55
67
|
/**
|
|
@@ -60,7 +72,7 @@ class Plugin extends BasePlugin {
|
|
|
60
72
|
* @param {server.Client} client
|
|
61
73
|
*/
|
|
62
74
|
async addClient(client) {
|
|
63
|
-
this
|
|
75
|
+
this.#clients.add(client);
|
|
64
76
|
}
|
|
65
77
|
|
|
66
78
|
/**
|
|
@@ -71,7 +83,7 @@ class Plugin extends BasePlugin {
|
|
|
71
83
|
* @param {server.Client} client
|
|
72
84
|
*/
|
|
73
85
|
async removeClient(client) {
|
|
74
|
-
this
|
|
86
|
+
this.#clients.delete(client);
|
|
75
87
|
}
|
|
76
88
|
}
|
|
77
89
|
|