@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
@@ -0,0 +1,73 @@
1
+ import { idGenerator } from '@ircam/sc-utils';
2
+ import { v4 as uuid } from 'uuid';
3
+
4
+ const generateId = idGenerator();
5
+
6
+ export const kServerClientToken = Symbol('soundworks:server-client-token');
7
+
8
+ /**
9
+ * Server-side representation of a `soundworks` client.
10
+ *
11
+ * @hideconstructor
12
+ * @see {@link Client}
13
+ */
14
+ class ServerClient {
15
+ #role = null;
16
+ #id = null;
17
+ #uuid = null;
18
+ #socket = null;
19
+
20
+ /**
21
+ * @param {String} role - Role of the client
22
+ * @param {ServerSocket} socket - Socket connection with the client
23
+ */
24
+ constructor(role, socket) {
25
+ this.#role = role;
26
+ this.#id = generateId.next().value;
27
+ this.#uuid = uuid();
28
+ this.#socket = socket;
29
+ /**
30
+ * Is set in server[kServerOnSocketConnection]
31
+ * @private
32
+ */
33
+ this[kServerClientToken] = null;
34
+ }
35
+
36
+ /**
37
+ * Client role, as specified in client side config {@link Client}.
38
+ *
39
+ * @type {String}
40
+ */
41
+ get role() {
42
+ return this.#role;
43
+ }
44
+
45
+ /**
46
+ * Session Id (incremented positive number).
47
+ *
48
+ * @type {Number}
49
+ */
50
+ get id() {
51
+ return this.#id;
52
+ }
53
+
54
+ /**
55
+ * Unique session Id (uuidv4).
56
+ *
57
+ * @type {String}
58
+ */
59
+ get uuid() {
60
+ return this.#uuid;
61
+ }
62
+
63
+ /**
64
+ * Socket connection with the remote {@link Client}.
65
+ *
66
+ * @type {ServerSocket}
67
+ */
68
+ get socket() {
69
+ return this.#socket;
70
+ }
71
+ }
72
+
73
+ export default ServerClient;
@@ -1,31 +1,38 @@
1
- import Client from './Client.js';
1
+ import ServerClient from './ServerClient.js';
2
2
  import Server from './Server.js';
3
3
 
4
+ import {
5
+ kServerContextManagerRegister,
6
+ } from './ServerContextManager.js';
7
+
8
+ export const kServerContextStatus = Symbol('soundworks:server-context-status');
9
+
4
10
  /**
5
11
  * Base class to extend in order to implment the optionnal server-side counterpart
6
- * of a {@link client.Context}. If not defined, a default context will be created
12
+ * of a {@link ClientContext}. If not defined, a default context will be created
7
13
  * and used by the server.
8
14
  *
9
15
  * In the `soundworks` paradigm, a client has a "role" (e.g. _player_, _controller_)
10
- * see {@link client.Client#role}) and can be in different "contexts" (e.g. different
16
+ * see {@link Client#role}) and can be in different "contexts" (e.g. different
11
17
  * part of the experience such as sections of a music piece, etc.). The
12
- * {@link client.Context} and optionnal {@link serverContext} abstractions provide
18
+ * {@link ClientContext} and optionnal {@link ServerContext} abstractions provide
13
19
  * a simple and unified way to model these reccuring aspects of an application.
14
20
  *
15
- * If a `server.Context` is recognized as the server-side counterpart of a
16
- * {@link client.Context}, based on their respective `name` (see {@link client.Context#name}
17
- * and {@link server.Context#name}), `soundworks` will ensure the logic defined
18
- * by the server-side Context will be executed at the beginning of the
19
- * {@link client.Client#enter} and {@link client.Client#exit} methods. The example
20
- * show how soundwords handles (and guarantees) the order of the `enter()` steps
21
- * between the client-side and the server-side parts of the context. The same goes
22
- * for the `exit()` method.
21
+ * If a `ServerContext` is recognized as the server-side counterpart of a
22
+ * {@link ClientContext}, based on their respective `name` (see {@link ClientContext#name}
23
+ * and {@link ServerContext#name}), `soundworks` will ensure the logic defined
24
+ * by the ServerContext will be executed at the beginning of the
25
+ * {@link ClientContext#enter} and {@link ClientContext#exit} methods.
23
26
  *
24
- * ```
27
+ * The example above shows how soundwords handles (and guarantees) the order of
28
+ * the `enter()` steps between the client-side and the server-side parts of the
29
+ * context. The same goes for the `exit()` method.
30
+ *
31
+ * ```js
25
32
  * // client-side
26
- * import { Context } from '@soundworks/core/client.js';
33
+ * import { ClientContext } from '@soundworks/core/client.js';
27
34
  *
28
- * class MyContext extends Context {
35
+ * class MyContext extends ClientContext {
29
36
  * async enter() {
30
37
  * // 1. client side context enter() starts
31
38
  * // server-side logic is triggered first
@@ -46,11 +53,11 @@ import Server from './Server.js';
46
53
  * await myContext.enter();
47
54
  * ```
48
55
  *
49
- * ```
56
+ * ```js
50
57
  * // server-side
51
- * import { Context } from '@soundworks/core/server.js';
58
+ * import { ServerContext } from '@soundworks/core/server.js';
52
59
  *
53
- * class MyContext extends Context {
60
+ * class MyContext extends ServerContext {
54
61
  * async enter(client) {
55
62
  * // 2. server-side context enter() starts
56
63
  * await super.enter(client);
@@ -61,14 +68,16 @@ import Server from './Server.js';
61
68
  * }
62
69
  *
63
70
  * // Instantiate the context
64
- * const myContext = new Context(server);
71
+ * const myContext = new MyContext(server);
65
72
  * ```
66
- *
67
- * @memberof server
68
73
  */
