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