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