@soundworks/core 4.0.0-alpha.28 → 4.0.0-alpha.29

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 (127) hide show
  1. package/.jsdoc +3 -1
  2. package/CHANGELOG.md +11 -5
  3. package/package.json +5 -5
  4. package/src/client/Client.js +78 -83
  5. package/src/client/{Context.js → ClientContext.js} +48 -38
  6. package/src/client/ClientContextManager.js +115 -0
  7. package/src/client/{Plugin.js → ClientPlugin.js} +22 -18
  8. package/src/client/{PluginManager.js → ClientPluginManager.js} +39 -35
  9. package/src/client/{Socket.js → ClientSocket.js} +54 -65
  10. package/src/client/{StateManager.js → ClientStateManager.js} +7 -8
  11. package/src/client/index.js +17 -41
  12. package/src/common/BasePlugin.js +56 -51
  13. package/src/common/BasePluginManager.js +175 -139
  14. package/src/common/BaseStateManager.js +316 -120
  15. package/src/common/BatchedTransport.js +24 -24
  16. package/src/common/ParameterBag.js +18 -6
  17. package/src/common/PromiseStore.js +1 -0
  18. package/src/common/{BaseSharedState.js → SharedState.js} +271 -199
  19. package/src/common/{BaseSharedStateCollection.js → SharedStateCollection.js} +92 -61
  20. package/src/common/logger.js +5 -0
  21. package/src/common/shared-state-types.js +76 -0
  22. package/src/common/version.js +1 -1
  23. package/src/server/Server.js +391 -354
  24. package/src/server/ServerClient.js +73 -0
  25. package/src/server/{Context.js → ServerContext.js} +85 -66
  26. package/src/server/{ContextManager.js → ServerContextManager.js} +98 -85
  27. package/src/server/{Plugin.js → ServerPlugin.js} +10 -24
  28. package/src/server/{PluginManager.js → ServerPluginManager.js} +76 -69
  29. package/src/server/{Socket.js → ServerSocket.js} +19 -18
  30. package/src/server/{Sockets.js → ServerSockets.js} +36 -27
  31. package/src/server/ServerStateManager.js +515 -0
  32. package/src/{common → server}/SharedStatePrivate.js +61 -26
  33. package/src/server/audit-schema.js +1 -1
  34. package/src/server/crypto.js +2 -0
  35. package/src/server/index.js +18 -21
  36. package/types/client/Client.d.ts +74 -95
  37. package/types/client/{Context.d.ts → ClientContext.d.ts} +25 -26
  38. package/types/client/{ContextManager.d.ts → ClientContextManager.d.ts} +19 -22
  39. package/types/client/{Plugin.d.ts → ClientPlugin.d.ts} +17 -20
  40. package/types/client/{PluginManager.d.ts → ClientPluginManager.d.ts} +13 -36
  41. package/types/client/ClientSocket.d.ts +74 -0
  42. package/types/client/{StateManager.d.ts → ClientStateManager.d.ts} +11 -15
  43. package/types/client/index.d.ts +26 -2
  44. package/types/common/BasePlugin.d.ts +52 -46
  45. package/types/common/BasePluginManager.d.ts +68 -44
  46. package/types/common/BaseStateManager.d.ts +207 -97
  47. package/types/common/BatchedTransport.d.ts +1 -5
  48. package/types/common/ParameterBag.d.ts +16 -18
  49. package/types/common/PromiseStore.d.ts +1 -1
  50. package/types/common/SharedState.d.ts +297 -0
  51. package/types/common/{BaseSharedStateCollection.d.ts → SharedStateCollection.d.ts} +34 -18
  52. package/types/common/constants.d.ts +0 -2
  53. package/types/common/logger.d.ts +2 -1
  54. package/types/common/shared-state-types.d.ts +83 -0
  55. package/types/common/sockets-utils.d.ts +0 -5
  56. package/types/common/version.d.ts +1 -2
  57. package/types/server/Server.d.ts +78 -132
  58. package/types/server/ServerClient.d.ts +45 -0
  59. package/types/server/{Context.d.ts → ServerContext.d.ts} +53 -43
  60. package/types/server/ServerContextManager.d.ts +80 -0
  61. package/types/server/{Plugin.d.ts → ServerPlugin.d.ts} +19 -33
  62. package/types/server/{PluginManager.d.ts → ServerPluginManager.d.ts} +39 -37
  63. package/types/server/ServerSocket.d.ts +85 -0
  64. package/types/server/ServerSockets.d.ts +50 -0
  65. package/types/server/ServerStateManager.d.ts +189 -0
  66. package/types/server/SharedStatePrivate.d.ts +21 -0
  67. package/types/server/audit-network-latency.worker.d.ts +1 -2
  68. package/types/server/audit-schema.d.ts +0 -1
  69. package/types/server/crypto.d.ts +2 -1
  70. package/types/server/index.d.ts +24 -2
  71. package/src/client/ContextManager.js +0 -111
  72. package/src/client/SharedState.js +0 -84
  73. package/src/client/SharedStateCollection.js +0 -38
  74. package/src/server/Client.js +0 -55
  75. package/src/server/SharedState.js +0 -85
  76. package/src/server/SharedStateCollection.js +0 -39
  77. package/src/server/StateManager.js +0 -696
  78. package/types/client/Client.d.ts.map +0 -1
  79. package/types/client/Context.d.ts.map +0 -1
  80. package/types/client/ContextManager.d.ts.map +0 -1
  81. package/types/client/Plugin.d.ts.map +0 -1
  82. package/types/client/PluginManager.d.ts.map +0 -1
  83. package/types/client/SharedState.d.ts +0 -85
  84. package/types/client/SharedState.d.ts.map +0 -1
  85. package/types/client/SharedStateCollection.d.ts +0 -41
  86. package/types/client/SharedStateCollection.d.ts.map +0 -1
  87. package/types/client/Socket.d.ts +0 -149
  88. package/types/client/Socket.d.ts.map +0 -1
  89. package/types/client/StateManager.d.ts.map +0 -1
  90. package/types/client/index.d.ts.map +0 -1
  91. package/types/common/BasePlugin.d.ts.map +0 -1
  92. package/types/common/BasePluginManager.d.ts.map +0 -1
  93. package/types/common/BaseSharedState.d.ts +0 -244
  94. package/types/common/BaseSharedState.d.ts.map +0 -1
  95. package/types/common/BaseSharedStateCollection.d.ts.map +0 -1
  96. package/types/common/BaseStateManager.d.ts.map +0 -1
  97. package/types/common/BatchedTransport.d.ts.map +0 -1
  98. package/types/common/ParameterBag.d.ts.map +0 -1
  99. package/types/common/PromiseStore.d.ts.map +0 -1
  100. package/types/common/SharedStatePrivate.d.ts +0 -21
  101. package/types/common/SharedStatePrivate.d.ts.map +0 -1
  102. package/types/common/constants.d.ts.map +0 -1
  103. package/types/common/logger.d.ts.map +0 -1
  104. package/types/common/sockets-utils.d.ts.map +0 -1
  105. package/types/common/version.d.ts.map +0 -1
  106. package/types/server/Client.d.ts +0 -45
  107. package/types/server/Client.d.ts.map +0 -1
  108. package/types/server/Context.d.ts.map +0 -1
  109. package/types/server/ContextManager.d.ts +0 -69
  110. package/types/server/ContextManager.d.ts.map +0 -1
  111. package/types/server/Plugin.d.ts.map +0 -1
  112. package/types/server/PluginManager.d.ts.map +0 -1
  113. package/types/server/Server.d.ts.map +0 -1
  114. package/types/server/SharedState.d.ts +0 -86
  115. package/types/server/SharedState.d.ts.map +0 -1
  116. package/types/server/SharedStateCollection.d.ts +0 -41
  117. package/types/server/SharedStateCollection.d.ts.map +0 -1
  118. package/types/server/Socket.d.ts +0 -164
  119. package/types/server/Socket.d.ts.map +0 -1
  120. package/types/server/Sockets.d.ts +0 -80
  121. package/types/server/Sockets.d.ts.map +0 -1
  122. package/types/server/StateManager.d.ts +0 -405
  123. package/types/server/StateManager.d.ts.map +0 -1
  124. package/types/server/audit-network-latency.worker.d.ts.map +0 -1
  125. package/types/server/audit-schema.d.ts.map +0 -1
  126. package/types/server/crypto.d.ts.map +0 -1
  127. package/types/server/index.d.ts.map +0 -1
