@soundworks/core 4.0.0-alpha.7 → 5.0.0
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 +102 -0
- package/README.md +16 -50
- package/package.json +26 -44
- package/src/client/Client.js +306 -256
- package/src/client/{Context.js → ClientContext.js} +63 -54
- package/src/client/ClientContextManager.js +115 -0
- package/src/client/{Plugin.js → ClientPlugin.js} +23 -19
- package/src/client/{PluginManager.js → ClientPluginManager.js} +43 -40
- package/src/client/ClientSocket.js +270 -0
- package/src/client/{StateManager.js → ClientStateManager.js} +13 -13
- package/src/client/index.js +36 -39
- package/src/common/BasePlugin.js +63 -58
- package/src/common/BasePluginManager.js +191 -142
- package/src/common/BaseStateManager.js +636 -143
- package/src/common/BatchedTransport.js +73 -0
- package/src/common/ParameterBag.js +90 -93
- package/src/common/PromiseStore.js +54 -0
- package/src/common/SharedState.js +821 -0
- package/src/common/SharedStateCollection.js +492 -0
- package/src/common/constants.js +14 -2
- package/src/common/logger.js +64 -68
- package/src/common/shared-state-types.js +82 -0
- package/src/common/sockets-utils.js +0 -64
- package/src/common/version.js +2 -0
- package/src/server/Server.js +559 -821
- package/src/server/ServerClient.js +74 -0
- package/src/server/{Context.js → ServerContext.js} +96 -77
- package/src/server/{ContextManager.js → ServerContextManager.js} +107 -87
- package/src/server/{Plugin.js → ServerPlugin.js} +30 -32
- package/src/server/ServerPluginManager.js +177 -0
- package/src/server/ServerSocket.js +256 -0
- package/src/server/ServerSockets.js +150 -0
- package/src/server/ServerStateManager.js +706 -0
- package/src/server/SharedStatePrivate.js +277 -0
- package/src/server/audit-network-latency.worker.js +19 -4
- package/src/server/{audit-schema.js → audit-state-class-description.js} +3 -2
- package/src/server/create-http-server.js +127 -0
- package/src/server/crypto.js +8 -7
- package/src/server/index.js +60 -17
- package/types/client/Client.d.ts +95 -70
- package/types/client/{Context.d.ts → ClientContext.d.ts} +59 -34
- package/types/client/{ContextManager.d.ts → ClientContextManager.d.ts} +19 -22
- package/types/client/ClientPlugin.d.ts +51 -0
- package/types/client/ClientPluginManager.d.ts +112 -0
- package/types/client/ClientSocket.d.ts +76 -0
- package/types/client/{StateManager.d.ts → ClientStateManager.d.ts} +18 -21
- package/types/client/index.d.ts +33 -6
- package/types/common/BasePlugin.d.ts +64 -53
- package/types/common/BasePluginManager.d.ts +84 -39
- package/types/common/BaseStateManager.d.ts +227 -49
- package/types/common/BatchedTransport.d.ts +17 -0
- package/types/common/ParameterBag.d.ts +73 -76
- package/types/common/PromiseStore.d.ts +11 -0
- package/types/common/SharedState.d.ts +291 -0
- package/types/common/SharedStateCollection.d.ts +311 -0
- package/types/common/constants.d.ts +10 -3
- package/types/common/logger.d.ts +8 -6
- package/types/common/shared-state-types.d.ts +95 -0
- package/types/common/sockets-utils.d.ts +0 -5
- package/types/common/version.d.ts +2 -0
- package/types/server/Server.d.ts +215 -136
- package/types/server/ServerClient.d.ts +45 -0
- package/types/server/ServerContext.d.ts +209 -0
- package/types/server/ServerContextManager.d.ts +75 -0
- package/types/server/ServerPlugin.d.ts +57 -0
- package/types/server/ServerPluginManager.d.ts +115 -0
- package/types/server/ServerSocket.d.ts +69 -0
- package/types/server/ServerSockets.d.ts +42 -0
- package/types/server/ServerStateManager.d.ts +284 -0
- package/types/server/SharedStatePrivate.d.ts +23 -0
- package/types/server/audit-network-latency.worker.d.ts +2 -2
- package/types/server/{audit-schema.d.ts → audit-state-class-description.d.ts} +8 -9
- package/types/server/create-http-server.d.ts +5 -0
- package/types/server/crypto.d.ts +4 -0
- package/types/server/index.d.ts +51 -3
- package/.eslintrc +0 -15
- package/.github/workflows/gh-pages.yml +0 -31
- package/.github/workflows/unit-tests.yml +0 -18
- package/src/client/ContextManager.js +0 -111
- package/src/client/SharedState.js +0 -84
- package/src/client/SharedStateCollection.js +0 -35
- package/src/client/Socket.js +0 -402
- package/src/common/BaseSharedState.js +0 -517
- package/src/common/BaseSharedStateCollection.js +0 -179
- package/src/common/SharedStatePrivate.js +0 -192
- package/src/common/promise-store.js +0 -25
- package/src/server/Client.js +0 -55
- package/src/server/PluginManager.js +0 -170
- package/src/server/SharedState.js +0 -85
- package/src/server/SharedStateCollection.js +0 -36
- package/src/server/Socket.js +0 -396
- package/src/server/Sockets.js +0 -224
- package/src/server/StateManager.js +0 -620
- package/tsconfig.json +0 -21
- 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 +0 -53
- package/types/client/Plugin.d.ts.map +0 -1
- package/types/client/PluginManager.d.ts +0 -96
- 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/Socket.d.ts +0 -138
- 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 -173
- package/types/common/BaseSharedState.d.ts.map +0 -1
- package/types/common/BaseStateManager.d.ts.map +0 -1
- package/types/common/ParameterBag.d.ts.map +0 -1
- package/types/common/SharedStatePrivate.d.ts +0 -20
- 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/promise-store.d.ts +0 -4
- package/types/common/promise-store.d.ts.map +0 -1
- package/types/common/sockets-utils.d.ts.map +0 -1
- package/types/common/utils.d.ts +0 -5
- package/types/common/utils.d.ts.map +0 -1
- package/types/server/Client.d.ts +0 -40
- package/types/server/Client.d.ts.map +0 -1
- package/types/server/Context.d.ts +0 -59
- 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 +0 -70
- package/types/server/Plugin.d.ts.map +0 -1
- package/types/server/PluginManager.d.ts +0 -91
- 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/Socket.d.ts +0 -128
- package/types/server/Socket.d.ts.map +0 -1
- package/types/server/Sockets.d.ts +0 -73
- package/types/server/Sockets.d.ts.map +0 -1
- package/types/server/StateManager.d.ts +0 -190
- 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/index.d.ts.map +0 -1
|
@@ -1,4 +1,8 @@
|
|
|
1
1
|
import Client from './Client.js';
|
|
2
|
+
import {
|
|
3
|
+
kClientContextManagerRegister,
|
|
4
|
+
} from './ClientContextManager.js';
|
|
5
|
+
import PromiseStore from '../common/PromiseStore.js';
|
|
2
6
|
import {
|
|
3
7
|
CONTEXT_ENTER_REQUEST,
|
|
4
8
|
CONTEXT_ENTER_RESPONSE,
|
|
@@ -7,19 +11,19 @@ import {
|
|
|
7
11
|
CONTEXT_EXIT_RESPONSE,
|
|
8
12
|
CONTEXT_EXIT_ERROR,
|
|
9
13
|
} from '../common/constants.js';
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
14
|
+
|
|
15
|
+
export const kClientContextStatus = Symbol('soundworks:client-context-status');
|
|
16
|
+
|
|
17
|
+
// share between all context, as channels are common to all contexts
|
|
18
|
+
const promiseStore = new PromiseStore('Context');
|
|
15
19
|
|
|
16
20
|
/**
|
|
17
21
|
* Base class to extend in order to implement a new Context.
|
|
18
22
|
*
|
|
19
23
|
* In the `soundworks` paradigm, a client has a "role" (e.g. _player_, _controller_)
|
|
20
|
-
* see {@link
|
|
24
|
+
* see {@link Client#role}) and can be in different "contexts" (e.g. different
|
|
21
25
|
* part of the experience such as sections of a music piece, etc.). This class
|
|
22
|
-
* provides a simple and unified way to model these
|
|
26
|
+
* provides a simple and unified way to model these recurring aspects of an application.
|
|
23
27
|
*
|
|
24
28
|
* You can also think of a `Context` as a state of a state machine from which a
|
|
25
29
|
* client can `enter()` or `exit()` (be aware that `soundworks` does not provide
|
|
@@ -30,12 +34,12 @@ import {
|
|
|
30
34
|
* the context. In such case, `soundworks` guarantees that the server-side
|
|
31
35
|
* logic is executed before the `enter()` and `exit()` promises are fulfilled.
|
|
32
36
|
*
|
|
33
|
-
* ```
|
|
34
|
-
* import { Client,
|
|
37
|
+
* ```js
|
|
38
|
+
* import { Client, ClientContext } from '@soundworks/core/index.js'
|
|
35
39
|
*
|
|
36
40
|
* const client = new Client(config);
|
|
37
41
|
*
|
|
38
|
-
* class MyContext extends
|
|
42
|
+
* class MyContext extends ClientContext {
|
|
39
43
|
* async enter() {
|
|
40
44
|
* await super.enter();
|
|
41
45
|
* console.log(`client ${this.client.id} entered my context`);
|
|
@@ -54,73 +58,78 @@ import {
|
|
|
54
58
|
* await new Promise(resolve => setTimeout(resolve, 2000));
|
|
55
59
|
* await myContext.exit();
|
|
56
60
|
* ```
|
|
57
|
-
*
|
|
58
|
-
* @memberof client
|
|
59
61
|
*/
|
|
60
|
-
class
|
|
62
|
+
class ClientContext {
|
|
63
|
+
#client = null;
|
|
64
|
+
|
|
61
65
|
/**
|
|
62
|
-
* @param {
|
|
66
|
+
* @param {Client} client - The soundworks client instance.
|
|
63
67
|
* @throws Will throw if the first argument is not a soundworks client instance.
|
|
64
68
|
*/
|
|
65
69
|
constructor(client) {
|
|
66
70
|
if (!(client instanceof Client)) {
|
|
67
|
-
throw new
|
|
71
|
+
throw new TypeError(`Cannot create 'ClientContext' (${this.name}): Argument 1 must be an instance of Client`);
|
|
68
72
|
}
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
*/
|
|
75
|
-
this.client = client;
|
|
76
|
-
|
|
77
|
-
/**
|
|
78
|
-
* Status of the context, i.e. 'idle', 'inited', 'started', 'errored'
|
|
79
|
-
* @type {string}
|
|
80
|
-
* @readonly
|
|
81
|
-
*/
|
|
82
|
-
this.status = 'idle';
|
|
83
|
-
|
|
84
|
-
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) => {
|
|
85
78
|
if (contextName !== this.name) {
|
|
86
79
|
return;
|
|
87
80
|
}
|
|
88
81
|
|
|
89
|
-
|
|
82
|
+
promiseStore.resolve(reqId);
|
|
90
83
|
});
|
|
91
84
|
|
|
92
|
-
this
|
|
85
|
+
this.#client.socket.addListener(CONTEXT_ENTER_ERROR, (reqId, contextName, msg) => {
|
|
93
86
|
if (contextName !== this.name) {
|
|
94
87
|
return;
|
|
95
88
|
}
|
|
96
89
|
|
|
97
|
-
|
|
90
|
+
promiseStore.reject(reqId, `Cannot execute 'enter' on Context: ${msg}`);
|
|
98
91
|
});
|
|
99
92
|
|
|
100
|
-
this
|
|
93
|
+
this.#client.socket.addListener(CONTEXT_EXIT_RESPONSE, (reqId, contextName) => {
|
|
101
94
|
if (contextName !== this.name) {
|
|
102
95
|
return;
|
|
103
96
|
}
|
|
104
97
|
|
|
105
|
-
|
|
98
|
+
promiseStore.resolve(reqId);
|
|
106
99
|
});
|
|
107
100
|
|
|
108
|
-
this
|
|
101
|
+
this.#client.socket.addListener(CONTEXT_EXIT_ERROR, (reqId, contextName, msg) => {
|
|
109
102
|
if (contextName !== this.name) {
|
|
110
103
|
return;
|
|
111
104
|
}
|
|
112
105
|
|
|
113
|
-
|
|
106
|
+
promiseStore.reject(reqId, `Cannot execute 'exit' on Context: ${msg}`);
|
|
114
107
|
});
|
|
115
108
|
|
|
116
|
-
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];
|
|
117
126
|
}
|
|
118
127
|
|
|
119
128
|
/**
|
|
120
|
-
*
|
|
129
|
+
* Optional user-defined name of the context (defaults to the class name).
|
|
121
130
|
*
|
|
122
131
|
* The context manager will match the client-side and server-side contexts based
|
|
123
|
-
* on this name. If the {@link
|
|
132
|
+
* on this name. If the {@link ServerContextManager} don't find a corresponding
|
|
124
133
|
* user-defined context with the same name, it will use a default (noop) context.
|
|
125
134
|
*
|
|
126
135
|
* @readonly
|
|
@@ -138,10 +147,10 @@ class Context {
|
|
|
138
147
|
}
|
|
139
148
|
|
|
140
149
|
/**
|
|
141
|
-
* Start the context. This method is
|
|
142
|
-
* context for the first time (cf. ${
|
|
150
|
+
* Start the context. This method is lazily called when the client enters the
|
|
151
|
+
* context for the first time (cf. ${ClientContext#enter}). If you know some
|
|
143
152
|
* some heavy and/or potentially long job has to be done when starting the context
|
|
144
|
-
* (e.g. call to a web service) it may be a good practice to call it
|
|
153
|
+
* (e.g. call to a web service) it may be a good practice to call it explicitly.
|
|
145
154
|
*
|
|
146
155
|
* This method should be implemented to perform operations that are valid for the
|
|
147
156
|
* whole lifetime of the context, regardless the client enters or exits the context.
|
|
@@ -178,12 +187,12 @@ class Context {
|
|
|
178
187
|
* done (e.g. creating a shared state, etc.) when the context is entered.
|
|
179
188
|
*
|
|
180
189
|
* If a server-side part of the context is defined (i.e. a context with the same
|
|
181
|
-
* {@link
|
|
190
|
+
* {@link ClientContext#name}), the corresponding server-side `enter()` method
|
|
182
191
|
* will be executed before the returned Promise is fulfilled.
|
|
183
192
|
*
|
|
184
|
-
* If the context has not been started yet, the `start` method is
|
|
193
|
+
* If the context has not been started yet, the `start` method is implicitly executed.
|
|
185
194
|
*
|
|
186
|
-
* @returns {Promise} - Promise that resolves when the context is entered.
|
|
195
|
+
* @returns {Promise<void>} - Promise that resolves when the context is entered.
|
|
187
196
|
* @example
|
|
188
197
|
* class MyContext extends Context {
|
|
189
198
|
* async enter() {
|
|
@@ -200,14 +209,14 @@ class Context {
|
|
|
200
209
|
async enter() {
|
|
201
210
|
// lazily start the context if registered after `client.start()`
|
|
202
211
|
if (this.status !== 'started' && this.status !== 'errored') {
|
|
203
|
-
await this
|
|
212
|
+
await this.#client.contextManager.get(this.name);
|
|
204
213
|
}
|
|
205
214
|
|
|
206
215
|
// we need the try/catch block to change the promise rejection into proper error
|
|
207
216
|
try {
|
|
208
217
|
await new Promise((resolve, reject) => {
|
|
209
|
-
const reqId =
|
|
210
|
-
this
|
|
218
|
+
const reqId = promiseStore.add(resolve, reject, 'ClientContext#enter');
|
|
219
|
+
this.#client.socket.send(CONTEXT_ENTER_REQUEST, reqId, this.name);
|
|
211
220
|
});
|
|
212
221
|
} catch (err) {
|
|
213
222
|
throw new Error(err);
|
|
@@ -219,10 +228,10 @@ class Context {
|
|
|
219
228
|
* done (e.g. delete a shared state, etc.) when the context is exited.
|
|
220
229
|
*
|
|
221
230
|
* If a server-side part of the context is defined (i.e. a context with the same
|
|
222
|
-
* {@link
|
|
231
|
+
* {@link ClientContext#name}), the corresponding server-side `exit()` method
|
|
223
232
|
* will be executed before the returned Promise is fulfilled.
|
|
224
233
|
*
|
|
225
|
-
* @returns {Promise} - Promise that resolves when the context is exited.
|
|
234
|
+
* @returns {Promise<void>} - Promise that resolves when the context is exited.
|
|
226
235
|
* @example
|
|
227
236
|
* class MyContext extends Context {
|
|
228
237
|
* async enter() {
|
|
@@ -240,8 +249,8 @@ class Context {
|
|
|
240
249
|
// we need the try/catch block to change the promise rejection into proper error
|
|
241
250
|
try {
|
|
242
251
|
await new Promise((resolve, reject) => {
|
|
243
|
-
const reqId =
|
|
244
|
-
this
|
|
252
|
+
const reqId = promiseStore.add(resolve, reject, 'ClientContext#exit');
|
|
253
|
+
this.#client.socket.send(CONTEXT_EXIT_REQUEST, reqId, this.name);
|
|
245
254
|
});
|
|
246
255
|
} catch (err) {
|
|
247
256
|
throw new Error(err);
|
|
@@ -249,4 +258,4 @@ class Context {
|
|
|
249
258
|
}
|
|
250
259
|
}
|
|
251
260
|
|
|
252
|
-
export default
|
|
261
|
+
export default ClientContext;
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import {
|
|
2
|
+
kClientContextStatus,
|
|
3
|
+
} from './ClientContext.js';
|
|
4
|
+
|
|
5
|
+
export const kClientContextManagerStart = Symbol('soundworks:client-context-manager-start');
|
|
6
|
+
export const kClientContextManagerStop = Symbol('soundworks:client-context-manager-stop');
|
|
7
|
+
export const kClientContextManagerRegister = Symbol('soundworks:client-context-manager-register');
|
|
8
|
+
|
|
9
|
+
/** @private */
|
|
10
|
+
class ContextCollection {
|
|
11
|
+
#inner = [];
|
|
12
|
+
|
|
13
|
+
constructor() {
|
|
14
|
+
this.#inner = [];
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
add(context) {
|
|
18
|
+
this.#inner.push(context);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
has(name) {
|
|
22
|
+
return this.#inner.find(c => c.name === name) !== undefined;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
get(name) {
|
|
26
|
+
return this.#inner.find(c => c.name === name);
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
map(func) {
|
|
30
|
+
return this.#inner.map(func);
|
|
31
|
+
}
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
/**
|
|
35
|
+
* Manage the different contexts and their lifecycle.
|
|
36
|
+
*
|
|
37
|
+
* See {@link Client#contextManager}
|
|
38
|
+
*
|
|
39
|
+
* _WARNING: In most cases, you should not have to manipulate the context manager directly._
|
|
40
|
+
*
|
|
41
|
+
* @hideconstructor
|
|
42
|
+
*/
|
|
43
|
+
class ClientContextManager {
|
|
44
|
+
#contexts = new ContextCollection();
|
|
45
|
+
#contextStartPromises = new Map();
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Register the context into the manager. Is called in context constructor.
|
|
49
|
+
*
|
|
50
|
+
* @param {ClientContext} context - The context to be registered.
|
|
51
|
+
* @private
|
|
52
|
+
*/
|
|
53
|
+
[kClientContextManagerRegister](context) {
|
|
54
|
+
if (this.#contexts.has(context.name)) {
|
|
55
|
+
throw new DOMException(`Cannot register '${context.name}': a Context with same name has already been registered`, 'NotSupportedError');
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.#contexts.add(context);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/**
|
|
62
|
+
* Start all registered contexts. Called during `client.start()`
|
|
63
|
+
* @private
|
|
64
|
+
*/
|
|
65
|
+
async [kClientContextManagerStart]() {
|
|
66
|
+
const promises = this.#contexts.map(context => this.get(context.name));
|
|
67
|
+
await Promise.all(promises);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
/**
|
|
71
|
+
* Stop all registered contexts. Called during `client.stop()`
|
|
72
|
+
* @private
|
|
73
|
+
*/
|
|
74
|
+
async [kClientContextManagerStop]() {
|
|
75
|
+
const promises = this.#contexts.map(context => context.stop());
|
|
76
|
+
await Promise.all(promises);
|
|
77
|
+
}
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* Retrieve a started context from its name.
|
|
81
|
+
*
|
|
82
|
+
* _WARNING: Most of the time, you should not have to call this method manually._
|
|
83
|
+
*
|
|
84
|
+
* @param {string} contextName - Name of the context.
|
|
85
|
+
* @returns {Promise<ClientContext>}
|
|
86
|
+
*/
|
|
87
|
+
async get(contextName) {
|
|
88
|
+
if (!this.#contexts.has(contextName)) {
|
|
89
|
+
throw new ReferenceError(`Cannot execute 'get' on ClientContextManager: Context '${contextName}' is not registered`);
|
|
90
|
+
}
|
|
91
|
+
|
|
92
|
+
const context = this.#contexts.get(contextName);
|
|
93
|
+
|
|
94
|
+
if (this.#contextStartPromises.has(contextName)) {
|
|
95
|
+
const startPromise = this.#contextStartPromises.get(contextName);
|
|
96
|
+
await startPromise;
|
|
97
|
+
} else {
|
|
98
|
+
context[kClientContextStatus] = 'inited';
|
|
99
|
+
|
|
100
|
+
try {
|
|
101
|
+
const startPromise = context.start();
|
|
102
|
+
this.#contextStartPromises.set(contextName, startPromise);
|
|
103
|
+
await startPromise;
|
|
104
|
+
context[kClientContextStatus] = 'started';
|
|
105
|
+
} catch (err) {
|
|
106
|
+
context[kClientContextStatus] = 'errored';
|
|
107
|
+
throw new Error(`Cannot execute 'get' on ClientContextManager: ${err.message}`);
|
|
108
|
+
}
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
return context;
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
export default ClientContextManager;
|
|
@@ -3,14 +3,14 @@ import BasePlugin from '../common/BasePlugin.js';
|
|
|
3
3
|
/**
|
|
4
4
|
* Callback executed when the plugin state is updated.
|
|
5
5
|
*
|
|
6
|
-
* @callback
|
|
7
|
-
* @param {
|
|
6
|
+
* @callback ClientPlugin~onStateChangeCallback
|
|
7
|
+
* @param {ClientPlugin#state} state - Current state of the plugin.
|
|
8
8
|
*/
|
|
9
9
|
|
|
10
10
|
/**
|
|
11
|
-
* Delete the registered {@link
|
|
11
|
+
* Delete the registered {@link ClientPlugin~onStateChangeCallback}.
|
|
12
12
|
*
|
|
13
|
-
* @callback
|
|
13
|
+
* @callback ClientPlugin~deleteOnStateChangeCallback
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
@@ -24,44 +24,48 @@ import BasePlugin from '../common/BasePlugin.js';
|
|
|
24
24
|
* have both a client-side and a server-side part.
|
|
25
25
|
*
|
|
26
26
|
* See [https://soundworks.dev/guide/ecosystem](https://soundworks.dev/guide/ecosystem)
|
|
27
|
-
* for more
|
|
27
|
+
* for more information on the available plugins.
|
|
28
28
|
*
|
|
29
29
|
* _Creating new plugins should be considered an advanced usage._
|
|
30
30
|
*
|
|
31
|
-
* @memberof client
|
|
32
31
|
* @extends BasePlugin
|
|
33
32
|
* @inheritdoc
|
|
34
33
|
*/
|
|
35
|
-
class
|
|
34
|
+
class ClientPlugin extends BasePlugin {
|
|
35
|
+
#client = null;
|
|
36
36
|
/**
|
|
37
|
-
* @param {
|
|
37
|
+
* @param {Client} client - A soundworks client instance.
|
|
38
38
|
* @param {string} id - User defined id of the plugin as defined in
|
|
39
|
-
* {@link
|
|
39
|
+
* {@link ClientPluginManager#register}.
|
|
40
40
|
*/
|
|
41
41
|
constructor(client, id) {
|
|
42
42
|
super(id);
|
|
43
43
|
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
44
|
+
this.#client = client;
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Instance of soundworks client.
|
|
49
|
+
*
|
|
50
|
+
* @type {Client}
|
|
51
|
+
* @see {@link Client}
|
|
52
|
+
*/
|
|
53
|
+
get client() {
|
|
54
|
+
return this.#client;
|
|
51
55
|
}
|
|
52
56
|
|
|
53
57
|
// @todo
|
|
54
58
|
// /**
|
|
55
59
|
// * Interface method to implement if specific logic should be done when a
|
|
56
|
-
// * {@link
|
|
60
|
+
// * {@link Client} enters the plugin.
|
|
57
61
|
// */
|
|
58
62
|
// async activate() {}
|
|
59
63
|
//
|
|
60
64
|
// /**
|
|
61
65
|
// * Interface method to implement if specific logic should be done when a
|
|
62
|
-
// * {@link
|
|
66
|
+
// * {@link Client} exists the plugin.
|
|
63
67
|
// */
|
|
64
68
|
// async deactivate() {}
|
|
65
69
|
}
|
|
66
70
|
|
|
67
|
-
export default
|
|
71
|
+
export default ClientPlugin;
|
|
@@ -1,38 +1,22 @@
|
|
|
1
1
|
import BasePluginManager from '../common/BasePluginManager.js';
|
|
2
|
-
import
|
|
2
|
+
import ClientPlugin from './ClientPlugin.js';
|
|
3
3
|
import Client from './Client.js';
|
|
4
4
|
|
|
5
|
-
/**
|
|
6
|
-
* Callback executed when a plugin internal state is updated.
|
|
7
|
-
*
|
|
8
|
-
* @callback client.PluginManager~onStateChangeCallback
|
|
9
|
-
* @param {object<client.Plugin#id, client.Plugin>} fullState - List of all plugins.
|
|
10
|
-
* @param {client.Plugin|null} initiator - Plugin that initiated the update or `null`
|
|
11
|
-
* if the change was initiated by the state manager (i.e. when the initialization
|
|
12
|
-
* of the plugins starts).
|
|
13
|
-
*/
|
|
14
|
-
|
|
15
|
-
/**
|
|
16
|
-
* Delete the registered {@link client.PluginManager~onStateChangeCallback}.
|
|
17
|
-
*
|
|
18
|
-
* @callback client.PluginManager~deleteOnStateChangeCallback
|
|
19
|
-
*/
|
|
20
|
-
|
|
21
5
|
/**
|
|
22
6
|
* The `PluginManager` allows to register and retrieve `soundworks` plugins.
|
|
23
7
|
*
|
|
24
8
|
* Plugins should always be registered both client-side and server-side,
|
|
25
|
-
* and before {@link
|
|
9
|
+
* and before {@link Client#start} or {@link Server#start}
|
|
26
10
|
* to be properly initialized.
|
|
27
11
|
*
|
|
28
|
-
* In some
|
|
12
|
+
* In some situations, you might want to register the same plugin factory several times
|
|
29
13
|
* using different ids (e.g. for watching several parts of the file system, etc.).
|
|
30
14
|
*
|
|
31
15
|
* Refer to the plugins' documentation for more precise examples, and the specific API
|
|
32
16
|
* they expose. See [https://soundworks.dev/guide/ecosystem](https://soundworks.dev/guide/ecosystem)
|
|
33
|
-
* for more
|
|
17
|
+
* for more information on the available plugins.
|
|
34
18
|
*
|
|
35
|
-
* See {@link
|
|
19
|
+
* See {@link Client#pluginManager}
|
|
36
20
|
*
|
|
37
21
|
* ```
|
|
38
22
|
* // client-side
|
|
@@ -72,28 +56,50 @@ import Client from './Client.js';
|
|
|
72
56
|
* }, 1000);
|
|
73
57
|
* ```
|
|
74
58
|
*
|
|
75
|
-
* @memberof client
|
|
76
59
|
* @extends BasePluginManager
|
|
77
60
|
* @inheritdoc
|
|
78
|
-
* @
|
|
61
|
+
* @template T
|
|
79
62
|
*/
|
|
80
|
-
class
|
|
63
|
+
class ClientPluginManager extends BasePluginManager {
|
|
81
64
|
/**
|
|
82
|
-
* @param {
|
|
65
|
+
* @param {Client} client - The soundworks client instance.
|
|
66
|
+
* @hideconstructor
|
|
83
67
|
*/
|
|
84
68
|
constructor(client) {
|
|
85
69
|
if (!(client instanceof Client)) {
|
|
86
|
-
throw new
|
|
70
|
+
throw new TypeError(`Cannot construct ClientPluginManager: Argument 1 must be an instance of Client`);
|
|
87
71
|
}
|
|
88
72
|
|
|
89
73
|
super(client);
|
|
90
74
|
}
|
|
91
75
|
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
76
|
+
/**
|
|
77
|
+
* Register a plugin into the manager.
|
|
78
|
+
*
|
|
79
|
+
* _A plugin must always be registered both on client-side and on server-side_
|
|
80
|
+
*
|
|
81
|
+
* Refer to the plugin documentation to check its options and proper way of
|
|
82
|
+
* registering it.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} id - Unique id of the plugin. Enables the registration of the
|
|
85
|
+
* same plugin factory under different ids.
|
|
86
|
+
* @param {T<ClientPlugin>} ctor - The client-side Class of the plugin.
|
|
87
|
+
* @param {object} [options={}] - Options to configure the plugin.
|
|
88
|
+
* @param {array} [deps=[]] - List of plugins' names the plugin depends on, i.e.
|
|
89
|
+
* the plugin initialization will start only after the plugins it depends on are
|
|
90
|
+
* fully started themselves.
|
|
91
|
+
* @see {@link ClientPluginManager#register}
|
|
92
|
+
* @see {@link ServerPluginManager#register}
|
|
93
|
+
* @example
|
|
94
|
+
* // client-side
|
|
95
|
+
* client.pluginManager.register('user-defined-id', pluginFactory);
|
|
96
|
+
* // server-side
|
|
97
|
+
* server.pluginManager.register('user-defined-id', pluginFactory);
|
|
98
|
+
*/
|
|
99
|
+
register(id, ctor, options = {}, deps = []) {
|
|
100
|
+
// Note that other arguments are checked on the BasePluginManager
|
|
101
|
+
if (!ctor || !(ctor.prototype instanceof ClientPlugin)) {
|
|
102
|
+
throw new TypeError(`Cannot execute 'register' on ClientPluginManager: argument 2 must be a class that extends 'ClientPlugin'`);
|
|
97
103
|
}
|
|
98
104
|
|
|
99
105
|
super.register(id, ctor, options, deps);
|
|
@@ -112,21 +118,18 @@ class PluginManager extends BasePluginManager {
|
|
|
112
118
|
* on the `onStateChange` method.
|
|
113
119
|
*
|
|
114
120
|
* _Note: the async API is designed to enable the dynamic creation of plugins
|
|
115
|
-
*
|
|
116
|
-
*
|
|
117
|
-
* @param {client.Plugin#id} id - Id of the plugin as defined when registered.
|
|
118
|
-
* @returns {client.Plugin}
|
|
119
|
-
* @see {@link client.PluginManager#onStateChange}
|
|
121
|
+
* in a future release._
|
|
120
122
|
*
|
|
121
|
-
* @
|
|
123
|
+
* @param {string} id - Id of the plugin as defined when registered.
|
|
124
|
+
* @returns {Promise<T<ClientPlugin>>}
|
|
122
125
|
*/
|
|
123
126
|
async get(id) {
|
|
124
127
|
if (this.status !== 'started') {
|
|
125
|
-
throw new
|
|
128
|
+
throw new DOMException(`Cannot execute 'get' on ClientPluginManager: 'Client#init' has not been called yet`, 'NotSupportedError');
|
|
126
129
|
}
|
|
127
130
|
|
|
128
|
-
return super.
|
|
131
|
+
return super.getUnsafe(id);
|
|
129
132
|
}
|
|
130
133
|
}
|
|
131
134
|
|
|
132
|
-
export default
|
|
135
|
+
export default ClientPluginManager;
|