@soundworks/core 3.6.0 → 3.7.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/client/AbstractPlugin.js +1 -1
- package/client/Client.js +16 -13
- package/client/PluginManager.js +39 -27
- package/client/Socket.js +17 -9
- package/common/ParameterBag.js +11 -13
- package/common/SharedState.js +48 -19
- package/common/SharedStateManagerServer.js +2 -2
- package/common/SharedStatePrivate.js +0 -2
- package/{server/utils → common}/logger.js +34 -6
- package/common/shared-state-utils.js +0 -1
- package/package.json +14 -3
- package/server/AbstractExperience.js +1 -1
- package/server/AbstractPlugin.js +7 -4
- package/server/Client.js +4 -4
- package/server/PluginManager.js +136 -40
- package/server/Server.js +251 -212
- package/server/Socket.js +9 -10
- package/server/Sockets.js +0 -1
package/server/AbstractPlugin.js
CHANGED
|
@@ -13,7 +13,7 @@ var _Signal = _interopRequireDefault(require("../common/Signal.js"));
|
|
|
13
13
|
|
|
14
14
|
var _SignalAll = _interopRequireDefault(require("../common/SignalAll.js"));
|
|
15
15
|
|
|
16
|
-
var _logger = _interopRequireDefault(require("
|
|
16
|
+
var _logger = _interopRequireDefault(require("../common/logger.js"));
|
|
17
17
|
|
|
18
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
19
|
|
|
@@ -63,13 +63,13 @@ class AbstractPlugin {
|
|
|
63
63
|
|
|
64
64
|
this.server.activities.add(this); // start when all required signals are fired
|
|
65
65
|
|
|
66
|
-
this.signals.start.addObserver(
|
|
66
|
+
this.signals.start.addObserver(() => {
|
|
67
67
|
_logger.default.pluginStart(this.name);
|
|
68
68
|
|
|
69
69
|
this.start();
|
|
70
70
|
}); // log errors if any
|
|
71
71
|
|
|
72
|
-
this.signals.errored.addObserver(
|
|
72
|
+
this.signals.errored.addObserver(() => _logger.default.pluginErrored(this.name));
|
|
73
73
|
this.ready = this.ready.bind(this);
|
|
74
74
|
}
|
|
75
75
|
/**
|
|
@@ -178,7 +178,10 @@ class AbstractPlugin {
|
|
|
178
178
|
}
|
|
179
179
|
|
|
180
180
|
error(msg) {
|
|
181
|
-
|
|
181
|
+
_logger.default.pluginError(this.name);
|
|
182
|
+
|
|
183
|
+
_logger.default.error(`> Error: ${msg}`);
|
|
184
|
+
|
|
182
185
|
this.signals.errored.msg = msg;
|
|
183
186
|
this.signals.errored.value = true;
|
|
184
187
|
}
|
package/server/Client.js
CHANGED
|
@@ -28,9 +28,9 @@ class Client {
|
|
|
28
28
|
constructor(clientType, socket) {
|
|
29
29
|
/**
|
|
30
30
|
* Client type, as specified when initializing the client side {@link client.Client}.
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
31
|
+
*
|
|
32
|
+
* @see {@link client.Client#init}
|
|
33
|
+
* @type {String}
|
|
34
34
|
*/
|
|
35
35
|
this.type = clientType;
|
|
36
36
|
/**
|
|
@@ -42,7 +42,7 @@ class Client {
|
|
|
42
42
|
this.id = generateId.next().value;
|
|
43
43
|
/**
|
|
44
44
|
* Unique session id (uuidv4).
|
|
45
|
-
|
|
45
|
+
* @type {String}
|
|
46
46
|
*/
|
|
47
47
|
|
|
48
48
|
this.uuid = (0, _uuid.v4)();
|
package/server/PluginManager.js
CHANGED
|
@@ -13,7 +13,7 @@ var _Signal = _interopRequireDefault(require("../common/Signal.js"));
|
|
|
13
13
|
|
|
14
14
|
var _SignalAll = _interopRequireDefault(require("../common/SignalAll.js"));
|
|
15
15
|
|
|
16
|
-
var _logger = _interopRequireDefault(require("
|
|
16
|
+
var _logger = _interopRequireDefault(require("../common/logger.js"));
|
|
17
17
|
|
|
18
18
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
19
19
|
|
|
@@ -28,19 +28,25 @@ const log = (0, _debug.default)('soundworks:lifecycle');
|
|
|
28
28
|
class PluginManager {
|
|
29
29
|
constructor(server) {
|
|
30
30
|
/** @private */
|
|
31
|
-
this.
|
|
31
|
+
this._server = server;
|
|
32
32
|
/** @private */
|
|
33
33
|
|
|
34
|
-
this.
|
|
34
|
+
this._registeredPlugins = new Map();
|
|
35
35
|
/** @private */
|
|
36
36
|
|
|
37
|
-
this.
|
|
37
|
+
this._instances = new Map();
|
|
38
|
+
/** @private */
|
|
39
|
+
|
|
40
|
+
this._observers = new Set();
|
|
41
|
+
/** @private */
|
|
42
|
+
|
|
43
|
+
this._pluginsStatuses = {}; // @todo - move to Promise based logic
|
|
38
44
|
|
|
39
45
|
this.signals = {
|
|
40
46
|
start: new _Signal.default(),
|
|
41
47
|
ready: new _SignalAll.default()
|
|
42
48
|
};
|
|
43
|
-
this.ready = new Promise(
|
|
49
|
+
this.ready = new Promise(resolve => {
|
|
44
50
|
this._resolveReadyPromise = resolve;
|
|
45
51
|
});
|
|
46
52
|
}
|
|
@@ -56,7 +62,10 @@ class PluginManager {
|
|
|
56
62
|
log('> pluginManager ready');
|
|
57
63
|
|
|
58
64
|
this._resolveReadyPromise();
|
|
59
|
-
}); //
|
|
65
|
+
}); // propagate all 'idle' statuses before start
|
|
66
|
+
|
|
67
|
+
this._propagateStatusChange(); // start before ready, even if no deps
|
|
68
|
+
|
|
60
69
|
|
|
61
70
|
this.signals.start.value = true;
|
|
62
71
|
|
|
@@ -66,6 +75,40 @@ class PluginManager {
|
|
|
66
75
|
|
|
67
76
|
return this.ready;
|
|
68
77
|
}
|
|
78
|
+
/** @private */
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
_propagateStatusChange(name = null, status = null) {
|
|
82
|
+
if (name != null && status != null) {
|
|
83
|
+
this._pluginsStatuses[name] = status;
|
|
84
|
+
const statuses = this.getStatuses();
|
|
85
|
+
|
|
86
|
+
this._observers.forEach(observer => observer(statuses, {
|
|
87
|
+
[name]: status
|
|
88
|
+
}));
|
|
89
|
+
} else {
|
|
90
|
+
const statuses = this.getStatuses();
|
|
91
|
+
|
|
92
|
+
this._observers.forEach(observer => observer(statuses, {}));
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
/** @private */
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
observe(observer) {
|
|
99
|
+
this._observers.add(observer);
|
|
100
|
+
|
|
101
|
+
return () => {
|
|
102
|
+
this._observers.delete(observer);
|
|
103
|
+
};
|
|
104
|
+
}
|
|
105
|
+
/** @private */
|
|
106
|
+
// @todo rename to `getStatuses`
|
|
107
|
+
|
|
108
|
+
|
|
109
|
+
getStatuses() {
|
|
110
|
+
return Object.assign({}, this._pluginsStatuses);
|
|
111
|
+
}
|
|
69
112
|
/**
|
|
70
113
|
* Register a plugin (server-side) into soundworks.
|
|
71
114
|
*
|
|
@@ -85,64 +128,55 @@ class PluginManager {
|
|
|
85
128
|
|
|
86
129
|
|
|
87
130
|
register(name, factory = null, options = {}, deps = []) {
|
|
88
|
-
if (this._registeredPlugins
|
|
131
|
+
if (this._registeredPlugins.has(name)) {
|
|
89
132
|
const ctor = factory(_AbstractPlugin.default);
|
|
90
133
|
throw new Error(`[soundworks:core] Plugin "${name}" of type "${ctor.name}" already registered`);
|
|
91
134
|
}
|
|
92
135
|
|
|
93
136
|
const ctor = factory(_AbstractPlugin.default);
|
|
94
|
-
|
|
137
|
+
|
|
138
|
+
this._registeredPlugins.set(name, {
|
|
95
139
|
ctor,
|
|
96
140
|
options,
|
|
97
141
|
deps
|
|
98
|
-
};
|
|
142
|
+
});
|
|
99
143
|
}
|
|
100
144
|
/**
|
|
101
145
|
* Retrieve an instance of a registered plugin according to its given name.
|
|
102
146
|
* Except if you know what you are doing, prefer `Experience.require('my-plugin')`
|
|
147
|
+
*
|
|
103
148
|
* @param {String} name - Name of the registered plugin
|
|
104
149
|
*/
|
|
150
|
+
// @todo - review this API, this is ugly and error prone
|
|
105
151
|
|
|
106
152
|
|
|
107
153
|
get(name, _experience = null) {
|
|
108
|
-
if (!this._registeredPlugins
|
|
109
|
-
|
|
154
|
+
if (!this._registeredPlugins.has(name)) {
|
|
155
|
+
let msg = `[soundworks:core] Cannot get or require plugin "${name}", plugin is not registered`;
|
|
156
|
+
|
|
157
|
+
if (this._registeredPlugins.size > 0) {
|
|
158
|
+
msg += `
|
|
110
159
|
> registered plugins are:
|
|
111
|
-
${
|
|
112
|
-
|
|
160
|
+
${Array.from(this._registeredPlugins.keys()).map(name => `> - ${name}\n`).join('')}`;
|
|
161
|
+
} else {
|
|
162
|
+
msg += `
|
|
163
|
+
> no plugin registered`;
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
throw new Error(msg);
|
|
113
167
|
} // required by experience and manager already started
|
|
114
168
|
|
|
115
169
|
|
|
116
170
|
if (_experience && this.signals.start.value === true) {
|
|
117
|
-
throw new Error(`[soundworks:core]
|
|
171
|
+
throw new Error(`[soundworks:core] Cannot require plugin ("${name}") after server.start()`);
|
|
118
172
|
}
|
|
119
173
|
|
|
120
|
-
if (!this._instances
|
|
121
|
-
|
|
122
|
-
const {
|
|
123
|
-
ctor,
|
|
124
|
-
options,
|
|
125
|
-
deps
|
|
126
|
-
} = this._registeredPlugins[name];
|
|
127
|
-
const instance = new ctor(this._server, name, options);
|
|
128
|
-
this.signals.ready.add(instance.signals.ready);
|
|
129
|
-
instance.signals.start.add(this.signals.start);
|
|
130
|
-
|
|
131
|
-
if (deps.length > 0) {
|
|
132
|
-
deps.forEach(dependencyName => {
|
|
133
|
-
if (!this._instances[dependencyName]) {
|
|
134
|
-
this.get(dependencyName, _experience); // propagate client types
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
const dependency = this._instances[dependencyName];
|
|
138
|
-
instance.signals.start.add(dependency.signals.ready);
|
|
139
|
-
});
|
|
140
|
-
}
|
|
141
|
-
|
|
142
|
-
this._instances[name] = instance;
|
|
174
|
+
if (!this._instances.has(name)) {
|
|
175
|
+
this._createInstance(name);
|
|
143
176
|
}
|
|
144
177
|
|
|
145
|
-
const instance = this._instances
|
|
178
|
+
const instance = this._instances.get(name); // if require by the experience, map client types
|
|
179
|
+
|
|
146
180
|
|
|
147
181
|
if (_experience) {
|
|
148
182
|
_experience.clientTypes.forEach(clientType => {
|
|
@@ -155,12 +189,74 @@ ${Object.keys(this._registeredPlugins).map(n => `> - ${n}\n`).join('')}
|
|
|
155
189
|
/** @private */
|
|
156
190
|
|
|
157
191
|
|
|
192
|
+
_createInstance(name) {
|
|
193
|
+
log(`> instantiating plugin "${name}"`);
|
|
194
|
+
|
|
195
|
+
const {
|
|
196
|
+
ctor,
|
|
197
|
+
options,
|
|
198
|
+
deps
|
|
199
|
+
} = this._registeredPlugins.get(name);
|
|
200
|
+
|
|
201
|
+
const instance = new ctor(this._server, name, options);
|
|
202
|
+
this.signals.ready.add(instance.signals.ready);
|
|
203
|
+
instance.signals.start.add(this.signals.start);
|
|
204
|
+
|
|
205
|
+
if (deps.length > 0) {
|
|
206
|
+
deps.forEach(dependencyName => {
|
|
207
|
+
if (!this._instances.has(dependencyName)) {
|
|
208
|
+
this.get(dependencyName, _experience); // propagate client types
|
|
209
|
+
}
|
|
210
|
+
|
|
211
|
+
const dependency = this._instances.get(dependencyName);
|
|
212
|
+
|
|
213
|
+
instance.signals.start.add(dependency.signals.ready);
|
|
214
|
+
});
|
|
215
|
+
} // handle plugin status reporting
|
|
216
|
+
|
|
217
|
+
|
|
218
|
+
this._pluginsStatuses[name] = 'idle';
|
|
219
|
+
|
|
220
|
+
const onPluginStarted = () => {
|
|
221
|
+
this._propagateStatusChange(name, 'started');
|
|
222
|
+
};
|
|
223
|
+
|
|
224
|
+
const onPluginErrored = () => {
|
|
225
|
+
this._propagateStatusChange(name, 'errored');
|
|
226
|
+
|
|
227
|
+
instance.signals.started.removeObserver(onPluginStarted);
|
|
228
|
+
instance.signals.ready.removeObserver(onPluginReady);
|
|
229
|
+
instance.signals.errored.removeObserver(onPluginErrored);
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const onPluginReady = () => {
|
|
233
|
+
this._propagateStatusChange(name, 'ready');
|
|
234
|
+
|
|
235
|
+
instance.signals.started.removeObserver(onPluginStarted);
|
|
236
|
+
instance.signals.ready.removeObserver(onPluginReady);
|
|
237
|
+
instance.signals.errored.removeObserver(onPluginErrored);
|
|
238
|
+
}; // listen the signals
|
|
239
|
+
|
|
240
|
+
|
|
241
|
+
instance.signals.started.addObserver(onPluginStarted);
|
|
242
|
+
instance.signals.ready.addObserver(onPluginReady);
|
|
243
|
+
instance.signals.errored.addObserver(onPluginErrored);
|
|
244
|
+
|
|
245
|
+
this._instances.set(name, instance);
|
|
246
|
+
}
|
|
247
|
+
/**
|
|
248
|
+
* @private
|
|
249
|
+
* This method is protected (semi-private), only used by server to check
|
|
250
|
+
* consistency between client and server experiences requirements.
|
|
251
|
+
*/
|
|
252
|
+
|
|
253
|
+
|
|
158
254
|
getRequiredPlugins(clientType = null) {
|
|
159
255
|
const plugins = [];
|
|
160
256
|
|
|
161
|
-
for (let name
|
|
257
|
+
for (let name of this._instances.keys()) {
|
|
162
258
|
if (clientType !== null) {
|
|
163
|
-
if (this._instances
|
|
259
|
+
if (this._instances.get(name).clientTypes.has(clientType)) {
|
|
164
260
|
plugins.push(name);
|
|
165
261
|
}
|
|
166
262
|
} else {
|