@soundworks/core 4.0.0-alpha.22 → 4.0.0-alpha.24
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 +13 -3
- package/package.json +13 -14
- package/src/client/Client.js +224 -188
- package/src/client/Socket.js +89 -241
- 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 +1 -1
- package/src/server/Plugin.js +22 -10
- package/src/server/Server.js +22 -46
- 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/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,20 +46,10 @@ 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}`;
|
|
@@ -105,13 +61,27 @@ class Socket {
|
|
|
105
61
|
let webSocketOptions;
|
|
106
62
|
|
|
107
63
|
if (isBrowser()) {
|
|
64
|
+
const hostname = window.location.hostname;
|
|
108
65
|
// if a server address is given in config, use it, else fallback to URL hostname
|
|
109
66
|
if (config.env.serverAddress !== '') {
|
|
110
67
|
serverAddress = config.env.serverAddress;
|
|
111
68
|
} else {
|
|
69
|
+
serverAddress = hostname;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
// when in https with self-signed certificates, we don't want to use
|
|
73
|
+
// the serverAddress defined in config as the socket would be blocked, so we
|
|
74
|
+
// just override serverAddress with hostname in this case
|
|
75
|
+
const localhosts = ['127.0.0.1', 'localhost'];
|
|
76
|
+
|
|
77
|
+
if (config.env.useHttps && localhosts.includes(hostname)) {
|
|
112
78
|
serverAddress = window.location.hostname;
|
|
113
79
|
}
|
|
114
80
|
|
|
81
|
+
if (config.env.useHttps && window.location.hostname !== serverAddress) {
|
|
82
|
+
console.warn(`The WebSocket will try to connect at ${serverAddress} as defined in environment configuration while the page is accessed from ${hostname}. If you run the application with self-signed certificates, this can lead to socket errors as the certificate may not have been accepted for ${serverAddress}. In such case you should rather access the page from ${serverAddress}.`);
|
|
83
|
+
}
|
|
84
|
+
|
|
115
85
|
webSocketOptions = [];
|
|
116
86
|
} else {
|
|
117
87
|
serverAddress = config.env.serverAddress;
|
|
@@ -121,77 +91,66 @@ class Socket {
|
|
|
121
91
|
};
|
|
122
92
|
}
|
|
123
93
|
|
|
124
|
-
|
|
125
|
-
let queryParams = `role=${role}&key=${key}`;
|
|
94
|
+
let queryParams = `role=${role}`;
|
|
126
95
|
|
|
127
96
|
if (config.token) {
|
|
128
97
|
queryParams += `&token=${config.token}`;
|
|
129
98
|
}
|
|
130
99
|
|
|
100
|
+
const url = `${protocol}//${serverAddress}:${port}/${path}?${queryParams}`;
|
|
101
|
+
|
|
131
102
|
// ----------------------------------------------------------
|
|
132
|
-
//
|
|
103
|
+
// Init socket
|
|
133
104
|
// ----------------------------------------------------------
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
await new Promise(resolve => {
|
|
105
|
+
return new Promise(resolve => {
|
|
137
106
|
let connectionRefusedLogged = false;
|
|
138
107
|
|
|
139
108
|
const trySocket = async () => {
|
|
140
|
-
const ws = new WebSocket(
|
|
109
|
+
const ws = new WebSocket(url, webSocketOptions);
|
|
141
110
|
|
|
142
|
-
ws.addEventListener('open',
|
|
111
|
+
ws.addEventListener('open', openEvent => {
|
|
143
112
|
// parse incoming messages for pubsub
|
|
144
|
-
this
|
|
145
|
-
|
|
146
|
-
// Heartbeat ping / pong check, to detect if connection has been
|
|
147
|
-
// broken but not detected by the client
|
|
148
|
-
// @note (2024-04): unstable and creates more issues than it solves, disable
|
|
149
|
-
// for now and review later
|
|
150
|
-
// const heartbeat = () => {
|
|
151
|
-
// clearTimeout(this._heartbeatId);
|
|
113
|
+
this.#socket = ws;
|
|
152
114
|
|
|
153
|
-
|
|
154
|
-
// console.log('> HEARTBEAT missed, closing...');
|
|
155
|
-
// this.terminate();
|
|
156
|
-
// }, PING_INTERVAL + PING_LATENCY_TOLERANCE);
|
|
157
|
-
// };
|
|
158
|
-
|
|
159
|
-
// heartbeat();
|
|
160
|
-
|
|
161
|
-
this.ws.addEventListener('message', e => {
|
|
115
|
+
this.#socket.addEventListener('message', e => {
|
|
162
116
|
if (e.data === PING_MESSAGE) {
|
|
163
117
|
// heartbeat();
|
|
164
|
-
this.
|
|
118
|
+
this.#socket.send(PONG_MESSAGE);
|
|
165
119
|
// do not propagate ping / pong messages
|
|
166
120
|
return;
|
|
167
121
|
}
|
|
168
122
|
|
|
169
123
|
const [channel, args] = unpackStringMessage(e.data);
|
|
170
|
-
this
|
|
171
|
-
});
|
|
172
|
-
|
|
173
|
-
// broadcast all `WebSocket` native events
|
|
174
|
-
this.ws.addEventListener('close', (e) => {
|
|
175
|
-
clearTimeout(this._heartbeatId);
|
|
176
|
-
this._emit(false, 'close', e);
|
|
124
|
+
this.#dispatchEvent(channel, ...args);
|
|
177
125
|
});
|
|
178
126
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
127
|
+
// propagate "native" events
|
|
128
|
+
// `close`:
|
|
129
|
+
// Fired when a connection with a websocket is closed.
|
|
130
|
+
// Also available via the onclose property
|
|
131
|
+
// `error`:
|
|
132
|
+
// Fired when a connection with a websocket has been closed because of an error,
|
|
133
|
+
// such as whensome data couldn't be sent.
|
|
134
|
+
// Also available via the onerror property.
|
|
135
|
+
// `message`:
|
|
136
|
+
// Fired when data is received through a websocket.
|
|
137
|
+
// Also available via the onmessage property.
|
|
138
|
+
// `open`:
|
|
139
|
+
// Fired when a connection with a websocket is opened.
|
|
140
|
+
// Also available via the onopen property.
|
|
141
|
+
[
|
|
142
|
+
'close',
|
|
143
|
+
'error',
|
|
144
|
+
'upgrade',
|
|
145
|
+
'message',
|
|
146
|
+
].forEach(eventName => {
|
|
147
|
+
this.#socket.addEventListener(eventName, e => {
|
|
148
|
+
this.#dispatchEvent(eventName, e);
|
|
149
|
+
});
|
|
150
|
+
})
|
|
191
151
|
|
|
192
152
|
// forward open event
|
|
193
|
-
this
|
|
194
|
-
|
|
153
|
+
this.#dispatchEvent('open', openEvent);
|
|
195
154
|
// continue with raw socket
|
|
196
155
|
resolve();
|
|
197
156
|
});
|
|
@@ -207,11 +166,14 @@ class Socket {
|
|
|
207
166
|
|
|
208
167
|
// for node clients, retry connection
|
|
209
168
|
if (e.error && e.error.code === 'ECONNREFUSED') {
|
|
169
|
+
// we want to log the warning just once
|
|
210
170
|
if (!connectionRefusedLogged) {
|
|
211
171
|
logger.log('[soundworks.Socket] Connection refused, waiting for the server to start');
|
|
172
|
+
console.log(e.error);
|
|
212
173
|
connectionRefusedLogged = true;
|
|
213
174
|
}
|
|
214
175
|
|
|
176
|
+
// retry in 1 second
|
|
215
177
|
setTimeout(trySocket, 1000);
|
|
216
178
|
}
|
|
217
179
|
}
|
|
@@ -220,39 +182,6 @@ class Socket {
|
|
|
220
182
|
|
|
221
183
|
trySocket();
|
|
222
184
|
});
|
|
223
|
-
|
|
224
|
-
// ----------------------------------------------------------
|
|
225
|
-
// init binary socket
|
|
226
|
-
// ----------------------------------------------------------
|
|
227
|
-
const binarySocketUrl = `${url}?binary=1&${queryParams}`;
|
|
228
|
-
|
|
229
|
-
await new Promise(resolve => {
|
|
230
|
-
const ws = new WebSocket(binarySocketUrl, webSocketOptions);
|
|
231
|
-
ws.binaryType = 'arraybuffer';
|
|
232
|
-
|
|
233
|
-
ws.addEventListener('open', connectEvent => {
|
|
234
|
-
// parse incoming messages for pubsub
|
|
235
|
-
this.binaryWs = ws;
|
|
236
|
-
this.binaryWs.addEventListener('message', e => {
|
|
237
|
-
const [channel, data] = unpackBinaryMessage(e.data);
|
|
238
|
-
this._emit(true, channel, data);
|
|
239
|
-
});
|
|
240
|
-
|
|
241
|
-
// broadcast all `WebSocket` native events
|
|
242
|
-
['close', 'error', 'upgrade', 'message'].forEach(eventName => {
|
|
243
|
-
this.binaryWs.addEventListener(eventName, (e) => {
|
|
244
|
-
this._emit(true, eventName, e);
|
|
245
|
-
});
|
|
246
|
-
});
|
|
247
|
-
|
|
248
|
-
// forward open event
|
|
249
|
-
this._emit(true, 'open', connectEvent);
|
|
250
|
-
resolve();
|
|
251
|
-
});
|
|
252
|
-
});
|
|
253
|
-
|
|
254
|
-
// wait for both sockets connected
|
|
255
|
-
return Promise.resolve();
|
|
256
185
|
}
|
|
257
186
|
|
|
258
187
|
/**
|
|
@@ -261,28 +190,18 @@ class Socket {
|
|
|
261
190
|
*
|
|
262
191
|
* Is also called when a disconnection is detected by the heartbeat (note that
|
|
263
192
|
* in this case, the launcher will call `client.stop()` but the listeners are
|
|
264
|
-
* already cleared so the event will be trigerred only once
|
|
265
|
-
*
|
|
266
|
-
* @private
|
|
193
|
+
* already cleared so the event will be trigerred only once.
|
|
267
194
|
*/
|
|
268
|
-
async
|
|
269
|
-
const closeListeners = this.
|
|
270
|
-
const closeBinaryListeners = this._binaryListeners.get('close');
|
|
271
|
-
|
|
195
|
+
async [kSocketTerminate]() {
|
|
196
|
+
const closeListeners = this.#listeners.get('close');
|
|
272
197
|
this.removeAllListeners();
|
|
273
|
-
this.removeAllBinaryListeners();
|
|
274
198
|
|
|
275
|
-
this.
|
|
276
|
-
this.binaryWs.close();
|
|
199
|
+
this.#socket.close();
|
|
277
200
|
|
|
278
201
|
if (closeListeners) {
|
|
279
202
|
closeListeners.forEach(listener => listener());
|
|
280
203
|
}
|
|
281
204
|
|
|
282
|
-
if (closeBinaryListeners) {
|
|
283
|
-
closeBinaryListeners.forEach(listener => listener());
|
|
284
|
-
}
|
|
285
|
-
|
|
286
205
|
return Promise.resolve();
|
|
287
206
|
}
|
|
288
207
|
|
|
@@ -290,63 +209,14 @@ class Socket {
|
|
|
290
209
|
* @param {boolean} binary - Emit to either the string or binary socket.
|
|
291
210
|
* @param {string} channel - Channel name.
|
|
292
211
|
* @param {...*} args - Content of the message.
|
|
293
|
-
* @private
|
|
294
212
|
*/
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
if (listeners.has(channel)) {
|
|
299
|
-
const callbacks = listeners.get(channel);
|
|
213
|
+
#dispatchEvent(channel, ...args) {
|
|
214
|
+
if (this.#listeners.has(channel)) {
|
|
215
|
+
const callbacks = this.#listeners.get(channel);
|
|
300
216
|
callbacks.forEach(callback => callback(...args));
|
|
301
217
|
}
|
|
302
218
|
}
|
|
303
219
|
|
|
304
|
-
/**
|
|
305
|
-
* @param {Function[]} listeners - List of listeners, either for the string or binary socket.
|
|
306
|
-
* @param {string} channel - Channel name.
|
|
307
|
-
* @param {Function} callback - The function to be added to the listeners.
|
|
308
|
-
* @private
|
|
309
|
-
*/
|
|
310
|
-
_addListener(listeners, channel, callback) {
|
|
311
|
-
if (!listeners.has(channel)) {
|
|
312
|
-
listeners.set(channel, new Set());
|
|
313
|
-
}
|
|
314
|
-
|
|
315
|
-
const callbacks = listeners.get(channel);
|
|
316
|
-
callbacks.add(callback);
|
|
317
|
-
}
|
|
318
|
-
|
|
319
|
-
/**
|
|
320
|
-
* @param {Function[]} listeners - List of listeners, either for the string or binary socket.
|
|
321
|
-
* @param {string} channel - Channel name.
|
|
322
|
-
* @param {Function} callback - The function to be removed from the listeners.
|
|
323
|
-
* @private
|
|
324
|
-
*/
|
|
325
|
-
_removeListener(listeners, channel, callback) {
|
|
326
|
-
if (listeners.has(channel)) {
|
|
327
|
-
const callbacks = listeners.get(channel);
|
|
328
|
-
callbacks.delete(callback);
|
|
329
|
-
|
|
330
|
-
if (callbacks.size === 0) {
|
|
331
|
-
listeners.delete(channel);
|
|
332
|
-
}
|
|
333
|
-
}
|
|
334
|
-
}
|
|
335
|
-
|
|
336
|
-
/**
|
|
337
|
-
* @param {Function[]} listeners - List of listeners, either for the string or binary socket.
|
|
338
|
-
* @param {string} [channel=null] - Channel name of the listeners to remove. If null
|
|
339
|
-
* all the listeners are cleared.
|
|
340
|
-
* @private
|
|
341
|
-
*/
|
|
342
|
-
_removeAllListeners(listeners, channel = null) {
|
|
343
|
-
if (channel === null) {
|
|
344
|
-
listeners.clear();
|
|
345
|
-
} else if (listeners.has(channel)) {
|
|
346
|
-
listeners.delete(channel);
|
|
347
|
-
}
|
|
348
|
-
}
|
|
349
|
-
|
|
350
220
|
/**
|
|
351
221
|
* Send messages with JSON compatible data types on a given channel.
|
|
352
222
|
*
|
|
@@ -355,8 +225,10 @@ class Socket {
|
|
|
355
225
|
* JSON compatible data types (i.e. string, number, boolean, object, array and null).
|
|
356
226
|
*/
|
|
357
227
|
send(channel, ...args) {
|
|
358
|
-
|
|
359
|
-
|
|
228
|
+
if (this.#socket.readyState === 1) {
|
|
229
|
+
const msg = packStringMessage(channel, ...args);
|
|
230
|
+
this.#socket.send(msg);
|
|
231
|
+
}
|
|
360
232
|
}
|
|
361
233
|
|
|
362
234
|
/**
|
|
@@ -368,7 +240,12 @@ class Socket {
|
|
|
368
240
|
* {@link server.Socket#send} method.
|
|
369
241
|
*/
|
|
370
242
|
addListener(channel, callback) {
|
|
371
|
-
this.
|
|
243
|
+
if (!this.#listeners.has(channel)) {
|
|
244
|
+
this.#listeners.set(channel, new Set());
|
|
245
|
+
}
|
|
246
|
+
|
|
247
|
+
const callbacks = this.#listeners.get(channel);
|
|
248
|
+
callbacks.add(callback);
|
|
372
249
|
}
|
|
373
250
|
|
|
374
251
|
/**
|
|
@@ -378,7 +255,14 @@ class Socket {
|
|
|
378
255
|
* @param {Function} callback - Callback to remove.
|
|
379
256
|
*/
|
|
380
257
|
removeListener(channel, callback) {
|
|
381
|
-
this.
|
|
258
|
+
if (this.#listeners.has(channel)) {
|
|
259
|
+
const callbacks = this.#listeners.get(channel);
|
|
260
|
+
callbacks.delete(callback);
|
|
261
|
+
|
|
262
|
+
if (callbacks.size === 0) {
|
|
263
|
+
this.#listeners.delete(channel);
|
|
264
|
+
}
|
|
265
|
+
}
|
|
382
266
|
}
|
|
383
267
|
|
|
384
268
|
/**
|
|
@@ -387,47 +271,11 @@ class Socket {
|
|
|
387
271
|
* @param {string} channel - Channel name.
|
|
388
272
|
*/
|
|
389
273
|
removeAllListeners(channel = null) {
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
*
|
|
396
|
-
* @param {string} channel - Channel name.
|
|
397
|
-
* @param {TypedArray} typedArray - Binary data to be sent.
|
|
398
|
-
*/
|
|
399
|
-
sendBinary(channel, typedArray) {
|
|
400
|
-
const msg = packBinaryMessage(channel, typedArray);
|
|
401
|
-
this.binaryWs.send(msg);
|
|
402
|
-
}
|
|
403
|
-
|
|
404
|
-
/**
|
|
405
|
-
* Listen binary messages on a given channel
|
|
406
|
-
*
|
|
407
|
-
* @param {string} channel - Channel name.
|
|
408
|
-
* @param {Function} callback - Callback to execute when a message is received.
|
|
409
|
-
*/
|
|
410
|
-
addBinaryListener(channel, callback) {
|
|
411
|
-
this._addListener(this._binaryListeners, channel, callback);
|
|
412
|
-
}
|
|
413
|
-
|
|
414
|
-
/**
|
|
415
|
-
* Remove a listener of binary compatible messages from a given channel
|
|
416
|
-
*
|
|
417
|
-
* @param {string} channel - Channel name.
|
|
418
|
-
* @param {Function} callback - Callback to remove.
|
|
419
|
-
*/
|
|
420
|
-
removeBinaryListener(channel, callback) {
|
|
421
|
-
this._removeListener(this._binaryListeners, channel, callback);
|
|
422
|
-
}
|
|
423
|
-
|
|
424
|
-
/**
|
|
425
|
-
* Remove all listeners of binary compatible messages on a given channel
|
|
426
|
-
*
|
|
427
|
-
* @param {string} channel - Channel of the message.
|
|
428
|
-
*/
|
|
429
|
-
removeAllBinaryListeners(channel = null) {
|
|
430
|
-
this._removeAllListeners(this._binaryListeners, channel);
|
|
274
|
+
if (channel === null) {
|
|
275
|
+
this.#listeners.clear();
|
|
276
|
+
} else if (this.#listeners.has(channel)) {
|
|
277
|
+
this.#listeners.delete(channel);
|
|
278
|
+
}
|
|
431
279
|
}
|
|
432
280
|
}
|
|
433
281
|
|
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/common/version.js
CHANGED
|
@@ -1,2 +1,2 @@
|
|
|
1
1
|
// [WARNING] This file is generated, do not edit
|
|
2
|
-
export default '4.0.0-alpha.
|
|
2
|
+
export default '4.0.0-alpha.24';
|
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
|
|