@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.
Files changed (85) hide show
  1. package/CHANGELOG.md +13 -3
  2. package/package.json +13 -14
  3. package/src/client/Client.js +224 -188
  4. package/src/client/Socket.js +89 -241
  5. package/src/common/constants.js +0 -1
  6. package/src/common/logger.js +15 -0
  7. package/src/common/sockets-utils.js +0 -63
  8. package/src/common/version.js +1 -1
  9. package/src/server/Plugin.js +22 -10
  10. package/src/server/Server.js +22 -46
  11. package/src/server/Socket.js +126 -250
  12. package/src/server/Sockets.js +103 -127
  13. package/src/server/audit-network-latency.worker.js +4 -3
  14. package/types/client/Client.d.ts +41 -8
  15. package/types/client/Client.d.ts.map +1 -1
  16. package/types/client/Context.d.ts +40 -14
  17. package/types/client/Context.d.ts.map +1 -1
  18. package/types/client/Plugin.d.ts +42 -41
  19. package/types/client/Plugin.d.ts.map +1 -1
  20. package/types/client/PluginManager.d.ts +26 -11
  21. package/types/client/PluginManager.d.ts.map +1 -1
  22. package/types/client/SharedState.d.ts +1 -1
  23. package/types/client/SharedState.d.ts.map +1 -1
  24. package/types/client/SharedStateCollection.d.ts +41 -0
  25. package/types/client/SharedStateCollection.d.ts.map +1 -0
  26. package/types/client/Socket.d.ts +41 -30
  27. package/types/client/Socket.d.ts.map +1 -1
  28. package/types/client/StateManager.d.ts +1 -1
  29. package/types/client/StateManager.d.ts.map +1 -1
  30. package/types/client/index.d.ts +0 -8
  31. package/types/common/BasePlugin.d.ts +24 -19
  32. package/types/common/BasePlugin.d.ts.map +1 -1
  33. package/types/common/BasePluginManager.d.ts +17 -5
  34. package/types/common/BasePluginManager.d.ts.map +1 -1
  35. package/types/common/BaseSharedState.d.ts +100 -29
  36. package/types/common/BaseSharedState.d.ts.map +1 -1
  37. package/types/common/BaseSharedStateCollection.d.ts +183 -0
  38. package/types/common/BaseSharedStateCollection.d.ts.map +1 -0
  39. package/types/common/BaseStateManager.d.ts +83 -18
  40. package/types/common/BaseStateManager.d.ts.map +1 -1
  41. package/types/common/BatchedTransport.d.ts +21 -0
  42. package/types/common/BatchedTransport.d.ts.map +1 -0
  43. package/types/common/ParameterBag.d.ts +61 -40
  44. package/types/common/ParameterBag.d.ts.map +1 -1
  45. package/types/common/PromiseStore.d.ts +11 -0
  46. package/types/common/PromiseStore.d.ts.map +1 -0
  47. package/types/common/SharedStatePrivate.d.ts +4 -3
  48. package/types/common/SharedStatePrivate.d.ts.map +1 -1
  49. package/types/common/constants.d.ts +10 -1
  50. package/types/common/constants.d.ts.map +1 -1
  51. package/types/common/logger.d.ts +1 -1
  52. package/types/common/sockets-utils.d.ts.map +1 -1
  53. package/types/common/version.d.ts +3 -0
  54. package/types/common/version.d.ts.map +1 -0
  55. package/types/server/Client.d.ts +5 -0
  56. package/types/server/Client.d.ts.map +1 -1
  57. package/types/server/Context.d.ts +162 -22
  58. package/types/server/Context.d.ts.map +1 -1
  59. package/types/server/ContextManager.d.ts +4 -4
  60. package/types/server/ContextManager.d.ts.map +1 -1
  61. package/types/server/Plugin.d.ts +56 -55
  62. package/types/server/Plugin.d.ts.map +1 -1
  63. package/types/server/PluginManager.d.ts +26 -6
  64. package/types/server/PluginManager.d.ts.map +1 -1
  65. package/types/server/Server.d.ts +207 -70
  66. package/types/server/Server.d.ts.map +1 -1
  67. package/types/server/SharedState.d.ts +1 -1
  68. package/types/server/SharedState.d.ts.map +1 -1
  69. package/types/server/SharedStateCollection.d.ts +41 -0
  70. package/types/server/SharedStateCollection.d.ts.map +1 -0
  71. package/types/server/Socket.d.ts +93 -57
  72. package/types/server/Socket.d.ts.map +1 -1
  73. package/types/server/Sockets.d.ts +32 -25
  74. package/types/server/Sockets.d.ts.map +1 -1
  75. package/types/server/StateManager.d.ts +242 -27
  76. package/types/server/StateManager.d.ts.map +1 -1
  77. package/types/server/audit-network-latency.worker.d.ts +2 -1
  78. package/types/server/audit-schema.d.ts +8 -8
  79. package/types/server/crypto.d.ts +3 -0
  80. package/types/server/crypto.d.ts.map +1 -0
  81. package/types/server/index.d.ts +0 -4
  82. package/types/common/promise-store.d.ts +0 -4
  83. package/types/common/promise-store.d.ts.map +0 -1
  84. package/types/common/utils.d.ts +0 -5
  85. package/types/common/utils.d.ts.map +0 -1
