@soundworks/core 3.5.1 → 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.
@@ -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("./utils/logger.js"));
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(value => {
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(value => _logger.default.pluginErrored(this.name));
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
- console.log(`> plugin "${this.name}" error: ${msg}`);
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
- * @see {@link client.Client#init}
33
- * @type {String}
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
- * @type {String}
45
+ * @type {String}
46
46
  */
47
47
 
48
48
  this.uuid = (0, _uuid.v4)();
@@ -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("./utils/logger.js"));
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._registeredPlugins = {};
31
+ this._server = server;
32
32
  /** @private */
33
33
 
34
- this._instances = {};
34
+ this._registeredPlugins = new Map();
35
35
  /** @private */
36
36
 
37
- this._server = server; // @todo - move to Promise based logic
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((resolve, reject) => {
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
- }); // start before ready, even if no deps
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[name]) {
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
- this._registeredPlugins[name] = {
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[name]) {
109
- throw new Error(`[soundworks:core] Cannot get or require plugin "${name}", plugin is not registered
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
- ${Object.keys(this._registeredPlugins).map(n => `> - ${n}\n`).join('')}
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] Plugin "${name}" required after pluginManager start`);
171
+ throw new Error(`[soundworks:core] Cannot require plugin ("${name}") after server.start()`);
118
172
  }
119
173
 
120
- if (!this._instances[name]) {
121
- log(`> instanciating plugin "${name}"`);
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[name]; // if require by the experience, map client types
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 in this._instances) {
257
+ for (let name of this._instances.keys()) {
162
258
  if (clientType !== null) {
163
- if (this._instances[name].clientTypes.has(clientType)) {
259
+ if (this._instances.get(name).clientTypes.has(clientType)) {
164
260
  plugins.push(name);
165
261
  }
166
262
  } else {