@soundworks/core 4.0.0-alpha.0 → 4.0.0-alpha.2
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/README.md +26 -16
- package/package.json +2 -2
- package/src/client/Client.js +37 -3
- package/src/client/Context.js +37 -10
- package/src/client/Plugin.js +2 -1
- package/src/client/PluginManager.js +26 -17
- package/src/client/Socket.js +44 -39
- package/src/client/index.js +0 -33
- package/src/common/BasePluginManager.js +12 -3
- package/src/common/ParameterBag.js +7 -196
- package/src/server/Context.js +162 -18
- package/src/server/ContextManager.js +4 -5
- package/src/server/Plugin.js +2 -1
- package/src/server/PluginManager.js +26 -6
- package/src/server/Server.js +185 -69
- package/src/server/Socket.js +105 -62
- package/src/server/Sockets.js +27 -22
- package/src/server/StateManager.js +223 -24
- package/src/server/index.js +0 -20
|
@@ -1,195 +1,6 @@
|
|
|
1
1
|
import cloneDeep from 'lodash.clonedeep';
|
|
2
2
|
import equal from 'fast-deep-equal';
|
|
3
3
|
|
|
4
|
-
/**
|
|
5
|
-
* @typedef {Object} server.SharedStateManagerServer~schema
|
|
6
|
-
*
|
|
7
|
-
* Description of a schema to be register by the {@link server.ServerStateManagerServer}
|
|
8
|
-
* A schema consists of a combinaison of key value pairs where the key is the
|
|
9
|
-
* name of the parameter, and the value is an object describing the parameter.
|
|
10
|
-
*
|
|
11
|
-
* Available types are:
|
|
12
|
-
* - {@link server.SharedStateManagerServer~schemaBooleanDef}
|
|
13
|
-
* - {@link server.SharedStateManagerServer~schemaStringDef}
|
|
14
|
-
* - {@link server.SharedStateManagerServer~schemaIntegerDef}
|
|
15
|
-
* - {@link server.SharedStateManagerServer~schemaFloatDef}
|
|
16
|
-
* - {@link server.SharedStateManagerServer~schemaEnumDef}
|
|
17
|
-
* - {@link server.SharedStateManagerServer~schemaAnyDef}
|
|
18
|
-
*
|
|
19
|
-
* @example
|
|
20
|
-
* {
|
|
21
|
-
* myBoolean: {
|
|
22
|
-
* type: 'boolean'
|
|
23
|
-
* default: false,
|
|
24
|
-
* },
|
|
25
|
-
* myFloat: {
|
|
26
|
-
* type: 'float'
|
|
27
|
-
* default: 0.1,
|
|
28
|
-
* min: -1,
|
|
29
|
-
* max: 1,
|
|
30
|
-
* event: true,
|
|
31
|
-
* }
|
|
32
|
-
* }
|
|
33
|
-
*/
|
|
34
|
-
/**
|
|
35
|
-
* @typedef {Object} server.SharedStateManagerServer~schemaBooleanDef
|
|
36
|
-
*
|
|
37
|
-
* @property {String} type='boolean' - Define a boolean parameter.
|
|
38
|
-
* @property {Boolean} default - Default value of the parameter.
|
|
39
|
-
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
|
|
40
|
-
* set to `true` the parameter `default` is set to `null`.
|
|
41
|
-
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
|
|
42
|
-
* set its value back to `null` after propagation. When `true`, `nullable` is
|
|
43
|
-
* automatically set to `true` and `default` to `null`.
|
|
44
|
-
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
|
|
45
|
-
* the propagation of a parameter even when its value do not change. It
|
|
46
|
-
* offers a kind of middle ground between the default bahavior (e.g. where
|
|
47
|
-
* only changed values are propagated) and the behavior of the `event` option
|
|
48
|
-
* (which has no state per se). As such, setting this options to `false` if
|
|
49
|
-
* `event=true` does not make sens.
|
|
50
|
-
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
51
|
-
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
52
|
-
* state that generate the update (i.e. calling `set`), before propagating the
|
|
53
|
-
* change on the network. This option can be usefull in cases the network
|
|
54
|
-
* would introduce a noticeable latency on the client. If for some reason
|
|
55
|
-
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
56
|
-
* will be called again on when the "real" / final value will be received.
|
|
57
|
-
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
58
|
-
*/
|
|
59
|
-
/**
|
|
60
|
-
* @typedef {Object} server.SharedStateManagerServer~schemaStringDef
|
|
61
|
-
*
|
|
62
|
-
* @property {String} type='string' - Define a boolean parameter.
|
|
63
|
-
* @property {Mixed} default - Default value of the parameter.
|
|
64
|
-
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
|
|
65
|
-
* set to `true` the parameter `default` is set to `null`.
|
|
66
|
-
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
|
|
67
|
-
* set its value back to `null` after propagation. When `true`, `nullable` is
|
|
68
|
-
* automatically set to `true` and `default` to `null`.
|
|
69
|
-
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
|
|
70
|
-
* the propagation of a parameter even when its value do not change. It
|
|
71
|
-
* offers a kind of middle ground between the default bahavior (e.g. where
|
|
72
|
-
* only changed values are propagated) and the behavior of the `event` option
|
|
73
|
-
* (which has no state per se). As such, setting this options to `false` if
|
|
74
|
-
* `event=true` does not make sens.
|
|
75
|
-
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
76
|
-
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
77
|
-
* state that generate the update (i.e. calling `set`), before propagating the
|
|
78
|
-
* change on the network. This option can be usefull in cases the network
|
|
79
|
-
* would introduce a noticeable latency on the client. If for some reason
|
|
80
|
-
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
81
|
-
* will be called again on when the "real" / final value will be received.
|
|
82
|
-
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
83
|
-
*/
|
|
84
|
-
/**
|
|
85
|
-
@typedef {Object} server.SharedStateManagerServer~schemaIntegerDef
|
|
86
|
-
*
|
|
87
|
-
* @property {String} type='integer' - Define a boolean parameter.
|
|
88
|
-
* @property {Mixed} default - Default value of the parameter.
|
|
89
|
-
* @property {Number} [min=-Infinity] - Minimum value of the parameter.
|
|
90
|
-
* @property {Number} [max=+Infinity] - Maximum value of the parameter.
|
|
91
|
-
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
|
|
92
|
-
* set to `true` the parameter `default` is set to `null`.
|
|
93
|
-
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
|
|
94
|
-
* set its value back to `null` after propagation. When `true`, `nullable` is
|
|
95
|
-
* automatically set to `true` and `default` to `null`.
|
|
96
|
-
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
|
|
97
|
-
* the propagation of a parameter even when its value do not change. It
|
|
98
|
-
* offers a kind of middle ground between the default bahavior (e.g. where
|
|
99
|
-
* only changed values are propagated) and the behavior of the `event` option
|
|
100
|
-
* (which has no state per se). As such, setting this options to `false` if
|
|
101
|
-
* `event=true` does not make sens.
|
|
102
|
-
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
103
|
-
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
104
|
-
* state that generate the update (i.e. calling `set`), before propagating the
|
|
105
|
-
* change on the network. This option can be usefull in cases the network
|
|
106
|
-
* would introduce a noticeable latency on the client. If for some reason
|
|
107
|
-
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
108
|
-
* will be called again on when the "real" / final value will be received.
|
|
109
|
-
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
110
|
-
*/
|
|
111
|
-
/**
|
|
112
|
-
* @typedef {Object} server.SharedStateManagerServer~schemaFloatDef
|
|
113
|
-
*
|
|
114
|
-
* @property {String} [type='float'] - Float parameter.
|
|
115
|
-
* @property {Mixed} default - Default value.
|
|
116
|
-
* @property {Number} [min=-Infinity] - Minimum value.
|
|
117
|
-
* @property {Number} [max=+Infinity] - Maximum value.
|
|
118
|
-
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
|
|
119
|
-
* set to `true` the parameter `default` is set to `null`.
|
|
120
|
-
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
|
|
121
|
-
* set its value back to `null` after propagation. When `true`, `nullable` is
|
|
122
|
-
* automatically set to `true` and `default` to `null`.
|
|
123
|
-
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
|
|
124
|
-
* the propagation of a parameter even when its value do not change. It
|
|
125
|
-
* offers a kind of middle ground between the default bahavior (e.g. where
|
|
126
|
-
* only changed values are propagated) and the behavior of the `event` option
|
|
127
|
-
* (which has no state per se). As such, setting this options to `false` if
|
|
128
|
-
* `event=true` does not make sens.
|
|
129
|
-
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
130
|
-
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
131
|
-
* state that generate the update (i.e. calling `set`), before propagating the
|
|
132
|
-
* change on the network. This option can be usefull in cases the network
|
|
133
|
-
* would introduce a noticeable latency on the client. If for some reason
|
|
134
|
-
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
135
|
-
* will be called again on when the "real" / final value will be received.
|
|
136
|
-
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
137
|
-
*/
|
|
138
|
-
/**
|
|
139
|
-
* @typedef {Object} server.SharedStateManagerServer~schemaEnumDef
|
|
140
|
-
*
|
|
141
|
-
* @property {String} [type='enum'] - Enum parameter.
|
|
142
|
-
* @property {Mixed} default - Default value of the parameter.
|
|
143
|
-
* @property {Array} list - Possible values of the parameter.
|
|
144
|
-
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
|
|
145
|
-
* set to `true` the parameter `default` is set to `null`.
|
|
146
|
-
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
|
|
147
|
-
* set its value back to `null` after propagation. When `true`, `nullable` is
|
|
148
|
-
* automatically set to `true` and `default` to `null`.
|
|
149
|
-
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
|
|
150
|
-
* the propagation of a parameter even when its value do not change. It
|
|
151
|
-
* offers a kind of middle ground between the default bahavior (e.g. where
|
|
152
|
-
* only changed values are propagated) and the behavior of the `event` option
|
|
153
|
-
* (which has no state per se). As such, setting this options to `false` if
|
|
154
|
-
* `event=true` does not make sens.
|
|
155
|
-
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
156
|
-
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
157
|
-
* state that generate the update (i.e. calling `set`), before propagating the
|
|
158
|
-
* change on the network. This option can be usefull in cases the network
|
|
159
|
-
* would introduce a noticeable latency on the client. If for some reason
|
|
160
|
-
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
161
|
-
* will be called again on when the "real" / final value will be received.
|
|
162
|
-
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
163
|
-
*/
|
|
164
|
-
/**
|
|
165
|
-
* @typedef {Object} server.SharedStateManagerServer~schemaAnyDef
|
|
166
|
-
*
|
|
167
|
-
* Note that the `any` type always return a shallow copy of the state internal
|
|
168
|
-
* value. Mutating the returned value will not modify the internal state.
|
|
169
|
-
*
|
|
170
|
-
* @property {String} [type='any'] - Parameter of any type.
|
|
171
|
-
* @property {Mixed} default - Default value of the parameter.
|
|
172
|
-
* @property {Boolean} [nullable=false] - Define if the parameter is nullable. If
|
|
173
|
-
* set to `true` the parameter `default` is set to `null`.
|
|
174
|
-
* @property {Boolean} [event=false] - Define if the parameter is a volatile, e.g.
|
|
175
|
-
* set its value back to `null` after propagation. When `true`, `nullable` is
|
|
176
|
-
* automatically set to `true` and `default` to `null`.
|
|
177
|
-
* @property {Boolean} [filterChange=true] - Setting this option to `false` forces
|
|
178
|
-
* the propagation of a parameter even when its value do not change. It
|
|
179
|
-
* offers a kind of middle ground between the default bahavior (e.g. where
|
|
180
|
-
* only changed values are propagated) and the behavior of the `event` option
|
|
181
|
-
* (which has no state per se). As such, setting this options to `false` if
|
|
182
|
-
* `event=true` does not make sens.
|
|
183
|
-
* @property {Boolean} [immediate=false] - Setting this option to `true` will
|
|
184
|
-
* trigger any change (e.g. call the `onUpdate` listeners) immediately on the
|
|
185
|
-
* state that generate the update (i.e. calling `set`), before propagating the
|
|
186
|
-
* change on the network. This option can be usefull in cases the network
|
|
187
|
-
* would introduce a noticeable latency on the client. If for some reason
|
|
188
|
-
* the value is overriden server-side (e.g. in an `updateHook`) the listeners
|
|
189
|
-
* will be called again on when the "real" / final value will be received.
|
|
190
|
-
* @property {Object} [metas={}] - Optionnal metadata of the parameter.
|
|
191
|
-
*/
|
|
192
|
-
|
|
193
4
|
export const sharedOptions = {
|
|
194
5
|
nullable: false,
|
|
195
6
|
event: false, // if event=true, nullable=true
|
|
@@ -422,7 +233,7 @@ class ParameterBag {
|
|
|
422
233
|
/**
|
|
423
234
|
* Define if the parameter exists.
|
|
424
235
|
*
|
|
425
|
-
* @param {
|
|
236
|
+
* @param {string} name - Name of the parameter.
|
|
426
237
|
* @return {Boolean}
|
|
427
238
|
*/
|
|
428
239
|
has(name) {
|
|
@@ -432,7 +243,7 @@ class ParameterBag {
|
|
|
432
243
|
/**
|
|
433
244
|
* Return values of all parameters as a flat object.
|
|
434
245
|
*
|
|
435
|
-
* @return {
|
|
246
|
+
* @return {object}
|
|
436
247
|
*/
|
|
437
248
|
getValues() {
|
|
438
249
|
let values = {};
|
|
@@ -448,7 +259,7 @@ class ParameterBag {
|
|
|
448
259
|
* Return the value of the given parameter. If the parameter is of `any` type,
|
|
449
260
|
* a deep copy is returned.
|
|
450
261
|
*
|
|
451
|
-
* @param {
|
|
262
|
+
* @param {string} name - Name of the parameter.
|
|
452
263
|
* @return {Mixed} - Value of the parameter.
|
|
453
264
|
*/
|
|
454
265
|
get(name) {
|
|
@@ -468,9 +279,9 @@ class ParameterBag {
|
|
|
468
279
|
* (aka if previous value is different from new value) all registered
|
|
469
280
|
* callbacks are registered.
|
|
470
281
|
*
|
|
471
|
-
* @param {
|
|
282
|
+
* @param {string} name - Name of the parameter.
|
|
472
283
|
* @param {Mixed} value - Value of the parameter.
|
|
473
|
-
* @param {
|
|
284
|
+
* @param {boolean} [forcePropagation=false] - if true, propagate value even
|
|
474
285
|
* if the value has not changed.
|
|
475
286
|
* @return {Array} - [new value, updated flag].
|
|
476
287
|
*/
|
|
@@ -504,7 +315,7 @@ class ParameterBag {
|
|
|
504
315
|
* @note - prefer `state.set(state.getInitValues())`
|
|
505
316
|
* or `state.set(state.getDefaultValues())`
|
|
506
317
|
*
|
|
507
|
-
* @param {
|
|
318
|
+
* @param {string} [name=null] - Name of the parameter to reset.
|
|
508
319
|
*/
|
|
509
320
|
// reset(name = null) {
|
|
510
321
|
// if (name !== null) {
|
|
@@ -519,7 +330,7 @@ class ParameterBag {
|
|
|
519
330
|
/**
|
|
520
331
|
* Return the given schema along with the initialization values.
|
|
521
332
|
*
|
|
522
|
-
* @return {
|
|
333
|
+
* @return {object}
|
|
523
334
|
*/
|
|
524
335
|
getSchema(name = null) {
|
|
525
336
|
if (name === null) {
|
package/src/server/Context.js
CHANGED
|
@@ -2,44 +2,116 @@ import Client from './Client.js';
|
|
|
2
2
|
import Server from './Server.js';
|
|
3
3
|
|
|
4
4
|
/**
|
|
5
|
-
* Base class to extend in order to
|
|
6
|
-
* a {@link client.Context}. If not defined, a default context will be created
|
|
5
|
+
* 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
|
|
7
7
|
* and used by the server.
|
|
8
8
|
*
|
|
9
|
+
* 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
|
|
11
|
+
* part of the experience such as sections of a music piece, etc.). The
|
|
12
|
+
* {@link client.Context} and optionnal {@link serverContext} abstractions provide
|
|
13
|
+
* a simple and unified way to model these reccuring aspects of an application.
|
|
14
|
+
*
|
|
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.
|
|
23
|
+
*
|
|
24
|
+
* ```
|
|
25
|
+
* // client-side
|
|
26
|
+
* import { Context } from '@soundworks/core/client.js';
|
|
27
|
+
*
|
|
28
|
+
* class MyContext extends Context {
|
|
29
|
+
* async enter() {
|
|
30
|
+
* // 1. client side context enter() starts
|
|
31
|
+
* // server-side logic is triggered first
|
|
32
|
+
* await super.enter();
|
|
33
|
+
* // 4. server-side context enter() is fully done
|
|
34
|
+
* // some async job can be done here
|
|
35
|
+
* await new Promise(resolve => setTimeout(resolve, 1000));
|
|
36
|
+
* // 5. client-side context enter() ends
|
|
37
|
+
* }
|
|
38
|
+
* }
|
|
39
|
+
*
|
|
40
|
+
* // Instanciate the context (assuming the `client.role` is 'test')
|
|
41
|
+
* const myContext = new MyContext(client);
|
|
42
|
+
*
|
|
43
|
+
* // At some point in the application, the client enters the context trigerring
|
|
44
|
+
* // the steps 1 to 5 described in the client-side and server-side `enter()`
|
|
45
|
+
* // implementations. Note that the server-side `enter()` is never called manually.
|
|
46
|
+
* await myContext.enter();
|
|
47
|
+
* ```
|
|
48
|
+
*
|
|
49
|
+
* ```
|
|
50
|
+
* // server-side
|
|
51
|
+
* import { Context } from '@soundworks/core/server.js';
|
|
52
|
+
*
|
|
53
|
+
* class MyContext extends Context {
|
|
54
|
+
* async enter(client) {
|
|
55
|
+
* // 2. server-side context enter() starts
|
|
56
|
+
* await super.enter(client);
|
|
57
|
+
* // some async job can be done here
|
|
58
|
+
* await new Promise(resolve => setTimeout(resolve, 1000));
|
|
59
|
+
* // 3. server-side context enter() ends
|
|
60
|
+
* }
|
|
61
|
+
* }
|
|
62
|
+
*
|
|
63
|
+
* // Instantiate the context
|
|
64
|
+
* const myContext = new Context(server);
|
|
65
|
+
* ```
|
|
66
|
+
*
|
|
9
67
|
* @memberof server
|
|
10
68
|
*/
|
|
11
69
|
class Context {
|
|
70
|
+
/**
|
|
71
|
+
* @param {client.Client} client - The soundworks client instance.
|
|
72
|
+
* @param {string|string[]} [roles=[]] - Optionnal list of client roles that can
|
|
73
|
+
* use this context. In large applications, this may be usefull to guarantee
|
|
74
|
+
* that a context can be consumed only by specific client roles, throwing an
|
|
75
|
+
* error if any other client role tries to use it. If empty, no access policy
|
|
76
|
+
* will be used.
|
|
77
|
+
* @throws Will throw if the first argument is not a soundworks server instance.
|
|
78
|
+
*/
|
|
12
79
|
constructor(server, roles = []) {
|
|
13
80
|
if (!(server instanceof Server)) {
|
|
14
81
|
throw new Error(`[soundworks:Context] Invalid argument, context "${this.constructor.name}" should receive a "soundworks.Server" instance as first argument`);
|
|
15
82
|
}
|
|
16
83
|
|
|
17
|
-
if (roles === null) {
|
|
18
|
-
throw new Error('Invalid client types');
|
|
19
|
-
}
|
|
20
|
-
|
|
21
84
|
/**
|
|
22
85
|
* soundworks server
|
|
23
86
|
* @type {server.Server}
|
|
87
|
+
* @readonly
|
|
24
88
|
*/
|
|
25
89
|
this.server = server;
|
|
26
90
|
|
|
27
91
|
/**
|
|
28
92
|
* List of clients that are currently in this context
|
|
29
|
-
* @type {Client[]}
|
|
93
|
+
* @type {server.Client[]}
|
|
30
94
|
*/
|
|
31
95
|
this.clients = new Set();
|
|
32
96
|
|
|
33
97
|
/**
|
|
34
98
|
* Status of the context ('idle', 'inited', 'started' or 'errored')
|
|
35
|
-
* @type {
|
|
99
|
+
* @type {string}
|
|
100
|
+
* @readonly
|
|
36
101
|
*/
|
|
37
102
|
this.status = 'idle';
|
|
38
103
|
|
|
39
104
|
/**
|
|
40
|
-
* List of client
|
|
105
|
+
* List of client roles that can use this context.
|
|
106
|
+
* @type {string[]}
|
|
107
|
+
* @readonly
|
|
41
108
|
*/
|
|
42
109
|
roles = Array.isArray(roles) ? roles : [roles];
|
|
110
|
+
|
|
111
|
+
if (roles.length === 0) {
|
|
112
|
+
roles = Object.keys(server.config.app.clients);
|
|
113
|
+
}
|
|
114
|
+
|
|
43
115
|
this.roles = new Set(roles);
|
|
44
116
|
|
|
45
117
|
// register in context manager
|
|
@@ -47,32 +119,87 @@ class Context {
|
|
|
47
119
|
}
|
|
48
120
|
|
|
49
121
|
/**
|
|
50
|
-
*
|
|
51
|
-
*
|
|
122
|
+
* Optionnal user-defined name of the context (defaults to the class name).
|
|
123
|
+
*
|
|
124
|
+
* 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
|
|
126
|
+
* user-defined context with the same name, it will use a default (noop) context.
|
|
127
|
+
*
|
|
128
|
+
* @readonly
|
|
129
|
+
* @type {string}
|
|
130
|
+
* @example
|
|
131
|
+
* // server-side and client-side contexts are matched based on their respective `name`
|
|
132
|
+
* class MyContext extends Context {
|
|
133
|
+
* get name() {
|
|
134
|
+
* return 'my-user-defined-context-name';
|
|
135
|
+
* }
|
|
136
|
+
* }
|
|
52
137
|
*/
|
|
53
138
|
get name() {
|
|
54
139
|
return this.constructor.name;
|
|
55
140
|
}
|
|
56
141
|
|
|
57
142
|
/**
|
|
58
|
-
*
|
|
59
|
-
* the
|
|
143
|
+
* 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
|
|
145
|
+
* some heavy and/or potentially long job has to be done when starting the context
|
|
146
|
+
* (e.g. connect to a database, parsing a long file) it may be a good practice
|
|
147
|
+
* to call it explicitely.
|
|
60
148
|
*
|
|
61
|
-
*
|
|
149
|
+
* This method should be implemented to perform operations that are valid for the
|
|
150
|
+
* whole lifetime of the context, regardless a client enters or exits the context.
|
|
151
|
+
*
|
|
152
|
+
* @example
|
|
153
|
+
* import { Context } from '@soundworks/core/server.js';
|
|
154
|
+
*
|
|
155
|
+
* class MyContext extends Context {
|
|
156
|
+
* async start() {
|
|
157
|
+
* await super.start();
|
|
158
|
+
* await this.doSomeLongJob();
|
|
159
|
+
* }
|
|
160
|
+
* }
|
|
161
|
+
*
|
|
162
|
+
* // Instantiate the context
|
|
163
|
+
* const myContext = new Context(server, ['test']);
|
|
164
|
+
* // manually start the context to perform the long operation before the first
|
|
165
|
+
* // client enters the context
|
|
166
|
+
* await myContext.start();
|
|
167
|
+
* ```
|
|
62
168
|
*/
|
|
63
169
|
async start() {}
|
|
64
170
|
|
|
65
171
|
/**
|
|
66
|
-
*
|
|
172
|
+
* Stop the context. The method that is automatically called when the server
|
|
173
|
+
* stops. It should be used to cleanup context wise operations made in `start()`
|
|
174
|
+
* (e.g. disconnect from a database, release a file handle).
|
|
67
175
|
*
|
|
68
176
|
* _WARNING: this method should never be called manually._
|
|
69
177
|
*/
|
|
70
178
|
async stop() {}
|
|
71
179
|
|
|
72
180
|
/**
|
|
73
|
-
*
|
|
181
|
+
* Enter the context. Implement this method to define the logic that should be
|
|
182
|
+
* done (e.g. creating a shared state, etc.) when a client enters the context.
|
|
183
|
+
*
|
|
184
|
+
* If the context has not been started yet, the `start` method is implicitely executed.
|
|
74
185
|
*
|
|
75
186
|
* _WARNING: this method should never be called manually._
|
|
187
|
+
*
|
|
188
|
+
* @param {server.Client} client - Server-side representation of the client
|
|
189
|
+
* that enters the context.
|
|
190
|
+
* @returns {Promise} - Promise that resolves when the context is entered.
|
|
191
|
+
* @example
|
|
192
|
+
* class MyContext extends Context {
|
|
193
|
+
* async enter(client) {
|
|
194
|
+
* await super.enter(client);
|
|
195
|
+
* registerTheClientSomewhere(client);
|
|
196
|
+
* }
|
|
197
|
+
*
|
|
198
|
+
* async exit(client) {
|
|
199
|
+
* await super.exit(client);
|
|
200
|
+
* unregisterTheClientSomewhere(client);
|
|
201
|
+
* }
|
|
202
|
+
* }
|
|
76
203
|
*/
|
|
77
204
|
async enter(client) {
|
|
78
205
|
if (!(client instanceof Client)) {
|
|
@@ -83,9 +210,26 @@ class Context {
|
|
|
83
210
|
}
|
|
84
211
|
|
|
85
212
|
/**
|
|
86
|
-
*
|
|
213
|
+
* Exit the context. Implement this method to define the logic that should be
|
|
214
|
+
* done (e.g. delete a shared state, etc.) when a client exits the context.
|
|
87
215
|
*
|
|
88
|
-
* _WARNING: this method should never be called manually._
|
|
216
|
+
* * _WARNING: this method should never be called manually._
|
|
217
|
+
*
|
|
218
|
+
* @param {server.Client} client - Server-side representation of the client
|
|
219
|
+
* that exits the context.
|
|
220
|
+
* @returns {Promise} - Promise that resolves when the context is exited.
|
|
221
|
+
* @example
|
|
222
|
+
* class MyContext extends Context {
|
|
223
|
+
* async enter(client) {
|
|
224
|
+
* await super.enter(client);
|
|
225
|
+
* this.state = await this.client.stateManager.create('my-context-state');
|
|
226
|
+
* }
|
|
227
|
+
*
|
|
228
|
+
* async exit(client) {
|
|
229
|
+
* await super.exit(client);
|
|
230
|
+
* await this.state.delete();
|
|
231
|
+
* }
|
|
232
|
+
* }
|
|
89
233
|
*/
|
|
90
234
|
async exit(client) {
|
|
91
235
|
if (!(client instanceof Client)) {
|
|
@@ -57,6 +57,7 @@ class ContextCollection {
|
|
|
57
57
|
* _WARNING: Most of the time, you should not have to manipulate the context manager directly._
|
|
58
58
|
*
|
|
59
59
|
* @memberof server
|
|
60
|
+
* @hideconstructor
|
|
60
61
|
*/
|
|
61
62
|
class ContextManager {
|
|
62
63
|
/**
|
|
@@ -91,10 +92,9 @@ class ContextManager {
|
|
|
91
92
|
/**
|
|
92
93
|
* Retrieve a started context from its name.
|
|
93
94
|
*
|
|
94
|
-
*
|
|
95
|
-
* @param {String} contextName - Name of the context.
|
|
95
|
+
* _WARNING: Most of the time, you should not have to call this method manually._
|
|
96
96
|
*
|
|
97
|
-
* @
|
|
97
|
+
* @param {server.Context#name} contextName - Name of the context.
|
|
98
98
|
*/
|
|
99
99
|
async get(contextName) {
|
|
100
100
|
if (!this._contexts.has(contextName)) {
|
|
@@ -136,9 +136,8 @@ class ContextManager {
|
|
|
136
136
|
if (!this._contexts.has(contextName)) {
|
|
137
137
|
// create default context for all client types
|
|
138
138
|
const ctor = createNamedContextClass(contextName);
|
|
139
|
-
const roles = Object.keys(this.server.config.app.clients);
|
|
140
139
|
// this will automatically register the context in the context manager
|
|
141
|
-
new ctor(this.server
|
|
140
|
+
new ctor(this.server);
|
|
142
141
|
}
|
|
143
142
|
|
|
144
143
|
// we ensure context is started, even lazilly after server.start()
|
package/src/server/Plugin.js
CHANGED
|
@@ -14,7 +14,8 @@ import BasePlugin from '../common/BasePlugin.js';
|
|
|
14
14
|
*/
|
|
15
15
|
|
|
16
16
|
/**
|
|
17
|
-
* Base class to extend in order to create
|
|
17
|
+
* Base class to extend in order to create the server-side counterpart of a
|
|
18
|
+
* `soundworks` plugin.
|
|
18
19
|
*
|
|
19
20
|
* In the `soundworks` paradigm, a plugin is a component that allows to extend
|
|
20
21
|
* the framework capabilities by encapsulating common and reusable logic in
|
|
@@ -23,7 +23,7 @@ import Server from './Server.js';
|
|
|
23
23
|
*
|
|
24
24
|
* Plugins should always be registered both client-side and server-side,
|
|
25
25
|
* and before {@link client.Client#start} or {@link server.Server#start}
|
|
26
|
-
*
|
|
26
|
+
* to be properly initialized.
|
|
27
27
|
*
|
|
28
28
|
* In some sitautions, you might want to register the same plugin factory several times
|
|
29
29
|
* using different ids (e.g. for watching several parts of the file system, etc.).
|
|
@@ -33,12 +33,32 @@ import Server from './Server.js';
|
|
|
33
33
|
* for more informations on the available plugins.
|
|
34
34
|
*
|
|
35
35
|
* ```
|
|
36
|
+
* // client-side
|
|
37
|
+
* import { Client } from '@soundworks/core/client.js';
|
|
38
|
+
* import syncPlugin from '@soundworks/plugin-sync/client.js';
|
|
39
|
+
*
|
|
40
|
+
* const client = new Client(config);
|
|
41
|
+
* // register the plugin before `client.start()`
|
|
42
|
+
* client.pluginManager.register('sync', syncPlugin);
|
|
43
|
+
*
|
|
44
|
+
* await client.start();
|
|
45
|
+
*
|
|
46
|
+
* const sync = await client.pluginManager.get('sync');
|
|
47
|
+
*
|
|
48
|
+
* setInterval(() => {
|
|
49
|
+
* // log the estimated global synced clock alongside the local clock.
|
|
50
|
+
* console.log(sync.getSyncTime(), sync.getLocalTime());
|
|
51
|
+
* }, 1000);
|
|
52
|
+
* ```
|
|
53
|
+
*
|
|
54
|
+
* ```
|
|
55
|
+
* // server-side
|
|
36
56
|
* import { Server } from '@soundworks/core/server.js';
|
|
37
|
-
* import
|
|
57
|
+
* import syncPlugin from '@soundworks/plugin-sync/server.js';
|
|
38
58
|
*
|
|
39
59
|
* const server = new Server(config);
|
|
40
60
|
* // register the plugin before `server.start()`
|
|
41
|
-
* server.pluginManager.register('sync',
|
|
61
|
+
* server.pluginManager.register('sync', syncPlugin);
|
|
42
62
|
*
|
|
43
63
|
* await server.start();
|
|
44
64
|
*
|
|
@@ -86,8 +106,8 @@ class PluginManager extends BasePluginManager {
|
|
|
86
106
|
* initialization (e.g. to display initialization screen, etc.), you should rely
|
|
87
107
|
* on the `onStateChange` method.
|
|
88
108
|
*
|
|
89
|
-
* _Note: the async API is designed to enable the dynamic creation of plugins
|
|
90
|
-
* without brealing changes) in a future release._
|
|
109
|
+
* _Note: the async API is designed to enable the dynamic creation of plugins
|
|
110
|
+
* (hopefully without brealing changes) in a future release._
|
|
91
111
|
*
|
|
92
112
|
* @param {server.Plugin#id} id - Id of the plugin as defined when registered.
|
|
93
113
|
* @returns {server.Plugin}
|
|
@@ -100,7 +120,7 @@ class PluginManager extends BasePluginManager {
|
|
|
100
120
|
throw new Error(`[soundworks.PluginManager] Cannot get plugin before "server.init()"`);
|
|
101
121
|
}
|
|
102
122
|
|
|
103
|
-
return super.
|
|
123
|
+
return super.unsafeGet(id);
|
|
104
124
|
}
|
|
105
125
|
|
|
106
126
|
// server only methods
|