package/CHANGELOG.md CHANGED
@@ -1,14 +1,24 @@
1
+ ## v4.0.0-alpha.24 - 24/05/2025
2
+
3
+ - Fix: unexpected behavior with new `serverAddress` behavior on browser clients and self-signed certificates
4
+
5
+ ## v4.0.0-alpha.23 - 24/05/2025
6
+
7
+ - Breaking: remove binary socket
8
+ - Fix: do not terminate socket on missed heartbeat, which seems to create false positive, just warn instead
9
+ - Refactor: some work toward upgrading code to es2022
10
+
1
11
  ## v4.0.0-alpha.22 - 16/05/2025
2
12
 
3
13
  - Feat: allow configuring socket endpoint on browser clients
4
- - Check and warn if version inconsistency between server and client
14
+ - Feat: check and warn if version inconsistency between server and client
5
15
 
6
16
  ## v4.0.0-alpha.21 - 6/05/2025
7
17
 
8
18
  - Fix: prevent multiple 'close' events
9
19
  - Fix: disable heartbeat check on client-side, was unstable
10
- - Feat(SharedState): introduce 'local' option for parameters
11
- - Feat(SharedState): ability to filter parameters of interest
20
+ - Feat: introduce 'local' option for SharedState parameters
21
+ - Feat: ability to filter parameters of interest in SharedState
12
22
 
13
23
  ## v4.0.0-alpha.20 - 19/02/2024
14
24
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soundworks/core",
3
- "version": "4.0.0-alpha.22",
3
+ "version": "4.0.0-alpha.24",
4
4
  "description": "Open-source creative coding framework for distributed applications based on Web technologies",
5
5
  "authors": [
6
6
  "Benjamin Matuszewski",
@@ -52,37 +52,36 @@
52
52
  "postversion": "node ./misc/scripts/check-changelog.js && git commit -am \"$npm_new_version\" --allow-empty"
53
53
  },
54
54
  "dependencies": {
55
- "@ircam/sc-utils": "^1.3.2",
55
+ "@ircam/sc-utils": "^1.3.3",
56
56
  "chalk": "^5.3.0",
57
57
  "columnify": "^1.6.0",
58
58
  "compression": "^1.7.1",
59
- "express": "^4.18.1",
59
+ "express": "^4.19.2",
60
60
  "fast-deep-equal": "^3.1.3",
61
- "fast-text-encoding": "^1.0.0",
62
61
  "isomorphic-ws": "^5.0.0",
63
62
  "keyv": "^4.5.4",
64
- "keyv-file": "^0.3.0",
63
+ "keyv-file": "^0.3.1",
65
64
  "lodash": "^4.17.21",
66
65
  "pem": "^1.14.8",
67
66
  "template-literal": "^1.0.4",
68
67
  "uuid": "^9.0.1",
69
- "ws": "^8.15.0"
68
+ "ws": "^8.17.0"
70
69
  },
