@soundworks/core 4.0.0-alpha.2 → 4.0.0-alpha.21
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/.eslintrc +0 -1
- package/.jsdoc +57 -0
- package/CHANGELOG.md +33 -0
- package/README.md +17 -51
- package/package.json +24 -31
- package/src/client/Client.js +13 -7
- package/src/client/Context.js +12 -13
- package/src/client/ContextManager.js +1 -1
- package/src/client/PluginManager.js +1 -1
- package/src/client/SharedStateCollection.js +38 -0
- package/src/client/Socket.js +80 -44
- package/src/common/BasePlugin.js +31 -21
- package/src/common/BasePluginManager.js +34 -19
- package/src/common/BaseSharedState.js +304 -79
- package/src/common/BaseSharedStateCollection.js +324 -0
- package/src/common/BaseStateManager.js +353 -49
- package/src/common/BatchedTransport.js +73 -0
- package/src/common/ParameterBag.js +92 -30
- package/src/common/PromiseStore.js +53 -0
- package/src/common/SharedStatePrivate.js +87 -46
- package/src/common/constants.js +14 -1
- package/src/common/sockets-utils.js +3 -4
- package/src/server/Client.js +7 -2
- package/src/server/ContextManager.js +4 -4
- package/src/server/PluginManager.js +1 -1
- package/src/server/Server.js +263 -43
- package/src/server/SharedState.js +0 -1
- package/src/server/SharedStateCollection.js +39 -0
- package/src/server/Socket.js +55 -49
- package/src/server/Sockets.js +33 -11
- package/src/server/StateManager.js +187 -100
- package/src/server/audit-network-latency.worker.js +16 -4
- package/src/server/crypto.js +37 -0
- package/src/common/promise-store.js +0 -25
- package/src/common/utils.js +0 -22
- package/tsconfig.json +0 -21
|
@@ -0,0 +1,324 @@
|
|
|
1
|
+
/** @private */
|
|
2
|
+
class BaseSharedStateCollection {
|
|
3
|
+
constructor(stateManager, schemaName, filter = null, options = {}) {
|
|
4
|
+
this._stateManager = stateManager;
|
|
5
|
+
this._schemaName = schemaName;
|
|
6
|
+
this._filter = filter;
|
|
7
|
+
this._options = Object.assign({ excludeLocal: false }, options);
|
|
8
|
+
|
|
9
|
+
this._schema = null;
|
|
10
|
+
this._states = [];
|
|
11
|
+
|
|
12
|
+
this._onUpdateCallbacks = new Set();
|
|
13
|
+
this._onAttachCallbacks = new Set();
|
|
14
|
+
this._onDetachCallbacks = new Set();
|
|
15
|
+
this._unobserve = null;
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
/** @private */
|
|
19
|
+
async _init() {
|
|
20
|
+
this._schema = await this._stateManager.getSchema(this._schemaName);
|
|
21
|
+
|
|
22
|
+
// if filter is set, check that it contains only valid param names
|
|
23
|
+
if (this._filter !== null) {
|
|
24
|
+
const keys = Object.keys(this._schema);
|
|
25
|
+
|
|
26
|
+
for (let filter of this._filter) {
|
|
27
|
+
if (!keys.includes(filter)) {
|
|
28
|
+
throw new ReferenceError(`[SharedStateCollection] Invalid filter key (${filter}) for schema "${this._schemaName}"`)
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
this._unobserve = await this._stateManager.observe(this._schemaName, async (schemaName, stateId) => {
|
|
34
|
+
const state = await this._stateManager.attach(schemaName, stateId, this._filter);
|
|
35
|
+
this._states.push(state);
|
|
36
|
+
|
|
37
|
+
state.onDetach(() => {
|
|
38
|
+
const index = this._states.indexOf(state);
|
|
39
|
+
this._states.splice(index, 1);
|
|
40
|
+
|
|
41
|
+
this._onDetachCallbacks.forEach(callback => callback(state));
|
|
42
|
+
});
|
|
43
|
+
|
|
44
|
+
state.onUpdate((newValues, oldValues, context) => {
|
|
45
|
+
Array.from(this._onUpdateCallbacks).forEach(callback => {
|
|
46
|
+
callback(state, newValues, oldValues, context);
|
|
47
|
+
});
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
this._onAttachCallbacks.forEach(callback => callback(state));
|
|
51
|
+
}, this._options);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Size of the collection, alias `size`
|
|
56
|
+
* @type {number}
|
|
57
|
+
* @readonly
|
|
58
|
+
*/
|
|
59
|
+
get length() {
|
|
60
|
+
return this._states.length;
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Size of the collection, , alias `length`
|
|
65
|
+
* @type {number}
|
|
66
|
+
* @readonly
|
|
67
|
+
*/
|
|
68
|
+
get size() {
|
|
69
|
+
return this._states.length;
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
/**
|
|
73
|
+
* Name of the schema from which the collection has been created.
|
|
74
|
+
* @type {String}
|
|
75
|
+
* @readonly
|
|
76
|
+
*/
|
|
77
|
+
get schemaName() {
|
|
78
|
+
return this._schemaName;
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
/**
|
|
82
|
+
* Definition of schema from which the collection has been created.
|
|
83
|
+
*
|
|
84
|
+
* @param {string} [name=null] - If given, returns only the definition
|
|
85
|
+
* corresponding to the given param name.
|
|
86
|
+
* @throws Throws if `name` does not correspond to an existing field
|
|
87
|
+
* of the schema.
|
|
88
|
+
* @return {object}
|
|
89
|
+
* @example
|
|
90
|
+
* const schema = collection.getSchema();
|
|
91
|
+
*/
|
|
92
|
+
getSchema(name = null) {
|
|
93
|
+
if (name) {
|
|
94
|
+
return this._schema[name];
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
return this._schema;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* Get the default values as declared in the schema.
|
|
102
|
+
*
|
|
103
|
+
* @return {object}
|
|
104
|
+
* @example
|
|
105
|
+
* const defaults = state.getDefaults();
|
|
106
|
+
*/
|
|
107
|
+
getDefaults() {
|
|
108
|
+
const defaults = {};
|
|
109
|
+
for (let name in this._schema) {
|
|
110
|
+
defaults[name] = this._schema[name].default;
|
|
111
|
+
}
|
|
112
|
+
return defaults;
|
|
113
|
+
}
|
|
114
|
+
|
|
115
|
+
/**
|
|
116
|
+
* Return the current values of all the states in the collection.
|
|
117
|
+
* @return {Object[]}
|
|
118
|
+
*/
|
|
119
|
+
getValues() {
|
|
120
|
+
return this._states.map(state => state.getValues());
|
|
121
|
+
}
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* Return the current values of all the states in the collection.
|
|
125
|
+
*
|
|
126
|
+
* Similar to `getValues` but returns a reference to the underlying value in
|
|
127
|
+
* case of `any` type. May be usefull if the underlying value is big (e.g.
|
|
128
|
+
* sensors recordings, etc.) and deep cloning expensive. Be aware that if
|
|
129
|
+
* changes are made on the returned object, the state of your application will
|
|
130
|
+
* become inconsistent.
|
|
131
|
+
*
|
|
132
|
+
* @return {Object[]}
|
|
133
|
+
*/
|
|
134
|
+
getValuesUnsafe() {
|
|
135
|
+
return this._states.map(state => state.getValues());
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
/**
|
|
139
|
+
* Return the current param value of all the states in the collection.
|
|
140
|
+
*
|
|
141
|
+
* @param {String} name - Name of the parameter
|
|
142
|
+
* @return {any[]}
|
|
143
|
+
*/
|
|
144
|
+
get(name) {
|
|
145
|
+
// we can delegate to the state.get(name) method for throwing in case of filtered
|
|
146
|
+
// keys, as the Promise.all will reject on first reject Promise
|
|
147
|
+
return this._states.map(state => state.get(name));
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
/**
|
|
151
|
+
* Similar to `get` but returns a reference to the underlying value in case of
|
|
152
|
+
* `any` type. May be usefull if the underlying value is big (e.g. sensors
|
|
153
|
+
* recordings, etc.) and deep cloning expensive. Be aware that if changes are
|
|
154
|
+
* made on the returned object, the state of your application will become
|
|
155
|
+
* inconsistent.
|
|
156
|
+
*
|
|
157
|
+
* @param {String} name - Name of the parameter
|
|
158
|
+
* @return {any[]}
|
|
159
|
+
*/
|
|
160
|
+
getUnsafe(name) {
|
|
161
|
+
// we can delegate to the state.get(name) method for throwing in case of filtered
|
|
162
|
+
// keys, as the Promise.all will reject on first reject Promise
|
|
163
|
+
return this._states.map(state => state.get(name));
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Update all states of the collection with given values.
|
|
168
|
+
* @param {object} updates - key / value pairs of updates to apply to the state.
|
|
169
|
+
* @param {mixed} [context=null] - optionnal contextual object that will be propagated
|
|
170
|
+
* alongside the updates of the state. The context is valid only for the
|
|
171
|
+
* current call and will be passed as third argument to all update listeners.
|
|
172
|
+
*/
|
|
173
|
+
async set(updates, context = null) {
|
|
174
|
+
// we can delegate to the state.set(update) method for throwing in case of
|
|
175
|
+
// filtered keys, as the Promise.all will reject on first reject Promise
|
|
176
|
+
const promises = this._states.map(state => state.set(updates, context));
|
|
177
|
+
return Promise.all(promises);
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
/**
|
|
181
|
+
* Subscribe to any state update of the collection.
|
|
182
|
+
*
|
|
183
|
+
* @param {server.SharedStateCollection~onUpdateCallback|client.SharedStateCollection~onUpdateCallback}
|
|
184
|
+
* callback - Callback to execute when an update is applied on a state.
|
|
185
|
+
* @param {Boolean} [executeListener=false] - Execute the callback immediately
|
|
186
|
+
* for all underlying states with current state values. (`oldValues` will be
|
|
187
|
+
* set to `{}`, and `context` to `null`)
|
|
188
|
+
* @returns {Function} - Function that delete the registered listener.
|
|
189
|
+
*/
|
|
190
|
+
onUpdate(callback, executeListener = false) {
|
|
191
|
+
this._onUpdateCallbacks.add(callback);
|
|
192
|
+
|
|
193
|
+
if (executeListener === true) {
|
|
194
|
+
this._states.forEach(state => {
|
|
195
|
+
const currentValues = state.getValues();
|
|
196
|
+
const oldValues = {};
|
|
197
|
+
const context = null;
|
|
198
|
+
|
|
199
|
+
callback(state, currentValues, oldValues, context);
|
|
200
|
+
});
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
return () => this._onUpdateCallbacks.delete(callback);
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
/**
|
|
207
|
+
* Register a function to execute when a state is added to the collection.
|
|
208
|
+
*
|
|
209
|
+
* @param {Function} callback - callback to execute when a state is added to
|
|
210
|
+
* the collection.
|
|
211
|
+
* @param {Function} executeListener - execute the callback with the states
|
|
212
|
+
* already present in the collection.
|
|
213
|
+
* @returns {Function} - Function that delete the registered listener.
|
|
214
|
+
*/
|
|
215
|
+
onAttach(callback, executeListener = false) {
|
|
216
|
+
if (executeListener === true) {
|
|
217
|
+
this._states.forEach(state => callback(state));
|
|
218
|
+
}
|
|
219
|
+
|
|
220
|
+
this._onAttachCallbacks.add(callback);
|
|
221
|
+
|
|
222
|
+
return () => this._onAttachCallbacks.delete(callback);
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
/**
|
|
226
|
+
* Register a function to execute when a state is removed from the collection.
|
|
227
|
+
*
|
|
228
|
+
* @param {Function} callback - callback to execute when a state is removed
|
|
229
|
+
* from the collection.
|
|
230
|
+
* @returns {Function} - Function that delete the registered listener.
|
|
231
|
+
*/
|
|
232
|
+
onDetach(callback) {
|
|
233
|
+
this._onDetachCallbacks.add(callback);
|
|
234
|
+
|
|
235
|
+
return () => this._onDetachCallbacks.delete(callback);
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
/**
|
|
239
|
+
* Detach from the collection, i.e. detach all underlying shared states.
|
|
240
|
+
* @type {number}
|
|
241
|
+
*/
|
|
242
|
+
async detach() {
|
|
243
|
+
this._unobserve();
|
|
244
|
+
this._onUpdateCallbacks.clear();
|
|
245
|
+
|
|
246
|
+
const promises = this._states.map(state => state.detach());
|
|
247
|
+
await Promise.all(promises);
|
|
248
|
+
|
|
249
|
+
this._onDetachCallbacks.clear();
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
/**
|
|
253
|
+
* Execute the given function once for each states of the collection (see `Array.forEach`).
|
|
254
|
+
*
|
|
255
|
+
* @param {Function} func - A function to execute for each element in the array.
|
|
256
|
+
* Its return value is discarded.
|
|
257
|
+
*/
|
|
258
|
+
forEach(func) {
|
|
259
|
+
this._states.forEach(func);
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
/**
|
|
263
|
+
* Creates a new array populated with the results of calling a provided function
|
|
264
|
+
* on every state of the collection (see `Array.map`).
|
|
265
|
+
*
|
|
266
|
+
* @param {Function} func - A function to execute for each element in the array.
|
|
267
|
+
* Its return value is added as a single element in the new array.
|
|
268
|
+
*/
|
|
269
|
+
map(func) {
|
|
270
|
+
return this._states.map(func);
|
|
271
|
+
}
|
|
272
|
+
|
|
273
|
+
/**
|
|
274
|
+
* Creates a shallow copy of a portion of the collection, filtered down to just
|
|
275
|
+
* the estates that pass the test implemented by the provided function (see `Array.filter`).
|
|
276
|
+
*
|
|
277
|
+
* @param {Function} func - A function to execute for each element in the array.
|
|
278
|
+
* It should return a truthy to keep the element in the resulting array, and a f
|
|
279
|
+
* alsy value otherwise.
|
|
280
|
+
*/
|
|
281
|
+
filter(func) {
|
|
282
|
+
return this._states.filter(func);
|
|
283
|
+
}
|
|
284
|
+
|
|
285
|
+
/**
|
|
286
|
+
* Sort the elements of the collection in place (see `Array.sort`).
|
|
287
|
+
*
|
|
288
|
+
* @param {Function} func - Function that defines the sort order.
|
|
289
|
+
*/
|
|
290
|
+
sort(func) {
|
|
291
|
+
this._states.sort(func);
|
|
292
|
+
}
|
|
293
|
+
|
|
294
|
+
/**
|
|
295
|
+
* Returns the first element of the collection that satisfies the provided testing
|
|
296
|
+
* function. If no values satisfy the testing function, undefined is returned.
|
|
297
|
+
*
|
|
298
|
+
* @param {Function} func - Function to execute for each element in the array.
|
|
299
|
+
* It should return a truthy value to indicate a matching element has been found.
|
|
300
|
+
* @return {}
|
|
301
|
+
*/
|
|
302
|
+
find(func) {
|
|
303
|
+
return this._states.find(func);
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
/**
|
|
307
|
+
* Iterable API, e.g. for use in `for .. of` loops
|
|
308
|
+
*/
|
|
309
|
+
[Symbol.iterator]() {
|
|
310
|
+
let index = 0;
|
|
311
|
+
|
|
312
|
+
return {
|
|
313
|
+
next: () => {
|
|
314
|
+
if (index >= this._states.length) {
|
|
315
|
+
return { value: undefined, done: true };
|
|
316
|
+
} else {
|
|
317
|
+
return { value: this._states[index++], done: false };
|
|
318
|
+
}
|
|
319
|
+
},
|
|
320
|
+
};
|
|
321
|
+
}
|
|
322
|
+
}
|
|
323
|
+
|
|
324
|
+
export default BaseSharedStateCollection;
|