69
- class Context {
74
+ class ServerContext {
75
+ #server = null;
76
+ #clients = new Set();
77
+ #roles = null;
78
+
70
79
  /**
71
- * @param {client.Client} client - The soundworks client instance.
80
+ * @param {Server} server - The soundworks server instance.
72
81
  * @param {string|string[]} [roles=[]] - Optionnal list of client roles that can
73
82
  * use this context. In large applications, this may be usefull to guarantee
74
83
  * that a context can be consumed only by specific client roles, throwing an
@@ -81,52 +90,62 @@ class Context {
81
90
  throw new Error(`[soundworks:Context] Invalid argument, context "${this.constructor.name}" should receive a "soundworks.Server" instance as first argument`);
82
91
  }
83
92
 
84
- /**
85
- * soundworks server
86
- * @type {server.Server}
87
- * @readonly
88
- */
89
- this.server = server;
90
-
91
- /**
92
- * List of clients that are currently in this context
93
- * @type {server.Client[]}
94
- */
95
- this.clients = new Set();
96
-
97
- /**
98
- * Status of the context ('idle', 'inited', 'started' or 'errored')
99
- * @type {string}
100
- * @readonly
101
- */
102
- this.status = 'idle';
103
-
104
- /**
105
- * List of client roles that can use this context.
106
- * @type {string[]}
107
- * @readonly
108
- */
109
93
  roles = Array.isArray(roles) ? roles : [roles];
110
94
 
111
- if (roles.length === 0) {
112
- roles = Object.keys(server.config.app.clients);
113
- }
114
-
115
- this.roles = new Set(roles);
95
+ this.#server = server;
96
+ this.#roles = new Set(roles);
116
97
 
98
+ /** @private */
99
+ this[kServerContextStatus] = 'idle';
117
100
  // register in context manager
118
- this.server.contextManager.register(this);
101
+ this.#server.contextManager[kServerContextManagerRegister](this);
102
+ }
103
+
104
+ /**
105
+ * The soundworks server instance.
106
+ *
107
+ * @type {Server}
108
+ */
109
+ get server() {
110
+ return this.#server;
111
+ }
112
+
113
+ /**
114
+ * List of clients that are currently in this context.
115
+ *
116
+ * @type {Set<ServerClient>}
117
+ */
118
+ get clients() {
119
+ return this.#clients;
120
+ }
121
+
122
+ /**
123
+ * List of client roles that can use this context. No access policy if empty.
124
+ *
125
+ * @type {Set<string>}
126
+ */
127
+ get roles() {
128
+ return this.#roles;
129
+ }
130
+
131
+ /**
132
+ * Status of the context ('idle', 'inited', 'started' or 'errored')
133
+ *
134
+ * @type {string}
135
+ */
136
+ get status() {
137
+ return this[kServerContextStatus];
119
138
  }
120
139
 
121
140
  /**
122
141
  * Optionnal user-defined name of the context (defaults to the class name).
123
142
  *
124
143
  * The context manager will match the client-side and server-side contexts based
125
- * on this name. If the {@link server.ContextManager} don't find a corresponding
144
+ * on this name. If the {@link ServerContextManager} don't find a corresponding
126
145
  * user-defined context with the same name, it will use a default (noop) context.
127
146
  *
128
- * @readonly
129
147
  * @type {string}
148
+ *
130
149
  * @example
131
150
  * // server-side and client-side contexts are matched based on their respective `name`
132
151
  * class MyContext extends Context {
@@ -141,7 +160,7 @@ class Context {
141
160
 
142
161
  /**
143
162
  * Start the context. This method is lazilly called when a client enters the
144
- * context for the first time (cf. ${server.Context#enter}). If you know some
163
+ * context for the first time (cf. ${ServerContext#enter}). If you know some
145
164
  * some heavy and/or potentially long job has to be done when starting the context
146
165
  * (e.g. connect to a database, parsing a long file) it may be a good practice
147
166
  * to call it explicitely.
@@ -185,7 +204,7 @@ class Context {
185
204
  *
186
205
  * _WARNING: this method should never be called manually._
187
206
  *
188
- * @param {server.Client} client - Server-side representation of the client
207
+ * @param {ServerClient} client - Server-side representation of the client
189
208
  * that enters the context.
190
209
  * @returns {Promise} - Promise that resolves when the context is entered.
191
210
  * @example
@@ -202,11 +221,11 @@ class Context {
202
221
  * }
203
222
  */
204
223
  async enter(client) {
205
- if (!(client instanceof Client)) {
206
- throw new Error(`[soundworks.Context] Invalid argument, ${this.name} context ".enter()" method should receive a server-side "soundworks.Client" instance argument`);
224
+ if (!(client instanceof ServerClient)) {
225
+ throw new Error(`[soundworks.Context] Invalid argument, ${this.name} context ".enter()" method should receive a "ServerClient" instance argument`);
207
226
  }
208
227
 
209
- this.clients.add(client);
228
+ this.#clients.add(client);
210
229
  }
211
230
 
212
231
  /**
@@ -215,7 +234,7 @@ class Context {
215
234
  *
216
235
  * * _WARNING: this method should never be called manually._
217
236
  *
218
- * @param {server.Client} client - Server-side representation of the client
237
+ * @param {ServerClient} client - Server-side representation of the client
219
238
  * that exits the context.
220
239
  * @returns {Promise} - Promise that resolves when the context is exited.
221
240
  * @example
@@ -232,12 +251,12 @@ class Context {
232
251
  * }
233
252
  */
234
253
  async exit(client) {
235
- if (!(client instanceof Client)) {
236
- throw new Error(`[soundworks.Context] Invalid argument, ${this.name}.exit() should receive a server-side "soundworks.Client" instance argument`);
254
+ if (!(client instanceof ServerClient)) {
255
+ throw new Error(`[soundworks.Context] Invalid argument, ${this.name}.exit() should receive a "ServerClient" instance argument`);
237
256
  }
238
257
 
239
- this.clients.delete(client);
258
+ this.#clients.delete(client);
240
259
  }
241
260
  }
242
261
 
243
- export default Context;
262
+ export default ServerContext;
@@ -1,4 +1,6 @@
1
- import Context from './Context.js';
1
+ import ServerContext, {
2
+ kServerContextStatus,
3
+ } from './ServerContext.js';
2
4
  import {
3
5
  CONTEXT_ENTER_REQUEST,
4
6
  CONTEXT_ENTER_RESPONSE,
@@ -8,15 +10,22 @@ import {
8
10
  CONTEXT_EXIT_ERROR,
9
11
  } from '../common/constants.js';
10
12
 
13
+ export const kServerContextManagerStart = Symbol('soundworks:server-context-manager-start');
14
+ export const kServerContextManagerStop = Symbol('soundworks:server-context-manager-stop');
15
+ export const kServerContextManagerAddClient = Symbol('soundworks:server-context-manager-add-client');
16
+ export const kServerContextManagerRemoveClient = Symbol('soundworks:server-context-manager-remove-client');
17
+ export const kServerContextManagerRegister = Symbol('soundworks:server-context-manager-register');
18
+ // for testing purposes
19
+ export const kServerContextManagerContexts = Symbol('soundworks:server-context-manager-contexts');
20
+
11
21
  /**
12
22
  * Create a dummy server side context if a proper server-side context has not
13
- * been declared and registered, one DefaultContext is created per unknown
23
+ * been declared and registered. Ane DefaultContext is created per unknown
14
24
  * contextName and associated to all known client types.
15
- *
16
25
  * @private
17
26
  */
18
27
  function createNamedContextClass(contextName) {
19
- return class DefaultContext extends Context {
28
+ return class DefaultContext extends ServerContext {
20
29
  get name() {
21
30
  return contextName;
22
31
  }
@@ -25,119 +34,110 @@ function createNamedContextClass(contextName) {
25
34
 
26
35
  /** @private */
27
36
  class ContextCollection {
28
- constructor() {
29
- this.inner = [];
37
+ #inner = [];
38
+
39
+ // for testing purposes
40
+ get length() {
41
+ return this.#inner.length;
30
42
  }
31
43
 
32
44
  add(context) {
33
- this.inner.push(context);
45
+ this.#inner.push(context);
34
46
  }
35
47
 
36
48
  has(name) {
37
- return this.inner.find(c => c.name === name) !== undefined;
49
+ return this.#inner.find(c => c.name === name) !== undefined;
38
50
  }
39
51
 
40
52
  get(name) {
41
- return this.inner.find(c => c.name === name);
53
+ return this.#inner.find(c => c.name === name);
42
54
  }
43
55
 
44
56
  map(func) {
45
- return this.inner.map(func);
57
+ return this.#inner.map(func);
46
58
  }
47
59
 
48
60
  filter(func) {
49
- return this.inner.filter(func);
61
+ return this.#inner.filter(func);
50
62
  }
51
63
  }
52
64
 
53
65
  /**
54
- * Manage the different server-side contexts and their lifecycle. The `ContextManager`
55
- * is automatically instantiated by the {@link server.Server}.
66
+ * Manage the different server-side contexts and their lifecycle.
67
+ *
68
+ * The `ServerContextManager` is automatically instantiated by the {@link Server}.
56
69
  *
57
70
  * _WARNING: Most of the time, you should not have to manipulate the context manager directly._
58
71
  *
59
- * @memberof server
60
72
  * @hideconstructor
61
73
  */
62
- class ContextManager {
74
+ class ServerContextManager {
75
+ #server = null;
76
+
77
+ #contextStartPromises = new Map();
78
+
63
79
  /**
64
- * @param {server.Server} server - Instance of the soundworks server.
80
+ * @param {Server} server - Instance of the soundworks server.
65
81
  */
66
82
  constructor(server) {
67
- /** @private */
68
- this.server = server;
69
- /** @private */
70
- this._contexts = new ContextCollection();
71
- /** @private */
72
- this._contextStartPromises = new Map();
83
+ this.#server = server;
84
+
85
+ this[kServerContextManagerContexts] = new ContextCollection();
73
86
  }
74
87
 
75
88
  /**
76
89
  * Register a context in the manager.
77
- * This method is called in the {@link server.Context} constructor
90
+ * This method is called in the {@link ServerContext} constructor
78
91
  *
79
- * @param {server.Context} context - Context instance to register.
92
+ * @param {ServerContext} context - Context instance to register.
80
93
  *
81
94
  * @private
82
95
  */
83
- register(context) {
96
+ [kServerContextManagerRegister](context) {
84
97
  // we must await the contructor initialization end to check the name and throw
85
- if (this._contexts.has(context.name)) {
86
- throw new Error(`[soundworks:ContextManager] Context "${context.name}" already registered`);
98
+ if (this[kServerContextManagerContexts].has(context.name)) {
99
+ throw new Error(`[soundworks:ServerContextManager] Context "${context.name}" already registered`);
87
100
  }
88
101
 
89
- this._contexts.add(context);
102
+ this[kServerContextManagerContexts].add(context);
90
103
  }
91
104
 
92
105
  /**
93
- * Retrieve a started context from its name.
94
- *
95
- * _WARNING: Most of the time, you should not have to call this method manually._
106
+ * Start all contexts registered before `await server.start()`.
107
+ * Called on {@link Server#start}
96
108
  *
97
- * @param {server.Context#name} contextName - Name of the context.
109
+ * @private
98
110
  */
99
- async get(contextName) {
100
- if (!this._contexts.has(contextName)) {
101
- throw new Error(`[soundworks:ContextManager] Can't get context "${contextName}", not registered`);
102
- }
103
-
104
- const context = this._contexts.get(contextName);
105
-
106
- if (this._contextStartPromises.has(contextName)) {
107
- const startPromise = this._contextStartPromises.get(contextName);
108
- await startPromise;
109
- } else {
110
- context.status = 'inited';
111
-
112
- try {
113
- const startPromise = context.start();
114
- this._contextStartPromises.set(contextName, startPromise);
115
- await startPromise;
116
- context.status = 'started';
117
- } catch (err) {
118
- context.status = 'errored';
119
- throw new Error(err);
120
- }
121
- }
111
+ async [kServerContextManagerStart]() {
112
+ const promises = this[kServerContextManagerContexts].map(context => this.get(context.name));
113
+ await Promise.all(promises);
114
+ }
122
115
 
123
- return context;
116
+ /**
117
+ * Stop all contexts. Called on {@link Server#stop}
118
+ *
119
+ * @private
120
+ */
121
+ async [kServerContextManagerStop]() {
122
+ const promises = this[kServerContextManagerContexts].map(context => context.stop());
123
+ await Promise.all(promises);
124
124
  }
125
125
 
126
126
  /**
127
127
  * Called when a client connects to the server (websocket handshake)
128
128
  *
129
- * @param {server.Client} client
129
+ * @param {ServerClient} client
130
130
  *
131
131
  * @private
132
132
  */
133
- addClient(client) {
133
+ [kServerContextManagerAddClient](client) {
134
134
  client.socket.addListener(CONTEXT_ENTER_REQUEST, async (reqId, contextName) => {
135
135
  // if no context found, create a DefaultContext on the fly
136
- if (!this._contexts.has(contextName)) {
136
+ if (!this[kServerContextManagerContexts].has(contextName)) {
137
137
  // create default context for all client types
138
138
  const ctor = createNamedContextClass(contextName);
139
139
  // this will automatically register the context in the context manager
140
- new ctor(this.server);
140
+ new ctor(this.#server);
141
141
  }
142
142
 
143
143
  // we ensure context is started, even lazilly after server.start()
@@ -161,7 +161,7 @@ class ContextManager {
161
161
  CONTEXT_ENTER_ERROR,
162
162
  reqId,
163
163
  contextName,
164
- `[soundworks:ContextManager] Client already in context (if only one context is created .enter() has been called automatically)`,
164
+ `[soundworks:ServerContextManager] Client already in context (if only one context is created .enter() has been called automatically)`,
165
165
  );
166
166
  return;
167
167
  }
@@ -171,12 +171,12 @@ class ContextManager {
171
171
  context = this.defaultContext;
172
172
  }
173
173
 
174
- if (!context.roles.has(client.role)) {
174
+ if (context.roles.size > 0 && !context.roles.has(client.role)) {
175
175
  client.socket.send(
176
176
  CONTEXT_ENTER_ERROR,
177
177
  reqId,
178
178
  contextName,
179
- `[soundworks:ContextManager] Clients with role "${client.role}" are not declared as possible consumers of context "${contextName}"`,
179
+ `[soundworks:ServerContextManager] Clients with role "${client.role}" are not declared as possible consumers of context "${contextName}"`,
180
180
  );
181
181
  return;
182
182
  }
@@ -191,12 +191,12 @@ class ContextManager {
191
191
  });
192
192
 
193
193
  client.socket.addListener(CONTEXT_EXIT_REQUEST, async (reqId, contextName) => {
194
- if (!this._contexts.has(contextName)) {
194
+ if (!this[kServerContextManagerContexts].has(contextName)) {
195
195
  client.socket.send(
196
196
  CONTEXT_EXIT_ERROR,
197
197
  reqId,
198
198
  contextName,
199
- `[soundworks:ContextManager] Cannot exit(), context ${contextName} does not exists`,
199
+ `[soundworks:ServerContextManager] Cannot exit(), context ${contextName} does not exists`,
200
200
  );
201
201
  return;
202
202
  }
@@ -215,7 +215,7 @@ class ContextManager {
215
215
  CONTEXT_EXIT_ERROR,
216
216
  reqId,
217
217
  contextName,
218
- `[soundworks:ContextManager] Client with role "${client.role}" is not in context "${contextName}"`,
218
+ `[soundworks:ServerContextManager] Client with role "${client.role}" is not in context "${contextName}"`,
219
219
  );
220
220
  }
221
221
  });
@@ -224,16 +224,16 @@ class ContextManager {
224
224
  /**
225
225
  * Called when a client connects to the server (websocket 'close' event)
226
226
  *
227
- * @param {server.Client} client
227
+ * @param {ServerClient} client
228
228
  *
229
229
  * @private
230
230
  */
231
- async removeClient(client) {
231
+ async [kServerContextManagerRemoveClient](client) {
232
232
  client.socket.removeAllListeners(CONTEXT_ENTER_REQUEST);
233
233
  client.socket.removeAllListeners(CONTEXT_EXIT_REQUEST);
234
234
 
235
235
  // exit from all contexts
236
- const promises = this._contexts
236
+ const promises = this[kServerContextManagerContexts]
237
237
  .filter(context => context.clients.has(client))
238
238
  .map(context => context.exit(client));
239
239
 
@@ -241,25 +241,38 @@ class ContextManager {
241
241
  }
242
242
 
243
243
  /**
244
- * Start all contexts registered before `await server.start()`.
245
- * Called on {@link server.Server#start}
244
+ * Retrieve a started context from its name.
246
245
  *
247
- * @private
248
- */
249
- async start() {
250
- const promises = this._contexts.map(context => this.get(context.name));
251
- await Promise.all(promises);
252
- }
253
-
254
- /**
255
- * Stop all contexts. Called on {@link server.Server#stop}
246
+ * _WARNING: Most of the time, you should not have to call this method manually._
256
247
  *
257
- * @private
248
+ * @param {ServerContext#name} contextName - Name of the context.
258
249
  */
259
- async stop() {
260
- const promises = this._contexts.map(context => context.stop());
261
- await Promise.all(promises);
250
+ async get(contextName) {
251
+ if (!this[kServerContextManagerContexts].has(contextName)) {
252
+ throw new Error(`[soundworks:ServerContextManager] Can't get context "${contextName}", not registered`);
253
+ }
254
+
255
+ const context = this[kServerContextManagerContexts].get(contextName);
256
+
257
+ if (this.#contextStartPromises.has(contextName)) {
258
+ const startPromise = this.#contextStartPromises.get(contextName);
259
+ await startPromise;
260
+ } else {
261
+ context[kServerContextStatus] = 'inited';
262
+
263
+ try {
264
+ const startPromise = context.start();
265
+ this.#contextStartPromises.set(contextName, startPromise);
266
+ await startPromise;
267
+ context[kServerContextStatus] = 'started';
268
+ } catch (err) {
269
+ context[kServerContextStatus] = 'errored';
270
+ throw new Error(err);
271
+ }
272
+ }
273
+
274
+ return context;
262
275
  }
263
276
  }
264
277
 
265
- export default ContextManager;
278
+ export default ServerContextManager;