@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/client/AbstractPlugin.js
CHANGED
|
@@ -93,7 +93,7 @@ class AbstractPlugin {
|
|
|
93
93
|
|
|
94
94
|
}; // start when all required signals are fired
|
|
95
95
|
|
|
96
|
-
this.signals.start.addObserver(
|
|
96
|
+
this.signals.start.addObserver(() => this.start());
|
|
97
97
|
this.started = this.started.bind(this);
|
|
98
98
|
this.ready = this.ready.bind(this);
|
|
99
99
|
this.error = this.error.bind(this);
|
package/client/Client.js
CHANGED
|
@@ -137,14 +137,9 @@ class Client {
|
|
|
137
137
|
|
|
138
138
|
async start() {
|
|
139
139
|
this._ready = new Promise((resolve, reject) => {
|
|
140
|
-
const payload = {
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
Object.assign(payload, {
|
|
144
|
-
requiredPlugins: Object.keys(this.pluginManager.getValues())
|
|
145
|
-
});
|
|
146
|
-
} // wait for handshake response to mark client as `ready`
|
|
147
|
-
|
|
140
|
+
const payload = {
|
|
141
|
+
requiredPlugins: this.pluginManager.getRequiredPlugins()
|
|
142
|
+
}; // wait for handshake response to mark client as `ready`
|
|
148
143
|
|
|
149
144
|
this.socket.addListener('s:client:start', ({
|
|
150
145
|
id,
|
|
@@ -164,15 +159,24 @@ class Client {
|
|
|
164
159
|
this.pluginManager.start().then(() => resolve());
|
|
165
160
|
});
|
|
166
161
|
this.socket.addListener('s:client:error', err => {
|
|
162
|
+
let msg = ``;
|
|
163
|
+
|
|
167
164
|
switch (err.type) {
|
|
168
165
|
case 'plugins':
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
166
|
+
msg = `"${err.data.join(', ')}" required client-side but not server-side`;
|
|
167
|
+
break;
|
|
168
|
+
|
|
169
|
+
case 'no-activity':
|
|
170
|
+
msg = `[soundworks:core] No activity registered server-side for "${this.type}", make sure an Experience has been created for this client`;
|
|
171
|
+
break;
|
|
172
|
+
|
|
173
|
+
default:
|
|
174
|
+
msg = `Unknown error`;
|
|
172
175
|
break;
|
|
173
176
|
}
|
|
174
177
|
|
|
175
|
-
|
|
178
|
+
this.socket.terminate();
|
|
179
|
+
reject(msg);
|
|
176
180
|
});
|
|
177
181
|
this.socket.send('s:client:handshake', payload);
|
|
178
182
|
});
|
|
@@ -185,6 +189,5 @@ class Client {
|
|
|
185
189
|
|
|
186
190
|
}
|
|
187
191
|
|
|
188
|
-
;
|
|
189
192
|
var _default = Client;
|
|
190
193
|
exports.default = _default;
|
package/client/PluginManager.js
CHANGED
|
@@ -45,7 +45,7 @@ class PluginManager {
|
|
|
45
45
|
this._observers = new Set();
|
|
46
46
|
/** @private */
|
|
47
47
|
|
|
48
|
-
this.
|
|
48
|
+
this._pluginsStatuses = {};
|
|
49
49
|
/** @private */
|
|
50
50
|
|
|
51
51
|
this._client = client;
|
|
@@ -54,7 +54,7 @@ class PluginManager {
|
|
|
54
54
|
start: new _Signal.default(),
|
|
55
55
|
ready: new _SignalAll.default()
|
|
56
56
|
};
|
|
57
|
-
this.ready = new Promise(
|
|
57
|
+
this.ready = new Promise(resolve => {
|
|
58
58
|
this._resolveReadyPromise = resolve;
|
|
59
59
|
});
|
|
60
60
|
}
|
|
@@ -70,7 +70,10 @@ class PluginManager {
|
|
|
70
70
|
log('> pluginManager ready');
|
|
71
71
|
|
|
72
72
|
this._resolveReadyPromise();
|
|
73
|
-
});
|
|
73
|
+
});
|
|
74
|
+
|
|
75
|
+
this._propagateStatusChange(); // start before ready, even if no deps
|
|
76
|
+
|
|
74
77
|
|
|
75
78
|
this.signals.start.value = true;
|
|
76
79
|
|
|
@@ -79,17 +82,23 @@ class PluginManager {
|
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
return this.ready;
|
|
82
|
-
}
|
|
83
|
-
// @unstable
|
|
84
|
-
// mimic state manager API, so we can change this later...
|
|
85
|
-
|
|
85
|
+
}
|
|
86
86
|
/** @private */
|
|
87
87
|
|
|
88
88
|
|
|
89
|
-
|
|
90
|
-
|
|
89
|
+
_propagateStatusChange(name = null, status = null) {
|
|
90
|
+
if (name != null && status != null) {
|
|
91
|
+
this._pluginsStatuses[name] = status;
|
|
92
|
+
const statuses = this.getStatuses();
|
|
93
|
+
|
|
94
|
+
this._observers.forEach(observer => observer(statuses, {
|
|
95
|
+
[name]: status
|
|
96
|
+
}));
|
|
97
|
+
} else {
|
|
98
|
+
const statuses = this.getStatuses();
|
|
91
99
|
|
|
92
|
-
|
|
100
|
+
this._observers.forEach(observer => observer(statuses, {}));
|
|
101
|
+
}
|
|
93
102
|
}
|
|
94
103
|
/** @private */
|
|
95
104
|
|
|
@@ -102,15 +111,12 @@ class PluginManager {
|
|
|
102
111
|
};
|
|
103
112
|
}
|
|
104
113
|
/** @private */
|
|
114
|
+
// @todo rename to `getStatuses`
|
|
105
115
|
|
|
106
116
|
|
|
107
|
-
|
|
108
|
-
return Object.assign({}, this.
|
|
109
|
-
}
|
|
110
|
-
// ------------------
|
|
111
|
-
|
|
112
|
-
/** @private */
|
|
113
|
-
|
|
117
|
+
getStatuses() {
|
|
118
|
+
return Object.assign({}, this._pluginsStatuses);
|
|
119
|
+
}
|
|
114
120
|
/**
|
|
115
121
|
* Register the client-side part of a plugin into soundworks instance.
|
|
116
122
|
*
|
|
@@ -200,30 +206,33 @@ ${Object.keys(this._registeredPlugins).map(n => `> - ${n}\n`).join('')}
|
|
|
200
206
|
} // handle plugin status for reporting
|
|
201
207
|
|
|
202
208
|
|
|
203
|
-
this.
|
|
209
|
+
this._pluginsStatuses[name] = 'idle';
|
|
204
210
|
let unsubscribe;
|
|
205
211
|
|
|
206
212
|
const onPluginStarted = () => {
|
|
207
|
-
this.
|
|
213
|
+
this._pluginsStatuses[name] = 'started';
|
|
208
214
|
/**
|
|
209
|
-
* @
|
|
215
|
+
* @FIXME
|
|
216
|
+
* relying on `instance.state` is very weak...
|
|
210
217
|
* we should find a better solution, to declare that we want the
|
|
211
218
|
* pluginManager to watch and propagate initialization steps.
|
|
219
|
+
*
|
|
220
|
+
* This is probably a dirty hack to make the init views working...
|
|
212
221
|
*/
|
|
213
222
|
|
|
214
223
|
if (instance.state) {
|
|
215
224
|
unsubscribe = instance.state.subscribe(() => {
|
|
216
|
-
this.
|
|
225
|
+
this._propagateStatusChange();
|
|
217
226
|
});
|
|
218
227
|
}
|
|
219
228
|
|
|
220
|
-
this.
|
|
229
|
+
this._propagateStatusChange();
|
|
221
230
|
};
|
|
222
231
|
|
|
223
232
|
const onPluginErrored = () => {
|
|
224
|
-
this.
|
|
233
|
+
this._pluginsStatuses[name] = 'errored';
|
|
225
234
|
|
|
226
|
-
this.
|
|
235
|
+
this._propagateStatusChange();
|
|
227
236
|
|
|
228
237
|
if (unsubscribe) {
|
|
229
238
|
unsubscribe();
|
|
@@ -235,9 +244,9 @@ ${Object.keys(this._registeredPlugins).map(n => `> - ${n}\n`).join('')}
|
|
|
235
244
|
};
|
|
236
245
|
|
|
237
246
|
const onPluginReady = () => {
|
|
238
|
-
this.
|
|
247
|
+
this._pluginsStatuses[name] = 'ready';
|
|
239
248
|
|
|
240
|
-
this.
|
|
249
|
+
this._propagateStatusChange();
|
|
241
250
|
|
|
242
251
|
if (unsubscribe) {
|
|
243
252
|
unsubscribe();
|
|
@@ -260,8 +269,11 @@ ${Object.keys(this._registeredPlugins).map(n => `> - ${n}\n`).join('')}
|
|
|
260
269
|
return instance;
|
|
261
270
|
}
|
|
262
271
|
|
|
272
|
+
getRequiredPlugins() {
|
|
273
|
+
return Object.keys(this._pluginsStatuses);
|
|
274
|
+
}
|
|
275
|
+
|
|
263
276
|
}
|
|
264
277
|
|
|
265
|
-
;
|
|
266
278
|
var _default = PluginManager;
|
|
267
279
|
exports.default = _default;
|
package/client/Socket.js
CHANGED
|
@@ -11,11 +11,13 @@ var _isomorphicWs = _interopRequireDefault(require("isomorphic-ws"));
|
|
|
11
11
|
|
|
12
12
|
var _socketsEncoderDecoder = require("../common/sockets-encoder-decoder.js");
|
|
13
13
|
|
|
14
|
+
var _logger = _interopRequireDefault(require("../common/logger.js"));
|
|
15
|
+
|
|
14
16
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
17
|
|
|
16
18
|
const log = (0, _debug.default)('soundworks:socket'); // https://stackoverflow.com/questions/17575790/environment-detection-node-js-or-browser
|
|
17
19
|
|
|
18
|
-
const isBrowser = new Function(
|
|
20
|
+
const isBrowser = new Function('try {return this===window;}catch(e){ return false;}'); // close
|
|
19
21
|
// Fired when a connection with a websocket is closed.
|
|
20
22
|
// Also available via the onclose property
|
|
21
23
|
// error
|
|
@@ -118,7 +120,7 @@ class Socket {
|
|
|
118
120
|
// ----------------------------------------------------------
|
|
119
121
|
|
|
120
122
|
const stringSocketUrl = `${url}?binary=0&${queryParams}`;
|
|
121
|
-
await new Promise(
|
|
123
|
+
await new Promise(resolve => {
|
|
122
124
|
let connectionRefusedLogged = false;
|
|
123
125
|
|
|
124
126
|
const trySocket = async () => {
|
|
@@ -148,22 +150,29 @@ class Socket {
|
|
|
148
150
|
ws.addEventListener('error', e => {
|
|
149
151
|
if (e.type === 'error' && e.error.code === 'ECONNREFUSED') {
|
|
150
152
|
if (!connectionRefusedLogged) {
|
|
151
|
-
|
|
153
|
+
_logger.default.log('[socket] connection refused, waiting for the server to start');
|
|
154
|
+
|
|
152
155
|
connectionRefusedLogged = true;
|
|
153
156
|
}
|
|
154
157
|
|
|
158
|
+
if (ws.terminate) {
|
|
159
|
+
ws.terminate();
|
|
160
|
+
} else {
|
|
161
|
+
ws.close();
|
|
162
|
+
}
|
|
163
|
+
|
|
155
164
|
setTimeout(trySocket, 1000);
|
|
156
165
|
}
|
|
157
166
|
});
|
|
158
167
|
};
|
|
159
168
|
|
|
160
169
|
trySocket();
|
|
161
|
-
}); //
|
|
162
|
-
//
|
|
163
|
-
//
|
|
170
|
+
}); // ----------------------------------------------------------
|
|
171
|
+
// init binary socket
|
|
172
|
+
// ----------------------------------------------------------
|
|
164
173
|
|
|
165
174
|
const binarySocketUrl = `${url}?binary=1&${queryParams}`;
|
|
166
|
-
await new Promise(
|
|
175
|
+
await new Promise(resolve => {
|
|
167
176
|
log(`[binary socket] trying connection - url: ${binarySocketUrl}`);
|
|
168
177
|
const ws = new _isomorphicWs.default(binarySocketUrl, webSocketOptions);
|
|
169
178
|
ws.binaryType = 'arraybuffer';
|
|
@@ -186,7 +195,7 @@ class Socket {
|
|
|
186
195
|
|
|
187
196
|
resolve();
|
|
188
197
|
});
|
|
189
|
-
}); //
|
|
198
|
+
}); // wait for both sockets connected
|
|
190
199
|
|
|
191
200
|
return Promise.resolve();
|
|
192
201
|
}
|
|
@@ -333,6 +342,5 @@ class Socket {
|
|
|
333
342
|
|
|
334
343
|
}
|
|
335
344
|
|
|
336
|
-
;
|
|
337
345
|
var _default = Socket;
|
|
338
346
|
exports.default = _default;
|
package/common/ParameterBag.js
CHANGED
|
@@ -359,18 +359,18 @@ class ParameterBag {
|
|
|
359
359
|
for (let name in schema) {
|
|
360
360
|
const def = schema[name];
|
|
361
361
|
|
|
362
|
-
if (!
|
|
362
|
+
if (!Object.prototype.hasOwnProperty.call(def, 'type')) {
|
|
363
363
|
throw new TypeError(`[stateManager] Invalid schema definition - param "${name}": "type" key is required`);
|
|
364
364
|
}
|
|
365
365
|
|
|
366
|
-
if (!
|
|
366
|
+
if (!Object.prototype.hasOwnProperty.call(types, def.type)) {
|
|
367
367
|
throw new TypeError(`[stateManager] Invalid schema definition - param "${name}": "{ type: '${def.type}' }" does not exists`);
|
|
368
368
|
}
|
|
369
369
|
|
|
370
370
|
const required = types[def.type].required;
|
|
371
|
-
required.forEach(
|
|
371
|
+
required.forEach(key => {
|
|
372
372
|
if (def.event === true && key === 'default') {// do nothing, default is always null for `event` params
|
|
373
|
-
} else if (!
|
|
373
|
+
} else if (!Object.prototype.hasOwnProperty.call(def, key)) {
|
|
374
374
|
throw new TypeError(`[stateManager] Invalid schema definition - param "${name}" (type "${def.type}"): "${key}" key is required`);
|
|
375
375
|
}
|
|
376
376
|
});
|
|
@@ -409,21 +409,19 @@ class ParameterBag {
|
|
|
409
409
|
ParameterBag.validateSchema(schema); // make shure initValues make sens according to the given schema
|
|
410
410
|
|
|
411
411
|
for (let name in initValues) {
|
|
412
|
-
if (!
|
|
412
|
+
if (!Object.prototype.hasOwnProperty.call(schema, name)) {
|
|
413
413
|
throw new ReferenceError(`[stateManager] init value defined for undefined param "${name}"`);
|
|
414
414
|
}
|
|
415
415
|
}
|
|
416
416
|
|
|
417
417
|
for (let [name, def] of Object.entries(schema)) {
|
|
418
|
-
const {
|
|
419
|
-
defaultOptions,
|
|
420
|
-
coerceFunction
|
|
421
|
-
} = types[def.type];
|
|
422
|
-
|
|
423
418
|
if (types[def.type].sanitizeSchema) {
|
|
424
419
|
def = types[def.type].sanitizeSchema(def);
|
|
425
420
|
}
|
|
426
421
|
|
|
422
|
+
const {
|
|
423
|
+
defaultOptions
|
|
424
|
+
} = types[def.type];
|
|
427
425
|
def = Object.assign({}, defaultOptions, def); // if event property is set to true, the param must
|
|
428
426
|
// be nullable and its default value is `undefined`
|
|
429
427
|
|
|
@@ -434,7 +432,7 @@ class ParameterBag {
|
|
|
434
432
|
|
|
435
433
|
let initValue;
|
|
436
434
|
|
|
437
|
-
if (
|
|
435
|
+
if (Object.prototype.hasOwnProperty.call(initValues, name)) {
|
|
438
436
|
initValue = initValues[name];
|
|
439
437
|
} else {
|
|
440
438
|
initValue = def.default;
|
|
@@ -456,7 +454,7 @@ class ParameterBag {
|
|
|
456
454
|
|
|
457
455
|
|
|
458
456
|
has(name) {
|
|
459
|
-
return
|
|
457
|
+
return Object.prototype.hasOwnProperty.call(this._schema, name);
|
|
460
458
|
}
|
|
461
459
|
/**
|
|
462
460
|
* Return values of all parameters as a flat object.
|
|
@@ -520,7 +518,7 @@ class ParameterBag {
|
|
|
520
518
|
if (value === null && def.nullable === false) {
|
|
521
519
|
throw new TypeError(`[stateManager] Invalid value for ${def.type} param "${name}": value is null and param is not nullable`);
|
|
522
520
|
} else if (value === null && def.nullable === true) {
|
|
523
|
-
value =
|
|
521
|
+
value = null;
|
|
524
522
|
} else {
|
|
525
523
|
value = coerceFunction(name, def, value);
|
|
526
524
|
}
|
package/common/SharedState.js
CHANGED
|
@@ -11,18 +11,20 @@ var _sharedStateUtils = require("./shared-state-utils.js");
|
|
|
11
11
|
|
|
12
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
13
13
|
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
/**
|
|
15
|
+
* Representation of a shared state.
|
|
16
|
+
*
|
|
17
|
+
* @memberof server
|
|
18
|
+
*
|
|
19
|
+
* @see {server.SharedStateManagerServer}
|
|
20
|
+
*/
|
|
17
21
|
|
|
18
22
|
/**
|
|
19
|
-
* Representation of a shared state.
|
|
20
|
-
* client-side and server-side.
|
|
23
|
+
* Representation of a shared state.
|
|
21
24
|
*
|
|
22
|
-
* @memberof
|
|
25
|
+
* @memberof client
|
|
23
26
|
*
|
|
24
27
|
* @see {client.SharedStateManagerClient}
|
|
25
|
-
* @see {server.SharedStateManagerServer}
|
|
26
28
|
*/
|
|
27
29
|
class SharedState {
|
|
28
30
|
constructor(id, remoteId, schemaName, schema, client, isOwner, manager, initValues = {}) {
|
|
@@ -46,23 +48,47 @@ ${JSON.stringify(initValues, null, 2)}`);
|
|
|
46
48
|
this._onDetachCallbacks = new Set();
|
|
47
49
|
this._onDeleteCallbacks = new Set(); // add listener for state updates
|
|
48
50
|
|
|
49
|
-
|
|
51
|
+
this._client.transport.addListener(`${_sharedStateUtils.UPDATE_RESPONSE}-${this.id}-${this.remoteId}`, async (reqId, updates, context) => {
|
|
50
52
|
const updated = await this._commit(updates, context, true, true);
|
|
51
53
|
(0, _sharedStateUtils.resolveRequest)(reqId, updated);
|
|
52
54
|
}); // retrieve values but do not propagate to subscriptions
|
|
53
55
|
|
|
54
|
-
|
|
56
|
+
|
|
57
|
+
this._client.transport.addListener(`${_sharedStateUtils.UPDATE_ABORT}-${this.id}-${this.remoteId}`, async (reqId, updates, context) => {
|
|
55
58
|
const updated = await this._commit(updates, context, false, true);
|
|
56
59
|
(0, _sharedStateUtils.resolveRequest)(reqId, updated);
|
|
57
60
|
});
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
+
|
|
62
|
+
this._client.transport.addListener(`${_sharedStateUtils.UPDATE_NOTIFICATION}-${this.id}-${this.remoteId}`, async (updates, context) => {
|
|
63
|
+
// https://github.com/collective-soundworks/soundworks/issues/18
|
|
64
|
+
//
|
|
65
|
+
// # note: 2002-10-03
|
|
66
|
+
//
|
|
67
|
+
// `setTimeout(async () => this._commit(updates, context, true, false));`
|
|
68
|
+
// appears to be the only way to push the update commit in the next event
|
|
69
|
+
// cycle so that `attach` can resolve before the update notification is
|
|
70
|
+
// actually dispatched. The alternative:
|
|
71
|
+
// `Promise.resolve().then(() => this._commit(updates, context, true, false))``
|
|
72
|
+
// does not behave as expected...
|
|
73
|
+
//
|
|
74
|
+
// However this breaks the reliability of:
|
|
75
|
+
// ```
|
|
76
|
+
// /* given a0, a1 and a2 being 3 similar attached states */
|
|
77
|
+
// await state.set({ int: i });
|
|
78
|
+
//
|
|
79
|
+
// assert.equal(a0.get('int'), i);
|
|
80
|
+
// assert.equal(a1.get('int'), i);
|
|
81
|
+
// assert.equal(a2.get('int'), i);
|
|
82
|
+
// ```
|
|
83
|
+
// which is far more important than the edge case reported in the issue
|
|
84
|
+
// therefore this wont be fixed for now
|
|
85
|
+
this._commit(updates, context, true, false);
|
|
61
86
|
}); // ---------------------------------------------
|
|
62
87
|
// DELETE initiated by creator, or schema deleted
|
|
63
88
|
// ---------------------------------------------
|
|
64
89
|
|
|
65
|
-
|
|
90
|
+
|
|
91
|
+
this._client.transport.addListener(`${_sharedStateUtils.DELETE_NOTIFICATION}-${this.id}-${this.remoteId}`, () => {
|
|
66
92
|
this._manager._statesById.delete(this.id);
|
|
67
93
|
|
|
68
94
|
this._clearTransport();
|
|
@@ -78,7 +104,7 @@ ${JSON.stringify(initValues, null, 2)}`);
|
|
|
78
104
|
// ---------------------------------------------
|
|
79
105
|
// DELETE (can only delete if creator)
|
|
80
106
|
// ---------------------------------------------
|
|
81
|
-
|
|
107
|
+
this._client.transport.addListener(`${_sharedStateUtils.DELETE_RESPONSE}-${this.id}-${this.remoteId}`, reqId => {
|
|
82
108
|
this._manager._statesById.delete(this.id);
|
|
83
109
|
|
|
84
110
|
this._clearTransport();
|
|
@@ -91,14 +117,15 @@ ${JSON.stringify(initValues, null, 2)}`);
|
|
|
91
117
|
|
|
92
118
|
(0, _sharedStateUtils.resolveRequest)(reqId, this);
|
|
93
119
|
});
|
|
94
|
-
|
|
120
|
+
|
|
121
|
+
this._client.transport.addListener(`${_sharedStateUtils.DELETE_ERROR}-${this.id}`, (reqId, msg) => {
|
|
95
122
|
(0, _sharedStateUtils.rejectRequest)(reqId, msg);
|
|
96
123
|
});
|
|
97
124
|
} else {
|
|
98
125
|
// ---------------------------------------------
|
|
99
126
|
// DETACH
|
|
100
127
|
// ---------------------------------------------
|
|
101
|
-
|
|
128
|
+
this._client.transport.addListener(`${_sharedStateUtils.DETACH_RESPONSE}-${this.id}-${this.remoteId}`, reqId => {
|
|
102
129
|
this._manager._statesById.delete(this.id);
|
|
103
130
|
|
|
104
131
|
this._clearTransport();
|
|
@@ -110,7 +137,8 @@ ${JSON.stringify(initValues, null, 2)}`);
|
|
|
110
137
|
(0, _sharedStateUtils.resolveRequest)(reqId, this);
|
|
111
138
|
}); // the state does not exists anymore in the server (should not happen)
|
|
112
139
|
|
|
113
|
-
|
|
140
|
+
|
|
141
|
+
this._client.transport.addListener(`${_sharedStateUtils.DETACH_ERROR}-${this.id}`, (reqId, msg) => {
|
|
114
142
|
(0, _sharedStateUtils.rejectRequest)(reqId, msg);
|
|
115
143
|
});
|
|
116
144
|
}
|
|
@@ -150,7 +178,6 @@ ${JSON.stringify(initValues, null, 2)}`);
|
|
|
150
178
|
for (let name in updates) {
|
|
151
179
|
const {
|
|
152
180
|
immediate,
|
|
153
|
-
filterChange,
|
|
154
181
|
event
|
|
155
182
|
} = this._parameters.getSchema(name); // @note 20211209 - we had an issue here server-side, because if the value
|
|
156
183
|
// is an object or an array, the reference is shared by everybody, therefore
|
|
@@ -185,7 +212,9 @@ ${JSON.stringify(initValues, null, 2)}`);
|
|
|
185
212
|
this._subscriptions.forEach(listener => {
|
|
186
213
|
promises.push(listener(newValues, oldValues, context));
|
|
187
214
|
});
|
|
188
|
-
}
|
|
215
|
+
} // on a given client, `await state.set(update)` resolves when all
|
|
216
|
+
// subscription have resolved themselves
|
|
217
|
+
|
|
189
218
|
|
|
190
219
|
await Promise.all(promises); // reset events to null after propagation of all listeners
|
|
191
220
|
|
|
@@ -190,7 +190,7 @@ class SharedStateManagerServer extends _SharedStateManagerClient.default {
|
|
|
190
190
|
|
|
191
191
|
|
|
192
192
|
removeClient(nodeId) {
|
|
193
|
-
for (let [
|
|
193
|
+
for (let [_id, state] of this._serverStatesById.entries()) {
|
|
194
194
|
let deleteState = false; // define if the client is the creator of the state, in which case
|
|
195
195
|
// everybody must delete it
|
|
196
196
|
|
|
@@ -274,7 +274,7 @@ class SharedStateManagerServer extends _SharedStateManagerClient.default {
|
|
|
274
274
|
|
|
275
275
|
deleteSchema(schemaName) {
|
|
276
276
|
// @note: deleting schema
|
|
277
|
-
for (let [
|
|
277
|
+
for (let [_id, state] of this._serverStatesById.entries()) {
|
|
278
278
|
if (state.schemaName === schemaName) {
|
|
279
279
|
for (let [remoteId, attached] of state._attachedClients.entries()) {
|
|
280
280
|
state._detachClient(remoteId, attached);
|
|
@@ -7,8 +7,6 @@ exports.default = void 0;
|
|
|
7
7
|
|
|
8
8
|
var _ParameterBag = _interopRequireDefault(require("./ParameterBag.js"));
|
|
9
9
|
|
|
10
|
-
var _lodash = _interopRequireDefault(require("lodash.clonedeep"));
|
|
11
|
-
|
|
12
10
|
var _sharedStateUtils = require("./shared-state-utils");
|
|
13
11
|
|
|
14
12
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
@@ -81,12 +81,16 @@ const logger = {
|
|
|
81
81
|
}));
|
|
82
82
|
console.log(``); // check if a route is defined but not in config
|
|
83
83
|
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
84
|
+
if (clientsConfig) {
|
|
85
|
+
const configClientTypes = Object.keys(clientsConfig);
|
|
86
|
+
routes.forEach(r => {
|
|
87
|
+
if (configClientTypes.indexOf(r.clientType) === -1) {
|
|
88
|
+
console.log(`@warning: no client config found for route ${r.clientType}`);
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
} else {
|
|
92
|
+
console.log(`@warning: no config found for clients`);
|
|
93
|
+
}
|
|
90
94
|
},
|
|
91
95
|
|
|
92
96
|
ip(protocol, address, port) {
|
|
@@ -127,6 +131,30 @@ const logger = {
|
|
|
127
131
|
}
|
|
128
132
|
|
|
129
133
|
console.log(` ${name} ${_chalk.default.red('errors')}`);
|
|
134
|
+
},
|
|
135
|
+
|
|
136
|
+
log(msg) {
|
|
137
|
+
if (!this.verbose) {
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
console.log(msg);
|
|
142
|
+
},
|
|
143
|
+
|
|
144
|
+
warn(msg) {
|
|
145
|
+
if (!this.verbose) {
|
|
146
|
+
return;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
console.warn(_chalk.default.yellow(msg));
|
|
150
|
+
},
|
|
151
|
+
|
|
152
|
+
error(msg) {
|
|
153
|
+
if (!this.verbose) {
|
|
154
|
+
return;
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
console.error(_chalk.default.red(msg));
|
|
130
158
|
}
|
|
131
159
|
|
|
132
160
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@soundworks/core",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.7.0",
|
|
4
4
|
"description": "full-stack javascript framework for distributed audio visual experiences on the web",
|
|
5
5
|
"authors": [
|
|
6
6
|
"Benjamin Matuszewski",
|
|
@@ -30,6 +30,7 @@
|
|
|
30
30
|
"dev:server": "chokidar src/server/ -c \"npm run build:server\"",
|
|
31
31
|
"dev:common": "chokidar src/common/ -c \"npm run build:common\"",
|
|
32
32
|
"dev": "npm run build && (npm run dev:client & npm run dev:server & npm run dev:common)",
|
|
33
|
+
"lint": "npx eslint src",
|
|
33
34
|
"test": "mocha"
|
|
34
35
|
},
|
|
35
36
|
"dependencies": {
|
|
@@ -46,7 +47,6 @@
|
|
|
46
47
|
"lodash.merge": "^4.6.2",
|
|
47
48
|
"pem": "^1.14.6",
|
|
48
49
|
"polka": "^0.5.2",
|
|
49
|
-
"serve-static": "^1.15.0",
|
|
50
50
|
"uuid": "^8.3.2",
|
|
51
51
|
"window-or-global": "^1.0.1",
|
|
52
52
|
"ws": "^8.5.0"
|
|
@@ -55,17 +55,28 @@
|
|
|
55
55
|
"@babel/cli": "^7.17.6",
|
|
56
56
|
"@babel/core": "^7.17.9",
|
|
57
57
|
"@babel/preset-env": "^7.16.11",
|
|
58
|
+
"@soundworks/plugin-delay": "^1.0.0",
|
|
58
59
|
"chai": "^4.3.6",
|
|
60
|
+
"chai-shallow-deep-equal": "^1.4.6",
|
|
59
61
|
"chokidar": "^3.5.3",
|
|
60
62
|
"chokidar-cli": "^3.0.0",
|
|
63
|
+
"commitizen": "^4.2.5",
|
|
64
|
+
"cz-conventional-changelog": "^3.3.0",
|
|
61
65
|
"docdash": "^1.2.0",
|
|
66
|
+
"dotenv": "^16.0.3",
|
|
67
|
+
"eslint": "^8.24.0",
|
|
62
68
|
"jsdoc": "^3.6.10",
|
|
63
69
|
"markdown-toc": "^1.2.0",
|
|
64
70
|
"mocha": "^9.2.2",
|
|
65
|
-
"
|
|
71
|
+
"puppeteer": "^18.0.5"
|
|
66
72
|
},
|
|
67
73
|
"optionalDependencies": {
|
|
68
74
|
"bufferutil": "^4.0.5",
|
|
69
75
|
"utf-8-validate": "^5.0.7"
|
|
76
|
+
},
|
|
77
|
+
"config": {
|
|
78
|
+
"commitizen": {
|
|
79
|
+
"path": "./node_modules/cz-conventional-changelog"
|
|
80
|
+
}
|
|
70
81
|
}
|
|
71
82
|
}
|
|
@@ -83,7 +83,7 @@ class AbstractExperience {
|
|
|
83
83
|
// sent when the client `enters` the Experience client side, i.e. when all
|
|
84
84
|
// required plugins are ready
|
|
85
85
|
|
|
86
|
-
return new Promise((
|
|
86
|
+
return new Promise(() => {
|
|
87
87
|
client.socket.addListener('s:exp:enter', () => {
|
|
88
88
|
this.clients.add(client);
|
|
89
89
|
this.enter(client);
|