package/.jsdoc CHANGED
@@ -23,9 +23,12 @@
23
23
  "footer": ""
24
24
  },
25
25
  "docdash": {
26
+ "search": true,
27
+ "typedefs": true,
26
28
  "sectionOrder": [
27
29
  "Namespaces",
28
30
  "Classes",
31
+ "Global",
29
32
  "Modules",
30
33
  "Externals",
31
34
  "Events",
@@ -40,7 +43,6 @@
40
43
  "title": "soundworks",
41
44
  "description": "Documentation of soundworks, a full-stack JavaScript framework for distributed WebAudio and multimedia applications"
42
45
  },
43
- "search": true,
44
46
  "menu": {
45
47
  "Website": {
46
48
  "href":"http://soundworks.dev/",
package/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ ## v4.0.0-alpha.29 - 23/09/2023
2
+
3
+ - Feat: add required option for SharedState parameter
4
+ - Chore: Renamed all classes to avoid namespace in doc and improve typescript support
5
+ - Chore: improve shared states abstractions naming
6
+
1
7
  ## v4.0.0-alpha.28 - 06/09/2025
2
8
 
3
9
  - Fix: make it can work without `node_modules` when bundled to `.cjs` for use in Max
@@ -12,7 +18,7 @@
12
18
 
13
19
  ## v4.0.0-alpha.25 - 24/05/2025
14
20
 
15
- - Fix: better default behavior for websocket connection URL
21
+ - Fix: better default behavior for WebSocket connection URL
16
22
 
17
23
  ## v4.0.0-alpha.24 - 24/05/2025
18
24
 
@@ -22,14 +28,14 @@
22
28
 
23
29
  - Breaking: remove binary socket
24
30
  - Fix: do not terminate socket on missed heartbeat, which seems to create false positive, just warn instead
25
- - Refactor: some work toward upgrading code to es2022
31
+ - Refactor: some work toward upgrading code to ES2022
26
32
 
27
33
  ## v4.0.0-alpha.22 - 16/05/2025
28
34
 
29
35
  - Feat: allow configuring socket endpoint on browser clients
30
36
  - Feat: check and warn if version inconsistency between server and client
31
37
 
32
- ## v4.0.0-alpha.21 - 6/05/2025
38
+ ## v4.0.0-alpha.21 - 06/05/2025
33
39
 
34
40
  - Fix: prevent multiple 'close' events
35
41
  - Fix: disable heartbeat check on client-side, was unstable
@@ -44,7 +50,7 @@
44
50
 
45
51
  ## v4.0.0-alpha.19 - 17/01/2024
46
52
 
47
- - Hotfix for #85
53
+ - Hot fix for #85
48
54
 
49
55
  ## v4.0.0-alpha.18 - 15/01/2024
50
56
 
@@ -52,7 +58,7 @@
52
58
 
53
59
  ## v4.0.0-alpha.17 - 16/12/2023
54
60
 
55
- - Support bundling the server to cjs for Max externals
61
+ - Support bundling the server to common js for Max externals
56
62
 
57
63
  ## v4.0.0-alpha.16 - 15/12/2023
58
64
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@soundworks/core",
3
- "version": "4.0.0-alpha.28",
3
+ "version": "4.0.0-alpha.29",
4
4
  "description": "Open-source creative coding framework for distributed applications based on Web technologies",
5
5
  "authors": [
6
6
  "Benjamin Matuszewski",
@@ -43,11 +43,11 @@
43
43
  "node": ">=16.0.0"
44
44
  },
45
45
  "scripts": {
46
- "doc": "rm -Rf docs && jsdoc -c .jsdoc --verbose && cp -R misc/assets docs/",
46
+ "doc": "npm run types && rm -rf docs && jsdoc -c .jsdoc --verbose && cp -R misc/assets docs/",
47
47
  "lint": "npx eslint src",
48
- "test": "npx mocha",
49
- "test:all": "npx mocha tests/*/*.spec.js",
50
- "types": "rm -Rf types && tsc",
48
+ "test": "npx mocha tests/*/*.spec.js",
49
+ "test:only": "npx mocha",
50
+ "types": "rm -rf types && tsc",
51
51
  "preversion": "node ./misc/scripts/generate-version-file.js $npm_new_version",
52
52
  "postversion": "node ./misc/scripts/check-changelog.js && git commit -am \"$npm_new_version\" --allow-empty"
53
53
  },
@@ -1,11 +1,21 @@
1
1
  import { isBrowser, isPlainObject } from '@ircam/sc-utils';
2
2
 
3
- import ContextManager from './ContextManager.js';
4
- import PluginManager from './PluginManager.js';
5
- import Socket, {
3
+ import ClientContextManager, {
4
+ kClientContextManagerStart,
5
+ kClientContextManagerStop,
6
+ } from './ClientContextManager.js';
7
+ import ClientPluginManager from './ClientPluginManager.js';
8
+ import {
9
+ kPluginManagerStart,
10
+ kPluginManagerStop,
11
+ } from '../common/BasePluginManager.js';
12
+ import ClientSocket, {
6
13
  kSocketTerminate,
7
- } from './Socket.js';
8
- import StateManager from './StateManager.js';
14
+ } from './ClientSocket.js';
15
+ import ClientStateManager from './ClientStateManager.js';
16
+ import {
17
+ kStateManagerInit,
18
+ } from '../common/BaseStateManager.js';
9
19
 
10
20
  import {
11
21
  CLIENT_HANDSHAKE_REQUEST,
@@ -14,53 +24,40 @@ import {
14
24
  AUDIT_STATE_NAME,
15
25
  } from '../common/constants.js';
16
26
  import logger from '../common/logger.js';
17
- import version from '../common/version.js';
27
+ import VERSION from '../common/version.js';
18
28
 
19
29
  // for testing purposes
20
30
  export const kClientVersionTest = Symbol('soundworks:client-version-test');
21
-
22
- /**
23
- * Configuration object for a client running in a browser runtime.
24
- *
25
- * @typedef ClientConfig
26
- * @memberof client
27
- * @type {object}
28
- * @property {string} role - Role of the client in the application (e.g. 'player', 'controller').
29
- * @property {object} [app] - Application configration object.
30
- * @property {string} [app.name=''] - Name of the application.
31
- * @property {string} [app.author=''] - Name of the author.
32
- * @property {object} [env] - Environment configration object.
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.
37
- * @property {string} [env.websockets={}] - Configuration options for websockets.
38
- * @property {string} [env.subpath=''] - If running behind a proxy, path to the application.
39
- */
31
+ export const kClientOnStatusChangeCallbacks = Symbol('soundworks:client-on-status-change-callbacks');
40
32
 
41
33
  /**
42
34
  * The `Client` class is the main entry point for the client-side of a soundworks
43
35
  * application.
44
36
  *
45
- * A `Client` instance allows to access soundworks components such as {@link client.StateManager},
46
- * {@link client.PluginManager},{@link client.Socket} or {@link client.ContextManager}.
47
- * Its is also responsible for handling the initialization lifecycles of the different
48
- * soundworks components.
37
+ * A `soundworks` client can run seamlessly in a browser or in a Node.js runtime.
38
+ *
39
+ * It provides an access to the different soundworks components such as the {@link ClientStateManager},
40
+ * {@link ClientPluginManager}, {@link ClientSocket} and the {@link ClientContextManager}.
49
41
  *
50
42
  * ```
51
43
  * import { Client } from '@soundworks/core/client.js';
52
- * // create a new soundworks `Client` instance
53
- * const client = new Client({ role: 'player' });
54
- * // init and start the client
44
+ * // create a `Client` instance
45
+ * const client = new Client({
46
+ * role: 'player',
47
+ * env: {
48
+ * useHttps: false,
49
+ * serverAddress: 'localhost',
50
+ * port: 8000,
51
+ * },
52
+ * });
53
+ * // start the client
55
54
  * await client.start();
56
55
  * ```
57
- *
58
- * @memberof client
59
56
  */
60
57
  class Client {
58
+ #config = null;
61
59
  #version = null;
62
60
  #role = null;
63
- #config = null;
64
61
  #id = null;
65
62
  #uuid = null;
66
63
  #target = null;
@@ -71,11 +68,10 @@ class Client {
71
68
  #status = 'idle';
72
69
  // Token of the client if connected through HTTP authentication.
73
70
  #token = null;
74
- #onStatusChangeCallbacks = new Set();
75
71
  #auditState = null;
76
72
 
77
73
  /**
78
- * @param {client.ClientConfig} config - Configuration of the soundworks client.
74
+ * @param {ClientConfig} config - Configuration of the soundworks client.
79
75
  * @throws Will throw if the given config object is invalid.
80
76
  */
81
77
  constructor(config) {
@@ -118,13 +114,7 @@ class Client {
118
114
  this.#config.env = {};
119
115
  }
120
116
 
121
- // minimal configuration for websockets
122
- this.#config.env.websockets = Object.assign({
123
- path: 'socket',
124
- pingInterval: 5000,
125
- }, config.env.websockets);
126
-
127
- this.#version = version;
117
+ this.#version = VERSION;
128
118
  // allow override though config for testing
129
119
  if (config[kClientVersionTest]) {
130
120
  this.#version = config[kClientVersionTest];
@@ -132,12 +122,17 @@ class Client {
132
122
 
133
123
  this.#role = config.role;
134
124
  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();
125
+ this.#socket = new ClientSocket(this.#role, this.#config, {
126
+ path: 'socket',
127
+ retryConnectionRefusedTimeout: 1000,
128
+ });
129
+ this.#contextManager = new ClientContextManager();
130
+ this.#pluginManager = new ClientPluginManager(this);
131
+ this.#stateManager = new ClientStateManager();
139
132
  this.#status = 'idle';
140
133
 
134
+ this[kClientOnStatusChangeCallbacks] = new Set();
135
+
141
136
  logger.configure(!!config.env.verbose);
142
137
  }
143
138
 
@@ -162,7 +157,7 @@ class Client {
162
157
  /**
163
158
  * Configuration object.
164
159
  *
165
- * @type {client.ClientConfig}
160
+ * @type {ClientConfig}
166
161
  */
167
162
  get config() {
168
163
  return this.#config;
@@ -183,7 +178,7 @@ class Client {
183
178
  /**
184
179
  * Unique session uuid of the client (uuidv4).
185
180
  *
186
- * Generated and retrieved by the server during {@link client.Client#init}.
181
+ * Generated and retrieved by the server during {@link Client#init}.
187
182
  * @type {string}
188
183
  */
189
184
  get uuid() {
@@ -203,53 +198,53 @@ class Client {
203
198
  /**
204
199
  * Instance of the {@link client.Socket} class.
205
200
  *
206
- * @see {@link client.Socket}
207
- * @type {client.Socket}
201
+ * @see {@link ClientSocket}
202
+ * @type {ClientSocket}
208
203
  */
209
204
  get socket() {
210
205
  return this.#socket;
211
206
  }
212
207
 
213
208
  /**
214
- * Instance of the {@link client.ContextManager} class.
209
+ * Instance of the {@link ClientContextManager} class.
215
210
  *
216
211
  * The context manager can be safely used after `client.init()` has been fulfilled.
217
212
  *
218
- * @see {@link client.ContextManager}
219
- * @type {client.ContextManager}
213
+ * @see {@link ClientContextManager}
214
+ * @type {ClientContextManager}
220
215
  */
221
216
  get contextManager() {
222
217
  return this.#contextManager;
223
218
  }
224
219
 
225
220
  /**
226
- * Instance of the {@link client.PluginManager} class.
221
+ * Instance of the {@link ClientPluginManager} class.
227
222
  *
228
223
  * The context manager can be safely used after `client.init()` has been fulfilled.
229
224
  *
230
- * @see {@link client.PluginManager}
231
- * @type {client.PluginManager}
225
+ * @see {@link ClientPluginManager}
226
+ * @type {ClientPluginManager}
232
227
  */
233
228
  get pluginManager() {
234
229
  return this.#pluginManager;
235
230
  }
236
231
 
237
232
  /**
238
- * Instance of the {@link client.StateManager} class.
233
+ * Instance of the {@link ClientStateManager} class.
239
234
  *
240
235
  * The context manager can be safely used after `client.init()` has been fulfilled.
241
236
  *
242
- * @see {@link client.StateManager}
243
- * @type {client.StateManager}
237
+ * @see {@link ClientStateManager}
238
+ * @type {ClientStateManager}
244
239
  */
245
240
  get stateManager() {
246
241
  return this.#stateManager;
247
242
  }
248
243
 
249
244
  /**
250
- * Status of the client, 'idle', 'inited', 'started' or 'errored'.
245
+ * Status of the client.
251
246
  *
252
- * @type {string}
247
+ * @type {'idle'|'inited'|'started'|'errored'}
253
248
  */
254
249
  get status() {
255
250
  return this.#status;
@@ -267,7 +262,7 @@ class Client {
267
262
  // execute all callbacks in parallel
268
263
  const promises = [];
269
264
 
270
- for (let callback of this.#onStatusChangeCallbacks) {
265
+ for (let callback of this[kClientOnStatusChangeCallbacks]) {
271
266
  promises.push(callback(status));
272
267
  }
273
268
 
@@ -276,11 +271,11 @@ class Client {
276
271
 
277
272
  /**
278
273
  * The `init` method is part of the initialization lifecycle of the `soundworks`
279
- * client. Most of the time, the `init` method will be implicitly called by the
280
- * {@link client.Client#start} method.
274
+ * client. Most of the time, this method will be implicitly executed by the
275
+ * {@link Client#start} method.
281
276
  *
282
277
  * In some situations you might want to call this method manually, in such cases
283
- * the method should be called before the {@link client.Client#start} method.
278
+ * the method should be called before the {@link Client#start} method.
284
279
  *
285
280
  * What it does:
286
281
  * - connect the sockets to be server
@@ -288,8 +283,8 @@ class Client {
288
283
  * - launch the state manager
289
284
  * - initialize all registered plugin
290
285
  *
291
- * After `await client.init()` is fulfilled, the {@link client.Client#stateManager},
292
- * the {@link client.Client#pluginManager} and the {@link client.Client#socket}
286
+ * After `await client.init()` is fulfilled, the {@link Client#stateManager},
287
+ * the {@link Client#pluginManager} and the {@link Client#socket}
293
288
  * can be safely used.
294
289
  *
295
290
  * @example
@@ -302,7 +297,7 @@ class Client {
302
297
  */
303
298
  async init() {
304
299
  // init socket communications
305
- await this.#socket.init(this.#role, this.#config);
300
+ await this.#socket.init();
306
301
 
307
302
  // we need the try/catch block to change the promise rejection into proper error
308
303
  try {
@@ -354,24 +349,24 @@ class Client {
354
349
  }
355
350
 
356
351
  // init state manager
357
- this.#stateManager.init(this.id, {
352
+ this.#stateManager[kStateManagerInit](this.id, {
358
353
  emit: this.#socket.send.bind(this.#socket), // need to alias this
359
354
  addListener: this.#socket.addListener.bind(this.#socket),
360
355
  removeAllListeners: this.#socket.removeAllListeners.bind(this.#socket),
361
356
  });
362
357
 
363
- await this.#pluginManager.start();
358
+ await this.#pluginManager[kPluginManagerStart]();
364
359
 
365
360
  await this.#dispatchStatus('inited');
366
361
  }
367
362
 
368
363
  /**
369
364
  * The `start` method is part of the initialization lifecycle of the `soundworks`
370
- * client. The `start` method will implicitly call the {@link client.Client#init}
371
- * method if it has not been called manually.
365
+ * client. This method will implicitly execute {@link Client#init} method if it
366
+ * has not been called manually.
372
367
  *
373
368
  * What it does:
374
- * - implicitly call {@link client.Client#init} if not done manually
369
+ * - implicitly call {@link Client#init} if not done manually
375
370
  * - start all created contexts. For that to happen, you will have to call `client.init`
376
371
  * manually and instantiate the contexts between `client.init()` and `client.start()`
377
372
  *
@@ -390,7 +385,7 @@ class Client {
390
385
  throw new Error(`[soundworks:Server] Cannot call "client.start()" twice`);
391
386
  }
392
387
 
393
- await this.#contextManager.start();
388
+ await this.#contextManager[kClientContextManagerStart]();
394
389
  await this.#dispatchStatus('started');
395
390
  }
396
391
 
@@ -415,8 +410,8 @@ class Client {
415
410
  throw new Error(`[soundworks:Client] Cannot "client.stop()" before "client.start()"`);
416
411
  }
417
412
 
418
- await this.#contextManager.stop();
419
- await this.#pluginManager.stop();
413
+ await this.#contextManager[kClientContextManagerStop]();
414
+ await this.#pluginManager[kPluginManagerStop]();
420
415
  await this.#socket[kSocketTerminate]();
421
416
 
422
417
  await this.#dispatchStatus('stopped');
@@ -425,16 +420,16 @@ class Client {
425
420
  /**
426
421
  * Attach and retrieve the global audit state of the application.
427
422
  *
428
- * The audit state is a {@link client.SharedState} instance that keeps track of
423
+ * The audit state is a {@link SharedState} instance that keeps track of
429
424
  * global informations about the application such as, the number of connected
430
425
  * clients, network latency estimation, etc. It is usefull for controller client
431
426
  * roles to give the user an overview about the state of the application.
432
427
  *
433
428
  * The audit state is lazily attached to the client only if this method is called.
434
429
  *
435
- * @returns {Promise<client.SharedState>}
430
+ * @returns {Promise<SharedState>}
436
431
  * @throws Will throw if called before `client.init()`
437
- * @see {@link client.SharedState}
432
+ * @see {@link SharedState}
438
433
  * @example
439
434
  * const auditState = await client.getAuditState();
440
435
  * auditState.onUpdate(() => console.log(auditState.getValues()), true);
@@ -458,8 +453,8 @@ class Client {
458
453
  * @returns {Function} Function that delete the listener when executed.
459
454
  */
460
455
  onStatusChange(callback) {
461
- this.#onStatusChangeCallbacks.add(callback);
462
- return () => this.#onStatusChangeCallbacks.delete(callback);
456
+ this[kClientOnStatusChangeCallbacks].add(callback);
457
+ return () => this[kClientOnStatusChangeCallbacks].delete(callback);
463
458
  }
464
459
  }
465
460
 
@@ -1,4 +1,7 @@
1
1
  import Client from './Client.js';
2
+ import {
3
+ kClientContextManagerRegister,
4
+ } from './ClientContextManager.js';
2
5
  import PromiseStore from '../common/PromiseStore.js';
3
6
  import {
4
7
  CONTEXT_ENTER_REQUEST,
@@ -9,6 +12,8 @@ import {
9
12
  CONTEXT_EXIT_ERROR,
10
13
  } from '../common/constants.js';
11
14
 
15
+ export const kClientContextStatus = Symbol('soundworks:client-context-status');
16
+
12
17
  // share between all context, as channels are common to all contexts
13
18
  const promiseStore = new PromiseStore('Context');
14
19
 
@@ -16,7 +21,7 @@ const promiseStore = new PromiseStore('Context');
16
21
  * Base class to extend in order to implement a new Context.
17
22
  *
18
23
  * In the `soundworks` paradigm, a client has a "role" (e.g. _player_, _controller_)
19
- * see {@link client.Client#role}) and can be in different "contexts" (e.g. different
24
+ * see {@link Client#role}) and can be in different "contexts" (e.g. different
20
25
  * part of the experience such as sections of a music piece, etc.). This class
21
26
  * provides a simple and unified way to model these reccuring aspects of an application.
22
27
  *
@@ -29,12 +34,12 @@ const promiseStore = new PromiseStore('Context');
29
34
  * the context. In such case, `soundworks` guarantees that the server-side
30
35
  * logic is executed before the `enter()` and `exit()` promises are fulfilled.
31
36
  *
32
- * ```
33
- * import { Client, Context } from '@soundworks/core/index.js'
37
+ * ```js
38
+ * import { Client, ClientContext } from '@soundworks/core/index.js'
34
39
  *
35
40
  * const client = new Client(config);
36
41
  *
37
- * class MyContext extends Context {
42
+ * class MyContext extends ClientContext {
38
43
  * async enter() {
39
44
  * await super.enter();
40
45
  * console.log(`client ${this.client.id} entered my context`);
@@ -53,34 +58,23 @@ const promiseStore = new PromiseStore('Context');
53
58
  * await new Promise(resolve => setTimeout(resolve, 2000));
54
59
  * await myContext.exit();
55
60
  * ```
56
- *
57
- * @memberof client
58
61
  */
59
- class Context {
62
+ class ClientContext {
63
+ #client = null;
64
+
60
65
  /**
61
- * @param {client.Client} client - The soundworks client instance.
66
+ * @param {Client} client - The soundworks client instance.
62
67
  * @throws Will throw if the first argument is not a soundworks client instance.
63
68
  */
64
69
  constructor(client) {
65
70
  if (!(client instanceof Client)) {
66
- throw new Error(`[soundworks:Context] Invalid argument, context "${this.name}" should receive a "soundworks.Client" instance as first argument`);
71
+ throw new Error(`[soundworks:ClientContext] Invalid argument, context "${this.name}" should receive a "soundworks.Client" instance as first argument`);
67
72
  }
68
73
 
69
- /**
70
- * The soundworks client instance.
71
- * @type {client.Client}
72
- * @readonly
73
- */
74
- this.client = client;
75
-
76
- /**
77
- * Status of the context, i.e. 'idle', 'inited', 'started', 'errored'
78
- * @type {string}
79
- * @readonly
80
- */
81
- this.status = 'idle';
82
-
83
- this.client.socket.addListener(CONTEXT_ENTER_RESPONSE, (reqId, contextName) => {
74
+ this.#client = client;
75
+ this[kClientContextStatus] = 'idle';
76
+
77
+ this.#client.socket.addListener(CONTEXT_ENTER_RESPONSE, (reqId, contextName) => {
84
78
  if (contextName !== this.name) {
85
79
  return;
86
80
  }
@@ -88,7 +82,7 @@ class Context {
88
82
  promiseStore.resolve(reqId);
89
83
  });
90
84
 
91
- this.client.socket.addListener(CONTEXT_ENTER_ERROR, (reqId, contextName, msg) => {
85
+ this.#client.socket.addListener(CONTEXT_ENTER_ERROR, (reqId, contextName, msg) => {
92
86
  if (contextName !== this.name) {
93
87
  return;
94
88
  }
@@ -96,7 +90,7 @@ class Context {
96
90
  promiseStore.reject(reqId, msg);
97
91
  });
98
92
 
99
- this.client.socket.addListener(CONTEXT_EXIT_RESPONSE, (reqId, contextName) => {
93
+ this.#client.socket.addListener(CONTEXT_EXIT_RESPONSE, (reqId, contextName) => {
100
94
  if (contextName !== this.name) {
101
95
  return;
102
96
  }
@@ -104,7 +98,7 @@ class Context {
104
98
  promiseStore.resolve(reqId);
105
99
  });
106
100
 
107
- this.client.socket.addListener(CONTEXT_EXIT_ERROR, (reqId, contextName, msg) => {
101
+ this.#client.socket.addListener(CONTEXT_EXIT_ERROR, (reqId, contextName, msg) => {
108
102
  if (contextName !== this.name) {
109
103
  return;
110
104
  }
@@ -112,14 +106,30 @@ class Context {
112
106
  promiseStore.reject(reqId, msg);
113
107
  });
114
108
 
115
- this.client.contextManager.register(this);
109
+ this.#client.contextManager[kClientContextManagerRegister](this);
110
+ }
111
+
112
+ /**
113
+ * The soundworks client instance.
114
+ * @type {Client}
115
+ */
116
+ get client() {
117
+ return this.#client;
118
+ }
119
+
120
+ /**
121
+ * Status of the context.
122
+ * @type {'idle'|'inited'|'started'|'errored'}
123
+ */
124
+ get status() {
125
+ return this[kClientContextStatus];
116
126
  }
117
127
 
118
128
  /**
119
129
  * Optionnal user-defined name of the context (defaults to the class name).
120
130
  *
121
131
  * The context manager will match the client-side and server-side contexts based
122
- * on this name. If the {@link server.ContextManager} don't find a corresponding
132
+ * on this name. If the {@link ServerContextManager} don't find a corresponding
123
133
  * user-defined context with the same name, it will use a default (noop) context.
124
134
  *
125
135
  * @readonly
@@ -138,7 +148,7 @@ class Context {
138
148
 
139
149
  /**
140
150
  * Start the context. This method is lazilly called when the client enters the
141
- * context for the first time (cf. ${client.Context#enter}). If you know some
151
+ * context for the first time (cf. ${ClientContext#enter}). If you know some
142
152
  * some heavy and/or potentially long job has to be done when starting the context
143
153
  * (e.g. call to a web service) it may be a good practice to call it explicitely.
144
154
  *
@@ -177,12 +187,12 @@ class Context {
177
187
  * done (e.g. creating a shared state, etc.) when the context is entered.
178
188
  *
179
189
  * If a server-side part of the context is defined (i.e. a context with the same
180
- * {@link client.Context#name}), the corresponding server-side `enter()` method
190
+ * {@link ClientContext#name}), the corresponding server-side `enter()` method
181
191
  * will be executed before the returned Promise is fulfilled.
182
192
  *
183
193
  * If the context has not been started yet, the `start` method is implicitely executed.
184
194
  *
185
- * @returns {Promise} - Promise that resolves when the context is entered.
195
+ * @returns {Promise<void>} - Promise that resolves when the context is entered.
186
196
  * @example
187
197
  * class MyContext extends Context {
188
198
  * async enter() {
@@ -199,14 +209,14 @@ class Context {
199
209
  async enter() {
200
210
  // lazily start the context if registered after `client.start()`
201
211
  if (this.status !== 'started' && this.status !== 'errored') {
202
- await this.client.contextManager.get(this.name);
212
+ await this.#client.contextManager.get(this.name);
203
213
  }
204
214
 
205
215
  // we need the try/catch block to change the promise rejection into proper error
206
216
  try {
207
217
  await new Promise((resolve, reject) => {
208
218
  const reqId = promiseStore.add(resolve, reject, 'enter-context');
209
- this.client.socket.send(CONTEXT_ENTER_REQUEST, reqId, this.name);
219
+ this.#client.socket.send(CONTEXT_ENTER_REQUEST, reqId, this.name);
210
220
  });
211
221
  } catch (err) {
212
222
  throw new Error(err);
@@ -218,10 +228,10 @@ class Context {
218
228
  * done (e.g. delete a shared state, etc.) when the context is exited.
219
229
  *
220
230
  * If a server-side part of the context is defined (i.e. a context with the same
221
- * {@link client.Context#name}), the corresponding server-side `exit()` method
231
+ * {@link ClientContext#name}), the corresponding server-side `exit()` method
222
232
  * will be executed before the returned Promise is fulfilled.
223
233
  *
224
- * @returns {Promise} - Promise that resolves when the context is exited.
234
+ * @returns {Promise<void>} - Promise that resolves when the context is exited.
225
235
  * @example
226
236
  * class MyContext extends Context {
227
237
  * async enter() {
@@ -240,7 +250,7 @@ class Context {
240
250
  try {
241
251
  await new Promise((resolve, reject) => {
242
252
  const reqId = promiseStore.add(resolve, reject, 'exit-context');
243
- this.client.socket.send(CONTEXT_EXIT_REQUEST, reqId, this.name);
253
+ this.#client.socket.send(CONTEXT_EXIT_REQUEST, reqId, this.name);
244
254
  });
245
255
  } catch (err) {
246
256
  throw new Error(err);
@@ -248,4 +258,4 @@ class Context {
248
258
  }
249
259
  }
250
260
 
251
- export default Context;
261
+ export default ClientContext;