@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.
- package/.jsdoc +3 -1
- package/CHANGELOG.md +11 -5
- package/package.json +5 -5
- package/src/client/Client.js +78 -83
- package/src/client/{Context.js → ClientContext.js} +48 -38
- package/src/client/ClientContextManager.js +115 -0
- package/src/client/{Plugin.js → ClientPlugin.js} +22 -18
- package/src/client/{PluginManager.js → ClientPluginManager.js} +39 -35
- package/src/client/{Socket.js → ClientSocket.js} +54 -65
- package/src/client/{StateManager.js → ClientStateManager.js} +7 -8
- package/src/client/index.js +17 -41
- package/src/common/BasePlugin.js +56 -51
- package/src/common/BasePluginManager.js +175 -139
- package/src/common/BaseStateManager.js +316 -120
- package/src/common/BatchedTransport.js +24 -24
- package/src/common/ParameterBag.js +18 -6
- package/src/common/PromiseStore.js +1 -0
- package/src/common/{BaseSharedState.js → SharedState.js} +271 -199
- package/src/common/{BaseSharedStateCollection.js → SharedStateCollection.js} +92 -61
- package/src/common/logger.js +5 -0
- package/src/common/shared-state-types.js +76 -0
- package/src/common/version.js +1 -1
- package/src/server/Server.js +391 -354
- package/src/server/ServerClient.js +73 -0
- package/src/server/{Context.js → ServerContext.js} +85 -66
- package/src/server/{ContextManager.js → ServerContextManager.js} +98 -85
- package/src/server/{Plugin.js → ServerPlugin.js} +10 -24
- package/src/server/{PluginManager.js → ServerPluginManager.js} +76 -69
- package/src/server/{Socket.js → ServerSocket.js} +19 -18
- package/src/server/{Sockets.js → ServerSockets.js} +36 -27
- package/src/server/ServerStateManager.js +515 -0
- package/src/{common → server}/SharedStatePrivate.js +61 -26
- package/src/server/audit-schema.js +1 -1
- package/src/server/crypto.js +2 -0
- package/src/server/index.js +18 -21
- package/types/client/Client.d.ts +74 -95
- package/types/client/{Context.d.ts → ClientContext.d.ts} +25 -26
- package/types/client/{ContextManager.d.ts → ClientContextManager.d.ts} +19 -22
- package/types/client/{Plugin.d.ts → ClientPlugin.d.ts} +17 -20
- package/types/client/{PluginManager.d.ts → ClientPluginManager.d.ts} +13 -36
- package/types/client/ClientSocket.d.ts +74 -0
- package/types/client/{StateManager.d.ts → ClientStateManager.d.ts} +11 -15
- package/types/client/index.d.ts +26 -2
- package/types/common/BasePlugin.d.ts +52 -46
- package/types/common/BasePluginManager.d.ts +68 -44
- package/types/common/BaseStateManager.d.ts +207 -97
- package/types/common/BatchedTransport.d.ts +1 -5
- package/types/common/ParameterBag.d.ts +16 -18
- package/types/common/PromiseStore.d.ts +1 -1
- package/types/common/SharedState.d.ts +297 -0
- package/types/common/{BaseSharedStateCollection.d.ts → SharedStateCollection.d.ts} +34 -18
- package/types/common/constants.d.ts +0 -2
- package/types/common/logger.d.ts +2 -1
- package/types/common/shared-state-types.d.ts +83 -0
- package/types/common/sockets-utils.d.ts +0 -5
- package/types/common/version.d.ts +1 -2
- package/types/server/Server.d.ts +78 -132
- package/types/server/ServerClient.d.ts +45 -0
- package/types/server/{Context.d.ts → ServerContext.d.ts} +53 -43
- package/types/server/ServerContextManager.d.ts +80 -0
- package/types/server/{Plugin.d.ts → ServerPlugin.d.ts} +19 -33
- package/types/server/{PluginManager.d.ts → ServerPluginManager.d.ts} +39 -37
- package/types/server/ServerSocket.d.ts +85 -0
- package/types/server/ServerSockets.d.ts +50 -0
- package/types/server/ServerStateManager.d.ts +189 -0
- package/types/server/SharedStatePrivate.d.ts +21 -0
- package/types/server/audit-network-latency.worker.d.ts +1 -2
- package/types/server/audit-schema.d.ts +0 -1
- package/types/server/crypto.d.ts +2 -1
- package/types/server/index.d.ts +24 -2
- package/src/client/ContextManager.js +0 -111
- package/src/client/SharedState.js +0 -84
- package/src/client/SharedStateCollection.js +0 -38
- package/src/server/Client.js +0 -55
- package/src/server/SharedState.js +0 -85
- package/src/server/SharedStateCollection.js +0 -39
- package/src/server/StateManager.js +0 -696
- package/types/client/Client.d.ts.map +0 -1
- package/types/client/Context.d.ts.map +0 -1
- package/types/client/ContextManager.d.ts.map +0 -1
- package/types/client/Plugin.d.ts.map +0 -1
- package/types/client/PluginManager.d.ts.map +0 -1
- package/types/client/SharedState.d.ts +0 -85
- package/types/client/SharedState.d.ts.map +0 -1
- package/types/client/SharedStateCollection.d.ts +0 -41
- package/types/client/SharedStateCollection.d.ts.map +0 -1
- package/types/client/Socket.d.ts +0 -149
- package/types/client/Socket.d.ts.map +0 -1
- package/types/client/StateManager.d.ts.map +0 -1
- package/types/client/index.d.ts.map +0 -1
- package/types/common/BasePlugin.d.ts.map +0 -1
- package/types/common/BasePluginManager.d.ts.map +0 -1
- package/types/common/BaseSharedState.d.ts +0 -244
- package/types/common/BaseSharedState.d.ts.map +0 -1
- package/types/common/BaseSharedStateCollection.d.ts.map +0 -1
- package/types/common/BaseStateManager.d.ts.map +0 -1
- package/types/common/BatchedTransport.d.ts.map +0 -1
- package/types/common/ParameterBag.d.ts.map +0 -1
- package/types/common/PromiseStore.d.ts.map +0 -1
- package/types/common/SharedStatePrivate.d.ts +0 -21
- package/types/common/SharedStatePrivate.d.ts.map +0 -1
- package/types/common/constants.d.ts.map +0 -1
- package/types/common/logger.d.ts.map +0 -1
- package/types/common/sockets-utils.d.ts.map +0 -1
- package/types/common/version.d.ts.map +0 -1
- package/types/server/Client.d.ts +0 -45
- package/types/server/Client.d.ts.map +0 -1
- package/types/server/Context.d.ts.map +0 -1
- package/types/server/ContextManager.d.ts +0 -69
- package/types/server/ContextManager.d.ts.map +0 -1
- package/types/server/Plugin.d.ts.map +0 -1
- package/types/server/PluginManager.d.ts.map +0 -1
- package/types/server/Server.d.ts.map +0 -1
- package/types/server/SharedState.d.ts +0 -86
- package/types/server/SharedState.d.ts.map +0 -1
- package/types/server/SharedStateCollection.d.ts +0 -41
- package/types/server/SharedStateCollection.d.ts.map +0 -1
- package/types/server/Socket.d.ts +0 -164
- package/types/server/Socket.d.ts.map +0 -1
- package/types/server/Sockets.d.ts +0 -80
- package/types/server/Sockets.d.ts.map +0 -1
- package/types/server/StateManager.d.ts +0 -405
- package/types/server/StateManager.d.ts.map +0 -1
- package/types/server/audit-network-latency.worker.d.ts.map +0 -1
- package/types/server/audit-schema.d.ts.map +0 -1
- package/types/server/crypto.d.ts.map +0 -1
- 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
|
|
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
|
|
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 -
|
|
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
|
-
-
|
|
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
|
|
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.
|
|
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 -
|
|
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:
|
|
50
|
-
"types": "rm -
|
|
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
|
},
|
package/src/client/Client.js
CHANGED
|
@@ -1,11 +1,21 @@
|
|
|
1
1
|
import { isBrowser, isPlainObject } from '@ircam/sc-utils';
|
|
2
2
|
|
|
3
|
-
import
|
|
4
|
-
|
|
5
|
-
|
|
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 './
|
|
8
|
-
import
|
|
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
|
|
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 `
|
|
46
|
-
*
|
|
47
|
-
*
|
|
48
|
-
*
|
|
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
|
|
53
|
-
* const client = new Client({
|
|
54
|
-
*
|
|
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 {
|
|
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
|
-
|
|
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
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
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 {
|
|
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
|
|
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
|
|
207
|
-
* @type {
|
|
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
|
|
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
|
|
219
|
-
* @type {
|
|
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
|
|
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
|
|
231
|
-
* @type {
|
|
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
|
|
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
|
|
243
|
-
* @type {
|
|
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
|
|
245
|
+
* Status of the client.
|
|
251
246
|
*
|
|
252
|
-
* @type {
|
|
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
|
|
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,
|
|
280
|
-
* {@link
|
|
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
|
|
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
|
|
292
|
-
* the {@link
|
|
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(
|
|
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
|
|
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
|
|
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.
|
|
371
|
-
*
|
|
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
|
|
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
|
|
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
|
|
419
|
-
await this.#pluginManager
|
|
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
|
|
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<
|
|
430
|
+
* @returns {Promise<SharedState>}
|
|
436
431
|
* @throws Will throw if called before `client.init()`
|
|
437
|
-
* @see {@link
|
|
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
|
|
462
|
-
return () => this
|
|
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
|
|
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,
|
|
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
|
|
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
|
|
62
|
+
class ClientContext {
|
|
63
|
+
#client = null;
|
|
64
|
+
|
|
60
65
|
/**
|
|
61
|
-
* @param {
|
|
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:
|
|
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
|
-
|
|
71
|
-
|
|
72
|
-
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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. ${
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
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
|
|
261
|
+
export default ClientContext;
|