71
70
  "devDependencies": {
72
71
  "@ircam/eslint-config": "^1.3.0",
73
- "chai": "^4.3.10",
72
+ "chai": "^5.1.1",
74
73
  "chai-shallow-deep-equal": "^1.4.6",
75
74
  "commitizen": "^4.2.5",
76
75
  "cz-conventional-changelog": "^3.3.0",
77
76
  "docdash": "^2.0.2",
78
- "dotenv": "^16.3.1",
79
- "eslint": "^8.55.0",
80
- "eslint-plugin-jsdoc": "^46.9.0",
81
- "jsdoc": "^4.0.0",
82
- "mocha": "^10.2.0",
83
- "puppeteer": "^21.6.0",
77
+ "dotenv": "^16.4.5",
78
+ "eslint": "^8.0.0",
79
+ "eslint-plugin-jsdoc": "^48.2.5",
80
+ "jsdoc": "^4.0.3",
81
+ "mocha": "^10.4.0",
82
+ "puppeteer": "^22.9.0",
84
83
  "tcp-ping": "^0.1.1",
85
- "typescript": "^5.3.3"
84
+ "typescript": "^5.4.5"
86
85
  },
87
86
  "optionalDependencies": {
88
87
  "bufferutil": "^4.0.5",
@@ -2,8 +2,11 @@ import { isBrowser, isPlainObject } from '@ircam/sc-utils';
2
2
 
3
3
  import ContextManager from './ContextManager.js';
4
4
  import PluginManager from './PluginManager.js';
5
- import Socket from './Socket.js';
5
+ import Socket, {
6
+ kSocketTerminate,
7
+ } from './Socket.js';
6
8
  import StateManager from './StateManager.js';
9
+
7
10
  import {
8
11
  CLIENT_HANDSHAKE_REQUEST,
9
12
  CLIENT_HANDSHAKE_RESPONSE,
@@ -13,6 +16,9 @@ import {
13
16
  import logger from '../common/logger.js';
14
17
  import version from '../common/version.js';
15
18
 
19
+ // for testing purposes
20
+ export const kClientVersionTest = Symbol('soundworks:client-version-test');
21
+
16
22
  /**
17
23
  * Configuration object for a client running in a browser runtime.
18
24
  *
@@ -52,6 +58,22 @@ import version from '../common/version.js';
52
58
  * @memberof client
53
59
  */
54
60
  class Client {
61
+ #version = null;
62
+ #role = null;
63
+ #config = null;
64
+ #id = null;
65
+ #uuid = null;
66
+ #target = null;
67
+ #socket = null;
68
+ #contextManager = null;
69
+ #pluginManager = null;
70
+ #stateManager = null;
71
+ #status = 'idle';
72
+ // Token of the client if connected through HTTP authentication.
73
+ #token = null;
74
+ #onStatusChangeCallbacks = new Set();
75
+ #auditState = null;
76
+
55
77
  /**
56
78
  * @param {client.ClientConfig} config - Configuration of the soundworks client.
57
79
  * @throws Will throw if the given config object is invalid.
@@ -90,121 +112,168 @@ class Client {
90
112
  }
91
113
  }
92
114
 
93
- /**
94
- * package version
95
- * @type string
96
- * @readonly
97
- */
98
- this.version = version;
99
-
100
- /**
101
- * Role of the client in the application.
102
- *
103
- * @type {string}
104
- */
105
- this.role = config.role;
106
-
107
- /**
108
- * Configuration object.
109
- *
110
- * @type {client.ClientConfig}
111
- */
112
- this.config = config;
113
-
114
- if (!this.config.env) {
115
- this.config.env = {};
115
+ this.#config = config;
116
+
117
+ if (!this.#config.env) {
118
+ this.#config.env = {};
116
119
  }
117
120
 
118
121
  // minimal configuration for websockets
119
- this.config.env.websockets = Object.assign({
122
+ this.#config.env.websockets = Object.assign({
120
123
  path: 'socket',
121
124
  pingInterval: 5000,
122
125
  }, config.env.websockets);
123
126
 
124
- /**
125
- * Session id of the client (incremeted positive number), generated and
126
- * retrieved by the server during `client.init`. The counter is reset when
127
- * the server restarts.
128
- *
129
- * @type {number}
130
- */
131
- this.id = null;
132
-
133
- /**
134
- * Unique session uuid of the client (uuidv4), generated and retrieved by
135
- * the server during {@link client.Client#init}.
136
- *
137
- * @type {string}
138
- */
139
- this.uuid = null;
140
-
141
- /**
142
- * Instance of the {@link client.Socket} class that handle websockets communications with
143
- * the server.
144
- *
145
- * @see {@link client.Socket}
146
- * @type {client.Socket}
147
- */
148
- this.socket = new Socket();
149
-
150
- /**
151
- * Runtime platform on which the client is executed, i.e. 'browser' or 'node'.
152
- *
153
- * @type {string}
154
- */
155
- this.target = isBrowser() ? 'browser' : 'node';
156
-
157
- /**
158
- * Instance of the {@link client.ContextManager} class.
159
- *
160
- * The context manager can be safely used after `client.init()` has been fulfilled.
161
- *
162
- * @see {@link client.ContextManager}
163
- * @type {client.ContextManager}
164
- */
165
- this.contextManager = new ContextManager();
166
-
167
- /**
168
- * Instance of the {@link client.PluginManager} class.
169
- *
170
- * The context manager can be safely used after `client.init()` has been fulfilled.
171
- *
172
- * @see {@link client.PluginManager}
173
- * @type {client.PluginManager}
174
- */
175
- this.pluginManager = new PluginManager(this);
176
-
177
- /**
178
- * Instance of the {@link client.StateManager} class.
179
- *
180
- * The context manager can be safely used after `client.init()` has been fulfilled.
181
- *
182
- * @see {@link client.StateManager}
183
- * @type {client.StateManager}
184
- */
185
- this.stateManager = new StateManager();
186
-
187
- /**
188
- * Status of the client, 'idle', 'inited', 'started' or 'errored'.
189
- *
190
- * @type {string}
191
- */
192
- this.status = 'idle';
193
-
194
- /**
195
- * Token of the client if connected through HTTP authentication.
196
- * @private
197
- */
198
- this.token = null;
127
+ this.#version = version;
128
+ // allow override though config for testing
129
+ if (config[kClientVersionTest]) {
130
+ this.#version = config[kClientVersionTest];
131
+ }
199
132
 
200
- /** @private */
201
- this._onStatusChangeCallbacks = new Set();
202
- /** @private */
203
- this._auditState = null;
133
+ this.#role = config.role;
134
+ this.#target = isBrowser() ? 'browser' : 'node';
135
+ this.#socket = new Socket();
136
+ this.#contextManager = new ContextManager();
137
+ this.#pluginManager = new PluginManager(this);
138
+ this.#stateManager = new StateManager();
139
+ this.#status = 'idle';
204
140
 
205
141
  logger.configure(!!config.env.verbose);
206
142
  }
207
143
 
144
+ /**
145
+ * Package version.
146
+ *
147
+ * @type {string}
148
+ */
149
+ get version() {
150
+ return this.#version;
151
+ }
152
+
153
+ /**
154
+ * Role of the client in the application.
155
+ *
156
+ * @type {string}
157
+ */
158
+ get role() {
159
+ return this.#role;
160
+ }
161
+
162
+ /**
163
+ * Configuration object.
164
+ *
165
+ * @type {client.ClientConfig}
166
+ */
167
+ get config() {
168
+ return this.#config;
169
+ }
170
+
171
+ /**
172
+ * Session id of the client.
173
+ *
174
+ * Incremeted positive integer generated and retrieved by the server during
175
+ * `client.init`. The counter is reset when the server restarts.
176
+ *
177
+ * @type {number}
178
+ */
179
+ get id() {
180
+ return this.#id;
181
+ }
182
+
183
+ /**
184
+ * Unique session uuid of the client (uuidv4).
185
+ *
186
+ * Generated and retrieved by the server during {@link client.Client#init}.
187
+ * @type {string}
188
+ */
189
+ get uuid() {
190
+ return this.#uuid;
191
+ }
192
+
193
+
194
+ /**
195
+ * Runtime platform on which the client is executed, i.e. 'browser' or 'node'.
196
+ *
197
+ * @type {string}
198
+ */
199
+ get target() {
200
+ return this.#target;
201
+ }
202
+
203
+ /**
204
+ * Instance of the {@link client.Socket} class.
205
+ *
206
+ * @see {@link client.Socket}
207
+ * @type {client.Socket}
208
+ */
209
+ get socket() {
210
+ return this.#socket;
211
+ }
212
+
213
+ /**
214
+ * Instance of the {@link client.ContextManager} class.
215
+ *
216
+ * The context manager can be safely used after `client.init()` has been fulfilled.
217
+ *
218
+ * @see {@link client.ContextManager}
219
+ * @type {client.ContextManager}
220
+ */
221
+ get contextManager() {
222
+ return this.#contextManager;
223
+ }
224
+
225
+ /**
226
+ * Instance of the {@link client.PluginManager} class.
227
+ *
228
+ * The context manager can be safely used after `client.init()` has been fulfilled.
229
+ *
230
+ * @see {@link client.PluginManager}
231
+ * @type {client.PluginManager}
232
+ */
233
+ get pluginManager() {
234
+ return this.#pluginManager;
235
+ }
236
+
237
+ /**
238
+ * Instance of the {@link client.StateManager} class.
239
+ *
240
+ * The context manager can be safely used after `client.init()` has been fulfilled.
241
+ *
242
+ * @see {@link client.StateManager}
243
+ * @type {client.StateManager}
244
+ */
245
+ get stateManager() {
246
+ return this.#stateManager;
247
+ }
248
+
249
+ /**
250
+ * Status of the client, 'idle', 'inited', 'started' or 'errored'.
251
+ *
252
+ * @type {string}
253
+ */
254
+ get status() {
255
+ return this.#status;
256
+ }
257
+
258
+ /** @private */
259
+ async #dispatchStatus(status) {
260
+ this.#status = status;
261
+
262
+ // if node target and launched in a child process, forward status to parent process
263
+ if (this.#target === 'node' && process.send !== undefined) {
264
+ process.send(`soundworks:client:${status}`);
265
+ }
266
+
267
+ // execute all callbacks in parallel
268
+ const promises = [];
269
+
270
+ for (let callback of this.#onStatusChangeCallbacks) {
271
+ promises.push(callback(status));
272
+ }
273
+
274
+ await Promise.all(promises);
275
+ }
276
+
208
277
  /**
209
278
  * The `init` method is part of the initialization lifecycle of the `soundworks`
210
279
  * client. Most of the time, the `init` method will be implicitly called by the
@@ -232,37 +301,26 @@ class Client {
232
301
  * await client.start();
233
302
  */
234
303
  async init() {
235
- // init socket communications (string and binary)
236
- await this.socket.init(this.role, this.config);
304
+ // init socket communications
305
+ await this.#socket.init(this.#role, this.#config);
237
306
 
238
307
  // we need the try/catch block to change the promise rejection into proper error
239
308
  try {
240
309
  await new Promise((resolve, reject) => {
241
310
  // wait for handshake response before starting stateManager and pluginManager
242
- this.socket.addListener(CLIENT_HANDSHAKE_RESPONSE, async ({ id, uuid, token, version }) => {
243
- this.id = id;
244
- this.uuid = uuid;
245
- this.token = token;
246
-
247
- if (version !== this.version) {
248
- console.warn(`
249
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!
250
-
251
- WARNING
252
-
253
- Version discrepancies between server and "${this.role}" client:
254
- + server: ${version} | client: ${this.version}
255
-
256
- This might lead to unexpected behavior, you should consider to update your
257
- dependancies on both your server and clients.
311
+ this.#socket.addListener(CLIENT_HANDSHAKE_RESPONSE, async ({ id, uuid, token, version }) => {
312
+ this.#id = id;
313
+ this.#uuid = uuid;
314
+ this.#token = token;
258
315
 
259
- !!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!!`);
316
+ if (version !== this.#version) {
317
+ logger.warnVersionDiscepancies(this.#role, this.#version, version);
260
318
  }
261
319
 
262
320
  resolve();
263
321
  });
264
322
 
265
- this.socket.addListener(CLIENT_HANDSHAKE_ERROR, (err) => {
323
+ this.#socket.addListener(CLIENT_HANDSHAKE_ERROR, (err) => {
266
324
  let msg = ``;
267
325
 
268
326
  switch (err.type) {
@@ -273,42 +331,38 @@ dependancies on both your server and clients.
273
331
  msg = `[soundworks:Client] ${err.message}`;
274
332
  break;
275
333
  default:
276
- msg = `Undefined error`;
334
+ msg = `[soundworks:Client] Undefined error: ${err.message}`;
277
335
  break;
278
336
  }
279
337
 
280
- this.socket.terminate();
338
+ // These are development errors, we can just hang. If we terminate the
339
+ // socket, a reload is triggered by the launcher which is bad in terms of DX
281
340
  reject(msg);
282
341
  });
283
342
 
284
343
  // send handshake request
285
344
  const payload = {
286
- role: this.role,
287
- version: this.version,
288
- registeredPlugins: this.pluginManager.getRegisteredPlugins(),
345
+ role: this.#role,
346
+ version: this.#version,
347
+ registeredPlugins: this.#pluginManager.getRegisteredPlugins(),
289
348
  };
290
349
 
291
- this.socket.send(CLIENT_HANDSHAKE_REQUEST, payload);
350
+ this.#socket.send(CLIENT_HANDSHAKE_REQUEST, payload);
292
351
  });
293
- } catch (err) {
294
- throw new Error(err);
352
+ } catch (msg) {
353
+ throw new Error(msg);
295
354
  }
296
355
 
297
- // ------------------------------------------------------------
298
- // CREATE STATE MANAGER
299
- // ------------------------------------------------------------
300
- this.stateManager.init(this.id, {
301
- emit: this.socket.send.bind(this.socket), // need to alias this
302
- addListener: this.socket.addListener.bind(this.socket),
303
- removeAllListeners: this.socket.removeAllListeners.bind(this.socket),
356
+ // init state manager
357
+ this.#stateManager.init(this.id, {
358
+ emit: this.#socket.send.bind(this.#socket), // need to alias this
359
+ addListener: this.#socket.addListener.bind(this.#socket),
360
+ removeAllListeners: this.#socket.removeAllListeners.bind(this.#socket),
304
361
  });
305
362
 
306
- // ------------------------------------------------------------
307
- // INIT PLUGIN MANAGER
308
- // ------------------------------------------------------------
309
- await this.pluginManager.start();
363
+ await this.#pluginManager.start();
310
364
 
311
- await this._dispatchStatus('inited');
365
+ await this.#dispatchStatus('inited');
312
366
  }
313
367
 
314
368
  /**
@@ -328,24 +382,16 @@ dependancies on both your server and clients.
328
382
  * await client.start();
329
383
  */
330
384
  async start() {
331
- if (this.status === 'idle') {
385
+ if (this.#status === 'idle') {
332
386
  await this.init();
333
387
  }
334
388
 
335
- if (this.status === 'started') {
389
+ if (this.#status === 'started') {
336
390
  throw new Error(`[soundworks:Server] Cannot call "client.start()" twice`);
337
391
  }
338
392
 
339
- if (this.status !== 'inited') {
340
- throw new Error(`[soundworks:Server] Cannot "client.start()" before "client.init()"`);
341
- }
342
-
343
- // ------------------------------------------------------------
344
- // START CONTEXT MANAGER
345
- // ------------------------------------------------------------
346
- await this.contextManager.start();
347
-
348
- await this._dispatchStatus('started');
393
+ await this.#contextManager.start();
394
+ await this.#dispatchStatus('started');
349
395
  }
350
396
 
351
397
  /**
@@ -365,15 +411,15 @@ dependancies on both your server and clients.
365
411
  * await client.stop();
366
412
  */
367
413
  async stop() {
368
- if (this.status !== 'started') {
414
+ if (this.#status !== 'started') {
369
415
  throw new Error(`[soundworks:Client] Cannot "client.stop()" before "client.start()"`);
370
416
  }
371
417
 
372
- await this.contextManager.stop();
373
- await this.pluginManager.stop();
374
- await this.socket.terminate();
418
+ await this.#contextManager.stop();
419
+ await this.#pluginManager.stop();
420
+ await this.#socket[kSocketTerminate]();
375
421
 
376
- await this._dispatchStatus('stopped');
422
+ await this.#dispatchStatus('stopped');
377
423
  }
378
424
 
379
425
  /**
@@ -394,47 +440,37 @@ dependancies on both your server and clients.
394
440
  * auditState.onUpdate(() => console.log(auditState.getValues()), true);
395
441
  */
396
442
  async getAuditState() {
397
- if (this.status === 'idle') {
443
+ if (this.#status === 'idle') {
398
444
  throw new Error(`[soundworks.Client] Cannot access audit state before "client.init()"`);
399
445
  }
400
446
 
401
- if (this._auditState === null) {
402
- this._auditState = await this.stateManager.attach(AUDIT_STATE_NAME);
447
+ if (this.#auditState === null) {
448
+ this.#auditState = await this.#stateManager.attach(AUDIT_STATE_NAME);
403
449
  }
404
450
 
405
- return this._auditState;
451
+ return this.#auditState;
406
452
  }
407
453
 
408
454
  /**
409
455
  * Listen for the status change ('inited', 'started', 'stopped') of the client.
410
456
  *
411
457
  * @param {Function} callback - Listener to the status change.
412
- * @returns {Function} Delete the listener.
458
+ * @returns {Function} Function that delete the listener when executed.
413
459
  */
414
460
  onStatusChange(callback) {
415
- this._onStatusChangeCallbacks.add(callback);
416
-
417
- return () => this._onStatusChangeCallbacks.delete(callback);
418
- }
419
-
420
- /** @private */
421
- async _dispatchStatus(status) {
422
- this.status = status;
423
-
424
- // if node target and launched in a child process, forward status to parent process
425
- if (this.target === 'node' && process.send !== undefined) {
426
- process.send(`soundworks:client:${status}`);
427
- }
428
-
429
- // execute all callbacks in parallel
430
- const promises = [];
431
-
432
- for (let callback of this._onStatusChangeCallbacks) {
433
- promises.push(callback(status));
434
- }
435
-
436
- await Promise.all(promises);
461
+ this.#onStatusChangeCallbacks.add(callback);
462
+ return () => this.#onStatusChangeCallbacks.delete(callback);
437
463
  }
438
464
  }
439
465
 
466
+ Object.defineProperties(Client.prototype, {
467
+ [Symbol.toStringTag]: {
468
+ __proto__: null,
469
+ writable: false,
470
+ enumerable: false,
471
+ configurable: true,
472
+ value: 'Client',
473
+ },
474
+ });
475
+
440
476
  export default Client;